#include <stdio.h>
#include <dirent.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>		/* for unlink() */

#include "global.h"

#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_person_list.h"
#include "manhat-lib/shared_news_util.h"
#include "manhat-lib/shared_post_outbox.h"
#include "manhat-lib/shared_translate_url.h"
#include "manhat-lib/shared_access.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_system_data.h"

typedef struct outbox_node
{
  OUTBOX_RECORD data;
  struct outbox_node *next;
  struct outbox_node *prev;
  long offset;
}
OUTBOX_NODE;


OUTBOX_NODE *outbox_head = 0;
OUTBOX_NODE *outbox_tail = 0;




static void
read_parameters (char *course, char *key)
{

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

}




static void
set_outbox_commands (const char *course, const char *key,  
                                   int can_write, int access, const char *fromstring)
{
 char url[MAX_PATH +1];


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

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

 cs_set_int_value("outbox", 1);
}





static void
build_outbox_list (CONFIG_STRUCT *conf, SESSION *user)
{
  FILE *fp;
  char outbox_path[MAX_PATH + 1];
  OUTBOX_RECORD msg;
  OUTBOX_NODE *ptr = 0;

  long offset = 0;

  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)
    {
      get_shared_lock(fileno(fp), 1);
      while (fread (&msg, sizeof (OUTBOX_RECORD), 1, fp) == 1)
	{
	  if (msg.how_sent != DELETED)
	    {
	      /* add the record to the end of the linked list */
	      if (!outbox_head)
		{
		  outbox_head = (OUTBOX_NODE *) malloc (sizeof (OUTBOX_NODE));
		  if (!outbox_head)
		    cs_critical_error (ERR_MALLOC_FAILED, "");
		  ptr = outbox_head;
		  ptr->prev = 0;
		}
	      else
		{
		  ptr->next = (OUTBOX_NODE *) malloc (sizeof (OUTBOX_NODE));
		  if (!ptr->next)
		    cs_critical_error (ERR_MALLOC_FAILED, "");
		  ptr->next->prev = ptr;
		  ptr = ptr->next;
		}
	      ptr->data = msg;
	      ptr->offset = offset;
	      ptr->next = 0;
	      outbox_tail = ptr;

	    }
	  offset = ftell (fp);
	}
      release_lock(fileno(fp));
      fclose (fp);
    }
}




