<commit_before>#include "decrypt.h"

Tag5::Ptr find_decrypting_key(const PGP & k, const std::string &keyid){
    if (k.get_ASCII_Armor() == 2){
        std::vector <Packet::Ptr> packets = k.get_packets();
        for(Packet::Ptr const & p : packets){
            if ((p -> get_tag() == 5) || (p -> get_tag() == 7)){
                std::string data = p -> raw();
                Tag5::Ptr key = std::make_shared<Tag5>(data);
                if ( key->get_public_ptr()->get_keyid() != keyid ){
                    continue;
                }
                // make sure key has signing material
                if ((key -> get_pka() == 1) || // RSA
                    (key -> get_pka() == 2) || // RSA
                    (key -> get_pka() == 16)){ // ElGamal
                        return key;
                }
            }
        }
    }
    return Tag5::Ptr();
}

std::string pka_decrypt(const uint8_t pka, std::vector <PGPMPI> & data, const std::vector <PGPMPI> & pri, const std::vector <PGPMPI> & pub){
    if (pka < 3){   // RSA
        return mpitoraw(RSA_decrypt(data[0], pri, pub));
    }
    if (pka == 16){ // ElGamal
        return ElGamal_decrypt(data, pri, pub);
    }
    else{
        std::stringstream s; s << static_cast <int> (pka);
        throw std::runtime_error("Error: PKA number " + s.str() + " not allowed or unknown.");
    }
    return ""; // should never reach here; mainly just to remove compiler warnings
}

std::vector <PGPMPI> decrypt_secret_key(const Tag5::Ptr & pri, const std::string & passphrase){
    std::vector <PGPMPI> out;
    S2K::Ptr s2k = pri -> get_s2k();

    // calculate key used in encryption algorithm
    std::string key = s2k -> run(passphrase, Symmetric_Algorithm_Key_Length.at(Symmetric_Algorithms.at(pri -> get_sym())) >> 3);

    // decrypt secret key
    std::string secret_key = use_normal_CFB_decrypt(pri -> get_sym(), pri -> get_secret(), key, pri -> get_IV());
    // get checksum and remove it from the string
    const unsigned int hash_size = (pri -> get_s2k_con() == 254)?20:2;
    std::string checksum = secret_key.substr(secret_key.size() - hash_size, hash_size);
    secret_key = secret_key.substr(0, secret_key.size() - hash_size);

    // calculate and check checksum
    if(pri -> get_s2k_con() == 254){
        if (use_hash(s2k -> get_hash(), secret_key) != checksum){
            throw std::runtime_error("Error: Secret key checksum and calculated checksum do not match.");
        }
    }
    else{ // all other values; **UNTESTED**
        uint16_t sum = 0;
        for(char & c : secret_key){
            sum += static_cast <unsigned char> (c);
        }
        if (unhexlify(makehex(sum, 4)) != checksum){
            if (use_hash(s2k -> get_hash(), secret_key) != checksum){
                throw std::runtime_error("Error: Secret key checksum and calculated checksum do not match.");
            }
        }
    }

    // extract MPI values
    while (secret_key.size()){
        out.push_back(read_MPI(secret_key));
    }
    return out;
}

std::string decrypt_message(PGP & m, PGP& pri, const std::string & passphrase){
    if ((m.get_ASCII_Armor() != 0)/* && (m.get_ASCII_Armor() != 3) && (m.get_ASCII_Armor() != 4)*/){
        throw std::runtime_error("Error: No encrypted message found.");
    }

    if (pri.get_ASCII_Armor() != 2){
        throw std::runtime_error("Error: No Private Key found.");
    }
    // reused variables
    uint8_t packet;
    std::string data;
    std::string checksum;

    std::string session_key;                    // session key
    uint8_t sym;                                // symmetric key algorithm used to encrypt original data
    unsigned int BS;                            // blocksize of symmetric key algorithm

    // find session key
    std::vector <Packet::Ptr> message_packets = m.get_packets();
    for(Packet::Ptr const & p : message_packets){
        if ((p -> get_tag() == 1) || (p -> get_tag() == 3)){
            data = p -> raw();
            packet = p -> get_tag();
            break;
        }
    }

    if (packet == 1){ // Public-Key Encrypted Session Key Packet (Tag 1)
        Tag1 tag1(data);
        uint8_t pka = tag1.get_pka();
        std::vector <PGPMPI> session_key_mpi = tag1.get_mpi();

        // find corresponding secret key
        Tag5::Ptr sec = find_decrypting_key(pri, tag1.get_keyid());

        if (!sec){
            throw std::runtime_error("Error: Correct Private Key not found.");
        }

        std::vector <PGPMPI> pub = sec -> get_mpi();
        std::vector <PGPMPI> pri = decrypt_secret_key(sec, passphrase);

        // get session key
        session_key = zero + pka_decrypt(pka, session_key_mpi, pri, pub);                   // symmetric algorithm, session key, 2 octet checksum wrapped in EME_PKCS1_ENCODE
        session_key = EME_PKCS1v1_5_DECODE(session_key);                                    // remove EME_PKCS1 encoding
        sym = session_key[0];                                                               // get symmetric algorithm
        checksum = session_key.substr(session_key.size() - 2, 2);                           // get 2 octet checksum
        session_key = session_key.substr(1, session_key.size() - 3);                        // remove both from session key
        uint16_t sum = 0;
        for(char & c : session_key){                                                        // calculate session key checksum
            sum += static_cast <uint8_t> (c);
        }
        if (unhexlify(makehex(sum, 4)) != checksum){                                        // check session key checksums
            throw std::runtime_error("Error: Calculated session key checksum does not match given checksum.");
        }
    }
    else if (packet == 3){ //Symmetric-Key Encrypted Session Key Packet (Tag 3)
        /* untested */
        Tag3 tag3(data);
        data = tag3.get_key(passphrase);
        sym = data[0];
        session_key = data.substr(1, data.size() - 1);
    }
    else{
        throw std::runtime_error("Error: No session key packet found.");
    }

    BS = Symmetric_Algorithm_Block_Length.at(Symmetric_Algorithms.at(sym)) >> 3;           // get blocksize

    // Find encrypted data
    data = "";
    for(Packet::Ptr const & p : message_packets){
        if (p -> get_tag() == 9){
            data = p -> raw();
            Tag9 tag9(data);
            data = tag9.get_encrypted_data();
            packet = 9;
            break;
        }
        else if (p -> get_tag() == 18){
            data = p -> raw();
            Tag18 tag18(data);
            data = tag18.get_protected_data();
            packet = 18;
            break;
        }
    }
    if (!data.size()){
        throw std::runtime_error("Error: No encrypted data packets found.");
    }

    if (sym == 2){ // Triple DES
        data = use_OpenPGP_CFB_decrypt(sym, packet, data, session_key.substr(0, 8), session_key.substr(8, 8), session_key.substr(16, 8)); // decrypt encrypted data
    }
    else{
        data = use_OpenPGP_CFB_decrypt(sym, packet, data, session_key); // decrypt encrypted data
    }

    if (packet == 18){ // Symmetrically Encrypted Integrity Protected Data Packet (Tag 18)
        checksum = data.substr(data.size() - 20, 20);       // get given SHA1 checksum
        data = data.substr(0, data.size() - 20);            // remove SHA1 checksum
        if (use_hash(2, data) != checksum){                 // check SHA1 checksum
            throw std::runtime_error("Error: Given checksum and calculated checksum do not match.");
        }
        data = data.substr(0, data.size() - 2);             // get rid of \xd3\x14
    }

    data = data.substr(BS + 2, data.size() - BS - 2);       // get rid of prefix

    if (packet == 9){ // Symmetrically Encrypted Data Packet (Tag 9)
        // figure out which compression algorithm was used
        // uncompressed literal data packet
        if ((data[0] == 'b') || (data[0] == 't') || (data[0] == 'u')){
            data = Tag11(data).write(); // add in Tag11 headers to be removed later
        }
        // BZIP2
        else if (data.substr(0, 2) == "BZ"){
            data = PGP_decompress(3, data);
        }
        // ZLIB
        else if ((data.substr(0, 2) == "\x78\x01") || (data.substr(0, 2) == "\x78\x9c") || (data.substr(0, 2) == "\x78\xda")){
            data = PGP_decompress(2, data);
        }
        // DEFLATE
        else{
            data = PGP_decompress(1, data);
        }
    }

    // get rid of header and figure out what type of packet data it is
    bool format;
    data = read_packet_header(data, packet, format);

    // output data
    switch (packet){
        case 8: // Compressed Data Packet
            {
                data = Tag8(data).get_data(); // compressed packets
                std::vector <Packet::Ptr> compressed_packets;

                while (data.size()){ // extract packets
                    compressed_packets.push_back(read_packet(data) -> clone());
                }

                // extract all packet data; probably needs better formatting
                for(const Packet::Ptr & p : compressed_packets){
                    if (p -> get_tag() == 11){
                        Tag11 tag11(data);
                        if (tag11.get_filename() == ""){
                            data = tag11.get_literal();
                        }
                        else{
                            tag11.write();
                            data = "Data written to file '" + Tag11(data).get_filename() + "'";
                        }

                    }
                    // else{
                        // data += p -> show() + "\n";
                    // }
                }
            }
            break;
        case 11: // Literal Data Packet
            {
                Tag11 tag11(data);
                if (tag11.get_filename() == ""){
                    data = tag11.get_literal();
                }
                else{
                    tag11.write();
                    data = "Data written to file '" + Tag11(data).get_filename() + "'";
                }
            }
            break;
        default:
            {
                std::stringstream s; s << packet;
                throw std::runtime_error("Error: No action defined for packet type " + s.str());
            }
            break;
    }
    return data;
}
<commit_msg>Debug time<commit_after>#include "decrypt.h"

