static char rcsid[] = "@(#)$Id: forms.c,v 1.29 2006/04/09 07:37:18 hurtta Exp $";

/*****************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.29 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 ******************************************************************************
 *  The Elm Mail System 
 *
 *			Copyright (c) 1988-1992 USENET Community Trust
 *			Copyright (c) 1986,1987 Dave Taylor
 ****************************************************************************/

/** This set of files supports the 'forms' options (AT&T Mail Forms) to
    the mail system.  The specs are drawn from a document from AT&T entitled
    "Standard for Exchanging Forms on AT&T Mail", version 1.9.

**/

/** Some notes on the format of a FORM;

	First off, in AT&T Mail parlance, this program only supports SIMPLE
	forms, currently.  This means that while each form must have three
 	sections;

		[options-section]
		***
		[form-image]
		***
		[rules-section]

	this program will ignore the first and third sections completely.  The
	program will assume that the user merely enteres the form-image section,
	and will append and prepend the triple asterisk sequences that *MUST*
	be part of the message.  The messages are also expected to have a 
	specific header - "Content-Type: mailform" - which will be added on all
	outbound mail and checked on inbound...
**/

#include "def_elm.h"
#include "s_elm.h"

DEBUG_VAR(Debug,__FILE__,"misc");

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif       

static void prompt_for_sized_entry P_((char *prompt, char *buffer,
				       int   field_size,
				       struct menu_context  *page));
static void prompt_for_entries P_((char *buffer,
				   FILE *fd,
				   int  entries,
				   struct menu_context  *page));

int check_form_file(filename)
     char *filename;
{
    /** This routine returns the number of fields in the specified file,
      or -1 if an error is encountered. **/

    FILE *form;
    char buffer[SLEN];
    int field_count = 0;

    int err = can_open(filename, "r");
    if (err) {
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorOpeningCheckFields,
			  "Error %s trying to open %s to check fields!"),
		  error_description(err), filename);
	return -1;
    }


    if ((form = fopen(filename, "r")) == NULL) {
	int err = errno;
	lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorOpeningCheckFields,
			  "Error %s trying to open %s to check fields!"),
		  error_description(err), filename);
	return -1;
    }
    
    while (mail_gets(buffer, SLEN, form)) {
	field_count += occurances_of(COLON, buffer);
    }
    
    fclose(form);
    
    return(field_count);
}

int format_form(filename)
     char *filename;
{
	/** This routine accepts a validated file that is the middle 
	    section of a form message and prepends and appends the appropriate 
	    instructions.  It's pretty simple. 
	    This returns the number of forms in the file, or -1 on errors
	**/
	
	FILE *form, *newform;
	char  newfname[SLEN], buffer[SLEN];
	int form_count = 0;
	int  len_buf, err;
	char *tmp;

	DPRINT(Debug,4, (&Debug,  "Formatting form file '%s'\n", filename));

	/** first off, let's open the files... **/

	err = can_open(filename, "r");
	if (err) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, 
			      ElmCantReadMessageToValidate,
			      "Can't read the message to validate the form!"));
	    return -1;
	}

	if ((form = fopen(filename, "r")) == NULL) {
	  err = errno;
	  lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantReadMessageToValidate,
			    "Can't read the message to validate the form!"));
	  DPRINT(Debug,1,(&Debug,
			  "** Error encountered opening file \"%s\" - %s (check_form) **\n",
			  filename, error_description(err)));
	  return(-1);
	}

	tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
	if (!tmp)
	    tmp = "/tmp/";

	elm_sfprintf(newfname, sizeof newfname,
		     FRM("%s%s%d"), tmp, temp_form_file, getpid());

	if ((newform = safeopen(newfname)) == NULL) {
	  err = errno;
	  lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCouldntOpenNewformOutput,
			    "Couldn't open newform file for form output!"));
	  DPRINT(Debug,1,(&Debug,
			  "** Error encountered opening file \"%s\" - %s (check_form) **\n",
			  newfname, error_description(err)));
	  fclose(form);
	  return(-1);
	}

	/** the required header... **/

	/* these are actually the defaults, but let's be sure, okay? */

	fprintf(newform, "WIDTH=78\nTYPE=SIMPLE\nOUTPUT=TEXT\n***\n");

	/** and let's have some fun transfering the stuff across... **/

	while (0 < (len_buf = 
		    mail_gets(buffer, SLEN, form))) {
	    fwrite(buffer, 1, len_buf, newform);
	    form_count += occurances_of(COLON, buffer);
	}

	fprintf(newform, "***\n");	/* that closing bit! */

	fclose(form);
	fclose(newform);

	if (form_count > 0) {
	  if (unlink(filename) != 0) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorUnlinkingFile,
			      "Error %s unlinking file %s."),
		      error_description(errno), 
		      filename);
	    return(-1);
	  }
	  if (link(newfname, filename)) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorLinkingFile,
			      "Error %s linking %s to %s."),
		      error_description(errno), newfname, filename);
	    return(-1);
	  }
	}

	if (unlink(newfname)) {
	  lib_error(CATGETS(elm_msg_cat, ElmSet, ElmErrorUnlinkingFile,
			    "Error %s unlinking file %s."),
		    error_description(errno), newfname);
	  return(-1);	
	}

	return(form_count);
}

