;----------------------------------------------------------------------------
;File name:	FTPD.S				Revision date:	1997.08.27
;Creator:	Ulf Ronald Andersson		Creation date:	1997.08.27
;(c)1997 by:	Ulf Ronald Andersson		All rights reserved
;Feedback to:	dlanor@oden.se			Released as FREEWARE
;----------------------------------------------------------------------------
;Required header declarations:
;
	.include	"uran\STRUCT.SH"	;PASM adapted structures
	.include	"uran\URAn_SYS.SH"	;Main system definitions
	.include	"uran\URAn_LA.SH"	;Line A variables etc
	.include	"uran\URAn_DOS.SH"	;GEMDOS, BIOS, XBIOS
	.include	"uran\URAn_GEM.SH"	;VDI & AES
	.include	"uran\URAn_SIM.SH"	;some AES funcs for TOS/TTP
	.include	"uran\URAn_APP.SH"	;General application support
	.include	"uran\URAn_JAR.SH"	;Cookie jar handling
	.include	"uran\URAn_AV.SH"	;AV/VA protocol + MP protocol
;
	.include	"sting\LAYER.SH"	;only used by servers
	.include	"sting\TRANSPRT.SH"	;used by servers/clients
	.include	"sting\NET_TCON.SH"	;handles network time conv.
	.include	"sting\DOMAIN.SH"	;handles domain name/ip conv.
;
;----------------------------------------------------------------------------
;
MAX_VA_ARGLEN	equ	512
;
;----------------------------------------------------------------------------
;Add client/server dependent assemblytime constants/variables here
;
FTP_PORT	equ	21		;standard ftp control port
FTP_DATA_PORT	equ	20		;standard ftp data port
;
struct	ftpch
	struc_p	ftpch_next		;-> next channel in queue, or is NULL
	uint16	ftpch_tcp_f		;0 = TCP,  1 = UDP
	uint16	ftpch_main_h		;TCP or UDP ftp main handle
	uint16	ftpch_data_h		;TCP or UDP ftp data handle
	d_s	ftpch_cmdbf,256		;256 char incoming command buffer
	d_s	ftpch_resbf,256		;256 char outgoing response buffer
d_end	ftpch
;
;----------------------------------------------------------------------------
;
;;;huge_program		= 1	;uncomment if TEXT+DATA sections are > 32KB
;;;keep_windows		= 1	;uncomment to keep ACC vwk in main eventloop
;;;exec_timeout		= 100	;uncomment to enable timer in main eventloop
exec_message		= 1	;uncomment for messages in main eventloop
;
;----------------------------------------------------------------------------
;
	.text
;
	start_app
;
;----------------------------------------------------------------------------
;
	.ifeq	huge_program
	.data
	.endif
;
;Edit the string below, to match the accessory title you want in ACC mode
;
acc_name:
	dc.b	'  STinG FTPD server',NUL	;for ACC menu registration
	dc.b	NUL
	even
;
;Also edit the string below, to match the application name, without
;file extension, padded to 8 bytes.  Normally this will be overwritten
;by runtime info from 'shel_read' during init, but do it just in case...
;
id_app_name_s:
	dc.b	'FTPD    ',NUL
	even
;
;----------------------------------------------------------------------------
;
	.ifeq	huge_program
	.bss
	.endif
;
message:	ds.w	8		;evnt_mesag message pipe
intin:		ds.w	30		;30 words or more for AES/VDI
intout:		ds.w	45		;45 words or more for AES/VDI
ptsin:		ds.w	30		;30 words or more for VDI
ptsout:		ds.w	12		;12 words or more for VDI output coordinates
		ds.l	200		;subroutine stack >= 100 longs
mystack:	ds.l	1		;top of subroutine stack
;
;----------------------------------------------------------------------------
;
	.text
