#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

#include "global.h"

#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_strtrm.h"    
#include "manhat-lib/shared_super_util.h"
#include "manhat-lib/shared_authenticate.h"
#include "manhat-lib/shared_server_string.h"
#include "manhat-lib/shared_person_list.h"
#include "manhat-lib/shared_roster_xml_parser.h"
#include "manhat-lib/shared_user_index.h"
#include "manhat-lib/shared_central_user.h"
#include "manhat-lib/shared_encrypt.h"
#include "manhat-lib/shared_update_user_info.h"
#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_super_menu.h"

#define MAX_ROSTER_LINE 200        /* max len of a line in the uploaded roster.txt file */


static void
read_parameters (char *subdir,  char *key, COURSE_TYPE *course_type)
{
  char tmp[21];
  char new_subdir[MAX_COURSE_SUBDIR +1];
  int i;


  cs_get_required_parameter ("id", key, MAX_KEY );
  cs_get_required_parameter ("subdir", subdir, MAX_COURSE_SUBDIR );
  cs_get_required_parameter ("type", tmp, 20);
  
  *course_type = CENTRAL;   /* to prevent uninitialized variable compiler warnings */
  i = atoi(tmp);
  switch(i)
  {
   case 0: 
	   *course_type = CENTRAL;
	   break;
   case 1:
	   *course_type = STANDALONE;
	   break;
   case 2:    
	   *course_type = TEMPLATE;
	   break;
   
   default: 
            cs_critical_error (ERR_INVALID_COURSE_TYPE, "Course type");
            break;
  }          
  if( !strcmp(subdir, "__new__") && *course_type != TEMPLATE)
  {
      cs_get_required_parameter("new_subdir", new_subdir, MAX_COURSE_SUBDIR );
      strncpy(subdir, new_subdir, MAX_COURSE_SUBDIR +1);
  }      
  
}


static void
write_cgi_file (FILE *infp, const char *filepath)
{

#define CHUNKSIZE 2048
  FILE  *outfp;
  int bytes_read;
  int bytes_left;
  char buffer[CHUNKSIZE];
  struct stat file_stat;

  if(fstat(fileno(infp), &file_stat))
     cs_critical_error(ERR_NO_FILE, "");
  if(file_stat.st_size ==0)
      cs_critical_error(ERR_NO_FILE, "");

  outfp = fopen (filepath, "w");
  if (!outfp)
    cs_critical_error (ERR_FOPEN_WRITE_FAILED, filepath);


  bytes_left = file_stat.st_size;
  bytes_read = CHUNKSIZE;
  while (bytes_left)
    {
      if (bytes_left < CHUNKSIZE)
	bytes_read = bytes_left;
      if (fread (buffer, bytes_read, 1, infp) != 1)
	cs_critical_error (ERR_FREAD_FAILED, "");
      bytes_left -= bytes_read;
      if (fwrite (buffer, bytes_read, 1, outfp) != 1)
	cs_critical_error(ERR_FWRITE_FAILED, filepath);

    }
  fclose (outfp);
#undef CHUNKSIZE

}

void
get_tmp_name(char *filepath)
{
   int fd;
   snprintf (filepath, MAX_PATH + 1, "%sCGI_DATA_XXXXXX", TMP_PATH);
   fd = mkstemp (filepath);
   if(fd == -1)
       cs_critical_error(ERR_MKSTEMP_FAILED, "");
   close(fd);
   change_mkstemp_permission(filepath);   /*shared_file_util.c */

}
 
 
/* writes the uploaded file to ROSTER_FNAME in course_dir 
** exits if the uploaded file is missing or empty
*/

static void
write_roster_file (FILE *fp, char *filepath)
{

   char file[MAX_PATH +1];
   get_tmp_name(filepath);
   snprintf(file, MAX_PATH +1, "%s.tar", filepath);
   write_cgi_file (fp, file);
}

