summaryrefslogtreecommitdiff
path: root/src/operator-fetch.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/operator-fetch.cc')
-rw-r--r--src/operator-fetch.cc641
1 files changed, 641 insertions, 0 deletions
diff --git a/src/operator-fetch.cc b/src/operator-fetch.cc
new file mode 100644
index 0000000..810afb5
--- /dev/null
+++ b/src/operator-fetch.cc
@@ -0,0 +1,641 @@
+/** --------------------------------------------------------------------
+ * @file operator-fetch.cc
+ * @brief Implementation of the FETCH command
+ * @author Andreas Aardal Hanssen
+ * @date 2002-2005
+ * ----------------------------------------------------------------- **/
+#include <string>
+
+#include "depot.h"
+#include "iodevice.h"
+#include "iofactory.h"
+#include "mailbox.h"
+#include "operators.h"
+#include "imapparser.h"
+#include "pendingupdates.h"
+#include "recursivedescent.h"
+#include "session.h"
+#include "convert.h"
+
+using namespace ::std;
+using namespace Binc;
+
+namespace {
+ void outputFlags(const Message & message)
+ {
+ bincClient << "FLAGS ";
+
+ bincClient << "(";
+ int flags = message.getStdFlags();
+ vector<string> flagv;
+ if (flags & Message::F_SEEN) flagv.push_back("\\Seen");
+ if (flags & Message::F_ANSWERED) flagv.push_back("\\Answered");
+ if (flags & Message::F_DELETED) flagv.push_back("\\Deleted");
+ if (flags & Message::F_DRAFT) flagv.push_back("\\Draft");
+ if (flags & Message::F_RECENT) flagv.push_back("\\Recent");
+ if (flags & Message::F_FLAGGED) flagv.push_back("\\Flagged");
+
+ for (vector<string>::const_iterator k
+ = flagv.begin(); k != flagv.end(); ++k) {
+ if (k != flagv.begin()) bincClient << " ";
+ bincClient << *k;
+ }
+
+ vector<string> customFlags = message.getCustomFlags();
+ for (vector<string>::const_iterator it = customFlags.begin();
+ it != customFlags.end(); ++it) {
+ if (flagv.size() > 0 || it != customFlags.begin()) bincClient << " ";
+ bincClient << *it;
+ }
+
+ bincClient << ")";
+ }
+
+}
+
+//----------------------------------------------------------------------
+FetchOperator::FetchOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+FetchOperator::~FetchOperator(void)
+{
+}
+
+//----------------------------------------------------------------------
+const string FetchOperator::getName(void) const
+{
+ return "FETCH";
+}
+
+//----------------------------------------------------------------------
+int FetchOperator::getState(void) const
+{
+ return Session::SELECTED;
+}
+
+//------------------------------------------------------------------------
+Operator::ProcessResult FetchOperator::process(Depot &depot,
+ Request &request)
+{
+ Session &session = Session::getInstance();
+
+ bool updateFlags = false;
+ Request req = request;
+
+ Mailbox *mailbox = depot.getSelected();
+
+ // If this is a UID FETCH, check if the UID attribute is fetched. If
+ // it is not, then add it to the list of fetch attributes.
+ vector<BincImapParserFetchAtt>::const_iterator f_i;
+ bool uidfetched = false;
+ if (request.getUidMode()) {
+ f_i = request.fatt.begin();
+ while (f_i != request.fatt.end()) {
+ if ((*f_i).type == "UID") {
+ uidfetched = true;
+ break;
+ }
+ f_i++;
+ }
+
+ if (!uidfetched) {
+ BincImapParserFetchAtt b;
+ b.type = "UID";
+ req.fatt.push_back(b);
+ }
+ }
+
+ // Convert macros ALL, FULL and FAST
+ f_i = request.fatt.begin();
+ while (f_i != request.fatt.end()) {
+ const string &type = (*f_i).type;
+ if (type == "ALL") {
+ req.fatt.push_back(BincImapParserFetchAtt("FLAGS"));
+ req.fatt.push_back(BincImapParserFetchAtt("INTERNALDATE"));
+ req.fatt.push_back(BincImapParserFetchAtt("RFC822.SIZE"));
+ req.fatt.push_back(BincImapParserFetchAtt("ENVELOPE"));
+ } else if (type == "FULL") {
+ req.fatt.push_back(BincImapParserFetchAtt("FLAGS"));
+ req.fatt.push_back(BincImapParserFetchAtt("INTERNALDATE"));
+ req.fatt.push_back(BincImapParserFetchAtt("RFC822.SIZE"));
+ req.fatt.push_back(BincImapParserFetchAtt("ENVELOPE"));
+ req.fatt.push_back(BincImapParserFetchAtt("BODY"));
+ } else if (type == "FAST") {
+ req.fatt.push_back(BincImapParserFetchAtt("FLAGS"));
+ req.fatt.push_back(BincImapParserFetchAtt("INTERNALDATE"));
+ req.fatt.push_back(BincImapParserFetchAtt("RFC822.SIZE"));
+ }
+
+ ++f_i;
+ }
+
+ int mode;
+ if (req.getUidMode())
+ mode = Mailbox::UID_MODE;
+ else
+ mode = Mailbox::SQNR_MODE;
+
+ Mailbox::iterator i
+ = mailbox->begin(req.bset, Mailbox::SKIP_EXPUNGED | mode);
+
+ for (; i != mailbox->end(); ++i) {
+ Message &message = *i;
+
+ bincClient << "* " << i.getSqnr() << " FETCH (";
+ bool hasprinted = false;
+ f_i = req.fatt.begin();
+ while (f_i != req.fatt.end()) {
+ BincImapParserFetchAtt fatt = *f_i;
+
+ string prefix = "";
+ if (hasprinted) prefix = " ";
+
+ if (fatt.type == "FLAGS") {
+ // FLAGS
+ hasprinted = true;
+ bincClient << prefix;
+
+ outputFlags(message);
+ } else if (fatt.type == "UID") {
+ // UID
+ hasprinted = true;
+ bincClient << prefix << "UID " << message.getUID();
+ } else if (fatt.type == "RFC822.SIZE") {
+ // RFC822.SIZE
+ hasprinted = true;
+ bincClient << prefix << "RFC822.SIZE " << message.getSize(true);
+ } else if (fatt.type == "ENVELOPE") {
+ // ENVELOPE
+ hasprinted = true;
+ bincClient << prefix << "ENVELOPE ";
+ message.printEnvelope();
+ } else if (fatt.type == "BODYSTRUCTURE") {
+ // BODYSTRUCTURE
+ hasprinted = true;
+ bincClient << prefix << "BODYSTRUCTURE ";
+ message.printBodyStructure(true);
+ } else if (fatt.type == "BODY" && !fatt.hassection) {
+ // BODY with no section
+ hasprinted = true;
+ session.addBody();
+ bincClient << prefix << "BODY ";
+ message.printBodyStructure(false);
+ } else if (fatt.type == "INTERNALDATE") {
+ // INTERNALDATE
+ hasprinted = true;
+ bincClient << prefix << "INTERNALDATE ";
+
+ time_t iDate = message.getInternalDate();
+ struct tm *_tm = gmtime(&iDate);
+ char internal[64];
+ string iDateStr;
+ if (strftime(internal, sizeof(internal),
+ "%d-%b-%Y %H:%M:%S %z", _tm) != 0) {
+ if (internal[0] == '0') internal[0] = ' ';
+ iDateStr = internal;
+ } else
+ iDateStr = "NIL";
+
+ bincClient << toImapString(iDateStr);
+ } else if (fatt.type == "BODY" || fatt.type == "BODY.PEEK") {
+ // BODY & BODY.PEEK
+ hasprinted = true;
+ session.addBody();
+
+ bincClient << prefix;
+ bool peek = (fatt.type == "BODY.PEEK");
+ bincClient << fatt.toString();
+
+ bool includeheaders = true;
+ bool fullheader = false;
+ bool bodyfetch = false;
+
+ if (fatt.section != "" || fatt.sectiontext == ""
+ || fatt.sectiontext == "TEXT") {
+ bodyfetch = true;
+ fullheader = true;
+ }
+
+ if (fatt.sectiontext == "HEADER.FIELDS.NOT")
+ includeheaders = false;
+
+ if (fatt.sectiontext == "HEADER"
+ || fatt.sectiontext == "HEADER.FIELDS"
+ || fatt.sectiontext == "HEADER.FIELDS.NOT"
+ || fatt.sectiontext == "MIME") {
+ vector<string> v;
+
+ if (fatt.sectiontext == "MIME") {
+ v.push_back("content-type");
+ v.push_back("content-transfer-encoding");
+ v.push_back("content-disposition");
+ v.push_back("content-description");
+ } else
+ v = fatt.headerlist;
+
+ string dummy;
+ unsigned int size = fullheader
+ ? message.getHeaderSize(fatt.section, v, true,
+ fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "MIME")
+ : message.getHeaderSize(fatt.section, fatt.headerlist,
+ includeheaders,
+ fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "MIME");
+
+ bincClient << "{" << size << "}\r\n";
+
+ if (fullheader) {
+ message.printHeader(fatt.section, v, true,
+ fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "MIME");
+ } else {
+ message.printHeader(fatt.section, fatt.headerlist,
+ includeheaders,
+ fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "MIME");
+ }
+ } else {
+ unsigned int size;
+ if ((fatt.sectiontext == "" || fatt.sectiontext == "TEXT")
+ && fatt.section == "")
+ size = message.getDocSize(fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "TEXT");
+ else
+ size = message.getBodySize(fatt.section,
+ fatt.offsetstart,
+ fatt.offsetlength);
+
+ bincClient << "{" << size << "}\r\n";
+
+ if ((fatt.sectiontext == "" || fatt.sectiontext == "TEXT")
+ && fatt.section == "")
+ message.printDoc(fatt.offsetstart,
+ fatt.offsetlength,
+ fatt.sectiontext == "TEXT");
+ else
+ message.printBody(fatt.section, fatt.offsetstart,
+ fatt.offsetlength);
+ }
+
+ // set the \Seen flag if .PEEK is not used.
+ if (!peek)
+ if ((message.getStdFlags() & Message::F_SEEN) == 0)
+ message.setStdFlag(Message::F_SEEN);
+ } else if (fatt.type == "RFC822") {
+ bincClient << prefix;
+ hasprinted = true;
+ session.addBody();
+ bincClient << fatt.toString();
+ unsigned int size = message.getDocSize(fatt.offsetstart,
+ fatt.offsetlength);
+ bincClient << " {" << size << "}\r\n";
+ message.printDoc(fatt.offsetstart, fatt.offsetlength);
+
+ // set the \Seen flag
+ if ((message.getStdFlags() & Message::F_SEEN) == 0)
+ message.setStdFlag(Message::F_SEEN);
+
+ } else if (fatt.type == "RFC822.HEADER") {
+ bincClient << prefix;
+ hasprinted = true;
+ bincClient << fatt.toString();
+ vector<string> v;
+ string dummy;
+ unsigned int size = message.getHeaderSize("", v, true,
+ fatt.offsetstart,
+ fatt.offsetlength);
+ bincClient << " {" << size << "}\r\n";
+ message.printHeader("", v, true, fatt.offsetstart,
+ fatt.offsetlength);
+ } else if (fatt.type == "RFC822.TEXT") {
+ // RFC822.TEXT
+ bincClient << prefix;
+ hasprinted = true;
+ session.addBody();
+
+ bincClient << fatt.toString();
+
+ bool bodyfetch = false;
+ bodyfetch = true;
+
+ unsigned int size;
+ if (fatt.sectiontext == "" && fatt.section == "")
+ size = message.getDocSize(fatt.offsetstart,
+ fatt.offsetlength, true);
+ else
+ size = message.getBodySize(fatt.section, fatt.offsetstart,
+ fatt.offsetlength);
+
+ bincClient << " {" << size << "}\r\n";
+
+ if (fatt.sectiontext == "" && fatt.section == "")
+ message.printDoc(fatt.offsetstart,
+ fatt.offsetlength, true);
+ else
+ message.printBody(fatt.section, fatt.offsetstart,
+ fatt.offsetlength);
+
+ // set the \Seen flag
+ if ((message.getStdFlags() & Message::F_SEEN) == 0)
+ message.setStdFlag(Message::F_SEEN);
+
+ } else {
+ // Unrecognized fetch_att, this is stopped by the parser
+ // so we never get here.
+ }
+
+ f_i++;
+ }
+
+ // FIXME: how are parse error passed back?
+
+ bincClient << ")" << endl;
+
+ if (message.hasFlagsChanged()) {
+ updateFlags = true;
+ bincClient << "* " << i.getSqnr() << " FETCH (";
+ outputFlags(message);
+ bincClient << ")" << endl;
+ message.setFlagsUnchanged();
+ }
+ }
+
+ if (updateFlags) mailbox->updateFlags();
+
+ pendingUpdates(mailbox,
+ PendingUpdates::FLAGS
+ | PendingUpdates::EXISTS
+ | PendingUpdates::EXPUNGE
+ | PendingUpdates::RECENT, true);
+
+ return OK;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult FetchOperator::parse(Request &c_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after FETCH");
+ return res;
+ }
+
+ if ((res = expectSet(c_in.getSet())) != ACCEPT) {
+ session.setLastError("Expected sequence set after FETCH SPACE");
+ return res;
+ }
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("Expected SPACE after FETCH SPACE set");
+ return res;
+ }
+
+ BincImapParserFetchAtt f;
+
+ if ((res = expectThisString("ALL")) == ACCEPT) {
+ f.type = "ALL";
+ c_in.fatt.push_back(f);
+ } else if ((res = expectThisString("FULL")) == ACCEPT) {
+ f.type = "FULL";
+ c_in.fatt.push_back(f);
+ } else if ((res = expectThisString("FAST")) == ACCEPT) {
+ f.type = "FAST";
+ c_in.fatt.push_back(f);
+ } else if ((res = expectFetchAtt(f)) == ACCEPT) {
+ c_in.fatt.push_back(f);
+ } else if ((res = expectThisString("(")) == ACCEPT) {
+ while (1) {
+ BincImapParserFetchAtt ftmp;
+ if ((res = expectFetchAtt(ftmp)) != ACCEPT) {
+ session.setLastError("Expected fetch_att");
+ return res;
+ }
+
+ c_in.fatt.push_back(ftmp);
+
+ if ((res = expectSPACE()) == REJECT) break;
+ else if (res == ERROR) return ERROR;
+ }
+
+ if ((res = expectThisString(")")) != ACCEPT) {
+ session.setLastError("Expected )");
+ return res;
+ }
+ } else {
+ session.setLastError("Expected ALL, FULL, FAST, fetch_att or (");
+ return res;
+ }
+
+ if ((res = expectCRLF()) != ACCEPT) {
+ session.setLastError("Expected CRLF");
+ return res;
+ }
+
+ c_in.setName("FETCH");
+ return ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectSectionText(BincImapParserFetchAtt &f_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectThisString("HEADER")) == ACCEPT) {
+ f_in.sectiontext = "HEADER";
+
+ if ((res = expectThisString(".FIELDS")) == ACCEPT) {
+ f_in.sectiontext += ".FIELDS";
+
+ if ((res = expectThisString(".NOT")) == ACCEPT)
+ f_in.sectiontext += ".NOT";
+
+ if ((res = expectSPACE()) != ACCEPT) {
+ session.setLastError("expected SPACE");
+ return res;
+ }
+
+ if ((res = expectHeaderList(f_in)) != ACCEPT) {
+ session.setLastError("Expected header_list");
+ return res;
+ }
+ }
+ } else if ((res = expectThisString("TEXT")) == ACCEPT)
+ f_in.sectiontext = "TEXT";
+ else
+ return REJECT;
+
+ return ACCEPT;
+
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectSection(BincImapParserFetchAtt &f_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectThisString("[")) != ACCEPT)
+ return REJECT;
+
+ if ((res = expectSectionText(f_in)) != ACCEPT) {
+ unsigned int n;
+ if ((res = expectNZNumber(n)) == ACCEPT) {
+ BincStream nstr;
+ nstr << n;
+ f_in.section = nstr.str();
+
+ bool gotadotalready = false;
+ while (1) {
+ if ((res = expectThisString(".")) != ACCEPT) break;
+
+ if ((res = expectNZNumber(n)) != ACCEPT) {
+ gotadotalready = true;
+ break;
+ }
+
+ f_in.section += ".";
+ BincStream nstr;
+ nstr << n;
+ f_in.section += nstr.str();
+ }
+
+ if (gotadotalready || (res = expectThisString(".")) == ACCEPT) {
+ if ((res = expectThisString("MIME")) == ACCEPT) {
+ f_in.sectiontext = "MIME";
+ } else if ((res = expectSectionText(f_in)) != ACCEPT) {
+ session.setLastError("Expected MIME or section_text");
+ return res;
+ }
+ }
+ }
+ }
+
+ if ((res = expectThisString("]")) != ACCEPT) {
+ session.setLastError("Expected ]");
+ return res;
+ }
+
+ return ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectHeaderList(BincImapParserFetchAtt &f_in) const
+{
+ Session &session = Session::getInstance();
+
+ Operator::ParseResult res;
+ if ((res = expectThisString("(")) != ACCEPT)
+ return REJECT;
+
+ string header_fld_name;
+ while (1) {
+ if ((res = expectAstring(header_fld_name)) != ACCEPT) {
+ session.setLastError("Expected header_fld_name");
+ return res;
+ }
+
+ f_in.headerlist.push_back(header_fld_name);
+
+ if ((res = expectSPACE()) == ACCEPT) continue;
+ else break;
+ }
+
+ if ((res = expectThisString(")")) != ACCEPT) {
+ session.setLastError("Expected )");
+ return res;
+ }
+
+ return ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectOffset(BincImapParserFetchAtt &f_in) const
+{
+ Session &session = Session::getInstance();
+ Operator::ParseResult res;
+
+ if ((res = expectThisString("<")) != ACCEPT) return REJECT;
+
+ unsigned int i;
+ if ((res = expectNumber(i)) != ACCEPT) {
+ session.setLastError("Expected number");
+ return res;
+ }
+
+ if ((res = expectThisString(".")) != ACCEPT) {
+ session.setLastError("Expected .");
+ return res;
+ }
+
+ unsigned int j;
+ if ((res = expectNZNumber(j)) != ACCEPT) {
+ session.setLastError("expected nz_number");
+ return res;
+ }
+
+ if ((res = expectThisString(">")) != ACCEPT) {
+ session.setLastError("Expected >");
+ return res;
+ }
+
+ f_in.offsetstart = i;
+ f_in.offsetlength = j;
+ return ACCEPT;
+}
+
+//----------------------------------------------------------------------
+Operator::ParseResult
+FetchOperator::expectFetchAtt(BincImapParserFetchAtt &f_in) const
+{
+ Operator::ParseResult res;
+
+ Session &session = Session::getInstance();
+
+ if ((res = expectThisString("ENVELOPE")) == ACCEPT) f_in.type = "ENVELOPE";
+ else if ((res = expectThisString("FLAGS")) == ACCEPT) f_in.type = "FLAGS";
+ else if ((res = expectThisString("INTERNALDATE")) == ACCEPT)
+ f_in.type = "INTERNALDATE";
+ else if ((res = expectThisString("UID")) == ACCEPT) f_in.type = "UID";
+ else if ((res = expectThisString("RFC822")) == ACCEPT) {
+ f_in.type = "RFC822";
+ if ((res = expectThisString(".HEADER")) == ACCEPT) f_in.type += ".HEADER";
+ else if ((res = expectThisString(".SIZE")) == ACCEPT) f_in.type += ".SIZE";
+ else if ((res = expectThisString(".TEXT")) == ACCEPT) f_in.type += ".TEXT";
+ else if ((res = expectThisString(".")) == ACCEPT) {
+ session.setLastError("Expected RFC822, RFC822.HEADER,"
+ " RFC822.SIZE or RFC822.TEXT");
+ return ERROR;
+ }
+
+ } else if ((res = expectThisString("BODY")) == ACCEPT) {
+ f_in.type = "BODY";
+
+ if ((res = expectThisString("STRUCTURE")) == ACCEPT) f_in.type += "STRUCTURE";
+ else if ((res = expectThisString(".PEEK")) == ACCEPT) f_in.type += ".PEEK";
+
+ if ((res = expectSection(f_in)) != ACCEPT)
+ f_in.hassection = false;
+ else {
+ f_in.hassection = true;
+ if ((res = expectOffset(f_in)) == ERROR) return ERROR;
+ }
+ } else
+ return REJECT;
+
+ return ACCEPT;
+}