Back to index

lightning-sunbird  0.9+nobinonly
nsDataChannel.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; 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 the GNU General Public License Version 2 or later (the "GPL"), or
00026  * 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 // data implementation
00039 
00040 #include "nsDataChannel.h"
00041 #include "nsIServiceManager.h"
00042 #include "nsIEventQueueService.h"
00043 #include "nsNetUtil.h"
00044 #include "nsInt64.h"
00045 #include "nsILoadGroup.h"
00046 #include "plbase64.h"
00047 #include "prmem.h"
00048 #include "nsIPipe.h"
00049 #include "nsIInputStream.h"
00050 #include "nsIOutputStream.h"
00051 #include "nsXPIDLString.h"
00052 #include "nsReadableUtils.h"
00053 #include "nsNetSegmentUtils.h"
00054 #include "nsCRT.h"
00055 #include "nsEscape.h"
00056 #include "nsNetCID.h"
00057 
00058 // nsDataChannel methods
00059 nsDataChannel::nsDataChannel() :
00060     mContentLength(-1),
00061     mOpened(PR_FALSE)
00062 {
00063 }
00064 
00065 nsDataChannel::~nsDataChannel() {
00066 }
00067 
00068 NS_IMPL_ISUPPORTS5(nsDataChannel,
00069                    nsIDataChannel,
00070                    nsIChannel,
00071                    nsIRequest,
00072                    nsIRequestObserver,
00073                    nsIStreamListener)
00074 
00075 nsresult
00076 nsDataChannel::Init(nsIURI* uri)
00077 {
00078     // Data urls contain all the data within the url string itself.
00079     mUrl = uri;
00080 
00081     nsresult rv;
00082     mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
00083     if (NS_FAILED(rv))
00084       return rv;
00085 
00086     rv = ParseData();
00087 
00088     return rv;
00089 }
00090 
00091 nsresult
00092 nsDataChannel::ParseData() {
00093     nsresult rv;
00094     PRBool lBase64 = PR_FALSE;
00095     NS_ASSERTION(mUrl, "no url in the data channel");
00096     if (!mUrl) return NS_ERROR_NULL_POINTER;
00097 
00098     nsCAutoString spec;
00099     rv = mUrl->GetAsciiSpec(spec);
00100     if (NS_FAILED(rv)) return rv;
00101 
00102     // move past "data:"
00103     char *buffer = (char *) strstr(spec.BeginWriting(), "data:");
00104     if (!buffer) {
00105         // malfored url
00106         return NS_ERROR_MALFORMED_URI;
00107     }
00108     buffer += 5;
00109 
00110     // First, find the start of the data
00111     char *comma = strchr(buffer, ',');
00112     if (!comma) return NS_ERROR_FAILURE;
00113 
00114     *comma = '\0';
00115 
00116     // determine if the data is base64 encoded.
00117     char *base64 = strstr(buffer, ";base64");
00118     if (base64) {
00119         lBase64 = PR_TRUE;
00120         *base64 = '\0';
00121     }
00122 
00123     if (comma == buffer) {
00124         // nothing but data
00125         mContentType.AssignLiteral("text/plain");
00126         mContentCharset.AssignLiteral("US-ASCII");
00127     } else {
00128         // everything else is content type
00129         char *semiColon = (char *) strchr(buffer, ';');
00130         if (semiColon)
00131             *semiColon = '\0';
00132         
00133         if (semiColon == buffer || base64 == buffer) {
00134           // there is no content type, but there are other parameters
00135           mContentType.AssignLiteral("text/plain");
00136         } else {
00137           mContentType = buffer;
00138           ToLowerCase(mContentType);
00139         }
00140 
00141         if (semiColon) {
00142             char *charset = PL_strcasestr(semiColon + 1, "charset=");
00143             if (charset)
00144                 mContentCharset = charset + sizeof("charset=") - 1;
00145 
00146             *semiColon = ';';
00147         }
00148     }
00149     mContentType.StripWhitespace();
00150     mContentCharset.StripWhitespace();
00151 
00152     char *dataBuffer = nsnull;
00153     PRBool cleanup = PR_FALSE;
00154     if (!lBase64 && ((strncmp(mContentType.get(),"text/",5) == 0) || mContentType.Find("xml") != kNotFound)) {
00155         // it's text, don't compress spaces
00156         dataBuffer = comma+1;
00157     } else {
00158         // it's ascii encoded binary, don't let any spaces in
00159         nsCAutoString dataBuf(comma+1);
00160         dataBuf.StripWhitespace();
00161         dataBuffer = ToNewCString(dataBuf);
00162         if (!dataBuffer)
00163             return NS_ERROR_OUT_OF_MEMORY;
00164         cleanup = PR_TRUE;
00165     }
00166     
00167     nsCOMPtr<nsIInputStream> bufInStream;
00168     nsCOMPtr<nsIOutputStream> bufOutStream;
00169     
00170     // create an unbounded pipe.
00171     rv = NS_NewPipe(getter_AddRefs(bufInStream),
00172                     getter_AddRefs(bufOutStream),
00173                     NET_DEFAULT_SEGMENT_SIZE, PR_UINT32_MAX,
00174                     PR_TRUE, PR_TRUE);
00175     if (NS_FAILED(rv))
00176         goto cleanup;
00177 
00178     PRUint32 dataLen;
00179     dataLen = nsUnescapeCount(dataBuffer);
00180     if (lBase64) {
00181         *base64 = ';';
00182         PRInt32 resultLen = 0;
00183         if (dataLen >= 1 && dataBuffer[dataLen-1] == '=') {
00184             if (dataLen >= 2 && dataBuffer[dataLen-2] == '=')
00185                 resultLen = dataLen-2;
00186             else
00187                 resultLen = dataLen-1;
00188         } else {
00189             resultLen = dataLen;
00190         }
00191         resultLen = ((resultLen * 3) / 4);
00192 
00193         // XXX PL_Base64Decode will return a null pointer for decoding
00194         // errors.  Since those are more likely than out-of-memory,
00195         // should we return NS_ERROR_MALFORMED_URI instead?
00196         char * decodedData = PL_Base64Decode(dataBuffer, dataLen, nsnull);
00197         if (!decodedData) {
00198             rv = NS_ERROR_OUT_OF_MEMORY;
00199             goto cleanup;
00200         }
00201 
00202         rv = bufOutStream->Write(decodedData, resultLen, (PRUint32*)&mContentLength);
00203 
00204         PR_Free(decodedData);
00205     } else {
00206         rv = bufOutStream->Write(dataBuffer, dataLen, (PRUint32*)&mContentLength);
00207     }
00208     if (NS_FAILED(rv))
00209         goto cleanup;
00210 
00211     rv = bufInStream->QueryInterface(NS_GET_IID(nsIInputStream), getter_AddRefs(mDataStream));
00212     if (NS_FAILED(rv))
00213         goto cleanup;
00214 
00215     *comma = ',';
00216 
00217     rv = NS_OK;
00218 
00219  cleanup:
00220     if (cleanup)
00221         nsMemory::Free(dataBuffer);
00222     return rv;
00223 }
00224 
00225 NS_METHOD
00226 nsDataChannel::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult)
00227 {
00228     nsDataChannel* dc = new nsDataChannel();
00229     if (dc == nsnull)
00230         return NS_ERROR_OUT_OF_MEMORY;
00231     NS_ADDREF(dc);
00232     nsresult rv = dc->QueryInterface(aIID, aResult);
00233     NS_RELEASE(dc);
00234     return rv;
00235 }
00236 
00238 // nsIRequest methods:
00239 
00240 NS_IMETHODIMP
00241 nsDataChannel::GetName(nsACString &result)
00242 {
00243     if (mUrl)
00244         return mUrl->GetSpec(result);
00245     result.Truncate();
00246     return NS_OK;
00247 }
00248 
00249 NS_IMETHODIMP
00250 nsDataChannel::IsPending(PRBool *result)
00251 {
00252     return mPump->IsPending(result);
00253 }
00254 
00255 NS_IMETHODIMP
00256 nsDataChannel::GetStatus(nsresult *status)
00257 {
00258     return mPump->GetStatus(status);
00259 }
00260 
00261 NS_IMETHODIMP
00262 nsDataChannel::Cancel(nsresult status)
00263 {
00264     NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
00265 
00266     return mPump->Cancel(status);
00267 }
00268 
00269 NS_IMETHODIMP
00270 nsDataChannel::Suspend(void)
00271 {
00272     return mPump->Suspend();
00273 }
00274 
00275 NS_IMETHODIMP
00276 nsDataChannel::Resume(void)
00277 {
00278     return mPump->Resume();
00279 }
00280 
00281 NS_IMETHODIMP
00282 nsDataChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00283 {
00284     *aLoadGroup = mLoadGroup;
00285     NS_IF_ADDREF(*aLoadGroup);
00286     return NS_OK;
00287 }
00288 
00289 NS_IMETHODIMP
00290 nsDataChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00291 {
00292     mLoadGroup = aLoadGroup;
00293     return NS_OK;
00294 }
00295 
00296 NS_IMETHODIMP
00297 nsDataChannel::GetLoadFlags(PRUint32 *aLoadFlags)
00298 {
00299     return mPump->GetLoadFlags(aLoadFlags);
00300 }
00301 
00302 NS_IMETHODIMP
00303 nsDataChannel::SetLoadFlags(PRUint32 aLoadFlags)
00304 {
00305     return mPump->SetLoadFlags(aLoadFlags);
00306 }
00307 
00309 // nsIChannel methods:
00310 
00311 NS_IMETHODIMP
00312 nsDataChannel::GetOriginalURI(nsIURI* *aURI)
00313 {
00314     *aURI = mOriginalURI ? mOriginalURI : mUrl;
00315     NS_ADDREF(*aURI);
00316     return NS_OK;
00317 }
00318 
00319 NS_IMETHODIMP
00320 nsDataChannel::SetOriginalURI(nsIURI* aURI)
00321 {
00322     mOriginalURI = aURI;
00323     return NS_OK;
00324 }
00325 
00326 NS_IMETHODIMP
00327 nsDataChannel::GetURI(nsIURI* *aURI)
00328 {
00329     *aURI = mUrl;
00330     NS_IF_ADDREF(*aURI);
00331     return NS_OK;
00332 }
00333 
00334 // This class 
00335 
00336 NS_IMETHODIMP
00337 nsDataChannel::Open(nsIInputStream **_retval)
00338 {
00339     NS_ENSURE_ARG_POINTER(_retval);
00340     NS_ADDREF(*_retval = mDataStream);
00341     mOpened = PR_TRUE;
00342     return NS_OK;
00343 }
00344 
00345 NS_IMETHODIMP
00346 nsDataChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
00347 {
00348     // Hold onto the real consumer...
00349     mListener = aListener;
00350     mOpened = PR_TRUE;
00351 
00352     nsresult rv = mPump->Init(mDataStream, nsInt64(-1), nsInt64(-1), 0, 0, PR_FALSE);
00353     if (NS_FAILED(rv))
00354         return rv;
00355 
00356     // Add to load group
00357     if (mLoadGroup)
00358         mLoadGroup->AddRequest(this, nsnull);
00359 
00360     // Start the read
00361     return mPump->AsyncRead(this, ctxt);
00362 
00363     // These notifications will be processed when control returns to the
00364     // message pump and the PLEvents are processed...
00365 }
00366 
00367 NS_IMETHODIMP
00368 nsDataChannel::GetContentType(nsACString &aContentType)
00369 {
00370     NS_ASSERTION(mContentType.Length() > 0, "data protocol should have content type by now");
00371     aContentType = mContentType;
00372     return NS_OK;
00373 }
00374 
00375 NS_IMETHODIMP
00376 nsDataChannel::SetContentType(const nsACString &aContentType)
00377 {
00378     // Ignore content-type hints
00379     if (mOpened) {
00380         mContentType = aContentType;
00381     }
00382     return NS_OK;
00383 }
00384 
00385 NS_IMETHODIMP
00386 nsDataChannel::GetContentCharset(nsACString &aContentCharset)
00387 {
00388     aContentCharset = mContentCharset;
00389     return NS_OK;
00390 }
00391 
00392 NS_IMETHODIMP
00393 nsDataChannel::SetContentCharset(const nsACString &aContentCharset)
00394 {
00395     // Ignore content-charset hints
00396     if (mOpened) {
00397         mContentCharset = aContentCharset;
00398     }
00399     return NS_OK;
00400 }
00401 
00402 NS_IMETHODIMP
00403 nsDataChannel::GetContentLength(PRInt32 *aContentLength)
00404 {
00405     *aContentLength = mContentLength;
00406     return NS_OK;
00407 }
00408 
00409 NS_IMETHODIMP
00410 nsDataChannel::SetContentLength(PRInt32 aContentLength)
00411 {
00412     mContentLength = aContentLength;
00413     return NS_OK;
00414 }
00415 
00416 NS_IMETHODIMP
00417 nsDataChannel::GetOwner(nsISupports* *aOwner)
00418 {
00419     *aOwner = mOwner.get();
00420     NS_IF_ADDREF(*aOwner);
00421     return NS_OK;
00422 }
00423 
00424 NS_IMETHODIMP
00425 nsDataChannel::SetOwner(nsISupports* aOwner)
00426 {
00427     mOwner = aOwner;
00428     return NS_OK;
00429 }
00430 
00431 NS_IMETHODIMP
00432 nsDataChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
00433 {
00434     NS_ENSURE_ARG_POINTER(aNotificationCallbacks);
00435     *aNotificationCallbacks = mCallbacks.get();
00436     NS_IF_ADDREF(*aNotificationCallbacks);
00437     return NS_OK;
00438 }
00439 
00440 NS_IMETHODIMP
00441 nsDataChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
00442 {
00443     mCallbacks = aNotificationCallbacks;
00444     return NS_OK;
00445 }
00446 
00447 NS_IMETHODIMP
00448 nsDataChannel::GetSecurityInfo(nsISupports **sec)
00449 {
00450     NS_ENSURE_ARG_POINTER(sec);
00451     *sec = nsnull;
00452     return NS_OK;
00453 }
00454 
00456 // nsIRequestObserver methods:
00457 
00458 NS_IMETHODIMP
00459 nsDataChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
00460 {
00461     if (mListener) {
00462         return mListener->OnStartRequest(this, ctxt);
00463     }
00464     return NS_OK;
00465 }
00466 
00467 NS_IMETHODIMP
00468 nsDataChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
00469                              nsresult status)
00470 {
00471     if (mListener) {
00472         mListener->OnStopRequest(this, ctxt, status);
00473 
00474         // Drop the reference to the stream listener -- it is no longer needed.
00475         mListener = nsnull;
00476     }
00477 
00478     // Remove from Loadgroup
00479     if (mLoadGroup)
00480         mLoadGroup->RemoveRequest(this, nsnull, status);
00481 
00482     // Drop notification callbacks to prevent cycles.
00483     mCallbacks = nsnull;
00484 
00485     return NS_OK;
00486 }
00487 
00488 
00490 // nsIStreamListener methods:
00491 
00492 NS_IMETHODIMP
00493 nsDataChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
00494                                nsIInputStream *input,
00495                                PRUint32 offset, PRUint32 count)
00496 {
00497     if (mListener) {
00498         return mListener->OnDataAvailable(this, ctxt, input,
00499                                           offset, count);
00500     }
00501     return NS_OK;
00502 }
00503