/*****************************************************************************
 *
 * File ..................: mbcico/openport.c
 * Purpose ...............: Fidonet mailer 
 * Last modification date : 07-Aug-2001
 *
 *****************************************************************************
 * Copyright (C) 1997-2001
 *   
 * Michiel Broek		FIDO:	2:280/2802
 * Beekmansbos 10
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS 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.
 *
 * MBSE BBS 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 MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "../config.h"
#include "../lib/libs.h"
#include "../lib/memwatch.h"
#include "../lib/structs.h"
#include "../lib/common.h"
#include "../lib/clcomm.h"
#include "ulock.h"
#include "ttyio.h"
#include "openport.h"


static char	*openedport = NULL, *pname;
static int	need_detach = 1;
extern int	f_flags;
static speed_t	transpeed(int);
int		hanged_up = 0;



void linedrop(int sig)
{
	Syslog('+', "openport: Lost Carrier");
	hanged_up=1;
	return;
}



void sigpipe(int sig)
{
	Syslog('+', "openport: Got SIGPIPE");
	hanged_up=1;
	return;
}



void interrupt(int sig)
{
	Syslog('+', "openport: Got SIGINT");
	signal(SIGINT,interrupt);
	return;
}



int openport(char *port, int speed)
{
	int rc, rc2;
	char *errtty=NULL;
	int fd;
	int outflags;

	Syslog('t', "Try opening port \"%s\" at %d",MBSE_SS(port),speed);
	if (openedport) 
		free(openedport);
	openedport = NULL;
	if (port[0] == '/') 
		openedport = xstrcpy(port);
	else {
		openedport = xstrcpy((char *)"/dev/");
		openedport = xstrcat(openedport, port);
	}
	pname = strrchr(openedport, '/');

	if ((rc = lock(pname))) {
		Syslog('+', "Port %s is locked (rc = %d)", port, rc);
		free(openedport);
		openedport = NULL;
		return rc;
	}

	if (need_detach) {
		fflush(stdin);
		fflush(stdout);
		setbuf(stdin, NULL);
		setbuf(stdout, NULL);
		close(0);
		close(1);

		/*
		 * If we were manual started the error tty must be closed.
		 */
		if ((errtty = ttyname(2))) {
			Syslog('t', "openport: stderr was on \"%s\", closing",errtty);
			fflush(stderr);
			close(2);
		}
	}
	tty_status = 0;
	hanged_up = 0;
	signal(SIGHUP, linedrop);
	signal(SIGPIPE, sigpipe);
	signal(SIGINT, interrupt);
	rc = 0;
	rc2 = 0;

	Syslog('T', "Try open %s", MBSE_SS(openedport));
	if ((fd = open(openedport,O_RDONLY|O_NONBLOCK)) != 0) {
		rc = 1;
		Syslog('+', "$Cannot open \"%s\" as stdin",MBSE_SS(openedport));
		fd = open("/dev/null",O_RDONLY);
	}

	if ((fd = open(openedport,O_WRONLY|O_NONBLOCK)) != 1) {
		rc = 1;
		Syslog('+', "$Cannot open \"%s\" as stdout",MBSE_SS(openedport));
		fd = open("/dev/null",O_WRONLY);
	}

	clearerr(stdin);
	clearerr(stdout);
	if (need_detach) {
#ifdef TIOCSCTTY
		if ((rc2 = ioctl(0,TIOCSCTTY,1L)) < 0) {
			Syslog('t', "$TIOCSCTTY failed rc = %d", rc2);
		}
#endif
		if (errtty) {
			rc = rc || (open(errtty,O_WRONLY) != 2);
		}
		need_detach=0;
	}

	Syslog('T', "after open rc=%d",rc);

	if (rc) 
		Syslog('+', "cannot switch i/o to port \"%s\"",MBSE_SS(openedport));
	else {
		if (tty_raw(speed)) {
			WriteError("$cannot set raw mode for \"%s\"",MBSE_SS(openedport));
			rc=1;
		}

		if (((f_flags = fcntl(0, F_GETFL, 0L)) == -1) || ((outflags = fcntl(1, F_GETFL, 0L)) == -1)) {
			rc = 1;
			WriteError("$GETFL error");
			f_flags = 0;
			outflags = 0;
		} else {
			Syslog('t', "Return to blocking mode");
			f_flags &= ~O_NONBLOCK;
			outflags &= ~O_NONBLOCK;
			if ((fcntl(0, F_SETFL, f_flags) != 0) || (fcntl(1, F_SETFL, outflags) != 0)) {
				rc = 1;
				WriteError("$SETFL error");
			}
		}
		Syslog('T', "File flags: stdin: 0x%04x, stdout: 0x%04x", f_flags,outflags);
	}

	if (rc) 
		closeport();
	else
		SetTTY(port);

	return rc;
}