;
;----------------------------------------------------------------------------
;	Constants, Variables, and Usage of the URAn_APP.S library
;
;rz		'relative zero' for internal references
;basepage_p	-> program's base page
;tsr_size	.L size of BASEPAGE+TEXT+DATA+BSS  (only TOS/TTP may TSR)
;progtype	.L indicates program type:
;  ...			$0000.$FFFF == TOS/TTP/boot_PRG
;  ...			$0000.ap_id == APP/GTP/non_boot_PRG
;  ...			$FFFF.ap_id == ACC
;acc_flag	is the high word of 'progtype'
;tos_flag	is the low word of 'progtype'
;bootflag	.W $FFFF == PRG booted from AUTO (also sets 'tos_flag')
;menu_id	.W menu id for an ACC, otherwise null
;g_handle	.W workstation handle for ACC/APP/GTP/non_boot_PRG
;vwk_handle	.W virtual workstation handle
;contrl		12 words for AES/VDI control data
;
;wk_x_rez \
;wk_y_rez  \/ from
;wk_pix_w  /\ opnvwk
;wk_pix_h /
;
;MiNT_p		.L NULL or -> MiNT structure
;MagX_p		.L NULL or -> MagX structure
;nAES_p		.L NULL or -> nAES structure
;Gnva_p		.L NULL or -> Gnva structure
;
;line_a_base_p	.L -> line_a variable base
;kbshift_p	.L -> kbshift byte of OS
;currbp_p_p	.L -> OS var -> current basepage
;
;NB: if symbol 'huge_program' is defined, above data is in TEXT section (else in BSS)
;NB: defining 'huge_program' also makes function calls use 'jsr' (instead of bsr)
;
;Required user routines:
;
;init_app	called for ACC/APP/GTP/non_boot_PRG to init application,
;		but doesn't need appl_init, graf_handle, or v_opnvwk,
;		nor does an ACC need menu_register.
;		Suitable use is for initialization of object trees.
;NB:  for ACC	menu_register is called between init_app and exec_app
;
;exec_app	called to execute the main application regardless of type,
;		but doesn't need v_clsvwk, or appl_exit, or ACC appl_mesag.
;		This call will be repeated for a reactivated ACC.
;		Non_acc programs should have exit code in d0 at RTS.
;		(See details at 'Exit codes:' below)
;
;	At entry to either of these two routines:
;
;		d6.W	== bootflag	\
;		d7.L	== progtype	 > See descriptions above.
;		a6.L	== rz		/
;
;Optional user routines:
;
;exec_timer	Called for ACC that has a defined constant 'exec_timeout',
;		whenever evnt_multi produces such a timer event.
;		The constant is placed as long at 'main_timeout', and may
;		there be dynamically adjusted by the program.
;
;exec_mesag	Called for ACC that has a defined constant 'exec_message',
;		whenever evnt_multi/evnt_mesag produces messages that are
;		not AC_OPEN  (such as VA_START).
;
;		If the constant 'keep_windows' is also set, the workstation
;		will not be closed at each return (you must obey AC_CLOSE).
;		This places a word == $FF00 at 'keep_wind_f', and if the top
;		byte is cleared the workstation closure is enabled again.
;
;NB:	Top bit of the word 'revise_type_f' is used for 3 purposes:
;   1:	Let ACC start 'exec_app' directly without awaiting event.
;   2:	Let APP delay 'exec_app' until an event occurs.
;   3:	Let APP loop back for more events after 'exec_app'
;The flag must be set by 'init_app' in the first two cases, and in case 3
;should be set/cleared in 'exec_app' to decide whether to exit program.
;
;Exit codes:	At exit from exec_app, d0 has the following effects
;		when the program was not started as accessory.
;
; negative	=> Pterm(d0)			=> error code exit
; 0		=> Pterm(d0)			=> error free exit
; 0x0000ADD0	=> Ptermres(tsr_size,0)		=> error free resident exit
; 0x0000ADD1	=> Ptermres(d1,0)		=> error free resident exit
; 0x0000ADD2	=> Ptermres(d1,d2)		=> error code resident exit
;
;----------------------------------------------------------------------------
;Start of:	init_app
;----------------------------------------------------------------------------
;
init_app:
;
	include		"uran\URAn_ARG.S"		;load ARGV handler
