quoted-printable.c revision e8ee95581992b8f7d734981550018d7ecdaae6b8
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2002-2013 Dovecot authors, see the included COPYING file */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "lib.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "buffer.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "hex-binary.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#include "quoted-printable.h"
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen#define QP_IS_TRAILING_SPACE(c) \
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen ((c) == ' ' || (c) == '\t')
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic int
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainenqp_is_end_of_line(const unsigned char *src, size_t *src_pos, size_t size)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen{
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen size_t i = *src_pos;
1d738cce754bc64bbc66d3355ebdaf3f6eac55f1Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_assert(src[i] == '=');
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen for (i++; i < size; i++) {
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen if (QP_IS_TRAILING_SPACE(src[i]) || src[i] == '\r')
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen continue;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen if (src[i] != '\n')
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen return 0;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen *src_pos = i;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen return 1;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen }
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen return -1;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen}
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainenstatic int
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainenquoted_printable_decode_full(const unsigned char *src, size_t src_size,
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen size_t *src_pos_r, buffer_t *dest, bool eof)
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen{
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen char hexbuf[3];
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen size_t src_pos, pos, next;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen bool errors = FALSE;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen int ret;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen hexbuf[2] = '\0';
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen next = 0;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen for (src_pos = 0; src_pos < src_size; src_pos++) {
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen if (src[src_pos] != '=' && src[src_pos] != '\n')
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen continue;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (src[src_pos] == '\n') {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* drop trailing whitespace */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen pos = src_pos;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (pos > 0 && src[pos-1] == '\r')
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen pos--;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen while (pos > 0 && QP_IS_TRAILING_SPACE(src[pos-1]))
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen pos--;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen buffer_append(dest, src + next, pos - next);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen next = src_pos+1;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen buffer_append_c(dest, '\r');
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen buffer_append_c(dest, '\n');
b397665e90fa0fc7c6a9156fdd6cf28b571e8e39Timo Sirainen continue;
b397665e90fa0fc7c6a9156fdd6cf28b571e8e39Timo Sirainen }
b397665e90fa0fc7c6a9156fdd6cf28b571e8e39Timo Sirainen
b397665e90fa0fc7c6a9156fdd6cf28b571e8e39Timo Sirainen /* '=' */
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen buffer_append(dest, src + next, src_pos - next);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen next = src_pos;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if ((ret = qp_is_end_of_line(src, &src_pos, src_size)) > 0) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* =[whitespace][\r]\n */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen next = src_pos+1;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen continue;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (ret < 0) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* '=' was followed only by whitespace */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen break;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (src_pos+2 >= src_size) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* '=' was followed by non-whitespace */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (eof)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen errors = TRUE;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen break;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* =<hex> */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen hexbuf[0] = src[src_pos+1];
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen hexbuf[1] = src[src_pos+2];
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (hex_to_binary(hexbuf, dest) == 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen src_pos += 2;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen next = src_pos + 1;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen } else {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* non-hex data, show as-is */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen errors = TRUE;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen next = src_pos;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (src_pos == src_size) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* add everything but trailing spaces */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (src_pos > 0 && src[src_pos-1] == '\r')
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen src_pos--;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen while (src_pos > 0 && QP_IS_TRAILING_SPACE(src[src_pos-1]))
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen src_pos--;
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen buffer_append(dest, src + next, src_pos - next);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen next = src_pos;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen *src_pos_r = next;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen return errors ? -1 : 0;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen}
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenint quoted_printable_decode(const unsigned char *src, size_t src_size,
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen size_t *src_pos_r, buffer_t *dest)
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen{
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen return quoted_printable_decode_full(src, src_size, src_pos_r, dest, FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen}
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenint quoted_printable_decode_final(const unsigned char *src, size_t src_size,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen size_t *src_pos_r, buffer_t *dest)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen{
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen return quoted_printable_decode_full(src, src_size, src_pos_r, dest, TRUE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainenint quoted_printable_q_decode(const unsigned char *src, size_t src_size,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen buffer_t *dest)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen char hexbuf[3];
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen size_t src_pos, next;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen bool errors = FALSE;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen hexbuf[2] = '\0';
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen next = 0;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen for (src_pos = 0; src_pos < src_size; src_pos++) {
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen if (src[src_pos] != '_' && src[src_pos] != '=')
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen continue;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen buffer_append(dest, src + next, src_pos - next);
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen next = src_pos;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen if (src[src_pos] == '_') {
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen buffer_append_c(dest, ' ');
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen next++;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen continue;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen if (src_pos+2 >= src_size)
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen break;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen /* =<hex> */
0bf3eac1110a902e7ec7e695c64e8e46c114e623Timo Sirainen hexbuf[0] = src[src_pos+1];
0bf3eac1110a902e7ec7e695c64e8e46c114e623Timo Sirainen hexbuf[1] = src[src_pos+2];
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen if (hex_to_binary(hexbuf, dest) == 0) {
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen src_pos += 2;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen next = src_pos+1;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen } else {
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen /* non-hex data, show as-is */
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen errors = TRUE;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen next = src_pos;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen }
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen }
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen buffer_append(dest, src + next, src_size - next);
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen return errors ? -1 : 0;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen}
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen