
/*
 *
 *	Finger support...
 *
 *	Finger client 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.
 *
 */

#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"

extern char	badhost[],
		hostname[];

/*
 *
 *	Open up a socket to a remote (or the local) host on its finger port.
 *
 */

int
dofinger(argc,argv)
int	argc;
char	*argv[];
{
	void		f_state(),
			fingcli_rcv();
	struct session	*s;
	struct tcb	*tcb;
	struct socket	lsocket,
			fsocket;
	struct finger	*finger, *alloc_finger();
	char		*host;

	lsocket.address = ip_addr;
	lsocket.port = lport++;

/*
 *	Extract user/host information.	It can be of the form:
 *
 *	finger user,			# finger local user
 *	finger user@host,		# finger remote user
 *	finger @host			# finger host (give system status)
 *
 */
	if ((finger = alloc_finger()) == NULLFING)
		return(1);

	if ((host = index(argv[1], '@')) == NULLCHAR) {
		fsocket.address = ip_addr;	/* no host, use local */
		if ((finger->user = malloc(strlen(argv[1]) + 3)) == NULLCHAR) {
			free_finger(finger);
			return(1);
		}
		strcpy(finger->user, argv[1]);
		strcat(finger->user, "\015\012");
	}
	else {
		*host++ = '\0';		/* null terminate user name */
		if (*host == '\0') {	/* must specify host */
			printf("%s: no host specified\n", argv[0]);
			free_finger(finger);
			return(-1);
		}
		if ((fsocket.address = resolve(host)) == 0) {
			printf("%s: ", argv[0]);
			printf(badhost, host);
			free_finger(finger);
			return(1);
		}
		if ((finger->user = malloc(strlen(argv[1])+3))==NULLCHAR) {
			free_finger(finger);
			return 1;
		}
		strcpy(finger->user, argv[1]);
		strcat(finger->user, "\015\012");
	}

	fsocket.port = FINGER_PORT;		/* use finger wnp */

	/* Allocate a session descriptor */
	if ((s = newsession()) == NULLSESSION){
		printf("Too many sessions\n");
		free_finger(finger);
		return 1;
	}
	current = s;
	s->cb.finger = finger;
	finger->session = s;

	if (host == NULLCHAR)				/* if no host specified */
		host = hostname;		/* use local host name */
	if ((s->name = malloc(strlen(host)+1)) != NULLCHAR)
		strcpy(s->name, host);

	s->type = FINGER;
	s->parse = (int (*)()) NULL;

	tcb = open_tcp(&lsocket, &fsocket, TCP_ACTIVE, 0,
	 fingcli_rcv, (void (*)()) 0, f_state, 0, (int *) finger);

	finger->tcb = tcb;
	tcb->user = (char *)finger; /* cast into what belonges there */
	go();
	return 0;
}

/*
 *	Allocate a finger structure for the new session
 */
struct finger *
alloc_finger()
{
	struct finger *tmp;

	if ((tmp = (struct finger *) malloc(sizeof(struct finger))) == NULLFING)
		return(NULLFING);
	tmp->session = NULLSESSION;
	tmp->user = (char *) NULL;
	return(tmp);
}

/*
 *	Free a finger structure
 */
int
free_finger(finger)
struct finger *finger;
{
	if (finger != NULLFING) {
		if (finger->session != NULLSESSION)
			freesession(finger->session);
		if (finger->user != (char *) NULL)
			free(finger->user);
		free(finger);
	}
	return 0;
}

/* Finger receiver upcall routine */
void
fingcli_rcv(tcb, cnt)
register struct tcb	*tcb;
int16			cnt;
{
	struct mbuf	*bp;
	char		*buf;

	/* Make sure valid finger session */
	if ((struct finger *) tcb->user == NULLFING) {
		return;
	}

	/* Hold output if we're not the current session */
	if (mode != CONV_MODE || current == NULLSESSION
		|| current->type != FINGER
		|| current->cb.finger != (struct finger *)tcb->user)
		return;

	if (recv_tcp(tcb, &bp, cnt) > 0)
		while (bp != NULLBUF) {
			buf = bp->data;
			while(bp->cnt--) {
				switch (*buf) {
					case '\012':
						break;
					case '\015':
						fputs("\n", stdout);
						break;
#ifdef MSDOS
					case '\032': /* CPMEOF (dont hang doubledos!) */
						break;
#endif
					default:
						fputc(*buf, stdout);
						break;
				}
				if(current->record != NULLFILE)
					fputc(*buf,current->record);
				buf++;
			}
			bp = free_mbuf(bp);
		}
	fflush(stdout);
}

/* State change upcall routine */
void
f_state(tcb,old,new)
register struct tcb	*tcb;
char			old,		/* old state */
			new;		/* new state */
{
	struct finger	*finger;
	char		notify = 0;
	extern char	*tcpstates[];
	extern char	*reasons[];
	extern char	*unreach[];
	extern char	*exceed[];
	struct mbuf	*bp;

	finger = (struct finger *)tcb->user;

	if(current != NULLSESSION && current->type == FINGER)
		notify = 1;

	switch(new){

	case CLOSE_WAIT:
		if(notify)
			printf("%s\n",tcpstates[new]);
		close_tcp(tcb);
		break;

	case CLOSED:	/* finish up */
		if(notify) {
			printf("%s (%s", tcpstates[new], reasons[tcb->reason]);
			if (tcb->reason == NETWORK){
				switch(tcb->type){
				case DEST_UNREACH:
					printf(": %s unreachable",unreach[tcb->code]);
					break;
				case TIME_EXCEED:
					printf(": %s time exceeded",exceed[tcb->code]);
					break;
				}
			}
			printf(")\n");
			cmdmode();
		}
		if(finger != NULLFING)
			free_finger(finger);
		del_tcp(tcb);
		break;
	case ESTABLISHED:
		if (notify) {
			printf("%s\n",tcpstates[new]);
			printf("[%s]\n", current->name);
		}
		bp = qdata(finger->user, (int16) strlen(finger->user));
		send_tcp(tcb, bp);
		break;

	default:
		if(notify)
			printf("%s\n",tcpstates[new]);
		break;
	}
	fflush(stdout);
}