;
; Here URAn_ARG.S defines two variables
;
;arg_cnt	== number of strings on command line
;arg_ptr	-> NUL_separated argument strings + final NUL
;
;NB:	Both of the above include a dummy program name (first string==NUL).
;-------
	clr.l		VA_arg_p
	cmp		#2,arg_cnt
	blo.s		.done_arg
	move.l		arg_ptr,a0
	str_pass	a0
	move.l		a0,VA_arg_p
.done_arg:
;
	st		fatal_f			;assume fatal errors can happen
	gemdos		Super,0.w
	move.l		d0,d4
	eval_cookie	#"STiK"
	move.l		d0,d3				;d3 = d0 -> DRV_LIST structure
	gemdos		Super|_ind,d4
	move.l		d3,sting_drivers		;sting_drivers -> DRV_LIST structure
	ble		.STiK_not_found
	move.l		d3,a3				;a3 -> DRV_LIST structure
	lea		DRV_LIST_magic(a3),a0
	lea		STiKmagic_s(pc),a1
	moveq		#10-1,d0
.strcmp_loop:					;loop to test STiKmagic of DRV_LIST
	cmpm.b		(a0)+,(a1)+
	dbne		d0,.strcmp_loop
	bne		.STiK_not_valid
;
	move.l		DRV_LIST_get_dftab(a3),a0	;a0 -> get_dftab function
	pea		TRANSPORT_DRIVER_s		;-(sp) = "TRANSPORT_TCPIP"
	jsr		(a0)				;call get_dftab
	addq		#4,sp
	move.l		d0,tpl				;store pointer in 'tpl'
	ble		.driver_not_valid
;
	move.l		DRV_LIST_get_dftab(a3),a0	;a0 -> get_dftab function
	pea		MODULE_DRIVER_s			;-(sp) = "MODULE_LAYER"
	jsr		(a0)				;call get_dftab
	addq		#4,sp
	move.l		d0,stx				;store pointer in 'stx'
	ble.s		.layer_not_valid
	clr		fatal_f
;
;Add client/server dependent init here
;
	bsr		update_ports
	move.l		ftp_tcp_h,d3
	and.l		ftp_udp_h,d3
	bmi.s		.no_ports_open
	clr.l		initerr_mess_p
	rts
;
.STiK_not_found:
	lea		STiK_not_found_al_s,a0
	bra.s		.init_error
;
.STiK_not_valid:
	lea		STiK_not_valid_al_s,a0
	bra.s		.init_error
;
.driver_not_valid:
	lea		driver_not_valid_al_s,a0
	bra.s		.init_error
;
.layer_not_valid:
	lea		layer_not_valid_al_s(pc),a0
	bra.s		.init_error
;
.no_ports_open:
	lea		no_ports_open_al_s,a0
.init_error:
	move.l		a0,initerr_mess_p
	rts
;
;----------------------------------------------------------------------------
;End of:	init_app
;----------------------------------------------------------------------------
;Start of:	exec_app
;----------------------------------------------------------------------------
;
exec_app:
	move.l		initerr_mess_p,d3
	beq.s		no_init_err
	move.l		d3,a3
	sim_aes		form_alert,#1,(a3)
	tst		fatal_f
	bne		exit_exec_err
no_init_err:
	tst.w		d7			;GEM/TOS program ?
	bmi.s		exec_TOS		;go run TOS/TTP code
exec_GEM:
	tst.l		d7			;APP/ACC program ?
	bmi		exec_multi_GEM		;go run ACC code
	cmp		#1,global+2		;Singletasking APP ?
	beq.s		exec_single_GEM		;then go show error message and exit program
