#include <stdio.h>
#include <time.h>
#include <string.h>
#include <ctype.h>

#include "global.h"

#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_authenticate.h"
#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_strtrm.h"
#include "manhat-lib/shared_encrypt.h"
#include "manhat-lib/shared_central_user.h"
#include "manhat-lib/shared_add_util.h"
#include "manhat-lib/shared_central_log.h"


static void
read_parameters (char *course, char *key,
		 char *current, char *new_passwd, char *verify)
{

  cs_get_optional_parameter ("crs", course, MAX_FILENAME);	/* shared_cgi_util.c */
  cs_get_required_parameter ("id", key, MAX_KEY);
  cs_get_required_parameter ("current", current, MAX_PASSWORD);
  cs_get_required_parameter ("new", new_passwd, MAX_PASSWORD);
  cs_get_required_parameter ("verify", verify, MAX_PASSWORD);

}



static void
check_standalone_password (const char *plaintext_password, const CONFIG_STRUCT *conf,
				const SESSION *user)
{
  char passwd_fname[MAX_PATH + 1];
  FILE *fp;
  char line[200];

  /* path to the individual's password file */
  snprintf (passwd_fname, MAX_PATH + 1, "%s%s/%s/%s",
	    conf->course_path, PEOPLE_DIR, user->username, PASSWORD_FNAME);

  fp = fopen (passwd_fname, "r");
  if (!fp)
    cs_critical_error (ERR_PASSWD_OPEN_FAILED, "");
  if (!fgets (line,200, fp))
    cs_critical_error (ERR_PASSWD_OPEN_FAILED, "");
  fclose(fp);
  strtrm(line);  /* shared_util.c */
  if(strcmp (line, encrypted_password(plaintext_password, user->username)))  /* shared_encrypted.c */
    cs_critical_error(ERR_INCORRECT_PASSWD_ENTERED,"");
}





static void
check_central_password (const char *username, const char *plaintext_passwd)
{
   char current_encrypted_password[MAX_ENCRYPTED_PASSWORD + 1];

   get_central_user_password(username, current_encrypted_password);  /* shared_encrypt.c */
   if(strcmp(current_encrypted_password, encrypted_password(plaintext_passwd, username)))   /* shared_encrypt.c */
      cs_critical_error (ERR_INCORRECT_PASSWD_ENTERED, "");
}




  


/* warning THIS MODIFIES all of the parameters !!! */
int contains_real_name(char *password, char *first, char *last)
{
 char *ptr;
 
 for(ptr = password; *ptr; ptr++)
   *ptr = tolower(*ptr);

 for(ptr = first; *ptr; ptr++)
   *ptr = tolower(*ptr);

 for(ptr = last; *ptr; ptr++)
   *ptr = tolower(*ptr);
   
 return( strstr(password, first) ||
         strstr(password, last) );
}           
   



static void
check_validity (const char *current, 
			const char *new_passwd, 
			const char *verify,
			SESSION *user)
{
  int len;
  char *ptr;
  char tmp_passwd[MAX_PASSWORD + 1];
  char first[MAX_NAME + 1];
  char last[MAX_NAME + 1];
  char default_passwd[MAX_PASSWORD + 1];
  char allowable_punctuation[]= "+=-$.^*";
    
  /* a copy of new_passwd is made only to keep the compiler
  ** from complaining about discarding 'const' modifier from
  ** new_passwd when we later point ptr at it
  */
  strncpy(tmp_passwd, new_passwd, MAX_PASSWORD+1);


  len = strlen (tmp_passwd);
  if ((len < 6) || (len > 15))
    cs_critical_error (ERR_PASSWD_WRONG_LENGTH, "");


  /* were the two new passwords typed in the same? */
  if (strcmp (tmp_passwd, verify))
    cs_critical_error (ERR_PASSWD_ENTERED_DIFFERENTLY, "");

  /* don't allow them to use their current password */
  if (!strcmp (current, tmp_passwd))
    cs_critical_error (ERR_NEW_PASSWD_SAME_AS_OLD, "");


  /* don't allow the password to be the same as the default
  ** password, as returned by derive_password()
  */
  get_first_last_names(user->realname, first, last);   /* shared_authenticate.c */
  derive_password(user->username, first, last, user->id, default_passwd, MAX_PASSWORD + 1);
  if (!strcmp (new_passwd, default_passwd))
    cs_critical_error (ERR_CANT_USE_DEFAULT_PASSWORD, "");


  /* don't allow password to be same as username */
  if (!strcmp (user->username, tmp_passwd))
    cs_critical_error (ERR_PASSWD_AND_USERNAME_SAME, "");


  /* don't allow password to be same their id number */
  if (!strcmp (user->id, tmp_passwd))
    cs_critical_error (ERR_PASSWD_AND_ID_SAME, "");
  

  /* check for reasonable characters */
  for (ptr = tmp_passwd; *ptr; ptr++)
    if (!isdigit (*ptr) && !isalpha (*ptr)  && !strchr(allowable_punctuation,*ptr))
      {
       cs_critical_error (ERR_PASSWD_HAS_INVALID_CHARS_X, tmp_passwd);
      }
      

         
   if(contains_real_name(tmp_passwd, first, last))
       cs_critical_error(ERR_PASSWD_CONTAINS_YOUR_NAME, "");          
      
    /*** tmp_password is now lower cased, thanks to contains_real_name() call */
    
    /* check some common bad passwords */
    if(!strcmp(tmp_passwd,"manhattan") ||
       !strcmp(tmp_passwd,"password") )
          cs_critical_error(ERR_PASSWD_TOO_SIMPLE, "");          
       
      
}





static void
change_password (const CONFIG_STRUCT *conf,
		const char *new_passwd,	const SESSION *user,
		int standalone)
{
  int success;
   
  if(standalone)
    success = write_password(user->username, new_passwd, conf->course_path);  /* shared_encrypt.c */   
  else
    {
     success = write_central_password_file(user->username, new_passwd);     /* shared_encrypt.c */
     if(success)
     {
	 cs_set_event_msg();
         write_central_log(user->username, hdf_get_value(global_cgi->hdf, "log_event.change_password",""), "",0);
     }
    }     

   if(!success)
         cs_critical_error(ERR_PASSWD_INTERNAL_ERROR, "");
         
         
}






int
main (void)
{
  SESSION user;
  CONFIG_STRUCT conf;		/* the configuration read from config file */
  char course[MAX_PATH + 1];	/* from crs=? command line */
  char key[MAX_KEY + 1];	/* from id=? command line */
  char current[MAX_PASSWORD + 1], new_passwd[MAX_PASSWORD + 1], verify[MAX_PASSWORD + 1];

  cs_cgi_init();
  read_parameters (course, key, current, new_passwd, verify);

  if(*course)
   {
     read_configuration_file (course, &conf);	/* shared_util.c */
     validate_key (key, &user, &conf);          /* shared_util.c */
     if(conf.standalone)
        check_standalone_password(current, &conf, &user);
     else
       check_central_password(user.username,current);
     check_validity (current, new_passwd, verify, &user);
     change_password (&conf, new_passwd,&user,conf.standalone);
     printf ("Location: %s?crs=%s&id=%s\n\n", "main_menu", course, key);
   }
  else
   {
     validate_server_key(key,&user);  /* shared_util.c */ 
     check_central_password(user.username,current);
     check_validity (current, new_passwd, verify, &user);
     change_password (&conf, new_passwd, &user,0);
     printf ("Location: %s?id=%s&\n\n", "mymanhattan",key);
   }
  cs_cgi_destroy();
  return 0;

}
