#include <stdio.h>
#include <string.h>
#include <unistd.h>		/* for unlink, mktmp ... */

#include <sys/types.h>
#include <sys/stat.h>
              

#include "global.h"
#include "custom.h"


#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_unread.h"
#include "manhat-lib/shared_authenticate.h"
#include "manhat-lib/shared_login.h"
#include "manhat-lib/shared_person_list.h"
#include "manhat-lib/shared_potato_util.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_news_util.h"  /* for remove_stale_symlinks() */
#include "manhat-lib/shared_server_locked.h"  
#include "manhat-lib/shared_strtrm.h"
#include "manhat-lib/shared_survey_deliver_list.h"
#include "manhat-lib/shared_file_util.h"
#include "manhat-lib/shared_access.h"
#include "manhat-lib/shared_central_log.h"
#include "manhat-lib/shared_time.h"
#include "survey.h"


typedef struct semester_sort_node
{
   char semester[MAX_SEMESTER + 1];
   int is_expanded;
   int is_unread_checked;        /* is this semester currently being checked for unread messages? */
   struct semester_sort_node  *next;
} SEMESTER_SORT_NODE;


typedef struct course_node
{
   CONFIG_STRUCT  conf;
   SESSION user_info;
   char crs[MAX_FILENAME + 1];
   struct course_node *next;
} COURSE_NODE;



/* for  survey */

static void
set_survey_data(const char *username, const char *key)
{
#define MAX_HDF_NAME 50

     char file[MAX_PATH +1];
     char file_base[MAX_PATH +1];
     SURVEY_DELIVER_INFO_LIST *head, *ptr;
     time_t now;
     int count = 0;

     char hdf_name[MAX_HDF_NAME + 1];
     char hdf_value[MAX_PATH + 1];

     now = time(NULL);

    /* sub_dir_name() is in shared_authenticate.c */
     snprintf(file_base, MAX_PATH +1,"../%s/%c/%s/%s",
	     USERS_DIR, sub_dir_name(username), username, SURVEY_DIR);
    
     snprintf(file, MAX_PATH +1,"%s/%s",
	     file_base, SURVEY_DELIVER_INFO_FNAME);
	     
     if(file_exists(file))    /* shared_file_util.c */
     {
	 head =build_survey_info_list(file);      /* shared_survey_deliver_list.c */
	 for(ptr = head; ptr; ptr = ptr->next)
	 {
	   if(now < ptr->record.record.unrelease_time )
	   {
	    if( now >= ptr->record.record.release_time)
	     {
	    
	       snprintf(hdf_name, MAX_HDF_NAME + 1, "survey.%d.url", count);
	       /* MODE_TAKE_MYMANHATTAN is #defined in survey.h */
	       snprintf(hdf_value, MAX_PATH + 1, "%s?id=%s&amp;count=%s&amp;admin=0&amp;mode=%d",
	                                            "survey_page",key, ptr->record.record.id, MODE_TAKE_MYMANHATTAN);

	       cs_set_value(hdf_name, hdf_value);     /* shared_cs_util.c */


	       snprintf(hdf_name, MAX_HDF_NAME + 1, "survey.%d.title", count);
	       cs_set_value(hdf_name, ptr->record.record.title);  /* shared_cs_util.c */
	      count++;
           }		 
	   }
	 }
	 free_survey_deliver_info_list(head);   /* shared_survey_deliver_list.c */
	 if(count ==0)
	    kill_directory(file_base);       	/* shared_file_util.c */
     }

#undef MAX_HDF_NAME
}


static void
read_parameters( char *key , char *host, int *log, char *expiration_date)
{
 char tmp[10];
  
   cs_get_required_parameter ("id", key, MAX_KEY);       /* shared_cs_util.c */
   cs_get_optional_parameter ("host", host, MAX_PATH);    /* shared_cs_util.c */
   cs_get_optional_parameter ("log", tmp, 9);    /* shared_cs_util.c */
   *log = atoi(tmp);
   cs_get_optional_parameter ("expire", expiration_date, MAX_PATH);    /* shared_cs_util.c */
}




