/*! 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 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); }
/* * Decrement the reference count (and remove from list when zero) */ static void Capi_conn_unref(capi_conn_t *conn) { _MSG(" Capi_conn_unref #%d %p\n", conn->Ref - 1, conn); /* We may validate conn here, but it doesn't *seem* necessary */ if (--conn->Ref == 0) { /* remove conn preserving the list order */ dList_remove(CapiConns, (void *)conn); /* free dynamic memory */ a_Url_free(conn->url); dFree(conn->server); dFree(conn->datastr); dFree(conn); } _MSG(" Capi_conn_unref CapiConns=%d\n", dList_length(CapiConns)); }
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); }
/* * 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; } } }
/* * 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; } } }
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; }
/* * Extract multiple fields from the header. */ static Dlist *Cache_parse_multiple_fields(const char *header, const char *fieldname) { uint_t i, j; Dlist *fields = dList_new(8); char *field; for (i = 0; header[i]; i++) { /* Search fieldname */ for (j = 0; fieldname[j]; j++) if (D_ASCII_TOLOWER(fieldname[j]) != D_ASCII_TOLOWER(header[i + j])) break; if (fieldname[j]) { /* skip to next line */ for (i += j; header[i] != '\n'; i++); continue; } i += j; if (header[i] == ':') { /* Field found! */ while (header[++i] == ' ' || header[i] == '\t'); for (j = 0; header[i + j] != '\n'; j++); while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t')) j--; field = dStrndup(header + i, j); dList_append(fields, field); } else { while (header[i] != '\n') i++; } } if (dList_length(fields) == 0) { dList_free(fields); fields = NULL; } return fields; }
/* * Read in cookies from 'stream' (cookies.txt) */ static void Cookies_load_cookies(FILE *stream) { char line[LINE_MAXLEN]; all_cookies = dList_new(32); domains = dList_new(32); /* Get all lines in the file */ while (!feof(stream)) { line[0] = '\0'; if ((fgets(line, LINE_MAXLEN, stream) == NULL) && ferror(stream)) { MSG("Error while reading from cookies.txt: %s\n", dStrerror(errno)); break; /* bail out */ } /* Remove leading and trailing whitespaces */ dStrstrip(line); if ((line[0] != '\0') && (line[0] != '#')) { /* * Split the row into pieces using a tab as the delimiter. * pieces[0] The domain name * pieces[1] TRUE/FALSE: is the domain a suffix, or a full domain? * pieces[2] The path * pieces[3] TRUE/FALSE: is the cookie for secure use only? * pieces[4] Timestamp of expire date * pieces[5] Name of the cookie * pieces[6] Value of the cookie */ CookieControlAction action; char *piece; char *line_marker = line; CookieData_t *cookie = dNew0(CookieData_t, 1); cookie->session_only = FALSE; cookie->domain = dStrdup(dStrsep(&line_marker, "\t")); piece = dStrsep(&line_marker, "\t"); if (piece != NULL && piece[0] == 'F') cookie->host_only = TRUE; cookie->path = dStrdup(dStrsep(&line_marker, "\t")); piece = dStrsep(&line_marker, "\t"); if (piece != NULL && piece[0] == 'T') cookie->secure = TRUE; piece = dStrsep(&line_marker, "\t"); if (piece != NULL) { /* There is some problem with simply putting the maximum value * into tm.tm_sec (although a value close to it works). */ long seconds = strtol(piece, NULL, 10); struct tm tm; Cookies_tm_init(&tm); tm.tm_min += seconds / 60; tm.tm_sec += seconds % 60; cookie->expires_at = mktime(&tm); } else { cookie->expires_at = (time_t) -1; } cookie->name = dStrdup(dStrsep(&line_marker, "\t")); cookie->value = dStrdup(line_marker ? line_marker : ""); if (!cookie->domain || cookie->domain[0] == '\0' || !cookie->path || cookie->path[0] != '/' || !cookie->name || !cookie->value) { MSG("Malformed line in cookies.txt file!\n"); Cookies_free_cookie(cookie); continue; } action = Cookies_control_check_domain(cookie->domain); if (action == COOKIE_DENY) { Cookies_free_cookie(cookie); continue; } else if (action == COOKIE_ACCEPT_SESSION) { cookie->session_only = TRUE; } /* Save cookie in memory */ Cookies_add_cookie(cookie); } } MSG("Cookies loaded: %d.\n", dList_length(all_cookies)); }
/* * 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); }
/* * 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; }
/*! Add services reading a dpidrc file * each non empty or commented line has the form * service = path_relative_to_dpidir * \Return: * \li Returns number of available services on success * \li -1 on failure */ int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list) { FILE *dpidrc_stream; char *p, *line = NULL, *service, *path; int i, st; struct service *s; char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL; user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL); if (access(user_dpidir, F_OK) == -1) { /* no dpis in user's space */ dFree(user_dpidir); user_dpidir = NULL; } dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL); if (access(dpidrc, F_OK) == -1) { dFree(dpidrc); dpidrc = dStrdup(DPIDRC_SYS); if (access(dpidrc, F_OK) == -1) { dFree(dpidrc); dpidrc = NULL; } } if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL) sys_dpidir = NULL; if (!user_dpidir && !sys_dpidir) { ERRMSG("fill_services_list", "Fatal error ", 0); MSG_ERR("\n - Can't find the directory for dpis.\n"); exit(1); } if ((dpidrc_stream = fopen(dpidrc, "r")) == NULL) { ERRMSG("fill_services_list", "popen failed", errno); dFree(dpidrc); dFree(sys_dpidir); dFree(user_dpidir); return (-1); } if (*services_list != NULL) { ERRMSG("fill_services_list", "services_list parameter is not NULL", 0); return -1; } *services_list = dList_new(8); /* dpidrc parser loop */ for (;(line = dGetline(dpidrc_stream)) != NULL; dFree(line)) { st = dParser_parse_rc_line(&line, &service, &path); if (st < 0) { MSG_ERR("dpid: Syntax error in %s: service=\"%s\" path=\"%s\"\n", dpidrc, service, path); continue; } else if (st != 0) { continue; } _MSG("dpid: service=%s, path=%s\n", service, path); /* ignore dpi_dir silently */ if (strcmp(service, "dpi_dir") == 0) continue; s = dNew(struct service, 1); /* init services list entry */ s->name = dStrdup(service); s->dp_index = -1; dList_append(*services_list, s); /* search the dpi for a service by its path */ for (i = 0; i < numdpis; i++) if ((p = strstr(attlist[i].path, path)) && *(p - 1) == '/' && strlen(p) == strlen(path)) break; /* if the dpi exist bind service and dpi */ if (i < numdpis) s->dp_index = i; } fclose(dpidrc_stream); dList_sort(*services_list, (dCompareFunc)services_alpha_comp); dFree(dpidrc); dFree(sys_dpidir); dFree(user_dpidir); return (dList_length(*services_list)); }