6#include "OwncloudSyncBackend.h" 
    8#include "CloudRouteModel.h" 
    9#include "CloudSyncManager.h" 
   10#include "GeoDataData.h" 
   11#include "GeoDataDocument.h" 
   12#include "GeoDataExtendedData.h" 
   13#include "GeoDataFolder.h" 
   14#include "GeoDataParser.h" 
   15#include "GeoDataPlacemark.h" 
   16#include "GeoDocument.h" 
   17#include "MarbleDebug.h" 
   18#include "MarbleDirs.h" 
   21#include "RenderPlugin.h" 
   24#include "RoutingManager.h" 
   25#include "RoutingModel.h" 
   31#include <QJsonDocument> 
   33#include <QNetworkAccessManager> 
   34#include <QNetworkRequest> 
   39class Q_DECL_HIDDEN OwncloudSyncBackend::Private
 
   42    Private(CloudSyncManager *cloudSyncManager);
 
   45    QNetworkAccessManager m_network;
 
   46    QNetworkReply *m_routeUploadReply = 
nullptr;
 
   47    QNetworkReply *m_routeListReply = 
nullptr;
 
   48    QNetworkReply *m_routeDownloadReply = 
nullptr;
 
   49    QNetworkReply *m_routeDeleteReply = 
nullptr;
 
   50    QNetworkReply *m_authReply = 
nullptr;
 
   52    QList<RouteItem> m_routeList;
 
   54    QString m_routeUploadEndpoint;
 
   55    QString m_routeListEndpoint;
 
   56    QString m_routeDownloadEndpoint;
 
   57    QString m_routeDeleteEndpoint;
 
   58    QString m_routePreviewEndpoint;
 
   60    CloudSyncManager *m_cloudSyncManager = 
nullptr;
 
   64OwncloudSyncBackend::Private::Private(CloudSyncManager *cloudSyncManager)
 
   65    : m_cacheDir(
MarbleDirs::localPath() + QLatin1StringView(
"/cloudsync/cache/routes/"))
 
   67    , m_routeUploadEndpoint(QStringLiteral(
"routes/create"))
 
   68    , m_routeListEndpoint(QStringLiteral(
"routes"))
 
   69    , m_routeDownloadEndpoint(QStringLiteral(
"routes"))
 
   70    , m_routeDeleteEndpoint(QStringLiteral(
"routes/delete"))
 
   71    , m_routePreviewEndpoint(QStringLiteral(
"routes/preview"))
 
   72    , m_cloudSyncManager(cloudSyncManager)
 
   76OwncloudSyncBackend::OwncloudSyncBackend(CloudSyncManager *cloudSyncManager)
 
   77    : d(new Private(cloudSyncManager))
 
   79    connect(d->m_cloudSyncManager, &CloudSyncManager::apiUrlChanged, 
this, &OwncloudSyncBackend::validateSettings);
 
   82OwncloudSyncBackend::~OwncloudSyncBackend()
 
   87void OwncloudSyncBackend::uploadRoute(
const QString ×tamp)
 
   89    QString word = QStringLiteral(
"----MarbleCloudBoundary");
 
   90    QString boundary = QStringLiteral(
"--%0").
arg(word);
 
   98    data.
append(
"Content-Disposition: form-data; name=\"timestamp\"");
 
  100    data.
append(
QString(timestamp + QStringLiteral(
"\r\n")).toUtf8());
 
  101    data.
append(
QString(boundary + QStringLiteral(
"\r\n")).toUtf8());
 
  104    data.
append(
"Content-Disposition: form-data; name=\"name\"");
 
  106    data.
append(routeName(timestamp).toUtf8());
 
  110    QFile kmlFile(d->m_cacheDir.absolutePath() + QStringLiteral(
"/%0.kml").arg(timestamp));
 
  113        mDebug() << 
"Could not open " << timestamp << 
".kml. Either it has not been saved" 
  114                 << 
