59bc284cd303cb87654e7ab74c9d37bc67eef875
[oftp] / src / ftp.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <sys/socket.h>
6 #include <arpa/inet.h>
7 #include <netinet/in.h>
8 #include <netdb.h>
9 #include "darray.h"
10 #include "ftp.h"
11 #include "util.h"
12
13 #ifdef __unix__
14 #include <unistd.h>
15 #include <fcntl.h>
16 #define closesocket             close
17 #define fcntlsocket             fcntl
18 #endif
19
20 static int handle_control(struct ftp *ftp);
21 static int handle_data(struct ftp *ftp, int s);
22 static void proc_control(struct ftp *ftp, const char *buf);
23
24 struct ftp *ftp_alloc(void)
25 {
26         struct ftp *ftp;
27
28         if(!(ftp = calloc(1, sizeof *ftp))) {
29                 return 0;
30         }
31         ftp->ctl = ftp->data = -1;
32         return ftp;
33 }
34
35 void ftp_free(struct ftp *ftp)
36 {
37         if(!ftp) return;
38
39         if(ftp->ctl >= 0) {
40                 ftp_close(ftp);
41         }
42 }
43
44 int ftp_connect(struct ftp *ftp, const char *hostname, int port)
45 {
46         struct hostent *host;
47         struct sockaddr_in addr;
48
49         if((ftp->ctl = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
50                 errmsg("failed to create control socket\n");
51                 return -1;
52         }
53
54         if(!(host = gethostbyname(hostname))) {
55                 errmsg("failed to resolve host: %s\n", hostname);
56                 closesocket(ftp->ctl);
57                 ftp->ctl = -1;
58                 return -1;
59         }
60
61         memset(&addr, 0, sizeof addr);
62         addr.sin_family = AF_INET;
63         addr.sin_addr = *((struct in_addr*)host->h_addr);
64         addr.sin_port = htons(port);
65
66         if(connect(ftp->ctl, (struct sockaddr*)&addr, sizeof addr) == -1) {
67                 errmsg("failed to connect to: %s (port: %d)\n", hostname, port);
68                 closesocket(ftp->ctl);
69                 ftp->ctl = -1;
70                 return -1;
71         }
72
73         fcntlsocket(ftp->ctl, F_SETFL, fcntlsocket(ftp->ctl, F_GETFL) | O_NONBLOCK);
74         ftp->num_crecv = 0;
75         return 0;
76 }
77
78 void ftp_close(struct ftp *ftp)
79 {
80         if(ftp->ctl >= 0) {
81                 closesocket(ftp->ctl);
82                 ftp->ctl = -1;
83                 ftp->num_crecv = 0;
84         }
85         if(ftp->data >= 0) {
86                 closesocket(ftp->data);
87                 ftp->data = -1;
88                 ftp->num_drecv = 0;
89         }
90 }
91
92 int ftp_sockets(struct ftp *ftp, int *sockv, int maxsize)
93 {
94         if(ftp->ctl >= 0 && maxsize) {
95                 *sockv++ = ftp->ctl;
96                 maxsize--;
97         }
98         return 1;
99 }
100
101 int ftp_pending(struct ftp *ftp)
102 {
103         int maxfd = -1;
104         fd_set rdset;
105         struct timeval tv = {0, 0};
106
107         FD_ZERO(&rdset);
108         if(ftp->ctl >= 0) {
109                 FD_SET(ftp->ctl, &rdset);
110                 maxfd = ftp->ctl;
111         }
112         if(ftp->data >= 0) {
113                 FD_SET(ftp->data, &rdset);
114                 if(ftp->data > maxfd) maxfd = ftp->data;
115         }
116         if(maxfd == -1) return 0;
117
118         return select(maxfd + 1, &rdset, 0, 0, &tv) > 0 ? 1 : 0;
119 }
120
121 int ftp_handle(struct ftp *ftp, int s)
122 {
123         if(s == ftp->ctl) {
124                 return handle_control(ftp);
125         }
126         if(s == ftp->data) {
127                 return handle_data(ftp, s);
128         }
129         return -1;
130 }
131
132 static int handle_control(struct ftp *ftp)
133 {
134         int i, sz, rd;
135         char *buf, *start, *end;
136
137         while((sz = sizeof ftp->crecv - ftp->num_crecv) > 0) {
138                 start = ftp->crecv + ftp->num_crecv;
139                 if((rd = recv(ftp->ctl, start, sz, 0)) == -1) {
140                         if(errno == EINTR) continue;
141                         /* assume EWOULDBLOCK, try again next time */
142                         return 0;
143                 }
144                 if(rd == 0) {
145                         ftp_close(ftp);
146                         return -1;
147                 }
148
149                 end = start + rd;
150                 buf = ftp->crecv;
151                 for(i=0; i<rd; i++) {
152                         if(start[i] == '\n') {
153                                 start[i] = 0;
154                                 proc_control(ftp, buf);
155                                 buf = start + i + 1;
156                         }
157                 }
158                 if(buf != ftp->crecv && buf < end) {
159                         ftp->num_crecv = end - buf;
160                         memmove(ftp->crecv, buf, ftp->num_crecv);
161                 }
162         }
163         return 0;
164 }
165
166 static int handle_data(struct ftp *ftp, int s)
167 {
168         return -1;
169 }
170
171 static void proc_control(struct ftp *ftp, const char *buf)
172 {
173 }
174
175 int ftp_update(struct ftp *ftp)
176 {
177         return -1;
178 }
179
180 int ftp_chdir(struct ftp *ftp, const char *dirname)
181 {
182         return -1;
183 }