/*
 * FINGER.C V1.0-02 - 22-Jan-1991 - tmk
 *
 *			C O P Y R I G H T
 *
 *	Copyright (C) 1991 by Terence M. Kennedy,
 *	All Rights Reserved.
 *
 *	This software is furnished under a license and may be used and
 *	copied  only  in accordance with the terms of such license and
 *	with the  inclusion  of  the  above  copyright  notice.   This
 *	software  or  any  other copies thereof may not be provided or
 *	otherwise made available to any other person.  No title to and
 *	ownership of the software is hereby transferred.
 *
 *	The information in this software is subject to change  without
 *	notice  and should not be  construed as a commitment by either
 *	Terence M. Kennedy or Saint Peter's College.
 */

/*
 *	M O D I F I C A T I O N    H I S T O R Y
 *
 *	VER/ED		EDIT DATE	REASON
 *	T1.0-00		20-Jan-91	Initial coding for field test
 *	V1.0-01		22-Jan-91	That was short and to the point!
 *	V1.0-02		22-Jan-91	Add test for missing space in options,
 *					change location of include files.
 */

#define VERSION "V1.0-02"
#define VERSDAT	"22-Jan-91"
#define OBJECT	"#117"			/* Don't change without a good reason */
#define TIMEOUT	45			/* Likewise */

#include <stdio.h>

/*
 * Network definitions
 */
#include <time.h>
#include <dn.h>
#include <socket.h>
#include <sioctl.h>
#include <errno.h>
#include <derrno.h>

#define	SEQUENCED_PACKET 0
#define	STREAM		 1

extern	int	errno;

/*
 * Code starts here.
 */
