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