void
make_work_space(const char *prefix)
{
    char command[MAX_PATH+1];
    unlink(prefix);
    if(mkdir(prefix,0770))
       cs_critical_error(ERR_MKDIR_FAILED, "");
    snprintf(command, MAX_PATH+1, "mv %s.tar %s", prefix, prefix);
    if(system(command) == -1)
       cs_critical_error(ERR_CANT_CREATE_WORK_SPACE,  "");

    snprintf(command, MAX_PATH +1, "cd %s ; tar -xzf *.tar ; cd ../../sbin", prefix);
    if(system(command) == -1)
       cs_critical_error(ERR_CANT_CREATE_WORK_SPACE,  "");
}
int
get_course_name(char *course_name, const char *prefix)
{
    DIR *dir_p, *test;
    struct dirent *dir_entry_p;
    char path[MAX_PATH +1];

    dir_p = opendir(prefix);
    if(!dir_p)
       cs_critical_error(ERR_CANT_FIND_WORK_SPACE,  prefix);

     while(NULL != (dir_entry_p = readdir(dir_p)))
     {
	 if(*(dir_entry_p->d_name) != '.' && !(strstr(dir_entry_p->d_name, ".tar")))
	 {
               snprintf(path, MAX_PATH +1, "%s/%s", prefix, dir_entry_p->d_name);
               test = opendir(path);
	       if(test)
	       {
		  strncpy(course_name, dir_entry_p->d_name, MAX_COURSE_ID +1);
		  closedir(test);
		  return 1; 
	       }
	 }
     }
     closedir(dir_p);
     course_name[0] = '\0';
     return 0;
}

void
get_conf(const char *prefix, const char *course_name, CONFIG_STRUCT *conf)
{
   FILE *fp;
   char conf_file[MAX_PATH +1];
   
   snprintf(conf_file, MAX_PATH +1, "%s/%s/%s", prefix, course_name, CONFIG_FNAME);
   fp = fopen(conf_file, "r");
   if(!fp)
      cs_critical_error(ERR_CANT_FIND_CONF , "");
   if(fread(conf, sizeof(CONFIG_STRUCT), 1, fp) != 1)
      cs_critical_error(ERR_FOPEN_READ_FAILED, "");
   fclose(fp);
}   
P_NODE *
build_person_list(const char *prefix, const char *course_name)
{
    char passwd_fname[MAX_PATH + 1];
    FILE *fp;
    int fd;
    char line[200];
    P_NODE *person, *pptr = 0;
    P_NODE *pers_head = 0;

    snprintf (passwd_fname, MAX_PATH + 1, "%s/%s/%s", prefix, course_name, PASSWORD_FNAME);
    fp = fopen (passwd_fname, "r");
    if (!fp)
        cs_critical_error (ERR_PASSWD_OPEN_FAILED, passwd_fname);

    fd = fileno(fp);
    get_shared_lock(fd, 1);                      /* shared_lock.c */

    while (fgets (line, 200, fp))
    {
       if (*line == '?')         /* password lines starting with a '?' are ignored */
          continue;
        person = (P_NODE *) malloc (sizeof (P_NODE));
       if (!person)
        cs_critical_error (ERR_MALLOC_FAILED, "build password");
      get_passwd_line_data(line, &(person->data));
      /* now add it to the linked list */
      person->data.team = 'A';   /* reassign team value to distinguish value 'O' from 'N' */
      person->next = 0;
      if (!pers_head)
        pers_head = person;
     else
        pptr->next = person;

     pptr = person;
   }

   release_lock(fd); /* shared_lock.c */
   fclose (fp);

    pers_head = sort_person_list (pers_head);
    return pers_head;

}

/*
void
do_untar(const char *prefix)
{
    pid_t pid, status;

    if ((pid = fork ()) < 0)
       critical_error (L47E_CANT_START_NEW_PROCESS, "");
    if(pid == 0)
    {
       execl(prefix, "tar", "-xzf", "*.tar", (char*)0);
       exit(88);
    }
    while (wait (&status) != pid);
   
}*/ 

XML_COURSE_INFO *
build_course_info(const char *prefix, const char *subdir)
{
   XML_COURSE_INFO *course_data;
   CONFIG_STRUCT conf;
   course_data = (XML_COURSE_INFO*) malloc(sizeof(XML_COURSE_INFO));
   if(!get_course_name(course_data->dir_id, prefix) )
      cs_critical_error(ERR_FOPEN_READ_FAILED, "");
   get_conf(prefix, course_data->dir_id, &conf);
   strncpy(course_data->subdir, subdir, MAX_COURSE_SUBDIR +1);
   strncpy(course_data->course_code, conf.course_no, MAX_COURSE_NO +1);
   strncpy(course_data->title, conf.title, MAX_COURSE_TITLE +1);
   strncpy(course_data->semester, conf.semester, MAX_SEMESTER +1);
   
   course_data->user_head = build_person_list(prefix, course_data->dir_id);
   return course_data;
}

