Back to index

lightning-sunbird  0.9+nobinonly
nsMailDatabase.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "msgCore.h"
00039 #include "nsImapCore.h" // for imap flags
00040 #include "nsMailDatabase.h"
00041 #include "nsDBFolderInfo.h"
00042 #include "nsMsgLocalFolderHdrs.h"
00043 #include "nsFileStream.h"
00044 #include "nsLocalFolderSummarySpec.h"
00045 #include "nsFileSpec.h"
00046 #include "nsMsgOfflineImapOperation.h"
00047 #include "nsMsgFolderFlags.h"
00048 #include "prlog.h"
00049 #include "prprf.h"
00050 #include "nsIFileSpec.h"
00051 #ifdef PUTUP_ALERT_ON_INVALID_DB
00052 #include "nsIPrompt.h"
00053 #include "nsIWindowWatcher.h"
00054 #endif
00055 
00056 extern PRLogModuleInfo *IMAPOffline;
00057 
00058 const char *kOfflineOpsScope = "ns:msg:db:row:scope:ops:all";  // scope for all offine ops table
00059 const char *kOfflineOpsTableKind = "ns:msg:db:table:kind:ops";
00060 struct mdbOid gAllOfflineOpsTableOID;
00061 
00062 
00063 nsMailDatabase::nsMailDatabase()
00064     : m_reparse(PR_FALSE), m_folderSpec(nsnull), m_folderStream(nsnull), m_ownFolderStream(PR_FALSE)
00065 {
00066   m_mdbAllOfflineOpsTable = nsnull;
00067 }
00068 
00069 nsMailDatabase::~nsMailDatabase()
00070 {
00071   delete m_folderSpec;
00072 }
00073 
00074 NS_IMETHODIMP nsMailDatabase::SetFolderStream(nsIOFileStream *aFileStream)
00075 {
00076   NS_ASSERTION(!m_folderStream || !aFileStream, "m_folderStream is not null and we are assigning a non null stream to it");
00077   m_folderStream = aFileStream; //m_folderStream is set externally, so m_ownFolderStream is false
00078   m_ownFolderStream = PR_FALSE;
00079   return NS_OK;
00080 }
00081 
00082 NS_IMETHODIMP nsMailDatabase::GetFolderStream(nsIOFileStream **aFileStream)
00083 {
00084   NS_ENSURE_ARG_POINTER(aFileStream);
00085   if (!m_folderStream)
00086   {
00087     m_folderStream = new nsIOFileStream(nsFileSpec(*m_folderSpec));
00088     m_ownFolderStream = PR_TRUE;
00089   }
00090   // N.B. - not a ref-counted interface pointer
00091   *aFileStream = m_folderStream;
00092   return NS_OK;
00093 }
00094 
00095 static PRBool gGotGlobalPrefs = PR_FALSE;
00096 static PRInt32 gTimeStampLeeway;
00097 
00098 void nsMailDatabase::GetGlobalPrefs()
00099 {
00100   if (!gGotGlobalPrefs)
00101   {
00102     nsMsgDatabase::GetGlobalPrefs();
00103     GetIntPref("mail.db_timestamp_leeway", &gTimeStampLeeway);
00104     gGotGlobalPrefs = PR_TRUE;
00105   }
00106 }
00107 // caller passes in upgrading==PR_TRUE if they want back a db even if the db is out of date.
00108 // If so, they'll extract out the interesting info from the db, close it, delete it, and
00109 // then try to open the db again, prior to reparsing.
00110 NS_IMETHODIMP nsMailDatabase::Open(nsIFileSpec *aFolderName, PRBool aCreate, PRBool aUpgrading)
00111 {
00112   nsFileSpec  folderName;
00113   aFolderName->GetFileSpec(&folderName);
00114   m_folderSpec = new nsFileSpec(folderName);
00115   nsresult rv = nsMsgDatabase::Open(aFolderName, aCreate, aUpgrading);
00116   return rv;
00117 }
00118 
00119 NS_IMETHODIMP nsMailDatabase::ForceClosed()
00120 {
00121   m_mdbAllOfflineOpsTable = nsnull;
00122   return nsMsgDatabase::ForceClosed();
00123 }
00124 
00125 // get this on demand so that only db's that have offline ops will
00126 // create the table. 
00127 nsresult nsMailDatabase::GetAllOfflineOpsTable()
00128 {
00129   nsresult rv = NS_OK;
00130   if (!m_mdbAllOfflineOpsTable)
00131     rv = GetTableCreateIfMissing(kOfflineOpsScope, kOfflineOpsTableKind, getter_AddRefs(m_mdbAllOfflineOpsTable), 
00132                                                 m_offlineOpsRowScopeToken, m_offlineOpsTableKindToken) ;
00133   return rv;
00134 }
00135 
00136 // cache m_folderStream to make updating mozilla status flags fast
00137 NS_IMETHODIMP nsMailDatabase::StartBatch()
00138 {
00139   if (!m_folderStream && m_folder)  //only if we create a stream, set m_ownFolderStream to true.
00140   {
00141     PRBool isLocked;
00142     m_folder->GetLocked(&isLocked);
00143     if (isLocked)
00144     {
00145       NS_ASSERTION(PR_FALSE, "Some other operation is in progress");
00146       return NS_MSG_FOLDER_BUSY;
00147     }
00148     m_folderStream = new nsIOFileStream(nsFileSpec(*m_folderSpec));
00149     m_ownFolderStream = PR_TRUE;
00150   }
00151 
00152   return NS_OK;
00153 }
00154 
00155 NS_IMETHODIMP nsMailDatabase::EndBatch()
00156 {
00157   if (m_ownFolderStream)   //only if we own the stream, then we should close it
00158   {
00159     if (m_folderStream)
00160     {
00161       m_folderStream->flush();
00162       m_folderStream->close();  
00163       delete m_folderStream;
00164     }
00165     m_folderStream = nsnull;
00166     m_ownFolderStream = PR_FALSE;
00167   }
00168   SetSummaryValid(PR_TRUE);
00169   return NS_OK;
00170 }
00171 
00172 NS_IMETHODIMP nsMailDatabase::DeleteMessages(nsMsgKeyArray* nsMsgKeys, nsIDBChangeListener *instigator)
00173 {
00174   if (!m_folderStream && m_folder)
00175   {
00176     PRBool isLocked;
00177     m_folder->GetLocked(&isLocked);
00178     if (isLocked)
00179     {
00180       NS_ASSERTION(PR_FALSE, "Some other operation is in progress");
00181       return NS_MSG_FOLDER_BUSY;
00182     }
00183     m_folderStream = new nsIOFileStream(nsFileSpec(*m_folderSpec));
00184     m_ownFolderStream = PR_TRUE;
00185   }
00186 
00187   nsresult rv = nsMsgDatabase::DeleteMessages(nsMsgKeys, instigator);
00188   if (m_ownFolderStream)//only if we own the stream, then we should close it
00189   {
00190     if (m_folderStream)
00191     {
00192       m_folderStream->flush(); // this does a sync
00193       m_folderStream->close();
00194       delete m_folderStream;
00195     }
00196     m_folderStream = nsnull;
00197     m_ownFolderStream = PR_FALSE;
00198   }
00199 
00200   SetFolderInfoValid(m_folderSpec, 0, 0);
00201   return rv;
00202 }
00203 
00204 // Helper routine - lowest level of flag setting
00205 PRBool nsMailDatabase::SetHdrFlag(nsIMsgDBHdr *msgHdr, PRBool bSet, MsgFlags flag)
00206 {
00207   nsIOFileStream *fileStream = nsnull;
00208   PRBool ret = PR_FALSE;
00209 
00210   if (!m_folderStream && m_folder)  //we are going to create a stream, bail out if someone else has lock
00211   {
00212     PRBool isLocked;
00213     m_folder->GetLocked(&isLocked);
00214     if (isLocked)
00215     {
00216       NS_ASSERTION(PR_FALSE, "Some other operation is in progress");
00217       return PR_FALSE;
00218     }
00219   }
00220   if (nsMsgDatabase::SetHdrFlag(msgHdr, bSet, flag))
00221   {
00222     UpdateFolderFlag(msgHdr, bSet, flag, &fileStream);
00223     if (fileStream)
00224     {
00225       fileStream->flush();
00226       fileStream->close();
00227       delete fileStream;
00228       SetFolderInfoValid(m_folderSpec, 0, 0);
00229     }
00230     ret = PR_TRUE;
00231   }
00232 
00233   return ret;
00234 }
00235 
00236 #ifdef XP_MAC
00237 extern PRFileDesc *gIncorporateFID;
00238 extern const char* gIncorporatePath;
00239 #endif // XP_MAC
00240 
00241 // ### should move this into some utils class...
00242 int msg_UnHex(char C)
00243 {
00244        return ((C >= '0' && C <= '9') ? C - '0' :
00245                      ((C >= 'A' && C <= 'F') ? C - 'A' + 10 :
00246                       ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)));
00247 }
00248 
00249 
00250 // We let the caller close the file in case he's updating a lot of flags
00251 // and we don't want to open and close the file every time through.
00252 // As an experiment, try caching the fid in the db as m_folderFile.
00253 // If this is set, use it but don't return *pFid.
00254 void nsMailDatabase::UpdateFolderFlag(nsIMsgDBHdr *mailHdr, PRBool bSet, 
00255                                                    MsgFlags flag, nsIOFileStream **ppFileStream)
00256 {
00257   static char buf[50];
00258   PRInt32 folderStreamPos = 0; //saves the folderStream pos in case we are sharing the stream with other code
00259   nsIOFileStream *fileStream = (m_folderStream) ? m_folderStream : *ppFileStream;
00260   //#ifdef GET_FILE_STUFF_TOGETHER
00261 #ifdef XP_MAC
00262   /* ducarroz: Do we still need this ??
00263   // This is a horrible hack and we should make sure we don't need it anymore.
00264   // It has to do with multiple people having the same file open, I believe, but the
00265   // mac file system only has one handle, and they compete for the file position.
00266   // Prevent closing the file from under the incorporate stuff. #82785.
00267   int32 savedPosition = -1;
00268   if (!fid && gIncorporatePath && !strcmp(m_folderSpec, gIncorporatePath))
00269   {
00270               fid = gIncorporateFID;
00271                 savedPosition = ftell(gIncorporateFID); // so we can restore it.
00272                 }
00273   */
00274 #endif // XP_MAC
00275   PRUint32 offset;
00276   (void)mailHdr->GetStatusOffset(&offset);
00277   if (offset > 0) 
00278   {
00279     
00280     if (fileStream == NULL) 
00281     {
00282       fileStream = new nsIOFileStream(nsFileSpec(*m_folderSpec));
00283     }
00284     else if (!m_ownFolderStream)
00285     {
00286       m_folderStream->flush();
00287       folderStreamPos = m_folderStream->tell();
00288     }
00289     if (fileStream) 
00290     {
00291       PRUint32 msgOffset;
00292       (void)mailHdr->GetMessageOffset(&msgOffset);
00293       PRUint32 statusPos = offset + msgOffset;
00294       PR_ASSERT(offset < 10000);
00295       fileStream->seek(PR_SEEK_SET, statusPos);
00296       buf[0] = '\0';
00297       if (fileStream->readline(buf, sizeof(buf))) 
00298       {
00299         if (strncmp(buf, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN) == 0 &&
00300           strncmp(buf + X_MOZILLA_STATUS_LEN, ": ", 2) == 0 &&
00301           strlen(buf) >= X_MOZILLA_STATUS_LEN + 6) 
00302         {
00303           PRUint32 flags;
00304           (void)mailHdr->GetFlags(&flags);
00305           if (!(flags & MSG_FLAG_EXPUNGED))
00306           {
00307             int i;
00308             char *p = buf + X_MOZILLA_STATUS_LEN + 2;
00309             
00310             for (i=0, flags = 0; i<4; i++, p++)
00311             {
00312               flags = (flags << 4) | msg_UnHex(*p);
00313             }
00314             
00315             PRUint32 curFlags;
00316             (void)mailHdr->GetFlags(&curFlags);
00317             flags = (flags & MSG_FLAG_QUEUED) |
00318               (curFlags & ~MSG_FLAG_RUNTIME_ONLY);
00319           }
00320           else
00321           {
00322             flags &= ~MSG_FLAG_RUNTIME_ONLY;
00323           }
00324           fileStream->seek(statusPos);
00325           // We are filing out x-mozilla-status flags here
00326           PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS_FORMAT,
00327             flags & 0x0000FFFF);
00328           PRInt32 lineLen = PL_strlen(buf);
00329           PRUint32 status2Pos = statusPos + lineLen + MSG_LINEBREAK_LEN;
00330           fileStream->write(buf, lineLen);
00331           
00332           // time to upate x-mozilla-status2
00333           fileStream->seek(status2Pos);
00334           if (fileStream->readline(buf, sizeof(buf))) 
00335           {
00336             if (strncmp(buf, X_MOZILLA_STATUS2, X_MOZILLA_STATUS2_LEN) == 0 &&
00337               strncmp(buf + X_MOZILLA_STATUS2_LEN, ": ", 2) == 0 &&
00338               strlen(buf) >= X_MOZILLA_STATUS2_LEN + 10) 
00339             {
00340               PRUint32 dbFlags;
00341               (void)mailHdr->GetFlags(&dbFlags);
00342               dbFlags &= 0xFFFF0000;
00343               fileStream->seek(status2Pos);
00344               PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS2_FORMAT, dbFlags);
00345               fileStream->write(buf, PL_strlen(buf));
00346             }
00347           }
00348         } else 
00349         {
00350 #ifdef DEBUG
00351           printf("Didn't find %s where expected at position %ld\n"
00352             "instead, found %s.\n",
00353             X_MOZILLA_STATUS, (long) statusPos, buf);
00354 #endif
00355           SetReparse(PR_TRUE);
00356         }                   
00357       } 
00358       else 
00359       {
00360 #ifdef DEBUG
00361         printf("Couldn't read old status line at all at position %ld\n",
00362           (long) statusPos);
00363 #endif
00364         SetReparse(PR_TRUE);
00365       }
00366 #ifdef XP_MAC
00367       /* ducarroz: Do we still need this ??
00368       // Restore the file position
00369       if (savedPosition >= 0)
00370       XP_FileSeek(fid, savedPosition, SEEK_SET);
00371       */
00372 #endif
00373     }
00374     else
00375     {
00376 #ifdef DEBUG
00377       printf("Couldn't open mail folder for update%s!\n",
00378         (const char*)m_folderSpec);
00379 #endif
00380       PR_ASSERT(PR_FALSE);
00381     }
00382   }
00383   //#endif // GET_FILE_STUFF_TOGETHER
00384   if (!m_folderStream)
00385     *ppFileStream = fileStream; // This tells the caller that we opened the file, and please to close it.
00386   else if (!m_ownFolderStream)
00387     m_folderStream->seek(PR_SEEK_SET, folderStreamPos);
00388 }
00389 
00390 PRUint32 nsMailDatabase::GetMailboxModDate()
00391 {
00392   PRUint32 retModTime = 0;
00393   nsCOMPtr <nsILocalFile> localFile;
00394   PRInt64 lastModTime;
00395   nsresult rv = NS_FileSpecToIFile(m_folderSpec, getter_AddRefs(localFile));
00396   if (NS_SUCCEEDED(rv))
00397   {
00398     rv = localFile->GetLastModifiedTime(&lastModTime);
00399     if (NS_SUCCEEDED(rv))
00400     {
00401 
00402       PRTime  temp64;
00403       PRInt64 thousand;
00404       LL_I2L(thousand, PR_MSEC_PER_SEC);
00405       LL_DIV(temp64, lastModTime, thousand);
00406       LL_L2UI(retModTime, temp64);
00407     }
00408   }
00409   if (!retModTime)
00410     m_folderSpec->GetModDate(retModTime) ;
00411 
00412   return retModTime;
00413 }
00414 
00415 NS_IMETHODIMP nsMailDatabase::GetSummaryValid(PRBool *aResult)
00416 {
00417   NS_ENSURE_ARG_POINTER(aResult);
00418   PRUint32 folderSize;
00419   PRUint32  folderDate;
00420   nsFileSpec::TimeStamp actualFolderTimeStamp;
00421   PRInt32 numUnreadMessages;
00422   nsAutoString errorMsg;
00423 
00424         
00425   *aResult = PR_FALSE;
00426   
00427   if (m_folderSpec && m_dbFolderInfo)
00428   {
00429     actualFolderTimeStamp = GetMailboxModDate();
00430   
00431     m_dbFolderInfo->GetNumUnreadMessages(&numUnreadMessages);
00432     m_dbFolderInfo->GetFolderSize(&folderSize);
00433     m_dbFolderInfo->GetFolderDate(&folderDate);
00434 
00435     // compare current version of db versus filed out version info, 
00436     // and file size in db vs file size on disk.
00437     PRUint32 version;
00438 
00439     m_dbFolderInfo->GetVersion(&version);
00440     if (folderSize == m_folderSpec->GetFileSize() &&
00441         numUnreadMessages >= 0 && GetCurVersion() == version)
00442     {
00443       GetGlobalPrefs();
00444       // if those values are ok, check time stamp
00445       if (gTimeStampLeeway == 0)
00446         *aResult = folderDate == actualFolderTimeStamp;
00447       else
00448         *aResult = PR_ABS((PRInt32) (actualFolderTimeStamp - folderDate)) <= gTimeStampLeeway;
00449 #ifndef PUTUP_ALERT_ON_INVALID_DB
00450     }
00451   }
00452 #else
00453       if (!*aResult)
00454       {
00455         errorMsg.AppendLiteral("time stamp didn't match delta = ");
00456         errorMsg.AppendInt(actualFolderTimeStamp - folderDate);
00457         errorMsg.AppendLiteral(" leeway = ");
00458         errorMsg.AppendInt(gTimeStampLeeway);
00459       }
00460     }
00461     else if (folderSize != m_folderSpec->GetFileSize())
00462     {
00463       errorMsg.AppendLiteral("folder size didn't match db size = ");
00464       errorMsg.AppendInt(folderSize);
00465       errorMsg.AppendLiteral(" actual size = ");
00466       errorMsg.AppendInt(m_folderSpec->GetFileSize());
00467     }
00468     else if (numUnreadMessages < 0)
00469     {
00470       errorMsg.AppendLiteral("numUnreadMessages < 0");
00471     }
00472   }
00473   if (errorMsg.Length())
00474   {
00475     nsCOMPtr<nsIPrompt> dialog;
00476 
00477     nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
00478     if (wwatch)
00479       wwatch->GetNewPrompter(0, getter_AddRefs(dialog));
00480     if (dialog)
00481       dialog->Alert(nsnull, errorMsg.get());
00482   }
00483 #endif // PUTUP_ALERT_ON_INVALID_DB
00484   return NS_OK;
00485 }
00486 
00487 NS_IMETHODIMP nsMailDatabase::SetSummaryValid(PRBool valid)
00488 {
00489   nsresult ret = NS_OK;
00490   
00491   if (!m_folderSpec->Exists()) 
00492     return NS_MSG_ERROR_FOLDER_MISSING;
00493   
00494   if (m_dbFolderInfo)
00495   {
00496     if (valid)
00497     {
00498       nsFileSpec::TimeStamp actualFolderTimeStamp = GetMailboxModDate();
00499       
00500       m_dbFolderInfo->SetFolderSize(m_folderSpec->GetFileSize());
00501       m_dbFolderInfo->SetFolderDate(actualFolderTimeStamp);
00502       m_dbFolderInfo->SetVersion(GetCurVersion());
00503     }
00504     else
00505     {
00506       m_dbFolderInfo->SetVersion(0);      // that ought to do the trick.
00507     }
00508   }
00509   Commit(nsMsgDBCommitType::kLargeCommit);
00510   return ret;
00511 }
00512 
00513 nsresult nsMailDatabase::GetFolderName(nsString &folderName)
00514 {
00515        folderName.AssignWithConversion(NS_STATIC_CAST(const char*, *m_folderSpec));
00516        return NS_OK;
00517 }
00518 
00519 
00520 NS_IMETHODIMP  nsMailDatabase::RemoveOfflineOp(nsIMsgOfflineImapOperation *op)
00521 {
00522   
00523   nsresult rv = GetAllOfflineOpsTable();
00524   NS_ENSURE_SUCCESS(rv, rv);
00525   
00526   if (!op || !m_mdbAllOfflineOpsTable)
00527     return NS_ERROR_NULL_POINTER;
00528   nsMsgOfflineImapOperation* offlineOp = NS_STATIC_CAST(nsMsgOfflineImapOperation*, op);  // closed system, so this is ok
00529   nsIMdbRow* row = offlineOp->GetMDBRow();
00530   rv = m_mdbAllOfflineOpsTable->CutRow(GetEnv(), row);
00531   row->CutAllColumns(GetEnv());
00532   return rv;
00533 }
00534 
00535 NS_IMETHODIMP nsMailDatabase::GetOfflineOpForKey(nsMsgKey msgKey, PRBool create, nsIMsgOfflineImapOperation **offlineOp)
00536 {
00537   mdb_bool    hasOid;
00538   mdbOid             rowObjectId;
00539   mdb_err   err;
00540   
00541   if (!IMAPOffline)
00542     IMAPOffline = PR_NewLogModule("IMAPOFFLINE");
00543   nsresult rv = GetAllOfflineOpsTable();
00544   NS_ENSURE_SUCCESS(rv, rv);
00545   
00546   if (!offlineOp || !m_mdbAllOfflineOpsTable)
00547     return NS_ERROR_NULL_POINTER;
00548   
00549   *offlineOp = NULL;
00550   
00551   rowObjectId.mOid_Id = msgKey;
00552   rowObjectId.mOid_Scope = m_offlineOpsRowScopeToken;
00553   err = m_mdbAllOfflineOpsTable->HasOid(GetEnv(), &rowObjectId, &hasOid);
00554   if (err == NS_OK && m_mdbStore && (hasOid  || create))
00555   {
00556     nsCOMPtr <nsIMdbRow> offlineOpRow;
00557     err = m_mdbStore->GetRow(GetEnv(), &rowObjectId, getter_AddRefs(offlineOpRow));
00558     
00559     if (create)
00560     {
00561       if (!offlineOpRow)
00562       {
00563         err  = m_mdbStore->NewRowWithOid(GetEnv(), &rowObjectId, getter_AddRefs(offlineOpRow));
00564         NS_ENSURE_SUCCESS(err, err);
00565       }
00566       if (offlineOpRow && !hasOid)
00567         m_mdbAllOfflineOpsTable->AddRow(GetEnv(), offlineOpRow);
00568     }
00569     
00570     if (err == NS_OK && offlineOpRow)
00571     {
00572       *offlineOp = new nsMsgOfflineImapOperation(this, offlineOpRow);
00573       if (*offlineOp)
00574         (*offlineOp)->SetMessageKey(msgKey);
00575       NS_IF_ADDREF(*offlineOp);
00576     }
00577     if (!hasOid && m_dbFolderInfo)
00578     {
00579       // set initial value for flags so we don't lose them.
00580       nsCOMPtr <nsIMsgDBHdr> msgHdr;
00581       GetMsgHdrForKey(msgKey, getter_AddRefs(msgHdr));
00582       if (msgHdr)
00583       {
00584         PRUint32 flags;
00585         msgHdr->GetFlags(&flags);
00586         (*offlineOp)->SetNewFlags(flags);
00587       }
00588       PRInt32 newFlags;
00589       m_dbFolderInfo->OrFlags(MSG_FOLDER_FLAG_OFFLINEEVENTS, &newFlags);
00590     }
00591   }
00592   
00593   return (err == 0) ? NS_OK : NS_ERROR_FAILURE;
00594 
00595 }
00596 
00597 NS_IMETHODIMP nsMailDatabase::EnumerateOfflineOps(nsISimpleEnumerator **enumerator)
00598 {
00599   NS_ASSERTION(PR_FALSE, "not impl yet");
00600   return NS_ERROR_NOT_IMPLEMENTED;
00601 }
00602 
00603 
00604 NS_IMETHODIMP nsMailDatabase::ListAllOfflineOpIds(nsMsgKeyArray *offlineOpIds)
00605 {
00606   NS_ENSURE_ARG(offlineOpIds);
00607   nsresult rv = GetAllOfflineOpsTable();
00608   NS_ENSURE_SUCCESS(rv, rv);
00609   nsIMdbTableRowCursor *rowCursor;
00610   if (!IMAPOffline)
00611     IMAPOffline = PR_NewLogModule("IMAPOFFLINE");
00612 
00613   if (m_mdbAllOfflineOpsTable)
00614   {
00615     nsresult err = m_mdbAllOfflineOpsTable->GetTableRowCursor(GetEnv(), -1, &rowCursor);
00616     while (err == NS_OK && rowCursor)
00617     {
00618       mdbOid outOid;
00619       mdb_pos outPos;
00620       
00621       err = rowCursor->NextRowOid(GetEnv(), &outOid, &outPos);
00622       // is this right? Mork is returning a 0 id, but that should valid.
00623       if (outPos < 0 || outOid.mOid_Id == (mdb_id) -1)  
00624         break;
00625       if (err == NS_OK)
00626       {
00627         offlineOpIds->Add(outOid.mOid_Id);
00628         if (PR_LOG_TEST(IMAPOffline, PR_LOG_ALWAYS))
00629         {
00630           nsCOMPtr <nsIMsgOfflineImapOperation> offlineOp;
00631           GetOfflineOpForKey(outOid.mOid_Id, PR_FALSE, getter_AddRefs(offlineOp));
00632           if (offlineOp)
00633           {
00634             nsMsgOfflineImapOperation *logOp = NS_STATIC_CAST(nsMsgOfflineImapOperation *, 
00635               NS_STATIC_CAST(nsIMsgOfflineImapOperation *, offlineOp.get()));
00636             if (logOp)
00637               logOp->Log(IMAPOffline);
00638 
00639           }
00640         }
00641       }
00642     }
00643     rv = (err == NS_OK) ? NS_OK : NS_ERROR_FAILURE;
00644     rowCursor->Release();
00645   }
00646   
00647   offlineOpIds->QuickSort();
00648   return rv;
00649 }
00650 
00651 NS_IMETHODIMP nsMailDatabase::ListAllOfflineDeletes(nsMsgKeyArray *offlineDeletes)
00652 {
00653   if (!offlineDeletes)
00654     return NS_ERROR_NULL_POINTER;
00655   
00656   nsresult rv = GetAllOfflineOpsTable();
00657   NS_ENSURE_SUCCESS(rv, rv);
00658   nsIMdbTableRowCursor *rowCursor;
00659   if (m_mdbAllOfflineOpsTable)
00660   {
00661     nsresult err = m_mdbAllOfflineOpsTable->GetTableRowCursor(GetEnv(), -1, &rowCursor);
00662     while (err == NS_OK && rowCursor)
00663     {
00664       mdbOid outOid;
00665       mdb_pos outPos;
00666       nsIMdbRow* offlineOpRow;
00667       
00668       err = rowCursor->NextRow(GetEnv(), &offlineOpRow, &outPos);
00669       // is this right? Mork is returning a 0 id, but that should valid.
00670       if (outPos < 0 || offlineOpRow == nsnull)  
00671         break;
00672       if (err == NS_OK)
00673       {
00674         offlineOpRow->GetOid(GetEnv(), &outOid);
00675         nsIMsgOfflineImapOperation *offlineOp = new nsMsgOfflineImapOperation(this, offlineOpRow);
00676         if (offlineOp)
00677         {
00678           NS_ADDREF(offlineOp);
00679           imapMessageFlagsType newFlags;
00680           nsOfflineImapOperationType opType;
00681           
00682           offlineOp->GetOperation(&opType);
00683           offlineOp->GetNewFlags(&newFlags);
00684           if (opType & nsIMsgOfflineImapOperation::kMsgMoved || 
00685             ((opType & nsIMsgOfflineImapOperation::kFlagsChanged) 
00686             && (newFlags & nsIMsgOfflineImapOperation::kMsgMarkedDeleted)))
00687             offlineDeletes->Add(outOid.mOid_Id);
00688           NS_RELEASE(offlineOp);
00689         }
00690         offlineOpRow->Release();
00691       }
00692     }
00693     rv = (err == NS_OK) ? NS_OK : NS_ERROR_FAILURE;
00694     rowCursor->Release();
00695   }
00696   return rv;
00697 }
00698 
00699 /* static */
00700 nsresult nsMailDatabase::SetFolderInfoValid(nsFileSpec *folderName, int num, int numunread)
00701 {
00702   nsLocalFolderSummarySpec  summarySpec(*folderName);
00703   nsFileSpec                              summaryPath(summarySpec);
00704   nsresult           err = NS_OK;
00705   PRBool                    bOpenedDB = PR_FALSE;
00706   
00707   if (!folderName->Exists())
00708     return NS_MSG_ERROR_FOLDER_SUMMARY_MISSING;
00709   
00710   // should we have type safe downcast methods again?
00711   nsMailDatabase *pMessageDB = (nsMailDatabase *) nsMailDatabase::FindInCache(summaryPath);
00712   if (pMessageDB == nsnull)
00713   {
00714     pMessageDB = new nsMailDatabase();
00715     if(!pMessageDB)
00716       return NS_ERROR_OUT_OF_MEMORY;
00717     
00718     pMessageDB->m_folderSpec = new nsLocalFolderSummarySpec();
00719     if(!pMessageDB->m_folderSpec)
00720     {
00721       delete pMessageDB;
00722       return NS_ERROR_OUT_OF_MEMORY;
00723     }
00724     
00725     *(pMessageDB->m_folderSpec) = summarySpec;
00726     // ### this does later stuff (marks latered messages unread), which may be a problem
00727     err = pMessageDB->OpenMDB(summaryPath, PR_FALSE);
00728     if (err != NS_OK)
00729     {
00730       delete pMessageDB;
00731       pMessageDB = nsnull;
00732     }
00733     bOpenedDB = PR_TRUE;
00734   }
00735   
00736   if (pMessageDB == nsnull)
00737   {
00738 #ifdef DEBUG
00739     printf("Exception opening summary file\n");
00740 #endif
00741     return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
00742   }
00743   
00744   {
00745     pMessageDB->m_folderSpec = folderName;
00746     nsFileSpec::TimeStamp actualFolderTimeStamp = pMessageDB->GetMailboxModDate();
00747     pMessageDB->m_dbFolderInfo->SetFolderSize(folderName->GetFileSize());
00748     pMessageDB->m_dbFolderInfo->SetFolderDate(actualFolderTimeStamp);
00749     pMessageDB->m_dbFolderInfo->ChangeNumUnreadMessages(numunread);
00750     pMessageDB->m_dbFolderInfo->ChangeNumMessages(num);
00751   }
00752   // if we opened the db, then we'd better close it. Otherwise, we found it in the cache,
00753   // so just commit and release.
00754   if (bOpenedDB)
00755   {
00756     pMessageDB->Close(PR_TRUE);
00757   }
00758   else if (pMessageDB)
00759   { 
00760     err = pMessageDB->Commit(nsMsgDBCommitType::kLargeCommit);
00761     pMessageDB->Release();
00762   }
00763   return err;
00764 }
00765 
00766 
00767 // This is used to remember that the db is out of sync with the mail folder
00768 // and needs to be regenerated.
00769 void nsMailDatabase::SetReparse(PRBool reparse)
00770 {
00771   m_reparse = reparse;
00772 }
00773 
00774 
00775 class nsMsgOfflineOpEnumerator : public nsISimpleEnumerator {
00776 public:
00777   NS_DECL_ISUPPORTS
00778 
00779   // nsISimpleEnumerator methods:
00780   NS_DECL_NSISIMPLEENUMERATOR
00781 
00782   nsMsgOfflineOpEnumerator(nsMailDatabase* db);
00783   virtual ~nsMsgOfflineOpEnumerator();
00784 
00785 protected:
00786   nsresult                                GetRowCursor();
00787   nsresult                                PrefetchNext();
00788   nsMailDatabase*              mDB;
00789   nsIMdbTableRowCursor*       mRowCursor;
00790   nsCOMPtr <nsIMsgOfflineImapOperation> mResultOp;
00791   PRBool            mDone;
00792   PRBool                                         mNextPrefetched;
00793 };
00794 
00795 nsMsgOfflineOpEnumerator::nsMsgOfflineOpEnumerator(nsMailDatabase* db)
00796     : mDB(db), mRowCursor(nsnull), mDone(PR_FALSE)
00797 {
00798   NS_ADDREF(mDB);
00799   mNextPrefetched = PR_FALSE;
00800 }
00801 
00802 nsMsgOfflineOpEnumerator::~nsMsgOfflineOpEnumerator()
00803 {
00804   NS_IF_RELEASE(mRowCursor);
00805   NS_RELEASE(mDB);
00806 }
00807 
00808 NS_IMPL_ISUPPORTS1(nsMsgOfflineOpEnumerator, nsISimpleEnumerator)
00809 
00810 nsresult nsMsgOfflineOpEnumerator::GetRowCursor()
00811 {
00812   nsresult rv = 0;
00813   mDone = PR_FALSE;
00814 
00815   if (!mDB || !mDB->m_mdbAllOfflineOpsTable)
00816     return NS_ERROR_NULL_POINTER;
00817 
00818   rv = mDB->m_mdbAllOfflineOpsTable->GetTableRowCursor(mDB->GetEnv(), -1, &mRowCursor);
00819   return rv;
00820 }
00821 
00822 NS_IMETHODIMP nsMsgOfflineOpEnumerator::GetNext(nsISupports **aItem)
00823 {
00824   if (!aItem)
00825     return NS_ERROR_NULL_POINTER;
00826   nsresult rv=NS_OK;
00827   if (!mNextPrefetched)
00828     rv = PrefetchNext();
00829   if (NS_SUCCEEDED(rv))
00830   {
00831     if (mResultOp) 
00832     {
00833       *aItem = mResultOp;
00834       NS_ADDREF(*aItem);
00835       mNextPrefetched = PR_FALSE;
00836     }
00837   }
00838   return rv;
00839 }
00840 
00841 nsresult nsMsgOfflineOpEnumerator::PrefetchNext()
00842 {
00843   nsresult rv = NS_OK;
00844   nsIMdbRow* offlineOpRow;
00845   mdb_pos rowPos;
00846 
00847   if (!mRowCursor)
00848   {
00849     rv = GetRowCursor();
00850     if (NS_FAILED(rv))
00851       return rv;
00852   }
00853 
00854   rv = mRowCursor->NextRow(mDB->GetEnv(), &offlineOpRow, &rowPos);
00855   if (!offlineOpRow) 
00856   {
00857     mDone = PR_TRUE;
00858     return NS_ERROR_FAILURE;
00859   }
00860   if (NS_FAILED(rv)) 
00861   {
00862     mDone = PR_TRUE;
00863     return rv;
00864   }
00865        //Get key from row
00866   mdbOid outOid;
00867   nsMsgKey key=0;
00868   if (offlineOpRow->GetOid(mDB->GetEnv(), &outOid) == NS_OK)
00869     key = outOid.mOid_Id;
00870 
00871   nsIMsgOfflineImapOperation *op = new nsMsgOfflineImapOperation(mDB, offlineOpRow);
00872   mResultOp = op;
00873   if (!op)
00874     return NS_ERROR_OUT_OF_MEMORY;
00875 
00876   if (mResultOp) 
00877   {
00878     mNextPrefetched = PR_TRUE;
00879     return NS_OK;
00880   }
00881   return NS_ERROR_FAILURE;
00882 }
00883 
00884 NS_IMETHODIMP nsMsgOfflineOpEnumerator::HasMoreElements(PRBool *aResult)
00885 {
00886   if (!aResult)
00887     return NS_ERROR_NULL_POINTER;
00888 
00889   if (!mNextPrefetched)
00890     PrefetchNext();
00891   *aResult = !mDone;
00892   return NS_OK;
00893 }