        NAME    mssser
; File MSSSER.ASM
; Edit history:
; Last edit: 1 Jan 1988
; 1 Jan 1988 version 2.30
; 26 Dec 1987 Telescope Bye, Fin, Logout commands, clean up. [jrd]
; 12 Dec 1987 Add TEK denyflg to defeat automatic invokation of Tek graphics
;  by ESC Control-L when in Connect mode. [jrd]
; 24 Oct 1987 Ignore received Naks in Server idle loop
; 21 Sept 1987 Add error exit in GET for filenames too long for packet. [jrd]
; 26 Aug 1987 Add Remote Send to remote help response. [jrd]
; 19 Aug 1987 Correct local override name for files sent to/from server. [jrd]
; 31 July 1987 Redo Logo, Bye, Fin to not shift block check when repeating.
; 25 July 1987 Add global word denyflg to control server restrictions,
;  and revise server functions accordingly. [jrd]
; 6 July 1987 Correct Server mode ACKs to use 1 char checksum throughout.[jrd]
; 25 June 1987 Add exit to DOS for LOGO and exit to Kermit prompt for
;  FIN command reception, for use with CTTY operation of server. [jrd]
; 7 June 1987 Add DOS errlev of 4 when REMOTE commands to Server fail. [jrd]
; 16 April 1987 Clear local filename in Server idle loop, from Jack Bryans.
; 5 April 1987 Set flags.xflg around Generic I-pkt sends to suppress on-screen
;  display of retry counts. Put cursor on line 25 at end of Server cmds. [jrd]
; 25 March 1987 Add Disable/Enable Delete/Host commands. [jrd]
; 9 March 1987 Add calls to statistics functions. [jrd]
; 6 March 1987 Respond to FIN, LOGO, BYE while in server mode
;   with 1 byte checksums. Thanks to Jack Bryans.
;   Triple waiting time in Server idle loop (fewer NAKs/minute). [jrd]
; 22 Feb 1987 Add Server SEND and WHO commands (both directions). [jrd]
; 1 Oct 1986 Version 2.29a
; 10 Sept 1986 Add display of ACK pkt in LOGO and FIN. Remove forced output
;  to screen for REMOTE cmds; thanks to Bill Porteous.
;  Add ACK message for received Bye, Logo, Fin commands. [jrd]
; 6 Sept 1986 Output cr/lf for REMote commands before rcving 1st pkt. [jrd]
; 14 August 1986 Allow changing EOL chars. [jrd]
; Modify Srvsnd to handle Set Dest Screen properly. [jrd]
; Correct negotiation on 8 bit quoting, fix checksum resetting. [jrd]
; 26 July 1986 Change ref to ext symbol rpack5 to be rpack. [jrd]
; 16 June 1986 Clear packet counters at start of Get command.
;  Also correct use of wrong block check type in Server mode. [jrd]
; 26 May 1986 Revise code to permit serial display. [jrd]
; 14 May 86 - Modify action routine for reception of 'E' packets in server
;  mode to be rskp rather than serv1. From Tad Marshall.
; Insert tests for Remote Kermit commands in proc genric to avoid counted
;  strings. [jrd]
; [2.29] code frozen on 6 May 1986 [jrd]

        public  logout, bye, finish, remote, get, server, srvdsa, srvena
        public  denyflg
        include mssdef.h

datas   segment public 'datas'
        extrn   data:byte, flags:byte, trans:byte, pack:byte, curchk:byte
        extrn   curdsk:byte, diskio:byte, locfil:byte, comand:byte, rptq:byte
        extrn   filtst:byte, maxtry:byte, imxtry:byte, dtrans:byte,fmtdsp:byte
        extrn   inichk:byte, errlev:byte, portval:word

scrser  equ     0023H           ; place for server state display line
scrsrm  equ     1000H           ; place for messages and dos echoes
cwdflg  equ     1               ; deny remote cwd
delflg  equ     2               ; deny remote del
dirflg  equ     4               ; deny remote dir
hostflg equ     8               ; deny remote host
spcflg  equ     10H             ; deny remote space
finflg  equ     20H             ; deny fin, bye, logo to server
getsflg equ     40H             ; deny paths in get cmds to server
sndflg  equ     80H             ; deny paths in send cmds to server
typflg  equ     100H            ; deny paths in type
tekxflg equ     200h            ; deny automatic Tektronix invokation

remcmd  db      0               ; Remote command to be executed.
rempac  db      0               ; Packet type: C (host) or G (generic).

cmer05  db      cr,lf,'?Filename must be specified$'
ermes6  db      '?Filename too long for packet$'
erms18  db      cr,lf,'?Unable to get response from host$'
erms21  db      cr,lf,'?Unable to tell host to execute command$'
infms1  db      'Server mode',cr,lf,'$'
remms1  db      'Kermit-MS Server: Unknown server command$'
remms2  db      'Kermit-MS Server: Illegal file name$'
remms3  db      'Kermit-MS Server: Could not create help file$'
remms4  db      'Kermit-MS Server: Unable to change directories$'
remms5  db      'Kermit-MS Server: No such file(s)$'
remms6  db      'Kermit-MS Server: Could not create directory listing$'
remms7  db      'Kermit-MS Server: Could not create space listing$'
remms8  db      'Kermit-MS Server: Protected or no such file(s)$'
remms9  db      'Kermit-MS Server: Command is Disabled$'
remms10 db      'Kermit-MS Server: Could not create work file$'
byemsg  db      'Kermit-MS Server:  Goodbye!',0
whomsg  db      'Kermit-MS Server: Just this Server',0
sdshlp  db      cr,lf,'Server restricts access of selected commands:',cr,lf
        db      '  CWD, DEL, DIR, FIN (incl BYE & LOGO), GET, SEND, SPACE,'
        db      ' TYPE, and ALL.'
        db      cr,lf,'Also TEK (automatic invokation of Tek4010 graphics);'
        db      ' not a member of ALL.$'
senhlp  db      cr,lf,'Server permits full access of selected commands:',cr,lf
        db      '  CWD, DEL, DIR, FIN (incl BYE & LOGO), GET, SEND, SPACE,'
        db      ' TYPE, and ALL.'
        db      cr,lf,'Also TEK (automatic invokation of Tek4010 graphics);'
        db      ' not a member of ALL.$'
pass    db      lf,cr,' Password: $'    ; When change directory
srvtmp  db      ' >$kermit$.tmp ',0     ; asciiz, kermit's temp output file
delstr  db      'del ',0
dirstr  db      'dir ',0
spcstr  db      'chkdsk.com ',0
crlf    db      cr,lf,'$'
curstim db      ?                       ; normal waiting time for packets
denyflg dw      0                       ; bit field of denied commands
temp    dw      0
inpbuf  dw      0                       ; Pointer to input buffer.
cnt     dw      0

srvchr  db      'SRGIEC'                ; server cmd characters
srvfln  equ     $-srvchr                ; length of tbl
srvfun  dw      srvsnd,srvrcv,srvgen,srvini,rskp,srvhos ; order as in srvchr

remhlp  db      cr,lf,'CWD        change working directory'     ; Answer to
        db      cr,lf,'Delete     a file'                       ; local
        db      cr,lf,'Directory  filespec'                     ; REM HELP
        db      cr,lf,'Help'
        db      cr,lf,'Host       command'
        db      cr,lf,'Kermit     command'
        db      cr,lf,'Send       short one line message'
        db      cr,lf,'Space      in a directory'
        db      cr,lf,'Type       a file'
        db      cr,lf,'Who        user spec$'

                                        ; Answer from Server to REMOTE HELP
hlprem  db      cr,lf,'Kermit-MS Server commands:',lf
        db      cr,lf,'GET filespec          REMOTE DELETE filespec     '
        db      'REMOTE SEND message'
        db      cr,lf,'SEND filespec         REMOTE DIRECTORY filespec  '
        db      'REMOTE SPACE'
        db      cr,lf,'FIN, LOGO, and BYE    REMOTE HELP                '
        db      'REMOTE TYPE filespec'
        db      cr,lf,'REMOTE CWD directory  REMOTE HOST command        '
        db      'REMOTE WHO',0                          ; null terminated

remtab  db      10              ; 10 entries
        mkeyw   'CWD',remcwd
        mkeyw   'Delete',remdel
        mkeyw   'Directory',remdir
        mkeyw   'Help',remhel
        mkeyw   'Host',remhos
        mkeyw   'Kermit',remker
        mkeyw   'Send',remsen
        mkeyw   'Space',remdis
        mkeyw   'Type',remtyp
        mkeyw   'Who',remwho

