static int MimeMultipart_parse_line()

in mailnews/mime/src/mimemult.cpp [110:310]


static int MimeMultipart_parse_line(const char* line, int32_t length,
                                    MimeObject* obj) {
  MimeMultipart* mult = (MimeMultipart*)obj;
  MimeContainer* container = (MimeContainer*)obj;
  int status = 0;
  MimeMultipartBoundaryType boundary;

  NS_ASSERTION(line && *line, "empty line in multipart parse_line");
  if (!line || !*line) return -1;

  NS_ASSERTION(!obj->closed_p, "obj shouldn't already be closed");
  if (obj->closed_p) return -1;

  /* If we're supposed to write this object, but aren't supposed to convert
     it to HTML, simply pass it through unaltered. */
  if (obj->output_p && obj->options && !obj->options->write_html_p &&
      obj->options->output_fn &&
      obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
    return MimeObject_write(obj, line, length, true);

  if (mult->state == MimeMultipartEpilogue) /* already done */
    boundary = MimeMultipartBoundaryTypeNone;
  else
    boundary =
        ((MimeMultipartClass*)obj->clazz)->check_boundary(obj, line, length);

  if (boundary == MimeMultipartBoundaryTypeTerminator ||
      boundary == MimeMultipartBoundaryTypeSeparator) {
    /* Match!  Close the currently-open part, move on to the next
       state, and discard this line.
     */
    bool endOfPart = (mult->state != MimeMultipartPreamble);
    if (endOfPart) status = ((MimeMultipartClass*)obj->clazz)->close_child(obj);
    if (status < 0) return status;

    if (boundary == MimeMultipartBoundaryTypeTerminator)
      mult->state = MimeMultipartEpilogue;
    else {
      mult->state = MimeMultipartHeaders;

      /* Reset the header parser for this upcoming part. */
      NS_ASSERTION(!mult->hdrs, "mult->hdrs should be null here");
      if (mult->hdrs) MimeHeaders_free(mult->hdrs);
      mult->hdrs = MimeHeaders_new();
      if (!mult->hdrs) return MIME_OUT_OF_MEMORY;
      if (obj->options && obj->options->state &&
          obj->options->state->partsToStrip.Length() > 0) {
        nsAutoCString newPart(mime_part_address(obj));
        newPart.Append('.');
        newPart.AppendInt(container->nchildren + 1);
        obj->options->state->strippingPart = false;
        // check if this is a sub-part of a part we're stripping.
        for (uint32_t partIndex = 0;
             partIndex < obj->options->state->partsToStrip.Length();
             partIndex++) {
          nsCString& curPartToStrip =
              obj->options->state->partsToStrip[partIndex];
          if (newPart.Find(curPartToStrip) == 0 &&
              (newPart.Length() == curPartToStrip.Length() ||
               newPart.CharAt(curPartToStrip.Length()) == '.')) {
            obj->options->state->strippingPart = true;
            if (partIndex < obj->options->state->detachToFiles.Length())
              obj->options->state->detachedFilePath =
                  obj->options->state->detachToFiles[partIndex];
            break;
          }
        }
      }
    }

    // if stripping out attachments, write the boundary line. Otherwise, return
    // to ignore it.
    if (obj->options &&
        obj->options->format_out == nsMimeOutput::nsMimeMessageAttach) {
      // Because MimeMultipart_parse_child_line strips out the
      // the CRLF of the last line before the end of a part, we need to add that
      // back in here.
      if (endOfPart) MimeWriteAString(obj, nsLiteralCString(MSG_LINEBREAK));

      status = MimeObject_write(obj, line, length, true);
    }
    return 0;
  }

  /* Otherwise, this isn't a boundary string.  So do whatever it is we
   should do with this line (parse it as a header, feed it to the
   child part, ignore it, etc.) */

  switch (mult->state) {
    case MimeMultipartPreamble:
    case MimeMultipartEpilogue:
      /* Ignore this line. */
      break;

    case MimeMultipartHeaders:
      /* Parse this line as a header for the sub-part. */
      {
        status = MimeHeaders_parse_line(line, length, mult->hdrs);
        bool stripping = false;

        if (status < 0) return status;

        // If this line is blank, we're now done parsing headers, and should
        // now examine the content-type to create this "body" part.
        //
        if (*line == '\r' || *line == '\n') {
          if (obj->options && obj->options->state &&
              obj->options->state->strippingPart) {
            stripping = true;
            bool detachingPart =
                obj->options->state->detachedFilePath.Length() > 0;

            nsAutoCString fileName;
            fileName.Adopt(MimeHeaders_get_name(mult->hdrs, obj->options));
            // clang-format off
            if (detachingPart) {
              char *contentType =
                  MimeHeaders_get(mult->hdrs, "Content-Type", false, false);
              if (contentType) {
                MimeWriteAString(obj, "Content-Type: "_ns);
                MimeWriteAString(obj, nsDependentCString(contentType));
                PR_Free(contentType);
              }
              MimeWriteAString(obj, nsLiteralCString(MSG_LINEBREAK));
              MimeWriteAString(obj, "Content-Disposition: attachment; filename=\""_ns);
              MimeWriteAString(obj, fileName);
              MimeWriteAString(obj, "\""_ns MSG_LINEBREAK);
              MimeWriteAString(obj, "X-Mozilla-External-Attachment-URL: "_ns);
              MimeWriteAString(obj, obj->options->state->detachedFilePath);
              MimeWriteAString(obj, nsLiteralCString(MSG_LINEBREAK));
              MimeWriteAString(obj, "X-Mozilla-Altered: AttachmentDetached; date=\""_ns);
            } else {
              nsAutoCString header("Content-Type: text/x-moz-deleted; name=\"Deleted: ");
              header.Append(fileName);
              MimeWriteAString(obj, header);
              MimeWriteAString(obj, "\""_ns MSG_LINEBREAK
                                    "Content-Transfer-Encoding: 8bit"_ns MSG_LINEBREAK);
              MimeWriteAString(obj, "Content-Disposition: inline; filename=\"Deleted: "_ns);
              MimeWriteAString(obj, fileName);
              MimeWriteAString(obj, "\""_ns MSG_LINEBREAK
                                    "X-Mozilla-Altered: AttachmentDeleted; date=\""_ns);
            }
            nsCString result;
            char timeBuffer[128];
            PRExplodedTime now;
            PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
            PR_FormatTimeUSEnglish(timeBuffer, sizeof(timeBuffer),
                                   "%a %b %d %H:%M:%S %Y", &now);
            MimeWriteAString(obj, nsDependentCString(timeBuffer));
            MimeWriteAString(obj, "\""_ns MSG_LINEBREAK);
            MimeWriteAString(obj, MSG_LINEBREAK
                                      "You deleted an attachment from this message. The original "_ns
                                      "MIME headers for the attachment were:"_ns MSG_LINEBREAK);
            MimeHeaders_write_raw_headers(mult->hdrs, obj->options, false);
            // clang-format on
          }
          int32_t old_nchildren = container->nchildren;
          status = ((MimeMultipartClass*)obj->clazz)->create_child(obj);
          if (status < 0) return status;
          NS_ASSERTION(mult->state != MimeMultipartHeaders,
                       "mult->state shouldn't be MimeMultipartHeaders");

          if (!stripping && container->nchildren > old_nchildren &&
              obj->options &&
              !mime_typep(obj,
                          (MimeObjectClass*)&mimeMultipartAlternativeClass)) {
            // Notify emitter about content type and part path.
            MimeObject* kid = container->children[container->nchildren - 1];
            MimeMultipart_notify_emitter(kid);
          }
        }
        break;
      }

    case MimeMultipartPartFirstLine:
      /* Hand this line off to the sub-part. */
      status = (((MimeMultipartClass*)obj->clazz)
                    ->parse_child_line(obj, line, length, true));
      if (status < 0) return status;
      mult->state = MimeMultipartPartLine;
      break;

    case MimeMultipartPartLine:
      /* Hand this line off to the sub-part. */
      status = (((MimeMultipartClass*)obj->clazz)
                    ->parse_child_line(obj, line, length, false));
      if (status < 0) return status;
      break;

    default:
      NS_ERROR("unexpected state in parse line");
      return -1;
  }

  if (obj->options &&
      obj->options->format_out == nsMimeOutput::nsMimeMessageAttach &&
      (!(obj->options->state && obj->options->state->strippingPart) &&
       mult->state != MimeMultipartPartLine))
    return MimeObject_write(obj, line, length, false);
  return 0;
}