   /****************************************************************
    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.
    ****************************************************************/

/******************************
 *
 *  DRIVER pour POP IP access
 *
 ******************************/

#include <serv.h>

#include <fbb_drv.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>

#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <linux/if_ether.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#undef open
#undef read
#undef write
#undef close

#define AX25_CALLSID 10
#define READ_EVENT 1
#define WRITE_EVENT 2
#define EXCEPT_EVENT 4
#define QUEUE_EVENT 8

#define DISCONNECT  0
#define CPROGRESS   1
#define WAITINGUSER 2
#define WAITINGPASS 3
#define CONNECTED   4
#define MESSAGE		5


#define DISC_EVENT  1
#define CONN_EVENT  2
#define RETR_EVENT  4
#define BUSY_EVENT  8
#define TIME_EVENT  16

#define TCP_MAXCAN 50

typedef struct
{
	long mess_num;
	long mess_size;
	int  nb_lines;
	char mess_stat;
	char mess_del;
}
tmess_t;

typedef struct
{
	int cr;
	int ncan;
	int sock;
	int port;
	int state;
	int paclen;
	int maxframe;
	int event;
	int queue;
	int lpos;
	int lqueue;
	int nb_try;
	int nb_ret;
	int lgcall;
	int mess_nb;
	int mess_cur;
	long mess_tot;
	long timeout;
	char call[80];
	indicat callsign;
	char *lbuf;
	char *lsend;
	tmess_t *mess;
}
tcan_t;

typedef struct
{
	int rem_port;
	int curcan;
	int nbcan;
	char rem_addr[80];
	tcan_t *tcan;
}
tport_t;

static tport_t tport[NBPORT];

static int stop_cnx (int port);
static int s_free (tcan_t *);
static int s_status (tcan_t *);
static void clear_can (int port, int canal);
static int pop_paclen (int port, int);
static int pop_getline (int port, int can, char *buffer);
static int pop_snd_dt (int port, int, char *, int);
static int pop_cmd (int, int, char *);
static int pop_stat (int, int, stat_ch *);
static int pop_check_call (int port, int can, char *callsign, char *address);
static int pop_check_pass (int port, int can, char *callsign);
static int pop_list (int port, int canal, int num);
static int pop_send(int port, int canal, char *fmt, ...);
static int pop_delete(int port, int canal);

static char *INVALID_CMD = "-ERR Invalid command; valid commands:";

/*
 * Fonctions g‚n‚riques du driver
 */


int snd_pop (int port, int canal, int cmd, char *buffer, int len, Beacon * ptr)
{
	switch (cmd)
	{
	case DATA:
		return pop_snd_dt (port, canal, buffer, len);
	}
	return 0;
}

