/* Copyright (C) 2020 C. McEnroe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "archive.h" struct Buffer { size_t cap; size_t len; char *ptr; }; static struct Buffer bufferAlloc(size_t cap) { struct Buffer buf = { .cap = cap, .len = 0, .ptr = malloc(cap), }; if (!buf.ptr) err(EX_OSERR, "malloc"); return buf; } static char *bufferDest(struct Buffer *buf, size_t len) { if (buf->len + len > buf->cap) { buf->cap *= 2; buf->ptr = realloc(buf->ptr, buf->cap); if (!buf->ptr) err(EX_OSERR, "realloc"); } char *dest = &buf->ptr[buf->len]; buf->len += len; return dest; } static void bufferCopy(struct Buffer *buf, const char *src, size_t len) { char *dst = bufferDest(buf, len); memcpy(dst, src, len); } static char *bufferString(struct Buffer *buf) { *bufferDest(buf, 1) = '\0'; return buf->ptr; } static void convertCharset( struct Buffer *dst, const char *charset, const char *src, size_t len ) { iconv_t conv = iconv_open("utf-8", charset); if (conv == (iconv_t)-1) { warn("cannot convert from %s to utf-8", charset); return; } for (size_t pad = 0; len; ++pad) { char *ptr = bufferDest(dst, len + pad); size_t cap = dst->cap - (ptr - dst->ptr); size_t n = iconv(conv, (char **)&src, &len, &ptr, &cap); if (n == (size_t)-1 && errno != E2BIG) { warn("iconv"); break; } dst->len = dst->cap - cap; } iconv_close(conv); } static void decodeQ(struct Buffer *dst, const char *src, size_t len) { while (len) { if (*src == '=') { if (len < 3) break; *bufferDest(dst, 1) = strtoul(&src[1], NULL, 16); len -= 3; src += 3; } else { *bufferDest(dst, 1) = (*src == '_' ? ' ' : *src); len--; src++; } } } static void decodeEncoding( struct Buffer *dst, const char *encoding, const char *src, size_t len ) { if (!strcasecmp(encoding, "Q")) { decodeQ(dst, src, len); } else { // TODO bufferCopy(dst, src, len); } } static void decode( struct Buffer *dst, const char *encoding, const char *charset, const char *src, size_t len ) { if ( !charset || !strcasecmp(charset, "us-ascii") || !strcasecmp(charset, "utf-8") ) { decodeEncoding(dst, encoding, src, len); } else { // TODO: Avoid copying if encoding is 8bit. struct Buffer decoded = bufferAlloc(len); decodeEncoding(&decoded, encoding, src, len); convertCharset(dst, charset, decoded.ptr, decoded.len); free(decoded.ptr); } } static void decodeWord(struct Buffer *dst, const char *src, size_t len) { struct Buffer word = bufferAlloc(len + 1); bufferCopy(&word, src, len); char *ptr = bufferString(&word); strsep(&ptr, "?"); char *charset = strsep(&ptr, "?"); char *encoding = strsep(&ptr, "?"); char *encoded = strsep(&ptr, "?"); if (charset && encoding && encoded && ptr && *ptr == '=') { decode(dst, encoding, charset, encoded, strlen(encoded)); } else { bufferCopy(dst, src, len); } free(word.ptr); } char *decodeHeader(const char *header) { struct Buffer buf = bufferAlloc(strlen(header) + 1); while (*header) { size_t len = strcspn(header, " "); if (!strncmp(header, "=?", 2)) { decodeWord(&buf, header, len); } else { if (header[len]) len++; bufferCopy(&buf, header, len); } header += len; } return bufferString(&buf); } int decodeContent( FILE *file, EscapeFn *escape, const struct BodyPart *part, const char *content ) { if (!escape) escape = escapeNull; // TODO return escape(file, content); }