From a5eb53fcbc12342f0a12ef4819146b2d0bb14652 Mon Sep 17 00:00:00 2001 From: Mavridis Philippe Date: Mon, 8 Jul 2024 18:58:28 +0300 Subject: [PATCH] Added Account portal, permission dialog class, moved some code to util Signed-off-by: Mavridis Philippe --- README.md | 2 +- interfaces/CMakeLists.txt | 2 +- interfaces/interfaces.xml | 12 +++ src/CMakeLists.txt | 4 +- src/account_portal.cpp | 209 ++++++++++++++++++++++++++++++++++++ src/account_portal.h | 73 +++++++++++++ src/email_portal.cpp | 1 - src/file_chooser_portal.cpp | 20 +--- src/permission_dialog.cpp | 100 +++++++++++++++++ src/permission_dialog.h | 54 ++++++++++ src/portal_service.cpp | 9 +- src/util.cpp | 54 ++++++++++ src/util.h | 13 +++ 13 files changed, 528 insertions(+), 25 deletions(-) create mode 100644 src/account_portal.cpp create mode 100644 src/account_portal.h create mode 100644 src/permission_dialog.cpp create mode 100644 src/permission_dialog.h diff --git a/README.md b/README.md index b2b3d5a..b523631 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ if they are set to 2, set them to 1. ## State of implementation Currently implemented are the following interfaces: +* Account - request user information (user ID, full name, user icon) * Email - request to send an e-mail via the system e-mail client * FileChooser - request a file dialog @@ -29,7 +30,6 @@ The following implementations are planned (listed in no particular order): * Clipboard - request clipboard access (*) * OpenURI - request to open a URL * Secret - integration with TDEWallet -* Account - request user information (username, full name, user icon) * Access - show a grant access dialog * Wallpaper - request to change the user's wallpaper * Print - request a file to be printed diff --git a/interfaces/CMakeLists.txt b/interfaces/CMakeLists.txt index d8e44c0..f4bb8e2 100644 --- a/interfaces/CMakeLists.txt +++ b/interfaces/CMakeLists.txt @@ -15,7 +15,7 @@ link_directories( ${DBUS_TQT_LIBRARY_DIRS} ) -set(interfaces filechooser email) +set(interfaces account email filechooser) set(interface_HDRS introspectableInterface.h desktopNode.h dbusbaseNode.h) set(interface_SRCS introspectableInterface.cpp desktopNode.cpp dbusbaseNode.cpp) diff --git a/interfaces/interfaces.xml b/interfaces/interfaces.xml index 003cdbd..c6f5357 100644 --- a/interfaces/interfaces.xml +++ b/interfaces/interfaces.xml @@ -1,5 +1,17 @@ + + + + + + + + + + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ed40be..b094414 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,8 +28,10 @@ tde_add_executable( main.cpp portal_daemon.cpp portal_service.cpp - file_chooser_portal.cpp + account_portal.cpp email_portal.cpp + file_chooser_portal.cpp + permission_dialog.cpp util.cpp LINK diff --git a/src/account_portal.cpp b/src/account_portal.cpp new file mode 100644 index 0000000..a20559b --- /dev/null +++ b/src/account_portal.cpp @@ -0,0 +1,209 @@ +/******************************************************************************* + XDG desktop portal implementation for TDE + Copyright © 2024 Mavridis Philippe + + Avatar detection code is based on code from the Redmond KSplash theme + Copyright © 2001-2003 Brian Ledbetter 2001-2003 + Copyright © 2003 Ravikiran Rajagopal 2003 + + This program or library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Improvements and feedback are welcome! +*******************************************************************************/ + +// TQt +#include +#include +#include +#include + +// TDE +#include +#include +#include +#include +#include +#include + +// Portal +#include "permission_dialog.h" +#include "account_portal.h" +#include "account_portal.moc" + +#define TQSTRING_TO_DBUS_VARIANT(x) \ + TQT_DBusData::fromString(x).getAsVariantData().toVariant() + +TDEAccountPortal::TDEAccountPortal(TQT_DBusConnection &connection) +: m_connection(connection) +{ +} + +TDEAccountPortal::~TDEAccountPortal() +{ +} + +void TDEAccountPortal::handleMethodReply(const TQT_DBusMessage &reply) +{ + m_connection.send(reply); +} + +bool TDEAccountPortal::handleSignalSend(const TQT_DBusMessage& reply) { + handleMethodReply(reply); + return true; +} + +bool TDEAccountPortal::GetUserInformation(const TQT_DBusObjectPath& handle, + const TQString& app_id, + const TQString& window, + const TQT_DBusVariantMap& options, + TQ_UINT32& response, + TQT_DBusVariantMap& results, + TQT_DBusError& error) +{ + Dictionary details; + + if (OPTION_VALID("reason", "s")) + { + TQString reason(options["reason"].value.toString()); + if (!reason.isEmpty()) + { + details[i18n("Reason")] = reason; + } + } + + // Try to detect which application requested the permission + WId wid = parse_window_id(window); + ApplicationInfo app = application_info_from_wid(wid); + + if (!app.path.isEmpty()) + details[i18n("Path")] = app.path; + + // Run the dialog + TDEPermissionDialog *dialog = new TDEPermissionDialog( + app.name, + i18n("Account information"), + "user-info", + details + ); + AccountInfo info = getAccountInfo(); + appendDataPreview(dialog, info); + + if (wid > 0) KWin::setMainWindow(dialog, wid); + + if (dialog->exec() == KDialogBase::Yes) + { + response = 0; + results.insert("id", TQSTRING_TO_DBUS_VARIANT(info.userId)); + results.insert("name", TQSTRING_TO_DBUS_VARIANT(info.realName)); + results.insert("image", TQSTRING_TO_DBUS_VARIANT(info.avatarPath)); + } + else response = 1; + + delete dialog; + return true; +} + +AccountInfo TDEAccountPortal::getAccountInfo() +{ + AccountInfo info; + KUser user; + info.userId = TQString::number(user.uid()); + info.loginName = user.loginName(); + info.realName = user.fullName(); + info.homeDirectory = user.homeDir(); + findUserAvatar(info); + return info; +} + +void TDEAccountPortal::findUserAvatar(AccountInfo &info) +{ + // Parse tdmrc settings to determine face source and system location + const int fAdminOnly = 1; + const int fAdminFirst = fAdminOnly + 1; + const int fUserFirst = fAdminFirst + 1; + const int fUserOnly = fUserFirst + 1; + + int faceSource = fAdminOnly; + TDEConfig *tdmconfig = new TDEConfig("tdm/tdmrc", true); + tdmconfig->setGroup("X-*-Greeter"); + + TQString fs = tdmconfig->readEntry("FaceSource"); + if (fs == TQString::fromLatin1("UserOnly")) + faceSource = fUserOnly; + else if (fs == TQString::fromLatin1("PreferUser")) + faceSource = fUserFirst; + else if (fs == TQString::fromLatin1("PreferAdmin")) + faceSource = fAdminFirst; + else + faceSource = fAdminOnly; + + TQString userPicsDir = tdmconfig->readEntry("FaceDir", + TDEGlobal::dirs()->resourceDirs("data").last() + "tdm/faces") + '/'; + + delete tdmconfig; + + // Faces provided by administrator (default and per user) + const TQString systemDefault(userPicsDir + ".default.face.icon"); + const TQString systemUser(userPicsDir + info.loginName + ".face.icon"); + + TQString avatar; + if (faceSource == fAdminFirst) + { + avatar = systemUser; + if (!TQFile::exists(avatar)) + faceSource = fUserOnly; + } + + if (faceSource >= fUserFirst) + { + avatar = info.homeDirectory + "/.face.icon"; + if (!TQFile::exists(avatar) && faceSource == fUserFirst) + avatar = systemUser; + + if (!TQFile::exists(avatar)) + avatar = systemDefault; + } + + if (faceSource <= fAdminOnly) + { + avatar = systemUser; + if (!TQFile::exists(avatar)) + avatar = systemDefault; + } + + info.avatarPath = avatar; +} + +void TDEAccountPortal::appendDataPreview(TDEPermissionDialog *dlg, AccountInfo info) +{ + TQHBox *frame = new TQHBox(dlg); + frame->setFrameStyle(TQFrame::StyledPanel | TQFrame::Sunken); + + TQLabel *avatar = new TQLabel(frame); + avatar->setPixmap(TQPixmap(info.avatarPath)); + + TQString userDataStr = "

