eee41b83dbeb0b299189b9700329868d1959b98a
[platform/upstream/evolution-data-server.git] / camel / providers / mbox / camel-mbox-parser.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-mbox-parser.c : mbox folder parser */
3
4 /* 
5  *
6  * Copyright (C) 1999 Bertrand Guiheneuf <bertrand@helixcode.com> .
7  *
8  * This program is free software; you can redistribute it and/or 
9  * modify it under the terms of the GNU General Public License as 
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21  * USA
22  */
23
24
25 #include <config.h> 
26 #include "camel-mbox-parser.h"
27 #include "camel-log.h"
28 #include "camel-exception.h"
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35
36
37
38 #define MBOX_PARSER_BUF_SIZE 10000
39  
40 #define MBOX_PARSER_FROM_KW "from:"               
41 #define MBOX_PARSER_FROM_KW_SZ 5
42
43 #define MBOX_PARSER_DATE_KW "date:"
44 #define MBOX_PARSER_DATE_KW_SZ 5
45
46 #define MBOX_PARSER_SUBJECT_KW "subject:"
47 #define MBOX_PARSER_SUBJECT_KW_SZ 8
48
49 #define MBOX_PARSER_X_EVOLUTION_KW "x-evolution:"
50 #define MBOX_PARSER_X_EVOLUTION_KW_SZ 12
51
52 /* the maximum lentgh of all the previous keywords */
53 #define MBOX_PARSER_MAX_KW_SIZE 12
54
55
56 #define MBOX_PARSER_SUMMARY_SIZE 150
57
58
59
60
61
62
63 typedef struct {
64         
65         int fd;                          /* file descriptor of the mbox file */
66         glong real_position;             /* real position in the file */
67
68         
69         gchar *message_delimiter;        /* message delimiter string */
70         guint message_delimiter_length;
71
72         guint message_summary_size;      /* how many characters from the begining of the 
73                                            mail to put into the message summary */
74         
75         GArray *preparsed_messages;      /* array of MessagePreParsingInfo */
76         CamelMboxParserMessageInfo current_message_info;  /* used to store curent info */
77         gboolean is_pending_message;     /* is there some message information pending ? */
78
79         /* buffer info */
80         gchar *buffer;                   /* temporary buffer */
81         guint left_chunk_size;           /* size of the left chunk in the temp buffer */
82         guint last_position;             /* last position that can be compared to a keyword */
83         guint current_position;          /* current position in the temp buffer */
84         gboolean eof;                    /* did we read the entire file */
85
86         /* other */
87         GString *tmp_string;             /* temporary string to fill the headers in */
88
89         
90         
91 } CamelMboxPreParser;
92
93
94 /* clear a preparsing info structure */
95 static void
96 clear_message_info (CamelMboxParserMessageInfo *preparsing_info)
97 {
98         preparsing_info->message_position = 0;
99         preparsing_info->size = 0;
100         preparsing_info->from = NULL;
101         preparsing_info->date = NULL;
102         preparsing_info->subject = NULL;
103         preparsing_info->status = NULL;
104         preparsing_info->priority = NULL;
105         preparsing_info->references = NULL;
106         preparsing_info->body_summary = NULL;
107         preparsing_info->end_of_headers_offset = 0;
108         
109         preparsing_info->x_evolution = NULL;
110         preparsing_info->x_evolution_offset = 0;
111         /* reparsing_info->x_evolution_length = 0; */
112                 
113 }
114
115
116
117 /**
118  * new_parser: create a new parser object
119  * @fd: file descriptor opened on the mbox file
120  * @message_delimiter: the string that announce the start of a new message.
121  * 
122  * Create a new parser object. This object is the place where are 
123  * stored all the information concerning the parsing process. 
124  * 
125  * Return value: The newly created parser object.
126  **/
127 static CamelMboxPreParser *
128 new_parser (int fd,
129             const gchar *message_delimiter) 
130 {
131         
132         CamelMboxPreParser *parser;
133
134         parser = g_new0 (CamelMboxPreParser, 1);
135         
136         parser->fd = fd;
137         parser->buffer = g_new (gchar, MBOX_PARSER_BUF_SIZE);
138         parser->current_position = 0;
139         parser->message_delimiter = g_strdup (message_delimiter);
140         parser->message_delimiter_length = strlen (message_delimiter);
141         parser->real_position = 0;      
142         parser->preparsed_messages = g_array_new (FALSE, FALSE, sizeof (CamelMboxParserMessageInfo));
143         parser->message_summary_size = MBOX_PARSER_SUMMARY_SIZE;
144         
145         parser->left_chunk_size = MAX (parser->message_delimiter_length, MBOX_PARSER_MAX_KW_SIZE);
146         parser->eof = FALSE;
147         
148         parser->tmp_string = g_string_sized_new (1000);
149
150         return parser;
151 }
152
153
154
155 /**
156  * parser_free: free the parser object
157  * @parser: the parser objet to free.
158  * 
159  * it is important to notice that all structures allocated
160  * in new_parser () are freed ** EXCEPT ** the message
161  * information array, i.e. the preparsed_messages 
162  * field. 
163  **/
164 static void 
165 parser_free (CamelMboxPreParser *parser)
166 {
167         g_free (parser->buffer);
168         g_free (parser->message_delimiter);
169         g_string_free (parser->tmp_string, TRUE);
170         g_free (parser);
171         
172 }
173
174
175
176
177 /* ** handle exceptions here */
178 /**
179  * initialize_buffer: read the first chunk of data in the buffer
180  * @parser: parser object to fill
181  * @first_position: position to start the read at
182  * 
183  * read the first chunk of data from the mbox file. 
184  * 
185  **/
186 static void 
187 initialize_buffer (CamelMboxPreParser *parser,
188                    glong first_position)
189 {
190         gint seek_res;
191         gint buf_nb_read;
192
193         g_assert (parser);
194
195         /* set the search start position */
196         seek_res = lseek (parser->fd, first_position, SEEK_SET);
197         //if (seek_res == (off_t)-1) goto io_error;
198         
199         
200         /* the first part of the buffer is filled with newlines, 
201            but the next time a chunk of buffer is read, it will
202            be filled with the last bytes of the previous chunk. 
203            This allows simple g_strcasecmp to test for the presence of 
204            the keyword */
205         memset (parser->buffer, '\n', parser->left_chunk_size);
206         do {
207                 buf_nb_read = read (parser->fd, parser->buffer + parser->left_chunk_size, 
208                                     MBOX_PARSER_BUF_SIZE - parser->left_chunk_size);
209         } while ((buf_nb_read == -1) && (errno == EINTR));
210         /* ** check for an error here */
211
212         parser->last_position = buf_nb_read;
213
214         if (buf_nb_read == 0)
215                 parser->eof =TRUE;
216
217         parser->current_position = parser->left_chunk_size;
218 }
219
220
221
222
223 /**
224  * read_next_buffer_chunk: read the next chunk of data in the mbox file
225  * @parser: parser object
226  * 
227  * read the next chunk of data in the mbox file. 
228  * Routine copies the last part of the buffer at 
229  * the begining are concatenate the read data to
230  * it. This allows strcmp of keywords in the buffer,
231  * until the last postion. That means you can 
232  * do a strcmp (buffer, keyword) for any of the
233  * keyword defined at the begining of this file. 
234  * 
235  **/
236 static void 
237 read_next_buffer_chunk (CamelMboxPreParser *parser)
238 {
239         gint buf_nb_read;
240
241         g_assert (parser);
242         
243         /* read the next chunk of data in the folder file  : */
244         /*  -   first, copy the last bytes from the previous 
245             chunk at the begining of the new one. */
246         memcpy (parser->buffer, 
247                 parser->buffer + MBOX_PARSER_BUF_SIZE - parser->left_chunk_size, 
248                 parser->left_chunk_size);
249
250         /*  -   then read the next chunk on disk */
251         do {
252                 buf_nb_read = read (parser->fd, 
253                                     parser->buffer + parser->left_chunk_size, 
254                                     MBOX_PARSER_BUF_SIZE - parser->left_chunk_size);    
255         } while ((buf_nb_read == -1) && (errno == EINTR));
256         /* ** check for an error here */
257
258         parser->last_position = buf_nb_read;
259
260         if (buf_nb_read == 0)
261                 parser->eof =TRUE;
262
263         parser->current_position = 0;
264         
265 }
266
267
268
269 /**
270  * goto_next_char: go one postion forward in the buffer
271  * @parser: parser object
272  * 
273  * goto one position forward in the buffer. If necessary, 
274  * read the next chunk of data in the file, possibly
275  * raising the parser->eof flag. 
276  * 
277  **/
278 static void 
279 goto_next_char (CamelMboxPreParser *parser) 
280 {       
281         if (parser->current_position < parser->last_position - 1)
282                         parser->current_position++;
283         else 
284                 read_next_buffer_chunk (parser);
285
286         parser->real_position++;
287 }
288
289
290
291
292
293
294
295 /**
296  * advance_n_chars: go n positions forward in the buffer.
297  * @parser: parser object
298  * @n: number of characters to advance. 
299  * 
300  **/
301 static void
302 advance_n_chars (CamelMboxPreParser *parser, guint n) 
303 {       
304         
305         gint position_to_the_end;
306
307         position_to_the_end = parser->last_position - parser->current_position;
308
309         if (n < position_to_the_end)
310                         parser->current_position += n;
311         else {
312                 printf ("Advance %d chars\n", n);
313                 printf ("Last position = %d\n", parser->last_position);
314                 printf ("Current position = %d\n", parser->current_position);
315                 read_next_buffer_chunk (parser);
316                 parser->current_position = n - position_to_the_end;
317                 printf ("New position = %d\n", parser->current_position);
318         }
319         
320         parser->real_position += n;
321 }
322
323
324
325
326
327
328 /* called when the buffer has detected the begining of 
329    a new message. This routine is supposed to simply 
330    store the previous message information and 
331    clean the temporary structure used to store 
332    the informations */
333
334
335 /**
336  * new_message_detected: routine to call when a new message has been detected
337  * @parser: parser object.
338  * 
339  * this routine must be called when the keyword determining the 
340  * begining of a new message has been detected. It pushes the
341  * information fetched for the last message into the message information
342  * array. Also, it gets the parser to the end of the line. 
343  **/
344 static void 
345 new_message_detected (CamelMboxPreParser *parser)
346 {
347         
348         gchar c;
349
350         /* if we were filling a message information 
351            save it in the message information array */ 
352
353         if (parser->is_pending_message) {
354                 parser->current_message_info.size = 
355                         parser->real_position - parser->current_message_info.message_position;
356                 g_array_append_vals (parser->preparsed_messages, (gchar *)parser + 
357                                     G_STRUCT_OFFSET (CamelMboxPreParser, current_message_info), 1);
358         }
359         
360         clear_message_info ( &(parser->current_message_info));
361
362         /* go to the end of the line */
363         do {
364
365                 c = parser->buffer[parser->current_position];
366                 goto_next_char (parser);        
367
368         } while (c != '\n');
369         
370         /* save message position in the message information structure */
371         (parser->current_message_info).message_position = parser->real_position;
372
373         parser->is_pending_message = TRUE;
374                 
375 }
376
377
378
379
380
381
382
383 /**
384  * read_header: read the header content contained after the current position.
385  * @parser: the parser object.
386  * @header_content: a pointer on a (char *) variable to feed with the obtained header string. 
387  * 
388  * This routine must be called when the parser has detected a header 
389  * and it wants the header content to be stored. The parser current position 
390  * must EXACTELY be located at the begining of the header content line. 
391  * For example, if the file contains the line : 
392  *     from:Bertrand Guiheneuf <bertrand@helixcode.com>
393  *
394  * When this routine is called, the parser must be located 
395  * on the "B" of "Bertrand".
396  * 
397  * When this routine returns, the parser is located just 
398  * after the "\n" at the end of the header content.
399  *
400  **/
401 static void 
402 read_header (CamelMboxPreParser *parser, gchar **header_content)
403 {
404         gboolean space = FALSE;
405         gboolean newline = FALSE;
406         gboolean header_end = FALSE;
407         gchar *buffer;
408         gchar c;
409         
410
411         g_assert (parser);
412
413         /* reset the header buffer string */
414         parser->tmp_string = g_string_truncate (parser->tmp_string, 0);
415
416         buffer = parser->buffer;
417
418         while (! (parser->eof || header_end) ) {
419                 
420                 /* read the current character */
421                 c = buffer[parser->current_position];
422                 
423                 if (space) {
424                         if (c == ' ' && c == '\t')
425                                 goto next_char;
426                         else
427                                 space = FALSE;
428                 }
429
430                 if (newline) {
431                         if (c == ' ' && c == '\t') {
432
433                                 space = TRUE;
434                                 newline = FALSE;
435                                 goto next_char;
436                         } else {
437
438                                 header_end = TRUE;
439                                 continue;
440                         }
441                 }
442
443                 if (c == '\n') {
444                         newline = TRUE;
445                         goto next_char;
446                 }
447
448                 /* feed the header content */
449                 parser->tmp_string = g_string_append_c (parser->tmp_string, c);
450
451         next_char: /* read next char in the buffer */
452                 goto_next_char (parser);
453         }
454
455         
456         /* copy the buffer in the preparsing information structure */
457         *header_content = g_strndup (parser->tmp_string->str, parser->tmp_string->len); 
458 }
459
460
461
462
463
464
465
466 /**
467  * read_message_begining: read the first characters of a message body
468  * @parser: parser object
469  * @message_summary: a pointer on a (gchar *) variable where the obtained string will be stored. 
470  * 
471  * Read the first lines of a message. When calling this routine, the
472  * parser must be located at the begining of the message body. 
473  * 
474  * Return value: if the parsing inside this routine last read a newline, then %TRUE is returned, otherwise %FALSE is returned
475  **/
476 static gboolean
477 read_message_begining (CamelMboxPreParser *parser, gchar **message_summary)
478 {
479         guint nb_read = 0;
480         gchar *buffer;
481         gboolean new_message = FALSE;
482         guint nb_line = 0;
483         g_assert (parser);
484         
485         /* reset the header buffer string */
486         parser->tmp_string = g_string_truncate (parser->tmp_string, 0);
487         
488         buffer = parser->buffer; 
489         /* the message should not be filled character by
490            character but there is no g_string_n_append 
491            function, so for the moment, this is a lazy 
492            implementation */
493         while (! (parser->eof) && (nb_line <2) && (nb_read<parser->message_summary_size) && (!new_message)) {
494                 
495                 
496                 /* test if we are not at the end of the message */
497                 if (buffer[parser->current_position] == '\n') {
498                         
499                         nb_line++;
500                         goto_next_char (parser);
501                         if ((parser->eof) || (g_strncasecmp (parser->buffer + parser->current_position, 
502                                            parser->message_delimiter, 
503                                            parser->message_delimiter_length) == 0)) {                           
504                                 new_message = TRUE;
505                                 continue;
506                         } else {
507                                 /* we're not at the end, so let's just add the cr to the summary */
508                                 parser->tmp_string = g_string_append_c (parser->tmp_string, 
509                                                         '\n');
510                                 nb_read++;
511                                 continue;                               
512                         }
513                                 
514                                 
515                 }
516                 
517                 parser->tmp_string = g_string_append_c (parser->tmp_string, 
518                                                         buffer[parser->current_position]);
519                 nb_read++;
520                 goto_next_char (parser);
521         }
522         
523         *message_summary = g_strndup (parser->tmp_string->str, parser->tmp_string->len);
524         
525         return new_message;
526 }
527
528
529
530
531
532
533
534
535
536
537 /**
538  * camel_mbox_parse_file: read an mbox file and parse it. 
539  * @fd: file descriptor opened on the mbox file.
540  * @message_delimiter: character string delimiting the beginig of a new message 
541  * @start_position: poition in the file where to start the parsing. 
542  * @get_message_summary: should the parser retrieve the begining of the messages
543  * @status_callback: function to call peridically to indicate the progress of the parser
544  * @status_interval: floating value between 0 and 1 indicate how often to call @status_callback. 
545  * @user_data: user data that will be passed to the callback function
546  * 
547  * This routine parses an mbox file and retreives both the message starting positions and 
548  * some of the informations contained in the message. Those informations are mainly 
549  * some RFC822 headers values but also (optionally) the first characters of the mail 
550  * body. The @get_message_summary parameter allows to enable or disable this option.
551  * 
552  * 
553  * Return value: An array of CamelMboxParserMessageInfo containing the informations on each message parsed in the file
554  **/
555 GArray *
556 camel_mbox_parse_file (int fd, 
557                        const gchar *message_delimiter,
558                        glong start_position,
559                        gboolean get_message_summary,
560                        camel_mbox_preparser_status_callback *status_callback,
561                        double status_interval,
562                        gpointer user_data)
563 {
564         CamelMboxPreParser *parser;
565         gboolean is_parsing_a_message = FALSE;
566         gchar c;
567         struct stat stat_buf;
568         gint fstat_result;
569         glong total_file_size;
570         int last_status = 0;
571         int real_interval;
572         gboolean newline;
573         GArray *return_value;
574
575         /* get file size */
576         fstat_result = fstat (fd, &stat_buf);
577         if (fstat_result == -1) {
578                 g_warning ("Manage exception here \n");
579         }
580                 
581         total_file_size = stat_buf.st_size;
582         real_interval = status_interval * total_file_size;
583         
584         
585         /* create the parser */
586         parser = new_parser (fd, message_delimiter);
587         
588         /* initialize the temporary char buffer */
589         initialize_buffer (parser, start_position);
590         
591         /* the first line is indeed at the begining of a new line ... */
592         newline = TRUE;
593
594         while (!parser->eof) {
595
596                 
597
598                 
599                 /* read the current character */
600                 if (!newline) {
601                         c = parser->buffer[parser->current_position];
602                         newline = (c == '\n');
603                         goto_next_char (parser);
604                 }
605                         
606                 if (newline) {
607                         
608                         /* check if we reached a status milestone */
609                         if ( status_callback && ((parser->real_position - last_status) > real_interval)) {
610                                 last_status += real_interval;
611                                 status_callback ((double)last_status / (double)total_file_size, 
612                                                  user_data);
613                         }
614                         
615                         /* is the next part a message delimiter ? */
616                         if (g_strncasecmp (parser->buffer + parser->current_position, 
617                                            parser->message_delimiter, 
618                                            parser->message_delimiter_length) == 0) {
619                                 
620                                 is_parsing_a_message = TRUE;
621                                 new_message_detected (parser);
622                                 newline = TRUE;
623                                 continue;
624                         }
625                         
626                         
627                         if (is_parsing_a_message) {
628                                 /* we could find the headers in a clever way, like
629                                    storing them in a list of pair 
630                                    [keyword, offset_in_CamelMboxParserMessageInfo]
631                                    I am too busy for now. Contribution welcome */
632                                    
633                                 /* is the next part a "from" header ? */
634                                 if (g_strncasecmp (parser->buffer + parser->current_position, 
635                                                   MBOX_PARSER_FROM_KW, 
636                                                   MBOX_PARSER_FROM_KW_SZ) == 0) {
637                                         
638                                         advance_n_chars (parser, MBOX_PARSER_FROM_KW_SZ);
639                                         read_header (parser, (gchar **) ((gchar *)parser +
640                                                      G_STRUCT_OFFSET (CamelMboxPreParser, current_message_info) + 
641                                                      G_STRUCT_OFFSET (CamelMboxParserMessageInfo, from)));
642                                         
643                                         newline = TRUE;
644                                         continue;
645                                 }
646
647                                 /* is the next part a "Date" header ? */
648                                 if (g_strncasecmp (parser->buffer + parser->current_position, 
649                                                   MBOX_PARSER_DATE_KW, 
650                                                   MBOX_PARSER_DATE_KW_SZ) == 0) {
651                                         
652                                         advance_n_chars (parser, MBOX_PARSER_DATE_KW_SZ);
653                                         read_header (parser, (gchar **) ((gchar *)parser +
654                                                      G_STRUCT_OFFSET (CamelMboxPreParser, current_message_info) + 
655                                                      G_STRUCT_OFFSET (CamelMboxParserMessageInfo, date)));
656                                         
657                                         newline = TRUE;
658                                         continue;
659                                 }
660
661
662                                 /* is the next part a "Subject" header ? */
663                                 if (g_strncasecmp (parser->buffer + parser->current_position, 
664                                                   MBOX_PARSER_SUBJECT_KW, 
665                                                   MBOX_PARSER_SUBJECT_KW_SZ) == 0) {
666
667                                         advance_n_chars (parser, MBOX_PARSER_SUBJECT_KW_SZ);
668                                         read_header (parser, (gchar **) ((gchar *)parser +
669                                                      G_STRUCT_OFFSET (CamelMboxPreParser, current_message_info) + 
670                                                      G_STRUCT_OFFSET (CamelMboxParserMessageInfo, subject)));
671
672                                         newline = TRUE;
673                                         continue;
674                                 }
675
676
677                                 /* is the next part a "X-evolution" header ? */
678                                 if (g_strncasecmp (parser->buffer + parser->current_position, 
679                                                   MBOX_PARSER_X_EVOLUTION_KW, 
680                                                   MBOX_PARSER_X_EVOLUTION_KW_SZ) == 0) {
681
682                                         /* in the case of the evolution private field, we store 
683                                            the field position as well as its length because 
684                                            we will have to change them */
685                                         parser->current_message_info.x_evolution_offset = parser->real_position
686                                                 - parser->current_message_info.message_position;
687                                         advance_n_chars (parser, MBOX_PARSER_X_EVOLUTION_KW_SZ);
688                                         read_header (parser, (gchar **) ((gchar *)parser +
689                                                      G_STRUCT_OFFSET (CamelMboxPreParser, current_message_info) + 
690                                                      G_STRUCT_OFFSET (CamelMboxParserMessageInfo, x_evolution)));
691
692                                         /* 
693                                            parser->current_message_info.x_evolution_length = 
694                                            parser->real_position - parser->current_message_info.x_evolution_position;
695                                         */
696                                         newline = TRUE;
697                                         continue;
698                                 }
699
700
701                                 
702
703                                 /* is it an empty line ? */
704                                 if (parser->buffer[parser->current_position] == '\n') {
705                                         
706                                         parser->current_message_info.end_of_headers_offset = 
707                                                 parser->real_position - parser->current_message_info.message_position;
708
709                                         goto_next_char (parser);
710                                         if (get_message_summary)
711                                                 newline = read_message_begining (parser, (gchar **) ((gchar *)parser +
712                                                            G_STRUCT_OFFSET (CamelMboxPreParser, current_message_info) + 
713                                                            G_STRUCT_OFFSET (CamelMboxParserMessageInfo, body_summary)));
714                                         
715                                         is_parsing_a_message = FALSE;
716                                         continue;
717                                 }
718                         }
719                         newline = FALSE;
720                 }
721                 
722         }
723         
724         /* if there is a pending message information put it in the array */
725         if (parser->is_pending_message) {
726                 g_array_append_vals (parser->preparsed_messages, (gchar *)parser + 
727                                      G_STRUCT_OFFSET (CamelMboxPreParser, current_message_info), 1);    
728         }
729
730         return_value = parser->preparsed_messages;
731
732         /* free the parser */
733         parser_free (parser);
734
735         return return_value;
736 }
737
738
739
740
741
742
743
744
745
746
747 #ifdef MBOX_PARSER_TEST
748 /* to build the test : 
749    
750    gcc -O3 -I/opt/gnome/lib/glib/include  `glib-config --cflags` -o test_parser -DMBOX_PARSER_TEST -I ../.. -I ../../..  -I /usr/lib/glib/include camel-mbox-parser.c  `glib-config --libs` -lm
751
752    
753  */
754
755
756 #include <math.h>
757
758 static void 
759 status (double done, gpointer user_data)
760 {
761         printf ("%d %% done\n", (int)floor (done * 100));
762 }
763 int 
764 main (int argc, char **argv)
765 {
766         int test_file_fd;
767         int i;
768         GArray *message_positions; 
769         CamelMboxParserMessageInfo *message_info;
770         gchar tmp_buffer[50];
771
772         tmp_buffer[49] = '\0';
773
774         test_file_fd = open (argv[1], O_RDONLY);
775         message_positions = camel_mbox_parse_file (test_file_fd, 
776                                                    "From ", 
777
778                                                    0,
779                                                    TRUE,
780                                                    status,
781                                                    0.05,
782                                                    NULL);
783
784         printf ("Found %d messages \n", message_positions->len);
785         
786
787         for (i=0; i<message_positions->len; i++) {
788                 
789                 message_info = ((CamelMboxParserMessageInfo *)(message_positions->data)) + i;
790                 printf ("\n\n** Message %d : \n", i);
791                 printf ("Size : %d\n", message_info->size);
792                 printf ("From: %s\n", message_info->from);
793                 printf ("Date: %s\n", message_info->date);
794                 printf ("Subject: %s\n", message_info->subject);
795                 printf ("Summary: %s\n", message_info->body_summary) ;
796                 
797
798                 lseek (test_file_fd, message_info->message_position, SEEK_SET);
799                 read (test_file_fd, tmp_buffer, 49);
800                 printf ("File content at position %d : \n===\n%s\n===\n", message_info->message_position, tmp_buffer);
801                 
802         }
803
804
805
806         return 0;
807 }
808
809
810
811
812 #endif /* MBOX_PARSER_TEST */