LCOV - code coverage report
Current view: top level - dom/html - HTMLFormSubmission.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 426 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 38 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "HTMLFormSubmission.h"
       8             : 
       9             : #include "nsCOMPtr.h"
      10             : #include "nsIForm.h"
      11             : #include "nsILinkHandler.h"
      12             : #include "nsIDocument.h"
      13             : #include "nsGkAtoms.h"
      14             : #include "nsIFormControl.h"
      15             : #include "nsIDOMHTMLFormElement.h"
      16             : #include "nsError.h"
      17             : #include "nsGenericHTMLElement.h"
      18             : #include "nsAttrValueInlines.h"
      19             : #include "nsISaveAsCharset.h"
      20             : #include "nsIFile.h"
      21             : #include "nsDirectoryServiceDefs.h"
      22             : #include "nsStringStream.h"
      23             : #include "nsIURI.h"
      24             : #include "nsIURL.h"
      25             : #include "nsNetUtil.h"
      26             : #include "nsLinebreakConverter.h"
      27             : #include "nsEscape.h"
      28             : #include "nsUnicharUtils.h"
      29             : #include "nsIMultiplexInputStream.h"
      30             : #include "nsIMIMEInputStream.h"
      31             : #include "nsIMIMEService.h"
      32             : #include "nsIConsoleService.h"
      33             : #include "nsIScriptError.h"
      34             : #include "nsIStringBundle.h"
      35             : #include "nsCExternalHandlerService.h"
      36             : #include "nsIFileStreams.h"
      37             : #include "nsContentUtils.h"
      38             : #include "mozilla/Telemetry.h"
      39             : 
      40             : #include "mozilla/dom/Directory.h"
      41             : #include "mozilla/dom/File.h"
      42             : 
      43             : namespace mozilla {
      44             : namespace dom {
      45             : 
      46             : namespace {
      47             : 
      48             : void
      49           0 : SendJSWarning(nsIDocument* aDocument,
      50             :               const char* aWarningName,
      51             :               const char16_t** aWarningArgs, uint32_t aWarningArgsLen)
      52             : {
      53           0 :   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
      54           0 :                                   NS_LITERAL_CSTRING("HTML"), aDocument,
      55             :                                   nsContentUtils::eFORMS_PROPERTIES,
      56             :                                   aWarningName,
      57           0 :                                   aWarningArgs, aWarningArgsLen);
      58           0 : }
      59             : 
      60             : void
      61           0 : RetrieveFileName(Blob* aBlob, nsAString& aFilename)
      62             : {
      63           0 :   if (!aBlob) {
      64           0 :     return;
      65             :   }
      66             : 
      67           0 :   RefPtr<File> file = aBlob->ToFile();
      68           0 :   if (file) {
      69           0 :     file->GetName(aFilename);
      70             :   }
      71             : }
      72             : 
      73             : void
      74           0 : RetrieveDirectoryName(Directory* aDirectory, nsAString& aDirname)
      75             : {
      76           0 :   MOZ_ASSERT(aDirectory);
      77             : 
      78           0 :   ErrorResult rv;
      79           0 :   aDirectory->GetName(aDirname, rv);
      80           0 :   if (NS_WARN_IF(rv.Failed())) {
      81           0 :     rv.SuppressException();
      82           0 :     aDirname.Truncate();
      83             :   }
      84           0 : }
      85             : 
      86             : // --------------------------------------------------------------------------
      87             : 
      88           0 : class FSURLEncoded : public EncodingFormSubmission
      89             : {
      90             : public:
      91             :   /**
      92             :    * @param aEncoding the character encoding of the form
      93             :    * @param aMethod the method of the submit (either NS_FORM_METHOD_GET or
      94             :    *        NS_FORM_METHOD_POST).
      95             :    */
      96           0 :   FSURLEncoded(NotNull<const Encoding*> aEncoding,
      97             :                int32_t aMethod,
      98             :                nsIDocument* aDocument,
      99             :                nsIContent* aOriginatingElement)
     100           0 :     : EncodingFormSubmission(aEncoding, aOriginatingElement)
     101             :     , mMethod(aMethod)
     102             :     , mDocument(aDocument)
     103           0 :     , mWarnedFileControl(false)
     104             :   {
     105           0 :   }
     106             : 
     107             :   virtual nsresult
     108             :   AddNameValuePair(const nsAString& aName, const nsAString& aValue) override;
     109             : 
     110             :   virtual nsresult
     111             :   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
     112             : 
     113             :   virtual nsresult
     114             :   AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
     115             : 
     116             :   virtual nsresult
     117             :   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override;
     118             : 
     119           0 :   virtual bool SupportsIsindexSubmission() override
     120             :   {
     121           0 :     return true;
     122             :   }
     123             : 
     124             :   virtual nsresult AddIsindex(const nsAString& aValue) override;
     125             : 
     126             : protected:
     127             : 
     128             :   /**
     129             :    * URL encode a Unicode string by encoding it to bytes, converting linebreaks
     130             :    * properly, and then escaping many bytes as %xx.
     131             :    *
     132             :    * @param aStr the string to encode
     133             :    * @param aEncoded the encoded string [OUT]
     134             :    * @throws NS_ERROR_OUT_OF_MEMORY if we run out of memory
     135             :    */
     136             :   nsresult URLEncode(const nsAString& aStr, nsACString& aEncoded);
     137             : 
     138             : private:
     139             :   /**
     140             :    * The method of the submit (either NS_FORM_METHOD_GET or
     141             :    * NS_FORM_METHOD_POST).
     142             :    */
     143             :   int32_t mMethod;
     144             : 
     145             :   /** The query string so far (the part after the ?) */
     146             :   nsCString mQueryString;
     147             : 
     148             :   /** The document whose URI to use when reporting errors */
     149             :   nsCOMPtr<nsIDocument> mDocument;
     150             : 
     151             :   /** Whether or not we have warned about a file control not being submitted */
     152             :   bool mWarnedFileControl;
     153             : };
     154             : 
     155             : nsresult
     156           0 : FSURLEncoded::AddNameValuePair(const nsAString& aName,
     157             :                                const nsAString& aValue)
     158             : {
     159             :   // Encode value
     160           0 :   nsCString convValue;
     161           0 :   nsresult rv = URLEncode(aValue, convValue);
     162           0 :   NS_ENSURE_SUCCESS(rv, rv);
     163             : 
     164             :   // Encode name
     165           0 :   nsAutoCString convName;
     166           0 :   rv = URLEncode(aName, convName);
     167           0 :   NS_ENSURE_SUCCESS(rv, rv);
     168             : 
     169             : 
     170             :   // Append data to string
     171           0 :   if (mQueryString.IsEmpty()) {
     172           0 :     mQueryString += convName + NS_LITERAL_CSTRING("=") + convValue;
     173             :   } else {
     174           0 :     mQueryString += NS_LITERAL_CSTRING("&") + convName
     175           0 :                   + NS_LITERAL_CSTRING("=") + convValue;
     176             :   }
     177             : 
     178           0 :   return NS_OK;
     179             : }
     180             : 
     181             : nsresult
     182           0 : FSURLEncoded::AddIsindex(const nsAString& aValue)
     183             : {
     184             :   // Encode value
     185           0 :   nsCString convValue;
     186           0 :   nsresult rv = URLEncode(aValue, convValue);
     187           0 :   NS_ENSURE_SUCCESS(rv, rv);
     188             : 
     189             :   // Append data to string
     190           0 :   if (mQueryString.IsEmpty()) {
     191           0 :     Telemetry::Accumulate(Telemetry::FORM_ISINDEX_USED, true);
     192           0 :     mQueryString.Assign(convValue);
     193             :   } else {
     194           0 :     mQueryString += NS_LITERAL_CSTRING("&isindex=") + convValue;
     195             :   }
     196             : 
     197           0 :   return NS_OK;
     198             : }
     199             : 
     200             : nsresult
     201           0 : FSURLEncoded::AddNameBlobOrNullPair(const nsAString& aName,
     202             :                                     Blob* aBlob)
     203             : {
     204           0 :   if (!mWarnedFileControl) {
     205           0 :     SendJSWarning(mDocument, "ForgotFileEnctypeWarning", nullptr, 0);
     206           0 :     mWarnedFileControl = true;
     207             :   }
     208             : 
     209           0 :   nsAutoString filename;
     210           0 :   RetrieveFileName(aBlob, filename);
     211           0 :   return AddNameValuePair(aName, filename);
     212             : }
     213             : 
     214             : nsresult
     215           0 : FSURLEncoded::AddNameDirectoryPair(const nsAString& aName,
     216             :                                    Directory* aDirectory)
     217             : {
     218             :   // No warning about because Directory objects are never sent via form.
     219             : 
     220           0 :   nsAutoString dirname;
     221           0 :   RetrieveDirectoryName(aDirectory, dirname);
     222           0 :   return AddNameValuePair(aName, dirname);
     223             : }
     224             : 
     225             : void
     226           0 : HandleMailtoSubject(nsCString& aPath)
     227             : {
     228             :   // Walk through the string and see if we have a subject already.
     229           0 :   bool hasSubject = false;
     230           0 :   bool hasParams = false;
     231           0 :   int32_t paramSep = aPath.FindChar('?');
     232           0 :   while (paramSep != kNotFound && paramSep < (int32_t)aPath.Length()) {
     233           0 :     hasParams = true;
     234             : 
     235             :     // Get the end of the name at the = op.  If it is *after* the next &,
     236             :     // assume that someone made a parameter without an = in it
     237           0 :     int32_t nameEnd = aPath.FindChar('=', paramSep+1);
     238           0 :     int32_t nextParamSep = aPath.FindChar('&', paramSep+1);
     239           0 :     if (nextParamSep == kNotFound) {
     240           0 :       nextParamSep = aPath.Length();
     241             :     }
     242             : 
     243             :     // If the = op is after the &, this parameter is a name without value.
     244             :     // If there is no = op, same thing.
     245           0 :     if (nameEnd == kNotFound || nextParamSep < nameEnd) {
     246           0 :       nameEnd = nextParamSep;
     247             :     }
     248             : 
     249           0 :     if (nameEnd != kNotFound) {
     250           0 :       if (Substring(aPath, paramSep+1, nameEnd-(paramSep+1)).
     251             :           LowerCaseEqualsLiteral("subject")) {
     252           0 :         hasSubject = true;
     253           0 :         break;
     254             :       }
     255             :     }
     256             : 
     257           0 :     paramSep = nextParamSep;
     258             :   }
     259             : 
     260             :   // If there is no subject, append a preformed subject to the mailto line
     261           0 :   if (!hasSubject) {
     262           0 :     if (hasParams) {
     263           0 :       aPath.Append('&');
     264             :     } else {
     265           0 :       aPath.Append('?');
     266             :     }
     267             : 
     268             :     // Get the default subject
     269           0 :     nsXPIDLString brandName;
     270             :     nsresult rv =
     271             :       nsContentUtils::GetLocalizedString(nsContentUtils::eBRAND_PROPERTIES,
     272           0 :                                          "brandShortName", brandName);
     273           0 :     if (NS_FAILED(rv))
     274           0 :       return;
     275           0 :     const char16_t *formatStrings[] = { brandName.get() };
     276           0 :     nsXPIDLString subjectStr;
     277             :     rv = nsContentUtils::FormatLocalizedString(
     278             :                                            nsContentUtils::eFORMS_PROPERTIES,
     279             :                                            "DefaultFormSubject",
     280             :                                            formatStrings,
     281           0 :                                            subjectStr);
     282           0 :     if (NS_FAILED(rv))
     283           0 :       return;
     284           0 :     aPath.AppendLiteral("subject=");
     285           0 :     nsCString subjectStrEscaped;
     286           0 :     rv = NS_EscapeURL(NS_ConvertUTF16toUTF8(subjectStr), esc_Query,
     287           0 :                       subjectStrEscaped, mozilla::fallible);
     288           0 :     if (NS_FAILED(rv))
     289           0 :       return;
     290             : 
     291           0 :     aPath.Append(subjectStrEscaped);
     292             :   }
     293             : }
     294             : 
     295             : nsresult
     296           0 : FSURLEncoded::GetEncodedSubmission(nsIURI* aURI,
     297             :                                    nsIInputStream** aPostDataStream)
     298             : {
     299           0 :   nsresult rv = NS_OK;
     300             : 
     301           0 :   *aPostDataStream = nullptr;
     302             : 
     303           0 :   if (mMethod == NS_FORM_METHOD_POST) {
     304             : 
     305           0 :     bool isMailto = false;
     306           0 :     aURI->SchemeIs("mailto", &isMailto);
     307           0 :     if (isMailto) {
     308             : 
     309           0 :       nsAutoCString path;
     310           0 :       rv = aURI->GetPath(path);
     311           0 :       NS_ENSURE_SUCCESS(rv, rv);
     312             : 
     313           0 :       HandleMailtoSubject(path);
     314             : 
     315             :       // Append the body to and force-plain-text args to the mailto line
     316           0 :       nsAutoCString escapedBody;
     317           0 :       if (NS_WARN_IF(!NS_Escape(mQueryString, escapedBody, url_XAlphas))) {
     318           0 :         return NS_ERROR_OUT_OF_MEMORY;
     319             :       }
     320             : 
     321           0 :       path += NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody;
     322             : 
     323           0 :       rv = aURI->SetPath(path);
     324             : 
     325             :     } else {
     326             : 
     327           0 :       nsCOMPtr<nsIInputStream> dataStream;
     328             :       // XXX We *really* need to either get the string to disown its data (and
     329             :       // not destroy it), or make a string input stream that owns the CString
     330             :       // that is passed to it.  Right now this operation does a copy.
     331           0 :       rv = NS_NewCStringInputStream(getter_AddRefs(dataStream), mQueryString);
     332           0 :       NS_ENSURE_SUCCESS(rv, rv);
     333             : 
     334             :       nsCOMPtr<nsIMIMEInputStream> mimeStream(
     335           0 :         do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
     336           0 :       NS_ENSURE_SUCCESS(rv, rv);
     337             : 
     338           0 :       mimeStream->AddHeader("Content-Type",
     339           0 :                             "application/x-www-form-urlencoded");
     340           0 :       mimeStream->SetAddContentLength(true);
     341           0 :       mimeStream->SetData(dataStream);
     342             : 
     343           0 :       *aPostDataStream = mimeStream;
     344           0 :       NS_ADDREF(*aPostDataStream);
     345             :     }
     346             : 
     347             :   } else {
     348             :     // Get the full query string
     349             :     bool schemeIsJavaScript;
     350           0 :     rv = aURI->SchemeIs("javascript", &schemeIsJavaScript);
     351           0 :     NS_ENSURE_SUCCESS(rv, rv);
     352           0 :     if (schemeIsJavaScript) {
     353           0 :       return NS_OK;
     354             :     }
     355             : 
     356           0 :     nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
     357           0 :     if (url) {
     358           0 :       url->SetQuery(mQueryString);
     359             :     }
     360             :     else {
     361           0 :       nsAutoCString path;
     362           0 :       rv = aURI->GetPath(path);
     363           0 :       NS_ENSURE_SUCCESS(rv, rv);
     364             :       // Bug 42616: Trim off named anchor and save it to add later
     365           0 :       int32_t namedAnchorPos = path.FindChar('#');
     366           0 :       nsAutoCString namedAnchor;
     367           0 :       if (kNotFound != namedAnchorPos) {
     368           0 :         path.Right(namedAnchor, (path.Length() - namedAnchorPos));
     369           0 :         path.Truncate(namedAnchorPos);
     370             :       }
     371             : 
     372             :       // Chop off old query string (bug 25330, 57333)
     373             :       // Only do this for GET not POST (bug 41585)
     374           0 :       int32_t queryStart = path.FindChar('?');
     375           0 :       if (kNotFound != queryStart) {
     376           0 :         path.Truncate(queryStart);
     377             :       }
     378             : 
     379           0 :       path.Append('?');
     380             :       // Bug 42616: Add named anchor to end after query string
     381           0 :       path.Append(mQueryString + namedAnchor);
     382             : 
     383           0 :       aURI->SetPath(path);
     384             :     }
     385             :   }
     386             : 
     387           0 :   return rv;
     388             : }
     389             : 
     390             : // i18n helper routines
     391             : nsresult
     392           0 : FSURLEncoded::URLEncode(const nsAString& aStr, nsACString& aEncoded)
     393             : {
     394             :   // convert to CRLF breaks
     395           0 :   int32_t convertedBufLength = 0;
     396             :   char16_t* convertedBuf =
     397           0 :     nsLinebreakConverter::ConvertUnicharLineBreaks(aStr.BeginReading(),
     398             :                                                    nsLinebreakConverter::eLinebreakAny,
     399             :                                                    nsLinebreakConverter::eLinebreakNet,
     400           0 :                                                    aStr.Length(),
     401           0 :                                                    &convertedBufLength);
     402           0 :   NS_ENSURE_TRUE(convertedBuf, NS_ERROR_OUT_OF_MEMORY);
     403             : 
     404           0 :   nsAutoString convertedString;
     405           0 :   convertedString.Adopt(convertedBuf, convertedBufLength);
     406             : 
     407           0 :   nsAutoCString encodedBuf;
     408           0 :   nsresult rv = EncodeVal(convertedString, encodedBuf, false);
     409           0 :   NS_ENSURE_SUCCESS(rv, rv);
     410             : 
     411           0 :   if (NS_WARN_IF(!NS_Escape(encodedBuf, aEncoded, url_XPAlphas))) {
     412           0 :     return NS_ERROR_OUT_OF_MEMORY;
     413             :   }
     414             : 
     415           0 :   return NS_OK;
     416             : }
     417             : 
     418             : } // anonymous namespace
     419             : 
     420             : // --------------------------------------------------------------------------
     421             : 
     422           0 : FSMultipartFormData::FSMultipartFormData(NotNull<const Encoding*> aEncoding,
     423           0 :                                          nsIContent* aOriginatingElement)
     424           0 :   : EncodingFormSubmission(aEncoding, aOriginatingElement)
     425             : {
     426             :   mPostDataStream =
     427           0 :     do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
     428           0 :   mTotalLength = 0;
     429             : 
     430           0 :   mBoundary.AssignLiteral("---------------------------");
     431           0 :   mBoundary.AppendInt(rand());
     432           0 :   mBoundary.AppendInt(rand());
     433           0 :   mBoundary.AppendInt(rand());
     434           0 : }
     435             : 
     436           0 : FSMultipartFormData::~FSMultipartFormData()
     437             : {
     438           0 :   NS_ASSERTION(mPostDataChunk.IsEmpty(), "Left unsubmitted data");
     439           0 : }
     440             : 
     441             : nsIInputStream*
     442           0 : FSMultipartFormData::GetSubmissionBody(uint64_t* aContentLength)
     443             : {
     444             :   // Finish data
     445           0 :   mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
     446           0 :                   + NS_LITERAL_CSTRING("--" CRLF);
     447             : 
     448             :   // Add final data input stream
     449           0 :   AddPostDataStream();
     450             : 
     451           0 :   *aContentLength = mTotalLength;
     452           0 :   return mPostDataStream;
     453             : }
     454             : 
     455             : nsresult
     456           0 : FSMultipartFormData::AddNameValuePair(const nsAString& aName,
     457             :                                       const nsAString& aValue)
     458             : {
     459           0 :   nsCString valueStr;
     460           0 :   nsAutoCString encodedVal;
     461           0 :   nsresult rv = EncodeVal(aValue, encodedVal, false);
     462           0 :   NS_ENSURE_SUCCESS(rv, rv);
     463             : 
     464           0 :   valueStr.Adopt(nsLinebreakConverter::
     465             :                  ConvertLineBreaks(encodedVal.get(),
     466             :                                    nsLinebreakConverter::eLinebreakAny,
     467           0 :                                    nsLinebreakConverter::eLinebreakNet));
     468             : 
     469           0 :   nsAutoCString nameStr;
     470           0 :   rv = EncodeVal(aName, nameStr, true);
     471           0 :   NS_ENSURE_SUCCESS(rv, rv);
     472             : 
     473             :   // Make MIME block for name/value pair
     474             : 
     475             :   // XXX: name parameter should be encoded per RFC 2231
     476             :   // RFC 2388 specifies that RFC 2047 be used, but I think it's not
     477             :   // consistent with MIME standard.
     478           0 :   mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
     479           0 :                  + NS_LITERAL_CSTRING(CRLF)
     480           0 :                  + NS_LITERAL_CSTRING("Content-Disposition: form-data; name=\"")
     481           0 :                  + nameStr + NS_LITERAL_CSTRING("\"" CRLF CRLF)
     482           0 :                  + valueStr + NS_LITERAL_CSTRING(CRLF);
     483             : 
     484           0 :   return NS_OK;
     485             : }
     486             : 
     487             : nsresult
     488           0 : FSMultipartFormData::AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob)
     489             : {
     490             :   // Encode the control name
     491           0 :   nsAutoCString nameStr;
     492           0 :   nsresult rv = EncodeVal(aName, nameStr, true);
     493           0 :   NS_ENSURE_SUCCESS(rv, rv);
     494             : 
     495           0 :   ErrorResult error;
     496             : 
     497           0 :   uint64_t size = 0;
     498           0 :   nsAutoCString filename;
     499           0 :   nsAutoCString contentType;
     500           0 :   nsCOMPtr<nsIInputStream> fileStream;
     501             : 
     502           0 :   if (aBlob) {
     503           0 :     nsAutoString filename16;
     504             : 
     505           0 :     RefPtr<File> file = aBlob->ToFile();
     506           0 :     if (file) {
     507           0 :       nsAutoString relativePath;
     508           0 :       file->GetRelativePath(relativePath);
     509           0 :       if (Directory::WebkitBlinkDirectoryPickerEnabled(nullptr, nullptr) &&
     510           0 :           !relativePath.IsEmpty()) {
     511           0 :         filename16 = relativePath;
     512             :       }
     513             : 
     514           0 :       if (filename16.IsEmpty()) {
     515           0 :         RetrieveFileName(aBlob, filename16);
     516             :       }
     517             :     }
     518             : 
     519           0 :     rv = EncodeVal(filename16, filename, true);
     520           0 :     NS_ENSURE_SUCCESS(rv, rv);
     521             : 
     522             :     // Get content type
     523           0 :     nsAutoString contentType16;
     524           0 :     aBlob->GetType(contentType16);
     525           0 :     if (contentType16.IsEmpty()) {
     526           0 :       contentType16.AssignLiteral("application/octet-stream");
     527             :     }
     528             : 
     529           0 :     contentType.Adopt(nsLinebreakConverter::
     530           0 :                       ConvertLineBreaks(NS_ConvertUTF16toUTF8(contentType16).get(),
     531             :                                         nsLinebreakConverter::eLinebreakAny,
     532           0 :                                         nsLinebreakConverter::eLinebreakSpace));
     533             : 
     534             :     // Get input stream
     535           0 :     aBlob->GetInternalStream(getter_AddRefs(fileStream), error);
     536           0 :     if (NS_WARN_IF(error.Failed())) {
     537           0 :       return error.StealNSResult();
     538             :     }
     539             : 
     540             :     // Get size
     541           0 :     size = aBlob->GetSize(error);
     542           0 :     if (error.Failed()) {
     543           0 :       error.SuppressException();
     544           0 :       fileStream = nullptr;
     545             :     }
     546             : 
     547           0 :     if (fileStream) {
     548             :       // Create buffered stream (for efficiency)
     549           0 :       nsCOMPtr<nsIInputStream> bufferedStream;
     550           0 :       rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
     551           0 :                                      fileStream, 8192);
     552           0 :       NS_ENSURE_SUCCESS(rv, rv);
     553             : 
     554           0 :       fileStream = bufferedStream;
     555             :     }
     556             :   } else {
     557           0 :     contentType.AssignLiteral("application/octet-stream");
     558             :   }
     559             : 
     560           0 :   AddDataChunk(nameStr, filename, contentType, fileStream, size);
     561           0 :   return NS_OK;
     562             : }
     563             : 
     564             : nsresult
     565           0 : FSMultipartFormData::AddNameDirectoryPair(const nsAString& aName,
     566             :                                           Directory* aDirectory)
     567             : {
     568           0 :   if (!Directory::WebkitBlinkDirectoryPickerEnabled(nullptr, nullptr)) {
     569           0 :     return NS_OK;
     570             :   }
     571             : 
     572             :   // Encode the control name
     573           0 :   nsAutoCString nameStr;
     574           0 :   nsresult rv = EncodeVal(aName, nameStr, true);
     575           0 :   NS_ENSURE_SUCCESS(rv, rv);
     576             : 
     577           0 :   nsAutoCString dirname;
     578           0 :   nsAutoString dirname16;
     579             : 
     580           0 :   ErrorResult error;
     581           0 :   nsAutoString path;
     582           0 :   aDirectory->GetPath(path, error);
     583           0 :   if (NS_WARN_IF(error.Failed())) {
     584           0 :     error.SuppressException();
     585             :   } else {
     586           0 :     dirname16 = path;
     587             :   }
     588             : 
     589           0 :   if (dirname16.IsEmpty()) {
     590           0 :     RetrieveDirectoryName(aDirectory, dirname16);
     591             :   }
     592             : 
     593           0 :   rv = EncodeVal(dirname16, dirname, true);
     594           0 :   NS_ENSURE_SUCCESS(rv, rv);
     595             : 
     596           0 :   AddDataChunk(nameStr, dirname,
     597           0 :                NS_LITERAL_CSTRING("application/octet-stream"),
     598           0 :                nullptr, 0);
     599           0 :   return NS_OK;
     600             : }
     601             : 
     602             : void
     603           0 : FSMultipartFormData::AddDataChunk(const nsACString& aName,
     604             :                                   const nsACString& aFilename,
     605             :                                   const nsACString& aContentType,
     606             :                                   nsIInputStream* aInputStream,
     607             :                                   uint64_t aInputStreamSize)
     608             : {
     609             :   //
     610             :   // Make MIME block for name/value pair
     611             :   //
     612             :   // more appropriate than always using binary?
     613           0 :   mPostDataChunk += NS_LITERAL_CSTRING("--") + mBoundary
     614           0 :                  + NS_LITERAL_CSTRING(CRLF);
     615             :   // XXX: name/filename parameter should be encoded per RFC 2231
     616             :   // RFC 2388 specifies that RFC 2047 be used, but I think it's not
     617             :   // consistent with the MIME standard.
     618             :   mPostDataChunk +=
     619           0 :          NS_LITERAL_CSTRING("Content-Disposition: form-data; name=\"")
     620           0 :        + aName + NS_LITERAL_CSTRING("\"; filename=\"")
     621           0 :        + aFilename + NS_LITERAL_CSTRING("\"" CRLF)
     622           0 :        + NS_LITERAL_CSTRING("Content-Type: ")
     623           0 :        + aContentType + NS_LITERAL_CSTRING(CRLF CRLF);
     624             : 
     625             :   // We should not try to append an invalid stream. That will happen for example
     626             :   // if we try to update a file that actually do not exist.
     627           0 :   if (aInputStream) {
     628             :     // We need to dump the data up to this point into the POST data stream
     629             :     // here, since we're about to add the file input stream
     630           0 :     AddPostDataStream();
     631             : 
     632           0 :     mPostDataStream->AppendStream(aInputStream);
     633           0 :     mTotalLength += aInputStreamSize;
     634             :   }
     635             : 
     636             :   // CRLF after file
     637           0 :   mPostDataChunk.AppendLiteral(CRLF);
     638           0 : }
     639             : 
     640             : nsresult
     641           0 : FSMultipartFormData::GetEncodedSubmission(nsIURI* aURI,
     642             :                                           nsIInputStream** aPostDataStream)
     643             : {
     644             :   nsresult rv;
     645             : 
     646             :   // Make header
     647             :   nsCOMPtr<nsIMIMEInputStream> mimeStream
     648           0 :     = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
     649           0 :   NS_ENSURE_SUCCESS(rv, rv);
     650             : 
     651           0 :   nsAutoCString contentType;
     652           0 :   GetContentType(contentType);
     653           0 :   mimeStream->AddHeader("Content-Type", contentType.get());
     654           0 :   mimeStream->SetAddContentLength(true);
     655             :   uint64_t unused;
     656           0 :   mimeStream->SetData(GetSubmissionBody(&unused));
     657             : 
     658           0 :   mimeStream.forget(aPostDataStream);
     659             : 
     660           0 :   return NS_OK;
     661             : }
     662             : 
     663             : nsresult
     664           0 : FSMultipartFormData::AddPostDataStream()
     665             : {
     666           0 :   nsresult rv = NS_OK;
     667             : 
     668           0 :   nsCOMPtr<nsIInputStream> postDataChunkStream;
     669           0 :   rv = NS_NewCStringInputStream(getter_AddRefs(postDataChunkStream),
     670           0 :                                 mPostDataChunk);
     671           0 :   NS_ASSERTION(postDataChunkStream, "Could not open a stream for POST!");
     672           0 :   if (postDataChunkStream) {
     673           0 :     mPostDataStream->AppendStream(postDataChunkStream);
     674           0 :     mTotalLength += mPostDataChunk.Length();
     675             :   }
     676             : 
     677           0 :   mPostDataChunk.Truncate();
     678             : 
     679           0 :   return rv;
     680             : }
     681             : 
     682             : // --------------------------------------------------------------------------
     683             : 
     684             : namespace {
     685             : 
     686           0 : class FSTextPlain : public EncodingFormSubmission
     687             : {
     688             : public:
     689           0 :   FSTextPlain(NotNull<const Encoding*> aEncoding,
     690             :               nsIContent* aOriginatingElement)
     691           0 :     : EncodingFormSubmission(aEncoding, aOriginatingElement)
     692             :   {
     693           0 :   }
     694             : 
     695             :   virtual nsresult
     696             :   AddNameValuePair(const nsAString& aName, const nsAString& aValue) override;
     697             : 
     698             :   virtual nsresult
     699             :   AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) override;
     700             : 
     701             :   virtual nsresult
     702             :   AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override;
     703             : 
     704             :   virtual nsresult
     705             :   GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream) override;
     706             : 
     707             : private:
     708             :   nsString mBody;
     709             : };
     710             : 
     711             : nsresult
     712           0 : FSTextPlain::AddNameValuePair(const nsAString& aName, const nsAString& aValue)
     713             : {
     714             :   // XXX This won't work well with a name like "a=b" or "a\nb" but I suppose
     715             :   // text/plain doesn't care about that.  Parsers aren't built for escaped
     716             :   // values so we'll have to live with it.
     717           0 :   mBody.Append(aName + NS_LITERAL_STRING("=") + aValue +
     718           0 :                NS_LITERAL_STRING(CRLF));
     719             : 
     720           0 :   return NS_OK;
     721             : }
     722             : 
     723             : nsresult
     724           0 : FSTextPlain::AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob)
     725             : {
     726           0 :   nsAutoString filename;
     727           0 :   RetrieveFileName(aBlob, filename);
     728           0 :   AddNameValuePair(aName, filename);
     729           0 :   return NS_OK;
     730             : }
     731             : 
     732             : nsresult
     733           0 : FSTextPlain::AddNameDirectoryPair(const nsAString& aName,
     734             :                                   Directory* aDirectory)
     735             : {
     736           0 :   nsAutoString dirname;
     737           0 :   RetrieveDirectoryName(aDirectory, dirname);
     738           0 :   AddNameValuePair(aName, dirname);
     739           0 :   return NS_OK;
     740             : }
     741             : 
     742             : nsresult
     743           0 : FSTextPlain::GetEncodedSubmission(nsIURI* aURI,
     744             :                                   nsIInputStream** aPostDataStream)
     745             : {
     746           0 :   nsresult rv = NS_OK;
     747             : 
     748             :   // XXX HACK We are using the standard URL mechanism to give the body to the
     749             :   // mailer instead of passing the post data stream to it, since that sounds
     750             :   // hard.
     751           0 :   bool isMailto = false;
     752           0 :   aURI->SchemeIs("mailto", &isMailto);
     753           0 :   if (isMailto) {
     754           0 :     nsAutoCString path;
     755           0 :     rv = aURI->GetPath(path);
     756           0 :     NS_ENSURE_SUCCESS(rv, rv);
     757             : 
     758           0 :     HandleMailtoSubject(path);
     759             : 
     760             :     // Append the body to and force-plain-text args to the mailto line
     761           0 :     nsAutoCString escapedBody;
     762           0 :     if (NS_WARN_IF(!NS_Escape(NS_ConvertUTF16toUTF8(mBody), escapedBody,
     763             :                               url_XAlphas))) {
     764           0 :       return NS_ERROR_OUT_OF_MEMORY;
     765             :     }
     766             : 
     767           0 :     path += NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody;
     768             : 
     769           0 :     rv = aURI->SetPath(path);
     770             : 
     771             :   } else {
     772             :     // Create data stream.
     773             :     // We do want to send the data through the charset encoder and we want to
     774             :     // normalize linebreaks to use the "standard net" format (\r\n), but we
     775             :     // don't want to perform any other encoding. This means that names and
     776             :     // values which contains '=' or newlines are potentially ambigiously
     777             :     // encoded, but that how text/plain is specced.
     778           0 :     nsCString cbody;
     779           0 :     EncodeVal(mBody, cbody, false);
     780           0 :     cbody.Adopt(nsLinebreakConverter::
     781             :                 ConvertLineBreaks(cbody.get(),
     782             :                                   nsLinebreakConverter::eLinebreakAny,
     783           0 :                                   nsLinebreakConverter::eLinebreakNet));
     784           0 :     nsCOMPtr<nsIInputStream> bodyStream;
     785           0 :     rv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), cbody);
     786           0 :     if (!bodyStream) {
     787           0 :       return NS_ERROR_OUT_OF_MEMORY;
     788             :     }
     789             : 
     790             :     // Create mime stream with headers and such
     791             :     nsCOMPtr<nsIMIMEInputStream> mimeStream
     792           0 :         = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv);
     793           0 :     NS_ENSURE_SUCCESS(rv, rv);
     794             : 
     795           0 :     mimeStream->AddHeader("Content-Type", "text/plain");
     796           0 :     mimeStream->SetAddContentLength(true);
     797           0 :     mimeStream->SetData(bodyStream);
     798           0 :     CallQueryInterface(mimeStream, aPostDataStream);
     799             :   }
     800             : 
     801           0 :   return rv;
     802             : }
     803             : 
     804             : } // anonymous namespace
     805             : 
     806             : // --------------------------------------------------------------------------
     807             : 
     808           0 : EncodingFormSubmission::EncodingFormSubmission(
     809             :   NotNull<const Encoding*> aEncoding,
     810           0 :   nsIContent* aOriginatingElement)
     811           0 :   : HTMLFormSubmission(aEncoding, aOriginatingElement)
     812             : {
     813           0 :   if (!aEncoding->CanEncodeEverything()) {
     814           0 :     nsAutoCString name;
     815           0 :     aEncoding->Name(name);
     816           0 :     NS_ConvertUTF8toUTF16 nameUtf16(name);
     817           0 :     const char16_t* namePtr = nameUtf16.get();
     818           0 :     SendJSWarning(aOriginatingElement ? aOriginatingElement->GetOwnerDocument()
     819             :                                       : nullptr,
     820             :                   "CannotEncodeAllUnicode",
     821             :                   &namePtr,
     822           0 :                   1);
     823             :   }
     824           0 : }
     825             : 
     826           0 : EncodingFormSubmission::~EncodingFormSubmission()
     827             : {
     828           0 : }
     829             : 
     830             : // i18n helper routines
     831             : nsresult
     832           0 : EncodingFormSubmission::EncodeVal(const nsAString& aStr, nsCString& aOut,
     833             :                                   bool aHeaderEncode)
     834             : {
     835             :   nsresult rv;
     836             :   const Encoding* ignored;
     837           0 :   Tie(rv, ignored) = mEncoding->Encode(aStr, aOut);
     838           0 :   if (NS_FAILED(rv)) {
     839           0 :     return rv;
     840             :   }
     841             : 
     842           0 :   if (aHeaderEncode) {
     843           0 :     aOut.Adopt(nsLinebreakConverter::
     844             :                ConvertLineBreaks(aOut.get(),
     845             :                                  nsLinebreakConverter::eLinebreakAny,
     846           0 :                                  nsLinebreakConverter::eLinebreakSpace));
     847           0 :     aOut.ReplaceSubstring(NS_LITERAL_CSTRING("\""),
     848           0 :                           NS_LITERAL_CSTRING("\\\""));
     849             :   }
     850             : 
     851             : 
     852           0 :   return NS_OK;
     853             : }
     854             : 
     855             : // --------------------------------------------------------------------------
     856             : 
     857             : namespace {
     858             : 
     859             : NotNull<const Encoding*>
     860           0 : GetSubmitEncoding(nsGenericHTMLElement* aForm)
     861             : {
     862           0 :   nsAutoString acceptCharsetValue;
     863           0 :   aForm->GetAttr(kNameSpaceID_None, nsGkAtoms::acceptcharset,
     864           0 :                  acceptCharsetValue);
     865             : 
     866           0 :   int32_t charsetLen = acceptCharsetValue.Length();
     867           0 :   if (charsetLen > 0) {
     868           0 :     int32_t offset=0;
     869           0 :     int32_t spPos=0;
     870             :     // get charset from charsets one by one
     871           0 :     do {
     872           0 :       spPos = acceptCharsetValue.FindChar(char16_t(' '), offset);
     873           0 :       int32_t cnt = ((-1==spPos)?(charsetLen-offset):(spPos-offset));
     874           0 :       if (cnt > 0) {
     875           0 :         nsAutoString uCharset;
     876           0 :         acceptCharsetValue.Mid(uCharset, offset, cnt);
     877             : 
     878           0 :         auto encoding = Encoding::ForLabelNoReplacement(uCharset);
     879           0 :         if (encoding) {
     880           0 :           return WrapNotNull(encoding);
     881             :         }
     882             :       }
     883           0 :       offset = spPos + 1;
     884           0 :     } while (spPos != -1);
     885             :   }
     886             :   // if there are no accept-charset or all the charset are not supported
     887             :   // Get the charset from document
     888           0 :   nsIDocument* doc = aForm->GetComposedDoc();
     889           0 :   if (doc) {
     890           0 :     return doc->GetDocumentCharacterSet();
     891             :   }
     892           0 :   return UTF_8_ENCODING;
     893             : }
     894             : 
     895             : void
     896           0 : GetEnumAttr(nsGenericHTMLElement* aContent,
     897             :             nsIAtom* atom, int32_t* aValue)
     898             : {
     899           0 :   const nsAttrValue* value = aContent->GetParsedAttr(atom);
     900           0 :   if (value && value->Type() == nsAttrValue::eEnum) {
     901           0 :     *aValue = value->GetEnumValue();
     902             :   }
     903           0 : }
     904             : 
     905             : } // anonymous namespace
     906             : 
     907             : /* static */ nsresult
     908           0 : HTMLFormSubmission::GetFromForm(nsGenericHTMLElement* aForm,
     909             :                                 nsGenericHTMLElement* aOriginatingElement,
     910             :                                 HTMLFormSubmission** aFormSubmission)
     911             : {
     912             :   // Get all the information necessary to encode the form data
     913           0 :   NS_ASSERTION(aForm->GetComposedDoc(),
     914             :                "Should have doc if we're building submission!");
     915             : 
     916             :   // Get encoding type (default: urlencoded)
     917           0 :   int32_t enctype = NS_FORM_ENCTYPE_URLENCODED;
     918           0 :   if (aOriginatingElement &&
     919           0 :       aOriginatingElement->HasAttr(kNameSpaceID_None, nsGkAtoms::formenctype)) {
     920           0 :     GetEnumAttr(aOriginatingElement, nsGkAtoms::formenctype, &enctype);
     921             :   } else {
     922           0 :     GetEnumAttr(aForm, nsGkAtoms::enctype, &enctype);
     923             :   }
     924             : 
     925             :   // Get method (default: GET)
     926           0 :   int32_t method = NS_FORM_METHOD_GET;
     927           0 :   if (aOriginatingElement &&
     928           0 :       aOriginatingElement->HasAttr(kNameSpaceID_None, nsGkAtoms::formmethod)) {
     929           0 :     GetEnumAttr(aOriginatingElement, nsGkAtoms::formmethod, &method);
     930             :   } else {
     931           0 :     GetEnumAttr(aForm, nsGkAtoms::method, &method);
     932             :   }
     933             : 
     934             :   // Get encoding
     935           0 :   auto encoding = GetSubmitEncoding(aForm)->OutputEncoding();
     936             : 
     937             :   // Choose encoder
     938           0 :   if (method == NS_FORM_METHOD_POST &&
     939           0 :       enctype == NS_FORM_ENCTYPE_MULTIPART) {
     940           0 :     *aFormSubmission = new FSMultipartFormData(encoding, aOriginatingElement);
     941           0 :   } else if (method == NS_FORM_METHOD_POST &&
     942           0 :              enctype == NS_FORM_ENCTYPE_TEXTPLAIN) {
     943           0 :     *aFormSubmission = new FSTextPlain(encoding, aOriginatingElement);
     944             :   } else {
     945           0 :     nsIDocument* doc = aForm->OwnerDoc();
     946           0 :     if (enctype == NS_FORM_ENCTYPE_MULTIPART ||
     947           0 :         enctype == NS_FORM_ENCTYPE_TEXTPLAIN) {
     948           0 :       nsAutoString enctypeStr;
     949           0 :       if (aOriginatingElement &&
     950           0 :           aOriginatingElement->HasAttr(kNameSpaceID_None,
     951             :                                        nsGkAtoms::formenctype)) {
     952           0 :         aOriginatingElement->GetAttr(kNameSpaceID_None, nsGkAtoms::formenctype,
     953           0 :                                      enctypeStr);
     954             :       } else {
     955           0 :         aForm->GetAttr(kNameSpaceID_None, nsGkAtoms::enctype, enctypeStr);
     956             :       }
     957           0 :       const char16_t* enctypeStrPtr = enctypeStr.get();
     958             :       SendJSWarning(doc, "ForgotPostWarning",
     959           0 :                     &enctypeStrPtr, 1);
     960             :     }
     961           0 :     *aFormSubmission =
     962           0 :       new FSURLEncoded(encoding, method, doc, aOriginatingElement);
     963             :   }
     964             : 
     965           0 :   return NS_OK;
     966             : }
     967             : 
     968             : } // dom namespace
     969             : } // mozilla namespace

Generated by: LCOV version 1.13