#include #include #include #include #include #include "global.h" #include "custom.h" #include "survey.h" #include "manhat-lib/shared_util.h" #include "manhat-lib/shared_msg_line.h" #include "manhat-lib/shared_potato_util.h" #include "manhat-lib/shared_lock.h" #include "manhat-lib/shared_translate_url.h" #include "manhat-lib/shared_strtrm.h" #include "manhat-lib/shared_access.h" #include "manhat-lib/shared_bio.h" #include "manhat-lib/shared_cs_util.h" #include "manhat-lib/shared_news_util.h" #include "manhat-lib/shared_survey_take_verify.h" #define NOTEAM '!' ATTACHMENT_NODE *attachment_head = 0; /* linked list of file info */ static void read_parameters (char *course, char *key, long *offset, char *topic_prog, int *grp, int *del_link, int *printer_friendly, int *fixed_font) { char tmp[MAX_FILENAME + 1]; cs_get_required_parameter ("crs", course, MAX_FILENAME); /* shared_cs_util.c */ cs_get_required_parameter ("id", key, MAX_KEY); /* shared_cs_util.c */ cs_get_required_parameter ("grp", tmp, MAX_FILENAME); /* shared_cs_util.c */ *grp = atoi (tmp); cs_get_required_parameter ("loc", tmp, MAX_FILENAME); /* shared_cs_util.c */ *offset = atol (tmp); snprintf (topic_prog, MAX_PATH + 1, "%s?crs=%s&id=%s&grp=%d", "news_topic", course, key, *grp); /* just check if the "del_link" parameter is there */ cs_get_optional_parameter ("del_link", tmp, 5); /* shared_cs_util.c */ *del_link = *tmp? 1: 0; /* provide printer-friendly version? */ cs_get_optional_parameter ("print", tmp, 5); /* shared_cs_util.c */ *printer_friendly = *tmp? 1: 0; /* provide preformatted text (instead of proportional) ? */ cs_get_optional_parameter ("fixed", tmp, 5); /* shared_cs_util.c */ *fixed_font = *tmp? (atoi(tmp)? 1: 0) : 0; } /* is this the teacher working with the team/teacher module? */ static void check_is_fac_team_teach (int grp, int *is_fac_teamteach, char *team, char *topic_prog, SESSION *user) { char tmp[5]; char teamstring[20]; if ((user->group == FACULTY) && (grp == TEAM_TEACH)) { *is_fac_teamteach = 1; cs_get_required_parameter ("team", tmp, 4); /* shared_cs_util.c */ *team = *tmp; snprintf (teamstring, 20, "&team=%c", *team); strcat (topic_prog, teamstring); } else { *is_fac_teamteach = 0; *team = NOTEAM; } } static MSG_TREE_NODE *find_last_node(MSG_TREE_NODE *root) { MSG_TREE_NODE *ptr; ptr = root; for(;;) { for(;ptr->next;ptr=ptr->next); if(!ptr->reply) return ptr; ptr=ptr->reply; } } static void get_next_msg_offset(MSG_TREE_NODE *msg_ptr, MSG_TREE_NODE *topic_ptr, int uses_topic, long *offset) { MSG_TREE_NODE *ptr,*last; int done; if(!uses_topic) *offset = topic_ptr->next ? topic_ptr->next->offset : -1; else if(msg_ptr->data.info.reply_to == -1) /* msg is the topic */ *offset = msg_ptr->reply? msg_ptr->reply->offset: - 1; else if(msg_ptr->reply) *offset = msg_ptr->reply->offset; else if(msg_ptr->next) *offset = msg_ptr->next->offset; /* message is a leaf */ else { for(ptr = msg_ptr->prev, last = msg_ptr, done = 0; !done; last=ptr, ptr=ptr->prev) { done = (ptr->data.info.reply_to == -1) || ((ptr->reply == last) && (ptr->next)); if(done) break; } if(ptr->data.info.reply_to == -1) *offset = -1; else *offset = ptr->next->offset; } } static void get_previous_msg_offset(MSG_TREE_NODE *msg_ptr, MSG_TREE_NODE *topic_ptr, int uses_topic, long *offset) { MSG_TREE_NODE *leaf; if(!uses_topic) *offset = topic_ptr->prev ? topic_ptr->prev->offset : -1; else if(msg_ptr->data.info.reply_to == -1) /* msg is the topic */ *offset = -1; else if( (msg_ptr->prev->reply == msg_ptr) || /* msg was first reply to another msg */ (!msg_ptr->prev->reply)) /* prev. message had no replies */ *offset = msg_ptr->prev->offset; else { leaf = find_last_node(msg_ptr->prev->reply); *offset = leaf->offset; } } static void get_topic_offsets( MSG_TREE_NODE *topic_ptr, int uses_topic, long *next_topic_offset, long *prev_topic_offset) { if (!uses_topic) { *next_topic_offset = -1; *prev_topic_offset = -1; } else { *prev_topic_offset = topic_ptr->prev ? topic_ptr->prev->offset : -1; *next_topic_offset = topic_ptr->next ? topic_ptr->next->offset : -1; } } static void get_offsets (MSG_TREE_NODE *msg_ptr, MSG_TREE_NODE *topic_ptr, int grp, long *next_msg_offset, long *prev_msg_offset, long *next_topic_offset, long *prev_topic_offset) { int uses_topic; uses_topic = group_uses_topic(grp); /* shared_news_util.c */ get_topic_offsets( topic_ptr, uses_topic, next_topic_offset, prev_topic_offset); get_previous_msg_offset(msg_ptr, topic_ptr, uses_topic, prev_msg_offset); get_next_msg_offset(msg_ptr, topic_ptr, uses_topic, next_msg_offset); } static void get_supporting_strings (const char *course, const char *key, int grp, long offset, int is_fac_teamteach, char team, char *fromstring, char *teamstring, const char *username, char *recipient_username) { /* AMP_SUB, et. al. are #defined in shared_translate_url.h */ snprintf (fromstring, MAX_PATH + 1, "from=%s%ccrs%c%s%cid%c%s%cgrp%c%d%cloc%c%ld", "news_read", QMARK_SUB, EQUAL_SUB, course, AMP_SUB, EQUAL_SUB, key, AMP_SUB, EQUAL_SUB, grp, AMP_SUB, EQUAL_SUB, offset); if (is_fac_teamteach) { snprintf (teamstring, 10, "%cteam%c%c", AMP_SUB, EQUAL_SUB, team); strcat (fromstring, teamstring); snprintf (teamstring, 10, "&team=%c", team); } else *teamstring = '\0'; /* only students should be running this program from within the ASSIGNMENTS module ** they need to send the message to themselves */ if(grp == ASSIGNMENTS) snprintf(recipient_username,MAX_USERNAME * 2, "&to=%s", username); else *recipient_username = '\0'; } static void set_command_urls (MSG_TREE_NODE *msg_ptr, MSG_TREE_NODE *topic_ptr, const char *course, const char *key, char *topic_prog, int grp, char team, int is_fac_teamteach, char *username, int can_write, int read_only) { char url[MAX_PATH + 1]; long next_msg_offset, prev_msg_offset, next_topic_offset, prev_topic_offset; char from_string[MAX_PATH + 1]; /* how to get back to THIS page... passed ** to the NEWS_NEW_MEMO program **/ char teamstring[10]; /* eg. "team=A" */ char to_string[MAX_USERNAME * 2]; /* eg "&to=snarmontas" */ get_offsets (msg_ptr, topic_ptr, grp, &next_msg_offset, &prev_msg_offset, &next_topic_offset, &prev_topic_offset); get_supporting_strings (course, key, grp, msg_ptr->offset, is_fac_teamteach, team, from_string, teamstring,username, to_string); /*** back button url*****/ snprintf (url,MAX_PATH + 1, "%s#%d", topic_prog, topic_ptr->data.info.msg_id); cs_set_value("back_button_url", url); /* shared_cs_util.h */ /* set a url for the main_menu */ snprintf (url, MAX_PATH + 1, "%s?crs=%s&id=%s", "main_menu", course, key); cs_set_value("main_menu_url", url); /* shared_cs_util.h */ /***** read previous msg ****/ if (prev_msg_offset != -1) { snprintf (url, MAX_PATH + 1, "%s?crs=%s&id=%s&loc=%ld&grp=%d%s", "news_read", course, key, prev_msg_offset, grp, teamstring); cs_set_value("prev_msg_url", url); /* shared_cs_util.h */ } /**** Add a New Message to this topic **/ if(can_write && group_uses_topic(grp) && !read_only) /* shared_news_util.c */ { snprintf (url, MAX_PATH + 1, "%s?crs=%s&id=%s&grp=%d&topic=%d%s%s&reply_to=%d&%s", "news_new_memo", course, key, grp, msg_ptr->data.info.topic_id, teamstring, to_string, msg_ptr->data.info.topic_id, from_string); cs_set_value("new_msg_url", url); /* shared_cs_util.h */ } /*** previous Topic ***/ if (!group_uses_topic (grp)) prev_topic_offset = -1; if (prev_topic_offset != -1) { snprintf (url, MAX_PATH + 1, "%s?crs=%s&id=%s&loc=%ld&grp=%d%s", "news_read", course, key, prev_topic_offset, grp, teamstring); cs_set_value("prev_topic_url", url); /* shared_news_util.h */ } /**** INFO ***/ if(grp == SURVEYS) snprintf (url, MAX_PATH + 1, "%s?crs=%s&id=%s&loc=%ld", "survey_info", course, key, msg_ptr->offset); else snprintf (url, MAX_PATH + 1, "%s?crs=%s&id=%s&loc=%ld&grp=%d", "news_info", course, key, msg_ptr->offset, grp); cs_set_value("info_url", url); /* shared_cs_util.h */ /**** read next ****/ if (next_msg_offset != -1) { snprintf (url, MAX_PATH + 1, "%s?crs=%s&id=%s&loc=%ld&grp=%d%s", "news_read", course, key, next_msg_offset, grp, teamstring); cs_set_value("next_msg_url", url); /* shared_cs_util.h */ } /*** reply to this message */ if(can_write && group_uses_topic(grp) && !read_only) { snprintf (url, MAX_PATH + 1, "%s?crs=%s&id=%s&grp=%d&topic=%d&loc=%ld%s%s&reply_to=%d&%s", "news_new_memo", course, key, grp, msg_ptr->data.info.topic_id, msg_ptr->offset, teamstring,to_string, msg_ptr->data.info.msg_id, from_string); cs_set_value("reply_url", url); /* shared_cs_util.h */ } /*** next Topic ***/ if (!group_uses_topic (grp)) next_topic_offset = -1; if (next_topic_offset != -1) { snprintf (url, MAX_PATH + 1, "%s?crs=%s&id=%s&loc=%ld&grp=%d%s", "news_read", course, key, next_topic_offset, grp, teamstring); cs_set_value("next_topic_url", url); /* shared_cs_util.h */ } /** set flag value and url */ cs_set_int_value("flagged", msg_ptr->user_data.flagged? 1:0); snprintf(url, MAX_PATH + 1, "%s?crs=%s&id=%s&grp=%d&msg_id=%d&%s", "news_flag", course, key, grp, msg_ptr->data.info.msg_id, from_string); cs_set_value("flag_url", url); } static void process_msg_node (MSG_TREE_NODE *msg_ptr, int *website_attached, CONFIG_STRUCT *conf, SESSION *user, int *attachments_locked, int grp) { FILE *fp; int fd; char infopath[MAX_PATH + 1]; int i; NEWS_INFO infofile_info; ATTACHMENT_NODE *aptr = 0; *attachments_locked = 0; if(!msg_ptr->user_data.time_read && (conf->access != ACCESS_CAPTURE)) /* if msg is unread and we're not in capture mode */ { if(IS_PROTECTED(msg_ptr->data.status) && msg_ptr->data.info.attachments) *attachments_locked = 1; else /* don't mark the message as read if this is a student reading ** a SURVEY message **/ if( (user->group == FACULTY) || (grp != SURVEYS)) mark_msg_as_read(&(msg_ptr->data),conf,user); /*shared_news_util.c */ } if( (grp == SURVEYS) && (user->group == STUDENT) && !msg_ptr ->user_data.time_read && is_unanswered_required_survey(conf, msg_ptr -> data.info.sender, msg_ptr->data.info_fname, user) ) /* shared_survey_take_verify.c */ cs_set_int_value("is_unanswered_required_survey", 1); /*** BUGFIX: Prior to version 3, a data variable was left uninitialized. **** The result was that when upgrading to version 3, all Internet Resources and **** Survey module messages would say "The attachments were locked, you unlocked them at:" *** which was a problem. Since Internet Resources and Surveys can NEVER have attachments, **** we'll deal with this bug by saying the attachments are not locked for these modules ***/ if( (grp == INTERNET_RESOURCES) || (grp == SURVEYS)) *attachments_locked = 0; /*** if there are attachments, read the info file to **** build the attachment list ***/ *website_attached = 0; /* assume this to start */ if(msg_ptr->data.info.attachments) { snprintf(infopath,MAX_PATH + 1,"%s%s/%s/%s/%s",conf->course_path,PEOPLE_DIR, msg_ptr->data.info.sender,discussion_fname,msg_ptr->data.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); /* shared_cs_util.c */ /* process attachments, build linked list */ *website_attached = (infofile_info.attachments == -1); if (*website_attached) infofile_info.attachments = 1; /* since we have to pick up the dirname */ 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, "process_msg_node()"); /* shared_cs_util.c */ aptr = attachment_head; } else { aptr->next = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE)); if (!aptr->next) cs_critical_error (ERR_MALLOC_FAILED, "process_msg_node()"); /* shared_cs_util.c */ aptr = aptr->next; } if(fread(&aptr->data,sizeof(ATTACHMENT),1,fp) != 1) cs_critical_error (ERR_FREAD_FAILED, infopath); /* shared_cs_util.c */ aptr->next = 0; } /* for .. */ release_lock(fd); /* shared_lock.c */ fclose (fp); } /* if attachments */ } static int teacher_hidden_setting(GROUP_TYPE group, CONFIG_STRUCT *conf) { FILE *fp; char filter_path[MAX_PATH + 1]; NEWS_FILTER filter; if(group != FACULTY) return 0; /* only teachers can see teacher hidden items */ if(conf->access == ACCESS_CAPTURE) /* show teacher hidden for teachers while in capture node */ return 1; snprintf (filter_path, MAX_PATH + 1, "%s/%s", discussion_path, NEWS_FILTER_FNAME); fp = fopen (filter_path, "r"); if (fp) { if(fread (&filter, sizeof (NEWS_FILTER), 1, fp) != 1) { fclose (fp); return 1; /* an error occurred, assume show_teacher_hidden is set */ } else { fclose(fp); return (filter.show_teacher_hidden); } } else return 1; } static void set_news_msg_values (const char *course, const char *key, long offset, char *topic_prog, int grp, char team, int is_fac_teamteach, CONFIG_STRUCT *conf, SESSION *user, int can_write, int printer_friendly) { MSG_TREE_NODE *msg_ptr; MSG_TREE_NODE *topic_ptr; int msg_count; int website_attached; int show_teacher_hidden; /* include msgs that are hidden by the teacher? */ int teacher_hidden; /* is THIS message hidden by the teacher? */ int read_only; /* did teacher mark this topic as read-only? */ int attachments_locked; /* did the teacher protect the attachments, and did the student not yet unlock them? */ time_t current_time; int topic_with_replies; /* is this a topic in a discussion that has replies? */ char url[MAX_PATH + 1]; char msg_path[MAX_PATH + 1]; char inet_url[MAX_URL + 1]; show_teacher_hidden = teacher_hidden_setting(user->group, conf); /* In the next function call, the parameter is 'show_teacher_hidden || printer_friendly' ** because... when the teacher is gathering ALL MESSAGES, even teacher hidden messages are ** shown. The PRINT feature must work from that page. */ build_one_topic_tree(user,grp,offset,&msg_ptr,&topic_ptr,&msg_count,1, show_teacher_hidden || printer_friendly, ¤t_time); /* shared_news_util.c */ if(!msg_ptr || !tree_head) cs_critical_error(ERR_MSG_NOT_FOUND, "display_news_msg()"); /* shared_cs_util.c */ teacher_hidden = is_teacher_hidden(topic_ptr->data.status, topic_ptr->data.release_time, topic_ptr->data.unrelease_time, current_time); /* shared_news_util.c */ read_only = BASIC_STATUS(topic_ptr->data.status) == READ_ONLY ? 1 : 0; /* see global.h */ process_msg_node (msg_ptr,&website_attached,conf,user, &attachments_locked, grp); /* shared_news_util.c */ set_news_msg_header_data (msg_ptr, "msg.", teacher_hidden, read_only, topic_ptr->data.info.subject); /* clear msg.time sent, which was set by the above call, if appropriate */ if ((grp == ANONYMOUS_DISCUSSION) && HIDE_ANONYMOUS_TIMES) /* see custom.h */ { cs_set_value("msg.time_sent", ""); /* shared_cs_util.h */ } /*** BUGFIX: Prior to version 3, a data variable was left uninitialized. **** The result was that when upgrading to version 3, all Internet Resources and **** Survey module messages would say "The attachments were locked, you unlocked them at:" *** which was a problem. Since Internet Resources and Surveys can NEVER have attachments, **** we'll deal with this bug by saying the user didn't unlock attachments for these modules ***/ if((grp == INTERNET_RESOURCES) || (grp == SURVEYS)) cs_set_int_value("msg.user_unlocked", 0); if(group_uses_topic(grp)) /* shared_news_util.c */ cs_set_value("group_uses_topic", "1"); /* shared_cs_util.h */ set_command_urls (msg_ptr, topic_ptr, course, key, topic_prog, grp, team, is_fac_teamteach,user->username, can_write, read_only && (user->group == STUDENT)); cs_set_bio_link(msg_ptr->data.info.sender, course, conf, key, grp, 0, "user", 30); /* shared_bio.c */ /* shared_news_util.c */ set_news_attachment_data (msg_ptr, topic_ptr->data.info.subject, course, key, grp, website_attached, "news_read", team, is_fac_teamteach, "", /* no assign_read_student value */ conf, attachments_locked, user->username, attachment_head, ""); /* no name prefix value */ /* handle body of message */ snprintf (msg_path, MAX_PATH + 1, "%s%s/%s/%s/%s", conf->course_path, PEOPLE_DIR, msg_ptr->data.info.sender, discussion_fname, msg_ptr->data.info.msg_fname); if(grp == INTERNET_RESOURCES) snprintf(inet_url, MAX_URL + 1, "%s?crs=%s&id=%s&loc=%ld&url=", "link_visit", course, key, offset); else *inet_url = '\0'; set_msg_body_data (msg_path, "", inet_url); /* shared_msg_line.c */ /* put in a list of things only the teacher can do */ if (user->group == FACULTY) { topic_with_replies = (msg_ptr == topic_ptr) && (msg_count > 1) && group_uses_topic(grp); if(topic_with_replies) cs_set_value("is_topic_with_replies", "1"); /* shared_cs_util.h */ if(can_write) cs_set_value("insert_kill_form", "1"); /* shared_cs_util.h */ if(website_attached) { snprintf(url, MAX_PATH + 1, "%s?crs=%s&id=%s&sender=%s&dir=%s&grp=%d", "web_page_zip", course, key, msg_ptr->data.info.sender, attachment_head->data.unique_fname, grp); cs_set_value("website_zip_download_url", url); /* shared_cs_util.h */ cs_set_value("website_dirname", attachment_head->data.unique_fname); /* shared_cs_util.h */ } } /* if(user.grp == FACULTY) */ free_msg_tree(tree_head); /*shared_news_util.c */ } static void free_attachment_list () { ATTACHMENT_NODE *aptr; while (attachment_head) { aptr = attachment_head; attachment_head = attachment_head->next; free (aptr); } } 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 */ char team; /* from team=? command line: used only for instructors in the team/teach module */ int del_link; /* from del_link=?command line do we need to rm a symbolic link ** to an attached web page? */ int printer_friendly; /* from print=? command line provide "printer friendly" version? */ int fixed_font; /* from fixed=? command line print body as fixed font? */ int is_fac_teamteach; /* is this user faculty using the team/teacher discussion module? */ char topic_prog[MAX_PATH + 1]; /* arguments to NEWS_TOPIC program */ SESSION user; CONFIG_STRUCT conf; /* the configuration read from config file */ int can_write; char teamstring[2]; cs_cgi_init(); /* shared_cs_util.c */ read_parameters (course, key, &offset, topic_prog, &grp, &del_link, &printer_friendly, &fixed_font); read_configuration_file (course, &conf); /* shared_util.c */ validate_key (key, &user, &conf); /* shared_util.c */ if(user.group == FACULTY) cs_set_value("is_faculty","1"); /* shared_cs_util.h */ if(conf.access == ACCESS_CAPTURE) { cs_set_value("capture_mode", "1"); /* shared_cs_util.h */ printer_friendly = 0; /* no printer option if capturing */ } cs_set_int_value("printer_friendly", printer_friendly); cs_set_int_value("fixed_font", fixed_font); can_write = has_write_permission(&user, &conf); /* shared_access.c */ if(can_write) cs_set_value("can_write", "1"); /* shared_cs_util.h */ cs_set_course_info(&conf); /* shared_cs_util.c */ cs_set_value("crs", course); /* shared_cs_util.h */ cs_set_int_value("grp", grp); /* shared_cs_util.h */ cs_set_value("key", key); /* shared_cs_util.h */ cs_set_value("username",user.username); /* shared_cs_util.h */ set_discussion_names (conf.course_path, user.username, grp); /* shared_news_util.c */ check_is_fac_team_teach (grp, &is_fac_teamteach, &team, topic_prog, &user); if(is_fac_teamteach) { user.team = team; cs_set_value("is_fac_teamteach", "1"); } if(team != NOTEAM) { *teamstring = team; *(teamstring + 1) = '\0'; cs_set_value("team", teamstring); /* shared_cs_util.h */ } set_news_msg_values (course, key, offset, topic_prog, grp, team, is_fac_teamteach, &conf, &user, can_write, printer_friendly); if (del_link) { remove_stale_symlinks (user.username, course, conf.access); /* shared_news_util.c */ if(grp == SELFTEST) kill_hotpotato_directory(user.username, &conf); /* shared_potato_util.c */ } free_attachment_list (); cs_cgi_display ("news_read", 1); /* shared_cs_util.c */ cs_cgi_destroy(); /* shared_cs_util.c */ return 0; }