void mail_filled_in_form(mail_index,address, subject, 
			 mailbox,aview, parent_page)
     int mail_index;
     struct addr_item *address;
     char *subject;
     struct MailboxView *mailbox;
     struct AliasView *aview;
     struct menu_context *parent_page;
{
    /** This is the interesting routine.  This one will read the
	message and prompt the user, line by line, for each of
	the fields...returns non-zero if it succeeds
    **/

	FILE  	     *fd;
	int lines = 0, count, len_buf, max_lines;
	char         buffer[SLEN];
	struct menu_context  *page = NULL;
	struct menu_context *cpage;
	struct header_rec *hdr = NULL;
	FILE *             mail_file = NULL;
	char *tmp;


	DPRINT(Debug,4,
	       (&Debug, 
		"replying to form with;\n\tsubject=%s\n",
		subject));

	
	if (!give_message_data(mailbox,mail_index,
			  &hdr,&mail_file,NULL,
			  NO_mime_parse))
	    return;

	/* now we can fly along and get to the message body... */

	max_lines = hdr->lines;
	while (0 < (len_buf = 
		    mail_gets(buffer, SLEN, mail_file))) {
	    if (len_buf == 1)	/* <return> only */
		break;
	    else if (lines >= max_lines) { 
		lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoFormInMessage,
				  "No form in this message!?"));
		return;
	    }

	  if (buffer[len_buf - 1] == '\n')
	    lines++;
	}

	if (len_buf == 0) {
	  lib_error(CATGETS(elm_msg_cat, ElmSet, ElmNoFormInMessage,
			    "No form in this message!?"));
	  return;
	}

	DPRINT(Debug,6,(&Debug,
			"- past header of form message -\n"));
	
	/* at this point we're at the beginning of the body of the message */

	/* now we can skip to the FORM-IMAGE section by reading through a 
	   line with a triple asterisk... */

	while (0 < (len_buf = 
		    mail_gets(buffer, SLEN, mail_file))) {
	  if (strcmp(buffer, "***\n") == 0)
	    break;	/* we GOT it!  It's a miracle! */	

	  if (buffer[len_buf - 1] == '\n')
	    lines++;

	  if (lines >= max_lines) {
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmBadForm,
			      "Badly constructed form.  Can't reply!"));
	    return;
	  }
	}

	if (len_buf == 0) {
	  lib_error(CATGETS(elm_msg_cat, ElmSet, ElmBadForm,
			    "Badly constructed form.  Can't reply!"));
	  return;
	}

	DPRINT(Debug,6,(&Debug,
			"- skipped the non-forms-image stuff -\n"));
	
	/* one last thing - let's open the tempfile for output... */
	tmp = give_dt_estr_as_str(&temp_dir_e,"tmpdir");
	if (!tmp)
	    tmp = "/tmp/";

	elm_sfprintf(buffer, sizeof buffer,
		     FRM("%s%s%d"), tmp, temp_form_file, getpid());

	DPRINT(Debug,2,(&Debug,
			"-- forms sending using file %s --\n", buffer));

	if ((fd = safeopen(buffer)) == NULL) {
	  lib_error(CATGETS(elm_msg_cat, ElmSet, ElmCantOpenAsOutputFile,
			    "Can't open \"%s\" as output file! (%s)."),
		    buffer, error_description(errno));
	  DPRINT(Debug,1,(&Debug,
			  "** Error %s encountered trying to open temp file %s;\n",
			  error_description(errno), buffer));
	  return;
	}

	/* NOW we're ready to read the form image in and start prompting... */

	/* FIXME: ? ? ? */
	page = new_menu_context();

	cpage = Raw(OFF);
	menu_ClearScreen(cpage);

	/* !!!   FIXME      this is quite bogus ... */

	while (0 < (len_buf = 
		    mail_gets(buffer, SLEN, mail_file))) {
	    DPRINT(Debug,9,(&Debug,
			    "- read %s", buffer));
	    if (strcmp(buffer, "***\n") == 0) /* end of form! */
		break;
	    
	    if (buffer[len_buf - 1] == '\n')
		lines++;
	 
	  if (lines > max_lines)
	    break; /* end of message */

	  switch ((count = occurances_of(COLON, buffer))) {
	    case 0 : fwrite(buffer, 1, len_buf, stdout);	/* output line */
		     fwrite(buffer, 1, len_buf, fd); 	
		     break;
            case 1 : if (buffer[0] == COLON) {
		elm_fprintf(stdout,
			    CATGETS(elm_msg_cat, ElmSet, ElmEnterAsManyLines,
				    "(Enter as many lines as needed, ending with a '.' by itself on a line)\n"));
                       while (fgets(buffer, SLEN, stdin) != NULL) {
		         no_ret(buffer);
	                 if (strcmp(buffer, ".") == 0)
	                   break;
	                 else 
			   fprintf(fd,"%s\n", buffer);
		       }
	             }
	             else 
			 prompt_for_entries(buffer, fd, count,page);
	             break;
	  default: prompt_for_entries(buffer, fd, count, page);
	  }
	}

	Raw(ON);
	fclose(fd);

	/** let's just mail this off now... **/

	mail_form(mail_index,address, subject, 
		  mailbox  /* for save_copy */,
		  aview, page);

	erase_menu_context(&page);

	menu_trigger_redraw(parent_page);

	return;
}


