#include <stdlib.h>
#include <ctype.h>  
#include <stdio.h>
#include <string.h>
#include <sys/types.h>		/* for umask() */
#include <sys/stat.h>
#include <sys/file.h>            /* for file locking */
#include <unistd.h>		/* for unlink() */
       

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

#include "shared_util.h"
#include "shared_authenticate.h"  /* for sub_dir_name() */
#include "shared_cookie.h"        
#include "shared_lock.h"
#include "shared_cs_util.h"

#define CHAR_SET ""


void
send_content_type ()
{
   static int content_type_sent = 0;
  
  if (!content_type_sent)
    {
      printf ("Content-type: text/html%s%s\n\n",
	      strlen (CHAR_SET) ? "; " : "", CHAR_SET);	      /* LANG_CHARSET is #defined in lang.h */
      content_type_sent = 1;
    }
}







void
read_configuration_file (const char *fname, CONFIG_STRUCT * config)
{
#define MAX_MSG 100
  FILE *fp;
  int fd;
  char fullpath[MAX_PATH + 1];
  int is_template;

  /* if the first letter of the course fname starts with a 'T',
     ** then this is a course template and the config file will be
     ** located in the TEMPLATE_PATH directory instead of the 
     ** COURSE_PATH directory
   */
  is_template = (*fname == 'T');
  snprintf (fullpath, MAX_PATH + 1, "%s%s/%s",
	    is_template ? TEMPLATE_PATH : COURSE_PATH, fname, CONFIG_FNAME);

  fp = fopen (fullpath, "r");
  if (!fp)
    {
      cs_critical_error (ERR_ERROR_OPENING_CONFIG_FILE,  fullpath);   /* shared_cs_util.c */
    }
    
  /* the config file can be changed by a teacher or administrator
  ** so we use advisory file locking 
  */  
  fd = fileno(fp);
  get_shared_lock(fd, 1);    /* shared_lock.c */
  
  if (fread (config, sizeof (CONFIG_STRUCT), 1, fp) != 1)
    cs_critical_error (ERR_ERROR_READING_CONFIG_FILE, fullpath);   /* shared_cs_util.c */

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

  /* whether the course is a template or not is NOT stored in the file..
     ** instead it is set here based on the course name
   */
  config->template = is_template;

  /* the course_path data item used to be saved in the file  since the
     ** addition of the course templates idea, it is more convenient to set
     ** it here
   */

  snprintf (config->course_path, MAX_PATH + 1, "%s%s/",
	    is_template ? TEMPLATE_PATH : COURSE_PATH, fname);
#undef MAX_MSG
}




/** this version of read_configuration_file()
*** returns 1 on success, else 0
**/
int
read_configuration_file_ok (const char *fname, CONFIG_STRUCT * config)
{
  FILE *fp;
  int fd;
  char fullpath[MAX_PATH + 1];
  int is_template;

  /* if the first letter of the course fname starts with a 'T',
     ** then this is a course template and the config file will be
     ** located in the TEMPLATE_PATH directory instead of the 
     ** COURSE_PATH directory
   */
  is_template = (*fname == 'T');
  snprintf (fullpath, MAX_PATH + 1, "%s%s/%s",
	    is_template ? TEMPLATE_PATH : COURSE_PATH, fname, CONFIG_FNAME);

  fp = fopen (fullpath, "r");
  if (!fp)
      return 0;
          
  /* the config file can be changed by a teacher or administrator
  ** so we use advisory file locking 
  */  
  fd = fileno(fp);
  get_shared_lock(fd, 1);    /* shared_lock.c */
  
  if (fread (config, sizeof (CONFIG_STRUCT), 1, fp) != 1)
   {
    release_lock(fd);
    fclose(fp);
    return 0;
   } 

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

  /* whether the course is a template or not is NOT stored in the file..
     ** instead it is set here based on the course name
   */
  config->template = is_template;

  /* the course_path data item used to be saved in the file  since the
     ** addition of the course templates idea, it is more convenient to set
     ** it here
   */

  snprintf (config->course_path, MAX_PATH + 1, "%s%s/",
	    is_template ? TEMPLATE_PATH : COURSE_PATH, fname);

  return 1;   /* success */
}










