#include #include #include #include #include #include "global.h" #include "custom.h" #include "manhat-lib/shared_cs_util.h" #include "manhat-lib/shared_util.h" #include "manhat-lib/shared_roster_xml_parser.h" #include "manhat-lib/shared_survey_string.h" #include "manhat-lib/shared_authenticate.h" #include "manhat-lib/shared_central_user.h" #include "manhat-lib/shared_file_util.h" #include "manhat-lib/shared_server_string.h" #include "manhat-lib/shared_encrypt.h" #include "manhat-lib/shared_user_index.h" #include "manhat-lib/shared_super_util.h" #include "manhat-lib/shared_strtrm.h" #include "manhat-lib/shared_new_course_lock.h" #include "manhat-lib/shared_add_util.h" #include "manhat-lib/shared_authenticate.h" #include "manhat-lib/shared_lock.h" #include "manhat-lib/shared_person_list.h" /* ** This data structure keeps track of the data items for each course created. */ typedef struct directory_name_node { char name[MAX_COURSE_NO + 1]; /* the original internal name of the course, as stored in the XML file */ char dir_id[MAX_COURSE_ID + 1]; /* the new internal name of the course, as modified by the user */ char subdir[MAX_COURSE_SUBDIR + 1]; /* the new internal subdirectory for the course */ COURSE_TYPE course_type; /* Standalone, Normal or Template */ struct directory_name_node *next; } DIRECTORY_NAME_NODE; typedef enum user_state{NEW, /* this is a new user */ NEW_1, EXISTS, /* this is an existing user */ ERROR_UID, /* couldn't derive a username */ ERROR_NAME /* name in xml file didn't match existing name */ }USER_STATE; typedef enum user_add_state{FINISH, /* user was successfully added */ FAILED, /* error occurred while creating user */ USEREXISTS /* user was already in the course */ }USER_ADD_STATE; typedef enum user_central_state{C_ERROR, /* an error occurred while adding user to central database */ SUCCESS, /* user was successfully added to central database */ UN_CHANGE /* user was already in database, no changes required */ }USER_CENTRAL_STATE; typedef struct user { char first[MAX_NAME + 1]; char last[MAX_NAME + 1]; char id[MAX_ID + 1]; char username[MAX_USERNAME + 1]; char password[MAX_PASSWORD + 1]; GROUP_TYPE group; USER_STATE is_new; USER_ADD_STATE add_state; USER_CENTRAL_STATE central_state; }USER; typedef struct user_node { USER user; struct user_node *next; }USER_NODE; typedef enum course_state{UPDATE, /* course already existed, this is an update */ C_NEW, /* this is a completely new course */ DIR_ERR, /* couldn't create course directory */ MSG_ERR, /* couldn't create chat message file */ CONFIG_ERR, /* couldn't write config.dat */ COURSE_ERR, /* general error? */ TYPE_CONFLICT /* course type conflict */ } COURSE_STATE; /* adding teacher to a course */ typedef enum t_pass_state{OPEN_ERR, /* error appending to password file */ T_SUCCESS /* all teachers added successfully */ }T_PASS_STATE; typedef struct master_course_list { char course_subdir[MAX_COURSE_SUBDIR + 1]; char course_dir[MAX_COURSE_ID + 1]; char course_path[MAX_PATH + 1]; char course_no[MAX_COURSE_NO + 1]; char course_title[MAX_COURSE_TITLE + 1]; char course_semester[MAX_SEMESTER + 1]; char instructor_title[MAX_INSTRUCTOR + 1]; COURSE_TYPE course_type; COURSE_STATE exists; T_PASS_STATE t_state; USER_NODE *head; /* list of teachers only */ USER_NODE *add_head; /* complete list of people who have been added to the course */ USER_NODE *add_current; struct master_course_list *next; } CREATED_COURSE_NODE; typedef struct course_group { char course_subdir[MAX_COURSE_SUBDIR + 1]; char course_dir[MAX_COURSE_NO + 1]; COURSE_TYPE course_type; GROUP_TYPE group; struct course_group *next; }COURSE_GROUP; typedef struct user_group_info { USER user; COURSE_GROUP *head; COURSE_GROUP *current; struct user_group_info *next; }USER_GROUP_INFO; typedef struct user_group_info_list { USER_GROUP_INFO *head; USER_GROUP_INFO *current; }USER_GROUP_INFO_LIST; /** Copies string 'source' into 'dest' without buffer overflow, *** and ensures the result is null terminated. *** *** len is the size (in chars) of dest. **/ static void safe_strncpy(char *dest, char *source, int len) { strncpy(dest, source, len); *(dest + len - 1) = '\0'; } USER_NODE * find_all_teachers(XML_COURSE_INFO *info) { USER_NODE *head =0, *ptr, *current=0; for(info->user_current = info->user_head; info->user_current; info->user_current=info->user_current->next) { if(info->user_current->data.group == FACULTY) { ptr = (USER_NODE*)malloc(sizeof(USER_NODE)); if(!ptr) cs_critical_error(ERR_MALLOC_FAILED, "in find_all_teachers()"); ptr->next = 0; safe_strncpy(ptr->user.first, info->user_current->data.realname, MAX_NAME + 1); safe_strncpy(ptr->user.last, info->user_current->data.lastname, MAX_NAME + 1); safe_strncpy(ptr->user.id, info->user_current->data.id, MAX_ID + 1); safe_strncpy(ptr->user.username, info->user_current->data.username, MAX_USERNAME + 1); ptr->user.group = FACULTY; ptr->user.central_state = UN_CHANGE; /* assume this to start */ if(!head) head = ptr; else current->next = ptr; current = ptr; } } return head; } USER_NODE * find_all_users_in_standalone(XML_COURSE_INFO *info) { USER_NODE *head =0, *ptr, *current=0; for(info->user_current = info->user_head; info->user_current; info->user_current=info->user_current->next) { ptr = (USER_NODE*)malloc(sizeof(USER_NODE)); if(!ptr) cs_critical_error(ERR_MALLOC_FAILED, "in find_all_users_in_standalone"); ptr->next =0; safe_strncpy(ptr->user.first, info->user_current->data.realname, MAX_NAME + 1); safe_strncpy(ptr->user.last, info->user_current->data.lastname, MAX_NAME + 1); safe_strncpy(ptr->user.id, info->user_current->data.id, MAX_ID + 1); safe_strncpy(ptr->user.username, info->user_current->data.username, MAX_USERNAME + 1); ptr->user.group = info->user_current->data.group; ptr->user.central_state = UN_CHANGE; if(!head) head = ptr; else current->next = ptr; current = ptr; } return head; } USER_GROUP_INFO * find_exist_users(USER_GROUP_INFO_LIST *list, P_NODE* info) { USER_GROUP_INFO *ptr; for(ptr=list->head; ptr; ptr = ptr->next) { if(strcmp(ptr->user.id, info->data.id) ==0) return ptr; } return 0; } USER_GROUP_INFO_LIST * process_one_course(USER_GROUP_INFO_LIST *list, XML_COURSE_INFO *info) { USER_GROUP_INFO *ptr, *find; /* for each user in the course */ for(info->user_current=info->user_head; info->user_current; info->user_current=info->user_current->next) { if(info->user_current->data.group != FACULTY) { find = find_exist_users(list, info->user_current); if(!find) { ptr = (USER_GROUP_INFO*)malloc(sizeof(USER_GROUP_INFO)); safe_strncpy(ptr->user.first, info->user_current->data.realname,MAX_NAME + 1); safe_strncpy(ptr->user.last, info->user_current->data.lastname,MAX_NAME + 1); safe_strncpy(ptr->user.id, info->user_current->data.id,MAX_ID + 1); if(strlen(info->user_current->data.username)>0) safe_strncpy(ptr->user.username, info->user_current->data.username,MAX_USERNAME + 1); else ptr->user.username[0]='\0'; ptr->user.is_new =NEW; ptr->user.central_state =UN_CHANGE; ptr->user.group=info->user_current->data.group; ptr->next =0; /** add a new course for this user */ ptr->head = (COURSE_GROUP*)malloc(sizeof(COURSE_GROUP)); ptr->current = ptr->head; ptr->head->next =0; safe_strncpy(ptr->head->course_subdir, info->subdir, MAX_COURSE_NO + 1); safe_strncpy(ptr->head->course_dir, info->dir_id, MAX_COURSE_NO + 1); ptr->head->course_type = info->course_type; ptr->head->group=info->user_current->data.group; if(!list->head) list->head = ptr; else list->current->next = ptr; list->current = ptr; } else /* user was already listed */ { find->user.central_state =UN_CHANGE; find->current->next=(COURSE_GROUP*)malloc(sizeof(COURSE_GROUP)); find->current = find->current->next; safe_strncpy(find->current->course_subdir, info->subdir, MAX_COURSE_NO + 1); safe_strncpy(find->current->course_dir, info->dir_id, MAX_COURSE_NO + 1); find->current->course_type = info->course_type; find->current->group = info->user_current->data.group; find->current->next =0; } } } return list; } USER_GROUP_INFO_LIST * build_user_list(XML_COURSE_INFO_LIST *list) { USER_GROUP_INFO_LIST *head; /* start with an empty user list */ head = (USER_GROUP_INFO_LIST *)malloc(sizeof(USER_GROUP_INFO_LIST)); if(!head) cs_critical_error(ERR_MALLOC_FAILED, "build_user_list()"); head->head =0; head->current =0; /* for each course */ for(list->current=list->head; list->current; list->current = list->current->next) { if(list->current->course_type != STANDALONE) head = process_one_course(head, list->current); } return head; } /* start list with normal and template courses ** and only teacher info */ CREATED_COURSE_NODE* build_normal_template_course_list(XML_COURSE_INFO_LIST *list) { USER_NODE *teacher_head; CREATED_COURSE_NODE *head = 0, *ptr, *current = 0; for(list->current=list->head; list->current; list->current = list->current->next) { /* if NOT Standalone course */ if(list->current->course_type != STANDALONE) { teacher_head = find_all_teachers(list->current); if(!teacher_head) { cs_critical_error(ERR_COURSE_X_Y_HAS_NO_TEACHER, list->current->title); } ptr = (CREATED_COURSE_NODE *)malloc(sizeof(CREATED_COURSE_NODE)); if(!ptr) cs_critical_error(ERR_MALLOC_FAILED, "build_normal_template_course_list()"); safe_strncpy(ptr->course_subdir, list->current->subdir, MAX_COURSE_SUBDIR + 1); safe_strncpy(ptr->course_dir, list->current->dir_id, MAX_COURSE_ID + 1); safe_strncpy(ptr->course_no, list->current->course_code, MAX_COURSE_NO + 1); safe_strncpy(ptr->course_title, list->current->title, MAX_COURSE_TITLE + 1); safe_strncpy(ptr->instructor_title, list->current->instructor_title, MAX_INSTRUCTOR + 1); ptr->course_type = list->current->course_type; /* change semester to a standard value for course templates */ if(list->current->course_type == TEMPLATE) safe_strncpy(ptr->course_semester, "Course Temlate", MAX_SEMESTER + 1); else safe_strncpy(ptr->course_semester, list->current->semester, MAX_SEMESTER + 1); ptr->next = 0; ptr->head = teacher_head; ptr->add_head = 0; ptr->add_current = 0; if(!head) head = ptr; else current-> next = ptr; current = ptr; } } return head; } int find_this_username(USER_NODE *all, const char *username) { USER_NODE *ptr; for(ptr = all; ptr; ptr = ptr->next) { if(strlen(ptr->user.username) > 0 && strcmp(ptr->user.username, username) ==0) return 1; } return 0; } USER_NODE * find_teacher_in_course_head(USER_NODE *head, const char *id) { USER_NODE *ptr; for(ptr = head; ptr; ptr = ptr->next) { if(strcmp(ptr->user.id, id) ==0) return ptr; } return 0; } /* build list of standalone courses with teachers and students */ CREATED_COURSE_NODE * standalone_course_list(XML_COURSE_INFO_LIST *list) { CREATED_COURSE_NODE *head =0, *ptr, *current=0; for(list->current=list->head; list->current; list->current = list->current->next) { if(list->current->course_type == STANDALONE) { ptr = (CREATED_COURSE_NODE *)malloc(sizeof(CREATED_COURSE_NODE)); safe_strncpy(ptr->course_subdir, list->current->subdir, MAX_COURSE_SUBDIR + 1); safe_strncpy(ptr->course_dir, list->current->dir_id, MAX_COURSE_ID + 1); snprintf(ptr->course_path, MAX_PATH + 1, "%s%s/%s", COURSE_PATH, ptr->course_subdir, ptr->course_dir); safe_strncpy(ptr->course_no, list->current->course_code, MAX_COURSE_NO + 1); safe_strncpy(ptr->course_title, list->current->title, MAX_COURSE_TITLE + 1); ptr->course_type = list->current->course_type; safe_strncpy(ptr->course_semester, list->current->semester, MAX_SEMESTER + 1); ptr->next = 0; ptr->head = find_all_teachers(list->current); ptr->add_head =find_all_users_in_standalone(list->current); ptr->add_current = ptr->add_head; if(!head) head = ptr; else current->next = ptr; current = ptr; } } return head; } static void print_heading(const char *key) { printf("%s%s\n", "Course List", ""); fflush(stdout); } static void print_course_table(CREATED_COURSE_NODE *head, const char *title) { CREATED_COURSE_NODE *ptr; USER_NODE *t_node; int course_count =0; int people_count; char c_color[9] = "#C7C5FF"; char t_color[9] = "#A4FFE5"; char s_color[9] = "#F6FFCD"; printf("

%s

\n", title); for(ptr = head; ptr; ptr = ptr->next) { if(ptr->course_type == CENTRAL) printf("\n", c_color); else if(ptr->course_type == STANDALONE) printf("
\n", s_color); else printf("
\n", t_color); printf("\n", "Group", ptr->course_subdir); printf("\n", "Internal Course Name", ptr->course_dir); printf("\n", "Course Code", ptr->course_no); printf("\n", "Course Title", ptr->course_title); printf("\n", "Semester", ptr->course_semester); printf("\n", "Course Type", ptr->course_type == STANDALONE? "Standalone": ptr->course_type == TEMPLATE? "Template": "Normal"); printf(""); printf("", "Success?", ptr->t_state == OPEN_ERR ? "No": "Yes"); printf("
%s %s
%s %s
%s %s
%s %s
%s %s
%s %s
%s", "Status"); if(ptr->exists == C_NEW) puts("New Course"); else if(ptr->exists == UPDATE) puts("Updated Course"); else if(ptr->exists == DIR_ERR) puts("Dir Error"); else if(ptr->exists == CONFIG_ERR) puts("Config Error"); else if(ptr->exists == COURSE_ERR) puts("Course Error"); else if(ptr->exists == TYPE_CONFLICT) puts("Course type conflict"); puts("
%s %s
\n"); printf("
\n"); course_count++; people_count =0; printf("\n"); printf("\n", ptr->course_no); printf("\n", "Full Name", "ID", "Username", "Central Status", "User Type", "Course Status", "Central Status" ); for(t_node = ptr->add_head; t_node; t_node = t_node->next) { people_count++ ; printf("\n", (t_node->user.is_new == ERROR_NAME) || (t_node->user.is_new == ERROR_UID)? "#ff9999" : "#ffffff"); printf( "", t_node->user.first, t_node->user.last); printf("", t_node->user.id); printf("", t_node->user.username); printf("", ptr->course_type == STANDALONE? "N/A":(t_node->user.is_new == NEW) || (t_node->user.is_new == NEW_1) ? "New User" : (t_node->user.is_new == EXISTS)? "Existing User": (t_node->user.is_new == ERROR_NAME)? "Name Mismatch": "Username Error"); printf("", (t_node->user.group == FACULTY) ? "faculty": "student"); printf("", (t_node->user.add_state == FINISH)? "Add Successful": (t_node->user.add_state == USEREXISTS)? "Existing User": "Add Failed"); printf("\n\n", ptr->course_type == STANDALONE? "N/A":t_node->user.central_state == C_ERROR? "Failed": t_node->user.central_state == SUCCESS? "Success": "Unchanged"); } printf("\n", "Total Users", people_count); printf("
%s
%s%s%s%s%s%s%s
%s %s %s %s%s %s%s%s
%s %d

