cutelyst 3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
multipartformdataparser.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2014-2022 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "common.h"
6#include "multipartformdataparser_p.h"
7#include "upload_p.h"
8
9using namespace Cutelyst;
10
12{
13 Uploads ret;
14 if (body->isSequential()) {
15 qCWarning(CUTELYST_MULTIPART) << "Parsing sequential body is not supported" << body;
16 return ret;
17 }
18
19 int start = contentType.indexOf(u"boundary=");
20 if (start == -1) {
21 qCWarning(CUTELYST_MULTIPART) << "No boundary match" << contentType;
22 return ret;
23 }
24
25 start += 9;
26 QByteArray boundary;
27 const int len = contentType.length();
28 boundary.reserve(contentType.length() - start + 2);
29
30 for (int i = start, quotes = 0; i < len; ++i) {
31 const QChar ch = contentType.at(i);
32 if (ch == u'\"') {
33 if ((quotes == 0 && i > start) || ++quotes == 2) {
34 break;
35 }
36 } else if (ch == u';') {
37 break;
38 } else {
39 boundary.append(ch.toLatin1());
40 }
41 }
42
43 if (boundary.isEmpty()) {
44 qCWarning(CUTELYST_MULTIPART) << "Boundary match was empty" << contentType;
45 return ret;
46 }
47 boundary.prepend("--", 2);
48
49 if (bufferSize < 1024) {
50 bufferSize = 1024;
51 }
52 char *buffer = new char[bufferSize];
53
54 ret = MultiPartFormDataParserPrivate::execute(buffer, bufferSize, body, boundary);
55
56 delete[] buffer;
57
58 return ret;
59}
60
61Uploads MultiPartFormDataParserPrivate::execute(char *buffer,
62 int bufferSize,
63 QIODevice *body,
64 const QByteArray &boundary)
65{
66 Uploads ret;
67 QByteArray headerLine;
68 Headers headers;
69 qint64 startOffset = 0;
70 qint64 pos = 0;
71 qint64 contentLength = body->size();
72 int bufferSkip = 0;
73 int boundarySize = boundary.size();
74 ParserState state = FindBoundary;
75 QByteArrayMatcher matcher(boundary);
76
77 while (pos < contentLength) {
78 qint64 len = body->read(buffer + bufferSkip, bufferSize - bufferSkip);
79 if (len < 0) {
80 qCWarning(CUTELYST_MULTIPART) << "Error while reading POST body" << body->errorString();
81 return ret;
82 }
83
84 pos += len;
85 len += bufferSkip;
86 bufferSkip = 0;
87 int i = 0;
88 while (i < len) {
89 switch (state) {
90 case FindBoundary:
91 i += findBoundary(buffer + i, len - i, matcher, boundarySize, state);
92 break;
93 case EndBoundaryCR:
94 // TODO the "--" case
95 if (buffer[i] != '\r') {
96 // qCDebug(CUTELYST_MULTIPART) << "EndBoundaryCR return!";
97 return ret;
98 }
99 state = EndBoundaryLF;
100 break;
101 case EndBoundaryLF:
102 if (buffer[i] != '\n') {
103 // qCDebug(CUTELYST_MULTIPART) << "EndBoundaryLF return!";
104 return ret;
105 }
106 state = StartHeaders;
107 break;
108 case StartHeaders:
109 if (headerLine.isEmpty() && buffer[i] == '\r') {
110 // nothing was read
111 state = EndHeaders;
112 } else {
113 char *pch = static_cast<char *>(memchr(buffer + i, '\r', len - i));
114 if (pch == NULL) {
115 headerLine.append(buffer + i, len - i);
116 i = len;
117 } else {
118 headerLine.append(buffer + i, pch - buffer - i);
119 i = pch - buffer;
120 state = FinishHeader;
121 }
122 }
123 break;
124 case FinishHeader:
125 if (buffer[i] == '\n') {
126 int dotdot = headerLine.indexOf(':');
127 headers.setHeader(QString::fromLatin1(headerLine.left(dotdot)),
128 QString::fromUtf8(headerLine.mid(dotdot + 1).trimmed()));
129 headerLine = QByteArray();
130 state = StartHeaders;
131 } else {
132 // qCDebug(CUTELYST_MULTIPART) << "FinishHeader return!";
133 return ret;
134 }
135 break;
136 case EndHeaders:
137 if (buffer[i] == '\n') {
138 state = StartData;
139 } else {
140 // qCDebug(CUTELYST_MULTIPART) << "EndHeaders return!";
141 return ret;
142 }
143 break;
144 case StartData:
145 // qCDebug(CUTELYST_MULTIPART) << "StartData" << body->pos() - len +
146 // i;
147 startOffset = pos - len + i;
148 state = EndData;
149 break;
150 case EndData:
151 i += findBoundary(buffer + i, len - i, matcher, boundarySize, state);
152
153 if (state == EndBoundaryCR) {
154 // qCDebug(CUTELYST_MULTIPART) << "EndData" << body->pos() -
155 // len + i - boundaryLength - 1;
156 const qint64 endOffset = pos - len + i - boundarySize - 1;
157 auto upload =
158 new Upload(new UploadPrivate(body, headers, startOffset, endOffset));
159 ret.append(upload);
160
161 headers = Headers();
162 } else {
163 // Boundary was not found so move the boundary size at end of the buffer
164 // to be sure we don't have the boundary in the middle of two chunks
165 bufferSkip = boundarySize - 1;
166 memmove(buffer, buffer + len - bufferSkip, bufferSkip);
167 }
168
169 break;
170 }
171 ++i;
172 }
173 }
174
175 return ret;
176}
177
178int MultiPartFormDataParserPrivate::findBoundary(char *buffer,
179 int len,
180 const QByteArrayMatcher &matcher,
181 int boundarySize,
182 MultiPartFormDataParserPrivate::ParserState &state)
183{
184 int i = matcher.indexIn(buffer, len);
185 // qCDebug(CUTELYST_MULTIPART) << "findBoundary" << QByteArray(buffer, len);
186 if (i != -1) {
187 // qCDebug(CUTELYST_MULTIPART) << "FindBoundary: found at" << i << body->pos() << len
188 // << body->pos() - len + i << i + boundaryLength;
189 state = EndBoundaryCR;
190 return i + boundarySize - 1;
191 }
192 return len;
193}
194
195#include "moc_multipartformdataparser_p.cpp"
void setHeader(const QString &field, const QString &value)
Definition headers.cpp:396
static Uploads parse(QIODevice *body, QStringView contentType, int bufferSize=4096)
Parser for multipart/formdata.
Cutelyst Upload handles file upload request
Definition upload.h:23
The Cutelyst namespace holds all public Cutelyst API.
Definition Mainpage.dox:8
QVector< Upload * > Uploads
Definition request.h:25
QByteArray & append(char ch)
int indexOf(char ch, int from) const const
bool isEmpty() const const
QByteArray left(int len) const const
QByteArray mid(int pos, int len) const const
QByteArray & prepend(char ch)
void reserve(int size)
int size() const const
QByteArray trimmed() const const
int indexIn(const QByteArray &ba, int from) const const
char toLatin1() const const
QString errorString() const const
virtual bool isSequential() const const
qint64 read(char *data, qint64 maxSize)
virtual qint64 size() const const
QString fromLatin1(const char *str, int size)
QString fromUtf8(const char *str, int size)
QChar at(qsizetype n) const const
qsizetype indexOf(QChar c, qsizetype from, Qt::CaseSensitivity cs) const const
int length() const const
void append(const T &value)