summaryrefslogtreecommitdiff
path: root/libmailutils/rfc2047.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/rfc2047.c')
-rw-r--r--libmailutils/rfc2047.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/libmailutils/rfc2047.c b/libmailutils/rfc2047.c
new file mode 100644
index 000000000..c30ba2fe0
--- /dev/null
+++ b/libmailutils/rfc2047.c
@@ -0,0 +1,314 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <mailutils/stream.h>
+#include <mailutils/filter.h>
+#include <mailutils/errno.h>
+#include <mailutils/mutil.h>
+
+static int
+realloc_buffer (char **bufp, size_t *bufsizep, size_t incr)
+{
+ size_t newsize = *bufsizep + incr;
+ char *newp = realloc (*bufp, newsize);
+ if (newp == NULL)
+ return 1;
+ *bufp = newp;
+ *bufsizep = newsize;
+ return 0;
+}
+
+int
+getword (char **pret, const char **pstr, int delim)
+{
+ size_t len;
+ char *ret;
+ const char *start = *pstr;
+ const char *end = strchr (start, delim);
+
+ free (*pret);
+ *pret = NULL;
+ if (!end)
+ return MU_ERR_BAD_2047_INPUT;
+ len = end - start;
+ ret = malloc (len + 1);
+ if (!ret)
+ return ENOMEM;
+ memcpy (ret, start, len);
+ ret[len] = 0;
+ *pstr = end + 1;
+ *pret = ret;
+ return 0;
+}
+
+int
+mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr)
+{
+ int status = 0;
+ const char *fromstr;
+ char *buffer;
+ size_t bufsize;
+ size_t bufpos;
+ size_t run_count = 0;
+ char *fromcode = NULL;
+ char *encoding_type = NULL;
+ char *encoded_text = NULL;
+
+#define BUFINC 128
+#define CHKBUF(count) do { \
+ if (bufpos+count >= bufsize) \
+ { \
+ size_t s = bufpos + count - bufsize; \
+ if (s < BUFINC) \
+ s = BUFINC; \
+ if (realloc_buffer (&buffer, &bufsize, s)) \
+ { \
+ free (buffer); \
+ free (fromcode); \
+ free (encoding_type); \
+ free (encoded_text); \
+ return ENOMEM; \
+ } \
+ } \
+ } while (0)
+
+ if (!input)
+ return EINVAL;
+ if (!ptostr)
+ return MU_ERR_OUT_PTR_NULL;
+
+ fromstr = input;
+
+ /* Allocate the buffer. It is assumed that encoded string is always
+ longer than it's decoded variant, so it's safe to use its length
+ as the first estimate */
+ bufsize = strlen (fromstr) + 1;
+ buffer = malloc (bufsize);
+ if (buffer == NULL)
+ return ENOMEM;
+ bufpos = 0;
+
+ while (*fromstr)
+ {
+ if (strncmp (fromstr, "=?", 2) == 0)
+ {
+ mu_stream_t filter = NULL;
+ mu_stream_t in_stream = NULL;
+ const char *filter_type = NULL;
+ size_t nbytes = 0, size;
+ const char *sp = fromstr + 2;
+ char tmp[128];
+
+ status = getword (&fromcode, &sp, '?');
+ if (status)
+ break;
+ status = getword (&encoding_type, &sp, '?');
+ if (status)
+ break;
+ status = getword (&encoded_text, &sp, '?');
+ if (status)
+ break;
+ if (sp == NULL || sp[0] != '=')
+ {
+ status = MU_ERR_BAD_2047_INPUT;
+ break;
+ }
+
+ size = strlen (encoded_text);
+
+ switch (encoding_type[0])
+ {
+ case 'b':
+ case 'B':
+ filter_type = "base64";
+ break;
+
+ case 'q':
+ case 'Q':
+ filter_type = "Q";
+ break;
+
+ default:
+ status = MU_ERR_BAD_2047_INPUT;
+ break;
+ }
+
+ if (status != 0)
+ break;
+
+ mu_memory_stream_create (&in_stream, 0);
+ mu_stream_write (in_stream, encoded_text, size, NULL);
+ mu_stream_seek (in_stream, 0, MU_SEEK_SET, NULL);
+ status = mu_decode_filter (&filter, in_stream, filter_type, fromcode,
+ tocode);
+ mu_stream_unref (in_stream);
+ if (status != 0)
+ break;
+
+ while (mu_stream_read (filter, tmp, sizeof (tmp), &nbytes) == 0
+ && nbytes)
+ {
+ CHKBUF (nbytes);
+ memcpy (buffer + bufpos, tmp, nbytes);
+ bufpos += nbytes;
+ }
+
+ mu_stream_close (filter);
+ mu_stream_destroy (&filter);
+
+ fromstr = sp + 1;
+ run_count = 1;
+ }
+ else if (run_count)
+ {
+ if (*fromstr == ' ' || *fromstr == '\t')
+ {
+ run_count++;
+ fromstr++;
+ continue;
+ }
+ else
+ {
+ if (--run_count)
+ {
+ CHKBUF (run_count);
+ memcpy (buffer + bufpos, fromstr - run_count, run_count);
+ bufpos += run_count;
+ run_count = 0;
+ }
+ CHKBUF (1);
+ buffer[bufpos++] = *fromstr++;
+ }
+ }
+ else
+ {
+ CHKBUF (1);
+ buffer[bufpos++] = *fromstr++;
+ }
+ }
+
+ if (*fromstr)
+ {
+ size_t len = strlen (fromstr);
+ CHKBUF (len);
+ memcpy (buffer + bufpos, fromstr, len);
+ bufpos += len;
+ }
+
+ CHKBUF (1);
+ buffer[bufpos++] = 0;
+
+ free (fromcode);
+ free (encoding_type);
+ free (encoded_text);
+
+ if (status)
+ free (buffer);
+ else
+ *ptostr = realloc (buffer, bufpos);
+ return status;
+}
+
+
+/**
+ Encode a header according to RFC 2047
+
+ @param charset
+ Charset of the text to encode
+ @param encoding
+ Requested encoding (must be "base64" or "quoted-printable")
+ @param text
+ Actual text to encode
+ @param result [OUT]
+ Encoded string
+
+ @return 0 on success
+*/
+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;
+ int rc;
+
+ if (charset == NULL || encoding == NULL || text == NULL)
+ return EINVAL;
+
+ if (strcmp (encoding, "base64") == 0)
+ encoding = "B";
+ else if (strcmp (encoding, "quoted-printable") == 0)
+ encoding = "Q";
+ else if (encoding[1] || !strchr ("BQ", encoding[0]))
+ return MU_ERR_BAD_2047_ENCODING;
+
+ rc = mu_memory_stream_create (&input_stream, 0);
+ if (rc)
+ return rc;
+
+ mu_stream_write (input_stream, text, strlen (text), NULL);
+ mu_stream_seek (input_stream, 0, MU_SEEK_SET, NULL);
+ rc = mu_filter_create (&output_stream, input_stream,
+ encoding, MU_FILTER_ENCODE,
+ MU_STREAM_READ | MU_STREAM_AUTOCLOSE);
+ 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)
+ {
+ char *p = *result;
+ size_t s;
+
+ p += sprintf (p, "=?%s?%s?", charset, encoding);
+
+ rc = mu_stream_read (output_stream,
+ p,
+ strlen (text) * 3, &s);
+
+ strcpy (p + s, "?=");
+ }
+ else
+ rc = ENOMEM;
+ mu_stream_destroy (&output_stream);
+ }
+ else
+ mu_stream_destroy (&input_stream);
+
+ return rc;
+}

Return to:

Send suggestions and report system problems to the System administrator.