summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/mime/src/mimeobj.cpp
blob: 7ad7e290d123fac4d4e15de4021a9fb162aa434e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
/* -*- 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