Tag5::Ptr find_decrypting_key(const PGP & k, const std::string &keyid){
    if (k.get_ASCII_Armor() == 2){
        std::vector <Packet::Ptr> packets = k.get_packets();
        for(Packet::Ptr const & p : packets){
            if ((p -> get_tag() == 5) || (p -> get_tag() == 7)){
                std::string data = p -> raw();
                Tag5::Ptr key = std::make_shared<Tag5>(data);
                if ( key->get_public_ptr()->get_keyid() != keyid ){
                    continue;
                }
                // make sure key has signing material
                if ((key -> get_pka() == 1) || // RSA
                    (key -> get_pka() == 2) || // RSA
                    (key -> get_pka() == 16)){ // ElGamal
                        return key;
                }
            }
        }
    }
    return Tag5::Ptr();
}

std::string pka_decrypt(const uint8_t pka, std::vector <PGPMPI> & data, const std::vector <PGPMPI> & pri, const std::vector <PGPMPI> & pub){
    if (pka < 3){   // RSA
        return mpitoraw(RSA_decrypt(data[0], pri, pub));
    }
    if (pka == 16){ // ElGamal
        return ElGamal_decrypt(data, pri, pub);
    }
    else{
        std::stringstream s; s << static_cast <int> (pka);
        throw std::runtime_error("Error: PKA number " + s.str() + " not allowed or unknown.");
    }
    return ""; // should never reach here; mainly just to remove compiler warnings
}

std::vector <PGPMPI> decrypt_secret_key(const Tag5::Ptr & pri, const std::string & passphrase){
    std::vector <PGPMPI> out;
    S2K::Ptr s2k = pri -> get_s2k();

    // calculate key used in encryption algorithm
    std::string key = s2k -> run(passphrase, Symmetric_Algorithm_Key_Length.at(Symmetric_Algorithms.at(pri -> get_sym())) >> 3);

    // decrypt secret key
    std::string secret_key = use_normal_CFB_decrypt(pri -> get_sym(), pri -> get_secret(), key, pri -> get_IV());
    // get checksum and remove it from the string
    const unsigned int hash_size = (pri -> get_s2k_con() == 254)?20:2;
    std::string checksum = secret_key.substr(secret_key.size() - hash_size, hash_size);
    secret_key = secret_key.substr(0, secret_key.size() - hash_size);

    // calculate and check checksum
    if(pri -> get_s2k_con() == 254){
        if (use_hash(s2k -> get_hash(), secret_key) != checksum){
            throw std::runtime_error("Error: Secret key checksum and calculated checksum do not match.");
        }
    }
    else{ // all other values; **UNTESTED**
        uint16_t sum = 0;
        for(char & c : secret_key){
            sum += static_cast <unsigned char> (c);
        }
        if (unhexlify(makehex(sum, 4)) != checksum){
            if (use_hash(s2k -> get_hash(), secret_key) != checksum){
                throw std::runtime_error("Error: Secret key checksum and calculated checksum do not match.");
            }
        }
    }

    // extract MPI values
    while (secret_key.size()){
        out.push_back(read_MPI(secret_key));
    }
    return out;
}

std::string decrypt_message(PGP & m, PGP& pri, const std::string & passphrase){
    if ((m.get_ASCII_Armor() != 0)/* && (m.get_ASCII_Armor() != 3) && (m.get_ASCII_Armor() != 4)*/){
        throw std::runtime_error("Error: No encrypted message found.");
    }

    if (pri.get_ASCII_Armor() != 2){
        throw std::runtime_error("Error: No Private Key found.");
    }
    // reused variables
    uint8_t packet;
    std::string data;
    std::string checksum;

    std::string session_key;                    // session key
    uint8_t sym;                                // symmetric key algorithm used to encrypt original data
    unsigned int BS;                            // blocksize of symmetric key algorithm

    // find session key
    std::vector <Packet::Ptr> message_packets = m.get_packets();
    for(Packet::Ptr const & p : message_packets){
        if ((p -> get_tag() == 1) || (p -> get_tag() == 3)){
            data = p -> raw();
            packet = p -> get_tag();
            break;
        }
    }

    if (packet == 1){ // Public-Key Encrypted Session Key Packet (Tag 1)
        Tag1 tag1(data);
        uint8_t pka = tag1.get_pka();
        std::vector <PGPMPI> session_key_mpi = tag1.get_mpi();

        // find corresponding secret key
        Tag5::Ptr sec = find_decrypting_key(pri, tag1.get_keyid());

        if (!sec){
            throw std::runtime_error("Error: Correct Private Key not found.");
        }

        std::vector <PGPMPI> pub = sec -> get_mpi();
        std::vector <PGPMPI> pri = decrypt_secret_key(sec, passphrase);

        // get session key
        session_key = zero + pka_decrypt(pka, session_key_mpi, pri, pub);                   // symmetric algorithm, session key, 2 octet checksum wrapped in EME_PKCS1_ENCODE
        session_key = EME_PKCS1v1_5_DECODE(session_key);                                    // remove EME_PKCS1 encoding
        sym = session_key[0];                                                               // get symmetric algorithm
        checksum = session_key.substr(session_key.size() - 2, 2);                           // get 2 octet checksum
        session_key = session_key.substr(1, session_key.size() - 3);                        // remove both from session key
        uint16_t sum = 0;
        for(char & c : session_key){                                                        // calculate session key checksum
            sum += static_cast <uint8_t> (c);
        }
        if (unhexlify(makehex(sum, 4)) != checksum){                                        // check session key checksums
            throw std::runtime_error("Error: Calculated session key checksum does not match given checksum.");
        }
    }
    else if (packet == 3){ //Symmetric-Key Encrypted Session Key Packet (Tag 3)
        /* untested */
        Tag3 tag3(data);
        data = tag3.get_key(passphrase);
        sym = data[0];
        session_key = data.substr(1, data.size() - 1);
    }
    else{
        throw std::runtime_error("Error: No session key packet found.");
    }

    BS = Symmetric_Algorithm_Block_Length.at(Symmetric_Algorithms.at(sym)) >> 3;           // get blocksize

    // Find encrypted data
    data = "";
    for(Packet::Ptr const & p : message_packets){
        if (p -> get_tag() == 9){
            data = p -> raw();
            Tag9 tag9(data);
            data = tag9.get_encrypted_data();
            packet = 9;
            break;
        }
        else if (p -> get_tag() == 18){
            data = p -> raw();
            Tag18 tag18(data);
            data = tag18.get_protected_data();
            packet = 18;
            break;
        }
    }
    if (!data.size()){
        throw std::runtime_error("Error: No encrypted data packets found.");
    }

    if (sym == 2){ // Triple DES
        data = use_OpenPGP_CFB_decrypt(sym, packet, data, session_key.substr(0, 8), session_key.substr(8, 8), session_key.substr(16, 8)); // decrypt encrypted data
    }
    else{
        data = use_OpenPGP_CFB_decrypt(sym, packet, data, session_key); // decrypt encrypted data
    }

    if (packet == 18){ // Symmetrically Encrypted Integrity Protected Data Packet (Tag 18)
        checksum = data.substr(data.size() - 20, 20);       // get given SHA1 checksum
        data = data.substr(0, data.size() - 20);            // remove SHA1 checksum
        if (use_hash(2, data) != checksum){                 // check SHA1 checksum
            throw std::runtime_error("Error: Given checksum and calculated checksum do not match.");
        }
        data = data.substr(0, data.size() - 2);             // get rid of \xd3\x14
    }

    data = data.substr(BS + 2, data.size() - BS - 2);       // get rid of prefix

    if (packet == 9){ // Symmetrically Encrypted Data Packet (Tag 9)
        // figure out which compression algorithm was used
        // uncompressed literal data packet
        if ((data[0] == 'b') || (data[0] == 't') || (data[0] == 'u')){
            data = Tag11(data).write(); // add in Tag11 headers to be removed later
        }
        // BZIP2
        else if (data.substr(0, 2) == "BZ"){
            data = PGP_decompress(3, data);
        }
        // ZLIB
        else if ((data.substr(0, 2) == "\x78\x01") || (data.substr(0, 2) == "\x78\x9c") || (data.substr(0, 2) == "\x78\xda")){
            data = PGP_decompress(2, data);
        }
        // DEFLATE
        else{
            data = PGP_decompress(1, data);
        }
    }

    // get rid of header and figure out what type of packet data it is
    bool format;
    data = read_packet_header(data, packet, format);

    // output data
    switch (packet){
        case 8: // Compressed Data Packet
            {
                data = Tag8(data).get_data(); // compressed packets
                std::vector <Packet::Ptr> compressed_packets;

                while (data.size()){ // extract packets
                    compressed_packets.push_back(read_packet(data) -> clone());
                }

                // extract all packet data; probably needs better formatting
                for(const Packet::Ptr & p : compressed_packets){
                    if (p -> get_tag() == 11){
                        Tag11 tag11(data);
                        if (tag11.get_filename() == ""){
                            data = tag11.get_literal();
                        }
                        else{
                            tag11.write();
                            data = "Data written to file '" + Tag11(data).get_filename() + "'";
                        }
                        std::cout << data << std::endl;
                    }
                    // else{
                        // data += p -> show() + "\n";
                    // }
                }
            }
            break;
        case 11: // Literal Data Packet
            {
                Tag11 tag11(data);
                if (tag11.get_filename() == ""){
                    data = tag11.get_literal();
                }
                else{
                    tag11.write();
                    data = "Data written to file '" + Tag11(data).get_filename() + "'";
                }
            }
            break;
        default:
            {
                std::stringstream s; s << packet;
                throw std::runtime_error("Error: No action defined for packet type " + s.str());
            }
            break;
    }
    return data;
}
<|endoftext|>


