bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
f8b23df4e11e19ebe8a159c5e205c48e425200a5Timo Sirainen
f8b23df4e11e19ebe8a159c5e205c48e425200a5Timo Sirainen#include "lib.h"
33ca6b017b6ebbd048651b5e3d16915001dbc291Timo Sirainen#include "str.h"
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen#include "imap-arg.h"
ca8863af6ce6b695a369dfb7b50e909420dc45dfTimo Sirainen#include "imap-quote.h"
f8b23df4e11e19ebe8a159c5e205c48e425200a5Timo Sirainen
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen/* If we have quoted-specials (<">, <\>) in a string, the minimum quoted-string
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen overhead is 3 bytes ("\") while the minimum literal overhead is 5 bytes
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen ("{n}\r\n"). But the literal overhead also depends on the string size. If
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen the string length is less than 10, literal catches up to quoted-string after
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen 3 quoted-specials. If the string length is 10..99, it catches up after 4
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen quoted-specials, and so on. We'll assume that the string lengths are usually
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen in double digits, so we'll switch to literals after seeing 4
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen quoted-specials. */
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen#define QUOTED_MAX_ESCAPE_CHARS 4
6764705326c7187d703b784b8e86083c08363549Timo Sirainen
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainenvoid imap_append_string(string_t *dest, const char *src)
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainen{
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainen i_assert(src != NULL);
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainen
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainen imap_append_nstring(dest, src);
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainen}
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainen
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainenvoid imap_append_astring(string_t *dest, const char *src)
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen{
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen unsigned int i;
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen i_assert(src != NULL);
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen for (i = 0; src[i] != '\0'; i++) {
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen if (!IS_ASTRING_CHAR(src[i])) {
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen imap_append_string(dest, src);
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen return;
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen }
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen }
1e430b62d5bb21dc0d227b06a7a322283d75c452Timo Sirainen /* don't mix up NIL and "NIL"! */
1e430b62d5bb21dc0d227b06a7a322283d75c452Timo Sirainen if (i == 0 || strcasecmp(src, "NIL") == 0)
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen imap_append_string(dest, src);
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen else
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen str_append(dest, src);
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen}
0b1929455dec03c97d35e5286a17a8c7bd1a633bTimo Sirainen
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainenstatic void
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainenimap_append_literal(string_t *dest, const char *src, unsigned int pos)
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen{
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen size_t full_len = pos + strlen(src+pos);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen str_printfa(dest, "{%"PRIuSIZE_T"}\r\n", full_len);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen buffer_append(dest, src, full_len);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen}
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainenvoid imap_append_nstring(string_t *dest, const char *src)
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainen{
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen unsigned int escape_count = 0;
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainen size_t i;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen if (src == NULL) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen str_append(dest, "NIL");
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen return;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen /* first check if we can (or want to) write this as quoted or
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen as literal.
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen quoted-specials = DQUOTE / "\"
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> /
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen "\" quoted-specials
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen TEXT-CHAR = <any CHAR except CR and LF>
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen */
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen for (i = 0; src[i] != '\0'; i++) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen switch (src[i]) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case '"':
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case '\\':
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen if (escape_count++ < QUOTED_MAX_ESCAPE_CHARS)
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen break;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen /* fall through */
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case 13:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case 10:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen imap_append_literal(dest, src, i);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen return;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen default:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen if ((unsigned char)src[i] >= 0x80) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen imap_append_literal(dest, src, i);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen return;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen break;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen imap_append_quoted(dest, src);
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainen}
169d60e9729b7a047ed17e82607101e20c69dcb1Timo Sirainen
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitovstatic void remove_newlines_and_append(string_t *dest, const char *src)
be0c9927cd9856b1a37c9c8c6b73bd7e5a75737cSergey Kitov{
be0c9927cd9856b1a37c9c8c6b73bd7e5a75737cSergey Kitov size_t src_len;
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov string_t *src_nolf;
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov src_len = strlen(src);
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov src_nolf = t_str_new(src_len + 1);
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov for (size_t i = 0; i < src_len; ++i) {
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov if (src[i] != '\r' && src[i] != '\n') {
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov str_append_c(src_nolf, src[i]);
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov } else if (src[i+1] != ' ' &&
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov src[i+1] != '\t' &&
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov src[i+1] != '\r' &&
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov src[i+1] != '\n' &&
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov src[i+1] != '\0') {
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov /* ensure whitespace between lines if new line doesn't start with whitespace */
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov str_append_c(src_nolf, ' ');
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov }
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov }
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov imap_append_nstring(dest, str_c(src_nolf));
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov}
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitovvoid imap_append_nstring_nolf(string_t *dest, const char *src)
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov{
be0c9927cd9856b1a37c9c8c6b73bd7e5a75737cSergey Kitov if (src == NULL || strpbrk(src, "\r\n") == NULL)
be0c9927cd9856b1a37c9c8c6b73bd7e5a75737cSergey Kitov imap_append_nstring(dest, src);
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov else if (buffer_get_pool(dest)->datastack_pool)
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov remove_newlines_and_append(dest, src);
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov else T_BEGIN {
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov remove_newlines_and_append(dest, src);
44c6d46cefc24daee032d2a0f95f8576fa10889aSergey Kitov } T_END;
be0c9927cd9856b1a37c9c8c6b73bd7e5a75737cSergey Kitov}
be0c9927cd9856b1a37c9c8c6b73bd7e5a75737cSergey Kitov
2ca9d7b51853007c0edd3bd373375792f2c8272fTimo Sirainenvoid imap_append_quoted(string_t *dest, const char *src)
6764705326c7187d703b784b8e86083c08363549Timo Sirainen{
6764705326c7187d703b784b8e86083c08363549Timo Sirainen str_append_c(dest, '"');
6764705326c7187d703b784b8e86083c08363549Timo Sirainen for (; *src != '\0'; src++) {
6764705326c7187d703b784b8e86083c08363549Timo Sirainen switch (*src) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case 13:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case 10:
ed8256349d7106b2d2accdf1ec390cc6a903d887Timo Sirainen /* not allowed */
ed8256349d7106b2d2accdf1ec390cc6a903d887Timo Sirainen break;
6764705326c7187d703b784b8e86083c08363549Timo Sirainen case '"':
6764705326c7187d703b784b8e86083c08363549Timo Sirainen case '\\':
6764705326c7187d703b784b8e86083c08363549Timo Sirainen str_append_c(dest, '\\');
6764705326c7187d703b784b8e86083c08363549Timo Sirainen str_append_c(dest, *src);
6764705326c7187d703b784b8e86083c08363549Timo Sirainen break;
6764705326c7187d703b784b8e86083c08363549Timo Sirainen default:
6764705326c7187d703b784b8e86083c08363549Timo Sirainen if ((unsigned char)*src >= 0x80) {
6764705326c7187d703b784b8e86083c08363549Timo Sirainen /* 8bit input not allowed in dquotes */
6764705326c7187d703b784b8e86083c08363549Timo Sirainen break;
6764705326c7187d703b784b8e86083c08363549Timo Sirainen }
6764705326c7187d703b784b8e86083c08363549Timo Sirainen
6764705326c7187d703b784b8e86083c08363549Timo Sirainen str_append_c(dest, *src);
6764705326c7187d703b784b8e86083c08363549Timo Sirainen break;
6764705326c7187d703b784b8e86083c08363549Timo Sirainen }
6764705326c7187d703b784b8e86083c08363549Timo Sirainen }
6764705326c7187d703b784b8e86083c08363549Timo Sirainen str_append_c(dest, '"');
6764705326c7187d703b784b8e86083c08363549Timo Sirainen}
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainenvoid imap_append_string_for_humans(string_t *dest,
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen const unsigned char *src, size_t size)
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen{
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen size_t i, pos, remove_count = 0;
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen bool whitespace_prefix = TRUE, last_lwsp = TRUE, modify = FALSE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen /* first check if there is anything to change */
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen for (i = 0; i < size; i++) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen switch (src[i]) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case 0:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen /* convert NUL to #0x80 */
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen last_lwsp = FALSE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen modify = TRUE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen break;
f950619b1d003b0e8c604478df7fe3067d934b26Timo Sirainen case 13:
f950619b1d003b0e8c604478df7fe3067d934b26Timo Sirainen case 10:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case '\t':
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen modify = TRUE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen /* fall through */
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case ' ':
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen if (last_lwsp) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen modify = TRUE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen remove_count++;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen last_lwsp = TRUE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen break;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case '"':
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case '\\':
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen modify = TRUE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen last_lwsp = FALSE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen break;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen default:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen if ((src[i] & 0x80) != 0)
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen modify = TRUE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen last_lwsp = FALSE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen break;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen if (!last_lwsp)
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen whitespace_prefix = FALSE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen if (last_lwsp && i > 0 && !whitespace_prefix) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen modify = TRUE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen remove_count++;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen if (!modify) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen /* fast path: we can simply write it as quoted string
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen without any escaping */
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen str_append_c(dest, '"');
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen str_append_n(dest, src, size);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen str_append_c(dest, '"');
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen return;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen if (size == remove_count) {
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen /* contained only whitespace */
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen str_append(dest, "\"\"");
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen return;
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen }
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen str_printfa(dest, "{%"PRIuSIZE_T"}\r\n", size - remove_count);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen pos = str_len(dest);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen last_lwsp = TRUE; whitespace_prefix = TRUE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen for (i = 0; i < size; i++) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen switch (src[i]) {
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case 0:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen str_append_c(dest, 128);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen last_lwsp = FALSE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen break;
f950619b1d003b0e8c604478df7fe3067d934b26Timo Sirainen case 13:
f950619b1d003b0e8c604478df7fe3067d934b26Timo Sirainen case 10:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case '\t':
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen case ' ':
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen if (!last_lwsp)
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen str_append_c(dest, ' ');
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen last_lwsp = TRUE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen break;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen default:
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen last_lwsp = FALSE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen str_append_c(dest, src[i]);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen break;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen if (!last_lwsp)
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen whitespace_prefix = FALSE;
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen }
d8ce48fb2b068ceae03f8df3610e254c3ccd2b2eTimo Sirainen if (last_lwsp && i > 0 && !whitespace_prefix)
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen str_truncate(dest, str_len(dest)-1);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen i_assert(str_len(dest) - pos == size - remove_count);
eea128bec7b5af0ece444e0c3b35297cd1e2b31cTimo Sirainen}