File indexing completed on 2024-12-22 04:45:28

0001 /*
0002    SPDX-FileCopyrightText: 2018-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "setavatarjob.h"
0008 #include "restapimethod.h"
0009 #include "rocketchatqtrestapi_debug.h"
0010 
0011 #include <KLocalizedString>
0012 
0013 #include <QFile>
0014 #include <QHttpMultiPart>
0015 #include <QJsonDocument>
0016 #include <QJsonObject>
0017 #include <QMimeDatabase>
0018 #include <QNetworkReply>
0019 using namespace RocketChatRestApi;
0020 SetAvatarJob::SetAvatarJob(QObject *parent)
0021     : UserBaseJob(parent)
0022 {
0023 }
0024 
0025 SetAvatarJob::~SetAvatarJob() = default;
0026 
0027 bool SetAvatarJob::start()
0028 {
0029     if (!canStart()) {
0030         deleteLater();
0031         return false;
0032     }
0033     addStartRestApiInfo("SetAvatarJob::start");
0034     if (!mAvatarInfo.mAvatarUrl.isEmpty()) {
0035         submitPostRequest(json());
0036     } else {
0037         const QString fileNameAsLocalFile = mAvatarInfo.mImageUrl.toLocalFile();
0038         auto file = new QFile(fileNameAsLocalFile);
0039         if (!file->open(QIODevice::ReadOnly)) {
0040             qCWarning(ROCKETCHATQTRESTAPI_LOG) << " Impossible to open filename " << mAvatarInfo.mImageUrl;
0041             Q_EMIT failed(i18n("File not found \'%1\'", fileNameAsLocalFile));
0042             delete file;
0043             deleteLater();
0044             return false;
0045         }
0046         QMimeDatabase db;
0047         const QMimeType mimeType = db.mimeTypeForFile(fileNameAsLocalFile);
0048 
0049         auto multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
0050 
0051         QHttpPart filePart;
0052         filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(mimeType.name()));
0053         const QString filePartInfo = QStringLiteral("form-data; name=\"image\"; filename=\"%1\"").arg(mAvatarInfo.mImageUrl.fileName());
0054         filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(filePartInfo));
0055 
0056         filePart.setBodyDevice(file);
0057         file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
0058         multiPart->append(filePart);
0059 
0060         QHttpPart userPart;
0061         userPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(QLatin1String("form-data; name=\"userId\"")));
0062         userPart.setBody(userId().toUtf8());
0063         multiPart->append(userPart);
0064 
0065         mReply = networkAccessManager()->post(request(), multiPart);
0066         // connect(reply, &QNetworkReply::uploadProgress, this, &UploadFileJob::slotUploadProgress);
0067         connect(mReply.data(), &QNetworkReply::finished, this, &SetAvatarJob::slotSetAvatar);
0068         multiPart->setParent(mReply); // delete the multiPart with the reply
0069     }
0070     return true;
0071 }
0072 
0073 void SetAvatarJob::slotSetAvatar()
0074 {
0075     auto reply = mReply;
0076     if (reply) {
0077         const QJsonDocument replyJson = convertToJsonDocument(reply);
0078         onPostRequestResponse(mReply->errorString(), replyJson);
0079         reply->deleteLater();
0080     }
0081     deleteLater();
0082 }
0083 
0084 void SetAvatarJob::onPostRequestResponse(const QString &replyErrorString, const QJsonDocument &replyJson)
0085 {
0086     const QJsonObject replyObject = replyJson.object();
0087     if (replyObject[QLatin1String("success")].toBool()) {
0088         addLoggerInfo(QByteArrayLiteral("SetAvatarJob: success: ") + replyJson.toJson(QJsonDocument::Indented));
0089         Q_EMIT setAvatarDone();
0090     } else {
0091         emitFailedMessage(replyErrorString, replyObject);
0092         addLoggerWarning(QByteArrayLiteral("SetAvatarJob: Problem: ") + replyJson.toJson(QJsonDocument::Indented));
0093     }
0094 }
0095 
0096 SetAvatarJob::SetAvatarInfo SetAvatarJob::avatarInfo() const
0097 {
0098     return mAvatarInfo;
0099 }
0100 
0101 void SetAvatarJob::setAvatarInfo(const SetAvatarInfo &avatarInfo)
0102 {
0103     mAvatarInfo = avatarInfo;
0104 }
0105 
0106 QString SetAvatarJob::errorMessage(const QString &str, const QJsonObject &details)
0107 {
0108     if (str == QLatin1String("error-avatar-invalid-url")) {
0109         const QString url = details[QLatin1String("url")].toString();
0110         return i18n("Invalid avatar URL: %1", url);
0111     } else if (str == QLatin1String("error-avatar-url-handling")) {
0112         const QString url = details[QLatin1String("url")].toString();
0113         const QString username = details[QLatin1String("username")].toString();
0114         return i18n("Error while handling avatar setting from a URL \"%1\" for %2", url, username);
0115     }
0116 
0117     return RestApiAbstractJob::errorMessage(str, details);
0118 }
0119 
0120 bool SetAvatarJob::requireHttpAuthentication() const
0121 {
0122     return true;
0123 }
0124 
0125 bool SetAvatarJob::canStart() const
0126 {
0127     if (!hasUserIdentifier()) {
0128         qCWarning(ROCKETCHATQTRESTAPI_LOG) << "SetAvatarJob: identifier is empty";
0129         return false;
0130     }
0131     if (!RestApiAbstractJob::canStart()) {
0132         return false;
0133     }
0134     if (!mAvatarInfo.isValid()) {
0135         qCWarning(ROCKETCHATQTRESTAPI_LOG) << "SetAvatarJob: mAvatarInfo is not valid";
0136         return false;
0137     }
0138     return true;
0139 }
0140 
0141 QNetworkRequest SetAvatarJob::request() const
0142 {
0143     const QUrl url = mRestApiMethod->generateUrl(RestApiUtil::RestApiUrlType::UsersSetAvatar);
0144     QNetworkRequest request(url);
0145     addAuthRawHeader(request);
0146     addRequestAttribute(request, !mAvatarInfo.mAvatarUrl.isEmpty()); // Don't show "json" when we send image
0147     return request;
0148 }
0149 
0150 QJsonDocument SetAvatarJob::json() const
0151 {
0152     QJsonObject jsonObj;
0153     jsonObj[QLatin1String("avatarUrl")] = mAvatarInfo.mAvatarUrl;
0154     generateJson(jsonObj);
0155     const QJsonDocument postData = QJsonDocument(jsonObj);
0156     return postData;
0157 }
0158 
0159 bool SetAvatarJob::SetAvatarInfo::isValid() const
0160 {
0161     return !mAvatarUrl.isEmpty() || !mImageUrl.isEmpty();
0162 }
0163 
0164 #include "moc_setavatarjob.cpp"