"Fossies" - the Fresh Open Source Software Archive

Member "links-1.03/ftp.c" (16 Oct 2011, 22105 Bytes) of archive /linux/www/links-1.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ftp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8_vs_1.03.

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