exec_multi_APP:
	btst		#7,revise_type_f	;been here before ?
	bne		exec_multi_GEM		;then behave like an ACC this time
	tst.l		d3			;init_error ?
	bne.s		exit_exec_err		;go exit program on init errors (bad STiK etc)
	bset		#7,revise_type_f	;tell main loop to behave like ACC
	cmp		#1,arg_cnt		;any arguments for this APP
	bgt		exec_multi_GEM		;then start directly
	bra		exit_exec_ok		;exit to main event loop
;
;NB: In multitasked APP mode, this code exits without reaching the user code
;    so as to let an autostarted APP simulate an ACC.  The user code will only
;    be reached on reception of VA_START or AC_OPEN messages.  This can easily
;    be changed however, by editing the code above.  Note that the present code
;    already overrides this 'delay' if an argument was used in program launch.
;    So with argument the user code starts directly, but without argument it is
;    only reached after AC_OPEN or VA_START messages.
;
;-------------------------------------
;
exec_TOS:
	lea		must_be_GEM_al_s,a3
	bra.s		exec_APP_error
;
;-------------------------------------
;
exec_single_GEM:
	lea		must_be_multi_al_s,a3
exec_APP_error:		;jump here to display alert (a3) and then exit
	sim_aes		form_alert,#1,(a3)	;NB: has simulation for TOS/TTP
exit_exec_err:		;jump here to exit with error code -1
	moveq		#-1,d0
exit_terminate:		;jump here to exit with error code in d0
	move.l		d0,d3
	bclr		#7,revise_type_f
;
	lea		ftp_tcp_h(pc),a3
	bsr		close_tcp_port
	lea		ftp_udp_h(pc),a3
	bsr		close_udp_port
;
	lea		active_ftp_q(pc),a3
.loop:
	move.l		(a3),d0			;a3 -> ftp channel or is NULL
	ble.s		.exit
	move.l		d0,a0			;a0 -> ftp channel
	bsr		close_channel
	bra.s		.loop
;
.exit:
	move.l		d3,d0
	bra.s		exit_exec_app
;
;-------------------------------------
;
exec_multi_GEM:
;
;Add client/server work for multi-tasking GEM program here  (ACC or APP/PRG/GTP)
;
	bra		exit_exec_ok
;
;-------------------------------------
;
exit_exec_ok:
	clr.l	d0
exit_exec_app:
	rts
;
;----------------------------------------------------------------------------
;End of:	exec_app
;----------------------------------------------------------------------------
;Start of:	exec_timer
;----------------------------------------------------------------------------
;
	.ifne		exec_timeout
exec_timer:
	lea	ftp_tcp_h(pc),a3
	tst	(a3)
	bmi.s	.done_ftp_TCP
	CNbyte_count	(a3)
	tst	d0
	bmi.s	.done_ftp_TCP
	bsr	service_ftp_TCP
.done_ftp_TCP:
	lea	ftp_udp_h(pc),a3
	tst	(a3)
	bmi.s	.done_ftp_UDP
	CNbyte_count	(a3)
	tst.l	d0
	bmi.s	.done_ftp_UDP
	bsr	service_ftp_UDP
.done_ftp_UDP:
exit_exec_timer:
	rts
	.endif		exec_timeout
;
;----------------------------------------------------------------------------
;End of:	exec_timer
;----------------------------------------------------------------------------
;Start of:	exec_mesag
;----------------------------------------------------------------------------
;
	.ifne		exec_message
exec_mesag:
	cmp		#AC_CLOSE,message
	bne.s		.not_AC_CLOSE
	sf		keep_wind_f
	bra		exit_exec_mesag
;
.not_AC_CLOSE:
	cmp		#AP_TERM,message
	bne.s		.not_AP_TERM
	tst.l		d7
	bmi		exit_exec_mesag		;AP_TERM is not for ACCs
	clr.l		d0			;flag no error
	bra		exit_terminate