<commit_before>/* JCHOR - random-wait chorus instrument based on Paul Lansky's chor
           and Doug Scott's trans code

   p0  = output start time
   p1  = input start time
   p2  = output duration
   p3  = input duration (not input end time)
   p4  = maintain input duration, regardless of transposition (1: yes, 0: no)
   p5  = transposition (8ve.pc)
   p6  = number of voices (minimum of 1)
   p7  = minimum grain amplitude
   p8  = maximum grain amplitude
   p9  = minimum grain wait (seconds)
   p10 = maximum grain wait (seconds)
   p11 = seed (0 - 1)
   p12 = input channel [optional; if missing and input has > 1 chan, input
         channels averaged]
   p13 = overall amplitude multiplier [optional; if missing, must use gen 1 *]
   p14 = reference to grain envelope table [optional; if missing, must use
         gen 2 **]

   p7 (min amp), p8 (max amp), p9 (min wait), p10 (max wait) and p13 (amp mult)
   can receive dynamic updates from a table or real-time control source.

   Output can be either mono or stereo. If it's stereo, the program randomly
   distributes the voices across the stereo field.

   Notes on p4 (maintain input duration):

      Because the transposition method doesn't try to maintain duration -- it
      works like the speed control on a tape deck -- you have an option about
      the way to handle the duration of the input read:

      - If p4 is 1, the grain length after transposition will be the same as
        that given by p3 (input duration). This means that the amount actually
        read from the input file will be shorter or longer than p3, depending
        on the transposition.

      - If p4 is 0, the segment of sound specified by p3 will be read, and the
        grain length will be shorter or longer than p3, depending on the
        transposition.

   ----

   Notes about backward compatibility with pre-v4 scores:

   * If an old-style gen table 1 is present, its values will be multiplied
   by p13 (amplitude multiplier), even if the latter is dynamic.

   ** If p14 is missing, you must use an old-style gen table 2 for the
   grain envelope.

   ----

   Differences between JCHOR and chor (besides RT ability):
      - No limit on input duration or number of voices
      - Transpose the input signal
      - Specify the input channel to use (or an average of them)
      - Specify overall amplitude curve and grain window function

   John Gibson (jgg9c@virginia.edu), 9/20/98, RT'd 6/24/99; rev for v4, 7/24/04
*/
#include <stdio.h>
#include <stdlib.h>
#include <ugens.h>
#include <Instrument.h>
#include <PField.h>
#include "JCHOR.h"
#include <rt.h>
#include <rtdefs.h>

extern "C" {
   extern double m_DUR(float [], int);     /* in sys/minc_info.c */
}

//#define DEBUG

#define ENVELOPE_TABLE_SLOT  1
#define WINDOW_FUNC_SLOT     2
#define AVERAGE_CHANS        -1           // average input chans flag value
#define WINDOW_CONTROL_RATE  22050.0      // don't skimp on this

// local functions
static double interp(double, double, double, double);

JCHOR::JCHOR() : Instrument()
{
   in = NULL;
   winarray = NULL;
   winarraylen = 0;
   voices = NULL;
   grain = NULL;
   grain_done = false;
   branch = 0;
}

JCHOR::~JCHOR()
{
   delete [] in;
   delete [] voices;
   delete [] grain;
}

int JCHOR::init(double p[], int n_args)
{
   nargs = n_args;
   float outskip = p[0];
   inskip = p[1];
   float outdur = p[2];
   indur = p[3];
   maintain_indur = (bool) p[4];
   transpose = p[5];
   nvoices = (int) p[6];
   minamp = p[7];
   float maxamp = p[8];
   minwait = p[9];
   float maxwait = p[10];
   seed = p[11];
   inchan = (n_args > 12) ? (int) p[12] : AVERAGE_CHANS;

   if (n_args < 12)
      return die("JCHOR", "Not enough pfields.");

   if (rtsetinput(inskip, this) == -1)
      return DONT_SCHEDULE;
   if (rtsetoutput(outskip, outdur, this) == -1)
      return DONT_SCHEDULE;

   if (outputChannels() > 2)
      return die("JCHOR", "Output must have no more than two channels.");

   if (nvoices < 1)
      return die("JCHOR", "Must have at least one voice.");

   if (minamp < 0.0 || maxamp < 0.0 || minamp > maxamp)
      return die("JCHOR", "Grain amplitude range confused.");
   ampdiff = maxamp - minamp;

   if (minwait < 0.0 || maxwait < 0.0 || minwait > maxwait)
      return die("JCHOR", "Grain wait range confused.");
   waitdiff = (maxwait - minwait) * SR;
   minwait *= SR;

   if (seed < 0.0 || seed > 1.0)
      return die("JCHOR", "Seed must be between 0 and 1 inclusive.");

   amparray = floc(ENVELOPE_TABLE_SLOT);
   if (amparray) {
      int len = fsize(ENVELOPE_TABLE_SLOT);
      tableset(SR, outdur, len, amptabs);
   }

	if (n_args > 14) {      // handle table coming in as optional p14 TablePField
		winarray = (double *) getPFieldTable(14, &winarraylen);
	}
	if (winarray == NULL) {
      // MUST do this here, rather than in grain_input_and_transpose,
      // because by the time that is called, the makegen for this slot
      // may have changed.
      winarray = floc(WINDOW_FUNC_SLOT);
		if (winarray == NULL)
			return die("JCHOR", "Either use the grain envelope pfield (p14) "
                    "or make an old-style gen function in slot %d.",
                    WINDOW_FUNC_SLOT);
      winarraylen = fsize(WINDOW_FUNC_SLOT);
	}

   skip = (int) (SR / (float) resetval);

   return nSamps();
}

void JCHOR::doupdate()
{
   double p[14];
   update(p, 14, kMinAmp | kMaxAmp | kMinWait | kMaxWait | kAmp);

   minamp = p[7];
   float maxamp = p[8];
   if (minamp < 0.0)
      minamp = 0.0;
   if (maxamp < 0.0)
      maxamp = 0.0;
   if (maxamp < minamp)
      maxamp = minamp;
   ampdiff = maxamp - minamp;

   minwait = p[9];
   float maxwait = p[10];
   if (minwait < 0.0)
      minwait = 0.0;
   if (maxwait < 0.0)
      maxwait = 0.0;
   if (maxwait < minwait)
      maxwait = minwait;
   waitdiff = (maxwait - minwait) * SR;
   minwait *= SR;

   if (nargs > 13)
      amp = p[13];
   else
      amp = 1.0;
   if (amparray)
      amp *= tablei(currentFrame(), amparray, amptabs);
}

int JCHOR::configure()
{
   in = new float [RTBUFSAMPS * inputChannels()];
   return in ? 0 : -1;
}

int JCHOR::run()
{
   if (!grain_done) {
      grain_input_and_transpose();
      setup_voices();
   }

   for (int i = 0; i < framesToRun(); i++) {
      if (--branch <= 0) {
         doupdate();
         branch = skip;
      }

      float out[2];
      out[0] = out[1] = 0.0;

      Voice *v = voices;
      for (int j = 0; j < nvoices; j++, v++) {
         if (v->index++ < 0)
            continue;
         if (v->index >= grainsamps) {
            seed = crandom(seed);
            v->index = (int) -(minwait + (seed * waitdiff));
            if (outputChannels() > 1) {
               seed = crandom(seed);
               v->left_amp = seed;
               v->right_amp = 1.0 - v->left_amp;
            }
            seed = crandom(seed);
            v->overall_amp = minamp + (seed * ampdiff);
         }
         else {
            float sig = grain[v->index] * v->overall_amp;
            if (outputChannels() > 1) {
               out[0] += sig * v->left_amp;
               out[1] += sig * v->right_amp;
            }
            else
               out[0] += sig;
         }
      }
      out[0] *= amp;
      out[1] *= amp;

      rtaddout(out);
      increment();
   }

   return framesToRun();
}

/* --------------------------------------------------------------- interp --- */
/* Cubic spline interpolation.
   Nabbed from Doug Scott's trans.
*/
static double
interp(double y0, double y1, double y2, double t)
{
   register double hy2, hy0, a, b, c;

   a = y0;
   hy0 = y0 / 2.0;
   hy2 =  y2 / 2.0;
   b = (-3.0 * hy0) + (2.0 * y1) - hy2;
   c = hy0 - y1 + hy2;

   return (a + b*t + c*t*t);
}

/* --------------------------------------------------------- setup_voices --- */
int JCHOR::setup_voices()
{
   voices = new Voice[nvoices];

   Voice *v = voices;
   for (int i = 0; i < nvoices; i++, v++) {
      seed = crandom(seed);
      v->index = (int) (-seed * (grainsamps - 1));
      if (outputChannels() > 1) {
         seed = crandom(seed);
         v->left_amp = seed;
         v->right_amp = 1.0 - v->left_amp;
      }
      seed = crandom(seed);
      v->overall_amp = minamp + (seed * ampdiff);
   }
#ifdef DEBUG
   printf("\n%d grainsamps\n", grainsamps);
   printf("Voices:\n");
   for (int i = 0, v = voices; i < nvoices; i++, v++)
      printf("%6d: index=%d, left=%g, right=%g, amp=%g\n",
             i, v->index, v->left_amp, v->right_amp, v->overall_amp);
#endif
   return 0;
}

