summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--example/hyprland.conf4
-rw-r--r--src/config/ConfigManager.cpp6
-rw-r--r--src/managers/permissions/DynamicPermissionManager.cpp19
-rw-r--r--src/managers/permissions/DynamicPermissionManager.hpp2
-rw-r--r--src/protocols/DataDeviceWlr.cpp32
-rw-r--r--src/protocols/core/DataDevice.cpp83
-rw-r--r--src/xwayland/XWM.cpp31
7 files changed, 175 insertions, 2 deletions
diff --git a/example/hyprland.conf b/example/hyprland.conf
index 98ac0996..9c88dcdb 100644
--- a/example/hyprland.conf
+++ b/example/hyprland.conf
@@ -67,6 +67,10 @@ env = HYPRCURSOR_SIZE,24
# permission = /usr/(bin|local/bin)/grim, screencopy, allow
# permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
# permission = /usr/(bin|local/bin)/hyprpm, plugin, allow
+# permission = /usr/bin/clipman, clipboard-read, allow
+# permission = /usr/bin/clipman, clipboard-write, allow
+# permission = /usr/bin/copyq, clipboard-read, allow
+# permission = /usr/bin/copyq, clipboard-write, allow
#####################
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 046a2667..979a4228 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -2833,6 +2833,12 @@ std::optional<std::string> CConfigManager::handlePermission(const std::string& c
type = PERMISSION_TYPE_PLUGIN;
else if (data[1] == "keyboard" || data[1] == "keeb")
type = PERMISSION_TYPE_KEYBOARD;
+ else if (data[1] == "clipboard" || data[1] == "clip")
+ type = PERMISSION_TYPE_CLIPBOARD_WRITE; // default to write for backward compatibility
+ else if (data[1] == "clipboard-read" || data[1] == "clip-read")
+ type = PERMISSION_TYPE_CLIPBOARD_READ;
+ else if (data[1] == "clipboard-write" || data[1] == "clip-write")
+ type = PERMISSION_TYPE_CLIPBOARD_WRITE;
if (data[2] == "ask")
mode = PERMISSION_RULE_ALLOW_MODE_ASK;
diff --git a/src/managers/permissions/DynamicPermissionManager.cpp b/src/managers/permissions/DynamicPermissionManager.cpp
index e2865459..62c6fb67 100644
--- a/src/managers/permissions/DynamicPermissionManager.cpp
+++ b/src/managers/permissions/DynamicPermissionManager.cpp
@@ -53,6 +53,21 @@ static const char* permissionToString(eDynamicPermissionType type) {
case PERMISSION_TYPE_SCREENCOPY: return "PERMISSION_TYPE_SCREENCOPY";
case PERMISSION_TYPE_PLUGIN: return "PERMISSION_TYPE_PLUGIN";
case PERMISSION_TYPE_KEYBOARD: return "PERMISSION_TYPE_KEYBOARD";
+ case PERMISSION_TYPE_CLIPBOARD_READ: return "PERMISSION_TYPE_CLIPBOARD_READ";
+ case PERMISSION_TYPE_CLIPBOARD_WRITE: return "PERMISSION_TYPE_CLIPBOARD_WRITE";
+ }
+
+ return "error";
+}
+
+static const char* permissionToHumanString(eDynamicPermissionType type) {
+ switch (type) {
+ case PERMISSION_TYPE_UNKNOWN: return "An application <b>{}</b> is requesting an unknown permission.";
+ case PERMISSION_TYPE_SCREENCOPY: return "An application <b>{}</b> is trying to capture your screen.<br/><br/>Do you want to allow it to do so?";
+ case PERMISSION_TYPE_PLUGIN: return "An application <b>{}</b> is trying to load a plugin: <b>{}</b>.<br/><br/>Do you want to load it?";
+ case PERMISSION_TYPE_KEYBOARD: return "A new keyboard has been plugged in: {}.<br/><br/>Do you want to allow it to operate?";
+ case PERMISSION_TYPE_CLIPBOARD_READ: return "An application <b>{}</b> is trying to read from your clipboard.<br/><br/>Do you want to allow it to do so?";
+ case PERMISSION_TYPE_CLIPBOARD_WRITE: return "An application <b>{}</b> is trying to write to your clipboard.<br/><br/>Do you want to allow it to do so?";
}
return "error";
@@ -140,6 +155,10 @@ eDynamicPermissionAllowMode CDynamicPermissionManager::clientPermissionMode(wl_c
return PERMISSION_RULE_ALLOW_MODE_PENDING;
}
+ // clipboard permissions are allow default (like keyboard)
+ if (permission == PERMISSION_TYPE_CLIPBOARD_READ || permission == PERMISSION_TYPE_CLIPBOARD_WRITE)
+ return PERMISSION_RULE_ALLOW_MODE_ALLOW;
+
// if we are here, we need to ask, that's the fallback for all these (keyboards won't come here)
askForPermission(client, LOOKUP.value_or(""), permission);
diff --git a/src/managers/permissions/DynamicPermissionManager.hpp b/src/managers/permissions/DynamicPermissionManager.hpp
index 4de1eb32..7f536661 100644
--- a/src/managers/permissions/DynamicPermissionManager.hpp
+++ b/src/managers/permissions/DynamicPermissionManager.hpp
@@ -18,6 +18,8 @@ enum eDynamicPermissionType : uint8_t {
PERMISSION_TYPE_SCREENCOPY,
PERMISSION_TYPE_PLUGIN,
PERMISSION_TYPE_KEYBOARD,
+ PERMISSION_TYPE_CLIPBOARD_READ,
+ PERMISSION_TYPE_CLIPBOARD_WRITE,
};
enum eDynamicPermissionRuleSource : uint8_t {
diff --git a/src/protocols/DataDeviceWlr.cpp b/src/protocols/DataDeviceWlr.cpp
index d29106e0..262bc109 100644
--- a/src/protocols/DataDeviceWlr.cpp
+++ b/src/protocols/DataDeviceWlr.cpp
@@ -1,6 +1,8 @@
#include "DataDeviceWlr.hpp"
#include <algorithm>
+#include <hyprutils/os/FileDescriptor.hpp>
#include "../managers/SeatManager.hpp"
+#include "../managers/permissions/DynamicPermissionManager.hpp"
#include "core/Seat.hpp"
using namespace Hyprutils::OS;
@@ -23,6 +25,16 @@ CWLRDataOffer::CWLRDataOffer(SP<CZwlrDataControlOfferV1> resource_, SP<IDataSour
return;
}
+ const auto CLIENT = r->client();
+ const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_READ);
+ if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) {
+ LOGM(Log::LOG, "Clipboard read denied by permission manager for client {:x}", (uintptr_t)CLIENT);
+ return;
+ } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) {
+ LOGM(Log::LOG, "Clipboard read permission pending for client {:x}", (uintptr_t)CLIENT);
+ return;
+ }
+
LOGM(Log::DEBUG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)m_source.get());
m_source->send(mime, std::move(sendFd));
@@ -118,6 +130,16 @@ CWLRDataDevice::CWLRDataDevice(SP<CZwlrDataControlDeviceV1> resource_) : m_resou
return;
}
+ const auto CLIENT = r->client();
+ const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE);
+ if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) {
+ LOGM(Log::LOG, "Clipboard write denied by permission manager for client {:x}", (uintptr_t)CLIENT);
+ return;
+ } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) {
+ LOGM(Log::LOG, "Clipboard write permission pending for client {:x}", (uintptr_t)CLIENT);
+ return;
+ }
+
if (source && source->used())
LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this.");
@@ -135,6 +157,16 @@ CWLRDataDevice::CWLRDataDevice(SP<CZwlrDataControlDeviceV1> resource_) : m_resou
return;
}
+ const auto CLIENT = r->client();
+ const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE);
+ if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) {
+ LOGM(Log::LOG, "Clipboard write denied by permission manager for client {:x}", (uintptr_t)CLIENT);
+ return;
+ } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) {
+ LOGM(Log::LOG, "Clipboard write permission pending for client {:x}", (uintptr_t)CLIENT);
+ return;
+ }
+
if (source && source->used())
LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this.");
diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp
index 41f07273..1318910a 100644
--- a/src/protocols/core/DataDevice.cpp
+++ b/src/protocols/core/DataDevice.cpp
@@ -1,5 +1,6 @@
#include "DataDevice.hpp"
#include <algorithm>
+#include <hyprutils/os/FileDescriptor.hpp>
#include "../../managers/SeatManager.hpp"
#include "../../managers/PointerManager.hpp"
#include "../../managers/eventLoop/EventLoopManager.hpp"
@@ -15,6 +16,7 @@
#include "../../helpers/Monitor.hpp"
#include "../../render/Renderer.hpp"
#include "../../xwayland/Dnd.hpp"
+#include "../../managers/permissions/DynamicPermissionManager.hpp"
using namespace Hyprutils::OS;
CWLDataOfferResource::CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataSource> source_) : m_source(source_), m_resource(resource_) {
@@ -53,7 +55,49 @@ CWLDataOfferResource::CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataS
return;
}
- LOGM(Log::DEBUG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)m_source.get());
+ const auto CLIENT = r->client();
+ const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_READ);
+ if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) {
+ LOGM(Log::LOG, "Clipboard read denied by permission manager for client {:x}", (uintptr_t)CLIENT);
+ return;
+ } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) {
+ LOGM(Log::LOG, "Clipboard read permission pending for client {:x}", (uintptr_t)CLIENT);
+ const auto PROMISE = g_pDynamicPermissionManager->promiseFor(CLIENT, PERMISSION_TYPE_CLIPBOARD_READ);
+ if (!PROMISE) {
+ LOGM(Log::ERR, "BUG THIS: No promise for client permission for clipboard read");
+ return;
+ }
+ auto sharedFd = std::make_shared<CFileDescriptor>(std::move(sendFd));
+ PROMISE->then([this, mime = std::string(mime ? mime : ""), sharedFd](SP<CPromiseResult<eDynamicPermissionAllowMode>> r) {
+ if (r->hasError()) {
+ LOGM(Log::ERR, "BUG THIS: No permission returned for clipboard read");
+ return;
+ }
+
+ if (r->result() != PERMISSION_RULE_ALLOW_MODE_ALLOW) {
+ LOGM(Log::LOG, "Clipboard read denied by user");
+ return;
+ }
+
+ if (!m_source) {
+ LOGM(Log::WARN, "Source disappeared while waiting for permission");
+ return;
+ }
+
+ LOGM(Log::LOG, "Offer {:x} asks to send data from source {:x} (permission granted)", (uintptr_t)this, (uintptr_t)m_source.get());
+
+ if (!m_accepted) {
+ LOGM(Log::WARN, "Offer was never accepted, sending accept first");
+ m_source->accepted(mime.c_str());
+ }
+
+ m_source->send(mime.c_str(), std::move(*sharedFd));
+ m_recvd = true;
+ });
+ return;
+ }
+
+ LOGM(Log::LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)m_source.get());
if (!m_accepted) {
LOGM(Log::WARN, "Offer was never accepted, sending accept first");
@@ -253,6 +297,43 @@ CWLDataDeviceResource::CWLDataDeviceResource(SP<CWlDataDevice> resource_) : m_re
return;
}
+ const auto CLIENT = r->client();
+ const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE);
+ if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) {
+ LOGM(Log::LOG, "Clipboard write denied by permission manager for client {:x}", (uintptr_t)CLIENT);
+ return;
+ } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) {
+ LOGM(Log::LOG, "Clipboard write permission pending for client {:x}", (uintptr_t)CLIENT);
+ const auto PROMISE = g_pDynamicPermissionManager->promiseFor(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE);
+ if (!PROMISE) {
+ LOGM(Log::ERR, "BUG THIS: No promise for client permission for clipboard write");
+ return;
+ }
+ PROMISE->then([source](SP<CPromiseResult<eDynamicPermissionAllowMode>> r) mutable {
+ if (r->hasError()) {
+ LOGM(Log::ERR, "BUG THIS: No permission returned for clipboard write");
+ return;
+ }
+
+ if (r->result() != PERMISSION_RULE_ALLOW_MODE_ALLOW) {
+ LOGM(Log::LOG, "Clipboard write denied by user");
+ return;
+ }
+
+ if (!source) {
+ LOGM(Log::WARN, "Source disappeared while waiting for permission");
+ return;
+ }
+
+ if (source && source->m_used)
+ LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this.");
+
+ source->markUsed();
+ g_pSeatManager->setCurrentSelection(source);
+ });
+ return;
+ }
+
if (source && source->m_used)
LOGM(Log::WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this.");
diff --git a/src/xwayland/XWM.cpp b/src/xwayland/XWM.cpp
index 7e20d9b1..a896e222 100644
--- a/src/xwayland/XWM.cpp
+++ b/src/xwayland/XWM.cpp
@@ -22,8 +22,9 @@
#include "../protocols/XWaylandShell.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../desktop/state/FocusState.hpp"
-using Hyprutils::Memory::CUniquePointer;
+#include "../managers/permissions/DynamicPermissionManager.hpp"
+using Hyprutils::Memory::CUniquePointer;
using namespace Hyprutils::OS;
#define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f
@@ -1279,6 +1280,20 @@ void CXWM::initSelection() {
}
void CXWM::setClipboardToWayland(SXSelection& sel) {
+ const auto CLIENT = g_pXWayland->m_server ? g_pXWayland->m_server->m_xwaylandClient : nullptr;
+ if (CLIENT && &sel == &m_clipboard) {
+ const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_WRITE);
+ if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) {
+ Debug::log(LOG, "[xwm] Clipboard write denied by permission manager for XWayland");
+ return;
+ } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) {
+ Debug::log(LOG, "[xwm] Clipboard write permission pending for XWayland");
+ // For now, deny clipboard access when permission is pending as XWayland doesn't support async operations
+ // TODO: In the future, this could be enhanced to store the request and retry after permission is granted
+ return;
+ }
+ }
+
auto source = makeShared<CXDataSource>(sel);
if (source->mimes().empty()) {
Log::logger->log(Log::ERR, "[xwm] can't set selection: no MIMEs");
@@ -1520,6 +1535,20 @@ bool SXSelection::sendData(xcb_selection_request_event_t* e, std::string mime) {
return false;
}
+ const auto CLIENT = g_pXWayland->m_server ? g_pXWayland->m_server->m_xwaylandClient : nullptr;
+ if (CLIENT) {
+ const auto PERM = g_pDynamicPermissionManager->clientPermissionMode(CLIENT, PERMISSION_TYPE_CLIPBOARD_READ);
+ if (PERM == PERMISSION_RULE_ALLOW_MODE_DENY) {
+ Debug::log(LOG, "[xwm] Clipboard read denied by permission manager for XWayland");
+ return false;
+ } else if (PERM == PERMISSION_RULE_ALLOW_MODE_PENDING) {
+ Debug::log(LOG, "[xwm] Clipboard read permission pending for XWayland");
+ // For now, deny clipboard access when permission is pending as XWayland doesn't support async operations
+ // TODO: In the future, this could be enhanced to store the request and retry after permission is granted
+ return false;
+ }
+ }
+
const auto MIMES = selection->mimes();
if (MIMES.empty()) {