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