示例#1
0
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;
}
示例#3
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;
}