personal_data_manager_mac.mm revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1// Copyright 2013 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 "components/autofill/core/browser/personal_data_manager.h" 6 7#include <math.h> 8 9#import <AddressBook/AddressBook.h> 10 11#include "base/format_macros.h" 12#include "base/guid.h" 13#include "base/logging.h" 14#import "base/mac/scoped_nsexception_enabler.h" 15#include "base/memory/scoped_ptr.h" 16#include "base/memory/scoped_vector.h" 17#include "base/strings/stringprintf.h" 18#include "base/strings/sys_string_conversions.h" 19#include "components/autofill/core/browser/autofill_country.h" 20#include "components/autofill/core/browser/autofill_profile.h" 21#include "components/autofill/core/browser/phone_number.h" 22#include "grit/component_strings.h" 23#include "ui/base/l10n/l10n_util_mac.h" 24 25namespace autofill { 26namespace { 27 28const char kAddressBookOrigin[] = "OS X Address Book"; 29 30// This implementation makes use of the Address Book API. Profiles are 31// generated that correspond to addresses in the "me" card that reside in the 32// user's Address Book. The caller passes a vector of profiles into the 33// the constructer and then initiate the fetch from the Mac Address Book "me" 34// card using the main |GetAddressBookMeCard()| method. This clears any 35// existing addresses and populates new addresses derived from the data found 36// in the "me" card. 37class AuxiliaryProfilesImpl { 38 public: 39 // Constructor takes a reference to the |profiles| that will be filled in 40 // by the subsequent call to |GetAddressBookMeCard()|. |profiles| may not 41 // be NULL. 42 explicit AuxiliaryProfilesImpl(ScopedVector<AutofillProfile>* profiles) 43 : profiles_(*profiles) { 44 } 45 virtual ~AuxiliaryProfilesImpl() {} 46 47 // Import the "me" card from the Mac Address Book and fill in |profiles_|. 48 void GetAddressBookMeCard(const std::string& app_locale); 49 50 private: 51 void GetAddressBookNames(ABPerson* me, 52 NSString* addressLabelRaw, 53 AutofillProfile* profile); 54 void GetAddressBookAddress(const std::string& app_locale, 55 NSDictionary* address, 56 AutofillProfile* profile); 57 void GetAddressBookEmail(ABPerson* me, 58 NSString* addressLabelRaw, 59 AutofillProfile* profile); 60 void GetAddressBookPhoneNumbers(ABPerson* me, 61 NSString* addressLabelRaw, 62 AutofillProfile* profile); 63 64 private: 65 // A reference to the profiles this class populates. 66 ScopedVector<AutofillProfile>& profiles_; 67 68 DISALLOW_COPY_AND_ASSIGN(AuxiliaryProfilesImpl); 69}; 70 71// This method uses the |ABAddressBook| system service to fetch the "me" card 72// from the active user's address book. It looks for the user address 73// information and translates it to the internal list of |AutofillProfile| data 74// structures. 75void AuxiliaryProfilesImpl::GetAddressBookMeCard( 76 const std::string& app_locale) { 77 profiles_.clear(); 78 79 // +[ABAddressBook sharedAddressBook] throws an exception internally in 80 // circumstances that aren't clear. The exceptions are only observed in crash 81 // reports, so it is unknown whether they would be caught by AppKit and nil 82 // returned, or if they would take down the app. In either case, avoid 83 // crashing. http://crbug.com/129022 84 ABAddressBook* addressBook = base::mac::RunBlockIgnoringExceptions(^{ 85 return [ABAddressBook sharedAddressBook]; 86 }); 87 ABPerson* me = [addressBook me]; 88 if (!me) 89 return; 90 91 ABMultiValue* addresses = [me valueForProperty:kABAddressProperty]; 92 93 // The number of characters at the end of the GUID to reserve for 94 // distinguishing addresses within the "me" card. Cap the number of addresses 95 // we will fetch to the number that can be distinguished by this fragment of 96 // the GUID. 97 const size_t kNumAddressGUIDChars = 2; 98 const size_t kNumHexDigits = 16; 99 const size_t kMaxAddressCount = pow(kNumHexDigits, kNumAddressGUIDChars); 100 NSUInteger count = MIN([addresses count], kMaxAddressCount); 101 for (NSUInteger i = 0; i < count; i++) { 102 NSDictionary* address = [addresses valueAtIndex:i]; 103 NSString* addressLabelRaw = [addresses labelAtIndex:i]; 104 105 // Create a new profile where the guid is set to the guid portion of the 106 // |kABUIDProperty| taken from from the "me" address. The format of 107 // the |kABUIDProperty| is "<guid>:ABPerson", so we're stripping off the 108 // raw guid here and using it directly, with one modification: we update the 109 // last |kNumAddressGUIDChars| characters in the GUID to reflect the address 110 // variant. Note that we capped the number of addresses above, so this is 111 // safe. 112 const size_t kGUIDLength = 36U; 113 const size_t kTrimmedGUIDLength = kGUIDLength - kNumAddressGUIDChars; 114 std::string guid = base::SysNSStringToUTF8( 115 [me valueForProperty:kABUIDProperty]).substr(0, kTrimmedGUIDLength); 116 117 // The format string to print |kNumAddressGUIDChars| hexadecimal characters, 118 // left-padded with 0's. 119 const std::string kAddressGUIDFormat = 120 base::StringPrintf("%%0%" PRIuS "X", kNumAddressGUIDChars); 121 guid += base::StringPrintf(kAddressGUIDFormat.c_str(), i); 122 DCHECK_EQ(kGUIDLength, guid.size()); 123 124 scoped_ptr<AutofillProfile> profile( 125 new AutofillProfile(guid, kAddressBookOrigin)); 126 DCHECK(base::IsValidGUID(profile->guid())); 127 128 // Fill in name and company information. 129 GetAddressBookNames(me, addressLabelRaw, profile.get()); 130 131 // Fill in address information. 132 GetAddressBookAddress(app_locale, address, profile.get()); 133 134 // Fill in email information. 135 GetAddressBookEmail(me, addressLabelRaw, profile.get()); 136 137 // Fill in phone number information. 138 GetAddressBookPhoneNumbers(me, addressLabelRaw, profile.get()); 139 140 profiles_.push_back(profile.release()); 141 } 142} 143 144// Name and company information is stored once in the Address Book against 145// multiple addresses. We replicate that information for each profile. 146// We only propagate the company name to work profiles. 147void AuxiliaryProfilesImpl::GetAddressBookNames( 148 ABPerson* me, 149 NSString* addressLabelRaw, 150 AutofillProfile* profile) { 151 NSString* firstName = [me valueForProperty:kABFirstNameProperty]; 152 NSString* middleName = [me valueForProperty:kABMiddleNameProperty]; 153 NSString* lastName = [me valueForProperty:kABLastNameProperty]; 154 NSString* companyName = [me valueForProperty:kABOrganizationProperty]; 155 156 profile->SetRawInfo(NAME_FIRST, base::SysNSStringToUTF16(firstName)); 157 profile->SetRawInfo(NAME_MIDDLE, base::SysNSStringToUTF16(middleName)); 158 profile->SetRawInfo(NAME_LAST, base::SysNSStringToUTF16(lastName)); 159 if ([addressLabelRaw isEqualToString:kABAddressWorkLabel]) 160 profile->SetRawInfo(COMPANY_NAME, base::SysNSStringToUTF16(companyName)); 161} 162 163// Addresss information from the Address Book may span multiple lines. 164// If it does then we represent the address with two lines in the profile. The 165// second line we join with commas. 166// For example: "c/o John Doe\n1122 Other Avenue\nApt #7" translates to 167// line 1: "c/o John Doe", line 2: "1122 Other Avenue, Apt #7". 168void AuxiliaryProfilesImpl::GetAddressBookAddress(const std::string& app_locale, 169 NSDictionary* address, 170 AutofillProfile* profile) { 171 if (NSString* addressField = [address objectForKey:kABAddressStreetKey]) { 172 // If there are newlines in the address, split into two lines. 173 if ([addressField rangeOfCharacterFromSet: 174 [NSCharacterSet newlineCharacterSet]].location != NSNotFound) { 175 NSArray* chunks = [addressField componentsSeparatedByCharactersInSet: 176 [NSCharacterSet newlineCharacterSet]]; 177 DCHECK([chunks count] > 1); 178 179 NSString* separator = l10n_util::GetNSString( 180 IDS_AUTOFILL_MAC_ADDRESS_LINE_SEPARATOR); 181 182 NSString* addressField1 = [chunks objectAtIndex:0]; 183 NSString* addressField2 = 184 [[chunks subarrayWithRange:NSMakeRange(1, [chunks count] - 1)] 185 componentsJoinedByString:separator]; 186 profile->SetRawInfo(ADDRESS_HOME_LINE1, 187 base::SysNSStringToUTF16(addressField1)); 188 profile->SetRawInfo(ADDRESS_HOME_LINE2, 189 base::SysNSStringToUTF16(addressField2)); 190 } else { 191 profile->SetRawInfo(ADDRESS_HOME_LINE1, 192 base::SysNSStringToUTF16(addressField)); 193 } 194 } 195 196 if (NSString* city = [address objectForKey:kABAddressCityKey]) 197 profile->SetRawInfo(ADDRESS_HOME_CITY, base::SysNSStringToUTF16(city)); 198 199 if (NSString* state = [address objectForKey:kABAddressStateKey]) 200 profile->SetRawInfo(ADDRESS_HOME_STATE, base::SysNSStringToUTF16(state)); 201 202 if (NSString* zip = [address objectForKey:kABAddressZIPKey]) 203 profile->SetRawInfo(ADDRESS_HOME_ZIP, base::SysNSStringToUTF16(zip)); 204 205 if (NSString* country = [address objectForKey:kABAddressCountryKey]) { 206 profile->SetInfo(ADDRESS_HOME_COUNTRY, 207 base::SysNSStringToUTF16(country), 208 app_locale); 209 } 210} 211 212// Fills in email address matching current address label. Note that there may 213// be multiple matching email addresses for a given label. We take the 214// first we find (topmost) as preferred. 215void AuxiliaryProfilesImpl::GetAddressBookEmail( 216 ABPerson* me, 217 NSString* addressLabelRaw, 218 AutofillProfile* profile) { 219 ABMultiValue* emailAddresses = [me valueForProperty:kABEmailProperty]; 220 NSString* emailAddress = nil; 221 for (NSUInteger j = 0, emailCount = [emailAddresses count]; 222 j < emailCount; j++) { 223 NSString* emailAddressLabelRaw = [emailAddresses labelAtIndex:j]; 224 if ([emailAddressLabelRaw isEqualToString:addressLabelRaw]) { 225 emailAddress = [emailAddresses valueAtIndex:j]; 226 break; 227 } 228 } 229 profile->SetRawInfo(EMAIL_ADDRESS, base::SysNSStringToUTF16(emailAddress)); 230} 231 232// Fills in telephone numbers. Each of these are special cases. 233// We match two cases: home/tel, work/tel. 234// Note, we traverse in reverse order so that top values in address book 235// take priority. 236void AuxiliaryProfilesImpl::GetAddressBookPhoneNumbers( 237 ABPerson* me, 238 NSString* addressLabelRaw, 239 AutofillProfile* profile) { 240 ABMultiValue* phoneNumbers = [me valueForProperty:kABPhoneProperty]; 241 for (NSUInteger k = 0, phoneCount = [phoneNumbers count]; 242 k < phoneCount; k++) { 243 NSUInteger reverseK = phoneCount - k - 1; 244 NSString* phoneLabelRaw = [phoneNumbers labelAtIndex:reverseK]; 245 if ([addressLabelRaw isEqualToString:kABAddressHomeLabel] && 246 [phoneLabelRaw isEqualToString:kABPhoneHomeLabel]) { 247 base::string16 homePhone = base::SysNSStringToUTF16( 248 [phoneNumbers valueAtIndex:reverseK]); 249 profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, homePhone); 250 } else if ([addressLabelRaw isEqualToString:kABAddressWorkLabel] && 251 [phoneLabelRaw isEqualToString:kABPhoneWorkLabel]) { 252 base::string16 workPhone = base::SysNSStringToUTF16( 253 [phoneNumbers valueAtIndex:reverseK]); 254 profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, workPhone); 255 } else if ([phoneLabelRaw isEqualToString:kABPhoneMobileLabel] || 256 [phoneLabelRaw isEqualToString:kABPhoneMainLabel]) { 257 base::string16 phone = base::SysNSStringToUTF16( 258 [phoneNumbers valueAtIndex:reverseK]); 259 profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, phone); 260 } 261 } 262} 263 264} // namespace 265 266// Populate |auxiliary_profiles_| with the Address Book data. 267void PersonalDataManager::LoadAuxiliaryProfiles() { 268 AuxiliaryProfilesImpl impl(&auxiliary_profiles_); 269 impl.GetAddressBookMeCard(app_locale_); 270} 271 272} // namespace autofill 273