Include sys/socket.h if it's available
[platform/upstream/rpm.git] / ftp.c
1 #if HAVE_CONFIG_H
2 # include "config.h"
3 #else
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
8 #endif
9
10 #if HAVE_MACHINE_TYPES_H
11 # include <machine/types.h>
12 #endif
13
14 #if HAVE_ALLOCA_H
15 # include <alloca.h>
16 #endif
17
18 #if HAVE_SYS_SOCKET_H
19 # include <sys/socket.h>
20 #endif
21
22 #if HAVE_NETINET_IN_SYSTM_H
23 # include <sys/types.h>
24 # include <netinet/in_systm.h>
25 #endif
26
27 #if ! HAVE_HERRNO
28 extern int h_errno;
29 #endif
30
31 #include <ctype.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <netdb.h>
35 #include <pwd.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/socket.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <unistd.h>
44
45 #include <netinet/in.h>
46 #include <netinet/ip.h>
47 #include <arpa/inet.h>
48
49 #include "inet_aton.h"          /* for systems too stupid to provide this */
50
51 #define TIMEOUT_SECS 60
52 #define BUFFER_SIZE 4096
53
54 #ifndef IPPORT_FTP
55 # define IPPORT_FTP 21
56 #endif
57
58 #include "ftp.h"
59
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);
64
65 static int ftpCheckResponse(int sock, char ** str) {
66     static char buf[BUFFER_SIZE + 1];
67     int bufLength = 0; 
68     fd_set emptySet, readSet;
69     char * chptr, * start;
70     struct timeval timeout;
71     int bytesRead, rc = 0;
72     int doesContinue = 1;
73     char errorCode[4];
74  
75     errorCode[0] = '\0';
76     
77     do {
78         FD_ZERO(&emptySet);
79         FD_ZERO(&readSet);
80         FD_SET(sock, &readSet);
81
82         timeout.tv_sec = TIMEOUT_SECS;
83         timeout.tv_usec = 0;
84     
85         rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
86         if (rc < 1) {
87             if (rc==0) 
88                 return FTPERR_BAD_SERVER_RESPONSE;
89             else
90                 rc = FTPERR_UNKNOWN;
91         } else
92             rc = 0;
93
94         bytesRead = read(sock, buf + bufLength, sizeof(buf) - bufLength - 1);
95
96         bufLength += bytesRead;
97
98         buf[bufLength] = '\0';
99
100         /* divide the response into lines, checking each one to see if 
101            we are finished or need to continue */
102
103         start = chptr = buf;
104
105         do {
106             while (*chptr != '\n' && *chptr) chptr++;
107
108             if (*chptr == '\n') {
109                 *chptr = '\0';
110                 if (*(chptr - 1) == '\r') *(chptr - 1) = '\0';
111                 if (str) *str = start;
112
113                 if (errorCode[0]) {
114                     if (!strncmp(start, errorCode, 3) && start[3] == ' ')
115                         doesContinue = 0;
116                 } else {
117                     strncpy(errorCode, start, 3);
118                     errorCode[3] = '\0';
119                     if (start[3] != '-') {
120                         doesContinue = 0;
121                     } 
122                 }
123
124                 start = chptr + 1;
125                 chptr++;
126             } else {
127                 chptr++;
128             }
129         } while (*chptr);
130
131         if (doesContinue && chptr > start) {
132             memcpy(buf, start, chptr - start - 1);
133             bufLength = chptr - start - 1;
134         } else {
135             bufLength = 0;
136         }
137     } while (doesContinue && !rc);
138
139     if (*errorCode == '4' || *errorCode == '5') {
140         if (!strncmp(errorCode, "550", 3)) {
141             return FTPERR_FILE_NOT_FOUND;
142         }
143
144         return FTPERR_BAD_SERVER_RESPONSE;
145     }
146
147     if (rc) return rc;
148
149     return 0;
150 }
151
152 int ftpCommand(int sock, char * command, ...) {
153     va_list ap;
154     int len;
155     char * s;
156     char * buf;
157     int rc;
158
159     va_start(ap, command);
160     len = strlen(command) + 2;
161     s = va_arg(ap, char *);
162     while (s) {
163         len += strlen(s) + 1;
164         s = va_arg(ap, char *);
165     }
166     va_end(ap);
167
168     buf = alloca(len + 1);
169
170     va_start(ap, command);
171     strcpy(buf, command);
172     strcat(buf, " ");
173     s = va_arg(ap, char *);
174     while (s) {
175         strcat(buf, s);
176         strcat(buf, " ");
177         s = va_arg(ap, char *);
178     }
179     va_end(ap);
180
181     buf[len - 2] = '\r';
182     buf[len - 1] = '\n';
183     buf[len] = '\0';
184      
185     if (write(sock, buf, len) != len) {
186         return FTPERR_SERVER_IO_ERROR;
187     }
188
189     if ((rc = ftpCheckResponse(sock, NULL)))
190         return rc;
191
192     return 0;
193 }
194
195 static int getHostAddress(const char * host, struct in_addr * address) {
196     struct hostent * hostinfo;
197
198     if (isdigit(host[0])) {
199       if (!inet_aton(host, address)) {
200           return FTPERR_BAD_HOST_ADDR;
201       }
202     } else {
203       hostinfo = gethostbyname(host);
204       if (!hostinfo) {
205           errno = h_errno;
206           return FTPERR_BAD_HOSTNAME;
207       }
208       
209       memcpy(address, hostinfo->h_addr_list[0], hostinfo->h_length);
210     }
211     
212     return 0;
213 }
214
215 int ftpOpen(char * host, char * name, char * password, char * proxy,
216             int port) {
217     static int sock;
218     /*static char * lastHost = NULL;*/
219     struct in_addr serverAddress;
220     struct sockaddr_in destPort;
221     struct passwd * pw;
222     char * buf;
223     int rc;
224
225     if (port < 0) port = IPPORT_FTP;
226
227     if (!name)
228         name = "anonymous";
229
230     if (!password) {
231         if (getuid()) {
232             pw = getpwuid(getuid());
233             password = alloca(strlen(pw->pw_name) + 2);
234             strcpy(password, pw->pw_name);
235             strcat(password, "@");
236         } else {
237             password = "root@";
238         }
239     }
240
241     if (proxy) {
242         buf = alloca(strlen(name) + strlen(host) + 5);
243         sprintf(buf, "%s@%s", name, host);
244         name = buf;
245         host = proxy;
246     }
247
248     if ((rc = getHostAddress(host, &serverAddress))) return rc;
249
250     sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
251     if (sock < 0) {
252         return FTPERR_FAILED_CONNECT;
253     }
254
255     destPort.sin_family = AF_INET;
256     destPort.sin_port = htons(port);
257     destPort.sin_addr = serverAddress;
258
259     if (connect(sock, (struct sockaddr *) &destPort, sizeof(destPort))) {
260         close(sock);
261         return FTPERR_FAILED_CONNECT;
262     }
263
264     /* ftpCheckResponse() assumes the socket is nonblocking */
265     if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
266         close(sock);
267         return FTPERR_FAILED_CONNECT;
268     }
269
270     if ((rc = ftpCheckResponse(sock, NULL))) {
271         return rc;     
272     }
273
274     if ((rc = ftpCommand(sock, "USER", name, NULL))) {
275         close(sock);
276         return rc;
277     }
278
279     if ((rc = ftpCommand(sock, "PASS", password, NULL))) {
280         close(sock);
281         return rc;
282     }
283
284     if ((rc = ftpCommand(sock, "TYPE", "I", NULL))) {
285         close(sock);
286         return rc;
287     }
288
289     return sock;
290 }
291
292 int ftpReadData(int sock, int out) {
293     char buf[BUFFER_SIZE];
294     fd_set emptySet, readSet;
295     struct timeval timeout;
296     int bytesRead, rc;
297     
298     while (1) {
299         FD_ZERO(&emptySet);
300         FD_ZERO(&readSet);
301         FD_SET(sock, &readSet);
302
303         timeout.tv_sec = TIMEOUT_SECS;
304         timeout.tv_usec = 0;
305     
306         rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
307         if (rc == 0) {
308             close(sock);
309             return FTPERR_SERVER_TIMEOUT;
310         } else if (rc < 0) {
311             close(sock);
312             return FTPERR_UNKNOWN;
313         }
314
315         bytesRead = read(sock, buf, sizeof(buf));
316         if (bytesRead == 0) {
317             close(sock);
318             return 0;
319         }
320
321         if (write(out, buf, bytesRead) != bytesRead) {
322             close(sock);
323             return FTPERR_FILE_IO_ERROR;
324         }
325     }
326 }
327
328 int ftpGetFileDesc(int sock, char * remotename) {
329     int dataSocket;
330     struct sockaddr_in dataAddress;
331     int i, j;
332     char * passReply;
333     char * chptr;
334     char * retrCommand;
335     int rc;
336
337     if (write(sock, "PASV\r\n", 6) != 6) {
338         return FTPERR_SERVER_IO_ERROR;
339     }
340     if ((rc = ftpCheckResponse(sock, &passReply)))
341         return FTPERR_PASSIVE_ERROR;
342
343     chptr = passReply;
344     while (*chptr && *chptr != '(') chptr++;
345     if (*chptr != '(') return FTPERR_PASSIVE_ERROR; 
346     chptr++;
347     passReply = chptr;
348     while (*chptr && *chptr != ')') chptr++;
349     if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
350     *chptr-- = '\0';
351
352     while (*chptr && *chptr != ',') chptr--;
353     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
354     chptr--;
355     while (*chptr && *chptr != ',') chptr--;
356     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
357     *chptr++ = '\0';
358     
359     /* now passReply points to the IP portion, and chptr points to the
360        port number portion */
361
362     dataAddress.sin_family = AF_INET;
363     if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
364         return FTPERR_PASSIVE_ERROR;
365     }
366     dataAddress.sin_port = htons((i << 8) + j);
367
368     chptr = passReply;
369     while (*chptr++) {
370         if (*chptr == ',') *chptr = '.';
371     }
372
373     if (!inet_aton(passReply, &dataAddress.sin_addr)) 
374         return FTPERR_PASSIVE_ERROR;
375
376     dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
377     if (dataSocket < 0) {
378         return FTPERR_FAILED_CONNECT;
379     }
380
381     retrCommand = alloca(strlen(remotename) + 20);
382     sprintf(retrCommand, "RETR %s\r\n", remotename);
383     i = strlen(retrCommand);
384    
385     if (write(sock, retrCommand, i) != i) {
386         return FTPERR_SERVER_IO_ERROR;
387     }
388
389     if (connect(dataSocket, (struct sockaddr *) &dataAddress, 
390                 sizeof(dataAddress))) {
391         close(dataSocket);
392         return FTPERR_FAILED_DATA_CONNECT;
393     }
394
395     if ((rc = ftpCheckResponse(sock, NULL))) {
396         close(dataSocket);
397         return rc;
398     }
399
400     return dataSocket;
401 }
402
403 int ftpGetFileDone(int sock) {
404     if (ftpCheckResponse(sock, NULL)) {
405         return FTPERR_BAD_SERVER_RESPONSE;
406     }
407
408     return 0;
409 }
410
411 int ftpGetFile(int sock, char * remotename, int dest) {
412     int dataSocket, rc;
413
414     dataSocket = ftpGetFileDesc(sock, remotename);
415     if (dataSocket < 0) return dataSocket;
416
417     rc = ftpReadData(dataSocket, dest);
418     close(dataSocket);
419     
420     if (rc) return rc;
421
422     return ftpGetFileDone(sock);
423 }
424
425 void ftpClose(int sock) {
426     close(sock);
427 }
428
429 const char *ftpStrerror(int errorNumber) {
430   switch (errorNumber) {
431     case FTPERR_BAD_SERVER_RESPONSE:
432       return ("Bad FTP server response");
433
434     case FTPERR_SERVER_IO_ERROR:
435       return("FTP IO error");
436
437     case FTPERR_SERVER_TIMEOUT:
438       return("FTP server timeout");
439
440     case FTPERR_BAD_HOST_ADDR:
441       return("Unable to lookup FTP server host address");
442
443     case FTPERR_BAD_HOSTNAME:
444       return("Unable to lookup FTP server host name");
445
446     case FTPERR_FAILED_CONNECT:
447       return("Failed to connect to FTP server");
448
449     case FTPERR_FAILED_DATA_CONNECT:
450       return("Failed to establish data connection to FTP server");
451
452     case FTPERR_FILE_IO_ERROR:
453       return("IO error to local file");
454
455     case FTPERR_PASSIVE_ERROR:
456       return("Error setting remote server to passive mode");
457
458     case FTPERR_FILE_NOT_FOUND:
459       return("File not found on server");
460
461     case FTPERR_UNKNOWN:
462     default:
463       return("FTP Unknown or unexpected error");
464   }
465 }
466