Skip to content

Commit

Permalink
Pass the password information to module to make use of the new auto c…
Browse files Browse the repository at this point in the history
…lear password option
  • Loading branch information
wengxt committed Apr 5, 2024
1 parent 2208a29 commit 1c162f0
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 71 deletions.
36 changes: 22 additions & 14 deletions src/modules/clipboard/clipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
#include "clipboard.h"
#include <cstdint>
#include <ctime>
#include <limits>
#include <unordered_set>
#include "fcitx-utils/event.h"
Expand All @@ -25,15 +24,16 @@ namespace fcitx {

namespace {

constexpr uint64_t oneSecond = 1000000U;
constexpr uint64_t oneSecond = 1000000ULL;

bool shouldClearPassword(const ClipboardEntry &entry) {
if (entry.passwordTimestamp == 0) {
bool shouldClearPassword(const ClipboardEntry &entry, uint64_t life) {
if (entry.passwordTimestamp == 0 || life == 0) {
// Not password.
return false;
}
// Allow 1 second skew.
return (entry.passwordTimestamp + oneSecond >= now(CLOCK_MONOTONIC));
// Allow 0.5 second skew.
return (entry.passwordTimestamp + life * oneSecond - oneSecond / 2) <=
now(CLOCK_MONOTONIC);
}

} // namespace
Expand Down Expand Up @@ -227,7 +227,7 @@ Clipboard::Clipboard(Instance *instance)
keyEvent.key().check(FcitxKey_Return) ||
keyEvent.key().check(FcitxKey_KP_Enter)) {
keyEvent.accept();
if (candidateList->size() > 0 &&
if (!candidateList->empty() &&
candidateList->cursorIndex() >= 0) {
candidateList->candidate(candidateList->cursorIndex())
.select(inputContext);
Expand Down Expand Up @@ -331,21 +331,21 @@ void Clipboard::updateUI(InputContext *inputContext) {
// Append first item from history_.
auto iter = history_.begin();
if (iter != history_.end()) {
candidateList->append<ClipboardCandidateWord>(this, *iter);
candidateList->append<ClipboardCandidateWord>(this, iter->text);
iter++;
}
// Append primary_, but check duplication first.
if (!primary_.empty()) {
if (!history_.contains(primary_)) {
candidateList->append<ClipboardCandidateWord>(this, primary_);
candidateList->append<ClipboardCandidateWord>(this, primary_.text);
}
}
// If primary_ is appended, it might squeeze one space out.
for (; iter != history_.end(); iter++) {
if (candidateList->totalSize() >= config_.numOfEntries.value()) {
break;
}
candidateList->append<ClipboardCandidateWord>(this, *iter);
candidateList->append<ClipboardCandidateWord>(this, iter->text);
}
candidateList->setSelectionKey(selectionKeys_);
candidateList->setLayoutHint(CandidateLayoutHint::Vertical);
Expand Down Expand Up @@ -388,6 +388,7 @@ void Clipboard::setClipboardEntry(const std::string &name,
if (!utf8::validate(entry.text)) {
return;
}

if (!history_.pushFront(entry)) {
history_.moveToTop(entry);
}
Expand All @@ -404,12 +405,12 @@ void Clipboard::setClipboardEntry(const std::string &name,
}
}

std::string Clipboard::primary(const InputContext *) const {
std::string Clipboard::primary(const InputContext * /*unused*/) const {
// TODO: per ic
return primary_.text;
}

std::string Clipboard::clipboard(const InputContext *) const {
std::string Clipboard::clipboard(const InputContext * /*unused*/) const {
// TODO: per ic
if (history_.empty()) {
return "";
Expand All @@ -419,13 +420,15 @@ std::string Clipboard::clipboard(const InputContext *) const {

void Clipboard::refreshPasswordTimer() {
if (*config_.clearPasswordAfter == 0) {
FCITX_CLIPBOARD_DEBUG() << "Disable Password Clearing Timer.";
clearPasswordTimer_->setEnabled(false);
return;
}

uint64_t minTimestamp = std::numeric_limits<uint64_t>::max();

if (shouldClearPassword(primary_)) {
if (shouldClearPassword(primary_, *config_.clearPasswordAfter)) {
FCITX_CLIPBOARD_DEBUG() << "Clear password in primary.";
primary_.clear();
} else if (primary_.passwordTimestamp) {
minTimestamp = std::min(minTimestamp, primary_.passwordTimestamp);
Expand All @@ -434,19 +437,24 @@ void Clipboard::refreshPasswordTimer() {
// Not efficient, but we don't have lots of entries anyway.
std::unordered_set<ClipboardEntry> needRemove;
for (const auto &entry : history_) {
if (shouldClearPassword(entry)) {
if (shouldClearPassword(entry, *config_.clearPasswordAfter)) {
needRemove.insert(entry);
} else if (entry.passwordTimestamp) {
minTimestamp = std::min(minTimestamp, entry.passwordTimestamp);
}
}
FCITX_CLIPBOARD_DEBUG() << "Clear " << needRemove.size()
<< " password(s) in clipboard history.";
for (const auto &entry : needRemove) {
history_.remove(entry);
}

if (minTimestamp != std::numeric_limits<uint64_t>::max()) {
clearPasswordTimer_->setTime(minTimestamp +
oneSecond * (*config_.clearPasswordAfter));
FCITX_CLIPBOARD_DEBUG()
<< "Password Clearing Timer will be triggered after: "
<< clearPasswordTimer_->time() - now(CLOCK_MONOTONIC);
clearPasswordTimer_->setOneShot();
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/modules/clipboard/clipboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ FCITX_CONFIGURATION(
Option<int, IntConstrain> numOfEntries{this, "Number of entries",
_("Number of entries"), 5,
IntConstrain(3, 30)};
ConditionalHidden<!isAndroid(), Option<bool>>
ConditionalHidden<isAndroid(), Option<bool>>
ignorePasswordFromPasswordManager{
this, "IgnorePasswordFromPasswordManager",
_("Ignore password from password manager"), true};
ConditionalHidden<
!isAndroid(),
isAndroid(),
Option<int, IntConstrain, DefaultMarshaller<int>, ToolTipAnnotation>>
clearPasswordAfter{this,
"ClearPasswordAfter",
_("Clear password after"),
_("Seconds before clearing password"),
30,
IntConstrain(0, 300),
{},
Expand Down Expand Up @@ -92,6 +92,7 @@ class Clipboard final : public AddonInstance {
void setPrimaryEntry(const std::string &name, ClipboardEntry entry);
void setClipboardEntry(const std::string &name,
const ClipboardEntry &entry);
const auto &config() const { return config_; }

#ifdef ENABLE_X11
FCITX_ADDON_DEPENDENCY_LOADER(xcb, instance_->addonManager());
Expand Down
69 changes: 44 additions & 25 deletions src/modules/clipboard/waylandclipboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
namespace fcitx {

uint64_t DataReaderThread::addTask(std::shared_ptr<UnixFD> fd,
DataOfferCallback callback) {
DataOfferDataCallback callback) {
auto id = nextId_++;
if (id == 0) {
id = nextId_++;
Expand Down Expand Up @@ -88,7 +88,9 @@ void DataReaderThread::realRun() {
tasks_ = nullptr;
}

DataOffer::DataOffer(wayland::ZwlrDataControlOfferV1 *offer) : offer_(offer) {
DataOffer::DataOffer(wayland::ZwlrDataControlOfferV1 *offer,
bool ignorePassword)
: offer_(offer), ignorePassword_(ignorePassword) {
offer_->setUserData(this);
conns_.emplace_back(offer_->offer().connect(
[this](const char *offer) { mimeTypes_.insert(offer); }));
Expand All @@ -106,22 +108,30 @@ void DataOffer::receiveData(DataReaderThread &thread,
return;
}

auto callbackWrapper = [this, callback](const std::vector<char> &data) {
return callback(data, isPassword_);
};

thread_ = &thread;
static const std::string passwordHint = PASSWORD_MIME_TYPE;
if (mimeTypes_.count(passwordHint)) {
receiveDataForMime(
passwordHint, [this, callback](const std::vector<char> &data) {
if (std::string_view(data.data(), data.size()) == "secret") {
return;
}
receiveRealData(callback);
});
receiveDataForMime(passwordHint, [this, callbackWrapper](
const std::vector<char> &data) {
if (std::string_view(data.data(), data.size()) == "secret" &&
ignorePassword_) {
FCITX_CLIPBOARD_DEBUG()
<< "Wayland clipboard contains password, ignore.";
return;
}
isPassword_ = true;
receiveRealData(callbackWrapper);
});
} else {
receiveRealData(std::move(callback));
receiveRealData(callbackWrapper);
}
}

void DataOffer::receiveRealData(DataOfferCallback callback) {
void DataOffer::receiveRealData(DataOfferDataCallback callback) {
if (!thread_) {
return;
}
Expand All @@ -141,7 +151,7 @@ void DataOffer::receiveRealData(DataOfferCallback callback) {
}

void DataOffer::receiveDataForMime(const std::string &mime,
DataOfferCallback callback) {
DataOfferDataCallback callback) {
if (!thread_) {
return;
}
Expand All @@ -162,7 +172,11 @@ DataDevice::DataDevice(WaylandClipboard *clipboard,
wayland::ZwlrDataControlDeviceV1 *device)
: clipboard_(clipboard), device_(device), thread_(clipboard_->eventLoop()) {
conns_.emplace_back(device_->dataOffer().connect(
[](wayland::ZwlrDataControlOfferV1 *offer) { new DataOffer(offer); }));
[this](wayland::ZwlrDataControlOfferV1 *offer) {
new DataOffer(offer, *clipboard_->parent()
->config()
.ignorePasswordFromPasswordManager);
}));
conns_.emplace_back(device_->selection().connect(
[this](wayland::ZwlrDataControlOfferV1 *offer) {
clipboardOffer_.reset(
Expand All @@ -171,9 +185,9 @@ DataDevice::DataDevice(WaylandClipboard *clipboard,
return;
}
clipboardOffer_->receiveData(
thread_, [this](std::vector<char> data) {
thread_, [this](std::vector<char> data, bool password) {
data.push_back('\0');
clipboard_->setClipboard(data.data());
clipboard_->setClipboard(data.data(), password);
clipboardOffer_.reset();
});
}));
Expand All @@ -182,14 +196,15 @@ DataDevice::DataDevice(WaylandClipboard *clipboard,
primaryOffer_.reset(
offer ? static_cast<DataOffer *>(offer->userData()) : nullptr);
if (!primaryOffer_) {
clipboard_->setPrimary("");
clipboard_->setPrimary("", false);
return;
}
primaryOffer_->receiveData(thread_, [this](std::vector<char> data) {
data.push_back('\0');
clipboard_->setPrimary(data.data());
primaryOffer_.reset();
});
primaryOffer_->receiveData(
thread_, [this](std::vector<char> data, bool password) {
data.push_back('\0');
clipboard_->setPrimary(data.data(), password);
primaryOffer_.reset();
});
}));
conns_.emplace_back(device_->finished().connect([this]() {
conns_.clear();
Expand Down Expand Up @@ -260,12 +275,16 @@ EventLoop *WaylandClipboard::eventLoop() {
return &parent_->instance()->eventLoop();
}

void WaylandClipboard::setClipboard(const std::string &str) {
parent_->setClipboard(name_, str);
void WaylandClipboard::setClipboard(const std::string &str, bool password) {
parent_->setClipboardEntry(
name_, {.text = str,
.passwordTimestamp = (password ? now(CLOCK_MONOTONIC) : 0)});
}

void WaylandClipboard::setPrimary(const std::string &str) {
parent_->setPrimary(name_, str);
void WaylandClipboard::setPrimary(const std::string &str, bool password) {
parent_->setPrimaryEntry(
name_, {.text = str,
.passwordTimestamp = (password ? now(CLOCK_MONOTONIC) : 0)});
}

} // namespace fcitx
25 changes: 16 additions & 9 deletions src/modules/clipboard/waylandclipboard.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ namespace fcitx {
// Upon receive DataOffer, DataReaderThread::addTask will be used to
// initiate a reading task and call the callback if it suceeds.

using DataOfferCallback = std::function<void(const std::vector<char> &)>;
using DataOfferDataCallback =
std::function<void(const std::vector<char> &data)>;
using DataOfferCallback =
std::function<void(const std::vector<char> &data, bool password)>;

struct DataOfferTask {
DataOfferCallback callback_;
DataOfferDataCallback callback_;
std::shared_ptr<UnixFD> fd_;
std::vector<char> data_;
std::unique_ptr<EventSourceIO> ioEvent_;
Expand All @@ -41,7 +44,7 @@ class DataReaderThread {
~DataReaderThread() {
if (thread_ && thread_->joinable()) {
dispatcherToWorker_.schedule([this]() {
if (auto loop = dispatcherToWorker_.eventLoop()) {
if (auto *loop = dispatcherToWorker_.eventLoop()) {
loop->exit();
}
});
Expand All @@ -55,7 +58,8 @@ class DataReaderThread {

static void run(DataReaderThread *self) { self->realRun(); }

uint64_t addTask(std::shared_ptr<UnixFD> fd, DataOfferCallback callback);
uint64_t addTask(std::shared_ptr<UnixFD> fd,
DataOfferDataCallback callback);
void removeTask(uint64_t token);

private:
Expand All @@ -72,19 +76,21 @@ class DataReaderThread {

class DataOffer {
public:
DataOffer(wayland::ZwlrDataControlOfferV1 *offer);
DataOffer(wayland::ZwlrDataControlOfferV1 *offer, bool ignorePassword);
~DataOffer();

void receiveData(DataReaderThread &thread, DataOfferCallback callback);

private:
void receiveDataForMime(const std::string &mime,
DataOfferCallback callback);
void receiveRealData(DataOfferCallback callback);
DataOfferDataCallback callback);
void receiveRealData(DataOfferDataCallback callback);

std::list<ScopedConnection> conns_;
std::unordered_set<std::string> mimeTypes_;
std::unique_ptr<wayland::ZwlrDataControlOfferV1> offer_;
bool ignorePassword_ = true;
bool isPassword_ = false;
UnixFD fd_;
DataReaderThread *thread_ = nullptr;
uint64_t taskId_ = 0;
Expand Down Expand Up @@ -113,10 +119,11 @@ class WaylandClipboard {
WaylandClipboard(Clipboard *clipboard, std::string name,
wl_display *display);

void setClipboard(const std::string &str);
void setPrimary(const std::string &str);
void setClipboard(const std::string &str, bool password);
void setPrimary(const std::string &str, bool password);
EventLoop *eventLoop();
auto display() const { return display_; }
auto parent() const { return parent_; }

private:
void refreshSeat();
Expand Down
Loading

0 comments on commit 1c162f0

Please sign in to comment.