summaryrefslogtreecommitdiff
path: root/src/operator-append.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/operator-append.cc')
-rw-r--r--src/operator-append.cc297
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;
+}