1 /*
2 * Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15 #include "ocsjob.h"
16 #include "networkjobs.h"
17 #include "account.h"
18
19 #include <QBuffer>
20 #include <QJsonDocument>
21 #include <QJsonObject>
22
23 namespace OCC {
24
25 Q_LOGGING_CATEGORY(lcOcs, "nextcloud.gui.sharing.ocs", QtInfoMsg)
26
OcsJob(AccountPtr account)27 OcsJob::OcsJob(AccountPtr account)
28 : AbstractNetworkJob(account, "")
29 {
30 _passStatusCodes.append(OCS_SUCCESS_STATUS_CODE);
31 _passStatusCodes.append(OCS_SUCCESS_STATUS_CODE_V2);
32 _passStatusCodes.append(OCS_NOT_MODIFIED_STATUS_CODE_V2);
33 setIgnoreCredentialFailure(true);
34 }
35
setVerb(const QByteArray & verb)36 void OcsJob::setVerb(const QByteArray &verb)
37 {
38 _verb = verb;
39 }
40
addParam(const QString & name,const QString & value)41 void OcsJob::addParam(const QString &name, const QString &value)
42 {
43 _params.append(qMakePair(name, value));
44 }
45
addPassStatusCode(int code)46 void OcsJob::addPassStatusCode(int code)
47 {
48 _passStatusCodes.append(code);
49 }
50
appendPath(const QString & id)51 void OcsJob::appendPath(const QString &id)
52 {
53 setPath(path() + QLatin1Char('/') + id);
54 }
55
addRawHeader(const QByteArray & headerName,const QByteArray & value)56 void OcsJob::addRawHeader(const QByteArray &headerName, const QByteArray &value)
57 {
58 _request.setRawHeader(headerName, value);
59 }
60
percentEncodeQueryItems(const QList<QPair<QString,QString>> & items)61 static QUrlQuery percentEncodeQueryItems(
62 const QList<QPair<QString, QString>> &items)
63 {
64 QUrlQuery result;
65 // Note: QUrlQuery::setQueryItems() does not fully percent encode
66 // the query items, see #5042
67 foreach (const auto &item, items) {
68 result.addQueryItem(
69 QUrl::toPercentEncoding(item.first),
70 QUrl::toPercentEncoding(item.second));
71 }
72 return result;
73 }
74
start()75 void OcsJob::start()
76 {
77 addRawHeader("Ocs-APIREQUEST", "true");
78 addRawHeader("Content-Type", "application/x-www-form-urlencoded");
79
80 auto *buffer = new QBuffer;
81
82 QUrlQuery queryItems;
83 if (_verb == "GET") {
84 queryItems = percentEncodeQueryItems(_params);
85 } else if (_verb == "POST" || _verb == "PUT") {
86 // Url encode the _postParams and put them in a buffer.
87 QByteArray postData;
88 Q_FOREACH (auto tmp, _params) {
89 if (!postData.isEmpty()) {
90 postData.append("&");
91 }
92 postData.append(QUrl::toPercentEncoding(tmp.first));
93 postData.append("=");
94 postData.append(QUrl::toPercentEncoding(tmp.second));
95 }
96 buffer->setData(postData);
97 }
98 queryItems.addQueryItem(QLatin1String("format"), QLatin1String("json"));
99 QUrl url = Utility::concatUrlPath(account()->url(), path(), queryItems);
100 sendRequest(_verb, url, _request, buffer);
101 AbstractNetworkJob::start();
102 }
103
finished()104 bool OcsJob::finished()
105 {
106 const QByteArray replyData = reply()->readAll();
107
108 QJsonParseError error;
109 QString message;
110 int statusCode = 0;
111 auto json = QJsonDocument::fromJson(replyData, &error);
112
113 // when it is null we might have a 304 so get status code from reply() and gives a warning...
114 if (error.error != QJsonParseError::NoError) {
115 statusCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
116 qCWarning(lcOcs) << "Could not parse reply to"
117 << _verb
118 << Utility::concatUrlPath(account()->url(), path())
119 << _params
120 << error.errorString()
121 << ":" << replyData;
122 } else {
123 statusCode = getJsonReturnCode(json, message);
124 }
125
126 //... then it checks for the statusCode
127 if (!_passStatusCodes.contains(statusCode)) {
128 qCWarning(lcOcs) << "Reply to"
129 << _verb
130 << Utility::concatUrlPath(account()->url(), path())
131 << _params
132 << "has unexpected status code:" << statusCode << replyData;
133 emit ocsError(statusCode, message);
134
135 } else {
136 // save new ETag value
137 if(reply()->rawHeaderList().contains("ETag"))
138 emit etagResponseHeaderReceived(reply()->rawHeader("ETag"), statusCode);
139
140 emit jobFinished(json, statusCode);
141 }
142 return true;
143 }
144
getJsonReturnCode(const QJsonDocument & json,QString & message)145 int OcsJob::getJsonReturnCode(const QJsonDocument &json, QString &message)
146 {
147 //TODO proper checking
148 auto meta = json.object().value("ocs").toObject().value("meta").toObject();
149 int code = meta.value("statuscode").toInt();
150 message = meta.value("message").toString();
151
152 return code;
153 }
154 }
155