NeoMutt  2025-12-11-1052-ga8f1ca
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
utf7.c
Go to the documentation of this file.
1
24
50
51#include "config.h"
52#include <stdbool.h>
53#include <string.h>
54#include "private.h"
55#include "mutt/lib.h"
56#include "core/lib.h"
57
66static const int Index64u[128] = {
67 // clang-format off
68 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
69 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
70 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, 63,-1,-1,-1,
71 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
72 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
73 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
74 -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
75 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
76 // clang-format on
77};
78
82static const char B64Chars[64] = {
83 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
84 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
85 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
86 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
87 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', ',',
88};
89
106static char *utf7_to_utf8(const char *u7, size_t u7len, char **u8, size_t *u8len)
107{
108 int b;
109 int ch;
110 int k;
111
112 char *buf = MUTT_MEM_MALLOC(u7len + u7len / 8 + 1, char);
113 char *p = buf;
114 int pair1 = 0;
115
116 for (; u7len; u7++, u7len--)
117 {
118 if (*u7 == '&')
119 {
120 u7++;
121 u7len--;
122
123 if (u7len && (*u7 == '-'))
124 {
125 *p++ = '&';
126 continue;
127 }
128
129 ch = 0;
130 k = 10;
131 for (; u7len; u7++, u7len--)
132 {
133 if ((*u7 & 0x80) || ((b = Index64u[(int) *u7]) == -1))
134 break;
135 if (k > 0)
136 {
137 ch |= b << k;
138 k -= 6;
139 }
140 else
141 {
142 ch |= b >> (-k);
143 if (ch < 0x80)
144 {
145 if ((0x20 <= ch) && (ch < 0x7f))
146 {
147 /* Printable US-ASCII */
148 goto bail;
149 }
150 *p++ = ch;
151 }
152 else if (ch < 0x800)
153 {
154 *p++ = 0xc0 | (ch >> 6);
155 *p++ = 0x80 | (ch & 0x3f);
156 }
157 else
158 {
159 /* High surrogate pair */
160 if ((ch & ~0x3ff) == 0xd800)
161 {
162 if (pair1)
163 goto bail;
164 pair1 = ch;
165 }
166 else
167 {
168 /* Low surrogate pair */
169 if ((ch & ~0x3ff) == 0xdc00)
170 {
171 if (!pair1)
172 goto bail;
173
174 ch = ((pair1 - 0xd800) << 10) + (ch - 0xdc00) + 0x10000;
175 pair1 = 0;
176 }
177 if (pair1)
178 goto bail;
179
180 if (ch < 0x10000)
181 {
182 *p++ = 0xe0 | (ch >> 12);
183 *p++ = 0x80 | ((ch >> 6) & 0x3f);
184 *p++ = 0x80 | (ch & 0x3f);
185 }
186 else
187 {
188 *p++ = 0xf0 | (ch >> 18);
189 *p++ = 0x80 | ((ch >> 12) & 0x3f);
190 *p++ = 0x80 | ((ch >> 6) & 0x3f);
191 *p++ = 0x80 | (ch & 0x3f);
192 }
193 }
194 }
195
196 ch = (b << (16 + k)) & 0xffff;
197 k += 10;
198 }
199 }
200 if (ch || (k < 6))
201 {
202 /* Non-zero or too many extra bits */
203 goto bail;
204 }
205 if (!u7len || (*u7 != '-'))
206 {
207 /* BASE64 not properly terminated */
208 goto bail;
209 }
210 if ((u7len > 2) && (u7[1] == '&') && (u7[2] != '-'))
211 {
212 /* Adjacent BASE64 sections */
213 goto bail;
214 }
215 }
216 else if ((*u7 < 0x20) || (*u7 >= 0x7f))
217 {
218 /* Not printable US-ASCII */
219 goto bail;
220 }
221 else
222 {
223 *p++ = *u7;
224 }
225 }
226 *p++ = '\0';
227 if (u8len)
228 *u8len = p - buf;
229
230 MUTT_MEM_REALLOC(&buf, p - buf, char);
231 if (u8)
232 *u8 = buf;
233 return buf;
234
235bail:
236 FREE(&buf);
237 return NULL;
238}
239
254static char *utf8_to_utf7(const char *u8, size_t u8len, char **u7, size_t *u7len)
255{
256 int ch;
257 int n;
258 int b = 0;
259 int k = 0;
260 bool base64 = false;
261
262 /* In the worst case we convert 2 chars to 7 chars. For example:
263 * "\x10&\x10&..." -> "&ABA-&-&ABA-&-...". */
264 char *buf = MUTT_MEM_MALLOC((u8len / 2) * 7 + 6, char);
265 char *p = buf;
266
267 while (u8len)
268 {
269 unsigned char c = *u8;
270
271 if (c < 0x80)
272 {
273 ch = c;
274 n = 0;
275 }
276 else if (c < 0xc2)
277 {
278 goto bail;
279 }
280 else if (c < 0xe0)
281 {
282 ch = c & 0x1f;
283 n = 1;
284 }
285 else if (c < 0xf0)
286 {
287 ch = c & 0x0f;
288 n = 2;
289 }
290 else if (c < 0xf8)
291 {
292 ch = c & 0x07;
293 n = 3;
294 }
295 else if (c < 0xfc)
296 {
297 ch = c & 0x03;
298 n = 4;
299 }
300 else if (c < 0xfe)
301 {
302 ch = c & 0x01;
303 n = 5;
304 }
305 else
306 {
307 goto bail;
308 }
309
310 u8++;
311 u8len--;
312 if (n > u8len)
313 goto bail;
314 for (int i = 0; i < n; i++)
315 {
316 if ((u8[i] & 0xc0) != 0x80)
317 goto bail;
318 ch = (ch << 6) | (u8[i] & 0x3f);
319 }
320 if ((n > 1) && !(ch >> (n * 5 + 1)))
321 goto bail;
322 if (ch > 0x10ffff)
323 {
324 /* Outside Unicode, so it has no UTF-16 representation */
325 goto bail;
326 }
327 u8 += n;
328 u8len -= n;
329
330 if ((ch < 0x20) || (ch >= 0x7f))
331 {
332 if (!base64)
333 {
334 *p++ = '&';
335 base64 = true;
336 b = 0;
337 k = 10;
338 }
339
340 // For code points >= 0x10000 we need to use a UTF-16 surrogate pair
341 if (ch & ~0xffff)
342 {
343 ch -= 0x10000;
344 int pair1 = 0xd800 + (ch >> 10);
345 int pair2 = 0xdc00 + (ch & 0x3ff);
346
347 /* Output the high surrogate */
348 *p++ = B64Chars[b | pair1 >> k];
349 k -= 6;
350 for (; k >= 0; k -= 6)
351 *p++ = B64Chars[(pair1 >> k) & 0x3f];
352 b = (pair1 << (-k)) & 0x3f;
353 k += 16;
354
355 /* The low surrogate will be output just below */
356 ch = pair2;
357 }
358
359 *p++ = B64Chars[b | ch >> k];
360 k -= 6;
361 for (; k >= 0; k -= 6)
362 *p++ = B64Chars[(ch >> k) & 0x3f];
363 b = (ch << (-k)) & 0x3f;
364 k += 16;
365 }
366 else
367 {
368 if (base64)
369 {
370 if (k > 10)
371 *p++ = B64Chars[b];
372 *p++ = '-';
373 base64 = false;
374 }
375 *p++ = ch;
376 if (ch == '&')
377 *p++ = '-';
378 }
379 }
380
381 if (base64)
382 {
383 if (k > 10)
384 *p++ = B64Chars[b];
385 *p++ = '-';
386 }
387
388 *p++ = '\0';
389 if (u7len)
390 *u7len = p - buf;
391 MUTT_MEM_REALLOC(&buf, p - buf, char);
392 if (u7)
393 *u7 = buf;
394 return buf;
395
396bail:
397 FREE(&buf);
398 return NULL;
399}
400
406void imap_utf_encode(bool unicode, char **s)
407{
408 if (!s || !*s)
409 return;
410
411 const char *c_charset = cc_charset();
412 if (!c_charset)
413 return;
414
415 if (unicode && mutt_ch_is_utf8(c_charset))
416 {
417 return;
418 }
419
420 if (mutt_ch_convert_string(s, c_charset, "utf-8", MUTT_ICONV_NONE) != 0)
421 {
422 FREE(s);
423 return;
424 }
425
426 if (!unicode)
427 {
428 char *utf7 = utf8_to_utf7(*s, strlen(*s), NULL, 0);
429 FREE(s);
430 *s = utf7;
431 }
432}
433
439void imap_utf_decode(bool unicode, char **s)
440{
441 if (!s || !*s)
442 return;
443
444 const char *c_charset = cc_charset();
445 if (!c_charset)
446 return;
447
448 if (unicode && mutt_ch_is_utf8(c_charset))
449 {
450 return;
451 }
452
453 if (!unicode)
454 {
455 char *utf8 = utf7_to_utf8(*s, strlen(*s), 0, 0);
456 FREE(s);
457 *s = utf8;
458 }
459
460 if (mutt_ch_convert_string(s, "utf-8", c_charset, MUTT_ICONV_NONE) != 0)
461 {
462 FREE(s);
463 }
464}
const char * cc_charset(void)
Get the cached value of $charset.
Convenience wrapper for the core headers.
Shared constants/structs that are private to IMAP.
#define MUTT_MEM_MALLOC(n, T)
Definition memory.h:73
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:92
#define MUTT_MEM_REALLOC(pptr, n, T)
Definition memory.h:78
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition charset.c:819
#define MUTT_ICONV_NONE
No flags are set.
Definition charset.h:66
#define mutt_ch_is_utf8(str)
Definition charset.h:107
Convenience wrapper for the library headers.
void imap_utf_encode(bool unicode, char **s)
Encode email from local charset to UTF-8.
Definition utf7.c:406
static char * utf8_to_utf7(const char *u8, size_t u8len, char **u7, size_t *u7len)
Convert data from UTF-8 to RFC2060's UTF-7.
Definition utf7.c:254
void imap_utf_decode(bool unicode, char **s)
Decode email from UTF-8 to local charset.
Definition utf7.c:439
static const char B64Chars[64]
Characters of the Base64 encoding.
Definition utf7.c:82
static char * utf7_to_utf8(const char *u7, size_t u7len, char **u8, size_t *u8len)
Convert data from RFC2060's UTF-7 to UTF-8.
Definition utf7.c:106
static const int Index64u[128]
Lookup table for Base64 encoding/decoding.
Definition utf7.c:66