; File MSSSER.ASM
; Edit history:
; [2.29] [jrd]
; Add a few safety clearings of filename buffers and the filename override
; flag to help prevent old commands from influencing current ones. [jrd]
;
; [v2.28 jrd]
; Correct protocol bugs in remote server commands
; Correct protocol error of server not decoding packets.
; Remove DOS 1.x code
; Add support for advanced server requests from distant kermit (CWD, Del,
;  Dir, Space, Type, and Host, but not Help). REM HOST support needs further
;  work to fully trap stdin, stdout, and stderr; else, some systems crashes
;  will occur with strange programs.
; General clean up as well.
; Joe R. Doupnik 13 Oct 1985 [jrd]
; Fix bug of server mode not sending files with repeat prefixing. Cause was
;  my confusion on non-standard protocol of setting the prefix char.
; Joe R. Doupnik  22 Dec 1985.
; Make keywords mixed case for reading ease. 27 Dec 1985 [jrd]
; For Get command allow the first char of either Local or Remote filename
;  to be '#' which will be translated into '?' when the line is finished.
; Reset flags.cxzflg and flags.xflg at start of each server mode command
;  to prevent previously failed commands to influence current command.
; Joe R. Doupnik 29 Dec 1985
; Add rejection of REM DELete file(s) if files are hidden, system, vol label,
;       or subdirectory. 9 Jan 1986
; [v2.28]
; Clear screen before trying to get init packet in GET, so we don't
; print an error message in the middle of an uninitialized screen
; if the packet never comes. (From Edgar Butt, Univ. of Md.)
; JD 1 May 1985

        public  logout, bye, finish, remote, get, server
        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

scrser  equ     0023H           ; place for server state display line [jrd]
scrsrm  equ     0100H           ; place for messages and dos echoes [jrd]
remcmd  db      0               ; Remote command to be executed. [21c]
rempac  db      0               ; Packet type: C (host) or G (generic). [21c]

cmer05  db      cr,lf,'?Filename must be specified$'     ; [21a]
ermes7  db      '?Unable to receive initiate$'
erms18  db      cr,lf,'?Unable to tell host that session is finished$'
erms19  db      cr,lf,'?Unable to tell host to logout$'
erms21  db      cr,lf,'?Unable to tell host to execute command$' ; [21c]
infms1  db      'Server mode',cr,lf,'$'
remms1  db      'Kermit-MS: Unknown server command$'
remms2  db      'Kermit-MS: Illegal file name$'
remms3  db      'Kermit-MS: Unknown generic command$'
remms4  db      'Kermit-MS: Unable to change directories$'; [jrd]
remms5  db      'Kermit-MS: No such file(s)$'   ; [jrd]
remms6  db      'Kermit-MS: Could not create directory listing$' ; [jrd]
remms7  db      'Kermit-MS: Could not create space listing$'    ; [jrd]
remms8  db      'Kermit-MS: Protected or no such file(s)$'  ; [jrd]
pass    db      lf,cr,' Password: $'    ; When change directory. [21c]
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,'$'
temp    dw      0
inpbuf  dw      0               ; Pointer to input buffer. [21c]
cnt     dw      0

srvchr  db      'SRGIEC'        ; server cmd characters [jrd]
srvfln  equ     $-srvchr        ; length of tbl
srvfun  dw      srvsnd,srvrcv,srvgen,srvini,serv1,srvhos ; order as in srvchr

remhlp  db      cr,lf,'CWD    connect to a directory'   ; [21c start]
        db      cr,lf,'Delete a file'
        db      cr,lf,'Directory  listing'
        db      cr,lf,'Help'
        db      cr,lf,'Host   command'
        db      cr,lf,'Space  in a directory'
        db      cr,lf,'Type   a file$'                  ; [21c end]

remtab  db      07H             ; Seven entries. [21c start]
        mkeyw   'CWD',remcwd
        mkeyw   'Delete',remdel
        mkeyw   'Directory',remdir
        mkeyw   'Help',remhel
        mkeyw   'Host',remhos
        mkeyw   'Space',remdis
        mkeyw   'Type',remtyp           ; [21c end]

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)
datas   ends

code    segment public
        extrn comnd:near, serrst:near, spack:near, rpack5: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, init1:near
        extrn rpack:near,nak:near, rrinit:near, cmblnk:near, poscur:near
        extrn error:near, 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
        assume  cs:code,ds:datas

