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>
22 /* #define DEBUG_FTP 1 */
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;
33 #define FTP_COMMAND_OK 200
34 #define FTP_SYNTAX_ERROR 500
35 #define FTP_GET_PASSWD 331
38 * Initialize the FTP handling.
43 gethostname(hostname, sizeof(hostname));
47 * Parsing of the server answer, we just extract the code.
49 * +XXX for last line of response
50 * -XXX for response to be continued
52 int parseFtpResponse(char *buf, int len)
58 if ((*buf >= '0') && (*buf <= '9'))
59 val = val * 10 + (*buf - '0');
63 if ((*buf >= '0') && (*buf <= '9'))
64 val = val * 10 + (*buf - '0');
68 if ((*buf >= '0') && (*buf <= '9'))
69 val = val * 10 + (*buf - '0');
79 * Read the response from the FTP server after a command.
80 * Returns the code number
83 int readFtpResponse(char *buf, int size)
93 if ((len = recv(ftpFd, buf, size - 1, 0)) < 0) {
110 res = parseFtpResponse(ptr, end - ptr);
115 fprintf(stderr, "readFtpResponse failed: %s\n",
120 while ((ptr < end) && (*ptr != '\n'))
125 "readFtpResponse: unexpected end %s\n",
128 return ((-res) / 100);
138 printf("Got %d\n", res);
144 * Get the response from the FTP server after a command.
145 * Returns the code number
148 int getFtpResponse(void)
150 char buf[16 * 1024 + 1];
161 res = select(ftpFd + 1, &rfd, NULL, NULL, &tv);
162 if (res <= 0) return(res);
165 return (readFtpResponse(buf, 16 * 1024));
169 * Check if there is a response from the FTP server after a command.
170 * Returns the code number, or 0
172 int checkFtpResponse(void)
182 switch (select(ftpFd + 1, &rfd, NULL, NULL, &tv)) {
193 return (readFtpResponse(buf, 1024));
197 * Send the user authentication
206 len = snprintf(buf, sizeof(buf), "USER anonymous\r\n");
210 res = send(ftpFd, buf, len, 0);
217 * Send the password authentication
227 snprintf(buf, sizeof(buf), "PASS mirrorfind@%s\r\n", hostname);
231 res = send(ftpFd, buf, len, 0);
247 len = snprintf(buf, sizeof(buf), "QUIT\r\n");
251 res = send(ftpFd, buf, len, 0);
256 * Connecting to the server, port 21 by default.
259 int connectFtp(const char *server, int port)
265 * Do the blocking DNS query.
267 hp = gethostbyname(server);
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);
279 ftpAddr.sin_port = htons(port);
280 ftpFd = socket(AF_INET, SOCK_STREAM, 0);
287 if (connect(ftpFd, (struct sockaddr *) &ftpAddr,
288 sizeof(struct sockaddr_in)) < 0) {
296 * Wait for the HELLO from the server.
298 res = getFtpResponse();
307 * State diagram for the login operation on the FTP server
312 * +---+ USER +---+------------->+---+
313 * | B |---------->| W | 2 ---->| E |
314 * +---+ +---+------ | -->+---+
317 * -------------- ----- | | |
323 * +---+ PASS +---+ 2 | ------>+---+
324 * | |---------->| W |------------->| S |
325 * +---+ +---+ ---------->+---+
328 * -------------- -------- |
334 * +---+ ACCT +---+-- | ----->+---+
335 * | |---------->| W | 4,5 -------->| F |
336 * +---+ +---+------------->+---+
345 res = getFtpResponse();
368 res = getFtpResponse();
374 "FTP server asking for ACCNT on anonymous\n");
390 * Check an FTP directory on the server
393 int changeFtpDirectory(char *directory)
400 * Expected response code for CWD:
404 * 500, 501, 502, 421, 530, 550
406 len = snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
410 res = send(ftpFd, buf, len, 0);
413 res = getFtpResponse();
436 unsigned char ad[6], *cur, *adp, *portp;
437 unsigned int temp[6];
438 struct sockaddr_in dataAddr;
439 socklen_t dataAddrLen;
441 dataFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
444 "dataConnectFtp: failed to create socket\n");
446 dataAddrLen = sizeof(dataAddr);
447 memset(&dataAddr, 0, dataAddrLen);
448 dataAddr.sin_family = AF_INET;
451 len = snprintf(buf, sizeof(buf), "PASV\r\n");
455 res = send(ftpFd, buf, len, 0);
461 res = readFtpResponse(buf, sizeof(buf) - 1);
469 * retry with an active connection
477 while (((*cur < '0') || (*cur > '9')) && *cur != '\0')
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");
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);
492 (dataFd, (struct sockaddr *) &dataAddr,
495 "Failed to create a data connection\n");
501 getsockname(dataFd, (struct sockaddr *) &dataAddr,
503 dataAddr.sin_port = 0;
505 (dataFd, (struct sockaddr *) &dataAddr,
507 fprintf(stderr, "Failed to bind a port\n");
512 getsockname(dataFd, (struct sockaddr *) &dataAddr,
515 if (listen(dataFd, 1) < 0) {
516 fprintf(stderr, "Could not listen on port %d\n",
517 ntohs(dataAddr.sin_port));
522 adp = (unsigned char *) &dataAddr.sin_addr;
523 portp = (unsigned char *) &dataAddr.sin_port;
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;
534 res = send(ftpFd, buf, len, 0);
540 res = getFtpResponse();
554 int dataConnectEndFtp()
560 res = getFtpResponse();
575 int parseListFtp(const char *list, ftpListCallback callback,
578 const char *cur = list;
588 unsigned long size = 0;
592 if (!strncmp(cur, "total", 5)) {
596 while ((*cur >= '0') && (*cur <= '9'))
597 links = (links * 10) + (*cur++ - '0');
598 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
601 } else if (*list == '+') {
604 while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
609 while (*cur != ' ') {
621 while ((*cur >= '0') && (*cur <= '9'))
622 links = (links * 10) + (*cur++ - '0');
628 while (*cur != ' ') {
641 while (*cur != ' ') {
653 while ((*cur >= '0') && (*cur <= '9'))
654 size = (size * 10) + (*cur++ - '0');
660 while (*cur != ' ') {
672 while ((*cur >= '0') && (*cur <= '9'))
673 day = (day * 10) + (*cur++ - '0');
678 if ((cur[1] == 0) || (cur[2] == 0))
680 if ((cur[1] == ':') || (cur[2] == ':')) {
681 while ((*cur >= '0') && (*cur <= '9'))
682 hour = (hour * 10) + (*cur++ - '0');
685 while ((*cur >= '0') && (*cur <= '9'))
686 minute = (minute * 10) + (*cur++ - '0');
688 while ((*cur >= '0') && (*cur <= '9'))
689 year = (year * 10) + (*cur++ - '0');
696 while ((*cur != '\n') && (*cur != '\r')) {
698 filename[i++] = *cur;
704 if ((*cur != '\n') && (*cur != '\r'))
706 while ((*cur == '\n') || (*cur == '\r'))
709 if (callback != NULL) {
710 callback(userData, filename, attrib, owner, group, size,
711 links, year, month, day, minute);
719 int listFtp(ftpListCallback callback, void *userData)
727 dataFd = dataConnectFtp();
729 len = snprintf(buf, sizeof(buf), "LIST -L\r\n");
733 res = send(ftpFd, buf, len, 0);
739 res = readFtpResponse(buf, sizeof(buf) - 1);
750 FD_SET(dataFd, &rfd);
752 FD_SET(dataFd, &efd);
753 res = select(dataFd + 1, &rfd, NULL, &efd, &tv);
763 res = checkFtpResponse();
780 read(dataFd, &buf[index],
781 sizeof(buf) - (index + 1))) < 0) {
791 write(1, &buf[index], len);
797 res = parseListFtp(&buf[base], callback, userData);
801 memmove(&buf[0], &buf[base], index - base);
812 int getFtpSocket(const char *filename)
816 if (filename == NULL)
818 dataFd = dataConnectFtp();
820 len = snprintf(buf, sizeof(buf), "TYPE I\r\n");
824 res = send(ftpFd, buf, len, 0);
830 res = readFtpResponse(buf, sizeof(buf) - 1);
836 len = snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
840 res = send(ftpFd, buf, len, 0);
846 res = readFtpResponse(buf, sizeof(buf) - 1);
861 return (dataConnectEndFtp());
867 int getFtp(ftpDataCallback callback, void *userData, const char *filename)
874 if (filename == NULL)
876 if (callback == NULL)
878 if (getFtpSocket(filename) < 0)
885 FD_SET(dataFd, &rfd);
886 res = select(dataFd + 1, &rfd, NULL, NULL, &tv);
896 res = checkFtpResponse();
911 if ((len = read(dataFd, &buf, sizeof(buf))) < 0) {
912 callback(userData, buf, len);
918 callback(userData, buf, len);
921 return (closeFtpSocket());
925 * Disconnect from the FTP server.
928 int disconnectFtp(void)