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