#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_person_list.h" #define MAX_LOGINS 10 /* how many logins do we want to list? */ typedef struct user_log_node { SESSION user; int unread_mail; /* number of unread messages */ int unread_discussion; int unread_lounge; int unread_team; int unread_handouts; int unread_inet; int unread_assignments; int unread_teamteach; int unread_grades; int unread_selftest; int unread_lectures; int unread_podcasts; int logins; /* total number of logins */ time_t login_time[MAX_LOGINS]; /* list of most recent logins */ struct user_log_node *next; } USER_LOG_NODE; static void read_parameters (char *course, char *key) { cs_get_required_parameter ("crs", course, MAX_PATH); cs_get_required_parameter ("id", key, MAX_ID); } USER_LOG_NODE * build_password_list( CONFIG_STRUCT *conf) { USER_LOG_NODE *ptr, *current_ptr = 0; USER_LOG_NODE *head = 0; P_NODE *pptr; P_NODE *p_head = build_option_person_list (conf); for(pptr = p_head; pptr; pptr=pptr->next) { ptr = ( USER_LOG_NODE *)malloc(sizeof(USER_LOG_NODE)); strncpy(ptr->user.username, pptr->data.username,MAX_USERNAME + 1); strncpy(ptr->user.realname, pptr->data.realname, MAX_NAME + 1); strncpy(ptr->user.id, pptr->data.id, MAX_ID + 1); ptr->user.group = pptr->data.group; ptr->user.team = pptr->data.team; ptr->next = 0; if(!head) head = ptr; else current_ptr->next = ptr; current_ptr = ptr; } free_option_person_list (p_head); return head; } /* returns the number of files ending in *.new in mail_dir */ int new_files (char *mail_dir) { DIR *dir_p; struct dirent *dir_entry_p; char *ptr; int count = 0; dir_p = opendir (mail_dir); if (!dir_p) cs_critical_error (ERR_OPENDIR_FAILED, ""); while (NULL != (dir_entry_p = readdir (dir_p))) { ptr = strstr (dir_entry_p->d_name, ".new"); if (ptr && *(ptr + 4) == '\0') /* i.e. if file ends in .new */ count++; } closedir (dir_p); return count; } /* returns number of unread Post Office messages recorded in the inbox */ int old_unread_mail (char *maildir) { char inbox_path[MAX_PATH + 1]; FILE *fp; INBOX_RECORD msg; int count = 0; snprintf (inbox_path, MAX_PATH + 1, "%s/%s", maildir, INBOX_FNAME); fp = fopen (inbox_path, "r"); if (fp) { while (fread (&msg, sizeof (INBOX_RECORD), 1, fp) == 1) if ( (msg.newfile_info.how_sent != DELETED) && !msg.time_read) count++; fclose (fp); } return count; } static int unread_grades (const char *username, CONFIG_STRUCT *conf) { FILE *fp; char fname[MAX_PATH + 1]; snprintf (fname, MAX_PATH + 1, "%speople/%s/grades/%s", conf->course_path, username, GRADES_NEW_FNAME); fp = fopen (fname, "r"); if (fp) { fclose (fp); return 1; } return 0; } /* returns number of unread news messages 'user' has in 'grp' */ int unread_news (SESSION *user, int grp, CONFIG_STRUCT *conf) { int unread_msgs = 0; int total_msgs = 0; int flagged_msgs = 0; /* needed for call to count_all_msgs() */ int show_all_teamteach; time_t current_time; /* needed for call to build_msg_tree */ show_all_teamteach = (user->group == FACULTY) && (grp == TEAM_TEACH); set_discussion_names (conf->course_path, user->username, grp); /* shared_news_util.c */ build_msg_tree(user,grp,show_all_teamteach,0,¤t_time); /*shared_news_util.c */ count_all_msgs(tree_head, &total_msgs, &unread_msgs, &flagged_msgs); /* shared_news_util.c */ free_msg_tree(tree_head); /* shared_news_util.c */ return unread_msgs; } /*** NOTE: Anonymous discussion is deliberately left out in this **** function because when someone posts a message it is **** automatically marked as read by them. If I post 10 **** Anonymous messages and I'm the only one who has 0 unread **** msgs, then teacher knows who posted the information... **/ void count_unread_msgs (CONFIG_STRUCT *conf, USER_LOG_NODE *head) { USER_LOG_NODE *person_ptr; for (person_ptr = head; person_ptr; person_ptr = person_ptr->next) { /* POST OFFICE */ if (!conf->postoffice) /* if module is not enabled, don't blame student for not reading posts! */ person_ptr->unread_mail = 0; else { set_discussion_names (conf->course_path, person_ptr->user.username, MAIL); /* shared_news_util.c */ person_ptr->unread_mail = old_unread_mail (discussion_path); person_ptr->unread_mail += new_files (discussion_path); } /* GRADES */ person_ptr->unread_grades = conf->grades ? unread_grades (person_ptr->user.username, conf) : 0; person_ptr->unread_discussion = conf->discussion ? unread_news (&(person_ptr->user), CLASS_DISCUSSION, conf) : 0; person_ptr->unread_lounge = conf->lounge ? unread_news (&(person_ptr->user), STUDENT_LOUNGE, conf) : 0; person_ptr->unread_team = conf->team ? unread_news (&(person_ptr->user), TEAM_DISCUSSION, conf) : 0; person_ptr->unread_handouts = conf->syllabus ? unread_news (&(person_ptr->user), HANDOUTS_NOTICES, conf) : 0; person_ptr->unread_inet = conf->internet ? unread_news (&(person_ptr->user), INTERNET_RESOURCES, conf) : 0; person_ptr->unread_assignments = conf->assignments ? unread_news (&(person_ptr->user), ASSIGNMENTS, conf) : 0; person_ptr->unread_teamteach = conf->team_teach ? unread_news (&(person_ptr->user), TEAM_TEACH, conf) : 0; person_ptr->unread_selftest = conf->selftest ? unread_news (&(person_ptr->user), SELFTEST, conf) : 0; person_ptr->unread_lectures = conf->lectures ? unread_news (&(person_ptr->user), LECTURES, conf) : 0; person_ptr->unread_podcasts = conf->podcasts ? unread_news (&(person_ptr->user), PODCASTS, conf) : 0; } } /* we'll just take the size of the access log file ** and divide it by the size of each record */ void count_total_logins (CONFIG_STRUCT *conf, USER_LOG_NODE *head) { USER_LOG_NODE *person_ptr; char logpath[MAX_PATH + 1]; FILE *fp; for (person_ptr = head; person_ptr; person_ptr = person_ptr->next) { snprintf (logpath, MAX_PATH + 1, "%speople/%s/%s", conf->course_path, person_ptr->user.username,ACCESS_LOG_FNAME); fp = fopen (logpath, "r"); if (!fp) person_ptr->logins = 0; else { fseek (fp, 0, SEEK_END); person_ptr->logins = ftell (fp) / sizeof (time_t); fclose (fp); } } } void record_recent_logins (CONFIG_STRUCT *conf, USER_LOG_NODE *head) { USER_LOG_NODE *person_ptr; char logpath[MAX_PATH + 1]; FILE *fp; int i; for (person_ptr = head; person_ptr; person_ptr = person_ptr->next) { for (i = 0; i < MAX_LOGINS; i++) person_ptr->login_time[i] = (time_t) 0; snprintf (logpath, MAX_PATH + 1, "%speople/%s/%s", conf->course_path, person_ptr->user.username, ACCESS_LOG_FNAME); fp = fopen (logpath, "r"); if (fp) { for (i = 0; i < MAX_LOGINS; i++) { if (fseek (fp, -((i + 1) * sizeof (time_t)), SEEK_END) == -1) break; if (fread (&(person_ptr->login_time[i]), sizeof (time_t), 1, fp) != 1) cs_critical_error (ERR_FREAD_FAILED, ""); } fclose (fp); } } } void free_person_list (USER_LOG_NODE *head) { USER_LOG_NODE *person_ptr; while (head) { person_ptr = head; head = head->next; free (person_ptr); } } void list_recent_logins (char *crs, char *id, CONFIG_STRUCT *conf) { #define MAX_TMP_NAME 30 USER_LOG_NODE *person_ptr; int i =0; char timestring[MAX_TIMESTRING + 1]; time_t now; int row; USER_LOG_NODE *head; char url[MAX_PATH + 1]; char name[MAX_TMP_NAME]; int count, j=0; /* get current server time */ now = time (NULL); strftime (timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime (&now)); head = build_password_list (conf); count_unread_msgs (conf, head); count_total_logins (conf, head); record_recent_logins (conf, head); cs_set_course_info(conf); cs_set_current_time(); snprintf(url, MAX_PATH +1, "%s?id=%s&crs=%s", "stats_menu", id, crs); cs_set_value("back_url", url); cs_set_int_value("max_logins", MAX_LOGINS); for (person_ptr = head, row = 1; person_ptr; person_ptr = person_ptr->next, row++) { snprintf(url, MAX_PATH +1, "%s?crs=%s&id=%s&username=%s&prog=%s","stats_ind_calendar",crs, id, person_ptr->user.username, "stats_recent_logins"); snprintf(name, MAX_TMP_NAME, "logins.%d.url", j); cs_set_value(name, url); snprintf(name, MAX_TMP_NAME, "logins.%d.id", j); cs_set_value(name, person_ptr->user.id); snprintf(name, MAX_TMP_NAME, "logins.%d.realname", j); cs_set_value(name, person_ptr->user.realname); snprintf(name, MAX_TMP_NAME, "logins.%d.unread_msg_count", j); count =person_ptr->unread_mail + person_ptr->unread_discussion + person_ptr->unread_lounge + person_ptr->unread_team + person_ptr->unread_handouts +person_ptr->unread_inet + person_ptr->unread_assignments + person_ptr->unread_teamteach + person_ptr->unread_grades +person_ptr->unread_selftest + person_ptr->unread_lectures + person_ptr->unread_podcasts; cs_set_int_value(name,count); snprintf(name, MAX_TMP_NAME, "logins.%d.logincount", j); cs_set_int_value(name,person_ptr->logins); for (i = 0; i < MAX_LOGINS; i++) { if (person_ptr->login_time[i]) { strftime (timestring, MAX_TIMESTRING, SPLIT_DOW_DATE_TIME_FORMAT, localtime (&person_ptr->login_time[i])); snprintf(name, MAX_TMP_NAME, "logins.%d.times.%d", j, i); cs_set_value(name, timestring); } } j++; } free_person_list (head); #undef MAX_TMP_NAME } int main () { char course[MAX_PATH + 1]; /* from crs=? command line */ char key[MAX_KEY + 1]; /* from id=? command line */ SESSION user; CONFIG_STRUCT conf; /* the configuration read from config file */ cs_cgi_init(); read_parameters (course, key); read_configuration_file (course, &conf); /* shared_util.c */ validate_key (key, &user, &conf); /* shared_util.c */ if ((user.group == FACULTY) || (user.group == ADMIN)) list_recent_logins (course, key, &conf); else cs_critical_error (ERR_REQUEST_DENIED, ""); cs_cgi_display("stats_recent_logins", 1); cs_cgi_destroy(); return 0; /* exit successfully */ }