/* LibCGI - A library to make CGI programs using C Copyright (C) 2001 Rafael Steil This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA You can contact the author by e-mail: rafael@insanecorp.com */ #include #include #include #include #include "cgi.h" #include "error.h" // Whow... if hextable array has a length less than 256, // the cgi_unescape_special_chars function will fail. And I don't know why static int hextable[256]; int headers_initialized = 0; formvars *formvars_start = NULL; formvars *formvars_last = NULL; // session.c extern formvars *sess_list_start; // Set to 1 to activate runtime debugation, 0 to disable it int cgi_display_errors = 1; // cookie.c extern formvars *cookie_start; extern formvars *cookie_end; // Separates *query in name=value pairs, then gets each piece of result of them, storing // the result in the linked list global variable formvars *process_data(char *query, formvars **start, formvars **last, const char delim, const char sep) { register size_t position = 0, total_len = 0, i = 0; char *aux; formvars *data; if (query == NULL) return *start; total_len = strlen(query); aux = query; while (*query) { position = 0; data = (formvars *)malloc(sizeof(formvars)); if (!data) libcgi_error(E_MEMORY, "%s, line %s", __FILE__, __LINE__); memset(data, 0, sizeof(formvars)); // Scans the string for the next 'delim' character while (*aux && (*aux != delim)) { position++; aux++; i++; } if (*aux) { aux++; i++; } data->name = (char *)malloc(position+1); if (data->name == NULL) libcgi_error(E_MEMORY, "%s, line %s", __FILE__, __LINE__); strncpy(data->name, query, position); data->name[position] = '\0'; query = aux; position = 0; while (*aux && (*aux != sep)) { if ((*aux == '%') && (i + 2 <= total_len)) { if (isalnum(aux[1]) && isalnum(aux[2])) { aux += 2; i += 2; position++; } } else position++; aux++; i++; } if (*aux) { aux++; i++; } data->value = (char *)malloc(position+1); if (data->value == NULL) libcgi_error(E_MEMORY, "%s, line %s", __FILE__, __LINE__); strncpy(data->value, cgi_unescape_special_chars(query), position); data->value[position] = '\0'; slist_add(data, start, last); query = aux; } return *start; } /***************************************************** CGI GROUP *****************************************************/ /** @defgroup libcgi_cgi CGI manipulation * @{ */ /** * Process HTML form or URL data. * Used to retrieve GET or POST data. It handles automaticaly the correct REQUEST_METHOD, so you don't need to afraid about it. * @return Returns the contents of URL or FORM into a formvars variable, or NULL if FALSE. Most of time, you * don't need any variable to store the form data, because is used an internal variable to manipulate the contents. * @see cgi_init, cgi_init_headers **/ formvars *cgi_process_form() { char *method; method = getenv("REQUEST_METHOD"); // When METHOD has no contents, the default action is to process it as GET method if (method == NULL || !strcasecmp("GET", method)) { char *dados; dados = getenv("QUERY_STRING"); // Sometimes, GET comes with not any data if (dados == NULL || strlen(dados) < 1) return NULL; return process_data(dados, &formvars_start, &formvars_last, '=', '&'); } else if (!strcasecmp("POST", method)) { char *post_data; char *tmp_data; int content_length; tmp_data = getenv("CONTENT_LENGTH"); if (tmp_data == NULL) return NULL; content_length = atoi(tmp_data); post_data = (char *)malloc(content_length + 1); if (post_data == NULL) libcgi_error(E_MEMORY, "%s, line %s", __FILE__, __LINE__); fread(post_data, content_length, 1, stdin); post_data[content_length] = '\0'; return process_data(post_data, &formvars_start, &formvars_last, '=', '&'); } return NULL; } /** * Kills the application with a message. * Writes msg and terminate * @param msg Message to send to the browser before killing */ void cgi_fatal(const char *msg) { cgi_init_headers(); puts(msg); cgi_end(); exit(1); } /** * Include static files. * Function used to include static data ( normaly html files ). * File contents will never be processed. * Note that I don't scan for any special character. The reason * I did it is, if the you are using this library, you have a shell * where you can compile the cgi program. And can do much more ;-) * @param filename Filename with full path to include * @return If an error occurs and libcgi_debug is true, then a warning * message is showed. * @see libcgi_debug * * \code * cgi_include("top_bar.htm"); * \endcode */ int cgi_include(const char *filename) { FILE *inc; char buffer[255]; if (!(inc = fopen(filename, "r"))) { cgi_init_headers(); libcgi_error(E_WARNING, "Failed to open include file %s", filename); return 0; } while (fgets(buffer, 255, inc)) printf("%s", buffer); fclose(inc); return 1; } /** * Initialize HTML headers. * You need to call this function before that any content is send to the brosert, otherwise you'll get an error (Error 500). * @see cgi_init **/ void cgi_init_headers() { if (!headers_initialized) { printf("Content-type: text/html\r\n\r\n"); headers_initialized = 1; } } /** * Return all values with the same name sent by a form. * @param name Form variable name * @return Form variable contents * @see cgi_param * * Example: * For example, if in your HTML you have something like
*
*
* "What do you like??"
* Computers : <input type='checkbox' name='like' value='computers'><br> * Internet : <input type='checkbox' name='like' value='net'><br> * games : <input type='checkbox' name='like' 'value='games''><br> *
*
* then, to retrieve all values, you can make a code like

