mutt stable branch with some hacks
1/*
2 * Copyright (C) 2003,2005,2008-2009 Thomas Roessler <roessler@does-not-exist.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 "charset.h"
25#include "mutt_idna.h"
26
27#ifdef HAVE_LIBIDN
28static int check_idn (char *domain)
29{
30 if (! domain)
31 return 0;
32
33 if (ascii_strncasecmp (domain, "xn--", 4) == 0)
34 return 1;
35
36 while ((domain = strchr (domain, '.')) != NULL)
37 {
38 if (ascii_strncasecmp (++domain, "xn--", 4) == 0)
39 return 1;
40 }
41
42 return 0;
43}
44#endif /* HAVE_LIBIDN */
45
46static int mbox_to_udomain (const char *mbx, char **user, char **domain)
47{
48 static char *buff = NULL;
49 char *p;
50
51 mutt_str_replace (&buff, mbx);
52
53 p = strchr (buff, '@');
54 if (!p || !p[1])
55 return -1;
56 *p = '\0';
57 *user = buff;
58 *domain = p + 1;
59 return 0;
60}
61
62static int addr_is_local (ADDRESS *a)
63{
64 return (a->intl_checked && !a->is_intl);
65}
66
67static int addr_is_intl (ADDRESS *a)
68{
69 return (a->intl_checked && a->is_intl);
70}
71
72static void set_local_mailbox (ADDRESS *a, char *local_mailbox)
73{
74 FREE (&a->mailbox);
75 a->mailbox = local_mailbox;
76 a->intl_checked = 1;
77 a->is_intl = 0;
78}
79
80static void set_intl_mailbox (ADDRESS *a, char *intl_mailbox)
81{
82 FREE (&a->mailbox);
83 a->mailbox = intl_mailbox;
84 a->intl_checked = 1;
85 a->is_intl = 1;
86}
87
88static char *intl_to_local (char *orig_user, char *orig_domain, int flags)
89{
90 char *local_user = NULL, *local_domain = NULL, *mailbox = NULL;
91 char *reversed_user = NULL, *reversed_domain = NULL;
92 char *tmp = NULL;
93#ifdef HAVE_LIBIDN
94 int is_idn_encoded = 0;
95#endif /* HAVE_LIBIDN */
96
97 local_user = safe_strdup (orig_user);
98 local_domain = safe_strdup (orig_domain);
99
100#ifdef HAVE_LIBIDN
101 is_idn_encoded = check_idn (local_domain);
102 if (is_idn_encoded && option (OPTIDNDECODE))
103 {
104 if (idna_to_unicode_8z8z (local_domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
105 goto cleanup;
106 mutt_str_replace (&local_domain, tmp);
107 FREE (&tmp);
108 }
109#endif /* HAVE_LIBIDN */
110
111 /* we don't want charset-hook effects, so we set flags to 0 */
112 if (mutt_convert_string (&local_user, "utf-8", Charset, 0) == -1)
113 goto cleanup;
114
115 if (mutt_convert_string (&local_domain, "utf-8", Charset, 0) == -1)
116 goto cleanup;
117
118 /*
119 * make sure that we can convert back and come out with the same
120 * user and domain name.
121 */
122 if ((flags & MI_MAY_BE_IRREVERSIBLE) == 0)
123 {
124 reversed_user = safe_strdup (local_user);
125
126 if (mutt_convert_string (&reversed_user, Charset, "utf-8", 0) == -1)
127 {
128 dprint (1, (debugfile,
129 "intl_to_local: Not reversible. Charset conv to utf-8 failed for user = '%s'.\n",
130 reversed_user));
131 goto cleanup;
132 }
133
134 if (ascii_strcasecmp (orig_user, reversed_user))
135 {
136 dprint (1, (debugfile, "intl_to_local: Not reversible. orig = '%s', reversed = '%s'.\n",
137 orig_user, reversed_user));
138 goto cleanup;
139 }
140
141 reversed_domain = safe_strdup (local_domain);
142
143 if (mutt_convert_string (&reversed_domain, Charset, "utf-8", 0) == -1)
144 {
145 dprint (1, (debugfile,
146 "intl_to_local: Not reversible. Charset conv to utf-8 failed for domain = '%s'.\n",
147 reversed_domain));
148 goto cleanup;
149 }
150
151#ifdef HAVE_LIBIDN
152 /* If the original domain was UTF-8, idna encoding here could
153 * produce a non-matching domain! Thus we only want to do the
154 * idna_to_ascii_8z() if the original domain was IDNA encoded.
155 */
156 if (is_idn_encoded && option (OPTIDNDECODE))
157 {
158 if (idna_to_ascii_8z (reversed_domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
159 {
160 dprint (1, (debugfile,
161 "intl_to_local: Not reversible. idna_to_ascii_8z failed for domain = '%s'.\n",
162 reversed_domain));
163 goto cleanup;
164 }
165 mutt_str_replace (&reversed_domain, tmp);
166 }
167#endif /* HAVE_LIBIDN */
168
169 if (ascii_strcasecmp (orig_domain, reversed_domain))
170 {
171 dprint (1, (debugfile, "intl_to_local: Not reversible. orig = '%s', reversed = '%s'.\n",
172 orig_domain, reversed_domain));
173 goto cleanup;
174 }
175 }
176
177 mailbox = safe_malloc (mutt_strlen (local_user) + mutt_strlen (local_domain) + 2);
178 sprintf (mailbox, "%s@%s", NONULL(local_user), NONULL(local_domain)); /* __SPRINTF_CHECKED__ */
179
180cleanup:
181 FREE (&local_user);
182 FREE (&local_domain);
183 FREE (&tmp);
184 FREE (&reversed_domain);
185 FREE (&reversed_user);
186
187 return mailbox;
188}
189
190static char *local_to_intl (char *user, char *domain)
191{
192 char *intl_user = NULL, *intl_domain = NULL;
193 char *mailbox = NULL;
194 char *tmp = NULL;
195
196 intl_user = safe_strdup (user);
197 intl_domain = safe_strdup (domain);
198
199 /* we don't want charset-hook effects, so we set flags to 0 */
200 if (mutt_convert_string (&intl_user, Charset, "utf-8", 0) == -1)
201 goto cleanup;
202
203 if (mutt_convert_string (&intl_domain, Charset, "utf-8", 0) == -1)
204 goto cleanup;
205
206#ifdef HAVE_LIBIDN
207 if (option (OPTIDNENCODE))
208 {
209 if (idna_to_ascii_8z (intl_domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
210 goto cleanup;
211 mutt_str_replace (&intl_domain, tmp);
212 }
213#endif /* HAVE_LIBIDN */
214
215 mailbox = safe_malloc (mutt_strlen (intl_user) + mutt_strlen (intl_domain) + 2);
216 sprintf (mailbox, "%s@%s", NONULL(intl_user), NONULL(intl_domain)); /* __SPRINTF_CHECKED__ */
217
218cleanup:
219 FREE (&intl_user);
220 FREE (&intl_domain);
221 FREE (&tmp);
222
223 return mailbox;
224}
225
226/* higher level functions */
227
228int mutt_addrlist_to_intl (ADDRESS *a, char **err)
229{
230 char *user = NULL, *domain = NULL;
231 char *intl_mailbox = NULL;
232 int rv = 0;
233
234 if (err)
235 *err = NULL;
236
237 for (; a; a = a->next)
238 {
239 if (!a->mailbox || addr_is_intl (a))
240 continue;
241
242 if (mbox_to_udomain (a->mailbox, &user, &domain) == -1)
243 continue;
244
245 intl_mailbox = local_to_intl (user, domain);
246 if (! intl_mailbox)
247 {
248 rv = -1;
249 if (err && !*err)
250 *err = safe_strdup (a->mailbox);
251 continue;
252 }
253
254 set_intl_mailbox (a, intl_mailbox);
255 }
256
257 return rv;
258}
259
260int mutt_addrlist_to_local (ADDRESS *a)
261{
262 char *user = NULL, *domain = NULL;
263 char *local_mailbox = NULL;
264
265 for (; a; a = a->next)
266 {
267 if (!a->mailbox || addr_is_local (a))
268 continue;
269
270 if (mbox_to_udomain (a->mailbox, &user, &domain) == -1)
271 continue;
272
273 local_mailbox = intl_to_local (user, domain, 0);
274 if (local_mailbox)
275 set_local_mailbox (a, local_mailbox);
276 }
277
278 return 0;
279}
280
281/* convert just for displaying purposes */
282const char *mutt_addr_for_display (ADDRESS *a)
283{
284 char *user = NULL, *domain = NULL;
285 static char *buff = NULL;
286 char *local_mailbox = NULL;
287
288 FREE (&buff);
289
290 if (!a->mailbox || addr_is_local (a))
291 return a->mailbox;
292
293 if (mbox_to_udomain (a->mailbox, &user, &domain) == -1)
294 return a->mailbox;
295
296 local_mailbox = intl_to_local (user, domain, MI_MAY_BE_IRREVERSIBLE);
297 if (! local_mailbox)
298 return a->mailbox;
299
300 mutt_str_replace (&buff, local_mailbox);
301 FREE (&local_mailbox);
302 return buff;
303}
304
305/* Convert an ENVELOPE structure */
306
307void mutt_env_to_local (ENVELOPE *e)
308{
309 mutt_addrlist_to_local (e->return_path);
310 mutt_addrlist_to_local (e->from);
311 mutt_addrlist_to_local (e->to);
312 mutt_addrlist_to_local (e->cc);
313 mutt_addrlist_to_local (e->bcc);
314 mutt_addrlist_to_local (e->reply_to);
315 mutt_addrlist_to_local (e->mail_followup_to);
316}
317
318/* Note that `a' in the `env->a' expression is macro argument, not
319 * "real" name of an `env' compound member. Real name will be substituted
320 * by preprocessor at the macro-expansion time.
321 * Note that #a escapes and double quotes the argument.
322 */
323#define H_TO_INTL(a) \
324 if (mutt_addrlist_to_intl (env->a, err) && !e) \
325 { \
326 if (tag) *tag = #a; e = 1; err = NULL; \
327 }
328
329int mutt_env_to_intl (ENVELOPE *env, char **tag, char **err)
330{
331 int e = 0;
332 H_TO_INTL(return_path);
333 H_TO_INTL(from);
334 H_TO_INTL(to);
335 H_TO_INTL(cc);
336 H_TO_INTL(bcc);
337 H_TO_INTL(reply_to);
338 H_TO_INTL(mail_followup_to);
339 return e;
340}
341
342#undef H_TO_INTL