/* ??? All the time junk should definitely be in OSinter */

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <ctype.h>

#include "sysdep.h"
#include "darray.h"
#include "foldert.h"
#include "verbs.h"
#include "rulerun.h"
#include "ruleset.h"
#include "RSreg.h"
#include "folder.h"
#include "msg.h"
#include "al.h"
#include "OSinter.h"
#include "RMFBuffer.h"
#include "rap.h"

Msg AlVerbs_current_msg = NULL;
AlFolder AlVerbs_current_folder = NULL;
AlRule AlVerbs_current_rule = NULL;
AlRuleSet AlVerbs_current_ruleset = NULL;

const char *AlVerbs_sendme_username = NULL;

NORET AlVerbs_delete(args)
     Darray args;
{
  assert(Darray_len(args) == 0);
  AlFolder_remove_cur_msg(AlVerbs_current_folder);
}

NORET AlVerbs_system(args)
     Darray args;
{
  FILE *p;
  char *command;

  assert(Darray_len(args) == 1);

  command = (char *)Darray_get(args, (unsigned int)0);

  if ((p = popen(command, "w")) == (FILE *)NULL)
    Al_warning1("Could not execute \"%s\"", command);
  else {
    (NORET)fputs(AlMsg_get_msg_buffer(AlVerbs_current_msg), p);
    (NORET)pclose(p);
  }
}

NORET AlVerbs_system_body(args)
     Darray args;
{
  FILE *p;
  char *command;

  assert(Darray_len(args) == 1);

  command = (char *)Darray_get(args, (unsigned int)0);

  if ((p = popen(command, "w")) == (FILE *)NULL)
    Al_warning1("Could not execute \"%s\"", command);
  else {
    (NORET)fputs(AlMsg_get_msg_body(AlVerbs_current_msg), p);
    (NORET)pclose(p);
  }
}

NORET AlVerbs_move(args)
     Darray args;
{
  AlFolderType new_folder_type_obj = NULL;
  AlFolder new_folder_obj;

  assert(Darray_len(args) >= 1 && Darray_len(args) <= 2);
  if (Darray_len(args) == 2)
    new_folder_type_obj =
      AlFolderT_get_folder_type((char *)Darray_get(args, (unsigned int)1));

  if (new_folder_type_obj == NULL)
    new_folder_type_obj = AlFolderT_default_type();
  new_folder_obj = AlFolder_create_from_file(Darray_get(args, (unsigned int)0),
					     new_folder_type_obj,
					     Bool_FALSE,
					     Bool_FALSE,
					     Bool_FALSE,
					     FOLDER_OPEN_APPEND);
  if (new_folder_obj != NULL) {
    (NORET)AlFolder_move_cur_msg(AlVerbs_current_folder, new_folder_obj);
    AlFolder_destroy(new_folder_obj);
  }				/* Copy message to new folder */
}

NORET AlVerbs_copy(args)
     Darray args;
{
  AlFolderType new_folder_type_obj = NULL;
  AlFolder new_folder_obj;

  assert(Darray_len(args) >= 1 && Darray_len(args) <= 2);
  if (Darray_len(args) == 2)
    new_folder_type_obj =
      AlFolderT_get_folder_type((char *)Darray_get(args, (unsigned int)1));

  if (new_folder_type_obj == NULL)
    new_folder_type_obj = AlFolderT_default_type();
  new_folder_obj = AlFolder_create_from_file(Darray_get(args, (unsigned int)0),
					     new_folder_type_obj,
					     Bool_FALSE,
					     Bool_FALSE,
					     Bool_FALSE,
					     FOLDER_OPEN_APPEND);
  if (new_folder_obj != NULL) {
    (NORET)AlFolder_copy_cur_msg(AlVerbs_current_folder, new_folder_obj);
    AlFolder_destroy(new_folder_obj);
  }				/* Copy message to new folder */
}

NORET AlVerbs_runrules(args)
     Darray args;
{
  unsigned int num_args = Darray_len(args);

  assert(num_args >= 1 || num_args <= 3);
  
  if (num_args == 1) {		/* start new ruleset on cur msg */
    char *new_rs_name = (char *)Darray_get(args, (unsigned int)0);
    AlRuleSet new_rs_obj = AlRSreg_find(new_rs_name);

    if (new_rs_obj != NULL) {
      
      AlRuleRun_do_rules(AlVerbs_current_folder, 
			 AlVerbs_current_msg,
			 new_rs_obj);
    }
  } else {			/* start new ruleset on new folder */
    AlFolderType new_folder_type_obj = NULL;
    char *new_rs_name = (char *)Darray_get(args, (unsigned int)0);
    char *new_folder_name = (char *)Darray_get(args, (unsigned int)1);
    AlRuleSet new_rs_obj = AlRSreg_find(new_rs_name);
    AlFolder new_folder_obj;

    if (new_rs_obj != NULL) {
      if (num_args == 3) 
	new_folder_type_obj =
	  AlFolderT_get_folder_type((char *)Darray_get(args, (unsigned int)2));
      
      if (new_folder_type_obj == NULL)
	new_folder_type_obj = AlFolderT_default_type();
      new_folder_obj = AlFolder_create_from_file(new_folder_name,
						 new_folder_type_obj,
						 Bool_FALSE,
						 Bool_FALSE,
						 Bool_FALSE,
						 FOLDER_OPEN_RW);
      if (new_folder_obj != NULL) {
	AlRuleRun_domsgs(new_folder_obj, new_rs_obj);
	AlFolder_destroy(new_folder_obj);
      }
    }
  }
}