/*void set_umask () 
{
  umask (0006);			 all files to have rw-rw---- access
}
*/





void check_key_for_cookie(const char *orig_key_fname, char *new_key_fname)
{

  if(!strcmp(orig_key_fname, USE_COOKIE))
   {
       if(!parse_cookie(new_key_fname))   /* shared_cookie.c */
         cs_critical_error (ERR_SORRY_YOUVE_BEEN_LOGGED_OUT, "");
   }
  else
    strncpy(new_key_fname, orig_key_fname, MAX_KEY + 1);
}


void get_ip_address(char *this_ip)
{
 char *ip_ptr; 
 
  ip_ptr = getenv("REMOTE_ADDR");
  if(!ip_ptr)
     *this_ip = '\0';
  else
   { 
     strncpy(this_ip, ip_ptr, MAX_IP + 1);
     *(this_ip + MAX_IP) = '\0';
   }  
}
        
 
void
secure_server_check()
{
 int server_port;
 
 if(USE_HTTPS)
  {
   server_port = atoi(getenv("SERVER_PORT"));
   if(server_port != HTTPS_PORT)
      cs_critical_error(ERR_THIS_IS_A_SECURE_SERVER, "");
  }
}      
    


/* converts first occurrence of a slash / to a 
** dot - returns results in new
** assumes length of orig < MAX_PATH 
*/    
void course_slash_to_dot(const char *orig, char *new)
{
 char *ptr;
 
 strncpy(new, orig, MAX_PATH + 1);
 ptr = strchr(new, '/');
 if(ptr)
    *ptr = '.';
}


void
get_symlink_fname ( const char *course, 
                     char *symlink_fname, const SESSION *user)
{
  char modified_course[MAX_PATH + 1];
  
    course_slash_to_dot(course, modified_course);    /* shared_util.c */
    snprintf (symlink_fname, MAX_FILENAME + 1, "%s_%s_%ld_%d", 
    				user->username, modified_course, (long)time(NULL), getpid());
}


    
    
 
 
    

void
validate_server_key (const char *key_fname, SESSION *user)
{
#define MAX_MSG 200
  FILE *fp;
  time_t now;
  char msg[MAX_MSG];
  char keypath[MAX_PATH + 1];
  char key_filename[MAX_KEY + 1];
  char this_ip[MAX_IP + 1];
  
      
  secure_server_check();  
  check_key_for_cookie(key_fname, key_filename);

  snprintf (keypath, MAX_PATH + 1, "../%s/keys/%s", 
                                  USERS_DIR, key_filename);
  fp = fopen (keypath, "r+");
  if (!fp)
      cs_timeout_error(ERR_SORRY_YOUVE_BEEN_LOGGED_OUT, "");
  if (fread (user, sizeof (SESSION), 1, fp) != 1)
    {
      cs_timeout_error (ERR_ERROR_READING_KEY_FILE, key_filename);
    }

  
   if(IP_CHECK_ENABLED)  /* see custom.h - compare ip address stored in key to this_ip */
   {
   
     get_ip_address(this_ip);
     if(strcmp(user->ip_address, this_ip))
           cs_timeout_error(ERR_REQUEST_DENIED, "");
   }
  
  if (user->expires)
    {
      now = time (NULL);
      if (user->expires < now)
	{
	 snprintf (msg, MAX_MSG, "%d", (MAX_KEY_EXPIRE) / 60);
	cs_timeout_error (ERR_YOUR_SESSION_HAS_TIMED_OUT, msg);
	}
      user->expires = now + MAX_KEY_EXPIRE;	

      if (fseek(fp,0,SEEK_SET))
	cs_timeout_error (ERR_CANT_CREATE_NEW_KEY, "");
      if (fwrite (user, sizeof (SESSION), 1, fp) != 1)
	cs_timeout_error (ERR_ERROR_WRITING_KEY, "");
      fclose (fp);
    }
#undef MAX_MSG
} 







