#include <stdio.h>
#include <time.h>
#include <string.h>
#include <ctype.h>		/* for tolower() */
#include <sys/types.h>
#include <unistd.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_post_inbox.h"
#include "manhat-lib/shared_translate_url.h"  /* for AMP_SUB, et. al */
#include "manhat-lib/shared_access.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_post_outbox.h"
#include "manhat-lib/shared_msg_line.h"

#define GATHER_ALL "_all_"
#define GATHER_UNREAD "_unread_"
#define GATHER_LAST   "_last_"



typedef struct msg_node
{
  INBOX_RECORD inbox;
  INFO infofile_info;
  ATTACHMENT_NODE *attachment_head;
  PERSON_NODE *person_head;
  int number_recipients;
  int website_attached;
  long offset;
  struct msg_node *next;
}
MSG_NODE;


MSG_NODE *msg_head = 0;

typedef enum gather_type
{ UNREAD, ALL, USERNAME, LAST }
GATHER_TYPE;




typedef struct gather_struct
{
  char username[MAX_USERNAME + 1];
  char realname[MAX_NAME + 1];
  GATHER_TYPE target;
}
GATHER_STRUCT;






static void
read_parameters (char *course, char *key, char *gather,
		 int *del_link, char *topic_prog,
                int *printer_friendly, int *fixed_font)
{
  char tmp[MAX_FILENAME + 1];	/* the long offset value,etc */

  cs_get_required_parameter ("crs", course, MAX_FILENAME);
  cs_get_required_parameter ("id", key, MAX_KEY);

  cs_get_optional_parameter ("gather", gather, MAX_PATH);
  if( !strlen(gather))
      strncpy (gather, GATHER_LAST, MAX_PATH + 1);

  snprintf (topic_prog, MAX_PATH + 1, "%s?crs=%s&amp;id=%s", "post_inbox", course, key);

  /* just check if the "del_link" parameter is there */
  cs_get_optional_parameter ("del_link", tmp, 5);
  *del_link = strlen(tmp);
  
  
      /* provide printer-friendly version? */
  cs_get_optional_parameter ("print", tmp, 5);              /* shared_cs_util.c */
  *printer_friendly = *tmp? (atoi(tmp)? 1 : 0) :  0;


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


}




static void
parse_gather_string (char *gather, GATHER_STRUCT * gather_target)
{
  char *ptr;

  *gather_target->username = '\0';
  *gather_target->realname = '\0';

  if (!strcmp (gather, GATHER_ALL))
    gather_target->target = ALL;
  else if (!strcmp (gather, GATHER_UNREAD))
    gather_target->target = UNREAD;
  else if (!strcmp (gather, GATHER_LAST))
    gather_target->target = LAST;
  else
    {
      ptr = strchr (gather, ':');
      if (ptr)
	{
	  *ptr = '\0';
	  strncpy (gather_target->username, gather, MAX_USERNAME);
	  strncpy (gather_target->realname, ptr + 1, MAX_NAME);
	  *ptr = ':';
	  gather_target->target = USERNAME;
	}
      else
	cs_critical_error (ERR_GATHER_PARSE_ERROR, gather);

    }
}





static int
meets_gather_criteria (GATHER_STRUCT * criteria, INBOX_RECORD * msg)
{
  switch (criteria->target)
    {
    case UNREAD:
      return (msg->time_read == 0);
      break;
    case ALL:
      return 1;
      break;
    case USERNAME:
      return (!strcmp (msg->newfile_info.sender, criteria->username));
      break;
    case LAST:
    default:
      cs_critical_error (ERR_UNKNOWN_GATHER_TYPE, "");
      break;
    }
  return 0;
}


static void
memory_error ()
{
  cs_critical_error (ERR_MALLOC_FAILED, "");
}



