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
172 if *data == 1 : return 0
173
174 this.ni.head.split( lines, 0xA, 0 )
175 foreach cur, lines
176 {
177 if nodigit( cur[0] ) || nodigit( cur[1] ) || nodigit( cur[2] ) ||
178 ( cur[3] != ' ' && cur[3] != '-' )
179 {
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, str 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
316 if !*user || this.anonymous : user = "anonymous"
317
318 if !this.sendcmd( "USER \( user )" ) : return 0
319
320 switch this.cmdresponse( )
321 {
322 case $FTP_LOGINOK {}
323 case $FTP_LOGINBAD
324 {
325 ineterror = $ERRFTP_BADUSER
326 goto error
327 }
328 case $FTP_PASSWD
329 {
330 if !this.sendcmd( "PASS \( password )" ) : return 0
331
332 switch this.cmdresponse()
333 {
334 case $FTP_LOGINOK {}
335 case $FTP_LOGINBAD
336 {
337 ineterror = $ERRFTP_BADPSW
338 goto error
339 }
340 default
341 {
342 ineterror = $ERRFTP_RESPONSE
343 goto error
344 }
345 }
346 }
347 default
348 {
349 ineterror = $ERRFTP_RESPONSE
350 goto error
351 }
352 }
353 return 1
354
355 label error
356 this.notify( $NFYINET_ERROR )
357 this.close( )
358 return 0
359 }
360
361 method uint ftp.listen
362 {
363 uint cmd sin addr port
364 sockaddr in
365
366 if this.passive
367 {
368 cmd = this.command( "PASV" )
369 if cmd == $FTP_PASSIVEOK
370 {
371 str ports response
372 uint off till
373 arrstr portval
374
375 off = this.lastresponse( response ).findch( '(', 0 ) + 1
376 till = response.findchfrom( ')', off )
377 ports.substr( response, off, till - off )
378 ports.split( portval, ',', 0 )
379 if *portval != 6
380 {
381 ineterror = $ERRFTP_RESPONSE
382 goto error
383 }
384 this.sockdata.host = "\(portval[0]).\(portval[1]).\(portval[2]).\(portval[3])"
385 this.sockdata.port = (uint( portval[4] ) << 8) + uint( portval[5] )
386 if this.sockdata.connect() : return 1
387 }
388 this.passive = 0
389 this.notify( $NFYFTP_NOTPASV )
390 }
391
392 this.sockserv.socket = createsocket( $AF_INET, $SOCK_STREAM, $IPPROTO_TCP )
393
394 if this.sockserv.socket == $INVALID_SOCKET : return inet_seterror()
395
396 sin = sizeof( sockaddr )
397 mcopy( &in, &this.local, sin )
398 in->sockaddr_in.sin_port = 0
399
400 if bind( this.sockserv.socket, &in, sin ) ||
401 listen( this.sockserv.socket, 1 ) ||
402 getsockname( this.sockserv.socket, &in, &sin )
403 {
404 inet_seterror()
405 goto error
406 }
407
408 addr = ntohl( this.local->sockaddr_in.sin_addr )
409 port = ntohs( in->sockaddr_in.sin_port )
410
411 if this.command( "PORT \((addr >> 24) & 0xFF ),\((addr >> 16) & 0xFF ),\((addr >> 8) & 0xFF ),\(addr & 0xFF ),\((port >> 8) & 0xFF ),\(port & 0xFF )") != $FTP_OK
412 {
413 ineterror = $ERRFTP_PORT
414 goto error
415 }
416 return 1
417
418 label error
419 this.notify( $NFYINET_ERROR )
420 return 0
421 }
422
423 method uint ftp.accept
424 {
425 if !this.passive && ( this.sockdata.socket =
426 accept( this.sockserv.socket, 0, 0 )) == $INVALID_SOCKET
427 {
428 inet_seterror()
429 this.notify( $NFYINET_ERROR )
430 return 0
431 }
432 return this.sockdata.socket
433 }
434
435 /*-----------------------------------------------------------------------------
436 * Id: ftp_list F2
437 *
438 * Summary: List of files. The method retrieves a list of files and
439 directories from the FTP server.
440 *
441 * Params: list - Result string.
442 cmd - The command is used to retrieve a list of files.$$[ftplist]
443 *
444 * Return: #lng/retf#
445 *
446 -----------------------------------------------------------------------------*/
447
448 method uint ftp.list( str data, str mode )
449 {
450 uint i dif
451
452 if !this.listen() : return 0
453
454 // if this.command( "MLSD" ) != $FTP_OPENING : return 0
455 if this.command( mode ) != $FTP_OPENING : return 0
456
457 if !this.accept() : return 0
458
459 data->buf.use = 0
460 do
461 {
462 i = *data
463 this.sockdata.recv( data->buf )
464 dif = *data - i
465 } while dif
466
467 data->buf += byte( 0 )
468
469 this.sockdata.close()
470
471 // ? надо или нет закрывать
472 if !this.passive : this.sockserv.close()
473
474 if this.cmdresponse() != $FTP_ENDTRAN
475 {
476 ineterror = $ERRFTP_RESPONSE
477 this.notify( $NFYINET_ERROR )
478 return 0
479 }
480 return 1
481 }
482
483 /*-----------------------------------------------------------------------------
484 * Id: ftp_createdir F2
485 *
486 * Summary: Creates a new directory. The method creates a new directory on
487 the FTP server.
488 *
489 * Params: dirname - The name of the directory
490 *
491 * Return: #lng/retf#
492 *
493 -----------------------------------------------------------------------------*/
494
495 method uint ftp.createdir( str dirname )
496 {
497 return this.command("MKD \(dirname)") == $FTP_MKDIROK
498 }
499
500 /*-----------------------------------------------------------------------------
501 * Id: ftp_deldir F2
502 *
503 * Summary: Deletes a directory. This method deletes a directory stored on
504 the FTP server.
505 *
506 * Params: dirname - The name of the required directory
507 *
508 * Return: #lng/retf#
509 *
510 -----------------------------------------------------------------------------*/
511
512 method uint ftp.deldir( str dirname )
513 {
514 return this.command("RMD \(dirname)") == $FTP_CWDOK
515 }
516
517 /*-----------------------------------------------------------------------------
518 * Id: ftp_delfile F2
519 *
520 * Summary: Deletes a file. The method deletes a file stored on the FTP server.
521 *
522 * Params: filename - The name of the required file.
523 *
524 * Return: #lng/retf#
525 *
526 -----------------------------------------------------------------------------*/
527
528 method uint ftp.delfile( str filename )
529 {
530 return this.command("DELE \(filename)") == $FTP_CWDOK
531 }
532
533 /*-----------------------------------------------------------------------------
534 * Id: ftp_setcurdir F2
535 *
536 * Summary: Sets the current directory. This method sets a new current
537 directory.
538 *
539 * Params: dirname - The name of a new directory.
540 *
541 * Return: #lng/retf#
542 *
543 -----------------------------------------------------------------------------*/
544
545 method uint ftp.setcurdir( str dirname )
546 {
547 return this.command("CWD \(dirname)") == $FTP_CWDOK
548 }
549
550 /*-----------------------------------------------------------------------------
551 * Id: ftp_getcurdir F2
552 *
553 * Summary: Retrieves the current directory. The method retrieves the current
554 directory name from the FTP server.
555 *
556 * Params: dirname - Result string.
557 *
558 * Return: #lng/retf#
559 *
560 -----------------------------------------------------------------------------*/
561
562 method uint ftp.getcurdir( str dirname )
563 {
564 str response
565 uint off till
566
567 dirname.clear()
568 if this.command("PWD") != $FTP_MKDIROK : return 0
569
570 off = this.lastresponse( response ).findch( '"', 0 ) + 1
571 till = response.findchfrom( '"', off )
572 dirname.substr( response, off, till - off )
573 if !*dirname : return 0
574
575 return 1
576 }
577
578 /*-----------------------------------------------------------------------------
579 * Id: ftp_rename F2
580 *
581 * Summary: Renames a file. This method renames a file or directory stored
582 on the FTP server.
583 *
584 * Params: from - The current name of the file or directory.
585 to - A new name.
586 *
587 * Return: #lng/retf#
588 *
589 -----------------------------------------------------------------------------*/
590
591 method uint ftp.rename( str from, str to )
592 {
593 if this.command("RNFR \(from)") != $FTP_FILEOK : return 0
594 return this.command("RNTO \(to)") == $FTP_CWDOK
595 }
596
597 /*-----------------------------------------------------------------------------
598 * Id: ftp_getsize F2
599 *
600 * Summary: Retrieves the file size from the FTP server.
601 *
602 * Params: name - Filename.
603 psize - A pointer to uint value is used to store the file size.
604 *
605 * Return: #lng/retf#
606 *
607 -----------------------------------------------------------------------------*/
608
609 method uint ftp.getsize( str name, uint psize )
610 {
611 str size
612
613 psize->uint = 0
614 if this.command("SIZE \(name)") != $FTP_FILESTAT : return 0
615
616 this.lastresponse( size ).del( 0, 4 )
617 psize->uint = uint( size )
618 return 1
619 }
620
621 /*-----------------------------------------------------------------------------
622 * Id: ftp_gettime F2
623 *
624 * Summary: Retrieves the file time. Retrieves last write times for the file
625 on the FTP server.
626 *
627 * Params: name - Filename.
628 dt - The variable of #a( tdatetime ) type is used to retrieve the /
629 file time.
630 *
631 * Return: #lng/retf#
632 *
633 -----------------------------------------------------------------------------*/
634
635 method uint ftp.gettime( str name, datetime dt )
636 {
637 str time year month day
638
639 mzero( &dt, sizeof( datetime ))
640 if this.command("MDTM \(name)") != $FTP_FILESTAT : return 0
641
642 this.lastresponse( time ).del( 0, 4 )
643 time.trimspace()
644 year.substr( time, 0, 4 )
645 month.substr( time, 4, 2 )
646 day.substr( time, 6, 2 )
647 dt.setdate( uint( day ), uint( month ), uint( year ))
648 dt.hour = uint( year.substr( time, 8, 2 ))
649 dt.minute = uint( month.substr( time, 10, 2 ))
650 dt.second = uint( day.substr( time, 12, 2 ))
651
652 return 1
653 }
654
655 /*-----------------------------------------------------------------------------
656 * Id: ftp_setattrib F2
657 *
658 * Summary: Sets the attributes. This method sets the attributes for the file
659 or the directory.
660 *
661 * Params: name - The name of a file or directory.
662 mode - The attributes for the file.
663 *
664 * Return: #lng/retf#
665 *
666 -----------------------------------------------------------------------------*/
667
668 method uint ftp.setattrib( str name, uint mode )
669 {
670 // str chmode
671
672 mode &= $S_IRWXU | $S_IRWXG | $S_IRWXO
673
674 if this.command("SITE CHMOD \(hex2strl( mode)) \(name)") != $FTP_OK
675 {
676 return 0
677 }
678 return 1
679 }
680
681 /*-----------------------------------------------------------------------------
682 * Id: ftp_getfile F2
683 *
684 * Summary: Retrieves a file. The method retrieves files from the FTP server.
685 *
686 * Params: filename - The downloaded file name.
687 databuf - The received data buffer. Data are not stored on a drive.
688 flag - Additional flags. $$[ftpget]$$[ftpgetbuf]
689 *
690 * Return: #lng/retf#
691 *
692 -----------------------------------------------------------------------------*/
693
694 method uint ftp.getfile( str filename, buf databuf, uint flag )
695 {
696 uint data dif i
697 buf fbuf
698 uint ret range
699 file fdwn
700 uint isfile = flag & $FTP_FILE
701 finfo fi
702
703 if this.command( "TYPE \(?( flag & $FTP_BINARY, "I", "A" ))") != $FTP_OK
704 {
705 return 0
706 }
707 if !this.listen() : return 0
708
709 this.ni.param = 0
710
711 if isfile && flag & $FTP_CONTINUE
712 {
713 getfileinfo( databuf->str, fi )
714 if fi.sizelo
715 {
716 if this.command( "REST \(fi.sizelo)" ) != $FTP_FILEOK
717 {
718 flag &= ~$FTP_CONTINUE
719 }
720 else : this.ni.param = fi.sizelo
721 }
722 }
723 data = ?( isfile, &fbuf, &databuf )
724 data as buf
725
726 if isfile
727 {
728 data.expand( 0x20000 )
729 if !( fdwn.open( databuf->str, ?( flag & $FTP_CONTINUE,
730 $OP_ALWAYS, $OP_CREATE )))
731 {
732 this.ni.sparam = databuf->str
733 ineterror = $ERRINET_OPENFILE
734 this.notify( $NFYINET_ERROR )
735 goto end
736 }
737 if this.ni.param : fdwn.setpos( 0, $FILE_END )
738 }
739 else
740 {
741 uint fullsize
742
743 data.clear()
744 this.getsize( filename, &fullsize )
745 if uint( fullsize ) : data.expand( uint( fullsize ) + 0x7FFF )
746 }
747
748 if this.command( "RETR \(filename)" ) != $FTP_OPENING : return 0
749
750 if !this.accept() : return 0
751
752 do
753 {
754 i = *data
755 if !this.notify( $NFYINET_GET )
756 {
757 this.sockdata.close()
758 // this.command("ABOR")
759 goto end
760 }
761 this.sockdata.recv( data )
762
763 dif = *data - i
764 this.ni.param += dif
765
766 if isfile && ( *data >= 0x7FFF || !dif )
767 {
768 if !( fdwn.write( data ))
769 {
770 this.ni.sparam = databuf->str
771 ineterror = $ERRINET_WRITEFILE
772 this.notify( $NFYINET_ERROR )
773 goto end
774 }
775 data.use = 0
776 }
777 } while dif
778
779 if flag & $FTP_STR : data += byte( 0 )
780 this.notify( $NFYINET_END )
781 this.sockdata.close()
782
783 if this.cmdresponse() != $FTP_ENDTRAN
784 {
785 ineterror = $ERRFTP_RESPONSE
786 goto end
787 }
788
789 ret = 1
790 label end
791 if fdwn.fopen
792 {
793 if ret && flag & $FTP_SETTIME
794 {
795 filetime ft
796 datetime dt
797
798 if this.gettime( filename, dt )
799 {
800 datetimetoftime( dt, ft )
801 fdwn.settime( ft )
802 }
803 }
804 fdwn.close( )
805 }
806 if !ret && ineterror : this.notify( $NFYINET_ERROR )
807
808 return ret
809 }
810
811 /*-----------------------------------------------------------------------------
812 * Id: ftp_getfile_1 FA
813 *
814 * Summary: The method retrieves files from the FTP server.
815 *
816 * Params: srcname - The downloaded file name.
817 destname - A new file name on user's machine.
818 flag - Flags.$$[ftpget]$$[ftpgetfile]
819 *
820 * Return: #lng/retf#
821 *
822 -----------------------------------------------------------------------------*/
823
824 method uint ftp.getfile( str srcname, str destname, uint flag )
825 {
826 flag &= 0xF0FF
827 return this.getfile( srcname, destname->buf, flag | $FTP_FILE )
828 }
829
830 /*-----------------------------------------------------------------------------
831 ** Id: ftp_putfile F2
832 *
833 * Summary: Stores a file on the FTP server. This method is used to upload
834 the required file from the remote host to the FTP server.
835 *
836 * Params: srcname - The name of the required source file.
837 destname - The name of a file stored on the FTP server.
838 flag - Flags. If the flag of the binary or text mode is not /
839 specified, the method makes effort to determine a file /
840 type. $$[ftpput]
841 *
842 * Return: #lng/retf#
843 *
844 -----------------------------------------------------------------------------*/
845
846 method uint ftp.putfile( str srcname, str destname, uint flag )
847 {
848 uint offset size cur ret i
849 file fdwn
850 buf data
851 str tmode
852
853 subfunc uint nextread
854 {
855 cur = min( size, 0x20000 )
856 data.use = 0
857 if fdwn.read( data, cur ) != cur
858 {
859 this.ni.sparam = srcname
860 ineterror = $ERRINET_READFILE
861 return 0
862 }
863 size -= cur
864 return 1
865 }
866
867 if !( fdwn.open( srcname, $OP_READONLY ))
868 {
869 this.ni.sparam = srcname
870 ineterror = $ERRINET_OPENFILE
871 this.notify( $NFYINET_ERROR )
872 return 0
873 }
874 size = fdwn.getsize( )
875 if flag & $FTP_CONTINUE
876 {
877 if !this.getsize( destname, &offset ) || offset >= size
878 {
879 flag &= ~$FTP_CONTINUE
880 }
881 else
882 {
883 this.ni.param = offset
884 size -= offset
885 fdwn.setpos( offset, $FILE_BEGIN )
886 }
887 }
888 data.expand( 0x20000 )
889 if !nextread() : goto end
890
891 tmode = "TYPE A"
892 if flag & $FTP_BINARY : tmode = "TYPE I"
893 elif !( flag & $FTP_TEXT )
894 {
895 fornum i = 0, *data - 1
896 {
897 if !data[i] || ( data[i] == 0xD && data[i + 1] != 0xA )
898 {
899 tmode = "TYPE I"
900 break
901 }
902 }
903 }
904 if this.command( tmode ) != $FTP_OK : goto close
905 if !this.listen() : goto close
906
907 if this.command( "\(?( flag & $FTP_CONTINUE, "APPE",
908 "STOR")) \( destname )") != $FTP_OPENING : goto close
909
910 if !this.accept() : goto close
911
912 while 1
913 {
914 uint off last
915 uint sent
916
917 last = *data
918
919 while last
920 {
921 sent = send( this.sockdata.socket, data.ptr() + off, min( 0x7FFF, last ),
922 0 )
923 if sent == $SOCKET_ERROR
924 {
925 inet_seterror()
926 this.sockdata.close()
927 goto close
928 }
929 else
930 {
931 this.ni.param += sent
932 if !this.notify( $NFYINET_PUT )
933 {
934 this.sockdata.close()
935 goto close
936 }
937 }
938 last -= sent
939 off += sent
940 }
941 // this.sockdata.send( data )
942 if !size : break
943 nextread()
944 }
945 this.notify( $NFYINET_END )
946 this.sockdata.close()
947 if this.cmdresponse() != $FTP_ENDTRAN
948 {
949 ineterror = $ERRFTP_RESPONSE
950 goto end
951 }
952 ret = 1
953 label end
954 if !ret && ineterror : this.notify( $NFYINET_ERROR )
955 label close
956 fdwn.close( )
957
958 return ret
959 }