public:
SyncJournalFileRecord();
- bool isValid()
+ bool isValid() const
{
return !_path.isEmpty();
}
, _sharePath(sharePath)
, _localPath(localPath)
, _maxSharingPermissions(maxSharingPermissions)
- , _numericFileId(numericFileId)
+ , _privateLinkUrl(accountState->account()->deprecatedPrivateLinkUrl(numericFileId).toString(QUrl::FullyEncoded))
, _linkWidget(NULL)
, _userGroupWidget(NULL)
, _progressIndicator(NULL)
// 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();
}
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
&& _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();
}
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);
QString _localPath;
SharePermissions _maxSharingPermissions;
QByteArray _numericFileId;
+ QString _privateLinkUrl;
ShareLinkWidget *_linkWidget;
ShareUserGroupWidget *_userGroupWidget;
const QString &sharePath,
const QString &localPath,
SharePermissions maxSharingPermissions,
- const QByteArray &numericFileId,
+ const QString &privateLinkUrl,
QWidget *parent)
: QWidget(parent)
, _ui(new Ui::ShareUserGroupWidget)
, _sharePath(sharePath)
, _localPath(localPath)
, _maxSharingPermissions(maxSharingPermissions)
- , _numericFileId(numericFileId)
+ , _privateLinkUrl(privateLinkUrl)
, _disableCompleterActivated(false)
{
setAttribute(Qt::WA_DeleteOnClose);
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);
}
const QString &sharePath,
const QString &localPath,
SharePermissions maxSharingPermissions,
- const QByteArray &numericFileId,
+ const QString &privateLinkUrl,
QWidget *parent = 0);
~ShareUserGroupWidget();
QString _sharePath;
QString _localPath;
SharePermissions _maxSharingPermissions;
- QByteArray _numericFileId;
+ QString _privateLinkUrl;
QCompleter *_completer;
ShareeModel *_completerModel;
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)
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
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);
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;
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)));
/** 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;
return _capabilities["dav"].toMap()["chunkingParallelUploadDisabled"].toBool();
}
+bool Capabilities::privateLinkPropertyAvailable() const
+{
+ return _capabilities["files"].toMap()["privateLinks"].toBool();
+}
+
QList<int> Capabilities::httpErrorCodesThatResetFailingChunkedUploads() const
{
QList<int> list;
/// 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;