/* -------------------------------------------- grain_input_and_transpose --- */
/* Reads part of a soundfile into a newly allocated array <grain>, transposing
   the signal as it's read, and shaping its amplitude with a window function.

   The method of transposition (adapted from the trans instrument) doesn't
   try to preserve duration. So we have a potential discrepancy between the
   input duration <indur> and the duration of that segment of sound after
   transposition. (For example, if transposition is down an octave, the
   length of the input segment after transposition will be <indur> * 2.)
   There are two ways to handle the discrepancy. Since both ways are useful,
   the caller can choose between them:
      - If <maintain_indur> is true, the grain length after transposition
        will be the same as that given by <indur>. This means that the amount
        actually read from the input file will be shorter or longer than
        <indur>, depending on the transposition.
      - If <maintain_indur> is false, the segment of sound specified by
        <indur> will be read, and the grain length will be shorter or longer
        than <indur>, depending on the transposition.

   <inchan> gives the input channel number to read, or if it's -1, specifies
   that all input channels will be averaged when read into the 1-channel array
   returned by the function. If the input file is mono, <inchan> is ignored.

   <WINDOW_FUNC_SLOT> is the makegen slot of a function used as an amplitude
   curve for the grain. Use gen 25 for a Hanning or Hamming window, or try
   gens 5 or 7 to make other envelope shapes.
*/
int JCHOR::grain_input_and_transpose()
{
   int     i, j, k, n, reset_count, inframes, bufframes;
   int     getflag, incount;
   float   read_indur, store_indur, total_indur, interval, grainamp = 0.0;
   double  increment, newsig, oldsig, oldersig, frac, counter;

   if (inputChannels() == 1)
      inchan = 0;

   interval = octpch(transpose);
   increment = cpsoct(10.0 + interval) / cpsoct(10.0);
   if (maintain_indur) {
      read_indur = (double)indur / increment;
      store_indur = indur;
   }
   else {
      read_indur = indur;
      store_indur = (double)indur / increment;
   }
#ifdef DEBUG
   printf("increment=%g, read_indur=%g, store_indur=%g\n",
           increment, read_indur, store_indur);
#endif
#ifdef NOT_YET
   total_indur = (float)m_DUR(NULL, 0);
   if (inskip < 0.0 || read_indur <= 0.0 || inskip + read_indur > total_indur)
      return die("JCHOR", "Input file segment out of range.");
#endif

   grainsamps = (int)(store_indur * SR + 0.5);

   grain = new float[grainsamps];

   tableset(SR, store_indur, winarraylen, wintabs);

   inframes = (int)(SR / read_indur);
   bufframes = RTBUFSAMPS;

   reset_count = (int) (SR / WINDOW_CONTROL_RATE);

   getflag = 1;
   incount = 0;            /* frames */
   counter = 0.0;
   oldersig = oldsig = newsig = 0.0;

   k = bufframes;
   for (i = j = 0; i < grainsamps; i++) {
      if (--j < 0) {
         grainamp = tablei(i, winarray, wintabs);
         j = reset_count;
      }
      while (getflag) {
         int index;
         if (k == bufframes) {              /* time for an input buffer */
            rtgetin(in, this, inputChannels() * bufframes);
            k = 0;
         }
         index = k * inputChannels();
         oldersig = oldsig;
         oldsig = newsig;
         if (inchan == AVERAGE_CHANS) {
            newsig = 0.0;
            for (n = 0; n < inputChannels(); n++)
               newsig += (double)in[index + n];
            newsig /= (double)inputChannels();
         }
         else
            newsig = (double)in[index + inchan];
         incount++;
         k++;
         if (counter - (float)incount < 0.5)
            getflag = 0;
      }
      frac = counter - (double)incount + 2.0;
      grain[i] = (float)interp(oldersig, oldsig, newsig, frac) * grainamp;
      counter += increment;
      if (counter - (float)incount >= -0.5)
         getflag = 1;
   }

   grain_done = true;

   return 0;
}

Instrument *makeJCHOR()
{
   JCHOR *inst;

   inst = new JCHOR();
   inst->set_bus_config("JCHOR");

   return inst;
}

void rtprofile()
{
   RT_INTRO("JCHOR", makeJCHOR);
}

<commit_msg>Fix a few casts.<commit_after>/* JCHOR - random-wait chorus instrument based on Paul Lansky's chor
           and Doug Scott's trans code

   p0  = output start time
   p1  = input start time
   p2  = output duration
   p3  = input duration (not input end time)
   p4  = maintain input duration, regardless of transposition (1: yes, 0: no)
   p5  = transposition (8ve.pc)
   p6  = number of voices (minimum of 1)
   p7  = minimum grain amplitude
   p8  = maximum grain amplitude
   p9  = minimum grain wait (seconds)
   p10 = maximum grain wait (seconds)
   p11 = seed (0 - 1)
   p12 = input channel [optional; if missing and input has > 1 chan, input
         channels averaged]
   p13 = overall amplitude multiplier [optional; if missing, must use gen 1 *]
   p14 = reference to grain envelope table [optional; if missing, must use
         gen 2 **]

   p7 (min amp), p8 (max amp), p9 (min wait), p10 (max wait) and p13 (amp mult)
   can receive dynamic updates from a table or real-time control source.

   Output can be either mono or stereo. If it's stereo, the program randomly
   distributes the voices across the stereo field.

   Notes on p4 (maintain input duration):

      Because the transposition method doesn't try to maintain duration -- it
      works like the speed control on a tape deck -- you have an option about
      the way to handle the duration of the input read:

      - If p4 is 1, the grain length after transposition will be the same as
        that given by p3 (input duration). This means that the amount actually
        read from the input file will be shorter or longer than p3, depending
        on the transposition.

      - If p4 is 0, the segment of sound specified by p3 will be read, and the
        grain length will be shorter or longer than p3, depending on the
        transposition.

   ----

   Notes about backward compatibility with pre-v4 scores:

   * If an old-style gen table 1 is present, its values will be multiplied
   by p13 (amplitude multiplier), even if the latter is dynamic.

   ** If p14 is missing, you must use an old-style gen table 2 for the
   grain envelope.

   ----

   Differences between JCHOR and chor (besides RT ability):
      - No limit on input duration or number of voices
      - Transpose the input signal
      - Specify the input channel to use (or an average of them)
      - Specify overall amplitude curve and grain window function

   John Gibson (jgg9c@virginia.edu), 9/20/98, RT'd 6/24/99; rev for v4, 7/24/04
*/
#include <stdio.h>
#include <stdlib.h>
#include <ugens.h>
#include <Instrument.h>
#include <PField.h>
#include "JCHOR.h"
#include <rt.h>
#include <rtdefs.h>

extern "C" {
   extern double m_DUR(float [], int);     /* in sys/minc_info.c */
}

//#define DEBUG

#define ENVELOPE_TABLE_SLOT  1
#define WINDOW_FUNC_SLOT     2
#define AVERAGE_CHANS        -1           // average input chans flag value
#define WINDOW_CONTROL_RATE  22050.0      // don't skimp on this

// local functions
static double interp(double, double, double, double);

JCHOR::JCHOR() : Instrument()
{
   in = NULL;
   winarray = NULL;
   winarraylen = 0;
   voices = NULL;
   grain = NULL;
   grain_done = false;
   branch = 0;
}

JCHOR::~JCHOR()
{
   delete [] in;
   delete [] voices;
   delete [] grain;
}

int JCHOR::init(double p[], int n_args)
{
   nargs = n_args;
   float outskip = p[0];
   inskip = p[1];
   float outdur = p[2];
   indur = p[3];
   maintain_indur = (bool) p[4];
   transpose = p[5];
   nvoices = (int) p[6];
   minamp = p[7];
   float maxamp = p[8];
   minwait = p[9];
   float maxwait = p[10];
   seed = p[11];
   inchan = (n_args > 12) ? (int) p[12] : AVERAGE_CHANS;

   if (n_args < 12)
      return die("JCHOR", "Not enough pfields.");

   if (rtsetinput(inskip, this) == -1)
      return DONT_SCHEDULE;
   if (rtsetoutput(outskip, outdur, this) == -1)
      return DONT_SCHEDULE;

   if (outputChannels() > 2)
      return die("JCHOR", "Output must have no more than two channels.");

   if (nvoices < 1)
      return die("JCHOR", "Must have at least one voice.");

   if (minamp < 0.0 || maxamp < 0.0 || minamp > maxamp)
      return die("JCHOR", "Grain amplitude range confused.");
   ampdiff = maxamp - minamp;

   if (minwait < 0.0 || maxwait < 0.0 || minwait > maxwait)
      return die("JCHOR", "Grain wait range confused.");
   waitdiff = (maxwait - minwait) * SR;
   minwait *= SR;

   if (seed < 0.0 || seed > 1.0)
      return die("JCHOR", "Seed must be between 0 and 1 inclusive.");

   amparray = floc(ENVELOPE_TABLE_SLOT);
   if (amparray) {
      int len = fsize(ENVELOPE_TABLE_SLOT);
      tableset(SR, outdur, len, amptabs);
   }

	if (n_args > 14) {      // handle table coming in as optional p14 TablePField
		winarray = (double *) getPFieldTable(14, &winarraylen);
	}
	if (winarray == NULL) {
      // MUST do this here, rather than in grain_input_and_transpose,
      // because by the time that is called, the makegen for this slot
      // may have changed.
      winarray = floc(WINDOW_FUNC_SLOT);
		if (winarray == NULL)
			return die("JCHOR", "Either use the grain envelope pfield (p14) "
                    "or make an old-style gen function in slot %d.",
                    WINDOW_FUNC_SLOT);
      winarraylen = fsize(WINDOW_FUNC_SLOT);
	}

   skip = (int) (SR / (float) resetval);

   return nSamps();
}