;
.not_AP_TERM:
	cmp		#VA_START,message
	bne		.not_VA_START
	st		VA_START_f
	move		message+2,AV_partner_id
	move.l		message+6,VA_START_cmd_p
	AV_send		PROTOKOLL,#w_VA_START+w_AV_STARTED_A,id_app_name_s
;
;Add ALL handling of the passed command line here, if you need to do some later
;you must copy it here, because the original may disappear after AV_STARTED.
;
;One way of doing this is included below, but may not always be appropriate,
;so feel free to invent your own methods...
;
	move.l		VA_START_cmd_p(pc),a0	;a0 -> argument on Venus
	move.l		a0,a1			;a1 -> argument on Venus
	str_pass	a1
	sub.l		VA_START_cmd_p(pc),a1
	move.l		a1,d1			;d1 = length of argument
	move		#MAX_VA_ARGLEN,d0
	cmp.l		d0,d1			;argument too long ?
	blo.s		.length_ok
	move.l		d0,d1			;limit argument length
.length_ok:
	move.b		-1(a0,d1.w),d0		;save byte at termination point
	clr.b		-1(a0,d1.w)		;enforce limited termination
	move.l		a0,a1			;a1 -> argument on Venus
	lea		VA_arg_s(pc),a2		;a2 -> local argument area
	move.l		a2,VA_arg_p		;prep a pointer for future
	str_copy	a1,a2			;make a local copy of argument
	move.b		d0,-1(a0,d1.w)		;repair original copy
;
;The message sent below is needed to let Venus release argument space
;
	AV_send.i	STARTED,VA_START_cmd_p
	bra		exec_app
;
.not_VA_START:
	cmp		#VA_PROTOSTATUS,message
	bne.s		.not_VA_PROTOSTATUS
	move		message+8,VA_protostatus
	move		message+6,VA_protostatus+2
	bra		exit_exec_mesag
;
.not_VA_PROTOSTATUS:
exec_mesag_extend:
;
;Add client/server dependent message event work here
;
exit_exec_mesag:
	rts
	.endif		exec_message
;
;----------------------------------------------------------------------------
;End of:	exec_mesag
;----------------------------------------------------------------------------
;Start of:	subroutines
;----------------------------------------------------------------------------
;
update_ports:
	movem.l		d0/d3/a0/a3-a4,-(sp)
	moveq		#FTP_PORT,d3
	lea		ftp_tcp_h(pc),a3
	lea		ftp_tcp_s(pc),a4
	bsr		update_tcp_port
	lea		ftp_udp_h(pc),a3
	lea		ftp_udp_s(pc),a4
	bsr		update_udp_port
	movem.l		(sp)+,d0/d3/a0/a3-a4
	rts
;
;----------------------------------------------------------------------------
;
update_tcp_port:
	movem.l		d0-d2/a0-a2/a5,-(sp)
	lea		three2upper(pc),a5
	tst		(a3)
	bpl.s		.port_ok
	TCP_open	#ZERO_IP,d3,#TOS_NORMAL,#BUF_SIZE
	move		d0,(a3)
	bpl.s		.port_ok
	lea		three2lower(pc),a5
.port_ok:
	jsr		(a5)
	movem.l		(sp)+,d0-d2/a0-a2/a5
	rts
;
;----------------------------------------------------------------------------
;
update_udp_port:
	movem.l		d0-d2/a0-a2/a5,-(sp)
	lea		three2upper(pc),a5
	tst		(a3)
	bpl.s		.port_ok
	UDP_open	#ZERO_IP,d3
	move		d0,(a3)
	bpl.s		.port_ok
	lea		three2lower(pc),a5
.port_ok:
	jsr		(a5)
	movem.l		(sp)+,d0-d2/a0-a2/a5
	rts
