pjs/network/protocol/imap4/imapearl.cpp

678 строки
20 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "mkutils.h"
#include "imap.h"
#include "imap4pvt.h"
#define ISHEX(c) ( ((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F') )
#define NONHEX(c) (!ISHEX(c))
extern "C" char * escape_unescaped_percents(const char *incomingURL);
extern "C"
{
char * escape_unescaped_percents(const char *incomingURL)
{
const char *inC;
char *outC;
char *result = (char *) XP_ALLOC(XP_STRLEN(incomingURL)*3+1);
if (result)
{
for(inC = incomingURL, outC = result; *inC != '\0'; inC++)
{
if (*inC == '%')
{
// Check if either of the next two characters are non-hex.
if ( !*(inC+1) || NONHEX(*(inC+1)) || !*(inC+2) || NONHEX(*(inC+2)) )
{
// Hex characters don't follow, escape the
// percent char
*outC++ = '%'; *outC++ = '2'; *outC++ = '5';
}
else
{
// Hex characters follow, so assume the percent
// is escaping something else
*outC++ = *inC;
}
}
else
*outC++ = *inC;
}
*outC = '\0';
}
return result;
}
}
// member functions of the TIMAPUrl class
TIMAPUrl::TIMAPUrl(const char *url_string, XP_Bool internal)
: fHostSubString(nil),
fUrlidSubString(nil),
fSourceCanonicalFolderPathSubString(nil),
fDestinationCanonicalFolderPathSubString(nil),
fSearchCriteriaString(nil),
fListOfMessageIds(nil),
fTokenPlaceHolder(nil),
fFlags(0),
fIdsAreUids(FALSE),
fIMAPstate(kAuthenticatedStateURL),
fUrlType(kTest),
fValidURL(FALSE),
fMimePartSelectorDetected(FALSE),
fInternal(internal),
fDiscoveryDepth(0)
{
fOnlineSubDirSeparator = '/'; // initial guess
fUrlString = escape_unescaped_percents(url_string); // this duplicates url_string
fUrlString = NET_UnEscape(fUrlString); // ### mwelch - Put spaces and '>'s back in.
Parse();
}
TIMAPUrl::~TIMAPUrl()
{
FREEIF( fUrlString);
}
void TIMAPUrl::ParseFolderPath(char **resultingCanonicalPath)
{
*resultingCanonicalPath = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL;
if (!*resultingCanonicalPath)
{
fValidURL = FALSE;
return;
}
// The delimiter will be set for a given URL, but will not be statically available
// from an arbitrary URL. It is the creator's responsibility to fill in the correct
// delimiter from the folder's namespace when creating the URL.
char dirSeparator = *(*resultingCanonicalPath)++;
if (dirSeparator != kOnlineHierarchySeparatorUnknown)
SetOnlineSubDirSeparator( dirSeparator);
// if dirSeparator == kOnlineHierarchySeparatorUnknown, then this must be a create
// of a top level imap box. If there is an online subdir, we will automatically
// use its separator. If there is not an online subdir, we don't need a separator.
/*
// I don't think we want to do any of this anymore (for 5.0).
const char *hostDir = TIMAPHostInfo::GetPersonalOrDefaultOnlineSubDirForHost(GetUrlHost());
if (!hostDir)
{
// couldn't find the host in our list
fValidURL = FALSE;
return;
}
int lengthOfImapSubDirString = XP_STRLEN(hostDir);
if (*resultingCanonicalPath &&
((lengthOfImapSubDirString + XP_STRLEN("INBOX")) == XP_STRLEN(*resultingCanonicalPath)) &&
!XP_STRCASECMP("INBOX", *resultingCanonicalPath + lengthOfImapSubDirString))
{
// this is the inbox
*resultingCanonicalPath = "INBOX";
}
*/
}
void TIMAPUrl::ParseSearchCriteriaString()
{
fSearchCriteriaString = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL;
if (!fSearchCriteriaString)
fValidURL = FALSE;
}
void TIMAPUrl::ParseChildDiscoveryDepth()
{
char *discoveryDepth = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL;
if (!discoveryDepth)
{
fValidURL = FALSE;
fDiscoveryDepth = 0;
return;
}
fDiscoveryDepth = atoi(discoveryDepth);
}
void TIMAPUrl::ParseUidChoice()
{
char *uidChoiceString = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL;
if (!uidChoiceString)
fValidURL = FALSE;
else
fIdsAreUids = XP_STRCMP(uidChoiceString, "UID") == 0;
}
void TIMAPUrl::ParseMsgFlags()
{
char *flagsPtr = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL;
if (flagsPtr)
{
// the url is encodes the flags byte as ascii
int intFlags = atoi(flagsPtr);
fFlags = (imapMessageFlagsType) intFlags; // cast here
}
else
fFlags = 0;
}
void TIMAPUrl::ParseListofMessageIds()
{
fListOfMessageIds = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL;
if (!fListOfMessageIds)
fValidURL = FALSE;
else
{
fMimePartSelectorDetected = XP_STRSTR(fListOfMessageIds, "&part=") != 0;
}
}
void TIMAPUrl::Parse()
{
fValidURL = TRUE; // hope for the best
// first token separator is a "?" so others can grab the host
char *urlStartToken = XP_STRTOK_R(fUrlString, "?", &fTokenPlaceHolder);
if (!XP_STRNCASECMP(urlStartToken, "IMAP://",7) )
{
fHostSubString = urlStartToken + 7;
fUrlidSubString = fTokenPlaceHolder ? XP_STRTOK_R(nil, IMAP_URL_TOKEN_SEPARATOR, &fTokenPlaceHolder) : (char *)NULL;
if (!fUrlidSubString)
{
fValidURL = FALSE;
return;
}
if (!XP_STRCASECMP(fUrlidSubString, "fetch"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kMsgFetch;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
}
else if (fInternal)
{
if (!XP_STRCASECMP(fUrlidSubString, "header"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kMsgHeader;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
}
else if (!XP_STRCASECMP(fUrlidSubString, "deletemsg"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kDeleteMsg;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
}
else if (!XP_STRCASECMP(fUrlidSubString, "deleteallmsgs"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kDeleteAllMsgs;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "addmsgflags"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kAddMsgFlags;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
ParseMsgFlags();
}
else if (!XP_STRCASECMP(fUrlidSubString, "subtractmsgflags"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kSubtractMsgFlags;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
ParseMsgFlags();
}
else if (!XP_STRCASECMP(fUrlidSubString, "setmsgflags"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kSetMsgFlags;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
ParseMsgFlags();
}
else if (!XP_STRCASECMP(fUrlidSubString, "onlinecopy"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kOnlineCopy;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
ParseFolderPath(&fDestinationCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "onlinemove"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kOnlineMove;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
ParseFolderPath(&fDestinationCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "onlinetoofflinecopy"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kOnlineToOfflineCopy;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
ParseFolderPath(&fDestinationCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "onlinetoofflinemove"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kOnlineToOfflineMove;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
ParseFolderPath(&fDestinationCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "offlinetoonlinecopy"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kOfflineToOnlineMove;
ParseFolderPath(&fDestinationCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "search"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kSearch;
ParseUidChoice();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseSearchCriteriaString();
}
else if (!XP_STRCASECMP(fUrlidSubString, "test"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kTest;
}
else if (!XP_STRCASECMP(fUrlidSubString, "select"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kSelectFolder;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
if (fTokenPlaceHolder && *fTokenPlaceHolder)
ParseListofMessageIds();
else
fListOfMessageIds = "";
}
else if (!XP_STRCASECMP(fUrlidSubString, "liteselect"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kLiteSelectFolder;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "expunge"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kExpungeFolder;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
fListOfMessageIds = ""; // no ids to UNDO
}
else if (!XP_STRCASECMP(fUrlidSubString, "create"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kCreateFolder;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "discoverchildren"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kDiscoverChildrenUrl;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "discoverlevelchildren"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kDiscoverLevelChildrenUrl;
ParseChildDiscoveryDepth();
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "discoverallboxes"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kDiscoverAllBoxesUrl;
}
else if (!XP_STRCASECMP(fUrlidSubString, "discoverallandsubscribedboxes"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kDiscoverAllAndSubscribedBoxesUrl;
}
else if (!XP_STRCASECMP(fUrlidSubString, "delete"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kDeleteFolder;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "rename"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kRenameFolder;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseFolderPath(&fDestinationCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "movefolderhierarchy"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kMoveFolderHierarchy;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
if (fTokenPlaceHolder && *fTokenPlaceHolder) // handle promote to root
ParseFolderPath(&fDestinationCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "list"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kLsubFolders;
ParseFolderPath(&fDestinationCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "biff"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kBiff;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
ParseListofMessageIds();
}
else if (!XP_STRCASECMP(fUrlidSubString, "netscape"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kGetMailAccountUrl;
}
else if (!XP_STRCASECMP(fUrlidSubString, "appendmsgfromfile"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kAppendMsgFromFile;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "appenddraftfromfile"))
{
fIMAPstate = kSelectedStateURL;
fUrlType = kAppendMsgFromFile;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "subscribe"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kSubscribe;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "unsubscribe"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kUnsubscribe;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "refreshacl"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kRefreshACL;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "refreshfolderurls"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kRefreshFolderUrls;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "refreshallacls"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kRefreshAllACLs;
}
else if (!XP_STRCASECMP(fUrlidSubString, "listfolder"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kListFolder;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "upgradetosubscription"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kUpgradeToSubscription;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else if (!XP_STRCASECMP(fUrlidSubString, "folderstatus"))
{
fIMAPstate = kAuthenticatedStateURL;
fUrlType = kFolderStatus;
ParseFolderPath(&fSourceCanonicalFolderPathSubString);
}
else
fValidURL = FALSE;
}
else fValidURL = FALSE;
}
else
fValidURL = FALSE;
}
TIMAPUrl::EUrlIMAPstate TIMAPUrl::GetUrlIMAPstate()
{
return fIMAPstate;
}
TIMAPUrl::EIMAPurlType TIMAPUrl::GetIMAPurlType()
{
return fUrlType;
}
char *TIMAPUrl::CreateCanonicalSourceFolderPathString()
{
return XP_STRDUP(fSourceCanonicalFolderPathSubString ? fSourceCanonicalFolderPathSubString : "");
}
char *TIMAPUrl::CreateCanonicalDestinationFolderPathString()
{
return XP_STRDUP(fDestinationCanonicalFolderPathSubString);
}
char *TIMAPUrl::CreateServerSourceFolderPathString()
{
return AllocateServerPath(fSourceCanonicalFolderPathSubString);
}
char *TIMAPUrl::CreateServerDestinationFolderPathString()
{
// its possible for the destination folder path to be the root
if (!fDestinationCanonicalFolderPathSubString)
return XP_STRDUP("");
else
return AllocateServerPath(fDestinationCanonicalFolderPathSubString);
}
char *TIMAPUrl::CreateSearchCriteriaString()
{
return XP_STRDUP(fSearchCriteriaString);
}
char *TIMAPUrl::CreateListOfMessageIdsString()
{
char *returnIdString = XP_STRDUP(fListOfMessageIds);
if (returnIdString)
{
// mime may have glommed a "&part=" for a part download
// we return the entire message and let mime extract
// the part. Pop and news work this way also.
// this algorithm truncates the "&part" string.
char *currentChar = returnIdString;
while (*currentChar && (*currentChar != '&'))
currentChar++;
if (*currentChar == '&')
*currentChar = 0;
// we should also strip off anything after "/;section="
// since that can specify an IMAP MIME part
char *wherepart = XP_STRSTR(returnIdString, "/;section=");
if (wherepart)
*wherepart = 0;
}
return returnIdString;
}
char *TIMAPUrl::GetIMAPPartToFetch()
{
char *wherepart = NULL, *rv = NULL;
if (fListOfMessageIds && (wherepart = XP_STRSTR(fListOfMessageIds, "/;section=")) != NULL)
{
wherepart += 10; // XP_STRLEN("/;section=")
if (wherepart)
{
char *wherelibmimepart = XP_STRSTR(wherepart, "&part=");
int len = XP_STRLEN(fListOfMessageIds), numCharsToCopy = 0;
if (wherelibmimepart)
numCharsToCopy = (wherelibmimepart - wherepart);
else
numCharsToCopy = XP_STRLEN(fListOfMessageIds) - (wherepart - fListOfMessageIds);
if (numCharsToCopy)
{
rv = (char *) XP_ALLOC(sizeof(char) * (numCharsToCopy + 1));
if (rv)
XP_STRNCPY_SAFE(rv, wherepart, numCharsToCopy + 1); // appends a \0
}
}
}
return rv;
}
imapMessageFlagsType TIMAPUrl::GetMsgFlags() // kAddMsgFlags or kSubtractMsgFlags only
{
return fFlags;
}
XP_Bool TIMAPUrl::ValidIMAPUrl()
{
return fValidURL;
}
XP_Bool TIMAPUrl::MessageIdsAreUids()
{
return fIdsAreUids;
}
char *TIMAPUrl::AllocateServerPath(const char *canonicalPath)
{
XP_ASSERT(GetOnlineSubDirSeparator() != kOnlineHierarchySeparatorUnknown);
return ReplaceCharsInCopiedString(canonicalPath, '/', GetOnlineSubDirSeparator());
}
char *TIMAPUrl::AllocateCanonicalPath(const char *serverPath)
{
char *canonicalPath = ReplaceCharsInCopiedString(serverPath, GetOnlineSubDirSeparator() , '/');
// eat any escape characters for escaped dir separators
if (canonicalPath)
{
char *currentEscapeSequence = XP_STRSTR(canonicalPath, "\\/");
while (currentEscapeSequence)
{
XP_STRCPY(currentEscapeSequence, currentEscapeSequence+1);
currentEscapeSequence = XP_STRSTR(currentEscapeSequence+1, "\\/");
}
}
return canonicalPath;
}
char *TIMAPUrl::ReplaceCharsInCopiedString(const char *stringToCopy, char oldChar, char newChar)
{
char oldCharString[2];
*oldCharString = oldChar;
*(oldCharString+1) = 0;
char *translatedString = XP_STRDUP(stringToCopy);
char *currentSeparator = strstr(translatedString, oldCharString);
while(currentSeparator)
{
*currentSeparator = newChar;
currentSeparator = strstr(currentSeparator+1, oldCharString);
}
return translatedString;
}
void TIMAPUrl::SetOnlineSubDirSeparator(char onlineDirSeparator)
{
fOnlineSubDirSeparator = onlineDirSeparator;
}
char TIMAPUrl::GetOnlineSubDirSeparator()
{
return fOnlineSubDirSeparator;
}
XP_Bool TIMAPUrl::GetShouldSubscribeToAll()
{
return (GetOnlineSubDirSeparator() == '.');
}
#if 0
// According to the comment in imap.h, where the prototype lives and has been commented out,
// this is obsolete. So let's get rid of the "no prototype" warning.
extern "C" {
void
IMAP_SetNamespacesFromPrefs(const char *hostName, char *personalPrefix, char *publicPrefixes, char *otherUsersPrefixes)
{
TIMAPHostInfo::ClearPrefsNamespacesForHost(hostName);
if (XP_STRCMP(personalPrefix,""))
{
TIMAPNamespace *ns = new TIMAPNamespace(kPersonalNamespace, personalPrefix, '/', TRUE);
if (ns)
TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns);
}
if (XP_STRCMP(publicPrefixes,""))
{
TIMAPNamespace *ns = new TIMAPNamespace(kPublicNamespace, publicPrefixes, '/', TRUE);
if (ns)
TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns);
}
if (XP_STRCMP(otherUsersPrefixes,""))
{
TIMAPNamespace *ns = new TIMAPNamespace(kOtherUsersNamespace, otherUsersPrefixes, '/', TRUE);
if (ns)
TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns);
}
}
} // extern "C"
#endif // 0