#include #include #include #include #include #include #include "ClearSilver.h" #include "global.h" #include "custom.h" #include "survey.h" #include "manhat-lib/shared_util.h" #include "manhat-lib/shared_lock.h" #include "manhat-lib/shared_news_util.h" #include "manhat-lib/shared_file_util.h" #include "manhat-lib/shared_cs_util.h" #include "manhat-lib/shared_time.h" #include "manhat-lib/shared_copy_file.h" #include "manhat-lib/shared_person_list.h" #include "manhat-lib/shared_add_util.h" /** DEFAULT_SUBJECT will be used as the subject of the message *** sent, IF ONE IS NOT PROVIDED on the command line via subject=. *** Whether taken from the following #define, or from the command line, *** the subject will be truncated to MAX_SUBJECT (i.e. 45) characters **/ #define DEFAULT_SUBJECT "Course Evaluation" /** DEFAULT_MSG will be used as the body of the message *** sent, IF ONE IS NOT PROVIDED on the command line via msg= *** Whether from the following #define, or from the command line, *** DO NOT MAKE ANY LINE (much) LONGER THAN 70 characters. (End a line with *** a '\n\' ). Lines much longer than 70 characters will look UGLY when the message *** is read. Lines longer than 255 characters might cause the program to fail. **/ #define DEFAULT_MSG "Please take the attached course evaluation survey. Your responses\n"\ "are completely anonymous.\n\n"\ "(This survey was posted on behalf of your instructor by an automated\n"\ "process.)" /** These correspond to choices the teacher gets to make *** when delivering the survey. Probably should leave them *** alone. **/ #define ALLOW_STUDENTS_TO_VIEW_RESULTS 0 #define ALLOW_STUDENTS_TO_VIEW_SURVEY_AGAIN 0 #define STUDENT_ANSWER_REQUIRED 0 typedef struct sched_dates { time_t start_time; time_t end_time; int no_ending; } SCHED_DATES; static void read_parameters (char *course, /* the course to receive the survey */ time_t *result_time, /* when should teacher be able to see survey results? */ SCHED_DATES *sched_date, /* message will be displayed btwn start_date and end_date */ char *username, /* username of *TEACHER* who will be sending the survey */ char *survey_id, /* the internal ID of the survey */ char *msg_subject, /* the subject of the message (optional)*/ char **msg_body) /* the body of the message (optional) */ { char temp[80]; char *msg; cs_get_required_parameter ("crs", course, MAX_PATH); /* shared_cs_util.c */ cs_get_required_parameter ("survey_id", survey_id, MAX_PATH); cs_get_required_parameter ("username", username, MAX_USERNAME); cs_get_required_parameter ("result_time", temp, 79); *result_time = atol(temp); cs_get_required_parameter ("start_time", temp, 79); sched_date->start_time = atol(temp); cs_get_required_parameter ("end_time", temp, 79); sched_date->end_time = atol(temp); sched_date->no_ending = 0; /** the subject of the message is either from the command line *** or is DEFAULT_SUBJECT **/ cs_get_optional_parameter ("subject", temp, MAX_SUBJECT); /* shared_cs_util.c */ if(*temp) strncpy(msg_subject, temp, MAX_SUBJECT + 1); else strncpy(msg_subject, DEFAULT_SUBJECT, MAX_SUBJECT + 1); *(msg_subject + MAX_SUBJECT) = '\0'; /** the body of the message is either from the command line *** or is DEFAULT_MSG **/ msg = hdf_get_value (global_cgi->hdf, "Query.msg", ""); /* from ClearSilver library */ if(!msg || !(*msg)) { *msg_body = (char *) malloc((strlen(DEFAULT_MSG) + 1) * sizeof(char)); strncpy(*msg_body, DEFAULT_MSG, strlen(DEFAULT_MSG) + 1); } else { *msg_body = (char *) malloc((strlen(msg) + 1) * sizeof(char)); strncpy(*msg_body, msg, strlen(msg) + 1); } if(sched_date->start_time >= sched_date->end_time) cs_critical_error (ERR_END_BEFORE_START, ""); } static void write_msg_file (const char *msg, const char *unique_fname) { FILE *outfp; char filepath[MAX_PATH + 1]; snprintf (filepath, MAX_PATH + 1, "%s/%s", discussion_path, unique_fname); outfp = fopen (filepath, "w"); if (!outfp) cs_critical_error (ERR_FOPEN_WRITE_FAILED, filepath); fprintf (outfp, "%s", msg); fclose (outfp); } /* note that this creates the file */ static void get_unique_fname(char *unique_fname, const char *base_dir, const char *filetype, time_t *time_sent) { int fd; fd = get_unique_fd(base_dir, filetype, unique_fname, time_sent); /* shared_news_util.c */ close(fd); } static void process_message_file (const char *msg, char *msg_fname, time_t *time_sent) { get_unique_fname (msg_fname, discussion_path, "msg", time_sent); write_msg_file (msg, msg_fname); } static void complete_send (const char *msg_fname, P_NODE *user, time_t time_sent, const char *subject, const char *survey_dir, SCHED_DATES *mydate) { FILE *inbox_fp; int info_fd, inbox_fd; struct stat buf; NEWS_PERSON person; char info_fname[MAX_FILENAME + 1]; char inbox_fname[MAX_PATH + 1]; int msg_id; CENTRAL_INBOX_RECORD inbox_record, new_inbox_record; int inbox_record_size, attachment_size; ATTACHMENT attach; /* memorize the sizes of these data structures, ** so we don't have to repeatedly call sizeof() */ inbox_record_size = sizeof(CENTRAL_INBOX_RECORD); attachment_size = sizeof(ATTACHMENT); /* this user has already read this message ** set a NEWS_PERSON record to be written ** to the end of the new inf file. */ strncpy(person.username,user->data.username, MAX_USERNAME + 1); strncpy(person.realname, user->data.realname, MAX_NAME + 1); person.time_read = time_sent; person.user_unlocked = 0; /* set up as much of the new inbox record as we can before locking ** the inbox.dat file */ strncpy(new_inbox_record.info.msg_fname, msg_fname, MAX_FILENAME + 1); strncpy(new_inbox_record.info.sender, user->data.username, MAX_USERNAME + 1); strncpy(new_inbox_record.info.sender_realname, user->data.realname, MAX_NAME + 1); *(new_inbox_record.info.sender_realname + MAX_NAME) = '\0'; strncpy(new_inbox_record.info.receiver_username,"",MAX_USERNAME + 1); strncpy(new_inbox_record.info.subject, subject, MAX_SUBJECT + 1); new_inbox_record.info.time_sent = time_sent; new_inbox_record.info.attachments = -1; /* pretend it's a web site attached */ new_inbox_record.info.sender_team = user->data.team; new_inbox_record.info.reply_to = -1; /* get the name of the 'inf' file to do so, we need to open ** (create) the file */ info_fd = get_unique_fd (discussion_path,"inf", info_fname, &time_sent); /* don't bother to lock it, since no one knows it's there yet! */ strncpy(new_inbox_record.info_fname,info_fname,MAX_FILENAME + 1); SET_BASIC_STATUS(new_inbox_record.status, TIMED_RELEASE); /* SET_BASIC_STATUS #defined in global.h */ new_inbox_record.release_time = mydate->start_time; new_inbox_record.unrelease_time = mydate->end_time; /* now open and lock the inbox.dat file */ snprintf(inbox_fname,MAX_PATH + 1,"%s%s",central_discussion_path,INBOX_FNAME); inbox_fp = fopen(inbox_fname,"a+"); if(!inbox_fp) /* couldn't open or create inbox.dat */ cs_critical_error(ERR_FOPEN_WRITE_FAILED,inbox_fname); inbox_fd = fileno(inbox_fp); get_exclusive_lock(inbox_fd, 1); /* shared_lock.c */ /* get the next available msg_id, by incrementing the msg_id of the ** last msg in the inbox.dat file */ fstat(inbox_fd, &buf); if(!buf.st_size) /* this is the first message */ msg_id = 1; else { if(fseek(inbox_fp,-inbox_record_size,SEEK_END) == -1) cs_critical_error(ERR_FSEEK_FAILED, inbox_fname); if(fread( &inbox_record, inbox_record_size, 1, inbox_fp) != 1) cs_critical_error(ERR_FREAD_FAILED, inbox_fname); msg_id = inbox_record.info.msg_id + 1; } new_inbox_record.info.msg_id = msg_id; new_inbox_record.info.topic_id = msg_id; if(fseek(inbox_fp,0,SEEK_END) == -1) cs_critical_error(ERR_FSEEK_FAILED, inbox_fname); if(fwrite(&new_inbox_record, inbox_record_size,1,inbox_fp) != 1) cs_critical_error(ERR_FWRITE_FAILED, inbox_fname); /* now write the info file */ if (write (info_fd, &new_inbox_record.info, sizeof (NEWS_INFO)) != sizeof(NEWS_INFO)) cs_critical_error (ERR_FWRITE_FAILED, "on info file"); strncpy (attach.unique_fname, survey_dir, MAX_FILENAME + 1); strncpy (attach.orig_fname, SURVEY_XML_FNAME, MAX_FILENAME + 1); if (write (info_fd, &attach, attachment_size) != attachment_size) cs_critical_error (ERR_FWRITE_FAILED, "on info file"); if(write (info_fd,&person, sizeof(NEWS_PERSON))!= sizeof(NEWS_PERSON)) cs_critical_error (ERR_FWRITE_FAILED, "on info file"); close (info_fd); release_lock(inbox_fd); /* shared_lock.c */ fclose(inbox_fp); write_user_inbox_record(msg_id, time_sent); } /** The selected survey needs to be copied into a new *** directory under the sender's 'surveys' directory. *** A file named SURVEY_RELEASE_FNAME, which records the *** results time is also created within that new directory. *** Returns name of the newly created directory in *** 'directory_fname' ***/ static void create_survey_dir(const char *survey_id, time_t results_time, const char *username, time_t *time_sent, char *directory_fname, int student_count) { char src_path[MAX_PATH + 1]; char dest_path[MAX_PATH + 1]; char dest_dir[MAX_PATH + 1]; SURVEY_RELEASE_RECORD survey_info; FILE *fp; /*** create a randomly named directory to hold the survey files ***/ get_unique_fname (directory_fname, discussion_path, NORMAL_WEBSITE_DIR_PREFIX, time_sent); snprintf (dest_dir, MAX_PATH + 1, "%s%s", discussion_path, directory_fname); unlink(dest_dir); /* because get_unique_fname() created the file */ if(mkdir (dest_dir, 0777)) cs_critical_error(ERR_ERROR_CREATING_SURVEY_DIRECTORY, dest_dir); snprintf(dest_path,MAX_PATH + 1, "%s/%s", dest_dir, SURVEY_XML_FNAME); snprintf(src_path, MAX_PATH + 1, "../%s/_%s_/%s/%s", SURVEY_DIR, ADMIN_DIR,survey_id, SURVEY_XML_FNAME); copy_file(src_path, dest_path); /* shared_copy_file.c */ /* now write the info file, which records when the teacher can see the results */ survey_info.results_time = results_time; survey_info.student_view_results = ALLOW_STUDENTS_TO_VIEW_RESULTS; survey_info.student_view_again = ALLOW_STUDENTS_TO_VIEW_SURVEY_AGAIN; survey_info.student_answer_required =STUDENT_ANSWER_REQUIRED; survey_info.future2 = 0; snprintf(dest_path, MAX_PATH + 1, "%s/%s", dest_dir, SURVEY_RELEASE_FNAME); fp = fopen(dest_path, "w"); if(!fp) cs_critical_error(ERR_FOPEN_WRITE_FAILED, dest_path); if(fwrite(&survey_info, sizeof(SURVEY_RELEASE_RECORD), 1, fp) != 1) cs_critical_error(ERR_FWRITE_FAILED, dest_path); fclose(fp); /* create the results file, with a few opening tags */ snprintf(dest_path, MAX_PATH + 1, "%s/%s", dest_dir, SURVEY_RESULT_FNAME); fp = fopen(dest_path, "w"); if(!fp) cs_critical_error(ERR_FOPEN_WRITE_FAILED, dest_path); fprintf(fp, "\n"); fprintf(fp, "\n"); fprintf(fp, " %d\n", student_count); fprintf(fp, SURVEY_RESULT_END_TAG); fclose(fp); } static void get_teacher_info(CONFIG_STRUCT *conf, P_NODE *teacher, const char *username, int *number_students) { P_NODE *head, *ptr; int found = 0; char error[MAX_PATH +1]; head = build_option_person_list(conf); /* shared_person_list.c */ *number_students = count_students(head); /* shared_person_list.c */ for(ptr = head; ptr; ptr = ptr->next) { if(!strcmp(ptr->data.username, username) && ptr->data.group == FACULTY) { strncpy(teacher->data.username, ptr->data.username, MAX_USERNAME +1); strncpy(teacher->data.realname, ptr->data.realname, MAX_NAME +1); strncpy(teacher->data.id, ptr->data.id, MAX_ID +1); teacher->data.group = ptr->data.group; teacher->data.team = ptr->data.team; found = 1; } } free_option_person_list(head); /* shared_person_list.c */ if(!found) { snprintf(error, MAX_PATH + 1, "No teacher with username: %s in course %s\n", username, conf->course_path); cs_critical_error(ERR_GENERAL_ERROR, error); } } static void write_config_file(CONFIG_STRUCT *conf) { char fname[MAX_PATH +1]; FILE *fp; snprintf (fname, MAX_PATH + 1, "%s%s", conf->course_path, CONFIG_FNAME); fp = fopen (fname, "w"); if (!fp) cs_critical_error(ERR_FOPEN_WRITE_FAILED, fname); /* shared_cs_util.c */ get_exclusive_lock(fileno(fp), 1); if (fwrite (conf, sizeof (CONFIG_STRUCT), 1, fp) != 1) { release_lock(fileno(fp)); fclose (fp); cs_critical_error(ERR_FWRITE_FAILED, fname); /* shared_cs_util.c */ } release_lock(fileno(fp)); fclose (fp); } static void create_survey_dirs(CONFIG_STRUCT *conf) { P_NODE *head, *ptr; char survey_central_inbox_dir[MAX_PATH + 1]; snprintf (survey_central_inbox_dir, MAX_PATH + 1, "%s%s/%s/", conf->course_path, INBOXES_DIR, SURVEYS_DIR); if(!file_exists(survey_central_inbox_dir)) /* shared_file_util.c */ { mkdir(survey_central_inbox_dir,0770); /* create courses/sample/inboxes/surveys directory */ /* for each user, create courses/sample/people/username/surveys directory */ head = build_option_person_list(conf); /* shared_person_list.c */ for(ptr=head; ptr; ptr=ptr->next) create_module_directory(conf->course_path, ptr->data.username,SURVEYS_DIR); /* shared_add_util.c */ free_option_person_list(head); /* shared_person_list.c */ } } void print_success(const char *course, const char *subject, time_t *result_time, SCHED_DATES *sched_date, const char *username, const char *survey_id) { char time_string[MAX_TIMESTRING +1]; send_content_type(); printf("Course: %s\n", course); printf("Subject: %s\n", subject); printf("Teacher Username: %s\n", username); printf("Survey ID: %s\n", survey_id); strftime(time_string, MAX_TIMESTRING,DOW_DATE_TIME_FORMAT, localtime(result_time)); printf("Result_time: %s\n", time_string); strftime(time_string, MAX_TIMESTRING,DOW_DATE_TIME_FORMAT, localtime(&(sched_date->start_time))); printf("Start_time: %s\n", time_string); strftime(time_string, MAX_TIMESTRING,DOW_DATE_TIME_FORMAT, localtime(&(sched_date->end_time))); printf("End_time: %s\n\n\n", time_string); } static void check_survey(const char *survey_id) { char survey_path[MAX_PATH + 1]; char error_msg[500]; snprintf(survey_path, MAX_PATH + 1, "../%s/_%s_/%s/%s", SURVEY_DIR, ADMIN_DIR,survey_id, SURVEY_XML_FNAME); if(!file_exists(survey_path)) /* shared_file_util.c */ { snprintf(error_msg, 500, "A survey with id: %s doesn't exist. I looked in: %s", survey_id, survey_path); cs_critical_error(ERR_GENERAL_ERROR, error_msg); } } int main (void) { char course[MAX_PATH + 1]; /* from crs=? command line */ char subject[MAX_SUBJECT +1]; /* from (optional) subject= command line */ char *msg_body = 0; /* from (optional) msg = command line */ time_t result_time; /* from result_time= command line */ SCHED_DATES sched_date; /* the start_time and end_time for this message - ** derived from "start_time" and "end_time" command line ** parameters */ char username[MAX_USERNAME +1]; /* from username=? command line */ char survey_id[MAX_PATH + 1]; /* from survey_id = command line - name of survey to be copied */ char survey_dir[MAX_FILENAME + 1]; /* mkstemp() name given to the directory where the survey ** xml files are copied */ time_t time_sent; char msg_fname[MAX_FILENAME + 1]; /* mkstemp() name given to the msg file */ int number_students; /* how many students are in this class ? */ P_NODE teacher_node; CONFIG_STRUCT conf; /* the configuration read from config file */ cs_cgi_init(); /* shared_cs_util.c */ read_parameters (course,&result_time,&sched_date, username, survey_id, subject, &msg_body); read_configuration_file (course, &conf); /* shared_util.c */ set_discussion_names (conf.course_path, username, SURVEYS); /* shared_news_util.c */ get_teacher_info(&conf, &teacher_node, username, &number_students); /* is 'username' a teacher in course? */ check_survey(survey_id); /* does the survey exist? */ /* If the Surveys module for this course is not activated, ** then turn it on */ if(!conf.surveys) { conf.surveys = 2; /* 2 means activate the module, and allow users to hide/unhide messages */ write_config_file(&conf); create_survey_dirs(&conf); } time_sent = time (NULL); /* sets time_sent to current time */ process_message_file (msg_body, msg_fname, &time_sent); /* writes the contents of the message to a file */ create_survey_dir(survey_id, result_time, username, &time_sent, survey_dir, number_students); complete_send(msg_fname,&teacher_node,time_sent, subject, survey_dir, &sched_date); if(msg_body) free(msg_body); print_success(course, subject, &result_time, &sched_date, username, survey_id); cs_cgi_destroy(); /* shared_cs_util.c */ return 0; }