123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "HTMLFormSubmission.h"
- #include "nsCOMPtr.h"
- #include "nsIForm.h"
- #include "nsILinkHandler.h"
- #include "nsIDocument.h"
- #include "nsGkAtoms.h"
- #include "nsIFormControl.h"
- #include "nsIDOMHTMLFormElement.h"
- #include "nsError.h"
- #include "nsGenericHTMLElement.h"
- #include "nsAttrValueInlines.h"
- #include "nsISaveAsCharset.h"
- #include "nsIFile.h"
- #include "nsDirectoryServiceDefs.h"
- #include "nsStringStream.h"
- #include "nsIURI.h"
- #include "nsIURL.h"
- #include "nsNetUtil.h"
- #include "nsLinebreakConverter.h"
- #include "nsEscape.h"
- #include "nsUnicharUtils.h"
- #include "nsIMultiplexInputStream.h"
- #include "nsIMIMEInputStream.h"
- #include "nsIMIMEService.h"
- #include "nsIConsoleService.h"
- #include "nsIScriptError.h"
- #include "nsIStringBundle.h"
- #include "nsCExternalHandlerService.h"
- #include "nsIFileStreams.h"
- #include "nsContentUtils.h"
- #include "mozilla/dom/Directory.h"
- #include "mozilla/dom/EncodingUtils.h"
- #include "mozilla/dom/File.h"
- namespace mozilla {
- namespace dom {
- namespace {
- void
- SendJSWarning(nsIDocument* aDocument,
- const char* aWarningName,
- const char16_t** aWarningArgs, uint32_t aWarningArgsLen)
- {
- nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
- NS_LITERAL_CSTRING("HTML"), aDocument,
- nsContentUtils::eFORMS_PROPERTIES,
- aWarningName,
- aWarningArgs, aWarningArgsLen);
- }
- void
- RetrieveFileName(Blob* aBlob, nsAString& aFilename)
- {
- if (!aBlob) {
- return;
- }
- RefPtr<File> file = aBlob->ToFile();
- if (file) {
- file->GetName(aFilename);
- }
- }
- void
- RetrieveDirectoryName(Directory* aDirectory, nsAString& aDirname)
- {
- MOZ_ASSERT(aDirectory);
- ErrorResult rv;
- aDirectory->GetName(aDirname, rv);
- if (NS_WARN_IF(rv.Failed())) {
- rv.SuppressException();
- aDirname.Truncate();
- }
- }
- // --------------------------------------------------------------------------
- class FSURLEncoded : public EncodingFormSubmission
- {
- public:
- /**
- * @param aCharset the charset of the form as a string
- * @param aMethod the method of the submit (either NS_FORM_METHOD_GET or
- * NS_FORM_METHOD_POST).
- */
- FSURLEncoded(const nsACString& aCharset,
- int32_t aMethod,
- nsIDocument* aDocument,
- nsIContent* aOriginatingElement)
- : EncodingFormSubmission(aCharset, aOriginatingElement),
- mMethod(aMethod),
- mDocument(aDocument),
- mWarnedFileControl(false)
- {
- }
- virtual nsresult
- AddNameValuePair(const nsAString& aName, const nsAString& aValue) override;
- virtual nsresult
- AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
- virtual nsresult
- AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
- virtual nsresult
- GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override;
- virtual bool SupportsIsindexSubmission() override
- {
- return true;
- }
- virtual nsresult AddIsindex(const nsAString& aValue) override;
- protected:
- /**
- * URL encode a Unicode string by encoding it to bytes, converting linebreaks
- * properly, and then escaping many bytes as %xx.
- *
- * @param aStr the string to encode
- * @param aEncoded the encoded string [OUT]
- * @throws NS_ERROR_OUT_OF_MEMORY if we run out of memory
- */
- nsresult URLEncode(const nsAString& aStr, nsACString& aEncoded);
- private:
- /**
- * The method of the submit (either NS_FORM_METHOD_GET or
- * NS_FORM_METHOD_POST).
- */
- int32_t mMethod;
- /** The query string so far (the part after the ?) */
- nsCString mQueryString;
- /** The document whose URI to use when reporting errors */
- nsCOMPtr<nsIDocument> mDocument;
- /** Whether or not we have warned about a file control not being submitted */
- bool mWarnedFileControl;
- };
- nsresult
- FSURLEncoded::AddNameValuePair(const nsAString& aName,
- const nsAString& aValue)
- {
- // Encode value
- nsCString convValue;
- nsresult rv = URLEncode(aValue, convValue);
- NS_ENSURE_SUCCESS(rv, rv);
- // Encode name
- nsAutoCString convName;
- rv = URLEncode(aName, convName);
- NS_ENSURE_SUCCESS(rv, rv);
- // Append data to string
- if (mQueryString.IsEmpty()) {
- mQueryString += convName + NS_LITERAL_CSTRING("=") + convValue;
- } else {
- mQueryString += NS_LITERAL_CSTRING("&") + convName
- + NS_LITERAL_CSTRING("=") + convValue;
- }
- return NS_OK;
- }
- nsresult
- FSURLEncoded::AddIsindex(const nsAString& aValue)
- {
- // Encode value
- nsCString convValue;
- nsresult rv = URLEncode(aValue, convValue);
- NS_ENSURE_SUCCESS(rv, rv);
- // Append data to string
- if (mQueryString.IsEmpty()) {
- mQueryString.Assign(convValue);
- } else {
- mQueryString += NS_LITERAL_CSTRING("&isindex=") + convValue;
- }
- return NS_OK;
- }
- nsresult
- FSURLEncoded::AddNameBlobOrNullPair(const nsAString& aName,
- Blob* aBlob)
- {
- if (!mWarnedFileControl) {
- SendJSWarning(mDocument, "ForgotFileEnctypeWarning", nullptr, 0);
- mWarnedFileControl = true;
- }
- nsAutoString filename;
- RetrieveFileName(aBlob, filename);
- return AddNameValuePair(aName, filename);
- }
- nsresult
- FSURLEncoded::AddNameDirectoryPair(const nsAString& aName,
- Directory* aDirectory)
- {
- // No warning about because Directory objects are never sent via form.
- nsAutoString dirname;
- RetrieveDirectoryName(aDirectory, dirname);
- return AddNameValuePair(aName, dirname);
- }
- void
- HandleMailtoSubject(nsCString& aPath)
- {
- // Walk through the string and see if we have a subject already.
- bool hasSubject = false;
- bool hasParams = false;
- int32_t paramSep = aPath.FindChar('?');
- while (paramSep != kNotFound && paramSep < (int32_t)aPath.Length()) {
- hasParams = true;
- // Get the end of the name at the = op. If it is *after* the next &,
- // assume that someone made a parameter without an = in it
- int32_t nameEnd = aPath.FindChar('=', paramSep+1);
- int32_t nextParamSep = aPath.FindChar('&', paramSep+1);
- if (nextParamSep == kNotFound) {
- nextParamSep = aPath.Length();
- }
- // If the = op is after the &, this parameter is a name without value.
- // If there is no = op, same thing.
- if (nameEnd == kNotFound || nextParamSep < nameEnd) {
- nameEnd = nextParamSep;
- }
- if (nameEnd != kNotFound) {
- if (Substring(aPath, paramSep+1, nameEnd-(paramSep+1)).
- LowerCaseEqualsLiteral("subject")) {
- hasSubject = true;
- break;
- }
- }
- paramSep = nextParamSep;
- }
- // If there is no subject, append a preformed subject to the mailto line
- if (!hasSubject) {
- if (hasParams) {
- aPath.Append('&');
- } else {
- aPath.Append('?');
- }
- // Get the default subject
- nsXPIDLString brandName;
- nsresult rv =
- nsContentUtils::GetLocalizedString(nsContentUtils::eBRAND_PROPERTIES,
- "brandShortName", brandName);
- if (NS_FAILED(rv))
- return;
- const char16_t *formatStrings[] = { brandName.get() };
- nsXPIDLString subjectStr;
- rv = nsContentUtils::FormatLocalizedString(
- nsContentUtils::eFORMS_PROPERTIES,
- "DefaultFormSubject",
- formatStrings,
- subjectStr);
- if (NS_FAILED(rv))
- return;
- aPath.AppendLiteral("subject=");
- nsCString subjectStrEscaped;
- rv = NS_EscapeURL(NS_ConvertUTF16toUTF8(subjectStr), esc_Query,
- subjectStrEscaped, mozilla::fallible);
- if (NS_FAILED(rv))
- return;
- aPath.Append(subjectStrEscaped);
- }
- }
- nsresult
- FSURLEncoded::GetEncodedSubmission(nsIURI* aURI,
- nsIInputStream** aPostDataStream)
- {
- nsresult rv = NS_OK;
- *aPostDataStream = nullptr;
- if (mMethod == NS_FORM_METHOD_POST) {
- bool isMailto = false;
- aURI->SchemeIs("mailto", &isMailto);
- if (isMailto) {
- nsAutoCString path;
- rv = aURI->GetPath(path);
- NS_ENSURE_SUCCESS(rv, rv);
- HandleMailtoSubject(path);
- // Append the body to and force-plain-text args to the mailto line
- nsAutoCString escapedBody;
- if (NS_WARN_IF(!NS_Escape(mQueryString, escapedBody, url_XAlphas))) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- path += NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody;
- rv = aURI->SetPath(path);
- } else {
- nsCOMPtr<nsIInputStream> dataStream;
- // XXX We *really* need to either get the string to disown its data (and
- // not destroy it), or make a string input stream that owns the CString
- // that is passed to it. Right now this operation does a copy.
- rv = NS_NewCStringInputStream(getter_AddRefs(dataStream), mQueryString);
- NS_ENSURE_SUCCESS(rv, rv);
- nsCOMPtr<nsIMIMEInputStream> mimeStream(
- do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
- NS_ENSURE_SUCCESS(rv, rv);
- #ifdef SPECIFY_CHARSET_IN_CONTENT_TYPE
- mimeStream->AddHeader("Content-Type",
- PromiseFlatString(
- "application/x-www-form-urlencoded; charset="
- + mCharset
- ).get());
- #else
- mimeStream->AddHeader("Content-Type",
- "application/x-www-form-urlencoded");
- #endif
- mimeStream->SetAddContentLength(true);
- mimeStream->SetData(dataStream);
- *aPostDataStream = mimeStream;
- NS_ADDREF(*aPostDataStream);
- }
- } else {
- // Get the full query string
- bool schemeIsJavaScript;
- rv = aURI->SchemeIs("javascript", &schemeIsJavaScript);
- NS_ENSURE_SUCCESS(rv, rv);
- if (schemeIsJavaScript) {
- return NS_OK;
- }
- nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
- if (url) {
- url->SetQuery(mQueryString);
- }
- else {
- nsAutoCString path;
- rv = aURI->GetPath(path);
- NS_ENSURE_SUCCESS(rv, rv);
- // Bug 42616: Trim off named anchor and save it to add later
- int32_t namedAnchorPos = path.FindChar('#');
- nsAutoCString namedAnchor;
- if (kNotFound != namedAnchorPos) {
- path.Right(namedAnchor, (path.Length() - namedAnchorPos));
- path.Truncate(namedAnchorPos);
- }
- // Chop off old query string (bug 25330, 57333)
- // Only do this for GET not POST (bug 41585)
- int32_t queryStart = path.FindChar('?');
- if (kNotFound != queryStart) {
- path.Truncate(queryStart);
- }
- path.Append('?');
- // Bug 42616: Add named anchor to end after query string
- path.Append(mQueryString + namedAnchor);
- aURI->SetPath(path);
- }
- }
- return rv;
- }
- // i18n helper routines
- nsresult
- FSURLEncoded::URLEncode(const nsAString& aStr, nsACString& aEncoded)
- {
- // convert to CRLF breaks
- int32_t convertedBufLength = 0;
- char16_t* convertedBuf =
- nsLinebreakConverter::ConvertUnicharLineBreaks(aStr.BeginReading(),
- nsLinebreakConverter::eLinebreakAny,
- nsLinebreakConverter::eLinebreakNet,
- aStr.Length(),
- &convertedBufLength);
- NS_ENSURE_TRUE(convertedBuf, NS_ERROR_OUT_OF_MEMORY);
- nsAutoString convertedString;
- convertedString.Adopt(convertedBuf, convertedBufLength);
- nsAutoCString encodedBuf;
- nsresult rv = EncodeVal(convertedString, encodedBuf, false);
- NS_ENSURE_SUCCESS(rv, rv);
- if (NS_WARN_IF(!NS_Escape(encodedBuf, aEncoded, url_XPAlphas))) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- return NS_OK;
- }
- } // anonymous namespace
- // --------------------------------------------------------------------------
- FSMultipartFormData::FSMultipartFormData(const nsACString& aCharset,
- nsIContent* aOriginatingElement)
- : EncodingFormSubmission(aCharset, aOriginatingElement)
- {
- mPostDataStream =
- do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
- mTotalLength = 0;
- mBoundary.AssignLiteral("---------------------------");
- mBoundary.AppendInt(rand());
- mBoundary.AppendInt(rand());
- mBoundary.AppendInt(rand());
- }
- FSMultipartFormData::~FSMultipartFormData()
- {
- NS_ASSERTION(mPostDataChunk.IsEmpty(), "Left unsubmitted data");
- }
- nsIInputStream*
- FSMultipartFormData::GetSubmissionBody(uint64_t* aContentLength)
- {
- // Finish data
- mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
- + NS_LITERAL_CSTRING("--" CRLF);
- // Add final data input stream
- AddPostDataStream();
- *aContentLength = mTotalLength;
- return mPostDataStream;
- }
- nsresult
- FSMultipartFormData::AddNameValuePair(const nsAString& aName,
- const nsAString& aValue)
- {
- nsCString valueStr;
- nsAutoCString encodedVal;
- nsresult rv = EncodeVal(aValue, encodedVal, false);
- NS_ENSURE_SUCCESS(rv, rv);
- valueStr.Adopt(nsLinebreakConverter::
- ConvertLineBreaks(encodedVal.get(),
- nsLinebreakConverter::eLinebreakAny,
- nsLinebreakConverter::eLinebreakNet));
- nsAutoCString nameStr;
- rv = EncodeVal(aName, nameStr, true);
- NS_ENSURE_SUCCESS(rv, rv);
- // Make MIME block for name/value pair
- // XXX: name parameter should be encoded per RFC 2231
- // RFC 2388 specifies that RFC 2047 be used, but I think it's not
- // consistent with MIME standard.
- mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
- + NS_LITERAL_CSTRING(CRLF)
- + NS_LITERAL_CSTRING("Content-Disposition: form-data; name=\"")
- + nameStr + NS_LITERAL_CSTRING("\"" CRLF CRLF)
- + valueStr + NS_LITERAL_CSTRING(CRLF);
- return NS_OK;
- }
- nsresult
- FSMultipartFormData::AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob)
- {
- // Encode the control name
- nsAutoCString nameStr;
- nsresult rv = EncodeVal(aName, nameStr, true);
- NS_ENSURE_SUCCESS(rv, rv);
- ErrorResult error;
- uint64_t size = 0;
- nsAutoCString filename;
- nsAutoCString contentType;
- nsCOMPtr<nsIInputStream> fileStream;
- if (aBlob) {
- nsAutoString filename16;
- RefPtr<File> file = aBlob->ToFile();
- if (file) {
- nsAutoString relativePath;
- file->GetRelativePath(relativePath);
- if (Directory::WebkitBlinkDirectoryPickerEnabled(nullptr, nullptr) &&
- !relativePath.IsEmpty()) {
- filename16 = relativePath;
- }
- if (filename16.IsEmpty()) {
- RetrieveFileName(aBlob, filename16);
- }
- }
- rv = EncodeVal(filename16, filename, true);
- NS_ENSURE_SUCCESS(rv, rv);
- // Get content type
- nsAutoString contentType16;
- aBlob->GetType(contentType16);
- if (contentType16.IsEmpty()) {
- contentType16.AssignLiteral("application/octet-stream");
- }
- contentType.Adopt(nsLinebreakConverter::
- ConvertLineBreaks(NS_ConvertUTF16toUTF8(contentType16).get(),
- nsLinebreakConverter::eLinebreakAny,
- nsLinebreakConverter::eLinebreakSpace));
- // Get input stream
- aBlob->GetInternalStream(getter_AddRefs(fileStream), error);
- if (NS_WARN_IF(error.Failed())) {
- return error.StealNSResult();
- }
- // Get size
- size = aBlob->GetSize(error);
- if (error.Failed()) {
- error.SuppressException();
- fileStream = nullptr;
- }
- if (fileStream) {
- // Create buffered stream (for efficiency)
- nsCOMPtr<nsIInputStream> bufferedStream;
- rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
- fileStream, 8192);
- NS_ENSURE_SUCCESS(rv, rv);
- fileStream = bufferedStream;
- }
- } else {
- contentType.AssignLiteral("application/octet-stream");
- }
- AddDataChunk(nameStr, filename, contentType, fileStream, size);
- return NS_OK;
- }
- nsresult
- FSMultipartFormData::AddNameDirectoryPair(const nsAString& aName,
- Directory* aDirectory)
- {
- if (!Directory::WebkitBlinkDirectoryPickerEnabled(nullptr, nullptr)) {
- return NS_OK;
- }
- // Encode the control name
- nsAutoCString nameStr;
- nsresult rv = EncodeVal(aName, nameStr, true);
- NS_ENSURE_SUCCESS(rv, rv);
- nsAutoCString dirname;
- nsAutoString dirname16;
- ErrorResult error;
- nsAutoString path;
- aDirectory->GetPath(path, error);
- if (NS_WARN_IF(error.Failed())) {
- error.SuppressException();
- } else {
- dirname16 = path;
- }
- if (dirname16.IsEmpty()) {
- RetrieveDirectoryName(aDirectory, dirname16);
- }
- rv = EncodeVal(dirname16, dirname, true);
- NS_ENSURE_SUCCESS(rv, rv);
- AddDataChunk(nameStr, dirname,
- NS_LITERAL_CSTRING("application/octet-stream"),
- nullptr, 0);
- return NS_OK;
- }
- void
- FSMultipartFormData::AddDataChunk(const nsACString& aName,
- const nsACString& aFilename,
- const nsACString& aContentType,
- nsIInputStream* aInputStream,
- uint64_t aInputStreamSize)
- {
- //
- // Make MIME block for name/value pair
- //
- // more appropriate than always using binary?
- mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
- + NS_LITERAL_CSTRING(CRLF);
- // XXX: name/filename parameter should be encoded per RFC 2231
- // RFC 2388 specifies that RFC 2047 be used, but I think it's not
- // consistent with the MIME standard.
- mPostDataChunk +=
- NS_LITERAL_CSTRING("Content-Disposition: form-data; name=\"")
- + aName + NS_LITERAL_CSTRING("\"; filename=\"")
- + aFilename + NS_LITERAL_CSTRING("\"" CRLF)
- + NS_LITERAL_CSTRING("Content-Type: ")
- + aContentType + NS_LITERAL_CSTRING(CRLF CRLF);
- // We should not try to append an invalid stream. That will happen for example
- // if we try to update a file that actually do not exist.
- if (aInputStream) {
- // We need to dump the data up to this point into the POST data stream
- // here, since we're about to add the file input stream
- AddPostDataStream();
- mPostDataStream->AppendStream(aInputStream);
- mTotalLength += aInputStreamSize;
- }
- // CRLF after file
- mPostDataChunk.AppendLiteral(CRLF);
- }
- nsresult
- FSMultipartFormData::GetEncodedSubmission(nsIURI* aURI,
- nsIInputStream** aPostDataStream)
- {
- nsresult rv;
- // Make header
- nsCOMPtr<nsIMIMEInputStream> mimeStream
- = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- nsAutoCString contentType;
- GetContentType(contentType);
- mimeStream->AddHeader("Content-Type", contentType.get());
- mimeStream->SetAddContentLength(true);
- uint64_t unused;
- mimeStream->SetData(GetSubmissionBody(&unused));
- mimeStream.forget(aPostDataStream);
- return NS_OK;
- }
- nsresult
- FSMultipartFormData::AddPostDataStream()
- {
- nsresult rv = NS_OK;
- nsCOMPtr<nsIInputStream> postDataChunkStream;
- rv = NS_NewCStringInputStream(getter_AddRefs(postDataChunkStream),
- mPostDataChunk);
- NS_ASSERTION(postDataChunkStream, "Could not open a stream for POST!");
- if (postDataChunkStream) {
- mPostDataStream->AppendStream(postDataChunkStream);
- mTotalLength += mPostDataChunk.Length();
- }
- mPostDataChunk.Truncate();
- return rv;
- }
- // --------------------------------------------------------------------------
- namespace {
- class FSTextPlain : public EncodingFormSubmission
- {
- public:
- FSTextPlain(const nsACString& aCharset, nsIContent* aOriginatingElement)
- : EncodingFormSubmission(aCharset, aOriginatingElement)
- {
- }
- virtual nsresult
- AddNameValuePair(const nsAString& aName, const nsAString& aValue) override;
- virtual nsresult
- AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
- virtual nsresult
- AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
- virtual nsresult
- GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override;
- private:
- nsString mBody;
- };
- nsresult
- FSTextPlain::AddNameValuePair(const nsAString& aName, const nsAString& aValue)
- {
- // XXX This won't work well with a name like "a=b" or "a\nb" but I suppose
- // text/plain doesn't care about that. Parsers aren't built for escaped
- // values so we'll have to live with it.
- mBody.Append(aName + NS_LITERAL_STRING("=") + aValue +
- NS_LITERAL_STRING(CRLF));
- return NS_OK;
- }
- nsresult
- FSTextPlain::AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob)
- {
- nsAutoString filename;
- RetrieveFileName(aBlob, filename);
- AddNameValuePair(aName, filename);
- return NS_OK;
- }
- nsresult
- FSTextPlain::AddNameDirectoryPair(const nsAString& aName,
- Directory* aDirectory)
- {
- nsAutoString dirname;
- RetrieveDirectoryName(aDirectory, dirname);
- AddNameValuePair(aName, dirname);
- return NS_OK;
- }
- nsresult
- FSTextPlain::GetEncodedSubmission(nsIURI* aURI,
- nsIInputStream** aPostDataStream)
- {
- nsresult rv = NS_OK;
- // XXX HACK We are using the standard URL mechanism to give the body to the
- // mailer instead of passing the post data stream to it, since that sounds
- // hard.
- bool isMailto = false;
- aURI->SchemeIs("mailto", &isMailto);
- if (isMailto) {
- nsAutoCString path;
- rv = aURI->GetPath(path);
- NS_ENSURE_SUCCESS(rv, rv);
- HandleMailtoSubject(path);
- // Append the body to and force-plain-text args to the mailto line
- nsAutoCString escapedBody;
- if (NS_WARN_IF(!NS_Escape(NS_ConvertUTF16toUTF8(mBody), escapedBody,
- url_XAlphas))) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- path += NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody;
- rv = aURI->SetPath(path);
- } else {
- // Create data stream.
- // We do want to send the data through the charset encoder and we want to
- // normalize linebreaks to use the "standard net" format (\r\n), but we
- // don't want to perform any other encoding. This means that names and
- // values which contains '=' or newlines are potentially ambigiously
- // encoded, but that how text/plain is specced.
- nsCString cbody;
- EncodeVal(mBody, cbody, false);
- cbody.Adopt(nsLinebreakConverter::
- ConvertLineBreaks(cbody.get(),
- nsLinebreakConverter::eLinebreakAny,
- nsLinebreakConverter::eLinebreakNet));
- nsCOMPtr<nsIInputStream> bodyStream;
- rv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), cbody);
- if (!bodyStream) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- // Create mime stream with headers and such
- nsCOMPtr<nsIMIMEInputStream> mimeStream
- = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
- NS_ENSURE_SUCCESS(rv, rv);
- mimeStream->AddHeader("Content-Type", "text/plain");
- mimeStream->SetAddContentLength(true);
- mimeStream->SetData(bodyStream);
- CallQueryInterface(mimeStream, aPostDataStream);
- }
- return rv;
- }
- } // anonymous namespace
- // --------------------------------------------------------------------------
- EncodingFormSubmission::EncodingFormSubmission(const nsACString& aCharset,
- nsIContent* aOriginatingElement)
- : HTMLFormSubmission(aCharset, aOriginatingElement)
- , mEncoder(aCharset)
- {
- if (!(aCharset.EqualsLiteral("UTF-8") || aCharset.EqualsLiteral("gb18030"))) {
- NS_ConvertUTF8toUTF16 charsetUtf16(aCharset);
- const char16_t* charsetPtr = charsetUtf16.get();
- SendJSWarning(aOriginatingElement ? aOriginatingElement->GetOwnerDocument()
- : nullptr,
- "CannotEncodeAllUnicode",
- &charsetPtr,
- 1);
- }
- }
- EncodingFormSubmission::~EncodingFormSubmission()
- {
- }
- // i18n helper routines
- nsresult
- EncodingFormSubmission::EncodeVal(const nsAString& aStr, nsCString& aOut,
- bool aHeaderEncode)
- {
- if (!mEncoder.Encode(aStr, aOut)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- if (aHeaderEncode) {
- aOut.Adopt(nsLinebreakConverter::
- ConvertLineBreaks(aOut.get(),
- nsLinebreakConverter::eLinebreakAny,
- nsLinebreakConverter::eLinebreakSpace));
- aOut.ReplaceSubstring(NS_LITERAL_CSTRING("\""),
- NS_LITERAL_CSTRING("\\\""));
- }
- return NS_OK;
- }
- // --------------------------------------------------------------------------
- namespace {
- void
- GetSubmitCharset(nsGenericHTMLElement* aForm,
- nsACString& oCharset)
- {
- oCharset.AssignLiteral("UTF-8"); // default to utf-8
- nsAutoString acceptCharsetValue;
- aForm->GetAttr(kNameSpaceID_None, nsGkAtoms::acceptcharset,
- acceptCharsetValue);
- int32_t charsetLen = acceptCharsetValue.Length();
- if (charsetLen > 0) {
- int32_t offset=0;
- int32_t spPos=0;
- // get charset from charsets one by one
- do {
- spPos = acceptCharsetValue.FindChar(char16_t(' '), offset);
- int32_t cnt = ((-1==spPos)?(charsetLen-offset):(spPos-offset));
- if (cnt > 0) {
- nsAutoString uCharset;
- acceptCharsetValue.Mid(uCharset, offset, cnt);
- if (EncodingUtils::FindEncodingForLabelNoReplacement(uCharset, oCharset))
- return;
- }
- offset = spPos + 1;
- } while (spPos != -1);
- }
- // if there are no accept-charset or all the charset are not supported
- // Get the charset from document
- nsIDocument* doc = aForm->GetComposedDoc();
- if (doc) {
- oCharset = doc->GetDocumentCharacterSet();
- }
- }
- void
- GetEnumAttr(nsGenericHTMLElement* aContent,
- nsIAtom* atom, int32_t* aValue)
- {
- const nsAttrValue* value = aContent->GetParsedAttr(atom);
- if (value && value->Type() == nsAttrValue::eEnum) {
- *aValue = value->GetEnumValue();
- }
- }
- } // anonymous namespace
- /* static */ nsresult
- HTMLFormSubmission::GetFromForm(nsGenericHTMLElement* aForm,
- nsGenericHTMLElement* aOriginatingElement,
- HTMLFormSubmission** aFormSubmission)
- {
- // Get all the information necessary to encode the form data
- NS_ASSERTION(aForm->GetComposedDoc(),
- "Should have doc if we're building submission!");
- // Get encoding type (default: urlencoded)
- int32_t enctype = NS_FORM_ENCTYPE_URLENCODED;
- if (aOriginatingElement &&
- aOriginatingElement->HasAttr(kNameSpaceID_None, nsGkAtoms::formenctype)) {
- GetEnumAttr(aOriginatingElement, nsGkAtoms::formenctype, &enctype);
- } else {
- GetEnumAttr(aForm, nsGkAtoms::enctype, &enctype);
- }
- // Get method (default: GET)
- int32_t method = NS_FORM_METHOD_GET;
- if (aOriginatingElement &&
- aOriginatingElement->HasAttr(kNameSpaceID_None, nsGkAtoms::formmethod)) {
- GetEnumAttr(aOriginatingElement, nsGkAtoms::formmethod, &method);
- } else {
- GetEnumAttr(aForm, nsGkAtoms::method, &method);
- }
- // Get charset
- nsAutoCString charset;
- GetSubmitCharset(aForm, charset);
- // We now have a canonical charset name, so we only have to check it
- // against canonical names.
- // use UTF-8 for UTF-16* (per WHATWG and existing practice of
- // MS IE/Opera).
- if (StringBeginsWith(charset, NS_LITERAL_CSTRING("UTF-16"))) {
- charset.AssignLiteral("UTF-8");
- }
- // Choose encoder
- if (method == NS_FORM_METHOD_POST &&
- enctype == NS_FORM_ENCTYPE_MULTIPART) {
- *aFormSubmission = new FSMultipartFormData(charset, aOriginatingElement);
- } else if (method == NS_FORM_METHOD_POST &&
- enctype == NS_FORM_ENCTYPE_TEXTPLAIN) {
- *aFormSubmission = new FSTextPlain(charset, aOriginatingElement);
- } else {
- nsIDocument* doc = aForm->OwnerDoc();
- if (enctype == NS_FORM_ENCTYPE_MULTIPART ||
- enctype == NS_FORM_ENCTYPE_TEXTPLAIN) {
- nsAutoString enctypeStr;
- if (aOriginatingElement &&
- aOriginatingElement->HasAttr(kNameSpaceID_None,
- nsGkAtoms::formenctype)) {
- aOriginatingElement->GetAttr(kNameSpaceID_None, nsGkAtoms::formenctype,
- enctypeStr);
- } else {
- aForm->GetAttr(kNameSpaceID_None, nsGkAtoms::enctype, enctypeStr);
- }
- const char16_t* enctypeStrPtr = enctypeStr.get();
- SendJSWarning(doc, "ForgotPostWarning",
- &enctypeStrPtr, 1);
- }
- *aFormSubmission = new FSURLEncoded(charset, method, doc,
- aOriginatingElement);
- }
- return NS_OK;
- }
- } // dom namespace
- } // mozilla namespace
|