138032Speter/* 2168520Sgshapiro * Copyright (c) 1998-2004, 2006, 2007 Sendmail, Inc. and its suppliers. 364565Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1490795Sgshapiro#include <sendmail.h> 15168520Sgshapiro#include <sm/sendmail.h> 1638032Speter 17244833SgshapiroSM_RCSID("@(#)$Id: headers.c,v 8.318 2012/06/14 23:54:02 ca Exp $") 1864565Sgshapiro 19168520Sgshapirostatic HDR *allocheader __P((char *, char *, int, SM_RPOOL_T *, bool)); 20111826Sgshapirostatic size_t fix_mime_header __P((HDR *, ENVELOPE *)); 2164565Sgshapirostatic int priencode __P((char *)); 22157006Sgshapirostatic bool put_vanilla_header __P((HDR *, char *, MCI *)); 2364565Sgshapiro 2438032Speter/* 2538032Speter** SETUPHEADERS -- initialize headers in symbol table 2638032Speter** 2738032Speter** Parameters: 2838032Speter** none 2938032Speter** 3038032Speter** Returns: 3138032Speter** none 3238032Speter*/ 3338032Speter 3438032Spetervoid 3538032Spetersetupheaders() 3638032Speter{ 3738032Speter struct hdrinfo *hi; 3838032Speter STAB *s; 3938032Speter 4038032Speter for (hi = HdrInfo; hi->hi_field != NULL; hi++) 4138032Speter { 4238032Speter s = stab(hi->hi_field, ST_HEADER, ST_ENTER); 4338032Speter s->s_header.hi_flags = hi->hi_flags; 4438032Speter s->s_header.hi_ruleset = NULL; 4538032Speter } 4638032Speter} 47168520Sgshapiro 4890795Sgshapiro/* 49168520Sgshapiro** DOCHOMPHEADER -- process and save a header line. 5038032Speter** 51168520Sgshapiro** Called by chompheader. 5238032Speter** 5338032Speter** Parameters: 5438032Speter** line -- header as a text line. 5566497Sgshapiro** pflag -- flags for chompheader() (from sendmail.h) 5638032Speter** hdrp -- a pointer to the place to save the header. 5738032Speter** e -- the envelope including this header. 5838032Speter** 5938032Speter** Returns: 6038032Speter** flags for this header. 6138032Speter** 6238032Speter** Side Effects: 6338032Speter** The header is saved on the header list. 6438032Speter** Contents of 'line' are destroyed. 6538032Speter*/ 6638032Speter 6764565Sgshapirostatic struct hdrinfo NormalHeader = { NULL, 0, NULL }; 68168520Sgshapirostatic unsigned long dochompheader __P((char *, int, HDR **, ENVELOPE *)); 6938032Speter 70168520Sgshapirostatic unsigned long 71168520Sgshapirodochompheader(line, pflag, hdrp, e) 7238032Speter char *line; 7364565Sgshapiro int pflag; 7438032Speter HDR **hdrp; 75168520Sgshapiro ENVELOPE *e; 7638032Speter{ 7790795Sgshapiro unsigned char mid = '\0'; 7838032Speter register char *p; 7938032Speter register HDR *h; 8038032Speter HDR **hp; 8138032Speter char *fname; 8238032Speter char *fvalue; 8390795Sgshapiro bool cond = false; 8464565Sgshapiro bool dropfrom; 8538032Speter bool headeronly; 8638032Speter STAB *s; 8738032Speter struct hdrinfo *hi; 8890795Sgshapiro bool nullheader = false; 8964565Sgshapiro BITMAP256 mopts; 9038032Speter 9138032Speter headeronly = hdrp != NULL; 9238032Speter if (!headeronly) 9338032Speter hdrp = &e->e_header; 9438032Speter 9538032Speter /* strip off options */ 9638032Speter clrbitmap(mopts); 9738032Speter p = line; 9864565Sgshapiro if (!bitset(pflag, CHHDR_USER) && *p == '?') 9938032Speter { 10064565Sgshapiro int c; 10164565Sgshapiro register char *q; 10264565Sgshapiro 10364565Sgshapiro q = strchr(++p, '?'); 10464565Sgshapiro if (q == NULL) 10564565Sgshapiro goto hse; 10664565Sgshapiro 10764565Sgshapiro *q = '\0'; 10864565Sgshapiro c = *p & 0377; 10964565Sgshapiro 11064565Sgshapiro /* possibly macro conditional */ 11164565Sgshapiro if (c == MACROEXPAND) 11238032Speter { 11364565Sgshapiro /* catch ?$? */ 11464565Sgshapiro if (*++p == '\0') 11564565Sgshapiro { 11664565Sgshapiro *q = '?'; 11764565Sgshapiro goto hse; 11864565Sgshapiro } 11964565Sgshapiro 12090795Sgshapiro mid = (unsigned char) *p++; 12164565Sgshapiro 12264565Sgshapiro /* catch ?$abc? */ 12364565Sgshapiro if (*p != '\0') 12464565Sgshapiro { 12564565Sgshapiro *q = '?'; 12664565Sgshapiro goto hse; 12764565Sgshapiro } 12864565Sgshapiro } 12964565Sgshapiro else if (*p == '$') 13064565Sgshapiro { 13164565Sgshapiro /* catch ?$? */ 13264565Sgshapiro if (*++p == '\0') 13364565Sgshapiro { 13464565Sgshapiro *q = '?'; 13564565Sgshapiro goto hse; 13664565Sgshapiro } 13764565Sgshapiro 13890795Sgshapiro mid = (unsigned char) macid(p); 13964565Sgshapiro if (bitset(0200, mid)) 140120259Sgshapiro { 14164565Sgshapiro p += strlen(macname(mid)) + 2; 142120259Sgshapiro SM_ASSERT(p <= q); 143120259Sgshapiro } 14464565Sgshapiro else 14564565Sgshapiro p++; 14664565Sgshapiro 14764565Sgshapiro /* catch ?$abc? */ 14864565Sgshapiro if (*p != '\0') 14964565Sgshapiro { 15064565Sgshapiro *q = '?'; 15164565Sgshapiro goto hse; 15264565Sgshapiro } 15364565Sgshapiro } 15464565Sgshapiro else 15564565Sgshapiro { 15664565Sgshapiro while (*p != '\0') 15764565Sgshapiro { 15864565Sgshapiro if (!isascii(*p)) 15964565Sgshapiro { 16064565Sgshapiro *q = '?'; 16164565Sgshapiro goto hse; 16264565Sgshapiro } 16364565Sgshapiro 16471348Sgshapiro setbitn(bitidx(*p), mopts); 16590795Sgshapiro cond = true; 16664565Sgshapiro p++; 16764565Sgshapiro } 16838032Speter } 16964565Sgshapiro p = q + 1; 17038032Speter } 17138032Speter 17238032Speter /* find canonical name */ 17338032Speter fname = p; 17438032Speter while (isascii(*p) && isgraph(*p) && *p != ':') 17538032Speter p++; 17638032Speter fvalue = p; 17738032Speter while (isascii(*p) && isspace(*p)) 17838032Speter p++; 17938032Speter if (*p++ != ':' || fname == fvalue) 18038032Speter { 18164565Sgshapirohse: 18264565Sgshapiro syserr("553 5.3.0 header syntax error, line \"%s\"", line); 18338032Speter return 0; 18438032Speter } 18538032Speter *fvalue = '\0'; 18643733Speter fvalue = p; 18738032Speter 18843733Speter /* if the field is null, go ahead and use the default */ 18943733Speter while (isascii(*p) && isspace(*p)) 19043733Speter p++; 19143733Speter if (*p == '\0') 19290795Sgshapiro nullheader = true; 19343733Speter 19438032Speter /* security scan: long field names are end-of-header */ 19538032Speter if (strlen(fname) > 100) 19638032Speter return H_EOH; 19738032Speter 19838032Speter /* check to see if it represents a ruleset call */ 19964565Sgshapiro if (bitset(pflag, CHHDR_DEF)) 20038032Speter { 20138032Speter char hbuf[50]; 20238032Speter 203168520Sgshapiro (void) expand(fvalue, hbuf, sizeof(hbuf), e); 20438032Speter for (p = hbuf; isascii(*p) && isspace(*p); ) 20538032Speter p++; 20638032Speter if ((*p++ & 0377) == CALLSUBR) 20738032Speter { 20838032Speter auto char *endp; 20964565Sgshapiro bool strc; 21038032Speter 21164565Sgshapiro strc = *p == '+'; /* strip comments? */ 21264565Sgshapiro if (strc) 21364565Sgshapiro ++p; 21438032Speter if (strtorwset(p, &endp, ST_ENTER) > 0) 21538032Speter { 21638032Speter *endp = '\0'; 21738032Speter s = stab(fname, ST_HEADER, ST_ENTER); 21890795Sgshapiro if (LogLevel > 9 && 21990795Sgshapiro s->s_header.hi_ruleset != NULL) 22090795Sgshapiro sm_syslog(LOG_WARNING, NOQID, 22190795Sgshapiro "Warning: redefined ruleset for header=%s, old=%s, new=%s", 22290795Sgshapiro fname, 22390795Sgshapiro s->s_header.hi_ruleset, p); 22438032Speter s->s_header.hi_ruleset = newstr(p); 22564565Sgshapiro if (!strc) 22664565Sgshapiro s->s_header.hi_flags |= H_STRIPCOMM; 22738032Speter } 22838032Speter return 0; 22938032Speter } 23038032Speter } 23138032Speter 23238032Speter /* see if it is a known type */ 23338032Speter s = stab(fname, ST_HEADER, ST_FIND); 23438032Speter if (s != NULL) 23538032Speter hi = &s->s_header; 23638032Speter else 23738032Speter hi = &NormalHeader; 23838032Speter 23938032Speter if (tTd(31, 9)) 24038032Speter { 24138032Speter if (s == NULL) 24290795Sgshapiro sm_dprintf("no header flags match\n"); 24338032Speter else 24490795Sgshapiro sm_dprintf("header match, flags=%lx, ruleset=%s\n", 24590795Sgshapiro hi->hi_flags, 24690795Sgshapiro hi->hi_ruleset == NULL ? "<NULL>" 24790795Sgshapiro : hi->hi_ruleset); 24838032Speter } 24938032Speter 25038032Speter /* see if this is a resent message */ 25164565Sgshapiro if (!bitset(pflag, CHHDR_DEF) && !headeronly && 25264565Sgshapiro bitset(H_RESENT, hi->hi_flags)) 25338032Speter e->e_flags |= EF_RESENT; 25438032Speter 25538032Speter /* if this is an Errors-To: header keep track of it now */ 25664565Sgshapiro if (UseErrorsTo && !bitset(pflag, CHHDR_DEF) && !headeronly && 25738032Speter bitset(H_ERRORSTO, hi->hi_flags)) 25838032Speter (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e); 25938032Speter 26038032Speter /* if this means "end of header" quit now */ 26138032Speter if (!headeronly && bitset(H_EOH, hi->hi_flags)) 26238032Speter return hi->hi_flags; 26338032Speter 26438032Speter /* 26538032Speter ** Horrible hack to work around problem with Lotus Notes SMTP 26638032Speter ** mail gateway, which generates From: headers with newlines in 26738032Speter ** them and the <address> on the second line. Although this is 26838032Speter ** legal RFC 822, many MUAs don't handle this properly and thus 26938032Speter ** never find the actual address. 27038032Speter */ 27138032Speter 27238032Speter if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader) 27338032Speter { 27438032Speter while ((p = strchr(fvalue, '\n')) != NULL) 27538032Speter *p = ' '; 27638032Speter } 27738032Speter 27838032Speter /* 27938032Speter ** If there is a check ruleset, verify it against the header. 28038032Speter */ 28138032Speter 28264565Sgshapiro if (bitset(pflag, CHHDR_CHECK)) 28364565Sgshapiro { 284102531Sgshapiro int rscheckflags; 28564565Sgshapiro char *rs; 28638032Speter 287102531Sgshapiro rscheckflags = RSF_COUNT; 288102531Sgshapiro if (!bitset(hi->hi_flags, H_FROM|H_RCPT)) 289102531Sgshapiro rscheckflags |= RSF_UNSTRUCTURED; 290132946Sgshapiro 291132946Sgshapiro /* no ruleset? look for default */ 292132946Sgshapiro rs = hi->hi_ruleset; 29364565Sgshapiro if (rs == NULL) 29464565Sgshapiro { 29564565Sgshapiro s = stab("*", ST_HEADER, ST_FIND); 29664565Sgshapiro if (s != NULL) 29764565Sgshapiro { 29864565Sgshapiro rs = (&s->s_header)->hi_ruleset; 299102531Sgshapiro if (bitset((&s->s_header)->hi_flags, 300102531Sgshapiro H_STRIPCOMM)) 301102531Sgshapiro rscheckflags |= RSF_RMCOMM; 30264565Sgshapiro } 30364565Sgshapiro } 304102531Sgshapiro else if (bitset(hi->hi_flags, H_STRIPCOMM)) 305102531Sgshapiro rscheckflags |= RSF_RMCOMM; 30664565Sgshapiro if (rs != NULL) 30764565Sgshapiro { 30890795Sgshapiro int l, k; 30964565Sgshapiro char qval[MAXNAME]; 31064565Sgshapiro 31164565Sgshapiro l = 0; 31290795Sgshapiro qval[l++] = '"'; 31390795Sgshapiro 31490795Sgshapiro /* - 3 to avoid problems with " at the end */ 315120259Sgshapiro /* should be sizeof(qval), not MAXNAME */ 31690795Sgshapiro for (k = 0; fvalue[k] != '\0' && l < MAXNAME - 3; k++) 31764565Sgshapiro { 31890795Sgshapiro switch (fvalue[k]) 31964565Sgshapiro { 32090795Sgshapiro /* XXX other control chars? */ 32164565Sgshapiro case '\011': /* ht */ 32264565Sgshapiro case '\012': /* nl */ 32364565Sgshapiro case '\013': /* vt */ 32464565Sgshapiro case '\014': /* np */ 32564565Sgshapiro case '\015': /* cr */ 32690795Sgshapiro qval[l++] = ' '; 32764565Sgshapiro break; 32864565Sgshapiro case '"': 32990795Sgshapiro qval[l++] = '\\'; 33064565Sgshapiro /* FALLTHROUGH */ 33164565Sgshapiro default: 33290795Sgshapiro qval[l++] = fvalue[k]; 33364565Sgshapiro break; 33464565Sgshapiro } 33564565Sgshapiro } 33690795Sgshapiro qval[l++] = '"'; 33790795Sgshapiro qval[l] = '\0'; 33890795Sgshapiro k += strlen(fvalue + k); 33990795Sgshapiro if (k >= MAXNAME) 34064565Sgshapiro { 34164565Sgshapiro if (LogLevel > 9) 34264565Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 34364565Sgshapiro "Warning: truncated header '%s' before check with '%s' len=%d max=%d", 34490795Sgshapiro fname, rs, k, MAXNAME - 1); 34564565Sgshapiro } 34690795Sgshapiro macdefine(&e->e_macro, A_TEMP, 34790795Sgshapiro macid("{currHeader}"), qval); 34890795Sgshapiro macdefine(&e->e_macro, A_TEMP, 34990795Sgshapiro macid("{hdr_name}"), fname); 35090795Sgshapiro 351168520Sgshapiro (void) sm_snprintf(qval, sizeof(qval), "%d", k); 35290795Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{hdrlen}"), qval); 353132946Sgshapiro if (bitset(H_FROM, hi->hi_flags)) 35490795Sgshapiro macdefine(&e->e_macro, A_PERM, 35590795Sgshapiro macid("{addr_type}"), "h s"); 356132946Sgshapiro else if (bitset(H_RCPT, hi->hi_flags)) 35790795Sgshapiro macdefine(&e->e_macro, A_PERM, 35890795Sgshapiro macid("{addr_type}"), "h r"); 35990795Sgshapiro else 36090795Sgshapiro macdefine(&e->e_macro, A_PERM, 36190795Sgshapiro macid("{addr_type}"), "h"); 362102531Sgshapiro (void) rscheck(rs, fvalue, NULL, e, rscheckflags, 3, 363168520Sgshapiro NULL, e->e_id, NULL); 36464565Sgshapiro } 36564565Sgshapiro } 36664565Sgshapiro 36738032Speter /* 36838032Speter ** Drop explicit From: if same as what we would generate. 36938032Speter ** This is to make MH (which doesn't always give a full name) 37038032Speter ** insert the full name information in all circumstances. 37138032Speter */ 37238032Speter 37390795Sgshapiro dropfrom = false; 37438032Speter p = "resent-from"; 37538032Speter if (!bitset(EF_RESENT, e->e_flags)) 37638032Speter p += 7; 37764565Sgshapiro if (!bitset(pflag, CHHDR_DEF) && !headeronly && 37890795Sgshapiro !bitset(EF_QUEUERUN, e->e_flags) && sm_strcasecmp(fname, p) == 0) 37938032Speter { 38038032Speter if (tTd(31, 2)) 38138032Speter { 38290795Sgshapiro sm_dprintf("comparing header from (%s) against default (%s or %s)\n", 38338032Speter fvalue, e->e_from.q_paddr, e->e_from.q_user); 38438032Speter } 38538032Speter if (e->e_from.q_paddr != NULL && 38664565Sgshapiro e->e_from.q_mailer != NULL && 38764565Sgshapiro bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) && 38838032Speter (strcmp(fvalue, e->e_from.q_paddr) == 0 || 38938032Speter strcmp(fvalue, e->e_from.q_user) == 0)) 39090795Sgshapiro dropfrom = true; 39138032Speter } 39238032Speter 39338032Speter /* delete default value for this header */ 39438032Speter for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link) 39538032Speter { 39690795Sgshapiro if (sm_strcasecmp(fname, h->h_field) == 0 && 39764565Sgshapiro !bitset(H_USER, h->h_flags) && 39838032Speter !bitset(H_FORCE, h->h_flags)) 39938032Speter { 40043733Speter if (nullheader) 40143733Speter { 40243733Speter /* user-supplied value was null */ 40343733Speter return 0; 40443733Speter } 40564565Sgshapiro if (dropfrom) 40664565Sgshapiro { 40764565Sgshapiro /* make this look like the user entered it */ 40864565Sgshapiro h->h_flags |= H_USER; 40964565Sgshapiro return hi->hi_flags; 41064565Sgshapiro } 41138032Speter h->h_value = NULL; 41238032Speter if (!cond) 41338032Speter { 41438032Speter /* copy conditions from default case */ 41590795Sgshapiro memmove((char *) mopts, (char *) h->h_mflags, 416168520Sgshapiro sizeof(mopts)); 41738032Speter } 41864565Sgshapiro h->h_macro = mid; 41938032Speter } 42038032Speter } 42138032Speter 42238032Speter /* create a new node */ 423168520Sgshapiro h = (HDR *) sm_rpool_malloc_x(e->e_rpool, sizeof(*h)); 42490795Sgshapiro h->h_field = sm_rpool_strdup_x(e->e_rpool, fname); 42590795Sgshapiro h->h_value = sm_rpool_strdup_x(e->e_rpool, fvalue); 42638032Speter h->h_link = NULL; 427168520Sgshapiro memmove((char *) h->h_mflags, (char *) mopts, sizeof(mopts)); 42864565Sgshapiro h->h_macro = mid; 42938032Speter *hp = h; 43038032Speter h->h_flags = hi->hi_flags; 43166497Sgshapiro if (bitset(pflag, CHHDR_USER) || bitset(pflag, CHHDR_QUEUE)) 43264565Sgshapiro h->h_flags |= H_USER; 43338032Speter 43438032Speter /* strip EOH flag if parsing MIME headers */ 43538032Speter if (headeronly) 43638032Speter h->h_flags &= ~H_EOH; 43764565Sgshapiro if (bitset(pflag, CHHDR_DEF)) 43838032Speter h->h_flags |= H_DEFAULT; 43964565Sgshapiro if (cond || mid != '\0') 44038032Speter h->h_flags |= H_CHECK; 44138032Speter 44238032Speter /* hack to see if this is a new format message */ 44364565Sgshapiro if (!bitset(pflag, CHHDR_DEF) && !headeronly && 44464565Sgshapiro bitset(H_RCPT|H_FROM, h->h_flags) && 44538032Speter (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL || 44638032Speter strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL)) 44738032Speter { 44838032Speter e->e_flags &= ~EF_OLDSTYLE; 44938032Speter } 45038032Speter 45138032Speter return h->h_flags; 45238032Speter} 453168520Sgshapiro 45490795Sgshapiro/* 455168520Sgshapiro** CHOMPHEADER -- process and save a header line. 456168520Sgshapiro** 457168520Sgshapiro** Called by collect, readcf, and readqf to deal with header lines. 458168520Sgshapiro** This is just a wrapper for dochompheader(). 459168520Sgshapiro** 460168520Sgshapiro** Parameters: 461168520Sgshapiro** line -- header as a text line. 462168520Sgshapiro** pflag -- flags for chompheader() (from sendmail.h) 463168520Sgshapiro** hdrp -- a pointer to the place to save the header. 464168520Sgshapiro** e -- the envelope including this header. 465168520Sgshapiro** 466168520Sgshapiro** Returns: 467168520Sgshapiro** flags for this header. 468168520Sgshapiro** 469168520Sgshapiro** Side Effects: 470168520Sgshapiro** The header is saved on the header list. 471168520Sgshapiro** Contents of 'line' are destroyed. 472168520Sgshapiro*/ 473168520Sgshapiro 474168520Sgshapiro 475168520Sgshapirounsigned long 476168520Sgshapirochompheader(line, pflag, hdrp, e) 477168520Sgshapiro char *line; 478168520Sgshapiro int pflag; 479168520Sgshapiro HDR **hdrp; 480168520Sgshapiro register ENVELOPE *e; 481168520Sgshapiro{ 482168520Sgshapiro unsigned long rval; 483168520Sgshapiro 484168520Sgshapiro if (tTd(31, 6)) 485168520Sgshapiro { 486168520Sgshapiro sm_dprintf("chompheader: "); 487168520Sgshapiro xputs(sm_debug_file(), line); 488168520Sgshapiro sm_dprintf("\n"); 489168520Sgshapiro } 490168520Sgshapiro 491168520Sgshapiro /* quote this if user (not config file) input */ 492168520Sgshapiro if (bitset(pflag, CHHDR_USER)) 493168520Sgshapiro { 494168520Sgshapiro char xbuf[MAXLINE]; 495168520Sgshapiro char *xbp = NULL; 496168520Sgshapiro int xbufs; 497168520Sgshapiro 498168520Sgshapiro xbufs = sizeof(xbuf); 499168520Sgshapiro xbp = quote_internal_chars(line, xbuf, &xbufs); 500168520Sgshapiro if (tTd(31, 7)) 501168520Sgshapiro { 502168520Sgshapiro sm_dprintf("chompheader: quoted: "); 503168520Sgshapiro xputs(sm_debug_file(), xbp); 504168520Sgshapiro sm_dprintf("\n"); 505168520Sgshapiro } 506168520Sgshapiro rval = dochompheader(xbp, pflag, hdrp, e); 507168520Sgshapiro if (xbp != xbuf) 508168520Sgshapiro sm_free(xbp); 509168520Sgshapiro } 510168520Sgshapiro else 511168520Sgshapiro rval = dochompheader(line, pflag, hdrp, e); 512168520Sgshapiro 513168520Sgshapiro return rval; 514168520Sgshapiro} 515168520Sgshapiro 516168520Sgshapiro/* 517132946Sgshapiro** ALLOCHEADER -- allocate a header entry 518132946Sgshapiro** 519132946Sgshapiro** Parameters: 520168520Sgshapiro** field -- the name of the header field (will not be copied). 521168520Sgshapiro** value -- the value of the field (will be copied). 522132946Sgshapiro** flags -- flags to add to h_flags. 523132946Sgshapiro** rp -- resource pool for allocations 524168520Sgshapiro** space -- add leading space? 525132946Sgshapiro** 526132946Sgshapiro** Returns: 527132946Sgshapiro** Pointer to a newly allocated and populated HDR. 528168520Sgshapiro** 529168520Sgshapiro** Notes: 530168520Sgshapiro** o field and value must be in internal format, i.e., 531168520Sgshapiro** metacharacters must be "quoted", see quote_internal_chars(). 532168520Sgshapiro** o maybe add more flags to decide: 533168520Sgshapiro** - what to copy (field/value) 534168520Sgshapiro** - whether to convert value to an internal format 535132946Sgshapiro*/ 536132946Sgshapiro 537132946Sgshapirostatic HDR * 538168520Sgshapiroallocheader(field, value, flags, rp, space) 539132946Sgshapiro char *field; 540132946Sgshapiro char *value; 541132946Sgshapiro int flags; 542132946Sgshapiro SM_RPOOL_T *rp; 543168520Sgshapiro bool space; 544132946Sgshapiro{ 545132946Sgshapiro HDR *h; 546132946Sgshapiro STAB *s; 547132946Sgshapiro 548132946Sgshapiro /* find info struct */ 549132946Sgshapiro s = stab(field, ST_HEADER, ST_FIND); 550132946Sgshapiro 551132946Sgshapiro /* allocate space for new header */ 552168520Sgshapiro h = (HDR *) sm_rpool_malloc_x(rp, sizeof(*h)); 553132946Sgshapiro h->h_field = field; 554168520Sgshapiro if (space) 555168520Sgshapiro { 556168520Sgshapiro size_t l; 557168520Sgshapiro char *n; 558168520Sgshapiro 559168520Sgshapiro l = strlen(value); 560168520Sgshapiro SM_ASSERT(l + 2 > l); 561168520Sgshapiro n = sm_rpool_malloc_x(rp, l + 2); 562168520Sgshapiro n[0] = ' '; 563168520Sgshapiro n[1] = '\0'; 564168520Sgshapiro sm_strlcpy(n + 1, value, l + 1); 565168520Sgshapiro h->h_value = n; 566168520Sgshapiro } 567168520Sgshapiro else 568168520Sgshapiro h->h_value = sm_rpool_strdup_x(rp, value); 569132946Sgshapiro h->h_flags = flags; 570132946Sgshapiro if (s != NULL) 571132946Sgshapiro h->h_flags |= s->s_header.hi_flags; 572132946Sgshapiro clrbitmap(h->h_mflags); 573132946Sgshapiro h->h_macro = '\0'; 574132946Sgshapiro 575132946Sgshapiro return h; 576132946Sgshapiro} 577168520Sgshapiro 578132946Sgshapiro/* 57938032Speter** ADDHEADER -- add a header entry to the end of the queue. 58038032Speter** 58138032Speter** This bypasses the special checking of chompheader. 58238032Speter** 58338032Speter** Parameters: 584168520Sgshapiro** field -- the name of the header field (will not be copied). 585168520Sgshapiro** value -- the value of the field (will be copied). 58664565Sgshapiro** flags -- flags to add to h_flags. 58790795Sgshapiro** e -- envelope. 588168520Sgshapiro** space -- add leading space? 58938032Speter** 59038032Speter** Returns: 59138032Speter** none. 59238032Speter** 59338032Speter** Side Effects: 59438032Speter** adds the field on the list of headers for this envelope. 595168520Sgshapiro** 596168520Sgshapiro** Notes: field and value must be in internal format, i.e., 597168520Sgshapiro** metacharacters must be "quoted", see quote_internal_chars(). 59838032Speter*/ 59938032Speter 60038032Spetervoid 601168520Sgshapiroaddheader(field, value, flags, e, space) 60238032Speter char *field; 60338032Speter char *value; 60464565Sgshapiro int flags; 60590795Sgshapiro ENVELOPE *e; 606168520Sgshapiro bool space; 60738032Speter{ 60838032Speter register HDR *h; 60938032Speter HDR **hp; 61090795Sgshapiro HDR **hdrlist = &e->e_header; 61138032Speter 61238032Speter /* find current place in list -- keep back pointer? */ 61338032Speter for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link) 61438032Speter { 61590795Sgshapiro if (sm_strcasecmp(field, h->h_field) == 0) 61638032Speter break; 61738032Speter } 61838032Speter 61938032Speter /* allocate space for new header */ 620168520Sgshapiro h = allocheader(field, value, flags, e->e_rpool, space); 62138032Speter h->h_link = *hp; 62238032Speter *hp = h; 62338032Speter} 624168520Sgshapiro 62590795Sgshapiro/* 626132946Sgshapiro** INSHEADER -- insert a header entry at the specified index 627132946Sgshapiro** This bypasses the special checking of chompheader. 628132946Sgshapiro** 629132946Sgshapiro** Parameters: 630132946Sgshapiro** idx -- index into the header list at which to insert 631168520Sgshapiro** field -- the name of the header field (will be copied). 632168520Sgshapiro** value -- the value of the field (will be copied). 633132946Sgshapiro** flags -- flags to add to h_flags. 634132946Sgshapiro** e -- envelope. 635168520Sgshapiro** space -- add leading space? 636132946Sgshapiro** 637132946Sgshapiro** Returns: 638132946Sgshapiro** none. 639132946Sgshapiro** 640132946Sgshapiro** Side Effects: 641132946Sgshapiro** inserts the field on the list of headers for this envelope. 642168520Sgshapiro** 643168520Sgshapiro** Notes: 644168520Sgshapiro** - field and value must be in internal format, i.e., 645168520Sgshapiro** metacharacters must be "quoted", see quote_internal_chars(). 646168520Sgshapiro** - the header list contains headers that might not be 647168520Sgshapiro** sent "out" (see putheader(): "skip"), hence there is no 648168520Sgshapiro** reliable way to insert a header at an exact position 649168520Sgshapiro** (except at the front or end). 650132946Sgshapiro*/ 651132946Sgshapiro 652132946Sgshapirovoid 653168520Sgshapiroinsheader(idx, field, value, flags, e, space) 654132946Sgshapiro int idx; 655132946Sgshapiro char *field; 656132946Sgshapiro char *value; 657132946Sgshapiro int flags; 658132946Sgshapiro ENVELOPE *e; 659168520Sgshapiro bool space; 660132946Sgshapiro{ 661132946Sgshapiro HDR *h, *srch, *last = NULL; 662132946Sgshapiro 663132946Sgshapiro /* allocate space for new header */ 664168520Sgshapiro h = allocheader(field, value, flags, e->e_rpool, space); 665132946Sgshapiro 666132946Sgshapiro /* find insertion position */ 667132946Sgshapiro for (srch = e->e_header; srch != NULL && idx > 0; 668132946Sgshapiro srch = srch->h_link, idx--) 669132946Sgshapiro last = srch; 670132946Sgshapiro 671132946Sgshapiro if (e->e_header == NULL) 672132946Sgshapiro { 673132946Sgshapiro e->e_header = h; 674132946Sgshapiro h->h_link = NULL; 675132946Sgshapiro } 676132946Sgshapiro else if (srch == NULL) 677132946Sgshapiro { 678132946Sgshapiro SM_ASSERT(last != NULL); 679132946Sgshapiro last->h_link = h; 680132946Sgshapiro h->h_link = NULL; 681132946Sgshapiro } 682132946Sgshapiro else 683132946Sgshapiro { 684132946Sgshapiro h->h_link = srch->h_link; 685132946Sgshapiro srch->h_link = h; 686132946Sgshapiro } 687132946Sgshapiro} 688168520Sgshapiro 689132946Sgshapiro/* 69038032Speter** HVALUE -- return value of a header. 69138032Speter** 69238032Speter** Only "real" fields (i.e., ones that have not been supplied 69338032Speter** as a default) are used. 69438032Speter** 69538032Speter** Parameters: 69638032Speter** field -- the field name. 69738032Speter** header -- the header list. 69838032Speter** 69938032Speter** Returns: 700168520Sgshapiro** pointer to the value part (internal format). 70138032Speter** NULL if not found. 70238032Speter** 70338032Speter** Side Effects: 70438032Speter** none. 70538032Speter*/ 70638032Speter 70738032Speterchar * 70838032Speterhvalue(field, header) 70938032Speter char *field; 71038032Speter HDR *header; 71138032Speter{ 71238032Speter register HDR *h; 71338032Speter 71438032Speter for (h = header; h != NULL; h = h->h_link) 71538032Speter { 71638032Speter if (!bitset(H_DEFAULT, h->h_flags) && 71790795Sgshapiro sm_strcasecmp(h->h_field, field) == 0) 718203004Sgshapiro { 719203004Sgshapiro char *s; 720203004Sgshapiro 721203004Sgshapiro s = h->h_value; 722203004Sgshapiro if (s == NULL) 723203004Sgshapiro return NULL; 724203004Sgshapiro while (isascii(*s) && isspace(*s)) 725203004Sgshapiro s++; 726203004Sgshapiro return s; 727203004Sgshapiro } 72838032Speter } 72964565Sgshapiro return NULL; 73038032Speter} 731168520Sgshapiro 73290795Sgshapiro/* 73338032Speter** ISHEADER -- predicate telling if argument is a header. 73438032Speter** 73538032Speter** A line is a header if it has a single word followed by 73638032Speter** optional white space followed by a colon. 73738032Speter** 73838032Speter** Header fields beginning with two dashes, although technically 73938032Speter** permitted by RFC822, are automatically rejected in order 74038032Speter** to make MIME work out. Without this we could have a technically 74138032Speter** legal header such as ``--"foo:bar"'' that would also be a legal 74238032Speter** MIME separator. 74338032Speter** 74438032Speter** Parameters: 74538032Speter** h -- string to check for possible headerness. 74638032Speter** 74738032Speter** Returns: 74890795Sgshapiro** true if h is a header. 74990795Sgshapiro** false otherwise. 75038032Speter** 75138032Speter** Side Effects: 75238032Speter** none. 75338032Speter*/ 75438032Speter 75538032Speterbool 75638032Speterisheader(h) 75738032Speter char *h; 75838032Speter{ 759168520Sgshapiro char *s; 76038032Speter 761168520Sgshapiro s = h; 76238032Speter if (s[0] == '-' && s[1] == '-') 76390795Sgshapiro return false; 76438032Speter 76538032Speter while (*s > ' ' && *s != ':' && *s != '\0') 76638032Speter s++; 76738032Speter 76838032Speter if (h == s) 76990795Sgshapiro return false; 77038032Speter 77138032Speter /* following technically violates RFC822 */ 77238032Speter while (isascii(*s) && isspace(*s)) 77338032Speter s++; 77438032Speter 77538032Speter return (*s == ':'); 77638032Speter} 777168520Sgshapiro 77890795Sgshapiro/* 77938032Speter** EATHEADER -- run through the stored header and extract info. 78038032Speter** 78138032Speter** Parameters: 78238032Speter** e -- the envelope to process. 78338032Speter** full -- if set, do full processing (e.g., compute 78438032Speter** message priority). This should not be set 78538032Speter** when reading a queue file because some info 78638032Speter** needed to compute the priority is wrong. 78790795Sgshapiro** log -- call logsender()? 78838032Speter** 78938032Speter** Returns: 79038032Speter** none. 79138032Speter** 79238032Speter** Side Effects: 79338032Speter** Sets a bunch of global variables from information 79438032Speter** in the collected header. 79538032Speter*/ 79638032Speter 79738032Spetervoid 79890795Sgshapiroeatheader(e, full, log) 79938032Speter register ENVELOPE *e; 80038032Speter bool full; 80190795Sgshapiro bool log; 80238032Speter{ 80338032Speter register HDR *h; 80438032Speter register char *p; 80538032Speter int hopcnt = 0; 80638032Speter char buf[MAXLINE]; 80738032Speter 80838032Speter /* 80938032Speter ** Set up macros for possible expansion in headers. 81038032Speter */ 81138032Speter 81290795Sgshapiro macdefine(&e->e_macro, A_PERM, 'f', e->e_sender); 81390795Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', e->e_sender); 81438032Speter if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0') 81590795Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', e->e_origrcpt); 81638032Speter else 81790795Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', NULL); 81838032Speter 81938032Speter /* full name of from person */ 82038032Speter p = hvalue("full-name", e->e_header); 82138032Speter if (p != NULL) 82238032Speter { 82338032Speter if (!rfc822_string(p)) 82438032Speter { 82538032Speter /* 82638032Speter ** Quote a full name with special characters 82738032Speter ** as a comment so crackaddr() doesn't destroy 82838032Speter ** the name portion of the address. 82938032Speter */ 83090795Sgshapiro 83190795Sgshapiro p = addquotes(p, e->e_rpool); 83238032Speter } 83390795Sgshapiro macdefine(&e->e_macro, A_PERM, 'x', p); 83438032Speter } 83538032Speter 83638032Speter if (tTd(32, 1)) 83790795Sgshapiro sm_dprintf("----- collected header -----\n"); 83890795Sgshapiro e->e_msgid = NULL; 83938032Speter for (h = e->e_header; h != NULL; h = h->h_link) 84038032Speter { 84138032Speter if (tTd(32, 1)) 842168520Sgshapiro sm_dprintf("%s:", h->h_field); 84338032Speter if (h->h_value == NULL) 84438032Speter { 84538032Speter if (tTd(32, 1)) 84690795Sgshapiro sm_dprintf("<NULL>\n"); 84738032Speter continue; 84838032Speter } 84938032Speter 85038032Speter /* do early binding */ 85164565Sgshapiro if (bitset(H_DEFAULT, h->h_flags) && 85264565Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 85338032Speter { 85438032Speter if (tTd(32, 1)) 85538032Speter { 85690795Sgshapiro sm_dprintf("("); 857132946Sgshapiro xputs(sm_debug_file(), h->h_value); 85890795Sgshapiro sm_dprintf(") "); 85938032Speter } 860168520Sgshapiro expand(h->h_value, buf, sizeof(buf), e); 861168520Sgshapiro if (buf[0] != '\0' && 862168520Sgshapiro (buf[0] != ' ' || buf[1] != '\0')) 86338032Speter { 86438032Speter if (bitset(H_FROM, h->h_flags)) 865111826Sgshapiro expand(crackaddr(buf, e), 866168520Sgshapiro buf, sizeof(buf), e); 86790795Sgshapiro h->h_value = sm_rpool_strdup_x(e->e_rpool, buf); 86838032Speter h->h_flags &= ~H_DEFAULT; 86938032Speter } 87038032Speter } 87138032Speter if (tTd(32, 1)) 87238032Speter { 873132946Sgshapiro xputs(sm_debug_file(), h->h_value); 87490795Sgshapiro sm_dprintf("\n"); 87538032Speter } 87638032Speter 87738032Speter /* count the number of times it has been processed */ 87838032Speter if (bitset(H_TRACE, h->h_flags)) 87938032Speter hopcnt++; 88038032Speter 88138032Speter /* send to this person if we so desire */ 88238032Speter if (GrabTo && bitset(H_RCPT, h->h_flags) && 88338032Speter !bitset(H_DEFAULT, h->h_flags) && 88490795Sgshapiro (!bitset(EF_RESENT, e->e_flags) || 88590795Sgshapiro bitset(H_RESENT, h->h_flags))) 88638032Speter { 88738032Speter#if 0 88838032Speter int saveflags = e->e_flags; 88964565Sgshapiro#endif /* 0 */ 89038032Speter 89190795Sgshapiro (void) sendtolist(denlstring(h->h_value, true, false), 89290795Sgshapiro NULLADDR, &e->e_sendqueue, 0, e); 89338032Speter 89438032Speter#if 0 89538032Speter /* 89642580Speter ** Change functionality so a fatal error on an 89742580Speter ** address doesn't affect the entire envelope. 89838032Speter */ 89964565Sgshapiro 90038032Speter /* delete fatal errors generated by this address */ 90138032Speter if (!bitset(EF_FATALERRS, saveflags)) 90238032Speter e->e_flags &= ~EF_FATALERRS; 90364565Sgshapiro#endif /* 0 */ 90438032Speter } 90538032Speter 90638032Speter /* save the message-id for logging */ 90738032Speter p = "resent-message-id"; 90838032Speter if (!bitset(EF_RESENT, e->e_flags)) 90938032Speter p += 7; 91090795Sgshapiro if (sm_strcasecmp(h->h_field, p) == 0) 91138032Speter { 91290795Sgshapiro e->e_msgid = h->h_value; 91390795Sgshapiro while (isascii(*e->e_msgid) && isspace(*e->e_msgid)) 91490795Sgshapiro e->e_msgid++; 915125823Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{msg_id}"), 916132946Sgshapiro e->e_msgid); 91738032Speter } 91838032Speter } 91938032Speter if (tTd(32, 1)) 92090795Sgshapiro sm_dprintf("----------------------------\n"); 92138032Speter 92238032Speter /* if we are just verifying (that is, sendmail -t -bv), drop out now */ 92338032Speter if (OpMode == MD_VERIFY) 92438032Speter return; 92538032Speter 92638032Speter /* store hop count */ 92738032Speter if (hopcnt > e->e_hopcount) 92890795Sgshapiro { 92938032Speter e->e_hopcount = hopcnt; 930168520Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%d", e->e_hopcount); 93190795Sgshapiro macdefine(&e->e_macro, A_TEMP, 'c', buf); 93290795Sgshapiro } 93338032Speter 93438032Speter /* message priority */ 93538032Speter p = hvalue("precedence", e->e_header); 93638032Speter if (p != NULL) 93738032Speter e->e_class = priencode(p); 93838032Speter if (e->e_class < 0) 93938032Speter e->e_timeoutclass = TOC_NONURGENT; 94038032Speter else if (e->e_class > 0) 94138032Speter e->e_timeoutclass = TOC_URGENT; 94238032Speter if (full) 94338032Speter { 94438032Speter e->e_msgpriority = e->e_msgsize 94538032Speter - e->e_class * WkClassFact 94638032Speter + e->e_nrcpts * WkRecipFact; 94738032Speter } 94838032Speter 949132946Sgshapiro /* check for DSN to properly set e_timeoutclass */ 950132946Sgshapiro p = hvalue("content-type", e->e_header); 951132946Sgshapiro if (p != NULL) 952132946Sgshapiro { 953132946Sgshapiro bool oldsupr; 954132946Sgshapiro char **pvp; 955132946Sgshapiro char pvpbuf[MAXLINE]; 956132946Sgshapiro extern unsigned char MimeTokenTab[256]; 957132946Sgshapiro 958132946Sgshapiro /* tokenize header */ 959132946Sgshapiro oldsupr = SuprErrs; 960132946Sgshapiro SuprErrs = true; 961168520Sgshapiro pvp = prescan(p, '\0', pvpbuf, sizeof(pvpbuf), NULL, 962132946Sgshapiro MimeTokenTab, false); 963132946Sgshapiro SuprErrs = oldsupr; 964132946Sgshapiro 965132946Sgshapiro /* Check if multipart/report */ 966132946Sgshapiro if (pvp != NULL && pvp[0] != NULL && 967132946Sgshapiro pvp[1] != NULL && pvp[2] != NULL && 968132946Sgshapiro sm_strcasecmp(*pvp++, "multipart") == 0 && 969132946Sgshapiro strcmp(*pvp++, "/") == 0 && 970132946Sgshapiro sm_strcasecmp(*pvp++, "report") == 0) 971132946Sgshapiro { 972132946Sgshapiro /* Look for report-type=delivery-status */ 973132946Sgshapiro while (*pvp != NULL) 974132946Sgshapiro { 975132946Sgshapiro /* skip to semicolon separator */ 976132946Sgshapiro while (*pvp != NULL && strcmp(*pvp, ";") != 0) 977132946Sgshapiro pvp++; 978132946Sgshapiro 979132946Sgshapiro /* skip semicolon */ 980132946Sgshapiro if (*pvp++ == NULL || *pvp == NULL) 981132946Sgshapiro break; 982132946Sgshapiro 983132946Sgshapiro /* look for report-type */ 984132946Sgshapiro if (sm_strcasecmp(*pvp++, "report-type") != 0) 985132946Sgshapiro continue; 986132946Sgshapiro 987132946Sgshapiro /* skip equal */ 988132946Sgshapiro if (*pvp == NULL || strcmp(*pvp, "=") != 0) 989132946Sgshapiro continue; 990132946Sgshapiro 991132946Sgshapiro /* check value */ 992132946Sgshapiro if (*++pvp != NULL && 993132946Sgshapiro sm_strcasecmp(*pvp, 994132946Sgshapiro "delivery-status") == 0) 995132946Sgshapiro e->e_timeoutclass = TOC_DSN; 996132946Sgshapiro 997132946Sgshapiro /* found report-type, no need to continue */ 998132946Sgshapiro break; 999132946Sgshapiro } 1000132946Sgshapiro } 1001132946Sgshapiro } 1002132946Sgshapiro 100338032Speter /* message timeout priority */ 100438032Speter p = hvalue("priority", e->e_header); 100538032Speter if (p != NULL) 100638032Speter { 100738032Speter /* (this should be in the configuration file) */ 100890795Sgshapiro if (sm_strcasecmp(p, "urgent") == 0) 100938032Speter e->e_timeoutclass = TOC_URGENT; 101090795Sgshapiro else if (sm_strcasecmp(p, "normal") == 0) 101138032Speter e->e_timeoutclass = TOC_NORMAL; 101290795Sgshapiro else if (sm_strcasecmp(p, "non-urgent") == 0) 101338032Speter e->e_timeoutclass = TOC_NONURGENT; 1014125823Sgshapiro else if (bitset(EF_RESPONSE, e->e_flags)) 1015125823Sgshapiro e->e_timeoutclass = TOC_DSN; 101638032Speter } 1017125823Sgshapiro else if (bitset(EF_RESPONSE, e->e_flags)) 1018112813Sgshapiro e->e_timeoutclass = TOC_DSN; 1019112813Sgshapiro 102038032Speter /* date message originated */ 102138032Speter p = hvalue("posted-date", e->e_header); 102238032Speter if (p == NULL) 102338032Speter p = hvalue("date", e->e_header); 102438032Speter if (p != NULL) 102590795Sgshapiro macdefine(&e->e_macro, A_PERM, 'a', p); 102638032Speter 102738032Speter /* check to see if this is a MIME message */ 102838032Speter if ((e->e_bodytype != NULL && 102990795Sgshapiro sm_strcasecmp(e->e_bodytype, "8BITMIME") == 0) || 103038032Speter hvalue("MIME-Version", e->e_header) != NULL) 103138032Speter { 103238032Speter e->e_flags |= EF_IS_MIME; 103338032Speter if (HasEightBits) 103438032Speter e->e_bodytype = "8BITMIME"; 103538032Speter } 103638032Speter else if ((p = hvalue("Content-Type", e->e_header)) != NULL) 103738032Speter { 103838032Speter /* this may be an RFC 1049 message */ 103938032Speter p = strpbrk(p, ";/"); 104038032Speter if (p == NULL || *p == ';') 104138032Speter { 104238032Speter /* yep, it is */ 104338032Speter e->e_flags |= EF_DONT_MIME; 104438032Speter } 104538032Speter } 104638032Speter 104738032Speter /* 104838032Speter ** From person in antiquated ARPANET mode 104938032Speter ** required by UK Grey Book e-mail gateways (sigh) 105038032Speter */ 105138032Speter 105238032Speter if (OpMode == MD_ARPAFTP) 105338032Speter { 105438032Speter register struct hdrinfo *hi; 105538032Speter 105638032Speter for (hi = HdrInfo; hi->hi_field != NULL; hi++) 105738032Speter { 105838032Speter if (bitset(H_FROM, hi->hi_flags) && 105938032Speter (!bitset(H_RESENT, hi->hi_flags) || 106038032Speter bitset(EF_RESENT, e->e_flags)) && 106138032Speter (p = hvalue(hi->hi_field, e->e_header)) != NULL) 106238032Speter break; 106338032Speter } 106438032Speter if (hi->hi_field != NULL) 106538032Speter { 106638032Speter if (tTd(32, 2)) 106790795Sgshapiro sm_dprintf("eatheader: setsender(*%s == %s)\n", 106838032Speter hi->hi_field, p); 106990795Sgshapiro setsender(p, e, NULL, '\0', true); 107038032Speter } 107138032Speter } 107238032Speter 107338032Speter /* 107438032Speter ** Log collection information. 107538032Speter */ 107638032Speter 1077203004Sgshapiro if (tTd(92, 2)) 1078203004Sgshapiro sm_dprintf("eatheader: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d, log=%d\n", 1079203004Sgshapiro e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel, 1080203004Sgshapiro log); 108190795Sgshapiro if (log && bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4) 108290795Sgshapiro { 108390795Sgshapiro logsender(e, e->e_msgid); 108490795Sgshapiro e->e_flags &= ~EF_LOGSENDER; 108590795Sgshapiro } 108638032Speter} 1087168520Sgshapiro 108890795Sgshapiro/* 108938032Speter** LOGSENDER -- log sender information 109038032Speter** 109138032Speter** Parameters: 109238032Speter** e -- the envelope to log 109338032Speter** msgid -- the message id 109438032Speter** 109538032Speter** Returns: 109638032Speter** none 109738032Speter*/ 109838032Speter 109938032Spetervoid 110038032Speterlogsender(e, msgid) 110138032Speter register ENVELOPE *e; 110238032Speter char *msgid; 110338032Speter{ 110438032Speter char *name; 110538032Speter register char *sbp; 110638032Speter register char *p; 110738032Speter char hbuf[MAXNAME + 1]; 110838032Speter char sbuf[MAXLINE + 1]; 110938032Speter char mbuf[MAXNAME + 1]; 111038032Speter 111138032Speter /* don't allow newlines in the message-id */ 111290795Sgshapiro /* XXX do we still need this? sm_syslog() replaces control chars */ 111338032Speter if (msgid != NULL) 111438032Speter { 1115157006Sgshapiro size_t l; 1116157006Sgshapiro 111738032Speter l = strlen(msgid); 1118168520Sgshapiro if (l > sizeof(mbuf) - 1) 1119168520Sgshapiro l = sizeof(mbuf) - 1; 112064565Sgshapiro memmove(mbuf, msgid, l); 112138032Speter mbuf[l] = '\0'; 112238032Speter p = mbuf; 112338032Speter while ((p = strchr(p, '\n')) != NULL) 112438032Speter *p++ = ' '; 112538032Speter } 112638032Speter 112738032Speter if (bitset(EF_RESPONSE, e->e_flags)) 112838032Speter name = "[RESPONSE]"; 112938032Speter else if ((name = macvalue('_', e)) != NULL) 113064565Sgshapiro /* EMPTY */ 113138032Speter ; 113238032Speter else if (RealHostName == NULL) 113338032Speter name = "localhost"; 113438032Speter else if (RealHostName[0] == '[') 113538032Speter name = RealHostName; 113638032Speter else 113738032Speter { 113838032Speter name = hbuf; 1139168520Sgshapiro (void) sm_snprintf(hbuf, sizeof(hbuf), "%.80s", RealHostName); 114038032Speter if (RealHostAddr.sa.sa_family != 0) 114138032Speter { 114238032Speter p = &hbuf[strlen(hbuf)]; 114390795Sgshapiro (void) sm_snprintf(p, SPACELEFT(hbuf, p), 114490795Sgshapiro " (%.100s)", 114590795Sgshapiro anynet_ntoa(&RealHostAddr)); 114638032Speter } 114738032Speter } 114838032Speter 114938032Speter /* some versions of syslog only take 5 printf args */ 115064565Sgshapiro#if (SYSLOG_BUFSIZE) >= 256 115138032Speter sbp = sbuf; 115290795Sgshapiro (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), 115390795Sgshapiro "from=%.200s, size=%ld, class=%d, nrcpts=%d", 115490795Sgshapiro e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr, 1155244833Sgshapiro PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts); 115638032Speter sbp += strlen(sbp); 115738032Speter if (msgid != NULL) 115838032Speter { 115990795Sgshapiro (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), 116090795Sgshapiro ", msgid=%.100s", mbuf); 116138032Speter sbp += strlen(sbp); 116238032Speter } 116338032Speter if (e->e_bodytype != NULL) 116438032Speter { 116590795Sgshapiro (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), 116690795Sgshapiro ", bodytype=%.20s", e->e_bodytype); 116738032Speter sbp += strlen(sbp); 116838032Speter } 116938032Speter p = macvalue('r', e); 117038032Speter if (p != NULL) 117164565Sgshapiro { 117290795Sgshapiro (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), 117390795Sgshapiro ", proto=%.20s", p); 117464565Sgshapiro sbp += strlen(sbp); 117564565Sgshapiro } 117690795Sgshapiro p = macvalue(macid("{daemon_name}"), e); 117764565Sgshapiro if (p != NULL) 117864565Sgshapiro { 117990795Sgshapiro (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), 118090795Sgshapiro ", daemon=%.20s", p); 118164565Sgshapiro sbp += strlen(sbp); 118264565Sgshapiro } 1183110563Sgshapiro sm_syslog(LOG_INFO, e->e_id, "%.850s, relay=%s", sbuf, name); 118438032Speter 118564565Sgshapiro#else /* (SYSLOG_BUFSIZE) >= 256 */ 118638032Speter 118738032Speter sm_syslog(LOG_INFO, e->e_id, 118864565Sgshapiro "from=%s", 118964565Sgshapiro e->e_from.q_paddr == NULL ? "<NONE>" 119090795Sgshapiro : shortenstring(e->e_from.q_paddr, 119190795Sgshapiro 83)); 119238032Speter sm_syslog(LOG_INFO, e->e_id, 119364565Sgshapiro "size=%ld, class=%ld, nrcpts=%d", 1194244833Sgshapiro PRT_NONNEGL(e->e_msgsize), e->e_class, e->e_nrcpts); 119538032Speter if (msgid != NULL) 119638032Speter sm_syslog(LOG_INFO, e->e_id, 119764565Sgshapiro "msgid=%s", 119864565Sgshapiro shortenstring(mbuf, 83)); 119938032Speter sbp = sbuf; 120038032Speter *sbp = '\0'; 120138032Speter if (e->e_bodytype != NULL) 120238032Speter { 120390795Sgshapiro (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), 120490795Sgshapiro "bodytype=%.20s, ", e->e_bodytype); 120538032Speter sbp += strlen(sbp); 120638032Speter } 120738032Speter p = macvalue('r', e); 120838032Speter if (p != NULL) 120938032Speter { 121090795Sgshapiro (void) sm_snprintf(sbp, SPACELEFT(sbuf, sbp), 121190795Sgshapiro "proto=%.20s, ", p); 121238032Speter sbp += strlen(sbp); 121338032Speter } 121438032Speter sm_syslog(LOG_INFO, e->e_id, 1215110563Sgshapiro "%.400srelay=%s", sbuf, name); 121664565Sgshapiro#endif /* (SYSLOG_BUFSIZE) >= 256 */ 121738032Speter} 1218168520Sgshapiro 121990795Sgshapiro/* 122038032Speter** PRIENCODE -- encode external priority names into internal values. 122138032Speter** 122238032Speter** Parameters: 122338032Speter** p -- priority in ascii. 122438032Speter** 122538032Speter** Returns: 122638032Speter** priority as a numeric level. 122738032Speter** 122838032Speter** Side Effects: 122938032Speter** none. 123038032Speter*/ 123138032Speter 123264565Sgshapirostatic int 123338032Speterpriencode(p) 123438032Speter char *p; 123538032Speter{ 123638032Speter register int i; 123738032Speter 123838032Speter for (i = 0; i < NumPriorities; i++) 123938032Speter { 124090795Sgshapiro if (sm_strcasecmp(p, Priorities[i].pri_name) == 0) 124164565Sgshapiro return Priorities[i].pri_val; 124238032Speter } 124338032Speter 124438032Speter /* unknown priority */ 124564565Sgshapiro return 0; 124638032Speter} 1247168520Sgshapiro 124890795Sgshapiro/* 124938032Speter** CRACKADDR -- parse an address and turn it into a macro 125038032Speter** 125138032Speter** This doesn't actually parse the address -- it just extracts 125238032Speter** it and replaces it with "$g". The parse is totally ad hoc 125338032Speter** and isn't even guaranteed to leave something syntactically 125438032Speter** identical to what it started with. However, it does leave 1255111826Sgshapiro** something semantically identical if possible, else at least 1256111826Sgshapiro** syntactically correct. 125738032Speter** 1258111826Sgshapiro** For example, it changes "Real Name <real@example.com> (Comment)" 1259111826Sgshapiro** to "Real Name <$g> (Comment)". 1260111826Sgshapiro** 126138032Speter** This algorithm has been cleaned up to handle a wider range 126238032Speter** of cases -- notably quoted and backslash escaped strings. 126338032Speter** This modification makes it substantially better at preserving 126438032Speter** the original syntax. 126538032Speter** 126638032Speter** Parameters: 126738032Speter** addr -- the address to be cracked. 1268111826Sgshapiro** e -- the current envelope. 126938032Speter** 127038032Speter** Returns: 127138032Speter** a pointer to the new version. 127238032Speter** 127338032Speter** Side Effects: 127438032Speter** none. 127538032Speter** 127638032Speter** Warning: 127738032Speter** The return value is saved in local storage and should 127838032Speter** be copied if it is to be reused. 127938032Speter*/ 128038032Speter 1281111826Sgshapiro#define SM_HAVE_ROOM ((bp < buflim) && (buflim <= bufend)) 1282111826Sgshapiro 1283111826Sgshapiro/* 1284111826Sgshapiro** Append a character to bp if we have room. 1285111826Sgshapiro** If not, punt and return $g. 1286111826Sgshapiro*/ 1287111826Sgshapiro 1288111826Sgshapiro#define SM_APPEND_CHAR(c) \ 1289111826Sgshapiro do \ 1290111826Sgshapiro { \ 1291111826Sgshapiro if (SM_HAVE_ROOM) \ 1292111826Sgshapiro *bp++ = (c); \ 1293111826Sgshapiro else \ 1294111826Sgshapiro goto returng; \ 1295111826Sgshapiro } while (0) 1296111826Sgshapiro 1297111826Sgshapiro#if MAXNAME < 10 1298111826SgshapiroERROR MAXNAME must be at least 10 1299111826Sgshapiro#endif /* MAXNAME < 10 */ 1300111826Sgshapiro 130138032Speterchar * 1302111826Sgshapirocrackaddr(addr, e) 130338032Speter register char *addr; 1304111826Sgshapiro ENVELOPE *e; 130538032Speter{ 130638032Speter register char *p; 130738032Speter register char c; 1308111826Sgshapiro int cmtlev; /* comment level in input string */ 1309111826Sgshapiro int realcmtlev; /* comment level in output string */ 1310111826Sgshapiro int anglelev; /* angle level in input string */ 1311111826Sgshapiro int copylev; /* 0 == in address, >0 copying */ 1312111826Sgshapiro int bracklev; /* bracket level for IPv6 addr check */ 1313111826Sgshapiro bool addangle; /* put closing angle in output */ 1314111826Sgshapiro bool qmode; /* quoting in original string? */ 1315111826Sgshapiro bool realqmode; /* quoting in output string? */ 1316111826Sgshapiro bool putgmac = false; /* already wrote $g */ 1317111826Sgshapiro bool quoteit = false; /* need to quote next character */ 1318111826Sgshapiro bool gotangle = false; /* found first '<' */ 1319111826Sgshapiro bool gotcolon = false; /* found a ':' */ 132038032Speter register char *bp; 132138032Speter char *buflim; 132238032Speter char *bufhead; 132338032Speter char *addrhead; 1324111826Sgshapiro char *bufend; 132538032Speter static char buf[MAXNAME + 1]; 132638032Speter 132738032Speter if (tTd(33, 1)) 132890795Sgshapiro sm_dprintf("crackaddr(%s)\n", addr); 132938032Speter 1330168520Sgshapiro buflim = bufend = &buf[sizeof(buf) - 1]; 1331168520Sgshapiro bp = bufhead = buf; 1332168520Sgshapiro 1333168520Sgshapiro /* skip over leading spaces but preserve them */ 133438032Speter while (*addr != '\0' && isascii(*addr) && isspace(*addr)) 1335168520Sgshapiro { 1336168520Sgshapiro SM_APPEND_CHAR(*addr); 133738032Speter addr++; 1338168520Sgshapiro } 1339168520Sgshapiro bufhead = bp; 134038032Speter 134138032Speter /* 134238032Speter ** Start by assuming we have no angle brackets. This will be 134338032Speter ** adjusted later if we find them. 134438032Speter */ 134538032Speter 134638032Speter p = addrhead = addr; 1347111826Sgshapiro copylev = anglelev = cmtlev = realcmtlev = 0; 134838032Speter bracklev = 0; 1349111826Sgshapiro qmode = realqmode = addangle = false; 135038032Speter 135138032Speter while ((c = *p++) != '\0') 135238032Speter { 135338032Speter /* 1354111826Sgshapiro ** Try to keep legal syntax using spare buffer space 1355111826Sgshapiro ** (maintained by buflim). 135638032Speter */ 135738032Speter 1358111826Sgshapiro if (copylev > 0) 1359111826Sgshapiro SM_APPEND_CHAR(c); 136038032Speter 136138032Speter /* check for backslash escapes */ 136238032Speter if (c == '\\') 136338032Speter { 136438032Speter /* arrange to quote the address */ 136538032Speter if (cmtlev <= 0 && !qmode) 136690795Sgshapiro quoteit = true; 136738032Speter 136838032Speter if ((c = *p++) == '\0') 136938032Speter { 137038032Speter /* too far */ 137138032Speter p--; 137238032Speter goto putg; 137338032Speter } 1374111826Sgshapiro if (copylev > 0) 1375111826Sgshapiro SM_APPEND_CHAR(c); 137638032Speter goto putg; 137738032Speter } 137838032Speter 137938032Speter /* check for quoted strings */ 138038032Speter if (c == '"' && cmtlev <= 0) 138138032Speter { 138238032Speter qmode = !qmode; 1383111826Sgshapiro if (copylev > 0 && SM_HAVE_ROOM) 1384111826Sgshapiro { 1385111826Sgshapiro if (realqmode) 1386111826Sgshapiro buflim--; 1387111826Sgshapiro else 1388111826Sgshapiro buflim++; 138938032Speter realqmode = !realqmode; 1390111826Sgshapiro } 139138032Speter continue; 139238032Speter } 139338032Speter if (qmode) 139438032Speter goto putg; 139538032Speter 139638032Speter /* check for comments */ 139738032Speter if (c == '(') 139838032Speter { 139938032Speter cmtlev++; 140038032Speter 140138032Speter /* allow space for closing paren */ 1402111826Sgshapiro if (SM_HAVE_ROOM) 140338032Speter { 140438032Speter buflim--; 140538032Speter realcmtlev++; 140638032Speter if (copylev++ <= 0) 140738032Speter { 140838032Speter if (bp != bufhead) 1409111826Sgshapiro SM_APPEND_CHAR(' '); 1410111826Sgshapiro SM_APPEND_CHAR(c); 141138032Speter } 141238032Speter } 141338032Speter } 141438032Speter if (cmtlev > 0) 141538032Speter { 141638032Speter if (c == ')') 141738032Speter { 141838032Speter cmtlev--; 141938032Speter copylev--; 1420111826Sgshapiro if (SM_HAVE_ROOM) 142138032Speter { 142238032Speter realcmtlev--; 142338032Speter buflim++; 142438032Speter } 142538032Speter } 142638032Speter continue; 142738032Speter } 142838032Speter else if (c == ')') 142938032Speter { 143038032Speter /* syntax error: unmatched ) */ 1431120259Sgshapiro if (copylev > 0 && SM_HAVE_ROOM && bp > bufhead) 143238032Speter bp--; 143338032Speter } 143438032Speter 143538032Speter /* count nesting on [ ... ] (for IPv6 domain literals) */ 143638032Speter if (c == '[') 143738032Speter bracklev++; 143838032Speter else if (c == ']') 143938032Speter bracklev--; 144038032Speter 144138032Speter /* check for group: list; syntax */ 144238032Speter if (c == ':' && anglelev <= 0 && bracklev <= 0 && 144338032Speter !gotcolon && !ColonOkInAddr) 144438032Speter { 144538032Speter register char *q; 144638032Speter 144738032Speter /* 144838032Speter ** Check for DECnet phase IV ``::'' (host::user) 1449111826Sgshapiro ** or DECnet phase V ``:.'' syntaxes. The latter 145038032Speter ** covers ``user@DEC:.tay.myhost'' and 145138032Speter ** ``DEC:.tay.myhost::user'' syntaxes (bletch). 145238032Speter */ 145338032Speter 145438032Speter if (*p == ':' || *p == '.') 145538032Speter { 145638032Speter if (cmtlev <= 0 && !qmode) 145790795Sgshapiro quoteit = true; 1458111826Sgshapiro if (copylev > 0) 145938032Speter { 1460111826Sgshapiro SM_APPEND_CHAR(c); 1461111826Sgshapiro SM_APPEND_CHAR(*p); 146238032Speter } 146338032Speter p++; 146438032Speter goto putg; 146538032Speter } 146638032Speter 146790795Sgshapiro gotcolon = true; 146838032Speter 146938032Speter bp = bufhead; 147038032Speter if (quoteit) 147138032Speter { 1472111826Sgshapiro SM_APPEND_CHAR('"'); 147338032Speter 147438032Speter /* back up over the ':' and any spaces */ 147538032Speter --p; 1476111826Sgshapiro while (p > addr && 1477111826Sgshapiro isascii(*--p) && isspace(*p)) 147838032Speter continue; 147938032Speter p++; 148038032Speter } 148138032Speter for (q = addrhead; q < p; ) 148238032Speter { 148338032Speter c = *q++; 1484111826Sgshapiro if (quoteit && c == '"') 1485111826Sgshapiro SM_APPEND_CHAR('\\'); 1486132946Sgshapiro SM_APPEND_CHAR(c); 148738032Speter } 148838032Speter if (quoteit) 148938032Speter { 149038032Speter if (bp == &bufhead[1]) 149138032Speter bp--; 149238032Speter else 1493111826Sgshapiro SM_APPEND_CHAR('"'); 149438032Speter while ((c = *p++) != ':') 1495111826Sgshapiro SM_APPEND_CHAR(c); 1496111826Sgshapiro SM_APPEND_CHAR(c); 149738032Speter } 149838032Speter 149938032Speter /* any trailing white space is part of group: */ 1500111826Sgshapiro while (isascii(*p) && isspace(*p)) 1501111826Sgshapiro { 1502111826Sgshapiro SM_APPEND_CHAR(*p); 1503111826Sgshapiro p++; 1504111826Sgshapiro } 150538032Speter copylev = 0; 150690795Sgshapiro putgmac = quoteit = false; 150738032Speter bufhead = bp; 150838032Speter addrhead = p; 150938032Speter continue; 151038032Speter } 151138032Speter 151238032Speter if (c == ';' && copylev <= 0 && !ColonOkInAddr) 1513111826Sgshapiro SM_APPEND_CHAR(c); 151438032Speter 151538032Speter /* check for characters that may have to be quoted */ 151638032Speter if (strchr(MustQuoteChars, c) != NULL) 151738032Speter { 151838032Speter /* 151938032Speter ** If these occur as the phrase part of a <> 152038032Speter ** construct, but are not inside of () or already 152138032Speter ** quoted, they will have to be quoted. Note that 152238032Speter ** now (but don't actually do the quoting). 152338032Speter */ 152438032Speter 152538032Speter if (cmtlev <= 0 && !qmode) 152690795Sgshapiro quoteit = true; 152738032Speter } 152838032Speter 152938032Speter /* check for angle brackets */ 153038032Speter if (c == '<') 153138032Speter { 153238032Speter register char *q; 153338032Speter 153438032Speter /* assume first of two angles is bogus */ 153538032Speter if (gotangle) 153690795Sgshapiro quoteit = true; 153790795Sgshapiro gotangle = true; 153838032Speter 153938032Speter /* oops -- have to change our mind */ 154038032Speter anglelev = 1; 1541111826Sgshapiro if (SM_HAVE_ROOM) 1542111826Sgshapiro { 1543111826Sgshapiro if (!addangle) 1544111826Sgshapiro buflim--; 1545111826Sgshapiro addangle = true; 1546111826Sgshapiro } 154738032Speter 154838032Speter bp = bufhead; 154938032Speter if (quoteit) 155038032Speter { 1551111826Sgshapiro SM_APPEND_CHAR('"'); 155238032Speter 155338032Speter /* back up over the '<' and any spaces */ 155438032Speter --p; 1555111826Sgshapiro while (p > addr && 1556111826Sgshapiro isascii(*--p) && isspace(*p)) 155738032Speter continue; 155838032Speter p++; 155938032Speter } 156038032Speter for (q = addrhead; q < p; ) 156138032Speter { 156238032Speter c = *q++; 1563111826Sgshapiro if (quoteit && c == '"') 156438032Speter { 1565111826Sgshapiro SM_APPEND_CHAR('\\'); 1566111826Sgshapiro SM_APPEND_CHAR(c); 156738032Speter } 1568111826Sgshapiro else 1569111826Sgshapiro SM_APPEND_CHAR(c); 157038032Speter } 157138032Speter if (quoteit) 157238032Speter { 157338032Speter if (bp == &buf[1]) 157438032Speter bp--; 157538032Speter else 1576111826Sgshapiro SM_APPEND_CHAR('"'); 157738032Speter while ((c = *p++) != '<') 1578111826Sgshapiro SM_APPEND_CHAR(c); 1579111826Sgshapiro SM_APPEND_CHAR(c); 158038032Speter } 158138032Speter copylev = 0; 158290795Sgshapiro putgmac = quoteit = false; 158338032Speter continue; 158438032Speter } 158538032Speter 158638032Speter if (c == '>') 158738032Speter { 158838032Speter if (anglelev > 0) 158938032Speter { 159038032Speter anglelev--; 1591111826Sgshapiro if (SM_HAVE_ROOM) 159238032Speter { 1593111826Sgshapiro if (addangle) 1594111826Sgshapiro buflim++; 1595111826Sgshapiro addangle = false; 159638032Speter } 159738032Speter } 1598111826Sgshapiro else if (SM_HAVE_ROOM) 159938032Speter { 160038032Speter /* syntax error: unmatched > */ 1601120259Sgshapiro if (copylev > 0 && bp > bufhead) 160238032Speter bp--; 160390795Sgshapiro quoteit = true; 160438032Speter continue; 160538032Speter } 160638032Speter if (copylev++ <= 0) 1607111826Sgshapiro SM_APPEND_CHAR(c); 160838032Speter continue; 160938032Speter } 161038032Speter 161138032Speter /* must be a real address character */ 161238032Speter putg: 161338032Speter if (copylev <= 0 && !putgmac) 161438032Speter { 1615111826Sgshapiro if (bp > buf && bp[-1] == ')') 1616111826Sgshapiro SM_APPEND_CHAR(' '); 1617111826Sgshapiro SM_APPEND_CHAR(MACROEXPAND); 1618111826Sgshapiro SM_APPEND_CHAR('g'); 161990795Sgshapiro putgmac = true; 162038032Speter } 162138032Speter } 162238032Speter 162338032Speter /* repair any syntactic damage */ 1624111826Sgshapiro if (realqmode && bp < bufend) 162538032Speter *bp++ = '"'; 1626111826Sgshapiro while (realcmtlev-- > 0 && bp < bufend) 162738032Speter *bp++ = ')'; 1628111826Sgshapiro if (addangle && bp < bufend) 162938032Speter *bp++ = '>'; 1630111826Sgshapiro *bp = '\0'; 1631111826Sgshapiro if (bp < bufend) 1632111826Sgshapiro goto success; 163338032Speter 1634111826Sgshapiro returng: 1635111826Sgshapiro /* String too long, punt */ 1636111826Sgshapiro buf[0] = '<'; 1637111826Sgshapiro buf[1] = MACROEXPAND; 1638111826Sgshapiro buf[2]= 'g'; 1639111826Sgshapiro buf[3] = '>'; 1640111826Sgshapiro buf[4]= '\0'; 1641111826Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 1642111826Sgshapiro "Dropped invalid comments from header address"); 1643111826Sgshapiro 1644111826Sgshapiro success: 164538032Speter if (tTd(33, 1)) 164638032Speter { 164790795Sgshapiro sm_dprintf("crackaddr=>`"); 1648132946Sgshapiro xputs(sm_debug_file(), buf); 164990795Sgshapiro sm_dprintf("'\n"); 165038032Speter } 165164565Sgshapiro return buf; 165238032Speter} 1653168520Sgshapiro 165490795Sgshapiro/* 165538032Speter** PUTHEADER -- put the header part of a message from the in-core copy 165638032Speter** 165738032Speter** Parameters: 165838032Speter** mci -- the connection information. 165964565Sgshapiro** hdr -- the header to put. 166038032Speter** e -- envelope to use. 166143733Speter** flags -- MIME conversion flags. 166238032Speter** 166338032Speter** Returns: 1664159613Sgshapiro** true iff header part was written successfully 166538032Speter** 166638032Speter** Side Effects: 166738032Speter** none. 166838032Speter*/ 166938032Speter 1670157006Sgshapirobool 167143733Speterputheader(mci, hdr, e, flags) 167238032Speter register MCI *mci; 167338032Speter HDR *hdr; 167438032Speter register ENVELOPE *e; 167543733Speter int flags; 167638032Speter{ 167738032Speter register HDR *h; 167890795Sgshapiro char buf[SM_MAX(MAXLINE,BUFSIZ)]; 167938032Speter char obuf[MAXLINE]; 168038032Speter 168138032Speter if (tTd(34, 1)) 168290795Sgshapiro sm_dprintf("--- putheader, mailer = %s ---\n", 168338032Speter mci->mci_mailer->m_name); 168438032Speter 168538032Speter /* 168638032Speter ** If we're in MIME mode, we're not really in the header of the 168738032Speter ** message, just the header of one of the parts of the body of 168838032Speter ** the message. Therefore MCIF_INHEADER should not be turned on. 168938032Speter */ 169038032Speter 169138032Speter if (!bitset(MCIF_INMIME, mci->mci_flags)) 169238032Speter mci->mci_flags |= MCIF_INHEADER; 169338032Speter 169438032Speter for (h = hdr; h != NULL; h = h->h_link) 169538032Speter { 169638032Speter register char *p = h->h_value; 169790795Sgshapiro char *q; 169838032Speter 169938032Speter if (tTd(34, 11)) 170038032Speter { 1701168520Sgshapiro sm_dprintf(" %s:", h->h_field); 1702132946Sgshapiro xputs(sm_debug_file(), p); 170338032Speter } 170438032Speter 170564565Sgshapiro /* Skip empty headers */ 170664565Sgshapiro if (h->h_value == NULL) 170764565Sgshapiro continue; 170864565Sgshapiro 170942580Speter /* heuristic shortening of MIME fields to avoid MUA overflows */ 171042580Speter if (MaxMimeFieldLength > 0 && 171142580Speter wordinclass(h->h_field, 171290795Sgshapiro macid("{checkMIMEFieldHeaders}"))) 171342580Speter { 171471348Sgshapiro size_t len; 171571348Sgshapiro 1716111826Sgshapiro len = fix_mime_header(h, e); 171771348Sgshapiro if (len > 0) 171842580Speter { 171942580Speter sm_syslog(LOG_ALERT, e->e_id, 172071348Sgshapiro "Truncated MIME %s header due to field size (length = %ld) (possible attack)", 172171348Sgshapiro h->h_field, (unsigned long) len); 172242580Speter if (tTd(34, 11)) 172390795Sgshapiro sm_dprintf(" truncated MIME %s header due to field size (length = %ld) (possible attack)\n", 172490795Sgshapiro h->h_field, 172590795Sgshapiro (unsigned long) len); 172642580Speter } 172742580Speter } 172842580Speter 172942580Speter if (MaxMimeHeaderLength > 0 && 173042580Speter wordinclass(h->h_field, 173190795Sgshapiro macid("{checkMIMETextHeaders}"))) 173242580Speter { 173371348Sgshapiro size_t len; 173471348Sgshapiro 173571348Sgshapiro len = strlen(h->h_value); 173671348Sgshapiro if (len > (size_t) MaxMimeHeaderLength) 173742580Speter { 173842580Speter h->h_value[MaxMimeHeaderLength - 1] = '\0'; 173942580Speter sm_syslog(LOG_ALERT, e->e_id, 174071348Sgshapiro "Truncated long MIME %s header (length = %ld) (possible attack)", 174171348Sgshapiro h->h_field, (unsigned long) len); 174242580Speter if (tTd(34, 11)) 174390795Sgshapiro sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n", 174490795Sgshapiro h->h_field, 174590795Sgshapiro (unsigned long) len); 174642580Speter } 174742580Speter } 174842580Speter 174942580Speter if (MaxMimeHeaderLength > 0 && 175042580Speter wordinclass(h->h_field, 175190795Sgshapiro macid("{checkMIMEHeaders}"))) 175242580Speter { 175371348Sgshapiro size_t len; 175471348Sgshapiro 175571348Sgshapiro len = strlen(h->h_value); 175671348Sgshapiro if (shorten_rfc822_string(h->h_value, 175771348Sgshapiro MaxMimeHeaderLength)) 175842580Speter { 1759111826Sgshapiro if (len < MaxMimeHeaderLength) 1760111826Sgshapiro { 1761111826Sgshapiro /* we only rebalanced a bogus header */ 1762111826Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 1763111826Sgshapiro "Fixed MIME %s header (possible attack)", 1764111826Sgshapiro h->h_field); 1765111826Sgshapiro if (tTd(34, 11)) 1766111826Sgshapiro sm_dprintf(" fixed MIME %s header (possible attack)\n", 1767111826Sgshapiro h->h_field); 1768111826Sgshapiro } 1769111826Sgshapiro else 1770111826Sgshapiro { 1771111826Sgshapiro /* we actually shortened header */ 1772111826Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 1773111826Sgshapiro "Truncated long MIME %s header (length = %ld) (possible attack)", 1774111826Sgshapiro h->h_field, 1775111826Sgshapiro (unsigned long) len); 1776111826Sgshapiro if (tTd(34, 11)) 1777111826Sgshapiro sm_dprintf(" truncated long MIME %s header (length = %ld) (possible attack)\n", 1778111826Sgshapiro h->h_field, 1779111826Sgshapiro (unsigned long) len); 1780111826Sgshapiro } 178142580Speter } 178242580Speter } 178342580Speter 178443733Speter /* 178543733Speter ** Suppress Content-Transfer-Encoding: if we are MIMEing 178643733Speter ** and we are potentially converting from 8 bit to 7 bit 178743733Speter ** MIME. If converting, add a new CTE header in 178843733Speter ** mime8to7(). 178943733Speter */ 179090795Sgshapiro 179138032Speter if (bitset(H_CTE, h->h_flags) && 179243733Speter bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, 179343733Speter mci->mci_flags) && 179443733Speter !bitset(M87F_NO8TO7, flags)) 179538032Speter { 179638032Speter if (tTd(34, 11)) 179790795Sgshapiro sm_dprintf(" (skipped (content-transfer-encoding))\n"); 179838032Speter continue; 179938032Speter } 180038032Speter 180138032Speter if (bitset(MCIF_INMIME, mci->mci_flags)) 180238032Speter { 180338032Speter if (tTd(34, 11)) 180490795Sgshapiro sm_dprintf("\n"); 1805157006Sgshapiro if (!put_vanilla_header(h, p, mci)) 1806157006Sgshapiro goto writeerr; 180738032Speter continue; 180838032Speter } 180938032Speter 181038032Speter if (bitset(H_CHECK|H_ACHECK, h->h_flags) && 181164565Sgshapiro !bitintersect(h->h_mflags, mci->mci_mailer->m_flags) && 181264565Sgshapiro (h->h_macro == '\0' || 181390795Sgshapiro (q = macvalue(bitidx(h->h_macro), e)) == NULL || 181490795Sgshapiro *q == '\0')) 181538032Speter { 181638032Speter if (tTd(34, 11)) 181790795Sgshapiro sm_dprintf(" (skipped)\n"); 181838032Speter continue; 181938032Speter } 182038032Speter 182138032Speter /* handle Resent-... headers specially */ 182238032Speter if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 182338032Speter { 182438032Speter if (tTd(34, 11)) 182590795Sgshapiro sm_dprintf(" (skipped (resent))\n"); 182638032Speter continue; 182738032Speter } 182838032Speter 182938032Speter /* suppress return receipts if requested */ 183038032Speter if (bitset(H_RECEIPTTO, h->h_flags) && 183138032Speter (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags))) 183238032Speter { 183338032Speter if (tTd(34, 11)) 183490795Sgshapiro sm_dprintf(" (skipped (receipt))\n"); 183538032Speter continue; 183638032Speter } 183738032Speter 183838032Speter /* macro expand value if generated internally */ 183964565Sgshapiro if (bitset(H_DEFAULT, h->h_flags) || 184064565Sgshapiro bitset(H_BINDLATE, h->h_flags)) 184138032Speter { 1842168520Sgshapiro expand(p, buf, sizeof(buf), e); 184338032Speter p = buf; 184438032Speter if (*p == '\0') 184538032Speter { 184638032Speter if (tTd(34, 11)) 184790795Sgshapiro sm_dprintf(" (skipped -- null value)\n"); 184838032Speter continue; 184938032Speter } 185038032Speter } 185138032Speter 185238032Speter if (bitset(H_BCC, h->h_flags)) 185338032Speter { 185438032Speter /* Bcc: field -- either truncate or delete */ 185538032Speter if (bitset(EF_DELETE_BCC, e->e_flags)) 185638032Speter { 185738032Speter if (tTd(34, 11)) 185890795Sgshapiro sm_dprintf(" (skipped -- bcc)\n"); 185938032Speter } 186038032Speter else 186138032Speter { 186238032Speter /* no other recipient headers: truncate value */ 1863168520Sgshapiro (void) sm_strlcpyn(obuf, sizeof(obuf), 2, 186490795Sgshapiro h->h_field, ":"); 1865157006Sgshapiro if (!putline(obuf, mci)) 1866157006Sgshapiro goto writeerr; 186738032Speter } 186838032Speter continue; 186938032Speter } 187038032Speter 187138032Speter if (tTd(34, 11)) 187290795Sgshapiro sm_dprintf("\n"); 187338032Speter 187438032Speter if (bitset(H_FROM|H_RCPT, h->h_flags)) 187538032Speter { 187638032Speter /* address field */ 187738032Speter bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 187838032Speter 187938032Speter if (bitset(H_FROM, h->h_flags)) 188090795Sgshapiro oldstyle = false; 1881173343Sgshapiro commaize(h, p, oldstyle, mci, e, 1882173343Sgshapiro PXLF_HEADER | PXLF_STRIPMQUOTE); 188338032Speter } 188438032Speter else 188538032Speter { 1886157006Sgshapiro if (!put_vanilla_header(h, p, mci)) 1887157006Sgshapiro goto writeerr; 188838032Speter } 188938032Speter } 189038032Speter 189138032Speter /* 189238032Speter ** If we are converting this to a MIME message, add the 189364565Sgshapiro ** MIME headers (but not in MIME mode!). 189438032Speter */ 189538032Speter 189638032Speter#if MIME8TO7 189738032Speter if (bitset(MM_MIME8BIT, MimeMode) && 189838032Speter bitset(EF_HAS8BIT, e->e_flags) && 189938032Speter !bitset(EF_DONT_MIME, e->e_flags) && 190038032Speter !bitnset(M_8BITS, mci->mci_mailer->m_flags) && 190164565Sgshapiro !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME, mci->mci_flags) && 190264565Sgshapiro hvalue("MIME-Version", e->e_header) == NULL) 190338032Speter { 1904157006Sgshapiro if (!putline("MIME-Version: 1.0", mci)) 1905157006Sgshapiro goto writeerr; 190638032Speter if (hvalue("Content-Type", e->e_header) == NULL) 190738032Speter { 1908168520Sgshapiro (void) sm_snprintf(obuf, sizeof(obuf), 190990795Sgshapiro "Content-Type: text/plain; charset=%s", 191090795Sgshapiro defcharset(e)); 1911157006Sgshapiro if (!putline(obuf, mci)) 1912157006Sgshapiro goto writeerr; 191338032Speter } 1914157006Sgshapiro if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL 1915157006Sgshapiro && !putline("Content-Transfer-Encoding: 8bit", mci)) 1916157006Sgshapiro goto writeerr; 191738032Speter } 191864565Sgshapiro#endif /* MIME8TO7 */ 1919157006Sgshapiro return true; 1920157006Sgshapiro 1921157006Sgshapiro writeerr: 1922157006Sgshapiro return false; 192338032Speter} 1924168520Sgshapiro 192590795Sgshapiro/* 192638032Speter** PUT_VANILLA_HEADER -- output a fairly ordinary header 192738032Speter** 192838032Speter** Parameters: 192938032Speter** h -- the structure describing this header 193038032Speter** v -- the value of this header 193138032Speter** mci -- the connection info for output 193238032Speter** 193338032Speter** Returns: 1934159613Sgshapiro** true iff header was written successfully 193538032Speter*/ 193638032Speter 1937157006Sgshapirostatic bool 193838032Speterput_vanilla_header(h, v, mci) 193938032Speter HDR *h; 194038032Speter char *v; 194138032Speter MCI *mci; 194238032Speter{ 194338032Speter register char *nlp; 194438032Speter register char *obp; 194538032Speter int putflags; 1946141862Sgshapiro char obuf[MAXLINE + 256]; /* additional length for h_field */ 194738032Speter 1948168520Sgshapiro putflags = PXLF_HEADER | PXLF_STRIPMQUOTE; 194938032Speter if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) 195038032Speter putflags |= PXLF_STRIP8BIT; 1951168520Sgshapiro (void) sm_snprintf(obuf, sizeof(obuf), "%.200s:", h->h_field); 195238032Speter obp = obuf + strlen(obuf); 195338032Speter while ((nlp = strchr(v, '\n')) != NULL) 195438032Speter { 195538032Speter int l; 195638032Speter 195738032Speter l = nlp - v; 1958120259Sgshapiro 1959120259Sgshapiro /* 1960120259Sgshapiro ** XXX This is broken for SPACELEFT()==0 1961120259Sgshapiro ** However, SPACELEFT() is always > 0 unless MAXLINE==1. 1962120259Sgshapiro */ 1963120259Sgshapiro 196490795Sgshapiro if (SPACELEFT(obuf, obp) - 1 < (size_t) l) 196538032Speter l = SPACELEFT(obuf, obp) - 1; 196638032Speter 196790795Sgshapiro (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v); 1968157006Sgshapiro if (!putxline(obuf, strlen(obuf), mci, putflags)) 1969157006Sgshapiro goto writeerr; 197038032Speter v += l + 1; 197138032Speter obp = obuf; 197238032Speter if (*v != ' ' && *v != '\t') 197338032Speter *obp++ = ' '; 197438032Speter } 1975120259Sgshapiro 1976120259Sgshapiro /* XXX This is broken for SPACELEFT()==0 */ 197790795Sgshapiro (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.*s", 197890795Sgshapiro (int) (SPACELEFT(obuf, obp) - 1), v); 1979157006Sgshapiro return putxline(obuf, strlen(obuf), mci, putflags); 1980157006Sgshapiro 1981157006Sgshapiro writeerr: 1982157006Sgshapiro return false; 198338032Speter} 1984168520Sgshapiro 198590795Sgshapiro/* 198638032Speter** COMMAIZE -- output a header field, making a comma-translated list. 198738032Speter** 198838032Speter** Parameters: 198938032Speter** h -- the header field to output. 199038032Speter** p -- the value to put in it. 199190795Sgshapiro** oldstyle -- true if this is an old style header. 199238032Speter** mci -- the connection information. 199338032Speter** e -- the envelope containing the message. 1994173343Sgshapiro** putflags -- flags for putxline() 199538032Speter** 199638032Speter** Returns: 1997159613Sgshapiro** true iff header field was written successfully 199838032Speter** 199938032Speter** Side Effects: 2000168520Sgshapiro** outputs "p" to "mci". 200138032Speter*/ 200238032Speter 2003157006Sgshapirobool 2004173343Sgshapirocommaize(h, p, oldstyle, mci, e, putflags) 200538032Speter register HDR *h; 200638032Speter register char *p; 200738032Speter bool oldstyle; 200838032Speter register MCI *mci; 200938032Speter register ENVELOPE *e; 2010173343Sgshapiro int putflags; 201138032Speter{ 201238032Speter register char *obp; 2013168520Sgshapiro int opos, omax, spaces; 201490795Sgshapiro bool firstone = true; 2015120259Sgshapiro char **res; 201638032Speter char obuf[MAXLINE + 3]; 201738032Speter 201838032Speter /* 201938032Speter ** Output the address list translated by the 202038032Speter ** mailer and with commas. 202138032Speter */ 202238032Speter 202338032Speter if (tTd(14, 2)) 2024168520Sgshapiro sm_dprintf("commaize(%s:%s)\n", h->h_field, p); 202538032Speter 202638032Speter if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags)) 202738032Speter putflags |= PXLF_STRIP8BIT; 202838032Speter 202938032Speter obp = obuf; 2030168520Sgshapiro (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%.200s:", h->h_field); 2031168520Sgshapiro /* opos = strlen(obp); instead of the next 3 lines? */ 2032168520Sgshapiro opos = strlen(h->h_field) + 1; 2033168520Sgshapiro if (opos > 201) 2034168520Sgshapiro opos = 201; 2035168520Sgshapiro obp += opos; 2036120259Sgshapiro 2037168520Sgshapiro spaces = 0; 2038168520Sgshapiro while (*p != '\0' && isascii(*p) && isspace(*p)) 2039168520Sgshapiro { 2040168520Sgshapiro ++spaces; 2041168520Sgshapiro ++p; 2042168520Sgshapiro } 2043168520Sgshapiro if (spaces > 0) 2044168520Sgshapiro { 2045168520Sgshapiro SM_ASSERT(sizeof(obuf) > opos * 2); 2046168520Sgshapiro 2047168520Sgshapiro /* 2048168520Sgshapiro ** Restrict number of spaces to half the length of buffer 2049168520Sgshapiro ** so the header field body can be put in here too. 2050168520Sgshapiro ** Note: this is a hack... 2051168520Sgshapiro */ 2052168520Sgshapiro 2053168520Sgshapiro if (spaces > sizeof(obuf) / 2) 2054168520Sgshapiro spaces = sizeof(obuf) / 2; 2055168520Sgshapiro (void) sm_snprintf(obp, SPACELEFT(obuf, obp), "%*s", spaces, 2056168520Sgshapiro ""); 2057168520Sgshapiro opos += spaces; 2058168520Sgshapiro obp += spaces; 2059168520Sgshapiro SM_ASSERT(obp < &obuf[MAXLINE]); 2060168520Sgshapiro } 2061168520Sgshapiro 206238032Speter omax = mci->mci_mailer->m_linelimit - 2; 206338032Speter if (omax < 0 || omax > 78) 206438032Speter omax = 78; 206538032Speter 206638032Speter /* 206738032Speter ** Run through the list of values. 206838032Speter */ 206938032Speter 207038032Speter while (*p != '\0') 207138032Speter { 207238032Speter register char *name; 207338032Speter register int c; 207438032Speter char savechar; 207538032Speter int flags; 207664565Sgshapiro auto int status; 207738032Speter 207838032Speter /* 207938032Speter ** Find the end of the name. New style names 208038032Speter ** end with a comma, old style names end with 208138032Speter ** a space character. However, spaces do not 208238032Speter ** necessarily delimit an old-style name -- at 208338032Speter ** signs mean keep going. 208438032Speter */ 208538032Speter 208638032Speter /* find end of name */ 208738032Speter while ((isascii(*p) && isspace(*p)) || *p == ',') 208838032Speter p++; 208938032Speter name = p; 2090120259Sgshapiro res = NULL; 209138032Speter for (;;) 209238032Speter { 209338032Speter auto char *oldp; 209438032Speter char pvpbuf[PSBUFSIZE]; 209538032Speter 2096120259Sgshapiro res = prescan(p, oldstyle ? ' ' : ',', pvpbuf, 2097168520Sgshapiro sizeof(pvpbuf), &oldp, ExtTokenTab, false); 209838032Speter p = oldp; 2099120259Sgshapiro#if _FFR_IGNORE_BOGUS_ADDR 2100120259Sgshapiro /* ignore addresses that can't be parsed */ 2101120259Sgshapiro if (res == NULL) 2102120259Sgshapiro { 2103120259Sgshapiro name = p; 2104120259Sgshapiro continue; 2105120259Sgshapiro } 2106120259Sgshapiro#endif /* _FFR_IGNORE_BOGUS_ADDR */ 210738032Speter 210838032Speter /* look to see if we have an at sign */ 210938032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 211038032Speter p++; 211138032Speter 211238032Speter if (*p != '@') 211338032Speter { 211438032Speter p = oldp; 211538032Speter break; 211638032Speter } 211790795Sgshapiro ++p; 211838032Speter while (*p != '\0' && isascii(*p) && isspace(*p)) 211938032Speter p++; 212038032Speter } 212138032Speter /* at the end of one complete name */ 212238032Speter 212338032Speter /* strip off trailing white space */ 212438032Speter while (p >= name && 212538032Speter ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0')) 212638032Speter p--; 212738032Speter if (++p == name) 212838032Speter continue; 2129120259Sgshapiro 2130120259Sgshapiro /* 2131120259Sgshapiro ** if prescan() failed go a bit backwards; this is a hack, 2132120259Sgshapiro ** there should be some better error recovery. 2133120259Sgshapiro */ 2134120259Sgshapiro 2135120259Sgshapiro if (res == NULL && p > name && 2136120259Sgshapiro !((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0')) 2137120259Sgshapiro --p; 213838032Speter savechar = *p; 213938032Speter *p = '\0'; 214038032Speter 214138032Speter /* translate the name to be relative */ 214238032Speter flags = RF_HEADERADDR|RF_ADDDOMAIN; 214338032Speter if (bitset(H_FROM, h->h_flags)) 214438032Speter flags |= RF_SENDERADDR; 214538032Speter#if USERDB 214638032Speter else if (e->e_from.q_mailer != NULL && 214738032Speter bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags)) 214838032Speter { 214938032Speter char *q; 215038032Speter 215190795Sgshapiro q = udbsender(name, e->e_rpool); 215238032Speter if (q != NULL) 215338032Speter name = q; 215438032Speter } 215564565Sgshapiro#endif /* USERDB */ 215664565Sgshapiro status = EX_OK; 215764565Sgshapiro name = remotename(name, mci->mci_mailer, flags, &status, e); 215838032Speter if (*name == '\0') 215938032Speter { 216038032Speter *p = savechar; 216138032Speter continue; 216238032Speter } 216390795Sgshapiro name = denlstring(name, false, true); 216438032Speter 216538032Speter /* output the name with nice formatting */ 216638032Speter opos += strlen(name); 216738032Speter if (!firstone) 216838032Speter opos += 2; 216938032Speter if (opos > omax && !firstone) 217038032Speter { 217190795Sgshapiro (void) sm_strlcpy(obp, ",\n", SPACELEFT(obuf, obp)); 2172157006Sgshapiro if (!putxline(obuf, strlen(obuf), mci, putflags)) 2173157006Sgshapiro goto writeerr; 217438032Speter obp = obuf; 2175168520Sgshapiro (void) sm_strlcpy(obp, " ", sizeof(obuf)); 217638032Speter opos = strlen(obp); 217738032Speter obp += opos; 217838032Speter opos += strlen(name); 217938032Speter } 218038032Speter else if (!firstone) 218138032Speter { 218290795Sgshapiro (void) sm_strlcpy(obp, ", ", SPACELEFT(obuf, obp)); 218338032Speter obp += 2; 218438032Speter } 218538032Speter 218638032Speter while ((c = *name++) != '\0' && obp < &obuf[MAXLINE]) 218738032Speter *obp++ = c; 218890795Sgshapiro firstone = false; 218938032Speter *p = savechar; 219038032Speter } 2191168520Sgshapiro if (obp < &obuf[sizeof(obuf)]) 2192120259Sgshapiro *obp = '\0'; 2193120259Sgshapiro else 2194168520Sgshapiro obuf[sizeof(obuf) - 1] = '\0'; 2195157006Sgshapiro return putxline(obuf, strlen(obuf), mci, putflags); 2196157006Sgshapiro 2197157006Sgshapiro writeerr: 2198157006Sgshapiro return false; 219938032Speter} 2200157006Sgshapiro 220190795Sgshapiro/* 220238032Speter** COPYHEADER -- copy header list 220338032Speter** 220438032Speter** This routine is the equivalent of newstr for header lists 220538032Speter** 220638032Speter** Parameters: 220738032Speter** header -- list of header structures to copy. 220890795Sgshapiro** rpool -- resource pool, or NULL 220938032Speter** 221038032Speter** Returns: 221138032Speter** a copy of 'header'. 221238032Speter** 221338032Speter** Side Effects: 221438032Speter** none. 221538032Speter*/ 221638032Speter 221738032SpeterHDR * 221890795Sgshapirocopyheader(header, rpool) 221938032Speter register HDR *header; 222090795Sgshapiro SM_RPOOL_T *rpool; 222138032Speter{ 222238032Speter register HDR *newhdr; 222338032Speter HDR *ret; 222438032Speter register HDR **tail = &ret; 222538032Speter 222638032Speter while (header != NULL) 222738032Speter { 2228168520Sgshapiro newhdr = (HDR *) sm_rpool_malloc_x(rpool, sizeof(*newhdr)); 222938032Speter STRUCTCOPY(*header, *newhdr); 223038032Speter *tail = newhdr; 223138032Speter tail = &newhdr->h_link; 223238032Speter header = header->h_link; 223338032Speter } 223438032Speter *tail = NULL; 223564565Sgshapiro 223638032Speter return ret; 223738032Speter} 2238168520Sgshapiro 223990795Sgshapiro/* 224042580Speter** FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header 224142580Speter** 224242580Speter** Run through all of the parameters of a MIME header and 224342580Speter** possibly truncate and rebalance the parameter according 224442580Speter** to MaxMimeFieldLength. 224542580Speter** 224642580Speter** Parameters: 2247111826Sgshapiro** h -- the header to truncate/rebalance 2248111826Sgshapiro** e -- the current envelope 224942580Speter** 225042580Speter** Returns: 225171348Sgshapiro** length of last offending field, 0 if all ok. 225242580Speter** 225342580Speter** Side Effects: 225442580Speter** string modified in place 225542580Speter*/ 225642580Speter 225771348Sgshapirostatic size_t 2258111826Sgshapirofix_mime_header(h, e) 2259111826Sgshapiro HDR *h; 2260111826Sgshapiro ENVELOPE *e; 226142580Speter{ 2262111826Sgshapiro char *begin = h->h_value; 226342580Speter char *end; 226471348Sgshapiro size_t len = 0; 226571348Sgshapiro size_t retlen = 0; 226664565Sgshapiro 2267111826Sgshapiro if (begin == NULL || *begin == '\0') 226871348Sgshapiro return 0; 226964565Sgshapiro 227042580Speter /* Split on each ';' */ 2271120259Sgshapiro /* find_character() never returns NULL */ 227242580Speter while ((end = find_character(begin, ';')) != NULL) 227342580Speter { 227442580Speter char save = *end; 227542580Speter char *bp; 227664565Sgshapiro 227742580Speter *end = '\0'; 227864565Sgshapiro 227971348Sgshapiro len = strlen(begin); 228071348Sgshapiro 228142580Speter /* Shorten individual parameter */ 228242580Speter if (shorten_rfc822_string(begin, MaxMimeFieldLength)) 2283111826Sgshapiro { 2284111826Sgshapiro if (len < MaxMimeFieldLength) 2285111826Sgshapiro { 2286111826Sgshapiro /* we only rebalanced a bogus field */ 2287111826Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 2288111826Sgshapiro "Fixed MIME %s header field (possible attack)", 2289111826Sgshapiro h->h_field); 2290111826Sgshapiro if (tTd(34, 11)) 2291111826Sgshapiro sm_dprintf(" fixed MIME %s header field (possible attack)\n", 2292111826Sgshapiro h->h_field); 2293111826Sgshapiro } 2294111826Sgshapiro else 2295111826Sgshapiro { 2296111826Sgshapiro /* we actually shortened the header */ 2297111826Sgshapiro retlen = len; 2298111826Sgshapiro } 2299111826Sgshapiro } 230064565Sgshapiro 230142580Speter /* Collapse the possibly shortened string with rest */ 230242580Speter bp = begin + strlen(begin); 230342580Speter if (bp != end) 230442580Speter { 230542580Speter char *ep = end; 230664565Sgshapiro 230742580Speter *end = save; 230842580Speter end = bp; 230964565Sgshapiro 231042580Speter /* copy character by character due to overlap */ 231142580Speter while (*ep != '\0') 231242580Speter *bp++ = *ep++; 231342580Speter *bp = '\0'; 231442580Speter } 231542580Speter else 231642580Speter *end = save; 231742580Speter if (*end == '\0') 231842580Speter break; 231964565Sgshapiro 232042580Speter /* Move past ';' */ 232142580Speter begin = end + 1; 232242580Speter } 232371348Sgshapiro return retlen; 232442580Speter} 2325