Subversion Repositories Kolibri OS

Rev

Rev 4918 | Rev 5011 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3701 hidnplayr 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                                 ;;
4729 hidnplayr 3
;; Copyright (C) KolibriOS team 2013-2014. All rights reserved.    ;;
3701 hidnplayr 4
;; Distributed under terms of the GNU General Public License       ;;
5
;;                                                                 ;;
6
;;  ftpc.asm - FTP client for KolibriOS                            ;;
7
;;                                                                 ;;
8
;;  Written by hidnplayr@kolibrios.org                             ;;
9
;;                                                                 ;;
10
;;          GNU GENERAL PUBLIC LICENSE                             ;;
11
;;             Version 2, June 1991                                ;;
12
;;                                                                 ;;
13
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
14
 
15
format binary as ""
16
 
4918 hidnplayr 17
TIMEOUT                 = 3     ; seconds
18
 
3800 hidnplayr 19
BUFFERSIZE              = 4096
3701 hidnplayr 20
 
21
STATUS_CONNECTING       = 0
22
STATUS_CONNECTED        = 1
23
STATUS_NEEDPASSWORD     = 2
24
STATUS_LOGGED_IN        = 3
25
 
3821 hidnplayr 26
OPERATION_NONE          = 0
27
OPERATION_LIST          = 1
28
OPERATION_RETR          = 2
29
OPERATION_STOR          = 3
4922 ashmew2 30
OPERATION_RDIR		    = 4
31
 
3701 hidnplayr 32
use32
33
; standard header
34
        db      'MENUET01'      ; signature
35
        dd      1               ; header version
36
        dd      start           ; entry point
37
        dd      i_end           ; initialized size
38
        dd      mem+0x1000      ; required memory
39
        dd      mem+0x1000      ; stack pointer
4922 ashmew2 40
        dd      buf_cmd         ; parameters
3701 hidnplayr 41
        dd      0               ; path
42
 
43
include '../../macros.inc'
44
purge mov,add,sub
45
include '../../proc32.inc'
46
include '../../dll.inc'
47
include '../../network.inc'
48
 
49
include 'usercommands.inc'
50
include 'servercommands.inc'
51
 
52
start:
4922 ashmew2 53
; initialize heap for using dynamic blocks
54
	mcall	68,11
55
	test 	eax,eax
56
	je 	exit2
57
 
4918 hidnplayr 58
; disable all events except network event
59
        mcall   40, EV_STACK
3701 hidnplayr 60
; load libraries
61
        stdcall dll.Load, @IMPORT
62
        test    eax, eax
63
        jnz     exit
64
; initialize console
3789 hidnplayr 65
        invoke  con_start, 1
3813 hidnplayr 66
        invoke  con_init, 80, 25, 80, 250, str_title
67
; Check for parameters, if there are some, resolve the address right away
4922 ashmew2 68
        cmp     byte [buf_cmd], 0
3701 hidnplayr 69
        jne     resolve
70
 
71
main:
3813 hidnplayr 72
; Clear screen
3789 hidnplayr 73
        invoke  con_cls
3701 hidnplayr 74
; Welcome user
3813 hidnplayr 75
        invoke  con_write_asciiz, str_welcome
76
; write prompt (in green color)
3789 hidnplayr 77
        invoke  con_set_flags, 0x0a
3813 hidnplayr 78
        invoke  con_write_asciiz, str_prompt
3701 hidnplayr 79
; read string
4922 ashmew2 80
        invoke  con_gets, buf_cmd, 256
3701 hidnplayr 81
; check for exit
82
        test    eax, eax
83
        jz      done
4922 ashmew2 84
        cmp     byte [buf_cmd], 10
3701 hidnplayr 85
        jz      done
3813 hidnplayr 86
; reset color back to grey and print newline
87
        invoke  con_set_flags, 0x07
88
        invoke  con_write_asciiz, str_newline
3701 hidnplayr 89
 
90
resolve:
91
; delete terminating '\n'
4922 ashmew2 92
        mov     esi, buf_cmd
3701 hidnplayr 93
  @@:
94
        lodsb
95
        cmp     al, 0x20
96
        ja      @r
97
        mov     byte [esi-1], 0
3813 hidnplayr 98
; Say to the user that we're resolving
99
        invoke  con_write_asciiz, str_resolve
4922 ashmew2 100
        invoke  con_write_asciiz, buf_cmd
3701 hidnplayr 101
; resolve name
102
        push    esp     ; reserve stack place
4922 ashmew2 103
        invoke  getaddrinfo, buf_cmd, 0, 0, esp
