/*:ts=8*/
/*****************************************************************************
 * FIDOGATE --- Gateway software UNIX <-> FIDO
 *
 * $Id: rfc2ftn.c,v 3.9.2.1 1995/06/28 19:53:21 mj Exp mj $
 *
 * Read mail or news from standard input and convert it to a FIDO packet.
 *
 *****************************************************************************
 * Copyright (C) 1990-1995
 *  _____ _____
 * |     |___  |   Martin Junius             FIDO:      2:2452/110.1
 * | | | |   | |   Republikplatz 3           Internet:  mj@sungate.fido.de
 * |_|_|_|@home|   D-52072 Aachen, Germany   Phone:     ++49-241-86931 (voice)
 *
 * This file is part of FIDOGATE.
 *
 * FIDOGATE is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * FIDOGATE is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with FIDOGATE; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "fidogate.h"
#include "getopt.h"

#include <pwd.h>




#define PROGRAM "rfc2ftn"
#define VERSION "$Revision: 3.9.2.1 $"



/*
 * MIME (RFC 1521) header info
 */
typedef struct st_mimeinfo
{
    char *version;		/* MIME-Version */
    char *type;			/* Content-Type */
    char *encoding;		/* Content-Transfer-Encoding */
}
MIMEInfo;



/*
 * Prototypes
 */
MIMEInfo *get_mime	P((void));
void  sendback		P((const char *, ...));
char *mail_sender	P((Node *));
char *mail_address	P((void));
char *receiver		P((char *, Node *));
char *mail_receiver	P((char *, Node *));
time_t	mail_date	P((void));
char *estrtok		P((char *, char *));
int snd_mail		P((char *, char *, int));
int snd_message		P((Message *, Area *, char *, char *, char *,
			   int, char *, int, MIMEInfo *));
int print_tear_line	P((FILE *));
int print_origin	P((FILE *, char *));
int print_local_msgid	P((FILE *));
int print_via		P((FILE *));
void short_usage	P((void));
void usage		P((void));
int	sendmail_t	P((char *, int));



static char *o_flag = NULL;	/* -o --out-packet-file option */
static char *w_flag = NULL;	/* -w --write-outbound  option */
static int   W_flag = FALSE;	/* -W --write-crash     option */


/* Private mail (default) */
bool private = TRUE;

/* News-article */
int newsmode = FALSE;


static int no_from_line	= FALSE;		/* config: NoFromLine */



/*
 * Global Textlist to save message body
 */
Textlist body = { NULL, NULL };



/*
 * Return MIME header
 */
MIMEInfo *get_mime()
{
    static MIMEInfo mime;
    
    mime.version  = header_getcomplete("MIME-Version");
    mime.type     = header_getcomplete("Content-Type");
    mime.encoding = header_getcomplete("Content-Transfer-Encoding");

    return &mime;
}



/*
 * In case of error print message (mail returned by MTA)
 */
