File indexing completed on 2025-01-05 03:53:31
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2012-02-02 0007 * Description : a tool to export items to ImageShack web service 0008 * 0009 * SPDX-FileCopyrightText: 2012 by Dodon Victor <dodonvictor at gmail dot com> 0010 * SPDX-FileCopyrightText: 2013-2018 by Caulier Gilles <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "imageshacktalker.h" 0017 0018 // Qt includes 0019 0020 #include <QJsonDocument> 0021 #include <QJsonParseError> 0022 #include <QJsonObject> 0023 #include <QJsonValue> 0024 #include <QJsonArray> 0025 #include <QDomDocument> 0026 #include <QDomElement> 0027 #include <QDomNode> 0028 #include <QXmlStreamReader> 0029 #include <QApplication> 0030 #include <QMimeDatabase> 0031 #include <QMimeType> 0032 #include <QUrlQuery> 0033 0034 // KDE includes 0035 0036 #include <klocalizedstring.h> 0037 0038 // Local includes 0039 0040 #include "digikam_debug.h" 0041 #include "digikam_version.h" 0042 #include "imageshacksession.h" 0043 #include "imageshackmpform.h" 0044 #include "networkmanager.h" 0045 0046 using namespace Digikam; 0047 0048 namespace DigikamGenericImageShackPlugin 0049 { 0050 0051 class Q_DECL_HIDDEN ImageShackTalker::Private 0052 { 0053 public: 0054 0055 enum State 0056 { 0057 IMGHCK_AUTHENTICATING, 0058 IMGHCK_DONOTHING, 0059 IMGHCK_GETGALLERIES, 0060 IMGHCK_ADDPHOTO, 0061 IMGHCK_ADDVIDEO, 0062 IMGHCK_ADDPHOTOGALLERY 0063 }; 0064 0065 public: 0066 0067 explicit Private() 0068 { 0069 userAgent = QString::fromLatin1("digiKam-ImageShack/%1").arg(digiKamVersion()); 0070 photoApiUrl = QUrl(QLatin1String("https://api.imageshack.com/v2/images")); 0071 videoApiUrl = QUrl(QLatin1String("http://render.imageshack.us/upload_api.php")); // krazy:exclude=insecurenet 0072 loginApiUrl = QUrl(QLatin1String("https://my.imageshack.us/setlogin.php")); 0073 galleryUrl = QUrl(QLatin1String("https://www.imageshack.us/gallery_api.php")); 0074 appKey = QLatin1String("YPZ2L9WV2de2a1e08e8fbddfbcc1c5c39f94f92a"); 0075 session = nullptr; 0076 loginInProgress = false; 0077 reply = nullptr; 0078 state = IMGHCK_DONOTHING; 0079 netMngr = nullptr; 0080 } 0081 0082 public: 0083 0084 ImageShackSession* session; 0085 0086 QString userAgent; 0087 QUrl photoApiUrl; 0088 QUrl videoApiUrl; 0089 QUrl loginApiUrl; 0090 QUrl galleryUrl; 0091 QString appKey; 0092 0093 bool loginInProgress; 0094 0095 QNetworkAccessManager* netMngr; 0096 0097 QNetworkReply* reply; 0098 0099 State state; 0100 }; 0101 0102 ImageShackTalker::ImageShackTalker(ImageShackSession* const session) 0103 : d(new Private) 0104 { 0105 d->session = session; 0106 d->netMngr = NetworkManager::instance()->getNetworkManager(this); 0107 0108 connect(d->netMngr, SIGNAL(finished(QNetworkReply*)), 0109 this, SLOT(slotFinished(QNetworkReply*))); 0110 } 0111 0112 ImageShackTalker::~ImageShackTalker() 0113 { 0114 if (d->reply) 0115 { 0116 d->reply->abort(); 0117 } 0118 0119 delete d; 0120 } 0121 0122 void ImageShackTalker::cancel() 0123 { 0124 if (d->reply) 0125 { 0126 d->reply->abort(); 0127 d->reply = nullptr; 0128 } 0129 0130 Q_EMIT signalBusy(false); 0131 } 0132 0133 QString ImageShackTalker::getCallString(QMap<QString, QString>& args) const 0134 { 0135 QString result; 0136 0137 for (QMap<QString, QString>::const_iterator it = args.constBegin(); 0138 it != args.constEnd(); 0139 ++it) 0140 { 0141 if (!result.isEmpty()) 0142 { 0143 result.append(QLatin1String("&")); 0144 } 0145 0146 result.append(it.key()); 0147 result.append(QLatin1String("=")); 0148 result.append(it.value()); 0149 } 0150 0151 return result; 0152 } 0153 0154 void ImageShackTalker::slotFinished(QNetworkReply* reply) 0155 { 0156 if (reply != d->reply) 0157 { 0158 return; 0159 } 0160 0161 d->reply = nullptr; 0162 0163 if (reply->error() != QNetworkReply::NoError) 0164 { 0165 if (d->state == Private::IMGHCK_AUTHENTICATING) 0166 { 0167 checkRegistrationCodeDone(reply->error(), reply->errorString()); 0168 Q_EMIT signalBusy(false); 0169 } 0170 else if (d->state == Private::IMGHCK_GETGALLERIES) 0171 { 0172 Q_EMIT signalBusy(false); 0173 Q_EMIT signalGetGalleriesDone(reply->error(), reply->errorString()); 0174 } 0175 else if (d->state == Private::IMGHCK_ADDPHOTO || d->state == Private::IMGHCK_ADDPHOTOGALLERY) 0176 { 0177 Q_EMIT signalBusy(false); 0178 Q_EMIT signalAddPhotoDone(reply->error(), reply->errorString()); 0179 } 0180 0181 d->state = Private::IMGHCK_DONOTHING; 0182 reply->deleteLater(); 0183 return; 0184 } 0185 0186 QByteArray buffer = reply->readAll(); 0187 0188 switch (d->state) 0189 { 0190 case Private::IMGHCK_AUTHENTICATING: 0191 parseAccessToken(buffer); 0192 break; 0193 0194 case Private::IMGHCK_ADDPHOTOGALLERY: 0195 parseAddPhotoToGalleryDone(buffer); 0196 break; 0197 0198 case Private::IMGHCK_ADDVIDEO: 0199 case Private::IMGHCK_ADDPHOTO: 0200 parseUploadPhotoDone(buffer); 0201 break; 0202 0203 case Private::IMGHCK_GETGALLERIES: 0204 parseGetGalleries(buffer); 0205 break; 0206 0207 default: 0208 break; 0209 } 0210 0211 reply->deleteLater(); 0212 } 0213 0214 void ImageShackTalker::authenticate() 0215 { 0216 if (d->reply) 0217 { 0218 d->reply->abort(); 0219 d->reply = nullptr; 0220 } 0221 0222 Q_EMIT signalBusy(true); 0223 Q_EMIT signalJobInProgress(1, 4, i18n("Authenticating the user")); 0224 0225 QUrl url(QLatin1String("https://api.imageshack.com/v2/user/login")); 0226 QUrlQuery q(url); 0227 q.addQueryItem(QLatin1String("user"), d->session->email()); 0228 q.addQueryItem(QLatin1String("password"), d->session->password()); 0229 url.setQuery(q); 0230 0231 QNetworkRequest netRequest(url); 0232 netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded")); 0233 0234 d->reply = d->netMngr->post(netRequest, QByteArray()); 0235 0236 d->state = Private::IMGHCK_AUTHENTICATING; 0237 } 0238 0239 void ImageShackTalker::getGalleries() 0240 { 0241 if (d->reply) 0242 { 0243 d->reply->abort(); 0244 d->reply = nullptr; 0245 } 0246 0247 Q_EMIT signalBusy(true); 0248 Q_EMIT signalJobInProgress(3, 4, i18n("Getting galleries from server")); 0249 0250 QUrl gUrl(d->galleryUrl); 0251 0252 QUrlQuery q(gUrl); 0253 q.addQueryItem(QLatin1String("action"), QLatin1String("gallery_list")); 0254 q.addQueryItem(QLatin1String("user"), d->session->username()); 0255 gUrl.setQuery(q); 0256 0257 d->reply = d->netMngr->get(QNetworkRequest(gUrl)); 0258 0259 d->state = Private::IMGHCK_GETGALLERIES; 0260 } 0261 0262 void ImageShackTalker::checkRegistrationCodeDone(int errCode, const QString& errMsg) 0263 { 0264 Q_EMIT signalBusy(false); 0265 Q_EMIT signalLoginDone(errCode, errMsg); 0266 d->loginInProgress = false; 0267 } 0268 0269 void ImageShackTalker::parseAccessToken(const QByteArray &data) 0270 { 0271 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Data received is "<< data; 0272 0273 QJsonParseError err; 0274 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0275 0276 if (err.error != QJsonParseError::NoError) 0277 { 0278 Q_EMIT signalBusy(false); 0279 return; 0280 } 0281 0282 QJsonObject jsonObject = doc.object(); 0283 0284 if (jsonObject[QLatin1String("success")].toBool()) 0285 { 0286 d->session->setLoggedIn(true); 0287 QJsonObject obj = jsonObject[QLatin1String("result")].toObject(); 0288 d->session->setUsername(obj[QLatin1String("username")].toString()); 0289 d->session->setEmail(obj[QLatin1String("email")].toString()); 0290 d->session->setAuthToken(obj[QLatin1String("auth_token")].toString()); 0291 checkRegistrationCodeDone(0,QLatin1String("")); 0292 } 0293 else 0294 { 0295 d->session->setLoggedIn(false); 0296 QJsonObject obj = jsonObject[QLatin1String("error")].toObject(); 0297 checkRegistrationCodeDone(obj[QLatin1String("error_code")].toInt(), obj[QLatin1String("error_message")].toString()); 0298 } 0299 } 0300 0301 void ImageShackTalker::parseGetGalleries(const QByteArray &data) 0302 { 0303 QDomDocument document; 0304 0305 if (!document.setContent(data)) 0306 { 0307 return; 0308 } 0309 0310 QDomElement rootElem = document.documentElement(); 0311 QDomNodeList children = rootElem.childNodes(); 0312 0313 QStringList gTexts; 0314 QStringList gNames; 0315 0316 for (int i = 0 ; i < children.size() ; ++i) 0317 { 0318 QDomElement e = children.at(i).toElement(); 0319 0320 if (e.tagName() == QLatin1String("gallery")) 0321 { 0322 QDomElement nameElem = e.firstChildElement(QLatin1String("name")); 0323 QDomElement titleElem = e.firstChildElement(QLatin1String("title")); 0324 QDomElement serverElem = e.firstChildElement(QLatin1String("server")); 0325 0326 if (!nameElem.isNull()) 0327 { 0328 QString fmt; 0329 fmt = nameElem.firstChild().toText().data(); 0330 gNames << nameElem.firstChild().toText().data(); 0331 gTexts << titleElem.firstChild().toText().data(); 0332 } 0333 } 0334 } 0335 0336 d->state = Private::IMGHCK_DONOTHING; 0337 0338 Q_EMIT signalUpdateGalleries(gTexts, gNames); 0339 Q_EMIT signalGetGalleriesDone(0, i18n("Successfully retrieved galleries")); 0340 } 0341 0342 void ImageShackTalker::authenticationDone(int errCode, const QString& errMsg) 0343 { 0344 if (errCode) 0345 { 0346 d->session->logOut(); 0347 } 0348 0349 Q_EMIT signalBusy(false); 0350 Q_EMIT signalLoginDone(errCode, errMsg); 0351 d->loginInProgress = false; 0352 } 0353 0354 void ImageShackTalker::logOut() 0355 { 0356 d->session->logOut(); 0357 d->loginInProgress = false; 0358 } 0359 0360 void ImageShackTalker::cancelLogIn() 0361 { 0362 logOut(); 0363 Q_EMIT signalLoginDone(-1, QLatin1String("Canceled by the user!")); 0364 } 0365 0366 QString ImageShackTalker::mimeType(const QString& path) const 0367 { 0368 QMimeDatabase db; 0369 QMimeType ptr = db.mimeTypeForUrl(QUrl::fromLocalFile(path)); 0370 0371 return ptr.name(); 0372 } 0373 0374 void ImageShackTalker::uploadItem(const QString& path, const QMap<QString, QString>& opts) 0375 { 0376 if (d->reply) 0377 { 0378 d->reply->abort(); 0379 d->reply = nullptr; 0380 } 0381 0382 Q_EMIT signalBusy(true); 0383 QMap<QString, QString> args; 0384 args[QLatin1String("key")] = d->appKey; 0385 args[QLatin1String("fileupload")] = QUrl(path).fileName(); 0386 0387 ImageShackMPForm form; 0388 0389 for (QMap<QString, QString>::const_iterator it = opts.constBegin(); 0390 it != opts.constEnd(); 0391 ++it) 0392 { 0393 form.addPair(it.key(), it.value()); 0394 } 0395 0396 for (QMap<QString, QString>::const_iterator it = args.constBegin(); 0397 it != args.constEnd(); 0398 ++it) 0399 { 0400 form.addPair(it.key(), it.value()); 0401 } 0402 0403 if (!form.addFile(QUrl(path).fileName(), path)) 0404 { 0405 Q_EMIT signalBusy(false); 0406 return; 0407 } 0408 0409 form.finish(); 0410 0411 QUrl uploadUrl = QUrl(d->photoApiUrl); 0412 d->state = Private::IMGHCK_ADDPHOTO; 0413 0414 QNetworkRequest netRequest(uploadUrl); 0415 netRequest.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType()); 0416 netRequest.setHeader(QNetworkRequest::UserAgentHeader, d->userAgent); 0417 0418 d->reply = d->netMngr->post(netRequest, form.formData()); 0419 0420 //uploadItemToGallery(path, QLatin1String(""), opts); 0421 } 0422 0423 void ImageShackTalker::uploadItemToGallery(const QString& path, 0424 const QString& /*gallery*/, 0425 const QMap<QString, QString>& opts) 0426 { 0427 if (d->reply) 0428 { 0429 d->reply->abort(); 0430 d->reply = nullptr; 0431 } 0432 0433 Q_EMIT signalBusy(true); 0434 QMap<QString, QString> args; 0435 args[QLatin1String("key")] = d->appKey; 0436 args[QLatin1String("fileupload")] = QUrl(path).fileName(); 0437 0438 ImageShackMPForm form; 0439 0440 for (QMap<QString, QString>::const_iterator it = opts.constBegin(); 0441 it != opts.constEnd(); 0442 ++it) 0443 { 0444 form.addPair(it.key(), it.value()); 0445 } 0446 0447 for (QMap<QString, QString>::const_iterator it = args.constBegin(); 0448 it != args.constEnd(); 0449 ++it) 0450 { 0451 form.addPair(it.key(), it.value()); 0452 } 0453 0454 if (!form.addFile(QUrl(path).fileName(), path)) 0455 { 0456 Q_EMIT signalBusy(false); 0457 return; 0458 } 0459 0460 form.finish(); 0461 0462 // Check where to upload 0463 0464 QString mime = mimeType(path); 0465 0466 QUrl uploadUrl; 0467 0468 uploadUrl = QUrl(d->photoApiUrl); 0469 d->state = Private::IMGHCK_ADDPHOTO; 0470 0471 QNetworkRequest netRequest(uploadUrl); 0472 netRequest.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType()); 0473 netRequest.setHeader(QNetworkRequest::UserAgentHeader, d->userAgent); 0474 0475 d->reply = d->netMngr->post(netRequest, form.formData()); 0476 } 0477 0478 int ImageShackTalker::parseErrorResponse(const QDomElement& elem, QString& errMsg) const 0479 { 0480 int errCode = -1; 0481 QString err_code; 0482 0483 for (QDomNode node = elem.firstChild(); 0484 !node.isNull(); 0485 node = node.nextSibling()) 0486 { 0487 if (!node.isElement()) 0488 { 0489 continue; 0490 } 0491 0492 QDomElement e = node.toElement(); 0493 0494 if (e.tagName() == QLatin1String("error")) 0495 { 0496 err_code = e.attributeNode(QLatin1String("id")).value(); 0497 errMsg = e.text(); 0498 } 0499 } 0500 0501 if (err_code == QLatin1String("file_too_big")) 0502 { 0503 errCode = 501; 0504 } 0505 else 0506 { 0507 errCode = 502; 0508 } 0509 0510 return errCode; 0511 } 0512 0513 void ImageShackTalker::parseUploadPhotoDone(const QByteArray& data) 0514 { 0515 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "ParseUploadPhotoDone data is "<<data; 0516 0517 QJsonParseError err; 0518 QJsonDocument doc = QJsonDocument::fromJson(data, &err); 0519 0520 if (err.error != QJsonParseError::NoError) 0521 { 0522 Q_EMIT signalBusy(false); 0523 return; 0524 } 0525 0526 QJsonObject jsonObject = doc.object(); 0527 0528 if ((d->state == Private::IMGHCK_ADDPHOTO) || 0529 (d->state == Private::IMGHCK_ADDVIDEO) || 0530 (d->state == Private::IMGHCK_ADDPHOTOGALLERY)) 0531 { 0532 if (jsonObject[QLatin1String("success")].toBool()) 0533 { 0534 Q_EMIT signalBusy(false); 0535 Q_EMIT signalAddPhotoDone(0,QLatin1String("")); 0536 } 0537 else 0538 { 0539 QJsonObject obj = jsonObject[QLatin1String("error")].toObject(); 0540 Q_EMIT signalAddPhotoDone(obj[QLatin1String("error_code")].toInt(), obj[QLatin1String("error_message")].toString()); 0541 Q_EMIT signalBusy(false); 0542 } 0543 } 0544 } 0545 0546 void ImageShackTalker::parseAddPhotoToGalleryDone(const QByteArray& data) 0547 { 0548 //int errCode = -1; 0549 QString errMsg = QLatin1String(""); 0550 QDomDocument domDoc(QLatin1String("galleryXML")); 0551 0552 qCDebug(DIGIKAM_WEBSERVICES_LOG) << data; 0553 0554 if (!domDoc.setContent(data)) 0555 { 0556 return; 0557 } 0558 0559 QDomElement rootElem = domDoc.documentElement(); 0560 0561 if (rootElem.isNull() || (rootElem.tagName() != QLatin1String("gallery"))) 0562 { 0563 // TODO error checking 0564 } 0565 else 0566 { 0567 Q_EMIT signalBusy(false); 0568 Q_EMIT signalAddPhotoDone(0, QLatin1String("")); 0569 } 0570 } 0571 0572 } // namespace DigikamGenericImageShackPlugin 0573 0574 #include "moc_imageshacktalker.cpp"