]> code.citadel.org Git - citadel.git/blob - citadel/imap_search.c
8141ff7f18b4dd25c0253985a3c46ed3bf1b5837
[citadel.git] / citadel / imap_search.c
1 /*
2  * $Id$
3  *
4  * Implements the SEARCH command in IMAP.
5  * This command is way too convoluted.  Marc Crispin is a fscking idiot.
6  *
7  * NOTE: this is a partial implementation.  It is NOT FINISHED.
8  *
9  */
10
11
12 #include "sysdep.h"
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 #include <pwd.h>
19 #include <errno.h>
20 #include <sys/types.h>
21
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
24 # include <time.h>
25 #else
26 # if HAVE_SYS_TIME_H
27 #  include <sys/time.h>
28 # else
29 #  include <time.h>
30 # endif
31 #endif
32
33 #include <sys/wait.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <limits.h>
37 #include "citadel.h"
38 #include "server.h"
39 #include "sysdep_decls.h"
40 #include "citserver.h"
41 #include "support.h"
42 #include "config.h"
43 #include "serv_extensions.h"
44 #include "room_ops.h"
45 #include "user_ops.h"
46 #include "policy.h"
47 #include "database.h"
48 #include "msgbase.h"
49 #include "tools.h"
50 #include "internet_addressing.h"
51 #include "serv_imap.h"
52 #include "imap_tools.h"
53 #include "imap_fetch.h"
54 #include "imap_search.h"
55 #include "genstamp.h"
56
57
58
59 /*
60  * imap_do_search() calls imap_do_search_msg() to search an individual
61  * message after it has been fetched from the disk.  This function returns
62  * nonzero if there is a match.
63  */
64 int imap_do_search_msg(int seq, struct CtdlMessage *msg,
65                         int num_items, char **itemlist, int is_uid) {
66
67         int match = 0;
68         int is_not = 0;
69         int is_or = 0;
70         int pos = 0;
71         int i;
72
73         if (num_items == 0) return(0);
74
75         /* Initially we start at the beginning. */
76         pos = 0;
77
78         /* Check for the dreaded NOT criterion. */
79         if (!strcasecmp(itemlist[0], "NOT")) {
80                 is_not = 1;
81                 pos = 1;
82         }
83
84         /* Check for the dreaded OR criterion. */
85         if (!strcasecmp(itemlist[0], "OR")) {
86                 is_or = 1;
87                 pos = 1;
88         }
89
90         /* Now look for criteria. */
91         if (!strcasecmp(itemlist[pos], "ALL")) {
92                 match = 1;
93                 ++pos;
94         }
95         
96         else if (!strcasecmp(itemlist[pos], "ANSWERED")) {
97                 if (IMAP->flags[seq-1] & IMAP_ANSWERED) {
98                         match = 1;
99                 }
100                 ++pos;
101         }
102
103         else if (!strcasecmp(itemlist[pos], "BCC")) {
104                 /* FIXME */
105                 pos += 2;
106         }
107
108         else if (!strcasecmp(itemlist[pos], "BEFORE")) {
109                 if (msg->cm_fields['T'] != NULL) {
110                         if (imap_datecmp(itemlist[pos+1],
111                                         atol(msg->cm_fields['T'])) < 0) {
112                                 match = 1;
113                         }
114                 }
115                 pos += 2;
116         }
117
118         else if (!strcasecmp(itemlist[pos], "BODY")) {
119                 if (bmstrstr(msg->cm_fields['M'], itemlist[pos+1], strncasecmp)) {
120                         match = 1;
121                 }
122                 pos += 2;
123         }
124
125         else if (!strcasecmp(itemlist[pos], "CC")) {
126                 /* FIXME */
127                 pos += 2;
128         }
129
130         else if (!strcasecmp(itemlist[pos], "DELETED")) {
131                 if (IMAP->flags[seq-1] & IMAP_DELETED) {
132                         match = 1;
133                 }
134                 ++pos;
135         }
136
137         else if (!strcasecmp(itemlist[pos], "DRAFT")) {
138                 if (IMAP->flags[seq-1] & IMAP_DRAFT) {
139                         match = 1;
140                 }
141                 ++pos;
142         }
143
144         else if (!strcasecmp(itemlist[pos], "FLAGGED")) {
145                 if (IMAP->flags[seq-1] & IMAP_FLAGGED) {
146                         match = 1;
147                 }
148                 ++pos;
149         }
150
151         else if (!strcasecmp(itemlist[pos], "FROM")) {
152                 if (bmstrstr(msg->cm_fields['A'], itemlist[pos+1], strncasecmp)) {
153                         match = 1;
154                 }
155                 pos += 2;
156         }
157
158         else if (!strcasecmp(itemlist[pos], "HEADER")) {
159                 /* FIXME */
160                 pos += 3;       /* Yes, three */
161         }
162
163         else if (!strcasecmp(itemlist[pos], "KEYWORD")) {
164                 /* FIXME */
165                 pos += 2;
166         }
167
168         else if (!strcasecmp(itemlist[pos], "LARGER")) {
169                 if (strlen(msg->cm_fields['M']) > atoi(itemlist[pos+1])) {
170                         match = 1;
171                 }
172                 pos += 2;
173         }
174
175         else if (!strcasecmp(itemlist[pos], "NEW")) {
176                 /* FIXME */
177                 ++pos;
178         }
179
180         else if (!strcasecmp(itemlist[pos], "OLD")) {
181                 /* FIXME */
182                 ++pos;
183         }
184
185         else if (!strcasecmp(itemlist[pos], "ON")) {
186                 if (msg->cm_fields['T'] != NULL) {
187                         if (imap_datecmp(itemlist[pos+1],
188                                         atol(msg->cm_fields['T'])) == 0) {
189                                 match = 1;
190                         }
191                 }
192                 pos += 2;
193         }
194
195         else if (!strcasecmp(itemlist[pos], "RECENT")) {
196                 /* FIXME */
197                 ++pos;
198         }
199
200         else if (!strcasecmp(itemlist[pos], "SEEN")) {
201                 if (IMAP->flags[seq-1] & IMAP_SEEN) {
202                         match = 1;
203                 }
204                 ++pos;
205         }
206
207         else if (!strcasecmp(itemlist[pos], "SENTBEFORE")) {
208                 if (msg->cm_fields['T'] != NULL) {
209                         if (imap_datecmp(itemlist[pos+1],
210                                         atol(msg->cm_fields['T'])) < 0) {
211                                 match = 1;
212                         }
213                 }
214                 pos += 2;
215         }
216
217         else if (!strcasecmp(itemlist[pos], "SENTON")) {
218                 if (msg->cm_fields['T'] != NULL) {
219                         if (imap_datecmp(itemlist[pos+1],
220                                         atol(msg->cm_fields['T'])) == 0) {
221                                 match = 1;
222                         }
223                 }
224                 pos += 2;
225         }
226
227         else if (!strcasecmp(itemlist[pos], "SENTSINCE")) {
228                 if (msg->cm_fields['T'] != NULL) {
229                         if (imap_datecmp(itemlist[pos+1],
230                                         atol(msg->cm_fields['T'])) >= 0) {
231                                 match = 1;
232                         }
233                 }
234                 pos += 2;
235         }
236
237         else if (!strcasecmp(itemlist[pos], "SINCE")) {
238                 if (msg->cm_fields['T'] != NULL) {
239                         if (imap_datecmp(itemlist[pos+1],
240                                         atol(msg->cm_fields['T'])) >= 0) {
241                                 match = 1;
242                         }
243                 }
244                 pos += 2;
245         }
246
247         else if (!strcasecmp(itemlist[pos], "SMALLER")) {
248                 if (strlen(msg->cm_fields['M']) < atoi(itemlist[pos+1])) {
249                         match = 1;
250                 }
251                 pos += 2;
252         }
253
254         else if (!strcasecmp(itemlist[pos], "SUBJECT")) {
255                 if (bmstrstr(msg->cm_fields['U'], itemlist[pos+1], strncasecmp)) {
256                         match = 1;
257                 }
258                 pos += 2;
259         }
260
261         else if (!strcasecmp(itemlist[pos], "TEXT")) {
262                 for (i='A'; i<='Z'; ++i) {
263                         if (bmstrstr(msg->cm_fields[i], itemlist[pos+1], strncasecmp)) {
264                                 match = 1;
265                         }
266                 }
267                 pos += 2;
268         }
269
270         else if (!strcasecmp(itemlist[pos], "TO")) {
271                 if (bmstrstr(msg->cm_fields['R'], itemlist[pos+1], strncasecmp)) {
272                         match = 1;
273                 }
274                 pos += 2;
275         }
276
277         else if (!strcasecmp(itemlist[pos], "UID")) {
278                 if (is_msg_in_mset(itemlist[pos+1], IMAP->msgids[seq-1])) {
279                         match = 1;
280                 }
281                 pos += 2;
282         }
283
284         /* Now here come the 'UN' criteria.  Why oh why do we have to
285          * implement *both* the 'UN' criteria *and* the 'NOT' keyword?  Why
286          * can't there be *one* way to do things?  Answer: because Mark
287          * Crispin is an idiot.
288          */
289
290         else if (!strcasecmp(itemlist[pos], "UNANSWERED")) {
291                 if ((IMAP->flags[seq-1] & IMAP_ANSWERED) == 0) {
292                         match = 1;
293                 }
294                 ++pos;
295         }
296
297         else if (!strcasecmp(itemlist[pos], "UNDELETED")) {
298                 if ((IMAP->flags[seq-1] & IMAP_DELETED) == 0) {
299                         match = 1;
300                 }
301                 ++pos;
302         }
303
304         else if (!strcasecmp(itemlist[pos], "UNDRAFT")) {
305                 if ((IMAP->flags[seq-1] & IMAP_DRAFT) == 0) {
306                         match = 1;
307                 }
308                 ++pos;
309         }
310
311         else if (!strcasecmp(itemlist[pos], "UNFLAGGED")) {
312                 if ((IMAP->flags[seq-1] & IMAP_FLAGGED) == 0) {
313                         match = 1;
314                 }
315                 ++pos;
316         }
317
318         else if (!strcasecmp(itemlist[pos], "UNKEYWORD")) {
319                 /* FIXME */
320                 pos += 2;
321         }
322
323         else if (!strcasecmp(itemlist[pos], "UNSEEN")) {
324                 if ((IMAP->flags[seq-1] & IMAP_SEEN) == 0) {
325                         match = 1;
326                 }
327                 ++pos;
328         }
329
330         /* Remember to negate if we were told to */
331         if (is_not) {
332                 match = !match;
333         }
334
335         /* Keep going if there are more criteria! */
336         if (pos < num_items) {
337
338                 if (is_or) {
339                         match = (match || imap_do_search_msg(seq, msg,
340                                 num_items - pos, &itemlist[pos], is_uid));
341                 }
342                 else {
343                         match = (match && imap_do_search_msg(seq, msg,
344                                 num_items - pos, &itemlist[pos], is_uid));
345                 }
346
347         }
348
349         return(match);
350 }
351
352
353 /*
354  * imap_search() calls imap_do_search() to do its actual work, once it's
355  * validated and boiled down the request a bit.
356  */
357 void imap_do_search(int num_items, char **itemlist, int is_uid) {
358         int i;
359         struct CtdlMessage *msg;
360
361         cprintf("* SEARCH ");
362         if (IMAP->num_msgs > 0)
363          for (i = 0; i < IMAP->num_msgs; ++i)
364           if (IMAP->flags[i] && IMAP_SELECTED) {
365                 msg = CtdlFetchMessage(IMAP->msgids[i]);
366                 if (msg != NULL) {
367                         if (imap_do_search_msg(i+1, msg, num_items,
368                            itemlist, is_uid)) {
369                                 if (is_uid) {
370                                         cprintf("%ld ", IMAP->msgids[i]);
371                                 }
372                                 else {
373                                         cprintf("%d ", i+1);
374                                 }
375                         }
376                         CtdlFreeMessage(msg);
377                 }
378                 else {
379                         lprintf(1, "SEARCH internal error\n");
380                 }
381         }
382         cprintf("\r\n");
383 }
384
385
386 /*
387  * This function is called by the main command loop.
388  */
389 void imap_search(int num_parms, char *parms[]) {
390         int i;
391
392         if (num_parms < 3) {
393                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
394                 return;
395         }
396
397         for (i=1; i<num_parms; ++i) {
398                 if (imap_is_message_set(parms[i])) {
399                         imap_pick_range(parms[i], 0);
400                 }
401         }
402
403         imap_do_search(num_parms-2, &parms[2], 0);
404         cprintf("%s OK SEARCH completed\r\n", parms[0]);
405 }
406
407 /*
408  * This function is called by the main command loop.
409  */
410 void imap_uidsearch(int num_parms, char *parms[]) {
411         int i;
412
413         if (num_parms < 4) {
414                 cprintf("%s BAD invalid parameters\r\n", parms[0]);
415                 return;
416         }
417
418         for (i=1; i<num_parms; ++i) {
419                 if (imap_is_message_set(parms[i])) {
420                         imap_pick_range(parms[i], 1);
421                 }
422         }
423
424         imap_do_search(num_parms-3, &parms[3], 1);
425         cprintf("%s OK UID SEARCH completed\r\n", parms[0]);
426 }
427
428