Commit | Line | Data |
---|---|---|
ed0512a1 JH |
1 | #include "exim.h" |
2 | ||
8c5d388a | 3 | #ifdef SUPPORT_I18N |
ed0512a1 JH |
4 | |
5 | uschar * | |
6 | imap_utf7_encode(uschar *string, const uschar *charset, uschar sep, | |
7 | uschar *specials, uschar **error) | |
8 | { | |
9 | static uschar encode_base64[64] = | |
10 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; | |
11 | int ptr = 0; | |
12 | int size = 0; | |
13 | size_t slen; | |
14 | uschar *sptr, *yield = NULL; | |
0539a19d JH |
15 | int i = 0, j; /* compiler quietening */ |
16 | uschar c = 0; /* compiler quietening */ | |
ed0512a1 JH |
17 | BOOL base64mode = FALSE; |
18 | BOOL lastsep = FALSE; | |
19 | uschar utf16buf[256]; | |
20 | uschar *utf16ptr; | |
21 | uschar *s; | |
22 | uschar outbuf[256]; | |
23 | uschar *outptr = outbuf; | |
24 | #if HAVE_ICONV | |
25 | iconv_t icd; | |
26 | #endif | |
27 | ||
fc362fc5 | 28 | if (!specials) specials = US""; |
ed0512a1 JH |
29 | |
30 | /* Pass over the string. If it consists entirely of "normal" characters | |
31 | (possibly with leading seps), return it as is. */ | |
32 | for (s = string; *s; s++) | |
33 | { | |
34 | if (s == string && *s == sep) | |
35 | string++; | |
36 | if ( *s >= 0x7f | |
37 | || *s < 0x20 | |
38 | || strchr("./&", *s) | |
39 | || *s == sep | |
fc362fc5 | 40 | || Ustrchr(specials, *s) |
ed0512a1 JH |
41 | ) |
42 | break; | |
43 | } | |
44 | ||
45 | if (!*s) | |
46 | return string; | |
47 | ||
48 | sptr = string; | |
49 | slen = Ustrlen(string); | |
50 | ||
51 | #if HAVE_ICONV | |
fc362fc5 | 52 | if ((icd = iconv_open("UTF-16BE", CCS charset)) == (iconv_t)-1) |
ed0512a1 JH |
53 | { |
54 | *error = string_sprintf( | |
55 | "imapfolder: iconv_open(\"UTF-16BE\", \"%s\") failed: %s%s", | |
56 | charset, strerror(errno), | |
57 | errno == EINVAL ? " (maybe unsupported conversion)" : ""); | |
58 | return NULL; | |
59 | } | |
94431adb | 60 | #endif |
ed0512a1 JH |
61 | |
62 | while (slen > 0) | |
63 | { | |
64 | #if HAVE_ICONV | |
65 | size_t left = sizeof(utf16buf); | |
66 | utf16ptr = utf16buf; | |
67 | ||
68 | if ( iconv(icd, (ICONV_ARG2_TYPE)&sptr, &slen, CSS &utf16ptr, &left) | |
69 | == (size_t)-1 | |
70 | && errno != E2BIG | |
71 | ) | |
72 | { | |
73 | *error = string_sprintf("imapfolder: iconv() failed to convert from %s: %s", | |
74 | charset, strerror(errno)); | |
75 | iconv_close(icd); | |
76 | return NULL; | |
77 | } | |
78 | #else | |
94431adb | 79 | for (utf16ptr = utf16buf; |
ed0512a1 JH |
80 | slen > 0 && (utf16ptr - utf16buf) < sizeof(utf16buf); |
81 | utf16ptr += 2, slen--, sptr++) | |
82 | { | |
83 | *utf16ptr = *sptr; | |
84 | *(utf16ptr+1) = '\0'; | |
85 | } | |
86 | #endif | |
87 | ||
88 | s = utf16buf; | |
89 | while (s < utf16ptr) | |
90 | { | |
91 | /* Now encode utf16buf as modified UTF-7 */ | |
92 | if ( s[0] != 0 | |
93 | || s[1] >= 0x7f | |
94 | || s[1] < 0x20 | |
0539a19d | 95 | || (Ustrchr(specials, s[1]) && s[1] != sep) |
ed0512a1 JH |
96 | ) |
97 | { | |
98 | lastsep = FALSE; | |
99 | /* Encode as modified BASE64 */ | |
94431adb | 100 | if (!base64mode) |
ed0512a1 JH |
101 | { |
102 | *outptr++ = '&'; | |
103 | base64mode = TRUE; | |
104 | i = 0; | |
105 | } | |
106 | ||
94431adb | 107 | for (j = 0; j < 2; j++, s++) switch (i++) |
ed0512a1 JH |
108 | { |
109 | case 0: | |
110 | /* Top 6 bits of the first octet */ | |
111 | *outptr++ = encode_base64[(*s >> 2) & 0x3F]; | |
112 | c = (*s & 0x03); break; | |
113 | case 1: | |
114 | /* Bottom 2 bits of the first octet, and top 4 bits of the second */ | |
115 | *outptr++ = encode_base64[(c << 4) | ((*s >> 4) & 0x0F)]; | |
116 | c = (*s & 0x0F); break; | |
117 | case 2: | |
118 | /* Bottom 4 bits of the second octet and top 2 bits of the third */ | |
119 | *outptr++ = encode_base64[(c << 2) | ((*s >> 6) & 0x03)]; | |
120 | /* Bottom 6 bits of the third octet */ | |
121 | *outptr++ = encode_base64[*s & 0x3F]; | |
122 | i = 0; | |
123 | } | |
124 | } | |
125 | ||
126 | else if ( (s[1] != '.' && s[1] != '/') | |
127 | || s[1] == sep | |
128 | ) | |
129 | { | |
130 | /* Encode as self (almost) */ | |
131 | if (base64mode) | |
132 | { | |
94431adb | 133 | switch (i) |
ed0512a1 JH |
134 | { |
135 | case 1: | |
136 | /* Remaining bottom 2 bits of the last octet */ | |
137 | *outptr++ = encode_base64[c << 4]; | |
138 | break; | |
139 | case 2: | |
140 | /* Remaining bottom 4 bits of the last octet */ | |
141 | *outptr++ = encode_base64[c << 2]; | |
142 | } | |
143 | *outptr++ = '-'; | |
144 | base64mode = FALSE; | |
145 | } | |
146 | ||
147 | if (*++s == sep) | |
148 | { | |
149 | if (!lastsep) | |
150 | { | |
151 | *outptr++ = '.'; | |
152 | lastsep = TRUE; | |
153 | } | |
154 | } | |
155 | else | |
156 | { | |
157 | *outptr++ = *s; | |
158 | if (*s == '&') | |
159 | *outptr++ = '-'; | |
160 | lastsep = FALSE; | |
161 | } | |
162 | ||
163 | s++; | |
164 | } | |
165 | else | |
166 | { | |
167 | *error = string_sprintf("imapfolder: illegal character '%c'", s[1]); | |
168 | if (yield) store_reset(yield); | |
169 | return NULL; | |
170 | } | |
171 | ||
172 | if (outptr > outbuf + sizeof(outbuf) - 3) | |
173 | { | |
c2f669a4 | 174 | yield = string_catn(yield, &size, &ptr, outbuf, outptr - outbuf); |
ed0512a1 JH |
175 | outptr = outbuf; |
176 | } | |
177 | ||
178 | } | |
94431adb | 179 | } /* End of input string */ |
ed0512a1 | 180 | |
94431adb | 181 | if (base64mode) |
ed0512a1 | 182 | { |
94431adb | 183 | switch (i) |
ed0512a1 JH |
184 | { |
185 | case 1: | |
186 | /* Remaining bottom 2 bits of the last octet */ | |
187 | *outptr++ = encode_base64[c << 4]; | |
188 | break; | |
189 | case 2: | |
190 | /* Remaining bottom 4 bits of the last octet */ | |
191 | *outptr++ = encode_base64[c << 2]; | |
192 | } | |
193 | *outptr++ = '-'; | |
194 | } | |
195 | ||
196 | #if HAVE_ICONV | |
197 | iconv_close(icd); | |
198 | #endif | |
199 | ||
c2f669a4 | 200 | yield = string_catn(yield, &size, &ptr, outbuf, outptr - outbuf); |
ed0512a1 JH |
201 | if (yield[ptr-1] == '.') |
202 | ptr--; | |
203 | yield[ptr] = '\0'; | |
204 | ||
205 | return yield; | |
206 | } | |
207 | ||
208 | #endif /* whole file */ | |
209 | /* vi: aw ai sw=2 | |
210 | */ |