/* FetchMail v1.40

   Name   : FetchMail preprocess
   Version: v1.40
   Date   : 19th February 1999 / 27th July 2007 for source code release.

   Purpose:

   Creator: Richard Murray.
            Shell created by BudgieSoft SeaShell v1.00 (24 Mar 1997)

*/



/* Pragmas */
#pragma warn_implicit_fn_decls
#pragma check_memory_accesses
#pragma optimise_crossjump
#pragma optimise_multiple_loads
#pragma check_stack
#pragma check_printf_formats
#pragma optimise_cse



/* Included files */
/* DeskLib */
#include "DeskLib:WimpSWIs.h" /* Low-level WIMP commands        */
#include "DeskLib:Window.h"   /* Window handling automation     */
#include "DeskLib:Core.h"     /* useful core functions          */
#include "DeskLib:Dialog2.h"  /* windows on menus made easy     */
#include "DeskLib:DragASpr.h" /* Drag sprite handler            */
#include "DeskLib:Error.h"    /* Error handler                  */
#include "DeskLib:Event.h"    /* Event despatcher               */
#include "DeskLib:EventMsg.h" /* Wimp Message event dispatcher  */
#include "DeskLib:File.h"     /* File handling routines         */
#include "DeskLib:Handler.h"  /* Default/example event handlers */
#include "DeskLib:Hourglass.h"/* Hourglass function             */
#include "DeskLib:Icon.h"     /* Icon handling automation       */
#include "DeskLib:Kbd.h"      /* Keyboard (for checking ALT)    */
#include "DeskLib:KernelSWIs.h" /* Kernel SWI definitions       */
#include "DeskLib:Menu.h"     /* Menu create & show support     */
#include "DeskLib:Resource.h" /* Handles finding resource files */
#include "DeskLib:Screen.h"   /* Getting screen size info etc   */
#include "DeskLib:SWI.h"      /* SWI definitions     	  	*/
#include "DeskLib:Time.h"     /* For Time_Monotonic()           */
#include "DeskLib:Template.h" /* Template loading and caching   */
#include "DeskLib:WimpSWIs.h" /* Wimp SWI definitions  	  	*/
/* ANSI C */
#include <ctype.h>            /* Character types                */
#include <stdio.h>            /* Standard input/output etc      */
#include <stdlib.h>           /* Standard additional library    */
#include <signal.h>	      /* For trapping problems		*/
#include <string.h>           /* String handling functions      */
#include <time.h>             /* Time functions                 */
#include "setjmp.h"
#include "stdarg.h"

/* Acorn RISC OS */
#include "kernel.h"           /* Access to RiscOS kernel        */



/* Definitions */



/* Variables */
/* DeskLib */
static icon_handle      iconbaricon;             /* Icon bar handle */
static BOOL finished    = FALSE;                 /* Done yet? */
static dialog2_block    *info;                   /* Info window, as a dialogue box */
static menu_ptr         menu = NULL;	         /* Icon bar menu */
static window_handle    preprocess_window;       /* Preprocessor window */
static window_handle	newcount_window;	 /* New messages count */
static window_handle	newmsg_window;		 /* New messages count */
/* Other */
static char             pversion[] = "1.40";     /* Version number */
static char		pdate[] = "19th February 1999";/* Date */
static _kernel_swi_regs r;                       /* SWI registers */
struct userdat                                   /* User data structure definition */
       {
          char username[33];                     /* Username, used for output filename */
          char realname[33];                     /* Realname, used in selection */
          char password[33];                     /* Password, used to ensure correct user */
          char userfile[12];                     /* Userfile, load from correct mailbox */
       };
static struct userdat   *userdata = NULL;        /* User data structure block (blank) */
struct idx                                       /* User actions structure definition */
       {
          char email[64];
          int  action;
       };
static struct idx       *actions = NULL;         /* User actions structure block (blank) */
static int    		actioncount = 0;	 /* Actions counter */
static int		actiontop = 0;		 /* Actions counter limit */
static int    		message_total = 0;	 /* Total number of messages */
static int		message_current = 0;	 /* Current message number */
static int		ackcount = 0;	  	 /* Number of ACKs */
static int		recount = 0;		 /* Number of REs */
static int		adcount = 0;		 /* Number of adverts */
static int		bincount = 0;		 /* Number of binaries */
static long int 	thismessagepsn = 0;	 /* Offset into message file */
static long int 	nextmessagepsn = 0;	 /* Offset into message file */
static char		which_savepath[256];     /* Save path */
static int		maximum_users = 0;       /* Maximum number of users */
static char		junk_s[2048];            /* Temp string */
static int		junk_v = 0;              /* Temp variable */
static int		autokillfile = FALSE;	 /* Automatic kill-file? */
static int		autobitbucket = FALSE;	 /* Automatic bit-bucket? */
static int		autoack = FALSE;	 /* Automatic ACK? */
static int		keepweeded = FALSE;	 /* Keep weeded messages? */
static int		diffreplyto = FALSE;	 /* Different reply-to address? */
static char		diffrtaddr[60];		 /* Reply to address if different */
static file_handle	plf = NULL;  		 /* Preprocessor log file */
static file_handle	messageout = NULL;	 /* Outgoing 'reply' message */
static file_handle	in = NULL;		 /* Incoming (angela) */
static file_handle	out = NULL;		 /* Outgoing (email) */
static int		msg_status = 0;		 /* Current message status */
static int 		c_defaultuser = 0;	 /* Default user to load */
static int		c_stdmbox = TRUE;	 /* Default mailbox */
static int		c_logmbox = FALSE;	 /* Default mailbox */
static int		c_svdmbox = FALSE;	 /* Default mailbox */
static int		c_binstrip = FALSE;	 /* Default strip */
static int		c_hdrstrip = FALSE;	 /* Default strip */
static int		c_sigstrip = FALSE;	 /* Default strip */
static int		c_shorthdr = FALSE;	 /* Short headers if header stripped? */
static int		c_p_noreject = FALSE;	 /* Don't allow rejection */
static int		c_p_ignoreua = FALSE;	 /* Ignore UserActions */
static int		c_p_preserve = FALSE;	 /* Preserve deleted messages */
static int		c_p_norecog = FALSE;	 /* Don't show recognised */
static int		c_p_autppre = FALSE;	 /* Automatic preprocessor */

/* Liberal use of junk_s and junk_v is permissable, but such use must NEVER be expected
   to remain constant EVEN between blocks of code. If other (even extraneous) functions
   are called then you should use a local temporary variable. */




