Back to index

enigmail  1.4.3
nsEnigMimeDecrypt.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 "nspr.h"
00042 #include "nsCOMPtr.h"
00043 #include "plstr.h"
00044 #include "nsStringAPI.h"
00045 #include "nsNetUtil.h"
00046 #include "nsIPrompt.h"
00047 #include "nsIMsgWindow.h"
00048 #include "nsMsgBaseCID.h"
00049 #include "nsIMsgMailSession.h"
00050 #include "nsIMimeMiscStatus.h"
00051 #include "nsIEnigMimeHeaderSink.h"
00052 #include "nsIAsyncInputStream.h"
00053 #include "nsIThread.h"
00054 #include "nsIEventTarget.h"
00055 #include "nsEnigMimeDecrypt.h"
00056 #include "nsIPipeTransport.h"
00057 #include "nsIIPCBuffer.h"
00058 #include "nsIEnigmail.h"
00059 
00060 #ifdef PR_LOGGING
00061 PRLogModuleInfo* gEnigMimeDecryptLog = NULL;
00062 #endif
00063 
00064 #define ERROR_LOG(args)    PR_LOG(gEnigMimeDecryptLog,PR_LOG_ERROR,args)
00065 #define WARNING_LOG(args)  PR_LOG(gEnigMimeDecryptLog,PR_LOG_WARNING,args)
00066 #define DEBUG_LOG(args)    PR_LOG(gEnigMimeDecryptLog,PR_LOG_DEBUG,args)
00067 
00068 #define MAX_BUFFER_BYTES 32768
00069 static const PRUint32 kCharMax = 1024;
00070 
00071 // nsEnigMimeDecrypt implementation
00072 
00073 // nsISupports implementation
00074 NS_IMPL_THREADSAFE_ISUPPORTS2(nsEnigMimeDecrypt,
00075                               nsIEnigMimeDecrypt,
00076                               nsIPipeReader)
00077 
00078 // nsEnigMimeDecrypt implementation
00079 nsEnigMimeDecrypt::nsEnigMimeDecrypt()
00080   : mInitialized(PR_FALSE),
00081     mVerifyOnly(PR_FALSE),
00082     mRfc2015(PR_FALSE),
00083     mDone(PR_FALSE),
00084 
00085     mInputLen(0),
00086     mOutputLen(0),
00087     mIterations(0),
00088     mCtFound(-1),
00089 
00090     mBuffer(nsnull),
00091     mListener(nsnull),
00092     mPipeTrans(nsnull),
00093     mSecurityInfo(nsnull),
00094     mUri(nsnull)
00095 {
00096   nsresult rv;
00097 
00098   NS_INIT_ISUPPORTS();
00099 
00100 #ifdef PR_LOGGING
00101   if (gEnigMimeDecryptLog == nsnull) {
00102     gEnigMimeDecryptLog = PR_NewLogModule("nsEnigMimeDecrypt");
00103   }
00104 #endif
00105 
00106 #ifdef FORCE_PR_LOG
00107   nsCOMPtr<nsIThread> myThread;
00108   rv = ENIG_GET_THREAD(myThread);
00109   DEBUG_LOG(("nsEnigMimeDecrypt:: <<<<<<<<< CTOR(%p): myThread=%p\n",
00110          this, myThread.get()));
00111 #endif
00112 }
00113 
00114 
00115 nsEnigMimeDecrypt::~nsEnigMimeDecrypt()
00116 {
00117   nsresult rv;
00118 #ifdef FORCE_PR_LOG
00119   nsCOMPtr<nsIThread> myThread;
00120   rv = ENIG_GET_THREAD(myThread);
00121   DEBUG_LOG(("nsEnigMimeDecrypt:: >>>>>>>>> DTOR(%p): myThread=%p\n",
00122          this, myThread.get()));
00123 #endif
00124 
00125   Finalize();
00126 }
00127 
00128 
00130 // nsIEnigMimeDecrypt methods:
00132 
00133 NS_IMETHODIMP
00134 nsEnigMimeDecrypt::Init(EMBool verifyOnly,
00135                         EMBool rfc2015,
00136                         EnigDecryptCallbackFun outputFun,
00137                         void* outputClosure)
00138 {
00139   nsresult rv;
00140 
00141   if (!outputFun || !outputClosure)
00142     return NS_ERROR_NULL_POINTER;
00143 
00144   mVerifyOnly = verifyOnly;
00145   mRfc2015 = rfc2015;
00146 
00147   mOutputFun     = outputFun;
00148   mOutputClosure = outputClosure;
00149 
00150   mBuffer = do_CreateInstance(NS_IPCBUFFER_CONTRACTID, &rv);
00151   if (NS_FAILED(rv)) return rv;
00152 
00153   // Prepare to copy data to buffer, with temp file overflow
00154   rv = mBuffer->Open(MAX_BUFFER_BYTES, PR_TRUE);
00155   if (NS_FAILED(rv)) return rv;
00156 
00157   if (mRfc2015) {
00158     // RFC 2015: Create PipeFilterListener to extract second MIME part
00159     mListener = do_CreateInstance(NS_PIPEFILTERLISTENER_CONTRACTID, &rv);
00160     if (NS_FAILED(rv)) return rv;
00161 
00162     rv = mListener->Init((nsIStreamListener*)(mBuffer),
00163                          nsnull, "", "", 1, PR_FALSE, PR_TRUE, nsnull);
00164     if (NS_FAILED(rv)) return rv;
00165   }
00166 
00167   mInitialized = PR_TRUE;
00168 
00169   return NS_OK;
00170 }
00171 
00172 
00173 nsresult
00174 nsEnigMimeDecrypt::Finalize()
00175 {
00176   DEBUG_LOG(("nsEnigMimeDecrypt::Finalize:\n"));
00177 
00178   mOutputFun = NULL;
00179   mOutputClosure = NULL;
00180 
00181   if (mPipeTrans) {
00182     mPipeTrans->Terminate();
00183     mPipeTrans = nsnull;
00184   }
00185 
00186   if (mListener) {
00187     mListener = nsnull;
00188   }
00189 
00190   if (mBuffer) {
00191     mBuffer->Shutdown();
00192     mBuffer = nsnull;
00193   }
00194 
00195   return NS_OK;
00196 }
00197 
00198 NS_IMETHODIMP
00199 nsEnigMimeDecrypt::Write(const char *buf, PRUint32 buf_size)
00200 
00201 {
00202   if (!mInitialized)
00203     return NS_ERROR_NOT_INITIALIZED;
00204 
00205   if (mListener)
00206     mListener->Write(buf, buf_size, nsnull, nsnull);
00207   else
00208     mBuffer->WriteBuf(buf, buf_size);
00209 
00210   mInputLen += buf_size;
00211 
00212   return NS_OK;
00213 }
00214 
00215 
00216 NS_IMETHODIMP
00217 nsEnigMimeDecrypt::Finish(nsIMsgWindow* msgWindow, nsIURI* uri)
00218 {
00219   // Enigmail stuff
00220   nsresult rv;
00221 
00222   DEBUG_LOG(("nsEnigMimeDecrypt::Finish:\n"));
00223 
00224   if (!mInitialized)
00225     return NS_ERROR_NOT_INITIALIZED;
00226 
00227   rv = FinishAux(msgWindow, uri);
00228   if (NS_FAILED(rv)) {
00229     Finalize();
00230     return rv;
00231   }
00232 
00233   return NS_OK;
00234 }
00235 
00236 
00237 nsresult
00238 nsEnigMimeDecrypt::FinishAux(nsIMsgWindow* msgWindow, nsIURI* uri)
00239 {
00240   // Enigmail stuff
00241   nsresult rv;
00242   nsCOMPtr<nsIThread> myThread;
00243   rv = ENIG_GET_THREAD(myThread);
00244 
00245   mUri = uri;
00246   nsCAutoString uriSpec("");
00247 
00248   if (mListener) {
00249     rv = mListener->OnStopRequest(nsnull, nsnull, 0);
00250     if (NS_FAILED(rv))
00251       return rv;
00252 
00253     nsCAutoString endLine;
00254     rv = mListener->GetEndLine(endLine);
00255     if (NS_FAILED(rv)) return rv;
00256 
00257     if (endLine.IsEmpty()) {
00258       ERROR_LOG(("nsEnigMimeDecrypt::FinishAux: ERROR MIME part not terminated\n"));
00259       return NS_ERROR_FAILURE;
00260     }
00261 
00262     mListener = nsnull;
00263   }
00264 
00265   rv = mBuffer->OnStopRequest(nsnull, nsnull, 0);
00266   if (NS_FAILED(rv))
00267     return rv;
00268 
00269   if (msgWindow) {
00270     nsCOMPtr<nsIMsgHeaderSink> headerSink;
00271     msgWindow->GetMsgHeaderSink(getter_AddRefs(headerSink));
00272     if (headerSink)
00273         headerSink->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
00274   }
00275   DEBUG_LOG(("nsEnigMimeDecrypt::FinishAux: securityInfo=%p\n", mSecurityInfo.get()));
00276 
00277   nsCOMPtr<nsIPrompt> prompter;
00278   if (msgWindow) {
00279     msgWindow->GetPromptDialog(getter_AddRefs(prompter));
00280   }
00281 
00282   if (!prompter) {
00283     nsCOMPtr <nsIMsgMailSession> mailSession (do_GetService(NS_MSGMAILSESSION_CONTRACTID));
00284     if (mailSession) {
00285       nsCOMPtr<nsIMsgWindow> msgwin;
00286       mailSession->GetTopmostMsgWindow(getter_AddRefs(msgwin));
00287       if (msgwin)
00288         msgwin->GetPromptDialog(getter_AddRefs(prompter));
00289     }
00290   }
00291 
00292   DEBUG_LOG(("nsEnigMimeDecrypt::FinishAux: prompter=%p\n", prompter.get()));
00293 
00294   nsCOMPtr<nsIEnigmail> enigmailSvc = do_GetService(NS_ENIGMAIL_CONTRACTID, &rv);
00295   if (NS_FAILED(rv))
00296     return rv;
00297 
00298   nsString errorMsg;
00299   EMBool noOutput = PR_FALSE;
00300   PRUint32 statusFlags;
00301 
00302   rv = enigmailSvc->DecryptMessageStart(nsnull,
00303                                         prompter,
00304                                         mVerifyOnly,
00305                                         noOutput,
00306                                         nsnull,
00307                                         &statusFlags,
00308                                         getter_Copies(errorMsg),
00309                                         getter_AddRefs(mPipeTrans) );
00310   if (NS_FAILED(rv)) return rv;
00311 
00312   if (!mPipeTrans) {
00313     if (mSecurityInfo) {
00314       nsCOMPtr<nsIEnigMimeHeaderSink> enigHeaderSink = do_QueryInterface(mSecurityInfo);
00315       if (enigHeaderSink) {
00316         NS_NAMED_LITERAL_STRING(nullString, "");
00317         rv = enigHeaderSink->UpdateSecurityStatus(uriSpec, -1, statusFlags, nullString.get(), nullString.get(), nullString.get(), errorMsg.get(), nullString.get(), mUri);
00318       }
00319     }
00320 
00321     return NS_ERROR_FAILURE;
00322   }
00323 
00324   mIterations = 0;
00325   mCtFound = -1;
00326   nsCOMPtr<nsIInputStream> plainStream = nsnull;
00327 
00328   // read via pipeTransport.jsm
00329   nsCOMPtr<nsIRequest> request;
00330   rv = mPipeTrans->ReadInputStream(this, getter_AddRefs(request));
00331   NS_ENSURE_SUCCESS(rv, rv);
00332 
00333   // Write buffered data asyncronously to process
00334   nsCOMPtr<nsIInputStream> bufStream;
00335   rv = mBuffer->OpenInputStream(getter_AddRefs(bufStream));
00336   if (NS_FAILED(rv)) return rv;
00337 
00338   PRUint32 available;
00339   rv = bufStream->Available(&available);
00340   if (NS_FAILED(rv)) return rv;
00341 
00342   DEBUG_LOG(("nsEnigMimeDecrypt::FinishAux: available=%d\n", available));
00343 
00344   rv = mPipeTrans->WriteAsync(bufStream, available, PR_TRUE);
00345   if (NS_FAILED(rv)) return rv;
00346 
00347   // read via pipeTransport.jsm
00348 
00349   nsCOMPtr<nsIThread> currentThread;
00350   rv = ENIG_GET_THREAD(currentThread);
00351   NS_ENSURE_SUCCESS(rv, rv);
00352 
00353   mDone = PR_FALSE;
00354 
00355   // wait with returning until message is completely processed
00356   // (simulate synchronous function)
00357   while (! mDone) {
00358     EMBool pendingEvents;
00359     rv = currentThread->ProcessNextEvent(PR_TRUE, &pendingEvents);
00360     NS_ENSURE_SUCCESS(rv, rv);
00361   }
00362 
00363   return NS_OK;
00364 }
00365 
00366 nsresult
00367 nsEnigMimeDecrypt::ProcessPlainData(char* buf, PRUint32 readCount)
00368 {
00369   DEBUG_LOG(("nsEnigMimeDecrypt::ProcessPlainData: readCount=%d\n", readCount));
00370 
00371   int status;
00372   ++mIterations;
00373   // Read synchronously
00374 
00375 
00376   if (mIterations == 1 && readCount > 25) {
00377     // add mime boundaries around text/plain message (bug 6627)
00378     if (PL_strncasecmp("content-type:", buf, 13)==0) {
00379       PRUint32 whitespace=13;
00380       while((whitespace<readCount) && buf[whitespace] &&
00381             ((buf[whitespace]==' ') || (buf[whitespace]=='\t'))) { whitespace++; }
00382       if (buf[whitespace] && (whitespace<readCount)) {
00383         mCtFound = PL_strncasecmp(buf + whitespace, "text/plain", 10);
00384         if (mCtFound != 0) {
00385           mCtFound=PL_strncasecmp(buf + whitespace, "text/html", 9);
00386         }
00387       }
00388       if (mCtFound == 0) {
00389         char* header = PR_smprintf(
00390         "Content-Type: multipart/mixed; boundary=\"enigDummy\""
00391         "\n\n--enigDummy\n");
00392         PR_SetError(0,0);
00393         status = mOutputFun(header, strlen(header), mOutputClosure);
00394         if (status < 0) {
00395           PR_SetError(status, 0);
00396           mOutputFun = NULL;
00397           mOutputClosure = NULL;
00398 
00399           return NS_ERROR_FAILURE;
00400         }
00401         mOutputLen += strlen(header);
00402       }
00403     }
00404   }
00405 
00406   if (readCount < kCharMax) {
00407     // make sure we can continue to write later
00408     if (buf[readCount-1]==0) --readCount;
00409   }
00410 
00411   PR_SetError(0,0);
00412   status = mOutputFun(buf, readCount, mOutputClosure);
00413   if (status < 0) {
00414     PR_SetError(status, 0);
00415     mOutputFun = NULL;
00416     mOutputClosure = NULL;
00417 
00418     return NS_ERROR_FAILURE;
00419   }
00420 
00421   mOutputLen += readCount;
00422 
00423   return NS_OK;
00424 } // loop end
00425 
00426 
00427 nsresult
00428 nsEnigMimeDecrypt::ProcessEnd(nsIInputStream* plainStream)
00429 {
00430   DEBUG_LOG(("nsEnigMimeDecrypt::ProcessEnd:\n"));
00431 
00432   char buf[kCharMax];
00433   nsresult rv;
00434   nsString errorMsg;
00435   nsCAutoString uriSpec("");
00436 
00437 
00438   nsCOMPtr<nsIEnigmail> enigmailSvc = do_GetService(NS_ENIGMAIL_CONTRACTID, &rv);
00439   if (NS_FAILED(rv))
00440     return rv;
00441 
00442 DEBUG_LOG(("nsEnigMimeDecrypt::ProcessEnd: got Enigmail Svc\n"));
00443 
00444   if (mCtFound==0) {
00445     // add mime boundaries around text/plain message (bug 6627)
00446     PR_SetError(0,0);
00447     strcpy(buf, "\n\n--enigDummy--\n");
00448 
00449     int status = mOutputFun(buf, strlen(buf), mOutputClosure);
00450     if (status < 0) {
00451       PR_SetError(status, 0);
00452       mOutputFun = NULL;
00453       mOutputClosure = NULL;
00454 DEBUG_LOG(("nsEnigMimeDecrypt::ProcessEnd: error 1\n"));
00455 
00456       return NS_ERROR_FAILURE;
00457     }
00458     mOutputLen+=strlen(buf);
00459   }
00460   else {
00461     // add final \n to make sure last line is always displayed (bug 5952)
00462     buf[0]='\n';
00463     PR_SetError(0,0);
00464     int status = mOutputFun(buf, 1, mOutputClosure);
00465     if (status >= 0) {
00466       // ignore any errors here
00467       mOutputLen++;
00468     }
00469   }
00470 
00471   PR_SetError(0,0);
00472 
00473   // Close input stream
00474   if (plainStream) plainStream->Close();
00475 
00476   // Close buffer
00477   mBuffer->Shutdown();
00478 
00479   PRInt32 exitCode;
00480   nsString keyId;
00481   nsString userId;
00482   nsString sigDate;
00483   nsString blockSeparation;
00484   PRUint32 statusFlags;
00485 
00486   PRUint32 uiFlags = nsIEnigmail::UI_PGP_MIME;
00487   EMBool noOutput = PR_FALSE;
00488 
00489   rv = enigmailSvc->DecryptMessageEnd(uiFlags,
00490                                       mOutputLen,
00491                                       mPipeTrans,
00492                                       mVerifyOnly,
00493                                       noOutput,
00494                                       &statusFlags,
00495                                       getter_Copies(keyId),
00496                                       getter_Copies(userId),
00497                                       getter_Copies(sigDate),
00498                                       getter_Copies(errorMsg),
00499                                       getter_Copies(blockSeparation),
00500                                       &exitCode);
00501 DEBUG_LOG(("nsEnigMimeDecrypt::ProcessEnd: location 2\n"));
00502 
00503   if (NS_FAILED(rv)) return rv;
00504 DEBUG_LOG(("nsEnigMimeDecrypt::ProcessEnd: location 3\n"));
00505 
00506   if (mSecurityInfo) {
00507     nsCOMPtr<nsIEnigMimeHeaderSink> enigHeaderSink = do_QueryInterface(mSecurityInfo);
00508     if (enigHeaderSink) {
00509       rv = enigHeaderSink->UpdateSecurityStatus(uriSpec, exitCode, statusFlags, keyId.get(), userId.get(), sigDate.get(), errorMsg.get(), blockSeparation.get(), mUri);
00510     }
00511   }
00512 
00513   if (exitCode != 0) {
00514     DEBUG_LOG(("nsEnigMimeDecrypt::ProcessEnd: ERROR EXIT %d\n", exitCode));
00515     return NS_ERROR_FAILURE;
00516   }
00517 
00518   return NS_OK;
00519 }
00520 
00521 
00522 NS_IMETHODIMP
00523 nsEnigMimeDecrypt::ReadData(const char* buf,
00524                             PRUint32 count)
00525 {
00526   nsresult rv;
00527   DEBUG_LOG(("nsEnigMimeDecrypt::ReadData: count=%d\n", count));
00528 
00529   NS_ENSURE_ARG(buf);
00530 
00531 /*
00532   PRUint32 readCount;
00533   char buf[count + 1];
00534 
00535   rv = stream->Read((char*) buf, count, &readCount);
00536   NS_ENSURE_SUCCESS(rv, rv);
00537 */
00538   if (count) {
00539     rv = ProcessPlainData((char *) buf, count);
00540     return rv;
00541   }
00542 
00543   return NS_OK;
00544 
00545 }
00546 
00547 
00548 NS_IMETHODIMP
00549 nsEnigMimeDecrypt::StopRequest(PRUint32 status)
00550 {
00551   DEBUG_LOG(("nsEnigMimeDecrypt::StopRequest:\n"));
00552 
00553   ProcessEnd(nsnull);
00554   mDone = PR_TRUE;
00555 
00556   return NS_OK;
00557 }