#include #include #include "table.h" /************************************************ * This program handles an Html form using cgi. * * * * The form should have fields called * * LASTNAME and PASSWORD, and should be * * submitted to this program using method POST. * * * * This program looks up the last name provided * * with the form in file 'grades'. That file * * should consist of lines of the following * * form * * * * name password info * * name password info * * ... * * * * where each line gives a person's last name, * * some white space, that person's password, * * some white space, and the information that * * will be provided to that person. * * * * If the name is found and the password is * * correct, then the information is displayed * * in a web page as a response. Otherwise, an * * error page is displayed. * ************************************************/ /************************************************ * NOTE * * * * This program is incomplete. You must write * * the main program, using the tools provided. * * * * The tools are in two parts. The first group * * of tools are basic functions that you should * * not use directly. The second group are * * functions that will be needed to implement * * main. The line between the two groups * * is labeled. * * * * Please delete this note before handing in, * * since it will not be relevant to the * * finished program. * ************************************************/ /************************************************ * BEGIN GROUP 1 * ************************************************/ /************************************************ * MAX_FORM_NAME_LENGTH * * MAX_FORM_VAL_LENGTH * * MAX_GRADE_LINE_LENGTH * ************************************************ * MAX_FORM_NAME_LENGTH is the maximum length * * of a name of an input field in the form. * * The input field names are given in the html * * document. * * * * MAX_FORM_VAL_LENGTH is the maximum length * * of a value of an input field in the form. * * The field values are input by the user. * * * * MAX_GRADE_LINE_LENGTH is the maximum length * * of a line in the grade file. * ************************************************/ #define MAX_FORM_NAME_LENGTH 50 #define MAX_FORM_VAL_LENGTH 50 #define MAX_GRADE_LINE_LENGTH 100 /************************************************ * hex_char * ************************************************ * digs is an array of two hexadecimal digits. * * Return the number that they represent. For * * example, "2A" represents 2*16 + 10 = 42. * ************************************************/ int hex_char(const char *digs) { int result; char digcpy[3]; digcpy[0] = digs[0]; digcpy[1] = digs[1]; digcpy[2] = '\0'; sscanf(digcpy, "%x", &result); return result; } /************************************************ * unmangle * ************************************************ * This function undoes transformations that a * * web browser does to a string. The * * transformations that are undone are as * * follows. * * * * 1. Each space is replaced by as + * * * * 2. A special character is replaced by %XX, * * where XX is the hexadecimal code for the * * character. For example, ':' is replaced * * by %3A. * * * * To undo those transformation, this function * * replaces each + by a space and each %XX by * * the character whose code is given. * * * * This function does the transformation on * * string src, and copies the result string * * into array result. Both are null-terminated.* * Array result must be allocated by the caller.* ************************************************/ void unmangle(const char *src, char *result) { char *res_p; /* Pointer into result */ const char *src_p; /* Pointer into src */ res_p = result; src_p = src; while(*src_p != '\0') { /*--------------------* * Handle + => space. * *--------------------*/ if(*src_p == '+') { *(res_p++) = ' '; src_p++; } /*-------------* * Handle %XX. * *-------------*/ else if(*src_p == '%') { *(res_p++) = hex_char(src_p + 1); src_p += 3; } /*-----------------------------------* * Default: just copy the character. * *-----------------------------------*/ else *(res_p++) = *(src_p++); } /*----------------------------* * Null-terminate the result. * *----------------------------*/ *res_p = '\0'; } /************************************************ * get_string_upto * ************************************************ * Read a string from the standard input up to * * and including the first occurrence of * * character stop. * * * * Put the string that is read into array * * word, as a null-terminated string. The stop * * character is not put into the string. * * * * For example, if the standard input contains * * Mouse=... and stop = '=', then all of the * * characters in Mouse= are consumed from * * stdin, and word is set to hold "Mouse". * * * * If the stop character is not found before a * * white-space character or end-of-file is * * encountered then the input up to the white- * * space or end-of-file is put into word. * * * * If the stop character was seen, 1 is * * returned. Otherwise, 0 is returned. * ************************************************/ bool get_string_upto(char *word, char stop) { int i, c; i = 0; c = getchar(); while(c != EOF && c != stop && !isspace(c)) { word[i++] = c; c = getchar(); } word[i] = '\0'; return c == stop; } /************************************************ * get_name_and_rest * ************************************************ * Array line has the following form. * * * * (1) possible leading white space, * * (2) a name, * * (3) a white space character, * * (4) possibly more white space, * * (5) the rest of the line. * * * * Set name to point to the name (part 2) and * * rest to point to the rest of the line (part * * 5). * * * * The space just after the name is replaced in * * the line array by a null character, * * terminating the name. * * * * In the event where there is no white space * * after the name, rest is set to point to the * * null character at the end of the string. * * * * For example, if the line contains * * * * " parrot is a bird" * * * * then the space just after parrot is * * replaced by a null character, name is set * * to point to the p in "parrot" and rest is * * set to point to the i is "is". * ************************************************/ void get_name_and_rest(char *line, char *& name, char *& rest) { char *p; /*--------------------------------------------* * Skip leading white space to find the name. * *--------------------------------------------*/ p = line; while(*p != '\0' && isspace(*p)) p++; name = p; /*---------------------------* * Find the end of the name. * *---------------------------*/ while(*p != '\0' && !isspace(*p)) p++; /*--------------------------------------------* * Null-terminate the name and find the rest. * *--------------------------------------------*/ if(*p !='\0') { *p = '\0'; p++; while(*p != '\0' && isspace(*p)) p++; rest = p; } else { rest = p; } } /************************************************ * Begin group 2 * ************************************************/ /************************************************ * acquire_info * ************************************************ * A web browser sends information to this * * program in the standard input. The * * information has the form * * * * name=val&name=val&... * * * * where each name is one of the input names * * and val is what was typed for it, in * * mangled form (see unmangle, above.) * * * * This function extracts the (name, val) pairs * * from the standard input, unmangles the val * * strings, and puts the (name,val) pairs into * * hash table tbl, where name is the key. tbl * * is made empty before doing the insertions. * ************************************************/ void acquire_info(HashTableType& tbl) { /*-----------------------------------------------* * Note: Due to mangling, the form values can be * * made longer than what was typed. * * So some extra space needs to be added to the * * val array. * *-----------------------------------------------*/ char name[MAX_FORM_NAME_LENGTH + 1]; char val[3*MAX_FORM_VAL_LENGTH + 1]; char cleanval[MAX_FORM_VAL_LENGTH + 1]; hashEmpty(tbl); while(!feof(stdin)) { bool found_equal = get_string_upto(name, '='); bool found_amp = get_string_upto(val, '&'); if(found_equal) { unmangle(val, cleanval); hashInsert(tbl, name, cleanval); } if(!found_amp) break; } } /************************************************ * write_form * ************************************************ * Copy file filename to the standard output. * * * * If file filename cannot be read, nothing is * * done. * ************************************************/ void write_form(const char *filename) { int c; FILE* inf = fopen(filename, "r"); if(inf != NULL) { printf("Content-Type: text/html\r\n\r\n"); c = getc(inf); while(c != EOF) { putchar(c); c = getc(inf); } } } /************************************************ * build_form * ************************************************ * This function is similar to write_form, but * * it does not do just a direct copy. It * * performs some substitutions while copying. * * * * The output form (an html document) is in a * * file given by string filename. Copy that * * form to the standard output, but replace * * * * $G by string grade. * * $N by string name. * * * * The return value is 1 on success and 0 if * * the copy could not be done. * ************************************************/ bool build_form(const char *filename, const char *grade, const char *name) { int c; FILE* inf = fopen(filename, "r"); if(inf == NULL) return 0; printf("Content-Type: text/html\n\n"); c = getc(inf); while(c != EOF) { /*--------------------------------------------* * Look for $G or $N. * * * * If we see a $ that is not followed by G, * * then write the $ to the standard output * * and loop again with c being the character * * that follows the $, so that $$G will be * * picked up. * *--------------------------------------------*/ if(c == '$') { c = getc(inf); if(c == 'G') { printf("%s", grade); c = getc(inf); } else if(c == 'N') { printf("%s", name); c = getc(inf); } else putchar('$'); } /*-------------------------------------------------------* * The default is just to copy c to the standard output * * and get the next character. * *-------------------------------------------------------*/ else { putchar(c); c = getc(inf); } } return 1; } /************************************************ * build_grade_table * ************************************************ * Read all of the grade information from file * * filename into table tbl. The grade file has * * the following form. * * * * name password grade * * name password grade * * ... * * * * The name and password fields must not * * contain any spaces, but the grade field can * * have spaces. * * * * The table is keyed by name, and the value * * has the form "password grade", where a * * space separates the password from the grade. * * * * The return value is 1 on success and 0 on * * failure. * ************************************************/ bool build_grade_table(const char *filename, HashTableType& tbl) { char line[MAX_GRADE_LINE_LENGTH + 1]; char *name, *password_grade; FILE* inf = fopen(filename, "r"); if(inf == NULL) return 0; while(!feof(inf)) { fgets(line, MAX_GRADE_LINE_LENGTH + 1, inf); get_name_and_rest(line, name, password_grade); hashInsert(tbl, name, password_grade); } return 1; } /************************************************ * password_ok * ************************************************ * String password contains a string that is * * a proposed password. * * * * String password_info has the form "pw info" * * where pw is a string that is a correct * * password. * * * * Check whether password = pw. If so, then * * set info to point to the info string after * * the password (after skipping leading white * * space in it) and return 1. * * * * If pw and password do not match, then return * * 0, and do not set info to anything. * ************************************************/ bool password_ok(const char *password, const char *password_info, const char *& info) { const char *password_p, *pw_p; bool match; /*------------------------* * Check password vs pw. * *------------------------*/ password_p = password; pw_p = password_info; match = 1; while(*pw_p != '\0' && *password_p != '\0' && !isspace(*pw_p)) { if(*pw_p != *password_p) { match = 0; break; } pw_p++; password_p++; } /*-------------------------------------------------------------* * It is possible to read the end of pw before reaching * * the end of password, or vice versa. Correct for that here. * *-------------------------------------------------------------*/ if(*password_p != '\0' || (*pw_p != '\0' && !isspace(*pw_p))) match = 0; /*----------------------------------------------------* * If the passwords do not match, just return 0. If * * they do match, then find the info by skipping over * * white space. * *----------------------------------------------------*/ if(!match) return 0; else { while(*pw_p != '\0' && isspace(*pw_p)) pw_p++; info = pw_p; return 1; } } /************************************************ * main * ************************************************ * Get the information from the form and * * write the response form to the standard * * output. * ************************************************/ /************************************************ * WRITE THIS FUNCTION. You are provided * * with html documents: * * * * resultform.html: result from successful * * query. This should be * * written using build_form. * * * * badpassword.html: indicates password was * * wrong. This should be * * written using write_form. * * * * badname.html: indicates that name was * * not in grade file. This * * should be written using * * build_form. * * * * badinfo.html: indicates that information* * was incomplete. This * * should be written using * * write_form. * * * * Be careful to do things in the correct * * order. For example, function password_ok * * checks the password, but it cannot be run * * until you have both got the information from * * the html form holding the proposed password * * and read the file containing the actual * * passwords and other information. * * * * Note that some function put results into * * hash tables. You will need to look up * * information that you need from those tables. * * * * IMPORTANT NOTE * * * * Notice that function password_ok has a * * parameter of type * * * * const char *& * * * * This is a reference to a variable of type * * const char*. Be sure that the actual * * parameter has type const char*. * * * * DELETE THIS COMMENT WHEN YOU ARE DONE. * ************************************************/ int main() { return 0; }