/* The outbound mail functions */

extern "C" {
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <pwd.h>
#include <limits.h>
}
#include "mime.h"
#include "terminal.h"
#include "rcfile.h"
#include "tempfile.h"
#include "outgoing.h"

#ifndef MAILER
#define MAILER	"/usr/lib/sendmail -t"
#endif
#define EDITOR	"vi +%d"
#define SIGFILE	".signature"

extern int verbose;

/* Bare bones mail delivery */
static int SendMail(char *file)
{
	char cmdline[2*PATH_MAX];
	char *sentpath;

	/* Save a copy of the mail, if desired */
	sentpath = ExpandPath(GetStartVar("SentFile"));
	if ( sentpath ) {
		sprintf(cmdline, "echo From you >>%s", sentpath);
		system(cmdline);
		sprintf(cmdline, "echo Date: `date \"+%%a, %%d %%b %%Y %%H:%%M:%%S\"` >>%s", sentpath);
		system(cmdline);
		sprintf(cmdline, "cat %s >>%s", file, sentpath);
		system(cmdline);
		delete[] sentpath;
	}

	sprintf(cmdline, "%s <%s", MAILER, file);
	return(system(cmdline));
}

/***************************************************************************/
/* Helper routines for the mail functions */

static char *LocalAddr(char *addr)
{
	struct passwd *pw;
	char  *fulladdr;
	char  *domain, domainbuf[BUFSIZ];

	/* Inet address: user@host.net */
	if ( strchr(addr, '@') != NULL )
		return(NULL);

	/* Local address, otherwise, so look in password file */
	if ( (pw=getpwnam(addr)) == NULL )
		return(NULL);

	/* Slight optimization :-) */
	domainbuf[0] = '@';
	if ( (domain=GetStartVar("MailDomain")) == NULL ) {
		if ( (getdomainname(&domainbuf[1], BUFSIZ-2) < 0) ||
		     (strcmp(&domainbuf[1], "(none)") == 0) )
			domain = "";
		else
			domain = domainbuf;
	} else {
		domainbuf[1] = '\0';
		strncat(domainbuf, domain, BUFSIZ-2);
		domain = domainbuf;
	}
	
	/* Assemble the full name and address */
	fulladdr = new char[1+strlen(pw->pw_gecos)+1+1+
					strlen(addr)+strlen(domain)+1];
	sprintf(fulladdr, "(%s) %s%s", pw->pw_gecos, addr, domain);
	return(fulladdr);
}

/* Possible perform line wrapping? */
static int WriteAddrs(char *Field, char *Addrs, FILE *output)
{
	MIME_body *aliases = NULL;
	char *ptr, *alias;

	/* Print the "To: " caption */
	fprintf(output, Field);

	/* Get any possible aliases we have */
	if ( startup )
		aliases = startup->ByName("aliases");

	/* Run along the line, split it into addresses */
	while ( *Addrs && isspace(*Addrs) )
		++Addrs;

	ptr = Addrs;
	while ( *Addrs ) {
		if ( ! *ptr || (*ptr == ',') ) {
			if ( *ptr ) { 
				*ptr = '\0';
				do {
					++ptr;
				} while ( *ptr && (isspace(*ptr) || *ptr == ',') );
			}
			if ( aliases &&
				(alias=(char *)aliases->GetField(Addrs)) ) {
				if ( fprintf(output, "%s", alias) <= 0 )
					return(-1);
			} else if ( (alias=LocalAddr(Addrs)) ) {
				if ( fprintf(output, "%s", alias) <= 0 ) {
					delete[] alias;
					return(-1);
				}
				delete[] alias;
			} else {
				if ( fprintf(output, "%s", Addrs) <= 0 )
					return(-1);
			}
			if ( *ptr != '\0' )
				fprintf(output, ", ");
			Addrs = ptr;
		} else
			++ptr;
	}
	fprintf(output, "\n");
	return(1);
}

