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(" <");
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("></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