diff options
author | Erwin Hoffmann <feh@fehcom.de> | 2023-09-21 17:36:16 +0200 |
---|---|---|
committer | Erwin Hoffmann <feh@fehcom.de> | 2023-09-21 17:36:16 +0200 |
commit | 44388ac49531af9e2565f76ef99ff7afb757b3fb (patch) | |
tree | 4eeb294db5bc3dbd075d0df5fea13c664cc331e2 /src/operator-fetch.cc | |
parent | 889d69a87d51c8df531885cf1ac3d12d64a0cff7 (diff) |
all sources
Diffstat (limited to 'src/operator-fetch.cc')
-rw-r--r-- | src/operator-fetch.cc | 641 |
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; +} |