NeoMutt  2023-11-03-85-g512e01
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
idna2.h File Reference

Handling of international domain names. More...

#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.
 
char * mutt_idna_local_to_intl (const char *user, const char *domain)
 Convert an email's domain to Punycode.
 
const char * mutt_idna_print_version (void)
 Create an IDN version string.
 
int mutt_idna_to_ascii_lz (const char *input, char **output, uint8_t flags)
 Convert a domain to Punycode.
 

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 29 of file idna2.h.

◆ MI_MAY_BE_IRREVERSIBLE

#define MI_MAY_BE_IRREVERSIBLE   (1 << 0)

Definition at line 30 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 116 of file idna.c.

117{
118 char *mailbox = NULL;
119 char *reversed_user = NULL, *reversed_domain = NULL;
120 char *tmp = NULL;
121
122 char *local_user = mutt_str_dup(user);
123 char *local_domain = mutt_str_dup(domain);
124
125#ifdef HAVE_LIBIDN
126 bool is_idn_encoded = check_idn(local_domain);
127 const bool c_idn_decode = cs_subset_bool(NeoMutt->sub, "idn_decode");
128 if (is_idn_encoded && c_idn_decode)
129 {
130 if (idn2_to_unicode_8z8z(local_domain, &tmp, IDN2_ALLOW_UNASSIGNED) != IDN2_OK)
131 {
132 goto cleanup;
133 }
134 mutt_str_replace(&local_domain, tmp);
135 FREE(&tmp);
136 }
137#endif
138
139 /* we don't want charset-hook effects, so we set flags to 0 */
140 if (mutt_ch_convert_string(&local_user, "utf-8", cc_charset(), MUTT_ICONV_NO_FLAGS) != 0)
141 goto cleanup;
142
143 if (mutt_ch_convert_string(&local_domain, "utf-8", cc_charset(), MUTT_ICONV_NO_FLAGS) != 0)
144 goto cleanup;
145
146 /* make sure that we can convert back and come out with the same
147 * user and domain name. */
148 if ((flags & MI_MAY_BE_IRREVERSIBLE) == 0)
149 {
150 reversed_user = mutt_str_dup(local_user);
151
152 if (mutt_ch_convert_string(&reversed_user, cc_charset(), "utf-8", MUTT_ICONV_NO_FLAGS) != 0)
153 {
154 mutt_debug(LL_DEBUG1, "Not reversible. Charset conv to utf-8 failed for user = '%s'\n",
155 reversed_user);
156 goto cleanup;
157 }
158
159 if (!mutt_istr_equal(user, reversed_user))
160 {
161 mutt_debug(LL_DEBUG1, "#1 Not reversible. orig = '%s', reversed = '%s'\n",
162 user, reversed_user);
163 goto cleanup;
164 }
165
166 reversed_domain = mutt_str_dup(local_domain);
167
168 if (mutt_ch_convert_string(&reversed_domain, cc_charset(), "utf-8", MUTT_ICONV_NO_FLAGS) != 0)
169 {
170 mutt_debug(LL_DEBUG1, "Not reversible. Charset conv to utf-8 failed for domain = '%s'\n",
171 reversed_domain);
172 goto cleanup;
173 }
174
175#ifdef HAVE_LIBIDN
176 /* If the original domain was UTF-8, idna encoding here could
177 * produce a non-matching domain! Thus we only want to do the
178 * idn2_to_ascii_8z() if the original domain was IDNA encoded. */
179 if (is_idn_encoded && c_idn_decode)
180 {
181 if (idn2_to_ascii_8z(reversed_domain, &tmp,
182 IDN2_ALLOW_UNASSIGNED | IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL) != IDN2_OK)
183 {
184 mutt_debug(LL_DEBUG1, "Not reversible. idn2_to_ascii_8z failed for domain = '%s'\n",
185 reversed_domain);
186 goto cleanup;
187 }
188 mutt_str_replace(&reversed_domain, tmp);
189 }
190#endif
191
192 if (!mutt_istr_equal(domain, reversed_domain))
193 {
194 mutt_debug(LL_DEBUG1, "#2 Not reversible. orig = '%s', reversed = '%s'\n",
195 domain, reversed_domain);
196 goto cleanup;
197 }
198 }
199
200 mutt_str_asprintf(&mailbox, "%s@%s", NONULL(local_user), NONULL(local_domain));
201
202cleanup:
203 FREE(&local_user);
204 FREE(&local_domain);
205 FREE(&tmp);
206 FREE(&reversed_domain);
207 FREE(&reversed_user);
208
209 return mailbox;
210}
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
const char * cc_charset(void)
Get the cached value of $charset.
Definition: config_cache.c:115
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define MI_MAY_BE_IRREVERSIBLE
Definition: idna2.h:30
static bool check_idn(char *domain)
Is domain in Punycode?
Definition: idna.c:60
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define FREE(x)
Definition: memory.h:45
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:826
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:810
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1022
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
#define NONULL(x)
Definition: string2.h:37
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
+ 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 226 of file idna.c.

227{
228 char *mailbox = NULL;
229 char *tmp = NULL;
230
231 char *intl_user = mutt_str_dup(user);
232 char *intl_domain = mutt_str_dup(domain);
233
234 /* we don't want charset-hook effects, so we set flags to 0 */
235 if (mutt_ch_convert_string(&intl_user, cc_charset(), "utf-8", MUTT_ICONV_NO_FLAGS) != 0)
236 goto cleanup;
237
238 if (mutt_ch_convert_string(&intl_domain, cc_charset(), "utf-8", MUTT_ICONV_NO_FLAGS) != 0)
239 goto cleanup;
240
241#ifdef HAVE_LIBIDN
242 const bool c_idn_encode = cs_subset_bool(NeoMutt->sub, "idn_encode");
243 if (c_idn_encode)
244 {
245 if (idn2_to_ascii_8z(intl_domain, &tmp,
246 IDN2_ALLOW_UNASSIGNED | IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL) != IDN2_OK)
247 {
248 goto cleanup;
249 }
250 mutt_str_replace(&intl_domain, tmp);
251 }
252#endif
253
254 mutt_str_asprintf(&mailbox, "%s@%s", NONULL(intl_user), NONULL(intl_domain));
255
256cleanup:
257 FREE(&intl_user);
258 FREE(&intl_domain);
259 FREE(&tmp);
260
261 return mailbox;
262}
+ 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 271 of file idna.c.

272{
273 static char vstring[256] = { 0 };
274
275 snprintf(vstring, sizeof(vstring), "libidn2: %s (compiled with %s)",
276 idn2_check_version(NULL), IDN2_VERSION);
277
278 return vstring;
279}
+ 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 
)

Convert a domain to Punycode.

Parameters
[in]inputDomain
[out]outputResult
[in]flagsFlags, e.g. IDNA_ALLOW_UNASSIGNED
Return values
0Success
>0Failure, error code

Convert a domain from the current locale to Punycode.

Note
The caller must free output

Definition at line 89 of file idna.c.

90{
91 if (!input || !output)
92 return 1;
93
94 return idn2_to_ascii_8z(input, output, flags | IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
95}
+ Here is the caller graph for this function: