]> err.no Git - sope/blob - sope-core/NGExtensions/NGQuotedPrintableCoding.m
added -isNotEmpty to NSData
[sope] / sope-core / NGExtensions / NGQuotedPrintableCoding.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 "NGQuotedPrintableCoding.h"
23 #include "common.h"
24 #include "NGMemoryAllocation.h"
25
26 @implementation NSString(QuotedPrintableCoding)
27
28 - (NSString *)stringByDecodingQuotedPrintable {
29   NSData   *data;
30   unsigned len;
31   
32   if ((len = [self cStringLength]) > 0) {
33     void *buf;
34     buf = malloc(len + 10);
35     [self getCString:buf];
36     data = [NSData dataWithBytes:buf length:len];
37     if (buf) free(buf);
38   }
39   else
40     data = [NSData data];
41   
42   data = [data dataByDecodingQuotedPrintable];
43   return [NSString stringWithCString:[data bytes] length:[data length]];
44 }
45 - (NSString *)stringByEncodingQuotedPrintable {
46   NSData *data;
47   unsigned len;
48   
49   if ((len = [self cStringLength])) {
50     void *buf;
51     buf = malloc(len + 10);
52     [self getCString:buf];
53     data = [NSData dataWithBytes:buf length:len];
54     free(buf);
55   }
56   else
57     data = [NSData data];
58   
59   data = [data dataByEncodingQuotedPrintable];
60   return [NSString stringWithCString:[data bytes] length:[data length]];
61 }
62
63 @end
64
65 @implementation NSData(QuotedPrintableCoding)
66
67 - (NSData *)dataByDecodingQuotedPrintable {
68   char   *dest    = NULL;
69   size_t destSize = 0;
70   size_t resSize  = 0;
71   
72   destSize = [self length];
73   dest     = NGMallocAtomic(destSize * sizeof(char));
74
75   resSize = NGDecodeQuotedPrintable([self bytes],[self length],dest,destSize);
76   
77   return ((int)resSize != -1)
78     ? [NSData dataWithBytesNoCopy:dest length:resSize]
79     : nil;
80 }
81
82 - (NSData *)dataByEncodingQuotedPrintable {
83   const char   *bytes  = [self bytes];
84   unsigned int length  = [self length];
85   char         *des    = NULL;
86   unsigned int desLen  = 0;
87
88   desLen = length *3;
89   des = NGMallocAtomic(sizeof(char) * desLen);
90
91   desLen = NGEncodeQuotedPrintable(bytes, length, des, desLen);
92   
93   return (int)desLen != -1
94     ? [NSData dataWithBytesNoCopy:des length:desLen]
95     : nil;
96 }
97
98 @end
99
100 // implementation
101
102 static inline char __hexToChar(char c) {
103   if ((c > 47) && (c < 58)) // '0' .. '9'
104     return c - 48;
105   if ((c > 64) && (c < 71)) // 'A' .. 'F'
106     return c - 55;
107   if ((c > 96) && (c < 103)) // 'a' .. 'f'
108     return c - 87;
109   return -1;
110 }
111
112 int NGDecodeQuotedPrintable(const char *_src, unsigned _srcLen,
113                             char *_dest, unsigned _destLen) {
114   unsigned cnt     = 0;
115   unsigned destCnt = 0;
116
117   if (_srcLen < _destLen)
118     return -1;
119
120   for (cnt = 0; ((cnt < _srcLen) && (destCnt < _destLen)); cnt++) {
121     if (_src[cnt] != '=') {
122       _dest[destCnt++] = _src[cnt];
123     }
124     else {
125       if ((_srcLen - cnt) > 1) {
126         signed char c1, c2;
127
128         c1 = _src[++cnt];
129
130         if (c1 == '\r' || c1 == '\n') {
131           if (_src[cnt+1] == '\r' || _src[cnt+1] == '\n' )
132             cnt++;
133           continue;
134         }
135         c1 = __hexToChar(c1);
136         c2 = __hexToChar(_src[++cnt]);
137         
138         if ((c1 == -1) || (c2 == -1)) {
139           if ((_destLen - destCnt) > 1) {
140             _dest[destCnt++] = _src[cnt - 1];
141             _dest[destCnt++] = _src[cnt];
142           }
143           else
144             break;
145         }
146         else {
147           char c = ((c1 << 4) | c2);
148           _dest[destCnt++] = c;
149         }
150       }
151       else 
152         break;
153     }
154   }
155   if (cnt < _srcLen)
156     return -1;
157   return destCnt;
158 }
159
160 /*
161   From RFC 2045 Multipurpose Internet Mail Extensions
162
163   6.7. Quoted-Printable Content-Transfer-Encoding
164
165   ...
166
167   In this encoding, octets are to be represented as determined by the
168   following rules: 
169
170
171     (1)   (General 8bit representation) Any octet, except a CR or
172           LF that is part of a CRLF line break of the canonical
173           (standard) form of the data being encoded, may be
174           represented by an "=" followed by a two digit
175           hexadecimal representation of the octet's value.  The
176           digits of the hexadecimal alphabet, for this purpose,
177           are "0123456789ABCDEF".  Uppercase letters must be
178           used; lowercase letters are not allowed.  Thus, for
179           example, the decimal value 12 (US-ASCII form feed) can
180           be represented by "=0C", and the decimal value 61 (US-
181           ASCII EQUAL SIGN) can be represented by "=3D".  This
182           rule must be followed except when the following rules
183           allow an alternative encoding.
184
185     (2)   (Literal representation) Octets with decimal values of
186           33 through 60 inclusive, and 62 through 126, inclusive,
187           MAY be represented as the US-ASCII characters which
188           correspond to those octets (EXCLAMATION POINT through
189           LESS THAN, and GREATER THAN through TILDE,
190           respectively).
191
192     (3)   (White Space) Octets with values of 9 and 32 MAY be
193           represented as US-ASCII TAB (HT) and SPACE characters,
194           respectively, but MUST NOT be so represented at the end
195           of an encoded line. Any TAB (HT) or SPACE characters on an
196           encoded line MUST thus be followed on that line by a printable
197           character. In particular, an "=" at the end of an encoded line,
198           indicating a soft line break (see rule #5) may follow one or
199           more TAB (HT) or SPACE characters. It follows that an octet
200           with decimal value 9 or 32 appearing at the end of an encoded line
201           must be represented according to Rule #1. This rule is necessary
202           because some MTAs (Message Transport Agents, programs which transport
203           messages from one user to another, or perform a portion of such
204           transfers) are known to pad lines of text with SPACEs, and others
205           are known to remove "white space" characters from the end of a line.
206           Therefore, when decoding a Quoted-Printable body, any trailing white
207           space on a line must be deleted, as it will necessarily have been
208           added by intermediate transport agents. 
209
210
211     (4)   (Line Breaks) A line break in a text body, represented
212           as a CRLF sequence in the text canonical form, must be
213           represented by a (RFC 822) line break, which is also a
214           CRLF sequence, in the Quoted-Printable encoding.  Since
215           the canonical representation of media types other than
216           text do not generally include the representation of
217           line breaks as CRLF sequences, no hard line breaks
218           (i.e. line breaks that are intended to be meaningful
219           and to be displayed to the user) can occur in the
220           quoted-printable encoding of such types.  Sequences
221           like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely
222           appear in non-text data represented in quoted-
223           printable, of course.
224
225     (5)   (Soft Line Breaks) The Quoted-Printable encoding
226           REQUIRES that encoded lines be no more than 76
227           characters long.  If longer lines are to be encoded
228           with the Quoted-Printable encoding, "soft" line breaks
229           must be used.  An equal sign as the last character on a
230           encoded line indicates such a non-significant ("soft")
231           line break in the encoded text.
232
233 */          
234
235 int NGEncodeQuotedPrintable(const char *_src, unsigned _srcLen,
236                             char *_dest, unsigned _destLen) {
237   unsigned cnt      = 0;
238   unsigned destCnt  = 0;
239   char     hexT[16] = {'0','1','2','3','4','5','6','7','8',
240                        '9','A','B','C','D','E','F'};
241   
242   if (_srcLen > _destLen)
243     return -1;
244   
245   for (cnt = 0; (cnt < _srcLen) && (destCnt < _destLen); cnt++) {
246     char c = _src[cnt];
247     if ((c == 9)  ||
248         (c == 10) ||
249         (c == 13) ||
250         ((c > 31) && (c < 61)) ||
251         ((c > 61) && (c < 127))) { // no quoting
252       _dest[destCnt++] = c;
253     }
254     else { // need to be quoted
255       if (_destLen - destCnt > 2) {
256         _dest[destCnt++] = '=';
257         _dest[destCnt++] = hexT[(c >> 4) & 15];
258         _dest[destCnt++] = hexT[c & 15];
259       }
260       else 
261         break;
262     }
263   }
264   if (cnt < _srcLen)
265     return -1;
266   return destCnt;
267 }
268
269 // static linking
270
271 void __link_NGQuotedPrintableCoding(void) {
272   __link_NGQuotedPrintableCoding();
273 }