DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T m

⟦b5d696b36⟧ TextFile

    Length: 73032 (0x11d48)
    Types: TextFile
    Names: »mssser.asm«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦71044c191⟧ »EurOpenD3/misc/kermit.ms-2.32.tar.Z« 
        └─⟦31f2d420d⟧ 
            └─⟦this⟧ »mssser.asm« 

TextFile

        NAME    mssser
; File MSSSER.ASM
; Edit history:
; Last edit 21 Nov 1988
; 21 Nov 1988 Version 2.32
; 10 Nov 1988 Clear flags.cxzflg when exiting server mode from Control-C.
; 1 Nov 1988 Add SET SERVER TIMEOUT control of NAKs in server idle loop.
; 16 Sept 1988 Add REMOTE LOGIN command for Terry Kennedy. Rewrite proc genric
;  to work with editing functions plus do prompts for login command. [jrd]
; 1 July 1988 Version 2.31
; 12 June 1988 Add error recovery if serial port does not initialize.
; 2 June 1988 Add timeout capability to Server command.
; 20 March 1988 Move ENABLE/DISABLE command to mssset, leave denyflg here.[jrd]
; 27 Jan 1988 Remove serrst call, done now in mssker idle loop. [jrd]
; 1 Jan 1988 version 2.30

        public  logout, bye, finish, remote, get, server, denyflg, srvtmo

        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, fsta:word,kstatus:word

scrser  equ     0209H           ; place for server state display line
scrsrm  equ     1000H           ; place for messages and dos echoes

remcmd  db      0               ; Remote command to be executed
rempac  db      0               ; Packet type: C (host) or G (generic)
remlen  db      0               ; length of following text field

ermes1  db      cr,lf,'?More parameters are needed$'
ermes6  db      '?Filename too long for packet$'
erms18  db      '?No response from host$'
infms1  db      'Server mode: type Control-C to exit',cr,lf,'$'
inthlp  db      cr,lf,' Time-limit to remain in Server mode, seconds or'
        db      ' specific hh:mm:ss (24h clock).'
        db      cr,lf,' SET TIMER ON to time.  Return for no time limit.$'
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
spcmsg  db      ' bytes available on disk',cr,lf,0
spcmsg2 db      ' Drive not ready',cr,lf,0
user    db      ' Username: $'          ; for Remote Login
password db     ' Password: $'          ; for Remote Login and Remote CD
account db      ' Account: $'           ; for Remote Login
srvtmp  db      ' >$kermit$.tmp ',0     ; asciiz, kermit's temp output file
delstr  db      'del ',0
dirstr  db      'dir ',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
srvtmo  db      dstime                  ; idle NAKs, use default send timeout
srvtime db      0                       ; non-zero if timing Server residence
srvchr  db      'SRGIEC'                ; server cmd characters
srvfln  equ     $-srvchr                ; length of table
srvfun  dw      srvsnd,srvrcv,srvgen,srvini,rskp,srvhos ; order as in srvchr

remhlp  db      cr,lf,'CD/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,'Login      to remote Kermit server'
        db      cr,lf,'Kermit     command'
        db      cr,lf,'Message    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 MESSAGE 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 CD/CWD directory  REMOTE HOST command       '
        db      'REMOTE WHO',0                          ; null terminated

remtab  db      12                      ; 12 entries
        mkeyw   'CD',remcwd
        mkeyw   'CWD',remcwd
        mkeyw   'Delete',remdel
        mkeyw   'Directory',remdir
        mkeyw   'Help',remhel
        mkeyw   'Host',remhos
        mkeyw   'Kermit',remker
        mkeyw   'Login',remlogin
        mkeyw   'Message',remmsg
        mkeyw   'Space',remdis
        mkeyw   'Type',remtyp
        mkeyw   'Who',remwho

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, lnout:near
        extrn erpos:near, rprpos:near, clrmod:near, crun:near, ctlu:near
        extrn prompt:near, updrtr: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
        extrn inptim:near, chktmo:near, pcwait: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
        mov     ah,trans.chklen         ; Don't forget the checksum length
        mov     curchk,ah
        mov     trans.chklen,1          ; Use one char for server functions
        call    serini                  ; Initialize port
        jc      logo2                   ; c = failure
        call    ihosts                  ; initialize the host
        mov     diskio.string,0         ; clear local filename for stats
        call    begtim                  ; start statistics counter
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                    ; a send operation
        call    endtim                  ; stop statistics counter
        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.seqnum,0           ; Packet number zero
        mov     pack.datlen,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.datlen,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.datlen          ; 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:  mov     ax,1                    ; a send operation
        call    endtim                  ; stop statistics counter
        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     get3b                   ; yes, analyze
                                        ; 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
        mov     al,ah
        mov     ah,0
        mov     cnt,ax                  ; remember number of chars read
        cmp     al,0                    ; count of entered chars
        je      get1                    ; e = none, try again