; LOGOUT - tell remote KERSRV to logout.

LOGOUT  PROC    NEAR
        mov ah,cmcfm
        call comnd              ; Get a confirm.
         jmp r
        call logo
         jmp rskp               ; Go get another command whether we ....
        jmp rskp                ; .... succeed or fail.
LOGOUT  ENDP

LOGO    PROC    NEAR
        mov pack.numtry,0       ; Initialize count.
        mov pack.numrtr,0       ; No retries yet.
        call serini             ; Initialize port.  [14]
        mov ah,trans.chklen     ; Don't forget the checksum length.
        mov curchk,ah
        mov trans.chklen,1      ; Use one char for server functions.
logo1:  cmp pack.state,'A'      ; Did user type a ^C?
        je logo2x               ; Yes just leave.
        mov ah,pack.numtry
        cmp ah,maxtry           ; Too many times?
        js logo3                ; No, try it.
logo2:  mov ah,prstr
        mov dx,offset erms19
        int dos
logo2x: call serrst             ; Reset port.  [14]
        mov ah,curchk
        mov trans.chklen,ah     ; Restore value.
        ret
logo3:  inc pack.numtry         ; Increment number of tries.
        mov pack.argblk,0       ; Packet number zero.
        mov pack.argbk1,1       ; One piece of data.
        mov bx,offset data
        mov ah,'L'
        mov [bx],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 rpack5             ; Get ACK (w/o screen msgs.)
         jmp logo1              ; Go try again.
         nop
        push ax
        call dodec              ; Decode packet.
        mov ah,curchk
        mov trans.chklen,ah     ; Restore value.
        pop ax
        cmp ah,'Y'              ; ACK?
        jne logo4
        call serrst             ; Reset port.  [14]
        jmp rskp
logo4:  cmp ah,'E'              ; Error packet?
        jnz logo1               ; Try sending the packet again.
        call error1
        call serrst             ; Reset port.  [14]
        ret
LOGO    ENDP

; FINISH - tell remote KERSRV to exit.

FINISH  PROC    NEAR
        mov ah,cmcfm            ; Parse a confirm.
        call comnd
         jmp r
        mov pack.numtry,0       ; Initialize count.
        mov pack.numrtr,0       ; No retries yet.
        call serini             ; Initialize port.  [14]
        mov ah,trans.chklen     ; Don't forget the checksum length.
        mov curchk,ah
        mov trans.chklen,1      ; Use one char for server functions.
fin1:   cmp pack.state,'A'      ; ^C typed?
        je fin2x
        mov ah,pack.numtry
        cmp ah,maxtry           ; Too many times?
        js fin3                 ; Nope, try it.
fin2:   mov ah,prstr
        mov dx,offset erms18
        int dos
fin2x:  call serrst             ; Reset port.  [14]
        mov ah,curchk
        mov trans.chklen,ah     ; Restore value.
        jmp rskp                ; Go home.
fin3:   inc pack.numtry         ; Increment number of tries.
        mov pack.argblk,0       ; Packet number zero.
        mov pack.argbk1,1       ; One piece of data.
        mov bx,offset data
        mov ah,'F'
        mov [bx],ah             ; Finish running Kermit.
        mov cx,1                ; One piece of data.
        call doenc              ; Do encoding.
        mov ah,'G'              ; Generic command packet.
        call spack
         jmp fin2               ; Tell user and die.
         nop
        call rpack5             ; Get ACK (w/o screen stuff).
         jmp fin1               ; Go try again.
         nop
        push ax
        call dodec              ; Decode data.
        mov ah,curchk
        mov trans.chklen,ah     ; Restore value.
        pop ax
        cmp ah,'Y'              ; Got an ACK?
        jnz fin4
        call serrst             ; Reset port. [14]
        jmp rskp                ; Yes, then we're done.
fin4:   cmp ah,'E'              ; Error packet?
        jnz fin1                ; Try sending it again.
        call error1
        call serrst             ; Reset port.  [14]
        jmp rskp
FINISH  ENDP

; BYE command - tell remote KERSRV to logout & exits to DOS.

BYE     PROC    NEAR
        mov ah,cmcfm            ; Parse a confirm.
        call comnd
         jmp r
        call logo               ; Tell the mainframe to logout.
         jmp rskp               ; Failed - don't exit.
        mov flags.extflg,1      ; Set exit flag.
        jmp rskp                                        ; [8 end]
