1 /*
2 * libEtPan! -- a mail stuff library
3 *
4 * Copyright (C) 2001, 2005 - 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 AUTHORS 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 AUTHORS 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: feeddriver.c,v 1.3 2008/04/11 07:33:08 hoa Exp $
34 */
35
36 #ifdef HAVE_CONFIG_H
37 # include <config.h>
38 #endif
39
40 #include "feeddriver.h"
41
42 #include <string.h>
43 #include <stdlib.h>
44 #include <time.h>
45
46 #include "mailimf_types_helper.h"
47 #include "newsfeed.h"
48 #include "mail.h"
49 #include "mailmessage.h"
50 #include "maildriver_tools.h"
51 #include "feeddriver_message.h"
52 #include "feeddriver_types.h"
53
54 #define MIN_DELAY 5
55
56 static int feeddriver_initialize(mailsession * session);
57
58 static void feeddriver_uninitialize(mailsession * session);
59
60 static int feeddriver_connect_path(mailsession * session, const char * path);
61
62 static int feeddriver_status_folder(mailsession * session, const char * mb,
63 uint32_t * result_messages,
64 uint32_t * result_recent,
65 uint32_t * result_unseen);
66
67 static int feeddriver_messages_number(mailsession * session, const char * mb,
68 uint32_t * result);
69
70 static int
71 feeddriver_get_envelopes_list(mailsession * session,
72 struct mailmessage_list * env_list);
73
74
75 static int feeddriver_get_messages_list(mailsession * session,
76 struct mailmessage_list ** result);
77
78 static int feeddriver_get_message(mailsession * session,
79 uint32_t num, mailmessage ** result);
80
81 static int feeddriver_get_message_by_uid(mailsession * session,
82 const char * uid,
83 mailmessage ** result);
84
85 static mailsession_driver local_feed_session_driver = {
86 /* sess_name */ "feed",
87
88 /* sess_initialize */ feeddriver_initialize,
89 /* sess_uninitialize */ feeddriver_uninitialize,
90
91 /* sess_parameters */ NULL,
92
93 /* sess_connect_stream */ NULL,
94 /* sess_connect_path */ feeddriver_connect_path,
95 /* sess_starttls */ NULL,
96 /* sess_login */ NULL,
97 /* sess_logout */ NULL,
98 /* sess_noop */ NULL,
99
100 /* sess_build_folder_name */ NULL,
101 /* sess_create_folder */ NULL,
102 /* sess_delete_folder */ NULL,
103 /* sess_rename_folder */ NULL,
104 /* sess_check_folder */ NULL,
105 /* sess_examine_folder */ NULL,
106 /* sess_select_folder */ NULL,
107 /* sess_expunge_folder */ NULL,
108 /* sess_status_folder */ feeddriver_status_folder,
109 /* sess_messages_number */ feeddriver_messages_number,
110 /* sess_recent_number */ feeddriver_messages_number,
111 /* sess_unseen_number */ feeddriver_messages_number,
112 /* sess_list_folders */ NULL,
113 /* sess_lsub_folders */ NULL,
114 /* sess_subscribe_folder */ NULL,
115 /* sess_unsubscribe_folder */ NULL,
116
117 /* sess_append_message */ NULL,
118 /* sess_append_message_flags */ NULL,
119 /* sess_copy_message */ NULL,
120 /* sess_move_message */ NULL,
121
122 /* sess_get_message */ feeddriver_get_message,
123 /* sess_get_message_by_uid */ feeddriver_get_message_by_uid,
124
125 /* sess_get_messages_list */ feeddriver_get_messages_list,
126 /* sess_get_envelopes_list */ feeddriver_get_envelopes_list,
127 /* sess_remove_message */ NULL,
128
129 /* sess_login_sasl */ NULL,
130 };
131
132
133 mailsession_driver * feed_session_driver = &local_feed_session_driver;
134
135 static void update(mailsession * session);
136
feeddriver_feed_error_to_mail_error(int error)137 static int feeddriver_feed_error_to_mail_error(int error)
138 {
139 switch (error) {
140 case NEWSFEED_NO_ERROR:
141 return MAIL_NO_ERROR;
142
143 case NEWSFEED_ERROR_CANCELLED:
144 return MAIL_ERROR_STREAM;
145
146 case NEWSFEED_ERROR_INTERNAL:
147 return MAIL_ERROR_UNKNOWN;
148
149 case NEWSFEED_ERROR_BADURL:
150 return MAIL_ERROR_INVAL;
151
152 case NEWSFEED_ERROR_RESOLVE_PROXY:
153 case NEWSFEED_ERROR_RESOLVE_HOST:
154 return MAIL_ERROR_CONNECT;
155
156 case NEWSFEED_ERROR_CONNECT:
157 return MAIL_ERROR_CONNECT;
158
159 case NEWSFEED_ERROR_STREAM:
160 return MAIL_ERROR_STREAM;
161
162 case NEWSFEED_ERROR_PROTOCOL:
163 case NEWSFEED_ERROR_PARSE:
164 return MAIL_ERROR_PARSE;
165
166 case NEWSFEED_ERROR_ACCESS:
167 return MAIL_ERROR_NO_PERMISSION;
168
169 case NEWSFEED_ERROR_AUTHENTICATION:
170 return MAIL_ERROR_LOGIN;
171
172 case NEWSFEED_ERROR_FTP:
173 return MAIL_ERROR_UNKNOWN;
174
175 case NEWSFEED_ERROR_PARTIAL_FILE:
176 case NEWSFEED_ERROR_FETCH:
177 return MAIL_ERROR_FETCH;
178
179 case NEWSFEED_ERROR_HTTP:
180 return MAIL_ERROR_UNKNOWN;
181
182 case NEWSFEED_ERROR_FILE:
183 return MAIL_ERROR_FILE;
184
185 case NEWSFEED_ERROR_PUT:
186 return MAIL_ERROR_APPEND;
187
188 case NEWSFEED_ERROR_MEMORY:
189 return MAIL_ERROR_MEMORY;
190
191 case NEWSFEED_ERROR_SSL:
192 return MAIL_ERROR_SSL;
193
194 case NEWSFEED_ERROR_LDAP:
195 return MAIL_ERROR_UNKNOWN;
196
197 case NEWSFEED_ERROR_UNSUPPORTED_PROTOCOL:
198 return MAIL_ERROR_INVAL;
199 }
200
201 return MAIL_ERROR_UNKNOWN;
202 }
203
204 static inline struct feed_session_state_data *
get_data(mailsession * session)205 get_data(mailsession * session)
206 {
207 return session->sess_data;
208 }
209
get_feed_session(mailsession * session)210 static inline struct newsfeed * get_feed_session(mailsession * session)
211 {
212 return get_data(session)->feed_session;
213 }
214
feeddriver_initialize(mailsession * session)215 static int feeddriver_initialize(mailsession * session)
216 {
217 struct feed_session_state_data * data;
218 struct newsfeed * feed;
219
220 feed = newsfeed_new();
221 if (feed == NULL)
222 goto err;
223
224 data = malloc(sizeof(* data));
225 if (data == NULL)
226 goto free;
227
228 data->feed_session = feed;
229 data->feed_error = MAIL_NO_ERROR;
230 session->sess_data = data;
231
232 return MAIL_NO_ERROR;
233
234 free:
235 newsfeed_free(feed);
236 err:
237 return MAIL_ERROR_MEMORY;
238 }
239
feeddriver_uninitialize(mailsession * session)240 static void feeddriver_uninitialize(mailsession * session)
241 {
242 struct feed_session_state_data * data;
243
244 data = get_data(session);
245
246 newsfeed_free(data->feed_session);
247 free(data);
248
249 session->sess_data = NULL;
250 }
251
feeddriver_connect_path(mailsession * session,const char * path)252 static int feeddriver_connect_path(mailsession * session, const char * path)
253 {
254 struct feed_session_state_data * data;
255 int r;
256
257 data = get_data(session);
258 r = newsfeed_set_url(data->feed_session, path);
259 return feeddriver_feed_error_to_mail_error(r);
260 }
261
feeddriver_status_folder(mailsession * session,const char * mb,uint32_t * result_messages,uint32_t * result_recent,uint32_t * result_unseen)262 static int feeddriver_status_folder(mailsession * session, const char * mb,
263 uint32_t * result_messages,
264 uint32_t * result_recent,
265 uint32_t * result_unseen)
266 {
267 uint32_t count;
268 int r;
269
270 r = feeddriver_messages_number(session, mb, &count);
271 if (r != MAIL_NO_ERROR)
272 return r;
273
274 * result_messages = count;
275 * result_recent = count;
276 * result_unseen = count;
277
278 return MAIL_NO_ERROR;
279 }
280
feeddriver_messages_number(mailsession * session,const char * mb,uint32_t * result)281 static int feeddriver_messages_number(mailsession * session, const char * mb,
282 uint32_t * result)
283 {
284 struct feed_session_state_data * data;
285 unsigned int count;
286 int res;
287
288 update(session);
289 data = get_data(session);
290 if (data->feed_error != MAIL_NO_ERROR) {
291 res = data->feed_error;
292 goto err;
293 }
294
295 count = newsfeed_item_list_get_count(data->feed_session);
296
297 * result = count;
298
299 return MAIL_NO_ERROR;
300
301 err:
302 return res;
303 }
304
update(mailsession * session)305 static void update(mailsession * session)
306 {
307 int r;
308 struct feed_session_state_data * data;
309 time_t value;
310
311 data = get_data(session);
312
313 value = time(NULL);
314 if (data->feed_last_update != (time_t) -1) {
315 if (value - data->feed_last_update < MIN_DELAY)
316 return;
317 }
318
319 r = newsfeed_update(data->feed_session, -1);
320 data->feed_error = feeddriver_feed_error_to_mail_error(r);
321 if (data->feed_error == MAIL_NO_ERROR) {
322 value = time(NULL);
323 data->feed_last_update = value;
324 }
325 }
326
327 static int
feeddriver_get_envelopes_list(mailsession * session,struct mailmessage_list * env_list)328 feeddriver_get_envelopes_list(mailsession * session,
329 struct mailmessage_list * env_list)
330 {
331 return MAIL_NO_ERROR;
332 }
333
to_be_quoted(const char * word,size_t size)334 static inline int to_be_quoted(const char * word, size_t size)
335 {
336 int do_quote;
337 const char * cur;
338 size_t i;
339
340 do_quote = 0;
341 cur = word;
342 for(i = 0 ; i < size ; i ++) {
343 switch (* cur) {
344 case ',':
345 case ':':
346 case '!':
347 case '"':
348 case '#':
349 case '$':
350 case '@':
351 case '[':
352 case '\\':
353 case ']':
354 case '^':
355 case '`':
356 case '{':
357 case '|':
358 case '}':
359 case '~':
360 case '=':
361 case '?':
362 case '_':
363 do_quote = 1;
364 break;
365 default:
366 if (((unsigned char) * cur) >= 128)
367 do_quote = 1;
368 break;
369 }
370 cur ++;
371 }
372
373 return do_quote;
374 }
375
376 #define MAX_IMF_LINE 72
377
quote_word(const char * display_charset,MMAPString * mmapstr,const char * word,size_t size)378 static inline int quote_word(const char * display_charset,
379 MMAPString * mmapstr, const char * word, size_t size)
380 {
381 const char * cur;
382 size_t i;
383 char hex[4];
384 int col;
385
386 if (mmap_string_append(mmapstr, "=?") == NULL)
387 return -1;
388 if (mmap_string_append(mmapstr, display_charset) == NULL)
389 return -1;
390 if (mmap_string_append(mmapstr, "?Q?") == NULL)
391 return -1;
392
393 col = (int) mmapstr->len;
394
395 cur = word;
396 for(i = 0 ; i < size ; i ++) {
397 int do_quote_char;
398
399 if (col + 2 /* size of "?=" */
400 + 3 /* max size of newly added character */
401 + 1 /* minimum column of string in a
402 folded header */ >= MAX_IMF_LINE) {
403 int old_pos;
404 /* adds a concatened encoded word */
405
406 if (mmap_string_append(mmapstr, "?=") == NULL)
407 return -1;
408
409 if (mmap_string_append(mmapstr, " ") == NULL)
410 return -1;
411
412 old_pos = (int) mmapstr->len;
413
414 if (mmap_string_append(mmapstr, "=?") == NULL)
415 return -1;
416 if (mmap_string_append(mmapstr, display_charset) == NULL)
417 return -1;
418 if (mmap_string_append(mmapstr, "?Q?") == NULL)
419 return -1;
420
421 col = (int) mmapstr->len - old_pos;
422 }
423
424 do_quote_char = 0;
425 switch (* cur) {
426 case ',':
427 case ':':
428 case '!':
429 case '"':
430 case '#':
431 case '$':
432 case '@':
433 case '[':
434 case '\\':
435 case ']':
436 case '^':
437 case '`':
438 case '{':
439 case '|':
440 case '}':
441 case '~':
442 case '=':
443 case '?':
444 case '_':
445 do_quote_char = 1;
446 break;
447
448 default:
449 if (((unsigned char) * cur) >= 128)
450 do_quote_char = 1;
451 break;
452 }
453
454 if (do_quote_char) {
455 snprintf(hex, 4, "=%2.2X", (unsigned char) * cur);
456 if (mmap_string_append(mmapstr, hex) == NULL)
457 return -1;
458 col += 3;
459 }
460 else {
461 if (* cur == ' ') {
462 if (mmap_string_append_c(mmapstr, '_') == NULL)
463 return -1;
464 }
465 else {
466 if (mmap_string_append_c(mmapstr, * cur) == NULL)
467 return -1;
468 }
469 col += 3;
470 }
471 cur ++;
472 }
473
474 if (mmap_string_append(mmapstr, "?=") == NULL)
475 return -1;
476
477 return 0;
478 }
479
get_word(const char * begin,const char ** pend,int * pto_be_quoted)480 static inline void get_word(const char * begin,
481 const char ** pend, int * pto_be_quoted)
482 {
483 const char * cur;
484
485 cur = begin;
486
487 while ((* cur != ' ') && (* cur != '\t') && (* cur != '\0')) {
488 cur ++;
489 }
490
491 if (cur - begin +
492 1 /* minimum column of string in a
493 folded header */ > MAX_IMF_LINE)
494 * pto_be_quoted = 1;
495 else
496 * pto_be_quoted = to_be_quoted(begin, cur - begin);
497
498 * pend = cur;
499 }
500
make_quoted_printable(const char * display_charset,const char * phrase)501 static char * make_quoted_printable(const char * display_charset,
502 const char * phrase)
503 {
504 char * str;
505 const char * cur;
506 MMAPString * mmapstr;
507 int r;
508
509 mmapstr = mmap_string_new("");
510 if (mmapstr == NULL)
511 return NULL;
512
513 cur = phrase;
514 while (* cur != '\0') {
515 const char * begin;
516 const char * end;
517 int do_quote;
518 int quote_words;
519
520 begin = cur;
521 end = begin;
522 quote_words = 0;
523 do_quote = 1;
524
525 while (* cur != '\0') {
526 get_word(cur, &cur, &do_quote);
527 if (do_quote) {
528 quote_words = 1;
529 end = cur;
530 }
531 else
532 break;
533 if (* cur != '\0')
534 cur ++;
535 }
536
537 if (quote_words) {
538 r = quote_word(display_charset, mmapstr, begin, end - begin);
539 if (r < 0) {
540 mmap_string_free(mmapstr);
541 return NULL;
542 }
543
544 if ((* end == ' ') || (* end == '\t')) {
545 if (mmap_string_append_c(mmapstr, * end) == NULL) {
546 mmap_string_free(mmapstr);
547 return NULL;
548 }
549 end ++;
550 }
551
552 if (* end != '\0') {
553 if (mmap_string_append_len(mmapstr, end, cur - end) == NULL) {
554 mmap_string_free(mmapstr);
555 return NULL;
556 }
557 }
558 }
559 else {
560 if (mmap_string_append_len(mmapstr, begin, cur - begin) == NULL) {
561 mmap_string_free(mmapstr);
562 return NULL;
563 }
564 }
565
566 if ((* cur == ' ') || (* cur == '\t')) {
567 if (mmap_string_append_c(mmapstr, * cur) == 0) {
568 mmap_string_free(mmapstr);
569 return NULL;
570 }
571 cur ++;
572 }
573 }
574
575 str = strdup(mmapstr->str);
576 if (str == NULL) {
577 mmap_string_free(mmapstr);
578 return NULL;
579 }
580
581 mmap_string_free(mmapstr);
582
583 return str;
584 }
585
feed_item_to_message(mailsession * session,unsigned int num,struct newsfeed_item * item)586 static mailmessage * feed_item_to_message(mailsession * session,
587 unsigned int num,
588 struct newsfeed_item * item)
589 {
590 struct mailimf_fields * fields;
591 struct mailimf_date_time * date_time;
592 time_t time_modified;
593 struct mailimf_mailbox_list * from;
594 mailmessage * msg;
595 char * subject;
596 const char * subject_const;
597 char * msg_id;
598 int r;
599 const char * author_const;
600
601 from = NULL;
602 author_const = newsfeed_item_get_author(item);
603 if (author_const != NULL) {
604 char * author;
605 char * addr_spec;
606 struct mailimf_mailbox * mb;
607
608 author = strdup(author_const);
609 if (author == NULL) {
610 goto err;
611 }
612
613 from = mailimf_mailbox_list_new_empty();
614 if (from == NULL) {
615 free(author);
616 goto err;
617 }
618 addr_spec = strdup("invalid@localhost.local");
619 if (addr_spec == NULL) {
620 free(author);
621 goto free_from;
622 }
623
624 /* XXX - encode author with MIME */
625 mb = mailimf_mailbox_new(author, addr_spec);
626 if (mb == NULL) {
627 free(addr_spec);
628 free(author);
629 goto free_from;
630 }
631
632 r = mailimf_mailbox_list_add(from, mb);
633 if (r != MAILIMF_NO_ERROR) {
634 mailimf_mailbox_free(mb);
635 goto free_from;
636 }
637 }
638
639 date_time = NULL;
640 time_modified = newsfeed_item_get_date_modified(item);
641 if (time_modified != (time_t) -1) {
642 date_time = mailimf_get_date(time_modified);
643 if (date_time == NULL) {
644 goto free_from;
645 }
646 }
647
648 subject = NULL;
649 subject_const = newsfeed_item_get_title(item);
650 if (subject_const != NULL) {
651 subject = make_quoted_printable("utf-8", subject_const);
652 if (subject == NULL) {
653 goto free_date;
654 }
655 }
656
657 msg_id = mailimf_get_message_id();
658 if (msg_id == NULL) {
659 goto free_subject;
660 }
661
662 fields = mailimf_fields_new_with_data_all(date_time,
663 from,
664 NULL,
665 NULL,
666 NULL,
667 NULL,
668 NULL,
669 msg_id,
670 NULL,
671 NULL,
672 subject);
673
674 msg = mailmessage_new();
675 r = mailmessage_init(msg, session, feed_message_driver, num, 0);
676 if (r != MAIL_NO_ERROR) {
677 goto free_fields;
678 }
679 msg->msg_fields = fields;
680
681 return msg;
682
683 free_fields:
684 mailimf_fields_free(fields);
685 goto err;
686 free_subject:
687 free(subject);
688 free_date:
689 mailimf_date_time_free(date_time);
690 free_from:
691 mailimf_mailbox_list_free(from);
692 err:
693 return NULL;
694 }
695
feeddriver_get_messages_list(mailsession * session,struct mailmessage_list ** result)696 static int feeddriver_get_messages_list(mailsession * session,
697 struct mailmessage_list ** result)
698 {
699 unsigned int i;
700 struct feed_session_state_data * data;
701 unsigned int count;
702 struct mailmessage_list * msg_list;
703 carray * tab;
704 int res;
705 int r;
706
707 update(session);
708 data = get_data(session);
709 if (data->feed_error != MAIL_NO_ERROR) {
710 res = data->feed_error;
711 goto err;
712 }
713
714 count = newsfeed_item_list_get_count(data->feed_session);
715
716 tab = carray_new(count);
717 if (tab == NULL) {
718 res = MAIL_ERROR_MEMORY;
719 goto err;
720 }
721 fprintf(stderr, "count: %i\n", count);
722
723 for(i = 0 ; i < count ; i ++) {
724 struct newsfeed_item * item;
725 mailmessage * msg;
726
727 item = newsfeed_get_item(data->feed_session, i);
728 msg = feed_item_to_message(session, i, item);
729 r = carray_add(tab, msg, NULL);
730 if (r < 0) {
731 res = MAIL_ERROR_MEMORY;
732 goto free_tab;
733 }
734 }
735
736 msg_list = mailmessage_list_new(tab);
737 if (msg_list == NULL) {
738 res = MAIL_ERROR_MEMORY;
739 goto free_tab;
740 }
741
742 * result = msg_list;
743
744 return MAIL_NO_ERROR;
745
746 free_tab:
747 for(i = 0 ; i < carray_count(tab) ; i ++) {
748 mailmessage * msg;
749
750 msg = carray_get(tab, i);
751 mailmessage_free(msg);
752 }
753 err:
754 return res;
755 }
756
feeddriver_get_message(mailsession * session,uint32_t num,mailmessage ** result)757 static int feeddriver_get_message(mailsession * session,
758 uint32_t num, mailmessage ** result)
759 {
760 mailmessage * msg_info;
761 int r;
762
763 msg_info = mailmessage_new();
764 if (msg_info == NULL)
765 return MAIL_ERROR_MEMORY;
766
767 r = mailmessage_init(msg_info, session, feed_message_driver, num, 0);
768 if (r != MAIL_NO_ERROR) {
769 mailmessage_free(msg_info);
770 return r;
771 }
772
773 * result = msg_info;
774
775 return MAIL_NO_ERROR;
776 }
777
feeddriver_get_message_by_uid(mailsession * session,const char * uid,mailmessage ** result)778 static int feeddriver_get_message_by_uid(mailsession * session,
779 const char * uid,
780 mailmessage ** result)
781 {
782 #if 0
783 uint32_t num;
784 char * p;
785
786 if (uid == NULL)
787 return MAIL_ERROR_INVAL;
788
789 num = strtoul(uid, &p, 10);
790 if ((p == uid) || (* p != '\0'))
791 return MAIL_ERROR_INVAL;
792
793 return feeddriver_get_message(session, num, result);
794 #endif
795 return MAIL_ERROR_INVAL;
796 }
797