/* * 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; }
/* * Test proxy settings and check the no_proxy domains list * Return value: whether to use proxy or not. */ static int Http_must_use_proxy(const DilloUrl *url) { char *np, *p, *tok; int ret = 0; if (HTTP_Proxy) { ret = 1; if (prefs.no_proxy) { const char *host = URL_HOST(url); size_t host_len = strlen(host); np = dStrdup(prefs.no_proxy); for (p = np; (tok = dStrsep(&p, " ")); ) { int start = host_len - strlen(tok); if (start >= 0 && dStrAsciiCasecmp(host + start, tok) == 0) { /* no_proxy token is suffix of host string */ ret = 0; break; } } dFree(np); } } _MSG("Http_must_use_proxy: %s\n %s\n", URL_STR(url), ret ? "YES":"NO"); return ret; }
/* * 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); }
/* * Safety test: only allow GET|POST dpi-urls from dpi-generated pages. */ int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url) { DilloUrl *referer; int allow = FALSE; /* test POST and GET */ if (dStrcasecmp(URL_SCHEME(url), "dpi") == 0 && URL_FLAGS(url) & (URL_Post + URL_Get)) { /* only allow dpi requests from dpi-generated urls */ if (a_Nav_stack_size(bw)) { referer = a_History_get_url(NAV_TOP_UIDX(bw)); if (dStrcasecmp(URL_SCHEME(referer), "dpi") == 0) { allow = TRUE; } } } else { allow = TRUE; } if (!allow) { MSG("a_Capi_dpi_verify_request: Permission Denied!\n"); MSG(" URL_STR : %s\n", URL_STR(url)); if (URL_FLAGS(url) & URL_Post) { MSG(" URL_DATA: %s\n", dStr_printable(URL_DATA(url), 1024)); } } return allow; }
/* * Let the client know that we're not following a redirection. */ static void Cache_provide_redirection_blocked_page(CacheEntry_t *entry, CacheClient_t *client) { DilloWeb *clientWeb = client->Web; a_Web_dispatch_by_type("text/html", clientWeb, &client->Callback, &client->CbData); client->Buf = dStrconcat("<!doctype html><html><body>" "Dillo blocked a redirection from <a href=\"", URL_STR(entry->Url), "\">", URL_STR(entry->Url), "</a> to <a href=\"", URL_STR(entry->Location), "\">", URL_STR(entry->Location), "</a> based on your domainrc " "settings.</body></html>", NULL); client->BufSize = strlen(client->Buf); (client->Callback)(CA_Send, client); dFree(client->Buf); }
/* this is the routine called by libjpeg when it detects an error. */ METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo) { /* display message and return to setjmp buffer */ my_error_ptr myerr = (my_error_ptr) cinfo->err; if (prefs.show_msg) { DilloJpeg *jpeg = ((my_source_mgr *) ((j_decompress_ptr) cinfo)->src)->jpeg; MSG_WARN("\"%s\": ", URL_STR(jpeg->url)); (*cinfo->err->output_message) (cinfo); } longjmp(myerr->setjmp_buffer, 1); }
/* * Determine whether the user needs to authenticate. */ static int Auth_do_auth_required(const AuthParse_t *auth_parse, const DilloUrl *url) { /* * TO DO: I dislike the way that this code must decide whether we * sent authentication during the request and trust us to resend it * after the reload. Could it be more robust if every DilloUrl * recorded its authentication, and whether it was accepted? (JCH) */ AuthHost_t *host; AuthRealm_t *realm; /* * The size of the following comments reflects the concerns in the * TO DO at the top of this function. It should not be so hard to * explain why code is correct! (JCH) */ /* * If we have authentication but did not send it (because we did * not know this path was in the realm) then we update the realm. * We do not re-authenticate because our authentication is probably * OK. Thanks to the updated realm the forthcoming reload will * make us send the authentication. If our authentication is not * OK the server will challenge us again after the reload and then * we will re-authenticate. */ if ((host = Auth_host_by_url(url)) && (realm = Auth_realm_by_name(host, auth_parse->realm))) { if (!Auth_realm_includes_path(realm, URL_PATH(url))) { _MSG("Auth_do_auth_required: updating realm '%s' with URL '%s'\n", auth_parse->realm, URL_STR(url)); Auth_realm_add_path(realm, URL_PATH(url)); return 0; } if (auth_parse->type == DIGEST && auth_parse->stale) { /* we do have valid credentials but our nonce is old */ dFree((void *)realm->nonce); realm->nonce = dStrdup(auth_parse->nonce); return 0; } } /* * Either we had no authentication or we sent it and the server * rejected it, so we must re-authenticate. */ return 1; }
/* * 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; }
/* * Given the MIME content type, and a fd to read it from, * this function connects the proper MIME viewer to it. * Return value: 1 on success, -1 for unhandled MIME types */ gint a_Web_dispatch_by_type (const char *Type, DilloWeb *Web, CA_Callback_t *Call, void **Data) { DwWidget *dw = NULL; DwStyle style_attrs, *style; DwStyleFont font; DEBUG_MSG(1, "a_Web_dispatch_by_type\n"); g_return_val_if_fail(Web->bw != NULL, -1); if (Web->flags & WEB_RootUrl) { /* We have RootUrl! */ dw = a_Mime_set_viewer(Type, Web, Call, Data); if (dw == NULL) return -1; /* Set a style for the widget */ font.name = prefs.vw_fontname; /* must be defined */ font.size = rint(12.0 * prefs.font_factor); font.weight = 400; font.style = DW_STYLE_FONT_STYLE_NORMAL; a_Dw_style_init_values (&style_attrs, Web->bw->main_window->window); a_Dw_style_box_set_val (&style_attrs.margin, 5); style_attrs.font = a_Dw_style_font_new (&font); style_attrs.color = a_Dw_style_color_new (prefs.text_color, Web->bw->main_window->window); style_attrs.background_color = a_Dw_style_color_new (prefs.bg_color, Web->bw->main_window->window); style = a_Dw_style_new (&style_attrs, Web->bw->main_window->window); a_Dw_widget_set_style (dw, style); a_Dw_style_unref (style); a_Dw_gtk_scrolled_window_set_dw( GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), dw); if (URL_POSX(Web->url) || URL_POSY(Web->url)) { a_Dw_gtk_scrolled_window_set_scrolling_position( GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), URL_POSX(Web->url), URL_POSY(Web->url)); } else { gchar *pf = a_Url_decode_hex_str(URL_FRAGMENT_(Web->url)); a_Dw_gtk_scrolled_window_set_anchor( GTK_DW_SCROLLED_WINDOW(Web->bw->docwin), pf); g_free(pf); } /* Clear the title bar for pages without a <TITLE> tag */ a_Interface_set_page_title(Web->bw, ""); a_Interface_set_location_text(Web->bw, URL_STR(Web->url)); a_Interface_reset_progress_bars(Web->bw); /* Reset the bug meter */ a_Interface_bug_meter_update(Web->bw, 0); /* Let the Nav module know... */ a_Nav_expect_done(Web->bw); } else { /* A non-RootUrl. At this moment we only handle image-children */ if (!g_strncasecmp(Type, "image/", 6)) dw = a_Mime_set_viewer(Type, Web, Call, Data); } if (!dw) { MSG_HTTP("unhandled MIME type: \"%s\"\n", Type); } return (dw ? 1 : -1); }
/* * Return URL string of HTTP proxy, if any */ const char *a_Http_get_proxy_urlstr() { return HTTP_Proxy ? URL_STR(HTTP_Proxy) : NULL; }
/* * 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; }
/* * 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); }