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