/* Function prototypes */
static void initialise_wimp(void);
static BOOL info_show(event_pollblock *event, void *reference);
static void info_handler(dialog2_block *dialog2);
static BOOL iconbar_handler(event_pollblock *event, void *reference);
static BOOL null_handler(event_pollblock *event, void *reference);
static BOOL menu_choice(event_pollblock *event, void *reference);
static BOOL preprocess_clickhandler(event_pollblock *event, void *reference);
static BOOL newcount_clickhandler(event_pollblock *event, void *reference);
static BOOL newmsg_clickhandler(event_pollblock *event, void *reference);
static void jewel(void);
static int  read_line(char *, file_handle);
static void load_settings(void);
static int  do_newpreprocessor(void);
static void get_who_from(char *, char *);
static void get_name_parameter(char *, char *);
static void get_parameter(char *, char *);
static void create_message_handle(char *, char *, char *);
static void drop_into_outgoing(char *);
static void add_signature(char *);
static void add_terminator(void);
static void do_plf(char *);
static void display_amount_done(int, int);
static int  check_message_id(char *);
static int  is_binary_start(char *);
static void lowercase(char []);
static void load_actions(void);
static void save_actions(void);
static void releaseactions(void);
static void add_action(int, char *);
static void open_mailfiles(void);
static void close_mailfiles(void);
static void get_new_message(void);
static void copy_message(void);
static void bounce_message(void);
static void reject_message(void);
static void log(char *, ...);




/* Program entry point */
int main(int argc, char *argv[])
{
   argc = argc;
   argv = argv;

   /* Load options */
   load_settings();

   /* Pull together enough bits to be able to at least pop up an error box if needed */
   Resource_Initialise("FetchMail");
   Event_Initialise("FetchMail preprocessor");
   initialise_wimp();

   log("FetchMail preprocessor started...");
   load_actions();
   junk_v = do_newpreprocessor();
   if (junk_v != TRUE)
   {
      if (junk_v != 2)
      {
         strcpy(junk_s, "%Copy <FetchMail$Dir>.angela <PMailIn$File> ~CDFL~N~PQ~V");
         r.r[0] = (int)junk_s;
         _kernel_swi(SWI_OS_CLI, &r, &r);
      }

      save_actions();
      Template_ClearAll();
      Event_CloseDown();
      exit(EXIT_SUCCESS);
   }

   /* If preprocessing then open the question window */
   if (message_total == 1)
      Icon_printf(newcount_window, 1, "You have %d email waiting to be read.", message_total);

   if (message_total > 1)
      Icon_printf(newcount_window, 1, "You have %d emails waiting to be read.", message_total);

   Icon_printf(newcount_window, 2, "(%d replies; %d acks; %d adverts; %d binaries)", recount, ackcount, adcount, bincount);
   Window_Show(newcount_window, open_CENTERED);

   /* Poll loop. Uses idle. Less load on system. */
   while( !finished )
   {
      Wimp_PollIdle(event_mask, &event_lastevent, (Time_Monotonic() + 5000));
      Event_Process(&event_lastevent);
   }

   /* Introducing Trevor McDonald's "And finally..." tour! */
   Template_ClearAll();
   Event_CloseDown();
   exit(EXIT_SUCCESS);
}



static void initialise_wimp(void)
{
   /* Initialise the libraries and set up associated crap */
   Resource_LoadSprites();
   EventMsg_Initialise();
   Screen_CacheModeInfo();
   Template_Initialise();
   Template_UseSpriteArea(resource_sprites);
   Template_LoadFile("Resources.P_template");

   /* Define our menu */
   menu = Menu_New("FM preproc", ">Info,Quit");
   Event_Claim(event_MENU, event_ANY, event_ANY, menu_choice, NULL);

   /* Load info window. It's a transient dialogue box. */
   info = Dialog2_CreateDialogBlock("info", -1, -1, info_handler, NULL, NULL);
   Menu_Warn(menu, 0, TRUE, info_show, info);

   /* Load preprocessor window */
   preprocess_window = Window_Create("preprocess", 45);
   if (preprocess_window == 0)
   {
      Error_ReportFatal(0, "I cannot find template definition \'%s\'", "preprocess");
   }
   Event_Claim(event_CLICK, preprocess_window, event_ANY, preprocess_clickhandler, NULL);

   /* Load new mail count window */
   newcount_window = Window_Create("newcount", 45);
   if (newcount_window == 0)
   {
      Error_ReportFatal(0, "I cannot find template definition \'%s\'", "newcount");
   }
   Event_Claim(event_CLICK, newcount_window, event_ANY, newcount_clickhandler, NULL);

   /* Load new message window */
   newmsg_window = Window_Create("newmsg", 45);
   if (newmsg_window == 0)
   {
      Error_ReportFatal(0, "I cannot find template definition \'%s\'", "newmsg");
   }
   Event_Claim(event_CLICK, newmsg_window, event_ANY, newmsg_clickhandler, NULL);

   /* Set up the default handlers to save me working out how to call a SWI :-) */
   Event_Claim(event_CLOSE, event_ANY, event_ANY, Handler_CloseWindow, NULL);
   Event_Claim(event_REDRAW, event_ANY, event_ANY, Handler_NullRedraw, NULL);
   Event_Claim(event_OPEN, event_ANY, event_ANY, Handler_OpenWindow, NULL);

   /* Stick an iconbar icon on, um, guess...
      But only if not in preprocess mode */
   iconbaricon = Icon_BarIconUser("!fetchmail", iconbar_RIGHT, (unsigned int *)resource_sprites);
   Event_Claim(event_CLICK, -2, iconbaricon, iconbar_handler, NULL);

   /* Other useful stuff we might /just/ be interested in knowing about */
   EventMsg_Claim(message_MODECHANGE, event_ANY, Handler_ModeChange, NULL);

   return;
}



static BOOL info_show(event_pollblock *event, void *reference)
{
   /* This routine wants to be a function when it grows up */
   Dialog2_OpenDialogMenuLeaf(event, (dialog2_block *) reference);

   return TRUE;
}



static void info_handler(dialog2_block *dialog2)
{
   /* Patch the correct version into the info window */

   Icon_printf(dialog2->window, 4, "%s %s", pversion, pdate);

   return;
}



static BOOL iconbar_handler(event_pollblock *event, void *reference)
{
   /* If you click SELECT, open the fetchwhich window. MENU opens the menu (like dah!)
      and ADJUST opens the configuration window.
   */

   reference = reference;

   if (event->data.mouse.button.data.menu)
   {
      Menu_Show(menu, event->data.mouse.pos.x,-1);
   }

   return TRUE;
}



static BOOL null_handler(event_pollblock *event, void *reference)
{
   /* Does nothing, allows 'null poll' routines. */

   reference = reference;
   event = event;

   return TRUE;
}



static BOOL menu_choice(event_pollblock *event, void *reference)
{
   /* Deals with menu selections. */

   mouse_block ptr;

   if (menu_currentopen != menu) return FALSE;

   switch(event->data.selection[0])
   {

      case 0 : Dialog2_OpenDialogMenuLeaf(event, (dialog2_block *)reference);
               break;
      case 1 : finished = TRUE;
               break;
      default: break;
   }

   Wimp_GetPointerInfo(&ptr);  /* If ADJUST was used to select, then keep the menu open. */
   if (ptr.button.data.adjust) Menu_ShowLast();

   return TRUE;
}



static BOOL preprocess_clickhandler(event_pollblock *event, void *reference)
{
   /* Handles mouse clicks on the 'preprocess' window */

   reference = reference;
   event = event;

   return TRUE;
}



static BOOL newcount_clickhandler(event_pollblock *event, void *reference)
{
   /* Handles mouse clicks on the 'newcount' window */

   int icon = -1;

   reference = reference;
   icon = event->data.mouse.icon;

   if (!event->data.mouse.button.data.menu)
   {
      icon = event->data.mouse.icon;

      switch(icon)
      {
         case  3 : Window_Hide(newcount_window);
               	   Window_Show(newmsg_window, open_CENTERED);
		   open_mailfiles();
               	   break;
         case  4 : strcpy(junk_s,"%Copy <FetchMail$Dir>.angela <PMailIn$File> ~CDFL~N~PQ~V");
                   r.r[0] = (int)junk_s;
                   _kernel_swi(SWI_OS_CLI, &r, &r);
                   finished = TRUE;
                   break;
         default : break;
      }
   }

   return TRUE;
}



static BOOL newmsg_clickhandler(event_pollblock *event, void *reference)
{
   /* Handles mouse clicks on the 'newmsg' window */

   int icon = -1;

   reference = reference;
   icon = event->data.mouse.icon;

   if (!event->data.mouse.button.data.menu)
   {
      icon = event->data.mouse.icon;

      switch(icon)
      {
         case  1 : raise(7);
               	   break;
         case 18 : if (msg_status != 'O')     /* Okay */
                   {
                      Icon_GetText(newmsg_window, 6, junk_s);
                      add_action('O', junk_s);
                   }
              	   /* fall through */
         case 19 : copy_message();            /* Keep */
               	   message_current++;
               	   get_new_message();
               	   break;
         case 20 : bounce_message();          /* Bounce */
              	   message_current++;
              	   get_new_message();
              	   break;
         case 21 : reject_message();          /* Reject */
                   Icon_GetText(newmsg_window, 6, junk_s);
                   add_action('R', junk_s);
              	   message_current++;
              	   get_new_message();
              	   break;
         case 22 : message_current++;         /* Kill */
               	   get_new_message();
               	   break;
         case 23 : strcpy(junk_s,"%Filer_Run <FetchMail$Dir>.Data.headers");
                   r.r[0] = (int)junk_s;
                   _kernel_swi(SWI_OS_CLI, &r, &r);
                   break;
         case 26 : strcpy(junk_s,"%Filer_Run <FetchMail$Dir>.Data.messages");
                   r.r[0] = (int)junk_s;
                   _kernel_swi(SWI_OS_CLI, &r, &r);
                   break;
         default : break;
      }
   }

   return TRUE;
}



static void jewel(void)
{
   /* :-) */

   printf("\n\n\nFoolish Games  (Jewel Kilcher; Pieces of Jewel)\n~~~~~~~~~~~~~\n\nYou took your coat off and stood in the rain,\nYou were always crazy like that.\nI watched from my window, always felt I was outside looking in on you.\nYou were always the mysterious one with dark eyes and careless hair,\nYou were fashionably sensitive, but too cool to care.\nThen you stood in my doorway, with nothing to say,\nBesides some comment on the weather.\nWell in case you failed to notice, in case you failed to see;\nThis is my heart bleeding before you, this is me down on my knees.\n\nThese foolish games are tearing me apart.\nYour thoughtless words are breaking my heart.\nYou're breaking my heart.\n\nYou were always brilliant in the morning,\nSmoking your cigarettes, talking over coffee.\nYour philosophies on art, Baroque moves you.\nYou loved Mozart and you'd speak of your loved ones,\nAs I clumsily strummed my guitar.\nYou'd teach me of honest things.\nThings that were daring, things that were clean.\nThings that knew what an honest dollar did mean.\nSo I hid my soiled hands behind my back.\nSomewhere along the line I must have gone off track with you.\nExcuse me, I think I've mistaken you for somebody else.\nSomebody who gave a damn,\nSomebody more like myself.\n\nThese foolish games are tearing me apart.\nYour thoughtless words are breaking my heart.\nYou're breaking my heart.\n\nYou took your coat off and stood in the rain,\nYou were always crazy like that.\n\n");
   return;
}



static int  read_line(char *line, file_handle hdl)
{
   /* Read a line and return TRUE if EOF/error condition or FALSE if okay. */
   int eofcond = FALSE;
   int byte = 0;
   int offset = 0;

   offset = 0;
   line[offset] = '\0';

   do
   {
      byte = File_ReadByte(hdl);
      if (byte > 31)
      {
         line[offset] = byte;
         offset++;
      }
   } while (byte > 31);
   line[offset] = '\0';

   if (eofcond == 1) line[0] = '\0';

   /* Weird code huh?
      The newline character is (wisely?) retained, so here we strip it off.
   */

   if (byte == -1) eofcond = TRUE;

   return eofcond;
}



static void load_settings(void)
{
   /* Load settings.
      MUST be called AFTER wimp init because it uses/alters templates.
   */

   in = File_Open("<FetchMail$Dir>.Resources.Options", file_READ);
   if (in == NULL)
   {
      Error_Report(1, "Unable to load from the Options file, does it exist?");
      return;
   }

   junk_v = File_ReadByte(in);
   if (junk_v < 2)
   {
      Error_Report(1, "You are trying to use the old format Options file (versions prior to 1.30) which is not compatible with this version of FetchMail, exiting...");
      File_Close(in);
      exit(0);
   }

   if (junk_v > 2)
   {
      Error_Report(1, "You are trying to use an unrecognised options file (format byte is '%d', expecting '2'), exiting...", junk_v);
      File_Close(in);
      exit(0);
   }

   junk_v = File_ReadByte(in); /* Skip newline */

   c_defaultuser = File_ReadByte(in);
   c_stdmbox = File_ReadByte(in);
   c_logmbox = File_ReadByte(in);
   c_svdmbox = File_ReadByte(in);
   c_binstrip = File_ReadByte(in);
   c_hdrstrip = File_ReadByte(in);
   c_sigstrip = File_ReadByte(in);
   c_shorthdr = File_ReadByte(in);
   c_p_noreject = File_ReadByte(in);
   c_p_ignoreua = File_ReadByte(in);
   c_p_preserve = File_ReadByte(in);
   c_p_norecog = File_ReadByte(in);
   c_p_autppre = File_ReadByte(in);
   File_Close(in);

   return;
}



static int  do_newpreprocessor(void)
{
   /* The new preprocessor routine - simply counts messages */

   int  endoffile = 0;
   int  filelength = 0;

   if ( (in = File_Open("<FetchMail$Dir>.angela", file_READ)) != NULL)
   {
      /* ANGELA FILE EXISTS - POSSIBLE PREVIOUS CRASH? */
      File_Close(in);
      Error_Report(1, "The file \"!FetchMail.angela\" exists... If FetchMail crashed last time you used it, YOUR EMAIL IS WITHIN THIS FILE. FetchMail will not preprocess until this file has been removed.");
      return 2;
   }

   strcpy(junk_s, "%Copy <PMailIn$File> <FetchMail$Dir>.angela ~CDFL~N~PQ~V");
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   if ( (in = File_Open("<FetchMail$Dir>.angela", file_READ)) == NULL)
   {
      /* No mail - so we can safely remove message IDs... */
      strcpy(junk_s, "%Remove <FetchMail$Dir>.Data.MessageIDs");
      r.r[0] = (int)junk_s;
      _kernel_swi(SWI_OS_CLI, &r, &r);

      return FALSE;
   }

   message_total = 0;

   filelength = File_ReadExtent(in);
   if (filelength == 0)
   {
      /* Mail file exists, but is empty */
      File_Close(in);

      strcpy(junk_s, "%Remove <FetchMail$Dir>.angela");
      r.r[0] = (int)junk_s;
      _kernel_swi(SWI_OS_CLI, &r, &r);

      /* So we can safely remove message IDs... */
      strcpy(junk_s, "%Remove <FetchMail$Dir>.Data.MessageIDs");
      r.r[0] = (int)junk_s;
      _kernel_swi(SWI_OS_CLI, &r, &r);

      return FALSE;
   }

   read_line(junk_s, in);
   if (strstr(junk_s, "#! rmail") == NULL)
   {
      /* All but ALL messages begin "#! rmail" */
      Error_Report(1, "Mail file found, but does not contain valid mail header...");
      File_Close(in);
      return FALSE;
   }

   Event_Claim(event_NULL, event_ANY, event_ANY, null_handler, NULL);
   Icon_SetText(preprocess_window, 4, "FetchMail is preprocessing your email...");
   Window_Show(preprocess_window, open_CENTERED);

   do
   {
      message_total++;

      thismessagepsn = File_ReturnPos(in);
      display_amount_done((int)thismessagepsn, (int)filelength);

      Wimp_Poll(event_mask, &event_lastevent);
      Event_Process(&event_lastevent);

      do
      {
         endoffile = read_line(junk_s, in);
         lowercase(junk_s);

         if (!endoffile)
         {
            if ((strstr(junk_s, "subject:	") != NULL) ||
                (strstr(junk_s, "subject: ") != NULL))
            {
               if ((junk_s[9]=='a') && (junk_s[10]=='c') && (junk_s[11]=='k') &&
                   (junk_s[12]==':') && (junk_s[13]==' ')) ackcount++;

               if ((junk_s[9]=='r') && (junk_s[10]=='e') && (junk_s[11]==':')) recount++;
            }

            if ((strstr(junk_s, "x-advertisement:	") != NULL) ||
                (strstr(junk_s, "x-advertisement: ") != NULL)) adcount++;

            if (is_binary_start(junk_s) == TRUE) bincount++;
         }

      } while (strstr(junk_s, "#! rmail") == NULL && !endoffile);

   } while (endoffile == 0);

   Window_Hide(preprocess_window);
   Event_Release(event_NULL, event_ANY, event_ANY, null_handler, NULL);

   /* Okay folks, that's a wrap */
   File_Close(in);

   return TRUE;
}



static void get_who_from(char *out, char *in)
{
   /* Look for who the mail is from.
      It'll first look between < and >
         (for "Richard Murray <rmurray@argonet.co.uk>").
      If that fails, it'll take up to the first space
         (for "rmurray@argonet.co.uk (Richard Murray)".
      Then it checks an '@' symbol is present.

      Twiddled to also pick up on comma ',' seperated multiple
      addresses. Will use the first one found.
   */

   int offset = 0;
   int length = 0;
   int count = 0;

   length = strlen(in);
   offset = strcspn(in, "<");

   if (offset != strlen(in))
   {
      length = strlen(in);
      offset++;
      for (; offset < (length-1); offset++)
      {
         out[count] = in[offset];
         count++;
      }
      out[count] = '\0';
   }
   else
   {
      offset = strcspn(in, ",");

      if (offset != strlen(in))
      {
         length = strlen(in);
         offset++;
         for (; offset < (length-1); offset++)
         {
            out[count] = in[offset];
            count++;
         }
         out[count] = '\0';
      }
      else
      {
         offset = strcspn(in, " ");
         offset++;
         while (in[offset] != ' ' && in[offset]!='\0')
         {
            out[count] = in[offset];
            offset++;
            count++;
         }
         out[count] = '\0';
      }
   }

   offset = strcspn(out, "@");
   if (offset == strlen(out))
   {
      do_plf("Unable to find a valid user ID from input.");
      Error_Report(1,"Email parsing error - unable to find valid ID with input \"%s\".", in);
   }

   return;
}



static void get_name_parameter(char *out, char *in)
{
   /* Look for a space and return what you find after. Also strips any '<' or '>' found. */

   int offset = 0;
   int length = 0;
   int count = 0;
   int byte = 0;

   offset = (strcspn(in, " ") + 1);
   length = strlen(in);

   for (; offset <= length; offset++)
   {
      byte = in[offset];
      if ((byte != '<') && (byte != '>'))
      {
         out[count] = byte;
         count++;
      }
   }
   out[count] = '\0';

   return;
}



static void get_parameter(char *out, char *in)
{
   /* Look for a space and return what you find after */

   int offset = 0;
   int length = 0;
   int count = 0;

   offset = (strcspn(in, " ") + 1);
   length = strlen(in);

   for (; offset <= length; offset++)
   {
      out[count] = in[offset];
      count++;
   }
   out[count] = '\0';

   return;
}



static void create_message_handle(char *to, char *from, char *subject)
{
   /* Create a nice outgoing email header and envelope. */

   char timevar[40];
   char timevar2[16];
   int  timeblock[16];

   /* Get this sequence number */
   messageout = File_Open("<Posty$Dir>.Transport.sequence", file_READ);
   read_line(junk_s, messageout);
   File_Close(messageout);

   junk_v = atoi(junk_s);
   /*Error_Report(1, "char = %s ; int = %d (%l)", junk_s, junk_v, junk_v);*/

   /* Output sequence number +2 for next time */
   messageout = File_Open("<Posty$Dir>.Transport.sequence", file_WRITE);
   File_printf(messageout, "%d", (junk_v + 2)); /* No "\n" */
   File_Close(messageout);

   /* Create envelope */
   sprintf(junk_s, "<PMailEnv$Dir>.%d", junk_v);
   messageout = File_Open(junk_s, file_WRITE);
   File_printf(messageout, "\n%s\n%s\n", from, to);
   File_Close(messageout);

   /* Create message header */

   sprintf(junk_s, "<PMailOut$Dir>.%d", junk_v);
   messageout = File_Open(junk_s, file_WRITE);
   File_Close(messageout);

   /* Set it to textual type (just in case!?). */
   sprintf(junk_s, "%%SetType <PMailOut$Dir>.%d &FFF", junk_v);
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   /* Reopen */
   sprintf(junk_s, "<PMailOut$Dir>.%d", junk_v);
   messageout = File_Open(junk_s, file_WRITE);

   /* Sort out the time */
   timeblock[0] = 3;                                  /* Get the five-byte time */
   r.r[0] = 14;
   r.r[1] = (int)timeblock;
   _kernel_swi(SWI_OS_Word, &r, &r);

   strcpy(junk_s, "%W3, %DY %M3 %CE%YR %24:%MI:%SE");    /* Convert to a string */
   r.r[0] = (int)timeblock;
   r.r[1] = (int)timevar;
   r.r[2] = 40;
   r.r[3] = (int)junk_s;
   _kernel_swi(SWI_OS_ConvertDateAndTime, &r, &r);

   strcpy(junk_s, "%TZ");                             /* Get the timezone */
   r.r[0] = (int)timeblock;
   r.r[1] = (int)timevar2;
   r.r[2] = 16;
   r.r[3] = (int)junk_s;
   _kernel_swi(SWI_OS_ConvertDateAndTime, &r, &r);

   switch (timevar2[0])
   {
      case 'G' : strcat(timevar, " +0000");          /* If Greenwich Mean Time */
      	       	 break;
      case 'B' : strcat(timevar, " +0100");          /* If British Summer Time */
      	       	 break;
      default  : strcat(timevar, " ");                /* Otherwise append timezone */
      	       	 strcat(timevar, timevar2);
      	       	 break;
   }

   /* time(&now);
      timepointer = localtime(&now);
      strftime(timevar, 40, "Date: %a, %d %b %y %H:%M:%S", timepointer);
   */

   File_printf(messageout, "From: %s\n", from);
   File_printf(messageout, "Reply-To: %s\n", from);
   File_printf(messageout, "To: %s\n", to);
   File_printf(messageout, "Envelope-To: %s\n", to);
   File_printf(messageout, "Subject: %s\n", subject);
   File_printf(messageout, "Date: %s\n", timevar);
   File_printf(messageout, "X-Mailer: BudgieSoft FetchMail preprocessor v%s\n", pversion);
   File_printf(messageout, "X-Creator: Message created by automated software.\n");
   /* File_printf(messageout, "Errors-To: support@heyrick.co.uk\n");
      ^^^ put your own email address here - DO NOT USE MINE (Heyrick doesn't work anyway) */
   File_printf(messageout, "\n");

   return;
}



