Sfoglia il codice sorgente

Private links: Retrieve link through propfind property #6020

* The sharing ui does a propfind anyway: use that to query the new
property as well!
* For the socket api, asynchronously query the server for the right url
when an action that needs it is triggered.

The old, manually generated URL will be used as fallback in case the
server doesn't support the new property or the property can't be
retrieved for some reason.

Depends on owncloud/core#29021
Christian Kamm 8 anni fa
parent
commit
dca2664707

+ 1 - 1
src/common/syncjournalfilerecord.h

@@ -37,7 +37,7 @@ class OCSYNC_EXPORT SyncJournalFileRecord
 public:
     SyncJournalFileRecord();
 
-    bool isValid()
+    bool isValid() const
     {
         return !_path.isEmpty();
     }

+ 15 - 7
src/gui/sharedialog.cpp

@@ -46,7 +46,7 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
     , _sharePath(sharePath)
     , _localPath(localPath)
     , _maxSharingPermissions(maxSharingPermissions)
-    , _numericFileId(numericFileId)
+    , _privateLinkUrl(accountState->account()->deprecatedPrivateLinkUrl(numericFileId).toString(QUrl::FullyEncoded))
     , _linkWidget(NULL)
     , _userGroupWidget(NULL)
     , _progressIndicator(NULL)
@@ -130,10 +130,13 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
     // Server versions >= 9.1 support the "share-permissions" property
     // older versions will just return share-permissions: ""
     auto job = new PropfindJob(accountState->account(), _sharePath);
-    job->setProperties(QList<QByteArray>() << "http://open-collaboration-services.org/ns:share-permissions");
+    job->setProperties(
+        QList<QByteArray>()
+        << "http://open-collaboration-services.org/ns:share-permissions"
+        << "http://owncloud.org/ns:privatelink");
     job->setTimeout(10 * 1000);
-    connect(job, SIGNAL(result(QVariantMap)), SLOT(slotMaxSharingPermissionsReceived(QVariantMap)));
-    connect(job, SIGNAL(finishedWithError(QNetworkReply *)), SLOT(slotMaxSharingPermissionsError()));
+    connect(job, SIGNAL(result(QVariantMap)), SLOT(slotPropfindReceived(QVariantMap)));
+    connect(job, SIGNAL(finishedWithError(QNetworkReply *)), SLOT(slotPropfindError()));
     job->start();
 }
 
@@ -149,18 +152,23 @@ void ShareDialog::done(int r)
     QDialog::done(r);
 }
 
-void ShareDialog::slotMaxSharingPermissionsReceived(const QVariantMap &result)
+void ShareDialog::slotPropfindReceived(const QVariantMap &result)
 {
     const QVariant receivedPermissions = result["share-permissions"];
     if (!receivedPermissions.toString().isEmpty()) {
         _maxSharingPermissions = static_cast<SharePermissions>(receivedPermissions.toInt());
         qCInfo(lcSharing) << "Received sharing permissions for" << _sharePath << _maxSharingPermissions;
     }
+    auto privateLinkUrl = result["privatelink"].toString();
+    if (!privateLinkUrl.isEmpty()) {
+        qCInfo(lcSharing) << "Received private link url for" << _sharePath << privateLinkUrl;
+        _privateLinkUrl = privateLinkUrl;
+    }
 
     showSharingUi();
 }
 
-void ShareDialog::slotMaxSharingPermissionsError()
+void ShareDialog::slotPropfindError()
 {
     // On error show the share ui anyway. The user can still see shares,
     // delete them and so on, even though adding new shares or granting
@@ -194,7 +202,7 @@ void ShareDialog::showSharingUi()
         && _accountState->account()->serverVersionInt() >= Account::makeServerVersion(8, 2, 0);
 
     if (userGroupSharing) {
-        _userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _numericFileId, this);
+        _userGroupWidget = new ShareUserGroupWidget(_accountState->account(), _sharePath, _localPath, _maxSharingPermissions, _privateLinkUrl, this);
         _ui->shareWidgets->addTab(_userGroupWidget, tr("Users and Groups"));
         _userGroupWidget->getShares();
     }

+ 3 - 2
src/gui/sharedialog.h

@@ -49,8 +49,8 @@ public:
 
 private slots:
     void done(int r);
-    void slotMaxSharingPermissionsReceived(const QVariantMap &result);
-    void slotMaxSharingPermissionsError();
+    void slotPropfindReceived(const QVariantMap &result);
+    void slotPropfindError();
     void slotThumbnailFetched(const int &statusCode, const QByteArray &reply);
     void slotAccountStateChanged(int state);
 
@@ -63,6 +63,7 @@ private:
     QString _localPath;
     SharePermissions _maxSharingPermissions;
     QByteArray _numericFileId;
+    QString _privateLinkUrl;
 
     ShareLinkWidget *_linkWidget;
     ShareUserGroupWidget *_userGroupWidget;

+ 5 - 5
src/gui/shareusergroupwidget.cpp