static NORET addsub_help(args, mult)
     Darray args;
     int mult;
{
  const char *uprop_name, *uprop_string;
  long cur_uprop_value=0, add_value;
  char bufr[100];
  extern long atol PROTOTYPE((const char *));

  uprop_name = (const char *)Darray_get(args, (unsigned int)0);
  add_value = atol((const char *)Darray_get(args, (unsigned int)1));
  uprop_string = AlMsg_get_uprop(AlVerbs_current_msg, uprop_name);
  if (uprop_string == NULL) 
    AlMsg_clear_error_code(AlVerbs_current_msg);
  else
    cur_uprop_value = atol(uprop_string);
  sprintf(&bufr[0],"%ld",cur_uprop_value+add_value*mult);
  AlMsg_set_uprop(AlVerbs_current_msg, uprop_name, &bufr[0]);
}

NORET AlVerbs_add(args)
     Darray args;
{
  assert(Darray_len(args) == 2);
  
  addsub_help(args, 1);

}

NORET AlVerbs_subtract(args)
     Darray args;
{
  assert(Darray_len(args) == 2);

  addsub_help(args, -1);
}

NORET AlVerbs_set(args)
     Darray args;
{
  assert(Darray_len(args) == 2);
  AlMsg_set_uprop(AlVerbs_current_msg,
		  (char *)Darray_get(args, (unsigned int)0),
		  (char *)Darray_get(args, (unsigned int)1));
}

NORET AlVerbs_clear(args)
     Darray args;
{
  assert(Darray_len(args) == 1);
  AlMsg_delete_uprop(AlVerbs_current_msg, 
		     (char *)Darray_get(args, (unsigned int)0));
}

NORET AlVerbs_stop_rs(args)
     Darray args;
{
  assert(Darray_len(args) == 0);
  AlRuleRun_stop_rs();
}

static NORET set_perm_help(uprop_name, flag)
     char *uprop_name;
     Bool flag;
{
  const char *uprop_string;

  uprop_string = AlMsg_get_uprop(AlVerbs_current_msg, uprop_name);
  if (uprop_string == NULL) {
    AlMsg_set_uprop(AlVerbs_current_msg, uprop_name, "");
    AlMsg_clear_error_code(AlVerbs_current_msg);
  }
  AlMsg_set_uprop_permanence(AlVerbs_current_msg, uprop_name, flag);
}

NORET AlVerbs_set_uprop_permanent(args)
     Darray args;
{
  assert(Darray_len(args) == 1);
  set_perm_help((char *)Darray_get(args, (unsigned int)0), Bool_TRUE);
}

NORET AlVerbs_set_uprop_nopermanent(args)
     Darray args;
{
  assert(Darray_len(args) == 1);
  set_perm_help((char *)Darray_get(args, (unsigned int)0), Bool_FALSE);
}

#define RESEND_COMMAND "/bin/rmail"

static const char *current_user = (const char *)NULL;

static Darray recepients = (Darray)NULL;
static int total_recepient_length=0; /* including one extra byte per recepient */

static NORET add_recepient(new_recp)
     const char *new_recp;
{
  char *new_buffer;
  int new_recepient_length;

  assert(new_recp != (const char *)NULL);
  if (recepients == (Darray)NULL)
    recepients = Darray_create();
  new_recepient_length = strlen(new_recp)+1;
  new_buffer = (char *)Memory_allocate(new_recepient_length);
  total_recepient_length += new_recepient_length;
  (NORET)strcpy(new_buffer, new_recp);
  Darray_addh(recepients, new_buffer);
}

