Andreas Krennmair:
[apps/madmutt.git] / mutt_idna.c
1 /*
2  * Copyright (C) 2003 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
17  */ 
18
19 #include "config.h"
20 #include "mutt.h"
21 #include "charset.h"
22 #include "mutt_idna.h"
23
24 /* The low-level interface we use. */
25
26 #ifndef HAVE_LIBIDN
27
28 int mutt_idna_to_local (const char *in, char **out, int flags)
29 {
30   *out = safe_strdup (in);
31   return 1;
32 }
33
34 int mutt_local_to_idna (const char *in, char **out)
35 {
36   *out = safe_strdup (in);
37   return 0;
38 }
39                         
40 #else
41
42 int mutt_idna_to_local (const char *in, char **out, int flags)
43 {
44   *out = NULL;
45
46   if (!in)
47     goto notrans;
48   
49   /* Is this the right function?  Interesting effects with some bad identifiers! */
50   if (idna_to_unicode_8z8z (in, out, 1) != IDNA_SUCCESS)
51     goto notrans;
52   if (mutt_convert_string (out, "utf-8", Charset, M_ICONV_HOOK_TO) == -1)
53     goto notrans;
54
55   /* 
56    * make sure that we can convert back and come out with the same
57    * domain name. */
58   
59   if ((flags & MI_MAY_BE_IRREVERSIBLE) == 0)
60   {
61     int irrev = 0;
62     char *t2 = NULL;
63     char *tmp = safe_strdup (*out);
64     if (mutt_convert_string (&tmp, Charset, "utf-8", M_ICONV_HOOK_FROM) == -1)
65       irrev = 1;
66     if (!irrev && idna_to_ascii_8z (tmp, &t2, 1) != IDNA_SUCCESS)
67       irrev = 1;
68     if (!irrev && ascii_strcasecmp (t2, in))
69     {
70       dprint (1, (debugfile, "mutt_idna_to_local: Not reversible. in = '%s', t2 = '%s'.\n",
71                   in, t2));
72       irrev = 1;
73     }
74     
75     FREE (&t2);
76     FREE (&tmp);
77
78     if (irrev)
79       goto notrans;
80   }
81
82   return 0;
83   
84  notrans:
85   FREE (out);
86   *out = safe_strdup (in);
87   return 1;
88 }
89
90 int mutt_local_to_idna (const char *in, char **out)
91 {
92   int rv = 0;
93   char *tmp = safe_strdup (in);
94   *out = NULL;
95
96   if (!in)
97   {
98     *out = NULL;
99     return -1;
100   }
101   
102   if (mutt_convert_string (&tmp, Charset, "utf-8", M_ICONV_HOOK_FROM) == -1)
103     rv = -1;
104   if (!rv && idna_to_ascii_8z (tmp, out, 1) != IDNA_SUCCESS)
105     rv = -2;
106   
107   FREE (&tmp);
108   if (rv < 0)
109   {
110     FREE (out);
111     *out = safe_strdup (in);
112   }
113   return rv;
114 }
115
116 #endif
117
118
119 /* higher level functions */
120
121 static int mbox_to_udomain (const char *mbx, char **user, char **domain)
122 {
123   char *p;
124   *user = NULL;
125   *domain = NULL;
126   
127   p = strchr (mbx, '@');
128   if (!p)
129     return -1;
130   *user = safe_calloc((p - mbx + 1), sizeof(mbx[0]));
131   strfcpy (*user, mbx, (p - mbx + 1));
132   *domain = safe_strdup(p + 1);
133   return 0;
134 }
135
136 int mutt_addrlist_to_idna (ADDRESS *a, char **err)
137 {
138   char *user = NULL, *domain = NULL;
139   char *tmp = NULL;
140   int e = 0;
141   
142   if (err)
143     *err = NULL;
144
145   for (; a; a = a->next)
146   {
147     if (!a->mailbox)
148       continue;
149     if (mbox_to_udomain (a->mailbox, &user, &domain) == -1)
150       continue;
151     
152     if (mutt_local_to_idna (domain, &tmp) < 0)
153     {
154       e = 1;
155       if (err)
156         *err = safe_strdup (domain);
157     }
158     else
159     {
160       safe_realloc (&a->mailbox, mutt_strlen (user) + mutt_strlen (tmp) + 2);
161       sprintf (a->mailbox, "%s@%s", NONULL(user), NONULL(tmp)); /* __SPRINTF_CHECKED__ */
162     }
163     
164     FREE (&domain);
165     FREE (&user);
166     FREE (&tmp);
167     
168     if (e)
169       return -1;
170   }
171   
172   return 0;
173 }
174
175 int mutt_addrlist_to_local (ADDRESS *a)
176 {
177   char *user, *domain;
178   char *tmp = NULL;
179   
180   for (; a; a = a->next)
181   {
182     if (!a->mailbox)
183       continue;
184     if (mbox_to_udomain (a->mailbox, &user, &domain) == -1)
185       continue;
186     
187     if (mutt_idna_to_local (domain, &tmp, 0) == 0)
188     {
189       safe_realloc (&a->mailbox, mutt_strlen (user) + mutt_strlen (tmp) + 2);
190       sprintf (a->mailbox, "%s@%s", NONULL (user), NONULL (tmp)); /* __SPRINTF_CHECKED__ */
191     }
192     
193     FREE (&domain);
194     FREE (&user);
195     FREE (&tmp);
196   }
197   
198   return 0;
199 }
200
201 /* convert just for displaying purposes */
202 const char *mutt_addr_for_display (ADDRESS *a)
203 {
204   static char *buff = NULL;
205   char *tmp = NULL;
206   /* user and domain will be either allocated or reseted to the NULL in
207    * the mbox_to_udomain(), but for safety... */
208   char *domain = NULL;
209   char *user = NULL;
210   
211   FREE (&buff);
212   
213   if (mbox_to_udomain (a->mailbox, &user, &domain) != 0)
214     return a->mailbox;
215   if (mutt_idna_to_local (domain, &tmp, MI_MAY_BE_IRREVERSIBLE) != 0)
216   {
217     FREE (&user);
218     FREE (&domain);
219     FREE (&tmp);
220     return a->mailbox;
221   }
222   
223   safe_realloc (&buff, mutt_strlen (tmp) + mutt_strlen (user) + 2);
224   sprintf (buff, "%s@%s", NONULL(user), NONULL(tmp)); /* __SPRINTF_CHECKED__ */
225   FREE (&tmp);
226   FREE (&user);
227   FREE (&domain);
228   return buff;
229 }
230
231 /* Convert an ENVELOPE structure */
232
233 void mutt_env_to_local (ENVELOPE *e)
234 {
235   mutt_addrlist_to_local (e->return_path);
236   mutt_addrlist_to_local (e->from);
237   mutt_addrlist_to_local (e->to);
238   mutt_addrlist_to_local (e->cc);
239   mutt_addrlist_to_local (e->bcc);
240   mutt_addrlist_to_local (e->reply_to);
241   mutt_addrlist_to_local (e->mail_followup_to);
242 }
243
244 /* Note that `a' in the `env->a' expression is macro argument, not
245  * "real" name of an `env' compound member.  Real name will be substituted
246  * by preprocessor at the macro-expansion time.
247  */
248 #define H_TO_IDNA(a)    \
249   if (mutt_addrlist_to_idna (env->a, err) && !e) \
250   { \
251      if (tag) *tag = #a; e = 1; err = NULL; \
252   }
253
254 int mutt_env_to_idna (ENVELOPE *env, char **tag, char **err)
255 {
256   int e = 0;
257   H_TO_IDNA(return_path);
258   H_TO_IDNA(from);
259   H_TO_IDNA(to);
260   H_TO_IDNA(cc);
261   H_TO_IDNA(bcc);
262   H_TO_IDNA(reply_to);
263   H_TO_IDNA(mail_followup_to);
264   return e;
265 }
266
267 #undef H_TO_IDNA