From 4f1d6acd6cf202afd19e1ad56392c7970a6f204a Mon Sep 17 00:00:00 2001 From: Colin Patrick McCabe Date: Mon, 11 Jul 2011 14:31:59 -0700 Subject: [PATCH] Add mime encoding Signed-off-by: Colin McCabe --- src/Makefile.am | 8 +++ src/common/mime.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++ src/common/mime.h | 48 +++++++++++++++++ src/test/mime.cc | 124 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 313 insertions(+) create mode 100644 src/common/mime.c create mode 100644 src/common/mime.h create mode 100644 src/test/mime.cc diff --git a/src/Makefile.am b/src/Makefile.am index 7ce6a941e9222..a364260544822 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -456,6 +456,12 @@ unittest_utf8_LDADD = ${UNITTEST_LDADD} $(LIBGLOBAL_LDA) unittest_utf8_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} check_PROGRAMS += unittest_utf8 +unittest_mime_SOURCES = test/mime.cc +unittest_mime_LDFLAGS = -pthread ${AM_LDFLAGS} +unittest_mime_LDADD = ${UNITTEST_LDADD} $(LIBGLOBAL_LDA) +unittest_mime_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} +check_PROGRAMS += unittest_mime + unittest_rgw_escape_SOURCES = test/rgw_escape.cc rgw/rgw_escape.c unittest_rgw_escape_LDFLAGS = -pthread ${AM_LDFLAGS} unittest_rgw_escape_LDADD = ${UNITTEST_LDADD} $(LIBGLOBAL_LDA) @@ -676,6 +682,7 @@ libcommon_files = \ include/ceph_frag.cc \ common/config.cc \ common/utf8.c \ + common/mime.c \ common/strtol.cc \ common/page.cc \ common/lockdep.cc \ @@ -860,6 +867,7 @@ noinst_HEADERS = \ common/config_obs.h\ common/ceph_crypto.h\ common/utf8.h\ + common/mime.h\ common/secret.h\ common/strtol.h\ common/static_assert.h\ diff --git a/src/common/mime.c b/src/common/mime.c new file mode 100644 index 0000000000000..7bb2f3103d14e --- /dev/null +++ b/src/common/mime.c @@ -0,0 +1,133 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ +#include "common/utf8.h" + +#include +#include +#include + +int mime_encode_as_qp(const char *input, char *output, int outlen) +{ + int ret = 1; + char *o = output; + const unsigned char *i = (const unsigned char*)input; + while (1) { + int c = *i; + if (c == '\0') { + break; + } + else if ((c & 0x80) || (c == '=')) { + if (outlen >= 3) { + snprintf(o, outlen, "=%02X", c); + outlen -= 3; + o += 3; + } + else + outlen = 0; + ret += 3; + } + else { + if (outlen >= 1) { + snprintf(o, outlen, "%c", c); + outlen -= 1; + o += 1; + } + ret += 1; + } + ++i; + } + return ret; +} + +static inline signed int hexchar_to_int(unsigned int c) +{ + switch(c) { + case '0': + return 0; + case '1': + return 1; + case '2': + return 2; + case '3': + return 3; + case '4': + return 4; + case '5': + return 5; + case '6': + return 6; + case '7': + return 7; + case '8': + return 8; + case '9': + return 9; + case 'A': + case 'a': + return 10; + case 'B': + case 'b': + return 11; + case 'C': + case 'c': + return 12; + case 'D': + case 'd': + return 13; + case 'E': + case 'e': + return 14; + case 'F': + case 'f': + return 15; + case '\0': + default: + return -EDOM; + } +} + +int mime_decode_from_qp(const char *input, char *output, int outlen) +{ + int ret = 1; + char *o = output; + const unsigned char *i = input; + while (1) { + unsigned int c = *i; + if (c == '\0') { + break; + } + else if (c & 0x80) { + /* The high bit is never set in quoted-printable encoding! */ + return -EDOM; + } + else if (c == '=') { + int high = hexchar_to_int(*++i); + if (high < 0) + return -EINVAL; + int low = hexchar_to_int(*++i); + if (low < 0) + return -EINVAL; + c = (high << 4) + low; + } + ++i; + + if (outlen >= 1) { + snprintf(o, outlen, "%c", c); + outlen -= 1; + o += 1; + } + ret += 1; + } + return ret; +} diff --git a/src/common/mime.h b/src/common/mime.h new file mode 100644 index 0000000000000..625d5520d0b88 --- /dev/null +++ b/src/common/mime.h @@ -0,0 +1,48 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef CEPH_COMMON_MIME_H +#define CEPH_COMMON_MIME_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Encode a buffer as quoted-printable. + * + * Returns the length of the buffer we would need to do the encoding. + * If we don't have enough buffer space, the output will be truncated. + * + * You may call mime_encode_as_qp(input, NULL, 0) to find the size of the + * buffer you will need. + */ +signed int mime_encode_as_qp(const char *input, char *output, int outlen); + +/* Decode a quoted-printable buffer. + * + * Returns a negative error code if the input is not a valid quoted-printable + * buffer. + * Returns the length of the buffer we would need to do the encoding. + * If we don't have enough buffer space, the output will be truncated. + * + * You may call mime_encode_as_qp(input, NULL, 0) to find the size of the + * buffer you will need. + */ +signed int mime_decode_from_qp(const char *input, char *output, int outlen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/test/mime.cc b/src/test/mime.cc new file mode 100644 index 0000000000000..3e1ed9365d08b --- /dev/null +++ b/src/test/mime.cc @@ -0,0 +1,124 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ +#include "common/mime.h" +#include "gtest/gtest.h" + +#include +#include + +using std::string; + +TEST(MimeTests, SimpleEncode) { + char output[256]; + memset(output, 0, sizeof(output)); + int len; + + len = mime_encode_as_qp("abc", NULL, 0); + ASSERT_EQ(len, 4); + len = mime_encode_as_qp("abc", output, 4); + ASSERT_EQ(len, 4); + ASSERT_EQ(string("abc"), string(output)); + + len = mime_encode_as_qp("a=b", NULL, 0); + ASSERT_EQ(len, 6); + len = mime_encode_as_qp("a=b", output, 6); + ASSERT_EQ(len, 6); + ASSERT_EQ(string("a=3Db"), string(output)); + + len = mime_encode_as_qp("Libert\xc3\xa9", NULL, 0); + ASSERT_EQ(len, 13); + len = mime_encode_as_qp("Libert\xc3\xa9", output, 13); + ASSERT_EQ(len, 13); + ASSERT_EQ(string("Libert=C3=A9"), string(output)); +} + +TEST(MimeTests, EncodeOutOfSpace) { + char output[256]; + memset(output, 0, sizeof(output)); + int len; + + len = mime_encode_as_qp("abcdefg", NULL, 0); + ASSERT_EQ(len, 8); + len = mime_encode_as_qp("abcdefg", output, 4); + ASSERT_EQ(len, 8); + ASSERT_EQ(string("abc"), string(output)); + len = mime_encode_as_qp("abcdefg", output, 1); + ASSERT_EQ(len, 8); + ASSERT_EQ(string(""), string(output)); + + len = mime_encode_as_qp("a=b", output, 2); + ASSERT_EQ(len, 6); + ASSERT_EQ(string("a"), string(output)); + len = mime_encode_as_qp("a=b", output, 3); + ASSERT_EQ(len, 6); + ASSERT_EQ(string("a"), string(output)); +} + +TEST(MimeTests, SimpleDecode) { + char output[256]; + memset(output, 0, sizeof(output)); + int len; + + len = mime_decode_from_qp("abc", NULL, 0); + ASSERT_EQ(len, 4); + len = mime_decode_from_qp("abc", output, 4); + ASSERT_EQ(len, 4); + ASSERT_EQ(string("abc"), string(output)); + + len = mime_decode_from_qp("a=3Db", NULL, 0); + ASSERT_EQ(len, 4); + len = mime_decode_from_qp("a=3Db", output, 4); + ASSERT_EQ(len, 4); + ASSERT_EQ(string("a=b"), string(output)); + + len = mime_decode_from_qp("Libert=C3=A9", NULL, 0); + ASSERT_EQ(len, 9); + len = mime_decode_from_qp("Libert=C3=A9", output, 9); + ASSERT_EQ(len, 9); + ASSERT_EQ(string("Libert\xc3\xa9"), string(output)); +} + +TEST(MimeTests, LowercaseDecode) { + char output[256]; + memset(output, 0, sizeof(output)); + int len; + + len = mime_decode_from_qp("Libert=c3=a9", NULL, 0); + ASSERT_EQ(len, 9); + len = mime_decode_from_qp("Libert=c3=a9", output, 9); + ASSERT_EQ(len, 9); + ASSERT_EQ(string("Libert\xc3\xa9"), string(output)); +} + +TEST(MimeTests, DecodeOutOfSpace) { + char output[256]; + memset(output, 0, sizeof(output)); + int len; + + len = mime_decode_from_qp("abcdefg", NULL, 0); + ASSERT_EQ(len, 8); + len = mime_decode_from_qp("abcdefg", output, 4); + ASSERT_EQ(len, 8); + ASSERT_EQ(string("abc"), string(output)); + len = mime_decode_from_qp("abcdefg", output, 1); + ASSERT_EQ(len, 8); + ASSERT_EQ(string(""), string(output)); + + len = mime_decode_from_qp("a=3Db", output, 2); + ASSERT_EQ(len, 4); + ASSERT_EQ(string("a"), string(output)); + len = mime_decode_from_qp("a=3Db", output, 3); + ASSERT_EQ(len, 4); + ASSERT_EQ(string("a="), string(output)); +} -- 2.39.5