Beginning of support for the Local-Mailbox profile entry.
[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 ((mptr = getm (am, NULL, 0, AD_NAME, NULL)) == NULL) {
377                 admonish (NULL, "invalid entry in local-mailbox: %s", am);
378                 return 0;
379             }
380
381             /*
382              * Yes, we're not freeing the whole entry, because all of those
383              * elements contain allocated pointers that we need; maybe
384              * later ...
385              */
386
387             mq = *mptr;
388
389             free(mptr);
390         } else {
391             mq.m_mbox = getusername ();
392         }
393
394         mq.m_next = NULL;
395
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 (0));
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 }