From eb8dce47fe2a89870d8b38f1fb6d9e9a81d815af Mon Sep 17 00:00:00 2001 From: Guenter Obiltschnig Date: Wed, 12 Nov 2014 10:33:57 +0100 Subject: [PATCH] fix #606: HTMLForm constructor read application/x-www-form-urlencoded UTF-8 request body first parameter with BOM in name --- Foundation/include/Poco/UTF8String.h | 7 +++++ Foundation/src/UTF8String.cpp | 12 +++++++++ Net/src/HTMLForm.cpp | 39 +++++++++++++++++++++------- Net/testsuite/src/HTMLFormTest.cpp | 15 +++++++++++ Net/testsuite/src/HTMLFormTest.h | 1 + 5 files changed, 64 insertions(+), 10 deletions(-) diff --git a/Foundation/include/Poco/UTF8String.h b/Foundation/include/Poco/UTF8String.h index 36c280dea..d719b609e 100644 --- a/Foundation/include/Poco/UTF8String.h +++ b/Foundation/include/Poco/UTF8String.h @@ -35,6 +35,9 @@ struct Foundation_API UTF8 /// /// toUpper(), toUpperInPlace(), toLower() and toLowerInPlace() provide /// Unicode-based character case transformation for UTF-8 encoded strings. + /// + /// removeBOM() removes the UTF-8 Byte Order Mark sequence (0xEF, 0xBB, 0xBF) + /// from the beginning of the given string, if it's there. { static int icompare(const std::string& str, std::string::size_type pos, std::string::size_type n, std::string::const_iterator it2, std::string::const_iterator end2); static int icompare(const std::string& str1, const std::string& str2); @@ -51,6 +54,10 @@ struct Foundation_API UTF8 static std::string& toUpperInPlace(std::string& str); static std::string toLower(const std::string& str); static std::string& toLowerInPlace(std::string& str); + + static void removeBOM(std::string& str); + /// Remove the UTF-8 Byte Order Mark sequence (0xEF, 0xBB, 0xBF) + /// from the beginning of the string, if it's there. }; diff --git a/Foundation/src/UTF8String.cpp b/Foundation/src/UTF8String.cpp index 8f10d8a0f..b7c6eb877 100644 --- a/Foundation/src/UTF8String.cpp +++ b/Foundation/src/UTF8String.cpp @@ -160,4 +160,16 @@ std::string& UTF8::toLowerInPlace(std::string& str) } +void UTF8::removeBOM(std::string& str) +{ + if (str.size() >= 3 + && static_cast(str[0]) == 0xEF + && static_cast(str[1]) == 0xBB + && static_cast(str[2]) == 0xBF) + { + str.erase(0, 3); + } +} + + } // namespace Poco diff --git a/Net/src/HTMLForm.cpp b/Net/src/HTMLForm.cpp index 92943092d..24ee4a60b 100644 --- a/Net/src/HTMLForm.cpp +++ b/Net/src/HTMLForm.cpp @@ -28,6 +28,7 @@ #include "Poco/URI.h" #include "Poco/String.h" #include "Poco/CountingStream.h" +#include "Poco/UTF8String.h" #include @@ -42,20 +43,31 @@ namespace Poco { namespace Net { -const std::string HTMLForm::ENCODING_URL = "application/x-www-form-urlencoded"; -const std::string HTMLForm::ENCODING_MULTIPART = "multipart/form-data"; -const int HTMLForm::UNKNOWN_CONTENT_LENGTH = -1; +const std::string HTMLForm::ENCODING_URL = "application/x-www-form-urlencoded"; +const std::string HTMLForm::ENCODING_MULTIPART = "multipart/form-data"; +const int HTMLForm::UNKNOWN_CONTENT_LENGTH = -1; -class HTMLFormCountingOutputStream : public CountingOutputStream +class HTMLFormCountingOutputStream: public CountingOutputStream { public: - HTMLFormCountingOutputStream() : _isvalid(true) {} + HTMLFormCountingOutputStream(): + _valid(true) + { + } + + bool isValid() const + { + return _valid; + } + + void setValid(bool v) + { + _valid = v; + } - bool getIsValid() const { return _isvalid; } - void setIsValid(bool v) { _isvalid = v; } private: - bool _isvalid; + bool _valid; }; @@ -238,7 +250,7 @@ std::streamsize HTMLForm::calculateContentLength() HTMLFormCountingOutputStream c; write(c); - if (c.getIsValid()) + if (c.isValid()) return c.chars(); else return UNKNOWN_CONTENT_LENGTH; @@ -274,6 +286,7 @@ void HTMLForm::readUrl(std::istream& istr) int fields = 0; int ch = istr.get(); + bool isFirst = true; while (ch != eof) { if (_fieldLimit > 0 && fields == _fieldLimit) @@ -296,6 +309,11 @@ void HTMLForm::readUrl(std::istream& istr) ch = istr.get(); } } + // remove UTF-8 byte order mark from first name, if present + if (isFirst) + { + UTF8::removeBOM(name); + } std::string decodedName; std::string decodedValue; URI::decode(name, decodedName); @@ -303,6 +321,7 @@ void HTMLForm::readUrl(std::istream& istr) add(decodedName, decodedValue); ++fields; if (ch == '&') ch = istr.get(); + isFirst = false; } } @@ -402,7 +421,7 @@ void HTMLForm::writeMultipart(std::ostream& ostr) if (partlen != PartSource::UNKNOWN_CONTENT_LENGTH) costr->addChars(static_cast(partlen)); else - costr->setIsValid(false); + costr->setValid(false); } else StreamCopier::copyStream(ita->pSource->stream(), ostr); diff --git a/Net/testsuite/src/HTMLFormTest.cpp b/Net/testsuite/src/HTMLFormTest.cpp index 3f0693571..318c66b83 100644 --- a/Net/testsuite/src/HTMLFormTest.cpp +++ b/Net/testsuite/src/HTMLFormTest.cpp @@ -191,6 +191,20 @@ void HTMLFormTest::testReadUrlPUT() } +void HTMLFormTest::testReadUrlBOM() +{ + HTTPRequest req("PUT", "/form.cgi?field0=value0"); + std::istringstream istr("\357\273\277field1=value1&field2=value%202&field3=value%3D3&field4=value%264"); + HTMLForm form(req, istr); + assert (form.size() == 5); + assert (form["field0"] == "value0"); + assert (form["field1"] == "value1"); + assert (form["field2"] == "value 2"); + assert (form["field3"] == "value=3"); + assert (form["field4"] == "value&4"); +} + + void HTMLFormTest::testReadMultipart() { std::istringstream istr( @@ -359,6 +373,7 @@ CppUnit::Test* HTMLFormTest::suite() CppUnit_addTest(pSuite, HTMLFormTest, testReadUrlGET); CppUnit_addTest(pSuite, HTMLFormTest, testReadUrlPOST); CppUnit_addTest(pSuite, HTMLFormTest, testReadUrlPUT); + CppUnit_addTest(pSuite, HTMLFormTest, testReadUrlBOM); CppUnit_addTest(pSuite, HTMLFormTest, testReadMultipart); CppUnit_addTest(pSuite, HTMLFormTest, testSubmit1); CppUnit_addTest(pSuite, HTMLFormTest, testSubmit2); diff --git a/Net/testsuite/src/HTMLFormTest.h b/Net/testsuite/src/HTMLFormTest.h index 2da5923fa..7f9242d49 100644 --- a/Net/testsuite/src/HTMLFormTest.h +++ b/Net/testsuite/src/HTMLFormTest.h @@ -31,6 +31,7 @@ public: void testReadUrlGET(); void testReadUrlPOST(); void testReadUrlPUT(); + void testReadUrlBOM(); void testReadMultipart(); void testSubmit1(); void testSubmit2();