#include #include #include #include #include #include #include #include #include "global.h" #include "manhat-lib/shared_util.h" #include "manhat-lib/shared_strtrm.h" #include "manhat-lib/shared_super_util.h" #include "manhat-lib/shared_authenticate.h" #include "manhat-lib/shared_server_string.h" #include "manhat-lib/shared_person_list.h" #include "manhat-lib/shared_roster_xml_parser.h" #include "manhat-lib/shared_survey_string.h" #include "manhat-lib/shared_cs_util.h" #define MAX_ROSTER_LINE 200 /* max len of a line in the uploaded roster.txt file */ static void read_parameters (char *subdir, char *course_id, char *key, COURSE_TYPE *course_type) { char tmp[21]; char new_subdir[MAX_COURSE_SUBDIR +1]; int i; cs_get_required_parameter ("crs", course_id, MAX_COURSE_ID ); cs_get_required_parameter ("id", key, MAX_KEY ); cs_get_required_parameter ("subdir", subdir, MAX_COURSE_SUBDIR ); cs_get_required_parameter("type", tmp, 20); *course_type = CENTRAL; /* to prevent unitialized variable compiler warnings */ i = atoi(tmp); switch(i) { case 0: *course_type = CENTRAL; break; case 1: *course_type = STANDALONE; break; case 2: *course_type = TEMPLATE; break; default: cs_critical_error (ERR_INVALID_COURSE_TYPE, ""); break; } if( !strcmp(subdir, "__new__") && *course_type != TEMPLATE) { cs_get_required_parameter("new_subdir", new_subdir, MAX_COURSE_SUBDIR ); strncpy(subdir, new_subdir, MAX_COURSE_SUBDIR +1); } } /** converts carriage returns to line feeds in first bytes_read chars of buffer **/ static void cr_to_nl (char *buffer, int bytes_read) { char *ptr; int i; for (ptr = buffer, i = 1; i <= bytes_read; ptr++, i++) if (*ptr == 13) *ptr = 10; } /** does an ASCII dec 10 appear in the first bytes_read chars of buffer? * **/ int has_newlines (char *buffer, int bytes_read) { char *ptr; int i; for (ptr = buffer, i = 1; i <= bytes_read; ptr++, i++) if (*ptr == 10) return 1; return 0; } static void write_cgi_file (FILE *infp, const char *filepath) { #define CHUNKSIZE 2048 FILE *outfp; int bytes_read; int bytes_left; char buffer[CHUNKSIZE]; int newlines = -1; /* does this file contain at least one newline ASCII 10 a Macintosh kludge */ struct stat file_stat; if(fstat(fileno(infp), &file_stat)) cs_critical_error(ERR_NO_FILE, ""); if(file_stat.st_size ==0) cs_critical_error(ERR_NO_FILE, ""); outfp = fopen (filepath, "w"); if (!outfp) cs_critical_error(ERR_FOPEN_WRITE_FAILED, ""); bytes_left = file_stat.st_size; bytes_read = CHUNKSIZE; while (bytes_left) { if (bytes_left < CHUNKSIZE) bytes_read = bytes_left; if (fread (buffer, bytes_read, 1, infp) != 1) cs_critical_error (ERR_FREAD_FAILED, ""); if (newlines == -1) newlines = has_newlines (buffer, bytes_read); /* do this once */ if (!newlines) /* assume it's a macintosh file and convert */ cr_to_nl (buffer, bytes_read); bytes_left -= bytes_read; if (fwrite (buffer, bytes_read, 1, outfp) != 1) cs_critical_error (ERR_FWRITE_FAILED, filepath); } fclose (outfp); #undef CHUNKSIZE } /* writes the uploaded file to ROSTER_FNAME in course_dir ** exits if the uploaded file is missing or empty */ static void write_roster_file ( FILE *fp, char *filepath) { #define MAX_MSG 500 int fd; snprintf (filepath, MAX_PATH + 1, "%sCGI_DATA_XXXXXX", TMP_PATH); fd = mkstemp (filepath); if(fd == -1) cs_critical_error(ERR_MKSTEMP_FAILED, ""); close(fd); change_mkstemp_permission(filepath); /*shared_file_util.c */ write_cgi_file (fp, filepath); #undef MAX_MSG } static void verify_ascii_file(const char *file) { char ch; FILE *fp; fp = fopen(file, "r"); if(!fp) cs_critical_error(ERR_FOPEN_READ_FAILED, file); while(fread(&ch, sizeof(char), 1, fp) == 1) if(!isascii(ch)) { fclose(fp); cs_critical_error(ERR_ROSTER_FILE_NOT_ASCII, ""); } /* rewind the file */ if(fseek(fp, 0 , SEEK_SET)) { fclose(fp); cs_critical_error(ERR_FSEEK_FAILED, ""); } } /* reads a line from fp, not to exceed maxlen in length ** strips leading/trailing whitespace */ int read_line (FILE * fp, char *string, int maxlen) { char line[MAX_ROSTER_LINE + 1]; if (!fgets (line, MAX_ROSTER_LINE, fp)) return 0; strtrm (line); /* shared_strtrm.c */ if ((int)strlen (line) > maxlen) return 0; strncpy (string, line, maxlen + 1); return 1; } /** used to read the first four lines of the roster.txt file, *** pointed to by fp; *** line_no indicates which line we're working with, so the value *** returned in 'result' can be checked **/ static void read_opening_line(FILE *fp, char *result, int line_no) { char line[MAX_ROSTER_LINE + 1]; int len; char msg[10]; int max_string_len[4] = {MAX_COURSE_NO, MAX_COURSE_TITLE, MAX_SEMESTER, MAX_INSTRUCTOR}; char read_error_msg[4][50] = {ERR_ERROR_READING_COURSE_CODE, ERR_ERROR_READING_COURSE_TITLE, ERR_ERROR_READING_SEMESTER, ERR_ERROR_READING_TEACHER_NAME}; char empty_value_msg[4][50] = {ERR_COURSE_CODE_EMPTY , ERR_COURSE_TITLE_EMPTY, ERR_SEMESTER_EMPTY, ERR_TEACHER_NAME_EMPTY }; char long_value_msg[4][50] = { ERR_COURSE_CODE_TOO_LONG, ERR_COURSE_TITLE_TOO_LONG, ERR_SEMESTER_TOO_LONG, ERR_TEACHER_NAME_TOO_LONG }; /* this function can only deal with lines 1-4 */ if(line_no > 4 || line_no < 1) cs_critical_error(ERR_GENERAL_ERROR, ""); if (!fgets (line, MAX_ROSTER_LINE, fp)) cs_critical_error(read_error_msg[line_no - 1], ""); strtrm(line); /* shared_strtrm.c */ len = strlen(line); if(!len) cs_critical_error(empty_value_msg[line_no - 1], ""); if(len > max_string_len[line_no - 1]) { snprintf(msg, 9, "%d", max_string_len[line_no - 1]); cs_critical_error(long_value_msg[line_no-1], msg); } strcpy(result, line); } static void get_info(FILE *fp, XML_COURSE_INFO * info) { read_opening_line(fp, info->course_code, 1); read_opening_line(fp, info->title, 2); read_opening_line(fp, info->semester, 3); read_opening_line(fp, info->instructor_title, 4); } static void fill_course_info(FILE * fp, XML_COURSE_INFO *info, const char *subdir, const char *course) { strncpy(info->subdir, subdir, MAX_COURSE_SUBDIR +1); strncpy(info->dir_id, course, MAX_COURSE_ID +1); get_info(fp, info); } /* does a quick check of the format of the person's id */ static void check_id_format(char *id) { char *ptr; char msg[20]; int found; if(strlen(id) > MAX_ID) { snprintf(msg, 19, "%d", MAX_ID); cs_critical_error(ERR_MAX_ID_LEN_IS_X, msg); } /*** an ID must have contain at least one digit 0-9 */ for(found = 0, ptr = id; !found && *ptr; ptr++) found = isdigit(*ptr); if(!found) cs_critical_error(ERR_ID_MUST_HAVE_DIGIT,""); /* check for some strange characters **/ for(ptr = id; *ptr; ptr++) if(strchr(ILLEGAL_ID_CHARS, *ptr)) /* global.h */ { snprintf(msg, 19, ILLEGAL_ID_CHARS); cs_critical_error(ERR_ID_HAS_BANNED_CHARS_X, msg); } } /* quick check to see if the 'name' is reasonable' */ static void check_name_format(char *name) { char msg[20]; char banned_characters[] = ":/\\;,?"; char *ptr; if(strlen(name) > MAX_NAME) { snprintf(msg, 19, "%d", MAX_NAME); cs_critical_error(ERR_MAX_NAME_LEN_IS_X, msg); } for(ptr=name; *ptr; ptr++) if(strchr(banned_characters,*ptr)) { snprintf(msg, 10, banned_characters); cs_critical_error(ERR_NAME_HAS_BANNED_CHARS_X, msg); } } /** parses 'line' into id, first,last, fullname fields *** while checking for valid data **/ static void parse_person_line(char *line, char *id, char *first, char *last) { char *ptr, *ptr2, tmp; char msg[20]; /*** handle ID ****/ for (ptr = line; !(isspace (*ptr)) && *ptr; ptr++); /* find first whitespace */ if (!*ptr) cs_critical_error(ERR_PERSON_LINE_HAS_BAD_FORMAT, line); tmp = *ptr; *ptr = '\0'; check_id_format(line); strncpy (id, line, MAX_ID + 1); /*** Handle first name ****/ *ptr = tmp; for (ptr++; *ptr && isspace (*ptr); ptr++); /* find next non-blank */ if (!*ptr) cs_critical_error(ERR_PERSON_LINE_HAS_BAD_FORMAT, line); for (ptr2 = ptr; *ptr2 && !isspace (*ptr2) ; ptr2++); /*ptr2->next non-space */ if (!*ptr2) cs_critical_error(ERR_PERSON_LINE_HAS_BAD_FORMAT, line); tmp = *ptr2; *ptr2 = '\0'; check_name_format(ptr); strncpy (first, ptr, MAX_NAME + 1); *ptr2 = tmp; /** handle last name ***/ for (ptr2++; *ptr2 && isspace (*ptr2) ; ptr2++); /* find next non-blank */ if (!*ptr2) cs_critical_error(ERR_PERSON_LINE_HAS_BAD_FORMAT, line); check_name_format(ptr2); strncpy (last, ptr2, MAX_NAME + 1); if((strlen(first) + strlen(last) + 1) > MAX_NAME) { snprintf(msg, 19, "%d", MAX_NAME); cs_critical_error( ERR_MAX_NAME_LEN_IS_X, msg); } // snprintf(fullname, MAX_NAME + 1, "%s %s", first, last); } static void check_invalid_chars(const char *string) { const char *cptr; char msg[20]; for(cptr = string; *cptr; cptr++) { if((*cptr != '_') && !isdigit(*cptr) && !isalpha(*cptr)) { snprintf(msg, 19, string); cs_critical_error(ERR_COURSE_X_HAS_INVALID_CHARS, msg); } } } /* assumes strings have been trimmed * ** in read_parameters() * */ static void check_internal_course_names(const char *subdir, COURSE_TYPE course_type) { if( course_type == CENTRAL || course_type == STANDALONE) { if(*(subdir) == 'T') cs_critical_error(ERR_COURSE_CANT_START_WITH_T, ""); } check_invalid_chars(subdir); } XML_COURSE_INFO * build_course_info(const char *prefix, const char *subdir, const char *course) { XML_COURSE_INFO *course_data; char path[MAX_PATH +1]; FILE *fp; char line[MAX_ROSTER_LINE +1]; snprintf(path, MAX_PATH +1, "%s", prefix); fp = fopen(path, "r"); if(!fp) cs_critical_error(ERR_FOPEN_READ_FAILED, ""); course_data = (XML_COURSE_INFO*) malloc(sizeof(XML_COURSE_INFO)); fill_course_info( fp, course_data, subdir, course); /* reads first four lines of the file */ /* the fifth line represents the teacher */ if(!fgets(line, MAX_ROSTER_LINE, fp)) cs_critical_error(ERR_TEACHER_ID_LINE_MISSING, ""); strtrm(line); /* shared_strtrm.c */ if(!strlen(line)) cs_critical_error(ERR_TEACHER_ID_LINE_MISSING, ""); course_data->user_head = (P_NODE *)malloc(sizeof(P_NODE)); course_data->user_head->next = 0; if(!course_data->user_head) cs_critical_error(ERR_MALLOC_FAILED, ""); parse_person_line(line, course_data->user_head->data.id, course_data->user_head->data.username, course_data->user_head->data.lastname); course_data->user_head->data.group = FACULTY; course_data->user_current = course_data->user_head; /* remaining lines are students */ while(fgets(line, MAX_ROSTER_LINE, fp)) { strtrm(line); /* shared_strtrm.c */ if(strlen(line)) /* ignore blank lines */ { course_data->user_current->next = (P_NODE *)malloc(sizeof(P_NODE)); course_data->user_current = course_data->user_current->next; course_data->user_current->next =0; parse_person_line(line, course_data->user_current->data.id, course_data->user_current->data.username, course_data->user_current->data.lastname); course_data->user_current->data.group = STUDENT; } } fclose(fp); unlink(path); return course_data; } void write_data_to_xml( char *prefix, XML_COURSE_INFO *course_data) { char path[MAX_PATH +1]; FILE *fp; P_NODE *ptr; snprintf(path, MAX_PATH+1, "%s.xml", prefix); strncpy(prefix, path, MAX_PATH +1); fp = fopen(path, "w"); if(!fp) cs_critical_error(ERR_FOPEN_WRITE_FAILED, ""); fprintf(fp,"\n"); fprintf(fp, "\n"); fprintf(fp, "\n"); fprintf(fp, " \n", replace_sp(course_data->subdir), replace_sp(course_data->dir_id)); fprintf(fp, " %s\n", replace_sp(course_data->course_code)); fprintf(fp, " %s\n", replace_sp(course_data->title)); fprintf(fp, " %s\n", replace_sp(course_data->instructor_title)); fprintf(fp, " %s\n", replace_sp(course_data->semester)); fprintf(fp, " \n"); for(ptr = course_data->user_head; ptr; ptr = ptr->next) { fprintf(fp, " \n", replace_sp(ptr->data.id)); fprintf(fp, " %s\n", replace_sp(ptr->data.username)); fprintf(fp, " %s\n", replace_sp(ptr->data.lastname)); fprintf(fp, " \n"); fprintf(fp, " %s\n", ptr->data.group==FACULTY?"faculty":"student"); fprintf(fp, " \n"); } fprintf(fp, " \n"); fprintf(fp, " \n"); fprintf(fp, "\n"); fclose(fp); } int main() { char course[MAX_COURSE_ID + 1]; char subdir[MAX_COURSE_SUBDIR + 1]; char key[MAX_KEY + 1]; COURSE_TYPE course_type; SESSION user; char temp_course[MAX_COURSE_SUBDIR +1]; char prefix_rosterfile[MAX_PATH +1]; XML_COURSE_INFO *course_data; FILE *fp; cs_cgi_init(); read_parameters (subdir, course, key, &course_type); validate_super_key(key, &user); /* shared_super_util.c */ fp = cgi_filehandle (global_cgi, "roster"); if(!fp) cs_critical_error(ERR_UPLOAD_FILE, ""); /** shared_cs_util.h */ check_internal_course_names(subdir, course_type); check_invalid_chars(course); if(course_type == TEMPLATE) { if(*subdir != 'T') { snprintf(temp_course, MAX_COURSE_SUBDIR+1, "%s%s", "T", subdir); strncpy(subdir, temp_course, MAX_COURSE_SUBDIR +1); } } write_roster_file(fp, prefix_rosterfile); verify_ascii_file(prefix_rosterfile); /* verify that the uploaded file is an ASCII file */ course_data = build_course_info(prefix_rosterfile, subdir, course); write_data_to_xml(prefix_rosterfile, course_data); free_option_person_list (course_data->user_head); free(course_data); printf("Location: %s?id=%s&roster_path=%s&c1_subdir=%s&c1_new_id=%s&c1_orig_id=%s&c1_type=%s&count=1&c1_subdir_or=%s\n\n", "super_xml_course_create", key, prefix_rosterfile, subdir, course, course, (course_type==CENTRAL ? "Normal": (course_type== TEMPLATE ? "Template": "Standalone")), subdir); cs_cgi_destroy(); return 0; }