" to cache for upload or another application removed it from there.";
 
  118    GeoDataParser parser(GeoData_KML);
 
  119    if (!parser.read(&kmlFile)) {
 
  120        mDebug() << 
"[OwncloudSyncBackend] KML file" << kmlFile.fileName() << 
"is broken so I can't fill required properties";
 
  124    auto root = 
dynamic_cast<GeoDataDocument *
>(parser.releaseDocument());
 
  125    if (!root || root->size() < 2) {
 
  126        mDebug() << 
"[OwncloudSyncBackend] Root document is broken";
 
  130    auto doc = geodata_cast<GeoDataDocument>(root->child(1));
 
  131    if (!doc || doc->size() < 1) {
 
  132        mDebug() << 
"[OwncloudSyncBackend] Tracking document is broken";
 
  136    auto placemark = geodata_cast<GeoDataPlacemark>(doc->child(0));
 
  138        mDebug() << 
"[OwncloudSyncBackend] Placemark is broken";
 
  144    mDebug() << 
"[Owncloud] Duration on write is" << duration;
 
  145    data.
append(
"Content-Disposition: form-data; name=\"duration\"");
 
  152    double distance = placemark->extendedData().value(QStringLiteral(
"length")).value().toDouble();
 
  153    mDebug() << 
"[Owncloud] Distance on write is" << 
distance;
 
  154    data.
append(
"Content-Disposition: form-data; name=\"distance\"");
 
  161    data.
append(QStringLiteral(
"Content-Disposition: form-data; name=\"kml\"; filename=\"%0.kml\"").arg(timestamp).toUtf8());
 
  163    data.
append(
"Content-Type: application/vnd.google-earth.kml+xml");
 
  167    data.
append(kmlFile.readAll());
 
  174    data.
append(QStringLiteral(
"Content-Disposition: form-data; name=\"preview\"; filename=\"%0.jpg\"").arg(timestamp).toUtf8());
 
  176    data.
append(
"Content-Type: image/jpg");
 
  180    QBuffer previewBuffer(&previewBytes);
 
  181    QPixmap preview = createPreview(timestamp);
 
  182    preview.
save(&previewBuffer, 
"JPG");
 
  184    data.
append(previewBytes);
 
  188    d->m_routeUploadReply = d->m_network.post(request, data);
 
  189    connect(d->m_routeUploadReply, SIGNAL(uploadProgress(qint64, qint64)), 
this, SIGNAL(routeUploadProgress(qint64, qint64)));
 
  192void OwncloudSyncBackend::downloadRouteList()
 
  195    d->m_routeListReply = d->m_network.get(request);
 
  196    connect(d->m_routeListReply, SIGNAL(downloadProgress(qint64, qint64)), 
this, SIGNAL(routeListDownloadProgress(qint64, qint64)));
 
  197    connect(d->m_routeListReply, SIGNAL(finished()), 
this, SLOT(prepareRouteList()));
 
  200void OwncloudSyncBackend::downloadRoute(
const QString ×tamp)
 
  202    QNetworkRequest routeRequest(endpointUrl(d->m_routeDownloadEndpoint, timestamp));
 
  203    d->m_routeDownloadReply = d->m_network.get(routeRequest);
 
  204    connect(d->m_routeDownloadReply, SIGNAL(finished()), 
this, SLOT(saveDownloadedRoute()));
 
  205    connect(d->m_routeDownloadReply, SIGNAL(downloadProgress(qint64, qint64)), 
this, SIGNAL(routeDownloadProgress(qint64, qint64)));
 
  208void OwncloudSyncBackend::deleteRoute(
const QString ×tamp)
 
  210    QUrl url(endpointUrl(d->m_routeDeleteEndpoint, timestamp));
 
  212    d->m_routeDeleteReply = d->m_network.deleteResource(request);
 
  213    connect(d->m_routeDeleteReply, SIGNAL(finished()), 
this, SIGNAL(routeDeleted()));
 
  216QPixmap OwncloudSyncBackend::createPreview(
const QString ×tamp)
 const 
  218    MarbleWidget mapWidget;
 
  219    for (RenderPlugin *plugin : mapWidget.renderPlugins()) {
 
  220        plugin->setEnabled(
false);
 
  223    mapWidget.setProjection(Mercator);
 
  224    mapWidget.setMapThemeId(QStringLiteral(
"earth/openstreetmap/openstreetmap.dgml"));
 
  225    mapWidget.resize(512, 512);
 
  227    RoutingManager *manager = mapWidget.model()->routingManager();
 
  228    manager->loadRoute(d->m_cacheDir.absolutePath() + QStringLiteral(
"/%0.kml").arg(timestamp));
 
  229    GeoDataLatLonBox 
const bbox = manager->routingModel()->route().bounds();
 
  231    if (!bbox.isEmpty()) {
 
  232        mapWidget.centerOn(bbox);
 
  235    QPixmap pixmap = mapWidget.grab();
 
  236    QDir(d->m_cacheDir.absolutePath()).
mkpath(QStringLiteral(
"preview"));
 
  242QString OwncloudSyncBackend::routeName(
const QString ×tamp)
 const 
  244    QFile file(d->m_cacheDir.absolutePath() + QStringLiteral(
"/%0.kml").arg(timestamp));
 
  247    GeoDataParser parser(GeoData_KML);
 
  248    if (!parser.read(&file)) {
 
  249        mDebug() << 
"Could not read " << timestamp << 
".kml. Timestamp will be used as " 
  250                 << 
"route name because of the problem";
 
  256    GeoDocument *geoDoc = parser.releaseDocument();
 
  257    auto container = 
dynamic_cast<GeoDataDocument *
>(geoDoc);
 
  258    if (container && !container->isEmpty()) {
 
  259        GeoDataFolder *folder = container->folderList().at(0);
 
  260        for (GeoDataPlacemark *placemark : folder->placemarkList()) {
 
  261            routeName.
append(placemark->name());
 
  262            routeName.
append(QStringLiteral(
" - "));
 
  266    return routeName.
left(routeName.
length() - 3);
 
  269void OwncloudSyncBackend::validateSettings()
 
  271    if (!d->m_cloudSyncManager->owncloudServer().isEmpty() && !d->m_cloudSyncManager->owncloudUsername().isEmpty()
 
  272        && !d->m_cloudSyncManager->owncloudPassword().isEmpty()) {
 
  274        d->m_authReply = d->m_network.get(request);
 
  279        d->m_cloudSyncManager->setStatus({}, CloudSyncManager::Success);
 
  286        QString const status = tr(
"Server '%1' could not be reached").arg(d->m_cloudSyncManager->owncloudServer());
 
  287        d->m_cloudSyncManager->setStatus(
status, CloudSyncManager::Error);
 
  291void OwncloudSyncBackend::checkAuthReply()
 
  304            d->m_cloudSyncManager->setStatus(tr(
"The Marble app is not installed on the ownCloud server"), CloudSyncManager::Error);
 
  306            d->m_cloudSyncManager->setStatus(tr(
"The server is not an ownCloud server"), CloudSyncManager::Error);
 
  308    } 
else if (result == 
QLatin1StringView(R
"({"message":"Current user is not logged in"})") && statusCode == 401) { 
  310        d->m_cloudSyncManager->setStatus(tr(
"Username or password are incorrect"), CloudSyncManager::Error);
 
  311    } 
else if (result.
contains(QStringLiteral(R
"("status":"success")")) && statusCode == 200) { 
  313        d->m_cloudSyncManager->setStatus(tr(
"Login successful"), CloudSyncManager::Success);
 
  317void OwncloudSyncBackend::cancelUpload()
 
  319    d->m_routeUploadReply->abort();
 
  322void OwncloudSyncBackend::prepareRouteList()
 
  327    d->m_routeList.clear();
 
  331        for (
int index = 0; index < dataArray.
size(); ++index) {
 
  332            QJsonObject dataObject = dataArray[index].toObject();
 
  335            route.setIdentifier(dataObject.
value(QStringLiteral(
"timestamp")).
toString());
 
  336            route.setName(dataObject.
value(QStringLiteral(
"name")).
toString());
 
  337            route.setDistance(dataObject.
value(QStringLiteral(
"distance")).
toString());
 
  338            route.setDuration(dataObject.
value(QStringLiteral(
"duration")).
toString());
 
  339            route.setPreviewUrl(endpointUrl(d->m_routePreviewEndpoint, route.identifier()));
 
  340            route.setOnCloud(
true);
 
  342            d->m_routeList.
append(route);
 
  347    if (!d->m_routeList.isEmpty()) {
 
  348        d->m_routeList.remove(d->m_routeList.count() - 1);
 
  351    Q_EMIT routeListDownloaded(d->m_routeList);
 
  354void OwncloudSyncBackend::saveDownloadedRoute()
 
  358    bool pathCreated = d->m_cacheDir.mkpath(d->m_cacheDir.absolutePath());
 
  360        mDebug() << 
"Couldn't create the path " << d->m_cacheDir.absolutePath() << 
". Check if your user has sufficient permissions for this operation.";
 
  363    QString kmlFilePath = QStringLiteral(
"%0/%1.kml").
arg(d->m_cacheDir.absolutePath(), timestamp);
 
  364    QFile kmlFile(kmlFilePath);
 
  368        mDebug() << 
"Failed to open file" << kmlFilePath << 
" for writing." 
  369                 << 
" Its directory either is missing or is not writable.";
 
  373    kmlFile.write(d->m_routeDownloadReply->readAll());
 
  376    QString previewPath = QStringLiteral(
"%0/preview/").
arg(d->m_cacheDir.absolutePath());
 
  377    bool previewPathCreated = d->m_cacheDir.mkpath(previewPath);
 
  378    if (!previewPathCreated) {
 
  379        mDebug() << 
"Couldn't create the path " << previewPath << 
". Check if your user has sufficient permissions for this operation.";
 
  382    QString previewFilePath = QStringLiteral(
"%0/preview/%1.jpg").
arg(d->m_cacheDir.absolutePath(), timestamp);
 
  383    QFile previewFile(previewFilePath);
 
  386    if (!previewFileOpened) {
 
  387        mDebug() << 
"Failed to open file" << previewFilePath << 
"for writing." 
  388                 << 
" Its directory either is missing or is not writable.";
 
  392    QPixmap preview = createPreview(timestamp);
 
  393    preview.
save(&previewFile, 
"JPG");
 
  396    Q_EMIT routeDownloaded();
 
  399QUrl OwncloudSyncBackend::endpointUrl(
const QString &endpoint)
 const 
  401    const QString endpointUrl = d->m_cloudSyncManager->apiUrl().toString() + 
QLatin1Char(
'/') + endpoint;
 
  402    return QUrl(endpointUrl);
 
  405QUrl OwncloudSyncBackend::endpointUrl(
const QString &endpoint, 
const QString ¶meter)
 const 
  408    return QUrl(endpointUrl);
 
  411void OwncloudSyncBackend::removeFromCache(
const QDir &cacheDir, 
const QString ×tamp)
 
  414    bool previewRemoved = 
QFile(QStringLiteral(
"%0/preview/%1.jpg").arg(cacheDir.
absolutePath(), timestamp)).
remove();
 
  415    if (!fileRemoved || !previewRemoved) {
 
  416        mDebug() << 
"Failed to remove locally cached route " << timestamp
 
  418                    "have been removed already, or its directory is missing / not writable.";
 
  421    Q_EMIT removedFromCache(timestamp);
 
  426#include "moc_OwncloudSyncBackend.cpp" 
This file contains the headers for MarbleModel.
 
A class that manages data look-up for Marble.
 
Q_SCRIPTABLE CaptureState status()
 
char * toString(const EngineQuery &query)
 
Binds a QML item to a specific geodetic location in screen coordinates.
 
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
 
QByteArray & append(QByteArrayView data)
 
QString absolutePath() const const
 
bool mkpath(const QString &dirPath) const const
 
QString fileName() const const
 
void append(const QJsonValue &value)
 
qsizetype size() const const
 
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
 
QJsonObject object() const const
 
QJsonValue value(QLatin1StringView key) const const
 
bool isArray() const const
 
QJsonArray toArray() const const
 
void errorOccurred(QNetworkReply::NetworkError code)
 
bool save(QIODevice *device, const char *format, int quality) const const
 
QString & append(QChar ch)
 
QString arg(Args &&... args) const const
 
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
 
QString fromLatin1(QByteArrayView str)
 
QString left(qsizetype n) const const
 
qsizetype length() const const
 
QString number(double n, char format, int precision)
 
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
 
int toInt(bool *ok, int base) const const
 
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
 
QTime fromString(QStringView string, QStringView format)
 
int secsTo(QTime t) const const