/* * Add a client to ClientQueue. * - Every client-field is just a reference (except 'Web'). * - Return a unique number for identifying the client. */ static int Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web, CA_Callback_t Callback, void *CbData) { static int ClientKey = 0; /* Provide a primary key for each client */ CacheClient_t *NewClient; if (ClientKey < INT_MAX) /* check for integer overflow */ ClientKey++; else ClientKey = 1; NewClient = dNew(CacheClient_t, 1); NewClient->Key = ClientKey; NewClient->Url = Url; NewClient->Version = 0; NewClient->Buf = NULL; NewClient->BufSize = 0; NewClient->Callback = Callback; NewClient->CbData = CbData; NewClient->Web = Web; dList_append(ClientQueue, NewClient); return ClientKey; }
/* * Create a new bitvec with 'num_bits' size */ bitvec_t *a_Bitvec_new(int num_bits) { bitvec_t *bvec = dNew(bitvec_t, 1); bvec->vec = dNew0(uchar_t, num_bits/BVEC_SIZE + 1); bvec->len = num_bits; return bvec; }
static AuthParse_t *Auth_parse_new() { AuthParse_t *auth_parse = dNew(AuthParse_t, 1); auth_parse->ok = 0; auth_parse->type = TYPENOTSET; auth_parse->realm = NULL; auth_parse->nonce = NULL; auth_parse->opaque = NULL; auth_parse->stale = 0; auth_parse->algorithm = ALGORITHMNOTSET; auth_parse->domain = NULL; auth_parse->qop = QOPNOTSET; return auth_parse; }
static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock) { SocketQueueEntry_t *se = dNew(SocketQueueEntry_t, 1); se->sock = sock; se->next = NULL; if (sq->tail) sq->tail->next = se; sq->tail = se; if (! sq->head) sq->head = se; }
/* * Allocate and set a new entry in the cache list */ static CacheEntry_t *Cache_entry_add(const DilloUrl *Url) { CacheEntry_t *old_entry, *new_entry; if ((old_entry = Cache_entry_search(Url))) { MSG_WARN("Cache_entry_add, leaking an entry.\n"); dList_remove(CachedURLs, old_entry); } new_entry = dNew(CacheEntry_t, 1); Cache_entry_init(new_entry, Url); /* Set safe values */ dList_insert_sorted(CachedURLs, new_entry, Cache_entry_cmp); return new_entry; }
/* * Create a new connection data structure */ static capi_conn_t * Capi_conn_new(DilloUrl *url, void *bw, char *server, char *datastr) { capi_conn_t *conn; conn = dNew(capi_conn_t, 1); conn->url = url ? a_Url_dup(url) : NULL; conn->bw = bw; conn->server = dStrdup(server); conn->datastr = dStrdup(datastr); conn->SockFD = -1; conn->Flags = (strcmp(server, "http") != 0) ? PENDING : 0; conn->InfoSend = NULL; conn->InfoRecv = NULL; conn->Ref = 0; /* Reference count */ return conn; }
/* * Create and initialize a dpip socket handler */ Dsh *a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz) { Dsh *dsh = dNew(Dsh, 1); /* init descriptors and streams */ dsh->fd_in = fd_in; dsh->fd_out = fd_out; /* init buffer */ dsh->wrbuf = dStr_sized_new(8 *1024); dsh->rdbuf = dStr_sized_new(8 *1024); dsh->flush_sz = flush_sz; dsh->mode = DPIP_TAG; if (fcntl(dsh->fd_in, F_GETFL) & O_NONBLOCK) dsh->mode |= DPIP_NONBLOCK; dsh->status = 0; return dsh; }
/* * Set a timeout function to ask for user/password. */ static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw) { static int busy = 0; CacheAuthData_t *data; if (!entry) { busy = 0; } else if (busy) { MSG_WARN("Cache_auth_entry: caught busy!\n"); } else if (entry->Auth) { busy = 1; data = dNew(CacheAuthData_t, 1); data->auth = entry->Auth; data->url = a_Url_dup(entry->Url); data->bw = bw; entry->Auth = NULL; a_Timeout_add(0.0, Cache_auth_callback, data); } }
/* * Return: Nonzero if we got new credentials from the user and everything * seems fine. */ static int Auth_do_auth_dialog(const AuthParse_t *auth_parse, const DilloUrl *url) { int ret; char *title, *msg; AuthDialogData_t *data; const char *typestr = auth_parse->type == DIGEST ? "Digest" : "Basic"; _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", auth_parse->realm); title = dStrconcat("Dillo: Password for ", auth_parse->realm, NULL); msg = dStrconcat("The server at ", URL_HOST(url), " requires a username" " and password for \"", auth_parse->realm, "\".\n\n" "Authentication scheme: ", typestr, NULL); data = dNew(AuthDialogData_t, 1); data->auth_parse = auth_parse; data->url = a_Url_dup(url); ret = a_Dialog_user_password(title, msg, Auth_do_auth_dialog_cb, data); dFree(title); dFree(msg); a_Url_free((void *)data->url); dFree(data); return ret; }
static void Auth_do_auth_dialog_cb(const char *user, const char *password, void *vData) { AuthDialogData_t *data; AuthHost_t *host; AuthRealm_t *realm; data = (AuthDialogData_t *)vData; /* find or create the host */ if (!(host = Auth_host_by_url(data->url))) { /* create a new host */ host = dNew(AuthHost_t, 1); host->scheme = dStrdup(URL_SCHEME(data->url)); host->authority = dStrdup(URL_AUTHORITY(data->url)); host->realms = dList_new(1); dList_append(auth_hosts, host); } /* find or create the realm */ if (!(realm = Auth_realm_by_name(host, data->auth_parse->realm))) { realm = dNew0(AuthRealm_t, 1); realm->name = dStrdup(data->auth_parse->realm); realm->paths = dList_new(1); dList_append(host->realms, realm); } realm->type = data->auth_parse->type; dFree(realm->authorization); realm->authorization = NULL; Auth_realm_add_path(realm, URL_PATH(data->url)); if (realm->type == BASIC) { char *user_password = dStrconcat(user, ":", password, NULL); char *response = a_Misc_encode_base64(user_password); char *authorization = dStrconcat("Authorization: Basic ", response, "\r\n", NULL); dFree(realm->authorization); realm->authorization = authorization; dFree(response); dStrshred(user_password); dFree(user_password); } else if (realm->type == DIGEST) { dFree(realm->username); realm->username = dStrdup(user); realm->nonce_count = 0; dFree(realm->nonce); realm->nonce = dStrdup(data->auth_parse->nonce); dFree(realm->opaque); realm->opaque = dStrdup(data->auth_parse->opaque); realm->algorithm = data->auth_parse->algorithm; dFree(realm->domain); realm->domain = dStrdup(data->auth_parse->domain); realm->qop = data->auth_parse->qop; dFree(realm->cnonce); if (realm->qop != QOPNOTSET) realm->cnonce = a_Digest_create_cnonce(); if (!a_Digest_compute_digest(realm, user, password)) { MSG("Auth_do_auth_dialog_cb: a_Digest_compute_digest failed.\n"); dList_remove_fast(host->realms, realm); Auth_realm_delete(realm); } } else { MSG("Auth_do_auth_dialog_cb: Unknown auth type: %i\n", realm->type); } dStrshred((char *)password); }
/* * 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; }