/*
 * xmail - X window system interface to the mail program
 *
 * Copyright 1990,1991,1992 by National Semiconductor Corporation
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of National Semiconductor Corporation not
 * be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * NATIONAL SEMICONDUCTOR CORPORATION MAKES NO REPRESENTATIONS ABOUT THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS"
 * WITHOUT EXPRESS OR IMPLIED WARRANTY.  NATIONAL SEMICONDUCTOR CORPORATION
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO
 * EVENT SHALL NATIONAL SEMICONDUCTOR CORPORATION BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * The following software modules were created and are Copyrighted by National
 * Semiconductor Corporation:
 *
 * 1. warp_handler: 
 * 2. extract_of: 
 * 3. endEdits: 
 * 4. editMail: 
 * 5. makeHeading: 
 * 6. makeButton: 
 * 7. sendMail: 
 *
 * Author:  Michael C. Wagnitz - National Semiconductor Corporation
 *
 */
#include "global.h"
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>

#ifdef vms
extern int	noshare errno;
#else
extern int	errno;
#endif

#if ! defined(SIGCHLD) && defined(SIGCLD)
#define	SIGCHLD		SIGCLD
#endif

#ifndef	DEFAULT_VISUAL
#ifndef linux
#define	DEFAULT_VISUAL	"/usr/ucb/vi"
#else
#define	DEFAULT_VISUAL	"/usr/bin/vi"
#endif
#endif

static int		editMail_pid;


/* ARGSUSED */
void
warp_handler(Widget w, XtPointer client_data, XEvent *event, Boolean *continueToDispatch)
{
 Widget Popup = XtNameToWidget(toplevel,"topBox.commandPanel.Send.popup");
 Widget    To = XtNameToWidget(Popup, "SubjCc.To");

 if (event->type == MapNotify) {
    XWarpPointer(XtDisplay(toplevel), None, XtWindow(To), 0,0,0,0, 15, 10);
    XtRemoveEventHandler(Popup, StructureNotifyMask, False, warp_handler, NULL);
   }
}


/*
** @(#)extract_of - strip off header information from a buffer and return
**                  the extracted information and the incremented pointer
*/
char *
extract_of(char **p)
{
 int	n;
 static char	tmp[BUFSIZ];


 for (n = 0; *(*p); (*p)++) {
     if (*(*p) == '\n' && *((*p) + 1) != ' ' && *((*p) + 1) != '\t') break;
     if (*(*p) == '\n') {
        if (tmp[n - 1] != ' ' && tmp[n - 1] != '\t') tmp[n++] = ' ';
        for ((*p)++; *(*p) && (*(*p) == ' ' || *(*p) == '\t');) (*p)++;
       }
     if (*(*p) != '\n') tmp[n++] = *(*p);
    }
 tmp[n] = '\0';
 return((char *)tmp);
}


