ClickHouse/base/poco/Net/src/MultipartReader.cpp
Robert Schulze b79ead9c84
Move poco to base/poco/ (#46075)
* Replicate poco into base/poco/

* De-register poco submodule

* Build poco from ClickHouse

* Exclude poco from stylecheck

* Exclude poco from whitespace check

* Exclude poco from typo check

* Remove x bit from sources/headers (the style check complained)

* Exclude poco from duplicate include check

* Fix fasttest

* Remove contrib/poco-cmake/*

* Simplify poco build descriptions

* Remove poco stuff not used by ClickHouse

* Glob poco sources

* Exclude poco from clang-tidy
2023-02-08 12:04:11 +01:00

310 lines
5.4 KiB
C++

//
// MultipartReader.cpp
//
// Library: Net
// Package: Messages
// Module: MultipartReader
//
// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Net/MultipartReader.h"
#include "Poco/Net/MessageHeader.h"
#include "Poco/Net/NetException.h"
#include "Poco/Ascii.h"
using Poco::BufferedStreamBuf;
namespace Poco {
namespace Net {
//
// MultipartStreamBuf
//
MultipartStreamBuf::MultipartStreamBuf(std::istream& istr, const std::string& boundary):
BufferedStreamBuf(STREAM_BUFFER_SIZE, std::ios::in),
_istr(istr),
_boundary(boundary),
_lastPart(false)
{
poco_assert (!boundary.empty() && boundary.length() < STREAM_BUFFER_SIZE - 6);
}
MultipartStreamBuf::~MultipartStreamBuf()
{
}
int MultipartStreamBuf::readFromDevice(char* buffer, std::streamsize length)
{
poco_assert_dbg (length >= _boundary.length() + 6);
static const int eof = std::char_traits<char>::eof();
std::streambuf& buf = *_istr.rdbuf();
int n = 0;
int ch = buf.sbumpc();
if (ch == eof) return -1;
*buffer++ = (char) ch; ++n;
if (ch == '\n' || (ch == '\r' && buf.sgetc() == '\n'))
{
if (ch == '\r')
{
ch = buf.sbumpc(); // '\n'
*buffer++ = (char) ch; ++n;
}
ch = buf.sgetc();
if (ch == '\r' || ch == '\n') return n;
*buffer++ = (char) buf.sbumpc(); ++n;
if (ch == '-' && buf.sgetc() == '-')
{
ch = buf.sbumpc(); // '-'
*buffer++ = (char) ch; ++n;
std::string::const_iterator it = _boundary.begin();
std::string::const_iterator end = _boundary.end();
ch = buf.sbumpc();
*buffer++ = (char) ch; ++n;
while (it != end && ch == *it)
{
++it;
ch = buf.sbumpc();
*buffer++ = (char) ch; ++n;
}
if (it == end)
{
if (ch == '\n' || (ch == '\r' && buf.sgetc() == '\n'))
{
if (ch == '\r')
{
buf.sbumpc(); // '\n'
}
return 0;
}
else if (ch == '-' && buf.sgetc() == '-')
{
buf.sbumpc(); // '-'
_lastPart = true;
return 0;
}
}
}
}
ch = buf.sgetc();
while (ch != eof && ch != '\r' && ch != '\n' && n < length)
{
*buffer++ = (char) buf.sbumpc(); ++n;
ch = buf.sgetc();
}
return n;
}
bool MultipartStreamBuf::lastPart() const
{
return _lastPart;
}
//
// MultipartIOS
//
MultipartIOS::MultipartIOS(std::istream& istr, const std::string& boundary):
_buf(istr, boundary)
{
poco_ios_init(&_buf);
}
MultipartIOS::~MultipartIOS()
{
try
{
_buf.sync();
}
catch (...)
{
}
}
MultipartStreamBuf* MultipartIOS::rdbuf()
{
return &_buf;
}
bool MultipartIOS::lastPart() const
{
return _buf.lastPart();
}
//
// MultipartInputStream
//
MultipartInputStream::MultipartInputStream(std::istream& istr, const std::string& boundary):
MultipartIOS(istr, boundary),
std::istream(&_buf)
{
}
MultipartInputStream::~MultipartInputStream()
{
}
//
// MultipartReader
//
MultipartReader::MultipartReader(std::istream& istr):
_istr(istr),
_pMPI(0)
{
}
MultipartReader::MultipartReader(std::istream& istr, const std::string& boundary):
_istr(istr),
_boundary(boundary),
_pMPI(0)
{
}
MultipartReader::~MultipartReader()
{
delete _pMPI;
}
void MultipartReader::nextPart(MessageHeader& messageHeader)
{
if (!_pMPI)
{
if (_boundary.empty())
guessBoundary();
else
findFirstBoundary();
}
else if (_pMPI->lastPart())
{
throw MultipartException("No more parts available");
}
parseHeader(messageHeader);
delete _pMPI;
_pMPI = new MultipartInputStream(_istr, _boundary);
}
bool MultipartReader::hasNextPart()
{
return (!_pMPI || !_pMPI->lastPart()) && _istr.good();
}
std::istream& MultipartReader::stream() const
{
poco_check_ptr (_pMPI);
return *_pMPI;
}
const std::string& MultipartReader::boundary() const
{
return _boundary;
}
void MultipartReader::findFirstBoundary()
{
std::string expect("--");
expect.append(_boundary);
std::string line;
line.reserve(expect.length());
bool ok = true;
do
{
ok = readLine(line, expect.length());
}
while (ok && line != expect);
if (!ok) throw MultipartException("No boundary line found");
}
void MultipartReader::guessBoundary()
{
static const int eof = std::char_traits<char>::eof();
int ch = _istr.get();
while (Poco::Ascii::isSpace(ch))
ch = _istr.get();
if (ch == '-' && _istr.peek() == '-')
{
_istr.get();
ch = _istr.peek();
while (ch != eof && ch != '\r' && ch != '\n' && _boundary.size() < 128) // Note: should be no longer than 70 chars acc. to RFC 2046
{
_boundary += (char) _istr.get();
ch = _istr.peek();
}
if (ch != '\r' && ch != '\n')
throw MultipartException("Invalid boundary line found");
if (ch == '\r' || ch == '\n')
_istr.get();
if (_istr.peek() == '\n')
_istr.get();
}
else throw MultipartException("No boundary line found");
}
void MultipartReader::parseHeader(MessageHeader& messageHeader)
{
messageHeader.clear();
messageHeader.read(_istr);
int ch = _istr.get();
if (ch == '\r' && _istr.peek() == '\n') _istr.get();
}
bool MultipartReader::readLine(std::string& line, std::string::size_type n)
{
static const int eof = std::char_traits<char>::eof();
static const int maxLength = 1024;
line.clear();
int ch = _istr.peek();
int length = 0;
while (ch != eof && ch != '\r' && ch != '\n' && length < maxLength)
{
ch = (char) _istr.get();
if (line.length() < n) line += ch;
ch = _istr.peek();
length++;
}
if (ch != eof) _istr.get();
if (ch == '\r' && _istr.peek() == '\n') _istr.get();
return ch != eof && length < maxLength;
}
} } // namespace Poco::Net