Commit 480f7d0b 480f7d0b369cc32ca30df6f10ed16c7308beab63 by Sergey Poznyakoff

'Q' encoding: encode question mark properly; limit length of encoded words

* libmailutils/base/rfc2047.c (mu_rfc2047_encode): Limit length
of encoded word to 75 bytes.
* libmailutils/filter/qpflt.c: Treat '?' as special character in
Q encoder.
* libmailutils/tests/encode2047.at: Add more tests.
* libmailutils/tests/encode2047.c: Use mailutils string I/O
1 parent afaecb9b
......@@ -286,17 +286,23 @@ mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr)
@return 0 on success
*/
#define MAX_ENCODED_WORD 75
int
mu_rfc2047_encode (const char *charset, const char *encoding,
const char *text, char **result)
{
mu_stream_t input_stream;
mu_stream_t output_stream;
mu_stream_t inter_stream;
int rc;
if (charset == NULL || encoding == NULL || text == NULL)
return EINVAL;
if (strlen (charset) > MAX_ENCODED_WORD - 8)
return EINVAL;
if (strcmp (encoding, "base64") == 0)
encoding = "B";
else if (strcmp (encoding, "quoted-printable") == 0)
......@@ -304,43 +310,68 @@ mu_rfc2047_encode (const char *charset, const char *encoding,
else if (encoding[1] || !strchr ("BQ", encoding[0]))
return MU_ERR_BAD_2047_ENCODING;
rc = mu_static_memory_stream_create (&input_stream, text, strlen (text));
if (rc)
return rc;
rc = mu_filter_create (&output_stream, input_stream,
rc = mu_filter_create (&inter_stream, input_stream,
encoding, MU_FILTER_ENCODE, MU_STREAM_READ);
mu_stream_unref (input_stream);
if (rc == 0)
{
/* Assume strlen(qp_encoded_text) <= strlen(text) * 3 */
/* malloced length is composed of:
"=?"
charset
"?"
B or Q
"?"
encoded_text
"?="
zero terminator */
*result = malloc (2 + strlen (charset) + 3 + strlen (text) * 3 + 3);
if (*result)
mu_stream_t output_stream;
rc = mu_memory_stream_create (&output_stream, MU_STREAM_RDWR);
if (rc == 0)
{
char *p = *result;
size_t s;
char buf[MAX_ENCODED_WORD];
size_t start, bs, n;
p += sprintf (p, "=?%s?%s?", charset, encoding);
start = snprintf (buf, sizeof buf, "=?%s?%s?", charset, encoding);
bs = sizeof buf - start - 2;
rc = mu_stream_read (output_stream,
p,
strlen (text) * 3, &s);
strcpy (p + s, "?=");
}
while (1)
{
rc = mu_stream_read (inter_stream, buf + start, bs, &n);
if (rc || n == 0)
break;
rc = mu_stream_write (output_stream, buf, n + start, NULL);
if (rc)
break;
rc = mu_stream_write (output_stream, "?=", 2, NULL);
if (rc)
break;
if (n == bs)
rc = mu_stream_write (output_stream, "\n ", 2, NULL);
else
break;
}
if (rc == 0)
{
mu_off_t sz;
char *ptr;
mu_stream_size (output_stream, &sz);
ptr = malloc (sz + 1);
if (!ptr)
rc = ENOMEM;
else
{
if ((rc = mu_stream_seek (output_stream, 0, MU_SEEK_SET,
NULL)) == 0
&& (rc = mu_stream_read (output_stream, ptr, sz, NULL))
== 0)
{
ptr[sz] = 0;
*result = ptr;
}
}
}
mu_stream_destroy (&output_stream);
}
mu_stream_destroy (&inter_stream);
}
else
mu_stream_destroy (&input_stream);
......
......@@ -37,7 +37,7 @@ _qp_decoder (void *xd,
size_t isize;
char *optr;
size_t osize;
int underscore_special = *(int*)xd;
char *specials = xd;
switch (cmd)
{
......@@ -143,7 +143,7 @@ _qp_decoder (void *xd,
consumed += 2;
}
}
else if (underscore_special && c == '_')
else if (c == '_' && specials && strchr (specials, c))
{
*optr++ = ' ';
nbytes++;
......@@ -175,7 +175,7 @@ _qp_encoder (void *xd,
size_t isize;
char *optr;
size_t osize;
int underscore_special = *(int*)xd;
char *specials = xd;
switch (cmd)
{
......@@ -203,7 +203,7 @@ _qp_encoder (void *xd,
/* candidate byte to convert */
c = *(unsigned char*) iptr;
if (underscore_special && c == '_')
if (specials && strchr (specials, c))
simple_char = 0;
else
simple_char = (c >= 32 && c <= 60)
......@@ -216,7 +216,7 @@ _qp_encoder (void *xd,
/* a non-quoted character uses up one byte */
if (nbytes + 1 > osize)
break;
if (underscore_special && c == ' ')
if (c == ' ' && specials && strchr (specials, '_'))
*optr++ = '_';
else
*optr++ = c;
......@@ -248,20 +248,9 @@ _qp_encoder (void *xd,
return mu_filter_ok;
}
static int
alloc_qp (void **pret, int mode MU_ARG_UNUSED, int argc, const char **argv)
{
int *x = malloc (sizeof (*x));
if (!x)
return ENOMEM;
*x = 0;
*pret = x;
return 0;
}
static struct _mu_filter_record _qp_filter = {
"quoted-printable",
alloc_qp,
NULL,
_qp_encoder,
_qp_decoder
};
......@@ -271,10 +260,7 @@ mu_filter_record_t mu_qp_filter = &_qp_filter;
static int
alloc_Q (void **pret, int mode MU_ARG_UNUSED, int argc, const char **argv)
{
int *x = malloc (sizeof (*x));
if (!x)
return ENOMEM;
*x = 1;
char *x = strdup ("_?");
*pret = x;
return 0;
}
......
......@@ -20,7 +20,7 @@ dnl
m4_pushdef([TESTENC2047],[
m4_pushdef([MU_TEST_GROUP],[Encode 2047])
m4_pushdef([MU_TEST_KEYWORDS],[encode2047 encode])
m4_pushdef([MU_TEST_COMMAND],[encode2047 -eB $3])
m4_pushdef([MU_TEST_COMMAND],[encode2047 $3])
MU_GENERIC_TEST([$1],[$2],[$4],[],[$5
])
m4_popdef([MU_TEST_COMMAND])
......@@ -29,25 +29,40 @@ m4_popdef([MU_TEST_GROUP])
])
TESTENC2047([8-bit input],[enc01],
[-c koi8-r -o],
[\\345\326\305\304\316\305\327\316\331\312\040\317\324\336\305\324],
[-eB -c koi8-r -o],
[\345\326\305\304\316\305\327\316\331\312\040\317\324\336\305\324],
[=?koi8-r?B?5dbFxM7F187ZyiDP1N7F1A==?=])
TESTENC2047([padding 1],[enc02],
[],
[-eB],
[abcd],
[=?iso-8859-1?B?YWJjZA==?=])
TESTENC2047([padding 2],[enc03],
[],
[-eB],
[abcdef],
[=?iso-8859-1?B?YWJjZGVm?=])
TESTENC2047([padding 3],[enc04],
[-cUTF-8],
[-eB -cUTF-8],
[Wichtige Mitteilung zur Schaltung Ihres Anschlusses],
[=?UTF-8?B?V2ljaHRpZ2UgTWl0dGVpbHVuZyB6dXIgU2NoYWx0dW5nIElocmVzIEFuc2NobHVzc2Vz?=])
[=?UTF-8?B?V2ljaHRpZ2UgTWl0dGVpbHVuZyB6dXIgU2NoYWx0dW5nIElocmVzIEFuc2NobHV?=
=?UTF-8?B?zc2Vz?=])
TESTENC2047([specials],[enc05],
[-eQ],
[_?=],
[=?iso-8859-1?Q?=5F=3F=3D?=])
TESTENC2047([length limit],[enc06],
[-cUTF-8 -eQ],
[J'interdis aux marchands de vanter trop leur marchandises. Car ils se font vite pédagogues et t'enseignent comme but ce qui n'est par essence qu'un moyen, et te trompant ainsi sur la route à suivre les voilà bientôt qui te dégradent, car si leur musique est vulgaire ils te fabriquent pour te la vendre une âme vulgaire.],
[=?UTF-8?Q?J'interdis_aux_marchands_de_vanter_trop_leur_marchandises._Car_?=
=?UTF-8?Q?ils_se_font_vite_p=C3=A9dagogues_et_t'enseignent_comme_but_ce_q?=
=?UTF-8?Q?ui_n'est_par_essence_qu'un_moyen,_et_te_trompant_ainsi_sur_la_r?=
=?UTF-8?Q?oute_=C3=A0_suivre_les_voil=C3=A0_bient=C3=B4t_qui_te_d=C3=A9gr?=
=?UTF-8?Q?adent,_car_si_leur_musique_est_vulgaire_ils_te_fabriquent_pour_?=
=?UTF-8?Q?te_la_vendre_une_=C3=A2me_vulgaire.?=])
m4_popdef([TESTENC2047])
......
......@@ -104,24 +104,23 @@ decode_octal (char *buf)
int
main (int argc, char *argv[])
{
int c;
char buf[256];
char vbuf[256];
char *charset = strdup ("iso-8859-1");
char *encoding = strdup ("quoted-printable");
int rc;
char *buf = NULL;
size_t size = 0;
size_t n;
char *charset = "iso-8859-1";
char *encoding = "quoted-printable";
int octal = 0;
while ((c = getopt (argc, argv, "c:e:hot")) != EOF)
switch (c)
while ((rc = getopt (argc, argv, "c:e:hot")) != EOF)
switch (rc)
{
case 'c':
free (charset);
charset = strdup (optarg);
charset = optarg;
break;
case 'e':
free (encoding);
encoding = strdup (optarg);
encoding = optarg;
break;
case 'o':
......@@ -140,67 +139,23 @@ main (int argc, char *argv[])
exit (1);
}
while (fgets (buf, sizeof (buf), stdin))
{
int len;
char *p = NULL;
char *cmd;
int rc;
len = strlen (buf);
if (len > 0 && buf[len - 1] == '\n')
buf[len - 1] = 0;
strncpy(vbuf, buf, sizeof vbuf);
cmd = vbuf;
if (cmd[0] == '\\')
mu_stdstream_setup (MU_STDSTREAM_RESET_NONE);
while ((rc = mu_stream_getline (mu_strin, &buf, &size, &n)) == 0 && n > 0)
{
if (cmd[1] == 0)
{
fprintf (stderr, "Unfinished command\n");
continue;
}
for (p = cmd + 2; *p && *p == ' '; p++)
;
switch (cmd[1])
{
case 'c':
free (charset);
charset = strdup (p);
continue;
case 'e':
free (encoding);
encoding = strdup (p);
continue;
case 'o':
octal = 1;
continue;
case 't':
octal = 0;
continue;
case '\\':
cmd++;
break;
default:
fprintf (stderr, "Unknown command\n");
continue;
}
}
char *p;
mu_rtrim_class (buf, MU_CTYPE_ENDLN);
if (octal)
decode_octal (cmd);
decode_octal (buf);
rc = mu_rfc2047_encode (charset, encoding, cmd, &p);
rc = mu_rfc2047_encode (charset, encoding, buf, &p);
if (rc)
fprintf (stderr, "%s", mu_strerror (rc));
mu_diag_funcall (MU_DIAG_ERROR, "mu_rfc2047_encode", NULL, rc);
else if (p)
printf ("%s\n", p);
mu_printf ("%s\n", p);
free (p);
}
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_getline", NULL, rc);
return 0;
}
......