static void drop_into_outgoing(char *fnm)
{
   /* Takes the filename pointed to, and drops the contents into the outgoing
      message, stripping lines beginning ';'.
   */
   file_handle widget;
   int  eofcheck = 0;

   if ( (widget = File_Open(fnm, file_READ)) == NULL)
   {
      do_plf("Missing a resource file, path follows:");
      do_plf(fnm);
      File_printf(messageout, "\n\n*** ERROR ***\n");
      File_printf(messageout, "Cannot open file \"%s\"!\n", fnm);
      File_printf(messageout, "*** ERROR ***\n\n");

      return;
   }

   do
   {
      eofcheck = read_line(junk_s, widget);
      if (junk_s[0] != ';') File_printf(messageout, "%s\n", junk_s);
   } while (!eofcheck);

   File_Close(widget);

   return;
}



static void add_signature(char *to)
{
   /* Looks for a signature file based upon name message sent to. */

   int  offset = 0;
   int  eofcheck = 0;
   char whatname[12] = "";
   file_handle widget;

   while ((to[offset] != '.') && (to[offset] != '@') && (to[offset] != '\0') && (strlen(whatname) < 10))
   {
      whatname[offset] = to[offset];
      offset++;
   }
   whatname[offset] = '\0';

   sprintf(junk_s, "<FetchMail$Dir>.Data.signoffs.%s", whatname);

   if ( (widget = File_Open(junk_s, file_READ)) == NULL)
   {
      do_plf("Missing signoff resource file for:");
      do_plf(whatname);
      /* If missing signoff due to weird name, then try to open 'default'. */
      if ( (widget = File_Open("<FetchMail$Dir>.Data.signoffs.default", file_READ)) == NULL)
      {
         do_plf("Missing default signoff resource file.");
         File_printf(messageout, "\n\n*** ERROR ***\n");
         File_printf(messageout, "Cannot open signoff file \"%s\" or the default!\n", whatname);
         File_printf(messageout, "*** ERROR ***\n\n");
      }
      return;
   }

   do
   {
      eofcheck = read_line(junk_s, widget);
      if (junk_s[0] != ';') File_printf(messageout, "%s\n", junk_s);
   } while (!eofcheck);

   File_Close(widget);

   return;
}



static void add_terminator(void)
{
   /* Add the terminator bit here... */

   File_printf(messageout, "-- \n");
   File_printf(messageout, "FetchMail v%s [%s]\n", pversion, pdate);
   File_printf(messageout, "Copyright (C) 1997-2007 Rick Murray\n\n");
   /* ^^^ if you extend the code, you can add "and 2007 Your Name" or whatever */

   return;
}



static void do_plf(char *in)
{
   /* Write entry to preprocessor log file */

   struct tm *timepointer;
   time_t now;
   char timevar[40];

   if (plf == NULL) return;

   time(&now);
   timepointer = localtime(&now);
   strftime(timevar, 40, "%d%m%Y%H%M%S", timepointer);

   File_printf(plf, "%s : %s\n", timevar, in);

   return;
}



static void display_amount_done(int ptr, int ext)
{
   /* Painful to create (until I realised that 'int' loses the stuff after the
      decimal point! :-) ) but quite 'fun' hacking around at low-level. I was
      surprised DeskLib didn't feed me a "resize icon" function, it has practically
      everything else! Ah - maybe that is what the slider is for? But it seemed
      more hassle to code the slider than this.
      It should all make sense... */

   icon_handle iconhandle;
   icon_block base;
   icon_createblock slider;
   window_redrawblock redrawit;
   int  basesize = 0;
   int  slidersize = 0;
   float multiply = 0;
   int  percentage = 0;
   int  icon = 3;

   percentage = (int)((ptr * 100) / ext);

   if (percentage < 0) percentage = 0;
   if (percentage > 99) percentage = 99;

   if (percentage > 10) Icon_printf(preprocess_window, icon, "%d%%", percentage);

   Wimp_GetIconState(preprocess_window, (icon - 1), &base);
   Wimp_GetIconState(preprocess_window, icon, &slider.icondata);

   redrawit.window = preprocess_window;
   redrawit.rect = slider.icondata.workarearect;
   redrawit.rect.max.x += 400;

   slidersize = slider.icondata.workarearect.max.x - slider.icondata.workarearect.min.x;
   basesize = base.workarearect.max.x - base.workarearect.min.x;
   multiply = (float)basesize / 100;
   multiply = multiply * (float)percentage;
   slider.icondata.workarearect.max.x = slider.icondata.workarearect.min.x + (int)multiply;

   slider.window = preprocess_window;
   Wimp_DeleteIcon(preprocess_window, icon);
   Wimp_CreateIcon(&slider, &iconhandle);

   /*Wimp_SetIconState(preprocess_window, 9, 0, 0);*/
   Wimp_ForceRedraw(&redrawit);

   Wimp_Poll(event_mask, &event_lastevent);
   Event_Process(&event_lastevent);

   return;
}