/* receives data */
int rcv_pop (int *port, int *canal, int *cmd, char *buffer, int *len, ui_header * ui)
{
#define LGBUF 252
	char buf[LGBUF + 2];
	int can;
	int valid;
	int res;

	*cmd = INVCMD;

	valid = 0;

	/* Teste s'il y a une connection */
	tport[*port].tcan[0].sock = p_port[*port].fd;
	res = s_status (&tport[*port].tcan[0]);

	if (res & READ_EVENT)
	{
		int new;
		int i;
		int addr_len;
		struct sockaddr_in sock_addr;
		addr_len = sizeof (sock_addr);

		new = accept (p_port[*port].fd, (struct sockaddr *) &sock_addr, &addr_len);

		/* Affecter le nouveau socket a un canal vide */
		for (i = 1; i <= tport[*port].nbcan; i++)
		{
			if (tport[*port].tcan[i].state == DISCONNECT)
			{
				break;
			}
		}

		if (i > tport[*port].nbcan)
		{
			/* Impossible d'affecter le canal -> deconnexion */
			sprintf (buf, "-ERR FBB POP3 server at %s - No free channel!\r\n", my_call);
			write (new, buf, strlen (buf));
			close (new);
		}
		else
		{
			int val = 0;

			tport[*port].tcan[i].state = WAITINGUSER;
			tport[*port].tcan[i].sock = new;
			tport[*port].tcan[i].paclen = (val == 0) ? 250 : val;
			tport[*port].tcan[i].queue = s_free (&tport[*port].tcan[i]);
			tport[*port].tcan[i].timeout = time (NULL) + 120L;

			sprintf (buf, "+OK FBB POP3 server ready at %s.\r\n", my_call);
			write (new, buf, strlen (buf));

			val = p_port[*port].pk_t;

			return (FALSE);
		}
	}

	/* Passe au canal suivant pour le polling */
	++tport[*port].curcan;
	if (tport[*port].curcan > tport[*port].nbcan)
		tport[*port].curcan = 1;
		
	can = tport[*port].curcan;

	if (tport[*port].tcan[can].lsend)
	{
		strcpy(buffer, tport[*port].tcan[can].lsend);
		*len = strlen(tport[*port].tcan[can].lsend);
		*cmd = DATA;
		*canal = can;
		
		free (tport[*port].tcan[can].lsend);
		tport[*port].tcan[can].lsend = NULL;
		return (TRUE);
	}
	
	if ((tport[*port].tcan[can].sock == -1) && (tport[*port].tcan[can].state != DISCONNECT))
	{
		sprintf (buffer, "(%d) DISCONNECTED fm TCP", can);
		tport[*port].tcan[can].state = DISCONNECT;
		clear_can (*port, can);
		*len = strlen (buffer);
		*cmd = COMMAND;
		*canal = can;
		return (TRUE);
	}

	/* Canal de communication */
	res = s_status (&tport[*port].tcan[can]);

	if (res & TIME_EVENT)
	{
		pop_send(*port, can, "-ERR time-out %s POP3 Server shutdown.\r\n", my_call);
		close (tport[*port].tcan[can].sock);
		clear_can (*port, can);
		return (FALSE);
	}

	if (res & WRITE_EVENT)
	{
		/* Can write to the socket... Unused */
	}

	if (res & EXCEPT_EVENT)
	{
	}

#define LGTCP 1100

	if ((res & QUEUE_EVENT) || (res & READ_EVENT))
	{
		int nb = 0;
		int i;

		if (tport[*port].tcan[can].sock == -1)
		{
			printf ("read on invalid socket\n");
			return (FALSE);
		}

		/* Alloue le buffer si necessaire */
		if (tport[*port].tcan[can].lbuf == NULL)
		{
			tport[*port].tcan[can].lbuf = calloc (LGTCP, 1);
			tport[*port].tcan[can].lpos = 0;
			tport[*port].tcan[can].lqueue = 0;
			tport[*port].tcan[can].nb_ret = 0;
		}

		if (res & READ_EVENT)
		{
			/* Reste de la place ds le buffer ? */
			nb = ((LGTCP - tport[*port].tcan[can].lqueue) > 256) ? 256 : LGTCP - tport[*port].tcan[can].lqueue;
			if (nb)
			{
				nb = read (tport[*port].tcan[can].sock, buffer, nb);
				if ((nb == 0) || ((nb == -1) && (errno == ENOTCONN)))
				{
					/* Deconnection */
					sprintf (buffer, "(%d) DISCONNECTED fm TCP", can);
					close (tport[*port].tcan[can].sock);
					clear_can (*port, can);
					*len = strlen (buffer);
					*cmd = COMMAND;
					*canal = can;
					return (TRUE);
				}
				else if (nb == -1)
				{
					printf ("errno = %d\n", errno);
					perror ("read");
					return (FALSE);
				}
			}
		}

		{
			int process;
			int pos;
			char *ptr;
			char *address;
			int addr_len;
			struct sockaddr_in sock_addr;

			if (nb == 0)
				return (FALSE);

			pos = tport[*port].tcan[can].lpos + tport[*port].tcan[can].lqueue;
			if (pos > LGTCP)
				pos -= LGTCP;

			for (i = 0; i < nb; i++)
			{
				if (tport[*port].tcan[can].lqueue > (LGTCP - 10))
				{
					++tport[*port].tcan[can].nb_ret;
					break;
				}

				tport[*port].tcan[can].lbuf[pos] = buffer[i];
				if (++pos == LGTCP)
					pos = 0;

				++tport[*port].tcan[can].lqueue;

				if (buffer[i] == '\r')
				{
					++tport[*port].tcan[can].nb_ret;
				}
			}

			nb = tport[*port].tcan[can].lqueue;

			tport[*port].tcan[can].timeout = time (NULL) + 120L;
			process = (tport[*port].tcan[can].nb_ret > 0);

			switch (tport[*port].tcan[can].state)
			{
			case WAITINGUSER:
				if (!process)
					return (FALSE);

				if (!pop_getline (*port, can, buffer))
					return 0;

				sup_ln (strupr (buffer));

				if (strncmpi(buffer, "QUIT", 4) == 0)
				{
					/* Received "QUIT" : disconnect */
					pop_send(*port, can, "+OK %s POP3 Server shutdown.\r\n", my_call);
					close (tport[*port].tcan[can].sock);
					tport[*port].tcan[can].sock = -1;
					
				}
				else if (strncmpi(buffer, "USER", 4) == 0)
				{
					char *ptr = buffer + 4;

					while (isspace(*ptr))
						++ptr;
						
					strn_cpy(6, tport[*port].tcan[can].callsign.call, ptr);
					tport[*port].tcan[can].state = WAITINGPASS;
					
					pop_send(*port, can, "+OK please send PASS command\r\n");
				}
				else
				{
					pop_send(*port, can, "%s  USER,  QUIT\r\n",INVALID_CMD);
					close (tport[*port].tcan[can].sock);
					tport[*port].tcan[can].sock = -1;
				}
				
				break;

			case WAITINGPASS:
				if (!process)
					return (FALSE);

				if (!pop_getline (*port, can, buffer))
					return 0;

				sup_ln (buffer);
				if (strncmpi(buffer, "QUIT", 4) == 0)
				{
					/* Received "QUIT" : disconnect */
					pop_send(*port, can, "+OK %s POP3 Server shutdown.\r\n", my_call);
					close (tport[*port].tcan[can].sock);
					tport[*port].tcan[can].sock = -1;
					
				}
				else if (strncmpi(buffer, "PASS", 4) == 0)
				{
					int ok = 0;
					char *ptr = buffer + 4;
					char *callsign = tport[*port].tcan[can].callsign.call;

					while (isspace(*ptr))
						++ptr;
						
					addr_len = sizeof (sock_addr);
					if ((getpeername(tport[*port].tcan[can].sock, (struct sockaddr *)&sock_addr, &addr_len) == 0) && (sock_addr.sin_family == AF_INET))
						address = inet_ntoa(sock_addr.sin_addr);
					else
						address = NULL;

					// Check login
					switch (pop_check_call (*port, can, callsign, address))
					{
					case 1:				
						// Check password
						if (pop_check_pass (*port, can, ptr))
						{
							sprintf (buffer, "(%d) CONNECTED to %s-%d",
									 can, tport[*port].tcan[can].callsign.call,
									 tport[*port].tcan[can].callsign.num);
							tport[*port].tcan[can].state = CONNECTED;
							tport[*port].tcan[can].nb_try = 0;
							tport[*port].tcan[can].timeout = 0L;
							*len = strlen (buffer);
							*cmd = COMMAND;
							*canal = can;

							return (TRUE);
						}
						break;
					}
					if (ok == 0)
					{
						pop_send(*port, can, "-ERR invalid usercode or password, please try again\r\n");
						close (tport[*port].tcan[can].sock);
						tport[*port].tcan[can].sock = -1;
					}
				}
				else
				{
					pop_send(*port, can, "%s  PASS,  QUIT\r\n",INVALID_CMD);
					close (tport[*port].tcan[can].sock);
					tport[*port].tcan[can].sock = -1;
				}
				return (FALSE);

			case CONNECTED:
				if (!process)
					return (FALSE);

				if (!pop_getline (*port, can, buffer))
					return 0;

				sup_ln (strupr (buffer));

				if (strncmpi(buffer, "QUIT", 4) == 0)
				{
					/* Received "QUIT" : disconnect */
					pop_send(*port, can,"+OK %s POP3 Server shutdown.\r\n", my_call);
					pop_delete(*port, can);
					close (tport[*port].tcan[can].sock);
					tport[*port].tcan[can].sock = -1;
				}
				else if (strncmpi(buffer, "LIST", 4) == 0)
				{
					int nb;
					char *ptr = buffer + 4;

					while (isspace(*ptr))
						++ptr;
						
					if (*ptr == '\0')
						nb = -1;
					else
						nb = atoi(ptr);
						
					pop_list(*port, can, nb);
				}
				else if (strncmpi(buffer, "LAST", 4) == 0)
				{
					int i, max = 0;
					
					for (i = 0 ; i < tport[*port].tcan[can].mess_nb ; i++)
						if (tport[*port].tcan[can].mess[i].mess_stat != 'N')
							max = i+1;
					pop_send(*port, can, "+OK %d\r\n", max);
				}
				else if (strncmpi(buffer, "DELE", 4) == 0)
				{
					char *ptr = buffer + 4;

					while (isspace(*ptr))
						++ptr;
						
					if (*ptr == '\0')
					{
						pop_send(*port, can, "-ERR message number required (e.g.  DELE 1)\r\n");
					}
					else
					{
						int nb = atoi(ptr);
						if (nb < 1 || nb > tport[*port].tcan[can].mess_nb)
						{
							pop_send(*port, can, "-ERR invalid message; number out of range\r\n");
						}
						else
						{
							tport[*port].tcan[can].mess[nb-1].mess_del = 1;
							pop_send(*port, can, "+OK message %d marked for deletion\r\n", nb);
						}
					}
				}
				else if (strncmpi(buffer, "NOOP", 4) == 0)
				{
					pop_send(*port, can, "+OK\r\n");
				}
				else if (strncmpi(buffer, "RETR", 4) == 0)
				{
					char *ptr = buffer + 4;

					while (isspace(*ptr))
						++ptr;
						
					if (*ptr == '\0')
					{
						pop_send(*port, can, "-ERR message number required (e.g.  TOP 1 7)\r\n");
					}
					else
					{
						int nb = atoi(ptr);
												
						if (nb < 1 || nb > tport[*port].tcan[can].mess_nb)
						{
							pop_send(*port, can, "-ERR invalid message; number out of range\r\n");
						}
						else if (tport[*port].tcan[can].mess[nb-1].mess_del)
						{
							pop_send(*port, can, "-ERR message %d has been marked for deletion\r\n", nb);
						}
						else
						{
							char buf[80];
							
							tport[*port].tcan[can].mess[nb-1].nb_lines = -1;
							tport[*port].tcan[can].mess_cur = nb-1;
							tport[*port].tcan[can].cr = 1;

							pop_send(*port, can, "+OK message %d (%ld bytes)\r\n", nb, tport[*port].tcan[can].mess[nb-1].mess_size);
							tport[*port].tcan[can].mess[nb-1].mess_stat = 'Y';
							tport[*port].tcan[can].state = MESSAGE;

							sprintf(buf, "R %ld\r", tport[*port].tcan[can].mess[nb-1].mess_num);
							if (tport[*port].tcan[can].lsend)
								free (tport[*port].tcan[can].lsend);
							tport[*port].tcan[can].lsend = strdup(buf);
						}
					}
				}
				else if (strncmpi(buffer, "RSET", 4) == 0)
				{
					int i ;
					for (i = 0 ; i < tport[*port].tcan[can].mess_nb ; i++)
						tport[*port].tcan[can].mess[i].mess_del = 0;
					pop_send(*port, can, "+OK %d messages %ld bytes\r\n", tport[*port].tcan[can].mess_nb, tport[*port].tcan[can].mess_tot);
				}
				else if (strncmpi(buffer, "STAT", 4) == 0)
				{
					pop_send(*port, can, "+OK %d %ld\r\n", tport[*port].tcan[can].mess_nb, tport[*port].tcan[can].mess_tot);
				}
				else if (strncmpi(buffer, "TOP", 3) == 0)
				{
					char *ptr = buffer + 3;

					while (isspace(*ptr))
						++ptr;
						
					if (*ptr == '\0')
					{
						pop_send(*port, can, "-ERR message number and line count required (e.g.  TOP 1 7)\r\n");
					}
					else
					{
						int nb = atoi(ptr);
						while (!isspace(*ptr))
							++ptr;
						while (isspace(*ptr))
							++ptr;
						if (*ptr == '\0')
						{
							pop_send(*port, can, "-ERR line count required (e.g.  TOP 1 7)\r\n");
						}
						else
						{
							int lines = atoi(ptr);
												
							if (nb < 1 || nb > tport[*port].tcan[can].mess_nb)
							{
								pop_send(*port, can, "-ERR invalid message; number out of range\r\n");
							}
							else if (tport[*port].tcan[can].mess[nb-1].mess_del)
							{
								pop_send(*port, can, "-ERR message %d has been marked for deletion\r\n", nb);
							}
							else
							{
								tport[*port].tcan[can].mess[nb-1].nb_lines = lines;
								tport[*port].tcan[can].mess_cur = nb-1;
								tport[*port].tcan[can].cr = 1;

								pop_send(*port, can, "+OK message %d (%ld bytes)\r\n", nb, tport[*port].tcan[can].mess[nb-1].mess_size);
								tport[*port].tcan[can].mess[nb-1].mess_stat = 'Y';
								tport[*port].tcan[can].state = MESSAGE;

								sprintf(buf, "R %ld\r", tport[*port].tcan[can].mess[nb-1].mess_num);
								if (tport[*port].tcan[can].lsend)
								free (tport[*port].tcan[can].lsend);
								tport[*port].tcan[can].lsend = strdup(buf);
							}
						}
					}
				}
				else
				{
					pop_send(*port, can, "%s  DELE, LAST, LIST, NOOP, RETR, RSET, STAT, TOP  or  QUIT\r\n",INVALID_CMD);
					/* close (tport[*port].tcan[can].sock);
					tport[*port].tcan[can].sock = -1; */
				}

			default:
				pos = tport[*port].tcan[can].lpos;
				ptr = tport[*port].tcan[can].lbuf;
				for (i = 0; i < tport[*port].tcan[can].lqueue; i++)
				{
					buffer[i] = ptr[pos];
					if (++pos == LGTCP)
						pos = 0;
				}
				*len = tport[*port].tcan[can].lqueue;
				*cmd = DATA;
				*canal = can;
				tport[*port].tcan[can].nb_ret = 0;
				tport[*port].tcan[can].lpos = 0;
				tport[*port].tcan[can].lqueue = 0;
				tport[*port].tcan[can].timeout = 0L;
				return (TRUE);
			}
		}
	}
	return (FALSE);
}

