summaryrefslogtreecommitdiffstats
path: root/libmail/imap.C
diff options
context:
space:
mode:
Diffstat (limited to 'libmail/imap.C')
-rw-r--r--libmail/imap.C1709
1 files changed, 0 insertions, 1709 deletions
diff --git a/libmail/imap.C b/libmail/imap.C
deleted file mode 100644
index 584a5ef..0000000
--- a/libmail/imap.C
+++ /dev/null
@@ -1,1709 +0,0 @@
-/*
-** Copyright 2002-2008, Double Precision Inc.
-**
-** See COPYING for distribution information.
-*/
-#include "imap.H"
-#include "driver.H"
-#include "smap.H"
-#include "smapfetch.H"
-#include "smapfetchattr.H"
-#include "imapfolder.H"
-#include "imaplogin.H"
-#include "logininfo.H"
-#include "imapfetchhandler.H"
-#include "base64.H"
-#include "structure.H"
-#include "misc.H"
-#include "genericdecode.H"
-
-#include <unicode/courier-unicode.h>
-#include <sstream>
-#include <iomanip>
-#include <algorithm>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <stdio.h>
-
-//#define DEBUG 1
-
-using namespace std;
-
-/////////////////////////////////////////////////////////////////////////////
-
-LIBMAIL_START
-
-static bool open_imap(mail::account *&accountRet,
- mail::account::openInfo &oi,
- mail::callback &callback,
- mail::callback::disconnect &disconnectCallback)
-{
- mail::loginInfo imapLoginInfo;
-
- if (!mail::loginUrlDecode(oi.url, imapLoginInfo))
- return false;
-
- if (imapLoginInfo.method != "imap" &&
- imapLoginInfo.method != "imaps")
- return false;
-
- accountRet=new mail::imap(oi.url, oi.pwd,
- oi.certificates,
- oi.loginCallbackObj,
- callback,
- disconnectCallback);
- return true;
-}
-
-
-static bool imap_remote(string url, bool &flag)
-{
- mail::loginInfo nntpLoginInfo;
-
- if (!mail::loginUrlDecode(url, nntpLoginInfo))
- return false;
-
- if (nntpLoginInfo.method != "imap" &&
- nntpLoginInfo.method != "imaps")
- return false;
-
- flag=true;
- return true;
-}
-
-driver imap_driver= { &open_imap, &imap_remote };
-
-LIBMAIL_END
-
-/////////////////////////////////////////////////////////////////////////////
-
-mail::imapHandler::imapHandler( int timeoutValArg)
- : myimap(NULL), installedFlag(false), isBackgroundTask(false),
- handlerTimeoutVal(timeoutValArg)
-{
- time(&timeoutAt);
-}
-
-// Default message handler timeout function invokes the application
-// callback.
-
-void mail::imapHandler::callbackTimedOut(mail::callback &callback,
- const char *errmsg)
-{
- callback.fail(errmsg ? errmsg:"Server timed out.");
-}
-
-mail::imapHandler::~imapHandler()
-{
- if (installedFlag) // If I'm in the active handler list, take me out
- myimap->remove(this);
-}
-
-void mail::imapHandler::anotherHandlerInstalled(mail::imap &imapAccount)
-{
-}
-
-bool mail::imapHandler::getTimeout(mail::imap &imapAccount, int &ioTimeout)
-{
- ioTimeout=getTimeout(imapAccount) * 1000;
-
- return (ioTimeout == 0);
-}
-
-int mail::imapHandler::getTimeout(mail::imap &imapAccount)
-{
- time_t t;
-
- time(&t);
-
- if (handlerTimeoutVal == 0)
- {
- handlerTimeoutVal=imapAccount.timeoutSetting;
- timeoutAt=t + handlerTimeoutVal;
- }
-
- return timeoutAt > t ? timeoutAt - t:0;
-}
-
-void mail::imapHandler::setTimeout(int t)
-{
- handlerTimeoutVal=t;
- setTimeout();
-}
-
-void mail::imapHandler::setTimeout()
-{
- if (handlerTimeoutVal == 0 && myimap)
- handlerTimeoutVal=myimap->timeoutSetting;
-
- time(&timeoutAt);
- timeoutAt += handlerTimeoutVal;
-}
-
-/////////////////////////////////////////////////////////////////////////
-//
-// mail::imap constructor
-//
-
-
-mail::imap::imap(string url,
- string passwd,
- std::vector<std::string> &certificates,
- mail::loginCallback *callback_func,
- mail::callback &callback,
- mail::callback::disconnect &disconnectCallback)
- : mail::fd(disconnectCallback, certificates),
- orderlyShutdown(false), sameServerFolderPtr(NULL),
- smap(false),
- smapProtocolHandlerFunc(&smapHandler::singleLineProcess),
- smapBinaryCount(0)
-{
- cmdcounter=0;
- currentFolder=NULL;
- currentFetch=NULL;
- wantIdleMode=false;
-
- mail::loginInfo loginInfo;
-
- loginInfo.loginCallbackFunc=callback_func;
- loginInfo.callbackPtr= &callback;
-
- if (!loginUrlDecode(url, loginInfo))
- {
- loginInfo.callbackPtr->fail("Invalid IMAP URL.");
- return;
- }
-
-
- timeoutSetting=getTimeoutSetting(loginInfo, "timeout", 60,
- 30, 600);
- noopSetting=getTimeoutSetting(loginInfo, "noop", 600,
- 5, 1800);
-
- noIdle=loginInfo.options.count("noidle") != 0;
- loginInfo.use_ssl= loginInfo.method == "imaps";
-
- if (passwd.size() > 0)
- loginInfo.pwd=passwd;
-
-
- string errmsg=socketConnect(loginInfo, "imap", "imaps");
-
- if (errmsg.size() > 0)
- {
- loginInfo.callbackPtr->fail(errmsg);
- return;
- }
-
- // Expect a server greeting as the first order of business.
-
- installBackgroundTask(new mail::imapGreetingHandler(loginInfo));
-}
-
-mail::imap::~imap()
-{
- removeAllHandlers(false, NULL);
-
- disconnect();
-}
-
-// Verbotten characters
-
-#if 0
-void mail::imap::mkverbotten(std::vector<unicode_char> &verbotten)
-{
- // Some chars should be modutf7-
- // encoded, just to be on the safe
- // side.
-
- verbotten.push_back('/');
- verbotten.push_back('\\');
- verbotten.push_back('%');
- verbotten.push_back('*');
- verbotten.push_back(':');
- verbotten.push_back('~');
- verbotten.push_back(0);
-}
-#endif
-
-//
-// Prefix an increasing counter to an imap tag, and send the entire command
-//
-
-void mail::imap::imapcmd(string cmd, string arg)
-{
- if (cmd.size() == 0)
- {
- socketWrite(arg);
- return;
- }
-
- if (smap)
- {
- socketWrite(cmd + " " + arg);
- return;
- }
-
- ostringstream o;
-
- cmdcounter=(cmdcounter + 1) % 1000000;
- o << setw(6) << setfill('0') << cmdcounter
- << cmd << " " << arg;
-
- socketWrite(o.str());
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-// Install a new handler to process replies from the IMAP server
-//
-// If there's already an active foreground task handler, push the handler
-// onto the task queue.
-
-void mail::imap::installForegroundTask(mail::imapHandler *handler)
-{
- imapHandler *foregroundTask;
-
- handler->setBackgroundTask(false);
-
- if ((foregroundTask=hasForegroundTask()) != NULL)
- {
- if (!task_queue.empty())
- foregroundTask=NULL;
-
- task_queue.push(handler);
-
- if (foregroundTask)
- foregroundTask->anotherHandlerInstalled(*this);
- return;
- }
-
- insertHandler(handler);
-}
-
-mail::imapHandler *mail::imap::hasForegroundTask()
-{
- handlerMapType::iterator b=handlers.begin(), e=handlers.end();
- imapHandler *h;
-
- while (b != e)
- if (!(h=(*b++).second)->getBackgroundTask())
- return h;
- return NULL;
-}
-
-// Unconditionally install a new message handler.
-// (any existing message handler with the same name is removed).
-
-void mail::imap::insertHandler(mail::imapHandler *handler)
-{
- if (!handler)
- LIBMAIL_THROW("mail::imap:: out of memory.");
-
- const char *name=handler->getName();
-
- if (handlers.count(name) > 0)
- {
- mail::imapHandler *oldHandler=
- &*handlers.find(name)->second;
-
- uninstallHandler(oldHandler);
- }
-
- handlers.insert(make_pair(name, handler));
- handler->installedFlag=true;
- handler->myimap=this;
- handler->setTimeout();
- handler->installed( *this ); // Tell the handler the show's on.
-}
-
-// Install a new message handler, and mark it as a background handler,
-// so that foreground message handlers can still be installed.
-// (any existing message handler with the same name is removed).
-
-void mail::imap::installBackgroundTask(mail::imapHandler *handler)
-{
- handler->setBackgroundTask(true);
- insertHandler(handler);
- current_handler=handler;
-}
-
-// Unconditionally remove a handler. The handler MUST be currently installed
-// as active (it cannot be on the task queue
-
-void mail::imap::uninstallHandler(mail::imapHandler *handler)
-{
- if (!handler->installedFlag)
- {
- delete handler;
- return;
- }
-
- remove(handler);
- delete handler;
-}
-
-// Remove an active handler.
-
-void mail::imap::remove(mail::imapHandler *handler)
-{
- const char *name=handler->getName();
-
- if (current_handler != NULL &&
- strcmp(current_handler->getName(), handler->getName()) == 0)
- current_handler=NULL; // Removed the current handler.
-
- handlers.erase(name);
- handler->installedFlag=false;
-
- // If we just popped off a foreground task, and there are other
- // foreground tasks waiting, install the next foreground task.
-
- if (!handler->getBackgroundTask())
- {
- if (!task_queue.empty())
- {
- mail::imapHandler *nextTask=task_queue.front();
- task_queue.pop();
-
- insertHandler(nextTask);
- }
- else if (wantIdleMode)
- {
- updateNotify(NULL);
- }
- }
-}
-
-void mail::imap::fatalError(string errmsg)
-{
- disconnect_callback.servererror(errmsg.c_str());
-}
-
-bool mail::imap::ready(mail::callback &callback)
-{
- if (socketConnected())
- return true;
-
- callback.fail("Account connection closed.");
- return false;
-}
-
-void mail::imap::resumed()
-{
- handlerMapType::iterator bb=handlers.begin(),
- ee=handlers.end();
-
- while (bb != ee)
- {
- if (strcasecmp(bb->first, mail::imapFOLDER::name) &&
- strcasecmp(bb->first, mail::smapFOLDER::name))
- bb->second->setTimeout();
- ++bb;
- }
-}
-
-///////////////////////////////////////////////////////////////////////////
-//
-// Main event dispatcher. Read any response received from the IMAP server,
-// and process it. Write any pending output to the imap server
-//
-// r, w: initialized to indicate any I/O status that must occur before
-// process() should be called again. r, w should be used as arguments to
-// select(2) when process() returns.
-
-void mail::imap::handler(vector<pollfd> &fds, int &ioTimeout)
-{
- bool timedoutFlag=false;
- int fd_save=getfd();
-
- handlerMapType::iterator bb=handlers.begin(),
- ee=handlers.end();
-
- writtenFlag=false;
-
- MONITOR(mail::imap);
-
- while (bb != ee)
- {
- int t;
-
- if (bb->second->getTimeout(*this, t))
- timedoutFlag=true;
-
- if (t < ioTimeout)
- ioTimeout=t;
- bb++;
- }
-
- if (timedoutFlag)
- {
- timeoutdisconnect();
- return;
- }
-
- if (mail::fd::process(fds, ioTimeout) < 0)
- return;
-
- if (DESTROYED())
- ioTimeout=0;
-
- if (DESTROYED() || getfd() < 0)
- {
- size_t i;
-
- for (i=fds.size(); i; )
- {
- --i;
-
- if (fds[i].fd == fd_save)
- {
- fds.erase(fds.begin()+i, fds.begin()+i+1);
- break;
- }
- }
- return;
- }
-
- if (writtenFlag)
- ioTimeout=0;
-
- return;
-}
-
-//
-// Figure out where a response from the server should go.
-
-int mail::imap::socketRead(const string &readbuffer)
-{
- MONITOR(mail::imap);
-
- string buffer_cpy=readbuffer;
-
- // Try whatever handler accepted previous folder output.
-
- if (current_handler != NULL)
- {
-#if DEBUG
- cerr << "Handler: " << current_handler->getName()
- << endl;
-#endif
- int n;
-
- n=current_handler->process(*this, buffer_cpy);
-
-
-#if DEBUG
- cerr << " Processed " << n << " bytes." << endl;
-#endif
- if (DESTROYED())
- return 0;
-
- if (n > 0)
- return n; // The handler took the input
- }
-
- // If the last handler didn't want it, step through the active
- // handlers, until someone picks it up.
-
- int processed=0;
-
- handlerMapType::iterator bb=handlers.begin(),
- ee=handlers.end();
-
- while (bb != ee)
- {
-#if DEBUG
- cerr << "Trying " << bb->second->getName() << " ... "
- << endl;
-
- if (strcmp(bb->second->getName(), "SELECT") == 0)
- {
- printf("OK\n");
- }
-#endif
- current_handler=bb->second;
-
- processed=bb->second->process(*this, buffer_cpy);
-
- if (DESTROYED())
- return 0;
-
- if (processed > 0)
- {
-#if DEBUG
- cerr << " ... processed " << processed
- << " of " << readbuffer.size()
- << " bytes." << endl;
-#endif
- break;
- }
- current_handler=NULL;
- bb++;
- }
-
- if (processed > 0)
- return processed;
-
- size_t pos=buffer_cpy.find('\n');
-
- if (pos == std::string::npos)
- {
- if (buffer_cpy.size() > 16000)
- return buffer_cpy.size() - 16000;
- // SANITY CHECK - DON'T LET HOSTILE SERVER DOS us
-
- return 0; // Read more
- }
-
- // Ok, there are some server responses we know how to do ourselves.
-
- buffer_cpy.erase(pos);
-
- size_t processedCnt=pos+1;
-
-
- while ((pos=buffer_cpy.find('\r')) != std::string::npos)
- buffer_cpy.erase(buffer_cpy.begin() + pos,
- buffer_cpy.begin() + pos + 1);
-
- string::iterator mb=buffer_cpy.begin(), me=buffer_cpy.end();
-
- if ( mb != me && *mb == '*' )
- {
- ++mb;
-
- string::iterator save_mb=mb;
-
- string msg=get_word(&mb, &me);
-
- upper(msg);
-
- if (msg == "CAPABILITY")
- {
- clearCapabilities();
- string status;
-
- while ((status=get_word(&mb, &me)).length() > 0)
- setCapability(status);
- return processedCnt;
- }
-
- if (msg == "OK")
- {
- size_t p=buffer_cpy.find('[');
- size_t q=buffer_cpy.find(']');
- string brk, brkw;
- string::iterator bb, be;
-
- if (p != std::string::npos && q != std::string::npos &&
- q > p && ((brk=string(buffer_cpy.begin()+p,
- buffer_cpy.begin()+q)),
- bb=brk.begin(),
- be=brk.end(),
- brkw=get_word(&bb, &be), upper(brkw),
- brkw) != "ALERT")
- {
- if (brkw == "PERMANENTFLAGS")
- setPermanentFlags(bb, be);
-
- return processedCnt;
- }
- }
-
- if (msg == "NO" || msg == "OK")
- {
- if (serverMsgs.size() < 15)
- {
- if (msg == "NO")
- mb=save_mb;
-
- serverMsgs.push_back(std::string(mb, me));
- }
-
- return processedCnt;
- }
-
- if (msg != "BYE")
- mb=save_mb;
-
- while (mb != me && unicode_isspace((unsigned char)*mb))
- ++mb;
-
- buffer_cpy.erase(buffer_cpy.begin(), mb);
- }
-
- fatalError(buffer_cpy);
-
- if (DESTROYED())
- return 0;
-
- return processedCnt;
-}
-
-void mail::imap::setPermanentFlags(string::iterator flagb,
- string::iterator flage)
-{
- string w;
-
- permanentFlags.clear();
-
- while (flagb != flage && unicode_isspace((unsigned char)*flagb))
- ++flagb;
-
- if (flagb == flage)
- return;
-
- if (*flagb != '(')
- {
- w=get_word(&flagb, &flage);
- upper(w);
- if (w != "NIL")
- permanentFlags.insert(w);
- return;
- }
- ++flagb;
-
- while ((w=get_word(&flagb, &flage)).size() > 0)
- {
- upper(w);
-
- permanentFlags.insert(w);
- }
-}
-
-void mail::imap::timeoutdisconnect()
-{
- const char *errmsg="Server connection closed due to a timeout.";
-
- removeAllHandlers(true, errmsg);
- disconnect(errmsg);
-}
-
-void mail::imap::disconnect(const char *errmsg)
-{
- string errmsg_cpy=errmsg ? errmsg:"";
-
- if (getfd() >= 0)
- {
- string errmsg2=socketDisconnect();
-
- if (errmsg2.size() > 0)
- errmsg_cpy=errmsg2;
-
- removeAllHandlers(true, errmsg_cpy.size() == 0 ?
- "Connection closed by remote host."
- :errmsg_cpy.c_str());
-
-
- if (orderlyShutdown)
- errmsg_cpy="";
- else if (errmsg_cpy.size() == 0)
- errmsg_cpy="Connection closed by remote host.";
-
- disconnect_callback.disconnected(errmsg_cpy.c_str());
- }
- else
- {
- removeAllHandlers(true, errmsg_cpy.size() == 0 ?
- "Connection closed by remote host.":errmsg);
- }
-}
-
-void mail::imap::setCapability(string status)
-{
- upper(status);
- capabilities.insert(status);
-}
-
-bool mail::imap::hasCapability(string status)
-{
- upper(status);
-
- if (status == LIBMAIL_SINGLEFOLDER)
- return false;
-
- if (status == "ACL") // Special logic
- {
- if (capabilities.count("ACL") > 0)
- return true;
-
- set<string>::iterator p;
-
- for (p=capabilities.begin(); p != capabilities.end(); ++p)
- {
- string s= *p;
- upper(s);
- if (strncmp(s.c_str(), "ACL2=", 5) == 0)
- return true;
- }
- return false;
- }
-
- return (capabilities.count(status) > 0);
-}
-
-string mail::imap::getCapability(string name)
-{
- upper(name);
-
- if (name == "ACL") // Special logic
- {
- set<string>::iterator p;
-
- for (p=capabilities.begin(); p != capabilities.end(); ++p)
- {
- string s= *p;
- upper(s);
- if (strncmp(s.c_str(), "ACL2=", 5) == 0)
- return s;
- }
- if (capabilities.count("ACL") > 0)
- return name;
- return "";
- }
-
- if (name == LIBMAIL_SERVERTYPE)
- {
- return servertype.size() == 0 ? "imap":servertype;
- }
-
- if (name == LIBMAIL_SERVERDESCR)
- {
- return serverdescr.size() == 0 ? "IMAP server":serverdescr;
- }
-
- return (capabilities.count(name) > 0 ? "1":"");
-}
-
-// Utility functions...
-
-string mail::imap::get_word(string::iterator *b, string::iterator *e)
-{
- string s="";
-
- while (*b != *e && unicode_isspace((unsigned char)**b))
- ++*b;
-
- while (*b != *e && !unicode_isspace((unsigned char)**b) &&
- strchr(")]", **b) == NULL)
- {
- s.append(&**b, 1);
- ++*b;
- }
- return s;
-}
-
-// IMAP tags have are prefixed with a numerical counter. Throw it away
-
-string mail::imap::get_cmdreply(string::iterator *b, string::iterator *e)
-{
- while (*b != *e && isdigit((int)(unsigned char)**b))
- ++*b;
- return (get_word(b, e));
-}
-
-
-string mail::imap::quoteSimple(string s)
-{
- string::iterator b, e;
-
-#if 0
- b=s.begin();
- e=s.end();
-
- while (b != e)
- {
- if (*b >= 127 || *b < ' ')
- {
- string buffer;
-
- {
- ostringstream o;
-
- o << "{" << s.size() << "}\r\n";
- buffer=o.str();
- }
-
- return buffer + s;
- }
- b++;
- }
-#endif
-
- string t="\"";
-
- b=s.begin();
- e=s.end();
-
- while (b != e)
- {
- if (*b == '"' || *b == '\\')
- t.append("\\");
- t.append(&*b, 1);
- b++;
- }
- t.append("\"");
- return t;
-}
-
-string mail::imap::quoteSMAP(string s)
-{
- string::iterator b, e;
-
- string t="\"";
-
- b=s.begin();
- e=s.end();
-
- while (b != e)
- {
- if (*b == '"')
- t.append("\"");
- t.append(&*b, 1);
- b++;
- }
- t.append("\"");
- return t;
-}
-
-//
-// Remove all handlers. Possible reasons: destructor, connection timeout,
-// orderly shutdown.
-//
-
-void mail::imap::removeAllHandlers(bool timedOutFlag, // true if conn timeout
- const char *errmsg)
-{
- while (!handlers.empty())
- {
- mail::imapHandler *handler=handlers.begin()->second;
-
- if (timedOutFlag)
- handler->timedOut(errmsg);
-
- handlers.erase(handlers.begin()->first);
- handler->installedFlag=false;
- delete handler;
- }
-
- while (!task_queue.empty())
- {
- mail::imapHandler *p=task_queue.front();
-
- task_queue.pop();
-
- if (timedOutFlag)
- p->timedOut(errmsg);
- delete p;
- }
-}
-
-mail::imap::msgSet::msgSet()
-{
-}
-
-mail::imap::msgSet::~msgSet()
-{
-}
-
-// Convert an array of message numbers to imap-style msg set
-
-mail::imap::msgSetRange::msgSetRange(mail::imap *pArg,
- const vector<size_t> &messages)
- : p(pArg)
-{
- msglist=messages;
-
- sort(msglist.begin(), msglist.end());
- nextMsg=msglist.begin();
-}
-
-mail::imap::msgSetRange::~msgSetRange()
-{
-}
-
-size_t mail::imap::msgSetRange::uidNum(mail::imap *i, size_t j)
-{
- if (i->currentFolder == NULL || j >= i->currentFolder->index.size())
- return 0;
-
- // mail::imapFolder constructs the abstract libmail.a uid as
- // UIDVALIDITY/UID, so find the UID part of it.
-
- string uid=i->currentFolder->index[j].uid;
-
- size_t n=uid.find('/');
-
- if (n == string::npos)
- return 0;
-
- uid=uid.substr(n+1);
-
- n=0;
-
- istringstream(uid.c_str()) >> n;
-
- return n;
-}
-
-bool mail::imap::msgSetRange::getNextRange(size_t &first, size_t &last)
-{
- do
- {
- if (nextMsg == msglist.end())
- return false;
- } while ((first=last= uidNum(p, *nextMsg++)) == 0);
-
-
- while (nextMsg != msglist.end() && last + 1 == uidNum(p, *nextMsg))
- last = uidNum(p, *nextMsg++);
-
- return true;
-}
-
-void mail::imap::messagecmd(const vector<size_t> &messages, string parameters,
- string operation,
- string name,
- mail::callback::message &callback)
-{
- msgSetRange setRange(this, messages);
-
- messagecmd(setRange, parameters, operation, name, callback);
-}
-
-void mail::imap::messagecmd(msgSet &msgRange, string parameters,
- string operation,
- string name,
- mail::callback::message &callback)
-{
- if (!ready(callback))
- return;
-
- // Prevent excessively huge IMAP commands that result from large
- // message sets. The message set is capped at about 100 characters,
- // and multiple commands are generated, if this is not enough.
- //
- // mail::imapFetchHandler is told how many commands went out, so
- // it knows to count the number of server replies.
-
- mail::imapFetchHandler *cmd=new mail::imapFetchHandler(callback, name);
-
- size_t first, last;
-
- string s="";
-
- try {
- if (!cmd)
- LIBMAIL_THROW(strerror(errno));
-
- while (msgRange.getNextRange(first, last))
- {
- cmd->messageCntTotal += last + 1 - first;
-
- if (s.length() > 0)
- {
- if (s.length() > 100)
- {
- cmd->commands
- .push( make_pair(name,
- operation
- + " " + s
- + parameters
- + "\r\n"));
- ++cmd->counter;
- s="";
- }
- else
- s += ",";
- }
-
- string buffer;
-
- if (first != last)
- {
- ostringstream o;
-
- o << first << ":" << last;
- buffer=o.str();
- }
- else
- {
- ostringstream o;
-
- o << first;
- buffer=o.str();
- }
-
- s += buffer;
- }
-
- if (s.size() == 0)
- {
- delete cmd;
- cmd=NULL;
- callback.success("No messages to process.");
- return;
- }
-
- cmd->commands.push( make_pair(name,
- operation + " " + s + parameters
- + "\r\n"));
- ++cmd->counter;
-
- installForegroundTask(cmd);
- } catch (...) {
-
- if (cmd)
- delete cmd;
-
- callback.fail("An exception occured while attempting to start a command.");
- }
-}
-
-
-
-
-////////////////////////////////////////////////////////////////////////
-//
-
-void mail::imap::readMessageAttributes(const vector<size_t> &messages,
- MessageAttributes attributes,
- mail::callback::message &callback)
-{
- if (smap)
- {
- installForegroundTask(new smapFETCHATTR(messages,
- attributes,
- callback,
- *this));
- return;
- }
-
- string attrList="";
-
- if (attributes & ARRIVALDATE)
- attrList += " INTERNALDATE";
-
- if (attributes & MESSAGESIZE)
- attrList += " RFC822.SIZE";
-
- if (attributes & ENVELOPE)
- attrList += " ENVELOPE BODY.PEEK[HEADER.FIELDS (References)]";
-
- if (attributes & MIMESTRUCTURE)
- attrList += " BODYSTRUCTURE";
-
- if (attrList.size() == 0)
- {
- callback.success("No attributes were requested.");
- return;
- }
-
- attrList[0]='(';
-
- messagecmd(messages, " " + attrList + ")",
- "UID FETCH", "FETCHENV",
- callback);
-}
-
-void mail::imap::readMessageContent(const vector<size_t> &messages,
- bool peek,
- enum mail::readMode readType,
- mail::callback::message &callback)
-{
- if (smap)
- {
- installForegroundTask(new smapFETCH(messages,
- peek,
- "",
- readType,
- "",
- callback,
- *this));
- return;
- }
-
- doReadMessageContent(messages, peek, NULL, readType, callback);
-}
-
-//////////////////////////////////////////////////////////////////////////
-//
-// There is no single RFC 2060 command that returns the headers of a
-// MIME section, a blank line, then contents. Therefore, we punt: first,
-// we request a .MIME, then when this command returns, we request the body.
-// This is implemented using the following helper object.
-//
-
-LIBMAIL_START
-
-class imapComboFetchCallback
- : public mail::callback::message {
-
- mail::callback::message &originalCallback;
- bool origPeek;
- imap &origImap;
- size_t origMessageNum;
-
- string mime_id;
-
- void reportProgress(size_t bytesCompleted,
- size_t bytesEstimatedTotal,
-
- size_t messagesCompleted,
- size_t messagesEstimatedTotal);
-
-public:
- imapComboFetchCallback(mail::callback::message &callback,
- bool peek,
- size_t messageNum,
- const mimestruct &msginfo,
- imap &imapAccount);
- ~imapComboFetchCallback();
- void messageTextCallback(size_t n, string text);
- void fail(string message);
- void success(string message);
-};
-
-LIBMAIL_END
-
-mail::imapComboFetchCallback::imapComboFetchCallback(mail::callback::message
- &callback,
- bool peek,
- size_t messageNum,
- const mimestruct &msginfo,
- imap &imapAccount)
- : originalCallback(callback), origPeek(peek), origImap(imapAccount),
- origMessageNum(messageNum),
- mime_id(msginfo.mime_id)
-{
-}
-
-mail::imapComboFetchCallback::~imapComboFetchCallback()
-{
-}
-
-void mail::imapComboFetchCallback::messageTextCallback(size_t n, string text)
-{
- originalCallback.messageTextCallback(n, text);
-}
-
-void mail::imapComboFetchCallback::fail(string message)
-{
- originalCallback.fail(message);
- delete this;
-}
-
-void mail::imapComboFetchCallback::success(string message)
-{
- vector<size_t> messageVector;
-
- messageVector.push_back(origMessageNum);
-
- string cmd="BODY";
-
- if (origPeek)
- cmd="BODY.PEEK";
-
- origImap.messagecmd(messageVector,
- " (" + cmd + "[" + mime_id + "])",
- "UID FETCH",
- "CONTENTS", originalCallback);
- delete this;
-}
-
-void mail::imapComboFetchCallback::reportProgress(size_t bytesCompleted,
- size_t bytesEstimatedTotal,
-
- size_t messagesCompleted,
- size_t messagesEstimatedTotal
- )
-{
- originalCallback.reportProgress(bytesCompleted,
- bytesEstimatedTotal,
- messagesCompleted,
- messagesEstimatedTotal);
-}
-
-void mail::imap::readMessageContent(size_t messageNum,
- bool peek,
- const mail::mimestruct &msginfo,
- enum mail::readMode readType,
- mail::callback::message &callback)
-{
- vector<size_t> messageVector;
-
- messageVector.push_back(messageNum);
-
- if (smap)
- {
- installForegroundTask(new smapFETCH(messageVector,
- peek,
- msginfo.mime_id,
- readType,
- "",
- callback,
- *this));
- return;
- }
-
- if (readType == readBoth)
- {
- const mail::mimestruct *parent=msginfo.getParent();
-
- if (parent != NULL && !parent->messagerfc822())
- {
- // There is no RFC2060 command that returns what we
- // want here: .MIME, then the body, so we punt
-
- mail::imapComboFetchCallback *fakeCallback=new
- mail::imapComboFetchCallback(callback,
- peek,
- messageNum,
- msginfo,
- *this);
-
- if (!fakeCallback)
- LIBMAIL_THROW("Out of memory.");
-
- try {
- doReadMessageContent(messageVector,
- peek,
- &msginfo,
- readHeaders,
- *fakeCallback);
- } catch (...) {
- delete fakeCallback;
- LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
- }
- return;
- }
- }
- doReadMessageContent(messageVector, peek, &msginfo,
- readType,
- callback);
-}
-
-//
-// readMessageContentDecoded is implemented by creating a proxy
-// mail::generic::Decode object, that handles MIME decoding by itself,
-// then forwards the decoded data to the original callback.
-
-void mail::imap::readMessageContentDecoded(size_t messageNum,
- bool peek,
- const mail::mimestruct &msginfo,
- mail::callback::message
- &callback)
-{
- if (smap)
- {
- vector<size_t> messageVector;
-
- messageVector.push_back(messageNum);
-
- installForegroundTask(new smapFETCH(messageVector,
- peek,
- msginfo.mime_id,
- readContents,
- ".DECODED",
- callback,
- *this));
- return;
- }
-
- mail::generic::Decode *d=
- new mail::generic::Decode(callback,
- msginfo.content_transfer_encoding);
-
- if (!d)
- LIBMAIL_THROW("Out of memory.");
-
- try {
- readMessageContent(messageNum, peek, msginfo,
- readContents, *d);
- } catch (...)
- {
- delete d;
- LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
- }
-}
-
-//
-// Helper class for folding headers read from the IMAP server.
-//
-
-LIBMAIL_START
-
-class imapFolderHeadersCallback : public mail::callback::message {
-
- mail::callback::message &originalCallback;
-
- bool doFolding;
-
- bool prevWasNewline;
- bool prevFoldingSpace;
-
- void reportProgress(size_t bytesCompleted,
- size_t bytesEstimatedTotal,
-
- size_t messagesCompleted,
- size_t messagesEstimatedTotal);
-
-public:
- imapFolderHeadersCallback(bool, mail::callback::message &);
- ~imapFolderHeadersCallback();
-
- void messageTextCallback(size_t n, string text);
- void success(string message);
- void fail(string message);
-};
-
-LIBMAIL_END
-
-mail::imapFolderHeadersCallback
-::imapFolderHeadersCallback(bool doFoldingArg,
- mail::callback::message &origArg)
- : originalCallback(origArg), doFolding(doFoldingArg),
- prevWasNewline(false), prevFoldingSpace(false)
-{
-}
-
-mail::imapFolderHeadersCallback::~imapFolderHeadersCallback()
-{
-}
-
-void mail::imapFolderHeadersCallback
-::reportProgress(size_t bytesCompleted,
- size_t bytesEstimatedTotal,
-
- size_t messagesCompleted,
- size_t messagesEstimatedTotal)
-{
- originalCallback.reportProgress(bytesCompleted, bytesEstimatedTotal,
- messagesCompleted,
- messagesEstimatedTotal);
-}
-
-// Intercept the data in the callback, and use it to fold header lines.
-
-void mail::imapFolderHeadersCallback
-::messageTextCallback(size_t n, string text)
-{
- string::iterator b=text.begin(), e=text.end(), c=b;
-
- string cpy="";
-
- while (b != e)
- {
- if (prevWasNewline) // Prev char was \n, which was eaten.
- {
- // This is the first character after a newline.
-
- prevWasNewline=false;
- if (*b == '\n') // Blank line, ignore
- {
- cpy += "\n"; // The eaten newline
- b++;
- c=b;
- continue;
- }
-
- if ( doFolding && unicode_isspace((unsigned char)*b))
- {
- // Bingo.
-
- prevFoldingSpace=true;
- cpy += " "; // Eaten newline replaced by spc
- b++;
- continue;
- }
-
- cpy += "\n"; // The eaten newline.
- c=b;
- b++;
- continue;
- }
-
- if (prevFoldingSpace)
- {
- if ( *b != '\n' && unicode_isspace((unsigned char)*b))
- {
- b++;
- continue; // Eating all whitespace
- }
-
- prevFoldingSpace=false; // Resume header.
- c=b;
- }
-
- if (*b == '\n')
- {
- cpy.insert(cpy.end(), c, b);
- prevWasNewline=true;
- b++;
- continue;
- }
- b++;
- }
-
- if (!prevWasNewline && !prevFoldingSpace)
- cpy.insert(cpy.end(), c, b);
-
- originalCallback.messageTextCallback(n, cpy);
-}
-
-void mail::imapFolderHeadersCallback::success(string message)
-{
- originalCallback.success(message);
- delete this;
-}
-
-void mail::imapFolderHeadersCallback::fail(string message)
-{
- originalCallback.fail(message);
- delete this;
-}
-
-//////
-void mail::imap::doReadMessageContent(const vector<size_t> &messages,
- bool peek,
- const mail::mimestruct *msginfo,
- mail::readMode readType,
- mail::callback::message &callback)
-{
- if (!ready(callback))
- return;
-
- bool needHeaderDecode=false;
- bool foldHeaders=false;
-
- string mime_id;
- const mail::mimestruct *parent=msginfo ? msginfo->getParent():NULL;
-
- // RFC 2060 mess.
-
- if (readType == readBoth)
- {
- if (parent == NULL)
- mime_id="";
- else
- mime_id=parent->mime_id;
- }
- else if (readType == readHeadersFolded ||
- readType == readHeaders)
- {
- needHeaderDecode=true;
-
- if (readType == readHeadersFolded)
- foldHeaders=true;
-
- if (parent == NULL)
- mime_id="HEADER";
- else if (parent->messagerfc822())
- mime_id=parent->mime_id + ".HEADER";
- else
- mime_id=msginfo->mime_id + ".MIME";
- }
- else
- {
- // Retrieve content
-
- if (parent == NULL)
- mime_id="TEXT";
- else if (parent->messagerfc822())
- mime_id=parent->mime_id + ".TEXT";
- else
- mime_id=msginfo->mime_id;
- }
-
- string cmd="BODY";
-
- if (peek)
- cmd="BODY.PEEK";
-
- mail::imapFolderHeadersCallback *decodeCallback=NULL;
-
- if (needHeaderDecode)
- {
- if ((decodeCallback=new mail::imapFolderHeadersCallback
- (foldHeaders, callback)) == NULL)
- {
- callback.fail("Out of memory.");
- return;
- }
- }
-
- try {
- mail::callback::message *cb= &callback;
-
- if (decodeCallback)
- cb=decodeCallback;
-
- messagecmd(messages,
- " (" + cmd + "[" + mime_id + "])", "UID FETCH",
- "CONTENTS", *cb);
-
- } catch (...) {
- if (decodeCallback)
- delete decodeCallback;
- LIBMAIL_THROW(LIBMAIL_THROW_EMPTY);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////
-//
-// Helper class for most commands. Determines whether the read line of
-// input contains an untagged message (which results in untaggedMessage()
-// getting invoked), or a tagged message (which results in taggedMessage()
-// getting invoked.
-
-mail::imapCommandHandler::imapCommandHandler(int timeout)
- : mail::imapHandler(timeout)
-{
-}
-
-mail::imapCommandHandler::~imapCommandHandler()
-{
-}
-
-int mail::imapCommandHandler::process(mail::imap &imapAccount, string &buffer)
-{
- string::iterator b=buffer.begin();
- string::iterator e=buffer.end();
-
- while (b != e)
- {
- if (unicode_isspace((unsigned char)*b))
- {
- b++;
- continue;
- }
- if (*b != '*')
- break;
-
- b++;
-
- string w=imapAccount.get_word(&b, &e);
-
- if (b == e)
- break;
-
- size_t cnt= b - buffer.begin();
-
- mail::upper(w);
-
- if (untaggedMessage(imapAccount, w))
- return (cnt);
- return (0);
- }
-
- size_t p=buffer.find('\n');
-
- if (p == std::string::npos)
- return (0);
-
- string buffer_cpy=buffer;
-
- buffer_cpy.erase(p);
-
- size_t stripcr;
-
- while ((stripcr=buffer_cpy.find('\r')) != std::string::npos)
- buffer_cpy.erase(buffer_cpy.begin() + stripcr,
- buffer_cpy.begin() + stripcr + 1);
-
- if (buffer_cpy.size() > 0 &&
- buffer_cpy[0] == (imapAccount.smap ? '>':'+'))
- return continuationRequest(imapAccount, buffer_cpy) ? p+1:0;
-
- b=buffer_cpy.begin();
- e=buffer_cpy.end();
-
- string w=imapAccount.get_cmdreply(&b, &e);
-
- upper(w);
-
- if (imapAccount.smap)
- {
- while (b != e && unicode_isspace((unsigned char)*b))
- b++;
-
- buffer_cpy.erase(0, b - buffer_cpy.begin());
-
- return taggedMessage(imapAccount,
- w,
- buffer_cpy,
- w == "+OK",
- buffer_cpy) ? p+1:0;
- }
-
- buffer_cpy.erase(0, b - buffer_cpy.begin());
-
- string errmsg=buffer_cpy;
-
- b=errmsg.begin();
- e=errmsg.end();
-
- string okfailWord=imapAccount.get_word(&b, &e);
-
- upper(okfailWord);
-
- while (b != e && unicode_isspace((unsigned char)*b))
- b++;
-
- errmsg.erase(0, b-errmsg.begin());
-
- b=errmsg.begin();
- e=errmsg.end();
-
- if (b != e && *b == '[') // Drop OK [APPENDUID ...], and friends
- {
- while (b != e && *b != ']')
- b++;
-
- if (b != e)
- b++;
-
- while (b != e && unicode_isspace((unsigned char)*b))
- b++;
- errmsg.erase(0, b-errmsg.begin());
- }
-
- ptr<mail::imap> acctPtr= &imapAccount;
-
- int n=taggedMessage(imapAccount, w, buffer_cpy,
- okfailWord == "OK", errmsg) ? p+1:0;
-
- // Report any accumulated server messages in the response
-
- if (!acctPtr.isDestroyed() && acctPtr->serverMsgs.size() > 0)
- {
- vector<string>::iterator b, e;
-
- b=acctPtr->serverMsgs.begin();
- e=acctPtr->serverMsgs.end();
-
- errmsg="";
-
- while (b != e)
- {
- if (errmsg.size() > 0)
- errmsg += "\n";
- errmsg += *b++;
- }
- acctPtr->serverMsgs.clear();
- acctPtr->fatalError(errmsg);
- }
- return n;
-}
-
-// Subclasses must explicitly declare their intention to handle continuation
-// requests.
-
-bool mail::imapCommandHandler::continuationRequest(mail::imap &imapAccount,
- string request)
-{
- return false;
-}