1 # include <stdio.h>
2 # include <ctype.h>
3 # include "dlvrmail.h"
4 
5 static char	SccsId[] = "@(#)parseaddr.c	1.3	08/02/80";
6 
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 **
42 **	Called By:
43 **		main
44 **		sendto
45 **		alias
46 **		savemail
47 */
48 
49 addrq *
50 parse(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 /*
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 **
253 **	Called By:
254 **		parse
255 */
256 
257 makelower(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 /*
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 **
304 **	Called By:
305 **		parse
306 **		maketemp
307 */
308 
309 char *
310 prescan(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 }
434