mirror of https://github.com/keeweb/keeweb.git
added native messaging host
This commit is contained in:
parent
19a42b7463
commit
ef6e5ebcbe
|
@ -14,6 +14,7 @@ obj/
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
*.suo
|
*.suo
|
||||||
.idea/
|
.idea/
|
||||||
.vscode
|
.vscode/
|
||||||
*.iml
|
*.iml
|
||||||
test/dist/
|
test/dist/
|
||||||
|
extension/native-messaging-host/build/
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 4
|
||||||
|
ColumnLimit: 100
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^<ext/.*\.h>'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '^<.*\.h>'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '^<.*'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 3
|
||||||
|
SortIncludes: true
|
|
@ -0,0 +1,30 @@
|
||||||
|
cmake_minimum_required(VERSION 3.7)
|
||||||
|
|
||||||
|
project(keeweb-native-messaging-host)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
libuv
|
||||||
|
GIT_REPOSITORY https://github.com/libuv/libuv.git
|
||||||
|
GIT_TAG v1.41.0
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(libuv)
|
||||||
|
|
||||||
|
set(OUTPUT_NAME ${PROJECT_NAME})
|
||||||
|
set(SOURCES src/main.cpp)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE uv_a)
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE ${libuv_SOURCE_DIR}/include)
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror)
|
||||||
|
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=address,undefined)
|
||||||
|
target_link_options(${PROJECT_NAME} PRIVATE -fsanitize=address,undefined)
|
||||||
|
endif()
|
|
@ -0,0 +1,13 @@
|
||||||
|
all:
|
||||||
|
cmake -B build .
|
||||||
|
cmake --build build --config MinSizeRel
|
||||||
|
|
||||||
|
debug:
|
||||||
|
cmake -B build .
|
||||||
|
cmake --build build --config Debug
|
||||||
|
|
||||||
|
format:
|
||||||
|
clang-format -i src/*.cpp
|
||||||
|
|
||||||
|
run:
|
||||||
|
echo -n 020000007b7d | xxd -r -p | build/keeweb-native-messaging-host chrome-extension://enjifmdnhaddmajefhfaoglcfdobkcpj
|
|
@ -0,0 +1,208 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <uv.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// https://developer.chrome.com/docs/apps/nativeMessaging/#native-messaging-host-protocol
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging#app_side
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
uv_stream_t *tty_in = nullptr;
|
||||||
|
uv_stream_t *tty_out = nullptr;
|
||||||
|
uv_stream_t *keeweb_pipe = nullptr;
|
||||||
|
std::queue<uv_buf_t *> pending_to_keeweb{};
|
||||||
|
std::queue<uv_buf_t *> pending_to_stdout{};
|
||||||
|
bool write_to_keeweb_in_progress = false;
|
||||||
|
bool write_to_stdout_in_progress = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
State state{};
|
||||||
|
|
||||||
|
constexpr std::array allowed_origins = {
|
||||||
|
std::string_view("chrome-extension://enjifmdnhaddmajefhfaoglcfdobkcpj")};
|
||||||
|
|
||||||
|
void process_keeweb_queue();
|
||||||
|
void process_stdout_queue();
|
||||||
|
void close_keeweb_pipe();
|
||||||
|
|
||||||
|
bool check_args(int argc, char *argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
std::cerr << "Expected origin";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string origin = argv[1];
|
||||||
|
auto found = std::find(allowed_origins.begin(), allowed_origins.end(), origin);
|
||||||
|
if (found == allowed_origins.end()) {
|
||||||
|
std::cerr << "Invalid origin";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void alloc_buf(uv_handle_t *, size_t size, uv_buf_t *buf) {
|
||||||
|
buf->base = new char[size];
|
||||||
|
buf->len = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void quit_after_stdio_error() {
|
||||||
|
if (state.keeweb_pipe) {
|
||||||
|
close_keeweb_pipe();
|
||||||
|
} else {
|
||||||
|
uv_read_stop(state.tty_in);
|
||||||
|
uv_loop_close(uv_default_loop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stdin_read_cb(uv_stream_t *, ssize_t nread, const uv_buf_t *buf) {
|
||||||
|
if (nread > 0) {
|
||||||
|
auto write_buf = new uv_buf_t{.base = buf->base, .len = static_cast<size_t>(nread)};
|
||||||
|
state.pending_to_keeweb.emplace(write_buf);
|
||||||
|
process_keeweb_queue();
|
||||||
|
} else if (nread < 0) {
|
||||||
|
quit_after_stdio_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stdout_write_cb(uv_write_t *req, int status) {
|
||||||
|
delete req;
|
||||||
|
|
||||||
|
auto buf = state.pending_to_stdout.front();
|
||||||
|
state.pending_to_stdout.pop();
|
||||||
|
delete buf->base;
|
||||||
|
delete buf;
|
||||||
|
|
||||||
|
state.write_to_stdout_in_progress = false;
|
||||||
|
|
||||||
|
auto success = status >= 0;
|
||||||
|
if (success) {
|
||||||
|
process_stdout_queue();
|
||||||
|
} else {
|
||||||
|
quit_after_stdio_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_stdout_queue() {
|
||||||
|
if (state.write_to_stdout_in_progress || state.pending_to_stdout.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto buf = state.pending_to_stdout.front();
|
||||||
|
|
||||||
|
auto write_req = new uv_write_t{};
|
||||||
|
uv_write(write_req, state.tty_out, buf, 1, stdout_write_cb);
|
||||||
|
|
||||||
|
state.write_to_stdout_in_progress = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void keeweb_pipe_close_cb(uv_handle_t *pipe) {
|
||||||
|
delete pipe;
|
||||||
|
uv_read_stop(state.tty_in);
|
||||||
|
uv_loop_close(uv_default_loop());
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_keeweb_pipe() {
|
||||||
|
if (!state.keeweb_pipe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto pipe = state.keeweb_pipe;
|
||||||
|
state.keeweb_pipe = nullptr;
|
||||||
|
uv_read_stop(pipe);
|
||||||
|
uv_close(reinterpret_cast<uv_handle_t *>(pipe), keeweb_pipe_close_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void keeweb_write_cb(uv_write_t *req, int status) {
|
||||||
|
delete req;
|
||||||
|
|
||||||
|
auto buf = state.pending_to_keeweb.front();
|
||||||
|
state.pending_to_keeweb.pop();
|
||||||
|
delete buf->base;
|
||||||
|
delete buf;
|
||||||
|
|
||||||
|
state.write_to_keeweb_in_progress = false;
|
||||||
|
|
||||||
|
auto success = status >= 0;
|
||||||
|
if (success) {
|
||||||
|
process_keeweb_queue();
|
||||||
|
} else {
|
||||||
|
close_keeweb_pipe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_keeweb_queue() {
|
||||||
|
if (!state.keeweb_pipe || state.write_to_keeweb_in_progress ||
|
||||||
|
state.pending_to_keeweb.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto buf = state.pending_to_keeweb.front();
|
||||||
|
|
||||||
|
auto write_req = new uv_write_t{};
|
||||||
|
uv_write(write_req, state.keeweb_pipe, buf, 1, keeweb_write_cb);
|
||||||
|
|
||||||
|
state.write_to_keeweb_in_progress = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void keeweb_pipe_read_cb(uv_stream_t *, ssize_t nread, const uv_buf_t *buf) {
|
||||||
|
if (nread > 0) {
|
||||||
|
auto write_buf = new uv_buf_t{.base = buf->base, .len = static_cast<size_t>(nread)};
|
||||||
|
state.pending_to_stdout.emplace(write_buf);
|
||||||
|
process_stdout_queue();
|
||||||
|
} else if (nread < 0) {
|
||||||
|
close_keeweb_pipe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void keeweb_pipe_connect_cb(uv_connect_t *req, int status) {
|
||||||
|
auto pipe = req->handle;
|
||||||
|
delete req;
|
||||||
|
auto connected = status >= 0;
|
||||||
|
if (connected) {
|
||||||
|
state.keeweb_pipe = pipe;
|
||||||
|
uv_read_start(pipe, alloc_buf, keeweb_pipe_read_cb);
|
||||||
|
process_keeweb_queue();
|
||||||
|
} else {
|
||||||
|
std::cerr << "Cannot connect to KeeWeb";
|
||||||
|
// TODO: start KeeWeb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void connect_keeweb_pipe() {
|
||||||
|
auto temp_path = std::filesystem::temp_directory_path();
|
||||||
|
auto keeweb_pipe_path = temp_path / "keeweb-example.sock";
|
||||||
|
auto keeweb_pipe_name = keeweb_pipe_path.c_str();
|
||||||
|
|
||||||
|
auto keeweb_pipe = new uv_pipe_t{};
|
||||||
|
uv_pipe_init(uv_default_loop(), keeweb_pipe, false);
|
||||||
|
|
||||||
|
auto connect_req = new uv_connect_t();
|
||||||
|
uv_pipe_connect(connect_req, keeweb_pipe, keeweb_pipe_name, keeweb_pipe_connect_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_reading_stdin() { uv_read_start(state.tty_in, alloc_buf, stdin_read_cb); }
|
||||||
|
|
||||||
|
void init_tty() {
|
||||||
|
auto stdin_tty = new uv_tty_t{};
|
||||||
|
uv_tty_init(uv_default_loop(), stdin_tty, fileno(stdin), 0);
|
||||||
|
state.tty_in = reinterpret_cast<uv_stream_t *>(stdin_tty);
|
||||||
|
|
||||||
|
auto stdout_tty = new uv_tty_t{};
|
||||||
|
uv_tty_init(uv_default_loop(), stdout_tty, fileno(stdout), 0);
|
||||||
|
state.tty_out = reinterpret_cast<uv_stream_t *>(stdout_tty);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
if (!check_args(argc, argv)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_tty();
|
||||||
|
start_reading_stdin();
|
||||||
|
connect_keeweb_pipe();
|
||||||
|
|
||||||
|
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
const os = require('os');
|
||||||
|
const net = require('net');
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const sockPath = path.join(os.tmpdir(), 'keeweb-example.sock');
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(sockPath);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const server = net.createServer((socket) => {
|
||||||
|
socket.on('data', (data) => {
|
||||||
|
socket.write(data);
|
||||||
|
});
|
||||||
|
socket.on('end', () => {});
|
||||||
|
});
|
||||||
|
server.listen(sockPath);
|
Loading…
Reference in New Issue