;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;    FTPS
;    FTP Server
;
;    Compile with FASM for Menuet
;

; note: telnet == 23, ftp cmd == 21, data on 20

use32

    org     0x0

    db      'MENUET01'              ; 8 byte id
    dd      1                       ; header version
    dd      START                   ; program start
    dd      I_END                   ; program image size
    dd      0x170000                ; required amount of memory
    dd      0x16FFF0                ; esp = 0x16FFF0
    dd      0, 0                    ; no params, no path

include 'macros.inc'
; Various states of client connection
USER_NONE       equ 0   ; Awaiting a connection
USER_CONNECTED  equ 1   ; User just connected, prompt given
USER_USERNAME   equ 2   ; User given username
USER_LOGGED_IN  equ 3   ; User given password





START:                          ; start of execution
    ; Clear the screen memory
    mov     eax, '    '
    mov     edi,text
    mov     ecx,80*30 /4
    cld
    rep     stosd

    call    draw_window

    ; init the receive buffer pointer
    mov     [buffptr], buff

    ; Init FTP server state machine
    mov     [state], USER_NONE

    ; Open the listening socket
    call    connect

still:
    ; check connection status
    mov     eax,53
    mov     ebx,6               ; Get socket status
    mov     ecx,[CmdSocket]
    mcall

    mov     ebx, [CmdSocketStatus]
    mov     [CmdSocketStatus], eax

    cmp     eax, ebx
    je      waitev

    ; If the socket closed by remote host, open it again.
    cmp     eax, 7
    je      con

    ; If socket closed by Reset, open it again
    cmp     eax, 11
    je      con

    ; If a user has just connected, start by outputting welcome msg
    cmp     eax, 4
    jne     noc

    mov     esi, loginStr0
    mov     edx, loginStr0_end - loginStr0
    call    outputStr

    mov     [state], USER_CONNECTED
    jmp     noc


con:
    ; Need to call disconnect, since a remote close does not fully
    ; close the socket
    call    disconnect
    mov     eax,5
    mov     ebx,10          ; Delay for 100ms
    mcall
    call    connect
    jmp     noc

noc:
    ; Display the changed connected status
    call    draw_window

waitev:
    mov     eax,23                 ; wait here for event
    mov     ebx,1                 ; Delay for up to 1s
    mcall

    cmp     eax,1                  ; redraw request ?
    je      red
    cmp     eax,2                  ; key in buffer ?
    je      key
    cmp     eax,3                  ; button in buffer ?
    je      button

    ; any data from the socket?

    mov     eax, 53
    mov     ebx, 2                  ; Get # of bytes in input queue
    mov     ecx, [CmdSocket]
    mcall
    test    eax, eax
    jz      still

read_input:
    mov     eax, 53
    mov     ebx, 3                  ; Get a byte from socket in bl
    mov     ecx, [CmdSocket]
    mcall

    call    ftpRxCmdData            ; process incoming ftp command

    ; Keep processing data until there is no more to process
    mov     eax, 53
    mov     ebx, 2                  ; Get # of bytes in input queue
    mov     ecx, [CmdSocket]
    mcall
    cmp     eax, 0
    jne     read_input

    ; Now redraw the text text field.
    ; Probably not required, since ftp requires no
    ; console i/o.
    ; Leave in for now, for debugging.
; (fall through to "red:")
;    call    draw_text
;    jmp     still

red:                          ; REDRAW WINDOW
    call    draw_window
    jmp     still

key:                          ; KEY
    mov     eax,2                  ; get but ignore
    mcall
    jmp     still

button:
    mov     eax,17
    mcall
    cmp     ah,1
    jne     still

    ; Exit button pressed, so close socket and quit
    mov     eax,53
    mov     ebx,8
    mov     ecx,[CmdSocket]
    mcall

    ; ... terminate program
    or     eax,-1
    mcall
    jmp     still



;   *********************************************
;   *******  WINDOW DEFINITIONS AND DRAW ********
;   *********************************************
draw_window:
    pusha

    mov  eax,12
    mov  ebx,1
    mcall

    xor  eax,eax                   ; DRAW WINDOW
    mov  ebx,100*65536+491 + 8 +15
    mov  ecx,100*65536+270 + 20     ; 20 for status bar
    mov  edx,0x14000000
    mov  edi,labelt
    mcall

    ; draw status bar
    mov     eax, 13
    mov     ebx, 4*65536+484 + 8 +15
    mov     ecx, 270*65536 + 3
    mov     edx, 0x00557799
    mcall


    mov  esi,contlen-contt          ; display connected status
    mov     edx, contt
    cmp     [CmdSocketStatus], 4    ; 4 is connected
    je      pcon
    mov     esi,discontlen-discontt
    mov     edx, discontt