void
validate_key (const char *key_fname, SESSION * user, CONFIG_STRUCT *conf)
{
#define MAX_MSG 256
  FILE *fp;
  time_t now;
  char msg[MAX_MSG];
  char keypath[MAX_PATH + 1];
  char *ptr2;
  char key_filename[MAX_KEY + 1];
  char this_ip[MAX_IP + 1];
  char course_name[MAX_PATH + 1];
 
  
 if(conf->standalone)
  {
   secure_server_check();
   check_key_for_cookie(key_fname, key_filename);
  
   snprintf (keypath, MAX_PATH + 1, "%skeys/%s", conf->course_path, key_filename);
  
   
   fp = fopen (keypath, "r+");
   if (!fp)
      cs_timeout_error (ERR_SORRY_YOUVE_BEEN_LOGGED_OUT, "");
   if (fread (user, sizeof (SESSION), 1, fp) != 1)
     {
       cs_timeout_error (ERR_ERROR_READING_KEY_FILE, key_filename);
     }


   if(IP_CHECK_ENABLED)   /*see custom.h */
   {
    get_ip_address(this_ip);
    if(strcmp(user->ip_address, this_ip))
        cs_timeout_error(ERR_REQUEST_DENIED, "");
   }
       
   if (user->expires)
     {				
       now = time (NULL);
       if (user->expires < now)
	{
	 snprintf (msg, MAX_MSG, "%d", conf->key_expire / 60);
	 cs_timeout_error (ERR_YOUR_SESSION_HAS_TIMED_OUT, msg); 
	}
      user->expires = now + conf->key_expire;
      if(fseek(fp,0,SEEK_SET))
	cs_timeout_error (ERR_CANT_CREATE_NEW_KEY, "");
      if (fwrite (user, sizeof (SESSION), 1, fp) != 1)
	cs_timeout_error (ERR_ERROR_WRITING_KEY, "");
      fclose (fp);
    }
   }
 else   /* course is a centralized login course */
  {
   validate_server_key (key_fname, user);
   if(user->group != ADMIN)   
   {
   
     strncpy(course_name, conf->course_path, MAX_PATH + 1);
     
     /* extract course name from course_path */
     ptr2 = strrchr(course_name,'/');
     if(!ptr2  || (ptr2 == course_name))
         cs_timeout_error(ERR_NO_SLASH_IN_COURSE_PATH, "");
         
     *ptr2 = '\0';
     ptr2 = strrchr(course_name, '/');
     if(!ptr2 || (ptr2== course_name))
           cs_timeout_error(ERR_NO_SLASH_IN_COURSE_PATH, "");
     
     *ptr2 = '.';

     ptr2 = strrchr(course_name, '/');
     if(!ptr2 || (ptr2== course_name))
           cs_timeout_error(ERR_NO_SLASH_IN_COURSE_PATH, "");
        
       
     /* sub_dir_name() is in shared_authenticate.c */  
     snprintf(keypath, MAX_PATH + 1, "../%s/%c/%s/%s%s",
                            USERS_DIR, sub_dir_name(user->username), user->username,
                            			ptr2 + 1,KEY_FILE_EXTENSION);
                            			
    fp = fopen(keypath, "r");
    if(!fp)
       cs_timeout_error (ERR_SORRY_YOUVE_BEEN_LOGGED_OUT, "");
    if(fread(user,sizeof(SESSION), 1, fp) != 1)
       cs_timeout_error (ERR_SORRY_YOUVE_BEEN_LOGGED_OUT, "");
    fclose(fp);
  }  
 }        
#undef MAX_MSG
} 



void
crash (const char *msg1, const char *msg2)
{
  puts (msg1);
  puts (msg2);
  exit (1);
}





/* this is only for WNEC */
/* WNEC's faculty id is 4 digits */

int is_wnec_faculty(const char *id)
{
#ifdef WNEC_FACULTY_ID
   int count = 0;
   int i, len;

 if(WNEC_FACULTY_ID)
 {
   len = strlen(id);
   for(i = 0; i < len; i++)
   {
      if(isdigit(id[i]))
	  count++; 
   }   
   if(count == 4)
      return 1;
   return 0;
 }
#endif
   return 1;
}

