From 81b9da542e545dfaa13fe8e9c847720cbef778cf Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Thu, 30 May 2024 11:53:15 -0700 Subject: [PATCH 1/2] Fix POST form parser edge cases From https://github.com/espressif/arduino-esp32/pull/9167 --- libraries/WebServer/src/Parsing.cpp | 427 ++++++++++++---------------- 1 file changed, 181 insertions(+), 246 deletions(-) diff --git a/libraries/WebServer/src/Parsing.cpp b/libraries/WebServer/src/Parsing.cpp index 28dcd5866..1252be77a 100644 --- a/libraries/WebServer/src/Parsing.cpp +++ b/libraries/WebServer/src/Parsing.cpp @@ -357,265 +357,200 @@ void HTTPServer::_uploadWriteByte(uint8_t b) { } int HTTPServer::_uploadReadByte(WiFiClient * client) { - int res = client->read(); - if (res < 0) { - // keep trying until you either read a valid byte or timeout - unsigned long startMillis = millis(); - unsigned long timeoutIntervalMillis = client->getTimeout(); - bool timedOut = false; - for (;;) { - if (!client->connected()) { - return -1; - } - // loosely modeled after blinkWithoutDelay pattern - while (!timedOut && !client->available() && client->connected()) { - delay(2); - timedOut = millis() - startMillis >= timeoutIntervalMillis; - } + int res = client->read(); - res = client->read(); - if (res >= 0) { - return res; // exit on a valid read - } - // NOTE: it is possible to get here and have all of the following - // assertions hold true - // - // -- client->available() > 0 - // -- client->connected == true - // -- res == -1 - // - // a simple retry strategy overcomes this which is to say the - // assertion is not permanent, but the reason that this works - // is elusive, and possibly indicative of a more subtle underlying - // issue - - timedOut = millis() - startMillis >= timeoutIntervalMillis; - if (timedOut) { - return res; // exit on a timeout - } - } + if (res < 0) { + while (!client->available() && client->connected()) { + delay(2); } - return res; + res = client->read(); + } + + return res; } bool HTTPServer::_parseForm(WiFiClient * client, String boundary, uint32_t len) { - (void) len; - log_v("Parse Form: Boundary: %s Length: %d", boundary.c_str(), len); - String line; - int retry = 0; - do { - line = client->readStringUntil('\r'); - ++retry; - } while (line.length() == 0 && retry < 3); - - client->readStringUntil('\n'); - //start reading the form - if (line == ("--" + boundary)) { - if (_postArgs) { - delete[] _postArgs; - } - _postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS]; - _postArgsLen = 0; - while (1) { - String argName; - String argValue; - String argType; - String argFilename; - bool argIsFile = false; - + (void)len; + log_v("Parse Form: Boundary: %s Length: %d", boundary.c_str(), len); + String line; + int retry = 0; + do { + line = client->readStringUntil('\r'); + ++retry; + } while (line.length() == 0 && retry < 3); + + client->readStringUntil('\n'); + //start reading the form + if (line == ("--" + boundary)) { + if (_postArgs) { + delete[] _postArgs; + } + _postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS]; + _postArgsLen = 0; + while (1) { + String argName; + String argValue; + String argType; + String argFilename; + bool argIsFile = false; + + line = client->readStringUntil('\r'); + client->readStringUntil('\n'); + if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) { + int nameStart = line.indexOf('='); + if (nameStart != -1) { + argName = line.substring(nameStart + 2); + nameStart = argName.indexOf('='); + if (nameStart == -1) { + argName = argName.substring(0, argName.length() - 1); + } else { + argFilename = argName.substring(nameStart + 2, argName.length() - 1); + argName = argName.substring(0, argName.indexOf('"')); + argIsFile = true; + log_v("PostArg FileName: %s", argFilename.c_str()); + //use GET to set the filename if uploading using blob + if (argFilename == F("blob") && hasArg(FPSTR(filename))) { + argFilename = arg(FPSTR(filename)); + } + } + log_v("PostArg Name: %s", argName.c_str()); + using namespace mime; + argType = FPSTR(mimeTable[txt].mimeType); + line = client->readStringUntil('\r'); + client->readStringUntil('\n'); + while (line.length() > 0) { + if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) { + argType = line.substring(line.indexOf(':') + 2); + } + //skip over any other headers line = client->readStringUntil('\r'); client->readStringUntil('\n'); - if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) { - int nameStart = line.indexOf('='); - if (nameStart != -1) { - argName = line.substring(nameStart + 2); - nameStart = argName.indexOf('='); - if (nameStart == -1) { - argName = argName.substring(0, argName.length() - 1); - } else { - argFilename = argName.substring(nameStart + 2, argName.length() - 1); - argName = argName.substring(0, argName.indexOf('"')); - argIsFile = true; - log_v("PostArg FileName: %s", argFilename.c_str()); - //use GET to set the filename if uploading using blob - if (argFilename == F("blob") && hasArg(FPSTR(filename))) { - argFilename = arg(FPSTR(filename)); - } - } - log_v("PostArg Name: %s", argName.c_str()); - using namespace mime; - argType = FPSTR(mimeTable[txt].mimeType); - line = client->readStringUntil('\r'); - client->readStringUntil('\n'); - if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) { - argType = line.substring(line.indexOf(':') + 2); - //skip next line - client->readStringUntil('\r'); - client->readStringUntil('\n'); - } - log_v("PostArg Type: %s", argType.c_str()); - if (!argIsFile) { - while (1) { - line = client->readStringUntil('\r'); - client->readStringUntil('\n'); - if (line.startsWith("--" + boundary)) { - break; - } - if (argValue.length() > 0) { - argValue += "\n"; - } - argValue += line; - } - log_v("PostArg Value: %s", argValue.c_str()); - - RequestArgument& arg = _postArgs[_postArgsLen++]; - arg.key = argName; - arg.value = argValue; - - if (line == ("--" + boundary + "--")) { - log_v("Done Parsing POST"); - break; - } else if (_postArgsLen >= WEBSERVER_MAX_POST_ARGS) { - log_e("Too many PostArgs (max: %d) in request.", WEBSERVER_MAX_POST_ARGS); - return false; - } - } else { - _currentUpload.reset(new HTTPUpload()); - _currentUpload->status = UPLOAD_FILE_START; - _currentUpload->name = argName; - _currentUpload->filename = argFilename; - _currentUpload->type = argType; - _currentUpload->totalSize = 0; - _currentUpload->currentSize = 0; - log_v("Start File: %s Type: %s", _currentUpload->filename.c_str(), _currentUpload->type.c_str()); - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { - _currentHandler->upload(*this, _currentUri, *_currentUpload); - } - _currentUpload->status = UPLOAD_FILE_WRITE; - int argByte = _uploadReadByte(client); -readfile: - - while (argByte != 0x0D) { - if (argByte < 0) { - return _parseFormUploadAborted(); - } - _uploadWriteByte(argByte); - argByte = _uploadReadByte(client); - } - - argByte = _uploadReadByte(client); - if (argByte < 0) { - return _parseFormUploadAborted(); - } - if (argByte == 0x0A) { - argByte = _uploadReadByte(client); - if (argByte < 0) { - return _parseFormUploadAborted(); - } - if ((char)argByte != '-') { - //continue reading the file - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - goto readfile; - } else { - argByte = _uploadReadByte(client); - if (argByte < 0) { - return _parseFormUploadAborted(); - } - if ((char)argByte != '-') { - //continue reading the file - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - _uploadWriteByte((uint8_t)('-')); - goto readfile; - } - } - - uint8_t endBuf[boundary.length()]; - uint32_t i = 0; - while (i < boundary.length()) { - argByte = _uploadReadByte(client); - if (argByte < 0) { - return _parseFormUploadAborted(); - } - if ((char)argByte == 0x0D) { - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - _uploadWriteByte((uint8_t)('-')); - _uploadWriteByte((uint8_t)('-')); - for (uint32_t j = 0; j < i; j++) { - _uploadWriteByte(endBuf[j]); - } - goto readfile; - } - endBuf[i++] = (uint8_t)argByte; - } - - if (strstr((const char*)endBuf, boundary.c_str()) != nullptr) { - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { - _currentHandler->upload(*this, _currentUri, *_currentUpload); - } - _currentUpload->totalSize += _currentUpload->currentSize; - _currentUpload->status = UPLOAD_FILE_END; - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { - _currentHandler->upload(*this, _currentUri, *_currentUpload); - } - log_v("End File: %s Type: %s Size: %d", _currentUpload->filename.c_str(), _currentUpload->type.c_str(), _currentUpload->totalSize); - line = client->readStringUntil(0x0D); - client->readStringUntil(0x0A); - if (line == "--") { - log_v("Done Parsing POST"); - break; - } - continue; - } else { - _uploadWriteByte(0x0D); - _uploadWriteByte(0x0A); - _uploadWriteByte((uint8_t)('-')); - _uploadWriteByte((uint8_t)('-')); - for (uint32_t j = 0; j < boundary.length(); j++) { - _uploadWriteByte(endBuf[j]); - } - argByte = _uploadReadByte(client); - goto readfile; - } - } else { - _uploadWriteByte(0x0D); - goto readfile; - } - break; - } + } + log_v("PostArg Type: %s", argType.c_str()); + if (!argIsFile) { + while (1) { + line = client->readStringUntil('\r'); + client->readStringUntil('\n'); + if (line.startsWith("--" + boundary)) { + break; + } + if (argValue.length() > 0) { + argValue += "\n"; + } + argValue += line; + } + log_v("PostArg Value: %s", argValue.c_str()); + + RequestArgument &arg = _postArgs[_postArgsLen++]; + arg.key = argName; + arg.value = argValue; + + if (line == ("--" + boundary + "--")) { + log_v("Done Parsing POST"); + break; + } else if (_postArgsLen >= WEBSERVER_MAX_POST_ARGS) { + log_e("Too many PostArgs (max: %d) in request.", WEBSERVER_MAX_POST_ARGS); + return false; + } + } else { + _currentUpload.reset(new HTTPUpload()); + _currentUpload->status = UPLOAD_FILE_START; + _currentUpload->name = argName; + _currentUpload->filename = argFilename; + _currentUpload->type = argType; + _currentUpload->totalSize = 0; + _currentUpload->currentSize = 0; + log_v("Start File: %s Type: %s", _currentUpload->filename.c_str(), _currentUpload->type.c_str()); + if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + _currentUpload->status = UPLOAD_FILE_WRITE; + + int fastBoundaryLen = 4 /* \r\n-- */ + boundary.length() + 1 /* \0 */; + char fastBoundary[fastBoundaryLen]; + snprintf(fastBoundary, fastBoundaryLen, "\r\n--%s", boundary.c_str()); + int boundaryPtr = 0; + while (true) { + int ret = _uploadReadByte(client); + if (ret < 0) { + // Unexpected, we should have had data available per above + return _parseFormUploadAborted(); + } + char in = (char)ret; + if (in == fastBoundary[boundaryPtr]) { + // The input matched the current expected character, advance and possibly exit this file + boundaryPtr++; + if (boundaryPtr == fastBoundaryLen - 1) { + // We read the whole boundary line, we're done here! + break; } + } else { + // The char doesn't match what we want, so dump whatever matches we had, the read in char, and reset ptr to start + for (int i = 0; i < boundaryPtr; i++) { + _uploadWriteByte(fastBoundary[i]); + } + if (in == fastBoundary[0]) { + // This could be the start of the real end, mark it so and don't emit/skip it + boundaryPtr = 1; + } else { + // Not the 1st char of our pattern, so emit and ignore + _uploadWriteByte(in); + boundaryPtr = 0; + } + } + } + // Found the boundary string, finish processing this file upload + if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + _currentUpload->totalSize += _currentUpload->currentSize; + _currentUpload->status = UPLOAD_FILE_END; + if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + log_v("End File: %s Type: %s Size: %d", _currentUpload->filename.c_str(), _currentUpload->type.c_str(), (int)_currentUpload->totalSize); + if (!client->connected()) { + return _parseFormUploadAborted(); } + line = client->readStringUntil('\r'); + client->readStringUntil('\n'); + if (line == "--") { // extra two dashes mean we reached the end of all form fields + log_v("Done Parsing POST"); + break; + } + continue; + } } + } + } - int iarg; - int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount) ? (WEBSERVER_MAX_POST_ARGS - _postArgsLen) : _currentArgCount; - for (iarg = 0; iarg < totalArgs; iarg++) { - RequestArgument& arg = _postArgs[_postArgsLen++]; - arg.key = _currentArgs[iarg].key; - arg.value = _currentArgs[iarg].value; - } - if (_currentArgs) { - delete[] _currentArgs; - } - _currentArgs = new RequestArgument[_postArgsLen]; - for (iarg = 0; iarg < _postArgsLen; iarg++) { - RequestArgument& arg = _currentArgs[iarg]; - arg.key = _postArgs[iarg].key; - arg.value = _postArgs[iarg].value; - } - _currentArgCount = iarg; - if (_postArgs) { - delete[] _postArgs; - _postArgs = nullptr; - _postArgsLen = 0; - } - return true; + int iarg; + int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount) ? (WEBSERVER_MAX_POST_ARGS - _postArgsLen) : _currentArgCount; + for (iarg = 0; iarg < totalArgs; iarg++) { + RequestArgument &arg = _postArgs[_postArgsLen++]; + arg.key = _currentArgs[iarg].key; + arg.value = _currentArgs[iarg].value; } - log_e("Error: line: %s", line.c_str()); - return false; + if (_currentArgs) { + delete[] _currentArgs; + } + _currentArgs = new RequestArgument[_postArgsLen]; + for (iarg = 0; iarg < _postArgsLen; iarg++) { + RequestArgument &arg = _currentArgs[iarg]; + arg.key = _postArgs[iarg].key; + arg.value = _postArgs[iarg].value; + } + _currentArgCount = iarg; + if (_postArgs) { + delete[] _postArgs; + _postArgs = nullptr; + _postArgsLen = 0; + } + return true; + } + log_e("Error: line: %s", line.c_str()); + return false; } String HTTPServer::urlDecode(const String & text) { From 77d0ed637e505f13877b74d678b597e7dd1df90c Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Thu, 30 May 2024 11:57:40 -0700 Subject: [PATCH 2/2] Style --- libraries/WebServer/src/Parsing.cpp | 362 ++++++++++++++-------------- 1 file changed, 181 insertions(+), 181 deletions(-) diff --git a/libraries/WebServer/src/Parsing.cpp b/libraries/WebServer/src/Parsing.cpp index 1252be77a..a0a88ef25 100644 --- a/libraries/WebServer/src/Parsing.cpp +++ b/libraries/WebServer/src/Parsing.cpp @@ -357,200 +357,200 @@ void HTTPServer::_uploadWriteByte(uint8_t b) { } int HTTPServer::_uploadReadByte(WiFiClient * client) { - int res = client->read(); + int res = client->read(); - if (res < 0) { - while (!client->available() && client->connected()) { - delay(2); - } + if (res < 0) { + while (!client->available() && client->connected()) { + delay(2); + } - res = client->read(); - } + res = client->read(); + } - return res; + return res; } bool HTTPServer::_parseForm(WiFiClient * client, String boundary, uint32_t len) { - (void)len; - log_v("Parse Form: Boundary: %s Length: %d", boundary.c_str(), len); - String line; - int retry = 0; - do { - line = client->readStringUntil('\r'); - ++retry; - } while (line.length() == 0 && retry < 3); - - client->readStringUntil('\n'); - //start reading the form - if (line == ("--" + boundary)) { - if (_postArgs) { - delete[] _postArgs; - } - _postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS]; - _postArgsLen = 0; - while (1) { - String argName; - String argValue; - String argType; - String argFilename; - bool argIsFile = false; - - line = client->readStringUntil('\r'); - client->readStringUntil('\n'); - if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) { - int nameStart = line.indexOf('='); - if (nameStart != -1) { - argName = line.substring(nameStart + 2); - nameStart = argName.indexOf('='); - if (nameStart == -1) { - argName = argName.substring(0, argName.length() - 1); - } else { - argFilename = argName.substring(nameStart + 2, argName.length() - 1); - argName = argName.substring(0, argName.indexOf('"')); - argIsFile = true; - log_v("PostArg FileName: %s", argFilename.c_str()); - //use GET to set the filename if uploading using blob - if (argFilename == F("blob") && hasArg(FPSTR(filename))) { - argFilename = arg(FPSTR(filename)); - } - } - log_v("PostArg Name: %s", argName.c_str()); - using namespace mime; - argType = FPSTR(mimeTable[txt].mimeType); - line = client->readStringUntil('\r'); - client->readStringUntil('\n'); - while (line.length() > 0) { - if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) { - argType = line.substring(line.indexOf(':') + 2); - } - //skip over any other headers - line = client->readStringUntil('\r'); - client->readStringUntil('\n'); - } - log_v("PostArg Type: %s", argType.c_str()); - if (!argIsFile) { - while (1) { - line = client->readStringUntil('\r'); - client->readStringUntil('\n'); - if (line.startsWith("--" + boundary)) { - break; - } - if (argValue.length() > 0) { - argValue += "\n"; - } - argValue += line; - } - log_v("PostArg Value: %s", argValue.c_str()); + (void)len; + log_v("Parse Form: Boundary: %s Length: %d", boundary.c_str(), len); + String line; + int retry = 0; + do { + line = client->readStringUntil('\r'); + ++retry; + } while (line.length() == 0 && retry < 3); + + client->readStringUntil('\n'); + //start reading the form + if (line == ("--" + boundary)) { + if (_postArgs) { + delete[] _postArgs; + } + _postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS]; + _postArgsLen = 0; + while (1) { + String argName; + String argValue; + String argType; + String argFilename; + bool argIsFile = false; - RequestArgument &arg = _postArgs[_postArgsLen++]; - arg.key = argName; - arg.value = argValue; - - if (line == ("--" + boundary + "--")) { - log_v("Done Parsing POST"); - break; - } else if (_postArgsLen >= WEBSERVER_MAX_POST_ARGS) { - log_e("Too many PostArgs (max: %d) in request.", WEBSERVER_MAX_POST_ARGS); - return false; - } - } else { - _currentUpload.reset(new HTTPUpload()); - _currentUpload->status = UPLOAD_FILE_START; - _currentUpload->name = argName; - _currentUpload->filename = argFilename; - _currentUpload->type = argType; - _currentUpload->totalSize = 0; - _currentUpload->currentSize = 0; - log_v("Start File: %s Type: %s", _currentUpload->filename.c_str(), _currentUpload->type.c_str()); - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { - _currentHandler->upload(*this, _currentUri, *_currentUpload); - } - _currentUpload->status = UPLOAD_FILE_WRITE; - - int fastBoundaryLen = 4 /* \r\n-- */ + boundary.length() + 1 /* \0 */; - char fastBoundary[fastBoundaryLen]; - snprintf(fastBoundary, fastBoundaryLen, "\r\n--%s", boundary.c_str()); - int boundaryPtr = 0; - while (true) { - int ret = _uploadReadByte(client); - if (ret < 0) { - // Unexpected, we should have had data available per above - return _parseFormUploadAborted(); - } - char in = (char)ret; - if (in == fastBoundary[boundaryPtr]) { - // The input matched the current expected character, advance and possibly exit this file - boundaryPtr++; - if (boundaryPtr == fastBoundaryLen - 1) { - // We read the whole boundary line, we're done here! - break; - } - } else { - // The char doesn't match what we want, so dump whatever matches we had, the read in char, and reset ptr to start - for (int i = 0; i < boundaryPtr; i++) { - _uploadWriteByte(fastBoundary[i]); - } - if (in == fastBoundary[0]) { - // This could be the start of the real end, mark it so and don't emit/skip it - boundaryPtr = 1; - } else { - // Not the 1st char of our pattern, so emit and ignore - _uploadWriteByte(in); - boundaryPtr = 0; - } - } - } - // Found the boundary string, finish processing this file upload - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { - _currentHandler->upload(*this, _currentUri, *_currentUpload); - } - _currentUpload->totalSize += _currentUpload->currentSize; - _currentUpload->status = UPLOAD_FILE_END; - if (_currentHandler && _currentHandler->canUpload(_currentUri)) { - _currentHandler->upload(*this, _currentUri, *_currentUpload); - } - log_v("End File: %s Type: %s Size: %d", _currentUpload->filename.c_str(), _currentUpload->type.c_str(), (int)_currentUpload->totalSize); - if (!client->connected()) { - return _parseFormUploadAborted(); - } line = client->readStringUntil('\r'); client->readStringUntil('\n'); - if (line == "--") { // extra two dashes mean we reached the end of all form fields - log_v("Done Parsing POST"); - break; + if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) { + int nameStart = line.indexOf('='); + if (nameStart != -1) { + argName = line.substring(nameStart + 2); + nameStart = argName.indexOf('='); + if (nameStart == -1) { + argName = argName.substring(0, argName.length() - 1); + } else { + argFilename = argName.substring(nameStart + 2, argName.length() - 1); + argName = argName.substring(0, argName.indexOf('"')); + argIsFile = true; + log_v("PostArg FileName: %s", argFilename.c_str()); + //use GET to set the filename if uploading using blob + if (argFilename == F("blob") && hasArg(FPSTR(filename))) { + argFilename = arg(FPSTR(filename)); + } + } + log_v("PostArg Name: %s", argName.c_str()); + using namespace mime; + argType = FPSTR(mimeTable[txt].mimeType); + line = client->readStringUntil('\r'); + client->readStringUntil('\n'); + while (line.length() > 0) { + if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) { + argType = line.substring(line.indexOf(':') + 2); + } + //skip over any other headers + line = client->readStringUntil('\r'); + client->readStringUntil('\n'); + } + log_v("PostArg Type: %s", argType.c_str()); + if (!argIsFile) { + while (1) { + line = client->readStringUntil('\r'); + client->readStringUntil('\n'); + if (line.startsWith("--" + boundary)) { + break; + } + if (argValue.length() > 0) { + argValue += "\n"; + } + argValue += line; + } + log_v("PostArg Value: %s", argValue.c_str()); + + RequestArgument &arg = _postArgs[_postArgsLen++]; + arg.key = argName; + arg.value = argValue; + + if (line == ("--" + boundary + "--")) { + log_v("Done Parsing POST"); + break; + } else if (_postArgsLen >= WEBSERVER_MAX_POST_ARGS) { + log_e("Too many PostArgs (max: %d) in request.", WEBSERVER_MAX_POST_ARGS); + return false; + } + } else { + _currentUpload.reset(new HTTPUpload()); + _currentUpload->status = UPLOAD_FILE_START; + _currentUpload->name = argName; + _currentUpload->filename = argFilename; + _currentUpload->type = argType; + _currentUpload->totalSize = 0; + _currentUpload->currentSize = 0; + log_v("Start File: %s Type: %s", _currentUpload->filename.c_str(), _currentUpload->type.c_str()); + if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + _currentUpload->status = UPLOAD_FILE_WRITE; + + int fastBoundaryLen = 4 /* \r\n-- */ + boundary.length() + 1 /* \0 */; + char fastBoundary[fastBoundaryLen]; + snprintf(fastBoundary, fastBoundaryLen, "\r\n--%s", boundary.c_str()); + int boundaryPtr = 0; + while (true) { + int ret = _uploadReadByte(client); + if (ret < 0) { + // Unexpected, we should have had data available per above + return _parseFormUploadAborted(); + } + char in = (char)ret; + if (in == fastBoundary[boundaryPtr]) { + // The input matched the current expected character, advance and possibly exit this file + boundaryPtr++; + if (boundaryPtr == fastBoundaryLen - 1) { + // We read the whole boundary line, we're done here! + break; + } + } else { + // The char doesn't match what we want, so dump whatever matches we had, the read in char, and reset ptr to start + for (int i = 0; i < boundaryPtr; i++) { + _uploadWriteByte(fastBoundary[i]); + } + if (in == fastBoundary[0]) { + // This could be the start of the real end, mark it so and don't emit/skip it + boundaryPtr = 1; + } else { + // Not the 1st char of our pattern, so emit and ignore + _uploadWriteByte(in); + boundaryPtr = 0; + } + } + } + // Found the boundary string, finish processing this file upload + if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + _currentUpload->totalSize += _currentUpload->currentSize; + _currentUpload->status = UPLOAD_FILE_END; + if (_currentHandler && _currentHandler->canUpload(_currentUri)) { + _currentHandler->upload(*this, _currentUri, *_currentUpload); + } + log_v("End File: %s Type: %s Size: %d", _currentUpload->filename.c_str(), _currentUpload->type.c_str(), (int)_currentUpload->totalSize); + if (!client->connected()) { + return _parseFormUploadAborted(); + } + line = client->readStringUntil('\r'); + client->readStringUntil('\n'); + if (line == "--") { // extra two dashes mean we reached the end of all form fields + log_v("Done Parsing POST"); + break; + } + continue; + } + } } - continue; - } } - } - } - int iarg; - int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount) ? (WEBSERVER_MAX_POST_ARGS - _postArgsLen) : _currentArgCount; - for (iarg = 0; iarg < totalArgs; iarg++) { - RequestArgument &arg = _postArgs[_postArgsLen++]; - arg.key = _currentArgs[iarg].key; - arg.value = _currentArgs[iarg].value; - } - if (_currentArgs) { - delete[] _currentArgs; - } - _currentArgs = new RequestArgument[_postArgsLen]; - for (iarg = 0; iarg < _postArgsLen; iarg++) { - RequestArgument &arg = _currentArgs[iarg]; - arg.key = _postArgs[iarg].key; - arg.value = _postArgs[iarg].value; - } - _currentArgCount = iarg; - if (_postArgs) { - delete[] _postArgs; - _postArgs = nullptr; - _postArgsLen = 0; + int iarg; + int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount) ? (WEBSERVER_MAX_POST_ARGS - _postArgsLen) : _currentArgCount; + for (iarg = 0; iarg < totalArgs; iarg++) { + RequestArgument &arg = _postArgs[_postArgsLen++]; + arg.key = _currentArgs[iarg].key; + arg.value = _currentArgs[iarg].value; + } + if (_currentArgs) { + delete[] _currentArgs; + } + _currentArgs = new RequestArgument[_postArgsLen]; + for (iarg = 0; iarg < _postArgsLen; iarg++) { + RequestArgument &arg = _currentArgs[iarg]; + arg.key = _postArgs[iarg].key; + arg.value = _postArgs[iarg].value; + } + _currentArgCount = iarg; + if (_postArgs) { + delete[] _postArgs; + _postArgs = nullptr; + _postArgsLen = 0; + } + return true; } - return true; - } - log_e("Error: line: %s", line.c_str()); - return false; + log_e("Error: line: %s", line.c_str()); + return false; } String HTTPServer::urlDecode(const String & text) {