Skip to content

Commit a1093fa

Browse files
committed
bin: update clipboard-server with additional params
1 parent 2844103 commit a1093fa

File tree

4 files changed

+162
-44
lines changed

4 files changed

+162
-44
lines changed

dotenv/bin/clipboard-server

+146-34
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,62 @@
11
#!/usr/bin/env node
2-
// title : clipboard-server
3-
// description : Forward clipboard access over a http socket
4-
// author : Wei Kin Huang
5-
// date : 2022-05-28
6-
// version : 1.0.0
7-
// usage : clipboard-server start|stop|server [--pidfile FILE] [--socket FILE|PORT]
8-
// requires : node pbcopy pbpaste
9-
// ===============================================================================
10-
// ssh -o StreamLocalBindMask=0277 -R /tmp/clipboard-server.sock:/tmp/clipboard-server.sock user@HOST
11-
// if the remote host doesn't have StreamLocalBindUnlink=yes in sshd_config, use a port
12-
// ssh -R 127.0.0.1:29009:/tmp/clipboard-server.sock user@HOST
13-
// ssh -o StreamLocalBindMask=0077 -o StreamLocalBindUnlink=yes -R 127.0.0.1:3306:/tmp/clipboard-server.sock [email protected] -vv
2+
/**
3+
* title : clipboard-server
4+
* description : Forward clipboard access over a http socket
5+
* author : Wei Kin Huang
6+
* date : 2022-05-28
7+
* version : 1.0.0
8+
* requires : node pbcopy pbpaste
9+
* =============================================================================
10+
* Usage: clipboard-server COMMAND [OPTION]...
11+
* Forward clipboard access over a http socket.
12+
* Example: clipboard-server start --disable-paste
13+
*
14+
* Commands:
15+
* start Run the server in the background
16+
* stop Stop the server if it is running
17+
* restart Restart the server
18+
* server Start the server in the foreground
19+
*
20+
* Options:
21+
* -d, --disable-paste Disable local clipboard access, only alloys the remote
22+
* server to send contents to local clipboard.
23+
* default: false
24+
* -p, --pidfile FILE Path to the pid file for the server.
25+
* default: ~/.config/clipboard-server/clipboard-server.pid
26+
* -s, --socket FILE|PORT A port number or path to the socket file location
27+
* default: ~/.config/clipboard-server/clipboard-server.sock
28+
*
29+
* Using over SSH
30+
*
31+
* Via binding a socket on the remote host
32+
* ssh -o StreamLocalBindMask=0177 -R /tmp/clipboard-server.sock:$HOME/.config/clipboard-server/clipboard-server.sock user@HOST
33+
* If the remote host doesn't have StreamLocalBindUnlink=yes in sshd_config, it will not clean up the socket file, in
34+
* this case use a port instead
35+
*
36+
* Via binding a port on the remote host
37+
* ssh -R 127.0.0.1:29009:$HOME/.config/clipboard-server/clipboard-server.sock user@HOST
38+
*
39+
* This server is integrated with the dotfiles provided pbcopy/pbpaste commands when used over ssh, additionally a env
40+
* var must be exported on the remote host to make use of this server:
41+
*
42+
* export CLIPBOARD_SERVER_PORT=29009 # the remote bound port
43+
* or if using a socket (the default is /tmp/clipboard-server.sock if not set)
44+
* export CLIPBOARD_SERVER_SOCK=/tmp/clipboard-server.sock
45+
*
46+
* Testing
47+
*
48+
* To read from the clipboard
49+
* curl -sSLi --unix-socket ~/.config/clipboard-server/clipboard-server.sock -X GET http://localhost/clipboard
50+
*
51+
* To write to the clipboard
52+
* date | curl -i -sSL --unix-socket ~/.config/clipboard-server/clipboard-server.sock http://localhost/clipboard --data-binary @-
53+
*/
1454

1555
const http = require("http");
1656
const childProcess = require("child_process");
1757
const fs = require("fs/promises");
58+
const os = require("os");
59+
const path = require("path");
1860
const { unlinkSync } = require("fs");
1961

2062
// set umask for files to be 0600
@@ -37,7 +79,6 @@ async function setClipboard(req, res) {
3779
});
3880
}
3981