@@ -48,7 +48,7 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
     const QString &sharePath,
     const QString &localPath,
     SharePermissions maxSharingPermissions,
-    const QByteArray &numericFileId,
+    const QString &privateLinkUrl,
     QWidget *parent)
     : QWidget(parent)
     , _ui(new Ui::ShareUserGroupWidget)
@@ -56,7 +56,7 @@ ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
     , _sharePath(sharePath)
     , _localPath(localPath)
     , _maxSharingPermissions(maxSharingPermissions)
-    , _numericFileId(numericFileId)
+    , _privateLinkUrl(privateLinkUrl)
     , _disableCompleterActivated(false)
 {
     setAttribute(Qt::WA_DeleteOnClose);
@@ -323,19 +323,19 @@ void ShareUserGroupWidget::displayError(int code, const QString &message)
 
 void ShareUserGroupWidget::slotPrivateLinkOpenBrowser()
 {
-    Utility::openBrowser(_account->filePermalinkUrl(_numericFileId), this);
+    Utility::openBrowser(_privateLinkUrl, this);
 }
 
 void ShareUserGroupWidget::slotPrivateLinkCopy()
 {
-    QApplication::clipboard()->setText(_account->filePermalinkUrl(_numericFileId).toString());
+    QApplication::clipboard()->setText(_privateLinkUrl);
 }
 
 void ShareUserGroupWidget::slotPrivateLinkEmail()
 {
     Utility::openEmailComposer(
         tr("I shared something with you"),
-        _account->filePermalinkUrl(_numericFileId).toString(),
+        _privateLinkUrl,
         this);
 }
 

+ 2 - 2
src/gui/shareusergroupwidget.h

@@ -57,7 +57,7 @@ public:
         const QString &sharePath,
         const QString &localPath,
         SharePermissions maxSharingPermissions,
-        const QByteArray &numericFileId,
+        const QString &privateLinkUrl,
         QWidget *parent = 0);
     ~ShareUserGroupWidget();
 
@@ -89,7 +89,7 @@ private:
     QString _sharePath;
     QString _localPath;
     SharePermissions _maxSharingPermissions;
-    QByteArray _numericFileId;
+    QString _privateLinkUrl;
 
     QCompleter *_completer;
     ShareeModel *_completerModel;

+ 58 - 29
src/gui/socketapi.cpp

@@ -491,23 +491,70 @@ void SocketApi::command_SHARE_MENU_TITLE(const QString &, SocketListener *listen
     listener->sendMessage(QLatin1String("SHARE_MENU_TITLE:") + tr("Share with %1", "parameter is ownCloud").arg(Theme::instance()->appNameGUI()));
 }
 
-void SocketApi::command_COPY_PRIVATE_LINK(const QString &localFile, SocketListener *)
+// Fetches the private link url asynchronously and then calls the target slot
+void fetchPrivateLinkUrl(const QString &localFile, SocketApi *target, void (SocketApi::*targetFun)(const QString &url) const)
 {
-    auto url = getPrivateLinkUrl(localFile);
-    if (!url.isEmpty()) {
-        QApplication::clipboard()->setText(url.toString());
+    Folder *shareFolder = FolderMan::instance()->folderForPath(localFile);
+    if (!shareFolder) {
+        qCWarning(lcSocketApi) << "Unknown path" << localFile;
+        return;
+    }
+
+    const QString localFileClean = QDir::cleanPath(localFile);
+    const QString file = localFileClean.mid(shareFolder->cleanPath().length() + 1);
+
+    // Generate private link ourselves: used as a fallback
+    const SyncJournalFileRecord rec = shareFolder->journalDb()->getFileRecord(file);
+    if (!rec.isValid())
+        return;
+    const QString oldUrl =
+        shareFolder->accountState()->account()->deprecatedPrivateLinkUrl(rec.numericFileId()).toString(QUrl::FullyEncoded);
+
+    // If the server doesn't have the property, use the old url directly.
+    if (!shareFolder->accountState()->account()->capabilities().privateLinkPropertyAvailable()) {
+        (target->*targetFun)(oldUrl);
+        return;
     }
+
+    // Retrieve the new link by PROPFIND
+    PropfindJob *job = new PropfindJob(shareFolder->accountState()->account(), file, target);
+    job->setProperties(QList<QByteArray>() << "http://owncloud.org/ns:privatelink");
+    job->setTimeout(10 * 1000);
+    QObject::connect(job, &PropfindJob::result, target, [=](const QVariantMap &result) {
+        auto privateLinkUrl = result["privatelink"].toString();
+        if (!privateLinkUrl.isEmpty()) {
+            (target->*targetFun)(privateLinkUrl);
+        } else {
+            (target->*targetFun)(oldUrl);
+        }
+    });
+    QObject::connect(job, &PropfindJob::finishedWithError, target, [=](QNetworkReply *) {
+        (target->*targetFun)(oldUrl);
+    });
+    job->start();
+}
+
+void SocketApi::command_COPY_PRIVATE_LINK(const QString &localFile, SocketListener *)
+{
+    fetchPrivateLinkUrl(localFile, this, &SocketApi::copyPrivateLinkToClipboard);
 }
 
 void SocketApi::command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *)
 {
-    auto url = getPrivateLinkUrl(localFile);
-    if (!url.isEmpty()) {
-        Utility::openEmailComposer(
-            tr("I shared something with you"),
-            url.toString(QUrl::FullyEncoded),
-            0);
-    }
+    fetchPrivateLinkUrl(localFile, this, &SocketApi::emailPrivateLink);
+}
+
+void SocketApi::copyPrivateLinkToClipboard(const QString &link) const
+{
+    QApplication::clipboard()->setText(link);
+}
+
+void SocketApi::emailPrivateLink(const QString &link) const
+{
+    Utility::openEmailComposer(
+        tr("I shared something with you"),
+        link,
+        0);
 }
 
 void SocketApi::command_GET_STRINGS(const QString &, SocketListener *listener)
