nsresult nsMsgComposeSecure::MimeFinishMultipartSigned (bool aOuter, nsIMsgSendReport *sendReport) { int status; nsresult rv; nsCOMPtr<nsICMSMessage> cinfo = do_CreateInstance(NS_CMSMESSAGE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsICMSEncoder> encoder = do_CreateInstance(NS_CMSENCODER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); char * header = nsnull; nsCOMPtr<nsIStringBundleService> bundleSvc = mozilla::services::GetStringBundleService(); NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED); nsCOMPtr<nsIStringBundle> sMIMEBundle; nsString mime_smime_sig_content_desc; bundleSvc->CreateBundle(SMIME_STRBUNDLE_URL, getter_AddRefs(sMIMEBundle)); if (!sMIMEBundle) return NS_ERROR_FAILURE; sMIMEBundle->GetStringFromName(NS_LITERAL_STRING("mime_smimeSignatureContentDesc").get(), getter_Copies(mime_smime_sig_content_desc)); NS_ConvertUTF16toUTF8 sig_content_desc_utf8(mime_smime_sig_content_desc); /* Compute the hash... */ nsCAutoString hashString; mDataHash->Finish(false, hashString); mDataHash = 0; status = PR_GetError(); if (status < 0) goto FAIL; /* Write out the headers for the signature. */ PRUint32 L; header = PR_smprintf(CRLF "--%s" CRLF "Content-Type: " APPLICATION_PKCS7_SIGNATURE "; name=\"smime.p7s\"" CRLF "Content-Transfer-Encoding: " ENCODING_BASE64 CRLF "Content-Disposition: attachment; " "filename=\"smime.p7s\"" CRLF "Content-Description: %s" CRLF CRLF, mMultipartSignedBoundary, sig_content_desc_utf8.get()); if (!header) { rv = NS_ERROR_OUT_OF_MEMORY; goto FAIL; } L = strlen(header); if (aOuter) { /* If this is the outer block, write it to the file. */ PRUint32 n; rv = mStream->Write(header, L, &n); if (NS_FAILED(rv) || n < L) { rv = MK_MIME_ERROR_WRITING_FILE; } } else { /* If this is an inner block, feed it through the crypto stream. */ rv = MimeCryptoWriteBlock (header, L); } PR_Free(header); /* Create the signature... */ PR_ASSERT(mHashType == nsICryptoHash::SHA1); PR_ASSERT (mSelfSigningCert); PR_SetError(0,0); rv = cinfo->CreateSigned(mSelfSigningCert, mSelfEncryptionCert, (unsigned char*)hashString.get(), hashString.Length()); if (NS_FAILED(rv)) { SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotSign").get()); goto FAIL; } /* Initialize the base64 encoder for the signature data. */ PR_ASSERT(!mSigEncoderData); mSigEncoderData = MIME_B64EncoderInit((aOuter ? mime_encoder_output_fn : mime_nested_encoder_output_fn), this); if (!mSigEncoderData) { rv = NS_ERROR_OUT_OF_MEMORY; goto FAIL; } /* Write out the signature. */ PR_SetError(0,0); rv = encoder->Start(cinfo, mime_crypto_write_base64, mSigEncoderData); if (NS_FAILED(rv)) { SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotSign").get()); goto FAIL; } // We're not passing in any data, so no update needed. rv = encoder->Finish(); if (NS_FAILED(rv)) { SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotSign").get()); goto FAIL; } /* Shut down the sig's base64 encoder. */ rv = MIME_EncoderDestroy(mSigEncoderData, false); mSigEncoderData = 0; if (NS_FAILED(rv)) { goto FAIL; } /* Now write out the terminating boundary. */ { PRUint32 L; char *header = PR_smprintf(CRLF "--%s--" CRLF, mMultipartSignedBoundary); PR_Free(mMultipartSignedBoundary); mMultipartSignedBoundary = 0; if (!header) { rv = NS_ERROR_OUT_OF_MEMORY; goto FAIL; } L = strlen(header); if (aOuter) { /* If this is the outer block, write it to the file. */ PRUint32 n; rv = mStream->Write(header, L, &n); if (NS_FAILED(rv) || n < L) rv = MK_MIME_ERROR_WRITING_FILE; } else { /* If this is an inner block, feed it through the crypto stream. */ rv = MimeCryptoWriteBlock (header, L); } } FAIL: return rv; }
// // 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 nsMsgComposeSecure::MimeInitEncryption(bool aSign, nsIMsgSendReport *sendReport) { nsresult rv; nsCOMPtr<nsIStringBundleService> bundleSvc = mozilla::services::GetStringBundleService(); NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED); nsCOMPtr<nsIStringBundle> sMIMEBundle; nsString mime_smime_enc_content_desc; bundleSvc->CreateBundle(SMIME_STRBUNDLE_URL, getter_AddRefs(sMIMEBundle)); if (!sMIMEBundle) return NS_ERROR_FAILURE; sMIMEBundle->GetStringFromName(NS_LITERAL_STRING("mime_smimeEncryptedContentDesc").get(), getter_Copies(mime_smime_enc_content_desc)); NS_ConvertUTF16toUTF8 enc_content_desc_utf8(mime_smime_enc_content_desc); /* First, construct and write out the opaque-crypto-blob MIME header data. */ char *s = PR_smprintf("Content-Type: " APPLICATION_PKCS7_MIME "; name=\"smime.p7m\"" CRLF "Content-Transfer-Encoding: " ENCODING_BASE64 CRLF "Content-Disposition: attachment" "; filename=\"smime.p7m\"" CRLF "Content-Description: %s" CRLF CRLF, enc_content_desc_utf8.get()); PRUint32 L; if (!s) return NS_ERROR_OUT_OF_MEMORY; L = strlen(s); PRUint32 n; rv = mStream->Write(s, L, &n); if (NS_FAILED(rv) || n < L) { return NS_ERROR_FAILURE; } PR_Free(s); s = 0; /* Now initialize the crypto library, so that we can filter the object to be encrypted through it. */ if (!mIsDraft) { PRUint32 numCerts; mCerts->GetLength(&numCerts); PR_ASSERT(numCerts > 0); if (numCerts == 0) return NS_ERROR_FAILURE; } /* Initialize the base64 encoder. */ PR_ASSERT(!mCryptoEncoderData); mCryptoEncoderData = MIME_B64EncoderInit(mime_encoder_output_fn, this); if (!mCryptoEncoderData) { return NS_ERROR_OUT_OF_MEMORY; } /* Initialize the encrypter (and add the sender's cert.) */ PR_ASSERT(mSelfEncryptionCert); PR_SetError(0,0); mEncryptionCinfo = do_CreateInstance(NS_CMSMESSAGE_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; rv = mEncryptionCinfo->CreateEncrypted(mCerts); if (NS_FAILED(rv)) { SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotEncrypt").get()); goto FAIL; } mEncryptionContext = do_CreateInstance(NS_CMSENCODER_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; if (!mBuffer) { mBuffer = new char[eBufferSize]; if (!mBuffer) return NS_ERROR_OUT_OF_MEMORY; } mBufferedBytes = 0; rv = mEncryptionContext->Start(mEncryptionCinfo, mime_crypto_write_base64, mCryptoEncoderData); if (NS_FAILED(rv)) { SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotEncrypt").get()); goto FAIL; } /* If we're signing, tack a multipart/signed header onto the front of the data to be encrypted, and initialize the sign-hashing code too. */ if (aSign) { rv = MimeInitMultipartSigned(false, sendReport); if (NS_FAILED(rv)) goto FAIL; } FAIL: return rv; }