* Support IMAP \Answered flag
[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.  Leave it to an
64  * idiot like Mark Crispin to make things unnecessarily complicated.
65  */
66 void imap_do_store_msg(int seq, char *oper, unsigned int bits_to_twiddle) {
67         int silent = 0;
68
69         if (!strncasecmp(oper, "FLAGS", 5)) {
70                 IMAP->flags[seq] &= IMAP_MASK_SYSTEM;
71                 IMAP->flags[seq] |= bits_to_twiddle;
72                 silent = strncasecmp(&oper[5], ".SILENT", 7);
73         }
74         else if (!strncasecmp(oper, "+FLAGS", 6)) {
75                 IMAP->flags[seq] |= bits_to_twiddle;
76                 silent = strncasecmp(&oper[6], ".SILENT", 7);
77         }
78         else if (!strncasecmp(oper, "-FLAGS", 6)) {
79                 IMAP->flags[seq] &= (~bits_to_twiddle);
80                 silent = strncasecmp(&oper[6], ".SILENT", 7);
81         }
82
83         if (bits_to_twiddle & IMAP_SEEN) {
84                 CtdlSetSeen(IMAP->msgids[seq],
85                                 ((IMAP->flags[seq] & IMAP_SEEN) ? 1 : 0),
86                                 ctdlsetseen_seen
87                 );
88         }
89         if (bits_to_twiddle & IMAP_ANSWERED) {
90                 CtdlSetSeen(IMAP->msgids[seq],
91                                 ((IMAP->flags[seq] & IMAP_ANSWERED) ? 1 : 0),
92                                 ctdlsetseen_answered
93                 );
94         }
95
96         /* 'silent' is actually the value returned from a strncasecmp() so
97          * we want that option only if its value is zero.  Seems backwards
98          * but that's the way it's supposed to be.
99          */
100         if (silent) {
101                 cprintf("* %d FETCH (", seq+1);
102                 imap_fetch_flags(seq);
103                 cprintf(")\r\n");
104         }
105 }
106
107
108
109 /*
110  * imap_store() calls imap_do_store() to perform the actual bit twiddling
111  * on the flags.
112  */
113 void imap_do_store(int num_items, char **itemlist) {
114         int i, j;
115         unsigned int bits_to_twiddle = 0;
116         char *oper;
117         char flag[SIZ];
118         char whichflags[SIZ];
119         char num_flags;
120
121         if (num_items < 2) return;
122         oper = itemlist[0];
123
124         for (i=1; i<num_items; ++i) {
125                 strcpy(whichflags, itemlist[i]);
126                 if (whichflags[0]=='(') strcpy(whichflags, &whichflags[1]);
127                 if (whichflags[strlen(whichflags)-1]==')') {
128                         whichflags[strlen(whichflags)-1]=0;
129                 }
130                 striplt(whichflags);
131
132                 /* A client might twiddle more than one bit at a time */
133                 num_flags = num_tokens(whichflags, ' ');
134                 for (j=0; j<num_flags; ++j) {
135                         extract_token(flag, whichflags, j, ' ');
136
137                         if (!strcasecmp(flag, "\\Deleted")) {
138                                 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom()) {
139                                         bits_to_twiddle |= IMAP_DELETED;
140                                 }
141                         }
142                         if (!strcasecmp(flag, "\\Seen")) {
143                                 bits_to_twiddle |= IMAP_SEEN;
144                         }
145                         if (!strcasecmp(flag, "\\Answered")) {
146                                 bits_to_twiddle |= IMAP_ANSWERED;
147                         }
148                 }
149         }
150
151         if (IMAP->num_msgs > 0) {
152                 for (i = 0; i < IMAP->num_msgs; ++i) {
153                         if (IMAP->flags[i] & IMAP_SELECTED) {
154                                 imap_do_store_msg(i, oper, bits_to_twiddle);
155                         }
156                 }
157         }
158 }
159
160
161 /*
162  * This function is called by the main command loop.
163  */
164 void imap_store(int num_parms, char *parms[]) {
165         char items[1024];
166         char *itemlist[256];
167         int num_items;
168         int i;
169
170         if (num_parms < 3) {
171                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
172                 return;
173         }
174
175         if (imap_is_message_set(parms[2])) {
176                 imap_pick_range(parms[2], 0);
177         }
178         else {
179                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
180                 return;
181         }
182
183         strcpy(items, "");
184         for (i=3; i<num_parms; ++i) {
185                 strcat(items, parms[i]);
186                 if (i < (num_parms-1)) strcat(items, " ");
187         }
188
189         num_items = imap_extract_data_items(itemlist, items);
190         if (num_items < 1) {
191                 cprintf("%s BAD invalid data item list\r\n", parms[0]);
192                 return;
193         }
194
195         imap_do_store(num_items, itemlist);
196         cprintf("%s OK STORE completed\r\n", parms[0]);
197 }
198
199 /*
200  * This function is called by the main command loop.
201  */
202 void imap_uidstore(int num_parms, char *parms[]) {
203         char items[1024];
204         char *itemlist[256];
205         int num_items;
206         int i;
207
208         if (num_parms < 4) {
209                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
210                 return;
211         }
212
213         if (imap_is_message_set(parms[3])) {
214                 imap_pick_range(parms[3], 1);
215         }
216         else {
217                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
218                 return;
219         }
220
221         strcpy(items, "");
222         for (i=4; i<num_parms; ++i) {
223                 strcat(items, parms[i]);
224                 if (i < (num_parms-1)) strcat(items, " ");
225         }
226
227         num_items = imap_extract_data_items(itemlist, items);
228         if (num_items < 1) {
229                 cprintf("%s BAD invalid data item list\r\n", parms[0]);
230                 return;
231         }
232
233         imap_do_store(num_items, itemlist);
234         cprintf("%s OK UID STORE completed\r\n", parms[0]);
235 }
236
237