void JCHOR::doupdate()
{
   double p[14];
   update(p, 14, kMinAmp | kMaxAmp | kMinWait | kMaxWait | kAmp);

   minamp = p[7];
   float maxamp = p[8];
   if (minamp < 0.0)
      minamp = 0.0;
   if (maxamp < 0.0)
      maxamp = 0.0;
   if (maxamp < minamp)
      maxamp = minamp;
   ampdiff = maxamp - minamp;

   minwait = p[9];
   float maxwait = p[10];
   if (minwait < 0.0)
      minwait = 0.0;
   if (maxwait < 0.0)
      maxwait = 0.0;
   if (maxwait < minwait)
      maxwait = minwait;
   waitdiff = (maxwait - minwait) * SR;
   minwait *= SR;

   if (nargs > 13)
      amp = p[13];
   else
      amp = 1.0;
   if (amparray)
      amp *= tablei(currentFrame(), amparray, amptabs);
}

int JCHOR::configure()
{
   in = new float [RTBUFSAMPS * inputChannels()];
   return in ? 0 : -1;
}

int JCHOR::run()
{
   if (!grain_done) {
      grain_input_and_transpose();
      setup_voices();
   }

   for (int i = 0; i < framesToRun(); i++) {
      if (--branch <= 0) {
         doupdate();
         branch = skip;
      }

      float out[2];
      out[0] = out[1] = 0.0;

      Voice *v = voices;
      for (int j = 0; j < nvoices; j++, v++) {
         if (v->index++ < 0)
            continue;
         if (v->index >= grainsamps) {
            seed = crandom(seed);
            v->index = (int) -(minwait + (seed * waitdiff));
            if (outputChannels() > 1) {
               seed = crandom(seed);
               v->left_amp = seed;
               v->right_amp = 1.0 - v->left_amp;
            }
            seed = crandom(seed);
            v->overall_amp = minamp + (seed * ampdiff);
         }
         else {
            float sig = grain[v->index] * v->overall_amp;
            if (outputChannels() > 1) {
               out[0] += sig * v->left_amp;
               out[1] += sig * v->right_amp;
            }
            else
               out[0] += sig;
         }
      }
      out[0] *= amp;
      out[1] *= amp;

      rtaddout(out);
      increment();
   }

   return framesToRun();
}

/* --------------------------------------------------------------- interp --- */
/* Cubic spline interpolation.
   Nabbed from Doug Scott's trans.
*/
static double
interp(double y0, double y1, double y2, double t)
{
   register double hy2, hy0, a, b, c;

   a = y0;
   hy0 = y0 / 2.0;
   hy2 =  y2 / 2.0;
   b = (-3.0 * hy0) + (2.0 * y1) - hy2;
   c = hy0 - y1 + hy2;

   return (a + b*t + c*t*t);
}

/* --------------------------------------------------------- setup_voices --- */
int JCHOR::setup_voices()
{
   voices = new Voice[nvoices];

   Voice *v = voices;
   for (int i = 0; i < nvoices; i++, v++) {
      seed = crandom(seed);
      v->index = (int) (-seed * (grainsamps - 1));
      if (outputChannels() > 1) {
         seed = crandom(seed);
         v->left_amp = seed;
         v->right_amp = 1.0 - v->left_amp;
      }
      seed = crandom(seed);
      v->overall_amp = minamp + (seed * ampdiff);
   }
#ifdef DEBUG
   printf("\n%d grainsamps\n", grainsamps);
   printf("Voices:\n");
   for (int i = 0, v = voices; i < nvoices; i++, v++)
      printf("%6d: index=%d, left=%g, right=%g, amp=%g\n",
             i, v->index, v->left_amp, v->right_amp, v->overall_amp);
#endif
   return 0;
}

/* -------------------------------------------- grain_input_and_transpose --- */
/* Reads part of a soundfile into a newly allocated array <grain>, transposing
   the signal as it's read, and shaping its amplitude with a window function.

   The method of transposition (adapted from the trans instrument) doesn't
   try to preserve duration. So we have a potential discrepancy between the
   input duration <indur> and the duration of that segment of sound after
   transposition. (For example, if transposition is down an octave, the
   length of the input segment after transposition will be <indur> * 2.)
   There are two ways to handle the discrepancy. Since both ways are useful,
   the caller can choose between them:
      - If <maintain_indur> is true, the grain length after transposition
        will be the same as that given by <indur>. This means that the amount
        actually read from the input file will be shorter or longer than
        <indur>, depending on the transposition.
      - If <maintain_indur> is false, the segment of sound specified by
        <indur> will be read, and the grain length will be shorter or longer
        than <indur>, depending on the transposition.

   <inchan> gives the input channel number to read, or if it's -1, specifies
   that all input channels will be averaged when read into the 1-channel array
   returned by the function. If the input file is mono, <inchan> is ignored.

   <WINDOW_FUNC_SLOT> is the makegen slot of a function used as an amplitude
   curve for the grain. Use gen 25 for a Hanning or Hamming window, or try
   gens 5 or 7 to make other envelope shapes.
*/
int JCHOR::grain_input_and_transpose()
{
   int     i, j, k, n, reset_count, inframes, bufframes;
   int     getflag, incount;
   float   read_indur, store_indur, total_indur, interval, grainamp = 0.0;
   double  increment, newsig, oldsig, oldersig, frac, counter;

   if (inputChannels() == 1)
      inchan = 0;

   interval = octpch(transpose);
   increment = cpsoct(10.0 + interval) / cpsoct(10.0);
   if (maintain_indur) {
      read_indur = (double)indur / increment;
      store_indur = indur;
   }
   else {
      read_indur = indur;
      store_indur = (double)indur / increment;
   }
#ifdef DEBUG
   printf("increment=%g, read_indur=%g, store_indur=%g\n",
           increment, read_indur, store_indur);
#endif
#ifdef NOT_YET
   total_indur = (float)m_DUR(NULL, 0);
   if (inskip < 0.0 || read_indur <= 0.0 || inskip + read_indur > total_indur)
      return die("JCHOR", "Input file segment out of range.");
#endif

   grainsamps = (int)(store_indur * SR + 0.5);

   grain = new float[grainsamps];

   tableset(SR, store_indur, winarraylen, wintabs);

   inframes = (int)(SR / read_indur);
   bufframes = RTBUFSAMPS;

   reset_count = (int) (SR / WINDOW_CONTROL_RATE);

   getflag = 1;
   incount = 0;            /* frames */
   counter = 0.0;
   oldersig = oldsig = newsig = 0.0;

   k = bufframes;
   for (i = j = 0; i < grainsamps; i++) {
      if (--j < 0) {
         grainamp = tablei(i, winarray, wintabs);
         j = reset_count;
      }
      while (getflag) {
         int index;
         if (k == bufframes) {              /* time for an input buffer */
            rtgetin(in, this, inputChannels() * bufframes);
            k = 0;
         }
         index = k * inputChannels();
         oldersig = oldsig;
         oldsig = newsig;
         if (inchan == AVERAGE_CHANS) {
            newsig = 0.0;
            for (n = 0; n < inputChannels(); n++)
               newsig += (double)in[index + n];
            newsig /= (double)inputChannels();
         }
         else
            newsig = (double)in[index + inchan];
         incount++;
         k++;
         if (counter - (double)incount < 0.5)
            getflag = 0;
      }
      frac = counter - (double)incount + 2.0;
      grain[i] = (float)interp(oldersig, oldsig, newsig, frac) * grainamp;
      counter += increment;
      if (counter - (double)incount >= -0.5)
         getflag = 1;
   }

   grain_done = true;

   return 0;
}

Instrument *makeJCHOR()
{
   JCHOR *inst;

   inst = new JCHOR();
   inst->set_bus_config("JCHOR");

   return inst;
}

void rtprofile()
{
   RT_INTRO("JCHOR", makeJCHOR);
}

<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/captcha_view.h"

#include "app/l10n_util.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/login/image_downloader.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "views/controls/image_view.h"
#include "views/controls/label.h"
#include "views/grid_layout.h"
#include "views/standard_layout.h"
#include "views/widget/widget_gtk.h"
#include "views/window/window.h"

using views::Label;
using views::Textfield;
using views::WidgetGtk;

