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 //----------------------------------------------------------------------
AppendOperator(void)55 AppendOperator::AppendOperator(void)
56 {
57 }
58
59 //----------------------------------------------------------------------
~AppendOperator(void)60 AppendOperator::~AppendOperator(void)
61 {
62 }
63
64 //----------------------------------------------------------------------
getName(void) const65 const string AppendOperator::getName(void) const
66 {
67 return "APPEND";
68 }
69
70 //----------------------------------------------------------------------
getState(void) const71 int AppendOperator::getState(void) const
72 {
73 return Session::AUTHENTICATED | Session::SELECTED;
74 }
75
76 //------------------------------------------------------------------------
process(Depot & depot,Request & command)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 //----------------------------------------------------------------------
parse(Request & c_in) const251 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 }
310