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