BYE     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 ctl-c typed yet...
        mov cnt,0               ; count of filename chars
        mov bx,offset srvbuf    ; Where to put text.  [8 start]
        mov byte ptr [bx],0     ; clear for safety. [jrd]
        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. [jrd]
        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 [jrd]
        mov byte ptr locfil,0   ; clear, for safety
        mov ah,cmfile           ; allow paths [jrd]
        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
        jmp rskp
get5:   cmp flags.destflg,2     ; receiving to screen?
        je get5a                ; e = yes, skip screen stuff
        mov flags.xflg,0        ; no, reset x flag
        cmp flags.remflg,0      ; in remote mode?
        jne get5a               ; yes, don't print anything
        call init               ; init screen
get5a:  call ipack              ; Initialize.
         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 [jrd]
        cmp flags.remflg,0      ; remote mode?
        jne get6                ; yes, don't print anything
        cmp flags.destflg,2     ; Receiving to screen? [27c]
        je get6                 ; Yes skip screen stuff. [27c]
        call prtfn              ; print filename in data [jrd]
get6:   call init1              ; init buffers
        mov pack.numtry,0       ; Initialize count.
        mov pack.numrtr,-1      ; No retries yet (gets incremented below).
        mov pack.state,'R'      ; this is what state will soon be...
        call serini             ; Initialize port. [jrd]
        mov cx,pack.argbk1      ; Data size.
        call doenc              ; Encode data.
        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:   cmp flags.remflg,0      ; remote mode?
        jne get9                ; yes, no printing
        call erpos
        mov ah,prstr
        mov dx,offset ermes7    ; Can't get init packet.
        int dos
get9:   call rprpos
        call clrmod
        call serrst             ; Reset port.
        mov ah,curchk
        mov trans.chklen,ah     ; Restore value.
        jmp rskp
get10:  inc pack.numtry         ; Increment number of tries.
        mov pack.argblk,0       ; Start at packet zero.
        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      ; this is where rinit wants pkt type if getting
        mov flags.getflg,1      ; note this is a get
        jmp read12              ; go join read code
get11:  mov ah,prstr            ; Complain if no filename.
        mov dx,offset cmer05
        int dos
        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 remote flag
        push    ax              ; preserve for later
;;;     mov     flags.remflg,1  ; set remote if server
        mov     ax,0            ; simulate empty parameter packet [jrd]
        call    spar            ; and thus set our params to defaults [jrd]
        mov     ah,drpt         ; force default repeat prefix char to be [jrd]
        mov     rptq,ah         ;   our active one. [jrd]
        call    cmblnk          ; clear screen
        mov     dx,scrser       ; move cursor to top of screen [jrd]
        call    poscur          ; [jrd]
        mov     ah,prstr
        mov     dx,offset infms1 ; say now in server mode
        int     dos

serv1:  mov     dx,scrsrm       ; move cursor to server message area [jrd]
        add     dx,0100H        ; look at line below (DOS does CR/LF first)
        call    poscur          ; [jrd]
        call    clearl          ; and clear the line
        mov     dx,scrsrm       ; back to message line
        call    poscur
; should increase timeout interval
        mov     flags.nmoflg,0  ; clear, say no local override filenames [jrd]
        mov     flags.cxzflg,0  ; clear ^X, ^Z, ^C seen flag. [jrd]
        mov     flags.xflg,0    ; reset X packet flag. [jrd]
        mov     byte ptr srvbuf,0       ; plant terminator to clear [jrd]
        call    serini          ; init serial line (send & recv reset it)
        mov     trans.chklen,1  ; checksum len = 1
        mov     pack.pktnum,0   ; pack number resets to 0
        mov     pack.numtry,0   ; no retries yet.
        call    rpack           ; get a packet
         jmp    short serv2     ; no good, nak and continue
         nop
        cmp     ah,'I'          ; never "decode" S, I, and A packets [jrd]
        je      serv3           ; its an I packet
        cmp     ah,'S'
        je      serv3
        cmp     ah,'A'
        je      serv3
        call    dodec           ; decode packet; protocol error if omitted [jrd]
        jmp     short serv3     ; try to figure this out

serv2:  cmp     flags.cxzflg,'C' ; ctl-C?
        je      serv5           ; yes, stop this.
        call    nak             ; nak the packet
        jmp     serv1           ; and keep readiserv2 packets