/*
 * Set port to local, hangup using DTR drop.
 */
void localport(void)
{
	Syslog('t', "Setting port \"%s\" local",MBSE_SS(openedport));
	signal(SIGHUP,SIG_IGN);
	signal(SIGPIPE,SIG_IGN);
	if (isatty(0)) 
		tty_local();
	return;
}



void nolocalport(void)
{
	Syslog('t', "Setting port \"%s\" non-local",MBSE_SS(openedport));
	if (isatty(0)) 
		tty_nolocal();
	return;
}



int rawport(void)
{
	tty_status = 0;
	signal(SIGHUP,linedrop);
	signal(SIGPIPE,sigpipe);

	if (isatty(0)) 
		return tty_raw(0);
	else 
		return 0;
}



int cookedport(void)
{
	signal(SIGHUP,SIG_IGN);
	signal(SIGPIPE,SIG_IGN);
	if (isatty(0)) 
		return tty_cooked();
	else 
		return 0;
}



void closeport(void)
{
	if (openedport == NULL)
		return;

	Syslog('t', "Closing port \"%s\"",MBSE_SS(openedport));
	fflush(stdin);
	fflush(stdout);
	tty_cooked();
	close(0);
	close(1);
	ulock(pname);
	if (openedport) 
		free(openedport);
	openedport = NULL;
	SetTTY((char *)"-");
	return;
}



void sendbrk(void)
{
	Syslog('t', "Send break");
	if (isatty(0))
#if (defined(TIOCSBRK))
	Syslog('t', "TIOCSBRK");
	ioctl(0, TIOCSBRK, 0L);
#elif (defined(TCSBRK))
	Syslog('t', "TCSBRK");
	ioctl(0, TCSBRK, 0L);
#else /* any ideas about BSD? */
	;
#endif
}