static int
course_cmp(COURSE_NODE *a, COURSE_NODE *b, SEMESTER_SORT_NODE *sort_head)
{
 SEMESTER_SORT_NODE *sptr;
 int compare = 0;
  
 if(strcmp(a->conf.semester, b->conf.semester))  /* the courses are from different semesters.  which one appears first in  the sort list? */
  {
   for(sptr = sort_head; sptr; sptr=sptr->next)
    {
     if(!strcmp(a->conf.semester, sptr->semester))
      {
         compare = -1;   /* 'a' appeared first  on list */
         break;
      }   
     else
       if(!strcmp(b->conf.semester, sptr->semester))
        {
          compare = 1;    /* b appeared first on list */
          break;
        }  
    }
   if(compare == 0)  /* since the sort list is already guaranteed to contain all of the semesters */
       cs_critical_error(ERR_GENERAL_ERROR, "in course_cmp()");    /* shared_cs_util.c */
   }                 
 else  /* same semester, return comparison of courses */
   compare = strcmp(a->conf.course_no, b->conf.course_no);
   
 return compare;   
}       



static
COURSE_NODE *
sort_course_list(COURSE_NODE *head, SEMESTER_SORT_NODE *sort_head)
{
  COURSE_NODE *q, *tail, *p, *r;


  if (head != NULL)
    {
      tail = head;
      while (tail->next)
	{
	  q = tail->next;
	  if (course_cmp(q, head,sort_head) < 0)
	    {
	      tail->next = q->next;
	      q->next = head;
	      head = q;
	    }
	  else
	    {
	      r = head;
	      p = r->next;
	      while (course_cmp(q, p,sort_head) > 0)
		{
		  r = p;
		  p = r->next;
		}
	      if (q == p)
		tail = q;
	      else
		{
		  tail->next = q->next;
		  q->next = p;
		  r->next = q;
		}
	    }
	}
    }
    return  head;
}

        

static void course_login(const char *username, COURSE_NODE *course_ptr, char dirname)
{
 char keypath[MAX_PATH + 1];
 char modified_course[MAX_PATH + 1];
 FILE *fp;
 
 
 if(!get_user_info ( username, &(course_ptr->user_info), &(course_ptr->conf)))    /* shared_person_list.c */
     cs_critical_error(ERR_REQUEST_DENIED, "call to get_user_info() failed within course_login()");      /* shared_cs_util.c */
 
 log_this_access (username, course_ptr->conf.course_path,PEOPLE_DIR);            /* shared_login.c */
 
 remove_stale_symlinks (username, course_ptr->crs, course_ptr->conf.access);     /* shared_news_util.c */
 kill_hotpotato_directory(username, &(course_ptr->conf));                        /* shared_potato_util.c */
 
 /** write the course specific SESSION info for this user
 *** to a file named course.key in the user's central directory
 *** since course_ptr->crs contains a slash, we must first convert
 *** the slash to a dot
 **/
 
 course_slash_to_dot(course_ptr->crs, modified_course);   /* shared_util.c */

 snprintf(keypath, MAX_PATH + 1, "../%s/%c/%s/%s%s",
                            USERS_DIR, dirname, username,
                            			modified_course,KEY_FILE_EXTENSION);
 fp = fopen(keypath, "w");
 if(!fp)
       cs_critical_error(ERR_FOPEN_WRITE_FAILED, keypath);    /* shared_cs_util.c */
 if(fwrite(&(course_ptr->user_info), sizeof(SESSION), 1, fp) != 1)
       cs_critical_error(ERR_FWRITE_FAILED, keypath);         /* shared_cs_util.c */
 fclose(fp);
}