srvtab  db      11                      ; Server Enable/Disable list
        mkeyw   'All',01ffh
        mkeyw   'Cwd',cwdflg
        mkeyw   'Delete',delflg
        mkeyw   'Dir',dirflg
        mkeyw   'Fin',finflg
        mkeyw   'Get',getsflg
        mkeyw   'Host',hostflg
        mkeyw   'Send',sndflg
        mkeyw   'Space',spcflg
        mkeyw   'Tek4010',tekxflg       ; for automatic Tektronix invokation
        mkeyw   'Type',typflg

remfnm  db      ' Remote Source File: $'
lclfnm  db      ' Local Destination File: $'
filhlp  db      ' File name to use locally$'
filmsg  db      ' Remote filename or confirm with carriage return $'
frem    db      ' Name of file on remote system $'
genmsg  db      ' Enter text to be sent to remote server $'
srvbuf  db      80H dup (0)
rdbuf   db      20 dup (0)
datas   ends

code    segment public 'code'
        extrn comnd:near, serrst:near, spack:near, rpack:near, init:near
        extrn read12:near, serini:near, read2:near, rpar:near, spar:near
        extrn rin21:near, rfile3:near, error1:near, clrfln:near
        extrn dodel:near, clearl:near, dodec: near, doenc:near
        extrn packlen:near, send11:near, errpack:near, pktsize:near
        extrn nak:near, rrinit:near, cmblnk:near, poscur:near
        extrn erpos:near, rprpos:near, clrmod:near, crun:near
        extrn prompt:near, updrtr:near, cmgetc:near, prtfn:near, prtscr:near
        extrn strcat:near, strlen:near, strcpy:near, fparse:near, isfile:near
        extrn prtasz:near, ihosts:near, begtim:near, endtim:near
        assume  cs:code, ds:datas, es:nothing


; BYE command - tell remote KERSRV to logout & exits to DOS.

BYE     PROC    NEAR
        mov     ah,cmcfm                ; Parse a confirm.
        call    comnd
         jmp    r
        mov     remcmd,'L'              ; Logout command letter
        call    logo                    ; Tell the mainframe to logout.
         jmp    rskp                    ; Failed - don't exit.
        mov     flags.extflg,1          ; Set exit flag.
        jmp     rskp
BYE     ENDP

; FINISH - tell remote KERSRV to exit.

FINISH  PROC    NEAR
        mov     ah,cmcfm                ; Parse a confirm.
        call    comnd
         jmp    r
        mov     remcmd,'F'              ; Finish command letter
        call    logo
         jmp    rskp
        jmp     rskp
FINISH  ENDP

; LOGOUT - tell remote KERSRV to logout.

LOGOUT  PROC    NEAR
        mov     ah,cmcfm
        call    comnd                   ; Get a confirm.
         jmp    r
        mov     remcmd,'L'              ; Logout command letter
        call    logo
         jmp    rskp                    ; Go get another command whether we
        jmp     rskp                    ;  succeed or fail.
LOGOUT  ENDP

; Common routine for FIN, LOGOUT, BYE
LOGO    PROC    NEAR
        mov     pack.numtry,0           ; Initialize count.
        mov     pack.numrtr,0           ; No retries yet.
        call    serini                  ; Initialize port.
        call    ihosts                  ; initialize the host
        mov     ah,trans.chklen         ; Don't forget the checksum length.
        mov     curchk,ah
        mov     trans.chklen,1          ; Use one char for server functions.
        call    begtim                  ; start statistics
logo1:  cmp     pack.state,'A'          ; Did user type a ^C?
        je      log2x               ; e = yes, leave in failure state for Bye.
        mov     ah,pack.numtry
        cmp     ah,maxtry               ; Too many times?
        jl      logo3                   ; No, try it.
logo2:  mov     ah,prstr
        mov     dx,offset erms18
        int     dos
log2x:  call    serrst                  ; Reset port
        mov     ax,1            ; tell statistics this was a send operation
        call    endtim                  ; finish statistics
        mov     ah,curchk
        mov     trans.chklen,ah         ; Restore value.
        ret                             ; and exit in failure state for Bye
logo3:  inc     pack.numtry             ; Increment number of tries.
        mov     pack.argblk,0           ; Packet number zero.
        mov     pack.argbk1,1           ; One piece of data.
        mov     ah,remcmd               ; get command letter ('L' or 'F')
        mov     data,ah                 ; Logout the remote host.
        mov     cx,1                    ; One piece of data.
        call    doenc                   ; Do encoding.
        mov     ah,'G'                  ; Generic command packet.
        call    spack
         jmp    logo2                   ; Tell user and die.
         nop
        call    rpack                   ; Get ACK (w/o screen msgs.)
         jmp    logo1                   ; Go try again.
         nop
        push    ax
        call    dodec                   ; Decode packet.
        pop     ax
        cmp     ah,'Y'                  ; ACK?
        jne     logo4
        cmp     pack.argbk1,0           ; Any data in the ACK?
        je      logo6                   ; Nope - just return.
        mov     ah,prstr                ; output a cr/lf
        mov     dx,offset crlf
        int     dos
        mov     di,offset data          ; Where the reply is.
        mov     cx,pack.argbk1          ; How much data we have.
        call    prtscr                  ; Print it on the screen.
        jmp     logo6                   ; and exit
logo4:  cmp     ah,'E'                  ; Error packet?
        je      logo5                   ; e = yes
        jmp     logo1                   ; try sending again
logo5:  call    error1
logo6:  call    serrst                  ; Reset port
        mov     ax,1            ; tell statistics this was a send operation
        call    endtim                  ; finish statistics
        mov     ah,curchk
        mov     trans.chklen,ah         ; Restore value.
        jmp     rskp                    ; use rskp so Bye succeeds
LOGO    ENDP

; GET command. Ask remote server to send the specified file(s).
; Queries for remote filename and optional local override path/filename.
GET     PROC    NEAR
        mov     flags.nmoflg,0          ; Reset flags from fn parsing.
        mov     byte ptr locfil,0       ; clear, for safety
        mov     byte ptr srvbuf,0       ; ditto
        mov     flags.cxzflg,0          ; no Control-C typed yet
        mov     cnt,0                   ; count of filename chars
        mov     bx,offset srvbuf        ; Where to put text.
        mov     byte ptr [bx],0         ; clear for safety
        mov     dx,offset filmsg        ; In case user needs help.
        mov     ah,cmtxt                ; filenames with embedded whitespace
        call    comnd                   ; Get text or confirm.
         jmp    r                       ; Fail.
        mov     al,ah
        mov     ah,0
        mov     cnt,ax                  ; Remember number of chars we read.
        cmp     al,0                    ; Read in any chars?
        je      get1                    ; e = no.
        jmp     get3                    ; yes, now check for override name.
                                        ; if empty line, ask for file names
get1:   mov     dx,offset remfnm        ; ask for remote name first
        call    prompt
        mov     bx,offset srvbuf        ; place for remote filename
        mov     dx,offset frem          ; the help message
        mov     ah,cmtxt                ; use this for embedded spaces
        call    comnd                   ; get a filename
         jmp    r
        cmp     flags.cxzflg,0          ; ^X, ^Z, or ^C typed?
        je      get2                    ; e = no, continue
        jmp     rskp                    ; yes, quit
get2:   mov     al,ah
        mov     ah,0
        mov     cnt,ax                  ; remember number of chars read.
        mov     bx,offset srvbuf        ; look at string again.
        push    es
        push    di
        push    si
        mov     ax,ds                   ; use segment 'datas' for es:
        mov     es,ax
        mov     si,bx           ; look at start of string, remove whitespace
get2c:  cmp     byte ptr [si],0         ; at terminator?
        je      get2d                   ; e = yes
        cmp     byte ptr [si],' '       ; text (greater than space)?
        ja      get2d                   ; a = yes.
        inc     si                      ; look at next char
        jmp     get2c                   ; look some more
get2d:  cmp     bx,si                   ; did we find leading whitespace?
        je      get2e                   ; e = no
        mov     di,bx                   ; place to copy chars
        call    strcpy                  ;   from ds:si to ds:di
