Home | History | Annotate | Download | only in autofill
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/autofill/personal_data_manager.h"
      6 
      7 #import <AddressBook/AddressBook.h>
      8 
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/memory/scoped_vector.h"
     12 #include "base/sys_string_conversions.h"
     13 #include "chrome/browser/autofill/autofill_profile.h"
     14 #include "chrome/browser/autofill/phone_number.h"
     15 #include "chrome/common/guid.h"
     16 #include "grit/generated_resources.h"
     17 #include "ui/base/l10n/l10n_util_mac.h"
     18 
     19 namespace {
     20 
     21 // This implementation makes use of the Address Book API.  Profiles are
     22 // generated that correspond to addresses in the "me" card that reside in the
     23 // user's Address Book.  The caller passes a vector of profiles into the
     24 // the constructer and then initiate the fetch from the Mac Address Book "me"
     25 // card using the main |GetAddressBookMeCard()| method.  This clears any
     26 // existing addresses and populates new addresses derived from the data found
     27 // in the "me" card.
     28 class AuxiliaryProfilesImpl {
     29  public:
     30   // Constructor takes a reference to the |profiles| that will be filled in
     31   // by the subsequent call to |GetAddressBookMeCard()|.  |profiles| may not
     32   // be NULL.
     33   explicit AuxiliaryProfilesImpl(ScopedVector<AutofillProfile>* profiles)
     34       : profiles_(*profiles) {
     35   }
     36   virtual ~AuxiliaryProfilesImpl() {}
     37 
     38   // Import the "me" card from the Mac Address Book and fill in |profiles_|.
     39   void GetAddressBookMeCard();
     40 
     41  private:
     42   void GetAddressBookNames(ABPerson* me,
     43                            NSString* addressLabelRaw,
     44                            AutofillProfile* profile);
     45   void GetAddressBookAddresses(NSDictionary* address, AutofillProfile* profile);
     46   void GetAddressBookEmail(ABPerson* me,
     47                            NSString* addressLabelRaw,
     48                            AutofillProfile* profile);
     49   void GetAddressBookPhoneNumbers(ABPerson* me,
     50                                   NSString* addressLabelRaw,
     51                                   AutofillProfile* profile);
     52 
     53  private:
     54   // A reference to the profiles this class populates.
     55   ScopedVector<AutofillProfile>& profiles_;
     56 
     57   DISALLOW_COPY_AND_ASSIGN(AuxiliaryProfilesImpl);
     58 };
     59 
     60 // This method uses the |ABAddressBook| system service to fetch the "me" card
     61 // from the active user's address book.  It looks for the user address
     62 // information and translates it to the internal list of |AutofillProfile| data
     63 // structures.
     64 void AuxiliaryProfilesImpl::GetAddressBookMeCard() {
     65   profiles_.reset();
     66 
     67   ABAddressBook* addressBook = [ABAddressBook sharedAddressBook];
     68   ABPerson* me = [addressBook me];
     69   if (me) {
     70     ABMultiValue* addresses = [me valueForProperty:kABAddressProperty];
     71     for (NSUInteger i = 0, count = [addresses count]; i < count; i++) {
     72       NSDictionary* address = [addresses valueAtIndex:i];
     73       NSString* addressLabelRaw = [addresses labelAtIndex:i];
     74 
     75       // Create a new profile where the guid is set to the guid portion of the
     76       // |kABUIDProperty| taken from from the "me" address.  The format of
     77       // the |kABUIDProperty| is "<guid>:ABPerson", so we're stripping off the
     78       // raw guid here and using it directly.
     79       const size_t kGUIDLength = 36U;
     80       std::string guid = base::SysNSStringToUTF8(
     81           [me valueForProperty:kABUIDProperty]).substr(0, kGUIDLength);
     82       scoped_ptr<AutofillProfile> profile(new AutofillProfile(guid));
     83       DCHECK(guid::IsValidGUID(profile->guid()));
     84 
     85       // Fill in name and company information.
     86       GetAddressBookNames(me, addressLabelRaw, profile.get());
     87 
     88       // Fill in address information.
     89       GetAddressBookAddresses(address, profile.get());
     90 
     91       // Fill in email information.
     92       GetAddressBookEmail(me, addressLabelRaw, profile.get());
     93 
     94       // Fill in phone and fax number information.
     95       GetAddressBookPhoneNumbers(me, addressLabelRaw, profile.get());
     96 
     97       profiles_.push_back(profile.release());
     98     }
     99   }
    100 }
    101 
    102 // Name and company information is stored once in the Address Book against
    103 // multiple addresses.  We replicate that information for each profile.
    104 // We only propagate the company name to work profiles.
    105 void AuxiliaryProfilesImpl::GetAddressBookNames(
    106     ABPerson* me,
    107     NSString* addressLabelRaw,
    108     AutofillProfile* profile) {
    109   NSString* firstName = [me valueForProperty:kABFirstNameProperty];
    110   NSString* middleName = [me valueForProperty:kABMiddleNameProperty];
    111   NSString* lastName = [me valueForProperty:kABLastNameProperty];
    112   NSString* companyName = [me valueForProperty:kABOrganizationProperty];
    113 
    114   profile->SetInfo(NAME_FIRST, base::SysNSStringToUTF16(firstName));
    115   profile->SetInfo(NAME_MIDDLE, base::SysNSStringToUTF16(middleName));
    116   profile->SetInfo(NAME_LAST, base::SysNSStringToUTF16(lastName));
    117   if ([addressLabelRaw isEqualToString:kABAddressWorkLabel])
    118     profile->SetInfo(COMPANY_NAME, base::SysNSStringToUTF16(companyName));
    119 }
    120 
    121 // Addresss information from the Address Book may span multiple lines.
    122 // If it does then we represent the address with two lines in the profile.  The
    123 // second line we join with commas.
    124 // For example:  "c/o John Doe\n1122 Other Avenue\nApt #7" translates to
    125 // line 1: "c/o John Doe", line 2: "1122 Other Avenue, Apt #7".
    126 void AuxiliaryProfilesImpl::GetAddressBookAddresses(
    127     NSDictionary* address,
    128     AutofillProfile* profile) {
    129   if (NSString* addressField = [address objectForKey:kABAddressStreetKey]) {
    130     // If there are newlines in the address, split into two lines.
    131     if ([addressField rangeOfCharacterFromSet:
    132             [NSCharacterSet newlineCharacterSet]].location != NSNotFound) {
    133       NSArray* chunks = [addressField componentsSeparatedByCharactersInSet:
    134           [NSCharacterSet newlineCharacterSet]];
    135       DCHECK([chunks count] > 1);
    136 
    137       NSString* separator = l10n_util::GetNSString(
    138             IDS_AUTOFILL_MAC_ADDRESS_LINE_SEPARATOR);
    139 
    140       NSString* addressField1 = [chunks objectAtIndex:0];
    141       NSString* addressField2 =
    142           [[chunks subarrayWithRange:NSMakeRange(1, [chunks count] - 1)]
    143               componentsJoinedByString:separator];
    144       profile->SetInfo(ADDRESS_HOME_LINE1,
    145                        base::SysNSStringToUTF16(addressField1));
    146       profile->SetInfo(ADDRESS_HOME_LINE2,
    147                        base::SysNSStringToUTF16(addressField2));
    148     } else {
    149       profile->SetInfo(ADDRESS_HOME_LINE1,
    150                        base::SysNSStringToUTF16(addressField));
    151     }
    152   }
    153 
    154   if (NSString* city = [address objectForKey:kABAddressCityKey])
    155     profile->SetInfo(ADDRESS_HOME_CITY, base::SysNSStringToUTF16(city));
    156   if (NSString* state = [address objectForKey:kABAddressStateKey])
    157     profile->SetInfo(ADDRESS_HOME_STATE, base::SysNSStringToUTF16(state));
    158   if (NSString* zip = [address objectForKey:kABAddressZIPKey])
    159     profile->SetInfo(ADDRESS_HOME_ZIP, base::SysNSStringToUTF16(zip));
    160   if (NSString* country = [address objectForKey:kABAddressCountryKey])
    161     profile->SetInfo(ADDRESS_HOME_COUNTRY, base::SysNSStringToUTF16(country));
    162 }
    163 
    164 // Fills in email address matching current address label.  Note that there may
    165 // be multiple matching email addresses for a given label.  We take the
    166 // first we find (topmost) as preferred.
    167 void AuxiliaryProfilesImpl::GetAddressBookEmail(
    168     ABPerson* me,
    169     NSString* addressLabelRaw,
    170     AutofillProfile* profile) {
    171   ABMultiValue* emailAddresses = [me valueForProperty:kABEmailProperty];
    172   NSString* emailAddress = nil;
    173   for (NSUInteger j = 0, emailCount = [emailAddresses count];
    174        j < emailCount; j++) {
    175     NSString* emailAddressLabelRaw = [emailAddresses labelAtIndex:j];
    176     if ([emailAddressLabelRaw isEqualToString:addressLabelRaw]) {
    177       emailAddress = [emailAddresses valueAtIndex:j];
    178       break;
    179     }
    180   }
    181   profile->SetInfo(EMAIL_ADDRESS, base::SysNSStringToUTF16(emailAddress));
    182 }
    183 
    184 // Fills in telephone numbers.  Each of these are special cases.
    185 // We match four cases: home/tel, home/fax, work/tel, work/fax.
    186 // Note, we traverse in reverse order so that top values in address book
    187 // take priority.
    188 void AuxiliaryProfilesImpl::GetAddressBookPhoneNumbers(
    189     ABPerson* me,
    190     NSString* addressLabelRaw,
    191     AutofillProfile* profile) {
    192   string16 number;
    193   string16 city_code;
    194   string16 country_code;
    195 
    196   ABMultiValue* phoneNumbers = [me valueForProperty:kABPhoneProperty];
    197   for (NSUInteger k = 0, phoneCount = [phoneNumbers count];
    198        k < phoneCount; k++) {
    199     NSUInteger reverseK = phoneCount - k - 1;
    200     NSString* phoneLabelRaw = [phoneNumbers labelAtIndex:reverseK];
    201     if ([addressLabelRaw isEqualToString:kABAddressHomeLabel] &&
    202         [phoneLabelRaw isEqualToString:kABPhoneHomeLabel]) {
    203       string16 homePhone = base::SysNSStringToUTF16(
    204           [phoneNumbers valueAtIndex:reverseK]);
    205       PhoneNumber::ParsePhoneNumber(
    206           homePhone, &number, &city_code, &country_code);
    207       profile->SetInfo(PHONE_HOME_NUMBER, number);
    208       profile->SetInfo(PHONE_HOME_CITY_CODE, city_code);
    209       profile->SetInfo(PHONE_HOME_COUNTRY_CODE, country_code);
    210     } else if ([addressLabelRaw isEqualToString:kABAddressHomeLabel] &&
    211                [phoneLabelRaw isEqualToString:kABPhoneHomeFAXLabel]) {
    212       string16 homeFax = base::SysNSStringToUTF16(
    213           [phoneNumbers valueAtIndex:reverseK]);
    214       PhoneNumber::ParsePhoneNumber(homeFax,
    215           &number, &city_code, &country_code);
    216       profile->SetInfo(PHONE_FAX_NUMBER, number);
    217       profile->SetInfo(PHONE_FAX_CITY_CODE, city_code);
    218       profile->SetInfo(PHONE_FAX_COUNTRY_CODE, country_code);
    219     } else if ([addressLabelRaw isEqualToString:kABAddressWorkLabel] &&
    220                [phoneLabelRaw isEqualToString:kABPhoneWorkLabel]) {
    221       string16 workPhone = base::SysNSStringToUTF16(
    222           [phoneNumbers valueAtIndex:reverseK]);
    223       PhoneNumber::ParsePhoneNumber(workPhone,
    224           &number, &city_code, &country_code);
    225       profile->SetInfo(PHONE_HOME_NUMBER, number);
    226       profile->SetInfo(PHONE_HOME_CITY_CODE, city_code);
    227       profile->SetInfo(PHONE_HOME_COUNTRY_CODE, country_code);
    228     } else if ([addressLabelRaw isEqualToString:kABAddressWorkLabel] &&
    229                [phoneLabelRaw isEqualToString:kABPhoneWorkFAXLabel]) {
    230       string16 workFax = base::SysNSStringToUTF16(
    231           [phoneNumbers valueAtIndex:reverseK]);
    232       PhoneNumber::ParsePhoneNumber(workFax,
    233           &number, &city_code, &country_code);
    234       profile->SetInfo(PHONE_FAX_NUMBER, number);
    235       profile->SetInfo(PHONE_FAX_CITY_CODE, city_code);
    236       profile->SetInfo(PHONE_FAX_COUNTRY_CODE, country_code);
    237     }
    238   }
    239 }
    240 
    241 }  // namespace
    242 
    243 // Populate |auxiliary_profiles_| with the Address Book data.
    244 void PersonalDataManager::LoadAuxiliaryProfiles() {
    245   AuxiliaryProfilesImpl impl(&auxiliary_profiles_);
    246   impl.GetAddressBookMeCard();
    247 }
    248