1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
4
5const EXPORTED_SYMBOLS = ["AddrBookCard"];
6
7ChromeUtils.defineModuleGetter(
8  this,
9  "newUID",
10  "resource:///modules/AddrBookUtils.jsm"
11);
12
13/**
14 * Prototype for nsIAbCard objects that are not mailing lists.
15 *
16 * @implements {nsIAbCard}
17 */
18function AddrBookCard() {
19  this._directoryUID = "";
20  this._properties = new Map([
21    ["PreferMailFormat", Ci.nsIAbPreferMailFormat.unknown],
22    ["PopularityIndex", 0],
23    ["LastModifiedDate", 0],
24  ]);
25}
26
27AddrBookCard.prototype = {
28  QueryInterface: ChromeUtils.generateQI(["nsIAbCard"]),
29  classID: Components.ID("{1143991d-31cd-4ea6-9c97-c587d990d724}"),
30
31  /* nsIAbCard */
32
33  generateName(generateFormat, bundle) {
34    let result = "";
35    let format;
36    if (generateFormat == Ci.nsIAbCard.GENERATE_DISPLAY_NAME) {
37      result = this.displayName;
38    } else if (!this.lastName.length) {
39      result = this.firstName;
40    } else if (!this.firstName.length) {
41      result = this.lastName;
42    } else if (generateFormat == Ci.nsIAbCard.GENERATE_LAST_FIRST_ORDER) {
43      format = bundle ? bundle.GetStringFromName("lastFirstFormat") : "%S, %S";
44      result = format
45        .replace("%S", this.lastName)
46        .replace("%S", this.firstName);
47    } else {
48      format = bundle ? bundle.GetStringFromName("firstLastFormat") : "%S %S";
49      result = format
50        .replace("%S", this.firstName)
51        .replace("%S", this.lastName);
52    }
53
54    if (result == "") {
55      result = this.getProperty("Company", "");
56    }
57    if (result == "") {
58      result = this.primaryEmail.split("@", 1)[0];
59    }
60
61    return result;
62  },
63  get directoryUID() {
64    return this._directoryUID;
65  },
66  set directoryUID(value) {
67    this._directoryUID = value;
68  },
69  get UID() {
70    if (!this._uid) {
71      this._uid = newUID();
72    }
73    return this._uid;
74  },
75  set UID(value) {
76    if (value != this._uid) {
77      throw Components.Exception("", Cr.NS_ERROR_FAILURE);
78    }
79  },
80  get properties() {
81    let props = [];
82    for (const [name, value] of this._properties) {
83      props.push({
84        get name() {
85          return name;
86        },
87        get value() {
88          return value;
89        },
90        QueryInterface: ChromeUtils.generateQI(["nsIProperty"]),
91      });
92    }
93    return props;
94  },
95  get firstName() {
96    return this.getProperty("FirstName", "");
97  },
98  set firstName(value) {
99    this.setProperty("FirstName", value);
100  },
101  get lastName() {
102    return this.getProperty("LastName", "");
103  },
104  set lastName(value) {
105    this.setProperty("LastName", value);
106  },
107  get displayName() {
108    return this.getProperty("DisplayName", "");
109  },
110  set displayName(value) {
111    this.setProperty("DisplayName", value);
112  },
113  get primaryEmail() {
114    return this.getProperty("PrimaryEmail", "");
115  },
116  set primaryEmail(value) {
117    this.setProperty("PrimaryEmail", value);
118  },
119  get isMailList() {
120    return false;
121  },
122  get mailListURI() {
123    return "";
124  },
125
126  getProperty(name, defaultValue) {
127    if (this._properties.has(name)) {
128      return this._properties.get(name);
129    }
130    return defaultValue;
131  },
132  getPropertyAsAString(name) {
133    if (!this._properties.has(name)) {
134      return "";
135    }
136    return this.getProperty(name);
137  },
138  getPropertyAsAUTF8String(name) {
139    if (!this._properties.has(name)) {
140      throw Components.Exception(`${name} N/A`, Cr.NS_ERROR_NOT_AVAILABLE);
141    }
142    return this.getProperty(name);
143  },
144  getPropertyAsUint32(name) {
145    let value = this.getProperty(name);
146    if (isNaN(parseInt(value, 10))) {
147      throw Components.Exception(
148        `${name}: ${value} - not an int`,
149        Cr.NS_ERROR_NOT_AVAILABLE
150      );
151    }
152    return value;
153  },
154  getPropertyAsBool(name) {
155    let value = this.getProperty(name);
156    switch (value) {
157      case false:
158      case 0:
159      case "0":
160        return false;
161      case true:
162      case 1:
163      case "1":
164        return true;
165    }
166    throw Components.Exception(
167      `${name}: ${value} - not a boolean`,
168      Cr.NS_ERROR_NOT_AVAILABLE
169    );
170  },
171  setProperty(name, value) {
172    if ([null, undefined, ""].includes(value)) {
173      this._properties.delete(name);
174      return;
175    }
176    if (typeof value == "boolean") {
177      value = value ? "1" : "0";
178    }
179    this._properties.set(name, "" + value);
180  },
181  setPropertyAsAString(name, value) {
182    throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
183  },
184  setPropertyAsAUTF8String(name, value) {
185    this.setProperty(name, value);
186  },
187  setPropertyAsUint32(name, value) {
188    this.setProperty(name, value);
189  },
190  setPropertyAsBool(name, value) {
191    this.setProperty(name, value ? "1" : "0");
192  },
193  deleteProperty(name) {
194    this._properties.delete(name);
195  },
196  hasEmailAddress(emailAddress) {
197    emailAddress = emailAddress.toLowerCase();
198    if (this.getProperty("PrimaryEmail", "").toLowerCase() == emailAddress) {
199      return true;
200    }
201    if (this.getProperty("SecondEmail", "").toLowerCase() == emailAddress) {
202      return true;
203    }
204    return false;
205  },
206  translateTo(type) {
207    // Get nsAbCardProperty to do the work, the code is in C++ anyway.
208    let cardCopy = Cc["@mozilla.org/addressbook/cardproperty;1"].createInstance(
209      Ci.nsIAbCard
210    );
211    cardCopy.UID = this.UID;
212    cardCopy.copy(this);
213    return cardCopy.translateTo(type);
214  },
215  generatePhoneticName(lastNameFirst) {
216    if (lastNameFirst) {
217      return (
218        this.getProperty("PhoneticLastName", "") +
219        this.getProperty("PhoneticFirstName", "")
220      );
221    }
222    return (
223      this.getProperty("PhoneticFirstName", "") +
224      this.getProperty("PhoneticLastName", "")
225    );
226  },
227  generateChatName() {
228    for (let name of [
229      "_GoogleTalk",
230      "_AimScreenName",
231      "_Yahoo",
232      "_Skype",
233      "_QQ",
234      "_MSN",
235      "_ICQ",
236      "_JabberId",
237      "_IRC",
238    ]) {
239      if (this._properties.has(name)) {
240        return this._properties.get(name);
241      }
242    }
243    return "";
244  },
245  copy(srcCard) {
246    throw Components.Exception(
247      "nsIAbCard.copy() not implemented",
248      Cr.NS_ERROR_NOT_IMPLEMENTED
249    );
250  },
251  equals(card) {
252    return this.UID == card.UID;
253  },
254};
255