;
;----------------------------------------------------------------------------
;
close_tcp_port:
	tst		(a3)
	bmi.s		.done_port
	TCP_close	(a3),#1
	st		(a3)
.done_port:
	rts
;
;----------------------------------------------------------------------------
;
close_udp_port:
	tst		(a3)
	bmi.s		.done_port
	UDP_close	(a3)
	st		(a3)
.done_port:
	rts
;
;----------------------------------------------------------------------------
;
three2lower:
	or.b	#$20,(a4)+
	or.b	#$20,(a4)+
	or.b	#$20,(a4)+
	rts
;
;----------------------------------------------------------------------------
;
three2upper:
	and.b	#$5F,(a4)+
	and.b	#$5F,(a4)+
	and.b	#$5F,(a4)+
	rts
;
;----------------------------------------------------------------------------
;
service_ftp_UDP:	;d0 = CNbyte_count, (a3) = UDP_handle
	clr		d3		;d3 flags UDP
	bra.s		service_ftp_start
;
;-------------------------------------
;
service_ftp_TCP:	;d0 = CNbyte_count, (a3) = TCP_handle
	moveq		#1,d3		;d3 flags TCP
service_ftp_start:
	KRmalloc	#sizeof_ftpch	;allocate ftp channel structure
	tst.l		d0		;success ?
	ble.s		.channel_done
	move.l		d0,a0
	move.l		active_ftp_q(pc),(a0)	;link new channel to queue
	move		(a3),ftpch_main_h(a0)	;init its handle
	move		d3,ftpch_tcp_f(a0)	;init its TCP/UDP flag
	clr		ftpch_data_h(a0)	;init data handle as zero
.channel_done:
	clr		(a3)		;remove this handle as 'listener'
	bsr		update_ports	;update (reopen) listeners
	rts	
;
;----------------------------------------------------------------------------
;
word2ascii4:
	and.l	#1<<16-1,d0
	divu	#1000,d0
	add	#'0',d0
	move.b	d0,(a1)+
	clr	d0
	swap	d0
	divu	#100,d0
	add	#'0',d0
	move.b	d0,(a1)+
	clr	d0
	swap	d0
word2ascii2:
	and.l	#1<<7-1,d0
	divu	#10,d0
	add	#'0',d0
	move.b	d0,(a1)+
	swap	d0
	add	#'0',d0
	move.b	d0,(a1)+
	rts
;
;----------------------------------------------------------------------------
;void	close_channel(FTPCH *ftp_channel);
;
close_channel:
	move.l		a0,d0			;d0 -> channel to be closed
	ble.s		.exit			;skip if pointer invalid
	lea		active_ftp_q(pc),a1	;a1 -> channel queue root
.loop:
	move.l		(a1),d1			;d1 -> next channel, or is NULL
	ble.s		.exit			;exit on queue end
	move.l		a1,a0			;a0 -> link to next channel
	move.l		d1,a1			;a1 -> next channel
	cmp.l		d1,d0			;is this the one we want ?
	bne.s		.loop			;loop back until that is found
	move.l		(a1),(a0)		;unlink it from chain
	move.l		a2,-(sp)		;push a2
	move.l		a1,a2			;a2 -> channel to be closed
	tst		ftpch_main_h(a2)	;does main handle exist ?
	beq.s		.main_handle_closed	;if not, bypass closing
	tst		ftpch_tcp_f(a2)		;TCP or UDP ?
	beq.s		.close_UDP
.close_TCP:
	TCP_close	ftpch_main_h(a2),#1	;close TCP handle
	bra.s		.main_handle_closed
;
.close_UDP:
	UDP_close	ftpch_main_h(a2)	;close UDP handle
.main_handle_closed:
	move		ftpch_data_h(a2),d0	;d0 = data handle or 0
	beq.s		.data_handle_closed	;if 0, bypass closing
	tst		ftpch_tcp_f(a2)		;TCP or UDP ?
	beq.s		.close_UDP_data