int main(argc, argv)
int	argc;
char	*argv[];
{
    char    user[80];			/* Username to finger */
    char    node[80];			/* Nodename to finger */
    char    router[80];			/* Alternate routing node */
    char    options[80] = "";		/* Optional arguments */
    char    fingercmd[255];		/* Actual command we send */
    char    msgbuffer[255];		/* Buffer for returned text */
    int     msglen;			/* Length of valid contents */
    int     atloc;			/* @ in user@node */
    int     optptr;			/* Pointer to next argv[] */
    struct  timeval tmv;		/* Delay interval */
    unsigned long readfds;		/* Socket number for select() */
    int     status;			/* Returned value from select() */
    int     sock_type;			/* Socket mode */
    int     sock;			/* Socket number */
    char    bio[1];			/* For setting socket mode */

/*
 * No arguments?
 */
    if (argc < 2) {
	printf("MS-DOS Finger Client %s - %s\n\n", VERSION, VERSDAT);
	printf("You are the only user, of course.\n");
	exit(1);
    }

/*
 * Asked for help?
 */
    if (strcmp(argv[1], "\/?") == 0) {	/* A little bird told me... */
	printf("Displays information about users on remote computers.\n\n");
	printf("FINGER [username]@hostname [options]\n\n");
	printf("  username  A user specification or wildcard in the remote host's\n");
	printf("            syntax.\n");
	printf("  hostname  The DECnet node name or address of the remote host.\n");
	printf("  options   Command options, passed to the remote host. Use the\n");
	printf("            command FINGER @hostname /HELP for more information.\n\n");
	printf("A default routing host name may be preset in the FINGROUT envi-\n");
	printf("ronment variable. See the documentation for more information.\n");
	exit(1);
    }

/*
 * Must be an actual finger command then...
 */
    strcpy(user, argv[1]);		/* Collect user, node info */
    atloc = strcspn(user, "@");		/* Look for the delimiter */
    if (strchr(user, '@') == 0) {	/* If there wasn't one */
	printf("?FINGER: Must specify nodename: FINGER [username]@nodename\n");
	exit(2);
    }
    user[atloc] = '\0';			/* Chop off the node part */
    if (strcmp(user, ".") == 0)		/* See if using shorthand */
	if (getenv("FINGUSER") != 0)
	    strcpy(user, getenv("FINGUSER"));
    strcpy(node, argv[1]+atloc+1);	/* But save it here */
    if (strchr(node, '/') != 0) {	/* Option in wrong place? */
	printf("?FINGER: Insert space before options\n");
	exit(2);
    }

/*
 * Were there any options?
 */
    optptr = 2;
    while (argc > 2) {
	strcat(options, argv[optptr]);	/* Save 'em for later... */
	strcat(options, " ");		/* Add a delimiter */
	argc--;				/* Now, repeat after me... */
	optptr++;			/* Next? */
    }

/*
 * Try to connect to the specified node or the designated FINGER router.
 */
    printf("[%s", upper(node));
    sock_type = SEQUENCED_PACKET;
    retry:
    if ((sock = dnet_conn(node, OBJECT, sock_type, NULL, 0, NULL, 0)) < 0) {
	if (errno == EADDRNOTAVAIL)
	    errno = EUNRNODNAM;		/* Nice of them to have choices */
	if (getenv("FINGROUT") == 0)
	    strcpy(router, node);	/* Make next test fail */
	else
	    strcpy(router, getenv("FINGROUT"));
	if (strcmp(router, node) != 0 && errno == EUNRNODNAM) {
	    strcat(user, "@");		/* Tack on the node we really want */
	    strcat(user, node);
	    strcpy(node, router);	/* And go try the router */
	    printf(" via routing host %s]\n", upper(node));
	    printf("[%s", upper(node));
	    goto retry;			/* Considered harmful? */
	} else {
	    printf(": link failed]\n\n");
	    if (errno == ENETUNREACH)	/* This one is different */
		printf("?FINGER: DECnet not started\n");
	    else {
		if (errno == EUNRNODNAM)/* Nicer still to have to map it back */
		    errno = EADDRNOTAVAIL;
		nerror("?FINGER");
	    }
	    exit(3);
	}
    }

/*
 * We got a connection. Build and send the command line.
 */
    printf(".DECnet]\n");		/* Say we connected */
    strcpy(fingercmd, "finger ");	/* First part of command line */
    strcat(fingercmd, user);		/* Username (if any) */
    if (strlen(options) !=0) {		/* Add options if present */
	strcat(fingercmd, " ");
	strcat(fingercmd, options);
    }
    strcat(fingercmd, "\r\n");		/* Pad with CRLF */
    if ((send(sock, fingercmd, strlen(fingercmd), 0)) < 0) {
	perror("\n?FINGER: Network send");
	sclose(sock);
	exit(4);
    }

/*
 * Now set the socket to nonblocking mode so we appear responsive.
 */
    bio[0] = 1;
    sioctl(sock, FIONBIO, &bio[0]);

/*
 * Initialize the receive buffer.
 */
    bzero(&msgbuffer, sizeof(msgbuffer));

/*
 * Loop with a read posted until no more data is available.
 */
    while (1) {
	if (dnet_eof(sock) == 1) {
	    sclose(sock); 
	    exit(0); 
        }

/*
 * Any data out there?
 */
	readfds = 1 << sock;
	tmv.tv_sec = TIMEOUT;
	if ((status = select(sock + 1, &readfds, 0, 0, &tmv)) < 0)
	    perror("\n?FINGER: Network select");
        else {
	    if (status == 0) {
		printf("\n?FINGER: Network read: Timed out\n");
		sclose(sock);
		exit(4);
	    }
	}
	if ((msglen = recv(sock, &msgbuffer, sizeof(msgbuffer), 0)) < 0) {
	    if (errno != EWOULDBLOCK) {
		perror("\n?FINGER: Network read");
		break;
	    }
	    else
		continue;
	}
	msgbuffer[msglen] = '\0';	/* Slap on a terminator */
	printf("%s", msgbuffer);
    }

/*
 * Close down the link and exit.
 */
    if (setsockopt(sock, SOL_SOCKET, SO_LINGER, 0, 0) < 0)
	perror("\n?FINGER: Network close");
    sclose(sock);
    exit(0);
}
