/* * Remove an entry, from the cache. * All the entry clients are removed too! (it may stop rendering of this * same resource on other windows, but nothing more). */ static void Cache_entry_remove(CacheEntry_t *entry, DilloUrl *url) { int i; CacheClient_t *Client; if (!entry && !(entry = Cache_entry_search(url))) return; if (entry->Flags & CA_InternalUrl) return; /* remove all clients for this entry */ for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) { if (Client->Url == entry->Url) { a_Cache_stop_client(Client->Key); --i; } } /* remove from DelayedQueue */ dList_remove(DelayedQueue, entry); /* remove from dicache */ a_Dicache_invalidate_entry(entry->Url); /* remove from cache */ dList_remove(CachedURLs, entry); Cache_entry_free(entry); }
/* * Given authentication challenge(s), prepare authorization. * Return: 0 on failure * nonzero on success. A new query will be sent to the server. */ int a_Auth_do_auth(Dlist *challenges, const DilloUrl *url) { int i; char *chal; for (i = 0; (chal = dList_nth_data(challenges, i)); ++i) if (!dStrnAsciiCasecmp(chal, "Digest ", 7)) if (Auth_do_auth(chal, DIGEST, url)) return 1; for (i = 0; (chal = dList_nth_data(challenges, i)); ++i) if (!dStrnAsciiCasecmp(chal, "Basic ", 6)) if (Auth_do_auth(chal, BASIC, url)) return 1; return 0; }
/* * Free Authentication fields. */ static void Cache_auth_free(Dlist *auth) { int i; void *auth_field; for (i = 0; (auth_field = dList_nth_data(auth, i)); ++i) dFree(auth_field); dList_free(auth); }
/* * Memory deallocator (only called at exit time) */ void a_Cache_freeall(void) { CacheClient_t *Client; void *data; /* free the client queue */ while ((Client = dList_nth_data(ClientQueue, 0))) Cache_client_dequeue(Client, NULLKey); /* Remove every cache entry */ while ((data = dList_nth_data(CachedURLs, 0))) { dList_remove_fast(CachedURLs, data); Cache_entry_free(data); } /* Remove the cache list */ dList_free(CachedURLs); }
/*! Free memory used by the services list */ void free_services_list(Dlist *s_list) { int i = 0; struct service *s; for (i=0; i < dList_length(s_list) ; i++) { s = dList_nth_data(s_list, i); dFree(s->name); } dList_free(s_list); }
static int Auth_realm_includes_path(const AuthRealm_t *realm, const char *path) { int i; char *realm_path; for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++) if (Auth_path_is_inside(path, realm_path, strlen(realm_path))) return 1; return 0; }
static void Http_host_connection_remove_all() { HostConnection_t *hc; while (dList_length(host_connections) > 0) { hc = (HostConnection_t*) dList_nth_data(host_connections, 0); while (Http_socket_dequeue(&hc->queue)); Http_host_connection_remove(hc); } dList_free(host_connections); }
/* * Search all realms for the one with the given name. */ static AuthRealm_t *Auth_realm_by_name(const AuthHost_t *host, const char *name) { AuthRealm_t *realm; int i; for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) if (strcmp(realm->name, name) == 0) return realm; return NULL; }
/* * Return the host that contains a URL, or NULL if there is no such host. */ static AuthHost_t *Auth_host_by_url(const DilloUrl *url) { AuthHost_t *host; int i; for (i = 0; (host = dList_nth_data(auth_hosts, i)); i++) if (((dStrAsciiCasecmp(URL_SCHEME(url), host->scheme) == 0) && (dStrAsciiCasecmp(URL_AUTHORITY(url), host->authority) == 0))) return host; return NULL; }
/* * Search all realms for the one with the best-matching path. */ static AuthRealm_t *Auth_realm_by_path(const AuthHost_t *host, const char *path) { AuthRealm_t *realm_best, *realm; int i, j; int match_length = 0; realm_best = NULL; for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) { char *realm_path; for (j = 0; (realm_path = dList_nth_data(realm->paths, j)); j++) { int realm_path_length = strlen(realm_path); if (Auth_path_is_inside(path, realm_path, realm_path_length) && !(realm_best && match_length >= realm_path_length)) { realm_best = realm; match_length = realm_path_length; } } } return realm_best; }
/* * Callback function for Cache_delayed_process_queue. */ static void Cache_delayed_process_queue_callback() { CacheEntry_t *entry; while ((entry = (CacheEntry_t *)dList_nth_data(DelayedQueue, 0))) { Cache_ref_data(entry); if ((entry = Cache_process_queue(entry))) { Cache_unref_data(entry); dList_remove(DelayedQueue, entry); } } DelayedQueueIdleId = 0; a_Timeout_remove(); }
/* * Last Client for this entry? * Return: Client if true, NULL otherwise * (cache.c has only one call to a capi function. This avoids a second one) */ CacheClient_t *a_Cache_client_get_if_unique(int Key) { int i, n = 0; CacheClient_t *Client, *iClient; if ((Client = dList_find_custom(ClientQueue, INT2VOIDP(Key), Cache_client_by_key_cmp))) { for (i = 0; (iClient = dList_nth_data(ClientQueue, i)); ++i) { if (iClient->Url == Client->Url) { ++n; } } } return (n == 1) ? Client : NULL; }
static void Auth_realm_delete(AuthRealm_t *realm) { int i; MSG("Auth_realm_delete: \"%s\"\n", realm->name); for (i = dList_length(realm->paths) - 1; i >= 0; i--) dFree(dList_nth_data(realm->paths, i)); dList_free(realm->paths); dFree(realm->name); dFree(realm->username); dFree(realm->authorization); dFree(realm->cnonce); dFree(realm->nonce); dFree(realm->opaque); dFree(realm->domain); dFree(realm); }
/* * Resume connections that were waiting for dpid to start. */ static void Capi_conn_resume(void) { int i; DataBuf *dbuf; capi_conn_t *conn; for (i = 0; i < dList_length(CapiConns); ++i) { conn = dList_nth_data (CapiConns, i); if (conn->Flags & PENDING) { dbuf = a_Chain_dbuf_new(conn->datastr,(int)strlen(conn->datastr), 0); if (conn->InfoSend) { a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL); } dFree(dbuf); conn->Flags &= ~PENDING; } } }
/* * Abort the connection for a given url, using its CCC. * (OpAbort 2,BCK removes the cache entry) * TODO: when conn is already done, the cache entry isn't removed. * This may be wrong and needs a revision. */ void a_Capi_conn_abort_by_url(const DilloUrl *url) { int i; capi_conn_t *conn; for (i = 0; i < dList_length(CapiConns); ++i) { conn = dList_nth_data (CapiConns, i); if (a_Url_cmp(conn->url, url) == 0) { if (conn->InfoSend) { a_Capi_ccc(OpAbort, 1, BCK, conn->InfoSend, NULL, NULL); } if (conn->InfoRecv) { a_Capi_ccc(OpAbort, 2, BCK, conn->InfoRecv, NULL, NULL); } break; } } }
static HostConnection_t *Http_host_connection_get(const char *host) { int i; HostConnection_t *hc; for (i = 0; i < dList_length(host_connections); i++) { hc = (HostConnection_t*) dList_nth_data(host_connections, i); if (dStrAsciiCasecmp(host, hc->host) == 0) return hc; } hc = dNew0(HostConnection_t, 1); Http_socket_queue_init(&hc->queue); hc->host = dStrdup(host); dList_append(host_connections, hc); return hc; }
static void Auth_realm_add_path(AuthRealm_t *realm, const char *path) { int len, i; char *realm_path, *n_path; n_path = dStrdup(path); len = strlen(n_path); /* remove trailing '/' */ if (len && n_path[len - 1] == '/') n_path[--len] = 0; /* delete existing paths that are inside the new one */ for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++) { if (Auth_path_is_inside(realm_path, path, len)) { dList_remove_fast(realm->paths, realm_path); dFree(realm_path); i--; /* reconsider this slot */ } } dList_append(realm->paths, n_path); }
/* * Scan, allocate, and set things according to header info. * (This function needs the whole header to work) */ static void Cache_parse_header(CacheEntry_t *entry) { char *header = entry->Header->str; char *Length, *Type, *location_str, *encoding; #ifndef DISABLE_COOKIES Dlist *Cookies; #endif Dlist *warnings; void *data; int i; _MSG("Cache_parse_header\n"); if (entry->Header->len > 12) { if (header[9] == '1' && header[10] == '0' && header[11] == '0') { /* 100: Continue. The "real" header has not come yet. */ MSG("An actual 100 Continue header!\n"); entry->Flags &= ~CA_GotHeader; dStr_free(entry->Header, 1); entry->Header = dStr_new(""); return; } if (header[9] == '3' && header[10] == '0' && (location_str = Cache_parse_field(header, "Location"))) { /* 30x: URL redirection */ DilloUrl *location_url = a_Url_new(location_str,URL_STR_(entry->Url)); if (prefs.filter_auto_requests == PREFS_FILTER_SAME_DOMAIN && !a_Url_same_organization(entry->Url, location_url)) { /* don't redirect; just show body like usual (if any) */ MSG("Redirection not followed from %s to %s\n", URL_HOST(entry->Url), URL_STR(location_url)); a_Url_free(location_url); } else { entry->Flags |= CA_Redirect; if (header[11] == '1') entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */ else if (header[11] == '2') entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */ if (URL_FLAGS(location_url) & (URL_Post + URL_Get) && dStrAsciiCasecmp(URL_SCHEME(location_url), "dpi") == 0 && dStrAsciiCasecmp(URL_SCHEME(entry->Url), "dpi") != 0) { /* Forbid dpi GET and POST from non dpi-generated urls */ MSG("Redirection Denied! '%s' -> '%s'\n", URL_STR(entry->Url), URL_STR(location_url)); a_Url_free(location_url); } else { entry->Location = location_url; } } dFree(location_str); } else if (strncmp(header + 9, "401", 3) == 0) { entry->Auth = Cache_parse_multiple_fields(header, "WWW-Authenticate"); } else if (strncmp(header + 9, "404", 3) == 0) { entry->Flags |= CA_NotFound; } } if ((warnings = Cache_parse_multiple_fields(header, "Warning"))) { for (i = 0; (data = dList_nth_data(warnings, i)); ++i) { MSG_HTTP("%s\n", (char *)data); dFree(data); } dList_free(warnings); } /* * Get Transfer-Encoding and initialize decoder */ encoding = Cache_parse_field(header, "Transfer-Encoding"); entry->TransferDecoder = a_Decode_transfer_init(encoding); if ((Length = Cache_parse_field(header, "Content-Length")) != NULL) { if (encoding) { /* * If Transfer-Encoding is present, Content-Length must be ignored. * If the Transfer-Encoding is non-identity, it is an error. */ if (dStrAsciiCasecmp(encoding, "identity")) MSG_HTTP("Content-Length and non-identity Transfer-Encoding " "headers both present.\n"); } else { entry->Flags |= CA_GotLength; entry->ExpectedSize = MAX(strtol(Length, NULL, 10), 0); } dFree(Length); } dFree(encoding); /* free Transfer-Encoding */ #ifndef DISABLE_COOKIES if ((Cookies = Cache_parse_multiple_fields(header, "Set-Cookie"))) { CacheClient_t *client; for (i = 0; (client = dList_nth_data(ClientQueue, i)); ++i) { if (client->Url == entry->Url) { DilloWeb *web = client->Web; if (!web->requester || a_Url_same_organization(entry->Url, web->requester)) { char *server_date = Cache_parse_field(header, "Date"); a_Cookies_set(Cookies, entry->Url, server_date); dFree(server_date); break; } } } if (i >= dList_length(ClientQueue)) { MSG("Cache: cookies not accepted from '%s'\n", URL_STR(entry->Url)); } for (i = 0; (data = dList_nth_data(Cookies, i)); ++i) dFree(data); dList_free(Cookies); } #endif /* !DISABLE_COOKIES */ /* * Get Content-Encoding and initialize decoder */ encoding = Cache_parse_field(header, "Content-Encoding"); entry->ContentDecoder = a_Decode_content_init(encoding); dFree(encoding); if (entry->ExpectedSize > 0) { if (entry->ExpectedSize > HUGE_FILESIZE) { entry->Flags |= CA_HugeFile; } /* Avoid some reallocs. With MAX_INIT_BUF we avoid a SEGFAULT * with huge files (e.g. iso files). * Note: the buffer grows automatically. */ dStr_free(entry->Data, 1); entry->Data = dStr_sized_new(MIN(entry->ExpectedSize, MAX_INIT_BUF)); } /* Get Content-Type */ if ((Type = Cache_parse_field(header, "Content-Type"))) { /* This HTTP Content-Type is not trusted. It's checked against real data * in Cache_process_queue(); only then CA_GotContentType becomes true. */ a_Cache_set_content_type(entry->Url, Type, "http"); _MSG("TypeHdr {%s} {%s}\n", Type, URL_STR(entry->Url)); _MSG("TypeMeta {%s}\n", entry->TypeMeta); dFree(Type); } Cache_ref_data(entry); }
/* * This function gets called after the DNS succeeds solving a hostname. * Task: Finish socket setup and start connecting the socket. * Return value: 0 on success; -1 on error. */ static int Http_connect_socket(ChainLink *Info) { int i, status; #ifdef ENABLE_IPV6 struct sockaddr_in6 name; #else struct sockaddr_in name; #endif SocketData_t *S; DilloHost *dh; socklen_t socket_len = 0; S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey)); /* TODO: iterate this address list until success, or end-of-list */ for (i = 0; (dh = dList_nth_data(S->addr_list, i)); ++i) { if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) { S->Err = errno; MSG("Http_connect_socket ERROR: %s\n", dStrerror(errno)); continue; } /* set NONBLOCKING and close on exec. */ fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL)); fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD)); /* Some OSes require this... */ memset(&name, 0, sizeof(name)); /* Set remaining parms. */ switch (dh->af) { case AF_INET: { struct sockaddr_in *sin = (struct sockaddr_in *)&name; socket_len = sizeof(struct sockaddr_in); sin->sin_family = dh->af; sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT); memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen); if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl)) MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr)); break; } #ifdef ENABLE_IPV6 case AF_INET6: { char buf[128]; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name; socket_len = sizeof(struct sockaddr_in6); sin6->sin6_family = dh->af; sin6->sin6_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT); memcpy(&sin6->sin6_addr, dh->data, dh->alen); inet_ntop(dh->af, dh->data, buf, sizeof(buf)); if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl)) MSG("Connecting to %s\n", buf); break; } #endif }/*switch*/ MSG_BW(S->web, 1, "Contacting host..."); status = connect(S->SockFD, (struct sockaddr *)&name, socket_len); if (status == -1 && errno != EINPROGRESS) { S->Err = errno; Http_socket_close(S); MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err)); } else { a_Chain_bcb(OpSend, Info, &S->SockFD, "FD"); a_Chain_fcb(OpSend, Info, &S->SockFD, "FD"); Http_send_query(S->Info, S); return 0; /* Success */ } } return -1; }
/* * Update cache clients for a single cache-entry * Tasks: * - Set the client function (if not already set) * - Look if new data is available and pass it to client functions * - Remove clients when done * - Call redirect handler * * Return: Cache entry, which may be NULL if it has been removed. * * TODO: Implement CA_Abort Op in client callback */ static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry) { uint_t i; int st; const char *Type; Dstr *data; CacheClient_t *Client; DilloWeb *ClientWeb; BrowserWindow *Client_bw = NULL; static bool_t Busy = FALSE; bool_t AbortEntry = FALSE; bool_t OfferDownload = FALSE; bool_t TypeMismatch = FALSE; if (Busy) MSG_ERR("FATAL!: >>>> Cache_process_queue Caught busy!!! <<<<\n"); if (!(entry->Flags & CA_GotHeader)) return entry; if (!(entry->Flags & CA_GotContentType)) { st = a_Misc_get_content_type_from_data( entry->Data->str, entry->Data->len, &Type); _MSG("Cache: detected Content-Type '%s'\n", Type); if (st == 0 || entry->Flags & CA_GotData) { if (a_Misc_content_type_check(entry->TypeHdr, Type) < 0) { MSG_HTTP("Content-Type '%s' doesn't match the real data.\n", entry->TypeHdr); TypeMismatch = TRUE; } entry->TypeDet = dStrdup(Type); entry->Flags |= CA_GotContentType; } else return entry; /* i.e., wait for more data */ } Busy = TRUE; for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) { if (Client->Url == entry->Url) { ClientWeb = Client->Web; /* It was a (void*) */ Client_bw = ClientWeb->bw; /* 'bw' in a local var */ if (ClientWeb->flags & WEB_RootUrl) { if (!(entry->Flags & CA_MsgErased)) { /* clear the "expecting for reply..." message */ a_UIcmd_set_msg(Client_bw, ""); entry->Flags |= CA_MsgErased; } if (TypeMismatch) { a_UIcmd_set_msg(Client_bw,"HTTP warning: Content-Type '%s' " "doesn't match the real data.", entry->TypeHdr); OfferDownload = TRUE; } if (entry->Flags & CA_Redirect) { if (!Client->Callback) { Client->Callback = Cache_null_client; Client_bw->redirect_level++; } } else { Client_bw->redirect_level = 0; } if (entry->Flags & CA_HugeFile) { a_UIcmd_set_msg(Client_bw,"Huge file! (%dMB)", entry->ExpectedSize / (1024*1024)); AbortEntry = OfferDownload = TRUE; } } else { /* For non root URLs, ignore redirections and 404 answers */ if (entry->Flags & CA_Redirect || entry->Flags & CA_NotFound) Client->Callback = Cache_null_client; } /* Set the client function */ if (!Client->Callback) { Client->Callback = Cache_null_client; if (TypeMismatch) { AbortEntry = TRUE; } else { const char *curr_type = Cache_current_content_type(entry); st = a_Web_dispatch_by_type(curr_type, ClientWeb, &Client->Callback, &Client->CbData); if (st == -1) { /* MIME type is not viewable */ if (ClientWeb->flags & WEB_RootUrl) { MSG("Content-Type '%s' not viewable.\n", curr_type); /* prepare a download offer... */ AbortEntry = OfferDownload = TRUE; } else { /* TODO: Resource Type not handled. * Not aborted to avoid multiple connections on the same * resource. A better idea is to abort the connection and * to keep a failed-resource flag in the cache entry. */ } } } if (AbortEntry) { if (ClientWeb->flags & WEB_RootUrl) a_Nav_cancel_expect_if_eq(Client_bw, Client->Url); a_Bw_remove_client(Client_bw, Client->Key); Cache_client_dequeue(Client, NULLKey); --i; /* Keep the index value in the next iteration */ continue; } } /* Send data to our client */ if (ClientWeb->flags & WEB_Download) { /* for download, always provide original data, not translated */ data = entry->Data; } else { data = Cache_data(entry); } if ((Client->BufSize = data->len) > 0) { Client->Buf = data->str; (Client->Callback)(CA_Send, Client); if (ClientWeb->flags & WEB_RootUrl) { /* show size of page received */ a_UIcmd_set_page_prog(Client_bw, entry->Data->len, 1); } } /* Remove client when done */ if (entry->Flags & CA_GotData) { /* Copy flags to a local var */ int flags = ClientWeb->flags; /* We finished sending data, let the client know */ (Client->Callback)(CA_Close, Client); if (ClientWeb->flags & WEB_RootUrl) a_UIcmd_set_page_prog(Client_bw, 0, 0); Cache_client_dequeue(Client, NULLKey); --i; /* Keep the index value in the next iteration */ /* within CA_GotData, we assert just one redirect call */ if (entry->Flags & CA_Redirect) Cache_redirect(entry, flags, Client_bw); } } } /* for */ if (AbortEntry) { /* Abort the entry, remove it from cache, and maybe offer download. */ DilloUrl *url = a_Url_dup(entry->Url); a_Capi_conn_abort_by_url(url); entry = NULL; if (OfferDownload) { /* Remove entry when 'conn' is already done */ Cache_entry_remove(NULL, url); if (a_Cache_download_enabled(url)) { Cache_savelink_t *data = dNew(Cache_savelink_t, 1); data->bw = Client_bw; data->url = a_Url_dup(url); a_Timeout_add(0.0, Cache_savelink_cb, data); } } a_Url_free(url); } else if (entry->Auth && (entry->Flags & CA_GotData)) { Cache_auth_entry(entry, Client_bw); } /* Trigger cleanup when there are no cache clients */ if (dList_length(ClientQueue) == 0) { _MSG(" a_Dicache_cleanup()\n"); a_Dicache_cleanup(); } Busy = FALSE; _MSG("QueueSize ====> %d\n", dList_length(ClientQueue)); return entry; }
/* * Flush cookies to disk and free all the memory allocated. */ static void Cookies_save_and_free() { int i, fd, saved = 0; DomainNode *node; CookieData_t *cookie; time_t now; #ifndef HAVE_LOCKF struct flock lck; #endif if (disabled) return; now = time(NULL); rewind(file_stream); fd = fileno(file_stream); if (ftruncate(fd, 0) == -1) MSG("Cookies: Truncate file stream failed: %s\n", dStrerror(errno)); fprintf(file_stream, "%s", cookies_txt_header_str); /* Iterate cookies per domain, saving and freeing */ while ((node = dList_nth_data(domains, 0))) { for (i = 0; (cookie = dList_nth_data(node->cookies, i)); ++i) { if (!cookie->session_only && difftime(cookie->expires_at, now) > 0) { int len; char buf[LINE_MAXLEN]; len = snprintf(buf, LINE_MAXLEN, "%s\t%s\t%s\t%s\t%ld\t%s\t%s\n", cookie->domain, cookie->host_only ? "FALSE" : "TRUE", cookie->path, cookie->secure ? "TRUE" : "FALSE", (long) difftime(cookie->expires_at, cookies_epoch_time), cookie->name, cookie->value); if (len < LINE_MAXLEN) { fprintf(file_stream, "%s", buf); saved++; } else { MSG("Not saving overly long cookie for %s.\n", cookie->domain); } } Cookies_free_cookie(cookie); } Cookies_delete_node(node); } dList_free(domains); dList_free(all_cookies); #ifdef HAVE_LOCKF lockf(fd, F_ULOCK, 0); #else /* POSIX file lock */ lck.l_start = 0; /* start at beginning of file */ lck.l_len = 0; /* lock entire file */ lck.l_type = F_UNLCK; lck.l_whence = SEEK_SET; /* absolute offset */ fcntl(fileno(file_stream), F_SETLKW, &lck); #endif fclose(file_stream); MSG("Cookies saved: %d.\n", saved); }
/* * Receive new data, update the reception buffer (for next read), update the * cache, and service the client queue. * * This function gets called whenever the IO has new data. * 'Op' is the operation to perform * 'VPtr' is a (void) pointer to the IO control structure */ bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, const DilloUrl *Url) { int offset, len; const char *str; Dstr *dstr1, *dstr2, *dstr3; bool_t done = FALSE; CacheEntry_t *entry = Cache_entry_search(Url); /* Assert a valid entry (not aborted) */ dReturn_val_if_fail (entry != NULL, FALSE); _MSG("__a_Cache_process_dbuf__\n"); if (Op == IORead) { /* * Cache_get_header() will set CA_GotHeader if it has a full header, and * Cache_parse_header() will unset it if the header ends being * merely an informational response from the server (i.e., 100 Continue) */ for (offset = 0; !(entry->Flags & CA_GotHeader) && (len = Cache_get_header(entry, buf + offset, buf_size - offset)); Cache_parse_header(entry) ) { offset += len; } if (entry->Flags & CA_GotHeader) { str = buf + offset; len = buf_size - offset; entry->TransferSize += len; dstr1 = dstr2 = dstr3 = NULL; /* Decode arrived data (<= 3 stages) */ if (entry->TransferDecoder) { dstr1 = a_Decode_transfer_process(entry->TransferDecoder, str,len); done = a_Decode_transfer_finished(entry->TransferDecoder); str = dstr1->str; len = dstr1->len; } if (entry->ContentDecoder) { dstr2 = a_Decode_process(entry->ContentDecoder, str, len); str = dstr2->str; len = dstr2->len; } dStr_append_l(entry->Data, str, len); if (entry->CharsetDecoder && entry->UTF8Data) { dstr3 = a_Decode_process(entry->CharsetDecoder, str, len); dStr_append_l(entry->UTF8Data, dstr3->str, dstr3->len); } dStr_free(dstr1, 1); dStr_free(dstr2, 1); dStr_free(dstr3, 1); if (entry->Data->len) entry->Flags &= ~CA_IsEmpty; if ((entry->Flags & CA_GotLength) && (entry->TransferSize >= entry->ExpectedSize)) { done = TRUE; } if (!(entry->Flags & CA_KeepAlive)) { /* Let IOClose finish it later */ done = FALSE; } entry = Cache_process_queue(entry); if (entry && done) Cache_finish_msg(entry); } } else if (Op == IOClose) { Cache_finish_msg(entry); } else if (Op == IOAbort) { int i; CacheClient_t *Client; for (i = 0; (Client = dList_nth_data(ClientQueue, i)); ++i) { if (Client->Url == entry->Url) { DilloWeb *web = (DilloWeb *)Client->Web; a_Bw_remove_client(web->bw, Client->Key); Cache_client_dequeue(Client); --i; /* Keep the index value in the next iteration */ } } } return done; }