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