get2e:  mov     dx,bx                   ; address of string
        call    strlen                  ; get its new length (returned in cx)
        mov     cnt,cx                  ; store it
        pop     si
        pop     di
        pop     es
        cmp     cnt,0                   ; count of entered chars
        jne     get2f                   ; ne = got some
        jmp     rskp                    ; empty so abort this command
get2f:  mov     dx,offset lclfnm        ; prompt for local filename
        call    prompt
get3:   mov     flags.nmoflg,0          ; assume no local override name
        mov     bx,offset filhlp
        mov     dx,offset locfil        ; complete local filename
        mov     byte ptr locfil,0       ; clear, for safety
        mov     ah,cmfile               ; allow paths
        call    comnd
         jmp    r
        mov     bx,offset locfil
        cmp     byte ptr [bx],'#'       ; Is first char a replacement for '?'
        jne     get3a                   ; ne = no
        mov     byte ptr [bx],'?'       ; yes. Replace '#' by '?'
get3a:  mov     al,ah           ; number of chars in locfil according to cmd
        mov     flags.nmoflg,al         ; 0 = no override
        mov     ah,0
        add     bx,ax
        mov     byte ptr [bx],0         ; force a termination null

        mov     bx,offset srvbuf        ; get remote filename address again
        cmp     byte ptr [bx],'#'       ; Is first char a replacement for '?' ?
        jne     get4                    ; ne = no.
        mov     byte ptr [bx],'?'       ; yes. Replace '#' by '?'

get4:   cmp     flags.cxzflg,0          ; ^X, ^Z, or ^C typed?
        je      get5                    ; e = no, keep going
        mov     flags.cxzflg,0          ; clear the interrupt flag
        or      errlev,2                ; say cannot receive
        jmp     rskp
get5:   call    begtim                  ; start statistics
        cmp     flags.destflg,2         ; receiving to screen?
        je      get5a                   ; e = yes, skip screen stuff
        mov     flags.xflg,0            ; no, reset x flag
        call    init                    ; init screen
get5a:  call    ipack                   ; Send Initialize, 'I', packet.
         jmp    get8                    ; Sorry can't do it.
         nop
        mov     cx,cnt                  ; Get back remote filename size.
        mov     pack.argbk1,cx          ; Need it here to send packet.
        mov     si,offset srvbuf        ; Move from here
        mov     di,offset data          ; to here.
        call    strcpy                  ; copy from srvbuf to data
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     get6                    ; nz = yes, don't print anything
        cmp     flags.remflg,dserial    ; serial mode display?
        je      get6                    ; e = yes, skip extra display item.
        cmp     flags.destflg,2         ; Receiving to screen?
        je      get6                    ; Yes skip screen stuff.
        call    prtfn                   ; print filename in data
get6:
        call    rrinit                  ; clear pack.xxx counters
        mov     pack.numrtr,-1  ; No retries yet (gets incremented below).
        mov     pack.state,'R'          ; this is what state will be soon
        mov     cx,pack.argbk1          ; Data size.
        call    doenc                   ; Encode data.
        jnc     get6a                   ; nc = success
        jmp     get12           ; c = data could not all fit into packet
get6a:  mov     ah,trans.chklen         ; Don't forget the checksum length.
        mov     curchk,ah
        mov     trans.chklen,1          ; Use one char for server functions.
get7:   call    updrtr
        cmp     pack.state,'A'          ; Did user type a ^C?
        je      get9                    ; Yes - just return to main loop.
        mov     ah,pack.numtry
        cmp     ah,maxtry               ; Too many times?
        jbe     get10                   ; Nope, try it.
get8:   test    flags.remflg,dquiet     ; quiet display mode?
        jnz     get9                    ; nz = yes, no printing
        call    erpos
        mov     ah,prstr
        mov     dx,offset erms18        ; Can't get init packet.
        int     dos
        or      errlev,2                ; set DOS error level to cannot rcv.
get9:   test    flags.remflg,dquiet+dserial ; quiet or serial display?
        jnz     get9a                   ; nz = yes
        call    clrmod
        call    rprpos
get9a:  call    serrst                  ; Reset port.
        mov     ah,curchk
        mov     trans.chklen,ah         ; Restore value.
        xor     ax,ax                   ; say this was a receive operation
        call    endtim                  ; do statistics
        jmp     rskp
get10:  inc     pack.numtry             ; Increment number of tries.
        mov     pack.argblk,0           ; Start at packet zero.
        call    pktsize                 ; report packet size
        mov     ah,'R'                  ; Receive init packet.
        call    spack                   ; Send the packet.
         jmp    get8                    ; Tell user we can't do it.
         nop
        call    rpack                   ; Get ACK
         jmp    get7                    ; Got a NAK - try again.
         nop
        push    ax
        mov     ah,curchk
        mov     trans.chklen,ah         ; Restore value.
        pop     ax
        mov     pack.argbk2,ax          ; where rinit wants pkt type if GET
        mov     flags.getflg,1          ; note this is a GET
        mov     pack.state,'R'          ; Set the state to receive initiate
        jmp     read12                  ; go join read code
get11:  mov     ah,prstr                ; Complain if no filename.
        mov     dx,offset cmer05
        int     dos
        jmp     rskp
get12:  mov     dx,offset ermes6    ; Complain if filename is too long for pkt
        test    flags.remflg,dquiet     ; quiet display mode?
        jnz     get13                   ; nz = yes, no printing
        call    erpos                   ; position cursor on formatted screen
        mov     ah,prstr
        int     dos
get13:  mov     bx,dx                   ; point to message, for errpack
        call    errpack                 ; tell the host we are quiting
        test    flags.remflg,dserial    ; serial display mode?
        jnz     get14                   ; nz = yes
        call    clrmod                  ; clear mode line
        call    rprpos                  ; Put prompt here.
get14:  or      errlev,2                ; set DOS error level to cannot rcv
        jmp     rskp
GET     ENDP

; server command

SERVER  PROC    NEAR
        mov     ah,cmcfm
        call    comnd
         jmp    r
        push    es
        mov     ax,ds
        mov     es,ax                   ; address data segment
        mov     al,flags.remflg         ; get display mode flag
        push    ax                      ; preserve for later
; Enable the line below if the server screen is to be quiet (clear),
; or make the line a comment if the server screen is to show file transfers.
;===>   mov     flags.remflg,dquiet     ; set quiet display flag if server
                                        ;
        or      flags.remflg,dserver    ; signify we are a server now
        mov     ax,0                    ; simulate empty parameter packet
        call    spar                    ; and thus set our params to defaults
        mov     ah,drpt                 ; force default repeat prefix char
        mov     rptq,ah                 ;  char be our active one
        test    flags.remflg,dquiet     ; quiet display?
        jnz     serv1c                  ; nz = yes
        mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        test    flags.remflg,dserial    ; serial display?
        jnz     serv1a                  ; nz = yes
        mov     fmtdsp,1                ; say using formatted display
        call    cmblnk                  ; clear screen
        mov     dx,scrser               ; move cursor to top of screen
        call    poscur
serv1a: mov     ah,prstr
        mov     dx,offset infms1        ; say now in server mode
        int     dos
serv1c: mov     ah,inichk               ; set default checksum length
        mov     curchk,ah               ; save it here

serv1:  test    flags.remflg,dquiet+dserial ; quiet or serial display?
        jnz     serv1b                  ; nz = yes
        mov     fmtdsp,1                ; say using formatted display
        mov     dx,scrsrm               ; move cursor to server message area
        add     dx,0100H        ; look at line below (DOS does CR/LF first)
        call    poscur
        call    clearl                  ; and clear the line
        mov     dx,scrsrm               ; back to message line
        call    poscur

