1 /*
2 ** Copyright 1998 - 2005 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #if HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9
10 #include "imaptoken.h"
11 #include "imapwrite.h"
12 #include <stdio.h>
13 #include <ctype.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <sys/time.h>
18 #include "numlib/numlib.h"
19 #if HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22
23 #ifndef BUFSIZ
24 #define BUFSIZ 8192
25 #endif
26
27 static const char rcsid[]="$Id: imaptoken.c,v 1.20 2005/10/01 03:23:44 mrsam Exp $";
28
29 static struct imaptoken curtoken;
30 static char readbuf[BUFSIZ];
31
32 char *imap_readptr=0;
33 size_t imap_readptrleft=0;
34 time_t start_time;
35
36 static time_t readtimeout;
37
38 extern FILE *debugfile;
39
40 extern unsigned long header_count, body_count;
41
42 unsigned long bytes_received_count = 0; /* counter for received bytes */
43 unsigned long bytes_sent_count; /* counter for sent bytes (imapwrite.c) */
44
45 extern void bye();
46
bye_msg(const char * type)47 void bye_msg(const char *type)
48 {
49 const char *a=getenv("AUTHENTICATED");
50 char buf[NUMBUFSIZE];
51 const char *tls=getenv("IMAP_TLS");
52
53 libmail_str_time_t(time(NULL)-start_time, buf);
54
55 if (tls && atoi(tls))
56 tls=", starttls=1";
57 else
58 tls="";
59
60 if (a && *a)
61 fprintf(stderr, "%s, user=%s, "
62 "ip=[%s], headers=%lu, body=%lu, rcvd=%lu, sent=%lu, time=%s%s\n",
63 type,
64 a, getenv("TCPREMOTEIP"), header_count, body_count, bytes_received_count, bytes_sent_count,
65 buf, tls);
66 else
67 fprintf(stderr, "DEBUG: Disconnected, ip=[%s], time=%s%s\n",
68 getenv("TCPREMOTEIP"),
69 buf, tls);
70 }
71
disconnected()72 void disconnected()
73 {
74 bye_msg("INFO: DISCONNECTED");
75 bye();
76 }
77
disconnected_timeout(void)78 static void disconnected_timeout(void)
79 {
80 writes("* BYE Disconnected for inactivity.\r\n");
81 writeflush();
82 bye_msg("INFO: TIMEOUT");
83 bye();
84 }
85
doidle(time_t idletimeout,int extraFd)86 int doidle(time_t idletimeout, int extraFd)
87 {
88 fd_set fds;
89 struct timeval tv;
90 time_t t;
91
92 time(&t);
93 if (t >= readtimeout) disconnected_timeout();
94 if (imap_readptrleft > 0) return 1;
95
96 FD_ZERO(&fds);
97 FD_SET(0, &fds);
98
99 if (extraFd > 0)
100 {
101 FD_SET(extraFd, &fds);
102 }
103 else
104 {
105 extraFd=0;
106 }
107
108 tv.tv_sec=idletimeout;
109 tv.tv_usec=0;
110
111 select(extraFd + 1, &fds, 0, 0, &tv);
112 return (FD_ISSET(0, &fds));
113 }
114
doread(char * buf,size_t bufsiz)115 size_t doread(char *buf, size_t bufsiz)
116 {
117 fd_set fds;
118 struct timeval tv;
119 time_t t;
120 int n = 0;
121
122 time(&t);
123 if (t >= readtimeout) disconnected_timeout();
124
125 FD_ZERO(&fds);
126 FD_SET(0, &fds);
127 tv.tv_sec=readtimeout - t;
128 tv.tv_usec=0;
129
130 if (select(1, &fds, 0, 0, &tv) <= 0)
131 {
132 disconnected_timeout();
133 return (0);
134 }
135 if (!FD_ISSET(0, &fds) || (n=read(0, buf, bufsiz)) <= 0)
136 {
137 if ( n > 0 )
138 bytes_received_count += n; /* count received bytes */
139 disconnected();
140 return (0);
141 }
142 if ( n > 0 )
143 bytes_received_count += n; /* count received bytes */
144 return (n);
145 }
146
readfill()147 void readfill()
148 {
149 imap_readptrleft=doread(readbuf, sizeof(readbuf));
150 imap_readptr=readbuf;
151 }
152
153 #define UNREAD(c) (*--imap_readptr=(c), ++imap_readptrleft)
154
unread(int c)155 void unread(int c)
156 {
157 UNREAD(c);
158 }
159
read_eol()160 void read_eol()
161 {
162 int c;
163
164 while ( (c=READ()) != '\n')
165 ;
166 curtoken.tokentype=IT_EOL;
167 }
168
read_timeout(time_t t)169 void read_timeout(time_t t)
170 {
171 time_t tt;
172
173 time(&tt);
174 readtimeout=tt+t;
175 }
176
alloc_tokenbuf(unsigned l)177 static void alloc_tokenbuf(unsigned l)
178 {
179 if (l >= curtoken.tokenbuf_size)
180 {
181 char *p=curtoken.tokenbuf ? realloc(curtoken.tokenbuf, l + 256):
182 malloc(l + 256);
183
184 if (!p)
185 write_error_exit("malloc");
186
187 curtoken.tokenbuf_size = l+256;
188 curtoken.tokenbuf=p;
189 }
190 }
191
192 static char LPAREN_CHAR='(';
193 static char RPAREN_CHAR=')';
194 static char LBRACKET_CHAR='[';
195 static char RBRACKET_CHAR=']';
196
ignorepunct()197 void ignorepunct()
198 {
199 LPAREN_CHAR=RPAREN_CHAR=LBRACKET_CHAR=RBRACKET_CHAR='\n';
200 }
201
202 #if SMAP
203
smap_readline(char * buffer,size_t bufsize)204 void smap_readline(char *buffer, size_t bufsize)
205 {
206 int c;
207
208 while ((c=READ()) != '\n')
209 {
210 if (bufsize > 1)
211 {
212 *buffer++ = c;
213 --bufsize;
214 }
215 }
216 *buffer=0;
217 }
218
219 #endif
220
do_readtoken(int touc)221 static struct imaptoken *do_readtoken(int touc)
222 {
223 int c=0;
224 unsigned l;
225
226 #define appendch(c) alloc_tokenbuf(l+1); curtoken.tokenbuf[l++]=(c);
227
228 if (curtoken.tokentype == IT_ERROR) return (&curtoken);
229
230 do
231 {
232 c=READ();
233 } while (c == '\r' || c == ' ' || c == '\t');
234
235 if (c == '\n')
236 {
237 UNREAD(c);
238 curtoken.tokentype=IT_EOL;
239 return (&curtoken);
240 }
241 c=(unsigned char)c;
242 if (c == LPAREN_CHAR)
243 {
244 curtoken.tokentype=IT_LPAREN;
245 return (&curtoken);
246 }
247
248 if (c == RPAREN_CHAR)
249 {
250 curtoken.tokentype=IT_RPAREN;
251 return (&curtoken);
252 }
253
254 if (c == LBRACKET_CHAR)
255 {
256 curtoken.tokentype=IT_LBRACKET;
257 return (&curtoken);
258 }
259
260 if (c == RBRACKET_CHAR)
261 {
262 curtoken.tokentype=IT_RBRACKET;
263 return (&curtoken);
264 }
265
266 if (c == '"')
267 {
268 l=0;
269 while ((c=READ()) != '"')
270 {
271 if (c == '\\')
272 c=READ();
273 if (c == '\r' || c == '\n')
274 {
275 UNREAD(c);
276 curtoken.tokentype=IT_ERROR;
277 return (&curtoken);
278 }
279 if (l < 8192)
280 {
281 appendch(c);
282 }
283 }
284 appendch(0);
285 curtoken.tokentype=IT_QUOTED_STRING;
286 return (&curtoken);
287 }
288
289 if (c == '{')
290 {
291 curtoken.tokennum=0;
292 while ((c=READ()) != '}')
293 {
294 if (!isdigit((int)(unsigned char)c))
295 {
296 UNREAD(c);
297 curtoken.tokentype=IT_ERROR;
298 return (&curtoken);
299 }
300 curtoken.tokennum = curtoken.tokennum*10 + (c-'0');
301 }
302 c=READ();
303 if (c == '\r')
304 {
305 c=READ();
306 }
307 if (c != '\n')
308 {
309 curtoken.tokentype=IT_ERROR;
310 return (&curtoken);
311 }
312 curtoken.tokentype=IT_LITERAL_STRING_START;
313 return (&curtoken);
314 }
315
316 l=0;
317 if (c == '\\')
318 {
319 appendch(c); /* Message flag */
320 c=READ();
321 }
322 else if (isdigit(c))
323 {
324 curtoken.tokentype=IT_NUMBER;
325 curtoken.tokennum=0;
326 do
327 {
328 appendch(c);
329 curtoken.tokennum = curtoken.tokennum*10 +
330 (c-'0');
331 c=READ();
332 } while (isdigit( (int)(unsigned char)c));
333
334 /* Could be stuff like mime.spec, so continue reading. */
335 }
336
337 while (c != '\r' && c != '\n'
338 && !isspace((int)(unsigned char)c)
339 && c != '\\' && c != '"' && c != LPAREN_CHAR && c != RPAREN_CHAR
340 && c != '{' && c != '}' && c != LBRACKET_CHAR && c != RBRACKET_CHAR)
341 {
342 curtoken.tokentype=IT_ATOM;
343 if (l < IT_MAX_ATOM_SIZE)
344 {
345 if (touc)
346 c=toupper(c);
347 appendch(c);
348 }
349 else
350 {
351 write_error_exit("max atom size too small");
352 }
353 c=READ();
354 }
355 if (l == 0)
356 {
357 curtoken.tokentype=IT_ERROR;
358 return (&curtoken);
359 }
360 appendch(0);
361 UNREAD(c);
362
363 if (strcmp(curtoken.tokenbuf, "NIL") == 0)
364 curtoken.tokentype=IT_NIL;
365 return (&curtoken);
366 }
367
readtoken(int touc)368 static struct imaptoken *readtoken(int touc)
369 {
370 struct imaptoken *tok=do_readtoken(touc);
371
372 if (tok->tokentype == IT_LITERAL_STRING_START)
373 {
374 unsigned long nbytes=curtoken.tokennum;
375
376 if (nbytes > 8192)
377 {
378 writes("* NO [ALERT] IMAP command too long.\r\n");
379 tok->tokentype=IT_ERROR;
380 }
381 else
382 {
383 unsigned long i;
384
385 writes("+ OK\r\n");
386 writeflush();
387 alloc_tokenbuf(nbytes+1);
388 for (i=0; i<nbytes; i++)
389 tok->tokenbuf[i]= READ();
390 tok->tokenbuf[i]=0;
391 tok->tokentype=IT_QUOTED_STRING;
392 }
393 }
394
395 if (debugfile)
396 {
397 char *p=0;
398
399 fprintf(debugfile, "READ: ");
400 switch (tok->tokentype) {
401 case IT_ATOM:
402 p=curtoken.tokenbuf; fprintf(debugfile, "ATOM"); break;
403 case IT_NUMBER:
404 p=curtoken.tokenbuf; fprintf(debugfile, "NUMBER"); break;
405 case IT_QUOTED_STRING:
406 p=curtoken.tokenbuf; fprintf(debugfile, "QUOTED_STRING"); break;
407 case IT_LPAREN:
408 fprintf(debugfile, "LPAREN"); break;
409 case IT_RPAREN:
410 fprintf(debugfile, "RPAREN"); break;
411 case IT_NIL:
412 fprintf(debugfile, "NIL"); break;
413 case IT_ERROR:
414 fprintf(debugfile, "ERROR"); break;
415 case IT_EOL:
416 fprintf(debugfile, "EOL"); break;
417 case IT_LBRACKET:
418 fprintf(debugfile, "LBRACKET"); break;
419 case IT_RBRACKET:
420 fprintf(debugfile, "RBRACKET"); break;
421 }
422
423 if (p)
424 fprintf(debugfile, ": %s", p);
425 fprintf(debugfile, "\n");
426 fflush(debugfile);
427 }
428 return (tok);
429 }
430
nexttoken(void)431 struct imaptoken *nexttoken(void)
432 {
433 return (readtoken(1));
434 }
435
nexttoken_nouc(void)436 struct imaptoken *nexttoken_nouc(void)
437 {
438 return (readtoken(0));
439 }
440
441 /* RFC 2060 sucks */
442
nexttoken_okbracket(void)443 struct imaptoken *nexttoken_okbracket(void)
444 {
445 struct imaptoken *t;
446
447 LBRACKET_CHAR=RBRACKET_CHAR='\n';
448
449 t=nexttoken();
450
451 LBRACKET_CHAR='[';
452 RBRACKET_CHAR=']';
453 return (t);
454 }
455
nexttoken_nouc_okbracket(void)456 struct imaptoken *nexttoken_nouc_okbracket(void)
457 {
458 struct imaptoken *t;
459
460 LBRACKET_CHAR=RBRACKET_CHAR='\n';
461
462 t=nexttoken_nouc();
463
464 LBRACKET_CHAR='[';
465 RBRACKET_CHAR=']';
466 return (t);
467 }
468
currenttoken(void)469 struct imaptoken *currenttoken(void)
470 {
471 return (&curtoken);
472 }
473
nexttoken_noparseliteral(void)474 struct imaptoken *nexttoken_noparseliteral(void)
475 {
476 return (do_readtoken(0));
477 }
478
479 /* Read an IMAP literal string (or a portion of) */
480
read_string(char ** ptr,unsigned long * left,unsigned long cnt)481 void read_string(char **ptr, unsigned long *left, unsigned long cnt)
482 {
483 if (imap_readptrleft == 0)
484 {
485 /* Keep reading until we fill the buffer or until we've
486 ** read the entire string.
487 */
488
489 read_timeout(SOCKET_TIMEOUT);
490 imap_readptr=readbuf;
491 while (imap_readptrleft < sizeof(readbuf) && imap_readptrleft < cnt)
492 imap_readptrleft += doread(readbuf+imap_readptrleft,
493 sizeof(readbuf)-imap_readptrleft);
494 }
495
496 if (cnt < imap_readptrleft) /* Can satisfy fully from buffer */
497 {
498 *ptr=imap_readptr;
499 *left=cnt;
500 imap_readptr += cnt;
501 imap_readptrleft -= cnt;
502 return;
503 }
504
505 *ptr=imap_readptr;
506 *left=imap_readptrleft;
507 imap_readptrleft=0;
508 return;
509 }
510
my_strdup(const char * s)511 char *my_strdup(const char *s)
512 {
513 char *q=strdup(s);
514
515 if (!q) write_error_exit("malloc");
516 return (q);
517 }
518
ismsgset(struct imaptoken * tok)519 int ismsgset(struct imaptoken *tok)
520 /* See if this token is a syntactically valid message set */
521 {
522
523 if (tok->tokentype == IT_NUMBER) return (1);
524 if (tok->tokentype != IT_ATOM) return (0);
525
526 return ismsgset_str(tok->tokenbuf);
527 }
528
ismsgset_str(const char * p)529 int ismsgset_str(const char *p)
530 {
531 while (isdigit((int)(unsigned char)*p) || *p == '*')
532 {
533 if (*p == '0') return (0);
534 if (*p == '*')
535 ++p;
536 else
537 do
538 {
539 ++p;
540 } while (isdigit((int)(unsigned char)*p));
541
542 if (*p == ':')
543 {
544 ++p;
545 if (!isdigit((int)(unsigned char)*p) &&
546 *p != '*')
547 return (0);
548 if (*p == '0') return (0);
549 if (*p == '*')
550 ++p;
551 else
552 do
553 {
554 ++p;
555 } while (isdigit((int)(unsigned char)*p));
556 }
557 if (*p != ',') break;
558 ++p;
559 }
560 if (*p) return (0);
561 return (1);
562 }
563
564