/* cgi.c - CGI Utilities * * * Copyright (C) 1999 Chad Dixon * Macmillan Computer Publishing * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software * Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will * be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General * Public License along with this program; if not, write * to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. * * Chad Dixon may be contacted by email at: * http://www.loopy.org * */ #include #include #include #include #include "cgi.h" #include "server.h" static char *dup_string (const char *s) { char *dup = NULL; if (s != NULL) { dup = malloc(strlen(s) + 1); if (dup != NULL) { strcpy(dup, s); } } return dup; } /* read_cgi_data() * * This function assumes your data is coming * either from GET or POST, and returns a * pointer to a safe copy of that data. * You are responsible for freeing the * memory pointed to by its return value, * if the function succeeded. * * error return: * NULL * *error loaded with one value from * CGI_SUCCESS * CGI_NULL_REQ_METHOD * CGI_UNKNOWN_METHOD * CGI_NO_QUERY_STRING * CGI_NO_MEMORY * CGI_BAD_CONTENT_LENGTH * CGI_NO_DATA * all of which are defined in cgi.h * */ char *read_cgi_data (int *error, char **bound) { char *Buffer = NULL; char *RequestMethod = NULL; char *ContentLength = NULL; char *ContentType = NULL; char *Boundary = NULL; char *CGIData = NULL; size_t Size = 0; *error = CGI_SUCCESS; *bound = NULL; RequestMethod = getenv("REQUEST_METHOD"); if (NULL == RequestMethod) { *error = CGI_NULL_REQ_METHOD; } if (!*error) { if (strcmp(RequestMethod, "GET") == 0) { CGIData = getenv("QUERY_STRING"); if (NULL == CGIData) { *error = CGI_NO_QUERY_STRING; } else { Buffer = dup_string(CGIData); if (NULL == Buffer) { *error = CGI_NO_MEMORY; } } } else if (strcmp(RequestMethod, "POST") == 0) { ContentLength = getenv("CONTENT_LENGTH"); ContentType = getenv("CONTENT_TYPE"); if (NULL == ContentLength) { *error = CGI_BAD_CONTENT_LENGTH; } if (!*error) { Size = (size_t) atoi(ContentLength); if (Size <= 0) { *error = CGI_BAD_CONTENT_LENGTH; } } if (!*error) { Buffer = malloc(Size + 1); if (NULL == Buffer) { *error = CGI_NO_MEMORY; } else { Buffer[Size] = 0; if (Size != fread(Buffer, 1, Size, stdin)) { *error = CGI_NO_DATA; free(Buffer); Buffer = NULL; } } } if (!*error && ContentType != NULL) { const char *p = strstr(ContentType, "boundary="); if (p != NULL) { p += 9; Boundary = malloc(strlen(p) + 3); if (NULL == Boundary) { *error = CGI_NO_MEMORY; } else { strcpy(Boundary, "--"); strcat(Boundary, p); } } } } else { *error = CGI_UNKNOWN_METHOD; } } #if SDEBUG > 1 fprintf(fdb, "RequestMethod='%s', ContentLength='%s', err = %d\n", RequestMethod, ContentLength, *error); if (Buffer != NULL) { fprintf(fdb, "Buffer = '%.31s...'\n", Buffer); } if (Boundary != NULL) { fprintf(fdb, "Boundary = '%s'\n", Boundary); } fflush(fdb); #endif *bound = Boundary; return Buffer; } /* cgi_hex_to_ascii() * * Converts CGI %xx values to their ASCII equivalents. * */ int cgi_hex_to_ascii (char *s) { int error = 0; static const char *Hex = "0123456789ABCDEF"; unsigned int Ascii = 0; char *p; char *Match; for (p = s; !error && *s != '\0'; s++) { if (*s == '%') { s++; if ((Match = strchr(Hex, *s)) != NULL) { Ascii = (unsigned int) (Match - Hex); s++; if ((Match = strchr(Hex, *s)) != NULL) { Ascii <<= 4; Ascii |= (unsigned int) (Match - Hex); *p++ = (char) Ascii; } else { error = 1; } } else { error = 1; } } else if (*s == '+') { *p++ = ' '; } else { *p++ = *s; } } if (!error) { *p = '\0'; } return error; } /* cgi_destroy_list() * * Destroys a list built by cgi_create_list. * */ void cgi_destroy_list (CGI_LIST *List) { CGI_LIST *next = NULL; while (List != NULL) { next = List->next; if (List->name != NULL) { free(List->name); } /* Note: we don't free val because * it points into the "name" area. */ free(List); List = next; } } /* cgi_create_list() * * Creates a list of CGI variable/value pairs. * */ CGI_LIST *cgi_create_list (char *Data) { CGI_LIST *CGIList; CGI_LIST *curr_item = NULL; int error = 0; char *field; CGIList = malloc(sizeof *CGIList); if (CGIList == NULL) return NULL; curr_item = CGIList; /* Get the first field. Because strtok modifies its * input, we can't use the query string directly in * case it was stored in the environment variable * (GET method) which is why we made a copy earlier. */ field = strtok(Data, "&\n"); if (NULL == field) { error = 1; /* No data! */ } /* For each field */ while (0 == error && field != NULL) { /* Convert encoded characters (eg %21 to '!') */ if (cgi_hex_to_ascii(field) != 0) { error = 1; } else { curr_item->val = NULL; curr_item->next = NULL; /* Copy the field data for safe keeping */ curr_item->name = dup_string(field); if (NULL == curr_item->name) { error = 2; } else { /* Start at name */ curr_item->val = curr_item->name; /* Keep going unti we hit '=' */ while (*curr_item->val != '\0' && *curr_item->val != '=') { ++curr_item->val; } if ('\0' == *curr_item->val) { error = 3; } else { /* Zero-terminate value */ *curr_item->val = '\0'; /* Point to first byte of value */ ++curr_item->val; } } } if (0 == error) { /* Get next token */ field = strtok(NULL, "&\n"); if (field != NULL) { /* allocate memory for next node */ curr_item->next = malloc(sizeof *curr_item->next); if (NULL == curr_item->next) { error = 1; } else { curr_item = curr_item->next; } } } } /* If anything went wrong, bin the whole list */ if (error != 0) { cgi_destroy_list(CGIList); CGIList = NULL; } return CGIList; } void header_check (void) { static int header_printed; if (!header_printed) { puts("Content-type: text/html\n"); header_printed = 1; } } void print_cgi_error (int error) { switch (error) { case CGI_NULL_REQ_METHOD: puts("No CGI request method could be identified."); break; case CGI_UNKNOWN_METHOD: puts("Unsupported CGI request method."); break; case CGI_NO_QUERY_STRING: puts("No CGI query string found."); break; case CGI_NO_MEMORY: puts("Memory allocation failure."); break; case CGI_BAD_CONTENT_LENGTH: puts("Missing or invalid CONTENT_LENGTH."); break; case CGI_NO_DATA: puts("No CGI input data could be found."); break; default: puts("Unknown CGI Error."); break; } }