#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "global.h"

#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_access.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_file_util.h"


static void
read_parameters (char *course, char *key, long *offset, long *next, int *outbox)
{

  char tmp[30 + 1];


  cs_get_required_parameter ("crs", course, MAX_PATH);
  cs_get_required_parameter ("id", key, MAX_KEY);
  cs_get_required_parameter ("loc", tmp, 30);
  *offset = atol(tmp);
  
  cs_get_required_parameter ("next", tmp, 30);
  *next = atol(tmp);
  
  cs_get_optional_parameter ("outbox", tmp, 30);
  *outbox = strlen(tmp)? 1: 0;
  
}




static void
clear_deleted_times(PERSON_NODE *person_head)
{
 PERSON_NODE *ptr;
 
 for(ptr = person_head; ptr; ptr=ptr->next)
   ptr->data.time_deleted = 0;
}

   
static void 
mark_deleted(const char *deleter_username, PERSON_NODE *person_head)
{
 PERSON_NODE *ptr;
 time_t now;
 
 now = time(NULL);
 
 for(ptr = person_head; ptr; ptr=ptr->next)
   if(!strcmp(ptr->data.username, deleter_username))
        ptr->data.time_deleted = now;
        

}





/** Has everyone - all of the recipients plus the sender of
*** the message deleted the message?
***
*** Note if a person has been removed from the classroom, without
*** deleting their Post Office messages, those messages can never
*** be deleted.
**/

static
int everyone_deleted(PERSON_NODE *person_head,  const char *sender_username, 
                        const char *info_fname, int is_outbox, const char *course_path)
{
 PERSON_NODE *ptr;
 FILE *fp;
 char outbox_path[MAX_PATH + 1];
 OUTBOX_RECORD outbox;
 int found;
 int outbox_deleted;
 

/* Check the list of recipients to see
** if there's a recipient who did NOT
** delete the message
*/
 for(ptr=person_head; ptr; ptr=ptr->next)
   if(!ptr->data.time_deleted)
       return 0;
       
 /* still here? then all recipients have deleted the msg
 ** Did the sender deleted the message?
 */
 
 if(is_outbox)  /* flag means person running this program from their outbox */
     return 1;      
 else               /* don't know if sender deleted - need to check his outbox */
  {
   snprintf(outbox_path, MAX_PATH + 1, "%s%s/%s/%s/%s",
                        course_path, PEOPLE_DIR, sender_username, MAIL_DIR, OUTBOX_FNAME);       
   fp = fopen(outbox_path, "r");
   if(!fp)
       return 0;   /* don't complain */
   
   get_shared_lock(fileno(fp), 1);   /* shared_lock.c */
 
   found = 0;
   outbox_deleted = 0;
   while(!found && (fread(&outbox, sizeof(OUTBOX_RECORD), 1, fp) == 1) )
    {
     if(!strcmp(outbox.info_fname, info_fname))
       {
         found = 1;
         outbox_deleted = outbox.how_sent == DELETED? 1 : 0;
       }
    }
  
    release_lock(fileno(fp));
    fclose(fp);
  
    return outbox_deleted;
   }   
}         
                                     
     



static void
delete_msg_files(const char *course_path, const char *sender_username, const char *msg_fname, 
                               int no_attachments, ATTACHMENT_NODE *attachment_head)
{
 char mail_dir[MAX_PATH + 1];
 char filepath[MAX_PATH + 1];
 ATTACHMENT_NODE *ptr;
 
 snprintf(mail_dir, MAX_PATH + 1, "%s%s/%s/%s", course_path, PEOPLE_DIR, sender_username, MAIL_DIR);
 
 /* delete the message file */
 snprintf(filepath, MAX_PATH + 1, "%s/%s", mail_dir, msg_fname);
 unlink(filepath);
 
 
 /* now delete all attachments */
 for(ptr=attachment_head; ptr; ptr=ptr->next)
 {
  snprintf(filepath, MAX_PATH + 1, "%s/%s", mail_dir,  ptr->data.unique_fname);
  if( (no_attachments == -1) && (ptr == attachment_head) && 
                       isdirectory(mail_dir, ptr->data.unique_fname))   /* shared_file_util.c */
    kill_directory(filepath);  /* shared_file_util.c */
  else
     unlink(filepath);
 }


}       
  
  
  