int
find_user_in_index(INDEX_USER_LIST *index_list, P_NODE *user_data)
{
    INDEX_USER *ptr;
    
    for(ptr = index_list->head; ptr; ptr = ptr->next)
    {
	 if(strcmp(ptr->id, user_data->data.id) ==0)
	 {
              if(strcmp(ptr->username, user_data->data.username) != 0 )
	      {
		strncpy(user_data->data.alias, "1" , MAX_NAME +1);
                 return 2;
	      }
	      else
		 return 1;
	 }
    }
    return 0;
}


int
find_teacher_in_course( P_NODE *head, INDEX_USER_LIST *index_list)
{
    P_NODE *teacher;
    int find =-1;
   
    for(teacher = head; teacher; teacher= teacher->next)
    {
       if(teacher->data.group == FACULTY)
       {
	    find = find_user_in_index(index_list, teacher);
	    if(find != 2)
	       return 1;
       }
    }
    return find;
}
int
find_invalid_user(P_NODE * invalid_head, const char *username)
{
   P_NODE *ptr;
   for( ptr = invalid_head; ptr; ptr = ptr->next)
   {
      if(strcmp(ptr->data.username, username) ==0)
	      return 1;
   }
   return 0;
}
void
update_course_path(XML_COURSE_INFO *course_data, 
		COURSE_TYPE course_type,  const char *prefix, INDEX_USER_LIST *index_list)
{
    char path[MAX_PATH+1];
    struct stat buff;
    char command[MAX_PATH +1];
    char subdir_path[MAX_PATH +1];

    if(course_type == TEMPLATE)
      snprintf(subdir_path, MAX_PATH +1, "%s%s", TEMPLATE_PATH, course_data->subdir);
    else
      snprintf(subdir_path, MAX_PATH +1, "%s%s", COURSE_PATH, course_data->subdir);
    snprintf(path, MAX_PATH +1, "%s/%s", subdir_path, course_data->dir_id); 
    if(!stat(path, &buff))
      cs_critical_error(ERR_COURSE_EXIST_ERROR_MSG, path);
    if(find_teacher_in_course(course_data->user_head, index_list) != 1)
      cs_critical_error(ERR_NO_VALID_TEACHER, "can't find teacher");
    mkdir(subdir_path, 0770); 
    snprintf(command, MAX_PATH +1, "mv %s/%s %s", prefix, course_data->dir_id, path);
    system(command);
    snprintf(command, MAX_PATH +1, "rm -rf %s", prefix);
    system(command);

}
P_NODE *
find_teacher(P_NODE *head)
{
   P_NODE *ptr;
   for(ptr = head; ptr; ptr = ptr->next)
   {
       if(ptr->data.group == FACULTY)
	     return ptr;
   }
   return 0;
}
void
update_course_info(XML_COURSE_INFO *course_data, COURSE_TYPE course_type)
{
     P_NODE *ptr;
     char subdir[MAX_COURSE_SUBDIR +1];
     if(course_type == TEMPLATE)
     {
        ptr = find_teacher(course_data->user_head);
	if(ptr)
	{
	   snprintf(subdir, MAX_COURSE_SUBDIR +1, "T%s", ptr->data.username);
           strncpy(course_data->subdir, subdir, MAX_COURSE_SUBDIR +1);
	}
	else
	 cs_critical_error(ERR_NO_VALID_TEACHER, "");
     }
}
  




