File indexing completed on 2025-01-05 04:35:36
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: 2015-2020 Harald Sitter <sitter@kde.org> 0006 SPDX-FileCopyrightText: 2019 Nate Graham <nate@kde.org> 0007 SPDX-FileCopyrightText: 2021 Slava Aseev <nullptrnine@basealt.ru> 0008 */ 0009 0010 #include "sambausershareplugin.h" 0011 0012 #include <QDebug> 0013 #include <QQmlApplicationEngine> 0014 #include <QQuickWidget> 0015 #include <QQuickItem> 0016 #include <QMetaMethod> 0017 #include <QVBoxLayout> 0018 #include <KLocalizedString> 0019 #include <QTimer> 0020 #include <QQmlContext> 0021 #include <QPushButton> 0022 #include <QDBusInterface> 0023 #include <QDBusConnection> 0024 0025 #include <KLocalizedContext> 0026 #include <KMessageBox> 0027 #include <KPluginFactory> 0028 #include <KService> 0029 #include <KIO/CommandLauncherJob> 0030 0031 0032 #include "groupmanager.h" 0033 0034 #ifdef SAMBA_INSTALL 0035 #include "sambainstaller.h" 0036 #endif 0037 0038 K_PLUGIN_CLASS_WITH_JSON(SambaUserSharePlugin, "sambausershareplugin.json") 0039 0040 0041 SambaUserSharePlugin::SambaUserSharePlugin(QObject *parent) 0042 : KPropertiesDialogPlugin(parent) 0043 , m_url(properties->item().mostLocalUrl().toLocalFile()) 0044 , m_userManager(new UserManager(this)) 0045 { 0046 if (m_url.isEmpty()) { 0047 return; 0048 } 0049 0050 const QFileInfo pathInfo(m_url); 0051 if (!pathInfo.permission(QFile::ReadUser | QFile::WriteUser)) { 0052 return; 0053 } 0054 0055 // TODO: this could be made to load delayed via invokemethod. we technically don't need to fully load 0056 // the backing data in the ctor, only the qml view with busyindicator 0057 // TODO: relatedly if we make ShareContext and the Model more async vis a vis construction we can init them from 0058 // QML and stop holding them as members in the plugin 0059 m_context = new ShareContext(properties->item().mostLocalUrl(), this); 0060 // FIXME maybe the manager ought to be owned by the model 0061 qmlRegisterAnonymousType<UserManager>("org.kde.filesharing.samba", 1); 0062 qmlRegisterAnonymousType<User>("org.kde.filesharing.samba", 1); 0063 m_model = new UserPermissionModel(m_context->m_shareData, m_userManager, this); 0064 qmlRegisterAnonymousType<PermissionsHelper>("org.kde.filesharing.samba", 1); 0065 m_permissionsHelper = new PermissionsHelper(m_context->m_shareData.path(), m_userManager, m_model); 0066 0067 #ifdef SAMBA_INSTALL 0068 qmlRegisterType<SambaInstaller>("org.kde.filesharing.samba", 1, 0, "Installer"); 0069 #endif 0070 qmlRegisterType<GroupManager>("org.kde.filesharing.samba", 1, 0, "GroupManager"); 0071 // Need access to the column enum, so register this as uncreatable. 0072 qmlRegisterUncreatableType<UserPermissionModel>("org.kde.filesharing.samba", 1, 0, "UserPermissionModel", 0073 QStringLiteral("Access through sambaPlugin.userPermissionModel")); 0074 qmlRegisterAnonymousType<ShareContext>("org.kde.filesharing.samba", 1); 0075 qmlRegisterAnonymousType<SambaUserSharePlugin>("org.kde.filesharing.samba", 1); 0076 0077 m_page.reset(new QWidget(qobject_cast<KPropertiesDialog *>(parent))); 0078 m_page->setAttribute(Qt::WA_TranslucentBackground); 0079 auto widget = new QQuickWidget(m_page.get()); 0080 // Set translation domain before setting the source so strings gets translated. 0081 auto i18nContext = new KLocalizedContext(widget->engine()); 0082 i18nContext->setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN)); 0083 widget->engine()->rootContext()->setContextObject(i18nContext); 0084 0085 widget->setResizeMode(QQuickWidget::SizeRootObjectToView); 0086 widget->setFocusPolicy(Qt::StrongFocus); 0087 widget->setAttribute(Qt::WA_AlwaysStackOnTop, true); 0088 widget->quickWindow()->setColor(Qt::transparent); 0089 auto layout = new QVBoxLayout(m_page.get()); 0090 layout->setContentsMargins({}); 0091 layout->addWidget(widget); 0092 0093 widget->rootContext()->setContextProperty(QStringLiteral("sambaPlugin"), this); 0094 0095 const QUrl url(QStringLiteral("qrc:/org.kde.filesharing.samba/qml/main.qml")); 0096 widget->setSource(url); 0097 0098 properties->setFileSharingPage(m_page.get()); 0099 if (qEnvironmentVariableIsSet("TEST_FOCUS_SHARE")) { 0100 QTimer::singleShot(100, properties, &KPropertiesDialog::showFileSharingPage); 0101 } 0102 0103 QTimer::singleShot(0, [this] { 0104 connect(m_userManager, &UserManager::loaded, this, [this] { 0105 m_permissionsHelper->reload(); 0106 setReady(true); 0107 }); 0108 m_userManager->load(); 0109 }); 0110 } 0111 0112 bool SambaUserSharePlugin::isSambaInstalled() 0113 { 0114 return QFile::exists(QStringLiteral("/usr/sbin/smbd")) 0115 || QFile::exists(QStringLiteral("/usr/local/sbin/smbd")); 0116 } 0117 0118 void SambaUserSharePlugin::showSambaStatus() 0119 { 0120 auto job = new KIO::CommandLauncherJob(QStringLiteral("kinfocenter"), {QStringLiteral("kcm_samba")}); 0121 job->setDesktopName(QStringLiteral("org.kde.kinfocenter")); 0122 job->start(); 0123 } 0124 0125 void SambaUserSharePlugin::applyChanges() 0126 { 0127 qDebug() << "!!! applying changes !!!" << m_context->enabled() << m_context->name() << m_context->guestEnabled() << m_model->getAcl() << m_context->m_shareData.path(); 0128 if (!m_context->enabled()) { 0129 reportRemove(m_context->m_shareData.remove()); 0130 return; 0131 } 0132 0133 // TODO: should run this through reportAdd() as well, ACLs may be invalid and then we shouldn't try to save 0134 m_context->m_shareData.setAcl(m_model->getAcl()); 0135 reportAdd(m_context->m_shareData.save()); 0136 } 0137 0138 static QString errorToString(KSambaShareData::UserShareError error) 0139 { 0140 // KSambaShare is a right mess. Every function with an error returns the same enum but can only return a subset of 0141 // possible values. Even so, because it returns the enum we had best mapped all values to semi-suitable string 0142 // representations even when those are utter garbage when they require specific (e.g. an invalid ACL) that 0143 // we do not have here. 0144 switch (error) { 0145 case KSambaShareData::UserShareNameOk: Q_FALLTHROUGH(); 0146 case KSambaShareData::UserSharePathOk: Q_FALLTHROUGH(); 0147 case KSambaShareData::UserShareAclOk: Q_FALLTHROUGH(); 0148 case KSambaShareData::UserShareCommentOk: Q_FALLTHROUGH(); 0149 case KSambaShareData::UserShareGuestsOk: Q_FALLTHROUGH(); 0150 case KSambaShareData::UserShareOk: 0151 // Technically anything but UserShareOk cannot happen, but best handle everything regardless. 0152 return QString(); 0153 case KSambaShareData::UserShareExceedMaxShares: 0154 return i18nc("@info detailed error messsage", 0155 "You have exhausted the maximum amount of shared directories you may have active at the same time."); 0156 case KSambaShareData::UserShareNameInvalid: 0157 return i18nc("@info detailed error messsage", "The share name is invalid."); 0158 case KSambaShareData::UserShareNameInUse: 0159 return i18nc("@info detailed error messsage", "The share name is already in use for a different directory."); 0160 case KSambaShareData::UserSharePathInvalid: 0161 return i18nc("@info detailed error messsage", "The path is invalid."); 0162 case KSambaShareData::UserSharePathNotExists: 0163 return i18nc("@info detailed error messsage", "The path does not exist."); 0164 case KSambaShareData::UserSharePathNotDirectory: 0165 return i18nc("@info detailed error messsage", "The path is not a directory."); 0166 case KSambaShareData::UserSharePathNotAbsolute: 0167 return i18nc("@info detailed error messsage", "The path is relative."); 0168 case KSambaShareData::UserSharePathNotAllowed: 0169 return i18nc("@info detailed error messsage", "This path may not be shared."); 0170 case KSambaShareData::UserShareAclInvalid: 0171 return i18nc("@info detailed error messsage", "The access rule is invalid."); 0172 case KSambaShareData::UserShareAclUserNotValid: 0173 return i18nc("@info detailed error messsage", "An access rule's user is not valid."); 0174 case KSambaShareData::UserShareGuestsInvalid: 0175 return i18nc("@info detailed error messsage", "The 'Guest' access rule is invalid."); 0176 case KSambaShareData::UserShareGuestsNotAllowed: 0177 return i18nc("@info detailed error messsage", "Enabling guest access is not allowed."); 0178 case KSambaShareData::UserShareSystemError: 0179 return KSambaShare::instance()->lastSystemErrorString().simplified(); 0180 } 0181 Q_UNREACHABLE(); 0182 return QString(); 0183 } 0184 0185 void SambaUserSharePlugin::reportAdd(KSambaShareData::UserShareError error) 0186 { 0187 if (error == KSambaShareData::UserShareOk) { 0188 return; 0189 } 0190 0191 QString errorMessage = errorToString(error); 0192 if (error == KSambaShareData::UserShareSystemError) { 0193 // System errors are (untranslated) CLI output. Give them localized context. 0194 errorMessage = xi18nc("@info error in the underlying binaries. %1 is CLI output", 0195 "<para>An error occurred while trying to share the directory." 0196 " The share has not been created.</para>" 0197 "<para>Samba internals report:</para><message>%1</message>", 0198 errorMessage); 0199 } 0200 KMessageBox::error(qobject_cast<QWidget *>(parent()), 0201 errorMessage, 0202 i18nc("@info/title", "Failed to Create Network Share")); 0203 } 0204 0205 void SambaUserSharePlugin::reportRemove(KSambaShareData::UserShareError error) 0206 { 0207 if (error == KSambaShareData::UserShareOk) { 0208 return; 0209 } 0210 0211 QString errorMessage = errorToString(error); 0212 if (error == KSambaShareData::UserShareSystemError) { 0213 // System errors are (untranslated) CLI output. Give them localized context. 0214 errorMessage = xi18nc("@info error in the underlying binaries. %1 is CLI output", 0215 "<para>An error occurred while trying to un-share the directory." 0216 " The share has not been removed.</para>" 0217 "<para>Samba internals report:</para><message>%1</message>", 0218 errorMessage); 0219 } 0220 KMessageBox::error(qobject_cast<QWidget *>(parent()), 0221 errorMessage, 0222 i18nc("@info/title", "Failed to Remove Network Share")); 0223 } 0224 0225 bool SambaUserSharePlugin::isReady() const 0226 { 0227 return m_ready; 0228 } 0229 0230 void SambaUserSharePlugin::setReady(bool ready) 0231 { 0232 m_ready = ready; 0233 Q_EMIT readyChanged(); 0234 } 0235 0236 void SambaUserSharePlugin::reboot() 0237 { 0238 QDBusInterface kdeShutdown(QStringLiteral("org.kde.Shutdown"), QStringLiteral("/Shutdown"), QStringLiteral("org.kde.Shutdown")); 0239 kdeShutdown.call(QStringLiteral("logoutAndReboot")); 0240 } 0241 0242 #include "sambausershareplugin.moc"