40-
// @TODO: option to disable forwarding clipboard contents to remote
4182
async function getClipboard(res) {
4283
return await new Promise((resolve, reject) => {
4384
const subprocess = childProcess.spawn("pbpaste", {
@@ -54,6 +95,17 @@ async function getClipboard(res) {
5495
});
5596
}
5697

98+
async function createBaseDir(filepath) {
99+
const dirname = path.dirname(filepath);
100+
try {
101+
await fs.stat(dirname);
102+
return;
103+
} catch {}
104+
const mask = process.umask(0o0077);
105+
await fs.mkdir(dirname, { recursive: true, mode: 0o700 });
106+
process.umask(mask);
107+
}
108+
57109
async function getPidFromPidFile(pidFile) {
58110
try {
59111
const pid = parseInt(await (await fs.readFile(pidFile, "utf8")).trim(), 10);
@@ -107,7 +159,7 @@ async function isListening(socket) {
107159
});
108160
}
109161

110-
async function start({ pidFile, socket }) {
162+
async function start({ pidFile, socket, disablePaste }) {
111163
// check if pid file exists
112164
const existingPid = await getPidFromPidFile(pidFile);
113165
// check if already running
@@ -116,10 +168,15 @@ async function start({ pidFile, socket }) {
116168
return;
117169
}
118170

171+
const args = ["--pidfile", pidFile, "--socket", socket];
172+
if (disablePaste) {
173+
args.push("--disable-paste");
174+
}
175+
119176
// spawn detached process
120177
const subprocess = childProcess.spawn(
121178
process.argv[0],
122-
[process.argv[1], "server", "--pidfile", pidFile, "--socket", socket],
179+
[process.argv[1], "server", ...args],
123180
{
124181
detached: true,
125182
stdio: "ignore",
@@ -150,6 +207,7 @@ async function start({ pidFile, socket }) {
150207

151208
// write child pid to file
152209
// console.log(subprocess.pid);
210+
await createBaseDir(pidFile);
153211
await fs.writeFile(pidFile, String(subprocess.pid), "utf8");
154212
}
155213

@@ -172,30 +230,36 @@ async function stop({ pidFile }) {
172230
} catch {}
173231
}
174232

175-
async function server({ socket }) {
176-
// cleanup
177-
process.on("SIGHUP", () => {
178-
try {
179-
if (socketIsFile(socket)) {
233+
async function server({ socket, disablePaste }) {
234+
if (socketIsFile(socket)) {
235+
await createBaseDir(socket);
236+
// cleanup
237+
process.on("SIGHUP", () => {
238+
try {
180239
unlinkSync(socket);
181-
}
182-
} catch {}
183-
process.exit();
184-
});
240+
} catch {}
241+
process.exit();
242+
});
243+
}
185244

186-
// ============================ plaintext h1 server
187-
const h1Server = http.createServer({});
188-
h1Server.on("error", (err) => console.error(err));
189-
h1Server.on("connection", () => {
245+
const server = http.createServer({});
246+
server.on("error", (err) => console.error(err));
247+
server.on("connection", () => {
190248
// connectLogger('connect: h1');
191249
});
192-
h1Server.on("request", async (req, res) => {
250+
server.on("request", async (req, res) => {
193251
try {
194252
switch (req.url) {
195253
case "/clipboard":
196254
if (req.method === "GET") {
197255
res.setHeader("content-type", "application/octet-stream");
198-
await getClipboard(res);
256+
if (!disablePaste) {
257+
await getClipboard(res);
258+
} else {
259+
res.statusMessage = "Forbidden";
260+
res.statusCode = 403;
261+
res.end();
262+
}
199263
} else if (req.method === "POST") {
200264
res.setHeader("content-type", "text/plain");
201265
res.statusCode = 200;
@@ -206,29 +270,63 @@ async function server({ socket }) {
206270
}
207271
break;
208272
case "/ping":
273+
res.setHeader("content-type", "text/plain");
209274
res.statusCode = 200;
210275
res.end("ok\n");
211276
break;
212277
default:
213-
res.statusCode = 400;
278+
res.statusMessage = "Not Found";
279+
res.statusCode = 404;
214280
res.end();
215281
break;
216282
}
217283
} catch (e) {
284+
res.statusMessage = "Forbidden";
218285
res.statusCode = 500;
219286
res.end();
220287
}
221288
});
222-
h1Server.listen(socket, () => console.log("ready"));
289+
server.listen(socket, () => console.log("ready"));
290+
}
291+
292+
function help() {
293+
return `
294+
Usage: clipboard-server COMMAND [OPTION]...
295+
Forward clipboard access over a http socket.
296+
Example: clipboard-server start --disable-paste
297+
298+
Commands:
299+
start Run the server in the background
300+
stop Stop the server if it is running
301+
restart Restart the server
302+
server Start the server in the foreground
303+
304+
Options:
305+
-d, --disable-paste Disable local clipboard access, only alloys the remote
306+
server to send contents to local clipboard.
307+
default: false
308+
-p, --pidfile FILE Path to the pid file for the server.
309+
default: ~/.config/clipboard-server/clipboard-server.pid
310+
-s, --socket FILE|PORT A port number or path to the socket file location
311+
default: ~/.config/clipboard-server/clipboard-server.sock
312+
--help Show this message
313+
`.trim();
223314
}
224315

225316
async function main([_1, _2, subcommand, ...args]) {
317+
const userInfo = os.userInfo();
318+
let showHelp = subcommand == "--help";
226319
const opts = {
227-
pidFile: "/tmp/clipboard-server.pid",
228-
socket: "/tmp/clipboard-server.sock",
320+
pidFile: `${userInfo.homedir}/.config/clipboard-server/clipboard-server.pid`,
321+
socket: `${userInfo.homedir}/.config/clipboard-server/clipboard-server.sock`,
322+
disablePaste: false,
229323
};
230324
for (let i = 0; i < args.length; i++) {
231325
switch (args[i]) {
326+
case "-d":
327+
case "--disable-paste":
328+
opts.disablePaste = true;
329+
break;
232330
case "-p":
233331
case "--pidfile":
234332
opts.pidFile = args[++i];
@@ -240,16 +338,30 @@ async function main([_1, _2, subcommand, ...args]) {
240338
throw new Error("socket must be a file or a port.");
241339
}
242340
break;
341+
case "--help":
342+
showHelp = true;
343+
break;
243344
}
244345
}
245346

347+
if (showHelp) {
348+
console.log(help());
349+
return;
350+
}
351+
246352
switch (subcommand) {
247353
case "start":
248354
await start(opts);
249355
break;
250356
case "stop":
251357
await stop(opts);
252358
break;
359+
case "restart":
360+
try {
361+
await stop(opts);
362+
} catch {}
363+
await start(opts);
364+
break;
253365
case "server":
254366
await server(opts);
255367
break;

dotenv/bin/git-undo-index

+6-6
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
# @see https://github.com/weikinhuang/git-undo-index/
99

1010
# colors
11-
COLOR_BOLD=
12-
COLOR_RESET=
13-
if type tput &>/dev/null; then
14-
COLOR_BOLD=$(tput bold)
15-
COLOR_RESET=$(tput sgr0)
16-
fi
11+
# COLOR_BOLD=
12+
# COLOR_RESET=
13+
# if type tput &>/dev/null; then
14+
# COLOR_BOLD=$(tput bold)
15+
# COLOR_RESET=$(tput sgr0)
16+
# fi
1717

1818
# variables
1919
UNIXTIME="$(date +%s)"

dotenv/ssh/bin/pbcopy

+5-2
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,19 @@
1111
set -euo pipefail
1212
IFS=$'\n\t'
1313

14+
CLIPBOARD_SERVER_CHECK=
1415
CURL_OPTS=(-sSL)
1516
CURL_HOST="http://localhost"
1617
if [[ -n "${CLIPBOARD_SERVER_PORT:-}" ]]; then
1718
CURL_HOST="http://localhost:${CLIPBOARD_SERVER_PORT}"
18-
else
19+
CLIPBOARD_SERVER_CHECK=1
20+
elif [[ -S "${CLIPBOARD_SERVER_SOCK:-/tmp/clipboard-server.sock}" ]]; then
1921
CURL_OPTS+=(--unix-socket "${CLIPBOARD_SERVER_SOCK:-/tmp/clipboard-server.sock}")
22+
CLIPBOARD_SERVER_CHECK=1
2023
fi
2124

2225
if
23-
[[ -S "${CLIPBOARD_SERVER_SOCK:-/tmp/clipboard-server.sock}" ]] \
26+
[[ -n "${CLIPBOARD_SERVER_CHECK}" ]] \
2427
&& curl "${CURL_OPTS[@]}" "${CURL_HOST}/ping" &>/dev/null </dev/null
2528
then
2629

dotenv/ssh/bin/pbpaste

+5-2
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,19 @@
1111
set -euo pipefail
1212
IFS=$'\n\t'
1313

14+
CLIPBOARD_SERVER_CHECK=
1415
CURL_OPTS=(-sSL)
1516
CURL_HOST="http://localhost"
1617
if [[ -n "${CLIPBOARD_SERVER_PORT:-}" ]]; then
1718
CURL_HOST="http://localhost:${CLIPBOARD_SERVER_PORT}"
18-
else
19+
CLIPBOARD_SERVER_CHECK=1
20+
elif [[ -S "${CLIPBOARD_SERVER_SOCK:-/tmp/clipboard-server.sock}" ]]; then
1921
CURL_OPTS+=(--unix-socket "${CLIPBOARD_SERVER_SOCK:-/tmp/clipboard-server.sock}")
22+
CLIPBOARD_SERVER_CHECK=1
2023
fi
2124

2225
if
23-
[[ -S "${CLIPBOARD_SERVER_SOCK:-/tmp/clipboard-server.sock}" ]] \
26+
[[ -n "${CLIPBOARD_SERVER_CHECK}" ]] \
2427
&& curl "${CURL_OPTS[@]}" "${CURL_HOST}/ping" &>/dev/null </dev/null
2528
then
2629

0 commit comments

Comments
 (0)