stack 16
interrupts 32
data 1 512
code auto

str devnamef "DEV:\\sock\0\0\0\0"
str devname "sock0"
str args_connect "\x3\0\0"
str index "index.htm"
str notfounddoc "404.htm"
str defaultresponse "HTTP/1.1 200 OK\r\n"
str notfoundresponse "HTTP/1.1 404 Not Found\r\n"
str cacheresponse "Cache-Control: no-store\r\n\r\n"
str htdocdir "C:\\htdocs\\"
str dirheader "header.dir"
str dirfooter "footer.dir"
word sock_desc

; change directory to htdocs
mov bx $htdocdir
mov ax 4
sys

mov ax $args_connect
inc ax
; port to listen
mov *ax 80

str sending_command "Sending command to driver."
mov ax $sending_command
;printstr ax

:open_port
mov ax 29
mov bx $devname
mov cx $args_connect
sys
;printuint ax
mov hx -1
cmp ax hx
jne port_opened_ok

; print error number
mov ax 38
sys
printuint ax

jmp open_port

:port_opened_ok
mov $sock_desc ax

str port_opened "Port opened."
mov ax $port_opened
printstr ax

mov ax $sock_desc
mov bx $devnamef
mov cx 9
add bx cx

call hextostr

str listening "Listening connections..."
mov ax $listening
printstr ax

mov ax 2
mov bx 0
mov cx 0
mov dx $devnamef
mov ex 0
;printstr dx
sys

word accepted_socket

mov $sock_desc ax
:accept_loop
mov ax 0
mov cx 2
mov bx &accepted_socket
mov dx $sock_desc
sys

mov ax $accepted_socket
mov bx $devnamef
mov cx 9
add bx cx

call hextostr

mov dx $devnamef
printstr dx

; open the accepted socket as file
mov ax 2
mov bx 0
mov cx 0
mov dx $devnamef
mov ex 0
sys
printsint ax
cmp ax hx
; if file could not be opened
je accept_loop

; else give the file descriptor to clone
mov fx ax

; make a clone
word clone_return
mov ax 16
mov bx .serve_client
mov cx &serve_client
mov dx 0
mov ex &clone_return
sys

; did the clone syscall fail?
cmp ax -1
je accept_close_socket

; create timeout thread
push fx
mov fx ax
mov ax 16
mov bx .timeout_thread
mov cx &timeout_thread
mov dx 0
mov ex &clone_return
sys
pop ex

; did the clone syscall fail?
cmp ax -1
; if yes, kill the previous clone
je accept_kill_clone

jmp accept_loop

:accept_close_socket
mov bx fx
mov ax 3
sys
jmp accept_loop

:accept_kill_clone
mov ax 23
mov bx fx
mov cx 15
sys
mov fx ex
jmp accept_close_socket


:serve_client
;set TERM signal handler
setint 15 .timeout_handler $timeout_handler
;move socket descriptor to fx
mov fx ax
;printuint ax

; allocate new segment for the buffer
mov ax 9
mov bx 0
mov cx 512
mov dx 0
mov ex 0
sys
mov hx -1
cmp ax hx
je close_socket

; set data segment
dseg ax

; read from socket to new data segment
:read_from_socket
mov dx fx
mov ax 0
mov bx 0
mov cx 511
sys
printsint ax
cmp ax hx
je serv_end

; find whitespace
mov dx 0
mov ah ' '
:whitespace_loop1
mov al *dx
cmp ah al
je whitespace_loop1_end
cmp dx cx
jge serv_end
inc dx
jmp whitespace_loop1
:whitespace_loop1_end

;find first character after whitespace
:whitespace_loop2
mov al *dx
cmp ah al
jne whitespace_loop2_end
cmp dx cx
jge serv_end
inc dx
jmp whitespace_loop2
:whitespace_loop2_end

; save position of requested path
push dx

; find first character that is less or equal than whitespace
; or a question mark
mov el '?'
:whitespace_loop3
mov al *dx
cmp al ah
jle whitespace_loop3_end
cmp al el
je whitespace_loop3_end
cmp dx cx
jge serv_end
inc dx
jmp whitespace_loop3
:whitespace_loop3_end

;null-terminate path
mov al 0
mov *dx al

