ycl

Artifact [81a3a7f438]
Login

Artifact [81a3a7f438]

Artifact 81a3a7f4389bbd9c87baea0e4bf979b1e702587a:


#! /usr/bin/env tclsh

apply [list {} {
	while 1 {
		set ns [info cmdcount]
		if {![namespace exists $ns]} break
	}
	namespace eval $ns {
		variable magic lGrLMS60bip3LjM8MIrkFuV#0uR4sikAcVh@W#V#

		package require {ycl proc}
		[yclprefix] proc alias alias [yclprefix] proc alias
		alias aliases [yclprefix] proc aliases

		aliases {
			{ycl chan chan} {
				command
			}
			{ycl dict deep}
			{ycl dict} {
				ddict deep
			}
			{ycl keep keep}
			{ycl list} {
				take
			}
			{ycl proc} {
				optswitch
			}
			{ycl string printable}
		}

		package require pki


		proc accept {} {
			variable responders {}
			variable requests
			while 1 {
				if {[llength $requests]} {
					take requests request
					take request chan host port
				} else {
					lappend responders [info coroutine]
					yield
					continue
				}
				chan configure $chan -encoding utf-8 -eofchar {} -translation lf
				repl $host $port $chan
			}
		}


		proc enqueue args  {
			variable maxrequests
			variable responders
			variable requests
			variable serverchan

			if {[llength $requests] > $maxrequests} {
				puts [list {too many requests}]
				take args chan host port
				catch {error {try again later}} cres copts
				respond $chan 0 $cres $copts
				close $serverchan
				after 1000 [list [namespace which mayberestart]]
			} else {
				puts [list enqueing request $args]
				lappend requests $args
				if {[llength $responders]} {
					take responders responder
					after 0 [list $responder]
				}
			}
			return
		}


		proc listen args {
			variable maxrequests
			variable serverargs
			variable serverport
			variable serverchan
			variable requests
			#package require tls
			dict size $args
			if {![info exists serverargs]} {
				set serverargs {}
			}
			if {![info exists maxrequests]} {
				set maxrequests 512
			}
			foreach {opt val} $args {
				switch $opt {
					port {
						set serverport $val
					}
					default {
						error [list {unknown option} $opt]
					}
				}
			}
			set serverchan [socket -server [list [
				namespace which enqueue]] {*}$serverargs $serverport]
			set sockets [chan configure $serverchan -sockname]
			puts [list listening sockets $sockets]
		}


		proc mayberestart args {
			variable maxrequests
			variable requests
			if {$requests < $maxrequests} {
				listen
			} else {
				after 1000 [list [namespace which mayberestart]]
			}
		}


		proc respond {chan encrypted cres copts} {
			ddict set rheader counter [keep counter]
			set response [list $rheader [list $cres[set cres {}] $copts[
				set copts {}]]]
			puts [list response size [
				string length $response] value [
					printable tcl 0 ascii 0 [string range $response 0 511]]]
			if {$encrypted} {
				puts [list {encrypting response}]
				ddict set rheader2 encrypted 1

				set key [keep pkikey]

				#set mypubkey [keep pubkey]
				#set encrypted [keep encrypt -binary -pad -priv -- \
				#	$response #$key]
				#set decrypted [keep decrypt -binary  -unpad -pub -- \
				#	$encrypted $mypubkey]
				#if {$decrypted eq $response} {
				#	puts [list {decryption check ok}]
				#} else {
				#	error [list {encryption check failed} {original len} [
				#		string length $response] {decrypted len} [
				#			string length $decrypted] ]
				#	]
				#	puts [list {original value}]
				#	puts [printable tcl 0 ascii 0 $response]
				#	puts [list {decrypted value}]
				#	puts [printable tcl 0 ascii 0 $decrypted]
				#	error [list {encryption failure}] 
				#}

				set response [list $rheader2 [
					keep encrypt -pad -binary -priv -- $response $key]]

			}
			puts [list responding chan $chan size [
				string length $response] value [
					printable tcl 0 ascii 0 [string range $response 0 511]]]
			puts $chan [list $response]
			flush $chan
			# for now, let the client close the channel
			# {to do} channel monitor
			#close $chan
		}


		proc main {argv0 argv} {
			variable responders {}
			variable requests {}

			set listenargs {}

			while {[llength $argv]} {
				take argv arg
				optswitch $arg {
					keep {
						take argv keep
					}
					port {
						take argv port
					}
				}
			}

			[yclprefix] keep keep .new [namespace current]::keep
			puts [list initializing keep]
			keep .init workdir $keep
			keep pkikey
			puts [list done initializing]
			if {![info exists port]} {
				set port [dict get [keep defaults] listen port]
			}
			coroutine accept[info cmdcount] accept
			lappend listenargs port $port
			listen {*}$listenargs
		}


		proc repl {host port chan} {
			while 1 {
				puts [list request from host $host port $port chan $chan]
				set count [command $chan command]
				if {$count < 0} {
					if {[eof $chan]} {
						close $chan
						break
					} else {
						continue
					}
				}
				set status [catch {
					puts [list received command [printable tcl 0 ascii 0 [
						string range $command 0 511]]]
					lassign $command[set command {}] header command
					if {[dict exists $header encrypted]} {
						set encrypted 1
						set key [keep pkikey]
						set command [keep decrypt -binary -unpad -priv -- $command[
							set command {}] $key]
						puts [list decrypted [printable tcl 0 ascii 0 [
							string range $command 0 511]]]
						lassign $command[set command {}] header1 command
					} else {
						set encrypted 0
					}
					set command [zlib decompress $command[set command {}]]
					keep dispatch {*}$command
				} cres copts]
				try {
					respond $chan $encrypted $cres $copts
				} on error {cres copts} {
					puts stderr [list {error resonding} \
						chan $chan host $host port $port]
					puts stderr [dict get $copts -errorinfo]
					break
				}
			}
		}


		after 0 [list [namespace which main] $argv0 $argv]
		vwait [namespace current]::status
		exit $status
	}
} [namespace current]]