bincimap

Log | Files | Refs | LICENSE

operator-append.cc (8594B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    operator-append.cc
      5  *  
      6  *  Description:
      7  *    Implementation of the APPEND command.
      8  *
      9  *  Authors:
     10  *    Andreas Aardal Hanssen <andreas-binc curly bincimap spot org>
     11  *
     12  *  Bugs:
     13  *
     14  *  ChangeLog:
     15  *
     16  *  --------------------------------------------------------------------
     17  *  Copyright 2002-2005 Andreas Aardal Hanssen
     18  *
     19  *  This program is free software; you can redistribute it and/or modify
     20  *  it under the terms of the GNU General Public License as published by
     21  *  the Free Software Foundation; either version 2 of the License, or
     22  *  (at your option) any later version.
     23  *
     24  *  This program is distributed in the hope that it will be useful,
     25  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     26  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     27  *  GNU General Public License for more details.
     28  *
     29  *  You should have received a copy of the GNU General Public License
     30  *  along with this program; if not, write to the Free Software
     31  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
     32  *  --------------------------------------------------------------------
     33  */
     34 #ifdef HAVE_CONFIG_H
     35 #include <config.h>
     36 #endif
     37 
     38 #include <algorithm>
     39 #include <string>
     40 
     41 #include <fcntl.h>
     42 
     43 #include "depot.h"
     44 #include "io.h"
     45 #include "mailbox.h"
     46 #include "operators.h"
     47 #include "recursivedescent.h"
     48 #include "pendingupdates.h"
     49 #include "session.h"
     50 
     51 using namespace ::std;
     52 using namespace Binc;
     53 
     54 //----------------------------------------------------------------------
     55 AppendOperator::AppendOperator(void)
     56 {
     57 }
     58 
     59 //----------------------------------------------------------------------
     60 AppendOperator::~AppendOperator(void)
     61 {
     62 }
     63 
     64 //----------------------------------------------------------------------
     65 const string AppendOperator::getName(void) const
     66 {
     67   return "APPEND";
     68 }
     69 
     70 //----------------------------------------------------------------------
     71 int AppendOperator::getState(void) const
     72 {
     73   return Session::AUTHENTICATED | Session::SELECTED;
     74 }
     75 
     76 //------------------------------------------------------------------------
     77 Operator::ProcessResult AppendOperator::process(Depot &depot,
     78 						Request &command)
     79 {
     80   IO &com = IOFactory::getInstance().get(1);
     81 
     82   Session &session = Session::getInstance();
     83 
     84   const string &srcmailbox = command.getMailbox();
     85   const string &canonmailbox = toCanonMailbox(srcmailbox);
     86   Mailbox *mailbox = 0;
     87 
     88   if ((mailbox = depot.get(canonmailbox)) == 0) {
     89     session.setResponseCode("TRYCREATE");
     90     session.setLastError("invalid destination mailbox "
     91 			 + toImapString(srcmailbox));
     92     return NO;
     93   }
     94 
     95   // mask all passed flags together
     96   unsigned int newflags = (unsigned int) Message::F_NONE;
     97   vector<string>::const_iterator f_i = command.flags.begin();
     98   while (f_i != command.flags.end()) {
     99     if (*f_i == "\\Deleted") newflags |= Message::F_DELETED;
    100     if (*f_i == "\\Answered") newflags |= Message::F_ANSWERED;
    101     if (*f_i == "\\Seen") newflags |= Message::F_SEEN;
    102     if (*f_i == "\\Draft") newflags |= Message::F_DRAFT;
    103     if (*f_i == "\\Flagged") newflags |= Message::F_FLAGGED;
    104     ++f_i;
    105   }
    106   
    107   int mday, year, hour, minute, second;
    108   char month[4];
    109 
    110   struct tm mytm;
    111   if (command.getDate() != "") {  
    112     sscanf(command.getDate().c_str(), "%2i-%3s-%4i %2i:%2i:%2i",
    113 	   &mday, month, &year, &hour, &minute, &second);
    114 
    115     month[3] = '\0';
    116     string monthstr = month;
    117     lowercase(monthstr);
    118     mytm.tm_sec = second;
    119     mytm.tm_min = minute;
    120     mytm.tm_hour = hour;
    121     mytm.tm_year = year - 1900;
    122     mytm.tm_mday = mday;
    123     if (monthstr == "jan") mytm.tm_mon = 0;
    124     else if (monthstr == "feb") mytm.tm_mon = 1;
    125     else if (monthstr == "mar") mytm.tm_mon = 2;
    126     else if (monthstr == "apr") mytm.tm_mon = 3;
    127     else if (monthstr == "may") mytm.tm_mon = 4;
    128     else if (monthstr == "jun") mytm.tm_mon = 5;
    129     else if (monthstr == "jul") mytm.tm_mon = 6;
    130     else if (monthstr == "aug") mytm.tm_mon = 7;
    131     else if (monthstr == "sep") mytm.tm_mon = 8;
    132     else if (monthstr == "oct") mytm.tm_mon = 9;
    133     else if (monthstr == "nov") mytm.tm_mon = 10;
    134     else if (monthstr == "dec") mytm.tm_mon = 11;
    135     mytm.tm_isdst = -1;
    136   }
    137 
    138   // Read number of characters in literal. Literal is required here.
    139   if (com.readChar() != '{') {
    140     session.setLastError("expected literal");
    141     return BAD;
    142   }
    143   
    144   string nr;
    145   while (1) {
    146     int c = com.readChar();
    147     if (c == -1) {
    148       session.setLastError("unexcepted EOF");
    149       return BAD;
    150     }
    151 
    152     if (c == '}')
    153       break;
    154     nr += (char) c;
    155   }
    156 
    157   int nchars = atoi(nr.c_str());
    158   if (nchars < 0) {
    159     session.setLastError("expected positive size of appended message");
    160     return BAD;
    161   }
    162 
    163   if (com.readChar() != '\r') {
    164     session.setLastError("expected CR");
    165     return BAD;
    166   }
    167 
    168   if (com.readChar() != '\n') {
    169     session.setLastError("expected LF");
    170     return BAD;
    171   }
    172 
    173   time_t newtime = (command.getDate() != "") ? mktime(&mytm) : time(0);
    174   if (newtime == -1) newtime = time(0);
    175   Message *dest = mailbox->createMessage(depot.mailboxToFilename(canonmailbox),
    176 					 newtime);
    177   if (!dest) {
    178     session.setLastError(mailbox->getLastError());
    179     return NO;
    180   }
    181 
    182   com << "+ go ahead with " << nchars << " characters" << endl;
    183   com.flushContent();
    184   com.disableInputLimit();
    185 
    186   while (nchars > 0) {
    187     // Read in chunks of 8192, followed by an optional chunk at the
    188     // end which is < 8192 bytes.
    189     string s;
    190     int bytesToRead = nchars > 8192 ? 8192 : nchars;
    191     int readBytes = com.readStr(s, bytesToRead);
    192     if (readBytes <= 0) {
    193       mailbox->rollBackNewMessages();
    194       session.setLastError(com.getLastError());
    195       return NO;
    196     }
    197 
    198     // Expect the exact number of bytes from readStr.
    199     if (readBytes != bytesToRead) {
    200       mailbox->rollBackNewMessages();
    201       session.setLastError("expected " + toString(nchars)
    202 			   + " bytes, but got " + toString(readBytes));
    203       return NO;
    204     }
    205 
    206     // Write the chunk to the message.
    207     if (!dest->appendChunk(s)) {
    208       mailbox->rollBackNewMessages();
    209       session.setLastError(dest->getLastError());
    210       return NO;
    211     }
    212 
    213     // Update the message count.
    214     nchars -= readBytes;
    215   }
    216 
    217   // Read the trailing CRLF after the message data.
    218   if (com.readChar() != '\r') {
    219     mailbox->rollBackNewMessages();
    220     session.setLastError("expected CR");
    221     return BAD;
    222   }
    223 
    224   if (com.readChar() != '\n') {
    225     mailbox->rollBackNewMessages();
    226     session.setLastError("expected LF");
    227     return BAD;
    228   }
    229 
    230   // Commit the message.
    231   dest->close();
    232   dest->setStdFlag(newflags);
    233   dest->setInternalDate(mktime(&mytm));
    234 
    235   if (!mailbox->commitNewMessages(depot.mailboxToFilename(canonmailbox))) {
    236     session.setLastError("failed to commit after successful APPEND: "
    237 			 + mailbox->getLastError());
    238     return NO;
    239   }
    240 
    241   if (mailbox == depot.getSelected()) {
    242     pendingUpdates(mailbox, PendingUpdates::EXISTS
    243 		   | PendingUpdates::RECENT 
    244 		   | PendingUpdates::FLAGS, true, false, true);
    245   }
    246 
    247   return OK;
    248 }
    249 
    250 //----------------------------------------------------------------------
    251 Operator::ParseResult AppendOperator::parse(Request &c_in) const
    252 {
    253   Session &session = Session::getInstance();
    254   Operator::ParseResult res;
    255 
    256   if (c_in.getUidMode())
    257     return REJECT;
    258 
    259   if ((res = expectSPACE()) != ACCEPT) {
    260     session.setLastError("Expected SPACE after APPEND");
    261     return res;
    262   }
    263 
    264   string mailbox;
    265   if ((res = expectMailbox(mailbox)) != ACCEPT) {
    266     session.setLastError("Expected mailbox after APPEND SPACE");
    267     return res;
    268   }
    269 
    270   c_in.setMailbox(mailbox);
    271 
    272   if ((res = expectSPACE()) != ACCEPT) {
    273     session.setLastError("Expected SPACE after APPEND SPACE mailbox");
    274     return res;
    275   }
    276 
    277   if ((res = expectThisString("(")) == ACCEPT) {
    278     if ((res = expectFlag(c_in.getFlags())) == ACCEPT)
    279       while (1) {
    280 	if ((res = expectSPACE()) != ACCEPT)
    281 	  break;
    282 	if ((res = expectFlag(c_in.getFlags())) != ACCEPT) {
    283 	  session.setLastError("expected a flag after the '('");
    284 	  return res;
    285 	}
    286       }
    287 
    288     if ((res = expectThisString(")")) != ACCEPT) {
    289       session.setLastError("expected a ')'");
    290       return res;
    291     }
    292 
    293     if ((res = expectSPACE()) != ACCEPT) {
    294       session.setLastError("expected a SPACE after the flag list");
    295       return res;
    296     }
    297   }
    298 
    299   string date;
    300   if ((res = expectDateTime(date)) == ACCEPT)
    301     if ((res = expectSPACE()) != ACCEPT) {
    302       session.setLastError("expected a SPACE after date_time");
    303       return res;
    304     }
    305 
    306   c_in.setDate(date);
    307   c_in.setName("APPEND");
    308   return ACCEPT;
    309 }