Commit caeb09a2 caeb09a20e112766b486af17407861bc6526254c by Sergey Poznyakoff

(_hexdigits): Must be zero-terminated,

otherwise the native Darwin linker refuses to load it.
(qp_decode): Bugfix: in accordance with rfc1892 a sequence
of whitespace is allowed in an encoded string provided that
it is followed by a printable character.
(qp_encode): Preserve trailing whitespace.
1 parent 6039d200
......@@ -251,7 +251,7 @@ base64_init (filter_t filter)
return ENOMEM;
ts->min_size = 4;
ts->s_buf = calloc (4, 1);
ts->s_buf = calloc (ts->min_size, 1);
if (ts->s_buf == NULL)
{
free (ts);
......@@ -348,7 +348,10 @@ base64_encode (const char *iptr, size_t isize, char *optr, size_t osize,
/*------------------------------------------------------
* quoted-printable decoder/encoder
*------------------------------------------------------*/
static const char _hexdigits[16] = "0123456789ABCDEF";
static const char _hexdigits[] = "0123456789ABCDEF";
#define QP_LINE_MAX 76
#define ISWS(c) ((c)==' ' || (c)=='\t')
static int
qp_init (filter_t filter)
......@@ -358,8 +361,8 @@ qp_init (filter_t filter)
if (ts == NULL)
return ENOMEM;
ts->min_size = 4;
ts->s_buf = calloc (4, 1);
ts->min_size = QP_LINE_MAX;
ts->s_buf = calloc (ts->min_size, 1);
if (ts->s_buf == NULL)
{
free (ts);
......@@ -380,80 +383,121 @@ qp_decode (const char *iptr, size_t isize, char *optr, size_t osize,
char c;
int last_char = 0;
size_t consumed = 0;
size_t wscount = 0;
*nbytes = 0;
while (consumed < isize && *nbytes < osize)
{
c = *iptr++;
if (c == '=')
if (ISWS (c))
{
/* There must be 2 more characters before I consume this. */
if ((consumed + 2) >= isize)
return consumed;
else
wscount++;
consumed++;
}
else
{
/* Octets with values of 9 and 32 MAY be
represented as US-ASCII TAB (HT) and SPACE characters,
respectively, but MUST NOT be so represented at the end
of an encoded line. Any TAB (HT) or SPACE characters
on an encoded line MUST thus be followed on that line
by a printable character. */
if (wscount)
{
/* you get =XX where XX are hex characters. */
char chr[3];
int new_c;
chr[3] = 0;
chr[0] = *iptr++;
/* Ignore LF. */
if (chr[0] != '\n')
if (c != '\r' && c != '\n')
{
chr[1] = *iptr++;
new_c = strtoul (chr, NULL, 16);
*optr++ = new_c;
(*nbytes)++;
consumed += 3;
size_t sz;
if (consumed >= isize)
break;
if (*nbytes + wscount > osize)
sz = osize - *nbytes;
else
sz = wscount;
memcpy (optr, iptr - wscount - 1, sz);
optr += sz;
(*nbytes) += sz;
if (wscount > sz)
{
wscount -= sz;
break;
}
}
wscount = 0;
}
if (c == '=')
{
/* There must be 2 more characters before I consume this. */
if (consumed + 2 >= isize)
break;
else
consumed += 2;
{
/* you get =XX where XX are hex characters. */
char chr[3];
int new_c;
chr[3] = 0;
chr[0] = *iptr++;
/* Ignore LF. */
if (chr[0] != '\n')
{
chr[1] = *iptr++;
new_c = strtoul (chr, NULL, 16);
*optr++ = new_c;
(*nbytes)++;
consumed += 3;
}
else
consumed += 2;
}
}
}
/* CR character. */
else if (c == '\r')
{
/* There must be at least 1 more character before I consume this. */
if ((consumed + 1) >= isize )
return (consumed);
else
/* CR character. */
else if (c == '\r')
{
iptr++; /* Skip the CR character. */
*optr++ = '\n';
(*nbytes)++;
consumed += 2;
/* There must be at least 1 more character before
I consume this. */
if (consumed + 1 >= isize)
break;
else
{
iptr++; /* Skip the CR character. */
*optr++ = '\n';
(*nbytes)++;
consumed += 2;
}
}
}
else if ((c == 9) || (c == 32))
{
if ((last_char == 9) || (last_char == 32))
consumed++;
else
{
*optr++ = c;
(*nbytes)++;
consumed++;
}
}
else
{
*optr++ = c;
(*nbytes)++;
consumed++;
}
}
last_char = c;
}
return consumed;
return consumed - wscount;
}
#define SOFTBRK() \
/* check if we have enough room for the soft linebreak */\
if (*nbytes + 2 > osize) \
break;\
*optr++ = '=';\
*optr++ = '\n';\
(*nbytes) += 2;\
*line_len = 0;
static int
qp_encode (const char *iptr, size_t isize, char *optr, size_t osize,
size_t *nbytes, int *line_len)
{
#define QP_LINE_MAX 76
int c;
unsigned int c;
size_t consumed = 0;
size_t wscount = 0;
*nbytes = 0;
......@@ -464,28 +508,37 @@ qp_encode (const char *iptr, size_t isize, char *optr, size_t osize,
while (consumed < isize)
{
if (*line_len == QP_LINE_MAX)
int simple_char;
/* candidate byte to convert */
c = *(unsigned char*) iptr;
simple_char = (c >= 32 && c <= 60)
|| (c >= 62 && c <= 126)
|| c == '\t'
|| c == '\n';
if (*line_len == QP_LINE_MAX
|| (c == '\n' && consumed && ISWS (optr[-1]))
|| (!simple_char && *line_len >= (QP_LINE_MAX - 3)))
{
/* to cut a qp line requires two bytes */
if ((*nbytes) + 2 > osize)
return consumed;
if (*nbytes + 2 > osize)
break;
*optr++ = '=';
*optr++ = '\n';
(*nbytes) += 2;
*line_len = 0;
}
/* candidate byte to convert */
c = *iptr;
if (((c >= 32) && (c <= 60)) ||
((c >= 62) && (c <= 126)) ||
(c == 9))
if (ISWS (c))
wscount++;
if (simple_char)
{
/* a non-quoted character uses up one byte */
if (*nbytes + 1 > osize)
return consumed;
break;
*optr++ = c;
(*nbytes)++;
......@@ -493,43 +546,34 @@ qp_encode (const char *iptr, size_t isize, char *optr, size_t osize,
iptr++;
consumed++;
if (!ISWS (c))
wscount = 0;
if (c == '\n')
*line_len = 0;
}
else
{
/* can we store the quoted character in the remaining of the
line ? */
if (*line_len >= (QP_LINE_MAX - 3))
{
/* check if we have enough room to store the padding */
if (*nbytes + *line_len - QP_LINE_MAX + 3 > osize)
return consumed;
/* add spaces. */
while (*line_len < QP_LINE_MAX)
{
*optr++ = ' ';
(*nbytes)++;
(*line_len)++;
}
}
else
{
/* a quoted character uses up three bytes */
if ((*nbytes) + 3 > osize)
return consumed;
/* a quoted character uses up three bytes */
if ((*nbytes) + 3 > osize)
break;
*optr++ = '=';
*optr++ = _hexdigits[(c >> 4) & 0xf];
*optr++ = _hexdigits[c & 0xf];
*optr++ = '=';
*optr++ = _hexdigits[(c >> 4) & 0xf];
*optr++ = _hexdigits[c & 0xf];
(*nbytes) += 3;
(*line_len) += 3;
(*nbytes) += 3;
(*line_len) += 3;
/* we've actuall used up one byte of input */
iptr ++;
consumed ++;
}
/* we've actuall used up one byte of input */
iptr++;
consumed++;
wscount = 0;
}
}
return consumed;
if (wscount)
wscount--;
(*nbytes) -= wscount;
return consumed - wscount;
}
......