2 * ftp.c: basic handling of an FTP command connection to check for
3 * directory availability. No transfer is needed.
10 #include <sys/types.h>
12 #include <sys/socket.h>
21 /* #define DEBUG_FTP 1 */
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;
32 #define FTP_COMMAND_OK 200
33 #define FTP_SYNTAX_ERROR 500
34 #define FTP_GET_PASSWD 331
37 * Initialize the FTP handling.
42 gethostname(hostname, sizeof(hostname));
46 * Parsing of the server answer, we just extract the code.
48 * +XXX for last line of response
49 * -XXX for response to be continued
51 int parseFtpResponse(char *buf, int len)
57 if ((*buf >= '0') && (*buf <= '9'))
58 val = val * 10 + (*buf - '0');
62 if ((*buf >= '0') && (*buf <= '9'))
63 val = val * 10 + (*buf - '0');
67 if ((*buf >= '0') && (*buf <= '9'))
68 val = val * 10 + (*buf - '0');
78 * Read the response from the FTP server after a command.
79 * Returns the code number
82 int readFtpResponse(char *buf, int size)
92 if ((len = recv(ftpFd, buf, size - 1, 0)) < 0) {
109 res = parseFtpResponse(ptr, end - ptr);
114 fprintf(stderr, "readFtpResponse failed: %s\n",
119 while ((ptr < end) && (*ptr != '\n'))
124 "readFtpResponse: unexpected end %s\n",
127 return ((-res) / 100);
137 printf("Got %d\n", res);
143 * Get the response from the FTP server after a command.
144 * Returns the code number
147 int getFtpResponse(void)
149 char buf[16 * 1024 + 1];
160 res = select(ftpFd + 1, &rfd, NULL, NULL, &tv);
161 if (res <= 0) return(res);
164 return (readFtpResponse(buf, 16 * 1024));
168 * Check if there is a response from the FTP server after a command.
169 * Returns the code number, or 0
171 int checkFtpResponse(void)
181 switch (select(ftpFd + 1, &rfd, NULL, NULL, &tv)) {
192 return (readFtpResponse(buf, 1024));
196 * Send the user authentification
205 len = snprintf(buf, sizeof(buf), "USER anonymous\r\n");
209 res = send(ftpFd, buf, len, 0);
216 * Send the password authentification
226 snprintf(buf, sizeof(buf), "PASS mirrorfind@%s\r\n", hostname);
230 res = send(ftpFd, buf, len, 0);
246 len = snprintf(buf, sizeof(buf), "QUIT\r\n");
250 res = send(ftpFd, buf, len, 0);
255 * Connecting to the server, port 21 by default.
258 int connectFtp(const char *server, int port)
264 * do the blocking DNS query.
266 hp = gethostbyname(server);
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);
278 ftpAddr.sin_port = htons(port);
279 ftpFd = socket(AF_INET, SOCK_STREAM, 0);
286 if (connect(ftpFd, (struct sockaddr *) &ftpAddr,
287 sizeof(struct sockaddr_in)) < 0) {
295 * Wait for the HELLO from the server.
297 res = getFtpResponse();
306 * State diagram for the login operation on the FTP server
311 * +---+ USER +---+------------->+---+
312 * | B |---------->| W | 2 ---->| E |
313 * +---+ +---+------ | -->+---+
316 * -------------- ----- | | |
322 * +---+ PASS +---+ 2 | ------>+---+
323 * | |---------->| W |------------->| S |
324 * +---+ +---+ ---------->+---+
327 * -------------- -------- |
333 * +---+ ACCT +---+-- | ----->+---+
334 * | |---------->| W | 4,5 -------->| F |
335 * +---+ +---+------------->+---+
344 res = getFtpResponse();
367 res = getFtpResponse();
373 "FTP server asking for ACCNT on anonymous\n");
389 * Check an FTP directory on the server
392 int changeFtpDirectory(char *directory)
399 * Expected response code for CWD:
403 * 500, 501, 502, 421, 530, 550
405 len = snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
409 res = send(ftpFd, buf, len, 0);
412 res = getFtpResponse();
435 unsigned char ad[6], *cur, *adp, *portp;
436 unsigned int temp[6];
437 struct sockaddr_in dataAddr;
438 socklen_t dataAddrLen;
440 dataFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
443 "dataConnectFtp: failed to create socket\n");
445 dataAddrLen = sizeof(dataAddr);
446 memset(&dataAddr, 0, dataAddrLen);
447 dataAddr.sin_family = AF_INET;
450 len = snprintf(buf, sizeof(buf), "PASV\r\n");
454 res = send(ftpFd, buf, len, 0);
460 res = readFtpResponse(buf, sizeof(buf) - 1);
468 * retry with an active connection
476 while (((*cur < '0') || (*cur > '9')) && *cur != '\0')
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");
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);
491 (dataFd, (struct sockaddr *) &dataAddr,
494 "Failed to create a data connection\n");
500 getsockname(dataFd, (struct sockaddr *) &dataAddr,
502 dataAddr.sin_port = 0;
504 (dataFd, (struct sockaddr *) &dataAddr,
506 fprintf(stderr, "Failed to bind a port\n");
511 getsockname(dataFd, (struct sockaddr *) &dataAddr,
514 if (listen(dataFd, 1) < 0) {
515 fprintf(stderr, "Could not listen on port %d\n",
516 ntohs(dataAddr.sin_port));
521 adp = (unsigned char *) &dataAddr.sin_addr;
522 portp = (unsigned char *) &dataAddr.sin_port;
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;
533 res = send(ftpFd, buf, len, 0);
539 res = getFtpResponse();
553 int dataConnectEndFtp()
559 res = getFtpResponse();
574 int parseListFtp(const char *list, ftpListCallback callback,
577 const char *cur = list;
587 unsigned long size = 0;
591 if (!strncmp(cur, "total", 5)) {
595 while ((*cur >= '0') && (*cur <= '9'))
596 links = (links * 10) + (*cur++ - '0');
597 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
600 } else if (*list == '+') {
603 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
608 while (*cur != ' ') {
620 while ((*cur >= '0') && (*cur <= '9'))
621 links = (links * 10) + (*cur++ - '0');
627 while (*cur != ' ') {
640 while (*cur != ' ') {
652 while ((*cur >= '0') && (*cur <= '9'))
653 size = (size * 10) + (*cur++ - '0');
659 while (*cur != ' ') {
671 while ((*cur >= '0') && (*cur <= '9'))
672 day = (day * 10) + (*cur++ - '0');
677 if ((cur[1] == 0) || (cur[2] == 0))
679 if ((cur[1] == ':') || (cur[2] == ':')) {
680 while ((*cur >= '0') && (*cur <= '9'))
681 hour = (hour * 10) + (*cur++ - '0');
684 while ((*cur >= '0') && (*cur <= '9'))
685 minute = (minute * 10) + (*cur++ - '0');
687 while ((*cur >= '0') && (*cur <= '9'))
688 year = (year * 10) + (*cur++ - '0');
695 while ((*cur != '\n') && (*cur != '\r')) {
697 filename[i++] = *cur;
703 if ((*cur != '\n') && (*cur != '\r'))
705 while ((*cur == '\n') || (*cur == '\r'))
708 if (callback != NULL) {
709 callback(userData, filename, attrib, owner, group, size,
710 links, year, month, day, minute);
718 int listFtp(ftpListCallback callback, void *userData)
726 dataFd = dataConnectFtp();
728 len = snprintf(buf, sizeof(buf), "LIST -L\r\n");
732 res = send(ftpFd, buf, len, 0);
738 res = readFtpResponse(buf, sizeof(buf) - 1);
749 FD_SET(dataFd, &rfd);
751 FD_SET(dataFd, &efd);
752 res = select(dataFd + 1, &rfd, NULL, &efd, &tv);
762 res = checkFtpResponse();
779 read(dataFd, &buf[index],
780 sizeof(buf) - (index + 1))) < 0) {
790 write(1, &buf[index], len);
796 res = parseListFtp(&buf[base], callback, userData);
800 memmove(&buf[0], &buf[base], index - base);
811 int getFtpSocket(const char *filename)
815 if (filename == NULL)
817 dataFd = dataConnectFtp();
819 len = snprintf(buf, sizeof(buf), "TYPE I\r\n");
823 res = send(ftpFd, buf, len, 0);
829 res = readFtpResponse(buf, sizeof(buf) - 1);
835 len = snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
839 res = send(ftpFd, buf, len, 0);
845 res = readFtpResponse(buf, sizeof(buf) - 1);
860 return (dataConnectEndFtp());
866 int getFtp(ftpDataCallback callback, void *userData, const char *filename)
873 if (filename == NULL)
875 if (callback == NULL)
877 if (getFtpSocket(filename) < 0)
884 FD_SET(dataFd, &rfd);
885 res = select(dataFd + 1, &rfd, NULL, NULL, &tv);
895 res = checkFtpResponse();
910 if ((len = read(dataFd, &buf, sizeof(buf))) < 0) {
911 callback(userData, buf, len);
917 callback(userData, buf, len);
920 return (closeFtpSocket());
924 * Disconnect from the FTP server.
927 int disconnectFtp(void)