diff --git a/kf5/src/ftpshareplugin.cpp b/kf5/src/ftpshareplugin.cpp index c48f4aa..9ba388d 100644 --- a/kf5/src/ftpshareplugin.cpp +++ b/kf5/src/ftpshareplugin.cpp @@ -141,17 +141,20 @@ FtpSharePlugin::FtpSharePlugin(QObject *parent, const QVariantList &args) usersLayout->addWidget(scrollArea); mainLayout->addWidget(usersGroup); - // Change password section - auto *pwGroup = new QGroupBox(i18n("FTP Password")); + // FTP user management section + auto *pwGroup = new QGroupBox(i18n("FTP User")); auto *pwLayout = new QHBoxLayout(pwGroup); m_pwUserCombo = new QComboBox(); m_pwUserCombo->addItems(users); auto *pwButton = new QPushButton(i18n("Change Password...")); + auto *deleteButton = new QPushButton(i18n("Delete FTP User")); pwLayout->addWidget(m_pwUserCombo); pwLayout->addWidget(pwButton); + pwLayout->addWidget(deleteButton); mainLayout->addWidget(pwGroup); connect(pwButton, &QPushButton::clicked, this, &FtpSharePlugin::onChangePassword); + connect(deleteButton, &QPushButton::clicked, this, &FtpSharePlugin::onDeleteFtpUser); // Server configuration section auto *serverGroup = new QGroupBox(i18n("Server Configuration")); @@ -483,4 +486,44 @@ void FtpSharePlugin::onChangePassword() } } +void FtpSharePlugin::onDeleteFtpUser() +{ + const QString username = m_pwUserCombo->currentText(); + if (username.isEmpty()) + return; + + if (!ftpUserExists(username)) { + QMessageBox::information(m_page, + i18n("Delete FTP User"), + i18n("User '%1' has no FTP account.", username)); + return; + } + + const auto result = QMessageBox::question(m_page, + i18n("Delete FTP User"), + i18n("Really delete FTP account for '%1'?\n\n" + "The system user stays untouched. Only the FTP login and\n" + "this user's bind-mounts in the FTP root will be removed.", username), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (result != QMessageBox::Yes) + return; + + QProcess proc; + proc.start(QStringLiteral("dolphin-ftp-share"), + {QStringLiteral("deluser"), username}); + proc.waitForFinished(10000); + + if (proc.exitCode() == 0) { + QMessageBox::information(m_page, + i18n("Delete FTP User"), + i18n("FTP user '%1' deleted.", username)); + } else { + QMessageBox::warning(m_page, + i18n("Delete FTP User"), + i18n("Failed to delete FTP user '%1'.", username)); + } +} + #include "ftpshareplugin.moc" diff --git a/kf5/src/ftpshareplugin.h b/kf5/src/ftpshareplugin.h index 9730472..72c0063 100644 --- a/kf5/src/ftpshareplugin.h +++ b/kf5/src/ftpshareplugin.h @@ -27,6 +27,7 @@ private: void loadServerConfig(); void onShareToggled(bool checked); void onChangePassword(); + void onDeleteFtpUser(); void onApplyServerConfig(); void onModeChanged(); diff --git a/kf6/src/ftpshareplugin.cpp b/kf6/src/ftpshareplugin.cpp index c48f4aa..9ba388d 100644 --- a/kf6/src/ftpshareplugin.cpp +++ b/kf6/src/ftpshareplugin.cpp @@ -141,17 +141,20 @@ FtpSharePlugin::FtpSharePlugin(QObject *parent, const QVariantList &args) usersLayout->addWidget(scrollArea); mainLayout->addWidget(usersGroup); - // Change password section - auto *pwGroup = new QGroupBox(i18n("FTP Password")); + // FTP user management section + auto *pwGroup = new QGroupBox(i18n("FTP User")); auto *pwLayout = new QHBoxLayout(pwGroup); m_pwUserCombo = new QComboBox(); m_pwUserCombo->addItems(users); auto *pwButton = new QPushButton(i18n("Change Password...")); + auto *deleteButton = new QPushButton(i18n("Delete FTP User")); pwLayout->addWidget(m_pwUserCombo); pwLayout->addWidget(pwButton); + pwLayout->addWidget(deleteButton); mainLayout->addWidget(pwGroup); connect(pwButton, &QPushButton::clicked, this, &FtpSharePlugin::onChangePassword); + connect(deleteButton, &QPushButton::clicked, this, &FtpSharePlugin::onDeleteFtpUser); // Server configuration section auto *serverGroup = new QGroupBox(i18n("Server Configuration")); @@ -483,4 +486,44 @@ void FtpSharePlugin::onChangePassword() } } +void FtpSharePlugin::onDeleteFtpUser() +{ + const QString username = m_pwUserCombo->currentText(); + if (username.isEmpty()) + return; + + if (!ftpUserExists(username)) { + QMessageBox::information(m_page, + i18n("Delete FTP User"), + i18n("User '%1' has no FTP account.", username)); + return; + } + + const auto result = QMessageBox::question(m_page, + i18n("Delete FTP User"), + i18n("Really delete FTP account for '%1'?\n\n" + "The system user stays untouched. Only the FTP login and\n" + "this user's bind-mounts in the FTP root will be removed.", username), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (result != QMessageBox::Yes) + return; + + QProcess proc; + proc.start(QStringLiteral("dolphin-ftp-share"), + {QStringLiteral("deluser"), username}); + proc.waitForFinished(10000); + + if (proc.exitCode() == 0) { + QMessageBox::information(m_page, + i18n("Delete FTP User"), + i18n("FTP user '%1' deleted.", username)); + } else { + QMessageBox::warning(m_page, + i18n("Delete FTP User"), + i18n("Failed to delete FTP user '%1'.", username)); + } +} + #include "ftpshareplugin.moc" diff --git a/kf6/src/ftpshareplugin.h b/kf6/src/ftpshareplugin.h index 9730472..72c0063 100644 --- a/kf6/src/ftpshareplugin.h +++ b/kf6/src/ftpshareplugin.h @@ -27,6 +27,7 @@ private: void loadServerConfig(); void onShareToggled(bool checked); void onChangePassword(); + void onDeleteFtpUser(); void onApplyServerConfig(); void onModeChanged(); diff --git a/scripts/dolphin-ftp-share b/scripts/dolphin-ftp-share index c013d26..1645351 100755 --- a/scripts/dolphin-ftp-share +++ b/scripts/dolphin-ftp-share @@ -92,7 +92,8 @@ ${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/mount --bind * ${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/mount -o remount?ro?bind * ${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/umount ${REAL_FTP_ROOT}/* ${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/pure-pw * -${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/tee /etc/pure-ftpd/conf/* +${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/tee /etc/pure-ftpd/* +${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/tee -a /etc/pure-ftpd/* ${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/rm -f /etc/pure-ftpd/conf/PassivePortRange ${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/sed -i *pureftpd.passwd* ${REAL_USER} ALL=(root) NOPASSWD: /usr/bin/systemctl restart pure-ftpd @@ -229,6 +230,38 @@ cmd_passwd() { rebuild_db } +cmd_deluser() { + local username="$1" + + if [ -z "$username" ]; then + echo "Error: username is required" >&2 + exit 1 + fi + + # Unmount all shares from this user's root first + local user_root="$FTP_ROOT/users/$username" + if [ -d "$user_root" ]; then + for mp in "$user_root"/*/; do + [ -d "$mp" ] || continue + if mountpoint -q "$mp" 2>/dev/null; then + sudo umount "$mp" + fi + rmdir "$mp" 2>/dev/null || true + done + rmdir "$user_root" 2>/dev/null || true + fi + + # Remove user entry from passwd file + if sudo pure-pw show "$username" -f "$PASSWD_FILE" >/dev/null 2>&1; then + sudo sed -i "/^${username}:/d" "$PASSWD_FILE" + rebuild_db + echo "FTP user '$username' deleted" + else + echo "FTP user '$username' does not exist" + exit 1 + fi +} + cmd_serverconfig() { local action="$1" @@ -276,9 +309,10 @@ case "${1:-}" in delete) cmd_delete "$2" ;; userexists) cmd_userexists "$2" ;; passwd) cmd_passwd "$2" "$3" ;; + deluser) cmd_deluser "$2" ;; serverconfig) cmd_serverconfig "$2" "$3" "$4" ;; *) - echo "Usage: dolphin-ftp-share {setup|info|add|delete|userexists|passwd|serverconfig}" >&2 + echo "Usage: dolphin-ftp-share {setup|info|add|delete|userexists|passwd|deluser|serverconfig}" >&2 echo "" >&2 echo "Commands:" >&2 echo " setup Initial Pure-FTPd setup" >&2 @@ -287,6 +321,7 @@ case "${1:-}" in echo " delete Remove a share" >&2 echo " userexists Check if FTP user exists" >&2 echo " passwd Set/create FTP password" >&2 + echo " deluser Delete FTP user (not system user)" >&2 echo " serverconfig show Show current server config" >&2 echo " serverconfig active Set active mode" >&2 echo " serverconfig passive Set passive mode with port range" >&2