#include <stdio.h>
#include <sys/stat.h>		/* for stat() */
#include <time.h>
#include <string.h>
#include <ctype.h>		/* for tolower() */
#include <unistd.h>		/* for mktemp() */
#include <sys/types.h>		/* for opendir() */
#include <dirent.h>

#include "global.h"
#include "custom.h"		/* for date FORMAT strings */

#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_news_util.h"
#include "manhat-lib/shared_translate_url.h"
#include "manhat-lib/shared_file_util.h"
#include "manhat-lib/shared_access.h"
#include "manhat-lib/shared_bio.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_post_outbox.h"
#include "manhat-lib/shared_post_inbox.h"
#include "manhat-lib/shared_msg_line.h"

static char inbox_path[MAX_PATH + 1];	/* full path to inbox.dat */



PERSON_NODE *person_head = 0;
ATTACHMENT_NODE *attachment_head = 0;



static void
read_parameters (char *course,
		 char *key, long *offset,int *del_link, int *printer_friendly, int *fixed_font)
{

  char off_set[MAX_FILENAME + 1];	/* for holding the long value */


  cs_get_required_parameter ("crs", course, MAX_PATH);	/* shared_cgi_util.c */
  cs_get_required_parameter ("id", key, MAX_KEY);

  /* just check if the "del_link" parameter is there */

  cs_get_optional_parameter ("del_link", off_set, 5);
  *del_link = strlen(off_set);
  cs_get_required_parameter ("loc", off_set, 10);
  *offset = atol (off_set);

  /* provide printer-friendly version? */
  cs_get_optional_parameter ("print", off_set, 5);              /* shared_cs_util.c */
  *printer_friendly = *off_set? 1: 0;


  /* provide preformatted text (instead of proportional) ? */
  cs_get_optional_parameter ("fixed", off_set, 5);              /* shared_cs_util.c */
  *fixed_font = *off_set? 1: 0;



}




static int
multiple_recipients (SESSION *user)
{
  PERSON_NODE *pptr;

  for (pptr = person_head; pptr; pptr = pptr->next)
    if (((pptr->data.how_received == CC) || (pptr->data.how_received == TO))
	&& strcmp (user->username, pptr->data.username))
      return 1;

  return 0;
}



static void
get_previous_offset (long offset, long *prev_offset, int *found, int show_user_hidden)
{
  FILE *fp;
  INBOX_RECORD data;


  fp = fopen (inbox_path, "r");	/* inbox_path is global top of this file */
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, inbox_path);

  get_shared_lock(fileno(fp), 1);   /* shared_lock.c */
  *prev_offset = offset;
  *found = 0;
  do
    {
      *prev_offset = (*prev_offset - sizeof (INBOX_RECORD));

      if (*prev_offset < 0)
	{
	  *found = 0;
	  release_lock(fileno(fp));  /* shared_lock.c */
	  fclose (fp);
	  return;
	}

      fseek (fp, *prev_offset, SEEK_SET);
      fread (&data, sizeof (INBOX_RECORD), 1, fp);
      if(data.newfile_info.how_sent != DELETED)
           *found = show_user_hidden || !data.is_sleepy;

      if (*found)
       {
        release_lock(fileno(fp));    /* shared_lock.c */
	fclose (fp);
      }	
    }
  while (!*found);
}





static void
get_next_offset (long offset, long *next_offset, int *found, int show_user_hidden)
{
  FILE *fp;
  INBOX_RECORD data;

  fp = fopen (inbox_path, "r");	/* inbox_path is global top of this file */
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, "");
    
  get_shared_lock(fileno(fp), 1);    /* shared_lock.c */  
  fseek (fp, offset, SEEK_SET);
  fread (&data, sizeof (INBOX_RECORD), 1, fp);	/* read THIS record */

  *found = 0;
  while (!*found && fread (&data, sizeof (INBOX_RECORD), 1, fp) == 1)
   if(data.newfile_info.how_sent != DELETED)
      *found = show_user_hidden || !data.is_sleepy;


  if (*found)
    *next_offset = ftell (fp) - sizeof (INBOX_RECORD);
  release_lock(fileno(fp)); /* shared_lock.c */  
  fclose (fp);
}