/*
** @(#)endEdits() - catch the signal when the edit child finishes
**                  (The idea for this was ``borrowed'' from xrn.)
**                  If editheaders is set, extract selected header
**                  information from the message text file and
**                  install it in the appropriate header windows.
**                  Then, popup the completion window with header
**                  edit windows and completion control buttons.
*/
#if defined(sun) || defined(SVR4)
void
#else
int
#endif
endEdits(int signum)
{
 int		fd, status;
 String		p, q;
 char		buf[BUFSIZ];
 Widget		sb, Popup;


 if (signum != SIGCHLD)
#if defined(sun) || defined(SVR4)
    return ;
#else 
    return 1;
#endif

 if (editMail_pid != wait(&status))
#if defined(sun) || defined(SVR4)
    return ;				/* the mail child could die too */
#else
    return 1;
#endif

 (void) signal(SIGCHLD, SIG_DFL);	/* turn off the endEdits hook */

 sb = XtNameToWidget(toplevel, "topBox.commandPanel.Send");

 if (p = GetMailEnv("editheaders")) {
    XtFree((String) p);
    if ((fd = open(tmpName, O_RDONLY)) >= 0) {
       while (1) {
          bzero(Command, BUFSIZ);
          status = read(fd, Command, BUFSIZ);
          for (p = Command; *p; p++) {
              if (*p == '\n' && *(p - 1) == '\n') {
                 status = 0;
                 break;
                }
              if (strncmp(p, "To: ", 4) == 0) {
                 p += 4;
                 writeTo(XtNameToWidget(sb, "*To"), extract_of(&p), REPLACE);
                } else 
              if (strncmp(p, "Subject: ", 9) == 0) {
                 p += 9;
                 writeTo(XtNameToWidget(sb,"*Subject"),extract_of(&p),REPLACE);
                } else 
              if (strncmp(p, "In-Reply-To: ", 13) == 0) {
                 p += 13;
                 (void) strcpy(buf, extract_of(&p));
                 if (! (q = strchr(buf, ':')))
                    (void) sprintf(InReply, "In-Reply-To: %s", buf);
                 else {
                    q -= 5;
                    if (strncmp(q, "dated: ", 7) != 0)
                       (void) sprintf(InReply, "In-Reply-To: %s", buf);
                    else {
                       *(q - 1) = '\0';
                       (void) sprintf(InReply, "In-Reply-To: %s\n      ", buf);
                       (void) strcat(InReply, q);
                      }
                   }
                } else 
              if (strncmp(p, "Forwarding: ", 12) == 0) {
                 p += 12;
                 (void) strcpy(buf, extract_of(&p));
                 if (! (q = strchr(buf, ':')))
                    (void) sprintf(InReply, "Forwarding: %s", buf);
                 else {
                    q -= 5;
                    if (strncmp(q, "dated: ", 7) != 0)
                       (void) sprintf(InReply, "Forwarding: %s", buf);
                    else {
                       *(q - 1) = '\0';
                       (void) sprintf(InReply, "Forwarding: %s\n     ", buf);
                       (void) strcat(InReply, q);
                      }
                   }
                } else 
              if (strncmp(p, "Cc: ", 4) == 0) {
                 p += 4;
                 writeTo(XtNameToWidget(sb, "*Cc"), extract_of(&p), REPLACE);
                } else 
              if (strncmp(p, "Bcc: ", 5) == 0) {
                 p += 5;
                 writeTo(XtNameToWidget(sb, "*Bcc"), extract_of(&p), REPLACE);
                } else {
                 for (;*p && *p != '\n';) p++;
                }
             }
          if (status < BUFSIZ) break;
         }
       (void) close(fd);
      }
   }

 Popup = XtNameToWidget(sb, ".popup");

 SetXY(Popup, XtNameToWidget(toplevel, "topBox.commandPanel"), 0, 0);

 XtPopup(Popup, XtGrabNone);

 XSync(XtDisplay(toplevel), False);

 XtAddEventHandler(Popup, StructureNotifyMask, False, warp_handler, NULL);

#if defined(sun) || defined(SVR4)
 return ;
#else
 return 1;
#endif
}


