Commit | Line | Data |
---|---|---|
0d7911ea JH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
5 | /* Copyright (c) Jeremy Harris 2015 */ | |
6 | /* See the file NOTICE for conditions of use and distribution. */ | |
7 | ||
8 | ||
9 | #include "exim.h" | |
10 | ||
11 | #ifdef EXPERIMENTAL_INTERNATIONAL | |
12 | ||
13 | #include <idna.h> | |
14 | #include <punycode.h> | |
15 | #include <stringprep.h> | |
16 | ||
17 | BOOL | |
18 | string_is_utf8(const uschar * s) | |
19 | { | |
20 | uschar c; | |
21 | while ((c = *s++)) if (c & 0x80) return TRUE; | |
22 | return FALSE; | |
23 | } | |
24 | ||
25 | /**************************************************/ | |
26 | /* Domain conversions */ | |
27 | ||
28 | uschar * | |
29 | string_domain_utf8_to_alabel(const uschar * utf8, uschar ** err) | |
30 | { | |
31 | uschar * s1; | |
32 | uschar * s; | |
33 | int rc; | |
34 | ||
35 | s = US stringprep_utf8_nfkc_normalize(CCS utf8, -1); | |
36 | if ( (rc = idna_to_ascii_8z(CCS s, CSS &s1, IDNA_USE_STD3_ASCII_RULES)) | |
37 | != IDNA_SUCCESS) | |
38 | { | |
39 | free(s); | |
40 | if (err) *err = US idna_strerror(rc); | |
41 | return NULL; | |
42 | } | |
43 | free(s); | |
44 | s = string_copy(s1); | |
45 | free(s1); | |
46 | return s; | |
47 | } | |
48 | ||
49 | ||
50 | ||
51 | uschar * | |
52 | string_domain_alabel_to_utf8(const uschar * alabel, uschar ** err) | |
53 | { | |
54 | uschar * s1; | |
55 | uschar * s; | |
56 | int rc; | |
57 | if ( (rc = idna_to_unicode_8z8z(CCS alabel, CSS &s1, IDNA_USE_STD3_ASCII_RULES)) | |
58 | != IDNA_SUCCESS) | |
59 | { | |
60 | if (err) *err = US idna_strerror(rc); | |
61 | return NULL; | |
62 | } | |
63 | s = string_copy(s1); | |
64 | free(s1); | |
65 | return s; | |
66 | } | |
67 | ||
68 | /**************************************************/ | |
69 | /* localpart conversions */ | |
70 | ||
71 | ||
72 | uschar * | |
73 | string_localpart_utf8_to_alabel(const uschar * utf8, uschar ** err) | |
74 | { | |
75 | size_t ucs4_len; | |
76 | punycode_uint * p = (punycode_uint *) stringprep_utf8_to_ucs4(CCS utf8, -1, &ucs4_len); | |
77 | size_t p_len = ucs4_len*4; /* this multiplier is pure guesswork */ | |
78 | uschar * res = store_get(p_len+5); | |
79 | int rc; | |
80 | ||
81 | res[0] = 'x'; res[1] = 'n'; res[2] = res[3] = '-'; | |
82 | ||
83 | if ((rc = punycode_encode(ucs4_len, p, NULL, &p_len, res+4)) != PUNYCODE_SUCCESS) | |
84 | { | |
85 | free(p); | |
86 | if (err) *err = US punycode_strerror(rc); | |
87 | return NULL; | |
88 | } | |
89 | free(p); | |
90 | res[p_len] = '\0'; | |
91 | return res; | |
92 | } | |
93 | ||
94 | ||
95 | uschar * | |
96 | string_localpart_alabel_to_utf8(const uschar * alabel, uschar ** err) | |
97 | { | |
98 | size_t p_len = strlen(alabel); | |
99 | punycode_uint * p; | |
100 | int rc; | |
101 | ||
102 | if (alabel[0] != 'x' || alabel[1] != 'n' || alabel[2] != '-' || alabel[3] != '-') | |
103 | { | |
104 | if (err) *err = US"bad alabel prefix"; | |
105 | return NULL; | |
106 | } | |
107 | p_len -= 4; | |
108 | ||
109 | p = (punycode_uint *) store_get((p_len+1) * sizeof(*p)); | |
110 | ||
111 | if ((rc = punycode_decode(p_len, CCS alabel+4, &p_len, p, NULL)) != PUNYCODE_SUCCESS) | |
112 | { | |
113 | if (err) *err = US punycode_strerror(rc); | |
114 | return NULL; | |
115 | } | |
116 | p[p_len] = 0; | |
117 | return US p; | |
118 | } | |
119 | ||
120 | ||
121 | #endif /* whole file */ | |
122 | ||
123 | /* vi: aw ai sw=2 | |
124 | */ | |
125 | /* End of utf8.c */ |