зеркало из https://github.com/mozilla/pjs.git
6036 строки
203 KiB
C++
6036 строки
203 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.
|
|
*/
|
|
|
|
/* imap related libmsg stuff */
|
|
|
|
#include "msg.h"
|
|
#include "errcode.h"
|
|
#include "msgimap.h"
|
|
#include "maildb.h"
|
|
#include "mailhdr.h"
|
|
#include "msgprefs.h"
|
|
#include "msgurlq.h"
|
|
#include "msgdbvw.h"
|
|
#include "grpinfo.h"
|
|
#include "msgtpane.h"
|
|
#include "msgspane.h"
|
|
#include "imap.h"
|
|
#include "msgfpane.h"
|
|
#include "pmsgfilt.h"
|
|
#include "prsembst.h"
|
|
#include "xpgetstr.h"
|
|
#include "xplocale.h"
|
|
#include "imapoff.h"
|
|
#include "msgurlq.h"
|
|
#include "prefapi.h"
|
|
#include "msgundmg.h"
|
|
#include "msgundac.h"
|
|
#include "libi18n.h"
|
|
#include "msgmpane.h"
|
|
#include "prtime.h"
|
|
#include "imaphost.h"
|
|
#include "subpane.h"
|
|
#include "prthread.h"
|
|
#include "xp_hash.h"
|
|
#include "pw_public.h"
|
|
#include "msgundmg.h"
|
|
#include "prlong.h"
|
|
#include "msgbiff.h"
|
|
#include "netutils.h"
|
|
|
|
extern "C"
|
|
{
|
|
extern int MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
extern int MK_OUT_OF_MEMORY;
|
|
extern int MK_MSG_CANT_COPY_TO_SAME_FOLDER;
|
|
extern int MK_MSG_CANT_COPY_TO_QUEUE_FOLDER;
|
|
extern int MK_MSG_CANT_COPY_TO_QUEUE_FOLDER_OLD;
|
|
extern int MK_MSG_CANT_COPY_TO_DRAFTS_FOLDER;
|
|
extern int MK_MSG_CANT_CREATE_FOLDER;
|
|
extern int MK_COULD_NOT_CREATE_DIRECTORY;
|
|
extern int MK_UNABLE_TO_DELETE_FILE;
|
|
extern int MK_UNABLE_TO_OPEN_FILE;
|
|
extern int MK_MSG_FOLDER_ALREADY_EXISTS;
|
|
extern int MK_MSG_FOLDER_BUSY;
|
|
extern int MK_MSG_CANT_DELETE_NEWS_DB;
|
|
extern int MK_MSG_IMAP_CONTAINER_NAME;
|
|
extern int MK_MSG_ONLINE_IMAP_WITH_NO_BODY;
|
|
extern int MK_MSG_ONLINE_COPY_FAILED;
|
|
extern int MK_MSG_ONLINE_MOVE_FAILED;
|
|
extern int MK_MSG_CANT_FIND_SNM;
|
|
extern int MK_MSG_NO_UNDO_DURING_IMAP_FOLDER_LOAD;
|
|
extern int MK_MSG_BOGUS_SERVER_MAILBOX_UID_STATE;
|
|
extern int MK_POP3_NO_MESSAGES;
|
|
extern int MK_MSG_LOTS_NEW_IMAP_FOLDERS;
|
|
extern int MK_MSG_IMAP_DIR_PROMPT;
|
|
extern int MK_MSG_IMAP_DISCOVERING_MAILBOX;
|
|
extern int XP_ALERT_OFFLINE_MODE_SELECTED;
|
|
extern int MK_MSG_IMAP_INBOX_NAME;
|
|
extern int XP_MSG_PASSWORD_FAILED;
|
|
extern int XP_MSG_IMAP_ACL_READ_RIGHT;
|
|
extern int XP_MSG_IMAP_ACL_WRITE_RIGHT;
|
|
extern int XP_MSG_IMAP_ACL_LOOKUP_RIGHT;
|
|
extern int XP_MSG_IMAP_ACL_INSERT_RIGHT;
|
|
extern int XP_MSG_IMAP_ACL_DELETE_RIGHT;
|
|
extern int XP_MSG_IMAP_ACL_CREATE_RIGHT;
|
|
extern int XP_MSG_IMAP_ACL_SEEN_RIGHT;
|
|
extern int XP_MSG_IMAP_ACL_POST_RIGHT;
|
|
extern int XP_MSG_IMAP_ACL_ADMINISTER_RIGHT;
|
|
extern int XP_MSG_IMAP_ACL_FULL_RIGHTS;
|
|
extern int XP_MSG_IMAP_UNKNOWN_USER;
|
|
extern int XP_MSG_IMAP_PERSONAL_FOLDER_TYPE_NAME;
|
|
extern int XP_MSG_IMAP_PERSONAL_SHARED_FOLDER_TYPE_NAME;
|
|
extern int XP_MSG_IMAP_PUBLIC_FOLDER_TYPE_NAME;
|
|
extern int XP_MSG_IMAP_OTHER_USERS_FOLDER_TYPE_NAME;
|
|
extern int XP_MSG_IMAP_PERSONAL_FOLDER_TYPE_DESCRIPTION;
|
|
extern int XP_MSG_IMAP_PERSONAL_SHARED_FOLDER_TYPE_DESCRIPTION;
|
|
extern int XP_MSG_IMAP_PUBLIC_FOLDER_TYPE_DESCRIPTION;
|
|
extern int XP_MSG_IMAP_OTHER_USERS_FOLDER_TYPE_DESCRIPTION;
|
|
}
|
|
|
|
// This string is defined in the ACL RFC to be "anyone"
|
|
#define IMAP_ACL_ANYONE_STRING "anyone"
|
|
|
|
// forward
|
|
|
|
extern "C" int32
|
|
NET_BlockingWrite (PRFileDesc *filedes, const void * buf, unsigned int nbyte);
|
|
|
|
void PostMessageCopyUrlExitFunc(URL_Struct *URL_s, int status, MWContext *window_id);
|
|
void UpdateStandAloneIMAPBiff(const IDArray& keysToFetch);
|
|
|
|
static
|
|
void PostEmptyImapTrashExitFunc(URL_Struct *URL_s, int status, MWContext* /*window_id*/)
|
|
{
|
|
// if this was launched from a folder pane. then start compress folders url.
|
|
if ((status >= 0) && (URL_s->msg_pane->GetPaneType() == MSG_FOLDERPANE))
|
|
((MSG_FolderPane *) URL_s->msg_pane)->CompressAllFolders();
|
|
}
|
|
|
|
|
|
// This exit function will get called when you pass false in the loadingFolder argument to
|
|
// MSG_IMAPFolderInfoMail::StartUpdateOfNewlySelectedFolder
|
|
void ImapFolderClearLoadingFolder(URL_Struct *URL_s, int /*status*/, MWContext* window_id)
|
|
{
|
|
if (URL_s->msg_pane && !URL_s->msg_pane->GetActiveImapFiltering())
|
|
{
|
|
MSG_IMAPFolderInfoMail *loadedFolder = URL_s->msg_pane->GetLoadingImapFolder();
|
|
URL_s->msg_pane->SetLoadingImapFolder(NULL);
|
|
if (loadedFolder)
|
|
{
|
|
loadedFolder->SetGettingMail(FALSE);
|
|
loadedFolder->NotifyFolderLoaded(URL_s->msg_pane); // dmb - fix stop watch when no connections?
|
|
}
|
|
}
|
|
}
|
|
|
|
// This exit function will get called when you pass true in the loadingFolder argument to
|
|
// MSG_IMAPFolderInfoMail::StartUpdateOfNewlySelectedFolder
|
|
void ImapFolderSelectCompleteExitFunction(URL_Struct *URL_s, int /*status*/, MWContext* window_id)
|
|
{
|
|
if (URL_s->msg_pane && !URL_s->msg_pane->GetActiveImapFiltering())
|
|
{
|
|
MSG_IMAPFolderInfoMail *loadedFolder = URL_s->msg_pane->GetLoadingImapFolder();
|
|
URL_s->msg_pane->SetLoadingImapFolder(NULL);
|
|
|
|
XP_Bool sendFolderLoaded = (loadedFolder != NULL);
|
|
|
|
if (sendFolderLoaded)
|
|
{
|
|
MSG_Master *master = URL_s->msg_pane->GetMaster();
|
|
loadedFolder->SetGettingMail(FALSE);
|
|
if (master->IsCachePasswordProtected() && !master->IsUserAuthenticated() && !NET_IsOffline())
|
|
{
|
|
sendFolderLoaded = FALSE;
|
|
char *alert = XP_GetString(XP_MSG_PASSWORD_FAILED);
|
|
#ifndef XP_UNIX
|
|
FE_Alert(window_id, alert);
|
|
#else
|
|
FE_Alert_modal(window_id, alert);
|
|
#endif
|
|
#ifndef XP_MAC
|
|
URL_Struct *url_struct = NET_CreateURLStruct("mailbox:", NET_SUPER_RELOAD);
|
|
if (url_struct)
|
|
FE_GetURL(window_id, url_struct);
|
|
#endif
|
|
FE_PaneChanged(URL_s->msg_pane, TRUE, MSG_PaneNotifyFolderDeleted, (uint32) loadedFolder);
|
|
}
|
|
}
|
|
if (sendFolderLoaded)
|
|
{
|
|
FE_PaneChanged(URL_s->msg_pane, TRUE, MSG_PaneNotifyFolderLoaded, (uint32) loadedFolder);
|
|
loadedFolder->NotifyFolderLoaded(URL_s->msg_pane); // dmb - fix stop watch when no connections?
|
|
}
|
|
}
|
|
#ifdef DEBUG_bienvenu
|
|
else if (URL_s->msg_pane)
|
|
XP_Trace("pane has active imap filtering so we didn't send a folderloaded notification\n");
|
|
#endif
|
|
}
|
|
|
|
// static
|
|
void MSG_IMAPFolderInfoMail::ReportErrorFromBuildIMAPFolderTree(MSG_Master *mailMaster, const char *fileName, XP_FileType filetype)
|
|
{
|
|
XP_FILE_NATIVE_PATH failedDir = WH_FileName(fileName,filetype);
|
|
char *errormsg = failedDir ? PR_smprintf(XP_GetString(MK_COULD_NOT_CREATE_DIRECTORY), failedDir) : (char *)NULL;
|
|
MWContext *alertContext = mailMaster->GetFirstPane() ? mailMaster->GetFirstPane()->GetContext() : (MWContext *)NULL;
|
|
if (errormsg)
|
|
FE_Alert(alertContext, errormsg);
|
|
FREEIF(failedDir);
|
|
FREEIF(errormsg);
|
|
}
|
|
|
|
|
|
MSG_IMAPFolderInfoContainer*
|
|
MSG_IMAPFolderInfoMail::BuildIMAPFolderTree (MSG_Master *mailMaster,
|
|
uint8 depth,
|
|
MSG_FolderArray * parentArray)
|
|
{
|
|
// the imap folders start at level 1
|
|
XP_ASSERT(depth == 1);
|
|
|
|
XP_Bool errorReturnRootContainerOnly = FALSE;
|
|
|
|
// make sure the root imap folder exists
|
|
XP_StatStruct imapfolderst;
|
|
if (XP_Stat("", &imapfolderst, xpImapRootDirectory))
|
|
{
|
|
XP_MakeDirectory("", xpImapRootDirectory);
|
|
if (XP_Stat("", &imapfolderst, xpImapRootDirectory))
|
|
{
|
|
errorReturnRootContainerOnly = TRUE; // can't create dir, why?
|
|
}
|
|
}
|
|
else if (!S_ISDIR(imapfolderst.st_mode))
|
|
errorReturnRootContainerOnly = TRUE;
|
|
|
|
if (errorReturnRootContainerOnly)
|
|
ReportErrorFromBuildIMAPFolderTree(mailMaster,"", xpImapRootDirectory);
|
|
|
|
MSG_Prefs *prefs = mailMaster->GetPrefs();
|
|
if (prefs)
|
|
{
|
|
int32 profileAge = prefs->GetStartingMailNewsProfileAge();
|
|
// Upgrade default IMAP host prefs
|
|
if (! (profileAge & MSG_IMAP_DEFAULT_HOST_UPGRADE_FLAG)) // have we upgraded the default host?
|
|
{
|
|
prefs->SetMailNewsProfileAgeFlag(MSG_IMAP_DEFAULT_HOST_UPGRADE_FLAG);
|
|
MSG_IMAPHost::UpgradeDefaultServerPrefs(mailMaster);
|
|
}
|
|
}
|
|
|
|
char *serverList = NULL;
|
|
MSG_IMAPFolderInfoContainer *container = NULL;
|
|
|
|
PREF_CopyCharPref("network.hosts.imap_servers", &serverList);
|
|
char *serverPtr = serverList;
|
|
while (serverPtr)
|
|
{
|
|
char *endPtr = XP_STRCHR(serverPtr, ',');
|
|
if (endPtr)
|
|
*endPtr++ = '\0';
|
|
MSG_IMAPFolderInfoContainer *curContainer = BuildIMAPServerTree(mailMaster, serverPtr, parentArray, errorReturnRootContainerOnly, depth);
|
|
if (!container)
|
|
container = curContainer; // we will return first container;
|
|
serverPtr = endPtr;
|
|
}
|
|
FREEIF(serverList);
|
|
return container; // return first container.
|
|
}
|
|
|
|
/* static */
|
|
MSG_IMAPFolderInfoContainer* MSG_IMAPFolderInfoMail::BuildIMAPServerTree(MSG_Master *mailMaster,
|
|
const char *imapServerName,
|
|
MSG_FolderArray * parentArray,
|
|
XP_Bool errorReturnRootContainerOnly,
|
|
int depth)
|
|
{
|
|
XP_StatStruct imapfolderst;
|
|
// see if there's a :port in the server name. If so, strip it off when
|
|
// creating the server directory.
|
|
char *port = 0;
|
|
if (imapServerName)
|
|
{
|
|
port = XP_STRCHR(imapServerName,':');
|
|
if (port)
|
|
*port=0;
|
|
}
|
|
const char *imapServerDir = imapServerName;
|
|
|
|
#if defined(XP_WIN16) || defined(XP_MAC) || defined(XP_OS2)
|
|
|
|
#if defined(XP_WIN16) || defined(XP_OS2)
|
|
const int limit = 8;
|
|
#elif XP_MAC
|
|
const int limit = 31;
|
|
#endif
|
|
|
|
int len = 0;
|
|
if ((len = XP_STRLEN(imapServerDir)) > limit) {
|
|
imapServerDir = imapServerDir + len - limit;
|
|
}
|
|
#endif
|
|
|
|
MSG_IMAPHost *imapHost = MSG_IMAPHost::AddHostFromPrefs(imapServerName, mailMaster);
|
|
XP_Bool alreadyReportedError = errorReturnRootContainerOnly;
|
|
if (!errorReturnRootContainerOnly && XP_Stat(imapServerDir, &imapfolderst, xpImapServerDirectory))
|
|
{
|
|
XP_MakeDirectory(imapServerDir, xpImapServerDirectory);
|
|
if (XP_Stat(imapServerDir, &imapfolderst, xpImapServerDirectory))
|
|
{
|
|
errorReturnRootContainerOnly = TRUE; // can't create dir, why?
|
|
}
|
|
}
|
|
else if (!errorReturnRootContainerOnly && !S_ISDIR(imapfolderst.st_mode))
|
|
errorReturnRootContainerOnly = TRUE;
|
|
|
|
if (errorReturnRootContainerOnly && !alreadyReportedError)
|
|
ReportErrorFromBuildIMAPFolderTree(mailMaster,imapServerDir, xpImapServerDirectory);
|
|
|
|
// create the root level imap box
|
|
char *defaultImapDirectory = XP_STRDUP(mailMaster->GetPrefs()->GetIMAPFolderDirectory());
|
|
char *imapDirLeafName = XP_STRRCHR(defaultImapDirectory, '/');
|
|
XP_ASSERT(imapDirLeafName);
|
|
imapDirLeafName++;
|
|
*imapDirLeafName = '\0';
|
|
char *imapDirectory = XP_AppendStr(defaultImapDirectory, imapServerName);
|
|
|
|
MSG_IMAPFolderInfoContainer *rootIMAPfolder = new MSG_IMAPFolderInfoContainer(imapServerName,1, imapHost, mailMaster);
|
|
imapHost->SetHostInfo(rootIMAPfolder);
|
|
|
|
if (rootIMAPfolder && parentArray) // need to figure out where to add imap host. - just before local host
|
|
{
|
|
int i = 0;
|
|
for (; i < parentArray->GetSize(); i++)
|
|
{
|
|
MSG_FolderInfo *folder = parentArray->GetAt(i);
|
|
if (folder && folder->GetFlags() & MSG_FOLDER_FLAG_DIRECTORY && folder->GetType() != FOLDER_IMAPSERVERCONTAINER) // local mail
|
|
break;
|
|
}
|
|
parentArray->InsertAt(i, rootIMAPfolder);
|
|
}
|
|
|
|
// start the buildfoldertree recursion from the inbox
|
|
if (rootIMAPfolder && !errorReturnRootContainerOnly)
|
|
{
|
|
// there is a similar loop in MSG_FolderInfoMail::BuildFolderTree
|
|
// I have my own here because I would have to mangle MSG_FolderInfoMail::BuildFolderTree
|
|
// a lot to make it work with the MSG_IMAPFolderInfoContainer type
|
|
|
|
XP_Dir dir = XP_OpenDir (imapDirectory, xpMailFolder);
|
|
if (dir)
|
|
{
|
|
XP_DirEntryStruct *entry = NULL;
|
|
for (entry = XP_ReadDir(dir); entry; entry = XP_ReadDir(dir))
|
|
{
|
|
// ###phil not nice, but now that ShouldIgnoreFile is in the folderInfoMail,
|
|
// we have no way to ask here, since the IMAP container isn't a folderInfoMail
|
|
if (StaticShouldIgnoreFile(entry->d_name))
|
|
continue;
|
|
|
|
char *subName = (char*) XP_ALLOC(XP_STRLEN(imapDirectory) + XP_STRLEN(entry->d_name) + 2); // '/' + '\0'
|
|
if (subName)
|
|
{
|
|
XP_STRCPY (subName, imapDirectory);
|
|
if (*entry->d_name != '/')
|
|
XP_STRCAT (subName, "/");
|
|
XP_STRCAT (subName, entry->d_name);
|
|
BuildFolderTree (subName, depth + 1, rootIMAPfolder->GetSubFolders(), mailMaster, TRUE, imapHost);
|
|
FREEIF(subName);
|
|
}
|
|
}
|
|
|
|
|
|
MSG_FolderInfo *foundInbox = NULL;
|
|
MSG_IMAPHostTable *imapHostTable = mailMaster->GetIMAPHostTable();
|
|
XP_ASSERT(imapHostTable); // we just added one, right?
|
|
MSG_IMAPHost *defaultIMAPHost = imapHostTable->GetDefaultHost();
|
|
|
|
// Always make sure there's an IMAP INBOX on the default server, if we're using IMAP.
|
|
// We also always want to create an INBOX if there are no folders for a given host.
|
|
int32 numberOfInboxesFound = rootIMAPfolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, &foundInbox, 1);
|
|
int32 numberOfmailboxesFound = rootIMAPfolder->GetNumSubFolders();
|
|
if ((imapHost == defaultIMAPHost) ? (numberOfInboxesFound == 0) : (numberOfmailboxesFound == 0))
|
|
{
|
|
char *inboxPath = (char*) XP_ALLOC (XP_STRLEN(imapHost->GetLocalDirectory()) + XP_STRLEN(INBOX_FOLDER_NAME) + 2); // '/' + '\0'
|
|
if (inboxPath)
|
|
{
|
|
XP_STRCPY(inboxPath, imapDirectory);
|
|
XP_STRCAT(inboxPath, "/");
|
|
XP_STRCAT(inboxPath, INBOX_FOLDER_NAME);
|
|
|
|
// create the new database, set the online name
|
|
MailDB *newDb = NULL;
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(inboxPath, TRUE, &newDb, mailMaster, &wasCreated);
|
|
if (newDb)
|
|
{
|
|
newDb->m_dbFolderInfo->SetMailboxName("INBOX"); // RFC inbox name
|
|
MSG_IMAPFolderInfoMail *inboxFolder = new MSG_IMAPFolderInfoMail(inboxPath, mailMaster, 2, MSG_FOLDER_FLAG_MAIL | MSG_FOLDER_FLAG_INBOX, imapHost);
|
|
if (imapHost->GetDefaultOfflineDownload())
|
|
inboxFolder->SetFolderPrefFlags(inboxFolder->GetFolderPrefFlags() | MSG_FOLDER_PREF_OFFLINE);
|
|
|
|
newDb->SetFolderInfoValid(inboxPath,0,0);
|
|
newDb->Close();
|
|
if (inboxFolder && rootIMAPfolder)
|
|
{
|
|
// inboxFolder->SetIsOnlineVerified(TRUE);
|
|
XPPtrArray *rootSubfolderArray = (XPPtrArray *) rootIMAPfolder->GetSubFolders();
|
|
rootSubfolderArray->Add(inboxFolder);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
XP_CloseDir(dir);
|
|
}
|
|
}
|
|
|
|
FREEIF(imapDirectory);
|
|
return rootIMAPfolder;
|
|
}
|
|
|
|
void
|
|
MSG_IMAPFolderInfoMail::MatchPathWithPersonalNamespaceThenSetFlag(const char *path,
|
|
const char *personalNameSpace, uint32 folderFlag, const char *onlineName)
|
|
{
|
|
if (!path || NET_URL_Type(path) != IMAP_TYPE_URL)
|
|
return;
|
|
|
|
char * folderName = NET_ParseURL(path, GET_PATH_PART);
|
|
|
|
if (!folderName || !*folderName)
|
|
{
|
|
StrAllocCopy(folderName, "/");
|
|
StrAllocCat(folderName, msg_DefaultFolderName(folderFlag));
|
|
}
|
|
|
|
if (folderName && *folderName && personalNameSpace && *personalNameSpace)
|
|
{
|
|
char *newN = PR_smprintf("/%s%s", personalNameSpace, folderName+1);
|
|
if(newN)
|
|
{
|
|
XP_FREEIF(folderName); folderName = newN;
|
|
}
|
|
}
|
|
if (folderName && *folderName && XP_STRCMP(folderName+1, onlineName) == 0)
|
|
SetFlag(folderFlag);
|
|
XP_FREEIF(folderName);
|
|
}
|
|
|
|
void
|
|
MSG_IMAPFolderInfoMail::SetPrefFolderFlag()
|
|
{
|
|
const char *path = GetPathname();
|
|
const char *personalNamespacePrefix = GetIMAPHost() ?
|
|
GetIMAPHost()->GetDefaultNamespacePrefixOfType(kPersonalNamespace) : (char *)NULL;
|
|
|
|
if (!path)
|
|
return;
|
|
|
|
char *onlineName =
|
|
CreateOnlineVersionOfLocalPathString(*(m_master->GetPrefs()), path);
|
|
char *draftsPath = msg_MagicFolderName (m_master->GetPrefs(),
|
|
MSG_FOLDER_FLAG_DRAFTS, NULL);
|
|
char *templatesPath = msg_MagicFolderName (m_master->GetPrefs(),
|
|
MSG_FOLDER_FLAG_TEMPLATES,
|
|
NULL);
|
|
|
|
ClearFlag(MSG_FOLDER_FLAG_DRAFTS);
|
|
MatchPathWithPersonalNamespaceThenSetFlag(draftsPath, personalNamespacePrefix,
|
|
MSG_FOLDER_FLAG_DRAFTS, onlineName);
|
|
XP_FREEIF(draftsPath);
|
|
|
|
ClearFlag(MSG_FOLDER_FLAG_TEMPLATES);
|
|
MatchPathWithPersonalNamespaceThenSetFlag(templatesPath, personalNamespacePrefix,
|
|
MSG_FOLDER_FLAG_TEMPLATES, onlineName);
|
|
XP_FREEIF(templatesPath);
|
|
|
|
char *imapSentMail = NULL;
|
|
char *imapSentNews = NULL;
|
|
|
|
ClearFlag(MSG_FOLDER_FLAG_SENTMAIL);
|
|
PREF_CopyCharPref("mail.imap_sentmail_path", &imapSentMail);
|
|
MatchPathWithPersonalNamespaceThenSetFlag(imapSentMail, personalNamespacePrefix,
|
|
MSG_FOLDER_FLAG_SENTMAIL, onlineName);
|
|
XP_FREEIF(imapSentMail);
|
|
|
|
PREF_CopyCharPref("news.imap_sentmail_path", &imapSentNews);
|
|
MatchPathWithPersonalNamespaceThenSetFlag(imapSentNews, personalNamespacePrefix,
|
|
MSG_FOLDER_FLAG_SENTMAIL, onlineName);
|
|
XP_FREEIF(imapSentNews);
|
|
|
|
XP_FREEIF(onlineName);
|
|
}
|
|
|
|
MSG_IMAPFolderInfoMail::MSG_IMAPFolderInfoMail(char* pathname,
|
|
MSG_Master *mailMaster,
|
|
uint8 depth,
|
|
uint32 flags,
|
|
MSG_IMAPHost *host) :
|
|
m_DownLoadState(kNoDownloadInProgress),
|
|
MSG_FolderInfoMail(pathname, mailMaster, depth, flags | MSG_FOLDER_FLAG_IMAPBOX | MSG_FOLDER_FLAG_MAIL)
|
|
{
|
|
m_canBeInFolderPane = TRUE;
|
|
m_VerifiedAsOnlineFolder = FALSE;
|
|
m_OnlineHierSeparator = kOnlineHierarchySeparatorUnknown;
|
|
m_OnlineFolderName = NULL;
|
|
m_runningIMAPUrl = MSG_NotRunning;
|
|
m_requiresCleanup = FALSE;
|
|
m_LoadingInContext = NULL;
|
|
m_TimeStampOfLastList = LL_ZERO;
|
|
m_uploadInfo = NULL;
|
|
m_hasOfflineEvents = FALSE;
|
|
m_host = host;
|
|
m_folderNeedsSubscribing = FALSE;
|
|
m_folderNeedsACLListed = TRUE;
|
|
m_ownerUserName = NULL;
|
|
m_folderACL = NULL;
|
|
m_adminUrl = NULL;
|
|
XP_BZERO(&m_mailboxSpec, sizeof(m_mailboxSpec));
|
|
if (flags & MSG_FOLDER_FLAG_INBOX) // LATER ON WE WILL ENABLE ANY FOLDER, NOT JUST INBOXES
|
|
MSG_Biff_Master::AddBiffFolder(pathname, host->GetCheckNewMail(),
|
|
host->GetBiffInterval(), TRUE, (char*) host->GetHostName());
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::SetTimeStampOfLastListNow() { m_TimeStampOfLastList = PR_Now(); }
|
|
|
|
|
|
#define kBogusImapFolderName "+=+=+="
|
|
|
|
MSG_IMAPFolderInfoMail::~MSG_IMAPFolderInfoMail()
|
|
{
|
|
MSG_Master *master = NULL;
|
|
TNavigatorImapConnection *imapConnection = NULL;
|
|
|
|
master = GetMaster();
|
|
if (master && (GetFlags() & MSG_FOLDER_FLAG_INBOX))
|
|
{
|
|
imapConnection = (TNavigatorImapConnection*) master->UnCacheImapConnection(GetHostName(), m_OnlineFolderName);
|
|
MSG_IMAP_KillConnection(imapConnection); // stop the connection and kill it
|
|
}
|
|
|
|
// MSG_Biff_Master::RemoveBiffFolder(m_pathName); // Done in MSG_FolderInfoMail destructor
|
|
if (m_OnlineFolderName && XP_STRCMP(m_OnlineFolderName, kBogusImapFolderName))
|
|
XP_FREE(m_OnlineFolderName);
|
|
delete m_folderACL;
|
|
FREEIF(m_ownerUserName);
|
|
FREEIF(m_adminUrl);
|
|
}
|
|
|
|
MSG_IMAPFolderInfoMail *MSG_IMAPFolderInfoMail::GetIMAPFolderInfoMail()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::OnCloseFolder ()
|
|
{
|
|
// Tell the connection cache this folder is closing.
|
|
m_master->ImapFolderClosed(this);
|
|
return eSUCCESS;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::SetOnlineName(const char *name)
|
|
{
|
|
if (m_OnlineFolderName && XP_STRCMP(m_OnlineFolderName, kBogusImapFolderName))
|
|
XP_FREE(m_OnlineFolderName);
|
|
m_OnlineFolderName = XP_STRDUP(name);
|
|
|
|
if (m_OnlineFolderName)
|
|
{
|
|
MailDB *mailDb = NULL;
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(m_pathName, TRUE, &mailDb, m_master, &wasCreated);
|
|
if (mailDb)
|
|
{
|
|
mailDb->m_dbFolderInfo->SetMailboxName(m_OnlineFolderName);
|
|
mailDb->Close();
|
|
|
|
// tell the kids, their online path is different now
|
|
int numberOfChildren = GetNumSubFolders();
|
|
int status = 0;
|
|
for (int childIndex = 0; (childIndex < numberOfChildren) && !status; childIndex++)
|
|
{
|
|
MSG_IMAPFolderInfoMail *currentChild = (MSG_IMAPFolderInfoMail *) GetSubFolder(childIndex);
|
|
status = currentChild->ParentRenamed(m_OnlineFolderName);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
#define kIMAPFolderCacheFormat "\t%s"
|
|
#define kIMAPFolderInfoCacheVersion 2
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::WriteToCache (XP_File f)
|
|
{
|
|
// This function is coupled tightly with ReadFromCache,
|
|
// and loosely with ReadDBFolderInfo
|
|
|
|
XP_FilePrintf (f, "\t%d", kIMAPFolderInfoCacheVersion);
|
|
XP_FilePrintf (f, kIMAPFolderCacheFormat, GetOnlineName());
|
|
|
|
return MSG_FolderInfoMail::WriteToCache (f);
|
|
}
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::ReadFromCache (char *buf)
|
|
{
|
|
MsgERR err = eSUCCESS;
|
|
char *saveBuf = buf;
|
|
int version;
|
|
int tokensRead = sscanf (buf, "\t%d", &version);
|
|
SkipCacheTokens (&buf, tokensRead);
|
|
|
|
if (version == 2)
|
|
{
|
|
char *tmpName = (char*) XP_ALLOC(XP_STRLEN(buf));
|
|
if (tmpName)
|
|
{
|
|
char *name = buf + 1; // skip over tab;
|
|
char *secondTab = XP_STRCHR(name, '\t');
|
|
if (secondTab)
|
|
{
|
|
*secondTab = '\0';
|
|
XP_FREEIF(m_OnlineFolderName);
|
|
m_OnlineFolderName = XP_STRDUP(name); // kinda hacky. maybe scan/printf wasn't such a good idea
|
|
*secondTab = '\t';
|
|
}
|
|
SkipCacheTokens (&buf, 1);
|
|
XP_FREE(tmpName);
|
|
}
|
|
else
|
|
err = eOUT_OF_MEMORY;
|
|
// m_HaveReadNameFromDB = TRUE;
|
|
}
|
|
else if (version == 1)
|
|
{
|
|
// this was really a MSG_FolderInfoMail generated line so reset buf
|
|
// and call base class.
|
|
buf = saveBuf;
|
|
}
|
|
else
|
|
err = eUNKNOWN;
|
|
|
|
err = MSG_FolderInfoMail::ReadFromCache (buf);
|
|
|
|
return err;
|
|
}
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::ReadDBFolderInfo (XP_Bool force /* = FALSE */)
|
|
{
|
|
MsgERR err = eSUCCESS;
|
|
|
|
if (m_master->InitFolderFromCache (this) && m_OnlineFolderName)
|
|
return err;
|
|
|
|
if (!m_OnlineFolderName || force)
|
|
{
|
|
MailDB *mailDb = NULL;
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(m_pathName, TRUE, &mailDb, m_master, &wasCreated);
|
|
if (mailDb)
|
|
{
|
|
XPStringObj serverPathName;
|
|
mailDb->m_dbFolderInfo->GetMailboxName(serverPathName);
|
|
if (XP_STRLEN(serverPathName))
|
|
{
|
|
// this is a current db that was created with the path name
|
|
// m_name was read from the db by MSG_FolderInfoMail::MSG_FolderInfoMail
|
|
// for imap folders, the name in the db is the full server path
|
|
XP_FREEIF(m_OnlineFolderName);
|
|
m_OnlineFolderName = XP_STRDUP(serverPathName);
|
|
|
|
// set m_name to the leaf of this.
|
|
if (!XP_STRCASECMP(m_OnlineFolderName,"INBOX"))
|
|
{
|
|
char *imapInboxName = XP_GetString(MK_MSG_IMAP_INBOX_NAME);
|
|
if (imapInboxName && *imapInboxName)
|
|
SetName(imapInboxName);
|
|
else
|
|
SetName("Inbox");
|
|
}
|
|
else
|
|
{
|
|
char *leafSlash = NULL;
|
|
const char *namespacePrefix = m_host->GetNamespacePrefixForFolder(m_OnlineFolderName);
|
|
XP_Bool folderIsNamespace = (namespacePrefix &&
|
|
!XP_STRCMP(namespacePrefix, m_OnlineFolderName));
|
|
if (!folderIsNamespace)
|
|
leafSlash = XP_STRRCHR(m_OnlineFolderName, '/');
|
|
if (leafSlash)
|
|
{
|
|
SetName(++leafSlash);
|
|
}
|
|
else
|
|
{
|
|
SetName(m_OnlineFolderName); // this is the leaf
|
|
}
|
|
}
|
|
}
|
|
else // this db is probably from before we store the path in it.
|
|
{ // Take a wild guess. Works for most platforms. A failed
|
|
// guess will be deleted when we delete non verified folders
|
|
m_OnlineFolderName = CreateOnlineVersionOfLocalPathString( *m_master->GetPrefs(), m_pathName);
|
|
mailDb->m_dbFolderInfo->SetMailboxName(m_OnlineFolderName);
|
|
}
|
|
|
|
// this is probably the first time we opened this db, so while we are here with an open
|
|
// db, preload a few more values that would cause a db open during mailbox discovery
|
|
char hierSeparator = GetOnlineHierarchySeparator();
|
|
|
|
// Call base class, but make sure it knows not to get the name again
|
|
m_HaveReadNameFromDB = TRUE;
|
|
err = MSG_FolderInfoMail::ReadDBFolderInfo(force);
|
|
|
|
mailDb->Close();
|
|
}
|
|
else
|
|
{
|
|
// can't open the DB? Well take a wild guess. Most stuff will be pretty horked but we can't return NULL
|
|
m_OnlineFolderName = CreateOnlineVersionOfLocalPathString( *m_master->GetPrefs(), m_pathName);
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
const char* MSG_IMAPFolderInfoMail::GetOnlineName()
|
|
{
|
|
ReadDBFolderInfo();
|
|
|
|
if (!m_OnlineFolderName) // memory allocation error?
|
|
{
|
|
MSG_IMAPFolderInfoMail *nonConstThis = (MSG_IMAPFolderInfoMail *) this;
|
|
nonConstThis->m_OnlineFolderName = kBogusImapFolderName; // invalid imap name
|
|
}
|
|
|
|
return m_OnlineFolderName;
|
|
}
|
|
|
|
MSG_IMAPHost * MSG_IMAPFolderInfoMail::GetIMAPHost()
|
|
{
|
|
return m_host;
|
|
}
|
|
|
|
MSG_IMAPFolderInfoContainer * MSG_IMAPFolderInfoMail::GetIMAPContainer()
|
|
{
|
|
return (m_host) ? m_host->GetHostFolderInfo() : (MSG_IMAPFolderInfoContainer *)NULL;
|
|
}
|
|
|
|
const char* MSG_IMAPFolderInfoMail::GetHostName()
|
|
{
|
|
return (m_host) ? m_host->GetHostName() : GetMaster()->GetPrefs()->GetPopHost();
|
|
}
|
|
|
|
// make sure the m_OnlineFolderName is pulled from the db and that m_name
|
|
// is the leaf of this m_OnlineFolderName
|
|
const char* MSG_IMAPFolderInfoMail::GetName() // Name of this folder (as presented to user).
|
|
{
|
|
if (!m_OnlineFolderName)
|
|
GetOnlineName(); // lazy initialize from db
|
|
return m_name;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::SetIsOnlineVerified(XP_Bool verified)
|
|
{
|
|
m_VerifiedAsOnlineFolder = verified;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::SetHierarchyIsOnlineVerified(XP_Bool verified)
|
|
{
|
|
// recursion for the children
|
|
int numberOfChildren = GetNumSubFolders();
|
|
for (int childIndex = 0; childIndex < numberOfChildren; childIndex++)
|
|
{
|
|
MSG_IMAPFolderInfoMail *currentChild = (MSG_IMAPFolderInfoMail *) GetSubFolder(childIndex);
|
|
if (currentChild)
|
|
currentChild->SetHierarchyIsOnlineVerified(verified);
|
|
}
|
|
// this level
|
|
SetIsOnlineVerified(verified);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::GetIsOnlineVerified()
|
|
{
|
|
return m_VerifiedAsOnlineFolder;
|
|
}
|
|
|
|
int32 MSG_IMAPFolderInfoMail::GetUnverifiedFolders(MSG_IMAPFolderInfoMail** result,
|
|
int32 resultsize)
|
|
{
|
|
int num = 0;
|
|
if (!GetIsOnlineVerified()) {
|
|
if (result && num < resultsize) {
|
|
result[num] = this;
|
|
}
|
|
num++;
|
|
}
|
|
MSG_IMAPFolderInfoMail *folder = NULL;
|
|
for (int i=0; i < m_subFolders->GetSize(); i++) {
|
|
folder = (MSG_IMAPFolderInfoMail*) m_subFolders->GetAt(i);
|
|
num += folder->GetUnverifiedFolders(result + num, resultsize - num);
|
|
}
|
|
return num;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::SetRunningIMAPUrl(MSG_RunningState runningIMAPUrl)
|
|
{
|
|
m_runningIMAPUrl = runningIMAPUrl;
|
|
}
|
|
|
|
MSG_RunningState MSG_IMAPFolderInfoMail::GetRunningIMAPUrl()
|
|
{
|
|
return m_runningIMAPUrl;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::SetHasOfflineEvents(XP_Bool hasOfflineEvents)
|
|
{
|
|
m_hasOfflineEvents = hasOfflineEvents;
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::GetHasOfflineEvents()
|
|
{
|
|
return m_hasOfflineEvents;
|
|
}
|
|
|
|
// define the message download stream functions, implemented at the bottom of file
|
|
unsigned int MessageDownLoadStreamWriteReady(NET_StreamClass *stream);
|
|
|
|
int IMAPHandleBlockForDataBaseCreate(NET_StreamClass *stream, const char *str, int32 msgUID);
|
|
void IMAPFinishStreamForDataBaseCreate(NET_StreamClass *stream);
|
|
void IMAPAbortStreamForDataBaseCreate (NET_StreamClass *stream, int status);
|
|
|
|
int IMAPHandleBlockForMessageCopyDownload(NET_StreamClass *stream, const char *str, int32 strlength);
|
|
void IMAPFinishStreamForMessageCopyDownload(NET_StreamClass *stream);
|
|
void IMAPAbortStreamForMessageCopyDownload(NET_StreamClass *stream, int status);
|
|
|
|
int MSG_IMAPFolderInfoMail::CreateMessageWriteStream(msg_move_state *state, uint32 msgSize, uint32 msg_flags)
|
|
{
|
|
imapMessageFlagsType imapFlags = kNoImapMsgFlag;
|
|
|
|
if (msg_flags & MSG_FLAG_READ)
|
|
imapFlags |= kImapMsgSeenFlag;
|
|
if (msg_flags & MSG_FLAG_REPLIED)
|
|
imapFlags |= kImapMsgAnsweredFlag;
|
|
if (msg_flags & MSG_FLAG_MARKED)
|
|
imapFlags |= kImapMsgFlaggedFlag;
|
|
|
|
// graph the progress for the user
|
|
state->totalSizeForProgressGraph = msgSize;
|
|
FE_GraphProgressInit(state->sourcePane->GetContext(),
|
|
NULL,
|
|
state->totalSizeForProgressGraph);
|
|
|
|
// remember these if we have to upload this later.
|
|
state->msgSize = msgSize;
|
|
state->msg_flags = msg_flags;
|
|
// upload the size for this message
|
|
if (state->imap_connection)
|
|
{
|
|
IMAP_UploadAppendMessageSize(state->imap_connection, msgSize,imapFlags);
|
|
state->haveUploadedMessageSize = TRUE;
|
|
}
|
|
else
|
|
{
|
|
XP_Trace(" no connection to append size to - what to do?\n");
|
|
}
|
|
|
|
// wait until the connection tells us which socket to write to
|
|
state->uploadToIMAPsocket = NULL;
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::MessageWriteStreamIsReadyForWrite(msg_move_state *state)
|
|
{
|
|
return state->uploadToIMAPsocket != 0;
|
|
}
|
|
|
|
|
|
uint32 MSG_IMAPFolderInfoMail::SeekToEndOfMessageStore(msg_move_state * /*state*/)
|
|
{return 0;} // we don't seek for online folder
|
|
|
|
|
|
|
|
uint32 MSG_IMAPFolderInfoMail::WriteMessageBuffer(msg_move_state *state, char *buffer, uint32 bytesToWrite, MailMessageHdr *offlineHdr)
|
|
{
|
|
uint32 bytesWritten = 0;
|
|
if (offlineHdr && state->destDB)
|
|
bytesWritten = offlineHdr->AddToOfflineMessage(buffer, bytesToWrite, state->destDB->GetDB());
|
|
else
|
|
bytesWritten = NET_BlockingWrite(state->uploadToIMAPsocket, buffer, bytesToWrite);
|
|
|
|
return bytesWritten; // success
|
|
}
|
|
|
|
|
|
void MSG_IMAPFolderInfoMail::CloseMessageWriteStream(msg_move_state *state)
|
|
{
|
|
// see if this will notify the end of a message
|
|
NET_BlockingWrite(state->uploadToIMAPsocket, CRLF, 2);
|
|
/* wrong context
|
|
FE_GraphProgressDestroy (GetFolderPaneContext(),
|
|
NULL,
|
|
state->totalSizeForProgressGraph,
|
|
state->messageBytesMovedSoFar);
|
|
*/
|
|
// imap was waiting for a specific number of bytes. Once they got there, we were done.
|
|
(state->uploadCompleteFunction) (state->uploadCompleteArgument);
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::SaveMessageHeader(msg_move_state * /*state*/, MailMessageHdr * /*hdr*/, uint32 /*messageKey*/, MessageDB * /*srcDB = NULL*/)
|
|
{} // we don't know the message key until our next full sync with the mailbox
|
|
|
|
|
|
|
|
class IMAPdownLoadStreamData {
|
|
public:
|
|
IMAPdownLoadStreamData();
|
|
~IMAPdownLoadStreamData();
|
|
|
|
void Clear();
|
|
|
|
MWContext *urlContext;
|
|
uint32 msgSize;
|
|
ParseOutgoingMessage outgoingParser;
|
|
char *obuffer;
|
|
int32 obufferSize;
|
|
int32 obufferIndex;
|
|
char *m_mozillaStatus;
|
|
};
|
|
|
|
IMAPdownLoadStreamData::IMAPdownLoadStreamData()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
IMAPdownLoadStreamData::~IMAPdownLoadStreamData()
|
|
{
|
|
XP_FREEIF(m_mozillaStatus);
|
|
}
|
|
|
|
|
|
void IMAPdownLoadStreamData::Clear()
|
|
{
|
|
urlContext = NULL;
|
|
msgSize = 0;
|
|
obuffer = NULL;
|
|
obufferSize = 0;
|
|
obufferIndex = 0;
|
|
m_mozillaStatus = NULL;
|
|
}
|
|
|
|
NET_StreamClass *MSG_IMAPFolderInfoMail::CreateDownloadMessageStream(ImapActiveEntry *ce,
|
|
uint32 size, const char *content_type,
|
|
XP_Bool content_modified)
|
|
{
|
|
MSG_Pane *urlPane = IMAP_GetActiveEntryPane(ce);
|
|
OfflineImapGoOnlineState *offlineImapGoOnlineState = (urlPane) ? urlPane->GetGoOnlineState() : (OfflineImapGoOnlineState *)NULL;
|
|
DownloadOfflineImapState *downloadOfflineImapState = (offlineImapGoOnlineState)
|
|
? offlineImapGoOnlineState->GetDownloadOfflineImapState() : (DownloadOfflineImapState *)NULL;
|
|
|
|
NET_StreamClass *returnStream = (NET_StreamClass *) XP_ALLOC(sizeof(NET_StreamClass));
|
|
IMAPdownLoadStreamData *imapStreamData = new IMAPdownLoadStreamData;
|
|
|
|
if (returnStream && imapStreamData) // all streams share some characteristics
|
|
{
|
|
imapStreamData->urlContext = urlPane->GetContext();
|
|
imapStreamData->msgSize = size;
|
|
imapStreamData->obuffer = NULL;
|
|
imapStreamData->obufferSize = 0;
|
|
imapStreamData->obufferIndex = 0;
|
|
|
|
returnStream->window_id = urlPane->GetContext();
|
|
returnStream->data_object = imapStreamData;
|
|
returnStream->is_write_ready = MessageDownLoadStreamWriteReady;
|
|
returnStream->is_multipart = FALSE;
|
|
}
|
|
|
|
switch (m_DownLoadState) {
|
|
case kDownLoadingAllMessageHeaders:
|
|
if (returnStream)
|
|
{
|
|
returnStream->name = XP_STRDUP("kDownLoadingAllMessageHeaders");
|
|
returnStream->put_block = IMAPHandleBlockForDataBaseCreate;
|
|
returnStream->complete = IMAPFinishStreamForDataBaseCreate;
|
|
returnStream->abort = IMAPAbortStreamForDataBaseCreate;
|
|
}
|
|
break;
|
|
case kDownLoadMessageForCopy:
|
|
if (returnStream)
|
|
{
|
|
returnStream->name = XP_STRDUP("kDownLoadMessageForCopy");
|
|
returnStream->put_block = IMAPHandleBlockForMessageCopyDownload;
|
|
returnStream->complete = IMAPFinishStreamForMessageCopyDownload;
|
|
returnStream->abort = IMAPAbortStreamForMessageCopyDownload;
|
|
}
|
|
break;
|
|
case kDownLoadMessageForOfflineDB:
|
|
if (returnStream)
|
|
{
|
|
delete imapStreamData;
|
|
returnStream->data_object = NULL; // set differently by CreateOfflineImapStream
|
|
// so lets not leak.
|
|
|
|
returnStream->name = XP_STRDUP("kDownLoadMessageForOfflineDB");
|
|
DownloadOfflineImapState::CreateOfflineImapStream(returnStream, this, NULL, downloadOfflineImapState);
|
|
if (!downloadOfflineImapState && offlineImapGoOnlineState)
|
|
{
|
|
DownloadOfflineImapState *downloadState = (DownloadOfflineImapState *) returnStream->data_object;
|
|
offlineImapGoOnlineState->SetDownloadOfflineImapState(downloadState);
|
|
downloadState->SetDeleteOnComplete(FALSE);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
delete imapStreamData;
|
|
|
|
XP_Bool captureOffline = (GetFolderPrefFlags() & MSG_FOLDER_PREF_OFFLINE) != 0;
|
|
|
|
NET_StreamClass *displayStream = IMAP_CreateDisplayStream(ce,captureOffline, size, content_type);
|
|
|
|
// If this is an offline imap folder, don't pass up the chance to cache this message.
|
|
// Note that we would not be here if there already was a body
|
|
// Don't do this if the message body has been modified (parts left out)
|
|
if (captureOffline && !content_modified)
|
|
DownloadOfflineImapState::CreateOfflineImapStream(returnStream, this, displayStream);
|
|
else
|
|
{
|
|
// this is a pure display stream. mkimap4.cpp in libnet will expect currentIMAPfolder==NULL in this case
|
|
urlPane->GetContext()->currentIMAPfolder = NULL;
|
|
XP_FREE(returnStream);
|
|
returnStream = displayStream;
|
|
}
|
|
}
|
|
}
|
|
return returnStream;
|
|
}
|
|
|
|
#define ServerBugWorkAroundString "Status: 0"
|
|
|
|
uint32 MSG_IMAPFolderInfoMail::WriteBlockToImapServer(DBMessageHdr *msgHeader, //move this header
|
|
msg_move_state *state, // track it in this state
|
|
MailMessageHdr *offlineHdr, // NON-NULL means offline append
|
|
XP_Bool actuallySendToServer)
|
|
{
|
|
// copy state->size or one message
|
|
uint32 messageLength = msgHeader->GetByteLength();
|
|
uint32 bytesLeft = messageLength - state->messageBytesMovedSoFar;
|
|
uint32 nRead=0;
|
|
uint32 nOfBytesWritten=0;
|
|
|
|
XP_Bool lastCharacterOfPrevBufferWasCR = FALSE;
|
|
|
|
do { // if the last char of this buffer is CR, then do another read/write
|
|
// we don't want to store that tidbit of info for the next visit
|
|
uint32 maxBytesItsSafeToConvert = state->size/2;
|
|
uint32 numberOfBytesToRead = bytesLeft > maxBytesItsSafeToConvert ? maxBytesItsSafeToConvert : bytesLeft;
|
|
nRead = XP_FileRead (state->buf, numberOfBytesToRead, state->infid);
|
|
XP_ASSERT(nRead == numberOfBytesToRead);
|
|
char *currentWriteableBuffer = state->buf;
|
|
if (state->messageBytesMovedSoFar == 0) // skip the first line. It's the Berkely envelope
|
|
{
|
|
// there might be one or more newlines before the envelope, so eat chars until we get the envelope
|
|
char *endOfLine = FindNextLineTerminatingCharacter(currentWriteableBuffer);
|
|
while (!msg_IsEnvelopeLine(currentWriteableBuffer, (endOfLine - currentWriteableBuffer) + 1))
|
|
{
|
|
currentWriteableBuffer = endOfLine;
|
|
if (*currentWriteableBuffer && (*++currentWriteableBuffer == LF)) // skip the line terminator, any kind
|
|
currentWriteableBuffer++;
|
|
endOfLine = FindNextLineTerminatingCharacter(currentWriteableBuffer);
|
|
}
|
|
|
|
// ok, so now currentWriteableBuffer is at the envelope, endOfLine is at the next eol char
|
|
// advance currentWriteableBuffer to leave room only for ServerBugWorkAroundString
|
|
currentWriteableBuffer += (endOfLine - currentWriteableBuffer) - XP_STRLEN(ServerBugWorkAroundString);
|
|
|
|
// replace the envelope with the mail server bug work around
|
|
char eolChar = *endOfLine;
|
|
XP_STRCPY(currentWriteableBuffer, ServerBugWorkAroundString);
|
|
*endOfLine = eolChar; // replace NULL left by XP_STRCPY
|
|
|
|
currentWriteableBuffer = FindNextLineTerminatingCharacter(currentWriteableBuffer);
|
|
if (*currentWriteableBuffer && (*++currentWriteableBuffer == LF)) // skip the line terminator, any kind
|
|
currentWriteableBuffer++;
|
|
}
|
|
uint32 nOfBytesToWrite = ConvertBufferToRFC822LineTermination(currentWriteableBuffer,
|
|
nRead - (currentWriteableBuffer - state->buf),
|
|
&lastCharacterOfPrevBufferWasCR);
|
|
if (actuallySendToServer)
|
|
nOfBytesWritten += WriteMessageBuffer(state, currentWriteableBuffer, nOfBytesToWrite, offlineHdr);
|
|
else
|
|
nOfBytesWritten += nOfBytesToWrite;
|
|
state->messageBytesMovedSoFar += nRead;
|
|
bytesLeft -= nRead;
|
|
} while (lastCharacterOfPrevBufferWasCR && (state->messageBytesMovedSoFar < messageLength));
|
|
|
|
return nOfBytesWritten;
|
|
}
|
|
|
|
|
|
uint32 MSG_IMAPFolderInfoMail::MessageCopySize(DBMessageHdr *msgHeader, msg_move_state *state)
|
|
{
|
|
// seek to the beginning of the message
|
|
XP_FileSeek (state->infid,
|
|
msgHeader->GetMessageOffset(),
|
|
SEEK_SET);
|
|
|
|
|
|
uint32 nOfBytesWritten = 0;
|
|
state->messageBytesMovedSoFar = 0;
|
|
while (state->messageBytesMovedSoFar < msgHeader->GetByteLength())
|
|
nOfBytesWritten += WriteBlockToImapServer(msgHeader, state, NULL, FALSE); // count only, no writing!
|
|
|
|
state->messageBytesMovedSoFar = 0; // we actually did not write anything
|
|
return nOfBytesWritten;
|
|
}
|
|
|
|
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::OfflineImapMsgToLocalFolder(MailMessageHdr *sourceHeader, MSG_FolderInfoMail *destFolder, MessageDB *sourceDB, msg_move_state *state)
|
|
{
|
|
MsgERR stopit = 0;
|
|
|
|
if (sourceHeader->GetOfflineMessageLength(state->destDB->GetDB()))
|
|
{
|
|
XP_File writeFid = XP_FileOpen (destFolder->GetPathname(), xpMailFolder, XP_FILE_APPEND_BIN);
|
|
if (writeFid)
|
|
{
|
|
XP_StatStruct destFolderStat;
|
|
uint32 newMsgPosition = 0;
|
|
if (!XP_Stat(destFolder->GetPathname(), &destFolderStat, xpMailFolder))
|
|
newMsgPosition = destFolderStat.st_size;
|
|
|
|
uint32 offlineMsgSize = sourceHeader->WriteOfflineMessage(writeFid, sourceDB->GetDB());
|
|
XP_FileClose( writeFid );
|
|
|
|
if (offlineMsgSize == 0)
|
|
{
|
|
// cruds. truncate back to where we started the file
|
|
XP_FileTruncate(destFolder->GetPathname(), xpMailFolder, newMsgPosition);
|
|
stopit = MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
}
|
|
|
|
if (!stopit)
|
|
{
|
|
MailMessageHdr *newMailHdr = new MailMessageHdr;
|
|
if (newMailHdr)
|
|
{
|
|
newMailHdr->CopyFromMsgHdr(sourceHeader, sourceDB->GetDB(), state->destDB->GetDB());
|
|
newMailHdr->PurgeOfflineMessage(state->destDB->GetDB());
|
|
newMailHdr->SetMessageKey(newMsgPosition);
|
|
newMailHdr->SetMessageSize(offlineMsgSize);
|
|
|
|
stopit = state->destDB->AddHdrToDB(newMailHdr, NULL, TRUE);
|
|
MailDB::SetFolderInfoValid(destFolder->GetPathname(), 0, 0);
|
|
destFolder->SummaryChanged();
|
|
}
|
|
else
|
|
stopit = MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
|
|
MailDB *sourceMailDB = sourceDB->GetMailDB();
|
|
if (state->ismove && sourceMailDB)
|
|
{
|
|
// the only playback action needed to to delete the source message
|
|
DBOfflineImapOperation *op = sourceMailDB->GetOfflineOpForKey(sourceHeader->GetMessageKey(), TRUE);
|
|
if (op)
|
|
{
|
|
op->SetImapFlagOperation(op->GetNewMessageFlags() | kImapMsgDeletedFlag);
|
|
delete op;
|
|
}
|
|
else
|
|
stopit = MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
|
|
}
|
|
}
|
|
}
|
|
else
|
|
stopit = MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
}
|
|
else
|
|
stopit = MK_MSG_ONLINE_IMAP_WITH_NO_BODY;
|
|
|
|
return stopit;
|
|
}
|
|
|
|
|
|
DBOfflineImapOperation *MSG_IMAPFolderInfoMail::GetClearedOriginalOp(DBOfflineImapOperation *op, MailDB **originalDB)
|
|
{
|
|
DBOfflineImapOperation *returnOp = NULL;
|
|
XP_ASSERT(op->GetOperationFlags() & kMoveResult);
|
|
|
|
XPStringObj originalBoxName;
|
|
MessageKey originalKey;
|
|
op->GetSourceInfo(originalBoxName, originalKey);
|
|
|
|
MSG_FolderInfoMail *sourceFolder = GetMaster()->FindImapMailFolder(originalBoxName);
|
|
if (!sourceFolder)
|
|
sourceFolder = GetMaster()->FindMailFolder(originalBoxName,FALSE);
|
|
if (sourceFolder)
|
|
{
|
|
if (sourceFolder->GetFlags() & MSG_FOLDER_FLAG_IMAPBOX)
|
|
{
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(sourceFolder->GetPathname(), TRUE, originalDB, GetMaster(), &wasCreated);
|
|
}
|
|
else
|
|
MailDB::Open(sourceFolder->GetPathname(), TRUE, originalDB);
|
|
|
|
if (*originalDB)
|
|
{
|
|
returnOp = (*originalDB)->GetOfflineOpForKey(originalKey, FALSE);
|
|
if (returnOp)
|
|
{
|
|
XPStringObj moveDestination;
|
|
returnOp->GetMoveDestination((*originalDB)->GetDB(), moveDestination);
|
|
if (!XP_STRCMP(moveDestination, this->GetOnlineName()))
|
|
returnOp->ClearMoveOperation((*originalDB)->GetDB());
|
|
}
|
|
}
|
|
}
|
|
|
|
return returnOp;
|
|
}
|
|
|
|
DBOfflineImapOperation *MSG_IMAPFolderInfoMail::GetOriginalOp(DBOfflineImapOperation *op, MailDB **originalDB)
|
|
{
|
|
DBOfflineImapOperation *returnOp = NULL;
|
|
|
|
XPStringObj originalBoxName;
|
|
MessageKey originalKey;
|
|
op->GetSourceInfo(originalBoxName, originalKey);
|
|
|
|
MSG_FolderInfoMail *sourceFolder = GetMaster()->FindImapMailFolder(originalBoxName);
|
|
if (!sourceFolder)
|
|
sourceFolder = GetMaster()->FindMailFolder(originalBoxName,FALSE);
|
|
if (sourceFolder)
|
|
{
|
|
if (sourceFolder->GetFlags() & MSG_FOLDER_FLAG_IMAPBOX)
|
|
{
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(sourceFolder->GetPathname(), TRUE, originalDB, GetMaster(), &wasCreated);
|
|
}
|
|
else
|
|
MailDB::Open(sourceFolder->GetPathname(), TRUE, originalDB);
|
|
|
|
if (*originalDB)
|
|
{
|
|
returnOp = (*originalDB)->GetOfflineOpForKey(originalKey, FALSE);
|
|
}
|
|
}
|
|
|
|
return returnOp;
|
|
}
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::BeginOfflineCopy (MSG_FolderInfo *dstFolder,
|
|
MessageDB *sourceDB,
|
|
IDArray *srcArray,
|
|
int32 srcCount,
|
|
msg_move_state *state)
|
|
{
|
|
MsgERR stopit = 0;
|
|
XP_Bool operationOnMoveResult = FALSE;
|
|
MailDB *sourceMailDB = sourceDB ? sourceDB->GetMailDB() : 0;
|
|
MSG_IMAPFolderInfoMail *dstImapFolder = dstFolder->GetIMAPFolderInfoMail();
|
|
XP_Bool deleteToTrash = FALSE;
|
|
if (GetMaster() && GetMaster()->GetPrefs())
|
|
deleteToTrash = DeleteIsMoveToTrash();
|
|
|
|
if (sourceMailDB)
|
|
{
|
|
UndoManager *undoManager = NULL;
|
|
|
|
if (state && state->sourcePane)
|
|
undoManager = state->sourcePane->GetUndoManager();
|
|
|
|
XP_Bool shouldUndoOffline = undoManager && NET_IsOffline();
|
|
if (shouldUndoOffline)
|
|
undoManager->StartBatch();
|
|
// save the future ops in the source DB, if this is not a imap->local copy/move
|
|
|
|
if (state->destDB && !stopit) // offline delete
|
|
{
|
|
// get the highest key in the dest db, so we can make up our fake keys
|
|
XP_Bool highWaterDeleted = FALSE;
|
|
XP_Bool haveWarnedUserAboutMoveTargets = FALSE;
|
|
MessageKey fakeBase = 1;
|
|
fakeBase += state->destDB->ListHighwaterMark();
|
|
|
|
// put fake message in destination db, delete source if move
|
|
for (int sourceKeyIndex=0; !stopit && (sourceKeyIndex < srcCount); sourceKeyIndex++)
|
|
{
|
|
XP_Bool messageReturningHome = FALSE;
|
|
char *originalBoxName = XP_STRDUP(GetOnlineName());
|
|
MessageKey originalKey = srcArray->GetAt(sourceKeyIndex);
|
|
|
|
if (dstFolder->GetFlags() & MSG_FOLDER_FLAG_IMAPBOX)
|
|
{
|
|
DBOfflineImapOperation *sourceOp = sourceMailDB->GetOfflineOpForKey(originalKey, TRUE);
|
|
if (sourceOp)
|
|
{
|
|
MailDB *originalDB = NULL;
|
|
if (sourceOp->GetOperationFlags() == kMoveResult) // offline move
|
|
{
|
|
// gracious me, we are moving something we already moved while offline!
|
|
// find the original operation and clear it!
|
|
DBOfflineImapOperation *originalOp = GetClearedOriginalOp(sourceOp, &originalDB);
|
|
if (originalOp)
|
|
{
|
|
XPStringObj originalString;
|
|
sourceOp->GetSourceInfo(originalString,originalKey);
|
|
FREEIF(originalBoxName);
|
|
originalBoxName = XP_STRDUP(originalString);
|
|
originalKey = sourceOp->GetMessageKey();
|
|
|
|
if (state->ismove)
|
|
sourceMailDB->DeleteOfflineOp(sourceOp->GetMessageKey());
|
|
delete sourceOp;
|
|
|
|
sourceOp = originalOp;
|
|
if (dstImapFolder && !XP_STRCMP(originalBoxName, dstImapFolder->GetOnlineName()))
|
|
{
|
|
messageReturningHome = TRUE;
|
|
originalDB->DeleteOfflineOp(originalOp->GetMessageKey());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!messageReturningHome)
|
|
{
|
|
const char *destinationPath = ((MSG_IMAPFolderInfoMail *) dstFolder)->GetOnlineName();
|
|
int32 opType = kMsgCopy;
|
|
|
|
if (state->ismove)
|
|
{
|
|
sourceOp->SetMessageMoveOperation(destinationPath); // offline move
|
|
opType = kMsgMoved;
|
|
}
|
|
else
|
|
sourceOp->AddMessageCopyOperation(destinationPath); // offline copy
|
|
if (shouldUndoOffline && undoManager->GetState() == UndoIdle)
|
|
{ // only need undo if we're really offline and not pseudo offline
|
|
OfflineIMAPUndoAction *undoAction = new
|
|
OfflineIMAPUndoAction(state->sourcePane, (MSG_FolderInfo*) this, sourceOp->GetMessageKey(), opType,
|
|
sourceMailDB->GetFolderInfo(), dstFolder, 0, NULL, 0);
|
|
if (undoAction)
|
|
undoManager->AddUndoAction(undoAction);
|
|
}
|
|
delete sourceOp;
|
|
}
|
|
if (originalDB)
|
|
originalDB->Close();
|
|
}
|
|
else
|
|
stopit = MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
|
|
}
|
|
|
|
MailMessageHdr *mailHdr = sourceMailDB->GetMailHdrForKey(originalKey);
|
|
if (mailHdr)
|
|
{
|
|
XP_Bool successfulCopy = FALSE;
|
|
highWaterDeleted = !highWaterDeleted && state->ismove && deleteToTrash &&
|
|
(mailHdr->GetMessageKey() == sourceMailDB->m_dbFolderInfo->m_LastMessageUID);
|
|
|
|
if (dstFolder->GetFlags() & MSG_FOLDER_FLAG_IMAPBOX)
|
|
{
|
|
MailMessageHdr *newMailHdr = new MailMessageHdr;
|
|
if (newMailHdr)
|
|
{
|
|
newMailHdr->CopyFromMsgHdr(mailHdr, sourceMailDB->GetDB(), state->destDB->GetDB());
|
|
newMailHdr->SetMessageKey(fakeBase + sourceKeyIndex);
|
|
stopit = state->destDB->AddHdrToDB(newMailHdr, NULL, TRUE);
|
|
}
|
|
else
|
|
stopit = MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
|
|
if (!stopit)
|
|
{
|
|
DBOfflineImapOperation *destOp = state->destDB->GetOfflineOpForKey(fakeBase + sourceKeyIndex, TRUE);
|
|
if (destOp)
|
|
{
|
|
// check if this is a move back to the original mailbox, in which case
|
|
// we just delete the offline operation.
|
|
if (messageReturningHome)
|
|
{
|
|
state->destDB->DeleteOfflineOp(destOp->GetMessageKey());
|
|
}
|
|
else
|
|
{
|
|
state->destDB->SetSourceMailbox(destOp, originalBoxName, originalKey);
|
|
if (shouldUndoOffline && undoManager->GetState() == UndoIdle)
|
|
{
|
|
OfflineIMAPUndoAction *undoAction = new
|
|
OfflineIMAPUndoAction(state->sourcePane, (MSG_FolderInfo*) this, destOp->GetMessageKey(), kAddedHeader,
|
|
state->destDB->GetFolderInfo(), dstFolder, 0, newMailHdr);
|
|
if (undoAction)
|
|
undoManager->AddUndoAction(undoAction);
|
|
}
|
|
}
|
|
delete destOp;
|
|
}
|
|
else
|
|
stopit = MK_MSG_ERROR_WRITING_MAIL_FOLDER;
|
|
}
|
|
successfulCopy = stopit == 0;
|
|
}
|
|
else
|
|
{
|
|
MSG_FolderInfoMail *mailDest = dstFolder->GetMailFolderInfo();
|
|
if (mailDest)
|
|
{
|
|
stopit = OfflineImapMsgToLocalFolder(mailHdr, mailDest, sourceMailDB, state);
|
|
successfulCopy = (stopit == 0);
|
|
}
|
|
}
|
|
|
|
|
|
if (state->ismove && successfulCopy)
|
|
{
|
|
int32 opType = kDeletedMsg;
|
|
if (!deleteToTrash)
|
|
opType = kMsgMarkedDeleted;
|
|
if (shouldUndoOffline && undoManager->GetState() == UndoIdle) {
|
|
OfflineIMAPUndoAction *undoAction = new
|
|
OfflineIMAPUndoAction(state->sourcePane, (MSG_FolderInfo*) this, mailHdr->GetMessageKey(), opType,
|
|
sourceMailDB->GetFolderInfo(), dstFolder, 0, mailHdr);
|
|
if (undoAction)
|
|
undoManager->AddUndoAction(undoAction);
|
|
}
|
|
if (deleteToTrash)
|
|
sourceMailDB->DeleteMessage(mailHdr->GetMessageKey(), NULL, FALSE);
|
|
else
|
|
sourceMailDB->MarkImapDeleted(mailHdr->GetMessageKey(),TRUE,NULL); // offline delete
|
|
}
|
|
|
|
delete mailHdr;
|
|
|
|
if (!successfulCopy)
|
|
highWaterDeleted = FALSE;
|
|
}
|
|
FREEIF(originalBoxName);
|
|
}
|
|
|
|
|
|
if (sourceMailDB->m_dbFolderInfo->GetNumVisibleMessages() && highWaterDeleted)
|
|
{
|
|
ListContext *listContext = NULL;
|
|
DBMessageHdr *highHdr = NULL;
|
|
if (sourceMailDB->ListLast(&listContext, &highHdr) == eSUCCESS)
|
|
{
|
|
sourceMailDB->m_dbFolderInfo->m_LastMessageUID = highHdr->GetMessageKey();
|
|
delete highHdr;
|
|
// sourceMailDB->m_dbFolderInfo->setDirty(); DMB TODO
|
|
sourceMailDB->ListDone(listContext);
|
|
}
|
|
}
|
|
|
|
state->destDB->Commit();
|
|
SummaryChanged();
|
|
dstFolder->SummaryChanged();
|
|
|
|
}
|
|
|
|
// load the next message in the message pane if needed
|
|
if (state->nextKeyToLoad && (state->sourcePane->GetPaneType() == MSG_MESSAGEPANE))
|
|
{
|
|
MSG_MessagePane *messagePane = (MSG_MessagePane *) state->sourcePane;
|
|
messagePane->LoadMessage(this, state->nextKeyToLoad, NULL, TRUE);
|
|
}
|
|
if (shouldUndoOffline)
|
|
undoManager->EndBatch();
|
|
}
|
|
|
|
return stopit ? stopit : -1;
|
|
}
|
|
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::BeginCopyingMessages (MSG_FolderInfo *dstFolder,
|
|
MessageDB *sourceDB,
|
|
IDArray *srcArray,
|
|
MSG_UrlQueue *urlQueue,
|
|
int32 srcCount,
|
|
MessageCopyInfo *copyInfo)
|
|
{
|
|
MsgERR returnError = CheckForLegalCopy(dstFolder);
|
|
|
|
msg_move_state *state = ©Info->moveState;
|
|
|
|
if (returnError != 0)
|
|
return returnError;
|
|
|
|
state->destfolder = dstFolder;
|
|
|
|
if (NET_IsOffline() || (!urlQueue && ShouldPerformOperationOffline() && state->destDB))
|
|
return BeginOfflineCopy(dstFolder, sourceDB, srcArray, srcCount, state);
|
|
|
|
// build a list of message UIDs
|
|
char *idString = AllocateImapUidString(*srcArray);
|
|
|
|
char *copyURL = NULL;
|
|
state->sourcePane->GetContext()->msgCopyInfo->imapOnLineCopyState = kInProgress;
|
|
|
|
MSG_IMAPFolderInfoMail *onlineDestinationFolder = dstFolder->GetIMAPFolderInfoMail();
|
|
|
|
if (FOLDER_MAIL == dstFolder->GetType()) // online to offline
|
|
{
|
|
// will be set in MSG_StartImapMessageToOfflineFolderDownload
|
|
// m_DownLoadState = kDownLoadMessageForCopy;
|
|
|
|
state->sourcePane->GetContext()->mailMaster = m_master;
|
|
state->sourcePane->GetContext()->currentIMAPfolder = this;
|
|
|
|
if (idString)
|
|
{
|
|
copyURL = CreateImapOnToOfflineCopyUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
idString,
|
|
"OFFLINEFOLDER",
|
|
TRUE, // ids are uids
|
|
state->ismove);
|
|
}
|
|
}
|
|
// check if we're copying within the same host
|
|
else if (onlineDestinationFolder && onlineDestinationFolder->GetIMAPHost() == GetIMAPHost())
|
|
{
|
|
|
|
if (idString)
|
|
{
|
|
copyURL = CreateImapOnlineCopyUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
idString,
|
|
onlineDestinationFolder->GetOnlineName(),
|
|
onlineDestinationFolder->GetOnlineHierarchySeparator(),
|
|
TRUE, // ids are uids
|
|
state->ismove);
|
|
}
|
|
}
|
|
// copying across hosts
|
|
else if (onlineDestinationFolder)
|
|
{
|
|
IDArray keysToSave;
|
|
for (int32 i = 0; i < srcCount; i++)
|
|
keysToSave.Add (srcArray->GetAt(i));
|
|
FREEIF(idString);
|
|
return DownloadToTempFileAndUpload(copyInfo, keysToSave, dstFolder, sourceDB);
|
|
|
|
}
|
|
MSG_UrlQueue::AddUrlToPane(copyURL, PostMessageCopyUrlExitFunc, state->sourcePane);
|
|
FREEIF(copyURL);
|
|
|
|
FREEIF(idString);
|
|
|
|
return returnError;
|
|
}
|
|
|
|
|
|
XP_Bool
|
|
MSG_IMAPFolderInfoMail::UndoMoveCopyMessagesHelper(MSG_Pane *urlPane,
|
|
const IDArray &keysToUndo,
|
|
UndoObject *undoObject)
|
|
{
|
|
UndoManager *undoManager = urlPane->GetUndoManager();
|
|
URL_Struct *url_struct = NULL;
|
|
|
|
if (urlPane->GetContext() == GetFolderLoadingContext())
|
|
{
|
|
FE_Alert(urlPane->GetContext(), XP_GetString(MK_MSG_NO_UNDO_DURING_IMAP_FOLDER_LOAD));
|
|
return FALSE;
|
|
}
|
|
|
|
if (undoManager->GetState() == UndoUndoing) {
|
|
StartUpdateOfNewlySelectedFolder(urlPane, FALSE, NULL, &keysToUndo, TRUE);
|
|
}
|
|
else if (undoManager->GetState() == UndoRedoing) {
|
|
StartUpdateOfNewlySelectedFolder(urlPane, FALSE, NULL, &keysToUndo, FALSE);
|
|
}
|
|
|
|
if (url_struct) {
|
|
url_struct->msg_pane = urlPane;
|
|
undoObject->UndoURLHook(url_struct);
|
|
urlPane->GetURL(url_struct, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void MSG_IMAPFolderInfoMail::NotifyFolderLoaded(MSG_Pane *urlPane, XP_Bool /*loadWasInterrupted*/ /* default false */)
|
|
{
|
|
// if we were loading in urlPane context, then clear the loading context
|
|
if (urlPane)
|
|
m_LoadingInContext = NULL;
|
|
|
|
// MSG_PaneNotifyFolderLoaded now handled in the ImapFolderSelectCompleteExitFunction
|
|
}
|
|
|
|
|
|
int
|
|
MSG_IMAPFolderInfoMail::FinishCopyingMessages(MWContext *currentContext,
|
|
MSG_FolderInfo *srcFolder,
|
|
MSG_FolderInfo * dstFolder,
|
|
MessageDB* /*srcDB*/,
|
|
IDArray *srcArray,
|
|
int32 srcCount,
|
|
msg_move_state *state)
|
|
{
|
|
int returnErr = 0;
|
|
|
|
if (!dstFolder->TestSemaphore(this))
|
|
{
|
|
returnErr = dstFolder->AcquireSemaphore(this);
|
|
if (returnErr)
|
|
{
|
|
FE_Alert (currentContext, XP_GetString(returnErr));
|
|
return MK_MSG_FOLDER_BUSY;
|
|
}
|
|
}
|
|
|
|
MessageDBView * view;
|
|
// added to handle search as view...
|
|
if (state->sourcePane && state->sourcePane->GetPaneType() == MSG_SEARCHPANE)
|
|
view = ((MSG_SearchPane *) state->sourcePane)->GetView(srcFolder);
|
|
else
|
|
view = (state->sourcePane) ? state->sourcePane->GetMsgView() : 0;
|
|
|
|
// during debugging I realized that the state can change from
|
|
// kInProgress -> kSuccessfulCopy -> kSuccessfulDelete before this
|
|
// function gets called. Code written to process interim states
|
|
// may not get called - km
|
|
|
|
switch (state->sourcePane->GetContext()->msgCopyInfo->imapOnLineCopyState) {
|
|
case kInProgress:
|
|
returnErr = 0; // tell ProcessMailbox to keep it coming..
|
|
break;
|
|
case kSuccessfulCopy:
|
|
if (!state->ismove) // This is the final state for copy
|
|
{
|
|
state->moveCompleted = TRUE;
|
|
|
|
// The download worked, update the destination panes
|
|
if (dstFolder->GetType() == FOLDER_MAIL)
|
|
((MSG_FolderInfoMail *) dstFolder)->CloseOfflineDestinationDbAfterMove(state);
|
|
// else imap, handled by url exit function
|
|
returnErr = MK_CONNECTED;
|
|
UpdatePendingCounts(dstFolder, srcArray);
|
|
}
|
|
// search as view modification...inform search pane, that we are done with this view
|
|
// if (state->sourcePane && state->sourcePane->GetPaneType() == MSG_SEARCHPANE)
|
|
// ((MSG_SearchPane *) state->sourcePane)->CloseView(srcFolder);
|
|
|
|
break;
|
|
case kFailedCopy:
|
|
returnErr = MK_MSG_ONLINE_COPY_FAILED;
|
|
break;
|
|
case kSuccessfulDelete:
|
|
{
|
|
XP_Bool showDeletedMessages = ShowDeletedMessages();
|
|
|
|
// if we filtering, assume missing are unread, otherwise read.
|
|
// don't diddle counts if playing back offline ops because the counts have already been diddled.
|
|
if (!m_master->GetPlayingBackOfflineOps())
|
|
UpdatePendingCounts(dstFolder, srcArray, TRUE, !state->sourcePane->GetActiveImapFiltering());
|
|
|
|
state->moveCompleted = TRUE;
|
|
// The download worked, update the destination panes
|
|
if (dstFolder->GetType() == FOLDER_MAIL)
|
|
((MSG_FolderInfoMail *) dstFolder)->CloseOfflineDestinationDbAfterMove(state);
|
|
// else imap, handled by url exit function
|
|
|
|
|
|
if (!showDeletedMessages)
|
|
{
|
|
// Run the delete loop forwards
|
|
// so the indices will be right for DeleteMessagesByIndex
|
|
MSG_ViewIndex doomedIndex = 0;
|
|
XP_Bool highWaterUIDwasDeleted = FALSE;
|
|
MailDB *affectedDB = NULL;
|
|
|
|
if (view)
|
|
{
|
|
affectedDB = (MailDB *) view->GetDB();
|
|
|
|
IDArray viewIndices;
|
|
int total = srcArray->GetSize(); // Check for bug 113085 rb
|
|
XP_ASSERT(srcCount == total);
|
|
for (int32 i = 0; i < total; i++)
|
|
{
|
|
// should we stop on error?
|
|
MessageKey doomedKey = srcArray->GetAt(i);
|
|
doomedIndex = view->FindViewIndex(doomedKey);
|
|
if (doomedIndex != MSG_VIEWINDEXNONE)
|
|
{
|
|
viewIndices.Add(doomedIndex);
|
|
|
|
// Tell the FE we're deleting this message in case they want to close
|
|
// any open message windows.
|
|
if (MSG_THREADPANE == state->sourcePane->GetPaneType())
|
|
FE_PaneChanged (state->sourcePane, TRUE, MSG_PaneNotifyMessageDeleted, (uint32) doomedKey);
|
|
}
|
|
|
|
if ((doomedKey == affectedDB->m_dbFolderInfo->m_LastMessageUID) && !highWaterUIDwasDeleted)
|
|
highWaterUIDwasDeleted = TRUE;
|
|
}
|
|
if (viewIndices.GetSize() != 0)
|
|
returnErr = view->DeleteMessagesByIndex (viewIndices.GetData(), viewIndices.GetSize(), TRUE /* delete from db */);
|
|
else
|
|
returnErr = 0;
|
|
}
|
|
else
|
|
{
|
|
// we are here because we did a get new mail from somewhere besides the inbox thread pane
|
|
// and some new messages were filtered to other folders
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(GetPathname(), FALSE, &affectedDB, GetMaster(), &wasCreated);
|
|
if (affectedDB)
|
|
{
|
|
IDArray messageKeys;
|
|
messageKeys.SetSize(srcCount);
|
|
for (int32 msgIndex=0; msgIndex < srcCount; msgIndex++)
|
|
{
|
|
MessageKey doomedKey = srcArray->GetAt(msgIndex);
|
|
messageKeys.SetAt(msgIndex, doomedKey);
|
|
|
|
if ((doomedKey == affectedDB->m_dbFolderInfo->m_LastMessageUID) && !highWaterUIDwasDeleted)
|
|
highWaterUIDwasDeleted = TRUE;
|
|
}
|
|
|
|
returnErr = affectedDB->DeleteMessages(messageKeys, NULL);
|
|
}
|
|
// else, how could we fail to open this db, oh well at least we don't mess with it
|
|
}
|
|
|
|
if (highWaterUIDwasDeleted && affectedDB)
|
|
{
|
|
// our sync info with the state of the server mailbox is stale
|
|
// grab the highest message keyleft, if there is one
|
|
|
|
// also, check for 0 messages here
|
|
|
|
if (affectedDB->m_dbFolderInfo->GetNumVisibleMessages())
|
|
{
|
|
ListContext *listContext = NULL;
|
|
DBMessageHdr *highHdr = NULL;
|
|
if (affectedDB->ListLast(&listContext, &highHdr) == eSUCCESS)
|
|
{
|
|
affectedDB->m_dbFolderInfo->m_LastMessageUID = highHdr->GetMessageKey();
|
|
delete highHdr;
|
|
// DMB TODO affectedDB->m_dbFolderInfo->setDirty();
|
|
affectedDB->ListDone(listContext);
|
|
}
|
|
}
|
|
}
|
|
// if I opened the db, I should close it
|
|
if (!view && affectedDB)
|
|
{
|
|
affectedDB->Close();
|
|
affectedDB = NULL;
|
|
}
|
|
if (view)
|
|
SetUrlForNextMessageLoad(state,view,doomedIndex);
|
|
}
|
|
else
|
|
{
|
|
DBFolderInfo *folderInfo = NULL;
|
|
MessageDB *messagedb = NULL;
|
|
MsgERR opendbErr = GetDBFolderInfoAndDB(&folderInfo, &messagedb);
|
|
if (opendbErr == 0)
|
|
{
|
|
MailDB *mailDB = messagedb->GetMailDB();
|
|
if (mailDB)
|
|
SetIMAPDeletedFlag(mailDB, *srcArray);
|
|
messagedb->Close();
|
|
messagedb = NULL;
|
|
}
|
|
}
|
|
|
|
srcFolder->SummaryChanged();
|
|
|
|
|
|
// Begin Undo hook
|
|
{
|
|
UndoManager *undoManager = state->sourcePane->GetUndoManager();
|
|
if (undoManager && undoManager->GetState() == UndoIdle) {
|
|
IMAPMoveCopyMessagesUndoAction *undoAction = new
|
|
IMAPMoveCopyMessagesUndoAction(state->sourcePane,
|
|
srcFolder,
|
|
dstFolder,
|
|
state->ismove,
|
|
srcArray->GetAt(0),
|
|
state->nextKeyToLoad);
|
|
if (undoAction) {
|
|
int32 i;
|
|
for (i=0; i < srcCount; i++)
|
|
undoAction->AddKey(srcArray->GetAt(i));
|
|
undoManager->AddUndoAction(undoAction);
|
|
}
|
|
}
|
|
}
|
|
// End Undo hook
|
|
|
|
delete srcArray; // shouldn't this be deleted everywhere???
|
|
|
|
// search as view modification...inform search pane, that we are done with this view
|
|
// if (state->sourcePane && state->sourcePane->GetPaneType() == MSG_SEARCHPANE)
|
|
// ((MSG_SearchPane *) state->sourcePane)->CloseView(srcFolder);
|
|
|
|
returnErr = MK_CONNECTED; // add code to delete from view
|
|
}
|
|
break;
|
|
case kFailedDelete:
|
|
// The download worked, update the destination panes
|
|
if (dstFolder->GetType() == FOLDER_MAIL)
|
|
dstFolder->SummaryChanged();
|
|
// else imap, handled by url exit function
|
|
|
|
returnErr = MK_MSG_ONLINE_MOVE_FAILED;
|
|
break;
|
|
}
|
|
|
|
|
|
return returnErr;
|
|
}
|
|
|
|
|
|
void MSG_IMAPFolderInfoMail::StoreImapFlags(MSG_Pane *paneForFlagUrl, imapMessageFlagsType flags, XP_Bool addFlags,
|
|
const IDArray &keysToFlag, MSG_UrlQueue *urlQueue /* = NULL */)
|
|
{
|
|
URL_Struct *url = NULL;
|
|
|
|
// fire off the url to do this marking
|
|
if (urlQueue || !NET_IsOffline() /* ShouldPerformOperationOffline() */)
|
|
{
|
|
// If we are not offline, we want to add the flag changes to the url queue. Otherwise our
|
|
// changes are reflected only after offline ops are executed. The screen wont match the
|
|
// flag states if they get new mail. Bug #108636
|
|
char *idString = MSG_IMAPFolderInfoMail::AllocateImapUidString(keysToFlag);
|
|
char *urlString;
|
|
|
|
if (addFlags)
|
|
urlString = CreateImapAddMessageFlagsUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
idString,
|
|
flags,
|
|
TRUE);
|
|
else
|
|
urlString = CreateImapSubtractMessageFlagsUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
idString,
|
|
flags,
|
|
TRUE);
|
|
|
|
url = NET_CreateURLStruct (urlString, NET_DONT_RELOAD);
|
|
url->internal_url = TRUE;
|
|
url->msg_pane = paneForFlagUrl;
|
|
MSG_UrlQueue::AddUrlToPane(url, NULL, paneForFlagUrl, TRUE);
|
|
|
|
FREEIF(urlString);
|
|
FREEIF( idString );
|
|
}
|
|
else
|
|
{
|
|
MailDB *mailDb = NULL; // change flags offline
|
|
XP_Bool wasCreated=FALSE;
|
|
|
|
ImapMailDB::Open(GetPathname(), TRUE, &mailDb, GetMaster(), &wasCreated);
|
|
if (mailDb)
|
|
{
|
|
UndoManager *undoManager = NULL;
|
|
MSG_Pane *fPane = mailDb->GetMaster()->FindFirstPaneOfType(MSG_FOLDERPANE);
|
|
uint32 total = keysToFlag.GetSize();
|
|
|
|
if (fPane)
|
|
undoManager = fPane->GetUndoManager();
|
|
|
|
for (int keyIndex=0; keyIndex < total; keyIndex++)
|
|
{
|
|
DBOfflineImapOperation *op = mailDb->GetOfflineOpForKey(keysToFlag[keyIndex], TRUE);
|
|
if (op)
|
|
{
|
|
MailDB *originalDB = NULL;
|
|
if (op->GetOperationFlags() & kMoveResult)
|
|
{
|
|
// get the op in the source db and change the flags there
|
|
DBOfflineImapOperation *originalOp = GetOriginalOp(op, &originalDB);
|
|
if (originalOp)
|
|
{
|
|
if (undoManager && undoManager->GetState() == UndoIdle && NET_IsOffline()) {
|
|
OfflineIMAPUndoAction *undoAction = new
|
|
OfflineIMAPUndoAction(paneForFlagUrl, (MSG_FolderInfo*) this, op->GetMessageKey(), kFlagsChanged,
|
|
this, NULL, flags, NULL, addFlags);
|
|
if (undoAction)
|
|
undoManager->AddUndoAction(undoAction);
|
|
}
|
|
delete op;
|
|
op = originalOp;
|
|
}
|
|
}
|
|
|
|
if (addFlags)
|
|
op->SetImapFlagOperation(op->GetNewMessageFlags() | flags);
|
|
else
|
|
op->SetImapFlagOperation(op->GetNewMessageFlags() & ~flags);
|
|
delete op;
|
|
|
|
if (originalDB)
|
|
{
|
|
originalDB->Close();
|
|
originalDB = NULL;
|
|
}
|
|
}
|
|
}
|
|
mailDb->Close();
|
|
mailDb = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::DeleteAllMessages(MSG_Pane *paneForDeleteUrl, XP_Bool deleteSubFoldersToo /* = TRUE */) // implements empty trash
|
|
{
|
|
MsgERR preFlightError = 0;
|
|
|
|
|
|
if (NET_IsOffline())
|
|
{
|
|
MailDB *mailDB = NULL;
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(GetPathname(), TRUE, &mailDB, GetMaster(), &wasCreated);
|
|
if (mailDB)
|
|
{
|
|
MessageKey fakeId = mailDB->GetUnusedFakeId();
|
|
DBOfflineImapOperation *op = mailDB->GetOfflineOpForKey(fakeId, TRUE);
|
|
if (op)
|
|
{
|
|
op->SetDeleteAllMsgs();
|
|
delete op;
|
|
}
|
|
}
|
|
URL_Struct *url_struct = NET_CreateURLStruct("mailbox:?null", NET_SUPER_RELOAD);
|
|
if (url_struct)
|
|
{
|
|
url_struct->msg_pane = paneForDeleteUrl;
|
|
url_struct->pre_exit_fn = PostEmptyImapTrashExitFunc;
|
|
paneForDeleteUrl->GetURL(url_struct, FALSE);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// this url will delete the subfolders as well. Preflight this with the user.
|
|
if (deleteSubFoldersToo && HasSubFolders())
|
|
{
|
|
MSG_FolderIterator iterator(this);
|
|
MSG_FolderInfo *currentFolder = iterator.First();
|
|
while (currentFolder && eSUCCESS == preFlightError)
|
|
{
|
|
if (currentFolder != this)
|
|
preFlightError = paneForDeleteUrl->PreflightDeleteFolder(currentFolder, TRUE /*getUserConfirmation*/);
|
|
currentFolder = iterator.Next();
|
|
}
|
|
}
|
|
|
|
if (eSUCCESS == preFlightError)
|
|
{
|
|
char *trashUrl = CreateImapDeleteAllMessagesUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator());
|
|
if (trashUrl)
|
|
{
|
|
MSG_UrlQueue::AddUrlToPane(trashUrl, PostEmptyImapTrashExitFunc, paneForDeleteUrl);
|
|
XP_FREE(trashUrl);
|
|
}
|
|
}
|
|
return preFlightError;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::DeleteSpecifiedMessages(MSG_Pane *paneForDeleteUrl,
|
|
const IDArray &keysToDelete,
|
|
MessageKey loadKeyOnExit)
|
|
{
|
|
// see if we should "undelete" a selection of deleted messages
|
|
XP_Bool doUndelete = FALSE;
|
|
if (loadKeyOnExit == MSG_MESSAGEKEYNONE)
|
|
{
|
|
MailDB *newDb = NULL;
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(GetPathname(), TRUE, &newDb, GetMaster(), &wasCreated);
|
|
if (newDb)
|
|
{
|
|
doUndelete = newDb->AllMessageKeysImapDeleted(keysToDelete);
|
|
if (doUndelete)
|
|
SetIMAPDeletedFlag(newDb,keysToDelete,FALSE);
|
|
newDb->Close();
|
|
}
|
|
}
|
|
|
|
|
|
// build the index string
|
|
char *idString = AllocateImapUidString(keysToDelete);
|
|
|
|
XP_Bool shouldRunInBackground = ShouldPerformOperationOffline();
|
|
if (doUndelete || shouldRunInBackground)
|
|
{
|
|
StoreImapFlags(paneForDeleteUrl, kImapMsgDeletedFlag, !doUndelete, keysToDelete);
|
|
if (ShouldPerformOperationOffline() && !doUndelete)
|
|
{
|
|
MessagesWereDeleted(paneForDeleteUrl, FALSE, idString);
|
|
if ((loadKeyOnExit != MSG_MESSAGEKEYNONE) && (paneForDeleteUrl->GetPaneType() == MSG_MESSAGEPANE))
|
|
{
|
|
// do loading of next message while offline
|
|
((MSG_MessagePane *) paneForDeleteUrl)->LoadMessage(this, loadKeyOnExit, NULL, TRUE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// do the url boogie
|
|
char *deleteUrl = CreateImapDeleteMessageUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
idString,
|
|
TRUE); // ids are uids
|
|
if (deleteUrl)
|
|
{
|
|
URL_Struct *url_struct = NET_CreateURLStruct(deleteUrl, NET_SUPER_RELOAD);
|
|
if (url_struct)
|
|
{
|
|
if ((loadKeyOnExit != MSG_MESSAGEKEYNONE) && (paneForDeleteUrl->GetPaneType() == MSG_MESSAGEPANE))
|
|
{
|
|
// url exit function will load next message
|
|
((MSG_MessagePane *) paneForDeleteUrl)->SetPostDeleteLoadKey(loadKeyOnExit);
|
|
url_struct->pre_exit_fn = MSG_MessagePane::PostDeleteLoadExitFunction;
|
|
}
|
|
paneForDeleteUrl->GetURL(url_struct, FALSE);
|
|
}
|
|
XP_FREE(deleteUrl);
|
|
}
|
|
}
|
|
FREEIF(idString);
|
|
}
|
|
|
|
// MSG_FolderInfoMail::Adopt does to much. We already know the
|
|
// newFolder pathname and parent sbd exist, so use this during
|
|
// imap folder discovery
|
|
void MSG_IMAPFolderInfoMail::LightWeightAdopt(MSG_IMAPFolderInfoMail *newChild)
|
|
{
|
|
GetSubFolders()->Add (newChild);
|
|
// GetSubFolders()->InsertAt (0, newChild);
|
|
// GetSubFolders()->QuickSort (CompareFolders);
|
|
}
|
|
|
|
|
|
// Try and see if we have any mail pending, if Biff does not run it is because either
|
|
// we have not yet connected to the server or because we are busy talking to it, in
|
|
// which case we don't need to run biff here since the server will talk to us anyway.
|
|
// Called if the user has selected not to get the mail headers automatically when new
|
|
// mail arrives
|
|
|
|
void MSG_IMAPFolderInfoMail::Biff(MWContext *biffContext)
|
|
{
|
|
MailDB *folderDB;
|
|
XP_Bool wasCreated;
|
|
uint32 highWaterUid = 0;
|
|
|
|
if (!NET_IsOffline()) {
|
|
if (ImapMailDB::Open(GetPathname(), FALSE, &folderDB, GetMaster(), &wasCreated) == eSUCCESS)
|
|
{
|
|
highWaterUid = folderDB->m_dbFolderInfo->m_LastMessageUID;
|
|
char *biffUrl = CreateImapBiffUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
highWaterUid);
|
|
if (biffUrl)
|
|
{
|
|
URL_Struct *url_struct = NET_CreateURLStruct(biffUrl, NET_SUPER_RELOAD);
|
|
if (url_struct)
|
|
{
|
|
biffContext->imapURLPane = NULL; // might have a bogus url pane, so clear it.
|
|
biffContext->mailMaster = GetMaster();// so the biff will use the cached connection
|
|
// we know biff isn't running in a pane
|
|
msg_GetURL(biffContext, url_struct, FALSE);
|
|
}
|
|
XP_FREE(biffUrl);
|
|
}
|
|
folderDB->Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Check to see if there is new mail waiting for us at the server, and then see if we should
|
|
// read the headers or not.
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::MailCheck(MWContext *biffContext)
|
|
{
|
|
MSG_Master *master;
|
|
XP_Bool newMail = FALSE;
|
|
|
|
if (NET_IsOffline())
|
|
return FALSE;
|
|
master = GetMaster();
|
|
if (master && (GetFlags() & MSG_FOLDER_FLAG_IMAPBOX))
|
|
{
|
|
TNavigatorImapConnection *imapConnection = NULL;
|
|
|
|
imapConnection = (TNavigatorImapConnection*) master->UnCacheImapConnection(GetHostName(), m_OnlineFolderName);
|
|
if (!imapConnection) {
|
|
biffContext->mailMaster = master;
|
|
Biff(biffContext); // start a connection
|
|
}
|
|
if (imapConnection)
|
|
{
|
|
if (!biffContext->mailMaster)
|
|
biffContext->mailMaster = master;
|
|
if (IMAP_CheckNewMail(imapConnection)) // if new mail, and they want headers we get in here
|
|
{
|
|
newMail = TRUE;
|
|
master->TryToCacheImapConnection(imapConnection, GetHostName(), m_OnlineFolderName); // put back for biff to use
|
|
// we should really find a pane that's not busy, if possible. Or create a progress pane!
|
|
MSG_Pane *pane = master->FindFirstPaneOfType(MSG_FOLDERPANE);
|
|
if (!pane)
|
|
pane = master->FindFirstPaneOfType(MSG_THREADPANE);
|
|
if (!pane)
|
|
pane = master->FindFirstPaneOfType(MSG_MESSAGEPANE);
|
|
if (pane /* && !XP_IsContextBusy(pane->GetContext()) */)
|
|
StartUpdateOfNewlySelectedFolder(pane, FALSE, NULL, NULL, FALSE, FALSE);
|
|
} else {
|
|
if (IMAP_NewMailDetected(imapConnection))
|
|
newMail = TRUE;
|
|
master->TryToCacheImapConnection(imapConnection, GetHostName(), m_OnlineFolderName); // put back for biff to use
|
|
Biff(biffContext); // just check for new mail and get flags
|
|
}
|
|
}
|
|
}
|
|
return newMail;
|
|
}
|
|
|
|
|
|
|
|
void MSG_IMAPFolderInfoMail::MessagesWereDeleted(MSG_Pane *urlPane,
|
|
XP_Bool deleteAllMsgs,
|
|
const char *doomedKeyString)
|
|
{
|
|
XP_Bool showDeletedMessages = ShowDeletedMessages();
|
|
|
|
if (deleteAllMsgs)
|
|
{
|
|
TDBFolderInfoTransfer *originalInfo = NULL;
|
|
MailDB *folderDB;
|
|
XP_Bool wasCreated;
|
|
if (ImapMailDB::Open(GetPathname(), FALSE, &folderDB, GetMaster(), &wasCreated) == eSUCCESS)
|
|
{
|
|
originalInfo = new TDBFolderInfoTransfer(*folderDB->m_dbFolderInfo);
|
|
folderDB->ForceClosed();
|
|
}
|
|
|
|
// Remove summary file.
|
|
XP_FileRemove(GetPathname(), xpMailFolderSummary);
|
|
|
|
// Create a new summary file, update the folder message counts, and
|
|
// Close the summary file db.
|
|
if (ImapMailDB::Open(GetPathname(), TRUE, &folderDB, GetMaster(), &wasCreated) == eSUCCESS)
|
|
{
|
|
if (originalInfo)
|
|
{
|
|
originalInfo->TransferFolderInfo(*folderDB->m_dbFolderInfo);
|
|
delete originalInfo;
|
|
}
|
|
SummaryChanged();
|
|
folderDB->Close();
|
|
}
|
|
|
|
// Reload any thread pane because it's invalid now.
|
|
MSG_ThreadPane* threadPane = NULL;
|
|
if (m_master != NULL)
|
|
threadPane = m_master->FindThreadPaneNamed(GetPathname());
|
|
if (threadPane != NULL)
|
|
threadPane->ReloadFolder();
|
|
return;
|
|
}
|
|
|
|
char *keyTokenString = XP_STRDUP(doomedKeyString);
|
|
IDArray affectedMessages;
|
|
ParseUidString(keyTokenString, affectedMessages);
|
|
|
|
if (doomedKeyString && !showDeletedMessages)
|
|
{
|
|
MessageDBView *trashView = urlPane->GetMsgView();
|
|
|
|
if (trashView && keyTokenString)
|
|
{
|
|
IDArray viewIndexArray;
|
|
|
|
for (uint32 i = 0; i < affectedMessages.GetSize(); i++)
|
|
viewIndexArray.Add(trashView->FindViewIndex(affectedMessages.GetAt(i)));
|
|
|
|
trashView->DeleteMessagesByIndex (viewIndexArray.GetData(), viewIndexArray.GetSize(), TRUE /* delete from db */);
|
|
SummaryChanged();
|
|
}
|
|
|
|
}
|
|
else if (doomedKeyString) // && !imapDeleteIsMoveToTrash
|
|
{
|
|
MailDB *folderDB;
|
|
XP_Bool wasCreated;
|
|
if (ImapMailDB::Open(GetPathname(), FALSE, &folderDB, GetMaster(), &wasCreated) == eSUCCESS)
|
|
{
|
|
SetIMAPDeletedFlag(folderDB, affectedMessages);
|
|
folderDB->Close();
|
|
}
|
|
}
|
|
FREEIF(keyTokenString);
|
|
}
|
|
|
|
|
|
// store MSG_FLAG_IMAP_DELETED in the specified mailhdr records
|
|
void MSG_IMAPFolderInfoMail::SetIMAPDeletedFlag(MailDB *mailDB, const IDArray &msgids, XP_Bool markDeleted)
|
|
{
|
|
MsgERR markStatus = 0;
|
|
uint32 total = msgids.GetSize();
|
|
|
|
for (int index=0; !markStatus && (index < total); index++)
|
|
{
|
|
markStatus = mailDB->MarkImapDeleted(msgids[index], markDeleted, NULL);
|
|
}
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::OpenThreadView(MailDB *mailDB,mailbox_spec *adoptedBoxSpec)
|
|
{
|
|
// update all of the database flags
|
|
if (adoptedBoxSpec && adoptedBoxSpec->folderSelected && adoptedBoxSpec->flagState)
|
|
{
|
|
int numberOfFlags = IMAP_GetFlagStateNumberOfMessages(adoptedBoxSpec->flagState);
|
|
for (int flagIndex=0; flagIndex < numberOfFlags; flagIndex++)
|
|
{
|
|
imap_uid currentUID = IMAP_GetUidOfMessage(flagIndex, adoptedBoxSpec->flagState);
|
|
imapMessageFlagsType serverflags = IMAP_GetMessageFlags(flagIndex, adoptedBoxSpec->flagState);
|
|
|
|
MailMessageHdr *mailHdr = mailDB->GetMailHdrForKey(currentUID);
|
|
if (mailHdr)
|
|
{
|
|
// noop calls that set db flags can cause a view invalidation and excessive
|
|
uint32 dbFlags = mailHdr->GetFlags();
|
|
|
|
if ( ((dbFlags & MSG_FLAG_READ) == 0) != ((serverflags & kImapMsgSeenFlag) == 0))
|
|
mailDB->MarkRead( currentUID, (serverflags & kImapMsgSeenFlag) != 0);
|
|
|
|
if ( ((dbFlags & MSG_FLAG_REPLIED) == 0) != ((serverflags & kImapMsgAnsweredFlag) == 0))
|
|
mailDB->MarkReplied( currentUID, (serverflags & kImapMsgAnsweredFlag) != 0);
|
|
|
|
if ( ((dbFlags & MSG_FLAG_MARKED) == 0) != ((serverflags & kImapMsgFlaggedFlag) == 0))
|
|
mailDB->MarkMarked( currentUID, (serverflags & kImapMsgFlaggedFlag) != 0);
|
|
|
|
if ( ((dbFlags & MSG_FLAG_IMAP_DELETED) == 0) != ((serverflags & kImapMsgDeletedFlag) == 0))
|
|
mailDB->MarkImapDeleted(currentUID, (serverflags & kImapMsgDeletedFlag) != 0, NULL);
|
|
|
|
delete mailHdr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// find the thread pane in the master
|
|
MSG_ThreadPane *threadPane = m_master->FindThreadPaneNamed(GetPathname());
|
|
MessageDBView *threadView = NULL;
|
|
|
|
// reinitialize the db view of the thread pane, if it has not been opened yet
|
|
if (threadPane)
|
|
threadView = threadPane->GetMsgView();
|
|
|
|
if (threadPane && threadView && !threadView->GetDB())
|
|
{
|
|
uint32 pCount=0;
|
|
threadPane->StartingUpdate(MSG_NotifyAll, 0, 0);
|
|
threadView->Open(mailDB, threadView->GetViewType() , &pCount);
|
|
threadPane->EndingUpdate(MSG_NotifyAll, 0, 0);
|
|
}
|
|
else // if there is no thread pane then we should close the db we would
|
|
mailDB->Close(); // have passed to its view.
|
|
}
|
|
|
|
|
|
|
|
// hierarchy separating character on the server
|
|
char MSG_IMAPFolderInfoMail::GetOnlineHierarchySeparator()
|
|
{
|
|
if (m_OnlineHierSeparator == kOnlineHierarchySeparatorUnknown)
|
|
{
|
|
MailDB *mailDB=NULL;
|
|
XP_Bool dbWasCreated=FALSE;
|
|
|
|
MsgERR dbStatus = ImapMailDB::Open(GetPathname(),
|
|
TRUE, // create if necessary
|
|
&mailDB,
|
|
m_master,
|
|
&dbWasCreated);
|
|
|
|
if (dbStatus == eSUCCESS)
|
|
{
|
|
m_OnlineHierSeparator = (char) mailDB->m_dbFolderInfo->GetIMAPHierarchySeparator();
|
|
mailDB->Close();
|
|
}
|
|
}
|
|
|
|
return m_OnlineHierSeparator ? m_OnlineHierSeparator : kOnlineHierarchySeparatorUnknown;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::SetOnlineHierarchySeparator(char separator)
|
|
{
|
|
if (separator != m_OnlineHierSeparator)
|
|
{
|
|
MailDB *mailDB=NULL;
|
|
XP_Bool dbWasCreated=FALSE;
|
|
|
|
MsgERR dbStatus = ImapMailDB::Open(GetPathname(),
|
|
TRUE, // create if necessary
|
|
&mailDB,
|
|
m_master,
|
|
&dbWasCreated);
|
|
|
|
if (dbStatus == eSUCCESS)
|
|
{
|
|
if (m_OnlineHierSeparator == kOnlineHierarchySeparatorUnknown)
|
|
m_OnlineHierSeparator = (char) mailDB->m_dbFolderInfo->GetIMAPHierarchySeparator();
|
|
if (separator != m_OnlineHierSeparator)
|
|
{
|
|
m_OnlineHierSeparator = separator;
|
|
mailDB->m_dbFolderInfo->SetIMAPHierarchySeparator(m_OnlineHierSeparator);
|
|
}
|
|
|
|
mailDB->Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// both of these algorithms assume that key arrays and flag states are sorted by increasing key.
|
|
void MSG_IMAPFolderInfoMail::FindKeysToDelete(const IDArray &existingKeys, IDArray &keysToDelete, TImapFlagAndUidState *flagState)
|
|
{
|
|
XP_Bool imapDeleteIsMoveToTrash = DeleteIsMoveToTrash();
|
|
uint32 total = existingKeys.GetSize();
|
|
uint32 index;
|
|
|
|
int onlineIndex=0; // current index into flagState
|
|
for (uint32 keyIndex=0; keyIndex < total; keyIndex++)
|
|
{
|
|
index = IMAP_GetFlagStateNumberOfMessages(flagState);
|
|
while ((onlineIndex < index) &&
|
|
(existingKeys[keyIndex] > IMAP_GetUidOfMessage(onlineIndex, flagState)) )
|
|
{
|
|
onlineIndex++;
|
|
}
|
|
|
|
// delete this key if it is not there or marked deleted
|
|
if ( (onlineIndex >= IMAP_GetFlagStateNumberOfMessages(flagState) ) ||
|
|
(existingKeys[keyIndex] != IMAP_GetUidOfMessage(onlineIndex, flagState)) ||
|
|
((IMAP_GetMessageFlags(onlineIndex, flagState) & kImapMsgDeletedFlag) && imapDeleteIsMoveToTrash) )
|
|
{
|
|
MessageKey doomedKey = existingKeys[keyIndex];
|
|
if ((int32) doomedKey < 0 && doomedKey != kIdNone && doomedKey != kIdPending)
|
|
continue;
|
|
else
|
|
keysToDelete.Add(existingKeys[keyIndex]);
|
|
}
|
|
|
|
if (existingKeys[keyIndex] == IMAP_GetUidOfMessage(onlineIndex, flagState))
|
|
onlineIndex++;
|
|
}
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::FindKeysToAdd(const IDArray &existingKeys, IDArray &keysToFetch, TImapFlagAndUidState *flagState)
|
|
{
|
|
XP_Bool showDeletedMessages = ShowDeletedMessages();
|
|
|
|
int dbIndex=0; // current index into existingKeys
|
|
uint32 existTotal, numberOfKnownKeys;
|
|
uint32 index;
|
|
|
|
existTotal = numberOfKnownKeys = existingKeys.GetSize();
|
|
index = IMAP_GetFlagStateNumberOfMessages(flagState);
|
|
for (uint32 flagIndex=0; flagIndex < index; flagIndex++)
|
|
{
|
|
while ( (flagIndex < numberOfKnownKeys) && (dbIndex < existTotal) &&
|
|
(existingKeys[dbIndex] < IMAP_GetUidOfMessage(flagIndex, flagState)) )
|
|
dbIndex++;
|
|
|
|
if ( (flagIndex >= numberOfKnownKeys) ||
|
|
(dbIndex >= existTotal) ||
|
|
(existingKeys[dbIndex] != IMAP_GetUidOfMessage(flagIndex , flagState) ) )
|
|
{
|
|
numberOfKnownKeys++;
|
|
if (showDeletedMessages || ! (IMAP_GetMessageFlags(flagIndex, flagState) & kImapMsgDeletedFlag))
|
|
{
|
|
keysToFetch.Add(IMAP_GetUidOfMessage(flagIndex, flagState));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::UpdateFolderStatus(mailbox_spec *adoptedBoxSpec, MSG_Pane * /*url_pane*/)
|
|
{
|
|
MailDB *mailDB=NULL;
|
|
XP_Bool dbWasCreated=FALSE;
|
|
|
|
MsgERR dbStatus = ImapMailDB::Open(GetPathname(),
|
|
TRUE, // create if necessary
|
|
&mailDB,
|
|
m_master,
|
|
&dbWasCreated);
|
|
|
|
if (dbStatus == eSUCCESS)
|
|
{
|
|
DBFolderInfo *groupInfo = mailDB->m_dbFolderInfo;
|
|
// diddle the imap pending counts to reflect the accurate counts according to the server
|
|
int32 totalMessages = groupInfo->GetNumMessages() + groupInfo->GetImapTotalPendingMessages();
|
|
int32 totalUnreadMessages = groupInfo->GetNumNewMessages() + groupInfo->GetImapUnreadPendingMessages();
|
|
int32 totalDelta = adoptedBoxSpec->number_of_messages - totalMessages;
|
|
int32 numNewDelta = adoptedBoxSpec->number_of_unseen_messages - totalUnreadMessages;
|
|
groupInfo->ChangeImapTotalPendingMessages(totalDelta - groupInfo->GetImapTotalPendingMessages());
|
|
groupInfo->ChangeImapUnreadPendingMessages(numNewDelta - groupInfo->GetImapUnreadPendingMessages());
|
|
SummaryChanged();
|
|
mailDB->Close();
|
|
}
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::UpdateNewlySelectedFolder(mailbox_spec *adoptedBoxSpec, MSG_Pane *url_pane)
|
|
{
|
|
MailDB *mailDB=NULL;
|
|
XP_Bool dbWasCreated=FALSE;
|
|
|
|
MSG_Biff_Master::MailCheckEnable((char*) GetPathname(), (XP_Bool) ~NET_IsOffline()); // enable or disable start mail checking if allowed
|
|
|
|
MsgERR dbStatus = ImapMailDB::Open(GetPathname(),
|
|
TRUE, // create if necessary
|
|
&mailDB,
|
|
m_master,
|
|
&dbWasCreated);
|
|
|
|
// remember the info in the maibox_spec. This is currently only used
|
|
// for cacheless view to know how big to make the view, but there
|
|
// could be other uses. It might be better to try to get hold
|
|
// of this from the connection object in the libnet imap thread
|
|
// instead of a potentially stale copy of the data.
|
|
m_mailboxSpec = *adoptedBoxSpec;
|
|
if ((dbStatus == eSUCCESS) && adoptedBoxSpec->folderSelected)
|
|
{
|
|
IDArray existingKeys;
|
|
IDArray keysToDelete;
|
|
IDArray keysToFetch;
|
|
|
|
|
|
mailDB->ListAllIds(existingKeys);
|
|
if (mailDB->ListAllOfflineDeletes(existingKeys) > 0)
|
|
existingKeys.QuickSort();
|
|
if ((mailDB->m_dbFolderInfo->GetImapUidValidity() != adoptedBoxSpec->folder_UIDVALIDITY) && // if UIDVALIDITY Changed
|
|
!NET_IsOffline())
|
|
{
|
|
// store the new UIDVALIDITY value
|
|
mailDB->m_dbFolderInfo->SetImapUidValidity(adoptedBoxSpec->folder_UIDVALIDITY);
|
|
// delete all my msgs, the keys are bogus now
|
|
// add every message in this folder
|
|
keysToDelete.CopyArray(&existingKeys);
|
|
|
|
if (adoptedBoxSpec->flagState)
|
|
{
|
|
IDArray no_existingKeys;
|
|
FindKeysToAdd(no_existingKeys, keysToFetch, adoptedBoxSpec->flagState);
|
|
}
|
|
}
|
|
else if (!adoptedBoxSpec->flagState && !NET_IsOffline()) // if there are no messages on the server
|
|
{
|
|
keysToDelete.CopyArray(&existingKeys);
|
|
}
|
|
else if (!NET_IsOffline())
|
|
{
|
|
FindKeysToDelete(existingKeys, keysToDelete, adoptedBoxSpec->flagState);
|
|
|
|
// if this is the result of an expunge then don't grab headers
|
|
if (!(adoptedBoxSpec->box_flags & kJustExpunged))
|
|
FindKeysToAdd(existingKeys, keysToFetch, adoptedBoxSpec->flagState);
|
|
}
|
|
|
|
|
|
if (keysToDelete.GetSize())
|
|
{
|
|
uint32 total;
|
|
|
|
XP_Bool highWaterDeleted = FALSE;
|
|
url_pane->StartingUpdate(MSG_NotifyNone, 0, 0);
|
|
mailDB->DeleteMessages(keysToDelete,NULL);
|
|
url_pane->EndingUpdate(MSG_NotifyNone, 0, 0);
|
|
total = keysToDelete.GetSize();
|
|
|
|
for (uint32 deleteKeyIndex=0; !highWaterDeleted && deleteKeyIndex < total; deleteKeyIndex++)
|
|
{
|
|
highWaterDeleted = (keysToDelete[deleteKeyIndex] == mailDB->m_dbFolderInfo->m_LastMessageUID);
|
|
}
|
|
|
|
if (highWaterDeleted)
|
|
{
|
|
if (mailDB->m_dbFolderInfo->GetNumVisibleMessages())
|
|
{
|
|
ListContext *listContext = NULL;
|
|
DBMessageHdr *currentHdr = NULL;
|
|
if ((mailDB->ListLast(&listContext, ¤tHdr) == eSUCCESS) &&
|
|
currentHdr)
|
|
{
|
|
mailDB->m_dbFolderInfo->m_LastMessageUID = currentHdr->GetMessageKey();
|
|
delete currentHdr;
|
|
mailDB->ListDone(listContext);
|
|
}
|
|
else
|
|
mailDB->m_dbFolderInfo->m_LastMessageUID = 0;
|
|
}
|
|
else
|
|
mailDB->m_dbFolderInfo->m_LastMessageUID = 0;
|
|
// mailDB->m_dbFolderInfo->setDirty(); DMB TODO
|
|
}
|
|
|
|
SummaryChanged();
|
|
}
|
|
|
|
// if this is the INBOX, tell the stand-alone biff about the new high water mark
|
|
if (GetFlags() & MSG_FOLDER_FLAG_INBOX)
|
|
{
|
|
UpdateStandAloneIMAPBiff(keysToFetch);
|
|
}
|
|
|
|
if (keysToFetch.GetSize())
|
|
{
|
|
MSG_SetBiffStateAndUpdateFE(MSG_BIFF_NewMail);
|
|
// Ending update called in NotifyFetchAnyNeededBodies
|
|
url_pane->StartingUpdate(MSG_NotifyNone, 0, 0);
|
|
|
|
OpenThreadView(mailDB,adoptedBoxSpec);
|
|
mailDB = NULL; // storage adopted by OpenThreadView
|
|
|
|
AddToSummaryMailDB(keysToFetch, url_pane, adoptedBoxSpec);
|
|
}
|
|
else if (url_pane) // somehow xfe gets here during biff(!) with no url_pane
|
|
{
|
|
// Ending update called in NotifyFetchAnyNeededBodies
|
|
url_pane->StartingUpdate(MSG_NotifyNone, 0, 0);
|
|
// let the imap libnet module know that we don't need headers
|
|
IMAP_DoNotDownLoadAnyMessageHeadersForMailboxSelect(adoptedBoxSpec->connection);
|
|
// wait until we can get body id monitor before continuing.
|
|
IMAP_BodyIdMonitor(adoptedBoxSpec->connection, TRUE);
|
|
// I think the real fix for this is to seperate the header ids from body id's.
|
|
NotifyFetchAnyNeededBodies(url_pane, adoptedBoxSpec->connection, mailDB);
|
|
IMAP_BodyIdMonitor(adoptedBoxSpec->connection, FALSE);
|
|
|
|
OpenThreadView(mailDB,adoptedBoxSpec);
|
|
mailDB = NULL; // storage adopted by OpenThreadView
|
|
if (GetGettingMail())
|
|
{
|
|
MSG_Pane *msgPane = url_pane;
|
|
MSG_Pane* parentpane = MSG_GetParentPane(url_pane);
|
|
if (parentpane)
|
|
msgPane = parentpane;
|
|
NET_Progress(msgPane->GetContext(), XP_GetString(MK_POP3_NO_MESSAGES));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (dbStatus != eSUCCESS)
|
|
|
|
{
|
|
if (XP_FileRemove(GetPathname(), xpMailFolderSummary) == 0)
|
|
UpdateNewlySelectedFolder(adoptedBoxSpec, url_pane);
|
|
else
|
|
{
|
|
// This DB is a read only file? We are hosed here.
|
|
// Let the user cancel this select. If we interrupt here
|
|
// we could re-enter NET_InterruptIMAP4.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!adoptedBoxSpec->folderSelected)
|
|
{
|
|
// we got here because the select url failed! Open what we've got
|
|
NotifyFolderLoaded(url_pane, TRUE); // TRUE was "interrupted"
|
|
OpenThreadView(mailDB,adoptedBoxSpec);
|
|
}
|
|
IMAP_FreeBoxSpec(adoptedBoxSpec);
|
|
}
|
|
SetGettingMail(FALSE);
|
|
}
|
|
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::StaticShouldIgnoreFile(const char *fileName)
|
|
{
|
|
XP_Bool ignoreIt = TRUE;
|
|
// for imap we ignore everything that is not a summary mail file
|
|
// or a directory
|
|
|
|
// check for directory
|
|
// cannot use XP_OpenDir for the check because sometimes fileName is a leaf name
|
|
// XP_OpenDir expects a full path. So, fall back on ".sbd" convention
|
|
if (XP_STRLEN(fileName) > XP_STRLEN(".sbd"))
|
|
ignoreIt = 0 != XP_STRCASECMP(fileName + XP_STRLEN(fileName) - XP_STRLEN(".sbd"), ".sbd");
|
|
|
|
if (ignoreIt)
|
|
{
|
|
// platform specific checks for summary mail files
|
|
#if defined (XP_WIN) || defined (XP_MAC) || defined (XP_OS2)
|
|
if (XP_STRLEN(fileName) > XP_STRLEN(".snm"))
|
|
ignoreIt = 0 != XP_STRCASECMP(fileName + XP_STRLEN(fileName) - XP_STRLEN(".snm"), ".snm");
|
|
#endif
|
|
|
|
#if defined (XP_UNIX)
|
|
if (XP_STRLEN(fileName) > XP_STRLEN(".summary"))
|
|
ignoreIt = (0 != XP_STRCASECMP(fileName + XP_STRLEN(fileName) - XP_STRLEN(".summary"), ".summary")) || (*fileName != '.');
|
|
#endif
|
|
}
|
|
|
|
return ignoreIt;
|
|
}
|
|
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::ShouldIgnoreFile (const char *name)
|
|
{
|
|
return StaticShouldIgnoreFile(name);
|
|
}
|
|
|
|
char *MSG_IMAPFolderInfoMail::CreateMailboxNameFromDbName(const char *dbName)
|
|
{
|
|
char *mailboxName = (char*) XP_ALLOC(XP_STRLEN(dbName) + 1);
|
|
if (mailboxName)
|
|
{
|
|
XP_STRCPY(mailboxName, dbName);
|
|
#if defined (XP_WIN) || defined (XP_MAC) || defined(XP_OS2)
|
|
// mac and windows append ".snm"
|
|
if (XP_STRLEN(mailboxName) > XP_STRLEN(".snm"))
|
|
*(mailboxName + XP_STRLEN(mailboxName) - XP_STRLEN(".snm")) = '\0';
|
|
#endif
|
|
#if defined (XP_UNIX)
|
|
// unix format is ".boxname.summary"
|
|
if (XP_STRLEN(mailboxName) > XP_STRLEN(".summary"))
|
|
{
|
|
// we edit the leaf, so find it
|
|
char *leafNode = XP_STRRCHR(mailboxName, '/');
|
|
if (leafNode)
|
|
leafNode++;
|
|
else
|
|
leafNode = mailboxName;
|
|
|
|
*(leafNode + XP_STRLEN(leafNode) - XP_STRLEN(".summary")) = '\0';
|
|
XP_STRCPY(leafNode, leafNode + 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return mailboxName;
|
|
}
|
|
|
|
|
|
void MSG_IMAPFolderInfoMail::GetFilterSearchUrlString(MSG_Filter *currenFilter, char **imapUrlString)
|
|
{
|
|
*imapUrlString = NULL;
|
|
|
|
if (currenFilter->IsRule())
|
|
{
|
|
MSG_Rule *rule=NULL;
|
|
if (currenFilter->GetRule(&rule) == FilterError_Success)
|
|
{
|
|
msg_SearchOnlineMail::Encode(imapUrlString, rule->GetTermList(),
|
|
INTL_DefaultWinCharSetID(0),
|
|
GetFolderCSID() & ~CS_AUTO);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::QueueUpImapFilterUrls(MSG_UrlQueue *urlQueue)
|
|
{
|
|
MSG_FilterList *filters = NULL;
|
|
if (MSG_FilterList::Open(GetMaster(), filterInbox, NULL, this, &filters) == FilterError_Success)
|
|
{
|
|
int32 filtCount=0;
|
|
filters->GetFilterCount(&filtCount);
|
|
for (int32 filterIndex=0; filterIndex < filtCount; filterIndex++)
|
|
{
|
|
MSG_Filter *currenFilter=NULL;
|
|
if (filters->GetFilterAt(filterIndex, ¤Filter) == FilterError_Success)
|
|
{
|
|
char *searchCommandString = NULL;
|
|
GetFilterSearchUrlString(currenFilter, &searchCommandString);
|
|
if (searchCommandString)
|
|
{
|
|
char *searchUrl = CreateImapSearchUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
searchCommandString,
|
|
TRUE); // use uids
|
|
if (searchUrl)
|
|
urlQueue->AddUrlAt(urlQueue->GetCursor() + filterIndex + 1, searchUrl, NULL);
|
|
FREEIF(searchUrl);
|
|
}
|
|
}
|
|
}
|
|
|
|
filters->Close();
|
|
}
|
|
}
|
|
|
|
#define WHITESPACE " \015\012" // token delimiter msgfinfo.h
|
|
|
|
IDArray *MSG_IMAPFolderInfoMail::CreateFilterIDArray(const char *imapSearchResultString, int &numberOfHits)
|
|
{
|
|
// I expect here a string of UID's, the '* SEARCH' should be already stripped
|
|
|
|
// make one pass to count the number of hits
|
|
numberOfHits = 0;
|
|
IDArray *retIDArray = new IDArray;
|
|
char *hitTokenString = XP_STRDUP(imapSearchResultString);
|
|
if (hitTokenString)
|
|
{
|
|
char *hitUidToken = XP_STRTOK(hitTokenString, WHITESPACE);
|
|
while (hitUidToken)
|
|
{
|
|
numberOfHits++;
|
|
hitUidToken = XP_STRTOK(NULL, WHITESPACE);
|
|
}
|
|
FREEIF(hitTokenString);
|
|
}
|
|
|
|
// now create the array - do we really need db anymore?
|
|
if (numberOfHits)
|
|
{
|
|
MailDB *imapDB = NULL;
|
|
XP_Bool wasCreated;
|
|
if (ImapMailDB::Open (GetPathname(), FALSE, &imapDB, GetMaster(), &wasCreated) == eSUCCESS)
|
|
{
|
|
hitTokenString = XP_STRDUP(imapSearchResultString);
|
|
if (hitTokenString)
|
|
{
|
|
char *hitUidToken = XP_STRTOK(hitTokenString, WHITESPACE);
|
|
while (hitUidToken)
|
|
{
|
|
MessageKey hitUid;
|
|
sscanf(hitUidToken, "%ld", &hitUid);
|
|
retIDArray->Add(hitUid);
|
|
hitUidToken = XP_STRTOK(NULL, WHITESPACE);
|
|
}
|
|
FREEIF(hitTokenString);
|
|
}
|
|
|
|
imapDB->Close();
|
|
}
|
|
}
|
|
|
|
return retIDArray;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::LogRuleHit(MSG_Filter *filter, DBMessageHdr *msgHdr, MWContext *context)
|
|
{
|
|
char *filterName = "";
|
|
time_t date;
|
|
char dateStr[40]; /* 30 probably not enough */
|
|
MSG_RuleActionType actionType;
|
|
MSG_Rule *rule;
|
|
void *value;
|
|
|
|
MailDB *imapDB = NULL;
|
|
XP_Bool wasCreated;
|
|
if (ImapMailDB::Open (GetPathname(), FALSE, &imapDB, GetMaster(), &wasCreated) == eSUCCESS)
|
|
{
|
|
MSG_DBHandle db = (imapDB) ? imapDB->GetDB() : 0;
|
|
XPStringObj author;
|
|
XPStringObj subject;
|
|
|
|
FILE *logFile = XP_FileOpen("", xpMailFilterLog, XP_FILE_APPEND); // will this create?
|
|
|
|
filter->GetName(&filterName);
|
|
if (filter->GetRule(&rule) != FilterError_Success)
|
|
return;
|
|
rule->GetAction(&actionType, &value);
|
|
date = msgHdr->GetDate();
|
|
XP_StrfTime(context, dateStr, sizeof(dateStr), XP_DATE_TIME_FORMAT,
|
|
localtime(&date));
|
|
msgHdr->GetAuthor(author, db);
|
|
msgHdr->GetSubject(subject, TRUE, db);
|
|
XP_FilePrintf(logFile, "Applied filter \"%s\" to message from %s - %s at %s\n", filterName,
|
|
(const char *) author, (const char *) subject, dateStr);
|
|
char *actionStr = rule->GetActionStr(actionType);
|
|
char *actionValue = "";
|
|
if (actionType == acMoveToFolder)
|
|
actionValue = (char *) value;
|
|
XP_FilePrintf(logFile, "Action = %s %s\n\n", actionStr, actionValue);
|
|
}
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::StartUpdateOfNewlySelectedFolder(MSG_Pane *paneOfCommand,
|
|
XP_Bool loadingFolder,
|
|
MSG_UrlQueue *urlQueueForSelectURL, // can be NULL
|
|
const IDArray *keysToUndoRedo, // can be NULL
|
|
XP_Bool undo,
|
|
XP_Bool playbackOfflineEvents, /* = TRUE */
|
|
Net_GetUrlExitFunc *selectExitFunction /* = NULL */)
|
|
{
|
|
XP_ASSERT(paneOfCommand);
|
|
|
|
// pane could be gone - just return for now...
|
|
if (!MSG_Pane::PaneInMasterList(paneOfCommand))
|
|
return;
|
|
SetGettingMail(TRUE);
|
|
if (playbackOfflineEvents)
|
|
{ // limit the scope of this db
|
|
|
|
DBFolderInfo *folderInfo = NULL;
|
|
MessageDB *messagedb = NULL;
|
|
|
|
// if there are any offline operations that have not been played back, then do it
|
|
MsgERR opendbErr = GetDBFolderInfoAndDB(&folderInfo, &messagedb);
|
|
if ((opendbErr == 0) && !NET_IsOffline())
|
|
{
|
|
IDArray offlineOperationKeys;
|
|
MailDB *mailDB = messagedb->GetMailDB();
|
|
if (mailDB)
|
|
mailDB->ListAllOfflineOpIds(offlineOperationKeys);
|
|
messagedb->Close();
|
|
messagedb = NULL;
|
|
|
|
XP_Bool offlineCreate = GetFolderPrefFlags() & MSG_FOLDER_PREF_CREATED_OFFLINE;
|
|
|
|
if (offlineCreate || offlineOperationKeys.GetSize() > 0)
|
|
{
|
|
// this can't be right if we have a url queue, because this code
|
|
// doesn't use it. The bug arises if we select the inbox in order to
|
|
// download new headers to filter them before going offline,
|
|
// we don't want to play back these events.
|
|
// So, if we don't have a url queue, or it's not the inbox, play back
|
|
// any offline events.
|
|
XP_ASSERT(!urlQueueForSelectURL);
|
|
// make sure this flag is turned on
|
|
SetFolderPrefFlags(GetFolderPrefFlags() | MSG_FOLDER_PREF_OFFLINEEVENTS);
|
|
OfflineImapGoOnlineState *goOnline = new OfflineImapGoOnlineState(paneOfCommand, this);
|
|
if (goOnline)
|
|
{
|
|
// will force a MSG_PaneNotifyFolderLoaded if offline playbasck fails
|
|
if (loadingFolder)
|
|
paneOfCommand->SetLoadingImapFolder(this);
|
|
|
|
goOnline->ProcessNextOperation();
|
|
SetGettingMail(FALSE);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((opendbErr == 0) && messagedb)
|
|
messagedb->Close();
|
|
|
|
} // limit the scopeof this db
|
|
|
|
|
|
XP_Bool selectFolder = !GetFolderLoadingContext();
|
|
|
|
if (selectFolder)
|
|
{
|
|
// if we're selecting the inbox, and the user wants us to cleanupinbox on exit,
|
|
// just set requiresCleanup to TRUE. It would be nicest if we knew a message
|
|
// had been moved or deleted, but I'm not sure where that would be known.
|
|
if (m_flags & MSG_FOLDER_FLAG_INBOX)
|
|
{
|
|
if (GetIMAPHost()->GetExpungeInboxOnExit())
|
|
m_requiresCleanup = TRUE;
|
|
}
|
|
char *undoString = NULL;
|
|
if (keysToUndoRedo)
|
|
{
|
|
undoString = AllocateImapUidString(*keysToUndoRedo);
|
|
if (undoString)
|
|
{
|
|
// enough space for uno mas
|
|
StrAllocCat(undoString, " ");
|
|
if (undoString)
|
|
{
|
|
XP_MEMMOVE(undoString + 1, undoString, XP_STRLEN(undoString) - 1);
|
|
if (undo)
|
|
*undoString = '-';
|
|
else
|
|
*undoString = '+';
|
|
}
|
|
}
|
|
}
|
|
|
|
char *selectMailboxesURL = GetOnlineName() ? CreateImapMailboxSelectUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
undoString)
|
|
: 0;
|
|
#ifdef DEBUG_bienvenu
|
|
if (!XP_STRCMP(GetName(), "r-thompson"))
|
|
selectMailboxesURL = CreateImapMailboxLITESelectUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator());
|
|
#endif
|
|
if (selectMailboxesURL)
|
|
{
|
|
// we are getting these messages for real now, so stop tricking the fe.
|
|
if (GetNumPendingUnread())
|
|
ChangeNumPendingUnread(-GetNumPendingUnread());
|
|
if (GetNumPendingTotalMessages())
|
|
{
|
|
ChangeNumPendingTotalMessages(-GetNumPendingTotalMessages());
|
|
SummaryChanged();
|
|
}
|
|
|
|
paneOfCommand->GetContext()->mailMaster = m_master;
|
|
paneOfCommand->GetContext()->imapURLPane = paneOfCommand;
|
|
|
|
// Revisit the url queue later, see if we can enable the following case
|
|
// urlQueueForSelectURL = MSG_UrlQueue::AddUrlToPane( selectMailboxesURL,
|
|
// ImapFolderSelectCompleteExitFunction, paneOfCommand);
|
|
// paneOfCommand->SetLoadingImapFolder(this);
|
|
|
|
MSG_UrlQueue *urlQueue = urlQueueForSelectURL;
|
|
|
|
if (!urlQueue)
|
|
urlQueueForSelectURL = urlQueue = MSG_UrlQueue::FindQueue(paneOfCommand);
|
|
if (!urlQueue)
|
|
urlQueue = new MSG_ImapLoadFolderUrlQueue(paneOfCommand);
|
|
if (urlQueue)
|
|
{
|
|
urlQueue->AddInterruptCallback(urlQueue->HandleFolderLoadInterrupt);
|
|
// if the url queue is in a different pane, this pane won't get it's
|
|
// loading imap folder cleared, so don't set it.
|
|
if (paneOfCommand == urlQueue->GetPane())
|
|
{
|
|
paneOfCommand->SetLoadingImapFolder(this);
|
|
// lock others from loading this folder or loading a message this this pane
|
|
m_LoadingInContext = paneOfCommand->GetContext();
|
|
}
|
|
|
|
if (loadingFolder)
|
|
{
|
|
if (!selectExitFunction) // don't override what the caller may have asked for
|
|
selectExitFunction = ImapFolderSelectCompleteExitFunction;
|
|
}
|
|
else if (!selectExitFunction)
|
|
selectExitFunction = ImapFolderClearLoadingFolder;
|
|
|
|
urlQueue->AddUrl( selectMailboxesURL, selectExitFunction);
|
|
|
|
if (!urlQueueForSelectURL) // do we need to start this url queue?
|
|
urlQueue->GetNextUrl();
|
|
}
|
|
FREEIF(selectMailboxesURL);
|
|
|
|
}
|
|
FREEIF(undoString);
|
|
}
|
|
else
|
|
{
|
|
// this folder is already being loaded elsewhere, so just open the view and notify loaded
|
|
MailDB *mailDB=NULL;
|
|
XP_Bool dbWasCreated=FALSE;
|
|
|
|
MsgERR dbStatus = ImapMailDB::Open(GetPathname(),
|
|
TRUE, // create if necessary
|
|
&mailDB,
|
|
m_master,
|
|
&dbWasCreated);
|
|
|
|
XP_Trace("folder already loading, or so we think\n");
|
|
if (dbStatus == eSUCCESS)
|
|
{
|
|
OpenThreadView(mailDB,NULL);
|
|
mailDB = NULL; // storage adopted by OpenThreadView
|
|
|
|
FE_PaneChanged(paneOfCommand, FALSE, MSG_PaneProgressDone, 0);
|
|
// tell the fe that this folder is loaded
|
|
NotifyFolderLoaded(paneOfCommand);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::AddToSummaryMailDB(const IDArray &keysToFetch,
|
|
MSG_Pane *url_pane,
|
|
mailbox_spec *boxSpec)
|
|
{
|
|
uint32 *theKeys = (uint32 *) XP_ALLOC( keysToFetch.GetSize() * sizeof(uint32) );
|
|
if (theKeys)
|
|
{
|
|
uint32 total = keysToFetch.GetSize();
|
|
|
|
for (int keyIndex=0; keyIndex < total; keyIndex++)
|
|
theKeys[keyIndex] = keysToFetch[keyIndex];
|
|
|
|
m_DownLoadState = kDownLoadingAllMessageHeaders;
|
|
|
|
url_pane->GetContext()->imapURLPane = url_pane;
|
|
url_pane->GetContext()->mailMaster = m_master;
|
|
url_pane->GetContext()->currentIMAPfolder = this;
|
|
|
|
char *currentUrl = IMAP_GetCurrentConnectionUrl(boxSpec->connection);
|
|
MSG_UrlQueue *urlQueue = MSG_UrlQueue::FindQueue(currentUrl, url_pane->GetContext());
|
|
FREEIF(currentUrl);
|
|
|
|
#ifdef DEBUG_bienvenu
|
|
// XP_ASSERT(urlQueue && urlQueue->IsIMAPLoadFolderUrlQueue());
|
|
#endif
|
|
SetParseMailboxState(new ParseIMAPMailboxState(m_master, m_host, this,
|
|
urlQueue,
|
|
boxSpec->flagState));
|
|
GetParseMailboxState()->SetPane(url_pane);
|
|
|
|
boxSpec->flagState = NULL; // adopted by ParseIMAPMailboxState
|
|
MailDB *mailDB=NULL;
|
|
XP_Bool dbWasCreated=FALSE;
|
|
|
|
MsgERR status = ImapMailDB::Open(m_pathName,
|
|
TRUE, // create if necessary
|
|
&mailDB,
|
|
m_master,
|
|
&dbWasCreated);
|
|
|
|
if (status == eSUCCESS)
|
|
{
|
|
GetParseMailboxState()->SetDB(mailDB);
|
|
GetParseMailboxState()->SetIncrementalUpdate(TRUE);
|
|
GetParseMailboxState()->SetMaster(m_master);
|
|
GetParseMailboxState()->SetContext(url_pane->GetContext());
|
|
GetParseMailboxState()->SetFolder(this);
|
|
|
|
GetParseMailboxState()->BeginParsingFolder(0);
|
|
|
|
// the imap libnet module will start downloading message headers imap.h
|
|
IMAP_DownLoadMessagesForMailboxSelect(boxSpec->connection, theKeys, total /*keysToFetch.GetSize() */);
|
|
}
|
|
else
|
|
{
|
|
IMAP_DoNotDownLoadAnyMessageHeadersForMailboxSelect(boxSpec->connection);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// returns TRUE if it's an inbox, and the user has selected it, and they've set the pref to cleanup inbox on exit.
|
|
XP_Bool MSG_IMAPFolderInfoMail::RequiresCleanup()
|
|
{
|
|
// if this is the trash, check if this host has auto empty trash turned on
|
|
if (m_flags & MSG_FOLDER_FLAG_TRASH)
|
|
{
|
|
MSG_IMAPHost *host = GetIMAPHost();
|
|
if (host && host->GetEmptyTrashOnExit())
|
|
{
|
|
if (GetTotalMessages() + GetNumPendingTotalMessages() > host->GetEmptyTrashThreshhold())
|
|
m_requiresCleanup = TRUE;
|
|
}
|
|
}
|
|
return m_requiresCleanup;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::ClearRequiresCleanup()
|
|
{
|
|
m_requiresCleanup = FALSE;
|
|
}
|
|
|
|
|
|
extern int32 msg_ParseFolderLine (char *line, uint32 line_size, void *closure);
|
|
extern MessageDB *GetDBFromState(struct msg_FolderParseState *state);
|
|
|
|
int MSG_IMAPFolderInfoMail::HandleBlockForDataBaseCreate(MSG_Pane *pane,
|
|
const char *str,
|
|
int32 len,
|
|
int32 msgUID,
|
|
uint32 msgSize)
|
|
{
|
|
// RFC822 messages do not contain the BSD 'From - <date>' message
|
|
// header that our mailbox parser expects. Create one for each msg.
|
|
ParseIMAPMailboxState *parser = (ParseIMAPMailboxState *) GetParseMailboxState();
|
|
if (NULL == parser)
|
|
{
|
|
#ifndef XP_UNIX
|
|
XP_ASSERT(FALSE);
|
|
#endif
|
|
#if 0
|
|
SetParseMailboxState(new ParseIMAPMailboxState(m_master, m_host, this,
|
|
NULL,
|
|
boxSpec->flagState));
|
|
GetParseMailboxState()->SetPane(pane);
|
|
|
|
boxSpec->flagState = NULL; // adopted by ParseIMAPMailboxState
|
|
MailDB *mailDB=NULL;
|
|
XP_Bool dbWasCreated=FALSE;
|
|
|
|
MsgERR status = ImapMailDB::Open(m_pathName,
|
|
TRUE, // create if necessary
|
|
&mailDB,
|
|
m_master,
|
|
&dbWasCreated);
|
|
|
|
if (status == eSUCCESS)
|
|
{
|
|
GetParseMailboxState()->SetDB(mailDB);
|
|
GetParseMailboxState()->SetIncrementalUpdate(TRUE);
|
|
GetParseMailboxState()->SetMaster(m_master);
|
|
GetParseMailboxState()->SetContext(pane->GetContext());
|
|
GetParseMailboxState()->SetFolder(this);
|
|
}
|
|
#endif
|
|
// Catch a problem relating to a stream created based on the value of
|
|
// m_DownLoadState.
|
|
// This is an extremely rare edge case that depends on the timing of when you
|
|
// click on message in a thread pane while the folder is being loaded.
|
|
// If you try to load the message after the folder starts loading and before
|
|
// the folder load starts downloading headers, and the message bytes arrive
|
|
// after the header bytes are finished, you will get into this state.
|
|
// The effect of this fix is to not crash and cancel the current url.
|
|
// I have only witnessed this happening when using the X-client.
|
|
return -1;
|
|
}
|
|
|
|
MessageDB *msgDB = parser->GetDB();
|
|
XP_ASSERT(msgDB);
|
|
|
|
int returnLength = 0;
|
|
|
|
if (msgDB->m_dbFolderInfo->m_LastMessageUID != msgUID)
|
|
{
|
|
// before we change the tracked UID, let the parser know the
|
|
// UID
|
|
parser->SetPublishUID(msgDB->m_dbFolderInfo->m_LastMessageUID);
|
|
|
|
msgDB->m_dbFolderInfo->m_LastMessageUID = msgUID;
|
|
char *envelopeString = msg_GetDummyEnvelope(); // not allocated, do not free
|
|
|
|
|
|
returnLength += HandleBlockForDataBaseCreate(pane, envelopeString, XP_STRLEN(envelopeString), msgUID, msgSize);
|
|
|
|
// let the parser know the message size for the next msg header publish
|
|
parser->SetPublishByteLength(msgSize);
|
|
}
|
|
|
|
// we can get blocks that contain more than one line,
|
|
// but they never contain partial lines
|
|
char *currentEOL = XP_STRSTR(str, LINEBREAK);
|
|
const char *currentLine = str;
|
|
while (currentLine < (str + len))
|
|
{
|
|
if (currentEOL)
|
|
{
|
|
parser->ParseFolderLine(currentLine, (currentEOL + LINEBREAK_LEN) - currentLine);
|
|
currentLine = currentEOL + LINEBREAK_LEN;
|
|
currentEOL = XP_STRSTR(currentLine, LINEBREAK);
|
|
}
|
|
else
|
|
{
|
|
parser->ParseFolderLine(currentLine, strlen(currentLine));
|
|
currentLine = str + len + 1;
|
|
}
|
|
}
|
|
|
|
return returnLength + len;
|
|
}
|
|
|
|
|
|
void MSG_IMAPFolderInfoMail::FinishStreamForDataBaseCreate()
|
|
{
|
|
}
|
|
|
|
|
|
void MSG_IMAPFolderInfoMail::ParseUidString(char *uidString, IDArray &keys)
|
|
{
|
|
// This is in the form <id>,<id>, or <id1>:<id2>
|
|
char curChar = *uidString;
|
|
XP_Bool isRange = FALSE;
|
|
int32 curToken;
|
|
int32 saveStartToken=0;
|
|
|
|
for (char *curCharPtr = uidString; curChar && *curCharPtr;)
|
|
{
|
|
char *currentKeyToken = curCharPtr;
|
|
curChar = *curCharPtr;
|
|
while (curChar != ':' && curChar != ',' && curChar != '\0')
|
|
curChar = *curCharPtr++;
|
|
*(curCharPtr - 1) = '\0';
|
|
curToken = atol(currentKeyToken);
|
|
if (isRange)
|
|
{
|
|
while (saveStartToken < curToken)
|
|
keys.Add(saveStartToken++);
|
|
}
|
|
keys.Add(curToken);
|
|
isRange = (curChar == ':');
|
|
if (isRange)
|
|
saveStartToken = curToken + 1;
|
|
}
|
|
}
|
|
|
|
// create a string of uids, suitable for passing to imap url create functions
|
|
char *MSG_IMAPFolderInfoMail::AllocateImapUidString(const IDArray &keys)
|
|
{
|
|
int blocksAllocated = 1;
|
|
char *returnIdString = (char *) XP_ALLOC(256);
|
|
if (returnIdString)
|
|
{
|
|
IDArray copyOfKeys;
|
|
|
|
copyOfKeys.InsertAt(0, &keys);
|
|
|
|
// sort keys if they're not already sorted so ranges will work
|
|
copyOfKeys.QuickSort(MSG_Pane::CompareViewIndices);
|
|
char *currentidString = returnIdString;
|
|
*returnIdString = 0;
|
|
|
|
int32 startSequence = (copyOfKeys.GetSize() > 0) ? copyOfKeys[0] : -1;
|
|
int32 curSequenceEnd = startSequence;
|
|
uint32 total = copyOfKeys.GetSize();
|
|
|
|
for (int keyIndex=0; returnIdString && (keyIndex < total); keyIndex++)
|
|
{
|
|
int32 curKey = copyOfKeys[keyIndex];
|
|
int32 nextKey = (keyIndex + 1 < total) ? copyOfKeys[keyIndex + 1] : -1;
|
|
XP_Bool lastKey = (nextKey == -1);
|
|
|
|
if (lastKey)
|
|
curSequenceEnd = curKey;
|
|
if (nextKey == curSequenceEnd + 1 && !lastKey)
|
|
{
|
|
curSequenceEnd = nextKey;
|
|
continue;
|
|
}
|
|
else if (curSequenceEnd > startSequence)
|
|
{
|
|
sprintf(currentidString, "%ld:%ld,", startSequence, curSequenceEnd);
|
|
startSequence = nextKey;
|
|
curSequenceEnd = startSequence;
|
|
}
|
|
else
|
|
{
|
|
startSequence = nextKey;
|
|
curSequenceEnd = startSequence;
|
|
sprintf(currentidString, "%ld,", copyOfKeys[keyIndex]);
|
|
}
|
|
currentidString += XP_STRLEN(currentidString);
|
|
if ((currentidString + 20) > (returnIdString + (blocksAllocated * 256)))
|
|
{
|
|
returnIdString = (char *) XP_REALLOC(returnIdString, ++blocksAllocated*256);
|
|
if (returnIdString)
|
|
currentidString = returnIdString + XP_STRLEN(returnIdString);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (returnIdString && *returnIdString)
|
|
*(returnIdString + XP_STRLEN(returnIdString) - 1) = 0; // eat the comma
|
|
|
|
return returnIdString;
|
|
}
|
|
|
|
// create a fixed size array of uids. Used for downloading bodies
|
|
uint32 *MSG_IMAPFolderInfoMail::AllocateImapUidArray(const IDArray &keys)
|
|
{
|
|
uint32 *returnArray = (uint32 *) XP_ALLOC(keys.GetSize() * sizeof(uint32));
|
|
uint32 total = keys.GetSize();
|
|
|
|
if (returnArray)
|
|
for(int keyIndex=0; keyIndex < total; keyIndex++)
|
|
returnArray[keyIndex] = keys[keyIndex];
|
|
return returnArray;
|
|
}
|
|
|
|
// when we get here, we're done filtering. Let the fe know its ok to load a message
|
|
void FilteringCompleteExitFunction (URL_Struct *URL_s, int status, MWContext *window_id)
|
|
{
|
|
if (URL_s->msg_pane && URL_s->msg_pane->GetMaster())
|
|
{
|
|
// find the imap inbox
|
|
MSG_IMAPFolderInfoMail *urlFolder = window_id->currentIMAPfolder;
|
|
MSG_IMAPFolderInfoMail *imapInbox;
|
|
if (urlFolder)
|
|
imapInbox = URL_s->msg_pane->GetMaster()->FindImapMailFolder(urlFolder->GetHostName(), "INBOX", NULL, FALSE);
|
|
else
|
|
imapInbox = URL_s->msg_pane->GetMaster()->FindImapMailFolder("INBOX");
|
|
if (imapInbox)
|
|
imapInbox->NotifyFolderLoaded(URL_s->msg_pane);
|
|
|
|
// make sure the inbox notfies the FE that its loaded
|
|
URL_s->msg_pane->SetActiveImapFiltering(FALSE);
|
|
ImapFolderSelectCompleteExitFunction(URL_s, status, window_id);
|
|
}
|
|
}
|
|
|
|
|
|
void MSG_IMAPFolderInfoMail::NotifyFetchAnyNeededBodies(MSG_Pane *urlPane, TNavigatorImapConnection *imapConnection, MailDB *folderDb)
|
|
{
|
|
uint32 *bodyFetchIds = NULL;
|
|
uint32 numberOfBodies= 0;
|
|
if (!urlPane)
|
|
{
|
|
XP_ASSERT(FALSE); // shouldn't happen, but it does - need to understand why.
|
|
return;
|
|
}
|
|
if ((GetFolderPrefFlags() & MSG_FOLDER_PREF_OFFLINE) && urlPane->GetGoOnlineState() && !urlPane->GetGoOnlineState()->ProcessingStaleFolderUpdate())
|
|
{
|
|
m_DownLoadState = kDownLoadMessageForOfflineDB;
|
|
IDArray bodyKeys;
|
|
|
|
folderDb->GetIdsWithNoBodies(bodyKeys);
|
|
|
|
if (bodyKeys.GetSize())
|
|
{
|
|
urlPane->GetContext()->imapURLPane = urlPane;
|
|
urlPane->GetContext()->mailMaster = m_master;
|
|
urlPane->GetContext()->currentIMAPfolder = this;
|
|
|
|
bodyFetchIds = AllocateImapUidArray(bodyKeys);
|
|
if (bodyFetchIds)
|
|
numberOfBodies = bodyKeys.GetSize();
|
|
}
|
|
}
|
|
|
|
IMAP_DownLoadMessageBodieForMailboxSelect(imapConnection, bodyFetchIds, numberOfBodies);
|
|
|
|
|
|
urlPane->EndingUpdate(MSG_NotifyNone, 0, 0);
|
|
|
|
NotifyFolderLoaded(urlPane); // release loading lock on this folder
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::AllFolderHeadersAreDownloaded(MSG_Pane *urlPane, TNavigatorImapConnection *imapConnection)
|
|
{
|
|
ParseIMAPMailboxState *currentParser = (ParseIMAPMailboxState *) GetParseMailboxState();
|
|
if (currentParser)
|
|
{
|
|
{ // a block here to limit the scope of this db.
|
|
MessageDB *folderDB = NULL;
|
|
DBFolderInfo *dbInfo = NULL;
|
|
|
|
// since there is a parser this should get the db from the cache
|
|
MsgERR err = GetDBFolderInfoAndDB(&dbInfo, &folderDB);
|
|
if (err == eSUCCESS)
|
|
{
|
|
currentParser->SetPublishUID(dbInfo->m_LastMessageUID);
|
|
currentParser->DoneParsingFolder();
|
|
NotifyFetchAnyNeededBodies(urlPane, imapConnection, folderDB->GetMailDB());
|
|
folderDB->Close();
|
|
}
|
|
}
|
|
|
|
|
|
// if this is the inbox, apply the filters...
|
|
if (GetFlags() & MSG_FOLDER_FLAG_INBOX)
|
|
{
|
|
MSG_FolderIterator folderIterator(m_master->GetFolderTree());
|
|
|
|
MSG_FolderInfo *curFolder;
|
|
XPPtrArray foldersToFireFilters;
|
|
|
|
curFolder = folderIterator.First();
|
|
MSG_UrlQueue *urlQueue = currentParser->GetFilterUrlQueue();
|
|
if (urlQueue)
|
|
urlQueue->SetSpecialIndexOfNextUrl(urlQueue->GetCursor() + 1);
|
|
|
|
// build up list of folders that need filter move actions fired.
|
|
while(curFolder)
|
|
{
|
|
MSG_FolderInfoMail *mailFolder = curFolder->GetMailFolderInfo();
|
|
if (mailFolder)
|
|
{
|
|
int numHits = mailFolder->GetImapIdsToMoveFromInbox()->GetSize();
|
|
if (numHits > 0)
|
|
{
|
|
IDArray *copyOfIds = new IDArray;
|
|
copyOfIds->InsertAt(0, mailFolder->GetImapIdsToMoveFromInbox());
|
|
// maybe we want to do this when we're sure it worked?
|
|
mailFolder->ClearImapIdsToMoveFromInbox();
|
|
StartAsyncCopyMessagesInto(mailFolder,
|
|
urlPane, NULL /*sourceDB*/, // don't need sourceDB for IMAP...
|
|
copyOfIds, numHits,
|
|
urlPane->GetContext(),
|
|
urlQueue,
|
|
TRUE,
|
|
MSG_MESSAGEKEYNONE);
|
|
foldersToFireFilters.Add(curFolder);
|
|
}
|
|
}
|
|
curFolder = folderIterator.Next();
|
|
}
|
|
|
|
// now that we know there are more filter urls to run, do not let this select url's exit function
|
|
// do the MSG_PaneNotifyFolderLoaded. We'll make it happen in FilteringCompleteExitFunction
|
|
urlPane->SetActiveImapFiltering(TRUE);
|
|
if (urlQueue)
|
|
{
|
|
urlQueue->SetSpecialIndexOfNextUrl(MSG_UrlQueue::kNoSpecialIndex);
|
|
urlQueue->AddUrl(kImapFilteringCompleteURL, FilteringCompleteExitFunction, urlPane);
|
|
// tell queue to put new things before the filtering complete exit function
|
|
urlQueue->SetSpecialIndexOfNextUrl(urlQueue->GetSize() - 1);
|
|
}
|
|
}
|
|
|
|
delete currentParser;
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace("clearing parse mailbox state in AllFolderHeadersAreDownloaded\n");
|
|
#endif
|
|
SetParseMailboxState(NULL);
|
|
}
|
|
else
|
|
XP_Trace("current parser is null\n");
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::AbortStreamForDataBaseCreate (int /*status*/)
|
|
{
|
|
FinishStreamForDataBaseCreate(); // I think its the same deal! -km
|
|
}
|
|
|
|
|
|
FolderType MSG_IMAPFolderInfoMail::GetType()
|
|
{
|
|
return FOLDER_IMAPMAIL;
|
|
}
|
|
|
|
MSG_IMAPFolderInfoMail *MSG_IMAPFolderInfoMail::FindImapMailOnline(const char* onlineServerName)
|
|
{
|
|
MSG_IMAPFolderInfoMail *thePrize = NULL;
|
|
|
|
|
|
if (GetOnlineName() && !XP_STRCMP(GetOnlineName(), onlineServerName))
|
|
{
|
|
/*
|
|
// The online folder names match. Then see if the namespace has been stripped off.
|
|
// If it has, we want to make sure that we still want that to be the case.
|
|
// Otherwise, we say the folders don't match.
|
|
const char *personalDir = m_host->GetPersonalNamespacePrefix();
|
|
XP_ASSERT(personalDir);
|
|
|
|
if (personalDir && (XP_STRLEN(personalDir) >= 1) && // personal namespace prefix exists
|
|
IMAP_GetNumberOfNamespacesForHost(GetHostName()) == 1) // only one namespace
|
|
{
|
|
// we should have stripped off the namespace
|
|
if (GetFolderPrefFlags() & MSG_FOLDER_PREF_NAMESPACE_STRIPPED)
|
|
thePrize = this;
|
|
}
|
|
else
|
|
{
|
|
// we should not have stripped off the namespace
|
|
if (!(GetFolderPrefFlags() & MSG_FOLDER_PREF_NAMESPACE_STRIPPED))
|
|
thePrize = this;
|
|
}
|
|
*/
|
|
thePrize = this;
|
|
}
|
|
|
|
if (!thePrize)
|
|
{
|
|
int numberOfChildren = GetNumSubFolders();
|
|
for (int childIndex = 0; (childIndex < numberOfChildren) && !thePrize; childIndex++)
|
|
{
|
|
MSG_IMAPFolderInfoMail *currentChild = (MSG_IMAPFolderInfoMail *) GetSubFolder(childIndex);
|
|
thePrize = currentChild->FindImapMailOnline(onlineServerName);
|
|
}
|
|
}
|
|
|
|
return thePrize;
|
|
}
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::ParentRenamed (const char * parentOnlineName)
|
|
{
|
|
MsgERR status = 0;
|
|
const char *currentOnlineName = GetOnlineName();
|
|
char *newName = (char *) XP_ALLOC(XP_STRLEN(currentOnlineName) + XP_STRLEN(parentOnlineName) + 2); // worse case
|
|
if (newName)
|
|
{
|
|
// append my leaf to parent path
|
|
XP_STRCPY(newName, parentOnlineName);
|
|
if (*parentOnlineName != '\0') // true if promoting to root and no root parent dir
|
|
XP_STRCAT(newName, "/");
|
|
|
|
const char *currentLeaf = XP_STRRCHR(currentOnlineName, '/');
|
|
if (currentLeaf)
|
|
currentLeaf++;
|
|
else
|
|
currentLeaf = currentOnlineName;
|
|
XP_STRCAT(newName, currentLeaf);
|
|
|
|
SetOnlineName(newName); // reset this node's name
|
|
|
|
|
|
FREEIF(newName);
|
|
}
|
|
else
|
|
status = MK_OUT_OF_MEMORY;
|
|
|
|
return status;
|
|
}
|
|
|
|
MsgERR MSG_IMAPFolderInfoMail::Rename (const char * newName)
|
|
{
|
|
// call the ancestor hotline
|
|
MsgERR status = MSG_FolderInfoMail::Rename (newName);
|
|
if (status == 0)
|
|
{
|
|
// fixup the online name
|
|
// replace the leaf of the m_OnlineFolderName with newName
|
|
char *newOnlineName = (char *) XP_ALLOC(XP_STRLEN(newName) + XP_STRLEN(GetOnlineName()) + 1);
|
|
if (newOnlineName)
|
|
{
|
|
XP_STRCPY(newOnlineName, GetOnlineName());
|
|
char *leafTarget = XP_STRRCHR(newOnlineName, '/');
|
|
if (leafTarget)
|
|
leafTarget++;
|
|
else
|
|
leafTarget = newOnlineName;
|
|
XP_STRCPY(leafTarget, newName);
|
|
|
|
SetOnlineName(newOnlineName); // reset this node's name
|
|
|
|
FREEIF(newOnlineName);
|
|
}
|
|
else
|
|
status = MK_OUT_OF_MEMORY;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
char *MSG_IMAPFolderInfoMail::BuildUrl (MessageDB *db, MessageKey key)
|
|
{
|
|
|
|
char *url = NULL;
|
|
if (db)
|
|
{
|
|
// This is a message fetch URL
|
|
char idList[10];
|
|
sprintf(idList, "%ld", (long)key);
|
|
url = CreateImapMessageFetchUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
idList, // messageKey
|
|
TRUE); // This is a UID
|
|
|
|
}
|
|
else
|
|
{
|
|
// This is a URL to the folder itself
|
|
if (!(m_flags & MSG_FOLDER_FLAG_IMAP_PUBLIC))
|
|
{
|
|
// this folder is owned by someone (maybe us). Adjust the URL accordingly.
|
|
url = PR_smprintf("%s//%s@%s/%s", "IMAP:", GetFolderOwnerUserName(), GetHostName(), GetOwnersOnlineFolderName());
|
|
}
|
|
else // public, or default
|
|
url = PR_smprintf("%s//%s/%s", "IMAP:", GetHostName(), GetOnlineName());
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
char *MSG_IMAPFolderInfoMail::SetupHeaderFetchUrl(MSG_Pane *pane, MessageDB *db, MessageKey startSeq, MessageKey endSeq)
|
|
{
|
|
char *url = NULL;
|
|
char idList[20];
|
|
ParseIMAPMailboxState *parseState;
|
|
|
|
sprintf(idList, "%ld:%ld", (long)startSeq, (long)endSeq);
|
|
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace("fetching idList %s\n", idList);
|
|
#endif
|
|
m_DownLoadState = kDownLoadingAllMessageHeaders;
|
|
|
|
// url_pane->GetContext()->imapURLPane = url_pane;
|
|
// url_pane->GetContext()->mailMaster = m_master;
|
|
// url_pane->GetContext()->currentIMAPfolder = this;
|
|
|
|
pane->GetContext()->mailMaster = m_master; // rb testing
|
|
|
|
// MSG_UrlQueue *urlQueue = MSG_UrlQueue::FindQueue(IMAP_GetCurrentConnectionUrl(boxSpec->connection), url_pane->GetContext());
|
|
parseState = new ParseIMAPMailboxState(m_master, m_host, this, /*urlQueue*/ NULL, NULL /*boxSpec->flagState*/) ;
|
|
SetParseMailboxState (parseState);
|
|
// boxSpec->flagState = NULL; // adopted by ParseIMAPMailboxState
|
|
GetParseMailboxState()->SetDB(db->GetMailDB());
|
|
parseState->SetNextSequenceNum(startSeq);
|
|
GetParseMailboxState()->SetIncrementalUpdate(TRUE);
|
|
GetParseMailboxState()->SetMaster(m_master);
|
|
// GetParseMailboxState()->SetContext(url_pane->GetContext());
|
|
GetParseMailboxState()->SetFolder(this);
|
|
|
|
GetParseMailboxState()->SetPane(pane);
|
|
|
|
// hook up the view to the parse mailbox state.
|
|
// convince context that we're downloading messages to get parsed.
|
|
// This is the wrong place for this but...
|
|
pane->GetContext()->currentIMAPfolder = this;
|
|
if (pane->GetMsgView())
|
|
{
|
|
GetParseMailboxState()->SetView(pane->GetMsgView());
|
|
for (MSG_ViewIndex viewIndex = startSeq - 1; viewIndex < endSeq - 1; viewIndex++)
|
|
pane->GetMsgView()->SetKeyByIndex(viewIndex, kIdPending);
|
|
}
|
|
|
|
GetParseMailboxState()->BeginParsingFolder(0);
|
|
|
|
return CreateImapMessageHeaderUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
idList, // sequence range
|
|
FALSE); // This is a message sequence number
|
|
|
|
}
|
|
|
|
|
|
int MSG_IMAPFolderInfoMail::SaveFlaggedMessagesToDB(MSG_Pane *pane, MessageDB *db, IDArray &keysToSave)
|
|
{
|
|
// this kinda sucks, because we're doing it in the foreground, but we don't have the infrastructure
|
|
// to do it in the background for imap.
|
|
db->ListMatchingKeys(MessageDB::MatchFlaggedNotOffline, keysToSave);
|
|
return SaveMessagesToDB(pane, db, keysToSave);
|
|
}
|
|
|
|
|
|
int MSG_IMAPFolderInfoMail::SaveMessagesToDB(MSG_Pane *pane, MessageDB *db, IDArray &keysToSave)
|
|
{
|
|
char *downloadURL = SetupMessageBodyFetchUrl(pane, db, keysToSave);
|
|
|
|
URL_Struct *url_s = NET_CreateURLStruct(downloadURL, NET_DONT_RELOAD);
|
|
if (!url_s)
|
|
return eUNKNOWN;
|
|
|
|
url_s->allow_content_change = FALSE;
|
|
|
|
MSG_UrlQueue::AddUrlToPane (url_s, NULL, pane);
|
|
return 0;
|
|
}
|
|
|
|
char *MSG_IMAPFolderInfoMail::SetupMessageBodyFetchUrl(MSG_Pane *pane, MessageDB* /*db*/, IDArray &keysToDownload)
|
|
{
|
|
char *url = NULL;
|
|
char *idList = AllocateImapUidString(keysToDownload);
|
|
|
|
#ifdef DEBUG_bienvenu
|
|
XP_Trace("fetching idList %s\n", idList);
|
|
#endif
|
|
m_DownLoadState = kDownLoadMessageForOfflineDB;
|
|
|
|
pane->GetContext()->currentIMAPfolder = this;
|
|
|
|
return CreateImapMessageFetchUrl(GetHostName(),
|
|
GetOnlineName(),
|
|
GetOnlineHierarchySeparator(),
|
|
idList, // sequence range
|
|
TRUE); // This is a message sequence number
|
|
|
|
}
|
|
|
|
// libmsg always uses the canonical '/' as the directory separator
|
|
// libnet will translate those chars to the the server
|
|
// dependent directory separator string.
|
|
char *MSG_IMAPFolderInfoMail::CreateOnlineVersionOfLocalPathString
|
|
(MSG_Prefs& /*prefs*/,
|
|
const char *localPath) const
|
|
{
|
|
const char *leafName = localPath +
|
|
strlen(m_host->GetLocalDirectory()) + 1;
|
|
|
|
const char *rootPrefix = m_host->GetRootNamespacePrefix();
|
|
XP_ASSERT(rootPrefix);
|
|
char *onlineReturnName = XP_STRDUP(rootPrefix);
|
|
if (onlineReturnName)
|
|
{
|
|
int lengthOfCanonicalInboxName = XP_STRLEN(INBOX_FOLDER_NAME);
|
|
if (!XP_STRNCMP(leafName, INBOX_FOLDER_NAME, lengthOfCanonicalInboxName))
|
|
{
|
|
// do not prepend server sub dir for INBOX
|
|
*onlineReturnName = '\0';
|
|
// convert canonical INBOX_FOLDER_NAME to "INBOX"
|
|
StrAllocCat(onlineReturnName, "INBOX");
|
|
// the inbox can have children on some servers
|
|
if (XP_STRLEN(leafName) > lengthOfCanonicalInboxName)
|
|
StrAllocCat(onlineReturnName, leafName + lengthOfCanonicalInboxName);
|
|
}
|
|
else
|
|
StrAllocCat(onlineReturnName, leafName);
|
|
}
|
|
|
|
|
|
// squish any ".sbd/" substrings to "/"
|
|
char *sbdSubString = XP_STRSTR(onlineReturnName, ".sbd/");
|
|
while(sbdSubString)
|
|
{
|
|
XP_STRCPY(sbdSubString, // destination
|
|
sbdSubString + 4); // source
|
|
|
|
sbdSubString = XP_STRSTR(sbdSubString + 4, ".sbd/");
|
|
}
|
|
|
|
// covert escaped spaces to spaces
|
|
char *escapedSpaceString = XP_STRSTR(onlineReturnName, "%20");
|
|
while(escapedSpaceString)
|
|
{
|
|
*escapedSpaceString++ = ' ';
|
|
XP_STRCPY(escapedSpaceString, // destination
|
|
escapedSpaceString + 2); // source
|
|
|
|
escapedSpaceString = XP_STRSTR(escapedSpaceString, "%20");
|
|
}
|
|
|
|
|
|
|
|
|
|
return onlineReturnName;
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::URLFinished(URL_Struct *URL_s, int /*status*/, MWContext* /*window_id*/)
|
|
{
|
|
DBFolderInfo *folderInfo = NULL;
|
|
MessageDB *messagedb = NULL;
|
|
|
|
MSG_IMAPFolderInfoMail *folder = NULL;
|
|
if (URL_s->msg_pane)
|
|
{
|
|
MSG_FolderInfo *mailFolder = URL_s->msg_pane->GetFolder();
|
|
if (mailFolder && mailFolder->GetType() == FOLDER_IMAPMAIL)
|
|
folder = (MSG_IMAPFolderInfoMail *) mailFolder;
|
|
}
|
|
if (!folder)
|
|
return;
|
|
// I thought this should be true...but it's not.
|
|
// XP_ASSERT(folder == window_id->currentIMAPfolder);
|
|
|
|
// if there are any offline operations that have not been played back, then try to just do it
|
|
// this isn't right because we don't want to play real offline events here.
|
|
MsgERR opendbErr = folder->GetDBFolderInfoAndDB(&folderInfo, &messagedb);
|
|
if ((opendbErr == 0) && !NET_IsOffline())
|
|
{
|
|
IDArray offlineOperationKeys;
|
|
MailDB *mailDB = messagedb->GetMailDB();
|
|
if (mailDB)
|
|
mailDB->ListAllOfflineOpIds(offlineOperationKeys);
|
|
messagedb->Close();
|
|
messagedb = NULL;
|
|
|
|
folder->SetHasOfflineEvents(FALSE); // turn off this flag, so we won't confuse MSG_IMAPURLFinished
|
|
|
|
if (offlineOperationKeys.GetSize() > 0)
|
|
{
|
|
OfflineImapGoOnlineState *goOnline = new OfflineImapGoOnlineState(URL_s->msg_pane, (MSG_IMAPFolderInfoMail *) folder);
|
|
if (goOnline)
|
|
{
|
|
goOnline->SetPseudoOffline(TRUE);
|
|
|
|
// will force a MSG_PaneNotifyFolderLoaded if offline playbasck fails
|
|
// if (loadingFolder)
|
|
// paneOfCommand->SetLoadingImapFolder(this);
|
|
|
|
goOnline->ProcessNextOperation();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((opendbErr == 0) && messagedb)
|
|
messagedb->Close();
|
|
}
|
|
|
|
const char *MSG_IMAPFolderInfoMail::GetFolderOwnerUserName()
|
|
{
|
|
|
|
if ((m_flags & MSG_FOLDER_FLAG_IMAP_PERSONAL) ||
|
|
!(m_flags & (MSG_FOLDER_FLAG_IMAP_PUBLIC | MSG_FOLDER_FLAG_IMAP_OTHER_USER)))
|
|
{
|
|
// this is one of our personal mail folders
|
|
// return our username on this host
|
|
return GetIMAPHost()->GetUserName();
|
|
}
|
|
|
|
// the only other type of owner is if it's in the other users' namespace
|
|
if (!(m_flags & MSG_FOLDER_FLAG_IMAP_OTHER_USER))
|
|
return NULL;
|
|
|
|
if (m_ownerUserName)
|
|
return m_ownerUserName;
|
|
|
|
char *onlineName = XP_STRDUP(GetOnlineName());
|
|
if (onlineName)
|
|
{
|
|
const char *otherUserNS = m_host->GetNamespacePrefixForFolder(onlineName);
|
|
if (otherUserNS)
|
|
{
|
|
char *otherUserName = onlineName + XP_STRLEN(otherUserNS);
|
|
if (otherUserName)
|
|
{
|
|
char *nextDelimiter = XP_STRCHR(otherUserName, GetOnlineHierarchySeparator());
|
|
if (nextDelimiter)
|
|
*nextDelimiter = 0;
|
|
|
|
m_ownerUserName = XP_STRDUP(otherUserName); // freed in the destructor
|
|
}
|
|
}
|
|
XP_FREE(onlineName);
|
|
}
|
|
|
|
return m_ownerUserName;
|
|
}
|
|
|
|
// returns the online folder name, with the other users' namespace and his username
|
|
// stripped out
|
|
const char *MSG_IMAPFolderInfoMail::GetOwnersOnlineFolderName()
|
|
{
|
|
const char *folder = GetOnlineName();
|
|
if (m_flags & MSG_FOLDER_FLAG_IMAP_OTHER_USER)
|
|
{
|
|
const char *user = GetFolderOwnerUserName();
|
|
if (folder && user)
|
|
{
|
|
char *where = XP_STRSTR(folder, user);
|
|
if (where)
|
|
{
|
|
char *relativeFolder = where + XP_STRLEN(user) + 1;
|
|
if (!relativeFolder) // root of this user's personal namespace
|
|
return (PR_smprintf("")); // leak of one character?
|
|
else
|
|
return relativeFolder;
|
|
}
|
|
}
|
|
return folder;
|
|
}
|
|
else if (!(m_flags & MSG_FOLDER_FLAG_IMAP_PUBLIC))
|
|
{
|
|
// we own this folder
|
|
const char *personalNS = m_host->GetNamespacePrefixForFolder(folder);
|
|
if (personalNS)
|
|
{
|
|
const char *where = folder + XP_STRLEN(personalNS);
|
|
if (where)
|
|
return where;
|
|
else // root of our personal namespace
|
|
return (PR_smprintf("")); // leak of one character?
|
|
}
|
|
else
|
|
return folder;
|
|
}
|
|
else
|
|
return folder;
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::GetAdminUrl(MWContext *context, MSG_AdminURLType type)
|
|
{
|
|
return m_host->RunAdminURL(context, this, type);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::HaveAdminUrl(MSG_AdminURLType type)
|
|
{
|
|
return m_host->HaveAdminURL(type);
|
|
}
|
|
|
|
MSG_IMAPFolderACL *MSG_IMAPFolderInfoMail::GetFolderACL()
|
|
{
|
|
if (!m_folderACL)
|
|
m_folderACL = new MSG_IMAPFolderACL(this);
|
|
return m_folderACL;
|
|
}
|
|
|
|
char *MSG_IMAPFolderInfoMail::CreateACLRightsStringForFolder()
|
|
{
|
|
GetFolderACL(); // lazy create
|
|
if (m_folderACL)
|
|
{
|
|
return m_folderACL->CreateACLRightsString();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Allocates and returns a string naming this folder's type
|
|
// i.e. "Personal Folder"
|
|
// The caller should free the returned value.
|
|
char *MSG_IMAPFolderInfoMail::GetTypeNameForFolder()
|
|
{
|
|
if (m_flags & MSG_FOLDER_FLAG_IMAP_PUBLIC)
|
|
{
|
|
return PR_smprintf(XP_GetString(XP_MSG_IMAP_PUBLIC_FOLDER_TYPE_NAME));
|
|
}
|
|
if (m_flags & MSG_FOLDER_FLAG_IMAP_OTHER_USER)
|
|
{
|
|
return PR_smprintf(XP_GetString(XP_MSG_IMAP_OTHER_USERS_FOLDER_TYPE_NAME));
|
|
}
|
|
// personal folder
|
|
if (GetFolderACL()->GetIsFolderShared())
|
|
{
|
|
return PR_smprintf(XP_GetString(XP_MSG_IMAP_PERSONAL_SHARED_FOLDER_TYPE_NAME));
|
|
}
|
|
else
|
|
{
|
|
return PR_smprintf(XP_GetString(XP_MSG_IMAP_PERSONAL_FOLDER_TYPE_NAME));
|
|
}
|
|
}
|
|
|
|
// Allocates and returns a string describing this folder's type
|
|
// i.e. "This is a personal folder. It is shared."
|
|
// The caller should free the returned value.
|
|
char *MSG_IMAPFolderInfoMail::GetTypeDescriptionForFolder()
|
|
{
|
|
if (m_flags & MSG_FOLDER_FLAG_IMAP_PUBLIC)
|
|
{
|
|
return PR_smprintf(XP_GetString(XP_MSG_IMAP_PUBLIC_FOLDER_TYPE_DESCRIPTION));
|
|
}
|
|
if (m_flags & MSG_FOLDER_FLAG_IMAP_OTHER_USER)
|
|
{
|
|
const char *owner = GetFolderOwnerUserName();
|
|
if (owner)
|
|
{
|
|
return PR_smprintf(XP_GetString(XP_MSG_IMAP_OTHER_USERS_FOLDER_TYPE_DESCRIPTION), owner);
|
|
}
|
|
else
|
|
{
|
|
// Another user's folder, for which we couldn't find an owner name
|
|
XP_ASSERT(FALSE);
|
|
return PR_smprintf(XP_GetString(XP_MSG_IMAP_OTHER_USERS_FOLDER_TYPE_DESCRIPTION),
|
|
XP_GetString(XP_MSG_IMAP_UNKNOWN_USER));
|
|
}
|
|
}
|
|
// personal folder
|
|
if (GetFolderACL()->GetIsFolderShared())
|
|
{
|
|
return PR_smprintf(XP_GetString(XP_MSG_IMAP_PERSONAL_SHARED_FOLDER_TYPE_DESCRIPTION));
|
|
}
|
|
else
|
|
{
|
|
return PR_smprintf(XP_GetString(XP_MSG_IMAP_PERSONAL_FOLDER_TYPE_DESCRIPTION));
|
|
}
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::AddFolderRightsForUser(const char *userName, const char *rights)
|
|
{
|
|
SetFolderNeedsACLListed(FALSE);
|
|
GetFolderACL()->SetFolderRightsForUser(userName, rights);
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::ClearAllFolderACLRights()
|
|
{
|
|
SetFolderNeedsACLListed(FALSE);
|
|
delete m_folderACL;
|
|
m_folderACL = new MSG_IMAPFolderACL(this);
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::RefreshFolderACLRightsView()
|
|
{
|
|
if (GetFolderACL()->GetIsFolderShared())
|
|
{
|
|
SetFlag(MSG_FOLDER_FLAG_PERSONAL_SHARED);
|
|
}
|
|
else
|
|
{
|
|
ClearFlag(MSG_FOLDER_FLAG_PERSONAL_SHARED);
|
|
}
|
|
|
|
// refresh the view in any folder panes
|
|
if (m_master)
|
|
{
|
|
m_master->BroadcastFolderChanged(this);
|
|
}
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::SetAdminUrl(const char *adminUrl)
|
|
{
|
|
FREEIF(m_adminUrl);
|
|
m_adminUrl = XP_STRDUP(adminUrl);
|
|
}
|
|
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::GetCanIOpenThisFolder()
|
|
{
|
|
if (GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT)
|
|
return FALSE;
|
|
|
|
return (GetFolderACL()->GetCanIReadFolder());
|
|
}
|
|
|
|
// returns TRUE if another folder can be dropped into this folder
|
|
XP_Bool MSG_IMAPFolderInfoMail::GetCanDropFolderIntoThisFolder()
|
|
{
|
|
// no inferiors allowed
|
|
if (GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOINFERIORS)
|
|
return FALSE;
|
|
|
|
return GetFolderACL()->GetCanICreateSubfolder();
|
|
}
|
|
|
|
// returns TRUE if messages can be dropped into this folder
|
|
XP_Bool MSG_IMAPFolderInfoMail::GetCanDropMessagesIntoFolder()
|
|
{
|
|
if (GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT)
|
|
return FALSE;
|
|
|
|
return (GetFolderACL()->GetCanIInsertInFolder());
|
|
}
|
|
|
|
// returns TRUE if messages can be dragged from of this folder (but not deleted)
|
|
XP_Bool MSG_IMAPFolderInfoMail::GetCanDragMessagesFromThisFolder()
|
|
{
|
|
if (GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT)
|
|
return FALSE;
|
|
|
|
return GetFolderACL()->GetCanIReadFolder();
|
|
}
|
|
|
|
// returns TRUE if we can delete messages in this folder
|
|
XP_Bool MSG_IMAPFolderInfoMail::GetCanDeleteMessagesInFolder()
|
|
{
|
|
return GetFolderACL()->GetCanIDeleteInFolder();
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::AllowsPosting ()
|
|
{
|
|
return (m_flags & MSG_FOLDER_FLAG_IMAP_PUBLIC);
|
|
}
|
|
|
|
void MSG_IMAPFolderInfoMail::RememberPassword(const char * password)
|
|
{
|
|
MailDB *mailDb = NULL;
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(m_pathName, TRUE, &mailDb, m_master, &wasCreated);
|
|
if (mailDb)
|
|
{
|
|
mailDb->SetCachedPassword(password);
|
|
mailDb->Close();
|
|
}
|
|
}
|
|
|
|
char *MSG_IMAPFolderInfoMail::GetRememberedPassword()
|
|
{
|
|
char *retPassword = NULL;
|
|
if (m_flags & MSG_FOLDER_FLAG_INBOX)
|
|
{
|
|
MailDB *mailDb = NULL;
|
|
XP_Bool wasCreated=FALSE;
|
|
ImapMailDB::Open(m_pathName, TRUE, &mailDb, m_master, &wasCreated);
|
|
if (mailDb)
|
|
{
|
|
XPStringObj cachedPassword;
|
|
mailDb->GetCachedPassword(cachedPassword);
|
|
retPassword = XP_STRDUP(cachedPassword);
|
|
mailDb->Close();
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MSG_FolderInfo *inbox = NULL;
|
|
if (GetIMAPContainer()->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, &inbox, 1) == 1
|
|
&& inbox != NULL)
|
|
{
|
|
retPassword = inbox->GetRememberedPassword();
|
|
}
|
|
}
|
|
return retPassword;
|
|
}
|
|
|
|
const char *MSG_IMAPFolderInfoMail::GetUserName()
|
|
{
|
|
return GetIMAPHost()->GetUserName();
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::UserNeedsToAuthenticateForFolder(XP_Bool displayOnly)
|
|
{
|
|
if (m_master->IsCachePasswordProtected() && !m_master->IsUserAuthenticated() && !m_master->AreLocalFoldersAuthenticated())
|
|
{
|
|
char *savedPassword = GetRememberedPassword();
|
|
XP_Bool havePassword = savedPassword && XP_STRLEN(savedPassword);
|
|
FREEIF(savedPassword);
|
|
|
|
if (havePassword)
|
|
{
|
|
// If we're offline or we're not authenticated, get authenticated
|
|
if (NET_IsOffline() /* && displayOnly */ )
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
unsigned int MessageDownLoadStreamWriteReady(NET_StreamClass *stream)
|
|
{
|
|
return MAX_WRITE_READY;
|
|
}
|
|
|
|
int IMAPHandleBlockForDataBaseCreate(NET_StreamClass *stream, const char *str, int32 msgUID)
|
|
{
|
|
void *data_object=stream->data_object;
|
|
MWContext *urlPaneContext = ((IMAPdownLoadStreamData *) data_object)->urlContext;
|
|
MSG_IMAPFolderInfoMail *imapFolder = urlPaneContext->currentIMAPfolder;
|
|
return imapFolder->HandleBlockForDataBaseCreate(urlPaneContext->imapURLPane, str,
|
|
strlen(str),
|
|
msgUID,
|
|
((IMAPdownLoadStreamData *) data_object)->msgSize);
|
|
}
|
|
|
|
void IMAPFinishStreamForDataBaseCreate(NET_StreamClass *stream)
|
|
{
|
|
IMAPdownLoadStreamData *downloadData = (IMAPdownLoadStreamData *) stream->data_object;
|
|
MWContext *urlPaneContext = downloadData->urlContext;
|
|
MSG_IMAPFolderInfoMail *imapFolder = urlPaneContext->currentIMAPfolder;
|
|
imapFolder->FinishStreamForDataBaseCreate();
|
|
delete downloadData;
|
|
}
|
|
|
|
extern "C" void NotifyHeaderFetchCompleted(MWContext *currentContext, TNavigatorImapConnection *imapConnection)
|
|
{
|
|
if (currentContext->currentIMAPfolder)
|
|
currentContext->currentIMAPfolder->AllFolderHeadersAreDownloaded(currentContext->imapURLPane, imapConnection);
|
|
else
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
|
|
|
|
void IMAPAbortStreamForDataBaseCreate (NET_StreamClass *stream, int status)
|
|
{
|
|
IMAPdownLoadStreamData *downloadData = (IMAPdownLoadStreamData *) stream->data_object;
|
|
MWContext *urlPaneContext = downloadData->urlContext;
|
|
MSG_IMAPFolderInfoMail *imapFolder = urlPaneContext->currentIMAPfolder;
|
|
imapFolder->AbortStreamForDataBaseCreate(status);
|
|
delete downloadData;
|
|
}
|
|
|
|
#ifndef XP_OS2
|
|
static
|
|
#else
|
|
extern "OPTLINK"
|
|
#endif
|
|
int32 IMAPHandleDownloadLine(char* line, uint32 length, void* closure)
|
|
{
|
|
ParseOutgoingMessage *outgoingParser = (ParseOutgoingMessage *) closure;
|
|
|
|
return outgoingParser->ParseFolderLine(line, length);
|
|
}
|
|
|
|
int IMAPHandleBlockForMessageCopyDownload(NET_StreamClass *stream, const char *str, int32 msgUID)
|
|
{
|
|
int returnValue = 0;
|
|
IMAPdownLoadStreamData *downloadStreamData = (IMAPdownLoadStreamData *) stream->data_object;
|
|
MWContext *urlPaneContext = downloadStreamData->urlContext;
|
|
MSG_FolderInfoMail *localFolder = (MSG_FolderInfoMail *) urlPaneContext->msgCopyInfo->dstFolder;
|
|
|
|
if (!urlPaneContext->msgCopyInfo->moveState.writestate.fid)
|
|
{
|
|
int createError = localFolder->CreateMessageWriteStream(&urlPaneContext->msgCopyInfo->moveState,0,0); // download msg size and flags don't matter
|
|
if (createError != 0)
|
|
{
|
|
FE_Alert(urlPaneContext, XP_GetString(createError));
|
|
return createError;
|
|
}
|
|
|
|
|
|
if (urlPaneContext->msgCopyInfo->moveState.writestate.fid)
|
|
{
|
|
downloadStreamData->outgoingParser.Clear();
|
|
downloadStreamData->outgoingParser.SetOutFile(urlPaneContext->msgCopyInfo->moveState.writestate.fid);
|
|
downloadStreamData->outgoingParser.SetMailDB(urlPaneContext->msgCopyInfo->moveState.destDB);
|
|
|
|
char *envelopeString = msg_GetDummyEnvelope(); // not allocated, do not free
|
|
uint32 storePosition = localFolder->SeekToEndOfMessageStore(&urlPaneContext->msgCopyInfo->moveState);
|
|
downloadStreamData->outgoingParser.Init(storePosition);
|
|
urlPaneContext->msgCopyInfo->offlineFolderPositionOfMostRecentMessage = storePosition;
|
|
downloadStreamData->outgoingParser.StartNewEnvelope(envelopeString, strlen(envelopeString));
|
|
|
|
// Conjure up an X-Mozilla-Status line from the source IMAP database so the
|
|
// message will have the right flags in the local mail folder. We don't
|
|
// apparently have a DB for filtering IMAP to local, but we should still
|
|
// put the X-Mozilla-Status header into the local folder.
|
|
uint32 xMozillaFlags = 0;
|
|
MessageDB *db = urlPaneContext->msgCopyInfo->srcDB;
|
|
if (db)
|
|
{
|
|
DBMessageHdr *dbHdr = db->GetDBHdrForKey (msgUID);
|
|
xMozillaFlags = dbHdr->GetMozillaStatusFlags() & ~MSG_FLAG_RUNTIME_ONLY;
|
|
delete dbHdr;
|
|
}
|
|
|
|
char *mozStatus = PR_smprintf("%04.4x", (int) xMozillaFlags);
|
|
if (mozStatus)
|
|
{
|
|
downloadStreamData->m_mozillaStatus = mozStatus;
|
|
downloadStreamData->outgoingParser.m_mozstatus.value = mozStatus;
|
|
downloadStreamData->outgoingParser.m_mozstatus.length = XP_STRLEN(mozStatus);
|
|
|
|
// If the message on the server has an X-Mozilla-Status, it must be bogus,
|
|
// so make sure our local opinion of X-Mozilla-Status is the one we keep
|
|
downloadStreamData->outgoingParser.m_IgnoreXMozillaStatus = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (urlPaneContext->msgCopyInfo->moveState.writestate.fid)
|
|
{
|
|
localFolder->SeekToEndOfMessageStore(&urlPaneContext->msgCopyInfo->moveState);
|
|
returnValue = msg_LineBuffer (str, strlen(str),
|
|
&downloadStreamData->obuffer, (uint32 *)&downloadStreamData->obufferSize,
|
|
(uint32*)&downloadStreamData->obufferIndex,
|
|
FALSE, IMAPHandleDownloadLine,
|
|
&downloadStreamData->outgoingParser);
|
|
// note that we need to update the db with the position of the start of a message,
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
void IMAPFinishStreamForMessageCopyDownload(NET_StreamClass *stream)
|
|
{
|
|
IMAPdownLoadStreamData *downloadStreamData = (IMAPdownLoadStreamData *) stream->data_object;
|
|
MWContext *urlPaneContext = downloadStreamData->urlContext;
|
|
MSG_FolderInfoMail *localFolder = (MSG_FolderInfoMail *) urlPaneContext->msgCopyInfo->dstFolder;
|
|
|
|
// if there are any bytes left in the buffer, send them. Happens when last line ends in <CR>
|
|
if (downloadStreamData->obufferIndex)
|
|
{
|
|
IMAPHandleDownloadLine(downloadStreamData->obuffer,
|
|
downloadStreamData->obufferIndex,
|
|
&downloadStreamData->outgoingParser);
|
|
}
|
|
|
|
|
|
// save the message header so relevant panes are updated
|
|
MailDB::SetFolderInfoValid(localFolder->GetPathname(), 0, 0);
|
|
|
|
msg_move_state *state = &urlPaneContext->msgCopyInfo->moveState;
|
|
downloadStreamData->outgoingParser.FinishHeader();
|
|
// MailMessageHdr *mailHdr = (MailMessageHdr*) urlPaneContext->msgCopyInfo->srcDB->GetDBHdrForKey(urlPaneContext->msgCopyInfo->srcArray->GetAt(state->msgIndex));
|
|
|
|
uint32 endOfMessageStore = localFolder->SeekToEndOfMessageStore(state);
|
|
localFolder->CloseMessageWriteStream(&urlPaneContext->msgCopyInfo->moveState);
|
|
|
|
// tweak the msg size because line termination and envelope lines are different
|
|
state->finalDownLoadMessageSize = endOfMessageStore - urlPaneContext->msgCopyInfo->offlineFolderPositionOfMostRecentMessage;
|
|
|
|
if (downloadStreamData->outgoingParser.m_newMsgHdr)
|
|
{
|
|
if (!(downloadStreamData->outgoingParser.m_newMsgHdr->GetFlags() & kIsRead))
|
|
downloadStreamData->outgoingParser.m_newMsgHdr->OrFlags(kNew);
|
|
downloadStreamData->outgoingParser.m_newMsgHdr->SetMessageKey (urlPaneContext->msgCopyInfo->offlineFolderPositionOfMostRecentMessage);
|
|
if (state->destDB)
|
|
state->destDB->AddHdrToDB (downloadStreamData->outgoingParser.m_newMsgHdr, NULL, TRUE); // tell db to notify
|
|
// we must tweak the message size to account
|
|
// for the envelope and line termination differences
|
|
if (state->finalDownLoadMessageSize)
|
|
{
|
|
downloadStreamData->outgoingParser.m_newMsgHdr->SetByteLength(state->finalDownLoadMessageSize);
|
|
downloadStreamData->outgoingParser.m_newMsgHdr->SetMessageSize(state->finalDownLoadMessageSize);
|
|
}
|
|
downloadStreamData->outgoingParser.m_newMsgHdr = NULL;
|
|
}
|
|
if (downloadStreamData->obuffer)
|
|
XP_FREE (downloadStreamData->obuffer);
|
|
|
|
// for IMAP downloads, this is the only place where msgIndex is used
|
|
// advance it so we do not copy the same header n times during a multimessage
|
|
// download.
|
|
state->msgIndex++;
|
|
delete downloadStreamData;
|
|
}
|
|
|
|
void IMAPAbortStreamForMessageCopyDownload(NET_StreamClass *stream, int /*status*/)
|
|
{
|
|
IMAPdownLoadStreamData *downloadStreamData = (IMAPdownLoadStreamData *) stream->data_object;
|
|
MWContext *urlPaneContext = downloadStreamData->urlContext;
|
|
MSG_FolderInfoMail *localFolder = (MSG_FolderInfoMail *) urlPaneContext->msgCopyInfo->moveState.destfolder;
|
|
localFolder->CloseMessageWriteStream(&urlPaneContext->msgCopyInfo->moveState);
|
|
delete downloadStreamData;
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" NET_StreamClass *CreateIMAPDownloadMessageStream(ImapActiveEntry *ce, uint32 size,
|
|
const char *content_type, XP_Bool content_modified)
|
|
{
|
|
MSG_Pane *urlPane = IMAP_GetActiveEntryPane(ce);
|
|
MWContext *currentContext = (urlPane) ? urlPane->GetContext() : (MWContext *)NULL;
|
|
|
|
NET_StreamClass *returnStream = (NET_StreamClass *)NULL;
|
|
if (currentContext && currentContext->currentIMAPfolder)
|
|
returnStream = currentContext->currentIMAPfolder->CreateDownloadMessageStream(ce, size, content_type, content_modified);
|
|
|
|
return returnStream;
|
|
}
|
|
|
|
extern "C" void UpdateIMAPMailboxStatus(mailbox_spec *adoptedBoxSpec, MWContext *currentContext)
|
|
{
|
|
MSG_IMAPFolderInfoMail *mailFolder = adoptedBoxSpec->allocatedPathName ?
|
|
currentContext->mailMaster->FindImapMailFolder(adoptedBoxSpec->hostName, adoptedBoxSpec->allocatedPathName, NULL, FALSE) :
|
|
(MSG_IMAPFolderInfoMail *)NULL;
|
|
if (mailFolder && currentContext->imapURLPane) // imap biff gets here for XFE somehow with null imapurlpane
|
|
{
|
|
mailFolder->UpdateFolderStatus(adoptedBoxSpec, currentContext->imapURLPane);
|
|
}
|
|
}
|
|
|
|
extern "C" void UpdateIMAPMailboxInfo(mailbox_spec *adoptedBoxSpec, MWContext *currentContext)
|
|
{
|
|
if (!currentContext->mailMaster) // bogus !
|
|
return; // currentContext->mailMaster = (MSG_Master*) adoptedBoxSpec->connection->fCurrentEntry->window_id;
|
|
MSG_IMAPFolderInfoMail *mailFolder = adoptedBoxSpec->allocatedPathName ?
|
|
currentContext->mailMaster->FindImapMailFolder(adoptedBoxSpec->hostName, adoptedBoxSpec->allocatedPathName, NULL, FALSE) :
|
|
(MSG_IMAPFolderInfoMail *)NULL;
|
|
if (mailFolder && currentContext->imapURLPane) // imap biff gets here for XFE somehow with null imapurlpane
|
|
{
|
|
mailFolder->UpdateNewlySelectedFolder(adoptedBoxSpec, currentContext->imapURLPane);
|
|
}
|
|
else
|
|
{
|
|
// we should never get here, some how we can't find folder info that started this update
|
|
|
|
// if we have a mailbox name, alert the user. Otherwise this was probably an interrupt context
|
|
if (adoptedBoxSpec->allocatedPathName)
|
|
{
|
|
const char *errorFormat = XP_GetString(MK_MSG_CANT_FIND_SNM);
|
|
char *errorMessage = (char *) XP_ALLOC(XP_STRLEN(errorFormat) + XP_STRLEN(adoptedBoxSpec->allocatedPathName) + 1);
|
|
if (errorMessage)
|
|
{
|
|
sprintf(errorMessage, errorFormat, adoptedBoxSpec->allocatedPathName);
|
|
FE_Alert(currentContext, errorMessage);
|
|
XP_FREE(errorMessage);
|
|
}
|
|
}
|
|
IMAP_DoNotDownLoadAnyMessageHeadersForMailboxSelect(adoptedBoxSpec->connection);
|
|
}
|
|
}
|
|
|
|
extern "C" int32 GetUIDValidityForSpecifiedImapFolder(const char *hostName, const char *canonicalimapName, MWContext *currentContext)
|
|
{
|
|
int32 rvalue = kUidUnknown;
|
|
|
|
XP_ASSERT(canonicalimapName);
|
|
|
|
if (currentContext->mailMaster && canonicalimapName)
|
|
{
|
|
MSG_IMAPFolderInfoMail *mailFolder = currentContext->mailMaster->FindImapMailFolder(hostName, canonicalimapName, NULL, FALSE);
|
|
|
|
if (mailFolder)
|
|
{
|
|
DBFolderInfo *folderInfo = NULL;
|
|
MessageDB *msgDb = NULL;
|
|
mailFolder->GetDBFolderInfoAndDB(&folderInfo,&msgDb);
|
|
if (folderInfo)
|
|
rvalue = folderInfo->GetImapUidValidity();
|
|
if (msgDb)
|
|
msgDb->Close();
|
|
}
|
|
}
|
|
|
|
return rvalue;
|
|
}
|
|
|
|
|
|
static
|
|
XP_Bool FolderNamesMatch(const char *name1, const char *name2)
|
|
{
|
|
XP_Bool theyMatch = XP_STRCMP(name1, name2) == 0;
|
|
if (!theyMatch)
|
|
{
|
|
char *nonConstName1 = (char *) name1;
|
|
char *slash1 = 0;
|
|
char *nonConstName2 = (char *) name2;
|
|
char *slash2 = 0;
|
|
|
|
// if one or both end in slash, ignore it
|
|
if ( *nonConstName1 && (*(nonConstName1 + XP_STRLEN(nonConstName1) - 1 ) == '/') )
|
|
{
|
|
slash1 = nonConstName1 + XP_STRLEN(nonConstName1) - 1;
|
|
*slash1 = '\0';
|
|
}
|
|
if ( *nonConstName2 && (*(nonConstName2 + XP_STRLEN(nonConstName2) - 1 ) == '/') )
|
|
{
|
|
slash2 = nonConstName2 + XP_STRLEN(nonConstName2) - 1;
|
|
*slash2 = '\0';
|
|
}
|
|
|
|
theyMatch = XP_STRCMP(nonConstName1, nonConstName2) == 0;
|
|
|
|
// replace the slashes, we promised 'const'
|
|
if (slash1) *slash1 = '/';
|
|
if (slash2) *slash2 = '/';
|
|
}
|
|
|
|
return theyMatch;
|
|
}
|
|
|
|
// forward
|
|
static
|
|
void DeleteNonVerifiedImapFolders(MSG_FolderInfo* parentFolder,
|
|
MSG_FolderPane *folderPane,
|
|
MSG_Pane **urlPane);
|
|
|
|
|
|
// obsolete ?
|
|
static enum EMailboxDiscoverStatus GiveUserAChanceToChangeServerDir(MSG_Pane *imapURLPane, const char* /*hostName*/)
|
|
{
|
|
EMailboxDiscoverStatus discoveryStatus = eContinue;
|
|
|
|
// I don't think we want this stopgap anymore, now that we're using subscription.
|
|
if (FALSE /*!FE_Confirm(imapURLPane->GetContext(), XP_GetString(MK_MSG_LOTS_NEW_IMAP_FOLDERS))*/)
|
|
{
|
|
discoveryStatus = eCancelled;
|
|
char onlineDir[256];
|
|
onlineDir[0] = '\0';
|
|
int stringSize = 256;
|
|
PREF_GetCharPref("mail.imap.server_sub_directory",
|
|
onlineDir, &stringSize);
|
|
|
|
char *newImapDir = FE_Prompt(imapURLPane->GetContext(), XP_GetString(MK_MSG_IMAP_DIR_PROMPT), onlineDir);
|
|
|
|
if (newImapDir && XP_STRCMP(newImapDir, onlineDir ))
|
|
{
|
|
discoveryStatus = eNewServerDirectory;
|
|
PREF_SetCharPref("mail.imap.server_sub_directory", newImapDir);
|
|
//IMAP_SetNamespacesFromPrefs(hostName, newImapDir, "", "");
|
|
|
|
// set all existing imap folders to be non verified and delete them from the folder pane
|
|
MSG_FolderIterator folderIterator(imapURLPane->GetMaster()->GetImapMailFolderTree());
|
|
MSG_FolderInfo *currentFolder = folderIterator.First();
|
|
while (currentFolder)
|
|
{
|
|
if ((currentFolder->GetType() == FOLDER_IMAPMAIL) && !(currentFolder->GetFlags() & MSG_FOLDER_FLAG_INBOX))
|
|
((MSG_IMAPFolderInfoMail *)currentFolder)->SetIsOnlineVerified(FALSE);
|
|
currentFolder = folderIterator.Next();
|
|
}
|
|
|
|
DeleteNonVerifiedImapFolders(imapURLPane->GetMaster()->GetImapMailFolderTree(),
|
|
(MSG_FolderPane *) imapURLPane->GetMaster()->FindFirstPaneOfType(MSG_FOLDERPANE), &imapURLPane);
|
|
XP_FREE(newImapDir);
|
|
}
|
|
|
|
}
|
|
|
|
return discoveryStatus;
|
|
}
|
|
|
|
|
|
static enum EMailboxDiscoverStatus DiscoverIMAPMailboxForSubscribe(mailbox_spec *adoptedBoxSpec, MWContext *currentContext)
|
|
{
|
|
char *folderName = adoptedBoxSpec->allocatedPathName;
|
|
if (currentContext->imapURLPane)
|
|
{
|
|
MSG_Pane *pane = currentContext->imapURLPane;
|
|
XP_ASSERT(pane->GetPaneType() == MSG_SUBSCRIBEPANE);
|
|
if (pane->GetPaneType() == MSG_SUBSCRIBEPANE)
|
|
{
|
|
uint32 folder_flags = 0;
|
|
if (adoptedBoxSpec->box_flags & kPersonalMailbox)
|
|
folder_flags |= MSG_GROUPNAME_FLAG_IMAP_PERSONAL;
|
|
if (adoptedBoxSpec->box_flags & kPublicMailbox)
|
|
folder_flags |= MSG_GROUPNAME_FLAG_IMAP_PUBLIC;
|
|
if (adoptedBoxSpec->box_flags & kOtherUsersMailbox)
|
|
folder_flags |= MSG_GROUPNAME_FLAG_IMAP_OTHER_USER;
|
|
if (adoptedBoxSpec->box_flags & kNoselect)
|
|
folder_flags |= MSG_GROUPNAME_FLAG_IMAP_NOSELECT;
|
|
((MSG_SubscribePane *)pane)->AddIMAPGroupToList(folderName, adoptedBoxSpec->hierarchySeparator, adoptedBoxSpec->discoveredFromLsub, folder_flags);
|
|
}
|
|
}
|
|
IMAP_FreeBoxSpec(adoptedBoxSpec);
|
|
return eContinue;
|
|
}
|
|
|
|
/* static */ MSG_IMAPFolderInfoMail *MSG_IMAPFolderInfoMail::CreateNewIMAPFolder(MSG_IMAPFolderInfoMail *parentFolder, MWContext *currentContext,
|
|
const char *mailboxName, const char *onlineLeafName, uint32 mailboxFlags, MSG_IMAPHost *host)
|
|
{
|
|
MailDB *newDb = NULL;
|
|
XP_Bool wasCreated=FALSE;
|
|
MSG_IMAPFolderInfoMail *newFolder = NULL;
|
|
|
|
// convert spaces back to %20 quote char
|
|
char *offlinePathLeafTokenString = XP_PlatformPartialPathToXPPartialPath(onlineLeafName);
|
|
|
|
// calculate a leaf name that works for this client's platform
|
|
char *diskLeafName;
|
|
if (parentFolder)
|
|
diskLeafName = MSG_FolderInfoMail::CreatePlatformLeafNameForDisk(offlinePathLeafTokenString,currentContext->mailMaster,parentFolder);
|
|
else
|
|
diskLeafName = MSG_FolderInfoMail::CreatePlatformLeafNameForDisk(offlinePathLeafTokenString,currentContext->mailMaster, host->GetLocalDirectory());
|
|
int newPathStringLength = parentFolder ? XP_STRLEN(parentFolder->GetPathname()) :
|
|
XP_STRLEN(host->GetLocalDirectory());
|
|
newPathStringLength += XP_STRLEN(diskLeafName) + 6; // 6 enough for .sbd/ and \0
|
|
char *newboxPath = (char*) XP_ALLOC (newPathStringLength);
|
|
|
|
if (newboxPath)
|
|
{
|
|
XP_STRCPY(newboxPath, parentFolder ? parentFolder->GetPathname() : host->GetLocalDirectory());
|
|
|
|
if (parentFolder)
|
|
{
|
|
// make sure the parent .sbd dir exists
|
|
int dirStatus = 0;
|
|
XP_StatStruct stat;
|
|
XP_BZERO (&stat, sizeof(stat));
|
|
if (0 == XP_Stat (newboxPath, &stat, xpMailSubdirectory))
|
|
{
|
|
if (!S_ISDIR(stat.st_mode))
|
|
dirStatus = MK_COULD_NOT_CREATE_DIRECTORY; // a file .sbd already exists
|
|
}
|
|
else
|
|
dirStatus = XP_MakeDirectory (newboxPath, xpMailSubdirectory);
|
|
if ((0 != dirStatus) && currentContext)
|
|
{
|
|
char *str = PR_smprintf(XP_GetString(MK_COULD_NOT_CREATE_DIRECTORY),newboxPath);
|
|
if (str)
|
|
{
|
|
FE_Alert(currentContext, str);
|
|
XP_FREE(str);
|
|
}
|
|
}
|
|
XP_STRCAT(newboxPath,".sbd");
|
|
}
|
|
|
|
XP_STRCAT(newboxPath, "/");
|
|
XP_STRCAT(newboxPath, diskLeafName);
|
|
|
|
char *progressString = PR_smprintf(XP_GetString(MK_MSG_IMAP_DISCOVERING_MAILBOX),onlineLeafName);
|
|
if (progressString)
|
|
{
|
|
NET_Progress(currentContext, progressString);
|
|
XP_FREE(progressString);
|
|
}
|
|
|
|
// create the new database, set the online name
|
|
ImapMailDB::Open(newboxPath, TRUE, &newDb, currentContext->mailMaster, &wasCreated);
|
|
if (newDb)
|
|
{
|
|
newDb->m_dbFolderInfo->SetMailboxName(mailboxName);
|
|
newFolder = new MSG_IMAPFolderInfoMail(newboxPath,
|
|
currentContext->mailMaster,
|
|
parentFolder ? parentFolder->GetDepth() + 1 : 2,
|
|
MSG_FOLDER_FLAG_MAIL, host);
|
|
|
|
if (host->GetDefaultOfflineDownload())
|
|
newFolder->SetFolderPrefFlags(newFolder->GetFolderPrefFlags() | MSG_FOLDER_PREF_OFFLINE);
|
|
|
|
newDb->SetFolderInfoValid(newboxPath,0,0);
|
|
|
|
|
|
// set this flag now because it is used in the compare function when adding the folder
|
|
// to its parent sub folder array
|
|
if (mailboxFlags & kImapTrash)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_TRASH);
|
|
|
|
// set the mailbox type flags, if they exist
|
|
if (mailboxFlags & kPersonalMailbox)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_IMAP_PERSONAL);
|
|
else if (mailboxFlags & kPublicMailbox)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_IMAP_PUBLIC);
|
|
else if (mailboxFlags & kOtherUsersMailbox)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_IMAP_OTHER_USER);
|
|
|
|
XP_Bool addToFolderPane = FALSE;
|
|
if (parentFolder)
|
|
{
|
|
if (!parentFolder->HasSubFolders())
|
|
{
|
|
// if this is a new parent, its initial state should be collapsed
|
|
parentFolder->SetFlagInAllFolderPanes(MSG_FOLDER_FLAG_ELIDED);
|
|
}
|
|
|
|
// MSG_FolderInfoMail::Adopt does too much. We already know the
|
|
// newFolder pathname and parent sbd exist
|
|
parentFolder->LightWeightAdopt(newFolder);
|
|
parentFolder->SummaryChanged();
|
|
addToFolderPane = !(parentFolder->GetFlags() & MSG_FOLDER_FLAG_ELIDED);
|
|
}
|
|
else // no parent
|
|
{
|
|
MSG_FolderInfoContainer *imapRootFolder = host->GetHostFolderInfo();
|
|
if (imapRootFolder)
|
|
{
|
|
XPPtrArray *rootSubfolderArray = (XPPtrArray *) imapRootFolder->GetSubFolders();
|
|
rootSubfolderArray->Add(newFolder);
|
|
imapRootFolder->SummaryChanged();
|
|
addToFolderPane = TRUE;
|
|
}
|
|
}
|
|
|
|
// add the new folder to the folder pane trees so the fe can see it.
|
|
|
|
if (addToFolderPane)
|
|
{
|
|
currentContext->mailMaster->BroadcastFolderAdded (newFolder);
|
|
newFolder->SummaryChanged();
|
|
}
|
|
}
|
|
}
|
|
if (newDb)
|
|
newDb->Close();
|
|
return newFolder;
|
|
}
|
|
|
|
// If currentContext is NULL, we are simply discovering this folder and adding it to the folder list automatically, as if
|
|
// the user clicked on a link to a folder that's not in his list yet.
|
|
// Master should never be NULL.
|
|
static enum EMailboxDiscoverStatus DiscoverIMAPMailboxForFolderPane(mailbox_spec *adoptedBoxSpec, MSG_Master *master,
|
|
MWContext *currentContext, XP_Bool broadcastDiscovery)
|
|
{
|
|
MailDB *newDb = NULL;
|
|
XP_Bool wasCreated=FALSE;
|
|
XP_Bool shouldStripNamespace = TRUE;
|
|
XP_Bool folderIsNew = FALSE;
|
|
XP_Bool folderIsNamespace = FALSE;
|
|
|
|
EMailboxDiscoverStatus discoveryStatus = eContinue;
|
|
|
|
MSG_IMAPHost *host = master->GetIMAPHost(adoptedBoxSpec->hostName);
|
|
if (!host)
|
|
{
|
|
// We discovered an IMAP folder on a host we don't know about. This should never happen.
|
|
XP_ASSERT(FALSE);
|
|
return discoveryStatus;
|
|
}
|
|
|
|
// Check to see if we already know about it. Maybe we created it when we found its child.
|
|
MSG_IMAPFolderInfoMail *newFolder = master->FindImapMailFolder(adoptedBoxSpec->hostName, adoptedBoxSpec->allocatedPathName, NULL, FALSE);
|
|
|
|
if (!newFolder) // if we haven't seen this guy before, then create it
|
|
{
|
|
folderIsNew = TRUE;
|
|
MSG_IMAPFolderInfoMail *parentFolder = NULL;
|
|
|
|
const char *namespacePrefix = host->GetNamespacePrefixForFolder(adoptedBoxSpec->allocatedPathName);
|
|
if (namespacePrefix &&
|
|
!XP_STRCMP(namespacePrefix, adoptedBoxSpec->allocatedPathName))
|
|
{
|
|
folderIsNamespace = TRUE; // the folder we are discovering is actually a namespace prefix.
|
|
}
|
|
|
|
if (!folderIsNamespace) // if the folder is really a namespace, don't break up the hierarchy
|
|
{
|
|
// make sure we know about the parent
|
|
char *parentFolderName = XP_STRDUP(adoptedBoxSpec->allocatedPathName);
|
|
|
|
if (parentFolderName) // trying to find/discover the parent
|
|
{
|
|
char *lastSlash = XP_STRRCHR(parentFolderName, '/');
|
|
if (lastSlash) // if there is a hierarchy, there is a parent
|
|
{
|
|
*lastSlash = '\0';
|
|
|
|
if (namespacePrefix &&
|
|
(XP_STRLEN(namespacePrefix) >= 1) &&
|
|
host->GetShouldStripThisNamespacePrefix(namespacePrefix))
|
|
{
|
|
// this namespace is being stripped
|
|
parentFolder = NULL;
|
|
}
|
|
else
|
|
{
|
|
const char *parentNamespace = host->GetNamespacePrefixForFolder(parentFolderName);
|
|
if (parentNamespace &&
|
|
!XP_STRNCMP(parentNamespace, parentFolderName, XP_STRLEN(parentFolderName)))
|
|
{
|
|
// The parent is a namespace itself. Find the folder for that namespace.
|
|
parentFolder = master->FindImapMailFolder(adoptedBoxSpec->hostName, parentNamespace, NULL, FALSE);
|
|
}
|
|
else
|
|
{
|
|
parentFolder = master->FindImapMailFolder(adoptedBoxSpec->hostName, parentFolderName, NULL, FALSE);
|
|
if (!parentFolder)
|
|
{
|
|
mailbox_spec *parentSpec = (mailbox_spec *) XP_ALLOC( sizeof(mailbox_spec));
|
|
if (parentSpec)
|
|
{
|
|
parentSpec->folderSelected = FALSE;
|
|
parentSpec->hierarchySeparator = adoptedBoxSpec->hierarchySeparator;
|
|
parentSpec->allocatedPathName = XP_STRDUP(parentFolderName);
|
|
parentSpec->hostName = XP_STRDUP(adoptedBoxSpec->hostName);
|
|
parentSpec->flagState = NULL;
|
|
parentSpec->discoveredFromLsub = FALSE;
|
|
parentSpec->box_flags = 0;
|
|
parentSpec->onlineVerified = FALSE;
|
|
|
|
DiscoverIMAPMailbox(parentSpec, // storage adopted
|
|
master,
|
|
currentContext,
|
|
broadcastDiscovery);
|
|
parentFolder = master->FindImapMailFolder(adoptedBoxSpec->hostName, parentFolderName, NULL, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
XP_FREE(parentFolderName);
|
|
}
|
|
}
|
|
|
|
if (folderIsNamespace && host->GetShouldStripThisNamespacePrefix(namespacePrefix))
|
|
{
|
|
// We've discovered a namespace (only) that we're going to strip off.
|
|
// So don't discover a folderinfo for it.
|
|
return eContinue;
|
|
}
|
|
|
|
// now create this folder
|
|
const char *onlineLeafName = NULL;
|
|
if (!folderIsNamespace) // only break out the hierarchy if this folder is not a namespace itself
|
|
onlineLeafName = XP_STRRCHR(adoptedBoxSpec->allocatedPathName, '/');
|
|
|
|
if (!onlineLeafName)
|
|
onlineLeafName = adoptedBoxSpec->allocatedPathName; // this is a root level folder
|
|
else
|
|
onlineLeafName++; // eat the '/'
|
|
|
|
// convert spaces back to %20 quote char
|
|
char *offlinePathLeafTokenString = XP_PlatformPartialPathToXPPartialPath(onlineLeafName);
|
|
|
|
// calculate a leaf name that works for this client's platform
|
|
char *diskLeafName;
|
|
if (parentFolder)
|
|
diskLeafName = MSG_FolderInfoMail::CreatePlatformLeafNameForDisk(offlinePathLeafTokenString,master,parentFolder);
|
|
else
|
|
diskLeafName = MSG_FolderInfoMail::CreatePlatformLeafNameForDisk(offlinePathLeafTokenString,master, host->GetLocalDirectory());
|
|
int newPathStringLength = parentFolder ? XP_STRLEN(parentFolder->GetPathname()) :
|
|
XP_STRLEN(host->GetLocalDirectory());
|
|
newPathStringLength += XP_STRLEN(diskLeafName) + 6; // 6 enough for .sbd/ and \0
|
|
char *newboxPath = (char*) XP_ALLOC (newPathStringLength);
|
|
|
|
if (newboxPath)
|
|
{
|
|
XP_STRCPY(newboxPath, parentFolder ? parentFolder->GetPathname() : host->GetLocalDirectory());
|
|
|
|
if (parentFolder)
|
|
{
|
|
// make sure the parent .sbd dir exists
|
|
int dirStatus = 0;
|
|
XP_StatStruct stat;
|
|
XP_BZERO (&stat, sizeof(stat));
|
|
if (0 == XP_Stat (newboxPath, &stat, xpMailSubdirectory))
|
|
{
|
|
if (!S_ISDIR(stat.st_mode))
|
|
dirStatus = MK_COULD_NOT_CREATE_DIRECTORY; // a file .sbd already exists
|
|
}
|
|
else
|
|
dirStatus = XP_MakeDirectory (newboxPath, xpMailSubdirectory);
|
|
if ((0 != dirStatus) && currentContext)
|
|
{
|
|
char *str = PR_smprintf(XP_GetString(MK_COULD_NOT_CREATE_DIRECTORY),newboxPath);
|
|
if (str)
|
|
{
|
|
FE_Alert(currentContext, str);
|
|
XP_FREE(str);
|
|
}
|
|
}
|
|
XP_STRCAT(newboxPath,".sbd");
|
|
}
|
|
|
|
XP_STRCAT(newboxPath, "/");
|
|
XP_STRCAT(newboxPath, diskLeafName);
|
|
|
|
if (currentContext)
|
|
{
|
|
char *progressString = PR_smprintf(XP_GetString(MK_MSG_IMAP_DISCOVERING_MAILBOX),onlineLeafName);
|
|
if (progressString)
|
|
{
|
|
NET_Progress(currentContext, progressString);
|
|
XP_FREE(progressString);
|
|
}
|
|
}
|
|
|
|
// create the new database, set the online name
|
|
ImapMailDB::Open(newboxPath, TRUE, &newDb, master, &wasCreated);
|
|
if (newDb)
|
|
{
|
|
newDb->m_dbFolderInfo->SetMailboxName(adoptedBoxSpec->allocatedPathName);
|
|
newFolder = new MSG_IMAPFolderInfoMail(newboxPath,
|
|
master,
|
|
parentFolder ? parentFolder->GetDepth() + 1 : 2,
|
|
MSG_FOLDER_FLAG_MAIL, host);
|
|
|
|
if (host->GetDefaultOfflineDownload())
|
|
newFolder->SetFolderPrefFlags(newFolder->GetFolderPrefFlags() | MSG_FOLDER_PREF_OFFLINE);
|
|
|
|
newDb->SetFolderInfoValid(newboxPath,0,0);
|
|
|
|
|
|
// set this flag now because it is used in the compare function when adding the folder
|
|
// to its parent sub folder array
|
|
if (adoptedBoxSpec->box_flags & kImapTrash)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_TRASH);
|
|
|
|
// set the mailbox type flags, if they exist
|
|
if (adoptedBoxSpec->box_flags & kPersonalMailbox)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_IMAP_PERSONAL);
|
|
else if (adoptedBoxSpec->box_flags & kPublicMailbox)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_IMAP_PUBLIC);
|
|
else if (adoptedBoxSpec->box_flags & kOtherUsersMailbox)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_IMAP_OTHER_USER);
|
|
|
|
XP_Bool addToFolderPane = FALSE;
|
|
if (parentFolder)
|
|
{
|
|
if (!parentFolder->HasSubFolders())
|
|
{
|
|
// if this is a new parent, its initial state should be collapsed
|
|
parentFolder->SetFlagInAllFolderPanes(MSG_FOLDER_FLAG_ELIDED);
|
|
}
|
|
|
|
// MSG_FolderInfoMail::Adopt does too much. We already know the
|
|
// newFolder pathname and parent sbd exist
|
|
parentFolder->LightWeightAdopt(newFolder);
|
|
parentFolder->SummaryChanged();
|
|
addToFolderPane = !(parentFolder->GetFlags() & MSG_FOLDER_FLAG_ELIDED);
|
|
}
|
|
else // no parent
|
|
{
|
|
MSG_FolderInfoContainer *imapRootFolder = master->GetImapMailFolderTreeForHost(adoptedBoxSpec->hostName);
|
|
if (imapRootFolder)
|
|
{
|
|
XPPtrArray *rootSubfolderArray = (XPPtrArray *) imapRootFolder->GetSubFolders();
|
|
rootSubfolderArray->Add(newFolder);
|
|
imapRootFolder->SummaryChanged();
|
|
addToFolderPane = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// add the new folder to the folder pane trees so the fe can see it.
|
|
if (addToFolderPane && currentContext)
|
|
{
|
|
if (broadcastDiscovery)
|
|
master->BroadcastFolderAdded (newFolder, currentContext->imapURLPane);
|
|
newFolder->SummaryChanged();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
FREEIF(offlinePathLeafTokenString);
|
|
FREEIF(diskLeafName);
|
|
}
|
|
|
|
if (newFolder && (discoveryStatus == eContinue) )
|
|
{
|
|
MSG_IMAPHost *host = master->GetIMAPHost(adoptedBoxSpec->hostName);
|
|
XP_ASSERT(host);
|
|
XP_Bool usingSubscription = host ? host->GetIsHostUsingSubscription() : FALSE;
|
|
|
|
if (adoptedBoxSpec->box_flags & kImapTrash)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_TRASH);
|
|
|
|
// set the mailbox type flags, if they exist
|
|
if (adoptedBoxSpec->box_flags & kPersonalMailbox)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_IMAP_PERSONAL);
|
|
else if (adoptedBoxSpec->box_flags & kPublicMailbox)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_IMAP_PUBLIC);
|
|
else if (adoptedBoxSpec->box_flags & kOtherUsersMailbox)
|
|
newFolder->SetFlag(MSG_FOLDER_FLAG_IMAP_OTHER_USER);
|
|
|
|
newFolder->SetIsOnlineVerified(adoptedBoxSpec->onlineVerified);
|
|
newFolder->SetOnlineHierarchySeparator(adoptedBoxSpec->hierarchySeparator);
|
|
|
|
int32 prefFlags = newFolder->GetFolderPrefFlags();
|
|
|
|
// for each imap folder flag, record it if its different
|
|
prefFlags = (adoptedBoxSpec->box_flags & kMarked) ? (prefFlags | MSG_FOLDER_PREF_IMAPMARKED) : (prefFlags & ~MSG_FOLDER_PREF_IMAPMARKED);
|
|
prefFlags = (adoptedBoxSpec->box_flags & kUnmarked) ? (prefFlags | MSG_FOLDER_PREF_IMAPUNMARKED) : (prefFlags & ~MSG_FOLDER_PREF_IMAPUNMARKED);
|
|
prefFlags = (adoptedBoxSpec->box_flags & kNoinferiors) ? (prefFlags | MSG_FOLDER_PREF_IMAPNOINFERIORS) : (prefFlags & ~MSG_FOLDER_PREF_IMAPNOINFERIORS);
|
|
prefFlags = (adoptedBoxSpec->box_flags & kNoselect) ? (prefFlags | MSG_FOLDER_PREF_IMAPNOSELECT) : (prefFlags & ~MSG_FOLDER_PREF_IMAPNOSELECT);
|
|
// good time to clear this flag.
|
|
prefFlags &= ~MSG_FOLDER_PREF_CREATED_OFFLINE;
|
|
|
|
// once we've discovered it using LSUB, it sticks
|
|
if (!(prefFlags & MSG_FOLDER_PREF_LSUB_DISCOVERED))
|
|
prefFlags = usingSubscription ? (prefFlags | MSG_FOLDER_PREF_LSUB_DISCOVERED) : (prefFlags & ~MSG_FOLDER_PREF_LSUB_DISCOVERED);
|
|
|
|
// Set a flag on whether or not we've stripped off the namespace
|
|
//if (folderIsNew)
|
|
// prefFlags = shouldStripNamespace ? (prefFlags | MSG_FOLDER_PREF_NAMESPACE_STRIPPED) : (prefFlags & ~MSG_FOLDER_PREF_NAMESPACE_STRIPPED);
|
|
|
|
if (prefFlags != newFolder->GetFolderPrefFlags())
|
|
newFolder->SetFolderPrefFlags(prefFlags);
|
|
}
|
|
|
|
if (newDb)
|
|
newDb->Close();
|
|
|
|
IMAP_FreeBoxSpec(adoptedBoxSpec);
|
|
|
|
if (discoveryStatus == eContinue && folderIsNew)
|
|
discoveryStatus = eContinueNew;
|
|
|
|
return discoveryStatus;
|
|
}
|
|
|
|
|
|
extern "C" enum EMailboxDiscoverStatus DiscoverIMAPMailbox(mailbox_spec *adoptedBoxSpec, MSG_Master *master,
|
|
MWContext *currentContext, XP_Bool broadcastDiscovery)
|
|
{
|
|
// Not sure why this happens (pane is deleted), but it's a guaranteed crash.
|
|
if (currentContext && !MSG_Pane::PaneInMasterList(currentContext->imapURLPane))
|
|
currentContext->imapURLPane = NULL;
|
|
if (currentContext && currentContext->imapURLPane &&
|
|
(currentContext->imapURLPane->GetPaneType() ==
|
|
MSG_SUBSCRIBEPANE))
|
|
return DiscoverIMAPMailboxForSubscribe(adoptedBoxSpec,
|
|
currentContext);
|
|
else if (currentContext && currentContext->imapURLPane &&
|
|
currentContext->imapURLPane->IMAPListInProgress())
|
|
{
|
|
currentContext->imapURLPane->SetIMAPListMailboxExist(TRUE);
|
|
return eContinue;
|
|
}
|
|
else
|
|
return DiscoverIMAPMailboxForFolderPane(adoptedBoxSpec,
|
|
master,
|
|
currentContext,
|
|
broadcastDiscovery);
|
|
}
|
|
|
|
extern "C" void ReportSuccessOfOnlineDelete(MWContext *currentContext, const char *hostName, const char *mailboxName)
|
|
{
|
|
XP_ASSERT(currentContext && currentContext->mailMaster);
|
|
if (currentContext && currentContext->mailMaster)
|
|
{
|
|
MSG_IMAPFolderInfoMail *doomedFolder = currentContext->mailMaster->FindImapMailFolder(hostName, mailboxName, NULL, FALSE);
|
|
XP_ASSERT(doomedFolder);
|
|
if (doomedFolder)
|
|
{
|
|
MSG_FolderInfo *tree = currentContext->mailMaster->GetImapMailFolderTree();
|
|
XP_ASSERT(tree);
|
|
if (tree)
|
|
{
|
|
// I'm leaving the FindParent split out in case we put in parent pointers,
|
|
// which would make this an O(1) operation
|
|
MSG_FolderInfo *parent = tree->FindParentOf (doomedFolder);
|
|
XP_ASSERT(parent);
|
|
if (parent)
|
|
parent->PropagateDelete ((MSG_FolderInfo**) &doomedFolder); // doomedFolder is null on return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern "C" void ReportFailureOfOnlineCreate(MWContext *currentContext, const char* /*serverMessage*/)
|
|
{
|
|
// Tell the FE object that was trying to create the folder that it failed. This is
|
|
// their signal to do something with the New Folder dialog. Not sure if they *should* take
|
|
// it down, but this is enough info for them to clean up.
|
|
//
|
|
// If we need to send this to more panes than just the URLPane, we should create a new
|
|
// broadcast thing in the master
|
|
|
|
XP_ASSERT(currentContext->imapURLPane);
|
|
if (currentContext->imapURLPane)
|
|
FE_PaneChanged (currentContext->imapURLPane, FALSE, MSG_PaneNotifyNewFolderFailed, 0);
|
|
}
|
|
|
|
extern "C" void ReportSuccessOfOnlineRename(MWContext *currentContext, folder_rename_struct *names)
|
|
{
|
|
XP_ASSERT(currentContext->mailMaster);
|
|
MSG_IMAPFolderInfoMail *renameFolder = currentContext->mailMaster->FindImapMailFolder(names->fOldName);
|
|
|
|
if (renameFolder)
|
|
{
|
|
// calculate the leaf of the new name
|
|
char *newleafName = XP_STRRCHR(names->fNewName, '/');
|
|
if (newleafName)
|
|
newleafName++;
|
|
else
|
|
newleafName = names->fNewName; // renaming root level box
|
|
|
|
// if the renameFolder->GetName() == newleafName, then this is a hierarchy move
|
|
if (0 == XP_STRCMP(renameFolder->GetName(), newleafName))
|
|
{
|
|
// find the new parent folder
|
|
MSG_FolderInfo *parentFolder = NULL;
|
|
char *parentOnlineName = XP_STRDUP(names->fNewName);
|
|
char *slash = XP_STRRCHR(parentOnlineName,'/');
|
|
if (slash)
|
|
{
|
|
*slash = 0;
|
|
parentFolder = currentContext->mailMaster->FindImapMailFolder(parentOnlineName);
|
|
}
|
|
else
|
|
{
|
|
// moving to root level
|
|
parentFolder = currentContext->mailMaster->GetImapMailFolderTree();
|
|
}
|
|
|
|
// fix up the folder pane
|
|
MSG_FolderPane *folderPane = (MSG_FolderPane *) currentContext->mailMaster->FindFirstPaneOfType(MSG_FOLDERPANE);
|
|
if (folderPane)
|
|
{
|
|
// folder pane
|
|
folderPane->PerformLegalFolderMove(renameFolder, parentFolder);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// do the rename
|
|
MSG_Pane *folderPane = currentContext->mailMaster->FindFirstPaneOfType(MSG_FOLDERPANE);
|
|
if (folderPane)
|
|
((MSG_FolderPane *) folderPane)->RenameOfflineFolder(renameFolder, newleafName);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
XP_Bool NoDescendantsAreVerified(MSG_IMAPFolderInfoMail *parentFolder)
|
|
{
|
|
XP_Bool nobodyIsVerified = TRUE;
|
|
int numberOfSubfolders = parentFolder->GetNumSubFolders();
|
|
|
|
for (int childIndex=0; nobodyIsVerified && (childIndex < numberOfSubfolders); childIndex++)
|
|
{
|
|
MSG_IMAPFolderInfoMail *currentChild = (MSG_IMAPFolderInfoMail *) parentFolder->GetSubFolder(childIndex);
|
|
nobodyIsVerified = !currentChild->GetIsOnlineVerified() && NoDescendantsAreVerified(currentChild);
|
|
}
|
|
|
|
return nobodyIsVerified;
|
|
}
|
|
|
|
static
|
|
XP_Bool AllDescendantsAreNoSelect(MSG_IMAPFolderInfoMail *parentFolder)
|
|
{
|
|
XP_Bool allDescendantsAreNoSelect = TRUE;
|
|
int numberOfSubfolders = parentFolder->GetNumSubFolders();
|
|
|
|
for (int childIndex=0; allDescendantsAreNoSelect && (childIndex < numberOfSubfolders); childIndex++)
|
|
{
|
|
MSG_IMAPFolderInfoMail *currentChild = (MSG_IMAPFolderInfoMail *) parentFolder->GetSubFolder(childIndex);
|
|
allDescendantsAreNoSelect = (currentChild->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT) &&
|
|
AllDescendantsAreNoSelect(currentChild);
|
|
}
|
|
|
|
return allDescendantsAreNoSelect;
|
|
}
|
|
|
|
static
|
|
void UnsubscribeFromAllDescendants(MSG_IMAPFolderInfoMail *parentFolder, MSG_Pane *pane)
|
|
{
|
|
int numberOfSubfolders = parentFolder->GetNumSubFolders();
|
|
|
|
for (int childIndex=0; childIndex < numberOfSubfolders; childIndex++)
|
|
{
|
|
MSG_IMAPFolderInfoMail *currentChild = (MSG_IMAPFolderInfoMail *) parentFolder->GetSubFolder(childIndex);
|
|
char *unsubscribeUrl = CreateIMAPUnsubscribeMailboxURL(currentChild->GetHostName(), currentChild->GetOnlineName()); // unsubscribe from child
|
|
if (unsubscribeUrl)
|
|
{
|
|
MSG_UrlQueue::AddUrlToPane(unsubscribeUrl, NULL, pane);
|
|
XP_FREE(unsubscribeUrl);
|
|
}
|
|
UnsubscribeFromAllDescendants(currentChild, pane); // unsubscribe from its children
|
|
}
|
|
}
|
|
|
|
static
|
|
void DeleteNonVerifiedImapFolders(MSG_FolderInfo* parentFolder,
|
|
MSG_FolderPane *folderPane,// can be null
|
|
MSG_Pane **url_pane)
|
|
{
|
|
XP_Bool autoUnsubscribeFromNoSelectFolders = TRUE;
|
|
PREF_GetBoolPref("mail.imap.auto_unsubscribe_from_noselect_folders", &autoUnsubscribeFromNoSelectFolders);
|
|
int numberOfSubFolders = parentFolder->GetNumSubFolders();
|
|
for (int folderIndex = 0; folderIndex < numberOfSubFolders; )
|
|
{
|
|
MSG_FolderInfo* currentFolder = parentFolder->GetSubFolder(folderIndex);
|
|
if (currentFolder && (currentFolder->GetType() == FOLDER_IMAPMAIL))
|
|
{
|
|
MSG_IMAPFolderInfoMail *currentImapFolder = (MSG_IMAPFolderInfoMail *) currentFolder;
|
|
|
|
MSG_IMAPFolderInfoMail *parentImapFolder = (parentFolder->GetType() == FOLDER_IMAPMAIL) ?
|
|
(MSG_IMAPFolderInfoMail *) parentFolder :
|
|
(MSG_IMAPFolderInfoMail *)NULL;
|
|
|
|
// if the parent is the imap container or an imap folder whose children were listed, then this bool is true.
|
|
// We only delete .snm files whose parent's children were listed
|
|
XP_Bool parentChildrenWereListed = (parentImapFolder == NULL) ||
|
|
(LL_CMP(parentImapFolder->GetTimeStampOfLastList(), >= , IMAP_GetTimeStampOfNonPipelinedList()));
|
|
|
|
MSG_IMAPHost *imapHost = currentImapFolder->GetIMAPHost();
|
|
#ifdef DEBUG_chrisf
|
|
XP_ASSERT(imapHost);
|
|
#endif
|
|
XP_Bool usingSubscription = imapHost ? imapHost->GetIsHostUsingSubscription() : TRUE;
|
|
XP_Bool folderIsNoSelectFolder = (currentImapFolder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT);
|
|
XP_Bool shouldDieBecauseNoSelect = usingSubscription ?
|
|
(folderIsNoSelectFolder ? (NoDescendantsAreVerified(currentImapFolder) || AllDescendantsAreNoSelect(currentImapFolder)): FALSE)
|
|
: FALSE;
|
|
|
|
if (shouldDieBecauseNoSelect ||
|
|
((usingSubscription ? TRUE : parentChildrenWereListed) && !currentImapFolder->GetIsOnlineVerified() && NoDescendantsAreVerified(currentImapFolder)))
|
|
{
|
|
// This folder is going away.
|
|
// Give notification so that folder menus can be rebuilt.
|
|
if (*url_pane)
|
|
{
|
|
XPPtrArray referringPanes;
|
|
uint32 total;
|
|
|
|
(*url_pane)->GetMaster()->FindPanesReferringToFolder(currentFolder,&referringPanes);
|
|
total = referringPanes.GetSize();
|
|
for (int i=0; i < total;i++)
|
|
{
|
|
MSG_Pane *currentPane = (MSG_Pane *) referringPanes.GetAt(i);
|
|
if (currentPane)
|
|
{
|
|
if (currentPane->GetFolder() == currentFolder)
|
|
{
|
|
currentPane->SetFolder(NULL);
|
|
FE_PaneChanged(currentPane, TRUE, MSG_PaneNotifyFolderDeleted, (uint32)currentFolder);
|
|
}
|
|
}
|
|
}
|
|
|
|
FE_PaneChanged(*url_pane, TRUE, MSG_PaneNotifyFolderDeleted, (uint32)currentFolder);
|
|
|
|
// If we are running the IMAP subscribe upgrade, and we are deleting the folder that we'd normally
|
|
// try to load after the process completes, then tell the pane not to load that folder.
|
|
if (((MSG_ThreadPane *)(*url_pane))->GetIMAPUpgradeFolder() == currentFolder)
|
|
((MSG_ThreadPane *)(*url_pane))->SetIMAPUpgradeFolder(NULL);
|
|
|
|
if ((*url_pane)->GetFolder() == currentFolder)
|
|
*url_pane = NULL;
|
|
|
|
|
|
if (shouldDieBecauseNoSelect && autoUnsubscribeFromNoSelectFolders && usingSubscription)
|
|
{
|
|
char *unsubscribeUrl = CreateIMAPUnsubscribeMailboxURL(imapHost->GetHostName(), currentImapFolder->GetOnlineName());
|
|
if (unsubscribeUrl)
|
|
{
|
|
if (url_pane)
|
|
MSG_UrlQueue::AddUrlToPane(unsubscribeUrl, NULL, *url_pane);
|
|
else if (folderPane)
|
|
MSG_UrlQueue::AddUrlToPane(unsubscribeUrl, NULL, folderPane);
|
|
#ifdef DEBUG_chrisf
|
|
else XP_ASSERT(FALSE);
|
|
#endif
|
|
XP_FREE(unsubscribeUrl);
|
|
}
|
|
|
|
if (AllDescendantsAreNoSelect(currentImapFolder) && (currentImapFolder->GetNumSubFolders() > 0))
|
|
{
|
|
// This folder has descendants, all of which are also \NoSelect.
|
|
// We'd like to unsubscribe from all of these as well.
|
|
if (url_pane)
|
|
UnsubscribeFromAllDescendants(currentImapFolder, *url_pane);
|
|
else if (folderPane)
|
|
UnsubscribeFromAllDescendants(currentImapFolder, folderPane);
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_chrisf
|
|
XP_ASSERT(FALSE);
|
|
#endif
|
|
}
|
|
|
|
parentFolder->PropagateDelete(¤tFolder); // currentFolder is null on return
|
|
numberOfSubFolders--;
|
|
folderIndex--;
|
|
}
|
|
else
|
|
{
|
|
if (currentFolder->HasSubFolders())
|
|
DeleteNonVerifiedImapFolders(currentFolder, folderPane, url_pane);
|
|
}
|
|
}
|
|
folderIndex++; // not in for statement because we modify it
|
|
}
|
|
}
|
|
|
|
|
|
static void DeleteNonVerifiedExitFunction (URL_Struct *URL_s, int status, MWContext *window_id)
|
|
{
|
|
if (URL_s->msg_pane)
|
|
{
|
|
MSG_Master *master = URL_s->msg_pane->GetMaster();
|
|
MSG_Pane *notificationPane = URL_s->msg_pane;
|
|
if (notificationPane->GetParentPane() != NULL)
|
|
notificationPane = notificationPane->GetParentPane();
|
|
|
|
Net_GetUrlExitFunc *existingExit = URL_s->msg_pane->GetPreImapFolderVerifyUrlExitFunction();
|
|
if (existingExit)
|
|
{
|
|
URL_s->msg_pane->SetPreImapFolderVerifyUrlExitFunction(NULL);
|
|
(existingExit) (URL_s, status, window_id);
|
|
}
|
|
|
|
if (MSG_Pane::PaneInMasterList(notificationPane))
|
|
{
|
|
char *host = NET_ParseURL (URL_s->address, GET_HOST_PART);
|
|
|
|
if (host && (status >= 0)) // don't delete non-verified folders if there was some kind of error (such as interruption)
|
|
{
|
|
// trash any MSG_IMAPFolderInfoMail's for folders that were not online verified
|
|
MSG_FolderInfoContainer *imapContainer = master->GetImapMailFolderTreeForHost(host);
|
|
MSG_FolderPane *folderPane = (MSG_FolderPane *) master->FindFirstPaneOfType(MSG_FOLDERPANE);
|
|
DeleteNonVerifiedImapFolders(imapContainer, folderPane, ¬ificationPane);
|
|
XP_FREE(host);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
extern "C" void ReportMailboxDiscoveryDone(MWContext *currentContext, URL_Struct *URL_s)
|
|
{
|
|
XP_ASSERT(currentContext->mailMaster);
|
|
|
|
if (currentContext->imapURLPane && (currentContext->imapURLPane->GetPaneType() == MSG_SUBSCRIBEPANE))
|
|
{
|
|
// Finished discovering folders for the subscribe pane.
|
|
((MSG_SubscribePane *)(currentContext->imapURLPane))->ReportIMAPFolderDiscoveryFinished();
|
|
}
|
|
else
|
|
{
|
|
// only do this if we're discovering folders for real (i.e. not subscribe UI)
|
|
|
|
// if this profile has not yet dealt with the mail.imap.delete_is_move_to_trash pref yet,
|
|
// deal with it now. If there is a trash folder change the preference to delete to trash.
|
|
if (currentContext->mailMaster)
|
|
{
|
|
MSG_Prefs *prefs = currentContext->mailMaster->GetPrefs();
|
|
if (!(prefs->GetStartingMailNewsProfileAge() & MSG_IMAP_DELETE_MODEL_UPGRADE_FLAG))
|
|
{
|
|
// 0 -> 1 transition is reserved for the imap delete model trick
|
|
prefs->SetMailNewsProfileAgeFlag(MSG_IMAP_DELETE_MODEL_UPGRADE_FLAG);
|
|
|
|
if (!prefs->IMAPMessageDeleteIsMoveToTrash())
|
|
{
|
|
MSG_IMAPFolderInfoContainer *tree = currentContext->mailMaster->GetImapMailFolderTree();
|
|
if (tree)
|
|
{
|
|
MSG_FolderInfo *trashFolder = NULL;
|
|
tree->GetFoldersWithFlag (MSG_FOLDER_FLAG_TRASH, &trashFolder, 1);
|
|
if (trashFolder)
|
|
{
|
|
// so a trash folder exists, lets change the delete model
|
|
PREF_SetBoolPref("mail.imap.delete_is_move_to_trash",TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (URL_s && URL_s->msg_pane && !URL_s->msg_pane->GetPreImapFolderVerifyUrlExitFunction())
|
|
{
|
|
URL_s->msg_pane->SetPreImapFolderVerifyUrlExitFunction(URL_s->pre_exit_fn);
|
|
URL_s->pre_exit_fn = DeleteNonVerifiedExitFunction;
|
|
}
|
|
|
|
XP_ASSERT(currentContext->imapURLPane);
|
|
if (currentContext->imapURLPane)
|
|
currentContext->imapURLPane->SetNumberOfNewImapMailboxes(0);
|
|
|
|
// Go through folders and find if there are still any that are left unverified.
|
|
// If so, manually LIST them to see if we can find out any info about them.
|
|
char *hostName = NET_ParseURL(URL_s->address, GET_HOST_PART);
|
|
if (hostName && currentContext->mailMaster && currentContext->imapURLPane)
|
|
{
|
|
MSG_FolderInfoContainer *hostContainerInfo = currentContext->mailMaster->GetImapMailFolderTreeForHost(hostName);
|
|
MSG_IMAPFolderInfoContainer *hostInfo = hostContainerInfo ? hostContainerInfo->GetIMAPFolderInfoContainer() : (MSG_IMAPFolderInfoContainer *)NULL;
|
|
if (hostInfo)
|
|
{
|
|
// for each folder
|
|
|
|
int32 numberOfUnverifiedFolders = hostInfo->GetUnverifiedFolders(NULL, 0);
|
|
if (numberOfUnverifiedFolders > 0)
|
|
{
|
|
MSG_IMAPFolderInfoMail **folderList = (MSG_IMAPFolderInfoMail **)XP_ALLOC(sizeof(MSG_IMAPFolderInfoMail*) * numberOfUnverifiedFolders);
|
|
if (folderList)
|
|
{
|
|
int32 numUsed = hostInfo->GetUnverifiedFolders(folderList, numberOfUnverifiedFolders);
|
|
for (int32 k = 0; k < numUsed; k++)
|
|
{
|
|
MSG_IMAPFolderInfoMail *currentFolder = folderList[k];
|
|
if ((currentFolder->GetNumSubFolders() > 0) && !NoDescendantsAreVerified(currentFolder))
|
|
{
|
|
// If there are no subfolders and this is unverified, we don't want to run
|
|
// this url. That is, we want to undiscover the folder.
|
|
// If there are subfolders and no descendants are verified, we want to
|
|
// undiscover all of the folders.
|
|
// Only if there are subfolders and at least one of them is verified do we want
|
|
// to refresh that folder's flags, because it won't be going away.
|
|
char *url = CreateIMAPListFolderURL(hostName, currentFolder->GetOnlineName());
|
|
if (url)
|
|
{
|
|
MSG_UrlQueue::AddUrlToPane(url, NULL, currentContext->imapURLPane);
|
|
XP_FREE(url);
|
|
}
|
|
}
|
|
}
|
|
XP_FREE(folderList);
|
|
}
|
|
}
|
|
}
|
|
XP_FREE(hostName);
|
|
}
|
|
else
|
|
{
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" void ReportSuccessOfOnlineCopy(MWContext *currentContext, enum ImapOnlineCopyState copyState)
|
|
{
|
|
if (currentContext->msgCopyInfo)
|
|
{
|
|
currentContext->msgCopyInfo->imapOnLineCopyState = copyState;
|
|
MSG_FinishCopyingMessages(currentContext);
|
|
}
|
|
|
|
if (copyState == kFailedCopy)
|
|
FE_Alert(currentContext, XP_GetString(MK_MSG_ONLINE_COPY_FAILED));
|
|
if (copyState == kFailedDelete)
|
|
FE_Alert(currentContext, XP_GetString(MK_MSG_ONLINE_MOVE_FAILED));
|
|
}
|
|
|
|
extern "C" void ReportSuccessOfChildMailboxDiscovery(MWContext *currentContext)
|
|
{
|
|
if (!currentContext || !currentContext->imapURLPane)
|
|
return;
|
|
MSG_Pane *pane = currentContext->imapURLPane;
|
|
if (pane->GetPaneType() == MSG_SUBSCRIBEPANE)
|
|
{
|
|
((MSG_SubscribePane *)pane)->IMAPChildDiscoverySuccessful();
|
|
}
|
|
}
|
|
|
|
|
|
extern "C"
|
|
void BeginMessageUpload(MWContext *currentContext,
|
|
PRFileDesc *ioSocket,
|
|
UploadCompleteFunctionPointer *completeFunction,
|
|
void *completionFunctionArgument)
|
|
{
|
|
msg_move_state *moveState = ¤tContext->msgCopyInfo->moveState;
|
|
moveState->uploadToIMAPsocket = ioSocket; // this will be detected and the copy will start
|
|
moveState->uploadCompleteFunction = completeFunction;
|
|
moveState->uploadCompleteArgument = completionFunctionArgument;
|
|
}
|
|
|
|
|
|
extern "C" XP_Bool MSG_ShouldUpgradeToIMAPSubscription(MSG_Master *mast)
|
|
{
|
|
MSG_Prefs *prefs = mast->GetPrefs();
|
|
XP_ASSERT(prefs);
|
|
if (prefs)
|
|
{
|
|
int32 profileAge = prefs->GetStartingMailNewsProfileAge();
|
|
if (profileAge & MSG_IMAP_SUBSCRIBE_UPGRADE_FLAG) // we've upgraded to subscription already
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
extern "C" void MSG_ReportSuccessOfUpgradeToIMAPSubscription(MWContext *context, enum EIMAPSubscriptionUpgradeState *state)
|
|
{
|
|
XP_ASSERT(context->mailMaster);
|
|
|
|
if (!context->mailMaster)
|
|
return;
|
|
|
|
if (*state == kBringUpSubscribeUI)
|
|
{
|
|
// force it to bring up the subscribe UI
|
|
context->mailMaster->TryUpgradeToIMAPSubscription(context, TRUE, NULL, NULL);
|
|
}
|
|
else // write out the prefs, all done!
|
|
{
|
|
context->mailMaster->SetIMAPSubscriptionUpgradedPrefs();
|
|
}
|
|
|
|
// storage now owned by us
|
|
FREEIF(state);
|
|
}
|
|
|
|
extern "C" XP_Bool MSG_GetFolderNeedsSubscribing(MSG_FolderInfo *folder)
|
|
{
|
|
if (folder)
|
|
{
|
|
MSG_IMAPFolderInfoMail *imapInfo = folder->GetIMAPFolderInfoMail();
|
|
if (imapInfo)
|
|
return imapInfo->GetFolderNeedsSubscribing();
|
|
else
|
|
return FALSE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
extern "C" XP_Bool MSG_GetFolderNeedsACLListed(MSG_FolderInfo *folder)
|
|
{
|
|
if (folder)
|
|
{
|
|
MSG_IMAPFolderInfoMail *imapInfo = folder->GetIMAPFolderInfoMail();
|
|
if (imapInfo)
|
|
return imapInfo->GetFolderNeedsACLListed();
|
|
else
|
|
return FALSE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
extern "C" void
|
|
MSG_AddFolderRightsForUser(MSG_Master *master, const char *hostName, const char*mailboxName,
|
|
const char *userName, const char *rights)
|
|
{
|
|
|
|
MSG_IMAPFolderInfoMail *fi = master->FindImapMailFolder(hostName, mailboxName, NULL, FALSE);
|
|
if (fi)
|
|
{
|
|
fi->AddFolderRightsForUser(userName, rights);
|
|
}
|
|
else
|
|
{
|
|
// we don't have info about this folder... maybe it's an unsolicited response? probably not.
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
extern "C" void
|
|
MSG_ClearFolderRightsForFolder(MSG_Master *master, const char *hostName, const char *mailboxName)
|
|
{
|
|
MSG_IMAPFolderInfoMail *fi = master->FindImapMailFolder(hostName, mailboxName, NULL, FALSE);
|
|
if (fi)
|
|
{
|
|
fi->ClearAllFolderACLRights();
|
|
}
|
|
else
|
|
{
|
|
// we don't have info about this folder... maybe it's an unsolicited response? probably not.
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
extern "C" void
|
|
MSG_RefreshFolderRightsViewForFolder(MSG_Master *master, const char *hostName, const char *mailboxName)
|
|
{
|
|
MSG_IMAPFolderInfoMail *fi = master->FindImapMailFolder(hostName, mailboxName, NULL, FALSE);
|
|
if (fi)
|
|
{
|
|
fi->RefreshFolderACLRightsView();
|
|
}
|
|
else
|
|
{
|
|
// we don't have info about this folder... maybe it's an unsolicited response? probably not.
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
--------------------------------------------------------------
|
|
MSG_IMAPFolderInfoContainer
|
|
--------------------------------------------------------------
|
|
*/
|
|
|
|
MSG_IMAPFolderInfoContainer::MSG_IMAPFolderInfoContainer (const char *name, uint8 depth, MSG_IMAPHost *host, MSG_Master *master)
|
|
: MSG_FolderInfoContainer (name, depth, MSG_FolderInfoMail::CompareFolders)
|
|
{
|
|
m_prettyName = NULL;
|
|
m_flags |= MSG_FOLDER_FLAG_MAIL | MSG_FOLDER_FLAG_IMAPBOX | MSG_FOLDER_FLAG_IMAP_SERVER;
|
|
m_host = host;
|
|
m_foldersNeedUpdating = FALSE;
|
|
m_master = master;
|
|
}
|
|
|
|
const char *MSG_IMAPFolderInfoContainer::GetPrettyName()
|
|
{
|
|
if (!m_prettyName)
|
|
{
|
|
m_prettyName = m_host->GetPrettyName();
|
|
if (!m_prettyName)
|
|
m_prettyName = PR_smprintf (XP_GetString(MK_MSG_IMAP_CONTAINER_NAME), GetName());
|
|
}
|
|
return m_prettyName;
|
|
}
|
|
|
|
FolderType MSG_IMAPFolderInfoContainer::GetType()
|
|
{
|
|
return FOLDER_IMAPSERVERCONTAINER;
|
|
}
|
|
|
|
XP_Bool
|
|
MSG_IMAPFolderInfoContainer::IsDeletable()
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
MSG_IMAPFolderInfoMail *MSG_IMAPFolderInfoContainer::FindImapMailOnline(const char* onlineServerName, const char *owner, XP_Bool createIfMissing)
|
|
{
|
|
MSG_IMAPFolderInfoMail *thePrize = NULL;
|
|
char *ourOnlineServerName = XP_STRDUP(onlineServerName);
|
|
XP_ASSERT(ourOnlineServerName);
|
|
if (!ourOnlineServerName)
|
|
return NULL;
|
|
|
|
if (owner && *owner)
|
|
{
|
|
// if there was an owner of this folder specified in the URL
|
|
if (!XP_STRCMP(owner, m_host->GetUserName()))
|
|
{
|
|
// the owner is us. Prepend the personal namespace, if it's not INBOX (protocol-defined)
|
|
if (XP_STRCASECMP(onlineServerName, "INBOX"))
|
|
{
|
|
const char *prefix = m_host->GetDefaultNamespacePrefixOfType(kPersonalNamespace);
|
|
if (prefix)
|
|
{
|
|
ourOnlineServerName = PR_smprintf("%s%s", prefix, onlineServerName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the owner is not us. Prepend the other users namespace
|
|
const char *prefix = m_host->GetDefaultNamespacePrefixOfType(kOtherUsersNamespace);
|
|
if (prefix)
|
|
{
|
|
ourOnlineServerName = PR_smprintf("%s%s%c%s", prefix, owner, prefix[XP_STRLEN(prefix)-1], onlineServerName);
|
|
}
|
|
}
|
|
}
|
|
|
|
int numberOfChildren = GetNumSubFolders();
|
|
for (int childIndex = 0; (childIndex < numberOfChildren) && !thePrize; childIndex++)
|
|
{
|
|
MSG_IMAPFolderInfoMail *currentChild = (MSG_IMAPFolderInfoMail *) GetSubFolder(childIndex);
|
|
thePrize = currentChild->FindImapMailOnline(ourOnlineServerName);
|
|
}
|
|
|
|
if (!thePrize && createIfMissing)
|
|
{
|
|
mailbox_spec *createdSpec = (mailbox_spec *) XP_ALLOC( sizeof(mailbox_spec));
|
|
if (createdSpec)
|
|
{
|
|
createdSpec->folderSelected = FALSE;
|
|
createdSpec->hierarchySeparator = '/'; // we don't know anything about this folder yet. Try '/'.
|
|
createdSpec->allocatedPathName = XP_STRDUP(ourOnlineServerName);
|
|
createdSpec->hostName = XP_STRDUP(GetHostName());
|
|
createdSpec->flagState = NULL;
|
|
createdSpec->discoveredFromLsub = FALSE;
|
|
createdSpec->box_flags = kNoselect; // default, until we've discovered otherwise
|
|
createdSpec->onlineVerified = FALSE;
|
|
|
|
DiscoverIMAPMailbox(createdSpec, m_master, NULL, TRUE);
|
|
|
|
thePrize = FindImapMailOnline(ourOnlineServerName, NULL, FALSE); // don't try to create it again
|
|
|
|
if (thePrize)
|
|
thePrize->SetFolderNeedsSubscribing(TRUE);
|
|
}
|
|
}
|
|
|
|
FREEIF(ourOnlineServerName);
|
|
|
|
return thePrize;
|
|
}
|
|
|
|
char *MSG_IMAPFolderInfoContainer::BuildUrl (MessageDB * /* db */, MessageKey /* key */)
|
|
{
|
|
return PR_smprintf("%s//%s", "IMAP:", GetHostName());
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoContainer::GetAdminUrl(MWContext *context, MSG_AdminURLType type)
|
|
{
|
|
return m_host->RunAdminURL(context, this, type);
|
|
}
|
|
XP_Bool MSG_IMAPFolderInfoContainer::HaveAdminUrl(MSG_AdminURLType type)
|
|
{
|
|
return m_host->HaveAdminURL(type);
|
|
}
|
|
|
|
int32 MSG_IMAPFolderInfoContainer::GetUnverifiedFolders(MSG_IMAPFolderInfoMail** result,
|
|
int32 resultsize)
|
|
{
|
|
int num = 0;
|
|
MSG_IMAPFolderInfoMail *folder = NULL;
|
|
for (int i=0; i < m_subFolders->GetSize(); i++) {
|
|
folder = (MSG_IMAPFolderInfoMail*) m_subFolders->GetAt(i);
|
|
num += folder->GetUnverifiedFolders(result + num, resultsize - num);
|
|
}
|
|
return num;
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::DeleteIsMoveToTrash()
|
|
{
|
|
return m_host->GetDeleteIsMoveToTrash();
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderInfoMail::ShowDeletedMessages()
|
|
{
|
|
return (m_host->GetIMAPDeleteModel() == MSG_IMAPDeleteIsIMAPDelete);
|
|
}
|
|
|
|
#ifdef FE_SetBiffInfoDefined // only Win32 has the stand-alone biff for now
|
|
// to enable this on other platforms, simply add them to the
|
|
// #ifdef around FE_SetBiffInfo in msgcom.h
|
|
// and implement FE_SetBiffInfo in the FE
|
|
|
|
|
|
void UpdateStandAloneIMAPBiff(const IDArray& keysToFetch)
|
|
{
|
|
// there are new keys to fetch here
|
|
// if this is the INBOX, we want to record the highest UID
|
|
// we've ever seen for purposes of the stand-alone biff
|
|
// affectedDB->m_dbFolderInfo->m_LastMessageUID;
|
|
uint32 highWaterUID = 0;
|
|
static const char szHighWaterKey[] = "IMAPHighWaterUID";
|
|
uint32 total = keysToFetch.GetSize();
|
|
|
|
for (uint32 fetchIndex = 0; fetchIndex < total; fetchIndex++)
|
|
{
|
|
if (keysToFetch[fetchIndex] > highWaterUID)
|
|
highWaterUID = keysToFetch[fetchIndex];
|
|
}
|
|
if (highWaterUID != 0)
|
|
{
|
|
FE_SetBiffInfo(MSG_IMAPHighWaterMark, highWaterUID);
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
void UpdateStandAloneIMAPBiff(const IDArray& /*keysToFetch*/)
|
|
{
|
|
}
|
|
|
|
#endif // #ifdef FE_SetBiffInfoDefined
|
|
|
|
MsgERR MSG_IMAPFolderInfoContainer::Adopt (MSG_FolderInfo *srcFolder, int32 *pOutPos)
|
|
{
|
|
MsgERR err = eSUCCESS;
|
|
XP_ASSERT (srcFolder->GetType() == FOLDER_IMAPMAIL); // we can only adopt imap folders msgfinfo.h
|
|
MSG_FolderInfoMail *mailFolder = (MSG_FolderInfoMail*) srcFolder;
|
|
|
|
if (srcFolder == this)
|
|
return MK_MSG_CANT_COPY_TO_SAME_FOLDER;
|
|
|
|
if (ContainsChildNamed(mailFolder->GetName()))
|
|
return MK_MSG_FOLDER_ALREADY_EXISTS;
|
|
|
|
// Recurse the tree to adopt srcFolder's children
|
|
err = mailFolder->PropagateAdopt (m_host->GetLocalDirectory(), m_depth);
|
|
// Add the folder to our tree in the right sorted position
|
|
if (eSUCCESS == err)
|
|
{
|
|
XP_ASSERT(m_subFolders->FindIndex(0, srcFolder) == -1);
|
|
*pOutPos = m_subFolders->Add (srcFolder);
|
|
// m_subFolders.InsertAt (0, srcFolder);
|
|
// m_subFolders.QuickSort (MSG_FolderInfoMail::CompareFolders);
|
|
// *pOutPos = m_subFolders.FindIndex (0, srcFolder);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
MSG_IMAPHost *MSG_IMAPFolderInfoContainer::GetIMAPHost() {return m_host;}
|
|
|
|
const char* MSG_IMAPFolderInfoContainer::GetHostName()
|
|
{
|
|
return (m_host) ? m_host->GetHostName() : GetMaster()->GetPrefs()->GetPopHost();
|
|
}
|
|
|
|
|
|
MSG_IMAPFolderInfoContainer::~MSG_IMAPFolderInfoContainer ()
|
|
{
|
|
FREEIF(m_prettyName);
|
|
}
|
|
|
|
MSG_IMAPFolderInfoContainer *MSG_IMAPFolderInfoContainer::GetIMAPFolderInfoContainer()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
|
|
///////// MSG_IMAPFolderACL class ///////////////////////////////
|
|
|
|
static int
|
|
imap_hash_strcmp (const void *a, const void *b)
|
|
{
|
|
return XP_STRCMP ((const char *) a, (const char *) b);
|
|
}
|
|
|
|
static XP_Bool imap_freehashrights(XP_HashTable /*table*/, const void* key,
|
|
void* value, void* /*closure*/)
|
|
{
|
|
char *valueChar = (char*) value;
|
|
char *userChar = (char*) key;
|
|
FREEIF(valueChar);
|
|
FREEIF(userChar);
|
|
return TRUE;
|
|
}
|
|
|
|
MSG_IMAPFolderACL::MSG_IMAPFolderACL(MSG_IMAPFolderInfoMail *folder)
|
|
{
|
|
XP_ASSERT(folder);
|
|
m_folder = folder;
|
|
m_rightsHash = XP_HashTableNew(24, XP_StringHash, imap_hash_strcmp);
|
|
m_aclCount = 0;
|
|
BuildInitialACLFromCache();
|
|
}
|
|
|
|
MSG_IMAPFolderACL::~MSG_IMAPFolderACL()
|
|
{
|
|
XP_MapRemhash(m_rightsHash, imap_freehashrights, NULL);
|
|
XP_HashTableDestroy(m_rightsHash);
|
|
}
|
|
|
|
// We cache most of our own rights in the MSG_FOLDER_PREF_* flags
|
|
void MSG_IMAPFolderACL::BuildInitialACLFromCache()
|
|
{
|
|
char *myrights = 0;
|
|
|
|
if (m_folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAP_ACL_READ)
|
|
StrAllocCat(myrights,"r");
|
|
if (m_folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAP_ACL_STORE_SEEN)
|
|
StrAllocCat(myrights,"s");
|
|
if (m_folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAP_ACL_WRITE)
|
|
StrAllocCat(myrights,"w");
|
|
if (m_folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAP_ACL_INSERT)
|
|
StrAllocCat(myrights,"i");
|
|
if (m_folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAP_ACL_POST)
|
|
StrAllocCat(myrights,"p");
|
|
if (m_folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAP_ACL_CREATE_SUBFOLDER)
|
|
StrAllocCat(myrights,"c");
|
|
if (m_folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAP_ACL_DELETE)
|
|
StrAllocCat(myrights,"d");
|
|
if (m_folder->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAP_ACL_ADMINISTER)
|
|
StrAllocCat(myrights,"a");
|
|
|
|
if (myrights && *myrights)
|
|
SetFolderRightsForUser(NULL, myrights);
|
|
FREEIF(myrights);
|
|
}
|
|
|
|
void MSG_IMAPFolderACL::UpdateACLCache()
|
|
{
|
|
if (GetCanIReadFolder())
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() | MSG_FOLDER_PREF_IMAP_ACL_READ);
|
|
else
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() & ~MSG_FOLDER_PREF_IMAP_ACL_READ);
|
|
|
|
if (GetCanIStoreSeenInFolder())
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() | MSG_FOLDER_PREF_IMAP_ACL_STORE_SEEN);
|
|
else
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() & ~MSG_FOLDER_PREF_IMAP_ACL_STORE_SEEN);
|
|
|
|
if (GetCanIWriteFolder())
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() | MSG_FOLDER_PREF_IMAP_ACL_WRITE);
|
|
else
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() & ~MSG_FOLDER_PREF_IMAP_ACL_WRITE);
|
|
|
|
if (GetCanIInsertInFolder())
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() | MSG_FOLDER_PREF_IMAP_ACL_INSERT);
|
|
else
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() & ~MSG_FOLDER_PREF_IMAP_ACL_INSERT);
|
|
|
|
if (GetCanIPostToFolder())
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() | MSG_FOLDER_PREF_IMAP_ACL_POST);
|
|
else
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() & ~MSG_FOLDER_PREF_IMAP_ACL_POST);
|
|
|
|
if (GetCanICreateSubfolder())
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() | MSG_FOLDER_PREF_IMAP_ACL_CREATE_SUBFOLDER);
|
|
else
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() & ~MSG_FOLDER_PREF_IMAP_ACL_CREATE_SUBFOLDER);
|
|
|
|
if (GetCanIDeleteInFolder())
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() | MSG_FOLDER_PREF_IMAP_ACL_DELETE);
|
|
else
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() & ~MSG_FOLDER_PREF_IMAP_ACL_DELETE);
|
|
|
|
if (GetCanIAdministerFolder())
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() | MSG_FOLDER_PREF_IMAP_ACL_ADMINISTER);
|
|
else
|
|
m_folder->SetFolderPrefFlags(m_folder->GetFolderPrefFlags() & ~MSG_FOLDER_PREF_IMAP_ACL_ADMINISTER);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::SetFolderRightsForUser(const char *userName, const char *rights)
|
|
{
|
|
XP_Bool rv = FALSE;
|
|
const char *myUserName = m_folder->GetIMAPHost()->GetUserName();
|
|
char *ourUserName = NULL;
|
|
|
|
if (!userName)
|
|
ourUserName = XP_STRDUP(myUserName);
|
|
else
|
|
ourUserName = XP_STRDUP(userName);
|
|
|
|
char *rightsWeOwn = XP_STRDUP(rights);
|
|
if (rightsWeOwn && ourUserName)
|
|
{
|
|
char *oldValue = (char *)XP_Gethash(m_rightsHash, ourUserName, NULL);
|
|
if (oldValue)
|
|
{
|
|
XP_FREE(oldValue);
|
|
XP_Remhash(m_rightsHash, ourUserName);
|
|
m_aclCount--;
|
|
XP_ASSERT(m_aclCount >= 0);
|
|
}
|
|
m_aclCount++;
|
|
rv = (XP_Puthash(m_rightsHash, ourUserName, rightsWeOwn) == 0);
|
|
|
|
}
|
|
|
|
if (ourUserName &&
|
|
(!XP_STRCMP(ourUserName, myUserName) || !XP_STRCMP(ourUserName, IMAP_ACL_ANYONE_STRING)))
|
|
{
|
|
// if this is setting an ACL for me, cache it in the folder pref flags
|
|
UpdateACLCache();
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
const char *MSG_IMAPFolderACL::GetRightsStringForUser(const char *userName)
|
|
{
|
|
if (!userName)
|
|
userName = m_folder->GetIMAPHost()->GetUserName();
|
|
|
|
return (const char *)XP_Gethash(m_rightsHash, userName, NULL);
|
|
}
|
|
|
|
// First looks for individual user; then looks for 'anyone' if the user isn't found.
|
|
// Returns defaultIfNotFound, if neither are found.
|
|
XP_Bool MSG_IMAPFolderACL::GetFlagSetInRightsForUser(const char *userName, char flag, XP_Bool defaultIfNotFound)
|
|
{
|
|
const char *flags = GetRightsStringForUser(userName);
|
|
if (!flags)
|
|
{
|
|
const char *anyoneFlags = GetRightsStringForUser(IMAP_ACL_ANYONE_STRING);
|
|
if (!anyoneFlags)
|
|
return defaultIfNotFound;
|
|
else
|
|
return (XP_STRCHR(anyoneFlags, flag) != NULL);
|
|
}
|
|
else
|
|
return (XP_STRCHR(flags, flag) != NULL);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanUserLookupFolder(const char *userName)
|
|
{
|
|
return GetFlagSetInRightsForUser(userName, 'l', FALSE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanUserReadFolder(const char *userName)
|
|
{
|
|
return GetFlagSetInRightsForUser(userName, 'r', FALSE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanUserStoreSeenInFolder(const char *userName)
|
|
{
|
|
return GetFlagSetInRightsForUser(userName, 's', FALSE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanUserWriteFolder(const char *userName)
|
|
{
|
|
return GetFlagSetInRightsForUser(userName, 'w', FALSE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanUserInsertInFolder(const char *userName)
|
|
{
|
|
return GetFlagSetInRightsForUser(userName, 'i', FALSE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanUserPostToFolder(const char *userName)
|
|
{
|
|
return GetFlagSetInRightsForUser(userName, 'p', FALSE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanUserCreateSubfolder(const char *userName)
|
|
{
|
|
return GetFlagSetInRightsForUser(userName, 'c', FALSE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanUserDeleteInFolder(const char *userName)
|
|
{
|
|
return GetFlagSetInRightsForUser(userName, 'd', FALSE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanUserAdministerFolder(const char *userName)
|
|
{
|
|
return GetFlagSetInRightsForUser(userName, 'a', FALSE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanILookupFolder()
|
|
{
|
|
return GetFlagSetInRightsForUser(NULL, 'l', TRUE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanIReadFolder()
|
|
{
|
|
return GetFlagSetInRightsForUser(NULL, 'r', TRUE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanIStoreSeenInFolder()
|
|
{
|
|
return GetFlagSetInRightsForUser(NULL, 's', TRUE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanIWriteFolder()
|
|
{
|
|
return GetFlagSetInRightsForUser(NULL, 'w', TRUE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanIInsertInFolder()
|
|
{
|
|
return GetFlagSetInRightsForUser(NULL, 'i', TRUE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanIPostToFolder()
|
|
{
|
|
return GetFlagSetInRightsForUser(NULL, 'p', TRUE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanICreateSubfolder()
|
|
{
|
|
return GetFlagSetInRightsForUser(NULL, 'c', TRUE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanIDeleteInFolder()
|
|
{
|
|
return GetFlagSetInRightsForUser(NULL, 'd', TRUE);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetCanIAdministerFolder()
|
|
{
|
|
return GetFlagSetInRightsForUser(NULL, 'a', TRUE);
|
|
}
|
|
|
|
// We use this to see if the ACLs think a folder is shared or not.
|
|
// We will define "Shared" in 5.0 to mean:
|
|
// At least one user other than the currently authenticated user has at least one
|
|
// explicitly-listed ACL right on that folder.
|
|
XP_Bool MSG_IMAPFolderACL::GetIsFolderShared()
|
|
{
|
|
// If we have more than one ACL count for this folder, which means that someone
|
|
// other than ourself has rights on it, then it is "shared."
|
|
if (m_aclCount > 1)
|
|
return TRUE;
|
|
|
|
// Or, if "anyone" has rights to it, it is shared.
|
|
const char *anyonesRights = (const char *)XP_Gethash(m_rightsHash, IMAP_ACL_ANYONE_STRING, NULL);
|
|
|
|
return (anyonesRights != NULL);
|
|
}
|
|
|
|
XP_Bool MSG_IMAPFolderACL::GetDoIHaveFullRightsForFolder()
|
|
{
|
|
return (GetCanIReadFolder() &&
|
|
GetCanIWriteFolder() &&
|
|
GetCanIInsertInFolder() &&
|
|
GetCanIAdministerFolder() &&
|
|
GetCanICreateSubfolder() &&
|
|
GetCanIDeleteInFolder() &&
|
|
GetCanILookupFolder() &&
|
|
GetCanIStoreSeenInFolder() &&
|
|
GetCanIPostToFolder());
|
|
}
|
|
|
|
// Returns a newly allocated string describing these rights
|
|
char *MSG_IMAPFolderACL::CreateACLRightsString()
|
|
{
|
|
char *rv = NULL;
|
|
|
|
if (GetDoIHaveFullRightsForFolder())
|
|
{
|
|
rv = PR_smprintf(XP_GetString(XP_MSG_IMAP_ACL_FULL_RIGHTS));
|
|
}
|
|
else
|
|
{
|
|
if (GetCanIReadFolder())
|
|
{
|
|
if (rv) StrAllocCat(rv, ", ");
|
|
StrAllocCat(rv, XP_GetString(XP_MSG_IMAP_ACL_READ_RIGHT));
|
|
}
|
|
if (GetCanIWriteFolder())
|
|
{
|
|
if (rv) StrAllocCat(rv, ", ");
|
|
StrAllocCat(rv, XP_GetString(XP_MSG_IMAP_ACL_WRITE_RIGHT));
|
|
}
|
|
if (GetCanIInsertInFolder())
|
|
{
|
|
if (rv) StrAllocCat(rv, ", ");
|
|
StrAllocCat(rv, XP_GetString(XP_MSG_IMAP_ACL_INSERT_RIGHT));
|
|
}
|
|
if (GetCanILookupFolder())
|
|
{
|
|
if (rv) StrAllocCat(rv, ", ");
|
|
StrAllocCat(rv, XP_GetString(XP_MSG_IMAP_ACL_LOOKUP_RIGHT));
|
|
}
|
|
if (GetCanIStoreSeenInFolder())
|
|
{
|
|
if (rv) StrAllocCat(rv, ", ");
|
|
StrAllocCat(rv, XP_GetString(XP_MSG_IMAP_ACL_SEEN_RIGHT));
|
|
}
|
|
if (GetCanIDeleteInFolder())
|
|
{
|
|
if (rv) StrAllocCat(rv, ", ");
|
|
StrAllocCat(rv, XP_GetString(XP_MSG_IMAP_ACL_DELETE_RIGHT));
|
|
}
|
|
if (GetCanICreateSubfolder())
|
|
{
|
|
if (rv) StrAllocCat(rv, ", ");
|
|
StrAllocCat(rv, XP_GetString(XP_MSG_IMAP_ACL_CREATE_RIGHT));
|
|
}
|
|
if (GetCanIPostToFolder())
|
|
{
|
|
if (rv) StrAllocCat(rv, ", ");
|
|
StrAllocCat(rv, XP_GetString(XP_MSG_IMAP_ACL_POST_RIGHT));
|
|
}
|
|
if (GetCanIAdministerFolder())
|
|
{
|
|
if (rv) StrAllocCat(rv, ", ");
|
|
StrAllocCat(rv, XP_GetString(XP_MSG_IMAP_ACL_ADMINISTER_RIGHT));
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
XP_Bool MSG_IsFolderACLInitialized(MSG_Master *master, const char *folderName, const char *hostName)
|
|
{
|
|
MSG_IMAPFolderInfoMail *fi = master->FindImapMailFolder(hostName, folderName, NULL, FALSE);
|
|
if (fi)
|
|
return (fi->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAP_ACL_RETRIEVED);
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
extern "C" XP_Bool
|
|
MSG_IsFolderNoSelect(MSG_Master *master, const char *folderName, const char *hostName)
|
|
{
|
|
MSG_IMAPFolderInfoMail *fi = master->FindImapMailFolder(hostName, folderName, NULL, FALSE);
|
|
if (fi)
|
|
return (fi->GetFolderPrefFlags() & MSG_FOLDER_PREF_IMAPNOSELECT);
|
|
else
|
|
return FALSE;
|
|
}
|