1 /**********************************************************************
2  * token.c                                               September 1999
3  * Horms                                             horms@verge.net.au
4  *
5  * Token to encapsulate a byte string
6  *
7  * perdition
8  * Mail retrieval proxy server
9  * Copyright (C) 1999-2005  Horms
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of the
14  * License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  **********************************************************************/
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 
31 #include "io.h"
32 #include "io_select.h"
33 #include "token.h"
34 #include "options.h"
35 #include "perdition_globals.h"
36 
37 #ifdef DMALLOC
38 #include <dmalloc.h>
39 #endif
40 
41 
42 
43 static char token_read_buffer[MAX_LINE_LENGTH];
44 static size_t token_read_offset=0;
45 static size_t token_read_bytes=0;
46 
47 static int token_fill_buffer(io_t *io, const options_t *opt,
48 		const char *log_str);
49 static int __token_fill_buffer(io_t *io, const options_t *opt,
50 		const char *log_str);
51 
52 /**********************************************************************
53  * token_create
54  * create an empty token
55  * pre: none
56  * post: token is created, and values are initialised
57  * return: allocated token_t
58  *         NULL on error
59  *
60  * 8 bit clean
61  **********************************************************************/
62 
token_create(void)63 token_t *token_create(void){
64   token_t *t;
65 
66   if((t=(token_t *)malloc(sizeof(token_t)))==NULL){
67     VANESSA_LOGGER_DEBUG_ERRNO("malloc");
68     return(NULL);
69   }
70   t->n=0;
71   t->buf=NULL;
72   return(t);
73 }
74 
75 
76 /**********************************************************************
77  * token_assign
78  * place bytes into a token
79  * pre: t: token to place bytes in
80  *      buf: buffer to use
81  *      n: number of bytes in buffer
82  *      flags: flags for token as per token.h
83  * post: if n!=0 then buf is used as the buffer in t
84  *       (no copying)
85  *       if n==0 the buffer in t is set to NULL
86  *       the flag in t is set
87  * return: none
88  *
89  * 8 bit clean
90  **********************************************************************/
91 
token_assign(token_t * t,char * buf,const size_t n,const flag_t flag)92 void token_assign(
93   token_t *t,
94   char *buf,
95   const size_t n,
96   const flag_t flag
97 ){
98   t->n=n;
99   t->flag=flag;
100   if(n==0){
101     if(buf!=NULL){
102       free(buf);
103     }
104     t->buf=NULL;
105   }
106   else {
107     t->buf=buf;
108   }
109 }
110 
111 
112 /**********************************************************************
113  * token_unassign
114  * make a token empty
115  * useful if you want to destroy a token but not what it contains
116  * pre: t: token to unassign values of
117  * post: values in t are reinitialised, but buffer is not destroyed
118  * return: none
119  *
120  * 8 bit clean
121  **********************************************************************/
122 
token_unassign(token_t * t)123 void token_unassign(token_t *t){
124   token_assign(t, NULL, 0, 0);
125 }
126 
127 
128 /**********************************************************************
129  * token_destroy
130  * pre: t pointer to a token
131  * post: if the token is null no nothing
132  *       if buf is non-NULL then free it
133  *       free the token
134  * return: none
135  *
136  * 8 bit clean
137  **********************************************************************/
138 
token_destroy(token_t ** t)139 void token_destroy(token_t **t){
140   if(*t==NULL){
141     return;
142   }
143 
144   if((*t)->buf!=NULL){
145     free((*t)->buf);
146   }
147 
148   free(*t);
149   *t=NULL;
150 }
151 
152 
153 /**********************************************************************
154  * token_write
155  * write a token to fd
156  * pre: io: io_t to write to
157  *      token: token to write
158  * post: contents of token is written to fd using
159  *       vanessa_socket_pipe_write_bytes
160  * return: -1 on error
161  *         0 otherwise
162  *
163  * 8 bit clean
164  **********************************************************************/
165 
token_write(io_t * io,const token_t * t)166 int token_write(io_t *io, const token_t *t){
167   if(io_write(io, t->buf, t->n)){
168     VANESSA_LOGGER_DEBUG("vanessa_socket_pipe_write_bytes");
169     return(-1);
170   }
171 
172   return(0);
173 }
174 
175 
176 /**********************************************************************
177  * token_flush
178  * Flush internal buffers used to read tokens.
179  * pre: none
180  * post: internal buffers are flushed
181  * return: none
182  **********************************************************************/
183 
token_flush(void)184 void token_flush(void) {
185 	token_read_offset = 0;
186 	token_read_bytes = 0;
187 	memset(token_read_buffer, 0, sizeof(token_read_buffer));
188 }
189 
190 
191 /**********************************************************************
192  * token_fill_buffer
193  * read a token in from fd
194  * pre: io: io_t to read from
195  *      opt: options
196  *      log_str: logging tag for connection logging
197  * post: Bytes are read from fd into a buffer, if the buffer is
198  *       empty
199  * return: number of bytes read, or number of unread bytes in buffer
200  *         -1 on error
201  *
202  * 8 bit clean
203  **********************************************************************/
204 
token_fill_buffer(io_t * io,const options_t * opt,const char * log_str)205 static int token_fill_buffer(io_t *io, const options_t *opt,
206 		const char *log_str) {
207   if(token_read_bytes>token_read_offset) {
208     if(token_read_bytes==0){
209       VANESSA_LOGGER_DEBUG("returning without read");
210     }
211     return(token_read_bytes);
212   }
213   return(__token_fill_buffer(io, opt, log_str));
214 }
215 
__token_fill_buffer(io_t * io,const options_t * opt,const char * log_str)216 static int __token_fill_buffer(io_t *io, const options_t *opt,
217 		const char *log_str){
218 	int bytes_read;
219 	char *dump_buf;
220 
221 	bytes_read = io_read(io, token_read_buffer, MAX_LINE_LENGTH-1);
222 	if (bytes_read < 0) {
223 		VANESSA_LOGGER_DEBUG_ERRNO("error reading input");
224 		return -1;
225 	}
226 
227 	token_read_offset = 0;
228 	token_read_bytes = bytes_read;
229 	if (opt->connection_logging) {
230 		dump_buf = VANESSA_LOGGER_DUMP(token_read_buffer,
231 					       bytes_read, 0);
232 		if (!dump_buf) {
233 			VANESSA_LOGGER_DEBUG("VANESSA_LOGGER_DUMP");
234 			return -1;
235 		}
236 		VANESSA_LOGGER_DEBUG_RAW_UNSAFE("%s \"%s\"", log_str, dump_buf);
237 		free(dump_buf);
238 	}
239 
240 	return bytes_read;
241 }
242 
243 
244 /**********************************************************************
245  * token_read
246  * read a token in from fd
247  * pre: io: io_t to read from
248  *      literal_buf: buffer to store bytes read from server in
249  *      n: pointer to size_t containing the size of literal_buf
250  *      flag: If logical or of TOKEN_EOL then all characters
251  *            up to a '\n' will be read as a token. That is the token may
252  *            have spaces.
253  *            If logical or of TOKEN_IMAP4 then spaces inside
254  *            quotes will be treated as literals rather than token
255  *            delimiters.
256  *            If logical or of TOKEN_IMAP4_LITERAL then m bytes
257  *            will be read as a single token.
258  *      m: Bytes to read if flag is TOKEN_IMAP4_LITERAL
259  *      log_str: logging tag for connection logging
260  * post: Token is read from fd into token
261  *       ' ' will terminate a token
262  *       '\r' is ignored
263  *       '\n' will terminate a token and set the eol element to 1
264  *       All other characters are considered to be a part of the token
265  *       If literal_buf is not NULL, and n is not NULL and *n is not 0
266  *       Bytes read from fd are copied to literal_buf, this includes
267  *       ' ', '\r' and '\n'.
268  * return: token
269  *         NULL on error
270  * Note: if a token larger than BUFFER_SIZE is read then only
271  *       BUFFER_SIZE will be read and the remainder will be
272  *       left (to be handled by an subsequent call to token_read).
273  *       The same applies to *n if literal_buf is being filled.
274  *
275  * 8 bit clean
276  **********************************************************************/
277 
token_read(io_t * io,char * literal_buf,size_t * n,flag_t flag,size_t m,const char * log_str)278 token_t *token_read(
279   io_t *io,
280   char *literal_buf,
281   size_t *n,
282   flag_t flag,
283   size_t m,
284   const char *log_str
285 ){
286   char buffer[MAX_LINE_LENGTH];
287   char *assign_buffer;
288   char c;
289   token_t *t;
290   size_t literal_offset=0;
291   size_t len=0;
292   int bytes_read = 0;
293   int do_literal;
294   flag_t save_flag=TOKEN_NONE;
295   flag_t quoted=0;
296 
297   memset(buffer, 0, MAX_LINE_LENGTH);
298 
299   do_literal=(literal_buf!=NULL && n!=NULL && *n!=0)?1:0;
300   while(!(do_literal && literal_offset>=*n) && len < MAX_LINE_LENGTH){
301     if((bytes_read=token_fill_buffer(io, &opt, log_str))<=0){
302       VANESSA_LOGGER_DEBUG("token_fill_buffer");
303       return(NULL);
304     }
305 
306     c=token_read_buffer[token_read_offset++];
307 
308     /*Place in literal buffer, if we are doooooooooooooing that today*/
309     if(do_literal){
310       *(literal_buf+(literal_offset++))=c;
311     }
312 
313     if(flag&TOKEN_IMAP4_LITERAL) {
314       buffer[len++]=c;
315       if(len >= m) {
316          break;
317       }
318       continue;
319     }
320 
321     switch(c){
322       case '\n':
323         save_flag=TOKEN_EOL;
324         goto end_while;
325       case '\r':
326         break;
327       case '\"':
328 	if(flag&TOKEN_IMAP4){
329 	  quoted^=1;
330 	}
331         buffer[len++]=c;
332 	break;
333       case ' ':
334         if(!(flag&TOKEN_EOL) && !quoted){
335 	  goto end_while;
336         }
337       default:
338         buffer[len++]=c;
339     }
340   }
341 end_while:
342 
343   /*Set return value for n*/
344   if(do_literal){
345     *n=literal_offset;
346   }
347 
348   /*Create token to return*/
349   if((t=token_create())==NULL){
350     VANESSA_LOGGER_DEBUG("token_create");
351     return(NULL);
352   }
353   assign_buffer = malloc(len);
354   if (!assign_buffer) {
355     VANESSA_LOGGER_DEBUG_ERRNO("malloc");
356     token_destroy(&t);
357     return(NULL);
358   }
359   memcpy(assign_buffer, buffer, len);
360   token_assign(t, assign_buffer, len, save_flag);
361   return(t);
362 }
363 
364 
365 /**********************************************************************
366  * token_cmp
367  * compare two tokens
368  * pre: a: token to compare
369  *      b: token to compare
370  * post: none
371  * return: 1 is they are the same
372  *         0 otherwise
373  * flag field will be ignored if either token has eol set to TOKEN_DONT_CARE
374  *
375  * Not 8 bit clean as it is case insensitive using toupper
376  **********************************************************************/
377 
token_cmp(const token_t * a,const token_t * b)378 int token_cmp(const token_t *a, const token_t *b){
379   if(
380     a->n!=b->n ||
381     ((a->flag!=b->flag)&&(a->flag!=TOKEN_DONT_CARE)&&(b->flag!=TOKEN_DONT_CARE))
382   ){
383     return(0);
384   }
385 
386   return(strncasecmp(a->buf, b->buf, a->n)?0:1);
387 }
388 
389 
390 /**********************************************************************
391  * token_to_string
392  * dump the buffer in a token into a \0 terminated string
393  * string will be dynamically allocated
394  * pre: t: token to dump to a string
395  *      strip: Character to strip from first and last character of
396  *             string if it is present. Ignored if TOKEN_NO_STRIP
397  * post: a string is allocated and the contents of it's buffer plus
398  *       a trailing '\0' is placed in the string
399  * return: the string
400  *         NULL on error
401  *
402  * Not 8 bit clean
403  **********************************************************************/
404 
token_to_string(const token_t * t,const unsigned char strip)405 char *token_to_string(const token_t *t, const unsigned char strip){
406   char *string;
407   char *buf;
408   size_t n;
409 
410   if(t==NULL || t->buf == NULL) {
411 	  string=malloc(1);
412 	  *string='\0';
413 	  return(string);
414   }
415 
416   buf=t->buf;
417   n=t->n;
418 
419   if(strip!=TOKEN_NO_STRIP && *buf==strip && *(buf+n-1)==strip){
420     buf++;
421     n-=2;
422   }
423 
424   if((string=strn_to_str((char *)buf, n))==NULL){
425     VANESSA_LOGGER_DEBUG("strn_to_str");
426     return(NULL);
427   }
428   return(string);
429 }
430 
431 /**********************************************************************
432  * token_casecmp_string
433  * Compare a token with a null-terminated string
434  * pre: t: token
435  *      str: string
436  * return: 1 on match
437  *         0 otherwise
438  *
439  * Not 8 bit clean
440  **********************************************************************/
441 
token_casecmp_string(const token_t * t,const char * str)442 int token_casecmp_string(const token_t *t, const char *str)
443 {
444 	if (token_len(t) == strlen(str) &&
445 	    !strncasecmp((char *)token_buf(t), str, token_len(t)))
446 		return 1;
447 
448 	return 0;
449 }
450 
451