michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsDataChannel.h" michael@0: #include "nsDataHandler.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsError.h" michael@0: michael@0: static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID); michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsDataHandler::nsDataHandler() { michael@0: } michael@0: michael@0: nsDataHandler::~nsDataHandler() { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsDataHandler, nsIProtocolHandler) michael@0: michael@0: nsresult michael@0: nsDataHandler::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) { michael@0: michael@0: nsDataHandler* ph = new nsDataHandler(); michael@0: if (ph == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(ph); michael@0: nsresult rv = ph->QueryInterface(aIID, aResult); michael@0: NS_RELEASE(ph); michael@0: return rv; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // nsIProtocolHandler methods: michael@0: michael@0: NS_IMETHODIMP michael@0: nsDataHandler::GetScheme(nsACString &result) { michael@0: result.AssignLiteral("data"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDataHandler::GetDefaultPort(int32_t *result) { michael@0: // no ports for data protocol michael@0: *result = -1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDataHandler::GetProtocolFlags(uint32_t *result) { michael@0: *result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT | michael@0: URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE | michael@0: URI_SYNC_LOAD_IS_OK; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDataHandler::NewURI(const nsACString &aSpec, michael@0: const char *aCharset, // ignore charset info michael@0: nsIURI *aBaseURI, michael@0: nsIURI **result) { michael@0: nsresult rv; michael@0: nsRefPtr uri; michael@0: michael@0: nsCString spec(aSpec); michael@0: michael@0: if (aBaseURI && !spec.IsEmpty() && spec[0] == '#') { michael@0: // Looks like a reference instead of a fully-specified URI. michael@0: // --> initialize |uri| as a clone of |aBaseURI|, with ref appended. michael@0: rv = aBaseURI->Clone(getter_AddRefs(uri)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = uri->SetRef(spec); michael@0: } else { michael@0: // Otherwise, we'll assume |spec| is a fully-specified data URI michael@0: nsAutoCString contentType, contentCharset, dataBuffer, hashRef; michael@0: bool base64; michael@0: rv = ParseURI(spec, contentType, contentCharset, base64, dataBuffer, hashRef); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // Strip whitespace unless this is text, where whitespace is important michael@0: // Don't strip escaped whitespace though (bug 391951) michael@0: if (base64 || (strncmp(contentType.get(),"text/",5) != 0 && michael@0: contentType.Find("xml") == kNotFound)) { michael@0: // it's ascii encoded binary, don't let any spaces in michael@0: spec.StripWhitespace(); michael@0: } michael@0: michael@0: uri = do_CreateInstance(kSimpleURICID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = uri->SetSpec(spec); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: uri.forget(result); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDataHandler::NewChannel(nsIURI* uri, nsIChannel* *result) { michael@0: NS_ENSURE_ARG_POINTER(uri); michael@0: nsDataChannel* channel = new nsDataChannel(uri); michael@0: if (!channel) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(channel); michael@0: michael@0: nsresult rv = channel->Init(); michael@0: if (NS_FAILED(rv)) { michael@0: NS_RELEASE(channel); michael@0: return rv; michael@0: } michael@0: michael@0: *result = channel; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDataHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) { michael@0: // don't override anything. michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: #define BASE64_EXTENSION ";base64" michael@0: michael@0: nsresult michael@0: nsDataHandler::ParseURI(nsCString& spec, michael@0: nsCString& contentType, michael@0: nsCString& contentCharset, michael@0: bool& isBase64, michael@0: nsCString& dataBuffer, michael@0: nsCString& hashRef) { michael@0: isBase64 = false; michael@0: michael@0: // move past "data:" michael@0: char *buffer = (char *) PL_strcasestr(spec.BeginWriting(), "data:"); michael@0: if (!buffer) { michael@0: // malformed uri michael@0: return NS_ERROR_MALFORMED_URI; michael@0: } michael@0: buffer += 5; michael@0: michael@0: // First, find the start of the data michael@0: char *comma = strchr(buffer, ','); michael@0: if (!comma) michael@0: return NS_ERROR_MALFORMED_URI; michael@0: michael@0: *comma = '\0'; michael@0: michael@0: // determine if the data is base64 encoded. michael@0: char *base64 = PL_strcasestr(buffer, BASE64_EXTENSION); michael@0: if (base64) { michael@0: char *beyond = base64 + strlen(BASE64_EXTENSION); michael@0: // per the RFC 2397 grammar, "base64" MUST be followed by a comma michael@0: // previously substituted by '\0', but we also allow it in between michael@0: // parameters so a subsequent ";" is ok as well (this deals with michael@0: // *broken* data URIs, see bug 781693 for an example) michael@0: if (*beyond == '\0' || *beyond == ';') { michael@0: isBase64 = true; michael@0: *base64 = '\0'; michael@0: } michael@0: } michael@0: michael@0: if (comma == buffer) { michael@0: // nothing but data michael@0: contentType.AssignLiteral("text/plain"); michael@0: contentCharset.AssignLiteral("US-ASCII"); michael@0: } else { michael@0: // everything else is content type michael@0: char *semiColon = (char *) strchr(buffer, ';'); michael@0: if (semiColon) michael@0: *semiColon = '\0'; michael@0: michael@0: if (semiColon == buffer || base64 == buffer) { michael@0: // there is no content type, but there are other parameters michael@0: contentType.AssignLiteral("text/plain"); michael@0: } else { michael@0: contentType = buffer; michael@0: ToLowerCase(contentType); michael@0: } michael@0: michael@0: if (semiColon) { michael@0: char *charset = PL_strcasestr(semiColon + 1, "charset="); michael@0: if (charset) michael@0: contentCharset = charset + sizeof("charset=") - 1; michael@0: michael@0: *semiColon = ';'; michael@0: } michael@0: } michael@0: michael@0: *comma = ','; michael@0: if (isBase64) michael@0: *base64 = ';'; michael@0: michael@0: contentType.StripWhitespace(); michael@0: contentCharset.StripWhitespace(); michael@0: michael@0: // Split encoded data from terminal "#ref" (if present) michael@0: char *data = comma + 1; michael@0: char *hash = strchr(data, '#'); michael@0: if (!hash) { michael@0: dataBuffer.Assign(data); michael@0: hashRef.Truncate(); michael@0: } else { michael@0: dataBuffer.Assign(data, hash - data); michael@0: hashRef.Assign(hash); michael@0: } michael@0: michael@0: return NS_OK; michael@0: }