/*:ts=8*/
/*****************************************************************************
 * FIDOGATE --- Gateway UNIX Mail/News <-> FTN NetMail/EchoMail
 *
 * $Id: outpkt.c,v 3.9.2.0 1995/06/12 17:14:03 mj Exp $
 *
 * Output packet handling for ftntoss and ftroute.
 *
 *****************************************************************************
 * 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"


/*
 * struct for maintaining output packet files
 */
typedef struct st_outpkt
{
    char *tmpname;		/* Temporary name */
    char *outname;		/* Final name */
    Node node;			/* Address */
    char flav;			/* Flavor: N, H, C, D */
    char type;			/* N=NetMail, E=EchoMail */
    char grade;			/* N=Normal, I=Insecure */
    FILE *fp;			/* FILE pointer, if opened */
    long n;			/* Number of messages written to packet */

    struct st_outpkt *next;
} OutPkt;

static OutPkt *outpkt_first = NULL;
static OutPkt *outpkt_last  = NULL;


static int outpkt_maxopen = MAXOPENFILES;	/* Max # */
static int outpkt_nopen   = 0;			/* Currently opened files */



/*
 * Prototypes
 */
static FILE *outpkt_fopen	P((char *, char *));
static int   outpkt_close_ln	P((void));



/*
 * Set number open max open packet files
 */
void outpkt_set_maxopen(n)
    int n;
{
    outpkt_maxopen = n;
}



/*
 * Close packet file with least number of messages
 */
static int outpkt_close_ln()
{
    OutPkt *p, *pmin;
    long min;
    int ret = OK;

    pmin = NULL;
    min  = -1;

    /* Search for least n */
    for(p=outpkt_first; p; p=p->next)
	if(p->fp)
	    if(min==-1 || min>=p->n)
	    {
		pmin = p;
		min  = p->n;
	    }

    /* Close it */
    if(pmin)
    {
	debug(3, "Close %s", pmin->tmpname);
	if(fclose(pmin->fp) == ERROR)
	    ret = ERROR;
	outpkt_nopen--;
	pmin->fp = NULL;
    }
    else
	ret = ERROR;
    
    return ret;
}



/*
 * Open file for output packet. If the max number of open files
 * exceeded, this function closes an old packet file. NULL is
 * returned when this doesn't suceed, either. 
 */
static FILE *outpkt_fopen(name, mode)
    char *name;
    char *mode;
{
    FILE *fp;

    while(outpkt_nopen >= outpkt_maxopen)
	/*
	 * Must close another packet file first
	 */
	if(outpkt_close_ln() == ERROR)
	    return NULL;

    if( (fp = fopen(name, mode)) )
    {
	debug(3, "Open %s, mode %s", name, mode);
	outpkt_nopen++;
	return fp;
    }
    else
	debug(3, "Open %s failed");

    return NULL;
}



/*
 * Return FILE for output packet
 */
FILE *outpkt_open(node, grade, type, flav)
    Node *node;
    int grade;
    int type;
    int flav;
{
    Packet pkt;
    OutPkt *p;
    Passwd *pwd;
    char *s;
    long n;
    
    /*
     * Search for existing packet
     */
    for(p=outpkt_first; p; p=p->next)
	if(node_eq(node, &p->node) &&
	   flav==p->flav && type==p->type && grade==p->grade)
	{
	    p->n++;

	    /*
	     * If file exists, but isn't currently opened, reopen it
	     */
	    if(!p->fp)
		p->fp = outpkt_fopen(p->tmpname, A_MODE);

	    return p->fp;
	}

    /*
     * Doesn't exist, create a new one
     */
    switch(flav)
    {
    case 'n':  s = "out";  break;
    case 'h':  s = "hut";  break;
    case 'c':  s = "cut";  break;
    case 'd':  s = "dut";  break;
    default:   return NULL;  break;
    }

    n = sequencer(SEQ_TOSS) % 1000000L;

    p = (OutPkt *)xmalloc(sizeof(OutPkt));
    p->node  = *node;
    p->flav  = flav;
    p->type  = type;
    p->grade = grade;
    p->n     = 1;
    p->fp    = NULL;
    
    p->next  = NULL;
    
    sprintf(buffer, "%s/%c%c%06ld.%s", pkt_get_outdir(), grade, type, n, s);
    p->outname = strsave(buffer);
    sprintf(buffer, "%s/%c%c%06ld.tmp", pkt_get_outdir(), grade, type, n);
    p->tmpname = strsave(buffer);

    /* Put into linked list */
    if(outpkt_first)
	outpkt_last->next = p;
    else
	outpkt_first      = p;
    outpkt_last       = p;

    /* Open file and write header */
    p->fp = outpkt_fopen(p->tmpname, W_MODE);
    if(p->fp == NULL)
    {
	log("$failed to open packet %s", p->tmpname);
	return NULL;
    }
    
    debug(2, "New packet %s (%s) for %s", p->outname, p->tmpname,
	  node_to_asc(&p->node, TRUE));
    
    pkt.from = cf_n_addr();
    pkt.to   = *node;
    pkt.time = time(NULL);
    /* Password */
    pwd = passwd_lookup("packet", node);
    strncpy0(pkt.passwd, pwd ? pwd->passwd : "", sizeof(pkt.passwd));

    /* Rest is filled in by pkt_put_hdr() */
    if(pkt_put_hdr(p->fp, &pkt) == ERROR)
    {
	log("$Can't write to packet file %s", p->tmpname);
	fclose(p->fp);
	p->fp = NULL;
	return NULL;
    }

    /* o.k., return the FILE */
    return p->fp;
}



/*
 * Close all output packets
 */
int outpkt_close()
{
    OutPkt *p, *pn;
    int ret = OK;
    
    for(p=outpkt_first; p; )
    {
	pn = p->next;

	/* Reopen, if already closed */
	if(!p->fp)
	    p->fp = outpkt_fopen(p->tmpname, A_MODE);

	if(p->fp)
	{
	    /* Terminating 0-word */
	    pkt_put_int16(p->fp, 0);
	    debug(3, "Close %s", p->tmpname);
	    if(fclose(p->fp) == ERROR)
	    {
		debug(3, "Close failed");
		ret = ERROR;
	    }
	    outpkt_nopen--;

	    /* Rename */
	    debug(3, "Rename %s -> %s", p->tmpname, p->outname);
	    if(rename(p->tmpname, p->outname) == ERROR)
	    {
		debug(3, "Rename failed");
		ret = ERROR;
	    }
	}
	else
	    ret = ERROR;
	
	/* Delete OutPkt entry */
	xfree(p->tmpname);
	xfree(p->outname);
	xfree(p);
	
	p = pn;
    }

    outpkt_first = NULL;
    outpkt_last  = NULL;
    outpkt_nopen = 0;			/* Just to be sure ... */
    
    return ret;
}
