4 #define HAVE_MACHINE_TYPES_H 1
5 #define HAVE_ALLOCA_H 1
6 #define HAVE_NETINET_IN_SYSTM_H 1
7 #define HAVE_SYS_SOCKET_H 1
10 #if HAVE_MACHINE_TYPES_H
11 # include <machine/types.h>
19 # include <sys/socket.h>
22 #if HAVE_NETINET_IN_SYSTM_H
23 # include <sys/types.h>
24 # include <netinet/in_systm.h>
40 #include <sys/socket.h>
42 #include <sys/types.h>
45 #include <netinet/in.h>
46 #include <netinet/ip.h>
47 #include <arpa/inet.h>
49 #include "inet_aton.h" /* for systems too stupid to provide this */
51 #define TIMEOUT_SECS 60
52 #define BUFFER_SIZE 4096
55 # define IPPORT_FTP 21
60 static int ftpCheckResponse(int sock, char ** str);
61 static int ftpCommand(int sock, char * command, ...);
62 static int ftpReadData(int sock, int out);
63 static int getHostAddress(const char * host, struct in_addr * address);
65 static int ftpCheckResponse(int sock, char ** str) {
66 static char buf[BUFFER_SIZE + 1];
68 fd_set emptySet, readSet;
69 char * chptr, * start;
70 struct timeval timeout;
71 int bytesRead, rc = 0;
80 FD_SET(sock, &readSet);
82 timeout.tv_sec = TIMEOUT_SECS;
85 rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
88 return FTPERR_BAD_SERVER_RESPONSE;
94 bytesRead = read(sock, buf + bufLength, sizeof(buf) - bufLength - 1);
96 bufLength += bytesRead;
98 buf[bufLength] = '\0';
100 /* divide the response into lines, checking each one to see if
101 we are finished or need to continue */
106 while (*chptr != '\n' && *chptr) chptr++;
108 if (*chptr == '\n') {
110 if (*(chptr - 1) == '\r') *(chptr - 1) = '\0';
111 if (str) *str = start;
114 if (!strncmp(start, errorCode, 3) && start[3] == ' ')
117 strncpy(errorCode, start, 3);
119 if (start[3] != '-') {
131 if (doesContinue && chptr > start) {
132 memcpy(buf, start, chptr - start - 1);
133 bufLength = chptr - start - 1;
137 } while (doesContinue && !rc);
139 if (*errorCode == '4' || *errorCode == '5') {
140 if (!strncmp(errorCode, "550", 3)) {
141 return FTPERR_FILE_NOT_FOUND;
144 return FTPERR_BAD_SERVER_RESPONSE;
152 int ftpCommand(int sock, char * command, ...) {
159 va_start(ap, command);
160 len = strlen(command) + 2;
161 s = va_arg(ap, char *);
163 len += strlen(s) + 1;
164 s = va_arg(ap, char *);
168 buf = alloca(len + 1);
170 va_start(ap, command);
171 strcpy(buf, command);
173 s = va_arg(ap, char *);
177 s = va_arg(ap, char *);
185 if (write(sock, buf, len) != len) {
186 return FTPERR_SERVER_IO_ERROR;
189 if ((rc = ftpCheckResponse(sock, NULL)))
195 static int getHostAddress(const char * host, struct in_addr * address) {
196 struct hostent * hostinfo;
198 if (isdigit(host[0])) {
199 if (!inet_aton(host, address)) {
200 return FTPERR_BAD_HOST_ADDR;
203 hostinfo = gethostbyname(host);
206 return FTPERR_BAD_HOSTNAME;
209 memcpy(address, hostinfo->h_addr_list[0], hostinfo->h_length);
215 int ftpOpen(char * host, char * name, char * password, char * proxy,
218 /*static char * lastHost = NULL;*/
219 struct in_addr serverAddress;
220 struct sockaddr_in destPort;
225 if (port < 0) port = IPPORT_FTP;
232 pw = getpwuid(getuid());
233 password = alloca(strlen(pw->pw_name) + 2);
234 strcpy(password, pw->pw_name);
235 strcat(password, "@");
242 buf = alloca(strlen(name) + strlen(host) + 5);
243 sprintf(buf, "%s@%s", name, host);
248 if ((rc = getHostAddress(host, &serverAddress))) return rc;
250 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
252 return FTPERR_FAILED_CONNECT;
255 destPort.sin_family = AF_INET;
256 destPort.sin_port = htons(port);
257 destPort.sin_addr = serverAddress;
259 if (connect(sock, (struct sockaddr *) &destPort, sizeof(destPort))) {
261 return FTPERR_FAILED_CONNECT;
264 /* ftpCheckResponse() assumes the socket is nonblocking */
265 if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
267 return FTPERR_FAILED_CONNECT;
270 if ((rc = ftpCheckResponse(sock, NULL))) {
274 if ((rc = ftpCommand(sock, "USER", name, NULL))) {
279 if ((rc = ftpCommand(sock, "PASS", password, NULL))) {
284 if ((rc = ftpCommand(sock, "TYPE", "I", NULL))) {
292 int ftpReadData(int sock, int out) {
293 char buf[BUFFER_SIZE];
294 fd_set emptySet, readSet;
295 struct timeval timeout;
301 FD_SET(sock, &readSet);
303 timeout.tv_sec = TIMEOUT_SECS;
306 rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
309 return FTPERR_SERVER_TIMEOUT;
312 return FTPERR_UNKNOWN;
315 bytesRead = read(sock, buf, sizeof(buf));
316 if (bytesRead == 0) {
321 if (write(out, buf, bytesRead) != bytesRead) {
323 return FTPERR_FILE_IO_ERROR;
328 int ftpGetFileDesc(int sock, char * remotename) {
330 struct sockaddr_in dataAddress;
337 if (write(sock, "PASV\r\n", 6) != 6) {
338 return FTPERR_SERVER_IO_ERROR;
340 if ((rc = ftpCheckResponse(sock, &passReply)))
341 return FTPERR_PASSIVE_ERROR;
344 while (*chptr && *chptr != '(') chptr++;
345 if (*chptr != '(') return FTPERR_PASSIVE_ERROR;
348 while (*chptr && *chptr != ')') chptr++;
349 if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
352 while (*chptr && *chptr != ',') chptr--;
353 if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
355 while (*chptr && *chptr != ',') chptr--;
356 if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
359 /* now passReply points to the IP portion, and chptr points to the
360 port number portion */
362 dataAddress.sin_family = AF_INET;
363 if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
364 return FTPERR_PASSIVE_ERROR;
366 dataAddress.sin_port = htons((i << 8) + j);
370 if (*chptr == ',') *chptr = '.';
373 if (!inet_aton(passReply, &dataAddress.sin_addr))
374 return FTPERR_PASSIVE_ERROR;
376 dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
377 if (dataSocket < 0) {
378 return FTPERR_FAILED_CONNECT;
381 retrCommand = alloca(strlen(remotename) + 20);
382 sprintf(retrCommand, "RETR %s\r\n", remotename);
383 i = strlen(retrCommand);
385 if (write(sock, retrCommand, i) != i) {
386 return FTPERR_SERVER_IO_ERROR;
389 if (connect(dataSocket, (struct sockaddr *) &dataAddress,
390 sizeof(dataAddress))) {
392 return FTPERR_FAILED_DATA_CONNECT;
395 if ((rc = ftpCheckResponse(sock, NULL))) {
403 int ftpGetFileDone(int sock) {
404 if (ftpCheckResponse(sock, NULL)) {
405 return FTPERR_BAD_SERVER_RESPONSE;
411 int ftpGetFile(int sock, char * remotename, int dest) {
414 dataSocket = ftpGetFileDesc(sock, remotename);
415 if (dataSocket < 0) return dataSocket;
417 rc = ftpReadData(dataSocket, dest);
422 return ftpGetFileDone(sock);
425 void ftpClose(int sock) {
429 const char *ftpStrerror(int errorNumber) {
430 switch (errorNumber) {
431 case FTPERR_BAD_SERVER_RESPONSE:
432 return ("Bad FTP server response");
434 case FTPERR_SERVER_IO_ERROR:
435 return("FTP IO error");
437 case FTPERR_SERVER_TIMEOUT:
438 return("FTP server timeout");
440 case FTPERR_BAD_HOST_ADDR:
441 return("Unable to lookup FTP server host address");
443 case FTPERR_BAD_HOSTNAME:
444 return("Unable to lookup FTP server host name");
446 case FTPERR_FAILED_CONNECT:
447 return("Failed to connect to FTP server");
449 case FTPERR_FAILED_DATA_CONNECT:
450 return("Failed to establish data connection to FTP server");
452 case FTPERR_FILE_IO_ERROR:
453 return("IO error to local file");
455 case FTPERR_PASSIVE_ERROR:
456 return("Error setting remote server to passive mode");
458 case FTPERR_FILE_NOT_FOUND:
459 return("File not found on server");
463 return("FTP Unknown or unexpected error");