#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: addrstring.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $"; #endif /* * ======================================================================== * Copyright 2006-2007 University of Washington * Copyright 2013-2021 Eduardo Chappa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * ======================================================================== */ #include "../pith/headers.h" #include "../pith/addrstring.h" #include "../pith/state.h" #include "../pith/copyaddr.h" #include "../pith/charset.h" #include "../pith/stream.h" /* * Internal prototypes */ void rfc822_write_address_decode(char *, size_t, ADDRESS *, int); /* * Format an address structure into a string * * Args: addr -- Single ADDRESS structure to turn into a string * * Result: Fills in buf and returns pointer to it. * Just uses the c-client call to do this. * (the address is not rfc1522 decoded) */ char * addr_string(struct mail_address *addr, char *buf, size_t buflen) { ADDRESS *next_addr; RFC822BUFFER rbuf; *buf = '\0'; next_addr = addr->next; addr->next = NULL; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = buf; rbuf.cur = buf; rbuf.end = buf+buflen-1; rfc822_output_address_list(&rbuf, addr, 0L, NULL); *rbuf.cur = '\0'; addr->next = next_addr; return(buf); } /* * Same as addr_string only it doesn't have to be a * single address. */ char * addr_string_mult(struct mail_address *addr, char *buf, size_t buflen) { RFC822BUFFER rbuf; *buf = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = buf; rbuf.cur = buf; rbuf.end = buf+buflen-1; rfc822_output_address_list(&rbuf, addr, 0L, NULL); *rbuf.cur = '\0'; return(buf); } /* * Format an address structure into a simple string: "mailbox@host" * * Args: addr -- Single ADDRESS structure to turn into a string * buf -- buffer to write address in; * * Result: Returns pointer to buf; */ char * simple_addr_string(struct mail_address *addr, char *buf, size_t buflen) { RFC822BUFFER rbuf; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = buf; rbuf.cur = buf; rbuf.end = buf+buflen-1; rfc822_output_address(&rbuf, addr); *rbuf.cur = '\0'; return(buf); } /* * Format an address structure into a simple string: "mailbox@host" * Like simple_addr_string but can be multiple addresses. * * Args: addr -- ADDRESS structure to turn into a string * buf -- buffer to write address in; * buflen -- length of buffer * sep -- separator string * * Result: Returns pointer to internal static formatted string. * Just uses the c-client call to do this. */ char * simple_mult_addr_string(struct mail_address *addr, char *buf, size_t buflen, char *sep) { ADDRESS *a; char *dest = buf; size_t seplen = 0; if(sep) seplen = strlen(sep); *dest = '\0'; for(a = addr; a; a = a->next){ if(dest > buf && seplen > 0 && buflen-1-(dest-buf) >= seplen){ strncpy(dest, sep, seplen); dest += seplen; *dest = '\0'; } simple_addr_string(a, dest, buflen-(dest-buf)); dest += strlen(dest); } buf[buflen-1] = '\0'; return(buf); } /* * 1522 encode the personal name portion of addr and return an allocated * copy of the resulting address string. */ char * encode_fullname_of_addrstring(char *addr, char *charset) { char *pers_encoded, *tmp_a_string, *ret = NULL; ADDRESS *adr; static char *fakedomain = "@"; RFC822BUFFER rbuf; size_t len; tmp_a_string = cpystr(addr ? addr : ""); adr = NULL; rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain); fs_give((void **)&tmp_a_string); if(!adr) return(cpystr("")); if(adr->personal && adr->personal[0]){ pers_encoded = cpystr(rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF, (unsigned char *)adr->personal, charset)); fs_give((void **)&adr->personal); adr->personal = pers_encoded; } len = est_size(adr); ret = (char *) fs_get(len * sizeof(char)); ret[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = ret; rbuf.cur = ret; rbuf.end = ret+len-1; rfc822_output_address_list(&rbuf, adr, 0L, NULL); *rbuf.cur = '\0'; mail_free_address(&adr); return(ret); } /* * 1522 decode the personal name portion of addr and return an allocated * copy of the resulting address string. */ char * decode_fullname_of_addrstring(char *addr, int verbose) { char *pers_decoded, *tmp_a_string, *ret = NULL; ADDRESS *adr; static char *fakedomain = "@"; RFC822BUFFER rbuf; size_t len; tmp_a_string = cpystr(addr ? addr : ""); adr = NULL; rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain); fs_give((void **)&tmp_a_string); if(!adr) return(cpystr("")); if(adr->personal && adr->personal[0]){ pers_decoded = cpystr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, adr->personal)); fs_give((void **)&adr->personal); adr->personal = pers_decoded; } len = est_size(adr); ret = (char *) fs_get(len * sizeof(char)); ret[0] = '\0'; rbuf.f = dummy_soutr; rbuf.s = NULL; rbuf.beg = ret; rbuf.cur = ret; rbuf.end = ret+len-1; rfc822_output_address_list(&rbuf, adr, 0L, NULL); *rbuf.cur = '\0'; mail_free_address(&adr); return(ret); } /* * Turn a list of address structures into a formatted string * * Args: adrlist -- An adrlist * f -- Function to use to print one address in list. If NULL, * use rfc822_write_address_decode to print whole list. * do_quote -- Quote quotes and dots (only used if f == NULL). * Result: comma separated list of addresses which is * malloced here and returned * (the list is rfc1522 decoded unless f is *not* NULL) */ char * addr_list_string(struct mail_address *adrlist, char *(*f)(struct mail_address *, char *, size_t), int do_quote) { size_t len; char *list, *s, string[MAX_ADDR_EXPN+1]; register ADDRESS *a; if(!adrlist) return(cpystr("")); if(f){ len = 0; for(a = adrlist; a; a = a->next) len += (strlen((*f)(a, string, sizeof(string))) + 2); list = (char *) fs_get((len+1) * sizeof(char)); s = list; s[0] = '\0'; for(a = adrlist; a; a = a->next){ sstrncpy(&s, (*f)(a, string, sizeof(string)), len-(s-list)); if(a->next && len-(s-list) > 2){ *s++ = ','; *s++ = SPACE; } } } else{ len = est_size(adrlist); list = (char *) fs_get((len+1) * sizeof(char)); list[0] = '\0'; rfc822_write_address_decode(list, len+1, adrlist, do_quote); removing_leading_and_trailing_white_space(list); } list[len] = '\0'; return(list); } static long rfc822_dummy_soutr (void *stream, char *string) { return LONGT; } /* * Copied from c-client/rfc822.c buf with dot and double quote removed. */ static const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177"; /* Write RFC822 address with 1522 decoding of personal name * and optional quoting. * * The idea is that there are some places where we'd just like to display * the personal name as is before applying confusing quoting. However, * we do want to be careful not to break things that should be quoted so * we'll only use this where we are sure. Quoting may look ugly but it * doesn't usually break anything. */ void rfc822_write_address_decode(char *dest, size_t destlen, struct mail_address *adr, int do_quote) { RFC822BUFFER buf; extern const char *rspecials; ADDRESS *copy, *a; /* * We want to print the adr list after decoding it. C-client knows * how to parse and print, so we want to use that. But c-client * doesn't decode. So we make a copy of the address list, decode * things there, and let c-client print that. */ copy = copyaddrlist(adr); for(a = copy; a; a = a->next){ if(a->host){ /* ordinary address? */ if(a->personal && *a->personal){ unsigned char *p; p = rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, a->personal); if(p && (char *) p != a->personal){ fs_give((void **) &a->personal); a->personal = cpystr((char *) p); } } } else if(a->mailbox && *a->mailbox){ /* start of group? */ unsigned char *p; p = rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, a->mailbox); if(p && (char *) p != a->mailbox){ fs_give((void **) &a->mailbox); a->mailbox = cpystr((char *) p); } } } buf.end = (buf.beg = buf.cur = dest) + destlen; buf.f = rfc822_dummy_soutr; *buf.cur = '\0'; buf.s = NIL; (void) rfc822_output_address_list(&buf, copy, 0, do_quote ? rspecials : rspecials_minus_quote_and_dot); *buf.cur = '\0'; if(copy) mail_free_address(©); } /* * Compute an upper bound on the size of the array required by * rfc822_write_address for this list of addresses. * * Args: adrlist -- The address list. * * Returns -- an integer giving the upper bound */ int est_size(struct mail_address *a) { int cnt = 0; for(; a; a = a->next){ /* two times personal for possible quoting */ cnt += 2 * (a->personal ? (strlen(a->personal)+1) : 0); cnt += 2 * (a->mailbox ? (strlen(a->mailbox)+1) : 0); cnt += (a->adl ? strlen(a->adl) : 0); cnt += (a->host ? strlen(a->host) : 0); /* * add room for: * possible single space between fullname and addr * left and right brackets * @ sign * possible : for route addr * , * * So I really think that adding 7 is enough. Instead, I'll add 10. */ cnt += 10; } return(MAX(cnt, 50)); /* just making sure */ } /* * Returns the number of addresses in the list. */ int count_addrs(struct mail_address *adrlist) { int cnt = 0; while(adrlist){ if(adrlist->mailbox && adrlist->mailbox[0]) cnt++; adrlist = adrlist->next; } return(cnt); } /* * Buf is at least size maxlen+1 */ void a_little_addr_string(struct mail_address *addr, char *buf, size_t maxlen) { buf[0] = '\0'; if(addr){ if(addr->personal && addr->personal[0]){ char tmp[MAILTMPLEN]; snprintf(tmp, sizeof(tmp), "%s", addr->personal); iutf8ncpy(buf, (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp), maxlen); } else if(addr->mailbox && addr->mailbox[0]){ strncpy(buf, addr->mailbox, maxlen); buf[maxlen] = '\0'; if(addr->host && addr->host[0] && addr->host[0] != '.'){ strncat(buf, "@", maxlen+1-1-strlen(buf)); strncat(buf, addr->host, maxlen+1-1-strlen(buf)); } } } buf[maxlen] = '\0'; }