char *bstr(speed_t);
char *bstr(speed_t sp)
{
#ifdef CBAUD
	switch(sp & CBAUD) {
#else
	switch(sp) {
#endif
		case 0:		return (char *)"0";
#if defined(B50)
		case B50:	return (char *)"50";
#endif
#if defined(B75)
		case B75:	return (char *)"75";
#endif
#if defined(B110)
		case B110:	return (char *)"110";
#endif
#if defined(B134)
		case B134:	return (char *)"134";
#endif
#if defined(B150)
		case B150:	return (char *)"150";
#endif
#if defined(B200)
		case B200:	return (char *)"200";
#endif
#if defined(B300)
		case B300:	return (char *)"300";
#endif
#if defined(B600)
		case B600:	return (char *)"600";
#endif
#if defined(B1200)
		case B1200:	return (char *)"1.200";
#endif
#if defined(B1800)
		case B1800:	return (char *)"1.800";
#endif
#if defined(B2400)
		case B2400:	return (char *)"2.400";
#endif
#if defined(B4800)
		case B4800:	return (char *)"4.800";
#endif
#if defined(B9600)
		case B9600:	return (char *)"9.600";
#endif
#if defined(B19200)
		case B19200:	return (char *)"19.200";
#endif
#if defined(B38400)
		case B38400:	return (char *)"38.400";
#endif
#if defined(B57600)
		case B57600:	return (char *)"57.600";
#endif
#if defined(B115200)
		case B115200:	return (char *)"115.200";
#endif
#if defined(B230400)
		case B230400:	return (char *)"230.400";
#endif
#if defined(B460800)
		case B460800:	return (char *)"460.800";
#endif
#if defined(B500000)
		case B500000:	return (char *)"500.000";
#endif
#if defined(B576000)
		case B576000:	return (char *)"576.000";
#endif
#if defined(B921600)
		case B921600:	return (char *)"921.600";
#endif
#if defined(B1000000)
		case B1000000:	return (char *)"1.000.000";
#endif
#if defined(B1152000)
		case B1152000:	return (char *)"1.152.000";
#endif
#if defined(B1500000)
		case B1500000:	return (char *)"1.500.000";
#endif
#if defined(B2000000)
		case B2000000:	return (char *)"2.000.000";
#endif
#if defined(B2500000)
		case B2500000:	return (char *)"2.500.000";
#endif
#if defined(B3000000)
		case B3000000:	return (char *)"3.000.000";
#endif
#if defined(B3500000)
		case B3500000:	return (char *)"3.500.000";
#endif
#if defined(B4000000)
		case B4000000:	return (char *)"4.000.000";
#endif
		default:	return (char *)"Unknown";
	}
}



static struct termios savetios;
static struct termios tios;


int tty_raw(int speed)
{
	int	rc;
	speed_t	tspeed, is, os;

	Syslog('t', "Set tty raw");
	tspeed = transpeed(speed);

	if ((rc = tcgetattr(0,&savetios))) {
		WriteError("$tcgetattr(0,save) return %d",rc);
		return rc;
	} else {
		Syslog('T', "savetios.c_iflag=0x%08x",savetios.c_iflag);
		Syslog('T', "savetios.c_oflag=0x%08x",savetios.c_oflag);
		Syslog('T', "savetios.c_cflag=0x%08x",savetios.c_cflag);
		Syslog('T', "savetios.c_lflag=0x%08x",savetios.c_lflag);
		Syslog('T', "savetios.c_cc=\"%s\"",printable(savetios.c_cc,NCCS));
		Syslog('T', "file flags: stdin: 0x%04x, stdout: 0x%04x", fcntl(0,F_GETFL,0L),fcntl(1,F_GETFL,0L));
	}

	tios = savetios;
	tios.c_iflag = 0;
	tios.c_oflag = 0;
	tios.c_cflag &= ~(CSTOPB | PARENB | PARODD);
	tios.c_cflag |= CS8 | CREAD | HUPCL | CLOCAL;
	tios.c_lflag = 0;
	tios.c_cc[VMIN] = 1;
	tios.c_cc[VTIME] = 0;

	if (tspeed) {
		cfsetispeed(&tios,tspeed);
		cfsetospeed(&tios,tspeed);
	}

	if ((rc = tcsetattr(0,TCSADRAIN,&tios)))
		WriteError("$tcsetattr(0,TCSADRAIN,raw) return %d",rc);

	is = cfgetispeed(&tios);
	os = cfgetospeed(&tios);

	return rc;
}



int tty_local(void)
{
	struct termios	Tios;
	tcflag_t	cflag;
	speed_t		ispeed, ospeed;
	int		rc;

	if ((rc = tcgetattr(0,&Tios))) {
		WriteError("$tcgetattr(0,save) return %d",rc);
		return rc;
	}
	Syslog('-', "Dropping DTR");

	cflag = Tios.c_cflag | CLOCAL;

	ispeed = cfgetispeed(&tios);
	ospeed = cfgetospeed(&tios);
	cfsetispeed(&Tios,0);
	cfsetospeed(&Tios,0);
	if ((rc = tcsetattr(0,TCSADRAIN,&Tios)))
		WriteError("$tcsetattr(0,TCSADRAIN,hangup) return %d",rc);

	sleep(1); /* as far as I notice, DTR goes back high on next op. */

	Tios.c_cflag = cflag;
	cfsetispeed(&Tios,ispeed);
	cfsetospeed(&Tios,ospeed);
	if ((rc = tcsetattr(0,TCSADRAIN,&Tios)))
		Syslog('t', "$tcsetattr(0,TCSADRAIN,clocal) return %d",rc);
	return rc;
}



int tty_nolocal(void)
{
	struct termios	Tios;
	int		rc;

	if ((rc = tcgetattr(0,&Tios))) {
		WriteError("$tcgetattr(0,save) return %d",rc);
		return rc;
	}
	Tios.c_cflag &= ~CLOCAL;
	Tios.c_cflag |= CRTSCTS;

	if ((rc = tcsetattr(0,TCSADRAIN,&Tios)))
		Syslog('t', "$tcsetattr(0,TCSADRAIN,clocal) return %d",rc);
	return rc;
}



int tty_cooked(void)
{
	int rc;

	if ((rc = tcsetattr(0,TCSAFLUSH,&savetios)))
		Syslog('t', "$tcsetattr(0,TCSAFLUSH,save) return %d",rc);
	return rc;
}



speed_t transpeed(int speed)
{
	speed_t tspeed;

	switch (speed)
	{
	case 0:		tspeed=0; break;
#if defined(B50)
	case 50:	tspeed=B50; break;
#endif
#if defined(B75)
	case 75:	tspeed=B75; break;
#endif
#if defined(B110)
	case 110:	tspeed=B110; break;
#endif
#if defined(B134)
	case 134:	tspeed=B134; break;
#endif
#if defined(B150)
	case 150:	tspeed=B150; break;
#endif
#if defined(B200)
	case 200:	tspeed=B200; break;
#endif
#if defined(B300)
	case 300:	tspeed=B300; break;
#endif
#if defined(B600)
	case 600:	tspeed=B600; break;
#endif
#if defined(B1200)
	case 1200:	tspeed=B1200; break;
#endif
#if defined(B1800)
	case 1800:	tspeed=B1800; break;
#endif
#if defined(B2400)
	case 2400:	tspeed=B2400; break;
#endif
#if defined(B4800)
	case 4800:	tspeed=B4800; break;
#endif
#if defined(B7200)
	case 7200:	tspeed=B7200; break;
#endif
#if defined(B9600)
	case 9600:	tspeed=B9600; break;
#endif
#if defined(B12000)
	case 12000:	tspeed=B12000; break;
#endif
#if defined(B14400)
	case 14400:	tspeed=B14400; break;
#endif
#if defined(B19200)
	case 19200:	tspeed=B19200; break;
#elif defined(EXTA)
	case 19200:	tspeed=EXTA; break;
#endif
#if defined(B38400)
	case 38400:	tspeed=B38400; break;
#elif defined(EXTB)
	case 38400:	tspeed=EXTB; break;
#endif
#if defined(B57600)
	case 57600:	tspeed=B57600; break;
#endif
#if defined(B115200)
	case 115200:	tspeed=B115200; break;
#endif
#if defined(B230400)
	case 230400:	tspeed=B230400; break;
#endif
#if defined(B460800)
	case 460800:	tspeed=B460800; break;
#endif
#if defined(B500000)
	case 500000:	tspeed=B500000; break;
#endif
#if defined(B576000)
	case 576000:	tspeed=B576000; break;
#endif
#if defined(B921600)
	case 921600:	tspeed=B921600; break;
#endif
#if defined(B1000000)
	case 1000000:	tspeed=B1000000; break;
#endif
#if defined(B1152000)
	case 1152000:	tspeed=B1152000; break;
#endif
#if defined(B1500000)
	case 1500000:	tspeed=B1500000; break;
#endif
#if defined(B2000000)
	case 2000000:	tspeed=B2000000; break;
#endif
#if defined(B2500000)
	case 2500000:	tspeed=B2500000; break;
#endif
#if defined(B3000000)
	case 3000000:	tspeed=B3000000; break;
#endif
#if defined(B3500000)
	case 3500000:	tspeed=B3500000; break;
#endif
#if defined(B4000000)
	case 4000000:	tspeed=B4000000; break;
#endif
	default:	WriteError("requested invalid speed %d",speed);
			tspeed=0; break;
	}

	return tspeed;
}