/* This script program manages a registry of user entries added via a form * the registry is used in conjunction with a template file to generate * an HTML document listing all entries. The template contains an iteration * construct for repeating poritions of the template for each record, * sustituting the record data into flagged portions of the template. * * Arugments: Standard WWWEXEC args (script method xlated-url protocol). * * The path_info for the script is assumed to be the virtual path of * the template file. The template file must begin with an * escape field with the following information: * * %[$escape registry-file subfield-list html-outfile] * * where: * registry-file VMS file specification of indexed file to * hold data. If file doesn't exist, this script * will attempt to create it. * subfield-list Names of subfields within each database record. * These names must correspond with the input names * in the submitting form. The first subfield must * be named key and the second subfield must be named * name. * html-outfile VMS file specification of html output file to * generate. * * The names in the subfield list may then be used in the placeholders * within a subsequent $readfile field. * * [$readfile registry-file sub-template * ... * ] * * Within the sub-template can insert fields of a record by specifying * %[field-name]. * * The registry file created by this routine has a primary key 40 bytes * long which is the upcased form of the name field. * * Author: David Jones * Date: 29-NOV-1995 * Revised: 19-NOV-1996 Add delete hack to allow user deletes */ #include #include #include #include #include #include #include #include "template.h" #include "subaccess.h" #include "cgilib.h" #include "scriptlib.h" struct db_params { int state; /* 0-initial, 1-middle, 2-done */ int tok_inwhite; /* Flags whether looking for non-whitespace */ int tok_count; /* number of tokens parsed */ int tok_used; /* space in token buffer taken */ char *tok[10]; /* Pointers into tok_buf */ FILE *html_file; /* Temporary HTML file */ char work_name[256];/* Name of temporary file */ char tok_buf[1024]; }; static char *fdl_string = "\ FILE; ORGANIZATION indexed; PROTECTION (system:RWED,owner:RWED,group,world);\ RECORD; CARRIAGE_CONTROL carriage_return; FORMAT variable; SIZE 8192;\ KEY 0; CHANGES no; PROLOG 3; SEG0_LENGTH 12; SEG0_POSITION 0; TYPE string;\ KEY 1; CHANGES yes; PROLOG 3; SEG0_LENGTH 40; SEG0_POSITION 13; TYPE string;\ "; /* * define special list for generating form using escape mechanims. */ static char *form_names[5] = { "username", "first", "last", "url", "comment"}; static char frm_username[16], frm_first[128], frm_last[128]; static char frm_url[256], frm_comment[256]; static char *form_values[5] = { frm_username, frm_first, frm_last, frm_url, frm_comment }; static int update_user ( subfield_ctx subfield, struct db_params *arg ); static int parse_form ( int key_limit, char **key, char **value ); static int my_out ( struct db_params *arg, char *buf, int length ) { int sent, status; /* * Ignore all received data until my_escape opens the output file. */ if ( arg->state != 2 ) return 1; if ( !arg->html_file ) return 0; /* no output file */ /* */ while ( length > 0 ) { sent = fwrite ( buf, sizeof(char), length, arg->html_file ); if ( sent <= 0 ) { return 0; /* write error */ } length = length - sent; } return 1; } static int my_escape ( struct db_params *arg, char *buf, int length ) { int i, j, sent, status, subfield_count, content_length; char *var; char test_url[512]; struct subfield_context subfield; /* * collect data until we have enough to process. */ if ( arg->state == 0 ) { arg->tok_count = 0; arg->tok_used = 0; arg->tok_inwhite = 1; arg->html_file = (FILE *) 0; arg->state = 1; } else if ( arg->state == 3 ) { if ( buf[length] ) { char save_c; save_c = buf[length]; buf[length] = '\0'; status = cgi_printf("%s",buf); buf[length] = save_c; } else { status = cgi_printf("%s",buf); } if ( (status&1) == 0 ) return status; } if ( arg->state > 1 ) return 1; /* ignore rest */ for ( i = 0; i < length; i++ ) { if ( arg->tok_used >= sizeof ( arg->tok_buf ) ) break; if ( arg->tok_inwhite ) { if ( !isspace ( buf[i] ) ) { arg->tok_inwhite = 0; arg->tok[arg->tok_count] = &arg->tok_buf[arg->tok_used]; arg->tok_buf[arg->tok_used++] = buf[i]; } } else { if ( isspace ( buf[i] ) ) { arg->tok_inwhite = 1; arg->tok_buf[arg->tok_used++] = '\0'; arg->tok_count++; if ( arg->tok_count >= 3 ) { arg->state = 2; break; } } else { arg->tok_buf[arg->tok_used++] = buf[i]; } } } /* * If arg-state reaches 2 we have all we need. */ if ( arg->state == 2 ) { /* * Count number of commas in field list (tok[1]) and save * first 2 fields. */ subfield_count = (arg->tok[1][0]) ? 1 : 0; for ( i = 0; arg->tok[1][i]; i++ ) if ( arg->tok[1][i] == ',' ) { subfield_count++; } /* * Attempt to locate the registry. */ status = subfield_open ( arg->tok[0], "r+", /* filename */ 8192, /* Max records */ &subfield ); printf("Open of '%s' status: %d\n", arg->tok[0], status ); if ( (status&1) == 0 ) { IFILE *tmp; char head_rec[512]; printf("Open of '%s' status: %d\n", arg->tok[0], status ); /* * Try to create it via FDL. */ status = ifdlcreate ( fdl_string, arg->tok[0], "sys$disk:[]" ); if ((status&1) == 0) printf( "ifldcreate status: %d, list: %s\n", status, arg->tok[1] ); sprintf(head_rec,"%12s%c%40s%c%s", "", 0, "", 0, arg->tok[1] ); tmp = ifopen ( arg->tok[0], "r+" ); ifwrite_rec ( head_rec, strlen(arg->tok[1])+55, tmp ); ifclose ( tmp ); if ( (status&1) == 1 ) { status = subfield_open ( arg->tok[0], "r+", /* filename */ 8192, /* Max records */ &subfield ); } if ( (status&1) == 0 ) return status; } /* * Process the form data and add a record. */ var = cgi_info ( "CONTENT_LENGTH" ); content_length = var ? atoi(var) : 0; if ( content_length > 0 ) { /* form data present */ char *url; status = update_user ( &subfield, arg ); url = subfield_value ( &subfield, "url" ); cgi_printf("Content-type: text/html\n\n"); cgi_printf ( "\n"); cgi_printf ( "Registry file has been updated.\n" ); if ( url ) cgi_printf ( "Please verify the URL you entered is correct\n", url ); cgi_printf ( "\n" ); } else if ( frm_username[0] ) { /* * Build form using default information. */ arg->state = 3; status = subfield_read ( &subfield, frm_username, 0, strlen(frm_username) ); if ( (status&1) == 0 ) { cgi_printf("Content-type: text/html\n\n"); return 1; } if ( var=subfield_value(&subfield,"last") ) { if (strlen(var) < sizeof(frm_last)) strcpy (frm_last,var); } if ( var=subfield_value(&subfield,"first") ) { if (strlen(var) < sizeof(frm_first)) strcpy (frm_first,var); } if ( var=subfield_value(&subfield,"url") ) { if (strlen(var) < sizeof(frm_url)) strcpy (frm_url,var); } if ( var=subfield_value(&subfield,"comment") ) { if (strlen(var) < sizeof(frm_comment)) strcpy (frm_comment,var); } cgi_printf("Content-type: text/html\n\n"); } else { cgi_printf("Content-type: text/html\n\n"); arg->state = 3; } } return 1; } /****************************************************************************/ /* Main program. * Arguments: * argv[1] name of template file. */ int main ( int argc, char **argv ) { char *template, *username; int length, status, i, t_used, t_alloc; struct db_params escape_arg; /* * Initialize cgilib and get template file name. */ status = cgi_init ( argc, argv ); if ( (status&1) == 0 ) exit ( status ); template = cgi_info ( "PATH_TRANSLATED" ); if ( !template ) { cgi_printf ( "content-type: text/plain\n" ); cgi_printf ( "status: 500 bad info\n\nMissing or invalid path info\n"); cgi_printf ( "Must supply template file name\n"); exit ( 1 ); } if ( !*template ) { cgi_printf ( "content-type: text/plain\n" ); cgi_printf ( "status: 500 bad info\n\nNull or invalid path info\n"); cgi_printf ( "Must supply template file name\n"); exit ( 1 ); } username = cgi_info ( "REMOTE_USER" ); if ( !username || !*username ) { cgi_printf("content-type: text/plain\n\n"); cgi_printf("Not authorized to post, be sure that the script is"); cgi_printf(" protected so you wil be prompted\n"); exit(1); } /* * Initialize form input values. */ set_default_template_fields ( 5, form_names, form_values ); if ( strlen(username) < sizeof(frm_username) ) { strcpy ( frm_username, username ); } else { strcpy ( frm_username, "" ); } frm_first[0] = frm_last[0] = frm_url[0] = frm_comment[0] = '\0'; /* * Scan template file. Function called will expand the template * file and callback my_out and my_escape with the result. */ escape_arg.state = 0; status = process_template_file ( template, (template_callback) my_out, &escape_arg, (template_callback) my_escape, &escape_arg ); printf("status of processing: %d\n", status ); if ( escape_arg.state < 2 ) { /* * Fallback error message. */ cgi_printf ( "content-type: text/plain\n" ); cgi_printf ( "status: 500 error\n\n" ); cgi_printf ( "Error processing template file, final status: %d\n", status ); } else { /* * Do post-processing. Close and rename the html file output. */ if ( escape_arg.html_file ) { fclose ( escape_arg.html_file ); rename ( escape_arg.work_name, escape_arg.tok[2] ); } } return status; } /****************************************************************************/ /* Read form content and parse. Return value is number of keywords parsed, * -1 for error. */ static int parse_form ( int key_limit, char **key, char **value ) { int i, j, content_length, length, count, start, finish, flen; char *var, *fdata; /* * Read form content into memory in preparation for parsing. */ count = 0; var = cgi_info ( "CONTENT_LENGTH" ); content_length = var ? atoi(var) : 0; if ( content_length > 0 ) { /* * Allocate buffer and read entire form data into it, forcing final &. */ fdata = malloc ( content_length+1 ); if ( !fdata ) return -1; length = cgi_read ( fdata, content_length ); } else { /* See if form method was GET by mistake. */ var = cgi_info ( "QUERY_STRING" ); if ( var ) { length = strlen ( var ); fdata = malloc ( length + 1 ); if ( !fdata ) return -1; strcpy ( fdata, var ); } else length = 0; } if ( length <= 0 ) return length; /* * Parse into keyword/value pairs, ampersands delimit the pairs. */ if ( fdata[length-1] != '&' ) fdata[length++] = '&'; start = finish = 0; for ( i = 0; i < length; i++ ) if ( !fdata[i] || (fdata[i] == '&') ) { /* * Value parsed. Unescape string and look for first equals to * to delimit field name from value. */ flen = i - start; for ( j = start; j < i; j++ ) if ( fdata[j] == '+' ) fdata[j] = ' '; net_unescape_string ( &fdata[start], &flen ); finish = start + flen; fdata[finish] = '\0'; for ( j = start; j < finish; j++ ) if ( fdata[j] == '=' ) { /* Save pointers to pair in callers buffer, terminate strings */ key[count] = &fdata[start]; fdata[j] = '\0'; value[count] = &fdata[j+1]; count++; if ( count >= key_limit ) return count; } else { /* Make characters in field name upper case */ /* fdata[j] = _toupper(fdata[j]); */ } start = i + 1; } return count; } /****************************************************************************/ /* Lookup field and parse form data. */ static int update_user ( subfield_ctx subfield, struct db_params *arg ) { int i, j, k, status, input_count, is_new, is_delete; char *keyword[64], *value[64], *oldval, *username; char ndx_key[44], now[40]; time_t date; /* * Parse form data into keyword/value pairs. */ input_count = parse_form ( 64, keyword, value ); if ( input_count < 0 ) { cgi_printf("Content-type: text/plain\nstatus: 500 bad form-data\n\n"); cgi_printf("Invalid form data\n"); subfield_close ( subfield ); exit ( 20 ); } /* * locate first and last name fields an combine to index key. * Also force to lower case the fields. */ for ( i = 0; i < 40; i++ ) ndx_key[i] = ' '; ndx_key[40] = '\0'; for ( i = 0; i < input_count; i++ ) { if ( strcmp(subfield->fld[2].name, keyword[i]) == 0 ) { /* Copy last name and pretty it */ for ( k = 0; value[i][k]; k++ ) { if ( k == 0 ) value[i][k] = _toupper ( value[i][k] ); else value[i][k] = _tolower ( value[i][k] ); if ( k < 30 ) ndx_key[k] = _toupper ( value[i][k] ); } /* Fixup mixed case prefixes */ if ( strncmp ( value[i],"Mc", 2 ) == 0 ) value[i][2] = _toupper ( value[i][2] ); } else if ( strcmp(subfield->fld[3].name, keyword[i]) == 0 ) { /* Copy first name (and others) */ int make_low; for ( make_low = k = 0; value[i][k]; k++ ) { if ( !make_low ) value[i][k] = _toupper ( value[i][k] ); else value[i][k] = _tolower ( value[i][k] ); make_low = !isspace(value[i][k]); if ( k < 10 ) ndx_key[k+30] = _toupper ( value[i][k] ); } } } if ( ndx_key[0] == ' ' ) ndx_key[0] = '1'; /* * See if key already present. */ username = cgi_info ( "REMOTE_USER" ); status = subfield_read ( subfield, username, 0, strlen(username) ); if ( (status&1) == 1 ) { is_new = 0; #ifdef DEBUG printf("ndx_key: '%s'\n", ndx_key ); for ( i=0; i < subfield->item_count; i++ ) printf("old[%d].name = '%s', .value = '%s'\n", i, subfield->fld[i].name, subfield->fld[i].value ); #endif /* Name already in use */ } else { is_new = 1; } /* * Scan form data and replace named fields. */ is_delete = 0; for ( i = 0; i < input_count; i++ ) { printf("form keyword: %s\n", keyword[i] ); if ( strcmp ( keyword[i], "delete" ) == 0 ) { /* Magic field, Delete record */ is_delete = 1; break; } oldval = subfield_value ( subfield, keyword[i] ); if ( !oldval ) { /* invalid field in form */ } else { subfield_set ( subfield, keyword[i], value[i] ); } } /* * Set special fields. */ status = subfield_set ( subfield, subfield->fld[0].name, username ); status = subfield_set ( subfield, subfield->fld[1].name, ndx_key ); if ( subfield_value ( subfield, "date" ) ) { /* special field, set to current date */ time ( &date ); strcpy ( now, ctime(&date) ); subfield_set ( subfield, "date", now ); } #ifdef DEBUG for ( i=0; i < subfield->item_count; i++ ) printf("new[%d].name = '%s', .value = '%s'\n", i, subfield->fld[i].name, subfield->fld[i].value ); #endif if ( is_delete ) { if ( !is_new ) status = subfield_delete_rec ( subfield ); else status = 20; printf("delete record status: %d, is_new: %d\n", status, is_new ); } else if ( is_new ) status = subfield_write ( subfield ); else status = subfield_update ( subfield ); /* * Create an output file for an updated HTML file. */ sprintf ( arg->work_name, "%s-%x", arg->tok[2], getpid() ); arg->html_file = fopen ( arg->work_name, "w" ); printf("Status: %d, Openned html file '%s'\n", status, arg->work_name ); return 1; }