serv1b: mov     flags.nmoflg,0  ; clear, say no local override filenames
        mov     flags.cxzflg,0          ; clear ^X, ^Z, ^C seen flag
        mov     flags.xflg,0            ; reset X packet flag
        mov     locfil,0                ; say no local filename [JB]
        mov     ah,dtrans.seol          ; restore default end-of-line char
        mov     trans.seol,ah
        mov     byte ptr srvbuf,0       ; plant terminator to clear
        call    serini          ; init serial line (send & receive reset it)
        mov     ax,1                    ; assume previous operation was a send
        call    endtim                  ; do end of statistics, for loop back
        mov     trans.chklen,1          ; checksum len = 1
        mov     pack.pktnum,0           ; pack number resets to 0
        mov     pack.numtry,0           ; no retries yet.
        mov     al,trans.stime          ; get current timeout interval
        mov     curstim,al              ; save current timeout interval
        add     al,al                   ; triple it for server idle loop
        add     al,curstim              ; times three
        mov     trans.stime,al  ;  use this longer interval in the idle loop
        call    rpack                   ; get a packet
         jmp    short serv2             ; no good, nak and continue
         nop
        call    begtim                  ; start statistics
        push    ax
        mov     al,curstim              ; get original timeout interval
        mov     trans.stime,al          ; restore timeout interval
        pop     ax
        cmp     ah,'I'                  ; never "decode" S, I, and A packets
        je      serv3                   ; its an I packet
        cmp     ah,'S'
        je      serv3
        cmp     ah,'A'
        je      serv3
        call    dodec           ;decode packet; protocol error if omitted
        jmp     short serv3             ; try to figure this out

serv2:  push    ax
        mov     al,curstim              ; get original timeout interval
        mov     trans.stime,al          ; restore timeout interval
        pop     ax
        cmp     flags.cxzflg,'C'        ; Control-C?
        je      serv5                   ; yes, stop this.
        call    nak                     ; nak the packet
        mov     al,curchk               ; restore checksum length
        mov     trans.chklen,al
        jmp     serv1                   ; and keep readiserv2 packets

serv3:  mov     al,curchk               ; restore checksum length
        mov     trans.chklen,al
        push    ds
        pop     es                      ; set es to datas segment
        mov     di,offset srvchr        ; server characters
        mov     cx,srvfln               ; length of striserv2
        mov     al,ah                   ; packet type
        cld
        repne   scasb                   ; hunt for it
        je      serv4                   ; we know this one, go handle it
        cmp     al,'N'                  ; received a Nak?
        je      serv3a                  ; e = yes, ignore it
        mov     bx,offset remms1        ; else give a message
        call    errpack                 ; back to local kermit
serv3a: jmp     serv1                   ; and keep lookiserv2 for a cmd
serv4:  sub     di,offset srvchr+1      ; find offset, +1 for pre-increment
        shl     di,1                    ; convert to word index.
        call    srvfun[di]              ; call the appropriate handler
         jmp    serv5                   ; someone wanted to exit...
        jmp     serv1                   ; else keep goiserv2 for more cmds.

serv5:  mov     al,curchk               ; restore checksum length
        mov     trans.chklen,al
        pop     ax                      ; get this off stack
        test    flags.remflg,dserial+dquiet ; serial or quiet display?
        jnz     serv5a                  ; nz = yes
        call    rprpos                  ; Put prompt here.
serv5a: mov     flags.remflg,al         ; restore old flag
        call    serrst                  ; reset serial handler
        mov     ax,1            ; tell statistics this was a send operation
        call    endtim                  ; stop statistics
        mov     fmtdsp,0                ; end of formatted display
        pop     es                      ; restore register
        jmp     rskp                    ; and return
SERVER  ENDP

; server commands.

; srvsnd - receives a file that a remote kermit is sending.
srvsnd  proc    near
        mov     bx,offset data
        mov     ax,pack.argbk1          ; get number of data bytes
        call    spar                    ; parse the send-init packet
        mov     al,trans.chklen         ; get negotiated checksum length
        mov     curchk,al               ;  and remember it here
        call    packlen                 ; figure max packet
        mov     bx,offset data
        call    rpar                    ; make answer for them
        mov     al,ah                   ; length of packet
        mov     ah,0
        mov     pack.argbk1,ax          ; store length for spack
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    pktsize                 ; report packet size
        mov     ah,'Y'                  ; ack
        call    spack                   ; answer them
         jmp    rskp                    ; can't answer, forget this
        mov     al,curchk               ; restore checksum length
        mov     trans.chklen,al
        call    rrinit                  ; init variables for init
        cmp     flags.destflg,2         ; file destination = screen?
        jne     srvsnd0                 ; ne = no
        mov     flags.xflg,1            ; say receiving to screen
        jmp     srvsnd1
srvsnd0:call    init                    ; setup display form
srvsnd1:test    denyflg,sndflg          ; is command enabled?
        jz      srvsnd2                 ; z = yes
        mov     si,offset srvbuf        ; work buffer
        mov     byte ptr[si],5ch        ; backslash
        inc     si
        mov     ah,gcd                  ; get current directory (path really)
        xor     dl,dl                   ; use current drive
        int     dos             ; returns ds:si with asciiz path (no drive)
        mov     si,offset srvbuf
        mov     di,offset locfil        ; destination is local override name
        call    strcpy                  ; copy the path to local filename
        mov     dx,di
        call    strlen                  ; get length of string into cx
        mov     di,cx                   ; length of local path
        mov     locfil[di],5ch          ; add backslash
        mov     locfil[di+1],0          ; null terminator
        mov     flags.nmoflg,1  ; say have override name (zaps external path)
srvsnd2:inc     pack.pktnum             ; count the send-init packet.
        mov     pack.state,'F'          ; expecting file name about now
        call    read12                  ; join read code. changed from read2
         nop
         nop
         nop                            ; ignore errors
        mov     flags.xflg,0
        jmp     rskp                    ; and return for more
srvsnd  endp

; srvrcv - send a file to a distant kermit

srvrcv  proc    near
        mov     si,offset data          ; received filename, asciiz from rpack
        test    denyflg,getsflg         ; command enabled?
        jz      srrcv2                  ; z = yes
        mov     di,offset srvbuf        ; local path
        mov     si,offset rdbuf         ; local filename
        mov     dx,offset data          ; local string
        call    fparse                  ; split string
        mov     si,offset rdbuf         ; copy local filename to
srrcv2: mov     di,offset diskio.string ; destination
        call    strcpy                  ; copy data to diskio.string
        mov     pack.state,'R'          ; remember state.
        call    send11                  ; this should send it
         jmp    rskp
        jmp     rskp                    ; return in any case
srvrcv  endp

; srvgen - G generic server command dispatcher.
;
srvgen  proc    near
        mov     al,data                 ; get 1st packet char
srvge2: cmp     al,'T'                  ; Type a file?
        jne     srvge3                  ; ne = no
        call    srvtyp                  ; do the typing
        jmp     rskp
srvge3: cmp     al,'D'                  ; do a directory?
        jne     srvge4
        call    srvdir                  ; do the directory command
        jmp     rskp
srvge4: cmp     al,'E'                  ; do a file erase (delete)?
        jne     srvge5
        call    srvdel                  ; do the delete command
        jmp     rskp
srvge5: cmp     al,'C'                  ; change working dir?
        jne     srvge6                  ; ne = no
        call    srvcwd                  ; do it
        jmp     rskp
srvge6: cmp     al,'U'                  ; do a space command?
        jne     srvge7
        call    srvspc                  ; do the space command
        jmp     rskp
srvge7: cmp     al,'F'                  ; FIN?
        jne     srvge8                  ; ne = no
        jmp     srvfin
srvge8: cmp     al,'L'                  ; LOGO or BYE?
        jne     srvge9                  ; ne = no
        call    srvfin
         jmp    short srvge8a           ; permitted to exit Kermit
         nop
        jmp     rskp                    ; stay active (command denied)
srvge8a:mov     flags.extflg,1          ; set exit flag.
        ret                             ; leave server mode and Kermit.
srvge9: cmp     al,'M'                  ; one line Message?
        jne     srvge10                 ; ne = no
        call    srvsen
        jmp     rskp
srvge10:cmp     al,'W'                  ; WHO?
        jne     srvge11                 ; ne = no
        call    srvwho
        jmp     rskp
srvge11:cmp     al,'H'                  ; Help?
        jne     srvgex                  ; ne = no
        jmp     srvhlp
srvgex: mov     bx,offset remms1        ; reply Unknown server command
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack
        jmp     rskp
srvgen  endp

; srvfin - respond to remote host's Fin command. [jrd]
srvfin  proc    near
        test    denyflg,finflg          ; command enabled?
        jz      srfin1                  ; z = yes
        mov     bx,offset remms9        ; else give a message
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack                 ; back to local kermit
        jmp     rskp                    ; stay in server mode
srfin1: mov     si,offset byemsg        ; add brief msg of goodbye
        mov     di,offset data          ; packet's data field
        call    strcpy                  ; copy msg to pkt
        mov     dx,si                   ; strlen works on dx
        call    strlen
        mov     ah,'Y'                  ; reply with an ack
        mov     pack.argbk1,cx          ; length
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    pktsize                 ; report packet size
        call    spack                   ; send it, expect no response
         nop                            ; ignore errors
         nop
         nop
        ret                             ; ret exits server mode
