#include <stdio.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#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_access.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_msg_line.h"
#include "manhat-lib/shared_bio.h"
#include "manhat-lib/shared_post_outbox.h"


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

static void
free_attachment_list (void)
{
  ATTACHMENT_NODE *ptr;

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


static void
free_person_list (void)
{
  PERSON_NODE *ptr;

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





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];

  cs_get_required_parameter ("crs", course, MAX_PATH);
  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 void
get_next_offset (const char *outbox_path, long offset,
		 long *next_offset, int *found, int show_user_hidden)
{
  FILE *fp;
  OUTBOX_RECORD data;

  fp = fopen (outbox_path, "r");
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, outbox_path);
   
  get_shared_lock(fileno(fp), 1);    /* shared_lock.c */  
  fseek (fp, offset, SEEK_SET);
  fread (&data, sizeof (OUTBOX_RECORD), 1, fp);	/* read THIS record */

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

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


static void
get_previous_offset (const char *outbox_path, long offset,
		     long *prev_offset, int *found, int show_user_hidden)
{
  FILE *fp;
  OUTBOX_RECORD data;


  fp = fopen (outbox_path, "r");
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, outbox_path);

  get_shared_lock(fileno(fp), 1); /* shared_lock.c */
  *prev_offset = offset;
  *found = 0;

  do
    {
      *prev_offset = (*prev_offset - sizeof (OUTBOX_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 (OUTBOX_RECORD), 1, fp);
      if(data.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
set_command_button_urls (char *course, char *key, long offset,
			    NEWS_FILTER * filter,
                            CONFIG_STRUCT *conf, SESSION *user, int can_write )
{
  char outbox_path[MAX_PATH + 1];
  long next_offset, prev_offset;
  int found;
  char url[MAX_PATH + 1];
  char fromstring[MAX_FROM + 1];


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


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


  /* goto inbox button */
  snprintf (url, MAX_PATH , "%s?crs=%s&amp;id=%s",
	  "post_inbox", course, key);
  cs_set_value("post_inbox_url", url);


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



/*the from parameter is the complete command line of this program. 
   ** substitutions are defined in global.h
   * */
 
  snprintf (fromstring, MAX_FROM + 1,
	    "%s%ccrs%c%s%cid%c%s%cloc%c%ld",
	    "post_read_outbox", QMARK_SUB, EQUAL_SUB, course, AMP_SUB, EQUAL_SUB,
	    key, AMP_SUB, EQUAL_SUB, offset);
  
 /* new memo button , only if can_write */
 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);
 }
  /* forward mail button, only if can_write */
  if(can_write)
  {
    snprintf
      (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;src=0&amp;from=%s",
       "post_forward_mail", course, key, offset, fromstring);
    cs_set_value("forward_url", url);
  }
    

  if (filter->oldest_first)
    get_next_offset (outbox_path, offset, &next_offset, &found, filter->show_user_hidden);
  else
    get_previous_offset (outbox_path, 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&amp;outbox=1",
       "post_delete", course, key, offset, found ? next_offset : -1);
     cs_set_value("delete_url", url);
   }
  
  /* read next button */
  if (found)
  {
    snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;loc=%ld",
	    "post_read_outbox",  course, key, next_offset);
    cs_set_value("next_msg_url", url);
  } 
  if (filter->oldest_first)
    get_previous_offset (outbox_path, offset, &prev_offset, &found, filter->show_user_hidden);
  else
    get_next_offset (outbox_path, offset, &prev_offset, &found, filter->show_user_hidden);

  /* read previous button */
  if (found)
  {
    snprintf (url, MAX_PATH, "%s?crs=%s&amp;id=%s&amp;loc=%ld",
	    "post_read_outbox",
	    course, key, prev_offset);
    cs_set_value("prev_msg_url", url);
  }

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


}



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

  snprintf (outbox_path, MAX_PATH + 1, "%s%s/%s/%s/%s",
                                         conf->course_path, PEOPLE_DIR, user->username, MAIL_DIR, OUTBOX_FNAME);
  fp = fopen (outbox_path, "r");
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, outbox_path);
    
  get_shared_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);
  release_lock(fileno(fp));   /* shared_lock.c */  
  fclose (fp);

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

  fp = fopen (infopath, "r");
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, infopath);

  get_shared_lock(fileno(fp), 1);   /* shared_lock.h */  
  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 in next loop */

  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, OUTBOX_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_outbox_msg_data (char *course, char *key, long offset, 
                       CONFIG_STRUCT *conf, SESSION *user, int can_write )
{
  OUTBOX_RECORD outbox;
  INFO infofile_info;
  NEWS_FILTER filter;
  int website_attached;
  char url[MAX_PATH + 1];
  char msg_path[MAX_PATH + 1];

  read_filter (&filter, conf, user);
  process_outbox_record (offset, &outbox, &infofile_info, &website_attached, conf, user);
  cs_set_course_info(conf);
  cs_set_value("crs", course);
  cs_set_value("key", key);

  set_command_button_urls (course, key, offset, &filter, conf, user, can_write);


  /* shared_post_outbox.c */
  set_outbox_msg_header_data(&outbox, 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, user->username,  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=no",
                  "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");





}




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;                /* can this user change this course? */
 
  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_outbox_msg_data (course, key, offset, &conf, &user, can_write);

  free_person_list ();		/* #included */

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

  free_attachment_list ();	/* #included */

  cs_set_int_value("is_inbox", 0);

  /* 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;
}


