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