pcon:

    mov  eax,4                      ; status text
    mov  ebx,380*65536+276
    mov  ecx,0x00ffffff
    mcall

    ; Draw the text on the screen, clearing it first
    ; This can go when we loose debuggin info.
    xor  eax,eax
    mov  edi,text+80*30
    mov  ecx,80*30 /4
    cld
    rep  stosd

    call draw_text

    mov  eax,12
    mov  ebx,2
    mcall

    popa

    ret


;***************************************************************************
;   Function
;      draw_text
;
;   Description
;       Updates the text on the screen. This is part of the debugging code
;
;   Inputs
;       Character to add in bl
;
;***************************************************************************
draw_text:

    pusha

    mov  esi,text
    mov  eax,0
    mov  ebx,0
  newletter:
    mov  cl,[esi]
    cmp  cl,[esi+30*80]
    jz   noletter
  yesletter:
    mov  [esi+30*80],cl

    ; erase character

    pusha
    mov     edx, 0                  ; bg colour
    mov     ecx, ebx
    add     ecx, 26
    shl     ecx, 16
    mov     cx, 9
    mov     ebx, eax
    add     ebx, 6
    shl     ebx, 16
    mov     bx, 6
    mov     eax, 13
    mcall
    popa

    ; draw character

    pusha
    mov     ecx, 0x00ffffff
    push bx
    mov  ebx,eax
    add  ebx,6
    shl  ebx,16
    pop  bx
    add  bx,26
    mov  eax,4
    mov  edx,esi
    mov  esi,1
    mcall
    popa

  noletter:

    add  esi,1
    add  eax,6
    cmp  eax,80*6
    jb   newletter
    mov  eax,0
    add  ebx,10
    cmp  ebx,24*10
    jb   newletter

    popa
    ret



