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 27static Class NSStringClass = Nil; 28static Class NGMimeTypeClass = Nil; 29 30- (id)decodeQuotedPrintableValueOfMIMEHeaderField:(NSString *)_name { 31 // check data for 8-bit headerfields (RFC 2047 (MIME PART III)) 32 /* 33 TODO: document 34 35 This method returns an NSString or an NSData object (or nil). 36 */ 37 enum { 38 NGMimeMessageParser_quoted_start = 1, 39 NGMimeMessageParser_quoted_charSet = 2, 40 NGMimeMessageParser_quoted_qpData = 3, 41 NGMimeMessageParser_quoted_end = 4 42 } status = NGMimeMessageParser_quoted_start; 43 unsigned int length; 44 const unsigned char *bytes, *firstEq; 45 BOOL foundQP = NO; 46 47 /* setup statics */ 48 if (NSStringClass == Nil) NSStringClass = [NSString class]; 49 if (NGMimeTypeClass == Nil) NGMimeTypeClass = [NGMimeType class]; 50 51 52 /* begin */ 53 length = [self length]; 54 55 /* check whether the string is long enough to be quoted etc */ 56 if (length <= 6) 57 return self; 58 59 /* check whether the string contains QP tokens ... */ 60 bytes = [self bytes]; 61 62 if ((firstEq = memchr(bytes, '=', length)) == NULL) 63 return self; 64 65 /* process data ... (quoting etc) */ 66 { 67 unichar *buffer; 68 unsigned int bufLen, maxBufLen; 69 NSString *charset; 70 BOOL appendLC, encodedLastWord; 71 int cnt, tmp; 72 unsigned char encoding; 73 74 buffer = calloc(length + 13, sizeof(unichar)); 75 76 maxBufLen = length + 3; 77 buffer[maxBufLen - 1] = '\0'; 78 bufLen = 0; 79 80 encoding = 0; 81 tmp = -1; 82 appendLC = YES; 83 encodedLastWord = NO; 84 charset = nil; 85 status = NGMimeMessageParser_quoted_start; 86 87 /* copy data up to first '=' sign */ 88 if ((cnt = (firstEq - bytes)) > 0) { 89 for (; bufLen < cnt; bufLen++) 90 buffer[bufLen] = bytes[bufLen]; 91 } 92 93 for (; cnt < (length - 1); cnt++) { 94 appendLC = YES; 95 96 if (status == NGMimeMessageParser_quoted_start) { 97 if ((bytes[cnt] == '=') && (bytes[cnt + 1] == '?')) { // found begin 98 cnt++; 99 if (encodedLastWord && tmp != -1) { 100 // We must remove the whitespace added since `tmp` 101 int i; 102 for (i = bufLen - 1; i >= tmp; i--) 103 buffer[i] = '\0'; 104 bufLen = tmp; 105 } 106 tmp = -1; 107 status = NGMimeMessageParser_quoted_charSet; 108 } else if (bytes[cnt] == ' ' || bytes[cnt] == '\r' || 109 bytes[cnt] == '\n' || bytes[cnt] == '\t') { 110 // We must ignore whitespace between qp encoded strings. 111 // Both are valid: 112 // (ab) -> (=?utf-8?q?a?= =?utf-8?q?b?=) 113 // (ab) -> (=?utf-8?q?a?= 114 // =?utf-8?q?b?=) 115 if (tmp == -1) // first whitespace found 116 tmp = bufLen; 117 buffer[bufLen++] = bytes[cnt]; 118 } else { 119 encodedLastWord = NO; 120 buffer[bufLen++] = bytes[cnt]; 121 } 122 } 123 else if (status == NGMimeMessageParser_quoted_charSet) { 124 if (tmp == -1) 125 tmp = cnt; 126 127 if (bytes[cnt] == '?') { 128 charset = 129 [NSStringClass stringWithCString:(char *)(bytes + tmp) 130 length:(cnt - tmp)]; 131 tmp = -1; 132 133 if ((length - cnt) > 2) { 134 // set encoding (eg 'q' for quoted printable) 135 cnt++; // skip '?' 136 encoding = bytes[cnt]; 137 cnt++; // skip encoding 138 status = NGMimeMessageParser_quoted_qpData; 139 } 140 else { // unexpected end 141 NSLog(@"WARNING: unexpected end of header"); 142 appendLC = NO; 143 break; 144 } 145 } 146 } 147 else if (status == NGMimeMessageParser_quoted_qpData) { 148 if (tmp == -1) 149 tmp = cnt; 150 151 if ((bytes[cnt] == '?') && (bytes[cnt + 1] == '=')) { 152 NSData *tmpData; 153 NSString *tmpStr; 154 unsigned int tmpLen; 155 156 tmpData = _rfc2047Decoding(encoding, (char *)bytes + tmp, cnt - tmp); 157 foundQP = YES; 158 159 /* 160 create a temporary string for charset conversion ... 161 Note: the headerfield is currently held in ISO Latin 1 162 */ 163 tmpStr = [NSStringClass stringWithData:tmpData 164 usingEncodingNamed:charset]; 165 166 if (tmpStr == nil) { 167 NSStringEncoding enc; 168 169 enc = [NGMimeTypeClass stringEncodingForCharset:charset]; 170 tmpStr = [[[NSStringClass alloc] initWithData:tmpData encoding:enc] 171 autorelease]; 172 173 // Fall-back to ISO Latin 1 if the decoding using the specified charset 174 // has failed. Note that we shouldn't be doing this here, we should let 175 // the application decide what to do but SOPE is just too lame and does 176 // not expose a failure vs. the initial value of the header 177 if (!tmpStr) 178 tmpStr = [[[NSStringClass alloc] initWithData:tmpData 179 encoding:NSISOLatin1StringEncoding] 180 autorelease]; 181 } 182 tmpLen = [tmpStr length]; 183 184 if ((tmpLen + bufLen) < maxBufLen) { 185 [tmpStr getCharacters:(buffer + bufLen)]; 186 bufLen += tmpLen; 187 } 188 else { 189 [self errorWithFormat:@"%s: quoted data to large --> ignored %@", 190 __PRETTY_FUNCTION__, tmpStr]; 191 } 192 tmp = -1; 193 cnt++; 194 appendLC = YES; 195 status = NGMimeMessageParser_quoted_start; 196 encodedLastWord = YES; 197 } 198 } 199 } 200 if (appendLC) { 201 if (cnt < length) { 202 buffer[bufLen] = bytes[cnt]; 203 bufLen++; 204 } 205 } 206 buffer[bufLen] = '\0'; 207 while(bufLen > 1 && buffer[bufLen-1] == '\0') 208 bufLen--; 209 { 210 id data; 211 212 data = nil; 213 214 if (buffer && foundQP) { 215 data = [[[NSStringClass alloc] initWithCharacters:buffer length:bufLen] 216 autorelease]; 217 if (data == nil) { 218 [self warnWithFormat: 219 @"%s: got no string for buffer '%s', length '%i' !", 220 __PRETTY_FUNCTION__, buffer, bufLen]; 221 } 222 } 223 224 if (data == nil) 225 data = self; /* we return an NSData */ 226 227 if (buffer != NULL) free(buffer); buffer = NULL; 228 return data; 229 } 230 } 231 return self; 232} 233 234@end /* NSData(MimeQPHeaderFieldDecoding) */ 235