Rearranged whitespace (and comments) in all the code!
[mmh] / sbr / addrsbr.c
1 /*
2  * addrsbr.c -- parse addresses 822-style
3  *
4  * This code is Copyright (c) 2002, by the authors of nmh.  See the
5  * COPYRIGHT file in the root directory of the nmh distribution for
6  * complete copyright information.
7  */
8
9 #include <h/mh.h>
10 #include <h/addrsbr.h>
11 #include <h/mf.h>
12
13 /*
14  * High level parsing of addresses:
15  *
16  * The routines in sbr/mf.c parse the syntactic representations of
17  * addresses.  The routines in sbr/addrsbr.c associate semantics with those
18  * addresses.
19  *
20  * If #ifdef DUMB is in effect, a full 822-style parser is called
21  * for syntax recongition.  This breaks each address into its components.
22  * Note however that no semantics are assumed about the parts or their
23  * totality.  This means that implicit hostnames aren't made explicit,
24  * and explicit hostnames aren't expanded to their "official" represenations.
25  *
26  * If DUMB is not in effect, then this module does some
27  * high-level thinking about what the addresses are.
28  *
29  * 1. for MMDF systems:
30  *
31  *      string%<uucp>@<local>  ->  string
32  *
33  * 2. for non-MMDF systems:
34  *
35  *      string@host.<uucp>  ->  host!string
36  *
37  * 3. for any system, an address interpreted relative to the local host:
38  *
39  *      string@<uucp>  ->  string
40  *
41  * For cases (1) and (3) above, the leftmost host is extracted.  If it's not
42  * present, the local host is used.  If the tests above fail, the address is
43  * considered to be a real 822-style address.
44  *
45  * If an explicit host is not present, then MH checks for a bang to indicate
46  * an explicit UUCP-style address.  If so, this is noted.  If not, the host is
47  * defaulted, typically to the local host.  The lack of an explict host is
48  * also noted.
49  *
50  * If an explicit 822-style host is present, then MH checks to see if it
51  * can expand this to the official name for the host.  If the hostname is
52  * unknown, the address is so typed.
53  *
54  * To summarize, when we're all done, here's what MH knows about the address:
55  *
56  * DUMB - type: local, uucp, or network
57  *              host:  not locally defaulted, not explicitly expanded
58  *              everything else
59  *
60  * other - type: local, uucp, network, unknown
61  *               everything else
62  */
63
64
65 static int  ingrp = 0;
66 static char *pers = NULL;
67 static char *mbox = NULL;
68 static char *host = NULL;
69 static char *route = NULL;
70 static char *grp = NULL;
71 static char *note = NULL;
72 static char err[BUFSIZ];
73 static char adr[BUFSIZ];
74
75
76 extern boolean  username_extension_masquerading;  /* defined in mts.c */
77
78
79 /*
80  * external prototypes
81  */
82 char *getusername (void);
83
84
85 char *
86 getname (char *addrs)
87 {
88         struct adrx *ap;
89
90         pers = mbox = host = route = grp = note = NULL;
91         err[0] = '\0';
92
93         if ((ap = getadrx (addrs ? addrs : "")) == NULL)
94                 return NULL;
95
96         strncpy (adr, ap->text, sizeof(adr));
97         pers = ap->pers;
98         mbox = ap->mbox;
99         host = ap->host;
100         route = ap->path;
101         grp = ap->grp;
102         ingrp = ap->ingrp;
103         note = ap->note;
104         if (ap->err && *ap->err)
105                 strncpy (err, ap->err, sizeof(err));
106
107         return adr;
108 }
109
110
111 struct mailname *
112 getm (char *str, char *dfhost, int dftype, int wanthost, char *eresult)
113 {
114         char *pp;
115         struct mailname *mp;
116 #ifndef DUMB
117         char *dp;
118 #endif /* not DUMB */
119
120         if (err[0]) {
121                 if (eresult)
122                         strcpy (eresult, err);
123                 else
124                         if (wanthost == AD_HOST)
125                                 admonish (NULL, "bad address '%s' - %s", str, err);
126                 return NULL;
127         }
128         if (pers == NULL && mbox == NULL && host == NULL && route == NULL
129                 && grp == NULL) {
130                 if (eresult)
131                         strcpy (eresult, "null address");
132                 else
133                         if (wanthost == AD_HOST)
134                                 admonish (NULL, "null address '%s'", str);
135                 return NULL;
136         }
137         if (mbox == NULL && grp == NULL) {
138                 if (eresult)
139                         strcpy (eresult, "no mailbox in address");
140                 else if (wanthost == AD_HOST)
141                         admonish (NULL, "no mailbox in address '%s'", str);
142                 return NULL;
143         }
144
145         if (dfhost == NULL) {
146                 dfhost = LocalName ();
147                 dftype = LOCALHOST;
148         }
149
150         mp = (struct mailname *) calloc ((size_t) 1, sizeof(*mp));
151         if (mp == NULL) {
152                 if (eresult)
153                         strcpy (eresult, "insufficient memory to represent address");
154                 else if (wanthost == AD_HOST)
155                         adios (NULL, "insufficient memory to represent address");
156                 return NULL;
157         }
158
159         mp->m_next = NULL;
160         mp->m_text = getcpy (str);
161         if (pers)
162                 mp->m_pers = getcpy (pers);
163
164         if (mbox == NULL) {
165                 mp->m_type = BADHOST;
166                 mp->m_nohost = 1;
167                 mp->m_ingrp = ingrp;
168                 mp->m_gname = getcpy (grp);
169                 if (note)
170                         mp->m_note = getcpy (note);
171                 return mp;
172         }
173
174         if (host) {
175                 mp->m_mbox = getcpy (mbox);
176                 mp->m_host = getcpy (host);
177         } else {
178                 if ((pp = strchr(mbox, '!'))) {
179                         *pp++ = '\0';
180                         mp->m_mbox = getcpy (pp);
181                         mp->m_host = getcpy (mbox);
182                         mp->m_type = UUCPHOST;
183                 } else {
184                         mp->m_nohost = 1;
185                         mp->m_mbox = getcpy (mbox);
186 #ifdef DUMB
187                         if (route == NULL && dftype == LOCALHOST) {
188                                 mp->m_host = NULL;
189                                 mp->m_type = dftype;
190                         } else
191 #endif /* DUMB */
192                         {
193                                 mp->m_host = route ? NULL : getcpy (dfhost);
194                                 mp->m_type = route ? NETHOST : dftype;
195                         }
196                 }
197                 goto got_host;
198         }
199
200         if (wanthost == AD_NHST)
201                 mp->m_type = !mh_strcasecmp (LocalName (), mp->m_host)
202                         ? LOCALHOST : NETHOST;
203 #ifdef DUMB
204         else
205                 mp->m_type = mh_strcasecmp (LocalName(), mp->m_host) ?  NETHOST : LOCALHOST;
206 #else /* not DUMB */
207         else
208                 if (pp = OfficialName (mp->m_host)) {
209         got_real_host: ;
210                         free (mp->m_host);
211                         mp->m_host = getcpy (pp);
212                         mp->m_type = mh_strcasecmp (LocalName(), mp->m_host) ? NETHOST : LOCALHOST;
213                 } else {
214                         if (dp = strchr(mp->m_host, '.')) {
215                                 *dp = NULL;
216                                 if (pp = OfficialName (mp->m_host))
217                                         goto got_real_host;
218                                 *dp = '.';
219                         }
220                         mp->m_type = BADHOST;
221                 }
222 #endif /* not DUMB */
223
224 got_host: ;
225         if (route)
226                 mp->m_path = getcpy (route);
227         mp->m_ingrp = ingrp;
228         if (grp)
229                 mp->m_gname = getcpy (grp);
230         if (note)
231                 mp->m_note = getcpy (note);
232
233         return mp;
234 }
235
236
237 void
238 mnfree (struct mailname *mp)
239 {
240         if (!mp)
241                 return;
242
243         if (mp->m_text)
244                 free (mp->m_text);
245         if (mp->m_pers)
246                 free (mp->m_pers);
247         if (mp->m_mbox)
248                 free (mp->m_mbox);
249         if (mp->m_host)
250                 free (mp->m_host);
251         if (mp->m_path)
252                 free (mp->m_path);
253         if (mp->m_gname)
254                 free (mp->m_gname);
255         if (mp->m_note)
256                 free (mp->m_note);
257
258         free ((char *) mp);
259 }
260
261
262 #define empty(s) ((s) ? (s) : "")
263
264 char *
265 auxformat (struct mailname *mp, int extras)
266 {
267         static char addr[BUFSIZ];
268         static char buffer[BUFSIZ];
269
270 #ifdef DUMB
271         if (mp->m_nohost)
272                 strncpy (addr, mp->m_mbox ? mp->m_mbox : "", sizeof(addr));
273         else
274 #endif /* DUMB */
275
276 #ifndef BANG
277         if (mp->m_type != UUCPHOST)
278                 snprintf (addr, sizeof(addr), mp->m_host ? "%s%s@%s" : "%s%s",
279                         empty(mp->m_path), empty(mp->m_mbox), mp->m_host);
280         else
281 #endif /* not BANG */
282                 snprintf (addr, sizeof(addr), "%s!%s", mp->m_host, mp->m_mbox);
283
284         if (!extras)
285                 return addr;
286
287         if (mp->m_pers || mp->m_path) {
288                 if (mp->m_note)
289                         snprintf (buffer, sizeof(buffer), "%s %s <%s>",
290                                 legal_person (mp->m_pers ? mp->m_pers : mp->m_mbox),
291                                 mp->m_note, addr);
292                 else
293                         snprintf (buffer, sizeof(buffer), "%s <%s>",
294                                 legal_person (mp->m_pers ? mp->m_pers : mp->m_mbox),
295                                 addr);
296         } else if (mp->m_note)
297                 snprintf (buffer, sizeof(buffer), "%s %s", addr, mp->m_note);
298         else
299                 strncpy (buffer, addr, sizeof(buffer));
300
301         return buffer;
302 }
303
304
305 /*
306  * address specific "sprintf"
307  */
308
309 char *
310 adrsprintf (char *username, char *domain)
311 {
312         int snprintf_return;
313         static char addr[BUFSIZ];
314
315         if (username == NULL)
316                 username = getusername();
317
318         if (username_extension_masquerading) {
319                 /* mts.conf contains "masquerade:[...]username_extension[...]",
320                  * so tack on the value of the $USERNAME_EXTENSION environment
321                  * variable, if set, to username.
322                  */
323                 char* extension = getenv("USERNAME_EXTENSION");
324                 static char username_with_extension[BUFSIZ];
325
326                 if (extension != NULL && *extension != '\0') {
327                         snprintf_return = snprintf(username_with_extension,
328                                 sizeof(username_with_extension),
329                                 "%s%s", username, extension);
330
331                         if (snprintf_return < 0 ||
332                                 snprintf_return >= sizeof(username_with_extension))
333                                 adios(NULL, "snprintf() error writing username (%d chars) and"
334                                           " $USERNAME_EXTENSION (%d chars) to array of BUFSIZ (%d)"
335                                           " chars",
336                                           strlen(username), strlen(extension), BUFSIZ);
337
338                         username = username_with_extension;
339                 }
340         }
341
342 #ifdef REALLYDUMB
343         return username;
344 #endif
345
346         if (domain == NULL)
347                 domain = LocalName();
348
349 #ifndef BANG
350         snprintf_return = snprintf (addr, sizeof(addr), "%s@%s", username, domain);
351 #else /* BANG */
352         snprintf_return = snprintf (addr, sizeof(addr), "%s!%s", domain, username);
353 #endif /* BANG */
354
355         if (snprintf_return < 0 || snprintf_return >= sizeof(addr))
356                 adios(NULL, "snprintf() error writing username (%d chars), domain (%d"
357                           " chars), and 1 separator char to array of BUFSIZ (%d) chars",
358                           strlen(username), strlen(domain), BUFSIZ);
359
360         return addr;
361 }
362
363
364 #define W_NIL   0x0000
365 #define W_MBEG  0x0001
366 #define W_MEND  0x0002
367 #define W_MBOX  (W_MBEG | W_MEND)
368 #define W_HBEG  0x0004
369 #define W_HEND  0x0008
370 #define W_HOST  (W_HBEG | W_HEND)
371 #define WBITS   "\020\01MBEG\02MEND\03HBEG\04HEND"
372
373 /*
374  * Check if this is my address
375  */
376
377 int
378 ismymbox (struct mailname *np)
379 {
380         int oops;
381         register int len, i;
382         register char *cp;
383         register char *pp;
384         char buffer[BUFSIZ];
385         struct mailname *mp;
386         static char *am = NULL;
387         static struct mailname mq={NULL};
388
389         /*
390          * If this is the first call, initialize
391          * list of alternate mailboxes.
392          */
393         if (am == NULL) {
394                 mq.m_next = NULL;
395                 mq.m_mbox = getusername ();
396                 if ((am = context_find ("alternate-mailboxes")) == NULL)
397                         am = getusername();
398                 else {
399                         mp = &mq;
400                         oops = 0;
401                         while ((cp = getname (am))) {
402                                 if ((mp->m_next = getm (cp, NULL, 0, AD_NAME, NULL)) == NULL) {
403                                         admonish (NULL, "illegal address: %s", cp);
404                                         oops++;
405                                 } else {
406                                         mp = mp->m_next;
407                                         mp->m_type = W_NIL;
408                                         if (*mp->m_mbox == '*') {
409                                                 mp->m_type |= W_MBEG;
410                                                 mp->m_mbox++;
411                                         }
412                                         if (*(cp = mp->m_mbox + strlen (mp->m_mbox) - 1) == '*') {
413                                                 mp->m_type |= W_MEND;
414                                                 *cp = '\0';
415                                         }
416                                         if (mp->m_host) {
417                                                 if (*mp->m_host == '*') {
418                                                         mp->m_type |= W_HBEG;
419                                                         mp->m_host++;
420                                                 }
421                                                 if (*(cp = mp->m_host + strlen (mp->m_host) - 1) == '*') {
422                                                         mp->m_type |= W_HEND;
423                                                         *cp = '\0';
424                                                 }
425                                         }
426                                         if ((cp = getenv ("MHWDEBUG")) && *cp)
427                                                 fprintf (stderr, "mbox=\"%s\" host=\"%s\" %s\n",
428                                                         mp->m_mbox, mp->m_host,
429                                                         snprintb (buffer, sizeof(buffer), (unsigned) mp->m_type, WBITS));
430                                 }
431                         }
432                         if (oops)
433                                 advise (NULL, "please fix the %s: entry in your %s file",
434                                         "alternate-mailboxes", mh_profile);
435                 }
436         }
437
438         if (np == NULL) /* XXX */
439                 return 0;
440
441         switch (np->m_type) {
442                 case NETHOST:
443                         len = strlen (cp = LocalName ());
444                         if (!uprf (np->m_host, cp) || np->m_host[len] != '.')
445                                 break;
446                         goto local_test;
447
448                 case UUCPHOST:
449                         if (mh_strcasecmp (np->m_host, SystemName()))
450                                 break;  /* fall */
451                 case LOCALHOST:
452 local_test: ;
453                         if (!mh_strcasecmp (np->m_mbox, mq.m_mbox))
454                                 return 1;
455                         break;
456
457                 default:
458                         break;
459         }
460
461         /*
462          * Now scan through list of alternate
463          * mailboxes, and check for a match.
464          */
465         for (mp = &mq; mp->m_next;) {
466                 mp = mp->m_next;
467                 if (!np->m_mbox)
468                         continue;
469                 if ((len = strlen (cp = np->m_mbox))
470                         < (i = strlen (pp = mp->m_mbox)))
471                         continue;
472                 switch (mp->m_type & W_MBOX) {
473                         case W_NIL:
474                                 if (mh_strcasecmp (cp, pp))
475                                         continue;
476                                 break;
477                         case W_MBEG:
478                                 if (mh_strcasecmp (cp + len - i, pp))
479                                         continue;
480                                 break;
481                         case W_MEND:
482                                 if (!uprf (cp, pp))
483                                         continue;
484                                 break;
485                         case W_MBEG | W_MEND:
486                                 if (stringdex (pp, cp) < 0)
487                                         continue;
488                                 break;
489                 }
490
491                 if (mp->m_nohost)
492                         return 1;
493                 if (np->m_host == NULL)
494                         continue;
495                 if ((len = strlen (cp = np->m_host))
496                         < (i = strlen (pp = mp->m_host)))
497                         continue;
498                 switch (mp->m_type & W_HOST) {
499                         case W_NIL:
500                                 if (mh_strcasecmp (cp, pp))
501                                         continue;
502                                 break;
503                         case W_HBEG:
504                                 if (mh_strcasecmp (cp + len - i, pp))
505                                         continue;
506                                 break;
507                         case W_HEND:
508                                 if (!uprf (cp, pp))
509                                         continue;
510                                 break;
511                         case W_HBEG | W_HEND:
512                                 if (stringdex (pp, cp) < 0)
513                                         continue;
514                                 break;
515                 }
516                 return 1;
517         }
518
519         return 0;
520 }
521
522
523 /*
524  * Moved from hosts.c -- find out the official name of a host
525  */
526
527 /*
528  * In the SendMail world, we really don't know what the valid
529  * hosts are.  We could poke around in the sendmail.cf file, but
530  * that still isn't a guarantee.  As a result, we'll say that
531  * everything is a valid host, and let SendMail worry about it.
532  */
533
534 #include <h/mts.h>
535 #include <netdb.h>
536
537
538 char *
539 OfficialName (char *name)
540 {
541         unsigned char *p;
542         char *q, site[BUFSIZ];
543         struct addrinfo hints, *res;
544
545         static char buffer[BUFSIZ];
546
547         for (p = name, q = site; *p && (q - site < sizeof(site) - 1); p++, q++)
548                 *q = isupper (*p) ? tolower (*p) : *p;
549         *q = '\0';
550         q = site;
551
552         if (!mh_strcasecmp (LocalName(), site))
553                 return LocalName();
554
555         memset(&hints, 0, sizeof(hints));
556         hints.ai_flags = AI_CANONNAME;
557         hints.ai_family = PF_UNSPEC;
558
559         if (getaddrinfo(q, NULL, &hints, &res) == 0) {
560                 strncpy (buffer, res->ai_canonname, sizeof(buffer));
561                 buffer[sizeof(buffer) - 1] = '\0';
562                 freeaddrinfo(res);
563                 return buffer;
564         }
565
566         strncpy (buffer, site, sizeof(buffer));
567         return buffer;
568 }