+++ /dev/null
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
- *
- * Copyright © 2006 Pierre Habouzit
- */
-/*
- * Copyright notice from original mutt:
- * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
- *
- * This file is part of mutt-ng, see http://www.muttng.org/.
- * It's licensed under the GNU General Public License,
- * please see the file GPL in the top level source directory.
- */
-
-#include <lib-lib/lib-lib.h>
-
-#include <lib-sys/unix.h>
-
-#include <lib-ui/curses.h>
-#include <lib-ui/enter.h>
-#include <lib-ui/menu.h>
-
-#include "alias.h"
-#include "mutt_idna.h"
-#include "sort.h"
-
-char *AliasFmt;
-char *AliasFile;
-alias_t *Aliases;
-rx_t GecosMask;
-
-#define RSORT(x) (SortAlias & SORT_REVERSE) ? -x : x
-
-static struct mapping_t AliasHelp[] = {
- {N_("Exit"), OP_EXIT},
- {N_("Del"), OP_DELETE},
- {N_("Undel"), OP_UNDELETE},
- {N_("Select"), OP_GENERIC_SELECT_ENTRY},
- {N_("Help"), OP_HELP},
- {NULL, OP_NULL}
-};
-
-static void mutt_alias_menu(char *, size_t, alias_t *);
-
-const address_t *alias_lookup(const char *s)
-{
- alias_t *list;
-
- for (list = Aliases; list; list = list->next) {
- if (!m_strcasecmp(s, list->name))
- return list->addr;
- }
-
- return NULL;
-}
-
-/* This routine looks to see if the user has an alias defined for the given
- address. */
-const address_t *alias_reverse_lookup(const address_t *a)
-{
- alias_t *list;
-
- if (!a || !a->mailbox)
- return NULL;
-
- for (list = Aliases; list; list = list->next) {
- address_t *ap;
-
- /* cycle through all addresses if this is a group alias */
- for (ap = list->addr; ap; ap = ap->next) {
- if (!ap->group && ap->mailbox
- && !ascii_strcasecmp(ap->mailbox, a->mailbox))
- return ap;
- }
- }
-
- return NULL;
-}
-
-static int string_is_address(const char *str, const char *u, const char *d)
-{
- char buf[LONG_STRING];
- snprintf(buf, sizeof (buf), "%s@%s", NONULL(u), NONULL(d));
- return !ascii_strcasecmp(str, buf);
-}
-
-/* returns TRUE if the given address belongs to the user. */
-int mutt_addr_is_user(address_t *addr)
-{
- /* NULL address is assumed to be the user. */
- if (!addr)
- return 1;
-
- if (!addr->mailbox)
- return 0;
-
- if (!ascii_strcasecmp(addr->mailbox, MCore.username)
- || string_is_address(addr->mailbox, MCore.username, Hostname)
- || string_is_address(addr->mailbox, MCore.username, mutt_fqdn(0))
- || string_is_address(addr->mailbox, MCore.username, mutt_fqdn(1))
- || (From && !ascii_strcasecmp(From->mailbox, addr->mailbox)))
- {
- return 1;
- }
-
- return rx_list_match(Alternates, addr->mailbox)
- && !rx_list_match(UnAlternates, addr->mailbox);
-}
-
-address_t *mutt_get_address(ENVELOPE *env, const char **pfxp)
-{
-#define RETURN(s, adr) do { if (pfxp) *pfxp = s; return adr; } while (0)
-
- if (mutt_addr_is_user(env->from)) {
- if (env->to && !mutt_is_mail_list(env->to)) {
- RETURN("To", env->to);
- } else {
- RETURN("Cc", env->cc);
- }
- } else {
- if (env->reply_to && !mutt_is_mail_list(env->reply_to)) {
- RETURN("Reply-To", env->reply_to);
- } else {
- RETURN("From", env->from);
- }
- }
-
-#undef RETURN
-}
-
-/* Only characters which are non-special to both the RFC 822 and the mutt
- configuration parser are permitted. */
-static int alias_sanitize(const char *s, char *d)
-{
- int rv = 0;
-
- while (*s) {
- if (isalnum((unsigned char)(*s)) || strchr("-_+=.", *s)) {
- if (d)
- *d++ = *s;
- } else {
- if (!d)
- return -1;
- *d++ = '_';
- rv = -1;
- }
- s++;
- }
-
- if (d)
- *d = '\0';
- return rv;
-}
-
-/*
- * if someone has an address like
- * From: Michael `/bin/rm -f ~` Elkins <me@mutt.org>
- * and the user creates an alias for this, Mutt could wind up executing
- * the backtics because it writes aliases like
- * alias me Michael `/bin/rm -f ~` Elkins <me@mutt.org>
- * To avoid this problem, use a backslash (\) to quote any backtics. We also
- * need to quote backslashes as well, since you could defeat the above by
- * doing
- * From: Michael \`/bin/rm -f ~\` Elkins <me@mutt.org>
- * since that would get aliased as
- * alias me Michael \\`/bin/rm -f ~\\` Elkins <me@mutt.org>
- * which still gets evaluated because the double backslash is not a quote.
- *
- * Additionally, we need to quote ' and " characters - otherwise, mutt will
- * interpret them on the wrong parsing step.
- *
- * $ wants to be quoted since it may indicate the start of an environment
- * variable.
- */
-static void write_safe_address(FILE *fp, const char *s)
-{
- while (*s) {
- if (strchr("\\`'\"$", *s)) {
- fputc('\\', fp);
- }
- fputc(*s++, fp);
- }
- fputc('\n', fp);
-}
-
-void mutt_create_alias(ENVELOPE *cur, address_t *iadr)
-{
- char buf[LONG_STRING], prompt[STRING];
- address_t *adr = iadr;
- alias_t *new;
- FILE *rc;
-
- if (cur) {
- adr = mutt_get_address(cur, NULL);
- }
-
- if (adr && adr->mailbox) {
- const char *p = m_strchrnul(adr->mailbox, '@');
- m_strncpy(buf, sizeof(buf), adr->mailbox, p - adr->mailbox);
- } else {
- buf[0] = '\0';
- }
-
- /* Don't suggest a bad alias name in the event of a strange local part. */
- alias_sanitize(buf, buf);
-
- /* add a new alias */
- if (mutt_get_field(_("Alias as: "), buf, sizeof(buf), 0) || !buf[0])
- return;
-
- /* check to see if the user already has an alias defined */
- if (alias_lookup(buf)) {
- mutt_error _("You already have an alias defined with that name!");
- return;
- }
-
- alias_sanitize(buf, buf);
- new = alias_new();
- new->name = m_strdup(buf);
-
- mutt_addrlist_to_local(adr);
- if (adr) {
- m_strcpy(buf, sizeof(buf), adr->mailbox);
- } else {
- buf[0] = 0;
- }
-
- mutt_addrlist_to_idna(adr, NULL);
-
- do {
- char *err = NULL;
-
- if (mutt_get_field(_("Address: "), buf, sizeof(buf), 0) || !buf[0]) {
- alias_list_wipe(&new);
- return;
- }
-
- new->addr = rfc822_parse_adrlist(new->addr, buf);
- if (!new->addr)
- BEEP();
-
- if (mutt_addrlist_to_idna(new->addr, &err)) {
- mutt_error(_("Error: '%s' is a bad IDN."), err);
- p_delete(&err);
- mutt_sleep(1);
- continue;
- }
- } while (!new->addr);
-
- if (adr && adr->personal && !mutt_is_mail_list(adr)) {
- m_strcpy(buf, sizeof(buf), adr->personal);
- } else {
- buf[0] = '\0';
- }
-
- if (mutt_get_field(_("Personal name: "), buf, sizeof(buf), 0)) {
- alias_list_wipe(&new);
- return;
- }
- new->addr->personal = m_strdup(buf);
-
- buf[0] = '\0';
- rfc822_addrcat(buf, sizeof(buf), new->addr, 1);
- snprintf(prompt, sizeof(prompt), _("[%s = %s] Accept?"), new->name, buf);
- if (mutt_yesorno(prompt, M_YES) != M_YES) {
- alias_list_wipe(&new);
- return;
- }
-
- alias_list_push(&Aliases, new);
-
- m_strcpy(buf, sizeof(buf), NONULL(AliasFile));
- if (mutt_get_field(_("Save to file: "), buf, sizeof(buf), M_FILE)) {
- return;
- }
-
- mutt_expand_path(buf, sizeof(buf));
- rc = safe_fopen (buf, "a");
-
- if (rc) {
- if (alias_sanitize(new->name, NULL)) {
- mutt_quote_filename(buf, sizeof(buf), new->name);
- fprintf(rc, "alias %s ", buf);
- } else {
- fprintf(rc, "alias %s ", new->name);
- }
-
- buf[0] = '\0';
- rfc822_addrcat(buf, sizeof(buf), new->addr, 0);
- write_safe_address(rc, buf);
- m_fclose(&rc);
- mutt_message _("Alias added.");
- } else {
- mutt_perror(buf);
- }
-}
-
-static address_t *mutt_expand_aliases_r(address_t *a, string_list_t **expn)
-{
- address_t *pop, *head = NULL;
- address_t **last = &head;
-
- while ((pop = address_list_pop(&a))) {
- if (!pop->group && !pop->personal
- && pop->mailbox && !strchr(pop->mailbox, '@'))
- {
- const address_t *t = alias_lookup(pop->mailbox);
-
- if (t) {
- string_list_t *u;
-
- for (u = *expn; u; u = u->next) {
- if (!m_strcmp(pop->mailbox, u->data)) { /* alias already found */
- address_list_wipe(&pop);
- continue;
- }
- }
-
- /* save the fact we saw it */
- u = string_item_new();
- u->data = m_strdup(pop->mailbox);
- u->next = *expn;
- *expn = u;
- address_list_wipe(&pop);
-
- /* recurse */
- last = address_list_last(last);
- *last = mutt_expand_aliases_r(address_list_dup(t), expn);
- continue;
- } else {
- struct passwd *pw = getpwnam(pop->mailbox);
-
- if (pw) {
- char namebuf[STRING];
- mutt_gecos_name(namebuf, sizeof(namebuf), pw, GecosMask.rx);
- m_strreplace(&pop->personal, namebuf);
- }
- }
- }
-
- last = address_list_append(last, pop);
- }
-
- if (option(OPTUSEDOMAIN)) {
- /* now qualify all local addresses */
- rfc822_qualify(head, mutt_fqdn(1));
- }
-
- return head;
-}
-
-address_t *mutt_expand_aliases(address_t *a)
-{
- address_t *t;
- string_list_t *expn = NULL; /* previously expanded aliases to avoid loops */
-
- t = mutt_expand_aliases_r(a, &expn);
- string_list_wipe(&expn);
- address_list_uniq(t);
- return t;
-}
-
-void mutt_expand_aliases_env(ENVELOPE *env)
-{
- env->from = mutt_expand_aliases(env->from);
- env->to = mutt_expand_aliases(env->to);
- env->cc = mutt_expand_aliases(env->cc);
- env->bcc = mutt_expand_aliases(env->bcc);
- env->reply_to = mutt_expand_aliases(env->reply_to);
- env->mail_followup_to = mutt_expand_aliases(env->mail_followup_to);
-}
-
-/************* READ MARK *********************/
-
-/* alias_complete() -- alias completion routine
- *
- * given a partial alias, this routine attempts to fill in the alias
- * from the alias list as much as possible. if given empty search string
- * or found nothing, present all aliases
- */
-int mutt_alias_complete (char *s, size_t buflen)
-{
- alias_t *a = Aliases;
- alias_t *a_list = NULL, *a_cur = NULL;
- char bestname[HUGE_STRING];
- int i;
-
-#define min(a,b) ((a<b)?a:b)
-
- if (s[0] != 0) { /* avoid empty string as strstr argument */
- p_clear(bestname, countof(bestname));
-
- while (a) {
- if (a->name && strstr (a->name, s) == a->name) {
- if (!bestname[0]) /* init */
- m_strcpy(bestname, MIN(m_strlen(a->name) + 1, ssizeof(bestname)),
- a->name);
- else {
- for (i = 0; a->name[i] && a->name[i] == bestname[i]; i++);
- bestname[i] = 0;
- }
- }
- a = a->next;
- }
-
- if (bestname[0] != 0) {
- if (m_strcmp(bestname, s) != 0) {
- /* we are adding something to the completion */
- m_strcpy(s, m_strlen(bestname) + 1, bestname);
- return 1;
- }
-
- /* build alias list and show it */
-
- a = Aliases;
- while (a) {
- if (a->name && (strstr (a->name, s) == a->name)) {
- if (!a_list) /* init */
- a_cur = a_list = alias_new();
- else {
- a_cur->next = alias_new();
- a_cur = a_cur->next;
- }
- *a_cur = *a;
- a_cur->next = NULL;
- }
- a = a->next;
- }
- }
- }
-
- bestname[0] = 0;
- mutt_alias_menu (bestname, sizeof (bestname), a_list ? a_list : Aliases);
- if (bestname[0] != 0)
- m_strcpy(s, buflen, bestname);
-
- /* free the alias list */
- while (a_list) {
- a_cur = a_list;
- a_list = a_list->next;
- p_delete(&a_cur);
- }
-
- /* remove any aliases marked for deletion */
- a_list = NULL;
- for (a_cur = Aliases; a_cur;) {
- if (a_cur->del) {
- if (a_list)
- a_list->next = a_cur->next;
- else
- Aliases = a_cur->next;
-
- a_cur->next = NULL;
- alias_list_wipe(&a_cur);
-
- if (a_list)
- a_cur = a_list;
- else
- a_cur = Aliases;
- }
- else {
- a_list = a_cur;
- a_cur = a_cur->next;
- }
- }
-
- return 0;
-}
-
-static const char *
-alias_format_str(char *dest, ssize_t destlen, char op, const char *src,
- const char *fmt, const char *ifstr __attribute__ ((unused)),
- const char *elstr __attribute__ ((unused)),
- anytype data, format_flag flags __attribute__ ((unused)))
-{
- char tmp[STRING], adr[STRING];
- alias_t *alias = data.ptr;
-
- switch (op) {
- case 'f':
- m_strputc(dest, destlen, alias->del ? 'D' : ' ');
- break;
- case 'a':
- mutt_format_s(dest, destlen, fmt, alias->name);
- break;
- case 'r':
- adr[0] = '\0';
- rfc822_addrcat(adr, sizeof(adr), alias->addr, 1);
- snprintf(tmp, sizeof(tmp), "%%%ss", fmt);
- snprintf(dest, destlen, tmp, adr);
- break;
- case 'n':
- snprintf(tmp, sizeof(tmp), "%%%sd", fmt);
- snprintf(dest, destlen, tmp, alias->num + 1);
- break;
- case 't':
- m_strputc(dest, destlen, alias->tagged ? '*' : ' ');
- break;
- }
-
- return src;
-}
-
-static void alias_entry(char *s, ssize_t slen, MUTTMENU *m, int num)
-{
- m_strformat(s, slen, COLS - SW, AliasFmt, alias_format_str,
- ((alias_t **)m->data)[num],
- option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
-}
-
-static int alias_tag (MUTTMENU * menu, int n, int m)
-{
- alias_t *cur = ((alias_t **) menu->data)[n];
- int ot = cur->tagged;
-
- cur->tagged = (m >= 0 ? m : !cur->tagged);
-
- return cur->tagged - ot;
-}
-
-static int alias_SortAlias (const void *a, const void *b)
-{
- alias_t *pa = *(alias_t **) a;
- alias_t *pb = *(alias_t **) b;
- int r = m_strcasecmp(pa->name, pb->name);
-
- return (RSORT (r));
-}
-
-static int alias_SortAddress (const void *a, const void *b)
-{
- address_t *pa = (*(alias_t **) a)->addr;
- address_t *pb = (*(alias_t **) b)->addr;
- int r;
-
- if (pa == pb)
- r = 0;
- else if (pa == NULL)
- r = -1;
- else if (pb == NULL)
- r = 1;
- else if (pa->personal) {
- if (pb->personal)
- r = m_strcasecmp(pa->personal, pb->personal);
- else
- r = 1;
- }
- else if (pb->personal)
- r = -1;
- else
- r = ascii_strcasecmp (pa->mailbox, pb->mailbox);
- return (RSORT (r));
-}
-
-void mutt_alias_menu (char *buf, size_t buflen, alias_t * aliases)
-{
- alias_t *aliasp;
- MUTTMENU *menu;
- alias_t **AliasTable = NULL;
- int t = -1;
- int i, done = 0;
- int op;
- char helpstr[STRING];
-
- int omax;
-
- if (!aliases) {
- mutt_error _("You have no aliases!");
-
- return;
- }
-
- /* tell whoever called me to redraw the screen when I return */
- set_option (OPTNEEDREDRAW);
-
- menu = mutt_new_menu ();
- menu->make_entry = alias_entry;
- menu->tag = alias_tag;
- menu->menu = MENU_ALIAS;
- menu->title = _("Aliases");
- menu->help = mutt_compile_help(helpstr, sizeof(helpstr),
- MENU_ALIAS, AliasHelp);
-
-new_aliases:
-
- omax = menu->max;
-
- /* count the number of aliases */
- for (aliasp = aliases; aliasp; aliasp = aliasp->next) {
- aliasp->del = 0;
- aliasp->tagged = 0;
- menu->max++;
- }
-
- p_realloc(&AliasTable, menu->max);
- menu->data = AliasTable;
-
- for (i = omax, aliasp = aliases; aliasp; aliasp = aliasp->next, i++) {
- AliasTable[i] = aliasp;
- aliases = aliasp;
- }
-
- if ((SortAlias & SORT_MASK) != SORT_ORDER) {
- qsort (AliasTable, i, sizeof (alias_t *),
- (SortAlias & SORT_MASK) ==
- SORT_ADDRESS ? alias_SortAddress : alias_SortAlias);
- }
-
- for (i = 0; i < menu->max; i++)
- AliasTable[i]->num = i;
-
- while (!done) {
- if (aliases->next) {
- menu->redraw |= REDRAW_FULL;
- aliases = aliases->next;
- goto new_aliases;
- }
-
- switch ((op = mutt_menuLoop (menu))) {
- case OP_DELETE:
- case OP_UNDELETE:
- if (menu->tagprefix) {
- for (i = 0; i < menu->max; i++)
- if (AliasTable[i]->tagged)
- AliasTable[i]->del = (op == OP_DELETE) ? 1 : 0;
- menu->redraw |= REDRAW_INDEX;
- }
- else {
- AliasTable[menu->current]->del = (op == OP_DELETE) ? 1 : 0;
- menu->redraw |= REDRAW_CURRENT;
- if (option (OPTRESOLVE) && menu->current < menu->max - 1) {
- menu->current++;
- menu->redraw |= REDRAW_INDEX;
- }
- }
- break;
- case OP_GENERIC_SELECT_ENTRY:
- t = menu->current;
- case OP_EXIT:
- done = 1;
- break;
- }
- }
-
- for (i = 0; i < menu->max; i++) {
- if (AliasTable[i]->tagged) {
- mutt_addrlist_to_local (AliasTable[i]->addr);
- rfc822_addrcat(buf, buflen, AliasTable[i]->addr, 0);
- t = -1;
- }
- }
-
- if (t != -1) {
- mutt_addrlist_to_local (AliasTable[t]->addr);
- rfc822_addrcat(buf, buflen, AliasTable[t]->addr, 0);
- }
-
- mutt_menuDestroy (&menu);
- p_delete(&AliasTable);
-}