/* Add any extra headers to the output */
static int AutoHeader(FILE *output)
{
	MIME_body *headers;
	char      *header_key;
	char      *header_val;
	int        written = 0;

	if ( ! startup || ((headers=startup->ByName("headers")) == NULL) )
		return(0);

	headers->GetHeader(NULL);
	while ( (header_val=headers->GetHeader(&header_key)) ) {
		/* Don't include MIME Content headers */
		if (!strncasecmp(header_key, "Content-", strlen("Content-")))
			continue;

		fprintf(output, "%s: %s\n", header_key, header_val);
		++written;
	}
	return(written);
}

/* Write the signature file to the message */
static void LoadSig(FILE *output)
{
	FILE *input;
	char buffer[BUFSIZ];
	char *home;
	char *filename;

	if ( (home=getenv("HOME")) == NULL )
		return;

	/* Open the signature file */
	filename = new char[strlen(home)+1+strlen(SIGFILE)+1];
	sprintf(filename, "%s/%s", home, SIGFILE);
	if ( (input=fopen(filename, "r")) == NULL ) {
		delete[] filename;
		return;
	}
	delete[] filename;

	/* Copy it to the mail message */
	while ( fgets(buffer, BUFSIZ-1, input) )
		fputs(buffer, output);
	fclose(input);
}

static int AttachFile(char *&message, char *file, int is_mime)
{
	IObottle *feeder;
	MIME_body *body;
	char *endings[] = { NULL };	/* No explicit boundaries */
	int   okay, olderr;

	/* First open an IObottle on the message */
	feeder = new IObottle(message);
	if ( feeder->fail() || feeder->eof() ) {
		delete feeder;
		return(-1);
	}

	/* Now turn the message into a MIME body */
	body = new MIME_body(feeder, endings);

	/* Try adding the file */
	if ( (okay = body->AddPart(file, is_mime)) > -1 ) {
		if ( strcmp(message, body->RawPath()) != 0 ) {
			/* We no longer need the original message */
			feeder->temp(1);

			/* Set up the message as the new raw file */
			delete[] message;
			message = new char[strlen(body->RawPath())+1];
			strcpy(message, body->RawPath());
		}
	}
	olderr = errno;
	delete body;
	delete feeder;
	errno = olderr;
	return(okay);
}

/* File tab-completion function */
extern char *file_complete(char *sofar);	/* From handlemail.cc */

/* We pass in the outgoing file by reference, so that we can modify it */
static int SendMenu(char *&outgoing, char command, int offset, Terminal *tty)
{ 
	char  buffer[BUFSIZ];
	char *editor;
	int   done = 0;
	int   retval = 0;

	/* Choose your editor */
	if ( ((editor=getenv("EDITOR")) == NULL) &&
				((editor=getenv("VISUAL")) == NULL) )
		editor = EDITOR;

	/* Run the menu loop */
	while ( ! done ) {
		if ( command == '\0' ) {
			command = tty->promptchar(0,
			"[S]end, [e]dit, [a]dd attachment, [n]one of these: ");
		}
		switch (command) {
			case 'E':
			case 'e':
				sprintf(buffer, "%s %s", editor, outgoing);
				tty->system(0, buffer, offset);
				break;
			case 'A':
			case 'a':
				/* We don't support attachments yet */
				tty->fillprompt(buffer, BUFSIZ,
				"Enter file to attach: ", NULL, file_complete);
				if ( buffer[0] == '\0' ) {
					tty->status(0, "Attachment canceled.");
					sleep(1);
					break;
				}
				if ( AttachFile(outgoing, buffer, 0) < 0 ) {
					tty->status(0, "Attachment failed: %s",
							strerror(errno));
					tty->waitchar();
				}
				break;
			case 'N':
			case 'n':
			case 'q':
				tty->status(0, "Message cancelled.");
				sleep(1);
				done = 1;
				break;
			case 'S':
			case 's':
			default:
				/* Send mail and break out */
				tty->status(0, "Sending mail...");
				retval = SendMail(outgoing);
				tty->status(0, "Mail sent!");
				sleep(1);
				done = 1;
				break;
		}
		command = '\0';

	}
	return(retval);
}

/***************************************************************************/
/* Hot potato, we've got mail! */

