#include <stdio.h>
#include <string.h>
#include <ctype.h>		/* for tolower() */
#include <fcntl.h>		/* for mkdir() */

#include <sys/stat.h>		/* for stat() */
#include <unistd.h>

#include <sys/types.h>		/* for exec(), wait() et. al */
#include <sys/wait.h>

#include <errno.h>
#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_clipboard_util.h"
#include "manhat-lib/shared_copy_file.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_post_inbox.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_fold_file.h"

typedef struct html_file_node
{
  char fname[MAX_FILENAME + 1];
  struct html_file_node *next;
}
HTML_FILE_NODE;

HTML_FILE_NODE *html_file_head;

SESSION user;
CONFIG_STRUCT conf;		/* the configuration read from config file */

RECEIVER_NODE *to_head = 0;

/* next two apply to POST OFFICE messages */
RECEIVER_NODE *cc_head = 0;	/* who was carbon copied? */
RECEIVER_NODE *bcc_head = 0;	/* who was blind carbon copied? */



FILE_NODE *file_head = (FILE_NODE *) 0;	/*list of attached files */
int attachments = 0;		/* how many files are attached? - 
				   **same as number of nodes pointed to 
				   ** by file_head
				 */



FILE_NODE *pasted_file_head = (FILE_NODE *) 0;	/* list of 'pasted' files */
int pasted_attachments = 0;	/* how many pasted files are attached? - 
				   **same as number of nodes pointed to 
				   ** by pasted_file_head
				 */


int attachments_written = 0;	/* set to 1 when uploaded files are written to disk */




/* deletes all files in list headed by file_head 
** called to clean up sender directory when a 
** critical error occurs after the files were written
*/
void
delete_attachment_files ()
{
  FILE_NODE *ptr;
  char fname[MAX_PATH + 1];

  for (ptr = file_head; ptr; ptr = ptr->next)
    {
      snprintf (fname, MAX_PATH + 1, "%s/%s", discussion_path,
		ptr->unique_fname);
      unlink (fname);
    }
}




void
big_error (const char* error_no, char *msg)
{
  if (attachments_written)
    delete_attachment_files ();
  cs_critical_error (error_no, msg);
}





static void
read_parameters (char *course, char *key, char *fromprog,
		  int *website,	 int *pasted_website, int *pasted_files, int *width)
{
  char string[80];


  cs_get_required_parameter ("crs", course, MAX_PATH);
  cs_get_required_parameter ("id", key, MAX_KEY);
  cs_get_required_parameter ("from", fromprog, MAX_PATH);
  cs_get_required_parameter ("width", string, 10);
  *width = atoi(string);

  cs_get_optional_parameter("website", string, 79);
  if(strlen(string))
    *website = 1; 
  else 
    *website = 0; 

  cs_get_optional_parameter("pasted_website", string, 79);
  if(strlen(string))
     *pasted_website = atoi(string);
  else  
     *pasted_website = 0;

  cs_get_optional_parameter("pasted_files", string, 79);
  if(strlen(string))
    *pasted_files = atoi(string);
  else
    *pasted_files = 0;

}








int
is_blank (char *string)
{
  char *ptr;

  for (ptr = string; *ptr; ptr++)
    if (!isspace (*ptr))
      return 0;

  return 1;
}














/* make sure no empty files have been uploaded and that there is an entry in
** the first file field, that is 'file1' must not be empty.
**/

static void
check_website_individual_files ()
{
 #define MAX_TMP_NAME 30
 int homefile = 0;
 int i;
 char name[MAX_TMP_NAME];
 char filename[MAX_PATH +1];
 FILE *fp;
 struct stat buf;

  for(i =1; i<= conf.mail_attachments; i++)
  {
    snprintf(name, MAX_TMP_NAME, "file%d", i);
    cs_get_optional_parameter(name, filename, MAX_PATH);

    if (strlen(filename))
    {
        fp = cgi_filehandle(global_cgi, name);
        if(fstat(fileno(fp), &buf))
             cs_critical_error (ERR_FOPEN_READ_FAILED, filename);
        if (buf.st_size == 0)
             big_error(ERR_EMPTY_FILE, filename);
        else if (!strcmp (name, "file1"))
             homefile = 1;
     }
  }
  if (!homefile)
    big_error (ERR_FIRST_ATTACHMENT_FIELD_BLANK, "");
  #undef MAX_TMP_NAME

}


static void
strtolower (char *string)
{
  char *ptr;

  for (ptr = string; *ptr; ptr++)
    *ptr = tolower (*ptr);
}

void
check_more_than_one_files(CONFIG_STRUCT *conf)
{
   int i;
   char fname[MAX_PATH +1];
  char value[MAX_PATH +1];
  int count =0;
  for(i =0; i<conf->mail_attachments; i++)
  {
    snprintf(fname, MAX_PATH +1, "file%d", i);
    cs_get_optional_parameter(fname, value, MAX_PATH);
    if(strlen(value))
           count++;
  }
  if(count > 1)
      cs_critical_error(ERR_ATTACH_ZIP_ONLY_ONE_FILE, "");
}





