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; | |
810d16ad | 57 | |
0d7911ea JH |
58 | if ( (rc = idna_to_unicode_8z8z(CCS alabel, CSS &s1, IDNA_USE_STD3_ASCII_RULES)) |
59 | != IDNA_SUCCESS) | |
60 | { | |
61 | if (err) *err = US idna_strerror(rc); | |
62 | return NULL; | |
63 | } | |
64 | s = string_copy(s1); | |
65 | free(s1); | |
66 | return s; | |
67 | } | |
68 | ||
69 | /**************************************************/ | |
70 | /* localpart conversions */ | |
71 | ||
72 | ||
73 | uschar * | |
74 | string_localpart_utf8_to_alabel(const uschar * utf8, uschar ** err) | |
75 | { | |
76 | size_t ucs4_len; | |
77 | punycode_uint * p = (punycode_uint *) stringprep_utf8_to_ucs4(CCS utf8, -1, &ucs4_len); | |
78 | size_t p_len = ucs4_len*4; /* this multiplier is pure guesswork */ | |
79 | uschar * res = store_get(p_len+5); | |
80 | int rc; | |
81 | ||
82 | res[0] = 'x'; res[1] = 'n'; res[2] = res[3] = '-'; | |
83 | ||
84 | if ((rc = punycode_encode(ucs4_len, p, NULL, &p_len, res+4)) != PUNYCODE_SUCCESS) | |
85 | { | |
4e08fd50 | 86 | DEBUG(D_expand) debug_printf("l_u2a: bad '%s'\n", punycode_strerror(rc)); |
0d7911ea JH |
87 | free(p); |
88 | if (err) *err = US punycode_strerror(rc); | |
89 | return NULL; | |
90 | } | |
4e08fd50 | 91 | p_len += 4; |
0d7911ea JH |
92 | free(p); |
93 | res[p_len] = '\0'; | |
94 | return res; | |
95 | } | |
96 | ||
97 | ||
98 | uschar * | |
99 | string_localpart_alabel_to_utf8(const uschar * alabel, uschar ** err) | |
100 | { | |
101 | size_t p_len = strlen(alabel); | |
102 | punycode_uint * p; | |
4e08fd50 JH |
103 | uschar * s; |
104 | uschar * res; | |
0d7911ea JH |
105 | int rc; |
106 | ||
107 | if (alabel[0] != 'x' || alabel[1] != 'n' || alabel[2] != '-' || alabel[3] != '-') | |
108 | { | |
109 | if (err) *err = US"bad alabel prefix"; | |
110 | return NULL; | |
111 | } | |
0d7911ea | 112 | |
9d4319df | 113 | p_len -= 4; |
0d7911ea JH |
114 | p = (punycode_uint *) store_get((p_len+1) * sizeof(*p)); |
115 | ||
116 | if ((rc = punycode_decode(p_len, CCS alabel+4, &p_len, p, NULL)) != PUNYCODE_SUCCESS) | |
117 | { | |
118 | if (err) *err = US punycode_strerror(rc); | |
119 | return NULL; | |
120 | } | |
4e08fd50 JH |
121 | |
122 | s = stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len); | |
123 | res = string_copyn(s, p_len); | |
124 | free(s); | |
125 | return res; | |
0d7911ea JH |
126 | } |
127 | ||
128 | ||
b04be5e7 JH |
129 | /************************************************* |
130 | * Report the library versions. * | |
131 | *************************************************/ | |
132 | ||
133 | /* See a description in tls-openssl.c for an explanation of why this exists. | |
134 | ||
135 | Arguments: a FILE* to print the results to | |
136 | Returns: nothing | |
137 | */ | |
138 | ||
139 | void | |
140 | utf8_version_report(FILE *f) | |
141 | { | |
142 | fprintf(f, "Library version: IDN: Compile: %s\n" | |
143 | " Runtime: %s\n", | |
144 | STRINGPREP_VERSION, | |
145 | stringprep_check_version(NULL)); | |
146 | } | |
147 | ||
0d7911ea JH |
148 | #endif /* whole file */ |
149 | ||
150 | /* vi: aw ai sw=2 | |
151 | */ | |
152 | /* End of utf8.c */ |