static void prompt_for_entries(buffer, fd, entries, page)
     char *buffer;
     FILE *fd;
     int  entries;
     struct menu_context  *page;
{
	/** deals with lines that have multiple colons on them.  It must first
	    figure out how many spaces to allocate for each field then prompts
	    the user, line by line, for the entries...
	**/

	char mybuffer[SLEN], prompt[SLEN], spaces[SLEN];
	register int  field_size, i, j, offset = 0, extra_tabs = 0;

	DPRINT(Debug,5,
	       (&Debug, 
		"prompt-for-multiple [%d] -entries \"%s\"\n", entries,
		buffer));

	strfcpy(prompt, catgets(elm_msg_cat, ElmSet, ElmFormNoPrompt,
		"No Prompt Available:"), sizeof prompt);

	while (entries--) {
	  j=0; 
	  i = chloc((char *) buffer + offset, COLON) + 1;
	  while (j < i - 1) {
	    prompt[j] = buffer[j+offset];
	    j++;
	  }
	  prompt[j] = '\0';

	  field_size = 0;

	  while (whitespace(buffer[i+offset])) {
	    if (buffer[i+offset] == TAB) {
	      field_size += 8 - (i % 8);
	      extra_tabs += (8 - (i % 8)) - 1;
	    }
	    else
	      field_size += 1;
	    i++;
	  }

	  offset += i;
	
	  if (field_size == 0) 	/* probably last prompt in line... */
	    field_size = 78 - (offset + extra_tabs);

	  prompt_for_sized_entry(prompt, mybuffer, field_size,
				 page);

	  spaces[0] = ' ';	/* always at least ONE trailing space... */
	  spaces[1] = '\0';

	  /*  field_size-1 for the space spaces[] starts with  */
	  for (j = strlen(mybuffer); j < field_size-1; j++)
	    strfcat(spaces, " ", sizeof spaces);

	  fprintf(fd, "%s: %s%s", prompt, mybuffer, spaces);
	  fflush(fd);
	}

	fprintf(fd, "\n");
}

static void prompt_for_sized_entry(prompt, buffer, field_size, page)
     char *prompt, *buffer;
     int   field_size;
     struct menu_context  *page;
{
	/* This routine prompts for an entry of the size specified. */

	register int i;

	DPRINT(Debug,5,
	       (&Debug,"prompt-for-sized-entry \"%s\" %d chars\n", 
		prompt, field_size));

	Write_to_screen(FRM("%s: "), prompt);
	
	for (i=0;i<field_size; i++)
	    Writechar('_');
	for (i=0;i<field_size; i++)
	    Writechar(BACKSPACE);
	FlushBuffer();

	/* TODO: Check that field_size is not larger than buffer! */
	optionally_enter(buffer,-1,-1,0,field_size, page);
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1