* Implement a different way to delete a password from the cache.
[alpine.git] / pith / takeaddr.c
blob630144ba2797cbdd3f044c63007877765e0da2b3
1 /*
2 * ========================================================================
3 * Copyright 2006-2007 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
16 takeaddr.c
17 Mostly support for Take Address command.
18 ====*/
21 #include "../pith/headers.h"
22 #include "../pith/takeaddr.h"
23 #include "../pith/conf.h"
24 #include "../pith/bldaddr.h"
25 #include "../pith/adrbklib.h"
26 #include "../pith/copyaddr.h"
27 #include "../pith/addrstring.h"
28 #include "../pith/status.h"
29 #include "../pith/mailview.h"
30 #include "../pith/reply.h"
31 #include "../pith/url.h"
32 #include "../pith/mailpart.h"
33 #include "../pith/sequence.h"
34 #include "../pith/stream.h"
35 #include "../pith/busy.h"
36 #include "../pith/ablookup.h"
37 #include "../pith/list.h"
40 static char *fakedomain = "@";
41 long msgno_for_pico_callback;
42 BODY *body_for_pico_callback = NULL;
43 ENVELOPE *env_for_pico_callback = NULL;
47 * Previous selectable TA line.
48 * skips over the elements with skip_it set
50 TA_S *
51 pre_sel_taline(TA_S *current)
53 TA_S *p;
55 if(!current)
56 return NULL;
58 p = current->prev;
59 while(p && p->skip_it)
60 p = p->prev;
62 return(p);
67 * Previous TA line, selectable or just printable.
68 * skips over the elements with skip_it set
70 TA_S *
71 pre_taline(TA_S *current)
73 TA_S *p;
75 if(!current)
76 return NULL;
78 p = current->prev;
79 while(p && (p->skip_it && !p->print))
80 p = p->prev;
82 return(p);
87 * Next selectable TA line.
88 * skips over the elements with skip_it set
90 TA_S *
91 next_sel_taline(TA_S *current)
93 TA_S *p;
95 if(!current)
96 return NULL;
98 p = current->next;
99 while(p && p->skip_it)
100 p = p->next;
102 return(p);
107 * Next TA line, including print only lines.
108 * skips over the elements with skip_it set unless they are print lines
110 TA_S *
111 next_taline(TA_S *current)
113 TA_S *p;
115 if(!current)
116 return NULL;
118 p = current->next;
119 while(p && (p->skip_it && !p->print))
120 p = p->next;
122 return(p);
127 * Mark all of the addresses with a check.
129 * Args: f_line -- the first ta line
131 * Returns the number of lines checked.
134 ta_mark_all(TA_S *f_line)
136 TA_S *ctmp;
137 int how_many_selected = 0;
139 for(ctmp = f_line; ctmp; ctmp = next_sel_taline(ctmp)){
140 ctmp->checked = 1;
141 how_many_selected++;
144 return(how_many_selected);
149 * Does the takeaddr list consist of a single address?
151 * Args: f_line -- the first ta line
153 * Returns 1 if only one address and 0 otherwise.
156 is_talist_of_one(TA_S *f_line)
158 if(f_line == NULL)
159 return 0;
161 /* there is at least one, see if there are two */
162 if(next_sel_taline(f_line) != NULL)
163 return 0;
165 return 1;
170 * Turn off all of the check marks.
172 * Args: f_line -- the first ta line
174 * Returns the number of lines checked (0).
177 ta_unmark_all(TA_S *f_line)
179 TA_S *ctmp;
181 for(ctmp = f_line; ctmp; ctmp = ctmp->next)
182 ctmp->checked = 0;
184 return 0;
189 * new_taline - create new TA_S, zero it out, and insert it after current.
190 * NOTE current gets set to the new TA_S, too!
192 TA_S *
193 new_taline(TA_S **current)
195 TA_S *p;
197 p = (TA_S *)fs_get(sizeof(TA_S));
198 memset((void *)p, 0, sizeof(TA_S));
199 if(current){
200 if(*current){
201 p->next = (*current)->next;
202 (*current)->next = p;
203 p->prev = *current;
204 if(p->next)
205 p->next->prev = p;
208 *current = p;
211 return(p);
215 void
216 free_taline(TA_S **p)
218 if(p && *p){
219 if((*p)->addr)
220 mail_free_address(&(*p)->addr);
222 if((*p)->strvalue)
223 fs_give((void **)&(*p)->strvalue);
225 if((*p)->nickname)
226 fs_give((void **)&(*p)->nickname);
228 if((*p)->fullname)
229 fs_give((void **)&(*p)->fullname);
231 if((*p)->fcc)
232 fs_give((void **)&(*p)->fcc);
234 if((*p)->comment)
235 fs_give((void **)&(*p)->comment);
237 if((*p)->prev)
238 (*p)->prev->next = (*p)->next;
240 if((*p)->next)
241 (*p)->next->prev = (*p)->prev;
243 fs_give((void **)p);
248 void
249 free_talines(TA_S **ta_list)
251 TA_S *current, *ctmp;
253 if(ta_list && (current = (*ta_list))){
254 while(current->prev)
255 current = current->prev;
257 while(current){
258 ctmp = current->next;
259 free_taline(&current);
260 current = ctmp;
263 *ta_list = NULL;
268 void
269 free_ltline(LINES_TO_TAKE **p)
271 if(p && *p){
272 if((*p)->printval)
273 fs_give((void **)&(*p)->printval);
275 if((*p)->exportval)
276 fs_give((void **)&(*p)->exportval);
278 if((*p)->prev)
279 (*p)->prev->next = (*p)->next;
281 if((*p)->next)
282 (*p)->next->prev = (*p)->prev;
284 fs_give((void **)p);
289 void
290 free_ltlines(LINES_TO_TAKE **lt_list)
292 LINES_TO_TAKE *current, *ctmp;
294 if(lt_list && (current = (*lt_list))){
295 while(current->prev)
296 current = current->prev;
298 while(current){
299 ctmp = current->next;
300 free_ltline(&current);
301 current = ctmp;
304 *lt_list = NULL;
310 * Return the first selectable TakeAddr line.
312 * Args: q -- any line in the list
314 TA_S *
315 first_sel_taline(TA_S *q)
317 TA_S *first;
319 if(q == NULL)
320 return NULL;
322 first = NULL;
323 /* back up to the head of the list */
324 while(q){
325 first = q;
326 q = pre_sel_taline(q);
330 * If first->skip_it, that means we were already past the first
331 * legitimate line, so we have to look in the other direction.
333 if(first->skip_it)
334 first = next_sel_taline(first);
336 return(first);
341 * Return the last selectable TakeAddr line.
343 * Args: q -- any line in the list
345 TA_S *
346 last_sel_taline(TA_S *q)
348 TA_S *last;
350 if(q == NULL)
351 return NULL;
353 last = NULL;
354 /* go to the end of the list */
355 while(q){
356 last = q;
357 q = next_sel_taline(q);
361 * If last->skip_it, that means we were already past the last
362 * legitimate line, so we have to look in the other direction.
364 if(last->skip_it)
365 last = pre_sel_taline(last);
367 return(last);
372 * Return the first TakeAddr line, selectable or just printable.
374 * Args: q -- any line in the list
376 TA_S *
377 first_taline(TA_S *q)
379 TA_S *first;
381 if(q == NULL)
382 return NULL;
384 first = NULL;
385 /* back up to the head of the list */
386 while(q){
387 first = q;
388 q = pre_taline(q);
392 * If first->skip_it, that means we were already past the first
393 * legitimate line, so we have to look in the other direction.
395 if(first->skip_it && !first->print)
396 first = next_taline(first);
398 return(first);
403 * Find the first TakeAddr line which is checked, beginning with the
404 * passed in line.
406 * Args: head -- A passed in TakeAddr line, usually will be the first
408 * Result: returns a pointer to the first checked line.
409 * NULL if no such line
411 TA_S *
412 first_checked(TA_S *head)
414 TA_S *p;
416 p = head;
418 for(p = head; p; p = next_sel_taline(p))
419 if(p->checked && !p->skip_it)
420 break;
422 return(p);
427 * Form a list of strings which are addresses to go in a list.
428 * These are entries in a list, so can be full rfc822 addresses.
429 * The strings are allocated here.
431 * Args: head -- A passed in TakeAddr line, usually will be the first
433 * Result: returns an allocated array of pointers to allocated strings
435 char **
436 list_of_checked(TA_S *head)
438 TA_S *p;
439 int count;
440 char **list, **cur;
441 ADDRESS *a;
443 /* first count them */
444 for(p = head, count = 0; p; p = next_sel_taline(p)){
445 if(p->checked && !p->skip_it){
446 if(p->frwrded){
448 * Remove fullname, fcc, comment, and nickname since not
449 * appropriate for list values.
451 if(p->fullname)
452 fs_give((void **)&p->fullname);
453 if(p->fcc)
454 fs_give((void **)&p->fcc);
455 if(p->comment)
456 fs_give((void **)&p->comment);
457 if(p->nickname)
458 fs_give((void **)&p->nickname);
460 for(a = p->addr; a; a = a->next)
461 count++;
463 else{
465 * Don't even attempt to include bogus addresses in
466 * the list. If the user wants to get at those, they
467 * have to try taking only that single address.
469 if(p->addr && p->addr->host && p->addr->host[0] == '.')
470 p->skip_it = 1;
471 else
472 count++;
477 /* allocate pointers */
478 list = (char **)fs_get((count + 1) * sizeof(char *));
479 memset((void *)list, 0, (count + 1) * sizeof(char *));
481 cur = list;
483 /* allocate and point to address strings */
484 for(p = head; p; p = next_sel_taline(p)){
485 if(p->checked && !p->skip_it){
486 if(p->frwrded)
487 for(a = p->addr; a; a = a->next){
488 ADDRESS *next_addr;
489 char *bufp;
490 size_t len;
492 next_addr = a->next;
493 a->next = NULL;
494 len = est_size(a);
495 bufp = (char *) fs_get(len * sizeof(char));
496 *cur++ = cpystr(addr_string(a, bufp, len));
497 a->next = next_addr;
498 fs_give((void **)&bufp);
500 else
501 *cur++ = cpystr(p->strvalue);
505 return(list);
508 /* jpf: This used to be the guts of cmd_take_addr, but I made this
509 * function so I could generalize with WP. It still has some display
510 * stuff that we need make sure not to do when in WP mode.
512 * Set things up for when we take addresses
514 * Args: ps -- pine state
515 * msgmap -- the MessageMap
516 * ta_ret -- the take addr lines
517 * selected_num -- the number of selectable addresses
518 * flags -- takeaddr flags, like whether or not
519 * we're doing aggs, and whether to do prompts
521 * Returns: -1 on "failure"
524 set_up_takeaddr(int cmd, struct pine *ps, MSGNO_S *msgmap, TA_S **ta_ret,
525 int *selected_num, int flags, int (*att_addr_f)(TA_S *, int))
527 long i;
528 ENVELOPE *env = NULL;
529 int how_many_selected = 0,
530 added, rtype = 0,
531 we_cancel = 0,
532 special_processing = 0,
533 select_froms = 0;
534 TA_S *current = NULL,
535 *prev_comment_line,
536 *ta;
537 BODY **body_h,
538 *special_body = NULL,
539 *body = NULL;
541 dprint((2, "\n - taking address into address book - \n"));
543 if(!(cmd == 'a' || cmd == 'e'))
544 return -1;
546 if((flags & TA_AGG) && !pseudo_selected(ps_global->mail_stream, msgmap))
547 return -1;
549 if(mn_get_total(msgmap) > 0 && mn_total_cur(msgmap) == 1)
550 special_processing = 1;
552 if(flags & TA_AGG)
553 select_froms = 1;
555 /* this is a non-selectable label */
556 current = fill_in_ta(&current, (ADDRESS *) NULL, 0,
557 /* TRANSLATORS: This is a heading describing some addresses the
558 user will have the chance to choose from. */
559 _(" These entries are taken from the attachments "));
560 prev_comment_line = current;
563 * Add addresses from special attachments for each message.
565 added = 0;
566 for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
567 env = pine_mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i), &body);
568 if(!env){
569 q_status_message(SM_ORDER | SM_DING, 3, 4,
570 _("Can't take address into address book. Error accessing folder"));
571 rtype = -1;
572 goto doneskee;
575 added += process_vcard_atts(ps->mail_stream, mn_m2raw(msgmap, i),
576 body, body, NULL, &current);
579 if(!(flags & TA_AGG)
580 && added > 1
581 && att_addr_f
582 && (*att_addr_f)(current, added) <= 0)
583 goto doneskee;
585 if(!(flags & TA_NOPROMPT))
586 we_cancel = busy_cue(NULL, NULL, 1);
589 * add a comment line to separate attachment address lines
590 * from header address lines
592 if(added > 0){
593 current = fill_in_ta(&current, (ADDRESS *) NULL, 0,
594 /* TRANSLATORS: msg is message */
595 _(" These entries are taken from the msg headers "));
596 prev_comment_line = current;
597 how_many_selected += added;
598 select_froms = 0;
600 else{ /* turn off header comment, and no separator comment */
601 prev_comment_line->print = 0;
602 prev_comment_line = NULL;
606 * Add addresses from headers of messages.
608 for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
610 if(special_processing)
611 body_h = &special_body;
612 else
613 body_h = NULL;
615 env = pine_mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i),
616 body_h);
617 if(!env){
618 if(we_cancel)
619 cancel_busy_cue(-1);
621 q_status_message(SM_ORDER | SM_DING, 3, 4,
622 _("Can't take address into address book. Error accessing folder"));
623 rtype = -1;
624 goto doneskee;
627 added = add_addresses_to_talist(ps, i, "from", &current,
628 env->from, select_froms);
629 if(select_froms)
630 how_many_selected += added;
632 if(!address_is_same(env->from, env->reply_to))
633 (void)add_addresses_to_talist(ps, i, "reply-to", &current,
634 env->reply_to, 0);
636 if(!address_is_same(env->from, env->sender))
637 (void)add_addresses_to_talist(ps, i, "sender", &current,
638 env->sender, 0);
640 (void)add_addresses_to_talist(ps, i, "to", &current, env->to, 0);
641 (void)add_addresses_to_talist(ps, i, "cc", &current, env->cc, 0);
642 (void)add_addresses_to_talist(ps, i, "bcc", &current, env->bcc, 0);
646 * Check to see if we added an explanatory line about the
647 * header addresses but no header addresses. If so, remove the
648 * explanatory line.
650 if(prev_comment_line){
651 how_many_selected -=
652 eliminate_dups_and_us(first_sel_taline(current));
653 for(ta = prev_comment_line->next; ta; ta = ta->next)
654 if(!ta->skip_it)
655 break;
657 /* all entries were skip_it entries, turn off print */
658 if(!ta){
659 prev_comment_line->print = 0;
660 prev_comment_line = NULL;
665 * If there's only one message we let the user view it using ^T
666 * from the header editor.
668 if(!(flags & TA_NOPROMPT) && special_processing && env && special_body){
669 msgno_for_pico_callback = mn_m2raw(msgmap, mn_first_cur(msgmap));
670 body_for_pico_callback = special_body;
671 env_for_pico_callback = env;
673 else{
674 env_for_pico_callback = NULL;
675 body_for_pico_callback = NULL;
678 current = fill_in_ta(&current, (ADDRESS *)NULL, 0,
679 _(" Below this line are some possibilities taken from the text of the msg "));
680 prev_comment_line = current;
683 * Add addresses from the text of the body.
685 added = 0;
686 for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
688 body = NULL;
689 env = pine_mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i),
690 &body);
691 if(env && body)
692 added += grab_addrs_from_body(ps->mail_stream,
693 mn_m2raw(msgmap, i),
694 body, &current);
697 if(added == 0){
698 prev_comment_line->print = 0;
699 prev_comment_line = NULL;
703 * Check to see if all we added are dups.
704 * If so, remove the comment line.
706 if(prev_comment_line){
707 how_many_selected -= eliminate_dups_and_us(first_sel_taline(current));
708 for(ta = prev_comment_line->next; ta; ta = ta->next)
709 if(!ta->skip_it)
710 break;
712 /* all entries were skip_it entries, turn off print */
713 if(!ta)
714 prev_comment_line->print = 0;
717 if(we_cancel)
718 cancel_busy_cue(-1);
720 doneskee:
721 env_for_pico_callback = NULL;
722 body_for_pico_callback = NULL;
724 ps->mangled_screen = 1;
726 if(flags & TA_AGG)
727 restore_selected(msgmap);
729 *ta_ret = current;
731 if(selected_num)
732 *selected_num = how_many_selected;
734 return(rtype);
739 convert_ta_to_lines(TA_S *ta_list, LINES_TO_TAKE **old_current)
741 ADDRESS *a;
742 TA_S *ta;
743 LINES_TO_TAKE *new_current;
744 char *exportval, *printval;
745 int ret = 0;
747 for(ta = first_sel_taline(ta_list);
749 ta = next_sel_taline(ta)){
750 if(ta->skip_it)
751 continue;
753 if(ta->frwrded){
754 if(ta->fullname)
755 fs_give((void **)&ta->fullname);
756 if(ta->fcc)
757 fs_give((void **)&ta->fcc);
758 if(ta->comment)
759 fs_give((void **)&ta->comment);
760 if(ta->nickname)
761 fs_give((void **)&ta->nickname);
763 else if(ta->addr && ta->addr->host && ta->addr->host[0] == '.')
764 ta->skip_it = 1;
766 if(ta->frwrded){
767 for(a = ta->addr; a; a = a->next){
768 ADDRESS *next_addr;
770 if(a->host && a->host[0] == '.')
771 continue;
773 next_addr = a->next;
774 a->next = NULL;
776 exportval = cpystr(simple_addr_string(a, tmp_20k_buf,
777 SIZEOF_20KBUF));
778 if(!exportval || !exportval[0]){
779 if(exportval)
780 fs_give((void **)&exportval);
782 a->next = next_addr;
783 continue;
786 printval = addr_list_string(a, NULL, 0);
787 if(!printval || !printval[0]){
788 if(printval)
789 fs_give((void **)&printval);
791 printval = cpystr(exportval);
794 new_current = new_ltline(old_current);
795 new_current->flags = LT_NONE;
797 new_current->printval = printval;
798 new_current->exportval = exportval;
799 a->next = next_addr;
800 ret++;
803 else{
804 if(ta->addr){
805 exportval = cpystr(simple_addr_string(ta->addr, tmp_20k_buf,
806 SIZEOF_20KBUF));
807 if(exportval && exportval[0]){
808 new_current = new_ltline(old_current);
809 new_current->flags = LT_NONE;
811 new_current->exportval = exportval;
813 if(ta->strvalue && ta->strvalue[0])
814 new_current->printval = cpystr(ta->strvalue);
815 else
816 new_current->printval = cpystr(new_current->exportval);
818 ret++;
820 else if(exportval)
821 fs_give((void **)&exportval);
826 return(ret);
831 * new_ltline - create new LINES_TO_TAKE, zero it out,
832 * and insert it after current.
833 * NOTE current gets set to the new current.
835 LINES_TO_TAKE *
836 new_ltline(LINES_TO_TAKE **current)
838 LINES_TO_TAKE *p;
840 p = (LINES_TO_TAKE *)fs_get(sizeof(LINES_TO_TAKE));
841 memset((void *)p, 0, sizeof(LINES_TO_TAKE));
842 if(current){
843 if(*current){
844 p->next = (*current)->next;
845 (*current)->next = p;
846 p->prev = *current;
847 if(p->next)
848 p->next->prev = p;
851 *current = p;
854 return(p);
859 add_addresses_to_talist(struct pine *ps, long int msgno, char *field,
860 TA_S **old_current, struct mail_address *adrlist, int checked)
862 ADDRESS *addr;
863 int added = 0;
865 for(addr = adrlist; addr; addr = addr->next){
866 if(addr->host && addr->host[0] == '.'){
867 char *h, *fields[2];
869 fields[0] = field;
870 fields[1] = NULL;
871 if((h = pine_fetchheader_lines(ps->mail_stream, msgno,NULL,fields)) != NULL){
872 char *p, fname[32];
873 int q;
875 q = strlen(h);
877 snprintf(fname, sizeof(fname), "%s:", field);
878 for(p = h; (p = strstr(p, fname)) != NULL; )
879 rplstr(p, q-(p-h), strlen(fname), ""); /* strip field strings */
881 sqznewlines(h); /* blat out CR's & LF's */
882 for(p = h; (p = strchr(p, TAB)) != NULL; )
883 *p++ = ' '; /* turn TABs to space */
885 if(*h){
886 unsigned long l;
887 ADDRESS *new_addr;
888 size_t ll;
890 new_addr = (ADDRESS *)fs_get(sizeof(ADDRESS));
891 memset(new_addr, 0, sizeof(ADDRESS));
894 * Base64 armor plate the gunk to protect against
895 * c-client quoting in output.
897 p = (char *)rfc822_binary((void *)h,
898 (unsigned long)strlen(h), &l);
899 sqznewlines(p);
900 ll = strlen(p) + 3;
901 new_addr->mailbox = (char *) fs_get((ll+1) * sizeof(char));
902 snprintf(new_addr->mailbox, ll+1, "&%s", p);
903 fs_give((void **)&p);
904 new_addr->host = cpystr(RAWFIELD);
905 fill_in_ta(old_current, new_addr, 0, (char *)NULL);
906 mail_free_address(&new_addr);
909 fs_give((void **)&h);
912 return(0);
916 /* no syntax errors in list, add them all */
917 for(addr = adrlist; addr; addr = addr->next){
918 fill_in_ta(old_current, addr, checked, (char *)NULL);
919 added++;
922 return(added);
927 * Look for Text/directory attachments and process them.
928 * Return number of lines added to the ta_list.
931 process_vcard_atts(MAILSTREAM *stream, long int msgno,
932 struct mail_bodystruct *root, struct mail_bodystruct *body,
933 char *partnum, TA_S **ta_list)
935 char *addrs, /* comma-separated list of addresses */
936 *value,
937 *encoded,
938 *escval,
939 *nickname,
940 *fullname,
941 *struct_name,
942 *fcc,
943 *tag,
944 *decoded,
945 *num = NULL,
946 *defaulttype = NULL,
947 *charset = NULL,
948 *altcharset,
949 *comma,
951 *comment,
952 *title,
953 **lines = NULL,
954 **ll;
955 size_t space;
956 int used = 0,
957 is_encoded = 0,
958 vcard_nesting = 0,
959 selected = 0;
960 PART *part;
961 PARAMETER *parm;
962 static char a_comma = ',';
965 * Look through all the subparts searching for our special type.
967 if(body && body->type == TYPEMULTIPART){
968 for(part = body->nested.part; part; part = part->next)
969 selected += process_vcard_atts(stream, msgno, root, &part->body,
970 partnum, ta_list);
972 /* only look in rfc822 subtype of type message */
973 else if(body && body->type == TYPEMESSAGE
974 && body->subtype && !strucmp(body->subtype, "rfc822")){
975 selected += process_vcard_atts(stream, msgno, root,
976 body->nested.msg->body,
977 partnum, ta_list);
979 /* found one (TYPEAPPLICATION for back compat.) */
980 else if(body && MIME_VCARD(body->type,body->subtype)){
982 dprint((4, "\n - found abook attachment to process - \n"));
985 * The Text/directory type has a possible parameter called
986 * "defaulttype" that we need to look for (this is from an old draft
987 * and is supported only for backwards compatibility). There is
988 * also a possible default "charset". (Default charset is also from
989 * an old draft and is no longer allowed.) We don't care about any of
990 * the other parameters.
992 for(parm = body->parameter; parm; parm = parm->next)
993 if(!strucmp("defaulttype", parm->attribute))
994 break;
996 if(parm)
997 defaulttype = parm->value;
999 /* ...and look for possible charset parameter */
1000 for(parm = body->parameter; parm; parm = parm->next)
1001 if(!strucmp("charset", parm->attribute))
1002 break;
1004 if(parm)
1005 charset = parm->value;
1007 num = partnum ? cpystr(partnum) : partno(root, body);
1008 lines = detach_vcard_att(stream, msgno, body, num);
1009 if(num)
1010 fs_give((void **)&num);
1012 nickname = fullname = comment = title = fcc = struct_name = NULL;
1013 #define CHUNK (size_t)500
1014 space = CHUNK;
1015 /* make comma-separated list of email addresses in addrs */
1016 addrs = (char *)fs_get((space+1) * sizeof(char));
1017 *addrs = '\0';
1018 for(ll = lines; ll && *ll; ll++){
1019 altcharset = NULL;
1020 decoded = NULL;
1021 escval = NULL;
1022 is_encoded = 0;
1023 value = getaltcharset(*ll, &tag, &altcharset, &is_encoded);
1025 if(!value || !tag){
1026 if(tag)
1027 fs_give((void **)&tag);
1029 if(altcharset)
1030 fs_give((void **)&altcharset);
1032 continue;
1035 if(is_encoded){
1036 unsigned long length;
1038 decoded = (char *)rfc822_base64((unsigned char *)value,
1039 (unsigned long)strlen(value),
1040 &length);
1041 if(decoded){
1042 decoded[length] = '\0';
1043 value = decoded;
1045 else
1046 value = "<malformed B64 data>";
1049 /* support default tag (back compat.) */
1050 if(*tag == '\0' && defaulttype){
1051 fs_give((void **)&tag);
1052 tag = cpystr(defaulttype);
1055 if(!strucmp(tag, "begin")){ /* vCard BEGIN */
1056 vcard_nesting++;
1058 else if(!strucmp(tag, "end")){ /* vCard END */
1059 if(--vcard_nesting == 0){
1060 if(title){
1061 if(!comment)
1062 comment = title;
1063 else
1064 fs_give((void **)&title);
1067 /* add this entry */
1068 selected += vcard_to_ta(addrs, fullname, struct_name,
1069 nickname, comment, fcc, ta_list);
1070 if(addrs)
1071 *addrs = '\0';
1073 used = 0;
1074 nickname = fullname = comment = title = fcc = NULL;
1075 struct_name = NULL;
1078 /* add another address to addrs */
1079 else if(!strucmp(tag, "email")){
1080 if(*value){
1081 escval = vcard_unescape(value);
1082 encoded = encode_fullname_of_addrstring(escval,
1083 (altcharset && *altcharset) ? altcharset
1084 : (charset && *charset)
1085 ? charset
1086 : ps_global->posting_charmap);
1087 if(encoded){
1088 /* allocate more space */
1089 if((used + strlen(encoded) + 1) > space){
1090 space += CHUNK;
1091 fs_resize((void **)&addrs, (space+1)*sizeof(char));
1094 if(*addrs){
1095 strncat(addrs, ",", space+1-1-strlen(addrs));
1096 addrs[space-1] = '\0';
1099 strncat(addrs, encoded, space+1-1-strlen(addrs));
1100 addrs[space-1] = '\0';
1101 used += (strlen(encoded) + 1);
1102 fs_give((void **)&encoded);
1106 else if(!strucmp(tag, "note") || !strucmp(tag, "misc")){
1107 if(*value){
1108 escval = vcard_unescape(value);
1109 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
1110 (unsigned char *)escval,
1111 (altcharset && *altcharset) ? altcharset
1112 : (charset && *charset)
1113 ? charset
1114 : ps_global->posting_charmap);
1115 if(encoded){
1116 /* Last one wins */
1117 if(comment)
1118 fs_give((void **)&comment);
1120 comment = cpystr(encoded);
1124 else if(!strucmp(tag, "title")){
1125 if(*value){
1126 escval = vcard_unescape(value);
1127 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
1128 (unsigned char *)escval,
1129 (altcharset && *altcharset) ? altcharset
1130 : (charset && *charset)
1131 ? charset
1132 : ps_global->posting_charmap);
1133 if(encoded){
1134 /* Last one wins */
1135 if(title)
1136 fs_give((void **)&title);
1138 title = cpystr(encoded);
1142 else if(!strucmp(tag, "x-fcc")){
1143 if(*value){
1144 escval = vcard_unescape(value);
1145 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
1146 (unsigned char *)escval,
1147 (altcharset && *altcharset) ? altcharset
1148 : (charset && *charset)
1149 ? charset
1150 : ps_global->posting_charmap);
1151 if(encoded){
1152 /* Last one wins */
1153 if(fcc)
1154 fs_give((void **)&fcc);
1156 fcc = cpystr(encoded);
1160 else if(!strucmp(tag, "fn") || !strucmp(tag, "cn")){
1161 if(*value){
1162 escval = vcard_unescape(value);
1163 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
1164 (unsigned char *)escval,
1165 (altcharset && *altcharset) ? altcharset
1166 : (charset && *charset)
1167 ? charset
1168 : ps_global->posting_charmap);
1169 if(encoded){
1170 /* Last one wins */
1171 if(fullname)
1172 fs_give((void **)&fullname);
1174 fullname = cpystr(encoded);
1178 else if(!strucmp(tag, "n")){
1180 * This is a structured name field. It has up to five
1181 * fields separated by ';'. The fields are Family Name (which
1182 * we'll take to be Last name for our purposes), Given Name
1183 * (First name for us), additional names, prefixes, and
1184 * suffixes. We'll ignore prefixes and suffixes.
1186 * If we find one of these records we'll use it in preference
1187 * to the formatted name field (fn).
1189 if(*value && *value != ';'){
1190 char *last, *first, *middle = NULL, *rest = NULL;
1191 char *esclast, *escfirst, *escmiddle;
1192 static char a_semi = ';';
1194 last = value;
1196 first = NULL;
1197 p = last;
1198 while(p && (first = strindex(p, a_semi)) != NULL){
1199 if(first > last && first[-1] != '\\')
1200 break;
1201 else
1202 p = first + 1;
1205 if(first)
1206 *first = '\0';
1208 if(first && *(first+1) && *(first+1) != a_semi){
1209 first++;
1211 middle = NULL;
1212 p = first;
1213 while(p && (middle = strindex(p, a_semi)) != NULL){
1214 if(middle > first && middle[-1] != '\\')
1215 break;
1216 else
1217 p = middle + 1;
1220 if(middle)
1221 *middle = '\0';
1223 if(middle && *(middle+1) && *(middle+1) != a_semi){
1224 middle++;
1226 rest = NULL;
1227 p = middle;
1228 while(p && (rest = strindex(p, a_semi)) != NULL){
1229 if(rest > middle && rest[-1] != '\\')
1230 break;
1231 else
1232 p = rest + 1;
1235 /* we don't care about the rest */
1236 if(rest)
1237 *rest = '\0';
1239 else
1240 middle = NULL;
1242 else
1243 first = NULL;
1246 * Structured name pieces can have multiple values.
1247 * We're just going to take the first value in each part.
1248 * Skip excaped commas, though.
1250 p = last;
1251 while(p && (comma = strindex(p, a_comma)) != NULL){
1252 if(comma > last && comma[-1] != '\\'){
1253 *comma = '\0';
1254 break;
1256 else
1257 p = comma + 1;
1260 p = first;
1261 while(p && (comma = strindex(p, a_comma)) != NULL){
1262 if(comma > first && comma[-1] != '\\'){
1263 *comma = '\0';
1264 break;
1266 else
1267 p = comma + 1;
1270 p = middle;
1271 while(p && (comma = strindex(p, a_comma)) != NULL){
1272 if(comma > middle && comma[-1] != '\\'){
1273 *comma = '\0';
1274 break;
1276 else
1277 p = comma + 1;
1280 esclast = vcard_unescape(last);
1281 escfirst = vcard_unescape(first);
1282 escmiddle = vcard_unescape(middle);
1283 snprintf(tmp_20k_buf+10000, SIZEOF_20KBUF-10000, "%s%s%s%s%s",
1284 esclast ? esclast : "",
1285 escfirst ? ", " : "",
1286 escfirst ? escfirst : "",
1287 escmiddle ? " " : "",
1288 escmiddle ? escmiddle : "");
1289 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1291 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF-10000,
1292 (unsigned char *)tmp_20k_buf+10000,
1293 (altcharset && *altcharset) ? altcharset
1294 : (charset && *charset)
1295 ? charset
1296 : ps_global->posting_charmap);
1297 tmp_20k_buf[SIZEOF_20KBUF-10000-1] = '\0';
1299 if(esclast && *esclast && escfirst && *escfirst){
1300 if(struct_name)
1301 fs_give((void **)&struct_name);
1303 struct_name = cpystr(encoded);
1305 else{
1306 /* in case we don't get a fullname better than this */
1307 if(!fullname)
1308 fullname = cpystr(encoded);
1311 if(esclast)
1312 fs_give((void **)&esclast);
1313 if(escfirst)
1314 fs_give((void **)&escfirst);
1315 if(escmiddle)
1316 fs_give((void **)&escmiddle);
1319 /* suggested nickname */
1320 else if(!strucmp(tag, "nickname") || !strucmp(tag, "x-nickname")){
1321 if(*value){
1322 /* Last one wins */
1323 if(nickname)
1324 fs_give((void **)&nickname);
1327 * Nickname can have multiple values. We're just
1328 * going to take the first. Skip escaped commas, though.
1330 p = value;
1331 while(p && (comma = strindex(p, a_comma)) != NULL){
1332 if(comma > value && comma[-1] != '\\'){
1333 *comma = '\0';
1334 break;
1336 else
1337 p = comma + 1;
1340 nickname = vcard_unescape(value);
1344 if(tag)
1345 fs_give((void **)&tag);
1347 if(altcharset)
1348 fs_give((void **)&altcharset);
1350 if(decoded)
1351 fs_give((void **)&decoded);
1353 if(escval)
1354 fs_give((void **)&escval);
1357 if(title){
1358 if(!comment)
1359 comment = title;
1360 else
1361 fs_give((void **)&title);
1364 if(fullname || struct_name || nickname || fcc
1365 || comment || (addrs && *addrs))
1366 selected += vcard_to_ta(addrs, fullname, struct_name, nickname,
1367 comment, fcc, ta_list);
1369 if(addrs)
1370 fs_give((void **)&addrs);
1372 free_list_array(&lines);
1375 return(selected);
1380 cmp_swoop_list(const qsort_t *a1, const qsort_t *a2)
1382 SWOOP_S *x = (SWOOP_S *)a1;
1383 SWOOP_S *y = (SWOOP_S *)a2;
1385 if(x->dup){
1386 if(y->dup)
1387 return((x->dst_enum > y->dst_enum) ? -1
1388 : (x->dst_enum == y->dst_enum) ? 0 : 1);
1389 else
1390 return(-1);
1392 else if(y->dup)
1393 return(1);
1394 else
1395 return((x > y) ? -1 : (x == y) ? 0 : 1); /* doesn't matter */
1401 * Take the split out contents of a vCard entry and turn them into a TA.
1403 * Always returns 1, the number of TAs added to ta_list.
1406 vcard_to_ta(char *addrs, char *fullname, char *better_fullname, char *nickname,
1407 char *comment, char *fcc, TA_S **ta_list)
1409 ADDRESS *addrlist = NULL;
1412 * Parse it into an addrlist, which is what fill_in_ta wants
1413 * to see.
1415 rfc822_parse_adrlist(&addrlist, addrs, fakedomain);
1416 if(!addrlist)
1417 addrlist = mail_newaddr(); /* empty addr, to make right thing happen */
1419 *ta_list = fill_in_ta(ta_list, addrlist, 1,
1420 fullname ? fullname
1421 : better_fullname ? better_fullname
1422 : "Forwarded Entry");
1423 (*ta_list)->nickname = nickname ? nickname : cpystr("");
1424 (*ta_list)->comment = comment;
1425 (*ta_list)->fcc = fcc;
1428 * We are tempted to want to call switch_to_last_comma_first() here when
1429 * we don't have a better_fullname (which is already last, first).
1430 * But, since this is the way it was sent to us we should probably leave
1431 * it alone. That means that if we use a fullname culled from the
1432 * body of a message, or from a header, or from an "N" vcard record,
1433 * we will make it be Last, First; but if we use one from an "FN" record
1434 * we won't.
1436 (*ta_list)->fullname = better_fullname ? better_fullname
1437 : fullname;
1439 if((*ta_list)->nickname)
1440 convert_possibly_encoded_str_to_utf8(&(*ta_list)->nickname);
1442 if((*ta_list)->comment)
1443 convert_possibly_encoded_str_to_utf8(&(*ta_list)->comment);
1445 if((*ta_list)->fcc)
1446 convert_possibly_encoded_str_to_utf8(&(*ta_list)->fcc);
1448 if((*ta_list)->fullname)
1449 convert_possibly_encoded_str_to_utf8(&(*ta_list)->fullname);
1452 * The TA list will free its members but we have to take care of this
1453 * extra one that isn't assigned to a TA member.
1455 if(better_fullname && fullname)
1456 fs_give((void **)&fullname);
1458 if(addrlist)
1459 mail_free_address(&addrlist);
1461 return(1);
1466 * Look through line which is supposed to look like
1468 * type;charset=iso-8859-1;encoding=b: value
1470 * Type might be email, or nickname, ... It is optional because of the
1471 * defaulttype parameter (there isn't such a thing as defaulttype parameters
1472 * as of Nov. 96 draft). The semicolon and colon are special chars. Each
1473 * parameter in this line is a semicolon followed by the parameter type "="
1474 * the parameter value. Parameters are optional, too. There is always a colon,
1475 * followed by value. Whitespace can be everywhere up to where value starts.
1476 * There is also an optional <group> "." preceding the type, which we will
1477 * ignore. It's for grouping related lines.
1478 * (As of July, 97 draft, it is no longer permissible to include a charset
1479 * parameter in each line. Only one is permitted in the content-type mime hdr.)
1480 * (Also as of July, 97 draft, all white space is supposed to be significant.
1481 * We will continue to skip white space surrounding '=' and so forth for
1482 * backwards compatibility and because it does no harm in almost all cases.)
1483 * (As of Nov, 97 draft, some white space is not significant. That is, it is
1484 * allowed in some places.)
1487 * Args: line -- the line to look at
1488 * type -- this is a return value, and is an allocated copy of
1489 * the type, freed by the caller. For example, "email".
1490 * alt -- this is a return value, and is an allocated copy of
1491 * the value of the alternate charset, to be freed by
1492 * the caller. For example, this might be "iso-8859-2".
1493 * encoded -- this is a return value, and is set to 1 if the value
1494 * is b-encoded
1496 * Return value: a pointer to the start of value, or NULL if the syntax
1497 * isn't right. It's possible for value to be equal to "".
1499 char *
1500 getaltcharset(char *line, char **type, char **alt, int *encoded)
1502 char *p, *q, *left_semi, *group_dot, *colon;
1503 char *start_of_enc, *start_of_cset, tmpsave;
1504 static char *cset = "charset";
1505 static char *enc = "encoding";
1507 if(type)
1508 *type = NULL;
1510 if(alt)
1511 *alt = NULL;
1513 if(encoded)
1514 *encoded = 0;
1516 colon = strindex(line, ':');
1517 if(!colon)
1518 return NULL;
1520 left_semi = strindex(line, ';');
1521 if(left_semi && left_semi > colon)
1522 left_semi = NULL;
1524 group_dot = strindex(line, '.');
1525 if(group_dot && (group_dot > colon || (left_semi && group_dot > left_semi)))
1526 group_dot = NULL;
1529 * Type is everything up to the semicolon, or the colon if no semicolon.
1530 * However, we want to skip optional <group> ".".
1532 if(type){
1533 q = left_semi ? left_semi : colon;
1534 tmpsave = *q;
1535 *q = '\0';
1536 *type = cpystr(group_dot ? group_dot+1 : line);
1537 *q = tmpsave;
1538 sqzspaces(*type);
1541 if(left_semi && alt
1542 && (p = srchstr(left_semi+1, cset))
1543 && p < colon){
1544 p += strlen(cset);
1545 p = skip_white_space(p);
1546 if(*p++ == '='){
1547 p = skip_white_space(p);
1548 if(p < colon){
1549 start_of_cset = p;
1550 p++;
1551 while(p < colon && !isspace((unsigned char)*p) && *p != ';')
1552 p++;
1554 tmpsave = *p;
1555 *p = '\0';
1556 *alt = cpystr(start_of_cset);
1557 *p = tmpsave;
1562 if(encoded && left_semi
1563 && (p = srchstr(left_semi+1, enc))
1564 && p < colon){
1565 p += strlen(enc);
1566 p = skip_white_space(p);
1567 if(*p++ == '='){
1568 p = skip_white_space(p);
1569 if(p < colon){
1570 start_of_enc = p;
1571 p++;
1572 while(p < colon && !isspace((unsigned char)*p) && *p != ';')
1573 p++;
1575 if(*start_of_enc == 'b' && start_of_enc + 1 == p)
1576 *encoded = 1;
1581 p = colon + 1;
1583 return(skip_white_space(p));
1588 * Return incoming_fullname in Last, First form.
1589 * The passed in fullname should already be in UTF-8.
1590 * If there is already a comma, we add quotes around the fullname so we can
1591 * tell it isn't a Last, First comma but a real comma in the Fullname.
1593 * Args: incoming_fullname --
1594 * new_full -- passed in pointer to place to put new fullname
1595 * nbuf -- size of new_full
1596 * Returns: new_full
1598 void
1599 switch_to_last_comma_first(char *incoming_fullname, char *new_full, size_t nbuf)
1601 char save_value;
1602 char *save_end, *nf, *inc, *p = NULL;
1604 if(nbuf < 1)
1605 return;
1607 if(incoming_fullname == NULL){
1608 new_full[0] = '\0';
1609 return;
1612 /* get rid of leading white space */
1613 for(inc = incoming_fullname; *inc && isspace((unsigned char)*inc); inc++)
1614 ;/* do nothing */
1616 /* get rid of trailing white space */
1617 for(p = inc + strlen(inc) - 1; *p && p >= inc &&
1618 isspace((unsigned char)*p); p--)
1619 ;/* do nothing */
1621 save_end = ++p;
1622 if(save_end == inc){
1623 new_full[0] = '\0';
1624 return;
1627 save_value = *save_end; /* so we don't alter incoming_fullname */
1628 *save_end = '\0';
1629 nf = new_full;
1630 memset(new_full, 0, nbuf); /* need this the way it is done below */
1632 if(strindex(inc, ',') != NULL){
1633 int add_quotes = 0;
1636 * Quote already existing comma.
1638 * We'll get this wrong if it is already quoted but the quote
1639 * doesn't start right at the beginning.
1641 if(inc[0] != '"'){
1642 add_quotes++;
1643 if(nf-new_full < nbuf-1)
1644 *nf++ = '"';
1647 strncpy(nf, inc, MIN(nbuf - (add_quotes ? 3 : 1), nbuf-(nf-new_full)-1));
1648 new_full[nbuf-1] = '\0';
1649 if(add_quotes){
1650 strncat(nf, "\"", nbuf-(nf-new_full)-1);
1651 new_full[nbuf-1] = '\0';
1654 else if(strindex(inc, SPACE)){
1655 char *last, *end;
1657 end = new_full + nbuf - 1;
1660 * Switch First Middle Last to Last, First Middle
1663 /* last points to last word, which does exist */
1664 last = skip_white_space(strrindex(inc, SPACE));
1666 /* copy Last */
1667 for(p = last; *p && nf < end-2 && nf-new_full < nbuf; *nf++ = *p++)
1668 ;/* do nothing */
1670 if(nf-new_full < nbuf-1)
1671 *nf++ = ',';
1673 if(nf-new_full < nbuf-1)
1674 *nf++ = SPACE;
1676 /* copy First Middle */
1677 for(p = inc; p < last && nf < end && nf-new_full < nbuf-1; *nf++ = *p++)
1678 ;/* do nothing */
1680 new_full[nbuf-1] = '\0';
1682 removing_trailing_white_space(new_full);
1684 else
1685 strncpy(new_full, inc, nbuf-1);
1687 new_full[nbuf-1] = '\0';
1689 *save_end = save_value;
1694 * Fetch this body part, content-decode it if necessary, split it into
1695 * lines, and return the lines in an allocated array, which is freed
1696 * by the caller. Folded lines are replaced by single long lines.
1698 char **
1699 detach_vcard_att(MAILSTREAM *stream, long int msgno, struct mail_bodystruct *body, char *partnum)
1701 unsigned long length;
1702 char *text = NULL, /* raw text */
1703 *dtext = NULL, /* content-decoded text */
1704 **res = NULL, /* array of decoded lines */
1705 *lptr, /* line pointer */
1706 *next_line;
1707 int i, count;
1709 /* make our own copy of text so we can change it */
1710 text = cpystr(mail_fetchbody_full(stream, msgno, partnum, &length,FT_PEEK));
1711 text[length] = '\0';
1713 /* decode the text */
1714 switch(body->encoding){
1715 default:
1716 case ENC7BIT:
1717 case ENC8BIT:
1718 case ENCBINARY:
1719 dtext = text;
1720 break;
1722 case ENCBASE64:
1723 dtext = (char *)rfc822_base64((unsigned char *)text,
1724 (unsigned long)strlen(text),
1725 &length);
1726 if(dtext)
1727 dtext[length] = '\0';
1728 else
1729 q_status_message(SM_ORDER | SM_DING, 3, 4,
1730 "Malformed B64 data in address book attachment.");
1732 if(text)
1733 fs_give((void **)&text);
1735 break;
1737 case ENCQUOTEDPRINTABLE:
1738 dtext = (char *)rfc822_qprint((unsigned char *)text,
1739 (unsigned long)strlen(text),
1740 &length);
1741 if(dtext)
1742 dtext[length] = '\0';
1743 else
1744 q_status_message(SM_ORDER | SM_DING, 3, 4,
1745 "Malformed QP data in address book attachment.");
1747 if(text)
1748 fs_give((void **)&text);
1750 break;
1753 /* count the lines */
1754 next_line = dtext;
1755 count = 0;
1756 for(lptr = next_line; lptr && *lptr; lptr = next_line){
1757 for(next_line = lptr; *next_line; next_line++){
1759 * Find end of current line.
1761 if(*next_line == '\r' && *(next_line+1) == '\n'){
1762 next_line += 2;
1763 /* not a folded line, count it */
1764 if(!*next_line || (*next_line != SPACE && *next_line != TAB))
1765 break;
1769 count++;
1772 /* allocate space for resulting array of lines */
1773 res = (char **)fs_get((count + 1) * sizeof(char *));
1774 memset((void *)res, 0, (count + 1) * sizeof(char *));
1775 next_line = dtext;
1776 for(i=0, lptr=next_line; lptr && *lptr && i < count; lptr=next_line, i++){
1778 * Move next_line to start of next line and null terminate
1779 * current line.
1781 for(next_line = lptr; *next_line; next_line++){
1783 * Find end of current line.
1785 if(*next_line == '\r' && *(next_line+1) == '\n'){
1786 next_line += 2;
1787 /* not a folded line, terminate it */
1788 if(!*next_line || (*next_line != SPACE && *next_line != TAB)){
1789 *(next_line-2) = '\0';
1790 break;
1795 /* turn folded lines into long lines in place */
1796 vcard_unfold(lptr);
1797 res[i] = cpystr(lptr);
1800 if(dtext)
1801 fs_give((void **)&dtext);
1803 res[count] = NULL;
1804 return(res);
1809 * Look for possible addresses in the first text part of a message for
1810 * use by TakeAddr command.
1811 * Returns the number of TA lines added.
1814 grab_addrs_from_body(MAILSTREAM *stream, long int msgno,
1815 struct mail_bodystruct *body, TA_S **ta_list)
1817 #define MAXLINESZ 2000
1818 char line[MAXLINESZ + 1];
1819 STORE_S *so;
1820 gf_o_t pc;
1821 SourceType src = CharStar;
1822 int added = 0, failure;
1823 char *partno = "1";
1824 ATTACH_S *a = ps_global->atmts;
1826 dprint((9, "\n - grab_addrs_from_body - \n"));
1829 * If it is text/plain or it is multipart with a first part of text/plain,
1830 * we want to continue, else forget it.
1832 if(!((body->type == TYPETEXT && body->subtype
1833 && ALLOWED_SUBTYPE(body->subtype))
1835 (body->type == TYPEMULTIPART && body->nested.part
1836 && body->nested.part->body.type == TYPETEXT
1837 && ALLOWED_SUBTYPE(body->nested.part->body.subtype))))
1838 return 0;
1840 #ifdef DOS
1841 src = TmpFileStar;
1842 #endif
1844 if((so = so_get(src, NULL, EDIT_ACCESS)) == NULL)
1845 return 0;
1847 gf_set_so_writec(&pc, so);
1849 if(body->type == TYPEMULTIPART
1850 && strucmp(body->subtype, "ALTERNATIVE") == 0
1851 && a && a+1 && (a+1)->shown)
1852 partno = "2";
1854 failure = !get_body_part_text(stream, body, msgno, partno, 0L, pc,
1855 NULL, NULL, GBPT_NONE);
1857 gf_clear_so_writec(so);
1859 if(failure){
1860 so_give(&so);
1861 return 0;
1864 so_seek(so, 0L, 0);
1866 while(get_line_of_message(so, line, sizeof(line))){
1867 ADDRESS *addr;
1868 char save_end, *start, *tmp_a_string, *tmp_personal, *p;
1869 int n;
1871 /* process each @ in the line */
1872 for(p = (char *) line; (start = mail_addr_scan(p, &n)); p = start + n){
1874 tmp_personal = NULL;
1876 if(start > line && *(start-1) == '<' && *(start+n) == '>'){
1877 int words, in_quote;
1878 char *fn_start;
1881 * Take a shot at looking for full name
1882 * If we can find a colon maybe we've got a header line
1883 * embedded in the body.
1884 * This is real ad hoc.
1888 * Go back until we run into a character that probably
1889 * isn't a fullname character.
1891 fn_start = start-1;
1892 in_quote = words = 0;
1893 while(fn_start > p && (in_quote || !(*(fn_start-1) == ':'
1894 || *(fn_start-1) == ';' || *(fn_start-1) == ','))){
1895 fn_start--;
1896 if(!in_quote && isspace((unsigned char)*fn_start))
1897 words++;
1898 else if(*fn_start == '"' &&
1899 (fn_start == p || *(fn_start-1) != '\\')){
1900 if(in_quote){
1901 in_quote = 0;
1902 break;
1904 else
1905 in_quote = 1;
1908 if(words == 5)
1909 break;
1912 /* wasn't a real quote, forget about fullname */
1913 if(in_quote)
1914 fn_start = start-1;
1916 /* Skip forward over the white space. */
1917 while(isspace((unsigned char)*fn_start) || *fn_start == '(')
1918 fn_start++;
1921 * Make sure the first word is capitalized.
1922 * (just so it is more likely it is a name)
1924 while(fn_start < start-1 &&
1925 !(isupper(*fn_start) || *fn_start == '"')){
1926 if(*fn_start == '('){
1927 fn_start++;
1928 continue;
1931 /* skip forward over this word */
1932 while(fn_start < start-1 &&
1933 !isspace((unsigned char)*fn_start))
1934 fn_start++;
1936 while(fn_start < start-1 &&
1937 isspace((unsigned char)*fn_start))
1938 fn_start++;
1941 if(fn_start < start-1){
1942 char *fn_end, save_fn_end;
1944 /* remove white space between fullname and start */
1945 fn_end = start-1;
1946 while(fn_end > fn_start
1947 && isspace((unsigned char)*(fn_end - 1)))
1948 fn_end--;
1950 save_fn_end = *fn_end;
1951 *fn_end = '\0';
1953 /* remove matching quotes */
1954 if(*fn_start == '"' && *(fn_end-1) == '"'){
1955 fn_start++;
1956 *fn_end = save_fn_end;
1957 fn_end--;
1958 save_fn_end = *fn_end;
1959 *fn_end = '\0';
1962 /* copy fullname */
1963 if(*fn_start)
1964 tmp_personal = cpystr(fn_start);
1966 *fn_end = save_fn_end;
1971 save_end = *(start+n);
1972 *(start+n) = '\0';
1973 /* rfc822_parse_adrlist feels free to destroy input so send copy */
1974 tmp_a_string = cpystr(start);
1975 *(start+n) = save_end;
1976 addr = NULL;
1977 ps_global->c_client_error[0] = '\0';
1978 rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
1979 if(tmp_a_string)
1980 fs_give((void **)&tmp_a_string);
1982 if(tmp_personal){
1983 if(addr){
1984 if(addr->personal)
1985 fs_give((void **)&addr->personal);
1987 addr->personal = tmp_personal;
1989 else
1990 fs_give((void **)&tmp_personal);
1993 if((addr && addr->host && addr->host[0] == '@') ||
1994 ps_global->c_client_error[0]){
1995 mail_free_address(&addr);
1996 continue;
1999 if(addr && addr->mailbox && addr->host){
2000 added++;
2001 *ta_list = fill_in_ta(ta_list, addr, 0, (char *)NULL);
2004 if(addr)
2005 mail_free_address(&addr);
2009 so_give(&so);
2010 return(added);
2015 * Get the next line of the object pointed to by source.
2016 * Skips empty lines.
2017 * Linebuf is a passed in buffer to put the line in.
2018 * Linebuf is null terminated and \r and \n chars are removed.
2019 * 0 is returned when there is no more input.
2022 get_line_of_message(STORE_S *source, char *linebuf, int linebuflen)
2024 int pos = 0;
2025 unsigned char c;
2027 if(linebuflen < 2)
2028 return 0;
2030 while(so_readc(&c, source)){
2031 if(c == '\n' || c == '\r'){
2032 if(pos > 0)
2033 break;
2035 else{
2036 linebuf[pos++] = c;
2037 if(pos >= linebuflen - 2)
2038 break;
2042 linebuf[pos] = '\0';
2044 return(pos);
2049 * Inserts a new entry based on addr in the TA list.
2050 * Makes sure everything is UTF-8. That is,
2051 * Values used for strvalue will be converted to UTF-8 first.
2052 * Personal name fields of addr args will be converted to UTF-8.
2054 * Args: old_current -- the TA list
2055 * addr -- the address for this line
2056 * checked -- start this line out checked
2057 * print_string -- if non-null, this line is informational and is just
2058 * printed out, it can't be selected
2060 TA_S *
2061 fill_in_ta(TA_S **old_current, struct mail_address *addr, int checked, char *print_string)
2063 TA_S *new_current;
2065 /* c-client convention for group syntax, which we don't want to deal with */
2066 if(!print_string && (!addr || !addr->mailbox || !addr->host))
2067 new_current = *old_current;
2068 else{
2070 new_current = new_taline(old_current);
2071 if(print_string && addr){ /* implied List when here */
2072 new_current->frwrded = 1;
2073 new_current->skip_it = 0;
2074 new_current->print = 0;
2075 new_current->checked = checked != 0;
2076 new_current->addr = copyaddrlist(addr);
2077 decode_addr_names_to_utf8(new_current->addr);
2078 new_current->strvalue = cpystr(print_string);
2079 convert_possibly_encoded_str_to_utf8(&new_current->strvalue);
2081 else if(print_string){
2082 new_current->frwrded = 0;
2083 new_current->skip_it = 1;
2084 new_current->print = 1;
2085 new_current->checked = 0;
2086 new_current->addr = 0;
2087 new_current->strvalue = cpystr(print_string);
2088 convert_possibly_encoded_str_to_utf8(&new_current->strvalue);
2090 else{
2091 new_current->frwrded = 0;
2092 new_current->skip_it = 0;
2093 new_current->print = 0;
2094 new_current->checked = checked != 0;
2095 new_current->addr = copyaddr(addr);
2096 decode_addr_names_to_utf8(new_current->addr);
2097 if(new_current->addr && new_current->addr->personal)
2098 remove_quotes(new_current->addr->personal);
2099 if(addr->host[0] == '.')
2100 new_current->strvalue = cpystr("Error in address (ok to try Take anyway)");
2101 else{
2102 char *bufp;
2103 size_t len;
2105 len = est_size(new_current->addr);
2106 bufp = (char *) fs_get(len * sizeof(char));
2107 new_current->strvalue = cpystr(addr_string(new_current->addr, bufp, len));
2108 fs_give((void **) &bufp);
2111 convert_possibly_encoded_str_to_utf8(&new_current->strvalue);
2115 return(new_current);
2120 eliminate_dups_and_us(TA_S *list)
2122 return(eliminate_dups_and_maybe_us(list, 1));
2127 eliminate_dups_but_not_us(TA_S *list)
2129 return(eliminate_dups_and_maybe_us(list, 0));
2134 * Look for dups in list and mark them so we'll skip them.
2136 * We also throw out any addresses that are us (if us_too is set), since
2137 * we won't usually want to take ourselves to the addrbook.
2138 * On the otherhand, if there is nothing but us, we leave it.
2140 * Don't toss out forwarded entries.
2142 * Returns the number of dups eliminated that were also selected.
2145 eliminate_dups_and_maybe_us(TA_S *list, int us_too)
2147 ADDRESS *a, *b;
2148 TA_S *ta, *tb;
2149 int eliminated = 0;
2151 /* toss dupes */
2152 for(ta = list; ta; ta = ta->next){
2154 if(ta->skip_it || ta->frwrded) /* already tossed or forwarded */
2155 continue;
2157 a = ta->addr;
2159 /* Check addr "a" versus tail of the list */
2160 for(tb = ta->next; tb; tb = tb->next){
2161 if(tb->skip_it || tb->frwrded) /* already tossed or forwarded */
2162 continue;
2164 b = tb->addr;
2165 if(dup_addrs(a, b)){
2166 if(ta->checked || !(tb->checked)){
2167 /* throw out b */
2168 if(tb->checked)
2169 eliminated++;
2171 tb->skip_it = 1;
2173 else{ /* tb->checked */
2174 /* throw out a */
2175 ta->skip_it = 1;
2176 break;
2182 if(us_too){
2183 /* check whether all remaining addrs are us */
2184 for(ta = list; ta; ta = ta->next){
2186 if(ta->skip_it) /* already tossed */
2187 continue;
2189 if(ta->frwrded) /* forwarded entry, so not us */
2190 break;
2192 a = ta->addr;
2194 if(!address_is_us(a, ps_global))
2195 break;
2199 * if at least one address that isn't us, remove all of us from
2200 * the list
2202 if(ta){
2203 for(ta = list; ta; ta = ta->next){
2205 if(ta->skip_it || ta->frwrded) /* already tossed or forwarded */
2206 continue;
2208 a = ta->addr;
2210 if(address_is_us(a, ps_global)){
2211 if(ta->checked)
2212 eliminated++;
2214 /* throw out a */
2215 ta->skip_it = 1;
2221 return(eliminated);
2226 * Returns 1 if x and y match, 0 if not.
2229 dup_addrs(struct mail_address *x, struct mail_address *y)
2231 return(x && y && strucmp(x->mailbox, y->mailbox) == 0
2232 && strucmp(x->host, y->host) == 0);