// // 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; }
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; }