#include #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_translate_url.h" #include "manhat-lib/shared_access.h" typedef struct chat_msg_node { long offset; time_t msg_time; char sender[40]; char *msg; struct chat_msg_node *next; } CHAT_MSG_NODE; typedef struct day_node { struct day_node *prev; time_t day; CHAT_MSG_NODE *msg_head; struct day_node *next; } DAY_NODE; DAY_NODE *head = 0; DAY_NODE *tail = 0; static void read_parameters (char *course, char *key, time_t * display_day) { char value[25]; cs_get_required_parameter ("crs", course, MAX_PATH); /* shared_cgi_util.c */ cs_get_required_parameter ("id", key, MAX_KEY); cs_get_optional_parameter ("day", value, 24); if(strlen(value)) *display_day = atol (value); else *display_day = 0; } static void read_news_filter (NEWS_FILTER * filter) { FILE *fp; char filter_path[MAX_PATH + 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) /* if read failed */ filter->oldest_first = 1; fclose (fp); } else { filter->oldest_first = 1; } } static void set_chat_commands (char *course, char *key, int can_write) { char url[MAX_PATH +1]; snprintf (url, MAX_PATH +1, "%s?crs=%s&id=%s", "main_menu", course, key); cs_set_value("main_menu_url", url); if(can_write) { snprintf (url, MAX_PATH +1, "%s?crs=%s&id=%s&join=yes", "chat_enter", course, key); cs_set_value("enter_chat_url", url); } } /* returns a time_t value which is 01:01.01 on the same date as 'a' */ static time_t normalized_time (time_t a) { struct tm a_tm; a_tm = *localtime (&a); a_tm.tm_hour = 1; a_tm.tm_min = 1; a_tm.tm_sec = 1; return (mktime (&a_tm)); } static void memory_error () { cs_critical_error (ERR_MALLOC_FAILED, ""); } static void add_to_list (time_t msg_time, char *sender, char *msg, long offset) { time_t day; DAY_NODE *day_ptr; CHAT_MSG_NODE *msg_ptr; int len; day = normalized_time (msg_time); /* search for node with correct day */ for (day_ptr = head; day_ptr && ((day_ptr->day) != day); day_ptr = day_ptr->next); if (!day_ptr) /* node not found, so add a new node */ { if (!head) { head = (DAY_NODE *) malloc (sizeof (DAY_NODE)); if (!head) memory_error (); head->next = (DAY_NODE *) 0; head->prev = (DAY_NODE *) 0; head->day = day; head->msg_head = (CHAT_MSG_NODE *) 0; day_ptr = head; } else { for (day_ptr = head; day_ptr->next; day_ptr = day_ptr->next); day_ptr->next = (DAY_NODE *) malloc (sizeof (DAY_NODE)); if (!day_ptr->next) memory_error (); day_ptr->next->prev = day_ptr; day_ptr = day_ptr->next; day_ptr->next = (DAY_NODE *) 0; day_ptr->msg_head = (CHAT_MSG_NODE *) 0; day_ptr->day = day; } } /* now add this message */ if (!(day_ptr->msg_head)) /* this is a newly created DAY_NODE */ { day_ptr->msg_head = (CHAT_MSG_NODE *) malloc (sizeof (CHAT_MSG_NODE)); if (!day_ptr->msg_head) memory_error (); msg_ptr = day_ptr->msg_head; } else { for (msg_ptr = day_ptr->msg_head; msg_ptr->next; msg_ptr = msg_ptr->next); msg_ptr->next = (CHAT_MSG_NODE *) malloc (sizeof (CHAT_MSG_NODE)); if (!msg_ptr->next) memory_error (); msg_ptr = msg_ptr->next; } msg_ptr->offset = offset; msg_ptr->msg_time = msg_time; strncpy (msg_ptr->sender, sender, 40); len = strlen (msg); msg_ptr->msg = (char *) malloc (len + 1); if (!msg_ptr->msg) memory_error (); strncpy (msg_ptr->msg, msg, len + 1); msg_ptr->next = (CHAT_MSG_NODE *) 0; } static void build_transcript_list (CONFIG_STRUCT *conf) { FILE *fp; char path[MAX_PATH + 1]; long offset = 0; /* track where we are in the file */ time_t msg_time; /* the CHAT_MSG_LOG has these data items in this order */ int deleted; char sender[40]; int msg_len; char *msg = (char *) 0; char *escaped_msg = (char *) 0; snprintf (path, MAX_PATH + 1, "%s/%s", conf->course_path, CHAT_MSG_LOG); fp = fopen (path, "r"); if (!fp) cs_critical_error (ERR_FOPEN_READ_FAILED, path); while (fread (&msg_time, sizeof (time_t), 1, fp) == 1) { /* was this message deleted by the teacher ? */ if (fread (&deleted, sizeof (int), 1, fp) != 1) cs_critical_error (ERR_READING_TRANSCRIPT_FILE, path); /* who typed the message (realname) **/ if (fread (sender, 40 * sizeof (char), 1, fp) != 1) cs_critical_error (ERR_READING_TRANSCRIPT_FILE, path); /* how many chars were typed - what is the length of the message? */ if (fread (&msg_len, sizeof (int), 1, fp) != 1) cs_critical_error (ERR_READING_TRANSCRIPT_FILE, path); /** allocate space for the 'raw' message **/ msg = (char *) malloc ((msg_len + 1) * sizeof (char)); /* read the raw message */ if ((int)fread (msg, sizeof (char), msg_len, fp) != msg_len) cs_critical_error (ERR_READING_TRANSCRIPT_FILE, path); msg[msg_len] = '\0'; /* need to escape html chars */ if(!deleted) { if(cgi_html_escape_strfunc(msg,&escaped_msg) == STATUS_OK) add_to_list (msg_time, sender, escaped_msg, offset); else add_to_list (msg_time, sender, msg, offset); /* should never happen ? */ } if(msg) { free (msg); msg = (char *) 0; } if(escaped_msg) { free(escaped_msg); escaped_msg = (char *) 0; } offset = ftell (fp); } fclose (fp); } static void free_transcript_list () { DAY_NODE *day_ptr; CHAT_MSG_NODE *msg_ptr; while (head) { day_ptr = head; while (day_ptr->msg_head) { msg_ptr = day_ptr->msg_head; day_ptr->msg_head = day_ptr->msg_head->next; free (msg_ptr); } free (day_ptr); head = head->next; } } static void set_one_day (DAY_NODE * day_ptr, char *course, char *key) { #define MAX_TMP_NAME 30 CHAT_MSG_NODE *msg_ptr; char timestring[MAX_TIMESTRING + 1]; static int day_count = 0; int count; char name[MAX_TMP_NAME]; char url[MAX_PATH +1]; strftime (timestring, MAX_TIMESTRING, LONG_DOW_DATE_FORMAT, localtime (&(day_ptr->day))); snprintf(name, MAX_TMP_NAME, "day.%d.date", day_count); cs_set_value(name, timestring); for (msg_ptr = day_ptr->msg_head, count = 1; msg_ptr; msg_ptr = msg_ptr->next, count++); count--; snprintf(name, MAX_TMP_NAME, "day.%d.msg_count", day_count); cs_set_int_value(name, count); snprintf(url, MAX_PATH +1, "%s?crs=%s&id=%s&day=%ld","chat_transcript", course, key, (long)day_ptr->day); snprintf(name, MAX_TMP_NAME, "day.%d.read_url", day_count); cs_set_value(name, url); day_count++; #undef MAX_TMP_NAME } static void set_list_data (NEWS_FILTER * filter, char *course, char *key, CONFIG_STRUCT *conf) { DAY_NODE *day_ptr; build_transcript_list (conf); if (!head) return; if (filter->oldest_first) { for (day_ptr = head; day_ptr; day_ptr = day_ptr->next) set_one_day (day_ptr, course, key); } else { for (day_ptr = head; day_ptr->next; day_ptr = day_ptr->next); for (; day_ptr; day_ptr = day_ptr->prev) set_one_day (day_ptr, course, key); } free_transcript_list (); } static void set_filter (char *course, char *key, int oldestfirst, int access) { char fromstring[MAX_PATH + 1]; char url[MAX_PATH + 1]; /* print "reverse order" button only if we're not capturing */ if(access != ACCESS_CAPTURE) { /* AMP_SUB, et. al. are #defined in shared_translate_url.h */ snprintf (fromstring, MAX_PATH + 1, "%s?crs%c%s%cid%c%s", "chat_transcript", EQUAL_SUB, course, AMP_SUB, EQUAL_SUB, key); snprintf (url, MAX_PATH +1, "%s?crs=%s&id=%s&grp=%d&cmd=%d&from=%s", "news_filter_set", course, key, CHATTER, oldestfirst? 1 : 0, fromstring); cs_set_value("sort_order_url", url); } } static void set_day_list (char *course, char *key, CONFIG_STRUCT *conf,int can_write) { NEWS_FILTER filter; if(conf->access == ACCESS_CAPTURE) /* oldest first if capturing */ filter.oldest_first = 1; else read_news_filter (&filter); set_chat_commands (course, key, can_write); set_filter (course, key, filter.oldest_first, conf->access); set_list_data (&filter, course, key, conf); } static void set_detailed_day (DAY_NODE * day_ptr, char *course, char *key, time_t display_day, SESSION *user, int can_write) { #define MAX_TMP_NAME 30 CHAT_MSG_NODE *msg_ptr; char timestring[MAX_TIMESTRING + 1]; int i =0; char name[MAX_TMP_NAME]; char url[MAX_PATH +1]; strftime (timestring, MAX_TIMESTRING, LONG_DOW_DATE_FORMAT, localtime (&(day_ptr->day))); cs_set_value("day_string", timestring); for (msg_ptr = day_ptr->msg_head, i = 0; msg_ptr; msg_ptr = msg_ptr->next, i++) { strftime (timestring, MAX_TIMESTRING, TIME_FORMAT, localtime (&(msg_ptr->msg_time))); snprintf(name, MAX_TMP_NAME, "msg.%d.time", i); cs_set_value(name, timestring); if ( (user->group == FACULTY) && can_write) { snprintf(name, MAX_TMP_NAME, "msg.%d.delete_url", i); snprintf(url, MAX_PATH +1, "%s?crs=%s&id=%s&loc=%ld&day=%ld", "chat_delete", course, key, msg_ptr->offset, (long)display_day); cs_set_value(name, url); } snprintf(name, MAX_TMP_NAME, "msg.%d.sender", i); cs_set_value(name, msg_ptr->sender); snprintf(name, MAX_TMP_NAME, "msg.%d.msg", i); cs_set_value(name, msg_ptr->msg); } #undef MAX_TMP_NAME } static void set_single_day (char *course, char *key, time_t display_day, CONFIG_STRUCT *conf, SESSION *user, int can_write) { DAY_NODE *day_ptr; char url[MAX_PATH +1]; build_transcript_list(conf); snprintf (url, MAX_PATH +1, "%s?crs=%s&id=%s","chat_transcript", course, key); cs_set_value("back_url", url); if ( (user->group == FACULTY) && can_write) cs_set_int_value("allow_deletions", 1); for (day_ptr = head; day_ptr; day_ptr = day_ptr->next) if (day_ptr->day == display_day) set_detailed_day (day_ptr, course, key, display_day, user, can_write); free_transcript_list (); } static void display_transcript (char *course, char *key, time_t display_day, CONFIG_STRUCT *conf, SESSION *user, int can_write) { cs_set_course_info(conf); cs_set_current_time(); if (display_day) { cs_set_int_value("single_day", 1); set_single_day (course, key, display_day, conf, user, can_write); } else { cs_set_int_value("single_day", 0); set_day_list (course, key, conf, can_write); } } int main (void) { char course[MAX_PATH + 1]; /* from crs=? command line */ char key[MAX_KEY + 1]; /* from id=? command line */ time_t display_day; /* from optional day=? command line */ SESSION user; CONFIG_STRUCT conf; /* the configuration read from config file */ int can_write; cs_cgi_init(); read_parameters (course, key, &display_day); read_configuration_file (course, &conf); /* shared_util.c */ validate_key (key, &user, &conf); /* shared_util.c */ can_write = has_write_permission(&user, &conf); set_discussion_names (conf.course_path, user.username, CHATTER); /* shared_news_util.c */ display_transcript (course, key, display_day, &conf, &user, can_write); cs_cgi_display("chat_transcript", 1); cs_cgi_destroy(); return 0; }