4 #define HAVE_MACHINE_TYPES_H 1
5 #define HAVE_ALLOCA_H 1
6 #define HAVE_NETINET_IN_SYSTM_H 1
9 #if HAVE_MACHINE_TYPES_H
10 # include <machine/types.h>
17 #if HAVE_NETINET_IN_SYSTM_H
18 # include <sys/types.h>
19 # include <netinet/in_systm.h>
35 #include <sys/socket.h>
37 #include <sys/types.h>
40 #include <netinet/in.h>
41 #include <netinet/ip.h>
42 #include <arpa/inet.h>
44 #include "inet_aton.h" /* for systems too stupid to provide this */
46 #define TIMEOUT_SECS 60
47 #define BUFFER_SIZE 4096
50 # define IPPORT_FTP 21
55 static int ftpCheckResponse(int sock, char ** str);
56 static int ftpCommand(int sock, char * command, ...);
57 static int ftpReadData(int sock, int out);
58 static int getHostAddress(const char * host, struct in_addr * address);
60 static int ftpCheckResponse(int sock, char ** str) {
61 static char buf[BUFFER_SIZE + 1];
63 fd_set emptySet, readSet;
64 char * chptr, * start;
65 struct timeval timeout;
66 int bytesRead, rc = 0;
75 FD_SET(sock, &readSet);
77 timeout.tv_sec = TIMEOUT_SECS;
80 rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
83 return FTPERR_BAD_SERVER_RESPONSE;
89 bytesRead = read(sock, buf + bufLength, sizeof(buf) - bufLength - 1);
91 bufLength += bytesRead;
93 buf[bufLength] = '\0';
95 /* divide the response into lines, checking each one to see if
96 we are finished or need to continue */
101 while (*chptr != '\n' && *chptr) chptr++;
103 if (*chptr == '\n') {
105 if (*(chptr - 1) == '\r') *(chptr - 1) = '\0';
106 if (str) *str = start;
109 if (!strncmp(start, errorCode, 3) && start[3] == ' ')
112 strncpy(errorCode, start, 3);
114 if (start[3] != '-') {
126 if (doesContinue && chptr > start) {
127 memcpy(buf, start, chptr - start - 1);
128 bufLength = chptr - start - 1;
132 } while (doesContinue && !rc);
134 if (*errorCode == '4' || *errorCode == '5') {
135 if (!strncmp(errorCode, "550", 3)) {
136 return FTPERR_FILE_NOT_FOUND;
139 return FTPERR_BAD_SERVER_RESPONSE;
147 int ftpCommand(int sock, char * command, ...) {
154 va_start(ap, command);
155 len = strlen(command) + 2;
156 s = va_arg(ap, char *);
158 len += strlen(s) + 1;
159 s = va_arg(ap, char *);
163 buf = alloca(len + 1);
165 va_start(ap, command);
166 strcpy(buf, command);
168 s = va_arg(ap, char *);
172 s = va_arg(ap, char *);
180 if (write(sock, buf, len) != len) {
181 return FTPERR_SERVER_IO_ERROR;
184 if ((rc = ftpCheckResponse(sock, NULL)))
190 static int getHostAddress(const char * host, struct in_addr * address) {
191 struct hostent * hostinfo;
193 if (isdigit(host[0])) {
194 if (!inet_aton(host, address)) {
195 return FTPERR_BAD_HOST_ADDR;
198 hostinfo = gethostbyname(host);
201 return FTPERR_BAD_HOSTNAME;
204 memcpy(address, hostinfo->h_addr_list[0], hostinfo->h_length);
210 int ftpOpen(char * host, char * name, char * password, char * proxy,
213 /*static char * lastHost = NULL;*/
214 struct in_addr serverAddress;
215 struct sockaddr_in destPort;
220 if (port < 0) port = IPPORT_FTP;
227 pw = getpwuid(getuid());
228 password = alloca(strlen(pw->pw_name) + 2);
229 strcpy(password, pw->pw_name);
230 strcat(password, "@");
237 buf = alloca(strlen(name) + strlen(host) + 5);
238 sprintf(buf, "%s@%s", name, host);
243 if ((rc = getHostAddress(host, &serverAddress))) return rc;
245 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
247 return FTPERR_FAILED_CONNECT;
250 destPort.sin_family = AF_INET;
251 destPort.sin_port = htons(port);
252 destPort.sin_addr = serverAddress;
254 if (connect(sock, (struct sockaddr *) &destPort, sizeof(destPort))) {
256 return FTPERR_FAILED_CONNECT;
259 /* ftpCheckResponse() assumes the socket is nonblocking */
260 if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
262 return FTPERR_FAILED_CONNECT;
265 if ((rc = ftpCheckResponse(sock, NULL))) {
269 if ((rc = ftpCommand(sock, "USER", name, NULL))) {
274 if ((rc = ftpCommand(sock, "PASS", password, NULL))) {
279 if ((rc = ftpCommand(sock, "TYPE", "I", NULL))) {
287 int ftpReadData(int sock, int out) {
288 char buf[BUFFER_SIZE];
289 fd_set emptySet, readSet;
290 struct timeval timeout;
296 FD_SET(sock, &readSet);
298 timeout.tv_sec = TIMEOUT_SECS;
301 rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
304 return FTPERR_SERVER_TIMEOUT;
307 return FTPERR_UNKNOWN;
310 bytesRead = read(sock, buf, sizeof(buf));
311 if (bytesRead == 0) {
316 if (write(out, buf, bytesRead) != bytesRead) {
318 return FTPERR_FILE_IO_ERROR;
323 int ftpGetFileDesc(int sock, char * remotename) {
325 struct sockaddr_in dataAddress;
332 if (write(sock, "PASV\r\n", 6) != 6) {
333 return FTPERR_SERVER_IO_ERROR;
335 if ((rc = ftpCheckResponse(sock, &passReply)))
336 return FTPERR_PASSIVE_ERROR;
339 while (*chptr && *chptr != '(') chptr++;
340 if (*chptr != '(') return FTPERR_PASSIVE_ERROR;
343 while (*chptr && *chptr != ')') chptr++;
344 if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
347 while (*chptr && *chptr != ',') chptr--;
348 if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
350 while (*chptr && *chptr != ',') chptr--;
351 if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
354 /* now passReply points to the IP portion, and chptr points to the
355 port number portion */
357 dataAddress.sin_family = AF_INET;
358 if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
359 return FTPERR_PASSIVE_ERROR;
361 dataAddress.sin_port = htons((i << 8) + j);
365 if (*chptr == ',') *chptr = '.';
368 if (!inet_aton(passReply, &dataAddress.sin_addr))
369 return FTPERR_PASSIVE_ERROR;
371 dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
372 if (dataSocket < 0) {
373 return FTPERR_FAILED_CONNECT;
376 retrCommand = alloca(strlen(remotename) + 20);
377 sprintf(retrCommand, "RETR %s\r\n", remotename);
378 i = strlen(retrCommand);
380 if (write(sock, retrCommand, i) != i) {
381 return FTPERR_SERVER_IO_ERROR;
384 if (connect(dataSocket, (struct sockaddr *) &dataAddress,
385 sizeof(dataAddress))) {
387 return FTPERR_FAILED_DATA_CONNECT;
390 if ((rc = ftpCheckResponse(sock, NULL))) {
398 int ftpGetFileDone(int sock) {
399 if (ftpCheckResponse(sock, NULL)) {
400 return FTPERR_BAD_SERVER_RESPONSE;
406 int ftpGetFile(int sock, char * remotename, int dest) {
409 dataSocket = ftpGetFileDesc(sock, remotename);
410 if (dataSocket < 0) return dataSocket;
412 rc = ftpReadData(dataSocket, dest);
417 return ftpGetFileDone(sock);
420 void ftpClose(int sock) {
424 const char *ftpStrerror(int errorNumber) {
425 switch (errorNumber) {
426 case FTPERR_BAD_SERVER_RESPONSE:
427 return ("Bad FTP server response");
429 case FTPERR_SERVER_IO_ERROR:
430 return("FTP IO error");
432 case FTPERR_SERVER_TIMEOUT:
433 return("FTP server timeout");
435 case FTPERR_BAD_HOST_ADDR:
436 return("Unable to lookup FTP server host address");
438 case FTPERR_BAD_HOSTNAME:
439 return("Unable to lookup FTP server host name");
441 case FTPERR_FAILED_CONNECT:
442 return("Failed to connect to FTP server");
444 case FTPERR_FAILED_DATA_CONNECT:
445 return("Failed to establish data connection to FTP server");
447 case FTPERR_FILE_IO_ERROR:
448 return("IO error to local file");
450 case FTPERR_PASSIVE_ERROR:
451 return("Error setting remote server to passive mode");
453 case FTPERR_FILE_NOT_FOUND:
454 return("File not found on server");
458 return("FTP Unknown or unexpected error");