/** this function should probably be moved to 
*** shared_unread.c 
**/
static int
unread_msgs(SESSION *user, CONFIG_STRUCT *conf, int *unread_postoffice)
{
 time_t current_time;
 
 current_time = time(NULL);

 *unread_postoffice =  conf->postoffice &&  unread_mail (user->username, conf);


 /** unread_news() and unread_mail() are in shared_unread.c 
 *** Note that 'chat' is not checked, and should not be.
 *** There's currently no good way to let a user know there's someone
 *** in the chat room for a particular class
 **/


 
 return ( *unread_postoffice ||
          (conf->assignments && 
               unread_news (user, ASSIGNMENTS, conf, current_time)) ||   
          
          ( conf->lectures &&
                  unread_news (user, LECTURES, conf, current_time)) ||

          (conf->syllabus &&
                  unread_news (user, HANDOUTS_NOTICES, conf, current_time)) ||

          (conf->internet &&
               unread_news (user, INTERNET_RESOURCES, conf, current_time))  ||

          (conf->selftest &&
                  unread_news (user, SELFTEST, conf, current_time)) ||

          (conf->discussion && 
                  unread_news (user, CLASS_DISCUSSION, conf, current_time)) ||

          (conf->lounge &&
                  unread_news (user, STUDENT_LOUNGE, conf, current_time)) ||

          (conf->anonymous &&
                  unread_news (user, ANONYMOUS_DISCUSSION, conf, current_time)) ||

          (conf->team &&
              unread_news (user, TEAM_DISCUSSION, conf, current_time)) ||

          (conf->team_teach &&
               unread_news (user, TEAM_TEACH, conf, current_time)) ||
               
          (conf->surveys &&
               unread_news (user, SURVEYS, conf, current_time)) ||

          (conf->podcasts &&
               unread_news (user, PODCASTS, conf, current_time)) ||

          (conf->grades &&
             unread_grades (user->username, conf))  );
}



static int
check_unread_status(const char *semester, SEMESTER_SORT_NODE *semester_head)
{
 SEMESTER_SORT_NODE *sptr;
 
 for(sptr = semester_head; sptr; sptr=sptr->next)
  {
   if(!strcmp(semester, sptr->semester))
       return sptr->is_unread_checked;
  }

 return 0;
}

     




static 
int  read_key_info( const char *username, COURSE_NODE *ptr, char subdir)
{
  char key_file[MAX_PATH + 1];
  char modified_course[MAX_PATH + 1];
  FILE *fp;

  course_slash_to_dot(ptr->crs, modified_course);   /* shared_util.c */
  snprintf(key_file, MAX_PATH + 1, "../%s/%c/%s/%s%s",
                            USERS_DIR, subdir, username, modified_course, KEY_FILE_EXTENSION);
     
  fp = fopen(key_file, "r");
  if(!fp)
        return 0;   /* file doesn't exist - failed */
  if(fread(&(ptr->user_info), sizeof(SESSION),1, fp) != 1)
    {
     fclose(fp);
     return 0;
    }
  fclose(fp);
 return 1;   /* success */
}           






