Back to index

lightning-sunbird  0.9+nobinonly
nsFingerChannel.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Brian Ryner.
00020  * Portions created by the Initial Developer are Copyright (C) 2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Brian Ryner <bryner@brianryner.com>
00025  *   Darin Fisher <darin@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 // finger implementation
00042 
00043 #include "nsFingerChannel.h"
00044 #include "nsIServiceManager.h"
00045 #include "nsILoadGroup.h"
00046 #include "nsIInterfaceRequestor.h"
00047 #include "nsIInterfaceRequestorUtils.h"
00048 #include "nsXPIDLString.h"
00049 #include "nsISocketTransportService.h"
00050 #include "nsIStringStream.h"
00051 #include "nsMimeTypes.h"
00052 #include "nsIStreamConverterService.h"
00053 #include "nsITXTToHTMLConv.h"
00054 #include "nsIProgressEventSink.h"
00055 #include "nsEventQueueUtils.h"
00056 #include "nsNetUtil.h"
00057 #include "nsCRT.h"
00058 
00059 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
00060 static NS_DEFINE_CID(kStreamConverterServiceCID, NS_STREAMCONVERTERSERVICE_CID);
00061 
00062 #define BUFFER_SEG_SIZE (4*1024)
00063 #define BUFFER_MAX_SIZE (64*1024)
00064 
00065 // nsFingerChannel methods
00066 nsFingerChannel::nsFingerChannel()
00067     : mLoadFlags(LOAD_NORMAL)
00068     , mStatus(NS_OK)
00069     , mPort(-1)
00070 {
00071 }
00072 
00073 nsFingerChannel::~nsFingerChannel()
00074 {
00075 }
00076 
00077 NS_IMPL_ISUPPORTS4(nsFingerChannel, 
00078                    nsIChannel, 
00079                    nsIRequest,
00080                    nsIStreamListener, 
00081                    nsIRequestObserver)
00082 
00083 nsresult
00084 nsFingerChannel::Init(nsIURI *uri, nsIProxyInfo *proxyInfo)
00085 {
00086     nsresult rv;
00087     nsCAutoString autoBuffer;
00088 
00089     NS_ASSERTION(uri, "no uri");
00090 
00091     mURI = uri;
00092     mProxyInfo = proxyInfo;
00093 
00094 //  For security reasons, we do not allow the user to specify a
00095 //  non-default port for finger: URL's.
00096 
00097     mPort = FINGER_PORT;
00098 
00099     rv = mURI->GetPath(autoBuffer); // autoBuffer = user@host
00100     if (NS_FAILED(rv)) return rv;
00101 
00102     // Now parse out the user and host
00103     const char* buf = autoBuffer.get();
00104     const char* pos = strchr(buf, '@');
00105 
00106     // Catch the case of just the host being given
00107     if (!pos) {
00108         mUser.Truncate();
00109         mHost.Assign(buf);
00110     } else {
00111         mUser.Assign(buf, pos-buf);
00112         mHost.Assign(pos+1); // ignore '@'
00113     }
00114 
00115     if (mHost.IsEmpty())
00116         return NS_ERROR_MALFORMED_URI;
00117 
00118     mContentType.AssignLiteral(TEXT_HTML); // expected content-type
00119     return NS_OK;
00120 }
00121 
00123 // nsIRequest methods:
00124 
00125 NS_IMETHODIMP
00126 nsFingerChannel::GetName(nsACString &result)
00127 {
00128     return mURI->GetSpec(result);
00129 }
00130 
00131 NS_IMETHODIMP
00132 nsFingerChannel::IsPending(PRBool *result)
00133 {
00134     *result = (mPump != nsnull);
00135     return NS_OK;
00136 }
00137 
00138 NS_IMETHODIMP
00139 nsFingerChannel::GetStatus(nsresult *status)
00140 {
00141     if (NS_SUCCEEDED(mStatus) && mPump)
00142         mPump->GetStatus(status);
00143     else
00144         *status = mStatus;
00145     return NS_OK;
00146 }
00147 
00148 NS_IMETHODIMP
00149 nsFingerChannel::Cancel(nsresult status)
00150 {
00151     NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
00152 
00153     mStatus = status;
00154     if (mPump)
00155         mPump->Cancel(status);
00156 
00157     return NS_ERROR_UNEXPECTED;
00158 }
00159 
00160 NS_IMETHODIMP
00161 nsFingerChannel::Suspend()
00162 {
00163     if (mPump)
00164         return mPump->Suspend();
00165     return NS_ERROR_UNEXPECTED;
00166 }
00167 
00168 NS_IMETHODIMP
00169 nsFingerChannel::Resume()
00170 {
00171     if (mPump)
00172         return mPump->Resume();
00173     return NS_ERROR_UNEXPECTED;
00174 }
00175 
00177 // nsIChannel methods:
00178 
00179 NS_IMETHODIMP
00180 nsFingerChannel::GetOriginalURI(nsIURI* *aURI)
00181 {
00182     *aURI = mOriginalURI ? mOriginalURI : mURI;
00183     NS_ADDREF(*aURI);
00184     return NS_OK;
00185 }
00186 
00187 NS_IMETHODIMP
00188 nsFingerChannel::SetOriginalURI(nsIURI* aURI)
00189 {
00190     mOriginalURI = aURI;
00191     return NS_OK;
00192 }
00193 
00194 NS_IMETHODIMP
00195 nsFingerChannel::GetURI(nsIURI* *aURI)
00196 {
00197     *aURI = mURI;
00198     NS_IF_ADDREF(*aURI);
00199     return NS_OK;
00200 }
00201 
00202 NS_IMETHODIMP
00203 nsFingerChannel::Open(nsIInputStream **_retval)
00204 {
00205     return NS_ImplementChannelOpen(this, _retval);
00206 }
00207 
00208 NS_IMETHODIMP
00209 nsFingerChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
00210 {
00211     nsresult rv = NS_CheckPortSafety(mPort, "finger");
00212     if (NS_FAILED(rv)) return rv;
00213 
00214     nsCOMPtr<nsIEventQueue> eventQ;
00215     rv = NS_GetCurrentEventQ(getter_AddRefs(eventQ));
00216     if (NS_FAILED(rv)) return rv;
00217 
00218     //
00219     // create transport
00220     //
00221     nsCOMPtr<nsISocketTransportService> sts = 
00222              do_GetService(kSocketTransportServiceCID, &rv);
00223     if (NS_FAILED(rv)) return rv;
00224 
00225     rv = sts->CreateTransport(nsnull, 0, mHost, mPort, mProxyInfo,
00226                               getter_AddRefs(mTransport));
00227     if (NS_FAILED(rv)) return rv;
00228 
00229     // not fatal if this fails
00230     mTransport->SetEventSink(this, eventQ);
00231 
00232     rv = WriteRequest(mTransport);
00233     if (NS_FAILED(rv)) return rv;
00234 
00235     //
00236     // create TXT to HTML stream converter
00237     //
00238     nsCOMPtr<nsIStreamConverterService> scs = 
00239              do_GetService(kStreamConverterServiceCID, &rv);
00240     if (NS_FAILED(rv)) return rv;
00241 
00242     nsCOMPtr<nsIStreamListener> convListener;
00243     rv = scs->AsyncConvertData("text/plain", "text/html", this, nsnull,
00244                                getter_AddRefs(convListener));
00245     if (NS_FAILED(rv)) return rv;
00246 
00247     nsCOMPtr<nsITXTToHTMLConv> conv = do_QueryInterface(convListener);
00248     if (conv) {
00249         nsCAutoString userHost;
00250         rv = mURI->GetPath(userHost);
00251 
00252         nsAutoString title;
00253         title.AppendLiteral("Finger information for ");
00254         AppendUTF8toUTF16(userHost, title);
00255 
00256         conv->SetTitle(title.get());
00257         conv->PreFormatHTML(PR_TRUE);
00258     }
00259 
00260     //
00261     // open input stream, and create input stream pump...
00262     //
00263     nsCOMPtr<nsIInputStream> sockIn;
00264     rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(sockIn));
00265     if (NS_FAILED(rv)) return rv;
00266 
00267     rv = NS_NewInputStreamPump(getter_AddRefs(mPump), sockIn);
00268     if (NS_FAILED(rv)) return rv;
00269 
00270     rv = mPump->AsyncRead(convListener, nsnull);
00271     if (NS_FAILED(rv)) return rv;
00272 
00273     if (mLoadGroup)
00274         mLoadGroup->AddRequest(this, nsnull);
00275 
00276     mListener = aListener;
00277     mListenerContext = ctxt;
00278     return NS_OK;
00279 }
00280 
00281 NS_IMETHODIMP
00282 nsFingerChannel::GetLoadFlags(PRUint32 *aLoadFlags)
00283 {
00284     *aLoadFlags = mLoadFlags;
00285     return NS_OK;
00286 }
00287 
00288 NS_IMETHODIMP
00289 nsFingerChannel::SetLoadFlags(PRUint32 aLoadFlags)
00290 {
00291     mLoadFlags = aLoadFlags;
00292     return NS_OK;
00293 }
00294 
00295 NS_IMETHODIMP
00296 nsFingerChannel::GetContentType(nsACString &aContentType)
00297 {
00298     aContentType = mContentType;
00299     return NS_OK;
00300 }
00301 
00302 NS_IMETHODIMP
00303 nsFingerChannel::SetContentType(const nsACString &aContentType)
00304 {
00305     mContentType = aContentType;
00306     return NS_OK;
00307 }
00308 
00309 NS_IMETHODIMP
00310 nsFingerChannel::GetContentCharset(nsACString &aContentCharset)
00311 {
00312     aContentCharset = mContentCharset;
00313     return NS_OK;
00314 }
00315 
00316 NS_IMETHODIMP
00317 nsFingerChannel::SetContentCharset(const nsACString &aContentCharset)
00318 {
00319     mContentCharset = aContentCharset;
00320     return NS_OK;
00321 }
00322 
00323 NS_IMETHODIMP
00324 nsFingerChannel::GetContentLength(PRInt32 *aContentLength)
00325 {
00326     *aContentLength = -1;
00327     return NS_OK;
00328 }
00329 
00330 NS_IMETHODIMP
00331 nsFingerChannel::SetContentLength(PRInt32 aContentLength)
00332 {
00333     // silently ignore this...
00334     return NS_OK;
00335 }
00336 
00337 NS_IMETHODIMP
00338 nsFingerChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00339 {
00340     *aLoadGroup = mLoadGroup;
00341     NS_IF_ADDREF(*aLoadGroup);
00342     return NS_OK;
00343 }
00344 
00345 NS_IMETHODIMP
00346 nsFingerChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00347 {
00348     mLoadGroup = aLoadGroup;
00349     mProgressSink = nsnull;
00350     return NS_OK;
00351 }
00352 
00353 NS_IMETHODIMP
00354 nsFingerChannel::GetOwner(nsISupports* *aOwner)
00355 {
00356     *aOwner = mOwner.get();
00357     NS_IF_ADDREF(*aOwner);
00358     return NS_OK;
00359 }
00360 
00361 NS_IMETHODIMP
00362 nsFingerChannel::SetOwner(nsISupports* aOwner)
00363 {
00364     mOwner = aOwner;
00365     return NS_OK;
00366 }
00367 
00368 NS_IMETHODIMP
00369 nsFingerChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
00370 {
00371     *aNotificationCallbacks = mCallbacks.get();
00372     NS_IF_ADDREF(*aNotificationCallbacks);
00373     return NS_OK;
00374 }
00375 
00376 NS_IMETHODIMP
00377 nsFingerChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
00378 {
00379     mCallbacks = aNotificationCallbacks;
00380     mProgressSink = nsnull;
00381     return NS_OK;
00382 }
00383 
00384 NS_IMETHODIMP 
00385 nsFingerChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
00386 {
00387     if (mTransport)
00388         return mTransport->GetSecurityInfo(aSecurityInfo);
00389 
00390     *aSecurityInfo = nsnull;
00391     return NS_OK;
00392 }
00393 
00394 //-----------------------------------------------------------------------------
00395 // nsIRequestObserver methods
00396 //-----------------------------------------------------------------------------
00397 
00398 NS_IMETHODIMP
00399 nsFingerChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
00400 {
00401     return mListener->OnStartRequest(this, mListenerContext);
00402 }
00403 
00404 NS_IMETHODIMP
00405 nsFingerChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
00406 {
00407     if (NS_SUCCEEDED(mStatus))
00408         mStatus = status;
00409 
00410     mListener->OnStopRequest(this, mListenerContext, mStatus);
00411     mListener = 0;
00412     mListenerContext = 0;
00413 
00414     if (mLoadGroup)
00415         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
00416 
00417     mPump = 0;
00418     mTransport = 0;
00419     
00420     // Drop notification callbacks to prevent cycles.
00421     mCallbacks = 0;
00422     mProgressSink = 0;
00423 
00424     return NS_OK;
00425 }
00426 
00427 NS_IMETHODIMP
00428 nsFingerChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
00429                                  nsIInputStream *stream, PRUint32 offset,
00430                                  PRUint32 count)
00431 {
00432     return mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
00433 }
00434 
00435 //-----------------------------------------------------------------------------
00436 // nsITransportEventSink methods
00437 //-----------------------------------------------------------------------------
00438 
00439 NS_IMETHODIMP
00440 nsFingerChannel::OnTransportStatus(nsITransport *trans, nsresult status,
00441                                    PRUint64 progress, PRUint64 progressMax)
00442 {
00443     if (!mProgressSink)
00444         NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
00445 
00446     // suppress status notification if channel is no longer pending!
00447     if (mProgressSink && NS_SUCCEEDED(mStatus) && mPump && !(mLoadFlags & LOAD_BACKGROUND)) {
00448         mProgressSink->OnStatus(this, nsnull, status,
00449                                 NS_ConvertUTF8toUTF16(mHost).get());
00450 
00451         if (status == nsISocketTransport::STATUS_RECEIVING_FROM ||
00452             status == nsISocketTransport::STATUS_SENDING_TO) {
00453             mProgressSink->OnProgress(this, nsnull, progress, progressMax);
00454         }
00455     }
00456     return NS_OK;
00457 }
00458 
00459 //-----------------------------------------------------------------------------
00460 
00461 nsresult
00462 nsFingerChannel::WriteRequest(nsITransport *trans)
00463 {
00464     // The text to send should already be in mUser
00465     nsresult rv;
00466 
00467     nsCAutoString requestBuf;
00468     requestBuf = mUser + NS_LITERAL_CSTRING("\r\n");
00469 
00470     nsCOMPtr<nsIOutputStream> stream;
00471     rv = trans->OpenOutputStream(0, requestBuf.Length(), 1, getter_AddRefs(stream));
00472     if (NS_FAILED(rv)) return rv;
00473 
00474     PRUint32 n;
00475     rv = stream->Write(requestBuf.get(), requestBuf.Length(), &n);
00476     if (NS_FAILED(rv)) return rv;
00477 
00478     NS_ENSURE_TRUE(n == requestBuf.Length(), NS_ERROR_UNEXPECTED);
00479     return NS_OK;
00480 }