Update copyright year to 2018
[exim.git] / src / src / imap_utf7.c
CommitLineData
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
8uschar *
9imap_utf7_encode(uschar *string, const uschar *charset, uschar sep,
10 uschar *specials, uschar **error)
11{
12static uschar encode_base64[64] =
13 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
ed0512a1 14size_t slen;
acec9514
JH
15uschar *sptr;
16gstring * yield = NULL;
0539a19d
JH
17int i = 0, j; /* compiler quietening */
18uschar c = 0; /* compiler quietening */
ed0512a1
JH
19BOOL base64mode = FALSE;
20BOOL lastsep = FALSE;
21uschar utf16buf[256];
22uschar *utf16ptr;
23uschar *s;
24uschar outbuf[256];
25uschar *outptr = outbuf;
26#if HAVE_ICONV
27iconv_t icd;
28#endif
29
fc362fc5 30if (!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. */
34for (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
47if (!*s)
48 return string;
49
50sptr = string;
51slen = Ustrlen(string);
52
53#if HAVE_ICONV
fc362fc5 54if ((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
64while (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 182if (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
198iconv_close(icd);
199#endif
200
acec9514 201yield = string_catn(yield, outbuf, outptr - outbuf);
ed0512a1 202
acec9514
JH
203if (yield->s[yield->ptr-1] == '.')
204 yield->ptr--;
205
206return string_from_gstring(yield);
ed0512a1
JH
207}
208
209#endif /* whole file */
210/* vi: aw ai sw=2
211*/