static void
check_website_upload_type (int *iszip)
{
#define MAX_MSG 300

  char fname[MAX_PATH + 1];
  char orig_fname[MAX_PATH + 1];
  char ext[MAX_FILENAME + 1];

  cs_get_required_parameter("file1", fname, MAX_PATH);

  strncpy (orig_fname, fname, MAX_FILENAME + 1);
  strtolower (fname);
  get_extension (fname, ext);   /* shared_news_util.c */

  if (!strcmp (ext, "zip"))
    {
      *iszip = 1;
      check_more_than_one_files(&conf);

    }
  else
    {				/* check to see if file ends in .htm or .html */
      *iszip = 0;
      if (strcmp (ext, "htm") && strcmp (ext, "html"))
	{
	  big_error (ERR_FIRST_FIELD_NOT_ZIP_OR_HTML, orig_fname);
	}
    }
#undef MAX_MSG
}








/* this simply ensures that the zipfile is not empty
** additional integrity checks are done later
**/
static void
check_zipfile ()
{
  char str[MAX_PATH +1];
  FILE *fp;
  struct stat buf;

  cs_get_required_parameter("file1", str, MAX_PATH);
  fp = cgi_filehandle(global_cgi, "file1");
  fstat(fileno(fp), &buf);
  if(buf.st_size ==0)
      big_error (ERR_EMPTY_FILE, str);

}




static void
check_website_data (int *iszip)
{
  char *subject =0;
  subject = hdf_get_value(global_cgi->hdf, "Query.subject", 0);
  if(!subject || !strlen(subject))
    cs_critical_error(ERR_PARAM_MISSING, "subject");

  check_website_upload_type (iszip);	/* did user upload a zip file or multiple files */
  if (*iszip)
    check_zipfile ();
  else
    check_website_individual_files ();

}


static void
add_to_file_list (FILE_NODE ** head, const char *unique_fname,
		  const char *orig_fname)
{
  FILE_NODE *ptr;

  if (!*head)
    {
      *head = (FILE_NODE *) malloc (sizeof (FILE_NODE));
      if (!*head)
	{
	  big_error (ERR_MALLOC_FAILED, "");
	}
      ptr = *head;
    }
  else
    {
      for (ptr = *head; ptr->next; ptr = ptr->next);
      ptr->next = (FILE_NODE *) malloc (sizeof (FILE_NODE));
      if (!ptr->next)
	{
	  big_error (ERR_MALLOC_FAILED, "");
	}
      ptr = ptr->next;
    }

  strncpy (ptr->unique_fname, unique_fname, MAX_FILENAME + 1);
  strncpy (ptr->orig_fname, orig_fname, MAX_FILENAME + 1);
  ptr->next = (FILE_NODE *) 0;
}



static void
parse_raw_filename (char *raw_fname, char *orig_fname, char *unique_fname)
{
  char *ptr;

  ptr = strstr (raw_fname, "::");
  if (!ptr)
    big_error (ERR_PARAM_FORMAT, raw_fname);
  *ptr = '\0';
  strncpy (orig_fname, raw_fname, MAX_FILENAME + 1);
  ptr += 2;			/* skip past "::" */
  strncpy (unique_fname, ptr, MAX_FILENAME + 1);
}





static void
build_pasted_attachment_list (int pasted_website, int pasted_files)
{
  int i;
  char parameter[10];		/* letter 'f' followed by a small integer, eg 'f1' f2' */
  char raw_filename[2 * (MAX_FILENAME + 1)];	/* consists of "orig_fname::unique_fname" */
  char orig_fname[MAX_FILENAME + 1];
  char unique_fname[MAX_FILENAME + 1];


  for (i = 1; i <= pasted_files + pasted_website; i++)
    {
      snprintf (parameter, 10, "f%d", i);
      cs_get_optional_parameter(parameter, raw_filename, 2*MAX_FILENAME);
      if(strlen(raw_filename))
      {
	  parse_raw_filename (raw_filename, orig_fname, unique_fname);
	  add_to_file_list (&pasted_file_head, unique_fname, orig_fname);
	  pasted_attachments++;

      }
    }

}






static void
get_unique_fname (char *unique_fname, const char *ext, time_t * time_sent)
{

  static int n = 0;
  char timestring[MAX_TIMESTRING + 1];
  char filepath[MAX_PATH + 1];

  strftime (timestring, MAX_TIMESTRING, "%m%d%Y%H%M%S", localtime (time_sent));


  do
    {
      n++;
      snprintf (unique_fname, MAX_FILENAME + 1, "%s%d%d.%s", timestring, n, getpid(), ext);
      snprintf (filepath, MAX_PATH + 1, "%s%s", discussion_path, unique_fname);
    }
  while (file_exists(filepath));  /* shared_file_util.c */

}







