├── .gitignore ├── Makefile ├── README.md ├── capture.c ├── cgic.c ├── cgic.h ├── cgic.html ├── cgictest.c └── license.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.cgi 4 | capture 5 | cgicunittest 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=-g -Wall 2 | CC=gcc 3 | AR=ar 4 | RANLIB=ranlib 5 | LIBS=-L./ -lcgic 6 | 7 | all: libcgic.a cgictest.cgi capture 8 | 9 | install: libcgic.a 10 | cp libcgic.a /usr/local/lib 11 | cp cgic.h /usr/local/include 12 | @echo libcgic.a is in /usr/local/lib. cgic.h is in /usr/local/include. 13 | 14 | libcgic.a: cgic.o cgic.h 15 | rm -f libcgic.a 16 | $(AR) rc libcgic.a cgic.o 17 | $(RANLIB) libcgic.a 18 | 19 | #mingw32 and cygwin users: replace .cgi with .exe 20 | 21 | cgictest.cgi: cgictest.o libcgic.a 22 | gcc cgictest.o -o cgictest.cgi ${LIBS} 23 | 24 | capture: capture.o libcgic.a 25 | gcc capture.o -o capture ${LIBS} 26 | 27 | clean: 28 | rm -f *.o *.a cgictest.cgi capture cgicunittest 29 | 30 | test: 31 | gcc -D UNIT_TEST=1 cgic.c -o cgicunittest 32 | ./cgicunittest 33 | -------------------------------------------------------------------------------- /capture.c: -------------------------------------------------------------------------------- 1 | #include "cgic.h" 2 | 3 | int cgiMain() { 4 | cgiWriteEnvironment("/CHANGE/THIS/PATH/capcgi.dat"); 5 | cgiHeaderContentType("text/html"); 6 | fprintf(cgiOut, "Captured\n"); 7 | fprintf(cgiOut, "

Captured

\n"); 8 | fprintf(cgiOut, "Your form submission was captured for use in\n"); 9 | fprintf(cgiOut, "debugging CGI code.\n"); 10 | return 0; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /cgic.c: -------------------------------------------------------------------------------- 1 | /* cgicTempDir is the only setting you are likely to need 2 | to change in this file. */ 3 | 4 | /* Used only in Unix environments, in conjunction with mkstemp(). 5 | Elsewhere (Windows), temporary files go where the tmpnam() 6 | function suggests. If this behavior does not work for you, 7 | modify the getTempFile() function to suit your needs. */ 8 | 9 | #define cgicTempDir "/tmp" 10 | #define cgicMaxTempSize 1073741824 11 | 12 | #if CGICDEBUG 13 | #define CGICDEBUGSTART \ 14 | { \ 15 | FILE *dout; \ 16 | dout = fopen("/home/boutell/public_html/debug", "a"); \ 17 | 18 | #define CGICDEBUGEND \ 19 | fclose(dout); \ 20 | } 21 | #else /* CGICDEBUG */ 22 | #define CGICDEBUGSTART 23 | #define CGICDEBUGEND 24 | #endif /* CGICDEBUG */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef WIN32 35 | #include 36 | 37 | /* cgic 2.01 */ 38 | #include 39 | 40 | #else 41 | #include 42 | #endif /* WIN32 */ 43 | #include "cgic.h" 44 | 45 | #define cgiStrEq(a, b) (!strcmp((a), (b))) 46 | 47 | char *cgiServerSoftware; 48 | char *cgiServerName; 49 | char *cgiGatewayInterface; 50 | char *cgiServerProtocol; 51 | char *cgiServerPort; 52 | char *cgiRequestMethod; 53 | char *cgiPathInfo; 54 | char *cgiPathTranslated; 55 | char *cgiScriptName; 56 | char *cgiQueryString; 57 | char *cgiRemoteHost; 58 | char *cgiRemoteAddr; 59 | char *cgiAuthType; 60 | char *cgiRemoteUser; 61 | char *cgiRemoteIdent; 62 | char cgiContentTypeData[1024]; 63 | char *cgiContentType = cgiContentTypeData; 64 | char *cgiMultipartBoundary; 65 | char *cgiCookie; 66 | int cgiContentLength; 67 | char *cgiAccept; 68 | char *cgiUserAgent; 69 | char *cgiReferrer; 70 | 71 | FILE *cgiIn; 72 | FILE *cgiOut; 73 | 74 | /* True if CGI environment was restored from a file. */ 75 | static int cgiRestored = 0; 76 | 77 | static void cgiGetenv(char **s, char *var); 78 | 79 | typedef enum { 80 | cgiParseSuccess, 81 | cgiParseMemory, 82 | cgiParseIO 83 | } cgiParseResultType; 84 | 85 | /* One form entry, consisting of an attribute-value pair, 86 | and an optional filename and content type. All of 87 | these are guaranteed to be valid null-terminated strings, 88 | which will be of length zero in the event that the 89 | field is not present, with the exception of tfileName 90 | which will be null when 'in' is null. DO NOT MODIFY THESE 91 | VALUES. Make local copies if modifications are desired. */ 92 | 93 | typedef struct cgiFormEntryStruct { 94 | char *attr; 95 | /* value is populated for regular form fields only. 96 | For file uploads, it points to an empty string, and file 97 | upload data should be read from the file tfileName. */ 98 | char *value; 99 | /* When fileName is not an empty string, tfileName is not null, 100 | and 'value' points to an empty string. */ 101 | /* Valid for both files and regular fields; does not include 102 | terminating null of regular fields. */ 103 | int valueLength; 104 | char *fileName; 105 | char *contentType; 106 | /* Temporary file descriptor for working storage of file uploads. */ 107 | FILE *tFile; 108 | struct cgiFormEntryStruct *next; 109 | } cgiFormEntry; 110 | 111 | /* The first form entry. */ 112 | static cgiFormEntry *cgiFormEntryFirst; 113 | 114 | static cgiParseResultType cgiParseGetFormInput(); 115 | static cgiParseResultType cgiParsePostFormInput(); 116 | static cgiParseResultType cgiParsePostMultipartInput(); 117 | static cgiParseResultType cgiParseFormInput(char *data, int length); 118 | static void cgiSetupConstants(); 119 | static void cgiFreeResources(); 120 | static int cgiStrEqNc(char *s1, char *s2); 121 | static int cgiStrBeginsNc(char *s1, char *s2); 122 | 123 | #ifdef UNIT_TEST 124 | static int unitTest(); 125 | #endif 126 | 127 | int main(int argc, char *argv[]) { 128 | int result; 129 | char *cgiContentLengthString; 130 | char *e; 131 | cgiSetupConstants(); 132 | cgiGetenv(&cgiServerSoftware, "SERVER_SOFTWARE"); 133 | cgiGetenv(&cgiServerName, "SERVER_NAME"); 134 | cgiGetenv(&cgiGatewayInterface, "GATEWAY_INTERFACE"); 135 | cgiGetenv(&cgiServerProtocol, "SERVER_PROTOCOL"); 136 | cgiGetenv(&cgiServerPort, "SERVER_PORT"); 137 | cgiGetenv(&cgiRequestMethod, "REQUEST_METHOD"); 138 | cgiGetenv(&cgiPathInfo, "PATH_INFO"); 139 | cgiGetenv(&cgiPathTranslated, "PATH_TRANSLATED"); 140 | cgiGetenv(&cgiScriptName, "SCRIPT_NAME"); 141 | cgiGetenv(&cgiQueryString, "QUERY_STRING"); 142 | cgiGetenv(&cgiRemoteHost, "REMOTE_HOST"); 143 | cgiGetenv(&cgiRemoteAddr, "REMOTE_ADDR"); 144 | cgiGetenv(&cgiAuthType, "AUTH_TYPE"); 145 | cgiGetenv(&cgiRemoteUser, "REMOTE_USER"); 146 | cgiGetenv(&cgiRemoteIdent, "REMOTE_IDENT"); 147 | /* 2.0: the content type string needs to be parsed and modified, so 148 | copy it to a buffer. */ 149 | e = getenv("CONTENT_TYPE"); 150 | if (e) { 151 | if (strlen(e) < sizeof(cgiContentTypeData)) { 152 | strcpy(cgiContentType, e); 153 | } else { 154 | /* Truncate safely in the event of what is almost certainly 155 | a hack attempt */ 156 | strncpy(cgiContentType, e, sizeof(cgiContentTypeData)); 157 | cgiContentType[sizeof(cgiContentTypeData) - 1] = '\0'; 158 | } 159 | } else { 160 | cgiContentType[0] = '\0'; 161 | } 162 | /* Never null */ 163 | cgiMultipartBoundary = ""; 164 | /* 2.0: parse semicolon-separated additional parameters of the 165 | content type. The one we're interested in is 'boundary'. 166 | We discard the rest to make cgiContentType more useful 167 | to the typical programmer. */ 168 | if (strchr(cgiContentType, ';')) { 169 | char *sat = strchr(cgiContentType, ';'); 170 | while (sat) { 171 | *sat = '\0'; 172 | sat++; 173 | while (isspace(*sat)) { 174 | sat++; 175 | } 176 | if (cgiStrBeginsNc(sat, "boundary=")) { 177 | char *s; 178 | cgiMultipartBoundary = sat + strlen("boundary="); 179 | s = cgiMultipartBoundary; 180 | while ((*s) && (!isspace(*s))) { 181 | s++; 182 | } 183 | *s = '\0'; 184 | break; 185 | } else { 186 | sat = strchr(sat, ';'); 187 | } 188 | } 189 | } 190 | cgiGetenv(&cgiContentLengthString, "CONTENT_LENGTH"); 191 | cgiContentLength = atoi(cgiContentLengthString); 192 | cgiGetenv(&cgiAccept, "HTTP_ACCEPT"); 193 | cgiGetenv(&cgiUserAgent, "HTTP_USER_AGENT"); 194 | cgiGetenv(&cgiReferrer, "HTTP_REFERER"); 195 | cgiGetenv(&cgiCookie, "HTTP_COOKIE"); 196 | #ifdef CGICDEBUG 197 | CGICDEBUGSTART 198 | fprintf(dout, "%d\n", cgiContentLength); 199 | fprintf(dout, "%s\n", cgiRequestMethod); 200 | fprintf(dout, "%s\n", cgiContentType); 201 | CGICDEBUGEND 202 | #endif /* CGICDEBUG */ 203 | #ifdef WIN32 204 | /* 1.07: Must set stdin and stdout to binary mode */ 205 | /* 2.0: this is particularly crucial now and must not be removed */ 206 | _setmode( _fileno( stdin ), _O_BINARY ); 207 | _setmode( _fileno( stdout ), _O_BINARY ); 208 | #endif /* WIN32 */ 209 | cgiFormEntryFirst = 0; 210 | cgiIn = stdin; 211 | cgiOut = stdout; 212 | cgiRestored = 0; 213 | 214 | 215 | /* These five lines keep compilers from 216 | producing warnings that argc and argv 217 | are unused. They have no actual function. */ 218 | if (argc) { 219 | if (argv[0]) { 220 | cgiRestored = 0; 221 | } 222 | } 223 | 224 | 225 | if (cgiStrEqNc(cgiRequestMethod, "post")) { 226 | #ifdef CGICDEBUG 227 | CGICDEBUGSTART 228 | fprintf(dout, "POST recognized\n"); 229 | CGICDEBUGEND 230 | #endif /* CGICDEBUG */ 231 | if (cgiStrEqNc(cgiContentType, "application/x-www-form-urlencoded")) { 232 | #ifdef CGICDEBUG 233 | CGICDEBUGSTART 234 | fprintf(dout, "Calling PostFormInput\n"); 235 | CGICDEBUGEND 236 | #endif /* CGICDEBUG */ 237 | if (cgiParsePostFormInput() != cgiParseSuccess) { 238 | #ifdef CGICDEBUG 239 | CGICDEBUGSTART 240 | fprintf(dout, "PostFormInput failed\n"); 241 | CGICDEBUGEND 242 | #endif /* CGICDEBUG */ 243 | cgiHeaderStatus(500, "Error reading form data"); 244 | cgiFreeResources(); 245 | return -1; 246 | } 247 | #ifdef CGICDEBUG 248 | CGICDEBUGSTART 249 | fprintf(dout, "PostFormInput succeeded\n"); 250 | CGICDEBUGEND 251 | #endif /* CGICDEBUG */ 252 | } else if (cgiStrEqNc(cgiContentType, "multipart/form-data")) { 253 | #ifdef CGICDEBUG 254 | CGICDEBUGSTART 255 | fprintf(dout, "Calling PostMultipartInput\n"); 256 | CGICDEBUGEND 257 | #endif /* CGICDEBUG */ 258 | if (cgiParsePostMultipartInput() != cgiParseSuccess) { 259 | #ifdef CGICDEBUG 260 | CGICDEBUGSTART 261 | fprintf(dout, "PostMultipartInput failed\n"); 262 | CGICDEBUGEND 263 | #endif /* CGICDEBUG */ 264 | cgiHeaderStatus(500, "Error reading form data"); 265 | cgiFreeResources(); 266 | return -1; 267 | } 268 | #ifdef CGICDEBUG 269 | CGICDEBUGSTART 270 | fprintf(dout, "PostMultipartInput succeeded\n"); 271 | CGICDEBUGEND 272 | #endif /* CGICDEBUG */ 273 | } 274 | } else if (cgiStrEqNc(cgiRequestMethod, "get")) { 275 | /* The spec says this should be taken care of by 276 | the server, but... it isn't */ 277 | cgiContentLength = strlen(cgiQueryString); 278 | if (cgiParseGetFormInput() != cgiParseSuccess) { 279 | #ifdef CGICDEBUG 280 | CGICDEBUGSTART 281 | fprintf(dout, "GetFormInput failed\n"); 282 | CGICDEBUGEND 283 | #endif /* CGICDEBUG */ 284 | cgiHeaderStatus(500, "Error reading form data"); 285 | cgiFreeResources(); 286 | return -1; 287 | } else { 288 | #ifdef CGICDEBUG 289 | CGICDEBUGSTART 290 | fprintf(dout, "GetFormInput succeeded\n"); 291 | CGICDEBUGEND 292 | #endif /* CGICDEBUG */ 293 | } 294 | } 295 | #ifdef UNIT_TEST 296 | unitTest(); 297 | cgiFreeResources(); 298 | return 0; 299 | #else 300 | result = cgiMain(); 301 | return result; 302 | #endif 303 | } 304 | 305 | static void cgiGetenv(char **s, char *var){ 306 | *s = getenv(var); 307 | if (!(*s)) { 308 | *s = ""; 309 | } 310 | } 311 | 312 | static cgiParseResultType cgiParsePostFormInput() { 313 | char *input; 314 | cgiParseResultType result; 315 | if (!cgiContentLength) { 316 | return cgiParseSuccess; 317 | } 318 | input = (char *) malloc(cgiContentLength); 319 | if (!input) { 320 | return cgiParseMemory; 321 | } 322 | if (((int) fread(input, 1, cgiContentLength, cgiIn)) 323 | != cgiContentLength) 324 | { 325 | return cgiParseIO; 326 | } 327 | result = cgiParseFormInput(input, cgiContentLength); 328 | free(input); 329 | return result; 330 | } 331 | 332 | /* 2.0: A virtual datastream supporting putback of 333 | enough characters to handle multipart boundaries easily. 334 | A simple memset(&mp, 0, sizeof(mp)) is suitable initialization. */ 335 | 336 | typedef struct { 337 | /* Buffer for putting characters back */ 338 | char putback[1024]; 339 | /* Position in putback from which next character will be read. 340 | If readPos == writePos, then next character should 341 | come from cgiIn. */ 342 | int readPos; 343 | /* Position in putback to which next character will be put back. 344 | If writePos catches up to readPos, as opposed to the other 345 | way around, the stream no longer functions properly. 346 | Calling code must guarantee that no more than 347 | sizeof(putback) bytes are put back at any given time. */ 348 | int writePos; 349 | /* Offset in the virtual datastream; can be compared 350 | to cgiContentLength */ 351 | int offset; 352 | } mpStream, *mpStreamPtr; 353 | 354 | int mpRead(mpStreamPtr mpp, char *buffer, int len) 355 | { 356 | int ilen = len; 357 | int got = 0; 358 | /* Refuse to read past the declared length in order to 359 | avoid deadlock */ 360 | if (len > (cgiContentLength - mpp->offset)) { 361 | len = cgiContentLength - mpp->offset; 362 | } 363 | while (len) { 364 | if (mpp->readPos != mpp->writePos) { 365 | *buffer++ = mpp->putback[mpp->readPos++]; 366 | mpp->readPos %= sizeof(mpp->putback); 367 | got++; 368 | len--; 369 | } else { 370 | break; 371 | } 372 | } 373 | if (len) { 374 | int fgot = fread(buffer, 1, len, cgiIn); 375 | if (fgot >= 0) { 376 | mpp->offset += (got + fgot); 377 | return got + fgot; 378 | } else if (got > 0) { 379 | mpp->offset += got; 380 | return got; 381 | } else { 382 | /* EOF or error */ 383 | return fgot; 384 | } 385 | } else if (got) { 386 | mpp->offset += got; 387 | return got; 388 | } else if (ilen) { 389 | return EOF; 390 | } else { 391 | /* 2.01 */ 392 | return 0; 393 | } 394 | } 395 | 396 | void mpPutBack(mpStreamPtr mpp, char *data, int len) 397 | { 398 | mpp->offset -= len; 399 | while (len) { 400 | mpp->putback[mpp->writePos++] = *data++; 401 | mpp->writePos %= sizeof(mpp->putback); 402 | len--; 403 | } 404 | } 405 | 406 | /* This function copies the body to outf if it is not null, otherwise to 407 | a newly allocated character buffer at *outP, which will be null 408 | terminated; if both outf and outP are null the body is not stored. 409 | If bodyLengthP is not null, the size of the body in bytes is stored 410 | to *bodyLengthP, not including any terminating null added to *outP. 411 | If 'first' is nonzero, a preceding newline is not expected before 412 | the boundary. If 'first' is zero, a preceding newline is expected. 413 | Upon return mpp is positioned after the boundary and its trailing 414 | newline, if any; if the boundary is followed by -- the next two 415 | characters read after this function returns will be --. Upon error, 416 | if outP is not null, *outP is a null pointer; *bodyLengthP 417 | is set to zero. Returns cgiParseSuccess, cgiParseMemory 418 | or cgiParseIO. */ 419 | 420 | static cgiParseResultType afterNextBoundary(mpStreamPtr mpp, 421 | FILE *outf, 422 | char **outP, 423 | int *bodyLengthP, 424 | int first 425 | ); 426 | 427 | static int readHeaderLine( 428 | mpStreamPtr mpp, 429 | char *attr, 430 | int attrSpace, 431 | char *value, 432 | int valueSpace); 433 | 434 | static void decomposeValue(char *value, 435 | char *mvalue, int mvalueSpace, 436 | char **argNames, 437 | char **argValues, 438 | int argValueSpace); 439 | 440 | static cgiParseResultType getTempFile(FILE **tFile); 441 | 442 | static cgiParseResultType cgiParsePostMultipartInput() { 443 | cgiParseResultType result; 444 | cgiFormEntry *n = 0, *l = 0; 445 | int got; 446 | FILE *outf = 0; 447 | char *out = 0; 448 | mpStream mp; 449 | mpStreamPtr mpp = ∓ 450 | memset(&mp, 0, sizeof(mp)); 451 | if (!cgiContentLength) { 452 | return cgiParseSuccess; 453 | } 454 | /* Read first boundary, including trailing newline */ 455 | result = afterNextBoundary(mpp, 0, 0, 0, 1); 456 | if (result == cgiParseIO) { 457 | /* An empty submission is not necessarily an error */ 458 | return cgiParseSuccess; 459 | } else if (result != cgiParseSuccess) { 460 | return result; 461 | } 462 | while (1) { 463 | char d[1024]; 464 | char fvalue[1024]; 465 | char fname[1024]; 466 | int bodyLength = 0; 467 | char ffileName[1024]; 468 | char fcontentType[1024]; 469 | char attr[1024]; 470 | char value[1024]; 471 | fvalue[0] = 0; 472 | fname[0] = 0; 473 | ffileName[0] = 0; 474 | fcontentType[0] = 0; 475 | out = 0; 476 | outf = 0; 477 | /* Check for EOF */ 478 | got = mpRead(mpp, d, 2); 479 | if (got < 2) { 480 | /* Crude EOF */ 481 | break; 482 | } 483 | if ((d[0] == '-') && (d[1] == '-')) { 484 | /* Graceful EOF */ 485 | break; 486 | } 487 | mpPutBack(mpp, d, 2); 488 | /* Read header lines until end of header */ 489 | while (readHeaderLine( 490 | mpp, attr, sizeof(attr), value, sizeof(value))) 491 | { 492 | char *argNames[3]; 493 | char *argValues[2]; 494 | /* Content-Disposition: form-data; 495 | name="test"; filename="googley.gif" */ 496 | if (cgiStrEqNc(attr, "Content-Disposition")) { 497 | argNames[0] = "name"; 498 | argNames[1] = "filename"; 499 | argNames[2] = 0; 500 | argValues[0] = fname; 501 | argValues[1] = ffileName; 502 | decomposeValue(value, 503 | fvalue, sizeof(fvalue), 504 | argNames, 505 | argValues, 506 | 1024); 507 | } else if (cgiStrEqNc(attr, "Content-Type")) { 508 | argNames[0] = 0; 509 | decomposeValue(value, 510 | fcontentType, sizeof(fcontentType), 511 | argNames, 512 | 0, 513 | 0); 514 | } 515 | } 516 | if (!cgiStrEqNc(fvalue, "form-data")) { 517 | /* Not form data */ 518 | result = afterNextBoundary(mpp, 0, 0, 0, 0); 519 | if (result != cgiParseSuccess) { 520 | /* Lack of a boundary here is an error. */ 521 | return result; 522 | } 523 | continue; 524 | } 525 | /* Body is everything from here until the next 526 | boundary. So, set it aside and move past boundary. 527 | If a filename was submitted as part of the 528 | disposition header, store to a temporary file. 529 | Otherwise, store to a memory buffer (it is 530 | presumably a regular form field). */ 531 | if (strlen(ffileName)) { 532 | if (getTempFile(&outf) != cgiParseSuccess) { 533 | return cgiParseIO; 534 | } 535 | } else { 536 | outf = 0; 537 | } 538 | result = afterNextBoundary(mpp, outf, &out, &bodyLength, 0); 539 | if (result != cgiParseSuccess) { 540 | /* Lack of a boundary here is an error. */ 541 | if (outf) { 542 | fclose(outf); 543 | } 544 | if (out) { 545 | free(out); 546 | } 547 | return result; 548 | } 549 | /* OK, we have a new pair, add it to the list. */ 550 | n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry)); 551 | if (!n) { 552 | goto outOfMemory; 553 | } 554 | memset(n, 0, sizeof(cgiFormEntry)); 555 | /* 2.01: one of numerous new casts required 556 | to please C++ compilers */ 557 | n->attr = (char *) malloc(strlen(fname) + 1); 558 | if (!n->attr) { 559 | goto outOfMemory; 560 | } 561 | strcpy(n->attr, fname); 562 | if (out) { 563 | n->value = out; 564 | out = 0; 565 | } else if (outf) { 566 | n->value = (char *) malloc(1); 567 | if (!n->value) { 568 | goto outOfMemory; 569 | } 570 | n->value[0] = '\0'; 571 | } 572 | n->valueLength = bodyLength; 573 | n->next = 0; 574 | if (!l) { 575 | cgiFormEntryFirst = n; 576 | } else { 577 | l->next = n; 578 | } 579 | n->fileName = (char *) malloc(strlen(ffileName) + 1); 580 | if (!n->fileName) { 581 | goto outOfMemory; 582 | } 583 | strcpy(n->fileName, ffileName); 584 | n->contentType = (char *) malloc(strlen(fcontentType) + 1); 585 | if (!n->contentType) { 586 | goto outOfMemory; 587 | } 588 | strcpy(n->contentType, fcontentType); 589 | 590 | if(outf) 591 | { 592 | n->tFile = fdopen (dup (fileno (outf)), "w+b"); 593 | fclose(outf); 594 | } 595 | 596 | l = n; 597 | } 598 | return cgiParseSuccess; 599 | outOfMemory: 600 | if (n) { 601 | if (n->attr) { 602 | free(n->attr); 603 | } 604 | if (n->value) { 605 | free(n->value); 606 | } 607 | if (n->fileName) { 608 | free(n->fileName); 609 | } 610 | if (n->tFile) { 611 | fclose(n->tFile); 612 | } 613 | if (n->contentType) { 614 | free(n->contentType); 615 | } 616 | free(n); 617 | } 618 | if (out) { 619 | free(out); 620 | } 621 | if (outf) { 622 | fclose(outf); 623 | } 624 | 625 | return cgiParseMemory; 626 | } 627 | 628 | static cgiParseResultType getTempFile(FILE **tFile) 629 | { 630 | /* tfileName must be 1024 bytes to ensure adequacy on 631 | win32 (1024 exceeds the maximum path length and 632 | certainly exceeds observed behavior of _tmpnam). 633 | May as well also be 1024 bytes on Unix, although actual 634 | length is strlen(cgiTempDir) + a short unique pattern. */ 635 | char tfileName[1024]; 636 | 637 | #ifndef WIN32 638 | /* Unix. Use the robust 'mkstemp' function to create 639 | a temporary file that is truly unique, with 640 | permissions that are truly safe. The 641 | fopen-for-write destroys any bogus information 642 | written by potential hackers during the brief 643 | window between the file's creation and the 644 | chmod call (glibc 2.0.6 and lower might 645 | otherwise have allowed this). */ 646 | int outfd; 647 | strcpy(tfileName, cgicTempDir "/cgicXXXXXX"); 648 | outfd = mkstemp(tfileName); 649 | if (outfd == -1) { 650 | return cgiParseIO; 651 | } 652 | close(outfd); 653 | /* Fix the permissions */ 654 | if (chmod(tfileName, 0600) != 0) { 655 | unlink(tfileName); 656 | return cgiParseIO; 657 | } 658 | #else 659 | /* Non-Unix. Do what we can. */ 660 | if (!tmpnam(tfileName)) { 661 | return cgiParseIO; 662 | } 663 | #endif 664 | *tFile = fopen(tfileName, "w+b"); 665 | unlink(tfileName); 666 | return cgiParseSuccess; 667 | } 668 | 669 | 670 | #define APPEND(string, char) \ 671 | { \ 672 | if ((string##Len + 1) < string##Space) { \ 673 | string[string##Len++] = (char); \ 674 | } \ 675 | } 676 | 677 | #define RAPPEND(string, ch) \ 678 | { \ 679 | if ((string##Len + 1) == string##Space) { \ 680 | char *sold = string; \ 681 | string##Space *= 2; \ 682 | string = (char *) realloc(string, string##Space); \ 683 | if (!string) { \ 684 | string = sold; \ 685 | goto outOfMemory; \ 686 | } \ 687 | } \ 688 | string[string##Len++] = (ch); \ 689 | } 690 | 691 | #define BAPPEND(ch) \ 692 | { \ 693 | if (outf) { \ 694 | putc(ch, outf); \ 695 | outLen++; \ 696 | } else if (out) { \ 697 | RAPPEND(out, ch); \ 698 | } \ 699 | } 700 | 701 | cgiParseResultType afterNextBoundary(mpStreamPtr mpp, FILE *outf, char **outP, 702 | int *bodyLengthP, int first) 703 | { 704 | int outLen = 0; 705 | int outSpace = 256; 706 | char *out = 0; 707 | cgiParseResultType result; 708 | int boffset; 709 | int got; 710 | char d[2]; 711 | /* This is large enough, because the buffer into which the 712 | original boundary string is fetched is shorter by more 713 | than four characters due to the space required for 714 | the attribute name */ 715 | char workingBoundaryData[1024]; 716 | char *workingBoundary = workingBoundaryData; 717 | int workingBoundaryLength; 718 | if ((!outf) && (outP)) { 719 | out = (char *) malloc(outSpace); 720 | if (!out) { 721 | goto outOfMemory; 722 | } 723 | } 724 | boffset = 0; 725 | sprintf(workingBoundaryData, "\r\n--%s", cgiMultipartBoundary); 726 | if (first) { 727 | workingBoundary = workingBoundaryData + 2; 728 | } 729 | workingBoundaryLength = strlen(workingBoundary); 730 | while (1) { 731 | got = mpRead(mpp, d, 1); 732 | if (got != 1) { 733 | /* 2.01: cgiParseIO, not cgiFormIO */ 734 | result = cgiParseIO; 735 | goto error; 736 | } 737 | if (d[0] == workingBoundary[boffset]) { 738 | /* We matched the next byte of the boundary. 739 | Keep track of our progress into the 740 | boundary and don't emit anything. */ 741 | boffset++; 742 | if (boffset == workingBoundaryLength) { 743 | break; 744 | } 745 | } else if (boffset > 0) { 746 | /* We matched part, but not all, of the 747 | boundary. Now we have to be careful: 748 | put back all except the first 749 | character and try again. The 750 | real boundary could begin in the 751 | middle of a false match. We can 752 | emit the first character only so far. */ 753 | BAPPEND(workingBoundary[0]); 754 | mpPutBack(mpp, 755 | workingBoundary + 1, boffset - 1); 756 | mpPutBack(mpp, d, 1); 757 | boffset = 0; 758 | } else { 759 | /* Not presently in the middle of a boundary 760 | match; just emit the character. */ 761 | BAPPEND(d[0]); 762 | } 763 | if(outLen > cgicMaxTempSize) { 764 | goto outOfMemory; 765 | } 766 | } 767 | /* Read trailing newline or -- EOF marker. A literal EOF here 768 | would be an error in the input stream. */ 769 | got = mpRead(mpp, d, 2); 770 | if (got != 2) { 771 | result = cgiParseIO; 772 | goto error; 773 | } 774 | if ((d[0] == '\r') && (d[1] == '\n')) { 775 | /* OK, EOL */ 776 | } else if (d[0] == '-') { 777 | /* Probably EOF, but we check for 778 | that later */ 779 | mpPutBack(mpp, d, 2); 780 | } 781 | if (out && outSpace) { 782 | char *oout = out; 783 | out[outLen] = '\0'; 784 | out = (char *) realloc(out, outLen + 1); 785 | if (!out) { 786 | /* Surprising if it happens; and not fatal! We were 787 | just trying to give some space back. We can 788 | keep it if we have to. */ 789 | out = oout; 790 | } 791 | *outP = out; 792 | } 793 | if (bodyLengthP) { 794 | *bodyLengthP = outLen; 795 | } 796 | return cgiParseSuccess; 797 | outOfMemory: 798 | result = cgiParseMemory; 799 | if (outP) { 800 | if (out) { 801 | free(out); 802 | } 803 | *outP = 0; 804 | } 805 | error: 806 | if (bodyLengthP) { 807 | *bodyLengthP = 0; 808 | } 809 | if (out) { 810 | free(out); 811 | } 812 | if (outP) { 813 | *outP = 0; 814 | } 815 | return result; 816 | } 817 | 818 | static void decomposeValue(char *value, 819 | char *mvalue, int mvalueSpace, 820 | char **argNames, 821 | char **argValues, 822 | int argValueSpace) 823 | { 824 | char argName[1024]; 825 | int argNameSpace = sizeof(argName); 826 | int argNameLen = 0; 827 | int mvalueLen = 0; 828 | char *argValue; 829 | int argNum = 0; 830 | while (argNames[argNum]) { 831 | if (argValueSpace) { 832 | argValues[argNum][0] = '\0'; 833 | } 834 | argNum++; 835 | } 836 | while (isspace(*value)) { 837 | value++; 838 | } 839 | /* Quoted mvalue */ 840 | if (*value == '\"') { 841 | value++; 842 | while ((*value) && (*value != '\"')) { 843 | APPEND(mvalue, *value); 844 | value++; 845 | } 846 | while ((*value) && (*value != ';')) { 847 | value++; 848 | } 849 | } else { 850 | /* Unquoted mvalue */ 851 | while ((*value) && (*value != ';')) { 852 | APPEND(mvalue, *value); 853 | value++; 854 | } 855 | } 856 | if (mvalueSpace) { 857 | mvalue[mvalueLen] = '\0'; 858 | } 859 | while (*value == ';') { 860 | int argNum; 861 | int argValueLen = 0; 862 | /* Skip the ; between parameters */ 863 | value++; 864 | /* Now skip leading whitespace */ 865 | while ((*value) && (isspace(*value))) { 866 | value++; 867 | } 868 | /* Now read the parameter name */ 869 | argNameLen = 0; 870 | while ((*value) && (isalnum(*value))) { 871 | APPEND(argName, *value); 872 | value++; 873 | } 874 | if (argNameSpace) { 875 | argName[argNameLen] = '\0'; 876 | } 877 | while ((*value) && isspace(*value)) { 878 | value++; 879 | } 880 | if (*value != '=') { 881 | /* Malformed line */ 882 | return; 883 | } 884 | value++; 885 | while ((*value) && isspace(*value)) { 886 | value++; 887 | } 888 | /* Find the parameter in the argument list, if present */ 889 | argNum = 0; 890 | argValue = 0; 891 | while (argNames[argNum]) { 892 | if (cgiStrEqNc(argName, argNames[argNum])) { 893 | argValue = argValues[argNum]; 894 | break; 895 | } 896 | argNum++; 897 | } 898 | /* Finally, read the parameter value */ 899 | if (*value == '\"') { 900 | value++; 901 | while ((*value) && (*value != '\"')) { 902 | if (argValue) { 903 | APPEND(argValue, *value); 904 | } 905 | value++; 906 | } 907 | while ((*value) && (*value != ';')) { 908 | value++; 909 | } 910 | } else { 911 | /* Unquoted value */ 912 | while ((*value) && (*value != ';')) { 913 | if (argNames[argNum]) { 914 | APPEND(argValue, *value); 915 | } 916 | value++; 917 | } 918 | } 919 | if (argValueSpace) { 920 | if (argValue) { 921 | argValue[argValueLen] = '\0'; 922 | } 923 | } 924 | } 925 | } 926 | 927 | static int readHeaderLine( 928 | mpStreamPtr mpp, 929 | char *attr, 930 | int attrSpace, 931 | char *value, 932 | int valueSpace) 933 | { 934 | int attrLen = 0; 935 | int valueLen = 0; 936 | int valueFound = 0; 937 | while (1) { 938 | char d[1]; 939 | int got = mpRead(mpp, d, 1); 940 | if (got != 1) { 941 | return 0; 942 | } 943 | if (d[0] == '\r') { 944 | got = mpRead(mpp, d, 1); 945 | if (got == 1) { 946 | if (d[0] == '\n') { 947 | /* OK */ 948 | } else { 949 | mpPutBack(mpp, d, 1); 950 | } 951 | } 952 | break; 953 | } else if (d[0] == '\n') { 954 | break; 955 | } else if ((d[0] == ':') && attrLen) { 956 | valueFound = 1; 957 | while (mpRead(mpp, d, 1) == 1) { 958 | if (!isspace(d[0])) { 959 | mpPutBack(mpp, d, 1); 960 | break; 961 | } 962 | } 963 | } else if (!valueFound) { 964 | if (!isspace(*d)) { 965 | if (attrLen < (attrSpace - 1)) { 966 | attr[attrLen++] = *d; 967 | } 968 | } 969 | } else if (valueFound) { 970 | if (valueLen < (valueSpace - 1)) { 971 | value[valueLen++] = *d; 972 | } 973 | } 974 | } 975 | if (attrSpace) { 976 | attr[attrLen] = '\0'; 977 | } 978 | if (valueSpace) { 979 | value[valueLen] = '\0'; 980 | } 981 | if (attrLen && valueLen) { 982 | return 1; 983 | } else { 984 | return 0; 985 | } 986 | } 987 | 988 | static cgiParseResultType cgiParseGetFormInput() { 989 | return cgiParseFormInput(cgiQueryString, cgiContentLength); 990 | } 991 | 992 | typedef enum { 993 | cgiEscapeRest, 994 | cgiEscapeFirst, 995 | cgiEscapeSecond 996 | } cgiEscapeState; 997 | 998 | typedef enum { 999 | cgiUnescapeSuccess, 1000 | cgiUnescapeMemory 1001 | } cgiUnescapeResultType; 1002 | 1003 | static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len); 1004 | 1005 | static cgiParseResultType cgiParseFormInput(char *data, int length) { 1006 | /* Scan for pairs, unescaping and storing them as they are found. */ 1007 | int pos = 0; 1008 | cgiFormEntry *n; 1009 | cgiFormEntry *l = 0; 1010 | while (pos != length) { 1011 | int foundAmp = 0; 1012 | int start = pos; 1013 | int len = 0; 1014 | char *attr; 1015 | char *value; 1016 | while (pos != length) { 1017 | if (data[pos] == '&') { 1018 | /* Tolerate attr name without a value. This will fall through 1019 | and give us an empty value */ 1020 | break; 1021 | } 1022 | if (data[pos] == '=') { 1023 | pos++; 1024 | break; 1025 | } 1026 | pos++; 1027 | len++; 1028 | } 1029 | if (!len) { 1030 | break; 1031 | } 1032 | if (cgiUnescapeChars(&attr, data+start, len) 1033 | != cgiUnescapeSuccess) { 1034 | return cgiParseMemory; 1035 | } 1036 | start = pos; 1037 | len = 0; 1038 | while (pos != length) { 1039 | if (data[pos] == '&') { 1040 | foundAmp = 1; 1041 | pos++; 1042 | break; 1043 | } 1044 | pos++; 1045 | len++; 1046 | } 1047 | /* The last pair probably won't be followed by a &, but 1048 | that's fine, so check for that after accepting it */ 1049 | if (cgiUnescapeChars(&value, data+start, len) 1050 | != cgiUnescapeSuccess) { 1051 | free(attr); 1052 | return cgiParseMemory; 1053 | } 1054 | /* OK, we have a new pair, add it to the list. */ 1055 | n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry)); 1056 | if (!n) { 1057 | free(attr); 1058 | free(value); 1059 | return cgiParseMemory; 1060 | } 1061 | n->attr = attr; 1062 | n->value = value; 1063 | n->valueLength = strlen(n->value); 1064 | n->fileName = (char *) malloc(1); 1065 | if (!n->fileName) { 1066 | free(attr); 1067 | free(value); 1068 | free(n); 1069 | return cgiParseMemory; 1070 | } 1071 | n->fileName[0] = '\0'; 1072 | n->contentType = (char *) malloc(1); 1073 | if (!n->contentType) { 1074 | free(attr); 1075 | free(value); 1076 | free(n->fileName); 1077 | free(n); 1078 | return cgiParseMemory; 1079 | } 1080 | n->contentType[0] = '\0'; 1081 | n->next = 0; 1082 | if (!l) { 1083 | cgiFormEntryFirst = n; 1084 | } else { 1085 | l->next = n; 1086 | } 1087 | l = n; 1088 | if (!foundAmp) { 1089 | break; 1090 | } 1091 | } 1092 | return cgiParseSuccess; 1093 | } 1094 | 1095 | static int cgiHexValue[256]; 1096 | 1097 | cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len) { 1098 | char *s; 1099 | cgiEscapeState escapeState = cgiEscapeRest; 1100 | int escapedValue = 0; 1101 | int srcPos = 0; 1102 | int dstPos = 0; 1103 | s = (char *) malloc(len + 1); 1104 | if (!s) { 1105 | return cgiUnescapeMemory; 1106 | } 1107 | while (srcPos < len) { 1108 | int ch = cp[srcPos]; 1109 | switch (escapeState) { 1110 | case cgiEscapeRest: 1111 | if (ch == '%') { 1112 | escapeState = cgiEscapeFirst; 1113 | } else if (ch == '+') { 1114 | s[dstPos++] = ' '; 1115 | } else { 1116 | s[dstPos++] = ch; 1117 | } 1118 | break; 1119 | case cgiEscapeFirst: 1120 | escapedValue = cgiHexValue[ch] << 4; 1121 | escapeState = cgiEscapeSecond; 1122 | break; 1123 | case cgiEscapeSecond: 1124 | escapedValue += cgiHexValue[ch]; 1125 | s[dstPos++] = escapedValue; 1126 | escapeState = cgiEscapeRest; 1127 | break; 1128 | } 1129 | srcPos++; 1130 | } 1131 | s[dstPos] = '\0'; 1132 | *sp = s; 1133 | return cgiUnescapeSuccess; 1134 | } 1135 | 1136 | static void cgiSetupConstants() { 1137 | int i; 1138 | for (i=0; (i < 256); i++) { 1139 | cgiHexValue[i] = 0; 1140 | } 1141 | cgiHexValue['0'] = 0; 1142 | cgiHexValue['1'] = 1; 1143 | cgiHexValue['2'] = 2; 1144 | cgiHexValue['3'] = 3; 1145 | cgiHexValue['4'] = 4; 1146 | cgiHexValue['5'] = 5; 1147 | cgiHexValue['6'] = 6; 1148 | cgiHexValue['7'] = 7; 1149 | cgiHexValue['8'] = 8; 1150 | cgiHexValue['9'] = 9; 1151 | cgiHexValue['A'] = 10; 1152 | cgiHexValue['B'] = 11; 1153 | cgiHexValue['C'] = 12; 1154 | cgiHexValue['D'] = 13; 1155 | cgiHexValue['E'] = 14; 1156 | cgiHexValue['F'] = 15; 1157 | cgiHexValue['a'] = 10; 1158 | cgiHexValue['b'] = 11; 1159 | cgiHexValue['c'] = 12; 1160 | cgiHexValue['d'] = 13; 1161 | cgiHexValue['e'] = 14; 1162 | cgiHexValue['f'] = 15; 1163 | } 1164 | 1165 | static void cgiFreeResources() { 1166 | cgiFormEntry *c = cgiFormEntryFirst; 1167 | cgiFormEntry *n; 1168 | while (c) { 1169 | n = c->next; 1170 | free(c->attr); 1171 | free(c->value); 1172 | free(c->fileName); 1173 | free(c->contentType); 1174 | if (c->tFile) { 1175 | fclose(c->tFile); 1176 | } 1177 | free(c); 1178 | c = n; 1179 | } 1180 | /* If the cgi environment was restored from a saved environment, 1181 | then these are in allocated space and must also be freed */ 1182 | if (cgiRestored) { 1183 | free(cgiServerSoftware); 1184 | free(cgiServerName); 1185 | free(cgiGatewayInterface); 1186 | free(cgiServerProtocol); 1187 | free(cgiServerPort); 1188 | free(cgiRequestMethod); 1189 | free(cgiPathInfo); 1190 | free(cgiPathTranslated); 1191 | free(cgiScriptName); 1192 | free(cgiQueryString); 1193 | free(cgiRemoteHost); 1194 | free(cgiRemoteAddr); 1195 | free(cgiAuthType); 1196 | free(cgiRemoteUser); 1197 | free(cgiRemoteIdent); 1198 | free(cgiContentType); 1199 | free(cgiAccept); 1200 | free(cgiUserAgent); 1201 | free(cgiReferrer); 1202 | } 1203 | /* 2.0: to clean up the environment for cgiReadEnvironment, 1204 | we must set these correctly */ 1205 | cgiFormEntryFirst = 0; 1206 | cgiRestored = 0; 1207 | } 1208 | 1209 | static cgiFormResultType cgiFormEntryString( 1210 | cgiFormEntry *e, char *result, int max, int newlines); 1211 | 1212 | static cgiFormEntry *cgiFormEntryFindFirst(char *name); 1213 | static cgiFormEntry *cgiFormEntryFindNext(); 1214 | 1215 | cgiFormResultType cgiFormString( 1216 | char *name, char *result, int max) { 1217 | cgiFormEntry *e; 1218 | e = cgiFormEntryFindFirst(name); 1219 | if (!e) { 1220 | strcpy(result, ""); 1221 | return cgiFormNotFound; 1222 | } 1223 | return cgiFormEntryString(e, result, max, 1); 1224 | } 1225 | 1226 | cgiFormResultType cgiFormFileName( 1227 | char *name, char *result, int resultSpace) 1228 | { 1229 | cgiFormEntry *e; 1230 | int resultLen = 0; 1231 | char *s; 1232 | e = cgiFormEntryFindFirst(name); 1233 | if (!e) { 1234 | strcpy(result, ""); 1235 | return cgiFormNotFound; 1236 | } 1237 | s = e->fileName; 1238 | while (*s) { 1239 | APPEND(result, *s); 1240 | s++; 1241 | } 1242 | if (resultSpace) { 1243 | result[resultLen] = '\0'; 1244 | } 1245 | if (!strlen(e->fileName)) { 1246 | return cgiFormNoFileName; 1247 | } else if (((int) strlen(e->fileName)) > (resultSpace - 1)) { 1248 | return cgiFormTruncated; 1249 | } else { 1250 | return cgiFormSuccess; 1251 | } 1252 | } 1253 | 1254 | cgiFormResultType cgiFormFileContentType( 1255 | char *name, char *result, int resultSpace) 1256 | { 1257 | cgiFormEntry *e; 1258 | int resultLen = 0; 1259 | char *s; 1260 | e = cgiFormEntryFindFirst(name); 1261 | if (!e) { 1262 | if (resultSpace) { 1263 | result[0] = '\0'; 1264 | } 1265 | return cgiFormNotFound; 1266 | } 1267 | s = e->contentType; 1268 | while (*s) { 1269 | APPEND(result, *s); 1270 | s++; 1271 | } 1272 | if (resultSpace) { 1273 | result[resultLen] = '\0'; 1274 | } 1275 | if (!strlen(e->contentType)) { 1276 | return cgiFormNoContentType; 1277 | } else if (((int) strlen(e->contentType)) > (resultSpace - 1)) { 1278 | return cgiFormTruncated; 1279 | } else { 1280 | return cgiFormSuccess; 1281 | } 1282 | } 1283 | 1284 | cgiFormResultType cgiFormFileSize( 1285 | char *name, int *sizeP) 1286 | { 1287 | cgiFormEntry *e; 1288 | e = cgiFormEntryFindFirst(name); 1289 | if (!e) { 1290 | if (sizeP) { 1291 | *sizeP = 0; 1292 | } 1293 | return cgiFormNotFound; 1294 | } else if (!e->tFile) { 1295 | if (sizeP) { 1296 | *sizeP = 0; 1297 | } 1298 | return cgiFormNotAFile; 1299 | } else { 1300 | if (sizeP) { 1301 | *sizeP = e->valueLength; 1302 | } 1303 | return cgiFormSuccess; 1304 | } 1305 | } 1306 | 1307 | typedef struct cgiFileStruct { 1308 | FILE *in; 1309 | } cgiFile; 1310 | 1311 | cgiFormResultType cgiFormFileOpen( 1312 | char *name, cgiFilePtr *cfpp) 1313 | { 1314 | cgiFormEntry *e; 1315 | cgiFilePtr cfp; 1316 | e = cgiFormEntryFindFirst(name); 1317 | if (!e) { 1318 | *cfpp = 0; 1319 | return cgiFormNotFound; 1320 | } 1321 | if (!e->tFile) { 1322 | *cfpp = 0; 1323 | return cgiFormNotAFile; 1324 | } 1325 | cfp = (cgiFilePtr) malloc(sizeof(cgiFile)); 1326 | if (!cfp) { 1327 | *cfpp = 0; 1328 | return cgiFormMemory; 1329 | } 1330 | cfp->in = fdopen(dup(fileno(e->tFile)), "rb"); 1331 | rewind(cfp->in); 1332 | if (!cfp->in) { 1333 | free(cfp); 1334 | return cgiFormIO; 1335 | } 1336 | *cfpp = cfp; 1337 | return cgiFormSuccess; 1338 | } 1339 | 1340 | cgiFormResultType cgiFormFileRead( 1341 | cgiFilePtr cfp, char *buffer, 1342 | int bufferSize, int *gotP) 1343 | { 1344 | int got = 0; 1345 | if (!cfp) { 1346 | return cgiFormOpenFailed; 1347 | } 1348 | got = fread(buffer, 1, bufferSize, cfp->in); 1349 | if (got <= 0) { 1350 | return cgiFormEOF; 1351 | } 1352 | *gotP = got; 1353 | return cgiFormSuccess; 1354 | } 1355 | 1356 | cgiFormResultType cgiFormFileClose(cgiFilePtr cfp) 1357 | { 1358 | if (!cfp) { 1359 | return cgiFormOpenFailed; 1360 | } 1361 | fclose(cfp->in); 1362 | free(cfp); 1363 | return cgiFormSuccess; 1364 | } 1365 | 1366 | cgiFormResultType cgiFormStringNoNewlines( 1367 | char *name, char *result, int max) { 1368 | cgiFormEntry *e; 1369 | e = cgiFormEntryFindFirst(name); 1370 | if (!e) { 1371 | strcpy(result, ""); 1372 | return cgiFormNotFound; 1373 | } 1374 | return cgiFormEntryString(e, result, max, 0); 1375 | } 1376 | 1377 | cgiFormResultType cgiFormStringMultiple( 1378 | char *name, char ***result) { 1379 | char **stringArray; 1380 | cgiFormEntry *e; 1381 | int i; 1382 | int total = 0; 1383 | /* Make two passes. One would be more efficient, but this 1384 | function is not commonly used. The select menu and 1385 | radio box functions are faster. */ 1386 | e = cgiFormEntryFindFirst(name); 1387 | if (e != 0) { 1388 | do { 1389 | total++; 1390 | } while ((e = cgiFormEntryFindNext()) != 0); 1391 | } 1392 | stringArray = (char **) malloc(sizeof(char *) * (total + 1)); 1393 | if (!stringArray) { 1394 | *result = 0; 1395 | return cgiFormMemory; 1396 | } 1397 | /* initialize all entries to null; the last will stay that way */ 1398 | for (i=0; (i <= total); i++) { 1399 | stringArray[i] = 0; 1400 | } 1401 | /* Now go get the entries */ 1402 | e = cgiFormEntryFindFirst(name); 1403 | #ifdef CGICDEBUG 1404 | CGICDEBUGSTART 1405 | fprintf(dout, "StringMultiple Beginning\n"); 1406 | CGICDEBUGEND 1407 | #endif /* CGICDEBUG */ 1408 | if (e) { 1409 | i = 0; 1410 | do { 1411 | int max = (int) (strlen(e->value) + 1); 1412 | stringArray[i] = (char *) malloc(max); 1413 | if (stringArray[i] == 0) { 1414 | /* Memory problems */ 1415 | cgiStringArrayFree(stringArray); 1416 | *result = 0; 1417 | return cgiFormMemory; 1418 | } 1419 | strcpy(stringArray[i], e->value); 1420 | cgiFormEntryString(e, stringArray[i], max, 1); 1421 | i++; 1422 | } while ((e = cgiFormEntryFindNext()) != 0); 1423 | *result = stringArray; 1424 | #ifdef CGICDEBUG 1425 | CGICDEBUGSTART 1426 | fprintf(dout, "StringMultiple Succeeding\n"); 1427 | CGICDEBUGEND 1428 | #endif /* CGICDEBUG */ 1429 | return cgiFormSuccess; 1430 | } else { 1431 | *result = stringArray; 1432 | #ifdef CGICDEBUG 1433 | CGICDEBUGSTART 1434 | fprintf(dout, "StringMultiple found nothing\n"); 1435 | CGICDEBUGEND 1436 | #endif /* CGICDEBUG */ 1437 | return cgiFormNotFound; 1438 | } 1439 | } 1440 | 1441 | cgiFormResultType cgiFormStringSpaceNeeded( 1442 | char *name, int *result) { 1443 | cgiFormEntry *e; 1444 | e = cgiFormEntryFindFirst(name); 1445 | if (!e) { 1446 | *result = 1; 1447 | return cgiFormNotFound; 1448 | } 1449 | *result = ((int) strlen(e->value)) + 1; 1450 | return cgiFormSuccess; 1451 | } 1452 | 1453 | static cgiFormResultType cgiFormEntryString( 1454 | cgiFormEntry *e, char *result, int max, int newlines) { 1455 | char *dp, *sp; 1456 | int truncated = 0; 1457 | int len = 0; 1458 | int avail = max-1; 1459 | int crCount = 0; 1460 | int lfCount = 0; 1461 | dp = result; 1462 | sp = e->value; 1463 | while (1) { 1464 | int ch; 1465 | /* 1.07: don't check for available space now. 1466 | We check for it immediately before adding 1467 | an actual character. 1.06 handled the 1468 | trailing null of the source string improperly, 1469 | resulting in a cgiFormTruncated error. */ 1470 | ch = *sp; 1471 | /* Fix the CR/LF, LF, CR nightmare: watch for 1472 | consecutive bursts of CRs and LFs in whatever 1473 | pattern, then actually output the larger number 1474 | of LFs. Consistently sane, yet it still allows 1475 | consecutive blank lines when the user 1476 | actually intends them. */ 1477 | if ((ch == 13) || (ch == 10)) { 1478 | if (ch == 13) { 1479 | crCount++; 1480 | } else { 1481 | lfCount++; 1482 | } 1483 | } else { 1484 | if (crCount || lfCount) { 1485 | int lfsAdd = crCount; 1486 | if (lfCount > crCount) { 1487 | lfsAdd = lfCount; 1488 | } 1489 | /* Stomp all newlines if desired */ 1490 | if (!newlines) { 1491 | lfsAdd = 0; 1492 | } 1493 | while (lfsAdd) { 1494 | if (len >= avail) { 1495 | truncated = 1; 1496 | break; 1497 | } 1498 | *dp = 10; 1499 | dp++; 1500 | lfsAdd--; 1501 | len++; 1502 | } 1503 | crCount = 0; 1504 | lfCount = 0; 1505 | } 1506 | if (ch == '\0') { 1507 | /* The end of the source string */ 1508 | break; 1509 | } 1510 | /* 1.06: check available space before adding 1511 | the character, because a previously added 1512 | LF may have brought us to the limit */ 1513 | if (len >= avail) { 1514 | truncated = 1; 1515 | break; 1516 | } 1517 | *dp = ch; 1518 | dp++; 1519 | len++; 1520 | } 1521 | sp++; 1522 | } 1523 | *dp = '\0'; 1524 | if (truncated) { 1525 | return cgiFormTruncated; 1526 | } else if (!len) { 1527 | return cgiFormEmpty; 1528 | } else { 1529 | return cgiFormSuccess; 1530 | } 1531 | } 1532 | 1533 | static int cgiFirstNonspaceChar(char *s); 1534 | 1535 | cgiFormResultType cgiFormInteger( 1536 | char *name, int *result, int defaultV) { 1537 | cgiFormEntry *e; 1538 | int ch; 1539 | e = cgiFormEntryFindFirst(name); 1540 | if (!e) { 1541 | *result = defaultV; 1542 | return cgiFormNotFound; 1543 | } 1544 | if (!strlen(e->value)) { 1545 | *result = defaultV; 1546 | return cgiFormEmpty; 1547 | } 1548 | ch = cgiFirstNonspaceChar(e->value); 1549 | if (!(isdigit(ch)) && (ch != '-') && (ch != '+')) { 1550 | *result = defaultV; 1551 | return cgiFormBadType; 1552 | } else { 1553 | *result = atoi(e->value); 1554 | return cgiFormSuccess; 1555 | } 1556 | } 1557 | 1558 | cgiFormResultType cgiFormIntegerBounded( 1559 | char *name, int *result, int min, int max, int defaultV) { 1560 | cgiFormResultType error = cgiFormInteger(name, result, defaultV); 1561 | if (error != cgiFormSuccess) { 1562 | return error; 1563 | } 1564 | if (*result < min) { 1565 | *result = min; 1566 | return cgiFormConstrained; 1567 | } 1568 | if (*result > max) { 1569 | *result = max; 1570 | return cgiFormConstrained; 1571 | } 1572 | return cgiFormSuccess; 1573 | } 1574 | 1575 | cgiFormResultType cgiFormDouble( 1576 | char *name, double *result, double defaultV) { 1577 | cgiFormEntry *e; 1578 | int ch; 1579 | e = cgiFormEntryFindFirst(name); 1580 | if (!e) { 1581 | *result = defaultV; 1582 | return cgiFormNotFound; 1583 | } 1584 | if (!strlen(e->value)) { 1585 | *result = defaultV; 1586 | return cgiFormEmpty; 1587 | } 1588 | ch = cgiFirstNonspaceChar(e->value); 1589 | if (!(isdigit(ch)) && (ch != '.') && (ch != '-') && (ch != '+')) { 1590 | *result = defaultV; 1591 | return cgiFormBadType; 1592 | } else { 1593 | *result = atof(e->value); 1594 | return cgiFormSuccess; 1595 | } 1596 | } 1597 | 1598 | cgiFormResultType cgiFormDoubleBounded( 1599 | char *name, double *result, double min, double max, double defaultV) { 1600 | cgiFormResultType error = cgiFormDouble(name, result, defaultV); 1601 | if (error != cgiFormSuccess) { 1602 | return error; 1603 | } 1604 | if (*result < min) { 1605 | *result = min; 1606 | return cgiFormConstrained; 1607 | } 1608 | if (*result > max) { 1609 | *result = max; 1610 | return cgiFormConstrained; 1611 | } 1612 | return cgiFormSuccess; 1613 | } 1614 | 1615 | cgiFormResultType cgiFormSelectSingle( 1616 | char *name, char **choicesText, int choicesTotal, 1617 | int *result, int defaultV) 1618 | { 1619 | cgiFormEntry *e; 1620 | int i; 1621 | e = cgiFormEntryFindFirst(name); 1622 | #ifdef CGICDEBUG 1623 | CGICDEBUGSTART 1624 | fprintf(dout, "%d\n", (int) e); 1625 | CGICDEBUGEND 1626 | #endif /* CGICDEBUG */ 1627 | if (!e) { 1628 | *result = defaultV; 1629 | return cgiFormNotFound; 1630 | } 1631 | for (i=0; (i < choicesTotal); i++) { 1632 | #ifdef CGICDEBUG 1633 | CGICDEBUGSTART 1634 | fprintf(dout, "%s %s\n", choicesText[i], e->value); 1635 | CGICDEBUGEND 1636 | #endif /* CGICDEBUG */ 1637 | if (cgiStrEq(choicesText[i], e->value)) { 1638 | #ifdef CGICDEBUG 1639 | CGICDEBUGSTART 1640 | fprintf(dout, "MATCH\n"); 1641 | CGICDEBUGEND 1642 | #endif /* CGICDEBUG */ 1643 | *result = i; 1644 | return cgiFormSuccess; 1645 | } 1646 | } 1647 | *result = defaultV; 1648 | return cgiFormNoSuchChoice; 1649 | } 1650 | 1651 | cgiFormResultType cgiFormSelectMultiple( 1652 | char *name, char **choicesText, int choicesTotal, 1653 | int *result, int *invalid) 1654 | { 1655 | cgiFormEntry *e; 1656 | int i; 1657 | int hits = 0; 1658 | int invalidE = 0; 1659 | for (i=0; (i < choicesTotal); i++) { 1660 | result[i] = 0; 1661 | } 1662 | e = cgiFormEntryFindFirst(name); 1663 | if (!e) { 1664 | *invalid = invalidE; 1665 | return cgiFormNotFound; 1666 | } 1667 | do { 1668 | int hit = 0; 1669 | for (i=0; (i < choicesTotal); i++) { 1670 | if (cgiStrEq(choicesText[i], e->value)) { 1671 | result[i] = 1; 1672 | hits++; 1673 | hit = 1; 1674 | break; 1675 | } 1676 | } 1677 | if (!(hit)) { 1678 | invalidE++; 1679 | } 1680 | } while ((e = cgiFormEntryFindNext()) != 0); 1681 | 1682 | *invalid = invalidE; 1683 | 1684 | if (hits) { 1685 | return cgiFormSuccess; 1686 | } else { 1687 | return cgiFormNotFound; 1688 | } 1689 | } 1690 | 1691 | cgiFormResultType cgiFormCheckboxSingle( 1692 | char *name) 1693 | { 1694 | cgiFormEntry *e; 1695 | e = cgiFormEntryFindFirst(name); 1696 | if (!e) { 1697 | return cgiFormNotFound; 1698 | } 1699 | return cgiFormSuccess; 1700 | } 1701 | 1702 | extern cgiFormResultType cgiFormCheckboxMultiple( 1703 | char *name, char **valuesText, int valuesTotal, 1704 | int *result, int *invalid) 1705 | { 1706 | /* Implementation is identical to cgiFormSelectMultiple. */ 1707 | return cgiFormSelectMultiple(name, valuesText, 1708 | valuesTotal, result, invalid); 1709 | } 1710 | 1711 | cgiFormResultType cgiFormRadio( 1712 | char *name, 1713 | char **valuesText, int valuesTotal, int *result, int defaultV) 1714 | { 1715 | /* Implementation is identical to cgiFormSelectSingle. */ 1716 | return cgiFormSelectSingle(name, valuesText, valuesTotal, 1717 | result, defaultV); 1718 | } 1719 | 1720 | cgiFormResultType cgiCookieString( 1721 | char *name, 1722 | char *value, 1723 | int space) 1724 | { 1725 | char *p = cgiCookie; 1726 | while (*p) { 1727 | char *n = name; 1728 | /* 2.02: if cgiCookie is exactly equal to name, this 1729 | can cause an overrun. The server probably wouldn't 1730 | allow it, since a name without values makes no sense 1731 | -- but then again it might not check, so this is a 1732 | genuine security concern. Thanks to Nicolas 1733 | Tomadakis. */ 1734 | while (*p == *n) { 1735 | if ((*p == '\0') && (*n == '\0')) { 1736 | /* Malformed cookie header from client */ 1737 | return cgiFormNotFound; 1738 | } 1739 | p++; 1740 | n++; 1741 | } 1742 | if ((!*n) && (*p == '=')) { 1743 | p++; 1744 | while ((*p != ';') && (*p != '\0') && 1745 | (space > 1)) 1746 | { 1747 | *value = *p; 1748 | value++; 1749 | p++; 1750 | space--; 1751 | } 1752 | if (space > 0) { 1753 | *value = '\0'; 1754 | } 1755 | /* Correct parens: 2.02. Thanks to 1756 | Mathieu Villeneuve-Belair. */ 1757 | if (!(((*p) == ';') || ((*p) == '\0'))) 1758 | { 1759 | return cgiFormTruncated; 1760 | } else { 1761 | return cgiFormSuccess; 1762 | } 1763 | } else { 1764 | /* Skip to next cookie */ 1765 | while (*p) { 1766 | if (*p == ';') { 1767 | break; 1768 | } 1769 | p++; 1770 | } 1771 | if (!*p) { 1772 | /* 2.01: default to empty */ 1773 | if (space) { 1774 | *value = '\0'; 1775 | } 1776 | return cgiFormNotFound; 1777 | } 1778 | p++; 1779 | /* Allow whitespace after semicolon */ 1780 | while ((*p) && isspace(*p)) { 1781 | p++; 1782 | } 1783 | } 1784 | } 1785 | /* 2.01: actually the above loop never terminates except 1786 | with a return, but do this to placate gcc */ 1787 | /* Actually, it can, so this is real. */ 1788 | if (space) { 1789 | *value = '\0'; 1790 | } 1791 | return cgiFormNotFound; 1792 | } 1793 | 1794 | cgiFormResultType cgiCookieInteger( 1795 | char *name, 1796 | int *result, 1797 | int defaultV) 1798 | { 1799 | char buffer[256]; 1800 | cgiFormResultType r = 1801 | cgiCookieString(name, buffer, sizeof(buffer)); 1802 | if (r != cgiFormSuccess) { 1803 | *result = defaultV; 1804 | } else { 1805 | *result = atoi(buffer); 1806 | } 1807 | return r; 1808 | } 1809 | 1810 | void cgiHeaderCookieSetInteger(char *name, int value, int secondsToLive, 1811 | char *path, char *domain) 1812 | { 1813 | char svalue[256]; 1814 | sprintf(svalue, "%d", value); 1815 | cgiHeaderCookieSet(name, svalue, secondsToLive, path, domain, 0); 1816 | } 1817 | 1818 | static char *days[] = { 1819 | "Sun", 1820 | "Mon", 1821 | "Tue", 1822 | "Wed", 1823 | "Thu", 1824 | "Fri", 1825 | "Sat" 1826 | }; 1827 | 1828 | static char *months[] = { 1829 | "Jan", 1830 | "Feb", 1831 | "Mar", 1832 | "Apr", 1833 | "May", 1834 | "Jun", 1835 | "Jul", 1836 | "Aug", 1837 | "Sep", 1838 | "Oct", 1839 | "Nov", 1840 | "Dec" 1841 | }; 1842 | 1843 | void cgiHeaderCookieSet(char *name, char *value, int secondsToLive, 1844 | char *path, char *domain, int options) 1845 | { 1846 | /* cgic 2.02: simpler and more widely compatible implementation. 1847 | Thanks to Chunfu Lai. 1848 | cgic 2.03: yes, but it didn't work. Reimplemented by 1849 | Thomas Boutell. ; after last element was a bug. 1850 | Examples of real world cookies that really work: 1851 | Set-Cookie: MSNADS=UM=; domain=.slate.com; 1852 | expires=Tue, 26-Apr-2022 19:00:00 GMT; path=/ 1853 | Set-Cookie: MC1=V=3&ID=b5bc08af2b8a43ff85fcb5efd8b238f0; 1854 | domain=.slate.com; expires=Mon, 04-Oct-2021 19:00:00 GMT; path=/ 1855 | */ 1856 | time_t now; 1857 | time_t then; 1858 | struct tm *gt; 1859 | time(&now); 1860 | then = now + secondsToLive; 1861 | gt = gmtime(&then); 1862 | fprintf(cgiOut, 1863 | "Set-Cookie: %s=%s; domain=%s; expires=%s, %02d-%s-%04d %02d:%02d:%02d GMT; path=%s%s%s%s\r\n", 1864 | name, value, domain, 1865 | days[gt->tm_wday], 1866 | gt->tm_mday, 1867 | months[gt->tm_mon], 1868 | gt->tm_year + 1900, 1869 | gt->tm_hour, 1870 | gt->tm_min, 1871 | gt->tm_sec, 1872 | path, 1873 | ((options & cgiCookieSecure) ? "; Secure" : ""), 1874 | ((options & cgiCookieHttpOnly) ? "; HttpOnly" : ""), 1875 | ((options & cgiCookieSameSiteStrict) ? "; SameSite=Strict" : "")); 1876 | } 1877 | 1878 | void cgiHeaderCookieSetString(char *name, char *value, int secondsToLive, 1879 | char *path, char *domain) 1880 | { 1881 | cgiHeaderCookieSet(name, value, secondsToLive, path, domain, 0); 1882 | } 1883 | 1884 | void cgiHeaderLocation(char *redirectUrl) { 1885 | fprintf(cgiOut, "Location: %s\r\n\r\n", redirectUrl); 1886 | } 1887 | 1888 | void cgiHeaderStatus(int status, char *statusMessage) { 1889 | fprintf(cgiOut, "Status: %d %s\r\n\r\n", status, statusMessage); 1890 | } 1891 | 1892 | void cgiHeaderContentType(char *mimeType) { 1893 | fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType); 1894 | } 1895 | 1896 | static int cgiWriteString(FILE *out, char *s); 1897 | 1898 | static int cgiWriteInt(FILE *out, int i); 1899 | 1900 | #define CGIC_VERSION "2.0" 1901 | 1902 | cgiEnvironmentResultType cgiWriteEnvironment(char *filename) { 1903 | FILE *out; 1904 | cgiFormEntry *e; 1905 | /* Be sure to open in binary mode */ 1906 | out = fopen(filename, "wb"); 1907 | if (!out) { 1908 | /* Can't create file */ 1909 | return cgiEnvironmentIO; 1910 | } 1911 | if (!cgiWriteString(out, "CGIC2.0")) { 1912 | goto error; 1913 | } 1914 | if (!cgiWriteString(out, cgiServerSoftware)) { 1915 | goto error; 1916 | } 1917 | if (!cgiWriteString(out, cgiServerName)) { 1918 | goto error; 1919 | } 1920 | if (!cgiWriteString(out, cgiGatewayInterface)) { 1921 | goto error; 1922 | } 1923 | if (!cgiWriteString(out, cgiServerProtocol)) { 1924 | goto error; 1925 | } 1926 | if (!cgiWriteString(out, cgiServerPort)) { 1927 | goto error; 1928 | } 1929 | if (!cgiWriteString(out, cgiRequestMethod)) { 1930 | goto error; 1931 | } 1932 | if (!cgiWriteString(out, cgiPathInfo)) { 1933 | goto error; 1934 | } 1935 | if (!cgiWriteString(out, cgiPathTranslated)) { 1936 | goto error; 1937 | } 1938 | if (!cgiWriteString(out, cgiScriptName)) { 1939 | goto error; 1940 | } 1941 | if (!cgiWriteString(out, cgiQueryString)) { 1942 | goto error; 1943 | } 1944 | if (!cgiWriteString(out, cgiRemoteHost)) { 1945 | goto error; 1946 | } 1947 | if (!cgiWriteString(out, cgiRemoteAddr)) { 1948 | goto error; 1949 | } 1950 | if (!cgiWriteString(out, cgiAuthType)) { 1951 | goto error; 1952 | } 1953 | if (!cgiWriteString(out, cgiRemoteUser)) { 1954 | goto error; 1955 | } 1956 | if (!cgiWriteString(out, cgiRemoteIdent)) { 1957 | goto error; 1958 | } 1959 | if (!cgiWriteString(out, cgiContentType)) { 1960 | goto error; 1961 | } 1962 | if (!cgiWriteString(out, cgiAccept)) { 1963 | goto error; 1964 | } 1965 | if (!cgiWriteString(out, cgiUserAgent)) { 1966 | goto error; 1967 | } 1968 | if (!cgiWriteString(out, cgiReferrer)) { 1969 | goto error; 1970 | } 1971 | if (!cgiWriteString(out, cgiCookie)) { 1972 | goto error; 1973 | } 1974 | if (!cgiWriteInt(out, cgiContentLength)) { 1975 | goto error; 1976 | } 1977 | e = cgiFormEntryFirst; 1978 | while (e) { 1979 | cgiFilePtr fp; 1980 | if (!cgiWriteString(out, e->attr)) { 1981 | goto error; 1982 | } 1983 | if (!cgiWriteString(out, e->value)) { 1984 | goto error; 1985 | } 1986 | /* New 2.0 fields and file uploads */ 1987 | if (!cgiWriteString(out, e->fileName)) { 1988 | goto error; 1989 | } 1990 | if (!cgiWriteString(out, e->contentType)) { 1991 | goto error; 1992 | } 1993 | if (!cgiWriteInt(out, e->valueLength)) { 1994 | goto error; 1995 | } 1996 | if (cgiFormFileOpen(e->attr, &fp) == cgiFormSuccess) { 1997 | char buffer[1024]; 1998 | int got; 1999 | if (!cgiWriteInt(out, 1)) { 2000 | cgiFormFileClose(fp); 2001 | goto error; 2002 | } 2003 | while (cgiFormFileRead(fp, buffer, 2004 | sizeof(buffer), &got) == cgiFormSuccess) 2005 | { 2006 | if (((int) fwrite(buffer, 1, got, out)) != got) { 2007 | cgiFormFileClose(fp); 2008 | goto error; 2009 | } 2010 | } 2011 | if (cgiFormFileClose(fp) != cgiFormSuccess) { 2012 | goto error; 2013 | } 2014 | } else { 2015 | if (!cgiWriteInt(out, 0)) { 2016 | goto error; 2017 | } 2018 | } 2019 | e = e->next; 2020 | } 2021 | fclose(out); 2022 | return cgiEnvironmentSuccess; 2023 | error: 2024 | fclose(out); 2025 | /* If this function is not defined in your system, 2026 | you must substitute the appropriate 2027 | file-deletion function. */ 2028 | unlink(filename); 2029 | return cgiEnvironmentIO; 2030 | } 2031 | 2032 | static int cgiWriteString(FILE *out, char *s) { 2033 | int len = (int) strlen(s); 2034 | cgiWriteInt(out, len); 2035 | if (((int) fwrite(s, 1, len, out)) != len) { 2036 | return 0; 2037 | } 2038 | return 1; 2039 | } 2040 | 2041 | static int cgiWriteInt(FILE *out, int i) { 2042 | if (!fwrite(&i, sizeof(int), 1, out)) { 2043 | return 0; 2044 | } 2045 | return 1; 2046 | } 2047 | 2048 | static int cgiReadString(FILE *out, char **s); 2049 | 2050 | static int cgiReadInt(FILE *out, int *i); 2051 | 2052 | cgiEnvironmentResultType cgiReadEnvironment(char *filename) { 2053 | FILE *in; 2054 | cgiFormEntry *e = 0, *p; 2055 | char *version; 2056 | /* Prevent compiler warnings */ 2057 | cgiEnvironmentResultType result = cgiEnvironmentIO; 2058 | /* Free any existing data first */ 2059 | cgiFreeResources(); 2060 | /* Be sure to open in binary mode */ 2061 | in = fopen(filename, "rb"); 2062 | if (!in) { 2063 | /* Can't access file */ 2064 | return cgiEnvironmentIO; 2065 | } 2066 | if (!cgiReadString(in, &version)) { 2067 | goto error; 2068 | } 2069 | if (strcmp(version, "CGIC" CGIC_VERSION)) { 2070 | /* 2.02: Merezko Oleg */ 2071 | free(version); 2072 | return cgiEnvironmentWrongVersion; 2073 | } 2074 | /* 2.02: Merezko Oleg */ 2075 | free(version); 2076 | if (!cgiReadString(in, &cgiServerSoftware)) { 2077 | goto error; 2078 | } 2079 | if (!cgiReadString(in, &cgiServerName)) { 2080 | goto error; 2081 | } 2082 | if (!cgiReadString(in, &cgiGatewayInterface)) { 2083 | goto error; 2084 | } 2085 | if (!cgiReadString(in, &cgiServerProtocol)) { 2086 | goto error; 2087 | } 2088 | if (!cgiReadString(in, &cgiServerPort)) { 2089 | goto error; 2090 | } 2091 | if (!cgiReadString(in, &cgiRequestMethod)) { 2092 | goto error; 2093 | } 2094 | if (!cgiReadString(in, &cgiPathInfo)) { 2095 | goto error; 2096 | } 2097 | if (!cgiReadString(in, &cgiPathTranslated)) { 2098 | goto error; 2099 | } 2100 | if (!cgiReadString(in, &cgiScriptName)) { 2101 | goto error; 2102 | } 2103 | if (!cgiReadString(in, &cgiQueryString)) { 2104 | goto error; 2105 | } 2106 | if (!cgiReadString(in, &cgiRemoteHost)) { 2107 | goto error; 2108 | } 2109 | if (!cgiReadString(in, &cgiRemoteAddr)) { 2110 | goto error; 2111 | } 2112 | if (!cgiReadString(in, &cgiAuthType)) { 2113 | goto error; 2114 | } 2115 | if (!cgiReadString(in, &cgiRemoteUser)) { 2116 | goto error; 2117 | } 2118 | if (!cgiReadString(in, &cgiRemoteIdent)) { 2119 | goto error; 2120 | } 2121 | if (!cgiReadString(in, &cgiContentType)) { 2122 | goto error; 2123 | } 2124 | if (!cgiReadString(in, &cgiAccept)) { 2125 | goto error; 2126 | } 2127 | if (!cgiReadString(in, &cgiUserAgent)) { 2128 | goto error; 2129 | } 2130 | if (!cgiReadString(in, &cgiReferrer)) { 2131 | goto error; 2132 | } 2133 | /* 2.0 */ 2134 | if (!cgiReadString(in, &cgiCookie)) { 2135 | goto error; 2136 | } 2137 | if (!cgiReadInt(in, &cgiContentLength)) { 2138 | goto error; 2139 | } 2140 | p = 0; 2141 | while (1) { 2142 | int fileFlag; 2143 | e = (cgiFormEntry *) calloc(1, sizeof(cgiFormEntry)); 2144 | if (!e) { 2145 | cgiFreeResources(); 2146 | fclose(in); 2147 | return cgiEnvironmentMemory; 2148 | } 2149 | memset(e, 0, sizeof(cgiFormEntry)); 2150 | if (!cgiReadString(in, &e->attr)) { 2151 | /* This means we've reached the end of the list. */ 2152 | /* 2.02: thanks to Merezko Oleg */ 2153 | free(e); 2154 | break; 2155 | } 2156 | if (!cgiReadString(in, &e->value)) { 2157 | goto outOfMemory; 2158 | } 2159 | if (!cgiReadString(in, &e->fileName)) { 2160 | goto outOfMemory; 2161 | } 2162 | if (!cgiReadString(in, &e->contentType)) { 2163 | goto outOfMemory; 2164 | } 2165 | if (!cgiReadInt(in, &e->valueLength)) { 2166 | goto outOfMemory; 2167 | } 2168 | if (!cgiReadInt(in, &fileFlag)) { 2169 | goto outOfMemory; 2170 | } 2171 | if (fileFlag) { 2172 | char buffer[1024]; 2173 | FILE *out = NULL; 2174 | int got; 2175 | int len = e->valueLength; 2176 | if (getTempFile(&out) 2177 | != cgiParseSuccess || !out) 2178 | { 2179 | result = cgiEnvironmentIO; 2180 | goto error; 2181 | } 2182 | while (len > 0) { 2183 | /* 2.01: try is a bad variable name in 2184 | C++, and it wasn't being used 2185 | properly either */ 2186 | int tryr = len; 2187 | if (tryr > ((int) sizeof(buffer))) { 2188 | tryr = sizeof(buffer); 2189 | } 2190 | got = fread(buffer, 1, tryr, in); 2191 | if (got <= 0) { 2192 | result = cgiEnvironmentIO; 2193 | fclose(out); 2194 | goto error; 2195 | } 2196 | if (((int) fwrite(buffer, 1, got, out)) != got) { 2197 | result = cgiEnvironmentIO; 2198 | fclose(out); 2199 | goto error; 2200 | } 2201 | len -= got; 2202 | } 2203 | /* cgic 2.05: should be fclose not rewind */ 2204 | e->tFile = out; 2205 | } else { 2206 | e->tFile = NULL; 2207 | } 2208 | e->next = 0; 2209 | if (p) { 2210 | p->next = e; 2211 | } else { 2212 | cgiFormEntryFirst = e; 2213 | } 2214 | p = e; 2215 | } 2216 | fclose(in); 2217 | cgiRestored = 1; 2218 | return cgiEnvironmentSuccess; 2219 | outOfMemory: 2220 | result = cgiEnvironmentMemory; 2221 | error: 2222 | cgiFreeResources(); 2223 | fclose(in); 2224 | if (e) { 2225 | if (e->attr) { 2226 | free(e->attr); 2227 | } 2228 | if (e->value) { 2229 | free(e->value); 2230 | } 2231 | if (e->fileName) { 2232 | free(e->fileName); 2233 | } 2234 | if (e->contentType) { 2235 | free(e->contentType); 2236 | } 2237 | if (e->tFile) { 2238 | fclose(e->tFile); 2239 | } 2240 | free(e); 2241 | } 2242 | return result; 2243 | } 2244 | 2245 | static int cgiReadString(FILE *in, char **s) { 2246 | int len; 2247 | /* 2.0 fix: test cgiReadInt for failure! */ 2248 | if (!cgiReadInt(in, &len)) { 2249 | return 0; 2250 | } 2251 | *s = (char *) malloc(len + 1); 2252 | if (!(*s)) { 2253 | return 0; 2254 | } 2255 | if (((int) fread(*s, 1, len, in)) != len) { 2256 | return 0; 2257 | } 2258 | (*s)[len] = '\0'; 2259 | return 1; 2260 | } 2261 | 2262 | static int cgiReadInt(FILE *out, int *i) { 2263 | if (!fread(i, sizeof(int), 1, out)) { 2264 | return 0; 2265 | } 2266 | return 1; 2267 | } 2268 | 2269 | static int cgiStrEqNc(char *s1, char *s2) { 2270 | while(1) { 2271 | if (!(*s1)) { 2272 | if (!(*s2)) { 2273 | return 1; 2274 | } else { 2275 | return 0; 2276 | } 2277 | } else if (!(*s2)) { 2278 | return 0; 2279 | } 2280 | if (isalpha(*s1)) { 2281 | if (tolower(*s1) != tolower(*s2)) { 2282 | return 0; 2283 | } 2284 | } else if ((*s1) != (*s2)) { 2285 | return 0; 2286 | } 2287 | s1++; 2288 | s2++; 2289 | } 2290 | } 2291 | 2292 | static int cgiStrBeginsNc(char *s1, char *s2) { 2293 | while(1) { 2294 | if (!(*s2)) { 2295 | return 1; 2296 | } else if (!(*s1)) { 2297 | return 0; 2298 | } 2299 | if (isalpha(*s1)) { 2300 | if (tolower(*s1) != tolower(*s2)) { 2301 | return 0; 2302 | } 2303 | } else if ((*s1) != (*s2)) { 2304 | return 0; 2305 | } 2306 | s1++; 2307 | s2++; 2308 | } 2309 | } 2310 | 2311 | static char *cgiFindTarget = 0; 2312 | static cgiFormEntry *cgiFindPos = 0; 2313 | 2314 | static cgiFormEntry *cgiFormEntryFindFirst(char *name) { 2315 | cgiFindTarget = name; 2316 | cgiFindPos = cgiFormEntryFirst; 2317 | return cgiFormEntryFindNext(); 2318 | } 2319 | 2320 | static cgiFormEntry *cgiFormEntryFindNext() { 2321 | while (cgiFindPos) { 2322 | cgiFormEntry *c = cgiFindPos; 2323 | cgiFindPos = c->next; 2324 | if (!strcmp(c -> attr, cgiFindTarget)) { 2325 | return c; 2326 | } 2327 | } 2328 | return 0; 2329 | } 2330 | 2331 | static int cgiFirstNonspaceChar(char *s) { 2332 | int len = strspn(s, " \n\r\t"); 2333 | return s[len]; 2334 | } 2335 | 2336 | void cgiStringArrayFree(char **stringArray) { 2337 | char *p; 2338 | char **arrayItself = stringArray; 2339 | p = *stringArray; 2340 | while (p) { 2341 | free(p); 2342 | stringArray++; 2343 | p = *stringArray; 2344 | } 2345 | /* 2.0: free the array itself! */ 2346 | free(arrayItself); 2347 | } 2348 | 2349 | cgiFormResultType cgiCookies(char ***result) { 2350 | char **stringArray; 2351 | int i; 2352 | int total = 0; 2353 | char *p; 2354 | char *n; 2355 | p = cgiCookie; 2356 | while (*p) { 2357 | if (*p == '=') { 2358 | total++; 2359 | } 2360 | p++; 2361 | } 2362 | stringArray = (char **) malloc(sizeof(char *) * (total + 1)); 2363 | if (!stringArray) { 2364 | *result = 0; 2365 | return cgiFormMemory; 2366 | } 2367 | /* initialize all entries to null; the last will stay that way */ 2368 | for (i=0; (i <= total); i++) { 2369 | stringArray[i] = 0; 2370 | } 2371 | i = 0; 2372 | p = cgiCookie; 2373 | while (*p) { 2374 | while (*p && isspace(*p)) { 2375 | p++; 2376 | } 2377 | n = p; 2378 | while (*p && (*p != '=')) { 2379 | p++; 2380 | } 2381 | if (p != n) { 2382 | stringArray[i] = (char *) malloc((p - n) + 1); 2383 | if (!stringArray[i]) { 2384 | cgiStringArrayFree(stringArray); 2385 | *result = 0; 2386 | return cgiFormMemory; 2387 | } 2388 | memcpy(stringArray[i], n, p - n); 2389 | stringArray[i][p - n] = '\0'; 2390 | i++; 2391 | } 2392 | while (*p && (*p != ';')) { 2393 | p++; 2394 | } 2395 | if (!*p) { 2396 | break; 2397 | } 2398 | if (*p == ';') { 2399 | p++; 2400 | } 2401 | } 2402 | *result = stringArray; 2403 | return cgiFormSuccess; 2404 | } 2405 | 2406 | cgiFormResultType cgiFormEntries(char ***result) { 2407 | char **stringArray; 2408 | cgiFormEntry *e, *pe; 2409 | int i; 2410 | int total = 0; 2411 | e = cgiFormEntryFirst; 2412 | while (e) { 2413 | /* Don't count a field name more than once if 2414 | multiple values happen to be present for it */ 2415 | pe = cgiFormEntryFirst; 2416 | while (pe != e) { 2417 | if (!strcmp(e->attr, pe->attr)) { 2418 | goto skipSecondValue; 2419 | } 2420 | pe = pe->next; 2421 | } 2422 | total++; 2423 | skipSecondValue: 2424 | e = e->next; 2425 | } 2426 | stringArray = (char **) malloc(sizeof(char *) * (total + 1)); 2427 | if (!stringArray) { 2428 | *result = 0; 2429 | return cgiFormMemory; 2430 | } 2431 | /* initialize all entries to null; the last will stay that way */ 2432 | for (i=0; (i <= total); i++) { 2433 | stringArray[i] = 0; 2434 | } 2435 | /* Now go get the entries */ 2436 | e = cgiFormEntryFirst; 2437 | i = 0; 2438 | while (e) { 2439 | size_t space; 2440 | /* Don't return a field name more than once if 2441 | multiple values happen to be present for it */ 2442 | pe = cgiFormEntryFirst; 2443 | while (pe != e) { 2444 | if (!strcmp(e->attr, pe->attr)) { 2445 | goto skipSecondValue2; 2446 | } 2447 | pe = pe->next; 2448 | } 2449 | space = strlen(e->attr) + 1; 2450 | stringArray[i] = (char *) malloc(space); 2451 | if (stringArray[i] == 0) { 2452 | /* Memory problems */ 2453 | cgiStringArrayFree(stringArray); 2454 | *result = 0; 2455 | return cgiFormMemory; 2456 | } 2457 | strcpy(stringArray[i], e->attr); 2458 | i++; 2459 | skipSecondValue2: 2460 | e = e->next; 2461 | } 2462 | *result = stringArray; 2463 | return cgiFormSuccess; 2464 | } 2465 | 2466 | #define TRYPUTC(ch) \ 2467 | { \ 2468 | if (putc((ch), cgiOut) == EOF) { \ 2469 | return cgiFormIO; \ 2470 | } \ 2471 | } 2472 | 2473 | cgiFormResultType cgiHtmlEscapeData(const char *data, int len) 2474 | { 2475 | while (len--) { 2476 | if (*data == '<') { 2477 | TRYPUTC('&'); 2478 | TRYPUTC('l'); 2479 | TRYPUTC('t'); 2480 | TRYPUTC(';'); 2481 | } else if (*data == '&') { 2482 | TRYPUTC('&'); 2483 | TRYPUTC('a'); 2484 | TRYPUTC('m'); 2485 | TRYPUTC('p'); 2486 | TRYPUTC(';'); 2487 | } else if (*data == '>') { 2488 | TRYPUTC('&'); 2489 | TRYPUTC('g'); 2490 | TRYPUTC('t'); 2491 | TRYPUTC(';'); 2492 | } else { 2493 | TRYPUTC(*data); 2494 | } 2495 | data++; 2496 | } 2497 | return cgiFormSuccess; 2498 | } 2499 | 2500 | cgiFormResultType cgiHtmlEscape(const char *s) 2501 | { 2502 | return cgiHtmlEscapeData(s, (int) strlen(s)); 2503 | } 2504 | 2505 | /* Output data with the " character HTML-escaped, and no 2506 | other characters escaped. This is useful when outputting 2507 | the contents of a tag attribute such as 'href' or 'src'. 2508 | 'data' is not null-terminated; 'len' is the number of 2509 | bytes in 'data'. Returns cgiFormIO in the event 2510 | of error, cgiFormSuccess otherwise. */ 2511 | cgiFormResultType cgiValueEscapeData(const char *data, int len) 2512 | { 2513 | while (len--) { 2514 | if (*data == '\"') { 2515 | TRYPUTC('&'); 2516 | TRYPUTC('#'); 2517 | TRYPUTC('3'); 2518 | TRYPUTC('4'); 2519 | TRYPUTC(';'); 2520 | } else { 2521 | TRYPUTC(*data); 2522 | } 2523 | data++; 2524 | } 2525 | return cgiFormSuccess; 2526 | } 2527 | 2528 | cgiFormResultType cgiValueEscape(const char *s) 2529 | { 2530 | return cgiValueEscapeData(s, (int) strlen(s)); 2531 | } 2532 | 2533 | 2534 | #ifdef UNIT_TEST 2535 | 2536 | static void unitTestAssert(const int value, const char *message); 2537 | 2538 | static int unitTest() { 2539 | char *input = "one=1&two=2&empty1&four=4&empty2"; 2540 | cgiFormEntry *e; 2541 | cgiParseResultType result = cgiParseFormInput(input, strlen(input)); 2542 | unitTestAssert(result == cgiParseSuccess, "cgiParseFormInput did not return cgiParseSuccess"); 2543 | e = cgiFormEntryFirst; 2544 | unitTestAssert(!!e, "first entry missing"); 2545 | unitTestAssert(!strcmp(e->attr, "one"), "first entry name is not one"); 2546 | unitTestAssert(!strcmp(e->value, "1"), "first entry value is not 1"); 2547 | e = e->next; 2548 | unitTestAssert(!!e, "Test failed: second entry missing"); 2549 | unitTestAssert(!strcmp(e->attr, "two"), "second entry name is not two"); 2550 | unitTestAssert(!strcmp(e->value, "2"), "second entry value is not 2"); 2551 | e = e->next; 2552 | unitTestAssert(!!e, "Test failed: third entry missing"); 2553 | unitTestAssert(!strcmp(e->attr, "empty1"), "third entry name is not empty1"); 2554 | unitTestAssert(!strcmp(e->value, ""), "third entry value is not empty string"); 2555 | e = e->next; 2556 | unitTestAssert(!!e, "Test failed: fourth entry missing"); 2557 | unitTestAssert(!strcmp(e->attr, "four"), "fourth entry name is not four"); 2558 | unitTestAssert(!strcmp(e->value, "4"), "fourth entry value is not 4"); 2559 | e = e->next; 2560 | unitTestAssert(!!e, "Test failed: fifth entry missing"); 2561 | unitTestAssert(!strcmp(e->attr, "empty2"), "fifth entry name is not empty2"); 2562 | unitTestAssert(!strcmp(e->value, ""), "fifth entry value is not empty string"); 2563 | unitTestAssert(!e->next, "unexpected entry at end of list"); 2564 | return 0; 2565 | } 2566 | 2567 | static void unitTestAssert(const int value, const char *message) 2568 | { 2569 | if (value) { 2570 | return; 2571 | } 2572 | fprintf(stderr, "Test failed: %s\n", message); 2573 | exit(1); 2574 | } 2575 | 2576 | #endif 2577 | -------------------------------------------------------------------------------- /cgic.h: -------------------------------------------------------------------------------- 1 | /* The CGI_C library, by Thomas Boutell, version 2.01. CGI_C is intended 2 | to be a high-quality API to simplify CGI programming tasks. */ 3 | 4 | /* Make sure this is only included once. */ 5 | 6 | #ifndef CGI_C 7 | #define CGI_C 1 8 | 9 | /* Ensure proper linkage to c++ programs. */ 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /* Bring in standard I/O since some of the functions refer to 15 | types defined by it, such as FILE *. */ 16 | 17 | #include 18 | 19 | /* The various CGI environment variables. Instead of using getenv(), 20 | the programmer should refer to these, which are always 21 | valid null-terminated strings (they may be empty, but they 22 | will never be null). If these variables are used instead 23 | of calling getenv(), then it will be possible to save 24 | and restore CGI environments, which is highly convenient 25 | for debugging. */ 26 | 27 | extern char *cgiServerSoftware; 28 | extern char *cgiServerName; 29 | extern char *cgiGatewayInterface; 30 | extern char *cgiServerProtocol; 31 | extern char *cgiServerPort; 32 | extern char *cgiRequestMethod; 33 | extern char *cgiPathInfo; 34 | extern char *cgiPathTranslated; 35 | extern char *cgiScriptName; 36 | extern char *cgiQueryString; 37 | extern char *cgiRemoteHost; 38 | extern char *cgiRemoteAddr; 39 | extern char *cgiAuthType; 40 | extern char *cgiRemoteUser; 41 | extern char *cgiRemoteIdent; 42 | extern char *cgiContentType; 43 | extern char *cgiAccept; 44 | extern char *cgiUserAgent; 45 | extern char *cgiReferrer; 46 | 47 | /* Cookies as sent to the server. You can also get them 48 | individually, or as a string array; see the documentation. */ 49 | extern char *cgiCookie; 50 | 51 | /* A macro providing the same incorrect spelling that is 52 | found in the HTTP/CGI specifications */ 53 | #define cgiReferer cgiReferrer 54 | 55 | /* The number of bytes of data received. 56 | Note that if the submission is a form submission 57 | the library will read and parse all the information 58 | directly from cgiIn; the programmer need not do so. */ 59 | 60 | extern int cgiContentLength; 61 | 62 | /* Pointer to CGI output. The cgiHeader functions should be used 63 | first to output the mime headers; the output HTML 64 | page, GIF image or other web document should then be written 65 | to cgiOut by the programmer. In the standard CGIC library, 66 | cgiOut is always equivalent to stdout. */ 67 | 68 | extern FILE *cgiOut; 69 | 70 | /* Pointer to CGI input. The programmer does not read from this. 71 | We have continued to export it for backwards compatibility 72 | so that cgic 1.x applications link properly. */ 73 | 74 | extern FILE *cgiIn; 75 | 76 | /* Possible return codes from the cgiForm family of functions (see below). */ 77 | 78 | typedef enum { 79 | cgiFormSuccess, 80 | cgiFormTruncated, 81 | cgiFormBadType, 82 | cgiFormEmpty, 83 | cgiFormNotFound, 84 | cgiFormConstrained, 85 | cgiFormNoSuchChoice, 86 | cgiFormMemory, 87 | cgiFormNoFileName, 88 | cgiFormNoContentType, 89 | cgiFormNotAFile, 90 | cgiFormOpenFailed, 91 | cgiFormIO, 92 | cgiFormEOF 93 | } cgiFormResultType; 94 | 95 | /* These functions are used to retrieve form data. See 96 | cgic.html for documentation. */ 97 | 98 | extern cgiFormResultType cgiFormString( 99 | char *name, char *result, int max); 100 | 101 | extern cgiFormResultType cgiFormStringNoNewlines( 102 | char *name, char *result, int max); 103 | 104 | 105 | extern cgiFormResultType cgiFormStringSpaceNeeded( 106 | char *name, int *length); 107 | 108 | 109 | extern cgiFormResultType cgiFormStringMultiple( 110 | char *name, char ***ptrToStringArray); 111 | 112 | extern void cgiStringArrayFree(char **stringArray); 113 | 114 | extern cgiFormResultType cgiFormInteger( 115 | char *name, int *result, int defaultV); 116 | 117 | extern cgiFormResultType cgiFormIntegerBounded( 118 | char *name, int *result, int min, int max, int defaultV); 119 | 120 | extern cgiFormResultType cgiFormDouble( 121 | char *name, double *result, double defaultV); 122 | 123 | extern cgiFormResultType cgiFormDoubleBounded( 124 | char *name, double *result, double min, double max, double defaultV); 125 | 126 | extern cgiFormResultType cgiFormSelectSingle( 127 | char *name, char **choicesText, int choicesTotal, 128 | int *result, int defaultV); 129 | 130 | 131 | extern cgiFormResultType cgiFormSelectMultiple( 132 | char *name, char **choicesText, int choicesTotal, 133 | int *result, int *invalid); 134 | 135 | /* Just an alias; users have asked for this */ 136 | #define cgiFormSubmitClicked cgiFormCheckboxSingle 137 | 138 | extern cgiFormResultType cgiFormCheckboxSingle( 139 | char *name); 140 | 141 | extern cgiFormResultType cgiFormCheckboxMultiple( 142 | char *name, char **valuesText, int valuesTotal, 143 | int *result, int *invalid); 144 | 145 | extern cgiFormResultType cgiFormRadio( 146 | char *name, char **valuesText, int valuesTotal, 147 | int *result, int defaultV); 148 | 149 | /* The paths returned by this function are the original names of files 150 | as reported by the uploading web browser and should NOT be 151 | blindly assumed to be "safe" names for server-side use! */ 152 | extern cgiFormResultType cgiFormFileName( 153 | char *name, char *result, int max); 154 | 155 | /* The content type of the uploaded file, as reported by the browser. 156 | It should NOT be assumed that browsers will never falsify 157 | such information. */ 158 | extern cgiFormResultType cgiFormFileContentType( 159 | char *name, char *result, int max); 160 | 161 | extern cgiFormResultType cgiFormFileSize( 162 | char *name, int *sizeP); 163 | 164 | typedef struct cgiFileStruct *cgiFilePtr; 165 | 166 | extern cgiFormResultType cgiFormFileOpen( 167 | char *name, cgiFilePtr *cfpp); 168 | 169 | extern cgiFormResultType cgiFormFileRead( 170 | cgiFilePtr cfp, char *buffer, int bufferSize, int *gotP); 171 | 172 | extern cgiFormResultType cgiFormFileClose( 173 | cgiFilePtr cfp); 174 | 175 | extern cgiFormResultType cgiCookieString( 176 | char *name, char *result, int max); 177 | 178 | extern cgiFormResultType cgiCookieInteger( 179 | char *name, int *result, int defaultV); 180 | 181 | cgiFormResultType cgiCookies( 182 | char ***ptrToStringArray); 183 | 184 | typedef enum { 185 | cgiCookieSecure = 1, 186 | cgiCookieHttpOnly = 2, 187 | cgiCookieSameSiteStrict = 4 188 | } cgiCookieOption; 189 | 190 | /* path can be null or empty in which case a path of / (entire site) is set. 191 | domain can be a single web site; if it is an entire domain, such as 192 | 'boutell.dev', it should begin with a dot: '.boutell.dev' */ 193 | extern void cgiHeaderCookieSet(char *name, char *value, 194 | int secondsToLive, char *path, char *domain, int options); 195 | extern void cgiHeaderCookieSetString(char *name, char *value, 196 | int secondsToLive, char *path, char *domain); 197 | extern void cgiHeaderCookieSetInteger(char *name, int value, 198 | int secondsToLive, char *path, char *domain); 199 | extern void cgiHeaderLocation(char *redirectUrl); 200 | extern void cgiHeaderStatus(int status, char *statusMessage); 201 | extern void cgiHeaderContentType(char *mimeType); 202 | 203 | typedef enum { 204 | cgiEnvironmentIO, 205 | cgiEnvironmentMemory, 206 | cgiEnvironmentSuccess, 207 | cgiEnvironmentWrongVersion 208 | } cgiEnvironmentResultType; 209 | 210 | extern cgiEnvironmentResultType cgiWriteEnvironment(char *filename); 211 | extern cgiEnvironmentResultType cgiReadEnvironment(char *filename); 212 | 213 | extern int cgiMain(); 214 | 215 | extern cgiFormResultType cgiFormEntries( 216 | char ***ptrToStringArray); 217 | 218 | /* Output string with the <, &, and > characters HTML-escaped. 219 | 's' is null-terminated. Returns cgiFormIO in the event 220 | of error, cgiFormSuccess otherwise. */ 221 | cgiFormResultType cgiHtmlEscape(const char *s); 222 | 223 | /* Output data with the <, &, and > characters HTML-escaped. 224 | 'data' is not null-terminated; 'len' is the number of 225 | bytes in 'data'. Returns cgiFormIO in the event 226 | of error, cgiFormSuccess otherwise. */ 227 | cgiFormResultType cgiHtmlEscapeData(const char *data, int len); 228 | 229 | /* Output string with the " character HTML-escaped, and no 230 | other characters escaped. This is useful when outputting 231 | the contents of a tag attribute such as 'href' or 'src'. 232 | 's' is null-terminated. Returns cgiFormIO in the event 233 | of error, cgiFormSuccess otherwise. */ 234 | cgiFormResultType cgiValueEscape(const char *s); 235 | 236 | /* Output data with the " character HTML-escaped, and no 237 | other characters escaped. This is useful when outputting 238 | the contents of a tag attribute such as 'href' or 'src'. 239 | 'data' is not null-terminated; 'len' is the number of 240 | bytes in 'data'. Returns cgiFormIO in the event 241 | of error, cgiFormSuccess otherwise. */ 242 | cgiFormResultType cgiValueEscapeData(const char *data, int len); 243 | 244 | #ifdef __cplusplus 245 | } 246 | #endif /* __cplusplus */ 247 | 248 | #endif /* CGI_C */ 249 | 250 | -------------------------------------------------------------------------------- /cgic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | cgic: an ANSI C library for CGI Programming 4 | 5 | 6 |