/*
** @(#)editMail() - edit a mail message using the preferred editor
**
** Support is now provided for declaring the editor command as an xmail
** resource, ala xrn.  If the resource ``xmail.editorCommand'' is defined,
** it must contain an `sprintf'-able format string that provides for the
** inclusion of both a display name and the name of the file to be edited.
** If the resource declaration is not included, or does not contain the two
** required percent-s (%s) formatting strings, xmail will use the VISUAL
** resource.  If VISUAL is used, try to accommodate those editors (emacs,
** xedit...) which start their own window in X11.  We know for a fact that
** vi and ed variants do not invoke windows.  We assume any other editor
** specification does.
*/
void
editMail(void)
{
 String		edit, cp;
 char		cmd[BUFSIZ];
 Display	*ad;


 bzero(cmd, BUFSIZ);

 ad  = XtDisplay(XtNameToWidget(toplevel, "topBox.statusWindow"));
/*
** If editorCommand resource exists, use it (format validated during initialize)
*/
 if (XMail.editorCommand)
    (void) sprintf(cmd, XMail.editorCommand, DisplayString(ad), tmpName);
 else {
    /*
    ** Otherwise, default to the VISUAL method of starting things
    */
    if (! (edit = GetMailEnv("VISUAL")))
           edit = XtNewString(DEFAULT_VISUAL);

    if ((cp = strrchr(edit, '/')) == NULL) cp = edit;
    else cp++;

    if (strcmp(cp, "ed") == 0 ||
        strcmp(cp,"red") == 0 ||
        strcmp(cp, "ex") == 0 ||
        strcmp(cp, "vi") == 0) {
       (void) sprintf(cmd, "exec xterm -display %s -name XMail -title 'Message entry' -e %s %s",
                     DisplayString(ad), edit, tmpName);
      } else (void) sprintf(cmd, "exec %s -display %s %s", edit, DisplayString(ad), tmpName);
    XtFree((String) edit);
   }

 editMail_pid = fork();

 switch (editMail_pid) {
    case -1:			/* fork failed ... */
         if (errno == ENOMEM)
            Bell("Not enough core for edits\n");
         else
            Bell("No more processes - no edits\n");
         break;

    case 0:			/* child starts the message entry session */
         (void) close(ConnectionNumber(XtDisplay(toplevel)));
         (void) execl("/bin/sh", "sh", "-c", cmd, 0);
         perror("editMail: Failed to start the text editor");
         (void) _exit(127);
         break;

    default:			/* parent waits for editing to conclude */
         (void) signal(SIGCHLD, endEdits);
         break;
   }
} /* editMail */


/*
** @(#)readMail() - callback invoked every time input is pending on mail fd
**
** Calls QueryMail() to read all available data from mail file descriptor,
** and passes output to parse() for analysis and appropriate action.
*/
/* ARGSUSED */
void
readMail(XtPointer client_data, int *source, XtInputId *id)
                      	/* unused */
    	          	/* unused */
              		/* unused */
{
 SetCursor(WATCH);
 parse(QueryMail(""));
} /* readMail */


/*
** @(#)makeHeading() - Create the specified message header windows
*/
void
makeHeading(Widget parent, String label_string, Widget *left, Widget *above, String info, String help_text)
{
 Arg	args[11];
 char	resource_name[10];
 Widget	input_window;


 XtSetArg(args[0], XtNfromVert, *left);
 XtSetArg(args[1], XtNfromHoriz, NULL);
 XtSetArg(args[2], XtNlabel, label_string);
 XtSetArg(args[3], XtNborderWidth, 0);
 XtSetArg(args[4], XtNfont, XMail.buttonFont);
 XtSetArg(args[5], XtNheight, XMail.buttonHeight + XMail.borderWidth + 7);
 XtSetArg(args[6], XtNwidth, XMail.buttonWidth);
 XtSetArg(args[7], XtNjustify, XtJustifyLeft);
 XtSetArg(args[8], XtNinternalHeight, 0);
 XtSetArg(args[9], XtNinternalWidth, 1);
 *left = XtCreateManagedWidget("SubjCc", labelWidgetClass, parent, args, 10);

 bzero(resource_name, 9);
 (void) strncpy(resource_name, label_string, strlen(label_string) - 1);
 input_window = CreateInputWindow(parent, resource_name);

 XtSetArg(args[0], XtNfromVert, *above);
 XtSetArg(args[1], XtNfromHoriz, *left);
 XtSetValues(input_window, args, 2);

 AddInfoHandler(input_window, info);

 AddHelpText(input_window, help_text);

 *above = input_window;
} /* makeHeading */


