1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qhttpmultipart.h"
41#include "qhttpmultipart_p.h"
42#include "QtCore/qdatetime.h" // for initializing the random number generator with QTime
43#include "QtCore/qmutex.h"
44#include "QtCore/qrandom.h"
45
46QT_BEGIN_NAMESPACE
47
48/*!
49 \class QHttpPart
50 \brief The QHttpPart class holds a body part to be used inside a
51 HTTP multipart MIME message.
52 \since 4.8
53
54 \ingroup network
55 \ingroup shared
56 \inmodule QtNetwork
57
58 The QHttpPart class holds a body part to be used inside a HTTP
59 multipart MIME message (which is represented by the QHttpMultiPart class).
60 A QHttpPart consists of a header block
61 and a data block, which are separated by each other by two
62 consecutive new lines. An example for one part would be:
63
64 \snippet code/src_network_access_qhttppart.cpp 0
65
66 For setting headers, use setHeader() and setRawHeader(), which behave
67 exactly like QNetworkRequest::setHeader() and QNetworkRequest::setRawHeader().
68
69 For reading small pieces of data, use setBody(); for larger data blocks
70 like e.g. images, use setBodyDevice(). The latter method saves memory by
71 not copying the data internally, but reading directly from the device.
72 This means that the device must be opened and readable at the moment when
73 the multipart message containing the body part is sent on the network via
74 QNetworkAccessManager::post().
75
76 To construct a QHttpPart with a small body, consider the following snippet
77 (this produces the data shown in the example above):
78
79 \snippet code/src_network_access_qhttppart.cpp 1
80
81 To construct a QHttpPart reading from a device (e.g. a file), the following
82 can be applied:
83
84 \snippet code/src_network_access_qhttppart.cpp 2
85
86 Be aware that QHttpPart does not take ownership of the device when set, so
87 it is the developer's responsibility to destroy it when it is not needed anymore.
88 A good idea might be to set the multipart message as parent object for the device,
89 as documented at the documentation for QHttpMultiPart.
90
91 \sa QHttpMultiPart, QNetworkAccessManager
92*/
93
94
95/*!
96 Constructs an empty QHttpPart object.
97*/
98QHttpPart::QHttpPart() : d(new QHttpPartPrivate)
99{
100}
101
102/*!
103 Creates a copy of \a other.
104*/
105QHttpPart::QHttpPart(const QHttpPart &other) : d(other.d)
106{
107}
108
109/*!
110 Destroys this QHttpPart.
111*/
112QHttpPart::~QHttpPart()
113{
114 d = nullptr;
115}
116
117/*!
118 Creates a copy of \a other.
119*/
120QHttpPart &QHttpPart::operator=(const QHttpPart &other)
121{
122 d = other.d;
123 return *this;
124}
125
126/*!
127 \fn void QHttpPart::swap(QHttpPart &other)
128 \since 5.0
129
130 Swaps this HTTP part with \a other. This function is very fast and
131 never fails.
132*/
133
134/*!
135 Returns \c true if this object is the same as \a other (i.e., if they
136 have the same headers and body).
137
138 \sa operator!=()
139*/
140bool QHttpPart::operator==(const QHttpPart &other) const
141{
142 return d == other.d || *d == *other.d;
143}
144
145/*!
146 \fn bool QHttpPart::operator!=(const QHttpPart &other) const
147
148 Returns \c true if this object is not the same as \a other.
149
150 \sa operator==()
151*/
152
153/*!
154 Sets the value of the known header \a header to be \a value,
155 overriding any previously set headers.
156
157 \sa QNetworkRequest::KnownHeaders, setRawHeader(), QNetworkRequest::setHeader()
158*/
159void QHttpPart::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
160{
161 d->setCookedHeader(header, value);
162}
163
164/*!
165 Sets the header \a headerName to be of value \a headerValue. If \a
166 headerName corresponds to a known header (see
167 QNetworkRequest::KnownHeaders), the raw format will be parsed and
168 the corresponding "cooked" header will be set as well.
169
170 \note Setting the same header twice overrides the previous
171 setting. To accomplish the behaviour of multiple HTTP headers of
172 the same name, you should concatenate the two values, separating
173 them with a comma (",") and set one single raw header.
174
175 \sa QNetworkRequest::KnownHeaders, setHeader(), QNetworkRequest::setRawHeader()
176*/
177void QHttpPart::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
178{
179 d->setRawHeader(key: headerName, value: headerValue);
180}
181
182/*!
183 Sets the body of this MIME part to \a body. The body set with this method
184 will be used unless the device is set via setBodyDevice(). For a large
185 amount of data (e.g. an image), use setBodyDevice(), which will not copy
186 the data internally.
187
188 \sa setBodyDevice()
189*/
190void QHttpPart::setBody(const QByteArray &body)
191{
192 d->setBody(body);
193}
194
195/*!
196 Sets the device to read the content from to \a device. For large amounts of data
197 this method should be preferred over setBody(),
198 because the content is not copied when using this method, but read
199 directly from the device.
200 \a device must be open and readable. QHttpPart does not take ownership
201 of \a device, i.e. the device must be closed and destroyed if necessary.
202 if \a device is sequential (e.g. sockets, but not files),
203 QNetworkAccessManager::post() should be called after \a device has
204 emitted finished().
205 For unsetting the device and using data set via setBody(), use
206 "setBodyDevice(0)".
207
208 \sa setBody(), QNetworkAccessManager::post()
209 */
210void QHttpPart::setBodyDevice(QIODevice *device)
211{
212 d->setBodyDevice(device);
213}
214
215
216
217/*!
218 \class QHttpMultiPart
219 \brief The QHttpMultiPart class resembles a MIME multipart message to be sent over HTTP.
220 \since 4.8
221
222 \ingroup network
223 \inmodule QtNetwork
224
225 The QHttpMultiPart resembles a MIME multipart message, as described in RFC 2046,
226 which is to be sent over HTTP.
227 A multipart message consists of an arbitrary number of body parts (see QHttpPart),
228 which are separated by a unique boundary. The boundary of the QHttpMultiPart is
229 constructed with the string "boundary_.oOo._" followed by random characters,
230 and provides enough uniqueness to make sure it does not occur inside the parts itself.
231 If desired, the boundary can still be set via setBoundary().
232
233 As an example, consider the following code snippet, which constructs a multipart
234 message containing a text part followed by an image part:
235
236 \snippet code/src_network_access_qhttpmultipart.cpp 0
237
238 \sa QHttpPart, QNetworkAccessManager::post()
239*/
240
241/*!
242 \enum QHttpMultiPart::ContentType
243
244 List of known content types for a multipart subtype as described
245 in RFC 2046 and others.
246
247 \value MixedType corresponds to the "multipart/mixed" subtype,
248 meaning the body parts are independent of each other, as described
249 in RFC 2046.
250
251 \value RelatedType corresponds to the "multipart/related" subtype,
252 meaning the body parts are related to each other, as described in RFC 2387.
253
254 \value FormDataType corresponds to the "multipart/form-data"
255 subtype, meaning the body parts contain form elements, as described in RFC 2388.
256
257 \value AlternativeType corresponds to the "multipart/alternative"
258 subtype, meaning the body parts are alternative representations of
259 the same information, as described in RFC 2046.
260
261 \sa setContentType()
262*/
263
264/*!
265 Constructs a QHttpMultiPart with content type MixedType and sets
266 \a parent as the parent object.
267
268 \sa QHttpMultiPart::ContentType
269*/
270QHttpMultiPart::QHttpMultiPart(QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
271{
272 Q_D(QHttpMultiPart);
273 d->contentType = MixedType;
274}
275
276/*!
277 Constructs a QHttpMultiPart with content type \a contentType and
278 sets parent as the parent object.
279
280 \sa QHttpMultiPart::ContentType
281*/
282QHttpMultiPart::QHttpMultiPart(QHttpMultiPart::ContentType contentType, QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
283{
284 Q_D(QHttpMultiPart);
285 d->contentType = contentType;
286}
287
288/*!
289 Destroys the multipart.
290*/
291QHttpMultiPart::~QHttpMultiPart()
292{
293}
294
295/*!
296 Appends \a httpPart to this multipart.
297*/
298void QHttpMultiPart::append(const QHttpPart &httpPart)
299{
300 d_func()->parts.append(t: httpPart);
301}
302
303/*!
304 Sets the content type to \a contentType. The content type will be used
305 in the HTTP header section when sending the multipart message via
306 QNetworkAccessManager::post().
307 In case you want to use a multipart subtype not contained in
308 QHttpMultiPart::ContentType,
309 you can add the "Content-Type" header field to the QNetworkRequest
310 by hand, and then use this request together with the multipart
311 message for posting.
312
313 \sa QHttpMultiPart::ContentType, QNetworkAccessManager::post()
314*/
315void QHttpMultiPart::setContentType(QHttpMultiPart::ContentType contentType)
316{
317 d_func()->contentType = contentType;
318}
319
320/*!
321 returns the boundary.
322
323 \sa setBoundary()
324*/
325QByteArray QHttpMultiPart::boundary() const
326{
327 return d_func()->boundary;
328}
329
330/*!
331 Sets the boundary to \a boundary.
332
333 Usually, you do not need to generate a boundary yourself; upon construction
334 the boundary is initiated with the string "boundary_.oOo._" followed by random
335 characters, and provides enough uniqueness to make sure it does not occur
336 inside the parts itself.
337
338 \sa boundary()
339*/
340void QHttpMultiPart::setBoundary(const QByteArray &boundary)
341{
342 d_func()->boundary = boundary;
343}
344
345
346
347// ------------------------------------------------------------------
348// ----------- implementations of private classes: ------------------
349// ------------------------------------------------------------------
350
351
352
353qint64 QHttpPartPrivate::bytesAvailable() const
354{
355 checkHeaderCreated();
356 qint64 bytesAvailable = header.count();
357 if (bodyDevice) {
358 bytesAvailable += bodyDevice->bytesAvailable() - readPointer;
359 } else {
360 bytesAvailable += body.count() - readPointer;
361 }
362 // the device might have closed etc., so make sure we do not return a negative value
363 return qMax(a: bytesAvailable, b: (qint64) 0);
364}
365
366qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
367{
368 checkHeaderCreated();
369 qint64 bytesRead = 0;
370 qint64 headerDataCount = header.count();
371
372 // read header if it has not been read yet
373 if (readPointer < headerDataCount) {
374 bytesRead = qMin(a: headerDataCount - readPointer, b: maxSize);
375 const char *headerData = header.constData();
376 memcpy(dest: data, src: headerData + readPointer, n: bytesRead);
377 readPointer += bytesRead;
378 }
379 // read content if there is still space
380 if (bytesRead < maxSize) {
381 if (bodyDevice) {
382 qint64 dataBytesRead = bodyDevice->read(data: data + bytesRead, maxlen: maxSize - bytesRead);
383 if (dataBytesRead == -1)
384 return -1;
385 bytesRead += dataBytesRead;
386 readPointer += dataBytesRead;
387 } else {
388 qint64 contentBytesRead = qMin(a: body.count() - readPointer + headerDataCount, b: maxSize - bytesRead);
389 const char *contentData = body.constData();
390 // if this method is called several times, we need to find the
391 // right offset in the content ourselves:
392 memcpy(dest: data + bytesRead, src: contentData + readPointer - headerDataCount, n: contentBytesRead);
393 bytesRead += contentBytesRead;
394 readPointer += contentBytesRead;
395 }
396 }
397 return bytesRead;
398}
399
400qint64 QHttpPartPrivate::size() const
401{
402 checkHeaderCreated();
403 qint64 size = header.count();
404 if (bodyDevice) {
405 size += bodyDevice->size();
406 } else {
407 size += body.count();
408 }
409 return size;
410}
411
412bool QHttpPartPrivate::reset()
413{
414 bool ret = true;
415 if (bodyDevice)
416 if (!bodyDevice->reset())
417 ret = false;
418 readPointer = 0;
419 return ret;
420}
421void QHttpPartPrivate::checkHeaderCreated() const
422{
423 if (!headerCreated) {
424 // copied from QHttpNetworkRequestPrivate::header() and adapted
425 QList<QPair<QByteArray, QByteArray> > fields = allRawHeaders();
426 QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
427 for (; it != fields.constEnd(); ++it)
428 header += it->first + ": " + it->second + "\r\n";
429 header += "\r\n";
430 headerCreated = true;
431 }
432}
433
434QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::MixedType), device(new QHttpMultiPartIODevice(this))
435{
436 // 24 random bytes, becomes 32 characters when encoded to Base64
437 quint32 random[6];
438 QRandomGenerator::global()->fillRange(buffer&: random);
439 boundary = "boundary_.oOo._"
440 + QByteArray::fromRawData(reinterpret_cast<char *>(random), size: sizeof(random)).toBase64();
441
442 // boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
443 Q_ASSERT(boundary.count() <= 70);
444}
445
446qint64 QHttpMultiPartIODevice::size() const
447{
448 // if not done yet, we calculate the size and the offsets of each part,
449 // including boundary (needed later in readData)
450 if (deviceSize == -1) {
451 qint64 currentSize = 0;
452 qint64 boundaryCount = multiPart->boundary.count();
453 for (int a = 0; a < multiPart->parts.count(); a++) {
454 partOffsets.append(t: currentSize);
455 // 4 additional bytes for the "--" before and the "\r\n" after the boundary,
456 // and 2 bytes for the "\r\n" after the content
457 currentSize += boundaryCount + 4 + multiPart->parts.at(i: a).d->size() + 2;
458 }
459 currentSize += boundaryCount + 6; // size for ending boundary, 2 beginning and ending dashes and "\r\n"
460 deviceSize = currentSize;
461 }
462 return deviceSize;
463}
464
465bool QHttpMultiPartIODevice::isSequential() const
466{
467 for (int a = 0; a < multiPart->parts.count(); a++) {
468 QIODevice *device = multiPart->parts.at(i: a).d->bodyDevice;
469 // we are sequential if any of the bodyDevices of our parts are sequential;
470 // when reading from a byte array, we are not sequential
471 if (device && device->isSequential())
472 return true;
473 }
474 return false;
475}
476
477bool QHttpMultiPartIODevice::reset()
478{
479 // Reset QIODevice's data
480 QIODevice::reset();
481 for (int a = 0; a < multiPart->parts.count(); a++)
482 if (!multiPart->parts[a].d->reset())
483 return false;
484 readPointer = 0;
485 return true;
486}
487qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
488{
489 qint64 bytesRead = 0, index = 0;
490
491 // skip the parts we have already read
492 while (index < multiPart->parts.count() &&
493 readPointer >= partOffsets.at(i: index) + multiPart->parts.at(i: index).d->size()
494 + multiPart->boundary.count() + 6) // 6 == 2 boundary dashes, \r\n after boundary, \r\n after multipart
495 index++;
496
497 // read the data
498 while (bytesRead < maxSize && index < multiPart->parts.count()) {
499
500 // check whether we need to read the boundary of the current part
501 QByteArray boundaryData = "--" + multiPart->boundary + "\r\n";
502 qint64 boundaryCount = boundaryData.count();
503 qint64 partIndex = readPointer - partOffsets.at(i: index);
504 if (partIndex < boundaryCount) {
505 qint64 boundaryBytesRead = qMin(a: boundaryCount - partIndex, b: maxSize - bytesRead);
506 memcpy(dest: data + bytesRead, src: boundaryData.constData() + partIndex, n: boundaryBytesRead);
507 bytesRead += boundaryBytesRead;
508 readPointer += boundaryBytesRead;
509 partIndex += boundaryBytesRead;
510 }
511
512 // check whether we need to read the data of the current part
513 if (bytesRead < maxSize && partIndex >= boundaryCount && partIndex < boundaryCount + multiPart->parts.at(i: index).d->size()) {
514 qint64 dataBytesRead = multiPart->parts[index].d->readData(data: data + bytesRead, maxSize: maxSize - bytesRead);
515 if (dataBytesRead == -1)
516 return -1;
517 bytesRead += dataBytesRead;
518 readPointer += dataBytesRead;
519 partIndex += dataBytesRead;
520 }
521
522 // check whether we need to read the ending CRLF of the current part
523 if (bytesRead < maxSize && partIndex >= boundaryCount + multiPart->parts.at(i: index).d->size()) {
524 if (bytesRead == maxSize - 1)
525 return bytesRead;
526 memcpy(dest: data + bytesRead, src: "\r\n", n: 2);
527 bytesRead += 2;
528 readPointer += 2;
529 index++;
530 }
531 }
532 // check whether we need to return the final boundary
533 if (bytesRead < maxSize && index == multiPart->parts.count()) {
534 QByteArray finalBoundary = "--" + multiPart->boundary + "--\r\n";
535 qint64 boundaryIndex = readPointer + finalBoundary.count() - size();
536 qint64 lastBoundaryBytesRead = qMin(a: finalBoundary.count() - boundaryIndex, b: maxSize - bytesRead);
537 memcpy(dest: data + bytesRead, src: finalBoundary.constData() + boundaryIndex, n: lastBoundaryBytesRead);
538 bytesRead += lastBoundaryBytesRead;
539 readPointer += lastBoundaryBytesRead;
540 }
541 return bytesRead;
542}
543
544qint64 QHttpMultiPartIODevice::writeData(const char *data, qint64 maxSize)
545{
546 Q_UNUSED(data);
547 Q_UNUSED(maxSize);
548 return -1;
549}
550
551
552QT_END_NAMESPACE
553

source code of qtbase/src/network/access/qhttpmultipart.cpp