   /****************************************************************
    Copyright (C) 1986-2000 by

    F6FBB - Jean-Paul ROUBELAT
    6, rue George Sand
    31120 - Roquettes - France
	jpr@f6fbb.org

    This program 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 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Parts of code have been taken from many other softwares.
    Thanks for the help.
    ****************************************************************/

/* For main module */
#define PUBLIC

#include <serv.h>
#include <locale.h>	/* Added Satoshi Yasuda for NLS */
#include <sys/vfs.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#define NB_INIT_B 11

static int verbose = 0;
static int init_phase = 1;

static void sig_fct (int sig)
{
	int pid, pstatus;

	sig &= 0xff;
	pid = wait (&pstatus);
	signal (sig, sig_fct);

	switch (sig)
	{
	case SIGHUP:
		/* reload system files */
		if (verbose)
			fprintf (stderr, "Update system files\n");
		init_buf_fwd ();
		init_buf_swap ();
		init_buf_rej ();
		init_bbs ();
		break;
	case SIGTERM:
		/* end of session */
		if (verbose)
			fprintf (stderr, "Closing connections\n");
		maintenance ();
		fbb_quit (1);
		break;
	case SIGBUS:
		/* end of session */
		fprintf (stderr, "xfbbd : Bus error\n");
		exit (5);
		break;
	case SIGSEGV:
		/* end of session */
		fprintf (stderr, "xfbbd : Segmentation violation\n");
		exit (5);
		break;
	}
}

static void xfbbd_init (void)
{
	while (step_initialisations (init_phase) == 0)
	{
		/* Next step */
		++init_phase;
	};
}

static char *XVersion (int dat)
{
	static char prodVersion[80];
	char sdate[30];

	if (dat)
		sprintf (sdate, " (%s)", date ());
	else
		*sdate = '\0';

	sprintf (prodVersion, "%s%s", version (), sdate);

	return (prodVersion);
}

void banner (void)
{
	fprintf (stderr,
			 "*********************************************************\n"
			 "* XFBB Linux daemon version %s PID=%d\n"
			 "* Copyright F6FBB 1986-1999. All rights reserved.\n"
			 "*\n"
			 "* This software is in the public domain. It can be copied\n"
			 "* or installed for any use abiding by the laws.\n"
			 "*\n"
			 "* F6FBB (Jean-Paul ROUBELAT) declines any responsibilty\n"
			 "* in the use of XFBB software.\n"
			 "*\n"
			 "* This software is free of charge, but a 100 FF or 20 US$\n"
			 "* (or more) contribution will be appreciated.\n"
#ifdef BETA
			 "*\n"
			 "* This version is only for test purpose.\n"
#endif
			 "*********************************************************\n",
			 XVersion (TRUE), getpid ());
}

void process (void)
{
	FbbMem (0);
	kernel ();
}

#ifdef __ORB__
extern int fbb_orb (char *service, int port);

#endif

int main (int ac, char **av)
{
	int init_mode = 0;
	int orb = 1;
	int port = 3286;
	char *service = NULL;
	int i, ng;

	daemon_mode = 1;
	all_packets = 0;
	setlocale(LC_CTYPE, "");	/* Added Satoshi Yasuda for NLS */

	for (i = 1; i < NSIG; i++)
	{
		signal (i, sig_fct);
	}

	for (ng = 1; ng < ac; ng++)
	{
		if (strcmp (av[ng], "-h") == 0)
		{
			fprintf (stderr, "usage : xfbbd [-v] [-p port | -s service]\n");
			return (0);
		}
		else if (strcmp (av[ng], "-v") == 0)
		{
			verbose = 1;
		}
		else if (strcmp (av[ng], "-a") == 0)
		{
			all_packets = 1;
		}
		else if (strcmp (av[ng], "-i") == 0)
		{
			init_mode = 1;
		}
		else if (strcmp (av[ng], "-p") == 0)
		{
			if ((ng + 1) != ac)
				port = atoi (av[ng + 1]);
		}
		else if (strcmp (av[ng], "-s") == 0)
		{
			if ((ng + 1) != ac)
				service = av[ng + 1];
		}
		else if (strcmp (av[ng], "-n") == 0)
		{
			orb = 0;
		}
	}

	banner ();

	/* Initialisations */
	xfbbd_init ();

	if (init_mode)
	{
		return (2);
	}

#ifdef __ORB__
	if (orb)
		fbb_orb (service, port);
	else
#endif

		fprintf (stderr, "xfbbd ready and running ...\n");

	/* mainloop */
	for (;;)
	{
		process ();
		if (is_idle)
		{
			usleep (10);
		}
		else
		{
			is_idle = 1;
		}
	}
}

