NeoMutt  2025-12-11-980-ge38c27
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
utf7.c File Reference

Convert strings to/from utf7/utf8. More...

#include "config.h"
#include <stdbool.h>
#include <string.h>
#include "private.h"
#include "mutt/lib.h"
#include "core/lib.h"
+ Include dependency graph for utf7.c:

Go to the source code of this file.

Functions

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.
 
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.
 
void imap_utf_encode (bool unicode, char **s)
 Encode email from local charset to UTF-8.
 
void imap_utf_decode (bool unicode, char **s)
 Decode email from UTF-8 to local charset.
 

Variables

static const int Index64u [128]
 Lookup table for Base64 encoding/decoding.
 
static const char B64Chars [64]
 Characters of the Base64 encoding.
 

Detailed Description

Convert strings to/from utf7/utf8.

Authors
  • Edmund Grimley Evans
  • Richard Russon
  • Pietro Cerutti

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file utf7.c.

Function Documentation

◆ utf7_to_utf8()

static char * utf7_to_utf8 ( const char * u7,
size_t u7len,
char ** u8,
size_t * u8len )
static

Convert data from RFC2060's UTF-7 to UTF-8.

Parameters
[in]u7UTF-7 data
[in]u7lenLength of UTF-7 data
[out]u8Save the UTF-8 data pointer
[out]u8lenSave the UTF-8 data length
Return values
ptrUTF-8 data
NULLError

RFC2060 obviously intends the encoding to be unique (see point 5 in section 5.1.3), so we reject any non-canonical form, such as &ACY- (instead of &-) or &AMA-&AMA- (instead of &AMAAwA-).

Note
The result is NUL-terminated.
The caller must free() the returned data.

Definition at line 106 of file utf7.c.

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}
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
static const int Index64u[128]
Lookup table for Base64 encoding/decoding.
Definition utf7.c:66
+ Here is the caller graph for this function:

◆ utf8_to_utf7()

static char * utf8_to_utf7 ( const char * u8,
size_t u8len,
char ** u7,
size_t * u7len )
static

Convert data from UTF-8 to RFC2060's UTF-7.

Parameters
[in]u8UTF-8 data
[in]u8lenLength of UTF-8 data
[out]u7Save the UTF-7 data pointer
[out]u7lenSave the UTF-7 data length
Return values
ptrUTF-7 data
NULLError

Unicode characters above U+FFFF converted to a UTF-16 surrogate pair.

Note
The result is NUL-terminated.
The caller must free() the returned data.

Definition at line 254 of file utf7.c.

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 u8 += n;
323 u8len -= n;
324
325 if ((ch < 0x20) || (ch >= 0x7f))
326 {
327 if (!base64)
328 {
329 *p++ = '&';
330 base64 = true;
331 b = 0;
332 k = 10;
333 }
334
335 // For code points >= 0x10000 we need to use a UTF-16 surrogate pair
336 if (ch & ~0xffff)
337 {
338 ch -= 0x10000;
339 int pair1 = 0xd800 + (ch >> 10);
340 int pair2 = 0xdc00 + (ch & 0x3ff);
341
342 /* Output the high surrogate */
343 *p++ = B64Chars[b | pair1 >> k];
344 k -= 6;
345 for (; k >= 0; k -= 6)
346 *p++ = B64Chars[(pair1 >> k) & 0x3f];
347 b = (pair1 << (-k)) & 0x3f;
348 k += 16;
349
350 /* The low surrogate will be output just below */
351 ch = pair2;
352 }
353
354 *p++ = B64Chars[b | ch >> k];
355 k -= 6;
356 for (; k >= 0; k -= 6)
357 *p++ = B64Chars[(ch >> k) & 0x3f];
358 b = (ch << (-k)) & 0x3f;
359 k += 16;
360 }
361 else
362 {
363 if (base64)
364 {
365 if (k > 10)
366 *p++ = B64Chars[b];
367 *p++ = '-';
368 base64 = false;
369 }
370 *p++ = ch;
371 if (ch == '&')
372 *p++ = '-';
373 }
374 }
375
376 if (base64)
377 {
378 if (k > 10)
379 *p++ = B64Chars[b];
380 *p++ = '-';
381 }
382
383 *p++ = '\0';
384 if (u7len)
385 *u7len = p - buf;
386 MUTT_MEM_REALLOC(&buf, p - buf, char);
387 if (u7)
388 *u7 = buf;
389 return buf;
390
391bail:
392 FREE(&buf);
393 return NULL;
394}
static const char B64Chars[64]
Characters of the Base64 encoding.
Definition utf7.c:82
+ Here is the caller graph for this function:

◆ imap_utf_encode()

void imap_utf_encode ( bool unicode,
char ** s )

Encode email from local charset to UTF-8.

Parameters
[in]unicodetrue if Unicode is allowed
[out]sEmail to convert

Definition at line 401 of file utf7.c.

402{
403 if (!s || !*s)
404 return;
405
406 const char *c_charset = cc_charset();
407 if (!c_charset)
408 return;
409
410 if (unicode && mutt_ch_is_utf8(c_charset))
411 {
412 return;
413 }
414
415 if (mutt_ch_convert_string(s, c_charset, "utf-8", MUTT_ICONV_NONE) != 0)
416 {
417 FREE(s);
418 return;
419 }
420
421 if (!unicode)
422 {
423 char *utf7 = utf8_to_utf7(*s, strlen(*s), NULL, 0);
424 FREE(s);
425 *s = utf7;
426 }
427}
const char * cc_charset(void)
Get the cached value of $charset.
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
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ imap_utf_decode()

void imap_utf_decode ( bool unicode,
char ** s )

Decode email from UTF-8 to local charset.

Parameters
[in]unicodetrue if Unicode is allowed
[out]sEmail to convert

Definition at line 434 of file utf7.c.

435{
436 if (!s || !*s)
437 return;
438
439 const char *c_charset = cc_charset();
440 if (!c_charset)
441 return;
442
443 if (unicode && mutt_ch_is_utf8(c_charset))
444 {
445 return;
446 }
447
448 if (!unicode)
449 {
450 char *utf8 = utf7_to_utf8(*s, strlen(*s), 0, 0);
451 FREE(s);
452 *s = utf8;
453 }
454
455 if (mutt_ch_convert_string(s, "utf-8", c_charset, MUTT_ICONV_NONE) != 0)
456 {
457 FREE(s);
458 }
459}
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
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ Index64u

const int Index64u[128]
static
Initial value:
= {
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, 63,-1,-1,-1,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
}

Lookup table for Base64 encoding/decoding.

This is very similar to the table in lib/lib_base64.c Encoding chars: utf7 A-Za-z0-9+, mime A-Za-z0-9+/

Definition at line 66 of file utf7.c.

66 {
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};

◆ B64Chars

const char B64Chars[64]
static
Initial value:
= {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', ',',
}

Characters of the Base64 encoding.

Definition at line 82 of file utf7.c.

82 {
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};