NeoMutt  2021-02-05-89-gabe350
Teaching an old dog new tricks
DOXYGEN
idna2.h File Reference
#include <stdbool.h>
#include <stdint.h>
+ Include dependency graph for idna2.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define MI_NO_FLAGS   0
 
#define MI_MAY_BE_IRREVERSIBLE   (1 << 0)
 

Functions

char * mutt_idna_intl_to_local (const char *user, const char *domain, uint8_t flags)
 Convert an email's domain from Punycode. More...
 
char * mutt_idna_local_to_intl (const char *user, const char *domain)
 Convert an email's domain to Punycode. More...
 
const char * mutt_idna_print_version (void)
 Create an IDN version string. More...
 
int mutt_idna_to_ascii_lz (const char *input, char **output, uint8_t flags)
 

Variables

bool C_IdnDecode
 
bool C_IdnEncode
 

Detailed Description

Handling of international domain names

Authors
  • Thomas Roessler

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file idna2.h.

Macro Definition Documentation

◆ MI_NO_FLAGS

#define MI_NO_FLAGS   0

Definition at line 33 of file idna2.h.

◆ MI_MAY_BE_IRREVERSIBLE

#define MI_MAY_BE_IRREVERSIBLE   (1 << 0)

Definition at line 34 of file idna2.h.

Function Documentation

◆ mutt_idna_intl_to_local()

char* mutt_idna_intl_to_local ( const char *  user,
const char *  domain,
uint8_t  flags 
)

Convert an email's domain from Punycode.

Parameters
userUsername
domainDomain
flagsFlags, e.g. MI_MAY_BE_IRREVERSIBLE
Return values
ptrNewly allocated local email address
NULLError in conversion

If $idn_decode is set, then the domain will be converted from Punycode. For example, "xn--ls8h.la" becomes the emoji domain: ":poop:.la" Then the user and domain are changed from 'utf-8' to the encoding in $charset.

If the flag MI_MAY_BE_IRREVERSIBLE is NOT given, then the results will be checked to make sure that the transformation is "undo-able".

Note
The caller must free the returned string.

Definition at line 147 of file idna.c.

