Bug 119459 - "Option to add a photo/image/picture to each entry of the addressbook" [r=Standard8 sr=bienvenu ui-review=clarkbw]
authorJosh Geenen <joshgeenen+bugzilla@gmail.com>
Thu, 27 Aug 2009 22:27:20 +0100
changeset 3439 5c26af85988a8fdd672f22d05452d3ab0009fa5e
parent 3438 220ad101680c30e87e650eff6c545863e58f6a7d
child 3440 d92896496cf2454bd053c1455e207549fe3fe13f
push idunknown
push userunknown
push dateunknown
reviewersStandard8, bienvenu
bugs119459
Bug 119459 - "Option to add a photo/image/picture to each entry of the addressbook" [r=Standard8 sr=bienvenu ui-review=clarkbw]
mail/components/addrbook/content/abCardOverlay.js
mail/components/addrbook/content/abCardViewOverlay.js
mail/components/addrbook/content/abCommon.js
mail/components/addrbook/content/addressbook.xul
mail/locales/en-US/chrome/messenger/addressbook/abCardOverlay.dtd
mail/locales/en-US/chrome/messenger/addressbook/addressBook.properties
mail/themes/gnomestripe/jar.mn
mail/themes/gnomestripe/mail/addrbook/contact-generic-tiny.png
mail/themes/gnomestripe/mail/addrbook/contact-generic.png
mail/themes/pinstripe/jar.mn
mail/themes/pinstripe/mail/addrbook/contact-generic-tiny.png
mail/themes/pinstripe/mail/addrbook/contact-generic.png
mail/themes/qute/jar.mn
mail/themes/qute/mail/addrbook/contact-generic-tiny.png
mail/themes/qute/mail/addrbook/contact-generic.png
mailnews/addrbook/content/abCardOverlay.xul
mailnews/addrbook/content/abCardViewOverlay.xul
mailnews/addrbook/public/nsIAbCard.idl
suite/locales/en-US/chrome/mailnews/addressbook/abCardOverlay.dtd
suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties
suite/mailnews/addrbook/abCardOverlay.js
suite/mailnews/addrbook/abCardViewOverlay.js
suite/mailnews/addrbook/abCommon.js
suite/themes/classic/jar.mn
suite/themes/classic/messenger/addressbook/icons/contact-generic-tiny.png
suite/themes/classic/messenger/addressbook/icons/contact-generic.png
suite/themes/modern/jar.mn
suite/themes/modern/messenger/addressbook/icons/contact-generic-tiny.png
suite/themes/modern/messenger/addressbook/icons/contact-generic.png
--- a/mail/components/addrbook/content/abCardOverlay.js
+++ b/mail/components/addrbook/content/abCardOverlay.js
@@ -33,17 +33,17 @@
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 const kNonVcardFields =
         ["nickNameContainer", "secondaryEmailContainer", "screenNameContainer",
-         "customFields", "allowRemoteContent"];
+         "customFields", "allowRemoteContent", "abPhotoTab"];
 
 const kPhoneticFields =
         ["PhoneticLastName", "PhoneticLabel1", "PhoneticSpacer1",
          "PhoneticFirstName", "PhoneticLabel2", "PhoneticSpacer2"];
 
 // Item is |[dialogField, cardProperty]|.
 const kVcardFields =
         [ // Contact > Name
@@ -88,16 +88,17 @@ const kVcardFields =
           // Other > Notes
          ["Notes", "Notes"]];
 
 const kDefaultYear = 2000;
 var gEditCard;
 var gOnSaveListeners = new Array();
 var gOkCallback = null;
 var gHideABPicker = false;