#ifdef __STDC__
void sendback(const char *fmt, ...)
{
    va_list args;
    
    va_start(args, fmt);
#else /**!__STDC__**/    
void sendback(va_alist)
va_dcl
{
    va_list args;
    char *fmt;

    va_start(args);
    fmt = va_arg(args, char *);
#endif /**__STDC__**/

    fprintf(stderr, "Internet -> FIDO gateway / FIDOGATE %s\n",
	    version_global()                                   );
    fprintf(stderr, "   ----- ERROR -----\n");
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
}



/*
 * Return sender's full name. This is taken from
 *   - Reply-To header
 *   - From header
 *   - user id and password file
 *   - environment LOGNAME or USER
 */

char *mail_sender(node)
    Node *node;
{
    struct passwd *pwd;
    static char buffer[BUFSIZ];
#ifdef PASSTHRU_NETMAIL
    static char name[MSG_MAXNAME];
    Node n;
#endif /**PASSTHRU_NETMAIL**/
    char *from;
    char *p;
    
    /*
     * Default sender node is gateway address
     */
    *node = cf_n_addr();
    
    /*
     * Look up name in Reply-To or From header of message
     */
    if((from = header_getcomplete("Reply-To")) == NULL)
	from = header_getcomplete("From");

    if(from)
    {
	/*
	 * Found Reply-To or From header
	 */
	debug(5, "RFC From: %s", from);

#ifdef PASSTHRU_NETMAIL
	/*
	 * If the from address is an FTN address, convert and pass it via
	 * parameter node. This may cause problems when operating different
	 * FTNs.
	 */
	if(addr_from_rfcaddr(from, buffer))
	    if(parse_address(buffer, name, &n)!=ERROR && isfido())
	    {
		*node = n;
		debug(5, "    is FTN address %s", node_to_asc(node, TRUE));
	    }
#endif /**PASSTHRU_NETMAIL**/

	if(!name_from_rfcaddr(from, buffer))
	    username_from_rfcaddr(from, buffer);
	debug(5, "RFC Full name: %s", buffer);
	return buffer;
    }

    /*
     * Try user id and passwort file entry
     */
    if((pwd = getpwuid(getuid())))
    {
	strcpy(buffer, pwd->pw_gecos);
	if((p = strchr(buffer, ',')))
	    /*
	     * Kill stuff after ','
	     */
	    *p = 0;
	if(!*buffer)
	    /*
	     * Empty, use user name
	     */
	    strcpy(buffer, pwd->pw_name);
	debug(5, "passwd Full name: %s", buffer);
	return buffer;
    }

    /*
     * Use user name from environment as a last resort
     */
    if((p = getenv("LOGNAME")))
    {
	strcpy(buffer, p);
	debug(5, "Env LOGNAME Full name: %s", buffer);
	return buffer;
    }
    if((p = getenv("USER")))
    {
	strcpy(buffer, p);
	debug(5, "Env USER Full name: %s", buffer);
	return buffer;
    }

    debug(5, "No name for sender found");
    strcpy(buffer, "Unknown User");
    return buffer;
}



/*
 * Return sender's mail address. This is taken from
 *   - Reply-To header
 *   - From header
 *   - user id and password file
 *   - environment LOGNAME or USER
 */

char *mail_address()
{
    struct passwd *pwd;
    char *p;
    
    /*
     * Look up name in Reply-To or From header of message
     */
    if((p = header_getcomplete("Reply-To")) == NULL)
	p = header_getcomplete("From");

    if(p)
	return buf_copy(p);

    /*
     * Try user id and passwort file entry
     */
    if((pwd = getpwuid(getuid())))
	return buf_sprintf("%s@%s", pwd->pw_name, cf_fqdn() );

    /*
     * Use user name from environment as a last resort
     */
    if((p = getenv("LOGNAME")))
	return buf_sprintf("%s@%s", p, cf_fqdn() );
    if((p = getenv("USER")))
	return buf_sprintf("%s@%s", p, cf_fqdn() );

    return NULL;
}



/*
 * receiver() --- Check for aliases and beautify name
 */

char *receiver(to, node)
    char *to;
    Node *node;
{
    static char name[MSG_MAXNAME];
    int i, c, convert_flag;
    Alias *alias;
    int us_flag;
    
    /*
     * Check for name alias
     */
    debug(5, "Name for alias checking: %s", to);

    if((alias = alias_lookup(node, to, NULL)))
    {
	debug(5, "Alias found: %s %s %s", alias->username,
	      node_to_asc(&alias->node, TRUE), alias->fullname);
	strcpy(name, alias->fullname);

	/*
	 * Store address from ALIASES into node, this will reroute
	 * the message to the point specified in ALIASES, if the message
	 * addressed to node without point address.
	 */
	*node = alias->node;
	
	return name;
    }
    
    /*
     * Alias not found. Return the the original receiver with all
     * '_' characters replaced by space and all words capitalized.
     * If no '_' chars are found, '.' are converted to spaces
     * (User.Name@p.f.n.z.fidonet.org addressing style).
     */
    convert_flag = isupper(*to) ? -1 : 1;
    us_flag      = strchr(to, '_') != NULL;
    
    for(i=0; *to && i<35; i++, to++) {
	c = *to;
	switch(c) {
	case '_':
	case '.':
	    if( c=='_' || (!us_flag && c=='.') )
		c = ' ';
	    name[i] = c;
	    if(!convert_flag)
		convert_flag = 1;
	    break;
	case '%':
	    if(convert_flag != -1)
		convert_flag = 2;
	    /**Fall thru**/
	default:
	    if(convert_flag > 0) {
		name[i] = islower(c) ? toupper(c) : c;
		if(convert_flag == 1)
		    convert_flag = 0;
	    }
	    else
		name[i] = c;
	    break;
	}
    }
    name[i] = 0;

    /**FIXME: implement a generic alias with pattern matching**/
    /*
     * Convert "postmaster" to "sysop"
     */
    if(!stricmp(name, "postmaster"))
	BUF_COPY(name, "Sysop");
    
    debug(5, "No alias found: return %s", name);

    return name;
}



/*
 * Return from field for FIDO message.
 * Alias checking is done via receiver().
 */

char *mail_receiver(address, node)
    char *address;
    Node *node;
{
    char *to;
    char name[MSG_MAXNAME];
    char realname[128];

    realname[0] = 0;

    if(address) {
	/*
	 * Address is argument
	 */
	if(parse_address(address, name, node)) {
	    log("unknown address %s", address);
	    return NULL;
	}
    }
    else {
	/*
	 * News/EchoMail: address is echo feed
	 */
	*node = cf_n_uplink();
	strcpy(name, "All");
    
	/*
	 * User-defined header line X-Comment-To for gateway software
	 * (can be patched into news reader)
	 */
	if( (to = header_get("X-Comment-To")) )
	    if(name_from_rfcaddr(to, realname) && *name)
		strncpy0(name, realname, sizeof(name));
    }

    return receiver(name, node);
}



/*
 * Get date field for FIDO message. Look for `Date:' header or use
 * current time.
 */
time_t mail_date()
{
    time_t timevar = -1;
    char *header_date;

    if((header_date = header_get("Date"))) {
	/* try to extract date and other information from it */
	debug(5, "RFC Date: %s", header_date);
	timevar = parsedate(header_date, NULL);
	if(timevar == -1)
	    debug(5, "          can't parse this date string");
    }

    return timevar;
}



/*
 * Process mail/news message
 */
int snd_mail(from, to, split)
    char *from;				/* Address from */
    char *to;				/* Address to send to (news = NULL) */
    int split;				/* Split message into several parts */
{
    char groups[BUFSIZ];
    Node node_from, node_to;
    char *p, *subj;
    int status, fido;
    Message msg;
    char *flags = NULL;
    MIMEInfo *mime;
    
    node_from.domain[0] = 0;
    node_to  .domain[0] = 0;
    
    if(to)
	debug(3, "RFC To: %s", to);

    /*
     * To name/node
     */
    p = mail_receiver(to, &node_to);
    if(!p) {
	if(*address_error)
	    sendback("Address %s:\n  %s", to, address_error);
	else
	    sendback("Address %s:\n  address/host is unknown", to);
	return(EX_NOHOST);
    }
    strncpy0(msg.name_to, p, sizeof(msg.name_to));
    fido = isfido();

    cf_set_zone(node_to.zone);

    /*
     * From name/node
     */
    p = mail_sender(&node_from);
    if(!p)
	p = "Gateway";
    strncpy0(msg.name_from, p, sizeof(msg.name_from));

    /*
     * Subject
     */
    subj = header_get("Subject");
    if(!subj)
	subj = "(no subject)";

    /*
     * Date
     */
    msg.date = mail_date();
    msg.cost = 0;
    msg.attr = 0;

    /*
     * MIME header
     */
    mime = get_mime();

    /*
     * X-Flags
     */
    flags = header_get("X-Flags");

    if(private)
    {
	msg.attr |= MSG_PRIVATE;

	p = flags;
	
	/*
	 * Allow only true local users to use the X-Flags header
	 */
	if(addr_is_local(from) && header_hops() <= 1)
	    debug(5, "true local address - o.k.");
	else
	{
	    if(flags)
		log("non-local %s, X-Flags: %s", from, flags);
	    flags = p = NULL;
	}
	    
	if(p)
	{
	    while(*p)
		switch(*p++)
		{
		case 'c': case 'C':
		    msg.attr |= MSG_CRASH;
		    break;
		case 'p': case 'P':
		    msg.attr |= MSG_PRIVATE;
		    break;
		case 'h': case 'H':
		    msg.attr |= MSG_HOLD;
		    break;
		case 'f': case 'F':
		    msg.attr |= MSG_FILE;
		    break;
		case 'r': case 'R':
		    msg.attr |= MSG_RRREQ;
		    break;
		}
	}	

	/*
	 * Return-Receipt-To -> RRREQ flag
	 */
	if( (p = header_get("Return-Receipt-To")) )
	   msg.attr |= MSG_RRREQ;
    }
    
    if(newsmode) {
	Area *pa;
	int xpost_flag;

#ifdef NO_CONTROL
	/*
	 * Check for news control message
	 */
	p = header_get("Control");
	if(p)
	{
	    debug(3, "Skipping Control: %s", p);
	    return EX_OK;
	}
#endif

	/*
	 * News message: get newsgroups and convert to FIDO areas
	 */
	p = header_get("Newsgroups");
	if(!p) {
	    sendback("No Newsgroups header in news message");
	    return(EX_DATAERR);
	}
	strncpy0(groups, p, sizeof(groups));
	debug(3, "RFC Newsgroups: %s", groups);

	xpost_flag = strchr(groups, ',') != NULL;
	
	for(p=strtok(groups, ","); p; p=strtok(NULL, ","))
	{
	    debug(5, "Look up newsgroup %s", p);
	    pa = areas_lookup(NULL, p);
	    if(!pa)
		debug(5, "No FTN area");
	    else
	    {
		debug(5, "Found: %s %s Z%d", pa->area, pa->group, pa->zone);

		if( xpost_flag && (pa->flags & AREA_NOXPOST) )
		{
		    debug(5, "No Xpostings allowed - skipped");
		    continue;
		}
		if( xpost_flag && (pa->flags & AREA_LOCALXPOST) )
		{
		    if(addr_is_local(from))
		    {
			debug(5, "Local Xposting - o.k.");
		    }
		    else
		    {
			debug(5, "No non-local Xpostings allowed - skipped");
			continue;
		    }
		}

		/* Set address or zone aka for this area */
		if(pa->addr.zone != -1)
		    cf_set_curr(&pa->addr);
		else
		    cf_set_zone(pa->zone);

		msg.area      = pa->area;
		msg.node_from = cf_n_addr();
		msg.node_to   = cf_n_uplink();
		status = snd_message(&msg, pa, from, NULL,
				     subj, split, flags, fido, mime);
		if(status)
		    return status;
	    }
	}
    }
    else {
	/*
	 * NetMail message
	 */
	log("%s -> %s", from, to);
	msg.area      = NULL;
	msg.node_from = node_from;
	msg.node_to   = node_to;
	return snd_message(&msg, NULL, from, to,
			   subj, split, flags, fido, mime);
    }
    
    return EX_OK;
}


int snd_message(msg, parea, from, to, subj, split, flags, fido, mime)
    Message *msg;			/* FTN nessage structure */
    Area *parea;			/* Area/newsgroup */
    char *from;				/* Internet sender */
    char *to;				/* Internet recipient */
    char *subj;				/* Internet Subject line */
    int split;				/* >0: split message part */
    char *flags;			/* From X-Flags header */
    int fido;				/* TRUE: recipient is FTN address */
    MIMEInfo *mime;			/* MIME stuff */
{
    static int last_zone = -1;		/* Zone address of last packet */
    static FILE *sf;			/* Packet file */
    char *header;
    int part = 1, line = 1;
    long size;
    Textline *p;
    char *id;
    int flag, add_empty;
#ifdef FSC_0047
    time_t time_split = -1;
    long seq          = 0;
#endif
    int mime_qp = 0;			/* quoted-printable flag */

    /*
     * MIME info
     */
    debug(6, "RFC MIME-Version:              %s",
	  mime->version  ? mime->version  : "-NONE-");
    debug(6, "RFC Content-Type:              %s",
	  mime->type     ? mime->type     : "-NONE-");
    debug(6, "RFC Content-Transfer-Encoding: %s",
	  mime->encoding ? mime->encoding : "-NONE-");

    if(mime->encoding && !stricmp(mime->encoding, "QUOTED-PRINTABLE"))
	mime_qp = MIME_QP;
    
    /*
     * Set pointer to first line in message body
     */
    p = body.first;
    
    /*
     * Open output packet
     */
    if(!o_flag && cf_zone()!=last_zone)
	pkt_close();
    if(!pkt_isopen())
    {
	int   crash = msg->attr & MSG_CRASH;
	char *p;

	if(w_flag || (W_flag && crash))
	{
	    char *flav  = crash ? "Crash"      : w_flag;
	    Node  nn    = crash ? msg->node_to : cf_n_uplink();

	    /* Crash mail for a point via its boss */
	    nn.point = 0;
	    if( (sf = pkt_open(o_flag, &nn, flav, TRUE)) == NULL )
		return EX_CANTCREAT;

	    /* File attach message, subject is file name */
	    if(msg->attr & MSG_FILE)
	    {
		/* Attach */
		if(bink_attach(&nn, 0, subj, flav, FALSE) != OK)
		    return EX_CANTCREAT;
		/* New subject is base name of file attach */
		if((p=strrchr(subj, '/')) || (p=strrchr(subj, '\\')))
		    subj = p + 1;
	    }
	}
	else
	{
	    if( (sf = pkt_open(o_flag, NULL, NULL, FALSE)) == NULL )
		return EX_CANTCREAT;
	}
    }
    
    last_zone = cf_zone();


 again:

    /* Subject with split part indication */
    if(split && part>1)
    {
	sprintf(msg->subject, "%02d: ", part);
	strncat0(msg->subject, subj, sizeof(msg->subject));
    }
    else
	strncpy0(msg->subject, subj, sizeof(msg->subject));

    /* Header */
    pkt_put_msg_hdr(sf, msg, TRUE);


    if(!flags || !strchr(flags, 'M'))		/* X-Flags: M */
    {
	/*
	 * Add kludges for MSGID / REPLY and ORIGID / ORIGREF
	 */
	if((header = header_get("Message-ID")))
	{
	    if((id = msgid_rfc_to_fido(&flag, header, part, split, msg->area)))
	    {
		fprintf(sf, "\001MSGID: %s\r\n", id);
#ifdef MSGID_ORIGID
		if(flag && (id = msgid_rfc_to_origid(header, part, split)))
		    fprintf(sf, "\001ORIGID: %s\r\n", id);
#endif
	    }
	    else
		print_local_msgid(sf);
	}	
	else
	    print_local_msgid(sf);
	
	if((header = header_get("References")) ||
	   (header = header_get("In-Reply-To")))
	{
	    if((id = msgid_rfc_to_fido(&flag, header, 0, 0, msg->area)))
	    {
		fprintf(sf, "\001REPLY: %s\r\n", id);
#ifdef MSGID_ORIGID
		if(flag && (id = msgid_rfc_to_origid(header, 0, 0)))
		    fprintf(sf, "\001ORIGREF: %s\r\n", id);
#endif
	    }
	}
    }
    else
	print_local_msgid(sf);

#ifdef FSC_0035
    if(!no_from_line)
	if(!flags || !strchr(flags, 'N'))
	{
	    /*
	     * Generate FSC-0035 ^AREPLYADDR, ^AREPLYTO
	     */
	    if(from) 
	    {
		fprintf(sf, "\001REPLYADDR %s\r\n", from);
		fprintf(sf, "\001REPLYTO %s %s\r\n",
			node_to_asc(cf_addr(), FALSE),
			msg->name_from);
	    }
	}
#endif /**FSC_0035**/    

    /*
     * Add ^ACHRS kludge indicating that this messages uses the
     * ISO-8859-1 charset. This may be not the case, but most Usenet
     * messages with umlauts use this character set and FIDOGATE
     * doesn't support MIME yet.  (^ACHRS is documented in FSC-0054)
     */
    fprintf(sf, "\001CHRS: LATIN-1 2\r\n");

    /*
     * Add ^ARFC header lines according to FIDO-Gatebau '94 specs
     */
    /* ^ARFC: 1 lines for now */
    fprintf(sf, "\001RFC: 1 lines\r\n");
    

    add_empty = FALSE;
    
    /*
     * If Gateway is set in config file, add To line for addressing
     * FIDO<->Internet gateway
     */
    if(cf_gateway().zone && to && !fido)
    {
	fprintf(sf, "To: %s\r\n", to);
	add_empty = TRUE;
    }

    if(!flags || !strchr(flags, 'N'))
    {
	if(!no_from_line)
	{
	    /*
	     * Add From line with return address
	     */
	    if(from)
	    {
		fprintf(sf, "From: %s\r\n", from);
		add_empty = TRUE;
	    }
	}

	/*
	 * Add empty line before " * " info lines, because otherwise
	 * Eugene Crosser's ifgate gets confused and treats them as
	 * RFC continuation headers.
	 */
	if(add_empty)
	    fprintf(sf, "\r\n");
	add_empty = FALSE;

	if(!split || part==1)		/* Info lines only in 1st part */
	{
	    /*
	     * Add some header lines as info for user
	     */
	    if((header = header_get("From")))
	    {
		if(strcmp(from, header)) 
		{
		    /*
		     * From header line is not equal to reply address, output
		     * From and Reply-To header line.
		     */
		    fprintf(sf, " * From: %s\r\n", header);
		    while((header = header_getnext()))
			fprintf(sf, " *       %s\r\n", header);
		    if((header = header_get("Reply-To")))
		    {
			fprintf(sf, " * Reply-To: %s\r\n", header);
			while((header = header_getnext()))
			    fprintf(sf, " *           %s\r\n", header);
		    }
		    add_empty = TRUE;
		}		    
	    }
	    if((header = header_get("To")))
	    {
		fprintf(sf, " * To: %s\r\n", header);
		while((header = header_getnext()))
		    fprintf(sf, " *     %s\r\n", header);
		add_empty = TRUE;
	    }
	    if((header = header_get("Cc")))
	    {
		fprintf(sf, " * Cc: %s\r\n", header);
		while((header = header_getnext()))
		    fprintf(sf, " *     %s\r\n", header);
		add_empty = TRUE;
	    }
	    if((header = header_get("Newsgroups")))
		if(strchr(header, ',') || !newsmode)
		{
		    /* Posted to multiple groups or mail */
		    fprintf(sf, " * Newsgroups: %s\r\n", header);
		    add_empty = TRUE;
		}
	}
	
    }
    if(add_empty)
	fprintf(sf, "\r\n");

#ifdef FSC_0047
    /*
     * Add ^ASPLIT kludge according to FSC-0047 for multi-part messages.
     * Format:
     *     ^ASPLIT: date      time     @net/node    nnnnn pp/xx +++++++++++
     * e.g.
     *     ^ASPLIT: 30 Mar 90 11:12:34 @494/4       123   02/03 +++++++++++
     */
    if(split)
    {
	char buf[20];
	
	if(part == 1)
	{
	    time_split = time(NULL);
	    seq        = sequencer(SEQ_SPLIT) % 100000;	/* Max. 5 digitss */
	}

	sprintf(buf, "%d/%d", cf_addr()->net, cf_addr()->node);
	
	fprintf(sf,
		"\001SPLIT: %-18s @%-11.11s %-5ld %02d/%02d +++++++++++\r\n",
		date("%d %b %y %H:%M:%S", &time_split), buf, seq, part, split);
    }
#else
    /*
     * Add line indicating splitted message
     */
    if(split)
	fprintf(sf,
		" * Large message split by FIDOGATE: part %02d/%02d\r\n\r\n",
		part, split						    );
#endif /**FSC_0047**/

    /*
     * Copy message body
     */
    size = 0;
    while(p)
    {
	/* Decode all MIME-style quoted printables
	 *
	 * Don't do anything right now, must check MIME headers.
	 * Otherwise the decoding will badly interfer with uuencoded
	 * messages!!!
	 */
	mime_dequote(buffer, sizeof(buffer), p->line, mime_qp);
	pkt_put_line(sf, buffer);
	size += strlen(buffer) + 1 /* additional CR */;
	if(size > MAXMSGSIZE) {
	    print_tear_line(sf);
	    if(newsmode)
		print_origin(sf,
			     parea && parea->origin ? parea->origin
			     : cf_origin() );
	    /* End of message */
	    putc(0, sf);
	    part++;
	    p = p->next;
	    goto again;
	}
	p = p->next;
	line++;
    }

    /*
     * If message is for echo mail (-n flag) then add
     * tear, origin, seen-by and path line.
     */
    print_tear_line(sf);
    if(newsmode)
	print_origin(sf,
		     parea && parea->origin ? parea->origin : cf_origin() );
    else
	print_via(sf);

    /*
     * Done
     */
    /* End of message */
    putc(0, sf);
    return EX_OK;
}



/*
 * Output tear line
 */
int print_tear_line(fp)
FILE *fp;
{
    fprintf(fp, "\r\n--- FIDOGATE %s\r\n", version_global());

    return ferror(fp);
}



/*
 * Generate origin, seen-by and path line
 */
int print_origin(fp, origin)
    FILE *fp;
    char *origin;
{
    char buf[80];
    char bufa[30];
    int len;
    
    strncpy0(buf, " * Origin: ", sizeof(buf));
    strncpy0(bufa, node_to_asc(cf_addr(), TRUE), sizeof(bufa));
    
    /*
     * Max. allowed length of origin line is 79 (80 - 1) chars,
     * 3 chars for additional " ()".
     */
    len = 80 - strlen(bufa) - 3;

    /* Add origin text */
    strncat0(buf, origin, len);
    /* Add address */
    strncat0(buf, " (", sizeof(buf));
    strncat0(buf, bufa, sizeof(buf));
    strncat0(buf, ")" , sizeof(buf));
    
    /* Origin */
    fprintf(fp, "%s\r\n", buf);

    if(cf_addr()->point)		/* Generate 4D addresses */
    {
	/* SEEN-BY */
	fprintf(fp, "SEEN-BY: %d/%d",
		cf_addr()->net, cf_addr()->node);
#ifdef FTN_4D_ECHOMAIL_WITH_POINTS
	fprintf(fp, ".%d", cf_addr()->point);
#endif
	/* PATH */
	fprintf(fp, "\r\n\001PATH: %d/%d",
		cf_addr()->net, cf_addr()->node);
#ifdef FTN_4D_ECHOMAIL_WITH_POINTS
	fprintf(fp, ".%d", cf_addr()->point);
#endif
	fputs("\r\n", fp);
    }
    else				/* Generate 3D addresses */
    {
	/* SEEN-BY */
	fprintf(fp, "SEEN-BY: %d/%d",
		cf_addr()->net, cf_addr()->node );
	if(cf_uplink()->net != cf_addr()->net)
	    fprintf(fp, " %d/%d", cf_uplink()->net, cf_uplink()->node);
	else if(cf_uplink()->node != cf_addr()->node)
	    fprintf(fp," %d", cf_uplink()->node);
	fprintf(fp,"\r\n");
	/* PATH */
	fprintf(fp, "\001PATH: %d/%d\r\n",
		cf_addr()->net, cf_addr()->node);
    }
    
    return ferror(fp);
}



/*
 * Generate local `^AMSGID:' if none is found in message header
 */
int print_local_msgid(fp)
FILE *fp;
{
    long msgid;

    msgid = sequencer(SEQ_MSGID);
    fprintf(fp, "\001MSGID: %s %08ld\r\n",
	    node_to_asc(cf_addr(), FALSE), msgid);

    return ferror(fp);
}



/*
 * Generate "^AVia" line for NetMail
 */
int print_via(fp)
    FILE *fp;
{
    fprintf(fp, "\001Via FIDOGATE/%s %s, %s\r\n",
	    PROGRAM, node_to_asc(cf_addr(), FALSE),
	    date("%a %b %d %Y at %H:%M:%S %Z", NULL)  );

    return ferror(fp);
}



/*
 * Send mail to addresses taken from To, Cc, Bcc headers
 */
int sendmail_t(from, split)
    char *from;
    int split;
{
    char addr[MAXINETADDR];
    char *header, *p;
    int status;
    
    /*
     * To:
     */
    for(header=header_get("To"); header; header=header_getnext())
	for(p=addr_token(header); p; p=addr_token(NULL))
	    if(addr_from_rfcaddr(p, addr))
		if((status = snd_mail(from, addr, split)) != EX_OK)
		    return status;

    /*
     * Cc:
     */
    for(header=header_get("Cc"); header; header=header_getnext())
	for(p=addr_token(header); p; p=addr_token(NULL))
	    if(addr_from_rfcaddr(p, addr))
		if((status = snd_mail(from, addr, split)) != EX_OK)
		    return status;

    /*
     * Bcc:
     */
    for(header=header_get("Bcc"); header; header=header_getnext())
	for(p=addr_token(header); p; p=addr_token(NULL))
	    if(addr_from_rfcaddr(p, addr))
		if((status = snd_mail(from, addr, split)) != EX_OK)
		    return status;

    return EX_OK;
}



/*
 * Usage messages
 */
void short_usage()
{
    fprintf(stderr, "usage: %s [-options] [user ...]\n", PROGRAM);
    fprintf(stderr, "       %s --help  for more information\n", PROGRAM);
}


void usage()
{
    fprintf(stderr, "FIDOGATE %s  %s %s\n\n",
	    version_global(), PROGRAM, version_local(VERSION) );
    
    fprintf(stderr, "usage:   %s [-options] [user ...]\n\n", PROGRAM);
    fprintf(stderr, "\
options: -b --news-batch              process news batch\n\
         -B --binkley NAME            set Binkley outbound directory\n\
         -i --ignore-registration     do not bounce unknown p.f.n.z.fido.de\n\
	 -n --news-mode               set news mode\n\
	 -o --out-packet-file  NAME   set outbound packet file name\n\
	 -O --out-packet-dir   NAME   set outbound packet directory\n\
         -t --to                      get recipient from To, Cc, Bcc\n\
         -w --write-outbound FLAV     write directly to Binkley .?UT packet\n\
         -W --write-crash             write crash directly to Binkley .CUT\n\
\n\
	 -v --verbose                 more verbose\n\
	 -h --help                    this help\n\
         -c --config name             read config file (\"\" = none)\n\
	 -L --lib-dir name            set lib directory\n\
	 -S --spool-dir name          set spool directory\n\
	 -a --addr Z:N/F.P            set FTN address\n\
	 -u --uplink-addr Z:N/F.P     set FTN uplink address\n");
    
    exit(0);
}



/***** main ******************************************************************/
int main(argc, argv)
    int argc;
    char *argv[];
{
    int cnt, c;
    int status = EX_OK;
    long size, nmsg;
    int split;
    char *from;
    int b_flag=FALSE, t_flag=FALSE;
    char *B_flag=NULL;
    char *O_flag=NULL;
    char *c_flag=NULL;
    char *S_flag=NULL, *L_flag=NULL;
    char *a_flag=NULL, *u_flag=NULL;
    int option_index;
    static struct option long_options[] =
    {
	{ "news-batch",   0, 0, 'b'},	/* Process news batch */
	{ "binkley",      1, 0, 'B'},	/* Binkley outbound base dir */
	{ "out-packet-file",1,0,'o'},	/* Set packet file name */
	{ "news-mode",    0, 0, 'n'},	/* Set news mode */
	{ "ignore-registration", 0, 0, 'i'}, /* Do not bounce unknown fido.de*/
	{ "out-dir",      1, 0, 'O'},	/* Set packet directory */
	{ "write-outbound",1,0, 'w'},	/* Write to Binkley outbound */
	{ "write-crash",  0, 0, 'w'},	/* Write crash to Binkley outbound */
	{ "to",           0, 0, 't'},	/* Get recipient from To, Cc, Bcc */

	{ "verbose",      0, 0, 'v'},	/* More verbose */
	{ "help",         0, 0, 'h'},	/* Help */
	{ "config",       1, 0, 'c'},	/* Config file */
	{ "spool-dir",    1, 0, 'S'},	/* Set FIDOGATE spool directory */
	{ "lib-dir",      1, 0, 'L'},	/* Set FIDOGATE lib directory */
	{ "addr",         1, 0, 'a'},	/* Set FIDO address */
	{ "uplink-addr",  1, 0, 'u'},	/* Set FIDO uplink address */
	{ 0,              0, 0, 0  }
    };

    log_program(PROGRAM);
    
    /* Init configuration */
    cf_initialize();
    
    newsmode = FALSE;

    

    while ((c = getopt_long(argc, argv, "bB:o:niO:w:Wtvhc:L:S:a:u:",
			    long_options, &option_index     )) != EOF)
	switch (c) {
	/***** rfc2ftn options *****/
	case 'b':
	    /* News batch flag */
	    b_flag   = TRUE;
	    newsmode = TRUE;
	    private  = FALSE;
	    break;
	case 'B':
	    B_flag = optarg;
	    break;
	case 'o':
	    /* Set packet file name */
	    o_flag = optarg;
	    break;
	case 'n':
	    /* Set news-mode */
	    newsmode = TRUE;
	    private  = FALSE;
	    break;
	case 'i':
	    /* Don't bounce non-Fido.DE */
	    i_flag = TRUE;
	    break;
	case 'O':
	    /* Set packet dir */
	    O_flag = optarg;
	    break;
	case 'w':
	    w_flag = optarg;
	    break;
	case 'W':
	    W_flag = TRUE;
	    break;
	case 't':
	    t_flag = TRUE;
	    break;
	    
	/***** Common options *****/
	case 'v':
	    verbose++;
	    break;
	case 'h':
	    usage();
	    exit(0);
	    break;
	case 'c':
	    c_flag = optarg;
	    break;
	case 'S':
	    S_flag = optarg;
	    break;
	case 'L':
	    L_flag = optarg;
	    break;
	case 'a':
	    a_flag = optarg;
	    break;
	case 'u':
	    u_flag = optarg;
	    break;
	default:
	    short_usage();
	    exit(EX_USAGE);
	    break;
	}


    /*
     * Read config file
     */
    if(L_flag)				/* Must set libdir beforehand */
	cf_set_libdir(L_flag);
    cf_read_config_file(c_flag);

    /*
     * Process config options
     */
    if(B_flag)
	cf_set_outbound(B_flag);
    if(L_flag)
	cf_set_libdir(L_flag);
    if(S_flag)
	cf_set_spooldir(S_flag);
    if(a_flag)
	cf_set_addr(a_flag);
    if(u_flag)
	cf_set_uplink(u_flag);

    cf_debug();

    /*
     * Process optional config statements
     */
    if(cf_get_string("NoFromLine", TRUE))
    {
	debug(8, "config: nofromline");
	no_from_line = TRUE;
    }
    

    /*
     * Process local options
     */
    if(O_flag)
	pkt_outdir(O_flag, NULL);
    else
	pkt_outdir(cf_spooldir(), OUTDIR);

    /*
     * Init various modules
     */
    if(newsmode)
	areas_init();
    hosts_init();
    alias_init();
#ifdef MAUS_DE
    maus_init();
#endif
    passwd_init();


    /**
     ** Main loop: read message(s) from stdin, batches if -b
     **/
    nmsg = 0;
    while(TRUE)
    {
	if(b_flag)
	{
	    size = read_rnews_size(stdin);
	    if(size == -1)
		log("ERROR: reading news batch");
	    if(size <= 0)
		break;
	    nmsg++;

	    debug(1, "Batch: message #%ld size %ld", nmsg, size);
	}
	
	/*
	 * Read message header from stdin
	 */
	header_delete();
	header_read(stdin);
    
	/*
	 * Read message body from stdin and count size
	 */
	size = 0;
	tl_clear(&body);
	while(read_line(buffer, BUFFERSIZE, stdin)) {
	    tl_append(&body, buffer);
	    size += strlen(buffer) + 1;	    /* `+1' for additional CR */
	}
	split = size>MAXMSGSIZE ? size/MAXMSGSIZE + 1 : 0;
	debug(1, "Message body size %ld", size);
	if(split)
	    debug(1, "Must split message, %d parts", split);
	from = mail_address();		/* From address */

	if(newsmode)
	    /*
	     * Send mail to echo feed for news messages
	     */
	    status = snd_mail(from, NULL, split);
	else
	    if(t_flag)
	    {
		/*
		 * Send mail to addresses from headers
		 */
		status = sendmail_t(from, split);
	    }
	    else
	    {
		/*
		 * Send mail to addresses from command line args
		 */
		for(cnt = optind; cnt < argc; cnt++)
		    if((status = snd_mail(from, argv[cnt], split)) != EX_OK)
			break;
	    }
	
	if(!b_flag)
	    break;
    }

    pkt_close();
    
    exit(status);

    /**NOT REACHED**/
    return 1;
}