static int  check_message_id(char *messageid)
{
   /* Checks message ID against those in the database. If it exists, return non-zero
      else add ID to database and return zero.
   */

   file_handle id;
   int  eofcheck = 0;
   int  foundit = 0;

   /* Open the message ID list file */
   if ( (id = File_Open("<FetchMail$Dir>.Data.MessageIDs", file_READ)) == NULL)
   {
      do_plf("Can't open Message IDs file, we must assume the ID is unknown. :-)");
   }
   else
   {
      /* Check through each entry looking to make an EXACT match. */
      do
      {
         eofcheck = read_line(junk_s, id);
         if (junk_s[0] != '\0' && junk_s[0] != ';')
            if (strstr(messageid, junk_s) != NULL) foundit = 1;
      } while (!eofcheck && !foundit);

      File_Close(id);
   }

   if (!foundit)
   {
      /* Add current ID to list... */
      if ( (id = File_Open("<FetchMail$Dir>.Data.MessageIDs", file_APPEND)) == NULL)
      {
         Error_ReportFatal(1, "cannot add new ID (cannot open \'MessageIDs\' file)");
      }

      File_Seek(id, File_ReadExtent(id));

      File_printf(id, "%s\n", messageid);

      File_Close(id);
   }

   return foundit;
}



static int  is_binary_start(char *line)
{
   /* Look for:
        "begin <number><number><number>"
      or
        "Content-Transfer-Encoding:"
   */
   char *marker;

   /* Is there a 'begin' present? */
   marker = strstr(line, "begin");
   if (marker != NULL)
   {
      /* If offset is not 0, it is not a UUcode */
      if ((marker - line) != 0) return FALSE;

      /* UUcode? Let's see if next character is a space, followed by three digits */
      if (line[5] != ' ') return FALSE;
      if (isdigit(line[6]) == FALSE) return FALSE;
      if (isdigit(line[7]) == FALSE) return FALSE;
      if (isdigit(line[8]) == FALSE) return FALSE;

      /* I guess it is a UUcode! */
      return TRUE;
   }

   /* Is there a 'Content-Transfer-Encoding: BASE64' present?
      We have to watch out for "Quoted-Printable" type headers... */
   marker = strstr(line, "Content-Transfer-Encoding: BASE64");
   if (marker != NULL)
   {
      /* If offset is not 0, it is not valid... */
      if ((marker - line) != 0) return FALSE;

      /* ...otherwise assume MIME start (text portion has 'content-type' header) */
      return TRUE;
   }

   /* MIME header, in lowercase (some routines use lowercase), this will become
      default when *all* scanning is case insensitive */
   marker = strstr(line, "content-transfer-encoding: base64");
   if (marker != NULL)
   {
      /* If offset is not 0, it is not valid... */
      if ((marker - line) != 0) return FALSE;

      /* ...otherwise assume MIME start (text portion has 'content-type' header) */
      return TRUE;
   }

   return FALSE;
}



static void lowercase(char string[])
{
   int i=0;

   while ( string[i] )
   {
      string[i] = tolower(string[i]);
      i++;
   }

   return;
}



static void load_actions(void)
{
   /* Load UserAction file */
   file_handle fp;
   int  size = 0;

   log("Loading user actions.");
   if ( (fp = File_Open("<FetchMail$Dir>.Data.UserAction", file_READ)) == NULL)
   {
      log("Unable to load user actions.");
      Error_ReportFatal(1,"unable to open user actions file");
   }

   read_line(junk_s, fp);
   actioncount = atoi( junk_s );
   actiontop = actioncount + 25;

   log("Defined user actions: actioncount = %d, actiontop = %d.", actioncount, actiontop);

   read_line(junk_s, fp);
   log("Read user actions version: \"%s\".", junk_s);
   junk_v = atoi( junk_s );
   if (junk_v != 1)
   {
      Error_ReportFatal(1, "format of user actions file is unrecognised; expecting type \'1\' received type \'%d\'", junk_v);
   }

   actions = calloc(actiontop, sizeof(struct idx));
   if (!actions)
   {
      Error_ReportFatal(1, "unable to claim memory to hold user actions - increase WimpSlot and try again");
   }
   atexit(releaseactions);

   for (junk_v = 0; junk_v < actioncount; junk_v++)
   {
      read_line(junk_s, fp);

      actions[junk_v].action = junk_s[0];
      strcpy(actions[junk_v].email, (junk_s + 2));

      /* Truncate unnecessary newline character */
      size = strlen(actions[junk_v].email) - 1;
      actions[junk_v].email[size] = '\0';
   }

   File_Close(fp);
   log("User actions loaded.");

   return;
}



static void save_actions(void)
{
   /* Save UserAction file */
   file_handle fp;

   if ( (fp = File_Open("<FetchMail$Dir>.Data.UserAction", file_WRITE)) == NULL)
   {
      Error_ReportFatal(1,"unable to open user actions file for update");
   }

   File_printf(fp, "%d\n1\n", actioncount);

   for (junk_v = 0; junk_v < actioncount; junk_v++)
      File_printf(fp, "%c %s\n", actions[junk_v].action, actions[junk_v].email);

   File_Close(fp);

   return;
}



static void releaseactions(void)
{
   if (actions)
   {
      free(actions);
      actions = NULL;
   }
}



static void add_action(int action, char *addr)
{
   /* Add an action to the list of available actions.
      Does not currently include 'sanity' checking (so technically the same user
      could have several actions set up... only, the UI is /supposed/ to take
      care of this...
   */
   if (actioncount == actiontop)
   {
      Error_Report(1, "Unable to add new user action, please rerun FetchMail...");
      return;
   }

   actions[actioncount].action = action;
   strcpy(actions[actioncount].email, addr);
   actioncount++;

   return;
}



static void open_mailfiles(void)
{
   /* Open the 'angela' file for reading, and the 'Email' file for updating. */

   nextmessagepsn = 0;
   message_current++;
   if ( (in = File_Open("<FetchMail$Dir>.angela", file_READ)) == NULL)
   {
      Error_ReportFatal(1,"\'angela\' file missing, lost or dead since last time I looked!?");
   }

   if ( (out = File_Open("<PMailIn$File>", file_WRITE)) == NULL)
   {
      File_Close(in);
      Error_ReportFatal(1, "unable to open DTposty's email file for update - your email is stored within FetchMail as \'angela\'...");
   }
   get_new_message();

   return;
}



static void close_mailfiles(void)
{
   /* Close the mail files and tidy up. */

   File_Close(in);
   File_Close(out);

   strcpy(junk_s, "%Remove <FetchMail$Dir>.angela");
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   strcpy(junk_s, "%SetType <PMailIn$File> &FFF");
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   return;
}



