NAME ccsser ; File CCSSER.ASM ;CHINESE ifdef MSDOS include mssser.dat else include ccsser.dat endif 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 mcmsg erms18, cerms18 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 mcmsg filmsg, cfilmsg 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 mcmsg remfnm, cremfnm call prompt mov bx,offset srvbuf ; place for remote filename ; mov dx,offset frem ; the help message mcmsg frem, cfrem 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 mcmsg lclfnm, clclfnm call prompt get3: mov flags.nmoflg,0 ; assume no local override name ; mov bx,offset filhlp mcmsgb filhlp, cfilhlp 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. mcmsg erms18, cerms18 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 mcmsg ermes6, cermes6 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 mcmsgb inthlp, cinthlp 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 mcscr scrser, cscrser call poscur serv1a: mov ah,prstr ; mov dx,offset infms1 ; say now in server mode mcmsg infms1, cinfms1 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 mcscr scrsrm, cscrsrm 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 mcscr scrsrm, cscrsrm 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 mcmsgb remms1, cremms1 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 mov flags.cxzflg,0 ; clear this flag before exiting 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: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) 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 mcmsgb remms1, cremms1 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 mcmsgb remms9, cremms9 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 mcmsgsi byemsg, cbyemsg 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 mcmsgb remms9, cremms9 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 mcmsgb remms4, cremms4 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)" mcmsgb remms5, cremms5 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)" mcmsgb remms8, cremms8 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" mcmsgb remms6, cremms6 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 mcmsgb remms9, cremms9 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)" mcmsgb remms8, cremms8 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 mcmsgb remms9, cremms9 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 mcmsgsi spcmsg2, cspcmsg2 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 mcmsgsi spcmsg2, cspcmsg2 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 mcmsgsi whomsg, cwhomsg 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 test denyflg,sndflg ; is command enabled? jnz srvsen1 ; nz = yes cmp pack.datlen,1 ; Any data in the packet? jbe srvsen1 ; e = no, just ack the message. cmp data,'M' ; Message packet? jne srvsen1 ; ne = no, ack and forget mov data,' ' ; remove the 'M' mov data+1,' ' ; and byte count field call dodec ; Decode data call ctlu ; clear the line mov dx,offset data+2 ; Where the reply is. (skip M and byte cnt) call prtasz ; 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 mcmsgb remms9, cremms9 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 mcmsgb remms10, cremms10 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 mcmsg hlprem, chlprem 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 mcmsgb remms3, cremms3 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 mcmsgb remhlp, cremhlp 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 mcmsg genmsg, cgenmsg 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 mcmsg ermes1, cermes1 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? jne genraa jmp genr0 ; e = yes genraa: cmp rempac,'C' ; Remote host command? jne genrb jmp 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 mcmsg user, cuser 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 mcmsg password, cpassword 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 mcmsg account, caccount 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 mcmsg erms18, cerms18 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 = 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