Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Set::exclude_from_history on linux by setting x-kde-passwordManagerHint #155

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl<F: FnOnce()> Drop for ScopeGuard<F> {
pub(crate) mod private {
// This is currently unused on macOS, so silence the warning which appears
// since there's no extension traits making use of this trait sealing structure.
#[cfg_attr(target_vendor = "apple", allow(unreachable_pub))]
#[cfg_attr(target_vendor = "apple", allow(unreachable_pub, dead_code))]
pub trait Sealed {}

impl Sealed for crate::Get<'_> {}
Expand Down
32 changes: 29 additions & 3 deletions src/platform/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ use log::{trace, warn};
use crate::ImageData;
use crate::{common::private, Error};

// Magic strings used in `Set::exclude_from_history()` on linux
const KDE_EXCLUSION_MIME: &str = "x-kde-passwordManagerHint";
const KDE_EXCLUSION_HINT: &[u8] = b"secret";

mod x11;

#[cfg(feature = "wayland-data-control")]
Expand Down Expand Up @@ -158,19 +162,29 @@ pub(crate) struct Set<'clipboard> {
clipboard: &'clipboard mut Clipboard,
wait: WaitConfig,
selection: LinuxClipboardKind,
exclude_from_history: bool,
}

impl<'clipboard> Set<'clipboard> {
pub(crate) fn new(clipboard: &'clipboard mut Clipboard) -> Self {
Self { clipboard, wait: WaitConfig::default(), selection: LinuxClipboardKind::Clipboard }
Self {
clipboard,
wait: WaitConfig::default(),
selection: LinuxClipboardKind::Clipboard,
exclude_from_history: false,
}
}

pub(crate) fn text(self, text: Cow<'_, str>) -> Result<(), Error> {
match self.clipboard {
Clipboard::X11(clipboard) => clipboard.set_text(text, self.selection, self.wait),
Clipboard::X11(clipboard) => {
clipboard.set_text(text, self.selection, self.wait, self.exclude_from_history)
}

#[cfg(feature = "wayland-data-control")]
Clipboard::WlDataControl(clipboard) => clipboard.set_text(text, self.selection, self.wait),
Clipboard::WlDataControl(clipboard) => {
clipboard.set_text(text, self.selection, self.wait, self.exclude_from_history)
}
}
}

Expand Down Expand Up @@ -254,6 +268,13 @@ pub trait SetExtLinux: private::Sealed {
/// # }
/// ```
fn clipboard(self, selection: LinuxClipboardKind) -> Self;

/// Excludes the data which will be set on the clipboard from being added to
/// the desktop clipboard managers' histories by adding the MIME-Type `x-kde-passwordMangagerHint`
/// to the clipboard's selection data.
///
/// This is the most widely adopted convention on Linux.
fn exclude_from_history(self) -> Self;
}

impl SetExtLinux for crate::Set<'_> {
Expand All @@ -271,6 +292,11 @@ impl SetExtLinux for crate::Set<'_> {
self.platform.wait = WaitConfig::Until(deadline);
self
}

fn exclude_from_history(mut self) -> Self {
self.platform.exclude_from_history = true;
self
}
}

pub(crate) struct Clear<'clipboard> {
Expand Down
15 changes: 14 additions & 1 deletion src/platform/linux/wayland.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use wl_clipboard_rs::{
#[cfg(feature = "image-data")]
use super::encode_as_png;
use super::{into_unknown, LinuxClipboardKind, WaitConfig};
use super::{KDE_EXCLUSION_HINT, KDE_EXCLUSION_MIME};
use crate::common::Error;
#[cfg(feature = "image-data")]
use crate::common::ImageData;
Expand Down Expand Up @@ -79,12 +80,24 @@ impl Clipboard {
text: Cow<'_, str>,
selection: LinuxClipboardKind,
wait: WaitConfig,
exclude_from_history: bool,
) -> Result<(), Error> {
let mut opts = Options::new();
opts.foreground(matches!(wait, WaitConfig::Forever));
opts.clipboard(selection.try_into()?);
let source = Source::Bytes(text.into_owned().into_bytes().into_boxed_slice());
opts.copy(source, MimeType::Text).map_err(|e| match e {
if exclude_from_history {
opts.copy_multi(vec![
MimeSource { source, mime_type: MimeType::Text },
MimeSource {
source: Source::Bytes(Box::from(KDE_EXCLUSION_HINT)),
mime_type: MimeType::Specific(String::from(KDE_EXCLUSION_MIME)),
},
])
} else {
opts.copy(source, MimeType::Text)
}
.map_err(|e| match e {
CopyError::PrimarySelectionUnsupported => Error::ClipboardNotSupported,
other => into_unknown(other),
})?;
Expand Down
15 changes: 12 additions & 3 deletions src/platform/linux/x11.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use std::{
thread::JoinHandle,
thread_local,
time::{Duration, Instant},
usize,
};

use log::{error, trace, warn};
Expand All @@ -46,6 +45,7 @@ use x11rb::{
#[cfg(feature = "image-data")]
use super::encode_as_png;
use super::{into_unknown, LinuxClipboardKind, WaitConfig};
use super::{KDE_EXCLUSION_HINT, KDE_EXCLUSION_MIME};
#[cfg(feature = "image-data")]
use crate::ImageData;
use crate::{common::ScopeGuard, Error};
Expand Down Expand Up @@ -80,6 +80,7 @@ x11rb::atom_manager! {
HTML: b"text/html",

PNG_MIME: b"image/png",
X_KDE_PASSWORDMANAGERHINT: KDE_EXCLUSION_MIME.as_bytes(),

// This is just some random name for the property on our window, into which
// the clipboard owner writes the data we requested.
Expand Down Expand Up @@ -878,11 +879,19 @@ impl Clipboard {
message: Cow<'_, str>,
selection: LinuxClipboardKind,
wait: WaitConfig,
exclude_from_history: bool,
) -> Result<()> {
let data = vec![ClipboardData {
let mut data = vec![];
data.push(ClipboardData {
bytes: message.into_owned().into_bytes(),
format: self.inner.atoms.UTF8_STRING,
}];
});
if exclude_from_history {
data.push(ClipboardData {
bytes: KDE_EXCLUSION_HINT.to_vec(),
format: self.inner.atoms.X_KDE_PASSWORDMANAGERHINT,
});
}
self.inner.write(data, selection, wait)
}

Expand Down