static void
set_command_button_urls (char *course, char *key, long offset,
			   NEWS_FILTER * filter, SESSION *user, int can_write)
{

  long next_offset, prev_offset;
  int found;
  char fromstring[MAX_FROM + 1];
  char url[MAX_PATH + 1];

  snprintf(url, MAX_PATH, "%s?crs=%s&amp;id=%s","post_inbox", course, key);
  cs_set_value("post_inbox_url", url);
  cs_set_value("back_url", url);


  snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s", "post_outbox",course, key);
  cs_set_value("post_outbox_url", url);

  snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s", "main_menu", course, key);
  cs_set_value("main_menu_url", url);


  snprintf (fromstring, MAX_FROM + 1,
	    "%s%ccrs%c%s%cid%c%s%cloc%c%ld",
	    "post_read_inbox", QMARK_SUB, EQUAL_SUB, course, AMP_SUB, EQUAL_SUB,
	    key, AMP_SUB, EQUAL_SUB, offset);

  /* new memo button is activated only if can_write is true */
  if(can_write)
  {
     snprintf (url,MAX_PATH, "%s?crs=%s&amp;id=%s&amp;from=%s",
	  "post_new_memo", course, key, fromstring);
     cs_set_value("new_memo_url", url);
  }

   /* reply button is active only if can_write is true */
  if(can_write)
  {
     snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;from=%s&amp;loc=%ld&amp;grp=0",
	  "post_new_memo", course, key, fromstring, offset);
     cs_set_value("reply_url", url);
  }
     

/* group reply button */
  if (multiple_recipients (user))
  {
    if(can_write)
    {
       snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;from=%s&amp;loc=%ld&amp;grp=1",
	    "post_new_memo", course, key,
	    fromstring, offset);
       cs_set_value("group_reply_url", url);
    }

  }  


 /* forward button, only if can_write */
 if(can_write)
 {
  snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;src=1&amp;from=%s&amp;loc=%ld",
	  "post_forward_mail", course, key, fromstring, offset);
   cs_set_value("forward_url", url);
 }
 
 
 
 if (filter->oldest_first)
    get_next_offset (offset, &next_offset, &found, filter->show_user_hidden);
 else
    get_previous_offset (offset, &next_offset, &found, filter->show_user_hidden);

   if(can_write)
   {
    snprintf
      (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;next=%ld",
       "post_delete", course, key, offset, found ? next_offset : -1);
    cs_set_value("delete_url", url);
   }


  if (found)
  {
    snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;loc=%ld",
	    "post_read_inbox",
	    course, key, next_offset);
    cs_set_value("next_msg_url", url);
  }


  if (filter->oldest_first)
    get_previous_offset (offset, &prev_offset, &found, filter->show_user_hidden);
  else
    get_next_offset (offset, &prev_offset, &found, filter->show_user_hidden);



  if (found)
  {
    snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;loc=%ld",
	    "post_read_inbox",
	    course, key, prev_offset);
    cs_set_value("prev_msg_url", url);
  }


  snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;from=%s",
	  "post_inbox_info", course, key, offset, fromstring);
  cs_set_value("info_url", url);

}




