Subversion Repositories Kolibri OS

Rev

Rev 2585 | Blame | Last modification | View Log | Download | RSS feed

  1.  
  2.  
  3. struct  thread_data
  4.                         rb 1024
  5.         stack           rb 0
  6.  
  7.         home_dir        rb 1024
  8.         work_dir        rb 1024
  9.         fpath           rb 1024*3       ; Will also be used to temporarily store username
  10.  
  11.         type            db ?    ; ASCII/EBDIC/IMAGE/..
  12.         mode            db ?    ; active/passive
  13.         socketnum       dd ?    ; Commands socket
  14.         state           dd ?    ; disconnected/logging in/logged in/..
  15.         passivesocknum  dd ?    ; when in passive mode, this is the listening socket
  16.         datasocketnum   dd ?    ; socket used for data transfers
  17.         permissions     dd ?
  18.         buffer_ptr      dd ?
  19.  
  20.         datasock        sockaddr_in
  21.  
  22.         buffer          rb BUFFERSIZE
  23. ends
  24.  
  25.  
  26. macro sendFTP str {
  27. local .string, .length, .label
  28.         xor     edi, edi
  29.         mcall   send, [edx + thread_data.socketnum], .string, .length
  30.         jmp     @f
  31. .string db str, 13, 10
  32. .length = $ - .string
  33. @@:
  34.  
  35. }
  36.  
  37. ;------------------------------------------------
  38. ; parse_cmd
  39. ;
  40. ; Internal function wich uses the 'commands'
  41. ;  table to call an appropriate cmd_xx function.
  42. ;
  43. ; input: esi = ptr to ascii commands
  44. ;        ecx = number of bytes input
  45. ;        edx = pointer to thread_data structure
  46. ;
  47. ; output: none
  48. ;
  49. ;------------------------------------------------
  50. align 4
  51. parse_cmd:                              ; esi must point to command
  52.  
  53.         cmp     byte [esi], 0x20        ; skip all leading characters
  54.         ja      .ok
  55.         inc     esi
  56.         dec     ecx
  57.         cmp     ecx, 3
  58.         jb      .error
  59.         jmp     parse_cmd
  60.   .ok:
  61.         cmp     byte [esi+3], 0x20
  62.         ja      @f
  63.         mov     byte [esi+3], 0
  64.        @@:
  65.  
  66.         mov     eax, [esi]
  67.         and     eax, not 0x20202020     ; convert to upper case
  68.         mov     edi, commands           ; list of commands to scan
  69.   .scanloop:
  70.         cmp     eax, [edi]
  71.         je      .got_it
  72.  
  73.         add     edi, 4+4*4
  74.         cmp     byte [edi], 0
  75.         jne     .scanloop
  76.  
  77.   .error:
  78.         cmp     [edx + thread_data.state], STATE_ACTIVE
  79.         jb      login_first
  80.         sendFTP "500 Unsupported command"
  81.         ret
  82.  
  83.   .got_it:
  84.         mov     eax, [edx + thread_data.state]
  85.         jmp     dword [edi + 4 + eax]
  86.  
  87.  
  88. align 4
  89. commands:               ; all commands must be in uppercase
  90.  
  91.         dd 'ABOR'
  92.         dd login_first, login_first, login_first, cmdABOR
  93. ;        dd 'ACCT
  94. ;        dd login_fitst, login_first, login_first, cmd_ACCT
  95. ;        dd 'APPE'
  96. ;        dd login_fitst, login_first, login_first, cmd_APPE
  97.         dd 'CDUP'
  98.         dd login_first, login_first, login_first, cmdCDUP
  99.         dd 'CWD'
  100.         dd login_first, login_first, login_first, cmdCWD
  101.         dd 'DELE'
  102.         dd login_first, login_first, login_first, cmdDELE
  103. ;        dd 'HELP'
  104. ;        dd login_fitst, login_first, login_first, cmd_HELP
  105.         dd 'LIST'
  106.         dd login_first, login_first, login_first, cmdLIST
  107. ;        dd 'MDTM'
  108. ;        dd login_fitst, login_first, login_first, cmd_MDTM
  109. ;        dd 'MKD'
  110. ;        dd login_fitst, login_first, login_first, cmd_MKD
  111. ;        dd 'MODE'
  112. ;        dd login_fitst, login_first, login_first, cmd_MODE
  113.         dd 'NLST'
  114.         dd login_first, login_first, login_first, cmdNLST
  115.         dd 'NOOP'
  116.         dd login_first, login_first, login_first, cmdNOOP
  117.         dd 'PASS'
  118.         dd cmdPASS.0,   cmdPASS    , cmdPASS.2,   cmdPASS.3
  119.         dd 'PASV'
  120.         dd login_first, login_first, login_first, cmdPASV
  121.         dd 'PORT'
  122.         dd login_first, login_first, login_first, cmdPORT
  123.         dd 'PWD'
  124.         dd login_first, login_first, login_first, cmdPWD
  125.         dd 'QUIT'
  126.         dd cmdQUIT,     cmdQUIT,     cmdQUIT,     cmdQUIT
  127. ;        dd 'REIN'
  128. ;        dd login_fitst, login_first, login_first, cmd_REIN
  129. ;        dd 'REST'
  130. ;        dd login_fitst, login_first, login_first, cmd_REST
  131.         dd 'RETR'
  132.         dd login_first, login_first, login_first, cmdRETR
  133. ;        dd 'RMD'
  134. ;        dd login_fitst, login_first, login_first, cmd_RMD
  135. ;        dd 'RNFR'
  136. ;        dd login_fitst, login_first, login_first, cmd_RNFR
  137. ;        dd 'RNTO'
  138. ;        dd login_fitst, login_first, login_first, cmd_RNTO
  139. ;        dd 'SITE'
  140. ;        dd login_fitst, login_first, login_first, cmd_SITE
  141. ;        dd 'SIZE'
  142. ;        dd login_fitst, login_first, login_first, cmd_SIZE
  143. ;        dd 'STAT'
  144. ;        dd login_fitst, login_first, login_first, cmd_STAT
  145.         dd 'STOR'
  146.         dd login_first, login_first, login_first, cmdSTOR
  147. ;        dd 'STOU'
  148. ;        dd login_fitst, login_first, login_first, cmd_STOU
  149. ;        dd 'STRU'
  150. ;        dd login_fitst, login_first, login_first, cmd_STRU
  151.         dd 'SYST'
  152.         dd login_first, login_first, login_first, cmdSYST
  153.         dd 'TYPE'
  154.         dd login_first, login_first, login_first, cmdTYPE
  155.         dd 'USER'
  156.         dd cmdUSER,     cmdUSER,     cmdUSER,     cmdUSER.2
  157.         db 0                                                    ; end marker
  158.  
  159. align 4
  160. login_first:
  161.         sendFTP "530 Please login with USER and PASS"
  162.         ret
  163.  
  164. align 4
  165. permission_denied:
  166.         sendFTP "550 Permission denied"
  167.         ret
  168.  
  169. align 4
  170. socketerror:
  171.         pushd   0x0c
  172.         call    [con_set_flags]
  173.         push    str_sockerr
  174.         call    [con_write_asciiz]
  175.         pushd   0x07
  176.         call    [con_set_flags]
  177.  
  178.         mov     edx, [ebp]
  179.         sendFTP "425 Can't open data connection"
  180.         ret
  181.  
  182. align 4
  183. abort_transfer:
  184.         and     [edx + thread_data.permissions], not ABORT
  185.         mov     [edx + thread_data.mode], MODE_NOTREADY
  186.         push    ebx
  187.         call    [file.close]
  188.         mcall   close, [edx + thread_data.datasocketnum]
  189.         mov     edx, [ebp]
  190.         sendFTP "530 Transfer aborted"
  191.         ret
  192.  
  193. align 4
  194. ip_to_dword:    ; esi = ptr to str, cl = separator ('.', ',')
  195.  
  196.         call    ascii_to_byte
  197.         mov     bh, al
  198.         cmp     byte [esi], cl
  199.         jne     .err
  200.  
  201.         call    ascii_to_byte
  202.         mov     bh, al
  203.         cmp     byte [esi], cl
  204.         jne     .err
  205.         shl     ebx, 16
  206.  
  207.         call    ascii_to_byte
  208.         mov     bh, al
  209.         cmp     byte [esi], cl
  210.         jne     .err
  211.  
  212.         call    ascii_to_byte
  213.         mov     bh, al
  214.  
  215.         ror     ebx, 16
  216.         ret
  217.  
  218.   .err:
  219.         xor     ebx, ebx
  220.         ret
  221.  
  222. align 4         ; esi = ptr to str, output in eax
  223. ascii_to_byte:
  224.  
  225.         xor     eax, eax
  226.         push    ebx
  227.  
  228.   .loop:
  229.         movzx   ebx, byte[esi]
  230.         sub     bl, '0'
  231.         jb      .done
  232.         cmp     bl, 9
  233.         ja      .done
  234.         lea     eax, [eax*4 + eax]      ;
  235.         shl     eax, 1                  ; eax = eax * 10
  236.         add     eax, ebx
  237.         inc     esi
  238.  
  239.         jmp     .loop
  240.  
  241.   .done:
  242.         pop     ebx
  243.         ret
  244.  
  245. align 4
  246. dword_to_ascii: ; edi = ptr where to write, eax is number
  247.  
  248.         push    edx ebx ecx
  249.         mov     ebx, 10
  250.         xor     ecx, ecx
  251.  
  252.        @@:
  253.         xor     edx, edx
  254.         div     ebx
  255.         add     edx, '0'
  256.         pushw   dx
  257.         inc     ecx
  258.         test    eax, eax
  259.         jnz     @r
  260.  
  261.        @@:
  262.         popw    ax
  263.         stosb
  264.         dec     ecx
  265.         jnz     @r
  266.  
  267.         pop     ecx ebx edx
  268.         ret
  269.  
  270. align 4
  271. create_path:            ; combine home_dir and work_dir strings into fpath
  272.  
  273.         mov     edx, [ebp]
  274.         lea     edi, [edx + thread_data.fpath]
  275.         lea     esi, [edx + thread_data.home_dir]
  276.         mov     ecx, 1024
  277.   .loop1:
  278.         lodsb
  279.         or      al, al
  280.         jz      .next
  281.         stosb
  282.         loop    .loop1
  283.   .next:
  284.  
  285.         cmp     byte[edi-1], '/'
  286.         jne     @f
  287.         dec     edi
  288.        @@:
  289.  
  290.         lea     esi, [edx + thread_data.work_dir]
  291.         mov     ecx, 1024
  292.   .loop2:
  293.         lodsb
  294.         or      al, al
  295.         jz      .done
  296.         stosb
  297.         loop    .loop2
  298.  
  299.   .done:
  300.         stosb
  301.         ret
  302.  
  303. ;------------------------------------------------
  304. ; "ABOR"
  305. ;
  306. ; This command aborts the current filetransfer.
  307. ;
  308. ;------------------------------------------------
  309. align 4
  310. cmdABOR:
  311.  
  312.         or      [edx + thread_data.permissions], ABORT
  313.         sendFTP "250 Command succesul"
  314.         ret
  315.  
  316. ;------------------------------------------------
  317. ; "CDUP"
  318. ;
  319. ; Change the directory to move up one level.
  320. ;
  321. ;------------------------------------------------
  322. align 4
  323. cmdCDUP:
  324.  
  325.         test    [edx + thread_data.permissions], PERMISSION_CD
  326.         jz      permission_denied
  327.  
  328.         cmp     byte [edx + thread_data.work_dir+1], 0                          ; are we in "/" ?
  329.         je      .done
  330.  
  331.         mov     ecx, 1024
  332.         xor     al, al
  333.         lea     edi, [edx + thread_data.work_dir]
  334.         repne   scasb
  335.         std
  336.         dec     edi
  337.         dec     edi
  338.         dec     edi
  339.         mov     al,'/'
  340.         repne   scasb
  341.         cld
  342.         mov     byte[edi+1], 0
  343.  
  344.   .done:
  345. ; Print the new working dir on the console
  346.         lea     eax, [edx + thread_data.work_dir]
  347.         push    eax
  348.         call    [con_write_asciiz]
  349.         push    str_newline
  350.         call    [con_write_asciiz]
  351.  
  352.         sendFTP "250 Command succesul"
  353.         ret
  354.  
  355. ;------------------------------------------------
  356. ; "CWD"
  357. ;
  358. ; Change Working Directory.
  359. ;
  360. ;------------------------------------------------
  361. align 4
  362. cmdCWD:
  363.  
  364.         test    [edx + thread_data.permissions], PERMISSION_CD
  365.         jz      permission_denied
  366.  
  367.         sub     ecx, 4
  368.         jb      .err
  369.         add     esi, 4
  370.  
  371.   .scan:
  372.         lea     edi, [edx + thread_data.work_dir + 1]
  373.         push    ecx
  374.         mov     ecx, 1024
  375.   .find_zero:
  376.         cmp     byte [edi], 0
  377.         je      .found_zero
  378.         inc     edi
  379.         loop    .find_zero
  380.   .found_zero:
  381.         pop     ecx
  382.   .scan2:
  383.  
  384.         cmp     byte [esi], '/'
  385.         jne     @f
  386.         inc     esi
  387.         dec     ecx
  388.         jz      .done
  389.        @@:
  390.  
  391.   .loop:
  392.         lodsb
  393.         cmp     al, 0x20
  394.         jb      .done
  395.         cmp     al, '.'
  396.         je      .up
  397.   .continue:
  398.         stosb
  399.         loop    .loop
  400.   .done:
  401.         cmp     byte [edi-1], '/'
  402.         je      @f
  403.         mov     byte [edi], '/'
  404.         inc     edi
  405.        @@:
  406.         mov     byte [edi], 0
  407.  
  408. ; Print the new working dir on the console
  409.         lea     eax, [edx + thread_data.work_dir]
  410.         push    eax
  411.         call    [con_write_asciiz]
  412.         push    str_newline
  413.         call    [con_write_asciiz]
  414.  
  415.         sendFTP "250 Command succesful"
  416.         ret
  417.  
  418.   .up:
  419.         lodsb
  420.         cmp     al, '.'
  421.         jne     .continue
  422.  
  423. ;;;;        TODO: find second last '\' in work_dir and make next char zero
  424. ;;;;        point edi to that 0
  425.  
  426.         jmp     .scan2
  427.  
  428.   .err:
  429.         sendFTP "550 Directory does not exist"
  430.         ret
  431.  
  432. ;------------------------------------------------
  433. ; "DELE"
  434. ;
  435. ; Delete a file from the server.
  436. ;
  437. ;------------------------------------------------
  438. align 4
  439. cmdDELE:
  440.  
  441.         test    [edx + thread_data.permissions], PERMISSION_DELETE
  442.         jz      permission_denied
  443.  
  444.         ret
  445.  
  446. ;------------------------------------------------
  447. ; "LIST"
  448. ;
  449. ; List the files in the current working directory.
  450. ;
  451. ;------------------------------------------------
  452. align 4
  453. cmdLIST:
  454.  
  455.         test    [edx + thread_data.permissions], PERMISSION_EXEC
  456.         jz      permission_denied
  457.  
  458. ; If we are in active mode, it's time to open a data socket..
  459.         cmp     [edx + thread_data.mode], MODE_ACTIVE
  460.         jne     @f
  461.         mov     ecx, [edx + thread_data.datasocketnum]
  462.         lea     edx, [edx + thread_data.datasock]
  463.         mov     esi, sizeof.thread_data.datasock
  464.         mcall   connect
  465.         cmp     eax, -1
  466.         je      socketerror
  467.   @@:
  468.  
  469. ; Create fpath from home_dir and work_dir
  470.         call    create_path
  471.  
  472.         lea     eax, [edx + thread_data.fpath]
  473.         push    eax
  474.         call    [con_write_asciiz]
  475.         push    str_newline
  476.         call    [con_write_asciiz]
  477.  
  478. ; Start the search
  479.         mov     edx, [ebp]
  480.         push    FA_ANY
  481.         push    str_mask
  482.         lea     eax, [edx + thread_data.fpath]
  483.         push    eax
  484.         call    [file.find.first]
  485.  
  486.         test    eax, eax
  487.         jz      .nosuchdir
  488.  
  489.         mov     edx, [ebp]
  490.         lea     edi, [edx + thread_data.buffer]
  491.  
  492.   .parse_file:
  493.         test    eax, eax        ; did we find a file?
  494.         jz      .done
  495.         mov     ebx, eax        ; yes, save the descripter in ebx
  496.  
  497. ; first, convert the attributes
  498.         test    [ebx + FileInfoA.Attributes], FA_FOLDER
  499.         jnz     .folder
  500.  
  501.         test    [ebx + FileInfoA.Attributes], FA_READONLY
  502.         jnz     .readonly
  503.  
  504.         mov     eax, '-rw-'
  505.         stosd
  506.         jmp     .attr
  507.  
  508.   .folder:
  509.         mov     eax, 'drwx'
  510.         stosd
  511.         jmp     .attr
  512.  
  513.   .readonly:
  514.         mov     eax, '-r--'
  515.         stosd
  516.  
  517.   .attr:
  518.         mov     eax, 'rw-r'
  519.         stosd
  520.         mov     ax, 'w-'
  521.         stosw
  522.         mov     al, ' '
  523.         stosb
  524.  
  525. ; now..
  526.         mov     ax, '1 '
  527.         stosw
  528.  
  529. ; now write owner, everything is owned by FTP, woohoo!
  530.         mov     eax, 'FTP '
  531.         stosd
  532.         stosd
  533.  
  534. ; now the filesize in ascii
  535.         mov     eax, [ebx + FileInfoA.FileSizeLow]
  536.         call    dword_to_ascii
  537.  
  538.         mov     al, ' '
  539.         stosb
  540.  
  541. ; then date (month/day/year)
  542.         movzx   eax, [ebx + FileInfoA.DateModify + FileDateTime.month]
  543.         mov     eax, [months + 4*eax]
  544.         stosd
  545.  
  546.         movzx   eax, [ebx + FileInfoA.DateModify + FileDateTime.day]
  547.         call    dword_to_ascii
  548.  
  549.         mov     al, ' '
  550.         stosb
  551.  
  552.         movzx   eax, [ebx + FileInfoA.DateModify + FileDateTime.year]
  553.         call    dword_to_ascii
  554.  
  555.         mov     al, ' '
  556.         stosb
  557.  
  558. ; and last but not least, filename
  559.         lea     esi, [ebx + FileInfoA.FileName]
  560.         mov     ecx, 264
  561.   .nameloop:
  562.         lodsb
  563.         test    al, al
  564.         jz      .namedone
  565.         stosb
  566.         loop    .nameloop
  567.  
  568. ; insert a cr lf
  569.   .namedone:
  570.         mov     ax, 0x0a0d
  571.         stosw
  572.  
  573.         test    [edx + thread_data.permissions], ABORT
  574. ;;;        jnz     .abort
  575.  
  576. ; check next file
  577.         push    ebx
  578.         call    [file.find.next]
  579.         jmp     .parse_file
  580.  
  581. ; close file desc
  582.   .done:
  583.         push    eax                             ; file discriptor is still in eax at this point!
  584.         call    [file.find.close]
  585.  
  586. ; append the string with a 0
  587.         xor     al, al
  588.         stosb
  589.  
  590. ; Warn the client we're about to send the data
  591.         mov     edx, [ebp]
  592.         push    edi
  593.         sendFTP "150 Here it comes.."
  594.         pop     esi
  595.  
  596. ; and send it to the client
  597.         mov     edx, [ebp]
  598.         mov     ecx, [edx + thread_data.datasocketnum]
  599.         lea     edx, [edx + thread_data.buffer]
  600.         sub     esi, edx
  601.         xor     edi, edi
  602.         mcall   send
  603.  
  604. ; close the data socket..
  605.         mov     edx, [ebp]                                      ; thread_data pointer
  606.         mcall   close, [edx + thread_data.datasocketnum]
  607.         mov     [edx + thread_data.mode], MODE_NOTREADY
  608.  
  609.         sendFTP "226 Transfer OK"
  610.         ret
  611.  
  612.   .nosuchdir:
  613.         sendFTP "550 Directory does not exist"
  614.         ret
  615.  
  616. ;------------------------------------------------
  617. ; "NLST"
  618. ;
  619. ; List the filenames of the files in the current working directory.
  620. ;
  621. ;------------------------------------------------
  622. align 4
  623. cmdNLST:
  624.  
  625.         test    [edx + thread_data.permissions], PERMISSION_EXEC
  626.         jz      permission_denied
  627.  
  628.         ; TODO: same as list but simpler output format
  629.  
  630.         ret
  631.  
  632. ;------------------------------------------------
  633. ; "NOOP"
  634. ;
  635. ; No operation, just keep the connection alive.
  636. ;
  637. ;------------------------------------------------
  638. align 4
  639. cmdNOOP:
  640.  
  641.         ret
  642.  
  643. ;------------------------------------------------
  644. ; "PASS"
  645. ;
  646. ; Second phase of login process, client provides password.
  647. ;
  648. ;------------------------------------------------
  649. align 4
  650. cmdPASS:
  651.         lea     esi, [esi + 5]
  652.         lea     edi, [edx + thread_data.buffer + 512]           ; temp pass
  653.         lea     eax, [edx + thread_data.fpath]                  ; temp username
  654.         invoke  ini.get_str, path2, eax, str_pass, edi, 512
  655.         test    eax, eax
  656.         jnz     .incorrect
  657.  
  658.         repe    cmpsb
  659.  
  660.         cmp     byte [esi], 0x20
  661.         jae     .incorrect
  662.  
  663.         cmp     byte [edi], 0
  664.         jne     .incorrect
  665.  
  666.   .pass_ok:
  667.         lea     eax, [edx + thread_data.fpath]
  668.         invoke  ini.get_int, path2, eax, str_mode, 0
  669.         mov     [edx + thread_data.permissions], eax
  670.  
  671.         push    str_pass_ok
  672.         call    [con_write_asciiz]
  673.  
  674.         mov     edx, [ebp]                                      ; thread_data pointer
  675.         mov     [edx + thread_data.state], STATE_ACTIVE
  676.         sendFTP "230 You are now logged in"
  677.         ret
  678.  
  679.   .2:
  680.   .incorrect:
  681.         mov     [edx + thread_data.state], STATE_CONNECTED
  682.         sendFTP "530 Login incorrect"
  683.         ret
  684.  
  685. align 4
  686.   .0:
  687.         sendFTP "503 Login with USER first"
  688.         ret
  689.  
  690. align 4
  691.   .3:
  692.         sendFTP "230 Already logged in"
  693.         ret
  694.  
  695. ;------------------------------------------------
  696. ; "PASV"
  697. ;
  698. ; Initiate a passive dataconnection.
  699. ;
  700. ;------------------------------------------------
  701. align 4
  702. cmdPASV:
  703.  
  704. ; Open a new TCP socket
  705.         mcall   socket, AF_INET4, SOCK_STREAM, 0
  706.         cmp     eax, -1
  707.         je      socketerror
  708.         mov     edx, [ebp]                                      ; thread_data pointer
  709.         mov     [edx + thread_data.passivesocknum], eax
  710.  
  711. ; Bind it to a known local port
  712.         mov     [edx + thread_data.datasock.sin_family], AF_INET4
  713.         mov     [edx + thread_data.datasock.sin_port], 2000
  714.         mov     [edx + thread_data.datasock.sin_addr], 0
  715.  
  716.         mov     ecx, eax                                        ; passivesocketnum
  717.         lea     edx, [edx + thread_data.datasock]
  718.         mov     esi, sizeof.thread_data.datasock
  719.         mcall   bind
  720.         cmp     eax, -1
  721. ;        je      bind_err
  722.  
  723. ; And set it to listen!
  724.         mcall   listen, , 1
  725.         cmp     eax, -1
  726. ;        je      listen_err
  727.  
  728. ; Tell our thread we are ready to accept incoming calls
  729.         mov     edx, [ebp]                                      ; thread_data pointer
  730.         mov     [edx + thread_data.mode], MODE_PASSIVE_WAIT
  731.  
  732. ; Now tell the client where to connect to in this format:
  733. ; 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
  734. ; where a1.a2.a3.a4 is the IP address and p1*256+p2 is the port number.
  735.  
  736. ; '227 ('
  737.         lea     edi, [edx + thread_data.buffer]
  738.         mov     eax, '227 '     ; FIXME (now hardcoded to 127.0.0.1:2000)
  739.         stosd
  740.         mov     al, '('
  741.         stosb
  742. ; ip
  743.         mov     eax, 127
  744.         call    dword_to_ascii
  745.         mov     al, ','
  746.         stosb
  747.         mov     eax, 0
  748.         call    dword_to_ascii
  749.         mov     al, ','
  750.         stosb
  751.         mov     eax, 0
  752.         call    dword_to_ascii
  753.         mov     al, ','
  754.         stosb
  755.         mov     eax, 1
  756.         call    dword_to_ascii
  757.         mov     al, ','
  758.         stosb
  759. ; port
  760.         mov     eax, 7
  761.         call    dword_to_ascii
  762.         mov     al, ','
  763.         stosb
  764.         mov     eax, 208
  765.         call    dword_to_ascii
  766. ; ')', 13, 10, 0
  767.         mov     eax, ')' + 0x000a0d00
  768.         stosd
  769.  
  770.         lea     esi, [edi - thread_data.buffer]
  771.         sub     esi, edx
  772.         mov     ecx, [edx + thread_data.socketnum]
  773.         lea     edx, [edx + thread_data.buffer]
  774.         xor     esi, esi
  775.         mcall   send
  776.  
  777.         ret
  778.  
  779. ;------------------------------------------------
  780. ; "PWD"
  781. ;
  782. ; Print the current working directory.
  783. ;
  784. ;------------------------------------------------
  785. align 4
  786. cmdPWD:
  787.  
  788.         mov     dword [edx + thread_data.buffer], '257 '
  789.         mov     byte [edx + thread_data.buffer+4], '"'
  790.  
  791.         lea     edi, [edx + thread_data.buffer+5]
  792.         lea     esi, [edx + thread_data.work_dir]
  793.         mov     ecx, 1024
  794.   .loop:
  795.         lodsb
  796.         or      al, al
  797.         jz      .ok
  798.         stosb
  799.         dec     ecx
  800.         jnz     .loop
  801.  
  802.   .ok:
  803.         mov     dword [edi], '"' + 0x000a0d00    ; '"',13,10,0
  804.         lea     esi, [edi - thread_data.buffer + 4]
  805.         sub     esi, edx
  806.         mov     ecx, [edx + thread_data.socketnum]
  807.         lea     edx, [edx + thread_data.buffer]
  808.         xor     edi, edi
  809.         mcall   send
  810.  
  811.         mov     edx, [ebp]
  812. ; Print the new working dir on the console
  813.         lea     eax, [edx + thread_data.work_dir]
  814.         push    eax
  815.         call    [con_write_asciiz]
  816.         push    str_newline
  817.         call    [con_write_asciiz]
  818.  
  819.         ret
  820.  
  821. ;------------------------------------------------
  822. ; "PORT"
  823. ;
  824. ; Initiate an active dataconnection.
  825. ;
  826. ;------------------------------------------------
  827. align 4
  828. cmdPORT:
  829.  
  830. ; PORT a1,a2,a3,a4,p1,p2
  831. ; IP address a1.a2.a3.a4, port p1*256+p2
  832.  
  833. ; Convert the IP
  834.         lea     esi, [esi+5]
  835.         mov     cl, ','
  836.         call    ip_to_dword
  837. ; And put it in datasock
  838.         mov     edx, [ebp]
  839.         mov     [edx + thread_data.datasock.sin_addr], ebx
  840.  
  841. ; Now the same with portnumber
  842.         call    ascii_to_byte
  843.         mov     bh, al
  844.         inc     esi
  845.         call    ascii_to_byte
  846.         mov     bl, al
  847.  
  848. ; Save it in datasock too
  849.         mov     [edx + thread_data.datasock.sin_port], bx
  850.  
  851. ; We will open the socket, but do not connect yet!
  852.         mov     [edx + thread_data.datasock.sin_family], AF_INET4
  853.         mcall   socket, AF_INET4, SOCK_STREAM, 0
  854.         cmp     eax, -1
  855.         je      socketerror
  856.  
  857.         mov     edx, [ebp]                                      ; thread_data pointer
  858.         mov     [edx + thread_data.datasocketnum], eax
  859.         mov     [edx + thread_data.mode], MODE_ACTIVE
  860.  
  861.         sendFTP "225 Data connection open"
  862.         ret
  863.  
  864. ;------------------------------------------------
  865. ; "QUIT"
  866. ;
  867. ; Close the connection with client.
  868. ;
  869. ;------------------------------------------------
  870. align 4
  871. cmdQUIT:
  872.  
  873.         mcall   close, [edx + thread_data.datasocketnum]
  874.  
  875.         sendFTP "221 Bye!"
  876.         mcall   close, [edx + thread_data.socketnum]
  877.  
  878.         add     esp, 4          ; get rid of call return address
  879.         jmp     thread_exit     ; now close this thread
  880.  
  881.  
  882. ;------------------------------------------------
  883. ; "RETR"
  884. ;
  885. ; Retrieve a file from the ftp server.
  886. ;
  887. ;------------------------------------------------
  888. align 4
  889. cmdRETR:
  890.  
  891.         test    [edx + thread_data.permissions], PERMISSION_READ
  892.         jz      permission_denied
  893.  
  894.         sub     ecx, 5
  895.         jb      .cannot_open
  896.  
  897.         cmp     [edx + thread_data.mode], MODE_ACTIVE
  898.         jne     @f
  899.         push    esi
  900.         mov     ecx, [edx + thread_data.datasocketnum]
  901.         lea     edx, [edx + thread_data.datasock]
  902.         mov     esi, sizeof.thread_data.datasock
  903.         mcall   connect
  904.         pop     esi
  905.         cmp     eax, -1
  906.         je      socketerror
  907.   @@:
  908.  
  909.         push    esi
  910.         call    create_path
  911.         pop     esi
  912.         dec     edi
  913.         add     esi, 5
  914.         mov     ecx, 1024
  915.   .loop:
  916.         lodsb
  917.         cmp     al, 0x20
  918.         jl      .done
  919.         stosb
  920.         loop    .loop
  921.   .done:
  922.         xor     al, al
  923.         stosb
  924.  
  925.         lea     eax, [edx + thread_data.fpath]
  926.         push    eax
  927.         call    [con_write_asciiz]
  928.         push    str_newline
  929.         call    [con_write_asciiz]
  930.  
  931.         mov     edx, [ebp]
  932.         push    O_READ
  933.         lea     eax, [edx + thread_data.fpath]
  934.         push    eax
  935.         call    [file.open]
  936.         test    eax, eax
  937.         jz      .cannot_open
  938.  
  939.         mov     edx, [ebp]
  940.         push    eax
  941.         sendFTP "150 Here it comes.."
  942.         pop     ebx
  943.  
  944.   .read_more:
  945.         mov     edx, [ebp]
  946.         test    [edx + thread_data.permissions], ABORT
  947.         jnz     abort_transfer
  948.  
  949.         push    BUFFERSIZE
  950.         lea     eax, [edx + thread_data.buffer]
  951.         push    eax
  952.         push    ebx
  953.         call    [file.read]
  954.         cmp     eax, -1
  955.         je      .cannot_open    ; fixme: this is not the correct error
  956.  
  957.         mov     edx, [ebp]
  958.         push    eax ebx
  959.         mov     esi, eax
  960.         mov     ecx, [edx + thread_data.datasocketnum]
  961.         lea     edx, [edx + thread_data.buffer]
  962.         xor     esi, esi
  963.         mcall   send
  964.         pop     ebx ecx
  965.         mov     edx, [ebp]                                      ; thread_data pointer
  966.         cmp     eax, -1
  967.         je      socketerror
  968.  
  969.         cmp     ecx, BUFFERSIZE
  970.         je      .read_more
  971.  
  972.         mcall   close, [edx + thread_data.datasocketnum]
  973.         mov     [edx + thread_data.mode], MODE_NOTREADY
  974.  
  975.         push    ebx
  976.         call    [file.close]
  977.  
  978.         sendFTP "226 Transfer OK, closing connection"
  979.         ret
  980.  
  981.   .cannot_open:
  982.         pushd   0x0c
  983.         call    [con_set_flags]
  984.         push    str_notfound
  985.         call    [con_write_asciiz]
  986.         pushd   0x07
  987.         call    [con_set_flags]
  988.  
  989.         mov     edx, [ebp]
  990.         sendFTP "550 No such file"
  991.         ret
  992.  
  993.  
  994.  
  995. ;------------------------------------------------
  996. ; "STOR"
  997. ;
  998. ; Store a file on the server.
  999. ;
  1000. ;------------------------------------------------
  1001. align 4
  1002. cmdSTOR:
  1003.  
  1004.         test    [edx + thread_data.permissions], PERMISSION_WRITE
  1005.         jz      permission_denied
  1006.  
  1007.  
  1008. ;;;;
  1009.         test    [edx + thread_data.permissions], ABORT
  1010.         jnz     abort_transfer
  1011.  
  1012. ;;;;
  1013.  
  1014.         ret
  1015.  
  1016. ;------------------------------------------------
  1017. ; "SYST"
  1018. ;
  1019. ; Send information about the system.
  1020. ;
  1021. ;------------------------------------------------
  1022. align 4
  1023. cmdSYST:
  1024.  
  1025.         sendFTP "215 UNIX type: L8"
  1026.         ret
  1027.  
  1028. ;------------------------------------------------
  1029. ; "TYPE"
  1030. ;
  1031. ; Choose the file transfer type.
  1032. ;
  1033. ;------------------------------------------------
  1034. align 4
  1035. cmdTYPE:
  1036.  
  1037.         cmp     ecx, 6
  1038.         jb      parse_cmd.error
  1039.  
  1040.         mov     al, byte[esi+5]
  1041.         and     al, not 0x20
  1042.  
  1043.         cmp     al, 'A'
  1044.         je      .ascii
  1045.         cmp     al, 'E'
  1046.         je      .ebdic
  1047.         cmp     al, 'I'
  1048.         je      .image
  1049.         cmp     al, 'L'
  1050.         je      .local
  1051.  
  1052.         jmp     parse_cmd.error
  1053.  
  1054.   .ascii:
  1055.         mov     [edx + thread_data.type], TYPE_ASCII
  1056.         jmp     .subtype
  1057.  
  1058.   .ebdic:
  1059.         mov     [edx + thread_data.type], TYPE_EBDIC
  1060.  
  1061.   .subtype:
  1062.         cmp     ecx, 8
  1063.         jb      .non_print
  1064.  
  1065.         mov     al, byte[esi+7]
  1066.         and     al, not 0x20
  1067.  
  1068.         cmp     al, 'N'
  1069.         je      .non_print
  1070.         cmp     al, 'T'
  1071.         je      .telnet
  1072.         cmp     al, 'C'
  1073.         je      .asacc
  1074.  
  1075.         jmp     parse_cmd.error
  1076.  
  1077.   .non_print:
  1078.         or      [edx + thread_data.type], TYPE_NP
  1079.         jmp     .ok
  1080.  
  1081.   .telnet:
  1082.         or      [edx + thread_data.type], TYPE_TELNET
  1083.         jmp     .ok
  1084.  
  1085.   .asacc:
  1086.         or      [edx + thread_data.type], TYPE_ASA
  1087.         jmp     .ok
  1088.  
  1089.   .image:
  1090.         mov     [edx + thread_data.type], TYPE_IMAGE
  1091.         jmp     .ok
  1092.  
  1093.   .local:
  1094.         cmp     ecx, 8
  1095.         jb      parse_cmd.error
  1096.  
  1097.         mov     al, byte[esi+7]
  1098.         sub     al, '0'
  1099.         jb      parse_cmd.error
  1100.         cmp     al, 9
  1101.         ja      parse_cmd.error
  1102.         or      al, TYPE_LOCAL
  1103.         mov     [edx + thread_data.type], al
  1104.  
  1105.   .ok:
  1106.         sendFTP "200 Command ok"
  1107.         ret
  1108.  
  1109. ;------------------------------------------------
  1110. ; "USER"
  1111. ;
  1112. ; Login to the server, step one of two.
  1113. ;
  1114. ;------------------------------------------------
  1115. align 4
  1116. cmdUSER:
  1117.  
  1118.         lea     esi, [esi + 5]
  1119.         lea     edi, [edx + thread_data.fpath]
  1120.   .loop:                                         ;;; TODO: prevent buffer overflow!
  1121.         lodsb
  1122.         stosb
  1123.         cmp     al, 0x20
  1124.         jae     .loop
  1125.         mov     byte [edi-1], 0
  1126.  
  1127.         lea     esi, [edx + thread_data.fpath]
  1128.         lea     eax, [edx + thread_data.home_dir]
  1129.         invoke  ini.get_str, path2, esi, str_home, eax, 1024
  1130.         cmp     eax, -1
  1131.         je      .login_fail
  1132.  
  1133.         mov     word [edx + thread_data.work_dir], "/"          ; "/", 0
  1134.  
  1135.         push    str_logged_in
  1136.         call    [con_write_asciiz]
  1137.  
  1138.         mov     edx, [ebp]
  1139.         mov     [edx + thread_data.state], STATE_LOGIN
  1140.   .sendstr:
  1141.         sendFTP "331 Please specify the password"
  1142.         ret
  1143.  
  1144.   .login_fail:
  1145.         push    str_login_invalid
  1146.         call    [con_write_asciiz]
  1147.  
  1148.         mov     edx, [ebp]
  1149.         mov     [edx + thread_data.state], STATE_LOGIN_FAIL
  1150.         jmp     .sendstr
  1151.  
  1152. align 4
  1153.   .2:
  1154.         sendFTP "530 Can't change to another user"
  1155.         ret
  1156.