comparison src/c/urweb.c @ 1094:db52c32dbe42

All three current protocols work with move to using uw_app
author Adam Chlipala <adamc@hcoop.net>
date Sun, 27 Dec 2009 10:37:24 -0500
parents f1647f16097d
children 72670131dace
comparison
equal deleted inserted replaced
1093:8d3aa6c7cee0 1094:db52c32dbe42
1 #define _XOPEN_SOURCE 500 1 #define _XOPEN_SOURCE 600
2 2
3 #include <stdlib.h> 3 #include <stdlib.h>
4 #include <stdio.h> 4 #include <stdio.h>
5 #include <string.h> 5 #include <string.h>
6 #include <strings.h> 6 #include <strings.h>
7 #include <ctype.h> 7 #include <ctype.h>
8 #include <setjmp.h> 8 #include <setjmp.h>
9 #include <stdarg.h> 9 #include <stdarg.h>
10 #include <assert.h> 10 #include <assert.h>
11 #include <ctype.h> 11 #include <ctype.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
12 14
13 #include <pthread.h> 15 #include <pthread.h>
14 16
15 #include "types.h" 17 #include "types.h"
16 18
285 } 287 }
286 288
287 289
288 // Global entry points 290 // Global entry points
289 291
290 extern void uw_client_init();
291
292 void uw_global_init() { 292 void uw_global_init() {
293 srand(time(NULL) ^ getpid()); 293 srand(time(NULL) ^ getpid());
294 294
295 clients = malloc(0); 295 clients = malloc(0);
296 296 }
297 uw_client_init(); 297
298 void uw_app_init(uw_app *app) {
299 app->client_init();
298 } 300 }
299 301
300 302
301 // Single-request state 303 // Single-request state
302 304
347 void *data; 349 void *data;
348 void (*free)(void*); 350 void (*free)(void*);
349 } global; 351 } global;
350 352
351 struct uw_context { 353 struct uw_context {
354 uw_app *app;
355
352 char *(*get_header)(void *, const char *); 356 char *(*get_header)(void *, const char *);
353 void *get_header_data; 357 void *get_header_data;
354 358
355 buf outHeaders, page, heap, script; 359 buf outHeaders, page, heap, script;
356 int returning_indirectly; 360 int returning_indirectly;
357 input *inputs, *subinputs, *cur_container; 361 input *inputs, *subinputs, *cur_container;
358 size_t n_subinputs, used_subinputs; 362 size_t sz_inputs, n_subinputs, used_subinputs;
359 363
360 int source_count; 364 int source_count;
361 365
362 void *db; 366 void *db;
363 367
365 369
366 regions *regions; 370 regions *regions;
367 371
368 cleanup *cleanup, *cleanup_front, *cleanup_back; 372 cleanup *cleanup, *cleanup_front, *cleanup_back;
369 373
370 const char *script_header, *url_prefix; 374 const char *script_header;
371 375
372 int needs_push, needs_sig; 376 int needs_push, needs_sig;
373 377
374 size_t n_deltas, used_deltas; 378 size_t n_deltas, used_deltas;
375 delta *deltas; 379 delta *deltas;
376 380
377 int timeout;
378
379 client *client; 381 client *client;
380 382
381 transactional *transactionals; 383 transactional *transactionals;
382 size_t n_transactionals, used_transactionals; 384 size_t n_transactionals, used_transactionals;
383 385
387 char *current_url; 389 char *current_url;
388 390
389 char error_message[ERROR_BUF_LEN]; 391 char error_message[ERROR_BUF_LEN];
390 }; 392 };
391 393
392 extern int uw_inputs_len, uw_timeout;
393
394 uw_context uw_init() { 394 uw_context uw_init() {
395 uw_context ctx = malloc(sizeof(struct uw_context)); 395 uw_context ctx = malloc(sizeof(struct uw_context));
396
397 ctx->app = NULL;
396 398
397 ctx->get_header = NULL; 399 ctx->get_header = NULL;
398 ctx->get_header_data = NULL; 400 ctx->get_header_data = NULL;
399 401
400 buf_init(&ctx->outHeaders, 0); 402 buf_init(&ctx->outHeaders, 0);
402 ctx->returning_indirectly = 0; 404 ctx->returning_indirectly = 0;
403 buf_init(&ctx->heap, 0); 405 buf_init(&ctx->heap, 0);
404 buf_init(&ctx->script, 1); 406 buf_init(&ctx->script, 1);
405 ctx->script.start[0] = 0; 407 ctx->script.start[0] = 0;
406 408
407 ctx->inputs = calloc(uw_inputs_len, sizeof(input)); 409 ctx->inputs = malloc(0);
408 ctx->cur_container = NULL; 410 ctx->cur_container = NULL;
409 ctx->subinputs = malloc(0); 411 ctx->subinputs = malloc(0);
410 ctx->n_subinputs = ctx->used_subinputs = 0; 412 ctx->sz_inputs = ctx->n_subinputs = ctx->used_subinputs = 0;
411 413
412 ctx->db = NULL; 414 ctx->db = NULL;
413 415
414 ctx->regions = NULL; 416 ctx->regions = NULL;
415 417
416 ctx->cleanup_front = ctx->cleanup_back = ctx->cleanup = malloc(0); 418 ctx->cleanup_front = ctx->cleanup_back = ctx->cleanup = malloc(0);
417 419
418 ctx->script_header = ""; 420 ctx->script_header = "";
419 ctx->url_prefix = "/";
420 ctx->needs_push = 0; 421 ctx->needs_push = 0;
421 ctx->needs_sig = 0; 422 ctx->needs_sig = 0;
422 423
423 ctx->error_message[0] = 0; 424 ctx->error_message[0] = 0;
424 425
425 ctx->source_count = 0; 426 ctx->source_count = 0;
426 427
427 ctx->n_deltas = ctx->used_deltas = 0; 428 ctx->n_deltas = ctx->used_deltas = 0;
428 ctx->deltas = malloc(0); 429 ctx->deltas = malloc(0);
429 430
430 ctx->timeout = uw_timeout;
431
432 ctx->client = NULL; 431 ctx->client = NULL;
433 432
434 ctx->error_message[0] = 0; 433 ctx->error_message[0] = 0;
435 434
436 ctx->transactionals = malloc(0); 435 ctx->transactionals = malloc(0);
440 ctx->n_globals = 0; 439 ctx->n_globals = 0;
441 440
442 ctx->current_url = ""; 441 ctx->current_url = "";
443 442
444 return ctx; 443 return ctx;
444 }
445
446 void uw_set_app(uw_context ctx, uw_app *app) {
447 ctx->app = app;
448
449 if (app->inputs_len > ctx->sz_inputs) {
450 ctx->sz_inputs = app->inputs_len;
451 ctx->inputs = realloc(ctx->inputs, ctx->sz_inputs * sizeof(input));
452 memset(ctx->inputs, 0, ctx->sz_inputs * sizeof(input));
453 }
445 } 454 }
446 455
447 void uw_set_db(uw_context ctx, void *db) { 456 void uw_set_db(uw_context ctx, void *db) {
448 ctx->db = db; 457 ctx->db = db;
449 } 458 }
497 ctx->error_message[0] = 0; 506 ctx->error_message[0] = 0;
498 } 507 }
499 508
500 void uw_reset(uw_context ctx) { 509 void uw_reset(uw_context ctx) {
501 uw_reset_keep_request(ctx); 510 uw_reset_keep_request(ctx);
502 memset(ctx->inputs, 0, uw_inputs_len * sizeof(input)); 511 memset(ctx->inputs, 0, ctx->app->inputs_len * sizeof(input));
503 memset(ctx->subinputs, 0, ctx->n_subinputs * sizeof(input)); 512 memset(ctx->subinputs, 0, ctx->n_subinputs * sizeof(input));
504 ctx->used_subinputs = 0; 513 ctx->used_subinputs = 0;
505 } 514 }
506 515
507 void uw_db_init(uw_context);
508 void uw_handle(uw_context, char *);
509
510 failure_kind uw_begin_init(uw_context ctx) { 516 failure_kind uw_begin_init(uw_context ctx) {
511 int r = setjmp(ctx->jmp_buf); 517 int r = setjmp(ctx->jmp_buf);
512 518
513 if (r == 0) 519 if (r == 0)
514 uw_db_init(ctx); 520 ctx->app->db_init(ctx);
515 521
516 return r; 522 return r;
517 } 523 }
518 524
519 void uw_set_headers(uw_context ctx, char *(*get_header)(void *, const char *), void *get_header_data) { 525 void uw_set_headers(uw_context ctx, char *(*get_header)(void *, const char *), void *get_header_data) {
520 ctx->get_header = get_header; 526 ctx->get_header = get_header;
521 ctx->get_header_data = get_header_data; 527 ctx->get_header_data = get_header_data;
522 } 528 }
523
524 int uw_db_begin(uw_context);
525 529
526 static void uw_set_error(uw_context ctx, const char *fmt, ...) { 530 static void uw_set_error(uw_context ctx, const char *fmt, ...) {
527 va_list ap; 531 va_list ap;
528 va_start(ap, fmt); 532 va_start(ap, fmt);
529 533
598 602
599 failure_kind uw_begin(uw_context ctx, char *path) { 603 failure_kind uw_begin(uw_context ctx, char *path) {
600 int r = setjmp(ctx->jmp_buf); 604 int r = setjmp(ctx->jmp_buf);
601 605
602 if (r == 0) { 606 if (r == 0) {
603 if (uw_db_begin(ctx)) 607 if (ctx->app->db_begin(ctx))
604 uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN"); 608 uw_error(ctx, BOUNDED_RETRY, "Error running SQL BEGIN");
605 609
606 uw_handle(ctx, path); 610 ctx->app->handle(ctx, path);
607 } 611 }
608 612
609 return r; 613 return r;
610 } 614 }
611 615
625 } 629 }
626 630
627 char *uw_error_message(uw_context ctx) { 631 char *uw_error_message(uw_context ctx) {
628 return ctx->error_message; 632 return ctx->error_message;
629 } 633 }
630
631 extern int uw_input_num(const char*);
632 634
633 static input *INP(uw_context ctx) { 635 static input *INP(uw_context ctx) {
634 if (ctx->cur_container == NULL) 636 if (ctx->cur_container == NULL)
635 return ctx->inputs; 637 return ctx->inputs;
636 else if (ctx->cur_container->kind == SUBFORM) 638 else if (ctx->cur_container->kind == SUBFORM)
672 size_t offset = new_subinputs - ctx->subinputs; 674 size_t offset = new_subinputs - ctx->subinputs;
673 675
674 if (ctx->subinputs != new_subinputs) { 676 if (ctx->subinputs != new_subinputs) {
675 for (i = 0; i < ctx->used_subinputs; ++i) 677 for (i = 0; i < ctx->used_subinputs; ++i)
676 adjust_input(&new_subinputs[i], ctx->subinputs, new_subinputs, ctx->used_subinputs); 678 adjust_input(&new_subinputs[i], ctx->subinputs, new_subinputs, ctx->used_subinputs);
677 for (i = 0; i < uw_inputs_len; ++i) 679 for (i = 0; i < ctx->app->inputs_len; ++i)
678 adjust_input(&ctx->inputs[i], ctx->subinputs, new_subinputs, ctx->used_subinputs); 680 adjust_input(&ctx->inputs[i], ctx->subinputs, new_subinputs, ctx->used_subinputs);
679 681
680 adjust_pointer(&ctx->cur_container, ctx->subinputs, new_subinputs, ctx->used_subinputs); 682 adjust_pointer(&ctx->cur_container, ctx->subinputs, new_subinputs, ctx->used_subinputs);
681 683
682 ctx->n_subinputs = ctx->used_subinputs + len; 684 ctx->n_subinputs = ctx->used_subinputs + len;
694 696
695 int uw_set_input(uw_context ctx, const char *name, char *value) { 697 int uw_set_input(uw_context ctx, const char *name, char *value) {
696 //printf("Input name %s\n", name); 698 //printf("Input name %s\n", name);
697 699
698 if (!strcasecmp(name, ".b")) { 700 if (!strcasecmp(name, ".b")) {
699 int n = uw_input_num(value); 701 int n = ctx->app->input_num(value);
700 input *inps; 702 input *inps;
701 703
702 if (n < 0) { 704 if (n < 0) {
703 uw_set_error(ctx, "Bad subform name %s", value); 705 uw_set_error(ctx, "Bad subform name %s", value);
704 return -1; 706 return -1;
705 } 707 }
706 708
707 if (n >= uw_inputs_len) { 709 if (n >= ctx->app->inputs_len) {
708 uw_set_error(ctx, "For subform name %s, index %d is out of range", value, n); 710 uw_set_error(ctx, "For subform name %s, index %d is out of range", value, n);
709 return -1; 711 return -1;
710 } 712 }
711 713
712 inps = check_input_space(ctx, uw_inputs_len); 714 inps = check_input_space(ctx, ctx->app->inputs_len);
713 715
714 INP(ctx)[n].kind = SUBFORM; 716 INP(ctx)[n].kind = SUBFORM;
715 INP(ctx)[n].data.subform.parent = ctx->cur_container; 717 INP(ctx)[n].data.subform.parent = ctx->cur_container;
716 INP(ctx)[n].data.subform.fields = inps; 718 INP(ctx)[n].data.subform.fields = inps;
717 ctx->cur_container = &INP(ctx)[n]; 719 ctx->cur_container = &INP(ctx)[n];
739 default: 741 default:
740 uw_set_error(ctx, "uw_set_input: Wrong kind"); 742 uw_set_error(ctx, "uw_set_input: Wrong kind");
741 return -1; 743 return -1;
742 } 744 }
743 } else if (!strcasecmp(name, ".s")) { 745 } else if (!strcasecmp(name, ".s")) {
744 int n = uw_input_num(value); 746 int n = ctx->app->input_num(value);
745 747
746 if (n < 0) { 748 if (n < 0) {
747 uw_set_error(ctx, "Bad subforms name %s", value); 749 uw_set_error(ctx, "Bad subforms name %s", value);
748 return -1; 750 return -1;
749 } 751 }
750 752
751 if (n >= uw_inputs_len) { 753 if (n >= ctx->app->inputs_len) {
752 uw_set_error(ctx, "For subforms name %s, index %d is out of range", value, n); 754 uw_set_error(ctx, "For subforms name %s, index %d is out of range", value, n);
753 return -1; 755 return -1;
754 } 756 }
755 757
756 INP(ctx)[n].kind = SUBFORMS; 758 INP(ctx)[n].kind = SUBFORMS;
768 if (ctx->cur_container->kind != SUBFORMS) { 770 if (ctx->cur_container->kind != SUBFORMS) {
769 uw_set_error(ctx, "Bad kind for entry parent"); 771 uw_set_error(ctx, "Bad kind for entry parent");
770 return -1; 772 return -1;
771 } 773 }
772 774
773 inps = check_input_space(ctx, uw_inputs_len + 1); 775 inps = check_input_space(ctx, ctx->app->inputs_len + 1);
774 776
775 inps->kind = ENTRY; 777 inps->kind = ENTRY;
776 inps->data.entry.parent = ctx->cur_container; 778 inps->data.entry.parent = ctx->cur_container;
777 inps->data.entry.next = ctx->cur_container->data.subforms.entries; 779 inps->data.entry.next = ctx->cur_container->data.subforms.entries;
778 ctx->cur_container->data.subforms.entries = inps; 780 ctx->cur_container->data.subforms.entries = inps;
779 781
780 inps->data.entry.fields = inps+1; 782 inps->data.entry.fields = inps+1;
781 ctx->cur_container = inps; 783 ctx->cur_container = inps;
782 } else { 784 } else {
783 int n = uw_input_num(name); 785 int n = ctx->app->input_num(name);
784 786
785 if (n < 0) { 787 if (n < 0) {
786 if (!strcmp(name, "null")) 788 if (!strcmp(name, "null"))
787 return 0; 789 return 0;
788 uw_set_error(ctx, "Bad input name %s", name); 790 uw_set_error(ctx, "Bad input name %s", name);
789 return -1; 791 return -1;
790 } 792 }
791 793
792 if (n >= uw_inputs_len) { 794 if (n >= ctx->app->inputs_len) {
793 uw_set_error(ctx, "For input name %s, index %d is out of range", name, n); 795 uw_set_error(ctx, "For input name %s, index %d is out of range", name, n);
794 return -1; 796 return -1;
795 } 797 }
796 798
797 INP(ctx)[n].kind = NORMAL; 799 INP(ctx)[n].kind = NORMAL;
802 } 804 }
803 805
804 char *uw_get_input(uw_context ctx, int n) { 806 char *uw_get_input(uw_context ctx, int n) {
805 if (n < 0) 807 if (n < 0)
806 uw_error(ctx, FATAL, "Negative input index %d", n); 808 uw_error(ctx, FATAL, "Negative input index %d", n);
807 if (n >= uw_inputs_len) 809 if (n >= ctx->app->inputs_len)
808 uw_error(ctx, FATAL, "Out-of-bounds input index %d", n); 810 uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
809 811
810 switch (INP(ctx)[n].kind) { 812 switch (INP(ctx)[n].kind) {
811 case UNSET: 813 case UNSET:
812 return NULL; 814 return NULL;
826 } 828 }
827 829
828 char *uw_get_optional_input(uw_context ctx, int n) { 830 char *uw_get_optional_input(uw_context ctx, int n) {
829 if (n < 0) 831 if (n < 0)
830 uw_error(ctx, FATAL, "Negative input index %d", n); 832 uw_error(ctx, FATAL, "Negative input index %d", n);
831 if (n >= uw_inputs_len) 833 if (n >= ctx->app->inputs_len)
832 uw_error(ctx, FATAL, "Out-of-bounds input index %d", n); 834 uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
833 835
834 switch (INP(ctx)[n].kind) { 836 switch (INP(ctx)[n].kind) {
835 case UNSET: 837 case UNSET:
836 return ""; 838 return "";
848 uw_error(ctx, FATAL, "Impossible input kind"); 850 uw_error(ctx, FATAL, "Impossible input kind");
849 } 851 }
850 } 852 }
851 853
852 int uw_set_file_input(uw_context ctx, const char *name, uw_Basis_file f) { 854 int uw_set_file_input(uw_context ctx, const char *name, uw_Basis_file f) {
853 int n = uw_input_num(name); 855 int n = ctx->app->input_num(name);
854 856
855 if (n < 0) { 857 if (n < 0) {
856 uw_set_error(ctx, "Bad file input name %s", name); 858 uw_set_error(ctx, "Bad file input name %s", name);
857 return -1; 859 return -1;
858 } 860 }
859 861
860 if (n >= uw_inputs_len) { 862 if (n >= ctx->app->inputs_len) {
861 uw_set_error(ctx, "For file input name %s, index %d is out of range", name, n); 863 uw_set_error(ctx, "For file input name %s, index %d is out of range", name, n);
862 return -1; 864 return -1;
863 } 865 }
864 866
865 ctx->inputs[n].kind = FIL; 867 ctx->inputs[n].kind = FIL;
867 869
868 return 0; 870 return 0;
869 } 871 }
870 872
871 void *uw_malloc(uw_context ctx, size_t len); 873 void *uw_malloc(uw_context ctx, size_t len);
872
873 874
874 static void parents(input *inp) { 875 static void parents(input *inp) {
875 printf("Stack: %p\n", inp); 876 printf("Stack: %p\n", inp);
876 while (inp) { 877 while (inp) {
877 switch (inp->kind) { 878 switch (inp->kind) {
900 } 901 }
901 902
902 uw_Basis_file uw_get_file_input(uw_context ctx, int n) { 903 uw_Basis_file uw_get_file_input(uw_context ctx, int n) {
903 if (n < 0) 904 if (n < 0)
904 uw_error(ctx, FATAL, "Negative file input index %d", n); 905 uw_error(ctx, FATAL, "Negative file input index %d", n);
905 if (n >= uw_inputs_len) 906 if (n >= ctx->app->inputs_len)
906 uw_error(ctx, FATAL, "Out-of-bounds file input index %d", n); 907 uw_error(ctx, FATAL, "Out-of-bounds file input index %d", n);
907 908
908 switch (INP(ctx)[n].kind) { 909 switch (INP(ctx)[n].kind) {
909 case UNSET: 910 case UNSET:
910 { 911 {
928 } 929 }
929 930
930 void uw_enter_subform(uw_context ctx, int n) { 931 void uw_enter_subform(uw_context ctx, int n) {
931 if (n < 0) 932 if (n < 0)
932 uw_error(ctx, FATAL, "Negative subform index %d", n); 933 uw_error(ctx, FATAL, "Negative subform index %d", n);
933 if (n >= uw_inputs_len) 934 if (n >= ctx->app->inputs_len)
934 uw_error(ctx, FATAL, "Out-of-bounds subform index %d", n); 935 uw_error(ctx, FATAL, "Out-of-bounds subform index %d", n);
935 936
936 switch (INP(ctx)[n].kind) { 937 switch (INP(ctx)[n].kind) {
937 case UNSET: 938 case UNSET:
938 uw_error(ctx, FATAL, "Missing subform"); 939 uw_error(ctx, FATAL, "Missing subform");
967 int uw_enter_subforms(uw_context ctx, int n) { 968 int uw_enter_subforms(uw_context ctx, int n) {
968 input *inps; 969 input *inps;
969 970
970 if (n < 0) 971 if (n < 0)
971 uw_error(ctx, FATAL, "Negative subforms index %d", n); 972 uw_error(ctx, FATAL, "Negative subforms index %d", n);
972 if (n >= uw_inputs_len) 973 if (n >= ctx->app->inputs_len)
973 uw_error(ctx, FATAL, "Out-of-bounds subforms index %d", n); 974 uw_error(ctx, FATAL, "Out-of-bounds subforms index %d", n);
974 975
975 switch (INP(ctx)[n].kind) { 976 switch (INP(ctx)[n].kind) {
976 case UNSET: 977 case UNSET:
977 uw_error(ctx, FATAL, "Missing subforms"); 978 uw_error(ctx, FATAL, "Missing subforms");
1026 1027
1027 void uw_set_script_header(uw_context ctx, const char *s) { 1028 void uw_set_script_header(uw_context ctx, const char *s) {
1028 ctx->script_header = s; 1029 ctx->script_header = s;
1029 } 1030 }
1030 1031
1031 void uw_set_url_prefix(uw_context ctx, const char *s) { 1032 const char *uw_get_url_prefix(uw_context ctx) {
1032 ctx->url_prefix = s; 1033 return ctx->app->url_prefix;
1033 } 1034 }
1034 1035
1035 void uw_set_needs_push(uw_context ctx, int n) { 1036 void uw_set_needs_push(uw_context ctx, int n) {
1036 ctx->needs_push = n; 1037 ctx->needs_push = n;
1037 } 1038 }
1202 sprintf(r, " onunload='unload();%s'", s); 1203 sprintf(r, " onunload='unload();%s'", s);
1203 return r; 1204 return r;
1204 } 1205 }
1205 } 1206 }
1206 1207
1207 extern uw_Basis_string uw_cookie_sig(uw_context);
1208
1209 const char *uw_Basis_get_settings(uw_context ctx, uw_unit u) { 1208 const char *uw_Basis_get_settings(uw_context ctx, uw_unit u) {
1210 if (ctx->client == NULL) { 1209 if (ctx->client == NULL) {
1211 if (ctx->needs_sig) { 1210 if (ctx->needs_sig) {
1212 char *sig = uw_cookie_sig(ctx); 1211 char *sig = ctx->app->cookie_sig(ctx);
1213 char *r = uw_malloc(ctx, strlen(sig) + 8); 1212 char *r = uw_malloc(ctx, strlen(sig) + 8);
1214 sprintf(r, "sig=\"%s\";", sig); 1213 sprintf(r, "sig=\"%s\";", sig);
1215 return r; 1214 return r;
1216 } 1215 }
1217 else 1216 else
1218 return ""; 1217 return "";
1219 } else { 1218 } else {
1220 char *sig = ctx->needs_sig ? uw_cookie_sig(ctx) : ""; 1219 char *sig = ctx->needs_sig ? ctx->app->cookie_sig(ctx) : "";
1221 char *r = uw_malloc(ctx, 59 + 3 * INTS_MAX + strlen(ctx->url_prefix) 1220 char *r = uw_malloc(ctx, 59 + 3 * INTS_MAX + strlen(ctx->app->url_prefix)
1222 + (ctx->needs_sig ? strlen(sig) + 7 : 0)); 1221 + (ctx->needs_sig ? strlen(sig) + 7 : 0));
1223 sprintf(r, "client_id=%u;client_pass=%d;url_prefix=\"%s\";timeout=%d;%s%s%slistener();", 1222 sprintf(r, "client_id=%u;client_pass=%d;url_prefix=\"%s\";timeout=%d;%s%s%slistener();",
1224 ctx->client->id, 1223 ctx->client->id,
1225 ctx->client->pass, 1224 ctx->client->pass,
1226 ctx->url_prefix, 1225 ctx->app->url_prefix,
1227 ctx->timeout, 1226 ctx->app->timeout,
1228 ctx->needs_sig ? "sig=\"" : "", 1227 ctx->needs_sig ? "sig=\"" : "",
1229 sig, 1228 sig,
1230 ctx->needs_sig ? "\";" : ""); 1229 ctx->needs_sig ? "\";" : "");
1231 return r; 1230 return r;
1232 } 1231 }
2764 buf_append(&d->msgs, "\n", 1); 2763 buf_append(&d->msgs, "\n", 1);
2765 2764
2766 return uw_unit_v; 2765 return uw_unit_v;
2767 } 2766 }
2768 2767
2769 int uw_db_commit(uw_context);
2770 int uw_db_rollback(uw_context);
2771
2772 void uw_commit(uw_context ctx) { 2768 void uw_commit(uw_context ctx) {
2773 unsigned i; 2769 unsigned i;
2774 2770
2775 for (i = 0; i < ctx->used_transactionals; ++i) 2771 for (i = 0; i < ctx->used_transactionals; ++i)
2776 if (ctx->transactionals[i].rollback != NULL) 2772 if (ctx->transactionals[i].rollback != NULL)
2780 for (i = 0; i < ctx->used_transactionals; ++i) 2776 for (i = 0; i < ctx->used_transactionals; ++i)
2781 if (ctx->transactionals[i].rollback == NULL) 2777 if (ctx->transactionals[i].rollback == NULL)
2782 if (ctx->transactionals[i].commit) 2778 if (ctx->transactionals[i].commit)
2783 ctx->transactionals[i].commit(ctx->transactionals[i].data); 2779 ctx->transactionals[i].commit(ctx->transactionals[i].data);
2784 2780
2785 if (uw_db_commit(ctx)) 2781 if (ctx->app->db_commit(ctx))
2786 uw_error(ctx, FATAL, "Error running SQL COMMIT"); 2782 uw_error(ctx, FATAL, "Error running SQL COMMIT");
2787 2783
2788 for (i = 0; i < ctx->used_deltas; ++i) { 2784 for (i = 0; i < ctx->used_deltas; ++i) {
2789 delta *d = &ctx->deltas[i]; 2785 delta *d = &ctx->deltas[i];
2790 client *c = find_client(d->client); 2786 client *c = find_client(d->client);
2853 2849
2854 for (i = 0; i < ctx->used_transactionals; ++i) 2850 for (i = 0; i < ctx->used_transactionals; ++i)
2855 if (ctx->transactionals[i].free) 2851 if (ctx->transactionals[i].free)
2856 ctx->transactionals[i].free(ctx->transactionals[i].data); 2852 ctx->transactionals[i].free(ctx->transactionals[i].data);
2857 2853
2858 return uw_db_rollback(ctx); 2854 return ctx->app->db_rollback(ctx);
2859 } 2855 }
2860 2856
2861 void uw_register_transactional(uw_context ctx, void *data, uw_callback commit, uw_callback rollback, 2857 void uw_register_transactional(uw_context ctx, void *data, uw_callback commit, uw_callback rollback,
2862 uw_callback free) { 2858 uw_callback free) {
2863 if (ctx->used_transactionals >= ctx->n_transactionals) { 2859 if (ctx->used_transactionals >= ctx->n_transactionals) {
2872 } 2868 }
2873 2869
2874 2870
2875 // "Garbage collection" 2871 // "Garbage collection"
2876 2872
2877 void uw_expunger(uw_context ctx, uw_Basis_client cli);
2878
2879 static failure_kind uw_expunge(uw_context ctx, uw_Basis_client cli) { 2873 static failure_kind uw_expunge(uw_context ctx, uw_Basis_client cli) {
2880 int r = setjmp(ctx->jmp_buf); 2874 int r = setjmp(ctx->jmp_buf);
2881 2875
2882 if (r == 0) { 2876 if (r == 0) {
2883 if (uw_db_begin(ctx)) 2877 if (ctx->app->db_begin(ctx))
2884 uw_error(ctx, FATAL, "Error running SQL BEGIN"); 2878 uw_error(ctx, FATAL, "Error running SQL BEGIN");
2885 uw_expunger(ctx, cli); 2879 ctx->app->expunger(ctx, cli);
2886 if (uw_db_commit(ctx)) 2880 if (ctx->app->db_commit(ctx))
2887 uw_error(ctx, FATAL, "Error running SQL COMMIT"); 2881 uw_error(ctx, FATAL, "Error running SQL COMMIT");
2888 } 2882 }
2889 2883
2890 return r; 2884 return r;
2891 } 2885 }
2892 2886
2893 void uw_prune_clients(uw_context ctx) { 2887 void uw_prune_clients(uw_context ctx) {
2894 client *c, *next, *prev = NULL; 2888 client *c, *next, *prev = NULL;
2895 time_t cutoff; 2889 time_t cutoff;
2896 2890
2897 cutoff = time(NULL) - uw_timeout; 2891 cutoff = time(NULL) - ctx->app->timeout;
2898 2892
2899 pthread_mutex_lock(&clients_mutex); 2893 pthread_mutex_lock(&clients_mutex);
2900 2894
2901 for (c = clients_used; c; c = next) { 2895 for (c = clients_used; c; c = next) {
2902 next = c->next; 2896 next = c->next;
2909 clients_used = next; 2903 clients_used = next;
2910 uw_reset(ctx); 2904 uw_reset(ctx);
2911 while (fk == UNLIMITED_RETRY) { 2905 while (fk == UNLIMITED_RETRY) {
2912 fk = uw_expunge(ctx, c->id); 2906 fk = uw_expunge(ctx, c->id);
2913 if (fk == UNLIMITED_RETRY) { 2907 if (fk == UNLIMITED_RETRY) {
2914 uw_db_rollback(ctx); 2908 ctx->app->db_rollback(ctx);
2915 printf("Unlimited retry during expunge: %s\n", uw_error_message(ctx)); 2909 printf("Unlimited retry during expunge: %s\n", uw_error_message(ctx));
2916 } 2910 }
2917 } 2911 }
2918 if (fk == SUCCESS) 2912 if (fk == SUCCESS)
2919 free_client(c); 2913 free_client(c);
2920 else { 2914 else {
2921 uw_db_rollback(ctx); 2915 ctx->app->db_rollback(ctx);
2922 fprintf(stderr, "Expunge blocked by error: %s\n", uw_error_message(ctx)); 2916 fprintf(stderr, "Expunge blocked by error: %s\n", uw_error_message(ctx));
2923 } 2917 }
2924 } 2918 }
2925 else 2919 else
2926 prev = c; 2920 prev = c;
2928 } 2922 }
2929 2923
2930 pthread_mutex_unlock(&clients_mutex); 2924 pthread_mutex_unlock(&clients_mutex);
2931 } 2925 }
2932 2926
2933 void uw_initializer(uw_context ctx);
2934
2935 failure_kind uw_initialize(uw_context ctx) { 2927 failure_kind uw_initialize(uw_context ctx) {
2936 int r = setjmp(ctx->jmp_buf); 2928 int r = setjmp(ctx->jmp_buf);
2937 2929
2938 if (r == 0) { 2930 if (r == 0) {
2939 if (uw_db_begin(ctx)) 2931 if (ctx->app->db_begin(ctx))
2940 uw_error(ctx, FATAL, "Error running SQL BEGIN"); 2932 uw_error(ctx, FATAL, "Error running SQL BEGIN");
2941 uw_initializer(ctx); 2933 ctx->app->initializer(ctx);
2942 if (uw_db_commit(ctx)) 2934 if (ctx->app->db_commit(ctx))
2943 uw_error(ctx, FATAL, "Error running SQL COMMIT"); 2935 uw_error(ctx, FATAL, "Error running SQL COMMIT");
2944 } 2936 }
2945 2937
2946 return r; 2938 return r;
2947 } 2939 }
2948
2949 extern int uw_check_url(const char *);
2950 extern int uw_check_mime(const char *);
2951 2940
2952 static int url_bad(uw_Basis_string s) { 2941 static int url_bad(uw_Basis_string s) {
2953 for (; *s; ++s) 2942 for (; *s; ++s)
2954 if (!isgraph(*s)) 2943 if (!isgraph(*s))
2955 return 1; 2944 return 1;
2958 } 2947 }
2959 2948
2960 uw_Basis_string uw_Basis_bless(uw_context ctx, uw_Basis_string s) { 2949 uw_Basis_string uw_Basis_bless(uw_context ctx, uw_Basis_string s) {
2961 if (url_bad(s)) 2950 if (url_bad(s))
2962 uw_error(ctx, FATAL, "Invalid URL %s", uw_Basis_htmlifyString(ctx, s)); 2951 uw_error(ctx, FATAL, "Invalid URL %s", uw_Basis_htmlifyString(ctx, s));
2963 if (uw_check_url(s)) 2952 if (ctx->app->check_url(s))
2964 return s; 2953 return s;
2965 else 2954 else
2966 uw_error(ctx, FATAL, "Disallowed URL %s", uw_Basis_htmlifyString(ctx, s)); 2955 uw_error(ctx, FATAL, "Disallowed URL %s", uw_Basis_htmlifyString(ctx, s));
2967 } 2956 }
2968 2957
2969 uw_Basis_string uw_Basis_checkUrl(uw_context ctx, uw_Basis_string s) { 2958 uw_Basis_string uw_Basis_checkUrl(uw_context ctx, uw_Basis_string s) {
2970 if (url_bad(s)) 2959 if (url_bad(s))
2971 return NULL; 2960 return NULL;
2972 if (uw_check_url(s)) 2961 if (ctx->app->check_url(s))
2973 return s; 2962 return s;
2974 else 2963 else
2975 return NULL; 2964 return NULL;
2976 } 2965 }
2977 2966
2985 2974
2986 uw_Basis_string uw_Basis_blessMime(uw_context ctx, uw_Basis_string s) { 2975 uw_Basis_string uw_Basis_blessMime(uw_context ctx, uw_Basis_string s) {
2987 if (!mime_format(s)) 2976 if (!mime_format(s))
2988 uw_error(ctx, FATAL, "MIME type \"%s\" contains invalid character", uw_Basis_htmlifyString(ctx, s)); 2977 uw_error(ctx, FATAL, "MIME type \"%s\" contains invalid character", uw_Basis_htmlifyString(ctx, s));
2989 2978
2990 if (uw_check_mime(s)) 2979 if (ctx->app->check_mime(s))
2991 return s; 2980 return s;
2992 else 2981 else
2993 uw_error(ctx, FATAL, "Disallowed MIME type %s", uw_Basis_htmlifyString(ctx, s)); 2982 uw_error(ctx, FATAL, "Disallowed MIME type %s", uw_Basis_htmlifyString(ctx, s));
2994 } 2983 }
2995 2984
2996 uw_Basis_string uw_Basis_checkMime(uw_context ctx, uw_Basis_string s) { 2985 uw_Basis_string uw_Basis_checkMime(uw_context ctx, uw_Basis_string s) {
2997 if (!mime_format(s)) 2986 if (!mime_format(s))
2998 return NULL; 2987 return NULL;
2999 2988
3000 if (uw_check_mime(s)) 2989 if (ctx->app->check_mime(s))
3001 return s; 2990 return s;
3002 else 2991 else
3003 return NULL; 2992 return NULL;
3004 } 2993 }
3005 2994
3018 3007
3019 return r; 3008 return r;
3020 } 3009 }
3021 3010
3022 uw_Basis_string uw_Basis_sigString(uw_context ctx, uw_unit u) { 3011 uw_Basis_string uw_Basis_sigString(uw_context ctx, uw_unit u) {
3023 return uw_cookie_sig(ctx); 3012 return ctx->app->cookie_sig(ctx);
3024 } 3013 }
3025 3014
3026 uw_Basis_string uw_Basis_fileName(uw_context ctx, uw_Basis_file f) { 3015 uw_Basis_string uw_Basis_fileName(uw_context ctx, uw_Basis_file f) {
3027 return f.name; 3016 return f.name;
3028 } 3017 }