/* reads the inbox file and then the corresponding *.inf file, to get everything
** ready to be displayed
**/
static void
process_inbox_record (long offset, INBOX_RECORD * inbox, INFO * infofile_info,
		      int *website_attached, CONFIG_STRUCT *conf, SESSION *user)
{
  char infopath[MAX_PATH + 1];
  FILE *fp;
  ATTACHMENT_NODE *aptr = 0;
  PERSON_NODE *pptr = 0;
  int i;
  PERSON person_buffer;


  snprintf (inbox_path, MAX_PATH + 1, "%s%s/%s/%s/%s",
	    conf->course_path, PEOPLE_DIR,user->username, MAIL_DIR, INBOX_FNAME);
  fp = fopen (inbox_path, "r+");
  if (!fp)
    cs_critical_error (ERR_FOPEN_APPEND_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 mark messages as read if we are in capture mode */
  if (!inbox->time_read && conf->access != ACCESS_CAPTURE)
    {
      inbox->time_read = time (NULL);
      mark_sender_files_read (inbox, conf, user);  /* shared_post_outbox.c */
      fseek (fp, offset, SEEK_SET);
      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);



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

  fp = fopen (infopath, "r");
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, infopath);
    
  get_shared_lock(fileno(fp), 1);    /* shared_lock.c */  
  if (fread (infofile_info, sizeof (INFO), 1, fp) != 1)
    cs_critical_error (ERR_FREAD_FAILED, infopath);


  /* process attachments */

  *website_attached = (infofile_info->no_attachments == -1);
  if (*website_attached)
    infofile_info->no_attachments = 1;	/* since we have to pick up the dirname */



  for (i = 1; i <= 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_FREAD_FAILED, 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;
    }


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















static void
read_filter (NEWS_FILTER * filter, CONFIG_STRUCT *conf, SESSION *user)
{
  FILE *fp;
  char filter_path[MAX_PATH + 1];

  snprintf (filter_path, MAX_PATH + 1, "%s%s/%s/%s/%s",
	    conf->course_path, PEOPLE_DIR, user->username, MAIL_DIR, NEWS_FILTER_FNAME);

  fp = fopen (filter_path, "r");
  if (fp)
    {
      fread (filter, sizeof (NEWS_FILTER), 1, fp);
      fclose (fp);
    }
  else
    {
      filter->oldest_first = 1;
      filter->show_user_hidden = 1;
    }
}






static void
set_inbox_msg_data (char *course, char *key, long offset, 
                   CONFIG_STRUCT *conf, SESSION *user, int can_write)
{
  INBOX_RECORD inbox;
  INFO infofile_info;
  NEWS_FILTER filter;
  int website_attached;
  char url[MAX_PATH*2];
  char msg_path[MAX_PATH + 1];
  
  read_filter (&filter, conf, user);
  process_inbox_record (offset, &inbox, &infofile_info, &website_attached, conf, user);
  cs_set_course_info(conf);
  cs_set_value("crs", course);
  cs_set_value("key", key);
  cs_set_bio_link(inbox.newfile_info.sender,course, conf, key, MAIL, 0, "user", 30);  /* shared_bio.c */

  set_command_button_urls (course, key, offset, &filter, user, can_write);
  
  /* shared_post_inbox.c */
  set_inbox_msg_header_data (&inbox, course, key, website_attached, offset,conf, user, attachment_head, person_head, -1); 
  
  
  snprintf (msg_path, MAX_PATH + 1, "%s%s/%s/%s/%s",
	    conf->course_path, PEOPLE_DIR, inbox.newfile_info.sender,  MAIL_DIR, infofile_info.msg_fname);

  set_msg_body_data (msg_path, "", "");    /* shared_msg_line.c */



  if(user->group == FACULTY)
   {
     cs_set_value("is_faculty", "1");
     
     if (conf->access != ACCESS_CAPTURE)
      {
        snprintf
          (url, MAX_PATH * 2, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;grp=%d&amp;inbox=yes",
                  "clipboard_copy", course, key, offset, MAIL);
         cs_set_value("clipboard_url", url);
      }
   }   


 cs_set_int_value("can_write", can_write? 1 : 0);

 if(conf->access == ACCESS_CAPTURE)
       cs_set_value("capture_mode", "1");
 
}


void
free_person_list (void)
{
  PERSON_NODE *ptr;

  while (person_head)
    {
      ptr = person_head;
      person_head = person_head->next;
      free (ptr);
    }
}




void
free_attachment_list (void)
{
  ATTACHMENT_NODE *ptr;

  while (attachment_head)
    {
      ptr = attachment_head;
      attachment_head = attachment_head->next;
      free (ptr);
    }
}



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 del_link;			/* from del_link=?command line  do we need to rm a symbolic link
				   ** to an attached web page?
				 */
				 
  int printer_friendly;         /* from print=? command line  provide "printer friendly" version? */				 
  int fixed_font;               /* from fixed=? command line  print body as fixed font? */				 
				 
   SESSION user;
   CONFIG_STRUCT conf;		/* the configuration read from config file */
   int can_write;               /* does this user have write permission? */
  
  cs_cgi_init(); 

  read_parameters (course, key, &offset, &del_link, &printer_friendly, &fixed_font);
  read_configuration_file (course, &conf);	/* shared_util.c */
  validate_key (key, &user, &conf);		/* shared_util.c */

  can_write = has_write_permission(&user, &conf);   	/* shared_access.c */
  
  cs_set_int_value("printer_friendly", printer_friendly);
  cs_set_int_value("fixed_font", fixed_font);

  set_inbox_msg_data (course, key, offset, &conf, &user, can_write);
  free_person_list ();

  if (del_link)
    remove_stale_symlinks (user.username, course, conf.access); /* shared_news_util.c */

  free_attachment_list ();


  cs_set_int_value("is_inbox", 1);

  /* NOTE: the post_read.cs ClearSilver template is used to process both 
  ** post_read_inbox.c and post_read_outbox.c
  */
  cs_cgi_display("post_read", 1);
  cs_cgi_destroy();

  return 0;
}

