* Remove the "SetRole" option when opening a URL.
[alpine.git] / pith / takeaddr.c
blob246e086167c493ba3e1d2d0cc59a09ac8600ffd4
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: takeaddr.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2020 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 takeaddr.c
21 Mostly support for Take Address command.
22 ====*/
25 #include "../pith/headers.h"
26 #include "../pith/takeaddr.h"
27 #include "../pith/conf.h"
28 #include "../pith/bldaddr.h"
29 #include "../pith/adrbklib.h"
30 #include "../pith/copyaddr.h"
31 #include "../pith/addrstring.h"
32 #include "../pith/status.h"
33 #include "../pith/mailview.h"
34 #include "../pith/reply.h"
35 #include "../pith/url.h"
36 #include "../pith/mailpart.h"
37 #include "../pith/sequence.h"
38 #include "../pith/stream.h"
39 #include "../pith/busy.h"
40 #include "../pith/ablookup.h"
41 #include "../pith/list.h"
44 static char *fakedomain = "@";
45 long msgno_for_pico_callback;
46 BODY *body_for_pico_callback = NULL;
47 ENVELOPE *env_for_pico_callback = NULL;
51 * Previous selectable TA line.
52 * skips over the elements with skip_it set
54 TA_S *
55 pre_sel_taline(TA_S *current)
57 TA_S *p;
59 if(!current)
60 return NULL;
62 p = current->prev;
63 while(p && p->skip_it)
64 p = p->prev;
66 return(p);
71 * Previous TA line, selectable or just printable.
72 * skips over the elements with skip_it set
74 TA_S *
75 pre_taline(TA_S *current)
77 TA_S *p;
79 if(!current)
80 return NULL;
82 p = current->prev;
83 while(p && (p->skip_it && !p->print))
84 p = p->prev;
86 return(p);
91 * Next selectable TA line.
92 * skips over the elements with skip_it set
94 TA_S *
95 next_sel_taline(TA_S *current)
97 TA_S *p;
99 if(!current)
100 return NULL;
102 p = current->next;
103 while(p && p->skip_it)
104 p = p->next;
106 return(p);
111 * Next TA line, including print only lines.
112 * skips over the elements with skip_it set unless they are print lines
114 TA_S *
115 next_taline(TA_S *current)
117 TA_S *p;
119 if(!current)
120 return NULL;
122 p = current->next;
123 while(p && (p->skip_it && !p->print))
124 p = p->next;
126 return(p);
131 * Mark all of the addresses with a check.
133 * Args: f_line -- the first ta line
135 * Returns the number of lines checked.
138 ta_mark_all(TA_S *f_line)
140 TA_S *ctmp;
141 int how_many_selected = 0;
143 for(ctmp = f_line; ctmp; ctmp = next_sel_taline(ctmp)){
144 ctmp->checked = 1;
145 how_many_selected++;
148 return(how_many_selected);
153 * Does the takeaddr list consist of a single address?
155 * Args: f_line -- the first ta line
157 * Returns 1 if only one address and 0 otherwise.
160 is_talist_of_one(TA_S *f_line)
162 if(f_line == NULL)
163 return 0;
165 /* there is at least one, see if there are two */
166 if(next_sel_taline(f_line) != NULL)
167 return 0;
169 return 1;
174 * Turn off all of the check marks.
176 * Args: f_line -- the first ta line
178 * Returns the number of lines checked (0).
181 ta_unmark_all(TA_S *f_line)
183 TA_S *ctmp;
185 for(ctmp = f_line; ctmp; ctmp = ctmp->next)
186 ctmp->checked = 0;
188 return 0;
193 * new_taline - create new TA_S, zero it out, and insert it after current.
194 * NOTE current gets set to the new TA_S, too!
196 TA_S *
197 new_taline(TA_S **current)
199 TA_S *p;
201 p = (TA_S *)fs_get(sizeof(TA_S));
202 memset((void *)p, 0, sizeof(TA_S));
203 if(current){
204 if(*current){
205 p->next = (*current)->next;
206 (*current)->next = p;
207 p->prev = *current;
208 if(p->next)
209 p->next->prev = p;
212 *current = p;
215 return(p);
219 void
220 free_taline(TA_S **p)
222 if(p && *p){
223 if((*p)->addr)
224 mail_free_address(&(*p)->addr);
226 if((*p)->strvalue)
227 fs_give((void **)&(*p)->strvalue);
229 if((*p)->nickname)
230 fs_give((void **)&(*p)->nickname);
232 if((*p)->fullname)
233 fs_give((void **)&(*p)->fullname);
235 if((*p)->fcc)
236 fs_give((void **)&(*p)->fcc);
238 if((*p)->comment)
239 fs_give((void **)&(*p)->comment);
241 if((*p)->prev)
242 (*p)->prev->next = (*p)->next;
244 if((*p)->next)
245 (*p)->next->prev = (*p)->prev;
247 fs_give((void **)p);
252 void
253 free_talines(TA_S **ta_list)
255 TA_S *current, *ctmp;
257 if(ta_list && (current = (*ta_list))){
258 while(current->prev)
259 current = current->prev;
261 while(current){
262 ctmp = current->next;
263 free_taline(&current);
264 current = ctmp;
267 *ta_list = NULL;
272 void
273 free_ltline(LINES_TO_TAKE **p)
275 if(p && *p){
276 if((*p)->printval)
277 fs_give((void **)&(*p)->printval);
279 if((*p)->exportval)
280 fs_give((void **)&(*p)->exportval);
282 if((*p)->prev)
283 (*p)->prev->next = (*p)->next;
285 if((*p)->next)
286 (*p)->next->prev = (*p)->prev;
288 fs_give((void **)p);
293 void
294 free_ltlines(LINES_TO_TAKE **lt_list)
296 LINES_TO_TAKE *current, *ctmp;
298 if(lt_list && (current = (*lt_list))){
299 while(current->prev)
300 current = current->prev;
302 while(current){
303 ctmp = current->next;
304 free_ltline(&current);
305 current = ctmp;
308 *lt_list = NULL;
314 * Return the first selectable TakeAddr line.
316 * Args: q -- any line in the list
318 TA_S *
319 first_sel_taline(TA_S *q)
321 TA_S *first;
323 if(q == NULL)
324 return NULL;
326 first = NULL;
327 /* back up to the head of the list */
328 while(q){
329 first = q;
330 q = pre_sel_taline(q);
334 * If first->skip_it, that means we were already past the first
335 * legitimate line, so we have to look in the other direction.
337 if(first->skip_it)
338 first = next_sel_taline(first);
340 return(first);
345 * Return the last selectable TakeAddr line.
347 * Args: q -- any line in the list
349 TA_S *
350 last_sel_taline(TA_S *q)
352 TA_S *last;
354 if(q == NULL)
355 return NULL;
357 last = NULL;
358 /* go to the end of the list */
359 while(q){
360 last = q;
361 q = next_sel_taline(q);
365 * If last->skip_it, that means we were already past the last
366 * legitimate line, so we have to look in the other direction.
368 if(last->skip_it)
369 last = pre_sel_taline(last);
371 return(last);
376 * Return the first TakeAddr line, selectable or just printable.
378 * Args: q -- any line in the list
380 TA_S *
381 first_taline(TA_S *q)
383 TA_S *first;
385 if(q == NULL)
386 return NULL;
388 first = NULL;
389 /* back up to the head of the list */
390 while(q){
391 first = q;
392 q = pre_taline(q);
396 * If first->skip_it, that means we were already past the first
397 * legitimate line, so we have to look in the other direction.
399 if(first->skip_it && !first->print)
400 first = next_taline(first);
402 return(first);
407 * Find the first TakeAddr line which is checked, beginning with the
408 * passed in line.
410 * Args: head -- A passed in TakeAddr line, usually will be the first
412 * Result: returns a pointer to the first checked line.
413 * NULL if no such line
415 TA_S *
416 first_checked(TA_S *head)
418 TA_S *p;
420 p = head;
422 for(p = head; p; p = next_sel_taline(p))
423 if(p->checked && !p->skip_it)
424 break;
426 return(p);
431 * Form a list of strings which are addresses to go in a list.
432 * These are entries in a list, so can be full rfc822 addresses.
433 * The strings are allocated here.
435 * Args: head -- A passed in TakeAddr line, usually will be the first
437 * Result: returns an allocated array of pointers to allocated strings
439 char **
440 list_of_checked(TA_S *head)
442 TA_S *p;
443 int count;
444 char **list, **cur;
445 ADDRESS *a;
447 /* first count them */
448 for(p = head, count = 0; p; p = next_sel_taline(p)){
449 if(p->checked && !p->skip_it){
450 if(p->frwrded){
452 * Remove fullname, fcc, comment, and nickname since not
453 * appropriate for list values.
455 if(p->fullname)
456 fs_give((void **)&p->fullname);
457 if(p->fcc)
458 fs_give((void **)&p->fcc);
459 if(p->comment)
460 fs_give((void **)&p->comment);
461 if(p->nickname)
462 fs_give((void **)&p->nickname);
464 for(a = p->addr; a; a = a->next)
465 count++;
467 else{
469 * Don't even attempt to include bogus addresses in
470 * the list. If the user wants to get at those, they
471 * have to try taking only that single address.
473 if(p->addr && p->addr->host && p->addr->host[0] == '.')
474 p->skip_it = 1;
475 else
476 count++;
481 /* allocate pointers */
482 list = (char **)fs_get((count + 1) * sizeof(char *));
483 memset((void *)list, 0, (count + 1) * sizeof(char *));
485 cur = list;
487 /* allocate and point to address strings */
488 for(p = head; p; p = next_sel_taline(p)){
489 if(p->checked && !p->skip_it){
490 if(p->frwrded)
491 for(a = p->addr; a; a = a->next){
492 ADDRESS *next_addr;
493 char *bufp;
494 size_t len;
496 next_addr = a->next;
497 a->next = NULL;
498 len = est_size(a);
499 bufp = (char *) fs_get(len * sizeof(char));
500 *cur++ = cpystr(addr_string(a, bufp, len));
501 a->next = next_addr;
502 fs_give((void **)&bufp);
504 else
505 *cur++ = cpystr(p->strvalue);
509 return(list);
512 /* jpf: This used to be the guts of cmd_take_addr, but I made this
513 * function so I could generalize with WP. It still has some display
514 * stuff that we need make sure not to do when in WP mode.
516 * Set things up for when we take addresses
518 * Args: ps -- pine state
519 * msgmap -- the MessageMap
520 * ta_ret -- the take addr lines
521 * selected_num -- the number of selectable addresses
522 * flags -- takeaddr flags, like whether or not
523 * we're doing aggs, and whether to do prompts
525 * Returns: -1 on "failure"
528 set_up_takeaddr(int cmd, struct pine *ps, MSGNO_S *msgmap, TA_S **ta_ret,
529 int *selected_num, int flags, int (*att_addr_f)(TA_S *, int))
531 long i;
532 ENVELOPE *env;
533 int how_many_selected = 0,
534 added, rtype = 0,
535 we_cancel = 0,
536 special_processing = 0,
537 select_froms = 0;
538 TA_S *current = NULL,
539 *prev_comment_line,
540 *ta;
541 BODY **body_h,
542 *special_body = NULL,
543 *body = NULL;
545 dprint((2, "\n - taking address into address book - \n"));
547 if(!(cmd == 'a' || cmd == 'e'))
548 return -1;
550 if((flags & TA_AGG) && !pseudo_selected(ps_global->mail_stream, msgmap))
551 return -1;
553 if(mn_get_total(msgmap) > 0 && mn_total_cur(msgmap) == 1)
554 special_processing = 1;
556 if(flags & TA_AGG)
557 select_froms = 1;
559 /* this is a non-selectable label */
560 current = fill_in_ta(&current, (ADDRESS *) NULL, 0,
561 /* TRANSLATORS: This is a heading describing some addresses the
562 user will have the chance to choose from. */
563 _(" These entries are taken from the attachments "));
564 prev_comment_line = current;
567 * Add addresses from special attachments for each message.
569 added = 0;
570 for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
571 env = pine_mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i), &body);
572 if(!env){
573 q_status_message(SM_ORDER | SM_DING, 3, 4,
574 _("Can't take address into address book. Error accessing folder"));
575 rtype = -1;
576 goto doneskee;
579 added += process_vcard_atts(ps->mail_stream, mn_m2raw(msgmap, i),
580 body, body, NULL, &current);
583 if(!(flags & TA_AGG)
584 && added > 1
585 && att_addr_f
586 && (*att_addr_f)(current, added) <= 0)
587 goto doneskee;
589 if(!(flags & TA_NOPROMPT))
590 we_cancel = busy_cue(NULL, NULL, 1);
593 * add a comment line to separate attachment address lines
594 * from header address lines
596 if(added > 0){
597 current = fill_in_ta(&current, (ADDRESS *) NULL, 0,
598 /* TRANSLATORS: msg is message */
599 _(" These entries are taken from the msg headers "));
600 prev_comment_line = current;
601 how_many_selected += added;
602 select_froms = 0;
604 else{ /* turn off header comment, and no separator comment */
605 prev_comment_line->print = 0;
606 prev_comment_line = NULL;
610 * Add addresses from headers of messages.
612 for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
614 if(special_processing)
615 body_h = &special_body;
616 else
617 body_h = NULL;
619 env = pine_mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i),
620 body_h);
621 if(!env){
622 if(we_cancel)
623 cancel_busy_cue(-1);
625 q_status_message(SM_ORDER | SM_DING, 3, 4,
626 _("Can't take address into address book. Error accessing folder"));
627 rtype = -1;
628 goto doneskee;
631 added = add_addresses_to_talist(ps, i, "from", &current,
632 env->from, select_froms);
633 if(select_froms)
634 how_many_selected += added;
636 if(!address_is_same(env->from, env->reply_to))
637 (void)add_addresses_to_talist(ps, i, "reply-to", &current,
638 env->reply_to, 0);
640 if(!address_is_same(env->from, env->sender))
641 (void)add_addresses_to_talist(ps, i, "sender", &current,
642 env->sender, 0);
644 (void)add_addresses_to_talist(ps, i, "to", &current, env->to, 0);
645 (void)add_addresses_to_talist(ps, i, "cc", &current, env->cc, 0);
646 (void)add_addresses_to_talist(ps, i, "bcc", &current, env->bcc, 0);
650 * Check to see if we added an explanatory line about the
651 * header addresses but no header addresses. If so, remove the
652 * explanatory line.
654 if(prev_comment_line){
655 how_many_selected -=
656 eliminate_dups_and_us(first_sel_taline(current));
657 for(ta = prev_comment_line->next; ta; ta = ta->next)
658 if(!ta->skip_it)
659 break;
661 /* all entries were skip_it entries, turn off print */
662 if(!ta){
663 prev_comment_line->print = 0;
664 prev_comment_line = NULL;
669 * If there's only one message we let the user view it using ^T
670 * from the header editor.
672 if(!(flags & TA_NOPROMPT) && special_processing && env && special_body){
673 msgno_for_pico_callback = mn_m2raw(msgmap, mn_first_cur(msgmap));
674 body_for_pico_callback = special_body;
675 env_for_pico_callback = env;
677 else{
678 env_for_pico_callback = NULL;
679 body_for_pico_callback = NULL;
682 current = fill_in_ta(&current, (ADDRESS *)NULL, 0,
683 _(" Below this line are some possibilities taken from the text of the msg "));
684 prev_comment_line = current;
687 * Add addresses from the text of the body.
689 added = 0;
690 for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
692 body = NULL;
693 env = pine_mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i),
694 &body);
695 if(env && body)
696 added += grab_addrs_from_body(ps->mail_stream,
697 mn_m2raw(msgmap, i),
698 body, &current);
701 if(added == 0){
702 prev_comment_line->print = 0;
703 prev_comment_line = NULL;
707 * Check to see if all we added are dups.
708 * If so, remove the comment line.
710 if(prev_comment_line){
711 how_many_selected -= eliminate_dups_and_us(first_sel_taline(current));
712 for(ta = prev_comment_line->next; ta; ta = ta->next)
713 if(!ta->skip_it)
714 break;
716 /* all entries were skip_it entries, turn off print */
717 if(!ta)
718 prev_comment_line->print = 0;
721 if(we_cancel)
722 cancel_busy_cue(-1);
724 doneskee:
725 env_for_pico_callback = NULL;
726 body_for_pico_callback = NULL;
728 ps->mangled_screen = 1;
730 if(flags & TA_AGG)
731 restore_selected(msgmap);
733 *ta_ret = current;
735 if(selected_num)
736 *selected_num = how_many_selected;
738 return(rtype);
743 convert_ta_to_lines(TA_S *ta_list, LINES_TO_TAKE **old_current)
745 ADDRESS *a;
746 TA_S *ta;
747 LINES_TO_TAKE *new_current;
748 char *exportval, *printval;
749 int ret = 0;
751 for(ta = first_sel_taline(ta_list);
753 ta = next_sel_taline(ta)){
754 if(ta->skip_it)
755 continue;
757 if(ta->frwrded){
758 if(ta->fullname)
759 fs_give((void **)&ta->fullname);
760 if(ta->fcc)
761 fs_give((void **)&ta->fcc);
762 if(ta->comment)
763 fs_give((void **)&ta->comment);
764 if(ta->nickname)
765 fs_give((void **)&ta->nickname);
767 else if(ta->addr && ta->addr->host && ta->addr->host[0] == '.')
768 ta->skip_it = 1;
770 if(ta->frwrded){
771 for(a = ta->addr; a; a = a->next){
772 ADDRESS *next_addr;
774 if(a->host && a->host[0] == '.')
775 continue;
777 next_addr = a->next;
778 a->next = NULL;
780 exportval = cpystr(simple_addr_string(a, tmp_20k_buf,
781 SIZEOF_20KBUF));
782 if(!exportval || !exportval[0]){
783 if(exportval)
784 fs_give((void **)&exportval);
786 a->next = next_addr;
787 continue;
790 printval = addr_list_string(a, NULL, 0);
791 if(!printval || !printval[0]){
792 if(printval)
793 fs_give((void **)&printval);
795 printval = cpystr(exportval);
798 new_current = new_ltline(old_current);
799 new_current->flags = LT_NONE;
801 new_current->printval = printval;
802 new_current->exportval = exportval;
803 a->next = next_addr;
804 ret++;
807 else{
808 if(ta->addr){
809 exportval = cpystr(simple_addr_string(ta->addr, tmp_20k_buf,
810 SIZEOF_20KBUF));
811 if(exportval && exportval[0]){
812 new_current = new_ltline(old_current);
813 new_current->flags = LT_NONE;
815 new_current->exportval = exportval;
817 if(ta->strvalue && ta->strvalue[0])
818 new_current->printval = cpystr(ta->strvalue);
819 else
820 new_current->printval = cpystr(new_current->exportval);
822 ret++;
824 else if(exportval)
825 fs_give((void **)&exportval);
830 return(ret);
835 * new_ltline - create new LINES_TO_TAKE, zero it out,
836 * and insert it after current.
837 * NOTE current gets set to the new current.
839 LINES_TO_TAKE *
840 new_ltline(LINES_TO_TAKE **current)
842 LINES_TO_TAKE *p;
844 p = (LINES_TO_TAKE *)fs_get(sizeof(LINES_TO_TAKE));
845 memset((void *)p, 0, sizeof(LINES_TO_TAKE));
846 if(current){
847 if(*current){
848 p->next = (*current)->next;
849 (*current)->next = p;
850 p->prev = *current;
851 if(p->next)
852 p->next->prev = p;
855 *current = p;
858 return(p);
863 add_addresses_to_talist(struct pine *ps, long int msgno, char *field,
864 TA_S **old_current, struct mail_address *adrlist, int checked)
866 ADDRESS *addr;
867 int added = 0;
869 for(addr = adrlist; addr; addr = addr->next){
870 if(addr->host && addr->host[0] == '.'){
871 char *h, *fields[2];
873 fields[0] = field;
874 fields[1] = NULL;
875 if((h = pine_fetchheader_lines(ps->mail_stream, msgno,NULL,fields)) != NULL){
876 char *p, fname[32];
877 int q;
879 q = strlen(h);
881 snprintf(fname, sizeof(fname), "%s:", field);
882 for(p = h; (p = strstr(p, fname)) != NULL; )
883 rplstr(p, q-(p-h), strlen(fname), ""); /* strip field strings */
885 sqznewlines(h); /* blat out CR's & LF's */
886 for(p = h; (p = strchr(p, TAB)) != NULL; )
887 *p++ = ' '; /* turn TABs to space */
889 if(*h){
890 unsigned long l;
891 ADDRESS *new_addr;
892 size_t ll;
894 new_addr = (ADDRESS *)fs_get(sizeof(ADDRESS));
895 memset(new_addr, 0, sizeof(ADDRESS));
898 * Base64 armor plate the gunk to protect against
899 * c-client quoting in output.
901 p = (char *)rfc822_binary((void *)h,
902 (unsigned long)strlen(h), &l);
903 sqznewlines(p);
904 ll = strlen(p) + 3;
905 new_addr->mailbox = (char *) fs_get((ll+1) * sizeof(char));
906 snprintf(new_addr->mailbox, ll+1, "&%s", p);
907 fs_give((void **)&p);
908 new_addr->host = cpystr(RAWFIELD);
909 fill_in_ta(old_current, new_addr, 0, (char *)NULL);
910 mail_free_address(&new_addr);
913 fs_give((void **)&h);
916 return(0);
920 /* no syntax errors in list, add them all */
921 for(addr = adrlist; addr; addr = addr->next){
922 fill_in_ta(old_current, addr, checked, (char *)NULL);
923 added++;
926 return(added);
931 * Look for Text/directory attachments and process them.
932 * Return number of lines added to the ta_list.
935 process_vcard_atts(MAILSTREAM *stream, long int msgno,
936 struct mail_bodystruct *root, struct mail_bodystruct *body,
937 char *partnum, TA_S **ta_list)
939 char *addrs, /* comma-separated list of addresses */
940 *value,
941 *encoded,
942 *escval,
943 *nickname,
944 *fullname,
945 *struct_name,
946 *fcc,
947 *tag,
948 *decoded,
949 *num = NULL,
950 *defaulttype = NULL,
951 *charset = NULL,
952 *altcharset,
953 *comma,
955 *comment,
956 *title,
957 **lines = NULL,
958 **ll;
959 size_t space;
960 int used = 0,
961 is_encoded = 0,
962 vcard_nesting = 0,
963 selected = 0;
964 PART *part;
965 PARAMETER *parm;
966 static char a_comma = ',';
969 * Look through all the subparts searching for our special type.
971 if(body && body->type == TYPEMULTIPART){
972 for(part = body->nested.part; part; part = part->next)
973 selected += process_vcard_atts(stream, msgno, root, &part->body,
974 partnum, ta_list);
976 /* only look in rfc822 subtype of type message */
977 else if(body && body->type == TYPEMESSAGE
978 && body->subtype && !strucmp(body->subtype, "rfc822")){
979 selected += process_vcard_atts(stream, msgno, root,
980 body->nested.msg->body,
981 partnum, ta_list);
983 /* found one (TYPEAPPLICATION for back compat.) */
984 else if(body && MIME_VCARD(body->type,body->subtype)){
986 dprint((4, "\n - found abook attachment to process - \n"));
989 * The Text/directory type has a possible parameter called
990 * "defaulttype" that we need to look for (this is from an old draft
991 * and is supported only for backwards compatibility). There is
992 * also a possible default "charset". (Default charset is also from
993 * an old draft and is no longer allowed.) We don't care about any of
994 * the other parameters.
996 for(parm = body->parameter; parm; parm = parm->next)
997 if(!strucmp("defaulttype", parm->attribute))
998 break;
1000 if(parm)
1001 defaulttype = parm->value;
1003 /* ...and look for possible charset parameter */
1004 for(parm = body->parameter; parm; parm = parm->next)
1005 if(!strucmp("charset", parm->attribute))
1006 break;
1008 if(parm)
1009 charset = parm->value;
1011 num = partnum ? cpystr(partnum) : partno(root, body);
1012 lines = detach_vcard_att(stream, msgno, body, num);
1013 if(num)
1014 fs_give((void **)&num);
1016 nickname = fullname = comment = title = fcc = struct_name = NULL;
1017 #define CHUNK (size_t)500
1018 space = CHUNK;
1019 /* make comma-separated list of email addresses in addrs */
1020 addrs = (char *)fs_get((space+1) * sizeof(char));
1021 *addrs = '\0';
1022 for(ll = lines; ll && *ll; ll++){
1023 altcharset = NULL;
1024 decoded = NULL;
1025 escval = NULL;
1026 is_encoded = 0;
1027 value = getaltcharset(*ll, &tag, &altcharset, &is_encoded);
1029 if(!value || !tag){
1030 if(tag)
1031 fs_give((void **)&tag);
1033 if(altcharset)
1034 fs_give((void **)&altcharset);
1036 continue;
1039 if(is_encoded){
1040 unsigned long length;
1042 decoded = (char *)rfc822_base64((unsigned char *)value,
1043 (unsigned long)strlen(value),
1044 &length);
1045 if(decoded){
1046 decoded[length] = '\0';
1047 value = decoded;
1049 else
1050 value = "<malformed B64 data>";
1053 /* support default tag (back compat.) */
1054 if(*tag == '\0' && defaulttype){
1055 fs_give((void **)&tag);
1056 tag = cpystr(defaulttype);
1059 if(!strucmp(tag, "begin")){ /* vCard BEGIN */
1060 vcard_nesting++;
1062 else if(!strucmp(tag, "end")){ /* vCard END */
1063 if(--vcard_nesting == 0){
1064 if(title){
1065 if(!comment)
1066 comment = title;
1067 else
1068 fs_give((void **)&title);
1071 /* add this entry */
1072 selected += vcard_to_ta(addrs, fullname, struct_name,
1073 nickname, comment, fcc, ta_list);
1074 if(addrs)
1075 *addrs = '\0';
1077 used = 0;
1078 nickname = fullname = comment = title = fcc = NULL;
1079 struct_name = NULL;
1082 /* add another address to addrs */
1083 else if(!strucmp(tag, "email")){
1084 if(*value){
1085 escval = vcard_unescape(value);
1086 encoded = encode_fullname_of_addrstring(escval,
1087 (altcharset && *altcharset) ? altcharset
1088 : (charset && *charset)
1089 ? charset
1090 : ps_global->posting_charmap);
1091 if(encoded){
1092 /* allocate more space */
1093 if((used + strlen(encoded) + 1) > space){
1094 space += CHUNK;
1095 fs_resize((void **)&addrs, (space+1)*sizeof(char));
1098 if(*addrs){
1099 strncat(addrs, ",", space+1-1-strlen(addrs));
1100 addrs[space-1] = '\0';
1103 strncat(addrs, encoded, space+1-1-strlen(addrs));
1104 addrs[space-1] = '\0';
1105 used += (strlen(encoded) + 1);
1106 fs_give((void **)&encoded);
1110 else if(!strucmp(tag, "note") || !strucmp(tag, "misc")){
1111 if(*value){
1112 escval = vcard_unescape(value);
1113 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
1114 (unsigned char *)escval,
1115 (altcharset && *altcharset) ? altcharset
1116 : (charset && *charset)
1117 ? charset
1118 : ps_global->posting_charmap);
1119 if(encoded){
1120 /* Last one wins */
1121 if(comment)
1122 fs_give((void **)&comment);
1124 comment = cpystr(encoded);
1128 else if(!strucmp(tag, "title")){
1129 if(*value){
1130 escval = vcard_unescape(value);
1131 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
1132 (unsigned char *)escval,
1133 (altcharset && *altcharset) ? altcharset
1134 : (charset && *charset)
1135 ? charset
1136 : ps_global->posting_charmap);
1137 if(encoded){
1138 /* Last one wins */
1139 if(title)
1140 fs_give((void **)&title);
1142 title = cpystr(encoded);
1146 else if(!strucmp(tag, "x-fcc")){
1147 if(*value){
1148 escval = vcard_unescape(value);
1149 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
1150 (unsigned char *)escval,
1151 (altcharset && *altcharset) ? altcharset
1152 : (charset && *charset)
1153 ? charset
1154 : ps_global->posting_charmap);
1155 if(encoded){
1156 /* Last one wins */
1157 if(fcc)
1158 fs_give((void **)&fcc);
1160 fcc = cpystr(encoded);
1164 else if(!strucmp(tag, "fn") || !strucmp(tag, "cn")){
1165 if(*value){
1166 escval = vcard_unescape(value);
1167 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
1168 (unsigned char *)escval,
1169 (altcharset && *altcharset) ? altcharset
1170 : (charset && *charset)
1171 ? charset
1172 : ps_global->posting_charmap);
1173 if(encoded){
1174 /* Last one wins */
1175 if(fullname)
1176 fs_give((void **)&fullname);
1178 fullname = cpystr(encoded);
1182 else if(!strucmp(tag, "n")){
1184 * This is a structured name field. It has up to five
1185 * fields separated by ';'. The fields are Family Name (which
1186 * we'll take to be Last name for our purposes), Given Name
1187 * (First name for us), additional names, prefixes, and
1188 * suffixes. We'll ignore prefixes and suffixes.
1190 * If we find one of these records we'll use it in preference
1191 * to the formatted name field (fn).
1193 if(*value && *value != ';'){
1194 char *last, *first, *middle = NULL, *rest = NULL;
1195 char *esclast, *escfirst, *escmiddle;
1196 static char a_semi = ';';
1198 last = value;
1200 first = NULL;
1201 p = last;
1202 while(p && (first = strindex(p, a_semi)) != NULL){
1203 if(first > last && first[-1] != '\\')
1204 break;
1205 else
1206 p = first + 1;
1209 if(first)
1210 *first = '\0';
1212 if(first && *(first+1) && *(first+1) != a_semi){
1213 first++;
1215 middle = NULL;
1216 p = first;
1217 while(p && (middle = strindex(p, a_semi)) != NULL){
1218 if(middle > first && middle[-1] != '\\')
1219 break;
1220 else
1221 p = middle + 1;
1224 if(middle)
1225 *middle = '\0';
1227 if(middle && *(middle+1) && *(middle+1) != a_semi){
1228 middle++;
1230 rest = NULL;
1231 p = middle;
1232 while(p && (rest = strindex(p, a_semi)) != NULL){
1233 if(rest > middle && rest[-1] != '\\')
1234 break;
1235 else
1236 p = rest + 1;
1239 /* we don't care about the rest */
1240 if(rest)
1241 *rest = '\0';
1243 else
1244 middle = NULL;
1246 else
1247 first = NULL;
1250 * Structured name pieces can have multiple values.
1251 * We're just going to take the first value in each part.
1252 * Skip excaped commas, though.
1254 p = last;
1255 while(p && (comma = strindex(p, a_comma)) != NULL){
1256 if(comma > last && comma[-1] != '\\'){
1257 *comma = '\0';
1258 break;
1260 else
1261 p = comma + 1;
1264 p = first;
1265 while(p && (comma = strindex(p, a_comma)) != NULL){
1266 if(comma > first && comma[-1] != '\\'){
1267 *comma = '\0';
1268 break;
1270 else
1271 p = comma + 1;
1274 p = middle;
1275 while(p && (comma = strindex(p, a_comma)) != NULL){
1276 if(comma > middle && comma[-1] != '\\'){
1277 *comma = '\0';
1278 break;
1280 else
1281 p = comma + 1;
1284 esclast = vcard_unescape(last);
1285 escfirst = vcard_unescape(first);
1286 escmiddle = vcard_unescape(middle);
1287 snprintf(tmp_20k_buf+10000, SIZEOF_20KBUF-10000, "%s%s%s%s%s",
1288 esclast ? esclast : "",
1289 escfirst ? ", " : "",
1290 escfirst ? escfirst : "",
1291 escmiddle ? " " : "",
1292 escmiddle ? escmiddle : "");
1293 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1295 encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF-10000,
1296 (unsigned char *)tmp_20k_buf+10000,
1297 (altcharset && *altcharset) ? altcharset
1298 : (charset && *charset)
1299 ? charset
1300 : ps_global->posting_charmap);
1301 tmp_20k_buf[SIZEOF_20KBUF-10000-1] = '\0';
1303 if(esclast && *esclast && escfirst && *escfirst){
1304 if(struct_name)
1305 fs_give((void **)&struct_name);
1307 struct_name = cpystr(encoded);
1309 else{
1310 /* in case we don't get a fullname better than this */
1311 if(!fullname)
1312 fullname = cpystr(encoded);
1315 if(esclast)
1316 fs_give((void **)&esclast);
1317 if(escfirst)
1318 fs_give((void **)&escfirst);
1319 if(escmiddle)
1320 fs_give((void **)&escmiddle);
1323 /* suggested nickname */
1324 else if(!strucmp(tag, "nickname") || !strucmp(tag, "x-nickname")){
1325 if(*value){
1326 /* Last one wins */
1327 if(nickname)
1328 fs_give((void **)&nickname);
1331 * Nickname can have multiple values. We're just
1332 * going to take the first. Skip escaped commas, though.
1334 p = value;
1335 while(p && (comma = strindex(p, a_comma)) != NULL){
1336 if(comma > value && comma[-1] != '\\'){
1337 *comma = '\0';
1338 break;
1340 else
1341 p = comma + 1;
1344 nickname = vcard_unescape(value);
1348 if(tag)
1349 fs_give((void **)&tag);
1351 if(altcharset)
1352 fs_give((void **)&altcharset);
1354 if(decoded)
1355 fs_give((void **)&decoded);
1357 if(escval)
1358 fs_give((void **)&escval);
1361 if(title){
1362 if(!comment)
1363 comment = title;
1364 else
1365 fs_give((void **)&title);
1368 if(fullname || struct_name || nickname || fcc
1369 || comment || (addrs && *addrs))
1370 selected += vcard_to_ta(addrs, fullname, struct_name, nickname,
1371 comment, fcc, ta_list);
1373 if(addrs)
1374 fs_give((void **)&addrs);
1376 free_list_array(&lines);
1379 return(selected);
1384 cmp_swoop_list(const qsort_t *a1, const qsort_t *a2)
1386 SWOOP_S *x = (SWOOP_S *)a1;
1387 SWOOP_S *y = (SWOOP_S *)a2;
1389 if(x->dup){
1390 if(y->dup)
1391 return((x->dst_enum > y->dst_enum) ? -1
1392 : (x->dst_enum == y->dst_enum) ? 0 : 1);
1393 else
1394 return(-1);
1396 else if(y->dup)
1397 return(1);
1398 else
1399 return((x > y) ? -1 : (x == y) ? 0 : 1); /* doesn't matter */
1405 * Take the split out contents of a vCard entry and turn them into a TA.
1407 * Always returns 1, the number of TAs added to ta_list.
1410 vcard_to_ta(char *addrs, char *fullname, char *better_fullname, char *nickname,
1411 char *comment, char *fcc, TA_S **ta_list)
1413 ADDRESS *addrlist = NULL;
1416 * Parse it into an addrlist, which is what fill_in_ta wants
1417 * to see.
1419 rfc822_parse_adrlist(&addrlist, addrs, fakedomain);
1420 if(!addrlist)
1421 addrlist = mail_newaddr(); /* empty addr, to make right thing happen */
1423 *ta_list = fill_in_ta(ta_list, addrlist, 1,
1424 fullname ? fullname
1425 : better_fullname ? better_fullname
1426 : "Forwarded Entry");
1427 (*ta_list)->nickname = nickname ? nickname : cpystr("");
1428 (*ta_list)->comment = comment;
1429 (*ta_list)->fcc = fcc;
1432 * We are tempted to want to call switch_to_last_comma_first() here when
1433 * we don't have a better_fullname (which is already last, first).
1434 * But, since this is the way it was sent to us we should probably leave
1435 * it alone. That means that if we use a fullname culled from the
1436 * body of a message, or from a header, or from an "N" vcard record,
1437 * we will make it be Last, First; but if we use one from an "FN" record
1438 * we won't.
1440 (*ta_list)->fullname = better_fullname ? better_fullname
1441 : fullname;
1443 if((*ta_list)->nickname)
1444 convert_possibly_encoded_str_to_utf8(&(*ta_list)->nickname);
1446 if((*ta_list)->comment)
1447 convert_possibly_encoded_str_to_utf8(&(*ta_list)->comment);
1449 if((*ta_list)->fcc)
1450 convert_possibly_encoded_str_to_utf8(&(*ta_list)->fcc);
1452 if((*ta_list)->fullname)
1453 convert_possibly_encoded_str_to_utf8(&(*ta_list)->fullname);
1456 * The TA list will free its members but we have to take care of this
1457 * extra one that isn't assigned to a TA member.
1459 if(better_fullname && fullname)
1460 fs_give((void **)&fullname);
1462 if(addrlist)
1463 mail_free_address(&addrlist);
1465 return(1);
1470 * Look through line which is supposed to look like
1472 * type;charset=iso-8859-1;encoding=b: value
1474 * Type might be email, or nickname, ... It is optional because of the
1475 * defaulttype parameter (there isn't such a thing as defaulttype parameters
1476 * as of Nov. 96 draft). The semicolon and colon are special chars. Each
1477 * parameter in this line is a semicolon followed by the parameter type "="
1478 * the parameter value. Parameters are optional, too. There is always a colon,
1479 * followed by value. Whitespace can be everywhere up to where value starts.
1480 * There is also an optional <group> "." preceding the type, which we will
1481 * ignore. It's for grouping related lines.
1482 * (As of July, 97 draft, it is no longer permissible to include a charset
1483 * parameter in each line. Only one is permitted in the content-type mime hdr.)
1484 * (Also as of July, 97 draft, all white space is supposed to be significant.
1485 * We will continue to skip white space surrounding '=' and so forth for
1486 * backwards compatibility and because it does no harm in almost all cases.)
1487 * (As of Nov, 97 draft, some white space is not significant. That is, it is
1488 * allowed in some places.)
1491 * Args: line -- the line to look at
1492 * type -- this is a return value, and is an allocated copy of
1493 * the type, freed by the caller. For example, "email".
1494 * alt -- this is a return value, and is an allocated copy of
1495 * the value of the alternate charset, to be freed by
1496 * the caller. For example, this might be "iso-8859-2".
1497 * encoded -- this is a return value, and is set to 1 if the value
1498 * is b-encoded
1500 * Return value: a pointer to the start of value, or NULL if the syntax
1501 * isn't right. It's possible for value to be equal to "".
1503 char *
1504 getaltcharset(char *line, char **type, char **alt, int *encoded)
1506 char *p, *q, *left_semi, *group_dot, *colon;
1507 char *start_of_enc, *start_of_cset, tmpsave;
1508 static char *cset = "charset";
1509 static char *enc = "encoding";
1511 if(type)
1512 *type = NULL;
1514 if(alt)
1515 *alt = NULL;
1517 if(encoded)
1518 *encoded = 0;
1520 colon = strindex(line, ':');
1521 if(!colon)
1522 return NULL;
1524 left_semi = strindex(line, ';');
1525 if(left_semi && left_semi > colon)
1526 left_semi = NULL;
1528 group_dot = strindex(line, '.');
1529 if(group_dot && (group_dot > colon || (left_semi && group_dot > left_semi)))
1530 group_dot = NULL;
1533 * Type is everything up to the semicolon, or the colon if no semicolon.
1534 * However, we want to skip optional <group> ".".
1536 if(type){
1537 q = left_semi ? left_semi : colon;
1538 tmpsave = *q;
1539 *q = '\0';
1540 *type = cpystr(group_dot ? group_dot+1 : line);
1541 *q = tmpsave;
1542 sqzspaces(*type);
1545 if(left_semi && alt
1546 && (p = srchstr(left_semi+1, cset))
1547 && p < colon){
1548 p += strlen(cset);
1549 p = skip_white_space(p);
1550 if(*p++ == '='){
1551 p = skip_white_space(p);
1552 if(p < colon){
1553 start_of_cset = p;
1554 p++;
1555 while(p < colon && !isspace((unsigned char)*p) && *p != ';')
1556 p++;
1558 tmpsave = *p;
1559 *p = '\0';
1560 *alt = cpystr(start_of_cset);
1561 *p = tmpsave;
1566 if(encoded && left_semi
1567 && (p = srchstr(left_semi+1, enc))
1568 && p < colon){
1569 p += strlen(enc);
1570 p = skip_white_space(p);
1571 if(*p++ == '='){
1572 p = skip_white_space(p);
1573 if(p < colon){
1574 start_of_enc = p;
1575 p++;
1576 while(p < colon && !isspace((unsigned char)*p) && *p != ';')
1577 p++;
1579 if(*start_of_enc == 'b' && start_of_enc + 1 == p)
1580 *encoded = 1;
1585 p = colon + 1;
1587 return(skip_white_space(p));
1592 * Return incoming_fullname in Last, First form.
1593 * The passed in fullname should already be in UTF-8.
1594 * If there is already a comma, we add quotes around the fullname so we can
1595 * tell it isn't a Last, First comma but a real comma in the Fullname.
1597 * Args: incoming_fullname --
1598 * new_full -- passed in pointer to place to put new fullname
1599 * nbuf -- size of new_full
1600 * Returns: new_full
1602 void
1603 switch_to_last_comma_first(char *incoming_fullname, char *new_full, size_t nbuf)
1605 char save_value;
1606 char *save_end, *nf, *inc, *p = NULL;
1608 if(nbuf < 1)
1609 return;
1611 if(incoming_fullname == NULL){
1612 new_full[0] = '\0';
1613 return;
1616 /* get rid of leading white space */
1617 for(inc = incoming_fullname; *inc && isspace((unsigned char)*inc); inc++)
1618 ;/* do nothing */
1620 /* get rid of trailing white space */
1621 for(p = inc + strlen(inc) - 1; *p && p >= inc &&
1622 isspace((unsigned char)*p); p--)
1623 ;/* do nothing */
1625 save_end = ++p;
1626 if(save_end == inc){
1627 new_full[0] = '\0';
1628 return;
1631 save_value = *save_end; /* so we don't alter incoming_fullname */
1632 *save_end = '\0';
1633 nf = new_full;
1634 memset(new_full, 0, nbuf); /* need this the way it is done below */
1636 if(strindex(inc, ',') != NULL){
1637 int add_quotes = 0;
1640 * Quote already existing comma.
1642 * We'll get this wrong if it is already quoted but the quote
1643 * doesn't start right at the beginning.
1645 if(inc[0] != '"'){
1646 add_quotes++;
1647 if(nf-new_full < nbuf-1)
1648 *nf++ = '"';
1651 strncpy(nf, inc, MIN(nbuf - (add_quotes ? 3 : 1), nbuf-(nf-new_full)-1));
1652 new_full[nbuf-1] = '\0';
1653 if(add_quotes){
1654 strncat(nf, "\"", nbuf-(nf-new_full)-1);
1655 new_full[nbuf-1] = '\0';
1658 else if(strindex(inc, SPACE)){
1659 char *last, *end;
1661 end = new_full + nbuf - 1;
1664 * Switch First Middle Last to Last, First Middle
1667 /* last points to last word, which does exist */
1668 last = skip_white_space(strrindex(inc, SPACE));
1670 /* copy Last */
1671 for(p = last; *p && nf < end-2 && nf-new_full < nbuf; *nf++ = *p++)
1672 ;/* do nothing */
1674 if(nf-new_full < nbuf-1)
1675 *nf++ = ',';
1677 if(nf-new_full < nbuf-1)
1678 *nf++ = SPACE;
1680 /* copy First Middle */
1681 for(p = inc; p < last && nf < end && nf-new_full < nbuf-1; *nf++ = *p++)
1682 ;/* do nothing */
1684 new_full[nbuf-1] = '\0';
1686 removing_trailing_white_space(new_full);
1688 else
1689 strncpy(new_full, inc, nbuf-1);
1691 new_full[nbuf-1] = '\0';
1693 *save_end = save_value;
1698 * Fetch this body part, content-decode it if necessary, split it into
1699 * lines, and return the lines in an allocated array, which is freed
1700 * by the caller. Folded lines are replaced by single long lines.
1702 char **
1703 detach_vcard_att(MAILSTREAM *stream, long int msgno, struct mail_bodystruct *body, char *partnum)
1705 unsigned long length;
1706 char *text = NULL, /* raw text */
1707 *dtext = NULL, /* content-decoded text */
1708 **res = NULL, /* array of decoded lines */
1709 *lptr, /* line pointer */
1710 *next_line;
1711 int i, count;
1713 /* make our own copy of text so we can change it */
1714 text = cpystr(mail_fetchbody_full(stream, msgno, partnum, &length,FT_PEEK));
1715 text[length] = '\0';
1717 /* decode the text */
1718 switch(body->encoding){
1719 default:
1720 case ENC7BIT:
1721 case ENC8BIT:
1722 case ENCBINARY:
1723 dtext = text;
1724 break;
1726 case ENCBASE64:
1727 dtext = (char *)rfc822_base64((unsigned char *)text,
1728 (unsigned long)strlen(text),
1729 &length);
1730 if(dtext)
1731 dtext[length] = '\0';
1732 else
1733 q_status_message(SM_ORDER | SM_DING, 3, 4,
1734 "Malformed B64 data in address book attachment.");
1736 if(text)
1737 fs_give((void **)&text);
1739 break;
1741 case ENCQUOTEDPRINTABLE:
1742 dtext = (char *)rfc822_qprint((unsigned char *)text,
1743 (unsigned long)strlen(text),
1744 &length);
1745 if(dtext)
1746 dtext[length] = '\0';
1747 else
1748 q_status_message(SM_ORDER | SM_DING, 3, 4,
1749 "Malformed QP data in address book attachment.");
1751 if(text)
1752 fs_give((void **)&text);
1754 break;
1757 /* count the lines */
1758 next_line = dtext;
1759 count = 0;
1760 for(lptr = next_line; lptr && *lptr; lptr = next_line){
1761 for(next_line = lptr; *next_line; next_line++){
1763 * Find end of current line.
1765 if(*next_line == '\r' && *(next_line+1) == '\n'){
1766 next_line += 2;
1767 /* not a folded line, count it */
1768 if(!*next_line || (*next_line != SPACE && *next_line != TAB))
1769 break;
1773 count++;
1776 /* allocate space for resulting array of lines */
1777 res = (char **)fs_get((count + 1) * sizeof(char *));
1778 memset((void *)res, 0, (count + 1) * sizeof(char *));
1779 next_line = dtext;
1780 for(i=0, lptr=next_line; lptr && *lptr && i < count; lptr=next_line, i++){
1782 * Move next_line to start of next line and null terminate
1783 * current line.
1785 for(next_line = lptr; *next_line; next_line++){
1787 * Find end of current line.
1789 if(*next_line == '\r' && *(next_line+1) == '\n'){
1790 next_line += 2;
1791 /* not a folded line, terminate it */
1792 if(!*next_line || (*next_line != SPACE && *next_line != TAB)){
1793 *(next_line-2) = '\0';
1794 break;
1799 /* turn folded lines into long lines in place */
1800 vcard_unfold(lptr);
1801 res[i] = cpystr(lptr);
1804 if(dtext)
1805 fs_give((void **)&dtext);
1807 res[count] = NULL;
1808 return(res);
1813 * Look for possible addresses in the first text part of a message for
1814 * use by TakeAddr command.
1815 * Returns the number of TA lines added.
1818 grab_addrs_from_body(MAILSTREAM *stream, long int msgno,
1819 struct mail_bodystruct *body, TA_S **ta_list)
1821 #define MAXLINESZ 2000
1822 char line[MAXLINESZ + 1];
1823 STORE_S *so;
1824 gf_io_t pc;
1825 SourceType src = CharStar;
1826 int added = 0, failure;
1827 char *partno = "1";
1828 ATTACH_S *a = ps_global->atmts;
1830 dprint((9, "\n - grab_addrs_from_body - \n"));
1833 * If it is text/plain or it is multipart with a first part of text/plain,
1834 * we want to continue, else forget it.
1836 if(!((body->type == TYPETEXT && body->subtype
1837 && ALLOWED_SUBTYPE(body->subtype))
1839 (body->type == TYPEMULTIPART && body->nested.part
1840 && body->nested.part->body.type == TYPETEXT
1841 && ALLOWED_SUBTYPE(body->nested.part->body.subtype))))
1842 return 0;
1844 #ifdef DOS
1845 src = TmpFileStar;
1846 #endif
1848 if((so = so_get(src, NULL, EDIT_ACCESS)) == NULL)
1849 return 0;
1851 gf_set_so_writec(&pc, so);
1853 if(body->type == TYPEMULTIPART
1854 && strucmp(body->subtype, "ALTERNATIVE") == 0
1855 && a && a+1 && (a+1)->shown)
1856 partno = "2";
1858 failure = !get_body_part_text(stream, body, msgno, partno, 0L, pc,
1859 NULL, NULL, GBPT_NONE);
1861 gf_clear_so_writec(so);
1863 if(failure){
1864 so_give(&so);
1865 return 0;
1868 so_seek(so, 0L, 0);
1870 while(get_line_of_message(so, line, sizeof(line))){
1871 ADDRESS *addr;
1872 char save_end, *start, *tmp_a_string, *tmp_personal, *p;
1873 int n;
1875 /* process each @ in the line */
1876 for(p = (char *) line; (start = mail_addr_scan(p, &n)); p = start + n){
1878 tmp_personal = NULL;
1880 if(start > line && *(start-1) == '<' && *(start+n) == '>'){
1881 int words, in_quote;
1882 char *fn_start;
1885 * Take a shot at looking for full name
1886 * If we can find a colon maybe we've got a header line
1887 * embedded in the body.
1888 * This is real ad hoc.
1892 * Go back until we run into a character that probably
1893 * isn't a fullname character.
1895 fn_start = start-1;
1896 in_quote = words = 0;
1897 while(fn_start > p && (in_quote || !(*(fn_start-1) == ':'
1898 || *(fn_start-1) == ';' || *(fn_start-1) == ','))){
1899 fn_start--;
1900 if(!in_quote && isspace((unsigned char)*fn_start))
1901 words++;
1902 else if(*fn_start == '"' &&
1903 (fn_start == p || *(fn_start-1) != '\\')){
1904 if(in_quote){
1905 in_quote = 0;
1906 break;
1908 else
1909 in_quote = 1;
1912 if(words == 5)
1913 break;
1916 /* wasn't a real quote, forget about fullname */
1917 if(in_quote)
1918 fn_start = start-1;
1920 /* Skip forward over the white space. */
1921 while(isspace((unsigned char)*fn_start) || *fn_start == '(')
1922 fn_start++;
1925 * Make sure the first word is capitalized.
1926 * (just so it is more likely it is a name)
1928 while(fn_start < start-1 &&
1929 !(isupper(*fn_start) || *fn_start == '"')){
1930 if(*fn_start == '('){
1931 fn_start++;
1932 continue;
1935 /* skip forward over this word */
1936 while(fn_start < start-1 &&
1937 !isspace((unsigned char)*fn_start))
1938 fn_start++;
1940 while(fn_start < start-1 &&
1941 isspace((unsigned char)*fn_start))
1942 fn_start++;
1945 if(fn_start < start-1){
1946 char *fn_end, save_fn_end;
1948 /* remove white space between fullname and start */
1949 fn_end = start-1;
1950 while(fn_end > fn_start
1951 && isspace((unsigned char)*(fn_end - 1)))
1952 fn_end--;
1954 save_fn_end = *fn_end;
1955 *fn_end = '\0';
1957 /* remove matching quotes */
1958 if(*fn_start == '"' && *(fn_end-1) == '"'){
1959 fn_start++;
1960 *fn_end = save_fn_end;
1961 fn_end--;
1962 save_fn_end = *fn_end;
1963 *fn_end = '\0';
1966 /* copy fullname */
1967 if(*fn_start)
1968 tmp_personal = cpystr(fn_start);
1970 *fn_end = save_fn_end;
1975 save_end = *(start+n);
1976 *(start+n) = '\0';
1977 /* rfc822_parse_adrlist feels free to destroy input so send copy */
1978 tmp_a_string = cpystr(start);
1979 *(start+n) = save_end;
1980 addr = NULL;
1981 ps_global->c_client_error[0] = '\0';
1982 rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
1983 if(tmp_a_string)
1984 fs_give((void **)&tmp_a_string);
1986 if(tmp_personal){
1987 if(addr){
1988 if(addr->personal)
1989 fs_give((void **)&addr->personal);
1991 addr->personal = tmp_personal;
1993 else
1994 fs_give((void **)&tmp_personal);
1997 if((addr && addr->host && addr->host[0] == '@') ||
1998 ps_global->c_client_error[0]){
1999 mail_free_address(&addr);
2000 continue;
2003 if(addr && addr->mailbox && addr->host){
2004 added++;
2005 *ta_list = fill_in_ta(ta_list, addr, 0, (char *)NULL);
2008 if(addr)
2009 mail_free_address(&addr);
2013 so_give(&so);
2014 return(added);
2019 * Get the next line of the object pointed to by source.
2020 * Skips empty lines.
2021 * Linebuf is a passed in buffer to put the line in.
2022 * Linebuf is null terminated and \r and \n chars are removed.
2023 * 0 is returned when there is no more input.
2026 get_line_of_message(STORE_S *source, char *linebuf, int linebuflen)
2028 int pos = 0;
2029 unsigned char c;
2031 if(linebuflen < 2)
2032 return 0;
2034 while(so_readc(&c, source)){
2035 if(c == '\n' || c == '\r'){
2036 if(pos > 0)
2037 break;
2039 else{
2040 linebuf[pos++] = c;
2041 if(pos >= linebuflen - 2)
2042 break;
2046 linebuf[pos] = '\0';
2048 return(pos);
2053 * Inserts a new entry based on addr in the TA list.
2054 * Makes sure everything is UTF-8. That is,
2055 * Values used for strvalue will be converted to UTF-8 first.
2056 * Personal name fields of addr args will be converted to UTF-8.
2058 * Args: old_current -- the TA list
2059 * addr -- the address for this line
2060 * checked -- start this line out checked
2061 * print_string -- if non-null, this line is informational and is just
2062 * printed out, it can't be selected
2064 TA_S *
2065 fill_in_ta(TA_S **old_current, struct mail_address *addr, int checked, char *print_string)
2067 TA_S *new_current;
2069 /* c-client convention for group syntax, which we don't want to deal with */
2070 if(!print_string && (!addr || !addr->mailbox || !addr->host))
2071 new_current = *old_current;
2072 else{
2074 new_current = new_taline(old_current);
2075 if(print_string && addr){ /* implied List when here */
2076 new_current->frwrded = 1;
2077 new_current->skip_it = 0;
2078 new_current->print = 0;
2079 new_current->checked = checked != 0;
2080 new_current->addr = copyaddrlist(addr);
2081 decode_addr_names_to_utf8(new_current->addr);
2082 new_current->strvalue = cpystr(print_string);
2083 convert_possibly_encoded_str_to_utf8(&new_current->strvalue);
2085 else if(print_string){
2086 new_current->frwrded = 0;
2087 new_current->skip_it = 1;
2088 new_current->print = 1;
2089 new_current->checked = 0;
2090 new_current->addr = 0;
2091 new_current->strvalue = cpystr(print_string);
2092 convert_possibly_encoded_str_to_utf8(&new_current->strvalue);
2094 else{
2095 new_current->frwrded = 0;
2096 new_current->skip_it = 0;
2097 new_current->print = 0;
2098 new_current->checked = checked != 0;
2099 new_current->addr = copyaddr(addr);
2100 decode_addr_names_to_utf8(new_current->addr);
2101 if(addr->host[0] == '.')
2102 new_current->strvalue = cpystr("Error in address (ok to try Take anyway)");
2103 else{
2104 char *bufp;
2105 size_t len;
2107 len = est_size(new_current->addr);
2108 bufp = (char *) fs_get(len * sizeof(char));
2109 new_current->strvalue = cpystr(addr_string(new_current->addr, bufp, len));
2110 fs_give((void **) &bufp);
2113 convert_possibly_encoded_str_to_utf8(&new_current->strvalue);
2117 return(new_current);
2122 eliminate_dups_and_us(TA_S *list)
2124 return(eliminate_dups_and_maybe_us(list, 1));
2129 eliminate_dups_but_not_us(TA_S *list)
2131 return(eliminate_dups_and_maybe_us(list, 0));
2136 * Look for dups in list and mark them so we'll skip them.
2138 * We also throw out any addresses that are us (if us_too is set), since
2139 * we won't usually want to take ourselves to the addrbook.
2140 * On the otherhand, if there is nothing but us, we leave it.
2142 * Don't toss out forwarded entries.
2144 * Returns the number of dups eliminated that were also selected.
2147 eliminate_dups_and_maybe_us(TA_S *list, int us_too)
2149 ADDRESS *a, *b;
2150 TA_S *ta, *tb;
2151 int eliminated = 0;
2153 /* toss dupes */
2154 for(ta = list; ta; ta = ta->next){
2156 if(ta->skip_it || ta->frwrded) /* already tossed or forwarded */
2157 continue;
2159 a = ta->addr;
2161 /* Check addr "a" versus tail of the list */
2162 for(tb = ta->next; tb; tb = tb->next){
2163 if(tb->skip_it || tb->frwrded) /* already tossed or forwarded */
2164 continue;
2166 b = tb->addr;
2167 if(dup_addrs(a, b)){
2168 if(ta->checked || !(tb->checked)){
2169 /* throw out b */
2170 if(tb->checked)
2171 eliminated++;
2173 tb->skip_it = 1;
2175 else{ /* tb->checked */
2176 /* throw out a */
2177 ta->skip_it = 1;
2178 break;
2184 if(us_too){
2185 /* check whether all remaining addrs are us */
2186 for(ta = list; ta; ta = ta->next){
2188 if(ta->skip_it) /* already tossed */
2189 continue;
2191 if(ta->frwrded) /* forwarded entry, so not us */
2192 break;
2194 a = ta->addr;
2196 if(!address_is_us(a, ps_global))
2197 break;
2201 * if at least one address that isn't us, remove all of us from
2202 * the list
2204 if(ta){
2205 for(ta = list; ta; ta = ta->next){
2207 if(ta->skip_it || ta->frwrded) /* already tossed or forwarded */
2208 continue;
2210 a = ta->addr;
2212 if(address_is_us(a, ps_global)){
2213 if(ta->checked)
2214 eliminated++;
2216 /* throw out a */
2217 ta->skip_it = 1;
2223 return(eliminated);
2228 * Returns 1 if x and y match, 0 if not.
2231 dup_addrs(struct mail_address *x, struct mail_address *y)
2233 return(x && y && strucmp(x->mailbox, y->mailbox) == 0
2234 && strucmp(x->host, y->host) == 0);