P_NODE *
update_userinfo(XML_COURSE_INFO *course_data, COURSE_TYPE course_type, int *count,INDEX_USER_LIST *index_list) 
{
     P_NODE *ptr;
     P_NODE *invalid_head =0, *current = 0, *invalid_ptr;
     char *space;
     char tempname[MAX_NAME +1];
     char course[MAX_PATH +1];
     int ok = -1, central =0, central_course =0;
     snprintf(course, MAX_PATH +1, "%s/%s", course_data->subdir, course_data->dir_id);

     if(course_type != STANDALONE)
     {
        for(ptr = course_data->user_head; ptr; ptr = ptr->next)
        {
           ok = find_user_in_index(index_list, ptr);
           if(ok ==0)  /* user is not in current server adding user to central database */
	   {
              index_list = add_to_index_list(index_list, 
			   ptr->data.username, ptr->data.id, ptr->data.realname);

              strncpy(tempname, ptr->data.realname, MAX_NAME +1);
	      space = strchr(tempname, ' ');
	      if(space)
		   *space = '\0';
              derive_password (ptr->data.username, tempname,ptr->data.lastname,ptr->data.id, ptr->data.password, MAX_PASSWORD +1); /* shared_authenticate.c */	    
              if(!create_central_letter_dir (sub_dir_name(ptr->data.username)))
		strncpy(ptr->data.alias,"1", MAX_NAME +1);
	      else
		strncpy(ptr->data.alias,"0", MAX_NAME +1);
              if(!create_central_user_dir(ptr->data.username))
		strncpy(ptr->data.alias,"1", MAX_NAME +1);
	      else
		strncpy(ptr->data.alias,"0", MAX_NAME +1);
	      if(!write_central_id_file (ptr->data.realname,ptr->data.username, ptr->data.id))
		strncpy(ptr->data.alias,"1", MAX_NAME +1);
	      else
		strncpy(ptr->data.alias,"0", MAX_NAME +1);
              if(!write_central_password_file (ptr->data.username, ptr->data.password))
		strncpy(ptr->data.alias,"1", MAX_NAME +1);
	      else
	      {
		strncpy(ptr->data.alias,"0", MAX_NAME +1);
                central =1;
	      }
	   }
	   /* update central course list */
	   if((ok ==0 && central ==1) || ok ==1)
	   {
             if(!add_course(ptr->data.username, course))
		strncpy(ptr->data.alias,"1", MAX_NAME +1);
             else
	     {
		strncpy(ptr->data.alias,"0", MAX_NAME +1);
		central_course =1;
	     }
	   }
	   /* add user to central database failed or user's username has a conflict with existing user's username */
	   if((central==0 && ok ==0 && central_course ==0) || ok == 2 || (ok ==1 && central_course ==0))
	   {
	        invalid_ptr = (P_NODE *)malloc(sizeof(P_NODE));
	        strncpy(invalid_ptr->data.username,  ptr->data.username, MAX_USERNAME +1);
	        strncpy(invalid_ptr->data.id,  ptr->data.id, MAX_ID +1);
	        strncpy(invalid_ptr->data.realname,  ptr->data.realname, MAX_NAME +1);
	        strncpy(invalid_ptr->data.lastname,  ptr->data.lastname, MAX_NAME +1);
		strncpy(invalid_ptr->data.alias, ptr->data.alias, MAX_NAME +1);
	        invalid_ptr->data.group = ptr->data.group;
	        invalid_ptr->next =0;
		(*count)++;
	        if(!invalid_head)
                 invalid_head = invalid_ptr;
	        else
	         current->next = invalid_ptr;
	        current = invalid_ptr;
	   }
	   if(ok == 0 && central ==1 && central_course ==1)
	   {
		   ptr->data.team = 'N';
		   central = 0;
		   central_course = 0;
	   }
	   else if(ok == 1 && central_course == 1)
	   {
		   ptr->data.team = 'O';
		   central_course = 0;
	   }

        }	

      }
     return invalid_head;
}

void
print_import_course_info(XML_COURSE_INFO *course_data,  COURSE_TYPE course_type, int invalid_count, 
		P_NODE *invalid_head)
{
     #define MAX_TMP_NAME 30
     P_NODE *ptr;
     int total =0, i =0;
     int flag =0;
     char name[MAX_TMP_NAME +1], prefix[6];
     
     if(course_type == STANDALONE)
     {
	  strncpy(prefix, "std", 6);
	  cs_set_int_value("std_total", 1);
     }
     else
     {
	  strncpy(prefix, "ctr", 6);
	  cs_set_int_value("ctr_total", 1);
     }
     snprintf(name, MAX_TMP_NAME, "%s.0.course_subdir", prefix);
     cs_set_value(name, course_data->subdir);
     snprintf(name, MAX_TMP_NAME, "%s.0.course_dir", prefix);
     cs_set_value(name, course_data->dir_id);
     snprintf(name, MAX_TMP_NAME, "%s.0.course_no", prefix);
     cs_set_value(name, course_data->course_code);
     snprintf(name, MAX_TMP_NAME, "%s.0.course_title", prefix);
     cs_set_value(name, course_data->title);
     snprintf(name, MAX_TMP_NAME, "%s.0.course_semester", prefix);
     cs_set_value(name, course_data->semester);
     snprintf(name, MAX_TMP_NAME, "%s.0.type", prefix);
     cs_set_int_value(name, course_type);

    for(ptr= course_data->user_head; ptr; ptr = ptr->next)
    {
      flag = 0;
       snprintf(name, MAX_TMP_NAME, "%s.0.user.%d.add_state", prefix, i);

       if(find_invalid_user(invalid_head, ptr->data.username))
       {
	    cs_set_int_value(name, 1);
            flag =1;
       }
       else
	    cs_set_int_value(name, 0);

       snprintf(name, MAX_TMP_NAME, "%s.0.users.%d.group", prefix, i);
       cs_set_int_value(name, ptr->data.group );
       snprintf(name, MAX_TMP_NAME, "%s.0.users.%d.first", prefix, i);
	cs_set_value(name, ptr->data.realname);
       snprintf(name, MAX_TMP_NAME, "%s.0.users.%d.id", prefix, i);
	cs_set_value(name, ptr->data.id);
       snprintf(name, MAX_TMP_NAME, "%s.0.users.%d.username", prefix, i);
	cs_set_value(name, ptr->data.username);
       snprintf(name, MAX_TMP_NAME, "%s.0.users.%d.is_new", prefix, i);
	if(course_type == STANDALONE)
          cs_set_int_value(name, 3 ); 
        else if(ptr->data.team == 'N')
          cs_set_int_value(name, 0 );
	else if(flag == 1)
          cs_set_int_value(name, 2 );
	else
          cs_set_int_value(name, 1 );

       snprintf(name, MAX_TMP_NAME, "%s.0.users.%d.central_state", prefix, i);
       cs_set_int_value(name, atoi(ptr->data.alias) ); 
        total++;
	i++;
    }
    cs_set_int_value("suc_total", total - invalid_count);
    cs_set_int_value("inv_total", invalid_count);
 #undef MAX_TMP_NAME 

}