srvfin  endp

; srvcwd - handle other side's Remote CWD dirspec [jrd]
srvcwd  proc    near
        test    denyflg,cwdflg          ; is command enabled?
        jz      srcwd4                  ; z = yes
        mov     bx,offset remms9        ; else give a message
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack                 ; back to local kermit
        ret
srcwd4: cmp     pack.argbk1,1           ; any data?
        je      srcwd3                  ; e = no
        mov     cl,data+1               ; get the filename byte count
        sub     cl,' '                  ; ascii to numeric
        mov     ch,0                    ; set up counter
        cmp     cl,0                    ; anything there?
        jle     srcwd3                  ; le = no, an error
        mov     si,offset data+2        ; received dir spec, from rpack
        mov     di,offset srvbuf        ; destination
        push    es                      ; save es
        push    ds
        pop     es                      ; make es:di point to datas segment
        cld
        rep movsb                       ; copy data to srvbuf, cx chars worth
        pop     es
        mov     byte ptr [di],0         ; plant terminator
        mov     dx,offset srvbuf        ; for DOS
        mov     ax,dx                   ; dir spec pointer for isfile
        cmp     byte ptr [di-1],':'     ; did user just type A: or similar?
        je      srcwd1                  ; e = yes, so skip directory part
        mov     ah,chdir                ; want to do change dir
        int     dos
        jnc     srcwd1                  ; nc = ok
srcwd3: mov     bx,offset remms4        ; an error.
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack                 ; send the bad news
        ret
srcwd1: mov     dl,data+3               ; see if drive given (look for :)
        cmp     dl,':'
        jne     srcwd2                  ; ne = no drive
        mov     dl,data+2
        and     dl,5fH                  ; convert to upper case
        sub     dl,'A'                  ; count A = 0 for seldsk call
        mov     ah,seldsk
        int     dos                     ; change disks
        jc      srcwd3                  ; c = an error
        inc     dl                      ; now make A = 1 etc internally
        mov     curdsk,dl               ;and update internal current disk code
srcwd2: mov     ah,'Y'                  ; return an ack
        mov     pack.argbk1,0           ; no data
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    pktsize                 ; report packet size
        call    spack
         nop
         nop
         nop
        ret
srvcwd  endp

; srvtyp - handle other side's Remote Type filename request [jrd]
; expects "data" to hold  Tcfilename   where c = # bytes in filename.
srvtyp  proc    near
        cmp     pack.argbk1,1           ; any data in packet
        je      srtyp2                  ; e = no
        mov     cl,data+1               ; get the filename byte count
        sub     cl,' '                  ; ascii to numeric
        mov     ch,0                    ; set up counter
        mov     si,offset data+2        ; received filename, asciiz from rpack
        mov     di,si
        add     di,cx
        mov     byte ptr [di],0         ; make string asciiz
        test    denyflg,typflg          ; paths permitted?
        jz      srtyp1                  ; z = yes, else use just filename part
        mov     di,offset srvbuf        ; local path
        mov     si,offset rdbuf         ; local filename
        mov     dx,offset data+2        ; local string
        call    fparse                  ; split string
        mov     si,offset rdbuf         ; copy local filename to
srtyp1: mov     di,offset diskio.string ; destination
        call    strcpy                  ; do the copy
        mov     ax,offset diskio.string ; pointer to filename, for isfile
        call    isfile                  ; does it exist?
        jnc     srtyp3                  ; nc = yes
srtyp2: mov     bx,offset remms5        ; "No such file(s)"
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack                 ; send error message
        ret                             ; and exit
srtyp3: mov     flags.xflg,1            ; say use X packet rather than F pkt
        mov     pack.state,'R'          ; remember state.
        call    send11                  ; this should send it
         nop
         nop
         nop
        mov     flags.xflg,0            ; clear flag
        ret                             ; return in any case
srvtyp  endp

; serdir - handle other side's Remote Dir filespec(optional) request [jrd]
srvdir  proc    near
        mov     cx,0                    ; assume no data in packet
        cmp     pack.argbk1,1           ; any data in the packet?
        je      srdir4                  ; e = no
        mov     cl,data+1               ; get the filename byte count
        sub     cl,' '                  ; ascii to numeric
        mov     ch,0                    ; set up counter
srdir4: mov     di,offset data+2        ; received filespec, asciiz from rpack
        add     di,cx
        mov     byte ptr [di],0         ; make string asciiz
        test    denyflg,dirflg          ; paths permitted?
        jz      srdir1                  ; z = yes, else use just filename part
        mov     di,offset srvbuf        ; local path
        mov     si,offset rdbuf         ; local filename
        mov     dx,offset data+2        ; local string
        call    fparse                  ; split string
        mov     si,offset rdbuf         ; copy local filename to
        mov     di,offset data+2        ; final filename
        call    strcpy                  ; do the copy
        mov     ax,di
        call    isfile                  ; is/are there any such file?
        jc      srdir1                  ; c = there is none
        test    byte ptr filtst.dta+21,1EH ; attr bits: is file protected?
        jz      srdir1                  ; z = not protected.
        mov     bx,offset remms8        ; "Protected or no such file(s)"
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack                 ; send error message
        ret                             ; and exit

srdir1: mov     di,offset srvbuf        ; work area
        mov     si,offset dirstr        ; prepend "dir "
        call    strcpy
        mov     si,offset data+2        ; directory spec, asciiz
        mov     di,offset srvbuf
        call    strcat
        mov     si,offset srvtmp    ; add redirection tag of " >$kermit$.tmp"
        mov     di,offset srvbuf
        call    strcat
        mov     si,offset srvbuf        ; command pointer for crun
        call    crun
         nop
         nop
         nop
        mov     si,offset srvtmp+2      ; get name of temp file
        mov     di,offset diskio.string ; destination
        call    strcpy                  ; copy it there
        mov     ax,di                   ; filename pointer for isfile
        call    isfile                  ; did we make the temp file?
        jnc     srdir3                  ; nc = yes
        mov     bx,offset remms6        ; "Could not create directory listing"
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack                 ; send the error message
        ret                             ; and exit
srdir3: mov     flags.xflg,1            ; say use X rather than F packet
        mov     pack.state,'R'          ; remember state.
        call    send11                  ; this should send it
         nop
         nop
         nop
        mov     flags.xflg,0            ; clear flag
        mov     dx,offset diskio.string
        mov     ah,del2                 ; delete the file
        int     dos
        ret                             ; return in any case
srvdir  endp

; serdel - handle other side's request of Remote Del filespec [jrd]
srvdel  proc    near
        test    denyflg,delflg          ; command enabled?
        jz      srvdel4                 ; z = yes
        mov     bx,offset remms9        ; else give a message
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack                 ; back to local kermit
        ret

srvdel4:cmp     pack.argbk1,1           ; any data?
        je      srdel1                  ; e = no
        mov     di,offset srvbuf        ; work area
        mov     si,offset delstr        ; prepend "del "
        call    strcpy
        mov     dx,offset srvbuf
        call    strlen
        add     di,cx                   ; di points at terminator
        mov     ax,di                   ; save pointer to incoming filespec
        mov     cl,data+1               ; get the filename byte count
        sub     cl,' '                  ; ascii to numeric
        mov     ch,0                    ; set up counter
        cmp     cl,0                    ; anything there?
        jle     srdel3                  ; le = no
        mov     si,offset data+2        ; received filespec, asciiz from rpack
        push    es                      ; save es
        push    ds
        pop     es                      ; set es to datas segment
        cld
        rep     movsb                   ; append data to srvbuf
        pop     es                      ; restore es
        mov     byte ptr [di],0         ; plant terminator
        call    isfile                  ; is/are there any to delete?
        jc      srdel1                  ; c = there is none
        test    byte ptr filtst.dta+21,1EH ; attr bits: is file protected?
        jz      srdel2                  ; z = not protected.
srdel1: mov     bx,offset remms8        ; "Protected or no such file(s)"
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack                 ; send error message
        ret                             ; and exit
srdel2: mov     si,offset srvbuf        ; set pointer for crun
        call    crun
         nop
         nop
         nop