get2:   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     temp,ax
        mov     ah,cmcfm
        call    comnd
         jmp    r
        mov     ax,temp
        cmp     ah,0                    ; any text?
        je      get2                    ; e = none, ask again
        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

get3b:  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
        or      fsta.xstatus,2+80h      ; set status failed + intervention
        mov     kstatus,2               ; local status
        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    begtim                  ; start statistics
        mov     kstatus,0               ; global status, success
        call    ipack                   ; Send Initialize, 'I', packet
         jmp    get8                    ; Sorry can't do it
         nop
        mov     cx,cnt                  ; Get back remote filename size
        mov     pack.datlen,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
        mov     di,offset fsta.xname    ; to statistics remote name field
        call    strcpy
        mov     di,offset diskio.string ; and to here for prtfn
        call    strcpy
        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.datlen          ; 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
        or      fsta.xstatus,2          ; set status
        mov     kstatus,2               ; global status
get9:   test    flags.remflg,dquiet+dserial ; quiet or serial display?
        jnz     get9a                   ; nz = yes
        call    clrmod
        call    rprpos
get9a:  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.seqnum,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     flags.getflg,ah         ; note this is a GET, use pkt type
        mov     pack.state,'R'          ; Set the state to receive initiate
        jmp     read12                  ; go join read code
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
        or      fsta.xstatus,2          ; set status
        mov     kstatus,2               ; global status
        jmp     rskp
GET     ENDP

; server command

SERVER  PROC    NEAR
        mov     ah,cmfile               ; get a word
        mov     dx,offset srvbuf        ; place to put text
        mov     bx,offset inthlp        ; help message
        call    comnd                   ; get the pattern text
         jmp    r                       ; nothing, complain
         nop
        mov     temp,ax                 ; ah has byte count
        mov     ah,cmcfm
        call    comnd
         jmp    r
        mov     srvtime,0               ; assume not doing timed residence
        cmp     byte ptr temp+1,0       ; time of day given?
        je      serv0b                  ; e = no
        mov     si,offset srvbuf
        cmp     byte ptr [si],'0'       ; numeric or colon?
        jb      serv0                   ; b = not proper time value
        cmp     byte ptr [si],':'       ; this covers the desired range
        ja      serv0                   ; a = no proper time value
        call    inptim                  ; convert text to timeout tod
        jnc     serv0a                  ; nc = no syntax errors in time
serv0:  ret                             ; else return now

serv0a: mov     srvtime,1               ; say doing timed residence
serv0b: 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
        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     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
        cmp     flags.debug,0           ; debug display active?
        je      serv1b                  ; e = no
        mov     fmtdsp,1                ; yes, do formatted display
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
        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    serini          ; init serial line (send & receive reset it)
        jnc     serv1e                  ; nc = success
        jmp     serv5                   ; c = failure
serv1e: cmp     srvtime,0               ; doing timed residence?
        je      serv1d                  ; e = no
        call    chktmo                  ; check for time to exit Server mode
        jnc     serv1d                  ; nc = ok
        jmp     serv5                   ; c = timeout, exit server mode
serv1d: call    rpack                   ; get a packet
         jmp    short serv2             ; no good, nak and continue
         nop
        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
        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?
        jne     serv2a                  ; ne = no
        mov     flags.cxzflg,0          ; clear flag for later uses
        jmp     serv5                   ; and exit server mode
serv2a: cmp     ah,'T'                  ; packet type of time-out?
        jne     serv2b                  ; ne = no
        mov     al,srvtmo               ; server timeout value
        mov     trans.stime,al
        call    nak                     ; nak the packet
        mov     al,curstim
        mov     trans.stime,al          ; restore regular send timeout
serv2b: 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     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.datlen          ; 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.datlen,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.datlen,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
        mov     ax,100                  ; wait 0.1 sec for client to settle
        call    pcwait
        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.datlen,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.datlen,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.datlen,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.datlen,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.datlen,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.datlen,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     dl,0                    ; use current drive
        mov     ah,36h                  ; get disk free space
        int     dos                     ; ax = sectors/cluster
        cmp     ax,0ffffh               ; invalid drive indicator?
        jne     srspc2                  ; ne = no
        mov     di,offset data
        mov     si,offset spcmsg2       ; give Drive not ready message
        call    strcpy
        jmp     short srspc3            ; send it
srspc2: mul     bx                      ; sectors/cluster * clusters = sectors
        mul     cx                      ; bytes = sectors * bytes/sector
        mov     di,offset data          ; destination
        mov     word ptr [di],0d0ah     ; cr/lf
        mov     word ptr[di+2],'  '     ; space space
        add     di,4                    ; start number here
        call    lnout                   ; convert number to asciiz in [di]
        mov     si,offset spcmsg        ; trailer of message
        call    strcat                  ; tack onto end of number part
