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