#!/usr/bin/env -S boron -sp
; [ʞ] clipserv.b
; ~ lexi hale <lexi@hale.su>
; # boron
; @ vim: ft=rebol
; ? a tool for transmitting clipboard from one
; host to another over the network. WARNING:
; simple nonce authentication is used but the
; clipboard is sent IN THE CLEAR
do read/text %ansi.b
verbose: no
readonly: no
to-stdout: no
port: "42069"
key: #{981A7879}
setter: case [
getenv "ANDROID_ROOT" "termux-clipboard-set"
true "xsel -bi"
]
getter: case [
getenv "ANDROID_ROOT" "termux-clipboard-get"
true "xsel -bo"
]
proto: context [
writecb: #{3E}
readcb: #{C5}
stopserver: #{69}
]
debug: func [x] [
if verbose [
x: either block? x [rejoin x] [x]
write stderr fmt [
[(fg red) "["] [(style hl) "debug"] " " x [(fg red) "]"] "^/"
]
]
]
entropy-sink: open %/dev/urandom
rand-block: func[sz] [ read/part entropy-sink sz ]
key-hash: func[k z /local] [
cksum: checksum join k z
slice cksum 0,4
]
debug "parsing arguments"
usage: does [
write stderr fmt [[(style hl)"usage:"]
" ./clip.b get <hostname> | send <hostname>" ]
]
flag: func [f /extern verbose readonly to-stdout] [
debug ["found flag " i]
do select [
"-v" [ verbose: yes ]
"-q" [ verbose: no ]
"-r" [ readonly: yes ]
"-o" [ to-stdout: yes ]
] f
]
set-clipboard: func[c] [
either to-stdout [ print c ] [
execute/in setter c
]
]
get-clipboard: func[/local] [
clip-out: ""
execute/out getter clip-out
clip-out
]
start-server: func[/local] [
debug ["serving clipboard from " hostname none ]
debug [{using setter "} setter {" and getter "} getter {"}]
sock: open join "tcp://:" port
forever [
connection: read wait sock
nonce: rand-block 4
expect-key: (key-hash nonce key)
write connection nonce
debug [ "sent nonce " nonce ";"
"expecting password " expect-key ";"]
client-message: read wait connection
client-key: slice client-message 0,4
client-command: slice client-message 4,1
client-body: slice client-message 5,-1
debug [ "received pw " client-key ]
either eq? client-key expect-key [
debug [ "admitted access" ]
debug [ "got command " client-command ]
case [
eq? client-command proto/readcb [
debug {sending clipboard to client}
write connection get-clipboard
]
eq? client-command proto/writecb [
new-clipboard: to-string client-body
debug ["got cb " new-clipboard]
set-clipboard new-clipboard
]
eq? client-command proto/stopserver [break]
true [ write connection "bad command" ]
]
] [
debug "denied access"
write connection "access denied"
]
close connection
debug [ "closed connection" ]
]
]
send-clip: func[host] [
catch [
debug [{using getter "} getter {"}]
debug ["sending clipboard to " host ]
sock: open rejoin ["tcp://" host ":" port]
nonce: read wait sock
debug [ "got nonce " nonce ]
client-key: key-hash nonce key
debug [ "sending password " client-key ]
msg: rejoin [
client-key
proto/writecb
get-clipboard #{00}
]
write sock msg
close sock
true
]
]
get-clip: func[host] [
catch [
debug [{using setter "} setter {"}]
debug ["getting clipboard from " host ]
sock: open rejoin ["tcp://" host ":" port]
nonce: read wait sock
debug [ "got nonce " nonce ]
client-key: key-hash nonce key
debug [ "sending password " client-key ]
msg: rejoin [ client-key proto/readcb ]
write sock msg
clipboard: read wait sock
debug ["server response: " to-string clipboard]
set-clipboard to-string clipboard
close sock
true
]
]
stop-clip: func[host] [
catch [
debug ["stopping clipserv on " host ]
sock: open rejoin ["tcp://" host ":" port]
nonce: read wait sock
debug [ "got nonce " nonce ]
client-key: key-hash nonce key
debug [ "sending password " client-key ]
msg: rejoin [ client-key proto/stopserver ]
write sock msg
close sock
true
]
]
main: func [argv checked-flags] [
case [
none? argv [ usage ]
parse argv ["get" host: string!] [ get-clip host ]
parse argv ["send" host: string!] [ send-clip host ]
parse argv ["stop" host: string!] [ stop-clip host ]
parse argv ["serve"] [ start-server ]
not checked-flags [
new-args: []
foreach i argv [
either eq? first i '-' [ flag i ] [
new-args: join new-args i ] ]
main new-args yes
]
true [ usage ]
]
]
success: main args no
if eq? success true [
quit/return 0
]
quit/return 1