1 /* ***** BEGIN LICENSE BLOCK *****
  2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3  *
  4  * The contents of this file are subject to the Mozilla Public License Version
  5  * 1.1 (the "License"); you may not use this file except in compliance with
  6  * the License. You may obtain a copy of the License at
  7  * http://www.mozilla.org/MPL/
  8  *
  9  * Software distributed under the License is distributed on an "AS IS" basis,
 10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11  * for the specific language governing rights and limitations under the
 12  * License.
 13  *
 14  * The Original Code is mozilla.org code.
 15  *
 16  * The Initial Developer of the Original Code is
 17  * Netscape Communications Corporation.
 18  * Portions created by the Initial Developer are Copyright (C) 1998
 19  * the Initial Developer. All Rights Reserved.
 20  *
 21  * Contributor(s):
 22  *   Seth Spitzer <sspitzer@netscape.com>
 23  *   Mark Banner <mark@standard8.demon.co.uk>
 24  *   Josh Geenen <gcontactsync@pirules.org>
 25  *
 26  * Alternatively, the contents of this file may be used under the terms of
 27  * either of the GNU General Public License Version 2 or later (the "GPL"),
 28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 29  * in which case the provisions of the GPL or the LGPL are applicable instead
 30  * of those above. If you wish to allow use of your version of this file only
 31  * under the terms of either the GPL or the LGPL, and not to allow others to
 32  * use your version of this file under the terms of the MPL, indicate your
 33  * decision by deleting the provisions above and replace them with the notice
 34  * and other provisions required by the GPL or the LGPL. If you do not delete
 35  * the provisions above, a recipient may use your version of this file under
 36  * the terms of any one of the MPL, the GPL or the LGPL.
 37  *
 38  * ***** END LICENSE BLOCK ***** */
 39 
 40 if (!com) var com = {}; // A generic wrapper variable
 41 // A wrapper for all GCS functions and variables
 42 if (!com.gContactSync) com.gContactSync = {};
 43 
 44 /**
 45  * Meant to override the code in the onDrop method of abDirTreeObserver (an
 46  * instance of nsIXULTreeBuilderObserver), which is called when the user drops
 47  * one or more cards.  The code is a modified version of onDrop found in
 48  * mailnews/addrbook/resources/abDragDrop.js
 49  * It's purpose is to copy over extra attributes that this extension adds to
 50  * address book cards and to work around bugs.
 51  *
 52  * @param row          The row
 53  * @param orientation  {int} An integer specifying on/after/before the given row
 54  */
 55 com.gContactSync.myOnDrop = function gCS_myOnDrop(row, orientation) {
 56   var dragSession = dragService.getCurrentSession();
 57   if (!dragSession)
 58     return;
 59   // get the attributes added by this extension
 60   var attributes    = com.gContactSync.ContactConverter.getExtraSyncAttributes(false, true),
 61       attributesLen = attributes.length,
 62       trans         = Components.classes["@mozilla.org/widget/transferable;1"]
 63                                 .createInstance(Components.interfaces.nsITransferable);
 64   trans.addDataFlavor("moz/abcard");
 65 
 66   var targetResource = dirTree.builderView.getResourceAtIndex(row),
 67   // get the source and target directory information
 68       targetURI      = targetResource.Value,
 69       srcURI         = GetSelectedDirectory(),
 70       toDirectory    = GetDirectoryFromURI(targetURI),
 71       srcDirectory   = GetDirectoryFromURI(srcURI),
 72       ab             = new com.gContactSync.GAddressBook(toDirectory);
 73   // iterate through each dropped item from the session
 74   for (var i = 0, dropItems = dragSession.numDropItems; i < dropItems; i++) {
 75     dragSession.getData(trans, i);
 76     var dataObj       = {},
 77         flavor        = {},
 78         len           = {},
 79         needToRefresh = false;
 80     try {
 81       trans.getAnyTransferData(flavor, dataObj, len);
 82       dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
 83     }
 84     catch (ex) { continue; }
 85     var transData = dataObj.data.split("\n"),
 86         rows      = transData[0].split(","),
 87         numrows   = rows.length,
 88         result,
 89     // needToCopyCard is used for whether or not we should be creating
 90     // copies of the cards in a mailing list in a different address book
 91     // - it's not for if we are moving or not.
 92         needToCopyCard = true;
 93     if (srcURI.length > targetURI.length) {
 94       result = srcURI.split(targetURI);
 95       if (result[0] !== srcURI) {
 96         // src directory is a mailing list on target directory, no need to copy card
 97         needToCopyCard = false;
 98         // workaround for a mailnews bug, get the childCards enumerator to
 99         // update the mIsMailingList variable in the directory
100         // https://www.mozdev.org/bugs/show_bug.cgi?id=19733
101         toDirectory.childCards || toDirectory.childNodes;
102       }
103     }
104     else {
105       result = targetURI.split(srcURI);
106       if (result[0] !== targetURI) {
107         // target directory is a mailing list on src directory, no need to copy card
108         needToCopyCard = false;
109         // workaround for a mailnews bug, get the childCards enumerator to
110         // update the mIsMailingList variable in the directory
111         // https://www.mozdev.org/bugs/show_bug.cgi?id=19733
112         toDirectory.childCards || toDirectory.childNodes;
113         needToRefresh = true;
114       }
115     }
116     // if we still think we have to copy the card,
117     // check if srcURI and targetURI are mailing lists on same directory
118     // if so, we don't have to copy the card
119     if (needToCopyCard) {
120       var targetParentURI = GetParentDirectoryFromMailingListURI(targetURI);
121       if (targetParentURI && (targetParentURI ===
122                               GetParentDirectoryFromMailingListURI(srcURI))) {
123         needToCopyCard = false;
124       }
125     }
126     // Only move if we are not transferring to a mail list
127     var actionIsMoving = (dragSession.dragAction & dragSession.DRAGDROP_ACTION_MOVE) &&
128                          !toDirectory.isMailList;
129     // get the cards first
130     var cards = [];
131     for (var j = 0; j < numrows; j++) {
132       cards.push(gAbView.getCardFromRow(rows[j]));
133     }
134     // iterate through each card and copy/move it
135     for (j = 0; j < numrows; j++) {
136       var card = cards[j];
137       if (!card)
138         continue;
139       if (card.isMailList) {
140         // This check ensures we haven't slipped through by mistake
141         if (needToCopyCard && actionIsMoving)
142           toDirectory.addMailList(GetDirectoryFromURI(card.mailListURI));
143       }
144       else {
145         var values = [];
146         // put in a try/catch block in case the card can't be QI'd to nsIAbMDBCard
147         var isMDBCard = false;
148         // only copy over the extra attributes if this is before Bug 413260 and
149         // if the card is an MDB Card (not an LDAP or different card)
150         try {
151           if (!card.getProperty) {
152             // MDB card was removed in 413260, but after that patch it is no
153             // longer necessary to copy the extra attributes manually
154             // the card may also be an LDAP card in which case it won't have
155             // extra attributes to copy
156             card.QueryInterface(Components.interfaces.nsIAbMDBCard);
157             isMDBCard = true;
158             for (var k = 0; k < attributesLen; k++) {
159               values[k] = card.getStringAttribute(attributes[k]);
160             }
161           }
162         }
163         catch (e) {
164           // ignore the error if the card wasn't an MDB card, otherwise log it
165           if (isMDBCard)
166             com.gContactSync.LOGGER.LOG_WARNING("Error while getting extra card attributes.", e);
167         }
168         // delete the card if the user chose to move it (rather than copy it)
169         if (actionIsMoving)
170           com.gContactSync.deleteCard(srcDirectory, card);
171         if (toDirectory.isMailList) {
172           needToRefresh = true;
173           var contact   = new com.gContactSync.TBContact(card);
174           if (!contact.getValue("PrimaryEmail")) {
175             com.gContactSync.LOGGER.VERBOSE_LOG("Forcing dummy email");
176             // force a dummy e-mail address
177             var dummyEmail = com.gContactSync.makeDummyEmail(contact, true);
178             contact.setValue("PrimaryEmail", dummyEmail, false);
179           }
180         }
181         var newCard = toDirectory.addCard(card);
182         if (isMDBCard) { // copy the attributes if this is an MDB card
183           try {
184             newCard.QueryInterface(Components.interfaces.nsIAbMDBCard);
185             if (isMDBCard) {
186               for (var k = 0; k < attributesLen; k++) {
187                 var value = values[k] ? values[k] : "";
188                 newCard.setStringAttribute(attributes[k], value);
189               }
190             }
191           } catch (e) { com.gContactSync.LOGGER.LOG_WARNING("Error while copying card", e); }
192         }
193         try {
194           var now = (new Date()).getTime()/1000,
195               newContact = new com.gContactSync.TBContact(newCard, ab);
196           // now set the new card's last modified date and update it
197           newContact.setValue("LastModifiedDate", now);
198           newContact.update();
199         } catch (e) { com.gContactSync.LOGGER.LOG_WARNING('copy card error: ' + e); }
200       }
201     }
202     var cardsTransferredText;
203 
204     // set the status bar text
205     if (actionIsMoving)
206       cardsTransferredText = 
207         numrows == 1 ? gAddressBookBundle.getString("cardMoved")
208                      : gAddressBookBundle.getFormattedString("cardsMoved",
209                                                               [numrows]);
210     else
211       cardsTransferredText =
212         numrows == 1 ? gAddressBookBundle.getString("cardCopied")
213                      : gAddressBookBundle.getFormattedString("cardsCopied",
214                                                               [numrows]);
215     // update the address book view so it doesn't show the card twice
216     SetAbView(GetSelectedDirectory(), false);
217     // select the first card, if any
218     if (gAbView && gAbView.getCardFromRow(0))
219       SelectFirstCard();
220     // set the status text
221     document.getElementById("statusText").label = cardsTransferredText;
222   }
223 }
224 /**
225  * Deletes the given card from the given directory.
226  * @param aDirectory {nsIAbDirectory} The directory from which the card is
227  *                                    deleted.
228  * @param aCard      {nsIAbCard}      The card that is deleted from the
229  *                                    directory.
230  */
231 com.gContactSync.deleteCard = function gCS_deleteCard(aDirectory, aCard) {
232   if (!aCard || !aDirectory)
233     return;
234   var arr;
235   // Thunderbird 2 and 3 differ in the type of array that must be passed to
236   // the deleteCards method
237   if (aDirectory.modifyCard) { // TB 3
238     arr = Components.classes["@mozilla.org/array;1"]
239                     .createInstance(Components.interfaces.nsIMutableArray);
240     arr.appendElement(aCard, false);
241   }
242   else { // TB 2
243     arr = Components.classes["@mozilla.org/supports-array;1"]
244                     .createInstance(Components.interfaces.nsISupportsArray);
245     arr.AppendElement(aCard, false);
246   }
247   aDirectory.deleteCards(arr);
248 };
249