static void
read_inf_file (MSG_NODE * msg, CONFIG_STRUCT *conf)
{
  FILE *fp;
  char infopath[MAX_PATH + 1];
  int i;
  ATTACHMENT_NODE *aptr = 0;
  PERSON_NODE *pptr = 0;
  PERSON person_buffer;

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

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

  /* process attachments, build linked list */
  msg->website_attached = (msg->infofile_info.no_attachments == -1);
  if (msg->website_attached)
    msg->infofile_info.no_attachments = 1;	/* since we have to pick up the dirname */

  for (i = 1; i <= msg->infofile_info.no_attachments; i++)
    {
      if (!msg->attachment_head)
	{
	  msg->attachment_head =
	    (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!msg->attachment_head)
	    memory_error ();
	  aptr = msg->attachment_head;
	}
      else
	{
	  aptr->next = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!aptr->next)
	    memory_error ();
	  aptr = aptr->next;
	}

      if (fread (&aptr->data, sizeof (ATTACHMENT), 1, fp) != 1)
	cs_critical_error (ERR_FREAD_FAILED, "");
      aptr->next = 0;
    }				/* for .. */

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

      msg->number_recipients++;
      pptr->data = person_buffer;
      pptr->next = 0;
    }

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

}


static void
build_new_list (GATHER_STRUCT * gather_criteria, CONFIG_STRUCT *conf, SESSION *user)
{
  FILE *fp;
  char inbox_path[MAX_PATH + 1];
  INBOX_RECORD msg;
  MSG_NODE *msg_ptr = 0;
  long offset = 0;

  snprintf (inbox_path, MAX_PATH + 1, "%s/%s", discussion_path, INBOX_FNAME);

  fp = fopen (inbox_path, "r+");
  if (fp)
    {
      get_exclusive_lock(fileno(fp), 1);
      
      while (fread (&msg, sizeof (INBOX_RECORD), 1, fp) == 1)
	{
	  if ( (msg.newfile_info.how_sent != DELETED) && 
	               meets_gather_criteria (gather_criteria, &msg))
	    {
	      if (!msg_head)
		{
		  msg_head = (MSG_NODE *) malloc (sizeof (MSG_NODE));
		  if (!msg_head)
		    memory_error ();
		  msg_ptr = msg_head;
		}
	      else
		{
		  msg_ptr->next = (MSG_NODE *) malloc (sizeof (MSG_NODE));
		  if (!msg_ptr->next)
		    memory_error ();
		  msg_ptr = msg_ptr->next;
		}
	      msg_ptr->inbox = msg;
	      msg_ptr->offset = offset;
	      msg_ptr->next = 0;
	      msg_ptr->attachment_head = 0;
	      msg_ptr->person_head = 0;

	      read_inf_file (msg_ptr, conf);
	      if (!msg_ptr->inbox.time_read)
		{
		  msg_ptr->inbox.time_read = time (NULL);
                  mark_sender_files_read (&(msg_ptr->inbox), conf, user);  /* shared_post_outbox.c */
		  if (fseek (fp, offset, SEEK_SET))
		    cs_critical_error (ERR_FSEEK_FAILED, "");
		  if (fwrite (&(msg_ptr->inbox), sizeof (INBOX_RECORD), 1, fp) != 1)
		    cs_critical_error (ERR_FWRITE_FAILED, "");
		}
	    }			/* if meets_gather_criteria ... */
	  offset = ftell (fp);
	}			/* while fread(... */
      release_lock(fileno(fp));
      fclose (fp);
    }				/* if fp */
}




static void
last_io_error ()
{
  cs_critical_error (ERR_LAST_FILE_IO_ERROR, "");
}





