//
// Given a content-type and some info about the contents of the document,
// decide what encoding it should have.
//
int
nsMsgAttachmentHandler::PickEncoding(const char *charset, nsIMsgSend *mime_delivery_state)
{
  nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));

  bool needsB64 = false;
  bool forceB64 = false;

  if (m_already_encoded_p)
    goto DONE;

  AnalyzeSnarfedFile();

  /* Allow users to override our percentage-wise guess on whether
  the file is text or binary */
  if (pPrefBranch)
    pPrefBranch->GetBoolPref ("mail.file_attach_binary", &forceB64);

  if (!mMainBody && (forceB64 || mime_type_requires_b64_p (m_type.get()) ||
    m_have_cr+m_have_lf+m_have_crlf != 1 || m_current_column != 0))
  {
  /* If the content-type is "image/" or something else known to be binary
  or several flavors of newlines are present or last line is incomplete,
  always use base64 (so that we don't get confused by newline
  conversions.)
     */
    needsB64 = true;
  }
  else
  {
  /* Otherwise, we need to pick an encoding based on the contents of
  the document.
     */

    bool encode_p;
    bool force_p = false;

    /*
      force quoted-printable if the sender does not allow
      conversion to 7bit
    */
    if (mCompFields) {
      if (mCompFields->GetForceMsgEncoding())
        force_p = true;
    }
    else if (mime_delivery_state) {
      if (((nsMsgComposeAndSend *)mime_delivery_state)->mCompFields->GetForceMsgEncoding())
        force_p = true;
    }

    if (force_p || (m_max_column > 900))
      encode_p = true;
    else if (UseQuotedPrintable() && m_unprintable_count)
      encode_p = true;

      else if (m_null_count)  /* If there are nulls, we must always encode,
        because sendmail will blow up. */
        encode_p = true;
      else
        encode_p = false;

        /* MIME requires a special case that these types never be encoded.
      */
      if (StringBeginsWith(m_type, NS_LITERAL_CSTRING("message"),
                           nsCaseInsensitiveCStringComparator()) ||
         StringBeginsWith(m_type, NS_LITERAL_CSTRING("multipart"),
                          nsCaseInsensitiveCStringComparator()))
      {
        encode_p = false;
        if (m_desiredType.LowerCaseEqualsLiteral(TEXT_PLAIN))
          m_desiredType.Truncate();
      }

      // If the Mail charset is multibyte, we force it to use Base64 for attachments.
      if ((!mMainBody && charset && nsMsgI18Nmultibyte_charset(charset)) &&
          (m_type.LowerCaseEqualsLiteral(TEXT_HTML) ||
           m_type.LowerCaseEqualsLiteral(TEXT_MDL) ||
           m_type.LowerCaseEqualsLiteral(TEXT_PLAIN) ||
           m_type.LowerCaseEqualsLiteral(TEXT_RICHTEXT) ||
           m_type.LowerCaseEqualsLiteral(TEXT_ENRICHED) ||
           m_type.LowerCaseEqualsLiteral(TEXT_VCARD) ||
           m_type.LowerCaseEqualsLiteral(APPLICATION_DIRECTORY) || /* text/x-vcard synonym */
           m_type.LowerCaseEqualsLiteral(TEXT_CSS) ||
           m_type.LowerCaseEqualsLiteral(TEXT_JSSS)))
        needsB64 = true;
      else if (charset && nsMsgI18Nstateful_charset(charset))
        m_encoding = ENCODING_7BIT;
      else if (encode_p &&
        m_unprintable_count > (m_size / 10))
        /* If the document contains more than 10% unprintable characters,
        then that seems like a good candidate for base64 instead of
        quoted-printable.
        */
        needsB64 = true;
      else if (encode_p)
        m_encoding = ENCODING_QUOTED_PRINTABLE;
      else if (m_highbit_count > 0)
        m_encoding = ENCODING_8BIT;
      else
        m_encoding = ENCODING_7BIT;
  }

  // always base64 binary data.
  if (needsB64)
    m_encoding = ENCODING_BASE64;

  /* Now that we've picked an encoding, initialize the filter.
  */
  NS_ASSERTION(!m_encoder_data, "not-null m_encoder_data");
  if (m_encoding.LowerCaseEqualsLiteral(ENCODING_BASE64))
  {
    m_encoder_data = MIME_B64EncoderInit(mime_encoder_output_fn,
      mime_delivery_state);
    if (!m_encoder_data) return NS_ERROR_OUT_OF_MEMORY;
  }
  else if (m_encoding.LowerCaseEqualsLiteral(ENCODING_QUOTED_PRINTABLE))
  {
    m_encoder_data = MIME_QPEncoderInit(mime_encoder_output_fn,
      mime_delivery_state);
    if (!m_encoder_data) return NS_ERROR_OUT_OF_MEMORY;
  }
  else
  {
    m_encoder_data = 0;
  }

  /* Do some cleanup for documents with unknown content type.
    There are two issues: how they look to MIME users, and how they look to
    non-MIME users.

      If the user attaches a "README" file, which has unknown type because it
      has no extension, we still need to send it with no encoding, so that it
      is readable to non-MIME users.

        But if the user attaches some random binary file, then base64 encoding
        will have been chosen for it (above), and in this case, it won't be
        immediately readable by non-MIME users.  However, if we type it as
        text/plain instead of application/octet-stream, it will show up inline
        in a MIME viewer, which will probably be ugly, and may possibly have
        bad charset things happen as well.

          So, the heuristic we use is, if the type is unknown, then the type is
          set to application/octet-stream for data which needs base64 (binary data)
          and is set to text/plain for data which didn't need base64 (unencoded or
          lightly encoded data.)
  */
