infopipe improvements
[monky] / src / ftp.c
1 /*
2  * ftp.c: basic handling of an FTP command connection to check for
3  *        directory availability. No transfer is needed.
4  *
5  *  Reference: RFC 959
6  *
7  *  $Id$
8  */
9
10 #include <sys/types.h>
11 #include <sys/time.h>
12 #include <sys/socket.h>
13 #include <resolv.h>
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <netdb.h>
17 #include <string.h>
18
19 #include "ftp.h"
20
21 /* #define DEBUG_FTP 1  */
22 #ifdef STANDALONE
23 #define DEBUG_FTP 1
24 #endif
25
26 static int ftpFd = -1;
27 static struct sockaddr_in ftpAddr;
28 static char hostname[100];
29 static int ftpPassive = 1;
30 static int dataFd = -1;
31
32 #define FTP_COMMAND_OK          200
33 #define FTP_SYNTAX_ERROR        500
34 #define FTP_GET_PASSWD          331
35
36 /*
37  * Initialize the FTP handling.
38  */
39
40 void initFtp(void)
41 {
42         gethostname(hostname, sizeof(hostname));
43 }
44
45 /*
46  * Parsing of the server answer, we just extract the code.
47  * return 0 for errors
48  *     +XXX for last line of response
49  *     -XXX for response to be continued
50  */
51 int parseFtpResponse(char *buf, int len)
52 {
53         int val = 0;
54
55         if (len < 3)
56                 return (-1);
57         if ((*buf >= '0') && (*buf <= '9'))
58                 val = val * 10 + (*buf - '0');
59         else
60                 return (0);
61         buf++;
62         if ((*buf >= '0') && (*buf <= '9'))
63                 val = val * 10 + (*buf - '0');
64         else
65                 return (0);
66         buf++;
67         if ((*buf >= '0') && (*buf <= '9'))
68                 val = val * 10 + (*buf - '0');
69         else
70                 return (0);
71         buf++;
72         if (*buf == '-')
73                 return (-val);
74         return (val);
75 }
76
77 /*
78  * Read the response from the FTP server after a command.
79  * Returns the code number
80  *
81  */
82 int readFtpResponse(char *buf, int size)
83 {
84         char *ptr, *end;
85         int len;
86         int res = -1;
87
88         if (size <= 0)
89                 return (-1);
90
91       get_more:
92         if ((len = recv(ftpFd, buf, size - 1, 0)) < 0) {
93                 close(ftpFd);
94                 ftpFd = -1;
95                 ftpFd = -1;
96                 return (-1);
97         }
98         if (len == 0) {
99                 return (-1);
100         }
101
102         end = &buf[len];
103         *end = 0;
104 #ifdef DEBUG_FTP
105         printf(buf);
106 #endif
107         ptr = buf;
108         while (ptr < end) {
109                 res = parseFtpResponse(ptr, end - ptr);
110                 if (res > 0)
111                         break;
112                 if (res == 0) {
113 #ifdef DEBUG_FTP
114                         fprintf(stderr, "readFtpResponse failed: %s\n",
115                                 ptr);
116 #endif
117                         return (-1);
118                 }
119                 while ((ptr < end) && (*ptr != '\n'))
120                         ptr++;
121                 if (ptr >= end) {
122 #ifdef DEBUG_FTP
123                         fprintf(stderr,
124                                 "readFtpResponse: unexpected end %s\n",
125                                 buf);
126 #endif
127                         return ((-res) / 100);
128                 }
129                 if (*ptr != '\r')
130                         ptr++;
131         }
132
133         if (res < 0)
134                 goto get_more;
135
136 #ifdef DEBUG_FTP
137         printf("Got %d\n", res);
138 #endif
139         return (res / 100);
140 }
141
142 /*
143  * Get the response from the FTP server after a command.
144  * Returns the code number
145  *
146  */
147 int getFtpResponse(void)
148 {
149         char buf[16 * 1024 + 1];
150
151 /**************
152     fd_set rfd;
153     struct timeval tv;
154     int res;
155
156     tv.tv_sec = 10;
157     tv.tv_usec = 0;
158     FD_ZERO(&rfd);
159     FD_SET(ftpFd, &rfd);
160     res = select(ftpFd + 1, &rfd, NULL, NULL, &tv);
161     if (res <= 0) return(res);
162  **************/
163
164         return (readFtpResponse(buf, 16 * 1024));
165 }
166
167 /*
168  * Check if there is a response from the FTP server after a command.
169  * Returns the code number, or 0
170  */
171 int checkFtpResponse(void)
172 {
173         char buf[1024 + 1];
174         fd_set rfd;
175         struct timeval tv;
176
177         tv.tv_sec = 0;
178         tv.tv_usec = 0;
179         FD_ZERO(&rfd);
180         FD_SET(ftpFd, &rfd);
181         switch (select(ftpFd + 1, &rfd, NULL, NULL, &tv)) {
182         case 0:
183                 return (0);
184         case -1:
185 #ifdef DEBUG_FTP
186                 perror("select");
187 #endif
188                 return (-1);
189
190         }
191
192         return (readFtpResponse(buf, 1024));
193 }
194
195 /*
196  * Send the user authentification
197  */
198
199 int sendUser(void)
200 {
201         char buf[200];
202         int len;
203         int res;
204
205         len = snprintf(buf, sizeof(buf), "USER anonymous\r\n");
206 #ifdef DEBUG_FTP
207         printf(buf);
208 #endif
209         res = send(ftpFd, buf, len, 0);
210         if (res < 0)
211                 return (res);
212         return (0);
213 }
214
215 /*
216  * Send the password authentification
217  */
218
219 int sendPasswd(void)
220 {
221         char buf[200];
222         int len;
223         int res;
224
225         len =
226             snprintf(buf, sizeof(buf), "PASS mirrorfind@%s\r\n", hostname);
227 #ifdef DEBUG_FTP
228         printf(buf);
229 #endif
230         res = send(ftpFd, buf, len, 0);
231         if (res < 0)
232                 return (res);
233         return (0);
234 }
235
236 /*
237  * Send a QUIT
238  */
239
240 int sendQuit(void)
241 {
242         char buf[200];
243         int len;
244         int res;
245
246         len = snprintf(buf, sizeof(buf), "QUIT\r\n");
247 #ifdef DEBUG_FTP
248         printf(buf);
249 #endif
250         res = send(ftpFd, buf, len, 0);
251         return (0);
252 }
253
254 /*
255  * Connecting to the server, port 21 by default.
256  */
257
258 int connectFtp(const char *server, int port)
259 {
260         struct hostent *hp;
261         int res;
262
263         /*
264          * do the blocking DNS query.
265          */
266         hp = gethostbyname(server);
267         if (hp == NULL)
268                 return (-1);
269
270         /*
271          * Prepare the socket
272          */
273         memset(&ftpAddr, 0, sizeof(ftpAddr));
274         ftpAddr.sin_family = AF_INET;
275         memcpy(&ftpAddr.sin_addr, hp->h_addr_list[0], hp->h_length);
276         if (port == 0)
277                 port = 21;
278         ftpAddr.sin_port = htons(port);
279         ftpFd = socket(AF_INET, SOCK_STREAM, 0);
280         if (ftpFd < 0)
281                 return (-1);
282
283         /*
284          * Do the connect.
285          */
286         if (connect(ftpFd, (struct sockaddr *) &ftpAddr,
287                     sizeof(struct sockaddr_in)) < 0) {
288                 close(ftpFd);
289                 ftpFd = -1;
290                 ftpFd = -1;
291                 return (-1);
292         }
293
294         /*
295          * Wait for the HELLO from the server.
296          */
297         res = getFtpResponse();
298         if (res != 2) {
299                 close(ftpFd);
300                 ftpFd = -1;
301                 ftpFd = -1;
302                 return (-1);
303         }
304
305         /*
306          * State diagram for the login operation on the FTP server
307          *
308          * Reference: RFC 959
309          *
310          *                       1
311          * +---+   USER    +---+------------->+---+
312          * | B |---------->| W | 2       ---->| E |
313          * +---+           +---+------  |  -->+---+
314          *                  | |       | | |
315          *                3 | | 4,5   | | |
316          *    --------------   -----  | | |
317          *   |                      | | | |
318          *   |                      | | | |
319          *   |                 ---------  |
320          *   |               1|     | |   |
321          *   V                |     | |   |
322          * +---+   PASS    +---+ 2  |  ------>+---+
323          * |   |---------->| W |------------->| S |
324          * +---+           +---+   ---------->+---+
325          *                  | |   | |     |
326          *                3 | |4,5| |     |
327          *    --------------   --------   |
328          *   |                    | |  |  |
329          *   |                    | |  |  |
330          *   |                 -----------
331          *   |             1,3|   | |  |
332          *   V                |  2| |  |
333          * +---+   ACCT    +---+--  |   ----->+---+
334          * |   |---------->| W | 4,5 -------->| F |
335          * +---+           +---+------------->+---+
336          */
337         res = sendUser();
338         if (res < 0) {
339                 close(ftpFd);
340                 ftpFd = -1;
341                 ftpFd = -1;
342                 return (-1);
343         }
344         res = getFtpResponse();
345         switch (res) {
346         case 2:
347                 return (0);
348         case 3:
349                 break;
350         case 1:
351         case 4:
352         case 5:
353         case -1:
354         default:
355                 close(ftpFd);
356                 ftpFd = -1;
357                 ftpFd = -1;
358                 return (-1);
359         }
360         res = sendPasswd();
361         if (res < 0) {
362                 close(ftpFd);
363                 ftpFd = -1;
364                 ftpFd = -1;
365                 return (-1);
366         }
367         res = getFtpResponse();
368         switch (res) {
369         case 2:
370                 return (0);
371         case 3:
372                 fprintf(stderr,
373                         "FTP server asking for ACCNT on anonymous\n");
374         case 1:
375         case 4:
376         case 5:
377         case -1:
378         default:
379                 close(ftpFd);
380                 ftpFd = -1;
381                 ftpFd = -1;
382                 return (-1);
383         }
384
385         return (0);
386 }
387
388 /*
389  * Check an FTP directory on the server
390  */
391
392 int changeFtpDirectory(char *directory)
393 {
394         char buf[400];
395         int len;
396         int res;
397
398         /*
399          * Expected response code for CWD:
400          *
401          * CWD
402          *     250
403          *     500, 501, 502, 421, 530, 550
404          */
405         len = snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
406 #ifdef DEBUG_FTP
407         printf(buf);
408 #endif
409         res = send(ftpFd, buf, len, 0);
410         if (res < 0)
411                 return (res);
412         res = getFtpResponse();
413         if (res == 4) {
414                 close(ftpFd);
415                 ftpFd = -1;
416                 ftpFd = -1;
417                 return (-1);
418         }
419         if (res == 2)
420                 return (1);
421         if (res == 5) {
422                 return (0);
423         }
424         return (0);
425 }
426
427 /*
428  * dataConnectFtp
429  */
430 int dataConnectFtp()
431 {
432         char buf[200];
433         int len, i;
434         int res;
435         unsigned char ad[6], *cur, *adp, *portp;
436         unsigned int temp[6];
437         struct sockaddr_in dataAddr;
438         socklen_t dataAddrLen;
439
440         dataFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
441         if (dataFd < 0) {
442                 fprintf(stderr,
443                         "dataConnectFtp: failed to create socket\n");
444         }
445         dataAddrLen = sizeof(dataAddr);
446         memset(&dataAddr, 0, dataAddrLen);
447         dataAddr.sin_family = AF_INET;
448
449         if (ftpPassive) {
450                 len = snprintf(buf, sizeof(buf), "PASV\r\n");
451 #ifdef DEBUG_FTP
452                 printf(buf);
453 #endif
454                 res = send(ftpFd, buf, len, 0);
455                 if (res < 0) {
456                         close(dataFd);
457                         dataFd = -1;
458                         return (res);
459                 }
460                 res = readFtpResponse(buf, sizeof(buf) - 1);
461                 if (res != 2) {
462                         if (res == 5) {
463                                 close(dataFd);
464                                 dataFd = -1;
465                                 return (-1);
466                         } else {
467                                 /*
468                                  * retry with an active connection
469                                  */
470                                 close(dataFd);
471                                 dataFd = -1;
472                                 ftpPassive = 0;
473                         }
474                 }
475                 cur = &buf[4];
476                 while (((*cur < '0') || (*cur > '9')) && *cur != '\0')
477                         cur++;
478                 if (sscanf
479                     (cur, "%d,%d,%d,%d,%d,%d", &temp[0], &temp[1],
480                      &temp[2], &temp[3], &temp[4], &temp[5]) != 6) {
481                         fprintf(stderr, "Invalid answer to PASV\n");
482                         close(dataFd);
483                         dataFd = -1;
484                         return (-1);
485                 }
486                 for (i = 0; i < 6; i++)
487                         ad[i] = (unsigned char) (temp[i] & 0xff);
488                 memcpy(&dataAddr.sin_addr, &ad[0], 4);
489                 memcpy(&dataAddr.sin_port, &ad[4], 2);
490                 if (connect
491                     (dataFd, (struct sockaddr *) &dataAddr,
492                      dataAddrLen) < 0) {
493                         fprintf(stderr,
494                                 "Failed to create a data connection\n");
495                         close(dataFd);
496                         dataFd = -1;
497                         return (-1);
498                 }
499         } else {
500                 getsockname(dataFd, (struct sockaddr *) &dataAddr,
501                             &dataAddrLen);
502                 dataAddr.sin_port = 0;
503                 if (bind
504                     (dataFd, (struct sockaddr *) &dataAddr,
505                      dataAddrLen) < 0) {
506                         fprintf(stderr, "Failed to bind a port\n");
507                         close(dataFd);
508                         dataFd = -1;
509                         return (-1);
510                 }
511                 getsockname(dataFd, (struct sockaddr *) &dataAddr,
512                             &dataAddrLen);
513
514                 if (listen(dataFd, 1) < 0) {
515                         fprintf(stderr, "Could not listen on port %d\n",
516                                 ntohs(dataAddr.sin_port));
517                         close(dataFd);
518                         dataFd = -1;
519                         return (-1);
520                 }
521                 adp = (unsigned char *) &dataAddr.sin_addr;
522                 portp = (unsigned char *) &dataAddr.sin_port;
523                 len =
524                     snprintf(buf, sizeof(buf),
525                              "PORT %d,%d,%d,%d,%d,%d\r\n", adp[0] & 0xff,
526                              adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
527                              portp[0] & 0xff, portp[1] & 0xff);
528                 buf[sizeof(buf) - 1] = 0;
529 #ifdef DEBUG_FTP
530                 printf(buf);
531 #endif
532
533                 res = send(ftpFd, buf, len, 0);
534                 if (res < 0) {
535                         close(dataFd);
536                         dataFd = -1;
537                         return (res);
538                 }
539                 res = getFtpResponse();
540                 if (res != 2) {
541                         close(dataFd);
542                         dataFd = -1;
543                         return (-1);
544                 }
545         }
546         return (dataFd);
547
548 }
549
550 /*
551  * dataConnectEndFtp
552  */
553 int dataConnectEndFtp()
554 {
555         int res;
556
557         close(dataFd);
558         dataFd = -1;
559         res = getFtpResponse();
560         if (res != 2) {
561                 close(dataFd);
562                 dataFd = -1;
563                 close(ftpFd);
564                 ftpFd = -1;
565                 return (-1);
566         }
567         return (0);
568 }
569
570 /*
571  * parseListFtp
572  */
573
574 int parseListFtp(const char *list, ftpListCallback callback,
575                  void *userData)
576 {
577         const char *cur = list;
578         char filename[151];
579         char attrib[11];
580         char owner[11];
581         char group[11];
582         char month[4];
583         int year = 0;
584         int minute = 0;
585         int hour = 0;
586         int day = 0;
587         unsigned long size = 0;
588         int links = 0;
589         int i;
590
591         if (!strncmp(cur, "total", 5)) {
592                 cur += 5;
593                 while (*cur == ' ')
594                         cur++;
595                 while ((*cur >= '0') && (*cur <= '9'))
596                         links = (links * 10) + (*cur++ - '0');
597                 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
598                         cur++;
599                 return (cur - list);
600         } else if (*list == '+') {
601                 return (0);
602         } else {
603                 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
604                         cur++;
605                 if (*cur == 0)
606                         return (0);
607                 i = 0;
608                 while (*cur != ' ') {
609                         if (i < 10)
610                                 attrib[i++] = *cur;
611                         cur++;
612                         if (*cur == 0)
613                                 return (0);
614                 }
615                 attrib[10] = 0;
616                 while (*cur == ' ')
617                         cur++;
618                 if (*cur == 0)
619                         return (0);
620                 while ((*cur >= '0') && (*cur <= '9'))
621                         links = (links * 10) + (*cur++ - '0');
622                 while (*cur == ' ')
623                         cur++;
624                 if (*cur == 0)
625                         return (0);
626                 i = 0;
627                 while (*cur != ' ') {
628                         if (i < 10)
629                                 owner[i++] = *cur;
630                         cur++;
631                         if (*cur == 0)
632                                 return (0);
633                 }
634                 owner[i] = 0;
635                 while (*cur == ' ')
636                         cur++;
637                 if (*cur == 0)
638                         return (0);
639                 i = 0;
640                 while (*cur != ' ') {
641                         if (i < 10)
642                                 group[i++] = *cur;
643                         cur++;
644                         if (*cur == 0)
645                                 return (0);
646                 }
647                 group[i] = 0;
648                 while (*cur == ' ')
649                         cur++;
650                 if (*cur == 0)
651                         return (0);
652                 while ((*cur >= '0') && (*cur <= '9'))
653                         size = (size * 10) + (*cur++ - '0');
654                 while (*cur == ' ')
655                         cur++;
656                 if (*cur == 0)
657                         return (0);
658                 i = 0;
659                 while (*cur != ' ') {
660                         if (i < 3)
661                                 month[i++] = *cur;
662                         cur++;
663                         if (*cur == 0)
664                                 return (0);
665                 }
666                 month[i] = 0;
667                 while (*cur == ' ')
668                         cur++;
669                 if (*cur == 0)
670                         return (0);
671                 while ((*cur >= '0') && (*cur <= '9'))
672                         day = (day * 10) + (*cur++ - '0');
673                 while (*cur == ' ')
674                         cur++;
675                 if (*cur == 0)
676                         return (0);
677                 if ((cur[1] == 0) || (cur[2] == 0))
678                         return (0);
679                 if ((cur[1] == ':') || (cur[2] == ':')) {
680                         while ((*cur >= '0') && (*cur <= '9'))
681                                 hour = (hour * 10) + (*cur++ - '0');
682                         if (*cur == ':')
683                                 cur++;
684                         while ((*cur >= '0') && (*cur <= '9'))
685                                 minute = (minute * 10) + (*cur++ - '0');
686                 } else {
687                         while ((*cur >= '0') && (*cur <= '9'))
688                                 year = (year * 10) + (*cur++ - '0');
689                 }
690                 while (*cur == ' ')
691                         cur++;
692                 if (*cur == 0)
693                         return (0);
694                 i = 0;
695                 while ((*cur != '\n') && (*cur != '\r')) {
696                         if (i < 150)
697                                 filename[i++] = *cur;
698                         cur++;
699                         if (*cur == 0)
700                                 return (0);
701                 }
702                 filename[i] = 0;
703                 if ((*cur != '\n') && (*cur != '\r'))
704                         return (0);
705                 while ((*cur == '\n') || (*cur == '\r'))
706                         cur++;
707         }
708         if (callback != NULL) {
709                 callback(userData, filename, attrib, owner, group, size,
710                          links, year, month, day, minute);
711         }
712         return (cur - list);
713 }
714
715 /*
716  * listFtp
717  */
718 int listFtp(ftpListCallback callback, void *userData)
719 {
720         char buf[4096 + 1];
721         int len, res;
722         int index = 0, base;
723         fd_set rfd, efd;
724         struct timeval tv;
725
726         dataFd = dataConnectFtp();
727
728         len = snprintf(buf, sizeof(buf), "LIST -L\r\n");
729 #ifdef DEBUG_FTP
730         printf(buf);
731 #endif
732         res = send(ftpFd, buf, len, 0);
733         if (res < 0) {
734                 close(dataFd);
735                 dataFd = -1;
736                 return (res);
737         }
738         res = readFtpResponse(buf, sizeof(buf) - 1);
739         if (res != 1) {
740                 close(dataFd);
741                 dataFd = -1;
742                 return (-res);
743         }
744
745         do {
746                 tv.tv_sec = 1;
747                 tv.tv_usec = 0;
748                 FD_ZERO(&rfd);
749                 FD_SET(dataFd, &rfd);
750                 FD_ZERO(&efd);
751                 FD_SET(dataFd, &efd);
752                 res = select(dataFd + 1, &rfd, NULL, &efd, &tv);
753                 if (res < 0) {
754 #ifdef DEBUG_FTP
755                         perror("select");
756 #endif
757                         close(dataFd);
758                         dataFd = -1;
759                         return (-1);
760                 }
761                 if (res == 0) {
762                         res = checkFtpResponse();
763                         if (res < 0) {
764                                 close(dataFd);
765                                 dataFd = -1;
766                                 dataFd = -1;
767                                 return (-1);
768                         }
769                         if (res == 2) {
770                                 close(dataFd);
771                                 dataFd = -1;
772                                 return (0);
773                         }
774
775                         continue;
776                 }
777
778                 if ((len =
779                      read(dataFd, &buf[index],
780                           sizeof(buf) - (index + 1))) < 0) {
781 #ifdef DEBUG_FTP
782                         perror("read");
783 #endif
784                         close(dataFd);
785                         dataFd = -1;
786                         dataFd = -1;
787                         return (-1);
788                 }
789 #ifdef DEBUG_FTP
790                 write(1, &buf[index], len);
791 #endif
792                 index += len;
793                 buf[index] = 0;
794                 base = 0;
795                 do {
796                         res = parseListFtp(&buf[base], callback, userData);
797                         base += res;
798                 } while (res > 0);
799
800                 memmove(&buf[0], &buf[base], index - base);
801                 index -= base;
802         } while (len != 0);
803         dataConnectEndFtp();
804         return (0);
805 }
806
807 /*
808  * getFtpSocket:
809  */
810
811 int getFtpSocket(const char *filename)
812 {
813         char buf[300];
814         int res, len;
815         if (filename == NULL)
816                 return (-1);
817         dataFd = dataConnectFtp();
818
819         len = snprintf(buf, sizeof(buf), "TYPE I\r\n");
820 #ifdef DEBUG_FTP
821         printf(buf);
822 #endif
823         res = send(ftpFd, buf, len, 0);
824         if (res < 0) {
825                 close(dataFd);
826                 dataFd = -1;
827                 return (res);
828         }
829         res = readFtpResponse(buf, sizeof(buf) - 1);
830         if (res != 2) {
831                 close(dataFd);
832                 dataFd = -1;
833                 return (-res);
834         }
835         len = snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
836 #ifdef DEBUG_FTP
837         printf(buf);
838 #endif
839         res = send(ftpFd, buf, len, 0);
840         if (res < 0) {
841                 close(dataFd);
842                 dataFd = -1;
843                 return (res);
844         }
845         res = readFtpResponse(buf, sizeof(buf) - 1);
846         if (res != 1) {
847                 close(dataFd);
848                 dataFd = -1;
849                 return (-res);
850         }
851         return (dataFd);
852 }
853
854 /*
855  * closeFtpSocket
856  */
857
858 int closeFtpSocket()
859 {
860         return (dataConnectEndFtp());
861 }
862
863 /*
864  * listFtp
865  */
866 int getFtp(ftpDataCallback callback, void *userData, const char *filename)
867 {
868         char buf[4096];
869         int len = 0, res;
870         fd_set rfd;
871         struct timeval tv;
872
873         if (filename == NULL)
874                 return (-1);
875         if (callback == NULL)
876                 return (-1);
877         if (getFtpSocket(filename) < 0)
878                 return (-1);
879
880         do {
881                 tv.tv_sec = 1;
882                 tv.tv_usec = 0;
883                 FD_ZERO(&rfd);
884                 FD_SET(dataFd, &rfd);
885                 res = select(dataFd + 1, &rfd, NULL, NULL, &tv);
886                 if (res < 0) {
887 #ifdef DEBUG_FTP
888                         perror("select");
889 #endif
890                         close(dataFd);
891                         dataFd = -1;
892                         return (-1);
893                 }
894                 if (res == 0) {
895                         res = checkFtpResponse();
896                         if (res < 0) {
897                                 close(dataFd);
898                                 dataFd = -1;
899                                 dataFd = -1;
900                                 return (-1);
901                         }
902                         if (res == 2) {
903                                 close(dataFd);
904                                 dataFd = -1;
905                                 return (0);
906                         }
907
908                         continue;
909                 }
910                 if ((len = read(dataFd, &buf, sizeof(buf))) < 0) {
911                         callback(userData, buf, len);
912                         close(dataFd);
913                         dataFd = -1;
914                         ftpFd = -1;
915                         return (-1);
916                 }
917                 callback(userData, buf, len);
918         } while (len != 0);
919
920         return (closeFtpSocket());
921 }
922
923 /*
924  * Disconnect from the FTP server.
925  */
926
927 int disconnectFtp(void)
928 {
929         if (ftpFd < 0)
930                 return (-1);
931         sendQuit();
932         close(ftpFd);
933         ftpFd = -1;
934         ftpFd = -1;
935         return (0);
936 }