Back to index

lightning-sunbird  0.9+nobinonly
nsImapFlagAndUidState.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  *   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 #include "msgCore.h"  // for pre-compiled headers
00040 
00041 #include "nsImapCore.h"
00042 #include "nsImapFlagAndUidState.h"
00043 #include "prcmon.h"
00044 #include "nspr.h"
00045 #include "nsAutoLock.h"
00046 
00047 NS_IMPL_THREADSAFE_ISUPPORTS1(nsImapFlagAndUidState, nsIImapFlagAndUidState)
00048 
00049 NS_IMETHODIMP nsImapFlagAndUidState::GetNumberOfMessages(PRInt32 *result)
00050 {
00051   if (!result)
00052     return NS_ERROR_NULL_POINTER;
00053   *result = fNumberOfMessagesAdded;
00054   return NS_OK;
00055 }
00056 
00057 NS_IMETHODIMP nsImapFlagAndUidState::GetUidOfMessage(PRInt32 zeroBasedIndex, PRUint32 *result)
00058 {
00059   if (!result)
00060     return NS_ERROR_NULL_POINTER;
00061   
00062   PR_CEnterMonitor(this);
00063   if (zeroBasedIndex < fNumberOfMessagesAdded)
00064     *result = fUids[zeroBasedIndex];
00065   else
00066     *result = 0xFFFFFFFF;   // so that value is non-zero and we don't ask for bad msgs
00067   PR_CExitMonitor(this);
00068   return NS_OK;
00069 }
00070 
00071 
00072 
00073 NS_IMETHODIMP nsImapFlagAndUidState::GetMessageFlags(PRInt32 zeroBasedIndex, PRUint16 *result)
00074 {
00075   if (!result)
00076     return NS_ERROR_NULL_POINTER;
00077   imapMessageFlagsType returnFlags = kNoImapMsgFlag;
00078   if (zeroBasedIndex < fNumberOfMessagesAdded)
00079     returnFlags = fFlags[zeroBasedIndex];
00080   
00081   *result = returnFlags;
00082   return NS_OK;
00083 }
00084 
00085 NS_IMETHODIMP nsImapFlagAndUidState::SetMessageFlags(PRInt32 zeroBasedIndex, unsigned short flags)
00086 {
00087   if (zeroBasedIndex < fNumberOfMessagesAdded)
00088     fFlags[zeroBasedIndex] = flags;
00089   return NS_OK;
00090 }
00091 
00092 NS_IMETHODIMP nsImapFlagAndUidState::GetNumberOfRecentMessages(PRInt32 *result)
00093 {
00094   if (!result)
00095     return NS_ERROR_NULL_POINTER;
00096   
00097   PR_CEnterMonitor(this);
00098   PRUint32 counter = 0;
00099   PRInt32 numUnseenMessages = 0;
00100   
00101   for (counter = 0; counter < (PRUint32) fNumberOfMessagesAdded; counter++)
00102   {
00103     if (fFlags[counter] & kImapMsgRecentFlag)
00104       numUnseenMessages++;
00105   }
00106   PR_CExitMonitor(this);
00107   
00108   *result = numUnseenMessages;
00109   
00110   return NS_OK;
00111 }
00112 
00113 /* amount to expand for imap entry flags when we need more */
00114 
00115 nsImapFlagAndUidState::nsImapFlagAndUidState(PRInt32 numberOfMessages, PRUint16 flags)
00116 {
00117   fNumberOfMessagesAdded = 0;
00118   fNumberOfMessageSlotsAllocated = numberOfMessages;
00119   if (!fNumberOfMessageSlotsAllocated)
00120          fNumberOfMessageSlotsAllocated = kImapFlagAndUidStateSize;
00121   fFlags = (imapMessageFlagsType*) PR_Malloc(sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); // new imapMessageFlagsType[fNumberOfMessageSlotsAllocated];
00122   
00123   fUids.SetSize(fNumberOfMessageSlotsAllocated);
00124   memset(fFlags, 0, sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated);
00125   fSupportedUserFlags = flags;
00126   fNumberDeleted = 0;
00127   m_customFlagsHash = nsnull;
00128 }
00129 
00130 nsImapFlagAndUidState::nsImapFlagAndUidState(const nsImapFlagAndUidState& state, 
00131                                                                          uint16 flags)
00132 {
00133   fNumberOfMessagesAdded = state.fNumberOfMessagesAdded;
00134   
00135   fNumberOfMessageSlotsAllocated = state.fNumberOfMessageSlotsAllocated;
00136   fFlags = (imapMessageFlagsType*) PR_Malloc(sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); // new imapMessageFlagsType[fNumberOfMessageSlotsAllocated];
00137   fUids.CopyArray((nsMsgKeyArray *) &state.fUids);
00138 
00139   memcpy(fFlags, state.fFlags, sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated);
00140   fSupportedUserFlags = flags;
00141   fNumberDeleted = 0;
00142   m_customFlagsHash = nsnull;
00143 }
00144 
00145 /* static */PRBool PR_CALLBACK nsImapFlagAndUidState::FreeCustomFlags(nsHashKey *aKey, void *aData,
00146                                         void *closure)
00147 {
00148   PR_Free(aData);
00149   return PR_TRUE;
00150 }
00151 
00152 nsImapFlagAndUidState::~nsImapFlagAndUidState()
00153 {
00154   PR_Free(fFlags);
00155   if (m_customFlagsHash)
00156   {
00157     m_customFlagsHash->Reset(FreeCustomFlags, nsnull);
00158     delete m_customFlagsHash;
00159   }
00160 }
00161        
00162 NS_IMETHODIMP
00163 nsImapFlagAndUidState::SetSupportedUserFlags(uint16 flags)
00164 {
00165   fSupportedUserFlags |= flags;
00166   return NS_OK;
00167 }
00168 
00169 
00170 // we need to reset our flags, (re-read all) but chances are the memory allocation needed will be
00171 // very close to what we were already using
00172 
00173 NS_IMETHODIMP nsImapFlagAndUidState::Reset(PRUint32 howManyLeft)
00174 {
00175   PR_CEnterMonitor(this);
00176   if (!howManyLeft)
00177     fNumberOfMessagesAdded = fNumberDeleted = 0; // used space is still here
00178   if (m_customFlagsHash)
00179     m_customFlagsHash->Reset(FreeCustomFlags, nsnull);
00180   PR_CExitMonitor(this);
00181   return NS_OK;
00182 }
00183 
00184 
00185 // Remove (expunge) a message from our array, since now it is gone for good
00186 
00187 NS_IMETHODIMP nsImapFlagAndUidState::ExpungeByIndex(PRUint32 msgIndex)
00188 {
00189   // protect ourselves in case the server gave us an index key of -1.....
00190   if ((PRInt32) msgIndex < 0)
00191     return NS_ERROR_INVALID_ARG;
00192 
00193   PRUint32 counter = 0;
00194   
00195   if ((PRUint32) fNumberOfMessagesAdded < msgIndex)
00196     return NS_ERROR_INVALID_ARG;
00197   
00198   PR_CEnterMonitor(this);
00199   msgIndex--;  // msgIndex is 1-relative
00200   fNumberOfMessagesAdded--;
00201   if (fFlags[msgIndex] & kImapMsgDeletedFlag)    // see if we already had counted this one as deleted
00202     fNumberDeleted--;
00203   for (counter = msgIndex; counter < (PRUint32) fNumberOfMessagesAdded; counter++)
00204   {
00205     fUids.SetAt(counter, fUids[counter + 1]);                               
00206     fFlags[counter] = fFlags[counter + 1];                                  
00207   }
00208 
00209   PR_CExitMonitor(this);
00210   return NS_OK;
00211 }
00212 
00213 
00214 // adds to sorted list.  protects against duplicates and going past fNumberOfMessageSlotsAllocated  
00215 NS_IMETHODIMP nsImapFlagAndUidState::AddUidFlagPair(PRUint32 uid, imapMessageFlagsType flags, PRUint32 zeroBasedIndex)
00216 {
00217   if (uid == nsMsgKey_None) // ignore uid of -1
00218     return NS_OK;
00219   // check for potential overflow in buffer size for uid array
00220   if (zeroBasedIndex > 0x3FFFFFFF)
00221     return NS_ERROR_INVALID_ARG;
00222   PR_CEnterMonitor(this);
00223   if (zeroBasedIndex + 1 > fNumberOfMessagesAdded)
00224     fNumberOfMessagesAdded = zeroBasedIndex + 1;
00225   // make sure there is room for this pair
00226   if (fNumberOfMessagesAdded >= fNumberOfMessageSlotsAllocated)
00227   {
00228     fNumberOfMessageSlotsAllocated += kImapFlagAndUidStateSize;
00229     fUids.SetSize(fNumberOfMessageSlotsAllocated);
00230     fFlags = (imapMessageFlagsType*) PR_REALLOC(fFlags, sizeof(imapMessageFlagsType) * fNumberOfMessageSlotsAllocated); // new imapMessageFlagsType[fNumberOfMessageSlotsAllocated];
00231     if (!fFlags)
00232       return NS_ERROR_OUT_OF_MEMORY;
00233   }
00234 
00235   fUids.SetAt(zeroBasedIndex, uid);
00236   fFlags[zeroBasedIndex] = flags;
00237   if (flags & kImapMsgDeletedFlag)
00238     fNumberDeleted++;
00239   PR_CExitMonitor(this);
00240   return NS_OK;
00241 }
00242 
00243        
00244 PRInt32 nsImapFlagAndUidState::GetNumberOfDeletedMessages()
00245 {
00246   return fNumberDeleted;
00247 }
00248        
00249 // since the uids are sorted, start from the back (rb)
00250 
00251 PRUint32  nsImapFlagAndUidState::GetHighestNonDeletedUID()
00252 {
00253   PRUint32 msgIndex = fNumberOfMessagesAdded;
00254   do 
00255   {
00256     if (msgIndex <= 0)
00257       return(0);
00258     msgIndex--;
00259     if (fUids[msgIndex] && !(fFlags[msgIndex] & kImapMsgDeletedFlag))
00260       return fUids[msgIndex];
00261   }
00262   while (msgIndex > 0);
00263   return 0;
00264 }
00265 
00266 
00267 // Has the user read the last message here ? Used when we first open the inbox to see if there
00268 // really is new mail there.
00269 
00270 PRBool nsImapFlagAndUidState::IsLastMessageUnseen()
00271 {
00272   PRUint32 msgIndex = fNumberOfMessagesAdded;
00273   
00274   if (msgIndex <= 0)
00275     return PR_FALSE;
00276   msgIndex--;
00277   // if last message is deleted, it was probably filtered the last time around
00278   if (fUids[msgIndex] && (fFlags[msgIndex] & (kImapMsgSeenFlag | kImapMsgDeletedFlag)))
00279     return PR_FALSE;
00280   return PR_TRUE; 
00281 }
00282 
00283 
00284 
00285 // find a message flag given a key with non-recursive binary search, since some folders
00286 // may have thousand of messages, once we find the key set its index, or the index of
00287 // where the key should be inserted
00288 
00289 imapMessageFlagsType nsImapFlagAndUidState::GetMessageFlagsFromUID(PRUint32 uid, PRBool *foundIt, PRInt32 *ndx)
00290 {
00291   PR_CEnterMonitor(this);
00292   
00293   PRInt32 msgIndex = 0;
00294   PRInt32 hi = fNumberOfMessagesAdded - 1;
00295   PRInt32 lo = 0;
00296   
00297   *foundIt = PR_FALSE;
00298   *ndx = -1;
00299   while (lo <= hi)
00300   {
00301     msgIndex = (lo + hi) / 2;
00302     if (fUids[msgIndex] == (PRUint32) uid)
00303     {
00304       PRInt32 returnFlags = fFlags[msgIndex];
00305       
00306       *foundIt = PR_TRUE;
00307       *ndx = msgIndex;
00308       PR_CExitMonitor(this);
00309       return returnFlags;
00310     }
00311     if (fUids[msgIndex] > (PRUint32) uid)
00312       hi = msgIndex -1;
00313     else if (fUids[msgIndex] < (PRUint32) uid)
00314       lo = msgIndex + 1;
00315   }
00316   msgIndex = lo;
00317   // leave msgIndex pointing to the first slot with a value > uid
00318   // first, move it before any ids that are > (shouldn't happen).
00319   while ((msgIndex > 0) && (fUids[msgIndex - 1] > (PRUint32) uid))
00320     msgIndex--;
00321   
00322   // next, move msgIndex up to the first slot > than uid.
00323   while ((PRUint32) uid < fUids[msgIndex])
00324     msgIndex++;
00325   
00326   if (msgIndex < 0)
00327     msgIndex = 0;
00328   *ndx = msgIndex;
00329   PR_CExitMonitor(this);
00330   return 0;
00331 }
00332 
00333 NS_IMETHODIMP nsImapFlagAndUidState::AddUidCustomFlagPair(PRUint32 uid, const char *customFlag)
00334 {
00335   nsAutoCMonitor mon(this);
00336   if (!m_customFlagsHash)
00337     m_customFlagsHash = new nsHashtable(10);
00338   if (!m_customFlagsHash)
00339     return NS_ERROR_OUT_OF_MEMORY;
00340   nsPRUint32Key hashKey(uid);
00341   char *ourCustomFlags;
00342   char *oldValue = (char *) m_customFlagsHash->Get(&hashKey);
00343   if (oldValue)
00344   {
00345   // we'll store multiple keys as space-delimited since space is not
00346   // a valid character in a keyword. First, we need to look for the
00347     // customFlag in the existing flags;
00348     char *existingCustomFlagPtr = PL_strstr(oldValue, customFlag);
00349     PRUint32 customFlagLen = strlen(customFlag);
00350     while (existingCustomFlagPtr)
00351     {
00352       // if existing flags ends with this exact flag, or flag + ' ', we have this flag already;
00353       if (strlen(existingCustomFlagPtr) == customFlagLen || existingCustomFlagPtr[customFlagLen] == ' ')
00354         return NS_OK;
00355       // else, advance to next flag
00356       existingCustomFlagPtr = PL_strstr(existingCustomFlagPtr + 1, customFlag);
00357     }
00358     ourCustomFlags = (char *) PR_Malloc(strlen(oldValue) + customFlagLen + 2);
00359     strcpy(ourCustomFlags, oldValue);
00360     strcat(ourCustomFlags, " ");
00361     strcat(ourCustomFlags, customFlag);
00362     PR_Free(oldValue);
00363     m_customFlagsHash->Remove(&hashKey);
00364   }
00365   else
00366   {
00367     ourCustomFlags = nsCRT::strdup(customFlag);
00368     if (!ourCustomFlags)
00369       return NS_ERROR_OUT_OF_MEMORY;
00370   }
00371   return (m_customFlagsHash->Put(&hashKey, ourCustomFlags) == 0) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00372 }
00373 
00374 NS_IMETHODIMP nsImapFlagAndUidState::GetCustomFlags(PRUint32 uid, char **customFlags)
00375 {
00376   nsAutoCMonitor mon(this);
00377   if (m_customFlagsHash)
00378   {
00379     nsPRUint32Key hashKey(uid);
00380     char *value = (char *) m_customFlagsHash->Get(&hashKey);
00381     if (value)
00382     {
00383       *customFlags = nsCRT::strdup(value);
00384       return (*customFlags) ? NS_OK : NS_ERROR_FAILURE;
00385     }
00386   }
00387   *customFlags = nsnull;
00388   return NS_OK;
00389 }
00390 
00391 NS_IMETHODIMP nsImapFlagAndUidState::ClearCustomFlags(PRUint32 uid)
00392 {
00393   nsAutoCMonitor mon(this);
00394   if (m_customFlagsHash)
00395   {
00396     nsPRUint32Key hashKey(uid);
00397     m_customFlagsHash->Remove(&hashKey);
00398   }
00399   return NS_OK;
00400 }
00401