root/alias.c

Revision 5530:ffefb446142c, 14.7 kB (checked in by Rocco Rutte <pdmef@…>, 3 weeks ago)

Fix compiler warnings

Line 
1/*
2 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
3 *
4 *     This program is free software; you can redistribute it and/or modify
5 *     it under the terms of the GNU General Public License as published by
6 *     the Free Software Foundation; either version 2 of the License, or
7 *     (at your option) any later version.
8 *
9 *     This program is distributed in the hope that it will be useful,
10 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 *     GNU General Public License for more details.
13 *
14 *     You should have received a copy of the GNU General Public License
15 *     along with this program; if not, write to the Free Software
16 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 */ 
18
19#if HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include "mutt.h"
24#include "mutt_regex.h"
25#include "mutt_curses.h"
26#include "mutt_idna.h"
27
28#include <string.h>
29#include <ctype.h>
30
31ADDRESS *mutt_lookup_alias (const char *s)
32{
33  ALIAS *t = Aliases;
34
35  for (; t; t = t->next)
36    if (!mutt_strcasecmp (s, t->name))
37      return (t->addr);
38  return (NULL);   /* no such alias */
39}
40
41static ADDRESS *mutt_expand_aliases_r (ADDRESS *a, LIST **expn)
42{
43  ADDRESS *head = NULL, *last = NULL, *t, *w;
44  LIST *u;
45  char i;
46  const char *fqdn;
47 
48  while (a)
49  {
50    if (!a->group && !a->personal && a->mailbox && strchr (a->mailbox, '@') == NULL)
51    {
52      t = mutt_lookup_alias (a->mailbox);
53
54      if (t)
55      { 
56        i = 0;
57        for (u = *expn; u; u = u->next)
58        {
59          if (mutt_strcmp (a->mailbox, u->data) == 0) /* alias already found */
60          {
61            dprint (1, (debugfile, "mutt_expand_aliases_r(): loop in alias found for '%s'\n", a->mailbox));
62            i = 1;
63            break;
64          }
65        }
66
67        if (!i)
68        {
69          u = safe_malloc (sizeof (LIST));
70          u->data = safe_strdup (a->mailbox);
71          u->next = *expn;
72          *expn = u;
73          w = rfc822_cpy_adr (t);
74          w = mutt_expand_aliases_r (w, expn);
75          if (head)
76            last->next = w;
77          else
78            head = last = w;
79          while (last && last->next)
80            last = last->next;
81        }
82        t = a;
83        a = a->next;
84        t->next = NULL;
85        rfc822_free_address (&t);
86        continue;
87      }
88      else
89      {
90        struct passwd *pw = getpwnam (a->mailbox);
91
92        if (pw)
93        {
94          char namebuf[STRING];
95         
96          mutt_gecos_name (namebuf, sizeof (namebuf), pw);
97          mutt_str_replace (&a->personal, namebuf);
98         
99#ifdef EXACT_ADDRESS
100          FREE (&a->val);
101#endif
102        }
103      }
104    }
105
106    if (head)
107    {
108      last->next = a;
109      last = last->next;
110    }
111    else
112      head = last = a;
113    a = a->next;
114    last->next = NULL;
115  }
116
117  if (option (OPTUSEDOMAIN) && (fqdn = mutt_fqdn(1)))
118  {
119    /* now qualify all local addresses */
120    rfc822_qualify (head, fqdn);
121  }
122
123  return (head);
124}
125
126ADDRESS *mutt_expand_aliases (ADDRESS *a)
127{
128  ADDRESS *t;
129  LIST *expn = NULL; /* previously expanded aliases to avoid loops */
130
131  t = mutt_expand_aliases_r (a, &expn);
132  mutt_free_list (&expn);
133  return (mutt_remove_duplicates (t));
134}
135
136void mutt_expand_aliases_env (ENVELOPE *env)
137{
138  env->from = mutt_expand_aliases (env->from);
139  env->to = mutt_expand_aliases (env->to);
140  env->cc = mutt_expand_aliases (env->cc);
141  env->bcc = mutt_expand_aliases (env->bcc);
142  env->reply_to = mutt_expand_aliases (env->reply_to);
143  env->mail_followup_to = mutt_expand_aliases (env->mail_followup_to);
144}
145
146
147/*
148 * if someone has an address like
149 *      From: Michael `/bin/rm -f ~` Elkins <me@mutt.org>
150 * and the user creates an alias for this, Mutt could wind up executing
151 * the backticks because it writes aliases like
152 *      alias me Michael `/bin/rm -f ~` Elkins <me@mutt.org>
153 * To avoid this problem, use a backslash (\) to quote any backticks.  We also
154 * need to quote backslashes as well, since you could defeat the above by
155 * doing
156 *      From: Michael \`/bin/rm -f ~\` Elkins <me@mutt.org>
157 * since that would get aliased as
158 *      alias me Michael \\`/bin/rm -f ~\\` Elkins <me@mutt.org>
159 * which still gets evaluated because the double backslash is not a quote.
160 *
161 * Additionally, we need to quote ' and " characters - otherwise, mutt will
162 * interpret them on the wrong parsing step.
163 *
164 * $ wants to be quoted since it may indicate the start of an environment
165 * variable.
166 */
167
168static void write_safe_address (FILE *fp, char *s)
169{
170  while (*s)
171  {
172    if (*s == '\\' || *s == '`' || *s == '\'' || *s == '"'
173        || *s == '$')
174      fputc ('\\', fp);
175    fputc (*s, fp);
176    s++;
177  }
178}
179
180ADDRESS *mutt_get_address (ENVELOPE *env, char **pfxp)
181{
182  ADDRESS *adr;
183  char *pfx = NULL;
184
185  if (mutt_addr_is_user (env->from))
186  {
187    if (env->to && !mutt_is_mail_list (env->to))
188    {
189      pfx = "To";
190      adr = env->to;
191    }
192    else
193    {
194      pfx = "Cc";
195      adr = env->cc;
196    }
197  }
198  else if (env->reply_to && !mutt_is_mail_list (env->reply_to))
199  {
200    pfx = "Reply-To";
201    adr = env->reply_to;
202  }
203  else
204  {
205    adr = env->from;
206    pfx = "From";
207  }
208
209  if (pfxp) *pfxp = pfx;
210
211  return adr;
212}
213
214static void recode_buf (char *buf, size_t buflen)
215{
216  char *s;
217
218  if (!ConfigCharset || !*ConfigCharset || !Charset)
219    return;
220  s = safe_strdup (buf);
221  if (!s)
222    return;
223  if (mutt_convert_string (&s, Charset, ConfigCharset, 0) == 0)
224    strfcpy (buf, s, buflen);
225  FREE(&s);
226}
227
228void mutt_create_alias (ENVELOPE *cur, ADDRESS *iadr)
229{
230  ALIAS *new, *t;
231  char buf[LONG_STRING], tmp[LONG_STRING], prompt[SHORT_STRING], *pc;
232  char *err = NULL;
233  char fixed[LONG_STRING];
234  FILE *rc;
235  ADDRESS *adr = NULL;
236
237  if (cur)
238  {
239    adr = mutt_get_address (cur, NULL);
240  }
241  else if (iadr)
242  {
243    adr = iadr;
244  }
245
246  if (adr && adr->mailbox)
247  {
248    strfcpy (tmp, adr->mailbox, sizeof (tmp));
249    if ((pc = strchr (tmp, '@')))
250      *pc = 0;
251  }
252  else
253    tmp[0] = '\0';
254
255  /* Don't suggest a bad alias name in the event of a strange local part. */
256  mutt_check_alias_name (tmp, buf, sizeof (buf));
257 
258retry_name:
259  /* add a new alias */
260  if (mutt_get_field (_("Alias as: "), buf, sizeof (buf), 0) != 0 || !buf[0])
261    return;
262
263  /* check to see if the user already has an alias defined */
264  if (mutt_lookup_alias (buf))
265  {
266    mutt_error _("You already have an alias defined with that name!");
267    return;
268  }
269 
270  if (mutt_check_alias_name (buf, fixed, sizeof (fixed)))
271  {
272    switch (mutt_yesorno (_("Warning: This alias name may not work.  Fix it?"), M_YES))
273    {
274      case M_YES:
275        strfcpy (buf, fixed, sizeof (buf));
276        goto retry_name;
277      case -1:
278        return;
279    }
280  }
281 
282  new       = safe_calloc (1, sizeof (ALIAS));
283  new->self = new;
284  new->name = safe_strdup (buf);
285
286  mutt_addrlist_to_local (adr);
287 
288  if (adr)
289    strfcpy (buf, adr->mailbox, sizeof (buf));
290  else
291    buf[0] = 0;
292
293  mutt_addrlist_to_idna (adr, NULL);
294 
295  do
296  {
297    if (mutt_get_field (_("Address: "), buf, sizeof (buf), 0) != 0 || !buf[0])
298    {
299      mutt_free_alias (&new);
300      return;
301    }
302   
303    if((new->addr = rfc822_parse_adrlist (new->addr, buf)) == NULL)
304      BEEP ();
305    if (mutt_addrlist_to_idna (new->addr, &err))
306    {
307      mutt_error (_("Error: '%s' is a bad IDN."), err);
308      mutt_sleep (2);
309      continue;
310    }
311  }
312  while(new->addr == NULL);
313 
314  if (adr && adr->personal && !mutt_is_mail_list (adr))
315    strfcpy (buf, adr->personal, sizeof (buf));
316  else
317    buf[0] = 0;
318
319  if (mutt_get_field (_("Personal name: "), buf, sizeof (buf), 0) != 0)
320  {
321    mutt_free_alias (&new);
322    return;
323  }
324  new->addr->personal = safe_strdup (buf);
325
326  buf[0] = 0;
327  rfc822_write_address (buf, sizeof (buf), new->addr, 1);
328  snprintf (prompt, sizeof (prompt), _("[%s = %s] Accept?"), new->name, buf);
329  if (mutt_yesorno (prompt, M_YES) != M_YES)
330  {
331    mutt_free_alias (&new);
332    return;
333  }
334
335  mutt_alias_add_reverse (new);
336 
337  if ((t = Aliases))
338  {
339    while (t->next)
340      t = t->next;
341    t->next = new;
342  }
343  else
344    Aliases = new;
345
346  strfcpy (buf, NONULL (AliasFile), sizeof (buf));
347  if (mutt_get_field (_("Save to file: "), buf, sizeof (buf), M_FILE) != 0)
348    return;
349  mutt_expand_path (buf, sizeof (buf));
350  if ((rc = fopen (buf, "a+")))
351  {
352    /* terminate existing file with \n if necessary */
353    if (fseek (rc, 0, SEEK_END))
354      goto fseek_err;
355    if (ftell(rc) > 0)
356    {
357      if (fseek (rc, -1, SEEK_CUR) < 0)
358        goto fseek_err;
359      if (fread(buf, 1, 1, rc) < 0)
360      {
361        mutt_perror (_("Error reading alias file"));
362        return;
363      }
364      if (fseek (rc, 0, SEEK_END) < 0)
365        goto fseek_err;
366      if (buf[0] != '\n')
367        fputc ('\n', rc);
368    }
369
370    if (mutt_check_alias_name (new->name, NULL, 0))
371      mutt_quote_filename (buf, sizeof (buf), new->name);
372    else
373      strfcpy (buf, new->name, sizeof (buf));
374    recode_buf (buf, sizeof (buf));
375    fprintf (rc, "alias %s ", buf);
376    buf[0] = 0;
377    rfc822_write_address (buf, sizeof (buf), new->addr, 0);
378    recode_buf (buf, sizeof (buf));
379    write_safe_address (rc, buf);
380    fputc ('\n', rc);
381    fclose (rc);
382    mutt_message _("Alias added.");
383  }
384  else
385    mutt_perror (buf);
386
387  return;
388 
389  fseek_err:
390  mutt_perror (_("Error seeking in alias file"));
391  fclose(rc);
392  return;
393}
394
395/*
396 * Sanity-check an alias name:  Only characters which are non-special to both
397 * the RFC 822 and the mutt configuration parser are permitted.
398 */
399
400int mutt_check_alias_name (const char *s, char *dest, size_t destlen)
401{
402  wchar_t wc;
403  mbstate_t mb;
404  size_t l;
405  int rv = 0, bad = 0, dry = !dest || !destlen;
406
407  memset (&mb, 0, sizeof (mbstate_t));
408
409  if (!dry)
410    destlen--;
411  for (; s && *s && (dry || destlen) &&
412       (l = mbrtowc (&wc, s, MB_CUR_MAX, &mb)) != 0;
413       s += l, destlen -= l)
414  {
415    bad = l == (size_t)(-1) || l == (size_t)(-2); /* conversion error */
416    bad = bad || (!dry && l > destlen);         /* too few room for mb char */
417    if (l == 1)
418      bad = bad || (strchr ("-_+=.", *s) == NULL && !iswalnum (wc));
419    else
420      bad = bad || !iswalnum (wc);
421    if (bad)
422    {
423      if (dry)
424        return -1;
425      *dest++ = '_';
426      rv = -1;
427    }
428    else if (!dry)
429    {
430      memcpy (dest, s, l);
431      dest += l;
432    }
433  }
434  if (!dry)
435    *dest = 0;
436  return rv;
437}
438
439/*
440 * This routine looks to see if the user has an alias defined for the given
441 * address.
442 */
443ADDRESS *alias_reverse_lookup (ADDRESS *a)
444{
445  if (!a || !a->mailbox)
446      return NULL;
447 
448  return hash_find (ReverseAlias, a->mailbox);
449}
450
451void mutt_alias_add_reverse (ALIAS *t)
452{
453  ADDRESS *ap;
454  if (!t)
455    return;
456 
457  for (ap = t->addr; ap; ap = ap->next)
458  {
459    if (!ap->group && ap->mailbox)
460      hash_insert (ReverseAlias, ap->mailbox, ap, 1);
461  }
462}
463
464void mutt_alias_delete_reverse (ALIAS *t)
465{
466  ADDRESS *ap;
467  if (!t)
468    return;
469 
470  for (ap = t->addr; ap; ap = ap->next)
471  {
472    if (!ap->group && ap->mailbox)
473      hash_delete (ReverseAlias, ap->mailbox, ap, NULL);
474  }
475}
476
477/* alias_complete() -- alias completion routine
478 *
479 * given a partial alias, this routine attempts to fill in the alias
480 * from the alias list as much as possible. if given empty search string
481 * or found nothing, present all aliases
482 */
483int mutt_alias_complete (char *s, size_t buflen)
484{
485  ALIAS *a = Aliases;
486  ALIAS *a_list = NULL, *a_cur = NULL;
487  char bestname[HUGE_STRING];
488  int i;
489
490#define min(a,b)        ((a<b)?a:b)
491
492  if (s[0] != 0) /* avoid empty string as strstr argument */
493  {
494    memset (bestname, 0, sizeof (bestname));
495
496    while (a)
497    {
498      if (a->name && strstr (a->name, s) == a->name)
499      {
500        if (!bestname[0]) /* init */
501          strfcpy (bestname, a->name,
502                   min (mutt_strlen (a->name) + 1, sizeof (bestname)));
503        else
504        {
505          for (i = 0 ; a->name[i] && a->name[i] == bestname[i] ; i++)
506            ;
507          bestname[i] = 0;
508        }
509      }
510      a = a->next;
511    }
512
513    if (bestname[0] != 0)
514    {
515      if (mutt_strcmp (bestname, s) != 0)
516      {
517        /* we are adding something to the completion */
518        strfcpy (s, bestname, mutt_strlen (bestname) + 1);
519        return 1;
520      }
521
522      /* build alias list and show it */
523
524      a = Aliases;
525      while (a)
526      {
527        if (a->name && (strstr (a->name, s) == a->name))
528        {
529          if (!a_list)  /* init */
530            a_cur = a_list = (ALIAS *) safe_malloc (sizeof (ALIAS));
531          else
532          {
533            a_cur->next = (ALIAS *) safe_malloc (sizeof (ALIAS));
534            a_cur = a_cur->next;
535          }
536          memcpy (a_cur, a, sizeof (ALIAS));
537          a_cur->next = NULL;
538        }
539        a = a->next;
540      }
541    }
542  }
543
544  bestname[0] = 0;
545  mutt_alias_menu (bestname, sizeof(bestname), a_list ? a_list : Aliases);
546  if (bestname[0] != 0)
547    strfcpy (s, bestname, buflen);
548
549  /* free the alias list */
550  while (a_list)
551  {
552    a_cur = a_list;
553    a_list = a_list->next;
554    FREE (&a_cur);
555  }
556
557  /* remove any aliases marked for deletion */
558  a_list = NULL;
559  for (a_cur = Aliases; a_cur;)
560  {
561    if (a_cur->del)
562    {
563      if (a_list)
564        a_list->next = a_cur->next;
565      else
566        Aliases = a_cur->next;
567     
568      a_cur->next = NULL;
569      mutt_free_alias (&a_cur);
570     
571      if (a_list)
572        a_cur = a_list;
573      else
574        a_cur = Aliases;
575    }
576    else
577    {
578      a_list = a_cur;
579      a_cur  = a_cur->next;
580    }
581  }
582 
583  return 0;
584}
585
586static int string_is_address(const char *str, const char *u, const char *d)
587{
588  char buf[LONG_STRING];
589 
590  snprintf(buf, sizeof(buf), "%s@%s", NONULL(u), NONULL(d));
591  if (ascii_strcasecmp(str, buf) == 0)
592    return 1;
593 
594  return 0;
595}
596
597/* returns TRUE if the given address belongs to the user. */
598int mutt_addr_is_user (ADDRESS *addr)
599{
600  const char *fqdn;
601
602  /* NULL address is assumed to be the user. */
603  if (!addr)
604  {
605    dprint (5, (debugfile, "mutt_addr_is_user: yes, NULL address\n"));
606    return 1;
607  }
608  if (!addr->mailbox)
609  {
610    dprint (5, (debugfile, "mutt_addr_is_user: no, no mailbox\n"));
611    return 0;
612  }
613
614  if (ascii_strcasecmp (addr->mailbox, Username) == 0)
615  {
616    dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, Username));
617    return 1;
618  }
619  if (string_is_address(addr->mailbox, Username, Hostname))
620  {
621    dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, Hostname));
622    return 1;
623  }
624  fqdn = mutt_fqdn (0);
625  if (string_is_address(addr->mailbox, Username, fqdn))
626  {
627    dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, NONULL(fqdn)));
628    return 1;
629  }
630  fqdn = mutt_fqdn (1);
631  if (string_is_address(addr->mailbox, Username, fqdn))
632  {
633    dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, NONULL(fqdn)));
634    return 1;
635  }
636
637  if (From && !ascii_strcasecmp (From->mailbox, addr->mailbox))
638  {
639    dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, From->mailbox));
640    return 1;
641  }
642
643  if (mutt_match_rx_list (addr->mailbox, Alternates))
644  {
645    dprint (5, (debugfile, "mutt_addr_is_user: yes, %s matched by alternates.\n", addr->mailbox));
646    if (mutt_match_rx_list (addr->mailbox, UnAlternates))
647      dprint (5, (debugfile, "mutt_addr_is_user: but, %s matched by unalternates.\n", addr->mailbox));
648    else
649      return 1;
650  }
651 
652  dprint (5, (debugfile, "mutt_addr_is_user: no, all failed.\n"));
653  return 0;
654}
Note: See TracBrowser for help on using the browser.