static void
set_one_course_data(COURSE_NODE *ptr, const char *server_key, SEMESTER_SORT_NODE *semester_head, const char *username)
{
#define MAX_HDF_NAME 80

 static char last_semester[MAX_SEMESTER + 1] = "";
 static int check_unread_messages = 0;
 static int semester_count = 0;
 static int course_count = 0;

 int classroom_is_locked;
 int unread_postoffice;
 int mods_enabled;
 int already_logged_in;
 static char base_hdf_name[MAX_HDF_NAME + 1] = "";
 char base_course_hdf_name[MAX_HDF_NAME + 1];
 char hdf_name[MAX_HDF_NAME + 1];
 char url[MAX_PATH + 1]; 
 char dirname;
                     
    if(strcmp(ptr->conf.semester,last_semester))   /* then set a new item.x.semester value */
     {
      strncpy(last_semester, ptr->conf.semester,MAX_SEMESTER + 1);
      snprintf(base_hdf_name, MAX_HDF_NAME + 1, "item.%d", semester_count);
      snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.semester", base_hdf_name);
      cs_set_value(hdf_name,ptr->conf.semester);   /* shared_cs_util.c */
     
      check_unread_messages = check_unread_status(ptr->conf.semester, semester_head);
      snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.unread_msgs_checked", base_hdf_name);
      cs_set_int_value(hdf_name,check_unread_messages);   /* shared_cs_util.c */
      
      semester_count++;
      course_count = 0;
    }
    
   snprintf(base_course_hdf_name, MAX_HDF_NAME + 1, "%s.course.%d", base_hdf_name, course_count);
   
   snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.course_no", base_course_hdf_name);
   cs_set_value(hdf_name, ptr->conf.course_no);   /* shared_cs_util.c */
   
   
   snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.title", base_course_hdf_name);
   cs_set_value(hdf_name, ptr->conf.title);  /* shared_cs_util.c */


   snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.instructor", base_course_hdf_name);
   cs_set_value(hdf_name, ptr->conf.instructor);   /* shared_cs_util.c */
   
   
   snprintf(url, MAX_PATH + 1, "%s?crs=%s&amp;id=%s", "main_menu", ptr->crs, server_key);
   snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.url", base_course_hdf_name);
   cs_set_value(hdf_name, url);   /* shared_cs_util.c */


   /** A classroom should be considered locked if
   *** it's actually locked, or if there are no modules enabled
   **/
   
   /* CLASSROOM_LOCKED is  #defined in global.h */   
   mods_enabled = modules_enabled(&(ptr->conf));   /* shared_news_util.c */
   classroom_is_locked = CLASSROOM_LOCKED(ptr->conf.misc )? 1 : mods_enabled? 0 : 1;


   snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.classroom_locked", base_course_hdf_name);   
   cs_set_int_value(hdf_name,classroom_is_locked);
   

   /* If a classroom is locked, we don't want to show any red start to STUDENTS,
   ** but we DO want to show FACULTY red stars, since faculty are not locked
   ** out of the classroom.
   ** If this classroom is locked, and this is FACULTY, then consider it unlocked
   ** when it comes to placing unread stars.
   */   
   already_logged_in = 0;
   if(classroom_is_locked && check_unread_messages && mods_enabled)
    {
     dirname = sub_dir_name(username);    /* shared_authenticate.c */ 
     already_logged_in = read_key_info(username, ptr, dirname);
     if(!already_logged_in)       
      {
        if(!get_user_info ( username, &(ptr->user_info), &(ptr->conf)))    /* shared_person_list.c */
                 cs_critical_error(ERR_REQUEST_DENIED, "call to set_one_course_data()");      /* shared_cs_util.c */
      }         
 
     if(ptr->user_info.group == FACULTY)  /* this is FACULTY and the classroom is locked, treat is if unlocked for next section */
              classroom_is_locked = 0;                   
    } 

    
   snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.unread", base_course_hdf_name);     
   if(!classroom_is_locked && check_unread_messages)
     {
       if(!already_logged_in)
        {
          dirname = sub_dir_name(username);    /* shared_authenticate.c */ 
          if(!read_key_info(username, ptr, dirname))
               course_login(username, ptr, dirname);
        }
        cs_set_int_value(hdf_name, 
                          unread_msgs(&(ptr->user_info),&(ptr->conf), &unread_postoffice) ?
                          1: 0);
        
        snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.unread_postoffice", base_course_hdf_name);     
        cs_set_int_value(hdf_name, unread_postoffice? 1:0);
     }
    else
     cs_set_int_value(hdf_name, 0);
                     


   snprintf(hdf_name, MAX_HDF_NAME + 1, "%s.access", base_course_hdf_name);   
   cs_set_int_value(hdf_name,ptr->conf.access );  /* shared_cs_util.c */

                        
   course_count++;

#undef MAX_HDF_NAME
}



static void
set_header_data (SESSION *user, char *host)
{
 char file_name[MAX_PATH +1];
 char login_time[MAX_TIMESTRING + 1] = "";
 char login_host[MAX_PATH + 1] = "";
 
 

 /* sub_dir_name() is in shared_authenticate.c */
 snprintf(file_name, MAX_PATH +1, "../users/%c/%s/%s",
		          sub_dir_name(user->username), user->username, LAST_LOGIN_FNAME);
 

 if(*host)
  {
     get_last_time_login(file_name, login_time, login_host );  /* shared_central_log.c */
     cs_set_current_time();                              	/* shared_cs_util.c */
     cs_set_value("remote_host", host);                         /* shared_cs_util.c */
     cs_set_value("last_login_time", login_time);		/* shared_cs_util.c */
     cs_set_value("last_login_host", login_host);     		/* shared_cs_util.c */
  }   
     
     
 cs_set_value("realname", user->realname);			/* shared_cs_util.c */
} 