3701 hidnplayr 104
        pop     esi
105
; test for error
106
        test    eax, eax
3813 hidnplayr 107
        jnz     error_resolve
3701 hidnplayr 108
; write results
3800 hidnplayr 109
        invoke  con_write_asciiz, str8          ; ' (',0
110
        mov     eax, [esi+addrinfo.ai_addr]     ; convert IP address to decimal notation
111
        mov     eax, [eax+sockaddr_in.sin_addr] ;
112
        mov     [sockaddr1.ip], eax             ;
113
        invoke  inet_ntoa, eax                  ;
114
        invoke  con_write_asciiz, eax           ; print ip
115
        invoke  freeaddrinfo, esi               ; free allocated memory
116
        invoke  con_write_asciiz, str9          ; ')',10,0
117
; open the socket
3701 hidnplayr 118
        mcall   socket, AF_INET4, SOCK_STREAM, 0
119
        cmp     eax, -1
3813 hidnplayr 120
        je      error_socket
3701 hidnplayr 121
        mov     [socketnum], eax
3800 hidnplayr 122
; connect to the server
3813 hidnplayr 123
        invoke  con_write_asciiz, str_connect
3701 hidnplayr 124
        mcall   connect, [socketnum], sockaddr1, 18
4918 hidnplayr 125
        cmp     eax, -1
126
        je      error_connect
3701 hidnplayr 127
        mov     [status], STATUS_CONNECTING
3813 hidnplayr 128
; Tell the user we're waiting for the server now.
129
        invoke  con_write_asciiz, str_waiting
3701 hidnplayr 130
 
3813 hidnplayr 131
; Reset 'offset' variable, it's used by the data receiver
3790 hidnplayr 132
        mov     [offset], 0
133
 
3789 hidnplayr 134
wait_for_servercommand:
3813 hidnplayr 135
; Any commands still in our buffer?
3790 hidnplayr 136
        cmp     [offset], 0
3813 hidnplayr 137
        je      .receive                        ; nope, receive some more
3790 hidnplayr 138
        mov     esi, [offset]
4922 ashmew2 139
        mov     edi, buf_cmd
3790 hidnplayr 140
        mov     ecx, [size]
141
        add     ecx, esi
142
        jmp     .byteloop
3701 hidnplayr 143
 
144
; receive socket data
3790 hidnplayr 145
  .receive:
4918 hidnplayr 146
        mcall   26, 9
147
        add     eax, TIMEOUT*100
148
        mov     [timeout], eax
149
  .receive_loop:
150
        mcall   23, 50          ; Wait for event with timeout
151
        mcall   26, 9
152
        cmp     eax, [timeout]
153
        jge     error_timeout
4922 ashmew2 154
        mcall   recv, [socketnum], buf_buffer1, BUFFERSIZE, MSG_DONTWAIT
4918 hidnplayr 155
        test    eax, eax
156
        jnz     .got_data
157
        cmp     ebx, EWOULDBLOCK
158
        jne     error_socket
159
        jmp     .receive_loop
3701 hidnplayr 160
 
4918 hidnplayr 161
  .got_data:
3790 hidnplayr 162
        mov     [offset], 0
3789 hidnplayr 163
 
4922 ashmew2 164
; extract commands, copy them to "buf_cmd" buffer
165
        lea     ecx, [eax + buf_buffer1]         ; ecx = end pointer
166
        mov     esi, buf_buffer1                 ; esi = current pointer
167
        mov     edi, buf_cmd
3701 hidnplayr 168
  .byteloop:
3789 hidnplayr 169
        cmp     esi, ecx
170
        jae     wait_for_servercommand
3701 hidnplayr 171
        lodsb
172
        cmp     al, 10                          ; excellent, we might have a command
173
        je      .got_command
3789 hidnplayr 174
        cmp     al, 13                          ; just ignore this byte
3701 hidnplayr 175
        je      .byteloop
176
        stosb
177
        jmp     .byteloop
3789 hidnplayr 178
  .got_command:                                 ; we have a newline check if its a command
3790 hidnplayr 179
        cmp     esi, ecx
180
        je      .no_more_data
181
        mov     [offset], esi
182
        sub     ecx, esi
183
        mov     [size], ecx
184
        jmp     .go_cmd
185
  .no_more_data:
186
        mov     [offset], 0
187
  .go_cmd:
4922 ashmew2 188
        lea     ecx, [edi - buf_cmd]                  ; length of command
3701 hidnplayr 189
        xor     al, al
190
        stosb
191
 
3789 hidnplayr 192
        invoke  con_set_flags, 0x03             ; change color