;***************************************************************************
;   Function
;      ftpRxCmdData
;
;   Description
;       Prcoesses incoming command data, calling a handler for each command.
;       Commands are built up in buff before being processed.
;
;   Inputs
;       Character to add in bl
;
;***************************************************************************
ftpRxCmdData:
    ; Quit if we are not connected
    ;( This case shouldn't be necessary, but be safe )
    mov     al, [state]
    cmp     al, USER_NONE
    je      frcd_exit

    ; Store the incoming character
    mov     esi, [buffptr]
    mov     [esi], bl
    inc     esi
    mov     [buffptr], esi

    ; For debugging, show the data coming in
    pusha
    call    printChar
    popa

    ; Do we have an end of line? (LF)
    ; if not, just exit
    cmp     bl, 0x0a
    jne     frcd_exit

    ; OK we have a complete command.
    ; Process, and send response

    ; There are a number of states involved in ftp,
    ; to do with logging in.

    mov     al, [state]
    cmp     al, USER_CONNECTED
    jne     fs001

    ; This should be the username

    ; TODO validate username

    ; OK, username accepted - ask for password
    mov     esi, loginStr1
    mov     edx, loginStr1_end - loginStr1
    call    outputStr

    mov     [state], USER_USERNAME

    ; init the receive buffer pointer
    mov     [buffptr], buff

    jmp     frcd_exit

fs001:
    cmp     al, USER_USERNAME
    jne     fs002

    ; This should be the password

    ; TODO validate password

    ; OK, password accepted - show they are logged in
    mov     esi, loginStr2
    mov     edx, loginStr2_end - loginStr2
    call    outputStr

    mov     [state], USER_LOGGED_IN

    ; init the receive buffer pointer
    mov     [buffptr], buff

    jmp     frcd_exit

fs002:
    cmp     al, USER_LOGGED_IN
    jne     fs003

    ; This should be a cmd
    call    findCmd
    mov     eax, [cmdPtr]
    cmp     eax, 0

    je      fs002b

    call    [cmdPtr]

fs002a:
    ; init the receive buffer pointer
    mov     [buffptr], buff

    jmp     frcd_exit

fs002b:
    ; an unsupported command was entered.
    ; Tell user that the command is not supported

    mov     esi, unsupStr
    mov     edx, unsupStr_end - unsupStr
    call    outputStr

    jmp     fs002a

fs003:
frcd_exit:
    ret



;***************************************************************************
;   Function
;      outputStr
;
;   Description
;       Sends a string over the 'Command' socket
;
;   Inputs
;       String in esi
;       Length in edx
;
;***************************************************************************
outputStr:
    push    esi
    push    edx
    mov     eax,53
    mov     ebx,7
    mov     ecx,[CmdSocket]
    mcall
    pop     edx
    pop     esi

    cmp     eax, 0
    je      os_exit

    ; The TCP/IP transmit queue is full; Wait a bit, then retry
    pusha
    mov     eax,5
    mov     ebx,1                 ; Delay for up 10ms
    mcall
    popa
    jmp     outputStr
os_exit:
    ret



;***************************************************************************
;   Function
;      outputDataStr
;
;   Description
;       Sends a string over the 'Data' socket
;
;   Inputs
;       String in esi
;       Length in edx
;
;***************************************************************************
outputDataStr:
    push    esi
    push    edx
    mov     eax,53
    mov     ebx,7
    mov     ecx,[DataSocket]
    mcall
    pop     edx
    pop     esi

    cmp     eax, 0
    je      ods_exit

    ; The TCP/IP transmit queue is full; Wait a bit, then retry
    pusha
    mov     eax,5
    mov     ebx,1                 ; Delay for up 10ms
    mcall
    popa
    jmp     outputDataStr
ods_exit:
    ret



;***************************************************************************
;   Function
;      printChar
;
;   Description
;       Writes a character to the screen; Used to display the data coming
;       in from the user. Really only useful for debugging.
;
;   Inputs
;       Character in bl
;
;***************************************************************************
printChar:
    cmp     bl,13                          ; BEGINNING OF LINE
    jne     nobol
    mov     ecx,[pos]
    add     ecx,1
boll1:
    sub     ecx,1
    mov     eax,ecx
    xor     edx,edx
    mov     ebx,80
    div     ebx
    cmp     edx,0
    jne     boll1
    mov     [pos],ecx
    jmp     newdata
nobol:

    cmp     bl,10                            ; LINE DOWN
    jne     nolf
addx1:
    add     [pos],dword 1
    mov     eax,[pos]
    xor     edx,edx
    mov     ecx,80
    div     ecx
    cmp     edx,0
    jnz     addx1
    mov     eax,[pos]
    jmp     cm1
nolf:

    cmp     bl,8                            ; BACKSPACE
    jne     nobasp
    mov     eax,[pos]
    dec     eax
    mov     [pos],eax
    mov     [eax+text],byte 32
    mov     [eax+text+60*80],byte 0
    jmp     newdata
nobasp:

    cmp     bl,15                           ; CHARACTER
    jbe     newdata
putcha:
    mov     eax,[pos]
    mov     [eax+text],bl
    mov     eax,[pos]
    add     eax,1
cm1:
    mov     ebx,[scroll+4]
    imul    ebx,80
    cmp     eax,ebx
    jb      noeaxz
    mov     esi,text+80
    mov     edi,text
    mov     ecx,ebx
    cld
    rep     movsb
    mov     eax,ebx
    sub     eax,80
noeaxz:
    mov     [pos],eax
newdata:
    ret


;***************************************************************************
;   Function
;      disconnect
;
;   Description
;       Closes the command socket
;
;   Inputs
;       None
;
;***************************************************************************
disconnect:
    mov     eax, 53         ; Stack Interface
    mov     ebx,8           ; Close TCP socket
    mov     ecx,[CmdSocket]
    mcall
    ret



;***************************************************************************
;   Function
;      disconnectData
;
;   Description
;       Closes the data socket
;
;   Inputs
;       None
;
;***************************************************************************
disconnectData:
    ; This delay would be better done by allowing the socket code
    ; to wait for all data to pass through the stack before closing
    pusha
    mov     eax,5
    mov     ebx,10          ; Delay for 100ms
    mcall
    popa

    mov     eax, 53         ; Stack Interface
    mov     ebx,8           ; Close TCP socket
    mov     ecx,[DataSocket]
    mcall
    ret




;***************************************************************************
;   Function
;      connect
;
;   Description
;       Opens the command socket
;
;   Inputs
;       None
;
;***************************************************************************
connect:
    pusha

    mov     eax, 53     ; Stack Interface
    mov     ebx, 5      ; Open TCP socket
    mov     esi, 0      ; No remote IP address
    mov     edx, 0      ; No remote port
    mov     ecx, 21     ; ftp command port id
    mov     edi, 0      ; passive open
    mcall
    mov     [CmdSocket], eax

    popa

    ret



;***************************************************************************
;   Function
;      connectData
;
;   Description
;       Opens the data socket
;
;   Inputs
;       None
;
;***************************************************************************
connectData:
    pusha

    mov     eax, 53     ; Stack Interface
    mov     ebx, 5      ; Open TCP socket
    mov     esi, [DataIP]      ; remote IP address
    mov     edx, [DataPort]    ; remote port
    mov     ecx, 0      ; ftp data port id
    mov     edi, 1      ; active open
    mcall
    mov     [DataSocket], eax

    popa

    ret




;***************************************************************************
;   Function
;      findCmd
;
;   Description
;       Scans the command string for a valid command. The command string
;       is in the global variable buff.
;
;       Returns result in cmdPtr. This will be zero if none found
;
;   Inputs
;       None
;
;***************************************************************************
findCmd:
    ; Setup to return 'none' in cmdPtr, if we find no cmd
    mov     eax, 0
    mov     [cmdPtr], eax
    cld
    mov     esi, buff
    mov     edi, CMDList

fc000:
    cmp     [edi], byte 0   ; Are we at the end of the CMDList?
    je      fc_exit

fc000a:
    cmpsb

    je      fc_nextbyte

    ; Command is different - move to the next entry in cmd table
    mov     esi, buff

fc001:
    ; skip to the next command in the list
    cmp     [edi], byte 0
    je      fc002
    inc     edi
    jmp     fc001
fc002:
    add     edi, 5
    jmp     fc000

fc_nextbyte:
    ; Have we reached the end of the CMD text?
    cmp     [edi], byte 0
    je      fc_got      ; Yes - so we have a match
    jmp     fc000a      ; No - loop back

fc_got:
    ; Copy the function pointer for the selected command
    inc     edi
    mov     eax, [edi]
    mov     [cmdPtr], eax

fc_exit:
    ret



;***************************************************************************
;   Function
;      decStr2Byte
;
;   Description
;       Converts the decimal string pointed to by esi to a byte
;
;   Inputs
;       string ptr in esi
;
;   Outputs
;       esi points to next character not in string
;       eax holds result ( 0..255)
;
;***************************************************************************
decStr2Byte:
    xor     eax, eax
    xor     ebx, ebx
    mov     ecx, 3

dsb001:
    mov     bl, [esi]

    cmp     bl, '0'
    jb      dsb_exit
    cmp     bl, '9'
    ja      dsb_exit

    imul    eax, 10
    add     eax, ebx
    sub     eax, '0'
    inc     esi
    loop    dsb001

dsb_exit:
    ret



;***************************************************************************
;   Function
;      parsePortStr
;
;   Description
;       Converts the parameters of the PORT command, and stores them in the
;       appropriate variables.
;
;   Inputs
;       None ( string in global variable buff )
;
;   Outputs
;       None
;
;***************************************************************************
parsePortStr:
    ; skip past the PORT text to get the the parameters. The command format
    ; is
    ; PORT i,i,i,i,p,p,0x0d,0x0a
    ; where i and p are decimal byte values, high byte first.
    xor     eax, eax
    mov     [DataIP], eax
    mov     [DataPort], eax
    mov     esi, buff + 4       ; Start at first space character

pps001:
    cmp     [esi], byte ' '     ; Look for first non space character
    jne     pps002
    inc     esi
    jmp     pps001

pps002:
    call    decStr2Byte
    add     [DataIP], eax
    ror     dword [DataIP], 8
    inc     esi
    call    decStr2Byte
    add     [DataIP], eax
    ror     dword [DataIP], 8
    inc     esi
    call    decStr2Byte
    add     [DataIP], eax
    ror     dword [DataIP], 8
    inc     esi
    call    decStr2Byte
    add     [DataIP], eax
    ror     dword [DataIP], 8
    inc     esi
    call    decStr2Byte
    add     [DataPort], eax
    shl     [DataPort], 8
    inc     esi
    call    decStr2Byte
    add     [DataPort], eax

    ret



;***************************************************************************
;   Function
;      sendDir
;
;   Description
;       Transmits the directory listing over the data connection.
;       The data connection is already open.
;
;   Inputs
;       None
;
;   Outputs
;       None
;
;***************************************************************************
sendDir:
        mov     eax, text+0x4000
        mov     [fsize], eax
        mov     ebx, dirinfoblock
        and     dword [ebx+4], 0        ; start from zero block
sd001:
; Read the next DirBlocksPerCall (=16) blocks
        mov     eax, 70
        mcall
; Did we read anything?
        test    eax, eax
        jz      @f
        cmp     eax, 6
        jnz     sd_exit
@@:
        test    ebx, ebx
        jz      sd_exit
; Parse these blocks. There could be up to 16 files specified
        mov     esi, text + 0x1300 + 0x20
sd002:
        dec     ebx
        js      sd004
        push    ebx
; OK, lets parse the entry. Ignore volume entries
        test    byte [esi], 8
        jnz     sd003
; Valid file or directory. Start to compose the string we will send
        mov     edi, dirStr
; If we have been called as a result of an NLST command, we only display
; the filename
        cmp     [buff], byte 'N'
        jz      sd006

        mov     [edi], byte '-'
        test    byte [esi], 10h
        jz      @f
        mov     [edi], byte 'd'
@@:
; Ok, now copy across the directory listing text that will be constant
; ( I dont bother looking at the read only or archive bits )

        mov     ebx, tmplStr
@@:
        inc     edi
        mov     al, [ebx]
        test    al, al
        jz      @f
        mov     [edi], al
        inc     ebx
        jmp     @b
@@:
; point to the last character of the string;
; We will write the file size here, backwards
        push    edi             ; Remember where the string ends

        dec     edi
; eax holds the number
        mov     eax, [esi+32]
        mov     ebx, 10
@@:
        xor     edx, edx
        div     ebx
        add     dl, '0'
        mov     [edi], dl
        dec     edi
        test    eax, eax
        jnz     @b

        pop     edi
; now create the time & date fields
;timeStr:            db ' Jan 1    2000 ',0
        mov     al, ' '
        stosb
        movzx   eax, byte [esi+28+1]
        mov     eax, dword [months + (eax-1)*4]
        stosd
        mov     al, byte [esi+28]
        aam
        test    ah, ah
        jz      sd005a
        xchg    al, ah
        add     ax, '00'
        jmp     sd005b
sd005a:
        add     al, '0'
        mov     ah, ' '
sd005b:
        stosw
        mov     al, ' '
        mov     ecx, 6
        rep     stosb
        push    edi
        movzx   eax, word [esi+28+2]
@@:
        xor     edx, edx
        div     ebx
        add     dl, '0'
        mov     [edi], dl
        dec     edi
        test    eax, eax
        jnz     @b
        pop     edi
        inc     edi
        mov     al, ' '
        stosb
sd006:
; ** End of copying

; now copy the filename across
        lea     ebx, [esi+40]
@@:
        mov     al, [ebx]
        inc     ebx
        test    al, al
        jz      @f
        stosb
        jmp     @b
@@:
terminate:
; Now terminate the line by putting CRLF sequence in
        mov     al, 0x0D
        stosb
        mov     al, 0x0A
        stosb
; Send the completed line to the user over data socket
        push    esi
        push    edi
        mov     esi, dirStr
        mov     ecx, edi
        sub     ecx, esi
        mov     edi, [fsize]
        cld
        rep     movsb
        mov     [fsize], edi
        cmp     edi, text+0x4400
        jb      @f
        mov     esi, text+0x4000
        mov     edx, [fsize]
        sub     edx, esi
        mov     [fsize], esi
        call    outputDataStr

@@:
        pop     edi
        pop     esi

sd003:                  ; Move to next entry in the block
        pop     ebx
        add     esi, 304
        jmp     sd002
sd004:
        mov     ebx, dirinfoblock
        add     dword [ebx+4], DirBlocksPerCall
        jmp     sd001

sd_exit:
        mov     esi, text+0x4000
        mov     edx, [fsize]
        sub     edx, esi
        call    outputDataStr
        ret





;***************************************************************************
;   Function
;      setupFilePath
;
;   Description
;       Copies the file name from the input request string into the
;       file descriptor
;
;   Inputs
;       None
;
;   Outputs
;       None
;
;***************************************************************************
setupFilePath:
    mov     esi, buff + 4       ; Point to (1 before) first character of file

    ; Skip any trailing spaces or / character
sfp001:
    inc     esi
    cmp     [esi], byte ' '
    je      sfp001
    cmp     [esi], byte '/'
    je      sfp001

    ; esi points to start of filename.
    cld
    push    esi

    ; Copy across the directory path '/'
    ; into the fileinfoblock
    mov     esi, dirpath
    mov     edi, filename
sfp003:
    movsb
    cmp     [esi], byte 0
    jne     sfp003
    mov     al, '/'
    stosb
    pop     esi

    ; Copy across the filename
sfp002:
    movsb
    cmp     [esi], byte 0x0d
    jne     sfp002
    mov     [edi], byte 0
    ret




;***************************************************************************
;   Function
;      sendFile
;
;   Description
;       Transmits the requested file over the open data socket
;       The file to send is named in the buff string
;
;   Inputs
;       None
;
;   Outputs
;       None
;
;***************************************************************************
sendFile:
    call    setupFilePath

    ; init fileblock descriptor, for file read
    mov     ebx, fileinfoblock
    and     dword [ebx], 0 ; read cmd
    and     dword [ebx+4], 0 ; first block
    mov     dword [ebx+12], 0x400 ; block size

sf002a:
    ; now read the file..
    mov     eax,70
    mcall
    test    eax, eax
    jz      @f
    cmp     eax, 6
    jnz     sf_exit
@@:
    push    eax
    mov     esi, text + 0x1300
    mov     edx, ebx
    call    outputDataStr
    pop     eax
    test    eax, eax
    jnz     sf_exit
; wait a bit
    mov     eax, 5
    mov     ebx, 1
    mcall
    mov     ebx, fileinfoblock
    add     dword [ebx+4], edx
    jmp     sf002a

sf_exit:
    ret


;***************************************************************************
;   Function
;      getFile
;
;   Description
;       Receives the specified file over the open data socket
;       The file to receive is named in the buff string
;
;   Inputs
;       None
;
;   Outputs
;       None
;
;***************************************************************************
getFile:
    call    setupFilePath

    ; init fileblock descriptor, for file write
    xor     eax, eax
    mov     [fsize], eax            ; Start filelength at 0
    mov     [fileinfoblock+4], eax    ; set to 0
    inc     eax
    inc     eax
    mov     [fileinfoblock], eax    ; write cmd

    ; Read data from the socket until the socket closes
    ; loop
    ;   loop
    ;     read byte from socket
    ;     write byte to file buffer
    ;   until no more bytes in socket
    ;   sleep 100ms
    ; until socket no longer connected
    ; write file to ram

gf000:
    mov     eax, 53
    mov     ebx, 2                  ; Get # of bytes in input queue
    mov     ecx, [DataSocket]
    mcall
    test    eax, eax
    je      gf_sleep

    mov     eax, 53
    mov     ebx, 11                 ; Get bytes from socket
    mov     ecx, [DataSocket]
    mov     edx, text + 0x1300
    add     edx, dword [fsize]
    xor     esi, esi
    mcall                           ; returned data len in eax
    add     dword [fsize], eax

    jmp     gf000

gf_sleep:
    ; Check to see if socket closed...
    mov     eax,53
    mov     ebx,6               ; Get socket status
    mov     ecx,[DataSocket]
    mcall

    cmp     eax, 7
    jne     gf001               ; still open, so just sleep a bit

    ; Finished, so write the file
    mov     eax, [fsize]
    mov     [fileinfoblock+12], eax
    mov     eax,70
    mov     ebx,fileinfoblock
    mcall

    ret                         ; Finished

gf001:
    ; wait a bit
    mov     eax,5
    mov     ebx,1               ; Delay for up 10ms
    mcall
    jmp     gf000               ; try for more data





;***************************************************************************
;   COMMAND HANDLERS FOLLOW
;
;   These handlers implement the functionality of each supported FTP Command
;
;***************************************************************************

cmdPWD:
    cld
    mov     edi, text+0x1300

    mov     esi, curdir_1
    mov     ecx, curdir_2-curdir_1
    rep     movsb

    mov     esi, [curdirptr]
    cmp     [esi], byte 0
    jne     cpwd_000
    mov     al, '/'
    stosb
    jmp     cpwd_001

cpwd_000:
    movsb
    cmp     [esi], byte 0
    jne     cpwd_000

cpwd_001:
    mov     esi, curdir_2
    mov     ecx, curdir_end-curdir_2
    rep     movsb

    ; OK, show the directory name text
    mov     esi, text+0x1300
    mov     edx, edi
    sub     edx, esi
    call    outputStr

    ret


cmdCWD:
    lea     esi, [buff+4]
    mov     edi, [curdirptr]
    mov     ecx, -1
    xor     eax, eax
    repne   scasb
    dec     edi
    cmp     [esi], word '..'
    je      ccwd_002
    test    [esi], byte 0xE0
    je      ccwd_000
    push    edi
    mov     al, '/'
    stosb
@@:
    movsb
    cmp     [esi], byte 0xD
    jne     @b
    xor     al, al
    stosb

    mov     eax, 70
    mov     ebx, dirinfoblock
    and     dword [ebx+4], 0
    mcall
    pop     edi
    test    eax, eax
    je      @f
    cmp     eax, 6
    jne     ccwd_000
@@:
    test    ebx, ebx
    je      ccwd_000
    mov     esi, text + 0x1300 + 0x20

    mov     al, byte [esi]
    and     al, 0x18
    cmp     al, 0x10
    jne     ccwd_000

ccwd_ok:
    ; OK, show the directory name text
    mov     esi, chdir
    mov     edx, chdir_end - chdir
    jmp     ccwd_001

ccwd_002:
    dec     edi
    cmp     byte [edi], '/'
    je      ccwd_003
    cmp     edi, [curdirptr]
    ja      ccwd_002
    jmp     ccwd_ok
ccwd_003:
    mov    byte [edi], 0
    jmp     ccwd_ok

ccwd_000:
    mov    byte [edi], 0
    ; Tell user there is no such directory
    mov     esi, noFileStr
    mov     edx, noFileStr_end - noFileStr

ccwd_001:
    call    outputStr

    ret


cmdQUIT:
    ; The remote end will do the close; We just
    ; say goodbye.
    mov     esi, byeStr
    mov     edx, byeStr_end - byeStr
    call    outputStr
    ret


cmdABOR:

    ; Close port
    call    disconnectData

    mov     esi, abortStr
    mov     edx, abortStr_end - abortStr
    call    outputStr

    ret

cmdPORT:
    ; TODO
    ; Copy the IP and port values to DataIP and DataPort

    call    parsePortStr

    ; Indicate the command was accepted
    mov     esi, cmdOKStr
    mov     edx, cmdOKStr_end - cmdOKStr
    call    outputStr
    ret

cmdnoop:
    ; Indicate the command was accepted
    mov     esi, cmdOKStr
    mov     edx, cmdOKStr_end - cmdOKStr
    call    outputStr
    ret


cmdTYPE:
    ; TODO
    ; Note the type field selected - reject if needed.

    ; Indicate the command was accepted
    mov     esi, cmdOKStr
    mov     edx, cmdOKStr_end - cmdOKStr
    call    outputStr
    ret

cmdsyst:
    ; Indicate the system type
    mov     esi, systStr
    mov     edx, systStr_end - systStr
    call    outputStr
    ret


cmdDELE:
    call    setupFilePath

    mov     ebx, fileinfoblock
    mov     dword [ebx], 8
    and     dword [ebx+4], 0
    push    dword [ebx+12]
    push    dword [ebx+16]
    and     dword [ebx+12], 0
    and     dword [ebx+16], 0
    mov     eax, 70
    mcall
    mov     ebx, fileinfoblock
    pop     dword [ebx+16]
    pop     dword [ebx+12]

    test    eax, eax
    jne     cmdDele_err

    mov     esi, delokStr
    mov     edx, delokStr_end - delokStr
    call    outputStr

    jmp     cmdDele_exit

cmdDele_err:
    mov     esi, noFileStr
    mov     edx, noFileStr_end - noFileStr
    call    outputStr


cmdDele_exit:
    ret


cmdNLST:
cmdLIST:
    ; Indicate the command was accepted
    mov     esi, startStr
    mov     edx, startStr_end - startStr
    call    outputStr

    call    connectData

    ; Wait for socket to establish

cl001:
    ; wait a bit
    mov     eax,5
    mov     ebx,10                ; Delay for up 100ms
    mcall

    ; check connection status
    mov     eax,53
    mov     ebx,6               ; Get socket status
    mov     ecx,[DataSocket]
    mcall

    cmp     eax, 4
    jne     cl001

    ; send directory listing
    call    sendDir

    ; Close port
    call    disconnectData

    mov     esi, endStr
    mov     edx, endStr_end - endStr
    call    outputStr
    ret

cmdRETR:
    ; Indicate the command was accepted
    mov     esi, startStr
    mov     edx, startStr_end - startStr
    call    outputStr

    call    connectData

    ; Wait for socket to establish

cr001:
    ; wait a bit
    mov     eax,5
    mov     ebx,10                ; Delay for up 100ms
    mcall

    ; check connection status
    mov     eax,53
    mov     ebx,6               ; Get socket status
    mov     ecx,[DataSocket]
    mcall

    cmp     eax, 4
    jne     cr001

    ; send data to remote user
    call    sendFile

    ; Close port
    call    disconnectData

    mov     esi, endStr
    mov     edx, endStr_end - endStr
    call    outputStr


    ret


cmdSTOR:
    ; Indicate the command was accepted
    mov     esi, storStr
    mov     edx, storStr_end - storStr
    call    outputStr

    call    connectData

    ; Wait for socket to establish

cs001:
    ; wait a bit
    mov     eax,5
    mov     ebx,10                ; Delay for up 100ms
    mcall

    ; check connection status
    mov     eax,53
    mov     ebx,6               ; Get socket status
    mov     ecx,[DataSocket]
    mcall

    cmp     eax, 4
    je      @f
    cmp     eax, 7
    jne     cs001
@@:

    ; get data file from remote user
    call    getFile

    mov     esi, endStr
    mov     edx, endStr_end - endStr
    call    outputStr

    ; Close port
    call    disconnectData

    ret



; DATA AREA

; This is the list of supported commands, and the function to call
; The list end with a NULL.
CMDList:
                    db  'pwd',0
                    dd  cmdPWD

                    db  'PWD',0
                    dd  cmdPWD

                    db  'XPWD',0
                    dd  cmdPWD

                    db  'xpwd',0
                    dd  cmdPWD

                    db  'QUIT',0
                    dd  cmdQUIT

                    db  'quit',0
                    dd  cmdQUIT

                    db  'PORT',0
                    dd  cmdPORT

                    db  'port',0
                    dd  cmdPORT

                    db  'LIST',0
                    dd  cmdLIST

                    db  'list',0
                    dd  cmdLIST

                    db  'NLST',0
                    dd  cmdNLST

                    db  'nlst',0
                    dd  cmdNLST

                    db  'TYPE',0
                    dd  cmdTYPE

                    db  'type',0
                    dd  cmdTYPE

                    db  'syst',0
                    dd  cmdsyst

                    db  'noop',0
                    dd  cmdnoop

                    db  'CWD',0
                    dd  cmdCWD

                    db  'cwd',0
                    dd  cmdCWD

                    db  'RETR',0
                    dd  cmdRETR

                    db  'retr',0
                    dd  cmdRETR

                    db  'DELE',0
                    dd  cmdDELE

                    db  'dele',0
                    dd  cmdDELE

                    db  'stor',0
                    dd  cmdSTOR

                    db  'STOR',0
                    dd  cmdSTOR

                    db  'ABOR',0
                    dd  cmdABOR

                    db  'abor',0
                    dd  cmdABOR

                    db  0xff,0xf4,0xff,0xf2,'ABOR',0
                    dd  cmdABOR

                    db  0


cmdPtr              dd  0
CmdSocket           dd  0x0
CmdSocketStatus     dd  0x0
DataSocket          dd  0x0
DataSocketStatus    dd  0x0
DataPort            dd  0x00
DataIP              dd  0x00
pos                 dd  80 * 1
scroll              dd  1
                    dd  24

labelt              db  'FTP Server v0.1',0
contt               db  'Connected'
contlen:
discontt            db  'Disconnected'
discontlen:

cmdOKStr:           db  '200 Command OK',0x0d,0x0a
cmdOKStr_end:

loginStr0:          db  '220-  Menuet FTP Server v0.1',0x0d,0x0a
                    db  '220 Username and Password required',0x0d,0x0a
loginStr0_end:

loginStr1:          db  '331 Password now required',0x0d,0x0a
loginStr1_end:

loginStr2:          db  '230 You are now logged in.',0x0d,0x0a
loginStr2_end:

byeStr:             db  '221 Bye bye!',0x0d,0x0a
byeStr_end:

systStr:            db  '215 UNIX system type',0x0d,0x0a
systStr_end:

curdir_1:           db  '257 "'
curdir_2:           db  '"',0x0d,0x0a
curdir_end:

chdir:              db  '250 CWD command successful',0x0d,0x0a
chdir_end:

unsupStr:           db  '500 Unsupported command',0x0d,0x0a
unsupStr_end:

noFileStr:          db  '550 No such file',0x0d,0x0a
noFileStr_end:

delokStr:           db  '250 DELE command successful',0x0d,0x0a
delokStr_end:

startStr:           db  '150 Here it comes...',0x0d,0x0a
startStr_end:

storStr:            db  '150 Connecting for STOR',0x0d,0x0a
storStr_end:

endStr:             db  '226 Transfer OK, Closing connection',0x0d,0x0a
endStr_end:

abortStr:           db  '225 Abort successful',0x0d,0x0a
abortStr_end:

 ; This is the buffer used for building up a directory listing line
dirStr:             times 128 db 0

; This is template string used in building up a directory listing line
tmplStr:            db 'rw-rw-rw-    1 0        0                ',0

months:
        dd     'Jan ','Feb ','Mar ','Apr ','May ','Jun '
        dd     'Jul ','Aug ','Sep ','Oct ','Nov ','Dec '

fileinfoblock:
                    dd      0x00
                    dd      0x00
                    dd      0x00
                    dd      0x200         ; bytes to read
                    dd      text + 0x1300 ; data area
filename:           times 256 db 0

; The following lines define data for reading a directory block
DirBlocksPerCall = 16
dirinfoblock:
                    dd      1
                    dd      0x00
                    dd      0x00
                    dd      DirBlocksPerCall
                    dd      text + 0x1300   ; data area
; The 'filename' for a directory listing
dirpath:            db      '/sys'
                    times 252 db 0
curdirptr:          dd      dirpath+4

fsize:              dd      0

state               db  0
buffptr             dd  0
buff:               times 256 db 0  ; Could put this after iend

; Ram use at the end of the application:
; text                  : 2400 bytes for screen memory
; text + 0x1300          : file data area
text:
I_END: