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