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 
46 QT_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 */
QHttpPart()98 QHttpPart::QHttpPart() : d(new QHttpPartPrivate)
99 {
100 }
101 
102 /*!
103     Creates a copy of \a other.
104 */
QHttpPart(const QHttpPart & other)105 QHttpPart::QHttpPart(const QHttpPart &other) : d(other.d)
106 {
107 }
108 
109 /*!
110     Destroys this QHttpPart.
111 */
~QHttpPart()112 QHttpPart::~QHttpPart()
113 {
114     d = nullptr;
115 }
116 
117 /*!
118     Creates a copy of \a other.
119 */
operator =(const QHttpPart & other)120 QHttpPart &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 */
operator ==(const QHttpPart & other) const140 bool 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 */
setHeader(QNetworkRequest::KnownHeaders header,const QVariant & value)159 void 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 */
setRawHeader(const QByteArray & headerName,const QByteArray & headerValue)177 void QHttpPart::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
178 {
179     d->setRawHeader(headerName, 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 */
setBody(const QByteArray & body)190 void 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   */
setBodyDevice(QIODevice * device)210 void 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 */
QHttpMultiPart(QObject * parent)270 QHttpMultiPart::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 */
QHttpMultiPart(QHttpMultiPart::ContentType contentType,QObject * parent)282 QHttpMultiPart::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 */
~QHttpMultiPart()291 QHttpMultiPart::~QHttpMultiPart()
292 {
293 }
294 
295 /*!
296     Appends \a httpPart to this multipart.
297 */
append(const QHttpPart & httpPart)298 void QHttpMultiPart::append(const QHttpPart &httpPart)
299 {
300     d_func()->parts.append(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 */
setContentType(QHttpMultiPart::ContentType contentType)315 void QHttpMultiPart::setContentType(QHttpMultiPart::ContentType contentType)
316 {
317     d_func()->contentType = contentType;
318 }
319 
320 /*!
321     returns the boundary.
322 
323     \sa setBoundary()
324 */
boundary() const325 QByteArray 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 */
setBoundary(const QByteArray & boundary)340 void 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 
bytesAvailable() const353 qint64 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(bytesAvailable, (qint64) 0);
364 }
365 
readData(char * data,qint64 maxSize)366 qint64 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(headerDataCount - readPointer, maxSize);
375         const char *headerData = header.constData();
376         memcpy(data, headerData + readPointer, 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 + bytesRead, maxSize - bytesRead);
383             if (dataBytesRead == -1)
384                 return -1;
385             bytesRead += dataBytesRead;
386             readPointer += dataBytesRead;
387         } else {
388             qint64 contentBytesRead = qMin(body.count() - readPointer + headerDataCount, 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(data + bytesRead, contentData + readPointer - headerDataCount, contentBytesRead);
393             bytesRead += contentBytesRead;
394             readPointer += contentBytesRead;
395         }
396     }
397     return bytesRead;
398 }
399 
size() const400 qint64 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 
reset()412 bool QHttpPartPrivate::reset()
413 {
414     bool ret = true;
415     if (bodyDevice)
416         if (!bodyDevice->reset())
417             ret = false;
418     readPointer = 0;
419     return ret;
420 }
checkHeaderCreated() const421 void 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 
QHttpMultiPartPrivate()434 QHttpMultiPartPrivate::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(random);
439     boundary = "boundary_.oOo._"
440                + QByteArray::fromRawData(reinterpret_cast<char *>(random), 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 
size() const446 qint64 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(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(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 
isSequential() const465 bool QHttpMultiPartIODevice::isSequential() const
466 {
467     for (int a = 0; a < multiPart->parts.count(); a++) {
468         QIODevice *device = multiPart->parts.at(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 
reset()477 bool 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 }
readData(char * data,qint64 maxSize)487 qint64 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(index) + multiPart->parts.at(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(index);
504         if (partIndex < boundaryCount) {
505             qint64 boundaryBytesRead = qMin(boundaryCount - partIndex, maxSize - bytesRead);
506             memcpy(data + bytesRead, boundaryData.constData() + partIndex, 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(index).d->size()) {
514             qint64 dataBytesRead = multiPart->parts[index].d->readData(data + bytesRead, 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(index).d->size()) {
524             if (bytesRead == maxSize - 1)
525                 return bytesRead;
526             memcpy(data + bytesRead, "\r\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(finalBoundary.count() - boundaryIndex, maxSize - bytesRead);
537         memcpy(data + bytesRead, finalBoundary.constData() + boundaryIndex, lastBoundaryBytesRead);
538         bytesRead += lastBoundaryBytesRead;
539         readPointer += lastBoundaryBytesRead;
540     }
541     return bytesRead;
542 }
543 
writeData(const char * data,qint64 maxSize)544 qint64 QHttpMultiPartIODevice::writeData(const char *data, qint64 maxSize)
545 {
546     Q_UNUSED(data);
547     Q_UNUSED(maxSize);
548     return -1;
549 }
550 
551 
552 QT_END_NAMESPACE
553