cleaned up comments
[unix-history] / usr / src / usr.sbin / sendmail / src / parseaddr.c
CommitLineData
b3cbe40f
EA
1# include <stdio.h>
2# include <ctype.h>
3# include "dlvrmail.h"
4
f895a45d 5static char SccsId[] = "@(#)parseaddr.c 1.3 %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
13** addrq header with the values squirreled away if necessary.
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
49addrq *
50parse(addr, a, copyf)
51 char *addr;
52 register addrq *a;
53 int copyf;
54{
55 register char *p;
56 register struct parsetab *t;
57 extern struct parsetab ParseTab[];
58 static char buf[MAXNAME];
59 register char c;
60 register char *q;
61 bool got_one;
62 extern char *prescan();
63 extern char *xalloc();
64
65 /*
66 ** Initialize and prescan address.
67 */
68
69 To = addr;
70 if (prescan(addr, buf, &buf[sizeof buf], '\0') == NULL)
71 return (NULL);
72
73 /*
74 ** Scan parse table.
75 ** Look for the first entry designating a character
76 ** that is contained in the address.
77 ** Arrange for q to point to that character.
78 ** Check to see that there is only one of the char
79 ** if it must be unique.
80 ** Find the last one if the host is on the RHS.
81 ** Insist that the host name is atomic.
82 ** If just doing a map, do the map and then start all
83 ** over.
84 */
85
86 rescan:
87 got_one = FALSE;
88 for (t = ParseTab; t->p_char != '\0'; t++)
89 {
90 q = NULL;
91 for (p = buf; (c = *p) != '\0'; p++)
92 {
93 /* find the end of this token */
94 while (isalnum(c) || c == '-' || c == '_')
95 c = *++p;
96 if (c == '\0')
97 break;
98
99 if (c == t->p_char)
100 {
101 got_one = TRUE;
102
103 /* do mapping as appropriate */
104 if (flagset(P_MAP, t->p_flags))
105 {
106 *p = t->p_arg[0];
107 if (flagset(P_ONE, t->p_flags))
108 goto rescan;
109 else
110 continue;
111 }
112
113 /* arrange for q to point to it */
114 if (q != NULL && flagset(P_ONE, t->p_flags))
115 {
116 usrerr("multichar error");
117 ExitStat = EX_USAGE;
118 return (NULL);
119 }
120 if (q == NULL || flagset(P_HLAST, t->p_flags))
121 q = p;
122 }
123 else
124 {
125 /* insist that host name is atomic */
126 if (flagset(P_HLAST, t->p_flags))
127 q = NULL;
128 else
129 break;
130 }
131 }
132
133 if (q != NULL)
134 break;
135 }
136
137 /*
138 ** If we matched nothing cleanly, but we did match something
139 ** somewhere in the process of scanning, then we have a
140 ** syntax error. This can happen on things like a@b:c where
141 ** @ has a right host and : has a left host.
142 **
143 ** We also set `q' to the null string, in case someone forgets
144 ** to put the P_MOVE bit in the local mailer entry of the
145 ** configuration table.
146 */
147
148 if (q == NULL)
149 {
150 q = "";
151 if (got_one)
152 {
153 usrerr("syntax error");
154 ExitStat = EX_USAGE;
155 return (NULL);
156 }
157 }
158
159 /*
160 ** Interpret entry.
161 ** t points to the entry for the mailer we will use.
162 ** q points to the significant character.
163 */
164
165 if (a == NULL)
166 a = (addrq *) xalloc(sizeof *a);
167 if (copyf > 0)
168 {
169 p = xalloc((unsigned) strlen(addr) + 1);
170 strcpy(p, addr);
171 a->q_paddr = p;
172 }
173 else
174 a->q_paddr = addr;
175 a->q_mailer = &Mailer[t->p_mailer];
176
177 if (flagset(P_MOVE, t->p_flags))
178 {
179 /* send the message to another host & retry */
180 a->q_host = t->p_arg;
181 if (copyf >= 0)
182 {
183 p = xalloc((unsigned) strlen(buf) + 1);
184 strcpy(p, buf);
185 a->q_user = p;
186 }
187 else
188 a->q_user = buf;
189 }
190 else
191 {
192 /*
193 ** Make local copies of the host & user and then
194 ** transport them out.
195 */
196
197 *q++ = '\0';
198 if (flagset(P_HLAST, t->p_flags))
199 {
200 a->q_host = q;
201 a->q_user = buf;
202 }
203 else
204 {
205 a->q_host = buf;
206 a->q_user = q;
207 }
208 if (copyf >= 0)
209 {
210 p = xalloc((unsigned) strlen(a->q_host) + 1);
211 strcpy(p, a->q_host);
212 a->q_host = p;
213 p = xalloc((unsigned) strlen(a->q_user) + 1);
214 strcpy(p, a->q_user);
215 a->q_user = p;
216 }
217 }
218
219 /*
220 ** Do UPPER->lower case mapping unless inhibited.
221 */
222
223 if (!flagset(P_HST_UPPER, t->p_flags))
224 makelower(a->q_host);
225 if (!flagset(P_USR_UPPER, t->p_flags))
226 makelower(a->q_user);
227
228 /*
229 ** Compute return value.
230 */
231
232# ifdef DEBUG
233 if (Debug && copyf >= 0)
234 printf("parse(\"%s\"): host \"%s\" user \"%s\" mailer %d\n",
235 addr, a->q_host, a->q_user, t->p_mailer);
236# endif DEBUG
237
238 return (a);
239}
240\f/*
241** MAKELOWER -- Translate a line into lower case
242**
243** Parameters:
244** p -- the string to translate. If NULL, return is
245** immediate.
246**
247** Returns:
248** none.
249**
250** Side Effects:
251** String pointed to by p is translated to lower case.
252**
b3cbe40f
EA
253** Called By:
254** parse
b3cbe40f
EA
255*/
256
257makelower(p)
258 register char *p;
259{
260 register char c;
261
262 if (p == NULL)
263 return;
264 for (; (c = *p) != '\0'; p++)
265 if ((c & 0200) == 0 && isupper(c))
266 *p = c - 'A' + 'a';
267}
268\f/*
269** PRESCAN -- Prescan name and make it canonical
270**
271** Scans a name and turns it into canonical form. This involves
272** deleting blanks, comments (in parentheses), and turning the
273** word "at" into an at-sign ("@"). The name is copied as this
274** is done; it is legal to copy a name onto itself, since this
275** process can only make things smaller.
276**
277** This routine knows about quoted strings and angle brackets.
278**
279** There are certain subtleties to this routine. The one that
280** comes to mind now is that backslashes on the ends of names
281** are silently stripped off; this is intentional. The problem
282** is that some versions of sndmsg (like at LBL) set the kill
283** character to something other than @ when reading addresses;
284** so people type "csvax.eric\@berkeley" -- which screws up the
285** berknet mailer.
286**
287** Parameters:
288** addr -- the name to chomp.
289** buf -- the buffer to copy it into.
290** buflim -- the last usable address in the buffer
291** (which will old a null byte). Normally
292** &buf[sizeof buf - 1].
293** delim -- the delimiter for the address, normally
294** '\0' or ','; \0 is accepted in any case.
295** are moving in place; set buflim to high core.
296**
297** Returns:
298** A pointer to the terminator of buf.
299** NULL on error.
300**
301** Side Effects:
302** buf gets clobbered.
303**
b3cbe40f
EA
304** Called By:
305** parse
306** maketemp
b3cbe40f
EA
307*/
308
309char *
310prescan(addr, buf, buflim, delim)
311 char *addr;
312 char *buf;
313 char *buflim;
314 char delim;
315{
316 register char *p;
317 bool space;
318 bool quotemode;
319 bool bslashmode;
320 int cmntcnt;
321 int brccnt;
322 register char c;
323 register char *q;
324 extern bool any();
325
326 space = TRUE;
327 q = buf;
328 bslashmode = quotemode = FALSE;
329 cmntcnt = brccnt = 0;
330 for (p = addr; (c = *p++ & 0177) != '\0'; )
331 {
332 /* chew up special characters */
333 *q = '\0';
334 if (bslashmode)
335 {
336 c |= 0200;
337 bslashmode == FALSE;
338 }
339 else if (c == '"')
340 quotemode = !quotemode;
341 else if (c == '\\')
342 {
343 bslashmode++;
344 continue;
345 }
346 else if (quotemode)
347 c |= 0200;
348 else if (c == delim)
349 break;
350 else if (c == '(')
351 cmntcnt++;
352 else if (c == ')')
353 {
354 if (cmntcnt <= 0)
355 {
356 usrerr("Unbalanced ')'");
357 return (NULL);
358 }
359 else
360 {
361 cmntcnt--;
362 continue;
363 }
364 }
365 if (cmntcnt > 0)
366 continue;
367 else if (c == '<')
368 {
369 brccnt++;
370 if (brccnt == 1)
371 {
372 /* we prefer using machine readable name */
373 q = buf;
374 *q = '\0';
375 continue;
376 }
377 }
378 else if (c == '>')
379 {
380 if (brccnt <= 0)
381 {
382 usrerr("Unbalanced `>'");
383 return (NULL);
384 }
385 else
386 brccnt--;
387 if (brccnt <= 0)
388 continue;
389 }
390
391 /*
392 ** Turn "at" into "@",
393 ** but only if "at" is a word in and to itself.
394 ** By the way, I violate the ARPANET RFC-733
395 ** standard here, by assuming that 'space' delimits
396 ** atoms. I assume that is just a mistake, since
397 ** it violates the spirit of the semantics
398 ** of the document.....
399 */
400
401 if (space && (c == 'a' || c == 'A') &&
402 (p[0] == 't' || p[0] == 'T') &&
403 (any(p[1], "()<>@,;:\\\"") || p[1] <= 040))
404 {
405 c = '@';
406 p++;
407 }
408
409 /* skip blanks */
410 if (((c & 0200) != 0 || !isspace(c)) && cmntcnt <= 0)
411 {
412 if (q >= buflim)
413 {
414 usrerr("Address too long");
415 return (NULL);
416 }
417 *q++ = c;
418 }
419 space = isspace(c);
420 }
421 *q = '\0';
422 if (c == '\0')
423 p--;
424 if (cmntcnt > 0)
425 usrerr("Unbalanced '('");
426 else if (quotemode)
427 usrerr("Unbalanced '\"'");
428 else if (brccnt > 0)
429 usrerr("Unbalanced '<'");
430 else if (buf[0] != '\0')
431 return (p);
432 return (NULL);
433}