DONE:
  if (m_type.IsEmpty() || m_type.LowerCaseEqualsLiteral(UNKNOWN_CONTENT_TYPE))
  {
    if (m_already_encoded_p)
      m_type = APPLICATION_OCTET_STREAM;
    else if (m_encoding.LowerCaseEqualsLiteral(ENCODING_BASE64) ||
             m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE))
      m_type = APPLICATION_OCTET_STREAM;
    else
      m_type = TEXT_PLAIN;
  }
  return 0;
}
Exemple #2
0
nsresult nsMsgI18NSaveAsCharset(const char* contentType, const char *charset, 
                                const char16_t* inString, char** outString, 
                                char **fallbackCharset, bool *isAsciiOnly)
{
  NS_ENSURE_ARG_POINTER(contentType);
  NS_ENSURE_ARG_POINTER(charset);
  NS_ENSURE_ARG_POINTER(inString);
  NS_ENSURE_ARG_POINTER(outString);

  *outString = nullptr;

  if (NS_IsAscii(inString)) {
    if (isAsciiOnly)
      *isAsciiOnly = true;
    *outString = ToNewCString(NS_LossyConvertUTF16toASCII(inString));
    return (nullptr != *outString) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
  }
  if (isAsciiOnly)
    *isAsciiOnly = false;

  bool bTEXT_HTML = false;
  nsresult res;

  if (!PL_strcasecmp(contentType, TEXT_HTML)) {
    bTEXT_HTML = true;
  }
  else if (PL_strcasecmp(contentType, TEXT_PLAIN)) {
    return NS_ERROR_ILLEGAL_VALUE;  // not supported type
  }

  nsCOMPtr <nsICharsetConverterManager> ccm =
    do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res);
  NS_ENSURE_SUCCESS(res, res);

  nsAutoCString charsetName;
  res = ccm->GetCharsetAlias(charset, charsetName);
  NS_ENSURE_SUCCESS(res, res);

  nsCOMPtr <nsISaveAsCharset> conv = do_CreateInstance(NS_SAVEASCHARSET_CONTRACTID, &res);
  NS_ENSURE_SUCCESS(res, res);

  // First try to transliterate, if that fails use '?' for "bad" chars.
  res = conv->Init(charsetName.get(),
                   nsISaveAsCharset::attr_FallbackQuestionMark +
                     nsISaveAsCharset::attr_EntityNone,
                   nsIEntityConverter::transliterate);
  NS_ENSURE_SUCCESS(res, res);

  const char16_t *input = inString;

  // Convert to charset
  res = conv->Convert(input, outString);

  // If the converer cannot encode to the charset,
  // then fallback to pref sepcified charsets.
  if (NS_ERROR_UENC_NOMAPPING == res && !bTEXT_HTML && fallbackCharset) {
    nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &res));
    NS_ENSURE_SUCCESS(res, res);

    nsAutoCString prefString("intl.fallbackCharsetList.");
    prefString.Append(charset);
    nsCString fallbackList;
    res = prefBranch->GetCharPref(prefString.get(), getter_Copies(fallbackList));
    // do the fallback only if there is a pref for the charset
    if (NS_FAILED(res) || fallbackList.IsEmpty())
      return NS_ERROR_UENC_NOMAPPING;

    res = conv->Init(fallbackList.get(), 
                     nsISaveAsCharset::attr_FallbackQuestionMark + 
                     nsISaveAsCharset::attr_EntityAfterCharsetConv +
                     nsISaveAsCharset::attr_CharsetFallback, 
                     nsIEntityConverter::transliterate);
    NS_ENSURE_SUCCESS(res, res);

    // free whatever we have now
    PR_FREEIF(*outString);  

    res = conv->Convert(input, outString);
    NS_ENSURE_SUCCESS(res, res);

    // get the actual charset used for the conversion
    if (NS_FAILED(conv->GetCharset(fallbackCharset)))
      *fallbackCharset = nullptr;
  }
  // Exclude stateful charset which is 7 bit but not ASCII only.
  else if (isAsciiOnly && *outString &&
           !nsMsgI18Nstateful_charset(charsetName.get()))
    *isAsciiOnly = NS_IsAscii(*outString);

  return res;
}