static void get_new_message(void)
{
   /* Get new message from the email file. Record our position. */

   file_handle hdrfile;
   file_handle msgfile;
   char to[1024] = "";
   char envto[128] = "";
   char from[512] = "";
   char subject[256] = "";
   char date[40] = "";
   char messageid[80] = "";
   char line1[80] = "";
   char line2[80] = "";
   int  endoffile = 0;
   int  foundit = 0;

   msg_status = 0;
   if (message_current > message_total)
   {
      save_actions();
      close_mailfiles();
      Template_ClearAll();
      Event_CloseDown();
      exit(EXIT_SUCCESS);
   }

   File_Seek(in, (int)nextmessagepsn); /* Move to offset */
   thismessagepsn = (int)File_ReturnPos(in); /* Remember it */

   /* Read information from a message */
   read_line(junk_s, in);
   if (strstr(junk_s, "#! rmail") == NULL)
   {
      /* All but ALL messages begin "#! rmail" */
      File_Close(in);
      File_Close(out);
      Error_ReportFatal(1, "mail file contains invalid header (at message %d, offset %ld)", message_current, thismessagepsn);
      return;
   }

   /* Open 'header' file */
   hdrfile = File_Open("<FetchMail$Dir>.Data.headers", file_WRITE);
   /* Open 'message' file */
   msgfile = File_Open("<FetchMail$Dir>.Data.messages", file_WRITE);

   do
   {
      /* Loop through header picking out valid information */
      read_line(junk_s, in);

      /* Write to headers file */
      File_printf(hdrfile, "%s\n", junk_s);
      File_printf(msgfile, "%s\n", junk_s);

      /* Look for it with TABs... */
      if (strstr(junk_s, "Envelope-to:	") != NULL) get_name_parameter(envto, junk_s);
      if (strstr(junk_s, "From:	") != NULL) get_who_from(from, junk_s);
      if (strstr(junk_s, "To:	") != NULL && (junk_s[0] == 'T')) get_name_parameter(to, junk_s);
      if (strstr(junk_s, "Subject:	") != NULL) get_parameter(subject, junk_s);
      if (strstr(junk_s, "Message-ID:	") != NULL) get_parameter(messageid, junk_s);
      if (strstr(junk_s, "Message-Id:	") != NULL) get_parameter(messageid, junk_s);
      if (strstr(junk_s, "Date:	") != NULL) get_parameter(date, junk_s);

      /* ...and without. */
      if (strstr(junk_s, "From: ") != NULL) get_who_from(from, junk_s);
      if (strstr(junk_s, "Envelope-to: ") != NULL) get_name_parameter(envto, junk_s);
      if (strstr(junk_s, "To: ") != NULL && (junk_s[0] == 'T')) get_name_parameter(to, junk_s);
      if (strstr(junk_s, "Subject: ") != NULL) get_parameter(subject, junk_s);
      if (strstr(junk_s, "Message-ID: ") != NULL) get_parameter(messageid, junk_s);
      if (strstr(junk_s, "Message-Id: ") != NULL) get_parameter(messageid, junk_s);
      if (strstr(junk_s, "Date: ") != NULL) get_parameter(date, junk_s);
   } while (strlen(junk_s) != 0);

   /* Read next two lines of message... */
   read_line(line1, in);
   read_line(line2, in);
   File_printf(msgfile, "%s\n", line1);
   File_printf(msgfile, "%s\n", line2);

   /* Close up headers file */
   File_Close(hdrfile);
   strcpy(junk_s,"%SetType <FetchMail$Dir>.Data.headers &FFF");
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   /* Scan for end of message */
   do
   {
      endoffile = read_line(junk_s, in);
      if (strstr(junk_s, "#! rmail") == NULL) File_printf(msgfile, "%s\n", junk_s);
   } while (strstr(junk_s, "#! rmail") == NULL && !endoffile);

   /* Close up message file */
   File_Close(msgfile);
   strcpy(junk_s,"%SetType <FetchMail$Dir>.Data.messages &FFF");
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   nextmessagepsn = File_ReturnPos(in); /* Remember new message position */
   nextmessagepsn = nextmessagepsn - 9; /* Rewind past header */

   /* Have we seen this one before? */
   if (check_message_id(messageid) != 0)
   {
      /* If this message ID exists in our database, we have already dealt with
         this message - so let it through...
      */
      copy_message();
      message_current++;
      get_new_message();

      return;
   }

   /* What is the status of this message? */
   strcpy(junk_s, from);
   lowercase(junk_s);
   junk_v = 0;
   do
   {
      if (strstr(junk_s, actions[junk_v].email) != NULL) foundit = 1;
      junk_v++;
   } while (!foundit && (junk_v < actioncount));
   if (foundit) msg_status = actions[junk_v-1].action;

   /* Draw information into window */
   sprintf(junk_s, "New message (%d of %d)", message_current, message_total);
   Window_SetTitle(newmsg_window, junk_s);

   Icon_SetShade(newmsg_window, 18, 0);
   Icon_SetShade(newmsg_window, 19, 0);
   Icon_SetShade(newmsg_window, 20, 0);
   Icon_SetShade(newmsg_window, 21, 0);
   Icon_SetShade(newmsg_window, 22, 0);
   Icon_SetSelect(newmsg_window, 8, FALSE);
   Icon_SetSelect(newmsg_window, 9, FALSE);


   Icon_SetText(newmsg_window, 5, to);
   Icon_SetText(newmsg_window, 25, envto);
   Icon_SetText(newmsg_window, 6, from);
   Icon_SetText(newmsg_window, 7, subject);
   Icon_SetText(newmsg_window, 12, line1);
   Icon_SetText(newmsg_window, 13, line2);

   if ((subject[0] == 'R') && (subject[1] == 'e') && (subject[2] == ':')) /* "Re:" */
   {
      Icon_SetSelect(newmsg_window, 8, TRUE);
   }

   if ((subject[0] == 'A') && (subject[1] == 'c') && (subject[2] == 'k') &&
       (subject[3] == ':') && (subject[4] == ' '))  /* Looks for "Ack: " */
   {
      Icon_SetSelect(newmsg_window, 9, TRUE);
   }

   switch(msg_status)
   {
      case 'O' : Icon_SetText(newmsg_window, 14, "A new message has been received from a recognised sender.");
                 Icon_SetShade(newmsg_window, 19, 1);
                 Icon_SetShade(newmsg_window, 20, 1);
                 Icon_SetShade(newmsg_window, 21, 1);
                 break;

      case '!' : Icon_SetText(newmsg_window, 14, "A new message has been received from a recognised sender.");
                 Icon_SetShade(newmsg_window, 20, 1);
                 Icon_SetShade(newmsg_window, 21, 1);
                 break;

      case 'R' : reject_message();          /* Reject */
      	       	 message_current++;
              	 get_new_message();
              	 break;

      case 'B' : bounce_message();          /* Bounce */
                 /* Fall through */

      case 'K' : message_current++;         /* Kill */
              	 get_new_message();
              	 break;

      default  : Icon_SetText(newmsg_window, 14, "A new message has been received from an unknown sender.");
                 break;
   }

   return;
}



static void copy_message(void)
{
   /* Copy message across to email file (for Okay/Keep) */

   int  endoffile = 0;

   File_Seek(in, (thismessagepsn + 9)); /* Shift past '#! rmail<cr>' */
   File_Seek(out, 0);

   Hourglass_On();

   File_printf(out, "#! rmail\n");
   do
   {
      endoffile = read_line(junk_s, in);
      if (strstr(junk_s, "#! rmail") == NULL)
         File_printf(out, "%s\n", junk_s);
   } while (strstr(junk_s, "#! rmail") == NULL && !endoffile);

   Hourglass_Off();

   return;
}