static void
free_outbox_list ()
{
  OUTBOX_NODE *ptr;

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




void
set_outbox_msgs (const char *course, const char *key, NEWS_FILTER *filter, CONFIG_STRUCT *conf, 
                     SESSION *user, const char *fromstring, int can_write, int allow_user_hidden)
{
#define MAX_TMP_NAME 30
  char timestring[MAX_TIMESTRING + 1];
  char *cptr;
  OUTBOX_NODE *ptr;
  int several_recipients;          /* does the message have more than one recipient? */
  int i =0;
  int str_index = 0, recipient_number = 0, partial_to_list = 0;
  char url[MAX_PATH +1];
  char temp[MAX_NAME +1];
  char name[MAX_TMP_NAME ];
  char *escaped_subject = (char *) 0;
  int fixed_width_font;
  
  fixed_width_font = USE_FIXED_FONT(conf->misc);    /* global.h */

  cs_set_int_value("fixed_font", fixed_width_font? 1: 0);
  cs_set_int_value("allow_user_hidden", allow_user_hidden? 1 : 0);
  
  if (outbox_head)
    {
      for (i = 0, ptr = filter->oldest_first ? outbox_head : outbox_tail;
	   ptr; ptr = filter->oldest_first ? ptr->next : ptr->prev, i++)
	{

         if(!allow_user_hidden || filter->show_user_hidden || !ptr->data.is_sleepy) 
	  {
	    several_recipients = strchr (ptr->data.to, '|')? 1 : 0;
          
            if(allow_user_hidden) 	    
	    {
  	       snprintf(url, MAX_PATH +1, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;outbox=1&amp;hide=%d", 
                        "post_mark_hidden", course, key, ptr->offset,
                        ptr->data.is_sleepy?0:1);

	       snprintf(name, MAX_TMP_NAME, "msg.%d.%s", i, ptr->data.is_sleepy? "unhide_url" : "hide_url");
	       cs_set_value(name, url);

	       snprintf(name, MAX_TMP_NAME, "msg.%d.is_sleepy", i);
	       cs_set_int_value(name, ptr->data.is_sleepy);
	    }

            snprintf(name, MAX_TMP_NAME, "msg.%d.is_unread", i);
            cs_set_int_value(name, ptr->data.unread? 1 : 0);


            snprintf(name, MAX_TMP_NAME, "msg.%d.has_attachments", i);
            cs_set_int_value(name, ptr->data.attachments? 1 : 0);
           
	    snprintf
	       (url, MAX_PATH +1, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;from=%s",
	           "post_outbox_info", course, key, ptr->offset, fromstring);
	    snprintf(name, MAX_TMP_NAME, "msg.%d.info_url", i);
            cs_set_value(name, url); 

	    snprintf(name, MAX_TMP_NAME, "msg.%d.read_msg_url", i);
	    snprintf (url, MAX_PATH +1, "%s?crs=%s&amp;id=%s&amp;loc=%ld",
	  	  "post_read_outbox", course, key, ptr->offset);
           if(fixed_width_font)
                strcat(url,"&fixed=1");		  
            cs_set_value(name, url); 

            /* handle subject - need to html escape it */
	    snprintf(name, MAX_TMP_NAME, "msg.%d.subject", i);
	    if(cgi_html_escape_strfunc(ptr->data.subject, &escaped_subject) == STATUS_OK)
	      cs_set_value(name, escaped_subject);
           else
              cs_set_value(name,  ptr->data.subject);
              
           if(escaped_subject)
             {
               free(escaped_subject);
               escaped_subject = (char *) 0;
             }     


	  strftime (timestring, MAX_TIMESTRING, system_data_str("DOW_DATE_FORMAT"),
		    localtime (&ptr->data.time_sent));
          snprintf(name, MAX_TMP_NAME, "msg.%d.time_sent", i);
          cs_set_value(name, timestring); 		

          /* insert delete link */
          if(can_write)
	   {
              snprintf(url, MAX_PATH +1, "%s?crs=%s&amp;id=%s&amp;loc=%ld&amp;next=-1&amp;outbox=1",
                           "post_delete", course, key, ptr->offset);
              snprintf(name, MAX_TMP_NAME, "msg.%d.delete_url", i);
              cs_set_value(name, url); 		
	   } 



          snprintf(name, MAX_TMP_NAME, "msg.%d.tolist", i);
          cs_set_value(name, ptr->data.to);
          

	  /* print list of recipients */
	  if (!several_recipients)	/* faster for one recipient */
	  {
	    snprintf(name, MAX_TMP_NAME, "msg.%d.to.0", i);
	    cs_set_value(name, ptr->data.to); 
	  }
	  else
	  {
	   recipient_number = 0;
	   str_index = 0;
	   partial_to_list = 0;
	   for (cptr = ptr->data.to; *cptr; cptr++)
	    {
	      if (*cptr == '|')   /* we've reached the end of a name */
		{
		  if (*(cptr + 1) == '\0')
	            {
	              partial_to_list = 1;
	            }

                   snprintf(name, MAX_TMP_NAME, "msg.%d.to.%d", i, recipient_number);
  	           cs_set_value(name, temp);
		   str_index = 0;

		   recipient_number++;   
		}
	       else
	        {
		  temp[str_index] = *cptr;
		  temp[str_index + 1] = '\0'; 
		  str_index++;
	        }
	     }
	    
	    if(partial_to_list)
	     {
	       snprintf(name, MAX_TMP_NAME, "msg.%d.partial_to_list", i);
	       cs_set_int_value(name, 1);
	     }
	    else
	     {
	      snprintf(name, MAX_TMP_NAME, "msg.%d.to.%d", i, recipient_number);
  	      cs_set_value(name, temp);
  	     } 
		   
            }
	}  /* if(!allow_user_hidden ... */
      }                         /* for(i = 0 ... */	
    }				/* if outbox_head */
#undef MAX_TMP_NAME 

}






static void
read_outbox_filter (NEWS_FILTER * filter, CONFIG_STRUCT *conf, SESSION *user)
{
  FILE *fp;
  char filter_path[MAX_PATH + 1];
  
  filter->oldest_first = 1;
  filter->show_user_hidden = 1;
  
  /* always use defaults for capture mode */
  if(conf->access == ACCESS_CAPTURE)
      return;

  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)
    {
         if(fread (filter, sizeof (NEWS_FILTER), 1, fp) != 1)
          {
           filter->oldest_first = 1;
           filter->show_user_hidden = 1;
          } 
         fclose (fp);
    }
}






static void
set_gather_form (const char *course, const char *key,
		   int oldestfirst,
                   CONFIG_STRUCT *conf,
                   const char *fromstring)
{
  P_NODE *p;

  cs_set_value("gather_action", "post_outbox_gather");
  
  p = build_option_person_list (conf);  /* shared_person_list.c */
  set_option_userlist (p);	/* shared_person_list.c */
  free_option_person_list (p);	/* shared_person_list.c */

}