srdel3: mov     ah,'Y'                  ; return an ack
        mov     pack.argbk1,0           ; no data
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    pktsize                 ; report packet size
        call    spack
         nop
         nop
         nop
        ret
srvdel  endp

; serspc - handle other side's request of Remote Space  [jrd]
srvspc  proc    near
        test    denyflg,spcflg          ; is command enabled?
        jz      srspc1                  ; z = yes
        mov     bx,offset remms9        ; else give a message
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    errpack                 ; back to local kermit
        ret
srspc1: mov     di,offset srvbuf        ; work area
        mov     si,offset spcstr        ; prepend "chkdsk.com "
        call    strcpy
        mov     si,offset srvtmp    ; add redirection tag of " >$kermit$.tmp"
        call    strcat
        mov     si,offset srvbuf        ; command pointer for crun
        call    crun
         nop
         nop
         nop
        mov     si,offset srvtmp+2      ; get name of temp file
        mov     di,offset diskio.string ; destination
        call    strcpy                  ; copy it there
        mov     ax,di                   ; filename pointer for isfile
        call    isfile                  ; did we make the temp file?
        jnc     srspc2                  ; nc = yes
        mov     trans.chklen,1          ; reply with 1 char checksum
        mov     bx,offset remms7        ; "Could not create space listing"
        call    errpack                 ; send the error message
        ret                             ; and exit
srspc2: mov     flags.xflg,1            ; say use X rather than F packet
        mov     pack.state,'R'          ; remember state.
        call    send11                  ; this should send it
         nop
         nop
         nop
        mov     flags.xflg,0            ; clear flag
        mov     dx,offset diskio.string
        mov     ah,del2                 ; delete the file
        int     dos
        ret                             ; return in any case
srvspc  endp

; srvwho - respond to remote host's WHO command. [jrd]
srvwho  proc    near
        mov     si,offset whomsg        ; add brief msg of just us chickens
        mov     di,offset data          ; packet's data field
        call    strcpy                  ; copy msg to pkt
        mov     dx,si                   ; strlen works on dx
        call    strlen
        mov     trans.chklen,1          ; reply with 1 char checksum
        mov     ah,'Y'                  ; reply with an ack
        mov     pack.argbk1,cx          ; length
        call    pktsize                 ; report packet size
        call    spack                   ; send it, expect no response
         nop                            ; ignore errors
         nop
         nop
        ret
srvwho  endp

; srvmsg - respond to remote host's Message (Send) command
;  show message on our screen. [jrd]
srvsen  proc    near
        cmp     pack.argbk1,1           ; Any data in the packet?
        jbe     srvsen1                 ; e = no, just ack the message.
        call    dodec                   ; Decode data.
        mov     di,offset data+2   ; Where the reply is. (skip M and byte cnt)
        cmp     byte ptr [di-2],'M'     ; Message packet?
        jne     srvsen1                 ; ne = no, ack and forget
        mov     cl,byte ptr [di-1]      ; How much data we have.
        sub     cl,' '                  ; remove ascii bias
        cbw                             ; make a whole word
        jle     srvsen1                 ; le = nothing
        call    prtscr                  ; Print it on the screen.
srvsen1:mov     ah,'Y'                  ; reply with an ack
        mov     pack.argbk1,0           ; length
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    pktsize                 ; report packet size
        call    spack                   ; send it, expect no response
         nop                            ; ignore errors
         nop
         nop
        ret
srvsen  endp


; srvhos - handle other side's request of REM Host command-line. [jrd]
; We execute the command with STDOUT redirected to $kermit$.tmp and then
; read and transmit that file to the other end. No such file results in
; returning just an error msg ACK packet.
srvhos  proc    near
        test    denyflg,hostflg         ; command enabled?
        jz      srvhos2                 ; z = yes
        mov     trans.chklen,1          ; reply with 1 char checksum
        mov     bx,offset remms9        ; else give a message
        call    errpack                 ; back to local kermit
        jmp     rskp

srvhos2:mov     si,offset data          ; received filename, asciiz from rpack
        mov     di,offset srvbuf        ; destination
        call    strcpy                  ; copy data to srvbuf
        mov     si,offset srvtmp    ; add redirection tag of " >$kermit$.tmp"
        call    strcat
        mov     si,offset srvbuf        ; si = pointer for crun
        call    crun                    ; go do the command
         nop
         nop
         nop
        mov     si,offset srvtmp+2      ; get name of temp file
        mov     di,offset diskio.string ; destination
        call    strcpy                  ; copy it to diskio.string
        mov     ax,di                   ; filename pointer for isfile
        call    isfile                  ; did we make the temp file?
        jnc     srhos1                  ; nc = yes
        mov     si,offset remms10       ; say could not create work file
        mov     di,offset data          ; packet's data field
        call    strcpy                  ; copy msg to pkt
        mov     dx,si                   ; strlen works on dx
        call    strlen
        mov     trans.chklen,1          ; reply with 1 char checksum
        mov     ah,'Y'                  ; reply with an ack
        mov     pack.argbk1,cx          ; length
        mov     trans.chklen,1          ; reply with 1 char checksum
        call    pktsize                 ; report packet size
        call    spack
         nop
         nop
         nop
        jmp     rskp                    ; and exit
srhos1: mov     flags.xflg,1            ; say use X rather than F packet
        mov     pack.state,'R'          ; remember state.
        call    send11                  ; this should send it
         nop
         nop
         nop
        mov     flags.xflg,0            ; clear flag
        mov     dx,offset diskio.string
        mov     ah,del2                 ; delete the temp file
        int     dos
        jmp     rskp                    ; return in any case
srvhos  endp

; Respond to other side's request of Remote Help. Write & read $kermit$.tmp.
; Return rskp. [jrd]
srvhlp  proc    near
        mov     si,offset srvtmp+2      ; use filename of $kermit$.tmp
        mov     di,offset diskio.string ; put name here
        call    strcpy
        mov     ah,creat2               ; create the file
        mov     cx,0                    ; attributes r/w
        mov     dx,offset diskio.string ; use $kermit$.tmp name
        int     dos
        jc      srvhlp4                 ; c = could not open
        mov     diskio.handle,ax        ; file handle
        mov     dx,offset hlprem        ; data to be sent, strlen uses dx
        call    strlen                  ; put string length in cx
        mov     ah,write2               ; write to file
        mov     bx,diskio.handle
        int     dos                     ; write the info, ignore errors
        mov     ah,close2       ; close the file so we can reread it below
        mov     bx,diskio.handle
        int     dos
                                        ; Send temporary file to remote screen
        mov     flags.xflg,1            ; say use X rather than F packet
        mov     pack.state,'R'          ; remember state.
        call    send11                  ; this should send it
         nop
         nop
         nop
        mov     flags.xflg,0            ; clear flag
        mov     dx,offset diskio.string ; filename
        mov     ah,del2                 ; delete the temp file
        int     dos
        jmp     rskp                    ; and return

srvhlp4:mov     si,offset remms3        ; say could not create help file
        mov     di,offset data          ; packet's data field
        call    strcpy                  ; copy msg to pkt
        mov     dx,si                   ; strlen works on dx
        call    strlen
        mov     trans.chklen,1          ; reply with 1 char checksum
        mov     ah,'Y'                  ; reply with an ack
        mov     pack.argbk1,cx          ; length
        call    pktsize                 ; report packet size
        call    spack
         nop
         nop
         nop
        jmp     rskp                    ; and exit
srvhlp  endp

srvdsa  proc    near                    ; DISABLE Server commands
        mov     dx,offset srvtab
        mov     bx,offset sdshlp
        mov     ah,cmkey                ; parse key word
        call    comnd
         jmp    r                       ; bad parse
        mov     temp,bx                 ; save key value
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r                       ; no confirm
        mov     bx,temp                 ; get selected item
        or      denyflg,bx              ; turn on bit (deny) for that item
        jmp     rskp                    ; return successfully
srvdsa  endp

srvena  proc    near                    ; ENABLE Server commands
        mov     dx,offset srvtab        ; keyword table
        mov     bx,offset senhlp        ; help on keywords
        mov     ah,cmkey                ; parse key word
        call    comnd
         jmp    r                       ; bad parse
        mov     temp,bx                 ; save key value
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r                       ; no confirm
        mov     bx,temp                 ; item to be enabled
        not     bx                      ; invert bits
        and     denyflg,bx              ; turn off (enable) selected item
        jmp     rskp
srvena  endp