4922 ashmew2 193
        invoke  con_write_asciiz, buf_cmd             ; print servercommand
3813 hidnplayr 194
        invoke  con_write_asciiz, str_newline
3800 hidnplayr 195
        invoke  con_set_flags, 0x07             ; reset color
3701 hidnplayr 196
 
3789 hidnplayr 197
        jmp     server_parser                   ; parse command
3701 hidnplayr 198
 
3813 hidnplayr 199
 
200
 
3789 hidnplayr 201
wait_for_usercommand:
3701 hidnplayr 202
 
4922 ashmew2 203
; Are there any files in the transfer queue?
204
 
205
	    cmp 	[queued], 0
206
        ja      transfer_queued                 ; Yes, transfer those first.
207
 
3813 hidnplayr 208
; change color to green for user input
3789 hidnplayr 209
        invoke  con_set_flags, 0x0a
3701 hidnplayr 210
 
3813 hidnplayr 211
; If we are not yet connected, request username/password
3701 hidnplayr 212
        cmp     [status], STATUS_CONNECTED
213
        je      .connected
214
 
215
        cmp     [status], STATUS_NEEDPASSWORD
216
        je      .needpass
217
 
218
; write prompt
3813 hidnplayr 219
        invoke  con_write_asciiz, str_prompt
3701 hidnplayr 220
; read string
4922 ashmew2 221
        invoke  con_gets, buf_cmd, 256
3794 hidnplayr 222
 
3813 hidnplayr 223
; print a newline and reset the color back to grey
224
        invoke  con_write_asciiz, str_newline
3789 hidnplayr 225
        invoke  con_set_flags, 0x07
3701 hidnplayr 226
 
4922 ashmew2 227
        cmp     dword[buf_cmd], "cwd "
3790 hidnplayr 228
        je      cmd_cwd
229
 
4922 ashmew2 230
        cmp     dword[buf_cmd], "mkd "
3813 hidnplayr 231
        je      cmd_mkd
3793 hidnplayr 232
 
4922 ashmew2 233
        cmp     dword[buf_cmd], "rmd "
3813 hidnplayr 234
        je      cmd_rmd
235
 
4922 ashmew2 236
        cmp     dword[buf_cmd], "pwd" + 10 shl 24
3794 hidnplayr 237
        je      cmd_pwd
238
 
4922 ashmew2 239
        cmp     dword[buf_cmd], "bye" + 10 shl 24
3813 hidnplayr 240
        je      cmd_bye
241
 
4922 ashmew2 242
        cmp     dword[buf_cmd], "rdir"
243
	je      cmd_rdir
244
 
245
        cmp     byte[buf_cmd+4], " "
3813 hidnplayr 246
        jne     @f
247
 
4922 ashmew2 248
        cmp     dword[buf_cmd], "lcwd"
3813 hidnplayr 249
        je      cmd_lcwd
250
 
4922 ashmew2 251
        cmp     dword[buf_cmd], "retr"
3813 hidnplayr 252
        je      cmd_retr
253
 
4922 ashmew2 254
        cmp     dword[buf_cmd], "stor"
3800 hidnplayr 255
        je      cmd_stor
3793 hidnplayr 256
 
4922 ashmew2 257
        cmp     dword[buf_cmd], "dele"
3800 hidnplayr 258
        je      cmd_dele
259
 
3813 hidnplayr 260
  @@:
4922 ashmew2 261
        cmp     byte[buf_cmd+4], 10
3813 hidnplayr 262
        jne     @f
3800 hidnplayr 263
 
4922 ashmew2 264
        cmp     dword[buf_cmd], "list"
3813 hidnplayr 265
        je      cmd_list
3802 hidnplayr 266
 
4922 ashmew2 267
        cmp     dword[buf_cmd], "help"
3813 hidnplayr 268
        je      cmd_help
3804 hidnplayr 269
 
4922 ashmew2 270
        cmp     dword[buf_cmd], "cdup"
3804 hidnplayr 271
        je      cmd_cdup
272
 
3813 hidnplayr 273
  @@:
274
; Uh oh.. unknown command, tell the user and wait for new input
3789 hidnplayr 275
        invoke  con_write_asciiz, str_unknown
3701 hidnplayr 276
        jmp     wait_for_usercommand
277
 
278
 
279
  .connected:
3813 hidnplayr 280
; request username
3789 hidnplayr 281
        invoke  con_write_asciiz, str_user
4922 ashmew2 282
        mov     dword[buf_cmd], "USER"
283
        mov     byte[buf_cmd+4], " "
3701 hidnplayr 284
        jmp     .send