%1


ID: %2
"; + userDataStr = userDataStr.arg(info.realName, info.userId); + TQLabel *userData = new TQLabel(userDataStr, frame); + + avatar->setMargin(TDEPermissionDialog::spacingHint()); + userData->setMargin(TDEPermissionDialog::spacingHint()); + + frame->setSizePolicy(TQSizePolicy::MinimumExpanding, TQSizePolicy::Fixed); + + dlg->appendWidget(frame); +} + +// kate: replace-tabs true; tab-width 4; indent-width 4; \ No newline at end of file diff --git a/src/account_portal.h b/src/account_portal.h new file mode 100644 index 0000000..b75fe26 --- /dev/null +++ b/src/account_portal.h @@ -0,0 +1,73 @@ +/******************************************************************************* + XDG desktop portal implementation for TDE + Copyright © 2024 Mavridis Philippe + + This program or library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Improvements and feedback are welcome! +*******************************************************************************/ + +#ifndef __ACCOUNT_PORTAL_H +#define __ACCOUNT_PORTAL_H + +// Portal +#include "interfaces/accountInterface.h" +#include "interface.h" + +struct AccountInfo +{ + TQString userId; + TQString loginName; + TQString realName; + TQString homeDirectory; + TQString avatarPath; +}; + +class TDEPermissionDialog; + +class TDEAccountPortal : public TQObject, + public org::freedesktop::impl::portal::AccountInterface +{ + public: + INTERFACE("org.freedesktop.impl.portal.Account") + + TDEAccountPortal(TQT_DBusConnection &connection); + virtual ~TDEAccountPortal(); + + protected: + virtual void handleMethodReply(const TQT_DBusMessage& reply); + virtual bool handleSignalSend(const TQT_DBusMessage& reply); + + virtual bool GetUserInformation(const TQT_DBusObjectPath& handle, + const TQString& app_id, + const TQString& window, + const TQT_DBusVariantMap& options, + TQ_UINT32& response, + TQT_DBusVariantMap& results, + TQT_DBusError& error); + + AccountInfo getAccountInfo(); + void appendDataPreview(TDEPermissionDialog *dlg, AccountInfo info); + + private: + void findUserAvatar(AccountInfo &info); + + private: + TQT_DBusConnection m_connection; +}; + +#endif // __ACCOUNT_PORTAL_H + +// kate: replace-tabs true; tab-width 4; indent-width 4; \ No newline at end of file diff --git a/src/email_portal.cpp b/src/email_portal.cpp index d12ba50..090ed8b 100644 --- a/src/email_portal.cpp +++ b/src/email_portal.cpp @@ -53,7 +53,6 @@ bool TDEEmailPortal::ComposeEmail(const TQT_DBusObjectPath& handle, TQT_DBusVariantMap &results, TQT_DBusError& error) { - // void invokeMailer (const TQString &to, const TQString &cc, const TQString &bcc, const TQString &subject, const TQString &body, const TQString &messageFile=TQString::null, const TQStringList &attachURLs=TQStringList()) EmailOpts opts; if (OPTION_VALID("address", "s")) opts.rcpt << options["address"].value.toString(); diff --git a/src/file_chooser_portal.cpp b/src/file_chooser_portal.cpp index 4668abd..9b8f6e7 100644 --- a/src/file_chooser_portal.cpp +++ b/src/file_chooser_portal.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include // Portal @@ -69,7 +68,7 @@ bool TDEFileChooserPortal::OpenFile(const TQT_DBusObjectPath& handle, if (OPTION_VALID("filters", "a(sa(us))")) opts.filters = parseFilterList(options["filters"], options["current_filter"]); - opts.windowId = parseWindowId(parent_window); + opts.windowId = parse_window_id(parent_window); return execFileDialog(opts, handle, response, results, error); } @@ -108,7 +107,7 @@ bool TDEFileChooserPortal::SaveFile(const TQT_DBusObjectPath& handle, if (OPTION_VALID("current_name", "s")) opts.startName = options["current_name"].value.toString(); - opts.windowId = parseWindowId(parent_window); + opts.windowId = parse_window_id(parent_window); return execFileDialog(opts, handle, response, results, error); } @@ -255,21 +254,6 @@ bool TDEFileChooserPortal::execFileDialog(FileDialogOpts options, return true; } -WId TDEFileChooserPortal::parseWindowId(const TQString data) -{ - if (!data.startsWith("x11:")) - { - kdWarning() << "[FileChooser] Window Identifiers are currently only " - << "supported for X11. Created dialog will be parentless." - << endl; - return 0; - } - - bool ok; - WId wid = data.mid(4).toInt(&ok, 16); - return ok ? wid : 0; -} - TQString TDEFileChooserPortal::parseFilter(const TQT_DBusData data) { TQStringList patternList; diff --git a/src/permission_dialog.cpp b/src/permission_dialog.cpp new file mode 100644 index 0000000..310bebb --- /dev/null +++ b/src/permission_dialog.cpp @@ -0,0 +1,100 @@ +/******************************************************************************* + XDG desktop portal implementation for TDE + Copyright © 2024 Mavridis Philippe + + This program or library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Improvements and feedback are welcome! +*******************************************************************************/ + +// TQt +#include +#include +#include +#include + +// TDE +#include +#include +#include + +// Portal +#include "permission_dialog.h" +#include "permission_dialog.moc" + +TDEPermissionDialog::TDEPermissionDialog(TQString application, TQString feature, + TQString icon, Dictionary details) + +: KDialogBase(TQString::null, /* we set plain caption below */ + KDialogBase::Yes | KDialogBase::No | KDialogBase::Details, + KDialogBase::NoDefault, KDialogBase::No, + nullptr, "permissiondlg", true, true, + KGuiItem(i18n("&Allow"), "button_ok"), + KGuiItem(i18n("&Deny"), "button_cancel")), + + m_vbox(new TQVBox(this)), + m_hbox(new TQHBox(m_vbox)), + m_icon(new TQLabel(m_hbox)), + m_text(new TQLabel(m_hbox)), + m_detail(new TQLabel(this)) + +{ + setPlainCaption(i18n("Permission request")); + + TQString text; + if (!application.isEmpty()) + { + text = "Application \"%1\" has requested the following permission: %2"; + text = text.arg(application); + } + else + text = "An unknown application has requested the following permission: %1"; + m_text->setText(text.arg(feature)); + + m_icon->setPixmap(DesktopIcon(icon)); + + m_icon->setSizePolicy(TQSizePolicy::Fixed, TQSizePolicy::Fixed); + m_text->setSizePolicy(TQSizePolicy::Minimum, TQSizePolicy::Fixed); + + TQString detailStr = ""; + for (Dictionary::iterator it = details.begin(); it != details.end(); ++it) + { + detailStr += TQString("") + .arg(it.key(), it.data()); + } + detailStr += "
%1:%2
"; + m_detail->setText(detailStr); + + setMainWidget(m_vbox); + setDetailsWidget(m_detail); + + m_hbox->setSizePolicy(TQSizePolicy::Preferred, TQSizePolicy::Fixed); + +// m_vbox->setMargin(KDialogBase::marginHint()); + m_icon->setMargin(KDialogBase::spacingHint()); + m_text->setMargin(KDialogBase::spacingHint()); + raise(); +} + +TDEPermissionDialog::~TDEPermissionDialog() +{ +} + +void TDEPermissionDialog::appendWidget(TQWidget *widget) +{ + m_vbox->hide(); + widget->reparent(m_vbox, TQPoint()); + m_vbox->show(); +} \ No newline at end of file diff --git a/src/permission_dialog.h b/src/permission_dialog.h new file mode 100644 index 0000000..9fe3a99 --- /dev/null +++ b/src/permission_dialog.h @@ -0,0 +1,54 @@ +/******************************************************************************* + XDG desktop portal implementation for TDE + Copyright © 2024 Mavridis Philippe + + This program or library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + Improvements and feedback are welcome! +*******************************************************************************/ + +#ifndef __PERMISSION_DIALOG_H +#define __PERMISSION_DIALOG_H + +// TDE +#include + +typedef TQMap Dictionary; + +class TQVBox; +class TQHBox; +class TQLabel; + +class TDEPermissionDialog : public KDialogBase +{ + TQ_OBJECT + + public: + TDEPermissionDialog(TQString application, TQString feature, + TQString icon = "messagebox_question", + Dictionary details = {}); + virtual ~TDEPermissionDialog(); + + void appendWidget(TQWidget *widget); + + private: + TQVBox *m_vbox; + TQHBox *m_hbox; + TQLabel *m_icon; + TQLabel *m_text; + TQLabel *m_detail; +}; + +#endif // __PERMISSION_DIALOG_H \ No newline at end of file diff --git a/src/portal_service.cpp b/src/portal_service.cpp index 280078d..0344830 100644 --- a/src/portal_service.cpp +++ b/src/portal_service.cpp @@ -30,8 +30,9 @@ #include // Portal -#include "file_chooser_portal.h" +#include "account_portal.h" #include "email_portal.h" +#include "file_chooser_portal.h" #include "portal_service.h" @@ -44,16 +45,18 @@ DesktopNodeService::DesktopNodeService(TQT_DBusConnection &connection) { m_interfaces.insert("org.freedesktop.DBus.Introspectable", this); - REGISTER_PORTAL(TDEFileChooserPortal) + REGISTER_PORTAL(TDEAccountPortal) REGISTER_PORTAL(TDEEmailPortal) + REGISTER_PORTAL(TDEFileChooserPortal) registerObject(connection, dbusObjectPath); } DesktopNodeService::~DesktopNodeService() { - DESTROY_PORTAL(TDEFileChooserPortal) + DESTROY_PORTAL(TDEAccountPortal) DESTROY_PORTAL(TDEEmailPortal) + DESTROY_PORTAL(TDEFileChooserPortal) } TQT_DBusObjectBase* DesktopNodeService::createInterface(const TQString& iface) diff --git a/src/util.cpp b/src/util.cpp index 7474fa1..4975663 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -19,12 +19,66 @@ Improvements and feedback are welcome! *******************************************************************************/ +// TQt +#include + // TDE +#include #include // Portal #include "util.h" +WId parse_window_id(const TQString data) +{ + if (!data.startsWith("x11:")) + { + kdWarning() << "[FileChooser] Window Identifiers are currently only " + << "supported for X11. Created dialog will be parentless." + << endl; + return 0; + } + + bool ok; + WId wid = data.mid(4).toInt(&ok, 16); + return ok ? wid : 0; +} + +ApplicationInfo application_info_from_wid(WId wid) +{ + ApplicationInfo app; + if (wid) + { + // we need to get the pid but only deprecated KWin::Info has it + KWin::Info info = KWin::info(wid); + TQFileInfo fi(TQString("/proc/%1/exe").arg(info.pid)); + if (fi.exists() && fi.isSymLink()) { + fi = TQFileInfo(fi.readLink()); + + app.path = fi.filePath(); + + // try to find corresponding desktop file + KService::List all = KService::allServices(); + KService::List::ConstIterator svc; + for (svc = all.begin(); svc != all.end(); ++svc) { + TQString exec((*svc)->exec()); + if (exec.startsWith(fi.filePath()) || + exec.startsWith(fi.fileName())) + { + app.desktopFile = (*svc)->desktopEntryPath(); + app.name = (*svc)->name(); + break; + } + } + + // Last resort + if (app.name.isEmpty()) + app.name = fi.fileName(); + } + } + return app; +} + bool check_variant(TQT_DBusVariant variant, TQString signature) { return !variant.signature.isNull() && variant.signature == signature; diff --git a/src/util.h b/src/util.h index f27b7b8..e5a2040 100644 --- a/src/util.h +++ b/src/util.h @@ -26,6 +26,7 @@ #include #include #include +#include // TDE #include @@ -33,6 +34,18 @@ typedef TQMap TQT_DBusVariantMap; typedef TQValueList TQT_DBusValueList; +struct ApplicationInfo +{ + TQString name; + TQString path; + TQString desktopFile; + int pid; +}; + +extern WId parse_window_id(const TQString data); + +extern ApplicationInfo application_info_from_wid(WId wid); + extern bool check_variant(TQT_DBusVariant variant, TQString signature); extern TQString bytelist_to_string(TQT_DBusDataList list);