Back to index

lightning-sunbird  0.9+nobinonly
nsImapUtils.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 #include "msgCore.h"
00040 #include "nsImapUtils.h"
00041 #include "nsCOMPtr.h"
00042 #include "nsReadableUtils.h"
00043 #include "nsIServiceManager.h"
00044 #include "prsystem.h"
00045 #include "nsEscape.h"
00046 #include "nsIFileSpec.h"
00047 #include "prprf.h"
00048 #include "nsNetCID.h"
00049 
00050 // stuff for temporary root folder hack
00051 #include "nsIMsgAccountManager.h"
00052 #include "nsIMsgIncomingServer.h"
00053 #include "nsIImapIncomingServer.h"
00054 #include "nsMsgBaseCID.h"
00055 #include "nsImapCore.h"
00056 #include "nsMsgUtils.h"
00057 #include "nsImapFlagAndUidState.h"
00058 #include "nsISupportsObsolete.h"
00059 
00060 nsresult
00061 nsImapURI2Path(const char* rootURI, const char* uriStr, nsFileSpec& pathResult)
00062 {
00063   nsresult rv;
00064   
00065   nsAutoString sbdSep;
00066   nsCOMPtr<nsIURL> url;
00067  
00068   rv = nsGetMailFolderSeparator(sbdSep);
00069   if (NS_FAILED(rv)) 
00070     return rv;
00071   
00072   url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
00073   if (NS_FAILED(rv)) return rv;
00074   
00075   nsCAutoString uri(uriStr);
00076   if (uri.Find(rootURI) != 0)     // if doesn't start with rootURI
00077     return NS_ERROR_FAILURE;
00078   
00079   if ((PL_strcmp(rootURI, kImapRootURI) != 0) &&
00080     (PL_strcmp(rootURI, kImapMessageRootURI) != 0)) 
00081   {
00082     pathResult = nsnull;
00083     rv = NS_ERROR_FAILURE; 
00084   }
00085   
00086   // Set our url to the string given
00087   rv = url->SetSpec(nsDependentCString(uriStr));
00088   if (NS_FAILED(rv)) return rv;
00089 
00090   // Set the folder to the url path
00091   nsCAutoString folder;
00092   rv = url->GetPath(folder);
00093   // can't have leading '/' in path
00094   if (folder.CharAt(0) == '/')
00095     folder.Cut(0, 1);
00096   // Now find the server from the URL
00097   nsCOMPtr<nsIMsgIncomingServer> server;
00098   nsCOMPtr<nsIMsgAccountManager> accountManager = 
00099     do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
00100   if(NS_FAILED(rv)) return rv;
00101   
00102   rv = accountManager->FindServerByURI(url, PR_FALSE,
00103     getter_AddRefs(server));
00104 
00105   if (NS_FAILED(rv)) return rv;
00106   
00107   if (server) 
00108   {
00109     nsCOMPtr<nsIFileSpec> localPath;
00110     rv = server->GetLocalPath(getter_AddRefs(localPath));
00111     if (NS_FAILED(rv)) return rv;
00112     
00113     rv = localPath->GetFileSpec(&pathResult);
00114     if (NS_FAILED(rv)) return rv;
00115     
00116     // This forces the creation of the parent server directory
00117     // so that we don't get imapservername.sbd instead
00118     // when the host directory has been deleted. See bug 210683
00119     nsFileSpec tempPath(pathResult.GetNativePathCString(), PR_TRUE); 
00120     pathResult.CreateDirectory();
00121   }
00122   
00123   if (NS_FAILED(rv)) 
00124   {
00125     pathResult = nsnull;
00126     return rv;
00127   }
00128   
00129   if (!folder.IsEmpty())
00130   {
00131     nsCAutoString parentName = folder;
00132     nsCAutoString leafName = folder;
00133     PRInt32 dirEnd = parentName.FindChar('/');
00134     
00135     // FIXME : This would break with multibyte encodings such as 
00136     // Shift_JIS, Big5 because '0x5c' in the second byte of a multibyte
00137     // character would be mistaken for '/'. 
00138     while(dirEnd > 0)
00139     {
00140       parentName.Right(leafName, parentName.Length() - dirEnd -1);
00141       parentName.Truncate(dirEnd);
00142       NS_MsgHashIfNecessary(parentName);
00143       parentName.AppendWithConversion(sbdSep);
00144       pathResult += parentName.get();
00145       // this fixes a strange purify warning.
00146       parentName = leafName.get();
00147       dirEnd = parentName.FindChar('/');
00148     }
00149     if (!leafName.IsEmpty()) 
00150     {
00151       NS_MsgHashIfNecessary(leafName);
00152       pathResult += leafName.get();
00153     }
00154   }
00155   
00156   return NS_OK;
00157 }
00158 
00159 nsresult
00160 nsImapURI2FullName(const char* rootURI, const char* hostname, const char* uriStr,
00161                    char **name)
00162 {
00163     nsAutoString uri; uri.AssignWithConversion(uriStr);
00164     nsAutoString fullName;
00165     if (uri.Find(rootURI) != 0)
00166       return NS_ERROR_FAILURE;
00167     uri.Right(fullName, uri.Length() - strlen(rootURI));
00168     uri = fullName;
00169     PRInt32 hostStart = uri.Find(hostname);
00170     if (hostStart <= 0) 
00171       return NS_ERROR_FAILURE;
00172     uri.Right(fullName, uri.Length() - hostStart);
00173     uri = fullName;
00174     PRInt32 hostEnd = uri.FindChar('/');
00175     if (hostEnd <= 0) 
00176       return NS_ERROR_FAILURE;
00177     uri.Right(fullName, uri.Length() - hostEnd - 1);
00178     if (fullName.IsEmpty())
00179       return NS_ERROR_FAILURE;
00180     *name = ToNewCString(fullName);
00181     return NS_OK;
00182 }
00183 
00184 /* parses ImapMessageURI */
00185 nsresult nsParseImapMessageURI(const char* uri, nsCString& folderURI, PRUint32 *key, char **part)
00186 {
00187   if(!key)
00188     return NS_ERROR_NULL_POINTER;
00189   
00190   nsCAutoString uriStr(uri);
00191   PRInt32 keySeparator = uriStr.RFindChar('#');
00192   if(keySeparator != -1)
00193   {
00194     PRInt32 keyEndSeparator = uriStr.FindCharInSet("/?&", 
00195       keySeparator); 
00196     nsAutoString folderPath;
00197     uriStr.Left(folderURI, keySeparator);
00198     folderURI.Cut(4, 8);    // cut out the _message part of imap-message:
00199     nsCAutoString keyStr;
00200     if (keyEndSeparator != -1)
00201       uriStr.Mid(keyStr, keySeparator+1, 
00202       keyEndSeparator-(keySeparator+1));
00203     else
00204       uriStr.Right(keyStr, uriStr.Length() - (keySeparator + 1));
00205     PRInt32 errorCode;
00206     *key = keyStr.ToInteger(&errorCode);
00207     
00208     if (part && keyEndSeparator != -1)
00209     {
00210       PRInt32 partPos = uriStr.Find("part=", PR_FALSE, keyEndSeparator);
00211       if (partPos != -1)
00212       {
00213         nsCString partSubStr;
00214         uriStr.Right(partSubStr, uriStr.Length() - keyEndSeparator);
00215         *part = ToNewCString(partSubStr);
00216         
00217       }
00218     }
00219   }
00220   return NS_OK;
00221   
00222 }
00223 
00224 nsresult nsBuildImapMessageURI(const char *baseURI, PRUint32 key, nsCString& uri)
00225 {
00226   
00227   uri.Append(baseURI);
00228   uri.Append('#');
00229   uri.AppendInt(key);
00230   return NS_OK;
00231 }
00232 
00233 nsresult nsCreateImapBaseMessageURI(const char *baseURI, char **baseMessageURI)
00234 {
00235   if(!baseMessageURI)
00236     return NS_ERROR_NULL_POINTER;
00237   
00238   nsCAutoString tailURI(baseURI);
00239   
00240   // chop off mailbox:/
00241   if (tailURI.Find(kImapRootURI) == 0)
00242     tailURI.Cut(0, PL_strlen(kImapRootURI));
00243   
00244   nsCAutoString baseURIStr(kImapMessageRootURI);
00245   baseURIStr += tailURI;
00246   
00247   *baseMessageURI = ToNewCString(baseURIStr);
00248   if(!*baseMessageURI)
00249     return NS_ERROR_OUT_OF_MEMORY;
00250   
00251   return NS_OK;
00252 }
00253 
00254 // nsImapMailboxSpec stuff
00255 
00256 NS_IMPL_ISUPPORTS1(nsImapMailboxSpec, nsIMailboxSpec)
00257 
00258 nsImapMailboxSpec::nsImapMailboxSpec()
00259 {
00260   folder_UIDVALIDITY = 0;
00261   number_of_messages = 0;
00262   number_of_unseen_messages = 0;
00263   number_of_recent_messages = 0;
00264   
00265   box_flags = 0;
00266   supportedUserFlags = 0;
00267   
00268   allocatedPathName = nsnull;
00269   unicharPathName = nsnull;
00270   hierarchySeparator = '\0';
00271   hostName = nsnull;
00272   
00273   folderSelected = PR_FALSE;
00274   discoveredFromLsub = PR_FALSE;
00275   
00276   onlineVerified = PR_FALSE;
00277   namespaceForFolder = nsnull;
00278 }
00279 
00280 nsImapMailboxSpec::~nsImapMailboxSpec()
00281 {
00282   nsCRT::free(allocatedPathName);
00283   nsCRT::free(unicharPathName);
00284   nsCRT::free(hostName);
00285 }
00286 
00287 NS_IMPL_GETSET(nsImapMailboxSpec, Folder_UIDVALIDITY, PRInt32, folder_UIDVALIDITY)
00288 NS_IMPL_GETSET(nsImapMailboxSpec, NumMessages, PRInt32, number_of_messages)
00289 NS_IMPL_GETSET(nsImapMailboxSpec, NumUnseenMessages, PRInt32, number_of_unseen_messages)
00290 NS_IMPL_GETSET(nsImapMailboxSpec, NumRecentMessages, PRInt32, number_of_recent_messages)
00291 NS_IMPL_GETSET(nsImapMailboxSpec, HierarchySeparator, char, hierarchySeparator)
00292 NS_IMPL_GETSET(nsImapMailboxSpec, FolderSelected, PRBool, folderSelected)
00293 NS_IMPL_GETSET(nsImapMailboxSpec, DiscoveredFromLsub, PRBool, discoveredFromLsub)
00294 NS_IMPL_GETSET(nsImapMailboxSpec, OnlineVerified, PRBool, onlineVerified)
00295 NS_IMPL_GETSET_STR(nsImapMailboxSpec, HostName, hostName)
00296 NS_IMPL_GETSET_STR(nsImapMailboxSpec, AllocatedPathName, allocatedPathName)
00297 NS_IMPL_GETSET(nsImapMailboxSpec, SupportedUserFlags, PRUint32, supportedUserFlags)
00298 NS_IMPL_GETSET(nsImapMailboxSpec, Box_flags, PRUint32, box_flags)
00299 NS_IMPL_GETSET(nsImapMailboxSpec, NamespaceForFolder, nsIMAPNamespace *, namespaceForFolder)
00300 
00301 NS_IMETHODIMP nsImapMailboxSpec::GetUnicharPathName(PRUnichar **aUnicharPathName)
00302 {
00303   if (!aUnicharPathName)
00304     return NS_ERROR_NULL_POINTER;
00305   *aUnicharPathName = (unicharPathName) ? nsCRT::strdup(unicharPathName) : nsnull;
00306   return NS_OK;
00307 }
00308 
00309 NS_IMETHODIMP nsImapMailboxSpec::GetFlagState(nsIImapFlagAndUidState ** aFlagState)
00310 {
00311   if (!aFlagState)
00312     return NS_ERROR_NULL_POINTER;
00313   *aFlagState = flagState;
00314   NS_IF_ADDREF(*aFlagState);
00315   return NS_OK;
00316 }
00317 
00318 NS_IMETHODIMP nsImapMailboxSpec::SetFlagState(nsIImapFlagAndUidState * aFlagState)
00319 {
00320   flagState = aFlagState;
00321   return NS_OK;
00322 }
00323 
00324 
00325 NS_IMETHODIMP nsImapMailboxSpec::SetUnicharPathName(const PRUnichar *aUnicharPathName)
00326 {
00327   PR_Free(unicharPathName);
00328   unicharPathName= (aUnicharPathName) ? nsCRT::strdup(aUnicharPathName ) : nsnull;
00329   return (unicharPathName) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00330 }
00331 
00332 nsImapMailboxSpec& nsImapMailboxSpec::operator=(const nsImapMailboxSpec& aCopy) 
00333 {
00334   folder_UIDVALIDITY = aCopy.folder_UIDVALIDITY;
00335   number_of_messages = aCopy.number_of_messages;
00336   number_of_unseen_messages = aCopy.number_of_unseen_messages;
00337   number_of_recent_messages = aCopy.number_of_recent_messages;
00338        
00339   box_flags = aCopy.box_flags;
00340   supportedUserFlags = aCopy.supportedUserFlags;
00341   
00342   allocatedPathName = (aCopy.allocatedPathName) ? strdup(aCopy.allocatedPathName) : nsnull;
00343   unicharPathName = (aCopy.unicharPathName) ? nsCRT::strdup(aCopy.unicharPathName) : nsnull;
00344   hierarchySeparator = aCopy.hierarchySeparator;
00345   hostName = strdup(aCopy.hostName);
00346        
00347   flagState = aCopy.flagState;
00348        
00349   folderSelected = aCopy.folderSelected;
00350   discoveredFromLsub = aCopy.discoveredFromLsub;
00351 
00352   onlineVerified = aCopy.onlineVerified;
00353 
00354   namespaceForFolder = aCopy.namespaceForFolder;
00355   
00356   return *this;
00357 }
00358 
00359 // use the flagState to determine if the gaps in the msgUids correspond to gaps in the mailbox,
00360 // in which case we can still use ranges. If flagState is null, we won't do this.
00361 void AllocateImapUidString(PRUint32 *msgUids, PRUint32 &msgCount, 
00362                            nsImapFlagAndUidState *flagState, nsCString &returnString)
00363 {
00364   PRUint32 startSequence = (msgCount > 0) ? msgUids[0] : 0xFFFFFFFF;
00365   PRUint32 curSequenceEnd = startSequence;
00366   PRUint32 total = msgCount;
00367   PRInt32  curFlagStateIndex = -1;
00368 
00369   for (PRUint32 keyIndex=0; keyIndex < total; keyIndex++)
00370   {
00371     PRUint32 curKey = msgUids[keyIndex];
00372     PRUint32 nextKey = (keyIndex + 1 < total) ? msgUids[keyIndex + 1] : 0xFFFFFFFF;
00373     PRBool lastKey = (nextKey == 0xFFFFFFFF);
00374 
00375     if (lastKey)
00376       curSequenceEnd = curKey;
00377 
00378     if (!lastKey)
00379     {
00380       if (nextKey == curSequenceEnd + 1)
00381       {
00382         curSequenceEnd = nextKey;
00383         curFlagStateIndex++;
00384         continue;
00385       }
00386       if (flagState)
00387       {
00388         if (curFlagStateIndex == -1)
00389         {
00390           PRBool foundIt;
00391           flagState->GetMessageFlagsFromUID(curSequenceEnd, &foundIt, &curFlagStateIndex);
00392           NS_ASSERTION(foundIt, "flag state missing key");
00393         }
00394         curFlagStateIndex++;
00395         PRUint32 nextUidInFlagState;
00396         nsresult rv = flagState->GetUidOfMessage(curFlagStateIndex, &nextUidInFlagState);
00397         if (NS_SUCCEEDED(rv) && nextUidInFlagState == nextKey)
00398         {
00399           curSequenceEnd = nextKey;
00400           continue;
00401         }
00402       }
00403     }
00404     if (curSequenceEnd > startSequence)
00405     {
00406       returnString.AppendInt(startSequence);
00407       returnString += ':';
00408       returnString.AppendInt(curSequenceEnd);
00409       startSequence = nextKey;
00410       curSequenceEnd = startSequence;
00411       curFlagStateIndex = -1;
00412     }
00413     else
00414     {
00415       startSequence = nextKey;
00416       curSequenceEnd = startSequence;
00417       returnString.AppendInt(msgUids[keyIndex]);
00418       curFlagStateIndex = -1;
00419     }
00420     // check if we've generated too long a string - if there's no flag state,
00421     // it means we just need to go ahead and generate a too long string
00422     // because the calling code won't handle breaking up the strings.
00423     if (flagState && returnString.Length() > 950) 
00424     {
00425       msgCount = keyIndex;
00426       break;
00427     }
00428     // If we are not the last item then we need to add the comma 
00429     // but it's important we do it here, after the length check 
00430     if (!lastKey) 
00431       returnString += ','; 
00432   }
00433 }
00434 
00435 void ParseUidString(const char *uidString, nsMsgKeyArray &keys)
00436 {
00437   // This is in the form <id>,<id>, or <id1>:<id2>
00438   char curChar = *uidString;
00439   PRBool isRange = PR_FALSE;
00440   int32 curToken;
00441   int32 saveStartToken=0;
00442 
00443   for (const char *curCharPtr = uidString; curChar && *curCharPtr;)
00444   {
00445     const char *currentKeyToken = curCharPtr;
00446     curChar = *curCharPtr;
00447     while (curChar != ':' && curChar != ',' && curChar != '\0')
00448       curChar = *curCharPtr++;
00449 
00450     // we don't need to null terminate currentKeyToken because atoi 
00451     // stops at non-numeric chars.
00452     curToken = atoi(currentKeyToken); 
00453     if (isRange)
00454     {
00455       while (saveStartToken < curToken)
00456         keys.Add(saveStartToken++);
00457     }
00458     keys.Add(curToken);
00459     isRange = (curChar == ':');
00460     if (isRange)
00461       saveStartToken = curToken + 1;
00462   }
00463 }
00464 
00465 void AppendUid(nsCString &msgIds, PRUint32 uid)
00466 {
00467   char buf[20];
00468   PR_snprintf(buf, sizeof(buf), "%u", uid);
00469   msgIds.Append(buf);
00470 }
00471