consolidate temporary allocation in tmps.c
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
CommitLineData
b3cbe40f
EA
1# include <stdio.h>
2# include <ctype.h>
406f98bc 3# include "postbox.h"
b3cbe40f 4
9e3c0a28 5static char SccsId[] = "@(#)parseaddr.c 3.8 %G%";
916b3375 6
b3cbe40f
EA
7/*
8** PARSE -- Parse an address
9**
10** Parses an address and breaks it up into three parts: a
11** net to transmit the message on, the host to transmit it
12** to, and a user on that host. These are loaded into an
406f98bc 13** ADDRESS header with the values squirreled away if necessary.
b3cbe40f
EA
14** The "user" part may not be a real user; the process may
15** just reoccur on that machine. For example, on a machine
16** with an arpanet connection, the address
17** csvax.bill@berkeley
18** will break up to a "user" of 'csvax.bill' and a host
19** of 'berkeley' -- to be transmitted over the arpanet.
20**
21** Parameters:
22** addr -- the address to parse.
23** a -- a pointer to the address descriptor buffer.
24** If NULL, a header will be created.
25** copyf -- determines what shall be copied:
26** -1 -- don't copy anything. The printname
27** (q_paddr) is just addr, and the
28** user & host are allocated internally
29** to parse.
30** 0 -- copy out the parsed user & host, but
31** don't copy the printname.
32** +1 -- copy everything.
33**
34** Returns:
35** A pointer to the address descriptor header (`a' if
36** `a' is non-NULL).
37** NULL on error.
38**
39** Side Effects:
40** none
41**
b3cbe40f
EA
42** Called By:
43** main
44** sendto
45** alias
46** savemail
b3cbe40f
EA
47*/
48
d6a28dd8 49# define DELIMCHARS "$()<>@!.,;:\\\" \t\r\n" /* word delimiters */
ecf90b7d
EA
50# define SPACESUB ('.'|0200) /* substitution for <lwsp> */
51
406f98bc 52ADDRESS *
b3cbe40f
EA
53parse(addr, a, copyf)
54 char *addr;
406f98bc 55 register ADDRESS *a;
b3cbe40f
EA
56 int copyf;
57{
d6a28dd8
EA
58 register char **pvp;
59 register struct mailer *m;
60 extern char **prescan();
406f98bc 61 extern char *newstr();
7b9c35c8 62 extern char *strcpy();
d6a28dd8 63 extern ADDRESS *buildaddr();
b3cbe40f
EA
64
65 /*
66 ** Initialize and prescan address.
67 */
68
69 To = addr;
9e3c0a28
EA
70# ifdef DEBUG
71 if (Debug)
72 printf("\n--parse(%s)\n", addr);
73# endif DEBUG
74
d6a28dd8
EA
75 pvp = prescan(addr, '\0');
76 if (pvp == NULL)
b3cbe40f
EA
77 return (NULL);
78
79 /*
d6a28dd8 80 ** Apply rewriting rules.
b3cbe40f
EA
81 */
82
d6a28dd8 83 rewrite(pvp);
b3cbe40f 84
d6a28dd8
EA
85 /*
86 ** See if we resolved to a real mailer.
87 */
b3cbe40f 88
d6a28dd8
EA
89 if (pvp[0][0] != CANONNET)
90 {
91 setstat(EX_USAGE);
92 usrerr("cannot resolve name");
93 return (NULL);
b3cbe40f
EA
94 }
95
96 /*
d6a28dd8 97 ** Build canonical address from pvp.
b3cbe40f
EA
98 */
99
d6a28dd8
EA
100 a = buildaddr(pvp, a);
101 m = Mailer[a->q_mailer];
b3cbe40f
EA
102
103 /*
d6a28dd8
EA
104 ** Make local copies of the host & user and then
105 ** transport them out.
b3cbe40f
EA
106 */
107
b3cbe40f 108 if (copyf > 0)
406f98bc 109 a->q_paddr = newstr(addr);
b3cbe40f
EA
110 else
111 a->q_paddr = addr;
b3cbe40f 112
d6a28dd8 113 if (copyf >= 0)
b3cbe40f 114 {
d6a28dd8 115 if (a->q_host != NULL)
406f98bc 116 a->q_host = newstr(a->q_host);
d6a28dd8
EA
117 else
118 a->q_host = "";
119 if (a->q_user != a->q_paddr)
120 a->q_user = newstr(a->q_user);
b3cbe40f
EA
121 }
122
123 /*
124 ** Do UPPER->lower case mapping unless inhibited.
125 */
126
d6a28dd8 127 if (!bitset(M_HST_UPPER, m->m_flags))
b3cbe40f 128 makelower(a->q_host);
d6a28dd8 129 if (!bitset(M_USR_UPPER, m->m_flags))
b3cbe40f
EA
130 makelower(a->q_user);
131
132 /*
133 ** Compute return value.
134 */
135
136# ifdef DEBUG
7160a5cb 137 if (Debug)
b3cbe40f 138 printf("parse(\"%s\"): host \"%s\" user \"%s\" mailer %d\n",
d6a28dd8 139 addr, a->q_host, a->q_user, a->q_mailer);
b3cbe40f
EA
140# endif DEBUG
141
142 return (a);
143}
144\f/*
b3cbe40f
EA
145** PRESCAN -- Prescan name and make it canonical
146**
147** Scans a name and turns it into canonical form. This involves
148** deleting blanks, comments (in parentheses), and turning the
149** word "at" into an at-sign ("@"). The name is copied as this
150** is done; it is legal to copy a name onto itself, since this
151** process can only make things smaller.
152**
153** This routine knows about quoted strings and angle brackets.
154**
155** There are certain subtleties to this routine. The one that
156** comes to mind now is that backslashes on the ends of names
157** are silently stripped off; this is intentional. The problem
158** is that some versions of sndmsg (like at LBL) set the kill
159** character to something other than @ when reading addresses;
160** so people type "csvax.eric\@berkeley" -- which screws up the
161** berknet mailer.
162**
163** Parameters:
164** addr -- the name to chomp.
b3cbe40f
EA
165** delim -- the delimiter for the address, normally
166** '\0' or ','; \0 is accepted in any case.
167** are moving in place; set buflim to high core.
168**
169** Returns:
d6a28dd8 170** A pointer to a vector of tokens.
b3cbe40f
EA
171** NULL on error.
172**
173** Side Effects:
d6a28dd8 174** none.
b3cbe40f
EA
175*/
176
d6a28dd8
EA
177# define OPER 1
178# define ATOM 2
179# define EOTOK 3
180# define QSTRING 4
181# define SPACE 5
182# define DOLLAR 6
183# define GETONE 7
184
185char **
186prescan(addr, delim)
b3cbe40f 187 char *addr;
b3cbe40f
EA
188 char delim;
189{
190 register char *p;
d6a28dd8
EA
191 static char buf[MAXNAME+MAXATOM];
192 static char *av[MAXATOM+1];
193 char **avp;
b3cbe40f 194 bool space;
b3cbe40f
EA
195 bool bslashmode;
196 int cmntcnt;
197 int brccnt;
198 register char c;
d6a28dd8 199 char *tok;
b3cbe40f 200 register char *q;
406f98bc 201 extern char *index();
d6a28dd8
EA
202 register int state;
203 int nstate;
b3cbe40f 204
ecf90b7d 205 space = FALSE;
b3cbe40f 206 q = buf;
d6a28dd8 207 bslashmode = FALSE;
b3cbe40f 208 cmntcnt = brccnt = 0;
d6a28dd8
EA
209 avp = av;
210 state = OPER;
211 for (p = addr; *p != '\0' && *p != delim; )
b3cbe40f 212 {
d6a28dd8
EA
213 /* read a token */
214 tok = q;
215 while ((c = *p++) != '\0' && c != delim)
b3cbe40f 216 {
d6a28dd8
EA
217 /* chew up special characters */
218 *q = '\0';
219 if (bslashmode)
220 {
221 c |= 0200;
222 bslashmode = FALSE;
223 }
224 else if (c == '\\')
225 {
226 bslashmode = TRUE;
227 continue;
228 }
229
230 nstate = toktype(c);
231 switch (state)
232 {
233 case QSTRING: /* in quoted string */
234 if (c == '"')
235 state = OPER;
236 break;
237
238 case ATOM: /* regular atom */
239 state = nstate;
240 if (state != ATOM)
241 {
242 state = EOTOK;
243 p--;
244 }
245 break;
246
247 case GETONE: /* grab one character */
248 state = OPER;
249 break;
250
251 case EOTOK: /* after atom or q-string */
252 state = nstate;
253 if (state == SPACE)
254 continue;
255 break;
256
257 case SPACE: /* linear white space */
258 state = nstate;
259 space = TRUE;
260 continue;
261
262 case OPER: /* operator */
263 if (nstate == SPACE)
264 continue;
265 state = nstate;
266 break;
267
268 case DOLLAR: /* $- etc. */
269 state = OPER;
270 switch (c)
271 {
272 case '$': /* literal $ */
273 break;
274
275 case '+': /* match anything */
276 c = MATCHANY;
277 state = GETONE;
278 break;
279
280 case '-': /* match one token */
281 c = MATCHONE;
282 state = GETONE;
283 break;
284
285 case '#': /* canonical net name */
286 c = CANONNET;
287 break;
288
289 case '@': /* canonical host name */
290 c = CANONHOST;
291 break;
292
293 case ':': /* canonical user name */
294 c = CANONUSER;
295 break;
296
297 default:
298 c = '$';
299 state = OPER;
300 p--;
301 break;
302 }
303 break;
304
305 default:
306 syserr("prescan: unknown state %d", state);
307 }
308
309 if (state == OPER)
310 space = FALSE;
311 else if (state == EOTOK)
312 break;
313 if (c == '$' && delim == '\t')
314 {
315 state = DOLLAR;
316 continue;
317 }
318
319 /* squirrel it away */
320 if (q >= &buf[sizeof buf - 5])
321 {
322 usrerr("Address too long");
323 return (NULL);
324 }
325 if (space)
326 *q++ = SPACESUB;
327 *q++ = c;
328
329 /* decide whether this represents end of token */
330 if (state == OPER)
331 break;
b3cbe40f 332 }
d6a28dd8
EA
333 if (c == '\0' || c == delim)
334 p--;
335
336 /* new token */
337 if (tok == q)
b3cbe40f 338 continue;
d6a28dd8
EA
339 *q++ = '\0';
340
341 c = tok[0];
342 if (c == '(')
2a119b55 343 {
b3cbe40f 344 cmntcnt++;
2a119b55
EA
345 continue;
346 }
b3cbe40f
EA
347 else if (c == ')')
348 {
349 if (cmntcnt <= 0)
350 {
351 usrerr("Unbalanced ')'");
352 return (NULL);
353 }
354 else
355 {
356 cmntcnt--;
357 continue;
358 }
359 }
d6a28dd8 360 else if (cmntcnt > 0)
ecf90b7d 361 continue;
d6a28dd8
EA
362
363 *avp++ = tok;
364
365 /* we prefer <> specs */
366 if (c == '<')
b3cbe40f 367 {
7a8fc396
EA
368 if (brccnt < 0)
369 {
370 usrerr("multiple < spec");
371 return (NULL);
372 }
b3cbe40f 373 brccnt++;
ecf90b7d 374 space = FALSE;
b3cbe40f
EA
375 if (brccnt == 1)
376 {
377 /* we prefer using machine readable name */
378 q = buf;
379 *q = '\0';
d6a28dd8 380 avp = av;
b3cbe40f
EA
381 continue;
382 }
383 }
384 else if (c == '>')
385 {
386 if (brccnt <= 0)
387 {
388 usrerr("Unbalanced `>'");
389 return (NULL);
390 }
391 else
392 brccnt--;
393 if (brccnt <= 0)
7a8fc396
EA
394 {
395 brccnt = -1;
b3cbe40f 396 continue;
7a8fc396 397 }
b3cbe40f
EA
398 }
399
400 /*
401 ** Turn "at" into "@",
2a119b55 402 ** but only if "at" is a word.
b3cbe40f
EA
403 */
404
d6a28dd8 405 if (lower(tok[0]) == 'a' && lower(tok[1]) == 't' && tok[2] == '\0')
b3cbe40f 406 {
d6a28dd8
EA
407 tok[0] = '@';
408 tok[1] = '\0';
b3cbe40f 409 }
d6a28dd8
EA
410 }
411 *avp = NULL;
412 if (cmntcnt > 0)
413 usrerr("Unbalanced '('");
414 else if (brccnt > 0)
415 usrerr("Unbalanced '<'");
416 else if (state == QSTRING)
417 usrerr("Unbalanced '\"'");
418 else if (av[0] != NULL)
419 return (av);
420 return (NULL);
421}
422\f/*
423** TOKTYPE -- return token type
424**
425** Parameters:
426** c -- the character in question.
427**
428** Returns:
429** Its type.
430**
431** Side Effects:
432** none.
433*/
b3cbe40f 434
d6a28dd8
EA
435toktype(c)
436 register char c;
437{
438 if (isspace(c))
439 return (SPACE);
440 if (index(DELIMCHARS, c) != NULL || iscntrl(c))
441 return (OPER);
442 return (ATOM);
443}
444\f/*
445** REWRITE -- apply rewrite rules to token vector.
446**
447** Parameters:
448** pvp -- pointer to token vector.
449**
450** Returns:
451** none.
452**
453** Side Effects:
454** pvp is modified.
455*/
456
457struct match
458{
459 char **firsttok; /* first token matched */
460 char **lasttok; /* last token matched */
461 char name; /* name of parameter */
462};
463
464# define MAXMATCH 8 /* max params per rewrite */
465
466
467rewrite(pvp)
468 char **pvp;
469{
470 register char *ap; /* address pointer */
471 register char *rp; /* rewrite pointer */
472 register char **avp; /* address vector pointer */
473 register char **rvp; /* rewrite vector pointer */
474 struct rewrite *rwr;
475 struct match mlist[MAXMATCH];
476 char *npvp[MAXATOM+1]; /* temporary space for rebuild */
477
9e3c0a28 478# ifdef DEBUGX
d6a28dd8
EA
479 if (Debug)
480 {
481 printf("rewrite: original pvp:\n");
482 printav(pvp);
483 }
9e3c0a28 484# endif DEBUGX
d6a28dd8
EA
485
486 /*
487 ** Run through the list of rewrite rules, applying
488 ** any that match.
489 */
490
491 for (rwr = RewriteRules; rwr != NULL; )
492 {
9e3c0a28 493# ifdef DEBUGX
d6a28dd8
EA
494 if (Debug)
495 {
496 printf("-----trying rule:\n");
497 printav(rwr->r_lhs);
498 }
9e3c0a28 499# endif DEBUGX
ecf90b7d 500
d6a28dd8
EA
501 /* try to match on this rule */
502 clrmatch(mlist);
503 for (rvp = rwr->r_lhs, avp = pvp; *avp != NULL; )
b3cbe40f 504 {
d6a28dd8
EA
505 ap = *avp;
506 rp = *rvp;
507
508 if (rp == NULL)
b3cbe40f 509 {
d6a28dd8
EA
510 /* end-of-pattern before end-of-address */
511 goto fail;
512 }
513
514 switch (*rp)
515 {
516 case MATCHONE:
517 /* match exactly one token */
518 setmatch(mlist, rp[1], avp, avp);
519 break;
520
521 case MATCHANY:
522 /* match any number of tokens */
523 setmatch(mlist, rp[1], NULL, avp);
524 break;
525
526 default:
527 /* must have exact match */
528 /* can scribble rp & ap here safely */
5e81958e 529 while (*rp != '\0' || *ap != '\0')
d6a28dd8
EA
530 {
531 if (*rp++ != lower(*ap++))
532 goto fail;
533 }
534 break;
535 }
536
537 /* successful match on this token */
538 avp++;
539 rvp++;
540 continue;
541
542 fail:
543 /* match failed -- back up */
544 while (--rvp >= rwr->r_lhs)
545 {
546 rp = *rvp;
547 if (*rp == MATCHANY)
548 break;
549
550 /* can't extend match: back up everything */
551 avp--;
552
553 if (*rp == MATCHONE)
554 {
555 /* undo binding */
556 setmatch(mlist, rp[1], NULL, NULL);
557 }
558 }
559
560 if (rvp < rwr->r_lhs)
561 {
562 /* total failure to match */
563 break;
b3cbe40f 564 }
b3cbe40f 565 }
d6a28dd8
EA
566
567 /*
568 ** See if we successfully matched
569 */
570
571 if (rvp >= rwr->r_lhs && *rvp == NULL)
572 {
9e3c0a28 573# ifdef DEBUGX
d6a28dd8
EA
574 if (Debug)
575 {
576 printf("-----rule matches:\n");
577 printav(rwr->r_rhs);
578 }
9e3c0a28 579# endif DEBUGX
d6a28dd8
EA
580
581 /* substitute */
582 for (rvp = rwr->r_rhs, avp = npvp; *rvp != NULL; rvp++)
583 {
584 rp = *rvp;
585 if (*rp == MATCHANY)
586 {
587 register struct match *m;
588 register char **pp;
589 extern struct match *findmatch();
590
591 m = findmatch(mlist, rp[1]);
592 if (m != NULL)
593 {
594 pp = m->firsttok;
595 do
596 {
597 *avp++ = *pp;
598 } while (pp++ != m->lasttok);
599 }
600 }
601 else
602 *avp++ = rp;
603 }
604 *avp++ = NULL;
605 bmove(npvp, pvp, (avp - npvp) * sizeof *avp);
606# ifdef DEBUG
607 if (Debug)
608 {
9e3c0a28
EA
609 char **vp;
610
611 printf("rewritten as `");
612 for (vp = pvp; *vp != NULL; vp++)
613 xputs(*vp);
614 printf("'\n");
d6a28dd8
EA
615 }
616# endif DEBUG
617 if (pvp[0][0] == CANONNET)
618 break;
619 }
620 else
621 {
9e3c0a28 622# ifdef DEBUGX
d6a28dd8
EA
623 if (Debug)
624 printf("----- rule fails\n");
9e3c0a28 625# endif DEBUGX
d6a28dd8
EA
626 rwr = rwr->r_next;
627 }
b3cbe40f 628 }
d6a28dd8
EA
629}
630\f/*
631** SETMATCH -- set parameter value in match vector
632**
633** Parameters:
634** mlist -- list of match values.
635** name -- the character name of this parameter.
636** first -- the first location of the replacement.
637** last -- the last location of the replacement.
638**
639** If last == NULL, delete this entry.
640** If first == NULL, extend this entry (or add it if
641** it does not exist).
642**
643** Returns:
644** nothing.
645**
646** Side Effects:
647** munges with mlist.
648*/
649
650setmatch(mlist, name, first, last)
651 struct match *mlist;
652 char name;
653 char **first;
654 char **last;
655{
656 register struct match *m;
657 struct match *nullm = NULL;
658
659 for (m = mlist; m < &mlist[MAXMATCH]; m++)
660 {
661 if (m->name == name)
662 break;
663 if (m->name == '\0')
664 nullm = m;
665 }
666
667 if (m >= &mlist[MAXMATCH])
668 m = nullm;
669
670 if (last == NULL)
671 {
672 m->name = '\0';
673 return;
674 }
675
676 if (m->name == '\0')
677 {
678 if (first == NULL)
679 m->firsttok = last;
680 else
681 m->firsttok = first;
682 }
683 m->name = name;
684 m->lasttok = last;
685}
686\f/*
687** FINDMATCH -- find match in mlist
688**
689** Parameters:
690** mlist -- list to search.
691** name -- name to find.
692**
693** Returns:
694** pointer to match structure.
695** NULL if no match.
696**
697** Side Effects:
698** none.
699*/
700
701struct match *
702findmatch(mlist, name)
703 struct match *mlist;
704 char name;
705{
706 register struct match *m;
707
708 for (m = mlist; m < &mlist[MAXMATCH]; m++)
709 {
710 if (m->name == name)
711 return (m);
712 }
713
b3cbe40f
EA
714 return (NULL);
715}
d6a28dd8
EA
716\f/*
717** CLRMATCH -- clear match list
718**
719** Parameters:
720** mlist -- list to clear.
721**
722** Returns:
723** none.
724**
725** Side Effects:
726** mlist is cleared.
727*/
728
729clrmatch(mlist)
730 struct match *mlist;
731{
732 register struct match *m;
733
734 for (m = mlist; m < &mlist[MAXMATCH]; m++)
735 m->name = '\0';
736}
737\f/*
738** BUILDADDR -- build address from token vector.
739**
740** Parameters:
741** tv -- token vector.
742** a -- pointer to address descriptor to fill.
743** If NULL, one will be allocated.
744**
745** Returns:
746** 'a'
747**
748** Side Effects:
749** fills in 'a'
750*/
751
752ADDRESS *
753buildaddr(tv, a)
754 register char **tv;
755 register ADDRESS *a;
756{
757 register int i;
758 static char buf[MAXNAME];
759 struct mailer **mp;
760 register struct mailer *m;
761 extern char *xalloc();
762
763 if (a == NULL)
764 a = (ADDRESS *) xalloc(sizeof *a);
9e3c0a28 765 a->q_flags = 0;
d6a28dd8
EA
766
767 /* figure out what net/mailer to use */
768 if (**tv != CANONNET)
769 syserr("buildaddr: no net");
770 tv++;
5e81958e 771 for (mp = Mailer, i = 0; (m = *mp++) != NULL; i++)
d6a28dd8
EA
772 {
773 if (strcmp(m->m_name, *tv) == 0)
774 break;
775 }
776 if (m == NULL)
777 syserr("buildaddr: unknown net %s", *tv);
778 a->q_mailer = i;
779
780 /* figure out what host (if any) */
781 tv++;
782 if (!bitset(M_NOHOST, m->m_flags))
783 {
784 if (**tv != CANONHOST)
785 syserr("buildaddr: no host");
786 tv++;
787 a->q_host = *tv;
788 tv++;
789 }
790 else
791 a->q_host = NULL;
792
793 /* figure out the user */
794 if (**tv != CANONUSER)
795 syserr("buildaddr: no user");
796 buf[0] = '\0';
797 while (**++tv != NULL)
798 strcat(buf, *tv);
799 a->q_user = buf;
800
801 return (a);
802}
9e3c0a28
EA
803\f/*
804** SAMEADDR -- Determine if two addresses are the same
805**
806** This is not just a straight comparison -- if the mailer doesn't
807** care about the host we just ignore it, etc.
808**
809** Parameters:
810** a, b -- pointers to the internal forms to compare.
811** wildflg -- if TRUE, 'a' may have no user specified,
812** in which case it is to match anything.
813**
814** Returns:
815** TRUE -- they represent the same mailbox.
816** FALSE -- they don't.
817**
818** Side Effects:
819** none.
820*/
821
822bool
823sameaddr(a, b, wildflg)
824 register ADDRESS *a;
825 register ADDRESS *b;
826 bool wildflg;
827{
828 /* if they don't have the same mailer, forget it */
829 if (a->q_mailer != b->q_mailer)
830 return (FALSE);
831
832 /* if the user isn't the same, we can drop out */
833 if ((!wildflg || a->q_user[0] != '\0') && strcmp(a->q_user, b->q_user) != 0)
834 return (FALSE);
835
836 /* if the mailer ignores hosts, we have succeeded! */
837 if (bitset(M_NOHOST, Mailer[a->q_mailer]->m_flags))
838 return (TRUE);
839
840 /* otherwise compare hosts (but be careful for NULL ptrs) */
841 if (a->q_host == NULL || b->q_host == NULL)
842 return (FALSE);
843 if (strcmp(a->q_host, b->q_host) != 0)
844 return (FALSE);
845
846 return (TRUE);
847}