/src/neomutt/alias/alias.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * Representation of a single alias to an email address |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org> |
7 | | * Copyright (C) 2019 Pietro Cerutti <gahr@gahr.ch> |
8 | | * Copyright (C) 2020 Richard Russon <rich@flatcap.org> |
9 | | * |
10 | | * @copyright |
11 | | * This program is free software: you can redistribute it and/or modify it under |
12 | | * the terms of the GNU General Public License as published by the Free Software |
13 | | * Foundation, either version 2 of the License, or (at your option) any later |
14 | | * version. |
15 | | * |
16 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
19 | | * details. |
20 | | * |
21 | | * You should have received a copy of the GNU General Public License along with |
22 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
23 | | */ |
24 | | |
25 | | /** |
26 | | * @page alias_alias Alias for an email address |
27 | | * |
28 | | * Representation of a single alias to an email address |
29 | | */ |
30 | | |
31 | | #include "config.h" |
32 | | #include <stddef.h> |
33 | | #include <pwd.h> |
34 | | #include <stdbool.h> |
35 | | #include <stdio.h> |
36 | | #include <stdlib.h> |
37 | | #include <string.h> |
38 | | #include <wchar.h> |
39 | | #include <wctype.h> |
40 | | #include "mutt/lib.h" |
41 | | #include "address/lib.h" |
42 | | #include "config/lib.h" |
43 | | #include "email/lib.h" |
44 | | #include "core/lib.h" |
45 | | #include "gui/lib.h" |
46 | | #include "mutt.h" |
47 | | #include "alias.h" |
48 | | #include "lib.h" |
49 | | #include "browser/lib.h" |
50 | | #include "editor/lib.h" |
51 | | #include "history/lib.h" |
52 | | #include "question/lib.h" |
53 | | #include "send/lib.h" |
54 | | #include "alternates.h" |
55 | | #include "globals.h" // IWYU pragma: keep |
56 | | #include "maillist.h" |
57 | | #include "muttlib.h" |
58 | | #include "reverse.h" |
59 | | |
60 | | struct AliasList Aliases = TAILQ_HEAD_INITIALIZER(Aliases); ///< List of all the user's email aliases |
61 | | |
62 | | /** |
63 | | * write_safe_address - Defang malicious email addresses |
64 | | * @param fp File to write to |
65 | | * @param s Email address to defang |
66 | | * |
67 | | * if someone has an address like |
68 | | * From: John `/bin/rm -f ~` Doe <john.doe@example.com> |
69 | | * and the user creates an alias for this, NeoMutt could wind up executing |
70 | | * the backticks because it writes aliases like |
71 | | * alias me John `/bin/rm -f ~` Doe <john.doe@example.com> |
72 | | * To avoid this problem, use a backslash (\) to quote any backticks. We also |
73 | | * need to quote backslashes as well, since you could defeat the above by |
74 | | * doing |
75 | | * From: John \`/bin/rm -f ~\` Doe <john.doe@example.com> |
76 | | * since that would get aliased as |
77 | | * alias me John \\`/bin/rm -f ~\\` Doe <john.doe@example.com> |
78 | | * which still gets evaluated because the double backslash is not a quote. |
79 | | * |
80 | | * Additionally, we need to quote ' and " characters, otherwise neomutt will |
81 | | * interpret them on the wrong parsing step. |
82 | | * |
83 | | * $ wants to be quoted since it may indicate the start of an environment |
84 | | * variable. |
85 | | */ |
86 | | static void write_safe_address(FILE *fp, const char *s) |
87 | 0 | { |
88 | 0 | while (*s) |
89 | 0 | { |
90 | 0 | if ((*s == '\\') || (*s == '`') || (*s == '\'') || (*s == '"') || (*s == '$')) |
91 | 0 | fputc('\\', fp); |
92 | 0 | fputc(*s, fp); |
93 | 0 | s++; |
94 | 0 | } |
95 | 0 | } |
96 | | |
97 | | /** |
98 | | * expand_aliases_r - Expand aliases, recursively |
99 | | * @param[in] al Address List |
100 | | * @param[out] expn Alias List |
101 | | * |
102 | | * ListHead expn is used as temporary storage for already-expanded aliases. |
103 | | */ |
104 | | static void expand_aliases_r(struct AddressList *al, struct ListHead *expn) |
105 | 0 | { |
106 | 0 | struct Address *a = TAILQ_FIRST(al); |
107 | 0 | while (a) |
108 | 0 | { |
109 | 0 | if (!a->group && !a->personal && a->mailbox && !buf_find_char(a->mailbox, '@')) |
110 | 0 | { |
111 | 0 | struct AddressList *alias = alias_lookup(buf_string(a->mailbox)); |
112 | 0 | if (alias) |
113 | 0 | { |
114 | 0 | bool duplicate = false; |
115 | 0 | struct ListNode *np = NULL; |
116 | 0 | STAILQ_FOREACH(np, expn, entries) |
117 | 0 | { |
118 | 0 | if (mutt_str_equal(buf_string(a->mailbox), np->data)) /* alias already found */ |
119 | 0 | { |
120 | 0 | mutt_debug(LL_DEBUG1, "loop in alias found for '%s'\n", buf_string(a->mailbox)); |
121 | 0 | duplicate = true; |
122 | 0 | break; |
123 | 0 | } |
124 | 0 | } |
125 | |
|
126 | 0 | if (duplicate) |
127 | 0 | { |
128 | | // We've already seen this alias, so drop it |
129 | 0 | struct Address *next = TAILQ_NEXT(a, entries); |
130 | 0 | TAILQ_REMOVE(al, a, entries); |
131 | 0 | mutt_addr_free(&a); |
132 | 0 | a = next; |
133 | 0 | continue; |
134 | 0 | } |
135 | | |
136 | | // Keep a list of aliases that we've already seen |
137 | 0 | mutt_list_insert_head(expn, buf_strdup(a->mailbox)); |
138 | | |
139 | | /* The alias may expand to several addresses, |
140 | | * some of which may themselves be aliases. |
141 | | * Create a copy and recursively expand any aliases within. */ |
142 | 0 | struct AddressList copy = TAILQ_HEAD_INITIALIZER(copy); |
143 | 0 | mutt_addrlist_copy(©, alias, false); |
144 | 0 | expand_aliases_r(©, expn); |
145 | | |
146 | | /* Next, move the expanded addresses |
147 | | * from the copy into the original list (before the alias) */ |
148 | 0 | struct Address *a2 = NULL, *tmp = NULL; |
149 | 0 | TAILQ_FOREACH_SAFE(a2, ©, entries, tmp) |
150 | 0 | { |
151 | 0 | TAILQ_INSERT_BEFORE(a, a2, entries); |
152 | 0 | } |
153 | 0 | a = TAILQ_PREV(a, AddressList, entries); |
154 | | // Finally, remove the alias itself |
155 | 0 | struct Address *next = TAILQ_NEXT(a, entries); |
156 | 0 | TAILQ_REMOVE(al, next, entries); |
157 | 0 | mutt_addr_free(&next); |
158 | 0 | } |
159 | 0 | else |
160 | 0 | { |
161 | 0 | struct passwd *pw = getpwnam(buf_string(a->mailbox)); |
162 | 0 | if (pw) |
163 | 0 | { |
164 | 0 | char namebuf[256] = { 0 }; |
165 | |
|
166 | 0 | mutt_gecos_name(namebuf, sizeof(namebuf), pw); |
167 | 0 | a->personal = buf_new(namebuf); |
168 | 0 | } |
169 | 0 | } |
170 | 0 | } |
171 | 0 | a = TAILQ_NEXT(a, entries); |
172 | 0 | } |
173 | |
|
174 | 0 | const char *fqdn = NULL; |
175 | 0 | const bool c_use_domain = cs_subset_bool(NeoMutt->sub, "use_domain"); |
176 | 0 | if (c_use_domain && (fqdn = mutt_fqdn(true, NeoMutt->sub))) |
177 | 0 | { |
178 | | /* now qualify all local addresses */ |
179 | 0 | mutt_addrlist_qualify(al, fqdn); |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | | /** |
184 | | * recode_buf - Convert some text between two character sets |
185 | | * @param buf Buffer to convert |
186 | | * |
187 | | * The 'from' charset is controlled by the 'charset' config variable. |
188 | | * The 'to' charset is controlled by the 'config_charset' config variable. |
189 | | */ |
190 | | static void recode_buf(struct Buffer *buf) |
191 | 0 | { |
192 | 0 | const char *const c_config_charset = cs_subset_string(NeoMutt->sub, "config_charset"); |
193 | 0 | if (!c_config_charset || !cc_charset()) |
194 | 0 | return; |
195 | | |
196 | 0 | char *s = buf_strdup(buf); |
197 | 0 | if (!s) |
198 | 0 | return; |
199 | 0 | if (mutt_ch_convert_string(&s, cc_charset(), c_config_charset, MUTT_ICONV_NO_FLAGS) == 0) |
200 | 0 | buf_strcpy(buf, s); |
201 | 0 | FREE(&s); |
202 | 0 | } |
203 | | |
204 | | /** |
205 | | * check_alias_name - Sanity-check an alias name |
206 | | * @param s Alias to check |
207 | | * @param dest Buffer for the result |
208 | | * @param destlen Length of buffer |
209 | | * @retval 0 Success |
210 | | * @retval -1 Error |
211 | | * |
212 | | * Only characters which are non-special to both the RFC822 and the neomutt |
213 | | * configuration parser are permitted. |
214 | | */ |
215 | | static int check_alias_name(const char *s, char *dest, size_t destlen) |
216 | 0 | { |
217 | 0 | wchar_t wc = 0; |
218 | 0 | mbstate_t mbstate = { 0 }; |
219 | 0 | size_t l; |
220 | 0 | int rc = 0; |
221 | 0 | bool dry = !dest || !destlen; |
222 | |
|
223 | 0 | if (!dry) |
224 | 0 | destlen--; |
225 | 0 | for (; s && *s && (dry || destlen) && (l = mbrtowc(&wc, s, MB_CUR_MAX, &mbstate)) != 0; |
226 | 0 | s += l, destlen -= l) |
227 | 0 | { |
228 | 0 | bool bad = (l == ICONV_ILLEGAL_SEQ) || (l == ICONV_BUF_TOO_SMALL); /* conversion error */ |
229 | 0 | bad = bad || (!dry && l > destlen); /* too few room for mb char */ |
230 | 0 | if (l == 1) |
231 | 0 | bad = bad || (!strchr("-_+=.", *s) && !iswalnum(wc)); |
232 | 0 | else |
233 | 0 | bad = bad || !iswalnum(wc); |
234 | 0 | if (bad) |
235 | 0 | { |
236 | 0 | if (dry) |
237 | 0 | return -1; |
238 | 0 | if (l == ICONV_ILLEGAL_SEQ) |
239 | 0 | memset(&mbstate, 0, sizeof(mbstate_t)); |
240 | 0 | *dest++ = '_'; |
241 | 0 | rc = -1; |
242 | 0 | } |
243 | 0 | else if (!dry) |
244 | 0 | { |
245 | 0 | memcpy(dest, s, l); |
246 | 0 | dest += l; |
247 | 0 | } |
248 | 0 | } |
249 | 0 | if (!dry) |
250 | 0 | *dest = '\0'; |
251 | 0 | return rc; |
252 | 0 | } |
253 | | |
254 | | /** |
255 | | * string_is_address - Does an email address match a user and domain? |
256 | | * @param str Address string to test |
257 | | * @param user User name |
258 | | * @param domain Domain name |
259 | | * @retval true They match |
260 | | */ |
261 | | static bool string_is_address(const char *str, const char *user, const char *domain) |
262 | 0 | { |
263 | 0 | char buf[1024] = { 0 }; |
264 | |
|
265 | 0 | snprintf(buf, sizeof(buf), "%s@%s", NONULL(user), NONULL(domain)); |
266 | 0 | if (mutt_istr_equal(str, buf)) |
267 | 0 | return true; |
268 | | |
269 | 0 | return false; |
270 | 0 | } |
271 | | |
272 | | /** |
273 | | * alias_lookup - Find an Alias |
274 | | * @param name Alias name to find |
275 | | * @retval ptr Address for the Alias |
276 | | * @retval NULL No such Alias |
277 | | * |
278 | | * @note The search is case-insensitive |
279 | | */ |
280 | | struct AddressList *alias_lookup(const char *name) |
281 | 0 | { |
282 | 0 | struct Alias *a = NULL; |
283 | |
|
284 | 0 | TAILQ_FOREACH(a, &Aliases, entries) |
285 | 0 | { |
286 | 0 | if (mutt_istr_equal(name, a->name)) |
287 | 0 | return &a->addr; |
288 | 0 | } |
289 | 0 | return NULL; |
290 | 0 | } |
291 | | |
292 | | /** |
293 | | * mutt_expand_aliases - Expand aliases in a List of Addresses |
294 | | * @param al AddressList |
295 | | * |
296 | | * Duplicate addresses are dropped |
297 | | */ |
298 | | void mutt_expand_aliases(struct AddressList *al) |
299 | 0 | { |
300 | 0 | struct ListHead expn; /* previously expanded aliases to avoid loops */ |
301 | |
|
302 | 0 | STAILQ_INIT(&expn); |
303 | 0 | expand_aliases_r(al, &expn); |
304 | 0 | mutt_list_free(&expn); |
305 | 0 | mutt_addrlist_dedupe(al); |
306 | 0 | } |
307 | | |
308 | | /** |
309 | | * mutt_expand_aliases_env - Expand aliases in all the fields of an Envelope |
310 | | * @param env Envelope to expand |
311 | | */ |
312 | | void mutt_expand_aliases_env(struct Envelope *env) |
313 | 0 | { |
314 | 0 | mutt_expand_aliases(&env->from); |
315 | 0 | mutt_expand_aliases(&env->to); |
316 | 0 | mutt_expand_aliases(&env->cc); |
317 | 0 | mutt_expand_aliases(&env->bcc); |
318 | 0 | mutt_expand_aliases(&env->reply_to); |
319 | 0 | mutt_expand_aliases(&env->mail_followup_to); |
320 | 0 | } |
321 | | |
322 | | /** |
323 | | * mutt_get_address - Get an Address from an Envelope |
324 | | * @param[in] env Envelope to examine |
325 | | * @param[out] prefix Prefix for the Address, e.g. "To:" |
326 | | * @retval ptr AddressList in the Envelope |
327 | | * |
328 | | * @note The caller must NOT free the returned AddressList |
329 | | */ |
330 | | struct AddressList *mutt_get_address(struct Envelope *env, const char **prefix) |
331 | 0 | { |
332 | 0 | struct AddressList *al = NULL; |
333 | 0 | const char *pfx = NULL; |
334 | |
|
335 | 0 | if (mutt_addr_is_user(TAILQ_FIRST(&env->from))) |
336 | 0 | { |
337 | 0 | if (!TAILQ_EMPTY(&env->to) && !mutt_is_mail_list(TAILQ_FIRST(&env->to))) |
338 | 0 | { |
339 | 0 | pfx = "To"; |
340 | 0 | al = &env->to; |
341 | 0 | } |
342 | 0 | else |
343 | 0 | { |
344 | 0 | pfx = "Cc"; |
345 | 0 | al = &env->cc; |
346 | 0 | } |
347 | 0 | } |
348 | 0 | else if (!TAILQ_EMPTY(&env->reply_to) && !mutt_is_mail_list(TAILQ_FIRST(&env->reply_to))) |
349 | 0 | { |
350 | 0 | pfx = "Reply-To"; |
351 | 0 | al = &env->reply_to; |
352 | 0 | } |
353 | 0 | else |
354 | 0 | { |
355 | 0 | al = &env->from; |
356 | 0 | pfx = "From"; |
357 | 0 | } |
358 | |
|
359 | 0 | if (prefix) |
360 | 0 | *prefix = pfx; |
361 | |
|
362 | 0 | return al; |
363 | 0 | } |
364 | | |
365 | | /** |
366 | | * alias_create - Create a new Alias from an Address |
367 | | * @param al Address to use |
368 | | * @param sub Config items |
369 | | */ |
370 | | void alias_create(struct AddressList *al, const struct ConfigSubset *sub) |
371 | 0 | { |
372 | 0 | struct Buffer *buf = buf_pool_get(); |
373 | 0 | struct Buffer *fixed = buf_pool_get(); |
374 | 0 | struct Buffer *prompt = NULL; |
375 | 0 | struct Buffer *tmp = buf_pool_get(); |
376 | |
|
377 | 0 | struct Address *addr = NULL; |
378 | 0 | char *pc = NULL; |
379 | 0 | char *err = NULL; |
380 | 0 | FILE *fp_alias = NULL; |
381 | |
|
382 | 0 | if (al) |
383 | 0 | { |
384 | 0 | addr = TAILQ_FIRST(al); |
385 | 0 | if (addr && addr->mailbox) |
386 | 0 | { |
387 | 0 | buf_copy(tmp, addr->mailbox); |
388 | 0 | pc = strchr(buf_string(tmp), '@'); |
389 | 0 | if (pc) |
390 | 0 | *pc = '\0'; |
391 | 0 | } |
392 | 0 | } |
393 | | |
394 | | /* Don't suggest a bad alias name in the event of a strange local part. */ |
395 | 0 | check_alias_name(buf_string(tmp), buf->data, buf->dsize); |
396 | |
|
397 | 0 | retry_name: |
398 | | /* L10N: prompt to add a new alias */ |
399 | 0 | if ((mw_get_field(_("Alias as: "), buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0) || |
400 | 0 | buf_is_empty(buf)) |
401 | 0 | { |
402 | 0 | goto done; |
403 | 0 | } |
404 | | |
405 | | /* check to see if the user already has an alias defined */ |
406 | 0 | if (alias_lookup(buf_string(buf))) |
407 | 0 | { |
408 | 0 | mutt_error(_("You already have an alias defined with that name")); |
409 | 0 | goto done; |
410 | 0 | } |
411 | | |
412 | 0 | if (check_alias_name(buf_string(buf), fixed->data, fixed->dsize)) |
413 | 0 | { |
414 | 0 | switch (query_yesorno(_("Warning: This alias name may not work. Fix it?"), MUTT_YES)) |
415 | 0 | { |
416 | 0 | case MUTT_YES: |
417 | 0 | buf_copy(buf, fixed); |
418 | 0 | goto retry_name; |
419 | 0 | case MUTT_ABORT: |
420 | 0 | goto done; |
421 | 0 | default:; // do nothing |
422 | 0 | } |
423 | 0 | } |
424 | | |
425 | 0 | struct Alias *alias = alias_new(); |
426 | 0 | alias->name = buf_strdup(buf); |
427 | |
|
428 | 0 | mutt_addrlist_to_local(al); |
429 | |
|
430 | 0 | if (addr && addr->mailbox) |
431 | 0 | buf_copy(buf, addr->mailbox); |
432 | 0 | else |
433 | 0 | buf_reset(buf); |
434 | |
|
435 | 0 | mutt_addrlist_to_intl(al, NULL); |
436 | |
|
437 | 0 | do |
438 | 0 | { |
439 | 0 | if ((mw_get_field(_("Address: "), buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0) || |
440 | 0 | buf_is_empty(buf)) |
441 | 0 | { |
442 | 0 | alias_free(&alias); |
443 | 0 | goto done; |
444 | 0 | } |
445 | | |
446 | 0 | mutt_addrlist_parse(&alias->addr, buf_string(buf)); |
447 | 0 | if (TAILQ_EMPTY(&alias->addr)) |
448 | 0 | mutt_beep(false); |
449 | 0 | if (mutt_addrlist_to_intl(&alias->addr, &err)) |
450 | 0 | { |
451 | 0 | mutt_error(_("Bad IDN: '%s'"), err); |
452 | 0 | FREE(&err); |
453 | 0 | continue; |
454 | 0 | } |
455 | 0 | } while (TAILQ_EMPTY(&alias->addr)); |
456 | | |
457 | 0 | if (addr && addr->personal && !mutt_is_mail_list(addr)) |
458 | 0 | buf_copy(buf, addr->personal); |
459 | 0 | else |
460 | 0 | buf_reset(buf); |
461 | |
|
462 | 0 | if (mw_get_field(_("Personal name: "), buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0) |
463 | 0 | { |
464 | 0 | alias_free(&alias); |
465 | 0 | goto done; |
466 | 0 | } |
467 | 0 | buf_copy(TAILQ_FIRST(&alias->addr)->personal, buf); |
468 | |
|
469 | 0 | buf_reset(buf); |
470 | 0 | if (mw_get_field(_("Comment: "), buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) == 0) |
471 | 0 | { |
472 | 0 | mutt_str_replace(&alias->comment, buf_string(buf)); |
473 | 0 | } |
474 | |
|
475 | 0 | buf_reset(buf); |
476 | 0 | mutt_addrlist_write(&alias->addr, buf, true); |
477 | 0 | prompt = buf_pool_get(); |
478 | 0 | if (alias->comment) |
479 | 0 | { |
480 | 0 | buf_printf(prompt, "[%s = %s # %s] %s", alias->name, buf_string(buf), |
481 | 0 | alias->comment, _("Accept?")); |
482 | 0 | } |
483 | 0 | else |
484 | 0 | { |
485 | 0 | buf_printf(prompt, "[%s = %s] %s", alias->name, buf_string(buf), _("Accept?")); |
486 | 0 | } |
487 | 0 | if (query_yesorno(buf_string(prompt), MUTT_YES) != MUTT_YES) |
488 | 0 | { |
489 | 0 | alias_free(&alias); |
490 | 0 | goto done; |
491 | 0 | } |
492 | | |
493 | 0 | alias_reverse_add(alias); |
494 | 0 | TAILQ_INSERT_TAIL(&Aliases, alias, entries); |
495 | |
|
496 | 0 | const char *const c_alias_file = cs_subset_path(sub, "alias_file"); |
497 | 0 | buf_strcpy(buf, c_alias_file); |
498 | |
|
499 | 0 | struct FileCompletionData cdata = { false, NULL, NULL, NULL }; |
500 | 0 | if (mw_get_field(_("Save to file: "), buf, MUTT_COMP_CLEAR, HC_FILE, |
501 | 0 | &CompleteMailboxOps, &cdata) != 0) |
502 | 0 | { |
503 | 0 | goto done; |
504 | 0 | } |
505 | 0 | mutt_expand_path(buf->data, buf->dsize); |
506 | 0 | fp_alias = fopen(buf_string(buf), "a+"); |
507 | 0 | if (!fp_alias) |
508 | 0 | { |
509 | 0 | mutt_perror("%s", buf_string(buf)); |
510 | 0 | goto done; |
511 | 0 | } |
512 | | |
513 | | /* terminate existing file with \n if necessary */ |
514 | 0 | if (!mutt_file_seek(fp_alias, 0, SEEK_END)) |
515 | 0 | { |
516 | 0 | goto done; |
517 | 0 | } |
518 | 0 | if (ftell(fp_alias) > 0) |
519 | 0 | { |
520 | 0 | if (!mutt_file_seek(fp_alias, -1, SEEK_CUR)) |
521 | 0 | { |
522 | 0 | goto done; |
523 | 0 | } |
524 | 0 | if (fread(buf->data, 1, 1, fp_alias) != 1) |
525 | 0 | { |
526 | 0 | mutt_perror(_("Error reading alias file")); |
527 | 0 | goto done; |
528 | 0 | } |
529 | 0 | if (!mutt_file_seek(fp_alias, 0, SEEK_END)) |
530 | 0 | { |
531 | 0 | goto done; |
532 | 0 | } |
533 | 0 | if (buf->data[0] != '\n') |
534 | 0 | fputc('\n', fp_alias); |
535 | 0 | } |
536 | | |
537 | 0 | if (check_alias_name(alias->name, NULL, 0)) |
538 | 0 | mutt_file_quote_filename(alias->name, buf->data, buf->dsize); |
539 | 0 | else |
540 | 0 | buf_strcpy(buf, alias->name); |
541 | |
|
542 | 0 | recode_buf(buf); |
543 | 0 | fprintf(fp_alias, "alias %s ", buf_string(buf)); |
544 | 0 | buf_reset(buf); |
545 | |
|
546 | 0 | mutt_addrlist_write(&alias->addr, buf, false); |
547 | 0 | recode_buf(buf); |
548 | 0 | write_safe_address(fp_alias, buf_string(buf)); |
549 | 0 | if (alias->comment) |
550 | 0 | fprintf(fp_alias, " # %s", alias->comment); |
551 | 0 | fputc('\n', fp_alias); |
552 | 0 | if (mutt_file_fsync_close(&fp_alias) != 0) |
553 | 0 | mutt_perror(_("Trouble adding alias")); |
554 | 0 | else |
555 | 0 | mutt_message(_("Alias added")); |
556 | |
|
557 | 0 | done: |
558 | 0 | mutt_file_fclose(&fp_alias); |
559 | 0 | buf_pool_release(&buf); |
560 | 0 | buf_pool_release(&fixed); |
561 | 0 | buf_pool_release(&prompt); |
562 | 0 | buf_pool_release(&tmp); |
563 | 0 | } |
564 | | |
565 | | /** |
566 | | * mutt_addr_is_user - Does the address belong to the user |
567 | | * @param addr Address to check |
568 | | * @retval true The given address belongs to the user |
569 | | */ |
570 | | bool mutt_addr_is_user(const struct Address *addr) |
571 | 0 | { |
572 | 0 | if (!addr) |
573 | 0 | { |
574 | 0 | mutt_debug(LL_DEBUG5, "no, NULL address\n"); |
575 | 0 | return false; |
576 | 0 | } |
577 | 0 | if (!addr->mailbox) |
578 | 0 | { |
579 | 0 | mutt_debug(LL_DEBUG5, "no, no mailbox\n"); |
580 | 0 | return false; |
581 | 0 | } |
582 | | |
583 | 0 | if (mutt_istr_equal(buf_string(addr->mailbox), Username)) |
584 | 0 | { |
585 | 0 | mutt_debug(LL_DEBUG5, "#1 yes, %s = %s\n", buf_string(addr->mailbox), Username); |
586 | 0 | return true; |
587 | 0 | } |
588 | 0 | if (string_is_address(buf_string(addr->mailbox), Username, ShortHostname)) |
589 | 0 | { |
590 | 0 | mutt_debug(LL_DEBUG5, "#2 yes, %s = %s @ %s\n", buf_string(addr->mailbox), |
591 | 0 | Username, ShortHostname); |
592 | 0 | return true; |
593 | 0 | } |
594 | 0 | const char *fqdn = mutt_fqdn(false, NeoMutt->sub); |
595 | 0 | if (string_is_address(buf_string(addr->mailbox), Username, fqdn)) |
596 | 0 | { |
597 | 0 | mutt_debug(LL_DEBUG5, "#3 yes, %s = %s @ %s\n", buf_string(addr->mailbox), |
598 | 0 | Username, NONULL(fqdn)); |
599 | 0 | return true; |
600 | 0 | } |
601 | 0 | fqdn = mutt_fqdn(true, NeoMutt->sub); |
602 | 0 | if (string_is_address(buf_string(addr->mailbox), Username, fqdn)) |
603 | 0 | { |
604 | 0 | mutt_debug(LL_DEBUG5, "#4 yes, %s = %s @ %s\n", buf_string(addr->mailbox), |
605 | 0 | Username, NONULL(fqdn)); |
606 | 0 | return true; |
607 | 0 | } |
608 | | |
609 | 0 | const struct Address *c_from = cs_subset_address(NeoMutt->sub, "from"); |
610 | 0 | if (c_from && mutt_istr_equal(buf_string(c_from->mailbox), buf_string(addr->mailbox))) |
611 | 0 | { |
612 | 0 | mutt_debug(LL_DEBUG5, "#5 yes, %s = %s\n", buf_string(addr->mailbox), |
613 | 0 | buf_string(c_from->mailbox)); |
614 | 0 | return true; |
615 | 0 | } |
616 | | |
617 | 0 | if (mutt_alternates_match(buf_string(addr->mailbox))) |
618 | 0 | return true; |
619 | | |
620 | 0 | mutt_debug(LL_DEBUG5, "no, all failed\n"); |
621 | 0 | return false; |
622 | 0 | } |
623 | | |
624 | | /** |
625 | | * alias_new - Create a new Alias |
626 | | * @retval ptr Newly allocated Alias |
627 | | * |
628 | | * Free the result with alias_free() |
629 | | */ |
630 | | struct Alias *alias_new(void) |
631 | 0 | { |
632 | 0 | struct Alias *a = mutt_mem_calloc(1, sizeof(struct Alias)); |
633 | 0 | TAILQ_INIT(&a->addr); |
634 | 0 | return a; |
635 | 0 | } |
636 | | |
637 | | /** |
638 | | * alias_free - Free an Alias |
639 | | * @param[out] ptr Alias to free |
640 | | */ |
641 | | void alias_free(struct Alias **ptr) |
642 | 0 | { |
643 | 0 | if (!ptr || !*ptr) |
644 | 0 | return; |
645 | | |
646 | 0 | struct Alias *alias = *ptr; |
647 | |
|
648 | 0 | mutt_debug(LL_NOTIFY, "NT_ALIAS_DELETE: %s\n", alias->name); |
649 | 0 | struct EventAlias ev_a = { alias }; |
650 | 0 | notify_send(NeoMutt->notify, NT_ALIAS, NT_ALIAS_DELETE, &ev_a); |
651 | |
|
652 | 0 | FREE(&alias->name); |
653 | 0 | FREE(&alias->comment); |
654 | 0 | mutt_addrlist_clear(&(alias->addr)); |
655 | 0 | FREE(ptr); |
656 | 0 | } |
657 | | |
658 | | /** |
659 | | * aliaslist_free - Free a List of Aliases |
660 | | * @param al AliasList to free |
661 | | */ |
662 | | void aliaslist_free(struct AliasList *al) |
663 | 0 | { |
664 | 0 | if (!al) |
665 | 0 | return; |
666 | | |
667 | 0 | struct Alias *np = NULL, *tmp = NULL; |
668 | 0 | TAILQ_FOREACH_SAFE(np, al, entries, tmp) |
669 | 0 | { |
670 | 0 | TAILQ_REMOVE(al, np, entries); |
671 | 0 | alias_free(&np); |
672 | 0 | } |
673 | 0 | TAILQ_INIT(al); |
674 | 0 | } |
675 | | |
676 | | /** |
677 | | * alias_init - Set up the Alias globals |
678 | | */ |
679 | | void alias_init(void) |
680 | 0 | { |
681 | 0 | alias_reverse_init(); |
682 | 0 | } |
683 | | |
684 | | /** |
685 | | * alias_cleanup - Clean up the Alias globals |
686 | | */ |
687 | | void alias_cleanup(void) |
688 | 0 | { |
689 | 0 | struct Alias *np = NULL; |
690 | 0 | TAILQ_FOREACH(np, &Aliases, entries) |
691 | 0 | { |
692 | 0 | alias_reverse_delete(np); |
693 | 0 | } |
694 | 0 | aliaslist_free(&Aliases); |
695 | 0 | alias_reverse_shutdown(); |
696 | 0 | } |