static void
set_buttons_data(SESSION *user, const char *server_key)
{

 char hdf_value[MAX_PATH + 1];
 
 
 if(ALLOW_STUDENT_CHANGE_PASSWORD )  /* #defined in custom.h */
  {
    snprintf(hdf_value, MAX_PATH + 1, "%s?id=%s", "passwd_form", server_key);
    cs_set_value("password_url", hdf_value);  		/* shared_cs_util.c */
  }

 snprintf(hdf_value,MAX_PATH + 1, "%s?id=%s", "mymanhattan_logout", server_key);
 cs_set_value("logout_url", hdf_value); 		/* shared_cs_util.c */


 if(ENABLE_VIEW_EDIT_BIO_BUTTON)  /* #defined in custom.h */  
  {
   snprintf(hdf_value, MAX_PATH + 1, "%s?id=%s&amp;user=%s", "bio_view", server_key, user->username);
   cs_set_value("bio_url", hdf_value);			/* shared_cs_util.c */
  }
  
}









static
COURSE_NODE * build_users_course_list(const char *username)
{
 COURSE_NODE *head = 0;
 COURSE_NODE *ptr, *current = 0;
 char course_file[MAX_PATH + 1];
 FILE *fp;
 char course[MAX_FILENAME + 1];
 int fd;
 char dirname;
 int can_view, can_write;
 
 dirname = sub_dir_name(username);    /* shared_authenticate.c */
 
 snprintf(course_file, MAX_PATH + 1, "../%s/%c/%s/%s",
                            USERS_DIR, dirname, username,COURSE_LIST_FNAME);

                            
 fp = fopen(course_file, "r"); 
 if(!fp)
   cs_critical_error(ERR_COURSE_LIST_OPEN_FAILED,course_file);

 fd = fileno(fp);
 get_shared_lock(fd, 1);   /* shared_lock.c */
 
 while(fgets(course,MAX_FILENAME + 1,fp))
  {
    strtrm(course);  					/* shared_strtrm.c */
    ptr = (COURSE_NODE *)malloc(sizeof(COURSE_NODE));
    if(!ptr)
        cs_critical_error(ERR_MALLOC_FAILED, "in build_users_course_list()" );
    
    strncpy(ptr->crs,course,MAX_FILENAME + 1);
    if(read_configuration_file_ok(ptr->crs, &(ptr->conf)))   /* shared_util.c */
    {
      get_username_access_rights(username, &(ptr->conf), &can_view, &can_write);   /* shared_access.c */
      if(can_view)
       {
         if(!head)
            head = ptr;
          else
            current->next = ptr;
          current = ptr;
          current->next = 0;
       }   
    }  
   }
  release_lock(fd);     /* shared_lock.c */
  fclose(fp);

 return head;
}










static void
free_course_list(COURSE_NODE *head)
{
   COURSE_NODE *ptr;
   while (head)
    {
      ptr = head->next;
      free (head);
      head = ptr;
    }
}




static void
free_semester_sort_list(SEMESTER_SORT_NODE *head)
{
   SEMESTER_SORT_NODE  *ptr;
   while (head)
    {
      ptr = head->next;
      free (head);
      head = ptr;
    }
}





/* removes items from sort_head list that do not apear in course_head_list
*/
static
SEMESTER_SORT_NODE *remove_removed_courses(SEMESTER_SORT_NODE *sort_head, COURSE_NODE *course_head, int *changed)
{
 SEMESTER_SORT_NODE *sptr, *sptr2;
 COURSE_NODE *cptr;
 int found;

 if(sort_head)
  {
    sptr=sort_head;
    do
    {
      for(cptr = course_head, found = 0; !found && cptr; cptr=cptr->next)  /* for each course in the list */
           found = !strcmp(sptr->semester, cptr->conf.semester);

      if(!found)  /* then remove it from the sort list */
       {
         *changed = 1;
         if(sptr == sort_head)
           {
             sort_head = sort_head->next;
             free(sptr);
             sptr = sort_head;   /* sptr points to next one to be processed */
           }
         else
           {
             for(sptr2=sort_head; sptr2 && sptr2->next != sptr; sptr2 = sptr2->next);
             if(!sptr2)
                 cs_critical_error(ERR_GENERAL_ERROR, "in remove_removed_courses()");   /* shared_cs_util.c */
             sptr2->next = sptr->next;
             free(sptr);
             sptr= sptr2->next;   /* sptr points to next node to be processed */
           }    
        }
       else 
          sptr=sptr->next;      /* sptr points to next node to be processed */
    } while (sptr);
  }    /* if (sort_head)  */
  
  return sort_head;
}




