aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/rfc822/MCAttachment.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/rfc822/MCAttachment.cpp')
-rw-r--r--src/core/rfc822/MCAttachment.cpp607
1 files changed, 607 insertions, 0 deletions
diff --git a/src/core/rfc822/MCAttachment.cpp b/src/core/rfc822/MCAttachment.cpp
new file mode 100644
index 00000000..cbc66a39
--- /dev/null
+++ b/src/core/rfc822/MCAttachment.cpp
@@ -0,0 +1,607 @@
+#include "MCWin32.h" // should be included first.
+
+#include "MCAttachment.h"
+
+#include "MCMultipart.h"
+#include "MCMessagePart.h"
+#include "MCMessageHeader.h"
+#include "MCMessageConstants.h"
+#include "MCLog.h"
+#include "MCZip.h"
+
+#include <stdlib.h>
+#include <string.h>
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <libetpan/libetpan.h>
+
+using namespace mailcore;
+
+static char * findBlank(const char * str)
+{
+ char * p = (char *) str;
+ while (!((* p == ' ') || (* p == '\t'))) {
+ if (* p == 0)
+ return NULL;
+ p ++;
+ }
+ return p;
+}
+
+HashMap * Attachment::readMimeTypesFile(String * filename)
+{
+ HashMap * result = HashMap::hashMap();
+
+ char line[512];
+ FILE * f = fopen(filename->fileSystemRepresentation(), "r");
+ if (f == NULL) {
+ return result;
+ }
+
+ while (fgets(line, sizeof(line), f)) {
+ char * p;
+ String * mimeType;
+
+ if (line[0] == '#') {
+ continue;
+ }
+
+ while ((p = strchr(line, '\r')) != NULL) {
+ * p = 0;
+ }
+ while ((p = strchr(line, '\n')) != NULL) {
+ * p = 0;
+ }
+
+ p = findBlank(line);
+ if (p == NULL) {
+ continue;
+ }
+
+ * p = 0;
+ p ++;
+ mimeType = String::stringWithUTF8Characters(line);
+
+ while (1) {
+ while ((* p == ' ') || (* p == '\t')) {
+ p ++;
+ }
+
+ char * ext_end = findBlank(p);
+ if (ext_end == NULL) {
+ String * ext = String::stringWithUTF8Characters(p);
+ result->setObjectForKey(ext, mimeType);
+ break;
+ }
+ else {
+ * ext_end = 0;
+ String * ext = String::stringWithUTF8Characters(p);
+ result->setObjectForKey(ext, mimeType);
+ p = ext_end + 1;
+ }
+ }
+ }
+
+ fclose(f);
+
+ return result;
+}
+
+String * Attachment::mimeTypeForFilename(String * filename)
+{
+ if (filename == NULL) {
+ return NULL;
+ }
+ static HashMap * mimeTypes = NULL;
+ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+ pthread_mutex_lock(&lock);
+ if (mimeTypes == NULL) {
+ mimeTypes = readMimeTypesFile(MCSTR("/etc/apache2/mime.types"));
+ mimeTypes->retain();
+ }
+ pthread_mutex_unlock(&lock);
+
+ String * ext;
+ String * result;
+
+ ext = filename->pathExtension()->lowercaseString();
+ result = (String *) mimeTypes->objectForKey(ext);
+ if (result != NULL)
+ return result;
+
+ if (ext->isEqual(MCSTR("jpg"))) {
+ return MCSTR("image/jpeg");
+ }
+ else if (ext->isEqual(MCSTR("jpeg"))) {
+ return MCSTR("image/jpeg");
+ }
+ else if (ext->isEqual(MCSTR("png"))) {
+ return MCSTR("image/png");
+ }
+ else if (ext->isEqual(MCSTR("gif"))) {
+ return MCSTR("image/gif");
+ }
+ else if (ext->isEqual(MCSTR("html"))) {
+ return MCSTR("text/html");
+ }
+ else if (ext->isEqual(MCSTR("txt"))) {
+ return MCSTR("text/plain");
+ }
+ return NULL;
+}
+
+Attachment * Attachment::attachmentWithContentsOfFile(String * filename)
+{
+ if (filename == NULL) {
+ return attachmentWithData(NULL, Data::data());
+ }
+
+ const char * cPath = filename->fileSystemRepresentation();
+ struct stat statinfo;
+ int r;
+
+ r = stat(cPath, &statinfo);
+ if (r < 0) {
+ return attachmentWithData(filename, Data::data());
+ }
+
+ if (S_ISDIR(statinfo.st_mode)) {
+ String * zipFilename = CreateTemporaryZipFileFromFolder(filename);
+ if (zipFilename == NULL) {
+ return NULL;
+ }
+ Attachment * result = attachmentWithContentsOfFile(zipFilename);
+ RemoveTemporaryZipFile(zipFilename);
+ return result;
+ }
+ else {
+ Data * data = Data::dataWithContentsOfFile(filename);
+ return attachmentWithData(filename, data);
+ }
+}
+
+Attachment * Attachment::attachmentWithData(String * filename, Data * data)
+{
+ Attachment * attachment;
+ String * mimeType;
+
+ attachment = new Attachment();
+ mimeType = Attachment::mimeTypeForFilename(filename);
+ if (mimeType != NULL) {
+ attachment->setMimeType(mimeType);
+ }
+ if (filename != NULL) {
+ attachment->setFilename(filename->lastPathComponent());
+ }
+ attachment->setData(data);
+
+ return (Attachment *) attachment->autorelease();
+}
+
+Attachment * Attachment::attachmentWithHTMLString(String * htmlString)
+{
+ Data * data;
+ Attachment * attachment;
+
+ attachment = new Attachment();
+ attachment->setInlineAttachment(true);
+ attachment->setMimeType(MCSTR("text/html"));
+ data = htmlString->dataUsingEncoding("utf-8");
+ attachment->setData(data);
+
+ return (Attachment *) attachment->autorelease();
+}
+
+Attachment * Attachment::attachmentWithRFC822Message(Data * messageData)
+{
+ Attachment * attachment;
+
+ attachment = new Attachment();
+ attachment->setMimeType(MCSTR("message/rfc822"));
+ attachment->setData(messageData);
+
+ return (Attachment *) attachment->autorelease();
+}
+
+Attachment * Attachment::attachmentWithText(String * text)
+{
+ Data * data;
+ Attachment * attachment;
+
+ attachment = new Attachment();
+ attachment->setInlineAttachment(true);
+ attachment->setMimeType(MCSTR("text/plain"));
+ data = text->dataUsingEncoding("utf-8");
+ attachment->setData(data);
+
+ return (Attachment *) attachment->autorelease();
+}
+
+void Attachment::init()
+{
+ mData = NULL;
+ setMimeType(MCSTR("application/octet-stream"));
+}
+
+Attachment::Attachment()
+{
+ init();
+}
+
+Attachment::Attachment(Attachment * other) : AbstractPart(other)
+{
+ init();
+ MC_SAFE_REPLACE_RETAIN(Data, mData, other->mData);
+}
+
+Attachment::~Attachment()
+{
+ MC_SAFE_RELEASE(mData);
+}
+
+String * Attachment::description()
+{
+ String * result = String::string();
+ result->appendUTF8Format("<%s:%p\n", className()->UTF8Characters(), this);
+ if (filename() != NULL) {
+ result->appendUTF8Format("filename: %s\n", filename()->UTF8Characters());
+ }
+ if (mimeType() != NULL) {
+ result->appendUTF8Format("mime type: %s\n", mimeType()->UTF8Characters());
+ }
+ if (charset() != NULL) {
+ result->appendUTF8Format("charset: %s\n", charset()->UTF8Characters());
+ }
+ if (contentID() != NULL) {
+ result->appendUTF8Format("content-ID: %s\n", contentID()->UTF8Characters());
+ }
+ if (contentLocation() != NULL) {
+ result->appendUTF8Format("content-location: %s\n", contentLocation()->UTF8Characters());
+ }
+ result->appendUTF8Format("inline: %i\n", isInlineAttachment());
+ if (mData != NULL) {
+ result->appendUTF8Format("data: %i bytes\n", mData->length());
+ }
+ else {
+ result->appendUTF8Format("no data\n");
+ }
+ result->appendUTF8Format(">");
+
+ return result;
+}
+
+Object * Attachment::copy()
+{
+ return new Attachment(this);
+}
+
+void Attachment::setData(Data * data)
+{
+ MC_SAFE_REPLACE_RETAIN(Data, mData, data);
+}
+
+Data * Attachment::data()
+{
+ return mData;
+}
+
+String * Attachment::decodedString()
+{
+ if (mData) {
+ return decodedStringForData(mData);
+ }
+ else {
+ return NULL;
+ }
+}
+
+AbstractPart * Attachment::attachmentsWithMIME(struct mailmime * mime)
+{
+ return attachmentsWithMIMEWithMain(mime, true);
+}
+
+void Attachment::fillMultipartSubAttachments(AbstractMultipart * multipart, struct mailmime * mime)
+{
+ switch (mime->mm_type) {
+ case MAILMIME_MULTIPLE:
+ {
+ clistiter * cur;
+ Array * subAttachments = Array::array();
+ for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ; cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime * submime;
+ AbstractPart * subAttachment;
+
+ submime = (struct mailmime *) clist_content(cur);
+ subAttachment = attachmentsWithMIMEWithMain(submime, false);
+ subAttachments->addObject(subAttachment);
+ }
+
+ multipart->setParts(subAttachments);
+ break;
+ }
+ }
+}
+
+AbstractPart * Attachment::attachmentsWithMIMEWithMain(struct mailmime * mime, bool isMain)
+{
+ switch (mime->mm_type) {
+ case MAILMIME_SINGLE:
+ {
+ Attachment * attachment;
+ attachment = attachmentWithSingleMIME(mime);
+ return attachment;
+ }
+ case MAILMIME_MULTIPLE:
+ {
+ if ((mime->mm_content_type != NULL) && (mime->mm_content_type->ct_subtype != NULL) &&
+ (strcasecmp(mime->mm_content_type->ct_subtype, "alternative") == 0)) {
+ Multipart * attachment;
+ attachment = new Multipart();
+ attachment->setPartType(PartTypeMultipartAlternative);
+ fillMultipartSubAttachments(attachment, mime);
+ return (Multipart *) attachment->autorelease();
+ }
+ else if ((mime->mm_content_type != NULL) && (mime->mm_content_type->ct_subtype != NULL) &&
+ (strcasecmp(mime->mm_content_type->ct_subtype, "related") == 0)) {
+ Multipart * attachment;
+ attachment = new Multipart();
+ attachment->setPartType(PartTypeMultipartRelated);
+ fillMultipartSubAttachments(attachment, mime);
+ return (Multipart *) attachment->autorelease();
+ }
+ else if ((mime->mm_content_type != NULL) && (mime->mm_content_type->ct_subtype != NULL) &&
+ (strcasecmp(mime->mm_content_type->ct_subtype, "signed") == 0)) {
+ Multipart * attachment;
+ attachment = new Multipart();
+ attachment->setPartType(PartTypeMultipartSigned);
+ fillMultipartSubAttachments(attachment, mime);
+ return (Multipart *) attachment->autorelease();
+ }
+ else {
+ Multipart * attachment;
+ attachment = new Multipart();
+ fillMultipartSubAttachments(attachment, mime);
+ return (Multipart *) attachment->autorelease();
+ }
+ }
+ case MAILMIME_MESSAGE:
+ {
+ if (isMain) {
+ AbstractPart * attachment;
+ attachment = attachmentsWithMIMEWithMain(mime->mm_data.mm_message.mm_msg_mime, false);
+ return attachment;
+ }
+ else {
+ MessagePart * messagePart;
+ messagePart = attachmentWithMessageMIME(mime);
+ return messagePart;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+Encoding Attachment::encodingForMIMEEncoding(struct mailmime_mechanism * mechanism, int defaultMimeEncoding)
+{
+ Encoding mimeEncoding = (Encoding) defaultMimeEncoding;
+
+ if (mechanism != NULL) {
+ mimeEncoding = (Encoding) mechanism->enc_type;
+ }
+
+ switch ((int) mimeEncoding) {
+ default:
+ case MAILMIME_MECHANISM_ERROR:
+ return EncodingOther;
+ case MAILMIME_MECHANISM_7BIT:
+ return Encoding7Bit;
+ case MAILMIME_MECHANISM_8BIT:
+ return Encoding8Bit;
+ case MAILMIME_MECHANISM_BINARY:
+ return EncodingBinary;
+ case MAILMIME_MECHANISM_QUOTED_PRINTABLE:
+ return EncodingQuotedPrintable;
+ case MAILMIME_MECHANISM_BASE64:
+ return EncodingBase64;
+ case MAILMIME_MECHANISM_TOKEN:
+ if (mechanism == NULL)
+ return Encoding8Bit;
+ if (mechanism->enc_token == NULL)
+ return Encoding8Bit;
+
+ if (strcasecmp(mechanism->enc_token, "x-uuencode") == 0) {
+ return EncodingUUEncode;
+ }
+ else {
+ return EncodingOther;
+ }
+ }
+}
+
+static const char * get_discrete_type(struct mailmime_discrete_type * discrete_type)
+{
+ switch (discrete_type->dt_type) {
+ case MAILMIME_DISCRETE_TYPE_TEXT:
+ return "text";
+
+ case MAILMIME_DISCRETE_TYPE_IMAGE:
+ return "image";
+
+ case MAILMIME_DISCRETE_TYPE_AUDIO:
+ return "audio";
+
+ case MAILMIME_DISCRETE_TYPE_VIDEO:
+ return "video";
+
+ case MAILMIME_DISCRETE_TYPE_APPLICATION:
+ return "application";
+
+ case MAILMIME_DISCRETE_TYPE_EXTENSION:
+ return discrete_type->dt_extension;
+ }
+
+ return NULL;
+}
+
+static const char *
+get_composite_type(struct mailmime_composite_type * composite_type)
+{
+ switch (composite_type->ct_type) {
+ case MAILMIME_COMPOSITE_TYPE_MESSAGE:
+ return "message";
+
+ case MAILMIME_COMPOSITE_TYPE_MULTIPART:
+ return "multipart";
+
+ case MAILMIME_COMPOSITE_TYPE_EXTENSION:
+ return composite_type->ct_token;
+ }
+
+ return NULL;
+}
+
+static char * get_content_type_str(struct mailmime_content * content)
+{
+ const char * str;
+ char * result;
+ const char * subtype;
+
+ if (content == NULL) {
+ return strdup("unknown/unknown");
+ }
+
+ str = "unknown";
+
+ switch (content->ct_type->tp_type) {
+ case MAILMIME_TYPE_DISCRETE_TYPE:
+ str = get_discrete_type(content->ct_type->tp_data.tp_discrete_type);
+ break;
+
+ case MAILMIME_TYPE_COMPOSITE_TYPE:
+ str = get_composite_type(content->ct_type->tp_data.tp_composite_type);
+ break;
+ }
+
+ if (str == NULL)
+ str = "unknown";
+ subtype = content->ct_subtype;
+ if (subtype == NULL)
+ subtype = "unknown";
+
+ size_t len = strlen(str) + strlen(subtype) + 2;
+ result = (char *) malloc(len);
+#ifndef _MSC_VER
+ strcpy(result, str);
+ strcat(result, "/");
+ strcat(result, subtype);
+#else
+ strcpy_s(result, len, str);
+ strcat_s(result, len, "/");
+ strcat_s(result, len, subtype);
+#endif
+
+ return result;
+}
+
+Attachment * Attachment::attachmentWithSingleMIME(struct mailmime * mime)
+{
+ struct mailmime_data * data;
+ const char * bytes;
+ size_t length;
+ Attachment * result;
+ struct mailmime_single_fields single_fields;
+ char * str;
+ char * name;
+ char * filename;
+ char * content_id;
+ char * description;
+ char * loc;
+ Encoding encoding;
+ clist * ct_parameters;
+
+ MCAssert(mime->mm_type == MAILMIME_SINGLE);
+
+ result = new Attachment();
+ result->setUniqueID(mailcore::String::uuidString());
+
+ data = mime->mm_data.mm_single;
+ bytes = data->dt_data.dt_text.dt_data;
+ length = data->dt_data.dt_text.dt_length;
+
+ mailmime_single_fields_init(&single_fields, mime->mm_mime_fields, mime->mm_content_type);
+
+ encoding = encodingForMIMEEncoding(single_fields.fld_encoding, data->dt_encoding);
+
+ Data * mimeData;
+ mimeData = Data::dataWithBytes(bytes, (unsigned int) length);
+ mimeData = mimeData->decodedDataUsingEncoding(encoding);
+ result->setData(mimeData);
+
+ str = get_content_type_str(mime->mm_content_type);
+ result->setMimeType(String::stringWithUTF8Characters(str));
+ free(str);
+
+ name = single_fields.fld_content_name;
+ filename = single_fields.fld_disposition_filename;
+ content_id = single_fields.fld_id;
+ description = single_fields.fld_description;
+ loc = single_fields.fld_location;
+ ct_parameters = single_fields.fld_content->ct_parameters;
+
+ if (filename != NULL) {
+ result->setFilename(String::stringByDecodingMIMEHeaderValue(filename));
+ }
+ else if (name != NULL) {
+ result->setFilename(String::stringByDecodingMIMEHeaderValue(name));
+ }
+ if (content_id != NULL) {
+ result->setContentID(String::stringWithUTF8Characters(content_id));
+ }
+ if (description != NULL) {
+ result->setContentDescription(String::stringWithUTF8Characters(description));
+ }
+ if (single_fields.fld_content_charset != NULL) {
+ result->setCharset(String::stringByDecodingMIMEHeaderValue(single_fields.fld_content_charset));
+ }
+ if (loc != NULL) {
+ result->setContentLocation(String::stringWithUTF8Characters(loc));
+ }
+
+ if (ct_parameters != NULL) {
+ clistiter * iter = clist_begin(ct_parameters);
+ struct mailmime_parameter * param;
+ while (iter != NULL) {
+ param = (struct mailmime_parameter *) clist_content(iter);
+ if (param != NULL) {
+ result->setContentTypeParameter(String::stringWithUTF8Characters(param->pa_name), String::stringWithUTF8Characters(param->pa_value));
+ }
+ iter = clist_next(iter);
+ }
+ }
+
+ if (single_fields.fld_disposition != NULL) {
+ if (single_fields.fld_disposition->dsp_type != NULL) {
+ if (single_fields.fld_disposition->dsp_type->dsp_type == MAILMIME_DISPOSITION_TYPE_INLINE) {
+ result->setInlineAttachment(true);
+ }
+ }
+ }
+
+ return (Attachment *) result->autorelease();
+}
+
+MessagePart * Attachment::attachmentWithMessageMIME(struct mailmime * mime)
+{
+ MessagePart * attachment;
+ AbstractPart * mainPart;
+
+ attachment = new MessagePart();
+ attachment->header()->importIMFFields(mime->mm_data.mm_message.mm_fields);
+ mainPart = attachmentsWithMIMEWithMain(mime->mm_data.mm_message.mm_msg_mime, false);
+ attachment->setMainPart(mainPart);
+
+ return (MessagePart *) attachment->autorelease();
+}