From eb4489dae948c83dbbea0cf4f584ab25b5417b86 Mon Sep 17 00:00:00 2001 From: "Steffen \"Daode\" Nurpmeso" Date: Tue, 9 Oct 2012 14:04:57 +0200 Subject: [PATCH] {Unbreaks} Change how address checking works.. Do one small step towards a more sophisticated address handling, and introduce enum nameflags, struct addrguts, addrspec_with_guts(), rename mime_name_invalid() to is_addr_invalid(), is_fileaddr() to is_fileorpipe_addr(), reorder where is_addr_invalid() and checkaddrs() are defined (head.c and names.c, respectively), and little things. It turned out that nail(1) is most terrible inefficient when dealing with addresses and that yet no mechanism was available to implement IDNA support, because the IDNA convertion has to be applied to the domain part of either of the plain addr-spec and the full address as is seen, e.g., in a To: line. Since no information is carried through all those layers but the plain name there was no possibility to perform the necessary steps in deeper layers. So the only chance to really implement this is at creation time of struct name objects. And then there is that terrific checking of address content from multiple layers, again, after any possible information has been thrown away. So add an address checking at creation time of struct name objects. It must be understood that this is work in progress. This is a complicated codebase that is full of bugs, where fiddling around with one screw may badly effect others, as has been seen already in the last weeks. On the long run i hope to be able to turn more and more places over to work with objects that have all necessary information available all the time, and managed to perform the parsing and gathering of information once, and once only. Until then legacy code is needed. Anyway. This first step will make it possible to implement IDNA support on the encoding side, once upon struct name creation time, and for the addr-spec and the entire address in one go. It may be that decoding IDNA will never be implemented. --- def.h | 38 ++++++++-- extern.h | 10 ++- head.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------- mime.c | 119 +++-------------------------- names.c | 124 +++++++++++++++++------------- sendout.c | 14 ++-- tty.c | 2 +- 7 files changed, 350 insertions(+), 213 deletions(-) diff --git a/def.h b/def.h index e1359a979..bfdb228bc 100644 --- a/def.h +++ b/def.h @@ -442,21 +442,47 @@ struct header { */ enum nameflags { - NAME_MIME_CHECKED = 1<<0, /* mime_name_invalid() yet checked.. */ - NAME_MIME_INVALID = 1<<1, /* ..and it was invalid */ - NAME_IDNA_REQUIRED = 1<<2, /* IDNA convertion desired.. */ - NAME_IDNA_APPLIED = 1<<3 /* ..and is applied to name.n_name */ -}; + NAME_NAME_SALLOC = 1<<0, /* .n_name is doped */ + NAME_FULLNAME_SALLOC = 1<<1, /* .n_fullname is doped */ + NAME_SKINNED = 1<<2, /* Is actually skin()ned */ + NAME_ADDRSPEC_CHECKED = 1<<3, + NAME_ADDRSPEC_ISFILE = 1<<4, /* is a file path */ + NAME_ADDRSPEC_ISPIPE = 1<<5, /* is a command for pipeing */ + NAME_ADDRSPEC_ISFILEORPIPE = NAME_ADDRSPEC_ISFILE | + NAME_ADDRSPEC_ISPIPE, + NAME_ADDRSPEC_INVALID = 1<<6, /* An invalid addr-spec */ + NAME_ADDRSPEC_ERR_EMPTY = 1<<7, /* An empty string (or NULL) */ + NAME_ADDRSPEC_ERR_ATSEQ = 1<<8, /* Weird @ sequence */ + /* More on _ERR_ below */ + NAME_IDNA = 1<<9, /* IDNA convertion needed/applied */ + /* Bit range for storing a faulty character */ + _NAME_ADDRSPEC_ERR_MASKC = 0xFF << 16 +}; + +#define NAME_ADDRSPEC_ERR_GETC(F) (((F) & 0x00FF0000) >> 16) +#define NAME_ADDRSPEC_ERR_SETC(C) (((unsigned char)(C) & 0xFF) << 16) struct name { struct name *n_flink; /* Forward link in list. */ struct name *n_blink; /* Backward list link */ enum gfield n_type; /* From which list it came */ - int n_flags; /* enum nameflags */ + enum nameflags n_flags; /* enum nameflags */ char *n_name; /* This fella's name */ char *n_fullname; /* Sometimes, name including comment */ }; +struct addrguts { + const char *ag_input; /* Input string as given */ + size_t ag_ilen; /* strlen() of input */ + size_t ag_iaddr_start; /* Start of address in .ag_input, */ + size_t ag_iaddr_end; /* its end (only if ! _FILEADDR) */ + char *ag_skinned; /* Output (alloced if !=.ag_input) */ + size_t ag_slen; /* strlen() of .ag_skinned */ + size_t ag_sdom_start; /* Start of domain in .ag_skinned, */ + size_t ag_sdom_end; /* its end */ + enum nameflags ag_n_flags; /* enum nameflags of .ag_skinned */ +}; + /* * Structure of a MIME attachment. */ diff --git a/extern.h b/extern.h index a3ecb85a8..4745e08cf 100644 --- a/extern.h +++ b/extern.h @@ -284,8 +284,12 @@ char *thisfield(const char *linebuf, const char *field); char *nameof(struct message *mp, int reptype); char *skip_comment(const char *cp); char *routeaddr(const char *name); -char *skin(char *name); +#define is_fileorpipe_addr(NP) \ + (((NP)->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) != 0) +int is_addr_invalid(struct name *np, int putmsg); char *skinned_name(struct name *np); +char *skin(char *name); +int addrspec_with_guts(int doskin, char *name, struct addrguts *agp); char *realname(char *name); char *name1(struct message *mp, int reptype); int msgidcmp(const char *s1, const char *s2); @@ -392,8 +396,6 @@ enum okay maildir_remove(const char *name); int main(int argc, char *argv[]); /* mime.c */ -int mime_name_invalid(struct name *np, int putmsg); -struct name *checkaddrs(struct name *np); char *gettcharset(void); char *need_hdrconv(struct header *hp, enum gfield w); #ifdef HAVE_ICONV @@ -423,8 +425,8 @@ struct name *extract(char *line, enum gfield ntype); struct name *sextract(char *line, enum gfield ntype); struct name *lextract(char *line, enum gfield ntype); char *detract(struct name *np, enum gfield ntype); +struct name *checkaddrs(struct name *np); struct name *outof(struct name *names, FILE *fo, struct header *hp); -int is_fileaddr(char *name); struct name *usermap(struct name *names); struct name *cat(struct name *n1, struct name *n2); char **unpack(struct name *np); diff --git a/head.c b/head.c index 499f6a9c9..4d9077eb2 100644 --- a/head.c +++ b/head.c @@ -47,12 +47,13 @@ * Routines for processing and detecting headlines. */ -static char *copyin(char *src, char **space); -static char *nextword(char *wp, char *wbuf); -static int gethfield(FILE *f, char **linebuf, size_t *linesize, int rem, - char **colon); -static int msgidnextc(const char **cp, int *status); -static int charcount(char *str, int c); +static char * copyin(char *src, char **space); +static char * nextword(char *wp, char *wbuf); +static int gethfield(FILE *f, char **linebuf, size_t *linesize, int rem, + char **colon); +static int addrspec_check(int doskin, struct addrguts *agp); +static int msgidnextc(const char **cp, int *status); +static int charcount(char *str, int c); /* * See if the passed line buffer is a mail header. @@ -277,7 +278,7 @@ nextword(char *wp, char *wbuf) } void -extract_header(FILE *fp, struct header *hp) +extract_header(FILE *fp, struct header *hp) /* XXX no header occur-cnt check */ { char *linebuf = NULL; size_t linesize = 0; @@ -584,13 +585,48 @@ routeaddr(const char *name) } /* + * Check if a name's address part contains invalid characters. + */ +int +is_addr_invalid(struct name *np, int putmsg) +{ + char *name = np->n_name; + int f = np->n_flags; + + if ((f & NAME_ADDRSPEC_INVALID) == 0 || ! putmsg || + (f & NAME_ADDRSPEC_ERR_EMPTY) != 0) + ; + else if (f & NAME_ADDRSPEC_ERR_ATSEQ) + fprintf(stderr, tr(142, "%s contains invalid @@ sequence\n"), + name); + else { + char ce[sizeof(1ul)]; + unsigned char c = NAME_ADDRSPEC_ERR_GETC(f); + + if ((unsigned char)c >= 040 && (unsigned char)c <= 0177) + ce[0] = c, ce[1] = '\0'; + else + snprintf(ce, sizeof(ce), "\\%03o", (unsigned int)c); + fprintf(stderr, tr(143, + "%s contains invalid character '%s'\n"), + name, ce); + } + return ((f & NAME_ADDRSPEC_INVALID) != 0); +} + +/* * Returned the skinned n_name, use the cached value if available. * Note well that it may *not* create a duplicate. */ char * -skinned_name(struct name *np) +skinned_name(struct name *np) /* TODO !HAVE_ASSERTS legacy */ { +#ifdef HAVE_ASSERTS + assert(np->n_flags & NAME_SKINNED); + return (np->n_name); +#else return ((np->n_flags & NAME_SKINNED) ? np->n_name : skin(np->n_name)); +#endif } /* @@ -600,22 +636,169 @@ skinned_name(struct name *np) char * skin(char *name) { - int c; - char *cp, *cp2; - char *bufend; - int gotlt, lastsp; - char *nbuf; + struct addrguts ag; if (name == NULL) - return(NULL); - if (strchr(name, '(') == NULL && strchr(name, '<') == NULL - && strchr(name, ' ') == NULL) - return(name); - gotlt = 0; - lastsp = 0; - nbuf = ac_alloc(strlen(name) + 1); - bufend = nbuf; - for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) { + return (NULL); + + (void)addrspec_with_guts(1, name, &ag); + name = ag.ag_skinned; + if ((ag.ag_n_flags & NAME_NAME_SALLOC) == 0) + name = savestrbuf(name, ag.ag_slen); + return (name); +} + +/* + * Classify and check a (possibly skinned) header body according to RFC + * *addr-spec* rules; if it (is assumed to has been) skinned it may however be + * also a file or a pipe command, so check that first, then. + * Otherwise perform content checking and isolate the domain part (for IDNA). + */ +static int +addrspec_check(int skinned, struct addrguts *agp) +{ + char *addr, *p, in_quote, in_domain, hadat; + union {char c; unsigned char u;} c; + + agp->ag_n_flags |= NAME_ADDRSPEC_CHECKED; + addr = agp->ag_skinned; + + if (agp->ag_iaddr_end <= agp->ag_iaddr_start) { + agp->ag_n_flags |= NAME_ADDRSPEC_INVALID | + NAME_ADDRSPEC_ERR_EMPTY; + goto jleave; + } + + /* If the field is not a recipient, it cannot be a file or a pipe */ + if (! skinned) /* XXX || (gfield & (GTO | GCC | GBCC)) == 0) */ + goto jaddr_check; + + /* + * Excerpt from nail.1: + * + * Recipient address specifications + * The rules are: Any name which starts with a `|' character specifies + * a pipe, the command string following the `|' is executed and + * the message is sent to its standard input; any other name which + * contains a `@' character is treated as a mail address; any other + * name which starts with a `+' character specifies a folder name; any + * other name which contains a `/' character but no `!' or `%' + * character before also specifies a folder name; what remains is + * treated as a mail address. + */ + if (*addr == '|') { + agp->ag_n_flags |= NAME_ADDRSPEC_ISPIPE; + goto jleave; + } + if (memchr(addr, '@', agp->ag_slen) == NULL) { + if (*addr == '+') + goto jisfile; + for (p = addr; (c.c = *p); ++p) { + if (c.c == '!' || c.c == '%') + break; + if (c.c == '/') { +jisfile: agp->ag_n_flags |= NAME_ADDRSPEC_ISFILE; + goto jleave; + } + } + } + +jaddr_check: + in_quote = in_domain = hadat = 0; + + for (p = addr; (c.c = *p++) != '\0';) { + if (c.c == '\"') { + in_quote = ! in_quote; + } else if (c.u < 040 || c.u >= 0177) { /*FIXME IDNA!!in_domin */ + /* + if (in_domain) + agp->ag_n_flags |= NAME_ADDRSPEC_IDNA; + else*/ + break; + } else if (in_domain == 2) { + if ((c.c == ']' && *p != '\0') || c.c == '\\' || + whitechar(c.c)) + break; + } else if (in_quote && in_domain == 0) { + /*EMPTY*/; + } else if (c.c == '\\' && *p != '\0') { + ++p; + } else if (c.c == '@') { + if (hadat++) { + agp->ag_n_flags |= NAME_ADDRSPEC_INVALID | + NAME_ADDRSPEC_ERR_ATSEQ | + NAME_ADDRSPEC_ERR_SETC('@'); + goto jleave; + } + agp->ag_sdom_start = (size_t)(p - addr); + in_domain = (*p == '[') ? 2 : 1; + continue; + } else if (c.c == '(' || c.c == ')' || + c.c == '<' || c.c == '>' || + c.c == ',' || c.c == ';' || c.c == ':' || + c.c == '\\' || c.c == '[' || c.c == ']') + break; + hadat = 0; + } + + if (c.c == '\0') { + agp->ag_sdom_end = (size_t)(--p - addr); + } else + agp->ag_n_flags |= NAME_ADDRSPEC_INVALID | + NAME_ADDRSPEC_ERR_SETC(c.c); +jleave: + return ((agp->ag_n_flags & NAME_ADDRSPEC_INVALID) != 0); +} + +/* + * TODO addrspec_with_guts(!DOSKIN): 'want to release v13, but the code is evil + * TODO in that {,GSKIN,GFULL} are not really enough to handle all names. + * TODO We will have to classify *exactly* those fields we really care about, + * TODO and simply perform high-bit-set checking only (?) for all the others. + * TODO For those we do care for, provide special parsers that classify and + * TODO extract the stuff *exactly* (after a short glance i think NetBSD mailx + * TODO does this). And *do* see namecache and header object TODO notes. + */ +/* + * Skin *name* and extract the *addr-spec* according to RFC 5322. TODO 822:5322 + * Store the result in .ag_skinned and also fill in those .ag_ fields that have + * actually been seen. + * Return 0 if something good has been parsed, 1 if fun didn't exactly know how + * to deal with the input, or if that was plain invalid. + */ +int +addrspec_with_guts(int doskin, char *name, struct addrguts *agp) +{ + char *cp, *cp2, *bufend, *nbuf, c; + int gotlt, lastsp; + + memset(agp, 0, sizeof *agp); + + if ((agp->ag_input = name) == NULL || /* XXX ever? */ + (agp->ag_ilen = strlen(name)) == 0) { + agp->ag_n_flags |= NAME_ADDRSPEC_CHECKED | + NAME_ADDRSPEC_INVALID | NAME_ADDRSPEC_ERR_EMPTY; + return (1); + } + + if (! doskin || (memchr(name, '(', agp->ag_ilen) == NULL && + memchr(name, '<', agp->ag_ilen) == NULL && + memchr(name, ' ', agp->ag_ilen) == NULL)) { + /*agp->ag_iaddr_start = 0;*/ + agp->ag_iaddr_end = agp->ag_ilen - 1; + agp->ag_skinned = name; + agp->ag_slen = agp->ag_ilen; + agp->ag_n_flags = NAME_SKINNED; + return (addrspec_check(doskin, agp)); + } + + /* Something makes us think we have to perform the skin operation */ + nbuf = ac_alloc(agp->ag_ilen + 1); + /*agp->ag_iaddr_start = 0;*/ + cp2 = bufend = nbuf; + gotlt = lastsp = 0; + + for (cp = name; (c = *cp++) != '\0'; ) { switch (c) { case '(': cp = skip_comment(cp); @@ -626,6 +809,10 @@ skin(char *name) /* * Start of a "quoted-string". * Copy it in its entirety. + * XXX RFC: quotes are "semantically invisible" + * XXX But it was explicitly added (Changelog.Heirloom, + * XXX [9.23] released 11/15/00, "Do not remove quotes + * XXX when skinning names"? No more info.. */ *cp2++ = c; while ((c = *cp) != '\0') { @@ -650,17 +837,12 @@ skin(char *name) else if (cp[0] == '@' && cp[1] == ' ') cp += 2, *cp2++ = '@'; -#if 0 - /* - * RFC 822 specifies spaces are STRIPPED when - * in an adress specifier. - */ else lastsp = 1; -#endif break; case '<': + agp->ag_iaddr_start = (size_t)(cp - name); cp2 = bufend; gotlt++; lastsp = 0; @@ -668,6 +850,8 @@ skin(char *name) case '>': if (gotlt) { + /* (addrspec_check() verifies these later!) */ + agp->ag_iaddr_end = (size_t)(cp - 1 - name); gotlt = 0; while ((c = *cp) != '\0' && c != ',') { cp++; @@ -685,7 +869,7 @@ skin(char *name) lastsp = 0; break; } - /* Fall into . . . */ + /* FALLTRHOUGH */ default: if (lastsp) { @@ -693,19 +877,23 @@ skin(char *name) *cp2++ = ' '; } *cp2++ = c; - if (c == ',' && !gotlt) { + if (c == ',' && ! gotlt) { *cp2++ = ' '; - for (; *cp == ' '; cp++) + for (; *cp == ' '; ++cp) ; lastsp = 0; bufend = cp2; } } } - *cp2 = 0; - cp = savestr(nbuf); + agp->ag_slen = (size_t)(cp2 - nbuf); + if (agp->ag_iaddr_end == 0) + agp->ag_iaddr_end = agp->ag_iaddr_start + agp->ag_slen; + + agp->ag_skinned = cp = savestrbuf(nbuf, agp->ag_slen); ac_free(nbuf); - return cp; + agp->ag_n_flags = NAME_NAME_SALLOC | NAME_SKINNED; + return (addrspec_check(doskin, agp)); } /* diff --git a/mime.c b/mime.c index 722c1b5bf..742616f27 100644 --- a/mime.c +++ b/mime.c @@ -142,108 +142,6 @@ delctrl(char *cp, size_t sz) return y; } -/* - * Check if a name's address part contains invalid characters. - */ -int -mime_name_invalid(struct name *np, int putmsg) -{ - char *name = np->n_name, *addr, *p; - int in_quote = 0, in_domain = 0, err = 0, hadat = 0; - - if (np->n_flags & NAME_MIME_CHECKED) - return (np->n_flags & NAME_MIME_INVALID) != 0; - np->n_flags |= NAME_MIME_CHECKED; - - if (is_fileaddr(name)) - return 0; - addr = skin(name); - - if (addr == NULL || *addr == '\0') { - np->n_flags |= NAME_MIME_INVALID; - return 1; - } - for (p = addr; *p != '\0'; p++) { - if (*p == '\"') { - in_quote = !in_quote; - } else if (*p < 040 || (*p & 0377) >= 0177) { - err = *p & 0377; - break; - } else if (in_domain == 2) { - if ((*p == ']' && p[1] != '\0') || *p == '\0' - || *p == '\\' || whitechar(*p & 0377)) { - err = *p & 0377; - break; - } - } else if (in_quote && in_domain == 0) { - /*EMPTY*/; - } else if (*p == '\\' && p[1] != '\0') { - p++; - } else if (*p == '@') { - if (hadat++) { - if (putmsg) { - fprintf(stderr, catgets(catd, CATSET, - 142, - "%s contains invalid @@ sequence\n"), - addr); - putmsg = 0; - } - err = *p; - break; - } - if (p[1] == '[') - in_domain = 2; - else - in_domain = 1; - continue; - } else if (*p == '(' || *p == ')' || *p == '<' || *p == '>' - || *p == ',' || *p == ';' || *p == ':' - || *p == '\\' || *p == '[' || *p == ']') { - err = *p & 0377; - break; - } - hadat = 0; - } - if (err) { - np->n_flags |= NAME_MIME_INVALID; - if (putmsg) { - char ce[sizeof(void*)]; - /* 2012-09-25: dropped isprint(3) even if available - * since that fails for UTF-8 anyway */ - if (err >= 040 && err <= 0177) - ce[0] = (char)err, ce[1] = '\0'; - else - snprintf(ce, sizeof(ce), "\\%03o", err); - fprintf(stderr, - tr(143, "%s contains invalid character '%s'\n"), - addr, ce); - } - } - return err; -} - -/* - * Check all addresses in np and delete invalid ones. - */ -struct name * -checkaddrs(struct name *np) -{ - struct name *n = np; - - while (n != NULL) { - if (mime_name_invalid(n, 1)) { - if (n->n_blink) - n->n_blink->n_flink = n->n_flink; - if (n->n_flink) - n->n_flink->n_blink = n->n_blink; - if (n == np) - np = n->n_flink; - } - n = n->n_flink; - } - return np; -} - static char defcharset[] = "utf-8"; /* @@ -800,6 +698,7 @@ gettextconversion(void) return convert; } +/*TODO Dobson: be037047c, contenttype==NULL||"text"==NULL control flow! */ int get_mime_convert(FILE *fp, char **contenttype, char **charset, enum mimeclean *isclean, int dosign) @@ -876,13 +775,13 @@ mime_write_toqp(struct str *in, FILE *fo, int (*mustquote)(int)) sz = in->l; upper = in->s + in->l; for (p = in->s, l = 0; p < upper; p++) { - if (mustquote(*p&0377) || - (p < upper-1 && p[1] == '\n' && - blankchar(p[0]&0377)) || - (p < upper-4 && l == 0 && - p[0] == 'F' && p[1] == 'r' && + if (mustquote(*p) || + (p < upper - 1 && p[1] == '\n' && + blankchar(*p)) || + (p < upper - 4 && l == 0 && + *p == 'F' && p[1] == 'r' && p[2] == 'o' && p[3] == 'm') || - (*p == '.' && l == 0 && p < upper-1 && + (*p == '.' && l == 0 && p < upper - 1 && p[1] == '\n')) { if (l >= 69) { sz += 2; @@ -891,7 +790,7 @@ mime_write_toqp(struct str *in, FILE *fo, int (*mustquote)(int)) } sz += 2; putc('=', fo); - h = ctohex(*p&0377, hex); + h = ctohex(*p, hex); fwrite(h, sizeof *h, 2, fo); l += 3; } else { @@ -1510,7 +1409,7 @@ prefixwrite(void *ptr, size_t size, size_t nmemb, FILE *f, /* (c: keep cc happy) */ for (c = i = 0; p + i < maxp;) { c = p[i++]; - if (blankspacechar(c)) /* XXX U+A0+ */ + if (blankspacechar(c)) continue; if (! ISQUOTE(c)) goto jquoteok; diff --git a/names.c b/names.c index ddfc0b1bd..90e6eb3c3 100644 --- a/names.c +++ b/names.c @@ -50,15 +50,16 @@ #include #include -static struct name *tailof(struct name *name); -static struct name *extract1(char *line, enum gfield ntype, char *separators, - int copypfx); -static char *yankword(char *ap, char *wbuf, char *separators, int copypfx); -static int same_name(char *n1, char *n2); -static struct name *gexpand(struct name *nlist, struct grouphead *gh, - int metoo, int ntype); -static struct name *put(struct name *list, struct name *node); -static struct name *delname(struct name *np, char *name); +static struct name * tailof(struct name *name); +static struct name * extract1(char *line, enum gfield ntype, + char *separators, int copypfx); +static char * yankword(char *ap, char *wbuf, char *separators, + int copypfx); +static int same_name(char *n1, char *n2); +static struct name * gexpand(struct name *nlist, struct grouphead *gh, + int metoo, int ntype); +static struct name * put(struct name *list, struct name *node); +static struct name * delname(struct name *np, char *name); /* * Allocate a single element of a name list, @@ -68,30 +69,38 @@ static struct name *delname(struct name *np, char *name); struct name * nalloc(char *str, enum gfield ntype) { + struct addrguts ag; + struct str in, out; struct name *np; - struct str in, out; - /*LINTED*/ - np = (struct name *)salloc(sizeof *np); + np = (struct name*)salloc(sizeof *np); np->n_flink = NULL; np->n_blink = NULL; np->n_type = ntype; np->n_flags = 0; - if (ntype & GFULL) { - np->n_name = savestr(skin(str)); - if (strcmp(np->n_name, str)) { + + if (ntype & (GFULL | GSKIN)) { + (void)addrspec_with_guts(1, str, &ag); + if ((ag.ag_n_flags & NAME_NAME_SALLOC) == 0) { + ag.ag_n_flags |= NAME_NAME_SALLOC; + ag.ag_skinned = savestr(ag.ag_skinned); + } + np->n_name = np->n_fullname = ag.ag_skinned; + np->n_flags = ag.ag_n_flags; + if ((ntype & GFULL) && ag.ag_ilen != ag.ag_slen) { in.s = str; - in.l = strlen(str); + in.l = ag.ag_ilen; mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV); np->n_fullname = savestr(out.s); free(out.s); - } else - np->n_fullname = np->n_name; - } else if (ntype & GSKIN) - np->n_fullname = np->n_name = savestr(skin(str)); - else + np->n_flags |= NAME_FULLNAME_SALLOC; + } + } else { np->n_fullname = np->n_name = savestr(str); - return(np); + (void)addrspec_with_guts(0, str, &ag); + np->n_flags = ag.ag_n_flags | NAME_NAME_SALLOC; + } + return (np); } struct name * @@ -99,15 +108,25 @@ ndup(struct name *np, enum gfield ntype) { struct name *nnp; + if ((ntype & (GFULL|GSKIN)) && (np->n_flags & NAME_SKINNED) == 0) { + nnp = nalloc(np->n_name, ntype); + goto jleave; + } + nnp = (struct name*)salloc(sizeof *np); - nnp->n_flink = NULL; - nnp->n_blink = NULL; + nnp->n_flink = nnp->n_blink = NULL; nnp->n_type = ntype; - nnp->n_flags = np->n_flags; + nnp->n_flags = (np->n_flags & + ~(NAME_NAME_SALLOC | NAME_FULLNAME_SALLOC)) | + NAME_NAME_SALLOC; nnp->n_name = savestr(np->n_name); - nnp->n_fullname = (((ntype & (GFULL|GSKIN)) == 0) || - np->n_name == np->n_fullname) - ? nnp->n_name : savestr(np->n_fullname); + if (np->n_name == np->n_fullname || (ntype & (GFULL|GSKIN)) == 0) + nnp->n_fullname = nnp->n_name; + else { + nnp->n_flags |= NAME_FULLNAME_SALLOC; + nnp->n_fullname = savestr(np->n_fullname); + } +jleave: return (nnp); } @@ -235,8 +254,8 @@ yankword(char *ap, char *wbuf, char *separators, int copypfx) cp = ap; wp = wbuf; - while (blankchar(*cp & 0377) || *cp == ',') - cp++; + while (blankspacechar(*cp) || *cp == ',') + ++cp; pp = cp; if ((cp = nexttoken(cp)) == NULL) return NULL; @@ -272,6 +291,28 @@ yankword(char *ap, char *wbuf, char *separators, int copypfx) } /* + * Check all addresses in np and delete invalid ones. + */ +struct name * +checkaddrs(struct name *np) +{ + struct name *n; + + for (n = np; n != NULL;) { + if (is_addr_invalid(n, 1)) { + if (n->n_blink) + n->n_blink->n_flink = n->n_flink; + if (n->n_flink) + n->n_flink->n_blink = n->n_blink; + if (n == np) + np = n->n_flink; + } + n = n->n_flink; + } + return (np); +} + +/* * For each recipient in the passed name list with a / * in the name, append the message to the end of the named file * and remove him from the recipient list. @@ -469,29 +510,6 @@ jdelall: goto jleave; } -/* - * Determine if the passed address is a local "send to file" address. - * If any of the network metacharacters precedes any slashes, it can't - * be a filename. We cheat with .'s to allow path names like ./... - */ -int -is_fileaddr(char *name) -{ - char *cp; - - if (strchr(name, '@') != NULL) - return 0; - if (*name == '+') - return 1; - for (cp = name; *cp; cp++) { - if (*cp == '!' || *cp == '%') - return 0; - if (*cp == '/') - return 1; - } - return 0; -} - static int same_name(char *n1, char *n2) { diff --git a/sendout.c b/sendout.c index 071fee68b..f12c6a56b 100644 --- a/sendout.c +++ b/sendout.c @@ -116,15 +116,19 @@ getencoding(enum conversion convert) * the distribution list into the appropriate fields. */ static struct name * -fixhead(struct header *hp, struct name *tolist) +fixhead(struct header *hp, struct name *tolist) /* TODO !HAVE_ASSERTS legacy*/ { struct name *np; hp->h_to = hp->h_cc = hp->h_bcc = NULL; for (np = tolist; np != NULL; np = np->n_flink) - if (np->n_type & GDEL) + if (np->n_type & GDEL) { +#ifdef HAVE_ASSERTS + assert(0); /* Shouldn't happen here, but later on :)) */ +#else continue; - else switch (np->n_type & GMASK) { +#endif + } else switch (np->n_type & GMASK) { case (GTO): hp->h_to = cat(hp->h_to, ndup(np, np->n_type|GFULL)); break; @@ -1299,7 +1303,7 @@ puthead(struct header *hp, FILE *fo, enum gfield w, if ((np = hp->h_ref) != NULL && np->n_name) { while (np->n_flink) np = np->n_flink; - if (mime_name_invalid(np, 0) == 0) { + if (is_addr_invalid(np, 0) == 0) { fprintf(fo, "In-Reply-To: %s\n", np->n_name); gotcha++; } @@ -1355,7 +1359,7 @@ fmt(char *str, struct name *np, FILE *fo, int flags, int dropinvalid, for (; np != NULL; np = np->n_flink) { if ((m & m_NOPF) && is_fileorpipe_addr(np)) continue; - if (mime_name_invalid(np, ! dropinvalid)) { + if (is_addr_invalid(np, ! dropinvalid)) { if (dropinvalid) continue; else diff --git a/tty.c b/tty.c index 5cc468a5f..6805f8e16 100644 --- a/tty.c +++ b/tty.c @@ -247,7 +247,7 @@ grabaddrs(const char *field, struct name *np, int comma, enum gfield gflags) loop: np = lextract(rtty_internal(field, detract(np, comma)), gflags); for (nq = np; nq != NULL; nq = nq->n_flink) - if (mime_name_invalid(nq, 1)) + if (is_addr_invalid(nq, 1)) goto loop; return np; } -- 2.11.4.GIT