char *itoa (int val, char *buffer, int base)
{
	sprintf (buffer, "%d", val);
	return buffer;
}

char *ltoa (long lval, char *buffer, int base)
{
	sprintf (buffer, "%ld", lval);
	return buffer;
}

char *ultoa (unsigned long lval, char *buffer, int base)
{
	sprintf (buffer, "%lu", lval);
	return buffer;
}

void InitText (char *text)
{
	static char *initext[NB_INIT_B] =
	{
		"Reading INIT.SRV",
		"Reading Texts (%s)",
		"Ports configuration (%s)",
		"TNC configuration (%s)",
		"Servers & PG (%s)",
		"Loading BIDs (%s)",
		"Callsigns set-up (%s)",
		"Messages set-up (%s)",
		"WP set-up (%s)",
		"Forward set-up (%s)",
		"BBS set-up (%s)"
	};

	if (!verbose)
		return;

	if (init_phase > NB_INIT_B)
		return;

	printf (initext[init_phase - 1], text);
	putchar ('\r');
	fflush (stdout);

}


void InfoMessage (int temps, char *texte, char *titre)
{
	static char def_titre[80];

	if (!verbose)
		return;

	if (texte == NULL)
		return;

	if (titre == NULL)
		titre = def_titre;
	else
		strcpy (def_titre, titre);

	printf ("%s : %s\n", titre, texte);

}

#include <stdarg.h>
void WinDebug (char *fmt,...)
{
	va_list argptr;
	int cnt;

	if (!verbose)
		return;

	va_start (argptr, fmt);
	cnt = vprintf (fmt, argptr);
	va_end (argptr);
}
/*
   cmd : tableau des commandes a executer
   nb_cmd : nombre de commandes a executer
   mode : REPORT_MODE : attend un fichier log en retour 
   NO_REPORT_MODE : pas de fichier log en retour
   log   : nom du fichier de retour
   xdir  : repertoire dans lequel doivent s'executer les commandes
 */
int call_nbdos (char **cmd, int nb_cmd, int mode, char *log, char *xdir, char *data)
{

	/* Appel DOS */
	int i;
	int ExitCode = 0;

	char *ptr;
	char file[256];
	char buf[256];
	char dir[256];
	char arg[256];

	/* semi-column is forbidden for security reasons */
		
	if (log)
		sprintf (file, " </dev/null >%s 2>&1", back2slash (log));
	else
		sprintf (file, " </dev/null");

	if (xdir)
	{
		ptr = strchr(xdir, ';');
		if (ptr)
			*ptr = '\0';
		
		sprintf (dir, "cd %s ; ", back2slash (xdir));
	}
	else
		*dir = '\0';

	if (data)
	{
		ptr = strchr(data, ';');
		if (ptr)
			*ptr = '\0';
		sprintf (arg, " \"%s\"", data);
	}
	else
		*arg = '\0';

	for (i = 0; i < nb_cmd; i++)
	{
		int retour;

		/* semi-column is forbidden for security reasons */
		ptr = strchr(cmd[i], ';');
		if (ptr)
			*ptr = '\0';

		sprintf (buf, "%s%s%s%s", dir, cmd[i], arg, file);
		printf ("Commande = {%s}\n", buf);

		retour = system (buf);
		ExitCode = retour >> 8;
		printf ("retour   = %x\n", retour);
		if (ExitCode == 127)
			ExitCode = -1;
		printf ("ExitCode = %d\n", ExitCode);
	}
	return (ExitCode);
}