* * \code * // ... * char *data; * \\ ... * while ((data = cgi_param_multiple("like")) != NULL) * puts(data); * \\ ... * \endcode **/ char *cgi_param_multiple(const char *name) { static unsigned int counter = 0; unsigned int pos = 0; formvars *begin; begin = formvars_start; while (begin) { if ((!strcasecmp(begin->name, name)) && (++pos > counter)) { counter++; return (begin->value); } begin = begin->next; } counter = 0; return NULL; } /** * Recirects to the specified url. * Remember that you cannot send any header before this function, or it will not work. * Note:
* LibCGI does not implement RFC 2396 to make the lib simple and quick. You should be sure * to pass a correct URI to this function. * @param url url to redirect the browser * * \code * cgi_redirect("http://wwww.linux.org"); * \endcode **/ void cgi_redirect(char *url) { if (headers_initialized) { libcgi_error(E_WARNING, "
Cannot redirect. Headers already sent
"); return; } printf("Location: %s\r\n\r\n", url); } // Original idea from cgic library void init_hex_table() { memset(hextable, 0, 255); hextable['1'] = 1; hextable['2'] = 2; hextable['3'] = 3; hextable['4'] = 4; hextable['5'] = 5; hextable['6'] = 6; hextable['7'] = 7; hextable['8'] = 8; hextable['9'] = 9; hextable['a'] = 10; hextable['b'] = 11; hextable['c'] = 12; hextable['d'] = 13; hextable['e'] = 13; hextable['f'] = 15; hextable['A'] = 10; hextable['B'] = 11; hextable['C'] = 12; hextable['D'] = 13; hextable['E'] = 14; hextable['F'] = 15; } /** * Main cgi function. * Configures all (most?) we need to get cgi library working correctly. It MUST be called before * any other cgi function. * @see cgi_end, cgi_process_form, cgi_init_headers **/ int cgi_init() { init_hex_table(); // Well... the reason I put cgi_get_cookies() here is to not // cause problems with session's. Note that, when you want // to use session within your program, you need cgi_get_cookies() // before session_start(), otherwise we will get some problems... :) // Calling this function here is the best way. Trust me :) cgi_get_cookies(); return 1; } /** * Performs cgi clean ups. * Provides some methods to clean memory or any other job that need to be done before the end of the application. * @see cgi_init **/ void cgi_end() { slist_free(&formvars_start); formvars_last = NULL; if (sess_list_start) slist_free(&sess_list_start); if (cookies_start) slist_free(&cookies_start); } /** * Transforms' URL special chars. * Search for special chars ( like %%E1 ) in str, converting them to the ascii character correspondent. * @param str String containing data to parse * @return The new string * @see cgi_escape_special_chars **/ char *cgi_unescape_special_chars(char *str) { char *tmp; register int i, len, pos = 0; len = strlen(str); tmp = (char *)malloc(len + 1); if (tmp == NULL) libcgi_error(E_MEMORY, "%s, line %s", __FILE__, __LINE__); for (i = 0; i < len; i++) { // If we found a '%' character, then the next two are the character // hexa code. Converting a hexadecimal code to their decimal is easy: // The first character needs to be multiplied by 16 ( << 4 ), and the another // one we just get the value from hextable variable if ((str[i] == '%') && isalnum(str[i+1]) && isalnum(str[i+2])) { tmp[pos] = (hextable[(unsigned char) str[i+1]] << 4) + hextable[(unsigned char) str[i+2]]; i += 2; } else if (str[i] == '+') tmp[pos] = ' '; else tmp[pos] = str[i]; pos++; } tmp[pos] = '\0'; return tmp; } /** * Transforms' special characters into hexadecimal form ( %%E1 ). * @param str String to parse * @return The new string * @see cgi_unescape_special_chars **/ char *cgi_escape_special_chars(char *str) { unsigned char hex[] = "0123456789ABCDEF"; register int i, j, len, tmp_len; unsigned char *tmp; len = strlen(str); tmp_len = len; tmp = (unsigned char*)malloc(len+1); if (tmp == NULL) libcgi_error(E_MEMORY, "%s, line %s", __FILE__, __LINE__); for (i = 0, j = 0; i < len; i++, j++) { tmp[j] = (unsigned char)str[i]; if (tmp[j] == ' ') tmp[j] = '+'; else if (!isalnum(tmp[j]) && strchr("_-.", tmp[j]) == NULL) { tmp_len += 3; tmp = realloc(tmp, tmp_len); if (!tmp) libcgi_error(E_MEMORY, "%s, line %s", __FILE__, __LINE__); tmp[j++] = '%'; tmp[j++] = hex[(unsigned char)str[i] >> 4]; tmp[j] = hex[(unsigned char)str[i] & 0x0F]; } } tmp[j] = '\0'; return tmp; } /** * Gets the of HTML or URL variable indicated by 'name' * @param name Form Variable name * @see cgi_param_multiple, cgi_process_form, cgi_init * * \code * // ... * char *contents; * * cgi_init(); * cgi_process_form(); * cgi_init_headers(); * * contents = cgi_param("foo"); * * puts(contents); * * // ... * \endcode **/ char *cgi_param(const char *var_name) { return slist_item(var_name, formvars_start); } /** * Sends a specific header. * Sends a specific HTTP header. You won't need to add '\\n\\n' chars. * @param header HTTP header to send, without new line characteres * @return True * @see cgi_init_headers **/ void cgi_send_header(const char *header) { printf("%s\r\n", header); } /** * @} */