7bf4673482d8f542aaebbdc2ea322d6ff3c16e54
[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     static int localmailbox = 0;
368
369     /*
370      * If this is the first call, initialize
371      * list of alternate mailboxes.
372      */
373     if (am == NULL) {
374         mq.m_next = NULL;
375         mq.m_mbox = getusername ();
376
377         if ((am = context_find ("local-mailbox"))) {
378
379             localmailbox++;
380
381             if ((cp = getname(am)) == NULL) {
382                 admonish (NULL, "Unable to find address in local-mailbox");
383                 return 0;
384             }
385
386             if ((mq.m_next = getm (cp, NULL, 0, AD_NAME, NULL)) == NULL) {
387                 admonish (NULL, "invalid entry in local-mailbox: %s", cp);
388                 return 0;
389             }
390
391             /*
392              * Sigh, it turns out that the address parser gets messed up
393              * if you don't call getname() until it returns NULL.
394              */
395
396             while ((cp = getname(am)) != NULL)
397                 ;
398         }
399
400         if ((am = context_find ("alternate-mailboxes")) == NULL)
401             am = getusername();
402         else {
403             mp = &mq;
404             oops = 0;
405             while ((cp = getname (am))) {
406                 if ((mp->m_next = getm (cp, NULL, 0, AD_NAME, NULL)) == NULL) {
407                     admonish (NULL, "illegal address: %s", cp);
408                     oops++;
409                 } else {
410                     mp = mp->m_next;
411                     mp->m_type = W_NIL;
412                     if (*mp->m_mbox == '*') {
413                         mp->m_type |= W_MBEG;
414                         mp->m_mbox++;
415                     }
416                     if (*(cp = mp->m_mbox + strlen (mp->m_mbox) - 1) == '*') {
417                         mp->m_type |= W_MEND;
418                         *cp = '\0';
419                     }
420                     if (mp->m_host) {
421                         if (*mp->m_host == '*') {
422                             mp->m_type |= W_HBEG;
423                             mp->m_host++;
424                         }
425                         if (*(cp = mp->m_host + strlen (mp->m_host) - 1) == '*') {
426                             mp->m_type |= W_HEND;
427                             *cp = '\0';
428                         }
429                     }
430                     if ((cp = getenv ("MHWDEBUG")) && *cp)
431                         fprintf (stderr, "mbox=\"%s\" host=\"%s\" %s\n",
432                             mp->m_mbox, mp->m_host,
433                             snprintb (buffer, sizeof(buffer), (unsigned) mp->m_type, WBITS));
434                 }
435             }
436             if (oops)
437                 advise (NULL, "please fix the %s: entry in your %s file",
438                         "alternate-mailboxes", mh_profile);
439         }
440     }
441
442     if (np == NULL) /* XXX */
443         return 0;
444     
445     /*
446      * Don't perform this "local" test if we have a Local-Mailbox set
447      */
448
449     if (! localmailbox)
450         switch (np->m_type) {
451             case NETHOST:
452                 len = strlen (cp = LocalName (0));
453                 if (!uprf (np->m_host, cp) || np->m_host[len] != '.')
454                     break;
455                 goto local_test;
456
457             case UUCPHOST:
458                 if (mh_strcasecmp (np->m_host, SystemName()))
459                     break;              /* fall */
460             case LOCALHOST:
461 local_test: ;
462                 if (!mh_strcasecmp (np->m_mbox, mq.m_mbox))
463                     return 1;
464                 break;
465
466             default:
467                 break;
468         }
469
470     /*
471      * Now scan through list of alternate
472      * mailboxes, and check for a match.
473      */
474     for (mp = &mq; mp->m_next;) {
475         mp = mp->m_next;
476         if (!np->m_mbox)
477             continue; if ((len = strlen (cp = np->m_mbox))
478                 < (i = strlen (pp = mp->m_mbox)))
479             continue;
480         switch (mp->m_type & W_MBOX) {
481             case W_NIL: 
482                 if (mh_strcasecmp (cp, pp))
483                     continue;
484                 break;
485             case W_MBEG: 
486                 if (mh_strcasecmp (cp + len - i, pp))
487                     continue;
488                 break;
489             case W_MEND: 
490                 if (!uprf (cp, pp))
491                     continue;
492                 break;
493             case W_MBEG | W_MEND: 
494                 if (stringdex (pp, cp) < 0)
495                     continue;
496                 break;
497         }
498
499         if (mp->m_nohost)
500             return 1;
501         if (np->m_host == NULL)
502             continue;
503         if ((len = strlen (cp = np->m_host))
504                 < (i = strlen (pp = mp->m_host)))
505             continue;
506         switch (mp->m_type & W_HOST) {
507             case W_NIL: 
508                 if (mh_strcasecmp (cp, pp))
509                     continue;
510                 break;
511             case W_HBEG: 
512                 if (mh_strcasecmp (cp + len - i, pp))
513                     continue;
514                 break;
515             case W_HEND: 
516                 if (!uprf (cp, pp))
517                     continue;
518                 break;
519             case W_HBEG | W_HEND: 
520                 if (stringdex (pp, cp) < 0)
521                     continue;
522                 break;
523         }
524         return 1;
525     }
526
527     return 0;
528 }