void CompressPosition (int mode, int val, long numero)
{
	static long last_num = 0;

	if ((verbose) && (numero != last_num))
	{
		last_num = numero;
		printf ("%s : msg %ld\n", (mode) ? "Compress" : "Decompress", numero);
	}
}

int filter (char *ligne, char *buffer, int len, char *data, char *xdir)
{
	char deroute[80];
	int retour;

	sprintf (deroute, "%sEXECUTE.xxx", MBINDIR);
	retour = call_nbdos (&ligne, 1, REPORT_MODE, deroute, xdir, data);
	if (retour != -1)
	{
		outfichs (deroute);
	}
	unlink (deroute);
	return (retour);
}

void sysop_call (char *texte)
{
}

void window_write (int numero, char *data, int len, int color, int header)
{
#ifdef __ORB__
	orb_write (numero, data, len, color, header);
#endif
}

void WinMessage (int temps, char *text)
{
	char str[256];
	fd_set sock_read;
	struct timeval to;

	FD_ZERO (&sock_read);
	FD_SET (0, &sock_read);
	to.tv_sec = temps;
	to.tv_usec = 0;

	fprintf (stderr, "Message : %s\n", text);
	if (select (1, &sock_read, NULL, NULL, &to) > 0)
		read (0, str, sizeof (str));
}

void win_status (char *txt)
{
}

#ifdef __ORB__
static int old_priv;
static int old_hold;
static int old_total;

void reset_msgs (void)
{
	old_priv = -1;
	old_hold = -1;
	old_total = -1;
}
#endif

void win_msg_cons (int priv, int hold)
{
#ifdef __ORB__
	unsigned num_indic;
	ind_noeud *noeud;

	if (priv == -1)
	{
		noeud = insnoeud (cons_call.call, &num_indic);
		priv = noeud->nbnew;
	}

	if (old_priv != priv || old_hold != nb_hold || old_total != nbmess)
	{
		old_priv = priv;
		old_hold = nb_hold;
		old_total = nbmess;
		orb_nb_msg (priv, nb_hold, nbmess);
	}
#endif
}

