File indexing completed on 2025-01-19 03:52:58

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2005-07-07
0007  * Description : a tool to export images to Flickr web service
0008  *
0009  * SPDX-FileCopyrightText: 2005-2009 by Vardhman Jain <vardhman at gmail dot com>
0010  * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2017-2019 by Maik Qualmann <metzpinguin at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "flickrtalker.h"
0018 
0019 // Qt includes
0020 
0021 #include <QDomDocument>
0022 #include <QDomElement>
0023 #include <QByteArray>
0024 #include <QFileInfo>
0025 #include <QFile>
0026 #include <QMap>
0027 #include <QImage>
0028 #include <QPointer>
0029 #include <QUrlQuery>
0030 #include <QStringList>
0031 #include <QMessageBox>
0032 #include <QInputDialog>
0033 #include <QApplication>
0034 #include <QProgressDialog>
0035 #include <QDesktopServices>
0036 
0037 // KDE includes
0038 
0039 #include <klocalizedstring.h>
0040 
0041 // Local includes
0042 
0043 #include "digikam_debug.h"
0044 #include "digikam_config.h"
0045 #include "digikam_version.h"
0046 #include "previewloadthread.h"
0047 #include "webbrowserdlg.h"
0048 #include "flickrwindow.h"
0049 #include "flickrmpform.h"
0050 #include "wstoolutils.h"
0051 #include "dmetadata.h"
0052 
0053 // OAuth2 library includes
0054 
0055 #if defined(Q_CC_CLANG)
0056 #   pragma clang diagnostic push
0057 #   pragma clang diagnostic ignored "-Wextra-semi"
0058 #endif
0059 
0060 #include "o1.h"
0061 #include "o0globals.h"
0062 #include "o1requestor.h"
0063 #include "o0settingsstore.h"
0064 #include "networkmanager.h"
0065 
0066 #if defined(Q_CC_CLANG)
0067 #   pragma clang diagnostic pop
0068 #endif
0069 
0070 namespace DigikamGenericFlickrPlugin
0071 {
0072 
0073 class Q_DECL_HIDDEN FlickrTalker::Private
0074 {
0075 public:
0076 
0077     explicit Private()
0078       : parent      (nullptr),
0079         netMngr     (nullptr),
0080         reply       (nullptr),
0081         settings    (nullptr),
0082         state       (FE_LOGOUT),
0083         iface       (nullptr),
0084         o1          (nullptr),
0085         store       (nullptr),
0086         requestor   (nullptr),
0087         browser     (nullptr)
0088     {
0089         apiUrl      = QLatin1String("https://www.flickr.com/services/rest/");
0090         authUrl     = QLatin1String("https://www.flickr.com/services/oauth/authorize?perms=write");
0091         tokenUrl    = QLatin1String("https://www.flickr.com/services/oauth/request_token");
0092         accessUrl   = QLatin1String("https://www.flickr.com/services/oauth/access_token");
0093         uploadUrl   = QLatin1String("https://up.flickr.com/services/upload/");
0094         callbackUrl = QLatin1String("https://www.flickr.com");
0095 
0096         apikey      = QLatin1String("74f882bf4dabe22baaaace1f6d33c66b");
0097         secret      = QLatin1String("537d58e3ead2d6d5");
0098     }
0099 
0100     QWidget*               parent;
0101 
0102     QString                serviceName;
0103     QString                apiUrl;
0104     QString                authUrl;
0105     QString                tokenUrl;
0106     QString                accessUrl;
0107     QString                uploadUrl;
0108     QString                callbackUrl;
0109     QString                apikey;
0110     QString                secret;
0111     QString                maxSize;
0112     QString                username;
0113     QString                userId;
0114     QString                lastTmpFile;
0115 
0116     QNetworkAccessManager* netMngr;
0117     QNetworkReply*         reply;
0118 
0119     QSettings*             settings;
0120 
0121     State                  state;
0122 
0123     DInfoInterface*        iface;
0124 
0125     O1*                    o1;
0126     O0SettingsStore*       store;
0127     O1Requestor*           requestor;
0128     WebBrowserDlg*         browser;
0129 };
0130 
0131 FlickrTalker::FlickrTalker(QWidget* const parent,
0132                            const QString& serviceName,
0133                            DInfoInterface* const iface)
0134     : d(new Private)
0135 {
0136     d->parent          = parent;
0137     d->serviceName     = serviceName;
0138     d->iface           = iface;
0139     m_photoSetsList    = nullptr;
0140     m_authProgressDlg  = nullptr;
0141 
0142     d->netMngr         = NetworkManager::instance()->getNetworkManager(this);
0143 
0144     connect(d->netMngr, SIGNAL(finished(QNetworkReply*)),
0145             this, SLOT(slotFinished(QNetworkReply*)));
0146 
0147     // Initialize selected photo set as empty.
0148 
0149     m_selectedPhotoSet = FPhotoSet();
0150 
0151     // Initialize photo sets list.
0152 
0153     m_photoSetsList    = new QList<FPhotoSet>();
0154 
0155     d->o1              = new O1(this);
0156     d->o1->setLocalPort(80);
0157     d->o1->setClientId(d->apikey);
0158     d->o1->setClientSecret(d->secret);
0159     d->o1->setCallbackUrl(d->callbackUrl);
0160     d->o1->setAuthorizeUrl(QUrl(d->authUrl));
0161     d->o1->setAccessTokenUrl(QUrl(d->accessUrl));
0162     d->o1->setRequestTokenUrl(QUrl(d->tokenUrl));
0163     d->o1->setUseExternalWebInterceptor(true);
0164 
0165     d->settings = WSToolUtils::getOauthSettings(this);
0166     d->store    = new O0SettingsStore(d->settings, QLatin1String(O2_ENCRYPTION_KEY), this);
0167     d->store->setGroupKey(d->serviceName);
0168     d->o1->setStore(d->store);
0169 
0170     connect(d->o1, SIGNAL(linkingFailed()),
0171             this, SLOT(slotLinkingFailed()));
0172 
0173     connect(d->o1, SIGNAL(linkingSucceeded()),
0174             this, SLOT(slotLinkingSucceeded()));
0175 
0176     connect(d->o1, SIGNAL(openBrowser(QUrl)),
0177             this, SLOT(slotOpenBrowser(QUrl)));
0178 
0179     d->requestor = new O1Requestor(d->netMngr, d->o1, this);
0180 }
0181 
0182 FlickrTalker::~FlickrTalker()
0183 {
0184     if (d->reply)
0185     {
0186         d->reply->abort();
0187     }
0188 
0189     WSToolUtils::removeTemporaryDir(d->serviceName.toLatin1().constData());
0190 
0191     delete m_photoSetsList;
0192     delete d;
0193 }
0194 
0195 void FlickrTalker::link(const QString& userName)
0196 {
0197     Q_EMIT signalBusy(true);
0198 
0199     if (userName.isEmpty())
0200     {
0201         d->store->setGroupKey(d->serviceName);
0202     }
0203     else
0204     {
0205         d->store->setGroupKey(d->serviceName + userName);
0206     }
0207 
0208     d->o1->link();
0209 }
0210 
0211 void FlickrTalker::unLink()
0212 {
0213     d->o1->unlink();
0214 }
0215 
0216 void FlickrTalker::removeUserName(const QString& userName)
0217 {
0218     if (userName.startsWith(d->serviceName))
0219     {
0220         d->settings->beginGroup(userName);
0221         d->settings->remove(QString());
0222         d->settings->endGroup();
0223     }
0224 }
0225 
0226 void FlickrTalker::slotCatchUrl(const QUrl& url)
0227 {
0228     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received URL from webview:" << url;
0229 
0230     QString   str = url.toString();
0231     QUrlQuery query(str.section(QLatin1Char('?'), -1, -1));
0232 
0233     if (query.hasQueryItem(QLatin1String("oauth_token")))
0234     {
0235         QMap<QString, QString> queryParams;
0236         queryParams.insert(QLatin1String("oauth_token"),
0237                                          query.queryItemValue(QLatin1String("oauth_token")));
0238         queryParams.insert(QLatin1String("oauth_verifier"),
0239                                          query.queryItemValue(QLatin1String("oauth_verifier")));
0240 
0241         d->o1->onVerificationReceived(queryParams);
0242     }
0243 }
0244 
0245 void FlickrTalker::slotLinkingFailed()
0246 {
0247     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Flickr fail";
0248     d->username = QString();
0249     Q_EMIT signalBusy(false);
0250 }
0251 
0252 void FlickrTalker::slotLinkingSucceeded()
0253 {
0254     if (!d->o1->linked())
0255     {
0256         qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Flickr ok";
0257         d->username = QString();
0258         return;
0259     }
0260 
0261     if (d->browser)
0262     {
0263         d->browser->close();
0264     }
0265 
0266     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Flickr ok";
0267 
0268     d->username = d->o1->extraTokens()[QLatin1String("username")].toString();
0269     d->userId   = d->o1->extraTokens()[QLatin1String("user_nsid")].toString();
0270 
0271     if (d->store->groupKey() == d->serviceName)
0272     {
0273         d->settings->beginGroup(d->serviceName);
0274         QStringList keys = d->settings->allKeys();
0275         d->settings->endGroup();
0276 
0277         Q_FOREACH (const QString& key, keys)
0278         {
0279             d->settings->beginGroup(d->serviceName);
0280             QVariant value = d->settings->value(key);
0281             d->settings->endGroup();
0282             d->settings->beginGroup(d->serviceName + d->username);
0283             d->settings->setValue(key, value);
0284             d->settings->endGroup();
0285         }
0286 
0287         d->store->setGroupKey(d->serviceName + d->username);
0288         removeUserName(d->serviceName);
0289     }
0290 
0291     Q_EMIT signalLinkingSucceeded();
0292 }
0293 
0294 void FlickrTalker::slotOpenBrowser(const QUrl& url)
0295 {
0296     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Open Browser... (" << url << ")";
0297 
0298 #ifdef HAVE_QWEBENGINE
0299 
0300     delete d->browser;
0301     d->browser = new WebBrowserDlg(url, d->parent, true);
0302     d->browser->setModal(true);
0303 
0304     connect(d->browser, SIGNAL(urlChanged(QUrl)),
0305             this, SLOT(slotCatchUrl(QUrl)));
0306 
0307     connect(d->browser, SIGNAL(closeView(bool)),
0308             this, SIGNAL(signalBusy(bool)));
0309 
0310     d->browser->show();
0311 
0312 #else
0313 
0314     QDesktopServices::openUrl(url);
0315     QPointer<QInputDialog> textDlg = new QInputDialog(d->parent);
0316 
0317     textDlg->setWindowTitle(i18nc("@title:window", "Enter Flickr Login URL"));
0318     textDlg->setLabelText(i18n("Copy the full URL from the external browser "
0319                                "that contains the \"oauth_token\" entry:"));
0320     textDlg->resize(770, textDlg->sizeHint().height());
0321     textDlg->setInputMode(QInputDialog::TextInput);
0322     textDlg->setTextEchoMode(QLineEdit::Normal);
0323     textDlg->setModal(true);
0324 
0325     if (textDlg->exec() != QDialog::Accepted)
0326     {
0327         Q_EMIT signalBusy(false);
0328         delete textDlg;
0329 
0330         return;
0331     }
0332 
0333     slotCatchUrl(QUrl(textDlg->textValue().trimmed()));
0334     delete textDlg;
0335 
0336 #endif
0337 
0338 }
0339 
0340 QString FlickrTalker::getMaxAllowedFileSize()
0341 {
0342     return d->maxSize;
0343 }
0344 
0345 void FlickrTalker::maxAllowedFileSize()
0346 {
0347     if (d->reply)
0348     {
0349         d->reply->abort();
0350         d->reply = nullptr;
0351     }
0352 
0353     if (!d->o1->linked())
0354     {
0355         return;
0356     }
0357 
0358     QUrl url(d->apiUrl);
0359     QNetworkRequest netRequest(url);
0360     QList<O0RequestParameter> reqParams = QList<O0RequestParameter>();
0361 
0362     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
0363 
0364     reqParams << O0RequestParameter("method", "flickr.people.getLimits");
0365 
0366     QByteArray postData = O1::createQueryParameters(reqParams);
0367 
0368     d->reply = d->requestor->post(netRequest, reqParams, postData);
0369 
0370     d->state = FE_GETMAXSIZE;
0371     m_authProgressDlg->setLabelText(i18n("Getting the maximum allowed file size."));
0372     m_authProgressDlg->setMaximum(4);
0373     m_authProgressDlg->setValue(1);
0374 
0375     Q_EMIT signalBusy(true);
0376 }
0377 
0378 void FlickrTalker::listPhotoSets()
0379 {
0380     if (d->reply)
0381     {
0382         d->reply->abort();
0383         d->reply = nullptr;
0384     }
0385 
0386     if (!d->o1->linked())
0387     {
0388         return;
0389     }
0390 
0391     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "List photoset invoked";
0392 
0393     QUrl url(d->apiUrl);
0394     QNetworkRequest netRequest(url);
0395     QList<O0RequestParameter> reqParams = QList<O0RequestParameter>();
0396 
0397     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
0398 
0399     reqParams << O0RequestParameter("method", "flickr.photosets.getList");
0400 
0401     QByteArray postData = O1::createQueryParameters(reqParams);
0402 
0403     d->reply = d->requestor->post(netRequest, reqParams, postData);
0404 
0405     d->state = FE_LISTPHOTOSETS;
0406     Q_EMIT signalBusy(true);
0407 }
0408 
0409 void FlickrTalker::getPhotoProperty(const QString& method, const QStringList& argList)
0410 {
0411     if (d->reply)
0412     {
0413         d->reply->abort();
0414         d->reply = nullptr;
0415     }
0416 
0417     if (!d->o1->linked())
0418     {
0419         return;
0420     }
0421 
0422     QUrl url(d->apiUrl);
0423     QNetworkRequest netRequest(url);
0424     QList<O0RequestParameter> reqParams = QList<O0RequestParameter>();
0425 
0426     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
0427 
0428     reqParams << O0RequestParameter("method", method.toLatin1());
0429 
0430     for (QStringList::const_iterator it = argList.constBegin(); it != argList.constEnd(); ++it)
0431     {
0432         QStringList str = (*it).split(QLatin1Char('='), QT_SKIP_EMPTY_PARTS);
0433         reqParams << O0RequestParameter(str[0].toLatin1(), str[1].toLatin1());
0434     }
0435 
0436     QByteArray postData = O1::createQueryParameters(reqParams);
0437 
0438     d->reply = d->requestor->post(netRequest, reqParams, postData);
0439 
0440     d->state = FE_GETPHOTOPROPERTY;
0441     Q_EMIT signalBusy(true);
0442 }
0443 
0444 void FlickrTalker::listPhotos(const QString& /*albumName*/)
0445 {
0446     // TODO
0447 }
0448 
0449 void FlickrTalker::createPhotoSet(const QString& /*albumName*/, const QString& albumTitle,
0450                                   const QString& albumDescription, const QString& primaryPhotoId)
0451 {
0452     if (d->reply)
0453     {
0454         d->reply->abort();
0455         d->reply = nullptr;
0456     }
0457 
0458     if (!d->o1->linked())
0459     {
0460         return;
0461     }
0462 
0463     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Create photoset invoked";
0464 
0465     QUrl url(d->apiUrl);
0466     QNetworkRequest netRequest(url);
0467     QList<O0RequestParameter> reqParams = QList<O0RequestParameter>();
0468 
0469     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
0470 
0471     reqParams << O0RequestParameter("method", "flickr.photosets.create");
0472     reqParams << O0RequestParameter("title", albumTitle.toLatin1());
0473     reqParams << O0RequestParameter("description", albumDescription.toLatin1());
0474     reqParams << O0RequestParameter("primary_photo_id", primaryPhotoId.toLatin1());
0475 
0476     QByteArray postData = O1::createQueryParameters(reqParams);
0477 
0478     d->reply = d->requestor->post(netRequest, reqParams, postData);
0479 
0480     d->state = FE_CREATEPHOTOSET;
0481     Q_EMIT signalBusy(true);
0482 }
0483 
0484 void FlickrTalker::addPhotoToPhotoSet(const QString& photoId,
0485                                       const QString& photoSetId)
0486 {
0487     if (d->reply)
0488     {
0489         d->reply->abort();
0490         d->reply = nullptr;
0491     }
0492 
0493     if (!d->o1->linked())
0494     {
0495         return;
0496     }
0497 
0498     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "AddPhotoToPhotoSet invoked";
0499 
0500     /*
0501      * If the photoset id starts with the special string "UNDEFINED_", it means
0502      * it doesn't exist yet on Flickr and needs to be created. Note that it's
0503      * not necessary to subsequently add the photo to the photo set, as this
0504      * is done in the set creation call to Flickr.
0505      */
0506 
0507     if (photoSetId.startsWith(QLatin1String("UNDEFINED_")))
0508     {
0509         createPhotoSet(QLatin1String(""), m_selectedPhotoSet.title, m_selectedPhotoSet.description, photoId);
0510     }
0511     else
0512     {
0513         QUrl url(d->apiUrl);
0514         QNetworkRequest netRequest(url);
0515         QList<O0RequestParameter> reqParams = QList<O0RequestParameter>();
0516 
0517         netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
0518 
0519         reqParams << O0RequestParameter("method", "flickr.photosets.addPhoto");
0520         reqParams << O0RequestParameter("photoset_id", photoSetId.toLatin1());
0521         reqParams << O0RequestParameter("photo_id", photoId.toLatin1());
0522 
0523         QByteArray postData = O1::createQueryParameters(reqParams);
0524 
0525         d->reply = d->requestor->post(netRequest, reqParams, postData);
0526 
0527         d->state = FE_ADDPHOTOTOPHOTOSET;
0528         Q_EMIT signalBusy(true);
0529     }
0530 }
0531 
0532 bool FlickrTalker::addPhoto(const QString& photoPath, const FPhotoInfo& info,
0533                             bool original, bool rescale, int maxDim, int imageQuality)
0534 {
0535     if (d->reply)
0536     {
0537         d->reply->abort();
0538         d->reply = nullptr;
0539     }
0540 
0541     if (!d->o1->linked())
0542     {
0543         return false;
0544     }
0545 
0546     Q_EMIT signalBusy(true);
0547 
0548     QUrl url(d->uploadUrl);
0549     QNetworkRequest netRequest(url);
0550     QList<O0RequestParameter> reqParams = QList<O0RequestParameter>();
0551 
0552     QString path = photoPath;
0553     FlickrMPForm  form;
0554 
0555     QString ispublic = (info.is_public == 1) ? QLatin1String("1") : QLatin1String("0");
0556     form.addPair(QLatin1String("is_public"), ispublic, QLatin1String("text/plain"));
0557     reqParams << O0RequestParameter("is_public", ispublic.toLatin1());
0558 
0559     QString isfamily = (info.is_family == 1) ? QLatin1String("1") : QLatin1String("0");
0560     form.addPair(QLatin1String("is_family"), isfamily, QLatin1String("text/plain"));
0561     reqParams << O0RequestParameter("is_family", isfamily.toLatin1());
0562 
0563     QString isfriend = (info.is_friend == 1) ? QLatin1String("1") : QLatin1String("0");
0564     form.addPair(QLatin1String("is_friend"), isfriend, QLatin1String("text/plain"));
0565     reqParams << O0RequestParameter("is_friend", isfriend.toLatin1());
0566 
0567     QString safetyLevel = QString::number(static_cast<int>(info.safety_level));
0568     form.addPair(QLatin1String("safety_level"), safetyLevel, QLatin1String("text/plain"));
0569     reqParams << O0RequestParameter("safety_level", safetyLevel.toLatin1());
0570 
0571     QString contentType = QString::number(static_cast<int>(info.content_type));
0572     form.addPair(QLatin1String("content_type"), contentType, QLatin1String("text/plain"));
0573     reqParams << O0RequestParameter("content_type", contentType.toLatin1());
0574 
0575     QString tags = QLatin1Char('"') + info.tags.join(QLatin1String("\" \"")) + QLatin1Char('"');
0576 
0577     if (tags.length() > 0)
0578     {
0579         form.addPair(QLatin1String("tags"), tags, QLatin1String("text/plain"));
0580         reqParams << O0RequestParameter("tags", tags.toUtf8());
0581     }
0582 
0583     if (!info.title.isEmpty())
0584     {
0585         form.addPair(QLatin1String("title"), info.title, QLatin1String("text/plain"));
0586         reqParams << O0RequestParameter("title", info.title.toUtf8());
0587     }
0588 
0589     if (!info.description.isEmpty())
0590     {
0591         form.addPair(QLatin1String("description"), info.description, QLatin1String("text/plain"));
0592         reqParams << O0RequestParameter("description", info.description.toUtf8());
0593     }
0594 
0595     if (!original)
0596     {
0597         QImage image = PreviewLoadThread::loadHighQualitySynchronously(photoPath).copyQImage();
0598 
0599         if (image.isNull())
0600         {
0601             image.load(photoPath);
0602         }
0603 
0604         if (!image.isNull())
0605         {
0606             if (!d->lastTmpFile.isEmpty())
0607             {
0608                 QFile::remove(d->lastTmpFile);
0609             }
0610 
0611             path = WSToolUtils::makeTemporaryDir(d->serviceName.toLatin1().constData()).filePath(QFileInfo(photoPath)
0612                                                                           .baseName().trimmed() + QLatin1String(".jpg"));
0613 
0614             if (rescale && (image.width() > maxDim || image.height() > maxDim))
0615             {
0616                 image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation);
0617             }
0618 
0619             image.save(path, "JPEG", imageQuality);
0620             d->lastTmpFile = path;
0621 
0622             // Restore all metadata.
0623 
0624             QScopedPointer<DMetadata> meta(new DMetadata);
0625 
0626             if (meta->load(photoPath))
0627             {
0628                 meta->setItemDimensions(image.size());
0629                 meta->setItemOrientation(MetaEngine::ORIENTATION_NORMAL);
0630 
0631                 // NOTE: see bug #153207: Flickr use IPTC keywords to create Tags in web interface
0632                 //       As IPTC do not support UTF-8, we need to remove it.
0633                 //       This function call remove all Application2 Tags.
0634 
0635                 meta->removeIptcTags(QStringList() << QLatin1String("Application2"));
0636 
0637                 // NOTE: see bug # 384260: Flickr use Xmp.dc.subject to create Tags
0638                 //       in web interface, we need to remove it.
0639                 //       This function call remove all Dublin Core Tags.
0640 
0641                 meta->removeXmpTags(QStringList() << QLatin1String("dc"));
0642 
0643                 meta->setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY);
0644                 meta->save(path, true);
0645             }
0646             else
0647             {
0648                 qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Flickr::Image do not have metadata";
0649             }
0650 
0651             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Resizing and saving to temp file: " << path;
0652         }
0653     }
0654 
0655     QFileInfo tempFileInfo(path);
0656 
0657     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "QUrl path is " << QUrl::fromLocalFile(path) << "Image size (in bytes) is "<< tempFileInfo.size();
0658 
0659     if (tempFileInfo.size() > (getMaxAllowedFileSize().toLongLong()))
0660     {
0661         Q_EMIT signalAddPhotoFailed(i18n("File Size exceeds maximum allowed file size."));
0662         Q_EMIT signalBusy(false);
0663 
0664         return false;
0665     }
0666 
0667     if (!form.addFile(QLatin1String("photo"), path))
0668     {
0669         Q_EMIT signalBusy(false);
0670 
0671         return false;
0672     }
0673 
0674     form.finish();
0675 
0676     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType());
0677 
0678     d->reply = d->requestor->post(netRequest, reqParams, form.formData());
0679     d->state = FE_ADDPHOTO;
0680 
0681     return true;
0682 }
0683 
0684 void FlickrTalker::setGeoLocation(const QString& photoId, const QString& lat, const QString& lon)
0685 {
0686     if (d->reply)
0687     {
0688         d->reply->abort();
0689         d->reply = nullptr;
0690     }
0691 
0692     if (!d->o1->linked())
0693     {
0694         return;
0695     }
0696 
0697     QUrl url(d->apiUrl);
0698     QNetworkRequest netRequest(url);
0699     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM));
0700 
0701     QList<O0RequestParameter> reqParams = QList<O0RequestParameter>();
0702 
0703     reqParams << O0RequestParameter("method", "flickr.photos.geo.setLocation");
0704     reqParams << O0RequestParameter("photo_id", photoId.toLatin1());
0705     reqParams << O0RequestParameter("lat", lat.toLatin1());
0706     reqParams << O0RequestParameter("lon", lon.toLatin1());
0707 
0708     QByteArray postData = O1::createQueryParameters(reqParams);
0709     d->reply            = d->requestor->post(netRequest, reqParams, postData);
0710     d->state            = FE_SETGEO;
0711 
0712     Q_EMIT signalBusy(true);
0713 }
0714 
0715 QString FlickrTalker::getUserName() const
0716 {
0717     return d->username;
0718 }
0719 
0720 QString FlickrTalker::getUserId() const
0721 {
0722     return d->userId;
0723 }
0724 
0725 void FlickrTalker::cancel()
0726 {
0727     if (d->reply)
0728     {
0729         d->reply->abort();
0730         d->reply = nullptr;
0731     }
0732 
0733     if (m_authProgressDlg && !m_authProgressDlg->isHidden())
0734     {
0735         m_authProgressDlg->hide();
0736     }
0737 }
0738 
0739 void FlickrTalker::slotError(const QString& error)
0740 {
0741     QString transError;
0742     int errorNo = error.toInt();
0743 
0744     switch (errorNo)
0745     {
0746         case 2:
0747             transError = i18n("No photo specified");
0748             break;
0749 
0750         case 3:
0751             transError = i18n("General upload failure");
0752             break;
0753 
0754         case 4:
0755             transError = i18n("Filesize was zero");
0756             break;
0757 
0758         case 5:
0759             transError = i18n("Filetype was not recognized");
0760             break;
0761 
0762         case 6:
0763             transError = i18n("User exceeded upload limit");
0764             break;
0765 
0766         case 96:
0767             transError = i18n("Invalid signature");
0768             break;
0769 
0770         case 97:
0771             transError = i18n("Missing signature");
0772             break;
0773 
0774         case 98:
0775             transError = i18n("Login Failed / Invalid auth token");
0776             break;
0777 
0778         case 100:
0779             transError = i18n("Invalid API Key");
0780             break;
0781 
0782         case 105:
0783             transError = i18n("Service currently unavailable");
0784             break;
0785 
0786         case 108:
0787             transError = i18n("Invalid Frob");
0788             break;
0789 
0790         case 111:
0791             transError = i18n("Format \"xxx\" not found");
0792             break;
0793 
0794         case 112:
0795             transError = i18n("Method \"xxx\" not found");
0796             break;
0797 
0798         case 114:
0799             transError = i18n("Invalid SOAP envelope");
0800             break;
0801 
0802         case 115:
0803             transError = i18n("Invalid XML-RPC Method Call");
0804             break;
0805 
0806         case 116:
0807             transError = i18n("The POST method is now required for all setters");
0808             break;
0809 
0810         default:
0811             transError = i18n("Unknown error");
0812             break;
0813     };
0814 
0815     QMessageBox::critical(QApplication::activeWindow(),
0816                           i18nc("@title:window", "Error"),
0817                           i18n("Error Occurred: %1\nCannot proceed any further.", transError));
0818 }
0819 
0820 void FlickrTalker::slotFinished(QNetworkReply* reply)
0821 {
0822     Q_EMIT signalBusy(false);
0823 
0824     if (reply != d->reply)
0825     {
0826         return;
0827     }
0828 
0829     d->reply = nullptr;
0830 
0831     if (reply->error() != QNetworkReply::NoError)
0832     {
0833         if (d->state == FE_ADDPHOTO)
0834         {
0835             Q_EMIT signalAddPhotoFailed(reply->errorString());
0836         }
0837         else
0838         {
0839             QMessageBox::critical(QApplication::activeWindow(),
0840                                   i18nc("@title:window", "Error"), reply->errorString());
0841         }
0842 
0843         reply->deleteLater();
0844 
0845         return;
0846     }
0847 
0848     QByteArray buffer = reply->readAll();
0849 
0850     switch (d->state)
0851     {
0852         case (FE_LOGIN):
0853 /*
0854             parseResponseLogin(buffer);
0855 */
0856             break;
0857 
0858         case (FE_LISTPHOTOSETS):
0859             parseResponseListPhotoSets(buffer);
0860             break;
0861 
0862         case (FE_LISTPHOTOS):
0863             parseResponseListPhotos(buffer);
0864             break;
0865 
0866         case (FE_GETPHOTOPROPERTY):
0867             parseResponsePhotoProperty(buffer);
0868             break;
0869 
0870         case (FE_ADDPHOTO):
0871             parseResponseAddPhoto(buffer);
0872             break;
0873 
0874         case (FE_ADDPHOTOTOPHOTOSET):
0875             parseResponseAddPhotoToPhotoSet(buffer);
0876             break;
0877 
0878         case (FE_CREATEPHOTOSET):
0879             parseResponseCreatePhotoSet(buffer);
0880             break;
0881 
0882         case (FE_GETMAXSIZE):
0883             parseResponseMaxSize(buffer);
0884             break;
0885 
0886         case (FE_SETGEO):
0887             parseResponseSetGeoLocation(buffer);
0888             break;
0889 
0890         default:  // FR_LOGOUT
0891             break;
0892     }
0893 
0894     reply->deleteLater();
0895 }
0896 
0897 void FlickrTalker::parseResponseMaxSize(const QByteArray& data)
0898 {
0899     QString errorString;
0900     QDomDocument doc(QLatin1String("mydocument"));
0901 
0902     if (!doc.setContent(data))
0903     {
0904         return;
0905     }
0906 
0907     QDomElement docElem = doc.documentElement();
0908     QDomNode node       = docElem.firstChild();
0909 
0910     QDomElement e;
0911 
0912     while (!node.isNull())
0913     {
0914         if (node.isElement() && (node.nodeName() == QLatin1String("person")))
0915         {
0916             e                = node.toElement();
0917             QDomNode details = e.firstChild();
0918 
0919             while (!details.isNull())
0920             {
0921                 if (details.isElement())
0922                 {
0923                     e = details.toElement();
0924 
0925                     if (details.nodeName() == QLatin1String("videos"))
0926                     {
0927                         QDomAttr a = e.attributeNode(QLatin1String("maxupload"));
0928                         d->maxSize = a.value();
0929                         qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Max upload size is"<<d->maxSize;
0930                     }
0931                 }
0932 
0933                 details = details.nextSibling();
0934             }
0935         }
0936 
0937         if (node.isElement() && (node.nodeName() == QLatin1String("err")))
0938         {
0939             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
0940             errorString = node.toElement().attribute(QLatin1String("code"));
0941             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << errorString;
0942             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg"));
0943         }
0944 
0945         node = node.nextSibling();
0946     }
0947 
0948     m_authProgressDlg->hide();
0949 }
0950 
0951 void FlickrTalker::parseResponseCreatePhotoSet(const QByteArray& data)
0952 {
0953     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Parse response create photoset received " << data;
0954 
0955     //bool success = false;
0956 
0957     QDomDocument doc(QLatin1String("getListPhotoSets"));
0958 
0959     if (!doc.setContent(data))
0960     {
0961         return;
0962     }
0963 
0964     QDomElement docElem = doc.documentElement();
0965     QDomNode    node    = docElem.firstChild();
0966     QDomElement e;
0967 
0968     while (!node.isNull())
0969     {
0970         if (node.isElement() && (node.nodeName() == QLatin1String("photoset")))
0971         {
0972             // Parse the id from the response.
0973 
0974             QString new_id = node.toElement().attribute(QLatin1String("id"));
0975 
0976             // Set the new id in the photo sets list.
0977 
0978             QList<FPhotoSet>::iterator it = m_photoSetsList->begin();
0979 
0980             while (it != m_photoSetsList->end())
0981             {
0982                 if (it->id == m_selectedPhotoSet.id)
0983                 {
0984                     it->id = new_id;
0985                     break;
0986                 }
0987 
0988                 ++it;
0989             }
0990 
0991             // Set the new id in the selected photo set.
0992 
0993             m_selectedPhotoSet.id = new_id;
0994 
0995             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "PhotoSet created successfully with id" << new_id;
0996             Q_EMIT signalAddPhotoSetSucceeded();
0997         }
0998 
0999         if (node.isElement() && (node.nodeName() == QLatin1String("err")))
1000         {
1001             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
1002             QString code = node.toElement().attribute(QLatin1String("code"));
1003             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code;
1004             QString msg  = node.toElement().attribute(QLatin1String("msg"));
1005             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << msg;
1006             QMessageBox::critical(QApplication::activeWindow(), i18nc("@title:window", "Error"), i18n("PhotoSet creation failed: ") + msg);
1007         }
1008 
1009         node = node.nextSibling();
1010     }
1011 }
1012 
1013 void FlickrTalker::parseResponseListPhotoSets(const QByteArray& data)
1014 {
1015     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseListPhotosets" << data;
1016     bool success = false;
1017     QDomDocument doc(QLatin1String("getListPhotoSets"));
1018 
1019     if (!doc.setContent(data))
1020     {
1021         return;
1022     }
1023 
1024     QDomElement docElem = doc.documentElement();
1025     QDomNode    node    = docElem.firstChild();
1026     QDomElement e;
1027 
1028     QString photoSet_id, photoSet_title, photoSet_description;
1029     m_photoSetsList->clear();
1030 
1031     while (!node.isNull())
1032     {
1033         if (node.isElement() && (node.nodeName() == QLatin1String("photosets")))
1034         {
1035             e                    = node.toElement();
1036             QDomNode details     = e.firstChild();
1037             FPhotoSet fps;
1038             QDomNode detailsNode = details;
1039 
1040             while (!detailsNode.isNull())
1041             {
1042                 if (detailsNode.isElement())
1043                 {
1044                     e = detailsNode.toElement();
1045 
1046                     if (detailsNode.nodeName() == QLatin1String("photoset"))
1047                     {
1048                         qCDebug(DIGIKAM_WEBSERVICES_LOG) << "id=" << e.attribute(QLatin1String("id"));
1049                         photoSet_id              = e.attribute(QLatin1String("id"));     // this is what is obtained from data.
1050                         fps.id                   = photoSet_id;
1051                         QDomNode photoSetDetails = detailsNode.firstChild();
1052                         QDomElement e_detail;
1053 
1054                         while (!photoSetDetails.isNull())
1055                         {
1056                             e_detail = photoSetDetails.toElement();
1057 
1058                             if      (photoSetDetails.nodeName() == QLatin1String("title"))
1059                             {
1060                                 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Title=" << e_detail.text();
1061                                 photoSet_title = e_detail.text();
1062                                 fps.title      = photoSet_title;
1063                             }
1064                             else if (photoSetDetails.nodeName() == QLatin1String("description"))
1065                             {
1066                                 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Description =" << e_detail.text();
1067                                 photoSet_description = e_detail.text();
1068                                 fps.description      = photoSet_description;
1069                             }
1070 
1071                             photoSetDetails = photoSetDetails.nextSibling();
1072                         }
1073 
1074                         m_photoSetsList->append(fps);
1075                     }
1076                 }
1077 
1078                 detailsNode = detailsNode.nextSibling();
1079             }
1080 
1081             details = details.nextSibling();
1082             success = true;
1083         }
1084 
1085         if (node.isElement() && (node.nodeName() == QLatin1String("err")))
1086         {
1087             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
1088             QString code = node.toElement().attribute(QLatin1String("code"));
1089             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code;
1090             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg"));
1091             Q_EMIT signalError(code);
1092         }
1093 
1094         node = node.nextSibling();
1095     }
1096 
1097     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "GetPhotoList finished";
1098 
1099     if (!success)
1100     {
1101         Q_EMIT signalListPhotoSetsFailed(i18n("Failed to fetch list of photo sets."));
1102     }
1103     else
1104     {
1105         Q_EMIT signalListPhotoSetsSucceeded();
1106         maxAllowedFileSize();
1107     }
1108 }
1109 
1110 void FlickrTalker::parseResponseListPhotos(const QByteArray& data)
1111 {
1112     QDomDocument doc(QLatin1String("getPhotosList"));
1113 
1114     if (!doc.setContent(data))
1115     {
1116         return;
1117     }
1118 
1119     QDomElement docElem = doc.documentElement();
1120     QDomNode node       = docElem.firstChild();
1121 
1122     //QDomElement e;
1123     //TODO
1124 }
1125 
1126 void FlickrTalker::parseResponseCreateAlbum(const QByteArray& data)
1127 {
1128     QDomDocument doc(QLatin1String("getCreateAlbum"));
1129 
1130     if (!doc.setContent(data))
1131     {
1132         return;
1133     }
1134 
1135     QDomElement docElem = doc.documentElement();
1136     QDomNode node       = docElem.firstChild();
1137 
1138     //TODO
1139 }
1140 
1141 void FlickrTalker::parseResponseAddPhoto(const QByteArray& data)
1142 {
1143     bool    success = false;
1144     QString line;
1145     QDomDocument doc(QLatin1String("AddPhoto Response"));
1146 
1147     if (!doc.setContent(data))
1148     {
1149         return;
1150     }
1151 
1152     QDomElement docElem = doc.documentElement();
1153     QDomNode node       = docElem.firstChild();
1154     QDomElement e;
1155     QString photoId;
1156 
1157     while (!node.isNull())
1158     {
1159         if (node.isElement() && (node.nodeName() == QLatin1String("photoid")))
1160         {
1161             e                = node.toElement();           // try to convert the node to an element.
1162             QDomNode details = e.firstChild();
1163             photoId          = e.text();
1164             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Photoid= " << photoId;
1165             success          = true;
1166         }
1167 
1168         if (node.isElement() && (node.nodeName() == QLatin1String("err")))
1169         {
1170             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
1171             QString code = node.toElement().attribute(QLatin1String("code"));
1172             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code;
1173             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg"));
1174             Q_EMIT signalError(code);
1175         }
1176 
1177         node = node.nextSibling();
1178     }
1179 
1180     if (!success)
1181     {
1182         Q_EMIT signalAddPhotoFailed(i18n("Failed to upload photo"));
1183     }
1184     else
1185     {
1186         QString photoSetId = m_selectedPhotoSet.id;
1187 
1188         if (photoSetId == QLatin1String("-1"))
1189         {
1190             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "PhotoSet Id not set, not adding the photo to any photoset";
1191             Q_EMIT signalAddPhotoSucceeded(photoId);
1192         }
1193         else
1194         {
1195             addPhotoToPhotoSet(photoId, photoSetId);
1196         }
1197     }
1198 }
1199 
1200 void FlickrTalker::parseResponsePhotoProperty(const QByteArray& data)
1201 {
1202     bool         success = false;
1203     QString      line;
1204     QDomDocument doc(QLatin1String("Photos Properties"));
1205 
1206     if (!doc.setContent(data))
1207     {
1208         return;
1209     }
1210 
1211     QDomElement docElem = doc.documentElement();
1212     QDomNode    node    = docElem.firstChild();
1213     QDomElement e;
1214 
1215     while (!node.isNull())
1216     {
1217         if (node.isElement() && (node.nodeName() == QLatin1String("photoid")))
1218         {
1219             e                = node.toElement();                 // try to convert the node to an element.
1220             QDomNode details = e.firstChild();
1221             success          = true;
1222             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Photoid=" << e.text();
1223         }
1224 
1225         if (node.isElement() && (node.nodeName() == QLatin1String("err")))
1226         {
1227             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response";
1228             QString code = node.toElement().attribute(QLatin1String("code"));
1229             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code;
1230             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg"));
1231             Q_EMIT signalError(code);
1232         }
1233 
1234         node = node.nextSibling();
1235     }
1236 
1237     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "GetToken finished";
1238 
1239     if (!success)
1240     {
1241         Q_EMIT signalAddPhotoFailed(i18n("Failed to query photo information"));
1242     }
1243     else
1244     {
1245         Q_EMIT signalAddPhotoSucceeded(QLatin1String(""));
1246     }
1247 }
1248 
1249 void FlickrTalker::parseResponseAddPhotoToPhotoSet(const QByteArray& data)
1250 {
1251     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseListPhotosets" << data;
1252     Q_EMIT signalAddPhotoSucceeded(QLatin1String(""));
1253 }
1254 
1255 void FlickrTalker::parseResponseSetGeoLocation(const QByteArray& data)
1256 {
1257     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseSetGeoLocation" << data;
1258     Q_EMIT signalAddPhotoSucceeded(QLatin1String(""));
1259 }
1260 
1261 } // namespace DigikamGenericFlickrPlugin
1262 
1263 #include "moc_flickrtalker.cpp"