148 {
149  char *mailbox = NULL;
150  char *reversed_user = NULL, *reversed_domain = NULL;
151  char *tmp = NULL;
152 
153  char *local_user = mutt_str_dup(user);
154  char *local_domain = mutt_str_dup(domain);
155 
156 #ifdef HAVE_LIBIDN
157  bool is_idn_encoded = check_idn(local_domain);
158  if (is_idn_encoded && C_IdnDecode)
159  {
160 #if (IDN_VERSION == 2)
161  if (idn2_to_unicode_8z8z(local_domain, &tmp, IDN2_ALLOW_UNASSIGNED) != IDN2_OK)
162 #else
163  if (idna_to_unicode_8z8z(local_domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
164 #endif
165  {
166  goto cleanup;
167  }
168  mutt_str_replace(&local_domain, tmp);
169  FREE(&tmp);
170  }
171 #endif /* HAVE_LIBIDN */
172 
173  /* we don't want charset-hook effects, so we set flags to 0 */
174  if (mutt_ch_convert_string(&local_user, "utf-8", C_Charset, MUTT_ICONV_NO_FLAGS) != 0)
175  goto cleanup;
176 
177  if (mutt_ch_convert_string(&local_domain, "utf-8", C_Charset, MUTT_ICONV_NO_FLAGS) != 0)
178  goto cleanup;
179 
180  /* make sure that we can convert back and come out with the same
181  * user and domain name. */
182  if ((flags & MI_MAY_BE_IRREVERSIBLE) == 0)
183  {
184  reversed_user = mutt_str_dup(local_user);
185 
186  if (mutt_ch_convert_string(&reversed_user, C_Charset, "utf-8", MUTT_ICONV_NO_FLAGS) != 0)
187  {
188  mutt_debug(LL_DEBUG1, "Not reversible. Charset conv to utf-8 failed for user = '%s'\n",
189  reversed_user);
190  goto cleanup;
191  }
192 
193  if (!mutt_istr_equal(user, reversed_user))
194  {
195  mutt_debug(LL_DEBUG1, "#1 Not reversible. orig = '%s', reversed = '%s'\n",
196  user, reversed_user);
197  goto cleanup;
198  }
199 
200  reversed_domain = mutt_str_dup(local_domain);
201 
202  if (mutt_ch_convert_string(&reversed_domain, C_Charset, "utf-8", MUTT_ICONV_NO_FLAGS) != 0)
203  {
204  mutt_debug(LL_DEBUG1, "Not reversible. Charset conv to utf-8 failed for domain = '%s'\n",
205  reversed_domain);
206  goto cleanup;
207  }
208 
209 #ifdef HAVE_LIBIDN
210  /* If the original domain was UTF-8, idna encoding here could
211  * produce a non-matching domain! Thus we only want to do the
212  * idna_to_ascii_8z() if the original domain was IDNA encoded. */
213  if (is_idn_encoded && C_IdnDecode)
214  {
215 #if (IDN_VERSION == 2)
216  if (idn2_to_ascii_8z(reversed_domain, &tmp,
217  IDN2_ALLOW_UNASSIGNED | IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL) != IDN2_OK)
218 #else
219  if (idna_to_ascii_8z(reversed_domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
220 #endif
221  {
222  mutt_debug(LL_DEBUG1, "Not reversible. idna_to_ascii_8z failed for domain = '%s'\n",
223  reversed_domain);
224  goto cleanup;
225  }
226  mutt_str_replace(&reversed_domain, tmp);
227  }
228 #endif /* HAVE_LIBIDN */
229 
230  if (!mutt_istr_equal(domain, reversed_domain))
231  {
232  mutt_debug(LL_DEBUG1, "#2 Not reversible. orig = '%s', reversed = '%s'\n",
233  domain, reversed_domain);
234  goto cleanup;
235  }
236  }
237 
238  mailbox = mutt_mem_malloc(mutt_str_len(local_user) + mutt_str_len(local_domain) + 2);
239  sprintf(mailbox, "%s@%s", NONULL(local_user), NONULL(local_domain));
240 
241 cleanup:
242  FREE(&local_user);
243  FREE(&local_domain);
244  FREE(&tmp);
245  FREE(&reversed_domain);
246  FREE(&reversed_user);
247 
248  return mailbox;
249 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_idna_local_to_intl()

char* mutt_idna_local_to_intl ( const char *  user,
const char *  domain 
)

Convert an email's domain to Punycode.

Parameters
userUsername
domainDomain
Return values
ptrNewly allocated Punycode email address
NULLError in conversion

The user and domain are assumed to be encoded according to $charset. They are converted to 'utf-8'. If $idn_encode is set, then the domain will be converted to Punycode. For example, the emoji domain: ":poop:.la" becomes "xn--ls8h.la"

Note
The caller must free the returned string.

Definition at line 265 of file idna.c.

266 {
267  char *mailbox = NULL;
268  char *tmp = NULL;
269 
270  char *intl_user = mutt_str_dup(user);
271  char *intl_domain = mutt_str_dup(domain);
272 
273  /* we don't want charset-hook effects, so we set flags to 0 */
274  if (mutt_ch_convert_string(&intl_user, C_Charset, "utf-8", MUTT_ICONV_NO_FLAGS) != 0)
275  goto cleanup;
276 
277  if (mutt_ch_convert_string(&intl_domain, C_Charset, "utf-8", MUTT_ICONV_NO_FLAGS) != 0)
278  goto cleanup;
279 
280 #ifdef HAVE_LIBIDN
281  if (C_IdnEncode)
282  {
283 #if (IDN_VERSION == 2)
284  if (idn2_to_ascii_8z(intl_domain, &tmp,
285  IDN2_ALLOW_UNASSIGNED | IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL) != IDN2_OK)
286 #else
287  if (idna_to_ascii_8z(intl_domain, &tmp, IDNA_ALLOW_UNASSIGNED) != IDNA_SUCCESS)
288 #endif
289  {
290  goto cleanup;
291  }
292  mutt_str_replace(&intl_domain, tmp);
293  }
294 #endif /* HAVE_LIBIDN */
295 
296  mailbox = mutt_mem_malloc(mutt_str_len(intl_user) + mutt_str_len(intl_domain) + 2);
297  sprintf(mailbox, "%s@%s", NONULL(intl_user), NONULL(intl_domain));
298 
299 cleanup:
300  FREE(&intl_user);
301  FREE(&intl_domain);
302  FREE(&tmp);
303 
304  return mailbox;
305 }
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_idna_print_version()

const char* mutt_idna_print_version ( void  )

Create an IDN version string.

Return values
ptrVersion string
Note
This is a static string and must not be freed.

Definition at line 313 of file idna.c.

314 {
315  static char vstring[256];
316 
317 #ifdef HAVE_LIBIDN
318 #if (IDN_VERSION == 2)
319  snprintf(vstring, sizeof(vstring), "libidn2: %s (compiled with %s)",
320  idn2_check_version(NULL), IDN2_VERSION);
321 #elif (IDN_VERSION == 1)
322  snprintf(vstring, sizeof(vstring), "libidn: %s (compiled with %s)",
323  stringprep_check_version(NULL), STRINGPREP_VERSION);
324 #endif
325 #endif /* HAVE_LIBIDN */
326 
327  return vstring;
328 }
+ Here is the caller graph for this function:

◆ mutt_idna_to_ascii_lz()

int mutt_idna_to_ascii_lz ( const char *  input,
char **  output,
uint8_t  flags 
)
+ Here is the caller graph for this function:

Variable Documentation

◆ C_IdnDecode

bool C_IdnDecode

◆ C_IdnEncode

bool C_IdnEncode
MI_MAY_BE_IRREVERSIBLE
#define MI_MAY_BE_IRREVERSIBLE
Definition: idna2.h:34
NONULL
#define NONULL(x)
Definition: string2.h:37
MUTT_ICONV_NO_FLAGS
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:73
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
FREE
#define FREE(x)
Definition: memory.h:40
C_IdnDecode
bool C_IdnDecode
mutt_ch_convert_string
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:754
mutt_istr_equal
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:883
C_IdnEncode
bool C_IdnEncode
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
mutt_str_len
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
mutt_str_replace
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
mutt_mem_malloc
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
C_Charset
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:53