Back to index

enigmail  1.4.3
nsEnigMsgCompose.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public
00005  * License Version 1.1 (the "MPL"); you may not use this file
00006  * except in compliance with the MPL. You may obtain a copy of
00007  * the MPL at http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the MPL is distributed on an "AS
00010  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
00011  * implied. See the MPL for the specific language governing
00012  * rights and limitations under the MPL.
00013  *
00014  * The Original Code is Enigmail.
00015  *
00016  * The Initial Developer of the Original Code is Ramalingam Saravanan.
00017  * Portions created by Ramalingam Saravanan <sarava@sarava.net> are
00018  * Copyright (C) 2002 Ramalingam Saravanan. All Rights Reserved.
00019  *
00020  * Contributor(s):
00021  * Patrick Brunschwig <patrick@mozilla-enigmail.org>
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  * ***** END LICENSE BLOCK ***** */
00035 
00036 // Logging of debug output
00037 // The following define statement should occur before any include statements
00038 #define FORCE_PR_LOG       /* Allow logging even in release build */
00039 
00040 #include "enigmail.h"
00041 #include "nsStringAPI.h"
00042 #include "nsIMsgCompFields.h"
00043 #include "nsIMsgWindow.h"
00044 #include "nsMsgBaseCID.h"
00045 #include "nsMsgCompCID.h"
00046 #include "nsIMsgMailSession.h"
00047 #include "nsIEnigMsgCompFields.h"
00048 #include "nsEnigMsgCompose.h"
00049 #include "nspr.h"
00050 #include "nsCOMPtr.h"
00051 #include "nsIPrompt.h"
00052 #include "nsNetUtil.h"
00053 #include "nsIThread.h"
00054 #include "nsIFactory.h"
00055 #include "msgCore.h"
00056 #include "nsComposeStrings.h"
00057 #undef MOZILLA_INTERNAL_API
00058 
00059 #ifdef PR_LOGGING
00060 PRLogModuleInfo* gEnigMsgComposeLog = NULL;
00061 #endif
00062 
00063 #define ERROR_LOG(args)    PR_LOG(gEnigMsgComposeLog,PR_LOG_ERROR,args)
00064 #define WARNING_LOG(args)  PR_LOG(gEnigMsgComposeLog,PR_LOG_WARNING,args)
00065 #define DEBUG_LOG(args)    PR_LOG(gEnigMsgComposeLog,PR_LOG_DEBUG,args)
00066 
00067 #define NS_MSGCOMPOSESECURE_CID                    \
00068 { /* dd753201-9a23-4e08-957f-b3616bf7e012 */       \
00069    0xdd753201, 0x9a23, 0x4e08,                     \
00070   {0x95, 0x7f, 0xb3, 0x61, 0x6b, 0xf7, 0xe0, 0x12 }}
00071 
00072 static NS_DEFINE_CID(kMsgComposeSecureCID, NS_MSGCOMPOSESECURE_CID);
00073 
00074 #define MAX_HEADER_BYTES 16000
00075 #define MAX_SIGNATURE_BYTES 16000
00076 
00077 static const PRUint32 kCharMax = 1024;
00078 
00079 // nsEnigMsgComposeFactory implementation
00080 
00081 NS_IMPL_ISUPPORTS1(nsEnigMsgComposeFactory, nsIFactory)
00082 
00083 nsEnigMsgComposeFactory::nsEnigMsgComposeFactory() {
00084 
00085   NS_INIT_ISUPPORTS();
00086 }
00087 
00088 nsEnigMsgComposeFactory::~nsEnigMsgComposeFactory() {
00089 }
00090 
00091 NS_IMETHODIMP
00092 nsEnigMsgComposeFactory::CreateInstance(nsISupports *aOuter,
00093                                         const nsIID & aIID,
00094                                         void **aResult)
00095 {
00096   NS_ENSURE_ARG_POINTER(aResult);
00097 
00098   *aResult = NULL;
00099   nsEnigMsgCompose *instance = new nsEnigMsgCompose;
00100   if (!instance)
00101     return NS_ERROR_OUT_OF_MEMORY;
00102 
00103   nsresult rv = instance->QueryInterface(aIID, aResult);
00104   if (rv != NS_OK) {
00105     delete instance;
00106   }
00107 
00108   return rv;
00109 }
00110 
00111 NS_IMETHODIMP nsEnigMsgComposeFactory::LockFactory(EMBool lock)
00112 {
00113   return NS_OK;
00114 }
00115 
00116 
00117 // nsEnigMsgCompose implementation
00118 
00119 const char* nsEnigMsgCompose::FromStr = "From ";
00120 EMBool nsEnigMsgCompose::mRandomSeeded = PR_FALSE;
00121 
00122 // nsISupports implementation
00123 NS_IMPL_THREADSAFE_ISUPPORTS3(nsEnigMsgCompose,
00124                               nsIMsgComposeSecure,
00125                               nsIRequestObserver,
00126                               nsIStreamListener)
00127 
00128 // nsEnigMsgCompose implementation
00129 nsEnigMsgCompose::nsEnigMsgCompose()
00130   : mInitialized(PR_FALSE),
00131     mUseSMIME(PR_FALSE),
00132     mIsDraft(PR_FALSE),
00133     mRequestStopped(PR_FALSE),
00134 
00135     mLinebreak(PR_TRUE),
00136     mSpace(0),
00137     mMatchFrom(0),
00138 
00139     mInputLen(0),
00140     mOutputLen(0),
00141 
00142     mSendFlags(0),
00143     mUIFlags(0),
00144 
00145     mMultipartSigned(PR_FALSE),
00146     mStripWhitespace(PR_FALSE),
00147 
00148     mSenderEmailAddr(""),
00149     mRecipients(""),
00150     mBccAddr(""),
00151     mHashAlgorithm("sha1"),
00152 
00153     mBoundary(""),
00154 
00155     mStream(0),
00156 
00157     mEncoderData(nsnull),
00158 
00159     mMsgComposeSecure(nsnull),
00160     mMimeListener(nsnull),
00161 
00162     mWriter(nsnull),
00163     mPipeTrans(nsnull)
00164 {
00165   nsresult rv;
00166 
00167   NS_INIT_ISUPPORTS();
00168 
00169 #ifdef PR_LOGGING
00170   if (gEnigMsgComposeLog == nsnull) {
00171     gEnigMsgComposeLog = PR_NewLogModule("nsEnigMsgCompose");
00172   }
00173 #endif
00174 
00175   // Remember to use original CID, not CONTRACTID, to avoid infinite looping!
00176   mMsgComposeSecure = do_CreateInstance(kMsgComposeSecureCID, &rv);
00177 
00178 #ifdef FORCE_PR_LOG
00179   nsCOMPtr<nsIThread> myThread;
00180   rv = ENIG_GET_THREAD(myThread);
00181   DEBUG_LOG(("nsEnigMsgCompose:: <<<<<<<<< CTOR(%p): myThread=%p\n",
00182          this, myThread.get()));
00183 #endif
00184 }
00185 
00186 
00187 nsEnigMsgCompose::~nsEnigMsgCompose()
00188 {
00189   nsresult rv;
00190 #ifdef FORCE_PR_LOG
00191   nsCOMPtr<nsIThread> myThread;
00192   rv = ENIG_GET_THREAD(myThread);
00193   DEBUG_LOG(("nsEnigMsgCompose:: >>>>>>>>> DTOR(%p): myThread=%p\n",
00194          this, myThread.get()));
00195 #endif
00196 
00197   Finalize();
00198 
00199 }
00200 
00201 nsresult
00202 nsEnigMsgCompose::Finalize()
00203 {
00204   DEBUG_LOG(("nsEnigMsgCompose::Finalize:\n"));
00205 
00206   mMsgComposeSecure = nsnull;
00207   mMimeListener = nsnull;
00208 
00209   if (mPipeTrans) {
00210     mPipeTrans->Terminate();
00211     mPipeTrans = nsnull;
00212   }
00213 
00214   if (mWriter) {
00215     mWriter->Close();
00216     mWriter = nsnull;
00217   }
00218 
00219   if (mEncoderData) {
00220     // Clear encoder buffer
00221     MimeEncoderDestroy(mEncoderData, PR_FALSE);
00222     mEncoderData = nsnull;
00223   }
00224 
00225   return NS_OK;
00226 }
00227 
00228 
00229 nsresult
00230 nsEnigMsgCompose::GetRandomTime(PRUint32 *_retval)
00231 {
00232   if (!*_retval)
00233     return NS_ERROR_NULL_POINTER;
00234 
00235   // Current local time (microsecond resolution)
00236   PRExplodedTime localTime;
00237   PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &localTime);
00238 
00239   PRUint32       randomNumberA = localTime.tm_sec*1000000+localTime.tm_usec;
00240 
00241   // Elapsed time (1 millisecond to 10 microsecond resolution)
00242   PRIntervalTime randomNumberB = PR_IntervalNow();
00243 
00244   DEBUG_LOG(("nsEnigMsgCompose::GetRandomTime: ranA=0x%p, ranB=0x%p\n",
00245                                            randomNumberA, randomNumberB));
00246 
00247   *_retval = ((randomNumberA & 0xFFFFF) << 12) | (randomNumberB & 0xFFF);
00248 
00249   return NS_OK;
00250 }
00251 
00252 
00253 nsresult
00254 nsEnigMsgCompose::MakeBoundary(const char *prefix)
00255 {
00256   DEBUG_LOG(("nsEnigMsgCompose::MakeBoundary:\n"));
00257 
00258   nsresult rv;
00259 
00260   if (!mRandomSeeded) {
00261     PRUint32 ranTime = 1;
00262 
00263     rv = GetRandomTime(&ranTime);
00264     if (NS_FAILED(rv))
00265       return rv;
00266 
00267     srand( ranTime );
00268     mRandomSeeded = PR_TRUE;
00269   }
00270 
00271 
00272   unsigned char ch[13];
00273   for( PRUint32 j = 0; j < 12; j++)
00274     ch[j] = rand() % 256;
00275 
00276   char* boundary = PR_smprintf("------------%s"
00277            "%02X%02X%02X%02X"
00278            "%02X%02X%02X%02X"
00279            "%02X%02X%02X%02X",
00280            prefix,
00281            ch[0], ch[1], ch[2], ch[3],
00282            ch[4], ch[5], ch[6], ch[7],
00283            ch[8], ch[9], ch[10], ch[11]);
00284 
00285   if (!boundary)
00286     return NS_ERROR_OUT_OF_MEMORY;
00287 
00288   DEBUG_LOG(("nsEnigMsgCompose::MakeBoundary: boundary='%s'\n",
00289          boundary));
00290 
00291 
00292   mBoundary = boundary;
00293 
00294   PR_Free(boundary);
00295 
00296   return NS_OK;
00297 }
00298 
00299 nsresult
00300 nsEnigMsgCompose::WriteEncryptedHeaders()
00301 {
00302   nsresult rv;
00303   DEBUG_LOG(("nsEnigMsgCompose::WriteEncryptedHeaders:\n"));
00304 
00305   rv = MakeBoundary("enig");
00306   if (NS_FAILED(rv))
00307     return rv;
00308 
00309   char* headers = PR_smprintf(
00310  "Content-Type: multipart/encrypted;\r\n"
00311  " protocol=\"application/pgp-encrypted\";\r\n"
00312  " boundary=\"%s\"\r\n"
00313  "\r\n"
00314  "This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)\r\n"
00315  "--%s\r\n"
00316  "Content-Type: application/pgp-encrypted\r\n"
00317  "Content-Description: PGP/MIME version identification\r\n"
00318  "\r\n"
00319  "Version: 1\r\n"
00320  "\r\n"
00321  "--%s\r\n"
00322  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
00323  "Content-Description: OpenPGP encrypted message\r\n"
00324  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
00325  "\r\n",
00326  mBoundary.get(), mBoundary.get(), mBoundary.get());
00327 
00328   if (!headers)
00329     return NS_ERROR_OUT_OF_MEMORY;
00330 
00331   rv = WriteOut(headers, strlen(headers));
00332 
00333   PR_Free(headers);
00334 
00335   return rv;
00336 }
00337 
00338 nsresult
00339 nsEnigMsgCompose::WriteSignedHeaders1(EMBool isEightBit)
00340 {
00341   nsresult rv;
00342   DEBUG_LOG(("nsEnigMsgCompose::WriteSignedHeaders1: %d\n", (int) isEightBit));
00343 
00344   rv = MakeBoundary("enig");
00345   if (NS_FAILED(rv))
00346     return rv;
00347 
00348   char* headers = PR_smprintf(
00349        "Content-Type: multipart/signed; micalg=pgp-%s;\r\n"
00350        " protocol=\"application/pgp-signature\";\r\n"
00351        " boundary=\"%s\"\r\n"
00352        "%s"
00353        "This is an OpenPGP/MIME signed message (RFC 2440 and 3156)\r\n"
00354        "--%s\r\n",
00355        mHashAlgorithm.get(), mBoundary.get(),
00356        isEightBit ? "Content-Transfer-Encoding: 8bit\r\n\r\n" : "\r\n",
00357        mBoundary.get());
00358 
00359   if (!headers)
00360     return NS_ERROR_OUT_OF_MEMORY;
00361 
00362   rv = WriteOut(headers, strlen(headers));
00363 
00364   PR_Free(headers);
00365 
00366   return rv;
00367 }
00368 
00369 nsresult
00370 nsEnigMsgCompose::WriteSignedHeaders2()
00371 {
00372   nsresult rv;
00373   DEBUG_LOG(("nsEnigMsgCompose::WriteSignedHeaders2:\n"));
00374 
00375   char* headers = PR_smprintf(
00376  "\r\n--%s\r\n"
00377  "Content-Type: application/pgp-signature; name=\"signature.asc\"\r\n"
00378  "Content-Description: OpenPGP digital signature\r\n"
00379  "Content-Disposition: attachment; filename=\"signature.asc\"\r\n"
00380  "\r\n",
00381  mBoundary.get());
00382 
00383   if (!headers)
00384     return NS_ERROR_OUT_OF_MEMORY;
00385 
00386   rv = WriteOut(headers, strlen(headers));
00387 
00388   PR_Free(headers);
00389 
00390   return rv;
00391 }
00392 
00393 nsresult
00394 nsEnigMsgCompose::WriteFinalSeparator()
00395 {
00396   nsresult rv;
00397   DEBUG_LOG(("nsEnigMsgCompose::WriteSeparator:\n"));
00398 
00399   if (mBoundary.IsEmpty())
00400     return NS_OK;
00401 
00402   // Write out final MIME multipart separator
00403   char* separator = PR_smprintf(
00404  "\r\n--%s--\r\n",
00405  mBoundary.get());
00406 
00407   if (!separator)
00408     return NS_ERROR_OUT_OF_MEMORY;
00409 
00410   rv = WriteOut(separator, strlen(separator));
00411 
00412   PR_Free(separator);
00413 
00414   return rv;
00415 }
00416 
00417 nsresult
00418 nsEnigMsgCompose::Init()
00419 {
00420   nsresult rv;
00421 
00422   DEBUG_LOG(("nsEnigMsgCompose::Init: sendFlags=%p\n", mSendFlags));
00423 
00424   EMBool signMsg    = mSendFlags & nsIEnigmail::SEND_SIGNED;
00425   EMBool encryptMsg = mSendFlags & nsIEnigmail::SEND_ENCRYPTED;
00426   EMBool usePgpMime = mSendFlags & nsIEnigmail::SEND_PGP_MIME;
00427 
00428   mMultipartSigned = usePgpMime && signMsg && !encryptMsg;
00429 
00430   mWriter = do_CreateInstance(NS_ENIGMIMEWRITER_CONTRACTID, &rv);
00431   if (NS_FAILED(rv)) return rv;
00432 
00433   rv = mWriter->Init(mStream, PR_TRUE);
00434   if (NS_FAILED(rv)) return rv;
00435 
00436   nsCOMPtr<nsIPrompt> prompter;
00437   nsCOMPtr <nsIMsgMailSession> mailSession (do_GetService(NS_MSGMAILSESSION_CONTRACTID));
00438   if (mailSession) {
00439     nsCOMPtr<nsIMsgWindow> msgWindow;
00440     mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
00441     if (msgWindow)
00442       msgWindow->GetPromptDialog(getter_AddRefs(prompter));
00443   }
00444 
00445   nsCOMPtr<nsIEnigmail> enigmailSvc = do_GetService(NS_ENIGMAIL_CONTRACTID, &rv);
00446   if (NS_FAILED(rv)) return rv;
00447 
00448   if (usePgpMime && signMsg && (! encryptMsg)) {
00449     // determine hash algorithm to use for PGP/MIME signed msg
00450     PRInt32 exitCode;
00451     PRUnichar* ha;
00452 
00453     rv = enigmailSvc->DetermineHashAlgorithm(prompter,
00454                                              mUIFlags,
00455                                              mSenderEmailAddr.get(),
00456                                              &ha,
00457                                              &exitCode);
00458 
00459     DEBUG_LOG(("nsEnigMsgCompose::Init: DetermineHash: rv=%d, exitCode=%d\n", rv, exitCode));
00460 
00461     if (NS_FAILED(rv))
00462       return rv;
00463 
00464     if (exitCode != 0)
00465       return NS_ERROR_SMTP_PASSWORD_UNDEFINED;
00466 
00467     mHashAlgorithm = NS_ConvertUTF16toUTF8(ha).get();
00468     DEBUG_LOG(("nsEnigMsgCompose::Init: hashAlgorithm=%s\n", mHashAlgorithm.get()));
00469   }
00470 
00471   nsString errorMsg;
00472   PRUint32 statusFlags;
00473   rv = enigmailSvc->EncryptMessageStart(nsnull, prompter,
00474                                         mUIFlags,
00475                                         mSenderEmailAddr.get(),
00476                                         mRecipients.get(),
00477                                         mBccAddr.get(),
00478                                         mHashAlgorithm.get(),
00479                                         mSendFlags,
00480                                         (nsIStreamListener*)(mWriter),
00481                                         &statusFlags,
00482                                         getter_Copies(errorMsg),
00483                                         getter_AddRefs(mPipeTrans) );
00484   if (NS_FAILED(rv))
00485     return rv;
00486 
00487   if (statusFlags & nsIEnigmail::MISSING_PASSPHRASE)
00488     return NS_ERROR_SMTP_PASSWORD_UNDEFINED;
00489 
00490   if (!mPipeTrans)
00491     return NS_OK;
00492 
00493   rv = enigmailSvc->StripWhitespace(mSendFlags,
00494                                     &mStripWhitespace);
00495   if (NS_FAILED(rv))
00496     return rv;
00497 
00498   mInitialized = PR_TRUE;
00499 
00500   return NS_OK;
00501 }
00502 
00504 // nsIMsgComposeSecure methods:
00506 
00507 NS_IMETHODIMP
00508 nsEnigMsgCompose::RequiresCryptoEncapsulation(
00509                                         nsIMsgIdentity* aIdentity,
00510                                         nsIMsgCompFields* aCompFields,
00511                                         EMBool* aRequiresEncryptionWork)
00512 {
00513   nsresult rv;
00514   DEBUG_LOG(("nsEnigMsgCompose::RequiresCryptoEncapsulation: \n"));
00515 
00516   if (!mMsgComposeSecure)
00517     return NS_ERROR_FAILURE;
00518 
00519   rv = mMsgComposeSecure->RequiresCryptoEncapsulation(aIdentity,
00520                                                       aCompFields,
00521                                                       &mUseSMIME);
00522   if (NS_FAILED(rv))
00523     return rv;
00524 
00525   if (mUseSMIME) {
00526     DEBUG_LOG(("nsEnigMsgCompose::RequiresCryptoEncapsulation: Using SMIME\n"));
00527     *aRequiresEncryptionWork = PR_TRUE;
00528     return NS_OK;
00529   }
00530 
00531   // Enigmail stuff
00532   nsCOMPtr<nsISupports> securityInfo;
00533 
00534   rv = aCompFields->GetSecurityInfo(getter_AddRefs(securityInfo));
00535   if (NS_FAILED(rv))
00536     return rv;
00537 
00538   if (!securityInfo) {
00539     DEBUG_LOG(("nsEnigMsgCompose::RequiresCryptoEncapsulation: no crypto required\n"));
00540     *aRequiresEncryptionWork = PR_FALSE;
00541     return NS_OK;
00542   }
00543 
00544   nsCOMPtr<nsIEnigMsgCompFields> enigSecurityInfo = do_QueryInterface(securityInfo);
00545 
00546   if (enigSecurityInfo) {
00547     PRUint32 sendFlags;
00548     rv = enigSecurityInfo->GetSendFlags(&sendFlags);
00549     if (NS_FAILED(rv))
00550       return rv;
00551 
00552     *aRequiresEncryptionWork = sendFlags &
00553       (nsIEnigmail::SEND_SIGNED | nsIEnigmail::SEND_ENCRYPTED);
00554 
00555   } else {
00556     DEBUG_LOG(("nsEnigMsgCompose::RequiresCryptoEncapsulation: no Enigmail crypto required\n"));
00557     *aRequiresEncryptionWork = PR_FALSE;
00558   }
00559 
00560   return NS_OK;
00561 }
00562 
00563 
00564 NS_IMETHODIMP
00565 nsEnigMsgCompose::BeginCryptoEncapsulation(
00566                                         nsIOutputStream* aStream,
00567                                         const char* aRecipients,
00568                                         nsIMsgCompFields* aCompFields,
00569                                         nsIMsgIdentity* aIdentity,
00570                                         nsIMsgSendReport* sendReport,
00571                                         EMBool aIsDraft)
00572 {
00573   nsresult rv;
00574 
00575   DEBUG_LOG(("nsEnigMsgCompose::BeginCryptoEncapsulation: %s\n", aRecipients));
00576 
00577   if (!mMsgComposeSecure) {
00578     ERROR_LOG(("nsEnigMsgCompose::BeginCryptoEncapsulation: ERROR MsgComposeSecure not instantiated\n"));
00579     return NS_ERROR_FAILURE;
00580   }
00581 
00582   if (mUseSMIME) {
00583     return mMsgComposeSecure->BeginCryptoEncapsulation(aStream, aRecipients,
00584                                                        aCompFields, aIdentity,
00585                                                        sendReport, aIsDraft);
00586   }
00587 
00588   if (!aStream)
00589     return NS_ERROR_NULL_POINTER;
00590 
00591   // Enigmail stuff
00592   mStream = aStream;
00593   mIsDraft = aIsDraft;
00594 
00595   nsCOMPtr<nsISupports> securityInfo;
00596 
00597   rv = aCompFields->GetSecurityInfo(getter_AddRefs(securityInfo));
00598   if (NS_FAILED(rv))
00599     return rv;
00600 
00601   if (!securityInfo)
00602     return NS_ERROR_FAILURE;
00603 
00604   nsCOMPtr<nsIEnigMsgCompFields> enigSecurityInfo = do_QueryInterface(securityInfo);
00605 
00606   if (!enigSecurityInfo)
00607     return NS_ERROR_FAILURE;
00608 
00609   rv = enigSecurityInfo->GetSendFlags(&mSendFlags);
00610   if (NS_FAILED(rv))
00611       return rv;
00612 
00613   rv = enigSecurityInfo->GetUIFlags(&mUIFlags);
00614   if (NS_FAILED(rv))
00615       return rv;
00616 
00617   rv = enigSecurityInfo->GetSenderEmailAddr(mSenderEmailAddr);
00618   if (NS_FAILED(rv))
00619       return rv;
00620 
00621   rv = enigSecurityInfo->GetRecipients(mRecipients);
00622   if (NS_FAILED(rv))
00623       return rv;
00624 
00625   rv = enigSecurityInfo->GetBccRecipients(mBccAddr);
00626   if (NS_FAILED(rv))
00627       return rv;
00628 
00629   rv = enigSecurityInfo->GetHashAlgorithm(mHashAlgorithm);
00630   if (NS_FAILED(rv))
00631       return rv;
00632 
00633 
00634   // Create listener to intercept MIME headers
00635   mMimeListener = do_CreateInstance(NS_ENIGMIMELISTENER_CONTRACTID, &rv);
00636   if (NS_FAILED(rv)) return rv;
00637 
00638   rv = mMimeListener->Init((nsIStreamListener*) this, nsnull,
00639                            MAX_HEADER_BYTES, PR_TRUE, PR_FALSE, PR_FALSE);
00640   if (NS_FAILED(rv)) return rv;
00641 
00642   return NS_OK;
00643 }
00644 
00645 
00646 NS_IMETHODIMP
00647 nsEnigMsgCompose::FinishCryptoEncapsulation(EMBool aAbort,
00648                                             nsIMsgSendReport* sendReport)
00649 {
00650   nsresult rv;
00651 
00652   DEBUG_LOG(("nsEnigMsgCompose::FinishCryptoEncapsulation: \n"));
00653 
00654   if (!mMsgComposeSecure)
00655     return NS_ERROR_NOT_INITIALIZED;
00656 
00657   if (mUseSMIME) {
00658     return mMsgComposeSecure->FinishCryptoEncapsulation(aAbort, sendReport);
00659   }
00660 
00661   // Enigmail stuff
00662   if (!mInitialized || !mPipeTrans)
00663     return NS_ERROR_NOT_INITIALIZED;
00664 
00665   rv = FinishAux(aAbort, sendReport);
00666   if (NS_FAILED(rv)) {
00667     Finalize();
00668     return rv;
00669   }
00670 
00671   return NS_OK;
00672 }
00673 
00674 nsresult
00675 nsEnigMsgCompose::FinishAux(EMBool aAbort,
00676                             nsIMsgSendReport* sendReport)
00677 {
00678   nsresult rv;
00679 
00680   if (mMatchFrom > 0) {
00681     // Flush "buffer" for detecting lines beginning with "From "
00682     rv = WriteCopy(FromStr, mMatchFrom);
00683     if (NS_FAILED(rv)) return rv;
00684   }
00685 
00686   DEBUG_LOG(("nsEnigMsgCompose::FinishAux: \n"));
00687 
00688   if (mMultipartSigned) {
00689     rv = WriteSignedHeaders2();
00690     if (NS_FAILED(rv)) return rv;
00691   }
00692 
00693   // Wait for STDOUT to close
00694   rv = mPipeTrans->Join();
00695   if (NS_FAILED(rv)) return rv;
00696 
00697   if (aAbort) {
00698     // Terminate process
00699     mPipeTrans->Terminate();
00700     mPipeTrans = nsnull;
00701 
00702     return NS_ERROR_FAILURE;
00703   }
00704 
00705   rv = WriteFinalSeparator();
00706   if (NS_FAILED(rv)) return rv;
00707 
00708   // Count total bytes sent to writer
00709   PRUint32 cmdOutputLen;
00710   rv = mWriter->GetBytesWritten(&cmdOutputLen);
00711   if (NS_FAILED(rv)) return rv;
00712 
00713   // Exclude passthru bytes to determine STDOUT bytes
00714   cmdOutputLen -= mOutputLen;
00715 
00716   // Close STDOUT writer
00717   mWriter->Close();
00718   mWriter = nsnull;
00719 
00720   nsCOMPtr<nsIPrompt> prompter;
00721   nsCOMPtr <nsIMsgMailSession> mailSession (do_GetService(NS_MSGMAILSESSION_CONTRACTID));
00722   if (mailSession) {
00723     nsCOMPtr<nsIMsgWindow> msgWindow;
00724     mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
00725     if (msgWindow)
00726       msgWindow->GetPromptDialog(getter_AddRefs(prompter));
00727   }
00728 
00729   nsCOMPtr<nsIEnigmail> enigmailSvc = do_GetService(NS_ENIGMAIL_CONTRACTID, &rv);
00730   if (NS_FAILED(rv)) return rv;
00731 
00732   PRInt32 exitCode;
00733   PRUint32 statusFlags;
00734   nsString errorMsg;
00735   rv = enigmailSvc->EncryptMessageEnd(nsnull,
00736                                       prompter,
00737                                       mUIFlags,
00738                                       mSendFlags,
00739                                       cmdOutputLen,
00740                                       mPipeTrans,
00741                                       &statusFlags,
00742                                       getter_Copies(errorMsg),
00743                                       &exitCode);
00744   if (NS_FAILED(rv)) return rv;
00745 
00746   if (exitCode != 0) {
00747     DEBUG_LOG(("nsEnigMsgCompose::FinishAux: ERROR EXIT %d\n", exitCode));
00748     return NS_ERROR_FAILURE;
00749   }
00750 
00751   return NS_OK;
00752 }
00753 
00754 
00755 NS_IMETHODIMP
00756 nsEnigMsgCompose::MimeCryptoWriteBlock(const char *aBuf, PRInt32 aLen)
00757 {
00758   nsresult rv;
00759 
00760   DEBUG_LOG(("nsEnigMsgCompose::MimeCryptoWriteBlock: \n"));
00761 
00762   if (!mMsgComposeSecure)
00763     return NS_ERROR_FAILURE;
00764 
00765   if (mUseSMIME) {
00766     return mMsgComposeSecure->MimeCryptoWriteBlock(aBuf, aLen);
00767   }
00768 
00769   // Enigmail stuff
00770   nsCAutoString temStr(aBuf, aLen);
00771   DEBUG_LOG(("nsEnigMsgCompose::MimeCryptoWriteBlock: aBuf='%s'\n",
00772              temStr.get()));
00773 
00774   if (!mMultipartSigned) {
00775     return WriteCopy(aBuf, aLen);
00776   }
00777 
00778   // Mangle lines beginning with "From "
00779   // strip trailing whitespaces prior to signing
00780   PRUint32 offset = 0;
00781   PRUint32 writeCount = 0;
00782 
00783   for (PRUint32 j=0; j<((PRUint32) aLen); j++) {
00784     if ((mSpace > 0) && ((aBuf[j] == '\r') || (aBuf[j] == '\n'))) {
00785       // strip trailing spaces and tabs
00786       writeCount = j-offset-mSpace;
00787       WriteCopy(&aBuf[offset], writeCount);
00788       DEBUG_LOG(("nsEnigMsgCompose::MimeCryptoWriteBlock: stripped trailing whitespaces\n"));
00789       offset = j;
00790     }
00791     if (mLinebreak || (mMatchFrom > 0)) {
00792 
00793       if (aBuf[j] != FromStr[mMatchFrom]) {
00794         // No match; reset count
00795         mMatchFrom = 0;
00796 
00797       } else {
00798         // Increment match count
00799         mMatchFrom++;
00800 
00801         if (mMatchFrom >= strlen(FromStr)) {
00802           // Complete match found
00803           // Write out characters preceding match
00804           writeCount = j+1-offset-mMatchFrom;
00805 
00806           if (writeCount > 0) {
00807             rv = WriteCopy(&aBuf[offset], writeCount);
00808             if (NS_FAILED(rv)) return rv;
00809           }
00810 
00811           mMatchFrom = 0;
00812           offset = j+1;
00813 
00814           // Write out mangled string
00815           rv = WriteCopy(">", 1);
00816           if (NS_FAILED(rv)) return rv;
00817 
00818           rv = WriteCopy(FromStr, strlen(FromStr));
00819           if (NS_FAILED(rv)) return rv;
00820 
00821           DEBUG_LOG(("nsEnigMsgCompose::MimeCryptoWriteBlock: >From\n"));
00822         }
00823 
00824       }
00825     }
00826 
00827     mLinebreak = (aBuf[j] == '\r') || (aBuf[j] == '\n');
00828     if (mStripWhitespace && ((aBuf[j] == ' ') || (aBuf[j] == '\t'))) {
00829       ++mSpace;
00830     }
00831     else {
00832       mSpace = 0;
00833     }
00834   }
00835 
00836   if ((offset+mMatchFrom) < (PRUint32) aLen) {
00837     // Write out characters preceding any match
00838     rv = WriteCopy(&aBuf[offset], aLen-offset-mMatchFrom-mSpace);
00839     if (NS_FAILED(rv)) return rv;
00840   }
00841 
00842   return NS_OK;
00843 }
00844 
00845 
00846 static nsresult
00847 EnigMsgCompose_write(const char *buf, PRInt32 size, void *closure)
00848 {
00849   DEBUG_LOG(("nsEnigMsgCompose::EnigMsgCompose_write: (%p) %d\n", closure, size));
00850 
00851   if (!closure)
00852     return NS_ERROR_FAILURE;
00853 
00854   nsIEnigMimeWriter* enigMimeWriter = (nsIEnigMimeWriter *) closure;
00855 
00856   return enigMimeWriter->Write(buf, size);
00857 }
00858 
00859 
00860 nsresult
00861 nsEnigMsgCompose::WriteOut(const char *aBuf, PRInt32 aLen)
00862 {
00863   DEBUG_LOG(("nsEnigMsgCompose::WriteOut: %d\n", aLen));
00864 
00865   if (!mWriter)
00866     return NS_ERROR_FAILURE;
00867 
00868   if (aLen <= 0)
00869     return NS_OK;
00870 
00871   mOutputLen += aLen;
00872 
00873   if (mEncoderData) {
00874     // Encode data before transmitting to writer
00875     int status = MimeEncoderWrite(mEncoderData, aBuf, aLen);
00876     return (status == 0) ? NS_OK : NS_ERROR_FAILURE;
00877   }
00878 
00879   return mWriter->Write(aBuf, aLen);
00880 }
00881 
00882 nsresult
00883 nsEnigMsgCompose::WriteToPipe(const char *aBuf, PRInt32 aLen)
00884 {
00885   nsresult rv;
00886   DEBUG_LOG(("nsEnigMsgCompose::WriteToPipe: %d\n", aLen));
00887 
00888   nsCString tmpStr;
00889   tmpStr.Assign(aBuf, aLen);
00890   DEBUG_LOG(("nsEnigMimeWriter::WriteToPipe: data: '%s'\n", tmpStr.get()));
00891 
00892   rv = mPipeTrans->Write(aBuf, aLen);
00893   return rv;
00894 }
00895 
00896 nsresult
00897 nsEnigMsgCompose::WriteCopy(const char *aBuf, PRInt32 aLen)
00898 {
00899   nsresult rv;
00900 
00901   DEBUG_LOG(("nsEnigMsgCompose::WriteCopy: %d\n", aLen));
00902 
00903   if (aLen <= 0)
00904     return NS_OK;
00905 
00906   mInputLen += aLen;
00907 
00908   if (mMimeListener) {
00909     // Write to listener
00910     rv = mMimeListener->Write(aBuf, aLen, nsnull, nsnull);
00911     if (NS_FAILED(rv)) return rv;
00912 
00913   } else if (mPipeTrans) {
00914     // Write to process and copy if multipart/signed
00915     rv = WriteToPipe(aBuf, aLen);
00916     if (NS_FAILED(rv)) return rv;
00917 
00918     if (mMultipartSigned) {
00919       rv = WriteOut(aBuf, aLen);
00920       if (NS_FAILED(rv)) return rv;
00921     }
00922   }
00923 
00924   return NS_OK;
00925 }
00926 
00928 // nsIRequestObserver methods
00930 
00931 NS_IMETHODIMP
00932 nsEnigMsgCompose::OnStartRequest(nsIRequest *aRequest,
00933                                    nsISupports *aContext)
00934 {
00935   nsresult rv;
00936   DEBUG_LOG(("nsEnigMsgCompose::OnStartRequest:\n"));
00937 
00938   nsCAutoString contentType;
00939   rv = mMimeListener->GetContentType(contentType);
00940   if (NS_FAILED(rv)) return rv;
00941 
00942   nsCAutoString contentEncoding;
00943   rv = mMimeListener->GetContentEncoding(contentEncoding);
00944   if (NS_FAILED(rv)) return rv;
00945 
00946   nsCAutoString headers;
00947   rv = mMimeListener->GetHeaders(headers);
00948   if (NS_FAILED(rv)) return rv;
00949 
00950   if (headers.IsEmpty())
00951     return NS_ERROR_FAILURE;
00952 
00953   DEBUG_LOG(("nsEnigMsgCompose::OnStartRequest: Content-Type: %s\n", headers.get()));
00954 
00955   EMBool encapsulate = PR_FALSE;
00956   if (mSendFlags & nsIEnigmail::SEND_PGP_MIME) {
00957     // RFC2015 crypto encapsulation
00958     encapsulate = PR_TRUE;
00959 
00960   } else if (!contentType.Equals("text/plain", CaseInsensitiveCompare)) {
00961     // Force RFC2015 crypto encapsulation for non-plaintext messages
00962     encapsulate = PR_TRUE;
00963     mSendFlags |= nsIEnigmail::SEND_PGP_MIME;
00964   }
00965 
00966   rv = Init();
00967   if (NS_FAILED(rv)) return rv;
00968 
00969   if (!mPipeTrans) return NS_OK;
00970 
00971   if (encapsulate) {
00972     // RFC2015 crypto encapsulation for headers
00973 
00974     // Send headers to crypto processor
00975     rv = WriteToPipe(headers.get(), headers.Length());
00976     if (NS_FAILED(rv)) return rv;
00977 
00978     if (mMultipartSigned) {
00979       rv = WriteSignedHeaders1( contentEncoding.Equals("8bit", CaseInsensitiveCompare) );
00980       if (NS_FAILED(rv)) return rv;
00981 
00982       // Copy original headers to output
00983       rv = WriteOut(headers.get(), headers.Length());
00984       if (NS_FAILED(rv)) return rv;
00985 
00986     } else {
00987       rv = WriteEncryptedHeaders();
00988       if (NS_FAILED(rv)) return rv;
00989     }
00990 
00991   } else {
00992     // No crypto encapsulation for headers
00993     DEBUG_LOG(("nsEnigMsgCompose::OnStartRequest: NO CRYPTO ENCAPSULATION\n"));
00994 
00995     rv = WriteOut(headers.get(), headers.Length());
00996     if (NS_FAILED(rv)) return rv;
00997 
00998     if (contentEncoding.Equals("base64", CaseInsensitiveCompare)) {
00999 
01000       mEncoderData = MimeB64EncoderInit(EnigMsgCompose_write, (void*) mWriter);
01001 
01002     } else if (contentEncoding.Equals("quoted-printable", CaseInsensitiveCompare)) {
01003 
01004       mEncoderData = MimeQPEncoderInit(EnigMsgCompose_write, (void*) mWriter);
01005     }
01006   }
01007 
01008   return NS_OK;
01009 }
01010 
01011 NS_IMETHODIMP
01012 nsEnigMsgCompose::OnStopRequest(nsIRequest* aRequest,
01013                                   nsISupports* aContext,
01014                                   nsresult aStatus)
01015 {
01016   DEBUG_LOG(("nsEnigMsgCompose::OnStopRequest:\n"));
01017 
01018   mRequestStopped = PR_TRUE;
01019 
01020   return NS_OK;
01021 }
01022 
01024 // nsIStreamListener method
01026 
01027 NS_IMETHODIMP
01028 nsEnigMsgCompose::OnDataAvailable(nsIRequest* aRequest,
01029                                   nsISupports* aContext,
01030                                   nsIInputStream *aInputStream,
01031                                   PRUint32 aSourceOffset,
01032                                   PRUint32 aLength)
01033 {
01034   nsresult rv;
01035 
01036   DEBUG_LOG(("nsEnigMsgCompose::OnDataAVailable: %d\n", aLength));
01037 
01038   if (!mPipeTrans)
01039     return NS_ERROR_NOT_INITIALIZED;
01040 
01041   char buf[kCharMax];
01042   PRUint32 readCount, readMax, writeCount;
01043 
01044   while (aLength > 0) {
01045     readMax = (aLength < kCharMax) ? aLength : kCharMax;
01046     rv = aInputStream->Read((char *) buf, readMax, &readCount);
01047 
01048     if (NS_FAILED(rv)){
01049       DEBUG_LOG(("nsEnigMsgCompose::OnDataAvailable: Error in reading from input stream, %p\n", rv));
01050       return rv;
01051     }
01052 
01053     if (readCount <= 0) return NS_OK;
01054 
01055     writeCount = readCount;
01056 
01057     if (mMultipartSigned) {
01058 
01059       nsCString tmpStr;
01060       tmpStr.Assign(buf, readCount);
01061 
01062       nsCString left(tmpStr);
01063       left.SetLength(15);
01064 
01065       if (left.LowerCaseEqualsLiteral("x-mozilla-keys:")) {
01066         DEBUG_LOG(("nsEnigMimeWriter::OnDataAvailable: workaround for 'X-Mozilla-Keys:' header\n"));
01067 
01068         tmpStr.StripWhitespace();
01069         if (left == tmpStr) {
01070           if (buf[readCount-2] == '\r' && buf[readCount-1] == '\n') {
01071             tmpStr.Append("\r\n");
01072           }
01073           else
01074             tmpStr.Append("\n");
01075 
01076           rv = WriteToPipe(tmpStr.get(), tmpStr.Length());
01077           if (NS_FAILED(rv)) return rv;
01078 
01079           rv = WriteOut(tmpStr.get(), tmpStr.Length());
01080           if (NS_FAILED(rv)) return rv;
01081 
01082           aLength -= readCount;
01083 
01084           return NS_OK;
01085 
01086         }
01087       }
01088 
01089 
01090       rv = WriteToPipe(buf, readCount);
01091       if (NS_FAILED(rv)) return rv;
01092 
01093       rv = WriteOut(buf, readCount);
01094       if (NS_FAILED(rv)) return rv;
01095 
01096     }
01097     else {
01098       rv = WriteToPipe(buf, readCount);
01099       if (NS_FAILED(rv)) return rv;
01100     }
01101 
01102     aLength -= readCount;
01103   }
01104 
01105   return NS_OK;
01106 }