static void mime_parse_stream_complete()

in mailnews/mime/src/mimedrft.cpp [1112:1662]


static void mime_parse_stream_complete(nsMIMESession* stream) {
  NS_ASSERTION(stream->data_object, "null mime data");
  if (!stream->data_object) {
    return;
  }

  mime_draft_data* mdd = stream->data_object.AsMimeDraftData();
  if (!mdd) {
    return;
  }

  nsCOMPtr<nsIMsgCompFields> fields;
  int htmlAction = 0;
  int lineWidth = 0;

  char* to_and_cc = 0;
  char* re_subject = 0;
  char* new_refs = 0;
  char* from = 0;
  char* repl = 0;
  char* subj = 0;
  char* id = 0;
  char* refs = 0;
  char* to = 0;
  char* cc = 0;
  char* bcc = 0;
  char* fcc = 0;
  char* org = 0;
  char* grps = 0;
  char* foll = 0;
  char* priority = 0;
  char* draftInfo = 0;
  char* contentLanguage = 0;
  char* identityKey = 0;
  nsTArray<nsString> readOtherHeaders;

  bool forward_inline = false;
  bool bodyAsAttachment = false;
  bool charsetOverride = false;

  if (mdd->obj) {
    int status;

    status = mdd->obj->clazz->parse_eof(mdd->obj, false);
    mdd->obj->clazz->parse_end(mdd->obj, status < 0 ? true : false);

    // RICHIE
    // We need to figure out how to pass the forwarded flag along with this
    // operation.

    // forward_inline = (mdd->format_out != FO_CMDLINE_ATTACHMENTS);
    forward_inline = mdd->forwardInline;

    NS_ASSERTION(mdd->options == mdd->obj->options,
                 "mime draft options not same as obj->options");
    mime_free(mdd->obj);
    mdd->obj = 0;
    if (mdd->options) {
      // save the override flag before it's unavailable
      charsetOverride = mdd->options->override_charset;
      // Override the charset only if requested. If the message doesn't have
      // one and we're not overriding, we'll detect it later.
      if (charsetOverride && mdd->options->default_charset) {
        PR_FREEIF(mdd->mailcharset);
        mdd->mailcharset = strdup(mdd->options->default_charset);
      }

      // mscott: aren't we leaking a bunch of strings here like the charset
      // strings and such?
      delete mdd->options;
      mdd->options = 0;
    }
    if (mdd->stream) {
      mdd->stream->complete(mdd->stream);
      PR_Free(mdd->stream);
      mdd->stream = 0;
    }
  }

  //
  // Now, process the attachments that we have gathered from the message
  // on disk
  //
  nsMsgAttachmentData* newAttachData = mime_draft_process_attachments(mdd);

  //
  // time to bring up the compose windows with all the info gathered
  //
  if (mdd->headers) {
    subj = MimeHeaders_get(mdd->headers, HEADER_SUBJECT, false, false);
    if (forward_inline) {
      if (subj) {
        nsresult rv;
        nsCOMPtr<nsIPrefBranch> prefBranch(
            do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
        if (NS_SUCCEEDED(rv)) {
          nsAutoCString fwdPrefix;
          prefBranch->GetCharPref("mail.forward_subject_prefix", fwdPrefix);
          char* newSubj = PR_smprintf(
              "%s: %s", !fwdPrefix.IsEmpty() ? fwdPrefix.get() : "Fwd", subj);
          if (newSubj) {
            PR_Free(subj);
            subj = newSubj;
          }
        }
      }
    } else {
      from = MimeHeaders_get(mdd->headers, HEADER_FROM, false, false);
      repl = MimeHeaders_get(mdd->headers, HEADER_REPLY_TO, false, false);
      to = MimeHeaders_get(mdd->headers, HEADER_TO, false, true);
      cc = MimeHeaders_get(mdd->headers, HEADER_CC, false, true);
      bcc = MimeHeaders_get(mdd->headers, HEADER_BCC, false, true);

      /* These headers should not be RFC-1522-decoded. */
      grps = MimeHeaders_get(mdd->headers, HEADER_NEWSGROUPS, false, true);
      foll = MimeHeaders_get(mdd->headers, HEADER_FOLLOWUP_TO, false, true);

      id = MimeHeaders_get(mdd->headers, HEADER_MESSAGE_ID, false, false);
      refs = MimeHeaders_get(mdd->headers, HEADER_REFERENCES, false, true);
      priority = MimeHeaders_get(mdd->headers, HEADER_X_PRIORITY, false, false);

      // Other headers via pref.
      nsCString otherHeaders;
      nsTArray<nsCString> otherHeadersArray;
      nsresult rv;
      nsCOMPtr<nsIPrefBranch> pPrefBranch(
          do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
      pPrefBranch->GetCharPref("mail.compose.other.header", otherHeaders);
      if (!otherHeaders.IsEmpty()) {
        ToLowerCase(otherHeaders);
        ParseString(otherHeaders, ',', otherHeadersArray);
        for (auto otherHeader : otherHeadersArray) {
          otherHeader.Trim(" ");
          nsAutoCString result;
          result.Assign(
              MimeHeaders_get(mdd->headers, otherHeader.get(), false, false));
          readOtherHeaders.AppendElement(NS_ConvertUTF8toUTF16(result));
        }
      }
    }

    CreateCompositionFields(from, repl, to, cc, bcc, fcc, grps, foll, org, subj,
                            refs, priority, readOtherHeaders, mdd->mailcharset,
                            getter_AddRefs(fields));

    contentLanguage =
        MimeHeaders_get(mdd->headers, HEADER_CONTENT_LANGUAGE, false, false);
    if (contentLanguage) {
      fields->SetContentLanguage(contentLanguage);
    }

    draftInfo = MimeHeaders_get(mdd->headers, HEADER_X_MOZILLA_DRAFT_INFO,
                                false, false);

    // We always preserve an existing message ID, if present, apart from some
    // exceptions.
    bool keepID = fields != nullptr;

    // Don't keep ID when forwarding inline.
    if (forward_inline) keepID = false;

    // nsMimeOutput::nsMimeMessageEditorTemplate is used for editing a message
    // "as new", creating a message from a template or editing a template.
    // Only in the latter case we want to preserve the ID.
    if (mdd->format_out == nsMimeOutput::nsMimeMessageEditorTemplate &&
        !PL_strstr(mdd->url_name, "&edittempl=true"))
      keepID = false;

    if (keepID) fields->SetMessageId(id);

    if (draftInfo && fields && !forward_inline) {
      char* parm = 0;
      parm = MimeHeaders_get_parameter(draftInfo, "vcard", NULL, NULL);
      fields->SetAttachVCard(parm && !strcmp(parm, "1"));
      PR_FREEIF(parm);

      parm = MimeHeaders_get_parameter(draftInfo, "receipt", NULL, NULL);
      if (!parm || !strcmp(parm, "0"))
        fields->SetReturnReceipt(false);
      else {
        int receiptType = 0;
        fields->SetReturnReceipt(true);
        sscanf(parm, "%d", &receiptType);
        // slight change compared to 4.x; we used to use receipt= to tell
        // whether the draft/template has request for either MDN or DNS or both
        // return receipt; since the DNS is out of the picture we now use the
        // header type - 1 to tell whether user has requested the return receipt
        fields->SetReceiptHeaderType(((int32_t)receiptType) - 1);
      }
      PR_FREEIF(parm);
      parm = MimeHeaders_get_parameter(draftInfo, "DSN", NULL, NULL);
      fields->SetDSN(parm && !strcmp(parm, "1"));
      PR_Free(parm);
      parm = MimeHeaders_get_parameter(draftInfo, "html", NULL, NULL);
      if (parm) sscanf(parm, "%d", &htmlAction);
      PR_FREEIF(parm);
      parm = MimeHeaders_get_parameter(draftInfo, "linewidth", NULL, NULL);
      if (parm) sscanf(parm, "%d", &lineWidth);
      PR_FREEIF(parm);
      parm = MimeHeaders_get_parameter(draftInfo, "attachmentreminder", NULL,
                                       NULL);
      if (parm && !strcmp(parm, "1"))
        fields->SetAttachmentReminder(true);
      else
        fields->SetAttachmentReminder(false);
      PR_FREEIF(parm);
      parm = MimeHeaders_get_parameter(draftInfo, "deliveryformat", NULL, NULL);
      if (parm) {
        int32_t deliveryFormat = nsIMsgCompSendFormat::Unset;
        sscanf(parm, "%d", &deliveryFormat);
        fields->SetDeliveryFormat(deliveryFormat);
      }
      PR_FREEIF(parm);
    }

    // identity to prefer when opening the message in the compose window?
    identityKey = MimeHeaders_get(mdd->headers, HEADER_X_MOZILLA_IDENTITY_KEY,
                                  false, false);
    if (identityKey && *identityKey) {
      nsresult rv = NS_OK;
      nsCOMPtr<nsIMsgAccountManager> accountManager =
          do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
      if (NS_SUCCEEDED(rv) && accountManager) {
        nsCOMPtr<nsIMsgIdentity> overrulingIdentity;
        rv = accountManager->GetIdentity(nsDependentCString(identityKey),
                                         getter_AddRefs(overrulingIdentity));

        if (NS_SUCCEEDED(rv) && overrulingIdentity) {
          mdd->identity = overrulingIdentity;
          fields->SetCreatorIdentityKey(identityKey);
        }
      }
    }

    if (mdd->messageBody) {
      MSG_ComposeFormat composeFormat = nsIMsgCompFormat::Default;
      if (!mdd->messageBody->m_type.IsEmpty()) {
        if (mdd->messageBody->m_type.LowerCaseFindASCII("text/html") !=
            kNotFound)
          composeFormat = nsIMsgCompFormat::HTML;
        else if (mdd->messageBody->m_type.LowerCaseFindASCII("text/plain") !=
                     kNotFound ||
                 mdd->messageBody->m_type.LowerCaseEqualsLiteral("text"))
          composeFormat = nsIMsgCompFormat::PlainText;
        else
          // We cannot use this kind of data for the message body! Therefore,
          // move it as attachment
          bodyAsAttachment = true;
      } else
        composeFormat = nsIMsgCompFormat::PlainText;

      char* body = nullptr;

      if (!bodyAsAttachment && mdd->messageBody->m_tmpFile) {
        int64_t fileSize;
        nsCOMPtr<nsIFile> tempFileCopy;
        mdd->messageBody->m_tmpFile->Clone(getter_AddRefs(tempFileCopy));
        mdd->messageBody->m_tmpFile = tempFileCopy;
        tempFileCopy = nullptr;
        mdd->messageBody->m_tmpFile->GetFileSize(&fileSize);
        uint32_t bodyLen = 0;

        // The stream interface can only read up to 4GB (32bit uint).
        // It is highly unlikely to encounter a body lager than that limit,
        // so we just skip it instead of reading it in chunks.
        if (fileSize < UINT32_MAX) {
          bodyLen = fileSize;
          body = (char*)PR_MALLOC(bodyLen + 1);
        }
        if (body) {
          memset(body, 0, bodyLen + 1);

          uint32_t bytesRead;
          nsCOMPtr<nsIInputStream> inputStream;

          nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
                                                   mdd->messageBody->m_tmpFile);
          if (NS_FAILED(rv)) {
            delete[] newAttachData;
            PR_Free(body);
            return;
          }

          inputStream->Read(body, bodyLen, &bytesRead);

          inputStream->Close();

          // Convert the body to UTF-8
          char* mimeCharset = nullptr;
          // Get a charset from the header if no override is set.
          if (!charsetOverride)
            mimeCharset = MimeHeaders_get_parameter(
                mdd->messageBody->m_type.get(), "charset", nullptr, nullptr);
          // If no charset is specified in the header then use the default.
          nsAutoCString bodyCharset;
          if (mimeCharset) {
            bodyCharset.Adopt(mimeCharset);
          } else if (mdd->mailcharset) {
            bodyCharset.Assign(mdd->mailcharset);
          }
          if (bodyCharset.IsEmpty()) {
            nsAutoCString detectedCharset;
            // We need to detect it.
            rv = MIME_detect_charset(body, bodyLen, detectedCharset);
            if (NS_SUCCEEDED(rv) && !detectedCharset.IsEmpty()) {
              bodyCharset = detectedCharset;
            }
          }
          if (!bodyCharset.IsEmpty()) {
            nsAutoString tmpUnicodeBody;
            rv = nsMsgI18NConvertToUnicode(
                bodyCharset, nsDependentCString(body), tmpUnicodeBody);
            if (NS_FAILED(rv))  // Tough luck, ASCII/ISO-8859-1 then...
              CopyASCIItoUTF16(nsDependentCString(body), tmpUnicodeBody);

            char* newBody = ToNewUTF8String(tmpUnicodeBody);
            if (newBody) {
              PR_Free(body);
              body = newBody;
            }
          }
        }
      }

      bool convertToPlainText = false;
      if (forward_inline) {
        if (mdd->identity) {
          bool identityComposeHTML;
          mdd->identity->GetComposeHtml(&identityComposeHTML);
          if ((identityComposeHTML && !mdd->overrideComposeFormat) ||
              (!identityComposeHTML && mdd->overrideComposeFormat)) {
            // In the end, we're going to compose in HTML mode...

            if (body && composeFormat == nsIMsgCompFormat::PlainText) {
              // ... but the message body is currently plain text.
              convert_plaintext_body_to_html(&body);
            }
            // Body is now HTML, set the format too (so headers are inserted in
            // correct format).
            composeFormat = nsIMsgCompFormat::HTML;
          } else if ((identityComposeHTML && mdd->overrideComposeFormat) ||
                     !identityComposeHTML) {
            // In the end, we're going to compose in plain text mode...

            if (composeFormat == nsIMsgCompFormat::HTML) {
              // ... but the message body is currently HTML.
              // We'll do the conversion later on when headers have been
              // inserted, body has been set and converted to unicode.
              convertToPlainText = true;
            }
          }
        }

        mime_insert_forwarded_message_headers(&body, mdd->headers,
                                              composeFormat, mdd->mailcharset);
      }

      MSG_ComposeType msgComposeType = 0;  // Keep compilers happy.
      if (mdd->format_out == nsMimeOutput::nsMimeMessageEditorTemplate) {
        if (PL_strstr(mdd->url_name, "?redirect=true") ||
            PL_strstr(mdd->url_name, "&redirect=true"))
          msgComposeType = nsIMsgCompType::Redirect;
        else if (PL_strstr(mdd->url_name, "?editasnew=true") ||
                 PL_strstr(mdd->url_name, "&editasnew=true"))
          msgComposeType = nsIMsgCompType::EditAsNew;
        else if (PL_strstr(mdd->url_name, "?edittempl=true") ||
                 PL_strstr(mdd->url_name, "&edittempl=true"))
          msgComposeType = nsIMsgCompType::EditTemplate;
        else
          msgComposeType = nsIMsgCompType::Template;
      }

      if (body && msgComposeType == nsIMsgCompType::EditAsNew) {
        // When editing as new, we respect the identities preferred format
        // which can be overridden.
        if (mdd->identity) {
          bool identityComposeHTML;
          mdd->identity->GetComposeHtml(&identityComposeHTML);

          if (composeFormat == nsIMsgCompFormat::HTML &&
              identityComposeHTML == mdd->overrideComposeFormat) {
            // We we have HTML:
            // If they want HTML and they want to override it (true == true)
            // or they don't want HTML and they don't want to override it
            // (false == false), then convert. Conversion happens below.
            convertToPlainText = true;
            composeFormat = nsIMsgCompFormat::PlainText;
          } else if (composeFormat == nsIMsgCompFormat::PlainText &&
                     identityComposeHTML != mdd->overrideComposeFormat) {
            // We have plain text:
            // If they want HTML and they don't want to override it (true !=
            // false) or they don't want HTML and they want to override it
            // (false != true), then convert.
            convert_plaintext_body_to_html(&body);
            composeFormat = nsIMsgCompFormat::HTML;
          }
        }
      } else if (body && mdd->overrideComposeFormat &&
                 (msgComposeType == nsIMsgCompType::Template ||
                  msgComposeType == nsIMsgCompType::EditTemplate ||
                  !mdd->forwardInline))  // Draft processing.
      {
        // When using a template and overriding, the user gets the
        // "other" format.
        if (composeFormat == nsIMsgCompFormat::PlainText) {
          convert_plaintext_body_to_html(&body);
          composeFormat = nsIMsgCompFormat::HTML;
        } else {
          // Conversion happens below.
          convertToPlainText = true;
          composeFormat = nsIMsgCompFormat::PlainText;
        }
      }

      // convert from UTF-8 to UTF-16
      if (body) {
        fields->SetBody(NS_ConvertUTF8toUTF16(body));
        PR_Free(body);
      }

      //
      // At this point, we need to create a message compose window or editor
      // window via XP-COM with the information that we have retrieved from
      // the message store.
      //
      if (mdd->format_out == nsMimeOutput::nsMimeMessageEditorTemplate) {
        // Set the draft ID when editing a template so the original is
        // overwritten when saving the template again.
        // Note that always setting the draft ID here would cause drafts to be
        // overwritten when edited "as new", which is undesired.
        if (msgComposeType == nsIMsgCompType::EditTemplate) {
          fields->SetDraftId(nsDependentCString(mdd->url_name));
          fields->SetTemplateId(nsDependentCString(
              mdd->url_name));  // Remember original template ID.
        }

        if (convertToPlainText) fields->ConvertBodyToPlainText();

        CreateTheComposeWindow(fields, newAttachData, msgComposeType,
                               composeFormat, mdd->identity,
                               mdd->originalMsgURI, mdd->origMsgHdr);
      } else {
        if (mdd->forwardInline) {
          if (convertToPlainText) fields->ConvertBodyToPlainText();
          if (mdd->overrideComposeFormat)
            composeFormat = nsIMsgCompFormat::OppositeOfDefault;
          if (mdd->forwardInlineFilter) {
            fields->SetTo(mdd->forwardToAddress);
            ForwardMsgInline(fields, newAttachData, composeFormat,
                             mdd->identity, mdd->originalMsgURI,
                             mdd->origMsgHdr);
          } else
            CreateTheComposeWindow(fields, newAttachData,
                                   nsIMsgCompType::ForwardInline, composeFormat,
                                   mdd->identity, mdd->originalMsgURI,
                                   mdd->origMsgHdr);
        } else {
          if (convertToPlainText) fields->ConvertBodyToPlainText();
          fields->SetDraftId(nsDependentCString(mdd->url_name));
          CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::Draft,
                                 composeFormat, mdd->identity,
                                 mdd->originalMsgURI, mdd->origMsgHdr);
        }
      }
    } else {
      //
      // At this point, we need to create a message compose window via
      // XP-COM with the information that we have retrieved from the message
      // store.
      //
      if (mdd->format_out == nsMimeOutput::nsMimeMessageEditorTemplate) {
#ifdef NS_DEBUG
        printf(
            "RICHIE: Time to create the EDITOR with this template - NO "
            "body!!!!\n");
#endif
        CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::Template,
                               nsIMsgCompFormat::Default, mdd->identity,
                               EmptyCString(), mdd->origMsgHdr);
      } else {
#ifdef NS_DEBUG
        printf("Time to create the composition window WITHOUT a body!!!!\n");
#endif
        if (mdd->forwardInline) {
          MSG_ComposeFormat composeFormat =
              (mdd->overrideComposeFormat) ? nsIMsgCompFormat::OppositeOfDefault
                                           : nsIMsgCompFormat::Default;
          CreateTheComposeWindow(fields, newAttachData,
                                 nsIMsgCompType::ForwardInline, composeFormat,
                                 mdd->identity, mdd->originalMsgURI,
                                 mdd->origMsgHdr);
        } else {
          fields->SetDraftId(nsDependentCString(mdd->url_name));
          CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::Draft,
                                 nsIMsgCompFormat::Default, mdd->identity,
                                 EmptyCString(), mdd->origMsgHdr);
        }
      }
    }
  } else {
    CreateCompositionFields(from, repl, to, cc, bcc, fcc, grps, foll, org, subj,
                            refs, priority, readOtherHeaders, mdd->mailcharset,
                            getter_AddRefs(fields));
    if (fields)
      CreateTheComposeWindow(fields, newAttachData, nsIMsgCompType::New,
                             nsIMsgCompFormat::Default, mdd->identity,
                             EmptyCString(), mdd->origMsgHdr);
  }

  if (mdd->headers) MimeHeaders_free(mdd->headers);

  //
  // Free the original attachment structure...
  // Make sure we only cleanup the local copy of the memory and not kill
  // files we need on disk
  //
  if (bodyAsAttachment)
    mdd->messageBody->m_tmpFile = nullptr;
  else if (mdd->messageBody && mdd->messageBody->m_tmpFile)
    mdd->messageBody->m_tmpFile->Remove(false);

  delete mdd->messageBody;

  for (uint32_t i = 0; i < mdd->attachments.Length(); i++)
    mdd->attachments[i]->m_tmpFile = nullptr;

  PR_FREEIF(mdd->mailcharset);

  mdd->identity = nullptr;
  PR_Free(mdd->url_name);
  mdd->origMsgHdr = nullptr;
  PR_Free(mdd);

  PR_FREEIF(to_and_cc);
  PR_FREEIF(re_subject);
  PR_FREEIF(new_refs);
  PR_FREEIF(from);
  PR_FREEIF(repl);
  PR_FREEIF(subj);
  PR_FREEIF(id);
  PR_FREEIF(refs);
  PR_FREEIF(to);
  PR_FREEIF(cc);
  PR_FREEIF(grps);
  PR_FREEIF(foll);
  PR_FREEIF(priority);
  PR_FREEIF(draftInfo);
  PR_Free(identityKey);

  delete[] newAttachData;
}