Back to index

lightning-sunbird  0.9+nobinonly
nsMsgProtocol.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "msgCore.h"
00039 #include "nsReadableUtils.h"
00040 #include "nsMsgProtocol.h"
00041 #include "nsIMsgMailNewsUrl.h"
00042 #include "nsIStreamTransportService.h"
00043 #include "nsISocketTransportService.h"
00044 #include "nsISocketTransport.h"
00045 #include "nsXPIDLString.h"
00046 #include "nsSpecialSystemDirectory.h"
00047 #include "nsILoadGroup.h"
00048 #include "nsIIOService.h"
00049 #include "nsNetUtil.h"
00050 #include "nsIFileURL.h"
00051 #include "nsFileStream.h"
00052 #include "nsIMsgWindow.h"
00053 #include "nsIMsgStatusFeedback.h"
00054 #include "nsIWebProgressListener.h"
00055 #include "nsIPipe.h"
00056 #include "nsIPrompt.h"
00057 #include "prprf.h"
00058 #include "plbase64.h"
00059 #include "nsIStringBundle.h"
00060 #include "nsIProtocolProxyService.h"
00061 #include "nsIProxyInfo.h"
00062 #include "nsEventQueueUtils.h"
00063 #include "nsIPrefBranch.h"
00064 #include "nsIPrefService.h"
00065 
00066 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
00067 static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
00068 
00069 NS_IMPL_THREADSAFE_ADDREF(nsMsgProtocol)
00070 NS_IMPL_THREADSAFE_RELEASE(nsMsgProtocol)
00071 
00072 NS_INTERFACE_MAP_BEGIN(nsMsgProtocol)
00073    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
00074    NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
00075    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
00076    NS_INTERFACE_MAP_ENTRY(nsIChannel)
00077    NS_INTERFACE_MAP_ENTRY(nsIRequest)
00078    NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
00079 NS_INTERFACE_MAP_END_THREADSAFE
00080 
00081 static PRUnichar *FormatStringWithHostNameByID(PRInt32 stringID, nsIMsgMailNewsUrl *msgUri);
00082 
00083 
00084 nsMsgProtocol::nsMsgProtocol(nsIURI * aURL)
00085 {
00086   m_flags = 0;
00087   m_readCount = 0;
00088   mLoadFlags = 0;
00089   m_socketIsOpen = PR_FALSE;
00090               
00091   m_tempMsgFileSpec = nsSpecialSystemDirectory(nsSpecialSystemDirectory::OS_TemporaryDirectory);
00092   m_tempMsgFileSpec += "tempMessage.eml";
00093   mSuppressListenerNotifications = PR_FALSE;
00094   InitFromURI(aURL);
00095 }
00096 
00097 nsresult nsMsgProtocol::InitFromURI(nsIURI *aUrl)
00098 {
00099   m_url = aUrl;
00100   
00101   nsCOMPtr <nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
00102   if (mailUrl)
00103   {
00104     mailUrl->GetLoadGroup(getter_AddRefs(m_loadGroup));
00105     nsCOMPtr<nsIMsgStatusFeedback> statusFeedback;
00106     mailUrl->GetStatusFeedback(getter_AddRefs(statusFeedback));
00107     mProgressEventSink = do_QueryInterface(statusFeedback);
00108   }
00109   return NS_OK;
00110 }
00111 
00112 nsMsgProtocol::~nsMsgProtocol()
00113 {}
00114 
00115 
00116 static PRBool gGotTimeoutPref;
00117 static PRInt32 gSocketTimeout = 60;
00118 
00119 nsresult
00120 nsMsgProtocol::OpenNetworkSocketWithInfo(const char * aHostName,
00121                                          PRInt32 aGetPort,
00122                                          const char *connectionType,
00123                                          nsIProxyInfo *aProxyInfo,
00124                                          nsIInterfaceRequestor* callbacks)
00125 {
00126   NS_ENSURE_ARG(aHostName);
00127 
00128   nsresult rv = NS_OK;
00129   nsCOMPtr<nsISocketTransportService> socketService (do_GetService(kSocketTransportServiceCID));
00130   NS_ENSURE_TRUE(socketService, NS_ERROR_FAILURE);
00131   
00132   // with socket connections we want to read as much data as arrives
00133   m_readCount = -1;
00134 
00135   nsCOMPtr<nsISocketTransport> strans;
00136   rv = socketService->CreateTransport(&connectionType, connectionType != nsnull,
00137                                       nsDependentCString(aHostName),
00138                                       aGetPort, aProxyInfo,
00139                                       getter_AddRefs(strans));
00140   if (NS_FAILED(rv)) return rv;
00141 
00142   strans->SetSecurityCallbacks(callbacks);
00143 
00144   // creates cyclic reference!
00145   nsCOMPtr<nsIEventQueue> eventQ;
00146   NS_GetCurrentEventQ(getter_AddRefs(eventQ));
00147   if (eventQ)
00148     strans->SetEventSink(this, eventQ);
00149 
00150   m_socketIsOpen = PR_FALSE;
00151   m_transport = strans;
00152 
00153   if (!gGotTimeoutPref)
00154   {
00155     nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
00156     if (prefBranch)
00157     {
00158       prefBranch->GetIntPref("mailnews.tcptimeout", &gSocketTimeout);
00159       gGotTimeoutPref = PR_TRUE;
00160     }
00161   }
00162   strans->SetTimeout(nsISocketTransport::TIMEOUT_CONNECT, gSocketTimeout + 60);  
00163   strans->SetTimeout(nsISocketTransport::TIMEOUT_READ_WRITE, gSocketTimeout);  
00164 
00165 
00166   return SetupTransportState();
00167 }
00168 
00169 // open a connection on this url
00170 nsresult
00171 nsMsgProtocol::OpenNetworkSocket(nsIURI * aURL, const char *connectionType,
00172                                  nsIInterfaceRequestor* callbacks) 
00173 {
00174   NS_ENSURE_ARG(aURL);
00175 
00176   nsCAutoString hostName;
00177   PRInt32 port = 0;
00178 
00179   aURL->GetPort(&port);
00180   aURL->GetAsciiHost(hostName);
00181 
00182   nsCOMPtr<nsIProxyInfo> proxyInfo;
00183 
00184   nsCOMPtr<nsIProtocolProxyService> pps =
00185       do_GetService("@mozilla.org/network/protocol-proxy-service;1");
00186 
00187   NS_ASSERTION(pps, "Couldn't get the protocol proxy service!");
00188 
00189   if (pps) 
00190   {
00191       nsresult rv = NS_OK;
00192 
00193       // Yes, this is ugly. But necko needs to grap a protocol handler
00194       // to ask for flags, and smtp isn't registered as a handler, only
00195       // mailto.
00196       // Note that I cannot just clone, and call SetSpec, since Clone on
00197       // nsSmtpUrl calls nsStandardUrl's clone method, which fails
00198       // because smtp isn't a registered protocol.
00199       // So we cheat. Whilst creating a uri manually is valid here,
00200       // do _NOT_ copy this to use in your own code - bbaetz
00201       nsCOMPtr<nsIURI> proxyUri = aURL;
00202       PRBool isSMTP = PR_FALSE;
00203       if (NS_SUCCEEDED(aURL->SchemeIs("smtp", &isSMTP)) && isSMTP) 
00204       {
00205           nsCAutoString spec;
00206           rv = aURL->GetSpec(spec);
00207           if (NS_SUCCEEDED(rv)) 
00208               proxyUri = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
00209 
00210           if (NS_SUCCEEDED(rv))
00211               rv = proxyUri->SetSpec(spec);
00212           if (NS_SUCCEEDED(rv))
00213               rv = proxyUri->SetScheme(NS_LITERAL_CSTRING("mailto"));
00214       }
00215       //
00216       // XXX(darin): Consider using AsyncResolve instead to avoid blocking
00217       //             the calling thread in cases where PAC may call into
00218       //             our DNS resolver.
00219       //
00220       if (NS_SUCCEEDED(rv))
00221           rv = pps->Resolve(proxyUri, 0, getter_AddRefs(proxyInfo));
00222       NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully resolve a proxy");
00223       if (NS_FAILED(rv)) proxyInfo = nsnull;
00224   }
00225 
00226   return OpenNetworkSocketWithInfo(hostName.get(), port, connectionType,
00227                                    proxyInfo, callbacks);
00228 }
00229 
00230 nsresult nsMsgProtocol::GetFileFromURL(nsIURI * aURL, nsIFile **aResult)
00231 {
00232   NS_ENSURE_ARG_POINTER(aURL);
00233   NS_ENSURE_ARG_POINTER(aResult);
00234   // extract the file path from the uri...
00235   nsCAutoString urlSpec;
00236   aURL->GetPath(urlSpec);
00237   urlSpec.Insert(NS_LITERAL_CSTRING("file://"), 0);
00238   nsresult rv;
00239 
00240 // dougt - there should be an easier way!
00241   nsCOMPtr<nsIURI> uri;
00242   if (NS_FAILED(rv = NS_NewURI(getter_AddRefs(uri), urlSpec.get())))
00243       return rv;
00244 
00245   nsCOMPtr<nsIFileURL>    fileURL = do_QueryInterface(uri);
00246   if (!fileURL)   return NS_ERROR_FAILURE;
00247 
00248   return fileURL->GetFile(aResult);
00249   // dougt
00250 }
00251 
00252 nsresult nsMsgProtocol::OpenFileSocket(nsIURI * aURL, PRUint32 aStartPosition, PRInt32 aReadCount)
00253 {
00254   // mscott - file needs to be encoded directly into aURL. I should be able to get
00255   // rid of this method completely.
00256 
00257   nsresult rv = NS_OK;
00258   m_readCount = aReadCount;
00259   nsCOMPtr <nsIFile> file;
00260 
00261   rv = GetFileFromURL(aURL, getter_AddRefs(file));
00262   NS_ENSURE_SUCCESS(rv, rv);
00263 
00264   nsCOMPtr<nsIInputStream> stream;
00265   rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
00266   if (NS_FAILED(rv)) return rv;
00267 
00268   // create input stream transport
00269   nsCOMPtr<nsIStreamTransportService> sts =
00270       do_GetService(kStreamTransportServiceCID, &rv);
00271   if (NS_FAILED(rv)) return rv;
00272 
00273   rv = sts->CreateInputTransport(stream, nsInt64(aStartPosition),
00274                                  nsInt64(aReadCount), PR_TRUE,
00275                                  getter_AddRefs(m_transport));
00276 
00277   m_socketIsOpen = PR_FALSE;
00278        return rv;
00279 }
00280 
00281 nsresult nsMsgProtocol::SetupTransportState()
00282 {
00283   if (!m_socketIsOpen && m_transport)
00284   {
00285     nsresult rv;
00286     
00287     // open buffered, blocking output stream
00288     rv = m_transport->OpenOutputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(m_outputStream));
00289     if (NS_FAILED(rv)) return rv;
00290     // we want to open the stream 
00291   } // if m_transport
00292   
00293   return NS_OK;
00294 }
00295 
00296 nsresult nsMsgProtocol::CloseSocket()
00297 {
00298   nsresult rv = NS_OK;
00299   // release all of our socket state
00300   m_socketIsOpen = PR_FALSE;
00301   m_inputStream = nsnull;
00302   m_outputStream = nsnull;
00303   if (m_transport) {
00304     nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport);
00305     if (strans) {
00306       strans->SetSecurityCallbacks(nsnull);
00307       strans->SetEventSink(nsnull, nsnull); // break cyclic reference!
00308     }
00309   }
00310   // we need to call Cancel so that we remove the socket transport from the mActiveTransportList.  see bug #30648
00311   if (m_request) {
00312     rv = m_request->Cancel(NS_BINDING_ABORTED);
00313   }
00314   m_request = 0;
00315   if (m_transport) {
00316     m_transport->Close(NS_BINDING_ABORTED);
00317     m_transport = 0;
00318   }
00319   
00320   return rv;
00321 }
00322 
00323 /*
00324 * Writes the data contained in dataBuffer into the current output stream. It also informs
00325 * the transport layer that this data is now available for transmission.
00326 * Returns a positive number for success, 0 for failure (not all the bytes were written to the
00327 * stream, etc). We need to make another pass through this file to install an error system (mscott)
00328 *
00329 * No logging is done in the base implementation, so aSuppressLogging is ignored.
00330 */
00331 
00332 PRInt32 nsMsgProtocol::SendData(nsIURI * aURL, const char * dataBuffer, PRBool aSuppressLogging)
00333 {
00334   PRUint32 writeCount = 0; 
00335   PRInt32 status = 0; 
00336   
00337   //   NS_PRECONDITION(m_outputStream, "oops....we don't have an output stream...how did that happen?");
00338   if (dataBuffer && m_outputStream)
00339     status = m_outputStream->Write(dataBuffer, PL_strlen(dataBuffer), &writeCount);
00340   
00341   return status;
00342 }
00343 
00344 // Whenever data arrives from the connection, core netlib notifices the protocol by calling
00345 // OnDataAvailable. We then read and process the incoming data from the input stream. 
00346 NS_IMETHODIMP nsMsgProtocol::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
00347 {
00348   // right now, this really just means turn around and churn through the state machine
00349   nsCOMPtr<nsIURI> uri = do_QueryInterface(ctxt);
00350   return ProcessProtocolState(uri, inStr, sourceOffset, count);
00351 }
00352 
00353 NS_IMETHODIMP nsMsgProtocol::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
00354 {
00355   nsresult rv = NS_OK;
00356   nsCOMPtr <nsIMsgMailNewsUrl> aMsgUrl = do_QueryInterface(ctxt, &rv);
00357   if (NS_SUCCEEDED(rv) && aMsgUrl)
00358   {
00359     rv = aMsgUrl->SetUrlState(PR_TRUE, NS_OK);
00360     if (m_loadGroup)
00361       m_loadGroup->AddRequest(NS_STATIC_CAST(nsIRequest *, this), nsnull /* context isupports */);
00362   }
00363   
00364   // if we are set up as a channel, we should notify our channel listener that we are starting...
00365   // so pass in ourself as the channel and not the underlying socket or file channel the protocol
00366   // happens to be using
00367   if (!mSuppressListenerNotifications && m_channelListener)
00368   {
00369     if (!m_channelContext)
00370       m_channelContext = do_QueryInterface(ctxt);
00371     rv = m_channelListener->OnStartRequest(this, m_channelContext);
00372   }
00373   
00374   nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport);
00375 
00376   if (strans)
00377     strans->SetTimeout(nsISocketTransport::TIMEOUT_READ_WRITE, gSocketTimeout);  
00378 
00379   NS_ENSURE_SUCCESS(rv, rv);
00380   return rv;
00381 }
00382 
00383 // stop binding is a "notification" informing us that the stream associated with aURL is going away. 
00384 NS_IMETHODIMP nsMsgProtocol::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult aStatus)
00385 {
00386   nsresult rv = NS_OK;
00387 
00388   // if we are set up as a channel, we should notify our channel listener that we are starting...
00389   // so pass in ourself as the channel and not the underlying socket or file channel the protocol
00390   // happens to be using
00391   if (!mSuppressListenerNotifications && m_channelListener)
00392     rv = m_channelListener->OnStopRequest(this, m_channelContext, aStatus);
00393 
00394   nsCOMPtr <nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(ctxt, &rv);
00395   if (NS_SUCCEEDED(rv) && msgUrl)
00396   {
00397     rv = msgUrl->SetUrlState(PR_FALSE, aStatus);
00398     if (m_loadGroup)
00399       m_loadGroup->RemoveRequest(NS_STATIC_CAST(nsIRequest *, this), nsnull, aStatus);
00400     
00401     // !NS_BINDING_ABORTED because we don't want to see an alert if the user 
00402     // cancelled the operation.  also, we'll get here because we call Cancel()
00403     // to force removal of the nsSocketTransport.  see CloseSocket()
00404     // bugs #30775 and #30648 relate to this
00405     if (NS_FAILED(aStatus) && (aStatus != NS_BINDING_ABORTED)) 
00406     {
00407       nsCOMPtr<nsIPrompt> msgPrompt;
00408       GetPromptDialogFromUrl(msgUrl , getter_AddRefs(msgPrompt));
00409       NS_ENSURE_TRUE(msgPrompt, NS_ERROR_FAILURE);
00410 
00411       PRInt32 errorID;
00412       switch (aStatus) 
00413       {
00414           case NS_ERROR_UNKNOWN_HOST:
00415           case NS_ERROR_UNKNOWN_PROXY_HOST:
00416              errorID = UNKNOWN_HOST_ERROR;
00417              break;
00418           case NS_ERROR_CONNECTION_REFUSED:
00419           case NS_ERROR_PROXY_CONNECTION_REFUSED:
00420              errorID = CONNECTION_REFUSED_ERROR;
00421              break;
00422           case NS_ERROR_NET_TIMEOUT:
00423              errorID = NET_TIMEOUT_ERROR;
00424              break;
00425           default: 
00426              errorID = UNKNOWN_ERROR;
00427              break;
00428       }
00429       
00430       NS_ASSERTION(errorID != UNKNOWN_ERROR, "unknown error, but don't alert user.");
00431       if (errorID != UNKNOWN_ERROR) 
00432       {
00433         PRUnichar *errorMsg = FormatStringWithHostNameByID(errorID, msgUrl);
00434         if (errorMsg == nsnull) 
00435         {
00436           nsAutoString resultString(NS_LITERAL_STRING("[StringID "));
00437           resultString.AppendInt(errorID);
00438           resultString.AppendLiteral("?]");
00439           errorMsg = ToNewUnicode(resultString);
00440         }
00441         rv = msgPrompt->Alert(nsnull, errorMsg);
00442         nsMemory::Free(errorMsg);
00443       }
00444     } // if we got an error code
00445   } // if we have a mailnews url.
00446 
00447   // Drop notification callbacks to prevent cycles.
00448   mCallbacks = 0;
00449   mProgressEventSink = 0;
00450   // Call CloseSocket(), in case we got here because the server dropped the 
00451   // connection while reading, and we never get a chance to get back into 
00452   // the protocol state machine via OnDataAvailable. 
00453   if (m_socketIsOpen)
00454     CloseSocket();
00455 
00456   return rv;
00457 }
00458 
00459 nsresult nsMsgProtocol::GetPromptDialogFromUrl(nsIMsgMailNewsUrl * aMsgUrl, nsIPrompt ** aPromptDialog)
00460 {
00461   // get the nsIPrompt interface from the message window associated wit this url.
00462   nsCOMPtr<nsIMsgWindow> msgWindow;
00463   aMsgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
00464   NS_ENSURE_TRUE(msgWindow, NS_ERROR_FAILURE);
00465 
00466   msgWindow->GetPromptDialog(aPromptDialog);
00467 
00468   NS_ENSURE_TRUE(*aPromptDialog, NS_ERROR_FAILURE);
00469 
00470   return NS_OK;
00471 }
00472 
00473 nsresult nsMsgProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer)
00474 {
00475   // okay now kick us off to the next state...
00476   // our first state is a process state so drive the state machine...
00477   nsresult rv = NS_OK;
00478   nsCOMPtr <nsIMsgMailNewsUrl> aMsgUrl = do_QueryInterface(aURL, &rv);
00479   
00480   if (NS_SUCCEEDED(rv) && aMsgUrl)
00481   {
00482     PRBool msgIsInLocalCache;
00483     aMsgUrl->GetMsgIsInLocalCache(&msgIsInLocalCache);
00484     
00485     rv = aMsgUrl->SetUrlState(PR_TRUE, NS_OK); // set the url as a url currently being run...
00486     
00487     // if the url is given a stream consumer then we should use it to forward calls to...
00488     if (!m_channelListener && aConsumer) // if we don't have a registered listener already
00489     {
00490       m_channelListener = do_QueryInterface(aConsumer);
00491       if (!m_channelContext)
00492         m_channelContext = do_QueryInterface(aURL);
00493     }
00494     
00495     if (!m_socketIsOpen)
00496     {
00497       nsCOMPtr<nsISupports> urlSupports = do_QueryInterface(aURL);
00498       if (m_transport)
00499       {
00500         // don't open the input stream more than once
00501         if (!m_inputStream)
00502         {
00503           // open buffered, asynchronous input stream
00504           rv = m_transport->OpenInputStream(0, 0, 0, getter_AddRefs(m_inputStream));
00505           if (NS_FAILED(rv)) return rv;
00506         }
00507         
00508         nsCOMPtr<nsIInputStreamPump> pump;
00509         rv = NS_NewInputStreamPump(getter_AddRefs(pump),
00510           m_inputStream, nsInt64(-1), m_readCount);
00511         if (NS_FAILED(rv)) return rv;
00512         
00513         m_request = pump; // keep a reference to the pump so we can cancel it
00514         
00515         // put us in a state where we are always notified of incoming data
00516         rv = pump->AsyncRead(this, urlSupports);
00517         NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncRead failed");
00518         m_socketIsOpen = PR_TRUE; // mark the channel as open
00519       }
00520     } // if we got an event queue service
00521     else if (!msgIsInLocalCache) // the connection is already open so we should begin processing our new url...
00522       rv = ProcessProtocolState(aURL, nsnull, 0, 0); 
00523   }
00524   
00525   return rv;
00526 }
00527 
00529 // The rest of this file is mostly nsIChannel mumbo jumbo stuff
00531 
00532 nsresult nsMsgProtocol::SetUrl(nsIURI * aURL)
00533 {
00534   m_url = aURL;
00535   return NS_OK;
00536 }
00537 
00538 NS_IMETHODIMP nsMsgProtocol::SetLoadGroup(nsILoadGroup * aLoadGroup)
00539 {
00540   m_loadGroup = aLoadGroup;
00541   return NS_OK;
00542 }
00543 
00544 NS_IMETHODIMP nsMsgProtocol::GetOriginalURI(nsIURI* *aURI)
00545 {
00546     *aURI = m_originalUrl ? m_originalUrl : m_url;
00547     NS_IF_ADDREF(*aURI);
00548     return NS_OK;
00549 }
00550  
00551 NS_IMETHODIMP nsMsgProtocol::SetOriginalURI(nsIURI* aURI)
00552 {
00553     m_originalUrl = aURI;
00554     return NS_OK;
00555 }
00556  
00557 NS_IMETHODIMP nsMsgProtocol::GetURI(nsIURI* *aURI)
00558 {
00559     *aURI = m_url;
00560     NS_IF_ADDREF(*aURI);
00561     return NS_OK;
00562 }
00563  
00564 NS_IMETHODIMP nsMsgProtocol::Open(nsIInputStream **_retval)
00565 {
00566   NS_NOTREACHED("Open");
00567   return NS_ERROR_NOT_IMPLEMENTED;
00568 }
00569 
00570 NS_IMETHODIMP nsMsgProtocol::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
00571 {
00572     PRInt32 port;
00573     nsresult rv = m_url->GetPort(&port);
00574     if (NS_FAILED(rv))
00575         return rv;
00576     
00577     nsCAutoString scheme;
00578     rv = m_url->GetScheme(scheme);
00579     if (NS_FAILED(rv))
00580         return rv;
00581     
00582 
00583     rv = NS_CheckPortSafety(port, scheme.get());
00584     if (NS_FAILED(rv))
00585         return rv;
00586 
00587     // set the stream listener and then load the url
00588     m_channelContext = ctxt;
00589     m_channelListener = listener;
00590     return LoadUrl(m_url, nsnull);
00591 }
00592 
00593 NS_IMETHODIMP nsMsgProtocol::GetLoadFlags(nsLoadFlags *aLoadFlags)
00594 {
00595     *aLoadFlags = mLoadFlags;
00596     return NS_OK;
00597 }
00598 
00599 NS_IMETHODIMP nsMsgProtocol::SetLoadFlags(nsLoadFlags aLoadFlags)
00600 {
00601   mLoadFlags = aLoadFlags;
00602   return NS_OK;       // don't fail when trying to set this
00603 }
00604 
00605 NS_IMETHODIMP nsMsgProtocol::GetContentType(nsACString &aContentType)
00606 {
00607   // as url dispatching matures, we'll be intelligent and actually start
00608   // opening the url before specifying the content type. This will allow
00609   // us to optimize the case where the message url actual refers to  
00610   // a part in the message that has a content type that is not message/rfc822
00611 
00612   if (m_ContentType.IsEmpty())
00613     aContentType.AssignLiteral("message/rfc822");
00614   else
00615     aContentType = m_ContentType;
00616   return NS_OK;
00617 }
00618 
00619 NS_IMETHODIMP nsMsgProtocol::SetContentType(const nsACString &aContentType)
00620 {
00621     nsCAutoString charset;
00622     return NS_ParseContentType(aContentType, m_ContentType, charset);
00623 }
00624 
00625 NS_IMETHODIMP nsMsgProtocol::GetContentCharset(nsACString &aContentCharset)
00626 {
00627   aContentCharset.Truncate();
00628   return NS_OK;
00629 }
00630 
00631 NS_IMETHODIMP nsMsgProtocol::SetContentCharset(const nsACString &aContentCharset)
00632 {
00633   NS_WARNING("nsMsgProtocol::SetContentCharset() not implemented");
00634   return NS_ERROR_NOT_IMPLEMENTED;
00635 }
00636 
00637 NS_IMETHODIMP nsMsgProtocol::GetContentLength(PRInt32 * aContentLength)
00638 {
00639   *aContentLength = -1;
00640   return NS_OK;
00641 }
00642 
00643 NS_IMETHODIMP nsMsgProtocol::GetSecurityInfo(nsISupports * *aSecurityInfo)
00644 {
00645   *aSecurityInfo = nsnull;
00646   return NS_ERROR_NOT_IMPLEMENTED;
00647 }
00648 
00649 NS_IMETHODIMP nsMsgProtocol::GetName(nsACString &aName)
00650 {
00651   return NS_ERROR_NOT_IMPLEMENTED;
00652 }
00653 
00654 
00655 NS_IMETHODIMP
00656 nsMsgProtocol::SetContentLength(PRInt32 aContentLength)
00657 {
00658   return NS_ERROR_NOT_IMPLEMENTED;
00659 }
00660  
00661 NS_IMETHODIMP nsMsgProtocol::GetOwner(nsISupports * *aPrincipal)
00662 {
00663   *aPrincipal = mOwner;
00664   NS_IF_ADDREF(*aPrincipal);
00665   return NS_OK;
00666 }
00667 
00668 NS_IMETHODIMP nsMsgProtocol::SetOwner(nsISupports * aPrincipal)
00669 {
00670   mOwner = aPrincipal;
00671   return NS_OK;
00672 }
00673 
00674 NS_IMETHODIMP nsMsgProtocol::GetLoadGroup(nsILoadGroup * *aLoadGroup)
00675 {
00676   *aLoadGroup = m_loadGroup;
00677   NS_IF_ADDREF(*aLoadGroup);
00678   return NS_OK;
00679 }
00680 
00681 NS_IMETHODIMP
00682 nsMsgProtocol::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
00683 {
00684   *aNotificationCallbacks = mCallbacks.get();
00685   NS_IF_ADDREF(*aNotificationCallbacks);
00686   return NS_OK;
00687 }
00688 
00689 NS_IMETHODIMP
00690 nsMsgProtocol::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
00691 {
00692   mCallbacks = aNotificationCallbacks;
00693   return NS_OK;
00694 }
00695 
00696 NS_IMETHODIMP
00697 nsMsgProtocol::OnTransportStatus(nsITransport *transport, nsresult status,
00698                                  PRUint64 progress, PRUint64 progressMax)
00699 {
00700   if ((mLoadFlags & LOAD_BACKGROUND) || !m_url)
00701     return NS_OK;
00702 
00703   // these transport events should not generate any status messages
00704   if (status == nsISocketTransport::STATUS_RECEIVING_FROM ||
00705       status == nsISocketTransport::STATUS_SENDING_TO)
00706     return NS_OK;
00707 
00708   if (!mProgressEventSink)
00709   {
00710     NS_QueryNotificationCallbacks(mCallbacks, m_loadGroup, mProgressEventSink);
00711     if (!mProgressEventSink)
00712       return NS_OK;
00713   }
00714 
00715   nsCAutoString host;
00716   m_url->GetHost(host);
00717 
00718   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url);
00719   if (mailnewsUrl) 
00720   {
00721     nsCOMPtr<nsIMsgIncomingServer> server;
00722     mailnewsUrl->GetServer(getter_AddRefs(server));
00723     if (server)
00724     {
00725       char *realHostName = nsnull;
00726       server->GetRealHostName(&realHostName);
00727       if (realHostName)
00728         host.Adopt(realHostName);
00729     }
00730   }
00731   mProgressEventSink->OnStatus(this, nsnull, status,
00732                                NS_ConvertUTF8toUTF16(host).get());
00733 
00734   return NS_OK;
00735 }
00736 
00738 // From nsIRequest
00740 
00741 NS_IMETHODIMP nsMsgProtocol::IsPending(PRBool *result)
00742 {
00743     *result = PR_TRUE;
00744     return NS_OK; 
00745 }
00746 
00747 NS_IMETHODIMP nsMsgProtocol::GetStatus(nsresult *status)
00748 {
00749   if (m_request)
00750     return m_request->GetStatus(status);
00751 
00752   *status = NS_OK;
00753   return *status;
00754 }
00755 
00756 NS_IMETHODIMP nsMsgProtocol::Cancel(nsresult status)
00757 {
00758   NS_ASSERTION(m_request,"no channel");
00759   if (!m_request)
00760     return NS_ERROR_FAILURE;
00761   
00762   return m_request->Cancel(status);
00763 }
00764 
00765 NS_IMETHODIMP nsMsgProtocol::Suspend()
00766 {
00767   if (m_request)
00768     return m_request->Suspend();
00769 
00770   NS_WARNING("no request to suspend");
00771   return NS_ERROR_NOT_AVAILABLE;
00772 }
00773 
00774 NS_IMETHODIMP nsMsgProtocol::Resume()
00775 {
00776   if (m_request)
00777     return m_request->Resume();
00778 
00779   NS_WARNING("no request to resume");
00780   return NS_ERROR_NOT_AVAILABLE;
00781 }
00782 
00783 nsresult nsMsgProtocol::PostMessage(nsIURI* url, nsIFileSpec *fileSpec)
00784 {
00785     if (!url || !fileSpec) return NS_ERROR_NULL_POINTER;
00786 
00787 #define POST_DATA_BUFFER_SIZE 2048
00788 
00789     // mscott -- this function should be re-written to use the file url code
00790     // so it can be asynch
00791     nsFileSpec afileSpec;
00792     fileSpec->GetFileSpec(&afileSpec);
00793     nsInputFileStream * fileStream = new nsInputFileStream(afileSpec,
00794                                                            PR_RDONLY, 00700);
00795     if (fileStream && fileStream->is_open())
00796     {
00797         PRInt32 amtInBuffer = 0; 
00798         PRBool lastLineWasComplete = PR_TRUE;
00799         
00800         PRBool quoteLines = PR_TRUE;  // it is always true but I'd like to
00801                                       // generalize this function and then it
00802                                       // might not be 
00803         char buffer[POST_DATA_BUFFER_SIZE];
00804         
00805         if (quoteLines /* || add_crlf_to_line_endings */)
00806         {
00807             char *line;
00808             char * b = buffer;
00809             PRInt32 bsize = POST_DATA_BUFFER_SIZE;
00810             amtInBuffer =  0;
00811             do {
00812                 lastLineWasComplete = PR_TRUE;
00813                 PRInt32 L = 0;
00814                 if (fileStream->eof())
00815                 {
00816                     line = nsnull;
00817                     break;
00818                 }
00819                 
00820                 if (!fileStream->readline(b, bsize-5)) 
00821                     lastLineWasComplete = PR_FALSE;
00822                 line = b;
00823                 
00824                 L = PL_strlen(line);
00825                 
00826                 /* escape periods only if quote_lines_p is set
00827                  */
00828                 if (quoteLines && lastLineWasComplete && line[0] == '.')
00829                 {
00830                     /* This line begins with "." so we need to quote it
00831                        by adding another "." to the beginning of the line.
00832                        */
00833                     PRInt32 i;
00834                     line[L+1] = 0;
00835                     for (i = L; i > 0; i--)
00836                         line[i] = line[i-1];
00837                     L++;
00838                 }
00839                 
00840                 if (!lastLineWasComplete || (L > 1 && line[L-2] == nsCRT::CR &&
00841                                              line[L-1] == nsCRT::LF))
00842                 {
00843                     /* already ok */
00844                 }
00845                 else if(L > 0 /* && (line[L-1] == nsCRT::LF || line[L-1] == nsCRT::CR) */)
00846                 {
00847                     /* only add the crlf if required
00848                      * we still need to do all the
00849                      * if comparisons here to know
00850                      * if the line was complete
00851                      */
00852                     if(/* add_crlf_to_line_endings */ PR_TRUE)
00853                     {
00854                         /* Change newline to CRLF. */
00855                           line[L++] = nsCRT::CR;
00856                           line[L++] = nsCRT::LF;
00857                           line[L] = 0;
00858                     }
00859                 }
00860                 else if (L == 0 && !fileStream->eof()
00861                          /* && add_crlf_to_line_endings */)
00862                 {
00863                     // jt ** empty line; output CRLF
00864                     line[L++] = nsCRT::CR;
00865                     line[L++] = nsCRT::LF;
00866                     line[L] = 0;
00867                 }
00868                 
00869                 bsize -= L;
00870                 b += L;
00871                 amtInBuffer += L;
00872                 // test hack by mscott. If our buffer is almost full, then
00873                 // send it off & reset ourselves 
00874                 // to make more room.
00875                 if (bsize < 100) // i chose 100 arbitrarily.
00876                 {
00877                     if (*buffer)
00878                         SendData(url, buffer);
00879                     buffer[0] = '\0';
00880                     b = buffer; // reset buffer
00881                     bsize = POST_DATA_BUFFER_SIZE;
00882                 }
00883                 
00884             } while (line /* && bsize > 100 */);
00885         }
00886         
00887         SendData(url, buffer); 
00888         delete fileStream;
00889     }
00890     return NS_OK;
00891 }
00892 
00893 nsresult nsMsgProtocol::DoGSSAPIStep1(const char *service, const char *username, nsCString &response)
00894 {
00895     nsresult rv;
00896 
00897     // if this fails, then it means that we cannot do GSSAPI SASL.
00898     m_authModule = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sasl-gssapi", &rv);
00899     NS_ENSURE_SUCCESS(rv,rv);
00900 
00901     m_authModule->Init(service, nsIAuthModule::REQ_DEFAULT, nsnull, NS_ConvertUTF8toUCS2(username).get(), nsnull);
00902 
00903     void *outBuf;
00904     PRUint32 outBufLen;
00905     rv = m_authModule->GetNextToken((void *)nsnull, 0, &outBuf, &outBufLen);
00906     if (NS_SUCCEEDED(rv) && outBuf)
00907     {
00908         char *base64Str = PL_Base64Encode((char *)outBuf, outBufLen, nsnull);
00909         if (base64Str)
00910             response.Adopt(base64Str);
00911         else 
00912             rv = NS_ERROR_OUT_OF_MEMORY;
00913         nsMemory::Free(outBuf);
00914     }
00915 
00916     return rv;
00917 }
00918 
00919 nsresult nsMsgProtocol::DoGSSAPIStep2(nsCString &commandResponse, nsCString &response)
00920 {
00921     nsresult rv;
00922     void *inBuf, *outBuf;
00923     PRUint32 inBufLen, outBufLen;
00924     PRUint32 len = commandResponse.Length();
00925 
00926     // Cyrus SASL may send us zero length tokens (grrrr)
00927     if (len > 0) {
00928         // decode into the input secbuffer
00929         inBufLen = (len * 3)/4;      // sufficient size (see plbase64.h)
00930         inBuf = nsMemory::Alloc(inBufLen);
00931         if (!inBuf)
00932             return NS_ERROR_OUT_OF_MEMORY;
00933 
00934         // strip off any padding (see bug 230351)
00935         const char *challenge = commandResponse.get();
00936         while (challenge[len - 1] == '=')
00937             len--;
00938 
00939         // We need to know the exact length of the decoded string to give to
00940         // the GSSAPI libraries. But NSPR's base64 routine doesn't seem capable
00941         // of telling us that. So, we figure it out for ourselves.
00942 
00943         // For every 4 characters, add 3 to the destination
00944         // If there are 3 remaining, add 2
00945         // If there are 2 remaining, add 1
00946         // 1 remaining is an error
00947         inBufLen = (len / 4)*3 + ((len % 4 == 3)?2:0) + ((len % 4 == 2)?1:0);
00948 
00949         rv = (PL_Base64Decode(challenge, len, (char *)inBuf))
00950              ? m_authModule->GetNextToken(inBuf, inBufLen, &outBuf, &outBufLen)
00951              : NS_ERROR_FAILURE;
00952 
00953         nsMemory::Free(inBuf);
00954     }
00955     else 
00956     {
00957         rv = m_authModule->GetNextToken(NULL, 0, &outBuf, &outBufLen);
00958     }
00959     if (NS_SUCCEEDED(rv)) 
00960     {
00961         // And in return, we may need to send Cyrus zero length tokens back
00962         if (outBuf) 
00963         {
00964             char *base64Str = PL_Base64Encode((char *)outBuf, outBufLen, nsnull);
00965             if (base64Str)
00966                 response.Adopt(base64Str);
00967             else 
00968                 rv = NS_ERROR_OUT_OF_MEMORY;
00969         }
00970         else
00971             response.Adopt((char *)nsMemory::Clone("",1));
00972     }
00973 
00974     return rv;
00975 }
00976 
00977 nsresult nsMsgProtocol::DoNtlmStep1(const char *username, const char *password, nsCString &response)
00978 {
00979     nsresult rv;
00980 
00981     m_authModule = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm", &rv);
00982     // if this fails, then it means that we cannot do NTLM auth.
00983     if (NS_FAILED(rv) || !m_authModule)
00984         return rv;
00985 
00986     m_authModule->Init(nsnull, 0, nsnull, NS_ConvertUTF8toUCS2(username).get(),
00987                        NS_ConvertUTF8toUCS2(password).get());
00988 
00989     void *outBuf;
00990     PRUint32 outBufLen;
00991     rv = m_authModule->GetNextToken((void *)nsnull, 0, &outBuf, &outBufLen);
00992     if (NS_SUCCEEDED(rv) && outBuf)
00993     {
00994         char *base64Str = PL_Base64Encode((char *)outBuf, outBufLen, nsnull);
00995         if (base64Str)
00996           response.Adopt(base64Str);
00997         else 
00998           rv = NS_ERROR_OUT_OF_MEMORY;
00999         nsMemory::Free(outBuf);
01000     }
01001 
01002     return rv;
01003 }
01004 
01005 nsresult nsMsgProtocol::DoNtlmStep2(nsCString &commandResponse, nsCString &response)
01006 {
01007     nsresult rv;
01008     void *inBuf, *outBuf;
01009     PRUint32 inBufLen, outBufLen;
01010     PRUint32 len = commandResponse.Length();
01011 
01012     // decode into the input secbuffer
01013     inBufLen = (len * 3)/4;      // sufficient size (see plbase64.h)
01014     inBuf = nsMemory::Alloc(inBufLen);
01015     if (!inBuf)
01016         return NS_ERROR_OUT_OF_MEMORY;
01017 
01018     // strip off any padding (see bug 230351)
01019     const char *challenge = commandResponse.get();
01020     while (challenge[len - 1] == '=')
01021         len--;
01022 
01023     rv = (PL_Base64Decode(challenge, len, (char *)inBuf))
01024          ? m_authModule->GetNextToken(inBuf, inBufLen, &outBuf, &outBufLen)
01025          : NS_ERROR_FAILURE;
01026 
01027     nsMemory::Free(inBuf);
01028     if (NS_SUCCEEDED(rv) && outBuf)
01029     {
01030         char *base64Str = PL_Base64Encode((char *)outBuf, outBufLen, nsnull);
01031         if (base64Str)
01032           response.Adopt(base64Str);
01033         else 
01034           rv = NS_ERROR_OUT_OF_MEMORY;
01035     }
01036 
01037     if (NS_FAILED(rv))
01038         response = "*";
01039 
01040     return rv;
01041 }
01042 
01044 // nsMsgAsyncWriteProtocol subclass and related helper classes
01046 
01047 class nsMsgProtocolStreamProvider : public nsIOutputStreamCallback 
01048 {
01049 public:
01050     NS_DECL_ISUPPORTS
01051 
01052     nsMsgProtocolStreamProvider() { }
01053     virtual ~nsMsgProtocolStreamProvider() {}
01054 
01055     void Init(nsMsgAsyncWriteProtocol *aProtInstance, nsIInputStream *aInputStream)
01056     {
01057       mMsgProtocol = aProtInstance;
01058       mInStream = aInputStream;
01059     }
01060 
01061     //
01062     // nsIOutputStreamCallback implementation ...
01063     //
01064     NS_IMETHODIMP OnOutputStreamReady(nsIAsyncOutputStream *aOutStream)
01065     { 
01066         NS_ASSERTION(mInStream, "not initialized");
01067 
01068         nsresult rv;
01069         PRUint32 avail;
01070 
01071         // Write whatever is available in the pipe. If the pipe is empty, then
01072         // return NS_BASE_STREAM_WOULD_BLOCK; we will resume the write when there
01073         // is more data.
01074 
01075         rv = mInStream->Available(&avail);
01076         if (NS_FAILED(rv)) return rv;
01077 
01078         if (avail == 0) 
01079         {
01080           // ok, stop writing...
01081           mMsgProtocol->mSuspendedWrite = PR_TRUE;
01082           return NS_OK;
01083         }
01084 
01085         PRUint32 bytesWritten;
01086         rv =  aOutStream->WriteFrom(mInStream, PR_MIN(avail, 4096), &bytesWritten);
01087         // if were full at the time, the input stream may be backed up and we need to read any remains from the last ODA call
01088         // before we'll get more ODA calls
01089         if (mMsgProtocol->mSuspendedRead)
01090           mMsgProtocol->UnblockPostReader();
01091 
01092         mMsgProtocol->UpdateProgress(bytesWritten);
01093 
01094         // try to write again...
01095         if (NS_SUCCEEDED(rv))
01096           rv = aOutStream->AsyncWait(this, 0, 0, mMsgProtocol->mProviderEventQ);
01097 
01098         NS_ASSERTION(NS_SUCCEEDED(rv) || rv == NS_BINDING_ABORTED, "unexpected error writing stream");
01099         return NS_OK;
01100     }
01101 
01102     
01103 protected:
01104   nsMsgAsyncWriteProtocol * mMsgProtocol;
01105   nsCOMPtr<nsIInputStream>  mInStream;
01106 };
01107 
01108 // XXX this probably doesn't need to be threadsafe
01109 NS_IMPL_THREADSAFE_ISUPPORTS1(nsMsgProtocolStreamProvider,
01110                               nsIOutputStreamCallback)
01111 
01112 class nsMsgFilePostHelper : public nsIStreamListener 
01113 {
01114 public:
01115   NS_DECL_ISUPPORTS
01116   NS_DECL_NSIREQUESTOBSERVER
01117   NS_DECL_NSISTREAMLISTENER
01118 
01119   nsMsgFilePostHelper() { mSuspendedPostFileRead = PR_FALSE;}
01120   nsresult Init(nsIOutputStream * aOutStream, nsMsgAsyncWriteProtocol * aProtInstance, nsIFile *aFileToPost);
01121   virtual ~nsMsgFilePostHelper() {}
01122   nsCOMPtr<nsIRequest> mPostFileRequest;
01123   PRBool mSuspendedPostFileRead;
01124   void CloseSocket() { mProtInstance = nsnull; }
01125 protected:
01126   nsCOMPtr<nsIOutputStream> mOutStream;
01127   nsMsgAsyncWriteProtocol * mProtInstance;
01128 };
01129 
01130 NS_IMPL_THREADSAFE_ADDREF(nsMsgFilePostHelper)
01131 NS_IMPL_THREADSAFE_RELEASE(nsMsgFilePostHelper)
01132 
01133 NS_INTERFACE_MAP_BEGIN(nsMsgFilePostHelper)
01134   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
01135   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
01136 NS_INTERFACE_MAP_END_THREADSAFE
01137 
01138 nsresult nsMsgFilePostHelper::Init(nsIOutputStream * aOutStream, nsMsgAsyncWriteProtocol * aProtInstance, nsIFile *aFileToPost)
01139 {
01140   nsresult rv = NS_OK;
01141   mOutStream = aOutStream;
01142   mProtInstance = aProtInstance; // mscott work out ref counting issue
01143 
01144   nsCOMPtr<nsIInputStream> stream;
01145   rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), aFileToPost);
01146   if (NS_FAILED(rv)) return rv;
01147 
01148   nsCOMPtr<nsIInputStreamPump> pump;
01149   rv = NS_NewInputStreamPump(getter_AddRefs(pump), stream);
01150   if (NS_FAILED(rv)) return rv;
01151 
01152   rv = pump->AsyncRead(this, nsnull);
01153   if (NS_FAILED(rv)) return rv;
01154 
01155   mPostFileRequest = pump;
01156   return NS_OK;
01157 }
01158 
01159 NS_IMETHODIMP nsMsgFilePostHelper::OnStartRequest(nsIRequest * aChannel, nsISupports *ctxt)
01160 {
01161   return NS_OK;
01162 }
01163 
01164 NS_IMETHODIMP nsMsgFilePostHelper::OnStopRequest(nsIRequest * aChannel, nsISupports *ctxt, nsresult aStatus)
01165 {
01166   if (!mProtInstance) return NS_OK; 
01167 
01168   if (!mSuspendedPostFileRead)
01169     mProtInstance->PostDataFinished();
01170 
01171   mSuspendedPostFileRead = PR_FALSE;
01172   mProtInstance->mFilePostHelper = nsnull;
01173   return NS_OK;
01174 }
01175 
01176 NS_IMETHODIMP nsMsgFilePostHelper::OnDataAvailable(nsIRequest * /* aChannel */, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
01177 {
01178   if (!mProtInstance) return NS_OK; 
01179 
01180   if (mSuspendedPostFileRead)
01181   {
01182     mProtInstance->UpdateSuspendedReadBytes(count, mProtInstance->mInsertPeriodRequired);
01183     return NS_OK;
01184   }
01185 
01186   mProtInstance->ProcessIncomingPostData(inStr, count);
01187 
01188   if (mProtInstance->mSuspendedWrite) 
01189   {
01190     // if we got here then we had suspended the write 'cause we didn't have anymore
01191     // data to write (i.e. the pipe went empty). So resume the channel to kick
01192     // things off again.
01193     mProtInstance->mSuspendedWrite = PR_FALSE;
01194     mProtInstance->mAsyncOutStream->AsyncWait(mProtInstance->mProvider, 0, 0,
01195                                               mProtInstance->mProviderEventQ);
01196   } 
01197 
01198   return NS_OK;
01199 }
01200 
01201 NS_IMPL_ADDREF_INHERITED(nsMsgAsyncWriteProtocol, nsMsgProtocol)
01202 NS_IMPL_RELEASE_INHERITED(nsMsgAsyncWriteProtocol, nsMsgProtocol)
01203 
01204 NS_INTERFACE_MAP_BEGIN(nsMsgAsyncWriteProtocol)
01205 NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol)
01206 
01207 nsMsgAsyncWriteProtocol::nsMsgAsyncWriteProtocol(nsIURI * aURL) : nsMsgProtocol(aURL)
01208 {
01209   mSuspendedWrite = PR_FALSE;
01210   mSuspendedReadBytes = 0;
01211   mSuspendedRead = PR_FALSE;
01212   mInsertPeriodRequired = PR_FALSE;
01213   mGenerateProgressNotifications = PR_FALSE;
01214   mSuspendedReadBytesPostPeriod = 0;
01215   mFilePostHelper = nsnull;
01216 }
01217 
01218 nsMsgAsyncWriteProtocol::~nsMsgAsyncWriteProtocol()
01219 {}
01220 
01221 NS_IMETHODIMP nsMsgAsyncWriteProtocol::Cancel(nsresult status)
01222 {
01223   mGenerateProgressNotifications = PR_FALSE;
01224 
01225   if (m_request)
01226     m_request->Cancel(status);
01227 
01228   if (mAsyncOutStream)
01229     mAsyncOutStream->CloseWithStatus(status);
01230 
01231   return NS_OK;
01232 }
01233 
01234 nsresult nsMsgAsyncWriteProtocol::PostMessage(nsIURI* url, nsIFileSpec *fileSpec)
01235 {
01236   // convert the file spec into a nsIFile....
01237   nsFileSpec spec;
01238   fileSpec->GetFileSpec(&spec);
01239 
01240   nsCOMPtr<nsILocalFile> file;
01241   NS_FileSpecToIFile(&spec, getter_AddRefs(file));
01242 
01243   nsCOMPtr<nsIStreamListener> listener;
01244   NS_NEWXPCOM(listener, nsMsgFilePostHelper);
01245   if (!listener) return NS_ERROR_OUT_OF_MEMORY;
01246 
01247   // be sure to initialize some state before posting
01248   mSuspendedReadBytes = 0;
01249   mNumBytesPosted = 0;
01250   fileSpec->GetFileSize(&mFilePostSize);
01251   mSuspendedRead = PR_FALSE;
01252   mInsertPeriodRequired = PR_FALSE;
01253   mSuspendedReadBytesPostPeriod = 0;
01254   mGenerateProgressNotifications = PR_TRUE;
01255 
01256   mFilePostHelper = NS_STATIC_CAST(nsMsgFilePostHelper*,NS_STATIC_CAST(nsIStreamListener*, listener));
01257 
01258   NS_STATIC_CAST(nsMsgFilePostHelper*,NS_STATIC_CAST(nsIStreamListener*, listener))->Init(m_outputStream, this, file);
01259 
01260   return NS_OK;
01261 }
01262 
01263 nsresult nsMsgAsyncWriteProtocol::SuspendPostFileRead()
01264 {
01265 #ifdef DEBUG_mscott
01266   printf("suspending post read during send\n");
01267 #endif
01268   if (mFilePostHelper && !mFilePostHelper->mSuspendedPostFileRead)
01269   {
01270     // uhoh we need to pause reading in the file until we get unblocked...
01271     mFilePostHelper->mPostFileRequest->Suspend();
01272     mFilePostHelper->mSuspendedPostFileRead = PR_TRUE;
01273   }
01274 
01275   return NS_OK;
01276 }
01277 
01278 nsresult nsMsgAsyncWriteProtocol::ResumePostFileRead()
01279 {
01280 #ifdef DEBUG_mscott
01281   printf("resuming post read during send\n");
01282 #endif
01283 
01284   if (mFilePostHelper)
01285   {
01286     if (mFilePostHelper->mSuspendedPostFileRead)
01287     {
01288       mFilePostHelper->mPostFileRequest->Resume();
01289       mFilePostHelper->mSuspendedPostFileRead = PR_FALSE;
01290     }
01291   }
01292   else // we must be done with the download so send the '.'
01293   {
01294     PostDataFinished(); 
01295   }
01296   
01297   return NS_OK;
01298 }
01299 
01300 nsresult nsMsgAsyncWriteProtocol::UpdateSuspendedReadBytes(PRUint32 aNewBytes, PRBool aAddToPostPeriodByteCount)
01301 {
01302   // depending on our current state, we'll either add aNewBytes to mSuspendedReadBytes
01303   // or mSuspendedReadBytesAfterPeriod. 
01304 
01305   mSuspendedRead = PR_TRUE;
01306   if (aAddToPostPeriodByteCount) 
01307     mSuspendedReadBytesPostPeriod += aNewBytes;
01308   else
01309     mSuspendedReadBytes += aNewBytes;
01310 
01311   return NS_OK;
01312 }
01313 
01314 nsresult nsMsgAsyncWriteProtocol::PostDataFinished()
01315 {
01316   SendData(nsnull, "." CRLF);
01317   mGenerateProgressNotifications = PR_FALSE;
01318   mPostDataStream = nsnull;
01319   return NS_OK;
01320 }
01321 
01322 nsresult nsMsgAsyncWriteProtocol::ProcessIncomingPostData(nsIInputStream *inStr, PRUint32 count)
01323 {
01324   if (!m_socketIsOpen) return NS_OK; // kick out if the socket was canceled
01325 
01326   // We need to quote any '.' that occur at the beginning of a line.
01327   // but I don't want to waste time reading out the data into a buffer and searching
01328   // let's try to leverage nsIBufferedInputStream and see if we can "peek" into the 
01329   // current contents for this particular case.
01330 
01331   nsCOMPtr<nsISearchableInputStream> bufferInputStr = do_QueryInterface(inStr);
01332   NS_ASSERTION(bufferInputStr, "i made a wrong assumption about the type of stream we are getting");
01333   NS_ASSERTION(mSuspendedReadBytes == 0, "oops, I missed something");
01334 
01335   if (!mPostDataStream) mPostDataStream = inStr;
01336 
01337   if (bufferInputStr)
01338   { 
01339     PRUint32 amountWritten;
01340 
01341     while (count > 0)
01342     {
01343       PRBool found = PR_FALSE;
01344       PRUint32 offset = 0;
01345       bufferInputStr->Search("\012.", PR_TRUE,  &found, &offset); // LF.
01346 
01347       if (!found || offset > count)
01348       {
01349         // push this data into the output stream
01350         m_outputStream->WriteFrom(inStr, count, &amountWritten);
01351         // store any remains which need read out at a later date
01352         if (count > amountWritten) // stream will block
01353         {
01354           UpdateSuspendedReadBytes(count - amountWritten, PR_FALSE);
01355           SuspendPostFileRead();
01356         }
01357         break;
01358       }
01359       else
01360       {
01361         // count points to the LF in a LF followed by a '.'
01362         // go ahead and write up to offset..
01363         m_outputStream->WriteFrom(inStr, offset + 1, &amountWritten);
01364         count -= amountWritten;
01365         if (offset+1 > amountWritten)
01366         {
01367           UpdateSuspendedReadBytes(offset+1 - amountWritten, PR_FALSE);
01368           mInsertPeriodRequired = PR_TRUE;
01369           UpdateSuspendedReadBytes(count, mInsertPeriodRequired);
01370           SuspendPostFileRead();
01371           break;
01372         }
01373 
01374         // write out the extra '.'
01375         m_outputStream->Write(".", 1, &amountWritten);
01376         if (amountWritten != 1)
01377         {
01378           mInsertPeriodRequired = PR_TRUE;
01379           // once we do write out the '.',  if we are now blocked we need to remember the remaining count that comes
01380           // after the '.' so we can perform processing on that once we become unblocked.
01381           UpdateSuspendedReadBytes(count, mInsertPeriodRequired);
01382           SuspendPostFileRead();
01383           break;
01384         }
01385       }
01386     } // while count > 0
01387   }
01388 
01389   return NS_OK;
01390 }
01391 nsresult nsMsgAsyncWriteProtocol::UnblockPostReader()
01392 {
01393   PRUint32 amountWritten = 0;
01394   
01395   if (!m_socketIsOpen) return NS_OK; // kick out if the socket was canceled
01396 
01397   if (mSuspendedRead)
01398   {
01399     // (1) attempt to write out any remaining read bytes we need in order to unblock the reader
01400     if (mSuspendedReadBytes > 0 && mPostDataStream)
01401     {
01402       PRUint32 avail = 0;
01403       mPostDataStream->Available(&avail);
01404 
01405       m_outputStream->WriteFrom(mPostDataStream, PR_MIN(avail, mSuspendedReadBytes), &amountWritten);
01406       // hmm sometimes my mSuspendedReadBytes is getting out of whack...so for now, reset it
01407       // if necessary.
01408       if (mSuspendedReadBytes > avail)
01409         mSuspendedReadBytes = avail;
01410 
01411       if (mSuspendedReadBytes > amountWritten)
01412         mSuspendedReadBytes -= amountWritten;
01413       else
01414         mSuspendedReadBytes = 0;
01415     }
01416 
01417     // (2) if we are now unblocked, and we need to insert a '.' then do so now...
01418     if (mInsertPeriodRequired && mSuspendedReadBytes == 0)
01419     {
01420       amountWritten = 0;
01421       m_outputStream->Write(".", 1, &amountWritten);
01422       if (amountWritten == 1) // if we succeeded then clear pending '.' flag
01423         mInsertPeriodRequired = PR_FALSE;
01424     }
01425 
01426     // (3) if we inserted a '.' and we still have bytes after the '.' which need processed before the stream is unblocked
01427     // then fake an ODA call to handle this now...
01428     if (!mInsertPeriodRequired && mSuspendedReadBytesPostPeriod > 0)
01429     {
01430       // these bytes actually need processed for extra '.''s.....
01431       PRUint32 postbytes = mSuspendedReadBytesPostPeriod;
01432       mSuspendedReadBytesPostPeriod = 0;
01433       ProcessIncomingPostData(mPostDataStream, postbytes);      
01434     }
01435 
01436     // (4) determine if we are out of the suspended read state...
01437     if (mSuspendedReadBytes == 0 && !mInsertPeriodRequired && mSuspendedReadBytesPostPeriod == 0)
01438     {
01439       mSuspendedRead = PR_FALSE;
01440       ResumePostFileRead();
01441     }
01442 
01443   } // if we are in the suspended read state
01444 
01445   return NS_OK;
01446 }
01447 
01448 nsresult nsMsgAsyncWriteProtocol::SetupTransportState()
01449 {
01450   nsresult rv = NS_OK;
01451   
01452   if (!m_outputStream && m_transport)
01453   {
01454     // first create a pipe which we'll use to write the data we want to send
01455     // into. 
01456     rv = NS_NewPipe(getter_AddRefs(mInStream), getter_AddRefs(m_outputStream),
01457       1024,  // segmentSize
01458       1024*8, // maxSize
01459       PR_TRUE, 
01460       PR_TRUE);
01461     
01462     rv = NS_GetCurrentEventQ(getter_AddRefs(mProviderEventQ));
01463     if (NS_FAILED(rv)) return rv;
01464     
01465     nsMsgProtocolStreamProvider *provider;
01466     NS_NEWXPCOM(provider, nsMsgProtocolStreamProvider);
01467     if (!provider) return NS_ERROR_OUT_OF_MEMORY;
01468     
01469     provider->Init(this, mInStream);
01470     mProvider = provider; // ADDREF
01471     
01472     nsCOMPtr<nsIOutputStream> stream;
01473     rv = m_transport->OpenOutputStream(0, 0, 0, getter_AddRefs(stream));
01474     if (NS_FAILED(rv)) return rv;
01475     
01476     mAsyncOutStream = do_QueryInterface(stream, &rv);
01477     if (NS_FAILED(rv)) return rv;
01478     
01479     // wait for the output stream to become writable
01480     rv = mAsyncOutStream->AsyncWait(mProvider, 0, 0, mProviderEventQ);
01481        } // if m_transport
01482 
01483        return rv;
01484 }
01485 
01486 nsresult nsMsgAsyncWriteProtocol::CloseSocket()
01487 {
01488   nsresult rv = NS_OK;
01489   if (mAsyncOutStream)
01490     mAsyncOutStream->CloseWithStatus(NS_BINDING_ABORTED);
01491 
01492   nsMsgProtocol::CloseSocket(); 
01493 
01494   if (mFilePostHelper)
01495   {
01496     mFilePostHelper->CloseSocket();
01497     mFilePostHelper = nsnull;
01498   }
01499 
01500   mAsyncOutStream = 0;
01501   mProvider = 0;
01502   mProviderEventQ = 0;
01503        return rv;
01504 }
01505 
01506 void nsMsgAsyncWriteProtocol::UpdateProgress(PRUint32 aNewBytes)
01507 {
01508   if (!mGenerateProgressNotifications) return;
01509 
01510   mNumBytesPosted += aNewBytes;
01511   if (mFilePostSize > 0)
01512   {
01513     nsCOMPtr <nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(m_url);
01514     if (!mailUrl) return;
01515 
01516     nsCOMPtr<nsIMsgStatusFeedback> statusFeedback;
01517     mailUrl->GetStatusFeedback(getter_AddRefs(statusFeedback));
01518     if (!statusFeedback) return;
01519 
01520     nsCOMPtr<nsIWebProgressListener> webProgressListener (do_QueryInterface(statusFeedback));
01521     if (!webProgressListener) return;
01522 
01523     // XXX not sure if m_request is correct here
01524     webProgressListener->OnProgressChange(nsnull, m_request, mNumBytesPosted, mFilePostSize, mNumBytesPosted, mFilePostSize);
01525   }
01526 
01527   return;
01528 }
01529     
01530 
01531 PRInt32 nsMsgAsyncWriteProtocol::SendData(nsIURI * aURL, const char * dataBuffer, PRBool aSuppressLogging)
01532 {
01533   PRUint32 len = strlen(dataBuffer);
01534   PRUint32 cnt;
01535   nsresult rv = m_outputStream->Write(dataBuffer, len, &cnt);
01536   if (NS_SUCCEEDED(rv) && len==cnt) 
01537   {
01538     if (mSuspendedWrite) 
01539     {
01540       // if we got here then we had suspended the write 'cause we didn't have anymore
01541       // data to write (i.e. the pipe went empty). So resume the channel to kick
01542       // things off again.
01543       mSuspendedWrite = PR_FALSE;
01544       mAsyncOutStream->AsyncWait(mProvider, 0, 0, mProviderEventQ);
01545     } 
01546     return NS_OK;
01547   }
01548   else
01549     return NS_ERROR_FAILURE;
01550 }
01551 
01552 #define MSGS_URL    "chrome://messenger/locale/messenger.properties"
01553 
01554 PRUnichar *FormatStringWithHostNameByID(PRInt32 stringID, nsIMsgMailNewsUrl *msgUri)
01555 {
01556   if (! msgUri)
01557     return nsnull;
01558 
01559   nsresult rv;
01560   nsCOMPtr <nsIStringBundle> sBundle = nsnull;
01561 
01562   nsCOMPtr<nsIStringBundleService> sBundleService = 
01563           do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
01564   if (NS_FAILED(rv) || (! sBundleService)) 
01565     return nsnull;
01566 
01567   rv = sBundleService->CreateBundle(MSGS_URL, getter_AddRefs(sBundle));
01568   if (NS_FAILED(rv)) 
01569     return nsnull;
01570 
01571   PRUnichar *ptrv = nsnull;
01572   nsXPIDLCString hostName;
01573   nsCOMPtr<nsIMsgIncomingServer> server;
01574   rv = msgUri->GetServer(getter_AddRefs(server));
01575 
01576   if (NS_SUCCEEDED(rv) && server)
01577    rv = server->GetRealHostName(getter_Copies(hostName));
01578 
01579   nsAutoString hostStr;
01580   hostStr.AssignWithConversion(hostName.get());
01581   const PRUnichar *params[] = { hostStr.get() };
01582   rv = sBundle->FormatStringFromID(stringID, params, 1, &ptrv);
01583   if (NS_FAILED(rv)) 
01584     return nsnull;
01585 
01586   return (ptrv);
01587 }
01588 
01589 // vim: ts=2 sw=2