"Fossies" - the Fresh Open Source Software Archive

Member "digikam-6.3.0/core/dplugins/generic/webservices/google/gdrive/gdtalker.cpp" (4 Sep 2019, 13375 Bytes) of package /linux/misc/digikam-6.3.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gdtalker.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.2.0_vs_6.3.0.

    1 /* ============================================================
    2  *
    3  * This file is a part of digiKam project
    4  * https://www.digikam.org
    5  *
    6  * Date        : 2013-11-18
    7  * Description : a tool to export items to Google web services
    8  *
    9  * Copyright (C) 2013      by Pankaj Kumar <me at panks dot me>
   10  * Copyright (C) 2013-2018 by Caulier Gilles <caulier dot gilles at gmail dot com>
   11  *
   12  * This program is free software; you can redistribute it
   13  * and/or modify it under the terms of the GNU General
   14  * Public License as published by the Free Software Foundation;
   15  * either version 2, or (at your option) any later version.
   16  *
   17  * This program is distributed in the hope that it will be useful,
   18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   20  * GNU General Public License for more details.
   21  *
   22  * ============================================================ */
   23 
   24 #include "gdtalker.h"
   25 
   26 // Qt includes
   27 
   28 #include <QMimeDatabase>
   29 #include <QApplication>
   30 #include <QJsonDocument>
   31 #include <QJsonParseError>
   32 #include <QJsonObject>
   33 #include <QJsonValue>
   34 #include <QJsonArray>
   35 #include <QByteArray>
   36 #include <QtAlgorithms>
   37 #include <QVBoxLayout>
   38 #include <QLineEdit>
   39 #include <QPlainTextEdit>
   40 #include <QList>
   41 #include <QVariant>
   42 #include <QVariantList>
   43 #include <QVariantMap>
   44 #include <QPair>
   45 #include <QFileInfo>
   46 #include <QMessageBox>
   47 #include <QStandardPaths>
   48 #include <QUrlQuery>
   49 
   50 // Local includes
   51 
   52 #include "wstoolutils.h"
   53 #include "digikam_version.h"
   54 #include "gswindow.h"
   55 #include "gdmpform.h"
   56 #include "digikam_debug.h"
   57 #include "previewloadthread.h"
   58 #include "dmetadata.h"
   59 
   60 using namespace Digikam;
   61 
   62 namespace DigikamGenericGoogleServicesPlugin
   63 {
   64 
   65 static bool gdriveLessThan(const GSFolder& p1, const GSFolder& p2)
   66 {
   67     return (p1.title.toLower() < p2.title.toLower());
   68 }
   69 
   70 class Q_DECL_HIDDEN GDTalker::Private
   71 {
   72 public:
   73 
   74     enum State
   75     {
   76         GD_LOGOUT      = -1,
   77         GD_LISTFOLDERS = 0,
   78         GD_CREATEFOLDER,
   79         GD_ADDPHOTO,
   80         GD_USERNAME,
   81     };
   82 
   83 public:
   84 
   85     explicit Private()
   86     {
   87         apiUrl         = QLatin1String("https://www.googleapis.com/drive/v2/%1");
   88         uploadUrl      = QLatin1String("https://www.googleapis.com/upload/drive/v2/files");
   89         state          = GD_LOGOUT;
   90         netMngr        = nullptr;
   91         rootid         = QLatin1String("root");
   92         rootfoldername = QLatin1String("GoogleDrive Root");
   93         listPhotoId    = QStringList();
   94     }
   95 
   96 public:
   97 
   98     QString                apiUrl;
   99     QString                uploadUrl;
  100     QString                rootid;
  101     QString                rootfoldername;
  102     QString                username;
  103     State                  state;
  104     QStringList            listPhotoId;
  105 
  106     QNetworkAccessManager* netMngr;
  107 };
  108 
  109 GDTalker::GDTalker(QWidget* const parent)
  110     : GSTalkerBase(parent, QStringList(QLatin1String("https://www.googleapis.com/auth/drive")), QLatin1String("GoogleDrive")), 
  111       d(new Private)
  112 {
  113     d->netMngr = new QNetworkAccessManager(this);
  114 
  115     connect(d->netMngr, SIGNAL(finished(QNetworkReply*)),
  116             this, SLOT(slotFinished(QNetworkReply*)));
  117 
  118     connect(this, SIGNAL(signalReadyToUpload()),
  119             this, SLOT(slotUploadPhoto()));
  120 }
  121 
  122 GDTalker::~GDTalker()
  123 {
  124     if (m_reply)
  125     {
  126         m_reply->abort();
  127     }
  128 
  129     WSToolUtils::removeTemporaryDir("google");
  130 
  131     delete d;
  132 }
  133 
  134 /**
  135  * Gets username
  136  */
  137 void GDTalker::getUserName()
  138 {
  139     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "getUserName";
  140 
  141     QUrl url(d->apiUrl.arg(QLatin1String("about")));
  142 
  143     QNetworkRequest netRequest(url);
  144     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json"));
  145     netRequest.setRawHeader("Authorization", m_bearerAccessToken.toLatin1());
  146 
  147     m_reply  = d->netMngr->get(netRequest);
  148     d->state = Private::GD_USERNAME;
  149     emit signalBusy(true);
  150 }
  151 
  152 /**
  153  * Gets list of folder of user in json format
  154  */
  155 void GDTalker::listFolders()
  156 {
  157     QUrl url(d->apiUrl.arg(QLatin1String("files")));
  158 
  159     QUrlQuery q;
  160     q.addQueryItem(QLatin1String("q"), QLatin1String("mimeType = 'application/vnd.google-apps.folder'"));
  161 
  162     url.setQuery(q);
  163 
  164     QNetworkRequest netRequest(url);
  165     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json"));
  166     netRequest.setRawHeader("Authorization", m_bearerAccessToken.toLatin1());
  167 
  168     m_reply  = d->netMngr->get(netRequest);
  169     d->state = Private::GD_LISTFOLDERS;
  170     emit signalBusy(true);
  171 }
  172 
  173 /**
  174  * Creates folder inside any folder(of which id is passed)
  175  */
  176 void GDTalker::createFolder(const QString& title, const QString& id)
  177 {
  178     if (m_reply)
  179     {
  180         m_reply->abort();
  181         m_reply = nullptr;
  182     }
  183 
  184     QUrl url(d->apiUrl.arg(QLatin1String("files")));
  185     QByteArray data;
  186     data += "{\"title\":\"";
  187     data += title.toLatin1();
  188     data += "\",\r\n";
  189     data += "\"parents\":";
  190     data += "[{";
  191     data += "\"id\":\"";
  192     data += id.toLatin1();
  193     data += "\"}],\r\n";
  194     data += "\"mimeType\":";
  195     data += "\"application/vnd.google-apps.folder\"";
  196     data += "}\r\n";
  197 
  198     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "data:" << data;
  199 
  200     QNetworkRequest netRequest(url);
  201     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json"));
  202     netRequest.setRawHeader("Authorization", m_bearerAccessToken.toLatin1());
  203 
  204     m_reply  = d->netMngr->post(netRequest, data);
  205     d->state = Private::GD_CREATEFOLDER;
  206     emit signalBusy(true);
  207 }
  208 
  209 bool GDTalker::addPhoto(const QString& imgPath, const GSPhoto& info,
  210                         const QString& id, bool original, bool rescale, int maxDim, int imageQuality)
  211 {
  212     if (m_reply)
  213     {
  214         m_reply->abort();
  215         m_reply = nullptr;
  216     }
  217 
  218     emit signalBusy(true);
  219 
  220     QString path(imgPath);
  221 
  222     QMimeDatabase mimeDB;
  223 
  224     if (!original && mimeDB.mimeTypeForFile(imgPath).name().startsWith(QLatin1String("image/")))
  225     {
  226         QImage image = PreviewLoadThread::loadHighQualitySynchronously(imgPath).copyQImage();
  227 
  228         if (image.isNull())
  229         {
  230             image.load(imgPath);
  231         }
  232 
  233         if (image.isNull())
  234         {
  235             emit signalBusy(false);
  236             return false;
  237         }
  238 
  239         path = WSToolUtils::makeTemporaryDir("google").filePath(QFileInfo(imgPath)
  240                                              .baseName().trimmed() + QLatin1String(".jpg"));
  241 
  242         if (rescale && (image.width() > maxDim || image.height() > maxDim))
  243         {
  244             image = image.scaled(maxDim,maxDim,Qt::KeepAspectRatio,Qt::SmoothTransformation);
  245         }
  246 
  247         image.save(path, "JPEG", imageQuality);
  248 
  249         DMetadata meta;
  250 
  251         if (meta.load(imgPath))
  252         {
  253             meta.setItemDimensions(image.size());
  254             meta.setItemOrientation(MetaEngine::ORIENTATION_NORMAL);
  255             meta.setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY);
  256             meta.save(path, true);
  257         }
  258     }
  259 
  260     GDMPForm form;
  261     form.addPair(QUrl::fromLocalFile(path).fileName(), info.description, path, id);
  262 
  263     if (!form.addFile(path))
  264     {
  265         emit signalBusy(false);
  266         return false;
  267     }
  268 
  269     form.finish();
  270 
  271     QUrl url(d->uploadUrl);
  272 
  273     QUrlQuery q;
  274     q.addQueryItem(QLatin1String("uploadType"), QLatin1String("multipart"));
  275 
  276     url.setQuery(q);
  277 
  278     QNetworkRequest netRequest(url);
  279     netRequest.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType());
  280     netRequest.setRawHeader("Authorization", m_bearerAccessToken.toLatin1());
  281     netRequest.setRawHeader("Host", "www.googleapis.com");
  282 
  283     m_reply = d->netMngr->post(netRequest, form.formData());
  284 
  285     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In add photo";
  286     d->state = Private::GD_ADDPHOTO;
  287     return true;
  288 }
  289 
  290 void GDTalker::slotFinished(QNetworkReply* reply)
  291 {
  292     if (reply != m_reply)
  293     {
  294         return;
  295     }
  296 
  297     m_reply = nullptr;
  298 
  299     if (reply->error() != QNetworkReply::NoError)
  300     {
  301         emit signalBusy(false);
  302         QMessageBox::critical(QApplication::activeWindow(),
  303                               i18n("Error"), reply->errorString());
  304 
  305         reply->deleteLater();
  306         return;
  307     }
  308 
  309     QByteArray buffer =  reply->readAll();
  310 
  311     switch (d->state)
  312     {
  313         case (Private::GD_LOGOUT):
  314             break;
  315         case (Private::GD_LISTFOLDERS):
  316             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In Private::GD_LISTFOLDERS";
  317             parseResponseListFolders(buffer);
  318             break;
  319         case (Private::GD_CREATEFOLDER):
  320             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In Private::GD_CREATEFOLDER";
  321             parseResponseCreateFolder(buffer);
  322             break;
  323         case (Private::GD_ADDPHOTO):
  324             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In Private::GD_ADDPHOTO"; // << buffer;
  325             parseResponseAddPhoto(buffer);
  326             break;
  327         case (Private::GD_USERNAME):
  328             qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In Private::GD_USERNAME"; // << buffer;
  329             parseResponseUserName(buffer);
  330             break;
  331         default:
  332             break;
  333     }
  334 
  335     reply->deleteLater();
  336 }
  337 
  338 void GDTalker::slotUploadPhoto()
  339 {
  340     qCDebug(DIGIKAM_WEBSERVICES_LOG) << d->listPhotoId.join(QLatin1String(", "));
  341     emit signalUploadPhotoDone(1, QString(), d->listPhotoId);
  342 }
  343 
  344 void GDTalker::parseResponseUserName(const QByteArray& data)
  345 {
  346     QJsonParseError err;
  347     QJsonDocument doc = QJsonDocument::fromJson(data, &err);
  348 
  349     if (err.error != QJsonParseError::NoError)
  350     {
  351         emit signalBusy(false);
  352         return;
  353     }
  354 
  355     QJsonObject jsonObject = doc.object();
  356     qCDebug(DIGIKAM_WEBSERVICES_LOG)<<"User Name is: " << jsonObject[QLatin1String("name")].toString();
  357     QString temp           = jsonObject[QLatin1String("name")].toString();
  358 
  359     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "in parseResponseUserName";
  360 
  361     emit signalBusy(false);
  362     emit signalSetUserName(temp);
  363 }
  364 
  365 void GDTalker::parseResponseListFolders(const QByteArray& data)
  366 {
  367     QJsonParseError err;
  368     QJsonDocument doc = QJsonDocument::fromJson(data, &err);
  369 
  370     qCDebug(DIGIKAM_WEBSERVICES_LOG) << doc;
  371 
  372     if (err.error != QJsonParseError::NoError)
  373     {
  374         emit signalBusy(false);
  375         emit signalListAlbumsDone(0,i18n("Failed to list folders"),QList<GSFolder>());
  376         return;
  377     }
  378 
  379     QJsonObject jsonObject = doc.object();
  380     QJsonArray jsonArray   = jsonObject[QLatin1String("items")].toArray();
  381 
  382     QList<GSFolder> albumList;
  383     GSFolder fps;
  384     fps.id    = d->rootid;
  385     fps.title = d->rootfoldername;
  386     albumList.append(fps);
  387 
  388     foreach (const QJsonValue& value, jsonArray)
  389     {
  390         QJsonObject obj      = value.toObject();
  391 
  392         // Verify if album is in trash
  393         QJsonObject labels   = obj[QLatin1String("labels")].toObject();
  394         bool        trashed  = labels[QLatin1String("trashed")].toBool();
  395 
  396         // Verify if album is editable
  397         bool        editable = obj[QLatin1String("editable")].toBool();
  398 
  399         /* Verify if album is visualized in a folder inside My Drive
  400          * If parents is empty, album is shared by another person and not added to My Drive yet
  401          */
  402         QJsonArray  parents  = obj[QLatin1String("parents")].toArray();
  403 
  404         fps.id          = obj[QLatin1String("id")].toString();
  405         fps.title       = obj[QLatin1String("title")].toString();
  406 
  407         if (editable && !trashed && !parents.isEmpty())
  408         {
  409             albumList.append(fps);
  410         }
  411     }
  412 
  413     std::sort(albumList.begin(), albumList.end(), gdriveLessThan);
  414 
  415     emit signalBusy(false);
  416     emit signalListAlbumsDone(1, QString(), albumList);
  417 }
  418 
  419 void GDTalker::parseResponseCreateFolder(const QByteArray& data)
  420 {
  421     QJsonParseError err;
  422     QJsonDocument doc = QJsonDocument::fromJson(data, &err);
  423 
  424     if (err.error != QJsonParseError::NoError)
  425     {
  426         emit signalBusy(false);
  427         return;
  428     }
  429 
  430     QJsonObject jsonObject = doc.object();
  431     QString temp           = jsonObject[QLatin1String("alternateLink")].toString();
  432     bool success           = false;
  433 
  434     if (!(QString::compare(temp, QLatin1String(""), Qt::CaseInsensitive) == 0))
  435         success = true;
  436 
  437     emit signalBusy(false);
  438 
  439     if (!success)
  440     {
  441         emit signalCreateFolderDone(0,i18n("Failed to create folder"));
  442     }
  443     else
  444     {
  445         emit signalCreateFolderDone(1,QString());
  446     }
  447 }
  448 
  449 void GDTalker::parseResponseAddPhoto(const QByteArray& data)
  450 {
  451     QJsonParseError err;
  452     QJsonDocument doc = QJsonDocument::fromJson(data, &err);
  453 
  454     if (err.error != QJsonParseError::NoError)
  455     {
  456         emit signalBusy(false);
  457         return;
  458     }
  459 
  460     QJsonObject jsonObject = doc.object();
  461     QString altLink        = jsonObject[QLatin1String("alternateLink")].toString();
  462     QString photoId        = jsonObject[QLatin1String("id")].toString();
  463     bool success           = false;
  464 
  465     if (!(QString::compare(altLink, QLatin1String(""), Qt::CaseInsensitive) == 0))
  466         success = true;
  467 
  468     emit signalBusy(false);
  469 
  470     if (!success)
  471     {
  472         emit signalAddPhotoDone(0, i18n("Failed to upload photo"));
  473     }
  474     else
  475     {
  476         d->listPhotoId << photoId;
  477         emit signalAddPhotoDone(1, QString());
  478     }
  479 }
  480 
  481 void GDTalker::cancel()
  482 {
  483     if (m_reply)
  484     {
  485         m_reply->abort();
  486         m_reply = nullptr;
  487     }
  488 
  489     emit signalBusy(false);
  490 }
  491 
  492 } // namespace DigikamGenericGoogleServicesPlugin