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