srspc3: mov     trans.chklen,1          ; reply with 1 char checksum
        mov     dx,offset data
        call    strlen                  ; get data size into cx for doenc
        call    doenc                   ; encode
        mov     ah,'Y'                  ; reply with an ack
        call    pktsize                 ; report packet size
        call    spack                   ; send it, expect no response
         nop                            ; ignore errors
         nop
         nop
        ret
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.datlen,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.datlen,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     ch,0
        mov     cl,byte ptr [di-1]      ; How much data we have
        sub     cl,' '                  ; remove ascii bias
        jle     srvsen1                 ; le = nothing
        push    cx
        push    di
        call    ctlu                    ; clear the line
        pop     di
        pop     cx
        call    prtscr                  ; Print it on the screen
srvsen1:mov     ah,'Y'                  ; reply with an ack
        mov     pack.datlen,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     trans.chklen,1          ; reply with 1 char checksum
        mov     bx,offset remms10       ; else give a message
        call    errpack                 ; back to local kermit
        call    pktsize                 ; report packet size
        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
        pushf                           ; save carry bit
        mov     ah,close2       ; close the file so we can reread it below
        mov     bx,diskio.handle
        int     dos
        popf                            ; recover carry bit
        jc      srvhlp4                 ; c = write error, tell remote user
                                        ; 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     trans.chklen,1          ; reply with 1 char checksum
        mov     bx,offset remms3        ; else give a message
        call    errpack                 ; back to local kermit
        call    pktsize                 ; report packet size
        jmp     rskp
srvhlp  endp

; srvini - init parms based on init packet
srvini  proc    near
        mov     bx,offset data
        mov     ax,pack.datlen          ; 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.datlen,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
        mov     remlen,0                ; no text required
        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
        mov     remlen,0                ; no text required
        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
        mov     remlen,1                ; text required
        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
        mov     remlen,1                ; text required
        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
        mov     remlen,1                ; text required
        jmp     genric
REMKER  ENDP

; REMDIR - Do a directory

REMDIR  PROC    NEAR
        mov     remcmd,'D'
        mov     rempac,'G'              ; Packet type = generic
        mov     remlen,0                ; no text required
        jmp     genric
REMDIR  ENDP

; REMDEL - Delete a remote file

REMDEL  PROC    NEAR
        mov     remcmd,'E'
        mov     rempac,'G'              ; Packet type = generic
        mov     remlen,1                ; text required
        jmp     genric
REMDEL  ENDP

; REMCWD - Change remote working directory

REMCWD  PROC    NEAR
        mov     remcmd,'C'
        mov     rempac,'G'              ; Packet type = generic
        mov     remlen,0                ; no text required
        jmp     genric
REMCWD  ENDP

; REMLOGIN - LOGIN [username [password [account]]]

REMLOGIN PROC   NEAR
        mov     remcmd,'I'
        mov     rempac,'G'              ; Packet type = generic
        mov     remlen,0                ; no text required
        jmp     genric
REMLOGIN ENDP

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

REMMSG  proc    near
        mov     remcmd,'M'
        mov     rempac,'G'
        mov     remlen,1                ; text required
        jmp     genric
REMMSG  endp

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

REMWHO  proc    near
        mov     remcmd,'W'
        mov     rempac,'G'
        mov     remlen,0                ; no text required
        jmp     genric
REMWHO  endp

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

GENRIC  PROC    NEAR
        mov     bx,offset srvbuf        ; Where to put the text
        mov     temp,bx                 ; where field starts
        cmp     rempac,'C'              ; Remote Host command?
        je      genra                   ; e = yes, no counted string(s)
        cmp     rempac,'K'              ; Remote Kermit command?
        je      genra                   ; e = yes, no counted string(s)
        mov     ah,remcmd               ; get command letter
        mov     [bx],ah                 ; store in buffer
        inc     temp                    ; inc to data field
        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 help
        call    comnd
         jmp    r
         nop
        mov     al,ah                   ; Don't forget the size
        mov     ah,0
        mov     cnt,ax                  ; Save it here
        add     temp,ax                 ; point to next field
        cmp     rempac,'C'              ; Remote Host command?
        je      genra3                  ; e = yes, no counted string(s)
        cmp     rempac,'K'              ; Remote Kermit command?
        je      genra3                  ; e = yes, no counted string(s)
        cmp     al,0                    ; any text?
        je      genra3                  ; e = no
        add     al,32                   ; Do the tochar function
        mov     srvbuf+1,al             ; Size of first field
        inc     temp                    ; include count byte