285
 
286
 
287
  .needpass:
3813 hidnplayr 288
; request password
3789 hidnplayr 289
        invoke  con_write_asciiz, str_pass
4922 ashmew2 290
        mov     dword[buf_cmd], "PASS"
291
        mov     byte[buf_cmd+4], " "
3803 hidnplayr 292
        invoke  con_set_flags, 0x00     ; black text on black background for password
3701 hidnplayr 293
 
294
  .send:
295
; read string
4922 ashmew2 296
        mov     esi, buf_cmd+5
3789 hidnplayr 297
        invoke  con_gets, esi, 256
3701 hidnplayr 298
 
3800 hidnplayr 299
; find end of string
4922 ashmew2 300
        mov     edi, buf_cmd+5
3701 hidnplayr 301
        mov     ecx, 256
302
        xor     al, al
303
        repne   scasb
4922 ashmew2 304
        lea     esi, [edi-buf_cmd]
3814 hidnplayr 305
        mov     word[edi-2], 0x0a0d
3800 hidnplayr 306
; and send it to the server
4922 ashmew2 307
        mcall   send, [socketnum], buf_cmd, , 0
3701 hidnplayr 308
 
3813 hidnplayr 309
        invoke  con_write_asciiz, str_newline
3800 hidnplayr 310
        invoke  con_set_flags, 0x07     ; reset color
3789 hidnplayr 311
        jmp     wait_for_servercommand
3701 hidnplayr 312
 
313
 
314
 
3800 hidnplayr 315
open_dataconnection:                    ; only passive for now..
3701 hidnplayr 316
        cmp     [status], STATUS_LOGGED_IN
317
        jne     .fail
318
 
3821 hidnplayr 319
        mcall   send, [socketnum], str_PASV, str_PASV.length, 0
3701 hidnplayr 320
        ret
321
 
322
  .fail:
3818 hidnplayr 323
        invoke  con_get_flags
324
        push    eax
325
        invoke  con_set_flags, 0x0c                     ; print errors in red
3813 hidnplayr 326
        invoke  con_write_asciiz, str_err_socket
3818 hidnplayr 327
        invoke  con_set_flags                           ; reset color
3701 hidnplayr 328
        ret
329
 
4918 hidnplayr 330
error_connect:
331
        invoke  con_set_flags, 0x0c                     ; print errors in red
332
        invoke  con_write_asciiz, str_err_connect
333
        jmp     wait_for_keypress
3701 hidnplayr 334
 
4918 hidnplayr 335
error_timeout:
336
        invoke  con_set_flags, 0x0c                     ; print errors in red
337
        invoke  con_write_asciiz, str_err_timeout
338
        jmp     wait_for_keypress
3701 hidnplayr 339
 
3813 hidnplayr 340
error_socket:
3818 hidnplayr 341
        invoke  con_set_flags, 0x0c                     ; print errors in red
3813 hidnplayr 342
        invoke  con_write_asciiz, str_err_socket
343
        jmp     wait_for_keypress
3701 hidnplayr 344
 
3813 hidnplayr 345
error_resolve:
3818 hidnplayr 346
        invoke  con_set_flags, 0x0c                     ; print errors in red
3813 hidnplayr 347
        invoke  con_write_asciiz, str_err_resolve
348
 
4922 ashmew2 349
error_heap:
350
	invoke  con_set_flags, 0x0c                     ; print errors in red
351
        invoke  con_write_asciiz, str_err_heap
352
 
3813 hidnplayr 353
wait_for_keypress:
3818 hidnplayr 354
        invoke  con_set_flags, 0x07                     ; reset color to grey
3813 hidnplayr 355
        invoke  con_write_asciiz, str_push
3789 hidnplayr 356
        invoke  con_getch2
3818 hidnplayr 357
        mcall   close, [socketnum]
3701 hidnplayr 358
        jmp     main
359
 
360
done:
3789 hidnplayr 361
        invoke  con_exit, 1
362
 
3701 hidnplayr 363
exit:
364
        mcall   close, [socketnum]
4922 ashmew2 365
exit2:
3701 hidnplayr 366
        mcall   -1
367
 
368
 
369
 
370
; data
3813 hidnplayr 371
str_title       db 'FTP client',0
4918 hidnplayr 372
str_welcome     db 'FTP client for KolibriOS v0.12',10
3813 hidnplayr 373
                db 10
374
                db 'Please enter ftp server address.',10,0
3800 hidnplayr 375
 