; srvini - init parms based on init packet
srvini  proc    near
        mov     bx,offset data
        mov     ax,pack.argbk1          ; get number of data bytes
        call    spar                    ; parse info
        call    packlen         ; this should really be part of spar, but...
        mov     bx,offset data
        call    rpar                    ; setup info about our reception
        push    ax
        mov     al,trans.chklen         ; checksum length negotiated
        mov     curchk,al               ; use as new working length
        pop     ax
        mov     al,ah
        mov     ah,0
        mov     pack.argbk1,ax          ; set size of return info
        mov     trans.chklen,1          ; reply with 1 char checksum
        mov     ah,'Y'
        call    pktsize                 ; report packet size
        call    spack                   ; send the packet off
         nop
         nop
         nop
        mov     al,curchk       ; restore checksum length before proceeding
        mov     trans.chklen,al
        jmp     rskp                    ; and go succeed
srvini  endp

;       This is the REMOTE command.

REMOTE  PROC    NEAR
        mov     dx,offset remtab      ; Parse a keyword from the REMOTE table.
        mov     bx,offset remhlp
        mov     ah,cmkey
        call    comnd
         jmp    r
        call    bx                      ; Call the appropriate routine.
         jmp    r                       ; Command failed.
        jmp     rskp
REMOTE  ENDP

; REMDIS - Get disk usage on remote system.

REMDIS  PROC    NEAR
        mov     remcmd,'U'              ; Disk usage command.
        mov     rempac,'G'              ; Packet type = generic.
        jmp     genric                  ; Execute generic Kermit command.
REMDIS  ENDP


; REMHEL - Get help about remote commands.

REMHEL  PROC    NEAR
        mov     remcmd,'H'              ; Help
        mov     rempac,'G'              ; Packet type = generic.
        jmp     genric                  ; Execute generic Kermit command.
REMHEL  ENDP

; REMTYP - Type a remote file.

REMTYP  PROC    NEAR
        mov     remcmd,'T'              ; Type the file.
        mov     rempac,'G'              ; Packet type = generic.
        jmp     genric
REMTYP  ENDP

; REMHOS - Execute a remote host command.

REMHOS  PROC    NEAR
        mov remcmd,' '                  ; Don't need one.
        mov rempac,'C'                  ; Packet type = remote command.
        jmp genric
REMHOS  ENDP

; REMKER - Execute a remote Kermit command.

REMKER  PROC    NEAR
        mov     remcmd,' '              ; Don't need one.
        mov     rempac,'K'              ; Packet type = remote Kermit command.
        jmp     genric
REMKER  ENDP

; REMDIR - Do a directory.

REMDIR  PROC    NEAR
        mov     remcmd,'D'
        mov     rempac,'G'              ; Packet type = generic.
        jmp     genric
REMDIR  ENDP

; REMDEL - Delete a remote file.

REMDEL  PROC    NEAR
        mov     remcmd,'E'
        mov     rempac,'G'              ; Packet type = generic.
        jmp     genric
REMDEL  ENDP

; REMCWD - Change remote working directory.

REMCWD  PROC    NEAR
        mov     remcmd,'C'
        mov     rempac,'G'              ; Packet type = generic.
        jmp     genric
REMCWD  ENDP

; REMSEN - Send one line short message to remote screen. [jrd]

REMSEN  proc    near
        mov     remcmd,'M'
        mov     rempac,'G'
        jmp     genric
REMSEN  endp

; REMWHO - ask for list of remote logged on users [jrd]

REMWHO  proc    near
        mov     remcmd,'W'
        mov     rempac,'G'
        jmp     genric
REMWHO  endp

; GENRIC - Send a generic command to a remote Kermit server.

GENRIC  PROC    NEAR
        call    begtim                  ; start statistics
        mov     bx,offset srvbuf        ; Where to put the text.
        cmp     rempac,'C'              ; Remote host command?
        je      genra                   ; Yes, leave as is.
        cmp     rempac,'K'              ; Remote Kermit command?
        je      genra                   ; e = yes. Don't use counted string.
        add     bx,2                    ; Leave room for type and size.
genra:  mov     ah,cmtxt                ; Parse arbitrary text up to a CR.
        mov     dx,offset genmsg        ; In case they want text.
        call    comnd
         jmp    r
        mov     al,ah                   ; Don't forget the size.
        mov     ah,0
        mov     cnt,ax                  ; Save it here.
        mov     flags.xflg,1            ; output coming to screen
        cmp     rempac,'K'              ; Remote Kermit command?
        je      genra1                  ; e = yes
        cmp     rempac,'C'              ; Remote host command?
        jne     genrb                   ; No, skip this part.
genra1: call    ipack                   ; Remote Host or Kermit
         jmp    genr2
        mov     curchk,ah               ; Save desired checksum length.
        mov     trans.chklen,1          ; Use 1 char for server functions.
        mov     pack.numrtr,0           ; No retries yet.
        mov     pack.numtry,0
        jmp     genr1                   ; Send the packet.
genrb:  mov     ax,cnt
        cmp     ax,0                    ; Any data?
        je      genr0                   ; Nope.
        mov     ah,al                   ; Don't overwrite the real count value
        add     ah,32                   ; Do the char function.
        mov     temp,bx                 ; Remember where we are.
        mov     bx,offset srvbuf+1      ; Size of remote command.
        mov     [bx],ah
        mov     ah,0
        inc     al                      ; For the size field.
        cmp     remcmd,'C'              ; Change working directory?
        jne     genr0                   ; No, so don't ask for password.
        mov     cnt,ax                  ; Save here for a bit.
        mov     ah,prstr
        mov     dx,offset pass          ; Send along an optional password.
        int     dos
        mov     bx,temp                 ; Where to put the password.
        push    bx                      ; Is safe since subroutine never fails
        inc     bx                      ; Leave room for count field.
        call    input                   ; Read in the password.
        mov     temp,bx                 ; Remember end of data pointer.
        pop     bx                      ; Where to put the size.
        cmp     ah,0                    ; No password given?
        jne     genrc
        mov     ax,cnt
        jmp     genr0                   ; Then that's it.
genrc:  mov     al,ah
        add     ah,32                   ; Make it printable.
        mov     [bx],ah                 ; Tell remote host the size.
        mov     ah,0
        push    ax                      ; Remember the count.
        call    clearl                  ; Clear to end-of-line.
        pop     ax
        inc     al                      ; For second count value.
        add     ax,cnt                  ; Total for both fields of input.
genr0:  inc     al                      ; For char representing the command.
        mov     pack.argbk1,ax          ; Set the size.
        mov     cnt,ax                  ; And remember it.
        mov     pack.argbk1,ax          ; Set the size.
        mov     cnt,ax                  ; And remember it.
        mov     pack.numtry,0           ; Initialize count
        mov     bx,offset srvbuf        ; Start of data buffer.
        mov     ah,remcmd               ; Command subtype.
        mov     [bx],ah
        call    ipack                   ; Send init parameters.
         jmp    genr2
         nop                            ; Make it 3 bytes long.
        mov     ah,trans.chklen
        mov     curchk,ah               ; Save desired checksum length.
        mov     trans.chklen,1          ; Use 1 char for server functions.
        mov     pack.numrtr,0           ; No retries yet.
genr1:  cmp     pack.state,'A'          ; Did the user type a ^C?
        je      genr2x
        mov     ah,pack.numtry
        cmp     ah,maxtry               ; Too many tries?
        jl      genr3                   ; Nope, keep trying.
genr2:  mov     ah,prstr
        mov     dx,offset erms21        ; Print error msg and fail.
        int     dos
genr2x: call    serrst                  ; Reset the port.
        mov     ah,curchk
        mov     trans.chklen,ah         ; Restore.
        mov     flags.xflg,0         ; reset screen output flag before leaving
        xor     ax,ax                   ; tell statistics this was a read
        or      errlev,4             ; DOS error level, failure of REMote cmd
        call    endtim
        jmp     rskp
