1//
2// MultipartReader.cpp
3//
4// Library: Net
5// Package: Messages
6// Module: MultipartReader
7//
8// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Net/MultipartReader.h"
16#include "Poco/Net/MessageHeader.h"
17#include "Poco/Net/NetException.h"
18#include "Poco/Ascii.h"
19
20
21using Poco::BufferedStreamBuf;
22
23
24namespace Poco {
25namespace Net {
26
27
28//
29// MultipartStreamBuf
30//
31
32
33MultipartStreamBuf::MultipartStreamBuf(std::istream& istr, const std::string& boundary):
34 BufferedStreamBuf(STREAM_BUFFER_SIZE, std::ios::in),
35 _istr(istr),
36 _boundary(boundary),
37 _lastPart(false)
38{
39 poco_assert (!boundary.empty() && boundary.length() < STREAM_BUFFER_SIZE - 6);
40}
41
42
43MultipartStreamBuf::~MultipartStreamBuf()
44{
45}
46
47
48int MultipartStreamBuf::readFromDevice(char* buffer, std::streamsize length)
49{
50 poco_assert_dbg (length >= _boundary.length() + 6);
51
52 static const int eof = std::char_traits<char>::eof();
53 std::streambuf& buf = *_istr.rdbuf();
54
55 int n = 0;
56 int ch = buf.sbumpc();
57 if (ch == eof) return -1;
58 *buffer++ = (char) ch; ++n;
59 if (ch == '\n' || (ch == '\r' && buf.sgetc() == '\n'))
60 {
61 if (ch == '\r')
62 {
63 ch = buf.sbumpc(); // '\n'
64 *buffer++ = (char) ch; ++n;
65 }
66 ch = buf.sgetc();
67 if (ch == '\r' || ch == '\n') return n;
68 *buffer++ = (char) buf.sbumpc(); ++n;
69 if (ch == '-' && buf.sgetc() == '-')
70 {
71 ch = buf.sbumpc(); // '-'
72 *buffer++ = (char) ch; ++n;
73 std::string::const_iterator it = _boundary.begin();
74 std::string::const_iterator end = _boundary.end();
75 ch = buf.sbumpc();
76 *buffer++ = (char) ch; ++n;
77 while (it != end && ch == *it)
78 {
79 ++it;
80 ch = buf.sbumpc();
81 *buffer++ = (char) ch; ++n;
82 }
83 if (it == end)
84 {
85 if (ch == '\n' || (ch == '\r' && buf.sgetc() == '\n'))
86 {
87 if (ch == '\r')
88 {
89 buf.sbumpc(); // '\n'
90 }
91 return 0;
92 }
93 else if (ch == '-' && buf.sgetc() == '-')
94 {
95 buf.sbumpc(); // '-'
96 _lastPart = true;
97 return 0;
98 }
99 }
100 }
101 }
102 ch = buf.sgetc();
103 while (ch != eof && ch != '\r' && ch != '\n' && n < length)
104 {
105 *buffer++ = (char) buf.sbumpc(); ++n;
106 ch = buf.sgetc();
107 }
108 return n;
109}
110
111
112bool MultipartStreamBuf::lastPart() const
113{
114 return _lastPart;
115}
116
117
118//
119// MultipartIOS
120//
121
122
123MultipartIOS::MultipartIOS(std::istream& istr, const std::string& boundary):
124 _buf(istr, boundary)
125{
126 poco_ios_init(&_buf);
127}
128
129
130MultipartIOS::~MultipartIOS()
131{
132 try
133 {
134 _buf.sync();
135 }
136 catch (...)
137 {
138 }
139}
140
141
142MultipartStreamBuf* MultipartIOS::rdbuf()
143{
144 return &_buf;
145}
146
147
148bool MultipartIOS::lastPart() const
149{
150 return _buf.lastPart();
151}
152
153
154//
155// MultipartInputStream
156//
157
158
159MultipartInputStream::MultipartInputStream(std::istream& istr, const std::string& boundary):
160 MultipartIOS(istr, boundary),
161 std::istream(&_buf)
162{
163}
164
165
166MultipartInputStream::~MultipartInputStream()
167{
168}
169
170
171//
172// MultipartReader
173//
174
175
176MultipartReader::MultipartReader(std::istream& istr):
177 _istr(istr),
178 _pMPI(0)
179{
180}
181
182
183MultipartReader::MultipartReader(std::istream& istr, const std::string& boundary):
184 _istr(istr),
185 _boundary(boundary),
186 _pMPI(0)
187{
188}
189
190
191MultipartReader::~MultipartReader()
192{
193 delete _pMPI;
194}
195
196
197void MultipartReader::nextPart(MessageHeader& messageHeader)
198{
199 if (!_pMPI)
200 {
201 if (_boundary.empty())
202 guessBoundary();
203 else
204 findFirstBoundary();
205 }
206 else if (_pMPI->lastPart())
207 {
208 throw MultipartException("No more parts available");
209 }
210 parseHeader(messageHeader);
211 delete _pMPI;
212 _pMPI = new MultipartInputStream(_istr, _boundary);
213}
214
215
216bool MultipartReader::hasNextPart()
217{
218 return (!_pMPI || !_pMPI->lastPart()) && _istr.good();
219}
220
221
222std::istream& MultipartReader::stream() const
223{
224 poco_check_ptr (_pMPI);
225
226 return *_pMPI;
227}
228
229
230const std::string& MultipartReader::boundary() const
231{
232 return _boundary;
233}
234
235
236void MultipartReader::findFirstBoundary()
237{
238 std::string expect("--");
239 expect.append(_boundary);
240 std::string line;
241 line.reserve(expect.length());
242 bool ok = true;
243 do
244 {
245 ok = readLine(line, expect.length());
246 }
247 while (ok && line != expect);
248
249 if (!ok) throw MultipartException("No boundary line found");
250}
251
252
253void MultipartReader::guessBoundary()
254{
255 static const int eof = std::char_traits<char>::eof();
256 int ch = _istr.get();
257 while (Poco::Ascii::isSpace(ch))
258 ch = _istr.get();
259 if (ch == '-' && _istr.peek() == '-')
260 {
261 _istr.get();
262 ch = _istr.peek();
263 while (ch != eof && ch != '\r' && ch != '\n' && _boundary.size() < 128) // Note: should be no longer than 70 chars acc. to RFC 2046
264 {
265 _boundary += (char) _istr.get();
266 ch = _istr.peek();
267 }
268 if (ch != '\r' && ch != '\n')
269 throw MultipartException("Invalid boundary line found");
270 if (ch == '\r' || ch == '\n')
271 _istr.get();
272 if (_istr.peek() == '\n')
273 _istr.get();
274 }
275 else throw MultipartException("No boundary line found");
276}
277
278
279void MultipartReader::parseHeader(MessageHeader& messageHeader)
280{
281 messageHeader.clear();
282 messageHeader.read(_istr);
283 int ch = _istr.get();
284 if (ch == '\r' && _istr.peek() == '\n') _istr.get();
285}
286
287
288bool MultipartReader::readLine(std::string& line, std::string::size_type n)
289{
290 static const int eof = std::char_traits<char>::eof();
291 static const int maxLength = 1024;
292
293 line.clear();
294 int ch = _istr.peek();
295 int length = 0;
296 while (ch != eof && ch != '\r' && ch != '\n' && length < maxLength)
297 {
298 ch = (char) _istr.get();
299 if (line.length() < n) line += ch;
300 ch = _istr.peek();
301 length++;
302 }
303 if (ch != eof) _istr.get();
304 if (ch == '\r' && _istr.peek() == '\n') _istr.get();
305 return ch != eof && length < maxLength;
306}
307
308
309} } // namespace Poco::Net
310