Fix long-standing problem with getans(); handle the case if we get
[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 ();
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 (), mp->m_host)
206             ? LOCALHOST : NETHOST;
207     else
208         mp->m_type = mh_strcasecmp (LocalName(), 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  * address specific "sprintf"
291  */
292
293 char *
294 adrsprintf (char *username, char *domain)
295 {
296     int          snprintf_return;
297     static char  addr[BUFSIZ];
298
299     if (username == NULL)
300         username = getusername();
301
302     if (username_extension_masquerading) {
303         /* mts.conf contains "masquerade:[...]username_extension[...]", so tack
304            on the value of the $USERNAME_EXTENSION environment variable, if set,
305            to username. */
306         char*        extension = getenv("USERNAME_EXTENSION");
307         static char  username_with_extension[BUFSIZ];
308
309         if (extension != NULL && *extension != '\0') {
310             snprintf_return = snprintf(username_with_extension,
311                                        sizeof(username_with_extension),
312                                        "%s%s", username, extension);
313             
314             if (snprintf_return < 0 ||
315                 snprintf_return >= (int) sizeof(username_with_extension))
316                 adios(NULL, "snprintf() error writing username (%d chars) and"
317                       " $USERNAME_EXTENSION (%d chars) to array of BUFSIZ (%d)"
318                       " chars",
319                       strlen(username), strlen(extension), BUFSIZ);
320             
321             username = username_with_extension;
322         }
323     }
324
325     return username;
326
327     if (domain == NULL)
328         domain = LocalName();
329
330     snprintf_return = snprintf (addr, sizeof(addr), "%s@%s", username, domain);
331
332     if (snprintf_return < 0 || snprintf_return >= (int) sizeof(addr))
333         adios(NULL, "snprintf() error writing username (%d chars), domain (%d"
334               " chars), and 1 separator char to array of BUFSIZ (%d) chars",
335               strlen(username), strlen(domain), BUFSIZ);
336     
337     return addr;
338 }
339
340
341 #define W_NIL   0x0000
342 #define W_MBEG  0x0001
343 #define W_MEND  0x0002
344 #define W_MBOX  (W_MBEG | W_MEND)
345 #define W_HBEG  0x0004
346 #define W_HEND  0x0008
347 #define W_HOST  (W_HBEG | W_HEND)
348 #define WBITS   "\020\01MBEG\02MEND\03HBEG\04HEND"
349
350 /*
351  * Check if this is my address
352  */
353
354 int
355 ismymbox (struct mailname *np)
356 {
357     int oops;
358     register int len, i;
359     register char *cp;
360     register char *pp;
361     char buffer[BUFSIZ];
362     struct mailname *mp;
363     static char *am = NULL;
364     static struct mailname mq;
365
366     /*
367      * If this is the first call, initialize
368      * list of alternate mailboxes.
369      */
370     if (am == NULL) {
371         mq.m_next = NULL;
372         mq.m_mbox = getusername ();
373         if ((am = context_find ("alternate-mailboxes")) == NULL)
374             am = getusername();
375         else {
376             mp = &mq;
377             oops = 0;
378             while ((cp = getname (am))) {
379                 if ((mp->m_next = getm (cp, NULL, 0, AD_NAME, NULL)) == NULL) {
380                     admonish (NULL, "illegal address: %s", cp);
381                     oops++;
382                 } else {
383                     mp = mp->m_next;
384                     mp->m_type = W_NIL;
385                     if (*mp->m_mbox == '*') {
386                         mp->m_type |= W_MBEG;
387                         mp->m_mbox++;
388                     }
389                     if (*(cp = mp->m_mbox + strlen (mp->m_mbox) - 1) == '*') {
390                         mp->m_type |= W_MEND;
391                         *cp = '\0';
392                     }
393                     if (mp->m_host) {
394                         if (*mp->m_host == '*') {
395                             mp->m_type |= W_HBEG;
396                             mp->m_host++;
397                         }
398                         if (*(cp = mp->m_host + strlen (mp->m_host) - 1) == '*') {
399                             mp->m_type |= W_HEND;
400                             *cp = '\0';
401                         }
402                     }
403                     if ((cp = getenv ("MHWDEBUG")) && *cp)
404                         fprintf (stderr, "mbox=\"%s\" host=\"%s\" %s\n",
405                             mp->m_mbox, mp->m_host,
406                             snprintb (buffer, sizeof(buffer), (unsigned) mp->m_type, WBITS));
407                 }
408             }
409             if (oops)
410                 advise (NULL, "please fix the %s: entry in your %s file",
411                         "alternate-mailboxes", mh_profile);
412         }
413     }
414
415     if (np == NULL) /* XXX */
416         return 0;
417     
418     switch (np->m_type) {
419         case NETHOST:
420             len = strlen (cp = LocalName ());
421             if (!uprf (np->m_host, cp) || np->m_host[len] != '.')
422                 break;
423             goto local_test;
424
425         case UUCPHOST:
426             if (mh_strcasecmp (np->m_host, SystemName()))
427                 break;          /* fall */
428         case LOCALHOST:
429 local_test: ;
430             if (!mh_strcasecmp (np->m_mbox, mq.m_mbox))
431                 return 1;
432             break;
433
434         default:
435             break;
436     }
437
438     /*
439      * Now scan through list of alternate
440      * mailboxes, and check for a match.
441      */
442     for (mp = &mq; mp->m_next;) {
443         mp = mp->m_next;
444         if (!np->m_mbox)
445             continue;
446         if ((len = strlen (cp = np->m_mbox))
447                 < (i = strlen (pp = mp->m_mbox)))
448             continue;
449         switch (mp->m_type & W_MBOX) {
450             case W_NIL: 
451                 if (mh_strcasecmp (cp, pp))
452                     continue;
453                 break;
454             case W_MBEG: 
455                 if (mh_strcasecmp (cp + len - i, pp))
456                     continue;
457                 break;
458             case W_MEND: 
459                 if (!uprf (cp, pp))
460                     continue;
461                 break;
462             case W_MBEG | W_MEND: 
463                 if (stringdex (pp, cp) < 0)
464                     continue;
465                 break;
466         }
467
468         if (mp->m_nohost)
469             return 1;
470         if (np->m_host == NULL)
471             continue;
472         if ((len = strlen (cp = np->m_host))
473                 < (i = strlen (pp = mp->m_host)))
474             continue;
475         switch (mp->m_type & W_HOST) {
476             case W_NIL: 
477                 if (mh_strcasecmp (cp, pp))
478                     continue;
479                 break;
480             case W_HBEG: 
481                 if (mh_strcasecmp (cp + len - i, pp))
482                     continue;
483                 break;
484             case W_HEND: 
485                 if (!uprf (cp, pp))
486                     continue;
487                 break;
488             case W_HBEG | W_HEND: 
489                 if (stringdex (pp, cp) < 0)
490                     continue;
491                 break;
492         }
493         return 1;
494     }
495
496     return 0;
497 }