; does the path end with '/'?
mov ax dx
dec ax
pop dx
cmp ax dx
je open_file
mov al *ax
cmp al '/'
je list_dir

; open file
:open_file
mov ax 2
mov ex 0
mov bx 0
mov cx 0
;mov dx fx
;pop dx
call fix_httpget_path
printstr dx
sys

printsint ax

cmp ax hx
jne file_found

:jump_notfound
mov bx 0
mov ax 2
mov ex 0
mov cx 0
push ds
dseg bx
mov dx $notfounddoc
sys
printstr dx
pop ds

printsint ax

mov cx $notfoundresponse
jmp file_not_found

:file_found
mov cx $defaultresponse
:file_not_found

push dx

; copy file descriptor to ex
mov ex ax

; send http header
; get length of header
push ds
dseg bx
;mov ax $defaultresponse
mov ax cx
;mov bx ax
call strlen
;length to cx
;mov cx ax
xchg ax cx
;mov ax 1
;mov dx fx
;sys
call send_socket
; send cache-control
mov cx $cacheresponse
mov ax cx
;mov bx ax
call strlen
xchg cx ax
;mov cx ax
;mov ax 1
;sys
call send_socket
pop ds

pop ax

str htastr ".HTA"

; does the filename end with .HTA?
xor bl bl
call strchr
sub ax 4

push ex

mov bx ax
mov ax ds
xor dx dx
mov ex $htastr
mov cx 4
memcmp

pop ex

; if not, send normal file
jcz server_script_file
jmp normal_file

; else send line by line
:server_script_file
xor cx cx
push cx
push cx

:script_send_line
;read one byte and write to socket
mov dx ex
xor ax ax
xor bx bx
mov cx 1
sys
cmp ax hx
je close_file

; are we in the beginning of a line?
pop cx
mov dl *bx

jcz script_line_begin

; are we in the second character of a line?
cmp cx 1
; if no
jne script_line_cont
; if yes, was the previous character a beginning of a html tag?
pop dx
cmp dx 1
push dx
mov dx *bx
; if no, handle this line normally
jne script_line_cont
; if yes, is this character a '?'
mov dl *bx
cmp dl '?'
; if no, handle this line normally
jne send_start_tag
; else this run an inline app
call run_inline_app
;jmp script_zero_newline_cntr
pop cx
xor cx cx
push cx
push cx
jmp script_send_line

:send_start_tag
mov bx 1
mov *bx '<'
mov ax bx
mov cx bx
mov dx fx
sys
xor bx bx
jmp script_line_cont

:script_line_begin
; is this '<'
cmp dl '<'
jne script_line_notag
; if yes, increment the second word in the stack
;printuint hx
pop dx
inc dx
push dx
;jmp script_no_newline
inc cx
push cx
jmp script_send_line


:script_line_notag

:script_line_cont
; is this a newline?
cmp dl 0x0A
; if no, increment cx normally
jne script_no_newline

; else zero cx
:script_zero_newline_cntr
pop cx
xor cx cx
push cx
jmp script_yes_newline

:script_no_newline
inc cx
:script_yes_newline
push cx

mov cx ax
jcz close_file
mov ax 1
mov dx fx
sys
cmp ax hx
je close_file

jmp script_send_line

:run_inline_app
push ax
push bx
push cx
push dx

; read file until newline
xor bx bx

:run_inline_app_rdloop
mov ax 0
mov cx 1
mov dx ex
sys

cmp ax hx
je close_file
cmp ax 0
je close_file
mov al *bx
cmp al 0x0A
je inline_app_rdloop_end

inc bx
jmp run_inline_app_rdloop

:inline_app_rdloop_end
mov *bx 0
xor bx bx

mov ax bx
mov bl ' '
call strchr
; now [0] == application name, [ax] == arguments

mov bx ax
xor ax ax
mov *bx al
inc bx
printstr ax
printstr bx
push bx

; create a fork
push ex
setint 18 .inline_runapp_chldret $inline_runapp_chldret
mov ax 17
mov dx 18
xor ex ex
sys
pop ex

pop dx

; is this the original?
cmp ax 0
; if yes, go to end
jne inline_runapp_end

