From eb629f8fa6f7edd3ba9f3e76146d71f62dafff57 Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Sun, 3 Mar 2024 22:56:04 +0100 Subject: refractor and add permission manager without save --- config.mk | 2 +- src/main.cpp | 173 +++++++++------------------------------------- src/permissionmanager.cpp | 93 +++++++++++++++++++++++++ src/permissionmanager.hpp | 28 ++++++++ src/tray.cpp | 136 ++++++++++++++++++++++++++++++++++++ src/tray.hpp | 46 ++++++++++++ src/webwindow.cpp | 114 ++++++++++++++++++++++++++++++ src/webwindow.hpp | 37 ++++++++++ 8 files changed, 487 insertions(+), 142 deletions(-) create mode 100644 src/permissionmanager.cpp create mode 100644 src/permissionmanager.hpp create mode 100644 src/tray.cpp create mode 100644 src/tray.hpp create mode 100644 src/webwindow.cpp create mode 100644 src/webwindow.hpp diff --git a/config.mk b/config.mk index 4f04f30..1bbe8b3 100644 --- a/config.mk +++ b/config.mk @@ -1,4 +1,4 @@ TARGET_DIRECTORY=target -CPPFLAGS=`pkg-config --cflags Qt6WebEngineWidgets Qt6WebEngineCore Qt6Core Qt6Gui` +CPPFLAGS=`pkg-config --cflags Qt6WebEngineWidgets Qt6WebEngineCore Qt6Core Qt6Gui` -Wall LDFLAGS=`pkg-config --libs Qt6WebEngineWidgets Qt6WebEngineCore Qt6Core Qt6Gui Qt6Multimedia` -fPIC CC=g++ diff --git a/src/main.cpp b/src/main.cpp index 246d0ad..9c6b3c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,168 +1,59 @@ -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include + #include -class MainWindow : public QMainWindow +#include "permissionmanager.hpp" +#include "tray.hpp" +#include "webwindow.hpp" + +QString +extract_url(const QStringList arguments) { -public: - void closeEvent(QCloseEvent *event) - { - this->hide(); - event->ignore(); + for (const auto &argument : arguments) { + if (!argument.endsWith("webtray") && !argument.startsWith("--")) { + return argument; + } } -}; + return ""; +} int main(int argc, char **argv) { QApplication app(argc, argv); - bool start_hidden = true; - QUrl url; + app.setQuitOnLastWindowClosed(false); - for (auto argument : app.arguments()) { - if (argument == "--open-at-startup") { - start_hidden = false; - } else { - url = argument; - } - } + QString url = extract_url(app.arguments()); - if (url.url().toStdString() == argv[0]) { - std::cerr << "webtray [--open-at-startup]\n"; + if (url.isEmpty()) { + std::cerr << "webtray [--open-at-startup] \n"; return -1; } - MainWindow main_window; - - QSystemTrayIcon tray; - QMenu menu; - - QWebEngineProfile profile(url.host().toStdString().c_str()); - QWebEnginePage page(&profile); - QWebEngineView view; + WebWindow webwindow(url); + Tray tray; + bool start_hidden = not app.arguments().contains("--open-at-startup"); - QAction *app_action = menu.addAction("Element"); - menu.addSeparator(); - QAction *quit_action = menu.addAction("Quit"); - - menu.connect(&menu, &QMenu::triggered, [&](QAction *action) { - if (action == app_action) { - main_window.setVisible(!main_window.isVisible()); - } else if (action == quit_action) { - main_window.close(); - page.windowCloseRequested(); - app.quit(); - } - }); - - view.setPage(&page); - view.setUrl(url); - - tray.setContextMenu(&menu); - - tray.connect(&tray, - &QSystemTrayIcon::activated, - [&](const QSystemTrayIcon::ActivationReason reason) { - switch (reason) { - case QSystemTrayIcon::Trigger: - main_window.setVisible(!main_window.isVisible()); - break; - default: - break; - } - }); - - view.connect(&view, &QWebEngineView::iconChanged, [&](const QIcon icon) { + webwindow.connect_icon_changed([&](auto icon) { tray.setIcon(icon); tray.show(); }); - view.connect(&view, &QWebEngineView::titleChanged, [&](const QString title) { - tray.setToolTip(title); - app_action->setText(title); - }); - - page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); - page.settings()->setAttribute(QWebEngineSettings::WebRTCPublicInterfacesOnly, - false); - page.settings()->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, - false); + webwindow.connect_title_changed([&](auto title) { tray.set_title(title); }); - profile.setPushServiceEnabled(true); - profile.setNotificationPresenter( - [&](std::unique_ptr notification) { - tray.showMessage(notification->title(), - notification->message(), - QSystemTrayIcon::MessageIcon::Information, - 3000); - }); - - page.connect(&page, - &QWebEnginePage::featurePermissionRequested, - [&](const QUrl origin, QWebEnginePage::Feature feature) { - QString feature_name; - - switch (feature) { - case QWebEnginePage::Feature::MouseLock: - feature_name = "lock the mouse"; - break; - case QWebEnginePage::Feature::Geolocation: - feature_name = "your location information"; - break; - case QWebEnginePage::Feature::Notifications: - feature_name = "send notifications"; - break; - case QWebEnginePage::Feature::MediaAudioCapture: - feature_name = "capture audio"; - break; - case QWebEnginePage::Feature::MediaVideoCapture: - feature_name = "capture video"; - break; - case QWebEnginePage::Feature::MediaAudioVideoCapture: - feature_name = "capture audio and video"; - break; - case QWebEnginePage::Feature::DesktopVideoCapture: - feature_name = "capture video from your desktop"; - break; - case QWebEnginePage::Feature::DesktopAudioVideoCapture: - feature_name = "capture audio and video from your desktop"; - break; - } - - QMessageBox dialog(QMessageBox::Icon::Question, - "Permission", - "Do you want to grant permission to " + - feature_name + "?", - QMessageBox::StandardButton::Yes | - QMessageBox::StandardButton::No); - int res = dialog.exec(); + webwindow.connect_notification([&](auto notification) { + tray.send_notification(std::move(notification)); + }); - if (res == QMessageBox::Yes) { - page.setFeaturePermission( - origin, feature, QWebEnginePage::PermissionGrantedByUser); - } else { - page.setFeaturePermission( - origin, feature, QWebEnginePage::PermissionDeniedByUser); - } - }); + tray.connect_toggle([&]() { webwindow.toggle_visibility(); }); + tray.connect_quit([&]() { webwindow.quit(); }); + tray.connect_permission_changed( + [&](auto feature, auto value) { webwindow.set_feature(feature, value); }); + tray.connect_reset_cookies([&]() { webwindow.reset_cookies(); }); - main_window.setCentralWidget(&view); - main_window.show(); + webwindow.show(); if (start_hidden) { - main_window.hide(); + webwindow.hide(); } - app.setQuitOnLastWindowClosed(false); return app.exec(); } diff --git a/src/permissionmanager.cpp b/src/permissionmanager.cpp new file mode 100644 index 0000000..0687c8a --- /dev/null +++ b/src/permissionmanager.cpp @@ -0,0 +1,93 @@ +#include "permissionmanager.hpp" + +PermissionManager::PermissionManager(std::string path) + : path(path) +{ + if (std::filesystem::exists(this->path)) { + std::ifstream in(path); + std::string content; + in >> this->_lock_mouse; + in >> this->_location; + in >> this->_notification; + in >> this->_media_audio_capture; + in >> this->_media_video_capture; + in >> this->_desktop_video_capture; + in >> this->_desktop_audio_video_capture; + } +} + +PermissionManager::~PermissionManager() +{ + this->save(); +} + +void +PermissionManager::save() +{ + std::ofstream out(this->path); + out << this->_lock_mouse; + out << this->_location; + out << this->_notification; + out << this->_media_audio_capture; + out << this->_media_video_capture; + out << this->_desktop_video_capture; + out << this->_desktop_audio_video_capture; +} + +bool +PermissionManager::get(QWebEnginePage::Feature feature) +{ + switch (feature) { + case QWebEnginePage::Feature::MouseLock: + return this->_lock_mouse; + case QWebEnginePage::Feature::Geolocation: + return this->_location; + case QWebEnginePage::Feature::Notifications: + return this->_notification; + case QWebEnginePage::Feature::MediaAudioCapture: + return this->_media_audio_capture; + case QWebEnginePage::Feature::MediaVideoCapture: + return this->_media_video_capture; + case QWebEnginePage::Feature::MediaAudioVideoCapture: + return this->_media_video_capture && this->_media_audio_capture; + case QWebEnginePage::Feature::DesktopVideoCapture: + return this->_desktop_video_capture; + case QWebEnginePage::Feature::DesktopAudioVideoCapture: + return this->_desktop_audio_video_capture; + default: + /* unreachable except QWebEnginePage::Feature gets new entries */ + return false; + } +} + +void +PermissionManager::set(QWebEnginePage::Feature feature, bool value) +{ + switch (feature) { + case QWebEnginePage::Feature::MouseLock: + this->_lock_mouse = value; + break; + case QWebEnginePage::Feature::Geolocation: + this->_location = value; + break; + case QWebEnginePage::Feature::Notifications: + this->_notification = value; + break; + case QWebEnginePage::Feature::MediaAudioCapture: + this->_media_audio_capture = value; + break; + case QWebEnginePage::Feature::MediaVideoCapture: + this->_media_video_capture = value; + break; + case QWebEnginePage::Feature::MediaAudioVideoCapture: + this->_media_audio_capture = value; + this->_media_video_capture = value; + break; + case QWebEnginePage::Feature::DesktopVideoCapture: + this->_desktop_video_capture = value; + break; + case QWebEnginePage::Feature::DesktopAudioVideoCapture: + this->_desktop_audio_video_capture = value; + break; + } +} diff --git a/src/permissionmanager.hpp b/src/permissionmanager.hpp new file mode 100644 index 0000000..70c17e9 --- /dev/null +++ b/src/permissionmanager.hpp @@ -0,0 +1,28 @@ +#ifndef PERMISSION_MANAGER_HPP +#define PERMISSION_MANAGER_HPP + +#include +#include +#include + +class PermissionManager +{ +private: + std::string path; + bool _lock_mouse = false; + bool _location = false; + bool _notification = false; + bool _media_audio_capture = false; + bool _media_video_capture = false; + bool _desktop_video_capture = false; + bool _desktop_audio_video_capture = false; + +public: + PermissionManager(std::string path); + ~PermissionManager(); + void save(); + bool get(QWebEnginePage::Feature feature); + void set(QWebEnginePage::Feature feature, bool value); +}; + +#endif diff --git a/src/tray.cpp b/src/tray.cpp new file mode 100644 index 0000000..c6e48a8 --- /dev/null +++ b/src/tray.cpp @@ -0,0 +1,136 @@ +#include "tray.hpp" + +Tray::Tray() +{ + this->app_toggle = this->menu.addAction(""); + this->menu.addSeparator(); + this->menu.addMenu(&this->settings)->setText("Settings"); + this->menu.addSeparator(); + this->quit = this->menu.addAction("Quit"); + + this->permissions.camera = this->settings.addAction("Allow Camera"); + this->permissions.microphone = this->settings.addAction("Allow Microphone"); + this->permissions.screenshare = this->settings.addAction("Allow Screenshare"); + this->permissions.system_audio = + this->settings.addAction("Allow System Audio Record"); + this->permissions.notifications = + this->settings.addAction("Allow Notifications"); + this->permissions.location = this->settings.addAction("Allow Location"); + this->permissions.lock_mouse = this->settings.addAction("Allow Lock Mouse"); + this->settings.addSeparator(); + this->reset_storage = this->settings.addAction("Reset Storage"); + + this->permissions.camera->setCheckable(true); + this->permissions.microphone->setCheckable(true); + this->permissions.screenshare->setCheckable(true); + this->permissions.system_audio->setCheckable(true); + this->permissions.notifications->setCheckable(true); + this->permissions.location->setCheckable(true); + this->permissions.lock_mouse->setCheckable(true); + + this->connect(this, + &QSystemTrayIcon::activated, + [&](QSystemTrayIcon::ActivationReason reason) { + if (reason == QSystemTrayIcon::ActivationReason::Trigger) { + this->toggle_signal(); + } + }); + + this->menu.connect(&this->menu, &QMenu::triggered, [&](QAction *action) { + if (action == this->app_toggle) { + this->toggle_signal(); + } else if (action == quit) { + this->quit_signal(); + } + }); + + this->settings.connect( + &this->settings, &QMenu::triggered, [&](QAction *action) { + if (action == this->reset_storage) { + this->reset_cookies_signal(); + + } else if (action == this->permissions.camera) { + this->permission_changed_signal( + QWebEnginePage::Feature::MediaVideoCapture, + this->permissions.camera->isChecked()); + + } else if (action == this->permissions.microphone) { + this->permission_changed_signal( + QWebEnginePage::Feature::MediaAudioCapture, + this->permissions.microphone->isChecked()); + + } else if (action == this->permissions.system_audio) { + this->permission_changed_signal( + QWebEnginePage::Feature::DesktopAudioVideoCapture, + this->permissions.system_audio->isChecked()); + + } else if (action == this->permissions.screenshare) { + this->permission_changed_signal( + QWebEnginePage::Feature::DesktopVideoCapture, + this->permissions.screenshare->isChecked()); + + } else if (action == this->permissions.notifications) { + this->permission_changed_signal( + QWebEnginePage::Feature::Notifications, + this->permissions.notifications->isChecked()); + + } else if (action == this->permissions.location) { + this->permission_changed_signal( + QWebEnginePage::Feature::Geolocation, + this->permissions.location->isChecked()); + + } else if (action == this->permissions.lock_mouse) { + this->permission_changed_signal( + QWebEnginePage::Feature::MouseLock, + this->permissions.lock_mouse->isChecked()); + } + }); + + this->setContextMenu(&this->menu); +} + +void +Tray::connect_toggle(std::function fn) +{ + this->toggle_signal = fn; +} + +void +Tray::connect_quit(std::function fn) +{ + this->quit_signal = fn; +} + +void +Tray::connect_reset_cookies(std::function fn) +{ + this->reset_cookies_signal = fn; +} + +void +Tray::connect_permission_changed( + std::function fn) +{ + this->permission_changed_signal = fn; +} + +void +Tray::set_title(const QString &title) +{ + this->setToolTip(title); + this->app_toggle->setText(title); +} + +void +Tray::send_notification(std::unique_ptr notification) +{ + this->showMessage(notification->title(), + notification->message(), + QSystemTrayIcon::MessageIcon::Information, + 2000); +} + +void +Tray::set_permission(QWebEnginePage::Feature feature, bool value) +{ +} diff --git a/src/tray.hpp b/src/tray.hpp new file mode 100644 index 0000000..427cdc8 --- /dev/null +++ b/src/tray.hpp @@ -0,0 +1,46 @@ +#ifndef TRAY_HPP +#define TRAY_HPP + +#include +#include + +#include "webwindow.hpp" + +class Tray : public QSystemTrayIcon +{ +private: + QMenu menu; + QMenu settings; + QAction *app_toggle; + QAction *quit; + + struct + { + QAction *camera; + QAction *microphone; + QAction *screenshare; + QAction *system_audio; + QAction *notifications; + QAction *location; + QAction *lock_mouse; + } permissions; + + QAction *reset_storage; + std::function toggle_signal; + std::function quit_signal; + std::function reset_cookies_signal; + std::function permission_changed_signal; + +public: + Tray(); + void connect_toggle(std::function fn); + void connect_quit(std::function fn); + void connect_reset_cookies(std::function fn); + void connect_permission_changed( + std::function fn); + void set_title(const QString &title); + void send_notification(std::unique_ptr notification); + void set_permission(QWebEnginePage::Feature feature, bool value); +}; + +#endif diff --git a/src/webwindow.cpp b/src/webwindow.cpp new file mode 100644 index 0000000..36e1818 --- /dev/null +++ b/src/webwindow.cpp @@ -0,0 +1,114 @@ +#include +#include + +#include "permissionmanager.hpp" +#include "webwindow.hpp" +#include +#include + +WebWindow::WebWindow(const QString &url) + : QMainWindow() + , _profile(QUrl(url).host()) + , _page(&_profile) + , _permissions((QUrl(url).host() + "/permissions.state").toStdString()) +{ + this->web_configure(); + + this->_profile.setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies); + + this->_view.setPage(&this->_page); + this->_view.setUrl(url); + + this->setCentralWidget(&this->_view); +} + +void +WebWindow::web_configure() +{ + this->_page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, + true); + + this->_page.settings()->setAttribute( + QWebEngineSettings::WebRTCPublicInterfacesOnly, false); + + this->_page.settings()->setAttribute( + QWebEngineSettings::ScrollAnimatorEnabled, false); + + this->_profile.setPushServiceEnabled(true); + + this->_page.connect(&this->_page, + &QWebEnginePage::featurePermissionRequested, + [&](const QUrl origin, QWebEnginePage::Feature feature) { + this->permission_requested(origin, feature); + }); +} + +void +WebWindow::permission_requested(const QUrl origin, + QWebEnginePage::Feature feature) +{ + if (this->_permissions.get(feature)) { + this->_page.setFeaturePermission( + origin, feature, QWebEnginePage::PermissionGrantedByUser); + } else { + this->_page.setFeaturePermission( + origin, feature, QWebEnginePage::PermissionDeniedByUser); + } + this->_page.setFeaturePermission( + origin, feature, QWebEnginePage::PermissionUnknown); +} + +void +WebWindow::closeEvent(QCloseEvent *event) +{ + this->hide(); + event->ignore(); +} + +void +WebWindow::connect_icon_changed(std::function fn) +{ + this->_view.connect(&this->_view, &QWebEngineView::iconChanged, fn); +} + +void +WebWindow::connect_notification( + std::function)> fn) +{ + this->_profile.setNotificationPresenter(fn); +} + +void +WebWindow::connect_title_changed(std::function fn) +{ + this->_view.connect(&this->_view, &QWebEngineView::titleChanged, fn); +} + +void +WebWindow::set_feature(QWebEnginePage::Feature feature, bool value) +{ + this->_permissions.set(feature, value); +} + +void +WebWindow::reset_cookies() +{ + this->_profile.cookieStore()->deleteAllCookies(); + this->_profile.cookieStore()->loadAllCookies(); + this->_profile.clearHttpCache(); + this->_profile.clearAllVisitedLinks(); + this->_profile.clientCertificateStore(); + this->_view.reload(); +} + +void +WebWindow::toggle_visibility() +{ + this->setVisible(!this->isVisible()); +} + +void +WebWindow::quit() +{ + QApplication::instance()->quit(); +} diff --git a/src/webwindow.hpp b/src/webwindow.hpp new file mode 100644 index 0000000..49b2394 --- /dev/null +++ b/src/webwindow.hpp @@ -0,0 +1,37 @@ +#ifndef MAINWINDOW_HPP +#define MAINWINDOW_HPP + +#include "permissionmanager.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +class WebWindow : public QMainWindow +{ +private: + QWebEngineProfile _profile; + QWebEnginePage _page; + QWebEngineView _view; + PermissionManager _permissions; + + void web_configure(); + void permission_requested(const QUrl origin, QWebEnginePage::Feature feature); + void closeEvent(QCloseEvent *event); + +public: + WebWindow(const QString &url); + void connect_icon_changed(std::function fn); + void connect_title_changed(std::function fn); + void connect_notification(std::function)> fn); + void set_feature(QWebEnginePage::Feature feature, bool value); + void reset_cookies(); + void toggle_visibility(); + void quit(); +}; + +#endif -- cgit v1.2.3-70-g09d2