diff --git a/ChangeLog b/ChangeLog
index 9290d8568..674c5c969 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2011-05-22 Richard Frith-Macdonald
+
+ * Source/Additions/GSMime.m:
+ Fixes for encoding quoted-printable document body content.
+
2011-05-19 Richard Frith-Macdonald
* Source/NSPortCoder.m:
diff --git a/Source/Additions/GSMime.m b/Source/Additions/GSMime.m
index 0c226d39d..aa04c76ea 100644
--- a/Source/Additions/GSMime.m
+++ b/Source/Additions/GSMime.m
@@ -175,6 +175,127 @@ encodebase64(unsigned char *dst, const unsigned char *src, int length)
return dIndex;
}
+
+static void
+encodeQuotedPrintable(NSMutableData *result,
+ const unsigned char *src, unsigned length)
+{
+ static char *hex = "0123456789ABCDEF";
+ unsigned offset;
+ unsigned column = 0;
+ unsigned size = 0;
+ unsigned i;
+ unsigned char *dst;
+
+ for (i = 0; i < length; i++)
+ {
+ unsigned char c = src[i];
+ int add;
+
+ if ('\r' == c && i < length && '\n' == src[i + 1])
+ {
+ /* A cr-lf sequence is an end of line, we send that literally
+ * as a hard line break.
+ */
+ i++;
+ size += 2;
+ column = 0;
+ continue;
+ }
+
+ if ((' ' == c || '\t' == c) && i < length
+ && ('\r' == src[i + 1] || '\n' == src[i + 1]))
+ {
+ /* RFC 2045 says we have to encode space and tab characters when
+ * they occur just before end of line.
+ */
+ add = 3;
+ }
+ else if ('\t' == c || (c >= 32 && c <= 60) || (c >= 62 && c <= 126))
+ {
+ /* Most characters can be sent literally.
+ */
+ add = 1;
+ }
+ else
+ {
+ /* Everything else must be escaped.
+ */
+ add = 3;
+ }
+ if (column + add > 75)
+ {
+ size += 3; // '=\r\n'
+ column = 0;
+ }
+ size += add;
+ column += add;
+ }
+
+ offset = [result length];
+ [result setLength: offset + size];
+ dst = (unsigned char*)[result mutableBytes];
+ column = 0;
+
+ for (i = 0; i < length; i++)
+ {
+ unsigned char c = src[i];
+ int add;
+
+ if ('\r' == c && i < length && '\n' == src[i + 1])
+ {
+ /* A cr-lf sequence is an end of line, we send that literally
+ * as a hard line break.
+ */
+ i++;
+ dst[offset++] = '\r';
+ dst[offset++] = '\n';
+ column = 0;
+ continue;
+ }
+
+ if ((' ' == c || '\t' == c) && i < length
+ && ('\r' == src[i + 1] || '\n' == src[i + 1]))
+ {
+ /* RFC 2045 says we have to encode space and tab characters when
+ * they occur just before end of line.
+ */
+ add = 3;
+ }
+ else if ('\t' == c || (c >= 32 && c <= 60) || (c >= 62 && c <= 126))
+ {
+ /* Most characters can be sent literally.
+ */
+ add = 1;
+ }
+ else
+ {
+ /* Everything else must be escaped.
+ */
+ add = 3;
+ }
+ if (column + add > 75)
+ {
+ dst[offset++] = '=';
+ dst[offset++] = '\r';
+ dst[offset++] = '\n';
+ column = 0;
+ }
+ if (3 == add)
+ {
+ dst[offset++] = '=';
+ dst[offset++] = hex[c >> 4];
+ dst[offset++] = hex[c & 15];
+ }
+ else
+ {
+ dst[offset++] = c;
+ }
+ column += add;
+ }
+}
+
+
typedef enum {
WE_QUOTED,
WE_BASE64
@@ -592,7 +713,11 @@ wordData(NSString *word)
{
if (pos > 0)
{
- if ((*src == '\n') || (*src == '\r'))
+ if (1 == pos && '\r' == *src)
+ {
+ pos++;
+ }
+ else if (*src == '\n')
{
pos = 0;
}
@@ -601,16 +726,44 @@ wordData(NSString *word)
buf[pos++] = *src;
if (pos == 3)
{
+ BOOL ok = YES;
int c;
int val;
pos = 0;
c = buf[1];
- val = isdigit(c) ? (c - '0') : (c - 55);
- val *= 0x10;
- c = buf[2];
- val += isdigit(c) ? (c - '0') : (c - 55);
- *dst++ = val;
+ if (isxdigit(c))
+ {
+ if (islower(c)) c = toupper(c);
+ val = isdigit(c) ? (c - '0') : (c - 55);
+ val *= 0x10;
+ c = buf[2];
+ if (isxdigit(c))
+ {
+ if (islower(c)) c = toupper(c);
+ val += isdigit(c) ? (c - '0') : (c - 55);
+ }
+ else
+ {
+ ok = NO;
+ }
+ }
+ else
+ {
+ ok = NO;
+ }
+ if (YES == ok)
+ {
+ *dst++ = val;
+ }
+ else
+ {
+ /* A bad escape sequence is copied literally.
+ */
+ *dst++ = '=';
+ *dst++ = buf[0];
+ *dst++ = buf[1];
+ }
}
}
}
@@ -5304,16 +5457,21 @@ appendString(NSMutableData *m, NSUInteger offset, NSUInteger fold,
* guarantees that we won't produce two identical strings in
* the same run of the program.
*
+ * The boundary has a suffix of '=_' to ensure it's not mistaken
+ * for quoted-printable data.
+ *
*/
- (NSString*) makeBoundary
{
static int count = 0;
unsigned char output[20];
+ unsigned char *ptr;
NSMutableData *md;
NSString *result;
NSData *source;
NSData *digest;
int sequence = ++count;
+ int length;
source = [[[NSProcessInfo processInfo] globallyUniqueString]
dataUsingEncoding: NSUTF8StringEncoding];
@@ -5327,7 +5485,11 @@ appendString(NSMutableData *m, NSUInteger offset, NSUInteger fold,
md = [NSMutableData allocWithZone: NSDefaultMallocZone()];
md = [md initWithLength: 40];
- [md setLength: encodebase64([md mutableBytes], output, 20)];
+ length = encodebase64([md mutableBytes], output, 20);
+ [md setLength: length + 2];
+ ptr = (unsigned char*)[md mutableBytes];
+ ptr[length-2] = '=';
+ ptr[length-1] = '_';
result = [NSStringClass allocWithZone: NSDefaultMallocZone()];
result = [result initWithData: md encoding: NSASCIIStringEncoding];
RELEASE(md);
@@ -5851,6 +6013,10 @@ appendString(NSMutableData *m, NSUInteger offset, NSUInteger fold,
[md appendBytes: "\r\n" length: 2];
}
}
+ else if ([[enc value] isEqualToString: @"quoted-printable"] == YES)
+ {
+ encodeQuotedPrintable(md, [d bytes], [d length]);
+ }
else if ([[enc value] isEqualToString: @"x-uuencode"] == YES)
{
NSString *name;