NORET AlVerbs_send_to_all_recepients(resend_from)
     const char *resend_from;
{
  FILE *process;
  char *command;
  int number_of_recepients;
  int i;
  char *send_command_buffer;
  int send_command_buffer_len;

  if (recepients == (Darray)NULL)
    return;

  number_of_recepients = Darray_len(recepients);
  if (number_of_recepients == 0)
    return;

  if (current_user == (const char *)NULL) {
    char *temp_ptr = (char *)Memory_allocate(OSINTER_USER_NAME_LENGTH+1);
    OSinter_get_user_name(temp_ptr);
    current_user = temp_ptr;
  }

  send_command_buffer =
    (char *) Memory_allocate((sizeof RESEND_COMMAND)+
			     total_recepient_length+
			     10	/* slop space */);
  (NORET)strcpy(send_command_buffer, RESEND_COMMAND);
  send_command_buffer[(sizeof RESEND_COMMAND)-1] = ' ';
  send_command_buffer[(sizeof RESEND_COMMAND)] = '\0';
  send_command_buffer_len = (sizeof RESEND_COMMAND);

  for (i = 0; i < number_of_recepients; ++i) {
    char *resend_to = (char *)Darray_reml(recepients);

    (NORET)strcat(send_command_buffer, resend_to);
    send_command_buffer_len += strlen(resend_to) + 1;
    send_command_buffer[send_command_buffer_len-1] = ' ';
    send_command_buffer[send_command_buffer_len] = '\0';
    Memory_free(resend_to);
  }

  total_recepient_length = 0;

  if ((process = popen(send_command_buffer, "w")) == (FILE *)NULL)
    Al_fatal_error("Could not create mail process");
  else {
    long time PROTOTYPE((long *));
    struct tm *localtime PROTOTYPE((long *));
    long t = time(0);
    struct tm *cur_time = localtime(&t);
    char time_buf[21];		/* Needs to be long to deal with
				 * long time zone (up to 5 char) */
    static const char *month_names[12] =
      {"Jan", "Feb", "Mar", "Apr",
       "May", "Jun", "Jul", "Aug", 
       "Sep", "Oct", "Nov", "Dec"};

    sprintf(&(time_buf[0]), "%2d %3s %02d %02d:%02d:%02d %-5s", 
	    cur_time->tm_mday,
	    month_names[cur_time->tm_mon],
	    cur_time->tm_year % 100,
	    cur_time->tm_hour,
	    cur_time->tm_min,
	    cur_time->tm_sec,
	    cur_time->tm_zone);	/* Need to check if non-US timezones
				 * end up in the correct format
				 * (probably not) */
    
    (NORET)fprintf(process, "Resent-Date: %s\n", time_buf);

    if (resend_from != (const char *)NULL) {
      (NORET)fprintf(process, "Resent-From: %s\n", resend_from);
      (NORET)fprintf(process, "Resent-Sender: %s\n", current_user);
    } else
      (NORET)fprintf(process, "Resent-From: %s\n", current_user);

    (NORET)fprintf(process, 
		   "Resent-To: All selected recepients <%s>\n",
		   current_user);

    (NORET)fputs(AlMsg_get_msg_buffer(AlVerbs_current_msg), process);
  }

  if (pclose(process) == -1)
    Al_fatal_error("Could not close mail process");

  Memory_free(send_command_buffer);
}

NORET AlVerbs_resend(args)
     Darray args;
{
  assert(Darray_len(args) == 1);
  assert(total_recepient_length == 0);
  add_recepient((const char *)Darray_get(args, (unsigned int)0));
  AlVerbs_send_to_all_recepients((const char *)NULL);
}

NORET AlVerbs_sendme(args)
     Darray args;
{
  assert(Darray_len(args) == 0);

  assert(AlVerbs_sendme_username != NULL);

  add_recepient(AlVerbs_sendme_username);
}

/*--------------------------------------------------*/
static char* shell_get_key(s,i)
char *s;
int  *i;
{
  RMFBuffer Key=RMFBuffer_create(NULL,10);
  Bool End, Quoted, Started;
  char c, *ret;

  End=Quoted=Started=Bool_FALSE;
  while (End!=Bool_TRUE) {
    c=s[(*i)++];
    switch (c) {
    case '\\':
      if (isdigit(s[*i])) {
	char oct;
	oct=RapString_get_char_from_octal(&(s[*i]));
	RMFBuffer_append_char(Key,oct);
	(*i)+=3;
      }
      else {
	RMFBuffer_append_char(Key,s[*i]);
	(*i)++;
      }
      break;
    case '"':
      if (Started==Bool_FALSE) 
	Quoted=Bool_TRUE;
      else {
	End=Bool_TRUE;
	(*i)--;
      }
      break;
    case ' ':
      if (Quoted==Bool_FALSE) {
	(*i)--;
	End=Bool_TRUE;
      }
      else 
	RMFBuffer_append_char(Key,c);
      break;
    case '\0':
      End=Bool_TRUE;
      if (Quoted==Bool_TRUE)
	Al_warning1("missing closing double quote in string: %s\n",&(s[*i]));
      break;
    default:
      RMFBuffer_append_char(Key,c);
      break;
    }
    Started=Bool_TRUE;
  }
  ret=RMFBuffer_copy_of_buff(Key);
  RMFBuffer_destroy(Key);
  return(ret);
}

