/*
 *
 *	Finger support...
 *
 *	Finger server routines.	 Written by Michael T. Horne - KA7AXD.
 *	Copyright 1988 by Michael T. Horne, All Rights Reserved.
 *	Permission granted for non-commercial use and copying, provided
 *	that this notice is retained.
 *	Modified for more memory-reasonable performance by PE1CHL.
 *
 */

#include <stdio.h>
#ifdef MSC
#include <sys/types.h>
#endif
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "internet.h"
#include "icmp.h"
#include "netuser.h"
#include "tcp.h"
#include "finger.h"
#include "session.h"

struct tcb *fing_tcb = NULLTCB;
int16 finger_notify = 0;

int
fing1(argc, argv)
int	argc;
char	*argv[];
{
	extern int32	ip_addr;
	struct socket	lsocket;
	void		fing_state();
	void		rcv_fing();
	char		tos = 0;

	if (fing_tcb != NULLTCB)
		return 1;
	/* start finger daemon */
	lsocket.address = ip_addr;
	if(argc < 2)
		lsocket.port = FINGER_PORT;
	else {
		if ((lsocket.port = atoi(argv[1])) == 0)
			lsocket.port = FINGER_PORT;
		tos = get_tos(argv[2]);
	}
	fing_tcb = open_tcp(&lsocket, NULLSOCK, TCP_SERVER, 0, rcv_fing,
		NULLVFP, fing_state, tos, (int *)NULL);
	finger_notify = (argc > 2);
	return 0;
}
/*
 *	Handle incoming finger connections and closures.
 *
 */
void
fing_state(tcb,old,new)
struct tcb	*tcb;
char		old,		/* old state */
		new;		/* new state */
{
	struct finger	*fing;

	switch(new){
	case ESTABLISHED:
		log_tcp(tcb,"open Finger");
		if ((fing = (struct finger *) calloc(1,sizeof(struct finger))) == NULLFING){
			close_tcp(tcb);
			return;
		}

		tcb->user = (char *)fing;	/* Upward pointer */
		fing->tcb = tcb;		/* Downward pointer */
		if (finger_notify)  {
			printf("\007You're being fingered by %s!\n",
				phsocket(&tcb->conn.remote));
			fflush(stdout);
		}
		return;
	case CLOSED:
		if (tcb == fing_tcb)
			fing_tcb = NULLTCB;
		if ((struct finger *)tcb->user != NULLFING)
			free(tcb->user);
		del_tcp(tcb);
		break;
	}
}

/*
 *	Stop the finger server.
 */

int
fing0()
{
	if (fing_tcb != NULLTCB) {
		close_tcp(fing_tcb);
		fing_tcb = NULLTCB;
	}
	return 0;
}

/*
 *	Finger receive upcall.	This is the guts of the finger server.
 *	The user to finger is read from the socket.  If only a newline
 *	is read, then send the remote host a list of all known 'users' on
 *	this system.
 */

void
rcv_fing(tcb, cnt)
register struct tcb	*tcb;
int16			cnt;
{
	struct finger	*fing;
	struct mbuf	*bp;
	char		*fingerpath,*getenv(),
			who[FINGNAMELEN + 1],
			path[80],
			temp[80],
			user[80];
	int		size;
	void xmt_fing();

	if ((fing = (struct finger *) tcb->user) == NULLFING ||	  /* uh oh! */
	    (fingerpath = getenv(FINGERPATH)) == NULLCHAR ||
	    recv_tcp(tcb,&bp,FINGNAMELEN) == 0) {
		close_tcp(tcb);
		return;
	}

	size = pullup(&bp, who, FINGNAMELEN);	/* get 'user' name */
	who[size] = '\0';			/* NUL terminate it */
	free_p(bp);				/* all done with bp */

	if (*who == '\r' || *who == '\n') {	/* give him a user listing */
		int found = 0;

		/* create wildcard path to finger files */
		strcpy(path, fingerpath);
		strcat(path, "*");
		strcat(path, fingersuf);

		send_tcp(tcb,qstring("Known users on this system:\r\n"));
		for (filedir(path, 0, user); user[0] != '\0';
			filedir (path, 1, user))  {
			found++;
			*index(user, '.') = '\0';
			sprintf(temp, "        %s\r\n", user);
			send_tcp(tcb,qstring(temp));
		}
		if (!found)
			send_tcp(tcb,qstring("None!\r\n"));

		close_tcp(tcb);
		return;
	}

	/*
	 *	Create path to user's finger file and see if the
	 *	the file exists.
	 */
	rip(who);
	strcpy(path, fingerpath);
	strcat(path, who);
	strcat(path, fingersuf);

	if ((fing->fp = fopen(path, "r")) == NULL) {
		sprintf(temp, "User %s unknown on this system\r\n",
			      who);
		send_tcp(tcb, qstring(temp));
		close_tcp(tcb);			/* close socket */
		return;
	}

	/* File opened, send first block and leave rest to upcalls */

	tcb->r_upcall = NULLVFP;
	(*(tcb->t_upcall = xmt_fing))(tcb,tcb->cwind);
}

/* Finger transmit upcall.  Send next window-full of file data */

void
xmt_fing(tcb, cnt)
register struct tcb	*tcb;
int16			cnt;
{
	struct finger *fing;
	int sent = 0;
	char temp[256];

	if ((fing = (struct finger *) tcb->user) == NULLFING)	/* uh oh! */
		return;

	if (fing->fp != NULLFILE) {
		while (fgets(temp,sizeof(temp),fing->fp) != NULLCHAR) {
			rip(temp);
			strcat(temp,"\r\n");
			send_tcp(tcb,qstring(temp));
			if ((sent += strlen(temp)) >= cnt)
				return;		/* window full, pause */
		}
		fclose(fing->fp);
		fing->fp = NULLFILE;
	}
	close_tcp(tcb);
}
