changeset 168:bf5adc969d86 HEAD

Convert 8bit text in headers to quoted-printable in ENVELOPE.
author Timo Sirainen <tss@iki.fi>
date Sat, 07 Sep 2002 00:51:21 +0300
parents 99a150cfc4e5
children 53e8e405266b
files src/lib-imap/imap-envelope.c
diffstat 1 files changed, 148 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-imap/imap-envelope.c	Fri Sep 06 22:27:32 2002 +0300
+++ b/src/lib-imap/imap-envelope.c	Sat Sep 07 00:51:21 2002 +0300
@@ -15,47 +15,169 @@
 	char *in_reply_to, *message_id;
 };
 
-static const char *
-t_buffer_get_quote(const char *value, unsigned int *value_len)
+#define IS_BREAK_CHAR(c) \
+	((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' || \
+	 (c) == ',' || (c) == ':' || (c) == ';' || (c) == '@' || \
+	 (c) == '<' || (c) == '>' || (c) == '(' || (c) == ')' || \
+	 (c) == '[' || (c) == ']' || (c) == '=')
+
+static unsigned int next_token_quoted(const char *value, unsigned int len,
+				      int *need_qp)
+{
+	unsigned int i;
+
+	i_assert(value[0] == '"');
+
+	*need_qp = FALSE;
+
+	for (i = 1; i < len; i++) {
+		if ((unsigned char)value[i] & 0x80)
+			*need_qp = TRUE;
+
+		if (value[i] == '"') {
+			i++;
+			break;
+		}
+	}
+
+	return i;
+}
+
+static unsigned int next_token(const char *value, unsigned int len,
+			       int *need_qp, int qp_on)
 {
-	char *buf, *p;
-	unsigned int i, len;
+	unsigned int i = 0;
+
+	if (value[0] == '"')
+		return next_token_quoted(value, len, need_qp);
+
+	*need_qp = FALSE;
+
+	if (qp_on) {
+		/* skip spaces, so we don't end up QP'ing word at a time */
+		for (i = 0; i < len; i++) {
+			if (value[i] != ' ')
+				break;
+		}
+	}
+
+	if (IS_BREAK_CHAR(value[i])) {
+		/* return all break-chars in one token */
+		for (i++; i < len; i++) {
+			if (!IS_BREAK_CHAR(value[i]))
+				break;
+		}
+
+		return i;
+	}
+
+	/* then stop at break-char */
+	for (; i < len; i++) {
+		if ((unsigned char)value[i] & 0x80)
+			*need_qp = TRUE;
+
+		if (IS_BREAK_CHAR(value[i]))
+			break;
+	}
 
-	len = *value_len;
-	p = buf = t_buffer_get(len * 2 + 3);
-	*p++ = '"';
+	return i;
+}
+
+static void append_quoted_qp(TempString *str, const char *value,
+			     unsigned int len)
+{
+	unsigned int i;
+	unsigned char c;
+
+	/* do this the easy way, it's already broken behaviour to leave the
+	   8bit text in mailbox, so we shouldn't need to try too hard to make
+	   it readable. Keep 'A'..'Z', 'a'..'z' and '0'..'9', QP rest */
+
+	for (i = 0; i < len; i++) {
+		if (value[i] == ' ')
+			t_string_append_c(str, '_');
+		else if ((value[i] >= 'A' && value[i] <= 'Z') ||
+			 (value[i] >= 'a' && value[i] <= 'z') ||
+			 (value[i] >= '0' && value[i] <= '9')) {
+			t_string_append_c(str, value[i]);
+		} else {
+			t_string_append_c(str, '=');
+			c = (unsigned char)value[i] >> 4;
+			t_string_append_c(str, c < 10 ? (c+'0') : (c-10+'A'));
+			c = (unsigned char)value[i] & 0x0f;
+			t_string_append_c(str, c < 10 ? (c+'0') : (c-10+'A'));
+		}
+	}
+}
+
+static void append_quoted(TempString *str, const char *value, unsigned int len)
+{
+	unsigned int i;
+
 	for (i = 0; i < len; i++) {
 		if (value[i] == '\\' || value[i] == '"')
-			*p++ = '\\';
-		*p++ = value[i];
+			t_string_append_c(str, '\\');
+		t_string_append_c(str, value[i]);
 	}
-	*p++ = '"';
-	*p++ = '\0';
+}
+
+/* does two things: 1) escape '\' and '"' characters, 2) 8bit text -> QP */
+static TempString *get_quoted_str(const char *value, unsigned int value_len)
+{
+	TempString *str;
+	unsigned int token_len;
+	int qp, need_qp;
+
+	str = t_string_new(value_len * 2);
+	qp = FALSE;
+
+	t_string_append_c(str, '"');
+	while (value_len > 0) {
+		token_len = next_token(value, value_len, &need_qp, qp);
+		i_assert(token_len > 0);
 
-	*value_len = (unsigned int) (p-buf);
-	return buf;
+		/* header may be split to multiple lines, we don't want them */
+		while (token_len > 0 && (value[0] == '\r' ||
+					 value[0] == '\n')) {
+			value++;
+			value_len--;
+		}
+
+		if (need_qp && !qp) {
+			t_string_append(str, "=?x-unknown?Q?");
+			qp = TRUE;
+		} else if (!need_qp && qp) {
+			t_string_append(str, "?=");
+			qp = FALSE;
+		}
+
+		if (need_qp)
+			append_quoted_qp(str, value, token_len);
+		else
+			append_quoted(str, value, token_len);
+
+		value += token_len;
+		value_len -= token_len;
+	}
+
+	if (qp) t_string_append(str, "?=");
+	t_string_append_c(str, '"');
+
+	return str;
 }
 
 static const char *quote_str_nil(const char *value)
 {
-	const char *buf;
-	unsigned int value_len;
-
-	if (value == NULL)
-		return "NIL";
-
-	value_len = strlen(value);
-	buf = t_buffer_get_quote(value, &value_len);
-	t_buffer_alloc(value_len);
-	return buf;
+	return value == NULL ? "NIL" :
+		get_quoted_str(value, strlen(value))->str;
 }
 
 static char *quote_value(Pool pool, const char *value, unsigned int value_len)
 {
-	const char *buf;
+	TempString *str;
 
-	buf = t_buffer_get_quote(value, &value_len);
-	return p_strndup(pool, buf, value_len);
+	str = get_quoted_str(value, value_len);
+	return p_strndup(pool, str->str, str->len);
 }
 
 static Rfc822Address *parse_address(Pool pool, const char *value,