#include #include /* for stat() */ #include #include #include #include "global.h" #include "custom.h" /* for date FORMAT strings */ #include "manhat-lib/shared_util.h" #include "manhat-lib/shared_cs_util.h" #include "manhat-lib/shared_news_util.h" #include "manhat-lib/shared_potato_util.h" /* for calculate_elapsed_time() */ #include "manhat-lib/shared_authenticate.h" /* for username_cmp() */ #include "manhat-lib/shared_person_list.h" /* for build_option_person_list() */ #include "manhat-lib/shared_lock.h" #include "manhat-lib/shared_podcast_util.h" typedef struct news_person_node { NEWS_PERSON data; struct news_person_node *next; } NEWS_PERSON_NODE; ATTACHMENT_NODE *attachment_head = 0; /* linked list of file info */ NEWS_PERSON_NODE *person_head = 0; /* linked list of recipients */ typedef struct history_node { HOTPOTATO_INFO_STRUCT data; struct history_node *next; } HISTORY_NODE; HISTORY_NODE *history_head = 0; /* linked list of selftest history info */ char inbox_path[MAX_PATH + 1]; /* full path to inbox.dat */ #define NOTEAM '!' static void read_parameters (char *course, char *key, long *offset, int *grp, int *send_selftest_csv) { char tmp[MAX_FILENAME + 1]; /* for holding the long value, length should be long enough, don't you think? */ cs_get_required_parameter ("crs", course, MAX_PATH); /* shared_cgi_util.c */ cs_get_required_parameter ("id", key, MAX_KEY); /* shared_cgi_util.c */ cs_get_required_parameter ("loc", tmp, MAX_FILENAME); /* shared_cgi_util.c */ *offset = atol (tmp); cs_get_required_parameter ("grp", tmp, MAX_FILENAME); /* shared_cgi_util.c */ *grp = atoi (tmp); if(*grp == SELFTEST) { cs_get_optional_parameter("get", tmp, 20); *send_selftest_csv = *tmp? 1 : 0; } else *send_selftest_csv = 0; } static void get_compare_data(USER_DATA *a, NEWS_PERSON *source) { char *lastname; strncpy(a->username, source->username, MAX_USERNAME +1); strncpy(a->realname, source->realname, MAX_NAME +1); lastname = get_lastname(source->realname); strncpy(a->lastname, lastname, MAX_NAME +1); if(lastname) free(lastname); } static void sort_news_person_list () { NEWS_PERSON_NODE *q, *tail, *p, *r; USER_DATA a, b, c; if (person_head != NULL) { tail = person_head; while (tail->next) { q = tail->next; get_compare_data(&a, &(q->data)); get_compare_data(&b, &(person_head->data)); if (username_cmp (&a, &b) < 0) /* shared_authenticate.c */ { tail->next = q->next; q->next = person_head; person_head = q; } else { r = person_head; p = r->next; get_compare_data(&a, &(q->data)); get_compare_data(&c, &(p->data)); while (username_cmp (&a, &c) > 0) /* shared_authenticate.c */ { r = p; p = r->next; get_compare_data(&c, &(p->data)); } if (q == p) tail = q; else { tail->next = q->next; q->next = p; r->next = q; } } } } } static void add_remaining_people(CONFIG_STRUCT *conf, int fac_only, int grp, char team) { int found; P_NODE *p_head, *p_ptr; NEWS_PERSON_NODE *person_ptr; p_head = build_option_person_list(conf); /* shared_person_list.c */ for(p_ptr = p_head; p_ptr; p_ptr=p_ptr->next) { if( (fac_only && p_ptr->data.group != FACULTY) || ( (grp == TEAM_DISCUSSION ) && (p_ptr->data.team != team) ) || ( (grp == TEAM_TEACH) && ((p_ptr->data.team != team) && (p_ptr->data.group != FACULTY))) ) continue; for(found=0, person_ptr=person_head; person_ptr && !found; person_ptr=person_ptr->next) found = !strcmp(person_ptr->data.username, p_ptr->data.username); if(!found) { if (!person_head) { person_head = (NEWS_PERSON_NODE *) malloc (sizeof (NEWS_PERSON_NODE)); if (!person_head) cs_critical_error (ERR_MALLOC_FAILED, ""); person_ptr = person_head; } else { for(person_ptr = person_head; person_ptr->next; person_ptr=person_ptr->next); person_ptr->next = (NEWS_PERSON_NODE *) malloc (sizeof (NEWS_PERSON_NODE)); if (!person_ptr->next) cs_critical_error (ERR_MALLOC_FAILED, ""); person_ptr = person_ptr->next; } strncpy(person_ptr->data.username, p_ptr->data.username, MAX_USERNAME + 1); strncpy(person_ptr->data.realname, p_ptr->data.realname, MAX_NAME + 1); person_ptr->data.time_read = 0; person_ptr->data.user_unlocked = 0; person_ptr->next = 0; } } free_option_person_list(p_head); /* shared_person_list.c */ } /* reads the inbox file and then the corresponding *.inf file, to get everything ** ready to be displayed **/ static void process_inbox_record (long offset, CENTRAL_INBOX_RECORD * inbox, NEWS_INFO * infofile_info, int grp, CONFIG_STRUCT *conf, SESSION *user) { FILE *fp; int fd; char infopath[MAX_PATH + 1]; int i; ATTACHMENT_NODE *aptr = 0; NEWS_PERSON_NODE *pptr = 0; NEWS_PERSON person; ATTACHMENT junker; char team; /* if this is TEAM or TEAM/TEACH modules, we must determine what team we're working with */ /** this sets global inbox_path string */ snprintf(inbox_path,MAX_PATH + 1,"%s%s",central_discussion_path,INBOX_FNAME); fp = fopen(inbox_path,"r"); if (!fp) cs_critical_error (ERR_FOPEN_READ_FAILED, inbox_path); fd = fileno(fp); get_shared_lock(fd, 1); /* shared_lock.c */ if (fseek (fp, offset, SEEK_SET)) cs_critical_error (ERR_FSEEK_FAILED, ""); if (fread (inbox, sizeof (CENTRAL_INBOX_RECORD), 1, fp) != 1) cs_critical_error (ERR_FREAD_FAILED, ""); release_lock(fd); /* shared_lock.c */ fclose (fp); /*** now read the info file */ snprintf (infopath, MAX_PATH + 1, "%s%s/%s/%s/%s", conf->course_path, PEOPLE_DIR, inbox->info.sender, discussion_fname, inbox->info_fname); fp = fopen (infopath, "r"); if (!fp) cs_critical_error (ERR_FOPEN_READ_FAILED, infopath); fd = fileno(fp); get_shared_lock(fd,1); /* shared_lock.c */ if (fread (infofile_info, sizeof (NEWS_INFO), 1, fp) != 1) cs_critical_error (ERR_FREAD_FAILED, infopath); /* process attachments, build linked list */ /* if a website was attached, we need to 'swallow' the attachment */ if (infofile_info->attachments == -1) if (fread (&junker, sizeof (ATTACHMENT), 1, fp) != 1) cs_critical_error (ERR_FREAD_FAILED, infopath); for (i = 1; i <= infofile_info->attachments; i++) { if (!attachment_head) { attachment_head = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE)); if (!attachment_head) cs_critical_error (ERR_MALLOC_FAILED, ""); aptr = attachment_head; } else { aptr->next = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE)); if (!aptr->next) cs_critical_error (ERR_MALLOC_FAILED, ""); aptr = aptr->next; } if (fread (&aptr->data, sizeof (ATTACHMENT), 1, fp) != 1) cs_critical_error (ERR_FREAD_FAILED, infopath); aptr->next = 0; } /** now read and build the people list */ while (fread (&person, sizeof (NEWS_PERSON), 1, fp) == 1) { if (!person_head) { person_head = (NEWS_PERSON_NODE *) malloc (sizeof (NEWS_PERSON_NODE)); if (!person_head) cs_critical_error (ERR_MALLOC_FAILED, ""); pptr = person_head; } else { pptr->next = (NEWS_PERSON_NODE *) malloc (sizeof (NEWS_PERSON_NODE)); if (!pptr->next) cs_critical_error (ERR_MALLOC_FAILED, ""); pptr = pptr->next; } pptr->data = person; pptr->next = 0; } release_lock(fd); /* shared_lock.c */ fclose (fp); if(grp == ASSIGNMENTS) { if(user->group == FACULTY) { if(inbox->info.reply_to == -1) add_remaining_people(conf,0, grp, NOTEAM); /* teachers see full list of students for the topic */ else add_remaining_people(conf,1, grp, NOTEAM); /* for other msgs, just add any missing faculty */ } else add_remaining_people(conf,1, grp, NOTEAM); /*for students, just add any missing faculty */ } else { team = ((grp == TEAM_DISCUSSION) || (grp == TEAM_TEACH))? inbox->info.sender_team : NOTEAM; add_remaining_people(conf,0, grp, team); /* if it's not assignments, then show full list of people */ } sort_news_person_list (); } static void set_attachment_info (const char *sender, int grp, int attachments, CONFIG_STRUCT *conf) { #define MAX_TMP_NAME 40 ATTACHMENT_NODE *aptr; char mail_dir[MAX_PATH + 1]; char file_path[MAX_PATH + 1]; struct stat file_stat; long filesize; char name[MAX_TMP_NAME + 1]; char sizestring[MAX_TMP_NAME + 1]; int i = 0; if (attachments == -1) cs_set_value("website_attached", "1"); else if (attachment_head) { snprintf (mail_dir, MAX_PATH + 1, "%s%s/%s/%s/", conf->course_path,PEOPLE_DIR, sender, discussion_fname); for (aptr = attachment_head, i = 0; aptr; aptr = aptr->next, i++) { /* special case, a null 'orig_fname' signals an HTML directory */ if (!*(aptr->data.orig_fname)) cs_set_int_value("web_page", 1); /* why is this here? */ else { snprintf(name, MAX_TMP_NAME, "attach.%d.orig_filename", i); cs_set_value(name, aptr->data.orig_fname); snprintf (file_path, MAX_PATH + 1, "%s%s", mail_dir, aptr->data.unique_fname); if(stat(file_path, &file_stat)) filesize = 0; else filesize = file_stat.st_size; snprintf(name, MAX_TMP_NAME, "attach.%d.size", i); snprintf(sizestring, MAX_TMP_NAME, "%ld", filesize); cs_set_value(name, sizestring); } } } #undef MAX_TMP_NAME } static void set_recipient_info ( char *sender, NEWS_INFO * infofile_info, int grp, SESSION *user, CONFIG_STRUCT *conf) { #define MAX_TMP_NAME 40 NEWS_PERSON_NODE *pptr; char timestring[MAX_TIMESTRING + 1]; char name[MAX_TMP_NAME]; int i =0; char *escaped_subject = 0; cs_set_value("subject", infofile_info->subject); cs_html_escape_string(infofile_info->subject, &escaped_subject); /* shared_cs_util.c */ if(escaped_subject) { cs_set_value("escaped_subject", (char *)escaped_subject); free(escaped_subject); } else /* unlikely but being paranoid here */ cs_set_value("escaped_subject", infofile_info->subject); cs_set_value("sender_realname", infofile_info->sender_realname); /*** time msg was posted **/ if ((grp == ANONYMOUS_DISCUSSION) && HIDE_ANONYMOUS_TIMES) /* see custom.h */ *timestring = '\0'; else strftime (timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime (&infofile_info->time_sent)); if(*timestring) cs_set_value("time_sent", timestring); /*** attachment info ***/ if(infofile_info->attachments || attachment_head) set_attachment_info(sender, grp, infofile_info->attachments, conf); /*** BEGIN COMMENTED OUT SECTION **** Here's the old pre-3.0 policy for controlling who can see **** when others have read the message if ( ( (user->group == FACULTY) && ( (grp == ASSIGNMENTS) || (grp == HANDOUTS_NOTICES) || (grp == INTERNET_RESOURCES) || (grp == SELFTEST) || (grp == LECTURES) ) ) || ((grp != ANONYMOUS_DISCUSSION) && !strcmp (user->username, sender)) ) **** END COMMENTED OUT SECTION ***/ /* Who gets to see when all others have read the message? ** Never show read times for ANONYMOUS DISCUSSION, ** otherwise show times to the teachers and the sender of the message ** This is a change from the previous policy starting with Manhattan 3 */ if ( (grp != ANONYMOUS_DISCUSSION) && ((user->group == FACULTY) || !strcmp (user->username, sender)) ) { cs_set_value("show_time_read", "1"); for (pptr = person_head, i = 0; pptr; pptr = pptr->next, i++) { /* see custom.h form DOW_DATE_TIME_FORMAT */ if (pptr->data.time_read) strftime (timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime (&pptr->data.time_read)); else *timestring = '\0'; snprintf(name, MAX_TMP_NAME, "recipient.%d.realname", i); cs_set_value(name, pptr->data.realname); if(*timestring) { snprintf(name, MAX_TMP_NAME, "recipient.%d.time_read", i); cs_set_value(name, timestring); } snprintf(name, MAX_TMP_NAME, "recipient.%d.unlocked_attachments", i); cs_set_int_value(name, pptr->data.user_unlocked); } } else /* this person is not the sender or a teacher - show the time they first opened this message */ { cs_set_value("show_time_read", "0"); for (pptr = person_head; pptr; pptr = pptr->next) if(!strcmp(pptr->data.username, user->username)) break; if(pptr) { if (pptr->data.time_read) strftime (timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime (&pptr->data.time_read)); else *timestring = '\0'; cs_set_value("recipient.0.realname", pptr->data.realname); if(*timestring) cs_set_value("recipient.0.time_read", timestring); cs_set_int_value("recipient.0.unlocked_attachments", pptr->data.user_unlocked); } } #undef MAX_TMP_NAME } static void free_history_list() { HISTORY_NODE *hptr; while(history_head) { hptr = history_head; history_head = history_head -> next; free(hptr); } } static int history_record_cmp(HISTORY_NODE *a, HISTORY_NODE *b) { int str_compare; str_compare = strcmp(a->data.system_fname, b->data.system_fname); if(str_compare) return(str_compare); /* the system filenames are the same */ return(a->data.time_submitted < b->data.time_submitted? -1 : (a->data.time_submitted > b->data.time_submitted)? +1 : 0 ); } static void sort_history_list () { HISTORY_NODE *q, *tail, *p, *r; if (history_head != NULL) { tail = history_head; while (tail->next) { q = tail->next; if (history_record_cmp (q, history_head) < 0) { tail->next = q->next; q->next = history_head; history_head = q; } else { r = history_head; p = r->next; while (history_record_cmp (q, p) > 0) { r = p; p = r->next; } if (q == p) tail = q; else { tail->next = q->next; q->next = p; r->next = q; } } } } } static void build_history_list(const char *info_fname, const char *sender, const char *quiz_taker_username, CONFIG_STRUCT *conf, SESSION *user) { FILE *fp; char history_path[MAX_PATH + 1]; HOTPOTATO_INFO_STRUCT data; HISTORY_NODE *hptr = 0; snprintf (history_path, MAX_PATH + 1, "%speople/%s/%s/%s", conf->course_path, quiz_taker_username, discussion_fname, HOTPOT_FNAME); fp = fopen(history_path,"r"); if(!fp) return; while(fread(&data,sizeof(HOTPOTATO_INFO_STRUCT),1,fp) == 1) { if( (!strcmp(data.sender,sender) && !strcmp(data.info_fname,info_fname)) && ((!data.hidden) || !strcmp (data.sender,user->username) ) ) { if(!history_head) { history_head = (HISTORY_NODE *) malloc(sizeof(HISTORY_NODE)); if(!history_head) cs_critical_error(ERR_MALLOC_FAILED,""); hptr = history_head; } else { hptr->next = (HISTORY_NODE *) malloc(sizeof(HISTORY_NODE)); if(!hptr->next) cs_critical_error(ERR_MALLOC_FAILED,""); hptr = hptr->next; } hptr->data = data; hptr->next = 0; } } fclose(fp); sort_history_list(); } static void set_history_list( char *realname) { #define MAX_TMP_NAME 80 HISTORY_NODE *hptr; char current_system_fname[MAX_FILENAME + 1] = ""; char timestring[MAX_TIMESTRING + 1]; int hours, minutes, seconds; char name[MAX_TMP_NAME + 1]; char name_prefix[MAX_TMP_NAME + 1]; int result_count = 0; int test_count = -1; static int user_count =0; char time[MAX_TIMESTRING +1]; if(history_head) { snprintf(name_prefix, MAX_TMP_NAME, "selftest_user.%d.realname",user_count); cs_set_value(name_prefix, realname); for(hptr = history_head; hptr; hptr = hptr -> next) { if(strcmp(hptr -> data.system_fname, current_system_fname)) { test_count++; snprintf(name, MAX_TMP_NAME, "selftest_user.%d.exercise.%d.title", user_count, test_count); cs_set_value(name, hptr->data.exercise); snprintf(name, MAX_TMP_NAME, "selftest_user.%d.exercise.%d.filename", user_count, test_count); cs_set_value(name, hptr->data.orig_fname); strncpy(current_system_fname,hptr->data.system_fname,MAX_FILENAME); result_count =0; } snprintf(name, MAX_TMP_NAME, "selftest_user.%d.exercise.%d.result.%d.time_submitted", user_count, test_count, result_count); strftime(timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime (&hptr->data.time_submitted)); cs_set_value(name, timestring); /* shared_potato_util.c */ calculate_elapsed_time(hptr->data.elapsed_time, &hours, &minutes, &seconds); snprintf(time, MAX_TIMESTRING +1, " %02d:%02d:%02d",hours,minutes,seconds); snprintf(name, MAX_TMP_NAME, "selftest_user.%d.exercise.%d.result.%d.elapsed_time", user_count, test_count,result_count); cs_set_value(name, time); snprintf(name, MAX_TMP_NAME, "selftest_user.%d.exercise.%d.result.%d.score", user_count, test_count, result_count); cs_set_value(name ,hptr->data.score); result_count++; } user_count++; } #undef MAX_TMP_NAME } static void send_csv_history_list(const char *realname, const char *username, CONFIG_STRUCT *conf) { HISTORY_NODE *hptr; char current_system_fname[MAX_FILENAME + 1] = ""; char datestring[MAX_TIMESTRING + 1]; char timestring[MAX_TIMESTRING + 1]; int hours, minutes, seconds; static int heading_sent = 0; if(history_head) { if(!heading_sent) { cs_load_lang("news_info"); /* shared_cs_util.c */ printf("\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n", hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_semester_label", "Semester"), hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_course_label", "Course"), hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_realname_label", "Real Name"), hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_username_label", "Manhattan Username"), hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_subject_label", "Message Subject"), hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_quiz_filename_label", "Quiz Filename"), hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_date_submitted_label", "Date Submitted"), hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_submitted_label", "Time Submitted"), hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_elapsed_label", "Elapsed Time"), hdf_get_value (global_cgi->hdf, "lang.news_info.selftest_score_label", "Score") ); heading_sent = 1; } for(hptr = history_head; hptr; hptr = hptr -> next) { if(strcmp(hptr -> data.system_fname, current_system_fname)) { strncpy(current_system_fname,hptr->data.system_fname,MAX_FILENAME); } strftime(datestring, MAX_TIMESTRING, DATE_FORMAT, localtime (&hptr->data.time_submitted)); strftime(timestring, MAX_TIMESTRING, TIME_FORMAT, localtime (&hptr->data.time_submitted)); /* shared_potato_util.c */ calculate_elapsed_time(hptr->data.elapsed_time, &hours, &minutes, &seconds); printf("\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",%s,%s,%02d:%02d:%02d,%s\n", conf->semester, conf->title, realname, username, hptr->data.exercise, hptr->data.orig_fname, datestring, timestring, hours, minutes, seconds, hptr->data.score); } } } static void show_one_selftest_history(const char *info_fname, const char *sender, const char *quiz_taker_username, char *realname, CONFIG_STRUCT *conf, SESSION *user, int sending_csv_file) { build_history_list(info_fname,sender,quiz_taker_username, conf, user); if(sending_csv_file) send_csv_history_list(realname, quiz_taker_username, conf); else set_history_list(realname); free_history_list(); } static void show_all_selftest_history(const char *info_fname, const char *sender, CONFIG_STRUCT *conf, SESSION *user, int sending_csv_file) { NEWS_PERSON_NODE *pptr; for (pptr = person_head; pptr; pptr = pptr->next) { if (pptr->data.time_read) show_one_selftest_history (info_fname, sender, pptr->data.username, pptr->data.realname, conf, user, sending_csv_file); } } static void set_selftest_info(const char *info_fname, const char *sender, CONFIG_STRUCT *conf, SESSION *user, int sending_csv_file) { if( (user->group == FACULTY)) show_all_selftest_history(info_fname, sender, conf, user, sending_csv_file); else show_one_selftest_history(info_fname,sender,user->username,user->realname, conf, user, sending_csv_file); } static void display_info (char *course, char *key, long offset, int grp, CONFIG_STRUCT *conf, SESSION *user) { CENTRAL_INBOX_RECORD inbox; NEWS_INFO infofile_info; char url[MAX_PATH + 1]; cs_set_course_info(conf); cs_set_current_time(); cs_set_int_value("grp", grp); process_inbox_record (offset, &inbox, &infofile_info, grp, conf, user); set_recipient_info (inbox.info.sender, &infofile_info, grp, user, conf); if(grp == SELFTEST) { if(user->group == FACULTY) { snprintf(url, MAX_PATH, "%s?crs=%s&id=%s&loc=%ld&grp=%d&get=csv", "news_info", course, key, offset, SELFTEST); cs_set_value("selftest_csv_url", url); } set_selftest_info(inbox.info_fname, inbox.info.sender, conf, user, 0); } if( (grp == PODCASTS) && (user->group == FACULTY) ) set_podcast_download_log_data(inbox.info_fname, conf); /* shared_podcast_util.c */ } static void free_news_person_list () { NEWS_PERSON_NODE *pptr; while (person_head) { pptr = person_head; person_head = person_head->next; free (pptr); } } static void free_news_attachment_list () { ATTACHMENT_NODE *pptr; while (attachment_head) { pptr = attachment_head; attachment_head = attachment_head->next; free (pptr); } } static void get_csv_filename(const char *course, const char *subject, char *filename) { strcpy(filename,"hotpot_info.csv"); } static void send_selftest_data_file (char *course, char *key, long offset, int grp, CONFIG_STRUCT *conf, SESSION *user) { CENTRAL_INBOX_RECORD inbox; NEWS_INFO infofile_info; char csv_filename[MAX_PATH + 1]; process_inbox_record (offset, &inbox, &infofile_info, grp, conf, user); get_csv_filename(course, infofile_info.subject, csv_filename); printf ("Content-type:application/octet-stream\n"); printf ("Content-disposition:attachment; filename=\"%s\"\n\n", csv_filename); show_all_selftest_history(inbox.info_fname, inbox.info.sender, conf, user, 1); } int main (void) { char course[MAX_PATH + 1]; /* from crs=? command line */ char key[MAX_KEY + 1]; /* from id=? command line */ long offset; /* from loc=? command line */ int grp; /* from grp=? command line */ int send_selftest_csv; /* from optional get=? cmd line - send Selftest Hot Potato ** quiz records as a CSV file? */ SESSION user; CONFIG_STRUCT conf; /* the configuration read from config file */ cs_cgi_init(); read_parameters (course, key, &offset, &grp, &send_selftest_csv); read_configuration_file (course, &conf); /* shared_util.c */ validate_key (key, &user, &conf); /* shared_util.c */ /** this program can't be used to view survey info *** Use survey_info.c instead **/ if(grp == SURVEYS) cs_critical_error(ERR_REQUEST_DENIED, ""); /** Only send a csv file of selftest results if this is a teacher running the program **/ if(send_selftest_csv && (user.group != FACULTY)) cs_critical_error(ERR_REQUEST_DENIED, ""); set_discussion_names (conf.course_path, user.username, grp); /* shared_news_util.c */ if(send_selftest_csv) { send_selftest_data_file(course,key, offset, grp, &conf, &user); free_news_person_list (); free_news_attachment_list (); } else { display_info (course, key, offset, grp, &conf, &user); free_news_person_list (); free_news_attachment_list (); cs_cgi_display("news_info", 1); } cs_cgi_destroy(); return 0; }