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); | |
37bf366e | 36 | if ( (rc = idna_to_ascii_8z(CCS s, CSS &s1, IDNA_ALLOW_UNASSIGNED)) |
0d7911ea JH |
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 | { | |
4e08fd50 | 85 | DEBUG(D_expand) debug_printf("l_u2a: bad '%s'\n", punycode_strerror(rc)); |
0d7911ea JH |
86 | free(p); |
87 | if (err) *err = US punycode_strerror(rc); | |
88 | return NULL; | |
89 | } | |
4e08fd50 | 90 | p_len += 4; |
0d7911ea JH |
91 | free(p); |
92 | res[p_len] = '\0'; | |
93 | return res; | |
94 | } | |
95 | ||
96 | ||
97 | uschar * | |
98 | string_localpart_alabel_to_utf8(const uschar * alabel, uschar ** err) | |
99 | { | |
100 | size_t p_len = strlen(alabel); | |
101 | punycode_uint * p; | |
4e08fd50 JH |
102 | uschar * s; |
103 | uschar * res; | |
0d7911ea JH |
104 | int rc; |
105 | ||
106 | if (alabel[0] != 'x' || alabel[1] != 'n' || alabel[2] != '-' || alabel[3] != '-') | |
107 | { | |
108 | if (err) *err = US"bad alabel prefix"; | |
109 | return NULL; | |
110 | } | |
0d7911ea | 111 | |
9d4319df | 112 | p_len -= 4; |
0d7911ea JH |
113 | p = (punycode_uint *) store_get((p_len+1) * sizeof(*p)); |
114 | ||
115 | if ((rc = punycode_decode(p_len, CCS alabel+4, &p_len, p, NULL)) != PUNYCODE_SUCCESS) | |
116 | { | |
117 | if (err) *err = US punycode_strerror(rc); | |
118 | return NULL; | |
119 | } | |
4e08fd50 JH |
120 | |
121 | s = stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len); | |
122 | res = string_copyn(s, p_len); | |
123 | free(s); | |
124 | return res; | |
0d7911ea JH |
125 | } |
126 | ||
127 | ||
b04be5e7 JH |
128 | /************************************************* |
129 | * Report the library versions. * | |
130 | *************************************************/ | |
131 | ||
132 | /* See a description in tls-openssl.c for an explanation of why this exists. | |
133 | ||
134 | Arguments: a FILE* to print the results to | |
135 | Returns: nothing | |
136 | */ | |
137 | ||
138 | void | |
139 | utf8_version_report(FILE *f) | |
140 | { | |
141 | fprintf(f, "Library version: IDN: Compile: %s\n" | |
142 | " Runtime: %s\n", | |
143 | STRINGPREP_VERSION, | |
144 | stringprep_check_version(NULL)); | |
145 | } | |
146 | ||
0d7911ea JH |
147 | #endif /* whole file */ |
148 | ||
149 | /* vi: aw ai sw=2 | |
150 | */ | |
151 | /* End of utf8.c */ |