/** adds items from course list that aren't already in sort list
**/

static 
SEMESTER_SORT_NODE *add_new_courses(SEMESTER_SORT_NODE *sort_head, COURSE_NODE *course_head, int *changed)
{
COURSE_NODE *cptr;
SEMESTER_SORT_NODE *sptr, *new_node;
int found;

  for(cptr = course_head; cptr; cptr=cptr->next)
  {
    for(sptr=sort_head, found = 0; !found && sptr; sptr=sptr->next)
        found = !strcmp(sptr->semester, cptr->conf.semester);
    if(!found)   /* add the new item to the top of the list */
     {
      *changed = 1;
      new_node = (SEMESTER_SORT_NODE *) malloc(sizeof(SEMESTER_SORT_NODE));
      if(!new_node)
           cs_critical_error(ERR_MALLOC_FAILED, "in add_new_courses()");   /* shared_cs_util.c */
      strncpy(new_node->semester, cptr->conf.semester, MAX_SEMESTER + 1);
      new_node->is_expanded = 1;
      new_node->is_unread_checked = 0;    /* Don't check this for unread messages */
      if(!sort_head)
         {
          sort_head = new_node;
          sort_head->next = 0;
         }
       else
         {
          new_node->next = sort_head;
          sort_head = new_node;
         }
         
       
     } /* if (!found) */     
       
    } /* for cptr= */                    

return sort_head;
} 





/** removes duplicate entries from sort list.
*** This doesn't do a thorough job, but it shouldn't 
*** be necessary in the first place.
**/

static
SEMESTER_SORT_NODE *  remove_duplicate_entries(SEMESTER_SORT_NODE *sort_head, int *changed)
{
 SEMESTER_SORT_NODE *sptr, *sptr2;
 
  for(sptr=sort_head; sptr; sptr = sptr->next)
    if(sptr->next && !strcmp(sptr->semester, sptr->next->semester))
      {
       sptr2 = sptr->next;
       sptr->next = sptr->next->next;
       free(sptr2);
       *changed = 1;
      }
      
    return sort_head;  
}



/** Check/adjust the is_unread_checked setting.
***   - Only one semester can be checked for unread messages
***   - If this is a new_login, then the first semester is the 
***       one that's checked.
***   - If more than one is checked, then use the one at the top
***     of the list.
***   - If none are checked, use the first one in the list
**/


static 
SEMESTER_SORT_NODE *adjust_unread_checking(SEMESTER_SORT_NODE *sort_head, int new_login, int *changed)
{
 SEMESTER_SORT_NODE *sptr;

  if(sort_head)
   {
    if(new_login)  /* then only the first semester needs to be checked for new messages */
      {
        for(sptr=sort_head; sptr; sptr=sptr->next)     
         {
          if(sptr == sort_head)
            {
              if(sptr->is_unread_checked != 1)
               {
                  *changed = 1;
                  sptr->is_unread_checked = 1;
               }
            }
          else
            if(sptr->is_unread_checked != 0)
             {
               *changed = 1;
               sptr->is_unread_checked = 0;
             }
         }
       }
    else /* not a new login */
     {
        for(sptr=sort_head; sptr && !(sptr->is_unread_checked); sptr=sptr->next);   /* find first checked semester */   
        if(!sptr)   /* none checked, check first item */
         {
            *changed = 1;
            sort_head -> is_unread_checked = 1;
         }
        else   /* make sure remaining items are unchecked */
         {
           for(sptr=sptr->next; sptr; sptr = sptr->next)
             {
               if(sptr->is_unread_checked)
                 {
                   *changed = 1;
                   sptr->is_unread_checked = 0;
                 }
             }
         }  
      }      
  } /* if sort_head */       

 return sort_head;
}





static void
write_sort_list(SEMESTER_SORT_NODE *sort_head, const char *sort_path)
{
FILE *fp;
SEMESTER_SORT_NODE *sptr;

     fp = fopen(sort_path, "w");
     if(!fp)
      cs_critical_error(ERR_FOPEN_WRITE_FAILED, sort_path);   /* shared_cs_util.c */
     for(sptr= sort_head; sptr; sptr=sptr->next)
       fprintf(fp,"%s\n%d%s\n", sptr->semester, sptr->is_expanded?1:0,sptr->is_unread_checked?"*":"");
     fclose(fp);
}