static
void set_sort_url(char *course, char *key, int oldestfirst, const char *fromstring)
{
 char url[MAX_PATH +1];


    snprintf
      (url, MAX_PATH +1, "%s?crs=%s&amp;id=%s&amp;grp=%d&amp;cmd=%d&amp;from=%s",
       "news_filter_set", course, key, MAIL, oldestfirst? 1: 0, fromstring);
     cs_set_value("sort_order_url", url);
}





static void
set_sleepy_urls(NEWS_FILTER *filter,const char *fromstring, const char *course, const char *key)
{
#define LARGE_STRING 512   

 OUTBOX_NODE *ptr;
 int hidden_count;
 char url[LARGE_STRING +1];
    
  /* count topics marked as 'hidden' by the user */
  for(ptr=outbox_head, hidden_count=0; ptr; ptr=ptr->next)
          if(ptr->data.is_sleepy)
              hidden_count++;   
  cs_set_int_value("hidden_count", hidden_count);
         
    
 if(filter->show_user_hidden)
    {
       snprintf (url, LARGE_STRING + 1, "%s?crs=%s&amp;id=%s&amp;grp=%d&amp;cmd=%d&amp;from=%s", 
	    "news_filter_set", course, key, MAIL, HIDE_USER_HIDDEN, fromstring);
       cs_set_value("hide_sleepy_url", url);
    }   
       
   else
    {
      snprintf (url,  LARGE_STRING + 1, "%s?crs=%s&amp;id=%s&amp;grp=%d&amp;cmd=%d&amp;from=%s",
   			"news_filter_set", course, key, MAIL, SHOW_USER_HIDDEN, fromstring);
      cs_set_value("list_all_url", url);

	    
                       
    }  

#undef LARGE_STRING
}








static void
set_outbox_data (char *course, char *key, CONFIG_STRUCT *conf, SESSION *user, int can_write)
{

  NEWS_FILTER filter;
  char fromstring[MAX_PATH + 1];
  int allow_user_hidden;

  cs_set_value("crs", course);
  cs_set_value("id", key);

  cs_set_course_info(conf);
  cs_set_current_time();

  cs_set_int_value("can_write", can_write? 1 : 0 );
  cs_set_int_value("capture_mode", (conf->access == ACCESS_CAPTURE)? 1: 0);

  
  allow_user_hidden = (conf->postoffice == 2);

  read_outbox_filter (&filter, conf, user);
  build_outbox_list (conf, user);
  snprintf(fromstring, MAX_PATH + 1, "%s%ccrs%c%s%cid%c%s",
              "post_outbox",QMARK_SUB,EQUAL_SUB,course,AMP_SUB,EQUAL_SUB,key);

 
  set_outbox_commands (course, key, can_write, conf->access,fromstring);
  
  /* show gather form, reverse order button and show sleepy options
  ** only if we are not
  ** capturing this course
  */
  if(conf->access != ACCESS_CAPTURE)
  {
     set_gather_form (course, key, filter.oldest_first, conf, fromstring);
     set_sort_url(course, key, filter.oldest_first, fromstring);
     if(allow_user_hidden)
            set_sleepy_urls(&filter, fromstring, course, key);
   }

  set_outbox_msgs (course, key, &filter, conf, user, fromstring, can_write, allow_user_hidden);
  free_outbox_list ();
}




int
main (void)
{
  char course[MAX_PATH + 1];	/* from crs=? command line */
  char key[MAX_KEY + 1];	/* from id=? command line */
  SESSION user;
  CONFIG_STRUCT conf;		/* the configuration read from config file */
  int can_write;                /* does this user have write access to this course */
 

  cs_cgi_init(); 

  read_parameters (course, key);
  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 */
  

/** see global.h for next **/
#ifndef SUPPORT_PRE24_POST_OFFICE_MSGS
        update_post_outbox (&conf, &user);	/* shared_post_outbox.c */
#else
  if(SUPPORT_PRE24_POST_OFFICE_MSGS)
        update_post_outbox (&conf, &user);	/* shared_post_outbox.c */   
#endif

  if(!conf.postoffice)
     cs_critical_error(ERR_REQUEST_DENIED, "");

  set_outbox_data (course, key, &conf, &user, can_write);

  /* NOTE: post_inbox_outbox.cs is used for both post_inbox.c and post_outbox.c */ 
  cs_cgi_display("post_inbox_outbox", 1);
  cs_cgi_destroy();
  return 0;
}

