diff options
Diffstat (limited to 'src/core/imap/MCIMAPSession.cpp')
-rwxr-xr-x | src/core/imap/MCIMAPSession.cpp | 3940 |
1 files changed, 3940 insertions, 0 deletions
diff --git a/src/core/imap/MCIMAPSession.cpp b/src/core/imap/MCIMAPSession.cpp new file mode 100755 index 00000000..f286cc52 --- /dev/null +++ b/src/core/imap/MCIMAPSession.cpp @@ -0,0 +1,3940 @@ +#include "MCWin32.h" // Should be included first. + +#include "MCIMAPSession.h" + +#include <libetpan/libetpan.h> +#include <string.h> +#include <stdlib.h> + +#include "MCDefines.h" +#include "MCIMAPSearchExpression.h" +#include "MCIMAPFolder.h" +#include "MCIMAPMessage.h" +#include "MCIMAPPart.h" +#include "MCMessageHeader.h" +#include "MCAbstractPart.h" +#include "MCIMAPProgressCallback.h" +#include "MCIMAPNamespace.h" +#include "MCIMAPSyncResult.h" +#include "MCIMAPFolderStatus.h" +#include "MCConnectionLogger.h" +#include "MCConnectionLoggerUtils.h" +#include "MCHTMLRenderer.h" +#include "MCString.h" +#include "MCUtils.h" +#include "MCHTMLRendererIMAPDataCallback.h" +#include "MCHTMLBodyRendererTemplateCallback.h" +#include "MCCertificateUtils.h" +#include "MCIMAPIdentity.h" +#include "MCLibetpan.h" + +using namespace mailcore; + +enum { + STATE_DISCONNECTED, + STATE_CONNECTED, + STATE_LOGGEDIN, + STATE_SELECTED, +}; + +String * mailcore::IMAPNamespacePersonal = NULL; +String * mailcore::IMAPNamespaceOther = NULL; +String * mailcore::IMAPNamespaceShared = NULL; + +static Array * resultsWithError(int r, clist * list, ErrorCode * pError); + +INITIALIZE(IMAPSEssion) +{ + AutoreleasePool * pool = new AutoreleasePool(); + IMAPNamespacePersonal = (String *) MCSTR("IMAPNamespacePersonal")->retain(); + IMAPNamespaceOther = (String *) MCSTR("IMAPNamespaceOther")->retain(); + IMAPNamespaceShared = (String *) MCSTR("IMAPNamespaceShared")->retain(); + + pool->release(); +} + +#define MAX_IDLE_DELAY (28 * 60) + +#define LOCK() pthread_mutex_lock(&mIdleLock) +#define UNLOCK() pthread_mutex_unlock(&mIdleLock) + +static struct mailimap_flag_list * flags_to_lep(MessageFlag value) +{ + struct mailimap_flag_list * flag_list; + + flag_list = mailimap_flag_list_new_empty(); + + if ((value & MessageFlagSeen) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_seen()); + } + + if ((value & MessageFlagFlagged) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flagged()); + } + + if ((value & MessageFlagDeleted) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_deleted()); + } + + if ((value & MessageFlagAnswered) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_answered()); + } + + if ((value & MessageFlagDraft) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_draft()); + } + + if ((value & MessageFlagForwarded) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flag_keyword(strdup("$Forwarded"))); + } + + if ((value & MessageFlagMDNSent) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flag_keyword(strdup("$MDNSent"))); + } + + if ((value & MessageFlagSubmitPending) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flag_keyword(strdup("$SubmitPending"))); + } + + if ((value & MessageFlagSubmitted) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flag_keyword(strdup("$Submitted"))); + } + + return flag_list; +} + +static MessageFlag flag_from_lep(struct mailimap_flag * flag) +{ + switch (flag->fl_type) { + case MAILIMAP_FLAG_ANSWERED: + return MessageFlagAnswered; + case MAILIMAP_FLAG_FLAGGED: + return MessageFlagFlagged; + case MAILIMAP_FLAG_DELETED: + return MessageFlagDeleted; + case MAILIMAP_FLAG_SEEN: + return MessageFlagSeen; + case MAILIMAP_FLAG_DRAFT: + return MessageFlagDraft; + case MAILIMAP_FLAG_KEYWORD: + if (strcasecmp(flag->fl_data.fl_keyword, "$Forwarded") == 0) { + return MessageFlagForwarded; + } + else if (strcasecmp(flag->fl_data.fl_keyword, "$MDNSent") == 0) { + return MessageFlagMDNSent; + } + else if (strcasecmp(flag->fl_data.fl_keyword, "$SubmitPending") == 0) { + return MessageFlagSubmitPending; + } + else if (strcasecmp(flag->fl_data.fl_keyword, "$Submitted") == 0) { + return MessageFlagSubmitted; + } + } + + return MessageFlagNone; +} + +static MessageFlag flags_from_lep_att_dynamic(struct mailimap_msg_att_dynamic * att_dynamic) +{ + if (att_dynamic->att_list == NULL) + return MessageFlagNone; + + MessageFlag flags; + clistiter * iter; + + flags = MessageFlagNone; + for(iter = clist_begin(att_dynamic->att_list) ;iter != NULL ; iter = clist_next(iter)) { + struct mailimap_flag_fetch * flag_fetch; + struct mailimap_flag * flag; + + flag_fetch = (struct mailimap_flag_fetch *) clist_content(iter); + if (flag_fetch->fl_type != MAILIMAP_FLAG_FETCH_OTHER) { + continue; + } + + flag = flag_fetch->fl_flag; + flags = (MessageFlag) (flags | flag_from_lep(flag)); + } + + return flags; +} + +static bool isKnownCustomFlag(const char * keyword) +{ + return !(strcmp(keyword, "$MDNSent") != 0 && strcmp(keyword, "$Forwarded") != 0 && strcmp(keyword, "$SubmitPending") != 0 && strcmp(keyword, "$Submitted") != 0); +} + +static Array * custom_flags_from_lep_att_dynamic(struct mailimap_msg_att_dynamic * att_dynamic) +{ + if (att_dynamic->att_list == NULL) + return NULL; + + clistiter * iter; + bool hasCustomFlags = false; + + for (iter = clist_begin(att_dynamic->att_list); iter != NULL; iter = clist_next(iter)) { + struct mailimap_flag_fetch * flag_fetch; + struct mailimap_flag * flag; + + flag_fetch = (struct mailimap_flag_fetch *) clist_content(iter); + if (flag_fetch->fl_type != MAILIMAP_FLAG_FETCH_OTHER) { + continue; + } + + flag = flag_fetch->fl_flag; + if (flag->fl_type == MAILIMAP_FLAG_KEYWORD) { + if (!isKnownCustomFlag(flag->fl_data.fl_keyword)) { + hasCustomFlags = true; + } + } + } + + if (!hasCustomFlags) + return NULL; + + Array * result = Array::array(); + for (iter = clist_begin(att_dynamic->att_list); iter != NULL; iter = clist_next(iter)) { + struct mailimap_flag_fetch * flag_fetch; + struct mailimap_flag * flag; + + flag_fetch = (struct mailimap_flag_fetch *) clist_content(iter); + if (flag_fetch->fl_type != MAILIMAP_FLAG_FETCH_OTHER) { + continue; + } + + flag = flag_fetch->fl_flag; + if (flag->fl_type == MAILIMAP_FLAG_KEYWORD) { + if (!isKnownCustomFlag(flag->fl_data.fl_keyword)) { + String * customFlag; + customFlag = String::stringWithUTF8Characters(flag->fl_data.fl_keyword); + result->addObject(customFlag); + } + } + } + + return result; +} + +#pragma mark set conversion + +static Array * arrayFromSet(struct mailimap_set * imap_set) +{ + Array * result; + clistiter * iter; + + result = Array::array(); + for(iter = clist_begin(imap_set->set_list) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set_item * item; + unsigned long i; + + item = (struct mailimap_set_item *) clist_content(iter); + for(i = item->set_first ; i <= item->set_last ; i ++) { + Value * nb; + + nb = Value::valueWithUnsignedLongValue(i); + result->addObject(nb); + } + } + + return result; +} + +static clist * splitSet(struct mailimap_set * set, unsigned int splitCount) +{ + struct mailimap_set * current_set; + clist * result; + unsigned int count; + + result = clist_new(); + + current_set = NULL; + count = 0; + for(clistiter * iter = clist_begin(set->set_list) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set_item * item; + + if (current_set == NULL) { + current_set = mailimap_set_new_empty(); + } + + item = (struct mailimap_set_item *) clist_content(iter); + mailimap_set_add_interval(current_set, item->set_first, item->set_last); + count ++; + + if (count >= splitCount) { + clist_append(result, current_set); + current_set = NULL; + count = 0; + } + } + if (current_set != NULL) { + clist_append(result, current_set); + } + + return result; +} + +static struct mailimap_set * setFromIndexSet(IndexSet * indexSet) +{ + struct mailimap_set * imap_set; + + imap_set = mailimap_set_new_empty(); + for(unsigned int i = 0 ; i < indexSet->rangesCount() ; i ++) { + uint64_t left = RangeLeftBound(indexSet->allRanges()[i]); + uint64_t right = RangeRightBound(indexSet->allRanges()[i]); + if (right == UINT64_MAX) { + right = 0; + } + mailimap_set_add_interval(imap_set, (uint32_t) left, (uint32_t) right); + } + + return imap_set; +} + +static IndexSet * indexSetFromSet(struct mailimap_set * imap_set) +{ + IndexSet * indexSet = IndexSet::indexSet(); + for(clistiter * cur = clist_begin(imap_set->set_list) ; cur != NULL ; cur = clist_next(cur)) { + struct mailimap_set_item * item = (struct mailimap_set_item *) clist_content(cur); + if (item->set_last == 0) { + indexSet->addRange(RangeMake(item->set_first, UINT64_MAX)); + } + else { + indexSet->addRange(RangeMake(item->set_first, item->set_last - item->set_first)); + } + } + return indexSet; +} + +void IMAPSession::init() +{ + mHostname = NULL; + mPort = 0; + mUsername = NULL; + mPassword = NULL; + mOAuth2Token = NULL; + mAuthType = AuthTypeSASLNone; + mConnectionType = ConnectionTypeClear; + mCheckCertificateEnabled = true; + mVoIPEnabled = true; + mDelimiter = 0; + + mBodyProgressEnabled = true; + mIdleEnabled = false; + mXListEnabled = false; + mQResyncEnabled = false; + mCondstoreEnabled = false; + mIdentityEnabled = false; + mNamespaceEnabled = false; + mCompressionEnabled = false; + mIsGmail = false; + mAllowsNewPermanentFlags = false; + mWelcomeString = NULL; + mNeedsMboxMailWorkaround = false; + mDefaultNamespace = NULL; + mServerIdentity = new IMAPIdentity(); + mClientIdentity = new IMAPIdentity(); + mTimeout = 30; + mUIDValidity = 0; + mUIDNext = 0; + mModSequenceValue = 0; + mFolderMsgCount = 0; + mFirstUnseenUid = 0; + mYahooServer = false; + mLastFetchedSequenceNumber = 0; + mCurrentFolder = NULL; + pthread_mutex_init(&mIdleLock, NULL); + mCanIdle = true; + mState = STATE_DISCONNECTED; + mImap = NULL; + mProgressCallback = NULL; + mProgressItemsCount = 0; + mConnectionLogger = NULL; + mAutomaticConfigurationEnabled = true; + mAutomaticConfigurationDone = false; + mShouldDisconnect = false; + mLoginResponse = NULL; + mGmailUserDisplayName = NULL; +} + +IMAPSession::IMAPSession() +{ + init(); +} + +IMAPSession::~IMAPSession() +{ + MC_SAFE_RELEASE(mGmailUserDisplayName); + MC_SAFE_RELEASE(mLoginResponse); + MC_SAFE_RELEASE(mClientIdentity); + MC_SAFE_RELEASE(mServerIdentity); + MC_SAFE_RELEASE(mHostname); + MC_SAFE_RELEASE(mUsername); + MC_SAFE_RELEASE(mPassword); + MC_SAFE_RELEASE(mOAuth2Token); + MC_SAFE_RELEASE(mWelcomeString); + MC_SAFE_RELEASE(mDefaultNamespace); + MC_SAFE_RELEASE(mCurrentFolder); + pthread_mutex_destroy(&mIdleLock); +} + +void IMAPSession::setHostname(String * hostname) +{ + MC_SAFE_REPLACE_COPY(String, mHostname, hostname); +} + +String * IMAPSession::hostname() +{ + return mHostname; +} + +void IMAPSession::setPort(unsigned int port) +{ + mPort = port; +} + +unsigned int IMAPSession::port() +{ + return mPort; +} + +void IMAPSession::setUsername(String * username) +{ + MC_SAFE_REPLACE_COPY(String, mUsername, username); +} + +String * IMAPSession::username() +{ + return mUsername; +} + +void IMAPSession::setPassword(String * password) +{ + MC_SAFE_REPLACE_COPY(String, mPassword, password); +} + +String * IMAPSession::password() +{ + return mPassword; +} + +void IMAPSession::setOAuth2Token(String * token) +{ + MC_SAFE_REPLACE_COPY(String, mOAuth2Token, token); +} + +String * IMAPSession::OAuth2Token() +{ + return mOAuth2Token; +} + +void IMAPSession::setAuthType(AuthType authType) +{ + mAuthType = authType; +} + +AuthType IMAPSession::authType() +{ + return mAuthType; +} + +void IMAPSession::setConnectionType(ConnectionType connectionType) +{ + mConnectionType = connectionType; +} + +ConnectionType IMAPSession::connectionType() +{ + return mConnectionType; +} + +void IMAPSession::setTimeout(time_t timeout) +{ + mTimeout = timeout; +} + +time_t IMAPSession::timeout() +{ + return mTimeout; +} + +void IMAPSession::setCheckCertificateEnabled(bool enabled) +{ + mCheckCertificateEnabled = enabled; +} + +bool IMAPSession::isCheckCertificateEnabled() +{ + return mCheckCertificateEnabled; +} + +void IMAPSession::setVoIPEnabled(bool enabled) +{ + mVoIPEnabled = enabled; +} + +bool IMAPSession::isVoIPEnabled() +{ + return mVoIPEnabled; +} + +static bool hasError(int errorCode) +{ + return ((errorCode != MAILIMAP_NO_ERROR) && (errorCode != MAILIMAP_NO_ERROR_AUTHENTICATED) && + (errorCode != MAILIMAP_NO_ERROR_NON_AUTHENTICATED)); +} + +bool IMAPSession::checkCertificate() +{ + if (!isCheckCertificateEnabled()) + return true; + return mailcore::checkCertificate(mImap->imap_stream, hostname()); +} + +void IMAPSession::body_progress(size_t current, size_t maximum, void * context) +{ + IMAPSession * session; + + session = (IMAPSession *) context; + session->bodyProgress((unsigned int) current, (unsigned int) maximum); +} + +void IMAPSession::items_progress(size_t current, size_t maximum, void * context) +{ + IMAPSession * session; + + session = (IMAPSession *) context; + session->itemsProgress((unsigned int) current, (unsigned int) maximum); +} + +static void logger(mailimap * imap, int log_type, const char * buffer, size_t size, void * context) +{ + IMAPSession * session = (IMAPSession *) context; + + if (session->connectionLogger() == NULL) + return; + + ConnectionLogType type = getConnectionType(log_type); + if ((int) type == -1) + return; + + bool isBuffer = isBufferFromLogType(log_type); + + if (isBuffer) { + Data * data = Data::dataWithBytes(buffer, (unsigned int) size); + session->connectionLogger()->log(session, type, data); + } + else { + session->connectionLogger()->log(session, type, NULL); + } +} + +void IMAPSession::setup() +{ + MCAssert(mImap == NULL); + + mImap = mailimap_new(0, NULL); + mailimap_set_timeout(mImap, timeout()); + mailimap_set_progress_callback(mImap, body_progress, IMAPSession::items_progress, this); + mailimap_set_logger(mImap, logger, this); +} + +void IMAPSession::unsetup() +{ + mailimap * imap; + + LOCK(); + imap = mImap; + mImap = NULL; + mCanIdle = false; + UNLOCK(); + + if (imap != NULL) { + if (imap->imap_stream != NULL) { + mailstream_close(imap->imap_stream); + imap->imap_stream = NULL; + } + mailimap_free(imap); + imap = NULL; + } + + mState = STATE_DISCONNECTED; +} + +void IMAPSession::connect(ErrorCode * pError) +{ + int r; + + setup(); + + MCLog("connect %s", MCUTF8DESC(this)); + + MCAssert(mState == STATE_DISCONNECTED); + + switch (mConnectionType) { + case ConnectionTypeStartTLS: + MCLog("STARTTLS connect"); + r = mailimap_socket_connect_voip(mImap, MCUTF8(mHostname), mPort, isVoIPEnabled()); + if (hasError(r)) { + * pError = ErrorConnection; + goto close; + } + + r = mailimap_socket_starttls(mImap); + if (hasError(r)) { + MCLog("no TLS %i", r); + * pError = ErrorTLSNotAvailable; + goto close; + } + break; + + case ConnectionTypeTLS: + r = mailimap_ssl_connect_voip(mImap, MCUTF8(mHostname), mPort, isVoIPEnabled()); + MCLog("ssl connect %s %u %u", MCUTF8(mHostname), mPort, r); + if (hasError(r)) { + MCLog("connect error %i", r); + * pError = ErrorConnection; + goto close; + } + if (!checkCertificate()) { + MCLog("ssl connect certificate ERROR %d", r); + * pError = ErrorCertificate; + goto close; + } + + break; + + default: + MCLog("socket connect %s %u", MCUTF8(mHostname), mPort); + r = mailimap_socket_connect_voip(mImap, MCUTF8(mHostname), mPort, isVoIPEnabled()); + MCLog("socket connect %i", r); + if (hasError(r)) { + MCLog("connect error %i", r); + * pError = ErrorConnection; + goto close; + } + break; + } + + mailstream_low * low; + String * identifierString; + char * identifier; + + low = mailstream_get_low(mImap->imap_stream); + identifierString = String::stringWithUTF8Format("%s@%s:%u", MCUTF8(mUsername), MCUTF8(mHostname), mPort); + identifier = strdup(identifierString->UTF8Characters()); + mailstream_low_set_identifier(low, identifier); + + if (mImap->imap_response != NULL) { + MC_SAFE_REPLACE_RETAIN(String, mWelcomeString, String::stringWithUTF8Characters(mImap->imap_response)); + mYahooServer = (mWelcomeString->locationOfString(MCSTR("yahoo.com")) != -1); + } + + mState = STATE_CONNECTED; + + if (isAutomaticConfigurationEnabled()) { + if ((mImap->imap_connection_info != NULL) && (mImap->imap_connection_info->imap_capability != NULL)) { + // Don't keep result. It will be kept in session state. + capabilitySetWithSessionState(IndexSet::indexSet()); + } + else { + capability(pError); + if (* pError != ErrorNone) { + MCLog("capabilities failed"); + goto close; + } + } + } + + * pError = ErrorNone; + MCLog("connect ok"); + return; + +close: + unsetup(); +} + +void IMAPSession::connectIfNeeded(ErrorCode * pError) +{ + if (mShouldDisconnect) { + disconnect(); + mShouldDisconnect = false; + } + + if (mState == STATE_DISCONNECTED) { + connect(pError); + } + else { + * pError = ErrorNone; + } +} + +void IMAPSession::loginIfNeeded(ErrorCode * pError) +{ + connectIfNeeded(pError); + if (* pError != ErrorNone) + return; + + if (mState == STATE_CONNECTED) { + login(pError); + } + else { + * pError = ErrorNone; + } +} + +void IMAPSession::login(ErrorCode * pError) +{ + int r; + + MCLog("login"); + + MCAssert(mState == STATE_CONNECTED); + + if (mImap->imap_connection_info != NULL) { + if (mImap->imap_connection_info->imap_capability != NULL) { + mailimap_capability_data_free(mImap->imap_connection_info->imap_capability); + mImap->imap_connection_info->imap_capability = NULL; + } + } + + const char * utf8username; + const char * utf8password; + utf8username = MCUTF8(mUsername); + utf8password = MCUTF8(mPassword); + if (utf8username == NULL) { + utf8username = ""; + } + if (utf8password == NULL) { + utf8password = ""; + } + + switch (mAuthType) { + case 0: + default: + r = mailimap_login(mImap, utf8username, utf8password); + break; + + case AuthTypeSASLCRAMMD5: + r = mailimap_authenticate(mImap, "CRAM-MD5", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLPlain: + r = mailimap_authenticate(mImap, "PLAIN", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLGSSAPI: + // needs to be tested + r = mailimap_authenticate(mImap, "GSSAPI", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL /* realm */); + break; + + case AuthTypeSASLDIGESTMD5: + r = mailimap_authenticate(mImap, "DIGEST-MD5", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLLogin: + r = mailimap_authenticate(mImap, "LOGIN", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLSRP: + r = mailimap_authenticate(mImap, "SRP", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLNTLM: + r = mailimap_authenticate(mImap, "NTLM", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL/* realm */); + break; + + case AuthTypeSASLKerberosV4: + r = mailimap_authenticate(mImap, "KERBEROS_V4", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL/* realm */); + break; + + case AuthTypeXOAuth2: + case AuthTypeXOAuth2Outlook: + r = mailimap_oauth2_authenticate(mImap, utf8username, MCUTF8(mOAuth2Token)); + break; + } + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + String * response; + + response = MCSTR(""); + if (mImap->imap_response != NULL) { + response = String::stringWithUTF8Characters(mImap->imap_response); + } + if (response->locationOfString(MCSTR("not enabled for IMAP use")) != -1) { + * pError = ErrorGmailIMAPNotEnabled; + } + else if (response->locationOfString(MCSTR("bandwidth limits")) != -1) { + * pError = ErrorGmailExceededBandwidthLimit; + } + else if (response->locationOfString(MCSTR("Too many simultaneous connections")) != -1) { + * pError = ErrorGmailTooManySimultaneousConnections; + } + else if (response->locationOfString(MCSTR("Maximum number of connections")) != -1) { + * pError = ErrorGmailTooManySimultaneousConnections; + } + else if (response->locationOfString(MCSTR("Application-specific password required")) != -1) { + * pError = ErrorGmailApplicationSpecificPasswordRequired; + } + else if (response->locationOfString(MCSTR("http://me.com/move")) != -1) { + * pError = ErrorMobileMeMoved; + } + else if (response->locationOfString(MCSTR("OCF12")) != -1) { + * pError = ErrorYahooUnavailable; + } + else { + * pError = ErrorAuthentication; + } + return; + } + + String * loginResponse = MCSTR(""); + if (mIsGmail) { + if (mImap->imap_response != NULL) { + loginResponse = String::stringWithUTF8Characters(mImap->imap_response); + + int location = loginResponse->locationOfString(MCSTR(" authenticated (Success)")); + if (location != -1) { + String * emailAndName = loginResponse->substringToIndex(location); + location = emailAndName->locationOfString(MCSTR(" ")); + MC_SAFE_RELEASE(mGmailUserDisplayName); + mGmailUserDisplayName = emailAndName->substringFromIndex(location + 1); + mGmailUserDisplayName->retain(); + } + } + } + MC_SAFE_REPLACE_COPY(String, mLoginResponse, loginResponse); + + mState = STATE_LOGGEDIN; + + if (isAutomaticConfigurationEnabled()) { + if ((mImap->imap_connection_info != NULL) && (mImap->imap_connection_info->imap_capability != NULL)) { + // Don't keep result. It will be kept in session state. + capabilitySetWithSessionState(IndexSet::indexSet()); + } + else { + capability(pError); + if (* pError != ErrorNone) { + MCLog("capabilities failed"); + return; + } + } + } + else { + // TODO: capabilities should be shared with other sessions for non automatic capabilities sessions. + } + enableFeatures(); + + if (isAutomaticConfigurationEnabled()) { + bool hasDefaultNamespace = false; + if (isNamespaceEnabled()) { + HashMap * result = fetchNamespace(pError); + if (* pError != ErrorNone) { + MCLog("fetch namespace failed"); + return; + } + IMAPNamespace * personalNamespace = (IMAPNamespace *) result->objectForKey(IMAPNamespacePersonal); + if (personalNamespace != NULL) { + setDefaultNamespace(personalNamespace); + mDelimiter = defaultNamespace()->mainDelimiter(); + hasDefaultNamespace = true; + } + } + + if (!hasDefaultNamespace) { + clist * imap_folders; + IMAPFolder * folder; + Array * folders; + + r = mailimap_list(mImap, "", "", &imap_folders); + folders = resultsWithError(r, imap_folders, pError); + if (* pError != ErrorNone) + return; + + if (folders->count() > 0) { + folder = (IMAPFolder *) folders->objectAtIndex(0); + } + else { + folder = NULL; + } + if (folder == NULL) { + * pError = ErrorNonExistantFolder; + return; + } + + mDelimiter = folder->delimiter(); + IMAPNamespace * defaultNamespace = IMAPNamespace::namespaceWithPrefix(MCSTR(""), folder->delimiter()); + setDefaultNamespace(defaultNamespace); + } + + if (isIdentityEnabled()) { + IMAPIdentity * serverIdentity = identity(clientIdentity(), pError); + if (* pError != ErrorNone) { + // Ignore identity errors + MCLog("fetch identity failed"); + } + else { + MC_SAFE_REPLACE_RETAIN(IMAPIdentity, mServerIdentity, serverIdentity); + } + } + } + else { + // TODO: namespace should be shared with other sessions for non automatic namespace. + } + + mAutomaticConfigurationDone = true; + + * pError = ErrorNone; + MCLog("login ok"); +} + +void IMAPSession::selectIfNeeded(String * folder, ErrorCode * pError) +{ + loginIfNeeded(pError); + if (* pError != ErrorNone) + return; + + if (mState == STATE_SELECTED) { + MCAssert(mCurrentFolder != NULL); + if (mCurrentFolder->caseInsensitiveCompare(folder) != 0) { + select(folder, pError); + } + } + else if (mState == STATE_LOGGEDIN) { + select(folder, pError); + } + else { + * pError = ErrorNone; + } +} + +static uint64_t get_mod_sequence_value(mailimap * session) +{ + uint64_t mod_sequence_value; + clistiter * cur; + + mod_sequence_value = 0; + for(cur = clist_begin(session->imap_response_info->rsp_extension_list) ; cur != NULL ; cur = clist_next(cur)) { + struct mailimap_extension_data * ext_data; + struct mailimap_condstore_resptextcode * resptextcode; + + ext_data = (struct mailimap_extension_data *) clist_content(cur); + if (ext_data->ext_extension->ext_id != MAILIMAP_EXTENSION_CONDSTORE) { + continue; + } + if (ext_data->ext_type != MAILIMAP_CONDSTORE_TYPE_RESP_TEXT_CODE) { + continue; + } + + resptextcode = (struct mailimap_condstore_resptextcode *) ext_data->ext_data; + switch (resptextcode->cs_type) { + case MAILIMAP_CONDSTORE_RESPTEXTCODE_HIGHESTMODSEQ: + mod_sequence_value = resptextcode->cs_data.cs_modseq_value; + break; + case MAILIMAP_CONDSTORE_RESPTEXTCODE_NOMODSEQ: + mod_sequence_value = 0; + break; + } + } + + return mod_sequence_value; +} + +void IMAPSession::select(String * folder, ErrorCode * pError) +{ + int r; + + MCLog("select"); + MCAssert(mState == STATE_LOGGEDIN || mState == STATE_SELECTED); + + r = mailimap_select(mImap, MCUTF8(folder)); + MCLog("select error : %i", r); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + MCLog("select error : %s %i", MCUTF8DESC(this), * pError); + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorNonExistantFolder; + mState = STATE_LOGGEDIN; + MC_SAFE_RELEASE(mCurrentFolder); + return; + } + + MC_SAFE_REPLACE_COPY(String, mCurrentFolder, folder); + + if (mImap->imap_selection_info != NULL) { + mUIDValidity = mImap->imap_selection_info->sel_uidvalidity; + mUIDNext = mImap->imap_selection_info->sel_uidnext; + if (mImap->imap_selection_info->sel_has_exists) { + mFolderMsgCount = (unsigned int) (mImap->imap_selection_info->sel_exists); + } else { + mFolderMsgCount = -1; + } + + if (mImap->imap_selection_info->sel_first_unseen) { + mFirstUnseenUid = mImap->imap_selection_info->sel_first_unseen; + } else { + mFirstUnseenUid = 0; + } + + if (mImap->imap_selection_info->sel_perm_flags) { + clistiter * cur; + + struct mailimap_flag_perm * perm_flag; + for(cur = clist_end(mImap->imap_selection_info->sel_perm_flags) ; cur != NULL ; + cur = clist_previous(cur)) { + perm_flag = (struct mailimap_flag_perm *)clist_content(cur); + mAllowsNewPermanentFlags = perm_flag->fl_type == MAILIMAP_FLAG_PERM_ALL; + if (mAllowsNewPermanentFlags) { + break; + } + } + } + + mModSequenceValue = get_mod_sequence_value(mImap); + } + + mState = STATE_SELECTED; + * pError = ErrorNone; + MCLog("select ok"); +} + + + + + +IMAPFolderStatus * IMAPSession::folderStatus(String * folder, ErrorCode * pError) +{ + int r; + + MCLog("status"); + MCAssert(mState == STATE_LOGGEDIN || mState == STATE_SELECTED); + + struct mailimap_mailbox_data_status * status; + + struct mailimap_status_att_list * status_att_list; + + status_att_list = mailimap_status_att_list_new_empty(); + mailimap_status_att_list_add(status_att_list, MAILIMAP_STATUS_ATT_UNSEEN); + mailimap_status_att_list_add(status_att_list, MAILIMAP_STATUS_ATT_MESSAGES); + mailimap_status_att_list_add(status_att_list, MAILIMAP_STATUS_ATT_RECENT); + mailimap_status_att_list_add(status_att_list, MAILIMAP_STATUS_ATT_UIDNEXT); + mailimap_status_att_list_add(status_att_list, MAILIMAP_STATUS_ATT_UIDVALIDITY); + if (mCondstoreEnabled) { + mailimap_status_att_list_add(status_att_list, MAILIMAP_STATUS_ATT_HIGHESTMODSEQ); + } + + r = mailimap_status(mImap, MCUTF8(folder), status_att_list, &status); + + IMAPFolderStatus * fs; + fs = new IMAPFolderStatus(); + fs->autorelease(); + + MCLog("status error : %i", r); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + MCLog("status error : %s %i", MCUTF8DESC(this), * pError); + return fs; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return fs; + } + else if (hasError(r)) { + * pError = ErrorNonExistantFolder; + return fs; + } + + clistiter * cur; + + + if (status != NULL) { + + struct mailimap_status_info * status_info; + for(cur = clist_begin(status->st_info_list) ; cur != NULL ; + cur = clist_next(cur)) { + + status_info = (struct mailimap_status_info *) clist_content(cur); + + switch (status_info->st_att) { + case MAILIMAP_STATUS_ATT_UNSEEN: + fs->setUnseenCount(status_info->st_value); + break; + case MAILIMAP_STATUS_ATT_MESSAGES: + fs->setMessageCount(status_info->st_value); + break; + case MAILIMAP_STATUS_ATT_RECENT: + fs->setRecentCount(status_info->st_value); + break; + case MAILIMAP_STATUS_ATT_UIDNEXT: + fs->setUidNext(status_info->st_value); + break; + case MAILIMAP_STATUS_ATT_UIDVALIDITY: + fs->setUidValidity(status_info->st_value); + break; + case MAILIMAP_STATUS_ATT_EXTENSION: { + struct mailimap_extension_data * ext_data = status_info->st_ext_data; + if (ext_data->ext_extension == &mailimap_extension_condstore) { + struct mailimap_condstore_status_info * status_info = (struct mailimap_condstore_status_info *) ext_data->ext_data; + fs->setHighestModSeqValue(status_info->cs_highestmodseq_value); + } + break; + } + } + } + + + } + + mailimap_status_att_list_free(status_att_list); + + + return fs; +} + +void IMAPSession::noop(ErrorCode * pError) +{ + int r; + + if (mImap == NULL) + return; + + MCLog("connect"); + loginIfNeeded(pError); + if (* pError != ErrorNone) { + return; + } + if (mImap->imap_stream != NULL) { + r = mailimap_noop(mImap); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + } + if (r == MAILIMAP_ERROR_NOOP) { + * pError = ErrorNoop; + } + } +} + +#pragma mark mailbox flags conversion + +static struct { + const char * name; + int flag; +} mb_keyword_flag[] = { + {"Inbox", IMAPFolderFlagInbox}, + {"AllMail", IMAPFolderFlagAllMail}, + {"Sent", IMAPFolderFlagSentMail}, + {"Spam", IMAPFolderFlagSpam}, + {"Starred", IMAPFolderFlagStarred}, + {"Trash", IMAPFolderFlagTrash}, + {"Important", IMAPFolderFlagImportant}, + {"Drafts", IMAPFolderFlagDrafts}, + {"Archive", IMAPFolderFlagArchive}, + {"All", IMAPFolderFlagAll}, + {"Junk", IMAPFolderFlagJunk}, + {"Flagged", IMAPFolderFlagFlagged}, +}; + +static int imap_mailbox_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags) +{ + int flags; + clistiter * cur; + + flags = 0; + if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) { + switch (imap_flags->mbf_sflag) { + case MAILIMAP_MBX_LIST_SFLAG_MARKED: + flags |= IMAPFolderFlagMarked; + break; + case MAILIMAP_MBX_LIST_SFLAG_NOSELECT: + flags |= IMAPFolderFlagNoSelect; + break; + case MAILIMAP_MBX_LIST_SFLAG_UNMARKED: + flags |= IMAPFolderFlagUnmarked; + break; + } + } + + for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ; + cur = clist_next(cur)) { + struct mailimap_mbx_list_oflag * oflag; + + oflag = (struct mailimap_mbx_list_oflag *) clist_content(cur); + + switch (oflag->of_type) { + case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS: + flags |= IMAPFolderFlagNoInferiors; + break; + + case MAILIMAP_MBX_LIST_OFLAG_FLAG_EXT: + for(unsigned int i = 0 ; i < sizeof(mb_keyword_flag) / sizeof(mb_keyword_flag[0]) ; i ++) { + if (strcasecmp(mb_keyword_flag[i].name, oflag->of_flag_ext) == 0) { + flags |= mb_keyword_flag[i].flag; + } + } + break; + } + } + + return flags; +} + +static Array * resultsWithError(int r, clist * list, ErrorCode * pError) +{ + clistiter * cur; + Array * result; + + result = Array::array(); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorNonExistantFolder; + return NULL; + } + + for(cur = clist_begin(list) ; cur != NULL ; cur = cur->next) { + struct mailimap_mailbox_list * mb_list; + IMAPFolderFlag flags; + IMAPFolder * folder; + String * path; + + mb_list = (struct mailimap_mailbox_list *) cur->data; + + flags = IMAPFolderFlagNone; + if (mb_list->mb_flag != NULL) + flags = (IMAPFolderFlag) imap_mailbox_flags_to_flags(mb_list->mb_flag); + + folder = new IMAPFolder(); + path = String::stringWithUTF8Characters(mb_list->mb_name); + if (path->uppercaseString()->isEqual(MCSTR("INBOX"))) { + folder->setPath(MCSTR("INBOX")); + } + else { + folder->setPath(path); + } + folder->setDelimiter(mb_list->mb_delimiter); + folder->setFlags(flags); + + result->addObject(folder); + + folder->release(); + } + + mailimap_list_result_free(list); + + * pError = ErrorNone; + return result; +} + +// Deprecated +char IMAPSession::fetchDelimiterIfNeeded(char defaultDelimiter, ErrorCode * pError) +{ + int r; + clist * imap_folders; + IMAPFolder * folder; + Array * folders; + + if (defaultDelimiter != 0) + return defaultDelimiter; + + r = mailimap_list(mImap, "", "", &imap_folders); + folders = resultsWithError(r, imap_folders, pError); + if (* pError == ErrorConnection) + mShouldDisconnect = true; + if (* pError != ErrorNone) + return 0; + + if (folders->count() > 0) { + folder = (IMAPFolder *) folders->objectAtIndex(0); + } + else { + folder = NULL; + } + if (folder == NULL) + return 0; + + * pError = ErrorNone; + return folder->delimiter(); +} + +Array * /* IMAPFolder */ IMAPSession::fetchSubscribedFolders(ErrorCode * pError) +{ + int r; + clist * imap_folders; + + MCLog("fetch subscribed"); + loginIfNeeded(pError); + if (* pError != ErrorNone) + return NULL; + + if (mDelimiter == 0) { + char delimiter; + + delimiter = fetchDelimiterIfNeeded(mDelimiter, pError); + if (* pError != ErrorNone) + return NULL; + + //setDelimiter(delimiter); + mDelimiter = delimiter; + } + + String * prefix; + prefix = defaultNamespace()->mainPrefix(); + if (prefix == NULL) { + prefix = MCSTR(""); + } + if (prefix->length() > 0) { + if (!prefix->hasSuffix(String::stringWithUTF8Format("%c", mDelimiter))) { + prefix = prefix->stringByAppendingUTF8Format("%c", mDelimiter); + } + } + + r = mailimap_lsub(mImap, MCUTF8(prefix), "*", &imap_folders); + MCLog("fetch subscribed %u", r); + Array * result = resultsWithError(r, imap_folders, pError); + if (* pError == ErrorConnection) + mShouldDisconnect = true; + return result; +} + +Array * /* IMAPFolder */ IMAPSession::fetchAllFolders(ErrorCode * pError) +{ + int r; + clist * imap_folders; + + loginIfNeeded(pError); + if (* pError != ErrorNone) + return NULL; + + if (mDelimiter == 0) { + char delimiter; + + delimiter = fetchDelimiterIfNeeded(mDelimiter, pError); + if (* pError != ErrorNone) + return NULL; + + //setDelimiter(delimiter); + mDelimiter = delimiter; + } + + String * prefix = NULL; + if (defaultNamespace()) { + prefix = defaultNamespace()->mainPrefix(); + } + if (prefix == NULL) { + prefix = MCSTR(""); + } + if (prefix->length() > 0) { + if (!prefix->hasSuffix(String::stringWithUTF8Format("%c", mDelimiter))) { + prefix = prefix->stringByAppendingUTF8Format("%c", mDelimiter); + } + } + + if (mXListEnabled) { + r = mailimap_xlist(mImap, MCUTF8(prefix), "*", &imap_folders); + } + else { + r = mailimap_list(mImap, MCUTF8(prefix), "*", &imap_folders); + } + Array * result = resultsWithError(r, imap_folders, pError); + if (* pError == ErrorConnection) + mShouldDisconnect = true; + + if (result != NULL) { + bool hasInbox = false; + mc_foreacharray(IMAPFolder, folder, result) { + if (folder->path()->isEqual(MCSTR("INBOX"))) { + hasInbox = true; + } + } + + if (!hasInbox) { + r = mailimap_list(mImap, "", "INBOX", &imap_folders); + Array * inboxResult = resultsWithError(r, imap_folders, pError); + if (* pError == ErrorConnection) + mShouldDisconnect = true; + result->addObjectsFromArray(inboxResult); + } + } + + return result; +} + +void IMAPSession::renameFolder(String * folder, String * otherName, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_rename(mImap, MCUTF8(folder), MCUTF8(otherName)); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorRename; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::deleteFolder(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_delete(mImap, MCUTF8(folder)); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorDelete; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::createFolder(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_create(mImap, MCUTF8(folder)); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorCreate; + return; + } + + * pError = ErrorNone; + subscribeFolder(folder, pError); +} + +void IMAPSession::subscribeFolder(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_subscribe(mImap, MCUTF8(folder)); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorSubscribe; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::unsubscribeFolder(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_unsubscribe(mImap, MCUTF8(folder)); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorSubscribe; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::appendMessage(String * folder, Data * messageData, MessageFlag flags, + IMAPProgressCallback * progressCallback, uint32_t * createdUID, ErrorCode * pError) +{ + this->appendMessageWithCustomFlags(folder, messageData, flags, NULL, progressCallback, createdUID, pError); +} + +void IMAPSession::appendMessageWithCustomFlags(String * folder, Data * messageData, MessageFlag flags, Array * customFlags, + IMAPProgressCallback * progressCallback, uint32_t * createdUID, ErrorCode * pError) +{ + this->appendMessageWithCustomFlagsAndDate(folder, messageData, flags, NULL, (time_t) -1, progressCallback, createdUID, pError); +} + +void IMAPSession::appendMessageWithCustomFlagsAndDate(String * folder, Data * messageData, MessageFlag flags, Array * customFlags, time_t date, + IMAPProgressCallback * progressCallback, uint32_t * createdUID, ErrorCode * pError) +{ + int r; + struct mailimap_flag_list * flag_list; + uint32_t uidvalidity; + uint32_t uidresult; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + mProgressCallback = progressCallback; + bodyProgress(0, messageData->length()); + + flag_list = NULL; + flag_list = flags_to_lep(flags); + if (customFlags != NULL) { + for (unsigned int i = 0 ; i < customFlags->count() ; i ++) { + struct mailimap_flag * f; + String * customFlag = (String *) customFlags->objectAtIndex(i); + + f = mailimap_flag_new_flag_keyword(strdup(customFlag->UTF8Characters())); + mailimap_flag_list_add(flag_list, f); + } + } + struct mailimap_date_time * imap_date = NULL; + if (date != (time_t) -1) { + imap_date = imapDateFromTimestamp(date); + } + r = mailimap_uidplus_append(mImap, MCUTF8(folder), flag_list, imap_date, messageData->bytes(), messageData->length(), + &uidvalidity, &uidresult); + if (imap_date != NULL) { + mailimap_date_time_free(imap_date); + } + mailimap_flag_list_free(flag_list); + + bodyProgress(messageData->length(), messageData->length()); + mProgressCallback = NULL; + + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorAppend; + return; + } + + * createdUID = uidresult; + * pError = ErrorNone; +} + +void IMAPSession::copyMessages(String * folder, IndexSet * uidSet, String * destFolder, + HashMap ** pUidMapping, ErrorCode * pError) +{ + int r; + struct mailimap_set * set; + struct mailimap_set * src_uid; + struct mailimap_set * dest_uid; + uint32_t uidvalidity; + clist * setList; + IndexSet * uidSetResult; + HashMap * uidMapping = NULL; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + set = setFromIndexSet(uidSet); + if (clist_count(set->set_list) == 0) { + mailimap_set_free(set); + return; + } + + setList = splitSet(set, 10); + uidSetResult = NULL; + + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + + r = mailimap_uidplus_uid_copy(mImap, current_set, MCUTF8(destFolder), + &uidvalidity, &src_uid, &dest_uid); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + goto release; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + goto release; + } + else if (hasError(r)) { + * pError = ErrorCopy; + goto release; + } + + if ((src_uid != NULL) && (dest_uid != NULL)) { + if (uidMapping == NULL) { + uidMapping = HashMap::hashMap(); + } + + Array * srcUidsArray = arrayFromSet(src_uid); + Array * destUidsArray = arrayFromSet(dest_uid); + + for(int i = 0 ; i < srcUidsArray->count() && i < destUidsArray->count() ; i ++) { + uidMapping->setObjectForKey(srcUidsArray->objectAtIndex(i), destUidsArray->objectAtIndex(i)); + } + } + + if (src_uid != NULL) { + mailimap_set_free(src_uid); + } + + if (dest_uid != NULL) { + mailimap_set_free(dest_uid); + } + } + if (pUidMapping != NULL) { + * pUidMapping = uidMapping; + } + * pError = ErrorNone; + + release: + + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + mailimap_set_free(current_set); + } + clist_free(setList); + mailimap_set_free(set); +} + +void IMAPSession::expunge(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + r = mailimap_expunge(mImap); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorExpunge; + return; + } + * pError = ErrorNone; +} + +static int +fetch_imap(mailimap * imap, bool identifier_is_uid, uint32_t identifier, + struct mailimap_fetch_type * fetch_type, + char ** result, size_t * result_len) +{ + int r; + struct mailimap_msg_att * msg_att; + struct mailimap_msg_att_item * msg_att_item; + clist * fetch_result; + struct mailimap_set * set; + char * text; + size_t text_length; + clistiter * cur; + + set = mailimap_set_new_single(identifier); + if (identifier_is_uid) { + r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result); + } + else { + r = mailimap_fetch(imap, set, fetch_type, &fetch_result); + } + + mailimap_set_free(set); + + switch (r) { + case MAILIMAP_NO_ERROR: + break; + default: + return r; + } + + if (clist_isempty(fetch_result)) { + mailimap_fetch_list_free(fetch_result); + return MAILIMAP_ERROR_FETCH; + } + + msg_att = (struct mailimap_msg_att *) clist_begin(fetch_result)->data; + + text = NULL; + text_length = 0; + + for(cur = clist_begin(msg_att->att_list) ; cur != NULL ; + cur = clist_next(cur)) { + msg_att_item = (struct mailimap_msg_att_item *) clist_content(cur); + + if (msg_att_item->att_type != MAILIMAP_MSG_ATT_ITEM_STATIC) { + continue; + } + + if (msg_att_item->att_data.att_static->att_type != + MAILIMAP_MSG_ATT_BODY_SECTION) { + continue; + } + + text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part; + msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL; + text_length = msg_att_item->att_data.att_static->att_data.att_body_section->sec_length; + } + + mailimap_fetch_list_free(fetch_result); + + if (text == NULL) + return MAILIMAP_ERROR_FETCH; + + * result = text; + * result_len = text_length; + + return MAILIMAP_NO_ERROR; +} + +HashMap * IMAPSession::fetchMessageNumberUIDMapping(String * folder, uint32_t fromUID, uint32_t toUID, + ErrorCode * pError) +{ + struct mailimap_set * imap_set; + struct mailimap_fetch_type * fetch_type; + clist * fetch_result; + HashMap * result; + struct mailimap_fetch_att * fetch_att; + int r; + clistiter * iter; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + result = HashMap::hashMap(); + + imap_set = mailimap_set_new_interval(fromUID, toUID); + fetch_type = mailimap_fetch_type_new_fetch_att_list_empty(); + fetch_att = mailimap_fetch_att_new_uid(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + + r = mailimap_uid_fetch(mImap, imap_set, fetch_type, &fetch_result); + mailimap_fetch_type_free(fetch_type); + mailimap_set_free(imap_set); + + if (r == MAILIMAP_ERROR_STREAM) { + MCLog("error stream"); + mShouldDisconnect = true; + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + MCLog("error parse"); + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + MCLog("error fetch"); + * pError = ErrorFetch; + return NULL; + } + + for(iter = clist_begin(fetch_result) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_msg_att * msg_att; + clistiter * item_iter; + uint32_t uid; + + msg_att = (struct mailimap_msg_att *) clist_content(iter); + uid = 0; + for(item_iter = clist_begin(msg_att->att_list) ; item_iter != NULL ; item_iter = clist_next(item_iter)) { + struct mailimap_msg_att_item * att_item; + + att_item = (struct mailimap_msg_att_item *) clist_content(item_iter); + if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) { + struct mailimap_msg_att_static * att_static; + + att_static = att_item->att_data.att_static; + if (att_static->att_type == MAILIMAP_MSG_ATT_UID) { + uid = att_static->att_data.att_uid; + } + } + } + + if (uid < fromUID) { + uid = 0; + } + + if (uid != 0) { + result->setObjectForKey(Value::valueWithUnsignedLongValue(msg_att->att_number), + Value::valueWithUnsignedLongValue(uid)); + } + } + + mailimap_fetch_list_free(fetch_result); + * pError = ErrorNone; + + return result; +} + +struct msg_att_handler_data { + IMAPSession * self; + bool fetchByUID; + Array * result; + String * folder; + IMAPMessagesRequestKind requestKind; + uint32_t mLastFetchedSequenceNumber; + HashMap * mapping; + bool needsHeader; + bool needsBody; + bool needsFlags; + bool needsGmailLabels; + bool needsGmailMessageID; + bool needsGmailThreadID; + uint32_t startUid; +}; + +static void msg_att_handler(struct mailimap_msg_att * msg_att, void * context) +{ + clistiter * item_iter; + uint32_t uid; + IMAPMessage * msg; + bool hasHeader; + bool hasBody; + bool hasFlags; + bool hasGmailLabels; + bool hasGmailMessageID; + bool hasGmailThreadID; + struct msg_att_handler_data * msg_att_context; + // struct + IMAPSession * self; + bool fetchByUID; + Array * result; + String * folder; + IMAPMessagesRequestKind requestKind; + uint32_t mLastFetchedSequenceNumber; + HashMap * mapping; + bool needsHeader; + bool needsBody; + bool needsFlags; + bool needsGmailLabels; + bool needsGmailMessageID; + bool needsGmailThreadID; + uint32_t startUid; + + msg_att_context = (struct msg_att_handler_data *) context; + self = msg_att_context->self; + fetchByUID = msg_att_context->fetchByUID; + result = msg_att_context->result; + folder = msg_att_context->folder; + requestKind = msg_att_context->requestKind; + mLastFetchedSequenceNumber = msg_att_context->mLastFetchedSequenceNumber; + mapping = msg_att_context->mapping; + needsHeader = msg_att_context->needsHeader; + needsBody = msg_att_context->needsBody; + needsFlags = msg_att_context->needsFlags; + needsGmailLabels = msg_att_context->needsGmailLabels; + needsGmailMessageID = msg_att_context->needsGmailMessageID; + needsGmailThreadID = msg_att_context->needsGmailThreadID; + startUid = msg_att_context->startUid; + + hasHeader = false; + hasBody = false; + hasFlags = false; + hasGmailLabels = false; + hasGmailMessageID = false; + hasGmailThreadID = false; + + msg = new IMAPMessage(); + + uid = 0; + mLastFetchedSequenceNumber = msg_att->att_number; + if (mapping != NULL) { + uid = (uint32_t) ((Value *) mapping->objectForKey(Value::valueWithUnsignedLongValue(msg_att->att_number)))->longLongValue(); + } + msg->setSequenceNumber(msg_att->att_number); + for(item_iter = clist_begin(msg_att->att_list) ; item_iter != NULL ; item_iter = clist_next(item_iter)) { + struct mailimap_msg_att_item * att_item; + + att_item = (struct mailimap_msg_att_item *) clist_content(item_iter); + if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_DYNAMIC) { + MessageFlag flags; + + flags = flags_from_lep_att_dynamic(att_item->att_data.att_dyn); + msg->setFlags(flags); + msg->setOriginalFlags(flags); + hasFlags = true; + + Array * customFlags; + customFlags = custom_flags_from_lep_att_dynamic(att_item->att_data.att_dyn); + msg->setCustomFlags(customFlags); + } + else if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) { + struct mailimap_msg_att_static * att_static; + + att_static = att_item->att_data.att_static; + if (att_static->att_type == MAILIMAP_MSG_ATT_UID) { + uid = att_static->att_data.att_uid; + } + else if (att_static->att_type == MAILIMAP_MSG_ATT_ENVELOPE) { + struct mailimap_envelope * env; + + MCLog("parse envelope %lu", (unsigned long) uid); + env = att_static->att_data.att_env; + msg->header()->importIMAPEnvelope(env); + hasHeader = true; + } + else if (att_static->att_type == MAILIMAP_MSG_ATT_BODY_SECTION) { + if ((requestKind & IMAPMessagesRequestKindFullHeaders) != 0 || + (requestKind & IMAPMessagesRequestKindExtraHeaders) != 0) { + char * bytes; + size_t length; + + bytes = att_static->att_data.att_body_section->sec_body_part; + length = att_static->att_data.att_body_section->sec_length; + + msg->header()->importHeadersData(Data::dataWithBytes(bytes, (unsigned int) length)); + hasHeader = true; + } + else { + char * references; + size_t ref_size; + + // references + references = att_static->att_data.att_body_section->sec_body_part; + ref_size = att_static->att_data.att_body_section->sec_length; + + msg->header()->importIMAPReferences(Data::dataWithBytes(references, (unsigned int) ref_size)); + } + } + else if (att_static->att_type == MAILIMAP_MSG_ATT_BODYSTRUCTURE) { + AbstractPart * mainPart; + + // bodystructure + mainPart = IMAPPart::attachmentWithIMAPBody(att_static->att_data.att_body); + msg->setMainPart(mainPart); + hasBody = true; + } + } + else if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_EXTENSION) { + struct mailimap_extension_data * ext_data; + + ext_data = att_item->att_data.att_extension_data; + if (ext_data->ext_extension == &mailimap_extension_condstore) { + struct mailimap_condstore_fetch_mod_resp * fetch_data; + + fetch_data = (struct mailimap_condstore_fetch_mod_resp *) ext_data->ext_data; + msg->setModSeqValue(fetch_data->cs_modseq_value); + } + else if (ext_data->ext_extension == &mailimap_extension_xgmlabels) { + struct mailimap_msg_att_xgmlabels * cLabels; + Array * labels; + clistiter * cur; + + labels = new Array(); + hasGmailLabels = true; + cLabels = (struct mailimap_msg_att_xgmlabels *) ext_data->ext_data; + for(cur = clist_begin(cLabels->att_labels) ; cur != NULL ; cur = clist_next(cur)) { + char * cLabel; + String * label; + + cLabel = (char *) clist_content(cur); + label = String::stringWithUTF8Characters(cLabel); + labels->addObject(label); + } + if (labels->count() > 0) { + msg->setGmailLabels(labels); + } + labels->release(); + } + else if (ext_data->ext_extension == &mailimap_extension_xgmthrid) { + uint64_t * threadID; + + threadID = (uint64_t *) ext_data->ext_data; + msg->setGmailThreadID(*threadID); + hasGmailThreadID = true; + } + else if (ext_data->ext_extension == &mailimap_extension_xgmmsgid) { + uint64_t * msgID; + + msgID = (uint64_t *) ext_data->ext_data; + msg->setGmailMessageID(*msgID); + hasGmailMessageID = true; + } + } + } + for(item_iter = clist_begin(msg_att->att_list) ; item_iter != NULL ; item_iter = clist_next(item_iter)) { + struct mailimap_msg_att_item * att_item; + + att_item = (struct mailimap_msg_att_item *) clist_content(item_iter); + if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) { + struct mailimap_msg_att_static * att_static; + + att_static = att_item->att_data.att_static; + if (att_static->att_type == MAILIMAP_MSG_ATT_INTERNALDATE) { + msg->header()->importIMAPInternalDate(att_static->att_data.att_internal_date); + } else if (att_static->att_type == MAILIMAP_MSG_ATT_RFC822_SIZE) { + msg->setSize(att_static->att_data.att_rfc822_size); + } + } + } + + if (fetchByUID) { + if (uid < startUid) { + uid = 0; + } + } + + if (needsBody && !hasBody) { + msg->release(); + return; + } + if (needsHeader && !hasHeader) { + msg->release(); + return; + } + if (needsFlags && !hasFlags) { + msg->release(); + return; + } + if (needsGmailThreadID && !hasGmailThreadID) { + msg->release(); + return; + } + if (needsGmailMessageID && !hasGmailMessageID) { + msg->release(); + return; + } + if (uid != 0) { + msg->setUid(uid); + } + else { + msg->release(); + return; + } + + result->addObject(msg); + msg->release(); + + msg_att_context->mLastFetchedSequenceNumber = mLastFetchedSequenceNumber; +} + +IMAPSyncResult * IMAPSession::fetchMessages(String * folder, IMAPMessagesRequestKind requestKind, bool fetchByUID, + struct mailimap_set * imapset, uint64_t modseq, HashMap * mapping, uint32_t startUid, + IMAPProgressCallback * progressCallback, Array * extraHeaders, ErrorCode * pError) +{ + struct mailimap_fetch_type * fetch_type; + clist * fetch_result; + struct mailimap_qresync_vanished * vanished; + struct mailimap_fetch_att * fetch_att; + int r; + bool needsHeader; + bool needsBody; + bool needsFlags; + bool needsGmailLabels; + bool needsGmailMessageID; + bool needsGmailThreadID; + Array * messages; + IndexSet * vanishedMessages; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + if (mNeedsMboxMailWorkaround && ((requestKind & IMAPMessagesRequestKindHeaders) != 0)) { + requestKind = (IMAPMessagesRequestKind) (requestKind & ~IMAPMessagesRequestKindHeaders); + requestKind = (IMAPMessagesRequestKind) (requestKind | IMAPMessagesRequestKindFullHeaders); + } + if (extraHeaders != NULL) { + requestKind = (IMAPMessagesRequestKind) (requestKind | IMAPMessagesRequestKindExtraHeaders); + } + + if ((requestKind & IMAPMessagesRequestKindHeaders) != 0) { + mProgressItemsCount = 0; + mProgressCallback = progressCallback; + } + + messages = Array::array(); + + needsHeader = false; + needsBody = false; + needsFlags = false; + needsGmailLabels = false; + needsGmailMessageID = false; + needsGmailThreadID = false; + clist * hdrlist = clist_new(); + + fetch_type = mailimap_fetch_type_new_fetch_att_list_empty(); + fetch_att = mailimap_fetch_att_new_uid(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + if ((requestKind & IMAPMessagesRequestKindFlags) != 0) { + MCLog("request flags"); + fetch_att = mailimap_fetch_att_new_flags(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsFlags = true; + } + if ((requestKind & IMAPMessagesRequestKindGmailLabels) != 0) { + MCLog("request flags"); + fetch_att = mailimap_fetch_att_new_xgmlabels(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsGmailLabels = true; + } + if ((requestKind & IMAPMessagesRequestKindGmailThreadID) != 0) { + fetch_att = mailimap_fetch_att_new_xgmthrid(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsGmailThreadID = true; + } + if ((requestKind & IMAPMessagesRequestKindGmailMessageID) != 0) { + fetch_att = mailimap_fetch_att_new_xgmmsgid(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsGmailMessageID = true; + } + if ((requestKind & IMAPMessagesRequestKindFullHeaders) != 0) { + char * header; + + MCLog("request envelope"); + + // most important header + header = strdup("Date"); + clist_append(hdrlist, header); + header = strdup("Subject"); + clist_append(hdrlist, header); + header = strdup("From"); + clist_append(hdrlist, header); + header = strdup("Sender"); + clist_append(hdrlist, header); + header = strdup("Reply-To"); + clist_append(hdrlist, header); + header = strdup("To"); + clist_append(hdrlist, header); + header = strdup("Cc"); + clist_append(hdrlist, header); + header = strdup("Message-ID"); + clist_append(hdrlist, header); + header = strdup("References"); + clist_append(hdrlist, header); + header = strdup("In-Reply-To"); + clist_append(hdrlist, header); + } + if ((requestKind & IMAPMessagesRequestKindHeaders) != 0) { + char * header; + + MCLog("request envelope"); + // envelope + fetch_att = mailimap_fetch_att_new_envelope(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + + // references header + header = strdup("References"); + clist_append(hdrlist, header); + if ((requestKind & IMAPMessagesRequestKindHeaderSubject) != 0) { + header = strdup("Subject"); + clist_append(hdrlist, header); + } + } + if ((requestKind & IMAPMessagesRequestKindSize) != 0) { + // message structure + MCLog("request size"); + fetch_att = mailimap_fetch_att_new_rfc822_size(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + } + + if ((requestKind & IMAPMessagesRequestKindStructure) != 0) { + // message structure + MCLog("request bodystructure"); + fetch_att = mailimap_fetch_att_new_bodystructure(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsBody = true; + } + if ((requestKind & IMAPMessagesRequestKindInternalDate) != 0) { + // internal date + fetch_att = mailimap_fetch_att_new_internaldate(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + } + if ((requestKind & IMAPMessagesRequestKindExtraHeaders) != 0) { + // custom header request + char * header; + + if (extraHeaders && extraHeaders->count() > 0) { + for (unsigned int i = 0; i < extraHeaders->count(); i++) { + String * headerString = (String *)extraHeaders->objectAtIndex(i); + header = strdup(headerString->UTF8Characters()); + clist_append(hdrlist, header); + } + } + } + + if (clist_begin(hdrlist) != NULL) { + struct mailimap_header_list * imap_hdrlist; + struct mailimap_section * section; + + imap_hdrlist = mailimap_header_list_new(hdrlist); + section = mailimap_section_new_header_fields(imap_hdrlist); + fetch_att = mailimap_fetch_att_new_body_peek_section(section); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsHeader = true; + } + else { + clist_free(hdrlist); + } + + struct msg_att_handler_data msg_att_data; + + memset(&msg_att_data, 0, sizeof(msg_att_data)); + msg_att_data.self = this; + msg_att_data.fetchByUID = fetchByUID; + msg_att_data.result = messages; + msg_att_data.folder = folder; + msg_att_data.requestKind = requestKind; + msg_att_data.mLastFetchedSequenceNumber = mLastFetchedSequenceNumber; + msg_att_data.mapping = mapping; + msg_att_data.needsHeader = needsHeader; + msg_att_data.needsBody = needsBody; + msg_att_data.needsFlags = needsFlags; + msg_att_data.needsGmailLabels = needsGmailLabels; + msg_att_data.startUid = startUid; + msg_att_data.needsGmailMessageID = needsGmailMessageID; + msg_att_data.needsGmailThreadID = needsGmailThreadID; + mailimap_set_msg_att_handler(mImap, msg_att_handler, &msg_att_data); + + mBodyProgressEnabled = false; + vanished = NULL; + + if (fetchByUID) { + if ((modseq != 0) && (mCondstoreEnabled || mQResyncEnabled)) { + if (mQResyncEnabled) { + r = mailimap_uid_fetch_qresync(mImap, imapset, fetch_type, modseq, + &fetch_result, &vanished); + } + else { /* condstore */ + r = mailimap_uid_fetch_changedsince(mImap, imapset, fetch_type, modseq, + &fetch_result); + } + } + else { + r = mailimap_uid_fetch(mImap, imapset, fetch_type, &fetch_result); + } + } else { + if ((modseq != 0) && (mCondstoreEnabled || mQResyncEnabled)) { + if (mQResyncEnabled) { + r = mailimap_fetch_qresync(mImap, imapset, fetch_type, modseq, + &fetch_result, &vanished); + } + else { /* condstore */ + r = mailimap_fetch_changedsince(mImap, imapset, fetch_type, modseq, + &fetch_result); + } + } + else { + r = mailimap_fetch(mImap, imapset, fetch_type, &fetch_result); + } + } + + vanishedMessages = NULL; + if (vanished != NULL) { + vanishedMessages = indexSetFromSet(vanished->qr_known_uids); + } + + mBodyProgressEnabled = true; + + mProgressCallback = NULL; + + mLastFetchedSequenceNumber = msg_att_data.mLastFetchedSequenceNumber; + + mailimap_fetch_type_free(fetch_type); + + mailimap_set_msg_att_handler(mImap, NULL, NULL); + + if (r == MAILIMAP_ERROR_STREAM) { + MCLog("error stream"); + mShouldDisconnect = true; + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + MCLog("error parse"); + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + MCLog("error fetch"); + * pError = ErrorFetch; + return NULL; + } + + IMAPSyncResult * result; + result = new IMAPSyncResult(); + result->setModifiedOrAddedMessages(messages); + result->setVanishedMessages(vanishedMessages); + result->autorelease(); + + if ((requestKind & IMAPMessagesRequestKindHeaders) != 0) { + if (messages->count() == 0) { + unsigned int count; + + count = clist_count(fetch_result); + if (count > 0) { + requestKind = (IMAPMessagesRequestKind) (requestKind & ~IMAPMessagesRequestKindHeaders); + requestKind = (IMAPMessagesRequestKind) (requestKind | IMAPMessagesRequestKindFullHeaders); + + result = fetchMessages(folder, requestKind, fetchByUID, + imapset, modseq, NULL, startUid, progressCallback, extraHeaders, pError); + if (result != NULL) { + if (result->modifiedOrAddedMessages() != NULL) { + if (result->modifiedOrAddedMessages()->count() > 0) { + mNeedsMboxMailWorkaround = true; + } + } + } + } + } + } + + mailimap_fetch_list_free(fetch_result); + * pError = ErrorNone; + + return result; +} + +Array * IMAPSession::fetchMessagesByUID(String * folder, IMAPMessagesRequestKind requestKind, + IndexSet * uids, IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + return fetchMessagesByUIDWithExtraHeaders(folder, requestKind, uids, progressCallback, NULL, pError); +} + + +Array * IMAPSession::fetchMessagesByUIDWithExtraHeaders(String * folder, IMAPMessagesRequestKind requestKind, + IndexSet * uids, IMAPProgressCallback * progressCallback, + Array * extraHeaders, ErrorCode * pError) +{ + struct mailimap_set * imapset = setFromIndexSet(uids); + IMAPSyncResult * syncResult = fetchMessages(folder, requestKind, true, imapset, 0, NULL, 0, + progressCallback, extraHeaders, pError); + if (syncResult == NULL) { + mailimap_set_free(imapset); + return NULL; + } + Array * result = syncResult->modifiedOrAddedMessages(); + result->retain()->autorelease(); + mailimap_set_free(imapset); + return result; +} + +Array * IMAPSession::fetchMessagesByNumber(String * folder, IMAPMessagesRequestKind requestKind, + IndexSet * numbers, IMAPProgressCallback * progressCallback, + ErrorCode * pError) +{ + return fetchMessagesByNumberWithExtraHeaders(folder, requestKind, numbers, progressCallback, NULL, pError); +} + +Array * IMAPSession::fetchMessagesByNumberWithExtraHeaders(String * folder, IMAPMessagesRequestKind requestKind, + IndexSet * numbers, IMAPProgressCallback * progressCallback, + Array * extraHeaders, ErrorCode * pError) +{ + struct mailimap_set * imapset = setFromIndexSet(numbers); + IMAPSyncResult * syncResult = fetchMessages(folder, requestKind, false, imapset, 0, NULL, 0, + progressCallback, extraHeaders, pError); + if (syncResult == NULL) { + mailimap_set_free(imapset); + return NULL; + } + Array * result = syncResult->modifiedOrAddedMessages(); + result->retain()->autorelease(); + mailimap_set_free(imapset); + return result; +} + +static int fetch_rfc822(mailimap * session, bool identifier_is_uid, + uint32_t identifier, char ** result, size_t * result_len) +{ + struct mailimap_section * section; + struct mailimap_fetch_att * fetch_att; + struct mailimap_fetch_type * fetch_type; + + section = mailimap_section_new(NULL); + fetch_att = mailimap_fetch_att_new_body_peek_section(section); + fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att); + int r = fetch_imap(session, identifier_is_uid, identifier, + fetch_type, result, result_len); + mailimap_fetch_type_free(fetch_type); + + return r; + +#if 0 + int r; + clist * fetch_list; + struct mailimap_section * section; + struct mailimap_fetch_att * fetch_att; + struct mailimap_fetch_type * fetch_type; + struct mailimap_set * set; + struct mailimap_msg_att * msg_att; + struct mailimap_msg_att_item * item; + int res; + clistiter * cur; + + section = mailimap_section_new(NULL); + fetch_att = mailimap_fetch_att_new_body_peek_section(section); + fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att); + + set = mailimap_set_new_single(identifier); + + if (identifier_is_uid) { + r = mailimap_uid_fetch(session, set, fetch_type, &fetch_list); + } + else { + r = mailimap_fetch(session, set, fetch_type, &fetch_list); + } + + mailimap_set_free(set); + mailimap_fetch_type_free(fetch_type); + + if (r != MAILIMAP_NO_ERROR) { + res = r; + goto err; + } + + if (clist_isempty(fetch_list)) { + res = MAILIMAP_ERROR_FETCH; + goto free; + } + + msg_att = (struct mailimap_msg_att *) clist_begin(fetch_list)->data; + + for(cur = clist_begin(msg_att->att_list) ; cur != NULL ; cur = clist_next(cur)) { + item = (struct mailimap_msg_att_item *) clist_content(cur); + + if (item->att_type != MAILIMAP_MSG_ATT_ITEM_STATIC) { + continue; + } + if (item->att_data.att_static->att_type != MAILIMAP_MSG_ATT_BODY_SECTION) { + continue; + } + + * result = item->att_data.att_static->att_data.att_body_section->sec_body_part; + item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL; + mailimap_fetch_list_free(fetch_list); + + return MAILIMAP_NO_ERROR; + } + + res = MAILIMAP_ERROR_FETCH; + +free: + mailimap_fetch_list_free(fetch_list); +err: + return res; +#endif +} + +Data * IMAPSession::fetchMessageByUID(String * folder, uint32_t uid, + IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + return fetchMessage(folder, true, uid, progressCallback, pError); +} + +Data * IMAPSession::fetchMessageByNumber(String * folder, uint32_t number, + IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + return fetchMessage(folder, false, number, progressCallback, pError); +} + +Data * IMAPSession::fetchMessage(String * folder, bool identifier_is_uid, uint32_t identifier, + IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + char * rfc822; + size_t rfc822_len; + int r; + Data * data; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + mProgressItemsCount = 0; + mProgressCallback = progressCallback; + + rfc822 = NULL; + r = fetch_rfc822(mImap, identifier_is_uid, identifier, &rfc822, &rfc822_len); + if (r == MAILIMAP_NO_ERROR) { + size_t len; + + len = 0; + if (rfc822 != NULL) { + len = strlen(rfc822); + } + bodyProgress((unsigned int) len, (unsigned int) len); + } + mProgressCallback = NULL; + + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorFetch; + return NULL; + } + + if (rfc822 == NULL) { + data = Data::data(); + } + else { + data = Data::dataWithBytes(rfc822, (unsigned int) rfc822_len); + } + + mailimap_nstring_free(rfc822); + * pError = ErrorNone; + + return data; +} + +Data * IMAPSession::fetchMessageAttachment(String * folder, bool identifier_is_uid, + uint32_t identifier, String * partID, + Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + struct mailimap_fetch_type * fetch_type; + struct mailimap_fetch_att * fetch_att; + struct mailimap_section * section; + struct mailimap_section_part * section_part; + clist * sec_list; + Array * partIDArray; + int r; + char * text; + size_t text_length; + Data * data; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + mProgressItemsCount = 0; + mProgressCallback = progressCallback; + bodyProgress(0, 0); + + partIDArray = partID->componentsSeparatedByString(MCSTR(".")); + sec_list = clist_new(); + for(unsigned int i = 0 ; i < partIDArray->count() ; i ++) { + uint32_t * value; + String * element; + + element = (String *) partIDArray->objectAtIndex(i); + value = (uint32_t *) malloc(sizeof(* value)); + * value = element->intValue(); + clist_append(sec_list, value); + } + section_part = mailimap_section_part_new(sec_list); + section = mailimap_section_new_part(section_part); + fetch_att = mailimap_fetch_att_new_body_peek_section(section); + fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att); + + r = fetch_imap(mImap, identifier_is_uid, identifier, fetch_type, &text, &text_length); + mailimap_fetch_type_free(fetch_type); + + mProgressCallback = NULL; + + MCLog("had error : %i", r); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorFetch; + return NULL; + } + + data = Data::dataWithBytes(text, (unsigned int) text_length); + data = data->decodedDataUsingEncoding(encoding); + + mailimap_nstring_free(text); + * pError = ErrorNone; + + return data; +} + +Data * IMAPSession::fetchMessageAttachmentByUID(String * folder, uint32_t uid, String * partID, + Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + return fetchMessageAttachment(folder, true, uid, partID, encoding, progressCallback, pError); +} + +Data * IMAPSession::fetchMessageAttachmentByNumber(String * folder, uint32_t number, String * partID, + Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + return fetchMessageAttachment(folder, false, number, partID, encoding, progressCallback, pError); +} + +IndexSet * IMAPSession::search(String * folder, IMAPSearchKind kind, String * searchString, ErrorCode * pError) +{ + IMAPSearchExpression * expr; + + expr = NULL; + switch (kind) { + case IMAPSearchKindAll: + { + expr = IMAPSearchExpression::searchAll(); + break; + } + case IMAPSearchKindFrom: + { + expr = IMAPSearchExpression::searchFrom(searchString); + break; + } + case IMAPSearchKindTo: + { + expr = IMAPSearchExpression::searchTo(searchString); + break; + } + case IMAPSearchKindCc: + { + expr = IMAPSearchExpression::searchCc(searchString); + break; + } + case IMAPSearchKindBcc: + { + expr = IMAPSearchExpression::searchBcc(searchString); + break; + } + case IMAPSearchKindRecipient: + { + expr = IMAPSearchExpression::searchRecipient(searchString); + break; + } + case IMAPSearchKindSubject: + { + expr = IMAPSearchExpression::searchSubject(searchString); + break; + } + case IMAPSearchKindContent: + { + expr = IMAPSearchExpression::searchContent(searchString); + break; + } + default: + { + MCAssert(0); + break; + } + } + return search(folder, expr, pError); +} + +static struct mailimap_search_key * searchKeyFromSearchExpression(IMAPSearchExpression * expression) +{ + switch (expression->kind()) { + case IMAPSearchKindAll: + { + return mailimap_search_key_new_all(); + } + case IMAPSearchKindFrom: + { + return mailimap_search_key_new_from(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindTo: + { + return mailimap_search_key_new_to(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindCc: + { + return mailimap_search_key_new_cc(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindBcc: + { + return mailimap_search_key_new_bcc(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindRecipient: + { + struct mailimap_search_key * to_search; + struct mailimap_search_key * cc_search; + struct mailimap_search_key * bcc_search; + struct mailimap_search_key * or_search1; + struct mailimap_search_key * or_search2; + + to_search = mailimap_search_key_new_to(strdup(expression->value()->UTF8Characters())); + cc_search = mailimap_search_key_new_cc(strdup(expression->value()->UTF8Characters())); + bcc_search = mailimap_search_key_new_bcc(strdup(expression->value()->UTF8Characters())); + + or_search1 = mailimap_search_key_new_or(to_search, cc_search); + or_search2 = mailimap_search_key_new_or(or_search1, bcc_search); + + return or_search2; + } + case IMAPSearchKindSubject: + { + return mailimap_search_key_new_subject(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindContent: + { + return mailimap_search_key_new_text(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindBody: + { + return mailimap_search_key_new_body(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindUIDs: + { + return mailimap_search_key_new_uid(setFromIndexSet(expression->uids())); + } + case IMAPSearchKindHeader: + { + return mailimap_search_key_new_header(strdup(expression->header()->UTF8Characters()), strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindBeforeDate: + { + time_t date = expression->date(); + struct tm timeinfo; + localtime_r(&date, &timeinfo); + return mailimap_search_key_new_sentbefore(mailimap_date_new(timeinfo.tm_mday, timeinfo.tm_mon+1, timeinfo.tm_year+1900)); + } + case IMAPSearchKindOnDate: + { + time_t date = expression->date(); + struct tm timeinfo; + localtime_r(&date, &timeinfo); + return mailimap_search_key_new_senton(mailimap_date_new(timeinfo.tm_mday, timeinfo.tm_mon+1, timeinfo.tm_year+1900)); + } + case IMAPSearchKindSinceDate: + { + time_t date = expression->date(); + struct tm timeinfo; + localtime_r(&date, &timeinfo); + return mailimap_search_key_new_sentsince(mailimap_date_new(timeinfo.tm_mday, timeinfo.tm_mon+1, timeinfo.tm_year+1900)); + } + case IMAPSearchKindBeforeReceivedDate: + { + time_t date = expression->date(); + struct tm timeinfo; + localtime_r(&date, &timeinfo); + return mailimap_search_key_new_before(mailimap_date_new(timeinfo.tm_mday, timeinfo.tm_mon+1, timeinfo.tm_year+1900)); + } + case IMAPSearchKindOnReceivedDate: + { + time_t date = expression->date(); + struct tm timeinfo; + localtime_r(&date, &timeinfo); + return mailimap_search_key_new_on(mailimap_date_new(timeinfo.tm_mday, timeinfo.tm_mon+1, timeinfo.tm_year+1900)); + } + case IMAPSearchKindSinceReceivedDate: + { + time_t date = expression->date(); + struct tm timeinfo; + localtime_r(&date, &timeinfo); + return mailimap_search_key_new_since(mailimap_date_new(timeinfo.tm_mday, timeinfo.tm_mon+1, timeinfo.tm_year+1900)); + } + case IMAPSearchKindGmailThreadID: + { + return mailimap_search_key_new_xgmthrid(expression->longNumber()); + } + case IMAPSearchKindGmailMessageID: + { + return mailimap_search_key_new_xgmmsgid(expression->longNumber()); + } + case IMAPSearchKindRead: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_SEEN, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindUnread: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_UNSEEN, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindFlagged: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_FLAGGED, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindUnflagged: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_UNFLAGGED, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindAnswered: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_ANSWERED, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindUnanswered: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_UNANSWERED, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindDraft: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_DRAFT, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindUndraft: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_UNDRAFT, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindDeleted: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_DELETED, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindSpam: + { + return mailimap_search_key_new(MAILIMAP_SEARCH_KEY_KEYWORD, + NULL, NULL, NULL, NULL, NULL, + strdup("Junk"), NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, 0, + NULL, NULL, NULL, NULL, NULL, + NULL, 0, NULL, NULL, NULL); + } + case IMAPSearchKindSizeLarger: + { + return mailimap_search_key_new_larger( (uint32_t) expression->longNumber()); + } + case IMAPSearchKindSizeSmaller: + { + return mailimap_search_key_new_smaller( (uint32_t) expression->longNumber()); + } + case IMAPSearchKindGmailRaw: + { + return mailimap_search_key_new_xgmraw(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindOr: + { + return mailimap_search_key_new_or(searchKeyFromSearchExpression(expression->leftExpression()), searchKeyFromSearchExpression(expression->rightExpression())); + } + case IMAPSearchKindAnd: + { + clist * list; + list = clist_new(); + clist_append(list, searchKeyFromSearchExpression(expression->leftExpression())); + clist_append(list, searchKeyFromSearchExpression(expression->rightExpression())); + return mailimap_search_key_new_multiple(list); + } + case IMAPSearchKindNot: + { + return mailimap_search_key_new_not(searchKeyFromSearchExpression(expression->leftExpression())); + } + + default: + MCAssert(0); + return NULL; + } +} + +IndexSet * IMAPSession::search(String * folder, IMAPSearchExpression * expression, ErrorCode * pError) +{ + struct mailimap_search_key * key; + + key = searchKeyFromSearchExpression(expression); + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + clist * result_list = NULL; + + const char * charset = "utf-8"; + if (mYahooServer) { + charset = NULL; + } + + int r; + if (mIsGmail) { + r = mailimap_uid_search_literalplus(mImap, charset, key, &result_list); + } + else { + r = mailimap_uid_search(mImap, charset, key, &result_list); + } + mailimap_search_key_free(key); + MCLog("had error : %i", r); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorFetch; + return NULL; + } + + IndexSet * result = IndexSet::indexSet(); + for(clistiter * cur = clist_begin(result_list) ; cur != NULL ; cur = clist_next(cur)) { + uint32_t * uid = (uint32_t *) clist_content(cur); + result->addIndex(* uid); + } + mailimap_search_result_free(result_list); + * pError = ErrorNone; + return result; +} + +void IMAPSession::getQuota(uint32_t *usage, uint32_t *limit, ErrorCode * pError) +{ + mailimap_quota_complete_data *quota_data; + + int r = mailimap_quota_getquotaroot(mImap, "INBOX", "a_data); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorFetch; + return; + } + for(clistiter * cur = clist_begin(quota_data->quota_list); cur != NULL; cur = clist_next(cur)) { + mailimap_quota_quota_data *quota = (mailimap_quota_quota_data*)clist_content(cur); + for (clistiter *cur2 = clist_begin(quota->quota_list); cur2 != NULL; cur2 = clist_next(cur2)) { + mailimap_quota_quota_resource *res = (mailimap_quota_quota_resource*)clist_content(cur2); + if (!strcasecmp("STORAGE", res->resource_name)) { + *usage = res->usage; + *limit = res->limit; + } + } + } + mailimap_quota_complete_data_free(quota_data); +} + +bool IMAPSession::setupIdle() +{ + // main thread + LOCK(); + bool canIdle = mCanIdle; + if (mCanIdle) { + mailstream_setup_idle(mImap->imap_stream); + } + UNLOCK(); + return canIdle; +} + +void IMAPSession::idle(String * folder, uint32_t lastKnownUID, ErrorCode * pError) +{ + int r; + + // connection thread + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + if (lastKnownUID != 0) { + Array * msgs; + + msgs = fetchMessagesByUID(folder, IMAPMessagesRequestKindUid, IndexSet::indexSetWithRange(RangeMake(lastKnownUID, UINT64_MAX)), + NULL, pError); + if (* pError != ErrorNone) + return; + if (msgs->count() > 0) { + IMAPMessage * msg; + + msg = (IMAPMessage *) msgs->objectAtIndex(0); + if (msg->uid() > lastKnownUID) { + MCLog("found msg UID %u %u", (unsigned int) msg->uid(), (unsigned int) lastKnownUID); + return; + } + } + } + + r = mailimap_idle(mImap); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorIdle; + return; + } + + if (!mImap->imap_selection_info->sel_has_exists && !mImap->imap_selection_info->sel_has_recent) { + int r; + r = mailstream_wait_idle(mImap->imap_stream, MAX_IDLE_DELAY); + switch (r) { + case MAILSTREAM_IDLE_ERROR: + case MAILSTREAM_IDLE_CANCELLED: + { + mShouldDisconnect = true; + * pError = ErrorConnection; + MCLog("error or cancelled"); + return; + } + case MAILSTREAM_IDLE_INTERRUPTED: + MCLog("interrupted by user"); + break; + case MAILSTREAM_IDLE_HASDATA: + MCLog("something on the socket"); + break; + case MAILSTREAM_IDLE_TIMEOUT: + MCLog("idle timeout"); + break; + } + } + else { + MCLog("found info before idling"); + } + + r = mailimap_idle_done(mImap); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorIdle; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::interruptIdle() +{ + // main thread + LOCK(); + if (mCanIdle) { + mailstream_interrupt_idle(mImap->imap_stream); + } + UNLOCK(); +} + +void IMAPSession::unsetupIdle() +{ + // main thread + LOCK(); + if (mCanIdle) { + mailstream_unsetup_idle(mImap->imap_stream); + } + UNLOCK(); +} + +void IMAPSession::disconnect() +{ + unsetup(); +} + +IMAPIdentity * IMAPSession::identity(IMAPIdentity * clientIdentity, ErrorCode * pError) +{ + connectIfNeeded(pError); + if (* pError != ErrorNone) + return NULL; + + struct mailimap_id_params_list * client_identification; + + client_identification = mailimap_id_params_list_new_empty(); + + mc_foreacharray(String, key, clientIdentity->allInfoKeys()) { + char * dup_name; + char * dup_value; + + dup_name = strdup(key->UTF8Characters()); + dup_value = strdup(clientIdentity->infoForKey(key)->UTF8Characters()); + mailimap_id_params_list_add_name_value(client_identification, dup_name, dup_value); + } + + int r; + struct mailimap_id_params_list * server_identification; + r = mailimap_id(mImap, client_identification, &server_identification); + mailimap_id_params_list_free(client_identification); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorIdentity; + return NULL; + } + + IMAPIdentity * result = new IMAPIdentity(); + + clistiter * cur; + for(cur = clist_begin(server_identification->idpa_list) ; cur != NULL ; cur = clist_next(cur)) { + struct mailimap_id_param * param; + + param = (struct mailimap_id_param *) clist_content(cur); + + String * responseKey; + String * responseValue; + responseKey = String::stringWithUTF8Characters(param->idpa_name); + responseValue = String::stringWithUTF8Characters(param->idpa_value); + result->setInfoForKey(responseKey, responseValue); + } + + mailimap_id_params_list_free(server_identification); + * pError = ErrorNone; + + result->autorelease(); + return result; +} + +void IMAPSession::bodyProgress(unsigned int current, unsigned int maximum) +{ + if (!mBodyProgressEnabled) + return; + + if (mProgressCallback != NULL) { + mProgressCallback->bodyProgress(this, current, maximum); + } +} + +void IMAPSession::itemsProgress(unsigned int current, unsigned int maximum) +{ + mProgressItemsCount ++; + if (mProgressCallback != NULL) { + mProgressCallback->itemsProgress(this, mProgressItemsCount, maximum); + } +} + +IMAPNamespace * IMAPSession::defaultNamespace() +{ + return mDefaultNamespace; +} + +void IMAPSession::setDefaultNamespace(IMAPNamespace * ns) +{ + MC_SAFE_REPLACE_RETAIN(IMAPNamespace, mDefaultNamespace, ns); +} + +IMAPIdentity * IMAPSession::serverIdentity() +{ + return mServerIdentity; +} + +IMAPIdentity * IMAPSession::clientIdentity() +{ + return mClientIdentity; +} + +HashMap * IMAPSession::fetchNamespace(ErrorCode * pError) +{ + HashMap * result; + struct mailimap_namespace_data * namespace_data; + int r; + + loginIfNeeded(pError); + if (* pError != ErrorNone) + return NULL; + + result = HashMap::hashMap(); + r = mailimap_namespace(mImap, &namespace_data); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorNamespace; + return NULL; + } + + IMAPNamespace * ns; + + if (namespace_data->ns_personal != NULL) { + ns = new IMAPNamespace(); + ns->importIMAPNamespace(namespace_data->ns_personal); + result->setObjectForKey(IMAPNamespacePersonal, ns); + ns->release(); + } + + if (namespace_data->ns_other != NULL) { + ns = new IMAPNamespace(); + ns->importIMAPNamespace(namespace_data->ns_other); + result->setObjectForKey(IMAPNamespaceOther, ns); + ns->release(); + } + + if (namespace_data->ns_shared != NULL) { + ns = new IMAPNamespace(); + ns->importIMAPNamespace(namespace_data->ns_shared); + result->setObjectForKey(IMAPNamespaceShared, ns); + ns->release(); + } + + mailimap_namespace_data_free(namespace_data); + * pError = ErrorNone; + + return result; +} + +void IMAPSession::storeFlagsByUID(String * folder, IndexSet * uids, IMAPStoreFlagsRequestKind kind, MessageFlag flags, ErrorCode * pError) +{ + this->storeFlagsAndCustomFlagsByUID(folder, uids, kind, flags, NULL, pError); +} + +void IMAPSession::storeFlagsAndCustomFlagsByUID(String * folder, IndexSet * uids, IMAPStoreFlagsRequestKind kind, MessageFlag flags, Array * customFlags, ErrorCode * pError) +{ + storeFlagsAndCustomFlags(folder, true, uids, kind, flags, customFlags, pError); +} + +void IMAPSession::storeFlagsAndCustomFlags(String * folder, bool identifier_is_uid, IndexSet * identifiers, + IMAPStoreFlagsRequestKind kind, MessageFlag flags, Array * customFlags, ErrorCode * pError) +{ + struct mailimap_set * imap_set; + struct mailimap_store_att_flags * store_att_flags; + struct mailimap_flag_list * flag_list; + int r; + clist * setList; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + imap_set = setFromIndexSet(identifiers); + if (clist_count(imap_set->set_list) == 0) { + mailimap_set_free(imap_set); + return; + } + + setList = splitSet(imap_set, 50); + + flag_list = mailimap_flag_list_new_empty(); + if ((flags & MessageFlagSeen) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_seen(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagAnswered) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_answered(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagFlagged) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flagged(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagDeleted) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_deleted(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagDraft) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_draft(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagMDNSent) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flag_keyword(strdup("$MDNSent")); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagForwarded) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flag_keyword(strdup("$Forwarded")); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagSubmitPending) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flag_keyword(strdup("$SubmitPending")); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagSubmitted) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flag_keyword(strdup("$Submitted")); + mailimap_flag_list_add(flag_list, f); + } + + if (customFlags != NULL) { + for (unsigned int i = 0 ; i < customFlags->count() ; i ++) { + struct mailimap_flag * f; + String * customFlag = (String *) customFlags->objectAtIndex(i); + + f = mailimap_flag_new_flag_keyword(strdup(customFlag->UTF8Characters())); + mailimap_flag_list_add(flag_list, f); + } + } + + store_att_flags = NULL; + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + + switch (kind) { + case IMAPStoreFlagsRequestKindRemove: + store_att_flags = mailimap_store_att_flags_new_remove_flags_silent(flag_list); + break; + case IMAPStoreFlagsRequestKindAdd: + store_att_flags = mailimap_store_att_flags_new_add_flags_silent(flag_list); + break; + case IMAPStoreFlagsRequestKindSet: + store_att_flags = mailimap_store_att_flags_new_set_flags_silent(flag_list); + break; + } + if (identifier_is_uid) { + r = mailimap_uid_store(mImap, current_set, store_att_flags); + } + else { + r = mailimap_store(mImap, current_set, store_att_flags); + } + + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + goto release; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorStore; + return; + } + } + + release: + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + mailimap_set_free(current_set); + } + clist_free(setList); + mailimap_store_att_flags_free(store_att_flags); + mailimap_set_free(imap_set); + * pError = ErrorNone; +} + +void IMAPSession::storeFlagsByNumber(String * folder, IndexSet * numbers, IMAPStoreFlagsRequestKind kind, MessageFlag flags, ErrorCode * pError) +{ + this->storeFlagsAndCustomFlagsByNumber(folder, numbers, kind, flags, NULL, pError); +} + +void IMAPSession::storeFlagsAndCustomFlagsByNumber(String * folder, IndexSet * numbers, IMAPStoreFlagsRequestKind kind, MessageFlag flags, Array * customFlags, ErrorCode * pError) +{ + storeFlagsAndCustomFlags(folder, false, numbers, kind, flags, customFlags, pError); +} + +void IMAPSession::storeLabelsByUID(String * folder, IndexSet * uids, IMAPStoreFlagsRequestKind kind, Array * labels, ErrorCode * pError) +{ + storeLabels(folder, true, uids, kind, labels, pError); +} + +void IMAPSession::storeLabelsByNumber(String * folder, IndexSet * numbers, IMAPStoreFlagsRequestKind kind, Array * labels, ErrorCode * pError) +{ + storeLabels(folder, false, numbers, kind, labels, pError); +} + +void IMAPSession::storeLabels(String * folder, bool identifier_is_uid, IndexSet * identifiers, IMAPStoreFlagsRequestKind kind, Array * labels, ErrorCode * pError) +{ + struct mailimap_set * imap_set; + struct mailimap_msg_att_xgmlabels * xgmlabels; + int r; + clist * setList; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + imap_set = setFromIndexSet(identifiers); + if (clist_count(imap_set->set_list) == 0) { + mailimap_set_free(imap_set); + return; + } + + setList = splitSet(imap_set, 10); + + xgmlabels = mailimap_msg_att_xgmlabels_new_empty(); + for(unsigned int i = 0 ; i < labels->count() ; i ++) { + String * label = (String *) labels->objectAtIndex(i); + mailimap_msg_att_xgmlabels_add(xgmlabels, strdup(label->UTF8Characters())); + } + + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + int fl_sign; + + current_set = (struct mailimap_set *) clist_content(iter); + + switch (kind) { + case IMAPStoreFlagsRequestKindRemove: + fl_sign = -1; + break; + case IMAPStoreFlagsRequestKindAdd: + fl_sign = 1; + break; + case IMAPStoreFlagsRequestKindSet: + fl_sign = 0; + break; + } + if (identifier_is_uid) { + r = mailimap_uid_store_xgmlabels(mImap, current_set, fl_sign, 1, xgmlabels); + } + else { + r = mailimap_store_xgmlabels(mImap, current_set, fl_sign, 1, xgmlabels); + } + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + goto release; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorStore; + return; + } + } + + release: + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + mailimap_set_free(current_set); + } + clist_free(setList); + mailimap_msg_att_xgmlabels_free(xgmlabels); + mailimap_set_free(imap_set); + * pError = ErrorNone; +} + +uint32_t IMAPSession::uidValidity() +{ + return mUIDValidity; +} + +uint32_t IMAPSession::uidNext() +{ + return mUIDNext; +} + +uint64_t IMAPSession::modSequenceValue() +{ + return mModSequenceValue; +} + +unsigned int IMAPSession::lastFolderMessageCount() +{ + return mFolderMsgCount; +} + +uint32_t IMAPSession::firstUnseenUid() +{ + return mFirstUnseenUid; +} + +IMAPSyncResult * IMAPSession::syncMessagesByUID(String * folder, IMAPMessagesRequestKind requestKind, + IndexSet * uids, uint64_t modseq, + IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + return syncMessagesByUIDWithExtraHeaders(folder, requestKind, uids, modseq, progressCallback, NULL, pError); +} + +IMAPSyncResult * IMAPSession::syncMessagesByUIDWithExtraHeaders(String * folder, IMAPMessagesRequestKind requestKind, + IndexSet * uids, uint64_t modseq, + IMAPProgressCallback * progressCallback, Array * extraHeaders, + ErrorCode * pError) +{ + MCAssert(uids->rangesCount() > 0); + struct mailimap_set * imapset = setFromIndexSet(uids); + IMAPSyncResult * result = fetchMessages(folder, requestKind, true, imapset, modseq, NULL, + (uint32_t) uids->allRanges()[0].location, + progressCallback, extraHeaders, pError); + mailimap_set_free(imapset); + return result; + +} + +IndexSet * IMAPSession::capability(ErrorCode * pError) +{ + int r; + struct mailimap_capability_data * cap; + + connectIfNeeded(pError); + if (* pError != ErrorNone) + return NULL; + + r = mailimap_capability(mImap, &cap); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorCapability; + return NULL; + } + + mailimap_capability_data_free(cap); + + IndexSet * result = new IndexSet(); + capabilitySetWithSessionState(result); + + * pError = ErrorNone; + result->autorelease(); + return result; +} + +void IMAPSession::capabilitySetWithSessionState(IndexSet * capabilities) +{ + if (mailimap_has_extension(mImap, (char *)"STARTTLS")) { + capabilities->addIndex(IMAPCapabilityStartTLS); + } + if (mailimap_has_authentication(mImap, (char *)"PLAIN")) { + capabilities->addIndex(IMAPCapabilityAuthPlain); + } + if (mailimap_has_authentication(mImap, (char *)"LOGIN")) { + capabilities->addIndex(IMAPCapabilityAuthLogin); + } + if (mailimap_has_idle(mImap)) { + LOCK(); + mCanIdle = true; + UNLOCK(); + } + if (mailimap_has_id(mImap)) { + capabilities->addIndex(IMAPCapabilityId); + } + if (mailimap_has_xlist(mImap)) { + capabilities->addIndex(IMAPCapabilityXList); + } + if (mailimap_has_extension(mImap, (char *) "X-GM-EXT-1")) { + // Disable use of XLIST if this is the Gmail IMAP server because it implements + // RFC 6154. + capabilities->addIndex(IMAPCapabilityGmail); + } + if (mailimap_has_idle(mImap)) { + capabilities->addIndex(IMAPCapabilityIdle); + } + if (mailimap_has_condstore(mImap)) { + capabilities->addIndex(IMAPCapabilityCondstore); + } + if (mailimap_has_qresync(mImap)) { + capabilities->addIndex(IMAPCapabilityQResync); + } + if (mailimap_has_xoauth2(mImap)) { + capabilities->addIndex(IMAPCapabilityXOAuth2); + } + if (mailimap_has_namespace(mImap)) { + capabilities->addIndex(IMAPCapabilityNamespace); + } + if (mailimap_has_compress_deflate(mImap)) { + capabilities->addIndex(IMAPCapabilityCompressDeflate); + } + if (mailimap_has_extension(mImap, (char *)"CHILDREN")) { + capabilities->addIndex(IMAPCapabilityChildren); + } + + applyCapabilities(capabilities); +} + +void IMAPSession::applyCapabilities(IndexSet * capabilities) +{ + if (capabilities->containsIndex(IMAPCapabilityId)) { + mIdentityEnabled = true; + } + if (capabilities->containsIndex(IMAPCapabilityXList)) { + mXListEnabled = true; + } + if (capabilities->containsIndex(IMAPCapabilityGmail)) { + mXListEnabled = false; + mIsGmail = true; + } + if (capabilities->containsIndex(IMAPCapabilityIdle)) { + mIdleEnabled = true; + } + if (capabilities->containsIndex(IMAPCapabilityCondstore)) { + mCondstoreEnabled = true; + } + if (capabilities->containsIndex(IMAPCapabilityQResync)) { + mQResyncEnabled = true; + } + if (capabilities->containsIndex(IMAPCapabilityXOAuth2)) { + mXOauth2Enabled = true; + } + if (capabilities->containsIndex(IMAPCapabilityNamespace)) { + mNamespaceEnabled = true; + } + if (capabilities->containsIndex(IMAPCapabilityCompressDeflate)) { + mCompressionEnabled = true; + } +} + +bool IMAPSession::isIdleEnabled() +{ + return mIdleEnabled; +} + +bool IMAPSession::isXListEnabled() +{ + return mXListEnabled; +} + +bool IMAPSession::isCondstoreEnabled() +{ + return mCondstoreEnabled; +} + +bool IMAPSession::isQResyncEnabled() +{ + return mQResyncEnabled; +} + +bool IMAPSession::isIdentityEnabled() +{ + return mIdentityEnabled; +} + +bool IMAPSession::isXOAuthEnabled() +{ + return mXOauth2Enabled; +} + +bool IMAPSession::isNamespaceEnabled() +{ + return mNamespaceEnabled; +} + +bool IMAPSession::isCompressionEnabled() +{ + return mCompressionEnabled; +} + +bool IMAPSession::allowsNewPermanentFlags() { + return mAllowsNewPermanentFlags; +} + +bool IMAPSession::isDisconnected() +{ + return mState == STATE_DISCONNECTED; +} + +void IMAPSession::setConnectionLogger(ConnectionLogger * logger) +{ + mConnectionLogger = logger; +} + +ConnectionLogger * IMAPSession::connectionLogger() +{ + return mConnectionLogger; +} + +String * IMAPSession::htmlRendering(IMAPMessage * message, String * folder, ErrorCode * pError) +{ + HTMLRendererIMAPDataCallback * dataCallback = new HTMLRendererIMAPDataCallback(this, message->uid()); + String * htmlString = HTMLRenderer::htmlForIMAPMessage(folder, + message, + dataCallback, + NULL); + * pError = dataCallback->error(); + + if (* pError != ErrorNone) { + return NULL; + } + + MC_SAFE_RELEASE(dataCallback); + return htmlString; +} + +String * IMAPSession::htmlBodyRendering(IMAPMessage * message, String * folder, ErrorCode * pError) +{ + HTMLRendererIMAPDataCallback * dataCallback = new HTMLRendererIMAPDataCallback(this, message->uid()); + HTMLBodyRendererTemplateCallback * htmlCallback = new HTMLBodyRendererTemplateCallback(); + + String * htmlBodyString = HTMLRenderer::htmlForIMAPMessage(folder, + message, + dataCallback, + htmlCallback); + + * pError = dataCallback->error(); + + if (* pError != ErrorNone) { + return NULL; + } + + MC_SAFE_RELEASE(dataCallback); + MC_SAFE_RELEASE(htmlCallback); + return htmlBodyString; +} + +String * IMAPSession::plainTextRendering(IMAPMessage * message, String * folder, ErrorCode * pError) +{ + String * htmlString = htmlRendering(message, folder, pError); + + if (* pError != ErrorNone) { + return NULL; + } + + String * plainTextString = htmlString->flattenHTML(); + return plainTextString; +} + +String * IMAPSession::plainTextBodyRendering(IMAPMessage * message, String * folder, bool stripWhitespace, ErrorCode * pError) +{ + String * htmlBodyString = htmlBodyRendering(message, folder, pError); + + if (* pError != ErrorNone) { + return NULL; + } + + String * plainTextBodyString = htmlBodyString->flattenHTML(); + if (stripWhitespace) { + return plainTextBodyString->stripWhitespace(); + } + + return plainTextBodyString; +} + +void IMAPSession::setAutomaticConfigurationEnabled(bool enabled) +{ + mAutomaticConfigurationEnabled = enabled; +} + +bool IMAPSession::isAutomaticConfigurationEnabled() +{ + return mAutomaticConfigurationEnabled; +} + +bool IMAPSession::enableFeature(String * feature) +{ + struct mailimap_capability_data * caps; + clist * cap_list; + struct mailimap_capability * cap; + int r; + + cap_list = clist_new(); + cap = mailimap_capability_new(MAILIMAP_CAPABILITY_NAME, NULL, strdup(MCUTF8(feature))); + clist_append(cap_list, cap); + caps = mailimap_capability_data_new(cap_list); + + struct mailimap_capability_data * result; + r = mailimap_enable(mImap, caps, &result); + if (r != MAILIMAP_NO_ERROR) + return false; + + mailimap_capability_data_free(caps); + mailimap_capability_data_free(result); + + return true; +} + +void IMAPSession::enableFeatures() +{ + if (isCompressionEnabled()) { + ErrorCode error; + enableCompression(&error); + if (error != ErrorNone) { + MCLog("could not enable compression"); + } + } + + if (isQResyncEnabled()) { + enableFeature(MCSTR("QRESYNC")); + } + else if (isCondstoreEnabled()) { + enableFeature(MCSTR("CONDSTORE")); + } +} + +void IMAPSession::enableCompression(ErrorCode * pError) +{ + int r; + r = mailimap_compress(mImap); + if (r == MAILIMAP_ERROR_STREAM) { + mShouldDisconnect = true; + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorCompression; + return; + } + + * pError = ErrorNone; +} + +bool IMAPSession::isAutomaticConfigurationDone() +{ + return mAutomaticConfigurationDone; +} + +void IMAPSession::resetAutomaticConfigurationDone() +{ + mAutomaticConfigurationDone = false; +} + +String * IMAPSession::gmailUserDisplayName() +{ + return mGmailUserDisplayName; +} |