1// Copyright (C) 2017 Witekio.
2// Copyright (C) 2018 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#include "qcoaprequest_p.h"
7
8#include <QtCore/qmath.h>
9#include <QtCore/qdatetime.h>
10#include <QtCore/qloggingcategory.h>
11#include <QtCore/QDebug>
12
13QT_BEGIN_NAMESPACE
14
15namespace {
16const auto CoapScheme = QLatin1String("coap");
17const auto CoapSecureScheme = QLatin1String("coaps");
18}
19
20QCoapRequestPrivate::QCoapRequestPrivate(const QUrl &url, QCoapMessage::Type type,
21 const QUrl &proxyUrl) :
22 QCoapMessagePrivate(type),
23 proxyUri(proxyUrl)
24{
25 setUrl(url);
26}
27
28QCoapRequestPrivate::~QCoapRequestPrivate()
29{
30}
31
32QCoapRequestPrivate *QCoapRequestPrivate::clone() const
33{
34 return new QCoapRequestPrivate(*this);
35}
36
37/*!
38 \internal
39
40 \brief Sets the url after adjusting it, and asserting its validity.
41*/
42void QCoapRequestPrivate::setUrl(const QUrl &url)
43{
44 // Print no warning when clearing URL
45 if (url.isEmpty()) {
46 uri = url;
47 return;
48 }
49
50 // Make first checks before editing the URL, to avoid editing it
51 // in a wrong way (e.g. when adding the scheme)
52 if (!url.isValid()) {
53 qCWarning(lcCoapExchange) << "Invalid CoAP url" << url.toString();
54 return;
55 }
56
57 // If the port is unknown, try to set it based on scheme
58 QUrl finalizedUrl = url;
59 if (!url.scheme().isEmpty()) {
60 if (url.scheme() == CoapScheme) {
61 if (url.port() == -1)
62 finalizedUrl.setPort(QtCoap::DefaultPort);
63 } else if (url.scheme() == CoapSecureScheme) {
64 if (url.port() == -1)
65 finalizedUrl.setPort(QtCoap::DefaultSecurePort);
66 } else {
67 qCWarning(lcCoapExchange) << "QCoapRequest: Request URL's scheme" << url.scheme()
68 << "isn't valid for CoAP";
69 return;
70 }
71 }
72
73 uri = finalizedUrl;
74}
75
76/*!
77 \class QCoapRequest
78 \inmodule QtCoap
79
80 \brief The QCoapRequest class holds a CoAP request. This request
81 can be sent with QCoapClient.
82
83 \reentrant
84
85 The QCoapRequest contains data needed to make CoAP frames that can be
86 sent to the URL it holds.
87
88 \sa QCoapClient, QCoapReply, QCoapResourceDiscoveryReply
89*/
90
91/*!
92 Constructs a QCoapRequest object with the target \a url,
93 the proxy URL \a proxyUrl and the \a type of the message.
94*/
95QCoapRequest::QCoapRequest(const QUrl &url, Type type, const QUrl &proxyUrl) :
96 QCoapMessage(*new QCoapRequestPrivate(url, type, proxyUrl))
97{
98}
99
100/*!
101 Constructs a QCoapRequest from a string literal
102*/
103QCoapRequest::QCoapRequest(const char *url, Type type) :
104 QCoapMessage(*new QCoapRequestPrivate(QUrl(QString::fromUtf8(utf8: url)), type))
105{
106}
107
108/*!
109 Constructs a copy of the \a other QCoapRequest.
110*/
111QCoapRequest::QCoapRequest(const QCoapRequest &other) :
112 //! No private data sharing, as QCoapRequestPrivate!=QCoapMessagePrivate
113 //! and the d_ptr is a QSharedDataPointer<QCoapMessagePrivate>
114 QCoapMessage(*other.d_func()->clone())
115{
116}
117
118/*!
119 Destroys the QCoapRequest.
120*/
121QCoapRequest::~QCoapRequest()
122{
123}
124
125/*!
126 Returns the target URI of the request.
127
128 \sa setUrl()
129*/
130QUrl QCoapRequest::url() const
131{
132 Q_D(const QCoapRequest);
133 return d->uri;
134}
135
136/*!
137 Returns the proxy URI of the request.
138 The request shall be sent directly if this is invalid.
139
140 \sa setProxyUrl()
141*/
142QUrl QCoapRequest::proxyUrl() const
143{
144 Q_D(const QCoapRequest);
145 return d->proxyUri;
146}
147
148/*!
149 Returns the method of the request.
150*/
151QtCoap::Method QCoapRequest::method() const
152{
153 Q_D(const QCoapRequest);
154 return d->method;
155}
156
157/*!
158 Returns \c true if the request is an observe request.
159
160 \sa enableObserve()
161*/
162bool QCoapRequest::isObserve() const
163{
164 return hasOption(name: QCoapOption::Observe);
165}
166
167/*!
168 Sets the target URI of the request to the given \a url.
169
170 If not indicated, the scheme of the URL will default to 'coap', and its
171 port will default to 5683.
172
173 \sa url()
174*/
175void QCoapRequest::setUrl(const QUrl &url)
176{
177 Q_D(QCoapRequest);
178 d->setUrl(url);
179}
180
181/*!
182 Sets the proxy URI of the request to the given \a proxyUrl.
183
184 \sa proxyUrl()
185*/
186void QCoapRequest::setProxyUrl(const QUrl &proxyUrl)
187{
188 Q_D(QCoapRequest);
189 d->proxyUri = proxyUrl;
190}
191
192/*!
193 Sets the observe to \c true to make an observe request.
194
195 \sa isObserve()
196*/
197void QCoapRequest::enableObserve()
198{
199 if (isObserve())
200 return;
201
202 addOption(name: QCoapOption::Observe);
203}
204
205/*!
206 \internal
207
208 Adjusts the request URL by setting the correct default scheme and port
209 (if not indicated) based on the \a secure parameter.
210
211 In non-secure mode the scheme of request URL will default to \c coap, and
212 its port will default to \e 5683. In secure mode the scheme will default to
213 \c coaps, and the port will default to \e 5684.
214*/
215void QCoapRequestPrivate::adjustUrl(bool secure)
216{
217 uri = adjustedUrl(url: uri, secure);
218}
219
220/*!
221 Creates a copy of \a other.
222*/
223QCoapRequest &QCoapRequest::operator=(const QCoapRequest &other)
224{
225 d_ptr = other.d_ptr;
226 return *this;
227}
228
229/*!
230 \internal
231
232 Returns \c true if the \a url is a valid CoAP URL.
233*/
234bool QCoapRequestPrivate::isUrlValid(const QUrl &url)
235{
236 return (url.isValid() && !url.isLocalFile() && !url.isRelative()
237 && (url.scheme() == CoapScheme || url.scheme() == CoapSecureScheme)
238 && !url.hasFragment());
239}
240
241/*!
242 \internal
243
244 Adjusts the \a url by setting the correct default scheme and port
245 (if not indicated) based on the \a secure parameter. Returns the
246 adjusted URL.
247
248 In non-secure mode the scheme of request URL will default to \c coap, and
249 its port will default to \e 5683. In secure mode the scheme will default to
250 \c coaps, and the port will default to \e 5684.
251*/
252QUrl QCoapRequestPrivate::adjustedUrl(const QUrl &url, bool secure)
253{
254 if (url.isEmpty() || !url.isValid())
255 return QUrl();
256
257 QUrl finalizedUrl = url;
258 const auto scheme = secure ? CoapSecureScheme : CoapScheme;
259 if (url.host().isEmpty() && url.isRelative()) {
260 // In some cases host address is mistaken for part of the relative path,
261 // prepending the scheme fixes this.
262 finalizedUrl = url.toString().prepend(s: scheme + QLatin1String("://"));
263 } else if (url.scheme().isEmpty()) {
264 finalizedUrl.setScheme(scheme);
265 }
266
267 if (finalizedUrl.host().isEmpty()) {
268 qCWarning(lcCoapExchange) << "The requested URL" << url << "is not a valid CoAP URL.";
269 return QUrl();
270 }
271
272 if (url.port() == -1) {
273 const auto port = secure ? QtCoap::DefaultSecurePort : QtCoap::DefaultPort;
274 finalizedUrl.setPort(port);
275 }
276
277 return finalizedUrl;
278}
279
280/*!
281 \internal
282
283 For QSharedDataPointer.
284*/
285QCoapRequestPrivate* QCoapRequest::d_func()
286{
287 return static_cast<QCoapRequestPrivate*>(d_ptr.data());
288}
289
290/*!
291 \internal
292
293 Creates a copy of \a other request and sets \a method as its request method.
294 Adjusts the request URL based on \a isSecure parameter.
295*/
296QCoapRequest
297QCoapRequestPrivate::createRequest(const QCoapRequest &other, QtCoap::Method method, bool isSecure)
298{
299 QCoapRequest request(other);
300 request.d_func()->method = method;
301 request.d_func()->adjustUrl(secure: isSecure);
302 return request;
303}
304
305QT_END_NAMESPACE
306

source code of qtcoap/src/coap/qcoaprequest.cpp