diff options
Diffstat (limited to 'src/operator-append.cc')
-rw-r--r-- | src/operator-append.cc | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/src/operator-append.cc b/src/operator-append.cc new file mode 100644 index 0000000..d4650ee --- /dev/null +++ b/src/operator-append.cc @@ -0,0 +1,297 @@ +/** -------------------------------------------------------------------- + * @file operator-append.cc + * @brief Implementation of the APPEND command. + * @author Andreas Aardal Hanssen + * @date 2002-2005 + * ----------------------------------------------------------------- **/ +#include <algorithm> +#include <string> + +#include <fcntl.h> + +#include "depot.h" +#include "iodevice.h" +#include "iofactory.h" +#include "mailbox.h" +#include "operators.h" +#include "recursivedescent.h" +#include "pendingupdates.h" +#include "session.h" + +using namespace ::std; +using namespace Binc; + +//---------------------------------------------------------------------- +AppendOperator::AppendOperator(void) +{ +} + +//---------------------------------------------------------------------- +AppendOperator::~AppendOperator(void) +{ +} + +//---------------------------------------------------------------------- +const string AppendOperator::getName(void) const +{ + return "APPEND"; +} + +//---------------------------------------------------------------------- +int AppendOperator::getState(void) const +{ + return Session::AUTHENTICATED | Session::SELECTED; +} + +//------------------------------------------------------------------------ +Operator::ProcessResult AppendOperator::process(Depot &depot, + Request &command) +{ + Session &session = Session::getInstance(); + + const string &srcmailbox = command.getMailbox(); + const string &canonmailbox = toCanonMailbox(srcmailbox); + Mailbox *mailbox = 0; + + if ((mailbox = depot.get(canonmailbox)) == 0) { + session.setResponseCode("TRYCREATE"); + session.setLastError("invalid destination mailbox " + + toImapString(srcmailbox)); + return NO; + } + + // mask all passed flags together + unsigned int newflags = (unsigned int) Message::F_NONE; + vector<string>::const_iterator f_i = command.flags.begin(); + while (f_i != command.flags.end()) { + if (*f_i == "\\Deleted") newflags |= Message::F_DELETED; + if (*f_i == "\\Answered") newflags |= Message::F_ANSWERED; + if (*f_i == "\\Seen") newflags |= Message::F_SEEN; + if (*f_i == "\\Draft") newflags |= Message::F_DRAFT; + if (*f_i == "\\Flagged") newflags |= Message::F_FLAGGED; + ++f_i; + } + + int mday, year, hour, minute, second; + char month[4]; + + struct tm mytm; + if (command.getDate() != "") { + sscanf(command.getDate().c_str(), "%2i-%3s-%4i %2i:%2i:%2i", + &mday, month, &year, &hour, &minute, &second); + + month[3] = '\0'; + string monthstr = month; + lowercase(monthstr); + mytm.tm_sec = second; + mytm.tm_min = minute; + mytm.tm_hour = hour; + mytm.tm_year = year - 1900; + mytm.tm_mday = mday; + if (monthstr == "jan") mytm.tm_mon = 0; + else if (monthstr == "feb") mytm.tm_mon = 1; + else if (monthstr == "mar") mytm.tm_mon = 2; + else if (monthstr == "apr") mytm.tm_mon = 3; + else if (monthstr == "may") mytm.tm_mon = 4; + else if (monthstr == "jun") mytm.tm_mon = 5; + else if (monthstr == "jul") mytm.tm_mon = 6; + else if (monthstr == "aug") mytm.tm_mon = 7; + else if (monthstr == "sep") mytm.tm_mon = 8; + else if (monthstr == "oct") mytm.tm_mon = 9; + else if (monthstr == "nov") mytm.tm_mon = 10; + else if (monthstr == "dec") mytm.tm_mon = 11; + mytm.tm_isdst = -1; + } + + // Read number of characters in literal. Literal is required here. + char c; + if (!bincClient.readChar(&c)) return ABORT; + + if (c != '{') { + session.setLastError("expected literal"); + return BAD; + } + + string nr; + bool literalPlus = false; + while (1) { + if (!bincClient.readChar(&c)) { + session.setLastError("unexcepted EOF"); + return BAD; + } + + if (c == '}') break; + + // Support LITERAL+ + if (c == '+' && !literalPlus) { + literalPlus = true; + continue; + } + + if (!isdigit(c)) { + session.setLastError("unexcepted non-digit character"); + return BAD; + } + + if (literalPlus) { + session.setLastError("expected '}'"); + return BAD; + } + + nr += (char) c; + } + + int nchars = atoi(nr.c_str()); + if (nchars < 0) { + session.setLastError("expected positive size of appended message"); + return BAD; + } + + if (!bincClient.readChar(&c)) return ABORT; + + if (c != '\r') { + session.setLastError("expected CR"); + return BAD; + } + + if (!bincClient.readChar(&c)) return ABORT; + + if (c != '\n') { + session.setLastError("expected LF"); + return BAD; + } + + time_t newtime = (command.getDate() != "") ? mktime(&mytm) : time(0); + if (newtime == -1) newtime = time(0); + Message *dest = mailbox->createMessage(depot.mailboxToFilename(canonmailbox), + newtime); + if (!dest) { + session.setLastError(mailbox->getLastError()); + return NO; + } + + if (!literalPlus) { + bincClient << "+ go ahead with " << nchars << " characters" << endl; + bincClient.flush(); + } + + bincClient.clearFlags(IODevice::HasInputLimit); + + while (nchars > 0) { + // Read in chunks of 8192, followed by an optional chunk at the + // end which is < 8192 bytes. + string s; + int bytesToRead = nchars > 8192 ? 8192 : nchars; + if (!bincClient.readStr(&s, bytesToRead)) { + mailbox->rollBackNewMessages(); + session.setLastError(bincClient.getLastErrorString()); + return NO; + } + + // Write the chunk to the message. + if (!dest->appendChunk(s)) { + mailbox->rollBackNewMessages(); + session.setLastError(dest->getLastError()); + return NO; + } + + // Update the message count. + nchars -= s.size(); + } + + // Read the trailing CRLF after the message data. + if (!bincClient.readChar(&c)) return ABORT; + + if (c != '\r') { + mailbox->rollBackNewMessages(); + session.setLastError("expected CR"); + return BAD; + } + + if (!bincClient.readChar(&c)) return ABORT; + + if (c != '\n') { + mailbox->rollBackNewMessages(); + session.setLastError("expected LF"); + return BAD; + } + + // Commit the message. + dest->close(); + dest->setStdFlag(newflags); + dest->setInternalDate(mktime(&mytm)); + + if (!mailbox->commitNewMessages(depot.mailboxToFilename(canonmailbox))) { + session.setLastError("failed to commit after successful APPEND: " + + mailbox->getLastError()); + return NO; + } + + if (mailbox == depot.getSelected()) { + pendingUpdates(mailbox, PendingUpdates::EXISTS + | PendingUpdates::RECENT + | PendingUpdates::FLAGS, true, false, true); + } + + return OK; +} + +//---------------------------------------------------------------------- +Operator::ParseResult AppendOperator::parse(Request &c_in) const +{ + Session &session = Session::getInstance(); + Operator::ParseResult res; + + if (c_in.getUidMode()) + return REJECT; + + if ((res = expectSPACE()) != ACCEPT) { + session.setLastError("Expected SPACE after APPEND"); + return res; + } + + string mailbox; + if ((res = expectMailbox(mailbox)) != ACCEPT) { + session.setLastError("Expected mailbox after APPEND SPACE"); + return res; + } + + c_in.setMailbox(mailbox); + + if ((res = expectSPACE()) != ACCEPT) { + session.setLastError("Expected SPACE after APPEND SPACE mailbox"); + return res; + } + + if ((res = expectThisString("(")) == ACCEPT) { + if ((res = expectFlag(c_in.getFlags())) == ACCEPT) + while (1) { + if ((res = expectSPACE()) != ACCEPT) break; + if ((res = expectFlag(c_in.getFlags())) != ACCEPT) { + session.setLastError("expected a flag after the '('"); + return res; + } + } + + if ((res = expectThisString(")")) != ACCEPT) { + session.setLastError("expected a ')'"); + return res; + } + + if ((res = expectSPACE()) != ACCEPT) { + session.setLastError("expected a SPACE after the flag list"); + return res; + } + } + + string date; + if ((res = expectDateTime(date)) == ACCEPT) + if ((res = expectSPACE()) != ACCEPT) { + session.setLastError("expected a SPACE after date_time"); + return res; + } + + c_in.setDate(date); + c_in.setName("APPEND"); + return ACCEPT; +} |