/*--------------------------------------------------*/
static NORET shell_write_fields_to_rmfb(b,sep,raw_fields,raw_field_count)
RMFBuffer b;
int raw_field_count;
char** raw_fields;
char *sep;
{
  int j;
  for (j=0;j<raw_field_count;j++) {
    if (j>0)
      RMFBuffer_append(b,sep);
    RMFBuffer_append(b,raw_fields[j]);
  }
}

/* Thgis is the character which if seen immediately following the \m option */
 /* causes multiple fields to be seperated by ",\n" as opposed to just ", " */
#define SHELL_MFIELD_NEWLINE_OPTION ','
/*--------------------------------------------------*/
/* o. Does not handle mutliple fields by the same name yet. */
/*----------------------------------------*/
static char* shell_make_string(s)
char *s;
{
  RMFBuffer b=RMFBuffer_create(NULL,100);
  char *command, *ret;
  int i;
  i=0;
  while (s[i]!='\0') {
    if (s[i]=='\\') {
      i++;
      switch (s[i]) {
      case 'n': RMFBuffer_append_char(b,'\n'); i++; break;
      case 't': RMFBuffer_append_char(b,'\t'); i++; break;
      default: RMFBuffer_append_char(b,s[i]); i++; break;
      }
    }
    else if (s[i]=='%') {
      i++;
      switch (s[i]) {
      case 'u':
	{
	  char *key, *raw_field;
	  i++;
	  key=shell_get_key(s,&i);
	  raw_field=AlMsg_get_uprop(AlVerbs_current_msg,key);
	  if (raw_field!=NULL)
	    RMFBuffer_append(b,raw_field);
	  break;
	}
      case 'm':
	{
	  char *key, **raw_fields, *seperator;
	  int j, raw_field_count;
	  i++;
	  if (s[i]==SHELL_MFIELD_NEWLINE_OPTION) {
	    seperator=",\n";
	    i++;
	  }
	  else
	    seperator=", ";
	  key=shell_get_key(s,&i);
	  raw_field_count=AlMsg_number_of(AlVerbs_current_msg,key);
	  raw_fields=AlMsg_get_raw_fields(AlVerbs_current_msg,key);
	  shell_write_fields_to_rmfb(b,seperator,raw_fields,raw_field_count);
	  break;
	}
      case 'w':
	{
	  i+=2;
	  RMFBuffer_append(b,AlMsg_get_msg_buffer(AlVerbs_current_msg));
	  break;
	}
      case 'b':
	{
	  i+=2;
	  RMFBuffer_append(b,AlMsg_get_msg_body(AlVerbs_current_msg));
	  break;
	}
      case 'h':
	{
	  int BodyLength, MsgLength, HeaderLength;
	  char *header;
	  i+=2;
	     /* this is wasteful, I should write an RMFBuffer routine to do */
	     /* it directly instead of copying into header */
	  MsgLength=AlMsg_get_msg_char_length(AlVerbs_current_msg);
	  BodyLength=AlMsg_get_body_char_length(AlVerbs_current_msg);
	  if (MsgLength<BodyLength) Al_fatal_error("in file verbs.c: call your Argus programmer immediately!!!\n");
	  HeaderLength=MsgLength-BodyLength;
	  header=Memory_allocate(HeaderLength+1);
	  strncpy(header,AlMsg_get_msg_buffer(AlVerbs_current_msg),
		  HeaderLength);
	  header[HeaderLength]='\0';
	  RMFBuffer_append(b,header);
	  break;
	}
      default: 
	Al_warning1("Invalid format command in shell verb: %%%c\n",s[i++]);
	break;
      }
    }
    else
      RMFBuffer_append_char(b,s[i++]);
  }
  command=RMFBuffer_copy_of_buff(b);
  RMFBuffer_destroy(b);
  return(command);
}


/*--------------------------------------------------*/
NORET AlVerbs_shell(args)
     Darray args;
{
  char *command;
  FILE *p;

  command=shell_make_string((char*)Darray_get(args,(unsigned)0));
  if ((p = popen(command, "w")) == (FILE *)NULL)
    Al_warning1("Could not execute \"%s\"", command);
  else {
    Memory_free(command);
    if (Darray_len(args)==2) {
      command=shell_make_string(Darray_get(args,(unsigned)1));
      fputs(command,p);
      Memory_free(command);
    }
    (NORET)pclose(p);
  }
}



