/* -*- 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. */ #define FORCE_PR_LOG /* Allow logging in the release build */ #include "rosetta.h" #include "mkutils.h" #include "imap4pvt.h" #include "imap.h" #include "imapbody.h" #include "msgcom.h" #include "msgnet.h" #include "xpgetstr.h" #include "secnav.h" #include HG26363 #include "sslerr.h" #include "prefapi.h" #include "prlog.h" #include "libi18n.h" #include "prtime.h" #include "netutils.h" #include "nslocks.h" #ifdef XP_MAC #include "prpriv.h" /* for NewNamedMonitor */ #else #include "private/prpriv.h" #endif #define MAX_NEW_IMAP_FOLDER_COUNT 50 #define IMAP_DB_HEADERS "From To Cc Subject Date Priority X-Priority Message-ID References Newsgroups" #define IMAP_YIELD(A) PR_Sleep(PR_INTERVAL_NO_WAIT) extern "C" { extern int MK_OUT_OF_MEMORY; /* 45678901234567890123456789012345678901234567890123456789012345678901234567890 */ extern int XP_ERRNO_EWOULDBLOCK; extern int XP_ERRNO_ENOTCONN; extern int MK_TCP_READ_ERROR; extern int MK_TCP_WRITE_ERROR; extern int MK_POP3_NO_MESSAGES; extern int MK_BAD_CONNECT; extern int XP_FOLDER_RECEIVING_MESSAGE_OF; extern int XP_RECEIVING_MESSAGE_HEADERS_OF; extern int XP_RECEIVING_MESSAGE_FLAGS_OF; extern int XP_IMAP_DELETING_MESSAGES; extern int XP_IMAP_DELETING_MESSAGE; extern int XP_IMAP_COPYING_MESSAGES_TO; extern int XP_IMAP_COPYING_MESSAGE_TO; extern int XP_IMAP_MOVING_MESSAGES_TO; extern int XP_IMAP_MOVING_MESSAGE_TO; extern int MK_MSG_IMAP_SERVER_NOT_IMAP4; extern int MK_MSG_IMAP_SERVER_SAID; extern int MK_MSG_TRASH_L10N_NAME; extern int MK_IMAP_STATUS_CREATING_MAILBOX; extern int MK_IMAP_STATUS_SELECTING_MAILBOX; extern int MK_IMAP_STATUS_DELETING_MAILBOX; extern int MK_IMAP_STATUS_RENAMING_MAILBOX; extern int MK_IMAP_STATUS_LOOKING_FOR_MAILBOX; extern int MK_IMAP_STATUS_SUBSCRIBE_TO_MAILBOX; extern int MK_IMAP_STATUS_UNSUBSCRIBE_MAILBOX; extern int MK_IMAP_STATUS_SEARCH_MAILBOX; extern int MK_IMAP_STATUS_MSG_INFO; extern int MK_IMAP_STATUS_CLOSE_MAILBOX; extern int MK_IMAP_STATUS_EXPUNGING_MAILBOX; extern int MK_IMAP_STATUS_LOGGING_OUT; extern int MK_IMAP_STATUS_CHECK_COMPAT; extern int MK_IMAP_STATUS_SENDING_LOGIN; extern int MK_IMAP_STATUS_SENDING_AUTH_LOGIN; extern int MK_IMAP_CREATE_FOLDER_BUT_NO_SUBSCRIBE; extern int MK_IMAP_DELETE_FOLDER_BUT_NO_UNSUBSCRIBE; extern int MK_IMAP_RENAME_FOLDER_BUT_NO_SUBSCRIBE; extern int MK_IMAP_RENAME_FOLDER_BUT_NO_UNSUBSCRIBE; extern int MK_IMAP_STATUS_GETTING_NAMESPACE; extern int MK_IMAP_UPGRADE_NO_PERSONAL_NAMESPACE; extern int MK_IMAP_UPGRADE_TOO_MANY_FOLDERS; extern int MK_MSG_IMAP_DISCOVERING_MAILBOX; extern int MK_IMAP_UPGRADE_PROMPT_USER; extern int MK_IMAP_UPGRADE_PROMPT_USER_2; extern int MK_IMAP_UPGRADE_WAIT_WHILE_UPGRADE; extern int MK_IMAP_UPGRADE_PROMPT_QUESTION; extern int MK_IMAP_UPGRADE_CUSTOM; extern int MK_IMAP_UPGRADE_SUCCESSFUL; extern int MK_MSG_DRAFTS_L10N_NAME; extern int MK_IMAP_GETTING_ACL_FOR_FOLDER; extern int MK_IMAP_GETTING_SERVER_INFO; extern int MK_IMAP_GETTING_MAILBOX_INFO; extern void NET_SetPopPassword2(const char *password); extern int XP_PROMPT_ENTER_PASSWORD; extern int XP_PASSWORD_FOR_POP3_USER; extern int XP_MSG_IMAP_LOGIN_FAILED; extern void net_graceful_shutdown(PRFileDesc* sock, XP_Bool isSecure); } extern PRLogModuleInfo* IMAP; #define out PR_LOG_ALWAYS static int32 gMIMEOnDemandThreshold = 15000; static XP_Bool gMIMEOnDemand = FALSE; static XP_Bool gOptimizedHeaders = FALSE; static int32 gTunnellingThreshold = 2000; static XP_Bool gIOTunnelling = FALSE; // off for now typedef struct _ImapConData { TNavigatorImapConnection *netConnection; void *offLineRetrievalData; // really a DisplayOfflineImapState object uint32 offLineMsgFlags; uint32 offLineMsgKey; NET_StreamClass *offlineDisplayStream; } ImapConData; typedef struct _GenericInfo { char *c, *hostName; } GenericInfo; typedef struct _StreamInfo { uint32 size; char *content_type; } StreamInfo; typedef struct _ProgressInfo { char *message; int percent; } ProgressInfo; typedef struct _FolderQueryInfo { char *name, *hostName; XP_Bool rv; } FolderQueryInfo; typedef struct _StatusMessageInfo { uint32 msgID; char * extraInfo; } StatusMessageInfo; typedef struct _UploadMessageInfo { uint32 newMsgID; XP_File fileId; char *dataBuffer; int32 bytesRemain; } UploadMessageInfo; struct utf_name_struct { XP_Bool toUtf7Imap; unsigned char *sourceString; unsigned char *convertedString; }; typedef struct _TunnelInfo { int32 maxSize; PRFileDesc* ioSocket; char** inputSocketBuffer; int32* inputBufferSize; } TunnelInfo; const char *ImapTRASH_FOLDER_NAME = NULL; const int32 kImapSleepTime = 1000000; int32 atoint32(char *ascii) { char *endptr; int32 rvalue = XP_STRTOUL(ascii, &endptr, 10); return rvalue; } XP_Bool IMAP_ContextIsBiff(ActiveEntry *ce) { return (ce->window_id == MSG_GetBiffContext() || ce->window_id->type == MWContextBiff); } XP_Bool IMAP_URLIsBiff(ActiveEntry *ce, TIMAPUrl ¤tUrl) { return (IMAP_ContextIsBiff(ce) && currentUrl.GetIMAPurlType() == TIMAPUrl::kSelectFolder); } static void IMAP_LoadTrashFolderName(void) { if (!ImapTRASH_FOLDER_NAME) ImapTRASH_FOLDER_NAME = MSG_GetSpecialFolderName(MK_MSG_TRASH_L10N_NAME); } void IMAP_DoNotDownLoadAnyMessageHeadersForMailboxSelect(TNavigatorImapConnection *connection) { if (connection) connection->NotifyKeyList(NULL,0); } void IMAP_DownLoadMessageBodieForMailboxSelect(TNavigatorImapConnection *connection, uint32 *messageKeys, /* uint32* is adopted */ uint32 numberOfKeys) { connection->NotifyKeyList(messageKeys, numberOfKeys); } void IMAP_BodyIdMonitor(TNavigatorImapConnection *connection, XP_Bool enter) { connection->BodyIdMonitor(enter); } void TNavigatorImapConnection::BodyIdMonitor(XP_Bool enter) { if (enter) PR_EnterMonitor(fWaitForBodyIdsMonitor); else PR_ExitMonitor(fWaitForBodyIdsMonitor); } void IMAP_DownLoadMessagesForMailboxSelect(TNavigatorImapConnection *connection, uint32 *messageKeys, /* uint32* is adopted */ uint32 numberOfKeys) { connection->NotifyKeyList(messageKeys, numberOfKeys); } char *IMAP_GetCurrentConnectionUrl(TNavigatorImapConnection *connection) { return connection->GetCurrentConnectionURL(); } void IMAP_UploadAppendMessageSize(TNavigatorImapConnection *connection, uint32 msgSize, imapMessageFlagsType flags) { connection->NotifyAppendSize(msgSize, flags); } void IMAP_TerminateConnection (TNavigatorImapConnection *conn) { conn->TellThreadToDie(); } char *IMAP_CreateOnlineSourceFolderNameFromUrl(const char *url) { TIMAPUrl urlObject(url, TRUE); return urlObject.CreateCanonicalSourceFolderPathString(); } void IMAP_FreeBoxSpec(mailbox_spec *victim) { if (victim) { FREEIF(victim->allocatedPathName); // delete victim->flagState; // not owned by us, leave it alone XP_FREE(victim); } } XP_Bool IMAP_CheckNewMail(TNavigatorImapConnection *connection) { return connection->CheckNewMail(); } XP_Bool IMAP_NewMailDetected(TNavigatorImapConnection *connection) { return connection->NewMailDetected(); } // These C functions implemented here are usually executed as TImapFEEvent's static void SetBiffIndicator(void *biffStateVoid, void *blockingConnectionVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; MSG_BIFF_STATE biffState = (MSG_BIFF_STATE) (uint32) biffStateVoid; MSG_SetBiffStateAndUpdateFE(biffState); imapConnection->NotifyEventCompletionMonitor(); } #ifndef XP_OS2 static #else extern "OPTLINK" #endif void MessageUploadComplete(void *blockingConnectionVoid) // called when upload is complete { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; imapConnection->NotifyMessageUploadMonitor(); } static void UploadMessageEvent(void *blockingConnectionVoid, void *) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; BeginMessageUpload(imapConnection->GetActiveEntry()->window_id, imapConnection->GetIOSocket(), MessageUploadComplete, imapConnection); } static void msgSetUserAuthenticated(void *blockingConnectionVoid, void *trueOrFalseVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); XP_Bool authenticated = (XP_Bool) (uint32) trueOrFalseVoid; if (ce && ce->URL_s->msg_pane) MSG_SetUserAuthenticated(MSG_GetMaster(ce->URL_s->msg_pane), authenticated); } static void LiteSelectEvent(void *blockingConnectionVoid, void * /*unused*/) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); if (ce && ce->URL_s->msg_pane) ReportLiteSelectUIDVALIDITY(ce->URL_s->msg_pane, imapConnection->GetServerStateParser().FolderUID()); imapConnection->NotifyEventCompletionMonitor(); } static void msgSetMailAccountURL(void *blockingConnectionVoid, void *hostName) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); if (ce && ce->URL_s->msg_pane) MSG_SetHostMailAccountURL(MSG_GetMaster(ce->URL_s->msg_pane), (const char *) hostName, imapConnection->GetServerStateParser().GetMailAccountUrl()); } static void msgSetMailServerURLs(void *blockingConnectionVoid, void *hostName) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); if (ce && ce->URL_s->msg_pane) { MSG_SetHostMailAccountURL(MSG_GetMaster(ce->URL_s->msg_pane), (const char *) hostName, imapConnection->GetServerStateParser().GetMailAccountUrl()); MSG_SetHostManageListsURL(MSG_GetMaster(ce->URL_s->msg_pane), (const char *) hostName, imapConnection->GetServerStateParser().GetManageListsUrl()); MSG_SetHostManageFiltersURL(MSG_GetMaster(ce->URL_s->msg_pane), (const char *) hostName, imapConnection->GetServerStateParser().GetManageFiltersUrl()); } } static void MOZ_THREADmsgSetFolderURL(void *blockingConnectionVoid, void *folderQueryInfo) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); if (ce && ce->window_id->mailMaster) { FolderQueryInfo *queryInfo = (FolderQueryInfo *) folderQueryInfo; MSG_SetFolderAdminURL(ce->window_id->mailMaster, queryInfo->hostName, queryInfo->name, imapConnection->GetServerStateParser().GetManageFolderUrl()); } } struct tFlagsKeyStruct { imapMessageFlagsType flags; MessageKey key; }; typedef struct tFlagsKeyStruct tFlagsKeyStruct; static void NotifyMessageFlagsEvent( void *blockingConnectionVoid, void *flagsVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; struct tFlagsKeyStruct *flagAndKey = (struct tFlagsKeyStruct *) flagsVoid; MSG_RecordImapMessageFlags(imapConnection->GetActiveEntry()->window_id->imapURLPane, flagAndKey->key, flagAndKey->flags); FREEIF( flagAndKey); } struct delete_message_struct { char *onlineFolderName; XP_Bool deleteAllMsgs; char *msgIdString; }; static void ConvertImapUtf7(void *utf_name_structVoid, void *blockingConnectionVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; utf_name_struct *names = (utf_name_struct *) utf_name_structVoid; // NULL if bad conversion names->convertedString = NULL; #ifdef XP_WIN16 names->convertedString = (unsigned char *) XP_STRDUP((const char *) names->sourceString); #else int16 fromCsid = names->toUtf7Imap ? (INTL_DocToWinCharSetID(INTL_DefaultDocCharSetID(0)) & ~CS_AUTO): CS_IMAP4_UTF7; int16 toCsid = !names->toUtf7Imap ? (INTL_DocToWinCharSetID(INTL_DefaultDocCharSetID(0)) & ~CS_AUTO): CS_IMAP4_UTF7; // convert from whatever to CS_UTF8 unsigned char *utf8String = INTL_ConvertLineWithoutAutoDetect(fromCsid, CS_UTF8, names->sourceString, XP_STRLEN((const char *) names->sourceString)); if (utf8String) { // convert from CS_UTF8 to whatever names->convertedString = INTL_ConvertLineWithoutAutoDetect(CS_UTF8, toCsid, utf8String, XP_STRLEN((const char *) utf8String)); XP_FREE(utf8String); } #endif if (imapConnection) imapConnection->NotifyEventCompletionMonitor(); } static void NotifyMessageDeletedEvent( void *blockingConnectionVoid, void *delete_message_structVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; struct delete_message_struct *msgStruct = (struct delete_message_struct *) delete_message_structVoid; if (msgStruct->onlineFolderName) MSG_ImapMsgsDeleted(imapConnection->GetActiveEntry()->window_id->imapURLPane, msgStruct->onlineFolderName, imapConnection->GetHostName(), msgStruct->deleteAllMsgs, msgStruct->msgIdString); FREEIF( msgStruct->onlineFolderName ); FREEIF( msgStruct->msgIdString); FREEIF( msgStruct); } static void AddSearchResultEvent( void *blockingConnectionVoid, void *hitLineVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; MSG_AddImapSearchHit(imapConnection->GetActiveEntry()->window_id, (const char *) hitLineVoid); imapConnection->NotifyEventCompletionMonitor(); } static void HeaderFetchCompletedEvent(void *blockingConnectionVoid, void *) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; NotifyHeaderFetchCompleted(imapConnection->GetActiveEntry()->window_id,imapConnection); } #ifdef XP_MAC // stuff used to measure performance of down loads // #define MACPERF 1 #ifdef MACPERF #include "Events.h" UInt32 g_StartTimeOfDownLoad; UInt32 g_TimeSpendInDownLoadFunction; UInt32 g_NumberOfVisits; #endif #endif /* this function ensures that the msg dislay stream is set up so that replying to a message causes the compose window to appear with the correct header info filled in and quoting works. */ #ifndef XP_OS2 static #else extern "OPTLINK" #endif char * imap_mail_generate_html_header_fn (const char* /*dest*/, void *closure, MimeHeaders *headers) { // This function is only called upon creation of a message pane. // If this is true then ce->URL_s->msg_pane will be set by // MSG_Pane::GetURL. ActiveEntry *ce = (ActiveEntry *) closure; MSG_Pane *messagepane = ce->URL_s->msg_pane; if (!messagepane && ce->window_id) messagepane = ce->window_id->imapURLPane; if (messagepane && (MSG_MESSAGEPANE == MSG_GetPaneType(messagepane))) MSG_ActivateReplyOptions (messagepane, headers); return 0; } /* this function generates the correct reference URLs for IMAP messages */ #ifndef XP_OS2 static #else extern "OPTLINK" #endif char * imap_mail_generate_reference_url_fn (const char *dest, void *closure, MimeHeaders*) { ActiveEntry *cur_entry = (ActiveEntry *) closure; char *addr = cur_entry->URL_s->address; char *search = (addr ? XP_STRRCHR (addr, '>') + 1 : 0); char *id2; char *new_dest = 0; char *result = 0; MessageKey key = MSG_MESSAGEKEYNONE; char *keyStr = 0; if (!dest || !*dest) return 0; id2 = XP_STRDUP (dest); if (!id2) return 0; if (id2[XP_STRLEN (id2)-1] == '>') id2[XP_STRLEN (id2)-1] = 0; // convert the Message-ID to a Key MSG_Pane *pane = cur_entry->window_id->imapURLPane; if (pane) { char *idInDB = id2; if (id2[0] == '<') { new_dest = NET_Escape (id2+1, URL_PATH); idInDB = id2 + 1; } else { new_dest = NET_Escape (id2, URL_PATH); } if (0 != MSG_GetKeyFromMessageId(pane, idInDB, &key) || key == MSG_MESSAGEKEYNONE) goto CLEANUP; } else { goto CLEANUP; } // now convert the message key to a string keyStr = PR_smprintf ("%ld", (long) key); result = (char *) XP_ALLOC ((search ? search - addr : 0) + (new_dest ? XP_STRLEN (new_dest) : 0) + 40); if (result && new_dest) { if (search) { XP_MEMCPY (result, addr, search - addr); result[search - addr] = 0; } else if (addr) XP_STRCPY (result, addr); else *result = 0; XP_STRCAT (result, keyStr); } CLEANUP: FREEIF (id2); FREEIF (new_dest); FREEIF (keyStr); return result; } MSG_Pane *IMAP_GetActiveEntryPane(ImapActiveEntry * imap_entry) { ActiveEntry * ce = (ActiveEntry *) imap_entry; MSG_Pane *returnPane = ce->URL_s->msg_pane; if (!returnPane) // can happen when libmime or layout reload a url returnPane = MSG_FindPaneOfContext(ce->window_id, MSG_ANYPANE); return returnPane; } NET_StreamClass *IMAP_CreateDisplayStream(ImapActiveEntry * imap_entry, XP_Bool clearCacheBit, uint32 size, const char *content_type) { ActiveEntry * ce = (ActiveEntry *) imap_entry; if (clearCacheBit) ce->format_out = CLEAR_CACHE_BIT(ce->format_out); // no need to use libnet cache if (ce->format_out == FO_PRESENT || ce->format_out == FO_CACHE_AND_PRESENT) { IMAP_InitializeImapFeData(ce); } ce->URL_s->content_length = size; StrAllocCopy(ce->URL_s->content_type, content_type); NET_StreamClass *displayStream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id); return displayStream; } int IMAP_InitializeImapFeData (ImapActiveEntry * imap_entry) { ActiveEntry * ce = (ActiveEntry *) imap_entry; MimeDisplayOptions *opt; { // make sure this is a valid imap url TIMAPUrl imapurl(ce->URL_s->address, ce->URL_s->internal_url); if (!imapurl.ValidIMAPUrl()) { XP_ASSERT(0); return -1; } } if (ce->URL_s->fe_data) { XP_ASSERT(0); return -1; } opt = XP_NEW (MimeDisplayOptions); if (!opt) return MK_OUT_OF_MEMORY; XP_MEMSET (opt, 0, sizeof(*opt)); opt->generate_reference_url_fn = imap_mail_generate_reference_url_fn; opt->generate_header_html_fn = imap_mail_generate_html_header_fn; opt->html_closure = ce; opt->missing_parts = ce->URL_s->content_modified; ce->URL_s->fe_data = opt; return 0; } extern "C" void IMAP_PseudoInterruptFetch(MWContext *context) { // OK, we're going to lie and stick the connection object in the context. TNavigatorImapConnection *imapConnection = context->imapConnection; if (imapConnection) { imapConnection->PseudoInterrupt(TRUE); } } // This function creates an output stream used to present an RFC822 message or one of its parts static void SetupMsgWriteStream(void *blockingConnectionVoid, void *StreamInfoVoid) { #ifdef MACPERF g_StartTimeOfDownLoad = TickCount(); g_TimeSpendInDownLoadFunction = 0; g_NumberOfVisits = 0; #endif PR_LOG(IMAP, out, ("STREAM: Begin Message Download Stream Event")); TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); StreamInfo *si = (StreamInfo *)StreamInfoVoid; NET_StreamClass *outputStream = NULL; TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); if ((currentUrl.GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineCopy) || (currentUrl.GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineMove)) MSG_StartImapMessageToOfflineFolderDownload(ce->window_id); if (ce->window_id->currentIMAPfolder) { outputStream = CreateIMAPDownloadMessageStream(ce, si->size, si->content_type, ce->URL_s->content_modified); } else { if (ce->format_out == FO_PRESENT || ce->format_out == FO_CACHE_AND_PRESENT) { IMAP_InitializeImapFeData(ce); } ce->URL_s->content_length = si->size; StrAllocCopy(ce->URL_s->content_type, si->content_type); outputStream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id); } if (outputStream) imapConnection->SetOutputStream(outputStream); else { // this should be impossible but be paranoid imapConnection->SetConnectionStatus(-1); imapConnection->GetServerStateParser().SetConnected(FALSE); } FREEIF(si->content_type); XP_FREE(si); imapConnection->NotifyEventCompletionMonitor(); } // report the success of the recent online copy static void OnlineCopyReport(void *blockingConnectionVoid, void *adoptedCopyStateVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ImapOnlineCopyState *copyState = (ImapOnlineCopyState *) adoptedCopyStateVoid; ReportSuccessOfOnlineCopy(imapConnection->GetActiveEntry()->window_id, *copyState); FREEIF( copyState); } static void OnlineFolderDelete(void *blockingConnectionVoid, void *adoptedmailboxNameVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; char *boxNameAdopted = (char *) adoptedmailboxNameVoid; ReportSuccessOfOnlineDelete(imapConnection->GetActiveEntry()->window_id, imapConnection->GetHostName(), boxNameAdopted); FREEIF( boxNameAdopted); } static void OnlineFolderCreateFailed(void *blockingConnectionVoid, void *serverMessageVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; char *serverMessage = (char *) serverMessageVoid; ReportFailureOfOnlineCreate(imapConnection->GetActiveEntry()->window_id, serverMessage); FREEIF( serverMessage); } static void OnlineFolderRename(void *blockingConnectionVoid, void *renameStructVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; folder_rename_struct *renameStruct = (folder_rename_struct *) renameStructVoid; ReportSuccessOfOnlineRename(imapConnection->GetActiveEntry()->window_id, renameStruct); FREEIF( renameStruct->fOldName); FREEIF( renameStruct->fNewName); FREEIF( renameStruct->fHostName); FREEIF( renameStruct); } static void MailboxDiscoveryDoneEvent(void *blockingConnectionVoid, void * /*unused*/) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ReportMailboxDiscoveryDone(imapConnection->GetActiveEntry()->window_id, imapConnection->GetActiveEntry()->URL_s); } // This function tells mail master about a discovered imap mailbox static void PossibleIMAPmailbox(void *blockingConnectionVoid, void *boxSpecVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; MSG_Master *master = NULL; MWContext *context = imapConnection->GetActiveEntry()->window_id; if (context) master = context->mailMaster; XP_Bool broadcastDiscovery = TRUE; char *url = imapConnection->GetCurrentConnectionURL(); if (url) { TIMAPUrl urlc(url, TRUE); if (urlc.GetIMAPurlType() == TIMAPUrl::kCreateFolder) broadcastDiscovery = FALSE; XP_FREE(url); } imapConnection->SetMailboxDiscoveryStatus( DiscoverIMAPMailbox((mailbox_spec *) boxSpecVoid, master, imapConnection->GetActiveEntry()->window_id, broadcastDiscovery)); imapConnection->NotifyEventCompletionMonitor(); } // tell the mail master about the newly selected mailbox static void UpdatedIMAPmailbox(void *blockingConnectionVoid, void *boxSpecVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; UpdateIMAPMailboxInfo((mailbox_spec *) boxSpecVoid, imapConnection->GetActiveEntry()->window_id); } // tell the mail master about the newly selected mailbox static void UpdatedIMAPmailboxStatus(void *blockingConnectionVoid, void *boxSpecVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; UpdateIMAPMailboxStatus((mailbox_spec *) boxSpecVoid, imapConnection->GetActiveEntry()->window_id); } struct uid_validity_info { char *canonical_boxname; const char *hostName; int32 returnValidity; }; static void GetStoredUIDValidity(void *blockingConnectionVoid, void *ValidityVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; uid_validity_info *info = (uid_validity_info *) ValidityVoid; info->returnValidity = GetUIDValidityForSpecifiedImapFolder(info->hostName, info->canonical_boxname, imapConnection->GetActiveEntry()->window_id); imapConnection->NotifyEventCompletionMonitor(); } struct msg_line_info { char *adoptedMessageLine; uint32 uidOfMessage; }; // This function adds one line to current message presentation static void ParseAdoptedMsgLine(void *blockingConnectionVoid, void *adoptedmsg_line_info_Void) { #ifdef MACPERF UInt32 startTicks = TickCount(); #endif TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); msg_line_info *downloadLineDontDelete = (msg_line_info *) adoptedmsg_line_info_Void; NET_StreamClass *outputStream = imapConnection->GetOutputStream(); unsigned int streamBytesMax = (*outputStream->is_write_ready) (outputStream); char *stringToPut = downloadLineDontDelete->adoptedMessageLine; XP_Bool allocatedString = FALSE; if ( streamBytesMax < (XP_STRLEN(downloadLineDontDelete->adoptedMessageLine) + 2)) { // dup the streamBytesMax number of chars, then put the rest of the string back into event queue if (streamBytesMax != 0) { stringToPut = (char *) XP_ALLOC(streamBytesMax + 1); // 1 for \0 if (stringToPut) { XP_MEMCPY(stringToPut, downloadLineDontDelete->adoptedMessageLine, streamBytesMax); *(stringToPut + streamBytesMax) = 0; allocatedString = TRUE; } // shift buffer bytes XP_MEMMOVE(downloadLineDontDelete->adoptedMessageLine, downloadLineDontDelete->adoptedMessageLine + streamBytesMax, (XP_STRLEN(downloadLineDontDelete->adoptedMessageLine) + 1) - streamBytesMax); } // the output stream can't handle this event yet! put it back on the // queue TImapFEEvent *parseLineEvent = new TImapFEEvent(ParseAdoptedMsgLine, // function to call blockingConnectionVoid, // access to current entry adoptedmsg_line_info_Void, FALSE); // line to display if (parseLineEvent) imapConnection->GetFEEventQueue(). AdoptEventToBeginning(parseLineEvent); else imapConnection->HandleMemoryFailure(); } if (streamBytesMax != 0) { imapConnection->SetBytesMovedSoFarForProgress(imapConnection->GetBytesMovedSoFarForProgress() + XP_STRLEN(stringToPut)); if (!imapConnection->GetPseudoInterrupted()) { int bytesPut = 0; if (ce->window_id->currentIMAPfolder) { // the definition of put_block in net.h defines // the 3rd parameter as the length of the block, // but since we are always sending a null terminated // string, I am able to sent the uid instead bytesPut = (*outputStream->put_block) (outputStream, stringToPut, downloadLineDontDelete->uidOfMessage); } else // this is a download for display { bytesPut = (*outputStream->put_block) (outputStream, stringToPut, XP_STRLEN(stringToPut)); } if (bytesPut < 0) { imapConnection->SetConnectionStatus(-1); // something bad happened, stop this url imapConnection->GetServerStateParser().SetConnected(FALSE); } } } #ifdef MACPERF g_TimeSpendInDownLoadFunction += TickCount() - startTicks; g_NumberOfVisits++; #endif if (allocatedString) XP_FREE(stringToPut); // event not done yet else if (streamBytesMax != 0) imapConnection->NotifyEventCompletionMonitor(); } // This function closes the message presentation stream static void NormalEndMsgWriteStream(void *blockingConnectionVoid, void *) { PR_LOG(IMAP, out, ("STREAM: Normal End Message Download Stream Event")); TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; NET_StreamClass *outputStream = imapConnection->GetOutputStream(); if (outputStream) { if (imapConnection->GetPseudoInterrupted()) (*outputStream->abort)(outputStream, -1); else (*outputStream->complete) (outputStream); outputStream->data_object = NULL; } ActiveEntry *ce = imapConnection->GetActiveEntry(); #ifdef MACPERF char perfMessage[500]; sprintf(perfMessage, (const char *) "Download time = %ld, mkimap4 time = %ld, for %ld visits", TickCount() - g_StartTimeOfDownLoad, g_TimeSpendInDownLoadFunction, g_NumberOfVisits); DebugStr((const unsigned char *) c2pstr(perfMessage)); #endif } // This function aborts the message presentation stream static void AbortMsgWriteStream(void *blockingConnectionVoid, void *) { PR_LOG(IMAP, out, ("STREAM: Abort Message Download Stream Event")); TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; NET_StreamClass *outputStream = imapConnection->GetOutputStream(); if (outputStream && outputStream->data_object) { (*outputStream->abort) (outputStream, -1); outputStream->data_object = NULL; } ActiveEntry *ce = imapConnection->GetActiveEntry(); #ifdef MACPERF char perfMessage[500]; sprintf(perfMessage, (const char *) "Download time = %ld, mkimap4 time = %ld, for %ld visits", TickCount() - g_StartTimeOfDownLoad, g_TimeSpendInDownLoadFunction, g_NumberOfVisits); DebugStr((const unsigned char *) c2pstr(perfMessage)); #endif } // This function displays a modal alert dialog based on // a XP_GetString of the passed id static void AlertEventFunction_UsingId(void *blockingConnectionVoid, void *errorMessageIdVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); // if this is a biff context, // don't put up the alert if (IMAP_ContextIsBiff(ce)) { imapConnection->NotifyEventCompletionMonitor(); return; } uint32 errorId = (uint32) errorMessageIdVoid; char *errorString = XP_GetString(errorId); if (errorString) #ifdef XP_UNIX FE_Alert_modal(ce->window_id, errorString); #else FE_Alert(ce->window_id, errorString); #endif imapConnection->NotifyEventCompletionMonitor(); } // This function displays a modal alert dialog static void AlertEventFunction(void *blockingConnectionVoid, void *errorMessageVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); // if this is a biff context, // don't put up the alert if (IMAP_ContextIsBiff(ce)) { imapConnection->NotifyEventCompletionMonitor(); return; } FE_Alert(ce->window_id, (const char *) errorMessageVoid); imapConnection->NotifyEventCompletionMonitor(); } // This function displays a modal alert dialog, appending the // message to "The IMAP server responded: " static void AlertEventFunctionFromServer(void *blockingConnectionVoid, void *serverSaidVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); // if this is a biff context, // don't put up the alert if (IMAP_ContextIsBiff(ce)) { imapConnection->NotifyEventCompletionMonitor(); return; } const char *serverSaid = (const char *) serverSaidVoid; if (serverSaid) { char *whereRealMessage = XP_STRCHR(serverSaid, ' '); if (whereRealMessage) whereRealMessage++; if (whereRealMessage) whereRealMessage = XP_STRCHR(whereRealMessage, ' '); if (whereRealMessage) whereRealMessage++; char *message = PR_smprintf(XP_GetString(MK_MSG_IMAP_SERVER_SAID), whereRealMessage ? whereRealMessage : serverSaid); if (message) FE_Alert(ce->window_id, message); FREEIF(message); } imapConnection->NotifyEventCompletionMonitor(); } // until preferences are worked out, save a copy of the password here static char *gIMAPpassword=NULL; // gross static XP_Bool preAuthSucceeded = FALSE; const char * IMAP_GetPassword() { return gIMAPpassword; } void IMAP_SetPassword(const char *password) { FREEIF(gIMAPpassword); if (password) gIMAPpassword = XP_STRDUP(password); } void IMAP_SetPasswordForHost(const char *host, const char *password) { TIMAPHostInfo::SetPasswordForHost(host, password); } void IMAP_ResetAnyCachedConnectionInfo() { if (gIMAPpassword) { FREEIF(gIMAPpassword); gIMAPpassword = NULL; } preAuthSucceeded = FALSE; TNavigatorImapConnection::ResetCachedConnectionInfo(); } // This function displays a modal alert dialog static void GetPasswordEventFunction(void *blockingConnectionVoid, void *userNameVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); #if 1 char* fmt = XP_GetString( XP_PASSWORD_FOR_POP3_USER ); if (fmt) { const char *host = imapConnection->GetHostName(); size_t len = (XP_STRLEN(fmt) + (host ? XP_STRLEN(host) : 0) + 300) * sizeof(char); char *prompt = (char*)XP_ALLOC(len); if (prompt) { PR_snprintf(prompt, len, fmt, (char*)userNameVoid, host); const char* password = FE_PromptPassword(ce->window_id, prompt); if (password != NULL) TIMAPHostInfo::SetPasswordForHost(host, password); XP_FREE(prompt); } } #else char *prompt = NULL; char *promptText = XP_GetString(XP_PROMPT_ENTER_PASSWORD); if (promptText) prompt = PR_smprintf(promptText, (char *) userNameVoid); if (prompt) { const char *password = FE_PromptPassword(ce->window_id, prompt); if (password != NULL) TIMAPHostInfo::SetPasswordForHost(imapConnection->GetHostName(), password); XP_FREE(prompt); } #endif imapConnection->NotifyEventCompletionMonitor(); } // This function alerts the FE that we are past the password check // (The WinFE needs this for progress updates) static void PastPasswordCheckFunction(void *blockingConnectionVoid, void* /*unused*/) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); if (ce->window_id->imapURLPane != NULL && MSG_GetPaneType(ce->window_id->imapURLPane) != MSG_COMPOSITIONPANE) FE_PaneChanged (ce->window_id->imapURLPane, FALSE, MSG_PanePastPasswordCheck, 0); imapConnection->NotifyEventCompletionMonitor(); } // This function updates the progress message at the bottom of the // window static void ProgressEventFunction(void *blockingConnectionVoid, void *progressMessageVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); NET_Progress(ce->window_id, (char *)progressMessageVoid); imapConnection->NotifyEventCompletionMonitor(); } // This function updates the progress message at the bottom of the // window based on a XP_GetString of the passed id static void ProgressStatusFunction_UsingId(void *blockingConnectionVoid, void *errorMessageIdVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); uint32 errorId = (uint32) errorMessageIdVoid; char *errorString = XP_GetString(errorId); if (errorString) NET_Progress(ce->window_id, errorString); imapConnection->NotifyEventCompletionMonitor(); } // This function updates the progress message at the bottom of the // window based on a XP_GetString of the passed id and formats // it with the passed in string static void ProgressStatusFunction_UsingIdWithString(void *blockingConnectionVoid, void *errorMessageStruct) { char * progressString = NULL; TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); StatusMessageInfo * msgInfo = (StatusMessageInfo *) errorMessageStruct; if (msgInfo) progressString = PR_sprintf_append(progressString, XP_GetString(msgInfo->msgID), msgInfo->extraInfo); if (progressString) NET_Progress(ce->window_id, progressString); FREEIF(msgInfo->extraInfo); FREEIF(msgInfo); FREEIF(progressString); imapConnection->NotifyEventCompletionMonitor(); } // This function updates the progress message at the bottom of the // window, along with setting the percent bar static void PercentProgressEventFunction(void *blockingConnectionVoid, void *progressVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); ProgressInfo *progress = (ProgressInfo *)progressVoid; if (progress && progress->message) NET_Progress(ce->window_id, progress->message); FE_SetProgressBarPercent(ce->window_id, progress->percent); FREEIF(progress->message); FREEIF(progress); imapConnection->NotifyEventCompletionMonitor(); } // This function uses the existing NET code to establish // a connection with the IMAP server. static void StartIMAPConnection(TNavigatorImapConnection *imapConnection) { ActiveEntry *ce = imapConnection->GetActiveEntry(); FE_SetProgressBarPercent(ce->window_id, -1); HG83344 ce->status = NET_BeginConnect(ce->URL_s->address, ce->URL_s->IPAddressString, "IMAP4", (HG26227 IMAP4_PORT), &ce->socket, HG27327, imapConnection->GetTCPConData(), ce->window_id, &ce->URL_s->error_msg, ce->socks_host, ce->socks_port, ce->URL_s->localIP); imapConnection->SetIOSocket(ce->socket); if(ce->socket != 0) NET_TotalNumberOfOpenConnections++; if(ce->status == MK_CONNECTED) { imapConnection->SetNeedsGreeting(TRUE); NET_SetReadSelect(ce->window_id, ce->socket); } else if(ce->status > -1) { ce->con_sock = ce->socket; /* set con sock so we can select on it */ NET_SetConnectSelect(ce->window_id, ce->con_sock); } else if(ce->status < 0) { /* close and clear the socket here * so that we don't try and send a RSET */ if(ce->socket != 0) { NET_TotalNumberOfOpenConnections--; NET_ClearConnectSelect(ce->window_id, ce->socket); TRACEMSG(("Closing and clearing socket ce->socket: %d", ce->socket)); net_graceful_shutdown(ce->socket, HG38373); PR_Close(ce->socket); ce->socket = NULL; imapConnection->SetIOSocket(ce->socket); } } imapConnection->SetConnectionStatus(ce->status); } // This function uses the existing NET code to establish // a connection with the IMAP server. static void FinishIMAPConnection(void *blockingConnectionVoid, void* /*unused*/) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); HG83344 ce->status = NET_FinishConnect(ce->URL_s->address, "IMAP4", (HG72524 IMAP4_PORT), &ce->socket, imapConnection->GetTCPConData(), ce->window_id, &ce->URL_s->error_msg, ce->URL_s->localIP); imapConnection->SetIOSocket(ce->socket); if(ce->status == MK_CONNECTED) { NET_ClearConnectSelect(ce->window_id, ce->con_sock); NET_SetReadSelect(ce->window_id, ce->socket); } else if(ce->status > -1) { /* unregister the old CE_SOCK from the select list * and register the new value in the case that it changes */ if(ce->con_sock != ce->socket) { NET_ClearConnectSelect(ce->window_id, ce->con_sock); ce->con_sock = ce->socket; NET_SetConnectSelect(ce->window_id, ce->con_sock); } } else if(ce->status < 0) { /* close and clear the socket here * so that we don't try and send a RSET */ NET_TotalNumberOfOpenConnections--; NET_ClearConnectSelect(ce->window_id, ce->socket); TRACEMSG(("Closing and clearing socket ce->socket: %d", ce->socket)); net_graceful_shutdown(ce->socket, HG73654); PR_Close(ce->socket); ce->socket = NULL; imapConnection->SetIOSocket(ce->socket); } imapConnection->SetConnectionStatus(ce->status); imapConnection->NotifyEventCompletionMonitor(); } // member functions of the TImapFEEvent class TImapFEEvent::TImapFEEvent(FEEventFunctionPointer *function, void *arg1, void*arg2, XP_Bool executeWhenInterrupted) : fEventFunction(function), fArg1(arg1), fArg2(arg2), fExecuteWhenInterrupted(executeWhenInterrupted) { } TImapFEEvent::~TImapFEEvent() { } void TImapFEEvent::DoEvent() { (*fEventFunction) (fArg1, fArg2); } // member functions of the TImapFEEventQueue class TImapFEEventQueue::TImapFEEventQueue() { fEventList = XP_ListNew(); fListMonitor = PR_NewNamedMonitor("list-monitor"); fNumberOfEvents = 0; } TImapFEEventQueue *TImapFEEventQueue::CreateFEEventQueue() { TImapFEEventQueue *returnQueue = new TImapFEEventQueue; if (!returnQueue || !returnQueue->fEventList || !returnQueue->fListMonitor) { delete returnQueue; returnQueue = nil; } return returnQueue; } TImapFEEventQueue::~TImapFEEventQueue() { if (fEventList) XP_ListDestroy(fEventList); if (fListMonitor) PR_DestroyMonitor(fListMonitor); } void TImapFEEventQueue::AdoptEventToEnd(TImapFEEvent *event) { PR_EnterMonitor(fListMonitor); XP_ListAddObjectToEnd(fEventList, event); fNumberOfEvents++; PR_ExitMonitor(fListMonitor); } void TImapFEEventQueue::AdoptEventToBeginning(TImapFEEvent *event) { PR_EnterMonitor(fListMonitor); XP_ListAddObject(fEventList, event); fNumberOfEvents++; PR_ExitMonitor(fListMonitor); } TImapFEEvent* TImapFEEventQueue::OrphanFirstEvent() { TImapFEEvent *returnEvent = nil; PR_EnterMonitor(fListMonitor); if (fNumberOfEvents) { fNumberOfEvents--; if (fNumberOfEvents == 0) PR_Notify(fListMonitor); returnEvent = (TImapFEEvent *) XP_ListRemoveTopObject(fEventList); } PR_ExitMonitor(fListMonitor); return returnEvent; } int TImapFEEventQueue::NumberOfEventsInQueue() { int returnEvents; PR_EnterMonitor(fListMonitor); returnEvents = fNumberOfEvents; PR_ExitMonitor(fListMonitor); return returnEvents; } TIMAP4BlockingConnection::TIMAP4BlockingConnection() : fConnectionStatus(0), // we want NET_ProcessIMAP4 to continue for now fServerState(*this), fBlockedForIO(FALSE), fCloseNeededBeforeSelect(FALSE), fCurrentServerCommandTagNumber(0) { fDataMemberMonitor = PR_NewNamedMonitor("data-member-monitor"); fIOMonitor = PR_NewNamedMonitor("io-monitor"); fDeleteModelIsMoveToTrash = FALSE; } XP_Bool TIMAP4BlockingConnection::ConstructionSuccess() { return (fDataMemberMonitor && fIOMonitor); } TIMAP4BlockingConnection::~TIMAP4BlockingConnection() { if (fDataMemberMonitor) PR_DestroyMonitor(fDataMemberMonitor); if (fIOMonitor) PR_DestroyMonitor(fIOMonitor); } TNavigatorImapConnection *TIMAP4BlockingConnection::GetNavigatorConnection() { return NULL; } void TIMAP4BlockingConnection::NotifyIOCompletionMonitor() { PR_EnterMonitor(fIOMonitor); fBlockedForIO = FALSE; PR_Notify(fIOMonitor); PR_ExitMonitor(fIOMonitor); } // escape any backslashes or quotes. Backslashes are used a lot with our NT server char *TIMAP4BlockingConnection::CreateEscapedMailboxName(const char *rawName) { int numberOfEscapes = 0; char *escapedName = (char *) XP_ALLOC(XP_STRLEN(rawName) + 20); // enough for 20 deep if (escapedName) { char *currentChar = escapedName; XP_STRCPY(escapedName,rawName); while (*currentChar) { if ((*currentChar == '\\') || (*currentChar == '\"')) { XP_MEMMOVE(currentChar+1, // destination currentChar, // source XP_STRLEN(currentChar) + 1); // remaining string + nil *currentChar++ = '\\'; if (++numberOfEscapes == 20) { char *oldString = escapedName; escapedName = (char *) XP_ALLOC(XP_STRLEN(oldString) + 20); // enough for 20 deep XP_STRCPY(escapedName,oldString); currentChar = escapedName + (currentChar - oldString); FREEIF( oldString); numberOfEscapes = 0; } } currentChar++; } } return escapedName; } void TIMAP4BlockingConnection::IncrementCommandTagNumber() { sprintf(fCurrentServerCommandTag,"%ld", (long) ++fCurrentServerCommandTagNumber); } #ifdef KMCENTEE_DEBUG int WaitForIOLoopCount; int WaitForFEEventLoopCount; #endif void TIMAP4BlockingConnection::WaitForIOCompletion() { PRIntervalTime sleepTime = PR_MicrosecondsToInterval(kImapSleepTime); PR_EnterMonitor(fIOMonitor); fBlockedForIO = TRUE; #ifdef KMCENTEE_DEBUG WaitForIOLoopCount=0; #endif while (fBlockedForIO && !DeathSignalReceived() ) { PR_Wait(fIOMonitor, sleepTime); #ifdef KMCENTEE_DEBUG WaitForIOLoopCount++; if (WaitForIOLoopCount == 10) XP_ASSERT(FALSE); #endif } PR_ExitMonitor(fIOMonitor); } XP_Bool TIMAP4BlockingConnection::BlockedForIO() { XP_Bool returnValue; PR_EnterMonitor(fIOMonitor); returnValue = fBlockedForIO; PR_ExitMonitor(fIOMonitor); return returnValue; } TImapServerState &TIMAP4BlockingConnection::GetServerStateParser() { return fServerState; } void TIMAP4BlockingConnection::SetConnectionStatus(int status) { PR_EnterMonitor(GetDataMemberMonitor()); fConnectionStatus = status; PR_ExitMonitor(GetDataMemberMonitor()); } int TIMAP4BlockingConnection::GetConnectionStatus() { int returnStatus; PR_EnterMonitor(GetDataMemberMonitor()); returnStatus = fConnectionStatus; PR_ExitMonitor(GetDataMemberMonitor()); return returnStatus; } extern "C" void MSG_IMAP_KillConnection(TNavigatorImapConnection *imapConnection) { if (imapConnection) { imapConnection->DeathSignalReceived(); // stop anything going on and kill thread //delete imapConnection; // destroy anything else, not mine to destroy. } } void TNavigatorImapConnection::SelectMailbox(const char *mailboxName) { TIMAP4BlockingConnection::SelectMailbox(mailboxName); int32 numOfMessagesInFlagState = fFlagState->GetNumberOfMessages(); TIMAPUrl::EIMAPurlType urlType = fCurrentUrl->GetIMAPurlType(); // if we've selected a mailbox, and we're not going to do an update because of the // url type, but don't have the flags, go get them! if (urlType != TIMAPUrl::kSelectFolder && urlType != TIMAPUrl::kExpungeFolder && urlType != TIMAPUrl::kLiteSelectFolder && urlType != TIMAPUrl::kDeleteAllMsgs && ((GetServerStateParser().NumberOfMessages() != numOfMessagesInFlagState) && (numOfMessagesInFlagState == 0))) ProcessMailboxUpdate(FALSE); } // authenticated state commands void TIMAP4BlockingConnection::SelectMailbox(const char *mailboxName) { ProgressEventFunction_UsingId (MK_IMAP_STATUS_SELECTING_MAILBOX); IncrementCommandTagNumber(); fCloseNeededBeforeSelect = FALSE; // initial value GetServerStateParser().ResetFlagInfo(0); char *escapedName = CreateEscapedMailboxName(mailboxName); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s select \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedName); FREEIF( escapedName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::CreateMailbox(const char *mailboxName) { ProgressEventFunction_UsingId (MK_IMAP_STATUS_CREATING_MAILBOX); IncrementCommandTagNumber(); char *escapedName = CreateEscapedMailboxName(mailboxName); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s create \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedName); FREEIF( escapedName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } static void setup_message_flags_string(char *flagString, imapMessageFlagsType flags, XP_Bool userDefined) { XP_ASSERT(flagString); flagString[0] = '\0'; if (flags & kImapMsgSeenFlag) XP_STRCAT(flagString, "\\Seen "); if (flags & kImapMsgAnsweredFlag) XP_STRCAT(flagString, "\\Answered "); if (flags & kImapMsgFlaggedFlag) XP_STRCAT(flagString, "\\Flagged "); if (flags & kImapMsgDeletedFlag) XP_STRCAT(flagString, "\\Deleted "); if (flags & kImapMsgDraftFlag) XP_STRCAT(flagString, "\\Draft "); if (flags & kImapMsgRecentFlag) XP_STRCAT(flagString, "\\Recent "); if ((flags & kImapMsgForwardedFlag) && userDefined) XP_STRCAT(flagString, "Forwarded "); // Not always available if ((flags & kImapMsgMDNSentFlag) && userDefined) XP_STRCAT(flagString, "MDNSent "); // Not always available // eat the last space if (*flagString) *(flagString + XP_STRLEN(flagString) - 1) = '\0'; } void TIMAP4BlockingConnection::AppendMessage(const char *mailboxName, const char *messageSizeString, imapMessageFlagsType flags) { //ProgressUpdateEvent("Appending a message..."); IncrementCommandTagNumber(); char *escapedName = CreateEscapedMailboxName(mailboxName); // enough room for "\\Seen \\Answered \\Flagged \\Deleted \\Draft \\Recent \\Replied" char flagString[90]; setup_message_flags_string(flagString, flags, SupportsUserDefinedFlags()); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s append \"%s\" (%s) {%s}" CRLF, // format string GetServerCommandTag(), // command tag escapedName, flagString, messageSizeString); FREEIF( escapedName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); if (GetServerStateParser().GetIMAPstate() == TImapServerState::kWaitingForMoreClientInput) { OnlineCopyCompleted(kReadyForAppendData); UploadMessage(); ParseIMAPandCheckForNewMail(); if (GetServerStateParser().LastCommandSuccessful()) OnlineCopyCompleted(kInProgress); else OnlineCopyCompleted(kFailedAppend); } } void TIMAP4BlockingConnection::DeleteMailbox(const char *mailboxName) { ProgressEventFunction_UsingIdWithString (MK_IMAP_STATUS_DELETING_MAILBOX, mailboxName); IncrementCommandTagNumber(); char *escapedName = CreateEscapedMailboxName(mailboxName); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s delete \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedName); FREEIF( escapedName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::RenameMailbox(const char *existingName, const char *newName) { ProgressEventFunction_UsingIdWithString (MK_IMAP_STATUS_RENAMING_MAILBOX, existingName); IncrementCommandTagNumber(); char *escapedExistingName = CreateEscapedMailboxName(existingName); char *escapedNewName = CreateEscapedMailboxName(newName); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s rename \"%s\" \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedExistingName, escapedNewName); FREEIF( escapedExistingName); FREEIF( escapedNewName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } int64 TIMAP4BlockingConnection::fgTimeStampOfNonPipelinedList = LL_ZERO; void TIMAP4BlockingConnection::TimeStampListNow() { fgTimeStampOfNonPipelinedList = PR_Now(); } extern "C" { int64 IMAP_GetTimeStampOfNonPipelinedList() { return TIMAP4BlockingConnection::GetTimeStampOfNonPipelinedList(); } } void TIMAP4BlockingConnection::List(const char *mailboxPattern, XP_Bool pipeLined /* = FALSE */, XP_Bool dontTimeStamp /* = FALSE */) { ProgressEventFunction_UsingId (MK_IMAP_STATUS_LOOKING_FOR_MAILBOX); IncrementCommandTagNumber(); char *escapedPattern = CreateEscapedMailboxName(mailboxPattern); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s list \"\" \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedPattern); FREEIF( escapedPattern); int ioStatus = WriteLineToSocket(GetOutputBuffer()); if (pipeLined) GetServerStateParser().IncrementNumberOfTaggedResponsesExpected(GetServerCommandTag()); else { if (!dontTimeStamp) TimeStampListNow(); ParseIMAPandCheckForNewMail(); } } void TIMAP4BlockingConnection::Lsub(const char *mailboxPattern, XP_Bool pipelined) { ProgressEventFunction_UsingId (MK_IMAP_STATUS_LOOKING_FOR_MAILBOX); IncrementCommandTagNumber(); char *escapedPattern = CreateEscapedMailboxName(mailboxPattern); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s lsub \"\" \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedPattern); FREEIF( escapedPattern); int ioStatus = WriteLineToSocket(GetOutputBuffer()); if (pipelined) GetServerStateParser().IncrementNumberOfTaggedResponsesExpected(GetServerCommandTag()); else { TimeStampListNow(); ParseIMAPandCheckForNewMail(); } } void TIMAP4BlockingConnection::Subscribe(const char *mailboxName) { ProgressEventFunction_UsingIdWithString (MK_IMAP_STATUS_SUBSCRIBE_TO_MAILBOX, mailboxName); IncrementCommandTagNumber(); char *escapedName = CreateEscapedMailboxName(mailboxName); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s subscribe \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedName); FREEIF( escapedName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::Unsubscribe(const char *mailboxName) { ProgressEventFunction_UsingIdWithString (MK_IMAP_STATUS_UNSUBSCRIBE_MAILBOX, mailboxName); IncrementCommandTagNumber(); char *escapedName = CreateEscapedMailboxName(mailboxName); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s unsubscribe \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedName); FREEIF( escapedName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::Search(const char *searchCriteria, XP_Bool useUID, XP_Bool notifyHit /* TRUE */) { fNotifyHit = notifyHit; ProgressEventFunction_UsingId (MK_IMAP_STATUS_SEARCH_MAILBOX); IncrementCommandTagNumber(); char *formatString; // the searchCriteria string contains the 'search ....' string if (useUID) formatString = "%s uid %s\015\012"; else formatString = "%s %s\015\012"; PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size formatString, // format string GetServerCommandTag(), // command tag searchCriteria); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::Copy(const char *messageList, const char *destinationMailbox, XP_Bool idsAreUid) { IncrementCommandTagNumber(); char *escapedDestination = CreateEscapedMailboxName(destinationMailbox); char *formatString; if (idsAreUid) formatString = "%s uid copy %s \"%s\"\015\012"; else formatString = "%s copy %s \"%s\"\015\012"; // since messageIds can be infinitely long, use a dynamic buffer rather than the fixed one const char *commandTag = GetServerCommandTag(); int protocolStringSize = XP_STRLEN(formatString) + XP_STRLEN(messageList) + XP_STRLEN(commandTag) + XP_STRLEN(escapedDestination) + 1; char *protocolString = (char *) XP_ALLOC( protocolStringSize ); if (protocolString) { PR_snprintf(protocolString, // string to create protocolStringSize, // max size formatString, // format string commandTag, // command tag messageList, escapedDestination); int ioStatus = WriteLineToSocket(protocolString); ParseIMAPandCheckForNewMail(protocolString); XP_FREE(protocolString); } else HandleMemoryFailure(); FREEIF( escapedDestination); } void TIMAP4BlockingConnection::Store(const char *messageList, const char *messageData, XP_Bool idsAreUid) { //ProgressUpdateEvent("Saving server message flags..."); IncrementCommandTagNumber(); char *formatString; if (idsAreUid) formatString = "%s uid store %s %s\015\012"; else formatString = "%s store %s %s\015\012"; // we might need to close this mailbox after this fCloseNeededBeforeSelect = fDeleteModelIsMoveToTrash && (XP_STRCASESTR(messageData, "\\Deleted")); const char *commandTag = GetServerCommandTag(); int protocolStringSize = XP_STRLEN(formatString) + XP_STRLEN(messageList) + XP_STRLEN(messageData) + XP_STRLEN(commandTag) + 1; char *protocolString = (char *) XP_ALLOC( protocolStringSize ); if (protocolString) { PR_snprintf(protocolString, // string to create protocolStringSize, // max size formatString, // format string commandTag, // command tag messageList, messageData); int ioStatus = WriteLineToSocket(protocolString); ParseIMAPandCheckForNewMail(protocolString); XP_FREE(protocolString); } else HandleMemoryFailure(); } void TIMAP4BlockingConnection::Close() { ProgressEventFunction_UsingId (MK_IMAP_STATUS_CLOSE_MAILBOX); IncrementCommandTagNumber(); GetServerStateParser().ResetFlagInfo(0); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s close" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::Check() { //ProgressUpdateEvent("Checking mailbox..."); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s check" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::Expunge() { ProgressEventFunction_UsingId (MK_IMAP_STATUS_EXPUNGING_MAILBOX); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s expunge" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } // any state commands void TIMAP4BlockingConnection::Logout() { ProgressEventFunction_UsingId (MK_IMAP_STATUS_LOGGING_OUT); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s logout" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); // the socket may be dead before we read the response, so drop it. ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::Noop() { //ProgressUpdateEvent("noop..."); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s noop" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::Capability() { ProgressEventFunction_UsingId (MK_IMAP_STATUS_CHECK_COMPAT); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s capability" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::XServerInfo() { ProgressEventFunction_UsingId (MK_IMAP_GETTING_SERVER_INFO); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s XSERVERINFO MANAGEACCOUNTURL MANAGELISTSURL MANAGEFILTERSURL" CRLF, GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::XMailboxInfo(const char *mailboxName) { ProgressEventFunction_UsingId (MK_IMAP_GETTING_MAILBOX_INFO); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s XMAILBOXINFO %s MANAGEURL POSTURL" CRLF, GetServerCommandTag(), mailboxName); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::Namespace() { ProgressEventFunction_UsingId (MK_IMAP_STATUS_GETTING_NAMESPACE); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s namespace" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::MailboxData() { IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s mailboxdata" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::GetMyRightsForFolder(const char *mailboxName) { IncrementCommandTagNumber(); char *escapedName = CreateEscapedMailboxName(mailboxName); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s myrights \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedName); FREEIF( escapedName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::GetACLForFolder(const char *mailboxName) { IncrementCommandTagNumber(); char *escapedName = CreateEscapedMailboxName(mailboxName); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s getacl \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag escapedName); FREEIF( escapedName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::StatusForFolder(const char *mailboxName) { IncrementCommandTagNumber(); char *escapedName = CreateEscapedMailboxName(mailboxName); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s STATUS \"%s\" (UIDNEXT MESSAGES UNSEEN)" CRLF, // format string GetServerCommandTag(), // command tag escapedName); FREEIF( escapedName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TNavigatorImapConnection::StatusForFolder(const char *mailboxName) { TIMAP4BlockingConnection::StatusForFolder(mailboxName); mailbox_spec *new_spec = GetServerStateParser().CreateCurrentMailboxSpec(mailboxName); if (new_spec) UpdateMailboxStatus(new_spec); } void TIMAP4BlockingConnection::Netscape() { //ProgressUpdateEvent("Netscape..."); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s netscape" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::InsecureLogin(const char *userName, const char *password) { ProgressEventFunction_UsingId (MK_IMAP_STATUS_SENDING_LOGIN); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s login \"%s\" \"%s\"" CRLF, // format string GetServerCommandTag(), // command tag userName, password); int ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); } void TIMAP4BlockingConnection::AuthLogin(const char *userName, const char *password) { ProgressEventFunction_UsingId (MK_IMAP_STATUS_SENDING_AUTH_LOGIN); IncrementCommandTagNumber(); const char *currentTagToken = GetServerCommandTag(); char *currentCommand = NULL; PR_snprintf(GetOutputBuffer(), // string to create kOutputBufferSize, // max size "%s authenticate login" CRLF, // format string GetServerCommandTag()); // command tag int ioStatus = WriteLineToSocket(GetOutputBuffer()); StrAllocCopy(currentCommand, GetOutputBuffer()); ParseIMAPandCheckForNewMail(); if (GetServerStateParser().LastCommandSuccessful()) { char *base64Str = NET_Base64Encode((char*)userName, XP_STRLEN(userName)); PR_snprintf(GetOutputBuffer(), kOutputBufferSize, "%s" CRLF, base64Str); XP_FREEIF(base64Str); ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(currentCommand); if (GetServerStateParser().LastCommandSuccessful()) { char *base64Str = NET_Base64Encode((char*)password, XP_STRLEN(password)); PR_snprintf(GetOutputBuffer(), kOutputBufferSize, "%s" CRLF, base64Str); XP_FREEIF(base64Str); ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(currentCommand); if (GetServerStateParser().LastCommandSuccessful()) { FREEIF(currentCommand); return; } } } FREEIF(currentCommand); // fall back to use InsecureLogin() InsecureLogin(userName, password); } void TIMAP4BlockingConnection::ProcessBIFFRequest() { if (GetConnectionStatus() == MK_WAITING_FOR_CONNECTION) EstablishServerConnection(); if (GetConnectionStatus() >= 0) SetConnectionStatus(-1); // stub! } char *TIMAP4BlockingConnection::GetOutputBuffer() { return fOutputBuffer; } char *TIMAP4BlockingConnection::GetServerCommandTag() { return fCurrentServerCommandTag; } PRMonitor *TIMAP4BlockingConnection::GetDataMemberMonitor() { return fDataMemberMonitor; } XP_Bool TIMAP4BlockingConnection::HandlingMultipleMessages(char *messageIdString) { return (XP_STRCHR(messageIdString,',') != NULL || XP_STRCHR(messageIdString,':') != NULL); } /******************* TNavigatorImapConnection *******************************/ void TNavigatorImapConnection::StartProcessingActiveEntries() { XP_Bool receivedNullEntry = FALSE; while (!receivedNullEntry && !DeathSignalReceived()) { WaitForNextActiveEntry(); if (!DeathSignalReceived()) { // while I'm processing this URL nobody can change it! PR_EnterMonitor(fActiveEntryMonitor); if (fCurrentEntry) { XP_Bool killedEarly = FALSE; // fCurrentUrl is accessed repeatedly by CurrentConnectionIsMove // CurrentConnectionIsMove is called from Mozilla thread LIBNET_LOCK(); if (!DeathSignalReceived()) { delete fCurrentUrl; fCurrentUrl = new TIMAPUrl(fCurrentEntry->URL_s->address, fCurrentEntry->URL_s->internal_url); } else killedEarly = TRUE; LIBNET_UNLOCK(); if (!fCurrentUrl && !killedEarly) HandleMemoryFailure(); else if (!killedEarly) { // process the url ProcessCurrentURL(); // wait for the event queue to empty before we trash // fCurrentEntry. Some of the events reference it. WaitForEventQueueEmptySignal(); } // this URL is finished so delete it in case // it is referenced before we create the new one // the next time through this loop PR_EnterMonitor(GetDataMemberMonitor()); delete fCurrentUrl; fCurrentUrl = NULL; PR_ExitMonitor(GetDataMemberMonitor()); fCurrentEntry = NULL; } else { receivedNullEntry = TRUE; if (GetIOSocket() != 0) Logout(); } PR_ExitMonitor(fActiveEntryMonitor); } } PRIntervalTime sleepTime = PR_MicrosecondsToInterval(kImapSleepTime); PR_EnterMonitor(fPermissionToDieMonitor); while (!fIsSafeToDie) PR_Wait(fPermissionToDieMonitor, sleepTime); PR_ExitMonitor(fPermissionToDieMonitor); } XP_Bool TNavigatorImapConnection::BlockedWaitingForActiveEntry() { return !PR_InMonitor(fActiveEntryMonitor); } char *TNavigatorImapConnection::GetCurrentConnectionURL() { PR_EnterMonitor(GetDataMemberMonitor()); char *rv = fCurrentEntry->URL_s->address ? XP_STRDUP(fCurrentEntry->URL_s->address) : (char *)NULL; PR_ExitMonitor(GetDataMemberMonitor()); return rv; } void TNavigatorImapConnection::WaitForEventQueueEmptySignal() { // notice that this Wait function is not like the others in that // it does not wake up and check DeathSignalReceived() // sometimes it is run after DeathSignalReceived and the thread // has to drain its final fe events before death. PRIntervalTime sleepTime = kImapSleepTime; PR_EnterMonitor(fEventQueueEmptySignalMonitor); while(!fEventQueueEmptySignalHappened) PR_Wait(fEventQueueEmptySignalMonitor, sleepTime); fEventQueueEmptySignalHappened = FALSE; PR_ExitMonitor(fEventQueueEmptySignalMonitor); } void TNavigatorImapConnection::SignalEventQueueEmpty() { PR_EnterMonitor(fEventQueueEmptySignalMonitor); fEventQueueEmptySignalHappened = TRUE; PR_Notify(fEventQueueEmptySignalMonitor); PR_ExitMonitor(fEventQueueEmptySignalMonitor); } void TNavigatorImapConnection::WaitForNextAppendMessageSize() { PRIntervalTime sleepTime = kImapSleepTime; PR_EnterMonitor(fMsgCopyDataMonitor); while(!fMsgAppendSizeIsNew && !DeathSignalReceived()) PR_Wait(fMsgCopyDataMonitor, sleepTime); PR_ExitMonitor(fMsgCopyDataMonitor); } void TNavigatorImapConnection::WaitForPotentialListOfMsgsToFetch(uint32 **msgIdList, uint32 &msgCount) { PRIntervalTime sleepTime = kImapSleepTime; PR_EnterMonitor(fMsgCopyDataMonitor); while(!fFetchMsgListIsNew && !DeathSignalReceived()) PR_Wait(fMsgCopyDataMonitor, sleepTime); fFetchMsgListIsNew = FALSE; *msgIdList = fFetchMsgIdList; msgCount = fFetchCount; PR_ExitMonitor(fMsgCopyDataMonitor); } void TNavigatorImapConnection::NotifyKeyList(uint32 *keys, uint32 keyCount) { PR_EnterMonitor(fMsgCopyDataMonitor); fFetchMsgIdList = keys; fFetchCount = keyCount; fFetchMsgListIsNew = TRUE; PR_Notify(fMsgCopyDataMonitor); PR_ExitMonitor(fMsgCopyDataMonitor); } void TNavigatorImapConnection::WaitForMessageUploadToComplete() { PRIntervalTime sleepTime = kImapSleepTime; PR_EnterMonitor(fMessageUploadMonitor); while(!fMessageUploadCompleted && !DeathSignalReceived()) PR_Wait(fMessageUploadMonitor, sleepTime); fMessageUploadCompleted = FALSE; PR_ExitMonitor(fMessageUploadMonitor); } void TNavigatorImapConnection::NotifyMessageUploadMonitor() { PR_EnterMonitor(fMessageUploadMonitor); fMessageUploadCompleted = TRUE; PR_Notify(fMessageUploadMonitor); PR_ExitMonitor(fMessageUploadMonitor); } void TNavigatorImapConnection::NotifyAppendSize(uint32 msgSize, imapMessageFlagsType flags) { PR_EnterMonitor(fMsgCopyDataMonitor); fAppendMessageSize = msgSize; fAppendMsgFlags = flags & ~kImapMsgRecentFlag; // don't let recent flag through, since we can't store it. fMsgAppendSizeIsNew = TRUE; PR_Notify(fMsgCopyDataMonitor); PR_ExitMonitor(fMsgCopyDataMonitor); } imapMessageFlagsType TNavigatorImapConnection::GetAppendFlags() { imapMessageFlagsType returnValue; PR_EnterMonitor(fMsgCopyDataMonitor); returnValue = fAppendMsgFlags; PR_ExitMonitor(fMsgCopyDataMonitor); return returnValue; } uint32 TNavigatorImapConnection::GetAppendSize() { uint32 returnValue; PR_EnterMonitor(fMsgCopyDataMonitor); returnValue = fAppendMessageSize; fMsgAppendSizeIsNew = FALSE; PR_ExitMonitor(fMsgCopyDataMonitor); return returnValue; } void TNavigatorImapConnection::SetFolderInfo(MSG_FolderInfo *folder, XP_Bool folderNeedsSubscribing, XP_Bool folderNeedsACLRefreshed) { PR_EnterMonitor(GetDataMemberMonitor()); fFolderInfo = folder; fFolderNeedsSubscribing = folderNeedsSubscribing; fFolderNeedsACLRefreshed = folderNeedsACLRefreshed; PR_ExitMonitor(GetDataMemberMonitor()); } void TNavigatorImapConnection::WaitForNextActiveEntry() { PRIntervalTime sleepTime = kImapSleepTime; PR_EnterMonitor(fActiveEntryMonitor); while(!fNextEntryEventSignalHappened && !DeathSignalReceived()) PR_Wait(fActiveEntryMonitor, sleepTime); fNextEntryEventSignalHappened = FALSE; PR_ExitMonitor(fActiveEntryMonitor); } void TNavigatorImapConnection::SubmitActiveEntry(ActiveEntry * ce, XP_Bool newConnection) { PR_EnterMonitor(fActiveEntryMonitor); fCurrentEntry = ce; if (newConnection) StartIMAPConnection(this); else SetConnectionStatus(0); // keep netlib running, connection is reused fNextEntryEventSignalHappened = TRUE; PR_Notify(fActiveEntryMonitor); PR_ExitMonitor(fActiveEntryMonitor); } XPPtrArray *TNavigatorImapConnection::connectionList; TNavigatorImapConnection::TNavigatorImapConnection(const char *hostName) : TIMAP4BlockingConnection(), fCurrentEntry(nil), fFEeventCompleted(FALSE), fTunnelCompleted(FALSE), fInputBufferSize(0), fInputSocketBuffer(nil), fBlockingThread(nil), fTCPConData(nil), fIOSocket(NULL), fCurrentUrl(nil), fEventQueueEmptySignalHappened(FALSE), fMessageUploadCompleted(FALSE), fHierarchyNameState(kNoOperationInProgress), fHierarchyMover(nil), fBytesMovedSoFarForProgress(0), fDeletableChildren(nil), fOnlineBaseFolderExists(FALSE), fFetchMsgIdList(NULL), fFetchCount(0), fFetchMsgListIsNew(FALSE), fMsgAppendSizeIsNew(FALSE), fAppendMessageSize(0), fNextEntryEventSignalHappened(FALSE), fThreadShouldDie(FALSE), fIsSafeToDie(FALSE), fProgressStringId(0), fProgressIndex(0), fNeedGreeting(FALSE), fNeedNoop(FALSE), fProgressCount(0), fOutputStream(nil), fPseudoInterrupted(FALSE), fAutoSubscribe(TRUE), fAutoUnsubscribe(TRUE), fShouldUpgradeToSubscription(FALSE), fUpgradeShouldLeaveAlone(FALSE), fUpgradeShouldSubscribeAll(FALSE), fFolderInfo(NULL), fFolderNeedsSubscribing(FALSE), fFolderNeedsACLRefreshed(FALSE) { fEventCompletionMonitor = PR_NewNamedMonitor("event-completion-monitor"); fActiveEntryMonitor = PR_NewNamedMonitor("active-entry-monitor"); fEventQueueEmptySignalMonitor = PR_NewNamedMonitor("event-queue-empty-signal-monitor"); fMessageUploadMonitor = PR_NewNamedMonitor("message-upload-monitor"); fMsgCopyDataMonitor = PR_NewNamedMonitor("msg-copy-data-monitor"); fThreadDeathMonitor = PR_NewNamedMonitor("thread-death-monitor"); fPermissionToDieMonitor = PR_NewNamedMonitor("die-monitor"); fPseudoInterruptMonitor = PR_NewNamedMonitor("imap-pseudo-interrupt-monitor"); fWaitForBodyIdsMonitor = PR_NewNamedMonitor("wait-for-body-ids-monitor"); fTunnelCompletionMonitor = PR_NewNamedMonitor("imap-io-tunnelling-monitor"); fFEEventQueue = TImapFEEventQueue::CreateFEEventQueue(); fSocketInfo = new TIMAPSocketInfo(); fListedMailboxList = XP_ListNew(); fHostName = XP_STRDUP(hostName); fFlagState = new TImapFlagAndUidState(kFlagEntrySize, FALSE); fMailToFetch = fGetHeaders = fNewMail = FALSE; fLastProgressStringId = 0; fLastPercent = -1; LL_I2L(fLastProgressTime, 0); ResetProgressInfo(); fActive = fTrackingTime = FALSE; fStartTime = fEndTime = 0; fTooFastTime = 2; fIdealTime = 4; fChunkAddSize = 2048; fChunkStartSize = fChunkSize = 10240; fFetchByChunks = TRUE; fChunkThreshold = fChunkSize + (fChunkSize / 2); fMaxChunkSize = 40960; connectionList->Add(this); } void TNavigatorImapConnection::Configure(XP_Bool GetHeaders, int32 TooFastTime, int32 IdealTime, int32 ChunkAddSize, int32 ChunkSize, int32 ChunkThreshold, XP_Bool FetchByChunks, int32 MaxChunkSize) { PR_EnterMonitor(fMsgCopyDataMonitor); fGetHeaders = GetHeaders; fTooFastTime = TooFastTime; // secs we read too little too fast fIdealTime = IdealTime; // secs we read enough in good time fChunkAddSize = ChunkAddSize; // buffer size to add when wasting time fChunkSize = ChunkSize; fChunkThreshold = ChunkThreshold; fFetchByChunks = FetchByChunks; fMaxChunkSize = MaxChunkSize; PR_ExitMonitor(fMsgCopyDataMonitor); } TIMAPHostInfo::TIMAPHostInfo(const char *hostName) { fHostName = XP_STRDUP(hostName); fNextHost = NULL; fCachedPassword = NULL; fCapabilityFlags = kCapabilityUndefined; fHierarchyDelimiters = NULL; fHaveWeEverDiscoveredFolders = FALSE; fCanonicalOnlineSubDir = NULL; fNamespaceList = TIMAPNamespaceList::CreateTIMAPNamespaceList(); fUsingSubscription = TRUE; fOnlineTrashFolderExists = FALSE; fShouldAlwaysListInbox = TRUE; fShellCache = TIMAPBodyShellCache::Create(); fPasswordVerifiedOnline = FALSE; } TIMAPHostInfo::~TIMAPHostInfo() { FREEIF(fHostName); if (fCachedPassword) XP_FREE(fCachedPassword); FREEIF(fHierarchyDelimiters); delete fNamespaceList; delete fShellCache; } TIMAPHostInfo *TIMAPHostInfo::FindHost(const char *hostName) { TIMAPHostInfo *host; for (host = fHostInfoList; host; host = host->fNextHost) { if (!XP_STRCASECMP(hostName, host->fHostName)) return host; } return host; } // reset any cached connection info - delete the lot of 'em void TIMAPHostInfo::ResetAll() { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *nextHost = NULL; for (TIMAPHostInfo *host = fHostInfoList; host; host = nextHost) { nextHost = host->fNextHost; delete host; } fHostInfoList = NULL; PR_ExitMonitor(gCachedHostInfoMonitor); } /* static */ XP_Bool TIMAPHostInfo::AddHostToList(const char *hostName) { TIMAPHostInfo *newHost=NULL; PR_EnterMonitor(gCachedHostInfoMonitor); if (!FindHost(hostName)) { // stick it on the front newHost = new TIMAPHostInfo(hostName); if (newHost) { newHost->fNextHost = fHostInfoList; fHostInfoList = newHost; } } PR_ExitMonitor(gCachedHostInfoMonitor); return newHost != NULL; } XP_Bool IMAP_HostHasACLCapability(const char *hostName) { return (TIMAPHostInfo::GetCapabilityForHost(hostName) & kACLCapability); } uint32 IMAP_GetCapabilityForHost(const char *hostName) { return TIMAPHostInfo::GetCapabilityForHost(hostName); } /* static */ uint32 TIMAPHostInfo::GetCapabilityForHost(const char *hostName) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); uint32 ret = (host) ? host->fCapabilityFlags : 0; PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::SetCapabilityForHost(const char *hostName, uint32 capability) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) host->fCapabilityFlags = capability; PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ char *TIMAPHostInfo::GetPasswordForHost(const char *hostName) { char *ret = NULL; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) ret = host->fCachedPassword; PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::SetPasswordForHost(const char *hostName, const char *password) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { FREEIF(host->fCachedPassword); if (password) host->fCachedPassword = XP_STRDUP(password); } PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::SetPasswordVerifiedOnline(const char *hostName) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) host->fPasswordVerifiedOnline = TRUE; PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::GetPasswordVerifiedOnline(const char *hostName) { XP_Bool ret = FALSE; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) ret = host->fPasswordVerifiedOnline; PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ char *TIMAPHostInfo::GetHierarchyDelimiterStringForHost(const char *hostName) { char *ret = NULL; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) ret = host->fHierarchyDelimiters; PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::AddHierarchyDelimiter(const char *hostName, char delimiter) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { if (!host->fHierarchyDelimiters) { host->fHierarchyDelimiters = PR_smprintf("%c",delimiter); } else if (!XP_STRCHR(host->fHierarchyDelimiters, delimiter)) { char *tmpDelimiters = PR_smprintf("%s%c",host->fHierarchyDelimiters,delimiter); FREEIF(host->fHierarchyDelimiters); host->fHierarchyDelimiters = tmpDelimiters; } } PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::GetHostIsUsingSubscription(const char *hostName) { XP_Bool ret = FALSE; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) ret = host->fUsingSubscription; PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::SetHostIsUsingSubscription(const char *hostName, XP_Bool usingSubscription) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) host->fUsingSubscription = usingSubscription; PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::GetHostHasAdminURL(const char *hostName) { XP_Bool ret = FALSE; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) ret = host->fHaveAdminURL; PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::SetHostHasAdminURL(const char *hostName, XP_Bool haveAdminURL) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) host->fHaveAdminURL = haveAdminURL; PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::GetHaveWeEverDiscoveredFoldersForHost(const char *hostName) { XP_Bool ret = FALSE; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) ret = host->fHaveWeEverDiscoveredFolders; PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::SetHaveWeEverDiscoveredFoldersForHost(const char *hostName, XP_Bool discovered) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) host->fHaveWeEverDiscoveredFolders = discovered; PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(const char *hostName, XP_Bool exists) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) host->fOnlineTrashFolderExists = exists; PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(const char *hostName) { XP_Bool ret = FALSE; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) ret = host->fOnlineTrashFolderExists; PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::AddNewNamespaceForHost(const char *hostName, TIMAPNamespace *ns) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { host->fNamespaceList->AddNewNamespace(ns); } PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ TIMAPNamespace *TIMAPHostInfo::GetNamespaceForMailboxForHost(const char *hostName, const char *mailbox_name) { TIMAPNamespace *ret = 0; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { ret = host->fNamespaceList->GetNamespaceForMailbox(mailbox_name); } PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::ClearPrefsNamespacesForHost(const char *hostName) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { host->fNamespaceList->ClearNamespaces(TRUE, FALSE); } PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::ClearServerAdvertisedNamespacesForHost(const char *hostName) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { host->fNamespaceList->ClearNamespaces(FALSE, TRUE); } PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ TIMAPNamespace *TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(const char *hostName, EIMAPNamespaceType type) { TIMAPNamespace *ret = NULL; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { ret = host->fNamespaceList->GetDefaultNamespaceOfType(type); } PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::GetNamespacesOverridableForHost(const char *hostName) { XP_Bool ret = FALSE; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) ret = host->fNamespacesOverridable; PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::SetNamespacesOverridableForHost(const char *hostName, XP_Bool overridable) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) host->fNamespacesOverridable = overridable; PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ int TIMAPHostInfo::GetNumberOfNamespacesForHost(const char *hostName) { int ret = 0; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { ret = host->fNamespaceList->GetNumberOfNamespaces(); } PR_ExitMonitor(gCachedHostInfoMonitor); XP_ASSERT(ret > 0); return ret; } /* static */ TIMAPNamespace *TIMAPHostInfo::GetNamespaceNumberForHost(const char *hostName, int n) { TIMAPNamespace *ret = 0; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { ret = host->fNamespaceList->GetNamespaceNumber(n); } PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } // Make sure this is running in the Mozilla thread when called /* static */ XP_Bool TIMAPHostInfo::CommitNamespacesForHost(const char *hostName, MSG_Master *master) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { EIMAPNamespaceType type = kPersonalNamespace; for (int i = 1; i <= 3; i++) { switch(i) { case 1: type = kPersonalNamespace; break; case 2: type = kPublicNamespace; break; case 3: type = kOtherUsersNamespace; break; default: type = kPersonalNamespace; break; } int32 numInNS = host->fNamespaceList->GetNumberOfNamespaces(type); if (numInNS == 0) { MSG_SetNamespacePrefixes(master, host->fHostName, type, NULL); } else if (numInNS >= 1) { char *pref = PR_smprintf(""); for (int count = 1; count <= numInNS; count++) { TIMAPNamespace *ns = host->fNamespaceList->GetNamespaceNumber(count, type); if (ns) { if (count > 1) { // append the comma char *tempPref = PR_smprintf("%s,",pref); FREEIF(pref); pref = tempPref; } char *tempPref = PR_smprintf("%s\"%s\"",pref,ns->GetPrefix()); FREEIF(pref); pref = tempPref; } } if (pref) { MSG_SetNamespacePrefixes(master, host->fHostName, type, pref); XP_FREE(pref); } } } } PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } // Caller must free the returned string // Returns NULL if there is no personal namespace on the given host /* static */ char *TIMAPHostInfo::GetOnlineInboxPathForHost(const char *hostName) { char *ret = NULL; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { TIMAPNamespace *ns = NULL; ns = host->fNamespaceList->GetDefaultNamespaceOfType(kPersonalNamespace); if (ns) { ret = PR_smprintf("%sINBOX",ns->GetPrefix()); } } PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } /* static */ XP_Bool TIMAPHostInfo::GetShouldAlwaysListInboxForHost(const char* /*hostName*/) { XP_Bool ret = TRUE; /* PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) ret = host->fShouldAlwaysListInbox; PR_ExitMonitor(gCachedHostInfoMonitor); */ return ret; } /* static */ XP_Bool TIMAPHostInfo::SetShouldAlwaysListInboxForHost(const char *hostName, XP_Bool shouldList) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) host->fShouldAlwaysListInbox = shouldList; PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::SetNamespaceHierarchyDelimiterFromMailboxForHost(const char *hostName, const char *boxName, char delimiter) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { TIMAPNamespace *ns = host->fNamespaceList->GetNamespaceForMailbox(boxName); if (ns && !ns->GetIsDelimiterFilledIn()) { ns->SetDelimiter(delimiter); } } PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ XP_Bool TIMAPHostInfo::AddShellToCacheForHost(const char *hostName, TIMAPBodyShell *shell) { PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { if (host->fShellCache) { XP_Bool rv = host->fShellCache->AddShellToCache(shell); PR_ExitMonitor(gCachedHostInfoMonitor); return rv; } else { PR_ExitMonitor(gCachedHostInfoMonitor); return FALSE; } } PR_ExitMonitor(gCachedHostInfoMonitor); return (host != 0); } /* static */ TIMAPBodyShell *TIMAPHostInfo::FindShellInCacheForHost(const char *hostName, const char *mailboxName, const char *UID) { TIMAPBodyShell *ret = NULL; PR_EnterMonitor(gCachedHostInfoMonitor); TIMAPHostInfo *host = FindHost(hostName); if (host) { if (host->fShellCache) ret = host->fShellCache->FindShellForUID(UID, mailboxName); } PR_ExitMonitor(gCachedHostInfoMonitor); return ret; } extern "C" { extern int IMAP_AddIMAPHost(const char *hostName, XP_Bool usingSubscription, XP_Bool overrideNamespaces, const char *personalNamespacePrefixes, const char *publicNamespacePrefixes, const char *otherUsersNamespacePrefixes, XP_Bool haveAdminURL) { TIMAPHostInfo::AddHostToList(hostName); TIMAPHostInfo::SetHostIsUsingSubscription(hostName,usingSubscription); TIMAPHostInfo::SetNamespacesOverridableForHost(hostName, overrideNamespaces); TIMAPHostInfo::SetHostHasAdminURL(hostName, haveAdminURL); if (personalNamespacePrefixes) { int numNamespaces = IMAP_UnserializeNamespaces(personalNamespacePrefixes, NULL, 0); char **prefixes = (char**) XP_CALLOC(numNamespaces, sizeof(char*)); if (prefixes) { int len = IMAP_UnserializeNamespaces(personalNamespacePrefixes, prefixes, numNamespaces); for (int i = 0; i < len; i++) { char *thisns = prefixes[i]; char delimiter = '/'; // a guess if (XP_STRLEN(thisns) >= 1) delimiter = thisns[XP_STRLEN(thisns)-1]; TIMAPNamespace *ns = new TIMAPNamespace(kPersonalNamespace, thisns, delimiter, TRUE); if (ns) TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns); FREEIF(thisns); } } } if (publicNamespacePrefixes) { int numNamespaces = IMAP_UnserializeNamespaces(publicNamespacePrefixes, NULL, 0); char **prefixes = (char**) XP_CALLOC(numNamespaces, sizeof(char*)); if (prefixes) { int len = IMAP_UnserializeNamespaces(publicNamespacePrefixes, prefixes, numNamespaces); for (int i = 0; i < len; i++) { char *thisns = prefixes[i]; char delimiter = '/'; // a guess if (XP_STRLEN(thisns) >= 1) delimiter = thisns[XP_STRLEN(thisns)-1]; TIMAPNamespace *ns = new TIMAPNamespace(kPublicNamespace, thisns, delimiter, TRUE); if (ns) TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns); FREEIF(thisns); } } } if (otherUsersNamespacePrefixes) { int numNamespaces = IMAP_UnserializeNamespaces(otherUsersNamespacePrefixes, NULL, 0); char **prefixes = (char**) XP_CALLOC(numNamespaces, sizeof(char*)); if (prefixes) { int len = IMAP_UnserializeNamespaces(otherUsersNamespacePrefixes, prefixes, numNamespaces); for (int i = 0; i < len; i++) { char *thisns = prefixes[i]; char delimiter = '/'; // a guess if (XP_STRLEN(thisns) >= 1) delimiter = thisns[XP_STRLEN(thisns)-1]; TIMAPNamespace *ns = new TIMAPNamespace(kOtherUsersNamespace, thisns, delimiter, TRUE); if (ns) TIMAPHostInfo::AddNewNamespaceForHost(hostName, ns); FREEIF(thisns); } } } return 0; } extern void IMAP_SetShouldAlwaysListInboxForHost(const char *hostName, XP_Bool shouldList) { TIMAPHostInfo::SetShouldAlwaysListInboxForHost(hostName, shouldList); } extern int IMAP_GetNumberOfNamespacesForHost(const char *hostName) { return TIMAPHostInfo::GetNumberOfNamespacesForHost(hostName); } extern XP_Bool IMAP_SetHostIsUsingSubscription(const char *hostname, XP_Bool using_subscription) { return TIMAPHostInfo::SetHostIsUsingSubscription(hostname, using_subscription); } #define SERIALIZER_SEPARATORS "," /* prefixes is an array of strings; len is the length of that array. If there is only one string, simply copy it and return it. Otherwise, put them in quotes and comma-delimit them. Returns a newly allocated string. */ extern char *IMAP_SerializeNamespaces(char **prefixes, int len) { char *rv = NULL; if (len <= 0) return rv; if (len == 1) { rv = XP_STRDUP(prefixes[0]); return rv; } else { for (int i = 0; i < len; i++) { char *temp = NULL; if (i == 0) temp = PR_smprintf("\"%s\"",prefixes[i]); /* quote the string */ else temp = PR_smprintf(",\"%s\"",prefixes[i]); /* comma, then quote the string */ char *next = PR_smprintf("%s%s",rv,temp); FREEIF(rv); rv = next; } return rv; } } /* str is the string which needs to be unserialized. If prefixes is NULL, simply returns the number of namespaces in str. (len is ignored) If prefixes is not NULL, it should be an array of length len which is to be filled in with newly-allocated string. Returns the number of strings filled in. */ extern int IMAP_UnserializeNamespaces(const char *str, char **prefixes, int len) { if (!str) return 0; if (!prefixes) { if (str[0] != '"') return 1; else { int count = 0; char *ourstr = XP_STRDUP(str); if (ourstr) { char *token = XP_STRTOK( ourstr, SERIALIZER_SEPARATORS ); while (token != NULL) { token = XP_STRTOK( NULL, SERIALIZER_SEPARATORS ); count++; } XP_FREE(ourstr); } return count; } } else { if ((str[0] != '"') && (len >= 1)) { prefixes[0] = XP_STRDUP(str); return 1; } else { int count = 0; char *ourstr = XP_STRDUP(str); if (ourstr) { char *token = XP_STRTOK( ourstr, SERIALIZER_SEPARATORS ); while ((count < len) && (token != NULL)) { char *current = XP_STRDUP(token), *where = current; if (where[0] == '"') where++; if (where[XP_STRLEN(where)-1] == '"') where[XP_STRLEN(where)-1] = 0; prefixes[count] = XP_STRDUP(where); FREEIF(current); token = XP_STRTOK( NULL, SERIALIZER_SEPARATORS ); count++; } XP_FREE(ourstr); } return count; } } } } // extern "C" ///////////////// TNavigatorImapConnection ////////////////////////////// //static void TNavigatorImapConnection::ImapStartup() { if (!fFindingMailboxesMonitor) fFindingMailboxesMonitor = PR_NewNamedMonitor("finding-mailboxes-monitor"); if (!fUpgradeToSubscriptionMonitor) fUpgradeToSubscriptionMonitor = PR_NewNamedMonitor("upgrade-to-imap-subscription-monitor"); if (!TIMAPHostInfo::gCachedHostInfoMonitor) TIMAPHostInfo::gCachedHostInfoMonitor = PR_NewNamedMonitor("accessing-hostlist-monitor"); } //static void TNavigatorImapConnection::ImapShutDown() { if (fFindingMailboxesMonitor) { PR_DestroyMonitor(fFindingMailboxesMonitor); fFindingMailboxesMonitor = NULL; } if (fUpgradeToSubscriptionMonitor) { PR_DestroyMonitor(fUpgradeToSubscriptionMonitor); fUpgradeToSubscriptionMonitor = NULL; } TIMAPHostInfo::ResetAll(); if (TIMAPHostInfo::gCachedHostInfoMonitor) { PR_DestroyMonitor(TIMAPHostInfo::gCachedHostInfoMonitor); TIMAPHostInfo::gCachedHostInfoMonitor = NULL; } } TNavigatorImapConnection * TNavigatorImapConnection::Create(const char *hostName) { TNavigatorImapConnection *connection = new TNavigatorImapConnection(hostName); if (!connection || !connection->ConstructionSuccess()) { delete connection; connection = nil; return NULL; } connection->fServerState.SetCapabilityFlag(TIMAPHostInfo::GetCapabilityForHost(hostName)); connection->fServerState.SetFlagState(connection->fFlagState); return connection; } XP_Bool TNavigatorImapConnection::ConstructionSuccess() { return (TIMAP4BlockingConnection::ConstructionSuccess() && fEventCompletionMonitor && fFEEventQueue && fActiveEntryMonitor && fFindingMailboxesMonitor && fUpgradeToSubscriptionMonitor && fEventQueueEmptySignalMonitor && fMessageUploadMonitor && fMsgCopyDataMonitor && fThreadDeathMonitor && fPermissionToDieMonitor && fPseudoInterruptMonitor && fTunnelCompletionMonitor && fSocketInfo && fFlagState && fListedMailboxList); } XP_Bool TNavigatorImapConnection::GetPseudoInterrupted() { XP_Bool rv = FALSE; PR_EnterMonitor(fPseudoInterruptMonitor); rv = fPseudoInterrupted; PR_ExitMonitor(fPseudoInterruptMonitor); return rv; } void TNavigatorImapConnection::PseudoInterrupt(XP_Bool the_interrupt) { PR_EnterMonitor(fPseudoInterruptMonitor); fPseudoInterrupted = the_interrupt; if (the_interrupt) { Log("CONTROL", NULL, "PSEUDO-Interrupted"); //PR_LOG(IMAP, out, ("PSEUDO-Interrupted")); } PR_ExitMonitor(fPseudoInterruptMonitor); } void TNavigatorImapConnection::SetActive(XP_Bool active) { PR_EnterMonitor(GetDataMemberMonitor()); fActive = active; PR_ExitMonitor(GetDataMemberMonitor()); } XP_Bool TNavigatorImapConnection::GetActive() { XP_Bool ret; PR_EnterMonitor(GetDataMemberMonitor()); ret = fActive; PR_ExitMonitor(GetDataMemberMonitor()); return ret; } /* static */ XP_Bool TNavigatorImapConnection::IsConnectionActive(const char *hostName, const char *folderName) { XP_Bool ret = FALSE; if (!connectionList) connectionList = new XPPtrArray; // go through connection list looking for connection in selected state on folder for (int32 i = 0; i < connectionList->GetSize() && !ret; i++) { TNavigatorImapConnection *curConnection = (TNavigatorImapConnection *) connectionList->GetAt(i); // we should add semaphores for these variables (state and selectedmailboxname). PR_EnterMonitor(curConnection->GetDataMemberMonitor()); char *mailboxName = (curConnection->fCurrentUrl) ? curConnection->fCurrentUrl->CreateServerSourceFolderPathString() : 0; if (curConnection->fActive && !XP_STRCMP(hostName, curConnection->fHostName) && folderName && mailboxName && !XP_STRCMP(folderName, mailboxName/* curConnection->GetServerStateParser().GetSelectedMailboxName() */)) ret = TRUE; #ifdef DEBUG_bienvenu XP_Trace("connection active = %s state = %d selected mailbox name = %s\n", (curConnection->fActive) ? "TRUE" : "FALSE", curConnection->GetServerStateParser().GetIMAPstate(), (mailboxName) ? mailboxName : "NULL" /* curConnection->GetServerStateParser().GetSelectedMailboxName()*/); #endif FREEIF(mailboxName); PR_ExitMonitor(curConnection->GetDataMemberMonitor()); } return ret; } // a thread safe accessor to fCurrentUrl XP_Bool TNavigatorImapConnection::CurrentConnectionIsMove() { XP_Bool isMove; PR_EnterMonitor(GetDataMemberMonitor()); isMove = (fCurrentUrl != NULL) && ((fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineCopy) || (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineMove) || (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineCopy) || (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineMove) || (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOfflineToOnlineMove)); PR_ExitMonitor(GetDataMemberMonitor()); return isMove; } void TNavigatorImapConnection::SetSubscribeParameters(XP_Bool autoSubscribe, XP_Bool autoUnsubscribe, XP_Bool autoSubscribeOnOpen, XP_Bool autoUnsubscribeFromNoSelect, XP_Bool shouldUpgrade, XP_Bool upgradeLeaveAlone, XP_Bool upgradeSubscribeAll) { PR_EnterMonitor(GetDataMemberMonitor()); fAutoSubscribe = autoSubscribe; fAutoUnsubscribe = autoUnsubscribe; fAutoSubscribeOnOpen = autoSubscribeOnOpen; fAutoUnsubscribeFromNoSelect = autoUnsubscribeFromNoSelect; PR_ExitMonitor(GetDataMemberMonitor()); PR_EnterMonitor(fUpgradeToSubscriptionMonitor); fShouldUpgradeToSubscription = shouldUpgrade; fUpgradeShouldLeaveAlone = upgradeLeaveAlone; fUpgradeShouldSubscribeAll = upgradeSubscribeAll; PR_ExitMonitor(fUpgradeToSubscriptionMonitor); } void TNavigatorImapConnection::TellThreadToDie(XP_Bool isSafeToDie) { if (GetIOSocket() != 0 && (fCurrentEntry && fCurrentEntry->status != MK_WAITING_FOR_CONNECTION)) { char *logoutString = PR_smprintf("xxxx logout\r\n"); if (logoutString) { NET_BlockingWrite(GetIOSocket(), logoutString, XP_STRLEN(logoutString)); Log("NET","WR",logoutString); //PR_LOG(IMAP, out, ("%s",logoutString)); Log("CONTROL","From TellThreadToDie","LOGOUT"); //PR_LOG(IMAP, out, ("Logged out from TellThreadToDie")); XP_FREEIF(logoutString); } } PR_EnterMonitor(fThreadDeathMonitor); fThreadShouldDie = TRUE; PR_ExitMonitor(fThreadDeathMonitor); // signal all of the monitors that might be in PR_Wait // they will wake up and see fThreadShouldDie==TRUE; PR_EnterMonitor(fEventCompletionMonitor); PR_Notify(fEventCompletionMonitor); PR_ExitMonitor(fEventCompletionMonitor); PR_EnterMonitor(fTunnelCompletionMonitor); PR_Notify(fTunnelCompletionMonitor); PR_ExitMonitor(fTunnelCompletionMonitor); PR_EnterMonitor(fMessageUploadMonitor); PR_Notify(fMessageUploadMonitor); PR_ExitMonitor(fMessageUploadMonitor); PR_EnterMonitor(fMsgCopyDataMonitor); PR_Notify(fMsgCopyDataMonitor); PR_ExitMonitor(fMsgCopyDataMonitor); PR_EnterMonitor(fIOMonitor); PR_Notify(fIOMonitor); PR_ExitMonitor(fIOMonitor); if (isSafeToDie) SetIsSafeToDie(); } XP_Bool TNavigatorImapConnection::DeathSignalReceived() { XP_Bool returnValue; PR_EnterMonitor(fThreadDeathMonitor); returnValue = fThreadShouldDie; PR_ExitMonitor(fThreadDeathMonitor); return returnValue; } TNavigatorImapConnection::~TNavigatorImapConnection() { // This is either being deleted as the last thing that the // imap thread does or during a url interrupt, in which case // the thread was already killed, so don't destroy the thread // here if (GetIOSocket() != 0) { // Logout(); *** We still need to close down the socket net_graceful_shutdown(GetIOSocket(), HG32830); PR_Close(GetIOSocket()); SetIOSocket(NULL); // delete connection if (GetActiveEntry()) GetActiveEntry()->socket = 0; } GetServerStateParser().SetFlagState(NULL); if (fEventCompletionMonitor) PR_DestroyMonitor(fEventCompletionMonitor); if (fTunnelCompletionMonitor) PR_DestroyMonitor(fTunnelCompletionMonitor); if (fActiveEntryMonitor) PR_DestroyMonitor(fActiveEntryMonitor); if (fEventQueueEmptySignalMonitor) PR_DestroyMonitor(fEventQueueEmptySignalMonitor); if (fMessageUploadMonitor) PR_DestroyMonitor(fMessageUploadMonitor); if (fMsgCopyDataMonitor) PR_DestroyMonitor(fMsgCopyDataMonitor); if (fThreadDeathMonitor) PR_DestroyMonitor(fThreadDeathMonitor); if (fPermissionToDieMonitor) PR_DestroyMonitor(fPermissionToDieMonitor); if (fPseudoInterruptMonitor) PR_DestroyMonitor(fPseudoInterruptMonitor); if (fWaitForBodyIdsMonitor) PR_DestroyMonitor(fWaitForBodyIdsMonitor); if (fFEEventQueue) delete fFEEventQueue; if (fHierarchyMover) delete fHierarchyMover; if (fCurrentUrl) delete fCurrentUrl; if (fSocketInfo) delete (fSocketInfo); if (fFlagState) delete fFlagState; while ((TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList)) { } XP_ListDestroy(fListedMailboxList); preAuthSucceeded = FALSE; FREEIF(fInputSocketBuffer); FREEIF(fHostName); connectionList->Remove(this); } TNavigatorImapConnection *TNavigatorImapConnection::GetNavigatorConnection() { return this; } void TNavigatorImapConnection::HandleMemoryFailure() { PR_EnterMonitor(GetDataMemberMonitor()); AlertUserEvent(XP_GetString(MK_OUT_OF_MEMORY)); SetConnectionStatus(-1); // stop this midnight train to crashville PR_ExitMonitor(GetDataMemberMonitor()); } void TNavigatorImapConnection::SetMailboxDiscoveryStatus(EMailboxDiscoverStatus status) { PR_EnterMonitor(GetDataMemberMonitor()); fDiscoveryStatus = status; PR_ExitMonitor(GetDataMemberMonitor()); } EMailboxDiscoverStatus TNavigatorImapConnection::GetMailboxDiscoveryStatus( ) { EMailboxDiscoverStatus returnStatus; PR_EnterMonitor(GetDataMemberMonitor()); returnStatus = fDiscoveryStatus; PR_ExitMonitor(GetDataMemberMonitor()); return returnStatus; } void TNavigatorImapConnection::WaitForFEEventCompletion() { PRIntervalTime sleepTime = kImapSleepTime; PR_EnterMonitor(fEventCompletionMonitor); //#ifdef KMCENTEE_DEBUG // WaitForFEEventLoopCount=0; //#endif while (!fFEeventCompleted && !DeathSignalReceived()) { PR_Wait(fEventCompletionMonitor, sleepTime); //#ifdef KMCENTEE_DEBUG // WaitForFEEventLoopCount++; // if (WaitForFEEventLoopCount == 10) // XP_ASSERT(FALSE); //#endif } fFEeventCompleted = FALSE; PR_ExitMonitor(fEventCompletionMonitor); } void TNavigatorImapConnection::WaitForTunnelCompletion() { PRIntervalTime sleepTime = kImapSleepTime; PR_EnterMonitor(fTunnelCompletionMonitor); while (!fTunnelCompleted && !DeathSignalReceived()) { PR_Wait(fTunnelCompletionMonitor, sleepTime); } fTunnelCompleted = FALSE; PR_ExitMonitor(fTunnelCompletionMonitor); } void TNavigatorImapConnection::NotifyEventCompletionMonitor() { PR_EnterMonitor(fEventCompletionMonitor); fFEeventCompleted = TRUE; PR_Notify(fEventCompletionMonitor); PR_ExitMonitor(fEventCompletionMonitor); } void TNavigatorImapConnection::NotifyTunnelCompletionMonitor() { PR_EnterMonitor(fTunnelCompletionMonitor); fTunnelCompleted = TRUE; PR_Notify(fTunnelCompletionMonitor); PR_ExitMonitor(fTunnelCompletionMonitor); } TImapFEEventQueue &TNavigatorImapConnection::GetFEEventQueue() { return *fFEEventQueue; } void TNavigatorImapConnection::BeginMessageDownLoad( uint32 total_message_size, // for user, headers and body const char *content_type) { char *sizeString = PR_smprintf("OPEN Size: %ld", total_message_size); Log("STREAM",sizeString,"Begin Message Download Stream"); FREEIF(sizeString); //PR_LOG(IMAP, out, ("STREAM: Begin Message Download Stream. Size: %ld", total_message_size)); StreamInfo *si = (StreamInfo *) XP_ALLOC (sizeof (StreamInfo)); // This will be freed in the event if (si) { si->size = total_message_size; si->content_type = XP_STRDUP(content_type); if (si->content_type) { TImapFEEvent *setupStreamEvent = new TImapFEEvent(SetupMsgWriteStream, // function to call this, // access to current entry (void *) si, TRUE); // ok to run when interrupted because si is FREE'd in the event if (setupStreamEvent) { fFEEventQueue->AdoptEventToEnd(setupStreamEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); fFromHeaderSeen = FALSE; } else HandleMemoryFailure(); } else HandleMemoryFailure(); } void TNavigatorImapConnection::AddXMozillaStatusLine(uint16 /* flags */) // flags not use now { static char statusLine[] = "X-Mozilla-Status: 0201\r\n"; HandleMessageDownLoadLine(statusLine, FALSE); } void TNavigatorImapConnection::HandleMessageDownLoadLine(const char *line, XP_Bool chunkEnd) { // when we duplicate this line, whack it into the native line // termination. Do not assume that it really ends in CRLF // to start with, even though it is supposed to be RFC822 // If we are fetching by chunks, we can make no assumptions about // the end-of-line terminator, and we shouldn't mess with it. // leave enough room for two more chars. (CR and LF) char *localMessageLine = (char *) XP_ALLOC(strlen(line) + 3); if (localMessageLine) strcpy(localMessageLine,line); char *endOfLine = localMessageLine + strlen(localMessageLine); if (!chunkEnd) { #if (LINEBREAK_LEN == 1) if ((endOfLine - localMessageLine) >= 2 && endOfLine[-2] == CR && endOfLine[-1] == LF) { /* CRLF -> CR or LF */ endOfLine[-2] = LINEBREAK[0]; endOfLine[-1] = '\0'; } else if (endOfLine > localMessageLine + 1 && endOfLine[-1] != LINEBREAK[0] && ((endOfLine[-1] == CR) || (endOfLine[-1] == LF))) { /* CR -> LF or LF -> CR */ endOfLine[-1] = LINEBREAK[0]; } else // no eol characters at all { endOfLine[0] = LINEBREAK[0]; // CR or LF endOfLine[1] = '\0'; } #else if (((endOfLine - localMessageLine) >= 2 && endOfLine[-2] != CR) || ((endOfLine - localMessageLine) >= 1 && endOfLine[-1] != LF)) { if ((endOfLine[-1] == CR) || (endOfLine[-1] == LF)) { /* LF -> CRLF or CR -> CRLF */ endOfLine[-1] = LINEBREAK[0]; endOfLine[0] = LINEBREAK[1]; endOfLine[1] = '\0'; } else // no eol characters at all { endOfLine[0] = LINEBREAK[0]; // CR endOfLine[1] = LINEBREAK[1]; // LF endOfLine[2] = '\0'; } } #endif } const char *xSenderInfo = GetServerStateParser().GetXSenderInfo(); if (xSenderInfo && *xSenderInfo && !fFromHeaderSeen) { if (!XP_STRNCMP("From: ", localMessageLine, 6)) { fFromHeaderSeen = TRUE; if (XP_STRSTR(localMessageLine, xSenderInfo) != NULL) AddXMozillaStatusLine(0); GetServerStateParser().FreeXSenderInfo(); } } // if this line is for a different message, or the incoming line is too big if (((fDownLoadLineCache.CurrentUID() != GetServerStateParser().CurrentResponseUID()) && !fDownLoadLineCache.CacheEmpty()) || (fDownLoadLineCache.SpaceAvailable() < (strlen(localMessageLine) + 1)) ) { if (!fDownLoadLineCache.CacheEmpty()) { msg_line_info *downloadLineDontDelete = fDownLoadLineCache.GetCurrentLineInfo(); PostLineDownLoadEvent(downloadLineDontDelete); } fDownLoadLineCache.ResetCache(); } // so now the cache is flushed, but this string might still be to big if (fDownLoadLineCache.SpaceAvailable() < (strlen(localMessageLine) + 1) ) { // has to be dynamic to pass to other win16 thread msg_line_info *downLoadInfo = (msg_line_info *) XP_ALLOC(sizeof(msg_line_info)); if (downLoadInfo) { downLoadInfo->adoptedMessageLine = localMessageLine; downLoadInfo->uidOfMessage = GetServerStateParser().CurrentResponseUID(); PostLineDownLoadEvent(downLoadInfo); if (!DeathSignalReceived()) XP_FREE(downLoadInfo); else { // this is very rare, interrupt while waiting to display a huge single line // Net_InterruptIMAP will read this line so leak the downLoadInfo // set localMessageLine to NULL so the FREEIF( localMessageLine) leaks also localMessageLine = NULL; } } } else fDownLoadLineCache.CacheLine(localMessageLine, GetServerStateParser().CurrentResponseUID()); FREEIF( localMessageLine); } char* TNavigatorImapConnection::CreateUtf7ConvertedString(const char *sourceString, XP_Bool toUtf7Imap) { char *convertedString = NULL; utf_name_struct *conversion = (utf_name_struct *) XP_ALLOC(sizeof(utf_name_struct)); if (conversion) { conversion->toUtf7Imap = toUtf7Imap; conversion->sourceString = (unsigned char *) XP_STRDUP(sourceString); conversion->convertedString = NULL; } TImapFEEvent *convertEvent = new TImapFEEvent(ConvertImapUtf7, // function to call conversion, this, TRUE); if (convertEvent && sourceString && conversion) { fFEEventQueue->AdoptEventToEnd(convertEvent); WaitForFEEventCompletion(); convertedString = (char *) conversion->convertedString; } if (!DeathSignalReceived()) { FREEIF(conversion->sourceString); FREEIF(conversion); // leak these 8 bytes if we were interrupted here. } return convertedString; } void TNavigatorImapConnection::PostLineDownLoadEvent(msg_line_info *downloadLineDontDelete) { TImapFEEvent *endEvent = new TImapFEEvent(ParseAdoptedMsgLine, // function to call this, // access to current entry (void *) downloadLineDontDelete, FALSE); // line/msg info if (endEvent && downloadLineDontDelete && downloadLineDontDelete->adoptedMessageLine) { fFEEventQueue->AdoptEventToEnd(endEvent); // I just put a big buffer in the event queue that I need to reuse, so wait. // The alternative would be to realloc the buffer each time it gets placed in the // queue, but I don't want the perf hit a continuously doing an alloc/delete // of a big buffer. Also, I don't a queue full of huge buffers laying around! WaitForFEEventCompletion(); // we better yield here, dealing with a huge buffer made for a slow fe event. // because this event was processed, the queue is empty. Yielding here will // cause NET_ProcessIMAP4 to exit. if (!DeathSignalReceived()) PR_Sleep(PR_INTERVAL_NO_WAIT); } else HandleMemoryFailure(); } // Make sure that we receive data chunks in a size that allows the user to see some // progress. Dont want to bore them just yet. // If we received our chunk too fast, ask for more // if we received our chunk too slow, ask for less, etc void TNavigatorImapConnection::AdjustChunkSize() { fEndTime = XP_TIME(); fTrackingTime = FALSE; time_t t = fEndTime - fStartTime; if (t < 0) return; // bogus for some reason if (t <= fTooFastTime) { fChunkSize += fChunkAddSize; fChunkThreshold = fChunkSize + (fChunkSize / 2); if (fChunkSize > fMaxChunkSize) fChunkSize = fMaxChunkSize; } else if (t <= fIdealTime) return; else { if (fChunkSize > fChunkStartSize) fChunkSize = fChunkStartSize; else if (fChunkSize > (fChunkAddSize * 2)) fChunkSize -= fChunkAddSize; fChunkThreshold = fChunkSize + (fChunkSize / 2); } } void TNavigatorImapConnection::NormalMessageEndDownload() { Log("STREAM", "CLOSE", "Normal Message End Download Stream"); //PR_LOG(IMAP, out, ("STREAM: Normal End Message Download Stream")); if (fTrackingTime) AdjustChunkSize(); if (!fDownLoadLineCache.CacheEmpty()) { msg_line_info *downloadLineDontDelete = fDownLoadLineCache.GetCurrentLineInfo(); PostLineDownLoadEvent(downloadLineDontDelete); fDownLoadLineCache.ResetCache(); } TImapFEEvent *endEvent = new TImapFEEvent(NormalEndMsgWriteStream, // function to call this, // access to current entry nil, // unused TRUE); if (endEvent) fFEEventQueue->AdoptEventToEnd(endEvent); else HandleMemoryFailure(); } void TNavigatorImapConnection::AbortMessageDownLoad() { Log("STREAM", "CLOSE", "Abort Message Download Stream"); //PR_LOG(IMAP, out, ("STREAM: Abort Message Download Stream")); if (fTrackingTime) AdjustChunkSize(); if (!fDownLoadLineCache.CacheEmpty()) { msg_line_info *downloadLineDontDelete = fDownLoadLineCache.GetCurrentLineInfo(); PostLineDownLoadEvent(downloadLineDontDelete); fDownLoadLineCache.ResetCache(); } TImapFEEvent *endEvent = new TImapFEEvent(AbortMsgWriteStream, // function to call this, // access to current entry nil, // unused TRUE); if (endEvent) fFEEventQueue->AdoptEventToEnd(endEvent); else HandleMemoryFailure(); //MSG_FolderSetGettingMail(fFolderInfo, FALSE); } char *TNavigatorImapConnection::CreatePossibleTrashName(const char *prefix) { IMAP_LoadTrashFolderName(); char *returnTrash = (char *) XP_ALLOC(XP_STRLEN(prefix) + XP_STRLEN(ImapTRASH_FOLDER_NAME) + 1); if (returnTrash) { XP_STRCPY(returnTrash, prefix); XP_STRCAT(returnTrash, ImapTRASH_FOLDER_NAME); } return returnTrash; } void TNavigatorImapConnection::CanonicalChildList(const char *canonicalPrefix, XP_Bool pipeLined) { char *truncatedPrefix = XP_STRDUP(canonicalPrefix); if (truncatedPrefix) { if (*(truncatedPrefix + XP_STRLEN(truncatedPrefix) - 1) == '/') *(truncatedPrefix + XP_STRLEN(truncatedPrefix) - 1) = '\0'; char *serverPrefix = fCurrentUrl->AllocateServerPath(truncatedPrefix); if (serverPrefix) { char *utf7ListArg = CreateUtf7ConvertedString(serverPrefix,TRUE); if (utf7ListArg) { char *pattern = PR_smprintf("%s%c%c",utf7ListArg, fCurrentUrl->GetOnlineSubDirSeparator(),'%'); if (pattern) { List(pattern,pipeLined); XP_FREE(pattern); } XP_FREE(utf7ListArg); } XP_FREE(serverPrefix); } XP_FREE(truncatedPrefix); } } void TNavigatorImapConnection::NthLevelChildList(const char *canonicalPrefix, int depth) { XP_ASSERT(depth >= 0); if (depth < 0) return; char *truncatedPrefix = XP_STRDUP(canonicalPrefix); if (truncatedPrefix) { if (*(truncatedPrefix + XP_STRLEN(truncatedPrefix) - 1) == '/') *(truncatedPrefix + XP_STRLEN(truncatedPrefix) - 1) = '\0'; char *serverPrefix = fCurrentUrl->AllocateServerPath(truncatedPrefix); if (serverPrefix) { char *utf7ListArg = CreateUtf7ConvertedString(serverPrefix,TRUE); if (utf7ListArg) { char *pattern = PR_smprintf("%s",utf7ListArg); int count = 0; char *suffix = PR_smprintf("%c%c",fCurrentUrl->GetOnlineSubDirSeparator(),'%'); if (suffix) { while (pattern && (count < depth)) { StrAllocCat(pattern, suffix); count++; } if (pattern) { List(pattern); } XP_FREE(suffix); } XP_FREEIF(pattern); XP_FREE(utf7ListArg); } XP_FREE(serverPrefix); } XP_FREE(truncatedPrefix); } } static void MOZTHREAD_ChildDiscoverySucceeded(void *blockingConnectionVoid, void* /*unused*/) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ReportSuccessOfChildMailboxDiscovery(imapConnection->GetActiveEntry()->window_id); } void TNavigatorImapConnection::ChildDiscoverySucceeded() { TImapFEEvent *succeedEvent = new TImapFEEvent(MOZTHREAD_ChildDiscoverySucceeded, // function to call this, // access to current entry/context NULL, TRUE); if (succeedEvent) fFEEventQueue->AdoptEventToEnd(succeedEvent); else HandleMemoryFailure(); } void TNavigatorImapConnection::DiscoverMailboxSpec(mailbox_spec *adoptedBoxSpec) { IMAP_LoadTrashFolderName(); TIMAPNamespace *ns = TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(fCurrentUrl->GetUrlHost(), kPersonalNamespace); const char *hostDir = ns ? ns->GetPrefix() : 0; // if (!hostDir) // no personal or default namespaces - only public? or none altogether? char *canonicalSubDir = NULL; if (hostDir) { canonicalSubDir = XP_STRDUP(hostDir); if (canonicalSubDir && *canonicalSubDir && (*(canonicalSubDir + XP_STRLEN(canonicalSubDir) - 1) == '/')) *(canonicalSubDir + XP_STRLEN(canonicalSubDir) - 1) = 0; } switch (fHierarchyNameState) { case kNoOperationInProgress: case kDiscoverTrashFolderInProgress: case kListingForInfoAndDiscovery: { if (canonicalSubDir && XP_STRSTR(adoptedBoxSpec->allocatedPathName, canonicalSubDir)) fOnlineBaseFolderExists = TRUE; if (ns && hostDir) // if no personal namespace, there can be no Trash folder { if (!TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost()) && XP_STRSTR(adoptedBoxSpec->allocatedPathName, ImapTRASH_FOLDER_NAME)) { XP_Bool trashExists = FALSE; char *trashMatch = CreatePossibleTrashName(hostDir); if (trashMatch) { trashExists = XP_STRCMP(trashMatch, adoptedBoxSpec->allocatedPathName) == 0; TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost(), trashExists); FREEIF(trashMatch); // special case check for cmu trash, child of inbox if (!TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost()) && (ns->GetDelimiter() == '.')) { //char *inboxPath = TIMAPHostInfo::GetOnlineInboxPathForHost(fCurrentUrl->GetUrlHost()); char *inboxPath = PR_smprintf("INBOX"); if (inboxPath) { char *inboxAsParent = PR_smprintf("%s/",inboxPath); if (inboxAsParent) { trashMatch = CreatePossibleTrashName(inboxAsParent); // "INBOX/" if (trashMatch) { trashExists = XP_STRCMP(trashMatch, adoptedBoxSpec->allocatedPathName) == 0; TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost(), trashExists); FREEIF(trashMatch); } XP_FREE(inboxAsParent); } XP_FREE(inboxPath); } } } if (trashExists) adoptedBoxSpec->box_flags |= kImapTrash; } } // Discover the folder (shuttle over to libmsg, yay) // Do this only if the folder name is not empty (i.e. the root) if (*adoptedBoxSpec->allocatedPathName) { char *boxNameCopy = XP_STRDUP(adoptedBoxSpec->allocatedPathName); TImapFEEvent *endEvent = new TImapFEEvent(PossibleIMAPmailbox, // function to call this, // access to current entry/context (void *) adoptedBoxSpec, TRUE); // now owned by msg folder if (endEvent) { char *listArg = XP_STRDUP(adoptedBoxSpec->allocatedPathName); fFEEventQueue->AdoptEventToEnd(endEvent); WaitForFEEventCompletion(); if ((GetMailboxDiscoveryStatus() != eContinue) && (GetMailboxDiscoveryStatus() != eContinueNew) && (GetMailboxDiscoveryStatus() != eListMyChildren)) { SetConnectionStatus(-1); } else if (listArg && (GetMailboxDiscoveryStatus() == eListMyChildren) && (!TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost()) || GetSubscribingNow())) { XP_ASSERT(FALSE); // we should never get here anymore. SetMailboxDiscoveryStatus(eContinue); if (listArg) CanonicalChildList(listArg,TRUE); } else if (GetMailboxDiscoveryStatus() == eContinueNew) { if (fHierarchyNameState == kListingForInfoAndDiscovery && boxNameCopy) { // remember the info here also TIMAPMailboxInfo *mb = new TIMAPMailboxInfo(boxNameCopy); XP_ListAddObject(fListedMailboxList, mb); } SetMailboxDiscoveryStatus(eContinue); } FREEIF(listArg); } else HandleMemoryFailure(); FREEIF(boxNameCopy); } } break; case kDiscoverBaseFolderInProgress: { if (canonicalSubDir && XP_STRSTR(adoptedBoxSpec->allocatedPathName, canonicalSubDir)) fOnlineBaseFolderExists = TRUE; } break; case kDeleteSubFoldersInProgress: { XP_ListAddObject(fDeletableChildren, adoptedBoxSpec->allocatedPathName); delete adoptedBoxSpec->flagState; FREEIF( adoptedBoxSpec); } break; case kListingForInfoOnly: { //UpdateProgressWindowForUpgrade(adoptedBoxSpec->allocatedPathName); ProgressEventFunction_UsingIdWithString(MK_MSG_IMAP_DISCOVERING_MAILBOX, adoptedBoxSpec->allocatedPathName); TIMAPMailboxInfo *mb = new TIMAPMailboxInfo(adoptedBoxSpec->allocatedPathName); XP_ListAddObject(fListedMailboxList, mb); IMAP_FreeBoxSpec(adoptedBoxSpec); } break; case kDiscoveringNamespacesOnly: { IMAP_FreeBoxSpec(adoptedBoxSpec); } break; default: XP_ASSERT(FALSE); break; } FREEIF(canonicalSubDir); } void TNavigatorImapConnection::OnlineCopyCompleted(ImapOnlineCopyState copyState) { ImapOnlineCopyState *orphanedCopyState = (ImapOnlineCopyState *) XP_ALLOC(sizeof(ImapOnlineCopyState)); if (orphanedCopyState) *orphanedCopyState = copyState; TImapFEEvent *endEvent = new TImapFEEvent(OnlineCopyReport, // function to call this, // access to current entry/context (void *) orphanedCopyState, TRUE); // storage passed if (endEvent && orphanedCopyState) fFEEventQueue->AdoptEventToEnd(endEvent); else HandleMemoryFailure(); } void TNavigatorImapConnection::FolderDeleted(const char *mailboxName) { char *convertedName = CreateUtf7ConvertedString((char *) mailboxName,FALSE); char *orphanedMailboxName = convertedName ? fCurrentUrl->AllocateCanonicalPath(convertedName) : 0; TImapFEEvent *deleteEvent = new TImapFEEvent(OnlineFolderDelete, // function to call this, // access to current entry/context (void *) orphanedMailboxName, TRUE); // storage passed if (deleteEvent && orphanedMailboxName) fFEEventQueue->AdoptEventToEnd(deleteEvent); else HandleMemoryFailure(); FREEIF(convertedName); } void TNavigatorImapConnection::FolderNotCreated(const char *serverMessageResponse) { char *serverMessage = XP_STRDUP(serverMessageResponse); TImapFEEvent *noCreateEvent = new TImapFEEvent(OnlineFolderCreateFailed, // function to call this, // access to current entry/context (void *) serverMessage, TRUE); // storage passed if (noCreateEvent && serverMessage) fFEEventQueue->AdoptEventToEnd(noCreateEvent); else HandleMemoryFailure(); } void TNavigatorImapConnection::FolderRenamed(const char *oldName, const char *newName) { if ((fHierarchyNameState == kNoOperationInProgress) || (fHierarchyNameState == kListingForInfoAndDiscovery)) { char *oldConvertedName = CreateUtf7ConvertedString((char *) oldName,FALSE); char *newConvertedName = CreateUtf7ConvertedString((char *) newName,FALSE); if (oldConvertedName && newConvertedName) { folder_rename_struct *orphanRenameStruct = (folder_rename_struct *) XP_ALLOC(sizeof(folder_rename_struct)); orphanRenameStruct->fHostName = XP_STRDUP(GetHostName()); orphanRenameStruct->fOldName = fCurrentUrl->AllocateCanonicalPath(oldConvertedName); orphanRenameStruct->fNewName = fCurrentUrl->AllocateCanonicalPath(newConvertedName); TImapFEEvent *renameEvent = new TImapFEEvent(OnlineFolderRename, // function to call this, // access to current entry/context (void *) orphanRenameStruct, TRUE); // storage passed if (renameEvent && orphanRenameStruct && orphanRenameStruct->fOldName && orphanRenameStruct->fNewName) fFEEventQueue->AdoptEventToEnd(renameEvent); else HandleMemoryFailure(); } else HandleMemoryFailure(); FREEIF(oldConvertedName); FREEIF(newConvertedName); } } void TNavigatorImapConnection::MailboxDiscoveryFinished() { if (!GetSubscribingNow() && ((fHierarchyNameState == kNoOperationInProgress) || (fHierarchyNameState == kListingForInfoAndDiscovery))) { TIMAPNamespace *ns = TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(fCurrentUrl->GetUrlHost(), kPersonalNamespace); const char *personalDir = ns ? ns->GetPrefix() : 0; // if (!personalDir) // no personal or default NS - only public folders if (!TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost()) && fDeleteModelIsMoveToTrash && TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost())) { // maybe we're not subscribed to the Trash folder if (personalDir) { char *originalTrashName = CreatePossibleTrashName(personalDir); fHierarchyNameState = kDiscoverTrashFolderInProgress; List(originalTrashName); fHierarchyNameState = kNoOperationInProgress; } } // There is no Trash folder (either LIST'd or LSUB'd), and we're using the // Delete-is-move-to-Trash model, and there is a personal namespace if (!TIMAPHostInfo::GetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost()) && fDeleteModelIsMoveToTrash && personalDir) { const char *trashPrefix = personalDir; char *trashName = CreatePossibleTrashName(trashPrefix); if (trashName) { GetServerStateParser().SetReportingErrors(FALSE); XP_Bool created = CreateMailboxRespectingSubscriptions(trashName); /* // we shouldn't need this case anymore, since we're handling namespaces // the right way, I think. if (!created && (TIMAPUrl::GetOnlineSubDirSeparator() == '.')) { trashPrefix = "INBOX."; FREEIF(trashName); trashName = CreatePossibleTrashName(trashPrefix); if (trashName) created = CreateMailboxRespectingSubscriptions(trashName); } */ GetServerStateParser().SetReportingErrors(TRUE); // force discovery of new trash folder. if (created) { fHierarchyNameState = kDiscoverTrashFolderInProgress; List(trashName); fHierarchyNameState = kNoOperationInProgress; } else TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost(), TRUE); // we only try this once, not every time we create any folder. FREEIF(trashName); } } TIMAPHostInfo::SetHaveWeEverDiscoveredFoldersForHost(fCurrentUrl->GetUrlHost(), TRUE); SetFolderDiscoveryFinished(); } } void TNavigatorImapConnection::SetFolderDiscoveryFinished() { TImapFEEvent *discoveryDoneEvent = new TImapFEEvent(MailboxDiscoveryDoneEvent, // function to call this, // access to current entry/context NULL, TRUE); if (discoveryDoneEvent) fFEEventQueue->AdoptEventToEnd(discoveryDoneEvent); else HandleMemoryFailure(); } void TNavigatorImapConnection::UpdatedMailboxSpec(mailbox_spec *adoptedBoxSpec) { TImapFEEvent *endEvent = new TImapFEEvent(UpdatedIMAPmailbox, // function to call this, // access to current entry/context (void *) adoptedBoxSpec, TRUE); // storage passed // now owned by msg folder if (endEvent) fFEEventQueue->AdoptEventToEnd(endEvent); else HandleMemoryFailure(); } void TNavigatorImapConnection::UpdateMailboxStatus(mailbox_spec *adoptedBoxSpec) { TImapFEEvent *endEvent = new TImapFEEvent(UpdatedIMAPmailboxStatus, // function to call this, // access to current entry/context (void *) adoptedBoxSpec, TRUE); // storage passed // now owned by msg folder if (endEvent) fFEEventQueue->AdoptEventToEnd(endEvent); else HandleMemoryFailure(); } void TNavigatorImapConnection::UploadMessage() { TImapFEEvent *endEvent = new TImapFEEvent(UploadMessageEvent, // function to call this, // access to current entry/context nil, TRUE); // we are the knights who say nyet! if (endEvent) { fFEEventQueue->AdoptEventToEnd(endEvent); WaitForMessageUploadToComplete(); } else HandleMemoryFailure(); } static void UploadMessageFileHandler(void *blockingConnectionVoid, void *msgInfo) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); UploadMessageInfo *uploadMsgInfo = (UploadMessageInfo*) msgInfo; uint32 bytesRead = 0; int ioStatus = 0; if (! uploadMsgInfo->dataBuffer) { // use 4k buffer uploadMsgInfo->dataBuffer = (char *) XP_ALLOC((kOutputBufferSize << 1)+1); } XP_ASSERT(uploadMsgInfo->fileId && uploadMsgInfo->dataBuffer); if(!uploadMsgInfo->fileId || !uploadMsgInfo->dataBuffer) { if (uploadMsgInfo->fileId) XP_FileClose(uploadMsgInfo->fileId); XP_FREEIF(uploadMsgInfo->dataBuffer); XP_FREEIF(uploadMsgInfo); XP_InterruptContext(ce->window_id); return; } XP_ASSERT(uploadMsgInfo->bytesRemain > 0); bytesRead = XP_FileRead(uploadMsgInfo->dataBuffer, (kOutputBufferSize << 1), uploadMsgInfo->fileId); *(uploadMsgInfo->dataBuffer+bytesRead) = 0; ioStatus = NET_BlockingWrite(ce->socket, uploadMsgInfo->dataBuffer, bytesRead); // PR_LOG(IMAP, out, ("%s", uploadMsgInfo->dataBuffer)); uploadMsgInfo->bytesRemain -= bytesRead; if (uploadMsgInfo->bytesRemain <= 0) { ioStatus = NET_BlockingWrite(ce->socket, CRLF, XP_STRLEN(CRLF)); imapConnection->NotifyMessageUploadMonitor(); } else { TImapFEEvent *uploadMsgFromFileEvent = new TImapFEEvent(UploadMessageFileHandler, (void *)imapConnection, (void *)msgInfo, TRUE); if (uploadMsgFromFileEvent) { imapConnection->GetFEEventQueue().AdoptEventToEnd(uploadMsgFromFileEvent); } else { imapConnection->HandleMemoryFailure(); } } } XP_Bool TNavigatorImapConnection::NewMailDetected() { return fNewMail; } void TNavigatorImapConnection::ParseIMAPandCheckForNewMail(char *buff /* = NULL */) { XP_Bool oldMail = fNewMail; if (buff) GetServerStateParser().ParseIMAPServerResponse(buff); else GetServerStateParser().ParseIMAPServerResponse(GetOutputBuffer()); int32 numOfMessagesInFlagState = fFlagState->GetNumberOfMessages(); if ((GetServerStateParser().NumberOfMessages() != numOfMessagesInFlagState) && (numOfMessagesInFlagState > 0)) { fNewMail = TRUE; if (!fFlagState->IsLastMessageUnseen()) fNewMail = FALSE; } else { fNewMail = FALSE; } if (fNewMail != oldMail) { if (fNewMail) fCurrentBiffState = MSG_BIFF_NewMail; else fCurrentBiffState = MSG_BIFF_NoMail; SendSetBiffIndicatorEvent(fCurrentBiffState); } } void TNavigatorImapConnection::UploadMessageFromFile(const char *srcFilePath, const char *mailboxName, imapMessageFlagsType flags) { XP_StatStruct st; if (-1 != XP_Stat (srcFilePath, &st, xpFileToPost)) { char *escapedName = CreateEscapedMailboxName(mailboxName); if (!escapedName) { XP_FREEIF(escapedName); HandleMemoryFailure(); return; } /* status command only available on IMAP4rev1 and beyond * lets try the status first to get the next UID */ MessageKey newkey = MSG_MESSAGEKEYNONE; int ioStatus; char flagString[70]; long bytesRead = 0; setup_message_flags_string(flagString, flags, SupportsUserDefinedFlags()); IncrementCommandTagNumber(); PR_snprintf (GetOutputBuffer(), kOutputBufferSize, "%s append \"%s\" (%s) {%ld}" CRLF, GetServerCommandTag(), escapedName, flagString, (long) st.st_size); ioStatus = WriteLineToSocket(GetOutputBuffer()); ParseIMAPandCheckForNewMail(); UploadMessageInfo *msgInfo = (UploadMessageInfo*) XP_ALLOC(sizeof(UploadMessageInfo)); if (!msgInfo) { HandleMemoryFailure(); return; } msgInfo->bytesRemain = st.st_size; msgInfo->newMsgID = newkey; msgInfo->dataBuffer = 0; msgInfo->fileId = XP_FileOpen(srcFilePath, xpFileToPost, XP_FILE_READ_BIN); TImapFEEvent *uploadMsgFromFileEvent = new TImapFEEvent(UploadMessageFileHandler, (void *)this, (void *)msgInfo, TRUE); if (uploadMsgFromFileEvent) { fFEEventQueue->AdoptEventToEnd(uploadMsgFromFileEvent); PR_Sleep(PR_INTERVAL_NO_WAIT); WaitForMessageUploadToComplete(); ParseIMAPandCheckForNewMail(); if (GetServerStateParser().LastCommandSuccessful() && MSG_IsSaveDraftDeliveryState(GetActiveEntry()->URL_s->fe_data)) { GetServerStateParser().SetIMAPstate(TImapServerState::kFolderSelected); // *** sucks..... if ((GetServerStateParser().GetSelectedMailboxName() && XP_STRCMP(GetServerStateParser().GetSelectedMailboxName(), escapedName))) { if (fCloseNeededBeforeSelect) Close(); SelectMailbox(escapedName); } const char *messageId = NULL; if (GetActiveEntry()->URL_s->fe_data) messageId = MSG_GetMessageIdFromState( GetActiveEntry()->URL_s->fe_data); if (GetServerStateParser().LastCommandSuccessful() && messageId) { char *tmpBuffer = PR_smprintf ( "SEARCH SEEN HEADER Message-ID %s", messageId); GetServerStateParser().ResetSearchResultSequence(); Search(tmpBuffer, TRUE, FALSE); if (GetServerStateParser().LastCommandSuccessful()) { TSearchResultIterator *searchResult = GetServerStateParser().CreateSearchResultIterator(); newkey = searchResult->GetNextMessageNumber(); delete searchResult; if (newkey != MSG_MESSAGEKEYNONE) MSG_SetIMAPMessageUID(newkey, GetActiveEntry()->URL_s->fe_data); XP_Bool bSuc = GetServerStateParser().LastCommandSuccessful(); } } } XP_FREEIF(msgInfo->dataBuffer); XP_FileClose(msgInfo->fileId); XP_FREEIF(msgInfo); } else { HandleMemoryFailure(); } XP_FREEIF(escapedName); } } void TNavigatorImapConnection::NotifyMessageFlags( imapMessageFlagsType flags, MessageKey key) { tFlagsKeyStruct *keyAndFlag = (tFlagsKeyStruct *) XP_ALLOC( sizeof(tFlagsKeyStruct)); keyAndFlag->flags = flags; keyAndFlag->key = key; TImapFEEvent *flagsEvent = new TImapFEEvent(NotifyMessageFlagsEvent, // function to call this, // access to current entry/context keyAndFlag, TRUE); // storage passed if (flagsEvent) fFEEventQueue->AdoptEventToEnd(flagsEvent); else HandleMemoryFailure(); } void TNavigatorImapConnection::NotifySearchHit(const char *hitLine) { if (!fNotifyHit) return; TImapFEEvent *hitEvent = new TImapFEEvent(AddSearchResultEvent, // function to call this, // access to current entry/context (void *)hitLine, TRUE); if (hitEvent) { fFEEventQueue->AdoptEventToEnd(hitEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } static void ArbitraryHeadersEvent(void *blockingConnectionVoid, void *valueVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); GenericInfo *value = (GenericInfo *)valueVoid; value->c = MSG_GetArbitraryHeadersForHost(ce->window_id->mailMaster, value->hostName); imapConnection->NotifyEventCompletionMonitor(); } // Returns a newly allocated, space-delimited string of arbitrary headers // which are used for filters on this host. char *TNavigatorImapConnection::GetArbitraryHeadersToDownload() { GenericInfo *value = (GenericInfo *)XP_ALLOC(sizeof(GenericInfo)); if (!value) return NULL; value->c = NULL; value->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); if (!value->hostName) { XP_FREE(value->c); XP_FREE(value); return NULL; } TImapFEEvent *headerEvent = new TImapFEEvent(ArbitraryHeadersEvent, // function to call this, // access to current entry/context value, FALSE); if (headerEvent) { fFEEventQueue->AdoptEventToEnd(headerEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); char *rv = NULL; if (!DeathSignalReceived()) rv = value->c; XP_FREE(value->hostName); XP_FREE(value); return rv; } void TNavigatorImapConnection::HeaderFetchCompleted() { TImapFEEvent *endEvent = new TImapFEEvent(HeaderFetchCompletedEvent, // function to call this, // access to current entry/context nil, TRUE); // we are the knights who say nyet! if (endEvent) fFEEventQueue->AdoptEventToEnd(endEvent); else HandleMemoryFailure(); } // Please call only with a single message ID void TNavigatorImapConnection::Bodystructure(const char *messageId, XP_Bool idIsUid) { IncrementCommandTagNumber(); char commandString[256]; if (idIsUid) XP_STRCPY(commandString, "%s UID fetch"); else XP_STRCPY(commandString, "%s fetch"); XP_STRCAT(commandString, " %s (BODYSTRUCTURE)"); XP_STRCAT(commandString,CRLF); const char *commandTag = GetServerCommandTag(); int protocolStringSize = XP_STRLEN(commandString) + XP_STRLEN(messageId) + XP_STRLEN(commandTag) + 1; char *protocolString = (char *) XP_ALLOC( protocolStringSize ); if (protocolString) { PR_snprintf(protocolString, // string to create protocolStringSize, // max size commandString, // format string commandTag, // command tag messageId); int ioStatus = WriteLineToSocket(protocolString); ParseIMAPandCheckForNewMail(protocolString); XP_FREE(protocolString); } else HandleMemoryFailure(); } void TNavigatorImapConnection::PipelinedFetchMessageParts(const char *uid, TIMAPMessagePartIDArray *parts) { // assumes no chunking // build up a string to fetch char *stringToFetch = NULL, *what = NULL; int32 currentPartNum = 0; while ((parts->GetNumParts() > currentPartNum) && !DeathSignalReceived()) { TIMAPMessagePartID *currentPart = parts->GetPart(currentPartNum); if (currentPart) { // Do things here depending on the type of message part // Append it to the fetch string if (currentPartNum > 0) StrAllocCat(stringToFetch, " "); switch (currentPart->GetFields()) { case kMIMEHeader: what = PR_smprintf("BODY[%s.MIME]",currentPart->GetPartNumberString()); if (what) { StrAllocCat(stringToFetch, what); XP_FREE(what); } else HandleMemoryFailure(); break; case kRFC822HeadersOnly: if (currentPart->GetPartNumberString()) { what = PR_smprintf("BODY[%s.HEADER]", currentPart->GetPartNumberString()); if (what) { StrAllocCat(stringToFetch, what); XP_FREE(what); } else HandleMemoryFailure(); } else { // headers for the top-level message StrAllocCat(stringToFetch, "BODY[HEADER]"); } break; default: XP_ASSERT(FALSE); // we should only be pipelining MIME headers and Message headers break; } } currentPartNum++; } // Run the single, pipelined fetch command if ((parts->GetNumParts() > 0) && !DeathSignalReceived() && !GetPseudoInterrupted() && stringToFetch) { IncrementCommandTagNumber(); char *commandString = PR_smprintf("%s UID fetch %s (%s)%s", GetServerCommandTag(), uid, stringToFetch, CRLF); if (commandString) { int ioStatus = WriteLineToSocket(commandString); ParseIMAPandCheckForNewMail(commandString); XP_FREE(commandString); } else HandleMemoryFailure(); XP_FREE(stringToFetch); } } void TNavigatorImapConnection::FetchMessage(const char *messageIds, eFetchFields whatToFetch, XP_Bool idIsUid, uint32 startByte, uint32 endByte, char *part) { IncrementCommandTagNumber(); char commandString[350]; if (idIsUid) XP_STRCPY(commandString, "%s UID fetch"); else XP_STRCPY(commandString, "%s fetch"); switch (whatToFetch) { case kEveryThingRFC822: if (fTrackingTime) AdjustChunkSize(); // we started another segment fStartTime = XP_TIME(); // save start of download time fTrackingTime = TRUE; if (GetServerStateParser().ServerHasIMAP4Rev1Capability()) { if (GetServerStateParser().GetCapabilityFlag() & kHasXSenderCapability) XP_STRCAT(commandString, " %s (XSENDER UID RFC822.SIZE BODY[]"); else XP_STRCAT(commandString, " %s (UID RFC822.SIZE BODY[]"); } else { if (GetServerStateParser().GetCapabilityFlag() & kHasXSenderCapability) XP_STRCAT(commandString, " %s (XSENDER UID RFC822.SIZE RFC822"); else XP_STRCAT(commandString, " %s (UID RFC822.SIZE RFC822"); } if (endByte > 0) { // if we are retrieving chunks char *byterangeString = PR_smprintf("<%ld.%ld>",startByte,endByte); if (byterangeString) { XP_STRCAT(commandString, byterangeString); XP_FREE(byterangeString); } } XP_STRCAT(commandString, ")"); break; case kEveryThingRFC822Peek: { char *formatString = ""; uint32 server_capabilityFlags = GetServerStateParser().GetCapabilityFlag(); if (server_capabilityFlags & kIMAP4rev1Capability) { // use body[].peek since rfc822.peek is not in IMAP4rev1 if (server_capabilityFlags & kHasXSenderCapability) formatString = " %s (XSENDER UID RFC822.SIZE BODY.PEEK[])"; else formatString = " %s (UID RFC822.SIZE BODY.PEEK[])"; } else { if (server_capabilityFlags & kHasXSenderCapability) formatString = " %s (XSENDER UID RFC822.SIZE RFC822.peek)"; else formatString = " %s (UID RFC822.SIZE RFC822.peek)"; } XP_STRCAT(commandString, formatString); } break; case kHeadersRFC822andUid: if (GetServerStateParser().ServerHasIMAP4Rev1Capability()) { if (gOptimizedHeaders) { char *headersToDL = NULL; char *arbitraryHeaders = GetArbitraryHeadersToDownload(); if (arbitraryHeaders) { headersToDL = PR_smprintf("%s %s",IMAP_DB_HEADERS,arbitraryHeaders); XP_FREE(arbitraryHeaders); } else { headersToDL = PR_smprintf("%s",IMAP_DB_HEADERS); } if (headersToDL) { char *what = PR_smprintf(" BODY.PEEK[HEADER.FIELDS (%s)])", headersToDL); if (what) { XP_STRCAT(commandString, " %s (UID RFC822.SIZE FLAGS"); XP_STRCAT(commandString, what); XP_FREE(what); } else { XP_STRCAT(commandString, " %s (UID RFC822.SIZE BODY.PEEK[HEADER] FLAGS)"); } XP_FREE(headersToDL); } else { XP_STRCAT(commandString, " %s (UID RFC822.SIZE BODY.PEEK[HEADER] FLAGS)"); } } else XP_STRCAT(commandString, " %s (UID RFC822.SIZE BODY.PEEK[HEADER] FLAGS)"); } else XP_STRCAT(commandString, " %s (UID RFC822.SIZE RFC822.HEADER FLAGS)"); break; case kUid: XP_STRCAT(commandString, " %s (UID)"); break; case kFlags: XP_STRCAT(commandString, " %s (FLAGS)"); break; case kRFC822Size: XP_STRCAT(commandString, " %s (RFC822.SIZE)"); break; case kRFC822HeadersOnly: if (GetServerStateParser().ServerHasIMAP4Rev1Capability()) { if (part) { XP_STRCAT(commandString, " %s (BODY["); char *what = PR_smprintf("%s.HEADER])", part); if (what) { XP_STRCAT(commandString, what); XP_FREE(what); } else HandleMemoryFailure(); } else { // headers for the top-level message XP_STRCAT(commandString, " %s (BODY[HEADER])"); } } else XP_STRCAT(commandString, " %s (RFC822.HEADER)"); break; case kMIMEPart: XP_STRCAT(commandString, " %s (BODY[%s]"); if (endByte > 0) { // if we are retrieving chunks char *byterangeString = PR_smprintf("<%ld.%ld>",startByte,endByte); if (byterangeString) { XP_STRCAT(commandString, byterangeString); XP_FREE(byterangeString); } } XP_STRCAT(commandString, ")"); break; case kMIMEHeader: XP_STRCAT(commandString, " %s (BODY[%s.MIME])"); break; }; XP_STRCAT(commandString,CRLF); // since messageIds can be infinitely long, use a dynamic buffer rather than the fixed one const char *commandTag = GetServerCommandTag(); int protocolStringSize = XP_STRLEN(commandString) + XP_STRLEN(messageIds) + XP_STRLEN(commandTag) + 1 + (part ? XP_STRLEN(part) : 0); char *protocolString = (char *) XP_ALLOC( protocolStringSize ); if (protocolString) { if ((whatToFetch == kMIMEPart) || (whatToFetch == kMIMEHeader)) { PR_snprintf(protocolString, // string to create protocolStringSize, // max size commandString, // format string commandTag, // command tag messageIds, part); } else { PR_snprintf(protocolString, // string to create protocolStringSize, // max size commandString, // format string commandTag, // command tag messageIds); } int ioStatus = WriteLineToSocket(protocolString); ParseIMAPandCheckForNewMail(protocolString); XP_FREE(protocolString); } else HandleMemoryFailure(); } void TNavigatorImapConnection::FetchTryChunking(const char *messageIds, eFetchFields whatToFetch, XP_Bool idIsUid, char *part, uint32 downloadSize) { GetServerStateParser().SetTotalDownloadSize(downloadSize); if (fFetchByChunks && GetServerStateParser().ServerHasIMAP4Rev1Capability() && (downloadSize > (uint32) fChunkThreshold)) { uint32 startByte = 0; GetServerStateParser().ClearLastFetchChunkReceived(); while (!DeathSignalReceived() && !GetPseudoInterrupted() && !GetServerStateParser().GetLastFetchChunkReceived() && GetServerStateParser().ContinueParse()) { uint32 sizeToFetch = startByte + fChunkSize > downloadSize ? downloadSize - startByte : fChunkSize; FetchMessage(messageIds, whatToFetch, idIsUid, startByte, sizeToFetch, part); startByte += sizeToFetch; } // Only abort the stream if this is a normal message download // Otherwise, let the body shell abort the stream. if ((whatToFetch == TIMAP4BlockingConnection::kEveryThingRFC822) && ((startByte > 0 && (startByte < downloadSize) && (DeathSignalReceived() || GetPseudoInterrupted())) || !GetServerStateParser().ContinueParse())) { AbortMessageDownLoad(); PseudoInterrupt(FALSE); } } else { // small message, or (we're not chunking and not doing bodystructure), // or the server is not rev1. // Just fetch the whole thing. FetchMessage(messageIds, whatToFetch,idIsUid, 0, 0, part); } } PRMonitor *TNavigatorImapConnection::fFindingMailboxesMonitor = nil; PRMonitor *TNavigatorImapConnection::fUpgradeToSubscriptionMonitor = nil; XP_Bool TNavigatorImapConnection::fHaveWeEverCheckedForSubscriptionUpgrade = FALSE; MSG_BIFF_STATE TNavigatorImapConnection::fCurrentBiffState = MSG_BIFF_Unknown; PRMonitor *TIMAPHostInfo::gCachedHostInfoMonitor = nil; TIMAPHostInfo *TIMAPHostInfo::fHostInfoList = nil; void TNavigatorImapConnection::ResetCachedConnectionInfo() { fCurrentBiffState = MSG_BIFF_Unknown; TIMAPHostInfo::ResetAll(); } static void WriteLineEvent(void *blockingConnectionVoid, void *sockInfoVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); TIMAPSocketInfo *sockInfo = (TIMAPSocketInfo *)sockInfoVoid; if (sockInfo && sockInfo->writeLine) { LIBNET_LOCK(); sockInfo->writeStatus = (int) NET_BlockingWrite(ce->socket, sockInfo->writeLine, XP_STRLEN(sockInfo->writeLine)); FREEIF(sockInfo->writeLine); LIBNET_UNLOCK(); } else { XP_ASSERT(FALSE); } imapConnection->NotifyEventCompletionMonitor(); } static void ReadFirstLineFromSocket(void *blockingConnectionVoid, void *sockInfoVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); TIMAPSocketInfo *sockInfo = (TIMAPSocketInfo *)sockInfoVoid; if (sockInfo) { LIBNET_LOCK(); Bool pause = sockInfo->GetPauseForRead(); sockInfo->readStatus = NET_BufferedReadLine(sockInfo->GetIOSocket(), sockInfo->GetNewLineBuffer(), sockInfo->GetInputSocketBuffer(), sockInfo->GetInputBufferSize(), &pause); LIBNET_UNLOCK(); } else { XP_ASSERT(FALSE); } imapConnection->NotifyEventCompletionMonitor(); } #ifndef XP_WIN16 #define DO_THREADED_IMAP_SOCKET_IO #endif #ifndef DO_THREADED_IMAP_SOCKET_IO // do socket writes in the mozilla thread int TNavigatorImapConnection::WriteLineToSocket(char *line) { int returnValue = 0; if (!DeathSignalReceived()) { if (line) fSocketInfo->writeLine = XP_STRDUP(line); fSocketInfo->writeStatus = 0; TImapFEEvent *feWriteLineEvent = new TImapFEEvent(WriteLineEvent, // function to call this, // for access to current // entry and monitor fSocketInfo, TRUE); fFEEventQueue->AdoptEventToEnd(feWriteLineEvent); PR_Sleep(PR_INTERVAL_NO_WAIT); // wait here for the read first line io to finish WaitForFEEventCompletion(); int socketError = SOCKET_ERRNO; Log("NET","WR",line); //PR_LOG(IMAP, out, ("WR: %s",line)); int writeStatus = fSocketInfo->writeStatus; if(writeStatus <= 0) { if (fCurrentEntry && fCurrentEntry->URL_s) fCurrentEntry->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, socketError); returnValue = MK_TCP_WRITE_ERROR; GetServerStateParser().SetConnected(FALSE); } else PR_Sleep(PR_INTERVAL_NO_WAIT); } return returnValue; } char *TNavigatorImapConnection::CreateNewLineFromSocket() { Bool pauseForRead = TRUE; char *newLine = nil; int socketReadStatus = -1; while (pauseForRead && !DeathSignalReceived()) { fSocketInfo->SetIOSocket(GetIOSocket()); fSocketInfo->SetPauseForRead(pauseForRead); fSocketInfo->SetReadStatus(socketReadStatus); fSocketInfo->SetInputSocketBuffer(&fInputSocketBuffer); fSocketInfo->SetInputBufferSize(&fInputBufferSize); TImapFEEvent *feReadFirstLineEvent = new TImapFEEvent(ReadFirstLineFromSocket, // function to call this, // for access to current // entry and monitor fSocketInfo, TRUE); fFEEventQueue->AdoptEventToEnd(feReadFirstLineEvent); PR_Sleep(PR_INTERVAL_NO_WAIT); // wait here for the read first line io to finish WaitForFEEventCompletion(); if (fSocketInfo->newLine) newLine = XP_STRDUP(fSocketInfo->newLine); socketReadStatus = fSocketInfo->GetReadStatus(); //if (*(fSocketInfo->GetNewLineBuffer())) newLine = XP_STRDUP(*(socketStuff->pNewLine)); //FREEIF(socketStuff->pNewLine); int socketError = PR_GetError(); if (newLine) Log("NET","RD", newLine); //PR_LOG(IMAP, out, ("RD: %s",newLine)); if (socketReadStatus <= 0) // error { #ifdef KMCENTEE_DEBUG XP_ASSERT(socketError != PR_NOT_CONNECTED_ERROR); #endif if (socketError == PR_WOULD_BLOCK_ERROR || socketError == PR_NOT_CONNECTED_ERROR) { pauseForRead = TRUE; WaitForIOCompletion(); } else { LIBNET_LOCK(); if (fCurrentEntry && fCurrentEntry->URL_s) fCurrentEntry->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, socketError); pauseForRead = FALSE; socketReadStatus = MK_TCP_READ_ERROR; GetServerStateParser().SetConnected(FALSE); LIBNET_UNLOCK(); } } else if (pauseForRead && !newLine) WaitForIOCompletion(); else pauseForRead = FALSE; } // the comments for NET_BufferedReadLine say that newLine is allocated // before it is set. TO me this means that the caller owns the newLine // storage. But I have seen it stepped on and we have assertion failures // when we delete it! char *bogusNewLine = NULL; if (newLine) { bogusNewLine = XP_STRDUP(newLine); if (bogusNewLine) StrAllocCat(bogusNewLine, CRLF); if (!bogusNewLine) HandleMemoryFailure(); } SetConnectionStatus(socketReadStatus); FREEIF(newLine); return bogusNewLine; } #else // not Win16: do it the usual way int TNavigatorImapConnection::WriteLineToSocket(char *line) { int returnValue = 0; if (!DeathSignalReceived()) { LIBNET_LOCK(); int writeStatus = 0; // check for death signal again inside LIBNET_LOCK because // we may have locked on LIBNET_LOCK because this context // was being interrupted and interrupting the context means // DeathSignalReceived is true and fCurrentEntry was deleted. if (!DeathSignalReceived()) writeStatus = (int) NET_BlockingWrite(fCurrentEntry->socket, line, XP_STRLEN(line)); int socketError = SOCKET_ERRNO; Log("NET","WR",line); //PR_LOG(IMAP, out, ("WR: %s",line)); LIBNET_UNLOCK(); if(writeStatus <= 0) { LIBNET_LOCK(); if (!DeathSignalReceived()) fCurrentEntry->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, socketError); LIBNET_UNLOCK(); returnValue = MK_TCP_WRITE_ERROR; GetServerStateParser().SetConnected(FALSE); } else PR_Sleep(PR_INTERVAL_NO_WAIT); } return returnValue; } char *TNavigatorImapConnection::CreateNewLineFromSocket() { Bool pauseForRead = TRUE; char *newLine = nil; int socketReadStatus = -1; while (pauseForRead && !DeathSignalReceived()) { LIBNET_LOCK(); // check for death signal again inside LIBNET_LOCK because // we may have locked on LIBNET_LOCK because this context // was being interrupted and interrupting the context means // DeathSignalReceived is true and fCurrentEntry was deleted. if (!DeathSignalReceived()) socketReadStatus = NET_BufferedReadLine(GetIOSocket(), &newLine, &fInputSocketBuffer, &fInputBufferSize, &pauseForRead); int socketError = PR_GetError(); if (newLine) Log("NET","RD",newLine); //PR_LOG(IMAP, out, ("RD: %s",newLine)); LIBNET_UNLOCK(); if (socketReadStatus <= 0) // error { #ifdef KMCENTEE_DEBUG XP_ASSERT(socketError != PR_NOT_CONNECTED_ERROR); #endif if (socketError == PR_WOULD_BLOCK_ERROR || socketError == PR_NOT_CONNECTED_ERROR) { pauseForRead = TRUE; WaitForIOCompletion(); } else { LIBNET_LOCK(); if (!DeathSignalReceived()) { fCurrentEntry->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, socketError); } pauseForRead = FALSE; socketReadStatus = MK_TCP_READ_ERROR; GetServerStateParser().SetConnected(FALSE); LIBNET_UNLOCK(); } } else if (pauseForRead && !newLine) WaitForIOCompletion(); else pauseForRead = FALSE; } // the comments for NET_BufferedReadLine say that newLine is allocated // before it is set. TO me this means that the caller owns the newLine // storage. But I have seen it stepped on and we have assertion failures // when we delete it! char *bogusNewLine = NULL; if (newLine) { bogusNewLine = XP_STRDUP(newLine); if (bogusNewLine) StrAllocCat(bogusNewLine, CRLF); if (!bogusNewLine) HandleMemoryFailure(); } SetConnectionStatus(socketReadStatus); return bogusNewLine; } #endif // not win16 void TNavigatorImapConnection::SetIOSocket(PRFileDesc *socket) { PR_EnterMonitor(GetDataMemberMonitor()); fIOSocket = socket; PR_ExitMonitor(GetDataMemberMonitor()); } PRFileDesc * TNavigatorImapConnection::GetIOSocket() { PRFileDesc * returnSocket; PR_EnterMonitor(GetDataMemberMonitor()); returnSocket = fIOSocket; PR_ExitMonitor(GetDataMemberMonitor()); return returnSocket; } void TNavigatorImapConnection::Logout() { TIMAP4BlockingConnection::Logout(); SetIOSocket(0); } void TNavigatorImapConnection::SetOutputStream(NET_StreamClass *outputStream) { PR_EnterMonitor(GetDataMemberMonitor()); fOutputStream = outputStream; PR_ExitMonitor(GetDataMemberMonitor()); } NET_StreamClass *TNavigatorImapConnection::GetOutputStream() { NET_StreamClass *returnStream; PR_EnterMonitor(GetDataMemberMonitor()); returnStream = fOutputStream; PR_ExitMonitor(GetDataMemberMonitor()); return returnStream; } void TNavigatorImapConnection::SetIsSafeToDie() // called by TellThreadToDie { PR_EnterMonitor(fPermissionToDieMonitor); fIsSafeToDie = TRUE; PR_Notify(fPermissionToDieMonitor); PR_ExitMonitor(fPermissionToDieMonitor); } void TNavigatorImapConnection::SetBlockingThread(PRThread *blockingThread) { fBlockingThread = blockingThread; } ActiveEntry *TNavigatorImapConnection::GetActiveEntry() { return fCurrentEntry; } // (Use Get/SetCurrentEntryStatus()) for passing values back to libnet about the currently running ActiveEntry. void TNavigatorImapConnection::SetCurrentEntryStatus(int status) { // There was a deadlock when the imap thread was waiting for this libnet lock and the mozilla thread // held this lock, finished the current url and tried to launch another url from the url exit function. // This caused the mozilla thread to never release the LIBNET_LOCK. XP_ASSERT(GetConnectionStatus() >= 0); LIBNET_LOCK(); if (!DeathSignalReceived()) { ActiveEntry *ce = GetActiveEntry(); if (ce) ce->status = status; } LIBNET_UNLOCK(); } int TNavigatorImapConnection::GetCurrentEntryStatus() { int returnStatus = 0; LIBNET_LOCK(); if (!DeathSignalReceived()) { ActiveEntry *ce = GetActiveEntry(); if (ce) returnStatus = ce->status; } LIBNET_UNLOCK(); return returnStatus; } TCP_ConData **TNavigatorImapConnection::GetTCPConData() { return &fTCPConData; } void TNavigatorImapConnection::EstablishServerConnection() { // let the fe-thread start the connection since we are using // old non-thread safe functions to do it. // call NET_FinishConnect until we are connected or errored out while (!DeathSignalReceived() && (GetConnectionStatus() == MK_WAITING_FOR_CONNECTION)) { TImapFEEvent *feFinishConnectionEvent = new TImapFEEvent(FinishIMAPConnection, // function to call this, // for access to current // entry and monitor nil, TRUE); fFEEventQueue->AdoptEventToEnd(feFinishConnectionEvent); PR_Sleep(PR_INTERVAL_NO_WAIT); // wait here for the connection finish io to finish WaitForFEEventCompletion(); if (!DeathSignalReceived() && (GetConnectionStatus() == MK_WAITING_FOR_CONNECTION)) WaitForIOCompletion(); } if (GetConnectionStatus() == MK_CONNECTED) { // get the one line response from the IMAP server char *serverResponse = CreateNewLineFromSocket(); if (serverResponse) { if (!XP_STRNCASECMP(serverResponse, "* OK", 4)) { SetConnectionStatus(0); preAuthSucceeded = FALSE; //if (!XP_STRNCASECMP(serverResponse, "* OK Netscape IMAP4rev1 Service 3.0", 35)) // GetServerStateParser().SetServerIsNetscape30Server(); } else if (!XP_STRNCASECMP(serverResponse, "* PREAUTH", 9)) { // we've been pre-authenticated. // we can skip the whole password step, right into the // kAuthenticated state GetServerStateParser().PreauthSetAuthenticatedState(); // tell the master that we're authenticated XP_Bool loginSucceeded = TRUE; TImapFEEvent *alertEvent = new TImapFEEvent(msgSetUserAuthenticated, // function to call this, // access to current entry (void *)loginSucceeded, TRUE); if (alertEvent) { fFEEventQueue->AdoptEventToEnd(alertEvent); } else HandleMemoryFailure(); preAuthSucceeded = TRUE; if (fCurrentBiffState == MSG_BIFF_Unknown) { fCurrentBiffState = MSG_BIFF_NoMail; SendSetBiffIndicatorEvent(fCurrentBiffState); } if (GetServerStateParser().GetCapabilityFlag() == kCapabilityUndefined) Capability(); if ( !(GetServerStateParser().GetCapabilityFlag() & (kIMAP4Capability | kIMAP4rev1Capability | kIMAP4other) ) ) { AlertUserEvent_UsingId(MK_MSG_IMAP_SERVER_NOT_IMAP4); SetCurrentEntryStatus(-1); SetConnectionStatus(-1); // stop netlib preAuthSucceeded = FALSE; } else { ProcessAfterAuthenticated(); // the connection was a success SetConnectionStatus(0); } } else { preAuthSucceeded = FALSE; SetConnectionStatus(MK_BAD_CONNECT); } fNeedGreeting = FALSE; FREEIF(serverResponse); } else SetConnectionStatus(MK_BAD_CONNECT); } if ((GetConnectionStatus() < 0) && !DeathSignalReceived()) { if (!MSG_Biff_Master_NikiCallingGetNewMail()) AlertUserEvent_UsingId(MK_BAD_CONNECT); } } void TNavigatorImapConnection::ProcessStoreFlags(const char *messageIds, XP_Bool idsAreUids, imapMessageFlagsType flags, XP_Bool addFlags) { if (!flags) return; char *flagString; XP_Bool userF = GetServerStateParser().SupportsUserFlags(); if (!userF && (flags == kImapMsgForwardedFlag || flags == kImapMsgMDNSentFlag)) // if only flag, and not supported bail out return; if (addFlags) flagString = XP_STRDUP("+Flags ("); else flagString = XP_STRDUP("-Flags ("); if (flags & kImapMsgSeenFlag) StrAllocCat(flagString, "\\Seen "); if (flags & kImapMsgAnsweredFlag) StrAllocCat(flagString, "\\Answered "); if (flags & kImapMsgFlaggedFlag) StrAllocCat(flagString, "\\Flagged "); if (flags & kImapMsgDeletedFlag) StrAllocCat(flagString, "\\Deleted "); if (flags & kImapMsgDraftFlag) StrAllocCat(flagString, "\\Draft "); if ((flags & kImapMsgForwardedFlag) && userF) StrAllocCat(flagString, "Forwarded "); // if supported if ((flags & kImapMsgMDNSentFlag) && userF) StrAllocCat(flagString, "MDNSent "); // if supported // replace the final space with ')' *(flagString + XP_STRLEN(flagString) - 1) = ')'; Store(messageIds, flagString, idsAreUids); FREEIF(flagString); } void TNavigatorImapConnection::ProcessAfterAuthenticated() { // if we're a netscape server, and we haven't got the admin url, get it if (!TIMAPHostInfo::GetHostHasAdminURL(fCurrentUrl->GetUrlHost())) { if (GetServerStateParser().GetCapabilityFlag() & kXServerInfoCapability) { XServerInfo(); if (GetServerStateParser().LastCommandSuccessful()) { TImapFEEvent *alertEvent = new TImapFEEvent(msgSetMailServerURLs, // function to call this, // access to current entry (void *) fCurrentUrl->GetUrlHost(), TRUE); if (alertEvent) { fFEEventQueue->AdoptEventToEnd(alertEvent); // WaitForFEEventCompletion(); } else HandleMemoryFailure(); } } else if (GetServerStateParser().GetCapabilityFlag() & kHasXNetscapeCapability) { Netscape(); if (GetServerStateParser().LastCommandSuccessful()) { TImapFEEvent *alertEvent = new TImapFEEvent(msgSetMailAccountURL, // function to call this, // access to current entry (void *) fCurrentUrl->GetUrlHost(), TRUE); if (alertEvent) { fFEEventQueue->AdoptEventToEnd(alertEvent); // WaitForFEEventCompletion(); } else HandleMemoryFailure(); } } } if (GetServerStateParser().ServerHasNamespaceCapability() && TIMAPHostInfo::GetNamespacesOverridableForHost(fCurrentUrl->GetUrlHost())) { Namespace(); } } XP_Bool TNavigatorImapConnection::TryToLogon() { int32 logonTries = 0; XP_Bool loginSucceeded = FALSE; do { char *passwordForHost = TIMAPHostInfo::GetPasswordForHost(fCurrentUrl->GetUrlHost()); XP_Bool imapPasswordIsNew = FALSE; XP_Bool setUserAuthenticatedIsSafe = FALSE; ActiveEntry *ce = GetActiveEntry(); MSG_Master *master = (ce) ? ce->window_id->mailMaster : 0; const char *userName = MSG_GetIMAPHostUsername(master, fCurrentUrl->GetUrlHost()); if (userName && !passwordForHost) { TImapFEEvent *fePasswordEvent = new TImapFEEvent(GetPasswordEventFunction, this, (void*) userName, TRUE); if (fePasswordEvent) { fFEEventQueue->AdoptEventToEnd(fePasswordEvent); IMAP_YIELD(PR_INTERVAL_NO_WAIT); // wait here password prompt to finish WaitForFEEventCompletion(); imapPasswordIsNew = TRUE; passwordForHost = TIMAPHostInfo::GetPasswordForHost(fCurrentUrl->GetUrlHost()); } else HandleMemoryFailure(); } if (userName && passwordForHost) { XP_Bool prefBool = FALSE; XP_Bool lastReportingErrors = GetServerStateParser().GetReportingErrors(); GetServerStateParser().SetReportingErrors(FALSE); // turn off errors - we'll put up our own. PREF_GetBoolPref("mail.auth_login", &prefBool); if (prefBool) { if (GetServerStateParser().GetCapabilityFlag() == kCapabilityUndefined) Capability(); if (GetServerStateParser().GetCapabilityFlag() & kHasAuthLoginCapability) { AuthLogin (userName, passwordForHost); logonTries++; // I think this counts against most servers as a logon try } else InsecureLogin(userName, passwordForHost); } else InsecureLogin(userName, passwordForHost); if (!GetServerStateParser().LastCommandSuccessful()) { // login failed! // if we failed because of an interrupt, then do not bother the user if (!DeathSignalReceived()) { AlertUserEvent_UsingId(XP_MSG_IMAP_LOGIN_FAILED); if (passwordForHost != NULL) { TIMAPHostInfo::SetPasswordForHost(fCurrentUrl->GetUrlHost(), NULL); } fCurrentBiffState = MSG_BIFF_Unknown; SendSetBiffIndicatorEvent(fCurrentBiffState); } HandleCurrentUrlError(); // SetConnectionStatus(-1); // stop netlib } else // login succeeded { MSG_SetIMAPHostPassword(ce->window_id->mailMaster, fCurrentUrl->GetUrlHost(), passwordForHost); imapPasswordIsNew = !TIMAPHostInfo::GetPasswordVerifiedOnline(fCurrentUrl->GetUrlHost()); if (imapPasswordIsNew) TIMAPHostInfo::SetPasswordVerifiedOnline(fCurrentUrl->GetUrlHost()); NET_SetPopPassword2(passwordForHost); // bug 53380 if (imapPasswordIsNew) { if (fCurrentBiffState == MSG_BIFF_Unknown) { fCurrentBiffState = MSG_BIFF_NoMail; SendSetBiffIndicatorEvent(fCurrentBiffState); } LIBNET_LOCK(); if (!DeathSignalReceived()) { setUserAuthenticatedIsSafe = GetActiveEntry()->URL_s->msg_pane != NULL; MWContext *context = GetActiveEntry()->window_id; if (context) FE_RememberPopPassword(context, SECNAV_MungeString(passwordForHost)); } LIBNET_UNLOCK(); } loginSucceeded = TRUE; } GetServerStateParser().SetReportingErrors(lastReportingErrors); // restore it if (loginSucceeded && imapPasswordIsNew) { TImapFEEvent *alertEvent = new TImapFEEvent(msgSetUserAuthenticated, // function to call this, // access to current entry (void *)loginSucceeded, TRUE); if (alertEvent) { fFEEventQueue->AdoptEventToEnd(alertEvent); // WaitForFEEventCompletion(); } else HandleMemoryFailure(); } if (loginSucceeded) { ProcessAfterAuthenticated(); } } else { // The user hit "Cancel" on the dialog box //AlertUserEvent("Login cancelled."); HandleCurrentUrlError(); SetCurrentEntryStatus(-1); SetConnectionStatus(-1); // stop netlib break; } } while (!loginSucceeded && ++logonTries < 4); if (!loginSucceeded) { fCurrentBiffState = MSG_BIFF_Unknown; SendSetBiffIndicatorEvent(fCurrentBiffState); HandleCurrentUrlError(); SetConnectionStatus(-1); // stop netlib } return loginSucceeded; } void TNavigatorImapConnection::ProcessCurrentURL() { XP_ASSERT(GetFEEventQueue().NumberOfEventsInQueue() == 0); if (fCurrentUrl->ValidIMAPUrl()) { // Reinitialize the parser GetServerStateParser().InitializeState(); // establish the connection and login if ((GetConnectionStatus() == MK_WAITING_FOR_CONNECTION) || fNeedGreeting) EstablishServerConnection(); if (!DeathSignalReceived() && (GetConnectionStatus() >= 0) && (GetServerStateParser().GetIMAPstate() == TImapServerState::kNonAuthenticated)) { /* if we got here, the server's greeting should not have been PREAUTH */ if (GetServerStateParser().GetCapabilityFlag() == kCapabilityUndefined) Capability(); if ( !(GetServerStateParser().GetCapabilityFlag() & (kIMAP4Capability | kIMAP4rev1Capability | kIMAP4other) ) ) { AlertUserEvent_UsingId(MK_MSG_IMAP_SERVER_NOT_IMAP4); SetCurrentEntryStatus(-1); SetConnectionStatus(-1); // stop netlib } else { TryToLogon(); } } if (!DeathSignalReceived() && (GetConnectionStatus() >= 0)) { FindMailboxesIfNecessary(); if (fCurrentUrl->GetUrlIMAPstate() == TIMAPUrl::kAuthenticatedStateURL) ProcessAuthenticatedStateURL(); else // must be kSelectedStateURL ProcessSelectedStateURL(); // The URL has now been processed if (GetConnectionStatus() < 0) HandleCurrentUrlError(); if (GetServerStateParser().LastCommandSuccessful()) SetCurrentEntryStatus(0); SetConnectionStatus(-1); // stop netlib if (DeathSignalReceived()) HandleCurrentUrlError(); } else HandleCurrentUrlError(); } else { if (!fCurrentUrl->ValidIMAPUrl()) AlertUserEvent("Invalid IMAP4 url"); SetCurrentEntryStatus(-1); SetConnectionStatus(-1); } PseudoInterrupt(FALSE); // clear this, because we must be done interrupting? //ProgressUpdateEvent("Current IMAP Url Completed"); } void TNavigatorImapConnection::FolderHeaderDump(uint32 *msgUids, uint32 msgCount) { FolderMsgDump(msgUids, msgCount, TIMAP4BlockingConnection::kHeadersRFC822andUid); if (GetServerStateParser().NumberOfMessages()) { HeaderFetchCompleted(); // if an INBOX exists on this host //char *inboxName = TIMAPHostInfo::GetOnlineInboxPathForHost(fCurrentUrl->GetUrlHost()); char *inboxName = PR_smprintf("INBOX"); if (inboxName) { // if this was the inbox, turn biff off if (!XP_STRCASECMP(GetServerStateParser().GetSelectedMailboxName(), inboxName)) { fCurrentBiffState = MSG_BIFF_NoMail; SendSetBiffIndicatorEvent(fCurrentBiffState); } XP_FREE(inboxName); } } } void TNavigatorImapConnection::ShowProgress() { if (fProgressStringId) { char *progressString = NULL; const char *mailboxName = GetServerStateParser().GetSelectedMailboxName(); progressString = PR_sprintf_append(progressString, XP_GetString(fProgressStringId), (mailboxName) ? mailboxName : "", ++fProgressIndex, fProgressCount); if (progressString) PercentProgressUpdateEvent(progressString,(100*(fProgressIndex))/fProgressCount ); FREEIF(progressString); } } void TNavigatorImapConnection::FolderMsgDump(uint32 *msgUids, uint32 msgCount, TIMAP4BlockingConnection::eFetchFields fields) { switch (fields) { case TIMAP4BlockingConnection::kHeadersRFC822andUid: fProgressStringId = XP_RECEIVING_MESSAGE_HEADERS_OF; break; case TIMAP4BlockingConnection::kFlags: fProgressStringId = XP_RECEIVING_MESSAGE_FLAGS_OF; break; default: fProgressStringId = XP_FOLDER_RECEIVING_MESSAGE_OF; break; } fProgressIndex = 0; fProgressCount = msgCount; FolderMsgDumpRecurse(msgUids, msgCount, fields); fProgressStringId = 0; } uint32 TNavigatorImapConnection::CountMessagesInIdString(const char *idString) { uint32 numberOfMessages = 0; char *uidString = XP_STRDUP(idString); if (uidString) { // This is in the form ,, or : 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) { numberOfMessages++; saveStartToken++; } } numberOfMessages++; isRange = (curChar == ':'); if (isRange) saveStartToken = curToken + 1; } XP_FREE(uidString); } return numberOfMessages; } char *IMAP_AllocateImapUidString(uint32 *msgUids, uint32 msgCount) { int blocksAllocated = 1; char *returnIdString = (char *) XP_ALLOC(256); if (returnIdString) { char *currentidString = returnIdString; *returnIdString = 0; int32 startSequence = (msgCount > 0) ? msgUids[0] : -1; int32 curSequenceEnd = startSequence; uint32 total = msgCount; for (uint32 keyIndex=0; returnIdString && (keyIndex < total); keyIndex++) { int32 curKey = msgUids[keyIndex]; int32 nextKey = (keyIndex + 1 < total) ? msgUids[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,", msgUids[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; } void TNavigatorImapConnection::FolderMsgDumpRecurse(uint32 *msgUids, uint32 msgCount, TIMAP4BlockingConnection::eFetchFields fields) { PastPasswordCheckEvent(); if (msgCount <= 200) { char *idString = IMAP_AllocateImapUidString(msgUids, msgCount); // 20 * 200 if (idString) { FetchMessage(idString, fields, TRUE); // msg ids are uids XP_FREE(idString); } else HandleMemoryFailure(); } else { FolderMsgDumpRecurse(msgUids, 200, fields); FolderMsgDumpRecurse(msgUids + 200, msgCount - 200, fields); } } void TNavigatorImapConnection::SendSetBiffIndicatorEvent(MSG_BIFF_STATE /*newState*/) { /* TImapFEEvent *biffIndicatorEvent = new TImapFEEvent(SetBiffIndicator, // function to call (void *) (unsigned long) newState, this); if (newState == MSG_BIFF_NewMail) fMailToFetch = TRUE; else fMailToFetch = FALSE; if (biffIndicatorEvent) { fFEEventQueue->AdoptEventToEnd(biffIndicatorEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); */ } // We get called to see if there is mail waiting for us at the server, even if it may have been // read elsewhere. We just want to know if we should download headers or not. XP_Bool TNavigatorImapConnection::CheckNewMail() { if (!fGetHeaders) return FALSE; // use biff instead return TRUE; /* if (!fMailToFetch && fNewMail) { // This was the old style biff, we don't want this anymore fMailToFetch = TRUE; if (fCurrentBiffState != MSG_BIFF_NewMail) { fCurrentBiffState = MSG_BIFF_NewMail; //SendSetBiffIndicatorEvent(fCurrentBiffState); deadlock } } return fMailToFetch; */ } // Use the noop to tell the server we are still here, and therefore we are willing to receive // status updates. The recent or exists response from the server could tell us that there is // more mail waiting for us, but we need to check the flags of the mail and the high water mark // to make sure that we do not tell the user that there is new mail when perhaps they have // already read it in another machine. void TNavigatorImapConnection::PeriodicBiff() { MSG_BIFF_STATE startingState = fCurrentBiffState; //if (fCurrentBiffState == MSG_BIFF_NewMail) // dont do this since another computer could be used to read messages // return; if (!fFlagState) return; if (GetServerStateParser().GetIMAPstate() == TImapServerState::kFolderSelected) { Noop(); // check the latest number of messages if (GetServerStateParser().NumberOfMessages() != fFlagState->GetNumberOfMessages()) { uint32 id = GetServerStateParser().HighestRecordedUID() + 1; char fetchStr[100]; // only update flags uint32 added = 0, deleted = 0; deleted = fFlagState->GetNumberOfDeletedMessages(); added = fFlagState->GetNumberOfMessages(); if (!added || (added == deleted)) // empty keys, get them all id = 1; //sprintf(fetchStr, "%ld:%ld", id, id + GetServerStateParser().NumberOfMessages() - fFlagState->GetNumberOfMessages()); sprintf(fetchStr, "%ld:*", id); FetchMessage(fetchStr, TIMAP4BlockingConnection::kFlags, TRUE); if (fFlagState && ((uint32) fFlagState->GetHighestNonDeletedUID() >= id) && fFlagState->IsLastMessageUnseen()) fCurrentBiffState = MSG_BIFF_NewMail; else fCurrentBiffState = MSG_BIFF_NoMail; } else fCurrentBiffState = MSG_BIFF_NoMail; } else fCurrentBiffState = MSG_BIFF_Unknown; if (startingState != fCurrentBiffState) SendSetBiffIndicatorEvent(fCurrentBiffState); } void TNavigatorImapConnection::HandleCurrentUrlError() { if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSelectFolder) { // let the front end know the select failed so they // don't leave the view without a database. mailbox_spec *notSelectedSpec = (mailbox_spec *) XP_CALLOC(1, sizeof( mailbox_spec)); if (notSelectedSpec) { notSelectedSpec->allocatedPathName = fCurrentUrl->CreateCanonicalSourceFolderPathString(); notSelectedSpec->hostName = fCurrentUrl->GetUrlHost(); notSelectedSpec->folderSelected = FALSE; notSelectedSpec->flagState = NULL; notSelectedSpec->onlineVerified = FALSE; UpdatedMailboxSpec(notSelectedSpec); } } } XP_Bool TNavigatorImapConnection::RenameHierarchyByHand(const char *oldParentMailboxName, const char *newParentMailboxName) { XP_Bool renameSucceeded = TRUE; fDeletableChildren = XP_ListNew(); XP_Bool nonHierarchicalRename = ((GetServerStateParser().GetCapabilityFlag() & kNoHierarchyRename) || MailboxIsNoSelectMailbox(oldParentMailboxName)); if (fDeletableChildren) { fHierarchyNameState = kDeleteSubFoldersInProgress; TIMAPNamespace *ns = TIMAPHostInfo::GetNamespaceForMailboxForHost(fCurrentUrl->GetUrlHost(), oldParentMailboxName); // for delimiter if (!ns) { if (!XP_STRCASECMP(oldParentMailboxName, "INBOX")) ns = TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(fCurrentUrl->GetUrlHost(), kPersonalNamespace); } if (ns) { char *pattern = PR_smprintf("%s%c*", oldParentMailboxName,ns->GetDelimiter()); if (pattern) { if (TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost())) Lsub(pattern); else List(pattern); XP_FREE(pattern); } } fHierarchyNameState = kNoOperationInProgress; if (GetServerStateParser().LastCommandSuccessful()) renameSucceeded = RenameMailboxRespectingSubscriptions(oldParentMailboxName,newParentMailboxName, TRUE); // rename this, and move subscriptions int numberToDelete = XP_ListCount(fDeletableChildren); for (int childIndex = 1; (childIndex <= numberToDelete) && renameSucceeded; childIndex++) { // the imap parser has already converted to a non UTF7 string in the canonical // format so convert it back char *currentName = (char *) XP_ListGetObjectNum(fDeletableChildren, childIndex); if (currentName) { char *serverName = fCurrentUrl->AllocateServerPath(currentName); char *convertedName = serverName ? CreateUtf7ConvertedString(serverName, TRUE) : (char *)NULL; FREEIF(serverName); currentName = convertedName; // currentName not leaked, deleted in XP_ListDestroy } // calculate the new name and do the rename char *newChildName = (char *) XP_ALLOC(XP_STRLEN(currentName) + XP_STRLEN(newParentMailboxName) + 1); if (newChildName) { XP_STRCPY(newChildName, newParentMailboxName); XP_STRCAT(newChildName, currentName + XP_STRLEN(oldParentMailboxName)); RenameMailboxRespectingSubscriptions(currentName,newChildName, nonHierarchicalRename); // pass in xNonHierarchicalRename to // determine if we should really reanme, // or just move subscriptions renameSucceeded = GetServerStateParser().LastCommandSuccessful(); XP_FREE(newChildName); } FREEIF(currentName); } XP_ListDestroy(fDeletableChildren); fDeletableChildren = NULL; } return renameSucceeded; } XP_Bool TNavigatorImapConnection::DeleteSubFolders(const char *selectedMailbox) { XP_Bool deleteSucceeded = TRUE; fDeletableChildren = XP_ListNew(); if (fDeletableChildren) { fHierarchyNameState = kDeleteSubFoldersInProgress; char *pattern = PR_smprintf("%s%c*", selectedMailbox, fCurrentUrl->GetOnlineSubDirSeparator()); if (pattern) { List(pattern); XP_FREE(pattern); } fHierarchyNameState = kNoOperationInProgress; // this should be a short list so perform a sequential search for the // longest name mailbox. Deleting the longest first will hopefully prevent the server // from having problems about deleting parents int numberToDelete = XP_ListCount(fDeletableChildren); deleteSucceeded = GetServerStateParser().LastCommandSuccessful(); for (int outerIndex = 1; (outerIndex <= numberToDelete) && deleteSucceeded; outerIndex++) { char *longestName = NULL; for (int innerIndex = 1; innerIndex <= XP_ListCount(fDeletableChildren); innerIndex++) { char *currentName = (char *) XP_ListGetObjectNum(fDeletableChildren, innerIndex); if (!longestName || (XP_STRLEN(longestName) < XP_STRLEN(currentName) ) ) longestName = currentName; } XP_ASSERT(longestName); XP_ListRemoveObject(fDeletableChildren, longestName); // the imap parser has already converted to a non UTF7 string in the canonical // format so convert it back if (longestName) { char *serverName = fCurrentUrl->AllocateServerPath(longestName); char *convertedName = serverName ? CreateUtf7ConvertedString(serverName, TRUE) : 0; FREEIF(serverName); XP_FREE(longestName); longestName = convertedName; } // some imap servers include the selectedMailbox in the list of // subfolders of the selectedMailbox. Check for this so we don't delete // the selectedMailbox (usually the trash and doing an empty trash) // The Cyrus imap server ignores the "INBOX.Trash" constraining string passed // to the list command. Be defensive and make sure we only delete children of the trash if (longestName && XP_STRCMP(selectedMailbox, longestName) && !XP_STRNCMP(selectedMailbox, longestName, XP_STRLEN(selectedMailbox))) { XP_Bool deleted = DeleteMailboxRespectingSubscriptions(longestName); if (deleted) FolderDeleted(longestName); deleteSucceeded = deleted; } FREEIF(longestName); } XP_ListDestroy(fDeletableChildren); fDeletableChildren = NULL; } return deleteSucceeded; } void TNavigatorImapConnection::ProcessMailboxUpdate(XP_Bool handlePossibleUndo, XP_Bool /*fromBiffUpdate*/) { if (DeathSignalReceived()) return; // fetch the flags and uids of all existing messages or new ones if (!DeathSignalReceived() && GetServerStateParser().NumberOfMessages()) { if (handlePossibleUndo) { // undo any delete flags we may have asked to char *undoIds = fCurrentUrl->CreateListOfMessageIdsString(); if (undoIds && *undoIds) { // if this string started with a '-', then this is an undo of a delete // if its a '+' its a redo if (*undoIds == '-') Store(undoIds+1, "-FLAGS (\\Deleted)", TRUE); // most servers will fail silently on a failure, deal with it? else if (*undoIds == '+') Store(undoIds+1, "+FLAGS (\\Deleted)", TRUE); // most servers will fail silently on a failure, deal with it? else XP_ASSERT(FALSE); } FREEIF(undoIds); } if (!DeathSignalReceived()) // only expunge if not reading messages manually and before fetching new { if (fFlagState && (fFlagState->GetNumberOfDeletedMessages() >= 40) && fDeleteModelIsMoveToTrash) Expunge(); // might be expensive, test for user cancel } // make the parser record these flags char fetchStr[100]; uint32 added = 0, deleted = 0; if (fFlagState) { added = fFlagState->GetNumberOfMessages(); deleted = fFlagState->GetNumberOfDeletedMessages(); } if (fFlagState && (!added || (added == deleted))) FetchMessage("1:*", TIMAP4BlockingConnection::kFlags, TRUE); // id string shows uids else { sprintf(fetchStr, "%ld:*", GetServerStateParser().HighestRecordedUID() + 1); FetchMessage(fetchStr, TIMAP4BlockingConnection::kFlags, TRUE); // only new messages please } } else if (!DeathSignalReceived()) GetServerStateParser().ResetFlagInfo(0); mailbox_spec *new_spec = GetServerStateParser().CreateCurrentMailboxSpec(); if (new_spec && !DeathSignalReceived()) { MWContext *ct = NULL; LIBNET_LOCK(); if (!DeathSignalReceived()) { // if this is an expunge url, libmsg will not ask for headers if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kExpungeFolder) new_spec->box_flags |= kJustExpunged; ct = new_spec->connection->fCurrentEntry->window_id; ct->imapURLPane = MSG_FindPane(fCurrentEntry->window_id, MSG_ANYPANE); } LIBNET_UNLOCK(); if (ct) { if (!ct->currentIMAPfolder) ct->currentIMAPfolder = (MSG_IMAPFolderInfoMail *) MSG_FindImapFolder(ct->imapURLPane, fCurrentUrl->GetUrlHost(), "INBOX"); // use real folder name /* MSG_IMAPFolderInfoMail *imapInbox; if (urlFolder) imapInbox = URL_s->msg_pane->GetMaster()->FindImapMailFolder(urlFolder->GetHostName(), "INBOX"); else imapInbox = URL_s->msg_pane->GetMaster()->FindImapMailFolder("INBOX"); if (imapInbox) imapInbox->NotifyFolderLoaded(URL_s->msg_pane); */ PR_EnterMonitor(fWaitForBodyIdsMonitor); UpdatedMailboxSpec(new_spec); } } else if (!new_spec) HandleMemoryFailure(); // Block until libmsg decides whether to download headers or not. uint32 *msgIdList; uint32 msgCount = 0; if (!DeathSignalReceived()) { WaitForPotentialListOfMsgsToFetch(&msgIdList, msgCount); if (new_spec) PR_ExitMonitor(fWaitForBodyIdsMonitor); if (msgIdList && !DeathSignalReceived() && GetServerStateParser().LastCommandSuccessful()) { FolderHeaderDump(msgIdList, msgCount); FREEIF( msgIdList); } // this might be bogus, how are we going to do pane notification and stuff when we fetch bodies without // headers! } // wait for a list of bodies to fetch. if (!DeathSignalReceived() && GetServerStateParser().LastCommandSuccessful()) { WaitForPotentialListOfMsgsToFetch(&msgIdList, msgCount); if ( msgCount && !DeathSignalReceived() && GetServerStateParser().LastCommandSuccessful()) { FolderMsgDump(msgIdList, msgCount, TIMAP4BlockingConnection::kEveryThingRFC822Peek); FREEIF(msgIdList); } } if (DeathSignalReceived()) GetServerStateParser().ResetFlagInfo(0); } void TNavigatorImapConnection::ProcessSelectedStateURL() { char *mailboxName = fCurrentUrl->CreateServerSourceFolderPathString(); if (mailboxName) { char *convertedName = CreateUtf7ConvertedString(mailboxName, TRUE); XP_FREE(mailboxName); mailboxName = convertedName; } if (mailboxName && !DeathSignalReceived()) { //char *inboxName = TIMAPHostInfo::GetOnlineInboxPathForHost(fCurrentUrl->GetUrlHost()); char *inboxName = PR_smprintf("INBOX"); TInboxReferenceCount inboxCounter(inboxName ? !XP_STRCASECMP(mailboxName, inboxName) : FALSE); FREEIF(inboxName); if ( (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kBiff) && (inboxCounter.GetInboxUsageCount() > 1)) { FREEIF(mailboxName); return; } XP_Bool selectIssued = FALSE; if (GetServerStateParser().GetIMAPstate() == TImapServerState::kFolderSelected) { if (GetServerStateParser().GetSelectedMailboxName() && XP_STRCMP(GetServerStateParser().GetSelectedMailboxName(), mailboxName)) { // we are selected in another folder if (fCloseNeededBeforeSelect) Close(); if (GetServerStateParser().LastCommandSuccessful()) { selectIssued = TRUE; AutoSubscribeToMailboxIfNecessary(mailboxName); SelectMailbox(mailboxName); } } else if (!GetServerStateParser().GetSelectedMailboxName()) { // why are we in the selected state with no box name? SelectMailbox(mailboxName); selectIssued = TRUE; } else { // get new message counts, if any, from server ProgressEventFunction_UsingId (MK_IMAP_STATUS_SELECTING_MAILBOX); if (fNeedNoop) { Noop(); // I think this is needed when we're using a cached connection fNeedNoop = FALSE; } } } else { // go to selected state AutoSubscribeToMailboxIfNecessary(mailboxName); SelectMailbox(mailboxName); selectIssued = TRUE; } if (selectIssued) { RefreshACLForFolderIfNecessary(mailboxName); } XP_Bool uidValidityOk = TRUE; if (GetServerStateParser().LastCommandSuccessful() && selectIssued && (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kSelectFolder) && (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kLiteSelectFolder)) { uid_validity_info *uidStruct = (uid_validity_info *) XP_ALLOC(sizeof(uid_validity_info)); if (uidStruct) { uidStruct->returnValidity = kUidUnknown; uidStruct->hostName = fCurrentUrl->GetUrlHost(); uidStruct->canonical_boxname = fCurrentUrl->CreateCanonicalSourceFolderPathString(); TImapFEEvent *endEvent = new TImapFEEvent(GetStoredUIDValidity, // function to call this, // access to current entry/context (void *) uidStruct, FALSE); // retain storage ownership here if (endEvent) { fFEEventQueue->AdoptEventToEnd(endEvent); WaitForFEEventCompletion(); // error on the side of caution, if the fe event fails to set uidStruct->returnValidity, then assume that UIDVALIDITY // did not role. This is a common case event for attachments that are fetched within a browser context. if (!DeathSignalReceived()) uidValidityOk = (uidStruct->returnValidity == kUidUnknown) || (uidStruct->returnValidity == GetServerStateParser().FolderUID()); } else HandleMemoryFailure(); FREEIF(uidStruct->canonical_boxname); XP_FREE(uidStruct); } else HandleMemoryFailure(); } if (GetServerStateParser().LastCommandSuccessful() && !DeathSignalReceived() && (uidValidityOk || fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDeleteAllMsgs)) { if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kLiteSelectFolder) { if (GetServerStateParser().LastCommandSuccessful()) { TImapFEEvent *liteselectEvent = new TImapFEEvent(LiteSelectEvent, // function to call (void *) this, NULL, TRUE); if (liteselectEvent) { fFEEventQueue->AdoptEventToEnd(liteselectEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); // need to update the mailbox count - is this a good place? ProcessMailboxUpdate(FALSE); // handle uidvalidity change } } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kMsgFetch) { char *messageIdString = fCurrentUrl->CreateListOfMessageIdsString(); if (messageIdString) { // we dont want to send the flags back in a group // GetServerStateParser().ResetFlagInfo(0); if (HandlingMultipleMessages(messageIdString)) { // multiple messages, fetch them all FetchMessage(messageIdString, TIMAP4BlockingConnection::kEveryThingRFC822Peek, fCurrentUrl->MessageIdsAreUids()); } else { // A single message ID // First, let's see if we're requesting a specific MIME part char *imappart = fCurrentUrl->GetIMAPPartToFetch(); if (imappart) { if (fCurrentUrl->MessageIdsAreUids()) { // We actually want a specific MIME part of the message. // The Body Shell will generate it, even though we haven't downloaded it yet. TIMAPBodyShell *foundShell = TIMAPHostInfo::FindShellInCacheForHost(fCurrentUrl->GetUrlHost(), GetServerStateParser().GetSelectedMailboxName(), messageIdString); if (!foundShell) { // The shell wasn't in the cache. Deal with this case later. Log("SHELL",NULL,"Loading part, shell not found in cache!"); //PR_LOG(IMAP, out, ("BODYSHELL: Loading part, shell not found in cache!")); // The parser will extract the part number from the current URL. Bodystructure(messageIdString, fCurrentUrl->MessageIdsAreUids()); } else { Log("SHELL", NULL, "Loading Part, using cached shell."); //PR_LOG(IMAP, out, ("BODYSHELL: Loading part, using cached shell.")); foundShell->SetConnection(this); GetServerStateParser().UseCachedShell(foundShell); foundShell->Generate(imappart); GetServerStateParser().UseCachedShell(NULL); } } else { // Message IDs are not UIDs. XP_ASSERT(FALSE); } XP_FREE(imappart); } else { // downloading a single message: try to do it by bodystructure, and/or do it by chunks uint32 messageSize = GetMessageSize(messageIdString, fCurrentUrl->MessageIdsAreUids()); // We need to check the format_out bits to see if we are allowed to leave out parts, // or if we are required to get the whole thing. Some instances where we are allowed // to do it by parts: when viewing a message, or its source // Some times when we're NOT allowed: when forwarding a message, saving it, moving it, etc. ActiveEntry *ce = GetActiveEntry(); XP_Bool allowedToBreakApart = (ce && !DeathSignalReceived()) ? ce->URL_s->allow_content_change : FALSE; if (gMIMEOnDemand && allowedToBreakApart && !GetShouldFetchAllParts() && GetServerStateParser().ServerHasIMAP4Rev1Capability() && (messageSize > (uint32) gMIMEOnDemandThreshold) && !fCurrentUrl->MimePartSelectorDetected()) // if a ?part=, don't do BS. { // OK, we're doing bodystructure // Before fetching the bodystructure, let's check our body shell cache to see if // we already have it around. TIMAPBodyShell *foundShell = NULL; SetContentModified(TRUE); // This will be looked at by the cache if (fCurrentUrl->MessageIdsAreUids()) { foundShell = TIMAPHostInfo::FindShellInCacheForHost(fCurrentUrl->GetUrlHost(), GetServerStateParser().GetSelectedMailboxName(), messageIdString); if (foundShell) { Log("SHELL",NULL,"Loading message, using cached shell."); //PR_LOG(IMAP, out, ("BODYSHELL: Loading message, using cached shell.")); foundShell->SetConnection(this); GetServerStateParser().UseCachedShell(foundShell); foundShell->Generate(NULL); GetServerStateParser().UseCachedShell(NULL); } } if (!foundShell) Bodystructure(messageIdString, fCurrentUrl->MessageIdsAreUids()); } else { // Not doing bodystructure. Fetch the whole thing, and try to do // it by parts. SetContentModified(FALSE); FetchTryChunking(messageIdString, TIMAP4BlockingConnection::kEveryThingRFC822, fCurrentUrl->MessageIdsAreUids(), NULL, messageSize); } } } FREEIF( messageIdString); } else HandleMemoryFailure(); } else if ((fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSelectFolder) || (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kExpungeFolder)) { if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kExpungeFolder) Expunge(); ProcessMailboxUpdate(TRUE); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kMsgHeader) { char *messageIdString = fCurrentUrl->CreateListOfMessageIdsString(); if (messageIdString) { // we don't want to send the flags back in a group // GetServerStateParser().ResetFlagInfo(0); FetchMessage(messageIdString, TIMAP4BlockingConnection::kHeadersRFC822andUid, fCurrentUrl->MessageIdsAreUids()); FREEIF( messageIdString); } else HandleMemoryFailure(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSearch) { char *searchCriteriaString = fCurrentUrl->CreateSearchCriteriaString(); if (searchCriteriaString) { Search(searchCriteriaString,fCurrentUrl->MessageIdsAreUids()); // drop the results on the floor for now FREEIF( searchCriteriaString); } else HandleMemoryFailure(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDeleteMsg) { char *messageIdString = fCurrentUrl->CreateListOfMessageIdsString(); if (messageIdString) { if (HandlingMultipleMessages(messageIdString)) ProgressEventFunction_UsingId (XP_IMAP_DELETING_MESSAGES); else ProgressEventFunction_UsingId(XP_IMAP_DELETING_MESSAGE); Store(messageIdString, "+FLAGS (\\Deleted)", fCurrentUrl->MessageIdsAreUids()); if (GetServerStateParser().LastCommandSuccessful()) { struct delete_message_struct *deleteMsg = (struct delete_message_struct *) XP_ALLOC (sizeof(struct delete_message_struct)); // convert name back from utf7 utf_name_struct *nameStruct = (utf_name_struct *) XP_ALLOC(sizeof(utf_name_struct)); char *convertedCanonicalName = NULL; if (nameStruct) { nameStruct->toUtf7Imap = FALSE; nameStruct->sourceString = (unsigned char *) GetServerStateParser().GetSelectedMailboxName(); nameStruct->convertedString = NULL; ConvertImapUtf7(nameStruct, NULL); if (nameStruct->convertedString) convertedCanonicalName = fCurrentUrl->AllocateCanonicalPath((char *) nameStruct->convertedString); } deleteMsg->onlineFolderName = convertedCanonicalName; deleteMsg->deleteAllMsgs = FALSE; deleteMsg->msgIdString = messageIdString; // storage adopted, do not delete messageIdString = nil; // deleting nil is ok TImapFEEvent *deleteEvent = new TImapFEEvent(NotifyMessageDeletedEvent, // function to call (void *) this, (void *) deleteMsg, TRUE); if (deleteEvent) fFEEventQueue->AdoptEventToEnd(deleteEvent); else HandleMemoryFailure(); } FREEIF( messageIdString); } else HandleMemoryFailure(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDeleteAllMsgs) { uint32 numberOfMessages = GetServerStateParser().NumberOfMessages(); if (numberOfMessages) { char messageIdString[100]; // enough for bazillion msgs sprintf(messageIdString, "1:*"); Store(messageIdString, "+FLAGS (\\Deleted)", FALSE); // use sequence #'s if (GetServerStateParser().LastCommandSuccessful()) Expunge(); // expunge messages with deleted flag if (GetServerStateParser().LastCommandSuccessful()) { struct delete_message_struct *deleteMsg = (struct delete_message_struct *) XP_ALLOC (sizeof(struct delete_message_struct)); // convert name back from utf7 utf_name_struct *nameStruct = (utf_name_struct *) XP_ALLOC(sizeof(utf_name_struct)); char *convertedCanonicalName = NULL; if (nameStruct) { nameStruct->toUtf7Imap = FALSE; nameStruct->sourceString = (unsigned char *) GetServerStateParser().GetSelectedMailboxName(); nameStruct->convertedString = NULL; ConvertImapUtf7(nameStruct, NULL); if (nameStruct->convertedString) convertedCanonicalName = fCurrentUrl->AllocateCanonicalPath((char *) nameStruct->convertedString); } deleteMsg->onlineFolderName = convertedCanonicalName; deleteMsg->deleteAllMsgs = TRUE; deleteMsg->msgIdString = nil; TImapFEEvent *deleteEvent = new TImapFEEvent(NotifyMessageDeletedEvent, // function to call (void *) this, (void *) deleteMsg, TRUE); if (deleteEvent) fFEEventQueue->AdoptEventToEnd(deleteEvent); else HandleMemoryFailure(); } } DeleteSubFolders(mailboxName); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kAppendMsgFromFile) { char *sourceMessageFile = XP_STRDUP(GetActiveEntry()->URL_s->post_data); char *mailboxName = fCurrentUrl->CreateServerSourceFolderPathString(); if (mailboxName) { char *convertedName = CreateUtf7ConvertedString(mailboxName, TRUE); XP_FREE(mailboxName); mailboxName = convertedName; } if (mailboxName) { UploadMessageFromFile(sourceMessageFile, mailboxName, kImapMsgSeenFlag); } else HandleMemoryFailure(); FREEIF( sourceMessageFile ); FREEIF( mailboxName ); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kAddMsgFlags) { char *messageIdString = fCurrentUrl->CreateListOfMessageIdsString(); if (messageIdString) { ProcessStoreFlags(messageIdString, fCurrentUrl->MessageIdsAreUids(), fCurrentUrl->GetMsgFlags(), TRUE); FREEIF( messageIdString); /* if ( !DeathSignalReceived() && GetServerStateParser().Connected() && !GetServerStateParser().SyntaxError()) { //if (fCurrentUrl->GetMsgFlags() & kImapMsgDeletedFlag) // Expunge(); // expunge messages with deleted flag Check(); // flush servers flag state } */ } else HandleMemoryFailure(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSubtractMsgFlags) { char *messageIdString = fCurrentUrl->CreateListOfMessageIdsString(); if (messageIdString) { ProcessStoreFlags(messageIdString, fCurrentUrl->MessageIdsAreUids(), fCurrentUrl->GetMsgFlags(), FALSE); FREEIF( messageIdString); } else HandleMemoryFailure(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSetMsgFlags) { char *messageIdString = fCurrentUrl->CreateListOfMessageIdsString(); if (messageIdString) { ProcessStoreFlags(messageIdString, fCurrentUrl->MessageIdsAreUids(), fCurrentUrl->GetMsgFlags(), TRUE); ProcessStoreFlags(messageIdString, fCurrentUrl->MessageIdsAreUids(), ~fCurrentUrl->GetMsgFlags(), FALSE); FREEIF( messageIdString); } else HandleMemoryFailure(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kBiff) { PeriodicBiff(); } else if ((fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineCopy) || (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineMove)) { char *messageIdString = fCurrentUrl->CreateListOfMessageIdsString(); char *destinationMailbox = fCurrentUrl->CreateServerDestinationFolderPathString(); if (destinationMailbox) { char *convertedName = CreateUtf7ConvertedString(destinationMailbox, TRUE); XP_FREE(destinationMailbox); destinationMailbox = convertedName; } if (messageIdString && destinationMailbox) { if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineMove) { if (HandlingMultipleMessages(messageIdString)) ProgressEventFunction_UsingIdWithString (XP_IMAP_MOVING_MESSAGES_TO, destinationMailbox); else ProgressEventFunction_UsingIdWithString (XP_IMAP_MOVING_MESSAGE_TO, destinationMailbox); } else { if (HandlingMultipleMessages(messageIdString)) ProgressEventFunction_UsingIdWithString (XP_IMAP_COPYING_MESSAGES_TO, destinationMailbox); else ProgressEventFunction_UsingIdWithString (XP_IMAP_COPYING_MESSAGE_TO, destinationMailbox); } Copy(messageIdString, destinationMailbox, fCurrentUrl->MessageIdsAreUids()); FREEIF( destinationMailbox); ImapOnlineCopyState copyState; if (DeathSignalReceived()) copyState = kInterruptedState; else copyState = GetServerStateParser().LastCommandSuccessful() ? kSuccessfulCopy : kFailedCopy; OnlineCopyCompleted(copyState); if (GetServerStateParser().LastCommandSuccessful() && (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineMove)) { Store(messageIdString, "+FLAGS (\\Deleted)", fCurrentUrl->MessageIdsAreUids()); XP_Bool storeSuccessful = GetServerStateParser().LastCommandSuccessful(); OnlineCopyCompleted( storeSuccessful ? kSuccessfulDelete : kFailedDelete); } FREEIF( messageIdString); } else HandleMemoryFailure(); } else if ((fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineCopy) || (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineMove)) { char *messageIdString = fCurrentUrl->CreateListOfMessageIdsString(); if (messageIdString) { fProgressStringId = XP_FOLDER_RECEIVING_MESSAGE_OF; fProgressIndex = 0; fProgressCount = CountMessagesInIdString(messageIdString); FetchMessage(messageIdString, TIMAP4BlockingConnection::kEveryThingRFC822Peek, fCurrentUrl->MessageIdsAreUids()); fProgressStringId = 0; OnlineCopyCompleted( GetServerStateParser().LastCommandSuccessful() ? kSuccessfulCopy : kFailedCopy); if (GetServerStateParser().LastCommandSuccessful() && (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOnlineToOfflineMove)) { Store(messageIdString, "+FLAGS (\\Deleted)", fCurrentUrl->MessageIdsAreUids()); XP_Bool storeSuccessful = GetServerStateParser().LastCommandSuccessful(); OnlineCopyCompleted( storeSuccessful ? kSuccessfulDelete : kFailedDelete); } FREEIF( messageIdString); } else HandleMemoryFailure(); } } else if (GetServerStateParser().LastCommandSuccessful() && !uidValidityOk) ProcessMailboxUpdate(FALSE); // handle uidvalidity change FREEIF( mailboxName); } else if (!DeathSignalReceived()) HandleMemoryFailure(); } void TNavigatorImapConnection::AutoSubscribeToMailboxIfNecessary(const char *mailboxName) { if (fFolderNeedsSubscribing) // we don't know about this folder - we need to subscribe to it / list it. { fHierarchyNameState = kListingForInfoOnly; List(mailboxName); fHierarchyNameState = kNoOperationInProgress; // removing and freeing it as we go. TIMAPMailboxInfo *mb = NULL; int total = XP_ListCount(fListedMailboxList); do { mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); delete mb; } while (mb); // if the mailbox exists (it was returned from the LIST response) if (total > 0) { // Subscribe to it, if the pref says so if (TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost()) && fAutoSubscribeOnOpen) { Subscribe(mailboxName); } // Always LIST it anyway, to get it into the folder lists, // so that we can continue to perform operations on it, at least // for this session. fHierarchyNameState = kNoOperationInProgress; List(mailboxName); } // We should now be subscribed to it, and have it in our folder lists // and panes. Even if something failed, we don't want to try this again. fFolderNeedsSubscribing = FALSE; } } void TNavigatorImapConnection::FindMailboxesIfNecessary() { PR_EnterMonitor(fFindingMailboxesMonitor); // biff should not discover mailboxes XP_Bool foundMailboxesAlready = TIMAPHostInfo::GetHaveWeEverDiscoveredFoldersForHost(fCurrentUrl->GetUrlHost()); if (!foundMailboxesAlready && (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kBiff) && (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kDiscoverAllBoxesUrl) && (fCurrentUrl->GetIMAPurlType() != TIMAPUrl::kUpgradeToSubscription) && !GetSubscribingNow()) { DiscoverMailboxList(); // If we decide to do it, here is where we should check to see if // a namespace exists (personal namespace only?) and possibly // create it if it doesn't exist. } PR_ExitMonitor(fFindingMailboxesMonitor); } // DiscoverMailboxList() is used to actually do the discovery of folders // for a host. This is used both when we initially start up (and re-sync) // and also when the user manually requests a re-sync, by collapsing and // expanding a host in the folder pane. This is not used for the subscribe // pane. // DiscoverMailboxList() also gets the ACLs for each newly discovered folder void TNavigatorImapConnection::DiscoverMailboxList() { SetMailboxDiscoveryStatus(eContinue); if (GetServerStateParser().ServerHasACLCapability()) fHierarchyNameState = kListingForInfoAndDiscovery; else fHierarchyNameState = kNoOperationInProgress; // Pretend that the Trash folder doesn't exist, so we will rediscover it if we need to. TIMAPHostInfo::SetOnlineTrashFolderExistsForHost(fCurrentUrl->GetUrlHost(), FALSE); // iterate through all namespaces and LSUB them. for (int i = TIMAPHostInfo::GetNumberOfNamespacesForHost(fCurrentUrl->GetUrlHost()); i >= 1; i-- ) { TIMAPNamespace *ns = TIMAPHostInfo::GetNamespaceNumberForHost(fCurrentUrl->GetUrlHost(), i); if (ns) { const char *prefix = ns->GetPrefix(); if (prefix) { if (*prefix) // only do it for non-empty namespace prefixes { // Explicitly discover each Namespace, so that we can create subfolders of them, mailbox_spec *boxSpec = (mailbox_spec *) XP_CALLOC(1, sizeof(mailbox_spec) ); if (boxSpec) { boxSpec->folderSelected = FALSE; boxSpec->hostName = fCurrentUrl->GetUrlHost(); boxSpec->connection = this; boxSpec->flagState = NULL; boxSpec->discoveredFromLsub = TRUE; boxSpec->onlineVerified = TRUE; boxSpec->box_flags = kNoselect; boxSpec->hierarchySeparator = ns->GetDelimiter(); boxSpec->allocatedPathName = fCurrentUrl->AllocateCanonicalPath(ns->GetPrefix()); switch (ns->GetType()) { case kPersonalNamespace: boxSpec->box_flags |= kPersonalMailbox; break; case kPublicNamespace: boxSpec->box_flags |= kPublicMailbox; break; case kOtherUsersNamespace: boxSpec->box_flags |= kOtherUsersMailbox; break; default: // (kUnknownNamespace) break; } DiscoverMailboxSpec(boxSpec); } else HandleMemoryFailure(); } // now do the folders within this namespace char *pattern = NULL, *pattern2 = NULL; if (TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost())) pattern = PR_smprintf("%s*", prefix); else { pattern = PR_smprintf("%s%%", prefix); char delimiter = ns->GetDelimiter(); if (delimiter) { // delimiter might be NIL, in which case there's no hierarchy anyway pattern2 = PR_smprintf("%s%%%c%%", prefix, delimiter); } } if (pattern) { if (TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost())) // && !GetSubscribingNow()) should never get here from subscribe pane Lsub(pattern); else { List(pattern); if (pattern2) { List(pattern2); XP_FREE(pattern2); } } XP_FREE(pattern); } } } } // explicitly LIST the INBOX if (a) we're not using subscription, or (b) we are using subscription and // the user wants us to always show the INBOX. if (GetServerStateParser().LastCommandSuccessful() && (!TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost()) || TIMAPHostInfo::GetShouldAlwaysListInboxForHost(fCurrentUrl->GetUrlHost()))) { List("INBOX", FALSE, TRUE); } fHierarchyNameState = kNoOperationInProgress; MailboxDiscoveryFinished(); // Get the ACLs for newly discovered folders if (GetServerStateParser().ServerHasACLCapability()) { int total = XP_ListCount(fListedMailboxList), count = 0; GetServerStateParser().SetReportingErrors(FALSE); if (total) { ProgressEventFunction_UsingId(MK_IMAP_GETTING_ACL_FOR_FOLDER); TIMAPMailboxInfo *mb = NULL; do { mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); if (mb) { if (FolderNeedsACLInitialized(mb->GetMailboxName())) RefreshACLForFolder(mb->GetMailboxName()); PercentProgressUpdateEvent(NULL, (count*100)/total); delete mb; // this is the last time we're using the list, so delete the entries here count++; } } while (mb && !DeathSignalReceived()); } } } void TNavigatorImapConnection::DiscoverAllAndSubscribedBoxes() { // used for subscribe pane // iterate through all namespaces for (int i = TIMAPHostInfo::GetNumberOfNamespacesForHost(fCurrentUrl->GetUrlHost()); i >= 1; i-- ) { TIMAPNamespace *ns = TIMAPHostInfo::GetNamespaceNumberForHost(fCurrentUrl->GetUrlHost(), i); if (ns) { const char *prefix = ns->GetPrefix(); if (prefix) { // Fills in the hierarchy delimiters for some namespaces, for the case that they // were filled in manually from the preferences. if (ns->GetIsDelimiterFilledIn()) { fHierarchyNameState = kDiscoveringNamespacesOnly; List(prefix); fHierarchyNameState = kNoOperationInProgress; } char *allPattern = PR_smprintf("%s*",prefix); char *topLevelPattern = PR_smprintf("%s%%",prefix); char *secondLevelPattern = NULL; char delimiter = ns->GetDelimiter(); if (delimiter) { // Hierarchy delimiter might be NIL, in which case there's no hierarchy anyway secondLevelPattern = PR_smprintf("%s%%%c%%",prefix, delimiter); } if (allPattern) { Lsub(allPattern); // LSUB all the subscribed XP_FREE(allPattern); } if (topLevelPattern) { List(topLevelPattern); // LIST the top level XP_FREE(topLevelPattern); } if (secondLevelPattern) { List(secondLevelPattern); // LIST the second level XP_FREE(secondLevelPattern); } } } } SetFolderDiscoveryFinished(); } // report the success of the upgrade to IMAP subscription static void MOZTHREAD_SubscribeUpgradeFinished(void *blockingConnectionVoid, void *endStateVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; EIMAPSubscriptionUpgradeState *endState = (EIMAPSubscriptionUpgradeState *) endStateVoid; MSG_ReportSuccessOfUpgradeToIMAPSubscription(imapConnection->GetActiveEntry()->window_id, endState); imapConnection->NotifyEventCompletionMonitor(); } void TNavigatorImapConnection::UpgradeToSubscriptionFinishedEvent(EIMAPSubscriptionUpgradeState endState) { EIMAPSubscriptionUpgradeState *orphanedUpgradeState = (EIMAPSubscriptionUpgradeState *) XP_ALLOC(sizeof(EIMAPSubscriptionUpgradeState)); if (orphanedUpgradeState) *orphanedUpgradeState = endState; TImapFEEvent *upgradeEvent = new TImapFEEvent(MOZTHREAD_SubscribeUpgradeFinished, // function to call this, // access to current entry/context orphanedUpgradeState, FALSE); // storage passed if (upgradeEvent) { fFEEventQueue->AdoptEventToEnd(upgradeEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } static void MOZTHREAD_GetOldHostEvent(void *blockingConnectionVoid, void *pOldHostNameVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; char **pOldHostName = (char **) pOldHostNameVoid; // fill in the value PREF_CopyCharPref("network.hosts.pop_server", pOldHostName); imapConnection->NotifyEventCompletionMonitor(); } void TNavigatorImapConnection::GetOldIMAPHostNameEvent(char **oldHostName) { TImapFEEvent *getHostEvent = new TImapFEEvent(MOZTHREAD_GetOldHostEvent, // function to call this, // access to current entry/context oldHostName, FALSE); if (getHostEvent) { fFEEventQueue->AdoptEventToEnd(getHostEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } static void MOZTHREAD_PromptUserForSubscriptionUpgradePath(void *blockingConnectionVoid, void *valueVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); XP_Bool *value = (XP_Bool *)valueVoid; XP_Bool shouldWeUpgrade = FALSE; char *promptString = XP_GetString(MK_IMAP_UPGRADE_PROMPT_USER); if (promptString) { char *promptString2 = XP_GetString(MK_IMAP_UPGRADE_PROMPT_USER_2); if (promptString2) { char *finalPrompt = StrAllocCat(promptString, promptString2); if (finalPrompt) { shouldWeUpgrade = FE_Confirm(ce->window_id, finalPrompt); } else shouldWeUpgrade = FALSE; } else shouldWeUpgrade = FALSE; } else shouldWeUpgrade = FALSE; if (!shouldWeUpgrade) FE_Alert(ce->window_id, XP_GetString(MK_IMAP_UPGRADE_CUSTOM)); *value = shouldWeUpgrade; imapConnection->NotifyEventCompletionMonitor(); } // Returns TRUE if we need to automatically upgrade them, and FALSE if // they want to do it manually (through the Subscribe UI). XP_Bool TNavigatorImapConnection::PromptUserForSubscribeUpgradePath() { XP_Bool *value = (XP_Bool *)XP_ALLOC(sizeof(XP_Bool)); XP_Bool rv = FALSE; if (!value) return FALSE; TImapFEEvent *getHostEvent = new TImapFEEvent(MOZTHREAD_PromptUserForSubscriptionUpgradePath, // function to call this, // access to current entry/context value, FALSE); if (getHostEvent) { fFEEventQueue->AdoptEventToEnd(getHostEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); rv = *value; XP_FREE(value); return rv; } // Should only use this for upgrading to subscribe model void TNavigatorImapConnection::SubscribeToAllFolders() { fHierarchyNameState = kListingForInfoOnly; TIMAPMailboxInfo *mb = NULL; // This will fill in the list if (!DeathSignalReceived()) List("*"); int total = XP_ListCount(fListedMailboxList), count = 0; GetServerStateParser().SetReportingErrors(FALSE); if (!DeathSignalReceived()) do { mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); if (mb) { Subscribe(mb->GetMailboxName()); PercentProgressUpdateEvent(NULL, (count*100)/total); delete mb; count++; } } while (mb && !DeathSignalReceived()); PercentProgressUpdateEvent(NULL, 100); GetServerStateParser().SetReportingErrors(TRUE); fHierarchyNameState = kNoOperationInProgress; } int TNavigatorImapConnection::GetNumberOfListedMailboxesWithUnlistedChildren() { int count = 0, listSize = XP_ListCount(fListedMailboxList), i = 1; for (i = 1; i <= listSize; i++) { TIMAPMailboxInfo *mb = (TIMAPMailboxInfo *)XP_ListGetObjectNum(fListedMailboxList, i); if (!mb->GetChildrenListed()) count++; } return count; } void TNavigatorImapConnection::UpgradeToSubscription() { EIMAPSubscriptionUpgradeState endState = kEverythingDone; if (fCurrentUrl->GetShouldSubscribeToAll()) { SubscribeToAllFolders(); } else { TIMAPNamespace *ns = TIMAPHostInfo::GetDefaultNamespaceOfTypeForHost(fCurrentUrl->GetUrlHost(), kPersonalNamespace); if (ns && ns->GetType()==kPersonalNamespace) { // This should handle the case of getting the personal namespace from either the // prefs or the NAMESPACE extension. // We will list all the personal folders, and subscribe to them all. // We can do this, because we know they're personal, and the user said to try // to subscribe to all. fHierarchyNameState = kDiscoveringNamespacesOnly; List(ns->GetPrefix()); fHierarchyNameState = kListingForInfoOnly; char *pattern = PR_smprintf("%s*",ns->GetPrefix()); if (pattern && !DeathSignalReceived()) List(pattern); fHierarchyNameState = kNoOperationInProgress; // removing and freeing it as we go. GetServerStateParser().SetReportingErrors(FALSE); int total = XP_ListCount(fListedMailboxList), count = 0; TIMAPMailboxInfo *mb = 0; if (!DeathSignalReceived()) do { mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); if (mb) { Subscribe(mb->GetMailboxName()); PercentProgressUpdateEvent(NULL, (count*100)/total); delete mb; count++; } } while (mb && !DeathSignalReceived()); PercentProgressUpdateEvent(NULL, 100); GetServerStateParser().SetReportingErrors(TRUE); } else if (!ns) { // There is no personal or default namespace. This means that the server // supports the NAMESPACE extension, and has other namespaces, but explicitly // has no personal namespace. I would be very surprised if we EVER encounter // this situation, since the user must have been using Communicator 4.0 on // a server that supported the NAMESPACE extension and only had, say, // public folders. Not likely. All we can really do is alert the user // and bring up the Subscribe UI. AlertUserEvent_UsingId(MK_IMAP_UPGRADE_NO_PERSONAL_NAMESPACE); endState = kBringUpSubscribeUI; } else if (!GetServerStateParser().ServerHasNamespaceCapability()) { // NAMESPACE extension not supported, and we only have a default namespace // (That is, no explicit personal namespace is specified -- only "") if ((GetServerStateParser().GetCapabilityFlag() & kHasXNetscapeCapability) && !DeathSignalReceived()) { Netscape(); } if (GetServerStateParser().ServerIsNetscape3xServer()) { // Server is a Netscape 3.0 Messaging Server. We know that all the // folders are personal mail folders, and can subscribe to all (*). SubscribeToAllFolders(); } else { // Worst case. It's not a Netscape 3.0 server, NAMESPACE is not supported, // the user hasn't manually specified a personal namespace prefix, and // Mission Control hasn't told us anything. Do the best we can. // Progressively LIST % until either we run out of folders or we encounter // 50 (MAX_NEW_IMAP_FOLDER_COUNT) const char *namespacePrefix = ns->GetPrefix(); // Has to be "", right? // List the prefix, to get the hierarchy delimiter. // We probably shouldn't subscribe to the namespace itself. fHierarchyNameState = kDiscoveringNamespacesOnly; if (!DeathSignalReceived()) List(namespacePrefix); // Now, generate the list // Do it one level at a time, and stop either when nothing comes back or // we pass the threshold mailbox count (50) fHierarchyNameState = kListingForInfoOnly; char *firstPattern = PR_smprintf("%s%%", namespacePrefix); if (firstPattern && !DeathSignalReceived()) { List(firstPattern); XP_FREE(firstPattern); } while ((XP_ListCount(fListedMailboxList) < MAX_NEW_IMAP_FOLDER_COUNT) && (GetNumberOfListedMailboxesWithUnlistedChildren() > 0)) { int listSize = XP_ListCount(fListedMailboxList), i = 1; for (i = 1; (i <= listSize) && !DeathSignalReceived(); i++) { TIMAPMailboxInfo *mb = (TIMAPMailboxInfo *)XP_ListGetObjectNum(fListedMailboxList, i); if (!mb->GetChildrenListed()) { char delimiter = ns->GetDelimiter(); // If there's a NIL hierarchy delimter, then we don't need to list children if (delimiter) { char *pattern = PR_smprintf("%s%s%c%%",namespacePrefix,mb->GetMailboxName(), ns->GetDelimiter()); if (pattern && !DeathSignalReceived()) List(pattern); } mb->SetChildrenListed(TRUE); } } } fHierarchyNameState = kNoOperationInProgress; if (XP_ListCount(fListedMailboxList) < MAX_NEW_IMAP_FOLDER_COUNT) { // Less than the threshold (50?) // Iterate through the list and subscribe to each mailbox, // removing and freeing it as we go. GetServerStateParser().SetReportingErrors(FALSE); int total = XP_ListCount(fListedMailboxList), count = 0; TIMAPMailboxInfo *mb = 0; if (!DeathSignalReceived()) do { mb = (TIMAPMailboxInfo *)XP_ListRemoveTopObject(fListedMailboxList); if (mb) { Subscribe(mb->GetMailboxName()); PercentProgressUpdateEvent(NULL, (100*count)/total); delete mb; count++; } } while (mb && !DeathSignalReceived()); PercentProgressUpdateEvent(NULL, 100); GetServerStateParser().SetReportingErrors(TRUE); } else { // More than the threshold. Bring up the subscribe UI. // We still must free the storage in the list TIMAPMailboxInfo *mb = 0; do { mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); delete mb; } while (mb); AlertUserEvent_UsingId(MK_IMAP_UPGRADE_TOO_MANY_FOLDERS); endState = kBringUpSubscribeUI; } } } } if (!DeathSignalReceived()) // if not interrupted UpgradeToSubscriptionFinishedEvent(endState); } void TNavigatorImapConnection::ProcessAuthenticatedStateURL() { if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kLsubFolders) { //XP_ASSERT(FALSE); // now handled in FindMailboxesIfNecessary // **** use to find out whether Drafts, Sent, & Templates folder // exists or not even the user didn't subscribe to it char *mailboxName = fCurrentUrl->CreateServerDestinationFolderPathString(); if (mailboxName) { char *convertedName = CreateUtf7ConvertedString(mailboxName, TRUE); if (convertedName) { FREEIF(mailboxName); mailboxName = convertedName; } ProgressEventFunction_UsingId (MK_IMAP_STATUS_LOOKING_FOR_MAILBOX); IncrementCommandTagNumber(); PR_snprintf(GetOutputBuffer(), kOutputBufferSize, "%s list \"\" \"%s\"" CRLF, GetServerCommandTag(), mailboxName); int ioStatus = WriteLineToSocket(GetOutputBuffer()); TimeStampListNow(); ParseIMAPandCheckForNewMail(); // send list command to the server; modify // DiscoverIMAPMailbox to check for the // existence of the folder FREEIF(mailboxName); } else { HandleMemoryFailure(); } } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kGetMailAccountUrl) { if (GetServerStateParser().GetCapabilityFlag() & kHasXNetscapeCapability) { Netscape(); if (GetServerStateParser().LastCommandSuccessful()) { TImapFEEvent *alertEvent = new TImapFEEvent(msgSetMailAccountURL, // function to call this, // access to current entry (void *) fCurrentUrl->GetUrlHost(), TRUE); if (alertEvent) { fFEEventQueue->AdoptEventToEnd(alertEvent); // WaitForFEEventCompletion(); } else HandleMemoryFailure(); } } } else { // even though we don't have to to be legal protocol, Close any select mailbox // so we don't miss any info these urls might change for the selected folder // (e.g. msg count after append) if (GetServerStateParser().GetIMAPstate() == TImapServerState::kFolderSelected) { // now we should be avoiding an implicit Close because it performs an implicit Expunge // authenticated state urls should not use a cached connection that is in the selected state XP_ASSERT(FALSE); //Close(); } if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kOfflineToOnlineMove) { char *destinationMailbox = fCurrentUrl->CreateServerDestinationFolderPathString(); if (destinationMailbox) { char *convertedName = CreateUtf7ConvertedString(destinationMailbox, TRUE); XP_FREE(destinationMailbox); destinationMailbox = convertedName; } if (destinationMailbox) { uint32 appendSize = 0; do { WaitForNextAppendMessageSize(); appendSize = GetAppendSize(); if (!DeathSignalReceived() && appendSize) { char messageSizeString[100]; sprintf(messageSizeString, "%ld",(long) appendSize); AppendMessage(destinationMailbox, messageSizeString, GetAppendFlags()); } } while (appendSize && GetServerStateParser().LastCommandSuccessful()); FREEIF( destinationMailbox); } else HandleMemoryFailure(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kAppendMsgFromFile) { char *sourceMessageFile = XP_STRDUP(GetActiveEntry()->URL_s->post_data); char *mailboxName = fCurrentUrl->CreateServerSourceFolderPathString(); if (mailboxName) { char *convertedName = CreateUtf7ConvertedString(mailboxName, TRUE); XP_FREE(mailboxName); mailboxName = convertedName; } if (mailboxName) { UploadMessageFromFile(sourceMessageFile, mailboxName, kImapMsgSeenFlag); } else HandleMemoryFailure(); FREEIF( sourceMessageFile ); FREEIF( mailboxName ); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDiscoverAllBoxesUrl) { XP_ASSERT(!GetSubscribingNow()); // should not get here from subscribe UI DiscoverMailboxList(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDiscoverAllAndSubscribedBoxesUrl) { XP_ASSERT(GetSubscribingNow()); DiscoverAllAndSubscribedBoxes(); } else { char *sourceMailbox = fCurrentUrl->CreateServerSourceFolderPathString(); if (sourceMailbox) { char *convertedName = CreateUtf7ConvertedString(sourceMailbox, TRUE); XP_FREE(sourceMailbox); sourceMailbox = convertedName; } if (sourceMailbox) { if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kCreateFolder) { XP_Bool created = CreateMailboxRespectingSubscriptions(sourceMailbox); if (created) { List(sourceMailbox); } else FolderNotCreated(sourceMailbox); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDiscoverChildrenUrl) { char *canonicalParent = fCurrentUrl->CreateCanonicalSourceFolderPathString(); if (canonicalParent) { NthLevelChildList(canonicalParent, 2); //CanonicalChildList(canonicalParent,FALSE); XP_FREE(canonicalParent); } } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDiscoverLevelChildrenUrl) { char *canonicalParent = fCurrentUrl->CreateCanonicalSourceFolderPathString(); int depth = fCurrentUrl->GetChildDiscoveryDepth(); if (canonicalParent) { NthLevelChildList(canonicalParent, depth); if (GetServerStateParser().LastCommandSuccessful()) ChildDiscoverySucceeded(); XP_FREE(canonicalParent); } } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSubscribe) { Subscribe(sourceMailbox); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kUnsubscribe) { Unsubscribe(sourceMailbox); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kRefreshACL) { RefreshACLForFolder(sourceMailbox); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kRefreshAllACLs) { RefreshAllACLs(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kListFolder) { char *folderName = fCurrentUrl->CreateCanonicalSourceFolderPathString(); List(folderName); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kUpgradeToSubscription) { UpgradeToSubscription(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kFolderStatus) { StatusForFolder(sourceMailbox); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kRefreshFolderUrls) { XMailboxInfo(sourceMailbox); if (GetServerStateParser().LastCommandSuccessful()) { InitializeFolderUrl(sourceMailbox); } } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kDeleteFolder) { XP_Bool deleted = DeleteSubFolders(sourceMailbox); if (deleted) deleted = DeleteMailboxRespectingSubscriptions(sourceMailbox); if (deleted) FolderDeleted(sourceMailbox); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kRenameFolder) { char *destinationMailbox = fCurrentUrl->CreateServerDestinationFolderPathString(); if (destinationMailbox) { char *convertedName = CreateUtf7ConvertedString(destinationMailbox, TRUE); XP_FREE(destinationMailbox); destinationMailbox = convertedName; } if (destinationMailbox) { XP_Bool renamed = RenameHierarchyByHand(sourceMailbox, destinationMailbox); if (renamed) FolderRenamed(sourceMailbox, destinationMailbox); FREEIF( destinationMailbox); } else HandleMemoryFailure(); } else if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kMoveFolderHierarchy) { char *destinationMailbox = fCurrentUrl->CreateServerDestinationFolderPathString(); if (destinationMailbox) { // worst case length char *newBoxName = (char *) XP_ALLOC(XP_STRLEN(destinationMailbox) + XP_STRLEN(sourceMailbox) + 2); if (newBoxName) { char onlineDirSeparator = fCurrentUrl->GetOnlineSubDirSeparator(); XP_STRCPY(newBoxName, destinationMailbox); char *leafSeparator = XP_STRRCHR(sourceMailbox, onlineDirSeparator); if (!leafSeparator) leafSeparator = sourceMailbox; // this is a root level box else leafSeparator++; if ( *newBoxName && ( *(newBoxName + XP_STRLEN(newBoxName) - 1) != onlineDirSeparator)) { char separatorStr[2]; separatorStr[0] = onlineDirSeparator; separatorStr[1] = 0; XP_STRCAT(newBoxName, separatorStr); } XP_STRCAT(newBoxName, leafSeparator); XP_Bool renamed = RenameHierarchyByHand(sourceMailbox, newBoxName); if (renamed) FolderRenamed(sourceMailbox, newBoxName); } } } FREEIF( sourceMailbox); } else HandleMemoryFailure(); } } } int32 TNavigatorImapConnection::GetMessageSizeForProgress() { return GetServerStateParser().SizeOfMostRecentMessage(); } int32 TNavigatorImapConnection::GetBytesMovedSoFarForProgress() { return fBytesMovedSoFarForProgress; } void TNavigatorImapConnection::SetBytesMovedSoFarForProgress(int32 bytesMoved) { fBytesMovedSoFarForProgress = bytesMoved; } void TNavigatorImapConnection::AlertUserEvent_UsingId(uint32 msgId) { TImapFEEvent *alertEvent = new TImapFEEvent(AlertEventFunction_UsingId, // function to call this, // access to current entry (void *) msgId, TRUE); // alert message if (alertEvent) { fFEEventQueue->AdoptEventToEnd(alertEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } void TNavigatorImapConnection::AlertUserEvent(char *message) { TImapFEEvent *alertEvent = new TImapFEEvent(AlertEventFunction, // function to call this, // access to current entry message, TRUE); // alert message if (alertEvent) { fFEEventQueue->AdoptEventToEnd(alertEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } void TNavigatorImapConnection::AlertUserEventFromServer(char *serverSaid) { TImapFEEvent *alertEvent = new TImapFEEvent(AlertEventFunctionFromServer, // function to call this, // access to current entry serverSaid, TRUE); // alert message if (alertEvent) { fFEEventQueue->AdoptEventToEnd(alertEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } void TNavigatorImapConnection::ProgressUpdateEvent(char *message) { TImapFEEvent *progressEvent = new TImapFEEvent(ProgressEventFunction, // function to call this, // access to current entry message, TRUE); // progress message if (progressEvent) { fFEEventQueue->AdoptEventToEnd(progressEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } void TNavigatorImapConnection::ProgressEventFunction_UsingId(uint32 msgId) { if (msgId != (uint32) fLastProgressStringId) { TImapFEEvent *progressEvent = new TImapFEEvent(ProgressStatusFunction_UsingId, // function to call this, // access to current entry (void *) msgId, TRUE); // alert message id if (progressEvent) { fFEEventQueue->AdoptEventToEnd(progressEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); fLastProgressStringId = msgId; } } void TNavigatorImapConnection::ProgressEventFunction_UsingIdWithString(uint32 msgId, const char* extraInfo) { StatusMessageInfo *msgInfo = (StatusMessageInfo *) XP_ALLOC(sizeof(StatusMessageInfo)); // free in event handler if (msgInfo) { if (extraInfo) { msgInfo->msgID = msgId; msgInfo->extraInfo = XP_STRDUP (extraInfo); // free in event handler TImapFEEvent *progressEvent = new TImapFEEvent(ProgressStatusFunction_UsingIdWithString, // function to call this, // access to current entry (void *) msgInfo, TRUE); // alert message info if (progressEvent) { fFEEventQueue->AdoptEventToEnd(progressEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } } } void TNavigatorImapConnection::PercentProgressUpdateEvent(char *message, int percent) { int64 nowMS; if (percent == fLastPercent) return; // hasn't changed, right? So just return. Do we need to clear this anywhere? if (percent < 100) // always need to do 100% { int64 minIntervalBetweenProgress; LL_I2L(minIntervalBetweenProgress, 250); int64 diffSinceLastProgress; LL_I2L(nowMS, PR_IntervalToMilliseconds(PR_IntervalNow())); LL_SUB(diffSinceLastProgress, nowMS, fLastProgressTime); // r = a - b LL_SUB(diffSinceLastProgress, diffSinceLastProgress, minIntervalBetweenProgress); // r = a - b if (!LL_GE_ZERO(diffSinceLastProgress)) return; } ProgressInfo *progress = (ProgressInfo *) XP_ALLOC(sizeof(ProgressInfo)); // free in event handler if (progress) { // we can do the event without a message - it will set the percent bar if (message) progress->message = XP_STRDUP(message); // free in event handler else progress->message = 0; progress->percent = percent; fLastPercent = percent; TImapFEEvent *progressEvent = new TImapFEEvent(PercentProgressEventFunction, // function to call this, // access to current entry progress, TRUE); // progress message fLastProgressTime = nowMS; if (progressEvent) { fFEEventQueue->AdoptEventToEnd(progressEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } } void TNavigatorImapConnection::PastPasswordCheckEvent() { TImapFEEvent *pwCheckEvent = new TImapFEEvent(PastPasswordCheckFunction, this, NULL, TRUE); if (pwCheckEvent) { fFEEventQueue->AdoptEventToEnd(pwCheckEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } static void CommitNamespacesFunction(void *blockingConnectionVoid, void *hostNameVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); char *hostName = (char *)hostNameVoid; if (hostName) TIMAPHostInfo::CommitNamespacesForHost(hostName, ce->window_id->mailMaster); imapConnection->NotifyEventCompletionMonitor(); } void TNavigatorImapConnection::CommitNamespacesForHostEvent() { TImapFEEvent *commitEvent = new TImapFEEvent(CommitNamespacesFunction,this,(void *)(fCurrentUrl->GetUrlHost()), TRUE); if (commitEvent) { fFEEventQueue->AdoptEventToEnd(commitEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } static void CommitCapabilityFunction(void *blockingConnectionVoid, void *hostNameVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); char *hostName = (char *)hostNameVoid; if (hostName) MSG_CommitCapabilityForHost(hostName, ce->window_id->mailMaster); imapConnection->NotifyEventCompletionMonitor(); } void TNavigatorImapConnection::CommitCapabilityForHostEvent() { TImapFEEvent *commitEvent = new TImapFEEvent(CommitCapabilityFunction,this,(void *)(fCurrentUrl->GetUrlHost()), TRUE); if (commitEvent) { fFEEventQueue->AdoptEventToEnd(commitEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); } typedef struct _MessageSizeInfo { char *id; char *folderName; XP_Bool idIsUid; uint32 size; } MessageSizeInfo; // Retrieves the message size given a message UID // Retrieves the message size given a UID or message sequence number static void GetMessageSizeEvent(void *blockingConnectionVoid, void *sizeInfoVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); MessageSizeInfo *sizeInfo = (MessageSizeInfo *)sizeInfoVoid; MSG_Pane *masterPane = ce->URL_s->msg_pane; // we need some pane to get to the master if (!masterPane) masterPane = MSG_FindPane(ce->window_id, MSG_ANYPANE); // last ditch effort sizeInfo->size = MSG_GetIMAPMessageSizeFromDB(masterPane, imapConnection->GetHostName(), sizeInfo->folderName, sizeInfo->id, sizeInfo->idIsUid); imapConnection->NotifyEventCompletionMonitor(); } uint32 TNavigatorImapConnection::GetMessageSize(const char *messageId, XP_Bool idsAreUids) { MessageSizeInfo *sizeInfo = (MessageSizeInfo *)XP_ALLOC(sizeof(MessageSizeInfo)); if (sizeInfo) { const char *folderFromParser = GetServerStateParser().GetSelectedMailboxName(); if (folderFromParser) { sizeInfo->id = (char *)XP_ALLOC(XP_STRLEN(messageId) + 1); sizeInfo->folderName = (char *)XP_ALLOC(XP_STRLEN(folderFromParser) + 1); XP_STRCPY(sizeInfo->id, messageId); sizeInfo->idIsUid = idsAreUids; if (sizeInfo->id && sizeInfo->folderName) { XP_STRCPY(sizeInfo->folderName, folderFromParser); TImapFEEvent *getMessageSizeEvent = new TImapFEEvent(GetMessageSizeEvent, this, sizeInfo, FALSE); if (getMessageSizeEvent) { fFEEventQueue->AdoptEventToEnd(getMessageSizeEvent); WaitForFEEventCompletion(); } else { HandleMemoryFailure(); return 0; } XP_FREE(sizeInfo->id); XP_FREE(sizeInfo->folderName); } else { HandleMemoryFailure(); XP_FREE(sizeInfo); return 0; } int32 rv = 0; if (!DeathSignalReceived()) rv = sizeInfo->size; XP_FREE(sizeInfo); return rv; } else { HandleMemoryFailure(); return 0; } } else { HandleMemoryFailure(); return 0; } } static void MOZTHREAD_FolderIsNoSelectEvent(void *blockingConnectionVoid, void *valueVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); FolderQueryInfo *value = (FolderQueryInfo *)valueVoid; value->rv = MSG_IsFolderNoSelect(ce->window_id->mailMaster, value->name, value->hostName); imapConnection->NotifyEventCompletionMonitor(); } // Returns TRUE if the mailbox is a NoSelect mailbox. // If we don't know about it, returns FALSE. XP_Bool TNavigatorImapConnection::MailboxIsNoSelectMailbox(const char *mailboxName) { FolderQueryInfo *value = (FolderQueryInfo *)XP_ALLOC(sizeof(FolderQueryInfo)); XP_Bool rv = FALSE; if (!value) return FALSE; value->name = XP_STRDUP(mailboxName); if (!value->name) { XP_FREE(value); return FALSE; } value->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); if (!value->hostName) { XP_FREE(value->name); XP_FREE(value); return FALSE; } TImapFEEvent *theEvent = new TImapFEEvent(MOZTHREAD_FolderIsNoSelectEvent, // function to call this, // access to current entry/context value, FALSE); if (theEvent) { fFEEventQueue->AdoptEventToEnd(theEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); rv = value->rv; XP_FREE(value->hostName); XP_FREE(value->name); XP_FREE(value); return rv; } /////////////////// Begin Subscription Management Functions ///////////////////////////// // returns TRUE is the create succeeded (regardless of subscription changes) XP_Bool TNavigatorImapConnection::CreateMailboxRespectingSubscriptions(const char *mailboxName) { TIMAP4BlockingConnection::CreateMailbox(mailboxName); XP_Bool rv = GetServerStateParser().LastCommandSuccessful(); if (rv) { if (fAutoSubscribe) // auto-subscribe is on { // create succeeded - let's subscribe to it XP_Bool reportingErrors = GetServerStateParser().GetReportingErrors(); GetServerStateParser().SetReportingErrors(FALSE); Subscribe(mailboxName); GetServerStateParser().SetReportingErrors(reportingErrors); } } return (rv); } // returns TRUE is the delete succeeded (regardless of subscription changes) XP_Bool TNavigatorImapConnection::DeleteMailboxRespectingSubscriptions(const char *mailboxName) { XP_Bool rv = TRUE; if (!MailboxIsNoSelectMailbox(mailboxName)) { // Only try to delete it if it really exists TIMAP4BlockingConnection::DeleteMailbox(mailboxName); rv = GetServerStateParser().LastCommandSuccessful(); } // We can unsubscribe even if the mailbox doesn't exist. if (rv && fAutoUnsubscribe) // auto-unsubscribe is on { XP_Bool reportingErrors = GetServerStateParser().GetReportingErrors(); GetServerStateParser().SetReportingErrors(FALSE); Unsubscribe(mailboxName); GetServerStateParser().SetReportingErrors(reportingErrors); } return (rv); } // returns TRUE is the rename succeeded (regardless of subscription changes) // reallyRename tells us if we should really do the rename (TRUE) or if we should just move subscriptions (FALSE) XP_Bool TNavigatorImapConnection::RenameMailboxRespectingSubscriptions(const char *existingName, const char *newName, XP_Bool reallyRename) { XP_Bool rv = TRUE; if (reallyRename && !MailboxIsNoSelectMailbox(existingName)) { TIMAP4BlockingConnection::RenameMailbox(existingName, newName); rv = GetServerStateParser().LastCommandSuccessful(); } if (rv) { if (fAutoSubscribe) // if auto-subscribe is on { XP_Bool reportingErrors = GetServerStateParser().GetReportingErrors(); GetServerStateParser().SetReportingErrors(FALSE); Subscribe(newName); GetServerStateParser().SetReportingErrors(reportingErrors); } if (fAutoUnsubscribe) // if auto-unsubscribe is on { XP_Bool reportingErrors = GetServerStateParser().GetReportingErrors(); GetServerStateParser().SetReportingErrors(FALSE); Unsubscribe(existingName); GetServerStateParser().SetReportingErrors(reportingErrors); } } return (rv); } XP_Bool TNavigatorImapConnection::GetSubscribingNow() { return (fCurrentEntry && fCurrentEntry->window_id && fCurrentEntry->window_id->imapURLPane && (MSG_SUBSCRIBEPANE == MSG_GetPaneType(fCurrentEntry->window_id->imapURLPane))); } /////////////////// End Subscription Management Functions ///////////////////////////// /////////////////// Begin ACL Stuff /////////////////////////////////////////////////// void TNavigatorImapConnection::RefreshACLForFolderIfNecessary(const char *mailboxName) { if (fFolderNeedsACLRefreshed && GetServerStateParser().ServerHasACLCapability()) { RefreshACLForFolder(mailboxName); fFolderNeedsACLRefreshed = FALSE; } } void TNavigatorImapConnection::RefreshACLForFolder(const char *mailboxName) { TIMAPNamespace *ns = TIMAPHostInfo::GetNamespaceForMailboxForHost(fCurrentUrl->GetUrlHost(), mailboxName); if (ns) { switch (ns->GetType()) { case kPersonalNamespace: // It's a personal folder, most likely. // I find it hard to imagine a server that supports ACL that doesn't support NAMESPACE, // so most likely we KNOW that this is a personal, rather than the default, namespace. // First, clear what we have. ClearAllFolderRights(mailboxName); // Now, get the new one. GetACLForFolder(mailboxName); // We're all done, refresh the icon/flags for this folder RefreshFolderACLView(mailboxName); break; default: // We know it's a public folder or other user's folder. // We only want our own rights // First, clear what we have ClearAllFolderRights(mailboxName); // Now, get the new one. GetMyRightsForFolder(mailboxName); // We're all done, refresh the icon/flags for this folder RefreshFolderACLView(mailboxName); break; } } else { // no namespace, not even default... can this happen? XP_ASSERT(FALSE); } } void TNavigatorImapConnection::RefreshAllACLs() { fHierarchyNameState = kListingForInfoOnly; TIMAPMailboxInfo *mb = NULL; // This will fill in the list List("*"); int total = XP_ListCount(fListedMailboxList), count = 0; GetServerStateParser().SetReportingErrors(FALSE); do { mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList); if (mb) { RefreshACLForFolder(mb->GetMailboxName()); PercentProgressUpdateEvent(NULL, (count*100)/total); delete mb; count++; } } while (mb); PercentProgressUpdateEvent(NULL, 100); GetServerStateParser().SetReportingErrors(TRUE); fHierarchyNameState = kNoOperationInProgress; } static void ClearFolderRightsEvent(void *blockingConnectionVoid, void *aclRightsInfoVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); TIMAPACLRightsInfo *aclRights = (TIMAPACLRightsInfo *)aclRightsInfoVoid; MSG_ClearFolderRightsForFolder(ce->window_id->mailMaster, aclRights->hostName, aclRights->mailboxName); imapConnection->NotifyEventCompletionMonitor(); } void TNavigatorImapConnection::ClearAllFolderRights(const char *mailboxName) { TIMAPACLRightsInfo *aclRightsInfo = new TIMAPACLRightsInfo(); if (aclRightsInfo) { aclRightsInfo->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); aclRightsInfo->mailboxName = XP_STRDUP(mailboxName); aclRightsInfo->rights = NULL; aclRightsInfo->userName = NULL; if (aclRightsInfo->hostName && aclRightsInfo->mailboxName) { TImapFEEvent *clearFolderRightsEvent = new TImapFEEvent(ClearFolderRightsEvent, // function to call this, // access to current entry/context aclRightsInfo, FALSE); if (clearFolderRightsEvent) { fFEEventQueue->AdoptEventToEnd(clearFolderRightsEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); XP_FREE(aclRightsInfo->hostName); XP_FREE(aclRightsInfo->mailboxName); } else HandleMemoryFailure(); delete aclRightsInfo; } else HandleMemoryFailure(); } static void AddFolderRightsEvent(void *blockingConnectionVoid, void *aclRightsInfoVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); TIMAPACLRightsInfo *aclRights = (TIMAPACLRightsInfo *)aclRightsInfoVoid; MSG_AddFolderRightsForUser(ce->window_id->mailMaster, aclRights->hostName, aclRights->mailboxName, aclRights->userName, aclRights->rights); imapConnection->NotifyEventCompletionMonitor(); } void TNavigatorImapConnection::AddFolderRightsForUser(const char *mailboxName, const char *userName, const char *rights) { TIMAPACLRightsInfo *aclRightsInfo = new TIMAPACLRightsInfo(); if (aclRightsInfo) { aclRightsInfo->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); aclRightsInfo->mailboxName = XP_STRDUP(mailboxName); if (userName) aclRightsInfo->userName = XP_STRDUP(userName); else aclRightsInfo->userName = NULL; aclRightsInfo->rights = XP_STRDUP(rights); if (aclRightsInfo->hostName && aclRightsInfo->mailboxName && aclRightsInfo->rights && userName ? (aclRightsInfo->userName != NULL) : TRUE) { TImapFEEvent *addFolderRightsEvent = new TImapFEEvent(AddFolderRightsEvent, // function to call this, // access to current entry/context aclRightsInfo, FALSE); if (addFolderRightsEvent) { fFEEventQueue->AdoptEventToEnd(addFolderRightsEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); XP_FREE(aclRightsInfo->hostName); XP_FREE(aclRightsInfo->mailboxName); XP_FREE(aclRightsInfo->rights); if (aclRightsInfo->userName) XP_FREE(aclRightsInfo->userName); } else HandleMemoryFailure(); delete aclRightsInfo; } else HandleMemoryFailure(); } static void RefreshFolderRightsEvent(void *blockingConnectionVoid, void *aclRightsInfoVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); TIMAPACLRightsInfo *aclRights = (TIMAPACLRightsInfo *)aclRightsInfoVoid; MSG_RefreshFolderRightsViewForFolder(ce->window_id->mailMaster, aclRights->hostName, aclRights->mailboxName); imapConnection->NotifyEventCompletionMonitor(); } void TNavigatorImapConnection::RefreshFolderACLView(const char *mailboxName) { TIMAPACLRightsInfo *aclRightsInfo = new TIMAPACLRightsInfo(); if (aclRightsInfo) { aclRightsInfo->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); aclRightsInfo->mailboxName = XP_STRDUP(mailboxName); aclRightsInfo->rights = NULL; aclRightsInfo->userName = NULL; if (aclRightsInfo->hostName && aclRightsInfo->mailboxName) { TImapFEEvent *refreshFolderRightsEvent = new TImapFEEvent(RefreshFolderRightsEvent, // function to call this, // access to current entry/context aclRightsInfo, FALSE); if (refreshFolderRightsEvent) { fFEEventQueue->AdoptEventToEnd(refreshFolderRightsEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); XP_FREE(aclRightsInfo->hostName); XP_FREE(aclRightsInfo->mailboxName); } else HandleMemoryFailure(); delete aclRightsInfo; } else HandleMemoryFailure(); } static void MOZTHREAD_FolderNeedsACLInitialized(void *blockingConnectionVoid, void *valueVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); FolderQueryInfo *value = (FolderQueryInfo *)valueVoid; value->rv = !MSG_IsFolderACLInitialized(ce->window_id->mailMaster, value->name, value->hostName); imapConnection->NotifyEventCompletionMonitor(); } XP_Bool TNavigatorImapConnection::FolderNeedsACLInitialized(const char *folderName) { FolderQueryInfo *value = (FolderQueryInfo *)XP_ALLOC(sizeof(FolderQueryInfo)); XP_Bool rv = FALSE; if (!value) return FALSE; value->name = XP_STRDUP(folderName); if (!value->name) { XP_FREE(value); return FALSE; } value->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); if (!value->hostName) { XP_FREE(value->name); XP_FREE(value); return FALSE; } TImapFEEvent *folderACLInitEvent = new TImapFEEvent(MOZTHREAD_FolderNeedsACLInitialized, // function to call this, // access to current entry/context value, FALSE); if (folderACLInitEvent) { fFEEventQueue->AdoptEventToEnd(folderACLInitEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); rv = value->rv; XP_FREE(value->hostName); XP_FREE(value->name); XP_FREE(value); return rv; } /////////////////// End ACL Stuff ///////////////////////////////////////////////////// XP_Bool TNavigatorImapConnection::InitializeFolderUrl(const char *folderName) { FolderQueryInfo *value = (FolderQueryInfo *)XP_ALLOC(sizeof(FolderQueryInfo)); XP_Bool rv = FALSE; if (!value) return FALSE; value->name = XP_STRDUP(folderName); if (!value->name) { XP_FREE(value); return FALSE; } value->hostName = XP_STRDUP(fCurrentUrl->GetUrlHost()); if (!value->hostName) { XP_FREE(value->name); XP_FREE(value); return FALSE; } TImapFEEvent *folderURLInitEvent = new TImapFEEvent(MOZ_THREADmsgSetFolderURL, // function to call this, // access to current entry/context value, TRUE); if (folderURLInitEvent) { fFEEventQueue->AdoptEventToEnd(folderURLInitEvent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); rv = value->rv; XP_FREE(value->hostName); XP_FREE(value->name); XP_FREE(value); return rv; } static void MOZTHREAD_GetShowAttachmentsInline(void *blockingConnectionVoid, void *valueVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; XP_Bool *value = (XP_Bool *)valueVoid; PREF_GetBoolPref("mail.inline_attachments", value); imapConnection->NotifyEventCompletionMonitor(); } XP_Bool TNavigatorImapConnection::GetShowAttachmentsInline() { XP_Bool *value = (XP_Bool *)XP_ALLOC(sizeof(XP_Bool)); XP_Bool rv = FALSE; if (!value) return FALSE; TImapFEEvent *levent = new TImapFEEvent(MOZTHREAD_GetShowAttachmentsInline, // function to call this, // access to current entry/context value, FALSE); if (levent) { fFEEventQueue->AdoptEventToEnd(levent); WaitForFEEventCompletion(); } else HandleMemoryFailure(); rv = *value; XP_FREE(value); return rv; } void TNavigatorImapConnection::SetContentModified(XP_Bool modified) { ActiveEntry *ce = GetActiveEntry(); if (ce && !DeathSignalReceived()) ce->URL_s->content_modified = modified; } XP_Bool TNavigatorImapConnection::GetShouldFetchAllParts() { ActiveEntry *ce = GetActiveEntry(); if (ce && !DeathSignalReceived()) return ce->URL_s->content_modified; else return TRUE; } void TNavigatorImapConnection::Log(const char *logSubName, const char *extraInfo, const char *logData) { static char *nonAuthStateName = "NA"; static char *authStateName = "A"; static char *selectedStateName = "S"; static char *waitingStateName = "W"; char *stateName = NULL; switch (GetServerStateParser().GetIMAPstate()) { case TImapServerState::kFolderSelected: if (fCurrentUrl) { if (extraInfo) PR_LOG(IMAP, out, ("%s:%s-%s:%s:%s: %s", fCurrentUrl->GetUrlHost(),selectedStateName, GetServerStateParser().GetSelectedMailboxName(), logSubName, extraInfo, logData)); else PR_LOG(IMAP, out, ("%s:%s-%s:%s: %s", fCurrentUrl->GetUrlHost(),selectedStateName, GetServerStateParser().GetSelectedMailboxName(), logSubName, logData)); } return; break; case TImapServerState::kNonAuthenticated: stateName = nonAuthStateName; break; case TImapServerState::kAuthenticated: stateName = authStateName; break; case TImapServerState::kWaitingForMoreClientInput: stateName = waitingStateName; break; } if (fCurrentUrl) { if (extraInfo) PR_LOG(IMAP, out, ("%s:%s:%s:%s: %s",fCurrentUrl->GetUrlHost(),stateName,logSubName,extraInfo,logData)); else PR_LOG(IMAP, out, ("%s:%s:%s: %s",fCurrentUrl->GetUrlHost(),stateName,logSubName,logData)); } } // essentially, just a copy of parseadoptedmsgline, except it doesn't notify the eventcompletion monitor static void MOZTHREAD_TunnelOutStream(void *blockingConnectionVoid, void *adoptedmsg_line_info_Void) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; ActiveEntry *ce = imapConnection->GetActiveEntry(); msg_line_info *downloadLineDontDelete = (msg_line_info *) adoptedmsg_line_info_Void; NET_StreamClass *outputStream = imapConnection->GetOutputStream(); unsigned int streamBytesMax = (*outputStream->is_write_ready) (outputStream); char *stringToPut = downloadLineDontDelete->adoptedMessageLine; XP_Bool allocatedString = FALSE; if ( streamBytesMax < (XP_STRLEN(downloadLineDontDelete->adoptedMessageLine) + 2)) { // dup the streamBytesMax number of chars, then put the rest of the string back into event queue if (streamBytesMax != 0) { stringToPut = (char *) XP_ALLOC(streamBytesMax + 1); // 1 for \0 if (stringToPut) { XP_MEMCPY(stringToPut, downloadLineDontDelete->adoptedMessageLine, streamBytesMax); *(stringToPut + streamBytesMax) = 0; allocatedString = TRUE; } // shift buffer bytes XP_MEMMOVE(downloadLineDontDelete->adoptedMessageLine, downloadLineDontDelete->adoptedMessageLine + streamBytesMax, (XP_STRLEN(downloadLineDontDelete->adoptedMessageLine) + 1) - streamBytesMax); } // the output stream can't handle this event yet! put it back on the // queue TImapFEEvent *parseLineEvent = new TImapFEEvent(MOZTHREAD_TunnelOutStream, // function to call blockingConnectionVoid, // access to current entry adoptedmsg_line_info_Void, FALSE); // line to display if (parseLineEvent) imapConnection->GetFEEventQueue(). AdoptEventToBeginning(parseLineEvent); else imapConnection->HandleMemoryFailure(); } if (streamBytesMax != 0) { imapConnection->SetBytesMovedSoFarForProgress(imapConnection->GetBytesMovedSoFarForProgress() + XP_STRLEN(stringToPut)); if (!imapConnection->GetPseudoInterrupted()) { int bytesPut = 0; if (ce->window_id->currentIMAPfolder) { // the definition of put_block in net.h defines // the 3rd parameter as the length of the block, // but since we are always sending a null terminated // string, I am able to sent the uid instead bytesPut = (*outputStream->put_block) (outputStream, stringToPut, downloadLineDontDelete->uidOfMessage); } else // this is a download for display { bytesPut = (*outputStream->put_block) (outputStream, stringToPut, XP_STRLEN(stringToPut)); } if (bytesPut < 0) { imapConnection->SetConnectionStatus(-1); // something bad happened, stop this url imapConnection->GetServerStateParser().SetConnected(FALSE); } } } if (allocatedString) XP_FREE(stringToPut); // event not done yet // else if (streamBytesMax != 0) // imapConnection->NotifyEventCompletionMonitor(); } static void MOZTHREAD_ProcessTunnel(void *blockingConnectionVoid, void *tunnelInfoVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) blockingConnectionVoid; TunnelInfo *info = (TunnelInfo *)tunnelInfoVoid; if (info->maxSize <= 150) { // We want to read less than the size of a line. // Close the tunnel, and switch back to the IMAP thread // to do the protocol parsing, etc. PR_LOG(IMAP, out, ("TUNNEL: Leaving Mozilla Thread with leftover size %ld",info->maxSize)); imapConnection->NotifyTunnelCompletionMonitor(); } else { // We know there's enough data to read. So let's do it. // Read a line char *newLine = NULL; XP_Bool pauseForRead = TRUE; int socketReadStatus = NET_BufferedReadLine(info->ioSocket, &newLine, info->inputSocketBuffer, info->inputBufferSize, &pauseForRead); if (socketReadStatus > 0) { // Set it up to handle reading what's left over if (newLine) { char *lineToStream = PR_smprintf("%s%s",newLine, CRLF); if (lineToStream) { info->maxSize -= XP_STRLEN(lineToStream); XP_ASSERT(info->maxSize > 0); TImapFEEvent *event = new TImapFEEvent(MOZTHREAD_ProcessTunnel, // function to call blockingConnectionVoid, // access to current entry tunnelInfoVoid, FALSE); if (event) imapConnection->GetFEEventQueue(). AdoptEventToBeginning(event); else imapConnection->HandleMemoryFailure(); // Finally, stream it out msg_line_info *outputStreamInfo = (msg_line_info *) XP_ALLOC(sizeof( msg_line_info)); if (outputStreamInfo) { outputStreamInfo->adoptedMessageLine = lineToStream; // Whoa, actually call one event from another. // Let's let it worry about making sure everything is streamed out before we get called here again. MOZTHREAD_TunnelOutStream(blockingConnectionVoid, outputStreamInfo); XP_FREE(outputStreamInfo); } else imapConnection->HandleMemoryFailure(); XP_FREE(lineToStream); } else imapConnection->HandleMemoryFailure(); } else { // no line yet, but there might have been data read (filling up the buffer) // Put ourselves back on the queue TImapFEEvent *event = new TImapFEEvent(MOZTHREAD_ProcessTunnel, // function to call blockingConnectionVoid, // access to current entry tunnelInfoVoid, FALSE); if (event) imapConnection->GetFEEventQueue(). AdoptEventToBeginning(event); else imapConnection->HandleMemoryFailure(); } } else { // socketReadStatus <= 0 int socketError = PR_GetError(); // If wouldblock, just put this event back on the queue if (socketError == XP_ERRNO_EWOULDBLOCK) { TImapFEEvent *event = new TImapFEEvent(MOZTHREAD_ProcessTunnel, // function to call blockingConnectionVoid, // access to current entry tunnelInfoVoid, FALSE); if (event) imapConnection->GetFEEventQueue(). AdoptEventToBeginning(event); else imapConnection->HandleMemoryFailure(); } else { // Some socket error - handle this here XP_ASSERT(FALSE); } } } } int32 TNavigatorImapConnection::OpenTunnel (int32 maxNumberOfBytesToRead) { char *bytesToRead = PR_smprintf("Max bytes to read: %ld",maxNumberOfBytesToRead); if (bytesToRead) { Log("TUNNEL","Opening Tunnel", bytesToRead); XP_FREE(bytesToRead); } else { Log("TUNNEL",NULL,"Opening Tunnel"); } TunnelInfo *info = (TunnelInfo *)XP_ALLOC(sizeof(TunnelInfo)); int32 originalMax = maxNumberOfBytesToRead, rv = 0; if (info) { info->maxSize = maxNumberOfBytesToRead; info->ioSocket = GetIOSocket(); info->inputSocketBuffer = &fInputSocketBuffer; info->inputBufferSize = &fInputBufferSize; TImapFEEvent *event = new TImapFEEvent(MOZTHREAD_ProcessTunnel, // function to call this, // access to current entry/context info, // the tunneling info FALSE); // Interrupts would be bad for this if (event) { fFEEventQueue->AdoptEventToEnd(event); //WaitForFEEventCompletion(); WaitForTunnelCompletion(); } else HandleMemoryFailure(); rv = originalMax - info->maxSize; XP_FREE(info); } char *bytesSoFar = PR_smprintf("Bytes read so far: %ld", rv); if (bytesSoFar) { Log("TUNNEL","Closing Tunnel", bytesSoFar); XP_FREE(bytesSoFar); } else { Log("TUNNEL",NULL,"Closing Tunnel"); } return rv; } int32 TNavigatorImapConnection::GetTunnellingThreshold() { return gTunnellingThreshold; } XP_Bool TNavigatorImapConnection::GetIOTunnellingEnabled() { return gIOTunnelling; } PR_STATIC_CALLBACK(void) imap_thread_main_function(void *imapConnectionVoid) { TNavigatorImapConnection *imapConnection = (TNavigatorImapConnection *) imapConnectionVoid; imapConnection->StartProcessingActiveEntries(); delete imapConnection; } /* start a imap4 load */ extern int MK_POP3_PASSWORD_UNDEFINED; //#define KEVIN_DEBUG 1 #if KEVIN_DEBUG static int gNumberOfVisits; static int gNumberOfVisitsProcessingEvents; static int gNumberOfEventsForCurrentUrl; #endif static void NotifyOfflineFolderLoad(TIMAPUrl ¤tUrl, MWContext *window_id) { mailbox_spec *notSelectedSpec = (mailbox_spec *) XP_CALLOC(1, sizeof( mailbox_spec)); if (notSelectedSpec) { notSelectedSpec->allocatedPathName =currentUrl.CreateCanonicalSourceFolderPathString(); notSelectedSpec->hostName =currentUrl.GetUrlHost(); notSelectedSpec->folderSelected = FALSE; notSelectedSpec->flagState = NULL; notSelectedSpec->onlineVerified = FALSE; UpdateIMAPMailboxInfo(notSelectedSpec, window_id); } } static XP_Bool gotPrefs = FALSE, useLibnetCacheForIMAP4Fetch; static XP_Bool gotUpgradePrefs = FALSE; static XP_Bool fTriedSavedPassword = FALSE; static XP_Bool triedUpgradingToSubscription = FALSE; static XP_Bool autoSubscribe = TRUE, autoUnsubscribe = TRUE, autoSubscribeOnOpen = TRUE, autoUnsubscribeFromNoSelect = TRUE; static XP_Bool allowMultipleFolderConnections = TRUE; static XP_Bool gGetHeaders = FALSE; static int32 gTooFastTime = 2; static int32 gIdealTime = 4; static int32 gChunkAddSize = 2048; static int32 gChunkSize = 10240; static int32 gChunkThreshold = 10240 + 4096; static XP_Bool gFetchByChunks = TRUE; static int32 gMaxChunkSize = 40960; /* static */ int TNavigatorImapConnection::SetupConnection(ActiveEntry * ce, MSG_Master *master, XP_Bool loadingURL) { int32 returnValue = MK_WAITING_FOR_CONNECTION; TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); XP_Bool thisHostUsingSubscription = FALSE; XP_Bool shouldUpgradeToSubscription = FALSE, upgradeLeaveAlone = FALSE, upgradeSubscribeAll = FALSE; if (!gotUpgradePrefs) { shouldUpgradeToSubscription = MSG_ShouldUpgradeToIMAPSubscription(master); PREF_GetBoolPref("mail.imap.upgrade.leave_subscriptions_alone", &upgradeLeaveAlone); PREF_GetBoolPref("mail.imap.upgrade.auto_subscribe_to_all", &upgradeSubscribeAll); gotUpgradePrefs = TRUE; } char *serverMailboxPath = (currentUrl.GetUrlIMAPstate() == TIMAPUrl::kAuthenticatedStateURL) ? 0 : currentUrl.CreateCanonicalSourceFolderPathString(); // if we are appending message even though it is in // TIMAPUrl::kAuthenticatedStateURL we should try to reuse the cached // connection. The reason for that is that the user might already have the // folder opened using a new connection to append message can cause the // thread window view out of sync. if (!serverMailboxPath && currentUrl.GetIMAPurlType() == TIMAPUrl::kAppendMsgFromFile) serverMailboxPath = currentUrl.CreateCanonicalSourceFolderPathString(); XP_Bool waitingForCachedConnection = FALSE; XP_Bool newConnection = FALSE; TNavigatorImapConnection *blockingConnection = NULL; if (master) { blockingConnection = MSG_UnCacheImapConnection(master, currentUrl.GetUrlHost(), serverMailboxPath); // if it's in the cache, by definition, it's not in use, I believe. // this code doesn't work if you don't have remember password turned on, because we never make a connection! if (!blockingConnection && IMAP_URLIsBiff(ce, currentUrl) && TNavigatorImapConnection::IsConnectionActive(currentUrl.GetUrlHost(), serverMailboxPath)) { PR_LOG(IMAP, out, ("URL: Biff blocking on active connection %s", ce->URL_s->address)); return -1; // we didn't have a cached connection but the inbox is busy, so biff should wait } } if (blockingConnection && (currentUrl.GetIMAPurlType() == TIMAPUrl::kSelectFolder || currentUrl.GetIMAPurlType() == TIMAPUrl::kDeleteAllMsgs)) blockingConnection->SetNeedsNoop(TRUE); if (blockingConnection && (blockingConnection->GetServerStateParser().GetIMAPstate() == TImapServerState::kFolderSelected) && (currentUrl.GetUrlIMAPstate() == TIMAPUrl::kAuthenticatedStateURL) ) { // We dont want to be in the selected state in case we operate on the selected mailbox // and we do not want an implicit Close() so shut this connection down IMAP_TerminateConnection(blockingConnection); blockingConnection = NULL; } ImapConData *newConnectionData = (ImapConData *) ce->con_data; if (blockingConnection) { MSG_SetFolderRunningIMAPUrl(ce->URL_s->msg_pane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_RunningOnline); // let's use the cached connection newConnectionData->netConnection = blockingConnection; ce->socket = blockingConnection->GetIOSocket(); ce->con_sock = ce->socket; ce->local_file = FALSE; NET_TotalNumberOfOpenConnections++; NET_SetReadSelect(ce->window_id, ce->socket); FE_SetProgressBarPercent(ce->window_id, -1); // I don't think I need to call FE_SetConnectSelect since we are already connected } else { if (!allowMultipleFolderConnections) { XP_Bool folderActive = TNavigatorImapConnection::IsConnectionActive(currentUrl.GetUrlHost(), serverMailboxPath); if (folderActive) { waitingForCachedConnection = TRUE; if (loadingURL) PR_LOG(IMAP, out, ("URL: Blocking on active connection %s", ce->URL_s->address)); ce->local_file = TRUE; // Note: Chrisf: This needs to be a PRFileDesc, we're not sure how to do this so we'll leave it for you // to fix when you get back... // ce->socket = 1; if (loadingURL) NET_SetCallNetlibAllTheTime(ce->window_id, "mkimap4"); } } // create the blocking connection object if (!waitingForCachedConnection) { HG73299 blockingConnection = TNavigatorImapConnection::Create(currentUrl.GetUrlHost()); } if (blockingConnection) { MSG_FolderInfo *folder = MSG_SetFolderRunningIMAPUrl(ce->URL_s->msg_pane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_RunningOnline); // Find out if delete is move-to-trash XP_Bool deleteIsMoveToTrash = MSG_GetIMAPHostDeleteIsMoveToTrash(master, currentUrl.GetUrlHost()); int threadPriority = 31; // medium priority was 15, but that may have slowed down thru-put #ifdef XP_UNIX threadPriority = 31; // highest priority for unix, especially SSL #endif newConnection = TRUE; // start the imap thread PRThread *imapThread = PR_CreateThread(PR_USER_THREAD, imap_thread_main_function, blockingConnection, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); // standard stack // save the thread object so an IMAP interrupt will // cause the TIMAP4BlockingConnection destructor to // destroy it. blockingConnection->SetBlockingThread(imapThread); newConnectionData->netConnection = blockingConnection; XP_Bool folderNeedsSubscribing = FALSE; XP_Bool folderNeedsACLRefreshed = FALSE; if (folder) { folderNeedsSubscribing = MSG_GetFolderNeedsSubscribing(folder); folderNeedsACLRefreshed = MSG_GetFolderNeedsACLListed(folder); } blockingConnection->SetFolderInfo(folder, folderNeedsSubscribing, folderNeedsACLRefreshed); blockingConnection->SetSubscribeParameters(autoSubscribe, autoUnsubscribe, autoSubscribeOnOpen, autoUnsubscribeFromNoSelect, shouldUpgradeToSubscription, upgradeLeaveAlone, upgradeSubscribeAll); blockingConnection->SetDeleteIsMoveToTrash(deleteIsMoveToTrash); blockingConnection->Configure(gGetHeaders, gTooFastTime, gIdealTime, gChunkAddSize, gChunkSize, gChunkThreshold, gFetchByChunks, gMaxChunkSize); } } if (blockingConnection) { blockingConnection->SetActive(TRUE); if (ce->window_id && ((ce->window_id->type == MWContextMail) || (ce->window_id->type == MWContextNews))) { FE_UpdateStopState(ce->window_id); } PR_Sleep(PR_INTERVAL_NO_WAIT); blockingConnection->SubmitActiveEntry(ce, newConnection); // force netlib to be called all the time, whether there is IO // or not. This enables us to process the fe event queue. // net_call_all_the_time_count is a global used by the existing netlib //if(net_call_all_the_time_count < 1) if (loadingURL) NET_SetCallNetlibAllTheTime(ce->window_id, "mkimap4"); blockingConnection->SetCallingNetlibAllTheTime(TRUE); // msg copy code has to use the TNavigatorImapConnection 'C' interface if (ce->window_id->msgCopyInfo) MSG_StoreNavigatorIMAPConnectionInMoveState(ce->window_id, blockingConnection); ce->window_id->imapConnection = blockingConnection; #if KEVIN_DEBUG gNumberOfVisits=0; gNumberOfVisitsProcessingEvents=0; gNumberOfEventsForCurrentUrl=0; #endif returnValue = NET_ProcessIMAP4(ce); } else if (!waitingForCachedConnection) { FE_Alert(ce->window_id, XP_GetString(MK_OUT_OF_MEMORY)); returnValue = -1; } FREEIF(serverMailboxPath); return returnValue; } MODULE_PRIVATE int32 NET_IMAP4Load (ActiveEntry * ce) { int returnValue=0; PR_LOG(IMAP, out, ("URL: Loading %s",ce->URL_s->address)); // Get the master and a master Pane. We will need them a few times later. MSG_Pane *masterPane = ce->URL_s->msg_pane; // we need some pane to get to the master, libimg will run fetch urls to get mime parts if (!masterPane) masterPane = MSG_FindPane(ce->window_id, MSG_ANYPANE); // last ditch effort, GROSS! MSG_Master *master = ce->window_id->mailMaster; if (!master) { master = MSG_GetMaster(masterPane); ce->window_id->mailMaster = master; } XP_ASSERT(master); // check to see if we have all the prefs we need for this load if (!gotPrefs) { // cacheing pref PREF_GetBoolPref("mail.imap.cache_fetch_responses",&useLibnetCacheForIMAP4Fetch); PREF_GetBoolPref("mail.imap.allow_multiple_folder_connections", &allowMultipleFolderConnections); // subscription prefs PREF_GetBoolPref("mail.imap.auto_subscribe",&autoSubscribe); PREF_GetBoolPref("mail.imap.auto_unsubscribe", &autoUnsubscribe); PREF_GetBoolPref("mail.imap.auto_subscribe_on_open",&autoSubscribeOnOpen); PREF_GetBoolPref("mail.imap.auto_unsubscribe_from_noselect_folders",&autoUnsubscribeFromNoSelect); // chunk size and header prefs PREF_GetBoolPref("mail.imap.new_mail_get_headers", &gGetHeaders); PREF_GetIntPref("mail.imap.chunk_fast", &gTooFastTime); // secs we read too little too fast PREF_GetIntPref("mail.imap.chunk_ideal", &gIdealTime); // secs we read enough in good time PREF_GetIntPref("mail.imap.chunk_add", &gChunkAddSize); // buffer size to add when wasting time PREF_GetIntPref("mail.imap.chunk_size", &gChunkSize); PREF_GetIntPref("mail.imap.min_chunk_size_threshold", &gChunkThreshold); PREF_GetBoolPref("mail.imap.fetch_by_chunks", &gFetchByChunks); PREF_GetIntPref("mail.imap.max_chunk_size", &gMaxChunkSize); // bodystructure prefs PREF_GetBoolPref("mail.imap.mime_parts_on_demand", &gMIMEOnDemand); PREF_GetIntPref("mail.imap.mime_parts_on_demand_threshold", &gMIMEOnDemandThreshold); PREF_GetBoolPref("mail.imap.optimize_header_dl", &gOptimizedHeaders); PREF_GetIntPref("mail.imap.tunnelling_threshold", &gTunnellingThreshold); PREF_GetBoolPref("mail.imap.io_tunnelling", &gIOTunnelling); gotPrefs = TRUE; } ce->window_id->imapConnection = NULL; // this might be simple note that the filtering is done if (!XP_STRCMP(ce->URL_s->address, kImapFilteringCompleteURL)) { MSG_ImapInboxFilteringComplete(ce->URL_s->msg_pane); return -1; } // this might be simple note that the on/off synch is done if (!XP_STRCMP(ce->URL_s->address, kImapOnOffSynchCompleteURL)) { MSG_ImapOnOffLineSynchComplete(ce->URL_s->msg_pane); return -1; } // if this url is bogus, be defensive and leave before we make a connection TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); if (!currentUrl.ValidIMAPUrl()) return -1; const char *password = NULL; TIMAPHostInfo::AddHostToList(currentUrl.GetUrlHost()); // try cached password for this host; if this fails, ask libmsg. XP_Bool savePassword = FALSE; password = TIMAPHostInfo::GetPasswordForHost(currentUrl.GetUrlHost()) ; if (!password) { savePassword = TRUE; password = MSG_GetIMAPHostPassword(ce->window_id->mailMaster, currentUrl.GetUrlHost()); } /* if (!fTriedSavedPassword) */ { // Try getting the password from the prefs, if it was saved if (savePassword && password && (XP_STRLEN(password) > 0)) { // make sure we actually have one in the prefs TIMAPHostInfo::SetPasswordForHost(currentUrl.GetUrlHost(), password); } fTriedSavedPassword = TRUE; } // if this is a biff and we have no valid password, then bail before we make a connection if (IMAP_URLIsBiff(ce, currentUrl)) { if (!MSG_GetIMAPHostIsSecure(master, currentUrl.GetUrlHost()) && !password) return -1; } // if we want to use the libnet cache: // if this is not a message fetch, make sure this url is not cached. How can // libnet cache a load mailbox command? duh. if (!useLibnetCacheForIMAP4Fetch || ((currentUrl.GetIMAPurlType() != TIMAPUrl::kMsgFetch) || currentUrl.MimePartSelectorDetected())) ce->format_out = CLEAR_CACHE_BIT(ce->format_out); // If this is a message fetch URL // See if it is a force-reload-all-attachments URL. If so, strip off the int whereStart = XP_STRLEN(ce->URL_s->address) - 9; // 9 == XP_STRLEN("&allparts") if (whereStart > 0) { if (!XP_STRNCASECMP(ce->URL_s->address + whereStart, "&allparts", 9)) { // It is a force reload all - strip off the "&allparts" *(ce->URL_s->address+whereStart) = 0; ce->URL_s->content_modified = TRUE; // tiny hack. Set this bit to mean this is a // forced reload. } } ImapConData *newConnectionData = (ImapConData *) XP_CALLOC(sizeof(ImapConData), 1); if (!newConnectionData) { FE_Alert(ce->window_id, XP_GetString(MK_OUT_OF_MEMORY)); return -1; } ce->con_data = newConnectionData; newConnectionData->offLineRetrievalData = NULL; newConnectionData->offlineDisplayStream = NULL; if ((currentUrl.GetIMAPurlType() == TIMAPUrl::kMsgFetch) && (currentUrl.MessageIdsAreUids()) ) { char *serverMailboxPath = currentUrl.CreateCanonicalSourceFolderPathString(); char *msgIdString = currentUrl.CreateListOfMessageIdsString(); MSG_Pane *masterPane = ce->URL_s->msg_pane; // we need some pane to get to the master, libimg will run fetch urls to get mime parts if (!masterPane) masterPane = MSG_FindPane(ce->window_id, MSG_ANYPANE); // last ditch effort, GROSS! newConnectionData->offLineMsgKey = atoint32(msgIdString); newConnectionData->offLineMsgFlags = MSG_GetImapMessageFlags(masterPane, currentUrl.GetUrlHost(), serverMailboxPath, newConnectionData->offLineMsgKey ); if ((newConnectionData->offLineMsgFlags & MSG_FLAG_OFFLINE) || NET_IsOffline()) { MSG_SetFolderRunningIMAPUrl(masterPane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_RunningOffline); ce->local_file = TRUE; ce->socket = NULL; NET_SetCallNetlibAllTheTime(ce->window_id, "mkimap4"); /// do not cache this url since its already local ce->format_out = CLEAR_CACHE_BIT(ce->format_out); MSG_StartOfflineImapRetrieval(masterPane, currentUrl.GetUrlHost(), serverMailboxPath, newConnectionData->offLineMsgKey, &newConnectionData->offLineRetrievalData); returnValue = NET_ProcessIMAP4(ce); } FREEIF(serverMailboxPath); FREEIF(msgIdString); } if (!newConnectionData->offLineRetrievalData && !NET_IsOffline()) { returnValue = TNavigatorImapConnection::SetupConnection(ce, master, TRUE); } else if (!newConnectionData->offLineRetrievalData && NET_IsOffline()) { // we handled offline fetch earlier, now handle offline select if (currentUrl.GetIMAPurlType() == TIMAPUrl::kSelectFolder) NotifyOfflineFolderLoad(currentUrl, ce->window_id); returnValue = MK_OFFLINE; } return returnValue; } // this is called from the memory cache to notify us that a cached imap message has finished loading void IMAP_URLFinished(URL_Struct *URL_s) { TIMAPUrl currentUrl(URL_s->address, URL_s->internal_url); char *serverMailboxPath = currentUrl.CreateCanonicalSourceFolderPathString(); MSG_FolderInfo *folder = MSG_SetFolderRunningIMAPUrl(URL_s->msg_pane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_NotRunning); FREEIF(serverMailboxPath); MSG_IMAPUrlFinished(folder, URL_s); } static MSG_FolderInfo *imap_clear_running_imap_url(ActiveEntry *ce) { TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); char *serverMailboxPath = currentUrl.CreateCanonicalSourceFolderPathString(); MSG_FolderInfo *folder = MSG_SetFolderRunningIMAPUrl(ce->URL_s->msg_pane, currentUrl.GetUrlHost(), serverMailboxPath, MSG_NotRunning); FREEIF(serverMailboxPath); return folder; } /* NET_ProcessOfflineIMAP4 will control the state machine that * loads messages from a imap db * * returns negative if the transfer is finished or error'd out * * returns zero or more if the transfer needs to be continued. */ static int NET_ProcessOfflineIMAP4 (ActiveEntry *ce, ImapConData *cd) { int keepCallingMe = 1; if (!cd->offlineDisplayStream) { if (ce->format_out == FO_PRESENT || ce->format_out == FO_CACHE_AND_PRESENT) { IMAP_InitializeImapFeData(ce); } ce->URL_s->content_length = MSG_GetOfflineMessageSize(cd->offLineRetrievalData); StrAllocCopy(ce->URL_s->content_type, ce->URL_s->content_length ? MESSAGE_RFC822 : TEXT_HTML); cd->offlineDisplayStream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id); if (!cd->offlineDisplayStream) ce->status = -1; } if (ce->status >= 0) { int32 read_size = (*cd->offlineDisplayStream->is_write_ready) (cd->offlineDisplayStream); if(!read_size) return(0); /* wait until we are ready to write */ else read_size = MIN(read_size, NET_Socket_Buffer_Size); ce->status = MSG_ProcessOfflineImap(cd->offLineRetrievalData, NET_Socket_Buffer, read_size); if(ce->status > 0) { ce->bytes_received += ce->status; (*cd->offlineDisplayStream->put_block) (cd->offlineDisplayStream, NET_Socket_Buffer, ce->status); } if (ce->status == 0) { if (cd->offlineDisplayStream) (*cd->offlineDisplayStream->complete) (cd->offlineDisplayStream); NET_ClearCallNetlibAllTheTime(ce->window_id, "mkimap4"); // clean up the offline info, in case we need to mark read online ce->local_file = FALSE; ce->socket = 0; cd->offLineRetrievalData = NULL; keepCallingMe = -1; } } if (ce->status < 0) keepCallingMe = -1; // here we need to check if we have offline events saved up while we were running this url // (and not plain old offline events getting played back). And we need to figure out how to run // them from here...because we will interrupt current url if not careful. if (keepCallingMe < 0) { // tell libmsg that we're not running an imap url for this folder anymore MSG_FolderInfo *folder = imap_clear_running_imap_url(ce); MSG_IMAPUrlFinished(folder, ce->URL_s); } return keepCallingMe; } static void imap_interrupt_possible_libmsg_select(ActiveEntry *ce) { // if this was a folder load then tell libmsg it was interrupted TIMAPUrl interruptedURL(ce->URL_s->address, ce->URL_s->internal_url); if (interruptedURL.GetIMAPurlType() == TIMAPUrl::kSelectFolder) { char *folderName = interruptedURL.CreateCanonicalSourceFolderPathString(); MSG_InterruptImapFolderLoad( MSG_FindPaneOfContext(ce->window_id, MSG_ANYPANE), interruptedURL.GetUrlHost(), folderName); FREEIF(folderName); } } void TNavigatorImapConnection::ResetProgressInfo() { LL_I2L(fLastProgressTime, 0); fLastPercent = -1; fLastProgressStringId = -1; } /* NET_ProcessIMAP4 will control the state machine that * loads messages from a imap4 server * * returns negative if the transfer is finished or error'd out * * returns zero or more if the transfer needs to be continued. */ MODULE_PRIVATE int32 NET_ProcessIMAP4 (ActiveEntry *ce) { #if KEVIN_DEBUG gNumberOfVisits++; #endif MSG_Master *currentMaster = ce->window_id->mailMaster; if (!currentMaster && ce->URL_s->msg_pane) // if there is no pane or master, then I can't find the master where the cache is currentMaster = MSG_GetMaster(ce->URL_s->msg_pane); ImapConData *cd = (ImapConData *) ce->con_data; XP_Bool shouldDieAtEnd = FALSE; if (cd->offLineRetrievalData) { return NET_ProcessOfflineIMAP4(ce, cd); } TNavigatorImapConnection *imapConnection = cd->netConnection; if (!imapConnection) { TNavigatorImapConnection::SetupConnection(ce, currentMaster, FALSE); return MK_WAITING_FOR_CONNECTION; } assert(imapConnection); // if this is a move/copy operation, be sure to let libmsg do its thing. if (imapConnection->CurrentConnectionIsMove() && ce->window_id->msgCopyInfo && !MSG_IsMessageCopyFinished(ce->window_id->msgCopyInfo)) { int finishReturn = MSG_FinishCopyingMessages(ce->window_id); if (finishReturn < 0) { // handled in ReportSuccessOfOnlineCopy } } // NotifyIOCompletionMonitor even if we end up here because we are calling netlib all of the time. // an extra NotifyIOCompletionMonitor will cause one extra poll of the port. // This can happen after we finish processing a bunch of fe events and go // into a BlockedForIO state. So, we might randomly do one extra poll per // blocked io read. After this poll, calling netlib off the time will // be turned off if we are still blocked on io. if (imapConnection->BlockedForIO()) { // we entered NET_ProcessIMAP4 because a net read is finished imapConnection->NotifyIOCompletionMonitor(); // will give time to imap thread } else PR_Sleep(PR_INTERVAL_NO_WAIT); // give the imap thread some time. int connectionStatus = imapConnection->GetConnectionStatus(); int eventLimitNotReached = 2; // if we don't limit the events, then huge downloads // are effectively synchronous #if KEVIN_DEBUG if (imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) gNumberOfVisitsProcessingEvents++; #endif while (eventLimitNotReached-- && (connectionStatus >= 0) && imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) { if (imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) { TImapFEEvent *event = imapConnection->GetFEEventQueue().OrphanFirstEvent(); event->DoEvent(); delete event; #if KEVIN_DEBUG gNumberOfEventsForCurrentUrl++; #endif } // PR_Sleep(PR_INTERVAL_NO_WAIT); // when this yield was here, a huge message download, // now that it is buffered, always caused an event // in the queue but never tripped eventLimitNotReached. // Since each buffered event is relatively slow, (e.g. // html parsing 1500 bytes) the effect was to cause // synchronous downloads connectionStatus = imapConnection->GetConnectionStatus(); } //if (eventLimitNotReached == -1) // DebugStr("\pTouch the sky"); if (connectionStatus < 0) { // we are done or errored out ce->window_id->imapConnection = NULL; XP_Bool diedWhileCallingNetlibSet = imapConnection->GetCallingNetlibAllTheTime(); // some events can fail and put another event in the queue // this is rare and catastrophic anyway so only process the number // of events we know about when we start this loop int numberOfEventsToProcess = imapConnection->GetFEEventQueue().NumberOfEventsInQueue(); while (numberOfEventsToProcess-- && imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) { TImapFEEvent *event = imapConnection->GetFEEventQueue().OrphanFirstEvent(); event->DoEvent(); delete event; #if KEVIN_DEBUG gNumberOfEventsForCurrentUrl++; #endif } imapConnection->SignalEventQueueEmpty(); // tell libmsg that we're not running an imap url for this folder anymore MSG_FolderInfo *folder = imap_clear_running_imap_url(ce); FREEIF(ce->con_data); if (ce->socket != 0) { // stop calling me, i am finished NET_ClearReadSelect(ce->window_id, ce->socket); NET_ClearConnectSelect(ce->window_id, ce->socket); NET_TotalNumberOfOpenConnections--; // maybe we can cache this connection XP_Bool wasCached = FALSE; if (currentMaster && // do not cache a non-authenticated or disconnected connection. (imapConnection->GetServerStateParser().GetIMAPstate() != TImapServerState::kNonAuthenticated) && (imapConnection->GetServerStateParser().Connected()) && !imapConnection->GetServerStateParser().SyntaxError()) { TIMAPUrl currentUrl(ce->URL_s->address, ce->URL_s->internal_url); imapConnection->ResetProgressInfo(); wasCached = MSG_TryToCacheImapConnection(currentMaster, currentUrl.GetUrlHost(), imapConnection->GetServerStateParser().GetSelectedMailboxName(), imapConnection); } if (!wasCached) { char *logoutString = PR_smprintf("xxxx logout\r\n"); if (logoutString) { PR_LOG(IMAP, out, ("%s",logoutString)); PR_LOG(IMAP, out, ("Logged out from NET_ProcessIMAP4")); NET_BlockingWrite(ce->socket, logoutString, XP_STRLEN(logoutString)); XP_FREEIF(logoutString); } if (!imapConnection->GetServerStateParser().Connected()) { imap_interrupt_possible_libmsg_select(ce); } shouldDieAtEnd = TRUE; imapConnection->SetIOSocket(NULL); // delete connection net_graceful_shutdown(ce->socket, HG87263); PR_Close(ce->socket); } else { imapConnection->SetActive(FALSE); } ce->socket = 0; } else shouldDieAtEnd = TRUE; // Whoa! // net_call_all_the_time_count is a global used by the existing netlib //net_call_all_the_time_count--; //if(net_call_all_the_time_count < 1) if (diedWhileCallingNetlibSet) { NET_ClearCallNetlibAllTheTime(ce->window_id, "mkimap4"); imapConnection->SetCallingNetlibAllTheTime(FALSE); } #if KEVIN_DEBUG char debugString[250]; sprintf(debugString, "visits = %ld, usefull visits = %ld, # of events = %ld", gNumberOfVisits, gNumberOfVisitsProcessingEvents, gNumberOfEventsForCurrentUrl); DebugStr(c2pstr(debugString)); #endif // here we need to check if we have offline events saved up while we were running this url // (and not plain old offline events getting played back). And we need to figure out how to run // them from here...because we will interrupt current url if not careful. MSG_IMAPUrlFinished(folder, ce->URL_s); } else { if (imapConnection->BlockedForIO() && !imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) { // if we are blocked and there are no events to process // then stop calling me until io completes if (imapConnection->GetCallingNetlibAllTheTime()) { imapConnection->SetCallingNetlibAllTheTime(FALSE); NET_ClearCallNetlibAllTheTime(ce->window_id, "mkimap4"); } } else if (!imapConnection->GetCallingNetlibAllTheTime()) { // the thread might need to process events, keep entering imapConnection->SetCallingNetlibAllTheTime(TRUE); NET_SetCallNetlibAllTheTime(ce->window_id, "mkimap4"); } } if (shouldDieAtEnd) imapConnection->TellThreadToDie(); // the imapConnection may now be deleted, so don't use it! return connectionStatus; } /* abort the connection in progress */ MODULE_PRIVATE int32 NET_InterruptIMAP4(ActiveEntry * ce) { PR_LOG(IMAP, out, ("INTERRUPT Entered")); ImapConData *cd = (ImapConData *) ce->con_data; TNavigatorImapConnection *imapConnection = cd->netConnection; if (!imapConnection || cd->offLineRetrievalData) { void *offLineRetrievalData = cd->offLineRetrievalData; ce->status = MK_INTERRUPTED; ce->window_id->imapConnection = NULL; FREEIF(cd); return MSG_InterruptOfflineImap(offLineRetrievalData); } // Whoa! // net_call_all_the_time_count is a global used by the existing netlib if (imapConnection->GetCallingNetlibAllTheTime()) { imapConnection->SetCallingNetlibAllTheTime(FALSE); NET_ClearCallNetlibAllTheTime(ce->window_id, "mkimap4"); } // tell libmsg that we're not running an imap url for this folder anymore imap_clear_running_imap_url(ce); // tell the imap thread that we are shutting down // pass FALSE and call SetIsSafeToDie later imapConnection->TellThreadToDie(FALSE); // some events can fail and put another event in the queue // this is rare and catastrophic anyway so only process the number // of events we know about when we start this loop int numberOfEventsToProcess = imapConnection->GetFEEventQueue().NumberOfEventsInQueue(); while (numberOfEventsToProcess-- && imapConnection->GetFEEventQueue().NumberOfEventsInQueue()) { TImapFEEvent *event = imapConnection->GetFEEventQueue().OrphanFirstEvent(); if (event->ShouldExecuteWhenInterrupted()) { if (event->GetEventFunction() == NormalEndMsgWriteStream) event->SetEventFunction(AbortMsgWriteStream); event->DoEvent(); } delete event; } imapConnection->SignalEventQueueEmpty(); imap_interrupt_possible_libmsg_select(ce); FREEIF(ce->con_data); ce->status = MK_INTERRUPTED; if (ce->socket != 0) { /* *** imapConnection->TellThreadToDie(FALSE) sends out the * logout command string already * char *logoutString = PR_smprintf("xxxx logout\r\n"); if (logoutString) { PR_LOG(IMAP, out, ("%s",logoutString)); PR_LOG(IMAP, out, ("Logged out from NET_InterruptIMAP4")); NET_BlockingWrite(ce->socket, logoutString, XP_STRLEN(logoutString)); XP_FREEIF(logoutString); } * */ NET_ClearReadSelect(ce->window_id, ce->socket); NET_ClearConnectSelect(ce->window_id, ce->socket); TRACEMSG(("Closing and clearing socket ce->socket: %d", ce->socket)); imapConnection->SetIOSocket(NULL); net_graceful_shutdown(ce->socket, HG83733); PR_Close(ce->socket); imapConnection->SetIOSocket(NULL); NET_TotalNumberOfOpenConnections--; ce->socket = 0; } // tell it to stop imapConnection->SetIsSafeToDie(); // the imapConnection may now be deleted, so don't use it! ce->window_id->imapConnection = NULL; return MK_INTERRUPTED; } /* called on shutdown to clean up */ extern "C" void net_CleanupIMAP4(void); extern "C" void net_CleanupIMAP4(void) { } extern "C" void NET_InitIMAP4Protocol(void) { static NET_ProtoImpl imap4_proto_impl; imap4_proto_impl.init = NET_IMAP4Load; imap4_proto_impl.process = NET_ProcessIMAP4; imap4_proto_impl.interrupt = NET_InterruptIMAP4; imap4_proto_impl.resume = NULL; imap4_proto_impl.cleanup = net_CleanupIMAP4; NET_RegisterProtocolImplementation(&imap4_proto_impl, IMAP_TYPE_URL); } TLineDownloadCache::TLineDownloadCache() { fLineInfo = (msg_line_info *) XP_ALLOC(sizeof( msg_line_info)); fLineInfo->adoptedMessageLine = fLineCache; fLineInfo->uidOfMessage = 0; fBytesUsed = 0; } TLineDownloadCache::~TLineDownloadCache() { FREEIF( fLineInfo); } uint32 TLineDownloadCache::CurrentUID() { return fLineInfo->uidOfMessage; } uint32 TLineDownloadCache::SpaceAvailable() { return kDownLoadCacheSize - fBytesUsed; } msg_line_info *TLineDownloadCache::GetCurrentLineInfo() { return fLineInfo; } void TLineDownloadCache::ResetCache() { fBytesUsed = 0; } XP_Bool TLineDownloadCache::CacheEmpty() { return fBytesUsed == 0; } void TLineDownloadCache::CacheLine(const char *line, uint32 uid) { uint32 lineLength = XP_STRLEN(line); XP_ASSERT((lineLength + 1) <= SpaceAvailable()); fLineInfo->uidOfMessage = uid; XP_STRCPY(fLineInfo->adoptedMessageLine + fBytesUsed, line); fBytesUsed += lineLength; } HG00374 TIMAPSocketInfo::TIMAPSocketInfo() : fIOSocket(0), pInputSocketBuffer(nil), pInputBufferSize(nil), newLine(nil), pauseForRead(FALSE), readStatus(-1) { } // TInboxReferenceCount functions PRMonitor *TInboxReferenceCount::fgDataSafeMonitor = nil; int TInboxReferenceCount::fgInboxUsageCount = 0; TInboxReferenceCount::TInboxReferenceCount(XP_Bool bumpInboxCount) { if (fgDataSafeMonitor) { PR_EnterMonitor(fgDataSafeMonitor); fInboxCountWasBumped = bumpInboxCount; if (fInboxCountWasBumped) fgInboxUsageCount++; PR_ExitMonitor(fgDataSafeMonitor); } else fInboxCountWasBumped = FALSE; } TInboxReferenceCount::~TInboxReferenceCount() { if (fInboxCountWasBumped && fgDataSafeMonitor) { PR_EnterMonitor(fgDataSafeMonitor); fgInboxUsageCount--; PR_ExitMonitor(fgDataSafeMonitor); } } int TInboxReferenceCount::GetInboxUsageCount() { int returnCount = 0; if (fgDataSafeMonitor) { PR_EnterMonitor(fgDataSafeMonitor); returnCount = fgInboxUsageCount; PR_ExitMonitor(fgDataSafeMonitor); } return returnCount; } //static void TInboxReferenceCount::ImapStartup() { if (!fgDataSafeMonitor) fgDataSafeMonitor = PR_NewNamedMonitor("inbox-data-safe-monitor"); } //static void TInboxReferenceCount::ImapShutDown() { if (fgDataSafeMonitor) { PR_DestroyMonitor(fgDataSafeMonitor); fgDataSafeMonitor = NULL; } } TIMAPMailboxInfo::TIMAPMailboxInfo(const char *name) { m_mailboxName = XP_STRDUP(name); m_childrenListed = FALSE; } TIMAPMailboxInfo::~TIMAPMailboxInfo() { FREEIF(m_mailboxName); } void IMAP_StartupImap() { TInboxReferenceCount::ImapStartup(); TNavigatorImapConnection::ImapStartup(); } void IMAP_ShutdownImap() { TInboxReferenceCount::ImapShutDown(); TNavigatorImapConnection::ImapShutDown(); } XP_Bool IMAP_HaveWeBeenAuthenticated() { return (gIMAPpassword || preAuthSucceeded); }