genr3:  mov     ah,prstr
        mov     dx,offset crlf          ; First go to a new line.
        int     dos
        push    es                      ; Prepare to put string into packet.
        push    ds
        pop     es
        mov     si,offset srvbuf        ; Move from here
        mov     di,offset data          ; to here.
        mov     cx,cnt                  ; Move this many characters.
        cld
        rep     movsb                   ; Perform the string move.
        pop     es
        mov     ax,cnt
        mov     pack.argbk1,ax          ; How much data to send.
        mov     cx,ax                   ; Size of data.
        call    doenc                   ; Encode it.
        inc     pack.numtry             ; Increment number of trials.
        mov     trans.chklen,1          ; use block check 1 to server
        mov     pack.argblk,0           ; Packet number 0.
        mov     ah,rempac               ; Packet type.
        call    pktsize                 ; report packet size
        call    spack                   ; Send the packet.
         jmp    genr2                   ; Tell user we can't do it.
         nop
        call    rpack                   ; Get ACK (w/o screen stuff)
         jmp    genr3a                  ; Got a NAK - try again.
         nop
        jmp     genr3b                  ; Ok.

genr3a: push    ax
        mov     ah,curchk
        mov     trans.chklen,ah         ; Restore after reception.
        pop     ax
        jmp     genr1                   ; NAK, try again

genr3b: push    ax                      ; Ok.
        mov     ah,curchk
        mov     trans.chklen,ah         ; Restore after reception.
        pop     ax
        cmp     ah,'Y'                  ; Is all OK?
        jne     genr4
        cmp     pack.argbk1,0           ; Any data in the ACK?
        je      genr31                  ; Nope - just return.
        call    dodec                   ; Decode data.
        mov     di,offset data          ; Where the reply is.
        mov     cx,pack.argbk1          ; How much data we have.
        call    prtscr                  ; Print it on the screen.
        mov     flags.xflg,0    ; reset screen output flag before leaving
genr31: xor     ax,ax                   ; tell statistics this was a read
        call    endtim                  ; tell statistics
        jmp     rskp                    ; And we're done.
genr4:  cmp     ah,'X'                  ; Text packet?
        je      genr5
        cmp     ah,'S'                  ; Handling this like a file?
        jne     genr6
        mov     pack.state,'R'          ; Set the state.
        mov     bx,offset rin21         ; Where to go to.
        jmp     genr51                  ; Continue.
genr5:  mov     pack.state,'F'
        call    dodec                   ; Decode data.
        mov     bx,offset rfile3        ; Jump to here.
genr51: mov     flags.xflg,1            ; Remember we saw an "X" packet.
        mov     pack.numtry,0
        mov     pack.numrtr,0
        mov     pack.numpkt,0
        mov     pack.pktnum,0
        call    bx                      ; Handle it almost like filename.
        call    read2                   ; Receive the rest.
         jmp    r                       ; Oops, we failed.
         nop
        xor     ax,ax                   ; tell statistics this was a read
        call    endtim
        jmp     rskp                    ; Done OK.
genr6:  cmp     ah,'E'                  ; Error packet?
        je      genr6x                  ; e = yes
        jmp     genr1                   ; Try again.
genr6x: call    dodec                   ; Decode data.
        call    error1                  ; Print the error messge.
        call    serrst
        xor     ax,ax                   ; tell statistics this was a read
        call    endtim
        mov     flags.xflg,0    ; reset screen output flag before leaving
        jmp     rskp                    ; And return.
GENRIC  ENDP

; Send  "I" packet with transmission parameters

IPACK   PROC    NEAR
        call    serini                  ; Initialize port
        call    ihosts                  ; initialize the host
        mov     pack.pktnum,0           ; Use packet number 0.
        mov     pack.numtry,0           ; Number of retries.
        mov     pack.numrtr,-1          ; no retries (incremented below)
ipk0:   call    updrtr
        cmp     pack.state,'A'          ; Did user type a ^C?
        je      ipk0x                   ; e = yes
        push    dx
        mov     dl,imxtry
        cmp     pack.numtry,dl          ; Reached our limit?
        pop     dx
        jl      ipk1                    ; l = no
ipk0x:  ret                             ; Yes, so we fail.
ipk1:   inc     pack.numtry             ; Save the updated number of tries.
        mov     bx,portval
        mov     trans.ebquot,dqbin   ; default 8 bit quote, needed with parity
        cmp     [bx].parflg,parnon      ; using parity = none locally?
        jne     ipk1a           ; ne = using local parity, need 8 bit quoting
        mov     trans.ebquot,'Y'   ; say can do 8 bit quoting, if they insist
ipk1a:  mov     bx,offset data          ; Get a pointer to our data block
        call    rpar                    ; Set up the parameter information.
        xchg    ah,al
        mov     ah,0
        mov     pack.argbk1,ax          ; Save the number of arguments.
        mov     pack.argblk,0           ; Use packet number 0.
        mov     ah,dtrans.seol          ; restore default end-of-line char
        mov     trans.seol,ah
        mov     ah,trans.chklen
        mov     curchk,ah               ; Save real value.
        mov     trans.chklen,1          ; One char for server function.
        call    pktsize                 ; report packet size
        mov     ah,'I'                  ; "I" packet.
        call    spack                   ; Send the packet.
         jmp    ipk4
         nop
        call    rpack                   ; Get a packet.
         jmp    ipk4                    ; Try again.
         nop
        push    ax
        mov     ah,curchk
        mov     trans.chklen,ah         ; Reset.
        pop     ax
        cmp     ah,'Y'                  ; ACK?
        jne     ipk3                    ; If not try next.
        mov     ax,pack.pktnum          ; Get the packet number.
        cmp     ax,pack.argblk          ; Is it the right packet number?
        je      ipk2
         jmp    ipk0                    ; If not try again.
ipk2:   mov     ax,pack.argbk1          ; Get the number of pieces of data.
        mov     bx,offset data          ; Pointer to the data.
ipk2a:  call    spar                    ; Read in the data.
        mov     ah,trans.chklen
        mov     curchk,ah               ; This is what we decided on.
        call    packlen                 ; Get max send packet size.
        mov     pack.numtry,0           ; Reset the number of tries.
        jmp     rskp
ipk3:   cmp     ah,'N'                  ; NAK?
        jne     ipk3y                   ; Yes, try again.
        jmp     ipk0
ipk3y:  cmp     ah,'E'                  ; Is it an error packet.
        je      ipk3x
        jmp     ipk0                    ; Trashed data.
ipk3x:  mov     ax,0            ; Other side doesn't know about "I" packet.
                                ; force defaults (zero length response)
        jmp     ipk2a           ;   to use lowest common denominator
ipk4:   mov     ah,curchk
        mov     trans.chklen,ah         ; Reset.
        cmp     flags.cxzflg,0          ; did user say quit?
        jne     ipk5                    ; ne = yes, quit
        jmp     ipk0                    ; Keep trying.
ipk5:   ret
IPACK   ENDP

; Returns in AH the count of characters read in.
;         in BX the updated pointer to the input buffer.

INPUT   PROC    NEAR
        mov     cl,0                    ; Keep a count.
        mov     inpbuf,bx               ; Where to put data.
        mov     comand.cmquiet,1        ; turn on quiet mode
input0: call    cmgetc                  ; get a character nicely
        cmp     al,CR                   ; Done with input?
        jne     input1
        push    cx                      ; save count
        mov     ah,prstr
        mov     dx,offset crlf
        int     dos                     ; echo a carriage return
        pop     cx
        mov     ah,cl                   ; Return count in AH.
        mov     comand.cmquiet,0        ; turn off quiet mode
        jmp     r
input1: cmp     al,BS                   ; Backspace?
        je      inpt11
        cmp     al,DEL                  ; Or delete?
        jne     input3
inpt11: dec     cl                      ; Don't include in char count.
        cmp     cl,0                    ; Backspaced too much?
        jns     input2                  ; No, is OK.
        mov     ah,conout
        mov     dl,bell
        int     dos
        mov     cl,0
        jmp     input0
input2: dec     bx                      ; 'Remove' from buffer.
        jmp     input0                  ; Go get more.
input3: cmp     al,'U'-64               ; Control-U?
        jne     input4
        mov     cl,0                    ; Reset count to zero.
        mov     bx,inpbuf               ; Start at head of buffer.
        jmp     input0
input4: mov     [bx],al                 ; Add char to buffer.
        inc     cl                      ; Include in count.
        inc     bx
        jmp     input0
INPUT   ENDP


; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.

RSKP    PROC    NEAR
        pop     bp
        add     bp,3
        push    bp
        ret
RSKP    ENDP

; Jumping here is the same as a ret.

R       PROC    NEAR
        ret
R       ENDP

code    ends
        end

; Jumping here is the same as a ret.

R       PROC    NEAR
        ret
R       ENDP

code    ends
        end
