1 /* Copyright (c) University of Cambridge 1995 - 2018 */
2 /* See the file NOTICE for conditions of use and distribution. */
3 
4 #include "exim.h"
5 
6 #ifdef SUPPORT_I18N
7 
8 uschar *
imap_utf7_encode(uschar * string,const uschar * charset,uschar sep,uschar * specials,uschar ** error)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+,";
14 size_t slen;
15 uschar *sptr;
16 gstring * yield = NULL;
17 int i = 0;	/* compiler quietening */
18 uschar c = 0;	/* compiler quietening */
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 
30 if (!specials) specials = US"";
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
42      || Ustrchr(specials, *s)
43      )
44     break;
45   }
46 
47 if (!*s)
48   return string;
49 
50 sptr = string;
51 slen = Ustrlen(string);
52 
53 #if HAVE_ICONV
54 if ((icd = iconv_open("UTF-16BE", CCS charset)) == (iconv_t)-1)
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   }
62 #endif
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
81   for (utf16ptr = utf16buf;
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
97        || (Ustrchr(specials, s[1]) && s[1] != sep)
98        )
99       {
100       lastsep = FALSE;
101       /* Encode as modified BASE64 */
102       if (!base64mode)
103         {
104         *outptr++ = '&';
105         base64mode = TRUE;
106         i = 0;
107         }
108 
109       for (int j = 0; j < 2; j++, s++) switch (i++)
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         {
135         switch (i)
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]);
170       return NULL;
171       }
172 
173     if (outptr > outbuf + sizeof(outbuf) - 3)
174       {
175       yield = string_catn(yield, outbuf, outptr - outbuf);
176       outptr = outbuf;
177       }
178 
179     }
180   } /* End of input string */
181 
182 if (base64mode)
183   {
184   switch (i)
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 
201 yield = string_catn(yield, outbuf, outptr - outbuf);
202 
203 if (yield->s[yield->ptr-1] == '.')
204   yield->ptr--;
205 
206 return string_from_gstring(yield);
207 }
208 
209 #endif	/* whole file */
210 /* vi: aw ai sw=2
211 */
212