static void
write_one_message (MSG_NODE * msg_ptr, FILE * fp)
{
  ATTACHMENT_NODE *attach_ptr;
  PERSON_NODE *person_ptr;

  if (fwrite (msg_ptr, sizeof (MSG_NODE), 1, fp) != 1)
    last_io_error ();
  for (attach_ptr = msg_ptr->attachment_head; attach_ptr;
       attach_ptr = attach_ptr->next)
    {
      if (fwrite (attach_ptr, sizeof (ATTACHMENT_NODE), 1, fp) != 1)
	last_io_error ();
    }
  for (person_ptr = msg_ptr->person_head; person_ptr;
       person_ptr = person_ptr->next)
    {
      if (fwrite (person_ptr, sizeof (PERSON_NODE), 1, fp) != 1)
	last_io_error ();
    }
}





static void
write_last_list (GATHER_STRUCT * gather_criteria)
{
  char last_path[MAX_PATH + 1];
  FILE *fp;
  MSG_NODE *msg_ptr;

  snprintf (last_path, MAX_PATH + 1, "%s%s", discussion_path, GATHER_LAST_INBOX_FNAME);
  fp = fopen (last_path, "w");
  if (!fp)
    last_io_error ();
  if (fwrite (gather_criteria, sizeof (GATHER_STRUCT), 1, fp) != 1)
    last_io_error ();
  if (gather_criteria->target == UNREAD)
    {
      for (msg_ptr = msg_head; msg_ptr; msg_ptr = msg_ptr->next)
	write_one_message (msg_ptr, fp);
    }
  fclose (fp);
}



static void
read_attachment_data (int no_attachments, MSG_NODE * msg_ptr, FILE * fp)
{
  ATTACHMENT_NODE *a_ptr = 0;
  ATTACHMENT_NODE attach;
  int i;

  if (no_attachments == -1)	/* -1 flags website attached */
    no_attachments = 1;

  for (i = 1; i <= no_attachments; i++)
    {
      if (fread (&attach, sizeof (ATTACHMENT_NODE), 1, fp) != 1)
	last_io_error ();
      if (!msg_ptr->attachment_head)
	{
	  msg_ptr->attachment_head
	    = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!msg_ptr->attachment_head)
	    memory_error ();
	  a_ptr = msg_ptr->attachment_head;
	}
      else
	{
	  a_ptr->next = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!a_ptr->next)
	    memory_error ();
	  a_ptr = a_ptr->next;
	}
      *a_ptr = attach;
      a_ptr->next = 0;
    }
}



static void
read_recipient_data (int number_recipients, MSG_NODE * msg_ptr, FILE * fp)
{
  PERSON_NODE *p_ptr = 0;
  PERSON_NODE person;
  int i;


  for (i = 1; i <= number_recipients; i++)
    {
      if (fread (&person, sizeof (PERSON_NODE), 1, fp) != 1)
	last_io_error ();
      if (!msg_ptr->person_head)
	{
	  msg_ptr->person_head
	    = (PERSON_NODE *) malloc (sizeof (PERSON_NODE));
	  if (!msg_ptr->person_head)
	    memory_error ();
	  p_ptr = msg_ptr->person_head;
	}
      else
	{
	  p_ptr->next = (PERSON_NODE *) malloc (sizeof (PERSON_NODE));
	  if (!p_ptr->next)
	    memory_error ();
	  p_ptr = p_ptr->next;
	}
      *p_ptr = person;
      p_ptr->next = 0;
    }
}





static void
build_last_list (FILE * fp)
{
  MSG_NODE msg;
  MSG_NODE *msg_ptr = 0;

  while (fread (&msg, sizeof (MSG_NODE), 1, fp) == 1)
    {
      if (!msg_head)
	{
	  msg_head = (MSG_NODE *) malloc (sizeof (MSG_NODE));
	  if (!msg_head)
	    memory_error ();
	  msg_ptr = msg_head;
	}
      else
	{
	  msg_ptr->next = (MSG_NODE *) malloc (sizeof (MSG_NODE));
	  if (!msg_ptr->next)
	    memory_error ();
	  msg_ptr = msg_ptr->next;
	}

      *msg_ptr = msg;
      msg_ptr->next = 0;
      msg_ptr->attachment_head = 0;
      msg_ptr->person_head = 0;
      if (msg_ptr->infofile_info.no_attachments)
	read_attachment_data (msg_ptr->infofile_info.no_attachments, msg_ptr,
			      fp);
      if (msg_ptr->number_recipients)
	read_recipient_data (msg_ptr->number_recipients, msg_ptr, fp);
    }
}


