/* -*- 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 NSPR20
#ifdef XP_MAC
#include "prpriv.h"             /* for NewNamedMonitor */
#else
#include "private/prpriv.h"
#endif
#endif /* NSPR20 */

#define MAX_NEW_IMAP_FOLDER_COUNT 50
#define IMAP_DB_HEADERS "From To Cc Subject Date Priority X-Priority Message-ID References Newsgroups"

#ifndef NSPR20
#define IMAP_YIELD(A) PR_Yield()
#else
#define IMAP_YIELD(A) PR_Sleep(PR_INTERVAL_NO_WAIT)
#endif

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);
}

#ifdef NSPR20
extern PRLogModuleInfo* IMAP;
#define out PR_LOG_ALWAYS
#endif

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 &currentUrl)
{
	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);

    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);

    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()
{
#ifdef NSPR20
	PRIntervalTime sleepTime = PR_MicrosecondsToInterval(kImapSleepTime);
#else
	int64 sleepTime;
	LL_I2L(sleepTime, kImapSleepTime);
#endif

    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);  
        }  
    }


#ifndef NSPR20
	int64 sleepTime;
	LL_I2L(sleepTime, kImapSleepTime);
#else
	PRIntervalTime sleepTime = PR_MicrosecondsToInterval(kImapSleepTime);
#endif
	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.
#ifdef NSPR20
	PRIntervalTime sleepTime = kImapSleepTime;
#else
	int64 sleepTime;
	LL_I2L(sleepTime, kImapSleepTime);
#endif

    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()
{
#ifdef NSPR20
	PRIntervalTime sleepTime = kImapSleepTime;
#else
	int64 sleepTime;
	LL_I2L(sleepTime, kImapSleepTime);
#endif

    PR_EnterMonitor(fMsgCopyDataMonitor);
    while(!fMsgAppendSizeIsNew && !DeathSignalReceived())
        PR_Wait(fMsgCopyDataMonitor, sleepTime);
    PR_ExitMonitor(fMsgCopyDataMonitor);
}


void TNavigatorImapConnection::WaitForPotentialListOfMsgsToFetch(uint32 **msgIdList, uint32 &msgCount)
{
#ifdef NSPR20
	PRIntervalTime sleepTime = kImapSleepTime;
#else
	int64 sleepTime;
	LL_I2L(sleepTime, kImapSleepTime);
#endif

    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()
{
#ifdef NSPR20
	PRIntervalTime sleepTime = kImapSleepTime;
#else
	int64 sleepTime;
	LL_I2L(sleepTime, kImapSleepTime);
#endif

    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()
{
#ifdef NSPR20
	PRIntervalTime sleepTime = kImapSleepTime;
#else
	int64 sleepTime;
	LL_I2L(sleepTime, kImapSleepTime);
#endif

    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()
{
#ifdef NSPR20
	PRIntervalTime sleepTime = kImapSleepTime;
#else
	int64 sleepTime;
	LL_I2L(sleepTime, kImapSleepTime);
#endif

    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()
{
#ifdef NSPR20
	PRIntervalTime sleepTime = kImapSleepTime;
#else
	int64 sleepTime;
	LL_I2L(sleepTime, kImapSleepTime);
#endif

    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 <id>,<id>, or <id1>:<id2>
		char curChar = *uidString;
		XP_Bool isRange = FALSE;
		int32	curToken;
		int32	saveStartToken=0;

		for (char *curCharPtr = uidString; curChar && *curCharPtr;)
		{
			char *currentKeyToken = curCharPtr;
			curChar = *curCharPtr;
			while (curChar != ':' && curChar != ',' && curChar != '\0')
				curChar = *curCharPtr++;
			*(curCharPtr - 1) = '\0';
			curToken = atol(currentKeyToken);
			if (isRange)
			{
				while (saveStartToken < curToken)
				{
					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)
#ifndef NSPR20
imap_thread_main_function(void *imapConnectionVoid, void *)
#else
imap_thread_main_function(void *imapConnectionVoid)
#endif /* NSPR20 */
{
    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 &currentUrl, 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
#ifdef NSPR20
	        PRThread *imapThread = PR_CreateThread(PR_USER_THREAD, imap_thread_main_function, blockingConnection,
												   PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD,
												   0);		// standard stack
#else
	        PRThread *imapThread = PR_CreateThread("imapIOthread",
	                                                threadPriority,  
	                                                0);     // standard stack
#endif	            
	        // save the thread object so an IMAP interrupt will
	        // cause the TIMAP4BlockingConnection destructor to
	        // destroy it.                                                                          
	        blockingConnection->SetBlockingThread(imapThread);
	        newConnectionData->netConnection = blockingConnection;
#ifndef NSPR20
	        PR_Start(imapThread,                                            // thread to start
	                 imap_thread_main_function,             // first function to call
	                 blockingConnection,                            // function argument
	                 nil);                                                          // function argument
#endif


			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.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);
}