namespace chromeos {

CaptchaView::CaptchaView(const GURL& captcha_url)
    : delegate_(NULL),
      captcha_url_(captcha_url),
      captcha_image_(NULL),
      captcha_textfield_(NULL) {
}

bool CaptchaView::Accept() {
  if (delegate_)
    delegate_->OnCaptchaEntered(UTF16ToUTF8(captcha_textfield_->text()));
  return true;
}

std::wstring CaptchaView::GetWindowTitle() const {
  return l10n_util::GetString(IDS_LOGIN_CAPTCHA_DIALOG_TITLE);
}

gfx::Size CaptchaView::GetPreferredSize() {
  // TODO(nkostylev): Once UI is finalized, create locale settings.
  return gfx::Size(views::Window::GetLocalizedContentsSize(
      IDS_CAPTCHA_INPUT_DIALOG_WIDTH_CHARS,
      IDS_CAPTCHA_INPUT_DIALOG_HEIGHT_LINES));
}

void CaptchaView::ViewHierarchyChanged(bool is_add,
                                       views::View* parent,
                                       views::View* child) {
  // Can't init before we're inserted into a Container, because we require
  // a HWND to parent native child controls to.
  if (is_add && child == this)
    Init();
}

bool CaptchaView::HandleKeystroke(views::Textfield* sender,
    const views::Textfield::Keystroke& keystroke) {
  return false;
}

void CaptchaView::OnImageDecoded(const SkBitmap& decoded_image) {
  captcha_image_->SetImage(decoded_image);
  SchedulePaint();
  Layout();
}

void CaptchaView::Init() {
  views::GridLayout* layout = CreatePanelGridLayout(this);
  SetLayoutManager(layout);

  int column_view_set_id = 0;
  views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);
  layout->StartRow(0, column_view_set_id);
  Label* label =
      new views::Label(l10n_util::GetString(IDS_LOGIN_CAPTCHA_INSTRUCTIONS));
  label->SetMultiLine(true);
  layout->AddView(label);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  layout->StartRow(0, column_view_set_id);
  captcha_image_ = new views::ImageView();
  layout->AddView(captcha_image_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  layout->StartRow(0, column_view_set_id);
  captcha_textfield_ = new views::Textfield(
      views::Textfield::STYLE_DEFAULT);
  captcha_textfield_->SetController(this);
  layout->AddView(captcha_textfield_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  layout->StartRow(0, column_view_set_id);
  label = new views::Label(
      l10n_util::GetString(IDS_SYNC_GAIA_CAPTCHA_CASE_INSENSITIVE_TIP));
  label->SetMultiLine(true);
  layout->AddView(label);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  captcha_textfield_->RequestFocus();

  // ImageDownloader will disable itself once URL is fetched.
  new ImageDownloader(this, GURL(captcha_url_), std::string());
}

}  // namespace chromeos
<commit_msg>chromeos: Make Enter key works on the Captcha dialog.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/captcha_view.h"

#include "app/l10n_util.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chromeos/login/image_downloader.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "views/controls/image_view.h"
#include "views/controls/label.h"
#include "views/grid_layout.h"
#include "views/standard_layout.h"
#include "views/widget/widget_gtk.h"
#include "views/window/window.h"

using views::Label;
using views::Textfield;
using views::WidgetGtk;

namespace chromeos {

CaptchaView::CaptchaView(const GURL& captcha_url)
    : delegate_(NULL),
      captcha_url_(captcha_url),
      captcha_image_(NULL),
      captcha_textfield_(NULL) {
}

bool CaptchaView::Accept() {
  if (delegate_)
    delegate_->OnCaptchaEntered(UTF16ToUTF8(captcha_textfield_->text()));
  return true;
}

std::wstring CaptchaView::GetWindowTitle() const {
  return l10n_util::GetString(IDS_LOGIN_CAPTCHA_DIALOG_TITLE);
}

gfx::Size CaptchaView::GetPreferredSize() {
  // TODO(nkostylev): Once UI is finalized, create locale settings.
  return gfx::Size(views::Window::GetLocalizedContentsSize(
      IDS_CAPTCHA_INPUT_DIALOG_WIDTH_CHARS,
      IDS_CAPTCHA_INPUT_DIALOG_HEIGHT_LINES));
}

void CaptchaView::ViewHierarchyChanged(bool is_add,
                                       views::View* parent,
                                       views::View* child) {
  // Can't init before we're inserted into a Container, because we require
  // a HWND to parent native child controls to.
  if (is_add && child == this)
    Init();
}

bool CaptchaView::HandleKeystroke(views::Textfield* sender,
    const views::Textfield::Keystroke& keystroke) {
  if (sender == captcha_textfield_ &&
      keystroke.GetKeyboardCode() == app::VKEY_RETURN) {
    GetDialogClientView()->AcceptWindow();
  }
  return false;
}

void CaptchaView::OnImageDecoded(const SkBitmap& decoded_image) {
  captcha_image_->SetImage(decoded_image);
  SchedulePaint();
  Layout();
}

void CaptchaView::Init() {
  views::GridLayout* layout = CreatePanelGridLayout(this);
  SetLayoutManager(layout);

  int column_view_set_id = 0;
  views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                        views::GridLayout::USE_PREF, 0, 0);
  layout->StartRow(0, column_view_set_id);
  Label* label =
      new views::Label(l10n_util::GetString(IDS_LOGIN_CAPTCHA_INSTRUCTIONS));
  label->SetMultiLine(true);
  layout->AddView(label);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  layout->StartRow(0, column_view_set_id);
  captcha_image_ = new views::ImageView();
  layout->AddView(captcha_image_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  layout->StartRow(0, column_view_set_id);
  captcha_textfield_ = new views::Textfield(
      views::Textfield::STYLE_DEFAULT);
  captcha_textfield_->SetController(this);
  layout->AddView(captcha_textfield_);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  layout->StartRow(0, column_view_set_id);
  label = new views::Label(
      l10n_util::GetString(IDS_SYNC_GAIA_CAPTCHA_CASE_INSENSITIVE_TIP));
  label->SetMultiLine(true);
  layout->AddView(label);
  layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);

  captcha_textfield_->RequestFocus();

  // ImageDownloader will disable itself once URL is fetched.
  new ImageDownloader(this, GURL(captcha_url_), std::string());
}

}  // namespace chromeos
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/user_manager.h"

#include "app/resource_bundle.h"
#include "base/compiler_specific.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/nss_util.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/input_method_library.h"
#include "chrome/browser/chromeos/login/ownership_service.h"
#include "chrome/browser/chromeos/wm_ipc.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "gfx/codec/png_codec.h"
#include "grit/theme_resources.h"

namespace chromeos {

namespace {

// A vector pref of the users who have logged into the device.
const char kLoggedInUsers[] = "LoggedInUsers";
// A dictionary that maps usernames to file paths to their images.
const char kUserImages[] = "UserImages";

// Incognito user is represented by an empty string (since some code already
// depends on that and it's hard to figure out what).
const char kIncognitoUser[] = "";

// Special pathes to default user images.
const char* kDefaultImageNames[] = {
    "default:blue",
    "default:green",
    "default:yellow",
    "default:red",
};

// Resource IDs of default user images.
const int kDefaultImageResources[] = {
  IDR_LOGIN_DEFAULT_USER_1,
  IDR_LOGIN_DEFAULT_USER_2,
  IDR_LOGIN_DEFAULT_USER_3,
  IDR_LOGIN_DEFAULT_USER_4
};

// The one true UserManager.
static UserManager* user_manager_ = NULL;

// Stores path to the image in local state. Runs on UI thread.
void SavePathToLocalState(const std::string& username,
                          const std::string& image_path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  PrefService* local_state = g_browser_process->local_state();
  DictionaryValue* images =
      local_state->GetMutableDictionary(kUserImages);
  images->SetWithoutPathExpansion(username, new StringValue(image_path));
  DLOG(INFO) << "Saving path to user image in Local State.";
  local_state->SavePersistentPrefs();
}

// Saves image to file with specified path. Runs on FILE thread.
// Posts task for saving image path to local state on UI thread.
void SaveImageToFile(const SkBitmap& image,
                     const FilePath& image_path,
                     const std::string& username) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  std::vector<unsigned char> encoded_image;
  if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, true, &encoded_image)) {
    LOG(ERROR) << "Failed to PNG encode the image.";
    return;
  }

  if (file_util::WriteFile(image_path,
                           reinterpret_cast<char*>(&encoded_image[0]),
                           encoded_image.size()) == -1) {
    LOG(ERROR) << "Failed to save image to file.";
    return;
  }

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableFunction(&SavePathToLocalState,
                          username, image_path.value()));
}

// Checks if given path is one of the default ones. If it is, returns true
// and its index in kDefaultImageNames through |image_id|. If not, returns
// false.
bool IsDefaultImagePath(const std::string& path, size_t* image_id) {
  DCHECK(image_id);
  for (size_t i = 0; i < arraysize(kDefaultImageNames); ++i) {
    if (path == kDefaultImageNames[i]) {
      *image_id = i;
      return true;
    }
  }
  return false;
}

// Checks current user's ownership on file thread.
void CheckOwnership() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  UserManager::Get()->set_current_user_is_owner(
      OwnershipService::GetSharedInstance()->CurrentUserIsOwner());
}

}  // namespace

UserManager::User::User() {
  image_ = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
      IDR_LOGIN_DEFAULT_USER);
}

std::string UserManager::User::GetDisplayName() const {
  size_t i = email_.find('@');
  if (i == 0 || i == std::string::npos) {
    return email_;
  }
  return email_.substr(0, i);
}

// static
UserManager* UserManager::Get() {
  if (!user_manager_)
    user_manager_ = new UserManager();
  return user_manager_;
}

// static
void UserManager::RegisterPrefs(PrefService* local_state) {
  local_state->RegisterListPref(kLoggedInUsers);
  local_state->RegisterDictionaryPref(kUserImages);
}

