1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
|
#line 2 "cgi.c"
/*-
* C-SaCzech
* Copyright (c) 1996-2002 Jaromir Dolecek <dolecek@ics.muni.cz>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Jaromir Dolecek
* for the CSacek project.
* 4. The name of Jaromir Dolecek may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY JAROMIR DOLECEK ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL JAROMIR DOLECEK BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* $Id: cgi.c,v 1.91 2002/02/03 11:13:41 dolecek Exp $ */
#include "csacek.h"
/* CGI specific code */
/* local functions */
static int x_connect_server __P((const char *serveraddr, const int port));
static void x_fclose_cleanup __P((void *));
static int x_isdir __P((const char *filename));
static int x_isfile __P((const char *filename));
static int x_init_method_http __P((csa_params_t *));
static RETSIGTYPE x_alarmtrap __P((int j));
int main __P((int argc, const char *argv[]));
#ifndef CSA_METHOD_HTTP
static const char *x_mimetype __P((const char *fname));
static int x_init_method_file __P((csa_params_t *));
static int x_file_make_headers __P((csa_params_t *p, const char *filename));
/* for purposes of x_mimetype() */
typedef struct mimetabitem {
const char *suffix;
short suffix_len;
const char *type;
} x_mimetabitem_t;
#ifndef HAVE_STRFTIME
static const char * weekdays[7] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
#endif
#ifndef CSA_DO_NOT_CACHE
static const char * months[12] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
"Nov", "Dec" };
#endif
static const x_mimetabitem_t x_mimetypes[] =
{
{ "cz", 2, "text/html" },
{ "html", 4, "text/html" },
{ "htm", 3, "text/html" },
{ "txt", 3, "text/plain" },
{ NULL, 0, NULL },
};
/*
* returns MIME type of filename given as parameter
*/
const char *
x_mimetype( fname )
const char *fname;
{
const char *endp, *pp;
int i;
if ( fname == NULL ) return 0;
endp = strchr(fname, '\0'); /* points to the end of ``fname'' */
for(i=0; x_mimetypes[i].suffix; i++)
{
pp = endp - x_mimetypes[i].suffix_len;
if (pp>fname && *(pp-1)=='.' && !strcasecmp(pp, x_mimetypes[i].suffix))
return x_mimetypes[i].type;
}
return NULL;
}
/*
* makes necessary headers out of informations got by stat() and friends
*/
static int
x_file_make_headers(p, filename)
csa_params_t *p;
const char *filename;
{
struct stat statbuf;
#ifndef CSA_DO_NOT_CACHE
struct tm *tmi;
char buf[2048];
const csa_String *if_modified_since;
#endif
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_file_make_headers: called for %s", filename);
#endif /* CSA_DEBUG */
if (csa_stat(filename, &statbuf) == -1)
return CSA_FAILED;
/* it's common practice to mark HTML files, which use SSI, by turning */
/* execute bit on */
if (statbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) {
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_file_make_headers: executable bit set, declining");
#endif
return CSA_FAILED;
}
#ifndef CSA_DO_NOT_CACHE
if_modified_since = csa_getheaderin(p, "If-Modified-Since");
if ( if_modified_since )
{
struct tm tmspec;
char *if_copy, *pom_if, *pch;
const char *mtime = "";
int year=0, year_len = 4;
int typ, pomi, valid=0;
time_t if_mtime;
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_file_make_headers: If-Modified-Since=%s",
if_modified_since->value);
#endif
memset((void *)&tmspec, '\0', sizeof(tmspec));
if_copy = ap_pstrdup(p->pool_tmp, if_modified_since->value);
pom_if = if_copy;
while( *pom_if && isspace((unsigned char)*pom_if)) pom_if++;
/*
typ 1: Sun, 06 Nov 1994 08:49:37 GMT - recommended
typ 2: Sunday, 06-Nov-94 08:49:37 GMT - obsolete
typ 3: Sun Nov 6 08:49:37 1994 - asctime()
*/
/* find out format in which the date was given */
if ( ! ( pch = strchr(if_copy, ',') ) ) {
typ = 3;
pom_if += 4; /* 3 chars abbr. of day + one char space */
}
else {
if ( pch - if_copy != 3 ) { typ = 2; year_len = 2; }
else typ = 1;
pom_if = pch + 1 + 1; /* comma, space */
}
pomi = strlen(pom_if);
/* sanity check - test if legth of date given correspond to format */
/* of date chosen */
if ( ( typ == 1 && pomi >= 24 )
|| ( typ == 2 && pomi >= 22 )
|| ( typ == 3 && pomi >= 15 ) ) valid = 1;
#ifdef CSA_DEBUG
else
csa_debug(p->dbg, "x_file_make_headers: date %s is invalid",
if_copy);
#endif
if ( valid && (typ == 1 || typ == 2) ) {
tmspec.tm_mday = atoi(pom_if);
pom_if += 3; /* day and space or dash */
for(pomi=0; pomi < 12; pomi++)
if (!strncasecmp(pom_if, months[pomi], 3) ) {
tmspec.tm_mon = pomi;
break;
}
pom_if += 4; /* abbr. of month and space or dash */
year = atoi(pom_if);
year += (year < 100) ? (year > 70 ? 1900 : 2000) : 0;
tmspec.tm_year = year;
pom_if += year_len + 1;
pom_if[8] = 0; /* cut off a timezone */
mtime = pom_if;
}
else if ( valid && typ == 3 ) {
for(pomi=0; pomi < 12; pomi++)
if (!strncasecmp(pom_if, months[pomi], 3) ) {
tmspec.tm_mon = pomi;
break;
}
pom_if += 4; /* skip over abbr. of month and space */
tmspec.tm_mday = atoi(pom_if);
pom_if += 3;
pom_if[8] = 0; /* cut off a timezone */
mtime = pom_if;
pom_if += 9;
tmspec.tm_year = atoi(pom_if);
}
if (valid) {
tmspec.tm_hour = atoi(mtime);
mtime += 3;
tmspec.tm_min = atoi(mtime);
mtime += 3;
tmspec.tm_sec = atoi(mtime);
tmspec.tm_year -= 1900; /* adjust year for mktime() */
if_mtime = mktime(&tmspec);
if ( if_mtime == statbuf.st_mtime )
{
csa_setheaderout(p, "Status", "304 Not modified",
CSA_I_OVERWRITE);
return CSA_DONE;
}
}
}
#endif /* ndef CSA_DO_NOT_CACHE */
/* finally, the headers can be sent */
csa_setheaderout(p, "Status", "200 OK Document follows", CSA_I_OVERWRITE);
/* csa_get_ct() would output proper Content-Type header, including */
/* charset specification, if needed and configured so */
csa_setheaderout(p, "Content-Type",
csa_get_ct(p->pool_req, p->outcharset, x_mimetype(filename)),
CSA_I_OVERWRITE);
#ifndef CSA_DO_NOT_CACHE
tmi = gmtime(&statbuf.st_mtime);
#ifdef HAVE_STRFTIME
strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmi );
#else
sprintf(buf, "%s, %02d %s %4d %02d:%02d:%02d GMT",
weekdays[tmi->tm_wday],
tmi->tm_mday, months[tmi->tm_mon], tmi->tm_year + 1900,
tmi->tm_hour, tmi->tm_min, tmi->tm_sec );
#endif /* HAVE_STRFTIME */
csa_setheaderout(p, "Last-Modified", buf, CSA_I_COPYVALUE|CSA_I_OVERWRITE);
#endif
/* set input content_length */
p->available_in = statbuf.st_size;
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_file_make_headers: end");
#endif /* CSA_DEBUG */
return CSA_OK;
} /* x_file_make_headers */
/*
* inicialization of method ``file''
*/
int
x_init_method_file(p)
csa_params_t *p;
{
int retval;
const csa_String *path_translated;
const char *mimetype=NULL;
FILE *tfp;
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_init_method_file: called");
#endif
/* some of these will be needed later */
path_translated = csa_getvar(p, "PATH_TRANSLATED");
if (!path_translated) return CSA_FATAL;
tfp = fopen(path_translated->value, "r");
/* cannot open file for reading */
if (!tfp) {
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_init_method_file: cannot open %s for reading",
path_translated->value);
#endif
return CSA_FAILED;
}
csa_set_fio(p, &(p->resp), 0, tfp, fileno(tfp));
/* avoid descriptor leaking */
ap_register_cleanup(p->pool_req, p->resp, x_fclose_cleanup,
x_fclose_cleanup);
/* find out, if the type of document is text/html */
mimetype = x_mimetype(path_translated->value);
if (mimetype) {
CSA_SET(p->flags, CSA_FL_CONVERT);
if (strncasecmp(mimetype, "text/html", 9) == 0)
CSA_SET(p->flags, CSA_FL_ISHTML);
}
/* make necessary headers */
retval = x_file_make_headers(p, path_translated->value);
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_init_method_file: end");
#endif
return retval;
} /* x_init_method_file */
#endif /* !CSA_METHOD_HTTP */
/*
* function which does cleanup for csa_FILE_t, when pool_req is
* destroyed
*/
static void
x_fclose_cleanup(ptr)
void *ptr;
{ (void) csa_fclose((csa_FILE_t *) ptr); }
/*
* returns non-zero if the path leads to regular file
*/
static int
x_isfile( name )
const char *name;
{
struct stat buf;
int result;
result = (!csa_stat( name, &buf ) && S_ISREG(buf.st_mode) );
errno = 0; /* could be set by stat(2) call */
return result;
}
/*
* return non-zero if the path points to a directory
*/
static int
x_isdir( name )
const char *name;
{
struct stat statbuf;
int result;
result = (!csa_stat( name, &statbuf ) && S_ISDIR(statbuf.st_mode) );
errno = 0; /* could be set by stat(2) call */
return result;
}
#ifdef CSA_AVOID_IODEADLOCK
/*
* function called when SIGALRM is received (possibly after previous
* alarm(2) call)
*/
/* ARGSUSED */
static RETSIGTYPE
x_alarmtrap(j)
int j;
{
const char *zprava = "<HTML><TITLE>C-SaCzech timed-out</TITLE><BODY><H1>C-SaCzech timed-out</H1>\nC-SaCzech timed out while connecting to server.\n";
printf("Status: 500 CGI script timed-out\n");
printf("Content-Type: text/html\n");
printf("Content-Length: %lu\n",
(unsigned long) strlen(zprava) * sizeof(char));
printf("\n");
printf("%s", zprava);
exit(CSA_FATAL);
}
#endif /* CSA_AVOID_IODEADLOCK */
/*
* creates socket connection with ``server'' on port number ``port''
*/
static int
x_connect_server(address, port)
const char *address;
const int port;
{
int sockd, ret_value;
struct sockaddr_in sin;
sockd=socket(AF_INET,SOCK_STREAM,0);
if (sockd<0) return CSA_FAILED;
/* find out wheter name or IP address has been given */
if (!atoi(address ? address : "0"))
{
struct hostent *host;
/* domain address */
host = gethostbyname(address);
/* LINTED */
memmove((void *)&sin.sin_addr, host->h_addr, host->h_length);
}
else
/* numerical address */
sin.sin_addr.s_addr = inet_addr( address );
sin.sin_family = AF_INET;
sin.sin_port = htons((unsigned short)port);
ret_value = connect(sockd, (void *)&sin, sizeof(sin));
if (ret_value) {
closesocket(sockd);
return CSA_FAILED;
}
return sockd;
}
/*
* initialization of method ``http''
*/
int
x_init_method_http(p)
csa_params_t *p;
{
char *post;
const char *cpost=NULL;
size_t post_len=0;
int sock, retval;
csa_item_t *httpheaders=NULL;
const csa_String *server_port, *request_method;
const csa_String *server_name, *str;
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_init_method_http: begin");
#endif
/* initialize variables needed for further processing */
server_port = csa_getvar(p, "SERVER_PORT");
request_method = csa_getvar(p, "REQUEST_METHOD");
server_name = csa_getvar(p, "SERVER_NAME");
sock = x_connect_server(server_name->value, atoi(server_port->value));
if (sock == CSA_FAILED ) {
size_t len;
char *chp;
const char *fmt = "Unable to connect server %s, port %s<BR>\n";
len = strlen(fmt) + server_name->len + server_port->len;
chp = (char *) csa_alloca(len, p->pool_tmp);
sprintf( chp, fmt, server_name->value, server_port->value );
csa_http_error(p, "Unable to connect", chp );
return CSA_FATAL;
}
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_init_method_http: opening stream above desriptor");
#endif
if ( ( p->resp = csa_fdopen( p, sock, CSA_SOCK_OP ) ) == NULL ) {
csa_http_error(p, "Unable to open stdio stream",
"Cannot open stdio stream for socket communication.\n");
return CSA_FATAL;
}
/* avoid descriptor leaking */
ap_register_cleanup(p->pool_req, p->resp, x_fclose_cleanup,
x_fclose_cleanup);
/* pass input headers; they would not have any sense for CSacek, */
/* but can be important through */
httpheaders = csa_make_headersin(p);
/* always close Connection immedially after serving the sub-request */
csa_setitem(p, &httpheaders, "Connection", "close",
CSA_I_TMP|CSA_I_OVERWRITE);
/* support for PUT and POST methods - pass input data to sub-request */
if (!strncasecmp(request_method->value, "POST", 4)
|| !strncasecmp(request_method->value, "PUT", 3) )
{
char *new_cl;
const csa_String *content_length;
content_length = csa_getheaderin(p, "Content-Length");
if (!content_length) {
csa_http_error(p, "Content-Length not set",
"Content-Length not set even through data was sent by POST method");
return CSA_FATAL;
}
post_len = atoi(content_length->value);
post = csa_alloca(post_len, p->pool_tmp);
fread(post, 1, post_len, stdin);
if (CSA_ISSET(p->flags, CSA_FL_RECODEINPUT)) {
csa_String sp;
csa_decodequery(&sp, p, post, post_len);
cpost = sp.value;
post_len = sp.len;
} else
cpost = post;
/* set Content-Type in case it was not in input headers */
csa_setitem(p, &httpheaders, "Content-Type", CSA_CT_FORMDATA,
CSA_I_IFNOTSET|CSA_I_TMP);
/* set Content-Length to correct value */
new_cl = ap_palloc(p->pool_tmp, CSA_GETMAXNUMCOUNT(post_len) + 1);
sprintf(new_cl, "%u", (unsigned int) post_len);
csa_setitem(p, &httpheaders, "Content-Length", new_cl,
CSA_I_TMP|CSA_I_OVERWRITE);
} /* if POST || PUT */
/* ensure Host header is sent - if it's not set yet, use value of
* SERVER_NAME */
if (!csa_getitem(httpheaders, "Host"))
csa_setitem(p, &httpheaders, "Host", server_name->value,
CSA_I_TMP);
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_init_method_http: sending query to the server");
#endif
#ifdef CSA_AVOID_IODEADLOCK
alarm(15); /* timeout for connection with server */
#endif /* CSA_AVOID_IODEADLOCK */
/* send a query to the server */
str = csa_getvar(p, "QUERY_STRING");
csa_fprintf(p->resp, "%s %s%s%s HTTP/1.1\n",
request_method->value,
csa_getvar(p, "PATH_INFO")->value,
(str ? "?" : ""), (str ? str->value : ""));
p->req_protocol = 11;
/* send headers, newline and data from stdin, if any */
while(httpheaders && httpheaders->prev) httpheaders = httpheaders->prev;
for(; httpheaders; httpheaders = httpheaders->next)
{
csa_fprintf(p->resp, "%s: %s\n", httpheaders->key.value,
httpheaders->value.value);
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_init_method_http: sending header ``%s: %s''",
httpheaders->key.value, httpheaders->value.value);
#endif
}
csa_fwrite("\n", 1, 1, p->resp); /* send a newline */
if (cpost)
csa_fwrite(cpost, 1, post_len, p->resp);
/* flush output buffer */
csa_fflush(p->resp);
#ifdef CSA_AVOID_IODEADLOCK
alarm(0); /* switch off alarm timer */
#endif /* CSA_AVOID_IODEADLOCK */
/* clear pool - data allocated there are not more needed */
ap_clear_pool(p->pool_tmp);
retval = csa_process_headers(p);
if (retval != CSA_OK) {
/* just ignore 100 Continue, if sent by server */
if (retval == CSA_CONTINUE)
retval = csa_process_headers(p);
return retval;
}
/* it's wise to close writing part of socket, so server won't expect */
/* any more data and closes connection immedially after serving request */
/* have to do it _AFTER_ some data has been read from response */
/* to give server some time to react on request - some servers */
/* need it (Netscape-Enterprise/2.01 for example) */
shutdown(csa_fileno(p->resp), 1);
#ifdef CSA_DEBUG
csa_debug(p->dbg, "x_init_method_http: end");
#endif
return CSA_OK;
}
/* -----------------------------------------------------------------------*/
/* common MD staff */
/*
* this is called from csa_alloc_fail() when some memory allocation
* function fails
*/
void
csa_md_alloc_fail()
{
const char *message = "<TITLE>C-SaCzech error</TITLE><H1>Internal server error: C-SaCzech error</H1>Cannot malloc()ate required memory.\n";
printf("Status: 500 Internal server error\n");
printf("Content-Type: text/html\n");
printf("Content-Length: %lu\n",
(unsigned long) strlen(message) * sizeof(char));
printf("\n");
printf("%s", message);
}
/*
* return page (generated from external template page), on which it's possible
* to explicitly choose preferred encoding
*/
int
csa_md_call_whichcode(p, filename)
csa_params_t *p;
const char *filename;
{
int retval = csa_process_whichcode_file(p, filename);
return (retval != CSA_OK) ? retval : CSA_SERVED;
}
/*
* returns value of variable ``var'' (one of "standard" CGI variables)
*/
/* ARGSUSED */
const char *
csa_md_getvalueof(p, var)
csa_params_t *p;
const char *var;
{
const char *retval = getenv(var);
/* handle SERVER_PROTOCOL specially - if it's HTTP/0.9 or not
* set, pretend it's a HTTP/1.0 client, so the MI code will
* send all the necessary headers from CGI back to server,
* so it won't get confused - it's server's responsibility
* to strip the headers when it's sending reply to the
* HTTP/0.9 client */
if (strcasecmp(var, "SERVER_PROTOCOL") == 0
&& (!retval || strcasecmp(retval, "HTTP/0.9") == 0))
retval = "HTTP/1.0";
return retval;
}
/*
* logs an error into server's error log
* returns 1 if no other output should be written (i.e. the
* rest of csa_http_error() should not be executed)
*/
/* ARGSUSED */
int
csa_md_log_error(p, log)
csa_params_t *p;
const char *log;
{
time_t tt;
char *time_str;
tt = time(NULL);
time_str = ctime(&tt);
time_str[strlen(time_str) - 1] = '\0'; /* strip newline */
fprintf(stderr, "[%s] %s\n", time_str, log);
return 0;
}
/*
* read's len bytes from input (i.e. result of request) and places
* it in buf
* returns number of characters written
*/
int
csa_md_read_response(p, buf, len)
csa_params_t *p;
char *buf;
size_t len;
{
return csa_fread(buf, 1, len, p->resp);
}
/*
* get all input headers (headers sent by client) and store them
* in ``p''
*/
int
csa_md_set_headersin(p)
csa_params_t *p;
{
extern char **environ;
char **envi = environ;
char *name, *value, *temp;
int len;
csa_setheaderin(p, "Content-Type", getenv("CONTENT_TYPE"), 0);
csa_setheaderin(p, "Content-Length", getenv("CONTENT_LENGTH"), 0);
for(; envi && *envi; envi++)
{
if (strncasecmp(*envi, "HTTP_", 5)!=0)
continue;
value = strchr( *envi, '=');
if (!value || *(value+1) == '\0') continue;
name = *envi + 5; /* skip the "HTTP_" */
len = value - name;
name = ap_pstrndup(p->pool_tmp, name, len);
name[len] = '\0';
value++;
temp = strchr(name, '_');
for( ;temp; temp = strchr(temp+1, '_'))
*temp = '-';
csa_setitem(p, &(p->headersin), name, value, CSA_I_COPYKEY);
}
return 0;
}
/*
* separates headers and data; in case of CGI, just send a newline (headers
* were sent already by csa_send_headersout() )
*/
/* ARGSUSED */
int
csa_md_send_separator(p)
csa_params_t *p;
{
printf("\r\n"); /* nothing more for (Fast)CGI */
return 0;
}
/*
* send header given to an output
*/
/* ARGSUSED */
void
csa_md_send_header(p, header_name, header_value)
csa_params_t *p;
const char *header_name, *header_value;
{
printf("%s: %s\r\n", header_name, header_value);
}
/*
* sends ``len'' bytes from the place pointed to by ``ptr''
* into output
*/
/* ARGSUSED */
int
csa_md_send_output(p, ptr, len)
csa_params_t *p;
const char *ptr;
size_t len;
{
size_t retval;
retval = fwrite(ptr, 1, len, stdout);
return (len == retval);
}
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/*
* main
*/
int
main( argc, argv)
int argc;
const char *argv[];
{
char *b;
csa_params_t params, *p = ¶ms;
int use_http=0, csa_retval=CSA_OK;
const csa_String *path_info, *script_name, *path_translated;
struct pool *main_pool;
FILE *dbg=NULL;
#ifdef CSA_WANT_SECURE
char *a, *foo;
int is_netscape;
const csa_String *server_name;
#endif
/* if first parameter is --version, CSacek is called from command line */
/* - write some minimal info to output */
if ( argc > 1 && !strncasecmp(argv[1], "--version", 9))
{
csa_version();
return CSA_OK;
}
/* time need to be expressed in GMT */
#ifdef HAVE_SETENV
setenv("TZ", "", 1);
#elif defined(HAVE_PUTENV)
putenv("TZ=");
#endif /* HAVE_SETENV || HAVE_PUTENV */
tzset();
#ifndef CSA_NO_INIT_COMPAT
if (csa_init_compat() != 0) /* OS specific initialization */
exit(CSA_FATAL);
#endif
#ifdef CSA_AVOID_IODEADLOCK
signal(SIGALRM, x_alarmtrap);
#endif
main_pool = ap_make_sub_pool(NULL);
/* initialize global list of CSacek servers */
csa_cs_slist_init(main_pool);
/**********************************************************/
/* main request loop - nothing more can be done just once */
#ifdef CSA_MUTACE_FASTCGI
while(FCGI_Accept() >= 0) {
#endif /* CSA_MUTACE_FASTCGI */
#ifdef CSA_DEBUG
dbg = csa_debug_start();
#endif
memset((void *)p, '\0', sizeof(csa_params_t));
csa_retval = csa_init_params(p, main_pool, NULL, NULL, dbg);
if (csa_retval != CSA_OK) goto end_request;
/* CSacek as CGI script doesn't work with SSL */
if (CSA_ISSET(p->flags, CSA_FL_ISHTTPS)) {
csa_http_error(p, "SSL unsupported in CGI",
"CGI CSacek doesn't support SSL. Sorry.");
goto end_request;
}
#ifdef CSA_DEBUG
csa_debug(p->dbg, "main: starting processing the query");
#endif
script_name = p->csacek;
#ifdef CSA_DEBUG
csa_debug(p->dbg, "main: script_name: %s", script_name->value);
#endif
path_info = csa_getvar(p, "PATH_INFO");
#ifdef CSA_DEBUG
csa_debug(p->dbg, "main: path_info: %s", path_info->value);
#endif
path_translated = csa_getvar(p, "PATH_TRANSLATED");
#ifdef CSA_DEBUG
csa_debug(p->dbg, "main: path_translated: %s", path_translated->value);
#endif
#ifdef CSA_CANONIFY_DIRURL
/* if the URL doesn't end with '/' and leads to a dir, redirect client */
/* to URL ending with '/' */
if ( (b = strrchr(path_info->value, '/')) && *(b+1) != 0
&& x_isdir(path_translated->value) )
{
csa_setheaderout(p, "Status", "301 Moved Permanently", 0);
b = (char *) csa_alloca(path_info->len + 1 + 1, p->pool_tmp);
sprintf(b, "%s/", path_info->value);
b = csa_construct_url(p, NULL, b);
csa_setheaderout(p, "Location", b, CSA_I_COPYVALUE);
csa_retval = HTTP_MOVED_PERMANENTLY;
#ifdef CSA_DEBUG
csa_debug(p->dbg,
"main: %s is dir and URL doesn't end with /, redirecting",
path_info->value);
#endif
goto end_request;
}
#endif /* CSA_CANONIFY_DIRURL */
#ifdef CSA_DEBUG
csa_debug(p->dbg, "main: encoding - in=%s, out=%s",
cstools_name(p->incharset, CSTOOLS_TRUENAME),
p->charset.value );
#endif
#ifdef CSA_WANT_SECURE
/* don't allow to bypass directory-based restriction by directly serving
* appropriate file; check whether server config file isn't on the way and
* if yes, use method HTTP to get the document */
a = ap_pstrndup(p->pool_tmp, path_translated->value,
(int) (path_translated->len + 1 + 20));
a[path_translated->len] = '\0';
server_name = csa_getvar(p, "SERVER_NAME");
is_netscape = (server_name&&csa_strcasestr(server_name->value,"Netscape"));
while((foo = strrchr(a, '/')) ||
/* CONSTCOND */
(CSA_DIRDELIM == '\\' && (foo = strrchr(a, '\\'))))
{
strcpy(foo+1, ".htaccess"); /* safe */
if (x_isfile(a) ||
(is_netscape && strcpy(foo+1, ".nsconfig") && x_isfile(a)))
{
size_t len = foo - a;
/* only force using of method HTTP if CSacek binary is not
* under the same security subtree as the target document */
use_http = (!len || strncmp(script_name->value, a, len+1) != 0);
#ifdef CSA_DEBUG
csa_debug(p->dbg, "main: secure: %s, found %s %.*s",
(use_http) ? "on" : "off", a,
len+1, a);
#endif
break;
}
*foo = '\0'; /* cut off last directory from name */
}
#endif /* CSA_WANT_SECURE */
#ifdef CSA_DEBUG
csa_debug(p->dbg, "main: deciding which method should be used");
#endif
#if CSA_METHOD_HTTP
/* use method HTTP */
use_http = 1;
#elif CSA_METHOD_GUESS || CSA_METHOD_FILE
/* only files with one of suffixes in CSA_CONVERT_SUFFIXES will be coded*/
/* by method FILE */
if (!csa_has_suffix(path_translated->value, CSA_CONVERT_SUFFIXES,
CSA_SEP))
use_http = 1;
#endif /* CSA_METHOD_FOO */
/* make everything ready for processing data - note we don't need */
/* to worry about p->compress anywhere - it would be handled on */
/* first call to csa_add_output() */
if (use_http)
csa_retval = x_init_method_http(p);
else {
#ifndef CSA_METHOD_HTTP
csa_retval = x_init_method_file(p);
if (csa_retval == CSA_FAILED)
csa_retval = x_init_method_http(p);
#else
csa_retval = CSA_FAILED;
#endif /* ! CSA_METHOD_HTTP */
}
if (csa_retval != CSA_OK) {
#ifdef CSA_DEBUG
csa_debug(p->dbg, (csa_retval == CSA_DONE)
? "main: request served by method init, exiting"
: "main: method init failed, exiting");
#endif /* CSA_DEBUG */
goto end_request;
}
if (CSA_SHOULD_DIRECT_FORWARD(p)) {
/* avoid memory caching if the result won't be changed by CSacek */
/* and Content-Length is known */
csa_direct_forward(p);
goto end_loop;
}
else
(void) csa_process_body(p);
end_request:
csa_output(p);
end_loop:
fflush(stdout);
#ifdef CSA_MUTACE_FASTCGI
/* free resources for re-use - do this _before_ csa_debug_end(), just */
/* in case some cleanup routine would write to debug log */
ap_clear_pool(main_pool);
#endif /* CSA_MUTACE_FASTCGI */
#ifdef CSA_DEBUG
/* close debug log */
csa_debug_end(p->dbg);
#endif
#ifdef CSA_MUTACE_FASTCGI
} /* while(FCGI_Accept() >= 0) */
#endif /* CSA_MUTACE_FASTCGI */
/* run all the cleanup routines; shouldn't be necessary (OS should */
/* free all resources used by process), but Windooze NT seems to not */
/* close socket properly, so we have to do it explicitely; it isn't */
/* the case on other systems, but it takes no harm to do it anyway */
csa_done_alloc();
return csa_retval;
} /* main */
|