int fbb_list (int update)
{
#ifdef __ORB__
	static char lcnx[MAXVOIES][80];
	static int premier = 0;
	static int prec_con = 0;
	int nb_con = 0;
	int i;
	char buffer[80];

	if ((premier) || (update))
	{
		memset (lcnx, 0, NBVOIES * 80);
		premier = 0;
	}

	for (i = 0; i < NBVOIES; i++)
	{
		int ch;

		if (svoie[i]->sta.connect)
		{
			int ok = 0;
			int nobbs;
			struct tm *sdate;
			unsigned t_cnx;
			Forward *pfwd;
			char bbs[10];
			char call[20];
			int choix = 0;
			int fwd = 0;
			char fwd_char;

			++nb_con;

			/* Test du forward */
			ok = 0;
			*bbs = '\0';
			pfwd = p_port[no_port (i)].listfwd;
			while (pfwd)
			{
				if ((svoie[i]->curfwd) && (pfwd->forward == i))
				{
					nobbs = svoie[i]->bbsfwd;
					choix = (int) svoie[i]->cur_choix;
					strn_cpy (6, bbs, bbs_ptr + (nobbs - 1) * 7);
					ok = 1;
					fwd = 1;
				}
				pfwd = pfwd->suite;
			}

			if ((!ok) && (svoie[i]->mode & F_FOR) && (i != CONSOLE))
			{
				fwd = 2;
				strcpy (bbs, svoie[i]->sta.indicatif.call);
			}

			strlwr (bbs);

			/* Affichage */
			sdate = localtime (&(svoie[i]->debut));
			t_cnx = (unsigned) (time (NULL) - svoie[i]->debut);
			if (i == 1)
				ch = 99;
			else
				ch = (i > 0) ? i - 1 : i;
			if (svoie[i]->sta.indicatif.num)
				sprintf (call, "%s-%d", svoie[i]->sta.indicatif.call, svoie[i]->sta.indicatif.num);
			else
				sprintf (call, "%s", svoie[i]->sta.indicatif.call);

			if (fwd == 1)
				fwd_char = '<';
			else if (fwd == 2)
				fwd_char = '>';
			else
				fwd_char = ' ';

			sprintf (buffer, "%c%02d %-9s %02d:%02d %02d:%02d %2d %3d %c%c%s",
					 fwd_char,
					 ch,
					 call,
					 sdate->tm_hour,
					 sdate->tm_min,
					 t_cnx / 3600,
					 (t_cnx / 60) % 60,
					 svoie[i]->sta.ret,
					 svoie[i]->sta.ack,
					 (choix < 2) ? ' ' : '0' + choix,
					 (choix < 2) ? ' ' : '/',
					 bbs
				);
		}
		else
		{
			if (i == 1)
				ch = 99;
			else
				ch = (i > 0) ? i - 1 : i;
			sprintf (buffer, " %02d", ch);
		}

		if (strcmp (buffer, lcnx[i]) != 0)
		{
			orb_con_list (i, buffer);
			strcpy (lcnx[i], buffer);
		}
	}
	if (nb_con != prec_con)
	{
		prec_con = nb_con;
		orb_con_nb (nb_con);
	}
	return (nb_con);
#else
	return (0);
#endif
}

void FbbStatus (char *callsign, char *texte)
{
}

void user_status (int voie)
{
}

void maj_menu_options (void)
{
}

void CloseFbbWindow (int numero)
{
#ifdef __ORB__
	if (numero == CONSOLE)
	{
		if (v_tell)
		{
			selvoie (v_tell);
			pvoie->seq = v_tell = 0;
			retour_mbl ();

			/*maj_niv (pvoie->sniv1, pvoie->sniv2, pvoie->sniv3);
			   prompt (pvoie->finf.flags, pvoie->niv1); */

			selvoie (CONSOLE);
			maj_niv (0, 0, 0);
			pvoie->sta.connect = FALSE;
		}
		orb_disc ();
	}
#endif
}

int xfbb_edit (void)
{
	return 1;
}

int end_xfbb_edit (void)
{
	return 1;
}

int call_dll (char *cmd, int mode, char *buffer, int len, char *data)
{
	return 1;
}

void AddUserList (char *callsign)
{
}

void AddUserLang (char *lang)
{
}

void AddMessageList (char *number)
{
}

static FILE *p_fptr = NULL;
static int p_pos = 0;
void RequestPendingForward(char *datafile)
{
	p_pos = 0;
	p_fptr = fopen(datafile, "w");
	if (p_fptr)
	{
		fwd_encours();
		fclose(p_fptr);
	}
}

void AddPendingLine (char *call, int priv, int bull, int kb)
{
	if (p_fptr == NULL)
		return;
	if (*call)
		fprintf(p_fptr, "%02d %s %d %d %d\n", p_pos++, call, priv, bull, kb);
	else
		fprintf(p_fptr, "%02d\n", p_pos++);
}