static
void complete_deletion(const char *sender_username, const char *info_fname, 
					const char *deleter_username, CONFIG_STRUCT *conf, int is_outbox)
{
  INFO infofile_info;
  char infopath[MAX_PATH + 1];
  FILE *fp;
  ATTACHMENT_NODE *aptr = 0;
  PERSON_NODE *pptr = 0;
  int i;
  PERSON person_buffer;
  
  PERSON_NODE *person_head = 0;
  ATTACHMENT_NODE *attachment_head = 0;

  snprintf (infopath, MAX_PATH + 1, "%s%s/%s/%s/%s", conf->course_path, PEOPLE_DIR,
	    sender_username, MAIL_DIR, info_fname);

  fp = fopen (infopath, "r+");
  if (!fp)
    cs_critical_error (ERR_OPENING_INFO, infopath);
    
  get_exclusive_lock(fileno(fp),1);   /* shared_lock.c */   

  if (fread (&infofile_info, sizeof (INFO), 1, fp) != 1)
    cs_critical_error (ERR_READING_INFO, infopath);


  /* process attachments */


  for (i = 1; i <= abs(infofile_info.no_attachments); i++)
    {
      if (!attachment_head)
	{
	  attachment_head =
	    (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!attachment_head)
	    cs_critical_error (ERR_MALLOC_FAILED, "");
	  aptr = attachment_head;
	}
      else
	{
	  aptr->next = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!aptr->next)
	    cs_critical_error (ERR_MALLOC_FAILED, "");
	  aptr = aptr->next;
	}

      if (fread (&aptr->data, sizeof (ATTACHMENT), 1, fp) != 1)
	cs_critical_error (ERR_ERROR_READING_ATTACHMENT, infopath);
      aptr->next = 0;
    }


  /* process list of people msg was sent to  */
  while (fread (&person_buffer, sizeof (PERSON), 1, fp) == 1)
    {
      if (!person_head)
	{
	  person_head = (PERSON_NODE *) malloc (sizeof (PERSON_NODE));
	  if (!person_head)
	    cs_critical_error (ERR_MALLOC_FAILED, "");
	  pptr = person_head;
	}
      else
	{
	  pptr->next = (PERSON_NODE *) malloc (sizeof (PERSON_NODE));
	  if (!pptr->next)
	    cs_critical_error (ERR_MALLOC_FAILED, "");
	  pptr = pptr->next;
	}

      pptr->data = person_buffer;
      pptr->next = 0;
    }
    
    
   /* now have all information from info file stored */
   
   if(infofile_info.version != VERSION24)   /* then this msg was sent with an earlier version of Manhattan */
      {
       clear_deleted_times(person_head);
       infofile_info.version = VERSION24;
      }
      
   if(!is_outbox)
             mark_deleted(deleter_username, person_head);   
       
   if(everyone_deleted(person_head,sender_username,info_fname,is_outbox, conf->course_path))    
       delete_msg_files(conf->course_path, sender_username, infofile_info.msg_fname, 
                                            infofile_info.no_attachments, attachment_head);
        

 /** now rewrite the info file .... */
 
   if (fseek (fp, 0, SEEK_SET))
    cs_critical_error (ERR_FSEEK_FAILED, infopath);

   if(fwrite(&infofile_info, sizeof(INFO), 1, fp) != 1)
       cs_critical_error(ERR_FWRITE_FAILED, infopath);
   
   for(aptr= attachment_head; aptr; aptr=aptr->next)
    {    
     if (fwrite (&aptr->data, sizeof (ATTACHMENT), 1, fp) != 1)
	cs_critical_error (ERR_FWRITE_FAILED, infopath);
    }


   for(pptr= person_head; pptr; pptr=pptr->next)
    {    
     if (fwrite (&pptr->data, sizeof (PERSON), 1, fp) != 1)
	cs_critical_error (ERR_FWRITE_FAILED, infopath);
    }
    
  release_lock(fileno(fp));   /* shared_lock.c */
  fclose (fp);
}