void
update_password(P_NODE *invalid_head, XML_COURSE_INFO *course_data)
{
   char path[MAX_PATH +1];
   CONFIG_STRUCT conf;
   P_NODE *ptr;

   if(invalid_head)
   {
     snprintf(path, MAX_PATH +1, "%s/%s", course_data->subdir, course_data->dir_id);
     read_configuration_file (path, &conf);
     for(ptr = invalid_head; ptr; ptr = ptr->next)
	 mark_passwd_file (ptr->data.username, &conf);
   }

}



void
do_action(XML_COURSE_INFO *course_data, const char *prefix, COURSE_TYPE course_type)
{
     P_NODE *invalid_head;
     int invalid_count =0;
     INDEX_USER_LIST *index_list =0;
     index_list = (INDEX_USER_LIST *)malloc(sizeof(INDEX_USER_LIST));
     index_list->current =0;
     index_list->head =0;
     index_list = build_index_user_list(index_list);
    
     update_course_info(course_data, course_type);
     update_course_path(course_data,  course_type,  prefix, index_list);
     invalid_head = update_userinfo(course_data, course_type, &invalid_count, index_list);

     update_password(invalid_head, course_data);
     reindex_users("../users");
     print_import_course_info(course_data, course_type, invalid_count, invalid_head);
     free_option_person_list (invalid_head);
     free_index_user_list(index_list);
}
    

        
int main()
{
  char subdir[MAX_COURSE_SUBDIR + 1];
  char temp_course[MAX_COURSE_SUBDIR + 1];
  char key[MAX_KEY + 1];
  COURSE_TYPE course_type;
  SESSION user;
  char prefix_rosterfile[MAX_PATH +1];
  XML_COURSE_INFO  *course_data;
  FILE *fp;
  struct stat file_stat;

  
  cs_cgi_init();  
  read_parameters (subdir,  key,  &course_type);
  validate_super_key(key, &user);   /* shared_super_util.c */
  set_admin_page(&user, key, 0, "super_new_course_form");  /* shared_super_menu.c */

  fp = cgi_filehandle (global_cgi, "roster");
  if(!fp)
      cs_critical_error(ERR_UPLOAD_FILE, "");   /** shared_cs_util.h */
  if(fstat(fileno(fp), &file_stat))
     cs_critical_error(ERR_NO_FILE, "");
  if(file_stat.st_size ==0)
      cs_critical_error(ERR_NO_FILE, "");


  if(course_type == TEMPLATE)
  {
       if(*subdir != 'T')
       {
           snprintf(temp_course, MAX_COURSE_SUBDIR+1, "%s%s", "T", subdir);
           strncpy(subdir, temp_course, MAX_COURSE_SUBDIR +1);
        }
  }
  write_roster_file(fp, prefix_rosterfile);
  make_work_space(prefix_rosterfile);
  course_data = build_course_info(prefix_rosterfile, subdir);
  do_action(course_data, prefix_rosterfile,  course_type);
  free_option_person_list (course_data->user_head);
  free(course_data);
  cs_cgi_display("super_upload_tar", 1);
  cs_cgi_destroy();
    return 0;
}