/*
** @(#)makeButton() - Create the specified command button with callback
*/
void
makeButton(Widget parent, String label, XtCallbackProc CBProc, String CBData, String info, String help_text)
{
 Arg		args[3];
 Widget		button;
 int		width = ((((int)XMail.shellWidth-2)/6) - (int)XMail.borderWidth*2) - 4;


 XtSetArg(args[0], XtNfont, XMail.buttonFont);
 XtSetArg(args[1], XtNheight, XMail.buttonHeight);
 XtSetArg(args[2], XtNwidth, width);

 button = XtCreateManagedWidget(label, commandWidgetClass, parent, args, 3);

 XtAddCallback(button, XtNcallback, CBProc, CBData);

 AddInfoHandler(button, info);

 AddHelpText(button, help_text);
} /* makeButton */


/*
** @(#)sendMail() - Create the send window popup for message headers
*/
void
sendMail(Widget parent)
{
 Arg		args[11];
 Widget		Popup, Layout, Box, left, above;


 Popup = XtNameToWidget(parent, "popup");

 if (! Popup) {
    XtSetArg(args[0], XtNinput, True);
    XtSetArg(args[1], XtNwidth, XMail.shellWidth - 2);
    XtSetArg(args[2], XtNheight,
           XMail.borderWidth*3 + XMail.buttonHeight*5 + 44);
    Popup = XtCreatePopupShell("popup",transientShellWidgetClass,parent,args,3);

    XtSetArg(args[0], XtNdefaultDistance, 2);
    Layout = XtCreateManagedWidget("SubjCc", formWidgetClass, Popup, args, 1);

    left = above = NULL;

    makeHeading(Layout,      "To:",&left,&above,SendMail_Info[0],To_Help);

    makeHeading(Layout, "Subject:",&left,&above,SendMail_Info[1],Subject_Help);

    makeHeading(Layout,      "Cc:",&left,&above,SendMail_Info[2],Cc_Help);

    makeHeading(Layout,     "Bcc:",&left,&above,SendMail_Info[3],Bcc_Help);

    XtRegisterGrabAction(SetAliases, True, ButtonPressMask | ButtonReleaseMask,
                         GrabModeAsync, GrabModeAsync);

    XtSetArg(args[0], XtNfont, XMail.buttonFont);
    XtSetArg(args[1], XtNheight, XMail.buttonHeight + XMail.borderWidth*2 + 4);
    XtSetArg(args[2], XtNwidth, XMail.shellWidth - 2);
    XtSetArg(args[3], XtNfromVert, left);
    XtSetArg(args[4], XtNfromHoriz, NULL);
    XtSetArg(args[5], XtNborderWidth, 0);
    XtSetArg(args[6], XtNresize, False);
    XtSetArg(args[7], XtNmin, args[1].value);
    XtSetArg(args[8], XtNmax, args[1].value);
    XtSetArg(args[9], XtNhSpace, 2);
    XtSetArg(args[10],XtNvSpace, 2);
    Box = XtCreateManagedWidget("Box", boxWidgetClass, Layout, args, 11);

    makeButton(Box, "Autograph", (XtCallbackProc) Autograph, "A",
                     Autograph_Info[0], Sign_Help);

    makeButton(Box, "autograph", (XtCallbackProc) Autograph, "a",
                     Autograph_Info[1], sign_Help);

    makeButton(Box, "ReEdit", (XtCallbackProc) ReEdit, "ReEdit",
                     Deliver_Info[1], ReEdit_Help);

    makeButton(Box, "Cancel", (XtCallbackProc) Done, "cancel",
                     Deliver_Info[2], Cancel_Help);

    makeButton(Box, "Abort", (XtCallbackProc) Done, "Cancel",
                     Deliver_Info[3], Abort_Help);

    makeButton(Box, "Deliver", (XtCallbackProc) Done, "Deliver",
                     Deliver_Info[0], Deliver_Help);
   }
} /* sendMail */


/*
** @(#)writeMail() - Write command s to the mail process.
*/
void
writeMail(char *s)
{
 (void) write(mail_fd, s, strlen(s));
} /* writeMail */
