Back to index

lightning-sunbird  0.9+nobinonly
nsSeamonkeyProfileMigrator.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; 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 The Mail Profile Migrator.
00016  *
00017  * The Initial Developer of the Original Code is Ben Goodger.
00018  * Portions created by the Initial Developer are Copyright (C) 2004
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *  Ben Goodger <ben@bengoodger.com>
00023  *  Scott MacGregor <mscott@mozilla.org>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsMailProfileMigratorUtils.h"
00040 #include "nsCRT.h"
00041 #include "nsDirectoryServiceDefs.h"
00042 #include "nsIObserverService.h"
00043 #include "nsIPasswordManagerInternal.h"
00044 #include "nsIPrefLocalizedString.h"
00045 #include "nsIPrefService.h"
00046 #include "nsIServiceManager.h"
00047 #include "nsISupportsArray.h"
00048 #include "nsISupportsPrimitives.h"
00049 #include "nsNetCID.h"
00050 #include "nsNetUtil.h"
00051 #include "nsSeamonkeyProfileMigrator.h"
00052 #include "nsIRelativeFilePref.h"
00053 #include "nsAppDirectoryServiceDefs.h"
00054 #include "prprf.h"
00055 #include "nsVoidArray.h"
00056 
00057 static PRUint32 StringHash(const char *ubuf);
00058 nsresult NS_MsgHashIfNecessary(nsCString &name);
00059 
00060 // Mail specific folder paths
00061 #define MAIL_DIR_50_NAME             NS_LITERAL_STRING("Mail")
00062 #define IMAP_MAIL_DIR_50_NAME        NS_LITERAL_STRING("ImapMail")
00063 #define NEWS_DIR_50_NAME             NS_LITERAL_STRING("News")
00064 
00065 
00067 // nsSeamonkeyProfileMigrator
00068 #define FILE_NAME_JUNKTRAINING    NS_LITERAL_STRING("training.dat")
00069 #define FILE_NAME_PERSONALDICTIONARY NS_LITERAL_STRING("persdict.dat")
00070 #define FILE_NAME_PERSONAL_ADDRESSBOOK NS_LITERAL_STRING("abook.mab")
00071 #define FILE_NAME_MAILVIEWS       NS_LITERAL_STRING("mailviews.dat")
00072 #define FILE_NAME_CERT8DB         NS_LITERAL_STRING("cert8.db")
00073 #define FILE_NAME_KEY3DB          NS_LITERAL_STRING("key3.db")
00074 #define FILE_NAME_SECMODDB        NS_LITERAL_STRING("secmod.db")
00075 #define FILE_NAME_MIMETYPES       NS_LITERAL_STRING("mimeTypes.rdf")
00076 #define FILE_NAME_PREFS           NS_LITERAL_STRING("prefs.js")
00077 #define FILE_NAME_USER_PREFS      NS_LITERAL_STRING("user.js")
00078 
00079 struct PrefBranchStruct {
00080   char*         prefName;
00081   PRInt32       type;
00082   union {
00083     char*       stringValue;
00084     PRInt32     intValue;
00085     PRBool      boolValue;
00086     PRUnichar*  wstringValue;
00087   };
00088 };
00089 
00090 NS_IMPL_ISUPPORTS2(nsSeamonkeyProfileMigrator, nsIMailProfileMigrator, nsITimerCallback)
00091 
00092 
00093 nsSeamonkeyProfileMigrator::nsSeamonkeyProfileMigrator()
00094 {
00095   mObserverService = do_GetService("@mozilla.org/observer-service;1");
00096   mMaxProgress = LL_ZERO;
00097   mCurrentProgress = LL_ZERO;
00098 }
00099 
00100 nsSeamonkeyProfileMigrator::~nsSeamonkeyProfileMigrator()
00101 {           
00102 }
00103 
00105 // nsITimerCallback
00106 
00107 NS_IMETHODIMP
00108 nsSeamonkeyProfileMigrator::Notify(nsITimer *timer)
00109 {
00110   CopyNextFolder();
00111   return NS_OK;
00112 }
00113 
00114 void nsSeamonkeyProfileMigrator::CopyNextFolder() 
00115 {
00116   if (mFileCopyTransactionIndex < mFileCopyTransactions->Count())
00117   {
00118     PRUint32 percentage = 0;
00119     fileTransactionEntry* fileTransaction = (fileTransactionEntry*) mFileCopyTransactions->SafeElementAt(mFileCopyTransactionIndex++);
00120     if (fileTransaction) // copy the file
00121     {
00122       fileTransaction->srcFile->CopyTo(fileTransaction->destFile, EmptyString());
00123 
00124       // add to our current progress
00125       PRInt64 fileSize;
00126       fileTransaction->srcFile->GetFileSize(&fileSize);
00127       LL_ADD(mCurrentProgress, mCurrentProgress, fileSize);
00128 
00129       PRInt64 percentDone;
00130       LL_MUL(percentDone, mCurrentProgress, 100);
00131 
00132       LL_DIV(percentDone, percentDone, mMaxProgress);
00133       
00134       LL_L2UI(percentage, percentDone);
00135 
00136       nsAutoString index;
00137       index.AppendInt( percentage ); 
00138 
00139       NOTIFY_OBSERVERS(MIGRATION_PROGRESS, index.get());
00140     }
00141     // fire a timer to handle the next one. 
00142     mFileIOTimer = do_CreateInstance("@mozilla.org/timer;1");
00143     if (mFileIOTimer)
00144       mFileIOTimer->InitWithCallback(NS_STATIC_CAST(nsITimerCallback *, this), percentage == 100 ? 500 : 0, nsITimer::TYPE_ONE_SHOT);
00145   } else
00146     EndCopyFolders();
00147   
00148   return;
00149 }
00150 
00151 void nsSeamonkeyProfileMigrator::EndCopyFolders() 
00152 {
00153   // clear out the file transaction array
00154   if (mFileCopyTransactions)
00155   {
00156     PRUint32 count = mFileCopyTransactions->Count();
00157     for (PRUint32 i = 0; i < count; ++i) 
00158     {
00159       fileTransactionEntry* fileTransaction = (fileTransactionEntry*) mFileCopyTransactions->ElementAt(i);
00160       if (fileTransaction)
00161       {
00162         fileTransaction->srcFile = nsnull;
00163         fileTransaction->destFile = nsnull;
00164         delete fileTransaction;
00165       }
00166     }
00167   
00168     mFileCopyTransactions->Clear();
00169     delete mFileCopyTransactions;
00170   }
00171 
00172   // notify the UI that we are done with the migration process
00173   nsAutoString index;
00174   index.AppendInt(nsIMailProfileMigrator::MAILDATA); 
00175   NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get());
00176 
00177   NOTIFY_OBSERVERS(MIGRATION_ENDED, nsnull);
00178 }
00179 
00181 // nsIMailProfileMigrator
00182 
00183 NS_IMETHODIMP
00184 nsSeamonkeyProfileMigrator::Migrate(PRUint16 aItems, nsIProfileStartup* aStartup, const PRUnichar* aProfile)
00185 {
00186   nsresult rv = NS_OK;
00187   PRBool aReplace = aStartup ? PR_TRUE : PR_FALSE;
00188 
00189   if (!mTargetProfile) {
00190     GetProfilePath(aStartup, mTargetProfile);
00191     if (!mTargetProfile) return NS_ERROR_FAILURE;
00192   }
00193   if (!mSourceProfile)
00194     GetSourceProfile(aProfile);
00195 
00196   NOTIFY_OBSERVERS(MIGRATION_STARTED, nsnull);
00197 
00198   COPY_DATA(CopyPreferences,  aReplace, nsIMailProfileMigrator::SETTINGS);
00199 
00200   // fake notifications for things we've already imported as part of CopyPreferences
00201   COPY_DATA(DummyCopyRoutine, aReplace, nsIMailProfileMigrator::ACCOUNT_SETTINGS);  
00202   COPY_DATA(DummyCopyRoutine, aReplace, nsIMailProfileMigrator::NEWSDATA);
00203 
00204   // copy junk mail training file
00205   COPY_DATA(CopyJunkTraining, aReplace, nsIMailProfileMigrator::JUNKTRAINING);
00206   COPY_DATA(CopyPasswords,    aReplace, nsIMailProfileMigrator::PASSWORDS);
00207 
00208   // the last thing to do is to actually copy over any mail folders we have marked for copying
00209   // we want to do this last and it will be asynchronous so the UI doesn't freeze up while we perform 
00210   // this potentially very long operation. 
00211   
00212   nsAutoString index;
00213   index.AppendInt(nsIMailProfileMigrator::MAILDATA); 
00214   NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get());
00215 
00216   // Generate the max progress value now that we know all of the files we need to copy
00217   PRUint32 count = mFileCopyTransactions->Count();
00218   for (PRUint32 i = 0; i < count; ++i) 
00219   {
00220     fileTransactionEntry* fileTransaction = (fileTransactionEntry*) mFileCopyTransactions->ElementAt(i);
00221     if (fileTransaction)
00222     {
00223       PRInt64 fileSize; 
00224       fileTransaction->srcFile->GetFileSize(&fileSize);
00225       LL_ADD(mMaxProgress, mMaxProgress, fileSize);
00226     }
00227   }
00228 
00229   CopyNextFolder();
00230 
00231   return rv;
00232 }
00233 
00234 NS_IMETHODIMP
00235 nsSeamonkeyProfileMigrator::GetMigrateData(const PRUnichar* aProfile, 
00236                                            PRBool aReplace, 
00237                                            PRUint16* aResult)
00238 {
00239   *aResult = 0;
00240 
00241   if (!mSourceProfile) {
00242     GetSourceProfile(aProfile);
00243     if (!mSourceProfile)
00244       return NS_ERROR_FILE_NOT_FOUND;
00245   }
00246 
00247   MigrationData data[] = { { ToNewUnicode(FILE_NAME_PREFS),
00248                              nsIMailProfileMigrator::SETTINGS,
00249                              PR_TRUE },
00250                            { ToNewUnicode(FILE_NAME_JUNKTRAINING),
00251                              nsIMailProfileMigrator::JUNKTRAINING,
00252                              PR_TRUE },
00253                           };
00254                                                                   
00255   // Frees file name strings allocated above.
00256   GetMigrateDataFromArray(data, sizeof(data)/sizeof(MigrationData), 
00257                           aReplace, mSourceProfile, aResult);
00258 
00259   // Now locate passwords
00260   nsXPIDLCString signonsFileName;
00261   GetSignonFileName(aReplace, getter_Copies(signonsFileName));
00262 
00263   if (!signonsFileName.IsEmpty()) {
00264     nsAutoString fileName; fileName.AssignWithConversion(signonsFileName);
00265     nsCOMPtr<nsIFile> sourcePasswordsFile;
00266     mSourceProfile->Clone(getter_AddRefs(sourcePasswordsFile));
00267     sourcePasswordsFile->Append(fileName);
00268     
00269     PRBool exists;
00270     sourcePasswordsFile->Exists(&exists);
00271     if (exists)
00272       *aResult |= nsIMailProfileMigrator::PASSWORDS;
00273   }
00274 
00275   // add some extra migration fields for things we also migrate
00276   *aResult |= nsIMailProfileMigrator::ACCOUNT_SETTINGS 
00277            | nsIMailProfileMigrator::MAILDATA 
00278            | nsIMailProfileMigrator::NEWSDATA
00279            | nsIMailProfileMigrator::ADDRESSBOOK_DATA;
00280 
00281   return NS_OK;
00282 }
00283 
00284 NS_IMETHODIMP
00285 nsSeamonkeyProfileMigrator::GetSourceExists(PRBool* aResult)
00286 {
00287   nsCOMPtr<nsISupportsArray> profiles;
00288   GetSourceProfiles(getter_AddRefs(profiles));
00289 
00290   if (profiles) { 
00291     PRUint32 count;
00292     profiles->Count(&count);
00293     *aResult = count > 0;
00294   }
00295   else
00296     *aResult = PR_FALSE;
00297 
00298   return NS_OK;
00299 }
00300 
00301 NS_IMETHODIMP
00302 nsSeamonkeyProfileMigrator::GetSourceHasMultipleProfiles(PRBool* aResult)
00303 {
00304   nsCOMPtr<nsISupportsArray> profiles;
00305   GetSourceProfiles(getter_AddRefs(profiles));
00306 
00307   if (profiles) {
00308     PRUint32 count;
00309     profiles->Count(&count);
00310     *aResult = count > 1;
00311   }
00312   else
00313     *aResult = PR_FALSE;
00314 
00315   return NS_OK;
00316 }
00317 
00318 NS_IMETHODIMP
00319 nsSeamonkeyProfileMigrator::GetSourceProfiles(nsISupportsArray** aResult)
00320 {
00321   if (!mProfileNames && !mProfileLocations) {
00322     nsresult rv = NS_NewISupportsArray(getter_AddRefs(mProfileNames));
00323     if (NS_FAILED(rv)) return rv;
00324 
00325     rv = NS_NewISupportsArray(getter_AddRefs(mProfileLocations));
00326     if (NS_FAILED(rv)) return rv;
00327 
00328     // Fills mProfileNames and mProfileLocations
00329     FillProfileDataFromSeamonkeyRegistry();
00330   }
00331   
00332   NS_IF_ADDREF(*aResult = mProfileNames);
00333   return NS_OK;
00334 }
00335 
00337 // nsSeamonkeyProfileMigrator
00338 
00339 nsresult
00340 nsSeamonkeyProfileMigrator::GetSourceProfile(const PRUnichar* aProfile)
00341 {
00342   PRUint32 count;
00343   mProfileNames->Count(&count);
00344   for (PRUint32 i = 0; i < count; ++i) {
00345     nsCOMPtr<nsISupportsString> str(do_QueryElementAt(mProfileNames, i));
00346     nsXPIDLString profileName;
00347     str->GetData(profileName);
00348     if (profileName.Equals(aProfile)) {
00349       mSourceProfile = do_QueryElementAt(mProfileLocations, i);
00350       break;
00351     }
00352   }
00353 
00354   return NS_OK;
00355 }
00356 
00357 nsresult
00358 nsSeamonkeyProfileMigrator::FillProfileDataFromSeamonkeyRegistry()
00359 {
00360   // Find the Seamonkey Registry
00361   nsCOMPtr<nsIProperties> fileLocator(do_GetService("@mozilla.org/file/directory_service;1"));
00362   nsCOMPtr<nsILocalFile> seamonkeyRegistry;
00363 #ifdef XP_WIN
00364   fileLocator->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(seamonkeyRegistry));
00365   NS_ENSURE_TRUE(seamonkeyRegistry, NS_ERROR_FAILURE);
00366 
00367   seamonkeyRegistry->Append(NS_LITERAL_STRING("Mozilla"));
00368   seamonkeyRegistry->Append(NS_LITERAL_STRING("registry.dat"));
00369 #elif defined(XP_MACOSX)
00370   fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(seamonkeyRegistry));
00371   NS_ENSURE_TRUE(seamonkeyRegistry, NS_ERROR_FAILURE);
00372   
00373   seamonkeyRegistry->Append(NS_LITERAL_STRING("Mozilla"));
00374   seamonkeyRegistry->Append(NS_LITERAL_STRING("Application Registry"));
00375 #elif defined(XP_UNIX)
00376   fileLocator->Get(NS_UNIX_HOME_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(seamonkeyRegistry));
00377   NS_ENSURE_TRUE(seamonkeyRegistry, NS_ERROR_FAILURE);
00378   
00379   seamonkeyRegistry->Append(NS_LITERAL_STRING(".mozilla"));
00380   seamonkeyRegistry->Append(NS_LITERAL_STRING("appreg"));
00381 #elif defined(XP_OS2)
00382   fileLocator->Get(NS_OS2_HOME_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(seamonkeyRegistry));
00383   NS_ENSURE_TRUE(seamonkeyRegistry, NS_ERROR_FAILURE);
00384   
00385   seamonkeyRegistry->Append(NS_LITERAL_STRING("Mozilla"));
00386   seamonkeyRegistry->Append(NS_LITERAL_STRING("registry.dat"));
00387 #endif
00388 
00389   return GetProfileDataFromRegistry(seamonkeyRegistry, mProfileNames, mProfileLocations);
00390 }
00391 
00392 #define F(a) nsSeamonkeyProfileMigrator::a
00393 
00394 #define MAKEPREFTRANSFORM(pref, newpref, getmethod, setmethod) \
00395   { pref, newpref, F(Get##getmethod), F(Set##setmethod), PR_FALSE, -1 }
00396 
00397 #define MAKESAMETYPEPREFTRANSFORM(pref, method) \
00398   { pref, 0, F(Get##method), F(Set##method), PR_FALSE, -1 }
00399 
00400 
00401 static 
00402 nsSeamonkeyProfileMigrator::PrefTransform gTransforms[] = {
00403 
00404 
00405   MAKESAMETYPEPREFTRANSFORM("signon.SignonFileName",                    String),
00406   MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showUserAgent",           Bool),
00407   MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showOrganization",        Bool),
00408   MAKESAMETYPEPREFTRANSFORM("mail.collect_addressbook",                 String), 
00409   MAKESAMETYPEPREFTRANSFORM("mail.collect_email_address_outgoing",      Bool),
00410   MAKESAMETYPEPREFTRANSFORM("mail.wrap_long_lines",                     Bool),
00411   MAKESAMETYPEPREFTRANSFORM("news.wrap_long_lines",                     Bool),
00412   MAKESAMETYPEPREFTRANSFORM("mailnews.customHeaders",                   String),
00413   MAKESAMETYPEPREFTRANSFORM("mail.default_html_action",                 Int),
00414   MAKESAMETYPEPREFTRANSFORM("mail.forward_message_mode",                Int),
00415   MAKESAMETYPEPREFTRANSFORM("mail.SpellCheckBeforeSend",                Bool),
00416   MAKESAMETYPEPREFTRANSFORM("mail.warn_on_send_accel_key",              Bool),
00417   MAKESAMETYPEPREFTRANSFORM("mailnews.html_domains",                    String),
00418   MAKESAMETYPEPREFTRANSFORM("mailnews.plaintext_domains",               String),
00419   MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showUserAgent",           Bool), 
00420   MAKESAMETYPEPREFTRANSFORM("mailnews.headers.showOrganization",        Bool), 
00421   MAKESAMETYPEPREFTRANSFORM("mail.biff.play_sound",                     Bool),
00422   MAKESAMETYPEPREFTRANSFORM("mail.biff.play_sound.type",                Int),
00423   MAKESAMETYPEPREFTRANSFORM("mail.biff.play_sound.url",                 String),
00424   MAKESAMETYPEPREFTRANSFORM("mail.biff.show_alert",                     Bool),
00425   MAKESAMETYPEPREFTRANSFORM("network.proxy.type",                       Int),
00426   MAKESAMETYPEPREFTRANSFORM("network.proxy.http",                       String),
00427   MAKESAMETYPEPREFTRANSFORM("network.proxy.http_port",                  Int),
00428   MAKESAMETYPEPREFTRANSFORM("network.proxy.ftp",                        String),
00429   MAKESAMETYPEPREFTRANSFORM("network.proxy.ftp_port",                   Int),
00430   MAKESAMETYPEPREFTRANSFORM("network.proxy.ssl",                        String),
00431   MAKESAMETYPEPREFTRANSFORM("network.proxy.ssl_port",                   Int),
00432   MAKESAMETYPEPREFTRANSFORM("network.proxy.socks",                      String),
00433   MAKESAMETYPEPREFTRANSFORM("network.proxy.socks_port",                 Int),
00434   MAKESAMETYPEPREFTRANSFORM("network.proxy.gopher",                     String),
00435   MAKESAMETYPEPREFTRANSFORM("network.proxy.gopher_port",                Int),
00436   MAKESAMETYPEPREFTRANSFORM("network.proxy.no_proxies_on",              String),
00437   MAKESAMETYPEPREFTRANSFORM("network.proxy.autoconfig_url",             String),
00438 
00439   MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.accounts",             String),
00440   MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.defaultaccount",       String),
00441   MAKESAMETYPEPREFTRANSFORM("mail.accountmanager.localfoldersserver",   String), 
00442   MAKESAMETYPEPREFTRANSFORM("mail.smtp.defaultserver",                  String),
00443   MAKESAMETYPEPREFTRANSFORM("mail.smtpservers",                         String),
00444 
00445   MAKESAMETYPEPREFTRANSFORM("msgcompose.font_face",                     String),
00446   MAKESAMETYPEPREFTRANSFORM("msgcompose.font_size",                     String),
00447   MAKESAMETYPEPREFTRANSFORM("msgcompose.text_color",                    String),
00448   MAKESAMETYPEPREFTRANSFORM("msgcompose.background_color",              String),
00449 
00450   MAKEPREFTRANSFORM("mail.pane_config","mail.pane_config.dynamic", Int, Int)
00451 };
00452 
00453 
00454 nsresult
00455 nsSeamonkeyProfileMigrator::TransformPreferences(const nsAString& aSourcePrefFileName,
00456                                                  const nsAString& aTargetPrefFileName)
00457 {
00458   PrefTransform* transform;
00459   PrefTransform* end = gTransforms + sizeof(gTransforms)/sizeof(PrefTransform);
00460 
00461   // Load the source pref file
00462   nsCOMPtr<nsIPrefService> psvc(do_GetService(NS_PREFSERVICE_CONTRACTID));
00463   psvc->ResetPrefs();
00464 
00465   nsCOMPtr<nsIFile> sourcePrefsFile;
00466   mSourceProfile->Clone(getter_AddRefs(sourcePrefsFile));
00467   sourcePrefsFile->Append(aSourcePrefFileName);
00468   psvc->ReadUserPrefs(sourcePrefsFile);
00469 
00470   nsCOMPtr<nsIPrefBranch> branch(do_QueryInterface(psvc));
00471   for (transform = gTransforms; transform < end; ++transform)
00472     transform->prefGetterFunc(transform, branch);
00473 
00474   // read in the various pref branch trees for accounts, identities, servers, etc. 
00475 
00476   nsVoidArray* accounts = new nsVoidArray();
00477   nsVoidArray* identities = new nsVoidArray();
00478   nsVoidArray* servers = new nsVoidArray();
00479   nsVoidArray* smtpservers = new nsVoidArray();
00480   nsVoidArray* ldapservers = new nsVoidArray();
00481   nsVoidArray* labelPrefs = new nsVoidArray();
00482 
00483   if (!accounts || !identities || !servers || !smtpservers || !ldapservers)
00484     return NS_ERROR_OUT_OF_MEMORY;
00485   
00486   ReadBranch("mail.account.", psvc, accounts);
00487   ReadBranch("mail.identity.", psvc, identities);
00488   ReadBranch("mail.server.", psvc, servers);
00489   ReadBranch("mail.smtpserver.", psvc, smtpservers);
00490   ReadBranch("ldap_2.servers.", psvc, ldapservers);
00491   ReadBranch("mailnews.labels.", psvc, labelPrefs);
00492 
00493   // the signature file prefs may be paths to files in the seamonkey profile path
00494   // so we need to copy them over and fix these paths up before we write them out to the new prefs.js
00495   CopySignatureFiles(identities, psvc);
00496 
00497   // certain mail prefs may actually be absolute paths instead of profile relative paths
00498   // we need to fix these paths up before we write them out to the new prefs.js
00499   CopyMailFolders(servers, psvc);
00500 
00501   CopyAddressBookDirectories(ldapservers, psvc);
00502 
00503   // Now that we have all the pref data in memory, load the target pref file,
00504   // and write it back out
00505   psvc->ResetPrefs();
00506   for (transform = gTransforms; transform < end; ++transform)
00507     transform->prefSetterFunc(transform, branch);
00508 
00509   WriteBranch("mail.account.", psvc, accounts);
00510   WriteBranch("mail.identity.", psvc, identities);
00511   WriteBranch("mail.server.", psvc, servers);
00512   WriteBranch("mail.smtpserver.", psvc, smtpservers);
00513   WriteBranch("ldap_2.servers.", psvc, ldapservers);
00514   WriteBranch("mailnews.labels.", psvc, labelPrefs);
00515 
00516   delete accounts;
00517   delete identities;
00518   delete servers;
00519   delete smtpservers;
00520   delete ldapservers;
00521   delete labelPrefs;
00522 
00523   nsCOMPtr<nsIFile> targetPrefsFile;
00524   mTargetProfile->Clone(getter_AddRefs(targetPrefsFile));
00525   targetPrefsFile->Append(aTargetPrefFileName);
00526   psvc->SavePrefFile(targetPrefsFile);
00527 
00528   return NS_OK;
00529 }
00530 
00531 nsresult nsSeamonkeyProfileMigrator::CopyAddressBookDirectories(nsVoidArray* aLdapServers, nsIPrefService* aPrefService)
00532 {
00533   // each server has a pref ending with .filename. The value of that pref points to a profile which we
00534   // need to migrate.
00535   nsAutoString index;
00536   index.AppendInt(nsIMailProfileMigrator::ADDRESSBOOK_DATA); 
00537   NOTIFY_OBSERVERS(MIGRATION_ITEMBEFOREMIGRATE, index.get());
00538 
00539   PRUint32 count = aLdapServers->Count();
00540   for (PRUint32 i = 0; i < count; ++i) 
00541   {
00542     PrefBranchStruct* pref = (PrefBranchStruct*) aLdapServers->ElementAt(i);
00543     nsDependentCString prefName (pref->prefName);
00544 
00545     if (StringEndsWith(prefName, nsDependentCString(".filename")))
00546     {
00547       // should we be assuming utf-8 or ascii here?
00548       CopyFile(NS_ConvertUTF8toUCS2(pref->stringValue), NS_ConvertUTF8toUCS2(pref->stringValue)); 
00549     }
00550 
00551     // we don't need to do anything to the fileName pref itself
00552   }
00553 
00554   NOTIFY_OBSERVERS(MIGRATION_ITEMAFTERMIGRATE, index.get());
00555 
00556   return NS_OK;
00557 }
00558 
00559 
00560 nsresult nsSeamonkeyProfileMigrator::CopySignatureFiles(nsVoidArray* aIdentities, nsIPrefService* aPrefService)
00561 {
00562   nsresult rv = NS_OK;
00563 
00564   PRUint32 count = aIdentities->Count();
00565   for (PRUint32 i = 0; i < count; ++i) 
00566   {
00567     PrefBranchStruct* pref = (PrefBranchStruct*)aIdentities->ElementAt(i);
00568     nsDependentCString prefName (pref->prefName);
00569 
00570     // a partial fix for bug #255043
00571     // if the user's signature file from seamonkey lives in the 
00572     // seamonkey profile root, we'll copy it over to the new 
00573     // thunderbird profile root and thenn set the pref to the new value
00574     // note, this doesn't work for multiple signatures that live
00575     // below the seamonkey profile root
00576     if (StringEndsWith(prefName, nsDependentCString(".sig_file")))
00577     {
00578       // turn the pref into a nsILocalFile
00579       nsCOMPtr<nsILocalFile> srcSigFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
00580       srcSigFile->SetPersistentDescriptor(nsDependentCString(pref->stringValue));
00581 
00582       nsCOMPtr<nsIFile> targetSigFile;
00583       rv = mTargetProfile->Clone(getter_AddRefs(targetSigFile));
00584       NS_ENSURE_SUCCESS(rv, rv); 
00585 
00586       // now make the copy
00587       PRBool exists; 
00588       srcSigFile->Exists(&exists);
00589       if (exists)
00590       {
00591         nsAutoString leafName;
00592         srcSigFile->GetLeafName(leafName);
00593         srcSigFile->CopyTo(targetSigFile,leafName); // will fail if we've already copied a sig file here
00594         targetSigFile->Append(leafName);
00595 
00596         // now write out the new descriptor
00597         nsCAutoString descriptorString;
00598         nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(targetSigFile);
00599         localFile->GetPersistentDescriptor(descriptorString);
00600         nsCRT::free(pref->stringValue);
00601         pref->stringValue = ToNewCString(descriptorString);
00602       }   
00603     }
00604   }
00605   return NS_OK;
00606 }
00607 
00608 nsresult nsSeamonkeyProfileMigrator::CopyMailFolders(nsVoidArray* aMailServers, nsIPrefService* aPrefService)
00609 {
00610   // Each server has a .directory pref which points to the location of the mail data
00611   // for that server. We need to do two things for that case...
00612   // (1) Fix up the directory path for the new profile
00613   // (2) copy the mail folder data from the source directory pref to the destination directory pref
00614 
00615   nsresult rv = NS_OK;
00616 
00617   PRUint32 count = aMailServers->Count();
00618   for (PRUint32 i = 0; i < count; ++i) 
00619   {
00620     PrefBranchStruct* pref = (PrefBranchStruct*)aMailServers->ElementAt(i);
00621     nsDependentCString prefName (pref->prefName);
00622 
00623     if (StringEndsWith(prefName, NS_LITERAL_CSTRING(".directory-rel"))) {
00624       // When the directories are modified below, we may change the .directory
00625       // pref. As we don't have a pref branch to modify at this stage and set
00626       // up the relative folders properly, we'll just remove all the
00627       // *.directory-rel prefs. Mailnews will cope with this, creating them
00628       // when it first needs them.
00629       if (pref->type == nsIPrefBranch::PREF_STRING)
00630         NS_Free(pref->stringValue);
00631 
00632       aMailServers->RemoveElementAt(i);
00633       // Now decrease i and count to match the removed element
00634       --i;
00635       --count;
00636     }
00637     else if (StringEndsWith(prefName, nsDependentCString(".directory")))
00638     {
00639       // let's try to get a branch for this particular server to simplify things
00640       prefName.Cut( prefName.Length() - strlen("directory"), strlen("directory"));
00641       prefName.Insert("mail.server.", 0);
00642 
00643       nsCOMPtr<nsIPrefBranch> serverBranch;
00644       aPrefService->GetBranch(prefName.get(), getter_AddRefs(serverBranch));
00645 
00646       if (!serverBranch)
00647         break; // should we clear out this server pref from aMailServers?
00648 
00649       nsXPIDLCString serverType; 
00650       serverBranch->GetCharPref("type", getter_Copies(serverType));
00651 
00652       nsCOMPtr<nsILocalFile> sourceMailFolder;
00653       serverBranch->GetComplexValue("directory", NS_GET_IID(nsILocalFile), getter_AddRefs(sourceMailFolder));
00654 
00655       // now based on type, we need to build a new destination path for the mail folders for this server
00656       nsCOMPtr<nsIFile> targetMailFolder;
00657       if (serverType.Equals("imap"))
00658       {
00659         mTargetProfile->Clone(getter_AddRefs(targetMailFolder));
00660         targetMailFolder->Append(IMAP_MAIL_DIR_50_NAME);       
00661       }
00662       else if (serverType.Equals("none") || serverType.Equals("pop3")) 
00663       {
00664         // local folders and POP3 servers go under <profile>\Mail
00665         mTargetProfile->Clone(getter_AddRefs(targetMailFolder));
00666         targetMailFolder->Append(MAIL_DIR_50_NAME);
00667       }
00668       else if (serverType.Equals("nntp"))
00669       {
00670         mTargetProfile->Clone(getter_AddRefs(targetMailFolder));
00671         targetMailFolder->Append(NEWS_DIR_50_NAME); 
00672       }
00673 
00674       if (targetMailFolder)
00675       {
00676         // for all of our server types, append the host name to the directory as part of the new location
00677         nsXPIDLCString hostName;
00678         serverBranch->GetCharPref("hostname", getter_Copies(hostName));
00679         targetMailFolder->Append(NS_ConvertASCIItoUTF16(hostName));
00680 
00681         // we should make sure the host name based directory we are going to migrate 
00682         // the accounts into is unique. This protects against the case where the user
00683         // has multiple servers with the same host name.
00684         targetMailFolder->CreateUnique(nsIFile::DIRECTORY_TYPE, 0777);
00685 
00686         rv = RecursiveCopy(sourceMailFolder, targetMailFolder);
00687         // now we want to make sure the actual directory pref that gets
00688         // transformed into the new profile's pref.js has the right file
00689         // location.
00690         nsCAutoString descriptorString;
00691         nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(targetMailFolder);
00692         localFile->GetPersistentDescriptor(descriptorString);
00693         nsCRT::free(pref->stringValue);
00694         pref->stringValue = ToNewCString(descriptorString);
00695       }
00696     }
00697     else if (StringEndsWith(prefName, nsDependentCString(".newsrc.file")))
00698     {
00699       // copy the news RC file into \News. this won't work if the user has different newsrc files for each account
00700       // I don't know what to do in that situation.
00701       
00702       nsCOMPtr<nsIFile> targetNewsRCFile;
00703       mTargetProfile->Clone(getter_AddRefs(targetNewsRCFile));
00704       targetNewsRCFile->Append(NEWS_DIR_50_NAME); 
00705 
00706       // turn the pref into a nsILocalFile
00707       nsCOMPtr<nsILocalFile> srcNewsRCFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
00708       srcNewsRCFile->SetPersistentDescriptor(nsDependentCString(pref->stringValue));
00709 
00710       // now make the copy
00711       PRBool exists; 
00712       srcNewsRCFile->Exists(&exists);
00713       if (exists)
00714       {
00715         nsAutoString leafName;
00716         srcNewsRCFile->GetLeafName(leafName);
00717         srcNewsRCFile->CopyTo(targetNewsRCFile,leafName); // will fail if we've already copied a newsrc file here
00718         targetNewsRCFile->Append(leafName);
00719 
00720         // now write out the new descriptor
00721         nsCAutoString descriptorString;
00722         nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(targetNewsRCFile);
00723         localFile->GetPersistentDescriptor(descriptorString);
00724         nsCRT::free(pref->stringValue);
00725         pref->stringValue = ToNewCString(descriptorString);
00726       }      
00727     }
00728   }
00729 
00730   return NS_OK;
00731 }
00732 
00733 nsresult
00734 nsSeamonkeyProfileMigrator::CopyPreferences(PRBool aReplace)
00735 {
00736   nsresult rv = NS_OK;
00737   if (!aReplace)
00738     return rv;
00739 
00740   rv |= TransformPreferences(FILE_NAME_PREFS, FILE_NAME_PREFS); 
00741   rv |= CopyFile(FILE_NAME_USER_PREFS, FILE_NAME_USER_PREFS);
00742 
00743   // Security Stuff
00744   rv |= CopyFile(FILE_NAME_CERT8DB, FILE_NAME_CERT8DB);
00745   rv |= CopyFile(FILE_NAME_KEY3DB, FILE_NAME_KEY3DB);
00746   rv |= CopyFile(FILE_NAME_SECMODDB, FILE_NAME_SECMODDB);
00747 
00748   // User MIME Type overrides
00749   rv |= CopyFile(FILE_NAME_MIMETYPES, FILE_NAME_MIMETYPES);
00750   rv |= CopyFile(FILE_NAME_PERSONALDICTIONARY, FILE_NAME_PERSONALDICTIONARY);
00751   rv |= CopyFile(FILE_NAME_MAILVIEWS, FILE_NAME_MAILVIEWS);
00752   return rv;
00753 }
00754 
00755 void nsSeamonkeyProfileMigrator::ReadBranch(const char * branchName, nsIPrefService* aPrefService, 
00756                                             nsVoidArray* aPrefs)
00757 {
00758   // Enumerate the branch
00759   nsCOMPtr<nsIPrefBranch> branch;
00760   aPrefService->GetBranch(branchName, getter_AddRefs(branch));
00761 
00762   PRUint32 count;
00763   char** prefs = nsnull;
00764   nsresult rv = branch->GetChildList("", &count, &prefs);
00765   if (NS_FAILED(rv)) return;
00766 
00767   for (PRUint32 i = 0; i < count; ++i) {
00768     // Save each pref's value into an array
00769     char* currPref = prefs[i];
00770     PRInt32 type;
00771     branch->GetPrefType(currPref, &type);
00772     PrefBranchStruct* pref = new PrefBranchStruct;
00773     pref->prefName = currPref;
00774     pref->type = type;
00775     switch (type) {
00776     case nsIPrefBranch::PREF_STRING:
00777       rv = branch->GetCharPref(currPref, &pref->stringValue);
00778       break;
00779     case nsIPrefBranch::PREF_BOOL:
00780       rv = branch->GetBoolPref(currPref, &pref->boolValue);
00781       break;
00782     case nsIPrefBranch::PREF_INT:
00783       rv = branch->GetIntPref(currPref, &pref->intValue);
00784       break;
00785     case nsIPrefBranch::PREF_INVALID:
00786       {
00787         nsCOMPtr<nsIPrefLocalizedString> str;
00788         rv = branch->GetComplexValue(currPref, 
00789                                     NS_GET_IID(nsIPrefLocalizedString), 
00790                                     getter_AddRefs(str));
00791         if (NS_SUCCEEDED(rv) && str)
00792           str->ToString(&pref->wstringValue);
00793       }
00794       break;
00795     }
00796 
00797     if (NS_SUCCEEDED(rv))
00798       aPrefs->AppendElement((void*)pref);
00799   }
00800 }
00801 
00802 void
00803 nsSeamonkeyProfileMigrator::WriteBranch(const char * branchName, nsIPrefService* aPrefService,
00804                                         nsVoidArray* aPrefs)
00805 {
00806   nsresult rv;
00807 
00808   // Enumerate the branch
00809   nsCOMPtr<nsIPrefBranch> branch;
00810   aPrefService->GetBranch(branchName, getter_AddRefs(branch));
00811 
00812   PRUint32 count = aPrefs->Count();
00813   for (PRUint32 i = 0; i < count; ++i) {
00814     PrefBranchStruct* pref = (PrefBranchStruct*)aPrefs->ElementAt(i);
00815     switch (pref->type) {
00816     case nsIPrefBranch::PREF_STRING:
00817       rv = branch->SetCharPref(pref->prefName, pref->stringValue);
00818       nsCRT::free(pref->stringValue);
00819       pref->stringValue = nsnull;
00820       break;
00821     case nsIPrefBranch::PREF_BOOL:
00822       rv = branch->SetBoolPref(pref->prefName, pref->boolValue);
00823       break;
00824     case nsIPrefBranch::PREF_INT:
00825       rv = branch->SetIntPref(pref->prefName, pref->intValue);
00826       break;
00827     case nsIPrefBranch::PREF_INVALID:
00828       nsCOMPtr<nsIPrefLocalizedString> pls(do_CreateInstance("@mozilla.org/pref-localizedstring;1"));
00829       pls->SetData(pref->wstringValue);
00830       rv = branch->SetComplexValue(pref->prefName, 
00831                                    NS_GET_IID(nsIPrefLocalizedString),
00832                                    pls);
00833       nsCRT::free(pref->wstringValue);
00834       pref->wstringValue = nsnull;
00835       break;
00836     }
00837     nsCRT::free(pref->prefName);
00838     pref->prefName = nsnull;
00839     delete pref;
00840     pref = nsnull;
00841   }
00842   aPrefs->Clear();
00843 }
00844 
00845 nsresult nsSeamonkeyProfileMigrator::DummyCopyRoutine(PRBool aReplace)
00846 {
00847   // place holder function only to fake the UI out into showing some migration process.
00848   return NS_OK;
00849 }
00850 
00851 nsresult
00852 nsSeamonkeyProfileMigrator::CopyJunkTraining(PRBool aReplace)
00853 {
00854   return aReplace ? CopyFile(FILE_NAME_JUNKTRAINING, FILE_NAME_JUNKTRAINING) : NS_OK;
00855 }
00856 
00857 nsresult
00858 nsSeamonkeyProfileMigrator::CopyPasswords(PRBool aReplace)
00859 {
00860   nsresult rv;
00861 
00862   nsXPIDLCString signonsFileName;
00863   GetSignonFileName(aReplace, getter_Copies(signonsFileName));
00864 
00865   if (signonsFileName.IsEmpty())
00866     return NS_ERROR_FILE_NOT_FOUND;
00867 
00868   nsAutoString fileName; fileName.AssignWithConversion(signonsFileName);
00869   if (aReplace)
00870     rv = CopyFile(fileName, fileName);
00871   else {
00872     // don't do anything right now
00873   }
00874   return rv;
00875 }
00876 
00877 // helper functions for news migration
00878 static PRUint32 StringHash(const char *ubuf)
00879 {
00880   unsigned char * buf = (unsigned char*) ubuf;
00881   PRUint32 h=1;
00882   while(*buf) {
00883     h = 0x63c63cd9*h + 0x9c39c33d + (int32)*buf;
00884     buf++;
00885   }
00886   return h;
00887 }
00888 
00889 nsresult NS_MsgHashIfNecessary(nsCString &name)
00890 {
00891 #if defined(XP_MAC)
00892   const PRUint32 MAX_LEN = 25;
00893 #elif defined(XP_UNIX) || defined(XP_BEOS)
00894   const PRUint32 MAX_LEN = 55;
00895 #elif defined(XP_WIN32)
00896   const PRUint32 MAX_LEN = 55;
00897 #elif defined(XP_OS2)
00898   const PRUint32 MAX_LEN = 55;
00899 #else
00900   #error need_to_define_your_max_filename_length
00901 #endif
00902   nsCAutoString illegalChars(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS);
00903   nsCAutoString str(name);
00904 
00905   // Given a filename, make it safe for filesystem
00906   // certain filenames require hashing because they 
00907   // are too long or contain illegal characters
00908   PRInt32 illegalCharacterIndex = str.FindCharInSet(illegalChars);
00909   char hashedname[MAX_LEN + 1];
00910   if (illegalCharacterIndex == kNotFound) 
00911   {
00912     // no illegal chars, it's just too long
00913     // keep the initial part of the string, but hash to make it fit
00914     if (str.Length() > MAX_LEN) 
00915     {
00916       PL_strncpy(hashedname, str.get(), MAX_LEN + 1);
00917       PR_snprintf(hashedname + MAX_LEN - 8, 9, "%08lx",
00918                 (unsigned long) StringHash(str.get()));
00919       name = hashedname;
00920     }
00921   }
00922   else 
00923   {
00924       // found illegal chars, hash the whole thing
00925       // if we do substitution, then hash, two strings
00926       // could hash to the same value.
00927       // for example, on mac:  "foo__bar", "foo:_bar", "foo::bar"
00928       // would map to "foo_bar".  this way, all three will map to
00929       // different values
00930       PR_snprintf(hashedname, 9, "%08lx",
00931                 (unsigned long) StringHash(str.get()));
00932       name = hashedname;
00933   }
00934   
00935   return NS_OK;
00936 }