static void
read_last_criteria (GATHER_STRUCT * gather_criteria)
{
  char last_path[MAX_PATH + 1];
  FILE *fp;

  snprintf (last_path, MAX_PATH + 1, "%s%s", discussion_path,
	    GATHER_LAST_INBOX_FNAME);
  fp = fopen (last_path, "r");
  if (!fp)
    last_io_error ();
  if (fread (gather_criteria, sizeof (GATHER_STRUCT), 1, fp) != 1)
    last_io_error ();

  if (gather_criteria->target == UNREAD)
    build_last_list (fp);
  fclose (fp);
}





static void
build_message_list (GATHER_STRUCT * gather_criteria,
                    CONFIG_STRUCT *conf, SESSION *user)
{
  if (gather_criteria->target == LAST)
    {
      read_last_criteria (gather_criteria);
      if (gather_criteria->target == UNREAD)
	return;
    }

  build_new_list (gather_criteria, conf, user);
  write_last_list (gather_criteria);

}







static void
set_common_values (char *course, char *key, int can_write, GATHER_STRUCT * gather_criteria, char *topic_prog,
                     CONFIG_STRUCT *conf)
{

 char fromstring[MAX_PATH + 1];  	/* how to get back to THIS page... */
 char url[MAX_PATH + 1];
 

  cs_set_course_info(conf);
  cs_set_current_time();
  cs_set_value("back_url", topic_prog);
  
  cs_set_value("crs", course);
  cs_set_value("key", key);
  cs_set_int_value("can_write", can_write? 1: 0);
  cs_set_int_value("is_inbox", 1);  

  switch(gather_criteria -> target)
  {
  case UNREAD:   cs_set_value("gather_target", "UNREAD"); break;
  case ALL:      cs_set_value("gather_target", "ALL"); break;
  case USERNAME: cs_set_value("gather_target", "USERNAME"); 
                 cs_set_value("gather_target_realname", gather_criteria->realname);
                 break;
  case LAST:     cs_set_value("gather_target", "LAST"); break;
  default:       cs_set_value("gather_target", "");   break;    /* should never happen? */
  }


  snprintf (fromstring, MAX_PATH + 1,
		"from=%s%ccrs%c%s%cid%c%s%cgather%c_last_",
		"post_inbox_gather", QMARK_SUB, EQUAL_SUB, course,
		AMP_SUB, EQUAL_SUB, key, AMP_SUB, EQUAL_SUB);


  snprintf (url, MAX_PATH + 1, "%s?crs=%s&amp;id=%s&amp;%s",
	  "post_inbox_info", course, key, fromstring);
  cs_set_value("info_partial_url", url);


  
   if(can_write)
    {
     snprintf (url, MAX_PATH + 1, "%s?crs=%s&amp;id=%s&amp;%s",
	  "post_new_memo", course, key, fromstring);
     cs_set_value("new_memo_url", url);
     
     
    snprintf (url, MAX_PATH + 1, "%s?crs=%s&amp;id=%s&amp;src=1&amp;%s",
	  "post_forward_mail", course, key, fromstring);

     cs_set_value("forward_partial_url", url);
     
     
    }

          
}