/* compares the sort list to the course list
** remove items from sort list with semesters that do not appear in course list
** add items to sort list that appear in course list, but not in sort list
** if any changes are made, to sort list, rewrites the sort data file 
*/
static
SEMESTER_SORT_NODE *check_sort_file(SEMESTER_SORT_NODE *sort_head, COURSE_NODE *course_head, 
                                                              const char *sort_path, int new_login)
{
 int changed;
  
  
 changed = 0; 
 
 /* remove items from sort list that are no longer in course list */
 sort_head = remove_removed_courses(sort_head, course_head, &changed);
 
 /* add any new entries that are in course list but not in sort list */
 sort_head = add_new_courses(sort_head, course_head, &changed);

 /** remove any duplicate entries in the sort_list
 ** this should never happen, but it's worth a few cycles 
 */
 sort_head = remove_duplicate_entries(sort_head, &changed);
 

 /* only one semester can be checked for unread msgs = which one? */
 sort_head = adjust_unread_checking(sort_head, new_login, &changed);
 
 
 if(changed)
    write_sort_list(sort_head, sort_path);

  return (sort_head);
} 








static
SEMESTER_SORT_NODE *build_semester_sort_list(COURSE_NODE *course_head, const char *username, int new_login)
{
#define MAX_LINE 100

 char line[MAX_LINE + 1];
 char dirname;
 FILE *fp;
 char sort_file[MAX_PATH + 1];
 SEMESTER_SORT_NODE *sort_head = 0, *ptr=0;

 dirname = sub_dir_name(username);    /* shared_authenticate.c */
 
 snprintf(sort_file, MAX_PATH + 1, "../%s/%c/%s/%s",
                            USERS_DIR, dirname, username,SEMESTER_SORT_FNAME);

                            
 fp = fopen(sort_file, "r"); 
 if(fp)
  {
    while(fgets(line,MAX_LINE, fp))
     {
      strtrm(line);  /* shared_strtrm.c*/
      if(!sort_head)
       {
        sort_head = (SEMESTER_SORT_NODE *) malloc(sizeof(SEMESTER_SORT_NODE));
        if(!sort_head)
           cs_critical_error(ERR_MALLOC_FAILED, "in build_semester_sort_list()");
        ptr = sort_head;
       }
      else
       {
        ptr->next = (SEMESTER_SORT_NODE *) malloc(sizeof(SEMESTER_SORT_NODE));
        if(!ptr->next)
           cs_critical_error(ERR_MALLOC_FAILED, "in build_semester_sort_list()");     
        ptr = ptr->next;
       }
       strncpy(ptr->semester, line, MAX_SEMESTER + 1);

       /* next line contains info on whether or not this semester is 'expanded',
       ** and whether or not we are checking this semester for unread messages
       */
       if(fgets(line, MAX_LINE, fp))
        {
         ptr->is_expanded = (*line == '1');
         ptr->is_unread_checked = strchr(line, '*')?1:0;
        } 
       else
        {
         ptr->is_expanded = 1;
         ptr->is_unread_checked = 0;
        } 

       ptr->next = 0;  
     } /* while (fgets..) */     
  
  fclose(fp);
 } 
 
 sort_head = check_sort_file(sort_head, course_head, sort_file, new_login);
 
 return sort_head;

#undef MAX_LINE
}



#ifdef ENABLE_FACULTY_COURSE_REQUEST
static void set_add_course_url(SESSION *user, const char *key)
{
    char url[MAX_PATH +1];
    if(is_wnec_faculty(user->id) )   /* shared_util.c */
   {
        snprintf(url, MAX_PATH +1, "%s?id=%s", "admin_add_course_form", key);
	cs_set_value("wnec_course_request_url", url);
   } 
}
#endif     


   