serv3:  mov     di,offset srvchr ; server characters
        mov     cx,srvfln       ; length of striserv2
        mov     al,ah           ; packet type
        repne   scasb           ; hunt for it
        je      serv4           ; we know this one, go handle it
        mov     bx,offset remms1 ; else give a message
        call    errpack         ; back to local kermit
        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...
                                ; should we reset serial line?
        jmp     serv1           ; else keep goiserv2 for more cmds.

serv5:                          ;** restore timer values
        pop     ax              ; get this off stack
        mov     flags.remflg,al ; restore old flag
        call    serrst          ; reset serial handler
        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
        call    spar            ; parse the send-init packet
        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     ah,'Y'          ; ack
        call    spack           ; answer them
         jmp    rskp            ; can't answer, forget this
        call    rrinit          ; init variables for init
        inc     pack.pktnum     ; count the send-init packet.
        mov     pack.state,'F'  ; expecting file name about now
        call    read2           ; and join read code
         nop
         nop
         nop                    ; ignore errors
        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 [jrd]
        mov     di,offset diskio.string ;destination [jrd]
        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
        cmp     al,'F'          ; maybe finish?
        je      srvge1          ; yup, handle
        cmp     al,'L'          ; logout?
        jne     srvge2          ; no.
srvge1: mov     pack.argbk1,0   ; 0-length data
        mov     ah,'Y'
        call    spack           ; ack it
         nop
         nop
         nop                    ; *** ignore error?
        ret                     ; and return to signal exit.
srvge2: cmp     al,'T'          ; Type a file? [jrd]
        jne     srvge3          ; ne = no [jrd]
        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? [jrd]
        jne     srvge6          ; ne = no
        call    srvcwd          ; do it
        jmp     rskp
srvge6: cmp     al,'U'          ; do a space command?
        jne     srvgex
        call    srvspc          ; do the space command
        jmp     rskp
srvgex: mov     bx,offset remms3
        call    errpack
        jmp     rskp
srvgen  endp

; srvcwd - handle other side's Remote CWD dirspec [jrd]
srvcwd  proc    near
        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
        rep movsb               ; copy data to srvbuf, cx chars worth
        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.
        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
        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
        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,offset diskio.string ;destination
        rep movsb               ; copy data to diskio.string
        mov     byte ptr [di],0 ; plant terminator
        mov     ax,offset diskio.string ; pointer to filename, for isfile
        call    isfile          ; does it exist?
        jnc     srtyp1          ; nc = yes
        mov     bx,offset remms5 ; "No such file(s)"
        call    errpack         ; send error message
        ret                     ; and exit
srtyp1: mov     flags.xflg,1    ; say use X packet 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
        ret                     ; return in any case
srvtyp  endp

; serdir - handle other side's Remote Dir filespec(optional) request [jrd]
srvdir  proc    near
        mov     ax,ds
        mov     es,ax
        mov     di,offset srvbuf        ; work area
        mov     si,offset dirstr        ; prepend "dir "
        call    strcpy
        mov     dx,offset srvbuf
        call    strlen
        add     di,cx           ; di points at terminator
        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     srdir1          ; le = no
        mov     si,offset data+2 ; received filespec, asciiz from rpack [jrd]
        rep movsb               ; append data to srvbuf
        mov     byte ptr [di],0 ; plant terminator
srdir1: 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     ax,ds
        mov     es,ax
        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     srdir2          ; nc = yes
        mov     bx,offset remms6 ; "Could not create directory listing"
        call    errpack         ; send the error message
        ret                     ; and exit
srdir2: mov     flags.xflg,1    ; say use X packet 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
        mov     ax,ds
        mov     es,ax
        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 [jrd]
        rep movsb               ; append data to srvbuf
        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)"
        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
        call    spack
         nop
         nop
         nop
        ret
srvdel  endp

; serspc - handle other side's request of Remote Space  [jrd]
srvspc  proc    near
        mov     ax,ds
        mov     es,ax
        mov     di,offset srvbuf        ; work area
        mov     si,offset spcstr        ; prepend "chkdsk.com "
        call    strcpy
        mov     dx,offset srvbuf
        call    strlen
        add     di,cx           ; di points at terminator
        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     ax,ds
        mov     es,ax
        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     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 packet 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

