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 }