summaryrefslogtreecommitdiff
path: root/apps/mail/modules/displayNameUtils.js
blob: 74e886cea4eeea79cee857965618659052315ba6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

Components.utils.import("resource:///modules/iteratorUtils.jsm");
Components.utils.import("resource:///modules/mailServices.js");
Components.utils.import("resource:///modules/StringBundle.js");

var EXPORTED_SYMBOLS = [
  "GetCardForEmail", "FormatDisplayName", "FormatDisplayNameList",
];

// XXX: Maybe the strings for this file should go in a separate bundle?
var gMessengerBundle = new StringBundle(
  "chrome://messenger/locale/messenger.properties"
);

/**
 * Returns an object with two properties, .book and .card. If the email address
 * is found in the address books, then the book will contain an nsIAbDirectory,
 * and card will contain an nsIAbCard. If the email address is not found, both
 * items will contain null.
 *
 * @param aEmailAddress The address to look for.
 * @return An object with two properties, .book and .card.
 */
function GetCardForEmail(aEmailAddress) {
  // Email address is searched for in any of the address books that support
  // the cardForEmailAddress function.
  // Future expansion could be to domain matches
  let books = MailServices.ab.directories;
  for (let book in fixIterator(books, Components.interfaces.nsIAbDirectory)) {
    try {
      let card = book.cardForEmailAddress(aEmailAddress);
      if (card)
        return { book: book, card: card };
    }
    catch (ex) {}
  }

  return { book: null, card: null };
}

function _getIdentityForAddress(aEmailAddress) {
  let emailAddress = aEmailAddress.toLowerCase();
  for (let identity in fixIterator(MailServices.accounts.allIdentities,
                                   Components.interfaces.nsIMsgIdentity)) {
    if (!identity.email)
      continue;
    if (emailAddress == identity.email.toLowerCase())
      return identity;
  }
  return null;
}

/**
 * Take an email address and compose a sensible display name based on the
 * header display name and/or the display name from the address book. If no
 * appropriate name can be made (e.g. there is no card for this address),
 * returns |null|.
 *
 * @param aEmailAddress      The email address to format.
 * @param aHeaderDisplayName The display name from the header, if any
 *                           (unused, maintained for add-ons, previously used
 *                           as a fallback).
 * @param aContext           The field being formatted (e.g. "to", "from").
 * @param aCard              The address book card, if any.
 * @return The formatted display name, or null.
 */
function FormatDisplayName(aEmailAddress, aHeaderDisplayName, aContext, aCard)
{
  var displayName = null;
  var identity = _getIdentityForAddress(aEmailAddress);
  var card = aCard || GetCardForEmail(aEmailAddress).card;

  // If this address is one of the user's identities...
  if (identity) {
    // ...pick a localized version of the word "Me" appropriate to this
    // specific header; fall back to the version used by the "to" header
    // if nothing else is available.
    try {
      displayName = gMessengerBundle.getString("header" + aContext + "FieldMe");
    } catch (e) {
      displayName = gMessengerBundle.getString("headertoFieldMe");
    }

    // Make sure we have an unambiguous name if there are multiple identities
    if (MailServices.accounts.allIdentities.length > 1)
      displayName = MailServices.headerParser
                                .makeMailboxObject(displayName,
                                                   identity.email).toString();
  }

  // If we don't have a card, refuse to generate a display name. Places calling
  // this are then responsible for falling back to something else (e.g. the
  // value from the message header).
  if (card) {
    // getProperty may return a "1" or "0" string, we want a boolean
    if (card.getProperty("PreferDisplayName", true) != false)
      displayName = card.displayName || null;

    // Note: aHeaderDisplayName is not used as a fallback as confusion could be
    // caused by a collected address using an e-mail address as display name.
  }

  return displayName;
}

/**
 * Format the display name from a list of addresses. First, try using
 * FormatDisplayName, then fall back to the header's display name or the
 * address.
 *
 * @param aHeaderValue  The decoded header value (e.g. mime2DecodedAuthor).
 * @param aContext      The context of the header field (e.g. "to", "from").
 * @return The formatted display name.
 */
function FormatDisplayNameList(aHeaderValue, aContext) {
  let addresses = MailServices.headerParser.parseDecodedHeader(aHeaderValue);
  if (addresses.length > 0) {
    let displayName = FormatDisplayName(addresses[0].email,
                                        addresses[0].name, aContext);
    if (displayName)
      return displayName;

    // Construct default display.
    if (addresses[0].email) {
      return MailServices.headerParser
                         .makeMailboxObject(addresses[0].name,
                                            addresses[0].email).toString();
    }
  }

  // Something strange happened, just return the raw header value.
  return aHeaderValue;
}