/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
// Reads over a CRLF and positions start after it. staticbool PushOverLine(nsACString::const_iterator& aStart, const nsACString::const_iterator& aEnd) { if (*aStart == nsCRT::CR && (aEnd - aStart > 1) && *(++aStart) == nsCRT::LF) {
++aStart; // advance to after CRLF returntrue;
}
returnfalse;
}
/** * A simple multipart/form-data parser as defined in RFC 2388 and RFC 2046. * This does not respect any encoding specified per entry, using UTF-8 * throughout. This is as the Fetch spec states in the consume body algorithm. * Borrows some things from Necko's nsMultiMixedConv, but is simpler since * unlike Necko we do not have to deal with receiving incomplete chunks of data. * * This parser will fail the entire parse on any invalid entry, so it will * never return a partially filled FormData. * The content-disposition header is used to figure out the name and filename * entries. The inclusion of the filename parameter decides if the entry is * inserted into the FormData as a string or a File. * * File blobs are copies of the underlying data string since we cannot adopt * char* chunks embedded within the larger body without significant effort. * FIXME(nsm): Bug 1127552 - We should add telemetry to calls to formData() and * friends to figure out if Fetch ends up copying big blobs to see if this is * worth optimizing.
*/ class MOZ_STACK_CLASS FormDataParser { private:
RefPtr<FormData> mFormData;
nsCString mMimeType;
nsCString mMixedCaseMimeType;
nsCString mData;
// Reads over a boundary and sets start to the position after the end of the // boundary. Returns false if no boundary is found immediately. bool PushOverBoundary(const nsACString& aBoundaryString,
nsACString::const_iterator& aStart,
nsACString::const_iterator& aEnd) { // We copy the end iterator to keep the original pointing to the real end // of the string.
nsACString::const_iterator end(aEnd); constchar* beginning = aStart.get(); if (FindInReadable(aBoundaryString, aStart, end)) { // We either should find the body immediately, or after 2 chars with the // 2 chars being '-', everything else is failure. if ((aStart.get() - beginning) == 0) {
aStart.advance(aBoundaryString.Length()); returntrue;
}
if (mName.IsVoid()) { // Could not parse a valid entry name. returnfalse;
}
} elseif (headerName.LowerCaseEqualsLiteral("content-type")) {
mContentType = headerValue;
}
returntrue;
}
// The end of a body is marked by a CRLF followed by the boundary. So the // CRLF is part of the boundary and not the body, but any prior CRLFs are // part of the body. This will position the iterator at the beginning of the // boundary (after the CRLF). bool ParseBody(const nsACString& aBoundaryString,
nsACString::const_iterator& aStart,
nsACString::const_iterator& aEnd) { constchar* beginning = aStart.get();
// Find the boundary marking the end of the body.
nsACString::const_iterator end(aEnd); if (!FindInReadable(aBoundaryString, aStart, end)) { returnfalse;
}
// We found a boundary, strip the just prior CRLF, and consider // everything else the body section. if (aStart.get() - beginning < 2) { // Only the first entry can have a boundary right at the beginning. Even // an empty body will have a CRLF before the boundary. So this is // a failure. returnfalse;
}
// Check that there is a CRLF right before the boundary.
aStart.advance(-2);
// Restore iterator to after the \r\n as we promised. // We do not need to handle the extra hyphens case since our boundary // parser in PushOverBoundary()
aStart.advance(2);
if (!mFormData) {
mFormData = new FormData();
}
NS_ConvertUTF8toUTF16 name(mName);
if (mFilename.IsVoid()) {
ErrorResult rv;
mFormData->Append(name, NS_ConvertUTF8toUTF16(body), rv);
MOZ_ASSERT(!rv.Failed());
} else { // Unfortunately we've to copy the data first since all our strings are // going to free it. We also need fallible alloc, so we can't just use // ToNewCString(). char* copy = static_cast<char*>(moz_xmalloc(body.Length()));
nsCString::const_iterator bodyIter, bodyEnd;
body.BeginReading(bodyIter);
body.EndReading(bodyEnd); char* p = copy; while (bodyIter != bodyEnd) {
*p++ = *bodyIter++;
}
p = nullptr;
bool Parse() { if (mData.IsEmpty()) { returnfalse;
}
// Determine boundary from mimetype.
RefPtr<CMimeType> parsed = CMimeType::Parse(mMixedCaseMimeType); if (!parsed) { returnfalse;
}
nsAutoCString boundaryString; if (!parsed->GetParameterValue("boundary"_ns, boundaryString)) { returnfalse;
}
nsACString::const_iterator start, end;
mData.BeginReading(start); // This should ALWAYS point to the end of data. // Helpers make copies.
mData.EndReading(end);
while (start != end) { switch (mState) { case START_PART:
mName.SetIsVoid(true);
mFilename.SetIsVoid(true);
mContentType = "text/plain"_ns;
while (start != end && NS_IsHTTPWhitespace(*start)) {
++start;
}
// MUST start with boundary. if (!PushOverBoundary(boundaryString, start, end)) { returnfalse;
}
if (start != end && *start == '-') { // End of data. if (!mFormData) {
mFormData = new FormData();
} returntrue;
}
if (!PushOverLine(start, end)) { returnfalse;
}
mState = PARSE_HEADER; break;
case PARSE_HEADER: bool emptyHeader; if (!ParseHeader(start, end, &emptyHeader)) { returnfalse;
}
if (emptyHeader && !PushOverLine(start, end)) { returnfalse;
}
case PARSE_BODY: if (mName.IsVoid()) {
NS_WARNING( "No content-disposition header with a valid name was " "found. Failing at body parse."); returnfalse;
}
if (!ParseBody(boundaryString, start, end)) { returnfalse;
}
mState = START_PART; break;
default:
MOZ_CRASH("Invalid case");
}
}
MOZ_ASSERT_UNREACHABLE("Should never reach here."); returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.