cgic 2.07: an ANSI C library for CGI Programming

7 |
8 | IMPORTANT NOTICES: 9 |

10 | If you have CGIC 1.05 or earlier, you should upgrade to CGIC 1.07, 11 | or to CGIC 2.02 or better, in order to obtain important security fixes. 12 |

13 | If you have CGIC 2.0 or CGIC 2.01 and you use the cgiCookie routines, 14 | you should upgrade to CGIC 2.02 or better, in order to obtain 15 | important security fixes. 16 |

17 |

Table of Contents

18 | 33 | 34 |

Credits and License Terms

35 | 36 |

37 | cgic is now distributed under the MIT license: 38 |

39 |

40 | Copyright (c) 1996-2019 Thomas Boutell 41 |

42 |

43 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 44 |

45 |

46 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 47 |

48 |

49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 50 |

51 |

52 | Thanks are due to Robert Gustavsson, Ken Holervich, Bob Nestor, 53 | Jon Ribbens, Thomas Strangert, Wu Yongwei, and other CGIC users 54 | who have corresponded over the years. Although the implementation 55 | of multipart/form-data file upload support in CGIC 2.x is my own, 56 | I particularly wish to thank those who submitted their own 57 | implementations of this feature. 58 |

59 |

What's new in version 2.07?