3813 hidnplayr 376
str_prompt      db '> ',0
377
str_resolve     db 'Resolving ',0
378
str_newline     db 10,0
379
str_err_resolve db 10,'Name resolution failed.',10,0
380
str_err_socket  db 10,'Socket error.',10,0
4922 ashmew2 381
str_err_heap	db 10,'Cannot allocate memory from heap.',10,0
4918 hidnplayr 382
str_err_timeout db 10,'Timeout - no response from server.',10,0
383
str_err_connect db 10,'Cannot connect to the server.',10,0
3813 hidnplayr 384
str8            db ' (',0
385
str9            db ')',10,0
386
str_push        db 'Push any key to continue.',0
387
str_connect     db 'Connecting...',10,0
388
str_waiting     db 'Waiting for welcome message.',10,0
389
str_user        db "username: ",0
390
str_pass        db "password: ",0
391
str_unknown     db "Unknown command or insufficient parameters - type help for more information.",10,0
392
str_lcwd        db "Local working directory is now: ",0
3701 hidnplayr 393
 
3813 hidnplayr 394
str_open        db "opening data socket",10,0
4922 ashmew2 395
str_close       db 10,"closing data socket",10,0
396
str_dot         db '.',0
3701 hidnplayr 397
 
3813 hidnplayr 398
str_help        db "available commands:",10
399
                db 10
400
                db "bye             - close the connection",10
401
                db "cdup            - change to parent of current directory on the server",10
402
                db "cwd  - change working directoy on the server",10
403
                db "dele      - delete file from the server",10
404
                db "list            - list files and folders in current server directory",10
405
                db "lcwd      - change local working directory",10
406
                db "mkd  - make directory on the server",10
407
                db "pwd             - print server working directory",10
408
                db "retr      - retreive file from the server",10
409
                db "rmd  - remove directory from the server",10
410
                db "stor      - store file on the server",10
4922 ashmew2 411
	            db "rdir            - retreive all files from current server dir",10
3813 hidnplayr 412
                db 10,0
413
 
4922 ashmew2 414
queued		dd 0
3821 hidnplayr 415
 
416
; FTP strings
417
 
418
str_PASV        db 'PASV',13,10
419
.length = $ - str_PASV
420
 
3701 hidnplayr 421
sockaddr1:
422
        dw AF_INET4
423
.port   dw 0x1500       ; 21
424
.ip     dd 0
425
        rb 10
426
 
427
sockaddr2:
428
        dw AF_INET4
429
.port   dw 0
430
.ip     dd 0
431
        rb 10
432
 
433
; import
434
align 4
435
@IMPORT:
436
 
437
library network, 'network.obj', console, 'console.obj'
438
 
439
import  network,        \
440
        getaddrinfo,    'getaddrinfo',  \
441
        freeaddrinfo,   'freeaddrinfo', \
442
        inet_ntoa,      'inet_ntoa'
443
 
444
import  console,        \
445
        con_start,      'START',        \
446
        con_init,       'con_init',     \
447
        con_write_asciiz,'con_write_asciiz',     \
448
        con_exit,       'con_exit',     \
449
        con_gets,       'con_gets',\
450
        con_cls,        'con_cls',\
451
        con_getch2,     'con_getch2',\
452
        con_set_cursor_pos, 'con_set_cursor_pos',\
453
        con_write_string, 'con_write_string',\
3789 hidnplayr 454
        con_get_flags,  'con_get_flags', \
455
        con_set_flags,  'con_set_flags'
3701 hidnplayr 456
 
457
 
458
i_end:
459
 
3813 hidnplayr 460
; uninitialised data
461
 
3794 hidnplayr 462
status          db ?
3701 hidnplayr 463
active_passive  db ?
3794 hidnplayr 464
 
3701 hidnplayr 465
socketnum       dd ?
466
datasocket      dd ?
3790 hidnplayr 467
offset          dd ?
468
size            dd ?
3800 hidnplayr 469
operation       dd ?
3701 hidnplayr 470
 
4922 ashmew2 471
size_fname	dd ?
472
ptr_queue	dd ?
4918 hidnplayr 473
timeout         dd ?
4922 ashmew2 474
ptr_fname_start	dd ?
4918 hidnplayr 475
 
3800 hidnplayr 476
filestruct:
477
.subfn  dd ?
478
.offset dd ?
479
        dd ?
480
.size   dd ?
481
.ptr    dd ?
482
.name   rb 1024
483
 
4922 ashmew2 484
buf_buffer1     rb BUFFERSIZE+1
485
buf_buffer2     rb BUFFERSIZE+1
486
buf_cmd         rb 1024			; buffer for holding command string
3794 hidnplayr 487
 
3701 hidnplayr 488
mem: