/*
 *	MAILCHK.C
 *
 *	DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved
 *
 *	Check the mailbox and reports if new mail
 *	has arrived.
 *
 *	Written by S. Laroche.
 *	April 19, 1990
 *
 *	Usage:	mailchk [-N(host) -s(flag) -w(flag) -t(time) -q
 *		-h:  Help message
 *		-s:  flag=0: Don't talk; otherwise talk
 *		-w:  flag=0: No window; other open a window
 *		-t:  (time) is the number of seconds between checks.
 *		     Default:  40 seconds
 *		-q:  Remove mailchk
 *		-h:  help message
 *
 *	Arp.library is needed... (Version 39.1)
 */

#include <stdio.h>
#include <local/typedefs.h>
#include <libraries/arpbase.h>
#include "/dnet/channel.h"
#include "/server/servers.h"
#include <local/deemu.h>
#include "mailmenu.h"          /* menu definitions */

/* Icon images */
#define YOUHAVEMAIL 0
#define NEWMAIL     1
#define NOMAIL	    2

/* Server commands */
#define GETNEWMAIL  1
#define GETMAILMSG  2
#define DELMAILMSG  3
#define INITSERVER  4
#define EDITMESSAGE 5

/* For use with Matt Dillon's config utility */
short Deemu[] = {
    DMSTRT, 0, 0,
    DMNW  , 0, 4,  0, 10,
/*    DMNW  , 0, 10, 2, 2, -80, 40, 0xFFFF, */
    DMEND , 0, 0
};

#define DMICONNWOFF  4
/* #define DMNWOFF     14 */

/* Buffer lengths */
#define MAILLENGTH 256	  /* Max. number of characters in 1 msg header */
#define BUFLENGTH  512	  /* Length of buffer used for communication */

/* Intuition shortcuts */
#define MENUSTRIP(n) (SHIFTMENU(n)|SHIFTITEM(NOITEM))
#define BUSYPOINTER(n) (SetPointer(n, BusyPointer, 22, 16, 0, 0))
#define IDCMPFLAGS NEWSIZE|MENUPICK|CLOSEWINDOW|MOUSEBUTTONS

/* Storage defined in mailchk_chip.c */
extern USHORT BusyPointer[];	       /* Busy pointer image	  */
extern struct Image MbImage;	       /* Mailbox icon		  */
extern USHORT MbBitMap[3][48*36];      /* Images for mailbox icon */

/* Globals */
ubyte Title[128];	/* Window title */
ubyte MailVersion[80];	/* Version number, formatted for Title */

/* Windows and gadgets */
NW Nw = {
    0, 20, 640, 100, -1, -1,
    NULL,
    WINSTD|NOCAREREFRESH,
    NULL, NULL, Title, NULL, NULL,
    32, 18, -1, -1, WBENCHSCREEN
};  /* Headers window */

static struct Gadget gadget =		/* Used to display the mailbox */
{ NULL,
  0, 0, 0, 0,
  GADGHNONE|GRELWIDTH|GRELHEIGHT|GADGIMAGE,
  GADGIMMEDIATE|RELVERIFY,
  WDRAGGING,
  (APTR) &MbImage,
  NULL,
  NULL,
  NULL,
  NULL,
  0, 0
};  /* Mailbox icon */

static NW IconNW =	  /* The mailbox window */
{ 0, 11,
  48, 36,
  -1, -1,
  GADGETDOWN|MENUPICK,
  BORDERLESS|SMART_REFRESH|NOCAREREFRESH,
  &gadget,
  NULL,
  NULL,
  NULL,
  NULL,
  0, 0, 0, 0,
  WBENCHSCREEN
}; /* Window for icon */

/* linked lists of headers */
struct MailMsg {
  char msg[MAILLENGTH];
  struct MailMsg *next;
};

struct MailMsg *MailP=NULL;  /* 1st pointer of linked list	  */
USHORT NumMsg = 0;	     /* Number of messages in linked list */

/* Message struct to communicate between two running mailchk */
struct mailmsg {
  struct Message ml_msg;
  unsigned long secs;
  BYTE quit, speak, win;
};


WIN *Win = NULL, *MbWin = NULL;
RP  *Rp;

char DefaultSpeaker[] = "SPEAK:";  /* Used when env. var. SPEAKER isn't set */

long IntuitionBase;
long GfxBase;
long ArpBase;

struct FileRequester *FileReq = NULL;
PORT *MailPort = NULL;

BYTE SpeakPlease = 2, WindowPlease = 2;

main(ac,av)

char *av[];

{
    long chan = NULL;
    unsigned long numsecs = 0L;
    BYTE firstrun = 1, quit = 0;
    char *host = NULL;
    int i;

    ArpBase = (long)OpenLibrary(ArpName,39L);
    if (ArpBase == NULL) exit(1);

    if (!param(ac,av,&numsecs,&quit,&host)) {
       CloseLibrary(ArpBase);
       exit(0);
    }
    {
      char buf[64];
      PORT *pr;

      sprintf(MailVersion, "MailChkV%s%s ", VERSION, MAILCHK_VERSION);
      strncpy(buf,MailVersion,7);
      buf[7] = '\0';
      strcat(MailVersion, " - April 25 1990");
      Enable_Abort = 0;
      if (pr = FindPort(buf)) {
	  PORT *mailrp;
	  struct mailmsg *mailmsg;

	  if (!(mailrp = CreatePort(NULL,0))) goto fail;
	  if (!(mailmsg = (struct mailmsg *) AllocMem(sizeof(struct mailmsg),
						MEMF_PUBLIC))) {
		DeletePort(mailrp);
		goto fail;
	  }
	  mailmsg->ml_msg.mn_Node.ln_Type = NT_MESSAGE;
	  mailmsg->ml_msg.mn_Length = sizeof(struct mailmsg);
	  mailmsg->ml_msg.mn_ReplyPort = mailrp;
	  mailmsg->secs = numsecs;
	  mailmsg->quit = quit;
	  mailmsg->speak = SpeakPlease;
	  mailmsg->win = WindowPlease;
	  PutMsg(pr,mailmsg);
	  Wait(1 << mailrp->mp_SigBit | SIGBREAKF_CTRL_C);
	  DeletePort(mailrp);
	  FreeMem(mailmsg,sizeof(struct mailmsg));
	  if (quit) Printf("Mailchk, removed\n");
	  else Printf("Mailchk, changed parameters\n");
	  goto fail;
      }
      else if (!(MailPort = CreatePort(buf,0))) goto fail;
    }
    if (numsecs == 0) numsecs = 40L;
    if (SpeakPlease == 2) SpeakPlease = 1;
    if (WindowPlease == 2) WindowPlease = 1;
    IntuitionBase = (long)OpenLibrary("intuition.library", 0);
    GfxBase = (long)OpenLibrary("graphics.library", 0);
    chan = DOpen(host, PORT_MAILCHK, 0, 0);
    if (chan == NULL) {
	Puts("no connect");
	goto fail;
    }

    if ((FileReq = ArpAllocFreq()) == NULL) goto fail;
    FileReq->fr_Hail = "Save message to which file?";
    FileReq->fr_Dir = "MAIL:";
/*    InitDeemuNW(Deemu+DMNWOFF, &Nw); */
    InitDeemuNW(Deemu+DMICONNWOFF, &IconNW);
    if ((MbWin = OpenWindow(&IconNW)) == NULL) goto fail;
    refreshmenu();
    SetMenuStrip(MbWin,Menu);
    OffMenu(MbWin,MENUSTRIP(1));
    if (initmailserver(chan,numsecs,firstrun))
	 checkmail(chan,numsecs);

fail:
    if (Win)
	CloseWinSafely(Win);
    if (MbWin)
	CloseWindow(MbWin);
    if (MailPort)
	DeletePort(MailPort);
    if (chan)
	DClose(chan);
    if (IntuitionBase)
	CloseLibrary(IntuitionBase);
    if (GfxBase)
	CloseLibrary(GfxBase);
    if (ArpBase)
	CloseLibrary(ArpBase);
}

int param(ac,av,numsecs,quit,host)

char *av[];
BYTE *quit;
unsigned long *numsecs;
ULONG *host;

{
      register short i;
      for (i = 1; i < ac; ++i) {
	if (strncmp(av[i], "-N", 2) == 0) {
	    *host = (ULONG) av[i]+2;
	    continue;
	}
	if (strncmp(av[i],"-d",2) == 0) {
	    continue;
	}
	if (strncmp(av[i],"-n",2) == 0) {
	    continue;
	}
	if (strncmp(av[i],"-t",2) == 0) {
	    *numsecs = atoi(av[i]+2);
	    continue;
	}
	if (strncmp(av[i],"-q",2) == 0) {
	    *quit = 1;
	    break;
	}
	if (strncmp(av[i],"-s",2) == 0) {
	    SpeakPlease = (BYTE) atoi(av[i]+2);
	    continue;
	}
	if (strncmp(av[i],"-w",2) == 0) {
	    WindowPlease = (BYTE) atoi(av[i]+2);
	    continue;
	}
	if (strncmp(av[i],"-h",2) != 0) {
	    Printf("Illegal switch:  %s\n",av[i]);
	}
	Printf("DNET    - (C) Matthew Dillon 1988\n");
	Printf("%s\n",MailVersion);
	Printf("\nUsage:  run mailchk [-N(host) -t(time) -s(flag) -w(flag) q|h]\n");
	Printf("        -t(time) Interval between checks in seconds [DEFAULT = 40 seconds]\n");
	Printf("        -N(network id) Dnet network number\n");
	Printf("        -s(flag) 0 = do not speak, 1 = speak [DEFAULT]\n");
	Printf("        -w(flag) 0 = no window, 1 = window [DEFAULT]\n");
	Printf("        -q Remove the MailChk client, if it is running\n");
	Printf("        -h This message...\n");
	return(0);
      }
      return(1);
}

initmailserver(chan,numsecs,firstrun)

long chan, numsecs;
char firstrun;

{
  char init = INITSERVER;

    if ((DWrite(chan,&init,1) == 1) && (DWrite(chan,&numsecs,4) == 4)
	 && (DWrite(chan,&firstrun,1) == 1)) return(1);
    return(0);

}

checkmail(chan,numsecs)

long chan, numsecs;

{
    long imask, dmask, omask, mask;
    char notdone = 1, nowindow = 0;
    ULONG prsec = 0, prmic = 0, prsec2 = 0, prmic2 = 0;
    BYTE pos = 0, oldpos = 0;

    dmask   = 1 << ((PORT *) chan)->mp_SigBit;
    omask   = 1 << MailPort->mp_SigBit;
    imask   = 1 << MbWin->UserPort->mp_SigBit;
    while (notdone) {
	if (Win) {
	    OnMenu(Win,MENUSTRIP(0));
	    OnMenu(Win,MENUSTRIP(1));
	}
	OnMenu(MbWin,MENUSTRIP(0));
	mask = Wait(imask|dmask|omask|SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_E);
	OffMenu(MbWin,MENUSTRIP(0));
	if (Win) {
	    OffMenu(Win,MENUSTRIP(0));
	    OffMenu(Win,MENUSTRIP(1));
	}
	if (mask & SIGBREAKF_CTRL_C) {
	    notdone = 0; break; }
	if (mask & omask) {
	    struct mailmsg *msg;
	    BYTE quit = 0;

	    while (msg = (struct mailmsg *) GetMsg(MailPort)) {
	      if (msg->secs > 0) numsecs = msg->secs;
	      quit |= msg->quit;
	      if (msg->speak < 2) SpeakPlease = msg->speak;
	      if (msg->win < 2) WindowPlease = msg->win;
	      if (msg->secs > 0)
		  initmailserver(chan,numsecs,2);
	      ReplyMsg(msg);
	    }
	    if (quit) { notdone = 0; break; }
	}
	if (mask & SIGBREAKF_CTRL_E) {
	    initmailserver(chan,numsecs,2);
	    continue;
	}
	if (mask & imask) {
	    IMESS *im;
	    ULONG class, code;

	    while (im = (IMESS *) GetMsg(MbWin->UserPort)) {
		class = im->Class;
		switch(class) {
		case GADGETDOWN:
		    if (im->IDCMPWindow != MbWin || NumMsg == 0) {
			ReplyMsg(im);
			break;
		    }
		    if (DoubleClick(prsec2,prmic2,im->Seconds,im->Micros)) {
			ReplyMsg(im);
			if (Win) {
			    WindowToFront(Win);
			    break;
			}
			else if (WindowPlease) {
				 Win = OpenWindow(&Nw);
				 if (Win != NULL) {
				     Rp = Win->RPort;
				     Win->UserPort = MbWin->UserPort;
				     ModifyIDCMP(Win,IDCMPFLAGS);
				     refreshmenu();
				     SetMenuStrip(Win,Menu);
				     OffMenu(Win,MENUSTRIP(0));
				     OffMenu(Win,MENUSTRIP(1));
				 }
			     }
			newimage(YOUHAVEMAIL);
			if (NumMsg == 65535) getnewmail(chan);
			else {
			    strcpy(Title,MailVersion);
			    dispnewmail(0,0,1);
			}
		    }
		    else {
			  prsec2 = im->Seconds;
			  prmic2 = im->Micros;
			  ReplyMsg(im);
		    }
		    break;
		case NEWSIZE:
		    dispnewmail(pos,0,1);
		    break;
		case MOUSEBUTTONS:
		    if (im->Code == SELECTUP) {
		      if (DoubleClick(prsec,prmic,im->Seconds,im->Micros)
			  && pos == oldpos) {
			if (pos > 0) {
			    getmailmsg(chan,pos,0);
			}
		      }
		      else {
			  prsec = im->Seconds;
			  prmic = im->Micros;
			    }
		      if (pos == 0) dispnewmail(0,oldpos,0);
		      else oldpos = pos;
		    }
		    else if (im->Code == SELECTDOWN) {
			     register short i=1;
			     short temp;
			     struct MailMsg *p = MailP;

			     temp = (im->MouseY - Win->BorderTop) / Rp->TxHeight + 1;
			     while ( i != temp && p != NULL) {
				i++;
				p = p->next;
			     }
			     if (p == NULL) { oldpos = pos; pos = 0;}
			     else { pos = temp;
				    dispnewmail(pos,oldpos,0);
				  }
			 }
		    break;
		case CLOSEWINDOW:
		    nowindow = 1;
		    break;
		case MENUPICK:
		    code = im->Code;
		    ReplyMsg(im);
		    switch((uword)((MENUNUM(code)<<8)|ITEMNUM(code))) {
		    case 0x0100:    /*	View	*/
			getmailmsg(chan,pos,0);
			break;
		    case 0x0101:    /*	Print	*/
			getmailmsg(chan,pos,1);
			break;
		    case 0x0102:    /*	Delete	*/
			delmailmsg(chan,pos);
			break;
		    case 0x0103:    /*	Save	*/
			getmailmsg(chan,pos,2);
			break;
		    case 0x0104:    /*	Speak	*/
			getmailmsg(chan,pos,3);
			break;
		    case 0x0105:    /*	Reply	*/
			editmessage(chan,pos);
			break;
		    case 0x0000:    /*	Talk	*/
			SpeakPlease = 1 - SpeakPlease;
			break;
		    case 0x0001:    /*	Window	*/
			WindowPlease = 1 - WindowPlease;
			break;
		    case 0x0002:    /*	Remove	*/
			notdone = 0;
			break;
		    }
		}
		if (class != MENUPICK && class != GADGETDOWN) ReplyMsg(im);
	    }
	}
	if (mask & dmask) {
	    char len = 0;
	    short temp = 0;

	    if (DNRead(chan, &len, 1) != 0) {
		if (len < sizeof(Title) && DRead(chan, Title, len) == len) {
		    Title[len-1] = 0;
		    if (strncmp(Title,"No mail",7) == 0) {
			nowindow = 1;
			freemail(MailP);
			newimage(NOMAIL);
		    }
		    else { if (strncmp(Title,"New mail",8) == 0) {
			       DisplayBeep(NULL);
			       newimage(NEWMAIL);
			       Delay(50);
			       DisplayBeep(NULL);
			   }
			   else newimage(YOUHAVEMAIL);
			   if (SpeakPlease) {
			       char *Speaker;
			       struct FileHandle *fh;

			       Speaker = GetDEnv("SPEAKER");
			       if (Speaker == NULL) Speaker = DefaultSpeaker;
			       fh = Open(Speaker,1006);
			       if (fh) {
				   if (strncmp(Title,"New",3) == 0)
				       Write(fh,"You have new mail.",18);
				   else Write(fh,"You have mail.",14);
				   Close(fh);
			       }
			   }
			   NumMsg = -1;
			   if (Win) {
			       WindowToFront(Win);
			       getnewmail(chan);
			       newimage(YOUHAVEMAIL);
			   }
			   if (!SpeakPlease && !WindowPlease) {
			       Puts(Title);
			       getnewmail(chan);
			   }
		    }
		}
	    }
	    else if (DCheckEof(chan)) notdone = 0;
	}
	if (nowindow && Win) {
	    ClearMenuStrip(Win);
	    Nw.LeftEdge = Win->LeftEdge;
	    Nw.TopEdge = Win->TopEdge;
	    Nw.Width = Win->Width;
	    Nw.Height = Win->Height;
	    CloseWinSafely(Win);
	    Win = NULL;
	}
	nowindow = 0;
    }

    freemail(MailP);
    if (Win) {
	ClearMenuStrip(Win);
	CloseWinSafely(Win);
	Win = NULL;
    }
}

/*
 *  Utility routines.	************************************************
 */

refreshmenu()

/* Refresh the two toggle menus(Talk, Window) in the Project strip
 * according to the flags SpeakPlease and WindowPlease.
 */

{
   struct MenuItem *mn_ad;

   mn_ad = (struct MenuItem *) ItemAddress(Menu,SHIFTMENU(0)|SHIFTITEM(0));
   if (SpeakPlease) mn_ad->Flags |= CHECKED;
   else mn_ad->Flags &= ~(CHECKED);
   mn_ad = (struct MenuItem *) ItemAddress(Menu,SHIFTMENU(0)|SHIFTITEM(1));
   if (WindowPlease) mn_ad->Flags |= CHECKED;
   else mn_ad->Flags &= ~(CHECKED);
}

newimage(index)

/* Change the mailbox icon.
 * Values of index:
 * 0 -> "You have mail", 1-> "New mail", 2-> "No mail"
 */

{
  MbImage.ImageData = &MbBitMap[index][0];
  RefreshGList(&gadget,MbWin,NULL,1);
}


getnewmail(chan)

long chan;

/* Get all messages (headers only) from the host.
 * It will also display thoses headers using dispnewmail().
 * The magic number 1 is used to tell the server what we want.
 */

{
  unsigned char len = GETNEWMAIL;
  struct MailMsg *p;
  register USHORT count = 0;

  if (Win) {
      BUSYPOINTER(Win);
      SetWindowTitles(Win,MailVersion,-1);
  }
  freemail(MailP);
  if (DWrite(chan, &len, 1) == 1 && DRead(chan, &len, 1) == 1) {
      while (len > 0) {
	if (MailP) {
	    p->next = AllocMem(sizeof(*p),0);
	    p = p->next;
	}
	else {
	    MailP = AllocMem(sizeof(*p),0);
	    p = MailP;
	}
	if (p == NULL) break;
	else p->next = NULL;
	if (len < MAILLENGTH && (DRead(chan, p->msg, len) == len)) {
	    p->msg[len-1] = '\0';
	    DRead(chan,&len,1);
	    count++;
	}
	else break;

      }
  }
  NumMsg = count;
  if (Win) ClearPointer(Win);
  dispnewmail(0,0,1);
}

dispnewmail(ONmsgno,OFFmsgno,flag)

BYTE ONmsgno, OFFmsgno, flag;

/* Display mail headers in a window if one is opened or on stdout otherwise.
 * ONmsgno :  msg no to be output in reverse ( 0 = none)
 * OFFmsgno:  msg no to be output normally.
 * flag    :  1 -> It's a new window, 0 -> it's an old window
 */

{
  char th, tb, tw;
  short y, Wh, Ww, WOx, WOy;
  int len;
  struct MailMsg *p;
  BYTE i=1;

  p = MailP;
  if (Win != NULL) {
      th = Rp->TxHeight;
      tb = Rp->TxBaseline;
      tw = Rp->TxWidth;
      y = Win->BorderTop;
      Ww = Win->Width - Win->BorderRight - Win->BorderLeft;
      Wh = Win->Height- Win->BorderTop - Win->BorderBottom;
      WOx = Win->BorderLeft;
      WOy = Win->BorderTop;
      if (flag) {
	  char buf[25], buf2[9];

	  SetAPen(Rp, 0);
	  RectFill(Rp, WOx, WOy, Ww + WOx, Wh + WOy);
	  WindowToFront(Win);
	  strcpy(buf2,"message");
	  if (NumMsg > 1) strcat(buf2,"s");
	  sprintf(buf," (%d %s)",NumMsg,buf2);
	  if (Title[strlen(Title)-1] != ')') strcat(Title,buf);
	  SetWindowTitles(Win, Title, -1);
      }
  }
  while (p != NULL && ((Win == NULL) || ((y+tb) < Wh))) {
    short tl;

    if (! Win && flag) Puts(p->msg);
    else { if (flag || i == ONmsgno || i == OFFmsgno) {
	       if (ONmsgno == i) {
		   SetBPen(Rp,2);
		   SetAPen(Rp,0);
	       }
	       else {
		   SetAPen(Rp,2);
		   SetBPen(Rp,0);
	       }
	       len = strlen(p->msg);
	       tl = TextLength(Rp,p->msg,len);
	       if (tl > Ww) len = Ww / tw;
	       Move(Rp,Win->BorderLeft,y+tb);
	       Text(Rp,p->msg,len);
	       if (ONmsgno == i) SetAPen(Rp,2);
	       else if (OFFmsgno == i || flag) SetAPen(Rp,0);
	       RectFill(Rp,Rp->cp_x,y,Ww+WOx,y+th-1);
	   }
	   y += th;
	   i++;
    }
    p = p->next;
  }
}

delmailmsg(chan,msgno)

long chan;
BYTE msgno;

/* Send a command to the server asking it to delete message number msgno.
 */

{
  register BYTE i=1;
  struct MailMsg *p = MailP;
  char dummy[32];
  BYTE dl = DELMAILMSG, ok;
  unsigned long stchar = 0,nochars;

  BUSYPOINTER(Win);
  for (i=1; (i != msgno && p != NULL); i++) {
    getmailprm(p,&nochars,dummy);
    stchar += nochars;
    p = p->next;
  }
  if (p == NULL) return();
  getmailprm(p,&nochars,dummy);

  if ((DWrite(chan,&dl,1) == 1) &&
      (DWrite(chan,&stchar,4) == 4) &&
      (DWrite(chan,&nochars,4) == 4) &&
      (DRead(chan,&ok,1) == 1)) {
      if (ok) strcpy(Title,"Delete successful ");
      else strcpy(Title,"Delete error ");
      getnewmail(chan);
  }
  ClearPointer(Win);
}

int getmailmsg(chan,msgno,flag)

long chan;
BYTE msgno, flag;

/* Get message number msgno from the host and take the following action
 * according to the value of flag:
 *
 *    0 -> View the message on screen (using more or the env var PAGER)
 *    1 -> Print the message on PRT:
 *    2 -> Save the message in a file.
 *    3 -> Send the message to the SPEAK: device (without the headers)
 */

{
  struct MailMsg *p = MailP;
  BYTE i, ok=0, hd = GETMAILMSG, l, start = 0;
  unsigned long stchar=0L, nochars, len;
  struct FileHandle *fh;
  ubyte *buf;
  char pname[128], vname[100], title[132], *tmp;
  int rcode = 1;
  struct NewShell *NS;
  if (NS = AllocMem(sizeof(*NS)+4096,MEMF_CLEAR)) {
      NS->nsh_StackSize = 4000;
      NS->nsh_Control = BACKGROUND_SHELL;
  }
  else return(rcode);
  strcpy(vname,"DPIPE:Mail");
  switch (flag) {
    case 0:			  /* View */
      if ((tmp = GetDEnv("PAGER")) == NULL) {
	  strcpy(pname,"sys:utilities/more");
      }
      else { strcpy(pname,tmp);
	     free(tmp);
      }
      strcat(pname," ");
      strcat(pname,vname);
      strcpy(title,"Viewing ");
      start = 1;
      break;
    case 1:			 /* Print */
      strcpy(vname,"PRT:");
      strcpy(title,"Printing ");
      break;
    case 2:			 /* Save */
      if (!(FileRequest(FileReq))) {
	    return(rcode);}
      strcpy(vname,FileReq->fr_Dir);
      TackOn(vname,FileReq->fr_File);
      strcpy(title,"Saving ");
      strcat(title,vname);
      break;
    case 3:			 /* Speak */
      tmp = GetDEnv("SPEAKER");
      if (tmp == NULL) tmp = DefaultSpeaker;
      strcpy(vname,tmp);
      strcpy(title,"Speaking ");
      break;
  }
  buf = (ubyte *) (NS + sizeof(*NS));
  l = strlen(title);
  for (i=1; (i != msgno && p != NULL); i++) {
    getmailprm(p,&nochars,&len);
    stchar += nochars;
    p = p->next;
  }
  if (p == NULL) return(rcode);
  getmailprm(p,&nochars,&len);
  if (flag == 3) {stchar += len; nochars -= len; }
  if (flag == 0) {
      strcat(title," using ");
      strcat(title,pname);
  }
  BUSYPOINTER(Win);
  if (fh = Open(vname,1006)) {
      if (DWrite(chan, &hd, 1) == 1 && DWrite(chan, &stchar, 4) == 4
	  && DWrite(chan,&nochars,4) == 4) {
	  SetWindowTitles(Win, title, -1);
	  while (DRead(chan,&len,4) == 4 && len > 0) {
	    if (DRead(chan,buf,len) == len) {
		if (start) {
		    if (ASyncRun(pname,0L,NS) < 0) {
			ok = 1;
			DWrite(chan,&ok,1);
			rcode = 0;
			break;
		    }
		    start = 0;
		}
		if (Write(fh,buf,len) != len) {
		    ok = 1;
		    DWrite(chan,&ok,1);
		    rcode = 0;
		    break;
		}
		DWrite(chan,&ok,1);
	    }
	  }
      }
      Close(fh);
  }
  else {
	rcode = 0;
	Printf("\nCould not open %s\n",vname);
  }
  SetWindowTitles(Win,Title,-1);
  FreeMem(NS,sizeof(*NS)+4096);
  ClearPointer(Win);
  return(rcode);
}

int editmessage(chan,msgno)

long *chan;
UBYTE msgno;

/* Reply to message number msgno using ed or the env var EDITOR.
 * A temporary file in the T: directory is used.
 *
 * Still buggy for unknown reasons.
 */

{
  char *buf, fname[32];
  struct MailMsg *p = MailP;
  int ok = 0, test;
  struct FileInfoBlock *fileinfo;
  struct FileLock *lock;
  struct FileHandle *fh = NULL;
  LONG len;
  short count;
  BYTE hd = EDITMESSAGE, i;

  if (!(fileinfo = AllocMem(sizeof(struct FileInfoBlock)+BUFLENGTH,0)))
      return(ok);
  buf = (char *) (fileinfo + sizeof(struct FileInfoBlock));
  strcpy(fname,"T:MailChk");
  strcpy(Title,"Replying...");
  SetWindowTitles(Win,Title,-1);
  if (GetDEnv("EDITOR") != NULL) strcpy(buf,GetDEnv("EDITOR"));
  else strcpy(buf,"ed");
  BUSYPOINTER(Win);
  if (SyncRun(buf,fname,0,0) >= 0) {
      if (((lock = Lock(fname,ACCESS_READ)) != 0) &&
	Examine(lock,fileinfo)) {
	len = fileinfo->fib_Size;
	UnLock(lock);
	strcpy(Title,"Could not open tmp file");
	if (!(fh = Open(fname,1005))) goto fin;
	strcpy(Title,"Inconsistency error");
	for (i=1; (i != msgno && p != NULL); i++)
	     p = p->next;
	if (p == NULL) goto fin;
	sscanf(p->msg+1,"%d %s",&test,buf);
	if (test != msgno) goto fin;
	strcpy(Title,"Transmission error");
	if (DWrite(chan,&hd,1) != 1) goto fin;
	count = strlen(buf);
	if (DWrite(chan,&count,2) != 2) goto fin;
	if (DWrite(chan,buf,count) != count) goto fin;
	if (DWrite(chan,&len,4) != 4) goto fin;
	do {
	  count = Read(fh,buf,BUFLENGTH);
	  if (count > 0 && DWrite(chan,buf,count) != count) goto fin;
	} while (count == BUFLENGTH);
	strcpy(Title,"Remote error");
	if (DRead(chan,&ok,1) != 1) ok = 0;
      }
      else {
	  strcpy(Title,"Reply function cancelled");
	  goto fin2;
      }
  }
  else { sprintf(Title,"Editor %s not found",buf);
	 goto fin2;
  }
fin:
  if (fh) Close(fh);
  if (ok) {
      DeleteFile(fname);
      strcpy(Title,"Reply was suscessful");
  }
  else {
      DeleteFile("T:dead.letter");
      Rename(fname,"T:dead.letter");
      strcat(Title," - Letter is in T:dead.letter");
  }
fin2:
  SetWindowTitles(Win,Title,-1);
  ClearPointer(Win);
  FreeMem(fileinfo,sizeof(struct FileInfoBlock)+BUFLENGTH);
  return(ok);
}

getmailprm(p,nochars,len)

struct MailMsg *p;
unsigned long *nochars, *len;

/* Extract the no of chars (nochars) from a particular header (pointed to
 * by p) in the message and the no of chars without the header lines
 * (len).
 */

{
  char *str;

  if (str = rindex(p->msg,'/')) {
      sscanf(++str,"%ld %ld",nochars,len);
  }
  else *nochars = 0;
}

freemail(p)

struct MailMsg *p;

/* Free all the memory used by the linked list
 */

{
  while (p != NULL) {
    FreeMem(p,sizeof(*p));
    p = p->next;
  }
  MailP = NULL;
  NumMsg = 0;
}

CloseWinSafely(win)

WIN *win;

/* From RKM 1.3.
 * Close window win safely when it shares an IDCMP port with other
 * windows.
 */

{
  Forbid();
  StripIntuiMessages(win->UserPort,win);
  win->UserPort = NULL;
  ModifyIDCMP(win,0);
  Permit();
  CloseWindow(win);
}

StripIntuiMessages(mp,win)

PORT *mp;
WIN *win;

/* From RKM 1.3 */

{
  IMESS *msg, *succ;

  msg = (IMESS *) mp->mp_MsgList.lh_Head;
  while (succ = (IMESS *) msg->ExecMessage.mn_Node.ln_Succ) {
    if (msg->IDCMPWindow == win) {
	Remove(msg);
	ReplyMsg(msg);
    }
    msg = succ;
  }
}

