/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * This Original Code has been modified by IBM Corporation. Modifications made * by IBM described herein are Copyright (c) International Business Machines * Corporation, 2000. Modifications to Mozilla code or documentation identified * per MPL Section 3.3 * * Date Modified by Description of modification * 04/20/2000 IBM Corp. OS/2 VisualAge build. */ #include "mimeobj.h" #include "prmem.h" #include "plstr.h" #include "prio.h" #include "mimebuf.h" #include "prlog.h" #include "nsMimeTypes.h" #include "nsMimeStringResources.h" #include "nsMsgUtils.h" #include "mimemsg.h" #include "mimemapl.h" /* Way to destroy any notions of modularity or class hierarchy, Terry! */ #include "mimetpla.h" #include "mimethtm.h" #include "mimecont.h" MimeDefClass(MimeObject, MimeObjectClass, mimeObjectClass, NULL); static int MimeObject_initialize(MimeObject*); static void MimeObject_finalize(MimeObject*); static int MimeObject_parse_begin(MimeObject*); static int MimeObject_parse_buffer(const char*, int32_t, MimeObject*); static int MimeObject_parse_line(const char*, int32_t, MimeObject*); static int MimeObject_parse_eof(MimeObject*, bool); static int MimeObject_parse_end(MimeObject*, bool); static bool MimeObject_displayable_inline_p(MimeObjectClass* clazz, MimeHeaders* hdrs); #if defined(DEBUG) && defined(XP_UNIX) static int MimeObject_debug_print(MimeObject*, PRFileDesc*, int32_t depth); #endif static int MimeObjectClassInitialize(MimeObjectClass* clazz) { NS_ASSERTION(!clazz->class_initialized, "class shouldn't already be initialized"); clazz->initialize = MimeObject_initialize; clazz->finalize = MimeObject_finalize; clazz->parse_begin = MimeObject_parse_begin; clazz->parse_buffer = MimeObject_parse_buffer; clazz->parse_line = MimeObject_parse_line; clazz->parse_eof = MimeObject_parse_eof; clazz->parse_end = MimeObject_parse_end; clazz->displayable_inline_p = MimeObject_displayable_inline_p; #if defined(DEBUG) && defined(XP_UNIX) clazz->debug_print = MimeObject_debug_print; #endif return 0; } static int MimeObject_initialize(MimeObject* obj) { /* This is an abstract class; it shouldn't be directly instantiated. */ NS_ASSERTION(obj->clazz != &mimeObjectClass, "should directly instantiate abstract class"); /* Set up the content-type and encoding. */ if (!obj->content_type && obj->headers) obj->content_type = MimeHeaders_get(obj->headers, HEADER_CONTENT_TYPE, true, false); if (!obj->encoding && obj->headers) obj->encoding = MimeHeaders_get( obj->headers, HEADER_CONTENT_TRANSFER_ENCODING, true, false); /* Special case to normalize some types and encodings to a canonical form. (These are nonstandard types/encodings which have been seen to appear in multiple forms; we normalize them so that things like looking up icons and extensions has consistent behavior for the receiver, regardless of the "alias" type that the sender used.) */ if (!obj->content_type || !*(obj->content_type)) ; else if (!PL_strcasecmp(obj->content_type, APPLICATION_UUENCODE2) || !PL_strcasecmp(obj->content_type, APPLICATION_UUENCODE3) || !PL_strcasecmp(obj->content_type, APPLICATION_UUENCODE4)) { PR_Free(obj->content_type); obj->content_type = strdup(APPLICATION_UUENCODE); } else if (!PL_strcasecmp(obj->content_type, IMAGE_XBM2) || !PL_strcasecmp(obj->content_type, IMAGE_XBM3)) { PR_Free(obj->content_type); obj->content_type = strdup(IMAGE_XBM); } else { // MIME-types are case-insenitive, but let's make it lower case internally // to avoid some hassle later down the road. nsAutoCString lowerCaseContentType; ToLowerCase(nsDependentCString(obj->content_type), lowerCaseContentType); PR_Free(obj->content_type); obj->content_type = ToNewCString(lowerCaseContentType); } if (!obj->encoding) ; else if (!PL_strcasecmp(obj->encoding, ENCODING_UUENCODE2) || !PL_strcasecmp(obj->encoding, ENCODING_UUENCODE3) || !PL_strcasecmp(obj->encoding, ENCODING_UUENCODE4)) { PR_Free(obj->encoding); obj->encoding = strdup(ENCODING_UUENCODE); } else if (!PL_strcasecmp(obj->encoding, ENCODING_COMPRESS2)) { PR_Free(obj->encoding); obj->encoding = strdup(ENCODING_COMPRESS); } else if (!PL_strcasecmp(obj->encoding, ENCODING_GZIP2)) { PR_Free(obj->encoding); obj->encoding = strdup(ENCODING_GZIP); } return 0; } static void MimeObject_finalize(MimeObject* obj) { obj->clazz->parse_eof(obj, false); obj->clazz->parse_end(obj, false); if (obj->headers) { MimeHeaders_free(obj->headers); obj->headers = 0; } /* Should have been freed by parse_eof, but just in case... */ NS_ASSERTION(!obj->ibuffer, "buffer not freed"); NS_ASSERTION(!obj->obuffer, "buffer not freed"); PR_FREEIF(obj->ibuffer); PR_FREEIF(obj->obuffer); PR_FREEIF(obj->content_type); PR_FREEIF(obj->encoding); if (obj->options && obj->options->state) { delete obj->options->state; obj->options->state = nullptr; } } static int MimeObject_parse_begin(MimeObject* obj) { NS_ASSERTION(!obj->closed_p, "object shouldn't be already closed"); /* If we haven't set up the state object yet, then this should be the outermost object... */ if (obj->options && !obj->options->state) { NS_ASSERTION( !obj->headers, "headers should be null"); /* should be the outermost object. */ obj->options->state = new MimeParseStateObject; if (!obj->options->state) return MIME_OUT_OF_MEMORY; obj->options->state->root = obj; obj->options->state->separator_suppressed_p = true; /* no first sep */ const char* delParts = PL_strcasestr(obj->options->url, "&del="); const char* detachLocations = PL_strcasestr(obj->options->url, "&detachTo="); if (delParts) { const char* delEnd = PL_strcasestr(delParts + 1, "&"); if (!delEnd) delEnd = delParts + strlen(delParts); ParseString(Substring(delParts + 5, delEnd), ',', obj->options->state->partsToStrip); } if (detachLocations) { detachLocations += 10; // advance past "&detachTo=" ParseString(nsDependentCString(detachLocations), ',', obj->options->state->detachToFiles); } } /* Decide whether this object should be output or not... */ if (!obj->options || obj->options->no_output_p || !obj->options->output_fn /* if we are decomposing the message in files and processing a multipart object, we must not output it without parsing it first */ || (obj->options->decompose_file_p && obj->options->decompose_file_output_fn && mime_typep(obj, (MimeObjectClass*)&mimeMultipartClass))) obj->output_p = false; else if (!obj->options->part_to_load) obj->output_p = true; else { char* id = mime_part_address(obj); if (!id) return MIME_OUT_OF_MEMORY; // We need to check if a part is the subpart of the part to load. // If so and this is a raw or body display output operation, then // we should mark the part for subsequent output. // First, check for an exact match obj->output_p = !strcmp(id, obj->options->part_to_load); if (!obj->output_p && (obj->options->format_out == nsMimeOutput::nsMimeMessageRaw || obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay || obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)) { // Then, check for subpart unsigned int partlen = strlen(obj->options->part_to_load); obj->output_p = (strlen(id) >= partlen + 2) && (id[partlen] == '.') && !strncmp(id, obj->options->part_to_load, partlen); } PR_Free(id); } // If we've decided not to output this part, we also shouldn't be showing it // as an attachment. obj->dontShowAsAttachment = !obj->output_p; return 0; } static int MimeObject_parse_buffer(const char* buffer, int32_t size, MimeObject* obj) { NS_ASSERTION(!obj->closed_p, "object shouldn't be closed"); if (obj->closed_p) return -1; return mime_LineBuffer(buffer, size, &obj->ibuffer, &obj->ibuffer_size, &obj->ibuffer_fp, true, ((int (*)(char*, int32_t, void*)) /* This cast is to turn void into MimeObject */ obj->clazz->parse_line), obj); } static int MimeObject_parse_line(const char* line, int32_t length, MimeObject* obj) { NS_ERROR("shouldn't call this method"); return -1; } static int MimeObject_parse_eof(MimeObject* obj, bool abort_p) { if (abort_p) { obj->closed_p = true; return 0; } if (obj->closed_p) return 0; NS_ASSERTION(!obj->parsed_p, "obj already parsed"); /* If there is still data in the ibuffer, that means that the last line of this part didn't end in a newline; so push it out anyway (this means that the parse_line method will be called with a string with no trailing newline, which isn't the usual case.) */ if (obj->ibuffer_fp > 0) { int status = obj->clazz->parse_line(obj->ibuffer, obj->ibuffer_fp, obj); obj->ibuffer_fp = 0; if (status < 0) { obj->closed_p = true; return status; } } obj->closed_p = true; return 0; } static int MimeObject_parse_end(MimeObject* obj, bool abort_p) { if (obj->parsed_p) { NS_ASSERTION(obj->closed_p, "object should be closed"); return 0; } /* We won't be needing these buffers any more; nuke 'em. */ PR_FREEIF(obj->ibuffer); obj->ibuffer_fp = 0; obj->ibuffer_size = 0; PR_FREEIF(obj->obuffer); obj->obuffer_fp = 0; obj->obuffer_size = 0; obj->parsed_p = true; return 0; } static bool MimeObject_displayable_inline_p(MimeObjectClass* clazz, MimeHeaders* hdrs) { NS_ERROR("shouldn't call this method"); return false; } #if defined(DEBUG) && defined(XP_UNIX) static int MimeObject_debug_print(MimeObject* obj, PRFileDesc* stream, int32_t depth) { int i; char* addr = mime_part_address(obj); for (i = 0; i < depth; i++) PR_Write(stream, " ", 2); /* fprintf(stream, "<%s %s 0x%08X>\n", obj->clazz->class_name, addr ? addr : "???", (uint32_t) obj); */ PR_FREEIF(addr); return 0; } #endif