; dup socket to fd 1 (stdout)
mov ax 32
mov bx fx
mov cx 1
sys

; execute the program
mov ax 30
xor bx bx
mov cx dx
sys

; if still alive, exit
mov ax 12
mov bx -1
sys

:inline_runapp_end
; did the fork syscall fail?
cmp ax -1
; if yes, exit the thread
je exit

; create timeout thread
push ex
push fx
mov fx ax
mov ax 16
mov bx .timeout_thread
mov cx &timeout_thread
mov dx 0
mov ex 0
sys
; did the clone syscall fail?
cmp ax -1
; if yes, kill the fork and exit
je inline_killfork
pop fx
pop ex

; wait for signal 18
wait

pop dx
pop cx
pop bx
pop ax

ret

:inline_runapp_chldret
iret

:inline_killfork
mov ax 23
mov bx fx
mov cx 15
sys
jmp exit

; read from file and write to socket
; ex = fd
:serve_loop
mov dx ex
mov ax 0
mov bx 0
mov cx 512
sys
cmp ax hx
je send_loop_end

;printuint ax

mov cx ax
jcz send_loop_end
mov ax 1
mov dx fx
sys

cmp ax hx
je send_loop_end

;printuint ax

; test that everything was sent
:send_loop
cmp ax cx
; if was, read more data from the file
je serve_loop

;else loop until everything is sent
add bx ax
sub cx ax
mov ax 1
sys
cmp ax hx
je send_loop_end
jmp send_loop

:send_loop_end
ret

:normal_file
call serve_loop

:close_file
mov bx ex
mov ax 3
sys

printsint ax

:serv_end
; free memory
mov ax 0
mov bx ds
dseg ax
mov ax 11
sys

:close_socket
; close socket
mov ax 3
mov bx fx
sys

;printuint ax

; exit
:exit
mov ax 12
mov bx 0
sys

:timeout_handler
; if file is open
mov hx 0
mov bx ex
cmp bx hx
je timeout_file_not_open
mov ax 3
sys
printsint ax
:timeout_file_not_open
; close socket
mov bx fx
mov ax 3
sys
printsint ax
jmp exit

:timeout_thread
; AX = server thread
mov hx ax
; get time
mov ax 18
sys
zflags
; increment time by 120 sec and sleep
mov ex 120
add dx ex
; if carry is not set, jump to timeout_pause
jfns 0x80 timeout_pause
; else increment cx
inc cx
:timeout_pause
mov ex dx
mov dx cx
mov cx bx
mov bx ax
mov ax 22
sys
;send TERM signal to server thread
mov ax 23
mov bx hx
mov cx 15
sys

jmp exit

; ax = number
; bx = pointer to buffer
; writes number in ax to buffer in [bx] as base-16
:hextostr
push ax
push bx
push cx
push dx
push ex
push fx

mov dh 'A'
mov dl '0'
mov eh 9
mov el 4

mov cx el

:hextostr_loop

mov fl ah
shr fl el
shl ax el

cmp fl eh
jg hextostr_highnums

add fl dl
jmp hextostr_addchar

:hextostr_highnums
sub fl eh
dec fl
add fl dh

:hextostr_addchar
mov *bx fl
inc bx

dec cx
jcz hextostr_end
jmp hextostr_loop

:hextostr_end
pop fx
pop ex
pop dx
pop cx
pop bx
pop ax
ret

; dx == pointer to path
:fix_httpget_path
push ax
push bx
push cx

mov cx 0

; replace /:s with \:s
push dx
mov ah '/'
:fix_httpget_path_loop1
mov cl *dx
jcz fix_httpget_path_loop1_end
cmp ah cl
jne fix_httpget_path_loop1_1
mov cl '\\'
mov *dx cl
:fix_httpget_path_loop1_1
inc dx
jmp fix_httpget_path_loop1
:fix_httpget_path_loop1_end

; increment pointer while path begins with \
pop dx
;printstr dx
mov ah '\\'
:fix_httpget_path_loop2
mov cl *dx
cmp ah cl
jne fix_httpget_path_loop2_end
jcz fix_httpget_path_loop2_end
inc dx
jmp fix_httpget_path_loop2
:fix_httpget_path_loop2_end