static void
get_system_filenames (const char *full_orig_name,
		      char *unique_fname,
		      char *orig_fname, time_t * time_sent)
{

  get_unique_fname (unique_fname, "fil", time_sent);
  get_orig_fname (full_orig_name, orig_fname);
}


static void
build_normal_file_list (time_t * time_sent)
{
 #define MAX_TMP_NAME 30
  char unique_fname[MAX_PATH + 1];
  char orig_fname[MAX_PATH + 1];
  char name[MAX_TMP_NAME];
  char filename[MAX_PATH +1];
  int i;
  FILE *fp;
  struct stat buf;
  
  for(i =1; i <= conf.mail_attachments; i++)
  {
     snprintf(name, MAX_TMP_NAME, "file%d", i);
     cs_get_optional_parameter(name, filename, MAX_PATH );
     if (strlen(filename))
     {
        fp = cgi_filehandle(global_cgi, name);
        if(fstat(fileno(fp), &buf))
               big_error(ERR_STAT_FAILED, filename);
        if(buf.st_size == 0)
             big_error(ERR_EMPTY_FILE, filename);
       get_system_filenames (filename, unique_fname, orig_fname,
                              time_sent);
       add_to_file_list (&file_head, unique_fname, orig_fname);
       write_cgi_file (fp, buf.st_size, unique_fname);
       attachments_written = 1;        /* set global flag for possible use in big_error() */
        attachments++;
      }

  }
 #undef MAX_TMP_NAME 

}




static void
check_form_data (int *is_web_zipfile,
		 int website,
		 int pasted_website,
		 int pasted_files, time_t * time_sent)
{
  char *to =0;
  to = hdf_get_value(global_cgi->hdf, "Query.to", 0);
  if(!to || !strlen(to))
     cs_critical_error(ERR_PARAM_MISSING, "to");


  /* the following test should always fail, unless someone
     ** is mucking around with the form
   */
  if (pasted_website && pasted_files)
    big_error (ERR_INTERNAL_ERROR, "check_form_data() found both pasted_website and pasted_files");

  build_pasted_attachment_list (pasted_website, pasted_files);


  if (website)
    {
      check_incomplete_upload(); /*shared_news_util.c */
      check_website_data (is_web_zipfile);	/* make sure message is not empty + do preliminary checks on web files */
      /* still here? make sure nothing but the new web site is attached */
      if (pasted_attachments)
	big_error (ERR_CANT_ATTACH_NEW_WEB_SITE, "");
    }
  else
    {
      *is_web_zipfile = 0;
      build_normal_file_list (time_sent);
//      verify_non_empty ();	/* make sure message is not empty */
      if(attachments)
          check_incomplete_upload();   /* shared_news_util.c */
          
      if (pasted_website && pasted_attachments && attachments)
	big_error (ERR_CANT_MAKE_ADDITIONAL_ATTACHMENTS, "");
    }
}




/****** POST OFFICE specific routines ********/



static int
in_to_list (char *username)
{
  RECEIVER_NODE *ptr;

  for (ptr = to_head; ptr; ptr = ptr->next)
    if (!strcmp (ptr->username, username))
      return 1;

  return 0;
}




static int
in_cc_list (char *username)
{
  RECEIVER_NODE *ptr;

  for (ptr = cc_head; ptr; ptr = ptr->next)
    if (!strcmp (ptr->username, username))
      return 1;

  return 0;
}





/** SCRAP THIS??? not used ***/
void
clean_bcc_list ()
{
  RECEIVER_NODE *ptr, *ptr2;

  ptr = bcc_head;
  while (ptr)
    if (in_to_list (ptr->username) || in_cc_list (ptr->username))
      {
	if (ptr == bcc_head)
	  {
	    ptr2 = ptr;
	    bcc_head = bcc_head->next;
	    free (ptr2);
	    ptr = bcc_head;
	  }
	else
	  {
	    for (ptr2 = bcc_head; ptr2->next != ptr; ptr2 = ptr2->next);
	    ptr2->next = ptr->next;
	    free (ptr);
	    ptr = ptr2->next;
	  }
      }
    else
      ptr = ptr->next;
}



static void
clean_cc_list ()
{
  RECEIVER_NODE *ptr, *ptr2;

  ptr = cc_head;
  while (ptr)
    if (in_to_list (ptr->username))
      {
	if (ptr == cc_head)
	  {
	    ptr2 = ptr;
	    cc_head = cc_head->next;
	    free (ptr2);
	    ptr = cc_head;
	  }
	else
	  {
	    for (ptr2 = cc_head; ptr2->next != ptr; ptr2 = ptr2->next);
	    ptr2->next = ptr->next;
	    free (ptr);
	    ptr = ptr2->next;
	  }
      }
    else
      ptr = ptr->next;
}

