1 /* ftp.c
2 * ftp:// processing
3 * (c) 2002 Mikulas Patocka
4 * This file is a part of the Links program, released under GPL.
5 */
6
7 #include "links.h"
8
9 #define FTP_BUF 16384
10
11 struct ftp_connection_info {
12 int pending_commands;
13 int opc;
14 int pasv;
15 int eprt_epsv;
16 int dir;
17 int rest_sent;
18 int we_are_in_root;
19 int conn_st;
20 int d;
21 int dpos;
22 int buf_pos;
23 unsigned char ftp_buffer[FTP_BUF];
24 unsigned char cmdbuf[1];
25 };
26
27 static void ftp_get_banner(struct connection *);
28 static void ftp_got_banner(struct connection *, struct read_buffer *);
29 static void ftp_login(struct connection *);
30 static void ftp_logged(struct connection *);
31 static void ftp_sent_passwd(struct connection *);
32 static void ftp_got_info(struct connection *, struct read_buffer *);
33 static void ftp_got_user_info(struct connection *, struct read_buffer *);
34 static void ftp_dummy_info(struct connection *, struct read_buffer *);
35 static void ftp_pass_info(struct connection *, struct read_buffer *);
36 static void ftp_send_retr_req(struct connection *, int, int);
37 static struct ftp_connection_info *add_file_cmd_to_str(struct connection *, int);
38 static void ftp_retr_1(struct connection *);
39 static void ftp_retr_file(struct connection *, struct read_buffer *);
40 static void ftp_got_final_response(struct connection *, struct read_buffer *);
41 static void created_data_connection(struct connection *);
42 static void got_something_from_data_connection(struct connection *);
43 static void ftp_end_request(struct connection *, int);
44 static int get_ftp_response(struct connection *, struct read_buffer *, int);
45 static int ftp_process_dirlist(struct cache_entry *, off_t *, int *, unsigned char *, int, int, int, int *);
46
47
get_ftp_response(struct connection * c,struct read_buffer * rb,int part)48 static int get_ftp_response(struct connection *c, struct read_buffer *rb, int part)
49 {
50 int l;
51 set_timeout(c);
52 again:
53 for (l = 0; l < rb->len; l++) if (rb->data[l] == 10) {
54 unsigned char *e;
55 long k = strtoul(cast_const_char rb->data, (char **)(void *)&e, 10);
56 if (e != rb->data + 3 || k < 100 || k >= 1000) return -1;
57 if (*e == '-') {
58 int i;
59 for (i = 0; i < rb->len - 5; i++) {
60 if (rb->data[i] == 10 && !memcmp(rb->data+i+1, rb->data, 3) && rb->data[i+4] == ' ') {
61 for (i++; i < rb->len; i++) if (rb->data[i] == 10) goto ok;
62 return 0;
63 }
64 }
65 return 0;
66 ok:
67 l = i;
68 }
69 if (!part && k >= 100 && k < 200) {
70 kill_buffer_data(rb, l + 1);
71 goto again;
72 }
73 if (part == 2) return (int)k;
74 kill_buffer_data(rb, l + 1);
75 return (int)k;
76 }
77 return 0;
78 }
79
ftp_func(struct connection * c)80 void ftp_func(struct connection *c)
81 {
82 int we_are_in_root;
83 unsigned char *de, *d;
84 int del, bad_url;
85 d = get_url_data(c->url);
86 de = init_str(), del = 0;
87 add_conv_str(&de, &del, d, (int)strcspn(cast_const_char d, POST_CHAR_STRING), -2);
88 bad_url = !!strchr(cast_const_char de, 10);
89 mem_free(de);
90 if (bad_url) {
91 setcstate(c, S_BAD_URL);
92 abort_connection(c);
93 return;
94 }
95
96 if (get_keepalive_socket(c, &we_are_in_root)) {
97 int p;
98 if ((p = get_port(c->url)) == -1) {
99 setcstate(c, S_BAD_URL);
100 abort_connection(c);
101 return;
102 }
103 make_connection(c, p, &c->sock1, ftp_options.fast_ftp ? ftp_login : ftp_get_banner);
104 } else {
105 ftp_send_retr_req(c, S_SENT, we_are_in_root);
106 }
107 }
108
ftp_get_banner(struct connection * c)109 static void ftp_get_banner(struct connection *c)
110 {
111 struct read_buffer *rb;
112 set_timeout(c);
113 setcstate(c, S_SENT);
114 if (!(rb = alloc_read_buffer(c))) return;
115 read_from_socket(c, c->sock1, rb, ftp_got_banner);
116 }
117
ftp_got_banner(struct connection * c,struct read_buffer * rb)118 static void ftp_got_banner(struct connection *c, struct read_buffer *rb)
119 {
120 int g = get_ftp_response(c, rb, 0);
121 if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
122 if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_banner); return; }
123 if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); retry_connection(c); return; }
124 ftp_login(c);
125 }
126
ftp_login(struct connection * c)127 static void ftp_login(struct connection *c)
128 {
129 unsigned char *login;
130 unsigned char *u;
131 int logl = 0;
132 set_timeout(c);
133 login = init_str();
134 add_to_str(&login, &logl, cast_uchar "USER ");
135 if ((u = get_user_name(c->url)) && *u) add_to_str(&login, &logl, u);
136 else add_to_str(&login, &logl, cast_uchar "anonymous");
137 if (u) mem_free(u);
138 if (ftp_options.fast_ftp) {
139 struct ftp_connection_info *fi;
140 add_to_str(&login, &logl, cast_uchar "\r\nPASS ");
141 if ((u = get_pass(c->url)) && *u) add_to_str(&login, &logl, u);
142 else add_to_str(&login, &logl, ftp_options.anon_pass);
143 if (u) mem_free(u);
144 add_to_str(&login, &logl, cast_uchar "\r\n");
145 if (!(fi = add_file_cmd_to_str(c, 0))) {
146 mem_free(login);
147 return;
148 }
149 add_to_str(&login, &logl, fi->cmdbuf);
150 } else add_to_str(&login, &logl, cast_uchar "\r\n");
151 write_to_socket(c, c->sock1, login, logl, ftp_logged);
152 mem_free(login);
153 setcstate(c, S_SENT);
154 }
155
ftp_logged(struct connection * c)156 static void ftp_logged(struct connection *c)
157 {
158 struct read_buffer *rb;
159 if (!(rb = alloc_read_buffer(c))) return;
160 if (!ftp_options.fast_ftp) {
161 ftp_got_user_info(c, rb);
162 return;
163 }
164 read_from_socket(c, c->sock1, rb, ftp_got_info);
165 }
166
ftp_got_info(struct connection * c,struct read_buffer * rb)167 static void ftp_got_info(struct connection *c, struct read_buffer *rb)
168 {
169 int g = get_ftp_response(c, rb, 0);
170 if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
171 if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_info); return; }
172 if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); retry_connection(c); return; }
173 ftp_got_user_info(c, rb);
174 }
175
ftp_got_user_info(struct connection * c,struct read_buffer * rb)176 static void ftp_got_user_info(struct connection *c, struct read_buffer *rb)
177 {
178 int g = get_ftp_response(c, rb, 0);
179 if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
180 if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_user_info); return; }
181 if (g >= 530 && g < 540) { setcstate(c, S_FTP_LOGIN); retry_connection(c); return; }
182 if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); retry_connection(c); return; }
183 if (g >= 200 && g < 300) {
184 if (ftp_options.fast_ftp) ftp_dummy_info(c, rb);
185 else ftp_send_retr_req(c, S_GETH, 0);
186 } else {
187 if (ftp_options.fast_ftp) ftp_pass_info(c, rb);
188 else {
189 unsigned char *login;
190 unsigned char *u;
191 int logl = 0;
192 login = init_str();
193 add_to_str(&login, &logl, cast_uchar "PASS ");
194 if ((u = get_pass(c->url)) && *u) add_to_str(&login, &logl, u);
195 else add_to_str(&login, &logl, ftp_options.anon_pass);
196 if (u) mem_free(u);
197 add_to_str(&login, &logl, cast_uchar "\r\n");
198 write_to_socket(c, c->sock1, login, logl, ftp_sent_passwd);
199 mem_free(login);
200 setcstate(c, S_LOGIN);
201 }
202 }
203 }
204
ftp_dummy_info(struct connection * c,struct read_buffer * rb)205 static void ftp_dummy_info(struct connection *c, struct read_buffer *rb)
206 {
207 int g = get_ftp_response(c, rb, 0);
208 if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
209 if (!g) { read_from_socket(c, c->sock1, rb, ftp_dummy_info); return; }
210 ftp_retr_file(c, rb);
211 }
212
ftp_sent_passwd(struct connection * c)213 static void ftp_sent_passwd(struct connection *c)
214 {
215 struct read_buffer *rb;
216 if (!(rb = alloc_read_buffer(c))) return;
217 read_from_socket(c, c->sock1, rb, ftp_pass_info);
218 }
219
ftp_pass_info(struct connection * c,struct read_buffer * rb)220 static void ftp_pass_info(struct connection *c, struct read_buffer *rb)
221 {
222 int g = get_ftp_response(c, rb, 0);
223 if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
224 if (!g) { read_from_socket(c, c->sock1, rb, ftp_pass_info); setcstate(c, S_LOGIN); return; }
225 if (g >= 530 && g < 540) { setcstate(c, S_FTP_LOGIN); abort_connection(c); return; }
226 if (g >= 400) { setcstate(c, S_FTP_UNAVAIL); abort_connection(c); return; }
227 if (ftp_options.fast_ftp) ftp_retr_file(c, rb);
228 else ftp_send_retr_req(c, S_GETH, 0);
229 }
230
add_port_pasv(unsigned char ** s,int * l,struct ftp_connection_info * inf,unsigned char * port_string)231 static void add_port_pasv(unsigned char **s, int *l, struct ftp_connection_info *inf, unsigned char *port_string)
232 {
233 if (!inf->pasv) {
234 if (inf->eprt_epsv) {
235 add_to_str(s, l, cast_uchar "EPRT ");
236 add_to_str(s, l, port_string);
237 } else {
238 add_to_str(s, l, cast_uchar "PORT ");
239 add_to_str(s, l, port_string);
240 }
241 } else {
242 if (inf->eprt_epsv) {
243 add_to_str(s, l, cast_uchar "EPSV");
244 } else {
245 add_to_str(s, l, cast_uchar "PASV");
246 }
247 }
248 add_to_str(s, l, cast_uchar "\r\n");
249 }
250
add_file_cmd_to_str(struct connection * c,int we_are_in_root)251 static struct ftp_connection_info *add_file_cmd_to_str(struct connection *c, int we_are_in_root)
252 {
253 unsigned char *d = get_url_data(c->url);
254 unsigned char *dd, *de;
255 int del;
256 unsigned char port_string[50];
257 struct ftp_connection_info *inf, *inf2;
258 unsigned char *s;
259 int l;
260 if (!d) {
261 internal("get_url_data failed");
262 setcstate(c, S_INTERNAL);
263 abort_connection(c);
264 return NULL;
265 }
266
267 de = init_str(), del = 0;
268 add_conv_str(&de, &del, d, (int)strlen(cast_const_char d), -2);
269 d = de;
270 inf = mem_alloc(sizeof(struct ftp_connection_info));
271 memset(inf, 0, sizeof(struct ftp_connection_info));
272 l = 0;
273 s = init_str();
274 inf->we_are_in_root = we_are_in_root;
275 inf->pasv = ftp_options.passive_ftp;
276 #ifdef LINKS_2
277 if (*c->socks_proxy) inf->pasv = 1;
278 if (ftp_options.eprt_epsv || is_ipv6(c->sock1)) inf->eprt_epsv = 1;
279 #endif
280 c->info = inf;
281
282 if (!inf->pasv) {
283 int ps;
284 #ifdef SUPPORT_IPV6
285 if (is_ipv6(c->sock1)) {
286 ps = get_pasv_socket_ipv6(c, c->sock1, &c->sock2, port_string);
287 if (ps) {
288 mem_free(d);
289 mem_free(s);
290 return NULL;
291 }
292 } else
293 #endif
294 {
295 unsigned char pc[6];
296 ps = get_pasv_socket(c, c->sock1, &c->sock2, pc);
297 if (ps) {
298 mem_free(d);
299 mem_free(s);
300 return NULL;
301 }
302 if (inf->eprt_epsv)
303 sprintf(cast_char port_string, "|1|%d.%d.%d.%d|%d|", pc[0], pc[1], pc[2], pc[3], (pc[4] << 8) | pc[5]);
304 else
305 sprintf(cast_char port_string, "%d,%d,%d,%d,%d,%d", pc[0], pc[1], pc[2], pc[3], pc[4], pc[5]);
306 }
307 if (strlen(cast_const_char port_string) >= sizeof(port_string))
308 internal("buffer overflow in get_pasv_socket_ipv6: %d > %d", (int)strlen(cast_const_char port_string), (int)sizeof(port_string));
309 }
310 #ifdef HAVE_IPTOS
311 if (ftp_options.set_tos) {
312 int rx;
313 int on = IPTOS_THROUGHPUT;
314 EINTRLOOP(rx, setsockopt(c->sock2, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)));
315 }
316 #endif
317 dd = d;
318 while (*dd == '/') dd++;
319 de = cast_uchar strchr(cast_const_char dd, 0);
320 if (dd == de || de[-1] == '/') {
321 inf->dir = 1;
322 inf->pending_commands = 3;
323 add_to_str(&s, &l, cast_uchar "TYPE A\r\n");
324 add_port_pasv(&s, &l, inf, port_string);
325 if (!inf->we_are_in_root) {
326 add_to_str(&s, &l, cast_uchar "CWD /\r\n");
327 inf->we_are_in_root = 1;
328 inf->pending_commands++;
329 }
330 if (dd != de) {
331 add_to_str(&s, &l, cast_uchar "CWD ");
332 add_bytes_to_str(&s, &l, dd, de - 1 - dd);
333 add_to_str(&s, &l, cast_uchar "\r\n");
334 inf->we_are_in_root = 0;
335 inf->pending_commands++;
336 }
337 add_to_str(&s, &l, cast_uchar "LIST\r\n");
338 c->from = 0;
339 } else {
340 inf->dir = 0;
341 inf->pending_commands = 3;
342 add_to_str(&s, &l, cast_uchar "TYPE I\r\n");
343 add_port_pasv(&s, &l, inf, port_string);
344 if (!inf->we_are_in_root) {
345 add_to_str(&s, &l, cast_uchar "CWD /\r\n");
346 inf->we_are_in_root = 1;
347 inf->pending_commands++;
348 }
349 if (c->from && c->no_cache < NC_IF_MOD) {
350 add_to_str(&s, &l, cast_uchar "REST ");
351 add_num_to_str(&s, &l, c->from);
352 add_to_str(&s, &l, cast_uchar "\r\n");
353 inf->rest_sent = 1;
354 inf->pending_commands++;
355 } else {
356 c->from = 0;
357 }
358 add_to_str(&s, &l, cast_uchar "RETR ");
359 add_bytes_to_str(&s, &l, dd, de - dd);
360 add_to_str(&s, &l, cast_uchar "\r\n");
361 }
362 inf->opc = inf->pending_commands;
363 if ((unsigned)l > MAXINT - sizeof(struct ftp_connection_info) - 1) overalloc();
364 inf2 = mem_realloc(inf, sizeof(struct ftp_connection_info) + l + 1);
365 strcpy(cast_char (inf = inf2)->cmdbuf, cast_const_char s);
366 mem_free(s);
367 c->info = inf;
368 mem_free(d);
369 return inf;
370 }
371
372
ftp_send_retr_req(struct connection * c,int state,int we_are_in_root)373 static void ftp_send_retr_req(struct connection *c, int state, int we_are_in_root)
374 {
375 struct ftp_connection_info *fi;
376 unsigned char *login;
377 int logl = 0;
378 set_timeout(c);
379 login = init_str();
380 if (!c->info && !(fi = add_file_cmd_to_str(c, we_are_in_root))) {
381 mem_free(login);
382 return;
383 } else {
384 fi = c->info;
385 }
386 if (ftp_options.fast_ftp) {
387 a:add_to_str(&login, &logl, fi->cmdbuf);
388 } else {
389 unsigned char *nl = cast_uchar strchr(cast_const_char fi->cmdbuf, '\n');
390 if (!nl) goto a;
391 nl++;
392 add_bytes_to_str(&login, &logl, fi->cmdbuf, nl - fi->cmdbuf);
393 memmove(fi->cmdbuf, nl, strlen(cast_const_char nl) + 1);
394 }
395 write_to_socket(c, c->sock1, login, logl, ftp_retr_1);
396 mem_free(login);
397 setcstate(c, state);
398 }
399
ftp_retr_1(struct connection * c)400 static void ftp_retr_1(struct connection *c)
401 {
402 struct read_buffer *rb;
403 if (!(rb = alloc_read_buffer(c))) return;
404 read_from_socket(c, c->sock1, rb, ftp_retr_file);
405 }
406
ftp_retr_file(struct connection * c,struct read_buffer * rb)407 static void ftp_retr_file(struct connection *c, struct read_buffer *rb)
408 {
409 int g;
410 struct ftp_connection_info *inf = c->info;
411 if (0) {
412 rep:
413 if (!ftp_options.fast_ftp) {
414 ftp_send_retr_req(c, S_GETH, inf->we_are_in_root);
415 return;
416 }
417 }
418 if (inf->pending_commands > 1) {
419 unsigned char pc[6];
420 if (inf->pasv && inf->opc - (inf->pending_commands - 1) == 2) {
421 int i, j;
422 i = 3;
423 if (!inf->eprt_epsv) while (i < rb->len) {
424 if (rb->data[i] >= '0' && rb->data[i] <= '9') {
425 for (j = 0; j < 6; j++) {
426 int n = 0;
427 while (rb->data[i] >= '0' && rb->data[i] <= '9') {
428 n = n * 10 + rb->data[i] - '0';
429 if (n >= 256) goto no_pasv;
430 if (++i >= rb->len) goto no_pasv;
431 }
432 pc[j] = (unsigned char)n;
433 if (j != 5) {
434 if (rb->data[i] != ',') goto xa;
435 if (++i >= rb->len) goto xa;
436 if (rb->data[i] < '0' || rb->data[i] > '9') {
437 xa:
438 if (j != 1) goto no_pasv;
439 pc[4] = pc[0];
440 pc[5] = pc[1];
441 pc[0] = pc[1] = pc[2] = pc[3] = 0;
442 goto pasv_ok;
443 }
444 }
445 }
446 goto pasv_ok;
447 }
448 i++;
449 }
450 no_pasv:
451 i = 3;
452 while (i < rb->len - 5) {
453 if (rb->data[i] == '(' && (rb->data[i + 1] < '0' || rb->data[i + 1] > '9') && rb->data[i + 1] == rb->data[i + 2] && rb->data[i + 2] == rb->data[i + 3]) {
454 unsigned char delim = rb->data[i + 1];
455 int n = 0;
456 i += 4;
457 while (rb->data[i] >= '0' && rb->data[i] <= '9') {
458 n = n * 10 + rb->data[i] - '0';
459 if (n >= 65536) goto no_epsv;
460 if (++i >= rb->len) goto no_epsv;
461 }
462 if (rb->data[i] != delim) goto no_epsv;
463 pc[4] = n >> 8;
464 pc[5] = n & 0xff;
465 pc[0] = pc[1] = pc[2] = pc[3] = 0;
466 goto pasv_ok;
467 }
468 i++;
469 }
470 no_epsv:
471 memset(pc, 0, sizeof pc);
472 pasv_ok:;
473 }
474 g = get_ftp_response(c, rb, 0);
475 if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
476 if (!g) { read_from_socket(c, c->sock1, rb, ftp_retr_file); setcstate(c, S_GETH); return; }
477 inf->pending_commands--;
478 switch (inf->opc - inf->pending_commands) {
479 case 1: /* TYPE */
480 goto rep;
481 case 2: /* PORT */
482 if (g >= 400) { setcstate(c, S_FTP_PORT); abort_connection(c); return; }
483 if (inf->pasv) {
484 if (!pc[4] && !pc[5]) {
485 setcstate(c, S_FTP_ERROR);
486 retry_connection(c);
487 return;
488 }
489 make_connection(c, (pc[4] << 8) + pc[5], &c->sock2, created_data_connection);
490 }
491 goto rep;
492 case 3: /* REST / CWD */
493 case 4:
494 if (g >= 400) {
495 if (!inf->dir && c->from && inf->pending_commands == 1) c->from = 0;
496 else { setcstate(c, S_FTP_NO_FILE); abort_connection(c); return; }
497 }
498 goto rep;
499 }
500 internal("WHAT???");
501 }
502 g = get_ftp_response(c, rb, 2);
503 if (!g) { read_from_socket(c, c->sock1, rb, ftp_retr_file); setcstate(c, S_GETH); return; }
504 if (g >= 100 && g < 200) {
505 unsigned char *d = rb->data;
506 int i, p = 0;
507 for (i = 0; i < rb->len && d[i] != 10; i++) if (d[i] == '(') p = i;
508 if (!p || p == rb->len - 1) goto nol;
509 p++;
510 if (d[p] < '0' || d[p] > '9') goto nol;
511 for (i = p; i < rb->len; i++) if (d[i] < '0' || d[i] > '9') goto quak;
512 goto nol;
513 quak:
514 for (; i < rb->len; i++) if (d[i] != ' ') break;
515 if (i + 4 > rb->len) goto nol;
516 if (casecmp(&d[i], cast_uchar "byte", 4)) goto nol;
517 /* when resuming file transfer, some servers return total size,
518 others return the number of remaining bytes. So it is not
519 reliable to guess file size in this case */
520 if (c->from) goto nol;
521 {
522 my_strtoll_t est = my_strtoll(&d[p], NULL);
523 if (est < 0 || (off_t)est < 0 || (off_t)est != est) est = 0;
524 if (est) c->est_length = est + c->from;
525 }
526 nol:;
527 }
528 if (!inf->pasv)
529 set_handlers(c->sock2, (void (*)(void *))got_something_from_data_connection, NULL, NULL, c);
530 /*read_from_socket(c, c->sock1, rb, ftp_got_final_response);*/
531 ftp_got_final_response(c, rb);
532 }
533
ftp_got_final_response(struct connection * c,struct read_buffer * rb)534 static void ftp_got_final_response(struct connection *c, struct read_buffer *rb)
535 {
536 struct ftp_connection_info *inf = c->info;
537 int g = get_ftp_response(c, rb, 0);
538 if (g == -1) { setcstate(c, S_FTP_ERROR); abort_connection(c); return; }
539 if (!g) { read_from_socket(c, c->sock1, rb, ftp_got_final_response); if (c->state != S_TRANS) setcstate(c, S_GETH); return; }
540 if (g == 425 || g == 450 || g == 500 || g == 501 || g == 550) {
541 if (c->url[strlen(cast_const_char c->url) - 1] == '/') goto skip_redir;
542 if (!c->cache) {
543 if (get_cache_entry(c->url, &c->cache)) {
544 setcstate(c, S_OUT_OF_MEM);
545 abort_connection(c);
546 return;
547 }
548 c->cache->refcount--;
549 }
550 if (c->cache->redirect) mem_free(c->cache->redirect);
551 c->cache->redirect = stracpy(c->url);
552 c->cache->redirect_get = 1;
553 add_to_strn(&c->cache->redirect, cast_uchar "/");
554 c->cache->incomplete = 0;
555 /*setcstate(c, S_FTP_NO_FILE);*/
556 setcstate(c, S__OK);
557 abort_connection(c);
558 return;
559 }
560 skip_redir:
561 if (g >= 400) { setcstate(c, S_FTP_FILE_ERROR); abort_connection(c); return; }
562 if (inf->conn_st == 2) {
563 ftp_end_request(c, S__OK);
564 } else {
565 inf->conn_st = 1;
566 if (c->state != S_TRANS) setcstate(c, S_GETH);
567 }
568 }
569
is_date(unsigned char * data)570 static int is_date(unsigned char *data) /* can touch at most data[-4] --- "n 12 "<--if fed with this --- if you change it, fix the caller */
571 {
572 /* fix for ftp://ftp.su.se/ */
573 if (*data == ' ') data--;
574 if (data[0] >= '0' && data[0] <= '9' && data[-1] >= '0' && data[-1] <= '9') data -= 2;
575 else if (data[0] >= '1' && data[0] <= '9' && data[-1] == ' ') data -= 1 + (data[-2] == ' ');
576 else return 0;
577 if (data[0] == ':') return 1;
578 if (data[0] != ' ') return 0;
579 if ((data[-1] < 'a' || data[-1] > 'z') && (data[-1] < 'A' || data[-1] > 'Z')) return 0;
580 return 1;
581 }
582
583 #define PARSE_MODE_VMS -1
584
ftp_process_dirlist(struct cache_entry * ce,off_t * pos,int * d,unsigned char * bf,int ln,int fin,int root,int * tr)585 static int ftp_process_dirlist(struct cache_entry *ce, off_t *pos, int *d, unsigned char *bf, int ln, int fin, int root, int *tr)
586 {
587 unsigned char *str, *buf;
588 int sl;
589 int ret = 0;
590 int p;
591 int len;
592 int f;
593 int a;
594 int dir;
595 int sp;
596 int ee;
597 again:
598 buf = bf + ret;
599 len = ln - ret;
600 for (p = 0; p < len; p++) if (buf[p] == '\n') goto lb;
601 if (p && (fin || len >= FTP_BUF)) {
602 ret += p;
603 goto pl;
604 }
605 return ret;
606 lb:
607 ret += p + 1;
608 if (p && buf[p - 1] == '\r') p--;
609 pl:
610 str = init_str();
611 sl = 0;
612 /*add_to_str(&str, &sl, cast_uchar " ");*/
613
614 dir = 0;
615 if (!*d || *d == PARSE_MODE_VMS) for (ee = 0; p - ee > 1 && !WHITECHAR(buf[ee]); ee++) {
616 if (buf[ee] == ';' && buf[ee + 1] >= '1' && buf[ee + 1] <= '9') {
617 if (!ee) goto skip_vms;
618 if (ee >= 4 && buf[ee - 4] == '.' && buf[ee - 3] == 'D' && buf[ee - 2] == 'I' && buf[ee - 1] == 'R') {
619 if (ee == 4) goto raw;
620 dir = 1, ee -= 4;
621 } else {
622 /*if (buf[ee + 1] == '1' && (ee + 2 == p || buf[ee + 2] < '0' || buf[ee + 2] > '9')) goto no_version;
623 ee += 2;
624 while (ee < p && buf[ee] >= '0' && buf[ee] < '9') ee++;
625 no_version:;*/
626 }
627 if (*d != PARSE_MODE_VMS && !root) {
628 add_to_str(&str, &sl, cast_uchar "<a href=\"../\">..</a>\n");
629 }
630 sp = 0;
631 *d = PARSE_MODE_VMS;
632 goto put_entry;
633 }
634 }
635 skip_vms:
636
637 if (*d < 0) goto raw;
638
639 f = *d;
640 if (*d && *d < p && WHITECHAR(buf[*d - 1])) {
641 sp = *d;
642 ppp:
643 for (ee = sp; ee <= p - 4; ee++)
644 if (!memcmp(buf + ee, cast_uchar " -> ", 4)) goto syml;
645 ee = p;
646 syml:
647 if (!f && !root) {
648 if ((ee - sp != 1 || buf[sp] != '.') &&
649 (ee - sp != 2 || buf[sp] != '.' || buf[sp + 1] != '.')) {
650 int i;
651 for (i = 0; i < sp; i++) add_chr_to_str(&str, &sl, ' ');
652 add_to_str(&str, &sl, cast_uchar "<a href=\"../\">..</a>\n");
653 }
654 }
655 dir = buf[0] == 'd';
656 if (!dir) {
657 unsigned char *p = memacpy(buf, sp);
658 if (strstr(cast_const_char p, "<DIR>")) dir = 1;
659 mem_free(p);
660 };
661 put_entry:
662 add_conv_str(&str, &sl, buf, sp, 0);
663 add_to_str(&str, &sl, cast_uchar "<a href=\"./");
664 add_conv_str(&str, &sl, buf + sp, ee - sp, 1);
665 if (dir) add_chr_to_str(&str, &sl, '/');
666 add_to_str(&str, &sl, cast_uchar "\">");
667 add_conv_str(&str, &sl, buf + sp, ee - sp, 0);
668 add_to_str(&str, &sl, cast_uchar "</a>");
669 add_conv_str(&str, &sl, buf + ee, p - ee, 0);
670 } else {
671 int pp, ppos;
672 int bp, bn;
673 if (p > 5 && !casecmp(buf, cast_uchar "total", 5)) goto raw;
674 if (p > 10 && !memcmp(buf, cast_uchar "Directory ", 10)) goto raw;
675 for (pp = p - 1; pp >= 0; pp--) if (!WHITECHAR(buf[pp])) break;
676 if (pp < 0) goto raw;
677 if (pp < p - 1) pp++;
678 ppos = -1;
679 for (; pp >= 10; pp--) if (WHITECHAR(buf[pp])) {
680 if (is_date(&buf[pp - 6]) &&
681 buf[pp - 5] == ' ' &&
682 ((buf[pp - 4] == '2' && buf[pp - 3] == '0') ||
683 (buf[pp - 4] == '1' && buf[pp - 3] == '9')) &&
684 buf[pp - 2] >= '0' && buf[pp - 2] <= '9' &&
685 buf[pp - 1] >= '0' && buf[pp - 1] <= '9') {
686 if (pp < p - 2 && buf[pp + 1] == ' ' && buf[pp + 2] != ' ') ppos = pp + 1;
687 else ppos = pp;
688 }
689 if (buf[pp - 6] == ' ' &&
690 ((buf[pp - 5] >= '0' && buf[pp - 5] <= '2') || buf[pp - 5] == ' ') &&
691 buf[pp - 4] >= '0' && buf[pp - 4] <= '9' &&
692 buf[pp - 3] == ':' &&
693 buf[pp - 2] >= '0' && buf[pp - 2] <= '5' &&
694 buf[pp - 1] >= '0' && buf[pp - 1] <= '9') {
695 ppos = pp;
696 if (p - pp > 2 && buf[pp + 1] == ' ' && buf[pp + 2] != ' ')
697 ppos++;
698 }
699 }
700 if (ppos != -1) {
701 pp = ppos;
702 goto done;
703 }
704
705 for (pp = 0; p - pp >= 5; pp++)
706 if (!casecmp(&buf[pp], cast_uchar "<DIR>", 5)) {
707 pp += 4;
708 while (p - pp > 1 && WHITECHAR(buf[pp + 1])) pp++;
709 if (p - pp > 1) goto done;
710 }
711
712 bn = -1;
713 bp = 0; /* warning, go away */
714 for (pp = 0; pp < p; ) {
715 if (buf[pp] >= '0' && buf[pp] <= '9') {
716 int i;
717 for (i = pp; i < p; i++)
718 if (buf[i] < '0' || buf[i] > '9') break;
719 if (i < p && WHITECHAR(buf[i])) {
720 if (i - pp > bn) {
721 bn = i - pp;
722 bp = pp;
723 }
724 }
725 pp = i;
726 }
727 while (pp < p && !WHITECHAR(buf[pp])) pp++;
728 while (pp < p && WHITECHAR(buf[pp])) pp++;
729 }
730 if (bn >= 0) {
731 pp = bp + bn;
732 while (p - pp > 1 && WHITECHAR(buf[pp + 1])) pp++;
733 if (p - pp > 1) goto done;
734 }
735
736 for (pp = p - 1; pp >= 0; pp--) if (!WHITECHAR(buf[pp])) break;
737 if (pp < 0) goto raw;
738 for (; pp >= 0; pp--) if (WHITECHAR(buf[pp]) && (pp < 3 || memcmp(buf + pp - 3, cast_uchar " -> ", 4)) && (pp > p - 4 || memcmp(buf + pp, cast_uchar " -> ", 4))) break;
739 done:
740 sp = *d = pp + 1;
741 goto ppp;
742 raw:
743 add_conv_str(&str, &sl, buf, p, 0);
744 }
745 add_chr_to_str(&str, &sl, '\n');
746 a = add_fragment(ce, *pos, str, sl);
747 if (a < 0) return a;
748 if (a == 1) *tr = 0;
749 *pos += sl;
750 mem_free(str);
751 goto again;
752 }
753
created_data_connection(struct connection * c)754 static void created_data_connection(struct connection *c)
755 {
756 struct ftp_connection_info *inf = c->info;
757 #ifdef HAVE_IPTOS
758 if (ftp_options.set_tos) {
759 int rx;
760 int on = IPTOS_THROUGHPUT;
761 EINTRLOOP(rx, setsockopt(c->sock2, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)));
762 }
763 #endif
764 inf->d = 1;
765 set_handlers(c->sock2, (void (*)(void *))got_something_from_data_connection, NULL, NULL, c);
766 }
767
got_something_from_data_connection(struct connection * c)768 static void got_something_from_data_connection(struct connection *c)
769 {
770 struct ftp_connection_info *inf = c->info;
771 int l;
772 int m;
773 int rs;
774 set_timeout(c);
775 if (!inf->d) {
776 int ns;
777 inf->d = 1;
778 set_handlers(c->sock2, NULL, NULL, NULL, NULL);
779 EINTRLOOP(ns, accept(c->sock2, NULL, NULL));
780 if (ns == -1) goto e;
781 set_nonblock(ns);
782 EINTRLOOP(rs, close(c->sock2));
783 c->sock2 = ns;
784 set_handlers(ns, (void (*)(void *))got_something_from_data_connection, NULL, NULL, c);
785 return;
786 }
787 if (!c->cache) {
788 if (get_cache_entry(c->url, &c->cache)) {
789 setcstate(c, S_OUT_OF_MEM);
790 abort_connection(c);
791 return;
792 }
793 c->cache->refcount--;
794 }
795 if (inf->dir && !c->from) {
796 unsigned char *ud;
797 unsigned char *s0;
798 int s0l;
799 int err = 0;
800 static unsigned char ftp_head[] = "<html><head><title>/";
801 static unsigned char ftp_head2[] = "</title></head><body><h2>Directory /";
802 static unsigned char ftp_head3[] = "</h2><pre>";
803 #define A(s) \
804 do { \
805 m = add_fragment(c->cache, c->from, s, strlen(cast_const_char s)); \
806 if (m < 0 && !err) err = m; \
807 c->from += strlen(cast_const_char s); \
808 } while (0)
809 A(ftp_head);
810 ud = stracpy(get_url_data(c->url));
811 if (strchr(cast_const_char ud, POST_CHAR)) *strchr(cast_const_char ud, POST_CHAR) = 0;
812 s0 = init_str();
813 s0l = 0;
814 add_conv_str(&s0, &s0l, ud, (int)strlen(cast_const_char ud), -1);
815 mem_free(ud);
816 A(s0);
817 A(ftp_head2);
818 A(s0);
819 A(ftp_head3);
820 mem_free(s0);
821 if (!c->cache->head) c->cache->head = stracpy(cast_uchar "\r\n");
822 add_to_strn(&c->cache->head, cast_uchar "Content-Type: text/html\r\n");
823 if (err) {
824 setcstate(c, err);
825 abort_connection(c);
826 return;
827 }
828 #undef A
829 }
830 EINTRLOOP(l, (int)read(c->sock2, inf->ftp_buffer + inf->buf_pos, FTP_BUF - inf->buf_pos));
831 if (l == -1) {
832 e:
833 if (inf->conn_st != 1 && !inf->dir && !c->from) {
834 set_handlers(c->sock2, NULL, NULL, NULL, NULL);
835 close_socket(&c->sock2);
836 inf->conn_st = 2;
837 return;
838 }
839 setcstate(c, get_error_from_errno(errno));
840 retry_connection(c);
841 return;
842 }
843 if (l > 0) {
844 if (!inf->dir) {
845 if ((off_t)(0UL + c->from + l) < 0) {
846 setcstate(c, S_LARGE_FILE);
847 abort_connection(c);
848 return;
849 }
850 c->received += l;
851 m = add_fragment(c->cache, c->from, inf->ftp_buffer, l);
852 if (m < 0) {
853 setcstate(c, m);
854 abort_connection(c);
855 return;
856 }
857 if (m == 1) c->tries = 0;
858 c->from += l;
859 } else {
860 c->received += l;
861 m = ftp_process_dirlist(c->cache, &c->from, &inf->dpos, inf->ftp_buffer, l + inf->buf_pos, 0, inf->we_are_in_root, &c->tries);
862 if (m < 0) {
863 setcstate(c, m);
864 abort_connection(c);
865 return;
866 }
867 memmove(inf->ftp_buffer, inf->ftp_buffer + m, inf->buf_pos + l - m);
868 inf->buf_pos += l - m;
869 }
870 setcstate(c, S_TRANS);
871 return;
872 }
873 m = ftp_process_dirlist(c->cache, &c->from, &inf->dpos, inf->ftp_buffer, inf->buf_pos, 1, inf->we_are_in_root, &c->tries);
874 if (m < 0) {
875 setcstate(c, m);
876 abort_connection(c);
877 return;
878 }
879 set_handlers(c->sock2, NULL, NULL, NULL, NULL);
880 close_socket(&c->sock2);
881 if (inf->conn_st == 1) {
882 ftp_end_request(c, S__OK);
883 } else {
884 inf->conn_st = 2;
885 }
886 }
887
ftp_end_request(struct connection * c,int state)888 static void ftp_end_request(struct connection *c, int state)
889 {
890 struct ftp_connection_info *inf = c->info;
891 if (state == S__OK) {
892 if (c->cache) {
893 truncate_entry(c->cache, c->from, 1);
894 c->cache->incomplete = 0;
895 }
896 }
897 setcstate(c, state);
898 add_keepalive_socket(c, FTP_KEEPALIVE_TIMEOUT, inf->we_are_in_root);
899 }
900
901