.close_TCP_data:
	TCP_close	ftpch_data_h(a2),#1	;close TCP data handle
	bra.s		.data_handle_closed
;
.close_UDP_data:
	UDP_close	ftpch_data_h(a2)	;close UDP data handle
.data_handle_closed:
	KRfree(a2)				;release ftp channel structure
	move.l		(sp)+,a2		;pull a2
.exit:
	rts
;
;----------------------------------------------------------------------------
;End of:	subroutines
;----------------------------------------------------------------------------
;	Library function code will be expanded here
;
	make		SIM_links
	make		JAR_links
	make		TCON_links
	make		DOMAIN_links
	make		AV_links
;
;----------------------------------------------------------------------------
;
text_limit:	.data
;
;----------------------------------------------------------------------------
;
STiKmagic_s:
	dc.b	'STiKmagic',NUL
TRANSPORT_DRIVER_s:
	dc.b	'TRANSPORT_TCPIP',NUL
MODULE_DRIVER_s:
	dc.b	'MODULE_LAYER',NUL
;
STiK_not_found_al_s:
	dc.b	'[3]['
	dc.b	'STinG FTPD server  Ver: 1.00:|'
	dc.b	'-----------------------------|'
	dc.b	'The STiK cookie is missing !]'
	dc.b	'[ Abort ]',NUL
;
STiK_not_valid_al_s:
	dc.b	'[3]['
	dc.b	'STinG FTPD server  Ver: 1.00:|'
	dc.b	'-----------------------------|'
	dc.b	'The STiK cookie is corrupted!]'
	dc.b	'[ Abort ]',NUL
;
driver_not_valid_al_s:
	dc.b	'[3]['
	dc.b	'STinG FTPD server  Ver: 1.00:|'
	dc.b	'-----------------------------|'
	dc.b	'TRANSPORT driver is missing !]'
	dc.b	'[ Abort ]',NUL
;
layer_not_valid_al_s:
	dc.b	'[3]['
	dc.b	'STinG FTPD server  Ver: 1.00:|'
	dc.b	'-----------------------------|'
	dc.b	'MODULE driver is missing !]'
	dc.b	'[ Abort ]',NUL
;
hello_there_al_s:
	dc.b	'[0]['
	dc.b	'STinG FTPD server  Ver: 1.00:|'
	dc.b	'-----------------------------|'
	dc.b	'Active ports in upper case:  |'
	dc.b	'FTP port 13:  '
ftp_udp_s:
	dc.b	                   'UDP  '
ftp_tcp_s:
	dc.b	                        'TCP  ]'
	dc.b	'[ Ok ]',NUL
;
no_ports_open_al_s:
	dc.b	'[3]['
	dc.b	'STinG FTPD server  Ver: 1.00:|'
	dc.b	'-----------------------------|'
	dc.b	'Attempts to open port failed,|'
	dc.b	'both for UDP and TCP !]'
	dc.b	'[ Abort ]',NUL
;
must_be_GEM_al_s:
	dc.b	'[3]['
	dc.b	'STinG FTPD server  Ver: 1.00:|'
	dc.b	'-----------------------------|'
	dc.b	'T_SERVER needs to use GEM, so|'
	dc.b	'TOS/TTP use does not work.]'
	dc.b	'[ Abort ]',NUL
;
must_be_multi_al_s:
	dc.b	'[3]['
	dc.b	'STinG FTPD server  Ver: 1.00:|'
	dc.b	'-----------------------------|'
	dc.b	'In singletasking TOS FTPD.ACC|'
	dc.b	'can only run as an ACCessory!]'
	dc.b	'[ Abort ]',NUL
	even
