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 }