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
This commit is contained in:
Christian Kamm 2017-09-15 14:24:34 +02:00 committed by Roeland Jago Douma
parent b3f90111e2
commit 05927b68a8
No known key found for this signature in database
GPG key ID: F941078878347C0C
11 changed files with 102 additions and 50 deletions

View file

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

View file

@ -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();
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -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()));
}
// 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)
{
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 *)
{
auto url = getPrivateLinkUrl(localFile);
if (!url.isEmpty()) {
QApplication::clipboard()->setText(url.toString());
}
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

View file

@ -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;

View file

@ -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)));

View file

@ -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;

View file

@ -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;

View file

@ -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;