diff options
Diffstat (limited to 'sched.c')
-rw-r--r-- | sched.c | 1026 |
1 files changed, 1026 insertions, 0 deletions
@@ -0,0 +1,1026 @@ +/* sched.c + * Links internal scheduler + * (c) 2002 Mikulas Patocka + * This file is a part of the Links program, released under GPL. + */ + +#include "links.h" + +static tcount connection_count = 0; + +static int active_connections = 0; + +struct list_head queue = {&queue, &queue}; + +struct h_conn { + struct h_conn *next; + struct h_conn *prev; + unsigned char *host; + int conn; +}; + +static struct list_head h_conns = {&h_conns, &h_conns}; + +struct list_head keepalive_connections = {&keepalive_connections, &keepalive_connections}; + +/* prototypes */ +static void send_connection_info(struct connection *c); +static void check_keepalive_connections(void); + +unsigned long connect_info(int type) +{ + int i = 0; + struct connection *ce; + struct k_conn *cee; + switch (type) { + case CI_FILES: + foreach(ce, queue) i++; + return i; + case CI_CONNECTING: + foreach(ce, queue) i += ce->state > S_WAIT && ce->state < S_TRANS; + return i; + case CI_TRANSFER: + foreach(ce, queue) i += ce->state == S_TRANS; + return i; + case CI_KEEP: + check_keepalive_connections(); + foreach(cee, keepalive_connections) i++; + return i; + case CI_LIST: + return (long) &queue; + default: + internal("connect_info: bad request"); + } + return 0; +} + +static int connection_disappeared(struct connection *c, tcount count) +{ + struct connection *d; + foreach(d, queue) if (c == d && count == d->count) return 0; + return 1; +} + +static struct h_conn *is_host_on_list(struct connection *c) +{ + unsigned char *ho; + struct h_conn *h; + if (!(ho = get_host_name(c->url))) return NULL; + foreach(h, h_conns) if (!strcmp(h->host, ho)) { + mem_free(ho); + return h; + } + mem_free(ho); + return NULL; +} + +static int st_r = 0; + +static void stat_timer(struct connection *c) +{ + struct remaining_info *r = &c->prg; + ttime a = (uttime)get_time() - (uttime)r->last_time; + if (getpri(c) == PRI_CANCEL && (c->est_length > (longlong)memory_cache_size * MAX_CACHED_OBJECT || c->from > (longlong)memory_cache_size * MAX_CACHED_OBJECT)) register_bottom_half(check_queue, NULL); + if (c->state > S_WAIT) { + r->loaded = c->received; + if ((r->size = c->est_length) < (r->pos = c->from) && r->size != -1) + r->size = c->from; + r->dis_b += (uttime)a; + while (r->dis_b >= SPD_DISP_TIME * CURRENT_SPD_SEC) { + r->cur_loaded -= r->data_in_secs[0]; + memmove(r->data_in_secs, r->data_in_secs + 1, sizeof(int) * (CURRENT_SPD_SEC - 1)); + r->data_in_secs[CURRENT_SPD_SEC - 1] = 0; + r->dis_b -= (uttime)SPD_DISP_TIME; + } + r->data_in_secs[CURRENT_SPD_SEC - 1] += r->loaded - r->last_loaded; + r->cur_loaded += (uttime)r->loaded - (uttime)r->last_loaded; + r->last_loaded = (uttime)r->loaded; + r->elapsed += (uttime)a; + } + r->last_time += (uttime)a; + r->timer = install_timer(SPD_DISP_TIME, (void (*)(void *))stat_timer, c); + if (!st_r) send_connection_info(c); +} + +void setcstate(struct connection *c, int state) +{ + struct status *stat; + if (c->state < 0 && state >= 0) c->prev_error = c->state; + if ((c->state = state) == S_TRANS) { + struct remaining_info *r = &c->prg; + if (r->timer == -1) { + tcount count = c->count; + if (!r->valid) { + memset(r, 0, sizeof(struct remaining_info)); + r->valid = 1; + } + r->last_time = get_time(); + r->last_loaded = r->loaded; + st_r = 1; + stat_timer(c); + st_r = 0; + if (connection_disappeared(c, count)) return; + } + } else { + struct remaining_info *r = &c->prg; + if (r->timer != -1) kill_timer(r->timer), r->timer = -1; + } + foreach(stat, c->statuss) { + stat->state = state; + stat->prev_error = c->prev_error; + } + if (state >= 0) send_connection_info(c); +} + +static struct k_conn *is_host_on_keepalive_list(struct connection *c) +{ + unsigned char *ho; + int po; + void (*ph)(struct connection *); + struct k_conn *h; + if ((po = get_port(c->url)) == -1) return NULL; + if (!(ph = get_protocol_handle(c->url))) return NULL; + if (!(ho = get_host_and_pass(c->url))) return NULL; + foreach(h, keepalive_connections) + if (h->protocol == ph && h->port == po && !strcmp(h->host, ho)) { + mem_free(ho); + return h; + } + mem_free(ho); + return NULL; +} + +int get_keepalive_socket(struct connection *c) +{ + struct k_conn *k; + int cc; + if (c->tries > 0 || c->unrestartable) return -1; + if (!(k = is_host_on_keepalive_list(c))) return -1; + cc = k->conn; + del_from_list(k); + mem_free(k->host); + mem_free(k); + c->sock1 = cc; + if (max_tries == 1) c->tries = -1; + return 0; +} + +void abort_all_keepalive_connections(void) +{ + struct k_conn *k; + int rs; + foreach(k, keepalive_connections) { + mem_free(k->host); + EINTRLOOP(rs, close(k->conn)); + } + free_list(keepalive_connections); + check_keepalive_connections(); +} + +static void free_connection_data(struct connection *c) +{ + struct h_conn *h; + int rs; + if (c->sock1 != -1) set_handlers(c->sock1, NULL, NULL, NULL, NULL); + if (c->sock2 != -1) set_handlers(c->sock2, NULL, NULL, NULL, NULL); + close_socket(&c->sock2); + if (c->pid) { + EINTRLOOP(rs, kill(c->pid, SIGINT)); + EINTRLOOP(rs, kill(c->pid, SIGTERM)); + EINTRLOOP(rs, kill(c->pid, SIGKILL)); + c->pid = 0; + } + if (!c->running) { + internal("connection already suspended"); + } + c->running = 0; + if (c->dnsquery) kill_dns_request(&c->dnsquery); + if (c->buffer) { + mem_free(c->buffer); + c->buffer = NULL; + } + if (c->newconn) { + mem_free(c->newconn); + c->newconn = NULL; + } + if (c->info) { + mem_free(c->info); + c->info = NULL; + } + if (c->timer != -1) kill_timer(c->timer), c->timer = -1; + if (--active_connections < 0) { + internal("active connections underflow"); + active_connections = 0; + } + if (c->state != S_WAIT) { + if ((h = is_host_on_list(c))) { + if (!--h->conn) { + del_from_list(h); + mem_free(h->host); + mem_free(h); + } + } else internal("suspending connection that is not on the list (state %d)", c->state); + } +} + +static void send_connection_info(struct connection *c) +{ + int st = c->state; + tcount count = c->count; + struct status *stat = c->statuss.next; + while ((void *)stat != &c->statuss) { + stat->ce = c->cache; + stat = stat->next; + if (stat->prev->end) stat->prev->end(stat->prev, stat->prev->data); + if (st >= 0 && connection_disappeared(c, count)) return; + } +} + +static void del_connection(struct connection *c) +{ + struct cache_entry *ce = c->cache; + if (ce) ce->refcount++; + del_from_list(c); + send_connection_info(c); + if (ce) ce->refcount--; + if (c->detached) { + if (ce && !ce->url[0] && !is_entry_used(ce) && !ce->refcount) + delete_cache_entry(ce); + } else { + if (ce) + trim_cache_entry(ce); + } + mem_free(c->url); + if (c->prev_url) mem_free(c->prev_url); + mem_free(c); +} + +#ifdef DEBUG +static void check_queue_bugs(void); +#endif + +void add_keepalive_socket(struct connection *c, ttime timeout) +{ + struct k_conn *k; + int rs; + free_connection_data(c); + if (c->sock1 == -1) { + internal("keepalive connection not connected"); + goto del; + } + k = mem_alloc(sizeof(struct k_conn)); + if ((k->port = get_port(c->url)) == -1 || !(k->protocol = get_protocol_handle(c->url)) || !(k->host = get_host_and_pass(c->url))) { + mem_free(k); + del_connection(c); + goto close; + } + k->conn = c->sock1; + k->timeout = timeout; + k->add_time = get_time(); + add_to_list(keepalive_connections, k); + del: + del_connection(c); +#ifdef DEBUG + check_queue_bugs(); +#endif + register_bottom_half(check_queue, NULL); + return; + close: + EINTRLOOP(rs, close(c->sock1)); +#ifdef DEBUG + check_queue_bugs(); +#endif + register_bottom_half(check_queue, NULL); +} + +static void del_keepalive_socket(struct k_conn *kc) +{ + int rs; + del_from_list(kc); + EINTRLOOP(rs, close(kc->conn)); + mem_free(kc->host); + mem_free(kc); +} + +static int keepalive_timeout = -1; + +static void keepalive_timer(void *x) +{ + keepalive_timeout = -1; + check_keepalive_connections(); +} + +static void check_keepalive_connections(void) +{ + struct k_conn *kc; + ttime ct = get_time(); + int p = 0; + if (keepalive_timeout != -1) kill_timer(keepalive_timeout), keepalive_timeout = -1; + foreach(kc, keepalive_connections) if (can_read(kc->conn) || (uttime)ct - (uttime)kc->add_time > (uttime)kc->timeout) { + kc = kc->prev; + del_keepalive_socket(kc->next); + } else p++; + for (; p > MAX_KEEPALIVE_CONNECTIONS; p--) + if (!list_empty(keepalive_connections)) + del_keepalive_socket(keepalive_connections.prev); + else internal("keepalive list empty"); + if (!list_empty(keepalive_connections)) keepalive_timeout = install_timer(KEEPALIVE_CHECK_TIME, keepalive_timer, NULL); +} + +static void add_to_queue(struct connection *c) +{ + struct connection *cc; + int pri = getpri(c); + foreach(cc, queue) if (getpri(cc) > pri) break; + add_at_pos(cc->prev, c); +} + +static void sort_queue(void) +{ + struct connection *c, *n; + int swp; + do { + swp = 0; + foreach(c, queue) if ((void *)c->next != &queue) { + if (getpri(c->next) < getpri(c)) { + n = c->next; + del_from_list(c); + add_at_pos(n, c); + swp = 1; + } + } + } while (swp); +} + +static void interrupt_connection(struct connection *c) +{ +#ifdef HAVE_SSL + if (c->ssl == (void *)-1) c->ssl = NULL; + if (c->ssl) { + SSL_free(c->ssl); + c->ssl = NULL; + } +#endif + if (c->sock1 != -1) set_handlers(c->sock1, NULL, NULL, NULL, NULL); + close_socket(&c->sock1); + free_connection_data(c); +} + +static void suspend_connection(struct connection *c) +{ + interrupt_connection(c); + setcstate(c, S_WAIT); +} + +static int try_to_suspend_connection(struct connection *c, unsigned char *ho) +{ + int pri = getpri(c); + struct connection *d; + foreachback(d, queue) { + if (getpri(d) <= pri) return -1; + if (d->state == S_WAIT) continue; + if (d->unrestartable == 2 && getpri(d) < PRI_CANCEL) continue; + if (ho) { + unsigned char *h; + if (!(h = get_host_name(d->url))) continue; + if (strcmp(h, ho)) { + mem_free(h); + continue; + } + mem_free(h); + } + suspend_connection(d); + return 0; + } + return -1; +} + +static void run_connection(struct connection *c) +{ + struct h_conn *hc; + void (*func)(struct connection *); + if (c->running) { + internal("connection already running"); + return; + } + + if (is_url_blocked(c->url)) { + setcstate(c, S_BLOCKED_URL); + del_connection(c); + return; + } + + safe_strncpy(c->socks_proxy, proxies.socks_proxy, sizeof c->socks_proxy); + + if (proxies.only_proxies && casecmp(c->url, "proxy://", 8) && (!*c->socks_proxy || url_bypasses_socks(c->url))) { + setcstate(c, S_NO_PROXY); + del_connection(c); + return; + } + + if (!(func = get_protocol_handle(c->url))) { + setcstate(c, S_BAD_URL); + del_connection(c); + return; + } + if (!(hc = is_host_on_list(c))) { + hc = mem_alloc(sizeof(struct h_conn)); + if (!(hc->host = get_host_name(c->url))) { + setcstate(c, S_BAD_URL); + del_connection(c); + mem_free(hc); + return; + } + hc->conn = 0; + add_to_list(h_conns, hc); + } + hc->conn++; + active_connections++; + c->running = 1; + func(c); +} + +static int is_connection_seekable(struct connection *c) +{ + unsigned char *protocol = get_protocol_name(c->url); + if (!strcasecmp(protocol, "http") || !strcasecmp(protocol, "https") || + !strcasecmp(protocol, "proxy")) { + unsigned char *d; + mem_free(protocol); + if (!c->cache || !c->cache->head) + return 1; + d = parse_http_header(c->cache->head, "Accept-Ranges", NULL); + if (d) { + mem_free(d); + return 1; + } + return 0; + } + if (!strcasecmp(protocol, "ftp")) { + mem_free(protocol); + return 1; + } + mem_free(protocol); + return 0; +} + +int is_connection_restartable(struct connection *c) +{ + return !(c->unrestartable >= 2 || (c->tries + 1 >= (max_tries ? max_tries : 1000))); +} + +int is_last_try(struct connection *c) +{ + int is_restartable; + c->tries++; + is_restartable = is_connection_restartable(c) && c->tries < 10; + c->tries--; + return !is_restartable; +} + +void retry_connection(struct connection *c) +{ + interrupt_connection(c); + if (!is_connection_restartable(c)) { + del_connection(c); +#ifdef DEBUG + check_queue_bugs(); +#endif + register_bottom_half(check_queue, NULL); + } else { + c->tries++; + c->prev_error = c->state; + run_connection(c); + } +} + +void abort_connection(struct connection *c) +{ + if (c->running) interrupt_connection(c); + del_connection(c); +#ifdef DEBUG + check_queue_bugs(); +#endif + register_bottom_half(check_queue, NULL); +} + +static int try_connection(struct connection *c) +{ + struct h_conn *hc = NULL; + if ((hc = is_host_on_list(c))) { + if (hc->conn >= max_connections_to_host) { + if (try_to_suspend_connection(c, hc->host)) return 0; + else return -1; + } + } + if (active_connections >= max_connections) { + if (try_to_suspend_connection(c, NULL)) return 0; + else return -1; + } + run_connection(c); + return 1; +} + +#ifdef DEBUG +static void check_queue_bugs(void) +{ + struct connection *d; + int p = 0, ps = 0; + int cc; + again: + cc = 0; + foreach(d, queue) { + int q = getpri(d); + cc += d->running; + if (q < p) { + if (!ps) { + internal("queue is not sorted"); + sort_queue(); + ps = 1; + goto again; + } else { + internal("queue is not sorted even after sort_queue!"); + break; + } + } else p = q; + if (d->state < 0) { + internal("interrupted connection on queue (conn %s, state %d)", d->url, d->state); + d = d->prev; + abort_connection(d->next); + } + } + if (cc != active_connections) { + internal("bad number of active connections (counted %d, stored %d)", cc, active_connections); + active_connections = cc; + } +} +#endif + +void check_queue(void *dummy) +{ + struct connection *c; + again: + c = queue.next; +#ifdef DEBUG + check_queue_bugs(); +#endif + check_keepalive_connections(); + while (c != (struct connection *)(void *)&queue) { + struct connection *d; + int cp = getpri(c); + for (d = c; d != (struct connection *)(void *)&queue && getpri(d) == cp;) { + struct connection *dd = d; d = d->next; + if (!dd->state) if (is_host_on_keepalive_list(dd)) { + if (try_connection(dd)) goto again; + } + } + for (d = c; d != (struct connection *)(void *)&queue && getpri(d) == cp;) { + struct connection *dd = d; d = d->next; + if (!dd->state) { + if (try_connection(dd)) goto again; + } + } + c = d; + } + again2: + foreachback(c, queue) { + if (getpri(c) < PRI_CANCEL) break; + if (c->state == S_WAIT) { + setcstate(c, S_INTERRUPTED); + del_connection(c); + goto again2; + } else if (c->est_length > (longlong)memory_cache_size * MAX_CACHED_OBJECT || c->from > (longlong)memory_cache_size * MAX_CACHED_OBJECT) { + setcstate(c, S_INTERRUPTED); + abort_connection(c); + goto again2; + } + } +#ifdef DEBUG + check_queue_bugs(); +#endif +} + +unsigned char *get_proxy(unsigned char *url) +{ + size_t l = strlen(url); + unsigned char *proxy = NULL; + unsigned char *u; + if (*proxies.http_proxy && l >= 7 && !casecmp(url, "http://", 7)) proxy = proxies.http_proxy; + if (*proxies.ftp_proxy && l >= 6 && !casecmp(url, "ftp://", 6)) proxy = proxies.ftp_proxy; + u = mem_alloc(l + 1 + (proxy ? strlen(proxy) + 9 : 0)); + if (proxy) strcpy(u, "proxy://"), strcat(u, proxy), strcat(u, "/"); + else *u = 0; + strcat(u, url); + return u; +} + +/* prev_url is a pointer to previous url or NULL */ +/* prev_url will NOT be deallocated */ +void load_url(unsigned char *url, unsigned char *prev_url, struct status *stat, int pri, int no_cache, int no_compress, off_t position) +{ + struct cache_entry *e = NULL; + struct connection *c; + unsigned char *u; + int must_detach = 0; + if (stat) stat->c = NULL, stat->ce = NULL, stat->pri = pri; +#ifdef DEBUG + foreach(c, queue) { + struct status *st; + foreach (st, c->statuss) { + if (st == stat) { + internal("status already assigned to '%s'", c->url); + stat->state = S_INTERNAL; + if (stat->end) stat->end(stat, stat->data); + return; + } + } + } +#endif + if (stat) stat->state = S_OUT_OF_MEM, stat->prev_error = 0; + if (no_cache <= NC_CACHE && !find_in_cache(url, &e)) { + if (e->incomplete) { + e->refcount--; + goto skip_cache; + } + if (!aggressive_cache && no_cache > NC_ALWAYS_CACHE) { + if (e->expire_time && e->expire_time < time(NULL)) { + if (no_cache < NC_IF_MOD) no_cache = NC_IF_MOD; + e->refcount--; + goto skip_cache; + } + } + if (no_compress) { + unsigned char *enc; + enc = parse_http_header(e->head, "Content-Encoding", NULL); + if (enc) { + mem_free(enc); + e->refcount--; + must_detach = 1; + goto skip_cache; + } + } + if (stat) { + stat->ce = e; + stat->state = S__OK; + if (stat->end) stat->end(stat, stat->data); + } + e->refcount--; + return; + } + skip_cache: + if (!casecmp(url, "proxy://", 8)) { + if (stat) { + stat->state = S_BAD_URL; + if (stat->end) stat->end(stat, stat->data); + } + return; + } + u = get_proxy(url); + foreach(c, queue) if (!c->detached && !strcmp(c->url, u)) { + if (c->from < position) continue; + if (no_compress && !c->no_compress) { + unsigned char *enc; + if ((c->state >= S_WAIT && c->state < S_TRANS) || !c->cache) { + must_detach = 1; + break; + } + enc = parse_http_header(c->cache->head, "Content-Encoding", NULL); + if (enc) { + mem_free(enc); + must_detach = 1; + break; + } + } + mem_free(u); + if (getpri(c) > pri) { + del_from_list(c); + c->pri[pri]++; + add_to_queue(c); + register_bottom_half(check_queue, NULL); + } else c->pri[pri]++; + if (stat) { + stat->prg = &c->prg; + stat->c = c; + stat->ce = c->cache; + add_to_list(c->statuss, stat); + setcstate(c, c->state); + } +#ifdef DEBUG + check_queue_bugs(); +#endif + return; + } + c = mem_calloc(sizeof(struct connection)); + c->count = connection_count++; + c->url = u; + c->prev_url = stracpy(prev_url); + c->running = 0; + c->prev_error = 0; + if (position || must_detach) { + c->from = position; + } else if (no_cache >= NC_IF_MOD || !e) { + c->from = 0; + } else { + struct fragment *frag; + c->from = 0; + foreach(frag, e->frag) { + if (frag->offset != c->from) + break; + c->from += frag->length; + } + + } + memset(c->pri, 0, sizeof c->pri); + c->pri[pri] = 1; + c->no_cache = no_cache; + c->sock1 = c->sock2 = -1; + c->dnsquery = NULL; + c->info = NULL; + c->buffer = NULL; + c->newconn = NULL; + c->cache = NULL; + c->tries = 0; + init_list(c->statuss); + c->est_length = -1; + c->unrestartable = 0; +#ifdef HAVE_ANY_COMPRESSION + c->no_compress = http_options.no_compression || no_compress; +#else + c->no_compress = 1; +#endif + c->prg.timer = -1; + c->timer = -1; + if (position || must_detach) { + if (new_cache_entry(c->url, &c->cache)) { + mem_free(c->url); + if (c->prev_url) mem_free(c->prev_url); + mem_free(c); + if (stat) { + stat->state = S_OUT_OF_MEM; + if (stat->end) stat->end(stat, stat->data); + } + return; + } + c->cache->refcount--; + detach_cache_entry(c->cache); + c->detached = 2; + } + if (stat) { + stat->prg = &c->prg; + stat->c = c; + stat->ce = NULL; + add_to_list(c->statuss, stat); + } + add_to_queue(c); + setcstate(c, S_WAIT); +#ifdef DEBUG + check_queue_bugs(); +#endif + register_bottom_half(check_queue, NULL); +} + +void change_connection(struct status *oldstat, struct status *newstat, int newpri) +{ /* !!! FIXME: one object in more connections */ + struct connection *c; + int oldpri; + if (!oldstat) { + internal("change_connection: oldstat == NULL"); + return; + } + oldpri = oldstat->pri; + if (oldstat->state < 0) { + if (newstat) { + struct cache_entry *ce = oldstat->ce; + if (ce) ce->refcount++; + newstat->ce = oldstat->ce; + newstat->state = oldstat->state; + newstat->prev_error = oldstat->prev_error; + if (newstat->end) newstat->end(newstat, newstat->data); + if (ce) ce->refcount--; + } + return; + } +#ifdef DEBUG + check_queue_bugs(); +#endif + c = oldstat->c; + if (--c->pri[oldpri] < 0) { + internal("priority counter underflow"); + c->pri[oldpri] = 0; + } + c->pri[newpri]++; + del_from_list(oldstat); + oldstat->state = S_INTERRUPTED; + if (newstat) { + newstat->prg = &c->prg; + add_to_list(c->statuss, newstat); + newstat->state = c->state; + newstat->prev_error = c->prev_error; + newstat->pri = newpri; + newstat->c = c; + newstat->ce = c->cache; + } + if (c->detached && !newstat) { + setcstate(c, S_INTERRUPTED); + abort_connection(c); + } + sort_queue(); +#ifdef DEBUG + check_queue_bugs(); +#endif + register_bottom_half(check_queue, NULL); +} + +void detach_connection(struct status *stat, off_t pos) +{ + struct connection *c; + int i; + off_t l; + if (stat->state < 0) return; + c = stat->c; + if (!c->cache) return; + if (c->detached) goto detach_done; + if (c->est_length == -1) l = c->from; + else l = c->est_length; + if (l < (longlong)memory_cache_size * MAX_CACHED_OBJECT && !(pos > c->from && is_connection_seekable(c))) return; + l = 0; + for (i = 0; i < PRI_CANCEL; i++) l += c->pri[i]; + if (!l) internal("detaching free connection"); + if (l != 1 || c->cache->refcount) return; + shrink_memory(SH_CHECK_QUOTA); + detach_cache_entry(c->cache); + c->detached = 1; + detach_done: + free_entry_to(c->cache, pos); + + if (c->detached < 2 && pos > c->from && is_connection_seekable(c)) { + int running = c->running; + if (running) interrupt_connection(c); + c->from = pos; + if (running) run_connection(c); + c->detached = 2; + } +} + +static void connection_timeout(struct connection *c) +{ + c->timer = -1; + setcstate(c, S_TIMEOUT); + if (c->dnsquery) abort_connection(c); + else retry_connection(c); +} + +static void connection_timeout_1(struct connection *c) +{ + c->timer = install_timer((c->unrestartable ? unrestartable_receive_timeout : receive_timeout) * 500, (void (*)(void *))connection_timeout, c); +} + +void set_timeout(struct connection *c) +{ + if (c->timer != -1) kill_timer(c->timer); + c->timer = install_timer((c->unrestartable ? unrestartable_receive_timeout : receive_timeout) * 500, (void (*)(void *))connection_timeout_1, c); +} + +#if 0 +static void reset_timeout(struct connection *c) +{ + if (c->timer != -1) kill_timer(c->timer), c->timer = -1; +} +#endif + +void abort_all_connections(void) +{ + while(queue.next != &queue) { + setcstate(queue.next, S_INTERRUPTED); + abort_connection(queue.next); + } + abort_all_keepalive_connections(); +} + +void abort_background_connections(void) +{ + int i = 0; + while (1) { + int j; + struct connection *c = (void *)&queue; + for (j = 0; j <= i; j++) if ((c = c->next) == (void *)&queue) goto brk; + if (getpri(c) >= PRI_CANCEL) { + setcstate(c, S_INTERRUPTED); + abort_connection(c); + } else i++; + } + brk: + abort_all_keepalive_connections(); +} + +int is_entry_used(struct cache_entry *e) +{ + struct connection *c; + foreach(c, queue) if (c->cache == e) return 1; + return 0; +} + +struct blacklist_entry { + struct blacklist_entry *next; + struct blacklist_entry *prev; + int flags; + unsigned char host[1]; +}; + +static struct list_head blacklist = { &blacklist, &blacklist }; + +void add_blacklist_entry(unsigned char *host, int flags) +{ + struct blacklist_entry *b; + foreach(b, blacklist) if (!strcasecmp(host, b->host)) { + b->flags |= flags; + return; + } + b = mem_alloc(sizeof(struct blacklist_entry) + strlen(host) + 1); + b->flags = flags; + strcpy(b->host, host); + add_to_list(blacklist, b); +} + +void del_blacklist_entry(unsigned char *host, int flags) +{ + struct blacklist_entry *b; + foreach(b, blacklist) if (!strcasecmp(host, b->host)) { + b->flags &= ~flags; + if (!b->flags) { + del_from_list(b); + mem_free(b); + } + return; + } +} + +int get_blacklist_flags(unsigned char *host) +{ + struct blacklist_entry *b; + foreach(b, blacklist) if (!strcasecmp(host, b->host)) return b->flags; + return 0; +} + +void free_blacklist(void) +{ + free_list(blacklist); +} + +struct s_msg_dsc msg_dsc[] = { + {S_WAIT, TEXT_(T_WAITING_IN_QUEUE)}, + {S_DNS, TEXT_(T_LOOKING_UP_HOST)}, + {S_CONN, TEXT_(T_MAKING_CONNECTION)}, + {S_SOCKS_NEG, TEXT_(T_SOCKS_NEGOTIATION)}, + {S_SSL_NEG, TEXT_(T_SSL_NEGOTIATION)}, + {S_SENT, TEXT_(T_REQUEST_SENT)}, + {S_LOGIN, TEXT_(T_LOGGING_IN)}, + {S_GETH, TEXT_(T_GETTING_HEADERS)}, + {S_PROC, TEXT_(T_SERVER_IS_PROCESSING_REQUEST)}, + {S_TRANS, TEXT_(T_TRANSFERRING)}, + + {S__OK, TEXT_(T_OK)}, + {S_INTERRUPTED, TEXT_(T_INTERRUPTED)}, + {S_EXCEPT, TEXT_(T_SOCKET_EXCEPTION)}, + {S_INTERNAL, TEXT_(T_INTERNAL_ERROR)}, + {S_OUT_OF_MEM, TEXT_(T_OUT_OF_MEMORY)}, + {S_NO_DNS, TEXT_(T_HOST_NOT_FOUND)}, + {S_CANT_WRITE, TEXT_(T_ERROR_WRITING_TO_SOCKET)}, + {S_CANT_READ, TEXT_(T_ERROR_READING_FROM_SOCKET)}, + {S_MODIFIED, TEXT_(T_DATA_MODIFIED)}, + {S_BAD_URL, TEXT_(T_BAD_URL_SYNTAX)}, + {S_TIMEOUT, TEXT_(T_RECEIVE_TIMEOUT)}, + {S_RESTART, TEXT_(T_REQUEST_MUST_BE_RESTARTED)}, + {S_STATE, TEXT_(T_CANT_GET_SOCKET_STATE)}, + {S_CYCLIC_REDIRECT, TEXT_(T_CYCLIC_REDIRECT)}, + {S_LARGE_FILE, TEXT_(T_TOO_LARGE_FILE)}, + + {S_HTTP_ERROR, TEXT_(T_BAD_HTTP_RESPONSE)}, + {S_HTTP_100, TEXT_(T_HTTP_100)}, + {S_HTTP_204, TEXT_(T_NO_CONTENT)}, + + {S_FILE_TYPE, TEXT_(T_UNKNOWN_FILE_TYPE)}, + {S_FILE_ERROR, TEXT_(T_ERROR_OPENING_FILE)}, + + {S_FTP_ERROR, TEXT_(T_BAD_FTP_RESPONSE)}, + {S_FTP_UNAVAIL, TEXT_(T_FTP_SERVICE_UNAVAILABLE)}, + {S_FTP_LOGIN, TEXT_(T_BAD_FTP_LOGIN)}, + {S_FTP_PORT, TEXT_(T_FTP_PORT_COMMAND_FAILED)}, + {S_FTP_NO_FILE, TEXT_(T_FILE_NOT_FOUND)}, + {S_FTP_FILE_ERROR, TEXT_(T_FTP_FILE_ERROR)}, + + {S_SSL_ERROR, TEXT_(T_SSL_ERROR)}, + {S_NO_SSL, TEXT_(T_NO_SSL)}, + {S_BAD_SOCKS_VERSION, TEXT_(T_BAD_SOCKS_VERSION)}, + {S_SOCKS_REJECTED, TEXT_(T_SOCKS_REJECTED_OR_FAILED)}, + {S_SOCKS_NO_IDENTD, TEXT_(T_SOCKS_NO_IDENTD)}, + {S_SOCKS_BAD_USERID, TEXT_(T_SOCKS_BAD_USERID)}, + {S_SOCKS_UNKNOWN_ERROR, TEXT_(T_SOCKS_UNKNOWN_ERROR)}, + + {S_BLOCKED_URL, TEXT_(T_BLOCKED_URL)}, + {S_NO_PROXY, TEXT_(T_NO_PROXY)}, + {S_NO_SMB_CLIENT, TEXT_(T_NO_SMB_CLIENT)}, + + {S_WAIT_REDIR, TEXT_(T_WAITING_FOR_REDIRECT_CONFIRMATION)}, + {0, NULL} +}; |