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