Back to index

lightning-sunbird  0.9+nobinonly
nsNewsDatabase.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Seth Spitzer <sspitzer@netscape.com>
00024  *   Pierre Phaneuf <pp@ludusdesign.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "msgCore.h"
00041 #include "nsNewsDatabase.h"
00042 #include "nsMsgKeySet.h"
00043 #include "nsCOMPtr.h"
00044 #include "prlog.h"
00045 #include "nsIFileSpec.h"
00046 
00047 #if defined(DEBUG_sspitzer_) || defined(DEBUG_seth_)
00048 #define DEBUG_NEWS_DATABASE 1
00049 #endif
00050 
00051 nsNewsDatabase::nsNewsDatabase()
00052 {
00053   m_readSet = nsnull;
00054 }
00055 
00056 nsNewsDatabase::~nsNewsDatabase()
00057 {
00058 }
00059 
00060 NS_IMPL_ADDREF_INHERITED(nsNewsDatabase, nsMsgDatabase)
00061 NS_IMPL_RELEASE_INHERITED(nsNewsDatabase, nsMsgDatabase)  
00062 
00063 NS_IMETHODIMP nsNewsDatabase::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00064 {
00065   if (!aInstancePtr) return NS_ERROR_NULL_POINTER;
00066   *aInstancePtr = nsnull;   
00067   
00068   if (aIID.Equals(NS_GET_IID(nsINewsDatabase)))
00069   {
00070     *aInstancePtr = NS_STATIC_CAST(nsINewsDatabase *, this);
00071   }
00072   
00073   if(*aInstancePtr)
00074   {
00075     AddRef();
00076     return NS_OK;
00077   }     
00078   
00079   return nsMsgDatabase::QueryInterface(aIID, aInstancePtr);
00080 }
00081 
00082 nsresult nsNewsDatabase::Close(PRBool forceCommit)
00083 {
00084   return nsMsgDatabase::Close(forceCommit);
00085 }
00086 
00087 nsresult nsNewsDatabase::ForceClosed()
00088 {
00089   return nsMsgDatabase::ForceClosed();
00090 }
00091 
00092 nsresult nsNewsDatabase::Commit(nsMsgDBCommit commitType)
00093 {
00094   if (m_dbFolderInfo && m_readSet)
00095   {
00096     // let's write out our idea of the read set so we can compare it with that of
00097     // the .rc file next time we start up.
00098     nsXPIDLCString readSet;
00099     m_readSet->Output(getter_Copies(readSet));
00100     m_dbFolderInfo->SetCharPtrProperty("readSet", readSet.get());
00101   }
00102   return nsMsgDatabase::Commit(commitType);
00103 }
00104 
00105 
00106 PRUint32 nsNewsDatabase::GetCurVersion()
00107 {
00108   return kMsgDBVersion;
00109 }
00110 
00111 NS_IMETHODIMP nsNewsDatabase::IsRead(nsMsgKey key, PRBool *pRead)
00112 {
00113   NS_ASSERTION(pRead, "null out param in IsRead");
00114   if (!pRead) return NS_ERROR_NULL_POINTER;
00115 
00116   if (!m_readSet) return NS_ERROR_FAILURE;
00117   
00118   *pRead = m_readSet->IsMember(key);
00119   return NS_OK;
00120 }
00121 
00122 nsresult nsNewsDatabase::IsHeaderRead(nsIMsgDBHdr *msgHdr, PRBool *pRead)
00123 {
00124     nsresult rv;
00125     nsMsgKey messageKey;
00126 
00127     if (!msgHdr || !pRead) return NS_ERROR_NULL_POINTER;
00128 
00129     rv = msgHdr->GetMessageKey(&messageKey);
00130     if (NS_FAILED(rv)) return rv;
00131 
00132     rv = IsRead(messageKey,pRead);
00133     return rv;
00134 }
00135 
00136 // return highest article number we've seen.
00137 NS_IMETHODIMP nsNewsDatabase::GetHighWaterArticleNum(nsMsgKey *key)
00138 {
00139   NS_ASSERTION(m_dbFolderInfo, "null db folder info");
00140   if (!m_dbFolderInfo) 
00141     return NS_ERROR_FAILURE;
00142   return m_dbFolderInfo->GetHighWater(key);    
00143 }
00144 
00145 // return the key of the first article number we know about.
00146 // Since the iterator iterates in id order, we can just grab the
00147 // messagekey of the first header it returns.
00148 // ### dmb
00149 // This will not deal with the situation where we get holes in
00150 // the headers we know about. Need to figure out how and when
00151 // to solve that. This could happen if a transfer is interrupted.
00152 // Do we need to keep track of known arts permanently?
00153 NS_IMETHODIMP nsNewsDatabase::GetLowWaterArticleNum(nsMsgKey *key)
00154 {
00155   nsresult  rv;
00156   nsMsgHdr  *pHeader;
00157 
00158   nsCOMPtr<nsISimpleEnumerator> hdrs;
00159   rv = EnumerateMessages(getter_AddRefs(hdrs));
00160   if (NS_FAILED(rv))
00161     return rv;
00162 
00163   rv = hdrs->GetNext((nsISupports**)&pHeader);
00164   NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
00165   if (NS_FAILED(rv)) 
00166     return rv;
00167   
00168   return pHeader->GetMessageKey(key);
00169 }
00170  
00171 nsresult  nsNewsDatabase::ExpireUpTo(nsMsgKey expireKey)
00172 {
00173   return NS_ERROR_NOT_IMPLEMENTED;
00174 }
00175 nsresult             nsNewsDatabase::ExpireRange(nsMsgKey startRange, nsMsgKey endRange)
00176 {
00177   return NS_ERROR_NOT_IMPLEMENTED;
00178 }
00179 
00180 
00181 NS_IMETHODIMP nsNewsDatabase::GetReadSet(nsMsgKeySet **pSet)
00182 {
00183     if (!pSet) return NS_ERROR_NULL_POINTER;
00184     *pSet = m_readSet;
00185     return NS_OK;
00186 }
00187 
00188 NS_IMETHODIMP nsNewsDatabase::SetReadSet(nsMsgKeySet *pSet)
00189 {
00190   m_readSet = pSet;
00191 
00192   if (m_readSet)
00193   {
00194     // compare this read set with the one in the db folder info.
00195     // If not equivalent, sync with this one.
00196     nsXPIDLCString dbReadSet;
00197     if (m_dbFolderInfo)
00198       m_dbFolderInfo->GetCharPtrProperty("readSet", getter_Copies(dbReadSet));
00199     nsXPIDLCString newsrcReadSet;
00200     m_readSet->Output(getter_Copies(newsrcReadSet));
00201     if (!dbReadSet.Equals(newsrcReadSet))
00202       SyncWithReadSet();
00203   }
00204   return NS_OK;
00205 }
00206  
00207  
00208 PRBool nsNewsDatabase::SetHdrReadFlag(nsIMsgDBHdr *msgHdr, PRBool bRead)
00209 {
00210     nsresult rv;
00211     PRBool isRead;
00212     rv = IsHeaderRead(msgHdr, &isRead);
00213     
00214     if (isRead == bRead) 
00215     {
00216       // give the base class a chance to update m_flags.
00217       nsMsgDatabase::SetHdrReadFlag(msgHdr, bRead);
00218       return PR_FALSE;
00219     }
00220     else {
00221       nsMsgKey messageKey;
00222 
00223       // give the base class a chance to update m_flags.
00224       nsMsgDatabase::SetHdrReadFlag(msgHdr, bRead);
00225       rv = msgHdr->GetMessageKey(&messageKey);
00226       if (NS_FAILED(rv)) return PR_FALSE;
00227 
00228       NS_ASSERTION(m_readSet, "m_readSet is null");
00229       if (!m_readSet) return PR_FALSE;
00230 
00231       if (!bRead) {
00232 #ifdef DEBUG_NEWS_DATABASE
00233         printf("remove %d from the set\n",messageKey);
00234 #endif
00235 
00236         rv = m_readSet->Remove(messageKey);
00237         if (NS_FAILED(rv)) return PR_FALSE;
00238 
00239         rv = NotifyReadChanged(nsnull);
00240         if (NS_FAILED(rv)) return PR_FALSE;
00241       }
00242       else {
00243 #ifdef DEBUG_NEWS_DATABASE
00244         printf("add %d to the set\n",messageKey);
00245 #endif
00246 
00247         rv = m_readSet->Add(messageKey);
00248         if (NS_FAILED(rv)) return PR_FALSE;
00249 
00250         rv = NotifyReadChanged(nsnull);
00251         if (NS_FAILED(rv)) return PR_FALSE;
00252       }
00253     }
00254     return PR_TRUE;
00255 }
00256 
00257 NS_IMETHODIMP nsNewsDatabase::MarkAllRead(nsMsgKeyArray *thoseMarked)
00258 {
00259   nsMsgKey lowWater = nsMsgKey_None, highWater;
00260   nsXPIDLCString knownArts;
00261   if (m_dbFolderInfo)
00262   {
00263     m_dbFolderInfo->GetKnownArtsSet(getter_Copies(knownArts));
00264     nsMsgKeySet *knownKeys = nsMsgKeySet::Create(knownArts);
00265     if (knownKeys)
00266       lowWater = knownKeys->GetFirstMember();
00267 
00268     delete knownKeys;
00269   }
00270   if (lowWater == nsMsgKey_None)
00271     GetLowWaterArticleNum(&lowWater);
00272   GetHighWaterArticleNum(&highWater);
00273   if (lowWater > 2)
00274     m_readSet->AddRange(1, lowWater - 1);
00275   nsresult err = nsMsgDatabase::MarkAllRead(thoseMarked);
00276   if (NS_SUCCEEDED(err) && 1 <= highWater)
00277     m_readSet->AddRange(1, highWater);    // mark everything read in newsrc.
00278   
00279   return err;
00280 }
00281 
00282 nsresult nsNewsDatabase::SyncWithReadSet()
00283 {
00284 
00285   // The code below attempts to update the underlying nsMsgDatabase's idea
00286   // of read/unread flags to match the read set in the .newsrc file. It should
00287   // only be called when they don't match, e.g., we crashed after committing the
00288   // db but before writing out the .newsrc
00289   nsCOMPtr <nsISimpleEnumerator> hdrs;
00290   nsresult rv = EnumerateMessages(getter_AddRefs(hdrs));
00291   NS_ENSURE_SUCCESS(rv, rv);
00292 
00293   PRBool hasMore = PR_FALSE, readInNewsrc, isReadInDB, changed = PR_FALSE;
00294   nsCOMPtr <nsIMsgDBHdr> header;
00295   PRInt32 numMessages = 0, numUnreadMessages = 0;
00296   nsMsgKey messageKey;
00297   nsCOMPtr <nsIMsgThread> threadHdr;
00298 
00299   // Scan all messages in DB
00300   while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE))
00301   {
00302       rv = hdrs->GetNext(getter_AddRefs(header));
00303       NS_ENSURE_SUCCESS(rv, rv);
00304 
00305       rv = nsMsgDatabase::IsHeaderRead(header, &isReadInDB);
00306       NS_ENSURE_SUCCESS(rv, rv);
00307   
00308       header->GetMessageKey(&messageKey);
00309       IsRead(messageKey,&readInNewsrc);
00310 
00311       numMessages++;
00312       if (!readInNewsrc) 
00313         numUnreadMessages++;
00314 
00315       // If DB and readSet disagree on Read/Unread, fix DB
00316       if (readInNewsrc!=isReadInDB) 
00317       {
00318         MarkHdrRead(header, readInNewsrc, nsnull);
00319         changed = PR_TRUE;
00320       }
00321   }
00322   
00323   // Update FolderInfo Counters
00324   PRInt32 oldMessages, oldUnreadMessages;
00325   rv = m_dbFolderInfo->GetNumMessages(&oldMessages);
00326   if (NS_SUCCEEDED(rv) && oldMessages!=numMessages) 
00327   {
00328       changed = PR_TRUE;
00329       m_dbFolderInfo->ChangeNumMessages(numMessages-oldMessages);
00330   }
00331   rv = m_dbFolderInfo->GetNumUnreadMessages(&oldUnreadMessages);
00332   if (NS_SUCCEEDED(rv) && oldUnreadMessages!=numUnreadMessages) 
00333   {
00334       changed = PR_TRUE;
00335       m_dbFolderInfo->ChangeNumUnreadMessages(numUnreadMessages-oldUnreadMessages);
00336   }
00337 
00338   if (changed)
00339       Commit(nsMsgDBCommitType::kLargeCommit);
00340 
00341   return rv;
00342 }
00343 
00344 nsresult nsNewsDatabase::AdjustExpungedBytesOnDelete(nsIMsgDBHdr *msgHdr)
00345 {
00346   PRUint32 msgFlags;
00347   msgHdr->GetFlags(&msgFlags);
00348   if (msgFlags & MSG_FLAG_OFFLINE && m_dbFolderInfo)
00349   {
00350     PRUint32 size = 0;
00351     (void)msgHdr->GetOfflineMessageSize(&size);
00352     return m_dbFolderInfo->ChangeExpungedBytes (size);
00353   }
00354   return NS_OK;
00355 }
00356 
00357 NS_IMETHODIMP 
00358 nsNewsDatabase::GetDefaultViewFlags(nsMsgViewFlagsTypeValue *aDefaultViewFlags)
00359 {
00360   NS_ENSURE_ARG_POINTER(aDefaultViewFlags);
00361   GetIntPref("mailnews.default_news_view_flags", aDefaultViewFlags);
00362   if (*aDefaultViewFlags < nsMsgViewFlagsType::kNone ||
00363       *aDefaultViewFlags > (nsMsgViewFlagsType::kThreadedDisplay |
00364                             nsMsgViewFlagsType::kShowIgnored |
00365                             nsMsgViewFlagsType::kUnreadOnly |
00366                             nsMsgViewFlagsType::kExpandAll |
00367                             nsMsgViewFlagsType::kGroupBySort))
00368     *aDefaultViewFlags = nsMsgViewFlagsType::kThreadedDisplay;
00369   return NS_OK;
00370 }
00371 
00372 NS_IMETHODIMP 
00373 nsNewsDatabase::GetDefaultSortType(nsMsgViewSortTypeValue *aDefaultSortType)
00374 {
00375   NS_ENSURE_ARG_POINTER(aDefaultSortType);
00376   GetIntPref("mailnews.default_news_sort_type", aDefaultSortType);
00377   if (*aDefaultSortType < nsMsgViewSortType::byDate ||
00378       *aDefaultSortType > nsMsgViewSortType::byAccount)
00379     *aDefaultSortType = nsMsgViewSortType::byThread;
00380   return NS_OK;
00381 }
00382 
00383 NS_IMETHODIMP
00384 nsNewsDatabase::GetDefaultSortOrder(nsMsgViewSortOrderValue *aDefaultSortOrder)
00385 {
00386   NS_ENSURE_ARG_POINTER(aDefaultSortOrder);
00387   GetIntPref("mailnews.default_news_sort_order", aDefaultSortOrder);
00388   if (*aDefaultSortOrder != nsMsgViewSortOrder::descending)
00389     *aDefaultSortOrder = nsMsgViewSortOrder::ascending;
00390   return NS_OK;
00391 }