]> err.no Git - sope/blob - sope-mime/NGImap4/NSString+Imap4.m
fixed OGo bug #1899
[sope] / sope-mime / NGImap4 / NSString+Imap4.m
1 /*
2   Copyright (C) 2000-2007 SKYRIX Software AG
3   Copyright (C) 2007      Helge Hess
4
5   This file is part of SOPE.
6
7   SOPE is free software; you can redistribute it and/or modify it under
8   the terms of the GNU Lesser General Public License as published by the
9   Free Software Foundation; either version 2, or (at your option) any
10   later version.
11
12   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or
14   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15   License for more details.
16
17   You should have received a copy of the GNU Lesser General Public
18   License along with SOPE; see the file COPYING.  If not, write to the
19   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20   02111-1307, USA.
21 */
22
23 #include <NGImap4/NSString+Imap4.h>
24 #include "imCommon.h"
25
26 /* TODO: NOT UNICODE SAFE (uses cString) */
27
28 static void _encodeToModifiedUTF7(unsigned char *_buf, int encLen,
29                                   unsigned char **result_,
30                                   unsigned int *cntRes_);
31 static int _decodeOfModifiedUTF7(unsigned char *_target, unsigned _targetLen,
32                                  unsigned *usedBytes_ ,
33                                  unsigned char **buffer_,
34                                  int *bufLen_, int maxBuf);
35
36 @implementation NSString(Imap4)
37
38 - (NSString *)stringByEncodingImap4FolderName {
39   // TBD: this is restricted to Latin1, should be fixed to UTF-8
40   /* dude.d& --> dude.d&- */
41   unsigned char *buf    = NULL;
42   unsigned char *res    = NULL;
43   unsigned int  len     = 0;
44   unsigned int  cnt     = 0;
45   unsigned int  cntRes  = 0;
46   NSString      *result = nil;
47   NSData        *data;
48
49   len = [self cStringLength];
50   buf = calloc(len + 3, sizeof(char));  
51   res = calloc((len * 6) + 3, sizeof(char));  
52   buf[len] = '\0';
53   res[len * 6] = '\0';  
54   [self getCString:(char *)buf];
55
56   while (cnt < len) {
57     int c = buf[cnt];
58     if (((c > 31) && (c < 38)) ||
59         ((c > 38) && (c < 127))) {
60       res[cntRes++] = c;
61       cnt++;
62     }
63     else {
64       if (c == '&') {
65         res[cntRes++]  = '&';
66         res[cntRes++]  = '-';
67         cnt++;
68       }
69       else {
70         int start;
71
72         start = cnt;
73
74         while (cnt < (len - 1)) {
75           int c = buf[cnt + 1];
76           if (((c > 31) && (c < 38)) ||
77               ((c > 38) && (c < 127)) ||
78               (c == '&')) {
79             break;
80           }
81           else {
82             cnt++;
83           }
84         }
85         {
86           unsigned length;
87           
88           res[cntRes++] = '&';
89
90           length = cnt - start + 1;
91           
92           _encodeToModifiedUTF7(buf + start, length, &res, &cntRes);
93           
94           res[cntRes] = '-';
95           cntRes++;
96           cnt++;
97         }
98       }
99     }
100   }
101   if (buf != NULL) free(buf); buf = NULL;
102
103   data = [[NSData alloc] initWithBytesNoCopy:res length:cntRes 
104                          freeWhenDone:YES];
105   result = [[NSString alloc] initWithData:data
106                              encoding:NSISOLatin1StringEncoding];
107   [data release]; data = nil;
108   
109   return [result autorelease];
110 }
111
112 - (NSString *)stringByDecodingImap4FolderName {
113   // TBD: this is restricted to Latin1, should be fixed to UTF-8
114   /* dude/d&- --> dude/d& */
115   unsigned char *buf;
116   unsigned char *res;
117   unsigned int  len;
118   unsigned int  cnt     = 0;
119   unsigned int  cntRes  = 0;
120   NSString      *result = nil;
121   NSData        *data;
122   
123   if ((len = [self cStringLength]) == 0)
124     return @"";
125   
126   buf = calloc(len + 3, sizeof(unsigned char));
127   res = calloc(len + 3, sizeof(unsigned char));  
128   buf[len] = '\0';
129   res[len] = '\0';
130   
131   [self getCString:(char *)buf];
132   
133   while (cnt < (len - 1)) { /* &- */
134     unsigned char c;
135
136     c = buf[cnt];
137
138     if (c == '&') {
139       if (buf[cnt + 1] == '-') {
140         res[cntRes++] = '&';
141         cnt += 2;
142       }
143       else {
144         unsigned      usedBytes = 0;
145         unsigned char *buffer;
146         int           maxBuf, bufLen;
147
148         cnt++;
149         maxBuf = 511;
150         bufLen = 0;
151         buffer = calloc(maxBuf + 3, sizeof(char));
152         
153         if (_decodeOfModifiedUTF7(buf + cnt, len - cnt, &usedBytes , &buffer,
154                                   &bufLen, maxBuf) == 0) {
155           int  cnt1;
156           
157           cnt1 = 0;
158           while (cnt1 < bufLen) {
159             res[cntRes++] = buffer[cnt1++];
160           }
161           cnt += usedBytes;
162         }
163         else {
164           NSCAssert(NO, @"couldn't decode UTF-7 ..");
165         }
166         free(buffer); buffer = NULL;
167       }
168     }
169     else {
170       res[cntRes++] = c;
171       cnt++;
172     }
173   }
174   if (cnt < len)
175     res[cntRes++] = buf[cnt++];
176   
177   if (buf != NULL) free(buf); buf = NULL;
178
179   data = [[NSData alloc] initWithBytesNoCopy:res length:cntRes 
180                          freeWhenDone:YES];
181   result = [[NSString alloc] initWithData:data
182                              encoding:NSISOLatin1StringEncoding];
183   [data release]; data = nil;
184   
185   return [result autorelease];
186 }
187
188 - (NSString *)stringByEscapingImap4Password {
189   // TODO: perf
190   unichar  *buffer;
191   unichar  *chars;
192   unsigned len, i, j;
193   NSString *s;
194
195   len   = [self length];
196   chars = calloc(len + 2, sizeof(unichar));
197   [self getCharacters:chars];
198   
199   buffer = calloc(len * 2 + 2, sizeof(unichar));
200   buffer[len * 2] = '\0';
201   
202   for (i = 0, j = 0; i < len; i++, j++) {
203       BOOL conv = NO;
204       
205       if (chars[i] <= 0x1F || chars[i] > 0x7F) {
206         conv = YES;
207       }
208       else switch (chars[i]) {
209         case '(':
210         case ')':
211         case '{':
212         case ' ':
213         case '%':
214         case '*':
215         case '"':
216         case '\\':
217           conv = YES;
218           break;
219       }
220       
221       if (conv) {
222         buffer[j] = '\\';
223         j++;
224       }
225       buffer[j] = chars[i];
226   }
227   if (chars != NULL) free(chars); chars = NULL;
228   
229   s = [NSString stringWithCharacters:buffer length:j];
230   if (buffer != NULL) free(buffer); buffer = NULL;
231   return s;
232 }
233
234 @end /* NSString(Imap4) */
235
236 static void writeChunk(int _c1, int _c2, int _c3, int _pads,
237                        unsigned char **result_,
238                        unsigned int *cntRes_);
239
240 static int getChar(int _cnt, int *cnt_, unsigned char *_buf) {
241   int result;
242   
243   if ((_cnt % 2)) {
244     result = _buf[*cnt_];
245     (*cnt_)++;
246   }
247   else {
248     result = 0;
249   }
250   return result;
251 }
252 static void _encodeToModifiedUTF7(unsigned char *_buf, int encLen,
253                                   unsigned char **result_, unsigned int *cntRes_)
254 {
255   int c1, c2, c3;
256   int cnt, cntAll;
257
258   cnt    = 0;
259   cntAll = 0;
260
261   while (cnt < encLen) {
262     c1 = getChar(cntAll++, &cnt, _buf);
263     if (cnt == encLen) {
264       writeChunk(c1, 0, 0, 2, result_, cntRes_);
265     }
266     else {
267       c2 = getChar(cntAll++, &cnt, _buf);
268       if (cnt == encLen) {
269         writeChunk(c1, c2, 0, 1, result_, cntRes_);
270       }
271       else {
272         c3 = getChar(cntAll++, &cnt, _buf);
273         writeChunk(c1, c2, c3, 0, result_, cntRes_);
274       }
275     }
276   }
277 }
278
279 /* check metamail output for correctness */
280
281 static unsigned char basis_64[] =
282    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
283
284 static void writeChunk(int c1, int c2, int c3, int pads, unsigned char **result_,
285                        unsigned int *cntRes_) {
286   unsigned char c;
287
288   c = basis_64[c1>>2];
289   (*result_)[*cntRes_] = c;
290   (*cntRes_)++;
291   
292   c = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
293
294   (*result_)[*cntRes_] = c;
295   (*cntRes_)++;
296   
297   
298   if (pads == 2) {
299     ;
300   }
301   else if (pads) {
302     c = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
303     (*result_)[*cntRes_] = c;
304     (*cntRes_)++;
305   }
306   else {
307     c = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
308     
309     (*result_)[*cntRes_] = c;
310     (*cntRes_)++;
311     
312     c = basis_64[c3 & 0x3F];
313     (*result_)[*cntRes_] = c;
314     (*cntRes_)++;
315   }
316 }
317
318 static char index_64[128] = {
319     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
320     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
321     -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
322     52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
323     -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
324     15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
325     -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
326     41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
327 };
328
329 #define char64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
330
331 static int _decodeOfModifiedUTF7(unsigned char *_target, unsigned _targetLen,
332                                  unsigned *usedBytes_ , unsigned char **buffer_,
333                                  int *bufLen_, int maxBuf)
334 {
335   int c1, c2, c3, c4;
336   unsigned int cnt;
337   
338   for (cnt = 0; cnt < _targetLen; ) {
339     c1 = '=';
340     c2 = '=';
341     c3 = '=';
342     c4 = '=';
343
344     c1 = _target[cnt++];
345
346     if (c1 == '-') {
347       (*usedBytes_)++;
348       return 0;
349     }
350     if (cnt < _targetLen)
351       c2 = _target[cnt++];
352
353     if (c2 == '-') {
354       (*usedBytes_)+=2;
355       return 0;
356     }
357     
358     (*usedBytes_) += 2;
359
360     if (cnt < _targetLen) {
361       c3 = _target[cnt++];
362       (*usedBytes_)++;
363     }
364
365     if (cnt < _targetLen) {
366       c4 = _target[cnt++];
367       if (c3 != '-')
368         (*usedBytes_)++;
369     }
370     
371     if (c2 == -1 || c3 == -1 || c4 == -1) {
372       fprintf(stderr, "Warning: base64 decoder saw premature EOF!\n");
373       return 0;
374     }
375
376     if (c1 == '=' || c2 == '=') {
377       continue;
378     }
379     
380     c1 = char64(c1);
381     c2 = char64(c2);
382     
383     if (*bufLen_ < maxBuf) {
384       unsigned char c;
385
386       c = ((c1<<2) | ((c2&0x30)>>4));
387
388       if (c) {
389         (*buffer_)[*bufLen_] = c;
390         *bufLen_ = *bufLen_ + 1;
391       }
392     }
393     if (c3 == '-') {
394       return 0;
395     }
396     else if (c3 == '=') {
397       continue;
398     } else {
399       
400       c3 = char64(c3);
401
402       if (*bufLen_ < maxBuf) {
403         unsigned char c;
404         c = (((c2&0XF) << 4) | ((c3&0x3C) >> 2));
405         if (c) {
406           (*buffer_)[*bufLen_] = c;
407           *bufLen_ = *bufLen_ + 1;
408         }
409       }
410
411       if (c4 == '-') {
412         return 0;
413       }
414       else if (c4 == '=') {
415         continue;
416       } else {
417         c4 = char64(c4);
418
419         if (*bufLen_ < maxBuf) {
420           unsigned char c;
421
422           c = (((c3&0x03) <<6) | c4);
423           if (c) {
424             (*buffer_)[*bufLen_] = c;
425             (*bufLen_) = (*bufLen_) + 1;
426           }
427         }
428       }
429     }
430   }
431   return 0;
432 }