/* Open port */
int opn_pop (int port, int nb)
{
	int i;
	int val;
	int len;
	int ok = TRUE;
	int tcp_port;
	char s[80];
	struct sockaddr_in sock_addr;
	char *ptr;

	sprintf (s, "Init PORT %d COM%d-%d",
			 port, p_port[port].ccom, p_port[port].ccanal);
	InitText (s);

	sock_addr.sin_family = AF_INET;
	sock_addr.sin_addr.s_addr = 0;

	/* Test if portname is hex number */
	ptr = p_com[(int) p_port[port].ccom].name;

	if (strcmp (ptr, "0") == 0)
	{
		tcp_port = p_com[(int) p_port[port].ccom].port;
	}
	else if (strspn (ptr, "0123456789abcdefABCDEF") != strlen (ptr))
	{
		/* It may be tcp address. Port number is in port */
		if (inet_aton (ptr, &sock_addr.sin_addr))
			tcp_port = p_com[(int) p_port[port].ccom].port;
		else
			tcp_port = p_com[(int) p_port[port].ccom].cbase;
	}
	else
	{
		/* for up compatibility */
		tcp_port = p_com[(int) p_port[port].ccom].cbase;
	}

	sock_addr.sin_port = htons (tcp_port);

	sprintf (s, "Init PORT %d COM%d-%d",
			 port, p_port[port].ccom, p_port[port].ccanal);
	InitText (s);

	/*
	old_can = last_can;
	last_can += nb;

	if (last_can > TCP_MAXCAN)
		last_can = TCP_MAXCAN;
	
	for (i = old_can; i < last_can; i++)
	{
		clear_can (i);
	}
	*/
	
	tport[port].tcan = (tcan_t *)calloc(nb+1, sizeof(tcan_t));
	if (tport[port].tcan == NULL)
		return 0;
		
	tport[port].curcan = 1;
	tport[port].nbcan = nb;
	for (i = 0 ; i <= nb ; i++)
		clear_can(port, i);

	/* Socket reception d'appels */
	if (p_port[port].fd == 0)
	{

		sprintf (s, "Open PORT %d COM%d-%d",
				 port, p_port[port].ccom, p_port[port].ccanal);
		InitText (s);
		sleep (1);

		if ((p_port[port].fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
		{
			perror ("socket_r");
			return (0);
		}

		val = 1;
		len = sizeof (val);
		if (setsockopt (p_port[port].fd, SOL_SOCKET, SO_REUSEADDR, (char *) &val, len) == -1)
		{
			perror ("opn_pop : setsockopt SO_REUSEADDR");
		}

		if (bind (p_port[port].fd, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) != 0)
		{
			perror ("opn_pop : bind");
			close (p_port[port].fd);
			p_port[port].fd = -1;
			return (0);
		}

		if (listen (p_port[port].fd, SOMAXCONN) == -1)
		{
			perror ("listen");
			close (p_port[port].fd);
			p_port[port].fd = -1;
			return (0);
		}

		memset (&tport[port].tcan[0], 0, sizeof (tcan_t));

	}
	
	sprintf (s, "Prog PORT %d COM%d-%d",
			 port, p_port[port].ccom, p_port[port].ccanal);
	InitText (s);

	return (ok);
}

/* Close port */
int cls_pop (int port)
{
	int i;

	for (i = 1; i <= tport[port].nbcan; i++)
	{
		if (tport[port].tcan[i].sock != -1)
		{
			close (tport[port].tcan[i].sock);
		}
		tport[port].tcan[i].state = DISCONNECT;
	}

	if ((p_port[port].typort == TYP_SCK) && (p_port[port].fd))
	{
		close (p_port[port].fd);
		p_port[port].fd = 0;
	}

	free(tport[port].tcan);
	
	return (1);
}

int sta_pop (int port, int canal, int cmd, void *ptr)
{
	switch (cmd)
	{
	case TNCSTAT:
		return (pop_stat (port, canal, (stat_ch *) ptr));
	case PACLEN:
		*((int *) ptr) = pop_paclen (port, canal);
		return (1);
	case SNDCMD:
		return (pop_cmd (port, canal, (char *) ptr));
	case SETBUSY:
		return stop_cnx (port);
	}
	return 0;
}

/********************************************************************/

static int stop_cnx (int port)
{
	close(p_port[port].fd);
	p_port[port].fd = 0;
	return 1;
}

static int pop_check_call (int port, int can, char *callsign, char *address)
{
	int res = 0;
	
	tport[port].tcan[can].callsign.num = extind (callsign, tport[port].tcan[can].callsign.call);
	if (find (tport[port].tcan[can].callsign.call))
	{
		if (chercoord (tport[port].tcan[can].callsign.call) != 0xffff)
			res = 1;
		else
			res = 2;
	}

	// Adresse autorisee sans password ?
	if (address)
	{
		FILE *fptr;
		char str[256];
		char ip[80];
		char pass[256];

		fptr = fopen(c_disque("passwd.sys"), "rt");
		if (fptr)
		{
			while (fgets(str, sizeof(str), fptr))
			{
				if ((*str == '\0') || (*str == '#'))
					continue;
				*ip = *pass = '\0';
				sscanf(str, "%s %s", ip, pass);
				if (strcmp("+", ip) == 0)
				{
					res = 5;
					break;
				}
				if (strcmp(address, ip) == 0)
				{
					if (strcmp("+", pass) == 0)
						res = 5;
					else
						res = 2;
					break;
				}
			}
			fclose(fptr);
		}
	}

	return (res);
}

static int pop_check_pass (int port, int can, char *passwd)
{
	unsigned record;

	record = chercoord (tport[port].tcan[can].callsign.call);

	if (record != 0xffff)
	{
		FILE *fptr;
		info frec;

		fptr = ouvre_nomenc ();
		fseek (fptr, ((long) record * sizeof (info)), 0);
		fread (&frec, sizeof (info), 1, fptr);
		ferme (fptr, 92);

		if (strcasecmp (passwd, frec.pass) == 0)
		{
			return (TRUE);
		}
	}

	return (FALSE);
}

static int pop_snd_dt (int port, int canal, char *buffer, int len)
{
	int i;
	int cr;
	int num;
	char *ptr;
	char buf[600];

	if (tport[port].tcan[canal].sock == -1)
		return (FALSE);

	if (tport[port].tcan[canal].state != MESSAGE)
		return TRUE;

	num = tport[port].tcan[canal].mess_cur;
	cr = tport[port].tcan[canal].cr;
	
	for (ptr = buf, i = 0; i < len; i++)
	{
		if (buffer[i] == '\033')
		{
			pop_send(port, canal, ".\r\n");
			tport[port].tcan[canal].state = CONNECTED;
			break;
		}
		if (cr && buffer[i] == '.')
			*ptr++ = '.';
			
		cr = 0;
		*ptr++ = buffer[i];
		if (buffer[i] == '\r')
		{
			cr = 1;
			*ptr++ = '\n';
			*ptr = '\0';
			pop_send(port, canal, "%s", buf);
			ptr = buf;
			
			if (--tport[port].tcan[canal].mess[num].nb_lines == 0)
			{
				pop_send(port, canal, ".\r\n");
				tport[port].tcan[canal].state = CONNECTED;
				break;
			}
		}
	}
	
	*ptr = '\0';
	if (*buf)
		pop_send(port, canal, "%s", buf);

	tport[port].tcan[canal].cr = cr;
	
	return (TRUE);
}

static int pop_list (int port, int canal, int num)
{
	int i;
	
	pop_send(port, canal, "+OK %d messages (%ld bytes).\r\n",
				tport[port].tcan[canal].mess_nb,
				tport[port].tcan[canal].mess_tot);
	for (i = 0 ; i < tport[port].tcan[canal].mess_nb ; i++)
		pop_send(port, canal, "%ld %ld \r\n", i+1, tport[port].tcan[canal].mess[i].mess_size);

	pop_send(port, canal, ".\r\n");
	return 1;
}

static int pop_send(int port, int canal, char *fmt, ...)
{
	char buf[1024];
	va_list argptr;

	if (tport[port].tcan[canal].sock != -1)
	{
		va_start (argptr, fmt);
		vsprintf (buf, fmt, argptr);
		va_end (argptr);
	
		write (tport[port].tcan[canal].sock, buf, strlen (buf));
		
		return 1;
	}
	return 0;
}

static int pop_cmd (int port, int canal, char *cmd)
{
	int i;
	char status;
	long nb, size;
	
	switch (*cmd++)
	{
	case 'S':
		/* Message information */
		sscanf(cmd, "%ld", &nb);
		tport[port].tcan[canal].mess_nb = nb;
		tport[port].tcan[canal].mess_cur = 0;
		tport[port].tcan[canal].mess_tot = 0L;
		tport[port].tcan[canal].mess = malloc(sizeof(tmess_t) * nb);
		break;
	case 'M':
		/* Message list */
		nb = 0;
		sscanf(cmd, "%ld %ld %c", &nb, &size, &status);
		
		if (nb == 0)
		{
			/* End of list */
			pop_send(port, canal, "+OK connected to %s BBS %d messages (%ld bytes)\r\n", 
					tport[port].tcan[canal].callsign.call,
					tport[port].tcan[canal].mess_nb,
					tport[port].tcan[canal].mess_tot);
		}
		
		i = tport[port].tcan[canal].mess_cur;

		if (i < tport[port].tcan[canal].mess_nb)
		{
			tport[port].tcan[canal].mess_tot += size;
			tport[port].tcan[canal].mess[i].mess_num = nb;
			tport[port].tcan[canal].mess[i].mess_size = size;
			tport[port].tcan[canal].mess[i].mess_stat = status;
			tport[port].tcan[canal].mess[i].mess_del = 0;
			tport[port].tcan[canal].mess_cur = i+1;
		}
		break;
	}
	return (0);
}

static int pop_stat (int port, int canal, stat_ch * ptr)
{
	int val;

	if ((canal == 0) || (tport[port].tcan[canal].sock == -1))
		return (0);

	ptr->mem = 100;

	val = s_free (&tport[port].tcan[canal]);

	if (tport[port].tcan[canal].state != CONNECTED)
		ptr->ack = 0;
	else
	{
		ptr->ack = (tport[port].tcan[canal].queue - val) / tport[port].tcan[canal].paclen;
		if ((tport[port].tcan[canal].queue - val) && (ptr->ack == 0))
			ptr->ack = 1;
	}

	return (1);
}

static int s_status (tcan_t * can)
{
	int nb;
	int res = 0;
	fd_set tcp_read;
	fd_set tcp_write;
	fd_set tcp_excep;
	struct timeval to;

	if (can->sock == -1)
		return (0);

	if ((can->timeout) && (can->timeout < time (NULL)))
	{
		res |= TIME_EVENT;
		can->timeout = 0L;
		return (res);
	}

	if (can->lqueue)
	{
		res |= QUEUE_EVENT;
	}

	to.tv_sec = to.tv_usec = 0;
	can->event = 0;

	FD_ZERO (&tcp_read);
	FD_ZERO (&tcp_write);
	FD_ZERO (&tcp_excep);

	FD_SET (can->sock, &tcp_read);
	FD_SET (can->sock, &tcp_write);
	FD_SET (can->sock, &tcp_excep);

	nb = select (can->sock + 1, &tcp_read, &tcp_write, &tcp_excep, &to);
	if (nb == -1)
	{
		perror ("select");
		return (res);
	}
	else if (nb == 0)
	{
		return (res);
	}
	else
	{
		if (FD_ISSET (can->sock, &tcp_read))
		{
			res |= READ_EVENT;
		}
		if (FD_ISSET (can->sock, &tcp_write))
		{
			res |= WRITE_EVENT;
		}
		if (FD_ISSET (can->sock, &tcp_excep))
		{
			res |= EXCEPT_EVENT;
		}
	}
	return (res);
}

/* Copie une ligne dans le buffer */
static int pop_getline (int port, int can, char *buffer)
{
	int i = 0;
	int c;
	int pos;
	char *ptr;

	pos = tport[port].tcan[can].lpos;
	ptr = tport[port].tcan[can].lbuf;

	while (tport[port].tcan[can].lqueue)
	{
		c = ptr[pos];
		if (++pos == LGTCP)
			pos = 0;

		--tport[port].tcan[can].lqueue;

		if (c != '\n')
			buffer[i++] = c;

		if (c == '\r')
		{
			--tport[port].tcan[can].nb_ret;
			break;
		}
	}

	buffer[i] = '\0';

	tport[port].tcan[can].lpos = pos;

	return (i);
}

static int pop_delete(int port, int canal)
{
	int i;
	char num[80];
	char buf[80];
	
	strcpy(buf, "K");
	for (i = 0 ; i < tport[port].tcan[canal].mess_nb ; i++)
	{
		if (tport[port].tcan[canal].mess[i].mess_del)
		{
			sprintf(num, " %ld", tport[port].tcan[canal].mess[i].mess_num);
			strcat(buf, num);
		}
		if ((i % 4) == 0)
		{
			strcat(buf, "\rK");
		}
	}
	strcat(buf, "\r");

	if (strlen(buf) > 2)
	{
		if (tport[port].tcan[canal].lsend)
		{
			tport[port].tcan[canal].lsend = realloc(tport[port].tcan[canal].lsend, strlen(buf));
			strcat(tport[port].tcan[canal].lsend, buf);
		}
		else
		{
			tport[port].tcan[canal].lsend = strdup(buf);
		}
	}
	return 1;
}

static int pop_paclen (int port, int canal)
{
	if (tport[port].tcan[canal].sock == -1)
		return (0);

	return (tport[port].tcan[canal].paclen);
}

static int s_free (tcan_t * can)
{
	int queue_free;

	if (ioctl (can->sock, TIOCOUTQ, &queue_free) == -1)
	{
		perror ("ioctl : TIOCOUTQ");
		return (0);
	}
	return (queue_free);
}

static void clear_can (int port, int canal)
{
	if (tport[port].tcan[canal].lbuf)
		free (tport[port].tcan[canal].lbuf);
	if (tport[port].tcan[canal].lsend)
		free (tport[port].tcan[canal].lsend);
	if (tport[port].tcan[canal].mess)
		free(tport[port].tcan[canal].mess);
	memset (&tport[port].tcan[canal], 0, sizeof (tcan_t));
	tport[port].tcan[canal].sock = -1;
	tport[port].tcan[canal].state = DISCONNECT;
}