60 | Per a suggestion by Geoff Mulligan, cgic now tolerates keys without a value in URL-encoded GET and POST submissions. Although the HTML5 spec discourages it, there are existing systems in the wild that do leave the `=` off entirely if the value is an empty string. Beginning with version 2.07, `cgic` handles this as you'd expect: you get an entry with the corresponding key and an empty string as the value. A simple unit test compilation target was also added, to test this feature and rule out side effects. 61 |

What's new in version 2.06?

62 | A long list of significant fixes generously contributed by Jeffrey Hutzelman. 63 | These are especially important on platforms where attempting to read beyond the content length stated by the request can lead to a deadlock. Please see the commit notes. 64 |

What's new in version 2.05?

65 | Uploaded files properly closed; corrects a resource leak and enables 66 | file uploads to work properly on platforms with particular file 67 | locking semantics. 68 |

What's new in version 2.04?

69 | Documentation fixes: the cgiHtmlEscape, cgiHtmlEscapeData, 70 | cgiValueEscape, and cgiValueEscapeData routines were named 71 | incorrectly in the manual. No code changes in version 2.04. 72 |

What's new in version 2.03?

73 |
    74 |
  • Support for setting cookies has been reimplemented. The new 75 | code closely follows the actual practice of web sites that successfully 76 | use cookies, rather than attempting to implement the specification. 77 | The new code can successfully set more than one cookie at a time in 78 | typical web browsers. 79 |
80 |

What's new in version 2.02?

81 |
    82 |
  • In CGIC 2.0 and 2.01, if the HTTP_COOKIE environment variable 83 | was exactly equal to the name of a cookie requested with cgiCookieString, 84 | with no value or equal sign or other characters present, a buffer 85 | overrun could take place. This was not normal behavior and it is 86 | unknown whether any actual web server would allow it to occur, however 87 | we have of course released a patch to correct it. 88 | Thanks to Nicolas Tomadakis. 89 |
  • cgiCookieString returned cgiFormTruncated when cgiFormSuccess would 90 | be appropriate. Fixed; thanks to Mathieu Villeneuve-Belair. 91 |
  • Cookies are now set using a simpler Set-Cookie: header, and with 92 | one header line per cookie, based on data collected by Chunfu Lai. 93 |
  • Memory leaks in cgiReadEnvironment fixed by Merezko Oleg. These 94 | memory leaks were not experienced in a normal CGI situation, only 95 | when reading a saved CGI environment. 96 |
97 |

What's new in version 2.01?

