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 | |
21 | using Poco::BufferedStreamBuf; |
22 | |
23 | |
24 | namespace Poco { |
25 | namespace Net { |
26 | |
27 | |
28 | // |
29 | // MultipartStreamBuf |
30 | // |
31 | |
32 | |
33 | MultipartStreamBuf::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 | |
43 | MultipartStreamBuf::~MultipartStreamBuf() |
44 | { |
45 | } |
46 | |
47 | |
48 | int 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 | |
112 | bool MultipartStreamBuf::lastPart() const |
113 | { |
114 | return _lastPart; |
115 | } |
116 | |
117 | |
118 | // |
119 | // MultipartIOS |
120 | // |
121 | |
122 | |
123 | MultipartIOS::MultipartIOS(std::istream& istr, const std::string& boundary): |
124 | _buf(istr, boundary) |
125 | { |
126 | poco_ios_init(&_buf); |
127 | } |
128 | |
129 | |
130 | MultipartIOS::~MultipartIOS() |
131 | { |
132 | try |
133 | { |
134 | _buf.sync(); |
135 | } |
136 | catch (...) |
137 | { |
138 | } |
139 | } |
140 | |
141 | |
142 | MultipartStreamBuf* MultipartIOS::rdbuf() |
143 | { |
144 | return &_buf; |
145 | } |
146 | |
147 | |
148 | bool MultipartIOS::lastPart() const |
149 | { |
150 | return _buf.lastPart(); |
151 | } |
152 | |
153 | |
154 | // |
155 | // MultipartInputStream |
156 | // |
157 | |
158 | |
159 | MultipartInputStream::MultipartInputStream(std::istream& istr, const std::string& boundary): |
160 | MultipartIOS(istr, boundary), |
161 | std::istream(&_buf) |
162 | { |
163 | } |
164 | |
165 | |
166 | MultipartInputStream::~MultipartInputStream() |
167 | { |
168 | } |
169 | |
170 | |
171 | // |
172 | // MultipartReader |
173 | // |
174 | |
175 | |
176 | MultipartReader::MultipartReader(std::istream& istr): |
177 | _istr(istr), |
178 | _pMPI(0) |
179 | { |
180 | } |
181 | |
182 | |
183 | MultipartReader::MultipartReader(std::istream& istr, const std::string& boundary): |
184 | _istr(istr), |
185 | _boundary(boundary), |
186 | _pMPI(0) |
187 | { |
188 | } |
189 | |
190 | |
191 | MultipartReader::~MultipartReader() |
192 | { |
193 | delete _pMPI; |
194 | } |
195 | |
196 | |
197 | void MultipartReader::(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 | |
216 | bool MultipartReader::hasNextPart() |
217 | { |
218 | return (!_pMPI || !_pMPI->lastPart()) && _istr.good(); |
219 | } |
220 | |
221 | |
222 | std::istream& MultipartReader::stream() const |
223 | { |
224 | poco_check_ptr (_pMPI); |
225 | |
226 | return *_pMPI; |
227 | } |
228 | |
229 | |
230 | const std::string& MultipartReader::boundary() const |
231 | { |
232 | return _boundary; |
233 | } |
234 | |
235 | |
236 | void 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 | |
253 | void 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 | |
279 | void MultipartReader::(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 | |
288 | bool MultipartReader::(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) |
300 | line += static_cast<char>(ch); |
301 | ch = _istr.peek(); |
302 | length++; |
303 | } |
304 | if (ch != eof) _istr.get(); |
305 | if (ch == '\r' && _istr.peek() == '\n') _istr.get(); |
306 | return ch != eof && length < maxLength; |
307 | } |
308 | |
309 | |
310 | } } // namespace Poco::Net |
311 | |