@@ -533,22 +580,4 @@ QString SocketApi::buildRegisterPathMessage(const QString &path)
     return message;
 }
 
-QUrl SocketApi::getPrivateLinkUrl(const QString &localFile) const
-{
-    Folder *shareFolder = FolderMan::instance()->folderForPath(localFile);
-    if (!shareFolder) {
-        qCWarning(lcSocketApi) << "Unknown path" << localFile;
-        return QUrl();
-    }
-
-    const QString localFileClean = QDir::cleanPath(localFile);
-    const QString file = localFileClean.mid(shareFolder->cleanPath().length() + 1);
-
-    SyncJournalFileRecord rec = shareFolder->journalDb()->getFileRecord(file);
-    if (rec.isValid()) {
-        return shareFolder->accountState()->account()->filePermalinkUrl(rec.numericFileId());
-    }
-    return QUrl();
-}
-
 } // namespace OCC

+ 3 - 1
src/gui/socketapi.h

@@ -64,6 +64,9 @@ private slots:
     void slotReadSocket();
     void broadcastStatusPushMessage(const QString &systemPath, SyncFileStatus fileStatus);
 
+    void copyPrivateLinkToClipboard(const QString &link) const;
+    void emailPrivateLink(const QString &link) const;
+
 private:
     void broadcastMessage(const QString &msg, bool doWait = false);
 
@@ -84,7 +87,6 @@ private:
     Q_INVOKABLE void command_GET_STRINGS(const QString &argument, SocketListener *listener);
 
     QString buildRegisterPathMessage(const QString &path);
-    QUrl getPrivateLinkUrl(const QString &localFile) const;
 
     QSet<QString> _registeredAliases;
     QList<SocketListener> _listeners;

+ 1 - 1
src/libsync/account.cpp

@@ -162,7 +162,7 @@ QUrl Account::davUrl() const
     return Utility::concatUrlPath(url(), davPath());
 }
 
-QUrl Account::filePermalinkUrl(const QByteArray &numericFileId) const
+QUrl Account::deprecatedPrivateLinkUrl(const QByteArray &numericFileId) const
 {
     return Utility::concatUrlPath(url(),
         QLatin1String("/index.php/f/") + QUrl::toPercentEncoding(QString::fromLatin1(numericFileId)));

+ 6 - 2
src/libsync/account.h

@@ -108,8 +108,12 @@ public:
     /** Returns webdav entry URL, based on url() */
     QUrl davUrl() const;
 
-    /** Returns a permalink url for a file */
-    QUrl filePermalinkUrl(const QByteArray &numericFileId) const;
+    /** Returns the legacy permalink url for a file.
+     *
+     * This uses the old way of manually building the url. New code should
+     * use the "privatelink" property accessible via PROPFIND.
+     */
+    QUrl deprecatedPrivateLinkUrl(const QByteArray &numericFileId) const;
 
     /** Holds the accounts credentials */
     AbstractCredentials *credentials() const;

+ 5 - 0
src/libsync/capabilities.cpp

@@ -132,6 +132,11 @@ bool Capabilities::chunkingParallelUploadDisabled() const
     return _capabilities["dav"].toMap()["chunkingParallelUploadDisabled"].toBool();
 }
 
+bool Capabilities::privateLinkPropertyAvailable() const
+{
+    return _capabilities["files"].toMap()["privateLinks"].toBool();
+}
+
 QList<int> Capabilities::httpErrorCodesThatResetFailingChunkedUploads() const
 {
     QList<int> list;

+ 3 - 0
src/libsync/capabilities.h

@@ -47,6 +47,9 @@ public:
     /// disable parallel upload in chunking
     bool chunkingParallelUploadDisabled() const;
 
+    /// Whether the "privatelink" DAV property is available
+    bool privateLinkPropertyAvailable() const;
+
     /// returns true if the capabilities report notifications
     bool notificationsAvailable() const;