mutt stable branch with some hacks
at master 342 lines 8.8 kB view raw
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