; 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 empty ACK packet.
srvhos  proc    near
        mov     ax,ds
        mov     es,ax
        mov     si,offset data  ; received filename, asciiz from rpack [jrd]
        mov     di,offset srvbuf ;destination [jrd]
        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     ax,ds
        mov     es,ax
        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     ah,'Y'          ; no. just return an empty ack
        mov     pack.argbk1,0   ; no data
        call    spack
         nop
         nop
         nop
        jmp     rskp            ; and exit
srhos1: mov     flags.xflg,1    ; say use X packet 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

; srvini - init parms based on init packet
srvini  proc    near
        mov     bx,offset data
        call    spar            ; parse info
        call    packlen         ; this should really be part of spar, but...
        mov     bx,offset data
        call    rpar            ; get receive info
        mov     al,ah
        mov     ah,0
        mov     pack.argbk1,ax  ; set size of return info
        mov     ah,'Y'
        call    spack           ; send the packet off
         jmp    rskp
        jmp     rskp            ; and go succeed
srvini  endp

;       This is the REMOTE command. [21c]

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. [21c]

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. [21c]

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. [21c]

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. [21c]

REMHOS  PROC    NEAR
        mov remcmd,' '          ; Don't need one.
        mov rempac,'C'          ; Packet type = remote command.
        jmp genric
REMHOS  ENDP

; REMDIR - Do a directory. [21c]

REMDIR  PROC    NEAR
        mov remcmd,'D'
        mov rempac,'G'          ; Packet type = generic.
        jmp genric
REMDIR  ENDP

; REMDEL - Delete a remote file. [21c]

REMDEL  PROC    NEAR
        mov remcmd,'E'
        mov rempac,'G'          ; Packet type = generic.
        jmp genric
REMDEL  ENDP

; REMCWD - Change remote working directory.  [21c]

REMCWD  PROC    NEAR
        mov remcmd,'C'
        mov rempac,'G'          ; Packet type = generic.
        jmp genric
REMCWD  ENDP

; GENRIC - Send a generic command to a remote Kermit server. [21c]

GENRIC  PROC    NEAR
        mov bx,offset srvbuf    ; Where to put the text.
        cmp rempac,'C'          ; Remote host command?
        je genra                ; Yes, leave as is.
        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,'C'          ; Remote host command?
        jne genrb               ; No, skip this part.
        call ipack
         jmp genr2
        mov pack.numtry,0
        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.
        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 the char representing the command.
        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?
        js 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.
        jmp rskp
genr3:  push es                 ; Prepare to put string into packet.
        mov ax,ds
        mov es,ax
        mov si,offset srvbuf    ; Move from here
        mov di,offset data      ; to here.
        mov cx,cnt              ; Move this many characters.
        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 pack.argblk,0       ; Packet number 0.
        mov ah,rempac           ; Packet type.
        call spack              ; Send the packet.
         jmp genr2              ; Tell user we can't do it.
         nop
        call rpack5             ; Get ACK (w/o screen stuff)
         jmp genr1              ; Got a NAK - try again.
         nop
        push ax
        mov ah,curchk
        mov trans.chklen,ah     ; Restore.
        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 ah,prstr
        mov dx,offset crlf      ; First go to a new line.
        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.
genr31: 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
        mov flags.cxzflg,0
        call bx                 ; Handle it almost like filename.
        call read2              ; Receive the rest.
         jmp r                  ; Oops, we failed.
        jmp rskp                ; Done OK.
genr6:  cmp ah,'E'              ; Error packet?
        je genr6x
        jmp genr1               ; Try again.
genr6x: call dodec              ; Decode data.
        call error1             ; Print the error messge.
        call serrst
        jmp rskp                ; And return.
GENRIC  ENDP

; Send "I" packet with transmission parameters. [21c]

IPACK   PROC    NEAR
        mov ah,trans.chklen
        mov curchk,ah           ; Initialize.
        call serini
        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
        cmp pack.numtry,imxtry  ; Reached our limit?
        jl ipk1
ipk0x:  ret                     ; Yes, so we fail.
ipk1:   inc pack.numtry         ; Save the updated number of tries.
        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,trans.chklen
        mov curchk,ah           ; Save real value.
        mov trans.chklen,1      ; One char for server function.
        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.
        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. [21b]
        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:  jmp rskp                ; Other side doesn't know about "I" packet.
ipk4:   mov ah,curchk
        mov trans.chklen,ah     ; Reset.
        jmp ipk0                ; Keep trying.
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


