]> err.no Git - sope/blob - sope-mime/NGMail/NSData+MimeQP.m
some code cleanups
[sope] / sope-mime / NGMail / NSData+MimeQP.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "common.h"
23 #include <string.h>
24
25 @implementation NSData(MimeQPHeaderFieldDecoding)
26
27 static Class NSStringClass   = Nil;
28 static Class NGMimeTypeClass = Nil;
29
30 static int   UseFoundationStringEncodingForMimeHeader = -1;
31
32 - (id)decodeQuotedPrintableValueOfMIMEHeaderField:(NSString *)_name {
33   // check data for 8-bit headerfields (RFC 2047 (MIME PART III))
34   /*
35     TODO: document
36     
37     This method returns an NSString or an NSData object (or nil).
38   */
39   enum {
40     NGMimeMessageParser_quoted_start   = 1,
41     NGMimeMessageParser_quoted_charSet = 2,
42     NGMimeMessageParser_quoted_qpData  = 3,
43     NGMimeMessageParser_quoted_end     = 4
44   } status = NGMimeMessageParser_quoted_start;
45   unsigned int        length;
46   const unsigned char *bytes, *firstEq;
47   BOOL foundQP = NO;
48
49   /* setup statics */
50
51   if (UseFoundationStringEncodingForMimeHeader == -1) {
52     UseFoundationStringEncodingForMimeHeader
53       = [[NSUserDefaults standardUserDefaults]
54           boolForKey:@"UseFoundationStringEncodingForMimeHeader"]
55       ? 1 : 0;
56   }
57
58   if (NSStringClass   == Nil) NSStringClass   = [NSString class];
59   if (NGMimeTypeClass == Nil) NGMimeTypeClass = [NGMimeType class];
60
61   
62   /* begin */
63   
64   length = [self length];
65   
66   /* check whether the string is long enough to be quoted etc */
67   if (length <= 6)
68     return self;
69   
70   /* check whether the string contains QP tokens ... */
71   bytes = [self bytes];
72   
73   if ((firstEq = memchr(bytes, '=', length)) == NULL)
74     return self;
75   
76   /* process data ... (quoting etc) */
77   {
78     unichar       *buffer;
79     unsigned int  bufLen, maxBufLen;
80     NSString      *charset;
81     BOOL          appendLC;
82     int           cnt, tmp;
83     unsigned char encoding;
84     
85     buffer = calloc(length + 13, sizeof(unichar));
86     
87     maxBufLen             = length + 3;
88     buffer[maxBufLen - 1] = '\0';
89     bufLen                = 0;
90     
91     encoding = 0;
92     tmp      = -1;
93     appendLC = YES;      
94     charset  = nil;
95     status   = NGMimeMessageParser_quoted_start;
96
97     /* copy data up to first '=' sign */
98     if ((cnt = (firstEq - bytes)) > 0) {
99       for (; bufLen < cnt; bufLen++) 
100         buffer[bufLen] = bytes[bufLen];
101     }
102     
103     for (; cnt < (length - 1); cnt++) {
104       appendLC = YES;      
105       
106       if (status == NGMimeMessageParser_quoted_start) {
107         if ((bytes[cnt] == '=') && (bytes[cnt + 1] == '?')) { // found begin
108           cnt++;
109           status = NGMimeMessageParser_quoted_charSet;
110         }
111         else { // other char
112           if (bytes[cnt + 1] != '=') {
113             buffer[bufLen++] = bytes[cnt];
114             buffer[bufLen++] = bytes[cnt+1];
115             cnt++;
116             if (cnt >= length - 1)
117               appendLC = NO;
118           }
119           else {
120             buffer[bufLen++] = bytes[cnt];
121           }
122         }
123       }
124       else if (status == NGMimeMessageParser_quoted_charSet) {
125         if (tmp == -1)
126           tmp = cnt;
127         
128         if (bytes[cnt] == '?') {
129           charset = 
130             [NSStringClass stringWithCString:(char *)(bytes + tmp) 
131                            length:(cnt - tmp)];
132           tmp = -1;
133           
134           if ((length - cnt) > 2) { 
135             // set encoding (eg 'q' for quoted printable)
136             cnt++; // skip '?'
137             encoding = bytes[cnt];
138             cnt++; // skip encoding
139             status = NGMimeMessageParser_quoted_qpData;
140           }
141           else { // unexpected end
142             NSLog(@"WARNING: unexpected end of header");
143             appendLC = NO;
144             break;
145           }
146         }
147       }
148       else if (status == NGMimeMessageParser_quoted_qpData) {
149         if (tmp == -1)
150           tmp = cnt;
151         
152         if ((bytes[cnt] == '?') && (bytes[cnt + 1] == '=')) {
153           NSData           *tmpData;
154           NSString         *tmpStr;
155           unsigned int     tmpLen;
156           
157           tmpData = _rfc2047Decoding(encoding, (char *)bytes + tmp, cnt - tmp);
158           foundQP = YES;
159
160           /* 
161              create a temporary string for charset conversion ... 
162              Note: the headerfield is currently held in ISO Latin 1
163           */
164           tmpStr = nil;
165           
166           if (!UseFoundationStringEncodingForMimeHeader) {
167             tmpStr = [NSStringClass stringWithData:tmpData
168                                     usingEncodingNamed:charset];
169           }
170           if (tmpStr == nil) {
171             NSStringEncoding enc;
172             
173             enc    = [NGMimeTypeClass stringEncodingForCharset:charset];
174             tmpStr = [[[NSStringClass alloc] initWithData:tmpData encoding:enc]
175                                       autorelease];
176           }
177           tmpLen = [tmpStr length];
178           
179           if ((tmpLen + bufLen) < maxBufLen) {
180             [tmpStr getCharacters:(buffer + bufLen)];
181             bufLen += tmpLen;
182           }
183           else {
184             [self errorWithFormat:@"%s: quoted data to large --> ignored %@",
185                   __PRETTY_FUNCTION__, tmpStr];
186           }
187           tmp = -1;
188           cnt++;
189           appendLC = YES;
190           status   = NGMimeMessageParser_quoted_start;
191         }
192       }
193     }
194     if (appendLC) {
195       if (cnt < length) {
196         buffer[bufLen] = bytes[cnt];
197         bufLen++;
198       }
199     }
200     buffer[bufLen] = '\0';
201     {
202       id data;
203
204       data = nil;
205       
206       if (buffer && foundQP) {
207         data = [[[NSStringClass alloc] initWithCharacters:buffer length:bufLen]
208                                 autorelease];
209         if (data == nil) {
210           [self warnWithFormat:
211                   @"%s: got no string for buffer '%s', length '%i' !", 
212                   __PRETTY_FUNCTION__, buffer, bufLen];
213         }
214       }
215       
216       if (data == nil)
217         data = self; /* we return an NSData */
218       
219       if (buffer != NULL) free(buffer); buffer = NULL;
220       return data;
221     }
222   }
223   return self;
224 }
225
226 @end /* NSData(MimeQPHeaderFieldDecoding) */