/* * Free the SockHandler structure */ void a_Dpip_dsh_free(Dsh *dsh) { dReturn_if (dsh == NULL); dStr_free(dsh->wrbuf, 1); dStr_free(dsh->rdbuf, 1); dFree(dsh); }
/* * If the url belongs to a dpi server, return its name. */ static int Capi_url_uses_dpi(DilloUrl *url, char **server_ptr) { char *p, *server = NULL, *url_str = URL_STR(url); Dstr *tmp; if ((dStrncasecmp(url_str, "http:", 5) == 0) || (dStrncasecmp(url_str, "about:", 6) == 0)) { /* URL doesn't use dpi (server = NULL) */ } else if (dStrncasecmp(url_str, "dpi:/", 5) == 0) { /* dpi prefix, get this server's name */ if ((p = strchr(url_str + 5, '/')) != NULL) { server = dStrndup(url_str + 5, (uint_t)(p - url_str - 5)); } else { server = dStrdup("?"); } if (strcmp(server, "bm") == 0) { dFree(server); server = dStrdup("bookmarks"); } } else if ((p = strchr(url_str, ':')) != NULL) { tmp = dStr_new("proto."); dStr_append_l(tmp, url_str, p - url_str); server = tmp->str; dStr_free(tmp, 0); } return ((*server_ptr = server) ? 1 : 0); }
/* * Read all the available data from a filedescriptor. * This is intended for short answers, i.e. when we know the server * will write it all before being preempted. For answers that may come * as an stream with delays, non-blocking is better. * Return value: read data, or NULL on error and no data. */ static char *Dpi_blocking_read(int fd) { int st; const int buf_sz = 8*1024; char buf[buf_sz], *msg = NULL; Dstr *dstr = dStr_sized_new(buf_sz); do { st = read(fd, buf, buf_sz); if (st < 0) { if (errno == EINTR) { continue; } else { MSG_ERR("[Dpi_blocking_read] %s\n", dStrerror(errno)); break; } } else if (st > 0) { dStr_append_l(dstr, buf, st); } } while (st == buf_sz); msg = (dstr->len > 0) ? dstr->str : NULL; dStr_free(dstr, (dstr->len > 0) ? FALSE : TRUE); return msg; }
/* * Build the dpip command tag, according to URL and server. */ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server) { char *cmd; if (strcmp(server, "proto.https") == 0) { /* Let's be kind and make the HTTP query string for the dpi */ char *proxy_connect = a_Http_make_connect_str(web->url); Dstr *http_query = a_Http_make_query_str(web->url, FALSE); /* BUG: embedded NULLs in query data will truncate message */ if (proxy_connect) { const char *proxy_urlstr = a_Http_get_proxy_urlstr(); cmd = a_Dpip_build_cmd("cmd=%s proxy_url=%s proxy_connect=%s " "url=%s query=%s", "open_url", proxy_urlstr, proxy_connect, URL_STR(web->url), http_query->str); } else { cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s", "open_url", URL_STR(web->url),http_query->str); } dFree(proxy_connect); dStr_free(http_query, 1); } else if (strcmp(server, "downloads") == 0) { /* let the downloads server get it */ cmd = a_Dpip_build_cmd("cmd=%s url=%s destination=%s", "download", URL_STR(web->url), web->filename); } else { /* For everyone else, the url string is enough... */ cmd = a_Dpip_build_cmd("cmd=%s url=%s", "open_url", URL_STR(web->url)); } return cmd; }
static void Decode_charset_free(Decode *dc) { /* iconv_close() frees dc->state */ (void)iconv_close((iconv_t)(dc->state)); dFree(dc->buffer); dStr_free(dc->leftover, 1); }
/* * Free the components of a CacheEntry_t struct. */ static void Cache_entry_free(CacheEntry_t *entry) { a_Url_free((DilloUrl *)entry->Url); dFree(entry->TypeDet); dFree(entry->TypeHdr); dFree(entry->TypeMeta); dFree(entry->TypeNorm); dStr_free(entry->Header, TRUE); a_Url_free((DilloUrl *)entry->Location); Cache_auth_free(entry->Auth); dStr_free(entry->Data, 1); dStr_free(entry->UTF8Data, 1); if (entry->CharsetDecoder) a_Decode_free(entry->CharsetDecoder); if (entry->TransferDecoder) a_Decode_free(entry->TransferDecoder); if (entry->ContentDecoder) a_Decode_free(entry->ContentDecoder); dFree(entry); }
/* * Reference the cache data. */ static void Cache_ref_data(CacheEntry_t *entry) { if (entry) { entry->DataRefcount++; _MSG("DataRefcount++: %d\n", entry->DataRefcount); if (entry->CharsetDecoder && (!entry->UTF8Data || entry->DataRefcount == 1)) { dStr_free(entry->UTF8Data, 1); entry->UTF8Data = a_Decode_process(entry->CharsetDecoder, entry->Data->str, entry->Data->len); } } }
/* * Change Content-Type for cache entry found by url. * from = { "http" | "meta" } * Return new content type. */ const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype, const char *from) { const char *curr; char *major, *minor, *charset; CacheEntry_t *entry = Cache_entry_search(url); dReturn_val_if_fail (entry != NULL, NULL); _MSG("a_Cache_set_content_type {%s} {%s}\n", ctype, URL_STR(url)); curr = Cache_current_content_type(entry); if (entry->TypeMeta || (*from == 'h' && entry->TypeHdr) ) { /* Type is already been set. Do nothing. * BTW, META overrides TypeHdr */ } else { if (*from == 'h') { /* Content-Type from HTTP header */ entry->TypeHdr = dStrdup(ctype); } else { /* Content-Type from META */ entry->TypeMeta = dStrdup(ctype); } if (a_Misc_content_type_cmp(curr, ctype)) { /* ctype gives one different from current */ a_Misc_parse_content_type(ctype, &major, &minor, &charset); if (*from == 'm' && charset && ((!major || !*major) && (!minor || !*minor))) { /* META only gives charset; use detected MIME type too */ entry->TypeNorm = dStrconcat(entry->TypeDet, ctype, NULL); } else if (*from == 'm' && !dStrnAsciiCasecmp(ctype, "text/xhtml", 10)) { /* WORKAROUND: doxygen uses "text/xhtml" in META */ entry->TypeNorm = dStrdup(entry->TypeDet); } if (charset) { if (entry->CharsetDecoder) a_Decode_free(entry->CharsetDecoder); entry->CharsetDecoder = a_Decode_charset_init(charset); curr = Cache_current_content_type(entry); /* Invalidate UTF8Data */ dStr_free(entry->UTF8Data, 1); entry->UTF8Data = NULL; } dFree(major); dFree(minor); dFree(charset); } } return curr; }
/* * Initialize dicache data */ void a_Cache_init(void) { ClientQueue = dList_new(32); DelayedQueue = dList_new(32); CachedURLs = dList_new(256); /* inject the splash screen in the cache */ { DilloUrl *url = a_Url_new("about:splash", NULL); Dstr *ds = dStr_new(AboutSplash); Cache_entry_inject(url, ds); dStr_free(ds, 1); a_Url_free(url); } }
/* * Unreference the cache data. */ static void Cache_unref_data(CacheEntry_t *entry) { if (entry) { entry->DataRefcount--; _MSG("DataRefcount--: %d\n", entry->DataRefcount); if (entry->CharsetDecoder) { if (entry->DataRefcount == 0) { dStr_free(entry->UTF8Data, 1); entry->UTF8Data = NULL; } else if (entry->DataRefcount < 0) { MSG_ERR("Cache_unref_data: negative refcount\n"); entry->DataRefcount = 0; } } } }
/* * Printf like function for building dpip commands. * It takes care of dpip escaping of its arguments. * NOTE : It ONLY accepts string parameters, and * only one %s per parameter. */ char *a_Dpip_build_cmd(const char *format, ...) { va_list argp; char *p, *q, *s; Dstr *cmd; /* Don't allow Quote characters in attribute names */ if (strchr(format, Quote)) return NULL; cmd = dStr_sized_new(64); dStr_append_c(cmd, '<'); va_start(argp, format); for (p = q = (char*)format; *q; ) { p = strstr(q, "%s"); if (!p) { dStr_append(cmd, q); break; } else { /* Copy format's part */ while (q != p) dStr_append_c(cmd, *q++); q += 2; dStr_append_c(cmd, Quote); /* Stuff-copy of argument */ s = va_arg (argp, char *); for ( ; *s; ++s) { dStr_append_c(cmd, *s); if (*s == Quote) dStr_append_c(cmd, *s); } dStr_append_c(cmd, Quote); } } va_end(argp); dStr_append_c(cmd, ' '); dStr_append_c(cmd, Quote); dStr_append_c(cmd, '>'); p = cmd->str; dStr_free(cmd, FALSE); return p; }
/* * Create and submit the HTTP query to the IO engine */ static void Http_send_query(ChainLink *Info, SocketData_t *S) { Dstr *query; DataBuf *dbuf; /* Create the query */ query = a_Http_make_query_str(S->web->url, S->web->requester, S->flags & HTTP_SOCKET_USE_PROXY); dbuf = a_Chain_dbuf_new(query->str, query->len, 0); /* actually this message is sent too early. * It should go when the socket is ready for writing (i.e. connected) */ _MSG_BW(S->web, 1, "Sending query to %s...", URL_HOST_(S->web->url)); /* send query */ a_Chain_bcb(OpSend, Info, dbuf, NULL); dFree(dbuf); dStr_free(query, 1); }
/* * Return a new string for the request used to tunnel HTTPS through a proxy. * As of 2009, the best reference appears to be section 5 of RFC 2817. */ char *a_Http_make_connect_str(const DilloUrl *url) { Dstr *dstr; const char *auth1; int auth_len; char *auth2, *proxy_auth, *retstr; dReturn_val_if_fail(Http_must_use_proxy(url), NULL); dstr = dStr_new(""); auth1 = URL_AUTHORITY(url); auth_len = strlen(auth1); if (auth_len > 0 && !isdigit(auth1[auth_len - 1])) /* if no port number, add HTTPS port */ auth2 = dStrconcat(auth1, ":443", NULL); else auth2 = dStrdup(auth1); proxy_auth = HTTP_Proxy_Auth_base64 ? dStrconcat ("Proxy-Authorization: Basic ", HTTP_Proxy_Auth_base64, "\r\n", NULL) : dStrdup(""); dStr_sprintfa( dstr, "CONNECT %s HTTP/1.1\r\n" "Host: %s\r\n" "%s" "\r\n", auth2, auth2, proxy_auth); dFree(auth2); dFree(proxy_auth); retstr = dstr->str; dStr_free(dstr, 0); return retstr; }
/* * Make the http query string */ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, bool_t use_proxy) { char *ptr, *cookies, *referer, *auth; Dstr *query = dStr_new(""), *request_uri = dStr_new(""), *proxy_auth = dStr_new(""); if (use_proxy) { dStr_sprintfa(request_uri, "%s%s", URL_STR(url), (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/"); if ((ptr = strrchr(request_uri->str, '#'))) dStr_truncate(request_uri, ptr - request_uri->str); if (HTTP_Proxy_Auth_base64) dStr_sprintf(proxy_auth, "Proxy-Authorization: Basic %s\r\n", HTTP_Proxy_Auth_base64); } else { dStr_sprintfa(request_uri, "%s%s%s%s", URL_PATH(url), URL_QUERY_(url) ? "?" : "", URL_QUERY(url), (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/"); } cookies = a_Cookies_get_query(url, requester); auth = a_Auth_get_auth_str(url, request_uri->str); referer = Http_get_referer(url); if (URL_FLAGS(url) & URL_Post) { Dstr *content_type = Http_make_content_type(url); dStr_sprintfa( query, "POST %s HTTP/1.1\r\n" "Connection: close\r\n" "Accept: text/*,image/*,*/*;q=0.2\r\n" "Accept-Charset: utf-8,*;q=0.8\r\n" "Accept-Encoding: gzip\r\n" "%s" /* language */ "%s" /* auth */ "Host: %s\r\n" "%s" "%s" "User-Agent: %s\r\n" "Content-Length: %ld\r\n" "Content-Type: %s\r\n" "%s" /* cookies */ "\r\n", request_uri->str, HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url), proxy_auth->str, referer, prefs.http_user_agent, (long)URL_DATA(url)->len, content_type->str, cookies); dStr_append_l(query, URL_DATA(url)->str, URL_DATA(url)->len); dStr_free(content_type, TRUE); } else { dStr_sprintfa( query, "GET %s HTTP/1.1\r\n" "%s" "Connection: close\r\n" "Accept: text/*,image/*,*/*;q=0.2\r\n" "Accept-Charset: utf-8,*;q=0.8\r\n" "Accept-Encoding: gzip\r\n" "%s" /* language */ "%s" /* auth */ "Host: %s\r\n" "%s" "%s" "User-Agent: %s\r\n" "%s" /* cookies */ "\r\n", request_uri->str, (URL_FLAGS(url) & URL_E2EQuery) ? "Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "", HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url), proxy_auth->str, referer, prefs.http_user_agent, cookies); } dFree(referer); dFree(cookies); dFree(auth); dStr_free(request_uri, TRUE); dStr_free(proxy_auth, TRUE); _MSG("Query: {%s}\n", dStr_printable(query, 8192)); return query; }
/* * 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 */ void 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; CacheEntry_t *entry = Cache_entry_search(Url); /* Assert a valid entry (not aborted) */ dReturn_if_fail (entry != NULL); _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_process(entry->TransferDecoder, str, len); 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; entry = Cache_process_queue(entry); } } else if (Op == IOClose) { if ((entry->ExpectedSize || entry->TransferSize) && entry->TypeHdr == NULL) { MSG_HTTP("Message with a body lacked Content-Type header.\n"); } if ((entry->Flags & CA_GotLength) && (entry->ExpectedSize != entry->TransferSize)) { MSG_HTTP("Content-Length does NOT match message body,\n" " at: %s\n", URL_STR_(entry->Url)); MSG("entry->ExpectedSize = %d, entry->TransferSize = %d\n", entry->ExpectedSize, entry->TransferSize); } if (!entry->TransferSize && !(entry->Flags & CA_Redirect) && (entry->Flags & WEB_RootUrl)) { char *eol = strchr(entry->Header->str, '\n'); if (eol) { char *status_line = dStrndup(entry->Header->str, eol - entry->Header->str); MSG_HTTP("Body was empty. Server sent status: %s\n", status_line); dFree(status_line); } } entry->Flags |= CA_GotData; entry->Flags &= ~CA_Stopped; /* it may catch up! */ if (entry->TransferDecoder) { a_Decode_free(entry->TransferDecoder); entry->TransferDecoder = NULL; } if (entry->ContentDecoder) { a_Decode_free(entry->ContentDecoder); entry->ContentDecoder = NULL; } dStr_fit(entry->Data); /* fit buffer size! */ if ((entry = Cache_process_queue(entry))) { if (entry->Flags & CA_GotHeader) { Cache_unref_data(entry); } } } else if (Op == IOAbort) { /* unused */ MSG("a_Cache_process_dbuf Op = IOAbort; not implemented!\n"); } }
/* * Parse authentication challenge into token-value pairs * and feed them into the callback function. * * The parsing is aborted should the callback function return 0. * * Return: 1 if the parse succeeds, 0 otherwise. */ static int Auth_parse_token_value(AuthParse_t *auth_parse, char **auth, Auth_parse_token_value_callback_t *callback) { char keep_going, expect_quoted; char *token, *beyond_token; Dstr *value; size_t *token_size; while (**auth) { _MSG("Auth_parse_token_value: remaining: %s\n", *auth); /* parse a token */ token = *auth; token_size = 0; while (Auth_is_token_char(**auth)) { (*auth)++; token_size++; } if (token_size == 0) { MSG("Auth_parse_token_value: missing auth token\n"); return 0; } beyond_token = *auth; /* skip linear whitespace characters */ while (**auth == ' ' || **auth == '\t') (*auth)++; /* parse the '=' */ switch (*(*auth)++) { case '=': *beyond_token = '\0'; break; case '\0': case ',': MSG("Auth_parse_token_value: missing auth token value\n"); return 0; break; default: MSG("Auth_parse_token_value: garbage after auth token\n"); return 0; break; } value = Auth_unquote_value(auth); expect_quoted = !(strcmp(token, "stale") == 0 || strcmp(token, "algorithm") == 0); if (((*auth)[-1] == '"') != expect_quoted) MSG_WARN("Auth_parse_token_value: " "Values for key %s should%s be quoted.\n", token, expect_quoted ? "" : " not"); keep_going = callback(auth_parse, token, value->str); dStr_free(value, 1); if (!keep_going) break; /* skip ' ' and ',' */ while ((**auth == ' ') || (**auth == ',')) (*auth)++; } return 1; }
/* * 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); }
static void Decode_chunked_free(Decode *dc) { dFree(dc->state); dStr_free(dc->leftover, 1); }
/* * 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; }