std::vector<UserManager::User> UserManager::GetUsers() const {
  std::vector<User> users;
  if (!g_browser_process)
    return users;

  PrefService* local_state = g_browser_process->local_state();
  const ListValue* prefs_users = local_state->GetList(kLoggedInUsers);
  const DictionaryValue* prefs_images =
      local_state->GetDictionary(kUserImages);

  if (prefs_users) {
    for (ListValue::const_iterator it = prefs_users->begin();
         it != prefs_users->end();
         ++it) {
      std::string email;
      if ((*it)->GetAsString(&email)) {
        User user;
        user.set_email(email);
        UserImages::const_iterator image_it = user_images_.find(email);
        std::string image_path;
        if (image_it == user_images_.end()) {
          if (prefs_images &&
              prefs_images->GetStringWithoutPathExpansion(email, &image_path)) {
            size_t default_image_id = arraysize(kDefaultImageNames);
            if (IsDefaultImagePath(image_path, &default_image_id)) {
              DCHECK(default_image_id < arraysize(kDefaultImageNames));
              int resource_id = kDefaultImageResources[default_image_id];
              user.set_image(
                  *ResourceBundle::GetSharedInstance().GetBitmapNamed(
                      resource_id));
              user_images_[email] = user.image();
            } else {
              // Insert the default image so we don't send another request if
              // GetUsers is called twice.
              user_images_[email] = user.image();
              image_loader_->Start(email, image_path);
            }
          }
        } else {
          user.set_image(image_it->second);
        }
        users.push_back(user);
      }
    }
  }
  return users;
}

void UserManager::OffTheRecordUserLoggedIn() {
  logged_in_user_ = User();
  logged_in_user_.set_email(kIncognitoUser);
  NotifyOnLogin();
}

void UserManager::UserLoggedIn(const std::string& email) {
  if (email == kIncognitoUser) {
    OffTheRecordUserLoggedIn();
    return;
  }

  // Get a copy of the current users.
  std::vector<User> users = GetUsers();

  // Clear the prefs view of the users.
  PrefService* prefs = g_browser_process->local_state();
  ListValue* prefs_users = prefs->GetMutableList(kLoggedInUsers);
  prefs_users->Clear();

  logged_in_user_.set_email(email);

  // Make sure this user is first.
  prefs_users->Append(Value::CreateStringValue(email));
  for (std::vector<User>::iterator it = users.begin();
       it != users.end();
       ++it) {
    std::string user_email = it->email();
    // Skip the most recent user.
    if (email != user_email) {
      prefs_users->Append(Value::CreateStringValue(user_email));
    } else {
      logged_in_user_ = *it;
    }
  }
  prefs->SavePersistentPrefs();
  NotifyOnLogin();
}

void UserManager::RemoveUser(const std::string& email) {
  // Get a copy of the current users.
  std::vector<User> users = GetUsers();

  // Clear the prefs view of the users.
  PrefService* prefs = g_browser_process->local_state();
  ListValue* prefs_users = prefs->GetMutableList(kLoggedInUsers);
  prefs_users->Clear();

  for (std::vector<User>::iterator it = users.begin();
       it != users.end();
       ++it) {
    std::string user_email = it->email();
    // Skip user that we would like to delete.
    if (email != user_email)
      prefs_users->Append(Value::CreateStringValue(user_email));
  }
  prefs->SavePersistentPrefs();
}

bool UserManager::IsKnownUser(const std::string& email) {
  std::vector<User> users = GetUsers();
  for (std::vector<User>::iterator it = users.begin();
       it < users.end();
       ++it) {
    if (it->email() == email)
      return true;
  }

  return false;
}

void UserManager::SetLoggedInUserImage(const SkBitmap& image) {
  if (logged_in_user_.email().empty())
    return;
  logged_in_user_.set_image(image);
  OnImageLoaded(logged_in_user_.email(), image);
}

void UserManager::SaveUserImage(const std::string& username,
                                const SkBitmap& image) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  std::string filename = username + ".png";
  FilePath user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  FilePath image_path = user_data_dir.AppendASCII(filename);
  DLOG(INFO) << "Saving user image to " << image_path.value();

  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      NewRunnableFunction(&SaveImageToFile,
                          image, image_path, username));
}

void UserManager::SetDefaultUserImage(const std::string& username) {
  if (!g_browser_process)
    return;

  PrefService* local_state = g_browser_process->local_state();
  DCHECK(local_state);
  const ListValue* prefs_users = local_state->GetList(kLoggedInUsers);
  DCHECK(prefs_users);
  const DictionaryValue* prefs_images =
      local_state->GetDictionary(kUserImages);
  DCHECK(prefs_images);

  // We want to distribute default images between users uniformly so that if
  // there're more users with red image, we won't add red one for sure.
  // Thus we count how many default images of each color are used and choose
  // the first color with minimal usage.
  std::vector<int> colors_count(arraysize(kDefaultImageNames), 0);
  for (ListValue::const_iterator it = prefs_users->begin();
       it != prefs_users->end();
       ++it) {
    std::string email;
    if ((*it)->GetAsString(&email)) {
      std::string image_path;
      size_t default_image_id = arraysize(kDefaultImageNames);
      if (prefs_images->GetStringWithoutPathExpansion(email, &image_path) &&
          IsDefaultImagePath(image_path, &default_image_id)) {
        DCHECK(default_image_id < arraysize(kDefaultImageNames));
        ++colors_count[default_image_id];
      }
    }
  }
  std::vector<int>::const_iterator min_it =
      std::min_element(colors_count.begin(), colors_count.end());
  int selected_id = min_it - colors_count.begin();
  std::string user_image_path = kDefaultImageNames[selected_id];
  int resource_id = kDefaultImageResources[selected_id];
  SkBitmap user_image = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
      resource_id);

  SavePathToLocalState(username, user_image_path);
  SetLoggedInUserImage(user_image);
}

void UserManager::OnImageLoaded(const std::string& username,
                                const SkBitmap& image) {
  DLOG(INFO) << "Loaded image for " << username;
  user_images_[username] = image;
  User user;
  user.set_email(username);
  user.set_image(image);
  NotificationService::current()->Notify(
      NotificationType::LOGIN_USER_IMAGE_CHANGED,
      Source<UserManager>(this),
      Details<const User>(&user));
}

// Private constructor and destructor. Do nothing.
UserManager::UserManager()
    : ALLOW_THIS_IN_INITIALIZER_LIST(image_loader_(new UserImageLoader(this))),
      current_user_is_owner_(false) {
  registrar_.Add(this, NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED,
      NotificationService::AllSources());
}

UserManager::~UserManager() {
  image_loader_->set_delegate(NULL);
}

void UserManager::NotifyOnLogin() {
  NotificationService::current()->Notify(
      NotificationType::LOGIN_USER_CHANGED,
      Source<UserManager>(this),
      Details<const User>(&logged_in_user_));

  chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
      SetDeferImeStartup(false);
  // Shut down the IME so that it will reload the user's settings.
  chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
      StopInputMethodProcesses();
  // Let the window manager know that we're logged in now.
  WmIpc::instance()->SetLoggedInProperty(true);
  // Ensure we've opened the real user's key/certificate database.
  base::OpenPersistentNSSDB();

  // Schedules current user ownership check on file thread.
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(&CheckOwnership));
}

void UserManager::Observe(NotificationType type,
                          const NotificationSource& source,
                          const NotificationDetails& details) {
  if (type == NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) {
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        NewRunnableFunction(&CheckOwnership));
  }
}

}  // namespace chromeos
<commit_msg>Correct call to create dictionary in Local State if it doesn't exist.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/user_manager.h"

#include "app/resource_bundle.h"
#include "base/compiler_specific.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/nss_util.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/input_method_library.h"
#include "chrome/browser/chromeos/login/ownership_service.h"
#include "chrome/browser/chromeos/wm_ipc.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "gfx/codec/png_codec.h"
#include "grit/theme_resources.h"

namespace chromeos {

namespace {

// A vector pref of the users who have logged into the device.
const char kLoggedInUsers[] = "LoggedInUsers";
// A dictionary that maps usernames to file paths to their images.
const char kUserImages[] = "UserImages";

// Incognito user is represented by an empty string (since some code already
// depends on that and it's hard to figure out what).
const char kIncognitoUser[] = "";

// Special pathes to default user images.
const char* kDefaultImageNames[] = {
    "default:blue",
    "default:green",
    "default:yellow",
    "default:red",
};

// Resource IDs of default user images.
const int kDefaultImageResources[] = {
  IDR_LOGIN_DEFAULT_USER_1,
  IDR_LOGIN_DEFAULT_USER_2,
  IDR_LOGIN_DEFAULT_USER_3,
  IDR_LOGIN_DEFAULT_USER_4
};

// The one true UserManager.
static UserManager* user_manager_ = NULL;

// Stores path to the image in local state. Runs on UI thread.
void SavePathToLocalState(const std::string& username,
                          const std::string& image_path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  PrefService* local_state = g_browser_process->local_state();
  DictionaryValue* images =
      local_state->GetMutableDictionary(kUserImages);
  images->SetWithoutPathExpansion(username, new StringValue(image_path));
  DLOG(INFO) << "Saving path to user image in Local State.";
  local_state->SavePersistentPrefs();
}

// Saves image to file with specified path. Runs on FILE thread.
// Posts task for saving image path to local state on UI thread.
void SaveImageToFile(const SkBitmap& image,
                     const FilePath& image_path,
                     const std::string& username) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  std::vector<unsigned char> encoded_image;
  if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, true, &encoded_image)) {
    LOG(ERROR) << "Failed to PNG encode the image.";
    return;
  }

  if (file_util::WriteFile(image_path,
                           reinterpret_cast<char*>(&encoded_image[0]),
                           encoded_image.size()) == -1) {
    LOG(ERROR) << "Failed to save image to file.";
    return;
  }

  BrowserThread::PostTask(
      BrowserThread::UI,
      FROM_HERE,
      NewRunnableFunction(&SavePathToLocalState,
                          username, image_path.value()));
}

