message-header-decode.c revision 2a3fc652e13a574ca14ff2405b5c29a59232db49
458N/A/* Copyright (c) 2002-2007 Dovecot authors, see the included COPYING file */
458N/A
458N/A#include "lib.h"
458N/A#include "base64.h"
458N/A#include "buffer.h"
458N/A#include "unichar.h"
458N/A#include "charset-utf8.h"
458N/A#include "quoted-printable.h"
458N/A#include "message-header-decode.h"
458N/A
458N/Astatic size_t
458N/Amessage_header_decode_encoded(const unsigned char *data, size_t size,
458N/A buffer_t *decodebuf, unsigned int *charsetlen_r)
458N/A{
458N/A#define QCOUNT 3
458N/A unsigned int num = 0;
458N/A size_t i, start_pos[QCOUNT];
458N/A
458N/A /* data should contain "charset?encoding?text?=" */
458N/A for (i = 0; i < size; i++) {
5680N/A if (data[i] == '?') {
5680N/A start_pos[num++] = i;
5466N/A if (num == QCOUNT)
458N/A break;
458N/A }
458N/A }
458N/A if (i == size || data[i+1] != '=') {
458N/A /* invalid block */
458N/A return 0;
5181N/A }
618N/A
458N/A buffer_append(decodebuf, data, start_pos[0]);
2833N/A buffer_append_c(decodebuf, '\0');
844N/A *charsetlen_r = decodebuf->used;
5181N/A
618N/A switch (data[start_pos[0]+1]) {
3555N/A case 'q':
1258N/A case 'Q':
458N/A quoted_printable_decode(data + start_pos[1] + 1,
5181N/A start_pos[2] - start_pos[1] - 1,
2899N/A NULL, decodebuf);
3817N/A break;
3817N/A case 'b':
3817N/A case 'B':
3817N/A if (base64_decode(data + start_pos[1] + 1,
458N/A start_pos[2] - start_pos[1] - 1,
1567N/A NULL, decodebuf) < 0) {
1567N/A /* contains invalid data. show what we got so far. */
1567N/A }
1567N/A break;
1567N/A default:
458N/A /* unknown encoding */
4976N/A return 0;
458N/A }
969N/A
969N/A return start_pos[2] + 2;
969N/A}
969N/A
969N/Avoid message_header_decode(const unsigned char *data, size_t size,
969N/A message_header_decode_callback_t *callback,
969N/A void *context)
458N/A{
458N/A buffer_t *decodebuf = NULL;
969N/A unsigned int charsetlen = 0;
969N/A size_t pos, start_pos, ret;
458N/A
458N/A /* =?charset?Q|B?text?= */
458N/A start_pos = pos = 0;
458N/A for (pos = 0; pos + 1 < size; ) {
458N/A if (data[pos] != '=' || data[pos+1] != '?') {
458N/A pos++;
458N/A continue;
458N/A }
458N/A
458N/A /* encoded string beginning */
646N/A if (pos != start_pos) {
646N/A /* send the unencoded data so far */
646N/A if (!callback(data + start_pos, pos - start_pos,
646N/A NULL, context)) {
646N/A start_pos = size;
646N/A break;
646N/A }
1091N/A }
646N/A
646N/A if (decodebuf == NULL) {
646N/A decodebuf = buffer_create_dynamic(default_pool,
646N/A size - pos);
581N/A } else {
581N/A buffer_set_used_size(decodebuf, 0);
581N/A }
3693N/A
3693N/A pos += 2;
3693N/A ret = message_header_decode_encoded(data + pos, size - pos,
3693N/A decodebuf, &charsetlen);
3693N/A if (ret == 0) {
3693N/A start_pos = pos-2;
3693N/A continue;
458N/A }
458N/A pos += ret;
458N/A
458N/A if (decodebuf->used > charsetlen) {
458N/A /* decodebuf contains <charset> NUL <text> */
458N/A if (!callback(CONST_PTR_OFFSET(decodebuf->data,
5466N/A charsetlen),
5466N/A decodebuf->used - charsetlen,
458N/A decodebuf->data, context)) {
458N/A start_pos = size;
969N/A break;
458N/A }
646N/A }
458N/A
3693N/A start_pos = pos;
458N/A }
646N/A
458N/A if (size != start_pos) {
458N/A (void)callback(data + start_pos, size - start_pos,
458N/A NULL, context);
458N/A }
3497N/A if (decodebuf != NULL)
3497N/A buffer_free(&decodebuf);
3497N/A}
3497N/A
3497N/Astruct decode_utf8_context {
3497N/A buffer_t *dest;
3497N/A unsigned int changed:1;
3497N/A unsigned int called:1;
3497N/A unsigned int dtcase:1;
3497N/A};
3497N/A
3497N/Astatic bool
3497N/Adecode_utf8_callback(const unsigned char *data, size_t size,
3497N/A const char *charset, void *context)
3497N/A{
3693N/A struct decode_utf8_context *ctx = context;
3497N/A struct charset_translation *t;
3497N/A enum charset_flags flags;
3497N/A
3497N/A /* one call with charset=NULL means nothing changed */
3497N/A if (!ctx->called && charset == NULL)
3497N/A ctx->called = TRUE;
3497N/A else
3497N/A ctx->changed = TRUE;
3497N/A
3497N/A if (charset == NULL || charset_is_utf8(charset)) {
3497N/A /* ASCII / UTF-8 */
3872N/A if (ctx->dtcase) {
3497N/A (void)uni_utf8_to_decomposed_titlecase(data, size,
3497N/A ctx->dest);
3497N/A } else {
1567N/A buffer_append(ctx->dest, data, size);
1567N/A }
1567N/A return TRUE;
3497N/A }
3497N/A
3497N/A flags = ctx->dtcase ? CHARSET_FLAG_DECOMP_TITLECASE : 0;
3872N/A if (charset_to_utf8_begin(charset, flags, &t) < 0) {
3872N/A /* let's just ignore this part */
458N/A return TRUE;
1699N/A }
1699N/A
1567N/A /* ignore any errors */
1567N/A (void)charset_to_utf8(t, data, &size, ctx->dest);
3477N/A charset_to_utf8_end(&t);
1567N/A return TRUE;
458N/A}
458N/A
1567N/Abool message_header_decode_utf8(const unsigned char *data, size_t size,
1567N/A buffer_t *dest, bool dtcase)
1567N/A{
1567N/A struct decode_utf8_context ctx;
1567N/A size_t used = dest->used;
1567N/A
1567N/A memset(&ctx, 0, sizeof(ctx));
1567N/A ctx.dest = dest;
1567N/A ctx.dtcase = dtcase;
1567N/A message_header_decode(data, size, decode_utf8_callback, &ctx);
1567N/A return ctx.changed || (dest->used - used != size);
1567N/A}
3722N/A