static void
show_mymanhattan_page (SESSION *user, char *server_key, char *host)
{
 COURSE_NODE *course_head, *ptr;
 SEMESTER_SORT_NODE *semester_head, *sptr;
 int semester_count;
 int new_login;
 
 new_login = *host? 1: 0;


 /*** build a linked list of courses this user is a member of */
 course_head = build_users_course_list(user->username);
 
 /* read the semester_sort data file to see how the user wants the page arranged */
 semester_head = build_semester_sort_list(course_head,user->username, new_login);
 
 /* sort the course list based on the contents of the semester_sort datafile */
 course_head = sort_course_list(course_head, semester_head);
 
 
 /* how many semesters are there? */
 for(sptr=semester_head, semester_count = 0; sptr; sptr=sptr->next)
         semester_count++;
 cs_set_int_value("semester_count", semester_count);   /* shared_cs_util.c */
         
 cs_set_value("key", server_key);    /* shared_cs_util.c */

 set_header_data(user, host);

 set_survey_data(user->username, server_key);
 
 
 for(ptr = course_head; ptr; ptr = ptr->next)
       set_one_course_data( ptr, server_key, semester_head, user->username);
  
       
  
 free_course_list(course_head);
 free_semester_sort_list(semester_head);
 
 set_buttons_data(user, server_key);

#ifdef ENABLE_FACULTY_COURSE_REQUEST
  if(ENABLE_FACULTY_COURSE_REQUEST)
      set_add_course_url(user, server_key);
#endif 
   


}

int get_value(char *date, int start, int end)
{
    char temp[10];
    int i, len, index =0;
    
    len = strlen(date);

    for (i = start; i<=end && i<len; i++)
    {
        temp[index] = date[i];
        temp[index + 1] = '\0';
        index++;	
    }
    return atoi(temp); 
}

void  get_month_day_year_hour_min_sec(char *expiration_date, int *year, 
		    int *month, int *day, int *hour, int *min, int *sec)
{
	*year = get_value(expiration_date, 0, 3) - 1900;
	*month = get_value(expiration_date, 4, 5) -1;
	*day = get_value(expiration_date, 6, 7);
	*hour = get_value(expiration_date, 8, 9);
	*min = get_value(expiration_date, 10, 11);
	*sec = get_value(expiration_date, 12, 13);
}

void set_days_hours_mins(int diff)
{
      int value_of_day = 86400;
      int value_of_hour = 3600;
      int value_of_minute = 60;
      int days;
      int hours;
      int min;

     days = diff / value_of_day;
     hours = (diff % value_of_day) / value_of_hour;
     min = ((diff % value_of_day) % value_of_hour) / value_of_minute;
     
     cs_set_int_value("expire.days", days);     
     cs_set_int_value("expire.hours", hours);     
     cs_set_int_value("expire.minutes", min);     
}	


void set_left_days_hours_mins(char *expiration_date)
{
   int year, day, month, hour, min, sec;
   time_t now;
   time_t expire;
   
   now = time(NULL);
   strtrm(expiration_date);
   get_month_day_year_hour_min_sec(expiration_date, &year, &month, &day, &hour, &min, &sec);
   expire = to_time_t(month, day, year, hour, min, sec);

   if(now <= expire)
       set_days_hours_mins(expire - now); 
}

			    

int
main()
{
   char key [MAX_KEY + 1];
   char expiration_date [MAX_PATH + 1];
   char host[MAX_PATH + 1];
   int log;
   SESSION user;

   cs_cgi_init();    /* shared_cs_util.c */


   read_parameters(key, host, &log, expiration_date);
   validate_server_key(key,&user);   	/* shared_util.c */
   central_user_check_lockout(key);	/* shared_server_locked.c - server shut down? */

   if(log)
    {
       write_central_log(user.username, "Successful login", "(from authentication server)", 0); /* shared_central_log.c */
       snprintf(host, MAX_PATH, "%s", hdf_get_value(global_cgi->hdf,"CGI.RemoteHost","???"));
       cs_set_value("Query.host", host);
    }
   if(strlen(expiration_date) == 14)
   {
       set_left_days_hours_mins(expiration_date);
   }

       
   show_mymanhattan_page(&user,key, host);
   cs_load_lang("common");               /* shared_cs_util.c */   
   cs_cgi_display ("mymanhattan", 1);    /* shared_cs_util.c */
   cs_cgi_destroy();        		/* shared_cs_util.c */
   return 0;
}