static void bounce_message(void)
{
   /* Bounce this message... */
   char from[80];
   char to[80];
   char subject[80];
   int  endoffile = 0;

   Icon_GetText(newmsg_window, 5, junk_s);
   junk_s[79] = '\0';
   strcpy(to, junk_s);
   Icon_GetText(newmsg_window, 6, junk_s);
   junk_s[79] = '\0';
   strcpy(from, junk_s);
   Icon_GetText(newmsg_window, 7, junk_s);
   junk_s[79] = '\0';
   strcpy(subject, junk_s);

   create_message_handle(from, to, subject);

   drop_into_outgoing("<FetchMail$Dir>.Data.BounceHdr");

   File_printf(messageout, "Subject       : %s\n", subject);
   File_printf(messageout, "Originally to : %s\n\n", to);
   File_printf(messageout, "Message text follows...\n\n--8<--------\n");

   File_Seek(in, (thismessagepsn + 9)); /* Move to start of message */
   do
   {
      endoffile = read_line(junk_s, in);
      if ((strstr(junk_s, "-- ") == NULL) && (strstr(junk_s, "X-POP3-Mailbox") == NULL))
         File_printf(messageout, "%s\n", junk_s);
   } while (strstr(junk_s, "-- ") == NULL && !endoffile);

   File_printf(messageout, "--8<--------\n\n");

   add_signature(to);
   add_terminator();
   File_Close(messageout);

   return;
}



static void reject_message(void)
{
   char from[80];
   char envto[80];
   char domain[80];
   char name[80];
   char replyto[80];
   int  endoffile = 0;
   int  offset = 0;
   int  length = 0;
   char timevar[40];
   char timevar2[16];
   int  timeblock[16];


   /* The name is already parsed as an absolute email address, so we can do a nice
      simple sort...
   */

   Icon_GetText(newmsg_window, 25, junk_s);
   junk_s[79] = '\0';
   strcpy(envto, junk_s);
   Icon_GetText(newmsg_window, 6, junk_s);
   junk_s[79] = '\0';
   strcpy(from, junk_s);

   strcpy(name, envto);
   strcpy(replyto, from); /* Use 'From' as 'Reply-To' at this time */
   offset = strcspn(envto, "@");
   length = strlen(envto);

   name[offset] = '\0';
   offset++;
   junk_v = 0;

   for (; (offset < length); offset++)
   {
      domain[junk_v] = envto[offset];
      junk_v++;
   }
   domain[junk_v] = '\0';

   /* So now 'name[]' contains the user name (ie "rmurray") and 'domain[]' contains
      the domain name (ie "argonet.co.uk").
   */


   /* Get the next sequence number, and process accordingly. */
   messageout = File_Open("<Posty$Dir>.Transport.sequence", file_READ);
   read_line(junk_s, messageout);
   File_Close(messageout);
   junk_v = atoi(junk_s);
   messageout = File_Open("<Posty$Dir>.Transport.sequence", file_WRITE);
   File_printf(messageout, "%d", (junk_v + 2)); /* No "\n" */
   File_Close(messageout);

   /* Sequence number is the rightmost six digits, prefixed 'D'... */
   junk_s[0] = '0';
   junk_s[1] = '0';
   junk_v = atoi(junk_s);

   /* Create envelope */
   sprintf(junk_s, "<PMailEnv$Dir>.D%06d", junk_v);
   messageout = File_Open(junk_s, file_WRITE);
   File_printf(messageout, "\nMailer-Daemon@argonet.co.uk\n");
   File_printf(messageout, "%s\n%s\n", from, replyto);
   File_Close(messageout);

   /* Create message header */
   sprintf(junk_s, "<PMailOut$Dir>.D%06d", junk_v);
   messageout = File_Open(junk_s, file_WRITE);
   File_Close(messageout);

   /* Set it to textual type (just in case!?). */
   sprintf(junk_s, "%%SetType <PMailOut$Dir>.D%06d &FFF", junk_v);
   r.r[0] = (int)junk_s;
   _kernel_swi(SWI_OS_CLI, &r, &r);

   /* Reopen */
   sprintf(junk_s, "<PMailOut$Dir>.D%06d", junk_v);
   messageout = File_Open(junk_s, file_WRITE);

   /* Sort out the time */
   timeblock[0] = 3;                                  /* Get the five-byte time */
   r.r[0] = 14;
   r.r[1] = (int)timeblock;
   _kernel_swi(SWI_OS_Word, &r, &r);

   strcpy(junk_s, "%W3 %Dy %M3 %YR (%24:%MI:%SE");    /* Convert to a string */
   r.r[0] = (int)timeblock;
   r.r[1] = (int)timevar;
   r.r[2] = 40;
   r.r[3] = (int)junk_s;
   _kernel_swi(SWI_OS_ConvertDateAndTime, &r, &r);

   strcpy(junk_s, "%TZ");                             /* Get the timezone */
   r.r[0] = (int)timeblock;
   r.r[1] = (int)timevar2;
   r.r[2] = 16;
   r.r[3] = (int)junk_s;
   _kernel_swi(SWI_OS_ConvertDateAndTime, &r, &r);

   switch (timevar2[0])
   {
      case 'G' : strcat(timevar, " +0000)");          /* If Greenwich Mean Time */
      	       	 break;
      case 'B' : strcat(timevar, " +0100)");          /* If British Summer Time */
      	       	 break;
      default  : strcat(timevar, " ");                /* Otherwise append timezone */
      	       	 strcat(timevar, timevar2);
      	       	 strcat(timevar, ")");
      	       	 break;
   }

   File_printf(messageout, "From: Mail Delivery System <Mailer-Daemon@argonet.co.uk>\n");
   File_printf(messageout, "To: %s\n", from);
   File_printf(messageout, "Subject: Mail delivery failed: returning message to sender\n");
   File_printf(messageout, "Date: %s\n", timevar);
   File_printf(messageout, "Status:\n");
   File_printf(messageout, "\n");
   File_printf(messageout, "This message was created automatically by mail delivery software.\n");
   File_printf(messageout, "\n");
   File_printf(messageout, "A message that you sent could not be delivered to all of its recipients.\n");
   File_printf(messageout, "The following address(es) failed:\n");
   File_printf(messageout, "\n");
   File_printf(messageout, "  %s@%s:\n", name, domain);
   File_printf(messageout, "    unknown local-part \"%s\" in domain \"%s\"\n", name, domain);
   File_printf(messageout, "\n");
   File_printf(messageout, "------ This is a copy of the message, including all the headers. ------\n");
   File_printf(messageout, "\n");

   /* And copy it across... */
   File_Seek(in, (thismessagepsn + 9));
   do
   {
      endoffile = read_line(junk_s, in);
      if ((strstr(junk_s, "#! rmail") == NULL) && (strstr(junk_s, "X-POP3-Mailbox") == NULL))
         File_printf(messageout, "%s\n", junk_s);
   } while (strstr(junk_s, "#! rmail") == NULL && !endoffile);

   File_Close(messageout);

   return;
}



static void log(char *message, ...)
{
   /* Trace file, accepts printf() style parameters */
   FILE *fp;
   int  items;
   char *out;
   va_list variables;

   if ((out = (char *)malloc(1024)) == NULL) /* Allocate a block of memory */
      return;

   va_start(variables, message); /* Begin format conversions */
   items = vsprintf(out, message, variables);

   /* Open up the trace file, move to end, write string. */
   remove("<FetchMail$Dir>.trace");
   fp = fopen("<FetchMail$Dir>.trace", "a+");
   if (!fp) return;

   fprintf(fp, "%s\n", out);
   fclose(fp);

   return;
}