genra3: cmp     al,remlen               ; got necessary command text?
        jae     genra2                  ; ae = yes
        mov     ah,prstr
        mov     dx,offset ermes1        ; need more info
        int     dos
        or      errlev,2                ; say cannot receive
        or      fsta.xstatus,2          ; set status failed
        mov     kstatus,2               ; global status
        jmp     rskp

genra2: mov     flags.xflg,1            ; output coming to screen
        cmp     rempac,'K'              ; Remote Kermit command?
        je      genr0                   ; e = yes
        cmp     rempac,'C'              ; Remote host command?
        je      genr0                   ; No, skip this part
genrb:
        cmp     remcmd,'C'              ; Change working directory?
        je      genrf                   ; e = yes, ask for password
        cmp     remcmd,'I'              ; remote login command?
        je      genrd                   ; e = yes
        jmp     genr0                   ; neither so no extra prompts here

genrd:  cmp     cnt,0                   ; have user name already?
        jne     genrf                   ; ne = yes
        mov     dx,offset user          ; prompt for username
        call    prompt
        mov     bx,offset srvbuf+1      ; skip command letter
        mov     temp,bx                 ; start of field
        call    input                   ; Read text
        jcxz    genr0                   ; z = none
        mov     temp,bx                 ; point to next data field

genrf:  mov     dx,offset password      ; Get optional password
        call    prompt
        mov     bx,temp                 ; Where to put the password
        mov     comand.cmquiet,1        ; turn on quiet mode
        call    input                   ; Read in the password
        mov     comand.cmquiet,0        ; turn off quiet mode
        jcxz    genr0                   ; z = no text, do not add field
        mov     temp,bx                 ; point to next data field
                                        ;
        cmp     remcmd,'I'              ; remote login command?
        jne     genr0                   ; ne = no
        mov     dx,offset account       ; get optional account ident
        call    prompt
        mov     bx,temp                 ; Where this field starts
        call    input                   ; Read in text
        jcxz    genr0                   ; z = no text, do not add field
        mov     temp,bx                 ; point to next data field
                                        ; All fields completed
genr0:  mov     ax,temp                 ; pointer to next field
        sub     ax,offset srvbuf        ; minus start of buffer = data length
        mov     cnt,ax                  ; remember size here
        cmp     flags.cxzflg,'C'        ; Control-C entered?
        jne     genr0a                  ; ne = no
        ret                             ; return failure

genr0a: mov     kstatus,0               ; global status
        mov     pack.numtry,0           ; Initialize count
        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 erms18        ; Print error msg and fail
        int     dos
genr2x: 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
        mov     fsta.xstatus,4          ; set status
        mov     kstatus,4               ; global status
        jmp     rskp
genr3:  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.datlen,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.seqnum,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.datlen,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.datlen          ; How much data we have
        jcxz    genr31                  ; z = nothing
        mov     ah,prstr
        mov     dx,offset crlf          ; start with cr/lf
        int     dos
        call    prtscr                  ; Print it on the screen
genr31: mov     flags.xflg,0    ; reset screen output flag before leaving
        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
        push    ax
        mov     ah,prstr
        mov     dx,offset crlf          ; for some systems
        int     dos
        pop     ax                      ; keep packet type in ah
        mov     pack.numtry,0
        mov     pack.numrtr,0
        mov     pack.numpkt,0
        mov     pack.pktnum,0
        call    begtim                  ; start next statistics group
        call    bx                      ; Handle it almost like filename
        call    read2                   ; Receive the rest
         jmp    r                       ; Oops, we failed
         nop
        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
        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
        jc      ipk0x                   ; c = failure
        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     ah,dtrans.ebquot     ; default 8 bit quote, needed with parity
        mov     trans.ebquot,ah         ; save as active mode
        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.datlen,ax          ; Save the number of arguments
        mov     pack.seqnum,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.seqnum          ; Is it the right packet number?
        je      ipk2
         jmp    ipk0                    ; If not try again
ipk2:   mov     ax,pack.datlen          ; 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 CX the count of characters read
;         BX the updated pointer to the input buffer
;         input buffer = <ascii data length count byte>textstring
INPUT   PROC    NEAR
        mov     inpbuf,bx               ; Where to put byte count
        inc     bx                      ; skip over count byte
        mov     dx,0                    ; help, none
        mov     ah,cmtxt                ; get text with embedded whitespace
        call    comnd
         jmp    r
         nop
        mov     al,ah                   ; length of text
        mov     ah,0
        mov     cnt,ax                  ; save here
input1: push    bx
        mov     bx,inpbuf
        mov     [bx],al                 ; store count byte
        add     byte ptr [bx],32        ; convert to ascii
        pop     bx                      ; return pointer to next free byte
        mov     cx,cnt                  ; return byte count
        ret
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