Back to index

lightning-sunbird  0.9+nobinonly
nsAddrDatabase.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 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) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or 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 // this file implements the nsAddrDatabase interface using the MDB Interface.
00040 
00041 #include "nsAddrDatabase.h"
00042 #include "nsIEnumerator.h"
00043 #include "nsString.h"
00044 #include "nsReadableUtils.h"
00045 #include "nsRDFCID.h"
00046 #include "nsUnicharUtils.h"
00047 #include "nsMsgUtils.h"
00048 #include "nsAbBaseCID.h"
00049 #include "nsIAbCard.h"
00050 #include "nsIAbMDBCard.h"
00051 #include "nsIAbDirectory.h"
00052 #include "nsIAbMDBDirectory.h"
00053 #include "nsIAddrBookSession.h"
00054 
00055 #include "nsIServiceManager.h"
00056 #include "nsRDFCID.h"
00057 
00058 #include "nsMorkCID.h"
00059 #include "nsIMdbFactoryFactory.h"
00060 #include "nsXPIDLString.h"
00061 #include "nsIRDFService.h"
00062 #include "nsIProxyObjectManager.h"
00063 #include "nsProxiedService.h"
00064 #include "prprf.h"
00065 
00066 #include "nsIPromptService.h"
00067 #include "nsIStringBundle.h"
00068 #include "nsIFile.h"
00069 #include "nsIFileSpec.h"
00070 
00071 #include "nsAddressBook.h" // for the map
00072 
00073 #include "nsEmbedCID.h"
00074 
00075 #define ID_PAB_TABLE            1
00076 #define ID_DELETEDCARDS_TABLE           2
00077 
00078 const PRInt32 kAddressBookDBVersion = 1;
00079 
00080 static const char kPabTableKind[] = "ns:addrbk:db:table:kind:pab";
00081 static const char kDeletedCardsTableKind[] = "ns:addrbk:db:table:kind:deleted"; // this table is used to keep the deleted cards
00082 
00083 static const char kCardRowScope[] = "ns:addrbk:db:row:scope:card:all";
00084 static const char kListRowScope[] = "ns:addrbk:db:row:scope:list:all";
00085 static const char kDataRowScope[] = "ns:addrbk:db:row:scope:data:all";
00086 
00087 #define DATAROW_ROWID 1
00088 
00089 #define COLUMN_STR_MAX 16
00090 
00091 #define PURGE_CUTOFF_COUNT 50
00092 
00093 static const char kRecordKeyColumn[] = "RecordKey";
00094 static const char kLastRecordKeyColumn[] = "LastRecordKey";
00095 
00096 static const char kMailListTotalLists[] = "ListTotalLists";    // total number of mail list in a mailing list
00097 static const char kLowerListNameColumn[] = "LowercaseListName";
00098 
00099 struct mdbOid gAddressBookTableOID;
00100 
00101 static const char kMailListAddressFormat[] = "Address%d";
00102 
00103 static NS_DEFINE_CID(kCMorkFactory, NS_MORK_CID);
00104 
00105 nsAddrDatabase::nsAddrDatabase()
00106     : m_mdbEnv(nsnull), m_mdbStore(nsnull),
00107       m_mdbPabTable(nsnull), 
00108       m_mdbDeletedCardsTable(nsnull),
00109       m_dbName(""), m_mdbTokensInitialized(PR_FALSE), 
00110       m_ChangeListeners(nsnull),
00111       m_PabTableKind(0),
00112       m_MailListTableKind(0),
00113       m_DeletedCardsTableKind(0),
00114       m_CardRowScopeToken(0),
00115       m_FirstNameColumnToken(0),
00116       m_LastNameColumnToken(0),
00117       m_PhoneticFirstNameColumnToken(0),
00118       m_PhoneticLastNameColumnToken(0),
00119       m_DisplayNameColumnToken(0),
00120       m_NickNameColumnToken(0),
00121       m_PriEmailColumnToken(0),
00122       m_2ndEmailColumnToken(0),
00123       m_DefaultEmailColumnToken(0),
00124       m_CardTypeColumnToken(0),
00125       m_WorkPhoneColumnToken(0),
00126       m_HomePhoneColumnToken(0),
00127       m_FaxColumnToken(0),
00128       m_PagerColumnToken(0),
00129       m_CellularColumnToken(0),
00130       m_WorkPhoneTypeColumnToken(0),
00131       m_HomePhoneTypeColumnToken(0),
00132       m_FaxTypeColumnToken(0),
00133       m_PagerTypeColumnToken(0),
00134       m_CellularTypeColumnToken(0),
00135       m_HomeAddressColumnToken(0),
00136       m_HomeAddress2ColumnToken(0),
00137       m_HomeCityColumnToken(0),
00138       m_HomeStateColumnToken(0),
00139       m_HomeZipCodeColumnToken(0),
00140       m_HomeCountryColumnToken(0),
00141       m_WorkAddressColumnToken(0),
00142       m_WorkAddress2ColumnToken(0),
00143       m_WorkCityColumnToken(0),
00144       m_WorkStateColumnToken(0),
00145       m_WorkZipCodeColumnToken(0),
00146       m_WorkCountryColumnToken(0),
00147       m_CompanyColumnToken(0),
00148       m_AimScreenNameColumnToken(0),
00149       m_AnniversaryYearColumnToken(0),
00150       m_AnniversaryMonthColumnToken(0),
00151       m_AnniversaryDayColumnToken(0),
00152       m_SpouseNameColumnToken(0),
00153       m_FamilyNameColumnToken(0),
00154       m_DefaultAddressColumnToken(0),
00155       m_CategoryColumnToken(0),
00156       m_WebPage1ColumnToken(0),
00157       m_WebPage2ColumnToken(0),
00158       m_BirthYearColumnToken(0),
00159       m_BirthMonthColumnToken(0),
00160       m_BirthDayColumnToken(0),
00161       m_Custom1ColumnToken(0),
00162       m_Custom2ColumnToken(0),
00163       m_Custom3ColumnToken(0),
00164       m_Custom4ColumnToken(0),
00165       m_NotesColumnToken(0),
00166       m_LastModDateColumnToken(0),
00167       m_MailFormatColumnToken(0),
00168       m_PopularityIndexColumnToken(0),
00169       m_AllowRemoteContentColumnToken(0),
00170       m_AddressCharSetColumnToken(0),
00171       m_LastRecordKey(0),
00172       m_dbDirectory(nsnull)
00173 {
00174 }
00175 
00176 nsAddrDatabase::~nsAddrDatabase()
00177 {
00178     Close(PR_FALSE);    // better have already been closed.
00179     if (m_ChangeListeners)
00180     {
00181         // better not be any listeners, because we're going away.
00182         NS_ASSERTION(m_ChangeListeners->Count() == 0, "shouldn't have any listeners");
00183         delete m_ChangeListeners;
00184     }
00185 
00186     RemoveFromCache(this);
00187 }
00188 
00189 NS_IMPL_THREADSAFE_ADDREF(nsAddrDatabase)
00190 
00191 NS_IMETHODIMP_(nsrefcnt) nsAddrDatabase::Release(void)                    
00192 {                                                      
00193   // XXX FIX THIS
00194   NS_PRECONDITION(0 != mRefCnt, "dup release");        
00195   nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
00196   NS_LOG_RELEASE(this, count,"nsAddrDatabase"); 
00197   if (count == 0)    // OK, the cache is no longer holding onto this, so we really want to delete it, 
00198   {                // after removing it from the cache.
00199     mRefCnt = 1; /* stabilize */
00200     RemoveFromCache(this);
00201     // clean up after ourself!
00202     if (m_mdbPabTable)
00203       m_mdbPabTable->Release();
00204     if (m_mdbDeletedCardsTable)
00205       m_mdbDeletedCardsTable->Release();
00206     NS_IF_RELEASE(m_mdbStore);
00207     NS_IF_RELEASE(m_mdbEnv);
00208     NS_DELETEXPCOM(this);                              
00209     return 0;                                          
00210   }
00211   return count;                                      
00212 }
00213 
00214 NS_IMETHODIMP nsAddrDatabase::QueryInterface(REFNSIID aIID, void** aResult)
00215 {   
00216     if (aResult == NULL)  
00217         return NS_ERROR_NULL_POINTER;  
00218 
00219     if (aIID.Equals(NS_GET_IID(nsIAddrDatabase)) ||
00220         aIID.Equals(NS_GET_IID(nsIAddrDBAnnouncer)) ||
00221         aIID.Equals(NS_GET_IID(nsISupports))) {
00222         *aResult = NS_STATIC_CAST(nsIAddrDatabase*, this);   
00223         NS_ADDREF_THIS();
00224         return NS_OK;
00225     }
00226     return NS_NOINTERFACE;
00227 }   
00228 
00229 NS_IMETHODIMP nsAddrDatabase::AddListener(nsIAddrDBListener *listener)
00230 {
00231   if (!listener)
00232     return NS_ERROR_NULL_POINTER;
00233   if (!m_ChangeListeners) 
00234     {
00235     m_ChangeListeners = new nsVoidArray();
00236     if (!m_ChangeListeners) 
00237             return NS_ERROR_OUT_OF_MEMORY;
00238   }
00239     PRInt32 count = m_ChangeListeners->Count();
00240     PRInt32 i;
00241     for (i = 0; i < count; i++)
00242     {
00243         nsIAddrDBListener *dbListener = (nsIAddrDBListener *)m_ChangeListeners->ElementAt(i);
00244         if (dbListener == listener)
00245             return NS_OK;
00246     }
00247     return m_ChangeListeners->AppendElement(listener);
00248 }
00249 
00250 NS_IMETHODIMP nsAddrDatabase::RemoveListener(nsIAddrDBListener *listener)
00251 {
00252     if (!m_ChangeListeners) 
00253         return NS_OK;
00254 
00255     PRInt32 count = m_ChangeListeners->Count();
00256     PRInt32 i;
00257     for (i = 0; i < count; i++)
00258     {
00259         nsIAddrDBListener *dbListener = (nsIAddrDBListener *)m_ChangeListeners->ElementAt(i);
00260         if (dbListener == listener)
00261         {
00262             m_ChangeListeners->RemoveElementAt(i);
00263             return NS_OK;
00264         }
00265     }
00266     return NS_ERROR_FAILURE;
00267 }
00268 
00269 NS_IMETHODIMP nsAddrDatabase::NotifyCardAttribChange(PRUint32 abCode)
00270 {
00271   if (!m_ChangeListeners)
00272       return NS_OK;
00273     PRInt32 i;
00274     for (i = 0; i < m_ChangeListeners->Count(); i++)
00275     {
00276         nsIAddrDBListener *changeListener =
00277             (nsIAddrDBListener *) m_ChangeListeners->ElementAt(i);
00278 
00279         nsresult rv = changeListener->OnCardAttribChange(abCode);
00280     NS_ENSURE_SUCCESS(rv, rv);
00281     }
00282     return NS_OK;
00283 }
00284 
00285 NS_IMETHODIMP nsAddrDatabase::NotifyCardEntryChange(PRUint32 abCode, nsIAbCard *card)
00286 {
00287   if (!m_ChangeListeners)
00288       return NS_OK;
00289   PRInt32 i;
00290   PRInt32 count = m_ChangeListeners->Count();
00291   for (i = count - 1; i >= 0; i--)
00292   {
00293     nsIAddrDBListener *changeListener = 
00294             (nsIAddrDBListener *) m_ChangeListeners->ElementAt(i);
00295 
00296     if (changeListener)
00297     {
00298       nsresult rv = changeListener->OnCardEntryChange(abCode, card);
00299       NS_ENSURE_SUCCESS(rv, rv);
00300     }
00301     else
00302       m_ChangeListeners->RemoveElementAt(i); //remove null ptr in the list
00303     // since we're looping down, this is ok
00304   }
00305   return NS_OK;
00306 }
00307 
00308 nsresult nsAddrDatabase::NotifyListEntryChange(PRUint32 abCode, nsIAbDirectory *dir)
00309 {
00310   if (!m_ChangeListeners)
00311         return NS_OK;
00312 
00313     PRInt32 i;
00314     PRInt32 count = m_ChangeListeners->Count();
00315     for (i = 0; i < count; i++)
00316     {
00317         nsIAddrDBListener *changeListener = 
00318             (nsIAddrDBListener *) m_ChangeListeners->ElementAt(i);
00319 
00320         nsresult rv = changeListener->OnListEntryChange(abCode, dir); 
00321     NS_ENSURE_SUCCESS(rv, rv);
00322     }
00323     return NS_OK;
00324 }
00325 
00326 
00327 NS_IMETHODIMP nsAddrDatabase::NotifyAnnouncerGoingAway(void)
00328 {
00329   if (!m_ChangeListeners)
00330     return NS_OK;
00331     // run loop backwards because listeners remove themselves from the list 
00332     // on this notification
00333     PRInt32 i;
00334     for (i = m_ChangeListeners->Count() - 1; i >= 0 ; i--)
00335     {
00336         nsIAddrDBListener *changeListener =
00337             (nsIAddrDBListener *) m_ChangeListeners->ElementAt(i);
00338 
00339         nsresult rv = changeListener->OnAnnouncerGoingAway(); 
00340     NS_ENSURE_SUCCESS(rv, rv);
00341     }
00342   return NS_OK;
00343 }
00344 
00345 
00346 
00347 nsVoidArray *nsAddrDatabase::m_dbCache = NULL;
00348 
00349 //----------------------------------------------------------------------
00350 // GetDBCache
00351 //----------------------------------------------------------------------
00352 
00353 nsVoidArray/*<nsAddrDatabase>*/ *
00354 nsAddrDatabase::GetDBCache()
00355 {
00356     if (!m_dbCache)
00357         m_dbCache = new nsVoidArray();
00358 
00359     return m_dbCache;
00360     
00361 }
00362 
00363 void
00364 nsAddrDatabase::CleanupCache()
00365 {
00366     if (m_dbCache) // clean up memory leak
00367     {
00368         PRInt32 i;
00369         for (i = 0; i < GetDBCache()->Count(); i++)
00370         {
00371             nsAddrDatabase* pAddrDB = NS_STATIC_CAST(nsAddrDatabase*, GetDBCache()->ElementAt(i));
00372             if (pAddrDB)
00373             {
00374                 pAddrDB->ForceClosed();
00375                 i--;    // back up array index, since closing removes db from cache.
00376             }
00377         }
00378 //        NS_ASSERTION(GetNumInCache() == 0, "some msg dbs left open");    // better not be any open db's.
00379         delete m_dbCache;
00380     }
00381     m_dbCache = nsnull; // Need to reset to NULL since it's a
00382               // static global ptr and maybe referenced 
00383               // again in other places.
00384 }
00385 
00386 //----------------------------------------------------------------------
00387 // FindInCache - this addrefs the db it finds.
00388 //----------------------------------------------------------------------
00389 nsAddrDatabase* nsAddrDatabase::FindInCache(nsFileSpec *dbName)
00390 {
00391     PRInt32 i;
00392     for (i = 0; i < GetDBCache()->Count(); i++)
00393     {
00394         nsAddrDatabase* pAddrDB = NS_STATIC_CAST(nsAddrDatabase*, GetDBCache()->ElementAt(i));
00395         if (pAddrDB->MatchDbName(dbName))
00396         {
00397             NS_ADDREF(pAddrDB);
00398             return pAddrDB;
00399         }
00400     }
00401     return nsnull;
00402 }
00403 
00404 //----------------------------------------------------------------------
00405 // FindInCache
00406 //----------------------------------------------------------------------
00407 PRInt32 nsAddrDatabase::FindInCache(nsAddrDatabase* pAddrDB)
00408 {
00409     PRInt32 i;
00410     for (i = 0; i < GetDBCache()->Count(); i++)
00411     {
00412         if (GetDBCache()->ElementAt(i) == pAddrDB)
00413         {
00414             return(i);
00415         }
00416     }
00417     return(-1);
00418 }
00419 
00420 PRBool nsAddrDatabase::MatchDbName(nsFileSpec* dbName)    // returns PR_TRUE if they match
00421 {
00422     return (m_dbName == (*dbName)); 
00423 }
00424 
00425 //----------------------------------------------------------------------
00426 // RemoveFromCache
00427 //----------------------------------------------------------------------
00428 void nsAddrDatabase::RemoveFromCache(nsAddrDatabase* pAddrDB)
00429 {
00430     PRInt32 i = FindInCache(pAddrDB);
00431     if (i != -1)
00432     {
00433         GetDBCache()->RemoveElementAt(i);
00434     }
00435 }
00436 
00437 nsIMdbFactory *nsAddrDatabase::GetMDBFactory()
00438 {
00439     static nsIMdbFactory *gMDBFactory = nsnull;
00440     if (!gMDBFactory)
00441     {
00442         nsresult rv;
00443     nsCOMPtr <nsIMdbFactoryFactory> factoryfactory = do_CreateInstance(kCMorkFactory, &rv);
00444 
00445         if (NS_SUCCEEDED(rv) && factoryfactory)
00446           rv = factoryfactory->GetMdbFactory(&gMDBFactory);
00447     }
00448     return gMDBFactory;
00449 }
00450 
00451 #if defined(XP_WIN) || defined(XP_OS2)
00452 // this code is stolen from nsFileSpecWin. Since MDB requires a native path, for 
00453 // the time being, we'll just take the Unix/Canonical form and munge it
00454 void nsAddrDatabase::UnixToNative(char*& ioPath)
00455 // This just does string manipulation.  It doesn't check reality, or canonify, or
00456 // anything
00457 //----------------------------------------------------------------------------------------
00458 {
00459     // Allow for relative or absolute.  We can do this in place, because the
00460     // native path is never longer.
00461     
00462     if (!ioPath || !*ioPath)
00463         return;
00464         
00465     char* src = ioPath;
00466     if (*ioPath == '/')
00467     {
00468       // Strip initial slash for an absolute path
00469       src++;
00470     }
00471         
00472     // Convert the vertical slash to a colon
00473     char* cp = src + 1;
00474     
00475     // If it was an absolute path, check for the drive letter
00476     if (*ioPath == '/' && strstr(cp, "|/") == cp)
00477     *cp = ':';
00478     
00479     // Convert '/' to '\'.
00480     while (*++cp)
00481     {
00482       if (*cp == '/')
00483         *cp = '\\';
00484     }
00485 
00486     if (*ioPath == '/') {
00487     for (cp = ioPath; *cp; ++cp)
00488       *cp = *(cp + 1);
00489   }
00490 }
00491 #endif /* XP_WIN || XP_OS2 */
00492 
00493 #ifdef XP_MAC
00494 // this code is stolen from nsFileSpecMac. Since MDB requires a native path, for 
00495 // the time being, we'll just take the Unix/Canonical form and munge it
00496 void nsAddrDatabase::UnixToNative(char*& ioPath)
00497 // This just does string manipulation.  It doesn't check reality, or canonify, or
00498 // anything
00499 //----------------------------------------------------------------------------------------
00500 {
00501     // Relying on the fact that the unix path is always longer than the mac path:
00502     size_t len = strlen(ioPath);
00503     char* result = new char[len + 2]; // ... but allow for the initial colon in a partial name
00504     if (result)
00505     {
00506         char* dst = result;
00507         const char* src = ioPath;
00508         if (*src == '/')             // * full path
00509             src++;
00510         else if (PL_strchr(src, '/'))    // * partial path, and not just a leaf name
00511             *dst++ = ':';
00512         strcpy(dst, src);
00513 
00514         while ( *dst != 0)
00515         {
00516             if (*dst == '/')
00517                 *dst++ = ':';
00518             else
00519                 *dst++;
00520         }
00521         nsCRT::free(ioPath);
00522         ioPath = result;
00523     }
00524 }
00525 #endif /* XP_MAC */
00526 
00527 /* caller need to delete *aDbPath */
00528 NS_IMETHODIMP nsAddrDatabase::GetDbPath(nsFileSpec * *aDbPath)
00529 {
00530     if (!aDbPath)
00531         return NS_ERROR_NULL_POINTER;
00532 
00533     nsFileSpec* pFilePath = new nsFileSpec();
00534     if (!pFilePath)
00535         return NS_ERROR_OUT_OF_MEMORY;
00536     *pFilePath = m_dbName;
00537     *aDbPath = pFilePath;
00538     return NS_OK;
00539 }
00540 
00541 NS_IMETHODIMP nsAddrDatabase::SetDbPath(nsFileSpec * aDbPath)
00542 {
00543     m_dbName = (*aDbPath);
00544     return NS_OK;
00545 }
00546 
00547 NS_IMETHODIMP nsAddrDatabase::Open
00548 (nsIFile *aMabFile, PRBool aCreate, PRBool upgrading /* unused */, nsIAddrDatabase** pAddrDB)
00549 { 
00550   *pAddrDB = nsnull;
00551   
00552   nsCOMPtr<nsIFileSpec> mabIFileSpec;
00553   nsFileSpec mabFileSpec;
00554   // Convert the nsILocalFile into an nsIFileSpec
00555   // TODO: convert users of nsIFileSpec to nsILocalFile
00556   // and avoid this step.
00557   nsresult rv = NS_NewFileSpecFromIFile(aMabFile, getter_AddRefs(mabIFileSpec));
00558   NS_ENSURE_SUCCESS(rv, rv);
00559   rv = mabIFileSpec->GetFileSpec(&mabFileSpec);
00560   NS_ENSURE_SUCCESS(rv, rv);
00561 
00562   nsAddrDatabase *pAddressBookDB = (nsAddrDatabase *) FindInCache(&mabFileSpec);
00563   if (pAddressBookDB) {
00564     *pAddrDB = pAddressBookDB;
00565     return NS_OK;
00566   }
00567   
00568   rv = OpenInternal(&mabFileSpec, aCreate, pAddrDB);
00569   if (NS_SUCCEEDED(rv))
00570     return NS_OK;
00571   
00572   if (rv == NS_ERROR_FILE_ACCESS_DENIED)
00573   {
00574     static PRBool gAlreadyAlerted;
00575      // only do this once per session to avoid annoying the user
00576     if (!gAlreadyAlerted)
00577     {
00578       gAlreadyAlerted = PR_TRUE;
00579       nsXPIDLCString mabFileName;
00580       mabFileName.Adopt(mabFileSpec.GetLeafName());
00581       AlertAboutLockedMabFile(NS_ConvertASCIItoUCS2(mabFileName).get());
00582     }
00583   }
00584   // try one more time
00585   // but first rename corrupt mab file
00586   // and prompt the user
00587   else if (aCreate) 
00588   {
00589     nsCOMPtr<nsIFile> dummyBackupMabFile;
00590     nsCOMPtr<nsIFile> actualBackupMabFile;
00591 
00592     // First create a clone of the corrupt mab file that we'll
00593     // use to generate the name for the backup file that we are
00594     // going to move it to.
00595     rv = aMabFile->Clone(getter_AddRefs(dummyBackupMabFile));
00596     NS_ENSURE_SUCCESS(rv, rv);
00597 
00598     // Now create a second clone that we'll use to do the move
00599     // (this allows us to leave the original name intact)
00600     rv = aMabFile->Clone(getter_AddRefs(actualBackupMabFile));
00601     NS_ENSURE_SUCCESS(rv, rv);
00602 
00603     // Now we try and generate a new name for the corrupt mab
00604     // file using the dummy backup mab file
00605 
00606     // First append .bak - we have to do this the long way as
00607     // AppendNative is to the path, not the LeafName.
00608     nsCAutoString dummyBackupMabFileName;
00609     rv = dummyBackupMabFile->GetNativeLeafName(dummyBackupMabFileName);
00610     NS_ENSURE_SUCCESS(rv, rv);
00611 
00612     dummyBackupMabFileName.Append(NS_LITERAL_CSTRING(".bak"));
00613 
00614     rv = dummyBackupMabFile->SetNativeLeafName(dummyBackupMabFileName);
00615     NS_ENSURE_SUCCESS(rv, rv);
00616 
00617     // Now see if we can create it unique
00618     rv = dummyBackupMabFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
00619     NS_ENSURE_SUCCESS(rv, rv);
00620 
00621     // Now get the new name
00622     nsCAutoString backupMabFileName;
00623     rv = dummyBackupMabFile->GetNativeLeafName(backupMabFileName);
00624     NS_ENSURE_SUCCESS(rv, rv);
00625 
00626     // And the parent directory
00627     nsCOMPtr<nsIFile> parentDir;
00628     rv = dummyBackupMabFile->GetParent(getter_AddRefs(parentDir));
00629     NS_ENSURE_SUCCESS(rv, rv);
00630 
00631     // Now move the corrupt file to its backup location
00632     rv = actualBackupMabFile->MoveToNative(parentDir, backupMabFileName);
00633     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to rename corrupt mab file");
00634 
00635     if (NS_SUCCEEDED(rv)) {
00636       nsCOMPtr<nsIFileSpec> newMabIFileSpec;
00637       nsFileSpec newMabFileSpec;
00638       // Convert the nsILocalFile into an nsIFileSpec
00639       // TODO: convert users of nsIFileSpec to nsILocalFile
00640       // and avoid this step.
00641       nsresult rv = NS_NewFileSpecFromIFile(aMabFile, getter_AddRefs(newMabIFileSpec));
00642       NS_ENSURE_SUCCESS(rv, rv);
00643 
00644       rv = newMabIFileSpec->GetFileSpec(&newMabFileSpec);
00645       NS_ENSURE_SUCCESS(rv, rv);
00646       
00647       rv = OpenInternal(&newMabFileSpec, aCreate, pAddrDB);
00648       NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create .mab file, after rename");
00649 
00650       if (NS_SUCCEEDED(rv)) {
00651         nsCAutoString originalMabFileName;
00652         rv = aMabFile->GetNativeLeafName(originalMabFileName);
00653         NS_ENSURE_SUCCESS(rv, rv);
00654 
00655         // if this fails, we don't care
00656         (void)AlertAboutCorruptMabFile(NS_ConvertASCIItoUCS2(originalMabFileName).get(), 
00657           NS_ConvertASCIItoUCS2(backupMabFileName).get());
00658       }
00659     }
00660   }
00661   return rv;
00662 }
00663 
00664 nsresult nsAddrDatabase::DisplayAlert(const PRUnichar *titleName, const PRUnichar *alertStringName, const PRUnichar **formatStrings, PRInt32 numFormatStrings)
00665 {
00666   nsresult rv;
00667   nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00668   NS_ENSURE_SUCCESS(rv, rv);
00669 
00670   nsCOMPtr<nsIStringBundle> bundle;
00671   rv = bundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", getter_AddRefs(bundle));
00672   NS_ENSURE_SUCCESS(rv, rv);
00673   
00674   nsXPIDLString alertMessage;
00675   rv = bundle->FormatStringFromName(alertStringName, formatStrings, numFormatStrings,
00676     getter_Copies(alertMessage));
00677   NS_ENSURE_SUCCESS(rv, rv);
00678 
00679   nsXPIDLString alertTitle;
00680   rv = bundle->GetStringFromName(titleName, getter_Copies(alertTitle));
00681   NS_ENSURE_SUCCESS(rv, rv);
00682 
00683   nsCOMPtr<nsIPromptService> prompter =
00684       do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv);
00685   NS_ENSURE_SUCCESS(rv, rv);
00686 
00687   return prompter->Alert(nsnull /* we don't know the parent window */, alertTitle.get(), alertMessage.get());
00688 }
00689 
00690 nsresult nsAddrDatabase::AlertAboutCorruptMabFile(const PRUnichar *aOldFileName, const PRUnichar *aNewFileName)
00691 {
00692   const PRUnichar *formatStrings[] = { aOldFileName, aOldFileName, aNewFileName };
00693   return DisplayAlert(NS_LITERAL_STRING("corruptMabFileTitle").get(),
00694     NS_LITERAL_STRING("corruptMabFileAlert").get(), formatStrings, 3);
00695 }
00696 
00697 nsresult nsAddrDatabase::AlertAboutLockedMabFile(const PRUnichar *aFileName)
00698 {
00699   const PRUnichar *formatStrings[] = { aFileName };
00700   return DisplayAlert(NS_LITERAL_STRING("lockedMabFileTitle").get(),
00701     NS_LITERAL_STRING("lockedMabFileAlert").get(), formatStrings, 1);
00702 }
00703 
00704 nsresult
00705 nsAddrDatabase::OpenInternal(nsFileSpec *aMabFile, PRBool aCreate, nsIAddrDatabase** pAddrDB)
00706 {    
00707   nsAddrDatabase *pAddressBookDB = new nsAddrDatabase();
00708   if (!pAddressBookDB) {
00709     return NS_ERROR_OUT_OF_MEMORY;
00710   }
00711   
00712   NS_ADDREF(pAddressBookDB);
00713   
00714   nsresult rv = pAddressBookDB->OpenMDB(aMabFile, aCreate);
00715   if (NS_SUCCEEDED(rv)) 
00716   {
00717     pAddressBookDB->SetDbPath(aMabFile);
00718     GetDBCache()->AppendElement(pAddressBookDB);
00719     *pAddrDB = pAddressBookDB;
00720   }
00721   else 
00722   {
00723     *pAddrDB = nsnull;
00724     pAddressBookDB->ForceClosed();
00725     NS_IF_RELEASE(pAddressBookDB);
00726     pAddressBookDB = nsnull;
00727   }
00728   return rv;
00729 }
00730 
00731 // Open the MDB database synchronously. If successful, this routine
00732 // will set up the m_mdbStore and m_mdbEnv of the database object 
00733 // so other database calls can work.
00734 NS_IMETHODIMP nsAddrDatabase::OpenMDB(nsFileSpec *dbName, PRBool create)
00735 {
00736   nsresult ret = NS_OK;
00737   nsIMdbFactory *myMDBFactory = GetMDBFactory();
00738   if (myMDBFactory)
00739   {
00740     ret = myMDBFactory->MakeEnv(NULL, &m_mdbEnv);
00741     if (NS_SUCCEEDED(ret))
00742     {
00743       nsIMdbThumb *thumb = nsnull;
00744       const char *pFilename = dbName->GetCString(); /* do not free */
00745       char    *nativeFileName = nsCRT::strdup(pFilename);
00746       nsIMdbHeap* dbHeap = 0;
00747       mdb_bool dbFrozen = mdbBool_kFalse; // not readonly, we want modifiable
00748       
00749       if (!nativeFileName)
00750         return NS_ERROR_OUT_OF_MEMORY;
00751       
00752       if (m_mdbEnv)
00753         m_mdbEnv->SetAutoClear(PR_TRUE);
00754       
00755 #if defined(XP_WIN) || defined(XP_OS2) || defined(XP_MAC)
00756       UnixToNative(nativeFileName);
00757 #endif
00758       if (!dbName->Exists()) 
00759         ret = NS_ERROR_FILE_NOT_FOUND;
00760       else
00761       {
00762         mdbOpenPolicy inOpenPolicy;
00763         mdb_bool    canOpen;
00764         mdbYarn        outFormatVersion;
00765         nsIMdbFile* oldFile = 0;
00766         PRBool isEmptyFile = !dbName->GetFileSize();
00767         
00768         ret = myMDBFactory->OpenOldFile(m_mdbEnv, dbHeap, nativeFileName,
00769           dbFrozen, &oldFile);
00770         if ( oldFile )
00771         {
00772           if ( ret == NS_OK )
00773           {
00774             ret = myMDBFactory->CanOpenFilePort(m_mdbEnv, oldFile, // the file to investigate
00775               &canOpen, &outFormatVersion);
00776             if (ret == 0 && canOpen)
00777             {
00778               inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
00779               inOpenPolicy.mOpenPolicy_MinMemory = 0;
00780               inOpenPolicy.mOpenPolicy_MaxLazy = 0;
00781               
00782               ret = myMDBFactory->OpenFileStore(m_mdbEnv, dbHeap,
00783                 oldFile, &inOpenPolicy, &thumb); 
00784             }
00785             else if (!isEmptyFile)
00786               ret = NS_ERROR_FILE_ACCESS_DENIED;
00787           }
00788           NS_RELEASE(oldFile); // always release our file ref, store has own
00789         }
00790         if (NS_FAILED(ret))
00791           ret = NS_ERROR_FILE_ACCESS_DENIED;
00792       }
00793       
00794       nsCRT::free(nativeFileName);
00795       
00796       if (NS_SUCCEEDED(ret) && thumb)
00797       {
00798         mdb_count outTotal;    // total somethings to do in operation
00799         mdb_count outCurrent;  // subportion of total completed so far
00800         mdb_bool outDone = PR_FALSE;      // is operation finished?
00801         mdb_bool outBroken;     // is operation irreparably dead and broken?
00802         do
00803         {
00804           ret = thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
00805           if (ret != 0)
00806           { 
00807             outDone = PR_TRUE;
00808             break;
00809           }
00810         }
00811         while (NS_SUCCEEDED(ret) && !outBroken && !outDone);
00812         if (NS_SUCCEEDED(ret) && outDone)
00813         {
00814           ret = myMDBFactory->ThumbToOpenStore(m_mdbEnv, thumb, &m_mdbStore);
00815           if (ret == NS_OK && m_mdbStore)
00816           {
00817             ret = InitExistingDB();
00818             create = PR_FALSE;
00819           }
00820         }
00821       }
00822       else if (create && ret != NS_ERROR_FILE_ACCESS_DENIED)
00823       {
00824         nsIMdbFile* newFile = 0;
00825         ret = myMDBFactory->CreateNewFile(m_mdbEnv, dbHeap, dbName->GetCString(), &newFile);
00826         if ( newFile )
00827         {
00828           if (ret == NS_OK)
00829           {
00830             mdbOpenPolicy inOpenPolicy;
00831             
00832             inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
00833             inOpenPolicy.mOpenPolicy_MinMemory = 0;
00834             inOpenPolicy.mOpenPolicy_MaxLazy = 0;
00835             
00836             ret = myMDBFactory->CreateNewFileStore(m_mdbEnv, dbHeap,
00837                                                    newFile, &inOpenPolicy,
00838                                                    &m_mdbStore);
00839             if (ret == NS_OK)
00840               ret = InitNewDB();
00841           }
00842           NS_RELEASE(newFile); // always release our file ref, store has own
00843         }
00844       }
00845       NS_IF_RELEASE(thumb);
00846     }
00847   }
00848   //Convert the DB error to a valid nsresult error.
00849   if (ret == 1)
00850     ret = NS_ERROR_FAILURE;
00851   return ret;
00852 }
00853 
00854 NS_IMETHODIMP nsAddrDatabase::CloseMDB(PRBool commit)
00855 {
00856     if (commit)
00857         Commit(nsAddrDBCommitType::kSessionCommit);
00858 //???    RemoveFromCache(this);  // if we've closed it, better not leave it in the cache.
00859     return NS_OK;
00860 }
00861 
00862 // force the database to close - this'll flush out anybody holding onto
00863 // a database without having a listener!
00864 // This is evil in the com world, but there are times we need to delete the file.
00865 NS_IMETHODIMP nsAddrDatabase::ForceClosed()
00866 {
00867     nsresult    err = NS_OK;
00868     nsCOMPtr<nsIAddrDatabase> aDb(do_QueryInterface(this, &err));
00869 
00870     // make sure someone has a reference so object won't get deleted out from under us.
00871     AddRef();    
00872     NotifyAnnouncerGoingAway();
00873     // OK, remove from cache first and close the store.
00874     RemoveFromCache(this);
00875 
00876     err = CloseMDB(PR_FALSE);    // since we're about to delete it, no need to commit.
00877     NS_IF_RELEASE(m_mdbStore);
00878     Release();
00879     return err;
00880 }
00881 
00882 NS_IMETHODIMP nsAddrDatabase::Commit(PRUint32 commitType)
00883 {
00884   nsresult err = NS_OK;
00885   nsIMdbThumb *commitThumb = nsnull;
00886 
00887   if (commitType == nsAddrDBCommitType::kLargeCommit ||
00888       commitType == nsAddrDBCommitType::kSessionCommit)
00889   {
00890     mdb_percent outActualWaste = 0;
00891     mdb_bool outShould;
00892     if (m_mdbStore && m_mdbEnv)
00893     {
00894       // check how much space would be saved by doing a compress commit.
00895       // If it's more than 30%, go for it.
00896       // N.B. - I'm not sure this calls works in Mork for all cases.
00897       err = m_mdbStore->ShouldCompress(m_mdbEnv, 30, &outActualWaste, &outShould);
00898       if (NS_SUCCEEDED(err) && outShould)
00899       {
00900         commitType = nsAddrDBCommitType::kCompressCommit;
00901       }
00902     }
00903   }
00904 
00905   if (m_mdbStore && m_mdbEnv)
00906   {
00907     switch (commitType)
00908     {
00909       case nsAddrDBCommitType::kSmallCommit:
00910         err = m_mdbStore->SmallCommit(m_mdbEnv);
00911         break;
00912       case nsAddrDBCommitType::kLargeCommit:
00913         err = m_mdbStore->LargeCommit(m_mdbEnv, &commitThumb);
00914         break;
00915       case nsAddrDBCommitType::kSessionCommit:
00916         // comment out until persistence works.
00917         err = m_mdbStore->SessionCommit(m_mdbEnv, &commitThumb);
00918         break;
00919       case nsAddrDBCommitType::kCompressCommit:
00920         err = m_mdbStore->CompressCommit(m_mdbEnv, &commitThumb);
00921         break;
00922     }
00923   }
00924   if (commitThumb && m_mdbEnv)
00925   {
00926     mdb_count outTotal = 0;    // total somethings to do in operation
00927     mdb_count outCurrent = 0;  // subportion of total completed so far
00928     mdb_bool outDone = PR_FALSE;      // is operation finished?
00929     mdb_bool outBroken = PR_FALSE;     // is operation irreparably dead and broken?
00930     while (!outDone && !outBroken && err == NS_OK)
00931     {
00932       err = commitThumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
00933     }
00934     NS_RELEASE(commitThumb);
00935   }
00936   // ### do something with error, but clear it now because mork errors out on commits.
00937   if (m_mdbEnv)
00938     m_mdbEnv->ClearErrors();
00939   return err;
00940 }
00941 
00942 NS_IMETHODIMP nsAddrDatabase::Close(PRBool forceCommit /* = TRUE */)
00943 {
00944     return CloseMDB(forceCommit);
00945 }
00946 
00947 // set up empty tablesetc.
00948 nsresult nsAddrDatabase::InitNewDB()
00949 {
00950   nsresult err = InitMDBInfo();
00951   if (NS_SUCCEEDED(err))
00952   {
00953     err = InitPabTable();
00954     err = InitLastRecorKey();
00955     Commit(nsAddrDBCommitType::kLargeCommit);
00956   }
00957   return err;
00958 }
00959 
00960 nsresult nsAddrDatabase::AddRowToDeletedCardsTable(nsIAbCard *card, nsIMdbRow **pCardRow)
00961 {
00962   if (!m_mdbEnv)
00963     return NS_ERROR_NULL_POINTER;
00964 
00965   nsresult rv = NS_OK;
00966   if (!m_mdbDeletedCardsTable)
00967     rv = InitDeletedCardsTable(PR_TRUE);
00968   if (NS_SUCCEEDED(rv)) {
00969     // lets first purge old records if there are more than PURGE_CUTOFF_COUNT records
00970     PurgeDeletedCardTable();
00971     nsCOMPtr<nsIMdbRow> cardRow;
00972     rv = GetNewRow(getter_AddRefs(cardRow));
00973     if (NS_SUCCEEDED(rv) && cardRow) {
00974       mdb_err merror = m_mdbDeletedCardsTable->AddRow(m_mdbEnv, cardRow);
00975       if (merror != NS_OK) return NS_ERROR_FAILURE;
00976       nsXPIDLString unicodeStr;
00977       card->GetFirstName(getter_Copies(unicodeStr));
00978       AddFirstName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
00979 
00980       card->GetLastName(getter_Copies(unicodeStr));
00981       AddLastName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
00982 
00983       card->GetDisplayName(getter_Copies(unicodeStr));
00984       AddDisplayName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
00985 
00986       card->GetPrimaryEmail(getter_Copies(unicodeStr));
00987       if (unicodeStr)
00988         AddUnicodeToColumn(cardRow, m_PriEmailColumnToken, m_LowerPriEmailColumnToken, unicodeStr);
00989 
00990       PRUint32 nowInSeconds;
00991       PRTime now = PR_Now();
00992       PRTime2Seconds(now, &nowInSeconds);
00993       AddIntColumn(cardRow, m_LastModDateColumnToken, nowInSeconds);
00994 
00995       nsXPIDLString value;
00996       GetCardValue(card, CARD_ATTRIB_PALMID, getter_Copies(value));
00997       if (value)
00998       {
00999         nsCOMPtr<nsIAbCard> addedCard;
01000         rv = CreateCardFromDeletedCardsTable(cardRow, 0, getter_AddRefs(addedCard));
01001         if (NS_SUCCEEDED(rv))
01002           SetCardValue(addedCard, CARD_ATTRIB_PALMID, value, PR_FALSE);
01003       }
01004       NS_IF_ADDREF(*pCardRow = cardRow);
01005     }
01006     Commit(nsAddrDBCommitType::kLargeCommit);
01007   }
01008   return rv;
01009 }
01010 
01011 nsresult nsAddrDatabase::DeleteRowFromDeletedCardsTable(nsIMdbRow *pCardRow)
01012 {
01013   if (!m_mdbEnv)
01014     return NS_ERROR_NULL_POINTER;
01015 
01016   mdb_err merror = NS_OK;
01017   if (m_mdbDeletedCardsTable) {
01018     pCardRow->CutAllColumns(m_mdbEnv);
01019     merror = m_mdbDeletedCardsTable->CutRow(m_mdbEnv, pCardRow);
01020   }
01021   return merror;
01022 }
01023 
01024 
01025 nsresult nsAddrDatabase::InitDeletedCardsTable(PRBool aCreate)
01026 {
01027   nsresult mdberr = NS_OK;
01028   if (!m_mdbDeletedCardsTable)
01029   {
01030     struct mdbOid deletedCardsTableOID;
01031     deletedCardsTableOID.mOid_Scope = m_CardRowScopeToken;
01032     deletedCardsTableOID.mOid_Id = ID_DELETEDCARDS_TABLE;
01033     if (m_mdbStore && m_mdbEnv)
01034     {
01035       m_mdbStore->GetTable(m_mdbEnv, &deletedCardsTableOID, &m_mdbDeletedCardsTable);
01036       // if deletedCardsTable does not exist and bCreate is set, create a new one
01037       if (!m_mdbDeletedCardsTable && aCreate)
01038       {
01039         mdberr = (nsresult) m_mdbStore->NewTableWithOid(m_mdbEnv, &deletedCardsTableOID, 
01040                                                         m_DeletedCardsTableKind, 
01041                                                         PR_TRUE, (const mdbOid*)nsnull, 
01042                                                         &m_mdbDeletedCardsTable);
01043       }
01044     }
01045   }
01046   return mdberr;
01047 }
01048 
01049 nsresult nsAddrDatabase::InitPabTable()
01050 {
01051   return m_mdbStore && m_mdbEnv ? m_mdbStore->NewTableWithOid(m_mdbEnv,
01052                                                   &gAddressBookTableOID, 
01053                                                   m_PabTableKind,
01054                                                   PR_FALSE,
01055                                                   (const mdbOid*)nsnull,
01056                                                   &m_mdbPabTable)
01057       : NS_ERROR_NULL_POINTER;
01058 }
01059 
01060 //save the last record number, store in m_DataRowScopeToken, row 1
01061 nsresult nsAddrDatabase::InitLastRecorKey()
01062 {
01063   if (!m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
01064     return NS_ERROR_NULL_POINTER;
01065 
01066   nsIMdbRow *pDataRow = nsnull;
01067   mdbOid dataRowOid;
01068   dataRowOid.mOid_Scope = m_DataRowScopeToken;
01069   dataRowOid.mOid_Id = DATAROW_ROWID;
01070   nsresult err = m_mdbStore->NewRowWithOid(m_mdbEnv, &dataRowOid, &pDataRow);
01071 
01072   if (NS_SUCCEEDED(err) && pDataRow)
01073   {
01074     m_LastRecordKey = 0;
01075     err = AddIntColumn(pDataRow, m_LastRecordKeyColumnToken, 0);
01076     err = m_mdbPabTable->AddRow(m_mdbEnv, pDataRow);
01077     NS_RELEASE(pDataRow);
01078   }
01079   return err;
01080 }
01081 
01082 nsresult nsAddrDatabase::GetDataRow(nsIMdbRow **pDataRow)
01083 {
01084   if (!m_mdbStore || !m_mdbEnv)
01085     return NS_ERROR_NULL_POINTER;
01086 
01087   nsIMdbRow *pRow = nsnull;
01088   mdbOid dataRowOid;
01089   dataRowOid.mOid_Scope = m_DataRowScopeToken;
01090   dataRowOid.mOid_Id = DATAROW_ROWID;
01091   m_mdbStore->GetRow(m_mdbEnv, &dataRowOid, &pRow);
01092   *pDataRow = pRow;
01093 
01094   return pRow ? NS_OK : NS_ERROR_FAILURE;
01095 }
01096 
01097 nsresult nsAddrDatabase::GetLastRecordKey()
01098 {
01099     if (!m_mdbPabTable)
01100         return NS_ERROR_NULL_POINTER;
01101 
01102     nsCOMPtr <nsIMdbRow> pDataRow;
01103     nsresult err = GetDataRow(getter_AddRefs(pDataRow));
01104 
01105     if (NS_SUCCEEDED(err) && pDataRow)
01106     {
01107         m_LastRecordKey = 0;
01108         err = GetIntColumn(pDataRow, m_LastRecordKeyColumnToken, &m_LastRecordKey, 0);
01109         if (NS_FAILED(err))
01110             err = NS_ERROR_NOT_AVAILABLE;
01111         return NS_OK;
01112     }
01113 
01114     return NS_ERROR_NOT_AVAILABLE;
01115 }
01116 
01117 nsresult nsAddrDatabase::UpdateLastRecordKey()
01118 {
01119   if (!m_mdbPabTable || !m_mdbEnv)
01120     return NS_ERROR_NULL_POINTER;
01121 
01122   nsCOMPtr <nsIMdbRow> pDataRow;
01123   nsresult err = GetDataRow(getter_AddRefs(pDataRow));
01124 
01125   if (NS_SUCCEEDED(err) && pDataRow)
01126   {
01127     err = AddIntColumn(pDataRow, m_LastRecordKeyColumnToken, m_LastRecordKey);
01128     err = m_mdbPabTable->AddRow(m_mdbEnv, pDataRow);
01129     return NS_OK;
01130   }
01131   else if (!pDataRow)
01132     err = InitLastRecorKey();
01133   else
01134     return NS_ERROR_NOT_AVAILABLE;
01135   return err;
01136 }
01137 
01138 nsresult nsAddrDatabase::InitExistingDB()
01139 {
01140   nsresult err = InitMDBInfo();
01141   if (err == NS_OK)
01142   {
01143     if (!m_mdbStore || !m_mdbEnv)
01144       return NS_ERROR_NULL_POINTER;
01145 
01146     err = m_mdbStore->GetTable(m_mdbEnv, &gAddressBookTableOID, &m_mdbPabTable);
01147     if (NS_SUCCEEDED(err) && m_mdbPabTable)
01148     {
01149       err = GetLastRecordKey();
01150       if (err == NS_ERROR_NOT_AVAILABLE)
01151         CheckAndUpdateRecordKey();
01152       UpdateLowercaseEmailListName();
01153     }
01154   }
01155   return err;
01156 }
01157 
01158 nsresult nsAddrDatabase::CheckAndUpdateRecordKey()
01159 {
01160   if (!m_mdbEnv)
01161     return NS_ERROR_NULL_POINTER;
01162 
01163   nsresult err = NS_OK;
01164   nsIMdbTableRowCursor* rowCursor = nsnull;
01165   nsIMdbRow* findRow = nsnull;
01166   mdb_pos rowPos = 0;
01167 
01168   mdb_err merror = m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
01169 
01170   if (!(merror == NS_OK && rowCursor))
01171     return NS_ERROR_FAILURE;
01172 
01173   nsCOMPtr <nsIMdbRow> pDataRow;
01174   err = GetDataRow(getter_AddRefs(pDataRow));
01175   if (NS_FAILED(err))
01176     InitLastRecorKey();
01177 
01178   do
01179   {  //add key to each card and mailing list row
01180     merror = rowCursor->NextRow(m_mdbEnv, &findRow, &rowPos);
01181     if (merror == NS_OK && findRow)
01182     {
01183       mdbOid rowOid;
01184 
01185       if (findRow->GetOid(GetEnv(), &rowOid) == NS_OK)
01186       {
01187         if (!IsDataRowScopeToken(rowOid.mOid_Scope))
01188         {
01189           m_LastRecordKey++;
01190           err = AddIntColumn(findRow, m_RecordKeyColumnToken, m_LastRecordKey);
01191         }
01192       }
01193     }
01194   } while (findRow);
01195 
01196   UpdateLastRecordKey();
01197   Commit(nsAddrDBCommitType::kLargeCommit);
01198   return NS_OK;
01199 }
01200 
01201 nsresult nsAddrDatabase::UpdateLowercaseEmailListName()
01202 {
01203   if (!m_mdbEnv)
01204     return NS_ERROR_NULL_POINTER;
01205 
01206   nsresult err = NS_OK;
01207   nsIMdbTableRowCursor* rowCursor = nsnull;
01208   nsIMdbRow* findRow = nsnull;
01209   mdb_pos    rowPos = 0;
01210   PRBool commitRequired = PR_FALSE;
01211   
01212   mdb_err merror = m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
01213   
01214   if (!(merror == NS_OK && rowCursor))
01215     return NS_ERROR_FAILURE;
01216   
01217   do
01218   {   //add lowercase primary email to each card and mailing list row
01219     merror = rowCursor->NextRow(m_mdbEnv, &findRow, &rowPos);
01220     if (merror == NS_OK && findRow)
01221     {
01222       mdbOid rowOid;
01223       
01224       if (findRow->GetOid(GetEnv(), &rowOid) == NS_OK)
01225       {
01226         nsAutoString tempString;
01227         if (IsCardRowScopeToken(rowOid.mOid_Scope))
01228         {
01229           err = GetStringColumn(findRow, m_LowerPriEmailColumnToken, tempString);
01230           if (NS_SUCCEEDED(err))
01231             break;
01232           
01233           err = ConvertAndAddLowercaseColumn(findRow, m_PriEmailColumnToken, 
01234             m_LowerPriEmailColumnToken);
01235           commitRequired = PR_TRUE;
01236         }
01237         else if (IsListRowScopeToken(rowOid.mOid_Scope))
01238         {
01239           err = GetStringColumn(findRow, m_LowerListNameColumnToken, tempString);
01240           if (NS_SUCCEEDED(err))
01241             break;
01242           
01243           err = ConvertAndAddLowercaseColumn(findRow, m_ListNameColumnToken, 
01244             m_LowerListNameColumnToken);
01245           commitRequired = PR_TRUE;
01246         }
01247       }
01248       findRow->Release();
01249     }
01250   } while (findRow);
01251 
01252   if (findRow)
01253     findRow->Release();
01254   rowCursor->Release();
01255   if (commitRequired)
01256     Commit(nsAddrDBCommitType::kLargeCommit);
01257   return NS_OK;
01258 }
01259 
01260 /*  
01261 We store UTF8 strings in the database.  We need to convert the UTF8 
01262 string into unicode string, then convert to lower case.  Before storing 
01263 back into the database,  we need to convert the lowercase unicode string 
01264 into UTF8 string.
01265 */
01266 nsresult nsAddrDatabase::ConvertAndAddLowercaseColumn
01267 (nsIMdbRow * row, mdb_token fromCol, mdb_token toCol)
01268 {
01269     nsAutoString colString;
01270 
01271     nsresult rv = GetStringColumn(row, fromCol, colString);
01272     if (!colString.IsEmpty())
01273     {
01274         rv = AddLowercaseColumn(row, toCol, NS_ConvertUCS2toUTF8(colString).get());
01275     }
01276     return rv;
01277 }
01278  
01279 // Change the unicode string to lowercase, then convert to UTF8 string to store in db
01280 nsresult nsAddrDatabase::AddUnicodeToColumn(nsIMdbRow * row, mdb_token aColToken, mdb_token aLowerCaseColToken, const PRUnichar* aUnicodeStr)
01281 {
01282   nsresult rv = AddCharStringColumn(row, aColToken, NS_ConvertUCS2toUTF8(aUnicodeStr).get());
01283   NS_ENSURE_SUCCESS(rv,rv);
01284 
01285   rv = AddLowercaseColumn(row, aLowerCaseColToken, NS_ConvertUCS2toUTF8(aUnicodeStr).get());
01286   NS_ENSURE_SUCCESS(rv,rv);
01287   return rv;
01288 }
01289 
01290 // initialize the various tokens and tables in our db's env
01291 nsresult nsAddrDatabase::InitMDBInfo()
01292 {
01293   nsresult err = NS_OK;
01294 
01295   if (!m_mdbTokensInitialized && m_mdbStore && m_mdbEnv)
01296   {
01297     m_mdbTokensInitialized = PR_TRUE;
01298     err = m_mdbStore->StringToToken(m_mdbEnv, kCardRowScope, &m_CardRowScopeToken); 
01299     err = m_mdbStore->StringToToken(m_mdbEnv, kListRowScope, &m_ListRowScopeToken); 
01300     err = m_mdbStore->StringToToken(m_mdbEnv, kDataRowScope, &m_DataRowScopeToken); 
01301     gAddressBookTableOID.mOid_Scope = m_CardRowScopeToken;
01302     gAddressBookTableOID.mOid_Id = ID_PAB_TABLE;
01303     if (NS_SUCCEEDED(err))
01304     { 
01305       m_mdbStore->StringToToken(m_mdbEnv,  kFirstNameColumn, &m_FirstNameColumnToken);
01306       m_mdbStore->StringToToken(m_mdbEnv,  kLastNameColumn, &m_LastNameColumnToken);
01307       m_mdbStore->StringToToken(m_mdbEnv,  kPhoneticFirstNameColumn, &m_PhoneticFirstNameColumnToken);
01308       m_mdbStore->StringToToken(m_mdbEnv,  kPhoneticLastNameColumn, &m_PhoneticLastNameColumnToken);
01309       m_mdbStore->StringToToken(m_mdbEnv,  kDisplayNameColumn, &m_DisplayNameColumnToken);
01310       m_mdbStore->StringToToken(m_mdbEnv,  kNicknameColumn, &m_NickNameColumnToken);
01311       m_mdbStore->StringToToken(m_mdbEnv,  kPriEmailColumn, &m_PriEmailColumnToken);
01312       m_mdbStore->StringToToken(m_mdbEnv,  kLowerPriEmailColumn, &m_LowerPriEmailColumnToken);
01313       m_mdbStore->StringToToken(m_mdbEnv,  k2ndEmailColumn, &m_2ndEmailColumnToken);
01314       m_mdbStore->StringToToken(m_mdbEnv,  kDefaultEmailColumn, &m_DefaultEmailColumnToken);
01315       m_mdbStore->StringToToken(m_mdbEnv,  kCardTypeColumn, &m_CardTypeColumnToken);
01316       m_mdbStore->StringToToken(m_mdbEnv,  kPreferMailFormatColumn, &m_MailFormatColumnToken);
01317       m_mdbStore->StringToToken(m_mdbEnv,  kPopularityIndexColumn, &m_PopularityIndexColumnToken);
01318       m_mdbStore->StringToToken(m_mdbEnv,  kAllowRemoteContentColumn, &m_AllowRemoteContentColumnToken);
01319       m_mdbStore->StringToToken(m_mdbEnv,  kWorkPhoneColumn, &m_WorkPhoneColumnToken);
01320       m_mdbStore->StringToToken(m_mdbEnv,  kHomePhoneColumn, &m_HomePhoneColumnToken);
01321       m_mdbStore->StringToToken(m_mdbEnv,  kFaxColumn, &m_FaxColumnToken);
01322       m_mdbStore->StringToToken(m_mdbEnv,  kPagerColumn, &m_PagerColumnToken);
01323       m_mdbStore->StringToToken(m_mdbEnv,  kCellularColumn, &m_CellularColumnToken);
01324       m_mdbStore->StringToToken(m_mdbEnv,  kWorkPhoneTypeColumn, &m_WorkPhoneTypeColumnToken);
01325       m_mdbStore->StringToToken(m_mdbEnv,  kHomePhoneTypeColumn, &m_HomePhoneTypeColumnToken);
01326       m_mdbStore->StringToToken(m_mdbEnv,  kFaxTypeColumn, &m_FaxTypeColumnToken);
01327       m_mdbStore->StringToToken(m_mdbEnv,  kPagerTypeColumn, &m_PagerTypeColumnToken);
01328       m_mdbStore->StringToToken(m_mdbEnv,  kCellularTypeColumn, &m_CellularTypeColumnToken);
01329       m_mdbStore->StringToToken(m_mdbEnv,  kHomeAddressColumn, &m_HomeAddressColumnToken);
01330       m_mdbStore->StringToToken(m_mdbEnv,  kHomeAddress2Column, &m_HomeAddress2ColumnToken);
01331       m_mdbStore->StringToToken(m_mdbEnv,  kHomeCityColumn, &m_HomeCityColumnToken);
01332       m_mdbStore->StringToToken(m_mdbEnv,  kHomeStateColumn, &m_HomeStateColumnToken);
01333       m_mdbStore->StringToToken(m_mdbEnv,  kHomeZipCodeColumn, &m_HomeZipCodeColumnToken);
01334       m_mdbStore->StringToToken(m_mdbEnv,  kHomeCountryColumn, &m_HomeCountryColumnToken);
01335       m_mdbStore->StringToToken(m_mdbEnv,  kWorkAddressColumn, &m_WorkAddressColumnToken);
01336       m_mdbStore->StringToToken(m_mdbEnv,  kWorkAddress2Column, &m_WorkAddress2ColumnToken);
01337       m_mdbStore->StringToToken(m_mdbEnv,  kWorkCityColumn, &m_WorkCityColumnToken);
01338       m_mdbStore->StringToToken(m_mdbEnv,  kWorkStateColumn, &m_WorkStateColumnToken);
01339       m_mdbStore->StringToToken(m_mdbEnv,  kWorkZipCodeColumn, &m_WorkZipCodeColumnToken);
01340       m_mdbStore->StringToToken(m_mdbEnv,  kWorkCountryColumn, &m_WorkCountryColumnToken);
01341       m_mdbStore->StringToToken(m_mdbEnv,  kJobTitleColumn, &m_JobTitleColumnToken);
01342       m_mdbStore->StringToToken(m_mdbEnv,  kDepartmentColumn, &m_DepartmentColumnToken);
01343       m_mdbStore->StringToToken(m_mdbEnv,  kCompanyColumn, &m_CompanyColumnToken);
01344       m_mdbStore->StringToToken(m_mdbEnv,  kAimScreenNameColumn, &m_AimScreenNameColumnToken);
01345       m_mdbStore->StringToToken(m_mdbEnv,  kAnniversaryYearColumn, &m_AnniversaryYearColumnToken);
01346       m_mdbStore->StringToToken(m_mdbEnv,  kAnniversaryMonthColumn, &m_AnniversaryMonthColumnToken);
01347       m_mdbStore->StringToToken(m_mdbEnv,  kAnniversaryDayColumn, &m_AnniversaryDayColumnToken);
01348       m_mdbStore->StringToToken(m_mdbEnv,  kSpouseNameColumn, &m_SpouseNameColumnToken);
01349       m_mdbStore->StringToToken(m_mdbEnv,  kFamilyNameColumn, &m_FamilyNameColumnToken);
01350       m_mdbStore->StringToToken(m_mdbEnv,  kDefaultAddressColumn, &m_DefaultAddressColumnToken);
01351       m_mdbStore->StringToToken(m_mdbEnv,  kCategoryColumn, &m_CategoryColumnToken);
01352       m_mdbStore->StringToToken(m_mdbEnv,  kWebPage1Column, &m_WebPage1ColumnToken);
01353       m_mdbStore->StringToToken(m_mdbEnv,  kWebPage2Column, &m_WebPage2ColumnToken);
01354       m_mdbStore->StringToToken(m_mdbEnv,  kBirthYearColumn, &m_BirthYearColumnToken);
01355       m_mdbStore->StringToToken(m_mdbEnv,  kBirthMonthColumn, &m_BirthMonthColumnToken);
01356       m_mdbStore->StringToToken(m_mdbEnv,  kBirthDayColumn, &m_BirthDayColumnToken);
01357       m_mdbStore->StringToToken(m_mdbEnv,  kCustom1Column, &m_Custom1ColumnToken);
01358       m_mdbStore->StringToToken(m_mdbEnv,  kCustom2Column, &m_Custom2ColumnToken);
01359       m_mdbStore->StringToToken(m_mdbEnv,  kCustom3Column, &m_Custom3ColumnToken);
01360       m_mdbStore->StringToToken(m_mdbEnv,  kCustom4Column, &m_Custom4ColumnToken);
01361       m_mdbStore->StringToToken(m_mdbEnv,  kNotesColumn, &m_NotesColumnToken);
01362       m_mdbStore->StringToToken(m_mdbEnv,  kLastModifiedDateColumn, &m_LastModDateColumnToken);
01363       m_mdbStore->StringToToken(m_mdbEnv,  kRecordKeyColumn, &m_RecordKeyColumnToken);
01364       m_mdbStore->StringToToken(m_mdbEnv,  kAddressCharSetColumn, &m_AddressCharSetColumnToken);
01365       m_mdbStore->StringToToken(m_mdbEnv,  kLastRecordKeyColumn, &m_LastRecordKeyColumnToken);
01366 
01367       err = m_mdbStore->StringToToken(m_mdbEnv, kPabTableKind, &m_PabTableKind); 
01368 
01369       m_mdbStore->StringToToken(m_mdbEnv,  kMailListName, &m_ListNameColumnToken);
01370       m_mdbStore->StringToToken(m_mdbEnv,  kMailListNickName, &m_ListNickNameColumnToken);
01371       m_mdbStore->StringToToken(m_mdbEnv,  kMailListDescription, &m_ListDescriptionColumnToken);
01372       m_mdbStore->StringToToken(m_mdbEnv,  kMailListTotalAddresses, &m_ListTotalColumnToken);
01373       m_mdbStore->StringToToken(m_mdbEnv,  kLowerListNameColumn, &m_LowerListNameColumnToken);
01374       m_mdbStore->StringToToken(m_mdbEnv,  kDeletedCardsTableKind, &m_DeletedCardsTableKind);
01375     }
01376   }
01377   return err;
01378 }
01379 
01381 
01382 nsresult nsAddrDatabase::AddRecordKeyColumnToRow(nsIMdbRow *pRow)
01383 {
01384   if (pRow && m_mdbEnv)
01385   {
01386     m_LastRecordKey++;
01387     nsresult err = AddIntColumn(pRow, m_RecordKeyColumnToken, m_LastRecordKey);
01388     NS_ENSURE_SUCCESS(err, err);
01389 
01390     err = m_mdbPabTable->AddRow(m_mdbEnv, pRow);
01391     UpdateLastRecordKey();
01392     return err;
01393   }
01394   return NS_ERROR_NULL_POINTER;
01395 }
01396 
01397 nsresult nsAddrDatabase::AddAttributeColumnsToRow(nsIAbCard *card, nsIMdbRow *cardRow)
01398 {
01399   nsresult err = NS_OK;
01400   
01401   if ((!card && !cardRow) || !m_mdbEnv)
01402     return NS_ERROR_NULL_POINTER;
01403   
01404   mdbOid rowOid, tableOid;
01405   m_mdbPabTable->GetOid(m_mdbEnv, &tableOid);
01406   cardRow->GetOid(m_mdbEnv, &rowOid);
01407   
01408   nsCOMPtr<nsIAbMDBCard> dbcard(do_QueryInterface(card, &err));
01409   if(NS_SUCCEEDED(err) && dbcard)
01410   {
01411     dbcard->SetDbTableID(tableOid.mOid_Id);
01412     dbcard->SetDbRowID(rowOid.mOid_Id);
01413   }
01414   // add the row to the singleton table.
01415   if (card && cardRow)
01416   {
01417     nsXPIDLString unicodeStr;
01418     card->GetFirstName(getter_Copies(unicodeStr));
01419     AddFirstName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01420     
01421     card->GetLastName(getter_Copies(unicodeStr));
01422     AddLastName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01423     
01424     card->GetPhoneticFirstName(getter_Copies(unicodeStr));
01425     AddPhoneticFirstName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01426     
01427     card->GetPhoneticLastName(getter_Copies(unicodeStr));
01428     AddPhoneticLastName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01429     
01430     card->GetDisplayName(getter_Copies(unicodeStr));
01431     AddDisplayName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01432     
01433     card->GetNickName(getter_Copies(unicodeStr));
01434     AddNickName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01435     
01436     card->GetPrimaryEmail(getter_Copies(unicodeStr));
01437     if (unicodeStr)
01438       AddUnicodeToColumn(cardRow, m_PriEmailColumnToken, m_LowerPriEmailColumnToken, unicodeStr);
01439 
01440     card->GetSecondEmail(getter_Copies(unicodeStr));
01441     Add2ndEmail(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01442     
01443     card->GetDefaultEmail(getter_Copies(unicodeStr));
01444     AddDefaultEmail(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01445     
01446     card->GetCardType(getter_Copies(unicodeStr));
01447     AddCardType(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01448     
01449     PRUint32 format = nsIAbPreferMailFormat::unknown;
01450     card->GetPreferMailFormat(&format);
01451     AddPreferMailFormat(cardRow, format);
01452 
01453     PRUint32 popularityIndex = 0;
01454     card->GetPopularityIndex(&popularityIndex);
01455     AddPopularityIndex(cardRow, popularityIndex);
01456 
01457     PRBool allowRemoteContent = PR_FALSE;
01458     card->GetAllowRemoteContent(&allowRemoteContent);
01459     AddAllowRemoteContent(cardRow, allowRemoteContent);
01460     
01461     card->GetWorkPhone(getter_Copies(unicodeStr));
01462     AddWorkPhone(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01463     
01464     card->GetHomePhone(getter_Copies(unicodeStr));
01465     AddHomePhone(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01466     
01467     card->GetFaxNumber(getter_Copies(unicodeStr));
01468     AddFaxNumber(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01469     
01470     card->GetPagerNumber(getter_Copies(unicodeStr));
01471     AddPagerNumber(cardRow,NS_ConvertUCS2toUTF8(unicodeStr).get());
01472     
01473     card->GetCellularNumber(getter_Copies(unicodeStr));
01474     AddCellularNumber(cardRow,NS_ConvertUCS2toUTF8(unicodeStr).get());
01475     
01476     card->GetWorkPhoneType(getter_Copies(unicodeStr));
01477     AddWorkPhoneType(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01478     
01479     card->GetHomePhoneType(getter_Copies(unicodeStr));
01480     AddHomePhoneType(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01481     
01482     card->GetFaxNumberType(getter_Copies(unicodeStr));
01483     AddFaxNumberType(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01484     
01485     card->GetPagerNumberType(getter_Copies(unicodeStr));
01486     AddPagerNumberType(cardRow,NS_ConvertUCS2toUTF8(unicodeStr).get());
01487     
01488     card->GetCellularNumberType(getter_Copies(unicodeStr));
01489     AddCellularNumberType(cardRow,NS_ConvertUCS2toUTF8(unicodeStr).get());
01490     
01491     card->GetHomeAddress(getter_Copies(unicodeStr));
01492     AddHomeAddress(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01493     
01494     card->GetHomeAddress2(getter_Copies(unicodeStr)); 
01495     AddHomeAddress2(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01496     
01497     card->GetHomeCity(getter_Copies(unicodeStr)); 
01498     AddHomeCity(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01499     
01500     card->GetHomeState(getter_Copies(unicodeStr)); 
01501     AddHomeState(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01502     
01503     card->GetHomeZipCode(getter_Copies(unicodeStr)); 
01504     AddHomeZipCode(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01505     
01506     card->GetHomeCountry(getter_Copies(unicodeStr)); 
01507     AddHomeCountry(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01508     
01509     card->GetWorkAddress(getter_Copies(unicodeStr));  
01510     AddWorkAddress(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01511       
01512     card->GetWorkAddress2(getter_Copies(unicodeStr)); 
01513     AddWorkAddress2(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01514       
01515     card->GetWorkCity(getter_Copies(unicodeStr)); 
01516     AddWorkCity(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01517       
01518     card->GetWorkState(getter_Copies(unicodeStr)); 
01519     AddWorkState(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01520      
01521     card->GetWorkZipCode(getter_Copies(unicodeStr)); 
01522     AddWorkZipCode(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01523      
01524     card->GetWorkCountry(getter_Copies(unicodeStr)); 
01525     AddWorkCountry(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01526       
01527     card->GetJobTitle(getter_Copies(unicodeStr)); 
01528     AddJobTitle(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01529       
01530     card->GetDepartment(getter_Copies(unicodeStr)); 
01531     AddDepartment(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01532       
01533     card->GetCompany(getter_Copies(unicodeStr)); 
01534     AddCompany(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01535      
01536     // AimScreenName
01537     card->GetAimScreenName(getter_Copies(unicodeStr)); 
01538     AddAimScreenName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01539      
01540     card->GetAnniversaryYear(getter_Copies(unicodeStr)); 
01541     AddAnniversaryYear(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01542   
01543     card->GetAnniversaryMonth(getter_Copies(unicodeStr)); 
01544     AddAnniversaryMonth(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01545   
01546     card->GetAnniversaryDay(getter_Copies(unicodeStr)); 
01547     AddAnniversaryDay(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01548 
01549     card->GetSpouseName(getter_Copies(unicodeStr)); 
01550     AddSpouseName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01551 
01552     card->GetFamilyName(getter_Copies(unicodeStr)); 
01553     AddFamilyName(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01554 
01555     card->GetDefaultAddress(getter_Copies(unicodeStr)); 
01556     AddDefaultAddress(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01557 
01558     card->GetCategory(getter_Copies(unicodeStr)); 
01559     AddCategory(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01560 
01561     card->GetWebPage1(getter_Copies(unicodeStr)); 
01562     AddWebPage1(cardRow,NS_ConvertUCS2toUTF8(unicodeStr).get());
01563      
01564     card->GetWebPage2(getter_Copies(unicodeStr)); 
01565     AddWebPage2(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01566      
01567     card->GetBirthYear(getter_Copies(unicodeStr)); 
01568     AddBirthYear(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01569       
01570     card->GetBirthMonth(getter_Copies(unicodeStr)); 
01571     AddBirthMonth(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01572       
01573     card->GetBirthDay(getter_Copies(unicodeStr)); 
01574     AddBirthDay(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01575       
01576     card->GetCustom1(getter_Copies(unicodeStr)); 
01577     AddCustom1(cardRow,NS_ConvertUCS2toUTF8(unicodeStr).get());
01578      
01579     card->GetCustom2(getter_Copies(unicodeStr)); 
01580     AddCustom2(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01581       
01582     card->GetCustom3(getter_Copies(unicodeStr)); 
01583     AddCustom3(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01584       
01585     card->GetCustom4(getter_Copies(unicodeStr)); 
01586     AddCustom4(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01587     
01588     card->GetNotes(getter_Copies(unicodeStr)); 
01589     AddNotes(cardRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01590       
01591     PRUint32 lastModDate = 0;
01592     card->GetLastModifiedDate(&lastModDate);
01593     AddIntColumn(cardRow, m_LastModDateColumnToken, lastModDate);
01594       
01595   }
01596   return NS_OK;
01597 }
01598 
01599 NS_IMETHODIMP nsAddrDatabase::CreateNewCardAndAddToDB(nsIAbCard *newCard, PRBool notify /* = FALSE */)
01600 {
01601   nsCOMPtr <nsIMdbRow> cardRow;
01602   
01603   if (!newCard || !m_mdbPabTable || !m_mdbEnv)
01604     return NS_ERROR_NULL_POINTER;
01605   
01606   nsresult rv = GetNewRow(getter_AddRefs(cardRow));
01607   if (NS_SUCCEEDED(rv) && cardRow)
01608   {
01609     AddAttributeColumnsToRow(newCard, cardRow);
01610     AddRecordKeyColumnToRow(cardRow);
01611     
01612     // we need to do this for dnd
01613     PRUint32 key = 0;
01614     rv = GetIntColumn(cardRow, m_RecordKeyColumnToken, &key, 0);
01615     if (NS_SUCCEEDED(rv)) {
01616       nsCOMPtr<nsIAbMDBCard> dbnewCard = do_QueryInterface(newCard);
01617       if (dbnewCard)
01618         dbnewCard->SetKey(key);
01619     }
01620     
01621     mdb_err merror = m_mdbPabTable->AddRow(m_mdbEnv, cardRow);
01622     if (merror != NS_OK) return NS_ERROR_FAILURE;
01623     
01624   }
01625   else
01626     return rv;
01627   
01628   //  do notification
01629   if (notify)
01630   {
01631     NotifyCardEntryChange(AB_NotifyInserted, newCard);
01632   }
01633   return rv;
01634 }
01635 
01636 NS_IMETHODIMP nsAddrDatabase::CreateNewCardAndAddToDBWithKey(nsIAbCard *newCard, PRBool notify /* = FALSE */, PRUint32 *key)
01637 {
01638   nsresult    err = NS_OK;
01639   *key = 0;
01640 
01641   err = CreateNewCardAndAddToDB(newCard, notify);
01642   if (NS_SUCCEEDED(err))
01643     *key = m_LastRecordKey;
01644 
01645   return err;
01646 }
01647 
01648 NS_IMETHODIMP nsAddrDatabase::CreateNewListCardAndAddToDB(nsIAbDirectory *aList, PRUint32 listRowID, nsIAbCard *newCard, PRBool notify /* = FALSE */)
01649 {
01650   if (!newCard || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
01651     return NS_ERROR_NULL_POINTER;
01652 
01653   nsIMdbRow* pListRow = nsnull;
01654   mdbOid listRowOid;
01655   listRowOid.mOid_Scope = m_ListRowScopeToken;
01656   listRowOid.mOid_Id = listRowID;
01657   nsresult rv = m_mdbStore->GetRow(m_mdbEnv, &listRowOid, &pListRow);
01658   NS_ENSURE_SUCCESS(rv,rv);
01659 
01660   if (!pListRow)
01661     return NS_OK;
01662 
01663   nsCOMPtr <nsISupportsArray> addressList;
01664   rv = aList->GetAddressLists(getter_AddRefs(addressList));
01665   NS_ENSURE_SUCCESS(rv,rv);
01666 
01667   PRUint32 count;
01668     addressList->Count(&count);
01669 
01670   nsXPIDLString newEmail;
01671   rv = newCard->GetPrimaryEmail(getter_Copies(newEmail));
01672   NS_ENSURE_SUCCESS(rv,rv);
01673 
01674     PRUint32 i;
01675   for (i = 0; i < count; i++) {
01676     nsCOMPtr<nsIAbCard> currentCard = do_QueryElementAt(addressList, i, &rv);
01677     NS_ENSURE_SUCCESS(rv,rv);
01678 
01679     PRBool equals;
01680     rv = newCard->Equals(currentCard, &equals);
01681     NS_ENSURE_SUCCESS(rv,rv);
01682 
01683     if (equals) {
01684       // card is already in list, bail out.
01685       // this can happen when dropping a card on a mailing list from the directory that contains the mailing list
01686       return NS_OK;
01687     }
01688 
01689     nsXPIDLString currentEmail;
01690     rv = currentCard->GetPrimaryEmail(getter_Copies(currentEmail));
01691     NS_ENSURE_SUCCESS(rv,rv);
01692 
01693     if (!nsCRT::strcmp(newEmail.get(), currentEmail.get())) {
01694       // card is already in list, bail out
01695       // this can happen when dropping a card on a mailing list from another directory (not the one that contains the mailing list
01696       // or if you have multiple cards on a directory, with the same primary email address.
01697       return NS_OK;
01698     }
01699   }  
01700     
01701   // start from 1
01702   PRUint32 totalAddress = GetListAddressTotal(pListRow) + 1;
01703   SetListAddressTotal(pListRow, totalAddress);
01704   nsCOMPtr<nsIAbCard> pNewCard;
01705   rv = AddListCardColumnsToRow(newCard, pListRow, totalAddress, getter_AddRefs(pNewCard), PR_TRUE /* aInMailingList */);
01706   NS_ENSURE_SUCCESS(rv,rv);
01707 
01708   addressList->AppendElement(newCard);
01709 
01710   if (notify)
01711     NotifyCardEntryChange(AB_NotifyInserted, newCard); 
01712 
01713     return rv;
01714 }
01715 
01716 NS_IMETHODIMP nsAddrDatabase::AddListCardColumnsToRow
01717 (nsIAbCard *pCard, nsIMdbRow *pListRow, PRUint32 pos, nsIAbCard** pNewCard, PRBool aInMailingList)
01718 {
01719   if (!pCard || !pListRow || !m_mdbStore || !m_mdbEnv)
01720     return NS_ERROR_NULL_POINTER;
01721   
01722   nsresult    err = NS_OK;
01723   nsXPIDLString email;
01724   pCard->GetPrimaryEmail(getter_Copies(email));
01725   if (email)
01726   {
01727     nsIMdbRow    *pCardRow = nsnull;
01728     // Please DO NOT change the 3rd param of GetRowFromAttribute() call to 
01729     // PR_TRUE (ie, case insensitive) without reading bugs #128535 and #121478.
01730     err = GetRowFromAttribute(kPriEmailColumn, NS_ConvertUCS2toUTF8(email).get(), PR_FALSE /* retain case */, &pCardRow);
01731     PRBool cardWasAdded = PR_FALSE;
01732     if (NS_FAILED(err) || !pCardRow)
01733     {
01734       //New Email, then add a new row with this email
01735       err  = GetNewRow(&pCardRow);
01736       
01737       if (NS_SUCCEEDED(err) && pCardRow)
01738       {
01739         AddPrimaryEmail(pCardRow, NS_ConvertUCS2toUTF8(email).get());
01740         err = m_mdbPabTable->AddRow(m_mdbEnv, pCardRow);
01741         // Create a key for this row as well.
01742         if (NS_SUCCEEDED(err))
01743           AddRecordKeyColumnToRow(pCardRow);
01744       }
01745       
01746       cardWasAdded = PR_TRUE;
01747     }
01748     
01749     NS_ENSURE_TRUE(pCardRow, NS_ERROR_NULL_POINTER);
01750     
01751     nsXPIDLString name;
01752     pCard->GetDisplayName(getter_Copies(name));
01753     if (!name.IsEmpty()) {
01754       AddDisplayName(pCardRow, NS_ConvertUCS2toUTF8(name).get());
01755       err = m_mdbPabTable->AddRow(m_mdbEnv, pCardRow);
01756     }
01757 
01758     nsCOMPtr<nsIAbCard> newCard;
01759     CreateABCard(pCardRow, 0, getter_AddRefs(newCard));
01760     NS_IF_ADDREF(*pNewCard = newCard);
01761     
01762     if (cardWasAdded) {
01763       NotifyCardEntryChange(AB_NotifyInserted, newCard);
01764     }
01765     else if (!aInMailingList) {
01766       NotifyCardEntryChange(AB_NotifyInserted, pCard);
01767     }
01768     else {
01769       NotifyCardEntryChange(AB_NotifyPropertyChanged, pCard);
01770     }
01771     
01772     //add a column with address row id to the list row
01773     mdb_token listAddressColumnToken;
01774     
01775     char columnStr[COLUMN_STR_MAX];
01776     PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
01777     m_mdbStore->StringToToken(m_mdbEnv,  columnStr, &listAddressColumnToken);
01778     
01779     mdbOid outOid;
01780     
01781     if (pCardRow->GetOid(m_mdbEnv, &outOid) == NS_OK)
01782     {
01783       //save address row ID to the list row
01784       err = AddIntColumn(pListRow, listAddressColumnToken, outOid.mOid_Id);
01785     }
01786     NS_RELEASE(pCardRow);
01787 
01788   }
01789   
01790   return NS_OK;
01791 }
01792 
01793 nsresult nsAddrDatabase::AddListAttributeColumnsToRow(nsIAbDirectory *list, nsIMdbRow *listRow)
01794 {
01795     nsresult    err = NS_OK;
01796 
01797     if ((!list && !listRow) || !m_mdbEnv)
01798         return NS_ERROR_NULL_POINTER;
01799 
01800     mdbOid rowOid, tableOid;
01801     m_mdbPabTable->GetOid(m_mdbEnv, &tableOid);
01802     listRow->GetOid(m_mdbEnv, &rowOid);
01803 
01804     nsCOMPtr<nsIAbMDBDirectory> dblist(do_QueryInterface(list,&err));
01805     if (NS_SUCCEEDED(err))
01806         dblist->SetDbRowID(rowOid.mOid_Id);
01807 
01808     // add the row to the singleton table.
01809     if (NS_SUCCEEDED(err) && listRow)
01810     {
01811         nsXPIDLString unicodeStr;
01812 
01813         list->GetDirName(getter_Copies(unicodeStr));
01814         if (unicodeStr)
01815             AddUnicodeToColumn(listRow, m_ListNameColumnToken, m_LowerListNameColumnToken, unicodeStr);
01816 
01817         list->GetListNickName(getter_Copies(unicodeStr));
01818         AddListNickName(listRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01819         
01820         list->GetDescription(getter_Copies(unicodeStr));
01821         AddListDescription(listRow, NS_ConvertUCS2toUTF8(unicodeStr).get());
01822             
01823     // XXX todo, this code has problems if you manually enter duplicate emails.
01824         nsCOMPtr <nsISupportsArray> pAddressLists;
01825         list->GetAddressLists(getter_AddRefs(pAddressLists));
01826         PRUint32 count;
01827         pAddressLists->Count(&count);
01828 
01829       nsXPIDLString email;
01830         PRUint32 i, total;
01831         total = 0;
01832         for (i = 0; i < count; i++)
01833         {
01834             nsCOMPtr<nsIAbCard> pCard(do_QueryElementAt(pAddressLists, i, &err));
01835             
01836             if (NS_FAILED(err))
01837                 continue;
01838 
01839             pCard->GetPrimaryEmail(getter_Copies(email));
01840             PRInt32 emailLength = nsCRT::strlen(email);
01841             if (email && emailLength)
01842                 total++;
01843         }
01844         SetListAddressTotal(listRow, total);
01845 
01846         PRUint32 pos;
01847         for (i = 0; i < count; i++)
01848         {
01849             nsCOMPtr<nsIAbCard> pCard(do_QueryElementAt(pAddressLists, i, &err));
01850             
01851             if (NS_FAILED(err))
01852                 continue;
01853 
01854             PRBool listHasCard = PR_FALSE;
01855             err = list->HasCard(pCard, &listHasCard);
01856 
01857             // start from 1
01858             pos = i + 1;
01859             pCard->GetPrimaryEmail(getter_Copies(email));
01860             PRInt32 emailLength = nsCRT::strlen(email);
01861             if (email && emailLength)
01862             {
01863                 nsCOMPtr<nsIAbCard> pNewCard;
01864                 err = AddListCardColumnsToRow(pCard, listRow, pos, getter_AddRefs(pNewCard), listHasCard);
01865                 if (pNewCard)
01866                     pAddressLists->ReplaceElementAt(pNewCard, i);
01867             }
01868         }
01869     }
01870     return NS_OK;
01871 }
01872 
01873 PRUint32 nsAddrDatabase::GetListAddressTotal(nsIMdbRow* listRow)
01874 {
01875     PRUint32 count = 0;
01876     GetIntColumn(listRow, m_ListTotalColumnToken, &count, 0);
01877     return count;
01878 }
01879 
01880 NS_IMETHODIMP nsAddrDatabase::SetListAddressTotal(nsIMdbRow* aListRow, PRUint32 aTotal)
01881 {
01882     return AddIntColumn(aListRow, m_ListTotalColumnToken, aTotal);
01883 }
01884 
01885 NS_IMETHODIMP nsAddrDatabase::FindRowByCard(nsIAbCard * aCard,nsIMdbRow **aRow)
01886 {
01887     nsXPIDLString primaryEmail;
01888     aCard->GetPrimaryEmail(getter_Copies(primaryEmail));
01889     return GetRowForCharColumn(primaryEmail, m_PriEmailColumnToken, PR_TRUE, aRow);
01890 }
01891 
01892 nsresult nsAddrDatabase::GetAddressRowByPos(nsIMdbRow* listRow, PRUint16 pos, nsIMdbRow** cardRow)
01893 {
01894   if (!m_mdbStore || !listRow || !cardRow || !m_mdbEnv)
01895     return NS_ERROR_NULL_POINTER;
01896 
01897   mdb_token listAddressColumnToken;
01898 
01899   char columnStr[COLUMN_STR_MAX];
01900   PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
01901   m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
01902 
01903   nsAutoString tempString;
01904   mdb_id rowID;
01905   nsresult err = GetIntColumn(listRow, listAddressColumnToken, (PRUint32*)&rowID, 0);
01906   NS_ENSURE_SUCCESS(err, err);
01907 
01908   return GetCardRowByRowID(rowID, cardRow);
01909 }
01910 
01911 NS_IMETHODIMP nsAddrDatabase::CreateMailListAndAddToDB(nsIAbDirectory *newList, PRBool notify /* = FALSE */)
01912 {
01913     nsresult    err = NS_OK;
01914     nsIMdbRow    *listRow;
01915 
01916     if (!newList || !m_mdbPabTable || !m_mdbEnv)
01917         return NS_ERROR_NULL_POINTER;
01918     
01919     err  = GetNewListRow(&listRow);
01920 
01921     if (NS_SUCCEEDED(err) && listRow)
01922     {
01923         AddListAttributeColumnsToRow(newList, listRow);
01924         AddRecordKeyColumnToRow(listRow);
01925         mdb_err merror = m_mdbPabTable->AddRow(m_mdbEnv, listRow);
01926         if (merror != NS_OK) return NS_ERROR_FAILURE;
01927 
01928         nsCOMPtr<nsIAbCard> listCard;
01929         CreateABListCard(listRow, getter_AddRefs(listCard));
01930         NotifyCardEntryChange(AB_NotifyInserted, listCard);
01931 
01932         NS_RELEASE(listRow);
01933         return NS_OK;
01934     }
01935     else 
01936         return NS_ERROR_FAILURE;
01937 
01938 }
01939 
01940 void nsAddrDatabase::DeleteCardFromAllMailLists(mdb_id cardRowID)
01941 {
01942   if (!m_mdbEnv)
01943     return;
01944 
01945     nsCOMPtr <nsIMdbTableRowCursor> rowCursor;
01946     m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
01947 
01948     if (rowCursor)
01949     {
01950         nsCOMPtr <nsIMdbRow> pListRow;
01951         mdb_pos rowPos;
01952         do 
01953         {
01954             mdb_err err = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(pListRow), &rowPos);
01955 
01956             if (err == NS_OK && pListRow)
01957             {
01958                 mdbOid rowOid;
01959 
01960                 if (pListRow->GetOid(m_mdbEnv, &rowOid) == NS_OK)
01961                 {
01962                     if (IsListRowScopeToken(rowOid.mOid_Scope))
01963                         DeleteCardFromListRow(pListRow, cardRowID);
01964                 }
01965             }
01966         } while (pListRow);
01967     }
01968 }
01969 
01970 NS_IMETHODIMP nsAddrDatabase::DeleteCard(nsIAbCard *card, PRBool notify)
01971 {
01972   if (!card || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
01973     return NS_ERROR_NULL_POINTER;
01974 
01975   nsresult err = NS_OK;
01976   PRBool bIsMailList = PR_FALSE;
01977   card->GetIsMailList(&bIsMailList);
01978 
01979   // get the right row
01980   nsIMdbRow* pCardRow = nsnull;
01981   mdbOid rowOid;
01982 
01983   rowOid.mOid_Scope = bIsMailList ? m_ListRowScopeToken : m_CardRowScopeToken;
01984 
01985   nsCOMPtr<nsIAbMDBCard> dbcard(do_QueryInterface(card, &err));
01986   NS_ENSURE_SUCCESS(err, err);
01987 
01988   dbcard->GetDbRowID((PRUint32*)&rowOid.mOid_Id);
01989 
01990   err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, &pCardRow);
01991   NS_ENSURE_SUCCESS(err,err);
01992   if (!pCardRow)
01993     return NS_OK;
01994 
01995   // Add the deleted card to the deletedcards table
01996   nsCOMPtr <nsIMdbRow> cardRow;
01997   AddRowToDeletedCardsTable(card, getter_AddRefs(cardRow));
01998   err = DeleteRow(m_mdbPabTable, pCardRow);
01999   
02000   //delete the person card from all mailing list
02001   if (!bIsMailList)
02002     DeleteCardFromAllMailLists(rowOid.mOid_Id);
02003     
02004   if (NS_SUCCEEDED(err)) {
02005     if (notify) 
02006       NotifyCardEntryChange(AB_NotifyDeleted, card);
02007   }
02008   else
02009     DeleteRowFromDeletedCardsTable(cardRow);
02010 
02011   NS_RELEASE(pCardRow);
02012   return NS_OK;
02013 }
02014 
02015 nsresult nsAddrDatabase::DeleteCardFromListRow(nsIMdbRow* pListRow, mdb_id cardRowID)
02016 {
02017   NS_ENSURE_ARG_POINTER(pListRow);
02018   if (!m_mdbStore || !m_mdbEnv)
02019     return NS_ERROR_NULL_POINTER;
02020 
02021   nsresult err = NS_OK;
02022 
02023   PRUint32 totalAddress = GetListAddressTotal(pListRow);
02024 
02025   PRUint32 pos;
02026   for (pos = 1; pos <= totalAddress; pos++)
02027   {
02028     mdb_token listAddressColumnToken;
02029     mdb_id rowID;
02030 
02031     char columnStr[COLUMN_STR_MAX];
02032     PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
02033     m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
02034 
02035     err = GetIntColumn(pListRow, listAddressColumnToken, (PRUint32*)&rowID, 0);
02036 
02037     if (cardRowID == rowID)
02038     {
02039       if (pos == totalAddress)
02040         err = pListRow->CutColumn(m_mdbEnv, listAddressColumnToken);
02041       else
02042       {
02043         //replace the deleted one with the last one and delete the last one
02044         mdb_id lastRowID;
02045         mdb_token lastAddressColumnToken;
02046         PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, totalAddress);
02047         m_mdbStore->StringToToken(m_mdbEnv, columnStr, &lastAddressColumnToken);
02048 
02049         err = GetIntColumn(pListRow, lastAddressColumnToken, (PRUint32*)&lastRowID, 0);
02050         NS_ENSURE_SUCCESS(err, err);
02051 
02052         err = AddIntColumn(pListRow, listAddressColumnToken, lastRowID);
02053         NS_ENSURE_SUCCESS(err, err);
02054 
02055         err = pListRow->CutColumn(m_mdbEnv, lastAddressColumnToken);
02056         NS_ENSURE_SUCCESS(err, err);
02057       }
02058 
02059       // Reset total count after the card has been deleted.
02060       SetListAddressTotal(pListRow, totalAddress-1);
02061       break;
02062     }
02063   }
02064   return NS_OK;
02065 }
02066 
02067 NS_IMETHODIMP nsAddrDatabase::DeleteCardFromMailList(nsIAbDirectory *mailList, nsIAbCard *card, PRBool aNotify)
02068 {
02069   if (!card || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
02070     return NS_ERROR_NULL_POINTER;
02071 
02072   nsresult err = NS_OK;
02073 
02074   // get the right row
02075   nsIMdbRow* pListRow = nsnull;
02076   mdbOid listRowOid;
02077   listRowOid.mOid_Scope = m_ListRowScopeToken;
02078 
02079   nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList,&err));
02080   NS_ENSURE_SUCCESS(err, err);
02081 
02082   dbmailList->GetDbRowID((PRUint32*)&listRowOid.mOid_Id);
02083 
02084   err = m_mdbStore->GetRow(m_mdbEnv, &listRowOid, &pListRow);
02085   NS_ENSURE_SUCCESS(err,err);
02086   if (!pListRow)
02087     return NS_OK;
02088 
02089   PRUint32 cardRowID;
02090     
02091   nsCOMPtr<nsIAbMDBCard> dbcard(do_QueryInterface(card, &err));
02092   if(NS_FAILED(err) || !dbcard)
02093     return NS_ERROR_NULL_POINTER;
02094   dbcard->GetDbRowID(&cardRowID);
02095     
02096   err = DeleteCardFromListRow(pListRow, cardRowID);
02097   if (NS_SUCCEEDED(err) && aNotify) {            
02098     NotifyCardEntryChange(AB_NotifyDeleted, card);
02099   }
02100   NS_RELEASE(pListRow);
02101   return NS_OK;
02102 }
02103 
02104 NS_IMETHODIMP nsAddrDatabase::SetCardValue(nsIAbCard *card, const char *name, const PRUnichar *value, PRBool notify)
02105 {
02106   NS_ENSURE_ARG_POINTER(card);
02107   NS_ENSURE_ARG_POINTER(name);
02108   NS_ENSURE_ARG_POINTER(value);
02109   if (!m_mdbStore || !m_mdbEnv)
02110     return NS_ERROR_NULL_POINTER;
02111 
02112   nsresult rv = NS_OK;
02113   
02114   nsCOMPtr <nsIMdbRow> cardRow;
02115   mdbOid rowOid;
02116   rowOid.mOid_Scope = m_CardRowScopeToken;
02117   
02118   // XXX todo
02119   // it might be that the caller always has a nsIAbMDBCard
02120   nsCOMPtr<nsIAbMDBCard> dbcard = do_QueryInterface(card, &rv);
02121   NS_ENSURE_SUCCESS(rv, rv);
02122   dbcard->GetDbRowID((PRUint32*)&rowOid.mOid_Id);
02123   
02124   rv = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(cardRow));
02125   NS_ENSURE_SUCCESS(rv, rv);
02126   
02127   if (!cardRow)
02128     return NS_OK;
02129     
02130   mdb_token token;
02131   rv = m_mdbStore->StringToToken(m_mdbEnv, name, &token);
02132   NS_ENSURE_SUCCESS(rv, rv);
02133   
02134   rv = AddCharStringColumn(cardRow, token, NS_ConvertUCS2toUTF8(value).get());
02135   return rv;
02136 }
02137 
02138 NS_IMETHODIMP nsAddrDatabase::GetCardValue(nsIAbCard *card, const char *name, PRUnichar **value)
02139 {
02140   if (!m_mdbStore || !card || !name || !value || !m_mdbEnv)
02141     return NS_ERROR_NULL_POINTER;
02142 
02143   nsresult rv = NS_OK;
02144   
02145   nsCOMPtr <nsIMdbRow> cardRow;
02146   mdbOid rowOid;
02147   rowOid.mOid_Scope = m_CardRowScopeToken;
02148   
02149   // XXX todo
02150   // it might be that the caller always has a nsIAbMDBCard
02151   nsCOMPtr<nsIAbMDBCard> dbcard = do_QueryInterface(card, &rv);
02152   NS_ENSURE_SUCCESS(rv, rv);
02153   dbcard->GetDbRowID((PRUint32*)&rowOid.mOid_Id);
02154   
02155   rv = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(cardRow));
02156   NS_ENSURE_SUCCESS(rv, rv);
02157   
02158   if (!cardRow) {
02159     *value = nsnull;
02160     // this can happen when adding cards when editing a mailing list
02161     return NS_OK;
02162   }
02163   
02164   mdb_token token;
02165   m_mdbStore->StringToToken(m_mdbEnv, name, &token);
02166   
02167   // XXX fix me
02168   // avoid extra copying and allocations (did dmb already do this on the trunk?)
02169   nsAutoString tempString;
02170   rv = GetStringColumn(cardRow, token, tempString);
02171   if (NS_FAILED(rv)) {
02172     // not all cards are going this column
02173     *value = nsnull;
02174     return NS_OK;
02175   }
02176   
02177   *value = nsCRT::strdup(tempString.get());
02178   if (!*value) 
02179     return NS_ERROR_OUT_OF_MEMORY;
02180   return NS_OK;
02181 }
02182 
02183 NS_IMETHODIMP nsAddrDatabase::GetDeletedCardList(PRUint32 *aCount, nsISupportsArray **aDeletedList)
02184 {
02185   if (!m_mdbEnv)
02186     return NS_ERROR_NULL_POINTER;
02187 
02188   nsCOMPtr<nsISupportsArray> resultCardArray;
02189   nsresult rv = NS_NewISupportsArray(getter_AddRefs(resultCardArray));
02190   if (NS_FAILED(rv)) return rv;
02191   *aCount = 0;
02192   // make sure the member is set properly
02193   InitDeletedCardsTable(PR_FALSE);
02194   if (m_mdbDeletedCardsTable)
02195   {
02196     nsCOMPtr<nsIMdbTableRowCursor>      rowCursor;
02197     mdb_pos                             rowPos;
02198     PRBool                              done = PR_FALSE;
02199     nsCOMPtr<nsIMdbRow>                 currentRow;
02200 
02201     m_mdbDeletedCardsTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
02202     if (!rowCursor)
02203         return NS_ERROR_FAILURE;
02204     while (!done)
02205     {
02206       nsresult rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
02207       if (currentRow && NS_SUCCEEDED(rv))
02208       {
02209         mdbOid rowOid;
02210         if (currentRow->GetOid(m_mdbEnv, &rowOid) == NS_OK)
02211         {
02212           nsCOMPtr<nsIAbCard>    card;
02213           rv = CreateCardFromDeletedCardsTable(currentRow, 0, getter_AddRefs(card));
02214           if (NS_SUCCEEDED(rv)) {
02215             (*aCount) += 1;
02216             resultCardArray->AppendElement(card);
02217           }
02218         }
02219       }
02220       else
02221           done = PR_TRUE;
02222     }
02223     if (*aCount > 0)
02224       NS_IF_ADDREF(*aDeletedList = resultCardArray);
02225   }
02226   return NS_OK;
02227 }
02228 
02229 NS_IMETHODIMP nsAddrDatabase::GetDeletedCardCount(PRUint32 *aCount)
02230 {
02231     // initialize count first
02232     *aCount = 0;
02233     InitDeletedCardsTable(PR_FALSE);
02234     if (m_mdbDeletedCardsTable)
02235       return m_mdbDeletedCardsTable->GetCount(m_mdbEnv, aCount);
02236     return NS_OK;
02237 }
02238 
02239 NS_IMETHODIMP nsAddrDatabase::PurgeDeletedCardTable()
02240 {
02241   if (!m_mdbEnv)
02242     return NS_ERROR_NULL_POINTER;
02243 
02244     if (m_mdbDeletedCardsTable) {
02245         mdb_count cardCount=0;
02246         // if not too many cards let it be
02247         m_mdbDeletedCardsTable->GetCount(m_mdbEnv, &cardCount);
02248         if(cardCount < PURGE_CUTOFF_COUNT)
02249             return NS_OK;
02250         PRUint32 purgeTimeInSec;
02251         PRTime2Seconds(PR_Now(), &purgeTimeInSec);
02252         purgeTimeInSec -= (182*24*60*60);  // six months in seconds
02253         nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
02254         nsresult rv = m_mdbDeletedCardsTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
02255         while(NS_SUCCEEDED(rv)) {
02256             nsCOMPtr<nsIMdbRow> currentRow;
02257             mdb_pos rowPos;
02258             rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
02259             if(currentRow) {
02260                 PRUint32 deletedTimeStamp = 0;
02261                 GetIntColumn(currentRow, m_LastModDateColumnToken, &deletedTimeStamp, 0);
02262                 // if record was deleted more than six months earlier, purge it
02263                 if(deletedTimeStamp && (deletedTimeStamp < purgeTimeInSec)) {
02264                     if(NS_SUCCEEDED(currentRow->CutAllColumns(m_mdbEnv)))
02265                         m_mdbDeletedCardsTable->CutRow(m_mdbEnv, currentRow);
02266                 }
02267                 else
02268                     // since the ordering in Mork is maintained and thus
02269                     // the cards added later appear on the top when retrieved
02270                     break;
02271             }
02272             else
02273                 break; // no more row
02274         }
02275     }
02276 
02277     return NS_OK;
02278 }
02279 
02280 NS_IMETHODIMP nsAddrDatabase::EditCard(nsIAbCard *card, PRBool notify)
02281 {
02282   // XXX make sure this isn't getting called when we're just editing one or two well known fields
02283   if (!card || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
02284     return NS_ERROR_NULL_POINTER;
02285   
02286   nsresult err = NS_OK;
02287   
02288   nsCOMPtr <nsIMdbRow> cardRow;
02289   mdbOid rowOid;
02290   rowOid.mOid_Scope = m_CardRowScopeToken;
02291   
02292   PRUint32 nowInSeconds;
02293   PRTime now = PR_Now();
02294   PRTime2Seconds(now, &nowInSeconds);
02295   card->SetLastModifiedDate(nowInSeconds);
02296   nsCOMPtr<nsIAbMDBCard> dbcard(do_QueryInterface(card, &err));
02297   NS_ENSURE_SUCCESS(err, err);
02298   dbcard->GetDbRowID((PRUint32*)&rowOid.mOid_Id);
02299   
02300   err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(cardRow));
02301   NS_ENSURE_SUCCESS(err, err);
02302   
02303   if (!cardRow)
02304     return NS_OK;
02305   
02306   err = AddAttributeColumnsToRow(card, cardRow);
02307   NS_ENSURE_SUCCESS(err, err);
02308   
02309   if (notify) 
02310     NotifyCardEntryChange(AB_NotifyPropertyChanged, card);
02311   
02312   return NS_OK;
02313 }
02314 
02315 NS_IMETHODIMP nsAddrDatabase::ContainsCard(nsIAbCard *card, PRBool *hasCard)
02316 {
02317     if (!card || !m_mdbPabTable || !m_mdbEnv)
02318         return NS_ERROR_NULL_POINTER;
02319 
02320     nsresult err = NS_OK;
02321     mdb_bool hasOid;
02322     mdbOid rowOid;
02323     PRBool bIsMailList;
02324 
02325     card->GetIsMailList(&bIsMailList);
02326     
02327     if (bIsMailList)
02328         rowOid.mOid_Scope = m_ListRowScopeToken;
02329     else
02330         rowOid.mOid_Scope = m_CardRowScopeToken;
02331 
02332     nsCOMPtr<nsIAbMDBCard> dbcard(do_QueryInterface(card, &err));
02333     NS_ENSURE_SUCCESS(err, err);
02334     dbcard->GetDbRowID((PRUint32*)&rowOid.mOid_Id);
02335 
02336     err = m_mdbPabTable->HasOid(m_mdbEnv, &rowOid, &hasOid);
02337     if (NS_SUCCEEDED(err))
02338     {
02339         *hasCard = hasOid;
02340     }
02341 
02342     return err;
02343 }
02344 
02345 NS_IMETHODIMP nsAddrDatabase::DeleteMailList(nsIAbDirectory *mailList, PRBool notify)
02346 {
02347   if (!mailList || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
02348     return NS_ERROR_NULL_POINTER;
02349 
02350   nsresult err = NS_OK;
02351 
02352   // get the row
02353   nsIMdbRow* pListRow = nsnull;
02354   mdbOid rowOid;
02355   rowOid.mOid_Scope = m_ListRowScopeToken;
02356 
02357   nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList,&err));
02358   NS_ENSURE_SUCCESS(err, err);
02359   dbmailList->GetDbRowID((PRUint32*)&rowOid.mOid_Id);
02360 
02361   err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, &pListRow);
02362   NS_ENSURE_SUCCESS(err,err);
02363 
02364   if (!pListRow)
02365     return NS_OK;
02366 
02367   err = DeleteRow(m_mdbPabTable, pListRow);
02368   NS_RELEASE(pListRow);
02369   return err;
02370 }
02371 
02372 NS_IMETHODIMP nsAddrDatabase::EditMailList(nsIAbDirectory *mailList, nsIAbCard *listCard, PRBool notify)
02373 {
02374   if (!mailList || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
02375     return NS_ERROR_NULL_POINTER;
02376 
02377   nsresult err = NS_OK;
02378 
02379   nsIMdbRow* pListRow = nsnull;
02380   mdbOid rowOid;
02381   rowOid.mOid_Scope = m_ListRowScopeToken;
02382 
02383   nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList,&err));
02384   NS_ENSURE_SUCCESS(err, err);
02385   dbmailList->GetDbRowID((PRUint32*)&rowOid.mOid_Id);
02386 
02387   err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, &pListRow);
02388   NS_ENSURE_SUCCESS(err, err);
02389 
02390   if (!pListRow)
02391     return NS_OK;
02392 
02393   err = AddListAttributeColumnsToRow(mailList, pListRow);
02394   NS_ENSURE_SUCCESS(err, err);
02395 
02396   if (notify)
02397   {
02398     NotifyListEntryChange(AB_NotifyPropertyChanged, mailList);
02399 
02400     if (listCard)
02401     {
02402       NotifyCardEntryChange(AB_NotifyPropertyChanged, listCard);
02403     }
02404   }
02405 
02406   NS_RELEASE(pListRow);
02407   return NS_OK;
02408 }
02409 
02410 NS_IMETHODIMP nsAddrDatabase::ContainsMailList(nsIAbDirectory *mailList, PRBool *hasList)
02411 {
02412     if (!mailList || !m_mdbPabTable || !m_mdbEnv)
02413         return NS_ERROR_NULL_POINTER;
02414 
02415     mdb_err err = NS_OK;
02416     mdb_bool hasOid;
02417     mdbOid rowOid;
02418 
02419     rowOid.mOid_Scope = m_ListRowScopeToken;
02420 
02421     nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList,&err));
02422     NS_ENSURE_SUCCESS(err, err);
02423     dbmailList->GetDbRowID((PRUint32*)&rowOid.mOid_Id);
02424 
02425     err = m_mdbPabTable->HasOid(m_mdbEnv, &rowOid, &hasOid);
02426     if (err == NS_OK)
02427         *hasList = hasOid;
02428 
02429     return (err == NS_OK) ? NS_OK : NS_ERROR_FAILURE;
02430 }
02431 
02432 NS_IMETHODIMP nsAddrDatabase::GetNewRow(nsIMdbRow * *newRow)
02433 {
02434   if (!m_mdbStore || !newRow || !m_mdbEnv)
02435     return NS_ERROR_NULL_POINTER;
02436 
02437   return m_mdbStore->NewRow(m_mdbEnv, m_CardRowScopeToken, newRow);
02438 }
02439 
02440 NS_IMETHODIMP nsAddrDatabase::GetNewListRow(nsIMdbRow * *newRow)
02441 {
02442   if (!m_mdbStore || !newRow || !m_mdbEnv)
02443     return NS_ERROR_NULL_POINTER;
02444 
02445   return m_mdbStore->NewRow(m_mdbEnv, m_ListRowScopeToken, newRow);
02446 }
02447 
02448 NS_IMETHODIMP nsAddrDatabase::AddCardRowToDB(nsIMdbRow *newRow)
02449 {
02450   if (m_mdbPabTable && m_mdbEnv)
02451   {
02452     if (m_mdbPabTable->AddRow(m_mdbEnv, newRow) == NS_OK)
02453     {
02454       AddRecordKeyColumnToRow(newRow);
02455       return NS_OK;
02456     }
02457     return NS_ERROR_FAILURE;
02458   }
02459 
02460   return NS_ERROR_FAILURE;
02461 }
02462  
02463 NS_IMETHODIMP nsAddrDatabase::AddLdifListMember(nsIMdbRow* listRow, const char* value)
02464 {
02465   if (!m_mdbStore || !listRow || !value || !m_mdbEnv)
02466     return NS_ERROR_NULL_POINTER;
02467 
02468   PRUint32 total = GetListAddressTotal(listRow);
02469   //add member
02470   nsCAutoString valueString(value);
02471   nsCAutoString email;
02472   PRInt32 emailPos = valueString.Find("mail=");
02473   emailPos += strlen("mail=");
02474   valueString.Right(email, valueString.Length() - emailPos);
02475   nsCOMPtr <nsIMdbRow> cardRow;
02476   // Please DO NOT change the 3rd param of GetRowFromAttribute() call to 
02477   // PR_TRUE (ie, case insensitive) without reading bugs #128535 and #121478.
02478   nsresult rv = GetRowFromAttribute(kPriEmailColumn, email.get(), PR_FALSE /* retain case */, getter_AddRefs(cardRow));
02479   if (NS_SUCCEEDED(rv) && cardRow)
02480   {
02481     mdbOid outOid;
02482     mdb_id rowID = 0;
02483     if (cardRow->GetOid(m_mdbEnv, &outOid) == NS_OK)
02484       rowID = outOid.mOid_Id;
02485 
02486     // start from 1
02487     total += 1;
02488     mdb_token listAddressColumnToken;
02489     char columnStr[COLUMN_STR_MAX];
02490     PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, total); 
02491     m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
02492 
02493     rv = AddIntColumn(listRow, listAddressColumnToken, rowID);
02494     NS_ENSURE_SUCCESS(rv, rv);
02495 
02496     SetListAddressTotal(listRow, total);
02497   }
02498   return NS_OK;
02499 }
02500  
02501 
02502 void nsAddrDatabase::GetCharStringYarn(char* str, struct mdbYarn* strYarn)
02503 {
02504     strYarn->mYarn_Grow = nsnull;
02505     strYarn->mYarn_Buf = str;
02506     strYarn->mYarn_Size = PL_strlen((const char *) strYarn->mYarn_Buf) + 1;
02507     strYarn->mYarn_Fill = strYarn->mYarn_Size - 1;
02508     strYarn->mYarn_Form = 0;
02509 }
02510 
02511 void nsAddrDatabase::GetStringYarn(const nsAString & aStr, struct mdbYarn* strYarn)
02512 {
02513     strYarn->mYarn_Buf = ToNewUTF8String(aStr);
02514     strYarn->mYarn_Size = PL_strlen((const char *) strYarn->mYarn_Buf) + 1;
02515     strYarn->mYarn_Fill = strYarn->mYarn_Size - 1;
02516     strYarn->mYarn_Form = 0;     
02517 }
02518 
02519 void nsAddrDatabase::GetIntYarn(PRUint32 nValue, struct mdbYarn* intYarn)
02520 {
02521     intYarn->mYarn_Fill = intYarn->mYarn_Size;
02522     intYarn->mYarn_Form = 0;
02523     intYarn->mYarn_Grow = nsnull;
02524 
02525     PR_snprintf((char*)intYarn->mYarn_Buf, intYarn->mYarn_Size, "%lx", nValue);
02526     intYarn->mYarn_Fill = PL_strlen((const char *) intYarn->mYarn_Buf);
02527 }
02528 
02529 nsresult nsAddrDatabase::AddCharStringColumn(nsIMdbRow* cardRow, mdb_column inColumn, const char* str)
02530 {
02531   if (!m_mdbEnv)
02532     return NS_ERROR_NULL_POINTER;
02533 
02534   struct mdbYarn yarn;
02535 
02536   GetCharStringYarn((char *) str, &yarn);
02537   mdb_err err = cardRow->AddColumn(m_mdbEnv,  inColumn, &yarn);
02538 
02539   return (err == NS_OK) ? NS_OK : NS_ERROR_FAILURE;
02540 }
02541 
02542 nsresult nsAddrDatabase::AddStringColumn(nsIMdbRow* aCardRow, mdb_column aInColumn, const nsAString & aStr)
02543 {
02544   if (!m_mdbEnv)
02545     return NS_ERROR_NULL_POINTER;
02546 
02547   struct mdbYarn yarn;
02548 
02549   GetStringYarn(aStr, &yarn);
02550   mdb_err err = aCardRow->AddColumn(m_mdbEnv, aInColumn, &yarn);
02551 
02552   return (err == NS_OK) ? NS_OK : NS_ERROR_FAILURE;
02553 }
02554 
02555 nsresult nsAddrDatabase::AddIntColumn(nsIMdbRow* cardRow, mdb_column inColumn, PRUint32 nValue)
02556 {
02557   if (!m_mdbEnv)
02558     return NS_ERROR_NULL_POINTER;
02559 
02560   struct mdbYarn yarn;
02561   char    yarnBuf[100];
02562 
02563   yarn.mYarn_Buf = (void *) yarnBuf;
02564   yarn.mYarn_Size = sizeof(yarnBuf);
02565   GetIntYarn(nValue, &yarn);
02566   mdb_err err = cardRow->AddColumn(m_mdbEnv,  inColumn, &yarn);
02567 
02568   return (err == NS_OK) ? NS_OK : NS_ERROR_FAILURE;
02569 }
02570 
02571 nsresult nsAddrDatabase::AddBoolColumn(nsIMdbRow* cardRow, mdb_column inColumn, PRBool bValue)
02572 {
02573   if (!m_mdbEnv)
02574     return NS_ERROR_NULL_POINTER;
02575 
02576   struct mdbYarn yarn;
02577   char    yarnBuf[100];
02578 
02579   yarn.mYarn_Buf = (void *) yarnBuf;
02580   yarn.mYarn_Size = sizeof(yarnBuf);
02581 
02582   GetIntYarn(bValue ? 1 : 0, &yarn);
02583 
02584   mdb_err err = cardRow->AddColumn(m_mdbEnv, inColumn, &yarn);
02585 
02586   return (err == NS_OK) ? NS_OK : NS_ERROR_FAILURE;
02587 }
02588 
02589 nsresult nsAddrDatabase::GetStringColumn(nsIMdbRow *cardRow, mdb_token outToken, nsString& str)
02590 {
02591   nsresult    err = NS_ERROR_NULL_POINTER;
02592   nsIMdbCell    *cardCell;
02593   
02594   if (cardRow && m_mdbEnv)
02595   {
02596     err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
02597     if (err == NS_OK && cardCell)
02598     {
02599       struct mdbYarn yarn;
02600       cardCell->AliasYarn(m_mdbEnv, &yarn);
02601       NS_ConvertUTF8toUCS2 uniStr((const char*) yarn.mYarn_Buf, yarn.mYarn_Fill);
02602       if (!uniStr.IsEmpty())
02603         str.Assign(uniStr);
02604       else
02605         err = NS_ERROR_FAILURE;
02606       cardCell->Release(); // always release ref
02607     }
02608     else
02609       err = NS_ERROR_FAILURE;
02610   }
02611   return err;
02612 }
02613 
02614 void nsAddrDatabase::YarnToUInt32(struct mdbYarn *yarn, PRUint32 *pResult)
02615 {
02616     PRUint32 i, result, numChars;
02617     char *p = (char *) yarn->mYarn_Buf;
02618     if (yarn->mYarn_Fill > 8)
02619         numChars = 8;
02620     else
02621         numChars = yarn->mYarn_Fill;
02622     for (i=0, result = 0; i < numChars; i++, p++)
02623     {
02624         char C = *p;
02625 
02626         PRInt8 unhex = ((C >= '0' && C <= '9') ? C - '0' :
02627             ((C >= 'A' && C <= 'F') ? C - 'A' + 10 :
02628              ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : -1)));
02629         if (unhex < 0)
02630             break;
02631         result = (result << 4) | unhex;
02632     }
02633     
02634     *pResult = result;
02635 }
02636 
02637 nsresult nsAddrDatabase::GetIntColumn
02638 (nsIMdbRow *cardRow, mdb_token outToken, PRUint32* pValue, PRUint32 defaultValue)
02639 {
02640     nsresult    err = NS_ERROR_NULL_POINTER;
02641     nsIMdbCell    *cardCell;
02642 
02643     if (pValue)
02644         *pValue = defaultValue;
02645     if (cardRow && m_mdbEnv)
02646     {
02647         err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
02648         if (err == NS_OK && cardCell)
02649         {
02650             struct mdbYarn yarn;
02651             cardCell->AliasYarn(m_mdbEnv, &yarn);
02652             YarnToUInt32(&yarn, pValue);
02653             cardCell->Release();
02654         }
02655         else
02656             err = NS_ERROR_FAILURE;
02657     }
02658     return err;
02659 }
02660 
02661 nsresult nsAddrDatabase::GetBoolColumn(nsIMdbRow *cardRow, mdb_token outToken, PRBool* pValue)
02662 {
02663   NS_ENSURE_ARG_POINTER(pValue);
02664 
02665     nsresult    err = NS_ERROR_NULL_POINTER;
02666     nsIMdbCell    *cardCell;
02667     PRUint32 nValue = 0;
02668 
02669     if (cardRow && m_mdbEnv)
02670     {
02671         err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
02672         if (err == NS_OK && cardCell)
02673         {
02674             struct mdbYarn yarn;
02675             cardCell->AliasYarn(m_mdbEnv, &yarn);
02676             YarnToUInt32(&yarn, &nValue);
02677             cardCell->Release();
02678         }
02679         else
02680             err = NS_ERROR_FAILURE;
02681     }
02682 
02683     *pValue = nValue ? PR_TRUE : PR_FALSE;
02684     return err;
02685 }
02686 
02687 /*  value is UTF8 string */
02688 NS_IMETHODIMP nsAddrDatabase::AddPrimaryEmail(nsIMdbRow *aRow, const char *aValue)
02689 {
02690   NS_ENSURE_ARG_POINTER(aValue);
02691   
02692   nsresult rv = AddCharStringColumn(aRow, m_PriEmailColumnToken, aValue);
02693   NS_ENSURE_SUCCESS(rv,rv);
02694 
02695   rv = AddLowercaseColumn(aRow, m_LowerPriEmailColumnToken, aValue);
02696   NS_ENSURE_SUCCESS(rv,rv);
02697   return rv;
02698 }
02699 
02700 /*  value is UTF8 string */
02701 NS_IMETHODIMP nsAddrDatabase::AddListName(nsIMdbRow *aRow, const char *aValue)
02702 {
02703   NS_ENSURE_ARG_POINTER(aValue);
02704   
02705   nsresult rv = AddCharStringColumn(aRow, m_ListNameColumnToken, aValue);
02706   NS_ENSURE_SUCCESS(rv,rv);
02707 
02708   rv = AddLowercaseColumn(aRow, m_LowerListNameColumnToken, aValue);
02709   NS_ENSURE_SUCCESS(rv,rv);
02710   return rv;
02711 }
02712 
02713 /* 
02714 columnValue is UTF8 string, need to convert back to lowercase unicode then 
02715 back to UTF8 string
02716 */
02717 nsresult nsAddrDatabase::AddLowercaseColumn
02718 (nsIMdbRow * row, mdb_token columnToken, const char* columnValue)
02719 {
02720   nsresult rv = NS_OK;
02721   if (columnValue)
02722   {
02723     NS_ConvertUTF8toUCS2 newUnicodeString(columnValue);
02724     ToLowerCase(newUnicodeString);
02725     rv = AddCharStringColumn(row, columnToken, NS_ConvertUCS2toUTF8(newUnicodeString).get());   
02726   }
02727   return rv;
02728 }
02729 
02730 NS_IMETHODIMP nsAddrDatabase::InitCardFromRow(nsIAbCard *newCard, nsIMdbRow* cardRow)
02731 {
02732     nsresult    err = NS_OK;
02733     if (!newCard || !cardRow)
02734         return NS_ERROR_NULL_POINTER;
02735 
02736   nsAutoString tempString;
02737 
02738   // FIX ME
02739   // there is no reason to set / copy all these attributes on the card, when we'll never even
02740   // ask for them.
02741     err = GetStringColumn(cardRow, m_FirstNameColumnToken, tempString);
02742     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02743     {
02744         newCard->SetFirstName(tempString.get());
02745     }
02746 
02747     err = GetStringColumn(cardRow, m_LastNameColumnToken, tempString);
02748     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02749     {
02750         newCard->SetLastName(tempString.get());
02751     }
02752 
02753     err = GetStringColumn(cardRow, m_PhoneticFirstNameColumnToken, tempString);
02754     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02755     {
02756         newCard->SetPhoneticFirstName(tempString.get());
02757     }
02758 
02759     err = GetStringColumn(cardRow, m_PhoneticLastNameColumnToken, tempString);
02760     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02761     {
02762         newCard->SetPhoneticLastName(tempString.get());
02763     }
02764 
02765     err = GetStringColumn(cardRow, m_DisplayNameColumnToken, tempString);
02766     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02767     {
02768         newCard->SetDisplayName(tempString.get());
02769     }
02770 
02771     err = GetStringColumn(cardRow, m_NickNameColumnToken, tempString);
02772     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02773     {
02774         newCard->SetNickName(tempString.get());
02775     }
02776 
02777     err = GetStringColumn(cardRow, m_PriEmailColumnToken, tempString);
02778     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02779     {
02780         newCard->SetPrimaryEmail(tempString.get());
02781     }
02782 
02783     err = GetStringColumn(cardRow, m_2ndEmailColumnToken, tempString);
02784     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02785     {
02786         newCard->SetSecondEmail(tempString.get());
02787     }
02788 
02789     err = GetStringColumn(cardRow, m_DefaultEmailColumnToken, tempString);
02790     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02791     {
02792         newCard->SetDefaultEmail(tempString.get());
02793     }
02794 
02795     err = GetStringColumn(cardRow, m_CardTypeColumnToken, tempString);
02796     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02797     {
02798         newCard->SetCardType(tempString.get());
02799     }
02800 
02801     PRUint32 format = nsIAbPreferMailFormat::unknown;
02802     err = GetIntColumn(cardRow, m_MailFormatColumnToken, &format, 0);
02803     if (NS_SUCCEEDED(err))
02804         newCard->SetPreferMailFormat(format);
02805 
02806     PRUint32 popularityIndex = 0;
02807     err = GetIntColumn(cardRow, m_PopularityIndexColumnToken, &popularityIndex, 0);
02808     if (NS_SUCCEEDED(err))
02809         newCard->SetPopularityIndex(popularityIndex);
02810 
02811     PRBool allowRemoteContent;
02812     err = GetBoolColumn(cardRow, m_AllowRemoteContentColumnToken, &allowRemoteContent);
02813     if (NS_SUCCEEDED(err))
02814         newCard->SetAllowRemoteContent(allowRemoteContent);
02815 
02816     err = GetStringColumn(cardRow, m_WorkPhoneColumnToken, tempString);
02817     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02818     {
02819         newCard->SetWorkPhone(tempString.get());
02820     }
02821 
02822     err = GetStringColumn(cardRow, m_HomePhoneColumnToken, tempString);
02823     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02824     {
02825         newCard->SetHomePhone(tempString.get());
02826     }
02827 
02828     err = GetStringColumn(cardRow, m_FaxColumnToken, tempString);
02829     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02830     {
02831         newCard->SetFaxNumber(tempString.get());
02832     }
02833 
02834     err = GetStringColumn(cardRow, m_PagerColumnToken, tempString);
02835     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02836     {
02837         newCard->SetPagerNumber(tempString.get());
02838     }
02839 
02840     err = GetStringColumn(cardRow, m_CellularColumnToken, tempString);
02841     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02842     {
02843         newCard->SetCellularNumber(tempString.get());
02844     }
02845 
02846     err = GetStringColumn(cardRow, m_WorkPhoneTypeColumnToken, tempString);
02847     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02848         newCard->SetWorkPhoneType(tempString.get());
02849 
02850     err = GetStringColumn(cardRow, m_HomePhoneTypeColumnToken, tempString);
02851     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02852         newCard->SetHomePhoneType(tempString.get());
02853 
02854     err = GetStringColumn(cardRow, m_FaxTypeColumnToken, tempString);
02855     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02856         newCard->SetFaxNumberType(tempString.get());
02857 
02858     err = GetStringColumn(cardRow, m_PagerTypeColumnToken, tempString);
02859     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02860         newCard->SetPagerNumberType(tempString.get());
02861 
02862     err = GetStringColumn(cardRow, m_CellularTypeColumnToken, tempString);
02863     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02864         newCard->SetCellularNumberType(tempString.get());
02865 
02866     err = GetStringColumn(cardRow, m_HomeAddressColumnToken, tempString);
02867     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02868     {
02869         newCard->SetHomeAddress(tempString.get());
02870     }
02871 
02872     err = GetStringColumn(cardRow, m_HomeAddress2ColumnToken, tempString);
02873     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02874     {
02875         newCard->SetHomeAddress2(tempString.get());
02876     }
02877 
02878     err = GetStringColumn(cardRow, m_HomeCityColumnToken, tempString);
02879     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02880     {
02881         newCard->SetHomeCity(tempString.get());
02882     }
02883 
02884     err = GetStringColumn(cardRow, m_HomeStateColumnToken, tempString);
02885     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02886     {
02887         newCard->SetHomeState(tempString.get());
02888     }
02889 
02890     err = GetStringColumn(cardRow, m_HomeZipCodeColumnToken, tempString);
02891     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02892     {
02893         newCard->SetHomeZipCode(tempString.get());
02894     }
02895 
02896     err = GetStringColumn(cardRow, m_HomeCountryColumnToken, tempString);
02897     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02898     {
02899         newCard->SetHomeCountry(tempString.get());
02900     }
02901 
02902     err = GetStringColumn(cardRow, m_WorkAddressColumnToken, tempString);
02903     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02904     {
02905         newCard->SetWorkAddress(tempString.get());
02906     }
02907 
02908     err = GetStringColumn(cardRow, m_WorkAddress2ColumnToken, tempString);
02909     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02910     {
02911         newCard->SetWorkAddress2(tempString.get());
02912     }
02913 
02914     err = GetStringColumn(cardRow, m_WorkCityColumnToken, tempString);
02915     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02916     {
02917         newCard->SetWorkCity(tempString.get());
02918     }
02919 
02920     err = GetStringColumn(cardRow, m_WorkStateColumnToken, tempString);
02921     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02922     {
02923         newCard->SetWorkState(tempString.get());
02924     }
02925 
02926     err = GetStringColumn(cardRow, m_WorkZipCodeColumnToken, tempString);
02927     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02928     {
02929         newCard->SetWorkZipCode(tempString.get());
02930     }
02931 
02932     err = GetStringColumn(cardRow, m_WorkCountryColumnToken, tempString);
02933     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02934     {
02935         newCard->SetWorkCountry(tempString.get());
02936     }
02937 
02938     err = GetStringColumn(cardRow, m_JobTitleColumnToken, tempString);
02939     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02940     {
02941         newCard->SetJobTitle(tempString.get());
02942     }
02943 
02944     err = GetStringColumn(cardRow, m_DepartmentColumnToken, tempString);
02945     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02946     {
02947         newCard->SetDepartment(tempString.get());
02948     }
02949 
02950     err = GetStringColumn(cardRow, m_CompanyColumnToken, tempString);
02951     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02952     {
02953         newCard->SetCompany(tempString.get());
02954     }
02955 
02956     // AimScreenName
02957     err = GetStringColumn(cardRow, m_AimScreenNameColumnToken, tempString);
02958     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02959         newCard->SetAimScreenName(tempString.get());
02960 
02961     err = GetStringColumn(cardRow, m_AnniversaryYearColumnToken, tempString);
02962     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02963         newCard->SetAnniversaryYear(tempString.get());
02964 
02965     err = GetStringColumn(cardRow, m_AnniversaryMonthColumnToken, tempString);
02966     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02967         newCard->SetAnniversaryMonth(tempString.get());
02968 
02969     err = GetStringColumn(cardRow, m_AnniversaryDayColumnToken, tempString);
02970     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02971         newCard->SetAnniversaryDay(tempString.get());
02972 
02973     err = GetStringColumn(cardRow, m_SpouseNameColumnToken, tempString);
02974     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02975         newCard->SetSpouseName(tempString.get());
02976 
02977     err = GetStringColumn(cardRow, m_FamilyNameColumnToken, tempString);
02978     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02979         newCard->SetFamilyName(tempString.get());
02980 
02981     err = GetStringColumn(cardRow, m_DefaultAddressColumnToken, tempString);
02982     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02983         newCard->SetDefaultAddress(tempString.get());
02984 
02985     err = GetStringColumn(cardRow, m_CategoryColumnToken, tempString);
02986     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02987         newCard->SetCategory(tempString.get());
02988 
02989     err = GetStringColumn(cardRow, m_WebPage1ColumnToken, tempString);
02990     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02991     {
02992         newCard->SetWebPage1(tempString.get());
02993     }
02994 
02995     err = GetStringColumn(cardRow, m_WebPage2ColumnToken, tempString);
02996     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
02997     {
02998         newCard->SetWebPage2(tempString.get());
02999     }
03000 
03001     err = GetStringColumn(cardRow, m_BirthYearColumnToken, tempString);
03002     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03003     {
03004         newCard->SetBirthYear(tempString.get());
03005     }
03006 
03007     err = GetStringColumn(cardRow, m_BirthMonthColumnToken, tempString);
03008     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03009     {
03010         newCard->SetBirthMonth(tempString.get());
03011     }
03012 
03013     err = GetStringColumn(cardRow, m_BirthDayColumnToken, tempString);
03014     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03015     {
03016         newCard->SetBirthDay(tempString.get());
03017     }
03018 
03019     err = GetStringColumn(cardRow, m_Custom1ColumnToken, tempString);
03020     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03021     {
03022         newCard->SetCustom1(tempString.get());
03023     }
03024 
03025     err = GetStringColumn(cardRow, m_Custom2ColumnToken, tempString);
03026     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03027     {
03028         newCard->SetCustom2(tempString.get());
03029     }
03030 
03031     err = GetStringColumn(cardRow, m_Custom3ColumnToken, tempString);
03032     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03033     {
03034         newCard->SetCustom3(tempString.get());
03035     }
03036 
03037     err = GetStringColumn(cardRow, m_Custom4ColumnToken, tempString);
03038     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03039     {
03040         newCard->SetCustom4(tempString.get());
03041     }
03042 
03043     err = GetStringColumn(cardRow, m_NotesColumnToken, tempString);
03044     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03045     {
03046         newCard->SetNotes(tempString.get());
03047     }
03048     PRUint32 lastModDate = 0;
03049     err = GetIntColumn(cardRow, m_LastModDateColumnToken, &lastModDate, 0);
03050     if (NS_SUCCEEDED(err))
03051       newCard->SetLastModifiedDate(lastModDate);
03052 
03053     PRUint32 key = 0;
03054     err = GetIntColumn(cardRow, m_RecordKeyColumnToken, &key, 0);
03055     if (NS_SUCCEEDED(err))
03056     {
03057         nsCOMPtr<nsIAbMDBCard> dbnewCard(do_QueryInterface(newCard, &err));
03058         if (NS_SUCCEEDED(err) && dbnewCard)
03059             dbnewCard->SetKey(key);
03060     }
03061 
03062     return err;
03063 }
03064 
03065 nsresult nsAddrDatabase::GetListCardFromDB(nsIAbCard *listCard, nsIMdbRow* listRow)
03066 {
03067     nsresult    err = NS_OK;
03068     if (!listCard || !listRow)
03069         return NS_ERROR_NULL_POINTER;
03070 
03071     nsAutoString tempString;
03072 
03073     err = GetStringColumn(listRow, m_ListNameColumnToken, tempString);
03074     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03075     {
03076         listCard->SetDisplayName(tempString.get());
03077         listCard->SetLastName(tempString.get());
03078     }
03079     err = GetStringColumn(listRow, m_ListNickNameColumnToken, tempString);
03080     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03081     {
03082         listCard->SetNickName(tempString.get());
03083     }
03084     err = GetStringColumn(listRow, m_ListDescriptionColumnToken, tempString);
03085     if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03086     {
03087         listCard->SetNotes(tempString.get());
03088     }
03089     PRUint32 key = 0;
03090     err = GetIntColumn(listRow, m_RecordKeyColumnToken, &key, 0);
03091     if (NS_SUCCEEDED(err))
03092     {
03093         nsCOMPtr<nsIAbMDBCard> dblistCard(do_QueryInterface(listCard, &err));
03094         if (NS_SUCCEEDED(err) && dblistCard)
03095             dblistCard->SetKey(key);
03096     }
03097     return err;
03098 }
03099 
03100 nsresult nsAddrDatabase::GetListFromDB(nsIAbDirectory *newList, nsIMdbRow* listRow)
03101 {
03102   nsresult    err = NS_OK;
03103   if (!newList || !listRow || !m_mdbStore || !m_mdbEnv)
03104     return NS_ERROR_NULL_POINTER;
03105 
03106   nsAutoString tempString;
03107 
03108   err = GetStringColumn(listRow, m_ListNameColumnToken, tempString);
03109   if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03110   {
03111     newList->SetDirName(tempString.get());
03112   }
03113   err = GetStringColumn(listRow, m_ListNickNameColumnToken, tempString);
03114   if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03115   {
03116     newList->SetListNickName(tempString.get());
03117   }
03118   err = GetStringColumn(listRow, m_ListDescriptionColumnToken, tempString);
03119   if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
03120   {
03121     newList->SetDescription(tempString.get());
03122   }
03123 
03124   PRUint32 totalAddress = GetListAddressTotal(listRow);
03125   PRUint32 pos;
03126   for (pos = 1; pos <= totalAddress; pos++)
03127   {
03128     mdb_token listAddressColumnToken;
03129     mdb_id rowID;
03130 
03131     char columnStr[COLUMN_STR_MAX];
03132     PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
03133     m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
03134 
03135     nsCOMPtr <nsIMdbRow> cardRow;
03136     err = GetIntColumn(listRow, listAddressColumnToken, (PRUint32*)&rowID, 0);
03137     NS_ENSURE_SUCCESS(err, err);
03138     err = GetCardRowByRowID(rowID, getter_AddRefs(cardRow));
03139     NS_ENSURE_SUCCESS(err, err);
03140         
03141     if (cardRow)
03142     {
03143       nsCOMPtr<nsIAbCard> card;
03144       err = CreateABCard(cardRow, 0, getter_AddRefs(card));
03145 
03146       nsCOMPtr<nsIAbMDBDirectory> dbnewList(do_QueryInterface(newList, &err));
03147       if(NS_SUCCEEDED(err))
03148         dbnewList->AddAddressToList(card);
03149     }
03150 //        NS_IF_ADDREF(card);
03151   }
03152 
03153   return err;
03154 }
03155 
03156 class nsAddrDBEnumerator : public nsIEnumerator 
03157 {
03158 public:
03159     NS_DECL_ISUPPORTS
03160 
03161     // nsIEnumerator methods:
03162     NS_DECL_NSIENUMERATOR
03163 
03164     // nsAddrDBEnumerator methods:
03165 
03166     nsAddrDBEnumerator(nsAddrDatabase* db);
03167     virtual ~nsAddrDBEnumerator();
03168 
03169 protected:
03170     nsCOMPtr<nsAddrDatabase>    mDB;
03171     nsCOMPtr<nsIAbDirectory>    mResultList;
03172     nsCOMPtr<nsIAbCard>            mResultCard;
03173     nsIMdbTable*                mDbTable;
03174     nsIMdbTableRowCursor*       mRowCursor;
03175     nsIMdbRow*                    mCurrentRow;
03176      mdb_pos                        mRowPos;
03177     PRBool                      mDone;
03178     PRBool                      mCurrentRowIsList;
03179 };
03180 
03181 nsAddrDBEnumerator::nsAddrDBEnumerator(nsAddrDatabase* db)
03182     : mDB(db), mRowCursor(nsnull), mCurrentRow(nsnull), mDone(PR_FALSE)
03183 {
03184     mDbTable = mDB->GetPabTable();
03185     mCurrentRowIsList = PR_FALSE;
03186 }
03187 
03188 nsAddrDBEnumerator::~nsAddrDBEnumerator()
03189 {
03190   NS_IF_RELEASE(mRowCursor);
03191 }
03192 
03193 NS_IMPL_ISUPPORTS1(nsAddrDBEnumerator, nsIEnumerator)
03194 
03195 NS_IMETHODIMP nsAddrDBEnumerator::First(void)
03196 {
03197     nsresult rv = 0;
03198     mDone = PR_FALSE;
03199 
03200     if (!mDB || !mDbTable || !mDB->GetEnv())
03201         return NS_ERROR_NULL_POINTER;
03202         
03203     mDbTable->GetTableRowCursor(mDB->GetEnv(), -1, &mRowCursor);
03204     if (rv != NS_OK) return NS_ERROR_FAILURE;
03205     return Next();
03206 }
03207 
03208 NS_IMETHODIMP nsAddrDBEnumerator::Next(void)
03209 {
03210     if (!mRowCursor)
03211     {
03212         mDone = PR_TRUE;
03213         return NS_ERROR_FAILURE;
03214     }
03215     NS_IF_RELEASE(mCurrentRow);
03216     nsresult rv = mRowCursor->NextRow(mDB->GetEnv(), &mCurrentRow, &mRowPos);
03217     if (mCurrentRow && NS_SUCCEEDED(rv))
03218     {
03219         mdbOid rowOid;
03220 
03221         if (mCurrentRow->GetOid(mDB->GetEnv(), &rowOid) == NS_OK)
03222         {
03223             if (mDB->IsListRowScopeToken(rowOid.mOid_Scope))
03224             {
03225                 mCurrentRowIsList = PR_TRUE;
03226                 return NS_OK;
03227             }
03228             else if (mDB->IsCardRowScopeToken(rowOid.mOid_Scope))
03229             {
03230                 mCurrentRowIsList = PR_FALSE;
03231                 return NS_OK;
03232             }
03233             else if (mDB->IsDataRowScopeToken(rowOid.mOid_Scope))
03234             {
03235                 return Next();
03236             }
03237             else
03238                 return NS_ERROR_FAILURE;
03239         }
03240     }
03241     else if (!mCurrentRow) 
03242     {
03243         mDone = PR_TRUE;
03244         return NS_ERROR_NULL_POINTER;
03245     }
03246     else if (NS_FAILED(rv)) 
03247     {
03248         mDone = PR_TRUE;
03249         return NS_ERROR_FAILURE;
03250     }
03251     return NS_ERROR_FAILURE;
03252 }
03253 
03254 NS_IMETHODIMP nsAddrDBEnumerator::CurrentItem(nsISupports **aItem)
03255 {
03256     if (mCurrentRow) 
03257     {
03258         nsresult rv;
03259         if (mCurrentRowIsList)
03260         {
03261             rv = mDB->CreateABListCard(mCurrentRow, getter_AddRefs(mResultCard));
03262             *aItem = mResultCard;
03263         }
03264         else
03265         {
03266             rv = mDB->CreateABCard(mCurrentRow, 0, getter_AddRefs(mResultCard));
03267             *aItem = mResultCard;
03268         }
03269         NS_IF_ADDREF(*aItem);
03270         return NS_OK;
03271     }
03272     return NS_ERROR_FAILURE;
03273 }
03274 
03275 NS_IMETHODIMP nsAddrDBEnumerator::IsDone(void)
03276 {
03277     return mDone ? NS_OK : NS_ERROR_FAILURE;
03278 }
03279 
03280 class nsListAddressEnumerator : public nsIEnumerator 
03281 {
03282 public:
03283     NS_DECL_ISUPPORTS
03284 
03285     // nsIEnumerator methods:
03286     NS_DECL_NSIENUMERATOR
03287 
03288     // nsAddrDBEnumerator methods:
03289 
03290     nsListAddressEnumerator(nsAddrDatabase* db, mdb_id rowID);
03291     virtual ~nsListAddressEnumerator();
03292 
03293 protected:
03294     nsCOMPtr<nsAddrDatabase>    mDB;
03295     nsCOMPtr<nsIAbCard>            mResultCard;
03296     nsIMdbTable*                mDbTable;
03297     nsIMdbRow*                    mListRow;
03298     nsIMdbRow*                    mCurrentRow;
03299     mdb_id                        mListRowID;
03300     PRBool                      mDone;
03301     PRUint32                    mAddressTotal;
03302     PRUint16                    mAddressPos;
03303 };
03304 
03305 nsListAddressEnumerator::nsListAddressEnumerator(nsAddrDatabase* db, mdb_id rowID)
03306     : mDB(db), mCurrentRow(nsnull), mListRowID(rowID), mDone(PR_FALSE)
03307 {
03308     mDbTable = mDB->GetPabTable();
03309     mDB->GetListRowByRowID(mListRowID, &mListRow);
03310     mAddressTotal = mDB->GetListAddressTotal(mListRow);
03311     mAddressPos = 0;
03312 }
03313 
03314 nsListAddressEnumerator::~nsListAddressEnumerator()
03315 {
03316   NS_IF_RELEASE(mListRow);
03317 }
03318 
03319 NS_IMPL_ISUPPORTS1(nsListAddressEnumerator, nsIEnumerator)
03320 
03321 NS_IMETHODIMP nsListAddressEnumerator::First(void)
03322 {
03323     mDone = PR_FALSE;
03324 
03325     if (!mDB || !mDbTable || !mDB->GetEnv())
03326         return NS_ERROR_NULL_POINTER;
03327     
03328     //got total address count and start
03329     if (mAddressTotal)
03330         return Next();
03331     else
03332         return NS_ERROR_FAILURE;
03333 }
03334 
03335 NS_IMETHODIMP nsListAddressEnumerator::Next(void)
03336 {
03337     //go to the next address column
03338     if (mCurrentRow)
03339     {
03340                 NS_RELEASE(mCurrentRow);
03341         mCurrentRow = nsnull;
03342     }
03343     mAddressPos++;
03344     if (mAddressPos <= mAddressTotal)
03345     {
03346         nsresult err;
03347         err = mDB->GetAddressRowByPos(mListRow, mAddressPos, &mCurrentRow);
03348         if (mCurrentRow)
03349             return NS_OK;
03350         else
03351         {
03352             mDone = PR_TRUE;
03353             return NS_ERROR_FAILURE;
03354         }
03355     }
03356     else
03357     {
03358         mDone = PR_TRUE;
03359         return NS_ERROR_FAILURE;
03360     }
03361 }
03362 
03363 NS_IMETHODIMP nsListAddressEnumerator::CurrentItem(nsISupports **aItem)
03364 {
03365   if (mCurrentRow) 
03366   {
03367     nsresult rv;
03368     rv = mDB->CreateABCard(mCurrentRow, mListRowID, getter_AddRefs(mResultCard));
03369     NS_IF_ADDREF(*aItem = mResultCard);
03370     return NS_OK;
03371   }
03372   return NS_ERROR_FAILURE;
03373 }
03374 
03375 NS_IMETHODIMP nsListAddressEnumerator::IsDone(void)
03376 {
03377     return mDone ? NS_OK : NS_ERROR_FAILURE;
03378 }
03379 
03381 
03382 NS_IMETHODIMP nsAddrDatabase::EnumerateCards(nsIAbDirectory *directory, nsIEnumerator **result)
03383 {
03384     nsAddrDBEnumerator* e = new nsAddrDBEnumerator(this);
03385     m_dbDirectory = directory;
03386     if (!e)
03387         return NS_ERROR_OUT_OF_MEMORY;
03388     NS_ADDREF(e);
03389     *result = e;
03390     return NS_OK;
03391 }
03392 
03393 NS_IMETHODIMP nsAddrDatabase::GetMailingListsFromDB(nsIAbDirectory *parentDir)
03394 {
03395     nsCOMPtr<nsIAbDirectory>    resultList;
03396     nsIMdbTableRowCursor*       rowCursor = nsnull;
03397     nsCOMPtr<nsIMdbRow> currentRow;
03398      mdb_pos                        rowPos;
03399     PRBool                      done = PR_FALSE;
03400 
03401   if (!m_mdbEnv)
03402     return NS_ERROR_NULL_POINTER;
03403 
03404     m_dbDirectory = parentDir;
03405 
03406     nsIMdbTable*                dbTable = GetPabTable();
03407 
03408     if (!dbTable)
03409         return NS_ERROR_FAILURE;
03410 
03411     dbTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
03412     if (!rowCursor)
03413         return NS_ERROR_FAILURE;
03414 
03415     while (!done)
03416     {
03417                 nsresult rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
03418         if (currentRow && NS_SUCCEEDED(rv))
03419         {
03420             mdbOid rowOid;
03421 
03422             if (currentRow->GetOid(m_mdbEnv, &rowOid) == NS_OK)
03423             {
03424                 if (IsListRowScopeToken(rowOid.mOid_Scope))
03425                     rv = CreateABList(currentRow, getter_AddRefs(resultList));
03426             }
03427         }
03428         else
03429             done = PR_TRUE;
03430     }
03431         NS_IF_RELEASE(rowCursor);
03432     return NS_OK;
03433 }
03434 
03435 NS_IMETHODIMP nsAddrDatabase::EnumerateListAddresses(nsIAbDirectory *directory, nsIEnumerator **result)
03436 {
03437     nsresult rv = NS_OK; 
03438     mdb_id rowID;
03439 
03440     nsCOMPtr<nsIAbMDBDirectory> dbdirectory(do_QueryInterface(directory,&rv));
03441     
03442     if(NS_SUCCEEDED(rv))
03443     {
03444     dbdirectory->GetDbRowID((PRUint32*)&rowID);
03445 
03446     nsListAddressEnumerator* e = new nsListAddressEnumerator(this, rowID);
03447     m_dbDirectory = directory;
03448     if (!e)
03449         return NS_ERROR_OUT_OF_MEMORY;
03450     NS_ADDREF(e);
03451     *result = e;
03452     }
03453     return rv;
03454 }
03455 
03456 nsresult nsAddrDatabase::CreateCardFromDeletedCardsTable(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result)
03457 {
03458   if (!cardRow || !m_mdbEnv || !result)
03459     return NS_ERROR_NULL_POINTER;
03460 
03461     nsresult rv = NS_OK; 
03462 
03463     mdbOid outOid;
03464     mdb_id rowID=0;
03465 
03466     if (cardRow->GetOid(m_mdbEnv, &outOid) == NS_OK)
03467         rowID = outOid.mOid_Id;
03468 
03469     if(NS_SUCCEEDED(rv))
03470     {
03471         nsCOMPtr<nsIAbCard> personCard;
03472         personCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
03473         NS_ENSURE_SUCCESS(rv,rv);
03474 
03475         nsCOMPtr<nsIAbMDBCard> dbpersonCard (do_QueryInterface(personCard, &rv));
03476 
03477         if (NS_SUCCEEDED(rv) && dbpersonCard)
03478         {
03479             InitCardFromRow(personCard, cardRow);
03480             mdbOid tableOid;
03481             m_mdbDeletedCardsTable->GetOid(m_mdbEnv, &tableOid);
03482 
03483             dbpersonCard->SetDbTableID(tableOid.mOid_Id);
03484             dbpersonCard->SetDbRowID(rowID);
03485             dbpersonCard->SetAbDatabase(this);
03486         }
03487 
03488         NS_IF_ADDREF(*result = personCard);
03489     }
03490 
03491     return rv;
03492 }
03493 
03494 nsresult nsAddrDatabase::CreateCard(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result)
03495 {
03496   if (!cardRow || !m_mdbEnv || !result)
03497     return NS_ERROR_NULL_POINTER;
03498 
03499     nsresult rv = NS_OK; 
03500 
03501     mdbOid outOid;
03502     mdb_id rowID=0;
03503 
03504     if (cardRow->GetOid(m_mdbEnv, &outOid) == NS_OK)
03505         rowID = outOid.mOid_Id;
03506 
03507     if(NS_SUCCEEDED(rv))
03508     {
03509         nsCOMPtr<nsIAbCard> personCard;
03510       personCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
03511       NS_ENSURE_SUCCESS(rv,rv);
03512 
03513         nsCOMPtr<nsIAbMDBCard> dbpersonCard (do_QueryInterface(personCard, &rv));
03514 
03515         if (NS_SUCCEEDED(rv) && dbpersonCard)
03516         {
03517             InitCardFromRow(personCard, cardRow);
03518             mdbOid tableOid;
03519             m_mdbPabTable->GetOid(m_mdbEnv, &tableOid);
03520 
03521             dbpersonCard->SetDbTableID(tableOid.mOid_Id);
03522             dbpersonCard->SetDbRowID(rowID);
03523             dbpersonCard->SetAbDatabase(this);
03524         }
03525 
03526         NS_IF_ADDREF(*result = personCard);
03527     }
03528 
03529     return rv;
03530 }
03531 
03532 nsresult nsAddrDatabase::CreateABCard(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result)
03533 {
03534     return CreateCard(cardRow, listRowID, result);
03535 }
03536 
03537 /* create a card for mailing list in the address book */
03538 nsresult nsAddrDatabase::CreateABListCard(nsIMdbRow* listRow, nsIAbCard **result)
03539 {
03540   if (!listRow || !m_mdbEnv || !result)
03541     return NS_ERROR_NULL_POINTER;
03542 
03543     nsresult rv = NS_OK; 
03544 
03545     mdbOid outOid;
03546     mdb_id rowID=0;
03547 
03548     if (listRow->GetOid(m_mdbEnv, &outOid) == NS_OK)
03549         rowID = outOid.mOid_Id;
03550 
03551     char* listURI = nsnull;
03552     char* file = nsnull;
03553     file = m_dbName.GetLeafName();
03554     listURI = PR_smprintf("%s%s/MailList%ld", kMDBDirectoryRoot, file, rowID);
03555 
03556     nsCOMPtr<nsIAbCard> personCard;
03557     nsCOMPtr<nsIAbMDBDirectory> dbm_dbDirectory(do_QueryInterface(m_dbDirectory, &rv));
03558     if(NS_SUCCEEDED(rv) && dbm_dbDirectory)
03559     {
03560     personCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
03561         NS_ENSURE_SUCCESS(rv,rv);
03562 
03563         if (personCard)
03564         {
03565             GetListCardFromDB(personCard, listRow);
03566             mdbOid tableOid;
03567             m_mdbPabTable->GetOid(m_mdbEnv, &tableOid);
03568     
03569             nsCOMPtr<nsIAbMDBCard> dbpersonCard(do_QueryInterface(personCard, &rv));
03570       if (NS_SUCCEEDED(rv) && dbpersonCard)
03571       {
03572               dbpersonCard->SetDbTableID(tableOid.mOid_Id);
03573               dbpersonCard->SetDbRowID(rowID);
03574               dbpersonCard->SetAbDatabase(this);
03575       }
03576             personCard->SetIsMailList(PR_TRUE);
03577             personCard->SetMailListURI(listURI);
03578         }
03579         
03580         NS_IF_ADDREF(*result = personCard);
03581     }
03582     if (file)
03583         nsCRT::free(file);
03584     if (listURI)
03585         PR_smprintf_free(listURI);
03586 
03587     return rv;
03588 }
03589 
03590 /* create a sub directory for mailing list in the address book left pane */
03591 nsresult nsAddrDatabase::CreateABList(nsIMdbRow* listRow, nsIAbDirectory **result)
03592 {
03593     nsresult rv = NS_OK; 
03594 
03595     if (!listRow || !m_mdbEnv || !result)
03596         return NS_ERROR_NULL_POINTER;
03597 
03598     mdbOid outOid;
03599     mdb_id rowID=0;
03600 
03601     if (listRow->GetOid(m_mdbEnv, &outOid) == NS_OK)
03602         rowID = outOid.mOid_Id;
03603 
03604     char* listURI = nsnull;
03605     char* file = nsnull;
03606 
03607     file = m_dbName.GetLeafName();
03608     listURI = PR_smprintf("%s%s/MailList%ld", kMDBDirectoryRoot, file, rowID);
03609 
03610     nsCOMPtr<nsIAbDirectory> mailList;
03611     nsCOMPtr<nsIAbMDBDirectory> dbm_dbDirectory(do_QueryInterface(m_dbDirectory, &rv));
03612     if(NS_SUCCEEDED(rv) && dbm_dbDirectory)
03613     {
03614         rv = dbm_dbDirectory->AddDirectory(listURI, getter_AddRefs(mailList));
03615 
03616         nsCOMPtr<nsIAbMDBDirectory> dbmailList (do_QueryInterface(mailList, &rv));
03617 
03618         if (mailList) 
03619         {
03620             // if we are using turbo, and we "exit" and restart with the same profile
03621             // the current mailing list will still be in memory, so when we do
03622             // GetResource() and QI, we'll get it again.
03623             // in that scenario, the mailList that we pass in will already be
03624             // be a mailing list, with a valid row and all the entries
03625             // in that scenario, we can skip GetListFromDB(), which would have
03626             // have added all the cards to the list again.
03627             // see bug #134743
03628             mdb_id existingID;
03629             dbmailList->GetDbRowID(&existingID);
03630             if (existingID != rowID) {
03631               GetListFromDB(mailList, listRow);
03632               dbmailList->SetDbRowID(rowID);
03633               mailList->SetIsMailList(PR_TRUE);
03634             }
03635 
03636             dbm_dbDirectory->AddMailListToDirectory(mailList);
03637             NS_IF_ADDREF(*result = mailList);
03638         }
03639     }
03640 
03641     if (file)
03642         nsCRT::free(file);
03643     if (listURI)
03644         PR_smprintf_free(listURI);
03645 
03646     return rv;
03647 }
03648 
03649 nsresult nsAddrDatabase::GetCardRowByRowID(mdb_id rowID, nsIMdbRow **dbRow)
03650 {
03651   if (!m_mdbStore || !m_mdbEnv)
03652     return NS_ERROR_NULL_POINTER;
03653 
03654   mdbOid rowOid;
03655   rowOid.mOid_Scope = m_CardRowScopeToken;
03656   rowOid.mOid_Id = rowID;
03657 
03658   return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
03659 }
03660 
03661 nsresult nsAddrDatabase::GetListRowByRowID(mdb_id rowID, nsIMdbRow **dbRow)
03662 {
03663   if (!m_mdbStore || !m_mdbEnv)
03664     return NS_ERROR_NULL_POINTER;
03665 
03666   mdbOid rowOid;
03667   rowOid.mOid_Scope = m_ListRowScopeToken;
03668   rowOid.mOid_Id = rowID;
03669 
03670   return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
03671 }
03672 
03673 nsresult nsAddrDatabase::GetRowFromAttribute(const char *aName, const char *aUTF8Value, PRBool aCaseInsensitive, nsIMdbRow **aCardRow)
03674 {
03675   NS_ENSURE_ARG_POINTER(aName);
03676   NS_ENSURE_ARG_POINTER(aUTF8Value);
03677   NS_ENSURE_ARG_POINTER(aCardRow);
03678   if (!m_mdbStore || !m_mdbEnv)
03679     return NS_ERROR_NULL_POINTER;
03680   
03681   mdb_token token;
03682   m_mdbStore->StringToToken(m_mdbEnv, aName, &token);
03683   
03684     NS_ConvertUTF8toUCS2 newUnicodeString(aUTF8Value);
03685 
03686   if (aCaseInsensitive)
03687     ToLowerCase(newUnicodeString);
03688   
03689   return GetRowForCharColumn(newUnicodeString.get(), token, PR_TRUE, aCardRow);
03690 }
03691 
03692 NS_IMETHODIMP nsAddrDatabase::GetCardFromAttribute(nsIAbDirectory *aDirectory, const char *aName, const char *aUTF8Value, PRBool aCaseInsensitive, nsIAbCard **aCardResult)
03693 {
03694   NS_ENSURE_ARG_POINTER(aCardResult);
03695   
03696   m_dbDirectory = aDirectory;
03697   nsCOMPtr <nsIMdbRow> cardRow;
03698   nsresult rv = GetRowFromAttribute(aName, aUTF8Value, aCaseInsensitive, getter_AddRefs(cardRow));
03699   if (NS_SUCCEEDED(rv) && cardRow)
03700     rv = CreateABCard(cardRow, 0, aCardResult);
03701   else 
03702   {
03703     *aCardResult = nsnull;
03704     rv = NS_OK;
03705   }
03706   return rv;
03707 }
03708 
03709 NS_IMETHODIMP nsAddrDatabase::AddRowValue(nsIMdbRow *aRow, const nsACString & aLDIFAttributeName, const nsAString & aColValue)
03710 {
03711   PRInt32 i;
03712   for (i = 0; i < EXPORT_ATTRIBUTES_TABLE_COUNT; i++) {
03713     // need strcasecmp, LDIF is case insensitive
03714     if (nsCRT::strcasecmp(EXPORT_ATTRIBUTES_TABLE[i].ldapPropertyName, PromiseFlatCString(aLDIFAttributeName).get()) == 0) {
03715       mdb_token token;
03716       m_mdbStore->StringToToken(GetEnv(), EXPORT_ATTRIBUTES_TABLE[i].abColName, &token);
03717       nsresult rv = AddStringColumn(aRow, token, aColValue);
03718       NS_ENSURE_SUCCESS(rv,rv);
03719       return NS_OK;
03720     }
03721   }
03722   NS_ASSERTION(0, "failed to map LDIF attribute to column");
03723   return NS_ERROR_FAILURE;
03724 }
03725 
03726 NS_IMETHODIMP nsAddrDatabase::GetDirectoryName(PRUnichar **name)
03727 {
03728     if (m_dbDirectory && name)
03729     {
03730         return m_dbDirectory->GetDirName(name);
03731     }
03732     else
03733         return NS_ERROR_FAILURE;
03734 }
03735 
03736 NS_IMETHODIMP nsAddrDatabase::AddListDirNode(nsIMdbRow * listRow)
03737 {
03738     nsresult rv = NS_OK;
03739 
03740     nsCOMPtr<nsIProxyObjectManager> proxyMgr = 
03741              do_GetService(NS_XPCOMPROXY_CONTRACTID, &rv);
03742     NS_ENSURE_SUCCESS(rv, rv);
03743 
03744   static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
03745     NS_WITH_PROXIED_SERVICE(nsIRDFService, rdfService, kRDFServiceCID, NS_UI_THREAD_EVENTQ, &rv);
03746     if (NS_SUCCEEDED(rv)) 
03747     {
03748         nsCOMPtr<nsIRDFResource>    parentResource;
03749 
03750         char* file = m_dbName.GetLeafName();
03751         char *parentUri = PR_smprintf("%s%s", kMDBDirectoryRoot, file);
03752         rv = rdfService->GetResource(nsDependentCString(parentUri), getter_AddRefs(parentResource));
03753         nsCOMPtr<nsIAbDirectory> parentDir;
03754         rv = proxyMgr->GetProxyForObject( NS_UI_THREAD_EVENTQ, NS_GET_IID( nsIAbDirectory),
03755                                     parentResource, PROXY_SYNC | PROXY_ALWAYS, getter_AddRefs( parentDir));
03756         if (parentDir)
03757         {
03758             m_dbDirectory = parentDir;
03759             nsCOMPtr<nsIAbDirectory> mailList;
03760             rv = CreateABList(listRow, getter_AddRefs(mailList));
03761             if (mailList)
03762             {
03763                 nsCOMPtr<nsIAbMDBDirectory> dbparentDir(do_QueryInterface(parentDir, &rv));
03764                 if(NS_SUCCEEDED(rv))
03765                     dbparentDir->NotifyDirItemAdded(mailList);
03766             }
03767         }
03768         if (parentUri)
03769             PR_smprintf_free(parentUri);
03770         if (file)
03771             nsCRT::free(file);
03772     }
03773     return rv;
03774 }
03775 
03776 NS_IMETHODIMP nsAddrDatabase::FindMailListbyUnicodeName(const PRUnichar *listName, PRBool *exist)
03777 {
03778   nsAutoString unicodeString(listName);
03779   ToLowerCase(unicodeString);
03780   
03781   nsCOMPtr <nsIMdbRow> listRow;
03782   nsresult rv = GetRowForCharColumn(unicodeString.get(), m_LowerListNameColumnToken, PR_FALSE, getter_AddRefs(listRow));
03783   *exist = (NS_SUCCEEDED(rv) && listRow);
03784   return rv;
03785 }
03786 
03787 NS_IMETHODIMP nsAddrDatabase::GetCardCount(PRUint32 *count)
03788 {    
03789     mdb_err rv;
03790     mdb_count c;
03791     rv = m_mdbPabTable->GetCount(m_mdbEnv, &c);
03792     if (rv == NS_OK)
03793         *count = c - 1;  // Don't count LastRecordKey 
03794 
03795     return rv;
03796 }
03797 
03798 PRBool 
03799 nsAddrDatabase::HasRowButDeletedForCharColumn(const PRUnichar *unicodeStr, mdb_column findColumn, PRBool aIsCard, nsIMdbRow **aFindRow)
03800 {
03801   if (!m_mdbStore || !aFindRow || !m_mdbEnv)
03802     return NS_ERROR_NULL_POINTER;
03803 
03804   mdbYarn    sourceYarn;
03805   
03806   NS_ConvertUCS2toUTF8 UTF8String(unicodeStr);
03807   sourceYarn.mYarn_Buf = (void *) UTF8String.get();
03808   sourceYarn.mYarn_Fill = UTF8String.Length();
03809   sourceYarn.mYarn_Form = 0;
03810   sourceYarn.mYarn_Size = sourceYarn.mYarn_Fill;
03811   
03812   mdbOid        outRowId;
03813   nsresult rv;
03814   
03815   if (aIsCard)
03816   {
03817     rv = m_mdbStore->FindRow(m_mdbEnv, m_CardRowScopeToken,
03818       findColumn, &sourceYarn,  &outRowId, aFindRow);
03819     
03820     // no such card, so bail out early
03821     if (NS_SUCCEEDED(rv) && !*aFindRow)
03822       return PR_FALSE;
03823     
03824     // we might not have loaded the "delete cards" table yet
03825     // so do that (but don't create it, if we don't have one), 
03826     // so we can see if the row is really a delete card.
03827     if (!m_mdbDeletedCardsTable)
03828       rv = InitDeletedCardsTable(PR_FALSE);
03829     
03830     // if still no deleted cards table, there are no deleted cards
03831     if (!m_mdbDeletedCardsTable)
03832       return PR_FALSE;
03833     
03834     mdb_bool hasRow = PR_FALSE;
03835     rv = m_mdbDeletedCardsTable->HasRow(m_mdbEnv, *aFindRow, &hasRow);
03836     return (NS_SUCCEEDED(rv) && hasRow);
03837   }
03838   
03839   rv = m_mdbStore->FindRow(m_mdbEnv, m_ListRowScopeToken,
03840     findColumn, &sourceYarn,  &outRowId, aFindRow);
03841   return (NS_SUCCEEDED(rv) && *aFindRow);
03842 }
03843 
03844 nsresult 
03845 nsAddrDatabase::GetRowForCharColumn(const PRUnichar *unicodeStr, mdb_column findColumn, PRBool aIsCard, nsIMdbRow **aFindRow)
03846 {
03847   NS_ENSURE_ARG_POINTER(unicodeStr);
03848   NS_ENSURE_ARG_POINTER(aFindRow);
03849   if (!m_mdbEnv)
03850     return NS_ERROR_NULL_POINTER;
03851 
03852   *aFindRow = nsnull;
03853 
03854   // see bug #198303
03855   // the addition of the m_mdbDeletedCardsTable table has complicated life in the addressbook
03856   // (it was added for palm sync).  until we fix the underlying problem, we have to jump through hoops
03857   // in order to know if we have a row (think card) for a given column value (think email=foo@bar.com)
03858   // there are 4 scenarios:
03859   //   1) no cards with a match
03860   //   2) at least one deleted card with a match, but no non-deleted cards
03861   //   3) at least one non-deleted card with a match, but no deleted cards
03862   //   4) at least one deleted card, and one non-deleted card with a match.
03863   // 
03864   // if we have no cards that match (FindRow() returns nothing), we can bail early
03865   // but if FindRow() returns something, we have to check if it is in the deleted table
03866   // if not in the deleted table we can return the row (we found a non-deleted card)
03867   // but if so, we have to search through the table of non-deleted cards
03868   // for a match.  If we find one, we return it.  but if not, we report that there are no
03869   // non-deleted cards.  This is the expensive part.  The worse case scenario is to have
03870   // deleted lots of cards, and then have a lot of non-deleted cards.
03871   // we'd have to call FindRow(), HasRow(), and then search the list of non-deleted cards
03872   // each time we call GetRowForCharColumn().
03873   if (!HasRowButDeletedForCharColumn(unicodeStr, findColumn, aIsCard, aFindRow))
03874   {
03875     // if we have a row, it's the row for the non-delete card, so return NS_OK.
03876     // otherwise, there is no such card (deleted or not), so return NS_ERROR_FAILURE
03877     return *aFindRow ? NS_OK : NS_ERROR_FAILURE;
03878   }
03879 
03880   // check if there is a non-deleted card
03881   nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
03882   mdb_pos rowPos;
03883   PRBool done = PR_FALSE;
03884   nsCOMPtr<nsIMdbRow> currentRow;
03885   nsAutoString columnValue;
03886   
03887   mdb_scope targetScope = aIsCard ? m_CardRowScopeToken : m_ListRowScopeToken;
03888   
03889   m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
03890   if (!rowCursor)
03891     return NS_ERROR_FAILURE;
03892   
03893   while (!done)
03894   {
03895     nsresult rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
03896     if (currentRow && NS_SUCCEEDED(rv))
03897     {
03898       mdbOid rowOid;
03899       if ((currentRow->GetOid(m_mdbEnv, &rowOid) == NS_OK) && (rowOid.mOid_Scope == targetScope))
03900       {
03901         rv = GetStringColumn(currentRow, findColumn, columnValue);
03902         if (NS_SUCCEEDED(rv) && columnValue.Equals(unicodeStr))
03903         {
03904           NS_IF_ADDREF(*aFindRow = currentRow);
03905           return NS_OK;
03906       }
03907     }
03908   }
03909   else
03910       done = PR_TRUE;
03911 }
03912   return NS_ERROR_FAILURE;
03913 }
03914 
03915 nsresult nsAddrDatabase::DeleteRow(nsIMdbTable* dbTable, nsIMdbRow* dbRow)
03916 {
03917   if (!m_mdbEnv)
03918     return NS_ERROR_NULL_POINTER;
03919 
03920   mdb_err err = dbRow->CutAllColumns(m_mdbEnv);
03921   err = dbTable->CutRow(m_mdbEnv, dbRow);
03922 
03923   return (err == NS_OK) ? NS_OK : NS_ERROR_FAILURE;
03924 }
03925 
03926 NS_IMETHODIMP nsAddrDatabase::CreateMailListAndAddToDBWithKey(nsIAbDirectory *newList, PRBool notify, PRUint32 *key)
03927 {
03928   NS_ENSURE_ARG_POINTER(key);
03929 
03930   *key = 0;
03931   nsresult rv = CreateMailListAndAddToDB(newList, notify);
03932   if (NS_SUCCEEDED(rv))
03933     *key = m_LastRecordKey;
03934   return rv;
03935 }