/* * C CGI Library version 1.1 * * Author: Stephen C. Losen, University of Virginia * * Copyright 2009 Stephen C. Losen. Distributed under the terms * of the GNU General Public License (GPL) * * C CGI is a C language library for parsing, decoding, storing and * retrieving data passed from a web browser to a CGI program. C CGI * supports URL encoded form data (application/x-www-form-urlencoded) * and multipart (mutlipart/form-data), including file uploads. HTML * cookies can also be stored and retrieved. * * The library builds one or more "variable lists" of type * CGI_varlist. A list entry consists of a name (null terminated * string) and one or more values (also null terminated strings) * associated with the name. We allow multiple values because 1) * some HTML form elements (such as checkboxes and selections) * allow the form user to select multiple values and 2) different * form elements may be given the same name. * * The library function CGI_lookup_all() searches a variable list for * an entry with the specified name and returns the values in a * null terminated array of pointers to null terminated strings. * * Variable list entries can be obtained iteratively with * CGI_first_name(), CGI_next_name() and CGI_lookup_all(). * * For a file upload, the user provides a filename template that * is passed to mkstemp() to create a new file to hold the data. */ #include #include #include #include #include #include #include "../includes/ccgi.h" #ifdef _WIN32 #define mkstemp(name) _mktemp_s(name, sizeof(name)) #endif /* CGI_val is an entry in a list of variable values */ typedef struct CGI_val CGI_val; struct CGI_val { CGI_val *next; /* next entry on list */ const char value[1]; /* variable value */ }; /* * CGI_varlist is an entry in a list of variables. The fields * "iter" and "tail" are only used in the first list entry. */ struct CGI_varlist { CGI_varlist *next; /* next entry on list */ CGI_varlist *tail; /* last entry on list */ CGI_varlist *iter; /* list iteration pointer */ int numvalue; /* number of values */ CGI_val *value; /* linked list of values */ CGI_val *valtail; /* last value on list */ CGI_value *vector; /* array of values */ const char varname[1]; /* variable name */ }; /* strbuf is a string buffer of arbitrary size */ typedef struct { int size; char str[1]; } strbuf; /* mymalloc() is a malloc() wrapper that exits on failure */ static void * mymalloc(int size) { void *ret = malloc(size); if (ret == 0) { fputs("C CGI Library out of memory\n", stderr); exit(1); } return ret; } /* * sb_get() creates or extends a strbuf */ static strbuf * sb_get(strbuf *sb, int len) { int size; for (size = 128; size < len; size += size) ; if (sb == 0) { sb = (strbuf *) mymalloc(sizeof(*sb) + size); } else { sb = (strbuf *) realloc(sb, sizeof(*sb) + size); if (sb == 0) { fputs("C CGI Library out of memory\n", stderr); exit(1); } } sb->size = size; return sb; } /* savechar() saves a character in a strbuf at index idx */ static strbuf * savechar(strbuf *sb, int idx, int c) { if (sb == 0 || idx >= sb->size) { sb = sb_get(sb, idx + 1); } sb->str[idx] = c; return sb; } /* savestr() saves a string in a strbuf */ static strbuf * savestr(strbuf *sb, const char *str) { int len = (int)strlen(str); if (sb == 0 || len >= sb->size) { sb = sb_get(sb, len + 1); } strcpy(sb->str, str); return sb; } /* * findvar() searches variable list "v" for an entry whose name * is "varname" and returns a pointer to the entry or else null * if not found. If varname is null then we return v->iter, * which is the "current" variable entry. */ static CGI_varlist * findvar(CGI_varlist *v, const char *varname) { if (varname == 0 && v != 0) { return v->iter; } for (; v != 0; v = v->next) { if (v->varname[0] == varname[0] && strcmp(v->varname, varname) == 0) { break; } } return v; } /* * hex() returns the numeric value of hexadecimal digit "digit" * or returns -1 if "digit" is not a hexadecimal digit. */ static int hex(int digit) { switch(digit) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return digit - '0'; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return 10 + digit - 'A'; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return 10 + digit - 'a'; default: return -1; } } /* urlcount() returns the number of bytes needed to URL encode a string */ static int urlcount(const char *p, const char *keep) { int k; for (k = 0; *p != 0; p++) { if (isalnum((int32_t)*p) || *p == ' ' || (keep != 0 && strchr(keep, *p) != 0)) { k++; } else { k += 3; } } return k; } /* * urlencode() URL encodes string "in" into string "out" and * returns a pointer to the null byte at the end of "out" */ static char * urlencode(const char *in, char *out, const char *keep) { const char hexdigit[] = "0123456789ABCDEF"; for (; *in != 0; in++) { if (isalnum((int32_t)*in) || (keep != 0 && strchr(keep, *in) != 0)) { *out++ = *in; } else if (*in == ' ') { *out++ = '+'; } else { *out++ = '%'; *out++ = hexdigit[(*in >> 4) & 0xf]; *out++ = hexdigit[*in & 0xf]; } } *out = 0; return out; } /* scanspaces() scans over spaces and tabs in a string */ static char * scanspaces(char *p) { while (*p == ' ' || *p == '\t') { p++; } return p; } /* * scanattr() scans for an attribute such as name="value" or * name=value; saving the attribute name in attr[0] and saving * the attribute value in attr[1]. If successful, we return a * pointer to where the scan ended, otherwise we return null. * The input string is modified. */ static char * scanattr(char *p, char *attr[2]) { int quote = 0; attr[0] = p = scanspaces(p); while (*p != '=' && *p != 0) { p++; } if (*p != '=' || p == attr[0]) { return 0; } *p++ = 0; if (*p == '"' || *p == '\'' || *p == '`') { quote = *p++; } attr[1] = p; if (quote != 0) { while(*p != quote && *p != 0) { p++; } if (*p != quote) { return 0; } *p++ = 0; if (*p == ';') { p++; } } else { while (*p != ';' && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n' && *p != 0) { p++; } if (*p != 0) { *p++ = 0; } } return p; } /* * scanheader() scans a line of input for a header name followed by * a colon and the header value, such as: * * Content-Disposition: form-data; * * We place the header name in header[0] and the value in header[1]. * If successful we return a pointer to the character where the scan * ended, otherwise we return null. The input string is modified. */ static char * scanheader(char *p, char *header[2]) { if (isalnum((int32_t)*p) == 0) { return 0; } header[0] = p; while (*p != ':' && *p != 0) { p++; } if (*p != ':') { return 0; } *p++ = 0; header[1] = p = scanspaces(p); while (*p != ';' && *p != '\r' && *p != '\n' && *p != 0) { p++; } if (*p != 0) { *p++ = 0; } return p; } /* * readline() reads characters from open file "in" into strbuf "line". * We stop reading when we encounter '\n' or end of file. We null * terminate "line" and return it. We return null if we encounter * end of file without reading any characters. */ static strbuf * readline(strbuf *line, FILE *in) { int c, i = 0; while ((c = getc(in)) != EOF) { line = savechar(line, i++, c); if (c == '\n') { break; } } if (i == 0) { if (line != 0) { free(line); } return 0; } return savechar(line, i, 0); /* null terminate */ } /* * copyvalue() reads bytes from open file "in", which contains * "multipart/form-data", delimited by "boundary". We read * bytes until we encounter the boundary. If the flag "wantfile" * is true then we write the bytes (minus the boundary) to open * file pointer "out" (or discard the output if "out" is NULL). * Otherwise we copy the bytes to "value". */ static strbuf * copyvalue(const char *boundary, FILE *in, const int wantfile, strbuf *value, FILE *out) { int c, i, k, matched; matched = k = 0; while ((c = getc(in)) != EOF) { /* * If we partially match the boundary, then we copy the * entire matching prefix to the output. We do not need to * backtrack and look for shorter matching prefixes because * they cannot exist. The boundary always begins with '\r' * and never contains another '\r'. */ if (matched > 0 && c != boundary[matched]) { for (i = 0; i < matched; i++) { if (wantfile == 0) { value = savechar(value, k++, boundary[i]); } else if (out != 0) { fputc(boundary[i], out); } } matched = 0; } /* check for full or partial boundary match */ if (c == boundary[matched]) { if (boundary[++matched] == 0) { break; /* full match */ } continue; /* partial match */ } /* no match, so copy byte to output */ if (wantfile == 0) { value = savechar(value, k++, c); } else if (out != 0) { fputc(c, out); } } if (wantfile == 0) { return savechar(value, k, 0); } return 0; } /* * read_multipart() reads form data encoded as "multipart/form-data" * and adds form field names and values to variable list "v" and * returns "v". * * The input is split into parts delimited by a boundary string. * Each part starts with this header: * * Content-Disposition: form-data; name="fieldname"; filename="filename" * * where "fieldname" is the name of the form field. The "filename=" * attribute is only present when this part contains file data for * a file upload. A "Content-type:" header line may also be present * with a file upload. After the header lines comes a blank line * followed by the field data (or file data) terminated by "\r\n--" * followed by the boundary string. If the boundary string is * followed by "--\r\n" then this is the last part. */ static CGI_varlist * read_multipart(CGI_varlist *v, const char *template) { const char *ctype, *name, *filename; char *p, *token[2], *boundary, *localname = 0; strbuf *bbuf = 0, *nbuf = 0, *fbuf = 0; strbuf *line = 0, *value = 0; int len, fd; FILE *out; /* * get the boundary string from the environment and prepend * "\r\n--" to it. */ if ((ctype = getenv("CONTENT_TYPE")) == 0 || strncasecmp(ctype, "multipart/form-data;", len = 20) != 0) { return v; } bbuf = savestr(bbuf, ctype + len); if (scanattr(bbuf->str, token) == 0 || strcasecmp(token[0], "boundary") != 0) { goto cleanup; } boundary = token[1] - 4; memcpy(boundary, "\r\n--", 4); /* * first line is the boundary string, but with "\r\n" * at the end rather than the start. */ len = (int)strlen(boundary) - 2; if ((line = readline(line, stdin)) == 0 || strncmp(line->str, boundary + 2, len) != 0 || line->str[len] != '\r' || line->str[len + 1] != '\n') { goto cleanup; } /* read all the parts */ for (;;) { /* Scan header lines for the Content-Disposition: header */ name = filename = 0; while ((line = readline(line, stdin)) != 0 && (p = scanheader(line->str, token)) != 0) { if (strcasecmp(token[0], "Content-Disposition") != 0 || strcasecmp(token[1], "form-data") != 0) { continue; } /* Content-Disposition: has field name and file name */ while ((p = scanattr(p, token)) != 0) { if (name == 0 && strcasecmp(token[0], "name") == 0) { nbuf = savestr(nbuf, token[1]); name = nbuf->str; } else if (filename == 0 && strcasecmp(token[0], "filename") == 0) { fbuf = savestr(fbuf, token[1]); filename = fbuf->str; } } } /* after the headers is a blank line (just "\r\n") */ if (line == 0 || name == 0 || line->str[0] != '\r' || line->str[1] != '\n') { break; } /* * If filename is non null (file upload) then we read file data, * otherwise we read field data. In either case the data * consists of everything up to, but not including the boundary. */ if (filename != 0) { /* copy file data to newly created file */ out = 0; if (template != 0 && *filename != 0) { if (localname == 0) { localname = (char *) mymalloc((int)strlen(template) + 1); } strcpy(localname, template); if ((fd = mkstemp(localname)) >= 0) { out = fdopen(fd, "wb"); } } copyvalue(boundary, stdin, 1, 0, out); if (out != 0) { fclose(out); v = CGI_add_var(v, name, localname); v = CGI_add_var(v, name, filename); } } else { value = copyvalue(boundary, stdin, 0, value, 0); v = CGI_add_var(v, name, value->str); } /* * read the rest of the line after the boundary. If we * get "--\r\n" then this is the last field. Otherwise * we presumably get "\r\n" and we continue. */ if ((line = readline(line, stdin)) != 0 && line->str[0] == '-' && line->str[1] == '-' && line->str[2] == '\r' && line->str[3] == '\n') { break; } } cleanup: if (bbuf != 0) { free(bbuf); } if (nbuf != 0) { free(nbuf); } if (fbuf != 0) { free(fbuf); } if (line != 0) { free(line); } if (value != 0) { free(value); } if (localname != 0) { free(localname); } return v; } /* * EXPORTED FUNCTIONS * * CGI_decode_url() returns a new string which is a copy of the input * string with '+' converted to ' ' and %xx converted to the character * whose hex numeric value is xx. */ char * CGI_decode_url(const char *p) { char *out; int i, k, L, R; if (p == 0) { return 0; } out = (char *) mymalloc((int)strlen(p) + 1); for (i = k = 0; p[i] != 0; i++) { switch(p[i]) { case '+': out[k++] = ' '; continue; case '%': if ((L = hex(p[i + 1])) >= 0 && (R = hex(p[i + 2])) >= 0) { out[k++] = (L << 4) + R; i += 2; continue; } break; } out[k++] = p[i]; } out[k] = 0; return out; } /* * CGI_encode_url() URL encodes a string and returns the result * in memory from malloc(). */ char * CGI_encode_url(const char *p, const char *keep) { char *out; if (p == 0) { return 0; } out = mymalloc(urlcount(p, keep) + 1); urlencode(p, out, keep); return out; } /* * CGI_encode_query() takes a variable arg list of strings * and encodes them into a URL query string of the form * name1=value1&name2=value2 ... where each name and value * is URL encoded. */ char * CGI_encode_query(const char *keep, ...) { char *out, *p; va_list ap; const char *name, *value; int k; /* calculate the size of the output string */ va_start(ap, keep); k = 0; while ((value = va_arg(ap, const char *)) != 0) { k += urlcount(value, keep) + 1; } va_end(ap); if (k == 0) { return 0; } p = out = mymalloc(k); /* url encode each name=value pair */ va_start(ap, keep); while ((name = va_arg(ap, const char *)) != 0 && (value = va_arg(ap, const char *)) != 0) { if (p != out) { *p++ = '&'; } p = urlencode(name, p, keep); *p++ = '='; p = urlencode(value, p, keep); } va_end(ap); *p = 0; return out; } /* * CGI_encode_varlist() encodes a CGI_varlist into a query * string of the form name1=value1&name2=value2 ... where * each name and value is URL encoded. */ char * CGI_encode_varlist(CGI_varlist *vlist, const char *keep) { char *out, *p; CGI_varlist *v; CGI_val *value; int k = 0; /* calculate size of the output string */ for (v = vlist; v != 0; v = v->next) { for (value = v->value; value != 0; value = value->next) { k += 2 + urlcount(v->varname, keep) + urlcount(value->value, keep); } } if (k == 0) { return 0; } p = out = mymalloc(k); /* URL encode each name=value pair */ for (v = vlist; v != 0; v = v->next) { for (value = v->value; value != 0; value = value->next) { if (p != out) { *p++ = '&'; } p = urlencode(v->varname, p, keep); *p++ = '='; p = urlencode(value->value, p, keep); } } *p = 0; return out; } /* * CGI_add_var() adds a new variable name and value to variable list * "v" and returns the resulting list. If "v" is null or if the * variable name is not on the list, then we create a new entry. * We add the value to the appropriate list entry. */ CGI_varlist * CGI_add_var(CGI_varlist *v, const char *varname, const char *value) { CGI_val *val; CGI_varlist *v2; if (varname == 0 || value == 0) { return v; } /* create a new value */ val = (CGI_val *) mymalloc(sizeof(*val) + (int)strlen(value)); strcpy((char *) val->value, value); val->next = 0; /* * find the list entry or else create a new one. Add the * new value. We use "tail" pointers to keep the lists * in the same order as the input. */ if ((v2 = findvar(v, varname)) == 0) { v2 = (CGI_varlist *) mymalloc((int)sizeof(*v2) + (int)strlen(varname)); strcpy((char *) v2->varname, varname); v2->value = val; v2->numvalue = 1; v2->next = v2->iter = v2->tail = 0; v2->vector = 0; if (v == 0) { v = v2; } else { v->tail->next = v2; } v->tail = v2; } else { v2->valtail->next = val; v2->numvalue++; } v2->valtail = val; if (v2->vector != 0) { free((void *)v2->vector); v2->vector = 0; } v->iter = 0; return v; } /* * CGI_decode_query() adds all the names and values in query string * "query" to variable list "v" (which may be null) and returns the * resulting variable list. The query string has the form * * name1=value1&name2=value2&name3=value3 * * We convert '+' to ' ' and convert %xx to the character whose * hex numeric value is xx. */ CGI_varlist * CGI_decode_query(CGI_varlist *v, const char *query) { char *buf; const char *name, *value; int i, k, L, R, done; if (query == 0) { return v; } buf = (char *) mymalloc((int)strlen(query) + 1); name = value = 0; for (i = k = done = 0; done == 0; i++) { switch (query[i]) { case '=': if (name != 0) { break; /* treat extraneous '=' as data */ } if (name == 0 && k > 0) { name = buf; buf[k++] = 0; value = buf + k; } continue; case 0: done = 1; /* fall through */ case '&': buf[k] = 0; if (name == 0 && k > 0) { name = buf; value = buf + k; } if (name != 0) { v = CGI_add_var(v, name, value); } k = 0; name = value = 0; continue; case '+': buf[k++] = ' '; continue; case '%': if ((L = hex(query[i + 1])) >= 0 && (R = hex(query[i + 2])) >= 0) { buf[k++] = (L << 4) + R; i += 2; continue; } break; /* treat extraneous '%' as data */ } buf[k++] = query[i]; } free(buf); return v; } /* * CGI_get_cookie() adds all the cookie names and values from the * environment variable HTTP_COOKIE to variable list "v" (which * may be null) and returns the resulting variable list. */ CGI_varlist * CGI_get_cookie(CGI_varlist *v) { const char *env; char *buf, *p, *cookie[2]; if ((env = getenv("HTTP_COOKIE")) == 0) { return v; } buf = (char *) mymalloc((int)strlen(env) + 1); p = strcpy(buf, env); while ((p = scanattr(p, cookie)) != 0) { v = CGI_add_var(v, cookie[0], cookie[1]); } free(buf); return v; } /* * CGI_get_query() adds all the field names and values from the * environment variable QUERY_STRING to variable list "v" (which * may be null) and returns the resulting variable list. */ CGI_varlist * CGI_get_query(CGI_varlist *v) { return CGI_decode_query(v, getenv("QUERY_STRING")); } /* * CGI_get_post() reads field names and values from stdin and adds * them to variable list "v" (which may be null) and returns the * resulting variable list. We accept input encoded as * "application/x-www-form-urlencoded" or as "multipart/form-data". * In the case of a file upload, we write to a new file created * with mkstemp() and "template". If the template is null or if * mkstemp() fails then we silently discard the uploaded file data. * The local name of the file (created by mkstemp()) and the remote * name (as specified by the user) can be obtained with * CGI_lookup_all(v, fieldname). */ CGI_varlist * CGI_get_post(CGI_varlist *v, const char *template) { const char *env; char *buf; int len; if ((env = getenv("CONTENT_TYPE")) != 0 && strcasecmp(env, "application/x-www-form-urlencoded") == 0 && (env = getenv("CONTENT_LENGTH")) != 0 && (len = atoi(env)) > 0) { buf = (char *) mymalloc(len + 1); if (fread(buf, 1, len, stdin) == len) { buf[len] = 0; v = CGI_decode_query(v, buf); } free(buf); } else { v = read_multipart(v, template); } return v; } /* * CGI_get_all() returns a variable list that contains a combination of the * following: cookie names and values from HTTP_COOKIE, field names and * values from QUERY_STRING, and POSTed field names and values from stdin. * File uploads are handled using "template" (see CGI_get_post()) */ CGI_varlist * CGI_get_all(const char *template) { CGI_varlist *v = 0; v = CGI_get_cookie(v); v = CGI_get_query(v); v = CGI_get_post(v, template); return v; } /* CGI_free_varlist() frees all memory used by variable list "v" */ void CGI_free_varlist(CGI_varlist *v) { CGI_val *val, *valnext; if (v != 0) { if (v->vector != 0) { free((void *)v->vector); } for (val = v->value; val != 0; val = valnext) { valnext = val->next; free(val); } CGI_free_varlist(v->next); free(v); } } /* * CGI_lookup() searches variable list "v" for an entry named * "varname" and returns null if not found. Otherwise we return the * first value associated with "varname", which is a null terminated * string. If varname is null then we return the first value of the * "current entry", which was set using the iterating functions * CGI_first_name() and CGI_next_name(). */ const char * CGI_lookup(CGI_varlist *v, const char *varname) { return (v = findvar(v, varname)) == 0 ? 0 : v->value->value; } /* * CGI_lookup_all() searches variable list "v" for an entry named * "varname" and returns null if not found. Otherwise we return * a pointer to a null terminated array of string pointers (see * CGI_value) where each string is a value of the variable. If * varname is null then we return the values of the "current entry", * which was set using the iterating functions CGI_first_name() and * CGI_next_name(). */ CGI_value * CGI_lookup_all(CGI_varlist *v, const char *varname) { CGI_val *val; int i; if ((v = findvar(v, varname)) == 0) { return 0; } if (v->vector == 0) { v->vector = (CGI_value *) mymalloc(sizeof(CGI_value) * (v->numvalue + 1)); i = 0; /* to initialize v->vector we must cast away const */ for (val = v->value; val != 0 && i < v->numvalue; val = val->next) { ((const char **)v->vector)[i++] = val->value; } ((const char **)v->vector)[i] = 0; } return v->vector; } /* * CGI_first_name() returns the name of the first entry in * variable list "v", or null if "v" is null. */ const char * CGI_first_name(CGI_varlist *v) { return v == 0 ? 0 : (v->iter = v)->varname; } /* * CGI_next_name() returns the name of the next entry in variable list * "v" after the most recent call to CGI_first_name() or CGI_next_name(). * We return null if there are no more entries */ const char * CGI_next_name(CGI_varlist *v) { return v == 0 || v->iter == 0 || (v->iter = v->iter->next) == 0 ? 0 : v->iter->varname; } /* * CGI_encode_entity() converts null terminated string "in" to * HTML entity encoding where > become > and < become < * and & becomes & etc., and returns the result. Allocates * memory for the result with malloc(). */ char * CGI_encode_entity(const char *in) { char *out, *p; int i, k; if (in == 0) { return 0; } for (i = k = 0; in[i] != 0; i++) { switch(in[i]) { case '<': case '>': k += 4; break; case '&': case '\'': case '\r': case '\n': k += 5; break; case '"': k += 6; break; default: k++; break; } } out = p = mymalloc(k + 1); for (i = 0; in[i] != 0; i++) { switch(in[i]) { case '<': *p++ = '&'; *p++ = 'l'; *p++ = 't'; *p++ = ';'; break; case '>': *p++ = '&'; *p++ = 'g'; *p++ = 't'; *p++ = ';'; break; case '&': *p++ = '&'; *p++ = 'a'; *p++ = 'm'; *p++ = 'p'; *p++ = ';'; break; case '\'': *p++ = '&'; *p++ = '#'; *p++ = '3'; *p++ = '9'; *p++ = ';'; break; case '\r': *p++ = '&'; *p++ = '#'; *p++ = '1'; *p++ = '3'; *p++ = ';'; break; case '\n': *p++ = '&'; *p++ = '#'; *p++ = '1'; *p++ = '0'; *p++ = ';'; break; case '"': *p++ = '&'; *p++ = 'q'; *p++ = 'u'; *p++ = 'o'; *p++ = 't'; *p++ = ';'; break; default: *p++ = in[i]; break; } } *p = 0; return out; } /* base64 conversion */ static const char b64encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789+/"; #define BAD 100 static const unsigned char b64decode[] = { BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, 62, BAD, BAD, BAD, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, BAD, BAD, BAD, BAD, BAD, BAD, BAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, BAD, BAD, BAD, BAD, BAD, BAD, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD, BAD }; /* * CGI_decode_base64() decodes null terminated base64 encoded string p * and returns the result. We store the length of the result in * *len and we write a null byte after the last byte of the result. * We allocate memory for the result with malloc(); */ void * CGI_decode_base64(const char *p, int *len) { const unsigned char *in = (const unsigned char *) p; unsigned char *out; int save = 0, nbits = 0, sixbits; int i, k; if (p == 0) { return 0; } out = mymalloc(3 + 3 * (int)strlen(p) / 4); /* every four base64 input characters becomes three output bytes */ for (i = k = 0; in[i] != 0; i++) { if ((sixbits = b64decode[in[i]]) == BAD) { continue; } save |= sixbits << (18 - nbits); /* 4 x 6 bits in */ if ((nbits += 6) == 24) { out[k++] = save >> 16; /* 3 x 8 bits out */ out[k++] = save >> 8; out[k++] = save; nbits = 0; save = 0; } } /* convert leftover bits */ for (i = 16; i >= 0 && nbits >= 8; i -= 8) { out[k++] = save >> i; nbits -= 8; } out[k] = 0; if (len != 0) { *len = k; } return out; } /* * CGI_encode_base64() base64 encodes bytes in array p of length len * and returns the result, which is a null terminated base64 encoded * string. We allocate memory for the result with malloc(). */ char * CGI_encode_base64(const void *p, int len) { const unsigned char *in = p; char *out; int save = 0, nbits = 0; int i, k = 0; if (in == 0 || len <= 0) { return 0; } out = mymalloc(4 + 4 * len / 3); /* every three input bytes becomes 4 base64 output characters */ for (i = 0; i < len; i++) { save |= in[i] << (16 - nbits); /* 3 x 8 bits in */ if ((nbits += 8) == 24) { out[k++] = b64encode[(save >> 18) & 077]; /* 4 x 6 bits out */ out[k++] = b64encode[(save >> 12) & 077]; out[k++] = b64encode[(save >> 6) & 077]; out[k++] = b64encode[ save & 077]; nbits = 0; save = 0; } } /* convert leftover bits */ if (nbits > 0) { for (i = 18; i >= 0; i -= 6) { if (nbits > 0) { out[k++] = b64encode[(save >> i) & 077]; nbits -= 6; } else { out[k++] = '='; } } } out[k] = 0; return out; } /* hex conversion */ /* * CGI_decode_hex() decodes null terminated hex encoded string p * and returns the result. We store the length of the result in * *len and we write a null byte after the last byte of the result. * We allocate memory for the result with malloc(); */ void * CGI_decode_hex(const char *p, int *len) { unsigned char *out; int i, k, n, L, R; if (p == 0 || ((n = (int)strlen(p)) & 1)) { return 0; /* length of input must be even */ } out = mymalloc(n / 2 + 1); for (i = k = 0; i < n; i += 2) { if ((L = hex(p[i])) >= 0 && (R = hex(p[i + 1])) >= 0) { out[k++] = (L << 4) + R; } else { free(out); return 0; } } out[k] = 0; if (len != 0) { *len = k; } return out; } /* * CGI_encode_hex() hex encodes bytes in array p of length len * and returns the result, which is a null terminated hex encoded * string. We allocate memory for the result with malloc(). */ char * CGI_encode_hex(const void *p, int len) { const unsigned char *in = p; int i, k; char *out; const char hexdigit[] = "0123456789ABCDEF"; if (in == 0 || len <= 0) { return 0; } out = mymalloc(len * 2 + 1); for (i = k = 0; i < len; i++) { out[k++] = hexdigit[in[i] >> 4]; out[k++] = hexdigit[in[i] & 0xf]; } out[k] = 0; return out; }