Subversion Repositories Kolibri OS

Rev

Rev 2578 | Go to most recent revision | 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
  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.  
  18.         datasock        sockaddr_in
  19.  
  20.         buffer          rb BUFFERSIZE
  21. ends
  22.  
  23.  
  24.  
  25.  
  26. align 4
  27. parse_cmd:                              ; esi must point to command
  28.  
  29.         cmp     byte [esi], 0x20        ; skip all leading characters
  30.         ja      .ok
  31.         inc     esi
  32.         dec     ecx
  33.         cmp     ecx, 3
  34.         ja      parse_cmd
  35.         ret
  36.   .ok:
  37.  
  38.         cmp     byte [esi+3], 0x20
  39.         jae     @f
  40.         mov     byte [esi+3], 0
  41.        @@:
  42.  
  43.         mov     eax, [esi]
  44.         and     eax, not 0x20202020     ; convert to upper case
  45.         mov     edi, commands           ; list of commands to scan
  46.   .scanloop:
  47.         cmp     eax, [edi]
  48.         jne     .try_next
  49.  
  50.         jmp     dword [edi+4]
  51.  
  52.   .try_next:
  53.         add     edi, 8
  54.         cmp     byte [edi], 0
  55.         jne     .scanloop
  56.  
  57.   .error:
  58.         mcall   send, [edx + thread_data.socketnum], str500, str500.length, 0
  59.  
  60.         ret
  61.  
  62.  
  63. align 4
  64. commands:               ; all commands must be in uppercase
  65.  
  66.         db 'ABOR'
  67.         dd cmdABOR
  68.         db 'CDUP'
  69.         dd cmdCDUP
  70.         db 'CWD', 0
  71.         dd cmdCWD
  72.         db 'DELE'
  73.         dd cmdDELE
  74.         db 'LIST'
  75.         dd cmdLIST
  76.         db 'NLST'
  77.         dd cmdNLST
  78.         db 'NOOP'
  79.         dd cmdNOOP
  80.         db 'PASS'
  81.         dd cmdPASS
  82.         db 'PASV'
  83.         dd cmdPASV
  84.         db 'PWD', 0
  85.         dd cmdPWD
  86.         db 'PORT'
  87.         dd cmdPORT
  88.         db 'QUIT'
  89.         dd cmdQUIT
  90.         db 'RETR'
  91.         dd cmdRETR
  92.         db 'STOR'
  93.         dd cmdSTOR
  94.         db 'SYST'
  95.         dd cmdSYST
  96.         db 'TYPE'
  97.         dd cmdTYPE
  98.         db 'USER'
  99.         dd cmdUSER
  100.         db 'XPWD'
  101.         dd cmdPWD
  102.         db 0            ; end marker
  103.  
  104.  
  105. align 4
  106. cmdABOR:
  107.  
  108.         ; TODO: abort the current filetransfer
  109.  
  110.         ret
  111.  
  112. align 4
  113. cmdCDUP:
  114.  
  115.         cmp     byte [edx + thread_data.work_dir+1], 0
  116.         je      .done
  117.  
  118.         mov     ecx, 1024
  119.         xor     al, al
  120.         lea     edi, [edx + thread_data.work_dir+1024]
  121.         repne   scasb
  122.         std
  123.         dec     edi
  124.         mov     al,'/'
  125.         scasb
  126.         cld
  127.         mov     byte[edi], 0
  128.  
  129.   .done:
  130.         mcall   send, [edx + thread_data.socketnum], str250, str250.length, 0    ; command successful
  131.         ret
  132.  
  133. align 4
  134. cmdCWD:                 ; Change Working Directory
  135.  
  136.         sub     ecx, 4
  137.         jb      .err
  138.         add     esi, 4
  139.  
  140.   .scan:
  141.         lea     edi, [edx + thread_data.work_dir + 1]
  142.         push    ecx
  143.         mov     ecx, 1024
  144.   .find_zero:
  145.         cmp     byte [edi], 0
  146.         je      .found_zero
  147.         inc     edi
  148.         loop    .find_zero
  149.   .found_zero:
  150.         pop     ecx
  151.   .scan2:
  152.  
  153.         cmp     byte [esi], '/'
  154.         jne     @f
  155.         inc     esi
  156.         dec     ecx
  157.         jz      .done
  158.        @@:
  159.  
  160.   .loop:
  161.         lodsb
  162.         cmp     al, 0x20
  163.         jb      .done
  164.         cmp     al, '.'
  165.         je      .up
  166.   .continue:
  167.         stosb
  168.         loop    .loop
  169.   .done:
  170.         cmp     byte [edi-1], '/'
  171.         je      @f
  172.         mov     byte [edi], '/'
  173.         inc     edi
  174.        @@:
  175.         mov     byte [edi], 0
  176.  
  177.         mcall   send, [edx + thread_data.socketnum], str250, str250.length, 0
  178.  
  179.         ret
  180.  
  181.   .up:
  182.         lodsb
  183.         cmp     al, '.'
  184.         jne     .continue
  185.  
  186. ;;;;        TODO: find second last '\' in work_dir and make next char zero
  187. ;;;;        point edi to that 0
  188.  
  189.         jmp     .scan2
  190.  
  191.   .err:
  192.         ; TODO: print correct error message (550?)
  193.  
  194.         ret
  195.  
  196. align 4
  197. cmdDELE:
  198.  
  199.         ret
  200.  
  201. align 4
  202. cmdLIST:
  203.  
  204. ; If we are in active mode, it's time to open a data socket..
  205.         cmp     [edx + thread_data.mode], MODE_ACTIVE
  206.         jne     @f
  207.         mov     ecx, [edx + thread_data.datasocketnum]
  208.         lea     edx, [edx + thread_data.datasock]
  209.         mov     esi, sizeof.thread_data.datasock
  210.         mcall   connect
  211.         mov     edx, [esp+4]                                    ; thread_data pointer
  212.         cmp     eax, -1
  213.         je      socketerror
  214.   @@:
  215.  
  216. ; Create fpath from home_dir and work_dir
  217.         call    create_path
  218.  
  219.         lea     eax, [edx + thread_data.fpath]
  220.         push    eax
  221.         call    [con_write_asciiz]
  222.         push    str_newline
  223.         call    [con_write_asciiz]
  224.  
  225. ; Start the search
  226.         push    FA_ANY
  227.         push    str_mask
  228.         lea     eax, [edx + thread_data.fpath]
  229.         push    eax
  230.         call    [file.find.first]
  231.  
  232.         test    eax, eax
  233.         jz      .nosuchdir
  234.  
  235.         lea     edi, [edx + thread_data.buffer]
  236.   .parse_file:
  237.  
  238.         test    eax, eax        ; did we find a file?
  239.         jz      .done
  240.         mov     ebx, eax        ; yes, save the descripter in ebx
  241.  
  242. ; first, convert the attributes
  243.         test    [ebx + FileInfoA.Attributes], FA_FOLDER
  244.         jnz     .folder
  245.  
  246.         test    [ebx + FileInfoA.Attributes], FA_READONLY
  247.         jnz     .readonly
  248.  
  249.         mov     eax, '-rw-'
  250.         stosd
  251.         jmp     .attr
  252.  
  253.   .folder:
  254.         mov     eax, 'drwx'
  255.         stosd
  256.         jmp     .attr
  257.  
  258.   .readonly:
  259.         mov     eax, '-r--'
  260.         stosd
  261.  
  262.   .attr:
  263.         mov     eax, 'rw-r'
  264.         stosd
  265.         mov     ax, 'w-'
  266.         stosw
  267.         mov     al, ' '
  268.         stosb
  269.  
  270. ; now..
  271.         mov     ax, '1 '
  272.         stosw
  273.  
  274. ; now write owner, everything is owned by FTP, woohoo!
  275.         mov     eax, 'FTP '
  276.         stosd
  277.         stosd
  278.  
  279. ; now the filesize in ascii
  280.         mov     eax, [ebx + FileInfoA.FileSizeLow]
  281.         call    dword_to_ascii
  282.  
  283.         mov     al, ' '
  284.         stosb
  285.  
  286. ; then date (month/day/year)
  287.         movzx   eax, [ebx + FileInfoA.DateModify + FileDateTime.month]
  288.         mov     eax, [months + 4*eax]
  289.         stosd
  290.  
  291.         movzx   eax, [ebx + FileInfoA.DateModify + FileDateTime.day]
  292.         call    dword_to_ascii
  293.  
  294.         mov     al, ' '
  295.         stosb
  296.  
  297.         movzx   eax, [ebx + FileInfoA.DateModify + FileDateTime.year]
  298.         call    dword_to_ascii
  299.  
  300.         mov     al, ' '
  301.         stosb
  302.  
  303. ; and last but not least, filename
  304.         lea     esi, [ebx + FileInfoA.FileName]
  305.         mov     ecx, 264
  306.   .nameloop:
  307.         lodsb
  308.         test    al, al
  309.         jz      .namedone
  310.         stosb
  311.         loop    .nameloop
  312.  
  313. ; insert a cr lf
  314.   .namedone:
  315.         mov     ax, 0x0a0d
  316.         stosw
  317.  
  318. ; check next file
  319.         push    ebx
  320.         call    [file.find.next]
  321.         jmp     .parse_file
  322.  
  323. ; close file desc
  324.   .done:
  325.         push    ebx
  326.         call    [file.find.close]
  327.  
  328. ; append the string with a 0
  329.         xor     al, al
  330.         stosb
  331.  
  332. ; Warn the client we're about to send the data
  333.         push    edi edx
  334.         mcall   send, [edx + thread_data.socketnum], str150, str150.length, 0    ; here it comes..
  335.         pop     edx esi
  336.  
  337. ; and send it to the client
  338.         mov     ecx, [edx + thread_data.datasocketnum]
  339.         lea     edx, [edx + thread_data.buffer]
  340.         sub     esi, edx
  341.         xor     edi, edi
  342.         mcall   send
  343.  
  344. ; close the data socket..
  345.         mov     edx, [esp+4]                                    ; thread_data pointer
  346.         mcall   close, [edx + thread_data.datasocketnum]
  347.  
  348.         cmp     [edx + thread_data.mode], MODE_PASSIVE_OK
  349.         jne     @f
  350.         mov     [edx + thread_data.mode], MODE_NOTREADY
  351.       @@:
  352.  
  353. ; And send "transfer ok" on the base connection
  354.         mcall   send, [edx + thread_data.socketnum], str226, str226.length, 0
  355.  
  356.         ret
  357.  
  358.   .nosuchdir:
  359.         mcall   send, [edx + thread_data.socketnum], str550, str550.length, 0
  360.  
  361.         ret
  362.  
  363.  
  364. align 4
  365. cmdNLST:
  366.  
  367.         ; TODO: same as list but simpler output format
  368.  
  369.         ret
  370.  
  371. align 4
  372. cmdNOOP:
  373.  
  374.         ret
  375.  
  376. align 4
  377. cmdPASS:
  378.  
  379.         ; TODO: verify password
  380.  
  381.         mcall   send, [edx + thread_data.socketnum], str230, str230.length, 0
  382.  
  383.         push    str_pass_ok
  384.         call    [con_write_asciiz]
  385.  
  386.         mov     edx, [esp+4]                                    ; thread_data pointer
  387.         mov     [edx + thread_data.state], STATE_ACTIVE
  388.  
  389.         ret
  390.  
  391. align 4
  392. cmdPASV:
  393.  
  394. ; Open a new TCP socket
  395.         mcall   socket, AF_INET4, SOCK_STREAM, 0
  396.         mov     edx, [esp+4]                                    ; thread_data pointer
  397.         cmp     eax, -1
  398.         je      socketerror
  399.         mov     [edx + thread_data.passivesocknum], eax
  400.  
  401. ; Bind it to a known local port
  402.         mov     [edx + thread_data.datasock.sin_family], AF_INET4
  403.         mov     [edx + thread_data.datasock.sin_port], 2000
  404.         mov     [edx + thread_data.datasock.sin_addr], 0
  405.  
  406.         mov     ecx, eax ;[edx + thread_data.passivesocknum]
  407.         lea     edx, [edx + thread_data.datasock]
  408.         mov     esi, sizeof.thread_data.datasock
  409.         mcall   bind
  410.         mov     edx, [esp+4]                                    ; thread_data pointer
  411.         cmp     eax, -1
  412.         je      bind_err
  413.  
  414. ; And set it to listen!
  415.         mcall   listen, [edx + thread_data.passivesocknum], 10    ;;;;; FIXME
  416.  
  417. ; Tell our thread we are ready to accept incoming calls
  418.         mov     edx, [esp+4]                                    ; thread_data pointer
  419.         mov     [edx + thread_data.mode], MODE_PASSIVE_WAIT
  420.  
  421. ; Now tell the client where to connect to in this format:
  422. ; 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
  423. ; where a1.a2.a3.a4 is the IP address and p1*256+p2 is the port number.
  424.         lea     edi, [edx + thread_data.buffer]
  425.         mov     eax, '227 '     ; FIXME (now hardcoded to 127.0.0.1:2000)
  426.         stosd
  427.         mov     eax, '(127'
  428.         stosd
  429.         mov     eax, ',0,0'
  430.         stosd
  431.         mov     eax, ',1,7'
  432.         stosd
  433.         mov     eax, ',208'
  434.         stosd
  435.         mov     al, ')'
  436.         stosb
  437.         mov     ax, 0x0a0d
  438.         stosw
  439.         xor     al, al
  440.         stosb
  441.  
  442.         lea     esi, [edi - thread_data.buffer]
  443.         sub     esi, edx
  444.         mov     ecx, [edx + thread_data.socketnum]
  445.         lea     edx, [edx + thread_data.buffer]
  446.         xor     esi, esi
  447.         mcall   send
  448.  
  449.         ret
  450.  
  451. align 4
  452. cmdPWD:         ; Print Working Directory
  453.  
  454.         mov     dword [edx + thread_data.buffer], '257 '
  455.         mov     byte [edx + thread_data.buffer+4], '"'
  456.  
  457.         lea     edi, [edx + thread_data.buffer+5]
  458.         lea     esi, [edx + thread_data.work_dir]
  459.         mov     ecx, 1024
  460.   .loop:
  461.         lodsb
  462.         or      al, al
  463.         jz      .ok
  464.         stosb
  465.         dec     ecx
  466.         jnz     .loop
  467.  
  468.   .ok:
  469.         mov     dword [edi], '"' + 0x000a0d00    ; '"',13,10,0
  470.         lea     esi, [edi - thread_data.buffer + 4]
  471.         sub     esi, edx
  472.         mov     ecx, [edx + thread_data.socketnum]
  473.         lea     edx, [edx + thread_data.buffer]
  474.         mcall   send, , , , 0
  475.  
  476. ;        push    work_dir
  477. ;        push    str_pwd
  478. ;        call    [con_printf]
  479.  
  480.         ret
  481.  
  482. align 4
  483. cmdPORT:
  484.  
  485. ; PORT a1,a2,a3,a4,p1,p2
  486. ; IP address a1.a2.a3.a4, port p1*256+p2
  487.  
  488.         mov     [edx + thread_data.mode], MODE_ACTIVE
  489.  
  490.         lea     esi, [esi+5]
  491. ; Convert the IP
  492.         call    ascii_to_byte
  493.         mov     bl, al
  494.         inc     esi             ; skip past ','
  495.         call    ascii_to_byte
  496.         mov     bh, al
  497.         shl     ebx, 16
  498.         inc     esi
  499.         call    ascii_to_byte
  500.         mov     bl, al
  501.         inc     esi
  502.         call    ascii_to_byte
  503.         mov     bh, al
  504.         inc     esi
  505.         rol     ebx, 16
  506.  
  507. ; And put it in datasock
  508.         mov     [edx + thread_data.datasock.sin_addr], ebx
  509.  
  510. ; Now the same with portnumber
  511.         call    ascii_to_byte
  512.         mov     bh, al
  513.         inc     esi
  514.         call    ascii_to_byte
  515.         mov     bl, al
  516.  
  517. ; Save it in datasock too
  518.         mov     [edx + thread_data.datasock.sin_port], bx
  519.  
  520. ; We will open the socket, but do not connect yet!
  521.         mov     [edx + thread_data.datasock.sin_family], AF_INET4
  522.         mcall   socket, AF_INET4, SOCK_STREAM, 0
  523.         mov     edx, [esp+4]                                    ; thread_data pointer
  524.         cmp     eax, -1
  525.         je      socketerror
  526.         mov     [edx + thread_data.datasocketnum], eax
  527.  
  528. ; Tell the client we are ready
  529.         mov     edx, [esp+4]                                    ; thread_data pointer
  530.         mcall   send, [edx + thread_data.socketnum], str225, str225.length, 0
  531.         ret
  532.  
  533.  
  534. align 4
  535. cmdQUIT:
  536.  
  537.         mcall   close, [edx + thread_data.datasocketnum]
  538.         mcall   send, [edx + thread_data.socketnum], str221, str221.length, 0    ; 221 - bye!
  539.         mcall   close;, [edx + thread_data.socketnum]
  540.  
  541.         add     esp, 4          ; get rid of call return address
  542.         jmp     thread_exit     ; now close this thread
  543.  
  544. align 4
  545. cmdRETR:
  546.  
  547.         sub     ecx, 5
  548.         jb      .cannot_open
  549.  
  550.         cmp     [edx + thread_data.mode], MODE_ACTIVE
  551.         jne     @f
  552.         push    esi
  553.         mov     ecx, [edx + thread_data.datasocketnum]
  554.         lea     edx, [edx + thread_data.datasock]
  555.         mov     esi, sizeof.thread_data.datasock
  556.         mcall   connect
  557.         pop     esi
  558.         mov     edx, [esp+4]                                    ; thread_data pointer
  559.         cmp     eax, -1
  560.         je      socketerror
  561.   @@:
  562.  
  563.         push    esi
  564.         call    create_path
  565.         pop     esi
  566.         dec     edi
  567.         add     esi, 5
  568.         mov     ecx, 1024
  569.   .loop:
  570.         lodsb
  571.         cmp     al, 0x20
  572.         jl      .done
  573.         stosb
  574.         loop    .loop
  575.   .done:
  576.         xor     al, al
  577.         stosb
  578.  
  579.         lea     eax, [edx + thread_data.fpath]
  580.         push    eax
  581.         call    [con_write_asciiz]
  582.         push    str_newline
  583.         call    [con_write_asciiz]
  584.  
  585.         push    O_READ
  586.         lea     eax, [edx + thread_data.fpath]
  587.         push    eax
  588.         call    [file.open]
  589.         test    eax, eax
  590.         jz      .cannot_open
  591.  
  592.         push    eax
  593.         mcall   send, [edx + thread_data.socketnum], str150, str150.length, 0    ; here it comes..
  594.         pop     ebx
  595.  
  596.         mov     edx, [esp+4]                                    ; thread_data pointer
  597.   .read_more:
  598.         push    BUFFERSIZE
  599.         lea     eax, [edx + thread_data.buffer]
  600.         push    eax
  601.         push    ebx
  602.         call    [file.read]
  603.         cmp     eax, -1
  604.         je      .cannot_open    ; fixme: this is not the correct error
  605.  
  606.         push    eax
  607.         push    ebx
  608.         mov     esi, eax
  609.         mov     ecx, [edx + thread_data.datasocketnum]
  610.         lea     edx, [edx + thread_data.buffer]
  611.         xor     esi, esi
  612.         mcall   send
  613.         pop     ebx
  614.         pop     ecx
  615.         mov     edx, [esp+4]                                    ; thread_data pointer
  616.         cmp     eax, -1
  617.         je      socketerror
  618.  
  619.         cmp     ecx, BUFFERSIZE
  620.         je      .read_more
  621.  
  622.         mcall   close, [edx + thread_data.datasocketnum]
  623.  
  624.         cmp     [edx + thread_data.mode], MODE_PASSIVE_OK
  625.         jne     @f
  626.         mov     [edx + thread_data.mode], MODE_PASSIVE_WAIT
  627.       @@:
  628.  
  629.         mcall   send, [edx + thread_data.socketnum], str226, str226.length, 0    ; transfer ok
  630.  
  631.         ret
  632.  
  633.   .cannot_open:
  634.         pushd   0x0c
  635.         call    [con_set_flags]
  636.         push    str_notfound
  637.         call    [con_write_asciiz]
  638.         pushd   0x07
  639.         call    [con_set_flags]
  640.  
  641.         mcall   send, [edx + thread_data.socketnum], str550, str550.length, 0    ; file not found
  642.  
  643.         ret
  644.  
  645. align 4
  646. cmdSTOR:
  647.  
  648.         ; TODO: check if user has write permission, and write file if so
  649.  
  650.         ret
  651.  
  652. align 4
  653. cmdSYST:
  654.  
  655.         mcall   send, [edx + thread_data.socketnum], str215, str215.length, 0
  656.  
  657.         ret
  658.  
  659. align 4
  660. cmdTYPE:
  661.  
  662.         cmp     ecx, 6
  663.         jb      parse_cmd.error
  664.  
  665.         mov     al, byte[esi+5]
  666.         and     al, not 0x20
  667.  
  668.         cmp     al, 'A'
  669.         je      .ascii
  670.         cmp     al, 'E'
  671.         je      .ebdic
  672.         cmp     al, 'I'
  673.         je      .image
  674.         cmp     al, 'L'
  675.         je      .local
  676.  
  677.         jmp     parse_cmd.error
  678.  
  679.   .ascii:
  680.         mov     [edx + thread_data.type], TYPE_ASCII
  681.         jmp     .subtype
  682.  
  683.   .ebdic:
  684.         mov     [edx + thread_data.type], TYPE_EBDIC
  685.  
  686.   .subtype:
  687.  
  688.         cmp     ecx, 8
  689.         jb      .non_print
  690.  
  691.         mov     al, byte[esi+7]
  692.         and     al, not 0x20
  693.  
  694.         cmp     al, 'N'
  695.         je      .non_print
  696.         cmp     al, 'T'
  697.         je      .telnet
  698.         cmp     al, 'C'
  699.         je      .asacc
  700.  
  701.         jmp     parse_cmd.error
  702.  
  703.   .non_print:
  704.         or      [edx + thread_data.type], TYPE_NP
  705.         jmp     .ok
  706.  
  707.   .telnet:
  708.         or      [edx + thread_data.type], TYPE_TELNET
  709.         jmp     .ok
  710.  
  711.   .asacc:
  712.         or      [edx + thread_data.type], TYPE_ASA
  713.         jmp     .ok
  714.  
  715.   .image:
  716.         mov     [edx + thread_data.type], TYPE_IMAGE
  717.         jmp     .ok
  718.  
  719.   .local:
  720.         cmp     ecx, 8
  721.         jb      parse_cmd.error
  722.  
  723.         mov     al, byte[esi+7]
  724.         sub     al, '0'
  725.         jb      parse_cmd.error
  726.         cmp     al, 9
  727.         ja      parse_cmd.error
  728.         or      al, TYPE_LOCAL
  729.         mov     [edx + thread_data.type], al
  730.  
  731.   .ok:
  732.         mcall   send, [edx + thread_data.socketnum], str200, str200.length, 0
  733.  
  734.         ret
  735.  
  736. align 4
  737. cmdUSER:
  738.  
  739.         ; TODO: check user and set home directory (and permissions)
  740.  
  741.         mcall   send, [edx + thread_data.socketnum], str331, str331.length, 0
  742.         mov     edx, [esp+4]                                    ; thread_data pointer
  743.         mov     [edx + thread_data.state], STATE_LOGIN
  744.  
  745.         mov     byte [edx + thread_data.work_dir], "/"
  746.         mov     byte [edx + thread_data.work_dir+1], 0
  747.  
  748.         push    str_logged_in
  749.         call    [con_write_asciiz]
  750.  
  751.         ret
  752.  
  753.  
  754.  
  755. align 4         ; esi = ptr to str, output in eax
  756. ascii_to_byte:
  757.  
  758.         xor     eax, eax
  759.         push    ebx
  760.  
  761.   .loop:
  762.         movzx   ebx, byte[esi]
  763.         sub     bl, '0'
  764.         jb      .done
  765.         cmp     bl, 9
  766.         ja      .done
  767.         lea     eax, [eax*4 + eax]      ;
  768.         shl     eax, 1                  ; eax = eax * 10
  769.         add     eax, ebx
  770.         inc     esi
  771.  
  772.         jmp     .loop
  773.  
  774.   .done:
  775.         pop     ebx
  776.         ret
  777.  
  778. align 4
  779. dword_to_ascii: ; edi = ptr where to write, eax is number
  780.  
  781.         mov     eax, '1'
  782.         stosb
  783.  
  784.         ret
  785.  
  786. align 4
  787. create_path:            ; combine home_dir and work_dir strings into fpath
  788.  
  789.         lea     edi, [edx + thread_data.fpath]
  790.         lea     esi, [edx + thread_data.home_dir]
  791.         mov     ecx, 1024
  792.   .loop1:
  793.         lodsb
  794.         or      al, al
  795.         jz      .next
  796.         stosb
  797.         loop    .loop1
  798.   .next:
  799.  
  800.         cmp     byte[edi-1], '/'
  801.         jne     @f
  802.         dec     edi
  803.        @@:
  804.  
  805.         lea     esi, [edx + thread_data.work_dir]
  806.         mov     ecx, 1024
  807.   .loop2:
  808.         lodsb
  809.         or      al, al
  810.         jz      .done
  811.         stosb
  812.         loop    .loop2
  813.  
  814.   .done:
  815.         stosb
  816.  
  817.         ret
  818.  
  819.  
  820. align 4
  821. socketerror:
  822.  
  823.         pushd   0x0c
  824.         call    [con_set_flags]
  825.         push    str_sockerr
  826.         call    [con_write_asciiz]
  827.         pushd   0x07
  828.         call    [con_set_flags]
  829.  
  830.         mcall   send, [edx + thread_data.socketnum], str425, str425.length, 0    ; data connection error
  831.  
  832.         ret
  833.  
  834.  
  835.  
  836.  
  837. str150  db '150 Here it comes...', 13, 10
  838. .length = $ - str150
  839. str200  db '200 Command OK.', 13, 10
  840. .length = $ - str200
  841. str215  db '215 UNIX type: L8', 13, 10
  842. .length = $ - str215
  843. str220  db '220 KolibriOS FTP Daemon 1.0', 13, 10
  844. .length = $ - str220
  845. str221  db '221 Bye!', 13, 10
  846. .length = $ - str221
  847. str225  db '225 Data connection open', 13, 10
  848. .length = $ - str225
  849. str226  db '226 Transfer OK, Closing connection', 13, 10
  850. .length = $ - str226
  851. str230  db '230 You are now logged in.', 13, 10
  852. .length = $ - str230
  853. str250  db '250 command successful', 13, 10
  854. .length = $ - str250
  855. str331  db '331 Please specify the password.', 13, 10
  856. .length = $ - str331
  857. str421  db '421 Timeout!', 13, 10
  858. .length = $ - str421
  859. str425  db '425 Cant open data connection.', 13, 10
  860. .length = $ - str425
  861. str500  db '500 Unsupported command', 13, 10
  862. .length = $ - str500
  863. str550  db '550 No such file', 13, 10
  864. .length = $ - str550