/* Curses menu mailing */
int NewMail(char *To, char *Cc, const char *InReplyTo, const char *Subject,
            int do_edit, FILE *incfile, Terminal *tty, int use_signature)
{
	char *outbound;
	char temp_name[PATH_MAX];
	FILE *output;
	int   hlen, lines;
	int   retval = 0;

	/* Open a temporary file for processing */
	if ( (output=create_tempfile("w", temp_name)) == NULL ) {
		tty->status(0, "Couldn't create temp file: %s", strerror(errno));
		tty->waitchar();
		return(-1);
	}

	/* Write the header and then one blank line to start */
	hlen = 0;
	if ( (lines=WriteAddrs("To: ", To, output)) < 0 ) {
		tty->status(0, "Write error on tmpfile %s: %s",
						temp_name, strerror(errno));
		tty->waitchar();
		(void) unlink(temp_name);
		return(-1);
	}
	hlen += lines;
	if ( Cc ) {
		if ( (lines=WriteAddrs("Cc: ", Cc, output)) < 0 ) {
			tty->status(0, "Write error on tmpfile %s: %s",
						temp_name, strerror(errno));
			tty->waitchar();
			(void) unlink(temp_name);
			return(-1);
		}
		hlen += lines;
	}
	if ( InReplyTo ) {
		fprintf(output, "In-Reply-To: %s\n", InReplyTo);
		++hlen;
	}
	fprintf(output, "Subject: %s\n", Subject);
	++hlen;
	hlen += AutoHeader(output);
	fprintf(output, "\n");
	++hlen;

	if ( incfile ) {
		char buffer[BUFSIZ];
		while ( fgets(buffer, BUFSIZ-1, incfile) ) {
			if ( fputs(buffer, output) == EOF ) {
				tty->status(0, "Write error on tmpfile %s: %s",
						temp_name, strerror(errno));
				tty->waitchar();
				(void) unlink(temp_name);
				return(-1);
			}
		}
	}

	/* Print an extra line for the body */
	fprintf(output, "\n");

	/* Load up a .signature file, if one exists */
	if ( use_signature ) {
		LoadSig(output);
	}
	fclose(output);

	/* Save the name of the tmp_file for later deletion */
	outbound = new char[strlen(temp_name)+1];
	strcpy(outbound, temp_name);

	/* Send (or abort) the mail */
	SendMenu(outbound, (do_edit ? 'e' : '\0'), hlen+1, tty);

	/* Clean up!  We're done! :) */
	(void) unlink(outbound);
	delete[] outbound;
	return(retval);
}

/* Command-line mailing */
int NewMail(char *ToArray[], const char *Subject)
{
	Terminal *tty;
	char   *To;
	int i, len;
	int retval;

	/* Create the "To: " line */
	for ( i=0, len=0; ToArray[i]; ++i ) {
		if ( i > 0 )
			len += 2;
		len += strlen(ToArray[i]);
	}
	To = new char[len+1];

	*To = '\0';
	for ( i=0; ToArray[i]; ++i ) {
		if ( i > 0 )
			strcat(To, ", ");
		strcat(To, ToArray[i]);
	}

	/* Are we sending a file? */
	if ( isatty(0) ) {
		char subject[BUFSIZ];

		tty = new Terminal(1);
		if ( Subject == NULL ) {
			tty->fillprompt(subject,BUFSIZ,"Subject: ",NULL,NULL);
			Subject = subject;
		}
		retval = NewMail(To, NULL, NULL, Subject, 1, NULL, tty);
		delete tty;
	} else {
		char temp_name[PATH_MAX];
		FILE *output;
		char line[BUFSIZ];

		/* Manually create the message */
		if ( (output=create_tempfile("w", temp_name)) == NULL ) {
			fprintf(stderr, "Couldn't write to temp file: %s\n", strerror(errno));
			return(-1);
		}

		/* Write the headers */
		fprintf(output, "To: %s\n", To);
		if ( ! Subject )
			Subject = "No subject <file transmission>";
		fprintf(output, "Subject: %s\n", Subject);
		AutoHeader(output);
		fprintf(output, "\n");

		/* Write the file */
		while ( fgets(line, BUFSIZ-1, stdin) )
			fputs(line, output);
		fclose(output);
		printf("Sending mail...");
		fflush(stdout);
		retval = SendMail(temp_name);
		printf("done.\n");
		(void) unlink(temp_name);
	}
	return(retval);
}


syntax highlighted by Code2HTML, v. 0.9.1