stable now but there are GIANT PIECES MISSING
[citadel.git] / citadel / modules / imap / imap_store.c
1 /*
2  * Implements the STORE command in IMAP.
3  *
4  * Copyright (c) 2001-2009 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "ctdl_module.h"
22
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <signal.h>
28 #include <pwd.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <time.h>
32 #include <sys/wait.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <limits.h>
36 #include <libcitadel.h>
37 #include "citadel.h"
38 #include "server.h"
39 #include "sysdep_decls.h"
40 #include "citserver.h"
41 #include "support.h"
42 #include "config.h"
43 #include "user_ops.h"
44 #include "database.h"
45 #include "room_ops.h"
46 #include "msgbase.h"
47 #include "internet_addressing.h"
48 #include "serv_imap.h"
49 #include "imap_tools.h"
50 #include "imap_fetch.h"
51 #include "imap_store.h"
52 #include "genstamp.h"
53
54
55 /*
56  * imap_do_store() calls imap_do_store_msg() to tweak the settings of
57  * an individual message.
58  *
59  * We also implement the ".SILENT" protocol option here.  :(
60  */
61 void imap_do_store_msg(int seq, const char *oper, unsigned int bits_to_twiddle) {
62         citimap *Imap = IMAP;
63
64         if (!strncasecmp(oper, "FLAGS", 5)) {
65                 Imap->flags[seq] &= IMAP_MASK_SYSTEM;
66                 Imap->flags[seq] |= bits_to_twiddle;
67         }
68         else if (!strncasecmp(oper, "+FLAGS", 6)) {
69                 Imap->flags[seq] |= bits_to_twiddle;
70         }
71         else if (!strncasecmp(oper, "-FLAGS", 6)) {
72                 Imap->flags[seq] &= (~bits_to_twiddle);
73         }
74 }
75
76
77 /*
78  * imap_store() calls imap_do_store() to perform the actual bit twiddling
79  * on the flags.
80  */
81 void imap_do_store(citimap_command *Cmd) {
82         int i, j;
83         unsigned int bits_to_twiddle = 0;
84         const char *oper;
85         char flag[32];
86         char whichflags[256];
87         char num_flags;
88         int silent = 0;
89         long *ss_msglist;
90         int num_ss = 0;
91         int last_item_twiddled = (-1);
92         citimap *Imap = IMAP;
93
94         if (Cmd->num_parms < 2) return;
95         oper = Cmd->Params[0].Key;
96         if (cbmstrcasestr(oper, ".SILENT")) {
97                 silent = 1;
98         }
99
100         /*
101          * ss_msglist is an array of message numbers to manipulate.  We
102          * are going to supply this array to CtdlSetSeen() later.
103          */
104         ss_msglist = malloc(Imap->num_msgs * sizeof(long));
105         if (ss_msglist == NULL) return;
106
107         /*
108          * Ok, go ahead and parse the flags.
109          */
110         for (i=1; i<Cmd->num_parms; ++i) {///TODO: why strcpy? 
111                 strcpy(whichflags, Cmd->Params[i].Key);
112                 if (whichflags[0]=='(') {
113                         safestrncpy(whichflags, &whichflags[1], 
114                                 sizeof whichflags);
115                 }
116                 if (whichflags[strlen(whichflags)-1]==')') {
117                         whichflags[strlen(whichflags)-1]=0;
118                 }
119                 striplt(whichflags);
120
121                 /* A client might twiddle more than one bit at a time.
122                  * Note that we check for the flag names without the leading
123                  * backslash because imap_parameterize() strips them out.
124                  */
125                 num_flags = num_tokens(whichflags, ' ');
126                 for (j=0; j<num_flags; ++j) {
127                         extract_token(flag, whichflags, j, ' ', sizeof flag);
128
129                         if ((!strcasecmp(flag, "\\Deleted"))
130                            || (!strcasecmp(flag, "Deleted"))) {
131                                 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom()) {
132                                         bits_to_twiddle |= IMAP_DELETED;
133                                 }
134                         }
135                         if ((!strcasecmp(flag, "\\Seen"))
136                            || (!strcasecmp(flag, "Seen"))) {
137                                 bits_to_twiddle |= IMAP_SEEN;
138                         }
139                         if ((!strcasecmp(flag, "\\Answered")) 
140                            || (!strcasecmp(flag, "Answered"))) {
141                                 bits_to_twiddle |= IMAP_ANSWERED;
142                         }
143                 }
144         }
145
146         if (Imap->num_msgs > 0) {
147                 for (i = 0; i < Imap->num_msgs; ++i) {
148                         if (Imap->flags[i] & IMAP_SELECTED) {
149                                 last_item_twiddled = i;
150
151                                 ss_msglist[num_ss++] = Imap->msgids[i];
152                                 imap_do_store_msg(i, oper, bits_to_twiddle);
153
154                                 if (!silent) {
155                                         IAPrintf("* %d FETCH (", i+1);
156                                         imap_fetch_flags(i);
157                                         IAPuts(")\r\n");
158                                 }
159
160                         }
161                 }
162         }
163
164         /*
165          * Now manipulate the database -- all in one shot.
166          */
167         if ( (last_item_twiddled >= 0) && (num_ss > 0) ) {
168
169                 if (bits_to_twiddle & IMAP_SEEN) {
170                         CtdlSetSeen(ss_msglist, num_ss,
171                                 ((Imap->flags[last_item_twiddled] & IMAP_SEEN) ? 1 : 0),
172                                 ctdlsetseen_seen,
173                                 NULL, NULL
174                         );
175                 }
176
177                 if (bits_to_twiddle & IMAP_ANSWERED) {
178                         CtdlSetSeen(ss_msglist, num_ss,
179                                 ((Imap->flags[last_item_twiddled] & IMAP_ANSWERED) ? 1 : 0),
180                                 ctdlsetseen_answered,
181                                 NULL, NULL
182                         );
183                 }
184
185         }
186
187         free(ss_msglist);
188         imap_do_expunge();              // Citadel always expunges immediately.
189         imap_rescan_msgids();
190 }
191
192
193 /*
194  * This function is called by the main command loop.
195  */
196 void imap_store(int num_parms, ConstStr *Params) {
197         citimap_command Cmd;
198         int num_items;
199
200         if (num_parms < 3) {
201                 IReply("BAD invalid parameters");
202                 return;
203         }
204
205         if (imap_is_message_set(Params[2].Key)) {
206                 imap_pick_range(Params[2].Key, 0);
207         }
208         else {
209                 IReply("BAD invalid parameters");
210                 return;
211         }
212
213         memset(&Cmd, 0, sizeof(citimap_command));
214         Cmd.CmdBuf = NewStrBufPlain(NULL, StrLength(IMAP->Cmd.CmdBuf));
215         MakeStringOf(Cmd.CmdBuf, 3);
216
217         num_items = imap_extract_data_items(&Cmd);
218         if (num_items < 1) {
219                 IReply("BAD invalid data item list");
220                 FreeStrBuf(&Cmd.CmdBuf);
221                 free(Cmd.Params);
222                 return;
223         }
224
225         imap_do_store(&Cmd);
226         IReply("OK STORE completed");
227         FreeStrBuf(&Cmd.CmdBuf);
228         free(Cmd.Params);
229 }
230
231 /*
232  * This function is called by the main command loop.
233  */
234 void imap_uidstore(int num_parms, ConstStr *Params) {
235         citimap_command Cmd;
236         int num_items;
237
238         if (num_parms < 4) {
239                 IReply("BAD invalid parameters");
240                 return;
241         }
242
243         if (imap_is_message_set(Params[3].Key)) {
244                 imap_pick_range(Params[3].Key, 1);
245         }
246         else {
247                 IReply("BAD invalid parameters");
248                 return;
249         }
250
251         memset(&Cmd, 0, sizeof(citimap_command));
252         Cmd.CmdBuf = NewStrBufPlain(NULL, StrLength(IMAP->Cmd.CmdBuf));
253         MakeStringOf(Cmd.CmdBuf, 4);
254
255         num_items = imap_extract_data_items(&Cmd);
256         if (num_items < 1) {
257                 IReply("BAD invalid data item list");
258                 FreeStrBuf(&Cmd.CmdBuf);
259                 free(Cmd.Params);
260                 return;
261         }
262
263         imap_do_store(&Cmd);
264         IReply("OK UID STORE completed");
265         FreeStrBuf(&Cmd.CmdBuf);
266         free(Cmd.Params);
267 }
268
269