diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 |
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 |
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /libmail/smap.C | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2 | |
Initial checkin
Imported from subversion report, converted to git. Updated all paths in
scripts and makefiles, reflecting the new directory hierarchy.
Diffstat (limited to 'libmail/smap.C')
| -rw-r--r-- | libmail/smap.C | 789 |
1 files changed, 789 insertions, 0 deletions
diff --git a/libmail/smap.C b/libmail/smap.C new file mode 100644 index 0000000..e614598 --- /dev/null +++ b/libmail/smap.C @@ -0,0 +1,789 @@ +/* +** Copyright 2003-2008, Double Precision Inc. +** +** See COPYING for distribution information. +*/ +#include "smap.H" +#include "misc.H" +#include "imapfolder.H" +#include "smapnoopexpunge.H" +#include "smapnewmail.H" +#include <limits.h> +#include <iostream> +#include <sstream> +#include <errno.h> +#include "rfc822/rfc822.h" +#include <cstring> + +using namespace std; + +mail::smapHandler::smapHandler(int timeoutValArg) + : mail::imapHandler(timeoutValArg), defaultCB(NULL) +{ +} + +mail::smapHandler::~smapHandler() +{ + fail("Operation aborted."); // Just in case +} + +int mail::smapHandler::process(imap &imapAccount, std::string &buffer) +{ + return ( (this->*(imapAccount.smapProtocolHandlerFunc))(imapAccount, + buffer)); +} + +// +// Expecting a single line reply. +// + +int mail::smapHandler::singleLineProcess(imap &imapAccount, + std::string &buffer) +{ + size_t p=buffer.find('\n'); + + if (p == std::string::npos) + return (0); // Wait until the whole line is read + + setTimeout(); + + ++p; + string buffer_cpy=buffer; + buffer_cpy.erase(p); + + // Parse line into individual words + + vector<const char *> words; + + string::iterator b=buffer_cpy.begin(), e=buffer_cpy.end(); + + while (b != e) + { + if (unicode_isspace((unsigned char)*b)) + { + b++; + continue; + } + + if (words.size() == 1) + { + if (strcmp(words[0], "+OK") == 0) + { + string s=string(b, e); + + while (s.size() > 0 && + unicode_isspace((unsigned char) + s.end()[-1])) + s.erase(s.end()-1, s.end()); + + doDestroy=true; + if (ok(fromutf8(s))) + { + if (doDestroy) + imapAccount. + uninstallHandler(this); + return p; + } + return 0; + } + + if (strcmp(words[0], "-ERR") == 0) + { + string s=string(b, e); + + while (s.size() > 0 && + unicode_isspace((unsigned char) + s.end()[-1])) + s.erase(s.end()-1, s.end()); + + doDestroy=true; + + if (fail(fromutf8(s))) + { + if (doDestroy) + imapAccount. + uninstallHandler(this); + return p; + } + return 0; + } + } + + words.push_back(&*b); + + if (*b != '"') // Not quoted - look for next space + { + while (b != e && !unicode_isspace((unsigned char)*b)) + b++; + + if (b != e) + *b++=0; + continue; + } + + // Extract quoted word. + + string::iterator c=b; + + b++; + + while (b != e) + { + if (*b == '"') + { + b++; + if (b == e || *b != '"') + break; + } + *c++ = *b++; + } + *c=0; + } + if (!processLine(imapAccount, words)) + return 0; + + return p; +} + +// +// Reading a dot-stuffed multiline response +// +int mail::smapHandler::multiLineProcessDotStuffed(imap &imapAccount, + std::string &buffer) +{ + size_t p=buffer.find('\n'); + + if (p == std::string::npos) + return (0); // Also wait until an entire line is read + + setTimeout(); + + string buffer_cpy=buffer; + buffer_cpy.erase(p); + ++p; + + // Swallow trailing CR + + if (buffer_cpy.size() > 0 && buffer_cpy.end()[-1] == '\r') + buffer_cpy.erase(buffer_cpy.end()-1, buffer_cpy.end()); + + if (buffer_cpy.size() > 0 && buffer_cpy[0] == '.') + { + buffer_cpy.erase(buffer_cpy.begin(), + buffer_cpy.begin()+1); + if (buffer_cpy.size() == 0) // Lone dot + { + imapAccount.smapProtocolHandlerFunc= + &smapHandler::singleLineProcess; + endData(imapAccount); + return p; + } + } + + processData(imapAccount, buffer_cpy + "\n"); + return p; +} + +// Reading a multi-line binary response + +int mail::smapHandler::multiLineProcessBinary(imap &imapAccount, + std::string &buffer) +{ + if (imapAccount.smapBinaryCount == 0) // Next chunk + { + size_t p=buffer.find('\n'); + + if (p == std::string::npos) + return (0); // Read one line + + setTimeout(); + + ++p; + string buffer_cpy=buffer; + buffer_cpy.erase(p); + + istringstream i(buffer_cpy); + unsigned long nextChunk=0; + + i >> nextChunk; + + if (i.fail() || nextChunk == 0) // Empty line. Done. + { + imapAccount.smapProtocolHandlerFunc= + &smapHandler::singleLineProcess; + endData(imapAccount); + return p; + } + + imapAccount.smapBinaryCount=nextChunk; + return p; + } + + // Ok, some # of bytes to go + + if (imapAccount.smapBinaryCount >= buffer.size()) + { + imapAccount.smapBinaryCount -= buffer.size(); + setTimeout(); + processData(imapAccount, buffer); + return buffer.size(); // Processed everything + } + + string buffer_cpy=buffer; + + buffer_cpy.erase(imapAccount.smapBinaryCount); + imapAccount.smapBinaryCount=0; + setTimeout(); + processData(imapAccount, buffer_cpy); + return buffer_cpy.size(); +} + +void mail::smapHandler::commaSplit(string s, vector<string> &a) +{ + while (s.size() > 0) + { + size_t n=s.find(','); + + if (n == std::string::npos) + { + a.push_back(s); + break; + } + + if (n > 0) + a.push_back(s.substr(0, n)); + s=s.substr(n+1); + } +} + +// +// Default single line response handler +// + +bool mail::smapHandler::processLine(imap &imapAccount, + std::vector<const char *> &words) +{ + if (words.size() == 0) + return true; + + const char *p=words[0]; + + if (*p == '{') // Start of a multiline response? + { + if (*++p == '.') // Dot-stuffed + { + string n(++p); + istringstream i(n); + + unsigned long estimate=0; + + i >> estimate; + + words.erase(words.begin(), words.begin()+1); + + imapAccount.smapProtocolHandlerFunc= + &smapHandler::multiLineProcessDotStuffed; + beginProcessData(imapAccount, words, estimate); + return true; + } + + // Binary + + string n(p); + istringstream i(n); + unsigned long firstChunk=0; + unsigned long estimate=0; + char dummy=0; + + i >> firstChunk >> dummy >> estimate; + + imapAccount.smapBinaryCount=firstChunk; + imapAccount.smapProtocolHandlerFunc= + &smapHandler::multiLineProcessBinary; + + words.erase(words.begin(), words.begin()+1); + beginProcessData(imapAccount, words, estimate); + return true; + } + + if (words.size() >= 3 && strcmp(words[0], "*") == 0 && + strcasecmp(words[1], "FETCH") == 0) + { + string n(words[2]); + istringstream i(n); + size_t msgNum=0; + + i >> msgNum; + + if (!i.fail() && msgNum > 0 && + imapAccount.currentFolder && + msgNum <= imapAccount.currentFolder->index.size()) + { + --msgNum; + + vector<const char *>::iterator b=words.begin()+3, + e=words.end(); + + for ( ; b != e; b++) + { + if (strncasecmp( *b, "FLAGS=", 6) == 0) + { + vector<string> flagList; + + commaSplit( (*b)+6, flagList); + + vector<string>::iterator fb, fe; + + fb=flagList.begin(); + fe=flagList.end(); + + mail::messageInfo newMessageInfo; + + newMessageInfo.uid= + imapAccount.currentFolder + ->index[msgNum].uid; + newMessageInfo.unread=true; + + while (fb != fe) + { + const char *c= (*fb).c_str(); + +#define FLAG true +#define NOTFLAG false +#define DOFLAG(value, field, name) \ + if (strcasecmp(c, name) == 0) \ + newMessageInfo.field=value; + + LIBMAIL_SMAPFLAGS; + + fb++; + } + (mail::messageInfo &) + imapAccount.currentFolder + ->index[msgNum]=newMessageInfo; + + if (msgNum < imapAccount.currentFolder + ->exists) + { + messageChanged(msgNum); + } +#if 0 + cerr << "FLAGS[" << msgNum + << "]: deleted=" + << newMessageInfo.deleted + << ", replied=" + << newMessageInfo.replied + << ", unread=" + << newMessageInfo.unread + << ", draft=" + << newMessageInfo.draft + << ", marked=" + << newMessageInfo.marked + << endl; +#endif + fetchedIndexInfo(); + } + + if (strncasecmp( *b, "KEYWORDS=", 9) == 0) + { + vector<string> flagList; + + commaSplit( (*b)+9, flagList); + + vector<string>::iterator fb, fe; + + fb=flagList.begin(); + fe=flagList.end(); + + mail::keywords::Message newMessage; + + while (fb != fe) + { + if (!newMessage + .addFlag(imapAccount. + keywordHashtable, + *fb)) + LIBMAIL_THROW(strerror(errno)); + ++fb; + } + + imapAccount.currentFolder + ->index[msgNum].keywords= + newMessage; + + if (msgNum < imapAccount.currentFolder + ->exists) + { + messageChanged(msgNum); + } + } + + if (strncasecmp( *b, "UID=", 4) == 0) + { + imapAccount.currentFolder + ->index[msgNum].uid= (*b)+4; +#if 0 + cerr << "UID[" << msgNum << "]=" + << imapAccount.currentFolder + ->index[msgNum].uid << endl; +#endif + fetchedIndexInfo(); + } + + if (strncasecmp( *b, "SIZE=", 5) == 0) + { + unsigned long bytes=0; + + string s= *b + 5; + istringstream i(s); + + i >> bytes; + + fetchedMessageSize(msgNum, bytes); + } + + if (strncasecmp( *b, "INTERNALDATE=", 13) == 0) + { + time_t n= rfc822_parsedt(*b + 13); + + if (n) + fetchedInternalDate(msgNum, n); + } + } + } + return true; + } + + if (words.size() >= 3 && strcmp(words[0], "*") == 0 && + strcasecmp(words[1], "EXISTS") == 0) + { + string n(words[2]); + istringstream i(n); + size_t msgNum=0; + + i >> msgNum; + + // Check against hostile servers + + if (msgNum > UINT_MAX / sizeof(mail::messageInfo)) + msgNum=UINT_MAX / sizeof(mail::messageInfo); + + if (!i.fail() && msgNum > 0 && + imapAccount.currentFolder && + !imapAccount.currentFolder->closeInProgress && + msgNum > imapAccount .currentFolder->index.size()) + { + existsOrExpungeSeen(); + imapAccount.currentFolder->existsMore(imapAccount, + msgNum); + } + return true; + } + + if (words.size() >= 2 && strcmp(words[0], "*") == 0 && + strcasecmp(words[1], "EXPUNGE") == 0) + { + vector< pair<size_t, size_t> > removedList; + + vector<const char *>::iterator b=words.begin()+2, + e=words.end(); + + while (b != e) + { + string n(*b++); + istringstream i(n); + + size_t first, last; + char dummy; + + size_t p=n.find('-'); + + if (p != std::string::npos) + { + i >> first >> dummy >> last; + } + else + { + i >> first; + dummy='-'; + last=first; + } + + if (i.fail() || dummy != '-' || last < first || + first <= 0 || + (last >= (imapAccount.currentFolder ? (size_t) + (imapAccount.currentFolder->index.size() + + 1):0)) + || (removedList.size() > 0 && + first <= removedList.end()[-1].second)) + continue; // Ignore bogosity + + removedList.push_back(make_pair(first, last)); + } + + if (removedList.size() == 0) + return true; + + vector< pair<size_t, size_t> >::iterator + rb=removedList.begin(), re=removedList.end(); + + vector<imapFOLDERinfo::indexInfo> &index= + imapAccount.currentFolder->index; + + while (rb != re) + { + --re; + + --re->first; + index.erase(index.begin() + re->first, + index.begin() + re->second); + + if (imapAccount.currentFolder->exists >= re->first) + { + if (imapAccount.currentFolder->exists + < re->second) + imapAccount.currentFolder->exists + =re->first; + else + imapAccount.currentFolder->exists + -= re->second - re->first; + } + --re->second; + } + + if (!imapAccount.currentFolder->closeInProgress) + existsOrExpungeSeen(); + + messagesRemoved(removedList); + return true; + } + + if (words.size() >= 3 && strcmp(words[0], "*") == 0 && + strcasecmp(words[1], "SNAPSHOT") == 0) + { + if (imapAccount.currentFolder) + imapAccount.currentFolder-> + folderCallback.saveSnapshot(words[2]); + + return true; + } + + return false; +} + +// Default notification handler passes along the expunged/changed list to the +// application. + +void mail::smapHandler::messagesRemoved(vector< pair<size_t, size_t> > + &removedList) +{ + myimap->currentFolder->folderCallback.messagesRemoved(removedList); +} + +void mail::smapHandler::messageChanged(size_t msgNum) +{ + myimap->currentFolder->folderCallback.messageChanged(msgNum); +} + +void mail::smapHandler::existsOrExpungeSeen() +{ +} + +void mail::smapHandler::fetchedMessageSize(size_t msgNum, + unsigned long bytes) +{ +} + +void mail::smapHandler::fetchedInternalDate(size_t msgNum, + time_t internalDate) +{ +} + +void mail::smapHandler::fetchedIndexInfo() +{ +} + +void mail::smapHandler::beginProcessData(imap &imapAccount, + std::vector<const char *> &w, + unsigned long estimatedSize) +{ +} + +void mail::smapHandler::processData(imap &imapAccount, + std::string data) +{ +} + +void mail::smapHandler::endData(imap &imapAccount) +{ +} + +// Default ok/fail handlers invoke the callback function + +bool mail::smapHandler::ok(std::string s) +{ + mail::callback *p=defaultCB; + + defaultCB=NULL; + + if (p) + p->success(s); + return true; +} + +bool mail::smapHandler::fail(std::string s) +{ + mail::callback *p=defaultCB; + + defaultCB=NULL; + + if (p) + p->fail(s); + return true; +} + +void mail::smapHandler::timedOut(const char *errmsg) +{ + mail::callback *p=defaultCB; + + defaultCB=NULL; + + if (p) + callbackTimedOut(*p, errmsg); +} + +// Convert folder names to a path string + +string mail::smapHandler::words2path(vector<const char *> &w) +{ + string path=""; + + vector<const char *>::iterator b=w.begin(), e=w.end(); + + while (b != e) + { + if (path.size() > 0) + path += "/"; + + path += mail::iconvert::convert(*b, "utf-8", + unicode_x_imap_modutf7 " /"); + + b++; + } + + return path; +} + +void mail::smapHandler::path2words(string path, vector<string> &words) +{ + string::iterator b=path.begin(), e=path.end(); + + while (b != e) + { + string::iterator c=b; + + while (c != e && *c != '/') + c++; + + string component=string(b, c); + + b=c; + if (b != e) + b++; + + words.push_back(mail::iconvert::convert(component, + unicode_x_imap_modutf7, + "utf-8")); + } + + if (words.size() == 0) + words.push_back(""); +} + +/////////////////////////////////////////////////////////////////////////// +// +// Currently open folder + +mail::smapFOLDER::smapFOLDER(std::string pathArg, + mail::callback::folder &folderCallbackArg, + mail::imap &myserver) + : imapFOLDERinfo(pathArg, folderCallbackArg), + imapHandler(myserver.noopSetting), openedFlag(false) +{ + mailCheckInterval=myserver.noopSetting; +} + +mail::smapFOLDER::~smapFOLDER() +{ +} + +void mail::smapFOLDER::existsMore(mail::imap &imapAccount, size_t n) +{ + size_t o=index.size(); + + imapFOLDERinfo::indexInfo newInfo; + newInfo.unread=true; + + index.resize(n); + + while (o < n) + index[o++]=newInfo; + + myimap->installForegroundTask(new smapNEWMAIL(NULL, false)); +} + +void mail::smapFOLDER::opened() +{ + openedFlag=true; +} + +void mail::smapFOLDER::resetMailCheckTimer() +{ + setTimeout(mailCheckInterval); +} + +int mail::smapFOLDER::getTimeout(mail::imap &imapAccount) +{ + int t= imapHandler::getTimeout(imapAccount); + + if (t == 0) + { + t=MAILCHECKINTERVAL; + setTimeout(t); + + imapHandler *h=NULL; + + if (!closeInProgress && + ((h=imapAccount.hasForegroundTask()) == NULL || + strcmp(h->getName(), "IDLE") == 0)) + { + imapAccount + .installForegroundTask(new + smapNoopExpunge("NOOP", + imapAccount) + ); + } + } + + return t; +} + +void mail::smapFOLDER::installed(imap &imapAccount) +{ +} + +int mail::smapFOLDER::process(imap &imapAccount, std::string &buffer) +{ + return 0; +} + +const char mail::smapFOLDER::name[]="smapFOLDER"; + +const char *mail::smapFOLDER::getName() +{ + return name; +} + +void mail::smapFOLDER::timedOut(const char *errmsg) +{ +} |
