links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

objreq.c (15772B)


      1 /* objreq.c
      2  * Object Requester
      3  * (c) 2002 Mikulas Patocka
      4  * This file is a part of the Links program, released under GPL.
      5  */
      6 
      7 #include "links.h"
      8 
      9 static void objreq_end(struct status *, void *);
     10 static void object_timer(void *);
     11 
     12 static struct list_head requests = { &requests, &requests };
     13 static tcount obj_req_count = 1;
     14 
     15 #define MAX_UID_LEN 256
     16 
     17 struct auth_dialog {
     18 	unsigned char uid[MAX_UID_LEN];
     19 	unsigned char passwd[MAX_UID_LEN];
     20 	unsigned char *realm;
     21 	int proxy;
     22 	tcount count;
     23 	unsigned char msg[1];
     24 };
     25 
     26 static inline struct object_request *
     27 find_rq(tcount c)
     28 {
     29 	struct object_request *rq = NULL;
     30 	struct list_head *lrq;
     31 	foreach (struct object_request, rq, lrq, requests)
     32 		if (rq->count == c)
     33 			return rq;
     34 	return NULL;
     35 }
     36 
     37 static void
     38 auth_fn(struct dialog_data *dlg)
     39 {
     40 	struct terminal *term = dlg->win->term;
     41 	struct auth_dialog *a = dlg->dlg->udata;
     42 	int max = 0, min = 0;
     43 	int w, rw;
     44 	int y = 0;
     45 	max_text_width(term, a->msg, &max, AL_LEFT);
     46 	min_text_width(term, a->msg, &min, AL_LEFT);
     47 	max_text_width(term, TEXT_(T_USERID), &max, AL_LEFT);
     48 	min_text_width(term, TEXT_(T_USERID), &min, AL_LEFT);
     49 	max_text_width(term, TEXT_(T_PASSWORD), &max, AL_LEFT);
     50 	min_text_width(term, TEXT_(T_PASSWORD), &min, AL_LEFT);
     51 	max_buttons_width(term, dlg->items + 2, 2, &max);
     52 	min_buttons_width(term, dlg->items + 2, 2, &min);
     53 	w = term->x * 9 / 10 - 2 * DIALOG_LB;
     54 	if (w > max)
     55 		w = max;
     56 	if (w < min)
     57 		w = min;
     58 	rw = w;
     59 	dlg_format_text(dlg, NULL, a->msg, 0, &y, w, &rw, COLOR_DIALOG_TEXT,
     60 	                AL_LEFT);
     61 	y++;
     62 	dlg_format_text_and_field(dlg, NULL, TEXT_(T_USERID), dlg->items, 0, &y,
     63 	                          w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
     64 	y++;
     65 	dlg_format_text_and_field(dlg, NULL, TEXT_(T_PASSWORD), dlg->items + 1,
     66 	                          0, &y, w, &rw, COLOR_DIALOG_TEXT, AL_LEFT);
     67 	y++;
     68 	dlg_format_buttons(dlg, NULL, dlg->items + 2, 2, 0, &y, w, &rw,
     69 	                   AL_CENTER);
     70 	w = rw;
     71 	dlg->xw = rw + 2 * DIALOG_LB;
     72 	dlg->yw = y + 2 * DIALOG_TB;
     73 	center_dlg(dlg);
     74 	draw_dlg(dlg);
     75 	y = dlg->y + DIALOG_TB;
     76 	y++;
     77 	dlg_format_text(dlg, term, a->msg, dlg->x + DIALOG_LB, &y, w, NULL,
     78 	                COLOR_DIALOG_TEXT, AL_LEFT);
     79 	y++;
     80 	dlg_format_text_and_field(dlg, term, TEXT_(T_USERID), dlg->items,
     81 	                          dlg->x + DIALOG_LB, &y, w, NULL,
     82 	                          COLOR_DIALOG_TEXT, AL_LEFT);
     83 	y++;
     84 	dlg_format_text_and_field(dlg, term, TEXT_(T_PASSWORD), dlg->items + 1,
     85 	                          dlg->x + DIALOG_LB, &y, w, NULL,
     86 	                          COLOR_DIALOG_TEXT, AL_LEFT);
     87 	y++;
     88 	dlg_format_buttons(dlg, term, dlg->items + 2, 2, dlg->x + DIALOG_LB, &y,
     89 	                   w, NULL, AL_CENTER);
     90 }
     91 
     92 static int
     93 auth_cancel(struct dialog_data *dlg, struct dialog_item_data *item)
     94 {
     95 	struct auth_dialog *a = dlg->dlg->udata;
     96 	struct object_request *rq = find_rq(a->count);
     97 	if (rq) {
     98 		rq->hold = 0;
     99 		rq->state = O_OK;
    100 		if (rq->timer != NULL)
    101 			kill_timer(rq->timer);
    102 		rq->timer = install_timer(0, object_timer, rq);
    103 		if (!rq->ce)
    104 			(rq->ce = rq->ce_internal)->refcount++;
    105 	}
    106 	cancel_dialog(dlg, item);
    107 	return 0;
    108 }
    109 
    110 static int
    111 auth_ok(struct dialog_data *dlg, struct dialog_item_data *item)
    112 {
    113 	struct auth_dialog *a = dlg->dlg->udata;
    114 	struct object_request *rq = find_rq(a->count);
    115 	if (rq) {
    116 		struct session *ses;
    117 		int net_cp;
    118 		unsigned char *uid, *passwd;
    119 		get_dialog_data(dlg);
    120 		ses = list_struct(dlg->win->term->windows.prev, struct window)
    121 		          ->data;
    122 		get_convert_table(rq->ce_internal->head, 0, ses->ds.assume_cp,
    123 		                  &net_cp, NULL, ses->ds.hard_assume);
    124 		uid = stracpy(a->uid);
    125 		passwd = stracpy(a->passwd);
    126 		add_auth(rq->url, a->realm, uid, passwd, a->proxy);
    127 		free(uid);
    128 		free(passwd);
    129 		rq->hold = 0;
    130 		change_connection(&rq->stat, NULL, PRI_CANCEL);
    131 		load_url(rq->url, rq->prev_url, &rq->stat, rq->pri, NC_RELOAD,
    132 		         0, 0, 0);
    133 	}
    134 	cancel_dialog(dlg, item);
    135 	return 0;
    136 }
    137 
    138 static int
    139 auth_window(struct object_request *rq, unsigned char *realm)
    140 {
    141 	unsigned char *host, *port;
    142 	struct dialog *d;
    143 	struct auth_dialog *a;
    144 	struct terminal *term;
    145 	unsigned char *urealm;
    146 	struct session *ses;
    147 	int net_cp;
    148 	if (!(term = find_terminal(rq->term)))
    149 		return -1;
    150 	ses = list_struct(term->windows.prev, struct window)->data;
    151 	get_convert_table(rq->ce_internal->head, 0, ses->ds.assume_cp, &net_cp,
    152 	                  NULL, ses->ds.hard_assume);
    153 	if (rq->ce_internal->http_code == 407) {
    154 		unsigned char *h = get_proxy_string(rq->url);
    155 		if (!h)
    156 			h = cast_uchar "";
    157 		host = display_host(term, h);
    158 	} else {
    159 		unsigned char *h = get_host_name(rq->url);
    160 		if (!h)
    161 			return -1;
    162 		host = display_host(term, h);
    163 		free(h);
    164 		if ((port = get_port_str(rq->url))) {
    165 			add_to_strn(&host, cast_uchar ":");
    166 			add_to_strn(&host, port);
    167 			free(port);
    168 		}
    169 	}
    170 	urealm = stracpy(realm);
    171 	d = xmalloc(
    172 	    sizeof(struct dialog) + 5 * sizeof(struct dialog_item)
    173 	    + sizeof(struct auth_dialog)
    174 	    + strlen(cast_const_char get_text_translation(
    175 		TEXT_(T_ENTER_USERNAME), term))
    176 	    + strlen(cast_const_char urealm) + 1
    177 	    + strlen(cast_const_char get_text_translation(TEXT_(T_AT), term))
    178 	    + strlen(cast_const_char host));
    179 	memset(d, 0,
    180 	       sizeof(struct dialog) + 5 * sizeof(struct dialog_item)
    181 	           + sizeof(struct auth_dialog));
    182 	a = (struct auth_dialog *)((unsigned char *)d + sizeof(struct dialog)
    183 	                           + 5 * sizeof(struct dialog_item));
    184 	strcpy(cast_char a->msg, cast_const_char get_text_translation(
    185 				     TEXT_(T_ENTER_USERNAME), term));
    186 	strcat(cast_char a->msg, cast_const_char urealm);
    187 	if (*host) {
    188 		strcat(cast_char a->msg, "\n");
    189 		strcat(cast_char a->msg,
    190 		       cast_const_char get_text_translation(TEXT_(T_AT), term));
    191 		strcat(cast_char a->msg, cast_const_char host);
    192 	}
    193 	free(host);
    194 	free(urealm);
    195 	a->proxy = rq->ce_internal->http_code == 407;
    196 	a->realm = stracpy(realm);
    197 	a->count = rq->count;
    198 	d->udata = a;
    199 	if (rq->ce_internal->http_code == 401)
    200 		d->title = TEXT_(T_AUTHORIZATION_REQUIRED);
    201 	else
    202 		d->title = TEXT_(T_PROXY_AUTHORIZATION_REQUIRED);
    203 	d->fn = auth_fn;
    204 	d->items[0].type = D_FIELD;
    205 	d->items[0].dlen = MAX_UID_LEN;
    206 	d->items[0].data = a->uid;
    207 
    208 	d->items[1].type = D_FIELD_PASS;
    209 	d->items[1].dlen = MAX_UID_LEN;
    210 	d->items[1].data = a->passwd;
    211 
    212 	d->items[2].type = D_BUTTON;
    213 	d->items[2].gid = B_ENTER;
    214 	d->items[2].fn = auth_ok;
    215 	d->items[2].text = TEXT_(T_OK);
    216 
    217 	d->items[3].type = D_BUTTON;
    218 	d->items[3].gid = B_ESC;
    219 	d->items[3].fn = auth_cancel;
    220 	d->items[3].text = TEXT_(T_CANCEL);
    221 
    222 	do_dialog(term, d, getml(d, a->realm, NULL));
    223 	return 0;
    224 }
    225 
    226 struct cert_dialog {
    227 	tcount term;
    228 	unsigned char *host;
    229 	int bl;
    230 	int state;
    231 };
    232 
    233 static void
    234 cert_action(struct object_request *rq, int yes)
    235 {
    236 	if (yes > 0) {
    237 		rq->hold = 0;
    238 		change_connection(&rq->stat, NULL, PRI_CANCEL);
    239 		load_url(rq->url, rq->prev_url, &rq->stat, rq->pri, NC_CACHE, 0,
    240 		         0, 0);
    241 	} else {
    242 		rq->hold = 0;
    243 		rq->dont_print_error = 1;
    244 		rq->state = O_FAILED;
    245 		if (rq->timer != NULL)
    246 			kill_timer(rq->timer);
    247 		rq->timer = install_timer(0, object_timer, rq);
    248 	}
    249 }
    250 
    251 static void
    252 cert_forall(struct cert_dialog *cs, int yes)
    253 {
    254 	struct object_request *rq = NULL;
    255 	struct list_head *lrq;
    256 	if (yes > 0) {
    257 		add_blacklist_entry(cs->host, cs->bl);
    258 		del_blacklist_entry(cs->host, BL_AVOID_INSECURE);
    259 	}
    260 	if (yes < 0) {
    261 		add_blacklist_entry(cs->host, BL_AVOID_INSECURE);
    262 		del_blacklist_entry(cs->host, BL_IGNORE_CERTIFICATE);
    263 		del_blacklist_entry(cs->host, BL_IGNORE_DOWNGRADE);
    264 		del_blacklist_entry(cs->host, BL_IGNORE_CIPHER);
    265 	}
    266 	foreach (struct object_request, rq, lrq, requests)
    267 		if (rq->term == cs->term && rq->hold == HOLD_CERT
    268 		    && rq->stat.state == cs->state) {
    269 			unsigned char *host = get_host_name(rq->url);
    270 			if (!strcmp(cast_const_char host,
    271 			            cast_const_char cs->host))
    272 				cert_action(rq, yes);
    273 			free(host);
    274 		}
    275 }
    276 
    277 static void
    278 cert_yes(void *data)
    279 {
    280 	cert_forall((struct cert_dialog *)data, 1);
    281 }
    282 
    283 static void
    284 cert_no(void *data)
    285 {
    286 	cert_forall((struct cert_dialog *)data, 0);
    287 }
    288 
    289 static void
    290 cert_never(void *data)
    291 {
    292 	cert_forall((struct cert_dialog *)data, -1);
    293 }
    294 
    295 static int
    296 cert_compare(void *data1, void *data2)
    297 {
    298 	struct cert_dialog *cs1 = (struct cert_dialog *)data1;
    299 	struct cert_dialog *cs2 = (struct cert_dialog *)data2;
    300 	return !strcmp(cast_const_char cs1->host, cast_const_char cs2->host)
    301 	       && cs1->state == cs2->state;
    302 }
    303 
    304 static int
    305 cert_window(struct object_request *rq)
    306 {
    307 	struct terminal *term;
    308 	unsigned char *h, *host, *title, *text;
    309 	struct cert_dialog *cs;
    310 	struct memory_list *ml;
    311 	if (!(term = find_terminal(rq->term)))
    312 		return -1;
    313 	h = get_host_name(rq->url);
    314 	if (get_blacklist_flags(h) & BL_AVOID_INSECURE) {
    315 		free(h);
    316 		return -1;
    317 	}
    318 	cs = xmalloc(sizeof(struct cert_dialog));
    319 	cs->term = rq->term;
    320 	cs->host = h;
    321 	cs->state = rq->stat.state;
    322 	if (rq->stat.state == S_INVALID_CERTIFICATE) {
    323 		title = TEXT_(T_INVALID_CERTIFICATE);
    324 		text = TEXT_(T_DOESNT_HAVE_A_VALID_CERTIFICATE);
    325 		cs->bl = BL_IGNORE_CERTIFICATE;
    326 	} else if (rq->stat.state == S_DOWNGRADED_METHOD) {
    327 		title = TEXT_(T_DOWNGRADED_METHOD);
    328 		text = TEXT_(T_USES_DOWNGRADED_METHOD);
    329 		cs->bl = BL_IGNORE_DOWNGRADE;
    330 	} else {
    331 		title = TEXT_(T_INSECURE_CIPHER);
    332 		text = TEXT_(T_USES_INSECURE_CIPHER);
    333 		cs->bl = BL_IGNORE_CIPHER;
    334 	}
    335 	host = display_host(term, h);
    336 	ml = getml(cs, h, host, NULL);
    337 	if (find_msg_box(term, title, cert_compare, cs)) {
    338 		freeml(ml);
    339 		return 0;
    340 	}
    341 	msg_box(term, ml, title, AL_CENTER, TEXT_(T_THE_SERVER_), host, text,
    342 	        MSG_BOX_END, (void *)cs, 3, TEXT_(T_NO), cert_no, B_ESC,
    343 	        TEXT_(T_YES), cert_yes, B_ENTER, TEXT_(T_NEVER), cert_never, 0);
    344 	return 0;
    345 }
    346 
    347 /* prev_url is a pointer to previous url or NULL */
    348 /* prev_url will NOT be deallocated */
    349 void
    350 request_object(struct terminal *term, unsigned char *url,
    351                unsigned char *prev_url, int pri, int cache, int allow_flags,
    352                void (*upcall)(struct object_request *, void *), void *data,
    353                struct object_request **rqp)
    354 {
    355 	struct object_request *rq;
    356 	rq = mem_calloc(sizeof(struct object_request));
    357 	rq->state = O_WAITING;
    358 	rq->refcount = 1;
    359 	rq->term = term ? term->count : 0;
    360 	rq->stat.end = objreq_end;
    361 	rq->stat.data = rq;
    362 	rq->orig_url = stracpy(url);
    363 	rq->url = stracpy(url);
    364 	rq->pri = pri;
    365 	rq->cache = cache;
    366 	rq->upcall = upcall;
    367 	rq->data = data;
    368 	rq->timer = NULL;
    369 	rq->last_update = get_time() - STAT_UPDATE_MAX;
    370 	free(rq->prev_url);
    371 	rq->prev_url = stracpy(prev_url);
    372 	if (rqp)
    373 		*rqp = rq;
    374 	rq->count = obj_req_count++;
    375 	add_to_list(requests, rq);
    376 	load_url(url, prev_url, &rq->stat, pri, cache, 0, allow_flags, 0);
    377 }
    378 
    379 static void
    380 set_ce_internal(struct object_request *rq)
    381 {
    382 	if (rq->stat.ce != rq->ce_internal) {
    383 		if (!rq->stat.ce) {
    384 			rq->ce_internal->refcount--;
    385 			rq->ce_internal = NULL;
    386 		} else {
    387 			if (rq->ce_internal)
    388 				rq->ce_internal->refcount--;
    389 			rq->ce_internal = rq->stat.ce;
    390 			rq->ce_internal->refcount++;
    391 		}
    392 	}
    393 }
    394 
    395 static void
    396 objreq_end(struct status *stat, void *data)
    397 {
    398 	struct object_request *rq = (struct object_request *)data;
    399 
    400 	set_ce_internal(rq);
    401 
    402 	if (stat->state < 0) {
    403 		if (!stat->ce && rq->state == O_WAITING
    404 		    && (stat->state == S_INVALID_CERTIFICATE
    405 		        || stat->state == S_DOWNGRADED_METHOD
    406 		        || stat->state == S_INSECURE_CIPHER)
    407 		    && ssl_options.certificates
    408 		           == SSL_WARN_ON_INVALID_CERTIFICATE) {
    409 			if (!cert_window(rq)) {
    410 				rq->hold = HOLD_CERT;
    411 				rq->redirect_cnt = 0;
    412 				goto tm;
    413 			}
    414 		}
    415 		if (stat->ce && rq->state == O_WAITING && stat->ce->redirect) {
    416 			if (rq->redirect_cnt++ < MAX_REDIRECTS) {
    417 				int cache, allow_flags;
    418 				unsigned char *u, *pos;
    419 				change_connection(stat, NULL, PRI_CANCEL);
    420 				u = join_urls(rq->url, stat->ce->redirect);
    421 				if ((pos = extract_position(u))) {
    422 					free(rq->goto_position);
    423 					rq->goto_position = pos;
    424 				}
    425 				cache = rq->cache;
    426 				if (cache < NC_RELOAD
    427 				    && (!strcmp(cast_const_char u,
    428 				                cast_const_char rq->url)
    429 				        || !strcmp(cast_const_char u,
    430 				                   cast_const_char rq->orig_url)
    431 				        || rq->redirect_cnt
    432 				               >= MAX_CACHED_REDIRECTS))
    433 					cache = NC_RELOAD;
    434 				allow_flags = get_allow_flags(rq->url);
    435 				free(rq->url);
    436 				rq->url = u;
    437 				load_url(u, rq->prev_url, &rq->stat, rq->pri,
    438 				         cache, 0, allow_flags, 0);
    439 				return;
    440 			} else {
    441 maxrd:
    442 				rq->stat.state = S_CYCLIC_REDIRECT;
    443 			}
    444 		}
    445 		if (stat->ce && rq->state == O_WAITING
    446 		    && (stat->ce->http_code == 401
    447 		        || stat->ce->http_code == 407)) {
    448 			unsigned char *realm =
    449 			    get_auth_realm(rq->url, stat->ce->head,
    450 			                   stat->ce->http_code == 407);
    451 			unsigned char *user;
    452 			if (!realm)
    453 				goto xx;
    454 			if (stat->ce->http_code == 401
    455 			    && !find_auth(rq->url, realm)) {
    456 				free(realm);
    457 				if (rq->redirect_cnt++ >= MAX_REDIRECTS)
    458 					goto maxrd;
    459 				change_connection(stat, NULL, PRI_CANCEL);
    460 				load_url(rq->url, rq->prev_url, &rq->stat,
    461 				         rq->pri, NC_RELOAD, 0, 0, 0);
    462 				return;
    463 			}
    464 			user = get_user_name(rq->url);
    465 			if (stat->ce->http_code == 401 && *user) {
    466 				free(user);
    467 				free(realm);
    468 				goto xx;
    469 			}
    470 			free(user);
    471 			if (!auth_window(rq, realm)) {
    472 				rq->hold = HOLD_AUTH;
    473 				rq->redirect_cnt = 0;
    474 				free(realm);
    475 				goto tm;
    476 			}
    477 			free(realm);
    478 			goto xx;
    479 		}
    480 	}
    481 	if ((stat->state < 0 || stat->state == S_TRANS) && stat->ce
    482 	    && !stat->ce->redirect && stat->ce->http_code != 401
    483 	    && stat->ce->http_code != 407) {
    484 		rq->state = O_LOADING;
    485 		if (0) {
    486 xx:
    487 			rq->state = O_OK;
    488 		}
    489 		if (!rq->ce)
    490 			(rq->ce = stat->ce)->refcount++;
    491 	}
    492 tm:
    493 	if (rq->timer != NULL)
    494 		kill_timer(rq->timer);
    495 	rq->timer = install_timer(0, object_timer, rq);
    496 }
    497 
    498 static void
    499 object_timer(void *rq_)
    500 {
    501 	struct object_request *rq = (struct object_request *)rq_;
    502 	off_t last;
    503 
    504 	rq->timer = NULL;
    505 
    506 	set_ce_internal(rq);
    507 
    508 	last = rq->last_bytes;
    509 	if (rq->ce)
    510 		rq->last_bytes = rq->ce->length;
    511 	if (rq->stat.state < 0 && !rq->hold
    512 	    && (!rq->ce_internal || !rq->ce_internal->redirect
    513 	        || rq->stat.state == S_CYCLIC_REDIRECT)) {
    514 		if (rq->ce_internal && rq->stat.state != S_CYCLIC_REDIRECT) {
    515 			rq->state =
    516 			    rq->stat.state != S__OK ? O_INCOMPLETE : O_OK;
    517 		} else
    518 			rq->state = O_FAILED;
    519 	}
    520 	if (rq->stat.state != S_TRANS) {
    521 		if (rq->stat.state >= 0)
    522 			rq->timer =
    523 			    install_timer(STAT_UPDATE_MAX, object_timer, rq);
    524 		rq->last_update = get_time() - STAT_UPDATE_MAX;
    525 		if (rq->upcall)
    526 			rq->upcall(rq, rq->data);
    527 	} else {
    528 		uttime ct = get_time();
    529 		uttime t = ct - rq->last_update;
    530 		rq->timer = install_timer(STAT_UPDATE_MIN, object_timer, rq);
    531 		if (t >= STAT_UPDATE_MAX
    532 		    || (t >= STAT_UPDATE_MIN && rq->ce
    533 		        && rq->last_bytes > last)) {
    534 			rq->last_update = ct;
    535 			if (rq->upcall)
    536 				rq->upcall(rq, rq->data);
    537 		}
    538 	}
    539 }
    540 
    541 void
    542 release_object_get_stat(struct object_request **rqq, struct status *news,
    543                         int pri)
    544 {
    545 	struct object_request *rq = *rqq;
    546 	if (!rq)
    547 		return;
    548 	*rqq = NULL;
    549 	if (--rq->refcount)
    550 		return;
    551 	change_connection(&rq->stat, news, pri);
    552 	if (rq->timer != NULL)
    553 		kill_timer(rq->timer);
    554 	if (rq->ce_internal)
    555 		rq->ce_internal->refcount--;
    556 	if (rq->ce)
    557 		rq->ce->refcount--;
    558 	free(rq->orig_url);
    559 	free(rq->url);
    560 	free(rq->prev_url);
    561 	free(rq->goto_position);
    562 	del_from_list(rq);
    563 	free(rq);
    564 }
    565 
    566 void
    567 release_object(struct object_request **rqq)
    568 {
    569 	release_object_get_stat(rqq, NULL, PRI_CANCEL);
    570 }
    571 
    572 void
    573 detach_object_connection(struct object_request *rq, off_t pos)
    574 {
    575 	if (rq->state == O_WAITING || rq->state == O_FAILED) {
    576 		internal("detach_object_connection: no data received");
    577 		return;
    578 	}
    579 	if (rq->refcount == 1) {
    580 		detach_connection(&rq->stat, pos, 0, 1);
    581 	}
    582 }
    583 
    584 void
    585 clone_object(struct object_request *rq, struct object_request **rqq)
    586 {
    587 	(*rqq = rq)->refcount++;
    588 }