\n"); } printf("

%s %d

\n", "Total Courses", course_count); } void read_parameters( char *roster_path) { cs_get_required_parameter ("roster_path", roster_path, MAX_PATH); /* shared_cs_util.c */ } static void check_invalid_chars(const char *string) { const char *cptr; /* check for invalid characters */ for(cptr = string; *cptr; cptr++) { if((*cptr != '_') && !isdigit(*cptr) && !isalpha(*cptr)) { cs_critical_error(ERR_COURSE_X_HAS_INVALID_CHARS, string); } } } /* assumes strings have been trimmed ** in read_parameters() */ static void check_internal_course_names(XML_COURSE_INFO_LIST *list) { for(list->current=list->head; list->current; list->current=list->current->next) { if(!strlen(list->current->dir_id) || !strlen(list->current->subdir) ) cs_critical_error(ERR_COURSE_NAMES_EMPTY, ""); /* Central or Standalone Course subdir names can't start with a 'T' */ if( list->current->course_type == CENTRAL) { if(*(list->current->subdir) == 'T') cs_critical_error(ERR_COURSE_CANT_START_WITH_T, list->current->subdir); } check_invalid_chars(list->current->dir_id); check_invalid_chars(list->current->subdir); } /* for each course ... */ } /* parses the xml file into a linked list of ** course nodes */ XML_COURSE_INFO_LIST * parse_xml_file(const char *xml_file_path) { XML_COURSE_INFO_LIST *list; /* shared_roster_xml_parser.h */ FILE *fp; struct stat file_stat; char *buf; if(stat(xml_file_path, &file_stat)) { cs_critical_error(ERR_FILE_X_NOT_FOUND, xml_file_path); } buf = (char*)malloc( (file_stat.st_size + 1) * sizeof(char)); /* allocate an additional byte for terminating NULL */ if(!buf) cs_critical_error(ERR_MALLOC_FAILED, "in parse_xml_file()"); fp = fopen(xml_file_path, "r"); if(!fp) cs_critical_error(ERR_FOPEN_READ_FAILED, xml_file_path); if(fread(buf, file_stat.st_size, 1, fp) != 1) cs_critical_error(ERR_FREAD_FAILED, xml_file_path); fclose(fp); *(buf + file_stat.st_size) = '\0'; /* null terminate the buf string */ list = xml_course_data_parser(buf); /*shared_roster_xml_parser.c */ free(buf); return list; } /** For this (baypath) version, this function simply sets all *** courses to type of CENTRAL. **/ static void update_xml_info_list(XML_COURSE_INFO_LIST *list) { if(list->head) { for(list->current=list->head; list->current; list->current=list->current->next) list->current->course_type = CENTRAL; } else cs_critical_error(ERR_NO_DATA_FOUND, ""); } static void free_teacher_node(USER_NODE *t_head) { USER_NODE *ptr; while(t_head) { ptr = t_head->next; free(t_head); t_head = ptr; } } static void free_course_f_list(CREATED_COURSE_NODE *head) { CREATED_COURSE_NODE *ptr; while(head) { ptr = head->next; if(head->head) free_teacher_node(head->head); if(head->add_head) free_teacher_node(head->add_head); free(head); head = ptr; } } static void free_user_group_info_list(USER_GROUP_INFO_LIST *list) { USER_GROUP_INFO *ptr; while(list->head) { ptr = list->head->next; free(list->head); list->head = ptr; } free(list); } INDEX_USER * find_this_user_from_index(INDEX_USER_LIST *list, USER *node) { INDEX_USER *ptr; for(ptr = list->head; ptr;ptr = ptr->next) { if(strcmp(ptr->id, node->id) ==0) return ptr; } return 0; } int username_exist(INDEX_USER_LIST *index_list, const char *username) { INDEX_USER *ptr; for(ptr = index_list->head; ptr; ptr = ptr->next) { if(strcmp(ptr->username, username) ==0) return 1; } return 0; } INDEX_USER_LIST * process_one_username(USER *ptr, INDEX_USER_LIST *index_list) { INDEX_USER *a_user; char realname[MAX_NAME + 1]; char username[MAX_USERNAME + 1]; int try; ptr->is_new = NEW ; /* assume this to start */ a_user = find_this_user_from_index(index_list, ptr); if(!a_user) { if(!strlen(ptr->username)) /* if username wasn't supplied in XML file */ { derive_username (ptr->first, ptr->last, ptr->id, username, MAX_USERNAME+ 1); /* shared_authenticate.c */ for(try = 1; (try <= MAX_USERNAME_TRIES) && (username_exist (index_list, username )); try++) { derive_another_username(ptr->first, ptr->last, ptr->id, username, MAX_USERNAME + 1, try); /* shared_authenticate.c */ } if(try > MAX_USERNAME_TRIES) ptr->is_new = ERROR_UID; /* couldn't derive username */ } else /* a username was supplied in the XML file */ { safe_strncpy(username, ptr->username, MAX_USERNAME + 1); *(username + MAX_USERNAME) = '\0'; if(username_exist(index_list, username)) ptr->is_new = ERROR_UID; } if(ptr->is_new != ERROR_UID) { ptr->is_new = NEW; safe_strncpy(ptr->username, username, MAX_USERNAME + 1); derive_password (ptr->username, ptr->first,ptr->last,ptr->id, ptr->password, MAX_PASSWORD + 1); /* shared_authenticate.c */ snprintf(realname, MAX_NAME+ 1, "%s %s", ptr->first,ptr->last); index_list = add_to_index_list(index_list, ptr->username, ptr->id, realname); /* shared_user_index.c */ } } else /* person already has a central account */ { safe_strncpy(ptr->username, a_user->username, MAX_USERNAME + 1); derive_password (ptr->username, ptr->first,ptr->last,ptr->id, ptr->password, MAX_PASSWORD + 1); /* shared_authenticate.c */ snprintf(realname, MAX_NAME+ 1, "%s %s", ptr->first,ptr->last); if(strcmp(realname, a_user->realname) == 0) ptr->is_new = EXISTS; else { /* copy correct real name into first, last fields */ get_first_last_names(a_user->realname, ptr->first, ptr->last); /* shared_authenticate.c */ ptr->is_new = ERROR_NAME; /* user exists, but they had a different real name on Manhattan */ } } return index_list; } INDEX_USER_LIST * derive_teacher_username_password(CREATED_COURSE_NODE *course_head, INDEX_USER_LIST *index_list) { CREATED_COURSE_NODE *course; USER_NODE *ptr; for(course = course_head; course; course = course->next) { for(ptr = course->head; ptr; ptr = ptr->next) index_list = process_one_username(&(ptr->user), index_list); } return index_list; } INDEX_USER_LIST * derive_student_username_password(USER_GROUP_INFO_LIST *user_list, INDEX_USER_LIST *index_list) { for(user_list->current = user_list->head; user_list->current; user_list->current = user_list->current->next) { index_list = process_one_username (&(user_list->current->user), index_list); } return index_list; } int create_course_dir(CREATED_COURSE_NODE *course) { char path[MAX_PATH + 1]; /* create subdir, if necessary */ snprintf(path, MAX_PATH + 1, "%s%s", course->course_type == TEMPLATE? TEMPLATE_PATH : COURSE_PATH, course->course_subdir); if(!file_exists(path)) { if(mkdir (path, 0777)) return 0; } if(mkdir(course->course_path, 0777)) return 0; return 1; } static void get_config_default_value(CREATED_COURSE_NODE *course, CONFIG_STRUCT *conf) { char professor[MAX_INSTRUCTOR + 1]; safe_strncpy(conf->course_no, course->course_no, MAX_COURSE_NO+ 1); safe_strncpy(conf->title, course->course_title, MAX_COURSE_TITLE+ 1); safe_strncpy(conf->semester, course->course_semester, MAX_SEMESTER+ 1); if(strlen(course->instructor_title)) snprintf(professor, MAX_INSTRUCTOR + 1, "%s", course->instructor_title); else snprintf(professor, MAX_INSTRUCTOR + 1, "%s %s", "Prof.", course->head->user.last); safe_strncpy(conf->instructor, professor, MAX_INSTRUCTOR + 1); conf->login_expire = 0; conf -> key_expire = MAX_KEY_EXPIRE; conf -> mail_attachments = MAX_ATTACHMENTS; conf -> assignments = 0; conf -> syllabus = 0; conf -> discussion = 0; conf -> postoffice = 0; conf -> internet = 0; conf -> lounge = 0; conf -> chat = 0; conf -> anonymous = 0; conf -> team = 0; conf -> team_teach = 0; conf -> grades = 0; conf -> selftest = 0; conf -> lectures = 0; conf -> people = 0; conf -> surveys = 0; conf->access = ACCESS_ALL; /*Full access for teachers and students */ conf ->standalone = (course->course_type == STANDALONE)? 1 : 0; conf -> template = (course->course_type == TEMPLATE)? 1 : 0; conf->misc = 0; conf->podcasts = 0; conf->future6 = 0; conf->theme = 0; } static int write_config_file(const char *course, CONFIG_STRUCT *conf) { FILE *fp; char file_path[MAX_PATH + 1]; snprintf(file_path, MAX_PATH + 1, "%s/%s", course, CONFIG_FNAME); fp = fopen(file_path,"w"); if(!fp) return 0; if(fwrite(conf, sizeof(CONFIG_STRUCT), 1, fp) != 1) { fclose(fp); return 0; } fclose(fp); return 1; } static int create_inboxes_directory (const char *crs ) { char path[MAX_PATH + 1]; snprintf (path, MAX_PATH + 1, "%s/%s", crs, INBOXES_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR ,ANONYMOUS_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, ASSIGNMENTS_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, DISCUSSION_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, TEAM_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, INET_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, LOUNGE_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, MAIL_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, HANDOUTS_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, TEAM_TEACH_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, GRADES_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, SELFTEST_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, LECTURES_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s/%s", crs, INBOXES_DIR, SURVEYS_DIR); if(mkdir (path, 0777)) return 0; return 1; } int create_key_people(const char *course_path) { char path[MAX_PATH + 1]; snprintf (path, MAX_PATH + 1, "%s/%s", course_path, KEY_DIR); if(mkdir (path, 0777)) return 0; snprintf (path, MAX_PATH + 1, "%s/%s", course_path, PEOPLE_DIR); if(mkdir (path, 0777)) return 0; return 1; } static int write_chat_message_log(const char *course) { FILE *fp; char log_fname[MAX_PATH + 1]; snprintf(log_fname, MAX_PATH + 1, "%s/%s", course, CHAT_MSG_LOG); fp = fopen (log_fname, "w"); if(!fp) return 0; fclose(fp); return 1; } int write_logout_html (const char *crs) { FILE *fp; char fname[MAX_PATH + 1]; snprintf(fname, MAX_PATH + 1, "%s/%s", crs, EXIT_FILE); fp = fopen (fname, "w"); if(!fp) return 0; fprintf (fp, "
%s
", EXIT_URL, EXIT_URL); fclose (fp); return 1; } int create_one_person_dir (const char *course_path, const char *username) { char basedir [MAX_PATH + 1]; snprintf (basedir, MAX_PATH + 1, "%s/", course_path); return create_directories(basedir,username); /* shared_add_util.c */ } CREATED_COURSE_NODE * add_user_to_course_list(CREATED_COURSE_NODE *course, USER *ptr) { USER_NODE *target; target = (USER_NODE *)malloc(sizeof(USER_NODE)); if(!target) cs_critical_error(ERR_MALLOC_FAILED, "in add_user_to_course_list()"); safe_strncpy(target->user.username, ptr->username, MAX_USERNAME + 1); safe_strncpy(target->user.first, ptr->first, MAX_NAME + 1); safe_strncpy(target->user.password, ptr->password, MAX_PASSWORD + 1); safe_strncpy(target->user.last, ptr->last, MAX_NAME + 1); safe_strncpy(target->user.id, ptr->id, MAX_ID + 1); target->user.is_new = ptr->is_new; target->user.group = ptr->group; target->user.add_state = ptr->add_state; target->user.central_state = ptr->central_state; target->next = 0; if(!course->add_head) { course->add_head = target; course->add_current = course->add_head; } else { course->add_current->next = target; course->add_current = course->add_current->next; } return course; } static void update_central_info(CREATED_COURSE_NODE *course, USER *user_ptr) { char realname[MAX_NAME + 1]; char course_name[MAX_PATH + 1]; snprintf(realname, MAX_NAME + 1, "%s %s", user_ptr->first, user_ptr->last); snprintf(course_name, MAX_PATH + 1, "%s/%s", course->course_subdir, course->course_dir); /*printf("

%s, %s, %d

", realname, course_name, user_ptr->add_state); */ if( (user_ptr->is_new == NEW) && (user_ptr->add_state == FINISH) && ( (course->exists == C_NEW) || (course->exists == UPDATE)) && (user_ptr->central_state != SUCCESS) ) { /* create_central_letter_dir is in shared_central_user.c */ /* sub_dir_name is in shared_authenticate.c */ if(!create_central_letter_dir (sub_dir_name(user_ptr->username))) user_ptr->central_state = C_ERROR; if(!create_central_user_dir(user_ptr->username)) /* shared_central_user.c */ user_ptr->central_state = C_ERROR; if(!write_central_id_file (realname,user_ptr->username, user_ptr->id)) /* shared_central_user.c */ user_ptr->central_state = C_ERROR; if(!write_central_password_file (user_ptr->username, user_ptr->password)) /* shared_central_user.c */ user_ptr->central_state = C_ERROR; if(!add_course(user_ptr->username,course_name)) /* shared_central_user.c */ user_ptr->central_state = C_ERROR; else { user_ptr->central_state = SUCCESS; user_ptr->is_new = NEW_1; /* central records already updated for this user */ } } else if((user_ptr->is_new != ERROR_UID || user_ptr->is_new != NEW) && user_ptr->add_state == FINISH) { if(course->exists == C_NEW || course->exists == UPDATE) { if(!add_course(user_ptr->username,course_name)) /* shared_central_user.c */ user_ptr->central_state = C_ERROR; else user_ptr->central_state = SUCCESS; } } else user_ptr->central_state = UN_CHANGE; } CREATED_COURSE_NODE * add_teachers(CREATED_COURSE_NODE *course) { USER_NODE *ptr; char file_path[MAX_PATH + 1]; if(course->exists == C_NEW) { for(ptr = course->head; ptr; ptr=ptr->next) /* for each teacher */ { if(create_one_person_dir (course->course_path, ptr->user.username)) ptr->user.add_state = FINISH; else ptr->user.add_state = FAILED; if(course->course_type != STANDALONE) { update_central_info(course, &(ptr->user)); course =add_user_to_course_list(course, &(ptr->user)); } } } else if(course->exists == UPDATE) { for(ptr = course->head; ptr; ptr =ptr->next) { snprintf(file_path, MAX_PATH + 1, "%s/%s/%s", course->course_path, PEOPLE_DIR, ptr->user.username); if(!file_exists(file_path)) { if(create_one_person_dir (course->course_path, ptr->user.username)) ptr->user.add_state = FINISH; else ptr->user.add_state = FAILED; if(course->course_type != STANDALONE) update_central_info(course, &(ptr->user)); } else ptr->user.add_state = USEREXISTS; if(course->course_type != STANDALONE) course =add_user_to_course_list(course, &(ptr->user)); } } return course; } int write_passwd_file(CREATED_COURSE_NODE *course, USER_NODE *head) { USER_NODE *ptr; char file_path[MAX_PATH + 1]; FILE *fp; if( (course->exists != C_NEW) && (course->exists != UPDATE) ) return 0; snprintf(file_path, MAX_PATH + 1, "%s/%s", course->course_path, PASSWORD_FNAME); fp = fopen(file_path, "a+"); if(!fp) return -1; get_exclusive_lock(fileno(fp),1); /* shared_lock.c */ for(ptr = head; ptr; ptr = ptr->next) { if( (course->course_type == STANDALONE) && ptr->user.add_state == FINISH) { fprintf(fp, "%s:x:%s %s:%s:anonymous:Z:%s\n", ptr->user.username, ptr->user.first, ptr->user.last, "faculty", ptr->user.id); write_password(ptr->user.username, ptr->user.password, course->course_path); /* shared_encrypt.c */ } else if(ptr->user.add_state == FINISH && ptr->user.central_state == SUCCESS) { fprintf(fp, "%s:x:%s %s:%s:anonymous:Z:%s\n", ptr->user.username, ptr->user.first, ptr->user.last, "faculty", ptr->user.id); } } release_lock(fileno(fp)); /* shared_lock.c */ fclose(fp); return 1; } CREATED_COURSE_NODE * find_this_course(CREATED_COURSE_NODE *course_head, const char *dir, const char *subdir) { CREATED_COURSE_NODE *ptr; for(ptr = course_head; ptr; ptr = ptr->next) { if(!strcmp(ptr->course_dir, dir) && !strcmp(ptr->course_subdir, subdir)) return ptr; } return 0; } int find_user_in_this_course(CREATED_COURSE_NODE *course, const char *username) { USER_NODE *ptr; for(ptr = course->add_head; ptr; ptr = ptr->next) { if(!strcmp(ptr->user.username, username)) return 1; } return 0; } CREATED_COURSE_NODE * add_students_to_courses(CREATED_COURSE_NODE *course_head, USER_GROUP_INFO_LIST *user_list) { CREATED_COURSE_NODE *course_ptr; USER_GROUP_INFO *user_ptr; char file_path[MAX_PATH + 1]; puts("

.

"); fflush(stdout); /* For each user. */ for(user_ptr = user_list->head; user_ptr; user_ptr = user_ptr->next) { puts("."); fflush(stdout); /* for each course the students has listed in the xml file */ for(user_ptr->current = user_ptr->head; user_ptr->current; user_ptr->current = user_ptr->current->next) { course_ptr = find_this_course(course_head, user_ptr->current->course_dir, user_ptr->current->course_subdir); if(course_ptr && (course_ptr->t_state == T_SUCCESS) && (course_ptr->course_type != TEMPLATE)) { snprintf(file_path, MAX_PATH + 1, "%s/%s/%s", course_ptr->course_path, PEOPLE_DIR, user_ptr->user.username); if(!file_exists(file_path)) { if(user_ptr->user.is_new != ERROR_UID) { if(create_one_person_dir(course_ptr->course_path, user_ptr->user.username)) user_ptr->user.add_state = FINISH; else user_ptr->user.add_state = FAILED; if(course_ptr->course_type != STANDALONE) update_central_info(course_ptr, &(user_ptr->user)); } } else { /*printf ("

We think %s in course %s exists.

", user_ptr->user.username, user_ptr->current->course_dir); */ user_ptr->user.add_state = USEREXISTS; /* but he may not be in the password file! */ } if(!find_user_in_this_course(course_ptr, user_ptr->user.username)) course_ptr = add_user_to_course_list(course_ptr, &(user_ptr->user)); } } } return course_head; } static P_NODE* build_password_list(const char *subdir, const char *dir) { char course_name[MAX_PATH + 1]; CONFIG_STRUCT conf; P_NODE *head = 0; snprintf(course_name, MAX_PATH + 1, "%s/%s", subdir, dir); if(read_configuration_file_ok(course_name,&conf)) head=build_option_person_list(&conf); return head; } static int listed_in_password(const char *username, P_NODE *person_head) { P_NODE *p_ptr; for(p_ptr = person_head; p_ptr; p_ptr=p_ptr->next) if(!strcmp(username,p_ptr->data.username)) return 1; return 0; } static void write_students_to_password_file(CREATED_COURSE_NODE *courses) { CREATED_COURSE_NODE *course_ptr; char file_path[MAX_PATH + 1]; FILE *fp; P_NODE *person_head; /* DFP 9/28/2005 Need this string so we can add the course for students who did work in a course, the dropped course, back into their course_list. */ char course_name[MAX_PATH + 1]; for(course_ptr = courses; course_ptr; course_ptr = course_ptr->next) { /* DFP 9/28/2005 Get the name of the course */ snprintf(course_name, MAX_PATH + 1, "%s/%s", course_ptr->course_subdir, course_ptr->course_dir); /* students are NOT added to course templates! */ if(course_ptr->course_type != TEMPLATE) { person_head = build_password_list( course_ptr->course_subdir, course_ptr->course_dir); snprintf(file_path, MAX_PATH + 1, "%s/%s", course_ptr->course_path,PASSWORD_FNAME); fp = fopen(file_path, "a"); if(!fp) { course_ptr->t_state = OPEN_ERR; break; } get_exclusive_lock(fileno(fp), 0); /* shared_lock.c */ for(course_ptr->add_current = course_ptr->add_head; course_ptr->add_current; course_ptr->add_current = course_ptr->add_current->next) { if( (course_ptr->add_current->user.group != FACULTY) && ( (course_ptr->add_current->user.add_state == FINISH) || ((course_ptr->add_current->user.add_state == USEREXISTS) && !listed_in_password(course_ptr->add_current->user.username,person_head)) ) ) { fprintf(fp, "%s:x:%s %s:%s:anonymous:A:%s\n", course_ptr->add_current->user.username, course_ptr->add_current->user.first, course_ptr->add_current->user.last, "student", course_ptr->add_current->user.id); if (course_ptr->add_current->user.add_state == USEREXISTS) { /* DFP 9/28/2005 With this type of student, the course was not being added to the course list. This should fix the problem. */ if(!add_course(course_ptr->add_current->user.username,course_name)) /* shared_central_user.c */ course_ptr->add_current->user.central_state = C_ERROR; } if(course_ptr->course_type == STANDALONE) { write_password(course_ptr->add_current->user.username, course_ptr->add_current->user.password, course_ptr->course_path); /* shared_encrypt.c */ } } } release_lock(fileno(fp)); /* shared_lock.c */ fclose(fp); free_option_person_list(person_head); /* shared_person_list.c */ } } /* for... */ } int check_course_type(CREATED_COURSE_NODE *course) { CONFIG_STRUCT conf; char file[MAX_PATH + 1]; snprintf(file, MAX_PATH + 1, "%s/%s", course->course_subdir, course->course_dir); read_configuration_file (file, &conf); if(conf.standalone && course->course_type == STANDALONE) return 1; else if((!conf.standalone) && course->course_type == CENTRAL) return 1; else return 0; } CREATED_COURSE_NODE * create_course_with_teachers(CREATED_COURSE_NODE *course_head) { CREATED_COURSE_NODE *ptr; CONFIG_STRUCT conf; for(ptr = course_head; ptr; ptr = ptr->next) { puts("."); fflush(stdout); if(ptr->course_type == TEMPLATE) { /* the first teacher's username is used as the course_subdir within the templates directory */ snprintf(ptr->course_subdir, MAX_COURSE_SUBDIR + 1, "T%s", ptr->head->user.username); *(ptr->course_subdir + MAX_COURSE_SUBDIR) = '\0'; snprintf(ptr->course_path, MAX_PATH + 1, "%s%s/%s", TEMPLATE_PATH, ptr->course_subdir, ptr->course_dir); } else snprintf(ptr->course_path, MAX_PATH + 1, "%s%s/%s", COURSE_PATH, ptr->course_subdir, ptr->course_dir); if(file_exists(ptr->course_path)) /* shared_file_util.c */ { if(ptr->course_type == TEMPLATE) ptr->exists = UPDATE; /* Course already exists */ else { if(check_course_type(ptr)) { ptr->exists = UPDATE; /* Course already exists */ ptr =add_teachers(ptr); } else ptr->exists = TYPE_CONFLICT; } } else { ptr->exists = C_NEW; /* new course */ if(create_course_dir(ptr)) { get_config_default_value(ptr, &conf); if(write_config_file(ptr->course_path, &conf)) { if(!create_inboxes_directory (ptr->course_path) ) ptr->exists = DIR_ERR; //can't create inbox, key, or people dirs if(!create_key_people(ptr->course_path)) ptr->exists = DIR_ERR; //can't create inbox, key, or people dirs else { if(conf.standalone && !write_logout_html(ptr->course_path)) ptr->exists = MSG_ERR; //can't write logout html file for standalone course or chat log if( !write_chat_message_log(ptr->course_path)) ptr->exists = MSG_ERR; // chat log else ptr =add_teachers(ptr); } } else ptr->exists = CONFIG_ERR; // can't write conf file } else { ptr->exists = COURSE_ERR; // can't create course dir } } ptr->t_state = write_passwd_file(ptr, ptr->head) == 1? T_SUCCESS : OPEN_ERR; } return course_head; } /* for debugging only */ void dump_xml_list(XML_COURSE_INFO_LIST *list) { int students = 0; send_content_type(); puts("
\n");
 for(list->current = list->head; list->current; list->current=list->current->next)
  {
   printf("\n\nCourse Code: %s\n", list->current->course_code);
   printf("Title: %s\n", list->current->title);
   printf("Semester: %s\n", list->current->semester);   
   printf("subdir: %s\n", list->current->subdir);   
   printf("dir_id: %s\n", list->current->dir_id);   
   
   for(list->current->user_current = list->current->user_head, students = 0; 
                   list->current->user_current;
                      list->current->user_current = list->current->user_current->next, students++)
                      
    {
     printf("real name: %s
\n", list->current->user_current->data.realname); printf("last name: %s
\n", list->current->user_current->data.lastname); printf("id number: %s
\n", list->current->user_current->data.id); } printf("Total users: %d

\n", students); } // puts(""); // exit(1); } /*** for debugging only ***/ void dump_user_list(USER_GROUP_INFO_LIST *user_list) { USER_GROUP_INFO *ptr; COURSE_GROUP *course_ptr; for(ptr=user_list->head; ptr; ptr = ptr->next) { printf("lastname= %s
\n", ptr->user.last); printf("id = %s
\n", ptr->user.id); for(course_ptr = ptr->head; course_ptr; course_ptr=course_ptr->next) { printf("subdir : %s
\n", course_ptr->course_subdir); printf("coursedir : %s
\n", course_ptr->course_dir); } } } int main() { char key[MAX_KEY + 1]; XML_COURSE_INFO_LIST *xml_info_list; /* shared_roster_xml_parser.h */ CREATED_COURSE_NODE *course_head = 0 ; USER_GROUP_INFO_LIST *user_list = 0; INDEX_USER_LIST *index_list = 0; char roster_path[MAX_PATH + 1]; /* full (relative) path to xml file */ cs_cgi_init(); /* shared_cs_util.c */ /* build a linked list of directory names assigned to each course by the user */ read_parameters( roster_path); /** NEXT COMMENTED OUT - no authentication is done in this version *** *** validate_super_key(key, &user); *** **/ /* parse the xml file into a linked list */ xml_info_list = parse_xml_file(roster_path); update_xml_info_list(xml_info_list); /* for this version, simply sets all courses to CENTRAL */ /* make sure the directory names provided are reasonable */ check_internal_course_names(xml_info_list); send_content_type(); print_heading(key); /* only one admin can create a course at a time */ get_new_course_lock(); /* shared_new_course_lock.c */ /* start list with normal and template courses ** and only teacher info */ course_head = build_normal_template_course_list(xml_info_list); if(course_head) { index_list = (INDEX_USER_LIST *)malloc(sizeof(INDEX_USER_LIST)); index_list->current =0; index_list->head = 0; index_list = build_index_user_list(index_list); /* shared_user_index.c */ index_list = derive_teacher_username_password(course_head,index_list); course_head = create_course_with_teachers(course_head); user_list = build_user_list(xml_info_list); /* list contains all students, with their courses */ if(user_list) { index_list=derive_student_username_password(user_list,index_list); course_head = add_students_to_courses(course_head, user_list); write_students_to_password_file(course_head); reindex_users("../users"); /* shared_user_index.c */ print_course_table(course_head, "Normal and Template Courses"); } } else printf("

%s

", "(No central courses)"); free_xml_data_list(xml_info_list); if(index_list) free_index_user_list(index_list); /* shared_user_index.c */ if(course_head) free_course_f_list(course_head); if(user_list) free_user_group_info_list(user_list); free_new_course_lock(); /* shared_new_course_lock.c */ unlink(roster_path); cs_cgi_destroy(); /* shared_cs_util.c */ return 0; }