static void
set_one_message (MSG_NODE * msg_ptr, char *course, char *key,
                   CONFIG_STRUCT *conf, SESSION *user, int can_write, int msg_no)
{

 char msg_path[MAX_PATH + 1];
 char name_prefix[30];
  
  /* shared_post_inbox.c */
  set_inbox_msg_header_data (&(msg_ptr->inbox), course, key, msg_ptr->website_attached, 
                                msg_ptr->offset, conf, user, msg_ptr->attachment_head, msg_ptr->person_head, msg_no); 


  snprintf (msg_path, MAX_PATH + 1, "%s/%s/%s/%s/%s", conf->course_path, PEOPLE_DIR,
	    						msg_ptr->inbox.newfile_info.sender, MAIL_DIR, 
	    						msg_ptr->infofile_info.msg_fname);

  snprintf(name_prefix, 30, "msg.%d.", msg_no);
  
  set_msg_body_data (msg_path, name_prefix, "");   /* shared_msg_line.c */


}



static void
free_message_list ()
{
  MSG_NODE *msg_ptr;
  ATTACHMENT_NODE *attach_ptr;
  PERSON_NODE *person_ptr;

  while (msg_head)
    {
      msg_ptr = msg_head;
      msg_head = msg_head->next;
      while (msg_ptr->attachment_head)
	{
	  attach_ptr = msg_ptr->attachment_head;
	  msg_ptr->attachment_head = msg_ptr->attachment_head->next;
	  free (attach_ptr);
	}
      while (msg_ptr->person_head)
	{
	  person_ptr = msg_ptr->person_head;
	  msg_ptr->person_head = msg_ptr->person_head->next;
	  free (person_ptr);
	}
      free (msg_ptr);
    }
}





static void
gather_msgs (char *course, char *key, char *gather,
	    char *topic_prog,  CONFIG_STRUCT *conf, SESSION *user, int can_write)
{
  GATHER_STRUCT gather_criteria;
  MSG_NODE *msg_ptr;
  int msg_no;

  parse_gather_string (gather, &gather_criteria);

  build_message_list (&gather_criteria, conf, user);


  set_common_values (course, key, can_write, &gather_criteria, topic_prog, conf);
  for (msg_no = 0, msg_ptr = msg_head; msg_ptr; msg_ptr = msg_ptr->next, msg_no++)
  {
    set_one_message (msg_ptr, course, key, conf, user, can_write, msg_no);
  }
  cs_set_int_value("msg_count", msg_no);
  
  free_message_list ();

}




int
main (void)
{
  char course[MAX_PATH + 1];	/* from crs=? command line */
  char key[MAX_KEY + 1];	/* from id=? command line */
  char gather[MAX_PATH + 1];	/* from gather=? command line */
  int del_link;			/* optional command line argument */


  int printer_friendly;         /* from print=? command line  provide "printer friendly" version? */				 
  int fixed_font;               /* from fixed=? command line  print body as fixed font? */				 


  char topic_prog[MAX_PATH + 1];	/* arguments to POST_INBOX program */
  SESSION user;
  CONFIG_STRUCT conf;		/* the configuration read from config file */
  int can_write;                 /* does this user have write access? */

  cs_cgi_init();
  read_parameters (course, key, gather, &del_link, topic_prog, &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 */

  set_discussion_names (conf.course_path, user.username, MAIL);	/* shared_news_util.c */

/* see custom.h  for next */
#ifndef SUPPORT_PRE24_POST_OFFICE_MSGS
       update_post_inbox (user.username, &conf);	/* shared_post_inbox.c */
#else
  if(SUPPORT_PRE24_POST_OFFICE_MSGS)
       update_post_inbox (user.username, &conf);	/* shared_post_inbox.c */
      

#endif


   cs_set_int_value("printer_friendly", printer_friendly);
   cs_set_int_value("fixed_font", fixed_font);


  gather_msgs (course, key, gather, topic_prog, &conf, &user, can_write);

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

 /* The 'post_read.lang' file is shared by several programs */
 cs_load_lang("post_read"); 
 
 /* The post_gather ClearSilver template is used by both this program and post_outbox_gather */
 cs_cgi_display("post_gather", 0);
 cs_cgi_destroy();
  return 0;
}
