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