static
void  delete_outbox_msg(long offset, CONFIG_STRUCT *conf, const char *username)
{
   FILE *fp;
   char outbox_path[MAX_PATH + 1];
   OUTBOX_RECORD outbox;
   
   
   snprintf(outbox_path, MAX_PATH + 1, "%s%s/%s/%s/%s",
            conf->course_path, PEOPLE_DIR, username, MAIL_DIR, OUTBOX_FNAME);
            
   fp = fopen(outbox_path, "r+");
   if(!fp)
      cs_critical_error(ERR_FOPEN_READ_FAILED, outbox_path);
               
   get_exclusive_lock(fileno(fp), 1);   /* shared_lock.c */
   
   if (fseek (fp, offset, SEEK_SET))
       cs_critical_error (ERR_FSEEK_FAILED, outbox_path);
   if (fread (&outbox, sizeof (OUTBOX_RECORD), 1, fp) != 1)
       cs_critical_error (ERR_FREAD_FAILED, outbox_path);

   outbox.how_sent = DELETED;
  
   if(fseek (fp, offset, SEEK_SET))
      cs_critical_error (ERR_FSEEK_FAILED, outbox_path);
   if (fwrite (&outbox, sizeof (OUTBOX_RECORD), 1, fp) != 1)
	cs_critical_error (ERR_FWRITE_FAILED, outbox_path);


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

   
   complete_deletion(username, outbox.info_fname, username, conf, 1);
   

   
}






static
void  delete_inbox_msg(long offset, CONFIG_STRUCT *conf, const char *username)
{
   FILE *fp;
   char inbox_path[MAX_PATH + 1];
   INBOX_RECORD inbox;
   
   
   snprintf(inbox_path, MAX_PATH + 1, "%s%s/%s/%s/%s",
            conf->course_path, PEOPLE_DIR, username, MAIL_DIR, INBOX_FNAME);
            
   fp = fopen(inbox_path, "r+");
   if(!fp)
      cs_critical_error(ERR_FOPEN_READ_FAILED, inbox_path);
               
   get_exclusive_lock(fileno(fp), 1);   /* shared_lock.c */
   
   if (fseek (fp, offset, SEEK_SET))
       cs_critical_error (ERR_FSEEK_FAILED, inbox_path);
   if (fread (&inbox, sizeof (INBOX_RECORD), 1, fp) != 1)
       cs_critical_error (ERR_FREAD_FAILED, inbox_path);

   /* Don't allow deletions if this message has not been read.
   ** This can never happen unless someone is hacking around!
   */
   
   if(!inbox.time_read)
        access_denied_error();    /* shared_access.c */

   inbox.newfile_info.how_sent = DELETED;
  
   if(fseek (fp, offset, SEEK_SET))
      cs_critical_error (ERR_FSEEK_FAILED, inbox_path);
   if (fwrite (&inbox, sizeof (INBOX_RECORD), 1, fp) != 1)
	cs_critical_error (ERR_FWRITE_FAILED, inbox_path);

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

   complete_deletion(inbox.newfile_info.sender, inbox.newfile_info.info_fname, username, conf, 0);
   
   
}












int
main (void)
{
  char course[MAX_PATH + 1];	/* from crs=? command line */
  char key[MAX_KEY + 1];	/* from id=? command line */
  long offset;			/* from loc=? command line */

  int outbox;                   /* from optional outbox=? cmd line */
  long next;                    /* from next=? command line */
  
  SESSION user;
  CONFIG_STRUCT conf;


  cs_cgi_init();
  read_parameters (course, key, &offset, &next, &outbox);

  read_configuration_file (course, &conf);	/* shared_util.c */
  validate_key (key, &user, &conf);		/* shared_util.c */

  if(!has_write_permission(&user, &conf))   /* shared_access.c */
      access_denied_error();                /* shared_access.c */


  if(outbox)
     delete_outbox_msg(offset, &conf, user.username);
  else
     delete_inbox_msg(offset, &conf, user.username);   


  if (next == -1)		/* if there is no next item */
    printf ("Location: %s?crs=%s&id=%s\n\n", outbox?"post_outbox":"post_inbox", course, key);
  else
    printf ("Location: %s?crs=%s&id=%s&loc=%ld\n\n", outbox?"post_read_outbox":"post_read_inbox",
	    									course, key, next);
  cs_cgi_destroy();
  return 0;

}




