#!/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
port: "42069"
key: #{981A7879}

proto: context [
	writecb: #{3E}
	readcb: #{C5}
]

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] [
	debug ["found flag " i]
	do select [
		"-v" [ verbose: yes ]
		"-q" [ verbose: no ]
		"-r" [ readonly: yes ]
	] f
]

set-clipboard: func[c] [ execute/in "xsel -bi" c ]

get-clipboard: func[/local] [ 
	clip-out: ""
	execute/out "xsel -bo" clip-out
	clip-out
]

start-server: func[/local] [
	debug ["serving clipboard from " hostname none ]
	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
				]
				true [ write connection "bad command" ]
			]
		] [
			debug "denied access"
			write connection "access denied"
		]
		close connection
		debug [ "closed connection" ]
	]
]

send-clip: func[host] [
	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
]
get-clip: func[host] [
	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
	print ["server response: " to-string clipboard]
]

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 ["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 ]
	]
]

main args no
