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