// // HTMLForm.cpp // // $Id: //poco/1.2/Net/src/HTMLForm.cpp#3 $ // // Library: Net // Package: HTML // Module: HTMLForm // // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. // and Contributors. // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. // #include "Poco/Net/HTMLForm.h" #include "Poco/Net/HTTPRequest.h" #include "Poco/Net/PartSource.h" #include "Poco/Net/PartHandler.h" #include "Poco/Net/MultipartWriter.h" #include "Poco/Net/MultipartReader.h" #include "Poco/Net/NullPartHandler.h" #include "Poco/NullStream.h" #include "Poco/StreamCopier.h" #include "Poco/Exception.h" #include "Poco/URI.h" #include "Poco/String.h" #include #include using Poco::NullInputStream; using Poco::StreamCopier; using Poco::SyntaxException; using Poco::URI; using Poco::icompare; namespace Poco { namespace Net { const std::string HTMLForm::ENCODING_URL = "application/x-www-form-urlencoded"; const std::string HTMLForm::ENCODING_MULTIPART = "multipart/form-data"; HTMLForm::HTMLForm(): _encoding(ENCODING_URL) { } HTMLForm::HTMLForm(const std::string& encoding): _encoding(encoding) { } HTMLForm::HTMLForm(const HTTPRequest& request, std::istream& requestBody, PartHandler& handler) { load(request, requestBody, handler); } HTMLForm::HTMLForm(const HTTPRequest& request, std::istream& requestBody) { load(request, requestBody); } HTMLForm::HTMLForm(const HTTPRequest& request) { load(request); } HTMLForm::~HTMLForm() { for (PartVec::iterator it = _parts.begin(); it != _parts.end(); ++it) { delete it->pSource; } } void HTMLForm::setEncoding(const std::string& encoding) { _encoding = encoding; } void HTMLForm::addPart(const std::string& name, PartSource* pSource) { poco_check_ptr (pSource); Part part; part.name = name; part.pSource = pSource; _parts.push_back(part); } void HTMLForm::load(const HTTPRequest& request, std::istream& requestBody, PartHandler& handler) { if (request.getMethod() == HTTPRequest::HTTP_POST) { std::string mediaType; NameValueCollection params; MessageHeader::splitParameters(request.getContentType(), mediaType, params); _encoding = mediaType; if (_encoding == ENCODING_MULTIPART) { _boundary = params["boundary"]; readMultipart(requestBody, handler); } else { readUrl(requestBody); } } else { URI uri(request.getURI()); std::istringstream istr(uri.getRawQuery()); readUrl(istr); } } void HTMLForm::load(const HTTPRequest& request, std::istream& requestBody) { NullPartHandler nah; load(request, requestBody, nah); } void HTMLForm::load(const HTTPRequest& request) { NullPartHandler nah; NullInputStream nis; load(request, nis, nah); } void HTMLForm::read(std::istream& istr, PartHandler& handler) { if (_encoding == ENCODING_URL) readUrl(istr); else readMultipart(istr, handler); } void HTMLForm::prepareSubmit(HTTPRequest& request) { if (request.getMethod() == HTTPRequest::HTTP_POST) { if (_encoding == ENCODING_URL) { request.setContentType(_encoding); } else { _boundary = MultipartWriter::createBoundary(); std::string ct(_encoding); ct.append("; boundary=\""); ct.append(_boundary); ct.append("\""); request.setContentType(ct); } if (request.getVersion() == HTTPMessage::HTTP_1_0) { request.setKeepAlive(false); request.setChunkedTransferEncoding(false); } else { request.setChunkedTransferEncoding(true); } } else { std::string uri = request.getURI(); std::ostringstream ostr; write(ostr); uri.append("?"); uri.append(ostr.str()); request.setURI(uri); } } void HTMLForm::write(std::ostream& ostr, const std::string& boundary) { if (_encoding == ENCODING_URL) { writeUrl(ostr); } else { _boundary = boundary; writeMultipart(ostr); } } void HTMLForm::write(std::ostream& ostr) { if (_encoding == ENCODING_URL) writeUrl(ostr); else writeMultipart(ostr); } void HTMLForm::readUrl(std::istream& istr) { static const int eof = std::char_traits::eof(); int ch = istr.get(); while (ch != eof) { std::string name; std::string value; while (ch != eof && ch != '=' && ch != '&') { if (ch == '+') ch = ' '; name += (char) ch; ch = istr.get(); } if (ch == '=') { ch = istr.get(); while (ch != eof && ch != '&') { if (ch == '+') ch = ' '; value += (char) ch; ch = istr.get(); } } std::string decodedName; std::string decodedValue; URI::decode(name, decodedName); URI::decode(value, decodedValue); add(decodedName, decodedValue); if (ch == '&') ch = istr.get(); } } void HTMLForm::readMultipart(std::istream& istr, PartHandler& handler) { static const int eof = std::char_traits::eof(); MultipartReader reader(istr, _boundary); while (reader.hasNextPart()) { MessageHeader header; reader.nextPart(header); std::string disp; NameValueCollection params; if (header.has("Content-Disposition")) { std::string cd = header.get("Content-Disposition"); MessageHeader::splitParameters(cd, disp, params); } if (params.has("filename")) { handler.handlePart(header, reader.stream()); // Ensure that the complete part has been read. while (reader.stream().good()) reader.stream().get(); } else { std::string name = params["name"]; std::string value; std::istream& istr = reader.stream(); int ch = istr.get(); while (ch != eof) { value += (char) ch; ch = istr.get(); } add(name, value); } } } void HTMLForm::writeUrl(std::ostream& ostr) { for (NameValueCollection::ConstIterator it = begin(); it != end(); ++it) { if (it != begin()) ostr << "&"; std::string name; URI::encode(it->first, "=&", name); std::string value; URI::encode(it->second, "=&", value); ostr << name << "=" << value; } } void HTMLForm::writeMultipart(std::ostream& ostr) { MultipartWriter writer(ostr, _boundary); for (NameValueCollection::ConstIterator it = begin(); it != end(); ++it) { MessageHeader header; std::string disp("form-data; name=\""); disp.append(it->first); disp.append("\""); header.set("Content-Disposition", disp); writer.nextPart(header); ostr << it->second; } for (PartVec::iterator ita = _parts.begin(); ita != _parts.end(); ++ita) { MessageHeader header; std::string disp("file; name=\""); disp.append(ita->name); disp.append("\""); std::string filename = ita->pSource->filename(); if (!filename.empty()) { disp.append("; filename=\""); disp.append(filename); disp.append("\""); } header.set("Content-Disposition", disp); header.set("Content-Type", ita->pSource->mediaType()); writer.nextPart(header); StreamCopier::copyStream(ita->pSource->stream(), ostr); } writer.close(); _boundary = writer.boundary(); } } } // namespace Poco::Net