HUGE PATCH. This moves all of mime_parser.c and all
[citadel.git] / citadel / modules / imap / imap_store.c
1 /*
2  * $Id$
3  *
4  * Implements the STORE command in IMAP.
5  *
6  */
7
8
9 #include "sysdep.h"
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <fcntl.h>
14 #include <signal.h>
15 #include <pwd.h>
16 #include <errno.h>
17 #include <sys/types.h>
18
19 #if TIME_WITH_SYS_TIME
20 # include <sys/time.h>
21 # include <time.h>
22 #else
23 # if HAVE_SYS_TIME_H
24 #  include <sys/time.h>
25 # else
26 #  include <time.h>
27 # endif
28 #endif
29
30 #include <sys/wait.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <limits.h>
34 #include <libcitadel.h>
35 #include "citadel.h"
36 #include "server.h"
37 #include "sysdep_decls.h"
38 #include "citserver.h"
39 #include "support.h"
40 #include "config.h"
41 #include "room_ops.h"
42 #include "user_ops.h"
43 #include "policy.h"
44 #include "database.h"
45 #include "msgbase.h"
46 #include "internet_addressing.h"
47 #include "serv_imap.h"
48 #include "imap_tools.h"
49 #include "imap_fetch.h"
50 #include "imap_store.h"
51 #include "genstamp.h"
52
53
54
55
56
57
58 /*
59  * imap_do_store() calls imap_do_store_msg() to tweak the settings of
60  * an individual message.
61  *
62  * We also implement the ".SILENT" protocol option here.  :(
63  */
64 void imap_do_store_msg(int seq, char *oper, unsigned int bits_to_twiddle) {
65
66
67         if (!strncasecmp(oper, "FLAGS", 5)) {
68                 IMAP->flags[seq] &= IMAP_MASK_SYSTEM;
69                 IMAP->flags[seq] |= bits_to_twiddle;
70         }
71         else if (!strncasecmp(oper, "+FLAGS", 6)) {
72                 IMAP->flags[seq] |= bits_to_twiddle;
73         }
74         else if (!strncasecmp(oper, "-FLAGS", 6)) {
75                 IMAP->flags[seq] &= (~bits_to_twiddle);
76         }
77 }
78
79
80
81 /*
82  * imap_store() calls imap_do_store() to perform the actual bit twiddling
83  * on the flags.
84  */
85 void imap_do_store(int num_items, char **itemlist) {
86         int i, j;
87         unsigned int bits_to_twiddle = 0;
88         char *oper;
89         char flag[32];
90         char whichflags[256];
91         char num_flags;
92         int silent = 0;
93         long *ss_msglist;
94         int num_ss = 0;
95         int last_item_twiddled = (-1);
96
97         if (num_items < 2) return;
98         oper = itemlist[0];
99         if (bmstrcasestr(oper, ".SILENT")) {
100                 silent = 1;
101         }
102
103         /*
104          * ss_msglist is an array of message numbers to manipulate.  We
105          * are going to supply this array to CtdlSetSeen() later.
106          */
107         ss_msglist = malloc(IMAP->num_msgs * sizeof(long));
108         if (ss_msglist == NULL) return;
109
110         /*
111          * Ok, go ahead and parse the flags.
112          */
113         for (i=1; i<num_items; ++i) {
114                 strcpy(whichflags, itemlist[i]);
115                 if (whichflags[0]=='(') {
116                         safestrncpy(whichflags, &whichflags[1], 
117                                 sizeof whichflags);
118                 }
119                 if (whichflags[strlen(whichflags)-1]==')') {
120                         whichflags[strlen(whichflags)-1]=0;
121                 }
122                 striplt(whichflags);
123
124                 /* A client might twiddle more than one bit at a time.
125                  * Note that we check for the flag names without the leading
126                  * backslash because imap_parameterize() strips them out.
127                  */
128                 num_flags = num_tokens(whichflags, ' ');
129                 for (j=0; j<num_flags; ++j) {
130                         extract_token(flag, whichflags, j, ' ', sizeof flag);
131
132                         if ((!strcasecmp(flag, "\\Deleted"))
133                            || (!strcasecmp(flag, "Deleted"))) {
134                                 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom()) {
135                                         bits_to_twiddle |= IMAP_DELETED;
136                                 }
137                         }
138                         if ((!strcasecmp(flag, "\\Seen"))
139                            || (!strcasecmp(flag, "Seen"))) {
140                                 bits_to_twiddle |= IMAP_SEEN;
141                         }
142                         if ((!strcasecmp(flag, "\\Answered")) 
143                            || (!strcasecmp(flag, "Answered"))) {
144                                 bits_to_twiddle |= IMAP_ANSWERED;
145                         }
146                 }
147         }
148
149         if (IMAP->num_msgs > 0) {
150                 for (i = 0; i < IMAP->num_msgs; ++i) {
151                         if (IMAP->flags[i] & IMAP_SELECTED) {
152                                 last_item_twiddled = i;
153
154                                 ss_msglist[num_ss++] = IMAP->msgids[i];
155                                 imap_do_store_msg(i, oper, bits_to_twiddle);
156
157                                 if (!silent) {
158                                         cprintf("* %d FETCH (", i+1);
159                                         imap_fetch_flags(i);
160                                         cprintf(")\r\n");
161                                 }
162
163                         }
164                 }
165         }
166
167         /*
168          * Now manipulate the database -- all in one shot.
169          */
170         if ( (last_item_twiddled >= 0) && (num_ss > 0) ) {
171
172                 if (bits_to_twiddle & IMAP_SEEN) {
173                         CtdlSetSeen(ss_msglist, num_ss,
174                                 ((IMAP->flags[last_item_twiddled] & IMAP_SEEN) ? 1 : 0),
175                                 ctdlsetseen_seen,
176                                 NULL, NULL
177                         );
178                 }
179
180                 if (bits_to_twiddle & IMAP_ANSWERED) {
181                         CtdlSetSeen(ss_msglist, num_ss,
182                                 ((IMAP->flags[last_item_twiddled] & IMAP_ANSWERED) ? 1 : 0),
183                                 ctdlsetseen_answered,
184                                 NULL, NULL
185                         );
186                 }
187
188         }
189
190         free(ss_msglist);
191
192         /*
193          * The following two commands implement "instant expunge" if enabled.
194          */
195         if (config.c_instant_expunge) {
196                 imap_do_expunge();
197                 imap_rescan_msgids();
198         }
199
200 }
201
202
203 /*
204  * This function is called by the main command loop.
205  */
206 void imap_store(int num_parms, char *parms[]) {
207         char items[1024];
208         char *itemlist[256];
209         int num_items;
210         int i;
211
212         if (num_parms < 3) {
213                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
214                 return;
215         }
216
217         if (imap_is_message_set(parms[2])) {
218                 imap_pick_range(parms[2], 0);
219         }
220         else {
221                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
222                 return;
223         }
224
225         strcpy(items, "");
226         for (i=3; i<num_parms; ++i) {
227                 strcat(items, parms[i]);
228                 if (i < (num_parms-1)) strcat(items, " ");
229         }
230
231         num_items = imap_extract_data_items(itemlist, items);
232         if (num_items < 1) {
233                 cprintf("%s BAD invalid data item list\r\n", parms[0]);
234                 return;
235         }
236
237         imap_do_store(num_items, itemlist);
238         cprintf("%s OK STORE completed\r\n", parms[0]);
239 }
240
241 /*
242  * This function is called by the main command loop.
243  */
244 void imap_uidstore(int num_parms, char *parms[]) {
245         char items[1024];
246         char *itemlist[256];
247         int num_items;
248         int i;
249
250         if (num_parms < 4) {
251                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
252                 return;
253         }
254
255         if (imap_is_message_set(parms[3])) {
256                 imap_pick_range(parms[3], 1);
257         }
258         else {
259                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
260                 return;
261         }
262
263         strcpy(items, "");
264         for (i=4; i<num_parms; ++i) {
265                 strcat(items, parms[i]);
266                 if (i < (num_parms-1)) strcat(items, " ");
267         }
268
269         num_items = imap_extract_data_items(itemlist, items);
270         if (num_items < 1) {
271                 cprintf("%s BAD invalid data item list\r\n", parms[0]);
272                 return;
273         }
274
275         imap_do_store(num_items, itemlist);
276         cprintf("%s OK UID STORE completed\r\n", parms[0]);
277 }
278
279