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