+var originalPhotoURI = "";
 
 function OnLoadNewCard()
 {
   InitEditCard();
 
   gEditCard.card =
     (("arguments" in window) && (window.arguments.length > 0) &&
      (window.arguments[0] instanceof Components.interfaces.nsIAbCard))
@@ -273,16 +274,25 @@ function OnLoadEditCard()
   if ("arguments" in window && window.arguments[0])
   {
     if ("abURI" in window.arguments[0]) {
       var abURI = window.arguments[0].abURI;
       var directory = GetDirectoryFromURI(abURI);
 
       if (directory.readOnly) 
       {
+        // Disable the photo field and buttons
+        document.getElementById("generic").disabled          = true;
+        document.getElementById("GenericPhotoList").disabled = true;
+        document.getElementById("file").disabled             = true;
+        document.getElementById("web").disabled              = true;
+        document.getElementById("PhotoURI").readOnly         = true;
+        document.getElementById("PhotoURI").emptyText        = "";
+        document.getElementById("BrowsePhoto").disabled      = true;
+        document.getElementById("UpdatePhoto").disabled      = true;
         // Set all the editable vcard fields to read only
         for (var i = kVcardFields.length; i-- > 0; )
           document.getElementById(kVcardFields[i][0]).readOnly = true;
 
         // the birthday fields
         document.getElementById("Birthday").readOnly = true;
         document.getElementById("BirthYear").readOnly = true;
         document.getElementById("Age").readOnly = true;
@@ -468,16 +478,48 @@ function GetCardValues(cardproperty, doc
     allowRemoteContentEl.checked = cardproperty.getProperty("AllowRemoteContent", false) != false;
 
   // get phonetic fields if exist
   try {
     doc.getElementById("PhoneticFirstName").value = cardproperty.getProperty("PhoneticFirstName", "");
     doc.getElementById("PhoneticLastName").value = cardproperty.getProperty("PhoneticLastName", "");
   }
   catch (ex) {}
+
+  // Store the original photo URI and update the photo
+  // Select the type if there is a valid value stored for that type, otherwise
+  // select the generic photo
+  var type = cardproperty.getProperty("PhotoType", "");
+  document.getElementById("PhotoType").selectedItem =
+    document.getElementById(type ? type : "generic");
+  if (type == "file") {
+    originalPhotoURI = getPhotoURI(cardproperty.getProperty("PhotoName", ""));
+    var file = Components.classes["@mozilla.org/network/io-service;1"]
+                         .getService(Components.interfaces.nsIIOService)
+                         .newURI(originalPhotoURI, null, null)
+                         .QueryInterface(Components.interfaces.nsIFileURL)
+                         .file;
+    if (file) {
+      document.getElementById("PhotoFile").file = file;
+      updatePhoto("file");
+    }
+    else
+      updatePhoto("generic");
+  }
+  else if (type == "web") {
+    originalPhotoURI = getPhotoURI(cardproperty.getProperty("PhotoName", ""));
+    document.getElementById("PhotoURI").value = originalPhotoURI;
+    updatePhoto("web");
+  }
+  else {
+    originalPhotoURI = cardproperty.getProperty("PhotoURI", "");
+    if (originalPhotoURI)
+      document.getElementById("GenericPhotoList").value = originalPhotoURI;
+    updatePhoto("generic");
+  }
 }
 
 // when the ab card dialog is being loaded to show a vCard,
 // hide the fields which aren't supported
 // by vCard so the user does not try to edit them.
 function HideNonVcardFields()
 {
   document.getElementById("homeTabButton").hidden = true;
@@ -517,24 +559,50 @@ function CheckAndSetCardValues(cardprope
 
   var popup = document.getElementById("PreferMailFormatPopup");
   if (popup)
     cardproperty.setProperty("PreferMailFormat", popup.value);
     
   var allowRemoteContentEl = document.getElementById("allowRemoteContent");
   if (allowRemoteContentEl)
     cardproperty.setProperty("AllowRemoteContent", allowRemoteContentEl.checked);
-    
+
   // set phonetic fields if exist
   try {
     cardproperty.setProperty("PhoneticFirstName", doc.getElementById("PhoneticFirstName").value);
     cardproperty.setProperty("PhoneticLastName", doc.getElementById("PhoneticLastName").value);
   }
   catch (ex) {}
 
+  var type = document.getElementById("PhotoType").selectedItem.id;
+  var photoURI = originalPhotoURI;
+  if (type == "file" && document.getElementById("PhotoFile").file)
+    photoURI = "file://" + document.getElementById("PhotoFile").file.path;
+  else if (type == "web" && document.getElementById("PhotoURI").value)
+    photoURI = document.getElementById("PhotoURI").value;
+  else {
+    type = "generic";
+    photoURI = document.getElementById("GenericPhotoList").value;
+  }
+  if (photoURI != originalPhotoURI) {
+    // Store the original URI
+    cardproperty.setProperty("PhotoURI", photoURI);
+    // Remove the original, if any
+    removePhoto(cardproperty.getProperty("PhotoName", null));
+    // Save the photo if it isn't one of the generic photos
+    if (type != "generic") {
+      cardproperty.setProperty("PhotoType", "file");
+      // Save the new file and store its URI as PhotoName 
+      var file = savePhoto(photoURI);
+      if (file)
+        cardproperty.setProperty("PhotoName", file.leafName);
+    }
+    else
+      cardproperty.setProperty("PhotoType", "generic");
+  }
   return true;
 }
 
 function CleanUpWebPage(webPage)
 {
   // no :// yet so we should add something
   if ( webPage.length && webPage.search("://") == -1 )
   {
@@ -822,8 +890,92 @@ function modifyDatepicker(aDatepicker) {
       this.dateField.value = null;
       this.monthField.value = null;
     }
     // make the field's value null if aValue is null and the field's value isn't
     if (aValue == null && aField.value != null)
       aField.value = null;
   }
 }
+
+/**
+ * Updates the photo by setting the src attribute of the photo element.
+ *
+ * @param aType Optional. The type of photo (web, file, or generic).
+ *              If supplied the corresponding radio button will be selected.
+ *              If not supplied the type will be determined by the currently
+ *              selected type.
+ */
+function updatePhoto(aType) {
+  if (aType) {
+    // Select the type's radio button
+    document.getElementById("PhotoType").selectedItem =
+      document.getElementById(aType);
+  }
+  else
+    aType = document.getElementById("PhotoType").selectedItem.id;
+
+  var value;
+  if (aType == "file") {
+    var file = document.getElementById("PhotoFile").file;
+    value = file ? "file://" + file.path : "";
+  }
+  else if (aType == "web")
+    value = document.getElementById("PhotoURI").value;
+  else
+    value = document.getElementById("GenericPhotoList").value;
+  document.getElementById("photo").setAttribute("src", value ? value
+                                                             : defaultPhotoURI);
+}
+
+/**
+ * Removes the photo file at the given path, if present.
+ *
+ * @param aName The name of the photo to remove from the Photos directory.
+ *
+ * @return true if the file was deleted.
+ */
+function removePhoto(aName) {
+  if (!aName)
+    return false;
+  // Get the directory with all the photos
+  var file = getPhotosDir();
+  // Get the photo (throws an exception for invalid names)
+  try {
+    file.append(aName);
+  }
+  catch (e) {
+    return false;
+  }
+  if (file.exists()) {
+    try {
+      file.remove(false);
+      return true;
+    }
+    catch (e) {}
+  }
+  return false;
+}
+
+/**
+ * Opens a file picker with image filters to look for a contact photo.
+ * If the user selects a file and clicks OK then the PhotoURI textbox is set
+ * with a file URI pointing to that file and updatePhoto is called.
+ *
+ * @return true if the OK button was clicked and a photo was chosen
+ */
+function browsePhoto() {
+  var nsIFilePicker = Components.interfaces.nsIFilePicker;
+  var fp = Components.classes["@mozilla.org/filepicker;1"]
+                     .createInstance(nsIFilePicker);
+  fp.init(window, gAddressBookBundle.getString("browsePhoto"), nsIFilePicker.modeOpen);
+  
+  // Add All Files & Image Files filters and select the latter
+  fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterImages);
+  fp.filterIndex = 1;
+
+  if (fp.show() == nsIFilePicker.returnOK) {
+    document.getElementById("PhotoFile").file = fp.file;
+    updatePhoto("file");
+    return true;
+  }
+  return false;
+}
--- a/mail/components/addrbook/content/abCardViewOverlay.js
+++ b/mail/components/addrbook/content/abCardViewOverlay.js
@@ -162,16 +162,18 @@ function OnLoadCardView()
   cvData.cvWorkAddress  = doc.getElementById("cvWorkAddress");
   cvData.cvWorkAddress2  = doc.getElementById("cvWorkAddress2");
   cvData.cvWorkCityStZip  = doc.getElementById("cvWorkCityStZip");
   cvData.cvWorkCountry  = doc.getElementById("cvWorkCountry");
   cvData.cvbWorkMapItBox  = doc.getElementById("cvbWorkMapItBox");
   cvData.cvWorkMapIt = doc.getElementById("cvWorkMapIt");
   cvData.cvWorkWebPageBox = doc.getElementById("cvWorkWebPageBox");
   cvData.cvWorkWebPage  = doc.getElementById("cvWorkWebPage");
+  cvData.cvbPhoto = doc.getElementById("cvbPhoto");
+  cvData.cvPhoto  = doc.getElementById("cvPhoto");
 }
 
 // XXX todo
 // some similar code (in spirit) already exists, see OnLoadEditList()
 // perhaps we could combine and put in abCommon.js?
 function GetAddressesFromURI(uri)
 {
   var addresses = "";
@@ -207,16 +209,19 @@ function DisplayCardViewPane(realCard)
                displayName : realCard.displayName,
                isMailList : realCard.isMailList,
                mailListURI : realCard.mailListURI
   };
 
   var data = top.cvData;
   var visible;
 
+  // Contact photo
+  cvData.cvPhoto.setAttribute("src", getPhotoURI(card.getProperty("PhotoName")));
+
   var titleString;
   if (generatedName == "")
     titleString = card.primaryEmail;  // if no generatedName, use email
   else
     titleString = generatedName;
 
   // set fields in card view pane
   if (card.isMailList)
--- a/mail/components/addrbook/content/abCommon.js
+++ b/mail/components/addrbook/content/abCommon.js
@@ -51,16 +51,18 @@ var gPrefs = Components.classes["@mozill
 var gHeaderParser = Components.classes["@mozilla.org/messenger/headerparser;1"].getService(Components.interfaces.nsIMsgHeaderParser);
 
 const kDefaultSortColumn = "GeneratedName";
 const kDefaultAscending = "ascending";
 const kDefaultDescending = "descending";
 const kLdapUrlPrefix = "moz-abldapdirectory://";
 const kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab";
 const kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab";
+// The default image for contacts
+var defaultPhotoURI = "chrome://messenger/skin/addressbook/icons/contact-generic.png";
 
 // Controller object for Dir Pane
 var DirPaneController =
 {
   supportsCommand: function(command)
   {
     switch (command) {
       case "cmd_selectAll":
@@ -914,8 +916,171 @@ function setupLdapAutocompleteSession()
               removeSession(gLDAPSession);
         gSessionAdded = false;
       }
     }
 
     gLDAPSession = LDAPSession;
     gSetupLdapAutocomplete = true;
 }
+
+/**
+ * Returns an nsIFile of the directory in which contact photos are stored.
+ * This will create the directory if it does not yet exist.
+ */
+function getPhotosDir() {
+  var file = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+  // Get the Photos directory
+  file.append("Photos");
+  if (!file.exists() || !file.isDirectory())
+    file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777);
+  return file;
+}
+
+/**
+ * Returns a URI specifying the location of a photo based on its name.
+ * If the name is blank, or if the photo with that name is not in the Photos
+ * directory then the default photo URI is returned.
+ *
+ * @param aPhotoName The name of the photo from the Photos folder, if any.
+ *
+ * @return A URI pointing to a photo.
+ */
+function getPhotoURI(aPhotoName) {
+  if (!aPhotoName)
+    return defaultPhotoURI;
+  var file = getPhotosDir();
+  try {
+    file.append(aPhotoName);
+  }
+  catch (e) {
+    return defaultPhotoURI;
+  }
+  if (!file.exists())
+    return defaultPhotoURI;
+  return Components.classes["@mozilla.org/network/io-service;1"]
+                   .getService(Components.interfaces.nsIIOService)
+                   .newFileURI(file).spec;
+}
+
+/**
+ * Saves the given input stream to a file.
+ *
+ * @param aIStream The input stream to save.
+ * @param aFile    The file to which the stream is saved.
+ */
+function saveStreamToFile(aIStream, aFile) {
+  if (!(aIStream instanceof Components.interfaces.nsIInputStream))
+    throw "Invalid stream passed to saveStreamToFile";
+  if (!(aFile instanceof Components.interfaces.nsIFile))
+    throw "Invalid file passed to saveStreamToFile";
+  // Write the input stream to the file
+  var fstream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]
+                          .createInstance(Components.interfaces.nsIFileOutputStream);
+  var buffer  = Components.classes["@mozilla.org/network/buffered-output-stream;1"]
+                          .createInstance(Components.interfaces.nsIBufferedOutputStream);
+  fstream.init(aFile, 0x04 | 0x08 | 0x20, 0600, 0); // write, create, truncate
+  buffer.init(fstream, 8192);
+
+  buffer.writeFrom(aIStream, aIStream.available());
+
+  // Close the output streams
+  if (buffer instanceof Components.interfaces.nsISafeOutputStream)
+      buffer.finish();
+  else
+      buffer.close();
+  if (fstream instanceof Components.interfaces.nsISafeOutputStream)
+      fstream.finish();
+  else
+      fstream.close();
+  // Close the input stream
+  aIStream.close();
+  return aFile;
+}
+
+/**
+ * Copies the photo at the given URI in a folder named "Photos" in the current
+ * profile folder.
+ * The filename is randomly generated and is unique.
+ * The URI is used to obtain a channel which is then opened synchronously and
+ * this stream is written to the new file to store an offline, local copy of the
+ * photo.
+ *
+ * @param aUri The URI of the photo.
+ *
+ * @return An nsIFile representation of the photo.
+ */
+function savePhoto(aUri) {
+  if (!aUri)
+    return false;
+
+  // Get the photos directory and check that it exists
+  var file = getPhotosDir();
+
+  // Create a channel from the URI and open it as an input stream
+  var ios = Components.classes["@mozilla.org/network/io-service;1"]
+                      .getService(Components.interfaces.nsIIOService);
+  var channel = ios.newChannelFromURI(ios.newURI(aUri, null, null));
+  var istream = channel.open();
+
+  // Get the photo file
+  file.append(makePhotoFilename(file.path, findPhotoExt(aUri, channel)));
+
+  return saveStreamToFile(istream, file);
+}
+
+/**
+ * Finds the file extension of the photo identified by the URI, if possible.
+ * This function can be overridden (with a copy of the original) for URIs that
+ * do not identify the extension or when the Content-Type response header is
+ * either not set or isn't 'image/png', 'image/jpeg', or 'image/gif'.
+ * The original function can be called if the URI does not match.
+ *
+ * @param aUri The URI of the photo.
+ * @param aChannel The opened channel for the URI.
+ *
+ * @return The extension of the file, if any, including the period.
+ */
+function findPhotoExt(aUri, aChannel) {
+  if (aChannel) {
+    try {
+      aChannel.QueryInterface(Components.interfaces.nsIHttpChannel);
+      var header = aChannel.getResponseHeader("Content-Type");
+      var type   = header ? header.split(";")[0] : "";
+      switch (type.toLowerCase()) {
+        case "image/png":
+          return ".png";
+        case "image/jpeg":
+          return ".jpg";
+        case "image/gif":
+          return ".gif";
+      }
+    } catch (e) {}
+  }
+
+  var index = aUri ? aUri.lastIndexOf(".") : -1;
+  if (index == -1)
+    return "";
+   return aUri.substring(index);
+}
+
+/**
+ * Generates a unique filename to be used for a local copy of a contact's photo.
+ *
+ * @param aPath      The path to the folder in which the photo will be saved.
+ * @param aExtension The file extension of the photo.
+ *
+ * @return A unique filename in the given path.
+ */
+function makePhotoFilename(aPath, aExtension) {
+  var filename, newFile;
+  // Find a random filename for the photo that doesn't exist yet
+  do {
+    filename = new String(Math.random()).replace("0.", "") + aExtension;
+    newFile = Components.classes["@mozilla.org/file/local;1"]
+                        .createInstance(Components.interfaces.nsILocalFile);
+    newFile.initWithPath(aPath);
+    newFile.append(filename);
+  } while (newFile.exists());
+  return filename;
+}
--- a/mail/components/addrbook/content/addressbook.xul
+++ b/mail/components/addrbook/content/addressbook.xul
@@ -738,16 +738,24 @@
       <splitter id="results-splitter" collapse="after" persist="state"/>
       
       <!-- card view -->
       <hbox id="CardViewOuterBox" flex="1" minheight="100" height="200" persist="height"> 
         <vbox id="CardViewBox" flex="1">
           <vbox id="CardViewInnerBox" collapsed="true" flex="1">
             <description id="CardTitle"/>
             <hbox flex="1">
+              <vbox id="cvbPhoto" class="cardViewGroup">
+                <hbox style="min-width: 10ch; max-width: 10ch;">
+                  <spacer flex="1"/>
+                  <html:img align="center" src="" id="cvPhoto"
+                            style="max-width: 10ch; max-height: 10ch; min-width: 1ch;"/>
+                  <spacer flex="1"/>
+                </hbox>
+              </vbox>
               <vbox flex="1">
                 <vbox id="cvbContact" class="cardViewGroup">
                   <description class="CardViewHeading" id="cvhContact">&contact.heading;</description>          
                   <description class="CardViewLink" id="cvListNameBox">
                     <html:p><html:a href="" id="cvListName"/></html:p>
                   </description>
                   <description class="CardViewText" id="cvDisplayName"/>
                   <description class="CardViewText" id="cvNickname"/>
--- a/mail/locales/en-US/chrome/messenger/addressbook/abCardOverlay.dtd
+++ b/mail/locales/en-US/chrome/messenger/addressbook/abCardOverlay.dtd
@@ -85,23 +85,23 @@
 embed content from remote sources. Opening such a message will open a
 connection to this external source. This may allow tracking of the
 message being read. Checking this box will allow such external embedded
 content in HTML messages from this contact.">
 
 <!ENTITY WorkPhone.label                "Work:">
 <!ENTITY WorkPhone.accesskey            "k">
 <!ENTITY HomePhone.label                "Home:">
-<!ENTITY HomePhone.accesskey            "o">
+<!ENTITY HomePhone.accesskey            "m">
 <!ENTITY FaxNumber.label                "Fax:">
 <!ENTITY FaxNumber.accesskey            "x">
 <!ENTITY PagerNumber.label              "Pager:">
 <!ENTITY PagerNumber.accesskey          "g">
 <!ENTITY CellularNumber.label           "Mobile:">
-<!ENTITY CellularNumber.accesskey       "M">
+<!ENTITY CellularNumber.accesskey       "b">
 
 <!ENTITY Home.tab                       "Private">
 <!ENTITY Home.accesskey                 "P">
 <!ENTITY HomeAddress.label              "Address:">
 <!ENTITY HomeAddress.accesskey          "A">
 <!ENTITY HomeAddress2.label             "">
 <!ENTITY HomeAddress2.accesskey         "">
 <!ENTITY HomeCity.label                 "City:">
@@ -124,17 +124,17 @@ content in HTML messages from this conta
 
 <!ENTITY Work.tab                       "Work">
 <!ENTITY Work.accesskey                 "W">
 <!ENTITY JobTitle.label                 "Title:">
 <!ENTITY JobTitle.accesskey             "i">
 <!ENTITY Department.label               "Department:">
 <!ENTITY Department.accesskey           "m">
 <!ENTITY Company.label                  "Organization:">
-<!ENTITY Company.accesskey              "O">
+<!ENTITY Company.accesskey              "n">
 <!ENTITY WorkAddress.label              "Address:">
 <!ENTITY WorkAddress.accesskey          "A">
 <!ENTITY WorkAddress2.label             "">
 <!ENTITY WorkAddress2.accesskey         "">
 <!ENTITY WorkCity.label                 "City:">
 <!ENTITY WorkCity.accesskey             "y">
 <!ENTITY WorkState.label                "State/Province:">
 <!ENTITY WorkState.accesskey            "S">
@@ -152,8 +152,24 @@ content in HTML messages from this conta
 <!ENTITY Custom2.label                  "Custom 2:">
 <!ENTITY Custom2.accesskey              "2">
 <!ENTITY Custom3.label                  "Custom 3:">
 <!ENTITY Custom3.accesskey              "3">
 <!ENTITY Custom4.label                  "Custom 4:">
 <!ENTITY Custom4.accesskey              "4">
 <!ENTITY Notes.label                    "Notes:">
 <!ENTITY Notes.accesskey                "N">
+
+<!ENTITY Photo.tab                      "Photo">
+<!ENTITY Photo.accesskey                "o">
+<!ENTITY PhotoDesc.label                "Pick one of the following:">
+<!ENTITY GenericPhoto.label             "Generic Photo">
+<!ENTITY GenericPhoto.accesskey         "G">
+<!ENTITY DefaultPhoto.label             "Default">
+<!ENTITY PhotoFile.label                "On this Computer">
+<!ENTITY PhotoFile.accesskey            "n">
+<!ENTITY BrowsePhoto.label              "Browse">
+<!ENTITY BrowsePhoto.accesskey          "r">
+<!ENTITY PhotoURL.label                 "On the Web">
+<!ENTITY PhotoURL.accesskey             "b">
+<!ENTITY PhotoURL.emptytext             "Paste or type the web address of a photo">
+<!ENTITY UpdatePhoto.label              "Update">
+<!ENTITY UpdatePhoto.accesskey          "u">
--- a/mail/locales/en-US/chrome/messenger/addressbook/addressBook.properties
+++ b/mail/locales/en-US/chrome/messenger/addressbook/addressBook.properties
@@ -110,16 +110,18 @@ cityOrStateAndZip=%1$S %2$S
 
 stateZipSeparator=
 
 prefixTo=To
 prefixCc=Cc
 prefixBcc=Bcc
 addressBook=Address Book
 
+browsePhoto=Contact Photo
+
 # mailnews.js
 ldap_2.servers.pab.description=Personal Address Book
 ldap_2.servers.history.description=Collected Addresses
 ## LOCALIZATION NOTE (ldap_2.servers.osx.description is only used on Mac OS X)
 ldap_2.servers.osx.description=Mac OS X Address Book
 
 # status bar stuff
 ## LOCALIZATION NOTE (totalContactStatus):
--- a/mail/themes/gnomestripe/jar.mn
+++ b/mail/themes/gnomestripe/jar.mn
@@ -48,16 +48,18 @@ classic.jar:
   skin/classic/messenger/activity/indexMailIcon.png           (mail/activity/indexMailIcon.png)
   skin/classic/messenger/addressbook/addressbook.css          (mail/addrbook/addressbook.css)
   skin/classic/messenger/addressbook/abContactsPanel.css      (mail/addrbook/abContactsPanel.css)
   skin/classic/messenger/addressbook/cardDialog.css           (mail/addrbook/cardDialog.css)
   skin/classic/messenger/addressbook/abResultsPane.css        (mail/addrbook/abResultsPane.css)
   skin/classic/messenger/addressbook/icons/abcard.png         (mail/addrbook/abcard.png)
   skin/classic/messenger/addressbook/icons/addrbook.png       (mail/addrbook/addrbook.png)
   skin/classic/messenger/addressbook/icons/ablist.png         (mail/addrbook/ablist.png)
+  skin/classic/messenger/addressbook/icons/contact-generic.png             (mail/addrbook/contact-generic.png)
+  skin/classic/messenger/addressbook/icons/contact-generic-tiny.png        (mail/addrbook/contact-generic-tiny.png)
   skin/classic/messenger/addressbook/icons/addressbook-toolbar.png         (mail/addrbook/addressbook-toolbar.png)
   skin/classic/messenger/addressbook/icons/addressbook-toolbar-small.png   (mail/addrbook/addressbook-toolbar-small.png)
   skin/classic/messenger/addressbook/icons/abcard-large.png   (mail/addrbook/abcard-large.png)
   skin/classic/messenger/addressbook/icons/remote-addrbook.png (mail/addrbook/remote-addrbook.png)
   skin/classic/messenger/addressbook/icons/remote-addrbook-error.png      (mail/addrbook/remote-addrbook-error.png)
   skin/classic/messenger/addressbook/icons/secure-remote-addrbook.png     (mail/addrbook/secure-remote-addrbook.png)
   skin/classic/messenger/messengercompose/messengercompose.css (mail/compose/messengercompose.css)
   skin/classic/messenger/messengercompose/editorOverlay.css    (mail/compose/editorOverlay.css)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..95e6a2e4afd27bbeea9619c13a73634f5334cfa2
GIT binary patch
literal 674
zc$@*E0$u%yP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXV>
z0|yjrB8a#E00JUOL_t(I%Z-yyPZU8A#edy1vjZgTu(%+L1fxU_EN2rFqlWL`cknaH
z3Aig9_@hjSpj-%XcV%~H|4dbRm}OywgYVEucUQgY_qxg*9v^>Ps8+YkEaw5NSdB;F
zy(e6#R=47~^r@S52z)R~P*fFgh~$_Vs*0$fs-wN|;`th(ndY5tn@00DQ5fNz2aGgL
zao*8tHYvq1mC77(Tw*ZjBch1ptSqk*g2OwHcLCl9c<(toJ7atMGwpVVZnr}m$E>fv
zVxwLsNfLs<VP@dK7fKDY;%PTGf7sjGrP*vEk~1|m1;EM4H`3D-=iH<ZV3=B=&x9<?
z==CyyiQp)Tn3<U&41!y4Zwc;9Glk2`OFEs-gGwSotJR|4@58+-Zw6|Hh@hI2=flwt
z+`&1Asxlb#G3;LnU}~rc!{LynT8-J+S<I|33NSOGD56@eQYw|k=l3KCq6NW|`3j4R
zi+JzH$pGbYnP)4@h{%LI4|ftog{;@3Uf*DPdU~ScmX@AUsmxDQ2MB?p%OnR^=q)D+
zK@g0Cf*_!rB!po&?!*ZKGsAgDJ{)rX<D8503$iR52jzKAdYWRUtgfz7E|*bN+%3T{
z&pA3g;^)OL8jS|sZWrg=SgV<FeSJ-<)#CJfN^QBu+S*IN5sC==U%s-lvx~!xCeS@P
zeS^V(cDv2h)fET(2fTUxmM{!M-oN|6W_@!sNdE^_B}wM^3#b}6f8+L94*&oF07*qo
IM6N<$f~S)$R{#J2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a0c2f959cf889f3debe4dbc4f781d9acdabafa12
GIT binary patch
literal 5392
zc$@(c74PbaP)<h;3K|Lk000e1NJLTq005Q%005Q<1^@s6JOOdy00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXV>
z0|qfZJ#D@K02Gi(L_t(|+U=cNQybTo$N#(4lDL_hF<@f^*hWai8BY~c=S-cG<C)~V
zB=hbMH#JrB1yVC7$y6md$wPAD#CBq1J76S`4Y3Up2H_Ay07<lZ&4X@z*?mz<*y>g<
zr%9`I-~H2nt-bc%s~Hi2F~$G@w{8iy0A#b-@@?OIvuPJDT%eBu;Ny>F0AST^VPPQ<
z3Wda91wkxH5*v^tA1>c)*?}lhKon_MmZf5`C@n57ma0&RU0q#WzZHwUaxfSU6d+QF
z7z%!)`DS5ZfE5{)!jOd1)52gVbd=BM^X%5GTY-&@jc{HN`dKM3Bs27jB!?(K3;-Bo
z<hP!0JR_<wQeq@Ap(qe?6e#U?$#PBvfUd5tfJ_uF$a4S6gS+?qCiAUPyYYuVeI&_*
za4>k>#aKYdX0uF^Bz9VWD3ToVo5(kZ@)Z(E4xJVtN|M9`0FWe!5fQ7pKi_<7*cgZc
z#E6L1!1B$v7Q|a6<!u#MexUAK6Ou(retgC9tu<I_M9Bxsw=*#xEZ@$=#73X+B)J3z
zR_zH`@0WEFOw)y^{KxHHa6&`=k@_El+oX6r)n3MWpH#1>-`BrK^|`fsbo;e8QS8_D
zK%_rb-&emLDZi&`|7=3!n}7b`>=nxvNDV_(8M0(dF?y-ols6RbQ2(RcRov!(CZ_lR
z%I7ie0S@nsagH<7r&zZuVD|;wkP~W=D>MR04;5otJeWCCTDBB`@!{gZrw7mKG#Pyd
zX0sU_6K3bNJqwmjt)So6H3m})hz2MXXxxnlm^ECs&#;kFnVel__+VzL^yZ(3ai4AU
zS($egi`i2yriO<Plp#|#luFy!%v811?vQbnsu2}x+U>R4bGQyHn`*^iBxBG)R#Psk
z5DTyY7c7*4IXF!gD#FY(%$}k-j;EMs6^pM{e6F<0rN7^3<HcFlN@ZWQECMn6=dwX1
zV+{ak`C7~~V6g@oi|K#aOcgzW=NYvJ`Mp0ot*^dAmSyvA!WvMUR;vkfp+vO|EF(8)
zs8&YaNugXScgX6u87H_kQ!0zVmI(Oq$9wj*!!ovF%}ZBP$T0VV#ccnInJI;g>khRM
zJ5vSw%Gclr*~~EKq`dq4+utFZ&A#zd#+)diOD8h5d`QQ<6UEUJX1C+4KP}umr3LJX
zL;?>UJV0MxA3x5r@k7bwa#&ki!_Ll*D%;K8pun^{Z(=LH8Z$l9%#=23$Vf3V!@yYe
zHbZ@=N4al&&vqt*ufP5Z0MJ_jOZ$yT{Zasc*RNkAlg=QMNh6(3<LKz9c3)Rl7bYVS
zOhqDyL?Q@xg<GU;q#wxQd^XR4)krSJzH%8P44E1(FyfP=Btvx)7!D6}`1I3HP%4$@
zqn$f<j{8|<Sw?m@i*$Mm>2w;IOa}RU-tfES^Lac?rSLSB0%HuLqoasKBAA+*!pMaY
z(>9@oU~v&u%_&x+Ld_h+AAGp#O>FSX*4U|ZicdcI8;*~UYsadtP?99P+IfZaRvo0%
z(^E&^nuxHovxA+T9o)ZvAKl&En4FwMWGaHomoFn63Zqj@Y@QqYa}<nnMoi}(nd)S5
zm8yZ?^p{_Lf!*C*)v;tEiF<eN;?=8HH5F^Mot&H?l}aI1QM0kJF-%4#5s5@_et6gi
z>nx&7W>dKsV>P&xN~MR4kJX(!caU0J(;ZJFR^Dhs6A@m$dWF5cJp_Y63=a*pS4Puy
zV7<jYPAZkcojX6YO=C|?OyK6f-7Lp<MDX$g1JBMN-QC^A7hindR-dW6yBmjxhklIJ
z8XsUskjx&``0x7Y=m>xN-~VwOgrAi?d-e>^o;^c<e?R8tqL__F(c9bWgY_nOtCUsZ
z<gk=8jd?+Q`9P^u!l$2p($>n5%jIzY{sTOCu!zf(moYathe#v>!9y{Z*gO?HL<pn{
z!>OEOV#=qLpGyQVR(~D-+-<V;vA+81??|W9u4sHB!sg3OY`)w?cXv0gUAu<pToi+Y
zgT7+5${T9o(t%1m#MA-s{rf-R$&+PQo`{o^6D%(;V|jTQV`F28Mx&UyG6PWzbQV}O
z1T3jhKcJ#f21-@Etxg}P1x(`xEBhN88~De!-+IE0RFkat?%l)8l^H~5qqs0S(kY6i
z0HOvN)pIjSo|F`ldhAdMnl6qqRW8EkUwn?S(Q%wVKa9^l`|Qk?KfNtGJv~KgErrxt
z3WGy~h|bPpHX21RD0&$zYuuD^mY%c9ks52(Jwqio`FtMBOG{YBlIOeb0|NtyL?Rd;
zA4f14)P(?^0IP<JWdLuaRONjYOcn2DRFi~s0!FS0(4E@w@Gz#Qr!hS}g@M6As28nz
zJb34GLm+0RRN77=&ke#{xd3D`8BYQ=Ha>=%H*eNwU)ajfw;ygzX5PvAeogL)l+RP$
zCX4q`xI+Rm8E-09b~g*zU{IW_+ML`B78&iUGnxF90O?_(@>A7-X}8Lr0@Cg6ZBK$F
z%QC+E?slakLM{E7x4mMSU}G&@x>!M>UJA%a0h79o3442cC=|R~D?FRcVr6B;9xQDd
zbhUO6np-Kw045z=q=F0@n5vtX<13{-TzrVb+@U2*)TWd<1Iwr^j~Qr2a1pO@3|wJl
z=hdrMo(D^kBz*t<4<^90cmJ^U(`K~27&}U3<OY?om4d8XE{9IA?d@%>RVOKuTaa=H
z7QcX)Mzu&EdGX-lx<X!%rBScnfA5SrO#pEJr~9f^L2j|uDHg}2DdR1@a`6B&h`E$U
z3Hbf@-#Z4Z)6-Mjy>r(j)j(}2W1=H~SUvk*+podI6ob>6^e91<Jgw8l>Pw+agH=MQ
zR8p<B@uCe33?M!qM|XF3W$S7%h<77cs;|Oyps8T0+^1S1QOV4!IkNw&^V&c&&lqEv
zpO15DjY3~%wN24#qE~1m_EIziX#)Ie97YYI91umt3{!8~!NCDaQppv{)IzX`3zK0w
zLJ=Ghrw0ilkODHZt@?OT6g>?V5n+FS-?01R2FwI!SZT%p4PYXUQjt;Ci^zJbSb6G*
zx9!(o_nrLK-R%nXYZ@AHgKEIY!F!Kiyy$tbPVy)E)**g8+K##<G)0wNxOhrM%2>;e
zD=fQA5P5!iI#8}+V+Ihlxnzls^olo8Pj|aswnL>}ys9$PSmShecY7YJa5(JR_{w>&
z&`_!LQcWB=C$m_~&ab%x!0IxqvqkOCmxHOvR!UWVR4uOsj(TYnOq0biY{~WYb!=^J
z;^M_i9iUiU9Ds<!6?p?JBbb!8%l{*d8#DvVTH+r+euQtn`33-R<3`EzVC8Z-Jh=Y=
z*RQ{WvGFn26%cWp$FlDRwSjy!4PoCI5)pp*p<Xp+YirB%U@iUpGd4Fj@zqyfBfXV&
zRea?<#v)_5NHG;gcT5AA7BZuMyV+e7i^ba0U%!6sd9bqCJpkoe6e}wU@Rq=}^XQht
z1gSAx++#+70kD;F@9-;CYpW5uLAjbtkZR!~4NqhUSR?7tP$<;dWHEQSL5IP6c^SQG
zSv^>-cgN~oL)1HlRIiY%lyYXTp+e!#1}i8AUF8N_6k|cBR2q5WD;Vs%yiTe%%ms@(
zHtMkiDbEcO2QDkX$cQEmY=R)5r>Cc5!3u>#hU0|Zjj^;!MK%D_t@0vG3POLh&!FIc
zCs<!!pDU80&4I;rgFN*z0Ssj70(oBX9On`xonqzh?yjz}!%~q{iNh6Bj1{%9RF$);
zliAgAdv%t(Hj+2})pn{Wulqw@y_Sxuk{@_IMAVLf2bjKdsB*B(!0iqU40O0+c|F}w
z83oiTl?o&dU@Dc;F3Dlm&31WmvSYy-s1&<((>fA|6a9$OL*x`B(o88P!+1-(L01*z
z{P1u`y2Fu?5%lzQyGpGb_X?FYg-CbI1Tc#fmKB1LNTlPynx2_*8&l4}B92m#UBJLm
zCWF`Kbr|Pjv5o_4YHG?gDTXE$i_~9}8Usu>SDWl_6PGSwXlSTopqiY#T)rfV3kTr^
zIF>99NL)7vUG=m^Com`<bN$_SI|i(G-g(EBYUS7!szD+Hl_G=sX~ZkjXB)U!Eav$I
ztJR==bZiuEttjBg6_OqGvf~Q1pxM|$!62g1Xa}g(yz6>`&G!nm6N@$KWgKfcbA>#2
z=;itieSLk1Ohw@4`X^1qSQ@}c4VbBlk#&;6>=7d)=kd{heB@b|`0l&cD?c3S9I!|~
zvaE)P^pJ5O;gv?V-0IJno|?voAAaaLs22WZ0khFr*OzN@^phtjmT{>E4>iL*3W`+2
zHhMQ^dD%J6vC*+HPeBz9hs&{*2j=)YT31+KE?6n5rXDC%fWXw3+o@4xcL*A$0c<!F
zf*=TxW!V#8RZ9nW;WFgTT%lE*rMr(z+@KwoXdHNXsg6TecNe@;u&}u+Qy4%57zb5k
zJ~7#L-=X?y=lZ=fv$U_*eG~dxoNfRgB0gefNi5h~vdY7;{9^HV+%sTJM!YjMsL6DL
zIbhty^vx_t<vj1&)ob-yQmzi%L%4M5V&{TY!^BXf$WWn-^v5!ZWwHQ62ne1~t;p0A
z1VQLv#j*g*P{qhtf}FX+y3NneyFL>?Gd=C8)Jb#KE3^q1SIJrvy#rJp0-1L4iRtd?
z#^~5+XM;ujOLi&XF}#k6fdavPVGuHYop=Aqz0+x~%*=Rh?9=grRb#;HL1qS<wL8??
z_}DnEU%&3U6L5ayJZ}8+4S3C(iWbcP<3mQRDNn>ntP%)+{MSEXWMsrOjo;hbgZJP6
zcLV|fA1v+48x=sd=CW(!E{p6sCo^xPVS*swgTH>@iVF+`gF(Fi!TX-A0PTD+1qU#_
zijk!%HI^o|csT+F`up+Tdw*{0MrVxSFE`)w^!)uM(hXG+v6^PAR5E+8HTEG$cOinS
z*REmYLi@VJsi_DiCMMuzb66CFe}PIP71M`BJlO1SRJ*W~@tEVKHZIvceEvMVZh}*h
zFjXmoi!0ekNi|=NwDO)8FOIiwPp|_n+2VL^Z>=Us^;EOkpZfiZlda0qXdc^?FEqH;
zOgm2k92^?-!7@M!iaIj2!%Q_(lyRS>q>?p<l&OV6A@uh4w%vugMN#y`pn2<Ep>A?J
z&rO1}Xrgtl(9*e9-n)OGzwH{lQwp0n&%Eo_4jQbsv9UU*$$qJ-x2a5twPQ`K-oD<B
z0IMlis9!6=cZI}xI<w`_oV!ARFwl;Q)!XNTWq3w)_1Uc;vxvD`V){YL{&VdIR=7iI
z);lT{sqY9H2lT3=hS5564v^-M^4{a)leTN{qoZRVEL*Nn<^6~Y3~|o7p?)#@evd3Z
zXFFcZ>G1H-2g|N2B*k?J)gcxcDib*mo@_tK2g`-7P@UuCL#BENYlIr+j5OuEK_cw$
z@3&oppPZaPk|fWUVrr_QAAkJWpt1}ZWE{vMm0)(4!MfJ68{C5=Np0Ea?Ff7ufTcgf
z_8>DUV?>VXf|wLV0ybZ6wr!(VFLLRFr9_20u51DsSx8RPoBrjqyL)*4{6*U~`qt(q
z4h|0d7>gIkP~{>3`tfAa?CCE<!JK3nEYb_IEaUm}=SU<Hc)i!&m-gM+*~Mp{{vR%0
zyomUG91|0le6X}<xE#zYN*6Kf9-9$xPTs#zC}3?Zg|*c+93Q(YN3^}YjqUAi^!N88
z9*^P5)hnI}>KpGB*005+SFze3GtipKE7a__-+n_fk;MA?21=!pt4~5Mm&1=g{)9)5
z9%FWP7IU#V^!E027FYl&Vk`#!ayUL{t#E_sbQ+131TvXz&$*9cv51wG6(kY~Oie`)
zkH_13iuKvLLQBYaahGz(nlMR{u)e;IWHO1|LC*6%Dk8%3=P&U5`3sDUj37QA$JA8B
zRf}3%=+M=W0f4=BFm{PYM@Lv&T|+AM6vbi@ooIV|d$|4GZ7lZmAQqd$Y;+c(P^go@
zGU_=t4qG<6hh!p&7cXA8zQ}8<?D+T?4<A0l($X@nT)l!=EY_COg3cH$yRfO{|HX@!
zNG6il+uQR=G6@S(Ybm7GQka;ygjhV@mUXq>df&EKEaGYEDOO#WE_mCPPG^u#XD~1@
zfcShISFX&o&2s&>2G+qr4y&uH*jV3a+c7=2*ulX8?%lnIM-LxiHad&g+#I@lx;qQ3
zOlBLa$t2S0j88<1txzc7$?_8<RuY(+o<ckx!}+(gIQETzRVtORvGEI%iKOSp$lAeV
zS;j9Lzu=dRUobj)0kL=-k;vp5O;?;LSSQCPSY2Dg)6`QG3I+d+zXjXf&0=>qi{9Q|
z#Nsi`Ub}{1DCkbGUcY{gWO5ZRp1<(SRU_PPM@L6ke6Wb0AODQ2*RCQSi=AnW{FZ@5
zM0ojf6RXK2c6YP>nQY^9#oFo`)>hYW`SNAN=VKTjA8$XfPK&2_`s^82ldEmFoZUBb
z+uGW~*47q=hK3M}$1pQ9-LiCrW3Uc$he)ojV*S~=pDu8@?eFj7?wz}M^zad)(OE?2
zqD@yCbTDm(u=Mj1mY1LS!z~`Od_Iq*rDY@%Nxb)$KVx#z`6XByo|^pSmw(67(y|Yg
z$K$YXzxfB2m!G^ju=cY4I9{jN<42Ei`@7q31}xt?%bYGs@xk)#Ow0$%Hy<qDe6W1;
z!Sc;FA1vQ|uzc%eYj{ZKzy9;T{Z{kMS1jLruzd5u^369NEZ^RM5fL*4OM9npzBPar
zQ}W|0mTxUF##lh7SfVHrV~kjd`Lp+Yb0}C~ti*^hMxrQ^c;UhY%49MW43JbP2*n$J
z_|r!q3W0%SS(X_SWX62peACBNM6w_Vf&c;oV?~Jx#c&vs$V3tWAfL~dih@uOD3Ft6
zf=F_R0>qdAFedv@`Nna50Eif&1ST_*m{1f5x#H<*L6GGV1Hcv*7R0WuuCCvT#a=lW
z3<nAjDMSqZ;$`2gEDW$Bqf!`>aC%x8426#J`Fx(CToH{eEGz^<p^*5iAc!SNVgr)o
u_Yi%vWe1{20a2u3S(b{$qO`cUSo(j?%_aqCcj`R=0000<MNUMnLSTXiYd_-v
--- a/mail/themes/pinstripe/jar.mn
+++ b/mail/themes/pinstripe/jar.mn
@@ -43,17 +43,19 @@ classic.jar:
   skin/classic/messenger/activity/compactMailIcon.png            (mail/activity/compactMailIcon.png)
   skin/classic/messenger/activity/indexMailIcon.png              (mail/activity/indexMailIcon.png)
   skin/classic/messenger/hud-style-button-middle-background.png  (mail/hud-style-button-middle-background.png)
   skin/classic/messenger/addressbook/addressbook.css             (mail/addrbook/addressbook.css)
   skin/classic/messenger/addressbook/abContactsPanel.css         (mail/addrbook/abContactsPanel.css)
   skin/classic/messenger/addressbook/cardDialog.css              (mail/addrbook/cardDialog.css)
   skin/classic/messenger/addressbook/abResultsPane.css           (mail/addrbook/abResultsPane.css)
   skin/classic/messenger/addressbook/icons/abcard.png            (mail/addrbook/abcard.png)
-  skin/classic/messenger/addressbook/icons/ablist.png            (mail/addrbook/ablist.png) 
+  skin/classic/messenger/addressbook/icons/ablist.png            (mail/addrbook/ablist.png)
+  skin/classic/messenger/addressbook/icons/contact-generic.png             (mail/addrbook/contact-generic.png)
+  skin/classic/messenger/addressbook/icons/contact-generic-tiny.png        (mail/addrbook/contact-generic-tiny.png)
   skin/classic/messenger/addressbook/icons/addressbook-toolbar.png         (mail/addrbook/addressbook-toolbar.png)
   skin/classic/messenger/addressbook/icons/addressbook-toolbar-small.png   (mail/addrbook/addressbook-toolbar-small.png)
   skin/classic/messenger/addressbook/icons/abcard.png            (mail/addrbook/abcard.png)
   skin/classic/messenger/addressbook/icons/ablist.png            (mail/addrbook/ablist.png)
   skin/classic/messenger/addressbook/icons/addrbook.png          (mail/addrbook/addrbook.png)
   skin/classic/messenger/addressbook/icons/addressbook-toolbar-small.png  (mail/addrbook/addressbook-toolbar-small.png)
   skin/classic/messenger/addressbook/icons/addressbook-toolbar.png        (mail/addrbook/addressbook-toolbar.png)
   skin/classic/messenger/addressbook/icons/remote-addrbook-error.png      (mail/addrbook/remote-addrbook-error.png)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..95e6a2e4afd27bbeea9619c13a73634f5334cfa2
GIT binary patch
literal 674
zc$@*E0$u%yP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXV>
z0|yjrB8a#E00JUOL_t(I%Z-yyPZU8A#edy1vjZgTu(%+L1fxU_EN2rFqlWL`cknaH
z3Aig9_@hjSpj-%XcV%~H|4dbRm}OywgYVEucUQgY_qxg*9v^>Ps8+YkEaw5NSdB;F
zy(e6#R=47~^r@S52z)R~P*fFgh~$_Vs*0$fs-wN|;`th(ndY5tn@00DQ5fNz2aGgL
zao*8tHYvq1mC77(Tw*ZjBch1ptSqk*g2OwHcLCl9c<(toJ7atMGwpVVZnr}m$E>fv
zVxwLsNfLs<VP@dK7fKDY;%PTGf7sjGrP*vEk~1|m1;EM4H`3D-=iH<ZV3=B=&x9<?
z==CyyiQp)Tn3<U&41!y4Zwc;9Glk2`OFEs-gGwSotJR|4@58+-Zw6|Hh@hI2=flwt
z+`&1Asxlb#G3;LnU}~rc!{LynT8-J+S<I|33NSOGD56@eQYw|k=l3KCq6NW|`3j4R
zi+JzH$pGbYnP)4@h{%LI4|ftog{;@3Uf*DPdU~ScmX@AUsmxDQ2MB?p%OnR^=q)D+
zK@g0Cf*_!rB!po&?!*ZKGsAgDJ{)rX<D8503$iR52jzKAdYWRUtgfz7E|*bN+%3T{
z&pA3g;^)OL8jS|sZWrg=SgV<FeSJ-<)#CJfN^QBu+S*IN5sC==U%s-lvx~!xCeS@P
zeS^V(cDv2h)fET(2fTUxmM{!M-oN|6W_@!sNdE^_B}wM^3#b}6f8+L94*&oF07*qo
IM6N<$f~S)$R{#J2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6555eceb0f3767123127a763da7883095aba3ad2
GIT binary patch
literal 5846
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
--- a/mail/themes/qute/jar.mn
+++ b/mail/themes/qute/jar.mn
@@ -77,16 +77,18 @@ classic.jar:
   skin/classic/messenger/activity/indexMailIcon.png              (mail/activity/indexMailIcon.png)
   skin/classic/messenger/addressbook/addressbook.css          (mail/addrbook/addressbook.css)
   skin/classic/messenger/addressbook/abContactsPanel.css      (mail/addrbook/abContactsPanel.css)
   skin/classic/messenger/addressbook/cardDialog.css           (mail/addrbook/cardDialog.css)
   skin/classic/messenger/addressbook/abResultsPane.css        (mail/addrbook/abResultsPane.css)
   skin/classic/messenger/addressbook/icons/abcard.png         (mail/addrbook/abcard.png)
   skin/classic/messenger/addressbook/icons/addrbook.png       (mail/addrbook/addrbook.png)
   skin/classic/messenger/addressbook/icons/ablist.png         (mail/addrbook/ablist.png)
+  skin/classic/messenger/addressbook/icons/contact-generic.png             (mail/addrbook/contact-generic.png)
+  skin/classic/messenger/addressbook/icons/contact-generic-tiny.png        (mail/addrbook/contact-generic-tiny.png)
   skin/classic/messenger/addressbook/icons/addressbook-toolbar.png         (mail/addrbook/addressbook-toolbar.png)
   skin/classic/messenger/addressbook/icons/addressbook-toolbar-small.png   (mail/addrbook/addressbook-toolbar-small.png)
   skin/classic/messenger/addressbook/icons/abcard-large.png   (mail/addrbook/abcard-large.png)
   skin/classic/messenger/addressbook/icons/remote-addrbook.png (mail/addrbook/remote-addrbook.png)
   skin/classic/messenger/addressbook/icons/remote-addrbook-error.png      (mail/addrbook/remote-addrbook-error.png)
   skin/classic/messenger/addressbook/icons/secure-remote-addrbook.png     (mail/addrbook/secure-remote-addrbook.png)
   skin/classic/messenger/messengercompose/messengercompose.css (mail/compose/messengercompose.css)
   skin/classic/messenger/messengercompose/compose-toolbar.png  (mail/compose/compose-toolbar.png)
@@ -264,16 +266,18 @@ classic.jar:
   skin/classic/aero/messenger/activity/indexMailIcon.png              (mail/activity/indexMailIcon-aero.png)
 * skin/classic/aero/messenger/addressbook/addressbook.css          (mail/addrbook/addressbook-aero.css)
   skin/classic/aero/messenger/addressbook/abContactsPanel.css      (mail/addrbook/abContactsPanel.css)
   skin/classic/aero/messenger/addressbook/cardDialog.css           (mail/addrbook/cardDialog.css)
   skin/classic/aero/messenger/addressbook/abResultsPane.css        (mail/addrbook/abResultsPane.css)
   skin/classic/aero/messenger/addressbook/icons/abcard.png         (mail/addrbook/abcard.png)
   skin/classic/aero/messenger/addressbook/icons/addrbook.png       (mail/addrbook/addrbook.png)
   skin/classic/aero/messenger/addressbook/icons/ablist.png         (mail/addrbook/ablist.png)
+  skin/classic/aero/messenger/addressbook/icons/contact-generic.png             (mail/addrbook/contact-generic.png)
+  skin/classic/aero/messenger/addressbook/icons/contact-generic-tiny.png        (mail/addrbook/contact-generic-tiny.png)
   skin/classic/aero/messenger/addressbook/icons/addressbook-toolbar.png         (mail/addrbook/addressbook-toolbar-aero.png)
   skin/classic/aero/messenger/addressbook/icons/addressbook-toolbar-small.png   (mail/addrbook/addressbook-toolbar-small-aero.png)
   skin/classic/aero/messenger/addressbook/icons/abcard-large.png   (mail/addrbook/abcard-large.png)
   skin/classic/aero/messenger/addressbook/icons/remote-addrbook.png (mail/addrbook/remote-addrbook.png)
   skin/classic/aero/messenger/addressbook/icons/remote-addrbook-error.png      (mail/addrbook/remote-addrbook-error.png)
   skin/classic/aero/messenger/addressbook/icons/secure-remote-addrbook.png     (mail/addrbook/secure-remote-addrbook.png)
   skin/classic/aero/messenger/messengercompose/messengercompose.css (mail/compose/messengercompose-aero.css)
   skin/classic/aero/messenger/messengercompose/compose-toolbar.png  (mail/compose/compose-toolbar.png)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..95e6a2e4afd27bbeea9619c13a73634f5334cfa2
GIT binary patch
literal 674
zc$@*E0$u%yP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXV>
z0|yjrB8a#E00JUOL_t(I%Z-yyPZU8A#edy1vjZgTu(%+L1fxU_EN2rFqlWL`cknaH
z3Aig9_@hjSpj-%XcV%~H|4dbRm}OywgYVEucUQgY_qxg*9v^>Ps8+YkEaw5NSdB;F
zy(e6#R=47~^r@S52z)R~P*fFgh~$_Vs*0$fs-wN|;`th(ndY5tn@00DQ5fNz2aGgL
zao*8tHYvq1mC77(Tw*ZjBch1ptSqk*g2OwHcLCl9c<(toJ7atMGwpVVZnr}m$E>fv
zVxwLsNfLs<VP@dK7fKDY;%PTGf7sjGrP*vEk~1|m1;EM4H`3D-=iH<ZV3=B=&x9<?
z==CyyiQp)Tn3<U&41!y4Zwc;9Glk2`OFEs-gGwSotJR|4@58+-Zw6|Hh@hI2=flwt
z+`&1Asxlb#G3;LnU}~rc!{LynT8-J+S<I|33NSOGD56@eQYw|k=l3KCq6NW|`3j4R
zi+JzH$pGbYnP)4@h{%LI4|ftog{;@3Uf*DPdU~ScmX@AUsmxDQ2MB?p%OnR^=q)D+
zK@g0Cf*_!rB!po&?!*ZKGsAgDJ{)rX<D8503$iR52jzKAdYWRUtgfz7E|*bN+%3T{
z&pA3g;^)OL8jS|sZWrg=SgV<FeSJ-<)#CJfN^QBu+S*IN5sC==U%s-lvx~!xCeS@P
zeS^V(cDv2h)fET(2fTUxmM{!M-oN|6W_@!sNdE^_B}wM^3#b}6f8+L94*&oF07*qo
IM6N<$f~S)$R{#J2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6555eceb0f3767123127a763da7883095aba3ad2
GIT binary patch
literal 5846
zc$|HBbyO5i)b@gaOLyl2Qj$vu5-ZChoeL6@OG$U9($XEu64EIR(!zq$-K}&iARr>(
zx4-ZE=lkP5@0^)CXX4D<dG0+k&z)FpO(kLi1_A&8K&+w+)5X-8{}mt}M#^BhZ(}MP
zPpFC>5R-y{)-jkizMHa<CuaV_e+3K5^T7|(N$;g-=mmGR_42j!umSk``U={+IC@%H
zy4eW2df0t9m0<t?Xy7U^c|E_6C#EOxmHFI%%vMg6K+Xt&f8Ps=_`IyK@z0*<@vg2V
zXLOcN&tB5XQqKM3=N+vl<4s*B(bp2rsS<UO7U5}=oEDVAyeO*A3Du3P)@n*hN)4PV
zhTvNfu+pAJ($D<-J*hhObX_ti5U_cDGU*J6gvv{ilJ#<pBW2YfcwtG9Osufkr1ti9
zq%tSPgE$p|!IM}b72OxLfR@jcBr{02Y?6@%y$Zczr6d;pqhf*~ME#;`hHB613gZbc
zhdM`k!K!`ABR>|MFg;++OHd>U=|~xSN|$bh9($|n#;K8!QEVo(P+s>$N`R6ms)DId
zHXQ%}MfezB1J-d$#1r-KGnC6Iig>1xhd%0=ah8A>6hanUGS$FODk#_K+tav>cgErJ
zu%*SS0#<Bl^@Y4t)+*QLAyY~HW=X##oWYStBOInBZ_lZ|gXyqTNmT{{I8`u5)_oR=
zg)NDPRU(_Nts1LZ4ZzC9EomA?9>q{g9%8MAYiB~<4iZdKoT>7%y$Wk#`@iD2skd=d
z2I!EW^ls1+!1kpv059f4L6PC7Bc&@=uD$f{T5}Ez(G|6FR*QZK0Y>5|vR6W1Bj(6>
z#0TvygM-t%v`pJ%1utqKp_^^-RtYw7%84raj18O-^j+4Z*AeaSuKVzEJnPjX$ioDN
z7VLf0z68X|S-dJHj{#Yjy=A?PFt#Qbi?QaU2Fb|Ah?gBk4?j(?KzPJ{v*Sc+`&d>V
zXbpbhYb9!gw~}rq8sa(QTDAF%Bgy1aK<Te~kIBfZMcSQ0`)zZFvty+xZ6pTCE!Vai
zilztQzp4VU1SSoA)Y;Vdija)>C>-MXx|GuGaa=B4R<7I6TO<rQ3j32yOOE)R3Zi=h
z4HY#&bqc_v?|Nf4wj{(jjqF+SfGQld>>LFJS@tFVI_?-z@~Xn7^{rGAfn%3R$EOm@
zCZ6;&V<eyGIVHq0f@^G)n|#ZNDH-RurZa3y80U9Dsp2a9=Xo)#G-d06K_-e|v(xm1
zpFM$&8a9Ge{3A3KMeiBS3M#6vNBm=CY238<MMd9K){ItDerosHO@6ay(ifGh`X*IO
zo8az-19CD7^))10xKkz3&+6hA5Lyy!Ll12uYJSA?&jy3=#uT%kJBZELi@fb{{_J~Y
z{YmQXVceI8ilAtv(c8|&^50UkN*D)hB-(2<WVQ4*f;&fXEQrJsQld!oB3ZNS{A^2s
zh=}M-vvDPll}Wwfz{LevLPEl;Ypq3U61%}K&owdHOAM$UZIsl6yTXtb59ScXiI|<h
zZz^Pwa6&d9Cw?5&^<V=o7h+Hw<Z}kN0Kea-%opu47k`*M+L^AeuV<SK`J-a}n9|NA
zx6dVu>dOnw_So18I_d750~l&!Q60)H7f>xGj*5X?C<k1YgIYA&ChAm;ZE;OtdqxW)
z-&{GWB<+7uto_O;Q)iK%E$g*{V3vYhzEXuAxT{sSB4TfeA8FUf7GdG#MRktZCoPRm
zN*ZCTNj)K96g0pmR#IQRxtc|m_Fl#2TZt?p&0Hc{;eaeZ=;GNi8eQe?4d+pzSa<1E
zbkX3L-rBMZXFwICBdn|*F)}jxoo!;>-rfp}h`hbAW}+1`gNaQOIPa3Sab&jn+aVRH
zzPB6_?drr$2GME4bM%B%A1+3OKDW15#Ele-wuPcu*1LnN9O_^F*xUpxIngZTN?;c4
zhXr9{^t6kLUh&YVE6`wwdmGCRp3#c3bcE`8)aMQ>gGFji*Sq45FB(8{d1g(S9T<(a
zPgYtNmzSZgu0l~-`B7y7Vz1+`DMx6Qe}y$J@t>~W7YcZH^hFZVx*ct}@IEj*JAJ*6
zug#PH(%v2`)P2)AgV>*3ScqO*Thokl=X}#MSoFCtxf(3>&lG)fvgP{kFQhi_%x4P~
zN9%XKjf?mAv554CUUl}j4-XHTadR|Yqg1&%6)iQXxp8^URgsFCofJAXug(w3^8JiO
zsp1r6{LaWoS>?W>V&k&&@)TuQl*;3R1bqjl>!C1m0tdJLis>QPB0|+j6${!FFyPtB
z=qO0ASylItXd-BbmU~X5%KK`g8gVtohr!!zXPBDPmQvFDP_InUkwK}b{@73fX%a;I
zd<-=x+Rh?#gFk^3XrpZkmz0$3{rVM~hK5F1LLv&r{`Np^1{fXqTp;&p8&{_4+o<X(
zyYd5`7;*i3CW{WzOM-aO@aD7OVS<PAlh)c(lw-MX!+K9B>r7T@O18@8ak;-xH|<o!
zAOfgE$~(H5cExW!2D*e*85EY#)qO7weL(HeF)|P3i1yzcH%=_K+*QCOWn^GJQgO!~
zG-aCkP=qa>Xe>@ZpzO4o_1jFfZ_^1^jqx&7sB#m>Rj{=`oG;_EFeC|gwDp!oCc3Z?
zH1NYcJ}K!_Yb$Lh;ijS_M(3<35*c-|42t=b3fr~OYfj5~>q?Oj_sKPcpC26@4$scb
z9hj_uPYM}d$>mNPBG$FF5hWE(lxA@87VE<=7C(Rev4#ES9~>NVJDL}8zK*P62|!QP
zd`JF%`E1}}V9}guC)GauOOux|;qI@sih6<%xZk+Kx4+ogA^RxZH2LM@)bWo3!6;K~
znRbUgym#kH0t3lj!NF_w`ypZcBR8iV(#=sZqNRQ@2B%_~xXz;l*~xLd`rLFoZxqD^
z^e5Vyn<G8i1Ah|;Q4tuL=-HYtUXsW}&yzq2eO0j{tij1~mZZMJFTEN=QQPOAh86{K
ziPUr~C*9^%+Bj%z#Hbl1em2SrDyR*8rJBcf-RSmy(vr`nLec%NTW&|`GN?;|o!l7W
z9Oa5yoYJ$k{T#9{`BI0U=Cxo8ZGCrl;?6CnB3xic1B0aBBN0u<^IQUi2fjw(x9{6c
z3+SFe-(6_c`DWSUFZ%K!@<(Esy~XtpnL3`az`<fd-9wqb_rEt@bG0nE`}xHL`l9JW
zkPJNPY0rUs%*9j%u%lpu{$|)!JT#By6Lm!GFFh;9ihrVAwzAqaue__AzmO{^C@7n1
z6xP+%`3wxu%Q~@qV2#*~Ddm6*XtctL=0!<gXbJD;@91|p6&T?_fwg8D_}2_I`R^~Y
zT9Im$HBh<X(T(Yn`R($<Xeoatsux<4qocwnn#ae-1$-i1XqI_(TQ_<Bc{q4VjNK!4
zu=5bdO4-(X_E&WOdcMRo5l_kD`uhBMj+Wpaxynx_LF33~t;!_wlvo0>3{awqqu*?S
zI`e_c-R+Uj4|Q+9WOKo2ucolm1?v$asFHBmL<BY+#CN=YG^Aht01#Eye`8?wEJ=e1
z+GxE!p=hhEE{wF^jYfdw?H7c!9;cf+0kSE+jU-BMR_ZH*K9<`_8n|Q%3&6tWFUS=#
zG+8vw&GS9$gQMQ)Exi~>dZ3<Ee_WvrKbXh}ed(=P6C}VG!~6(=s=i(V8<-;B##MYz
z5X(qaJ6U-5B5>|$rM<B1z(@~Zb7v&W@U<zb-E%MR{fRW}lL*$^_}h`H#tqZ!&ev&b
zf0U#|-zW;7Vq?n$-{Ncp{UQcz?v7nMN&kHE->npMMz&CA-oJR0t#n{TvT$#BJ;sLq
zw$K4OW>iq+4Y`qD5w~@A9&2Be>%W_TSb2L>n+IQsdpO>SIiY7pZ6-RT+RF!7tUemE
zKvQ$>ooOU=D)eTz(d%8F7_Zdkal+ERE;2MxR|sA{wz?GVG4V<Mnr`w{QuLk30dbNQ
zQOGkW@p`bZhEMXv&myLLx>s&Ep3|}nv9Q&cCE3YlyG}JO8o`gg1Z1}2Y&Ff4b7M(!
zrJ$xL5bl%`PyOK?gCneH=!0RGr44XI$d>F)Y?3}9{b<3ioxR9zcV-5*v}Ol!92*Pc
zl|-ZQwp9hFYc8C+ubln^jx}K<RMLGr0<P16m-43gI{UJ_I%}#k3FXM=5N=`ee1m`r
zMUw4w6UrnkEL`v-)j(R&sEsayCPzNyx`B;O3rtyp+AiLtelmnR+!dIw&TryEt>PFW
zDT>NoVpQ`!*8ipK*V838l2<Vk@M^zTgp20yI%}QuiFX^*(+JyN!~(E#_pmUF=Fq`C
zg`B&f>W1FUHfUYVeILKUj4dozRf!X;FI{XN#NmVu@1UdQ=K2`7{DiL3+2@GX8Mh6&
zKlI0Fw3azN$qpH{UV^w6c(6#zXcO)4ADU)LjE~<Oh`kj9{SzL!;$se{tQ#~Ui}%n6
zl2}#JydU~eJ8&ghTFRXf7AlF`YXhOFtGIL*=FXOv&RGz05FY1B(?ng#a&c25{uIW&
zjU2HT)l=q_#+iYzq|l2&1V2KOX3DgrX_v~fv)jr$DNyDgu{Y<3D*qVavEz@i(V356
z9_OurtYSdzM%$L5b<g&4vm@ZHtR}29%IlC7ae|`IQU>?EitzzvsdIIN^zju)o_w<U
z)a`)Z;ATT`1)JI?I!{msX^Al^6}QsH!oL3Jdd-doj-XKR$POQ~3qqu<YcyEEw=`ME
z^L{X)cD={a!UAyqz0I|mdNMt!<YPIGRTHfi*i<Tyeil9)?-rDpk|KJu50N`GPV@UQ
zc(qJA)HCYfpahZaBkIJ~5*C@-?4@$BDdj3V+lprW(q`job!xsVoR)ol@1s#0a^dZ9
zU>>ad%=6eS=@k^>WF=-rY%a|m!^q6oDA34Cmhh5#V|W9Df`@5gi;1?>R2P2Y`KL?9
zS`lyHdJcTTHAIJ_C<O>DB(pYZ;qDmH*?8WYv_>ein)CH#NG51(bYnYdkSn?8<+GC?
zWJhs-mM!h3RzL8nD;KN%ShW7geR#QiyW97@?=;8g`6RjrKiJl-i#H}2W|FI|&_{Rh
zGpUY0L$$pNcfyi=T^1gmd+hR=i>|KF%YWb=|GIXJ$w1J2>N@p8q-Y8_o{vTn@0k2C
zd<lvBlpGrco`E#~9E?8I`$KpY@dfIL;^rnNXns%x<w1XyY8SXy2MYvi{w~$-pQ|zm
z-k^lYiqF}T8^7OOHvk6WP&zcs`=J5)Q7=k^?ogL``~CZiZ^}?_cujnsX%oJSRT<nB
z@iX%oEf{BF_~&w$7FsrAl8e0V8r<0m=&O|KJ5Es_LEmXboY%XX^n|R9op@2%y&T`%
zg=`!*(FrKQ*Ndq87QF<LdYQiT2z&A@-2CWyAm`xV5PT{daQQpG_b>XNbU;ABa{<+n
zEte-^3$oP|-{wtx*3n?l$Fl;ySlDxZDuS)>o{-HrTB6JA>+O5eb#!IQBbrlMI1pG`
z;u%q|lb<G0sBfbX9`1+kf=GAOaNvS)Lffy*3??>Qi#iHxVTU-B_9A9!OB^3WK|E#O
z!$u5hEq+$}YHa_+uw6lu{U$o+$Po>B98N@!wT_4w`-l7RdD540=LWLqErE{ciH`?O
zK<8*_czH1041mMF`q0DOkg(k3*J`_aS*wq=BuSl<yKHOLyxi<PUCeD^Nfr0%tejw7
z;Qkn2poWkZwv6{JhT*-DUhPtQ3}{S>XV6F|HGOWbDosAd;Vl?R&z=Ij{rgw;chRYv
z<SKReZo=j&mBSeH%)D+K%0Qz5JapI=PSKQdSYTmd@CpcY9&%q=90;C}7RR|fC<@}L
z@?3pe5pD0>V2UtXg;#|Z^vtj`;pc)Q14PRSdsjCQ@U^rmca`@bA&2URABFX~hU$GG
z%?ZkQhPNgHSmGZ^ke4j=MMKsT0S2!X85rN~l5`2q&%XBE9wg+kp9ZgPK-{#^Qx>kS
z|8L!Ak<X8YLJtr{_PoTDMBgQf=JbTRN9}!mTmKt2Mi9&;J_?U}!j;QY;tu5~j0eWd
zNwrBt-pXl;eD^YP>V6jebf+LbQ{O&9*U8m&MJN!4p|@e2DyAK17_-L7J~vC~yf61z
z7jYEJV~{I9MSDY$cg=hH)pwjK<F07nrsCID1`YMgnQqfSPRit(kTab6=L`#Y0K?$M
z!-x6h_**r>4*J@F^@Jb8G@Om!R&x6dgDpJ?>&&`*0vwg1n&<Q}M8nHCzU8ZTl{Sc$
zJf0gaXdOfx`Q5DuPVOadlzDzQ_q2G%zH{XQIWvw4ktp+nZ$l-0PnmQL;B9Vw9q^#l
zA{EjG34v_zYg+nmh*>_{2v5&@kM{VT9sbO$%=#X5om|n?m3MVrot2v#S%n@&mZ13|
zL)p52RdOX7fv{?x$mN1VG@5lit8Y(QH<DHC&MtNcD(4*F-rfu-1Ucm4h7u{`|2`*&
zew52@Y6Fe_E&3%eD{&Z~oh>I->E1e~l(1bayxoN403~+u$J>|Mk1j(MH!dztxojr;
zV?0NOs(--yW@?PBh*e5|ibiC6Bw)y;S$9yZI)(&mP9+p{W0z22AQ`MOs1pH$-_6cZ
zadH3XHlAIEhSze_vw?HAJ!}_OR{Afd!3|4$<$8Q$1p8`E&dyuozP@Ms1NfY2joB)r
zY!v4YFMK}8D7NevMh7=g6TJRbEb4cE+l+qZ?@WFD`}g_HIpyet(B|}xnle`!@*|Dj
z5V?dr%-Twq7j?AYiaGh`=DB<37#ENVMZ<`Zbyj1W+m=jfMLPHIxkJB|7Tt<X|7)qt
zpheS3`=1{Em`p@W4DuF_RLd%rZ8Yhz8dzT9McCWpKNT{NkuKe%#n^W9q3f^T#SOkK
zg{boQco7<Amvzc2Rgb}m-v<0`=Rzs?pjN5fWUw*PPFbi_L`pnXk1tk~*_bJibD;87
zm7&oe+#IjcqoHq|0E{z?3=7@Z?9j$1A{D_IP#HP*;x%gLC-Z#|Secnw=Q4kpc0Ahi
zvIs2Rjg4VTJR{skqm{rJ1EU#F_Y;~zpCMbwj@CMSzgcIV-Oe4=`a4E!)VH=qE0MBp
z?Ug}(Tx5(WKS^_5<LI#&C`#WR`=F0eadDNusd5=05XGrtXl6W!v4EgiXUyyz=it!m
zo(aX7f#=ms+kfI{4Tt&tU8Dj5j@30amd?(^Wm@?!F<W0=>WzGPYC`GQ+7>rDjWN0(
zy%+5b)5^+9o9}NHq)f;=7y>VL2{t25{3TmLLFC^r85$ZSZohzOlx{`DpFg<8GxVLm
zvmjQ8ulG+4nY1)8(SDCrNC=2cpH?B2GwAOa<n!mVQE%qN*k*4c8f+Zxv&--B)jy|+
zRSj#uE;0emZ&ws~vFfp#aQ}Ca8140N1o_v&?18fn#$kB+;x!rB|1Z>D@XpoD0*#*F
z0WDb5Ki}bty$Z_=BP;qa!-O5?CaI1OO4lQH1pvu<S$KdI6so|&Ew}8b38bvruRk&;
zCxyE0OqHv5{rPg(CFEq<MwvEHC>Vsbg}pD&`y_q1P@9Je8y5>i9UH+n#ZxSdKO+oC
zS1oaPLd6&TMoTA^_p4swB#WvtkPSwnOx+8B0<=|mD%rq=6!`j3D%9|nr4;9gK6cpX
z*jS`hz2R}IYKi&jNy2qHc`3On8NV6HB=CRuL)G?yad>)q%BCoD-Z}D6)v8j^rxq7P
v()H5#e6?JrB$=@^%H+#<f4M*P>G0p9@i8u3x5e>p04Av@YQk!v7VrKK#2yCY
--- a/mailnews/addrbook/content/abCardOverlay.xul
+++ b/mailnews/addrbook/content/abCardOverlay.xul
@@ -37,33 +37,35 @@
 
  ***** END LICENSE BLOCK ***** -->
 
 <?xml-stylesheet href="chrome://messenger/skin/addressbook/cardDialog.css" type="text/css"?>
 
 <!DOCTYPE overlay SYSTEM "chrome://messenger/locale/addressbook/abCardOverlay.dtd">
 
 <overlay id="editcardOverlay"
-     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+     xmlns:html="http://www.w3.org/1999/xhtml">
 
 <stringbundleset id="stringbundleset">
   <stringbundle id="bundle_addressBook" src="chrome://messenger/locale/addressbook/addressBook.properties"/>
 </stringbundleset>
 
 <script type="application/x-javascript" src="chrome://messenger/content/addressbook/abCommon.js"/>
 <script type="application/x-javascript" src="chrome://messenger/content/addressbook/abCardOverlay.js"/>
 
 <vbox id="editcard">
   <tabbox>
     <tabs id="abTabs">
       <tab id="contactTabButton" label="&Contact.tab;"
            accesskey="&Contact.accesskey;"/>
       <tab id="homeTabButton" label="&Home.tab;" accesskey="&Home.accesskey;"/>
       <tab id="workTabButton" label="&Work.tab;" accesskey="&Work.accesskey;"/>
       <tab id="otherTabButton" label="&Other.tab;" accesskey="&Other.accesskey;"/>
+      <tab id="photoTabButton" label="&Photo.tab;" accesskey="&Photo.accesskey;"/>
     </tabs>
 
     <tabpanels id="abTabPanels" flex="1">
       <!-- ** Name Tab ** -->
       <!-- The following vbox contains two hboxes
            top: Name/Email/Phonenumber bottom: Email prefs. -->
       <vbox id="abNameTab" >
         <!-- This hbox contains two vboxes
@@ -384,12 +386,74 @@
                    accesskey="&Custom4.accesskey;"/>
             <textbox id="Custom4" flex="1"/>
           </hbox>
         </vbox>
         <label control="Notes" value="&Notes.label;"
                accesskey="&Notes.accesskey;"/>
         <textbox id="Notes" multiline="true" wrap="virtual" flex="1"/>
       </vbox>
+
+      <!-- ** Photo Tab ** -->
+      <vbox id="abPhotoTab" >
+        <hbox flex="1">
+          <vbox align="left">
+            <spacer flex="1"/>
+            <hbox id="photoBox" style="min-width: 25ch; max-width: 25ch;">
+              <spacer flex="1"/>
+              <html:img align="center" src="" id="photo"
+                        style="max-width: 25ch; max-height: 25ch; min-width: 1ch;"/>
+              <spacer flex="1"/>
+            </hbox>
+            <spacer flex="1"/>
+          </vbox>
+          <vbox>
+            <command id="PhotoCmd" oncommand="updatePhoto();"/>
+            <spacer flex="1"/>
+            <groupbox flex="1">
+              <caption label="&PhotoDesc.label;"/>
+              <radiogroup id="PhotoType">
+                <radio id="generic" label="&GenericPhoto.label;"
+                       command="PhotoCmd"
+                       accesskey="&GenericPhoto.accesskey;"
+                       selected="true"/>
+                <hbox class="indent">
+                  <menulist id="GenericPhotoList"
+                            oncommand="updatePhoto('generic');">
+                    <menupopup>
+                      <menuitem label="&DefaultPhoto.label;" selected="true"
+                                value="chrome://messenger/skin/addressbook/icons/contact-generic.png"
+                                image="chrome://messenger/skin/addressbook/icons/contact-generic-tiny.png"/>
+                    </menupopup>
+                  </menulist>
+                </hbox>
+                <radio id="file" label="&PhotoFile.label;"
+                       command="PhotoCmd"
+                       accesskey="&PhotoFile.accesskey;"/>
+                <hbox class="indent">
+                  <filefield id="PhotoFile" maxlength="255" flex="1"
+                             disabled="true"
+                             style="-moz-margin-start:2px;"/>
+                  <button oncommand="browsePhoto();" id="BrowsePhoto"
+                          label="&BrowsePhoto.label;"
+                          accesskey="&BrowsePhoto.accesskey;"/>
+                </hbox>
+                <radio id="web" label="&PhotoURL.label;"
+                       command="PhotoCmd"
+                       accesskey="&PhotoURL.accesskey;"/>
+                <hbox class="indent">
+                  <textbox id="PhotoURI" maxlength="255" style="width: 45ch;"
+                           emptytext="&PhotoURL.emptytext;"
+                           class="AddressCardEditWidth"/>
+                  <button oncommand="updatePhoto('web');" id="UpdatePhoto"
+                          label="&UpdatePhoto.label;"
+                          accesskey="&UpdatePhoto.accesskey;"/>
+                </hbox>
+              </radiogroup>
+            </groupbox>
+            <spacer flex="1"/>
+          </vbox>
+        </hbox>
+      </vbox>
     </tabpanels>
   </tabbox>
 </vbox>
 </overlay>
--- a/mailnews/addrbook/content/abCardViewOverlay.xul
+++ b/mailnews/addrbook/content/abCardViewOverlay.xul
@@ -47,17 +47,24 @@
 
   <vbox id="CardViewBox" flex="1">
   
   <vbox id="CardViewInnerBox" collapsed="true" flex="1">
     
     <description id="CardTitle"/>
 
     <hbox style="width:100%" flex="1">
-      
+      <vbox id="cvbPhoto" class="cardViewGroup">
+        <hbox style="min-width: 10ch; max-width: 10ch;">
+          <spacer flex="1"/>
+          <html:img align="center" src="" id="cvPhoto"
+            style="max-width: 10ch; max-height: 10ch; min-width: 1ch;"/>
+          <spacer flex="1"/>
+        </hbox>
+      </vbox>
       <vbox flex="1">
         <vbox id="cvbContact" class="cardViewGroup">
             <description class="CardViewHeading" id="cvhContact">&contact.heading;</description>          
 
             <description class="CardViewLink" id="cvListNameBox">
               <html:p><html:a href="" id="cvListName"/></html:p>
             </description>
 
--- a/mailnews/addrbook/public/nsIAbCard.idl
+++ b/mailnews/addrbook/public/nsIAbCard.idl
@@ -81,16 +81,20 @@ interface nsIAbPreferMailFormat {
  * - Custom1, Custom2, Custom3, Custom4
  * - Notes
  * - Integral properties:
  *   - LastModifiedDate
  *   - PopularityIndex
  *   - PreferMailFormat (see nsIAbPreferMailFormat)
  * - Boolean properties:
  *   - AllowRemoteContent
+ * - Photo properties:
+ *   - PhotoName
+ *   - PhotoType
+ *   - PhotoURI
  */
 [scriptable, uuid(fdac4023-cd19-4e75-9a88-8e48881076ea)]
 interface nsIAbCard : nsIAbItem {
   /**
    * A list of all the properties that this card has as an enumerator, whose
    * members are all nsIProperty objects.
    */
   readonly attribute nsISimpleEnumerator properties;
--- a/suite/locales/en-US/chrome/mailnews/addressbook/abCardOverlay.dtd
+++ b/suite/locales/en-US/chrome/mailnews/addressbook/abCardOverlay.dtd
@@ -85,23 +85,23 @@
 embed content from remote sources. Opening such a message will open a
 connection to this external source. This may allow tracking of the
 message being read. Checking this box will allow such external embedded
 content in HTML messages from this contact.">
 
 <!ENTITY WorkPhone.label                "Work:">
 <!ENTITY WorkPhone.accesskey            "k">
 <!ENTITY HomePhone.label                "Home:">
-<!ENTITY HomePhone.accesskey            "o">
+<!ENTITY HomePhone.accesskey            "m">
 <!ENTITY FaxNumber.label                "Fax:">
 <!ENTITY FaxNumber.accesskey            "x">
 <!ENTITY PagerNumber.label              "Pager:">
 <!ENTITY PagerNumber.accesskey          "g">
 <!ENTITY CellularNumber.label           "Mobile:">
-<!ENTITY CellularNumber.accesskey       "M">
+<!ENTITY CellularNumber.accesskey       "b">
 
 <!ENTITY Home.tab                       "Private">
 <!ENTITY Home.accesskey                 "P">
 <!ENTITY HomeAddress.label              "Address:">
 <!ENTITY HomeAddress.accesskey          "A">
 <!ENTITY HomeAddress2.label             "">
 <!ENTITY HomeAddress2.accesskey         "">
 <!ENTITY HomeCity.label                 "City:">
@@ -124,17 +124,17 @@ content in HTML messages from this conta
 
 <!ENTITY Work.tab                       "Work">
 <!ENTITY Work.accesskey                 "W">
 <!ENTITY JobTitle.label                 "Title:">
 <!ENTITY JobTitle.accesskey             "i">
 <!ENTITY Department.label               "Department:">
 <!ENTITY Department.accesskey           "m">
 <!ENTITY Company.label                  "Organization:">
-<!ENTITY Company.accesskey              "O">
+<!ENTITY Company.accesskey              "n">
 <!ENTITY WorkAddress.label              "Address:">
 <!ENTITY WorkAddress.accesskey          "A">
 <!ENTITY WorkAddress2.label             "">
 <!ENTITY WorkAddress2.accesskey         "">
 <!ENTITY WorkCity.label                 "City:">
 <!ENTITY WorkCity.accesskey             "y">
 <!ENTITY WorkState.label                "State/Province:">
 <!ENTITY WorkState.accesskey            "S">
@@ -152,8 +152,24 @@ content in HTML messages from this conta
 <!ENTITY Custom2.label                  "Custom 2:">
 <!ENTITY Custom2.accesskey              "2">
 <!ENTITY Custom3.label                  "Custom 3:">
 <!ENTITY Custom3.accesskey              "3">
 <!ENTITY Custom4.label                  "Custom 4:">
 <!ENTITY Custom4.accesskey              "4">
 <!ENTITY Notes.label                    "Notes:">
 <!ENTITY Notes.accesskey                "N">
+
+<!ENTITY Photo.tab                      "Photo">
+<!ENTITY Photo.accesskey                "o">
+<!ENTITY PhotoDesc.label                "Pick one of the following:">
+<!ENTITY GenericPhoto.label             "Generic Photo">
+<!ENTITY GenericPhoto.accesskey         "G">
+<!ENTITY DefaultPhoto.label             "Default">
+<!ENTITY PhotoFile.label                "On this Computer">
+<!ENTITY PhotoFile.accesskey            "n">
+<!ENTITY BrowsePhoto.label              "Browse">
+<!ENTITY BrowsePhoto.accesskey          "r">
+<!ENTITY PhotoURL.label                 "On the Web">
+<!ENTITY PhotoURL.accesskey             "b">
+<!ENTITY PhotoURL.emptytext             "Paste or type the web address of a photo">
+<!ENTITY UpdatePhoto.label              "Update">
+<!ENTITY UpdatePhoto.accesskey          "u">
--- a/suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties
+++ b/suite/locales/en-US/chrome/mailnews/addressbook/addressBook.properties
@@ -112,16 +112,18 @@ stateZipSeparator=
 
 prefixTo=To
 prefixCc=Cc
 prefixBcc=Bcc
 emptyEmailAddCard=You cannot add a card that has no primary email address
 emptyEmailAddCardTitle=Cannot Add Card
 addressBook=Address Book
 
+browsePhoto=Contact Photo
+
 # mailnews.js
 ldap_2.servers.pab.description=Personal Address Book
 ldap_2.servers.history.description=Collected Addresses
 ## LOCALIZATION NOTE (ldap_2.servers.oe.description is only used on Windows)
 ldap_2.servers.oe.description=OE Contacts
 ## LOCALIZATION NOTE (ldap_2.servers.osx.description is only used on Mac OS X)
 ldap_2.servers.osx.description=Mac OS X Address Book
 
--- a/suite/mailnews/addrbook/abCardOverlay.js
+++ b/suite/mailnews/addrbook/abCardOverlay.js
@@ -88,16 +88,17 @@ const kVcardFields =
           // Other > Notes
          ["Notes", "Notes"]];
 
 const kDefaultYear = 2000;
 var gEditCard;
 var gOnSaveListeners = new Array();
 var gOkCallback = null;
 var gHideABPicker = false;
+var originalPhotoURI = "";
 
 function OnLoadNewCard()
 {
   InitEditCard();
 
   gEditCard.card =
     (("arguments" in window) && (window.arguments.length > 0) &&
      (window.arguments[0] instanceof Components.interfaces.nsIAbCard))
@@ -273,16 +274,25 @@ function OnLoadEditCard()
   if ("arguments" in window && window.arguments[0])
   {
     if ("abURI" in window.arguments[0]) {
       var abURI = window.arguments[0].abURI;
       var directory = GetDirectoryFromURI(abURI);
 
       if (directory.readOnly) 
       {
+        // Disable the photo field and buttons
+        document.getElementById("generic").disabled          = true;
+        document.getElementById("GenericPhotoList").disabled = true;
+        document.getElementById("file").disabled             = true;
+        document.getElementById("web").disabled              = true;
+        document.getElementById("PhotoURI").readOnly         = true;
+        document.getElementById("PhotoURI").emptyText        = "";
+        document.getElementById("BrowsePhoto").disabled      = true;
+        document.getElementById("UpdatePhoto").disabled      = true;
         // Set all the editable vcard fields to read only
         for (var i = kVcardFields.length; i-- > 0; )
           document.getElementById(kVcardFields[i][0]).readOnly = true;
 
         // the birthday fields
         document.getElementById("Birthday").readOnly = true;
         document.getElementById("BirthYear").readOnly = true;
         document.getElementById("Age").readOnly = true;
@@ -465,16 +475,48 @@ function GetCardValues(cardproperty, doc
     allowRemoteContentEl.checked = cardproperty.getProperty("AllowRemoteContent", false) != false;
 
   // get phonetic fields if exist
   try {
     doc.getElementById("PhoneticFirstName").value = cardproperty.getProperty("PhoneticFirstName", "");
     doc.getElementById("PhoneticLastName").value = cardproperty.getProperty("PhoneticLastName", "");
   }
   catch (ex) {}
+
+  // Store the original photo URI and update the photo
+  // Select the type if there is a valid value stored for that type, otherwise
+  // select the generic photo
+  var type = cardproperty.getProperty("PhotoType", "");
+  document.getElementById("PhotoType").selectedItem =
+    document.getElementById(type ? type : "generic");
+  if (type == "file") {
+    originalPhotoURI = getPhotoURI(cardproperty.getProperty("PhotoName", ""));
+    var file = Components.classes["@mozilla.org/network/io-service;1"]
+                         .getService(Components.interfaces.nsIIOService)
+                         .newURI(originalPhotoURI, null, null)
+                         .QueryInterface(Components.interfaces.nsIFileURL)
+                         .file;
+    if (file) {
+      document.getElementById("PhotoFile").file = file;
+      updatePhoto("file");
+    }
+    else
+      updatePhoto("generic");
+  }
+  else if (type == "web") {
+    originalPhotoURI = getPhotoURI(cardproperty.getProperty("PhotoName", ""));
+    document.getElementById("PhotoURI").value = originalPhotoURI;
+    updatePhoto("web");
+  }
+  else {
+    originalPhotoURI = cardproperty.getProperty("PhotoURI", "");
+    if (originalPhotoURI)
+      document.getElementById("GenericPhotoList").value = originalPhotoURI;
+    updatePhoto("generic");
+  }
 }
 
 // when the ab card dialog is being loaded to show a vCard,
 // hide the fields which aren't supported
 // by vCard so the user does not try to edit them.
 function HideNonVcardFields()
 {
   document.getElementById("homeTabButton").hidden = true;
@@ -522,16 +564,43 @@ function CheckAndSetCardValues(cardprope
 
   // set phonetic fields if exist
   try {
     cardproperty.setProperty("PhoneticFirstName", doc.getElementById("PhoneticFirstName").value);
     cardproperty.setProperty("PhoneticLastName", doc.getElementById("PhoneticLastName").value);
   }
   catch (ex) {}
 
+  var type = document.getElementById("PhotoType").selectedItem.id;
+  var photoURI = originalPhotoURI;
+  if (type == "file" && document.getElementById("PhotoFile").file)
+    photoURI = "file://" + document.getElementById("PhotoFile").file.path;
+  else if (type == "web" && document.getElementById("PhotoURI").value)
+    photoURI = document.getElementById("PhotoURI").value;
+  else {
+    type = "generic";
+    photoURI = document.getElementById("GenericPhotoList").value;
+  }
+  if (photoURI != originalPhotoURI) {
+    // Store the original URI
+    cardproperty.setProperty("PhotoURI", photoURI);
+    // Remove the original, if any
+    removePhoto(cardproperty.getProperty("PhotoName", null));
+    // Save the photo if it isn't one of the generic photos
+    if (type != "generic") {
+      cardproperty.setProperty("PhotoType", "file");
+      // Save the new file and store its URI as PhotoName 
+      var file = savePhoto(photoURI);
+      if (file)
+        cardproperty.setProperty("PhotoName", file.leafName);
+    }
+    else
+      cardproperty.setProperty("PhotoType", "generic");
+  }
+
   return true;
 }
 
 function CleanUpWebPage(webPage)
 {
   // no :// yet so we should add something
   if ( webPage.length && webPage.search("://") == -1 )
   {
@@ -819,8 +888,92 @@ function modifyDatepicker(aDatepicker) {
       this.dateField.value = null;
       this.monthField.value = null;
     }
     // make the field's value null if aValue is null and the field's value isn't
     if (aValue == null && aField.value != null)
       aField.value = null;
   }
 }
+
+/**
+ * Updates the photo by setting the src attribute of the photo element.
+ *
+ * @param aType Optional. The type of photo (web, file, or generic).
+ *              If supplied the corresponding radio button will be selected.
+ *              If not supplied the type will be determined by the currently
+ *              selected type.
+ */
+function updatePhoto(aType) {
+  if (aType) {
+    // Select the type's radio button
+    document.getElementById("PhotoType").selectedItem =
+      document.getElementById(aType);
+  }
+  else
+    aType = document.getElementById("PhotoType").selectedItem.id;
+
+  var value;
+  if (aType == "file") {
+    var file = document.getElementById("PhotoFile").file;
+    value = file ? "file://" + file.path : "";
+  }
+  else if (aType == "web")
+    value = document.getElementById("PhotoURI").value;
+  else
+    value = document.getElementById("GenericPhotoList").value;
+  document.getElementById("photo").setAttribute("src", value ? value
+                                                             : defaultPhotoURI);
+}
+
+/**
+ * Removes the photo file at the given path, if present.
+ *
+ * @param aName The name of the photo to remove from the Photos directory.
+ *
+ * @return true if the file was deleted.
+ */
+function removePhoto(aName) {
+  if (!aName)
+    return false;
+  // Get the directory with all the photos
+  var file = getPhotosDir();
+  // Get the photo (throws an exception for invalid names)
+  try {
+    file.append(aName);
+  }
+  catch (e) {
+    return false;
+  }
+  if (file.exists()) {
+    try {
+      file.remove(false);
+      return true;
+    }
+    catch (e) {}
+  }
+  return false;
+}
+
+/**
+ * Opens a file picker with image filters to look for a contact photo.
+ * If the user selects a file and clicks OK then the PhotoURI textbox is set
+ * with a file URI pointing to that file and updatePhoto is called.
+ *
+ * @return true if the OK button was clicked and a photo was chosen
+ */
+function browsePhoto() {
+  var nsIFilePicker = Components.interfaces.nsIFilePicker;
+  var fp = Components.classes["@mozilla.org/filepicker;1"]
+	                   .createInstance(nsIFilePicker);
+  fp.init(window, gAddressBookBundle.getString("browsePhoto"), nsIFilePicker.modeOpen);
+  
+  // Add All Files & Image Files filters and select the latter
+  fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterImages);
+  fp.filterIndex = 1;
+
+  if (fp.show() == nsIFilePicker.returnOK) {
+    document.getElementById("PhotoFile").file = fp.file;
+    updatePhoto("file");
+    return true;
+  }
+  return false;
+}
--- a/suite/mailnews/addrbook/abCardViewOverlay.js
+++ b/suite/mailnews/addrbook/abCardViewOverlay.js
@@ -161,16 +161,18 @@ function OnLoadCardView()
 	cvData.cvWorkAddress	= doc.getElementById("cvWorkAddress");
 	cvData.cvWorkAddress2	= doc.getElementById("cvWorkAddress2");
 	cvData.cvWorkCityStZip	= doc.getElementById("cvWorkCityStZip");
 	cvData.cvWorkCountry	= doc.getElementById("cvWorkCountry");
   cvData.cvbWorkMapItBox  = doc.getElementById("cvbWorkMapItBox");
   cvData.cvWorkMapIt = doc.getElementById("cvWorkMapIt");
 	cvData.cvWorkWebPageBox = doc.getElementById("cvWorkWebPageBox");
 	cvData.cvWorkWebPage	= doc.getElementById("cvWorkWebPage");
+  cvData.cvbPhoto = doc.getElementById("cvbPhoto");
+  cvData.cvPhoto  = doc.getElementById("cvPhoto");
 }
 	
 // XXX todo
 // some similar code (in spirit) already exists, see OnLoadEditList()
 // perhaps we could combine and put in abCommon.js?
 function GetAddressesFromURI(uri)
 {
   var addresses = "";
@@ -203,16 +205,20 @@ function DisplayCardViewPane(realCard)
   var card = { getProperty : function (prop) {
                  return realCard.getProperty(prop, "");
                },
                primaryEmail : realCard.primaryEmail,
                displayName : realCard.displayName,
                isMailList : realCard.isMailList,
                mailListURI : realCard.mailListURI
   };
+
+  // Contact photo
+  cvData.cvPhoto.setAttribute("src", getPhotoURI(card.getProperty("PhotoName")));
+
   var titleString;
   if (generatedName == "")
     titleString = card.primaryEmail;  // if no generatedName, use email
   else
     titleString = generatedName;
 
   // set fields in card view pane
   if (card.isMailList)
--- a/suite/mailnews/addrbook/abCommon.js
+++ b/suite/mailnews/addrbook/abCommon.js
@@ -50,16 +50,18 @@ var gPrefs = Components.classes["@mozill
 var gHeaderParser = Components.classes["@mozilla.org/messenger/headerparser;1"].getService(Components.interfaces.nsIMsgHeaderParser);
 
 const kDefaultSortColumn = "GeneratedName";
 const kDefaultAscending = "ascending";
 const kDefaultDescending = "descending";
 const kLdapUrlPrefix = "moz-abldapdirectory://";
 const kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab";
 const kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab";
+// The default image for contacts
+var defaultPhotoURI = "chrome://messenger/skin/addressbook/icons/contact-generic.png";
 
 // Controller object for Dir Pane
 var DirPaneController =
 {
   supportsCommand: function(command)
   {
     switch (command) {
       case "cmd_selectAll":
@@ -624,8 +626,171 @@ function GetSelectedDirectory()
     return abList.value;
   else {
     if (dirTree.currentIndex < 0)
       return null;
     var selected = dirTree.builderView.getResourceAtIndex(dirTree.currentIndex)
     return selected.Value;
   }
 }
+
+/**
+ * Returns an nsIFile of the directory in which contact photos are stored.
+ * This will create the directory if it does not yet exist.
+ */
+function getPhotosDir() {
+  var file = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+  // Get the Photos directory
+  file.append("Photos");
+  if (!file.exists() || !file.isDirectory())
+    file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777);
+  return file;
+}
+
+/**
+ * Returns a URI specifying the location of a photo based on its name.
+ * If the name is blank, or if the photo with that name is not in the Photos
+ * directory then the default photo URI is returned.
+ *
+ * @param aPhotoName The name of the photo from the Photos folder, if any.
+ *
+ * @return A URI pointing to a photo.
+ */
+function getPhotoURI(aPhotoName) {
+  if (!aPhotoName)
+    return defaultPhotoURI;
+  var file = getPhotosDir();
+  try {
+    file.append(aPhotoName);
+  }
+  catch (e) {
+    return defaultPhotoURI;
+  }
+  if (!file.exists())
+    return defaultPhotoURI;
+  return Components.classes["@mozilla.org/network/io-service;1"]
+                   .getService(Components.interfaces.nsIIOService)
+                   .newFileURI(file).spec;
+}
+
+/**
+ * Saves the given input stream to a file.
+ *
+ * @param aIStream The input stream to save.
+ * @param aFile    The file to which the stream is saved.
+ */
+function saveStreamToFile(aIStream, aFile) {
+  if (!(aIStream instanceof Components.interfaces.nsIInputStream))
+    throw "Invalid stream passed to saveStreamToFile";
+  if (!(aFile instanceof Components.interfaces.nsIFile))
+    throw "Invalid file passed to saveStreamToFile";
+  // Write the input stream to the file
+  var fstream = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]
+                          .createInstance(Components.interfaces.nsIFileOutputStream);
+  var buffer  = Components.classes["@mozilla.org/network/buffered-output-stream;1"]
+                          .createInstance(Components.interfaces.nsIBufferedOutputStream);
+  fstream.init(aFile, 0x04 | 0x08 | 0x20, 0600, 0); // write, create, truncate
+  buffer.init(fstream, 8192);
+
+  buffer.writeFrom(aIStream, aIStream.available());
+
+  // Close the output streams
+  if (buffer instanceof Components.interfaces.nsISafeOutputStream)
+      buffer.finish();
+  else
+      buffer.close();
+  if (fstream instanceof Components.interfaces.nsISafeOutputStream)
+      fstream.finish();
+  else
+      fstream.close();
+  // Close the input stream
+  aIStream.close();
+  return aFile;
+}
+
+/**
+ * Copies the photo at the given URI in a folder named "Photos" in the current
+ * profile folder.
+ * The filename is randomly generated and is unique.
+ * The URI is used to obtain a channel which is then opened synchronously and
+ * this stream is written to the new file to store an offline, local copy of the
+ * photo.
+ *
+ * @param aUri The URI of the photo.
+ *
+ * @return An nsIFile representation of the photo.
+ */
+function savePhoto(aUri) {
+  if (!aUri)
+    return false;
+
+  // Get the photos directory and check that it exists
+  var file = getPhotosDir();
+
+  // Create a channel from the URI and open it as an input stream
+  var ios = Components.classes["@mozilla.org/network/io-service;1"]
+                      .getService(Components.interfaces.nsIIOService);
+  var channel = ios.newChannelFromURI(ios.newURI(aUri, null, null));
+  var istream = channel.open();
+
+  // Get the photo file
+  file.append(makePhotoFilename(file.path, findPhotoExt(aUri, channel)));
+
+  return saveStreamToFile(istream, file);
+}
+
+/**
+ * Finds the file extension of the photo identified by the URI, if possible.
+ * This function can be overridden (with a copy of the original) for URIs that
+ * do not identify the extension or when the Content-Type response header is
+ * either not set or isn't 'image/png', 'image/jpeg', or 'image/gif'.
+ * The original function can be called if the URI does not match.
+ *
+ * @param aUri The URI of the photo.
+ * @param aChannel The opened channel for the URI.
+ *
+ * @return The extension of the file, if any, including the period.
+ */
+function findPhotoExt(aUri, aChannel) {
+  if (aChannel) {
+    try {
+      aChannel.QueryInterface(Components.interfaces.nsIHttpChannel);
+      var header = aChannel.getResponseHeader("Content-Type");
+      var type   = header ? header.split(";")[0] : "";
+      switch (type.toLowerCase()) {
+        case "image/png":
+          return ".png";
+        case "image/jpeg":
+          return ".jpg";
+        case "image/gif":
+          return ".gif";
+      }
+    } catch (e) {}
+  }
+
+  var index = aUri ? aUri.lastIndexOf(".") : -1;
+  if (index == -1)
+    return "";
+   return aUri.substring(index);
+}
+
+/**
+ * Generates a unique filename to be used for a local copy of a contact's photo.
+ *
+ * @param aPath      The path to the folder in which the photo will be saved.
+ * @param aExtension The file extension of the photo.
+ *
+ * @return A unique filename in the given path.
+ */
+function makePhotoFilename(aPath, aExtension) {
+  var filename, newFile;
+  // Find a random filename for the photo that doesn't exist yet
+  do {
+    filename = new String(Math.random()).replace("0.", "") + aExtension;
+    newFile = Components.classes["@mozilla.org/file/local;1"]
+                        .createInstance(Components.interfaces.nsILocalFile);
+    newFile.initWithPath(aPath);
+    newFile.append(filename);
+  } while (newFile.exists());
+  return filename;
+}
--- a/suite/themes/classic/jar.mn
+++ b/suite/themes/classic/jar.mn
@@ -317,16 +317,18 @@ classic.jar:
   skin/classic/messenger/addressbook/selectAddressesDialog.css          (messenger/addressbook/selectAddressesDialog.css)
   skin/classic/messenger/addressbook/sidebarPanel.css                   (messenger/addressbook/sidebarPanel.css)
   skin/classic/messenger/addressbook/icons/addrbook.gif                 (messenger/addressbook/icons/addrbook.gif)
   skin/classic/messenger/addressbook/icons/ablist.gif                   (messenger/addressbook/icons/ablist.gif)
   skin/classic/messenger/addressbook/icons/remote-addrbook.gif          (messenger/addressbook/icons/remote-addrbook.gif)
   skin/classic/messenger/addressbook/icons/secure-remote-addrbook.gif   (messenger/addressbook/icons/secure-remote-addrbook.gif)
   skin/classic/messenger/addressbook/icons/remote-addrbook-error.gif    (messenger/addressbook/icons/remote-addrbook-error.gif)
   skin/classic/messenger/addressbook/icons/abcard.gif                   (messenger/addressbook/icons/abcard.gif)
+  skin/classic/messenger/addressbook/icons/contact-generic.png          (messenger/addressbook/icons/contact-generic.png)
+  skin/classic/messenger/addressbook/icons/contact-generic-tiny.png     (messenger/addressbook/icons/contact-generic-tiny.png)
   skin/classic/messenger/smime/msgReadSecurityInfo.css                  (messenger/smime/msgReadSecurityInfo.css)
   skin/classic/messenger/smime/msgCompSecurityInfo.css                  (messenger/smime/msgCompSecurityInfo.css)
   skin/classic/messenger/smime/msgReadSMIMEOverlay.css                  (messenger/smime/msgReadSMIMEOverlay.css)
   skin/classic/messenger/smime/certFetchingStatus.css                   (messenger/smime/certFetchingStatus.css)
   skin/classic/messenger/smime/msgHdrViewSMIMEOverlay.css               (messenger/smime/msgHdrViewSMIMEOverlay.css)
   skin/classic/messenger/smime/icons/sbSignOk.gif                       (messenger/smime/icons/sbSignOk.gif)
   skin/classic/messenger/smime/icons/sbSignUnknown.gif                  (messenger/smime/icons/sbSignUnknown.gif)
   skin/classic/messenger/smime/icons/sbSignNotOk.gif                    (messenger/smime/icons/sbSignNotOk.gif)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..95e6a2e4afd27bbeea9619c13a73634f5334cfa2
GIT binary patch
literal 674
zc$@*E0$u%yP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXV>
z0|yjrB8a#E00JUOL_t(I%Z-yyPZU8A#edy1vjZgTu(%+L1fxU_EN2rFqlWL`cknaH
z3Aig9_@hjSpj-%XcV%~H|4dbRm}OywgYVEucUQgY_qxg*9v^>Ps8+YkEaw5NSdB;F
zy(e6#R=47~^r@S52z)R~P*fFgh~$_Vs*0$fs-wN|;`th(ndY5tn@00DQ5fNz2aGgL
zao*8tHYvq1mC77(Tw*ZjBch1ptSqk*g2OwHcLCl9c<(toJ7atMGwpVVZnr}m$E>fv
zVxwLsNfLs<VP@dK7fKDY;%PTGf7sjGrP*vEk~1|m1;EM4H`3D-=iH<ZV3=B=&x9<?
z==CyyiQp)Tn3<U&41!y4Zwc;9Glk2`OFEs-gGwSotJR|4@58+-Zw6|Hh@hI2=flwt
z+`&1Asxlb#G3;LnU}~rc!{LynT8-J+S<I|33NSOGD56@eQYw|k=l3KCq6NW|`3j4R
zi+JzH$pGbYnP)4@h{%LI4|ftog{;@3Uf*DPdU~ScmX@AUsmxDQ2MB?p%OnR^=q)D+
zK@g0Cf*_!rB!po&?!*ZKGsAgDJ{)rX<D8503$iR52jzKAdYWRUtgfz7E|*bN+%3T{
z&pA3g;^)OL8jS|sZWrg=SgV<FeSJ-<)#CJfN^QBu+S*IN5sC==U%s-lvx~!xCeS@P
zeS^V(cDv2h)fET(2fTUxmM{!M-oN|6W_@!sNdE^_B}wM^3#b}6f8+L94*&oF07*qo
IM6N<$f~S)$R{#J2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6555eceb0f3767123127a763da7883095aba3ad2
GIT binary patch
literal 5846
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
--- a/suite/themes/modern/jar.mn
+++ b/suite/themes/modern/jar.mn
@@ -348,16 +348,18 @@ modern.jar:
   skin/modern/messenger/addressbook/icons/list.gif                 (messenger/addressbook/icons/list.gif)
   skin/modern/messenger/addressbook/icons/mast-ab.gif              (messenger/addressbook/icons/mast-ab.gif)
   skin/modern/messenger/addressbook/icons/myaddrbk.gif             (messenger/addressbook/icons/myaddrbk.gif)
   skin/modern/messenger/addressbook/icons/person.gif               (messenger/addressbook/icons/person.gif)
   skin/modern/messenger/addressbook/icons/im.gif                   (messenger/addressbook/icons/im.gif)
   skin/modern/messenger/addressbook/icons/im-act.gif               (messenger/addressbook/icons/im-act.gif)
   skin/modern/messenger/addressbook/icons/im-hov.gif               (messenger/addressbook/icons/im-hov.gif)
   skin/modern/messenger/addressbook/icons/im-dis.gif               (messenger/addressbook/icons/im-dis.gif)
+  skin/modern/messenger/addressbook/icons/contact-generic.png      (messenger/addressbook/icons/contact-generic.png)
+  skin/modern/messenger/addressbook/icons/contact-generic-tiny.png (messenger/addressbook/icons/contact-generic-tiny.png)
   skin/modern/messenger/icons/acct-compose.gif                     (messenger/icons/acct-compose.gif)
   skin/modern/messenger/icons/acct-filters.gif                     (messenger/icons/acct-filters.gif)
   skin/modern/messenger/icons/acct-newaccount.gif                  (messenger/icons/acct-newaccount.gif)
   skin/modern/messenger/icons/acct-prefs.gif                       (messenger/icons/acct-prefs.gif)
   skin/modern/messenger/icons/acct-read.gif                        (messenger/icons/acct-read.gif)
   skin/modern/messenger/icons/acct-search.gif                      (messenger/icons/acct-search.gif)
   skin/modern/messenger/icons/acct-subscribe.gif                   (messenger/icons/acct-subscribe.gif)
   skin/modern/messenger/icons/attach.gif                           (messenger/icons/attach.gif)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..95e6a2e4afd27bbeea9619c13a73634f5334cfa2
GIT binary patch
literal 674
zc$@*E0$u%yP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXV>
z0|yjrB8a#E00JUOL_t(I%Z-yyPZU8A#edy1vjZgTu(%+L1fxU_EN2rFqlWL`cknaH
z3Aig9_@hjSpj-%XcV%~H|4dbRm}OywgYVEucUQgY_qxg*9v^>Ps8+YkEaw5NSdB;F
zy(e6#R=47~^r@S52z)R~P*fFgh~$_Vs*0$fs-wN|;`th(ndY5tn@00DQ5fNz2aGgL
zao*8tHYvq1mC77(Tw*ZjBch1ptSqk*g2OwHcLCl9c<(toJ7atMGwpVVZnr}m$E>fv
zVxwLsNfLs<VP@dK7fKDY;%PTGf7sjGrP*vEk~1|m1;EM4H`3D-=iH<ZV3=B=&x9<?
z==CyyiQp)Tn3<U&41!y4Zwc;9Glk2`OFEs-gGwSotJR|4@58+-Zw6|Hh@hI2=flwt
z+`&1Asxlb#G3;LnU}~rc!{LynT8-J+S<I|33NSOGD56@eQYw|k=l3KCq6NW|`3j4R
zi+JzH$pGbYnP)4@h{%LI4|ftog{;@3Uf*DPdU~ScmX@AUsmxDQ2MB?p%OnR^=q)D+
zK@g0Cf*_!rB!po&?!*ZKGsAgDJ{)rX<D8503$iR52jzKAdYWRUtgfz7E|*bN+%3T{
z&pA3g;^)OL8jS|sZWrg=SgV<FeSJ-<)#CJfN^QBu+S*IN5sC==U%s-lvx~!xCeS@P
zeS^V(cDv2h)fET(2fTUxmM{!M-oN|6W_@!sNdE^_B}wM^3#b}6f8+L94*&oF07*qo
IM6N<$f~S)$R{#J2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6555eceb0f3767123127a763da7883095aba3ad2
GIT binary patch
literal 5846
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