1 /*
2  * libEtPan! -- a mail stuff library
3  *
4  * Copyright (C) 2001, 2002 - DINH Viet Hoa
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the libEtPan! project nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * $Id$
34  */
35 
36 #include "mailmbox_parse.h"
37 
38 #include "mailmbox.h"
39 
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <string.h>
43 #include <stdlib.h>
44 
45 #define UID_HEADER "X-LibEtPan-UID:"
46 
47 #include "utils.h"
48 
49 #ifndef TRUE
50 #define TRUE 1
51 #endif
52 
53 #ifndef FALSE
54 #define FALSE 0
55 #endif
56 
57 enum {
58   UNSTRUCTURED_START,
59   UNSTRUCTURED_CR,
60   UNSTRUCTURED_LF,
61   UNSTRUCTURED_WSP,
62   UNSTRUCTURED_OUT
63 };
64 
65 static inline int
claws_mailmbox_fields_parse(char * str,size_t length,size_t * index,uint32_t * puid,size_t * phlen)66 claws_mailmbox_fields_parse(char * str, size_t length,
67 		      size_t * index,
68 		      uint32_t * puid,
69 		      size_t * phlen)
70 {
71   size_t cur_token;
72   int r;
73   size_t hlen;
74   size_t uid;
75   int end;
76 
77   cur_token = * index;
78 
79   end = FALSE;
80   uid = 0;
81   while (!end) {
82     size_t begin;
83 
84     begin = cur_token;
85 
86     r = mailimf_ignore_field_parse(str, length, &cur_token);
87     switch (r) {
88     case MAILIMF_NO_ERROR:
89       if (str[begin] == 'X') {
90 
91 	if (strncasecmp(str + begin, UID_HEADER, strlen(UID_HEADER)) == 0) {
92 	  begin += strlen(UID_HEADER);
93 
94 	  while (str[begin] == ' ')
95 	    begin ++;
96 
97 	  uid = strtoul(str + begin, NULL, 10);
98 	}
99       }
100 
101       break;
102     case MAILIMF_ERROR_PARSE:
103     default:
104       end = TRUE;
105       break;
106     }
107   }
108 
109   hlen = cur_token - * index;
110 
111   * phlen = hlen;
112   * puid = uid;
113   * index = cur_token;
114 
115   return MAILMBOX_NO_ERROR;
116 }
117 
118 enum {
119   IN_MAIL,
120   FIRST_CR,
121   FIRST_LF,
122   SECOND_CR,
123   SECOND_LF,
124   PARSING_F,
125   PARSING_R,
126   PARSING_O,
127   PARSING_M,
128   OUT_MAIL
129 };
130 
131 
132 
133 
134 static inline int
claws_mailmbox_single_parse(char * str,size_t length,size_t * index,size_t * pstart,size_t * pstart_len,size_t * pheaders,size_t * pheaders_len,size_t * pbody,size_t * pbody_len,size_t * psize,size_t * ppadding,uint32_t * puid)135 claws_mailmbox_single_parse(char * str, size_t length,
136 		      size_t * index,
137 		      size_t * pstart,
138 		      size_t * pstart_len,
139 		      size_t * pheaders,
140 		      size_t * pheaders_len,
141 		      size_t * pbody,
142 		      size_t * pbody_len,
143 		      size_t * psize,
144 		      size_t * ppadding,
145 		      uint32_t * puid)
146 {
147   size_t cur_token;
148   size_t start;
149   size_t start_len;
150   size_t headers;
151   size_t headers_len;
152   size_t body;
153   size_t end;
154   size_t next;
155   size_t message_length;
156   uint32_t uid;
157   int r;
158 #if 0
159   int in_mail_data;
160 #endif
161 #if 0
162   size_t begin;
163 #endif
164 
165   int state;
166 
167   cur_token = * index;
168 
169   if (cur_token >= length)
170     return MAILMBOX_ERROR_PARSE;
171 
172   start = cur_token;
173   start_len = 0;
174   headers = cur_token;
175 
176   if (cur_token + 5 < length) {
177     if (strncmp(str + cur_token, "From ", 5) == 0) {
178       cur_token += 5;
179       while (str[cur_token] != '\n') {
180         cur_token ++;
181         if (cur_token >= length)
182           break;
183       }
184       if (cur_token < length) {
185         cur_token ++;
186         headers = cur_token;
187         start_len = headers - start;
188       }
189     }
190   }
191 
192   next = length;
193 
194   r = claws_mailmbox_fields_parse(str, length, &cur_token,
195 			    &uid, &headers_len);
196   if (r != MAILMBOX_NO_ERROR)
197     return r;
198 
199   /* save position */
200 #if 0
201   begin = cur_token;
202 #endif
203 
204   mailimf_crlf_parse(str, length, &cur_token);
205 
206 #if 0
207   if (str[cur_token] == 'F') {
208     printf("start !\n");
209     printf("%50.50s\n", str + cur_token);
210     getchar();
211   }
212 #endif
213 
214   body = cur_token;
215 
216   /* restore position */
217   /*  cur_token = begin; */
218 
219   state = FIRST_LF;
220 
221   end = length;
222 
223 #if 0
224   in_mail_data = 0;
225 #endif
226   while (state != OUT_MAIL) {
227 
228     if (cur_token >= length) {
229       if (state == IN_MAIL)
230 	end = length;
231       next = length;
232       break;
233     }
234 
235     switch(state) {
236     case IN_MAIL:
237       switch(str[cur_token]) {
238       case '\r':
239         state = FIRST_CR;
240         break;
241       case '\n':
242         state = FIRST_LF;
243         break;
244       case 'F':
245         if (cur_token == body) {
246           end = cur_token;
247           next = cur_token;
248           state = PARSING_F;
249         }
250         break;
251 #if 0
252       default:
253         in_mail_data = 1;
254         break;
255 #endif
256       }
257       break;
258 
259     case FIRST_CR:
260       end = cur_token;
261       switch(str[cur_token]) {
262       case '\r':
263         state = SECOND_CR;
264         break;
265       case '\n':
266         state = FIRST_LF;
267         break;
268       default:
269         state = IN_MAIL;
270 #if 0
271         in_mail_data = 1;
272 #endif
273         break;
274       }
275       break;
276 
277     case FIRST_LF:
278       end = cur_token;
279       switch(str[cur_token]) {
280       case '\r':
281         state = SECOND_CR;
282         break;
283       case '\n':
284         state = SECOND_LF;
285         break;
286       default:
287         state = IN_MAIL;
288 #if 0
289         in_mail_data = 1;
290 #endif
291         break;
292       }
293       break;
294 
295     case SECOND_CR:
296       switch(str[cur_token]) {
297         case '\r':
298           end = cur_token;
299           break;
300         case '\n':
301           state = SECOND_LF;
302           break;
303         case 'F':
304           next = cur_token;
305           state = PARSING_F;
306           break;
307         default:
308           state = IN_MAIL;
309 #if 0
310           in_mail_data = 1;
311 #endif
312           break;
313       }
314       break;
315 
316     case SECOND_LF:
317       switch(str[cur_token]) {
318         case '\r':
319           state = SECOND_CR;
320           break;
321         case '\n':
322           end = cur_token;
323           break;
324         case 'F':
325           next = cur_token;
326           state = PARSING_F;
327           break;
328         default:
329           state = IN_MAIL;
330 #if 0
331           in_mail_data = 1;
332 #endif
333           break;
334       }
335       break;
336 
337     case PARSING_F:
338       switch(str[cur_token]) {
339         case 'r':
340           state = PARSING_R;
341           break;
342         default:
343           state = IN_MAIL;
344 #if 0
345           in_mail_data = 1;
346 #endif
347           break;
348       }
349       break;
350 
351     case PARSING_R:
352       switch(str[cur_token]) {
353         case 'o':
354           state = PARSING_O;
355           break;
356         default:
357           state = IN_MAIL;
358 #if 0
359           in_mail_data = 1;
360 #endif
361           break;
362       }
363       break;
364 
365     case PARSING_O:
366       switch(str[cur_token]) {
367         case 'm':
368           state = PARSING_M;
369           break;
370         default:
371           state = IN_MAIL;
372 #if 0
373           in_mail_data = 1;
374 #endif
375           break;
376       }
377       break;
378 
379     case PARSING_M:
380       switch(str[cur_token]) {
381         case ' ':
382           state = OUT_MAIL;
383           break;
384       default:
385           state = IN_MAIL;
386           break;
387       }
388       break;
389     }
390 
391     cur_token ++;
392   }
393 
394   message_length = end - start;
395 
396   * pstart = start;
397   * pstart_len = start_len;
398   * pheaders = headers;
399   * pheaders_len = headers_len;
400   * pbody = body;
401   * pbody_len = end - body;
402   * psize = message_length;
403   * ppadding = next - end;
404   * puid = uid;
405 
406   * index = next;
407 
408   return MAILMBOX_NO_ERROR;
409 }
410 
411 
412 int
claws_mailmbox_parse_additionnal(struct claws_mailmbox_folder * folder,size_t * index)413 claws_mailmbox_parse_additionnal(struct claws_mailmbox_folder * folder,
414 			   size_t * index)
415 {
416   size_t cur_token;
417 
418   size_t start;
419   size_t start_len;
420   size_t headers;
421   size_t headers_len;
422   size_t body;
423   size_t body_len;
424   size_t size;
425   size_t padding;
426   uint32_t uid;
427   int r;
428   int res;
429 
430   uint32_t max_uid;
431   uint32_t first_index;
432   unsigned int i;
433   unsigned int j;
434 
435   cur_token = * index;
436 
437   /* remove temporary UID that we will parse */
438 
439   first_index = carray_count(folder->mb_tab);
440 
441   for(i = 0 ; i < carray_count(folder->mb_tab) ; i++) {
442     struct claws_mailmbox_msg_info * info;
443 
444     info = carray_get(folder->mb_tab, i);
445 
446     if (info->msg_start < cur_token) {
447       continue;
448     }
449 
450     if (!info->msg_written_uid) {
451       chashdatum key;
452 
453       key.data = &info->msg_uid;
454       key.len = sizeof(info->msg_uid);
455 
456       chash_delete(folder->mb_hash, &key, NULL);
457       carray_delete_fast(folder->mb_tab, i);
458       claws_mailmbox_msg_info_free(info);
459       if (i < first_index)
460 	first_index = i;
461     }
462   }
463 
464   /* make a sequence in the table */
465 
466   max_uid = folder->mb_written_uid;
467 
468   i = 0;
469   j = 0;
470   while (i < carray_count(folder->mb_tab)) {
471     struct claws_mailmbox_msg_info * info;
472 
473     info = carray_get(folder->mb_tab, i);
474     if (info != NULL) {
475       carray_set(folder->mb_tab, j, info);
476 
477       if (info->msg_uid > max_uid)
478 	max_uid = info->msg_uid;
479 
480       info->msg_index = j;
481       j ++;
482     }
483     i ++;
484   }
485   carray_set_size(folder->mb_tab, j);
486 
487   /* parse content */
488 
489   first_index = j;
490 
491   while (1) {
492     struct claws_mailmbox_msg_info * info;
493     chashdatum key;
494     chashdatum data;
495 
496     r = claws_mailmbox_single_parse(folder->mb_mapping, folder->mb_mapping_size,
497 			      &cur_token,
498 			      &start, &start_len,
499 			      &headers, &headers_len,
500 			      &body, &body_len,
501 			      &size, &padding, &uid);
502     if (r == MAILMBOX_NO_ERROR) {
503       /* do nothing */
504     }
505     else if (r == MAILMBOX_ERROR_PARSE) {
506       break;
507     } else {
508       res = r;
509       goto err;
510     }
511 
512     key.data = &uid;
513     key.len = sizeof(uid);
514 
515     r = chash_get(folder->mb_hash, &key, &data);
516     if (r == 0) {
517       info = data.data;
518 
519       if (!info->msg_written_uid) {
520 	/* some new mail has been written and override an
521 	   existing temporary UID */
522 
523 	chash_delete(folder->mb_hash, &key, NULL);
524 	info->msg_uid = 0;
525 
526 	if (info->msg_index < first_index)
527 	  first_index = info->msg_index;
528       }
529       else
530         uid = 0;
531     }
532 
533     if (uid > max_uid)
534       max_uid = uid;
535 
536     r = claws_mailmbox_msg_info_update(folder,
537 				 start, start_len, headers, headers_len,
538 				 body, body_len, size, padding, uid);
539     if (r != MAILMBOX_NO_ERROR) {
540       debug_print("claws_mailmbox_msg_info_update failed with %d\n", r);
541       res = r;
542       goto err;
543     }
544   }
545 
546   * index = cur_token;
547 
548   folder->mb_written_uid = max_uid;
549 
550   /* attribute uid */
551 
552   for(i = first_index ; i < carray_count(folder->mb_tab) ; i ++) {
553     struct claws_mailmbox_msg_info * info;
554     chashdatum key;
555     chashdatum data;
556 
557     info = carray_get(folder->mb_tab, i);
558 
559     if (info->msg_uid != 0) {
560       continue;
561     }
562 
563     max_uid ++;
564     info->msg_uid = max_uid;
565 
566     key.data = &info->msg_uid;
567     key.len = sizeof(info->msg_uid);
568     data.data = info;
569     data.len = 0;
570 
571     r = chash_set(folder->mb_hash, &key, &data, NULL);
572     if (r < 0) {
573       debug_print("chash_set failed with %d\n", r);
574       res = MAILMBOX_ERROR_MEMORY;
575       goto err;
576     }
577   }
578 
579   folder->mb_max_uid = max_uid;
580 
581   return MAILMBOX_NO_ERROR;
582 
583  err:
584   return res;
585 }
586 
flush_uid(struct claws_mailmbox_folder * folder)587 static void flush_uid(struct claws_mailmbox_folder * folder)
588 {
589   unsigned int i;
590 
591   for(i = 0 ; i < carray_count(folder->mb_tab) ; i++) {
592     struct claws_mailmbox_msg_info * info;
593 
594     info = carray_get(folder->mb_tab, i);
595     if (info != NULL)
596       claws_mailmbox_msg_info_free(info);
597   }
598 
599   chash_clear(folder->mb_hash);
600   carray_set_size(folder->mb_tab, 0);
601 }
602 
claws_mailmbox_parse(struct claws_mailmbox_folder * folder)603 int claws_mailmbox_parse(struct claws_mailmbox_folder * folder)
604 {
605   int r;
606   int res;
607   size_t cur_token;
608 
609   flush_uid(folder);
610 
611   cur_token = 0;
612 
613   r = claws_mailmbox_parse_additionnal(folder, &cur_token);
614 
615   if (r != MAILMBOX_NO_ERROR) {
616     res = r;
617     goto err;
618   }
619 
620   return MAILMBOX_NO_ERROR;
621 
622  err:
623   return res;
624 }
625