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