/[imapfilter]/imapfilter/response.c
ViewVC logotype

Contents of /imapfilter/response.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.51 - (show annotations)
Sat Feb 14 22:48:04 2004 UTC (20 years, 1 month ago) by lefcha
Branch: MAIN
Changes since 1.50: +69 -11 lines
File MIME type: text/plain
Added IMAP4 protocol support.

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <strings.h>
5 #include <ctype.h>
6 #include <sys/types.h> /* For POSIX.1-2001 non-conformant systems. */
7 #include <regex.h>
8 #include <setjmp.h>
9
10 #include "config.h"
11 #include "imapfilter.h"
12 #include "buffer.h"
13
14
15 extern connection_t connpri, connaux;
16 extern jmp_buf acctloop;
17
18 buffer_t ibuf; /* Input buffer. */
19
20
21 void receive_response(connection_t * conn, char *buf);
22 void response_bye(char *buf);
23 int analyze_response(connection_t * conn, char *buf);
24
25
26 /*
27 * Read one packet of data that the server sent.
28 */
29 void
30 receive_response(connection_t * conn, char *buf)
31 {
32
33 if (socket_read(conn, buf) == ERROR_NETWORK)
34 longjmp(acctloop, -1);
35
36 debug("getting response (%s):\n\n%s\n",
37 (conn == &connpri ? "primary" : "auxiliary"), buf);
38 }
39
40
41 /*
42 * Get server response to client's request.
43 */
44 int
45 response_generic(connection_t * conn, unsigned int tag)
46 {
47
48 buffer_reset(&ibuf);
49
50 do {
51 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
52 receive_response(conn, ibuf.data + strlen(ibuf.data));
53 response_bye(ibuf.data);
54 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
55
56 return analyze_response(conn, ibuf.data);
57 }
58
59
60 /*
61 * Check if server sent a BYE response (connection is closed immediately).
62 */
63 void
64 response_bye(char *buf)
65 {
66
67 if (strcasestr(buf, "* BYE"))
68 longjmp(acctloop, -1);
69 }
70
71
72 /*
73 * Process the greeting that server sends during connection.
74 */
75 int
76 response_greeting(connection_t * conn)
77 {
78
79 buffer_reset(&ibuf);
80
81 receive_response(conn, ibuf.data);
82
83 verbose("%s: %s", (conn == &connpri ? "S" : "s"), ibuf);
84
85 response_bye(ibuf.data);
86
87 if (strcasestr(ibuf.data, "* PREAUTH"))
88 return RESPONSE_PREAUTH;
89
90 return RESPONSE_OK;
91 }
92
93
94 /*
95 * Process the data that server sent due to IMAP LOGOUT client request.
96 */
97 int
98 response_logout(connection_t * conn, unsigned int tag)
99 {
100
101 buffer_reset(&ibuf);
102
103 do {
104 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
105 receive_response(conn, ibuf.data + strlen(ibuf.data));
106 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
107
108 return analyze_response(conn, ibuf.data);
109 }
110
111
112 /*
113 * Process the data that server sent due to IMAP CAPABILITY client request.
114 */
115 int
116 response_capability(connection_t * conn, unsigned int tag)
117 {
118
119 buffer_reset(&ibuf);
120
121 do {
122 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
123 receive_response(conn, ibuf.data + strlen(ibuf.data));
124 response_bye(ibuf.data);
125 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
126
127 if (strcasestr(ibuf.data, "IMAP4rev1"))
128 conn->prot |= PROTOCOL_IMAP4REV1;
129 else if (strcasestr(ibuf.data, "IMAP4"))
130 conn->caps |= PROTOCOL_IMAP4;
131 else {
132 error("server supports neither the IMAP4rev1 nor the "
133 "IMAP4 protocol\n");
134 return -2;
135 }
136
137 if (strcasestr(ibuf.data, "NAMESPACE"))
138 conn->caps |= CAPABILITY_NAMESPACE;
139 #ifdef CRAM_MD5
140 if (strcasestr(ibuf.data, "AUTH=CRAM-MD5"))
141 conn->caps |= CAPABILITY_CRAMMD5;
142 #endif
143 #ifdef SSL_TLS
144 if (strcasestr(ibuf.data, "STARTTLS"))
145 conn->caps |= CAPABILITY_STARTTLS;
146 #endif
147 return analyze_response(conn, ibuf.data);
148 }
149
150
151 #ifdef CRAM_MD5
152 /*
153 * Process the data that server sent due to IMAP AUTHENTICATE client request.
154 */
155 int
156 response_authenticate(connection_t * conn, unsigned int tag, unsigned char **cont)
157 {
158 int i;
159 char *c;
160
161 buffer_reset(&ibuf);
162
163 do {
164 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
165 receive_response(conn, ibuf.data + strlen(ibuf.data));
166 response_bye(ibuf.data);
167 } while (strlen(ibuf.data) == RESPONSE_BUF &&
168 !strcasestr(ibuf.data, ultostr(tag, 16)));
169
170 if (cont != NULL && ibuf.data[0] == '+' && ibuf.data[1] == ' ') {
171 c = *cont = (unsigned char *)xmalloc(strlen(ibuf.data) + 1);
172
173 for (i = 2; ibuf.data[i] != '\r'; i++)
174 *c++ = ibuf.data[i];
175
176 *c = '\0';
177 }
178 return analyze_response(conn, ibuf.data);
179 }
180 #endif
181
182
183 /*
184 * Process the data that server sent due to IMAP NAMESPACE client request.
185 */
186 int
187 response_namespace(connection_t * conn, unsigned int tag)
188 {
189 char *c, *d;
190
191 buffer_reset(&ibuf);
192
193 do {
194 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
195 receive_response(conn, ibuf.data + strlen(ibuf.data));
196 response_bye(ibuf.data);
197 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
198
199 if ((c = strcasestr(ibuf.data, "* NAMESPACE"))) {
200 c += 12;
201 if (strncasecmp(c, "NIL", 3)) {
202 c = strchr(c, '"') + 1;
203 d = strchr(c, '"') + 1;
204
205 strncat(conn->ns.prefix, c, d - c - 1);
206 conn->ns.delim = *(strchr(d, '"') + 1);
207 }
208 }
209 debug("namespace (%s): '%s' '%c'\n",
210 (conn == &connpri ? "primary" : "auxiliary"), conn->ns.prefix,
211 conn->ns.delim);
212 return analyze_response(conn, ibuf.data);
213 }
214
215
216 /*
217 * Process the data that server sent due to IMAP STATUS client request.
218 */
219 int
220 response_status(connection_t * conn, unsigned int tag, char *mbox)
221 {
222 int r;
223 unsigned int exists, recent, unseen;
224 char *c;
225
226 exists = recent = unseen = 0;
227
228 buffer_reset(&ibuf);
229
230 do {
231 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
232 receive_response(conn, ibuf.data + strlen(ibuf.data));
233 response_bye(ibuf.data);
234 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
235
236 r = analyze_response(conn, ibuf.data);
237
238 if (r == RESPONSE_NO)
239 return -2;
240
241 if ((c = strcasestr(ibuf.data, "MESSAGES"))) {
242 c += 9;
243 exists = strtoul(c, NULL, 10);
244 }
245 if (exists == 0) {
246 info("No messages in mailbox \"%s\".\n", mbox);
247 return -2;
248 }
249 if ((c = strcasestr(ibuf.data, "RECENT"))) {
250 c += 7;
251 recent = strtoul(c, NULL, 10);
252 }
253 if ((c = strcasestr(ibuf.data, "UNSEEN"))) {
254 c += 7;
255 unseen = strtoul(c, NULL, 10);
256 }
257 info("%d message%s, %d recent, %d unseen, in mailbox \"%s\".\n",
258 exists, plural(exists), recent, unseen, mbox);
259
260 return r;
261 }
262
263
264 /*
265 * Process the data that server sent due to IMAP EXAMINE client request.
266 */
267 int
268 response_examine(connection_t * conn, unsigned int tag, char *mbox)
269 {
270 int r;
271 unsigned int exists, recent, unseen;
272 char *c;
273
274 exists = recent = unseen = 0;
275
276 buffer_reset(&ibuf);
277
278 do {
279 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
280 receive_response(conn, ibuf.data + strlen(ibuf.data));
281 response_bye(ibuf.data);
282 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
283
284 r = analyze_response(conn, ibuf.data);
285
286 if (r == RESPONSE_NO)
287 return -2;
288
289 if ((c = strcasestr(ibuf.data, "EXISTS"))) {
290 while (--c >= ibuf.data && *c != '*');
291 c++;
292 exists = strtoul(c, NULL, 10);
293 }
294 if (exists == 0) {
295 info("No messages in mailbox \"%s\".\n", mbox);
296 return -2;
297 }
298 if ((c = strcasestr(ibuf.data, "RECENT"))) {
299 while (--c >= ibuf.data && *c != '*');
300 c++;
301 recent = strtoul(c, NULL, 10);
302 }
303 if ((c = strcasestr(ibuf.data, "UNSEEN"))) {
304 c += 7;
305 unseen = strtoul(c, NULL, 10);
306 info("%d message%s, %d recent, %d unseen, in mailbox \"%s\".\n",
307 exists, plural(exists), recent, unseen, mbox);
308 } else
309 info("%d message%s, %d recent, in mailbox \"%s\".\n", exists,
310 plural(exists), recent, mbox);
311
312 return r;
313 }
314
315
316 /*
317 * Process the data that server sent due to IMAP SELECT client request.
318 */
319 int
320 response_select(connection_t * conn, unsigned int tag)
321 {
322
323 buffer_reset(&ibuf);
324
325 do {
326 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
327 receive_response(conn, ibuf.data + strlen(ibuf.data));
328 response_bye(ibuf.data);
329 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
330
331 if (strcasestr(ibuf.data, "[READ-ONLY]"))
332 return RESPONSE_READONLY;
333
334 return analyze_response(conn, ibuf.data);
335 }
336
337
338 /*
339 * Process the data that server sent due to IMAP SEARCH client request.
340 */
341 int
342 response_search(connection_t * conn, unsigned int tag, char **mesgs)
343 {
344 char *c, *m;
345 unsigned int blen;
346
347 buffer_reset(&ibuf);
348
349 do {
350 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
351 receive_response(conn, ibuf.data + strlen(ibuf.data));
352 response_bye(ibuf.data);
353 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
354
355 if ((c = strcasestr(ibuf.data, "* SEARCH "))) {
356 blen = strlen(ibuf.data);
357
358 m = *mesgs = (char *)xmalloc(blen + 1);
359
360 c += 9;
361
362 while (*c != '\0' && (isdigit(*c) || *c == ' '))
363 *(m++) = *(c++);
364
365 *m = 0;
366 }
367 return analyze_response(conn, ibuf.data);
368 }
369
370
371 /*
372 * Process the data that server sent due to IMAP FETCH client request.
373 */
374 int
375 response_fetch(connection_t * conn, unsigned int tag, int reset, char *fetch)
376 {
377 unsigned int i;
378 static unsigned int s;
379 char *b;
380
381 if (reset) {
382 s = 0;
383 return 0;
384 }
385 i = 0;
386
387 buffer_reset(&ibuf);
388
389 do {
390 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
391 receive_response(conn, ibuf.data + strlen(ibuf.data));
392 response_bye(ibuf.data);
393 } while (strlen(ibuf.data) < RESPONSE_BUF &&
394 !strcasestr(ibuf.data, ultostr(tag, 16)));
395
396 b = ibuf.data;
397
398 if (s == 0) {
399 if ((b = strstr(b, "}\r\n"))) {
400 while (b - ibuf.data > 0)
401 if (*--b == '{')
402 break;
403 s = atoi(++b) - 2;
404 b = strchr(b, '}');
405 b += 3;
406 } else {
407 return RESPONSE_NULLBODY; /* Null body. */
408 }
409 }
410 while (*b != '\0' && s-- != 0)
411 fetch[i++] = *(b++);
412
413 fetch[i] = '\0';
414
415 return analyze_response(conn, ibuf.data);
416 }
417
418
419 /*
420 * Process the data that server sent due to IMAP FETCH FAST client request.
421 */
422 int
423 response_fetchfast(connection_t * conn, char **flags, char **date,
424 unsigned int *size, unsigned int tag)
425 {
426 char *c, *m;
427 unsigned int blen;
428
429 *size = 0;
430
431 buffer_reset(&ibuf);
432
433 do {
434 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
435 receive_response(conn, ibuf.data + strlen(ibuf.data));
436 response_bye(ibuf.data);
437 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
438
439 if ((c = strcasestr(ibuf.data, "FETCH (FLAGS ("))) {
440 blen = strlen(ibuf.data);
441
442 m = *flags = (char *)xmalloc(blen + 1);
443
444 c += 14;
445
446 while (*c != '\0' && *c != ')') {
447 /* The \Recent flag can not be altered by the client. */
448 if (*c == '\\') {
449 if (strcasestr(c - 1, "(\\Recent)") ||
450 strcasestr(c - 1, " \\Recent)"))
451 break;
452 else if (strcasestr(c, "\\Recent "))
453 c += 8;
454 }
455 *(m++) = *(c++);
456 }
457
458 *m = 0;
459 }
460 if ((c = strcasestr(ibuf.data, " INTERNALDATE \""))) {
461 blen = strlen(ibuf.data);
462
463 m = *date = (char *)xmalloc(blen + 1);
464
465 c += 15;
466
467 while (*c != '\0' && *c != '"')
468 *(m++) = *(c++);
469
470 *m = 0;
471 }
472 if ((c = strcasestr(ibuf.data, " RFC822.SIZE "))) {
473 c += 13;
474 *size = strtoul(c, NULL, 10);
475 }
476 return analyze_response(conn, ibuf.data);
477 }
478
479
480 /*
481 * Process the data that server sent due to IMAP APPEND client request.
482 */
483 int
484 response_append(connection_t * conn, unsigned int tag)
485 {
486 int r;
487
488 r = RESPONSE_OK;
489
490 buffer_reset(&ibuf);
491
492 do {
493 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
494 receive_response(conn, ibuf.data + strlen(ibuf.data));
495 response_bye(ibuf.data);
496 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
497
498 if ((r = analyze_response(conn, ibuf.data)) == RESPONSE_NO &&
499 strcasestr(ibuf.data, "[TRYCREATE]"))
500 return RESPONSE_TRYCREATE;
501
502 return r;
503 }
504
505
506 /*
507 * Process the data that server sent due to IMAP COPY client request.
508 */
509 int
510 response_copy(connection_t * conn, unsigned int tag)
511 {
512 int r;
513
514 r = RESPONSE_OK;
515
516 buffer_reset(&ibuf);
517
518 do {
519 buffer_check(&ibuf, strlen(ibuf.data) + RESPONSE_BUF);
520 receive_response(conn, ibuf.data + strlen(ibuf.data));
521 response_bye(ibuf.data);
522 } while (!strcasestr(ibuf.data, ultostr(tag, 16)));
523
524 if ((r = analyze_response(conn, ibuf.data)) == RESPONSE_NO &&
525 strcasestr(ibuf.data, "[TRYCREATE]"))
526 return RESPONSE_TRYCREATE;
527
528 return r;
529 }
530
531
532 /*
533 * Check if response of server to client's request was succesfully
534 * delivered or there was some kind of error.
535 */
536 int
537 analyze_response(connection_t * conn, char *buf)
538 {
539 int r;
540 regex_t creg;
541 regmatch_t match[3];
542 const char *reg;
543 char result[RESULT_BUF];
544
545 r = RESPONSE_OK;
546 reg = "[[:xdigit:]]{4,4} ((OK|NO|BAD)[[:print:]]*)\r\n";
547 result[0] = '\0';
548
549 regcomp(&creg, reg, REG_EXTENDED | REG_ICASE);
550
551 if (!regexec(&creg, buf, 3, match, 0)) {
552 strncat(result, buf + match[1].rm_so,
553 min(match[1].rm_eo - match[1].rm_so, RESULT_BUF - 1));
554
555 if (!strncasecmp(buf + match[2].rm_so, "NO", 2))
556 r = RESPONSE_NO;
557 else if (!strncasecmp(buf + match[2].rm_so, "BAD", 3))
558 r = RESPONSE_BAD;
559
560 verbose("%s: %s", (conn == &connpri ? "S" : "s"),
561 buf + match[0].rm_so);
562 } else
563 r = RESPONSE_NONE;
564
565 regfree(&creg);
566
567 return r;
568 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26