1 /*****************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * The contents of this file are subject to the Mozilla Public License
9 * Version 1.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS"
14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
15 * License for the specific language governing rights and limitations
18 * The Original Code is Curl.
20 * The Initial Developer of the Original Code is Daniel Stenberg.
22 * Portions created by the Initial Developer are Copyright (C) 1998.
23 * All Rights Reserved.
25 * ------------------------------------------------------------
27 * - Daniel Stenberg <Daniel.Stenberg@haxx.nu>
31 * $Source: /cvsroot/curl/curl/lib/ftp.c,v $
33 * $Date: 1999-12-29 14:21:24 $
38 * ------------------------------------------------------------
39 ****************************************************************************/
52 #ifdef HAVE_SYS_SELECT_H
53 #include <sys/select.h>
56 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
58 #else /* some kind of unix */
59 #include <sys/socket.h>
60 #include <netinet/in.h>
61 #ifdef HAVE_ARPA_INET_H
62 #include <arpa/inet.h>
64 #include <sys/utsname.h>
68 #if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
73 #include <curl/curl.h>
84 /* returns last node in linked list */
85 static struct curl_slist
*slist_get_last(struct curl_slist
*list
)
87 struct curl_slist
*item
;
89 /* if caller passed us a NULL, return now */
93 /* loop through to find the last item */
101 /* append a struct to the linked list. It always retunrs the address of the
102 * first record, so that you can sure this function as an initialization
103 * function as well as an append function. If you find this bothersome,
104 * then simply create a separate _init function and call it appropriately from
105 * within the proram. */
106 struct curl_slist
*curl_slist_append(struct curl_slist
*list
, char *data
)
108 struct curl_slist
*last
;
109 struct curl_slist
*new_item
;
111 new_item
= (struct curl_slist
*) malloc(sizeof(struct curl_slist
));
113 new_item
->next
= NULL
;
114 new_item
->data
= strdup(data
);
117 fprintf(stderr
, "Cannot allocate memory for QUOTE list.\n");
122 last
= slist_get_last(list
);
123 last
->next
= new_item
;
127 /* if this is the first item, then new_item *is* the list */
131 /* be nice and clean up resources */
132 void curl_slist_free_all(struct curl_slist
*list
)
134 struct curl_slist
*next
;
135 struct curl_slist
*item
;
153 static UrgError
AllowServerConnect(struct UrlData
*data
,
161 FD_SET(sock
, &rdset
);
163 /* we give the server 10 seconds to connect to us */
167 switch ( select(sock
+1, &rdset
, NULL
, NULL
, &dt
)) {
170 failf(data
, "Error while waiting for server connect");
171 return URG_FTP_PORT_FAILED
;
172 case 0: /* timeout */
174 failf(data
, "Timeout while waiting for server connect");
175 return URG_FTP_PORT_FAILED
;
177 /* we have received data here */
180 size_t size
= sizeof(struct sockaddr_in
);
181 struct sockaddr_in add
;
183 getsockname(sock
, (struct sockaddr
*) &add
, (int *)&size
);
184 s
=accept(sock
, (struct sockaddr
*) &add
, (int *)&size
);
188 failf(data
, "Error accept()ing server connect");
189 return URG_FTP_PORT_FAILED
;
191 infof(data
, "Connection accepted from server\n");
193 data
->secondarysocket
= s
;
201 /* --- parse FTP server responses --- */
203 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
204 isdigit((int)line[2]) && (' ' == line[3]))
206 static int GetLastResponse(int sockfd
, char *buf
,
207 struct UrlData
*data
)
215 /* get us a full line, terminated with a newline */
217 (nread
<BUFSIZE
) && read_rc
;
221 read_rc
= SSL_read(data
->ssl
, ptr
, 1);
225 read_rc
= sread(sockfd
, ptr
, 1);
228 #endif /* USE_SSLEAY */
232 *ptr
=0; /* zero terminate */
234 if(data
->conf
& CONF_VERBOSE
) {
235 fputs("< ", data
->err
);
236 fwrite(buf
, 1, nread
, data
->err
);
237 fputs("\n", data
->err
);
240 (nread
<4 || !lastline(buf
)) );
244 /* -- who are we? -- */
245 char *getmyhost(void)
247 static char myhost
[256];
248 #if !defined(WIN32) && !defined(HAVE_UNAME) && !defined(HAVE_GETHOSTNAME)
249 /* We have no means of finding the local host name! */
250 strcpy(myhost
, "localhost");
252 #if defined(WIN32) || !defined(HAVE_UNAME)
253 gethostname(myhost
, 256);
257 if (uname(&ugnm
) < 0)
260 (void) strncpy(myhost
, ugnm
.nodename
, 255);
270 * This function returns a string converted FROM the input URL format to a
271 * format that is more likely usable for the remote server. That is, all
272 * special characters (found as %XX-codes) will be eascaped with \<letter>.
275 static char *URLfix(char *string
)
277 /* The length of the new string can't be longer than twice the original
278 string, if all letters are '+'... */
279 int alloc
= strlen(string
)*2;
280 char *ns
= malloc(alloc
);
296 if(sscanf(string
+1, "%02X", &hex
)) {
308 ns
[index
]=0; /* terminate it */
314 UrgError
_ftp(struct UrlData
*data
,
320 /* this is FTP and no proxy */
323 char *buf
= data
->buffer
; /* this is our buffer */
324 /* for the ftp PORT mode */
326 struct sockaddr_in serv_addr
;
328 struct curl_slist
*qitem
; /* QUOTE item */
330 /* The first thing we do is wait for the "220*" line: */
331 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
332 if(strncmp(buf
, "220", 3)) {
333 failf(data
, "This doesn't seem like a nice ftp-server response");
334 return URG_FTP_WEIRD_SERVER_REPLY
;
338 sendf(data
->firstsocket
, data
, "USER %s\r\n", ftpuser
);
340 /* wait for feedback */
341 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
343 if(!strncmp(buf
, "530", 3)) {
344 /* 530 User ... access denied
345 (the server denies to log the specified user) */
346 failf(data
, "Access denied: %s", &buf
[4]);
347 return URG_FTP_ACCESS_DENIED
;
349 else if(!strncmp(buf
, "331", 3)) {
350 /* 331 Password required for ...
351 (the server requires to send the user's password too) */
352 sendf(data
->firstsocket
, data
, "PASS %s\r\n", ftppasswd
);
353 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
355 if(!strncmp(buf
, "530", 3)) {
356 /* 530 Login incorrect.
357 (the username and/or the password are incorrect) */
358 failf(data
, "the username and/or the password are incorrect");
359 return URG_FTP_USER_PASSWORD_INCORRECT
;
361 else if(!strncmp(buf
, "230", 3)) {
362 /* 230 User ... logged in.
363 (user successfully logged in) */
365 infof(data
, "We have successfully logged in\n");
368 failf(data
, "Odd return code after PASS");
369 return URG_FTP_WEIRD_PASS_REPLY
;
372 else if(! strncmp(buf
, "230", 3)) {
373 /* 230 User ... logged in.
374 (the user logged in without password) */
375 infof(data
, "We have successfully logged in\n");
378 failf(data
, "Odd return code after USER");
379 return URG_FTP_WEIRD_USER_REPLY
;
382 /* Send any QUOTE strings? */
385 /* Send all QUOTE strings in same order as on command-line */
389 sendf(data
->firstsocket
, data
, "%s\r\n", qitem
->data
);
391 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
394 failf(data
, "QUOT string not accepted: %s",
396 return URG_FTP_QUOTE_ERROR
;
403 /* If we have selected NOBODY, it means that we only want file information.
404 Which in FTP can't be much more than the file size! */
405 if(data
->conf
& CONF_NOBODY
) {
406 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
407 may not support it! It is however the only way we have to get a file's
410 sendf(data
->firstsocket
, data
, "SIZE %s\r\n", ppath
);
412 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
414 if(strncmp(buf
, "213", 3)) {
415 failf(data
, "Couldn't get file size: %s", buf
+4);
416 return URG_FTP_COULDNT_GET_SIZE
;
418 /* get the size from the ascii string: */
419 filesize
= atoi(buf
+4);
421 sprintf(buf
, "Content-Length: %d\n", filesize
);
423 if(strlen(buf
) != data
->fwrite(buf
, 1, strlen(buf
), data
->out
)) {
424 failf (data
, "Failed writing output");
425 return URG_WRITE_ERROR
;
427 if(data
->writeheader
) {
428 /* the header is requested to be written to this file */
429 if(strlen(buf
) != fwrite (buf
, 1, strlen(buf
), data
->writeheader
)) {
430 failf (data
, "Failed writing output");
431 return URG_WRITE_ERROR
;
437 /* We have chosen to use the PORT command */
438 if(data
->conf
& CONF_FTPPORT
) {
439 struct sockaddr_in sa
;
440 struct hostent
*h
=NULL
;
442 unsigned short porttouse
;
447 myhost
= if2ip(data
->ftpport
);
449 h
= GetHost(data
, myhost
);
452 if(strlen(data
->ftpport
)>1)
453 h
= GetHost(data
, data
->ftpport
);
455 myhost
=data
->ftpport
;
459 myhost
= getmyhost();
460 h
=GetHost(data
, myhost
);
462 infof(data
, "We connect from %s\n", myhost
);
465 if( (portsock
= socket(AF_INET
, SOCK_STREAM
, 0)) >= 0 ) {
466 memset((char *)&sa
, 0, sizeof(sa
));
467 memcpy((char *)&sa
.sin_addr
,
470 sa
.sin_family
= AF_INET
;
471 sa
.sin_addr
.s_addr
= INADDR_ANY
;
475 if(bind(portsock
, (struct sockaddr
*)&sa
, size
) >= 0) {
476 /* we succeeded to bind */
477 struct sockaddr_in add
;
480 if(getsockname(portsock
, (struct sockaddr
*) &add
,
482 failf(data
, "getsockname() failed");
483 return URG_FTP_PORT_FAILED
;
485 porttouse
= ntohs(add
.sin_port
);
487 if ( listen(portsock
, 1) < 0 ) {
488 failf(data
, "listen(2) failed on socket");
489 return URG_FTP_PORT_FAILED
;
493 failf(data
, "bind(2) failed on socket");
494 return URG_FTP_PORT_FAILED
;
498 failf(data
, "socket(2) failed (%s)");
499 return URG_FTP_PORT_FAILED
;
503 failf(data
, "could't find my own IP address (%s)", myhost
);
504 return URG_FTP_PORT_FAILED
;
508 unsigned short ip
[5];
509 (void) memcpy(&in
.s_addr
, *h
->h_addr_list
, sizeof (in
.s_addr
));
510 sscanf( inet_ntoa(in
), "%hu.%hu.%hu.%hu",
511 &ip
[0], &ip
[1], &ip
[2], &ip
[3]);
512 sendf(data
->firstsocket
, data
, "PORT %d,%d,%d,%d,%d,%d\n",
513 ip
[0], ip
[1], ip
[2], ip
[3],
518 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
520 if(strncmp(buf
, "200", 3)) {
521 failf(data
, "Server does not grok PORT, try without it!");
522 return URG_FTP_PORT_FAILED
;
525 else { /* we use the PASV command */
527 sendf(data
->firstsocket
, data
, "PASV\r\n");
529 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
531 if(strncmp(buf
, "227", 3)) {
532 failf(data
, "Odd return code after PASV");
533 return URG_FTP_WEIRD_PASV_REPLY
;
538 unsigned short newport
;
544 * New 227-parser June 3rd 1999.
545 * It now scans for a sequence of six comma-separated numbers and
546 * will take them as IP+port indicators.
548 * Found reply-strings include:
549 * "227 Entering Passive Mode (127,0,0,1,4,51)"
550 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
551 * "227 Entering passive mode. 127,0,0,1,4,51"
555 if (6 == sscanf(str
, "%d,%d,%d,%d,%d,%d",
556 &ip
[0], &ip
[1], &ip
[2], &ip
[3],
562 failf(data
, "Couldn't interpret this 227-reply: %s", buf
);
563 return URG_FTP_WEIRD_227_FORMAT
;
565 sprintf(newhost
, "%d.%d.%d.%d", ip
[0], ip
[1], ip
[2], ip
[3]);
566 he
= GetHost(data
, newhost
);
568 failf(data
, "Can't resolve new host %s", newhost
);
569 return URG_FTP_CANT_GET_HOST
;
573 newport
= (port
[0]<<8) + port
[1];
574 data
->secondarysocket
= socket(AF_INET
, SOCK_STREAM
, 0);
576 memset((char *) &serv_addr
, '\0', sizeof(serv_addr
));
577 memcpy((char *)&(serv_addr
.sin_addr
), he
->h_addr
, he
->h_length
);
578 serv_addr
.sin_family
= he
->h_addrtype
;
579 serv_addr
.sin_port
= htons(newport
);
581 if(data
->conf
& CONF_VERBOSE
) {
584 struct hostent
* answer
;
586 unsigned long address
;
587 #if defined(HAVE_INET_ADDR) || defined(WIN32)
588 address
= inet_addr(newhost
);
589 answer
= gethostbyaddr((char *) &address
, sizeof(address
),
594 (void) memcpy(&in
.s_addr
, *he
->h_addr_list
, sizeof (in
.s_addr
));
595 infof(data
, "Connecting to %s (%s) port %u\n",
596 answer
?answer
->h_name
:newhost
, inet_ntoa(in
), newport
);
598 (void) memcpy(&in
.s_addr
, *he
->h_addr_list
, sizeof (in
.s_addr
));
599 infof(data
, "Connecting to %s (%s) port %u\n",
600 he
->h_name
, inet_ntoa(in
), newport
);
604 if (connect(data
->secondarysocket
, (struct sockaddr
*) &serv_addr
,
605 sizeof(serv_addr
)) < 0) {
608 /* this should be made nicer */
610 failf(data
, "Connection refused by ftp server");
615 failf(data
, "Connection timeouted to ftp server");
619 failf(data
, "Can't connect to ftp server");
622 return URG_FTP_CANT_RECONNECT
;
627 /* we have the (new) data connection ready */
629 if(data
->conf
& CONF_UPLOAD
) {
631 /* Set type to binary (unless specified ASCII) */
632 sendf(data
->firstsocket
, data
, "TYPE %s\r\n",
633 (data
->conf
&CONF_FTPASCII
)?"A":"I");
635 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
637 if(strncmp(buf
, "200", 3)) {
638 failf(data
, "Couldn't set %s mode",
639 (data
->conf
&CONF_FTPASCII
)?"ASCII":"binary");
640 return (data
->conf
&CONF_FTPASCII
)? URG_FTP_COULDNT_SET_ASCII
:
641 URG_FTP_COULDNT_SET_BINARY
;
644 if(data
->resume_from
) {
645 /* we're about to continue the uploading of a file */
646 /* 1. get already existing file's size. We use the SIZE
647 command for this which may not exist in the server!
648 The SIZE command is not in RFC959. */
650 /* 2. This used to set REST. But since we can do append, we
651 don't another ftp command. We just skip the source file
652 offset and then we APPEND the rest on the file instead */
654 /* 3. pass file-size number of bytes in the source file */
655 /* 4. lower the infilesize counter */
656 /* => transfer as usual */
658 if(data
->resume_from
< 0 ) {
659 /* we could've got a specified offset from the command line,
660 but now we know we didn't */
662 sendf(data
->firstsocket
, data
, "SIZE %s\r\n", ppath
);
664 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
666 if(strncmp(buf
, "213", 3)) {
667 failf(data
, "Couldn't get file size: %s", buf
+4);
668 return URG_FTP_COULDNT_GET_SIZE
;
671 /* get the size from the ascii string: */
672 data
->resume_from
= atoi(buf
+4);
675 if(data
->resume_from
) {
676 /* do we still game? */
679 /* Set resume file transfer offset */
680 infof(data
, "Instructs server to resume from offset %d\n",
683 sendf(data
->firstsocket
, data
, "REST %d\r\n", data
->resume_from
);
685 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
687 if(strncmp(buf
, "350", 3)) {
688 failf(data
, "Couldn't use REST: %s", buf
+4);
689 return URG_FTP_COULDNT_USE_REST
;
692 /* enable append instead */
693 data
->conf
|= CONF_FTPAPPEND
;
695 /* Now, let's read off the proper amount of bytes from the
696 input. If we knew it was a proper file we could've just
697 fseek()ed but we only have a stream here */
699 int readthisamountnow
= (data
->resume_from
- passed
);
702 if(readthisamountnow
> BUFSIZE
)
703 readthisamountnow
= BUFSIZE
;
706 data
->fread(data
->buffer
, 1, readthisamountnow
, data
->in
);
708 passed
+= actuallyread
;
709 if(actuallyread
!= readthisamountnow
) {
710 failf(data
, "Could only read %d bytes from the input\n",
712 return URG_FTP_COULDNT_USE_REST
;
715 while(passed
!= data
->resume_from
);
717 /* now, decrease the size of the read */
718 if(data
->infilesize
>0) {
719 data
->infilesize
-= data
->resume_from
;
721 if(data
->infilesize
<= 0) {
722 infof(data
, "File already completely uploaded\n");
726 /* we've passed, proceed as normal */
730 /* Send everything on data->in to the socket */
731 if(data
->conf
& CONF_FTPAPPEND
)
732 /* we append onto the file instead of rewriting it */
733 sendf(data
->firstsocket
, data
, "APPE %s\r\n", ppath
);
735 sendf(data
->firstsocket
, data
, "STOR %s\r\n", ppath
);
737 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
740 failf(data
, "Failed FTP upload:%s", buf
+3);
741 /* oops, we never close the sockets! */
742 return URG_FTP_COULDNT_STOR_FILE
;
745 if(data
->conf
& CONF_FTPPORT
) {
746 result
= AllowServerConnect(data
, portsock
);
753 /* When we know we're uploading a specified file, we can get the file
754 size prior to the actual upload. */
756 ProgressInit(data
, data
->infilesize
);
757 result
= Upload(data
, data
->secondarysocket
, bytecountp
);
761 if((-1 != data
->infilesize
) && (data
->infilesize
!= *bytecountp
)) {
762 failf(data
, "Wrote only partial file (%d out of %d bytes)",
763 *bytecountp
, data
->infilesize
);
764 return URG_PARTIAL_FILE
;
768 /* Retrieve file or directory */
770 long downloadsize
=-1;
772 if(data
->conf
&CONF_RANGE
&& data
->range
) {
778 from
=strtol(data
->range
, &ptr
, 0);
779 while(ptr
&& *ptr
&& (isspace((int)*ptr
) || (*ptr
=='-')))
781 to
=strtol(ptr
, &ptr2
, 0);
783 /* we didn't get any digit */
788 data
->resume_from
= from
;
795 data
->maxdownload
= totalsize
;
800 data
->maxdownload
= totalsize
;
802 infof(data
, "range-download from %d to %d, totally %d bytes\n",
803 from
, to
, totalsize
);
807 /* make sure this becomes a valid name */
810 if((data
->conf
& CONF_FTPLISTONLY
) ||
811 ('/' == ppath
[strlen(ppath
)-1] )) {
812 /* The specified path ends with a slash, and therefore we think this
813 is a directory that is requested, use LIST. But before that we
814 need to set ASCII transfer mode. */
817 /* Set type to ASCII */
818 sendf(data
->firstsocket
, data
, "TYPE A\r\n");
820 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
822 if(strncmp(buf
, "200", 3)) {
823 failf(data
, "Couldn't set ascii mode");
824 return URG_FTP_COULDNT_SET_ASCII
;
827 /* if this output is to be machine-parsed, the NLST command will be
828 better used since the LIST command output is not specified or
829 standard in any way */
831 sendf(data
->firstsocket
, data
, "%s %s\r\n",
832 data
->customrequest
?data
->customrequest
:
833 (data
->conf
&CONF_FTPLISTONLY
?"NLST":"LIST"),
837 /* Set type to binary (unless specified ASCII) */
838 sendf(data
->firstsocket
, data
, "TYPE %s\r\n",
839 (data
->conf
&CONF_FTPASCII
)?"A":"I");
841 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
843 if(strncmp(buf
, "200", 3)) {
844 failf(data
, "Couldn't set %s mode",
845 (data
->conf
&CONF_FTPASCII
)?"ASCII":"binary");
846 return (data
->conf
&CONF_FTPASCII
)? URG_FTP_COULDNT_SET_ASCII
:
847 URG_FTP_COULDNT_SET_BINARY
;
850 if(data
->resume_from
) {
852 /* Daniel: (August 4, 1999)
854 * We start with trying to use the SIZE command to figure out the size
855 * of the file we're gonna get. If we can get the size, this is by far
856 * the best way to know if we're trying to resume beyond the EOF. */
858 sendf(data
->firstsocket
, data
, "SIZE %s\r\n", ppath
);
860 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
862 if(strncmp(buf
, "213", 3)) {
863 infof(data
, "server doesn't support SIZE: %s", buf
+4);
864 /* We couldn't get the size and therefore we can't know if there
865 really is a part of the file left to get, although the server
866 will just close the connection when we start the connection so it
867 won't cause us any harm, just not make us exit as nicely. */
870 int foundsize
=atoi(buf
+4);
871 /* We got a file size report, so we check that there actually is a
872 part of the file left to get, or else we go home. */
873 if(foundsize
<= data
->resume_from
) {
874 failf(data
, "Offset (%d) was beyond file size (%d)",
875 data
->resume_from
, foundsize
);
876 return URG_FTP_BAD_DOWNLOAD_RESUME
;
878 /* Now store the number of bytes we are expected to download */
879 downloadsize
= foundsize
-data
->resume_from
;
882 /* Set resume file transfer offset */
883 infof(data
, "Instructs server to resume from offset %d\n",
886 sendf(data
->firstsocket
, data
, "REST %d\r\n", data
->resume_from
);
888 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
890 if(strncmp(buf
, "350", 3)) {
891 failf(data
, "Couldn't use REST: %s", buf
+4);
892 return URG_FTP_COULDNT_USE_REST
;
896 sendf(data
->firstsocket
, data
, "RETR %s\r\n", ppath
);
899 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
901 if(!strncmp(buf
, "150", 3) || !strncmp(buf
, "125", 3)) {
905 150 Opening BINARY mode data connection for /etc/passwd (2241
906 bytes). (ok, the file is being transfered)
909 150 Opening ASCII mode data connection for /bin/ls
912 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
915 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
918 125 Data connection already open; Transfer starting. */
920 int size
=-1; /* default unknown size */
922 if(!dirlist
&& (-1 == downloadsize
)) {
924 * It seems directory listings either don't show the size or very
925 * often uses size 0 anyway.
926 * Example D above makes this parsing a little tricky
929 bytes
=strstr(buf
, " bytes");
932 /* this is a hint there is size information in there! ;-) */
934 /* scan for the parenthesis and break there */
937 /* if only skip digits, or else we're in deep trouble */
938 if(!isdigit((int)*bytes
)) {
942 /* one more estep backwards */
945 /* only if we have nothing but digits: */
947 /* get the number! */
953 if(2 != sscanf(buf
, "%*[^(](%d bytes%c", &size
, &paren
))
957 else if(downloadsize
> -1)
961 if((size
> -1) && (data
->resume_from
>0)) {
962 size
-= data
->resume_from
;
964 failf(data
, "Offset (%d) was beyond file size (%d)",
965 data
->resume_from
, data
->resume_from
+size
);
966 return URG_PARTIAL_FILE
;
971 if(data
->conf
& CONF_FTPPORT
) {
972 result
= AllowServerConnect(data
, portsock
);
977 infof(data
, "Getting file with size: %d\n", size
);
980 result
=Download(data
, data
->secondarysocket
, size
, FALSE
,
985 if((-1 != size
) && (size
!= *bytecountp
)) {
986 failf(data
, "Received only partial file");
987 return URG_PARTIAL_FILE
;
989 else if(0 == *bytecountp
) {
990 failf(data
, "No data was received!");
991 return URG_FTP_COULDNT_RETR_FILE
;
995 failf(data
, "%s", buf
+4);
996 return URG_FTP_COULDNT_RETR_FILE
;
1000 /* end of transfer */
1003 /* shut down the socket to inform the server we're done */
1004 sclose(data
->secondarysocket
);
1005 data
->secondarysocket
= -1;
1007 /* now let's see what the server says about the transfer we
1009 nread
= GetLastResponse(data
->firstsocket
, buf
, data
);
1011 /* 226 Transfer complete */
1012 if(strncmp(buf
, "226", 3)) {
1013 failf(data
, "%s", buf
+4);
1014 return URG_FTP_WRITE_ERROR
;
1020 /* -- deal with the ftp server! -- */
1022 UrgError
ftp(struct UrlData
*data
,
1032 realpath
= URLfix(urlpath
);
1034 realpath
= curl_unescape(urlpath
);
1037 retcode
= _ftp(data
, bytecountp
, ftpuser
, ftppasswd
, realpath
);
1041 /* then we try the original path */
1042 retcode
= _ftp(data
, bytecountp
, ftpuser
, ftppasswd
, urlpath
);