char *StartForward(int numbbs)
{
	static char str[80];
	char ifwd[NBBBS][7];
	char bbs[8];
	int retour;
	int port_fwd;
	
	ch_bbs (1, ifwd);
	if (numbbs < 0 || numbbs >= NBBBS || *ifwd[numbbs] == '\0')
	{
		sprintf(str, "Unknown BBS nb %d", numbbs);
		return str;
	}
	
	strn_cpy(6, bbs, ifwd[numbbs]);
	retour = val_fwd(bbs, &port_fwd, 1);
	if (retour < 0)
	{
		switch (retour)
		{
	    case -1 :
			sprintf(str, "No forwarding channel on port %d", port_fwd);
			break;
	    case -2:
			sprintf(str, "No port affected to %s", bbs);
			break;
	    case -3 :
			sprintf(str, "Unknown BBS %s", bbs);
			break;
	    case -4 :
			sprintf(str, "BBS %s already connected", bbs);
			break;
	    default :
			sprintf(str, "Unknown error code %d", retour);
			break;
		}
	}
	else
	{
		sprintf(str, "Starting forward to %s on port %d", bbs, port_fwd);
	}
	
	return str;
}

char *StopForward(int numbbs)
{
	static char str[80];
	char ifwd[NBBBS][7];
	char bbs[8];
	int port_fwd;
	
	ch_bbs (1, ifwd);
	if (numbbs < 0 || numbbs >= NBBBS || *ifwd[numbbs] == '\0')
	{
		sprintf(str, "Unknown BBS nb %d", numbbs);
		return str;
	}
	
	strn_cpy(6, bbs, ifwd[numbbs]);
	port_fwd = dec_fwd(bbs);
	if (port_fwd < 0)
	{
		switch (port_fwd)
		{
		case -1 :
			sprintf(str, "BBS %s is not forwarding", bbs);
			break;
		case -2 :
			sprintf(str, "Unknown BBS %s", bbs);
			break;
		default :
			sprintf(str, "Unknown error code %d", port_fwd);
			break;
		}
	}
	else
	{
		sprintf(str, "Stopping forward with %s on port %d", bbs, port_fwd);
	}
	
	return str;
}

void window_connect (int numero)
{
}

void ShowError (char *titre, char *info, int lig)
{
	printf ("%s : %s %d\n", titre, info, lig);
}

void fbb_quit (unsigned retour)
{
	sortie_prg ();
	exit (retour);
}

void WinMSleep (unsigned milliseconds)
{
	usleep (milliseconds * 1000);
}

void WinSleep (unsigned seconds)
{
	sleep (seconds);
}

int sel_option (char *texte, int *val)
{
	char str[256];

	for (;;)
	{
		fprintf (stderr, "%s (Y/N) ? ", texte);
		fflush (stdout);

		read (0, str, sizeof (str));
		if ((*str == 'Y') || (*str == 'y'))
		{
			*val = 'y';
			break;
		}
		if ((*str == 'N') || (*str == 'n'))
			break;
	}

	return (*val == 'y');
}

void aff_traite (int voie, int val)
{
}

void bipper (void)
{
	if (!bip)
		return;
}

void music (int stat)
{
	int i, j;
	int pid;
	static int pid_fils = 0;

	if (stat)
	{
		t_tell = 1000;

		pid = fork ();
		if (pid == 0)
		{
			/* fils */
			for (j = 0; j < 5; j++)
			{
				if (!play ("syscall.wav"))
				{
					int fd;
					char c = '\a';

					fd = open ("/dev/console", O_WRONLY);
					if (fd > 0)
					{
						for (i = 0; i < 10; i++)
						{
							write (fd, &c, 1);
							usleep (200000);
						}
						close (fd);
					}
				}
				sleep (10);
			}
			exit (0);
		}
		else
			pid_fils = pid;
	}
	else
	{
		if (pid_fils)
			kill (pid_fils, SIGKILL);
		pid_fils = 0;
		t_tell = -1;
	}
}

void SpoolLine (int voie, int attr, char *data, int lg)
{
}

void window_init (void)
{
}

void set_win_colors (void)
{
}

void window_disconnect (int numero)
{
}

void sysop_end (void)
{
}