static void
build_mail_receiver_list ()
{

  create_rec_list ("to", &to_head);	/* shared multipart.c */
  if(!to_head)
     big_error(ERR_PARAM_MISSING, "to");

  create_rec_list ("cc", &cc_head);

  clean_cc_list ();


}

/***** end POST OFFICE routines *****/



static void
free_receiver_lists ()
{
  RECEIVER_NODE *ptr;

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

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

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


}


static void
free_file_list (FILE_NODE ** head)
{
  FILE_NODE *ptr;

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

static void
free_html_filelist (void)
{
  HTML_FILE_NODE *ptr;

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


}



static void
process_message_file (char *msg_fname, time_t * time_sent, int width)
{
  char *msg =0;
  FILE *fp;
  char filepath[MAX_PATH +1];
  int len;

  msg = hdf_get_value(global_cgi->hdf, "Query.msg", 0);
  
  get_unique_fname (msg_fname, "msg", time_sent);
  snprintf(filepath, MAX_PATH +1, "%s%s",    discussion_path, msg_fname); 
  fp = fopen(filepath, "w");
  
  if(!fp)
     big_error(ERR_FOPEN_WRITE_FAILED, msg_fname);
  
  
  if(msg && strlen(msg))
    {
        len = strlen(msg);
        if(len < width || USE_TEXTAREA_HARD_WRAP)	
	  {
           if(fwrite (msg, strlen(msg), 1, fp) != 1)
             big_error (ERR_FWRITE_FAILED, filepath);
	  }
	else
           write_folded(fp, msg, width);
    }
      fclose(fp);
}





static void
process_pasted_attached_files (int pasted_files, int pasted_website,
			       time_t * time_sent)
{
  FILE_NODE *fptr;
  char clipboard_path[MAX_PATH + 1];
  char unique_fname[MAX_FILENAME + 1];
  char sourcepath[MAX_PATH + 1];
  char destpath[MAX_PATH + 1];

  if ((pasted_files || pasted_website) && pasted_file_head)
    {
      snprintf (clipboard_path, MAX_PATH + 1, "%s%s/", CLIPBOARD_PATH,
		user.id);
      if (pasted_website)
	{
	  get_unique_fname (unique_fname, "dir", time_sent);
	  copy_dir (clipboard_path,     /* shared_copy_file.c */
		    discussion_path,
		    pasted_file_head->unique_fname, unique_fname);
	  strncpy (pasted_file_head->unique_fname, unique_fname,
		   MAX_FILENAME + 1);
	}
      else
	{
	  for (fptr = pasted_file_head; fptr; fptr = fptr->next)
	    {
	      get_unique_fname (unique_fname, "fil", time_sent);
	      snprintf (sourcepath, MAX_PATH + 1, "%s%s", clipboard_path,
			fptr->unique_fname);
	      snprintf (destpath, MAX_PATH + 1, "%s%s", discussion_path,
			unique_fname);
	      copy_file (sourcepath, destpath);	/* shared_copy_file.c */
	      strncpy (fptr->unique_fname, unique_fname, MAX_FILENAME + 1);
	    }
	}
    }
}





static void
write_info_file (char *msg_fname,
		 char *info_fname,
		 char *subject,
		 time_t time_sent,
		 int website, int pasted_website)
{
  FILE *fp;
  INFO info;
  ATTACHMENT attach;

  PERSON mail_person;
  FILE_NODE *fptr;
  RECEIVER_NODE *rptr;

  char info_path[MAX_PATH + 1];

  cs_get_required_parameter("subject", subject, MAX_SUBJECT);
  get_unique_fname (info_fname, "inf", &time_sent);
  strncpy (info.msg_fname, msg_fname, MAX_FILENAME + 1);
  strncpy (info.subject, subject, MAX_SUBJECT + 1);
  
  info.version = VERSION24;   /* flags this as a message sent with the 'new' post office module
                                          ** introduced with version 24
                                           */
                              
  info.time_sent = time_sent;
  /* websites are marked with a -1 flag in no_attachments */
  info.no_attachments =
    website || (pasted_website && pasted_attachments) ? -1 :
    attachments + pasted_attachments;

  snprintf (info_path, MAX_PATH + 1, "%s/%s", discussion_path, info_fname);

  fp = fopen (info_path, "w");
  if (!fp)
    big_error (ERR_FOPEN_WRITE_FAILED, info_path);
  if (fwrite (&info, sizeof (INFO), 1, fp) != 1)
    big_error (ERR_FWRITE_FAILED, info_path);

  for (fptr = file_head; fptr; fptr = fptr->next)
    {
      strncpy (attach.unique_fname, fptr->unique_fname, MAX_FILENAME + 1);
      strncpy (attach.orig_fname, fptr->orig_fname, MAX_FILENAME + 1);
      if (fwrite (&attach, sizeof (ATTACHMENT), 1, fp) != 1)
	big_error (ERR_FWRITE_FAILED, info_path);
    }


  for (fptr = pasted_file_head; fptr; fptr = fptr->next)
    {
      strncpy (attach.unique_fname, fptr->unique_fname, MAX_FILENAME + 1);
      strncpy (attach.orig_fname, fptr->orig_fname, MAX_FILENAME + 1);
      if (fwrite (&attach, sizeof (ATTACHMENT), 1, fp) != 1)
	big_error (ERR_FWRITE_FAILED, info_path);
    }


      for (rptr = to_head; rptr; rptr = rptr->next)
	{
	  strncpy (mail_person.username, rptr->username, MAX_USERNAME + 1);
	  strncpy (mail_person.realname, rptr->realname, MAX_NAME + 1);
	  mail_person.how_received = TO;
	  mail_person.time_read = 0;
	  mail_person.time_deleted = 0;
	  if (fwrite (&mail_person, sizeof (PERSON), 1, fp) != 1)
	    big_error (ERR_FWRITE_FAILED, info_path);
	}

      for (rptr = cc_head; rptr; rptr = rptr->next)
	{
	  strncpy (mail_person.username, rptr->username, MAX_USERNAME + 1);
	  strncpy (mail_person.realname, rptr->realname, MAX_NAME + 1);
	  mail_person.how_received = CC;
	  mail_person.time_read = 0;
	  mail_person.time_deleted = 0;
	  if (fwrite (&mail_person, sizeof (PERSON), 1, fp) != 1)
	    big_error (ERR_FWRITE_FAILED, info_path);
	}

      for (rptr = bcc_head; rptr; rptr = rptr->next)
	{
	  strncpy (mail_person.username, rptr->username, MAX_USERNAME + 1);
	  strncpy (mail_person.realname, rptr->realname, MAX_NAME + 1);
	  mail_person.how_received = BCC;
	  mail_person.time_read = 0;
	  mail_person.time_deleted = 0;
	  if (fwrite (&mail_person, sizeof (PERSON), 1, fp) != 1)
	    big_error (ERR_FWRITE_FAILED, info_path);
	}




  fclose (fp);
}







static void
update_inboxes (NEWFILE_RECORD * newinfo, RECEIVE_TYPE t)
{
  RECEIVER_NODE *rptr;
  char inbox_path[MAX_PATH + 1];

  rptr = (t == TO ? to_head : t == CC ? cc_head : bcc_head);
  newinfo->how_received = t;

  for (; rptr; rptr = rptr->next)
    {
      snprintf (inbox_path, MAX_PATH + 1, "%s%s/%s/%s/%s", 
                    conf.course_path,PEOPLE_DIR, rptr->username, MAIL_DIR, INBOX_FNAME);
      add_new_post_msg(newinfo, inbox_path);   /* shared_post_inbox.c */
    }
}






void
update_all_inboxes (char *inf_fname, char *subject, time_t time_sent,
		     int website, int pasted_website)
{
  NEWFILE_RECORD newinfo;


  strncpy (newinfo.sender, user.username, MAX_USERNAME + 1);
  strncpy (newinfo.sender_realname, user.realname, MAX_NAME + 1);
  newinfo.how_sent = NEWMSG;
  strncpy (newinfo.subject, subject, MAX_SUBJECT + 1);
  newinfo.time_sent = time_sent;



  newinfo.attachments = website || (pasted_website
				    && pasted_attachments) ? 1 : attachments + pasted_attachments;

  strncpy (newinfo.info_fname, inf_fname, MAX_FILENAME + 1);

  update_inboxes (&newinfo, TO);
  update_inboxes (&newinfo, CC);
  update_inboxes (&newinfo, BCC);

}


static void
back_up (char *program)
{
  char temp[MAX_PATH + 1];

  translate_url_substitutes (temp, program, MAX_PATH + 1, 0);	/* shared_translate_url.c */
  printf ("Location: %s\n\n", temp);
}







void
clean_zip_temp_dir ()
{
  char dirname[MAX_PATH + 1];

  snprintf (dirname, MAX_PATH + 1, "%s/%s", discussion_path, ZIP_TMP_DIRNAME);
  kill_directory(dirname);     /* shared_file_util.c */

}




void
create_tmp_zip_dir ()
{
  char dirname[MAX_PATH + 1];



  snprintf (dirname, MAX_PATH + 1, "%s/%s", discussion_path, ZIP_TMP_DIRNAME);

  if (mkdir (dirname, 0777))
    big_error (ERR_MKDIR_FAILED, dirname);

}




void
delete_zip_tmp_file ()
{
  char fname[MAX_PATH + 1];

  snprintf (fname, MAX_PATH + 1, "%s/%s", discussion_path, ZIP_TMP_FNAME);
  unlink (fname);
}


static void
check_unzip_status (int error)
{

  delete_zip_tmp_file ();


  switch (error)
    {
      /* this is my error code if exec() failed */
    case 88:
      big_error (ERR_CANT_START_UNZIP, "code_88");
      break;



      /* remaining errors reported by unzip */
    case 0:
      return;			/* everything went OK */

    case 1:
      big_error (ERR_UNZIP_CODE_1, "code_1");
      break;


    case 2:
      big_error (ERR_UNZIP_CODE_2, "code_2");
      break;

    case 3:
      big_error (ERR_UNZIP_CODE_3, "code_3");
      break;

    case 51:
      big_error (ERR_UNZIP_CODE_51, "code_51");
      break;

    case 4:			/* memory allocation error 4-8 */
    case 5:
    case 6:
    case 7:
    case 8:
      big_error (ERR_UNZIP_MEMORY_ERROR, "");
      break;

    case 9:
      big_error (ERR_UNZIP_CODE_9, "code_9");
      break;



    case 10:
      big_error (ERR_UNZIP_CODE_10, "code_10");
      break;

    case 11:
      big_error (ERR_UNZIP_CODE_11, "code_11");
      break;


    case 50:
      big_error (ERR_UNZIP_CODE_50, "code_50");
      break;

    default:
      big_error (ERR_UNZIP_UNDOCUMENTED_ERROR, "default");
      break;

    }
}




static void
unzip_zipfile ()
{
  char zip_path[MAX_PATH + 1];
  char zipdir_path[MAX_PATH + 1];
  char zipdir_switch[MAX_PATH + 1];
  char zipfile[MAX_PATH +1];
  FILE *fp;
  struct stat buf;
  pid_t pid, status;

  cs_get_required_parameter("file1", zipfile, MAX_PATH);
  fp = cgi_filehandle(global_cgi, "file1");

  if(fstat(fileno(fp), &buf))
      big_error (ERR_CANT_FIND_ZIPFILE, zipfile);
  if(buf.st_size ==0)
        big_error (ERR_EMPTY_FILE, zipfile);

  write_cgi_file (fp, buf.st_size, ZIP_TMP_FNAME);

  snprintf (zipdir_path, MAX_PATH + 1, "%s/%s", discussion_path,
	    ZIP_TMP_DIRNAME);
  clean_zip_temp_dir ();
  snprintf (zipdir_switch, MAX_PATH + 1, "-d%s", zipdir_path);	/* tells unzip to extract to this directory */

  snprintf (zip_path, MAX_PATH + 1, "%s/%s", discussion_path, ZIP_TMP_FNAME);


  if ((pid = fork ()) < 0)
    big_error (ERR_CANT_START_NEW_PROCESS, "in unzip_zipfile()");


  if (pid == 0)
    {				/* child process */
      execl (UNZIP_PATH, "unzip", zipdir_switch, "-o", "-qq", zip_path, (char *) 0);
      exit (88);		/* this is returned if above execl() fails */
    }
 /*** parent process */
  while (wait (&status) != pid);

  check_unzip_status (WEXITSTATUS (status));	/* report on status */

}




static void
get_lowercase_extension (const char *filename, char *extension)
{
  char *ptr;

  ptr = strrchr (filename, '.');
  if (!ptr)
    {
      *extension = '\0';
      return;
    }

  strncpy (extension, ptr + 1, MAX_FILENAME + 1);
  for (ptr = extension; *ptr; ptr++)
    *ptr = tolower (*ptr);
}



static void
add_to_html_file_list (const char *fname)
{
  HTML_FILE_NODE *ptr;

  if (!html_file_head)
    {
      html_file_head = (HTML_FILE_NODE *) malloc (sizeof (HTML_FILE_NODE));
      if (!html_file_head)
	big_error (ERR_MALLOC_FAILED, "");
      ptr = html_file_head;
    }
  else
    {
      for (ptr = html_file_head; ptr->next; ptr = ptr->next);
      ptr->next = (HTML_FILE_NODE *) malloc (sizeof (HTML_FILE_NODE));
      if (!ptr->next)
	big_error (ERR_MALLOC_FAILED, "");
      ptr = ptr->next;
    }

  strncpy (ptr->fname, fname, MAX_FILENAME + 1);
  ptr->next = (HTML_FILE_NODE *) 0;
}





static void
build_html_file_list (int *file_count)
{
  char dirname[MAX_PATH + 1];
  DIR *dir_p;
  struct dirent *dir_entry_p;
  char extension[MAX_FILENAME + 1];


  *file_count = 0;
  snprintf (dirname, MAX_PATH + 1, "%s/%s", discussion_path, ZIP_TMP_DIRNAME);
  dir_p = opendir (dirname);
  if (!dir_p)
   big_error (ERR_OPENDIR_FAILED, dirname);
   
  while (NULL != (dir_entry_p = readdir (dir_p)))
    {
      if (!isdirectory (dirname, dir_entry_p->d_name))  /* shared_file_util.c */
	{
	  get_lowercase_extension (dir_entry_p->d_name, extension);
	  if (!strcmp (extension, "htm") || !strcmp (extension, "html"))
	    {
	      add_to_html_file_list (dir_entry_p->d_name);
	      (*file_count)++;
	    }
	}
    }
  closedir (dir_p);
}



static void
verify_start_file (char *start_fname, int *done)
{

#define MAX_MSG 100

  char start_path[MAX_PATH + 1];
  struct stat buf;
  int file_count;
  char msg[MAX_MSG];

  *done = 1;			/* be optimistic! */
  snprintf (start_path, MAX_PATH + 1, "%s/%s/%s", discussion_path,
	    ZIP_TMP_DIRNAME, ZIP_START_FNAME);
  if (!stat (start_path, &buf))
    {				/* if a file named ZIP_START_FNAME exists ... */
      if (S_ISDIR (buf.st_mode))
	{			/* is it a directory? */
	  build_html_file_list (&file_count);
	  if (file_count == 1)
	    strncpy (start_fname, html_file_head->fname, MAX_FILENAME + 1);
	  else
	    {
	      snprintf (msg, MAX_MSG, "%s %s", ZIP_START_FNAME, ZIP_START_FNAME);
	      big_error (ERR_DIR_NAMED_X_FOUND_X_IS_RESERVED, msg);
	    }
	}
      else			/* ZIP_START_FNAME was not a directory */
	strncpy (start_fname, ZIP_START_FNAME, MAX_FILENAME + 1);
    }
  else
    {				/* no file named ZIP_START_FNAME was found */
      build_html_file_list (&file_count);
      if (!file_count)
	big_error (ERR_NO_HTML_FILES_FOUND, "");
      else if (file_count == 1)
	strncpy (start_fname, html_file_head->fname, MAX_FILENAME + 1);
      else
	{
	  *done = 0;		/* we'll have to ask the user to name the file */
	  strncpy (start_fname, ZIP_START_FNAME, MAX_FILENAME + 1);
	}
    }
#undef MAX_MSG
}





static void
process_individual_website_files (char *start_fname)
{
   #define MAX_TMP_NAME 30

  char name[MAX_TMP_NAME];
  char orig_fname[MAX_PATH + 1];
  char write_dest_fname[MAX_PATH + 1];
  int i;
  char filename[MAX_PATH +1];
  FILE *fp;
  struct stat buf;

  clean_zip_temp_dir ();
  create_tmp_zip_dir ();
  strncpy (start_fname, "index.html", MAX_FILENAME + 1);	/* assume this, just to be safe */
   for(i =1; i<= conf.mail_attachments; i++)
  {
     snprintf(name, MAX_TMP_NAME, "file%d", i);
    cs_get_optional_parameter(name, filename, MAX_PATH);
    if (strlen(filename))
    {
         fp = cgi_filehandle(global_cgi, name);
         if(fstat(fileno(fp), &buf))
                big_error(ERR_STAT_FAILED, filename);
         if(buf.st_size == 0)
                big_error(ERR_EMPTY_FILE, filename);
	 get_orig_fname (filename, orig_fname);
        snprintf (write_dest_fname, MAX_PATH + 1, "%s/%s", ZIP_TMP_DIRNAME, orig_fname);        /* so we can spoof write_cgi_file */
        write_cgi_file (fp,buf.st_size,  write_dest_fname);
       if (!strcmp (name, "file1"))
        strncpy (start_fname, orig_fname, MAX_FILENAME + 1);
     }
   }
   #undef MAX_TMP_NAME 
}


static void
rename_tmp_dir (char *directory_fname, time_t * time_sent)
{
  char oldname[MAX_PATH + 1];
  char newname[MAX_PATH + 1];

  snprintf (oldname, MAX_PATH + 1, "%s/%s", discussion_path, ZIP_TMP_DIRNAME);
  get_unique_fname (directory_fname, "dir", time_sent);
  snprintf (newname, MAX_PATH + 1, "%s/%s", discussion_path, directory_fname);
  if (rename (oldname, newname))
    big_error (ERR_CANT_RENAME_TEMP_DIR, newname);

}


static void
process_website_attached_files (time_t * time_sent, int is_web_zipfile,
				int *done)
{
  char start_file[MAX_FILENAME + 1];
  char directory_fname[MAX_FILENAME + 1];

  if (is_web_zipfile)
    {
      unzip_zipfile ();
      verify_start_file (start_file, done);
    }
  else
    {
      process_individual_website_files (start_file);
      *done = 1;
    }

  rename_tmp_dir (directory_fname, time_sent);
  add_to_file_list (&file_head, directory_fname, start_file);

}


void
ask_which_fname ( char *course,  char *key,  char *from)
{
 #define MAX_TMP_NAME 30
  HTML_FILE_NODE *ptr;
   int i =0;
   char name[MAX_TMP_NAME];

     cs_set_course_info(&conf);
     cs_set_current_time();
     cs_set_int_value("grp", MAIL);
     cs_set_value("crs", course);
     cs_set_value("id", key);
     cs_set_value("from", from);
     cs_set_value("dir", file_head->unique_fname);

     cs_set_int_value("ask_which", 1);
  for (ptr = html_file_head; ptr; ptr = ptr->next)
  {
     snprintf(name, MAX_TMP_NAME, "file.%d.name", i);
    cs_set_value(name, "fname");
     snprintf(name, MAX_TMP_NAME, "file.%d.value", i);
     cs_set_value(name, ptr->fname);
     if(i ==0)
     {
          snprintf(name, MAX_TMP_NAME, "file.%d.checked", i);
           cs_set_int_value(name, 1);
     }
     i++;

  }
   free_html_filelist ();
   free_file_list (&file_head);

   #undef MAX_TMP_NAME
}

static void
record_in_outbox (const char *inf_fname, const char *subject,
		  time_t time_sent, int website, int pasted_website)
{
  OUTBOX_RECORD outbox;
  FILE *fp;
  RECEIVER_NODE *rptr;
  int i;
  char outbox_fname[MAX_PATH + 1];

  strncpy (outbox.info_fname, inf_fname, MAX_FILENAME + 1);
  strncpy (outbox.subject, subject, MAX_SUBJECT + 1);
  outbox.time_sent = time_sent;

  /* build list of up to MAX_TO recipients, each recipient name separated by 
     ** vertical bar character
   */
  *outbox.to = '\0';
  for (rptr = to_head, i = 1; rptr && i <= MAX_TO; rptr = rptr->next, i++)
    {
      if (i > 1)
	strcat (outbox.to, "|");
      strcat (outbox.to, rptr->realname);
    }
  if (rptr)			/* if we did not finish list */
    strcat (outbox.to, "|");	/* then end list with a vertical bar */

 /** the how_sent item is not used at the moment, for now fill in with a valid
 *** value for all
 ***/

  outbox.how_sent = NEWMSG;

  outbox.is_sleepy = 0;	/* not yet deleted */
  outbox.unread = 1;

  outbox.attachments = website || (pasted_website && pasted_attachments) ? 1 :
    attachments + pasted_attachments;



  snprintf (outbox_fname, MAX_PATH + 1, "%s%s/%s/%s/%s",
	    conf.course_path, PEOPLE_DIR, user.username, MAIL_DIR, OUTBOX_FNAME);
  fp = fopen (outbox_fname, "a");
  if (!fp)
    big_error (ERR_FOPEN_APPEND_FAILED, outbox_fname);

  get_exclusive_lock(fileno(fp), 1);    /* shared_lock.c */
  if (fwrite (&outbox, sizeof (OUTBOX_RECORD), 1, fp) != 1)
    big_error (ERR_FWRITE_FAILED, outbox_fname);
  release_lock(fileno(fp));  
  fclose (fp);
}




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

  int pasted_website;		/* from optional pasted_website=? cmd line */
  int pasted_files;		/* from optional pasted_files=? cmd line */

  char msg_fname[MAX_FILENAME + 1];	/* name given to the message */
  char inf_fname[MAX_FILENAME + 1];	/* the info file name */
  time_t time_sent;
  char subject[MAX_SUBJECT + 1];

  int website;			/* was the 'website' checkbox checked? */
  int is_web_zipfile;		/* was a web page attached as a single zip file? */
  int done;			/* are we done, or do we have to ask user to pick the starting html file from a list */
  int width;

  cs_cgi_init();
  read_parameters (course, key, from,  &website, &pasted_website, &pasted_files, &width);

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


  time_sent = time (NULL);
  set_discussion_names (conf.course_path, user.username, MAIL);	/* shared_news_util.c */

  check_form_data (&is_web_zipfile, website, pasted_website, pasted_files, &time_sent);


  if (website)
    process_website_attached_files (&time_sent, is_web_zipfile, &done);
  else
    done = 1;			/* no need for more info from user */

  build_mail_receiver_list();	/* determine who to send to */

  process_message_file (msg_fname, &time_sent, width);

  process_pasted_attached_files (pasted_files, pasted_website, &time_sent);
  write_info_file (msg_fname, inf_fname, subject,
		   time_sent, website, pasted_website);



  update_all_inboxes (inf_fname, subject, time_sent, website, pasted_website);
  record_in_outbox (inf_fname, subject, time_sent, website, pasted_website);


  free_receiver_lists ();

  if (done)
    {
      free_file_list (&file_head);
      free_file_list (&pasted_file_head);
      free_html_filelist ();
      back_up (from);
    }
  else
  {
    ask_which_fname (course, key, from);
    cs_cgi_display("select_home_html_file", 1);
  }
  cs_cgi_destroy();

  return 0;
}



