1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "nsAbCardProperty.h"
7 #include "nsAbBaseCID.h"
8 #include "nsIPrefService.h"
9 #include "nsIAbDirectory.h"
10 #include "plbase64.h"
11 #include "nsIStringBundle.h"
12 #include "plstr.h"
13 #include "nsMsgUtils.h"
14 #include "nsINetUtil.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsServiceManagerUtils.h"
17 #include "nsMemory.h"
18 #include "mozITXTToHTMLConv.h"
19 #include "nsIAbManager.h"
20 #include "nsIUUIDGenerator.h"
21 #include "nsIMsgVCardService.h"
22 #include "nsVariant.h"
23 #include "nsIProperty.h"
24 #include "nsCOMArray.h"
25 #include "prmem.h"
26 #include "mozilla/ArrayUtils.h"
27 #include "mozilla/Services.h"
28 using namespace mozilla;
29 
30 #define PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST "mail.addr_book.lastnamefirst"
31 
32 const char sAddrbookProperties[] =
33     "chrome://messenger/locale/addressbook/addressBook.properties";
34 
35 enum EAppendType {
36   eAppendLine,
37   eAppendLabel,
38   eAppendCityStateZip,
39   eAppendUndefined
40 };
41 
42 struct AppendItem {
43   const char* mColumn;
44   const char* mLabel;
45   EAppendType mAppendType;
46 };
47 
48 static const AppendItem NAME_ATTRS_ARRAY[] = {
49     {kDisplayNameProperty, "propertyDisplayName", eAppendLabel},
50     {kNicknameProperty, "propertyNickname", eAppendLabel},
51     {kPriEmailProperty, "", eAppendLine},
52     {k2ndEmailProperty, "", eAppendLine}};
53 
54 static const AppendItem PHONE_ATTRS_ARRAY[] = {
55     {kWorkPhoneProperty, "propertyWork", eAppendLabel},
56     {kHomePhoneProperty, "propertyHome", eAppendLabel},
57     {kFaxProperty, "propertyFax", eAppendLabel},
58     {kPagerProperty, "propertyPager", eAppendLabel},
59     {kCellularProperty, "propertyCellular", eAppendLabel}};
60 
61 static const AppendItem HOME_ATTRS_ARRAY[] = {
62     {kHomeAddressProperty, "", eAppendLine},
63     {kHomeAddress2Property, "", eAppendLine},
64     {kHomeCityProperty, "", eAppendCityStateZip},
65     {kHomeCountryProperty, "", eAppendLine},
66     {kHomeWebPageProperty, "", eAppendLine}};
67 
68 static const AppendItem WORK_ATTRS_ARRAY[] = {
69     {kJobTitleProperty, "", eAppendLine},
70     {kDepartmentProperty, "", eAppendLine},
71     {kCompanyProperty, "", eAppendLine},
72     {kWorkAddressProperty, "", eAppendLine},
73     {kWorkAddress2Property, "", eAppendLine},
74     {kWorkCityProperty, "", eAppendCityStateZip},
75     {kWorkCountryProperty, "", eAppendLine},
76     {kWorkWebPageProperty, "", eAppendLine}};
77 
78 static const AppendItem CUSTOM_ATTRS_ARRAY[] = {
79     {kCustom1Property, "propertyCustom1", eAppendLabel},
80     {kCustom2Property, "propertyCustom2", eAppendLabel},
81     {kCustom3Property, "propertyCustom3", eAppendLabel},
82     {kCustom4Property, "propertyCustom4", eAppendLabel},
83     {kNotesProperty, "", eAppendLine}};
84 
85 static const AppendItem CHAT_ATTRS_ARRAY[] = {
86     {kGtalkProperty, "propertyGtalk", eAppendLabel},
87     {kAIMProperty, "propertyAIM", eAppendLabel},
88     {kYahooProperty, "propertyYahoo", eAppendLabel},
89     {kSkypeProperty, "propertySkype", eAppendLabel},
90     {kQQProperty, "propertyQQ", eAppendLabel},
91     {kMSNProperty, "propertyMSN", eAppendLabel},
92     {kICQProperty, "propertyICQ", eAppendLabel},
93     {kXMPPProperty, "propertyXMPP", eAppendLabel},
94     {kIRCProperty, "propertyIRC", eAppendLabel}};
95 
nsAbCardProperty()96 nsAbCardProperty::nsAbCardProperty() : m_IsMailList(false) {
97   // Initialize some default properties
98   SetPropertyAsUint32(kPreferMailFormatProperty,
99                       nsIAbPreferMailFormat::unknown);
100   SetPropertyAsUint32(kPopularityIndexProperty, 0);
101   // Uninitialized...
102   SetPropertyAsUint32(kLastModifiedDateProperty, 0);
103 }
104 
~nsAbCardProperty(void)105 nsAbCardProperty::~nsAbCardProperty(void) {}
106 
NS_IMPL_ISUPPORTS(nsAbCardProperty,nsIAbCard)107 NS_IMPL_ISUPPORTS(nsAbCardProperty, nsIAbCard)
108 
109 NS_IMETHODIMP nsAbCardProperty::GetDirectoryUID(nsACString& dirUID) {
110   dirUID = m_directoryUID;
111   return NS_OK;
112 }
113 
SetDirectoryUID(const nsACString & aDirUID)114 NS_IMETHODIMP nsAbCardProperty::SetDirectoryUID(const nsACString& aDirUID) {
115   m_directoryUID = aDirUID;
116   return NS_OK;
117 }
118 
119 ////////////////////////////////////////////////////////////////////////////////
120 
GetIsMailList(bool * aIsMailList)121 NS_IMETHODIMP nsAbCardProperty::GetIsMailList(bool* aIsMailList) {
122   *aIsMailList = m_IsMailList;
123   return NS_OK;
124 }
125 
SetIsMailList(bool aIsMailList)126 NS_IMETHODIMP nsAbCardProperty::SetIsMailList(bool aIsMailList) {
127   m_IsMailList = aIsMailList;
128   return NS_OK;
129 }
130 
GetMailListURI(char ** aMailListURI)131 NS_IMETHODIMP nsAbCardProperty::GetMailListURI(char** aMailListURI) {
132   if (aMailListURI) {
133     *aMailListURI = ToNewCString(m_MailListURI);
134     return (*aMailListURI) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
135   } else
136     return NS_ERROR_NULL_POINTER;
137 }
138 
SetMailListURI(const char * aMailListURI)139 NS_IMETHODIMP nsAbCardProperty::SetMailListURI(const char* aMailListURI) {
140   if (aMailListURI) {
141     m_MailListURI = aMailListURI;
142     return NS_OK;
143   } else
144     return NS_ERROR_NULL_POINTER;
145 }
146 
147 ///////////////////////////////////////////////////////////////////////////////
148 // Property bag portion of nsAbCardProperty
149 ///////////////////////////////////////////////////////////////////////////////
150 
151 class nsAbSimpleProperty final : public nsIProperty {
152  public:
nsAbSimpleProperty(const nsACString & aName,nsIVariant * aValue)153   nsAbSimpleProperty(const nsACString& aName, nsIVariant* aValue)
154       : mName(aName), mValue(aValue) {}
155 
156   NS_DECL_ISUPPORTS
157   NS_DECL_NSIPROPERTY
158  protected:
~nsAbSimpleProperty()159   ~nsAbSimpleProperty() {}
160   nsCString mName;
161   nsCOMPtr<nsIVariant> mValue;
162 };
163 
NS_IMPL_ISUPPORTS(nsAbSimpleProperty,nsIProperty)164 NS_IMPL_ISUPPORTS(nsAbSimpleProperty, nsIProperty)
165 
166 NS_IMETHODIMP
167 nsAbSimpleProperty::GetName(nsAString& aName) {
168   aName.Assign(NS_ConvertUTF8toUTF16(mName));
169   return NS_OK;
170 }
171 
172 NS_IMETHODIMP
GetValue(nsIVariant ** aValue)173 nsAbSimpleProperty::GetValue(nsIVariant** aValue) {
174   NS_IF_ADDREF(*aValue = mValue);
175   return NS_OK;
176 }
177 
GetProperties(nsTArray<RefPtr<nsIProperty>> & props)178 NS_IMETHODIMP nsAbCardProperty::GetProperties(
179     nsTArray<RefPtr<nsIProperty>>& props) {
180   props.ClearAndRetainStorage();
181   props.SetCapacity(m_properties.Count());
182   for (auto iter = m_properties.Iter(); !iter.Done(); iter.Next()) {
183     props.AppendElement(new nsAbSimpleProperty(iter.Key(), iter.UserData()));
184   }
185   return NS_OK;
186 }
187 
GetProperty(const nsACString & name,nsIVariant * defaultValue,nsIVariant ** value)188 NS_IMETHODIMP nsAbCardProperty::GetProperty(const nsACString& name,
189                                             nsIVariant* defaultValue,
190                                             nsIVariant** value) {
191   if (!m_properties.Get(name, value)) NS_ADDREF(*value = defaultValue);
192   return NS_OK;
193 }
194 
GetPropertyAsAString(const char * name,nsAString & value)195 NS_IMETHODIMP nsAbCardProperty::GetPropertyAsAString(const char* name,
196                                                      nsAString& value) {
197   NS_ENSURE_ARG_POINTER(name);
198 
199   nsCOMPtr<nsIVariant> variant;
200   return m_properties.Get(nsDependentCString(name), getter_AddRefs(variant))
201              ? variant->GetAsAString(value)
202              : NS_ERROR_NOT_AVAILABLE;
203 }
204 
GetPropertyAsAUTF8String(const char * name,nsACString & value)205 NS_IMETHODIMP nsAbCardProperty::GetPropertyAsAUTF8String(const char* name,
206                                                          nsACString& value) {
207   NS_ENSURE_ARG_POINTER(name);
208 
209   nsCOMPtr<nsIVariant> variant;
210   return m_properties.Get(nsDependentCString(name), getter_AddRefs(variant))
211              ? variant->GetAsAUTF8String(value)
212              : NS_ERROR_NOT_AVAILABLE;
213 }
214 
GetPropertyAsUint32(const char * name,uint32_t * value)215 NS_IMETHODIMP nsAbCardProperty::GetPropertyAsUint32(const char* name,
216                                                     uint32_t* value) {
217   NS_ENSURE_ARG_POINTER(name);
218 
219   nsCOMPtr<nsIVariant> variant;
220   return m_properties.Get(nsDependentCString(name), getter_AddRefs(variant))
221              ? variant->GetAsUint32(value)
222              : NS_ERROR_NOT_AVAILABLE;
223 }
224 
GetPropertyAsBool(const char * name,bool * value)225 NS_IMETHODIMP nsAbCardProperty::GetPropertyAsBool(const char* name,
226                                                   bool* value) {
227   NS_ENSURE_ARG_POINTER(name);
228 
229   nsCOMPtr<nsIVariant> variant;
230   return m_properties.Get(nsDependentCString(name), getter_AddRefs(variant))
231              ? variant->GetAsBool(value)
232              : NS_ERROR_NOT_AVAILABLE;
233 }
234 
SetProperty(const nsACString & name,nsIVariant * value)235 NS_IMETHODIMP nsAbCardProperty::SetProperty(const nsACString& name,
236                                             nsIVariant* value) {
237   m_properties.InsertOrUpdate(name, value);
238   return NS_OK;
239 }
240 
SetPropertyAsAString(const char * name,const nsAString & value)241 NS_IMETHODIMP nsAbCardProperty::SetPropertyAsAString(const char* name,
242                                                      const nsAString& value) {
243   NS_ENSURE_ARG_POINTER(name);
244 
245   nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
246   variant->SetAsAString(value);
247   m_properties.InsertOrUpdate(nsDependentCString(name), variant);
248   return NS_OK;
249 }
250 
SetPropertyAsAUTF8String(const char * name,const nsACString & value)251 NS_IMETHODIMP nsAbCardProperty::SetPropertyAsAUTF8String(
252     const char* name, const nsACString& value) {
253   NS_ENSURE_ARG_POINTER(name);
254 
255   nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
256   variant->SetAsAUTF8String(value);
257   m_properties.InsertOrUpdate(nsDependentCString(name), variant);
258   return NS_OK;
259 }
260 
SetPropertyAsUint32(const char * name,uint32_t value)261 NS_IMETHODIMP nsAbCardProperty::SetPropertyAsUint32(const char* name,
262                                                     uint32_t value) {
263   NS_ENSURE_ARG_POINTER(name);
264 
265   nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
266   variant->SetAsUint32(value);
267   m_properties.InsertOrUpdate(nsDependentCString(name), variant);
268   return NS_OK;
269 }
270 
SetPropertyAsBool(const char * name,bool value)271 NS_IMETHODIMP nsAbCardProperty::SetPropertyAsBool(const char* name,
272                                                   bool value) {
273   NS_ENSURE_ARG_POINTER(name);
274 
275   nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
276   variant->SetAsBool(value);
277   m_properties.InsertOrUpdate(nsDependentCString(name), variant);
278   return NS_OK;
279 }
280 
DeleteProperty(const nsACString & name)281 NS_IMETHODIMP nsAbCardProperty::DeleteProperty(const nsACString& name) {
282   m_properties.Remove(name);
283   return NS_OK;
284 }
285 
GetUID(nsACString & uid)286 NS_IMETHODIMP nsAbCardProperty::GetUID(nsACString& uid) {
287   nsAutoString aString;
288   nsresult rv = GetPropertyAsAString(kUIDProperty, aString);
289   if (NS_SUCCEEDED(rv)) {
290     uid = NS_ConvertUTF16toUTF8(aString);
291     return rv;
292   }
293 
294   nsCOMPtr<nsIUUIDGenerator> uuidgen = mozilla::services::GetUUIDGenerator();
295   NS_ENSURE_TRUE(uuidgen, NS_ERROR_FAILURE);
296 
297   nsID id;
298   rv = uuidgen->GenerateUUIDInPlace(&id);
299   NS_ENSURE_SUCCESS(rv, rv);
300 
301   char idString[NSID_LENGTH];
302   id.ToProvidedString(idString);
303 
304   uid.AppendASCII(idString + 1, NSID_LENGTH - 3);
305   return SetUID(uid);
306 }
307 
SetUID(const nsACString & aUID)308 NS_IMETHODIMP nsAbCardProperty::SetUID(const nsACString& aUID) {
309   nsAutoString aString;
310   nsresult rv = GetPropertyAsAString(kUIDProperty, aString);
311   if (NS_SUCCEEDED(rv)) {
312     if (!aString.Equals(NS_ConvertUTF8toUTF16(aUID))) {
313       return NS_ERROR_FAILURE;
314     }
315   }
316 
317   rv = SetPropertyAsAString(kUIDProperty, NS_ConvertUTF8toUTF16(aUID));
318   NS_ENSURE_SUCCESS(rv, rv);
319 
320   if (m_directoryUID.IsEmpty()) {
321     // This card's not in a directory.
322     return NS_OK;
323   }
324 
325   nsCOMPtr<nsIAbManager> abManager =
326       do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
327   NS_ENSURE_SUCCESS(rv, rv);
328 
329   nsCOMPtr<nsIAbDirectory> directory = nullptr;
330   rv =
331       abManager->GetDirectoryFromUID(m_directoryUID, getter_AddRefs(directory));
332   NS_ENSURE_SUCCESS(rv, rv);
333 
334   if (!directory) {
335     // This card claims to be in a directory, but we can't find it.
336     return NS_OK;
337   }
338 
339   bool readOnly;
340   rv = directory->GetReadOnly(&readOnly);
341   NS_ENSURE_SUCCESS(rv, rv);
342 
343   if (readOnly) {
344     // The directory is read-only.
345     return NS_OK;
346   }
347 
348   // Save the new UID so we can use it again in the future.
349   return directory->ModifyCard(this);
350 }
351 
GetFirstName(nsAString & aString)352 NS_IMETHODIMP nsAbCardProperty::GetFirstName(nsAString& aString) {
353   nsresult rv = GetPropertyAsAString(kFirstNameProperty, aString);
354   if (rv == NS_ERROR_NOT_AVAILABLE) {
355     aString.Truncate();
356     return NS_OK;
357   }
358   return rv;
359 }
360 
SetFirstName(const nsAString & aString)361 NS_IMETHODIMP nsAbCardProperty::SetFirstName(const nsAString& aString) {
362   return SetPropertyAsAString(kFirstNameProperty, aString);
363 }
364 
GetLastName(nsAString & aString)365 NS_IMETHODIMP nsAbCardProperty::GetLastName(nsAString& aString) {
366   nsresult rv = GetPropertyAsAString(kLastNameProperty, aString);
367   if (rv == NS_ERROR_NOT_AVAILABLE) {
368     aString.Truncate();
369     return NS_OK;
370   }
371   return rv;
372 }
373 
SetLastName(const nsAString & aString)374 NS_IMETHODIMP nsAbCardProperty::SetLastName(const nsAString& aString) {
375   return SetPropertyAsAString(kLastNameProperty, aString);
376 }
377 
GetDisplayName(nsAString & aString)378 NS_IMETHODIMP nsAbCardProperty::GetDisplayName(nsAString& aString) {
379   nsresult rv = GetPropertyAsAString(kDisplayNameProperty, aString);
380   if (rv == NS_ERROR_NOT_AVAILABLE) {
381     aString.Truncate();
382     return NS_OK;
383   }
384   return rv;
385 }
386 
SetDisplayName(const nsAString & aString)387 NS_IMETHODIMP nsAbCardProperty::SetDisplayName(const nsAString& aString) {
388   return SetPropertyAsAString(kDisplayNameProperty, aString);
389 }
390 
GetPrimaryEmail(nsAString & aString)391 NS_IMETHODIMP nsAbCardProperty::GetPrimaryEmail(nsAString& aString) {
392   nsresult rv = GetPropertyAsAString(kPriEmailProperty, aString);
393   if (rv == NS_ERROR_NOT_AVAILABLE) {
394     aString.Truncate();
395     return NS_OK;
396   }
397   return rv;
398 }
399 
SetPrimaryEmail(const nsAString & aString)400 NS_IMETHODIMP nsAbCardProperty::SetPrimaryEmail(const nsAString& aString) {
401   return SetPropertyAsAString(kPriEmailProperty, aString);
402 }
403 
HasEmailAddress(const nsACString & aEmailAddress,bool * aResult)404 NS_IMETHODIMP nsAbCardProperty::HasEmailAddress(const nsACString& aEmailAddress,
405                                                 bool* aResult) {
406   NS_ENSURE_ARG_POINTER(aResult);
407 
408   *aResult = false;
409 
410   nsCString emailAddress;
411   nsresult rv = GetPropertyAsAUTF8String(kPriEmailProperty, emailAddress);
412   if (rv != NS_ERROR_NOT_AVAILABLE &&
413       emailAddress.Equals(aEmailAddress, nsCaseInsensitiveCStringComparator)) {
414     *aResult = true;
415     return NS_OK;
416   }
417 
418   rv = GetPropertyAsAUTF8String(k2ndEmailProperty, emailAddress);
419   if (rv != NS_ERROR_NOT_AVAILABLE &&
420       emailAddress.Equals(aEmailAddress, nsCaseInsensitiveCStringComparator))
421     *aResult = true;
422 
423   return NS_OK;
424 }
425 
426 // This function may be overridden by derived classes for
427 // nsAb*Card specific implementations.
Copy(nsIAbCard * srcCard)428 NS_IMETHODIMP nsAbCardProperty::Copy(nsIAbCard* srcCard) {
429   NS_ENSURE_ARG_POINTER(srcCard);
430 
431   nsTArray<RefPtr<nsIProperty>> properties;
432   nsresult rv = srcCard->GetProperties(properties);
433   NS_ENSURE_SUCCESS(rv, rv);
434 
435   for (nsIProperty* property : properties) {
436     nsAutoString name;
437     property->GetName(name);
438     nsCOMPtr<nsIVariant> value;
439     property->GetValue(getter_AddRefs(value));
440 
441     SetProperty(NS_ConvertUTF16toUTF8(name), value);
442   }
443 
444   bool isMailList;
445   srcCard->GetIsMailList(&isMailList);
446   SetIsMailList(isMailList);
447 
448   nsCString mailListURI;
449   srcCard->GetMailListURI(getter_Copies(mailListURI));
450   SetMailListURI(mailListURI.get());
451 
452   return NS_OK;
453 }
454 
Equals(nsIAbCard * card,bool * result)455 NS_IMETHODIMP nsAbCardProperty::Equals(nsIAbCard* card, bool* result) {
456   *result = (card == this);
457   return NS_OK;
458 }
459 
460 ////////////////////////////////////////////////////////////////////////////////
461 // The following methods are other views of a card
462 ////////////////////////////////////////////////////////////////////////////////
463 
464 // XXX: Use the category manager instead of this file to implement these
TranslateTo(const nsACString & type,nsACString & result)465 NS_IMETHODIMP nsAbCardProperty::TranslateTo(const nsACString& type,
466                                             nsACString& result) {
467   if (type.EqualsLiteral("base64xml")) {
468     return ConvertToBase64EncodedXML(result);
469   } else if (type.EqualsLiteral("xml")) {
470     nsString utf16String;
471     nsresult rv = ConvertToXMLPrintData(utf16String);
472     NS_ENSURE_SUCCESS(rv, rv);
473     result = NS_ConvertUTF16toUTF8(utf16String);
474     return NS_OK;
475   } else if (type.EqualsLiteral("vcard")) {
476     return ConvertToEscapedVCard(result);
477   }
478 
479   return NS_ERROR_ILLEGAL_VALUE;
480 }
481 
ConvertToEscapedVCard(nsACString & aResult)482 nsresult nsAbCardProperty::ConvertToEscapedVCard(nsACString& aResult) {
483   nsresult rv;
484   nsCOMPtr<nsIMsgVCardService> vCardService =
485       do_GetService(NS_MSGVCARDSERVICE_CONTRACTID, &rv);
486   NS_ENSURE_SUCCESS(rv, rv);
487 
488   nsAutoString result;
489   rv = vCardService->AbCardToEscapedVCard(this, result);
490   NS_ENSURE_SUCCESS(rv, rv);
491 
492   aResult = NS_ConvertUTF16toUTF8(result);
493   return NS_OK;
494 }
495 
ConvertToBase64EncodedXML(nsACString & result)496 nsresult nsAbCardProperty::ConvertToBase64EncodedXML(nsACString& result) {
497   nsresult rv;
498   nsString xmlStr;
499 
500   xmlStr.AppendLiteral(
501       "<?xml version=\"1.0\"?>\n"
502       "<?xml-stylesheet type=\"text/css\" "
503       "href=\"chrome://messagebody/content/addressbook/print.css\"?>\n"
504       "<directory>\n");
505 
506   // Get Address Book string and set it as title of XML document
507   nsCOMPtr<nsIStringBundle> bundle;
508   nsCOMPtr<nsIStringBundleService> stringBundleService =
509       mozilla::services::GetStringBundleService();
510   if (stringBundleService) {
511     rv = stringBundleService->CreateBundle(sAddrbookProperties,
512                                            getter_AddRefs(bundle));
513     if (NS_SUCCEEDED(rv)) {
514       nsString addrBook;
515       rv = bundle->GetStringFromName("addressBook", addrBook);
516       if (NS_SUCCEEDED(rv)) {
517         xmlStr.AppendLiteral("<title xmlns=\"http://www.w3.org/1999/xhtml\">");
518         xmlStr.Append(addrBook);
519         xmlStr.AppendLiteral("</title>\n");
520       }
521     }
522   }
523 
524   nsString xmlSubstr;
525   rv = ConvertToXMLPrintData(xmlSubstr);
526   NS_ENSURE_SUCCESS(rv, rv);
527 
528   xmlStr.Append(xmlSubstr);
529   xmlStr.AppendLiteral("</directory>\n");
530 
531   char* tmpRes =
532       PL_Base64Encode(NS_ConvertUTF16toUTF8(xmlStr).get(), 0, nullptr);
533   result.Assign(tmpRes);
534   PR_Free(tmpRes);
535   return NS_OK;
536 }
537 
ConvertToXMLPrintData(nsAString & aXMLSubstr)538 nsresult nsAbCardProperty::ConvertToXMLPrintData(nsAString& aXMLSubstr) {
539   nsresult rv;
540   nsCOMPtr<nsIPrefBranch> prefBranch =
541       do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
542   NS_ENSURE_SUCCESS(rv, rv);
543 
544   int32_t generatedNameFormat;
545   rv = prefBranch->GetIntPref(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST,
546                               &generatedNameFormat);
547   NS_ENSURE_SUCCESS(rv, rv);
548 
549   nsCOMPtr<nsIStringBundleService> stringBundleService =
550       mozilla::services::GetStringBundleService();
551   NS_ENSURE_TRUE(stringBundleService, NS_ERROR_UNEXPECTED);
552 
553   nsCOMPtr<nsIStringBundle> bundle;
554   rv = stringBundleService->CreateBundle(sAddrbookProperties,
555                                          getter_AddRefs(bundle));
556   NS_ENSURE_SUCCESS(rv, rv);
557 
558   nsString generatedName;
559   rv = GenerateName(generatedNameFormat, bundle, generatedName);
560   NS_ENSURE_SUCCESS(rv, rv);
561 
562   nsCOMPtr<mozITXTToHTMLConv> conv =
563       do_CreateInstance(MOZ_TXTTOHTMLCONV_CONTRACTID, &rv);
564   NS_ENSURE_SUCCESS(rv, rv);
565 
566   nsString xmlStr;
567   xmlStr.SetLength(
568       4096);  // to reduce allocations. should be enough for most cards
569   xmlStr.AssignLiteral("<GeneratedName>\n");
570 
571   // use ScanTXT to convert < > & to safe values.
572   nsString safeText;
573   if (!generatedName.IsEmpty()) {
574     rv = conv->ScanTXT(generatedName, mozITXTToHTMLConv::kEntities, safeText);
575     NS_ENSURE_SUCCESS(rv, rv);
576   }
577 
578   if (safeText.IsEmpty()) {
579     nsAutoString primaryEmail;
580     GetPrimaryEmail(primaryEmail);
581 
582     // use ScanTXT to convert < > & to safe values.
583     rv = conv->ScanTXT(primaryEmail, mozITXTToHTMLConv::kEntities, safeText);
584     NS_ENSURE_SUCCESS(rv, rv);
585   }
586   xmlStr.Append(safeText);
587 
588   xmlStr.AppendLiteral(
589       "</GeneratedName>\n"
590       "<table><tr><td>");
591 
592   rv = AppendSection(NAME_ATTRS_ARRAY,
593                      sizeof(NAME_ATTRS_ARRAY) / sizeof(AppendItem),
594                      EmptyString(), bundle, conv, xmlStr);
595 
596   xmlStr.AppendLiteral("</td></tr><tr><td>");
597 
598   rv = AppendSection(PHONE_ATTRS_ARRAY,
599                      sizeof(PHONE_ATTRS_ARRAY) / sizeof(AppendItem),
600                      u"headingPhone"_ns, bundle, conv, xmlStr);
601 
602   if (!m_IsMailList) {
603     rv = AppendSection(CUSTOM_ATTRS_ARRAY,
604                        sizeof(CUSTOM_ATTRS_ARRAY) / sizeof(AppendItem),
605                        u"headingOther"_ns, bundle, conv, xmlStr);
606     rv = AppendSection(CHAT_ATTRS_ARRAY,
607                        sizeof(CHAT_ATTRS_ARRAY) / sizeof(AppendItem),
608                        u"headingChat"_ns, bundle, conv, xmlStr);
609   } else {
610     rv = AppendSection(CUSTOM_ATTRS_ARRAY,
611                        sizeof(CUSTOM_ATTRS_ARRAY) / sizeof(AppendItem),
612                        u"headingDescription"_ns, bundle, conv, xmlStr);
613 
614     xmlStr.AppendLiteral("<section><sectiontitle>");
615 
616     nsString headingAddresses;
617     rv = bundle->GetStringFromName("headingAddresses", headingAddresses);
618     NS_ENSURE_SUCCESS(rv, rv);
619 
620     xmlStr.Append(headingAddresses);
621     xmlStr.AppendLiteral("</sectiontitle>");
622 
623     nsCOMPtr<nsIAbManager> abManager =
624         do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
625     NS_ENSURE_SUCCESS(rv, rv);
626 
627     nsCOMPtr<nsIAbDirectory> mailList = nullptr;
628     rv = abManager->GetDirectory(m_MailListURI, getter_AddRefs(mailList));
629     NS_ENSURE_SUCCESS(rv, rv);
630 
631     nsTArray<RefPtr<nsIAbCard>> mailListAddresses;
632     rv = mailList->GetChildCards(mailListAddresses);
633     NS_ENSURE_SUCCESS(rv, rv);
634 
635     for (nsIAbCard* listCard : mailListAddresses) {
636       xmlStr.AppendLiteral("<PrimaryEmail>\n");
637 
638       nsAutoString displayName;
639       rv = listCard->GetDisplayName(displayName);
640       NS_ENSURE_SUCCESS(rv, rv);
641 
642       // use ScanTXT to convert < > & to safe values.
643       nsString safeText;
644       rv = conv->ScanTXT(displayName, mozITXTToHTMLConv::kEntities, safeText);
645       NS_ENSURE_SUCCESS(rv, rv);
646       xmlStr.Append(safeText);
647 
648       xmlStr.AppendLiteral(" &lt;");
649 
650       nsAutoString primaryEmail;
651       listCard->GetPrimaryEmail(primaryEmail);
652 
653       // use ScanTXT to convert < > & to safe values.
654       nsString safeText2;
655       rv = conv->ScanTXT(primaryEmail, mozITXTToHTMLConv::kEntities, safeText2);
656       NS_ENSURE_SUCCESS(rv, rv);
657       xmlStr.Append(safeText2);
658 
659       xmlStr.AppendLiteral("&gt;</PrimaryEmail>\n");
660     }
661     xmlStr.AppendLiteral("</section>");
662   }
663 
664   xmlStr.AppendLiteral("</td><td>");
665 
666   rv = AppendSection(HOME_ATTRS_ARRAY,
667                      sizeof(HOME_ATTRS_ARRAY) / sizeof(AppendItem),
668                      u"headingHome"_ns, bundle, conv, xmlStr);
669   rv = AppendSection(WORK_ATTRS_ARRAY,
670                      sizeof(WORK_ATTRS_ARRAY) / sizeof(AppendItem),
671                      u"headingWork"_ns, bundle, conv, xmlStr);
672 
673   xmlStr.AppendLiteral("</td></tr></table>");
674 
675   aXMLSubstr = xmlStr;
676 
677   return NS_OK;
678 }
679 
AppendSection(const AppendItem * aArray,int16_t aCount,const nsString & aHeading,nsIStringBundle * aBundle,mozITXTToHTMLConv * aConv,nsString & aResult)680 nsresult nsAbCardProperty::AppendSection(
681     const AppendItem* aArray, int16_t aCount, const nsString& aHeading,
682     nsIStringBundle* aBundle, mozITXTToHTMLConv* aConv, nsString& aResult) {
683   nsresult rv = NS_OK;
684 
685   aResult.AppendLiteral("<section>");
686 
687   nsString attrValue;
688   bool sectionIsEmpty = true;
689 
690   int16_t i = 0;
691   for (i = 0; i < aCount; i++) {
692     rv = GetPropertyAsAString(aArray[i].mColumn, attrValue);
693     if (NS_SUCCEEDED(rv) && !attrValue.IsEmpty()) sectionIsEmpty = false;
694   }
695 
696   if (!sectionIsEmpty && !aHeading.IsEmpty()) {
697     nsString heading;
698     rv = aBundle->GetStringFromName(NS_ConvertUTF16toUTF8(aHeading).get(),
699                                     heading);
700     NS_ENSURE_SUCCESS(rv, rv);
701 
702     aResult.AppendLiteral("<sectiontitle>");
703     aResult.Append(heading);
704     aResult.AppendLiteral("</sectiontitle>");
705   }
706 
707   for (i = 0; i < aCount; i++) {
708     switch (aArray[i].mAppendType) {
709       case eAppendLine:
710         rv = AppendLine(aArray[i], aConv, aResult);
711         break;
712       case eAppendLabel:
713         rv = AppendLabel(aArray[i], aBundle, aConv, aResult);
714         break;
715       case eAppendCityStateZip:
716         rv = AppendCityStateZip(aArray[i], aBundle, aConv, aResult);
717         break;
718       default:
719         rv = NS_ERROR_FAILURE;
720         break;
721     }
722 
723     if (NS_FAILED(rv)) {
724       NS_WARNING("append item failed");
725       break;
726     }
727   }
728   aResult.AppendLiteral("</section>");
729 
730   return rv;
731 }
732 
AppendLine(const AppendItem & aItem,mozITXTToHTMLConv * aConv,nsString & aResult)733 nsresult nsAbCardProperty::AppendLine(const AppendItem& aItem,
734                                       mozITXTToHTMLConv* aConv,
735                                       nsString& aResult) {
736   NS_ENSURE_ARG_POINTER(aConv);
737 
738   nsString attrValue;
739   nsresult rv = GetPropertyAsAString(aItem.mColumn, attrValue);
740 
741   if (NS_FAILED(rv) || attrValue.IsEmpty()) return NS_OK;
742 
743   aResult.Append(char16_t('<'));
744   aResult.Append(NS_ConvertUTF8toUTF16(aItem.mColumn));
745   aResult.Append(char16_t('>'));
746 
747   // use ScanTXT to convert < > & to safe values.
748   nsString safeText;
749   rv = aConv->ScanTXT(attrValue, mozITXTToHTMLConv::kEntities, safeText);
750   NS_ENSURE_SUCCESS(rv, rv);
751   aResult.Append(safeText);
752 
753   aResult.AppendLiteral("</");
754   aResult.Append(NS_ConvertUTF8toUTF16(aItem.mColumn));
755   aResult.Append(char16_t('>'));
756 
757   return NS_OK;
758 }
759 
AppendLabel(const AppendItem & aItem,nsIStringBundle * aBundle,mozITXTToHTMLConv * aConv,nsString & aResult)760 nsresult nsAbCardProperty::AppendLabel(const AppendItem& aItem,
761                                        nsIStringBundle* aBundle,
762                                        mozITXTToHTMLConv* aConv,
763                                        nsString& aResult) {
764   NS_ENSURE_ARG_POINTER(aBundle);
765 
766   nsresult rv;
767   nsString label, attrValue;
768 
769   rv = GetPropertyAsAString(aItem.mColumn, attrValue);
770 
771   if (NS_FAILED(rv) || attrValue.IsEmpty()) return NS_OK;
772 
773   rv = aBundle->GetStringFromName(aItem.mLabel, label);
774   NS_ENSURE_SUCCESS(rv, rv);
775 
776   aResult.AppendLiteral("<labelrow><label>");
777 
778   aResult.Append(label);
779   aResult.AppendLiteral(": </label>");
780 
781   rv = AppendLine(aItem, aConv, aResult);
782   NS_ENSURE_SUCCESS(rv, rv);
783 
784   aResult.AppendLiteral("</labelrow>");
785 
786   return NS_OK;
787 }
788 
AppendCityStateZip(const AppendItem & aItem,nsIStringBundle * aBundle,mozITXTToHTMLConv * aConv,nsString & aResult)789 nsresult nsAbCardProperty::AppendCityStateZip(const AppendItem& aItem,
790                                               nsIStringBundle* aBundle,
791                                               mozITXTToHTMLConv* aConv,
792                                               nsString& aResult) {
793   NS_ENSURE_ARG_POINTER(aBundle);
794 
795   nsresult rv;
796   AppendItem item;
797   const char *statePropName, *zipPropName;
798 
799   if (strcmp(aItem.mColumn, kHomeCityProperty) == 0) {
800     statePropName = kHomeStateProperty;
801     zipPropName = kHomeZipCodeProperty;
802   } else {
803     statePropName = kWorkStateProperty;
804     zipPropName = kWorkZipCodeProperty;
805   }
806 
807   nsAutoString cityResult, stateResult, zipResult;
808 
809   rv = AppendLine(aItem, aConv, cityResult);
810   NS_ENSURE_SUCCESS(rv, rv);
811 
812   item.mColumn = statePropName;
813   item.mLabel = "";
814   item.mAppendType = eAppendUndefined;
815 
816   rv = AppendLine(item, aConv, stateResult);
817   NS_ENSURE_SUCCESS(rv, rv);
818 
819   item.mColumn = zipPropName;
820 
821   rv = AppendLine(item, aConv, zipResult);
822   NS_ENSURE_SUCCESS(rv, rv);
823 
824   nsString formattedString;
825 
826   if (!cityResult.IsEmpty() && !stateResult.IsEmpty() && !zipResult.IsEmpty()) {
827     AutoTArray<nsString, 3> formatStrings = {cityResult, stateResult,
828                                              zipResult};
829     rv = aBundle->FormatStringFromName("cityAndStateAndZip", formatStrings,
830                                        formattedString);
831     NS_ENSURE_SUCCESS(rv, rv);
832   } else if (!cityResult.IsEmpty() && !stateResult.IsEmpty() &&
833              zipResult.IsEmpty()) {
834     AutoTArray<nsString, 2> formatStrings = {cityResult, stateResult};
835     rv = aBundle->FormatStringFromName("cityAndStateNoZip", formatStrings,
836                                        formattedString);
837     NS_ENSURE_SUCCESS(rv, rv);
838   } else if ((!cityResult.IsEmpty() && stateResult.IsEmpty() &&
839               !zipResult.IsEmpty()) ||
840              (cityResult.IsEmpty() && !stateResult.IsEmpty() &&
841               !zipResult.IsEmpty())) {
842     AutoTArray<nsString, 2> formatStrings = {
843         cityResult.IsEmpty() ? stateResult : cityResult, zipResult};
844     rv = aBundle->FormatStringFromName("cityOrStateAndZip", formatStrings,
845                                        formattedString);
846     NS_ENSURE_SUCCESS(rv, rv);
847   } else {
848     if (!cityResult.IsEmpty())
849       formattedString = cityResult;
850     else if (!stateResult.IsEmpty())
851       formattedString = stateResult;
852     else
853       formattedString = zipResult;
854   }
855 
856   aResult.Append(formattedString);
857 
858   return NS_OK;
859 }
860 
GenerateName(int32_t aGenerateFormat,nsIStringBundle * aBundle,nsAString & aResult)861 NS_IMETHODIMP nsAbCardProperty::GenerateName(int32_t aGenerateFormat,
862                                              nsIStringBundle* aBundle,
863                                              nsAString& aResult) {
864   aResult.Truncate();
865 
866   // Cache the first and last names
867   nsAutoString firstName, lastName;
868   GetFirstName(firstName);
869   GetLastName(lastName);
870 
871   // No need to check for aBundle present straight away, only do that if we're
872   // actually going to use it.
873   if (aGenerateFormat == GENERATE_DISPLAY_NAME)
874     GetDisplayName(aResult);
875   else if (lastName.IsEmpty())
876     aResult = firstName;
877   else if (firstName.IsEmpty())
878     aResult = lastName;
879   else {
880     nsresult rv;
881     nsCOMPtr<nsIStringBundle> bundle(aBundle);
882     if (!bundle) {
883       nsCOMPtr<nsIStringBundleService> stringBundleService =
884           mozilla::services::GetStringBundleService();
885       NS_ENSURE_TRUE(stringBundleService, NS_ERROR_UNEXPECTED);
886 
887       rv = stringBundleService->CreateBundle(sAddrbookProperties,
888                                              getter_AddRefs(bundle));
889       NS_ENSURE_SUCCESS(rv, rv);
890     }
891 
892     nsString result;
893 
894     if (aGenerateFormat == GENERATE_LAST_FIRST_ORDER) {
895       AutoTArray<nsString, 2> stringParams = {lastName, firstName};
896 
897       rv =
898           bundle->FormatStringFromName("lastFirstFormat", stringParams, result);
899     } else {
900       AutoTArray<nsString, 2> stringParams = {firstName, lastName};
901 
902       rv =
903           bundle->FormatStringFromName("firstLastFormat", stringParams, result);
904     }
905     NS_ENSURE_SUCCESS(rv, rv);
906 
907     aResult.Assign(result);
908   }
909 
910   if (aResult.IsEmpty()) {
911     // The normal names have failed, does this card have a company name? If so,
912     // use that instead, because that is likely to be more meaningful than an
913     // email address.
914     //
915     // If this errors, the string isn't found and we'll fall into the next
916     // check.
917     (void)GetPropertyAsAString(kCompanyProperty, aResult);
918   }
919 
920   if (aResult.IsEmpty()) {
921     // see bug #211078
922     // if there is no generated name at this point
923     // use the userid from the email address
924     // it is better than nothing.
925     GetPrimaryEmail(aResult);
926     int32_t index = aResult.FindChar('@');
927     if (index != -1) aResult.SetLength(index);
928   }
929 
930   return NS_OK;
931 }
932 
GeneratePhoneticName(bool aLastNameFirst,nsAString & aResult)933 NS_IMETHODIMP nsAbCardProperty::GeneratePhoneticName(bool aLastNameFirst,
934                                                      nsAString& aResult) {
935   nsAutoString firstName, lastName;
936   GetPropertyAsAString(kPhoneticFirstNameProperty, firstName);
937   GetPropertyAsAString(kPhoneticLastNameProperty, lastName);
938 
939   if (aLastNameFirst) {
940     aResult = lastName;
941     aResult += firstName;
942   } else {
943     aResult = firstName;
944     aResult += lastName;
945   }
946 
947   return NS_OK;
948 }
949 
GenerateChatName(nsAString & aResult)950 NS_IMETHODIMP nsAbCardProperty::GenerateChatName(nsAString& aResult) {
951   aResult.Truncate();
952 
953 #define CHECK_CHAT_PROPERTY(aProtocol)                                       \
954   if (NS_SUCCEEDED(GetPropertyAsAString(k##aProtocol##Property, aResult)) && \
955       !aResult.IsEmpty())                                                    \
956   return NS_OK
957   CHECK_CHAT_PROPERTY(Gtalk);
958   CHECK_CHAT_PROPERTY(AIM);
959   CHECK_CHAT_PROPERTY(Yahoo);
960   CHECK_CHAT_PROPERTY(Skype);
961   CHECK_CHAT_PROPERTY(QQ);
962   CHECK_CHAT_PROPERTY(MSN);
963   CHECK_CHAT_PROPERTY(ICQ);
964   CHECK_CHAT_PROPERTY(XMPP);
965   CHECK_CHAT_PROPERTY(IRC);
966   return NS_OK;
967 }
968