1 //=============================================================================
2 // File:       dw_cte.cpp
3 // Contents:   Function definitions for content transfer encodings
4 // Maintainer: Doug Sauder <dwsauder@fwb.gulf.net>
5 // WWW:        http://www.fwb.gulf.net/~dwsauder/mimepp.html
6 // $Revision: 1.6 $
7 // $Date: 1997/09/27 11:53:43 $
8 //
9 // Copyright (c) 1996, 1997 Douglas W. Sauder
10 // All rights reserved.
11 //
12 // IN NO EVENT SHALL DOUGLAS W. SAUDER BE LIABLE TO ANY PARTY FOR DIRECT,
13 // INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
14 // THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DOUGLAS W. SAUDER
15 // HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16 //
17 // DOUGLAS W. SAUDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
18 // NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 // PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
20 // BASIS, AND DOUGLAS W. SAUDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
21 // SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
22 //
23 //=============================================================================
24 
25 #define DW_IMPLEMENTATION
26 
27 #include <mimelib/config.h>
28 #include <mimelib/debug.h>
29 #include <string.h>
30 #include <mimelib/string.h>
31 #include <mimelib/utility.h>
32 
33 #define MAXLINE  76
34 
35 static size_t calc_crlf_buff_size(const char* srcBuf, size_t srcLen);
36 static int to_crlf(const char* srcBuf, size_t srcLen, char* destBuf,
37     size_t destSize, size_t* destLen);
38 static int to_lf(const char* srcBuf, size_t srcLen, char* destBuf,
39     size_t destSize, size_t* destLen);
40 static int to_cr(const char* srcBuf, size_t srcLen, char* destBuf,
41     size_t destSize, size_t* destLen);
42 static int encode_base64(const char* aIn, size_t aInLen, char* aOut,
43     size_t aOutSize, size_t* aOutLen);
44 static int decode_base64(const char* aIn, size_t aInLen, char* aOut,
45     size_t aOutSize, size_t* aOutLen);
46 static int encode_qp(const char* aIn, size_t aInLen, char* aOut,
47     size_t aOutSize, size_t* aOutLen);
48 static int decode_qp(const char* aIn, size_t aInLen, char* aOut,
49     size_t aOutSize, size_t* aOutLen);
50 static size_t calc_qp_buff_size(const char* aIn, size_t aInLen);
51 
52 
DwToCrLfEol(const DwString & aSrcStr,DwString & aDestStr)53 int DwToCrLfEol(const DwString& aSrcStr, DwString& aDestStr)
54 {
55     // Estimate required destination buffer size
56     size_t srcLen = aSrcStr.length();
57     const char* srcBuf = aSrcStr.data();
58     size_t destSize = calc_crlf_buff_size(srcBuf, srcLen);
59 
60     // Allocate destination buffer
61     DwString destStr(destSize, (char)0);
62     char* destBuf = (char*) destStr.data();
63 
64     // Encode source to destination
65     size_t destLen;
66     to_crlf(srcBuf, srcLen, destBuf, destSize, &destLen);
67     aDestStr.assign(destStr, 0, destLen);
68     return 0;
69 }
70 
71 
DwToLfEol(const DwString & aSrcStr,DwString & aDestStr)72 int DwToLfEol(const DwString& aSrcStr, DwString& aDestStr)
73 {
74     size_t srcLen = aSrcStr.length();
75     const char* srcBuf = aSrcStr.data();
76     size_t destSize = srcLen;
77 
78     // Allocate destination buffer
79     DwString destStr(destSize, (char)0);
80     char* destBuf = (char*) destStr.data();
81 
82     // Encode source to destination
83     size_t destLen;
84     to_lf(srcBuf, srcLen, destBuf, destSize, &destLen);
85     aDestStr.assign(destStr, 0, destLen);
86     return 0;
87 }
88 
89 
DwToCrEol(const DwString & aSrcStr,DwString & aDestStr)90 int DwToCrEol(const DwString& aSrcStr, DwString& aDestStr)
91 {
92     size_t srcLen = aSrcStr.length();
93     const char* srcBuf = aSrcStr.data();
94     size_t destSize = srcLen;
95 
96     // Allocate destination buffer
97     DwString destStr(destSize, (char)0);
98     char* destBuf = (char*) destStr.data();
99 
100     // Encode source to destination
101     size_t destLen;
102     to_cr(srcBuf, srcLen, destBuf, destSize, &destLen);
103     aDestStr.assign(destStr, 0, destLen);
104     return 0;
105 }
106 
107 
DwToLocalEol(const DwString & aSrcStr,DwString & aDestStr)108 int DwToLocalEol(const DwString& aSrcStr, DwString& aDestStr)
109 {
110 #if defined(DW_EOL_CRLF)
111     return DwToCrLfEol(aSrcStr, aDestStr);
112 #elif defined(DW_EOL_LF)
113     return DwToLfEol(aSrcStr, aDestStr);
114 #else
115 #   error "Must define DW_EOL_CRLF, DW_EOL_LF"
116 #endif
117 }
118 
119 
DwEncodeBase64(const DwString & aSrcStr,DwString & aDestStr)120 int DwEncodeBase64(const DwString& aSrcStr, DwString& aDestStr)
121 {
122     // Estimate required destination buffer size
123     size_t srcLen = aSrcStr.length();
124     const char* srcBuf = aSrcStr.data();
125     size_t destSize = (srcLen+2)/3*4;
126     destSize += strlen(DW_EOL)*destSize/72 + 2;
127     destSize += 64;  // a little extra room
128 
129     // Allocate destination buffer
130     DwString destStr(destSize, (char)0);
131     char* destBuf = (char*) destStr.data();
132 
133     // Encode source to destination
134     size_t destLen;
135     int result =
136     encode_base64(srcBuf, srcLen, destBuf, destSize, &destLen);
137     aDestStr.assign(destStr, 0, destLen);
138     return result;
139 }
140 
141 
DwDecodeBase64(const DwString & aSrcStr,DwString & aDestStr)142 int DwDecodeBase64(const DwString& aSrcStr, DwString& aDestStr)
143 {
144     // Set destination buffer size same as source buffer size
145     size_t srcLen = aSrcStr.length();
146     const char* srcBuf = aSrcStr.data();
147     size_t destSize = srcLen;
148 
149     // Allocate destination buffer
150     DwString destStr(destSize, (char)0);
151     char* destBuf = (char*) destStr.data();
152 
153     // Encode source to destination
154     size_t destLen;
155     int result =
156     decode_base64(srcBuf, srcLen, destBuf, destSize, &destLen);
157     aDestStr.assign(destStr, 0, destLen);
158     return result;
159 }
160 
161 
DwEncodeQuotedPrintable(const DwString & aSrcStr,DwString & aDestStr)162 int DwEncodeQuotedPrintable(const DwString& aSrcStr, DwString& aDestStr)
163 {
164     // Estimate required destination buffer size
165     size_t srcLen = aSrcStr.length();
166     const char* srcBuf = aSrcStr.data();
167     size_t destSize = calc_qp_buff_size(srcBuf, srcLen);
168     destSize += 64;  // a little extra room
169 
170     // Allocate destination buffer
171     DwString destStr(destSize, (char)0);
172     char* destBuf = (char*) destStr.data();
173 
174     // Encode source to destination
175     size_t destLen;
176     int result =
177     encode_qp(srcBuf, srcLen, destBuf, destSize, &destLen);
178     aDestStr.assign(destStr, 0, destLen);
179     return result;
180 }
181 
182 
DwDecodeQuotedPrintable(const DwString & aSrcStr,DwString & aDestStr)183 int DwDecodeQuotedPrintable(const DwString& aSrcStr, DwString& aDestStr)
184 {
185     // Set destination buffer size same as source buffer size
186     size_t srcLen = aSrcStr.length();
187     const char* srcBuf = aSrcStr.data();
188     size_t destSize = srcLen;
189 
190     // Allocate destination buffer
191     DwString destStr(destSize, (char)0);
192     char* destBuf = (char*) destStr.data();
193 
194     // Encode source to destination
195     size_t destLen;
196     int result =
197     decode_qp(srcBuf, srcLen, destBuf, destSize, &destLen);
198     aDestStr.assign(destStr, 0, destLen);
199     return result;
200 }
201 
202 
203 //============================================================================
204 // Everything below this line is private to this file (static)
205 //============================================================================
206 
207 
calc_crlf_buff_size(const char * srcBuf,size_t srcLen)208 static size_t calc_crlf_buff_size(const char* srcBuf, size_t srcLen)
209 {
210     size_t i, extra;
211 
212     if (!srcBuf) return 0;
213     extra = 0;
214     for (i=0; i < srcLen; ) {
215         switch (srcBuf[i]) {
216         /* Bare LF (UNIX or C text) */
217         case '\n':
218             ++extra;
219             ++i;
220             break;
221         case '\r':
222             /* CR LF (DOS, Windows, or MIME text) */
223             if (i+1 < srcLen && srcBuf[i+1] == '\n') {
224                 i += 2;
225             }
226             /* Bare CR (Macintosh text) */
227             else {
228                 ++extra;
229                 ++i;
230             }
231             break;
232         default:
233             ++i;
234         }
235     }
236     return srcLen + extra;
237 }
238 
239 
to_crlf(const char * srcBuf,size_t srcLen,char * destBuf,size_t destSize,size_t * destLen)240 static int to_crlf(const char* srcBuf, size_t srcLen, char* destBuf,
241     size_t destSize, size_t* destLen)
242 {
243     size_t iSrc, iDest;
244 
245     if (!srcBuf || !destBuf || !destLen) return -1;
246     iSrc = iDest = 0;
247     while (iSrc < srcLen && iDest < destSize) {
248         switch (srcBuf[iSrc]) {
249         /* Bare LF (UNIX or C text) */
250         case '\n':
251             destBuf[iDest++] = '\r';
252             if (iDest < destSize) {
253                 destBuf[iDest++] = srcBuf[iSrc++];
254             }
255             break;
256         case '\r':
257             /* CR LF (DOS, Windows, or MIME text) */
258             if (iSrc+1 < srcLen && srcBuf[iSrc+1] == '\n') {
259                 destBuf[iDest++] = srcBuf[iSrc++];
260                 if (iDest < destSize) {
261                     destBuf[iDest++] = srcBuf[iSrc++];
262                 }
263             }
264             /* Bare CR (Macintosh text) */
265             else {
266                 destBuf[iDest++] = srcBuf[iSrc++];
267                 if (iDest < destSize) {
268                     destBuf[iDest++] = '\n';
269                 }
270             }
271             break;
272         default:
273             destBuf[iDest++] = srcBuf[iSrc++];
274         }
275     }
276     *destLen = iDest;
277     if (iDest < destSize) {
278         destBuf[iDest] = 0;
279     }
280     return 0;
281 }
282 
283 
to_lf(const char * srcBuf,size_t srcLen,char * destBuf,size_t destSize,size_t * destLen)284 static int to_lf(const char* srcBuf, size_t srcLen, char* destBuf,
285     size_t destSize, size_t* destLen)
286 {
287     size_t iSrc, iDest;
288 
289     if (!srcBuf || !destBuf || !destLen) return -1;
290     iSrc = iDest = 0;
291     while (iSrc < srcLen && iDest < destSize) {
292         switch (srcBuf[iSrc]) {
293         /* Bare LF (UNIX or C text) */
294         case '\n':
295             destBuf[iDest++] = srcBuf[iSrc++];
296             break;
297         case '\r':
298             /* CR LF (DOS, Windows, or MIME text) */
299             if (iSrc+1 < srcLen && srcBuf[iSrc+1] == '\n') {
300                 ++iSrc;
301                 destBuf[iDest++] = srcBuf[iSrc++];
302             }
303             /* Bare CR (Macintosh text) */
304             else {
305                 destBuf[iDest++] = '\n';
306                 ++iSrc;
307             }
308             break;
309         default:
310             destBuf[iDest++] = srcBuf[iSrc++];
311         }
312     }
313     *destLen = iDest;
314     if (iDest < destSize) {
315         destBuf[iDest] = 0;
316     }
317     return 0;
318 }
319 
320 
to_cr(const char * srcBuf,size_t srcLen,char * destBuf,size_t destSize,size_t * destLen)321 static int to_cr(const char* srcBuf, size_t srcLen, char* destBuf,
322     size_t destSize, size_t* destLen)
323 {
324     size_t iSrc, iDest;
325 
326     if (!srcBuf || !destBuf || !destLen) return -1;
327     iSrc = iDest = 0;
328     while (iSrc < srcLen && iDest < destSize) {
329         switch (srcBuf[iSrc]) {
330         /* Bare LF (UNIX or C text) */
331         case '\n':
332             destBuf[iDest++] = '\r';
333             ++iSrc;
334             break;
335         case '\r':
336             /* CR LF (DOS, Windows, or MIME text) */
337             if (iSrc+1 < srcLen && srcBuf[iSrc+1] == '\n') {
338                 destBuf[iDest++] = srcBuf[iSrc++];
339                 ++iSrc;
340             }
341             /* Bare CR (Macintosh text) */
342             else {
343                 destBuf[iDest++] = srcBuf[iSrc++];
344             }
345             break;
346         default:
347             destBuf[iDest++] = srcBuf[iSrc++];
348         }
349     }
350     *destLen = iDest;
351     if (iDest < destSize) {
352         destBuf[iDest] = 0;
353     }
354     return 0;
355 }
356 
357 
358 static char base64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
359     "abcdefghijklmnopqrstuvwxyz0123456789+/";
360 
361 static char base64idx[128] = {
362     '\377','\377','\377','\377','\377','\377','\377','\377',
363     '\377','\377','\377','\377','\377','\377','\377','\377',
364     '\377','\377','\377','\377','\377','\377','\377','\377',
365     '\377','\377','\377','\377','\377','\377','\377','\377',
366     '\377','\377','\377','\377','\377','\377','\377','\377',
367     '\377','\377','\377',    62,'\377','\377','\377',    63,
368         52,    53,    54,    55,    56,    57,    58,    59,
369         60,    61,'\377','\377','\377','\377','\377','\377',
370     '\377',     0,     1,     2,     3,     4,     5,     6,
371          7,     8,     9,    10,    11,    12,    13,    14,
372         15,    16,    17,    18,    19,    20,    21,    22,
373         23,    24,    25,'\377','\377','\377','\377','\377',
374     '\377',    26,    27,    28,    29,    30,    31,    32,
375         33,    34,    35,    36,    37,    38,    39,    40,
376         41,    42,    43,    44,    45,    46,    47,    48,
377         49,    50,    51,'\377','\377','\377','\377','\377'
378 };
379 
380 static char hextab[] = "0123456789ABCDEF";
381 
382 #ifdef __cplusplus
isbase64(int a)383 inline int isbase64(int a) {
384     return ('A' <= a && a <= 'Z')
385         || ('a' <= a && a <= 'z')
386         || ('0' <= a && a <= '9')
387         || a == '+' || a == '/';
388 }
389 #else
390 #define isbase64(a) (  ('A' <= (a) && (a) <= 'Z') \
391                     || ('a' <= (a) && (a) <= 'z') \
392                     || ('0' <= (a) && (a) <= '9') \
393                     ||  (a) == '+' || (a) == '/'  )
394 #endif
395 
396 
encode_base64(const char * aIn,size_t aInLen,char * aOut,size_t aOutSize,size_t * aOutLen)397 static int encode_base64(const char* aIn, size_t aInLen, char* aOut,
398     size_t aOutSize, size_t* aOutLen)
399 {
400     if (!aIn || !aOut || !aOutLen)
401         return -1;
402     size_t inLen = aInLen;
403     char* out = aOut;
404     size_t outSize = (inLen+2)/3*4;     /* 3:4 conversion ratio */
405     outSize += strlen(DW_EOL)*outSize/MAXLINE + 2;  /* Space for newlines and NUL */
406     if (aOutSize < outSize)
407         return -1;
408     size_t inPos  = 0;
409     size_t outPos = 0;
410     int c1, c2, c3;
411     int lineLen = 0;
412     /* Get three characters at a time and encode them. */
413     for (size_t i=0; i < inLen/3; ++i) {
414         c1 = aIn[inPos++] & 0xFF;
415         c2 = aIn[inPos++] & 0xFF;
416         c3 = aIn[inPos++] & 0xFF;
417         out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
418         out[outPos++] = base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
419         out[outPos++] = base64tab[((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6)];
420         out[outPos++] = base64tab[c3 & 0x3F];
421 		lineLen += 4;
422         if (lineLen >= MAXLINE-3) {
423 			char* cp = DW_EOL;
424             out[outPos++] = *cp++;
425 			if (*cp) {
426 				out[outPos++] = *cp;
427 			}
428 			lineLen = 0;
429         }
430     }
431     /* Encode the remaining one or two characters. */
432 	char* cp;
433     switch (inLen % 3) {
434     case 0:
435 		cp = DW_EOL;
436         out[outPos++] = *cp++;
437 		if (*cp) {
438 			out[outPos++] = *cp;
439 		}
440         break;
441     case 1:
442         c1 = aIn[inPos] & 0xFF;
443         out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
444         out[outPos++] = base64tab[((c1 & 0x03) << 4)];
445         out[outPos++] = '=';
446         out[outPos++] = '=';
447 		cp = DW_EOL;
448         out[outPos++] = *cp++;
449 		if (*cp) {
450 			out[outPos++] = *cp;
451 		}
452         break;
453     case 2:
454         c1 = aIn[inPos++] & 0xFF;
455         c2 = aIn[inPos] & 0xFF;
456         out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
457         out[outPos++] = base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
458         out[outPos++] = base64tab[((c2 & 0x0F) << 2)];
459         out[outPos++] = '=';
460 		cp = DW_EOL;
461         out[outPos++] = *cp++;
462 		if (*cp) {
463 			out[outPos++] = *cp;
464 		}
465         break;
466     }
467     out[outPos] = 0;
468     *aOutLen = outPos;
469     return 0;
470 }
471 
472 
decode_base64(const char * aIn,size_t aInLen,char * aOut,size_t aOutSize,size_t * aOutLen)473 static int decode_base64(const char* aIn, size_t aInLen, char* aOut,
474     size_t aOutSize, size_t* aOutLen)
475 {
476     if (!aIn || !aOut || !aOutLen)
477         return -1;
478     size_t inLen = aInLen;
479     char* out = aOut;
480     size_t outSize = (inLen/4+1)*3+1;
481     if (aOutSize < outSize)
482         return -1;
483     /* Get four input chars at a time and decode them. Ignore white space
484      * chars (CR, LF, SP, HT). If '=' is encountered, terminate input. If
485      * a char other than white space, base64 char, or '=' is encountered,
486      * flag an input error, but otherwise ignore the char.
487      */
488     int isErr = 0;
489     int isEndSeen = 0;
490     int b1, b2, b3;
491     int a1, a2, a3, a4;
492     size_t inPos = 0;
493     size_t outPos = 0;
494     while (inPos < inLen) {
495         a1 = a2 = a3 = a4 = 0;
496         while (inPos < inLen) {
497             a1 = aIn[inPos++] & 0xFF;
498             if (isbase64(a1)) {
499                 break;
500             }
501             else if (a1 == '=') {
502                 isEndSeen = 1;
503                 break;
504             }
505             else if (a1 != '\r' && a1 != '\n' && a1 != ' ' && a1 != '\t') {
506                 isErr = 1;
507             }
508         }
509         while (inPos < inLen) {
510             a2 = aIn[inPos++] & 0xFF;
511             if (isbase64(a2)) {
512                 break;
513             }
514             else if (a2 == '=') {
515                 isEndSeen = 1;
516                 break;
517             }
518             else if (a2 != '\r' && a2 != '\n' && a2 != ' ' && a2 != '\t') {
519                 isErr = 1;
520             }
521         }
522         while (inPos < inLen) {
523             a3 = aIn[inPos++] & 0xFF;
524             if (isbase64(a3)) {
525                 break;
526             }
527             else if (a3 == '=') {
528                 isEndSeen = 1;
529                 break;
530             }
531             else if (a3 != '\r' && a3 != '\n' && a3 != ' ' && a3 != '\t') {
532                 isErr = 1;
533             }
534         }
535         while (inPos < inLen) {
536             a4 = aIn[inPos++] & 0xFF;
537             if (isbase64(a4)) {
538                 break;
539             }
540             else if (a4 == '=') {
541                 isEndSeen = 1;
542                 break;
543             }
544             else if (a4 != '\r' && a4 != '\n' && a4 != ' ' && a4 != '\t') {
545                 isErr = 1;
546             }
547         }
548         if (isbase64(a1) && isbase64(a2) && isbase64(a3) && isbase64(a4)) {
549             a1 = base64idx[a1] & 0xFF;
550             a2 = base64idx[a2] & 0xFF;
551             a3 = base64idx[a3] & 0xFF;
552             a4 = base64idx[a4] & 0xFF;
553             b1 = ((a1 << 2) & 0xFC) | ((a2 >> 4) & 0x03);
554             b2 = ((a2 << 4) & 0xF0) | ((a3 >> 2) & 0x0F);
555             b3 = ((a3 << 6) & 0xC0) | ( a4       & 0x3F);
556             out[outPos++] = char(b1);
557             out[outPos++] = char(b2);
558             out[outPos++] = char(b3);
559         }
560         else if (isbase64(a1) && isbase64(a2) && isbase64(a3) && a4 == '=') {
561             a1 = base64idx[a1] & 0xFF;
562             a2 = base64idx[a2] & 0xFF;
563             a3 = base64idx[a3] & 0xFF;
564             b1 = ((a1 << 2) & 0xFC) | ((a2 >> 4) & 0x03);
565             b2 = ((a2 << 4) & 0xF0) | ((a3 >> 2) & 0x0F);
566             out[outPos++] = char(b1);
567             out[outPos++] = char(b2);
568             break;
569         }
570         else if (isbase64(a1) && isbase64(a2) && a3 == '=' && a4 == '=') {
571             a1 = base64idx[a1] & 0xFF;
572             a2 = base64idx[a2] & 0xFF;
573             b1 = ((a1 << 2) & 0xFC) | ((a2 >> 4) & 0x03);
574             out[outPos++] = char(b1);
575             break;
576         }
577         else {
578             break;
579         }
580         if (isEndSeen) {
581             break;
582         }
583     } /* end while loop */
584     *aOutLen = outPos;
585     return (isErr) ? -1 : 0;
586 }
587 
588 
589 /***** Warning: calc_qp_buff_size must stay in sync with encode_qp ******/
590 
encode_qp(const char * aIn,size_t aInLen,char * aOut,size_t,size_t * aOutLen)591 static int encode_qp(const char* aIn, size_t aInLen, char* aOut,
592     size_t /*aOutSize */, size_t* aOutLen)
593 {
594     size_t inPos, outPos, lineLen;
595     int ch;
596 
597     if (!aIn || !aOut || !aOutLen) {
598         return -1;
599     }
600     inPos  = 0;
601     outPos = 0;
602     lineLen = 0;
603     while (inPos < aInLen) {
604         ch = aIn[inPos++] & 0xFF;
605         /* '.' at beginning of line (confuses some SMTPs) */
606         if (lineLen == 0 && ch == '.') {
607             aOut[outPos++] = '=';
608             aOut[outPos++] = hextab[(ch >> 4) & 0x0F];
609             aOut[outPos++] = hextab[ch & 0x0F];
610             lineLen += 3;
611         }
612         /* "From " at beginning of line (gets mangled in mbox folders) */
613         else if (lineLen == 0 && inPos+3 < aInLen && ch == 'F'
614                  && aIn[inPos  ] == 'r' && aIn[inPos+1] == 'o'
615                  && aIn[inPos+2] == 'm' && aIn[inPos+3] == ' ') {
616             aOut[outPos++] = '=';
617             aOut[outPos++] = hextab[(ch >> 4) & 0x0F];
618             aOut[outPos++] = hextab[ch & 0x0F];
619             lineLen += 3;
620         }
621         /* Normal printable char */
622         else if ((62 <= ch && ch <= 126) || (33 <= ch && ch <= 60)) {
623             aOut[outPos++] = (char) ch;
624             ++lineLen;
625         }
626         /* Space */
627         else if (ch == ' ') {
628             /* Space at end of line or end of input must be encoded */
629 #if defined(DW_EOL_LF)
630             if (inPos >= aInLen           /* End of input? */
631                 || aIn[inPos] == '\n') {  /* End of line? */
632 
633                 aOut[outPos++] = '=';
634                 aOut[outPos++] = '2';
635                 aOut[outPos++] = '0';
636                 lineLen += 3;
637             }
638 #elif defined(DW_EOL_CRLF)
639 			if (inPos >= aInLen           /* End of input? */
640 				|| (inPos < aInLen-1      /* End of line? */
641 				    && aIn[inPos  ] == '\r'
642 					&& aIn[inPos+1] == '\n') ) {
643 
644                 aOut[outPos++] = '=';
645                 aOut[outPos++] = '2';
646                 aOut[outPos++] = '0';
647                 lineLen += 3;
648 			}
649 #else
650 # error Must define DW_EOL_LF or DW_EOL_CRLF
651 #endif
652             else {
653                 aOut[outPos++] = ' ';
654                 ++lineLen;
655             }
656         }
657         /* Hard line break */
658 #if defined(DW_EOL_LF)
659         else if (ch == '\n') {
660             aOut[outPos++] = '\n';
661             lineLen = 0;
662         }
663 #elif defined(DW_EOL_CRLF)
664         else if (inPos < aInLen && ch == '\r' && aIn[inPos] == '\n') {
665             ++inPos;
666             aOut[outPos++] = '\r';
667             aOut[outPos++] = '\n';
668             lineLen = 0;
669         }
670 #endif
671         /* Non-printable char */
672         else if (ch & 0x80        /* 8-bit char */
673                  || !(ch & 0xE0)  /* control char */
674                  || ch == 0x7F    /* DEL */
675                  || ch == '=') {  /* special case */
676             aOut[outPos++] = '=';
677             aOut[outPos++] = hextab[(ch >> 4) & 0x0F];
678             aOut[outPos++] = hextab[ch & 0x0F];
679             lineLen += 3;
680         }
681         /* Soft line break */
682 #if defined(DW_EOL_LF)
683         if (lineLen >= MAXLINE-3 && inPos < aInLen && aIn[inPos] != '\n') {
684             aOut[outPos++] = '=';
685             aOut[outPos++] = '\n';
686             lineLen = 0;
687         }
688 #elif defined(DW_EOL_CRLF)
689         if (lineLen >= MAXLINE-3 && !(inPos < aInLen-1 &&
690 			aIn[inPos] == '\r' && aIn[inPos+1] == '\n')) {
691 
692             aOut[outPos++] = '=';
693             aOut[outPos++] = '\r';
694             aOut[outPos++] = '\n';
695             lineLen = 0;
696         }
697 #endif
698     }
699     aOut[outPos] = 0;
700     *aOutLen = outPos;
701     return 0;
702 }
703 
704 
decode_qp(const char * aIn,size_t aInLen,char * aOut,size_t,size_t * aOutLen)705 static int decode_qp(const char* aIn, size_t aInLen, char* aOut,
706     size_t /* aOutSize */, size_t* aOutLen)
707 {
708     size_t i, inPos, outPos, lineLen, nextLineStart, numChars, charsEnd;
709     int isEolFound, softLineBrk, isError;
710     int ch, c1, c2;
711 
712     if (!aIn || !aOut || !aOutLen)
713         return -1;
714     isError = 0;
715     inPos = 0;
716     outPos = 0;
717     for (i=0; i < aInLen; ++i) {
718         if (aIn[i] == 0) {
719             aInLen = i;
720             break;
721         }
722     }
723     if (aInLen == 0) {
724         aOut[0] = 0;
725         *aOutLen = 0;
726         return 0;
727     }
728     while (inPos < aInLen) {
729         /* Get line */
730         lineLen = 0;
731         isEolFound = 0;
732         while (!isEolFound && lineLen < aInLen - inPos) {
733             ch = aIn[inPos+lineLen];
734             ++lineLen;
735             if (ch == '\n') {
736                 isEolFound = 1;
737             }
738         }
739         nextLineStart = inPos + lineLen;
740         numChars = lineLen;
741         /* Remove white space from end of line */
742         while (numChars > 0) {
743             ch = aIn[inPos+numChars-1] & 0x7F;
744             if (ch != '\n' && ch != '\r' && ch != ' ' && ch != '\t') {
745                 break;
746             }
747             --numChars;
748         }
749         charsEnd = inPos + numChars;
750         /* Decode line */
751         softLineBrk = 0;
752         while (inPos < charsEnd) {
753             ch = aIn[inPos++] & 0x7F;
754             if (ch != '=') {
755                 /* Normal printable char */
756                 aOut[outPos++] = (char) ch;
757             }
758             else /* if (ch == '=') */ {
759                 /* Soft line break */
760                 if (inPos >= charsEnd) {
761                     softLineBrk = 1;
762                     break;
763                 }
764                 /* Non-printable char */
765                 else if (inPos < charsEnd-1) {
766                     c1 = aIn[inPos++] & 0x7F;
767                     if ('0' <= c1 && c1 <= '9')
768                         c1 -= '0';
769                     else if ('A' <= c1 && c1 <= 'F')
770                         c1 = c1 - 'A' + 10;
771                     else if ('a' <= c1 && c1 <= 'f')
772                         c1 = c1 - 'a' + 10;
773                     else
774                         isError = 1;
775                     c2 = aIn[inPos++] & 0x7F;
776                     if ('0' <= c2 && c2 <= '9')
777                         c2 -= '0';
778                     else if ('A' <= c2 && c2 <= 'F')
779                         c2 = c2 - 'A' + 10;
780                     else if ('a' <= c2 && c2 <= 'f')
781                         c2 = c2 - 'a' + 10;
782                     else
783                         isError = 1;
784                     aOut[outPos++] = (char) ((c1 << 4) + c2);
785                 }
786                 else /* if (inPos == charsEnd-1) */ {
787                     isError = 1;
788                 }
789             }
790         }
791         if (isEolFound && !softLineBrk) {
792             const char* cp = DW_EOL;
793             aOut[outPos++] = *cp++;
794             if (*cp) {
795                 aOut[outPos++] = *cp;
796             }
797         }
798         inPos = nextLineStart;
799     }
800     aOut[outPos] = 0;
801     *aOutLen = outPos;
802     return (isError) ? -1 : 0;
803 }
804 
805 
806 /***** Warning: calc_qp_buff_size must stay in sync with encode_qp ******/
807 
calc_qp_buff_size(const char * aIn,size_t aInLen)808 static size_t calc_qp_buff_size(const char* aIn, size_t aInLen)
809 {
810     size_t inPos, outLen, lineLen;
811     int ch;
812 
813     if (!aIn || aInLen == 0) {
814         return 0;
815     }
816     inPos  = 0;
817     outLen = 0;
818     lineLen = 0;
819     while (inPos < aInLen) {
820         ch = aIn[inPos++] & 0xFF;
821         /* '.' at beginning of line (confuses some SMTPs) */
822         if (lineLen == 0 && ch == '.') {
823             outLen += 3;
824             lineLen += 3;
825         }
826         /* "From " at beginning of line (gets mangled in mbox folders) */
827         else if (lineLen == 0 && inPos+3 < aInLen && ch == 'F'
828                  && aIn[inPos  ] == 'r' && aIn[inPos+1] == 'o'
829                  && aIn[inPos+2] == 'm' && aIn[inPos+3] == ' ') {
830             outLen += 3;
831             lineLen += 3;
832         }
833         /* Normal printable char */
834         else if ((62 <= ch && ch <= 126) || (33 <= ch && ch <= 60)) {
835             ++outLen;
836             ++lineLen;
837         }
838         /* Space */
839         else if (ch == ' ') {
840             /* Space at end of line or end of input must be encoded */
841 #if defined(DW_EOL_LF)
842             if (inPos >= aInLen           /* End of input? */
843                 || aIn[inPos] == '\n') {  /* End of line? */
844 
845                 outLen += 3;
846                 lineLen += 3;
847             }
848 #elif defined(DW_EOL_CRLF)
849 			if (inPos >= aInLen           /* End of input? */
850 				|| (inPos < aInLen-1      /* End of line? */
851 				    && aIn[inPos  ] == '\r'
852 					&& aIn[inPos+1] == '\n') ) {
853 
854                 outLen += 3;
855                 lineLen += 3;
856 			}
857 #else
858 # error Must define DW_EOL_LF or DW_EOL_CRLF
859 #endif
860             else {
861                 ++outLen;
862                 ++lineLen;
863             }
864         }
865         /* Hard line break */
866 #if defined(DW_EOL_LF)
867         else if (ch == '\n') {
868             ++outLen;
869             lineLen = 0;
870         }
871 #elif defined(DW_EOL_CRLF)
872         else if (inPos < aInLen && ch == '\r' && aIn[inPos] == '\n') {
873             ++inPos;
874             outLen += 2;
875             lineLen = 0;
876         }
877 #endif
878         /* Non-printable char */
879         else if (ch & 0x80        /* 8-bit char */
880                  || !(ch & 0xE0)  /* control char */
881                  || ch == 0x7F    /* DEL */
882                  || ch == '=') {  /* special case */
883             outLen += 3;
884             lineLen += 3;
885         }
886         /* Soft line break */
887 #if defined(DW_EOL_LF)
888         if (lineLen >= MAXLINE-3 && inPos < aInLen && aIn[inPos] != '\n') {
889             outLen += 2;
890             lineLen = 0;
891         }
892 #elif defined(DW_EOL_CRLF)
893         if (lineLen >= MAXLINE-3 && !(inPos < aInLen-1 &&
894 			aIn[inPos] == '\r' && aIn[inPos+1] == '\n')) {
895 
896             outLen += 3;
897             lineLen = 0;
898         }
899 #endif
900     }
901     return outLen;
902 }
903 
904