; if path contains drive letter, return empty string
push dx
mov ah ':'
inc dx
mov al *dx
cmp ah al
je fix_httpget_path_return0
pop dx
;printstr dx

; if path contains .., return index document
push dx
mov ah '.'
:fix_httpget_path_loop3
mov cl *dx
jcz fix_httpget_path_loop3_end
cmp ah cl
jne fix_httpget_path_loop_2
inc dx
mov al *dx
cmp ah al
je fix_httpget_path_return0
:fix_httpget_path_loop_2
inc dx
jmp fix_httpget_path_loop3
:fix_httpget_path_loop3_end
pop dx
;printstr dx

; if the path is now empty, return index
mov cl *dx
jcz fix_httpget_path_return1

pop cx
pop bx
pop ax
ret

:fix_httpget_path_return0
pop dx
:fix_httpget_path_return1
push dx
mov ax $index
:copyindexloop
push ds
mov ds 0
mov cl *ax
pop ds
mov *dx cl
jcz copyindexloop_end
inc ax
inc dx
jmp copyindexloop
:copyindexloop_end
pop dx

pop cx
pop bx
pop ax
ret

; AX == pointer to str
; Returns length of the string to AX
:strlen
push cx
push bx
mov bx ax
mov ax 0
mov cx 0
:strlen_loop
mov cl *bx
jcz strlen_end
inc ax
inc bx
jmp strlen_loop
:strlen_end
pop bx
pop cx
ret

:strchr
; AX == pointer to str
; BL == character to find
; returns pointer to AX, null ptr if not found
push bx

:strchr_loop
mov bh *ax
cmp bh bl
je strchr_end
cmp bh 0
je strchr_notfound
inc ax
jmp strchr_loop

:strchr_notfound
xor ax ax

:strchr_end
pop bx
ret

:list_dir
call fix_httpget_path
mov bx dx

; open directory
mov ax 5
printstr bx
sys
printsint ax
cmp ax -1
je jump_notfound
push ax

push bx

; open dirheader
mov ax 2
push ds
mov ds 0
xor cx cx
mov dx $dirheader
mov ex cx
mov bx ex
sys
pop ds
pop bx

cmp ax -1
je jump_notfound

mov ex ax

; change dir
mov ax 4
sys

push ax

push ds
mov ds 0
mov ax $defaultresponse
call strlen
mov cx ax
mov ax $defaultresponse
call send_socket
mov ax $cacheresponse
call strlen
mov cx ax
mov ax $cacheresponse
call send_socket

pop ds

pop ax

call serve_loop

mov bx ex
mov ax 3
sys

; read directory
:readdir_loop

xor cx cx
pop bx
mov ax 6
sys
cmp ax -1
je readdir_end
push bx

xor ax ax
call strlen
xor cx cx
mov bx ax
inc bx
mov ax 10
sys
;printstr cx
;printsint ax
mov cl *bx
test cl 0x10
je no_directory

mov *bx 0
dec bx
mov *bx '/'

:no_directory


; print dirlist
str dirlink1 "<a href = \""
str dirlink2 "\">"
str dirlink3 "</a><br>\n"
push ds
mov ds 0

mov ax $dirlink1
mov cx 11
call send_socket

pop ds
xor ax ax
call strlen
push ax
mov cx ax
xor ax ax
call send_socket

push ds
mov ds 0
mov ax $dirlink2
mov cx 2
call send_socket

pop ds
pop cx
xor ax ax
call send_socket

push ds
mov ds 0
mov ax $dirlink3
mov cx 9
call send_socket

pop ds
jmp readdir_loop

:readdir_end
; close directory
mov ax 7
sys

; open dirfooter
push ds
mov ds 0
mov ax 4
mov bx $htdocdir
sys
mov ax 2
xor cx cx
mov dx $dirfooter
mov ex cx
mov bx ex
sys
pop ds
cmp ax -1
je jump_notfound

mov ex ax
call serve_loop

mov bx ex
mov ax 3
sys

jmp serv_end

:send_socket
mov dx fx
mov bx ax
:send_socket_loop
mov ax 1
sys
cmp ax -1
jmp send_socket_end
sub cx ax
jcz send_socket_end
add bx ax
jmp send_socket_loop
:send_socket_end
ret