;
;----------------------------------------------------------------------------
;
;Add initialized constant data here
;----------------------------------------------------------------------------
;
ftp_tcp_h:	dc.w	-1
ftp_udp_h:	dc.w	-1
active_ftp_q:	dc.l	0
;
;----------------------------------------------------------------------------
;
commands:
	dc.l	"user"	;
	dc.l	"acct"	;
	dc.l	"pass"	;
	dc.l	"type"	;
	dc.l	"list"	;
	dc.l	"cwd"	;
	dc.l	"dele"	;
	dc.l	"name"	;
	dc.l	"quit"	;
	dc.l	"retr"	;
	dc.l	"stor"	;
	dc.l	"port"	;
	dc.l	"nlst"	;
	dc.l	"pwd"	;
	dc.l	"xpwd"	;For compatibility with 4.2BSD
	dc.l	"mkd "	;
	dc.l	"xmkd"	;For compatibility with 4.2BSD
	dc.l	"xrmd"	;For compatibility with 4.2BSD
	dc.l	"rmd "	;
	dc.l	"stru"	;
	dc.l	"mode"	;
	dc.l	"cdup"	;
	dc.l	0
;
;----------------------------------------------------------------------------
;	Response messages
;
banner:		dc.b	"220 %s FTP version %s ready at %s\n";
badcmd:		dc.b	"500 Unknown command\n";
binwarn:	dc.b	"100 Warning: type is ASCII and %s appears to be binary\n";
unsupp:		dc.b	"500 Unsupported command or option\n";
givepass:	dc.b	"331 Enter PASS command\n";
giveanonpass:	dc.b	"331 Enter Email address as password\n";
logged:		dc.b	"230 Logged in\n";
typeok:		dc.b	"200 Type %s OK\n";
only8:		dc.b	"501 Only logical bytesize 8 supported\n";
deleok:		dc.b	"250 File deleted\n";
mkdok:		dc.b	"200 MKD ok\n";
delefail:	dc.b	"550 Delete failed: %s\n";
pwdmsg:		dc.b	"257 \"%s\" is current directory\n";
badtype:	dc.b	"501 Unknown type \"%s\"\n";
badport:	dc.b	"501 Bad port syntax\n";
unimp:		dc.b	"502 Command not yet implemented\n";
bye:		dc.b	"221 Goodbye!\n";
nodir:		dc.b	"553 Can't read directory \"%s\": %s\n";
cantopen:	dc.b	"550 Can't read file \"%s\": %s\n";
sending:	dc.b	"150 Opening data connection for %s %s\n";
cantmake:	dc.b	"553 Can't create \"%s\": %s\n";
writerr:	dc.b	"552 Write error: %s\n";
portok:		dc.b	"200 Port command okay\n";
rxok:		dc.b	"226 File received OK\n";
txok:		dc.b	"226 File sent OK\n";
noperm:		dc.b	"550 Permission denied\n";
noconn:		dc.b	"425 Data connection reset\n";
lowmem:		dc.b	"421 System overloaded, try again later\n";
notlog:		dc.b	"530 Please log in with USER and PASS\n";
userfirst:	dc.b	"503 Login with USER first.\n";
okay:		dc.b	"200 Ok\n";
;
;----------------------------------------------------------------------------
;
data_limit:	.bss
;
;----------------------------------------------------------------------------
;
sting_drivers:	ds.l	1	;DRV_LIST	*sting_drivers;
tpl:		ds.l	1	;TPL		*tpl;
stx:		ds.l	1	;STX		*stx;
initerr_mess_p:	ds.l	1
fatal_f:	ds.w	1
VA_START_f:	ds.w	1
VA_START_cmd_p:	ds.l	1
VA_protostatus:	ds.l	1
VA_arg_p:	ds.l	1
VA_arg_s:	ds.b	MAX_VA_ARGLEN
		even
;
;----------------------------------------------------------------------------
;Add uninitialized variables here
;----------------------------------------------------------------------------
;
;
;----------------------------------------------------------------------------
bss_limit:	.end
;----------------------------------------------------------------------------
;End of file:	FTPD.S
;----------------------------------------------------------------------------
