File indexing completed on 2025-01-05 04:35:37

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
0004     SPDX-FileCopyrightText: 2011 Rodrigo Belem <rclbelem@gmail.com>
0005     SPDX-FileCopyrightText: 2019 Nate Graham <nate@kde.org>
0006     SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
0007     SPDX-FileCopyrightText: 2021 Slava Aseev <nullptrnine@basealt.ru>
0008 */
0009 
0010 #ifndef SAMBAUSERSHAREPLUGIN_H
0011 #define SAMBAUSERSHAREPLUGIN_H
0012 
0013 #include <KPropertiesDialogPlugin>
0014 #include <KPropertiesDialog>
0015 #include <KSambaShareData>
0016 #include <KSambaShare>
0017 #include <QFileInfo>
0018 
0019 #include <memory>
0020 #include "usermanager.h"
0021 #include "model.h"
0022 #include "permissionshelper.h"
0023 
0024 class ShareContext : public QObject
0025 {
0026     Q_OBJECT
0027     Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
0028     Q_PROPERTY(bool canEnableGuest READ canEnableGuest CONSTANT)
0029     Q_PROPERTY(bool guestEnabled READ guestEnabled WRITE setGuestEnabled NOTIFY guestEnabledChanged)
0030     Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
0031     Q_PROPERTY(int maximumNameLength READ maximumNameLength CONSTANT)
0032     Q_PROPERTY(QString path READ path CONSTANT)
0033 public:
0034     explicit ShareContext(const QUrl &url, QObject *parent = nullptr)
0035         : QObject(parent)
0036         , m_shareData(resolveShare(url))
0037         , m_enabled(KSambaShare::instance()->isDirectoryShared(m_shareData.path()))
0038         // url isn't a member. always use .path()!
0039     {
0040     }
0041 
0042     bool enabled() const
0043     {
0044         return m_enabled;
0045     }
0046 
0047     void setEnabled(bool enabled)
0048     {
0049         m_enabled = enabled;
0050         Q_EMIT enabledChanged();
0051     }
0052 
0053     bool canEnableGuest()
0054     {
0055         return KSambaShare::instance()->areGuestsAllowed();
0056     }
0057 
0058     bool guestEnabled() const
0059     {
0060         // WTF is that enum even...
0061         switch (m_shareData.guestPermission()) {
0062         case KSambaShareData::GuestsNotAllowed:
0063             return false;
0064         case KSambaShareData::GuestsAllowed:
0065             return true;
0066         }
0067         Q_UNREACHABLE();
0068         return false;
0069     }
0070 
0071     void setGuestEnabled(bool enabled)
0072     {
0073         m_shareData.setGuestPermission(enabled ? KSambaShareData::GuestsAllowed : KSambaShareData::GuestsNotAllowed);
0074         Q_EMIT guestEnabledChanged();
0075     }
0076 
0077     QString name() const
0078     {
0079         return m_shareData.name();
0080     }
0081 
0082     QString path() const {
0083         return m_shareData.path();
0084     }
0085 
0086     void setName(const QString &name)
0087     {
0088         m_shareData.setName(name);
0089         Q_EMIT nameChanged();
0090     }
0091 
0092     static constexpr int maximumNameLength()
0093     {
0094         // Windows 10 allows creating shares with a maximum of 60 characters when measured on 2020-08-13.
0095         // We consider this kind of a soft limit as there appears to be no actual limit specified anywhere.
0096         return 60;
0097     }
0098 
0099 
0100     Q_INVOKABLE static bool isNameFree(const QString &name)
0101     {
0102         return KSambaShare::instance()->isShareNameAvailable(name);
0103     }
0104 
0105 public Q_SLOTS:
0106     QString newShareName(const QUrl &url)
0107     {
0108         Q_ASSERT(url.isValid());
0109         Q_ASSERT(!url.isEmpty());
0110         // TODO pretty sure this is buggy for urls with trailing slash where filename would be ""
0111         return url.fileName().left(maximumNameLength());
0112     }
0113 
0114 Q_SIGNALS:
0115     void enabledChanged();
0116     void guestEnabledChanged();
0117     void nameChanged();
0118 
0119 private:
0120     KSambaShareData resolveShare(const QUrl &url)
0121     {
0122         QFileInfo info(url.toLocalFile());
0123         const QString path = info.canonicalFilePath();
0124         Q_ASSERT(!path.isEmpty());
0125         const QList<KSambaShareData> shareList = KSambaShare::instance()->getSharesByPath(path);
0126         if (!shareList.isEmpty()) {
0127             return shareList.first(); // FIXME: using just the first in the list for a while
0128         }
0129         KSambaShareData newShare;
0130         newShare.setName(newShareName(url));
0131         newShare.setGuestPermission(KSambaShareData::GuestsNotAllowed);
0132         newShare.setPath(path);
0133         return newShare;
0134     }
0135 
0136 public:
0137     // TODO shouldn't be public may need refactoring though because the ACL model needs an immutable copy
0138     KSambaShareData m_shareData;
0139 private:
0140     bool m_enabled = false; // this gets cached so we can manipulate its state from qml
0141 };
0142 
0143 
0144 class SambaUserSharePlugin : public KPropertiesDialogPlugin
0145 {
0146     Q_OBJECT
0147     Q_PROPERTY(bool dirty READ isDirty WRITE setDirty NOTIFY changed) // So qml can mark dirty
0148     Q_PROPERTY(bool ready READ isReady NOTIFY readyChanged) // intentionally not writable from qml
0149     // Expose instance-singleton members so QML may access them.
0150     // They aren't application-wide singletons and also cannot easily be ctor'd from QML.
0151     Q_PROPERTY(UserManager *userManager MEMBER m_userManager CONSTANT)
0152     Q_PROPERTY(UserPermissionModel *userPermissionModel MEMBER m_model CONSTANT)
0153     Q_PROPERTY(ShareContext *shareContext MEMBER m_context CONSTANT)
0154     Q_PROPERTY(PermissionsHelper *permissionsHelper MEMBER m_permissionsHelper CONSTANT)
0155 
0156 public:
0157     SambaUserSharePlugin(QObject *parent);
0158     ~SambaUserSharePlugin() override = default;
0159     void applyChanges() override;
0160 
0161     Q_INVOKABLE static bool isSambaInstalled();
0162     Q_INVOKABLE static void reboot();
0163     Q_INVOKABLE static void showSambaStatus();
0164 
0165     bool isReady() const;
0166 
0167 Q_SIGNALS:
0168     void readyChanged();
0169 
0170 private:
0171     void setReady(bool ready);
0172     void reportAdd(KSambaShareData::UserShareError error);
0173     void reportRemove(KSambaShareData::UserShareError error);
0174 
0175     const QString m_url;
0176     ShareContext *m_context= nullptr;
0177     UserPermissionModel *m_model = nullptr;
0178     UserManager *m_userManager = nullptr;
0179     PermissionsHelper *m_permissionsHelper = nullptr;
0180     bool m_ready = false;
0181     // Hold the qquickwidget so it gets destroyed with us. Otherwise we'd have bogus reference errors
0182     // as the Plugin instance is exposed as contextProperty to qml but the widget is parented to the PropertiesDialog
0183     // (i.e. our parent). So the lifetime of the widget would usually exceed ours.
0184     std::unique_ptr<QWidget> m_page = nullptr;
0185 };
0186 
0187 #endif // SAMBAUSERSHAREPLUGIN_H