1 /******************************************************************************
2 *
3 * Copyright (C) 2004-2008, The Gentee Group. All rights reserved.
4 * This file is part of the Gentee open source project - http://www.gentee.com.
5 *
6 * THIS FILE IS PROVIDED UNDER THE TERMS OF THE GENTEE LICENSE ("AGREEMENT").
7 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE CONSTITUTES RECIPIENTS
8 * ACCEPTANCE OF THE AGREEMENT.
9 *
10 * Author: Alexey Krivonogov ( gentee )
11 *
12 ******************************************************************************/
13
14 /*-----------------------------------------------------------------------------
15 * Id: ftp L "FTP"
16 *
17 * Summary: FTP protocol. You must call
18 #a(inet_init) function before using this library. For using this
19 library, it is
20 required to specify the file ftp.g (from lib\ftp
21 subfolder) with include command. #srcg[
22 |include : $"...\gentee\lib\ftp\ftp.g"]
23 *
24 * List: *,ftp_close,ftp_command,ftp_createdir,ftp_deldir,ftp_delfile,
25 ftp_getcurdir,ftp_getfile,ftp_getsize,ftp_gettime,
26 ftp_lastresponse,ftp_list,ftp_open,ftp_putfile,ftp_rename,
27 ftp_setattrib,ftp_setcurdir,
28 *@Common internet functions,inet_close,inet_error,inet_init,
29 inet_proxy,inet_proxyenable,inetnotify_func,
30 *@URL strings,str_iencoding,str_ihead,str_ihttpinfo,str_iurl,
31 *
32 -----------------------------------------------------------------------------*/
33
34 include : $"..\socket\internet.g"
35
36 type ftp
37 {
38 str path // Дополнительный путь при открытии
39 socket sock // Главный сокет
40 socket sockdata // Сокет передачи данных
41 socket sockserv // Сокет передачи данных PORT
42 sockaddr local // Локальный адрес
43 uint notify // Функция для уведомлений
44 inetnotify ni
45 uint anonymous // 1 если anonymous
46 uint passive // 1 если passive mode
47 }
48
49 define
50 {
51 FTP_OPENING = 150 /* 150 Opening data connection*/
52 FTP_ENDTRAN = 226 /* 226 Transfer Complete */
53 FTP_OK = 200
54 FTP_FILESTAT = 213 // File status.
55 FTP_HELLO = 220
56 FTP_GOODBYE = 221
57 FTP_PASSIVEOK = 227
58 FTP_LOGINOK = 230
59 FTP_CWDOK = 250 /* 250 CWD command successful. */
60 FTP_MKDIROK = 257
61 FTP_PASSWD = 331
62 FTP_FILEOK = 350 /* RNFR command successful */
63 FTP_LOGINBAD = 530
64 FTP_NOTFOUND = 550 /* 550 No such file or directory */
65 }
66
67 define <export>
68 {
69 /*-----------------------------------------------------------------------------
70 * Id: ftpflag D
71 *
72 * Summary: FTP flags.
73 *
74 -----------------------------------------------------------------------------*/
75 FTP_ANONYM = 0x0001 // Anonymous connection.
76 FTP_PASV = 0x0002 // Establishes a connection in passive mode.
77
78 /*-----------------------------------------------------------------------------
79 * Id: ftpget D
80 *
81 * Summary: FTP flags.
82 *
83 -----------------------------------------------------------------------------*/
84 FTP_BINARY = 0x0004 // A binary file is downloaded.
85 FTP_TEXT = 0x0008 // A text file is downloaded. This is a default mode.
86
87 /*-----------------------------------------------------------------------------
88 * Id: ftpgetbuf D
89 *
90 * Summary: FTP flags.
91 *
92 -----------------------------------------------------------------------------*/
93 FTP_STR = 0x0100 // Appends zero to the end of received data.
94
95 /*-----------------------------------------------------------------------------
96 * Id: ftpgetfile D
97 *
98 * Summary: FTP flags.
99 *
100 -----------------------------------------------------------------------------*/
101 FTP_CONTINUE = 0x0010 // Proceeds with retrieving.
102 FTP_SETTIME = 0x0020 // Sets the same file times as on the FTP server.
103
104 //-----------------------------------------------------------------------------
105 FTP_FILE = 0x1000 // для метода get - закачка в файл
106
107 S_IRWXU = 0x0700
108 S_IRUSR = 0x0400
109 S_IWUSR = 0x0200
110 S_IXUSR = 0x0100
111 S_IRWXG = 0x0070
112 S_IRGRP = 0x0040
113 S_IWGRP = 0x0020
114 S_IXGRP = 0x0010
115 S_IRWXO = 0x0007
116 S_IROTH = 0x0004
117 S_IWOTH = 0x0002
118 S_IXOTH = 0x0001
119 }
120
121 /*-----------------------------------------------------------------------------
122 * Id: ftpput D
123 *
124 * Summary: FTP flags.
125 *
126 -----------------------------------------------------------------------------
127 FTP_BINARY // A binary file is uploaded.
128 FTP_TEXT // A text file is uploaded.
129 FTP_CONTINUE // To proceed with file uploading.
130
131 //---------------------------------------------------------------------------*//*-----------------------------------------------------------------------------
132 * Id: ftplist D
133 *
134 * Summary: FTP list.
135 *
136 -----------------------------------------------------------------------------
137 #define "LIST" // Returns a list of files in the format of the LIST command.
138 #define "NLST" // Returns a list of filenames with no other information.
139 #define "MLSD" // Returns a list of files in the format of the MLSD command.
140
141 //---------------------------------------------------------------------------*/
142
143 method uint ftp.notify( uint code )
144 {
145 if !this.notify : return 1
146
147 if !this.notify->func( code, this.ni )
148 {
149 ineterror = $ERRINET_USERBREAK
150 // ret = 0
151 return 0
152 }
153 if code == $NFYINET_ERROR : return 0
154 return 1
155 }
156
157 method uint ftp.cmdresponse
158 {
159 uint ret i
160 buf data
161 arrstr lines
162
163 subfunc uint nodigit( ubyte ch )
164 {
165 return ch < '0' || ch > '9'
166 }
167 label again
168 this.sock.recv( data )
169 data += byte( 0 )
170 this.ni.head = data->str
171 if *data == 1 : return 0
172
173 this.ni.head.split( lines, 0xA, 0 )
174 foreach cur, lines
175 {
176 if nodigit( cur[0] ) || nodigit( cur[1] ) || nodigit( cur[2] ) ||
177 ( cur[3] != ' ' && cur[3] != '-' )
178 {
179 if cur[0] == ' ' : continue
180 ineterror = $ERRFTP_RESPONSE
181 return 0
182 }
183 ret = uint( cur )
184 }
185 if (lines[ *lines - 1 ])[ 3 ] != ' '
186 {
187 data.use--
188 goto again
189 }
190
191 this.notify( $NFYFTP_RESPONSE )
192 return ret
193 }
194
195 /*-----------------------------------------------------------------------------
196 * Id: ftp_lastresponse F2
197 *
198 * Summary: The last response from the FTP server. The method returns the
199 last response from the FTP server.
200 *
201 * Params: out - Result string.
202 *
203 * Return: #lng/retpar( out )
204 *
205 -----------------------------------------------------------------------------*/
206
207 method str ftp.lastresponse( str out )
208 {
209 return out = this.ni.head
210 }
211
212 method uint ftp.sendcmd( str cmd )
213 {
214 this.ni.head = cmd
215 if !this.ni.head.islast( 0xA ) : this.ni.head += "\l"
216
217 if !this.sock.send( this.ni.head ) : return this.notify( $NFYINET_ERROR )
218 this.notify( $NFYFTP_SENDCMD )
219
220 return 1
221 }
222
223 /*-----------------------------------------------------------------------------
224 * Id: ftp_command F2
225 *
226 * Summary: Sends a command. This methos is used to send the specified
227 command directly to an FTP server. The response from the server
228 can be received with help of the #a(ftp_lastresponse) method.
229 *
230 * Params: cmd - The command text.
231 *
232 * Return: #lng/retf#
233 *
234 -----------------------------------------------------------------------------*/
235
236 method uint ftp.command( str cmd )
237 {
238 if !this.sendcmd( cmd ) : return 0
239 return this.cmdresponse()
240 }
241
242 /*-----------------------------------------------------------------------------
243 * Id: ftp_close F3
244 *
245 * Summary: Terminates the FTP connection. The method terminates the connection
246 on the FTP server.
247 *
248 * Return: #lng/retf#
249 *
250 -----------------------------------------------------------------------------*/
251
252 method uint ftp.close()
253 {
254 uint cmd ret = 1
255
256 if this.sock.socket
257 {
258 cmd = this.command( "QUIT" )
259 if cmd && cmd != $FTP_GOODBYE
260 {
261 ineterror = $ERRFTP_QUIT
262 this.notify( $NFYINET_ERROR )
263 ret = 0
264 }
265 this.sock.close( )
266 }
267 return ret
268 }
269
270 /*-----------------------------------------------------------------------------
271 * Id: ftp_open F2
272 *
273 * Summary: Establishes an FTP connection. This method establishes an FTP
274 connection with the server. This method must be called before other
275 methods dealing with the FTP server are called.
276 *
277 * Params: url - The name or address of the FTP server.
278 user - A user name. If the string is empty, anonymous connections /
279 are used.
280 password - A user password. If the connection is anonymous, your /
281 e-mail address is required.
282 flag - Connection flags.$$[ftpflag]
283 notify - #a(inetnotify_func, Function ) is used to receive /
284 notification messages. This parameter can be zero.
285 *
286 * Return: #lng/retf#
287 *
288 -----------------------------------------------------------------------------*/
289
290 method uint ftp.open( str url, str user, str password, uint flag, uint notify )
291 {
292 uint len
293 str host
294
295 this.notify = notify
296 ineterror = 0
297 this.ni.url = url
298
299 if flag & $FTP_ANONYM : this.anonymous = 1
300 if flag & $FTP_PASV : this.passive = 1
301
302 this.notify( $NFYINET_CONNECT )
303
304 this.sock.flag |= $SOCKF_FTP
305
306 if !this.sock.urlconnect( url, host, this.path ) : goto error
307 if this.cmdresponse() != $FTP_HELLO : goto error
308 len = sizeof( sockaddr )
309
310 if getsockname( this.sock.socket, &this.local, &len )
311 {
312 inet_seterror()
313 goto error
314 }
315 if !*user || this.anonymous : user = "anonymous"
316
317 if !this.sendcmd( "USER \( user )" ) : return 0
318
319 switch this.cmdresponse( )
320 {
321 case $FTP_LOGINOK {}
322 case $FTP_LOGINBAD
323 {
324 ineterror = $ERRFTP_BADUSER
325 goto error
326 }
327 case $FTP_PASSWD
328 {
329 if !this.sendcmd( "PASS \( password )" ) : return 0
330
331 switch this.cmdresponse()
332 {
333 case $FTP_LOGINOK {}
334 case $FTP_LOGINBAD
335 {
336 ineterror = $ERRFTP_BADPSW
337 goto error
338 }
339 default
340 {
341 ineterror = $ERRFTP_RESPONSE
342 goto error
343 }
344 }
345 }
346 default
347 {
348 ineterror = $ERRFTP_RESPONSE
349 goto error
350 }
351 }
352 return 1
353
354 label error
355 this.notify( $NFYINET_ERROR )
356 this.close( )
357 return 0
358 }
359
360 method uint ftp.listen
361 {
362 uint cmd sin addr port
363 sockaddr in
364
365 if this.passive
366 {
367 cmd = this.command( "PASV" )
368 if cmd == $FTP_PASSIVEOK
369 {
370 str ports response
371 uint off till
372 arrstr portval
373
374 off = this.lastresponse( response ).findch( '(' ) + 1
375 till = response.findchfrom( ')', off )
376 ports.substr( response, off, till - off )
377 ports.split( portval, ',', 0 )
378 if *portval != 6
379 {
380 ineterror = $ERRFTP_RESPONSE
381 goto error
382 }
383 this.sockdata.host = "\(portval[0]).\(portval[1]).\(portval[2]).\(portval[3])"
384 this.sockdata.port = (uint( portval[4] ) << 8) + uint( portval[5] )
385 if this.sockdata.connect() : return 1
386 }
387 this.passive = 0
388 this.notify( $NFYFTP_NOTPASV )
389 }
390
391 this.sockserv.socket = createsocket( $AF_INET, $SOCK_STREAM, $IPPROTO_TCP )
392
393 if this.sockserv.socket == $INVALID_SOCKET : return inet_seterror()
394
395 sin = sizeof( sockaddr )
396 mcopy( &in, &this.local, sin )
397 in->sockaddr_in.sin_port = 0
398
399 if bind( this.sockserv.socket, &in, sin ) ||
400 listen( this.sockserv.socket, 1 ) ||
401 getsockname( this.sockserv.socket, &in, &sin )
402 {
403 inet_seterror()
404 goto error
405 }
406
407 addr = ntohl( this.local->sockaddr_in.sin_addr )
408 port = ntohs( in->sockaddr_in.sin_port )
409
410 if this.command( "PORT \((addr >> 24) & 0xFF ),\((addr >> 16) & 0xFF ),\((addr >> 8) & 0xFF ),\(addr & 0xFF ),\((port >> 8) & 0xFF ),\(port & 0xFF )") != $FTP_OK
411 {
412 ineterror = $ERRFTP_PORT
413 goto error
414 }
415 return 1
416
417 label error
418 this.notify( $NFYINET_ERROR )
419 return 0
420 }
421
422 method uint ftp.accept
423 {
424 if !this.passive && ( this.sockdata.socket =
425 accept( this.sockserv.socket, 0, 0 )) == $INVALID_SOCKET
426 {
427 inet_seterror()
428 this.notify( $NFYINET_ERROR )
429 return 0
430 }
431 return this.sockdata.socket
432 }
433
434 /*-----------------------------------------------------------------------------
435 * Id: ftp_list F2
436 *
437 * Summary: List of files. The method retrieves a list of files and
438 directories from the FTP server.
439 *
440 * Params: list - Result string.
441 cmd - The command is used to retrieve a list of files.$$[ftplist]
442 *
443 * Return: #lng/retf#
444 *
445 -----------------------------------------------------------------------------*/
446
447 method uint ftp.list( str data, str mode )
448 {
449 uint i dif
450
451 if !this.listen() : return 0
452
453 // if this.command( "MLSD" ) != $FTP_OPENING : return 0
454 if this.command( mode ) != $FTP_OPENING : return 0
455
456 if !this.accept() : return 0
457
458 data->buf.use = 0
459 do
460 {
461 i = *data
462 this.sockdata.recv( data->buf )
463 dif = *data - i
464 } while dif
465
466 data->buf += byte( 0 )
467
468 this.sockdata.close()
469
470 // ? надо или нет закрывать
471 if !this.passive : this.sockserv.close()
472
473 if this.cmdresponse() != $FTP_ENDTRAN
474 {
475 ineterror = $ERRFTP_RESPONSE
476 this.notify( $NFYINET_ERROR )
477 return 0
478 }
479 return 1
480 }
481
482 /*-----------------------------------------------------------------------------
483 * Id: ftp_createdir F2
484 *
485 * Summary: Creates a new directory. The method creates a new directory on
486 the FTP server.
487 *
488 * Params: dirname - The name of the directory
489 *
490 * Return: #lng/retf#
491 *
492 -----------------------------------------------------------------------------*/
493
494 method uint ftp.createdir( str dirname )
495 {
496 return this.command("MKD \(dirname)") == $FTP_MKDIROK
497 }
498
499 /*-----------------------------------------------------------------------------
500 * Id: ftp_deldir F2
501 *
502 * Summary: Deletes a directory. This method deletes a directory stored on
503 the FTP server.
504 *
505 * Params: dirname - The name of the required directory
506 *
507 * Return: #lng/retf#
508 *
509 -----------------------------------------------------------------------------*/
510
511 method uint ftp.deldir( str dirname )
512 {
513 return this.command("RMD \(dirname)") == $FTP_CWDOK
514 }
515
516 /*-----------------------------------------------------------------------------
517 * Id: ftp_delfile F2
518 *
519 * Summary: Deletes a file. The method deletes a file stored on the FTP server.
520 *
521 * Params: filename - The name of the required file.
522 *
523 * Return: #lng/retf#
524 *
525 -----------------------------------------------------------------------------*/
526
527 method uint ftp.delfile( str filename )
528 {
529 return this.command("DELE \(filename)") == $FTP_CWDOK
530 }
531
532 /*-----------------------------------------------------------------------------
533 * Id: ftp_setcurdir F2
534 *
535 * Summary: Sets the current directory. This method sets a new current
536 directory.
537 *
538 * Params: dirname - The name of a new directory.
539 *
540 * Return: #lng/retf#
541 *
542 -----------------------------------------------------------------------------*/
543
544 method uint ftp.setcurdir( str dirname )
545 {
546 return this.command("CWD \(dirname)") == $FTP_CWDOK
547 }
548
549 /*-----------------------------------------------------------------------------
550 * Id: ftp_getcurdir F2
551 *
552 * Summary: Retrieves the current directory. The method retrieves the current
553 directory name from the FTP server.
554 *
555 * Params: dirname - Result string.
556 *
557 * Return: #lng/retf#
558 *
559 -----------------------------------------------------------------------------*/
560
561 method uint ftp.getcurdir( str dirname )
562 {
563 str response
564 uint off till
565
566 dirname.clear()
567 if this.command("PWD") != $FTP_MKDIROK : return 0
568
569 off = this.lastresponse( response ).findch( '"' ) + 1
570 till = response.findchfrom( '"', off )
571 dirname.substr( response, off, till - off )
572 if !*dirname : return 0
573
574 return 1
575 }
576
577 /*-----------------------------------------------------------------------------
578 * Id: ftp_rename F2
579 *
580 * Summary: Renames a file. This method renames a file or directory stored
581 on the FTP server.
582 *
583 * Params: from - The current name of the file or directory.
584 to - A new name.
585 *
586 * Return: #lng/retf#
587 *
588 -----------------------------------------------------------------------------*/
589
590 method uint ftp.rename( str from, str to )
591 {
592 if this.command("RNFR \(from)") != $FTP_FILEOK : return 0
593 return this.command("RNTO \(to)") == $FTP_CWDOK
594 }
595
596 /*-----------------------------------------------------------------------------
597 * Id: ftp_getsize F2
598 *
599 * Summary: Retrieves the file size from the FTP server.
600 *
601 * Params: name - Filename.
602 psize - A pointer to uint value is used to store the file size.
603 *
604 * Return: #lng/retf#
605 *
606 -----------------------------------------------------------------------------*/
607
608 method uint ftp.getsize( str name, uint psize )
609 {
610 str size
611
612 psize->uint = 0
613 if this.command("SIZE \(name)") != $FTP_FILESTAT : return 0
614
615 this.lastresponse( size ).del( 0, 4 )
616 psize->uint = uint( size )
617 return 1
618 }
619
620 /*-----------------------------------------------------------------------------
621 * Id: ftp_gettime F2
622 *
623 * Summary: Retrieves the file time. Retrieves last write times for the file
624 on the FTP server.
625 *
626 * Params: name - Filename.
627 dt - The variable of #a( tdatetime ) type is used to retrieve the /
628 file time.
629 *
630 * Return: #lng/retf#
631 *
632 -----------------------------------------------------------------------------*/
633
634 method uint ftp.gettime( str name, datetime dt )
635 {
636 str time year month day
637
638 mzero( &dt, sizeof( datetime ))
639 if this.command("MDTM \(name)") != $FTP_FILESTAT : return 0
640
641 this.lastresponse( time ).del( 0, 4 )
642 time.trimspace()
643 year.substr( time, 0, 4 )
644 month.substr( time, 4, 2 )
645 day.substr( time, 6, 2 )
646 dt.setdate( uint( day ), uint( month ), uint( year ))
647 dt.hour = uint( year.substr( time, 8, 2 ))
648 dt.minute = uint( month.substr( time, 10, 2 ))
649 dt.second = uint( day.substr( time, 12, 2 ))
650
651 return 1
652 }
653
654 /*-----------------------------------------------------------------------------
655 * Id: ftp_setattrib F2
656 *
657 * Summary: Sets the attributes. This method sets the attributes for the file
658 or the directory.
659 *
660 * Params: name - The name of a file or directory.
661 mode - The attributes for the file.
662 *
663 * Return: #lng/retf#
664 *
665 -----------------------------------------------------------------------------*/
666
667 method uint ftp.setattrib( str name, uint mode )
668 {
669 // str chmode
670
671 mode &= $S_IRWXU | $S_IRWXG | $S_IRWXO
672
673 if this.command("SITE CHMOD \(hex2strl( mode)) \(name)") != $FTP_OK
674 {
675 return 0
676 }
677 return 1
678 }
679
680 /*-----------------------------------------------------------------------------
681 * Id: ftp_getfile F2
682 *
683 * Summary: Retrieves a file. The method retrieves files from the FTP server.
684 *
685 * Params: filename - The downloaded file name.
686 databuf - The received data buffer. Data are not stored on a drive.
687 flag - Additional flags. $$[ftpget]$$[ftpgetbuf]
688 *
689 * Return: #lng/retf#
690 *
691 -----------------------------------------------------------------------------*/
692
693 method uint ftp.getfile( str filename, buf databuf, uint flag )
694 {
695 uint data dif i
696 buf fbuf
697 uint ret range
698 file fdwn
699 uint isfile = flag & $FTP_FILE
700 finfo fi
701
702 if this.command( "TYPE \(?( flag & $FTP_BINARY, "I", "A" ))") != $FTP_OK
703 {
704 return 0
705 }
706 if !this.listen() : return 0
707
708 this.ni.param = 0
709
710 if isfile && flag & $FTP_CONTINUE
711 {
712 getfileinfo( databuf->str, fi )
713 if fi.sizelo
714 {
715 if this.command( "REST \(fi.sizelo)" ) != $FTP_FILEOK
716 {
717 flag &= ~$FTP_CONTINUE
718 }
719 else : this.ni.param = fi.sizelo
720 }
721 }
722 data = ?( isfile, &fbuf, &databuf )
723 data as buf
724
725 if isfile
726 {
727 data.expand( 0x20000 )
728 if !( fdwn.open( databuf->str, ?( flag & $FTP_CONTINUE,
729 $OP_ALWAYS, $OP_CREATE )))
730 {
731 this.ni.sparam = databuf->str
732 ineterror = $ERRINET_OPENFILE
733 this.notify( $NFYINET_ERROR )
734 goto end
735 }
736 if this.ni.param : fdwn.setpos( 0, $FILE_END )
737 }
738 else
739 {
740 uint fullsize
741
742 data.clear()
743 this.getsize( filename, &fullsize )
744 if uint( fullsize ) : data.expand( uint( fullsize ) + 0x7FFF )
745 }
746
747 if this.command( "RETR \(filename)" ) != $FTP_OPENING : return 0
748
749 if !this.accept() : return 0
750
751 do
752 {
753 i = *data
754 this.sockdata.recv( data )
755
756 dif = *data - i
757 this.ni.param += dif
758
759 if isfile && ( *data >= 0x7FFF || !dif )
760 {
761 if !( fdwn.write( data ))
762 {
763 this.ni.sparam = databuf->str
764 ineterror = $ERRINET_WRITEFILE
765 this.notify( $NFYINET_ERROR )
766 goto end
767 }
768 if !this.notify( $NFYINET_GET )
769 {
770 this.sockdata.close()
771 // this.command("ABOR")
772 goto end
773 }
774 data.use = 0
775 }
776 } while dif
777
778 if flag & $FTP_STR : data += byte( 0 )
779 this.notify( $NFYINET_END )
780 this.sockdata.close()
781
782 if this.cmdresponse() != $FTP_ENDTRAN
783 {
784 ineterror = $ERRFTP_RESPONSE
785 goto end
786 }
787
788 ret = 1
789 label end
790 if fdwn.fopen
791 {
792 if ret && flag & $FTP_SETTIME
793 {
794 filetime ft
795 datetime dt
796
797 if this.gettime( filename, dt )
798 {
799 datetimetoftime( dt, ft )
800 fdwn.settime( ft )
801 }
802 }
803 fdwn.close( )
804 }
805 if !ret && ineterror : this.notify( $NFYINET_ERROR )
806
807 return ret
808 }
809
810 /*-----------------------------------------------------------------------------
811 * Id: ftp_getfile_1 FA
812 *
813 * Summary: The method retrieves files from the FTP server.
814 *
815 * Params: srcname - The downloaded file name.
816 destname - A new file name on user's machine.
817 flag - Flags.$$[ftpget]$$[ftpgetfile]
818 *
819 * Return: #lng/retf#
820 *
821 -----------------------------------------------------------------------------*/
822
823 method uint ftp.getfile( str srcname, str destname, uint flag )
824 {
825 flag &= 0xF0FF
826 return this.getfile( srcname, destname->buf, flag | $FTP_FILE )
827 }
828
829 /*-----------------------------------------------------------------------------
830 ** Id: ftp_putfile F2
831 *
832 * Summary: Stores a file on the FTP server. This method is used to upload
833 the required file from the remote host to the FTP server.
834 *
835 * Params: srcname - The name of the required source file.
836 destname - The name of a file stored on the FTP server.
837 flag - Flags. If the flag of the binary or text mode is not /
838 specified, the method makes effort to determine a file /
839 type. $$[ftpput]
840 *
841 * Return: #lng/retf#
842 *
843 -----------------------------------------------------------------------------*/
844
845 method uint ftp.putfile( str srcname, str destname, uint flag )
846 {
847 uint offset size cur ret i
848 file fdwn
849 buf data
850 str tmode
851
852 subfunc uint nextread
853 {
854 cur = min( size, 0x20000 )
855 data.use = 0
856 if fdwn.read( data, cur ) != cur
857 {
858 this.ni.sparam = srcname
859 ineterror = $ERRINET_READFILE
860 return 0
861 }
862 size -= cur
863 return 1
864 }
865
866 if !( fdwn.open( srcname, $OP_READONLY ))
867 {
868 this.ni.sparam = srcname
869 ineterror = $ERRINET_OPENFILE
870 this.notify( $NFYINET_ERROR )
871 return 0
872 }
873 size = fdwn.getsize( )
874 if flag & $FTP_CONTINUE
875 {
876 if !this.getsize( destname, &offset ) || offset >= size
877 {
878 flag &= ~$FTP_CONTINUE
879 }
880 else
881 {
882 this.ni.param = offset
883 size -= offset
884 fdwn.setpos( offset, $FILE_BEGIN )
885 }
886 }
887 data.expand( 0x20000 )
888 if !nextread() : goto end
889
890 tmode = "TYPE A"
891 if flag & $FTP_BINARY : tmode = "TYPE I"
892 elif !( flag & $FTP_TEXT )
893 {
894 fornum i = 0, *data - 1
895 {
896 if !data[i] || ( data[i] == 0xD && data[i + 1] != 0xA )
897 {
898 tmode = "TYPE I"
899 break
900 }
901 }
902 }
903 if this.command( tmode ) != $FTP_OK : goto close
904 if !this.listen() : goto close
905
906 if this.command( "\(?( flag & $FTP_CONTINUE, "APPE",
907 "STOR")) \( destname )") != $FTP_OPENING : goto close
908
909 if !this.accept() : goto close
910
911 while 1
912 {
913 uint off last
914 uint sent
915
916 last = *data
917
918 while last
919 {
920 sent = send( this.sockdata.socket, data.ptr() + off, min( 0x7FFF, last ),
921 0 )
922 if sent == $SOCKET_ERROR
923 {
924 inet_seterror()
925 this.sockdata.close()
926 goto close
927 }
928 else
929 {
930 this.ni.param += sent
931 if !this.notify( $NFYINET_PUT )
932 {
933 this.sockdata.close()
934 goto close
935 }
936 }
937 last -= sent
938 off += sent
939 }
940 // this.sockdata.send( data )
941 if !size : break
942 nextread()
943 }
944 this.notify( $NFYINET_END )
945 this.sockdata.close()
946 if this.cmdresponse() != $FTP_ENDTRAN
947 {
948 ineterror = $ERRFTP_RESPONSE
949 goto end
950 }
951 ret = 1
952 label end
953 if !ret && ineterror : this.notify( $NFYINET_ERROR )
954 label close
955 fdwn.close( )
956
957 return ret
958 }
959