98 |
    99 |
  • Makefile supports "make install" 100 |
  • Compiles without warnings under both C and C++ with strict 101 | warnings and strict ANSI compliance enabled 102 |
  • Builds out of the box on Windows (#include <fcntl.h> was needed) 103 |
  • Rare problem in cgiReadEnvironment corrected; no impact on 104 | normal CGI operations 105 |
  • cgiCookieString now sets the result to an empty string 106 | when returning cgiFormNotFound 107 |
  • Minor code cleanups 108 |
109 |

What's new in version 2.0?

110 | 1. CGIC 2.0 provides support for file upload fields. User-uploaded 111 | files are kept in temporary files, to avoid the use of 112 | excessive swap space (Solaris users may wish to change the 113 | cgicTempDir macro in cgic.c before compiling). 114 | The cgiFormFileName, 115 | cgiFormFileContentType, 116 | cgiFormFileSize, 117 | cgiFormFileOpen, 118 | cgiFormFileRead, and 119 | cgiFormFileClose functions 120 | provide a complete interface to this new functionality. Remember, 121 | the enctype attribute of the form tag 122 | must be set to multipart/form-data when 123 | <input type="file"> tags are used. 124 |

125 | 2. CGIC 2.0 provides support for setting and examining cookies 126 | (persistent data storage on the browser side). 127 | The cgiCookieString, 128 | and cgiCookieInteger 129 | and cgiCookies 130 | functions retrieve cookies. The 131 | cgiHeaderCookieSetString 132 | and cgiHeaderCookieSetInteger functions set cookies. 133 |

134 | 3. CGIC 2.0 offers a convenient way to retrieve a list of all form fields. 135 | The new cgiFormEntries 136 | function performs this operation. 137 |

138 | 4. CGIC 2.0 provides convenience functions to correctly escape 139 | text before outputting it as part of HTML, or as part of the 140 | value of a tag attribute, such as the HREF or 141 | VALUE attribute. See 142 | cgiHtmlEscape, 143 | cgiHtmlEscapeData, 144 | cgiValueEscape and 145 | cgiValueEscapeData. 146 |

147 | 5. Users have often asked the correct way to determine which submit 148 | button was clicked. This could always be accomplished in previous versions, 149 | but CGIC 2.0 also provides 150 | cgiFormSubmitClicked, 151 | a convenient alternate label for the 152 | cgiFormCheckboxSingle function. 153 |

What's new in version 1.07?

154 | A problem with the cgiFormString and related functions has been 155 | corrected. These functions were previously incorrectly returning cgiFormTruncated 156 | in cases where the returned string fit the buffer exactly. 157 |

What's new in version 1.06?

158 | 1. A potentially significant buffer overflow problem has been 159 | corrected. Jon Ribbens correctly pointed out to me (and to the 160 | Internet's bugtraq mailing list) that the cgiFormEntryString 161 | function, which is used directly or indirectly by almost all 162 | CGIC programs, can potentially write past the buffer passed 163 | to it by the programmer. This bug has been corrected. 164 | Upgrading to version 1.06 is strongly recommended. 165 |

166 | 2. The function cgiSaferSystem() has been 167 | removed entirely. This function escaped only a few metacharacters, 168 | while most shells have many, and there was no way to account for 169 | the many different operating system shells that might be in use 170 | on different operating systems. Since this led to a false sense 171 | of security, the function has been removed. It is our recommendation 172 | that user input should never be passed directly on the command line 173 | unless it has been carefully shown to contain only characters 174 | regarded as safe and appropriate by the programmer. Even then, it is 175 | better to design your utilities to accept their input from standard 176 | input rather than the command line. 177 |

What's new in version 1.05?

178 | Non-exclusive commercial license fee reduced to $200. 179 |

What's new in version 1.04?

180 | For consistency with other packages, the standard Makefile 181 | now produces a true library for cgic (libcgic.a). 182 |

What's new in version 1.03?

183 | Version 1.03 sends line feeds only (ascii 10) to end 184 | Content-type:, Status:, and other HTTP protocol output lines, 185 | instead of CR/LF sequences. The standard specifies CR/LF. 186 | Unfortunately, too many servers reject CR/LF to make 187 | implementation of that standard practical. No server 188 | tested ever rejects LF alone in this context. 189 |

What's new in version 1.02?

190 | Version 1.02 corrects bugs in previous versions: 191 |
    192 |
  • 193 | cgiFormDoubleBounded specified 194 | its arguments in the wrong order, with surprising results. 195 | This bug has been corrected. 196 |
197 |

What's new in version 1.01?

198 | Version 1.01 adds no major functionality but corrects 199 | significant bugs and incompatibilities: 200 |
    201 |
  • 202 | cgiFormInteger, 203 | cgiFormIntegerBounded, 204 | cgiFormDouble and 205 | cgiFormDoubleBounded now 206 | accept negative numbers properly. They also accept positive 207 | numbers with an explicit + sign. 208 |
  • Hex values containing the digit 9 are 209 | now properly decoded. 210 |
  • cgiFormString now 211 | represents each newline as a single line feed (ascii 10 decimal) 212 | as described in the documentation, not a carriage return 213 | (ascii 13 decimal) as in version 1.0. The latter approach 214 | pleased no one. 215 |
  • cgiFormString and 216 | cgiFormStringNoNewlines 217 | no longer erroneously return cgiFormEmpty in place of 218 | cgiFormSuccess. 219 |
  • The main() function of cgic now flushes standard output 220 | and sleeps for one second before exiting in order to inhibit 221 | problems with the completion of I/O on some platforms. This was 222 | not a cgic bug per se, but has been reported as a common problem 223 | with CGI when used with the CERN server. This change should 224 | improve compatibility. 225 |
  • The single selection example in the testform.html 226 | example now works properly. This was an error in the 227 | form itself, not cgic. 228 |
  • cgiRemoteUser and 229 | cgiRemoteIdent are now 230 | documented accurately. They were reversed earlier. 231 |
232 |

What is cgic?

233 | cgic is an ANSI C-language library for the creation of CGI-based 234 | World Wide Web applications. For basic information about 235 | the CGI standard, see the 236 | CGI documentation at NCSA. 237 |

238 | cgic performs the following tasks: 239 |

    240 |
  • Parses form data, correcting for defective and/or inconsistent browsers 241 |
  • Transparently accepts both GET and POST form data 242 |
  • Accepts uploaded files as well as regular form fields 243 |
  • Provides functions to set and retrieve "cookies" 244 | (browser-side persistent information) 245 |
  • Handles line breaks in form fields in a consistent manner 246 |
  • Provides string, integer, floating-point, and single- and 247 | multiple-choice functions to retrieve form data 248 |
  • Provides bounds checking for numeric fields 249 |
  • Loads CGI environment variables into C strings which are always non-null 250 |
  • Provides a way to capture CGI situations for replay in a debugging 251 | environment, including file uploads and cookies 252 |
253 |

254 | cgic is compatible with any CGI-compliant server environment, and 255 | compiles without modification in Posix/Unix/Linux and Windows 256 | environments. 257 |

Obtaining cgic

258 | cgic is distributed via the web in two forms: as a Windows-compatible 259 | .ZIP file, and as a gzipped tar file. Most users of Windows and 260 | related operating systems have access to 'unzip' or 'pkunzip'. All modern Unix 261 | systems come with 'gunzip' and 'tar' as standard equipment, and gzip/gunzip 262 | is not difficult to find if yours does not. Versions 263 | of these programs for other operating systems are widely 264 | available if you do not already have them. 265 |

266 | Important: to use cgic, you will need an ANSI-standard 267 | C compiler. Under Unix, just obtain and use gcc. Most Unix systems have 268 | standardized on gcc. Users of Windows operating systems should not have 269 | ANSI C-related problems as all of the popular compilers follow the ANSI 270 | standard. 271 |

272 | Note for Windows Programmers: you must use a modern 273 | 32-bit compiler. Visual C++ 2.0 or higher, Borland C++ and the 274 | mingw32 gcc compiler are all appropriate, as is cygwin. Do 275 | NOT use an ancient 16-bit DOS executable compiler, please. 276 |

277 |

What Operating System Does Your WEB SERVER Run?

278 | Remember, the computer on your desk is usually NOT your web server. 279 | Compiling a Windows console executable will not give you a CGI program that 280 | can be installed on a Linux-based server. 281 |
282 |

Building cgic: a sample application

283 | The sample application 'cgictest.c' is provided as part of the 284 | cgic distribution. This CGI program displays an input form, 285 | accepts a submission, and then displays what was submitted. 286 | In the process essentially all of cgic's features are tested. 287 |

288 | On a Unix system, you can build cgictest simply by typing 289 | 'make cgictest.cgi'. cgic.c and cgictest.c will be compiled and linked 290 | together to produce the cgictest application. Under non-Unix 291 | operating systems, you will need to create and compile an appropriate 292 | project containing the files cgic.c and cgictest.c. 293 |

294 | IMPORTANT: after compiling cgictest.cgi, you will 295 | need to place it in a location on your server system which is 296 | designated by your server administrator as an appropriate location 297 | for CGI scripts. Some servers are configured to recognize any 298 | file ending in .cgi as a CGI program when it is found in any 299 | subdirectory of the server's web space, but this is not always 300 | the case! The right locations for CGI 301 | programs vary greatly from one server to another. Resolving 302 | this issue is between you, your web server administrator, 303 | and your web server documentation. Before submitting a bug 304 | report for cgic, make certain that the CGI example programs 305 | which came with your server do work for you. Otherwise 306 | it is very likely that you have a server configuration problem. 307 |

308 | Once you have moved cgictest.cgi (or cgictest.exe, under Windows) 309 | to an appropriate cgi directory, 310 | use the web browser of your choice to access the URL at which 311 | you have installed it 312 | (for instance, www.mysite.com/cgi-bin/cgictest.cgi). 313 | Fill out the various fields in any manner you wish, then 314 | select the SUBMIT button. 315 |

316 | If all goes well, cgictest.cgi will respond with a page which 317 | indicates the various settings you submitted. If not, 318 | please reread the section above regarding the correct location in 319 | which to install your CGI program on your web server. 320 |

What to do if it won't compile

321 |
    322 |
  • Are you using Visual C++ or Borland C++? Did you forget to add 323 | cgic.c to your project? 324 |
  • Make sure you are using an ANSI C or C++ compiler. 325 | (All of the Windows compilers are ANSI C compliant.) 326 |
327 | If none of the above proves effective, please see the 328 | section regarding support. 329 |

How to write a cgic application

330 | Note: All cgic applications must be linked to the cgic.c module 331 | itself. How to do this depends on your operating system; under Unix, 332 | just use the provided Makefile as an example. 333 |

334 | Since all CGI applications must perform certain initial 335 | tasks, such as parsing form data and examining 336 | environment variables, the cgic library provides its 337 | own main() function. When you write applications that 338 | use cgic, you will begin your own programs by writing 339 | a cgiMain() function, which cgic will invoke when 340 | the initial cgi work has been successfully completed. Your 341 | program must also be sure to #include the file cgic.h. 342 |

343 | Important: if you write your own main() 344 | function, your program will not link properly. Your own 345 | code should begin with cgiMain(). The library 346 | provides main() for you. (Those who prefer different behavior 347 | can easily modify cgic.c.) 348 |

349 | Consider the cgiMain function of cgictest.c: 350 |

351 |

 352 | int cgiMain() {
 353 | #ifdef DEBUG
 354 |   LoadEnvironment();
 355 | #endif /* DEBUG */
 356 |   /* Load a previously saved CGI scenario if that button
 357 |     has been pressed. */
 358 |   if (cgiFormSubmitClicked("loadenvironment") == cgiFormSuccess) {
 359 |     LoadEnvironment();
 360 |   }
 361 |   /* Set any new cookie requested. Must be done *before*
 362 |     outputting the content type. */
 363 |   CookieSet();
 364 |   /* Send the content type, letting the browser know this is HTML */
 365 |   cgiHeaderContentType("text/html");
 366 |   /* Top of the page */
 367 |   fprintf(cgiOut, "<HTML><HEAD>\n");
 368 |   fprintf(cgiOut, "<TITLE>cgic test</TITLE></HEAD>\n");
 369 |   fprintf(cgiOut, "<BODY><H1>cgic test</H1>\n");
 370 |   /* If a submit button has already been clicked, act on the 
 371 |     submission of the form. */
 372 |   if ((cgiFormSubmitClicked("testcgic") == cgiFormSuccess) ||
 373 |     cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess)
 374 |   {
 375 |     HandleSubmit();
 376 |     fprintf(cgiOut, "<hr>\n");
 377 |   }
 378 |   /* Now show the form */
 379 |   ShowForm();
 380 |   /* Finish up the page */
 381 |   fprintf(cgiOut, "</BODY></HTML>\n");
 382 |   return 0;
 383 | }
 384 | 
385 | Note the DEBUG #ifdef. If DEBUG is defined at compile time, either by 386 | inserting the line "#define DEBUG 1" into the program or by setting 387 | it in the Makefile or other development environment, then the 388 | LoadEnvironment function is invoked. This function calls 389 | cgiReadEnvironment() 390 | to restore a captured CGI environment for debugging purposes. See 391 | also the discussion of the capture program, which is 392 | provided for use in CGI debugging. Because this is a test program, 393 | the cgiFormSubmitClicked function is 394 | also called to check for the use of a button that requests the reloading 395 | of a saved CGI environment. A completed CGI program typically would 396 | never allow the end user to make that decision. 397 |

Setting Cookies

398 | Next, one of the cgiHeader functions should be called. 399 | This particular program demonstrates many features, including 400 | the setting of cookies. If the programmer wishes to set a cookie, 401 | the cookie-setting function must be called 402 | first, before other headers are output. This is done by the 403 | CookieSet() function of cgictest.c: 404 |
 405 | void CookieSet()
 406 | {
 407 |   char cname[1024];
 408 |   char cvalue[1024];
 409 |   /* Must set cookies BEFORE calling 
 410 |     cgiHeaderContentType */
 411 |   cgiFormString("cname", cname, sizeof(cname));  
 412 |   cgiFormString("cvalue", cvalue, sizeof(cvalue));  
 413 |   if (strlen(cname)) {
 414 |     /* Cookie lives for one day (or until 
 415 |       browser chooses to get rid of it, which 
 416 |       may be immediately), and applies only to 
 417 |       this script on this site. */  
 418 |     cgiHeaderCookieSet(cname, cvalue,
 419 |       86400, cgiScriptName, cgiServerName,
 420 |       cgiCookieHttpOnly | cgiCookieSameSiteStrict);
 421 |   }
 422 | }
 423 | 
424 | Since this is a test program, the cgiFormString 425 | function is used to fetch the name and value from the form previously filled 426 | in by the user. Normally, cookie names and values are chosen to meet the 427 | needs of the programmer and provide a means of identifying the same 428 | user again later. 429 |

430 | The cgiHeaderCookieSet 431 | function sets the cookie by requesting that the web browser store it. 432 | There is never any guarantee that this will happen! 433 | Many browsers reject cookies completely; others do not necessarily keep 434 | them as long as requested or return them with their values intact. 435 | Always code defensively when using cookies. 436 |

437 | The cname and cvalue parameters are of course the name and value for 438 | the cookie. The third argument is the time, in seconds, that the 439 | cookie should "live" on the browser side before it expires; in this 440 | case it has been set to 86,400 seconds, which is exactly one day. 441 | The browser may or may not respect this setting, as with everything 442 | else about cookies. 443 |

444 | The fourth argument identifies the "path" within the web site for which 445 | the cookie is considered valid. A cookie that should be sent back 446 | for every access to the site should be set with a path of /. 447 | In this case the cookie is relevant only to the CGI program itself, so 448 | cgiScriptName (the URL of the CGI program, not including the 449 | domain name) is sent. Similarly, a cookie can be considered relevant 450 | to a single web site or to an entire domain, such as 451 | www.boutell.dev or the entire .boutell.dev 452 | domain. In this case, the current site on which the program is running 453 | is the only relevant site, so cgiServerName is used 454 | as the domain. 455 |

456 | The sixth argument sets extra security options, for example HttpOnly or 457 | SameSite=Strict to prevent cross-site-scripting attacks. 458 |

Outputting the Content Type Header

459 | Next, cgiHeaderContentType() is 460 | called to indicate the MIME type of the document being output, in this case 461 | "text/html" (a normal HTML document). A few other common MIME types are 462 | "image/gif", "image/jpeg" and "audio/wav". 463 |

464 | Note that cgiHeaderStatus() or 465 | cgiHeaderLocation() could have 466 | been invoked instead to output an error code or redirect the 467 | request to a different URL. Only one of the cgiHeader functions 468 | should be called in a single execution of the program. 469 |

470 | Important: one of the cgiHeader functions, 471 | usually cgiHeaderContentType(), 472 | must be invoked before outputting any other 473 | response to the user. Otherwise, the result will not be a valid 474 | document and the browser's behavior will be unpredictable. 475 | You may, of course, output your own ContentType and other 476 | header information to cgiOut if you prefer. The cgiHeader functions 477 | are provided as a convenience. 478 |

Handling Form Submissions

479 | Like many CGI programs, cgictest makes decisions about the way it 480 | should behave based on whether various submit buttons have been clicked. 481 | When either the testcgic or saveenvironment button is present, cgictest 482 | invokes the HandleSubmit function, which invokes additional functions to 483 | handle various parts of the form: 484 |
 485 | void HandleSubmit()
 486 | {
 487 |   Name();
 488 |   Address();
 489 |   Hungry();
 490 |   Temperature();
 491 |   Frogs();
 492 |   Color();
 493 |   Flavors();
 494 |   NonExButtons();
 495 |   RadioButtons();
 496 |   File();
 497 |   Entries();
 498 |   Cookies();
 499 |   /* The saveenvironment button, in addition to submitting 
 500 |     the form, also saves the resulting CGI scenario to 
 501 |     disk for later replay with the 'load saved environment' 
 502 |     button. */
 503 |   if (cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess) {
 504 |     SaveEnvironment();
 505 |   }
 506 | }
 507 | 
508 |

Handling Text Input

509 | The Name() function of cgictest is shown below, in its simplest 510 | possible form: 511 |
 512 | void Name() {
 513 |         char name[81];
 514 |         cgiFormStringNoNewlines("name", name, 81);
 515 |         fprintf(cgiOut, "Name: ");
 516 |         cgicHtmlEscape(name);
 517 |         fprintf(cgiOut, "
\n"); 518 | } 519 |
520 | The purpose of this function is to retrieve and display the name that was 521 | input by the user. Since the programmer has decided that names should 522 | be permitted to have up to 80 characters, a buffer of 81 characters 523 | has been declared (allowing for the final null character). 524 | The cgiFormStringNoNewlines() 525 | function is then invoked to retrieve the name and ensure that 526 | carriage returns are not present in the name (despite the 527 | incorrect behavior of some web browsers). The first argument 528 | is the name of the input field in the form, the second argument 529 | is the buffer to which the data should be copied, and the third 530 | argument is the size of the buffer. cgic will never write beyond 531 | the size of the buffer, and will always provide a null-terminated 532 | string in response; if the buffer is too small, the string will 533 | be shortened. If this is not acceptable, the 534 | cgiFormStringSpaceNeeded() 535 | function can be used to check the amount of space needed; the 536 | return value of cgiFormStringNoNewlines() can also be checked 537 | to determine whether truncation occurred. See 538 | the full description of 539 | cgiFormStringNoNewlines(). 540 |

Handling Output

541 | Note that Name() writes its HTML output to cgiOut, not 542 | to stdout. 543 |

544 | The actual name submitted by the user may or may not contain 545 | characters that have special meaning in HTML, specifically the 546 | the <, >, and & characters. 547 | The cgiHtmlEscape function is used to output 548 | the user-entered name with any occurrences of these characters 549 | correctly escaped as &lt;, &gt;, 550 | and &amp;. 551 |

552 | Important: cgiOut is normally equivalent 553 | to stdout, and there is no performance penalty for using it. 554 | It is recommended that you write output to cgiOut to ensure compatibility 555 | with modified versions of the cgic library for special 556 | environments that do not provide stdin and stdout for 557 | each cgi connection. 558 |

559 | Note that, for text input areas in which carriage returns are 560 | desired, the function cgiFormString 561 | should be used instead. cgiFormString ensures that line breaks 562 | are always represented by a single carriage return (ascii decimal 13), 563 | making life easier for the programmer. See the source code to 564 | the Address() function of cgictest.c for an example. 565 |

Handling Single Checkboxes

566 | Consider the Hungry() function, which determines whether 567 | the user has selected the "hungry" checkbox: 568 |
 569 | void Hungry() {
 570 |         if (cgiFormCheckboxSingle("hungry") == cgiFormSuccess) {
 571 |                 fprintf(cgiOut, "I'm Hungry!<BR>\n");
 572 |         } else {
 573 |                 fprintf(cgiOut, "I'm Not Hungry!<BR>\n");
 574 |         }
 575 | }
 576 | 
577 | This function takes advantage of the 578 | cgiFormCheckboxSingle() function, which 579 | determines whether a single checkbox has been selected. 580 | cgiFormCheckboxSingle() accepts the name attribute of the checkbox 581 | as its sole argument and returns 582 | cgiFormSuccess if the checkbox is selected, or 583 | cgiFormNotFound if it is not. 584 | If multiple checkboxes with the same name are in use, 585 | consider the 586 | cgiFormCheckboxMultiple() and 587 | cgiFormStringMultiple() 588 | functions. 589 |

Handling Numeric Input

590 | Now consider the Temperature() function, which retrieves 591 | a temperature in degrees (a floating-point value) and ensures 592 | that it lies within particular bounds: 593 |
 594 | void Temperature() {
 595 |         double temperature;
 596 |         cgiFormDoubleBounded("temperature", &temperature, 80.0, 120.0, 98.6);
 597 |         fprintf(cgiOut, "My temperature is %f.<BR>\n", temperature);
 598 | }
 599 | 
600 | The temperature is retrieved by the function 601 | cgiFormDoubleBounded(). The first 602 | argument is the name of the temperature input field in the form; 603 | the second argument points to the address of the variable that will 604 | contain the result. The next two arguments are the lower and upper 605 | bounds, respectively. The final argument is the default value to 606 | be returned if the user did not submit a value. 607 |

608 | This function always retrieves a reasonable value within the 609 | specified bounds; values above or below bounds are constrained 610 | to fit the bounds. However, the return value of 611 | cgiFormDoubleBounded can be checked to make sure the 612 | actual user entry was in bounds, not blank, and so forth; 613 | see the description of 614 | cgiFormDoubleBounded() for more details. If bounds checking 615 | is not desired, consider using 616 | cgiFormDouble() instead. 617 |

618 | Note that, for integer input, the functions 619 | cgiFormInteger and 620 | cgiFormIntegerBounded 621 | are available. The behavior of these functions is similar to 622 | that of their floating-point counterparts above. 623 |

Handling Single-Choice Input

624 | The <SELECT> tag of HTML is used to provide the user with 625 | several choices. Radio buttons and checkboxes can also be used 626 | when the number of choices is relatively small. Consider 627 | the Color() function of cgictest.c: 628 |
 629 | char *colors[] = {
 630 |         "Red",
 631 |         "Green",
 632 |         "Blue"
 633 | };
 634 | 
 635 | void Color() {
 636 |         int colorChoice;
 637 |         cgiFormSelectSingle("colors", colors, 3, &colorChoice, 0);
 638 |         fprintf(cgiOut, "I am: %s<BR>\n", colors[colorChoice]);
 639 | }
 640 | 
641 | This function determines which of several colors the user chose 642 | from a <SELECT> list in the form. An array of colors is 643 | declared; the cgiFormSelectSingle() 644 | function is then invoked to determine which, if any, of those choices 645 | was selected. The first argument indicates the name of the input 646 | field in the form. The second argument points to the list of 647 | acceptable colors. The third argument indicates the number of 648 | entries in the color array. The fourth argument points to the 649 | variable which will accept the chosen color, and the last argument 650 | indicates the index of the default value to be set if no 651 | selection was submitted by the browser. 652 |

653 | cgiFormSelectSingle() will 654 | always indicate a reasonable selection value. However, if 655 | the programmer wishes to know for certain that a value was 656 | actually submitted, that the value submitted was a legal 657 | response, and so on, the return value of cgiFormSelectSingle() 658 | can be consulted. See the full description of 659 | cgiFormSelectSingle() for 660 | more information. 661 |

662 | Note that radio button groups and <SELECT> lists can both 663 | be handled by this function. If you are processing radio 664 | button groups, you may prefer to invoke 665 | cgiFormRadio(), which functions 666 | identically. 667 |

668 | "What if I won't know the acceptable choices at runtime?" 669 |

670 | If the acceptable choices aren't known until runtime, 671 | one can simply load the choices from disk. But if the acceptable 672 | choices aren't fixed at all (consider a list of country names; 673 | new names may be added to the form at any time and it is 674 | inconvenient to also update program code or a separate list 675 | of countries), simply invoke 676 | cgiFormStringNoNewlines() 677 | instead to retrieve the string directly. Keep in mind that, if 678 | you do so, validating the response to make sure it is 679 | safe and legitimate becomes a problem for your own 680 | program to solve. The advantage of cgiFormSelectSingle() is that invalid 681 | responses are never returned. 682 |

683 | To handle multiple-selection <SELECT> lists and 684 | groups of checkboxes with the same name, see the 685 | discussion of the NonExButtons() function of cgictest.c, immediately below. 686 |

Handling Multiple-Choice Input

687 | Consider the first half of the NonExButtons() function of cgictest.c: 688 |
 689 | char *votes[] = {
 690 |   "A",
 691 |   "B",
 692 |   "C",
 693 |   "D"
 694 | };
 695 | 
 696 | void NonExButtons() {
 697 |   int voteChoices[4];
 698 |   int i;
 699 |   int result;  
 700 |   int invalid;
 701 | 
 702 |   char **responses;
 703 | 
 704 |   /* Method #1: check for valid votes. This is a good idea,
 705 |     since votes for nonexistent candidates should probably
 706 |     be discounted... */
 707 |   fprintf(cgiOut, "Votes (method 1):<BR>\n");
 708 |   result = cgiFormCheckboxMultiple("vote", votes, 4, 
 709 |     voteChoices, &invalid);
 710 |   if (result == cgiFormNotFound) {
 711 |     fprintf(cgiOut, "I hate them all!<p>\n");
 712 |   } else {  
 713 |     fprintf(cgiOut, "My preferred candidates are:\n");
 714 |     fprintf(cgiOut, "<ul>\n");
 715 |     for (i=0; (i < 4); i++) {
 716 |       if (voteChoices[i]) {
 717 |         fprintf(cgiOut, "<li>%s\n", votes[i]);
 718 |       }
 719 |     }
 720 |     fprintf(cgiOut, "</ul>\n");
 721 |   }
 722 | 
723 | This function takes advantage of 724 | cgiFormCheckboxMultiple(), 725 | which is used to identify one or more selected checkboxes with 726 | the same name. This function performs identically to 727 | cgiFormSelectMultiple(). 728 | That is, <SELECT> tags with the MULTIPLE attribute are handled 729 | just like a group of several checkboxes with the same name. 730 |

731 | The first argument to 732 | cgiFormCheckboxMultiple() is the name given to all 733 | checkbox input fields in the group. The second argument 734 | points to an array of legitimate values; these should 735 | correspond to the VALUE attributes of the checkboxes 736 | (or OPTION tags in a <SELECT> list). The third argument 737 | indicates the number of entries in the array of 738 | legitimate values. The fourth argument points to 739 | an array of integers with the same number of entries 740 | as the array of legitimate values; each entry 741 | will be set true if that checkbox or option was selected, 742 | false otherwise. 743 |

744 | The last argument points to an integer which will be set to the 745 | number of invalid responses (responses not in the array of 746 | valid responses) that were submitted. If this value is not 747 | of interest, the last argument may be a null pointer (0). 748 |

749 | Note that the return value of cgiFormCheckboxMultiple is 750 | inspected to determine whether any choices at all were 751 | set. See the full description of 752 | cgiFormCheckboxMultiple 753 | for other possible return values. 754 |

755 | "What if I won't know the acceptable choices at runtime?" 756 |

757 | If the acceptable choices aren't known until runtime, 758 | one can simply load the choices from disk. But if the acceptable 759 | choices aren't fixed at all (consider a list of ice cream flavors; 760 | new names may be added to the form at any time and it is 761 | inconvenient to also update program code or a separate list 762 | of countries), a more dynamic approach is needed. Consider 763 | the second half of the NonExButtons() function of cgictest.c: 764 |

 765 |   /* Method #2: get all the names voted for and trust them.
 766 |     This is good if the form will change more often
 767 |     than the code and invented responses are not a danger
 768 |     or can be checked in some other way. */
 769 |   fprintf(cgiOut, "Votes (method 2):<BR>\n");
 770 |   result = cgiFormStringMultiple("vote", &responses);
 771 |   if (result == cgiFormNotFound) {  
 772 |     fprintf(cgiOut, "I hate them all!<p>\n");
 773 |   } else {
 774 |     int i = 0;
 775 |     fprintf(cgiOut, "My preferred candidates are:\n");
 776 |     fprintf(cgiOut, "<ul>\n");
 777 |     while (responses[i]) {
 778 |       fprintf(cgiOut, "<li>%s\n", responses[i]);
 779 |       i++;
 780 |     }
 781 |     fprintf(cgiOut, "</ul>\n");
 782 |   }
 783 |   /* We must be sure to free the string array or a memory
 784 |     leak will occur. Simply calling free() would free
 785 |     the array but not the individual strings. The
 786 |     function cgiStringArrayFree() does the job completely. */  
 787 |   cgiStringArrayFree(responses);
 788 | }
 789 | 
790 | This code excerpt demonstrates an alternate means of retrieving 791 | a list of choices. The function 792 | cgiFormStringMultiple() is used 793 | to retrieve an array consisting of all the strings submitted 794 | for with a particular input field name. This works both for 795 | <SELECT> tags with the MULTIPLE attribute and for 796 | groups of checkboxes with the same name. 797 |

798 | The first argument to 799 | cgiFormStringMultiple() is the name of the input field or 800 | group of input fields in question. The second argument should 801 | be the address of a pointer to a pointer to a string, which 802 | isn't as bad as it sounds. Consider the following simple call 803 | of the function: 804 |

 805 | /* An array of strings; each C string is an array of characters */
 806 | char **responses; 
 807 | 
 808 | cgiFormStringMultiple("vote", &responses);
 809 | 
810 | "How do I know how many responses there are?" 811 |

812 | After the call, the last entry in the string array will be 813 | a null pointer. Thus the simple loop: 814 |

 815 | int i = 0;
 816 | while (responses[i]) {
 817 |   /* Do something with the string responses[i] */
 818 |   i++;
 819 | }
 820 | 
821 | can be used to walk through the array until the last 822 | entry is encountered. 823 |

824 | Important: the 825 | cgiFormStringMultiple function 826 | returns a pointer to allocated memory. Your code 827 | should not modify the strings in the responses array or the responses 828 | array itself; if modification is needed, the strings should be 829 | copied. When your code is done examining the responses array, 830 | you MUST call 831 | cgiStringArrayFree() with the array as an argument to free the memory 832 | associated with the array. Otherwise, the memory will not be available 833 | again until the program exists. Don't just call the 834 | free() function; if you do, the individual strings will not be freed. 835 |

Accessing Uploaded Files

836 | CGIC provides functions to access files that have been uploaded 837 | as part of a form submission. IMPORTANT: you MUST set 838 | the enctype attribute of your form tag 839 | to multipart/form-data for this feature to work! For an 840 | example, see the ShowForm function of 841 | cgictest.c, examined below. 842 |

843 | The File function of cgictest.c takes care of 844 | receiving uploaded files: 845 |

 846 | void File()
 847 | {
 848 |   cgiFilePtr file;
 849 |   char name[1024];
 850 |   char contentType[1024];
 851 |   char buffer[1024];
 852 |   int size;
 853 |   int got;
 854 |   if (cgiFormFileName("file", name, sizeof(name)) != 
 855 |     cgiFormSuccess) 
 856 |   {
 857 |     printf("<p>No file was uploaded.<p>\n");
 858 |     return;
 859 |   } 
 860 |         fprintf(cgiOut, "The filename submitted was: ");
 861 |         cgiHtmlEscape(name);
 862 |         fprintf(cgiOut, "<p>\n");
 863 |         cgiFormFileSize("file", &size);
 864 |         fprintf(cgiOut, "The file size was: %d bytes<p>\n", size);
 865 |         cgiFormFileContentType("file", contentType, sizeof(contentType));
 866 |         fprintf(cgiOut, "The alleged content type of the file was: ");
 867 |         cgiHtmlEscape(contentType);
 868 |         fprintf(cgiOut, "<p>\n");
 869 |   fprintf(cgiOut, "Of course, this is only the claim the browser "
 870 |     "made when uploading the file. Much like the filename, "
 871 |     "it cannot be trusted.<p>\n");
 872 |   fprintf(cgiOut, "The file's contents are shown here:<p>\n");
 873 |   if (cgiFormFileOpen("file", &file) != cgiFormSuccess) {
 874 |     fprintf(cgiOut, "Could not open the file.<p>\n");
 875 |     return;
 876 |   }
 877 |   fprintf(cgiOut, "<pre>\n");
 878 |   while (cgiFormFileRead(file, buffer, sizeof(buffer), &got) ==
 879 |     cgiFormSuccess)
 880 |   {
 881 |     cgiHtmlEscapeData(buffer, got);
 882 |   }
 883 |   fprintf(cgiOut, "</pre>\n");
 884 |   cgiFormFileClose(file);
 885 | }
 886 | 
887 | First, the File function checks to determine the filename that was 888 | submitted by the user. VERY IMPORTANT: this filename may or 889 | may not bear any relation to the real name of the file on the user's 890 | computer, may be deliberately manipulated with malicious intent, 891 | and should not be used for any purpose unless you have 892 | determined that its content is safe for your intended use and will not, 893 | at the very least, overwrite another file of importance to you, especially if 894 | you intend to use it as a file name on the server side. The cgic library 895 | itself does not use this file name for temporary storage. 896 |

897 | If the cgiFormFileName function does 898 | not succeed, no file was uploaded. 899 |

900 | Next, the cgiFormFileSize function is called 901 | to determine the size of the uploaded file, in bytes. 902 |

903 | The File function then proceeds to query the content type of the uploaded 904 | file. Files uploaded by the user have their own content type information, 905 | which may be useful in determining whether the file is an image, HTML document, 906 | word processing document, or other type of file. However, 907 | as with the filename and any other claim made by the browser, 908 | this information should not be blindly trusted. The browser 909 | may upload a file with the name picture.jpg and the 910 | content type image/jpeg, but this does not guarantee that the 911 | actual file will contain a valid JPEG image suitable for display. 912 |

913 | The content type submitted by the browser can be queried using the 914 | cgiFormFileContentType function. 915 |

916 | Of course, CGIC also provides access to the actual uploaded file. 917 | First, the programmer calls cgiFormFileOpen, 918 | passing the address of a cgiFilePtr object. If this function 919 | succeeds, the cgiFilePtr object becomes valid, and can be 920 | used in subsequent calls to cgiFormFileRead. 921 | Notice that the number of bytes read may be less than the number requested, 922 | in particular on the last successful call before cgiFormFileRead begins 923 | to return cgiFormEOF. When cgiFormFileRead no longer returns 924 | cgiFormSuccess, 925 | the programmer calls cgiFormFileClose to 926 | release the cgiFilePtr object. 927 |

928 | The uploaded file data may contain anything, including binary data, 929 | null characters, and so on. The example program uses the 930 | cgiHtmlEscapeData function to output the 931 | data with any special characters that have meaning in HTML escaped. 932 | Most programs will save the uploaded information to a server-side file or 933 | database. 934 |

Fetching All Form Entries

935 | From time to time, the programmer may not know the names of all 936 | form fields in advance. In such situations it is convenient to 937 | use the cgiFormEntries function. 938 | The Entries function of cgictest.c demonstrates the use of 939 | cgiFormEntries: 940 |
 941 | void Entries()
 942 | {
 943 |         char **array, **arrayStep;
 944 |         fprintf(cgiOut, "List of All Submitted Form Field Names:<p>\n");
 945 |         if (cgiFormEntries(&array) != cgiFormSuccess) {
 946 |                 return;
 947 |         }
 948 |         arrayStep = array;
 949 |         fprintf(cgiOut, "<ul>\n");
 950 |         while (*arrayStep) {
 951 |                 fprintf(cgiOut, "<li>");
 952 |                 cgiHtmlEscape(*arrayStep);
 953 |                 fprintf(cgiOut, "\n");
 954 |                 arrayStep++;
 955 |         }
 956 |         fprintf(cgiOut, "</ul>\n");
 957 |         cgiStringArrayFree(array);
 958 | }
 959 | 
960 | The cgiFormEntries function retrieves an array of form field names. 961 | This array consists of pointers to strings, with a final null pointer 962 | to mark the end of the list. The above code illustrates one way of 963 | looping through the returned strings. Note the final call to 964 | cgiStringArrayFree, which is 965 | essential in order to return the memory used to store the strings 966 | and the string array. 967 |

Retrieving Cookies

968 | The Cookies function of cgictest.c displays a list of all cookies 969 | submitted by the browser with the current form submission, along 970 | with their values: 971 |
 972 | void Cookies()
 973 | {
 974 |   char **array, **arrayStep;
 975 |   char cname[1024], cvalue[1024];
 976 |   fprintf(cgiOut, "Cookies Submitted On This Call, With Values "
 977 |     "(Many Browsers NEVER Submit Cookies):<p>\n");
 978 |   if (cgiCookies(&array) != cgiFormSuccess) {
 979 |     return;
 980 |   }
 981 |   arrayStep = array;
 982 |   fprintf(cgiOut, "<table border=1>\n");
 983 |   fprintf(cgiOut, "<tr><th>Cookie<th>Value</tr>\n");
 984 |   while (*arrayStep) {
 985 |     char value[1024];
 986 |     fprintf(cgiOut, "<tr>");
 987 |     fprintf(cgiOut, "<td>");
 988 |     cgiHtmlEscape(*arrayStep);
 989 |     fprintf(cgiOut, "<td>");
 990 |     cgiCookieString(*arrayStep, value, sizeof(value));
 991 |     cgiHtmlEscape(value);
 992 |     fprintf(cgiOut, "\n");
 993 |     arrayStep++;
 994 |   }
 995 |   fprintf(cgiOut, "</table>\n");
 996 |   cgiFormString("cname", cname, sizeof(cname));  
 997 |   cgiFormString("cvalue", cvalue, sizeof(cvalue));  
 998 |   if (strlen(cname)) {
 999 |     fprintf(cgiOut, "New Cookie Set On This Call:<p>\n");
1000 |     fprintf(cgiOut, "Name: ");  
1001 |     cgiHtmlEscape(cname);
1002 |     fprintf(cgiOut, "Value: ");  
1003 |     cgiHtmlEscape(cvalue);
1004 |     fprintf(cgiOut, "<p>\n");
1005 |     fprintf(cgiOut, "If your browser accepts cookies "
1006 |       "(many do not), this new cookie should appear "
1007 |       "in the above list the next time the form is "
1008 |       "submitted.<p>\n"); 
1009 |   }
1010 |   cgiStringArrayFree(array);
1011 | }
1012 | 
1013 | VERY IMPORTANT: YOUR BROWSER MIGHT NOT SUBMIT COOKIES, 1014 | EVER, REGARDLESS OF WHAT VALUES YOU ENTER INTO THE TEST FORM. 1015 | Many, many browsers are configured not to accept or send cookies; 1016 | others are configured to send them as little as possible to meet the 1017 | bare minimum requirements for entry into popular sites. Users will often 1018 | refuse your cookies; make sure your code still works in that situation! 1019 |

1020 | The above code uses the cgiCookies function 1021 | to retrieve a list of all currently set cookies as a null-terminated 1022 | array of strings. The cgiCookieString 1023 | function is then used to fetch the value associated with each cookie; 1024 | this function works much like cgiFormString, 1025 | discussed earlier. Note that a cookie set as a part of the current 1026 | form submission process does not appear on this list immediately, as 1027 | it has not yet been sent back by the browser. It should appear on 1028 | future submissions, provided that the browser chooses to accept 1029 | and resend the cookie at all. 1030 |

Displaying a Form That Submits to the Current Program

1031 | CGI programmers often need to display HTML pages as part of the output 1032 | of CGI programs; these HTML pages often contain forms which should submit 1033 | fields back to the same program they came from. Provided that your 1034 | web server is well-configured, this can be done conveniently using 1035 | the cgiScriptName environment variable, as shown below. Here is the 1036 | source code of the ShowForm function of cgictest.c: 1037 |
1038 | void ShowForm()
1039 | {
1040 |   fprintf(cgiOut, "<!-- 2.0: multipart/form-data is required 
1041 |     "for file uploads. -->");
1042 |   fprintf(cgiOut, "<form method=\"POST\" "
1043 |     "enctype=\"multipart/form-data\" ");
1044 |   fprintf(cgiOut, "  action=\"");
1045 |   cgiValueEscape(cgiScriptName);
1046 |   fprintf(cgiOut, "\">\n");
1047 |   fprintf(cgiOut, "<p>\n");
1048 |   fprintf(cgiOut, "Text Field containing Plaintext\n");
1049 |   fprintf(cgiOut, "<p>\n");
1050 |   fprintf(cgiOut, "<input type=\"text\" name=\"name\">Your Name\n");
1051 |   fprintf(cgiOut, "<p>\n");
1052 |   fprintf(cgiOut, "Multiple-Line Text Field\n");
1053 |   fprintf(cgiOut, "<p>\n");
1054 |   fprintf(cgiOut, "<textarea NAME=\"address\" ROWS=4 COLS=40>\n");
1055 |   fprintf(cgiOut, "Default contents go here. \n");
1056 |   fprintf(cgiOut, "</textarea>\n");
1057 |   fprintf(cgiOut, "<p>\n");
1058 |   fprintf(cgiOut, "Checkbox\n");
1059 |   fprintf(cgiOut, "<p>\n");
1060 |   fprintf(cgiOut, "<input type=\"checkbox\" name=\"hungry\" checked>Hungry\n");
1061 |   fprintf(cgiOut, "<p>\n");
1062 |   fprintf(cgiOut, "Text Field containing a Numeric Value\n");
1063 |   fprintf(cgiOut, "<p>\n");
1064 |   fprintf(cgiOut, "<input type=\"text\" name=\"temperature\" value=\"98.6\">\n");
1065 |   fprintf(cgiOut, "Blood Temperature (80.0-120.0)\n");
1066 |   fprintf(cgiOut, "<p>\n");
1067 |   fprintf(cgiOut, "Text Field containing an Integer Value\n");
1068 |   fprintf(cgiOut, "<p>\n");
1069 |   fprintf(cgiOut, "<input type=\"text\" name=\"frogs\" value=\"1\">\n");
1070 |   fprintf(cgiOut, "Frogs Eaten\n");
1071 |   fprintf(cgiOut, "<p>\n");
1072 |   fprintf(cgiOut, "Single-SELECT\n");
1073 |   fprintf(cgiOut, "<br>\n");
1074 |   fprintf(cgiOut, "<select name=\"colors\">\n");
1075 |   fprintf(cgiOut, "<option value=\"Red\">Red\n");
1076 |   fprintf(cgiOut, "<option value=\"Green\">Green\n");
1077 |   fprintf(cgiOut, "<option value=\"Blue\">Blue\n");
1078 |   fprintf(cgiOut, "</select>\n");
1079 |   fprintf(cgiOut, "<br>\n");
1080 |   fprintf(cgiOut, "Multiple-SELECT\n");
1081 |   fprintf(cgiOut, "<br>\n");
1082 |   fprintf(cgiOut, "<select name=\"flavors\" multiple>\n");
1083 |   fprintf(cgiOut, "<option value=\"pistachio\">Pistachio\n");
1084 |   fprintf(cgiOut, "<option value=\"walnut\">Walnut\n");
1085 |   fprintf(cgiOut, "<option value=\"creme\">Creme\n");
1086 |   fprintf(cgiOut, "</select>\n");
1087 |   fprintf(cgiOut, "<p>Exclusive Radio Button Group: Age of "
1088 |     "Truck in Years\n");
1089 |   fprintf(cgiOut, "<input type=\"radio\" name=\"age\" "
1090 |     "value=\"1\">1\n");
1091 |   fprintf(cgiOut, "<input type=\"radio\" name=\"age\" "
1092 |     "value=\"2\">2\n");
1093 |   fprintf(cgiOut, "<input type=\"radio\" name=\"age\" "
1094 |     "value=\"3\" checked>3\n");
1095 |   fprintf(cgiOut, "<input type=\"radio\" name=\"age\" "
1096 |     "value=\"4\">4\n");
1097 |   fprintf(cgiOut, "<p>Nonexclusive Checkbox Group: "
1098 |     "Voting for Zero through Four Candidates\n");
1099 |   fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" "
1100 |     "value=\"A\">A\n");
1101 |   fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" "
1102 |     "value=\"B\">B\n");
1103 |   fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" "
1104 |     "value=\"C\">C\n");
1105 |   fprintf(cgiOut, "<input type=\"checkbox\" name=\"vote\" "
1106 |     "value=\"D\">D\n");
1107 |   fprintf(cgiOut, "<p>File Upload:\n");
1108 |   fprintf(cgiOut, "<input type=\"file\" name=\"file\" "
1109 |     "value=\"\"> (Select A Local File)\n");
1110 |   fprintf(cgiOut, "<p>\n");
1111 |   fprintf(cgiOut, "<p>Set a Cookie<p>\n");
1112 |   fprintf(cgiOut, "<input name=\"cname\" "
1113 |     "value=\"\"> Cookie Name\n");
1114 |   fprintf(cgiOut, "<input name=\"cvalue\" "
1115 |     "value=\"\"> Cookie Value<p>\n");
1116 |   fprintf(cgiOut, "<input type=\"submit\" "
1117 |     "name=\"testcgic\" value=\"Submit Request\">\n");
1118 |   fprintf(cgiOut, "<input type=\"reset\" "
1119 |     "value=\"Reset Request\">\n");
1120 |   fprintf(cgiOut, "<p>Save the CGI Environment<p>\n");
1121 |   fprintf(cgiOut, "Pressing this button will submit the form, then "
1122 |     "save the CGI environment so that it can be replayed later "
1123 |     "by calling cgiReadEnvironment (in a debugger, for "
1124 |     "instance).<p>\n");
1125 |   fprintf(cgiOut, "<input type=\"submit\" name=\"saveenvironment\" "
1126 |     "value=\"Save Environment\">\n");
1127 |   fprintf(cgiOut, "</form>\n");
1128 | }
1129 | 
1130 | Note the use of enctype="multipart/form-data" in the 1131 | FORM tag. This is absolutely required if the form 1132 | will contain file upload fields, as in the above example. Most 1133 | browsers will not even attempt file uploads without the 1134 | presence of this attribute. 1135 |

Examining CGI environment variables

1136 | The CGI standard specifies a number of environment variables 1137 | which are set by the server. However, servers are somewhat 1138 | unpredictable as to whether these variables will be null or 1139 | point to empty strings when an environment variable is not set. 1140 | Also, in order to allow the programmer to restore saved 1141 | CGI environments, the cgic library needs have a way of insulating 1142 | the programmer from the actual environment variables. 1143 |

1144 | Instead of calling getenv() to determine the value of a 1145 | variable such as HTTP_USER_AGENT (the browser software being used), 1146 | always use the 1147 | cgic copies of the environment variables, 1148 | which are always valid C strings (they are never null, although 1149 | they may point to an empty string). For instance, the cgic 1150 | variable containing the name of the browser software is 1151 | cgiUserAgent. The referring URL appears 1152 | in the variable cgiReferrer. 1153 |

How can I generate images from my cgic application?

1154 | cgic can be used in conjunction with the gd graphics library, which 1155 | can produce GIF images on the fly. 1156 |

1157 | The following short sample program hints at the possibilities: 1158 |

1159 | #include "cgic.h"
1160 | #include "gd.h"
1161 | 
1162 | char *colors[] = {
1163 |   "red", "green", "blue"
1164 | };
1165 | 
1166 | #define colorsTotal 3
1167 | 
1168 | int cgiMain() {
1169 |   int colorChosen;
1170 |   gdImagePtr im;
1171 |   int r, g, b;
1172 |   /* Use gd to create an image */
1173 |   im = gdImageCreate(64, 64);
1174 |   r = gdImageColorAllocate(im, 255, 0, 0);  
1175 |   g = gdImageColorAllocate(im, 0, 255, 0);  
1176 |   b = gdImageColorAllocate(im, 0, 0, 255);  
1177 |   /* Now use cgic to find out what color the user requested */
1178 |   cgiFormSelectSingle("color", 3, &colorChosen, 0);  
1179 |   /* Now fill with the desired color */
1180 |   switch(colorChosen) {
1181 |     case 0:
1182 |     gdImageFill(im, 32, 32, r);
1183 |     break;
1184 |     case 1:
1185 |     gdImageFill(im, 32, 32, g);
1186 |     break;
1187 |     case 2:
1188 |     gdImageFill(im, 32, 32, b);
1189 |     break;
1190 |   }  
1191 |   /* Now output the image. Note the content type! */
1192 |   cgiHeaderContentType("image/gif");
1193 |   /* Send the image to cgiOut */
1194 |   gdImageGif(im, cgiOut);
1195 |   /* Free the gd image */
1196 |   gdImageDestroy(im);
1197 |   return 0;
1198 | }
1199 | 
1200 | Note that this program would need to be linked with both cgic.o 1201 | and libgd.a. Often programs of this type respond to one 1202 | cgiPathInfo value or set of form fields by returning an HTML page 1203 | with an inline image reference that, in turn, generates a GIF image. 1204 |

Debugging CGI applications: using capture

1205 | Debugging CGI applications can be a painful task. Since CGI applications 1206 | run in a special environment created by the web server, it is difficult 1207 | to execute them in a debugger. However, the cgic library provides a way 1208 | of capturing "live" CGI environments to a file, and also provides a way 1209 | to reload saved environments. 1210 |

1211 | The provided program 'capture.c' can be used to capture CGI 1212 | environments. Just change the first line of the cgiMain() function 1213 | of capture.c to save the CGI environment to a filename appropriate 1214 | on your system and type 'make capture'. Then place capture in your 1215 | cgi directory and set the form action or other link you want to test 1216 | to point to it. When the form submission or other link takes place, 1217 | capture will write the CGI environment active at that time to 1218 | the filename you specified in the source. The 1219 | cgiReadEnvironment() function can then 1220 | be invoked on the same filename at the beginning of the cgiMain() function 1221 | of the application you want to test in order to restore the captured 1222 | environment. You can then execute your program in the debugger of your choice, 1223 | and it should perform exactly as it would have performed had 1224 | it been launched by the actual web server, including file uploads, 1225 | cookies and all other phenomena within the purview of cgic. 1226 |

1227 | Important: Make sure you specify the full path, as the 1228 | current working directory of a CGI script may not be what you 1229 | think it is! 1230 |

1231 | Even More Important: If you call getenv() yourself 1232 | in your code, instead of using the provided 1233 | cgic copies of the CGI environment variables, you will 1234 | not get the values you expect when running with 1235 | a saved CGI environment. Always use the cgic variables instead 1236 | of calling getenv(). 1237 |

cgic function reference

1238 |
1239 |
cgiFormResultType cgiFormString( 1240 | char *name, char *result, int max) 1241 |
cgiFormString attempts to retrieve the string sent for the 1242 | specified input field. The text will be copied into 1243 | the buffer specified by result, up to but not 1244 | exceeding max-1 bytes; a terminating null is then 1245 | added to complete the string. Regardless of the newline 1246 | format submitted by the browser, cgiFormString always 1247 | encodes each newline as a single line feed (ascii decimal 10); as 1248 | a result the final string may be slightly shorter than indicated 1249 | by a call to 1250 | cgiFormStringSpaceNeeded but will never be longer. 1251 | cgiFormString returns cgiFormSuccess if the string was 1252 | successfully retrieved, 1253 | cgiFormTruncated if the string was 1254 | retrieved but was truncated to fit the buffer, 1255 | cgiFormEmpty if the string was 1256 | retrieved but was empty, and cgiFormNotFound if no 1257 | such input field was submitted. In the last case, 1258 | an empty string is copied to result. 1259 |

1260 | cgiFormResultType cgiFormStringNoNewlines( 1261 | char *name, char *result, int max) 1262 |
1263 | cgiFormStringNoNewlines() is exactly equivalent to 1264 | cgiFormString(), except 1265 | that any carriage returns or line feeds that occur in the input 1266 | will be stripped out. The use of this function is recommended 1267 | for single-line text input fields, as some browsers will submit 1268 | carriage returns and line feeds when they should not. 1269 |

1270 | cgiFormResultType cgiFormStringSpaceNeeded( 1271 | char *name, int *length) 1272 |
1273 | cgiFormStringSpaceNeeded() is used to determine the length of the input text 1274 | buffer needed to receive the contents of the specified input field. 1275 | This is useful if the programmer wishes to allocate sufficient memory 1276 | for input of arbitrary length. The actual length of the string 1277 | retrieved by a subsequent call to cgiFormString() may be slightly shorter 1278 | but will never be longer than *result. On success, cgiFormStringSpaceNeeded() 1279 | sets the value pointed to by length to the number of bytes of data, 1280 | including the terminating null, and returns cgiFormSuccess. If no 1281 | value was submitted for the specified field, cgiFormStringSpaceNeeded sets 1282 | the value pointed to by length to 1 and returns cgiFormNotFound. 1 is 1283 | set to ensure space for an empty string (a single null 1284 | character) if cgiFormString is called despite the return value. 1285 | 1286 |

cgiFormResultType cgiFormStringMultiple( 1287 | char *name, char ***ptrToStringArray) 1288 |
cgiFormStringMultiple is useful in the unusual case in which several 1289 | input elements in the form have the same name and, for whatever 1290 | reason, the programmer does not wish to use the checkbox, radio 1291 | button and selection menu functions provided below. This is 1292 | occasionally needed if the programmer cannot know 1293 | in advance what values might appear in a multiple-selection list 1294 | or group of checkboxes on a form. The value pointed to 1295 | by result will be set to a pointer to an array of strings; the last 1296 | entry in the array will be a null pointer. This array is allocated 1297 | by the CGI library. Important: when done working with the array, 1298 | you must call cgiStringArrayFree() with the array pointer as the 1299 | argument. cgiFormStringMultiple() returns cgiFormSuccess if at least 1300 | one occurrence of the name is found, cgiFormNotFound 1301 | if no occurrences are found, or cgiFormMemory if not enough 1302 | memory is available to allocate the array to be returned. 1303 | In all cases except the last, ptrToStringArray is set to point to a 1304 | valid array of strings, with the last element in the array being a 1305 | null pointer; in the out-of-memory case ptrToStringArray is set to 1306 | a null pointer. 1307 | 1308 |

cgiFormResultType cgiFormEntries( 1309 | char ***ptrToStringArray) 1310 |
cgiFormEntries is useful when the programmer cannot know the names 1311 | of all relevant form fields in advance. The value pointed to 1312 | by result will be set to a pointer to an array of strings; the last 1313 | entry in the array will be a null pointer. This array is allocated 1314 | by the CGI library. Important: when done working with the array, 1315 | you must call cgiStringArrayFree() with the array pointer as the 1316 | argument. cgiFormEntries() returns cgiFormSuccess except in the event of an out of memory error. 1317 | On success, ptrToStringArray is set to point to a 1318 | valid array of strings, with the last element in the array being a 1319 | null pointer; in the out-of-memory case ptrToStringArray is set to 1320 | a null pointer, and 1321 | cgiFormOutOfMemory is returned. 1322 | 1323 |

void cgiStringArrayFree(char **stringArray) 1324 | 1325 |
1326 | cgiStringArrayFree() is used to free the memory associated with 1327 | a string array created by 1328 | cgiFormStringMultiple(), 1329 | cgiFormEntries(), or 1330 | cgiFormCookies(). 1331 |

cgiFormResultType cgiFormInteger( 1332 | char *name, int *result, int defaultV) 1333 |
cgiFormInteger() attempts to retrieve the integer sent for the 1334 | specified input field. The value pointed to by result 1335 | will be set to the value submitted. cgiFormInteger() returns 1336 | cgiFormSuccess if the value was successfully retrieved, 1337 | cgiFormEmpty if the value submitted is an empty string, 1338 | cgiFormBadType if the value submitted is not an integer, 1339 | and cgiFormNotFound if no such input field was submitted. 1340 | In the last three cases, the value pointed to by result 1341 | is set to the specified default. 1342 |

1343 | cgiFormResultType cgiFormIntegerBounded( 1344 | char *name, int *result, int min, int max, int defaultV) 1345 |
cgiFormIntegerBounded() attempts to retrieve the integer sent for the 1346 | specified input field, and constrains the result to be within 1347 | the specified bounds. The value pointed to by result 1348 | will be set to the value submitted. cgiFormIntegerBounded() returns 1349 | cgiFormSuccess if the value was successfully retrieved, 1350 | cgiFormConstrained if the value was out of bounds and result 1351 | was adjusted accordingly, cgiFormEmpty if the value submitted is 1352 | an empty string, cgiFormBadType if the value submitted is not an 1353 | integer, and cgiFormNotFound if no such input field was submitted. 1354 | In the last three cases, the value pointed to by result 1355 | is set to the specified default. 1356 | 1357 |

cgiFormResultType cgiFormDouble( 1358 | char *name, double *result, double defaultV) 1359 |
cgiFormDouble attempts to retrieve the floating-point value sent for 1360 | the specified input field. The value pointed to by result 1361 | will be set to the value submitted. cgiFormDouble returns 1362 | cgiFormSuccess if the value was successfully retrieved, 1363 | cgiFormEmpty if the value submitted is an empty string, 1364 | cgiFormBadType if the value submitted is not a number, 1365 | and cgiFormNotFound if no such input field was submitted. 1366 | In the last three cases, the value pointed to by result 1367 | is set to the specified default. 1368 |

1369 | cgiFormResultType cgiFormDoubleBounded( 1370 | char *name, double *result, double min, double max, 1371 | double defaultV) 1372 |
1373 | cgiFormDoubleBounded() attempts to retrieve the floating-point 1374 | value sent for the specified input field, and constrains the 1375 | result to be within the specified bounds. The value pointed to by 1376 | result will be set to the value submitted. cgiFormDoubleBounded() returns 1377 | cgiFormSuccess if the value was successfully retrieved, 1378 | cgiFormConstrained if the value was out of bounds and result 1379 | was adjusted accordingly, cgiFormEmpty if the value submitted is 1380 | an empty string, cgiFormBadType if the value submitted is not a 1381 | number, and cgiFormNotFound if no such input field was submitted. 1382 | In the last three cases, the value pointed to by result 1383 | is set to the specified default. 1384 | 1385 |

1386 | cgiFormResultType cgiFormSelectSingle( 1387 | char *name, char **choicesText, int choicesTotal, 1388 | int *result, int defaultV) 1389 |
1390 | cgiFormSelectSingle() retrieves the selection number associated with a 1391 | <SELECT> element that does not allow multiple selections. name 1392 | should identify the NAME attribute of the <SELECT> element. choicesText 1393 | should point to an array of strings identifying each choice; 1394 | choicesTotal should indicate the total number of choices. The value 1395 | pointed to by result will be set to the position of the actual choice 1396 | selected within the choicesText array, if any, or to the value of 1397 | default, if no selection was submitted or an invalid selection was 1398 | made. cgiFormSelectSingle() returns cgiFormSuccess if the value was 1399 | successfully retrieved, cgiFormNotFound if no selection 1400 | was submitted, and cgiFormNoSuchChoice if the selection 1401 | does not match any of the possibilities in the choicesText array. 1402 |
1403 | 1404 | cgiFormResultType cgiFormSelectMultiple( 1405 | char *name, char **choicesText, int choicesTotal, 1406 | int *result, int *invalid) 1407 |
cgiFormSelectMultiple() retrieves the selection numbers associated with a 1408 | <SELECT> element that does allow multiple selections. name should 1409 | identify the NAME attribute of the <SELECT> element. choicesText 1410 | should point to an array of strings identifying each choice; 1411 | choicesTotal should indicate the total number of choices. result 1412 | should point to an array of integers with as many elements as there 1413 | are strings in the choicesText array. For each choice in the 1414 | choicesText array that is selected, the corresponding integer in 1415 | the result array will be set to one; other entries in the result 1416 | array will be set to zero. cgiFormSelectMultiple() returns cgiFormSuccess 1417 | if at least one valid selection was successfully retrieved or 1418 | cgiFormNotFound if no valid selections were submitted. 1419 | The integer pointed to by invalid is set to the number of 1420 | invalid selections that were submitted, which should be zero 1421 | unless the form and the choicesText array do not agree. 1422 | 1423 |

1424 | 1425 | cgiFormResultType cgiFormSubmitClicked( 1426 | char *name) 1427 |
1428 | It is often desirable to know whether a particular submit button was clicked, 1429 | when multiple submit buttons with different name attributes exist. 1430 | cgiFormSubmitClicked is an alternative name for the 1431 | cgiFormCheckboxSingle function, 1432 | which is suitable for testing whether a particular submit button 1433 | was used. 1434 |

1435 | 1436 | cgiFormResultType cgiFormCheckboxSingle( 1437 | char *name) 1438 |
1439 | cgiFormCheckboxSingle determines whether the checkbox with the specified name 1440 | is checked. cgiFormCheckboxSingle returns cgiFormSuccess if the 1441 | button is checked, cgiFormNotFound if the checkbox is 1442 | not checked. cgiFormCheckboxSingle is intended for single 1443 | checkboxes with a unique name; see below for functions to 1444 | deal with multiple checkboxes with the same name, and 1445 | with radio buttons. 1446 | 1447 |

1448 | cgiFormResultType cgiFormCheckboxMultiple( 1449 | char *name, char **valuesText, int valuesTotal, 1450 | int *result, int *invalid) 1451 |
cgiFormCheckboxMultiple() determines which checkboxes among a group 1452 | of checkboxes with the same name are checked. This is distinct 1453 | from radio buttons (see cgiFormRadio). 1454 | valuesText 1455 | should point to an array of strings identifying the VALUE 1456 | attribute of each checkbox; valuesTotal should indicate the total 1457 | number of checkboxes. result should point to an array of integers with 1458 | as many elements as there are strings in the valuesText array. For 1459 | each choice in the valuesText array that is selected, the corresponding 1460 | integer in the result array will be set to one; other entries in the 1461 | result array will be set to zero. cgiFormCheckboxMultiple returns 1462 | cgiFormSuccess if at least one valid checkbox was checked or 1463 | cgiFormNotFound if no valid checkboxes were checked. 1464 | The integer pointed to by invalid is set to the number of 1465 | invalid selections that were submitted, which should be zero 1466 | unless the form and the valuesText array do not agree. 1467 |

1468 | cgiFormResultType cgiFormRadio( 1469 | char *name, char **valuesText, int valuesTotal, 1470 | int *result, int defaultV) 1471 |
cgiFormRadio() determines which, if any, of a group of radio boxes with 1472 | the same name was selected. valuesText should point to an array of 1473 | strings identifying the VALUE attribute of each radio box; 1474 | valuesTotal should indicate the total number of radio boxes. The value 1475 | pointed to by result will be set to the position of the actual choice 1476 | selected within the valuesText array, if any, or to the value of 1477 | default, if no radio box was checked or an invalid selection was 1478 | made. cgiFormRadio() returns cgiFormSuccess if a checked radio box was 1479 | found in the group, cgiFormNotFound if no box was checked, and 1480 | cgiFormNoSuchChoice if the radio box submitted does not match any of 1481 | the possibilities in the valuesText array. 1482 | 1483 |
cgiFormResultType cgiFormFileName( 1484 | char *name, char *fileName, int max) 1485 |
cgiFormFileName attempts to retrieve the file name uploaded by the 1486 | user for the specified form input field of type file. 1487 | NEVER, EVER TRUST THIS FILENAME TO BE REASONABLE AND 1488 | SAFE FOR DIRECT USE ON THE SERVER SIDE. 1489 | The text will be copied into 1490 | the buffer specified by fileName, up to but not 1491 | exceeding max-1 bytes; a terminating null is then 1492 | added to complete the string. cgiFormFileName returns 1493 | cgiFormSuccess if the string was 1494 | successfully retrieved and was not empty, 1495 | cgiFormNoFileName if the string was 1496 | successfully retrieved but empty indicating that no file was uploaded, 1497 | cgiFormTruncated if the string was 1498 | retrieved but was truncated to fit the buffer, 1499 | and cgiFormNotFound if no 1500 | such input field was submitted. In the last case, 1501 | an empty string is copied to result. 1502 |
cgiFormResultType cgiFormFileSize( 1503 | char *name, int *sizeP) 1504 |
cgiFormFileSize attempts to retrieve the size, in bytes, of a 1505 | file uploaded by the browser in response to the 1506 | input field of type file specified by the 1507 | name parameter. On success, the size is stored 1508 | to *sizeP, and this function returns 1509 | cgiFormSuccess. If the form 1510 | field does not exist, this function returns 1511 | cgiFormNotFound. 1512 | If the form field exists but no file was uploaded, this function 1513 | returns cgiFormNotAFile. 1514 |
cgiFormResultType cgiFormFileContentType( 1515 | char *name, char *contentType, int max) 1516 |
cgiFormString attempts to retrieve the content name claimed by the 1517 | user for the specified form input field of type file. 1518 | THERE IS NO GUARANTEE THAT THE CONTENT TYPE WILL BE 1519 | ACCURATE. 1520 | The content type string will be copied into 1521 | the buffer specified by contentType, up to but not 1522 | exceeding max-1 bytes; a terminating null is then 1523 | added to complete the string. cgiFormFileContentType returns 1524 | cgiFormSuccess if the string was 1525 | successfully retrieved and was not empty, 1526 | cgiFormNoContentType if the string was 1527 | successfully retrieved but empty indicating that no file was uploaded 1528 | or the browser did not know the content type, 1529 | cgiFormTruncated if the string was 1530 | retrieved but was truncated to fit the buffer, 1531 | and cgiFormNotFound if no 1532 | such input field was submitted. In the last case, 1533 | an empty string is copied to result. 1534 | 1535 |
cgiFormResultType cgiFormFileOpen( 1536 | char *name, cgiFilePtr *cfpp) 1537 |
cgiFormFileOpen attempts to open the actual uploaded file data for 1538 | the specified form field of type file. Upon success, 1539 | this function returns retrieve the content name claimed by the 1540 | user for the specified form input field of type file. 1541 | On success, this function sets *cfpp to a valid cgiFilePtr 1542 | object for use with cgiFormFileRead 1543 | and returns cgiFormSuccess. 1544 | On failure, this function sets *cfpp to a null pointer, and 1545 | returns cgiFormNotFound, 1546 | cgiFormNotAFile, 1547 | cgiFormMemory or 1548 | cgiFormIO as appropriate. 1549 |

1550 | See also cgiFormFileRead 1551 | and cgiFormFileClose. 1552 |

cgiFormResultType cgiFormFileRead( 1553 | cgiFilePtr cfp, char *buffer, int bufferSize, int *gotP) 1554 |
cgiFormFileRead attempts to read up to bufferSize 1555 | bytes from a cgiFilePtr object previously opened with 1556 | cgiFormFileOpen. If any data 1557 | is successfully read, it is copied to buffer, 1558 | and the number of bytes successfully read is stored 1559 | to *gotP. This function returns 1560 | cgiFormSuccess if any data 1561 | is successfully read. At end of file, this function 1562 | returns cgiFormEOF. In the event 1563 | of an I/O error, this function returns 1564 | cgiFormIO. If cfp is a null pointer, 1565 | this function returns cgiFormOpenFailed. 1566 |

1567 | See also cgiFormFileOpen 1568 | and cgiFormFileClose. 1569 |

cgiFormResultType cgiFormFileClose( 1570 | cgiFilePtr cfp) 1571 |
cgiFormFileClose closes a cgiFilePtr object previously opened 1572 | with cgiFormFileOpen, freeing 1573 | memory and other system resources. This 1574 | function returns cgiFormSuccess 1575 | unless cfp is null, in which case 1576 | cgiFormOpenFailed is returned. 1577 |

1578 | See also cgiFormFileOpen 1579 | and cgiFormFileRead. 1580 |

1581 | void cgiHeaderLocation(char *redirectUrl) 1582 |
1583 | cgiHeaderLocation() should be called if the programmer wishes to 1584 | redirect the user to a different URL. No further output 1585 | is needed in this case. 1586 |

1587 | If you wish to set cookies, 1588 | you must make your calls to 1589 | cgiHeaderCookieSet 1590 | and 1591 | cgiHeaderCookieSetInteger 1592 | BEFORE invoking cgiHeaderLocation. 1593 |

1594 | void cgiHeaderStatus(int status, char *statusMessage) 1595 |
1596 | cgiHeaderStatus() should be called if the programmer wishes to 1597 | output an HTTP error status code instead of a document. The status 1598 | code is the first argument; the second argument is the status 1599 | message to be displayed to the user. 1600 |

1601 | If you wish to set cookies, 1602 | you must make your calls to 1603 | cgiHeaderCookieSet 1604 | and 1605 | cgiHeaderCookieSetInteger 1606 | BEFORE invoking cgiHeaderStatus. 1607 |

1608 | void cgiHeaderContentType(char *mimeType) 1609 |
1610 | cgiHeaderContentType() should be called if the programmer wishes to 1611 | output a new document in response to the user's request. This is 1612 | the normal case. The single argument is the MIME document type 1613 | of the response; typical values are "text/html" for HTML documents, 1614 | "text/plain" for plain ASCII without HTML tags, "image/gif" for 1615 | a GIF image and "audio/basic" for .au-format audio. 1616 |

1617 | If you wish to set cookies, 1618 | you must make your calls to 1619 | cgiHeaderCookieSet 1620 | and 1621 | cgiHeaderCookieSetInteger 1622 | BEFORE invoking cgiHeaderContentType. 1623 |

1624 | void cgiHeaderCookieSet(char *name, char *value, 1625 | int secondsToLive, char *path, char *domain, int options) 1626 |
1627 | cgiHeaderCookieSet() should be called when the programmer wishes 1628 | to store a piece of information in the user's browser, so that the 1629 | stored information is again presented to the server on subsequent 1630 | accesses to the relevant site. The first argument is the name of the 1631 | cookie to be stored; for best results in all browsers, use a short 1632 | name without spaces or unusual punctuation. The second argument is 1633 | the value of the cookie to be stored. Again, for best results, use 1634 | a short string; it is recommended that cookies be used to store a 1635 | unique identifier which is then used to look up more detailed 1636 | information in a database on the server side. Attempts to store 1637 | elaborate information on the browser side are much more likely to fail. 1638 | The third argument is the number of seconds that the cookie should 1639 | be kept by the browser; 86400 is a single full day, 365*86400 is 1640 | roughly one year. The fourth argument is the partial URL of the 1641 | web site within which the cookie is relevant. If the cookie should 1642 | be sent to the server for every access to the entire site, 1643 | set this argument to /. The final argument is the 1644 | web site name or entire domain for which this cookie should be 1645 | submitted; if you choose to have the cookie sent back for an 1646 | entire domain, this argument must begin with a dot, such as 1647 | .boutell.dev. The cgic variables cgiScriptName 1648 | and cgiServerName are convenient 1649 | values for the fourth and fifth arguments. 1650 | The sixth argument is a bitmask for specifying cookie security options. 1651 | It can be zero (no options) or a bitwise-OR of the following 1652 | enum cgiCookieOption values: 1653 |
    1654 |
  • cgiCookieSecure
  • 1655 |
  • cgiCookieHttpOnly
  • 1656 |
  • cgiCookieSameSiteStrict
  • 1657 |
1658 | See HTTP cookies 1659 | for more information.
1660 | See also cgiHeaderCookieSetString, 1661 | cgiHeaderCookieSetInteger, 1662 | cgiCookieString, 1663 | cgiCookieInteger and 1664 | cgiCookies. 1665 |

1666 | void cgiHeaderCookieSetString(char *name, char *value, 1667 | int secondsToLive, char *path, char *domain) 1668 |
1669 | cgiHeaderCookieSetString() is kept for API compatibility reasons. It calls 1670 | cgiHeaderCookieSet with zero as sixth 1671 | argument, i.e. no cookie options are set. 1672 |

1673 | void cgiHeaderCookieSetInteger(char *name, int value, 1674 | int secondsToLive, char *path, char *domain) 1675 |
1676 | cgiHeaderCookieSetInteger() is identical to 1677 | cgiHeaderCookieSetString, 1678 | except that the value to be set is an integer rather than a string. 1679 | See cgiHeaderCookieSetString 1680 | for complete information. 1681 |
1682 |
cgiFormResultType cgiCookieString( 1683 | char *name, char *result, int max) 1684 |
cgiFormString attempts to retrieve the string sent for the 1685 | specified cookie (browser-side persistent storage). The 1686 | text will be copied into 1687 | the buffer specified by result, up to but not 1688 | exceeding max-1 bytes; a terminating null is then 1689 | added to complete the string. 1690 | cgiCookieString returns cgiFormSuccess if the string was 1691 | successfully retrieved, 1692 | cgiFormTruncated if the string was 1693 | retrieved but was truncated to fit the buffer, 1694 | cgiFormEmpty if the string was 1695 | retrieved but was empty, and cgiFormNotFound if no 1696 | such cookie was submitted. In the last case, 1697 | an empty string is copied to result. 1698 |

cgiFormResultType cgiCookieInteger( 1699 | char *name, int *result, int defaultV) 1700 | See also cgiCookieInteger, 1701 | cgiCookies, 1702 | cgiHeaderCookieSet, 1703 | cgiHeaderCookieSetString, and 1704 | cgiHeaderCookieSetInteger. 1705 |
cgiCookieInteger() attempts to retrieve the integer sent for the 1706 | specified cookie (browser-side persistent storage). The value 1707 | pointed to by result will be set to the value submitted. 1708 | cgiCookieInteger() returns 1709 | cgiFormSuccess if the value was successfully retrieved, 1710 | cgiFormEmpty if the value submitted is an empty string, 1711 | cgiFormBadType if the value submitted is not an integer, 1712 | and cgiFormNotFound if no such 1713 | input field was submitted. In the last three cases, the value 1714 | pointed to by result is set to the specified default. 1715 | See also cgiCookieString, 1716 | cgiCookies, 1717 | cgiHeaderCookieSet, 1718 | cgiHeaderCookieSetString, and 1719 | cgiHeaderCookieSetInteger. 1720 |

cgiFormResultType cgiCookies( 1721 | char *name, char ***ptrToStringArray) 1722 |
cgiCookies is useful when the programmer cannot know the names 1723 | of all relevant cookies (browser-side persistent strings) in advance. 1724 | The value pointed to by result will be set to a pointer to an array 1725 | of strings; the last 1726 | entry in the array will be a null pointer. This array is allocated 1727 | by the CGI library. Important: when done working with the array, 1728 | you must call cgiStringArrayFree() with the array pointer as the 1729 | argument. cgiCookies() returns cgiFormSuccess except in the event of an out of memory error. 1730 | On success, ptrToStringArray is set to point to a 1731 | valid array of strings, with the last element in the array being a 1732 | null pointer; in the out-of-memory case ptrToStringArray is set to 1733 | a null pointer, and 1734 | cgiFormOutOfMemory is returned. 1735 |

1736 | cgiFormResultType cgiHtmlEscape(char *s) 1737 |
1738 | cgiHtmlEscape() outputs the specified null-terminated string to 1739 | cgiOut, 1740 | escaping any <, &, and > characters encountered correctly so that 1741 | they do not interfere with HTML markup. Returns 1742 | cgiFormSuccess, or 1743 | cgiFormIO in the event of an I/O error. 1744 |

1745 |

1746 | cgiFormResultType cgiHtmlEscapeData(char *data, int len) 1747 |
1748 | cgiHtmlEscapeData() is identical to cgiHtmlEscape, 1749 | except that the data is not null-terminated. This version of the function 1750 | outputs len bytes. See cgiHtmlEscape 1751 | for more information. 1752 |

1753 | cgiFormResultType cgiValueEscape(char *s) 1754 |
1755 | cgiValueEscape() outputs the specified null-terminated string to 1756 | cgiOut, 1757 | escaping any " characters encountered correctly so that 1758 | they do not interfere with the quotation marks of HTML attribute 1759 | values. This is useful when outputting a string as part of the 1760 | value attribute of an input tag, or the href attribute of a link 1761 | or form tag. This function returns 1762 | cgiFormSuccess, or 1763 | cgiFormIO in the event of an I/O error. 1764 |

1765 |

1766 | cgiFormResultType cgiValueEscapeData(char *data, int len) 1767 |
1768 | cgiValueEscapeData() is identical to cgiValueEscape, 1769 | except that the data is not null-terminated. This version of the function 1770 | outputs len bytes. See cgiValueEscape 1771 | for more information. 1772 |

1773 | cgiEnvironmentResultType cgiWriteEnvironment(char *filename) 1774 |
1775 | cgiWriteEnvironment() can 1776 | be used to write the entire CGI environment, including 1777 | form data, to the specified output file; 1778 | cgiReadEnvironment() 1779 | can then be used to restore that environment from the specified 1780 | input file for debugging. Of course, these will only work as expected 1781 | if you use the cgic copies of the CGI environment 1782 | variables and cgiIn and 1783 | cgiOut rather than stdin and 1784 | stdout (also see above). These functions are useful in order 1785 | to capture real CGI situations while the web server is running, then 1786 | recreate them in a debugging environment. Both functions 1787 | return cgiEnvironmentSuccess on 1788 | success, cgiEnvironmentIO on an I/O 1789 | error, and cgiEnvironmentMemory 1790 | on an out-of-memory error. 1791 |

1792 | cgiEnvironmentResultType cgiReadEnvironment(char *filename) 1793 |
1794 | cgiReadEnvironment() restores a CGI environment saved to the specified file by 1795 | cgiWriteEnvironment(). 1796 | Of course, these will only work as expected 1797 | if you use the cgic copies of the CGI environment 1798 | variables and cgiIn and 1799 | cgiOut rather than stdin and 1800 | stdout (also see above). These functions are useful in order 1801 | to capture real CGI situations while the web server is running, then 1802 | recreate them in a debugging environment. Both functions 1803 | return cgiEnvironmentSuccess on success, 1804 | cgiEnvironmentIO on an I/O error, and 1805 | cgiEnvironmentMemory 1806 | on an out-of-memory error. 1807 |

int cgiMain() 1808 |
The programmer must write this function, which performs 1809 | the unique task of the program and is invoked by the true main() 1810 | function, found in the cgic library itself. The return value from 1811 | cgiMain will be the return value of the program. It is expected that 1812 | the user will make numerous calls to the cgiForm functions 1813 | from within this function. See how to write 1814 | a cgic application for details. 1815 |
1816 |

cgic variable reference

1817 | This section provides a reference guide to the various global 1818 | variables provided by cgic for the programmer to utilize. 1819 | These variables should always be used in preference to 1820 | stdin, stdout, and calls to getenv() in order to ensure 1821 | compatibility with the cgic CGI debugging features. 1822 |

1823 | Most of these variables are equivalent to various CGI environment 1824 | variables. The most important difference is that the cgic 1825 | environment string variables are never null pointers. They will always 1826 | point to valid C strings of zero or more characters. 1827 |

1828 |
char *cgiServerSoftware 1829 |
Points to the name of the server software, 1830 | or to an empty string if unknown. 1831 |
char *cgiServerName 1832 |
Points to the name of the server, 1833 | or to an empty string if unknown. 1834 |
char *cgiGatewayInterface 1835 |
Points to the name of the gateway interface (usually CGI/1.1), 1836 | or to an empty string if unknown. 1837 |
char *cgiServerProtocol 1838 |
Points to the protocol in use (usually HTTP/1.0), 1839 | or to an empty string if unknown. 1840 |
char *cgiServerPort 1841 |
Points to the port number on which the server is listening 1842 | for HTTP connections (usually 80), or an empty string if unknown. 1843 |
char *cgiRequestMethod 1844 |
Points to the method used in the request (usually GET or POST), 1845 | or an empty string if unknown (this should not happen). 1846 |
char *cgiPathInfo 1847 |
Most web servers recognize any additional path information in 1848 | the URL of the request beyond the name of the CGI program itself and 1849 | pass that information on to the program. cgiPathInfo points to this 1850 | additional path information. 1851 |
char *cgiPathTranslated 1852 |
Most web servers recognize any additional path information in 1853 | the URL of the request beyond the name of the CGI program itself and 1854 | pass that information on to the program. cgiPathTranslated points 1855 | to this additional path information, translated by the server into a 1856 | filesystem path on the local server. 1857 |
char *cgiScriptName 1858 |
Points to the name under which the program was invoked. 1859 |
char *cgiQueryString 1860 |
Contains any query information submitted by the user as a result 1861 | of a GET-method form or an <ISINDEX> tag. Note that this 1862 | information need not be parsed directly unless an <ISINDEX> tag 1863 | was used; normally it is parsed automatically by the cgic library. Use 1864 | the cgiForm family of functions to retrieve the values associated 1865 | with form input fields. See how to write 1866 | a cgic application for more information. 1867 |
char *cgiRemoteHost 1868 |
Points to the fully resolved hostname of the browser, if known, 1869 | or an empty string if unknown. 1870 |
char *cgiRemoteAddr 1871 |
Points to the dotted-decimal IP address of the browser, if known, 1872 | or an empty string if unknown. 1873 |
char *cgiAuthType 1874 |
Points to the type of authorization used for the request, 1875 | if any, or an empty string if none or unknown. 1876 |
char *cgiRemoteUser 1877 |
Points to the user name under which the user has 1878 | authenticated; an empty string if no authentication has 1879 | taken place. The certainty of this information depends on 1880 | the type of authorization in use; see 1881 | cgiAuthType. 1882 |
char *cgiRemoteIdent 1883 |
Points to the user name volunteered by the user via 1884 | the user identification protocol; an empty 1885 | string if unknown. This information is not secure. 1886 | Identification demons can be installed by users on 1887 | insecure systems such as Windows machines. 1888 |
char *cgiContentType 1889 |
Points to the MIME content type of the information 1890 | submitted by the user, if any; an empty string if no 1891 | information was submitted. If this string is equal to 1892 | application/x-www-form-urlencoded or 1893 | multipart/form-data, the cgic 1894 | library will automatically examine the form data submitted. 1895 | If this string has any other non-empty value, a different 1896 | type of data has been submitted. This is currently very rare, 1897 | as most browsers can only submit forms and file uploads which 1898 | cgic parses directly. 1899 |
char *cgiCookie 1900 |
Points to the raw cookie (browser-side persistent storage) 1901 | data submitted by the web browser. 1902 | Programmers should use the functions cgiCookies, 1903 | cgiCookieString and 1904 | cgiCookieInteger instead of 1905 | examining this string directly. 1906 |
char *cgiAccept 1907 |
Points to a space-separated list of MIME content types 1908 | acceptable to the browser (see 1909 | cgiHeaderContentType() ), or an empty string. Unfortunately, this variable 1910 | is not supplied in a useful form by most current browsers. Programmers wishing 1911 | to make decisions based on the capabilities of the browser 1912 | are advised to check the cgiUserAgent 1913 | variable against a list of browsers and capabilities instead. 1914 |
char *cgiUserAgent 1915 |
1916 | Points to the name of the browser in use, or an empty 1917 | string if this information is not available. 1918 |
char *cgiReferrer 1919 |
1920 | Points to the URL of the previous page visited by the user. This is 1921 | often the URL of the form that brought the user to your program. 1922 | Note that reporting this information is entirely up to the browser, 1923 | which may choose not do so, and may choose not to do so truthfully. 1924 | However, this variable is typically accurate. The frequently 1925 | used misspelling cgiReferer is also supplied as a macro. 1926 |
int cgiContentLength 1927 |
The number of bytes of form or query data received. 1928 | Note that if the submission is a form or query submission 1929 | the library will read and parse all the information 1930 | directly from cgiIn and/or cgiQueryString. The programmer should 1931 | not do so, and indeed the cgiIn pointer will be at end-of-file 1932 | in such cases. 1933 |
FILE *cgiOut 1934 |
Pointer to CGI output. The cgiHeader functions, such as 1935 | cgiHeaderContentType, should 1936 | be used first to output the mime headers; the output HTML 1937 | page, GIF image or other web document should then be written 1938 | to cgiOut by the programmer using standard C I/O functions 1939 | such as fprintf() and fwrite(). cgiOut is normally equivalent 1940 | to stdout; however, it is recommended that cgiOut be used to 1941 | ensure compatibility with future versions of cgic for 1942 | specialized environments. 1943 |
FILE *cgiIn 1944 |
Pointer to CGI input. In 99.99% of cases, you will not 1945 | need this. CGIC 2.0 supports both regular POST form submissions 1946 | and multipart/form-data file upload form submissions directly. 1947 |
1948 |

cgic result code reference

1949 |

1950 | In most cases, cgic functions are designed to produce reasonable results 1951 | even when browsers and users do unreasonable things. However, it is sometimes 1952 | important to know precisely which unreasonable things took place, especially 1953 | when assigning a default value or bounding a value is an inadequate 1954 | solution. The following result codes are useful in making this determination. 1955 |

1956 |
cgiFormSuccess 1957 |
Indicates that the function successfully performed at least one 1958 | action (or retrieved at least one value, where applicable). 1959 |
cgiFormTruncated 1960 |
Indicates that a string value retrieved from the user was 1961 | cut short to avoid overwriting the end of a buffer. 1962 |
cgiFormBadType 1963 |
Indicates that a "numeric" value submitted by the user was 1964 | in fact not a legal number. 1965 |
cgiFormEmpty 1966 |
Indicates that a field was retrieved but contained no data. 1967 |
cgiFormNotFound 1968 |
Indicates that no value was submitted for a particular field. 1969 |
cgiFormConstrained 1970 |
Indicates that a numeric value was beyond the specified bounds 1971 | and was forced to the lower or upper bound as appropriate. 1972 |
cgiFormNoSuchChoice 1973 |
Indicates that the value submitted for a single-choice field 1974 | (such as a radio-button group) was not one of the acceptable values. 1975 | This usually indicates a discrepancy between the form and the program. 1976 |
cgiFormEOF 1977 |
Returned by cgiFormFileRead 1978 | when, at the start of the call, the cgiFilePtr object is already 1979 | positioned at the end of the uploaded file data. 1980 |
cgiFormIO 1981 |
Returned by cgiFormFileRead 1982 | when an I/O error occurs while reading uploaded file data. 1983 |
cgiFormNotAFile 1984 |
Returned in response to an attempt to manipulate a form field 1985 | that is not a file upload field using a file-related function. 1986 |
cgiFormNoContentType 1987 |
Returned in response to an attempt to fetch the content type of 1988 | a file-upload field when the content type is not specified by the browser. 1989 |
cgiFormNoFileName 1990 |
Returned in response to an attempt to fetch the file name of 1991 | a file-upload field when a file name is not specified by the browser. 1992 |
cgiFormOpenFailed 1993 |
Returned in response to an attempt to read from a null 1994 | cgiFilePtr object, typically when the programmer has failed to 1995 | check the result of a call to cgiFormFileOpen. 1996 |
cgiEnvironmentMemory 1997 |
Indicates that an attempt to read or write the CGI environment 1998 | to or from a capture file failed due to an out-of-memory error. 1999 |
cgiEnvironmentSuccess 2000 |
Indicates that an attempt to read or write the CGI environment 2001 | to or from a capture file was successful. 2002 |
cgiEnvironmentIO 2003 |
Indicates that an attempt to read or write the CGI environment 2004 | to or from a capture file failed due to an I/O error. 2005 |
cgiEnvironmentWrongVersion 2006 |
Indicates that an attempt to read from a saved debugging CGI environment 2007 | produced by a pre-2.0 version of CGIC was made. 2008 |
2009 |

cgic quick index

2010 | cgiAccept | 2011 | cgiAuthType | 2012 | cgiContentLength | 2013 | cgiContentType | 2014 | cgiEnvironmentIO | 2015 | cgiEnvironmentMemory | 2016 | cgiEnvironmentSuccess | 2017 | cgiCookieInteger | 2018 | cgiCookies | 2019 | cgiHeaderCookieSet | 2020 | cgiHeaderCookieSetString | 2021 | cgiHeaderCookieSetInteger | 2022 | cgiCookieString | 2023 | cgiCookieInteger | 2024 | cgiHtmlEscape | 2025 | cgiHtmlEscapeData | 2026 | cgiValueEscape | 2027 | cgiValueEscapeData | 2028 | cgiFormBadType | 2029 | cgiFormCheckboxMultiple() | 2030 | cgiFormCheckboxSingle() | 2031 | cgiFormConstrained | 2032 | cgiFormDouble() | 2033 | cgiFormDoubleBounded() | 2034 | cgiFormEOF | 2035 | cgiFormEmpty | 2036 | cgiFormEntries | 2037 | cgiFormFileClose | 2038 | cgiFormFileContentType | 2039 | cgiFormFileName | 2040 | cgiFormFileOpen | 2041 | cgiFormFileRead | 2042 | cgiFormFileSize | 2043 | cgiFormInteger() | 2044 | cgiFormIntegerBounded() | 2045 | cgiFormNoContentType | 2046 | cgiFormNoFileName | 2047 | cgiFormNoSuchChoice | 2048 | cgiFormNotAFile | 2049 | cgiFormNotFound | 2050 | cgiFormRadio() | 2051 | cgiFormSelectMultiple() | 2052 | cgiFormSelectSingle() | 2053 | cgiFormString() | 2054 | cgiFormStringMultiple() | 2055 | cgiFormStringNoNewlines() | 2056 | cgiFormStringSpaceNeeded() | 2057 | cgiFormSuccess | 2058 | cgiFormTruncated | 2059 | cgiGatewayInterface | 2060 | cgiHeaderContentType() | 2061 | cgiHeaderLocation() | 2062 | cgiHeaderStatus() | 2063 | cgiIn | 2064 | cgiMain() 2065 | cgiOut | 2066 | cgiPathInfo | 2067 | cgiPathTranslated | 2068 | cgiQueryString | 2069 | cgiReadEnvironment() | 2070 | cgiReferrer() | 2071 | cgiRemoteAddr | 2072 | cgiRemoteHost | 2073 | cgiRemoteIdent | 2074 | cgiRemoteUser | 2075 | cgiRequestMethod | 2076 | cgiScriptName | 2077 | cgiServerName | 2078 | cgiServerPort | 2079 | cgiServerProtocol | 2080 | cgiServerSoftware | 2081 | cgiStringArrayFree() | 2082 | cgiUserAgent | 2083 | cgiWriteEnvironment() 2084 | 2085 | 2086 | -------------------------------------------------------------------------------- /cgictest.c: -------------------------------------------------------------------------------- 1 | /* Change this if the SERVER_NAME environment variable does not report 2 | the true name of your web server. */ 3 | #if 1 4 | #define SERVER_NAME cgiServerName 5 | #endif 6 | #if 0 7 | #define SERVER_NAME "www.boutell.dev" 8 | #endif 9 | 10 | /* You may need to change this, particularly under Windows; 11 | it is a reasonable guess as to an acceptable place to 12 | store a saved environment in order to test that feature. 13 | If that feature is not important to you, you needn't 14 | concern yourself with this. */ 15 | 16 | #ifdef WIN32 17 | #define SAVED_ENVIRONMENT "c:\\cgicsave.env" 18 | #else 19 | #define SAVED_ENVIRONMENT "/tmp/cgicsave.env" 20 | #endif /* WIN32 */ 21 | 22 | #include 23 | #include "cgic.h" 24 | #include 25 | #include 26 | 27 | void HandleSubmit(); 28 | void ShowForm(); 29 | void CookieSet(); 30 | void Name(); 31 | void Address(); 32 | void Hungry(); 33 | void Temperature(); 34 | void Frogs(); 35 | void Color(); 36 | void Flavors(); 37 | void NonExButtons(); 38 | void RadioButtons(); 39 | void File(); 40 | void Entries(); 41 | void Cookies(); 42 | void LoadEnvironment(); 43 | void SaveEnvironment(); 44 | 45 | int cgiMain() { 46 | #ifdef DEBUG 47 | LoadEnvironment(); 48 | #endif /* DEBUG */ 49 | /* Load a previously saved CGI scenario if that button 50 | has been pressed. */ 51 | if (cgiFormSubmitClicked("loadenvironment") == cgiFormSuccess) { 52 | LoadEnvironment(); 53 | } 54 | /* Set any new cookie requested. Must be done *before* 55 | outputting the content type. */ 56 | CookieSet(); 57 | /* Send the content type, letting the browser know this is HTML */ 58 | cgiHeaderContentType("text/html"); 59 | /* Top of the page */ 60 | fprintf(cgiOut, "\n"); 61 | fprintf(cgiOut, "cgic test\n"); 62 | fprintf(cgiOut, "

cgic test

\n"); 63 | /* If a submit button has already been clicked, act on the 64 | submission of the form. */ 65 | if ((cgiFormSubmitClicked("testcgic") == cgiFormSuccess) || 66 | cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess) 67 | { 68 | HandleSubmit(); 69 | fprintf(cgiOut, "
\n"); 70 | } 71 | /* Now show the form */ 72 | ShowForm(); 73 | /* Finish up the page */ 74 | fprintf(cgiOut, "\n"); 75 | return 0; 76 | } 77 | 78 | void HandleSubmit() 79 | { 80 | Name(); 81 | Address(); 82 | Hungry(); 83 | Temperature(); 84 | Frogs(); 85 | Color(); 86 | Flavors(); 87 | NonExButtons(); 88 | RadioButtons(); 89 | File(); 90 | Entries(); 91 | Cookies(); 92 | /* The saveenvironment button, in addition to submitting the form, 93 | also saves the resulting CGI scenario to disk for later 94 | replay with the 'load saved environment' button. */ 95 | if (cgiFormSubmitClicked("saveenvironment") == cgiFormSuccess) { 96 | SaveEnvironment(); 97 | } 98 | } 99 | 100 | void Name() { 101 | char name[81]; 102 | cgiFormStringNoNewlines("name", name, 81); 103 | fprintf(cgiOut, "Name: "); 104 | cgiHtmlEscape(name); 105 | fprintf(cgiOut, "
\n"); 106 | } 107 | 108 | void Address() { 109 | char address[241]; 110 | cgiFormString("address", address, 241); 111 | fprintf(cgiOut, "Address:
\n");
112 | 	cgiHtmlEscape(address);
113 | 	fprintf(cgiOut, "
\n"); 114 | } 115 | 116 | void Hungry() { 117 | if (cgiFormCheckboxSingle("hungry") == cgiFormSuccess) { 118 | fprintf(cgiOut, "I'm Hungry!
\n"); 119 | } else { 120 | fprintf(cgiOut, "I'm Not Hungry!
\n"); 121 | } 122 | } 123 | 124 | void Temperature() { 125 | double temperature; 126 | cgiFormDoubleBounded("temperature", &temperature, 80.0, 120.0, 98.6); 127 | fprintf(cgiOut, "My temperature is %f.
\n", temperature); 128 | } 129 | 130 | void Frogs() { 131 | int frogsEaten; 132 | cgiFormInteger("frogs", &frogsEaten, 0); 133 | fprintf(cgiOut, "I have eaten %d frogs.
\n", frogsEaten); 134 | } 135 | 136 | char *colors[] = { 137 | "Red", 138 | "Green", 139 | "Blue" 140 | }; 141 | 142 | void Color() { 143 | int colorChoice; 144 | cgiFormSelectSingle("colors", colors, 3, &colorChoice, 0); 145 | fprintf(cgiOut, "I am: %s
\n", colors[colorChoice]); 146 | } 147 | 148 | char *flavors[] = { 149 | "pistachio", 150 | "walnut", 151 | "creme" 152 | }; 153 | 154 | void Flavors() { 155 | int flavorChoices[3]; 156 | int i; 157 | int result; 158 | int invalid; 159 | result = cgiFormSelectMultiple("flavors", flavors, 3, 160 | flavorChoices, &invalid); 161 | if (result == cgiFormNotFound) { 162 | fprintf(cgiOut, "I hate ice cream.

\n"); 163 | } else { 164 | fprintf(cgiOut, "My favorite ice cream flavors are:\n"); 165 | fprintf(cgiOut, "

    \n"); 166 | for (i=0; (i < 3); i++) { 167 | if (flavorChoices[i]) { 168 | fprintf(cgiOut, "
  • %s\n", flavors[i]); 169 | } 170 | } 171 | fprintf(cgiOut, "
\n"); 172 | } 173 | } 174 | 175 | char *ages[] = { 176 | "1", 177 | "2", 178 | "3", 179 | "4" 180 | }; 181 | 182 | void RadioButtons() { 183 | int ageChoice; 184 | char ageText[10]; 185 | /* Approach #1: check for one of several valid responses. 186 | Good if there are a short list of possible button values and 187 | you wish to enumerate them. */ 188 | cgiFormRadio("age", ages, 4, &ageChoice, 0); 189 | 190 | fprintf(cgiOut, "Age of Truck: %s (method #1)
\n", 191 | ages[ageChoice]); 192 | 193 | /* Approach #2: just get the string. Good 194 | if the information is not critical or if you wish 195 | to verify it in some other way. Note that if 196 | the information is numeric, cgiFormInteger, 197 | cgiFormDouble, and related functions may be 198 | used instead of cgiFormString. */ 199 | cgiFormString("age", ageText, 10); 200 | 201 | fprintf(cgiOut, "Age of Truck: %s (method #2)
\n", ageText); 202 | } 203 | 204 | char *votes[] = { 205 | "A", 206 | "B", 207 | "C", 208 | "D" 209 | }; 210 | 211 | void NonExButtons() { 212 | int voteChoices[4]; 213 | int i; 214 | int result; 215 | int invalid; 216 | 217 | char **responses; 218 | 219 | /* Method #1: check for valid votes. This is a good idea, 220 | since votes for nonexistent candidates should probably 221 | be discounted... */ 222 | fprintf(cgiOut, "Votes (method 1):
\n"); 223 | result = cgiFormCheckboxMultiple("vote", votes, 4, 224 | voteChoices, &invalid); 225 | if (result == cgiFormNotFound) { 226 | fprintf(cgiOut, "I hate them all!

\n"); 227 | } else { 228 | fprintf(cgiOut, "My preferred candidates are:\n"); 229 | fprintf(cgiOut, "

    \n"); 230 | for (i=0; (i < 4); i++) { 231 | if (voteChoices[i]) { 232 | fprintf(cgiOut, "
  • %s\n", votes[i]); 233 | } 234 | } 235 | fprintf(cgiOut, "
\n"); 236 | } 237 | 238 | /* Method #2: get all the names voted for and trust them. 239 | This is good if the form will change more often 240 | than the code and invented responses are not a danger 241 | or can be checked in some other way. */ 242 | fprintf(cgiOut, "Votes (method 2):
\n"); 243 | result = cgiFormStringMultiple("vote", &responses); 244 | if (result == cgiFormNotFound) { 245 | fprintf(cgiOut, "I hate them all!

\n"); 246 | } else { 247 | int i = 0; 248 | fprintf(cgiOut, "My preferred candidates are:\n"); 249 | fprintf(cgiOut, "

    \n"); 250 | while (responses[i]) { 251 | fprintf(cgiOut, "
  • %s\n", responses[i]); 252 | i++; 253 | } 254 | fprintf(cgiOut, "
\n"); 255 | } 256 | /* We must be sure to free the string array or a memory 257 | leak will occur. Simply calling free() would free 258 | the array but not the individual strings. The 259 | function cgiStringArrayFree() does the job completely. */ 260 | cgiStringArrayFree(responses); 261 | } 262 | 263 | void Entries() 264 | { 265 | char **array, **arrayStep; 266 | fprintf(cgiOut, "List of All Submitted Form Field Names:

\n"); 267 | if (cgiFormEntries(&array) != cgiFormSuccess) { 268 | return; 269 | } 270 | arrayStep = array; 271 | fprintf(cgiOut, "

    \n"); 272 | while (*arrayStep) { 273 | fprintf(cgiOut, "
  • "); 274 | cgiHtmlEscape(*arrayStep); 275 | fprintf(cgiOut, "\n"); 276 | arrayStep++; 277 | } 278 | fprintf(cgiOut, "
\n"); 279 | cgiStringArrayFree(array); 280 | } 281 | 282 | void Cookies() 283 | { 284 | char **array, **arrayStep; 285 | char cname[1024], cvalue[1024]; 286 | fprintf(cgiOut, "Cookies Submitted On This Call, With Values (Many Browsers NEVER Submit Cookies):

\n"); 287 | if (cgiCookies(&array) != cgiFormSuccess) { 288 | return; 289 | } 290 | arrayStep = array; 291 | fprintf(cgiOut, "\n"); 292 | fprintf(cgiOut, "\n"); 293 | while (*arrayStep) { 294 | char value[1024]; 295 | fprintf(cgiOut, ""); 296 | fprintf(cgiOut, "
CookieValue
"); 297 | cgiHtmlEscape(*arrayStep); 298 | fprintf(cgiOut, ""); 299 | cgiCookieString(*arrayStep, value, sizeof(value)); 300 | cgiHtmlEscape(value); 301 | fprintf(cgiOut, "\n"); 302 | arrayStep++; 303 | } 304 | fprintf(cgiOut, "
\n"); 305 | cgiFormString("cname", cname, sizeof(cname)); 306 | cgiFormString("cvalue", cvalue, sizeof(cvalue)); 307 | if (strlen(cname)) { 308 | fprintf(cgiOut, "New Cookie Set On This Call:

\n"); 309 | fprintf(cgiOut, "Name: "); 310 | cgiHtmlEscape(cname); 311 | fprintf(cgiOut, "Value: "); 312 | cgiHtmlEscape(cvalue); 313 | fprintf(cgiOut, "

\n"); 314 | fprintf(cgiOut, "If your browser accepts cookies (many do not), this new cookie should appear in the above list the next time the form is submitted.

\n"); 315 | } 316 | cgiStringArrayFree(array); 317 | } 318 | 319 | void File() 320 | { 321 | cgiFilePtr file; 322 | char name[1024]; 323 | char contentType[1024]; 324 | char buffer[1024]; 325 | int size; 326 | int got; 327 | if (cgiFormFileName("file", name, sizeof(name)) != cgiFormSuccess) { 328 | printf("

No file was uploaded.

\n"); 329 | return; 330 | } 331 | fprintf(cgiOut, "The filename submitted was: "); 332 | cgiHtmlEscape(name); 333 | fprintf(cgiOut, "

\n"); 334 | cgiFormFileSize("file", &size); 335 | fprintf(cgiOut, "The file size was: %d bytes

\n", size); 336 | cgiFormFileContentType("file", contentType, sizeof(contentType)); 337 | fprintf(cgiOut, "The alleged content type of the file was: "); 338 | cgiHtmlEscape(contentType); 339 | fprintf(cgiOut, "

\n"); 340 | fprintf(cgiOut, "Of course, this is only the claim the browser made when uploading the file. Much like the filename, it cannot be trusted.

\n"); 341 | fprintf(cgiOut, "The file's contents are shown here:

\n"); 342 | if (cgiFormFileOpen("file", &file) != cgiFormSuccess) { 343 | fprintf(cgiOut, "Could not open the file.

\n"); 344 | return; 345 | } 346 | fprintf(cgiOut, "

\n");
347 | 	while (cgiFormFileRead(file, buffer, sizeof(buffer), &got) ==
348 | 		cgiFormSuccess)
349 | 	{
350 | 		cgiHtmlEscapeData(buffer, got);
351 | 	}
352 | 	fprintf(cgiOut, "
\n"); 353 | cgiFormFileClose(file); 354 | } 355 | 356 | void ShowForm() 357 | { 358 | fprintf(cgiOut, ""); 359 | fprintf(cgiOut, "
\n"); 363 | fprintf(cgiOut, "

\n"); 364 | fprintf(cgiOut, "Text Field containing Plaintext\n"); 365 | fprintf(cgiOut, "

\n"); 366 | fprintf(cgiOut, "Your Name\n"); 367 | fprintf(cgiOut, "

\n"); 368 | fprintf(cgiOut, "Multiple-Line Text Field\n"); 369 | fprintf(cgiOut, "

\n"); 370 | fprintf(cgiOut, "\n"); 373 | fprintf(cgiOut, "

\n"); 374 | fprintf(cgiOut, "Checkbox\n"); 375 | fprintf(cgiOut, "

\n"); 376 | fprintf(cgiOut, "Hungry\n"); 377 | fprintf(cgiOut, "

\n"); 378 | fprintf(cgiOut, "Text Field containing a Numeric Value\n"); 379 | fprintf(cgiOut, "

\n"); 380 | fprintf(cgiOut, "\n"); 381 | fprintf(cgiOut, "Blood Temperature (80.0-120.0)\n"); 382 | fprintf(cgiOut, "

\n"); 383 | fprintf(cgiOut, "Text Field containing an Integer Value\n"); 384 | fprintf(cgiOut, "

\n"); 385 | fprintf(cgiOut, "\n"); 386 | fprintf(cgiOut, "Frogs Eaten\n"); 387 | fprintf(cgiOut, "

\n"); 388 | fprintf(cgiOut, "Single-SELECT\n"); 389 | fprintf(cgiOut, "
\n"); 390 | fprintf(cgiOut, "\n"); 395 | fprintf(cgiOut, "
\n"); 396 | fprintf(cgiOut, "Multiple-SELECT\n"); 397 | fprintf(cgiOut, "
\n"); 398 | fprintf(cgiOut, "\n"); 403 | fprintf(cgiOut, "

Exclusive Radio Button Group: Age of Truck in Years\n"); 404 | fprintf(cgiOut, "1\n"); 405 | fprintf(cgiOut, "2\n"); 406 | fprintf(cgiOut, "3\n"); 407 | fprintf(cgiOut, "4\n"); 408 | fprintf(cgiOut, "

Nonexclusive Checkbox Group: Voting for Zero through Four Candidates\n"); 409 | fprintf(cgiOut, "A\n"); 410 | fprintf(cgiOut, "B\n"); 411 | fprintf(cgiOut, "C\n"); 412 | fprintf(cgiOut, "D\n"); 413 | fprintf(cgiOut, "

File Upload:\n"); 414 | fprintf(cgiOut, " (Select A Local File)\n"); 415 | fprintf(cgiOut, "

\n"); 416 | fprintf(cgiOut, "

Set a Cookie

\n"); 417 | fprintf(cgiOut, " Cookie Name\n"); 418 | fprintf(cgiOut, " Cookie Value

\n"); 419 | fprintf(cgiOut, "\n"); 420 | fprintf(cgiOut, "\n"); 421 | fprintf(cgiOut, "

Save the CGI Environment

\n"); 422 | fprintf(cgiOut, "Pressing this button will submit the form, then save the CGI environment so that it can be replayed later by calling cgiReadEnvironment (in a debugger, for instance).

\n"); 423 | fprintf(cgiOut, "\n"); 424 | fprintf(cgiOut, "

\n"); 425 | } 426 | 427 | void CookieSet() 428 | { 429 | char cname[1024]; 430 | char cvalue[1024]; 431 | /* Must set cookies BEFORE calling cgiHeaderContentType */ 432 | cgiFormString("cname", cname, sizeof(cname)); 433 | cgiFormString("cvalue", cvalue, sizeof(cvalue)); 434 | if (strlen(cname)) { 435 | /* Cookie lives for one day (or until browser chooses 436 | to get rid of it, which may be immediately), 437 | and applies only to this script on this site. */ 438 | cgiHeaderCookieSetString(cname, cvalue, 439 | 86400, cgiScriptName, SERVER_NAME); 440 | } 441 | } 442 | 443 | void LoadEnvironment() 444 | { 445 | if (cgiReadEnvironment(SAVED_ENVIRONMENT) != 446 | cgiEnvironmentSuccess) 447 | { 448 | cgiHeaderContentType("text/html"); 449 | fprintf(cgiOut, "Error\n"); 450 | fprintf(cgiOut, "

Error

\n"); 451 | fprintf(cgiOut, "cgiReadEnvironment failed. Most " 452 | "likely you have not saved an environment " 453 | "yet.\n"); 454 | exit(0); 455 | } 456 | /* OK, return now and show the results of the saved environment */ 457 | } 458 | 459 | void SaveEnvironment() 460 | { 461 | if (cgiWriteEnvironment(SAVED_ENVIRONMENT) != 462 | cgiEnvironmentSuccess) 463 | { 464 | fprintf(cgiOut, "

cgiWriteEnvironment failed. Most " 465 | "likely %s is not a valid path or is not " 466 | "writable by the user that the CGI program " 467 | "is running as.

\n", SAVED_ENVIRONMENT); 468 | } else { 469 | fprintf(cgiOut, "

Environment saved. Click this button " 470 | "to restore it, playing back exactly the same " 471 | "scenario: " 472 | "

" 475 | "

\n"); 478 | } 479 | } 480 | 481 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | cgic is now distributed under the MIT license: 2 | 3 | Copyright (c) 1996-2019 Thomas Boutell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | 11 | --------------------------------------------------------------------------------