// Checks if given path is one of the default ones. If it is, returns true
// and its index in kDefaultImageNames through |image_id|. If not, returns
// false.
bool IsDefaultImagePath(const std::string& path, size_t* image_id) {
  DCHECK(image_id);
  for (size_t i = 0; i < arraysize(kDefaultImageNames); ++i) {
    if (path == kDefaultImageNames[i]) {
      *image_id = i;
      return true;
    }
  }
  return false;
}

// Checks current user's ownership on file thread.
void CheckOwnership() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));

  UserManager::Get()->set_current_user_is_owner(
      OwnershipService::GetSharedInstance()->CurrentUserIsOwner());
}

}  // namespace

UserManager::User::User() {
  image_ = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
      IDR_LOGIN_DEFAULT_USER);
}

std::string UserManager::User::GetDisplayName() const {
  size_t i = email_.find('@');
  if (i == 0 || i == std::string::npos) {
    return email_;
  }
  return email_.substr(0, i);
}

// static
UserManager* UserManager::Get() {
  if (!user_manager_)
    user_manager_ = new UserManager();
  return user_manager_;
}

// static
void UserManager::RegisterPrefs(PrefService* local_state) {
  local_state->RegisterListPref(kLoggedInUsers);
  local_state->RegisterDictionaryPref(kUserImages);
}

std::vector<UserManager::User> UserManager::GetUsers() const {
  std::vector<User> users;
  if (!g_browser_process)
    return users;

  PrefService* local_state = g_browser_process->local_state();
  const ListValue* prefs_users = local_state->GetList(kLoggedInUsers);
  const DictionaryValue* prefs_images =
      local_state->GetDictionary(kUserImages);

  if (prefs_users) {
    for (ListValue::const_iterator it = prefs_users->begin();
         it != prefs_users->end();
         ++it) {
      std::string email;
      if ((*it)->GetAsString(&email)) {
        User user;
        user.set_email(email);
        UserImages::const_iterator image_it = user_images_.find(email);
        std::string image_path;
        if (image_it == user_images_.end()) {
          if (prefs_images &&
              prefs_images->GetStringWithoutPathExpansion(email, &image_path)) {
            size_t default_image_id = arraysize(kDefaultImageNames);
            if (IsDefaultImagePath(image_path, &default_image_id)) {
              DCHECK(default_image_id < arraysize(kDefaultImageNames));
              int resource_id = kDefaultImageResources[default_image_id];
              user.set_image(
                  *ResourceBundle::GetSharedInstance().GetBitmapNamed(
                      resource_id));
              user_images_[email] = user.image();
            } else {
              // Insert the default image so we don't send another request if
              // GetUsers is called twice.
              user_images_[email] = user.image();
              image_loader_->Start(email, image_path);
            }
          }
        } else {
          user.set_image(image_it->second);
        }
        users.push_back(user);
      }
    }
  }
  return users;
}

void UserManager::OffTheRecordUserLoggedIn() {
  logged_in_user_ = User();
  logged_in_user_.set_email(kIncognitoUser);
  NotifyOnLogin();
}

void UserManager::UserLoggedIn(const std::string& email) {
  if (email == kIncognitoUser) {
    OffTheRecordUserLoggedIn();
    return;
  }

  // Get a copy of the current users.
  std::vector<User> users = GetUsers();

  // Clear the prefs view of the users.
  PrefService* prefs = g_browser_process->local_state();
  ListValue* prefs_users = prefs->GetMutableList(kLoggedInUsers);
  prefs_users->Clear();

  logged_in_user_.set_email(email);

  // Make sure this user is first.
  prefs_users->Append(Value::CreateStringValue(email));
  for (std::vector<User>::iterator it = users.begin();
       it != users.end();
       ++it) {
    std::string user_email = it->email();
    // Skip the most recent user.
    if (email != user_email) {
      prefs_users->Append(Value::CreateStringValue(user_email));
    } else {
      logged_in_user_ = *it;
    }
  }
  prefs->SavePersistentPrefs();
  NotifyOnLogin();
}

void UserManager::RemoveUser(const std::string& email) {
  // Get a copy of the current users.
  std::vector<User> users = GetUsers();

  // Clear the prefs view of the users.
  PrefService* prefs = g_browser_process->local_state();
  ListValue* prefs_users = prefs->GetMutableList(kLoggedInUsers);
  prefs_users->Clear();

  for (std::vector<User>::iterator it = users.begin();
       it != users.end();
       ++it) {
    std::string user_email = it->email();
    // Skip user that we would like to delete.
    if (email != user_email)
      prefs_users->Append(Value::CreateStringValue(user_email));
  }
  prefs->SavePersistentPrefs();
}

bool UserManager::IsKnownUser(const std::string& email) {
  std::vector<User> users = GetUsers();
  for (std::vector<User>::iterator it = users.begin();
       it < users.end();
       ++it) {
    if (it->email() == email)
      return true;
  }

  return false;
}

void UserManager::SetLoggedInUserImage(const SkBitmap& image) {
  if (logged_in_user_.email().empty())
    return;
  logged_in_user_.set_image(image);
  OnImageLoaded(logged_in_user_.email(), image);
}

void UserManager::SaveUserImage(const std::string& username,
                                const SkBitmap& image) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  std::string filename = username + ".png";
  FilePath user_data_dir;
  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
  FilePath image_path = user_data_dir.AppendASCII(filename);
  DLOG(INFO) << "Saving user image to " << image_path.value();

  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      NewRunnableFunction(&SaveImageToFile,
                          image, image_path, username));
}

void UserManager::SetDefaultUserImage(const std::string& username) {
  if (!g_browser_process)
    return;

  PrefService* local_state = g_browser_process->local_state();
  DCHECK(local_state);
  const ListValue* prefs_users = local_state->GetList(kLoggedInUsers);
  DCHECK(prefs_users);
  const DictionaryValue* prefs_images =
      local_state->GetMutableDictionary(kUserImages);
  DCHECK(prefs_images);

  // We want to distribute default images between users uniformly so that if
  // there're more users with red image, we won't add red one for sure.
  // Thus we count how many default images of each color are used and choose
  // the first color with minimal usage.
  std::vector<int> colors_count(arraysize(kDefaultImageNames), 0);
  for (ListValue::const_iterator it = prefs_users->begin();
       it != prefs_users->end();
       ++it) {
    std::string email;
    if ((*it)->GetAsString(&email)) {
      std::string image_path;
      size_t default_image_id = arraysize(kDefaultImageNames);
      if (prefs_images->GetStringWithoutPathExpansion(email, &image_path) &&
          IsDefaultImagePath(image_path, &default_image_id)) {
        DCHECK(default_image_id < arraysize(kDefaultImageNames));
        ++colors_count[default_image_id];
      }
    }
  }
  std::vector<int>::const_iterator min_it =
      std::min_element(colors_count.begin(), colors_count.end());
  int selected_id = min_it - colors_count.begin();
  std::string user_image_path = kDefaultImageNames[selected_id];
  int resource_id = kDefaultImageResources[selected_id];
  SkBitmap user_image = *ResourceBundle::GetSharedInstance().GetBitmapNamed(
      resource_id);

  SavePathToLocalState(username, user_image_path);
  SetLoggedInUserImage(user_image);
}

void UserManager::OnImageLoaded(const std::string& username,
                                const SkBitmap& image) {
  DLOG(INFO) << "Loaded image for " << username;
  user_images_[username] = image;
  User user;
  user.set_email(username);
  user.set_image(image);
  NotificationService::current()->Notify(
      NotificationType::LOGIN_USER_IMAGE_CHANGED,
      Source<UserManager>(this),
      Details<const User>(&user));
}

// Private constructor and destructor. Do nothing.
UserManager::UserManager()
    : ALLOW_THIS_IN_INITIALIZER_LIST(image_loader_(new UserImageLoader(this))),
      current_user_is_owner_(false) {
  registrar_.Add(this, NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED,
      NotificationService::AllSources());
}

UserManager::~UserManager() {
  image_loader_->set_delegate(NULL);
}

void UserManager::NotifyOnLogin() {
  NotificationService::current()->Notify(
      NotificationType::LOGIN_USER_CHANGED,
      Source<UserManager>(this),
      Details<const User>(&logged_in_user_));

  chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
      SetDeferImeStartup(false);
  // Shut down the IME so that it will reload the user's settings.
  chromeos::CrosLibrary::Get()->GetInputMethodLibrary()->
      StopInputMethodProcesses();
  // Let the window manager know that we're logged in now.
  WmIpc::instance()->SetLoggedInProperty(true);
  // Ensure we've opened the real user's key/certificate database.
  base::OpenPersistentNSSDB();

  // Schedules current user ownership check on file thread.
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(&CheckOwnership));
}

void UserManager::Observe(NotificationType type,
                          const NotificationSource& source,
                          const NotificationDetails& details) {
  if (type == NotificationType::OWNER_KEY_FETCH_ATTEMPT_SUCCEEDED) {
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
        NewRunnableFunction(&CheckOwnership));
  }
}

}  // namespace chromeos
<|endoftext|>