void FbbMem (int update)
{
	static long old_avail = 0;
	static time_t old_time = 0;
	static int old_getd = 0;
	static long old_nbmess = -1L;
	static long old_temp = -1L;
	static long old_gMem = -1L;
	static long old_us = 0xffffffffL;

	int chg = 0;
	char texte[80];
	int gMem = nb_ems_pages ();
	long us = mem_alloue;

	time_t new_time = time (NULL);

	if (update)
	{
		old_avail = 0;
		old_time = 0;
		old_getd = 0;
		old_nbmess = -1L;
		old_temp = -1L;
		old_gMem = -1;
		old_us = 0xffffffffL;
	}

	if (operationnel == -1)
		return;

	/* Mise a jour toutes les secondes */
	if (old_time == new_time)
		return;

	old_time = new_time;

	if (us < 0L)
		us = 0L;

	if (us != old_us)
	{
		old_us = us;
		chg = 1;
	}

	if (gMem != old_gMem)
	{
		old_gMem = gMem;
		chg = 1;
	}

	/* Test disque toutes les 10 secondes */
	if (old_getd == 0)
	{
		struct statfs dfree;
		fsid_t fsid;

		if (statfs (DATADIR, &dfree) == 0)
		{
			if (dfree.f_bavail != old_avail)
			{
				sys_disk = dfree.f_bavail * (dfree.f_bsize / 1024L);

				old_avail = dfree.f_bavail;
				memcpy (&fsid, &dfree.f_fsid, sizeof (fsid));
				chg = 1;
			}
		}

		if ((statfs (MBINDIR, &dfree) == 0) &&
			(dfree.f_bavail != old_avail))
			/*      (memcmp(&fsid, &dfree.f_fsid,sizeof(fsid_t)) != 0)) */
		{
			if (dfree.f_bavail != old_temp)
			{
				tmp_disk = dfree.f_bavail * (dfree.f_bsize / 1024L);

				old_temp = dfree.f_bavail;
				chg = 1;
			}
		}
		else
		{
			tmp_disk = sys_disk;
		}


		old_getd = 10;
	}
	else
		--old_getd;

#ifdef __ORB__
	if (chg)
	{
		orb_status (old_us, old_gMem, old_avail, old_temp);
	}
#endif

	if (nbmess != old_nbmess)
	{
		sprintf (texte, ": %ld", nbmess);
		old_nbmess = nbmess;
	}

}

void DisplayResync (int port, int nb)
{
	static int tot_resync = 0;

	if (nb)
	{
		if (nb == 1)
		{
			printf ("Resynchro port %d (total = %d)\n", port, ++tot_resync);
		}
	}
	else
	{
		printf ("Port %d OK\n", port);
	}
}

int connect_tell (void)
{
	char s[256];

	if ((!svoie[CONSOLE]->sta.connect) && (svoie[v_tell]->sta.connect))

	{

		console_on ();

#ifdef ENGLISH
		sprintf (s, "*** Talking with %s (%s) ", svoie[v_tell]->sta.indicatif.call, svoie[v_tell]->finf.prenom);

#else
		sprintf (s, "*** Convers. avec %s (%s)", svoie[v_tell]->sta.indicatif.call, svoie[v_tell]->finf.prenom);

#endif
		selvoie (CONSOLE);
		pvoie->sta.connect = 16;
		pvoie->deconnect = FALSE;
		pvoie->ret = 0;
		pvoie->sid = 0;
		pvoie->pack = 0;
		pvoie->read_only = 0;
		pvoie->vdisk = 2;
		pvoie->xferok = 1;
		pvoie->msg_held = 0;
		pvoie->mess_recu = 1;
		pvoie->mbl = 0;
		init_timout (CONSOLE);
		pvoie->temp3 = 0;
		pvoie->nb_err = 0;
		pvoie->finf.lang = langue[0]->numlang;
		init_langue (voiecur);
		maj_niv (N_MBL, 9, 2);
		outln (s, strlen (s));
		selvoie (v_tell);
		init_langue (voiecur);
		maj_niv (N_MBL, 9, 2);
		texte (T_MBL + 15);
		return 1;
	}
	return 0;
}
