Back to index

lightning-sunbird  0.9+nobinonly
nsAddressBook.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Seth Spitzer <sspitzer@netscape.com>
00024  *   Pierre Phaneuf <pp@ludusdesign.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nscore.h"
00041 #include "nsIAddressBook.h"
00042 #include "nsAddressBook.h"
00043 #include "nsAbBaseCID.h"
00044 #include "nsDirPrefs.h"
00045 #include "nsIAddrBookSession.h"
00046 #include "nsIAbLDIFService.h"
00047 #include "nsAddrDatabase.h"
00048 #include "nsIOutputStream.h"
00049 #include "nsNetUtil.h"
00050 #include "msgCore.h"
00051 #include "nsIImportService.h"
00052 #include "nsIStringBundle.h"
00053 
00054 #include "plstr.h"
00055 #include "prmem.h"
00056 #include "prprf.h" 
00057 
00058 #include "nsCOMPtr.h"
00059 #include "nsIRDFCompositeDataSource.h"
00060 #include "nsIRDFResource.h"
00061 #include "nsRDFResource.h"
00062 #include "nsIRDFService.h"
00063 #include "nsIServiceManager.h"
00064 #include "nsIDOMWindowInternal.h"
00065 #include "nsIContentViewer.h"
00066 #include "nsIDocShell.h"
00067 #include "nsXPIDLString.h"
00068 #include "nsReadableUtils.h"
00069 #include "nsICategoryManager.h"
00070 #include "nsIAbUpgrader.h"
00071 #include "nsIFilePicker.h"
00072 #include "nsIPrefService.h"
00073 #include "nsIPrefBranch.h"
00074 #include "nsIPrefLocalizedString.h"
00075 #include "nsIAbCard.h"
00076 #include "nsIAbMDBCard.h"
00077 #include "plbase64.h"
00078 #include "nsIWindowWatcher.h"
00079 
00080 #include "nsEscape.h"
00081 #include "nsVCard.h"
00082 #include "nsVCardObj.h"
00083 #include "nsISupportsPrimitives.h"
00084 #include "nsIInterfaceRequestor.h"
00085 #include "nsIInterfaceRequestorUtils.h"
00086 #include "nsIDocShell.h"
00087 #include "nsAutoPtr.h"
00088 #include "nsIMsgVCardService.h"
00089 
00090 #include "nsCRT.h"
00091 
00092 #ifdef MOZ_XUL_APP
00093 #include "nsICommandLine.h"
00094 #endif
00095 
00096 // according to RFC 2849
00097 // SEP = (CR LF / LF)
00098 // so we LF for unix and beos (since that is the natural line ending for
00099 // those platforms, see nsCRT.h)
00100 //
00101 // otherwise we use CR LF (windows linebreak)
00102 #if defined(XP_UNIX) || defined(XP_BEOS)
00103 #define LDIF_LINEBREAK          "\012"
00104 #define LDIF_LINEBREAK_LEN     1
00105 #else
00106 #define LDIF_LINEBREAK          "\015\012"
00107 #define LDIF_LINEBREAK_LEN     2
00108 #endif
00109 
00110 // our schema is not fixed yet, but we still want some sort of objectclass
00111 // for now, use obsolete in the class name, hinting that this will change
00112 // see bugs bug #116692 and #118454
00113 #define MOZ_AB_OBJECTCLASS "mozillaAbPersonAlpha"
00114 
00115 const ExportAttributesTableStruct EXPORT_ATTRIBUTES_TABLE[EXPORT_ATTRIBUTES_TABLE_COUNT] = { 
00116   {kFirstNameColumn, "givenName", 2100},
00117   {kLastNameColumn, "sn", 2101},
00118   {kDisplayNameColumn, "cn", 2102},
00119   {kNicknameColumn, MOZ_AB_LDIF_PREFIX "Nickname", 2103},
00120   {kPriEmailColumn, "mail", 2104},
00121   {k2ndEmailColumn, MOZ_AB_LDIF_PREFIX "SecondEmail", 2105},
00122   {kDefaultEmailColumn, MOZ_AB_LDIF_PREFIX "DefaultEmail"},
00123   {kCardTypeColumn, MOZ_AB_LDIF_PREFIX "CardType"},
00124   {kAimScreenNameColumn, "nsAIMid"},
00125   {kPreferMailFormatColumn, MOZ_AB_LDIF_PREFIX "UseHtmlMail"},
00126   {kLastModifiedDateColumn, "modifytimestamp"},
00127   {kWorkPhoneColumn, "telephoneNumber", 2106},
00128   {kWorkPhoneTypeColumn, MOZ_AB_LDIF_PREFIX "WorkPhoneType"},
00129   {kHomePhoneColumn, "homePhone", 2107},
00130   {kHomePhoneTypeColumn, MOZ_AB_LDIF_PREFIX "HomePhoneType"},
00131   {kFaxColumn, "fax", 2108},
00132   {kFaxTypeColumn, MOZ_AB_LDIF_PREFIX "FaxNumberType"},
00133   {kPagerColumn, "pager", 2109},
00134   {kPagerTypeColumn, MOZ_AB_LDIF_PREFIX "PagerNumberType"},
00135   {kCellularColumn, "mobile", 2110},
00136   {kCellularTypeColumn, MOZ_AB_LDIF_PREFIX "CellularNumberType"},
00137   {kHomeAddressColumn, "homeStreet", 2111},
00138   {kHomeAddress2Column, MOZ_AB_LDIF_PREFIX "HomeStreet2", 2112},
00139   {kHomeCityColumn, MOZ_AB_LDIF_PREFIX "HomeLocalityName", 2113},
00140   {kHomeStateColumn, MOZ_AB_LDIF_PREFIX "HomeState", 2114},
00141   {kHomeZipCodeColumn, MOZ_AB_LDIF_PREFIX "HomePostalCode", 2115},
00142   {kHomeCountryColumn, MOZ_AB_LDIF_PREFIX "HomeCountryName", 2116},
00143   {kWorkAddressColumn, "street", 2117},
00144   {kWorkAddress2Column, MOZ_AB_LDIF_PREFIX "WorkStreet2", 2118},
00145   {kWorkCityColumn, "l", 2119},
00146   {kWorkStateColumn, "st", 2120},
00147   {kWorkZipCodeColumn, "postalCode", 2121}, 
00148   {kWorkCountryColumn, "c", 2122}, 
00149   {kJobTitleColumn, "title", 2123},
00150   {kDepartmentColumn, "department", 2124},
00151   {kCompanyColumn, "company", 2125},
00152   {kWebPage1Column, MOZ_AB_LDIF_PREFIX "WorkUrl", 2126},
00153   {kWebPage2Column, MOZ_AB_LDIF_PREFIX "HomeUrl", 2127},
00154   {kBirthYearColumn, nsnull, 2128}, // unused for now
00155   {kBirthMonthColumn, nsnull, 2129}, // unused for now
00156   {kBirthDayColumn, nsnull, 2130}, // unused for now
00157   {kCustom1Column, MOZ_AB_LDIF_PREFIX "Custom1", 2131},
00158   {kCustom2Column, MOZ_AB_LDIF_PREFIX "Custom2", 2132},
00159   {kCustom3Column, MOZ_AB_LDIF_PREFIX "Custom3", 2133},
00160   {kCustom4Column, MOZ_AB_LDIF_PREFIX "Custom4", 2134},
00161   {kNotesColumn, "description", 2135},
00162   {kAnniversaryYearColumn, MOZ_AB_LDIF_PREFIX "AnniversaryYear"},
00163   {kAnniversaryMonthColumn, MOZ_AB_LDIF_PREFIX "AnniversaryMonth"},
00164   {kAnniversaryDayColumn, MOZ_AB_LDIF_PREFIX "AnniversaryDay"},
00165   {kSpouseNameColumn, MOZ_AB_LDIF_PREFIX "SpouseName"},
00166   {kFamilyNameColumn, MOZ_AB_LDIF_PREFIX "FamilyName"},
00167   {kDefaultAddressColumn, MOZ_AB_LDIF_PREFIX "DefaultAddress"},
00168   {kCategoryColumn, MOZ_AB_LDIF_PREFIX "Category"},
00169 };
00170 
00171 //
00172 // nsAddressBook
00173 //
00174 nsAddressBook::nsAddressBook()
00175 {
00176 }
00177 
00178 nsAddressBook::~nsAddressBook()
00179 {
00180 }
00181 
00182 NS_IMPL_THREADSAFE_ADDREF(nsAddressBook)
00183 NS_IMPL_THREADSAFE_RELEASE(nsAddressBook)
00184 NS_IMPL_QUERY_INTERFACE4(nsAddressBook,
00185                          nsIAddressBook,
00186                          ICOMMANDLINEHANDLER,
00187                          nsIContentHandler,
00188                          nsIStreamLoaderObserver)
00189 
00190 //
00191 // nsIAddressBook
00192 //
00193 
00194 NS_IMETHODIMP nsAddressBook::NewAddressBook(nsIAbDirectoryProperties *aProperties)
00195 {
00196   NS_ENSURE_ARG_POINTER(aProperties);
00197 
00198   nsresult rv;
00199 
00200   nsCOMPtr<nsIRDFService> rdfService = do_GetService (NS_RDF_CONTRACTID "/rdf-service;1", &rv);
00201   NS_ENSURE_SUCCESS(rv, rv);
00202 
00203   nsCOMPtr<nsIRDFResource> parentResource;
00204   rv = rdfService->GetResource(NS_LITERAL_CSTRING(kAllDirectoryRoot),
00205                                getter_AddRefs(parentResource));
00206   NS_ENSURE_SUCCESS(rv, rv);
00207 
00208   nsCOMPtr<nsIAbDirectory> parentDir = do_QueryInterface(parentResource, &rv);
00209   NS_ENSURE_SUCCESS(rv, rv);
00210 
00211   rv = parentDir->CreateNewDirectory(aProperties);
00212   return rv;
00213 }
00214 
00215 NS_IMETHODIMP nsAddressBook::ModifyAddressBook
00216 (nsIRDFDataSource* aDS, nsIAbDirectory *aParentDir, nsIAbDirectory *aDirectory, nsIAbDirectoryProperties *aProperties)
00217 {
00218   NS_ENSURE_ARG_POINTER(aDS);
00219   NS_ENSURE_ARG_POINTER(aParentDir);
00220   NS_ENSURE_ARG_POINTER(aDirectory);
00221   NS_ENSURE_ARG_POINTER(aProperties);
00222 
00223   nsresult rv;
00224   nsCOMPtr<nsISupportsArray> parentArray (do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv));
00225   NS_ENSURE_SUCCESS(rv, rv);
00226   nsCOMPtr<nsISupportsArray> resourceElement (do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv));
00227   NS_ENSURE_SUCCESS(rv, rv);
00228   nsCOMPtr<nsISupportsArray> resourceArray (do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv));
00229   NS_ENSURE_SUCCESS(rv, rv);
00230 
00231   parentArray->AppendElement(aParentDir);
00232 
00233   nsCOMPtr<nsIRDFResource> dirSource(do_QueryInterface(aDirectory, &rv));
00234   NS_ENSURE_SUCCESS(rv, rv);
00235 
00236   resourceElement->AppendElement(dirSource);
00237   resourceElement->AppendElement(aProperties);
00238   resourceArray->AppendElement(resourceElement);
00239 
00240   return DoCommand(aDS, NS_LITERAL_CSTRING(NC_RDF_MODIFY), parentArray, resourceArray);
00241 }
00242 
00243 NS_IMETHODIMP nsAddressBook::DeleteAddressBooks
00244 (nsIRDFDataSource* aDS, nsISupportsArray *aParentDir, nsISupportsArray *aResourceArray)
00245 {
00246   NS_ENSURE_ARG_POINTER(aDS);
00247   NS_ENSURE_ARG_POINTER(aParentDir);
00248   NS_ENSURE_ARG_POINTER(aResourceArray);
00249   
00250   return DoCommand(aDS, NS_LITERAL_CSTRING(NC_RDF_DELETE), aParentDir, aResourceArray);
00251 }
00252 
00253 nsresult nsAddressBook::DoCommand(nsIRDFDataSource* db,
00254                                   const nsACString& command,
00255                                   nsISupportsArray *srcArray,
00256                                   nsISupportsArray *argumentArray)
00257 {
00258   nsresult rv = NS_OK;
00259 
00260   nsCOMPtr<nsIRDFService> rdfService = do_GetService (NS_RDF_CONTRACTID "/rdf-service;1", &rv);
00261   NS_ENSURE_SUCCESS(rv, rv);
00262 
00263   nsCOMPtr<nsIRDFResource> commandResource;
00264   rv = rdfService->GetResource(command, getter_AddRefs(commandResource));
00265   if(NS_SUCCEEDED(rv))
00266   {
00267     rv = db->DoCommand(srcArray, commandResource, argumentArray);
00268   }
00269 
00270   return rv;
00271 }
00272 
00273 NS_IMETHODIMP nsAddressBook::GetAbDatabaseFromURI(const char *aURI, nsIAddrDatabase **aDB)
00274 {
00275   NS_ENSURE_ARG_POINTER(aURI);
00276   NS_ENSURE_ARG_POINTER(aDB);
00277   
00278   nsresult rv;
00279   
00280   nsCOMPtr<nsIAddrBookSession> abSession = 
00281     do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv);
00282   NS_ENSURE_SUCCESS(rv,rv);
00283 
00284   nsCOMPtr<nsILocalFile> dbPath;
00285   rv = abSession->GetUserProfileDirectory(getter_AddRefs(dbPath));
00286   NS_ENSURE_SUCCESS(rv,rv);
00287 
00288  /* directory URIs are of the form
00289   * moz-abmdbdirectory://foo
00290   * mailing list URIs are of the form
00291   * moz-abmdbdirectory://foo/bar
00292   *
00293   * if we are passed a mailing list URI, we want the db for the parent.
00294   */
00295   if (strlen(aURI) < kMDBDirectoryRootLen)
00296     return NS_ERROR_UNEXPECTED;
00297 
00298   nsCAutoString file(aURI + kMDBDirectoryRootLen);
00299   PRInt32 pos = file.Find("/");
00300   if (pos != kNotFound)
00301     file.Truncate(pos);
00302   rv = dbPath->AppendNative(file);
00303   NS_ENSURE_SUCCESS(rv,rv);
00304   
00305   nsCOMPtr<nsIAddrDatabase> addrDBFactory = 
00306     do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
00307   NS_ENSURE_SUCCESS(rv,rv);
00308 
00309   /* Don't create otherwise we end up re-opening a deleted address book */
00310   /* bug 66410 */
00311   rv = addrDBFactory->Open(dbPath, PR_FALSE /* no create */, PR_TRUE, aDB);
00312   
00313   return rv;
00314 }
00315 
00316 nsresult nsAddressBook::GetAbDatabaseFromFile(char* pDbFile, nsIAddrDatabase **db)
00317 {
00318     nsresult rv = NS_OK;
00319     nsCOMPtr<nsIAddrDatabase> database;
00320     if (pDbFile)
00321     {
00322         nsCOMPtr<nsILocalFile> dbPath;
00323 
00324         nsCOMPtr<nsIAddrBookSession> abSession = 
00325                  do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv); 
00326         if(NS_SUCCEEDED(rv))
00327         {
00328           rv = abSession->GetUserProfileDirectory(getter_AddRefs(dbPath));
00329           NS_ENSURE_SUCCESS(rv, rv);
00330         }
00331         
00332         nsCAutoString file(pDbFile);
00333         rv = dbPath->AppendNative(file);
00334         NS_ENSURE_SUCCESS(rv,rv);
00335 
00336         nsCOMPtr<nsIAddrDatabase> addrDBFactory = 
00337                  do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
00338         if (NS_SUCCEEDED(rv) && addrDBFactory)
00339             rv = addrDBFactory->Open(dbPath, PR_TRUE, PR_TRUE, getter_AddRefs(database));
00340 
00341         if (NS_SUCCEEDED(rv) && database)
00342         {
00343             NS_IF_ADDREF(*db = database);
00344         }
00345         else
00346             rv = NS_ERROR_NULL_POINTER;
00347 
00348     }
00349     return NS_OK;
00350 }
00351 
00352 NS_IMETHODIMP nsAddressBook::MailListNameExists(const PRUnichar *name, PRBool *exist)
00353 {
00354   *exist = PR_FALSE;
00355   nsVoidArray* pDirectories = DIR_GetDirectories();
00356   if (pDirectories)
00357   {
00358     PRInt32 count = pDirectories->Count();
00359     /* check: only show personal address book for now */
00360     /* not showing 4.x address book unitl we have the converting done */
00361     PRInt32 i;
00362     for (i = 0; i < count; i++)
00363     {
00364       DIR_Server *server = (DIR_Server *)pDirectories->ElementAt(i);
00365       if (server->dirType == PABDirectory)
00366       {
00367         /* check: this is a 4.x file, remove when conversion is done */
00368         PRUint32 fileNameLen = strlen(server->fileName);
00369         if ((fileNameLen > kABFileName_PreviousSuffixLen) && 
00370           strcmp(server->fileName + fileNameLen - kABFileName_PreviousSuffixLen, kABFileName_PreviousSuffix) == 0)
00371           continue;
00372         
00373         nsCOMPtr<nsIAddrDatabase> database;
00374         nsresult rv = GetAbDatabaseFromFile(server->fileName, getter_AddRefs(database));                
00375         if (NS_SUCCEEDED(rv) && database)
00376         {
00377           database->FindMailListbyUnicodeName(name, exist);
00378           if (*exist)
00379             return NS_OK;
00380         }
00381       }
00382     }
00383   }
00384   return NS_OK;
00385 }
00386 
00387 nsresult nsAddressBook::Migrate4xAb(nsIFileSpec *aFileSpec, PRBool aMigrating, PRBool aStoreLocAsHome)
00388 {
00389   NS_ENSURE_ARG_POINTER(aFileSpec);
00390 
00391   nsresult rv = NS_OK;
00392 
00393   // We are migrating 4.x profile
00394   /* Get database file name */
00395   char *dbUri = nsnull;
00396   char *leafName = nsnull;
00397   if (aFileSpec) {
00398     aFileSpec->GetLeafName(&leafName);
00399 
00400     PRInt32 i = 0;
00401     while (leafName[i] != '\0')
00402     {
00403       if (leafName[i] == '.')
00404       {
00405         leafName[i] = '\0';
00406         break;
00407       }
00408       else
00409        i++;
00410     }
00411     if (leafName)
00412       dbUri = PR_smprintf("%s%s.mab", kMDBDirectoryRoot, leafName);
00413   }
00414 
00415   nsCOMPtr<nsILocalFile> dbPath;
00416   nsCOMPtr<nsIAddrDatabase> newDatabase;
00417   nsCAutoString fileName(leafName);
00418   fileName.Append(NS_LITERAL_CSTRING(".mab"));
00419 
00420   nsCOMPtr<nsIAddrBookSession> abSession = 
00421            do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv); 
00422   if(NS_SUCCEEDED(rv))
00423     rv = abSession->GetUserProfileDirectory(getter_AddRefs(dbPath));
00424     
00425   /* create address book database  */
00426   if(NS_SUCCEEDED(rv))
00427   {
00428     dbPath->AppendNative(fileName);
00429     NS_ENSURE_SUCCESS(rv, rv);
00430 
00431     nsCOMPtr<nsIAddrDatabase> addrDBFactory = 
00432              do_GetService(NS_ADDRDATABASE_CONTRACTID, &rv);
00433     if (NS_SUCCEEDED(rv) && addrDBFactory)
00434       rv = addrDBFactory->Open(dbPath, PR_TRUE, PR_TRUE, getter_AddRefs(newDatabase));
00435   }
00436   NS_ENSURE_SUCCESS(rv, rv);
00437 
00438   nsCOMPtr<nsIRDFService> rdfService = do_GetService (NS_RDF_CONTRACTID "/rdf-service;1", &rv);
00439   NS_ENSURE_SUCCESS(rv, rv);
00440 
00441   nsCOMPtr<nsIRDFResource> parentResource;
00442   rv = rdfService->GetResource(NS_LITERAL_CSTRING(kAllDirectoryRoot),
00443                                getter_AddRefs(parentResource));
00444   nsCOMPtr<nsIAbDirectory> parentDir = do_QueryInterface(parentResource);
00445   if (!parentDir)
00446     return NS_ERROR_NULL_POINTER;
00447 
00448   // Get Pretty name from prefs.
00449   nsCOMPtr<nsIPrefBranch> pPref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00450   if (NS_FAILED(rv)) 
00451     return nsnull;
00452 
00453   nsXPIDLString dirName;
00454   nsCOMPtr<nsIPrefLocalizedString> locString;
00455   nsCAutoString prefName;
00456   if (strcmp(fileName.get(), kPersonalAddressbook) == 0)
00457     prefName.AssignLiteral("ldap_2.servers.pab.description");
00458   else
00459     prefName = NS_LITERAL_CSTRING("ldap_2.servers.") + nsDependentCString(leafName) + NS_LITERAL_CSTRING(".description");
00460 
00461   rv = pPref->GetComplexValue(prefName.get(), NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(locString));
00462 
00463   if (NS_SUCCEEDED(rv))
00464     rv = locString->ToString(getter_Copies(dirName));
00465 
00466   // If a name is found then use it, otherwise use the filename as last resort.
00467   if (NS_FAILED(rv) || dirName.IsEmpty())
00468     dirName.AssignASCII(leafName);
00469   parentDir->CreateDirectoryByURI(dirName, dbUri, aMigrating);
00470         
00471   nsCOMPtr<nsIAbLDIFService> ldifService = do_GetService(NS_ABLDIFSERVICE_CONTRACTID, &rv);
00472   if (NS_SUCCEEDED(rv))
00473     rv = ldifService->ImportLDIFFile(newDatabase, aFileSpec, aStoreLocAsHome, nsnull);
00474 
00475   if (leafName)
00476     nsCRT::free(leafName);
00477   if (dbUri)
00478     nsCRT::free(dbUri);
00479 
00480   return rv;
00481 }
00482 
00483 NS_IMETHODIMP nsAddressBook::ConvertNA2toLDIF(nsIFileSpec *srcFileSpec, nsIFileSpec *dstFileSpec)
00484 {
00485   nsresult rv = NS_OK;
00486   if (!srcFileSpec || !dstFileSpec) return NS_ERROR_NULL_POINTER;
00487   
00488   nsCOMPtr <nsIAbUpgrader> abUpgrader = do_GetService(NS_AB4xUPGRADER_CONTRACTID, &rv);
00489   NS_ENSURE_SUCCESS(rv, rv);
00490   if (!abUpgrader) return NS_ERROR_FAILURE;
00491 
00492   rv = abUpgrader->StartUpgrade4xAddrBook(srcFileSpec, dstFileSpec);
00493   if (NS_SUCCEEDED(rv)) {
00494     PRBool done = PR_FALSE;
00495     
00496     do {
00497       rv = abUpgrader->ContinueExport(&done);
00498       // XXX todo 
00499       // put this in the msg status
00500       printf("converting na2 to ldif...\n");
00501     } while (NS_SUCCEEDED(rv) && !done);
00502   }
00503   return rv;  
00504 }
00505 
00506 #define CSV_DELIM ","
00507 #define CSV_DELIM_LEN 1
00508 #define TAB_DELIM "\t"
00509 #define TAB_DELIM_LEN 1
00510 #define LDIF_DELIM (nsnull)
00511 #define LDIF_DELIM_LEN 0
00512 
00513 #define CSV_FILE_EXTENSION ".csv"
00514 #define TAB_FILE_EXTENSION ".tab"
00515 #define TXT_FILE_EXTENSION ".txt"
00516 #define LDIF_FILE_EXTENSION ".ldi"
00517 #define LDIF_FILE_EXTENSION2 ".ldif"
00518 
00519 enum ADDRESSBOOK_EXPORT_FILE_TYPE 
00520 {
00521  LDIF_EXPORT_TYPE =  0,
00522  CSV_EXPORT_TYPE = 1,
00523  TAB_EXPORT_TYPE = 2
00524 };
00525 
00526 NS_IMETHODIMP nsAddressBook::ExportAddressBook(nsIDOMWindow *aParentWin, nsIAbDirectory *aDirectory)
00527 {
00528   NS_ENSURE_ARG_POINTER(aParentWin);
00529 
00530   nsresult rv;
00531   nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1", &rv);
00532   NS_ENSURE_SUCCESS(rv, rv);
00533 
00534   nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00535   NS_ENSURE_SUCCESS(rv, rv);
00536   nsCOMPtr<nsIStringBundle> bundle;
00537   rv = bundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", getter_AddRefs(bundle));
00538   NS_ENSURE_SUCCESS(rv, rv);
00539  
00540   nsXPIDLString title;
00541   rv = bundle->GetStringFromName(NS_LITERAL_STRING("ExportAddressBookTitle").get(), getter_Copies(title));
00542   NS_ENSURE_SUCCESS(rv, rv);
00543 
00544   rv = filePicker->Init(aParentWin, title, nsIFilePicker::modeSave);
00545   NS_ENSURE_SUCCESS(rv, rv);
00546  
00547   nsXPIDLString filterString;
00548   rv = bundle->GetStringFromName(NS_LITERAL_STRING("LDIFFiles").get(), getter_Copies(filterString));
00549   NS_ENSURE_SUCCESS(rv, rv);
00550 
00551   rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.ldi; *.ldif"));
00552   NS_ENSURE_SUCCESS(rv, rv);
00553 
00554   rv = bundle->GetStringFromName(NS_LITERAL_STRING("CSVFiles").get(), getter_Copies(filterString));
00555   NS_ENSURE_SUCCESS(rv, rv);
00556 
00557   rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.csv"));
00558   NS_ENSURE_SUCCESS(rv, rv);
00559 
00560   rv = bundle->GetStringFromName(NS_LITERAL_STRING("TABFiles").get(), getter_Copies(filterString));
00561   NS_ENSURE_SUCCESS(rv, rv);
00562 
00563   rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.tab; *.txt"));
00564   NS_ENSURE_SUCCESS(rv, rv);
00565 
00566   PRInt16 dialogResult;
00567   filePicker->Show(&dialogResult);
00568 
00569   if (dialogResult == nsIFilePicker::returnCancel)
00570     return rv;
00571 
00572   nsCOMPtr<nsILocalFile> localFile;
00573   rv = filePicker->GetFile(getter_AddRefs(localFile));
00574   NS_ENSURE_SUCCESS(rv, rv);
00575   
00576   if (dialogResult == nsIFilePicker::returnReplace) {
00577     // be extra safe and only delete when the file is really a file
00578     PRBool isFile;
00579     rv = localFile->IsFile(&isFile);
00580     if (NS_SUCCEEDED(rv) && isFile) {
00581       rv = localFile->Remove(PR_FALSE /* recursive delete */);
00582       NS_ENSURE_SUCCESS(rv, rv);
00583     }
00584   }
00585 
00586   // The type of export is determined by the drop-down in
00587   // the file picker dialog.
00588   PRInt32 exportType;
00589   rv = filePicker->GetFilterIndex(&exportType);
00590   NS_ENSURE_SUCCESS(rv,rv);
00591 
00592   nsAutoString fileName;
00593   rv = localFile->GetLeafName(fileName);
00594   NS_ENSURE_SUCCESS(rv, rv);
00595     
00596   switch ( exportType )
00597   {
00598     default:
00599     case LDIF_EXPORT_TYPE: // ldif
00600       // If filename does not have the correct ext, add one.
00601       if ((fileName.RFind(LDIF_FILE_EXTENSION, PR_TRUE, -1, sizeof(LDIF_FILE_EXTENSION)-1) == kNotFound) &&
00602        (fileName.RFind(LDIF_FILE_EXTENSION2, PR_TRUE, -1, sizeof(LDIF_FILE_EXTENSION2)-1) == kNotFound)) {
00603 
00604        // Add the extenstion and build a new localFile.
00605        fileName.AppendLiteral(LDIF_FILE_EXTENSION2);
00606        localFile->SetLeafName(fileName);
00607     }
00608       rv = ExportDirectoryToLDIF(aDirectory, localFile);
00609       break;
00610 
00611     case CSV_EXPORT_TYPE: // csv
00612       // If filename does not have the correct ext, add one.
00613       if (fileName.RFind(CSV_FILE_EXTENSION, PR_TRUE, -1, sizeof(CSV_FILE_EXTENSION)-1) == kNotFound) {
00614 
00615        // Add the extenstion and build a new localFile.
00616        fileName.AppendLiteral(CSV_FILE_EXTENSION);
00617        localFile->SetLeafName(fileName);
00618     }
00619       rv = ExportDirectoryToDelimitedText(aDirectory, CSV_DELIM, CSV_DELIM_LEN, localFile);
00620       break;
00621 
00622     case TAB_EXPORT_TYPE: // tab & text
00623       // If filename does not have the correct ext, add one.
00624       if ( (fileName.RFind(TXT_FILE_EXTENSION, PR_TRUE, -1, sizeof(TXT_FILE_EXTENSION)-1) == kNotFound) &&
00625           (fileName.RFind(TAB_FILE_EXTENSION, PR_TRUE, -1, sizeof(TAB_FILE_EXTENSION)-1) == kNotFound) ) {
00626 
00627        // Add the extenstion and build a new localFile.
00628        fileName.AppendLiteral(TXT_FILE_EXTENSION);
00629        localFile->SetLeafName(fileName);
00630   }
00631       rv = ExportDirectoryToDelimitedText(aDirectory, TAB_DELIM, TAB_DELIM_LEN, localFile);
00632       break;
00633   };
00634  
00635   return rv;
00636 }
00637 
00638 nsresult
00639 nsAddressBook::ExportDirectoryToDelimitedText(nsIAbDirectory *aDirectory, const char *aDelim, PRUint32 aDelimLen, nsILocalFile *aLocalFile)
00640 {
00641   nsCOMPtr <nsIEnumerator> cardsEnumerator;
00642   nsCOMPtr <nsIAbCard> card;
00643 
00644   nsresult rv;
00645 
00646   nsCOMPtr <nsIImportService> importService = do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv);
00647   NS_ENSURE_SUCCESS(rv,rv);
00648 
00649   nsCOMPtr <nsIOutputStream> outputStream;
00650   rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
00651                                    aLocalFile,
00652                                    PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
00653                                    0664);
00654 
00655   // the desired file may be read only
00656   if (NS_FAILED(rv))
00657     return rv;
00658 
00659   PRUint32 i;
00660   PRUint32 writeCount;
00661   PRUint32 length;
00662 
00663   nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00664   NS_ENSURE_SUCCESS(rv, rv);
00665 
00666   nsCOMPtr<nsIStringBundle> bundle;
00667   rv = bundleService->CreateBundle("chrome://messenger/locale/importMsgs.properties", getter_AddRefs(bundle));
00668   NS_ENSURE_SUCCESS(rv, rv);
00669 
00670   nsXPIDLCString revisedName;
00671   nsXPIDLString columnName;
00672 
00673   for (i = 0; i < EXPORT_ATTRIBUTES_TABLE_COUNT; i++) {
00674     if (EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID != 0) {
00675 
00676       // We don't need to truncate the string here as getter_Copies will
00677       // do that for us.
00678       if (NS_FAILED(bundle->GetStringFromID(EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID, getter_Copies(columnName))))
00679         columnName.AppendInt(EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID);
00680 
00681       importService->SystemStringFromUnicode(columnName.get(), revisedName);
00682 
00683       rv = outputStream->Write(revisedName.get(),
00684                                revisedName.Length(),
00685                                &writeCount);
00686       NS_ENSURE_SUCCESS(rv,rv);
00687 
00688       if (revisedName.Length() != writeCount)
00689         return NS_ERROR_FAILURE;
00690       
00691       if (i < EXPORT_ATTRIBUTES_TABLE_COUNT - 1) {
00692         rv = outputStream->Write(aDelim, aDelimLen, &writeCount);
00693         NS_ENSURE_SUCCESS(rv,rv);
00694 
00695         if (aDelimLen != writeCount)
00696           return NS_ERROR_FAILURE;
00697       }
00698     }
00699   }
00700   rv = outputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &writeCount);
00701   NS_ENSURE_SUCCESS(rv,rv);
00702   if (MSG_LINEBREAK_LEN != writeCount)
00703     return NS_ERROR_FAILURE;
00704 
00705   rv = aDirectory->GetChildCards(getter_AddRefs(cardsEnumerator));
00706   if (NS_SUCCEEDED(rv) && cardsEnumerator) {
00707     nsCOMPtr<nsISupports> item;
00708     for (rv = cardsEnumerator->First(); NS_SUCCEEDED(rv); rv = cardsEnumerator->Next()) {
00709       rv = cardsEnumerator->CurrentItem(getter_AddRefs(item));
00710       if (NS_SUCCEEDED(rv)) {
00711         nsCOMPtr <nsIAbCard> card = do_QueryInterface(item, &rv);
00712         NS_ENSURE_SUCCESS(rv,rv);
00713          
00714         PRBool isMailList;
00715         rv = card->GetIsMailList(&isMailList);
00716         NS_ENSURE_SUCCESS(rv,rv);
00717        
00718          
00719         if (isMailList) {
00720           // .tab, .txt and .csv aren't able to export mailing lists
00721           // use LDIF for that.
00722         }
00723         else {
00724           nsXPIDLString value;          
00725           nsCString valueCStr;
00726 
00727           for (i = 0; i < EXPORT_ATTRIBUTES_TABLE_COUNT; i++) {
00728             if (EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID != 0) {
00729               rv = card->GetCardValue(EXPORT_ATTRIBUTES_TABLE[i].abColName, getter_Copies(value));
00730               NS_ENSURE_SUCCESS(rv,rv);
00731 
00732               // If a string contains at least one comma, tab or double quote then
00733               // we need to quote the entire string. Also if double quote is part
00734               // of the string we need to quote the double quote(s) as well.  
00735               nsAutoString newValue(value);
00736               PRBool needsQuotes = PR_FALSE;
00737               if(newValue.FindChar('"') != kNotFound)
00738               {
00739                 needsQuotes = PR_TRUE;
00740                 newValue.ReplaceSubstring(NS_LITERAL_STRING("\"").get(), NS_LITERAL_STRING("\"\"").get());
00741               }
00742               if (!needsQuotes && (newValue.FindChar(',') != kNotFound || newValue.FindChar('\x09') != kNotFound))
00743                 needsQuotes = PR_TRUE;
00744 
00745               if (needsQuotes)
00746               {
00747                 newValue.Insert(NS_LITERAL_STRING("\""), 0);
00748                 newValue.AppendLiteral("\"");
00749               }
00750 
00751               // For notes, make sure CR/LF is converted to spaces 
00752               // to avoid creating multiple lines for a single card.
00753               //
00754               // the import code expects .txt, .tab, .csv files to
00755               // have non-ASCII data in the system charset
00756               //
00757               // note, this means if you machine is set to US-ASCII
00758               // but you have cards with Japanese characters
00759               // you will lose data when exporting.
00760               //
00761               // the solution is to export / import as LDIF.
00762               // non-ASCII data is treated as base64 encoded UTF-8 in LDIF
00763               if (!strcmp(EXPORT_ATTRIBUTES_TABLE[i].abColName, kNotesColumn))
00764               {
00765                 if (!newValue.IsEmpty())
00766                 {
00767                   newValue.ReplaceChar(nsCRT::CR, ' ');
00768                   newValue.ReplaceChar(nsCRT::LF, ' ');
00769                 }
00770               }
00771               rv = importService->SystemStringFromUnicode(newValue.get(), valueCStr);
00772 
00773               if (NS_FAILED(rv)) {
00774                 NS_ASSERTION(0, "failed to convert string to system charset.  use LDIF");
00775                 valueCStr = "?";
00776               }
00777               
00778               length = valueCStr.Length();
00779               if (length) {
00780                 rv = outputStream->Write(valueCStr.get(), length, &writeCount);
00781                 NS_ENSURE_SUCCESS(rv,rv);
00782                 if (length != writeCount)
00783                   return NS_ERROR_FAILURE;
00784               }
00785               valueCStr = "";
00786             }
00787             else {
00788               // something we don't support for the current export
00789               // for example, .tab doesn't export preferred html format
00790               continue; // go to next field
00791             }
00792 
00793             if (i < EXPORT_ATTRIBUTES_TABLE_COUNT - 1) {
00794               rv = outputStream->Write(aDelim, aDelimLen, &writeCount);
00795               NS_ENSURE_SUCCESS(rv,rv);
00796               if (aDelimLen != writeCount)
00797                 return NS_ERROR_FAILURE;
00798             }
00799           }
00800 
00801           // write out the linebreak that separates the cards
00802           rv = outputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &writeCount);
00803           NS_ENSURE_SUCCESS(rv,rv);
00804           if (MSG_LINEBREAK_LEN != writeCount)
00805             return NS_ERROR_FAILURE;
00806         }
00807       }
00808     }
00809   }
00810 
00811   rv = outputStream->Flush();
00812   NS_ENSURE_SUCCESS(rv,rv);
00813 
00814   rv = outputStream->Close();
00815   NS_ENSURE_SUCCESS(rv,rv);
00816   return NS_OK;
00817 }
00818 
00819 nsresult
00820 nsAddressBook::ExportDirectoryToLDIF(nsIAbDirectory *aDirectory, nsILocalFile *aLocalFile)
00821 {
00822   nsCOMPtr <nsIEnumerator> cardsEnumerator;
00823   nsCOMPtr <nsIAbCard> card;
00824 
00825   nsresult rv;
00826 
00827   nsCOMPtr <nsIOutputStream> outputStream;
00828   rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
00829                                    aLocalFile,
00830                                    PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
00831                                    0664);
00832 
00833   // the desired file may be read only
00834   if (NS_FAILED(rv))
00835     return rv;
00836 
00837   PRUint32 i;
00838   PRUint32 writeCount;
00839   PRUint32 length;
00840    
00841   rv = aDirectory->GetChildCards(getter_AddRefs(cardsEnumerator));
00842   if (NS_SUCCEEDED(rv) && cardsEnumerator) {
00843     nsCOMPtr<nsISupports> item;
00844     for (rv = cardsEnumerator->First(); NS_SUCCEEDED(rv); rv = cardsEnumerator->Next()) {
00845       rv = cardsEnumerator->CurrentItem(getter_AddRefs(item));
00846       if (NS_SUCCEEDED(rv)) {
00847         nsCOMPtr <nsIAbCard> card = do_QueryInterface(item, &rv);
00848         NS_ENSURE_SUCCESS(rv,rv);
00849          
00850         PRBool isMailList;
00851         rv = card->GetIsMailList(&isMailList);
00852         NS_ENSURE_SUCCESS(rv,rv);
00853        
00854         if (isMailList) {
00855           nsCString mailListCStr;
00856 
00857           rv = AppendLDIFForMailList(card, mailListCStr);
00858           NS_ENSURE_SUCCESS(rv,rv);
00859 
00860           length = mailListCStr.Length();
00861           rv = outputStream->Write(mailListCStr.get(), length, &writeCount);
00862           NS_ENSURE_SUCCESS(rv,rv);
00863           if (length != writeCount)
00864             return NS_ERROR_FAILURE;
00865         }
00866         else {
00867           nsXPIDLString value;          
00868           nsCString valueCStr;
00869     
00870           rv = AppendBasicLDIFForCard(card, valueCStr);
00871           NS_ENSURE_SUCCESS(rv,rv);
00872           
00873           length = valueCStr.Length();
00874           rv = outputStream->Write(valueCStr.get(), length, &writeCount);
00875           NS_ENSURE_SUCCESS(rv,rv);
00876           if (length != writeCount)
00877             return NS_ERROR_FAILURE;
00878           
00879           valueCStr.Truncate();
00880 
00881           for (i = 0; i < EXPORT_ATTRIBUTES_TABLE_COUNT; i++) {
00882             if (EXPORT_ATTRIBUTES_TABLE[i].ldapPropertyName) {
00883               rv = card->GetCardValue(EXPORT_ATTRIBUTES_TABLE[i].abColName, getter_Copies(value));
00884               NS_ENSURE_SUCCESS(rv,rv);
00885  
00886               if (!PL_strcmp(EXPORT_ATTRIBUTES_TABLE[i].abColName, kPreferMailFormatColumn)) {
00887                 if (value.Equals(NS_LITERAL_STRING("html").get()))
00888                   value.AssignLiteral("true");
00889                 else if (value.Equals(NS_LITERAL_STRING("plaintext").get()))
00890                   value.AssignLiteral("false");
00891                 else
00892                   value.Truncate(); // unknown.
00893               }
00894 
00895               if (!value.IsEmpty()) {
00896                 rv = AppendProperty(EXPORT_ATTRIBUTES_TABLE[i].ldapPropertyName, value.get(), valueCStr);
00897                 NS_ENSURE_SUCCESS(rv,rv);
00898                 
00899                 valueCStr += LDIF_LINEBREAK;
00900               }
00901               else
00902                 valueCStr.Truncate();
00903               
00904               length = valueCStr.Length();
00905               if (length) {
00906                 rv = outputStream->Write(valueCStr.get(), length, &writeCount);
00907                 NS_ENSURE_SUCCESS(rv,rv);
00908                 if (length != writeCount)
00909                   return NS_ERROR_FAILURE;
00910               }
00911               valueCStr.Truncate();
00912             }
00913             else {
00914               // something we don't support yet
00915               // ldif doesn't export mutliple addresses
00916             }
00917           }
00918         
00919           // write out the linebreak that separates the cards
00920           rv = outputStream->Write(LDIF_LINEBREAK, LDIF_LINEBREAK_LEN, &writeCount);
00921           NS_ENSURE_SUCCESS(rv,rv);
00922           if (LDIF_LINEBREAK_LEN != writeCount)
00923             return NS_ERROR_FAILURE;
00924         }
00925       }
00926     }
00927   }
00928 
00929   rv = outputStream->Flush();
00930   NS_ENSURE_SUCCESS(rv,rv);
00931 
00932   rv = outputStream->Close();
00933   NS_ENSURE_SUCCESS(rv,rv);
00934   return NS_OK;
00935 }
00936 
00937 nsresult nsAddressBook::AppendLDIFForMailList(nsIAbCard *aCard, nsACString &aResult)
00938 {
00939   nsresult rv;
00940   nsXPIDLString attrValue;
00941   
00942   rv = aCard->GetCardValue(kDisplayNameColumn, getter_Copies(attrValue));
00943   NS_ENSURE_SUCCESS(rv,rv);
00944 
00945   rv = AppendDNForCard("dn", aCard, aResult);
00946   NS_ENSURE_SUCCESS(rv,rv);
00947 
00948   aResult += LDIF_LINEBREAK \
00949              "objectclass: top" LDIF_LINEBREAK \
00950              "objectclass: groupOfNames" LDIF_LINEBREAK;
00951 
00952   rv = AppendProperty("cn", attrValue.get(), aResult);
00953   NS_ENSURE_SUCCESS(rv,rv);
00954   aResult += LDIF_LINEBREAK;
00955 
00956   rv = aCard->GetCardValue(kNicknameColumn, getter_Copies(attrValue));
00957   NS_ENSURE_SUCCESS(rv,rv);
00958 
00959   if (!attrValue.IsEmpty()) {
00960     rv = AppendProperty("mozillaNickname", attrValue.get(), aResult);
00961     NS_ENSURE_SUCCESS(rv,rv);
00962     aResult += LDIF_LINEBREAK;
00963   }
00964 
00965   rv = aCard->GetCardValue(kNotesColumn, getter_Copies(attrValue));
00966   NS_ENSURE_SUCCESS(rv,rv);
00967 
00968   if (!attrValue.IsEmpty()) {
00969     rv = AppendProperty("description", attrValue.get(), aResult);
00970     NS_ENSURE_SUCCESS(rv,rv);
00971     aResult += LDIF_LINEBREAK;
00972   }
00973 
00974   nsCOMPtr<nsIRDFService> rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
00975   NS_ENSURE_SUCCESS(rv,rv);
00976   
00977   nsXPIDLCString mailListURI;
00978   rv = aCard->GetMailListURI(getter_Copies(mailListURI));
00979   NS_ENSURE_SUCCESS(rv,rv);
00980   
00981   nsCOMPtr <nsIRDFResource> resource;
00982   rv = rdfService->GetResource(mailListURI, getter_AddRefs(resource));
00983   NS_ENSURE_SUCCESS(rv,rv);
00984     
00985   nsCOMPtr <nsIAbDirectory> mailList = do_QueryInterface(resource, &rv);
00986   NS_ENSURE_SUCCESS(rv,rv);
00987     
00988   nsCOMPtr<nsISupportsArray> addresses;
00989   rv = mailList->GetAddressLists(getter_AddRefs(addresses));
00990   if (addresses) {
00991     PRUint32 total = 0;
00992     addresses->Count(&total);
00993     if (total) {
00994       PRUint32 i;
00995       for (i = 0; i < total; i++) {
00996         nsCOMPtr <nsIAbCard> listCard = do_QueryElementAt(addresses, i, &rv);
00997         NS_ENSURE_SUCCESS(rv,rv);
00998 
00999         rv = AppendDNForCard("member", listCard, aResult);
01000         NS_ENSURE_SUCCESS(rv,rv);
01001 
01002         aResult += LDIF_LINEBREAK;
01003       }
01004     }
01005   }
01006   
01007   aResult += LDIF_LINEBREAK;
01008   return NS_OK;
01009 }
01010 
01011 nsresult nsAddressBook::AppendDNForCard(const char *aProperty, nsIAbCard *aCard, nsACString &aResult)
01012 {
01013   nsXPIDLString email;
01014   nsXPIDLString displayName;
01015 
01016   nsresult rv = aCard->GetCardValue(kPriEmailColumn, getter_Copies(email));
01017   NS_ENSURE_SUCCESS(rv,rv);
01018 
01019   rv = aCard->GetCardValue(kDisplayNameColumn, getter_Copies(displayName));
01020   NS_ENSURE_SUCCESS(rv,rv);
01021 
01022   nsString cnStr;
01023 
01024   if (!displayName.IsEmpty()) {
01025     cnStr += NS_LITERAL_STRING("cn=") + displayName;
01026     if (!email.IsEmpty()) {
01027       cnStr.AppendLiteral(",");
01028     }
01029   }
01030 
01031   if (!email.IsEmpty()) {
01032     cnStr += NS_LITERAL_STRING("mail=") + email;
01033   }
01034 
01035   rv = AppendProperty(aProperty, cnStr.get(), aResult);
01036   NS_ENSURE_SUCCESS(rv,rv);
01037   return rv;
01038 }
01039 
01040 nsresult nsAddressBook::AppendBasicLDIFForCard(nsIAbCard *aCard, nsACString &aResult)
01041 {
01042   nsresult rv = AppendDNForCard("dn", aCard, aResult);
01043   NS_ENSURE_SUCCESS(rv,rv);
01044   aResult += LDIF_LINEBREAK \
01045     "objectclass: top" LDIF_LINEBREAK \
01046     "objectclass: person" LDIF_LINEBREAK \
01047     "objectclass: organizationalPerson" LDIF_LINEBREAK \
01048     "objectclass: inetOrgPerson" LDIF_LINEBREAK \
01049     "objectclass: " MOZ_AB_OBJECTCLASS LDIF_LINEBREAK;  
01050 
01051 
01052   return rv;
01053 }
01054 
01055 PRBool nsAddressBook::IsSafeLDIFString(const PRUnichar *aStr)
01056 {
01057   // follow RFC 2849 to determine if something is safe "as is" for LDIF
01058   if (aStr[0] == PRUnichar(' ') ||
01059       aStr[0] == PRUnichar(':') ||
01060       aStr[0] == PRUnichar('<')) 
01061     return PR_FALSE;
01062 
01063   PRUint32 i;
01064   PRUint32 len = nsCRT::strlen(aStr);
01065   for (i=0; i<len; i++) {
01066     // If string contains CR or LF, it is not safe for LDIF
01067     // and MUST be base64 encoded
01068     if ((aStr[i] == PRUnichar('\n')) ||
01069         (aStr[i] == PRUnichar('\r')) ||
01070         (!nsCRT::IsAscii(aStr[i])))
01071       return PR_FALSE;
01072   }
01073   return PR_TRUE;
01074 }
01075 
01076 nsresult nsAddressBook::AppendProperty(const char *aProperty, const PRUnichar *aValue, nsACString &aResult)
01077 {
01078   NS_ENSURE_ARG_POINTER(aValue);
01079 
01080   aResult += aProperty;
01081  
01082   // if the string is not safe "as is", base64 encode it
01083   if (IsSafeLDIFString(aValue)) {
01084     aResult.AppendLiteral(": ");
01085     LossyAppendUTF16toASCII(aValue, aResult);
01086   }
01087   else {
01088     char *base64Str = PL_Base64Encode(NS_ConvertUCS2toUTF8(aValue).get(), 0, nsnull);
01089     if (!base64Str)
01090       return NS_ERROR_OUT_OF_MEMORY;
01091 
01092     aResult += NS_LITERAL_CSTRING(":: ") + nsDependentCString(base64Str);
01093     PR_Free(base64Str);
01094   }
01095 
01096   return NS_OK;
01097 }
01098 
01099 char *getCString(VObject *vObj)
01100 {
01101     if (VALUE_TYPE(vObj) == VCVT_USTRINGZ)
01102         return fakeCString(vObjectUStringZValue(vObj));
01103     if (VALUE_TYPE(vObj) == VCVT_STRINGZ)
01104         return PL_strdup(vObjectStringZValue(vObj));
01105     return NULL;
01106 }
01107 
01108 static void convertNameValue(VObject *vObj, nsIAbCard *aCard)
01109 {
01110   const char *cardColName = NULL;
01111 
01112   // if the vCard property is not a root property then we need to determine its exact property.
01113   // a good example of this is VCTelephoneProp, this prop has four objects underneath it:
01114   // fax, work and home and cellular.
01115   if (PL_strcasecmp(VCCityProp, vObjectName(vObj)) == 0)
01116       cardColName = kWorkCityColumn;
01117   else if (PL_strcasecmp(VCTelephoneProp, vObjectName(vObj)) == 0)
01118   {
01119       if (isAPropertyOf(vObj, VCFaxProp))
01120           cardColName = kFaxColumn;
01121       else if (isAPropertyOf(vObj, VCWorkProp))
01122           cardColName = kWorkPhoneColumn; 
01123       else if (isAPropertyOf(vObj, VCHomeProp))
01124           cardColName = kHomePhoneColumn;
01125       else if (isAPropertyOf(vObj, VCCellularProp))
01126           cardColName = kCellularColumn;
01127       else if (isAPropertyOf(vObj, VCPagerProp))
01128           cardColName = kPagerColumn;
01129       else
01130           return;
01131   }
01132   else if (PL_strcasecmp(VCEmailAddressProp, vObjectName(vObj)) == 0)
01133       cardColName = kPriEmailColumn;
01134   else if (PL_strcasecmp(VCFamilyNameProp, vObjectName(vObj)) == 0) 
01135       cardColName = kLastNameColumn;
01136   else if (PL_strcasecmp(VCFullNameProp, vObjectName(vObj)) == 0)
01137       cardColName = kDisplayNameColumn;
01138   else if (PL_strcasecmp(VCGivenNameProp, vObjectName(vObj)) == 0)
01139       cardColName = kFirstNameColumn;
01140   else if (PL_strcasecmp(VCOrgNameProp, vObjectName(vObj)) == 0) 
01141       cardColName = kCompanyColumn;
01142   else if (PL_strcasecmp(VCOrgUnitProp, vObjectName(vObj)) == 0)
01143       cardColName = kDepartmentColumn;
01144   else if (PL_strcasecmp(VCPostalCodeProp, vObjectName(vObj)) == 0) 
01145       cardColName = kWorkZipCodeColumn;
01146   else if (PL_strcasecmp(VCRegionProp, vObjectName(vObj)) == 0)
01147       cardColName = kWorkStateColumn;
01148   else if (PL_strcasecmp(VCStreetAddressProp, vObjectName(vObj)) == 0)
01149       cardColName = kWorkAddressColumn;
01150   else if (PL_strcasecmp(VCPostalBoxProp, vObjectName(vObj)) == 0) 
01151       cardColName = kWorkAddress2Column;
01152   else if (PL_strcasecmp(VCCountryNameProp, vObjectName(vObj)) == 0)
01153       cardColName = kWorkCountryColumn;
01154   else if (PL_strcasecmp(VCTitleProp, vObjectName(vObj)) == 0) 
01155       cardColName = kJobTitleColumn;
01156   else if (PL_strcasecmp(VCUseHTML, vObjectName(vObj)) == 0) 
01157       cardColName = kPreferMailFormatColumn;
01158   else if (PL_strcasecmp(VCNoteProp, vObjectName(vObj)) == 0) 
01159       cardColName = kNotesColumn;
01160   else if (PL_strcasecmp(VCURLProp, vObjectName(vObj)) == 0)
01161       cardColName = kWebPage1Column;
01162   else
01163       return;
01164   
01165   if (!VALUE_TYPE(vObj))
01166       return;
01167 
01168   char *cardColValue = getCString(vObj);
01169   aCard->SetCardValue(cardColName, NS_ConvertUTF8toUCS2(cardColValue).get());
01170   PR_FREEIF(cardColValue);
01171   return;
01172 }
01173 
01174 static void convertFromVObject(VObject *vObj, nsIAbCard *aCard)
01175 {
01176     if (vObj)
01177     {
01178         VObjectIterator t;
01179 
01180         convertNameValue(vObj, aCard);
01181         
01182         initPropIterator(&t, vObj);
01183         while (moreIteration(&t))
01184         {
01185             VObject * nextObject = nextVObject(&t);
01186             convertFromVObject(nextObject, aCard);
01187         }
01188     }
01189     return;
01190 }
01191 
01192 NS_IMETHODIMP nsAddressBook::HandleContent(const char * aContentType,
01193                                            nsIInterfaceRequestor * aWindowContext,
01194                                            nsIRequest *request)
01195 {
01196   NS_ENSURE_ARG_POINTER(request);
01197   
01198   nsresult rv = NS_OK;
01199 
01200   // First of all, get the content type and make sure it is a content type we know how to handle!
01201   if (nsCRT::strcasecmp(aContentType, "application/x-addvcard") == 0) {
01202     nsCOMPtr<nsIURI> uri;
01203     nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
01204     if (!aChannel) return NS_ERROR_FAILURE;
01205 
01206     rv = aChannel->GetURI(getter_AddRefs(uri));
01207     if (uri)
01208     {
01209         nsCAutoString path;
01210         rv = uri->GetPath(path);
01211         NS_ENSURE_SUCCESS(rv,rv);
01212 
01213         const char *startOfVCard = strstr(path.get(), "add?vcard=");
01214         if (startOfVCard)
01215         {
01216             char *unescapedData = PL_strdup(startOfVCard + strlen("add?vcard="));
01217             
01218             // XXX todo, explain why we is escaped twice
01219             nsUnescape(unescapedData);
01220             
01221             if (!aWindowContext) 
01222                 return NS_ERROR_FAILURE;
01223 
01224             nsCOMPtr<nsIDOMWindowInternal> parentWindow = do_GetInterface(aWindowContext);
01225             if (!parentWindow) 
01226                 return NS_ERROR_FAILURE;
01227             
01228             nsCOMPtr <nsIAbCard> cardFromVCard;
01229             rv = EscapedVCardToAbCard((const char *)unescapedData, getter_AddRefs(cardFromVCard));
01230             NS_ENSURE_SUCCESS(rv, rv);
01231 
01232             nsCOMPtr<nsISupportsInterfacePointer> ifptr =
01233                 do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
01234             NS_ENSURE_SUCCESS(rv, rv);
01235             
01236             ifptr->SetData(cardFromVCard);
01237             ifptr->SetDataIID(&NS_GET_IID(nsIAbCard));
01238 
01239             nsCOMPtr<nsIDOMWindow> dialogWindow;
01240 
01241             rv = parentWindow->OpenDialog(
01242                 NS_LITERAL_STRING("chrome://messenger/content/addressbook/abNewCardDialog.xul"),
01243                 EmptyString(),
01244                 NS_LITERAL_STRING("chrome,resizable=no,titlebar,modal,centerscreen"),
01245                 ifptr, getter_AddRefs(dialogWindow));
01246             NS_ENSURE_SUCCESS(rv, rv);
01247 
01248             PL_strfree(unescapedData);
01249         }
01250         rv = NS_OK;
01251     }
01252   } 
01253   else if (nsCRT::strcasecmp(aContentType, "text/x-vcard") == 0) {
01254     // create a vcard stream listener that can parse the data stream
01255     // and bring up the appropriate UI
01256 
01257     // (1) cancel the current load operation. We'll restart it
01258     request->Cancel(NS_ERROR_ABORT); 
01259     // get the url we were trying to open
01260     nsCOMPtr<nsIURI> uri;
01261     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
01262     NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
01263 
01264     rv = channel->GetURI(getter_AddRefs(uri));
01265     NS_ENSURE_SUCCESS(rv, rv);
01266 
01267     // create a stream loader to handle the v-card data
01268     nsCOMPtr<nsIStreamLoader> streamLoader;
01269     rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), uri, this, aWindowContext);
01270     NS_ENSURE_SUCCESS(rv, rv);
01271 
01272   }
01273   else // The content-type was not application/x-addvcard...
01274     return NS_ERROR_WONT_HANDLE_CONTENT;
01275 
01276   return rv;
01277 }
01278 
01279 NS_IMETHODIMP nsAddressBook::OnStreamComplete(nsIStreamLoader *aLoader, nsISupports *aContext, nsresult aStatus, PRUint32 datalen, const PRUint8 *data)
01280 {
01281   NS_ENSURE_ARG_POINTER(aContext);
01282   NS_ENSURE_SUCCESS(aStatus, aStatus); // don't process the vcard if we got a status error
01283   nsresult rv = NS_OK;
01284 
01285   // take our vCard string and open up an address book window based on it
01286   nsCOMPtr<nsIMsgVCardService> vCardService = do_GetService(NS_MSGVCARDSERVICE_CONTRACTID);
01287   if (vCardService)
01288   {
01289     nsAutoPtr<VObject> vObj(vCardService->Parse_MIME((const char *)data, datalen));
01290     if (vObj)
01291     {
01292       PRInt32 len = 0;
01293       nsAdoptingCString vCard(vCardService->WriteMemoryVObjects(0, &len, vObj, PR_FALSE));
01294 
01295       nsCOMPtr <nsIAbCard> cardFromVCard;
01296       rv = EscapedVCardToAbCard(vCard.get(), getter_AddRefs(cardFromVCard));
01297       NS_ENSURE_SUCCESS(rv, rv);
01298 
01299       nsCOMPtr<nsIDOMWindowInternal> parentWindow = do_GetInterface(aContext);
01300       NS_ENSURE_TRUE(parentWindow, NS_ERROR_FAILURE);
01301 
01302       nsCOMPtr<nsIDOMWindow> dialogWindow;
01303       rv = parentWindow->OpenDialog(
01304            NS_LITERAL_STRING("chrome://messenger/content/addressbook/abNewCardDialog.xul"),
01305            EmptyString(),
01306            NS_LITERAL_STRING("chrome,resizable=no,titlebar,modal,centerscreen"),
01307            cardFromVCard, getter_AddRefs(dialogWindow));      
01308     }
01309   }
01310 
01311   return rv;
01312 }
01313 
01314 NS_IMETHODIMP nsAddressBook::EscapedVCardToAbCard(const char *aEscapedVCardStr, nsIAbCard **aCard)
01315 {
01316     NS_ENSURE_ARG_POINTER(aEscapedVCardStr);
01317     NS_ENSURE_ARG_POINTER(aCard);
01318     
01319     nsCOMPtr <nsIAbCard> cardFromVCard = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID);
01320     if (!cardFromVCard)
01321         return NS_ERROR_FAILURE;
01322 
01323     // aEscapedVCardStr will be "" the first time, before you have a vCard
01324     if (*aEscapedVCardStr != '\0') {   
01325         char *unescapedData = PL_strdup(aEscapedVCardStr);
01326         if (!unescapedData)
01327             return NS_ERROR_OUT_OF_MEMORY;
01328         
01329         nsUnescape(unescapedData);
01330         VObject *vObj = parse_MIME(unescapedData, strlen(unescapedData));
01331         PL_strfree(unescapedData);
01332         NS_ASSERTION(vObj, "Parse of vCard failed");
01333 
01334         convertFromVObject(vObj, cardFromVCard);
01335         
01336         if (vObj)
01337             cleanVObject(vObj);
01338     }
01339     
01340     NS_IF_ADDREF(*aCard = cardFromVCard);
01341     return NS_OK;
01342 }
01343 
01344 NS_IMETHODIMP nsAddressBook::AbCardToEscapedVCard(nsIAbCard *aCard, char **aEscapedVCardStr)
01345 {
01346     NS_ENSURE_ARG_POINTER(aCard);
01347     NS_ENSURE_ARG_POINTER(aEscapedVCardStr);
01348     
01349     nsresult rv = aCard->ConvertToEscapedVCard(aEscapedVCardStr);
01350     NS_ENSURE_SUCCESS(rv,rv);
01351     return rv;
01352 }
01353 
01354 static nsresult addProperty(char **currentVCard, const char *currentRoot, const char *mask)
01355 {
01356     // keep in mind as we add properties that we want to filter out any begin and end vcard types....because
01357     // we add those automatically...
01358     
01359     const char *beginPhrase = "begin";
01360     
01361     nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
01362     if (currentVCard && prefBranch)
01363     {
01364         PRUint32 childCount;
01365         char **childArray;
01366         nsresult rv = prefBranch->GetChildList(currentRoot, &childCount, &childArray);
01367         NS_ENSURE_SUCCESS(rv, rv);
01368         
01369         for (PRUint32 i = 0; i < childCount; ++i) 
01370         {
01371             char *child = childArray[i];
01372 
01373             if (!strcmp(child, currentRoot))
01374                 continue;
01375                 
01376             // first iterate over the child in case the child has children    
01377             addProperty(currentVCard, child, mask);
01378                
01379             // child length should be greater than the mask....
01380             if (strlen(child) > strlen(mask) + 1)  // + 1 for the '.' in .property
01381             {
01382                 nsXPIDLCString value;
01383                 prefBranch->GetCharPref(child, getter_Copies(value));
01384                 if (mask)
01385                     child += strlen(mask) + 1;  // eat up the "mail.identity.vcard" part...
01386                 // turn all '.' into ';' which is what vcard format uses
01387                 char * marker = strchr(child, '.');
01388                 while (marker)
01389                 {
01390                     *marker = ';';
01391                     marker = strchr(child, '.');
01392                 }
01393                 
01394                 // filter property to make sure it is one we want to add.....
01395                 if ((PL_strncasecmp(child, beginPhrase, strlen(beginPhrase)) != 0) && (PL_strncasecmp(child, VCEndProp, strlen(VCEndProp)) != 0))
01396                 {
01397                     if (!value.IsEmpty()) // only add the value is not an empty string...
01398                         if (*currentVCard)
01399                         {
01400                             char * tempString = *currentVCard;
01401                             *currentVCard = PR_smprintf ("%s%s:%s%s", tempString, child, value.get(), "\n");
01402                             PR_FREEIF(tempString);
01403                         }
01404                         else
01405                             *currentVCard = PR_smprintf ("%s:%s%s", child, value.get(), "\n");
01406                 }
01407             }
01408             else {
01409                 NS_ASSERTION(0, "child length should be greater than the mask");
01410             }
01411         }     
01412         NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(childCount, childArray);
01413     } 
01414     return NS_OK;
01415 }
01416 
01417 NS_IMETHODIMP nsAddressBook::Convert4xVCardPrefs(const char *prefRoot, char **escapedVCardStr)
01418 {
01419     NS_ENSURE_ARG_POINTER(prefRoot);
01420     NS_ENSURE_ARG_POINTER(escapedVCardStr);
01421     
01422     char *vCardString = nsnull;
01423     vCardString = PL_strdup("begin:vcard \n");
01424     
01425     nsresult rv = addProperty(&vCardString, prefRoot, prefRoot);
01426     NS_ENSURE_SUCCESS(rv,rv);
01427     
01428     char *vcard = PR_smprintf("%send:vcard\n", vCardString);
01429     PR_FREEIF(vCardString);
01430     
01431     VObject *vObj = parse_MIME(vcard, strlen(vcard));
01432     PR_FREEIF(vcard);
01433     
01434     nsCOMPtr<nsIAbCard> cardFromVCard = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID);
01435     convertFromVObject(vObj, cardFromVCard);
01436     
01437     if (vObj)
01438         cleanVObject(vObj);
01439     
01440     rv = cardFromVCard->ConvertToEscapedVCard(escapedVCardStr);
01441     NS_ENSURE_SUCCESS(rv,rv);
01442     return rv;
01443 }
01444 
01445 #ifdef MOZ_XUL_APP
01446 
01447 NS_IMETHODIMP
01448 nsAddressBook::Handle(nsICommandLine* aCmdLine)
01449 {
01450   nsresult rv; 
01451   PRBool found;
01452 
01453   rv = aCmdLine->HandleFlag(NS_LITERAL_STRING("addressbook"), PR_FALSE, &found);
01454   NS_ENSURE_SUCCESS(rv, rv);
01455 
01456   if (!found)
01457     return NS_OK;
01458 
01459   nsCOMPtr<nsIWindowWatcher> wwatch (do_GetService(NS_WINDOWWATCHER_CONTRACTID));
01460   NS_ENSURE_TRUE(wwatch, NS_ERROR_FAILURE);
01461 
01462   nsCOMPtr<nsIDOMWindow> opened;
01463   wwatch->OpenWindow(nsnull, "chrome://messenger/content/addressbook/addressbook.xul",
01464                      "_blank", "chrome,dialog=no,all", nsnull, getter_AddRefs(opened));
01465   aCmdLine->SetPreventDefault(PR_TRUE);
01466   return NS_OK;
01467 }
01468 
01469 NS_IMETHODIMP
01470 nsAddressBook::GetHelpInfo(nsACString& aResult)
01471 {
01472   aResult.Assign(NS_LITERAL_CSTRING("  -addressbook         Open the address book at startup.\n"));
01473   return NS_OK;
01474 }
01475 
01476 #else // !MOZ_XUL_APP
01477 
01478 CMDLINEHANDLER_IMPL(nsAddressBook,"-addressbook","general.startup.addressbook","chrome://messenger/content/addressbook/addressbook.xul","Start with the addressbook.",NS_ADDRESSBOOKSTARTUPHANDLER_CONTRACTID,"Addressbook Startup Handler",PR_FALSE,"", PR_TRUE)
01479 
01480 #endif