/*
 * This file is part of the program "zping" and contains the
 * code to finger at workstations to get idle times and other
 * information.
 *
 * Original code from University of California
 *
 *      Created by: Derek Atkins
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <utmp.h>
#include <sys/signal.h>
#include <pwd.h>
#include <stdio.h>
#include <lastlog.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>

#define ASTERISK	'*'		/* ignore this in real name */
#define COMMA		','		/* separator in pw_gecos field */
#define COMMAND		'-'		/* command line flag char */
#define CORY		'C'		/* cory hall office */
#define EVANS		'E'		/* evans hall office */
#define SAMENAME	'&'		/* repeat login name in real name */
#define TALKABLE	0220		/* tty is writable if 220 mode */

struct utmp user;
#define NMAX sizeof(user.ut_name)
#define LMAX sizeof(user.ut_line)
#define HMAX sizeof(user.ut_host)

struct person {			/* one for each person fingered */
	char *name;			/* name */
	char tty[LMAX+1];		/* null terminated tty line */
	char host[HMAX+1];		/* null terminated remote host name */
	long loginat;			/* time of (last) login */
	long idletime;			/* how long idle (if logged in) */
	char *realname;			/* pointer to full name */
	char *office;			/* pointer to office name */
	char *officephone;		/* pointer to office phone no. */
	char *homephone;		/* pointer to home phone no. */
	char *random;			/* for any random stuff in pw_gecos */
	struct passwd *pwd;		/* structure of /etc/passwd stuff */
	char loggedin;			/* person is logged in */
	char writable;			/* tty is writable */
	char original;			/* this is not a duplicate entry */
	struct person *link;		/* link to next person */
};

int unshort;
int lf;					/* LASTLOG file descriptor */
int linenum;				/* number of lines in finger info */
int num_count, p_flag;			/* pointer to line, plan flag */
long int idle_time;			/* the current idle time */
long int small_time;			/* smallest idle time */
struct person *person1;			/* list of people */
long tloc;				/* current time */
char user_name[NMAX];			/* username of person */
char remote_host[HMAX];			/* name of remote host if exists */
char host_name_string[BUFSIZ];		/* name of host given for user */

struct passwd *pwdcopy();
char *strcpy();
char *malloc();
char *ctime();

struct hostent *hp;

typedef struct _names {
    char *name;
    struct _names *next;
} names;

names *namelist;

extern char *whoami;
extern int v_flag;			/* verbose flag set in zping.c */
extern int exactmach;			/* machine name included? */

finger(name)

char *name;
{
    int netfinger();
    
    /* 
     * No 'commands' should come to this procedure, only the name
     * of the person in question.
     */

    if (netfinger(name))
	return(1);
    else
	return(0);

}

netfinger(name)
char *name;
{
    char *host;
    struct servent *sp;
    struct sockaddr_in sin;
    int s;
    char *rindex();
    char line[BUFSIZ];		/* Having problems setting this and passing */
    register FILE *f;
    register int c;
    register int lastc;
    int large = 0, x = 0;
    size_t lnth;

    if ((lnth = strcspn(name, "@")) != 0)
	strncpy(user_name, name, lnth);
    user_name[lnth] = '\0';

    hp = NULL;
    linenum = 0;
    p_flag = 1;
    small_time = -1;

    if (v_flag)
	printf("\n");

    if (name == NULL)
	return (0);
    host = rindex(name, '@');
    if (host == NULL)
	return (0);
    *host++ = 0;
    strcpy (host_name_string, host);
    hp = gethostbyname(host);
    if (hp == NULL) {
	static struct hostent def;
	static struct in_addr defaddr;
	static char *alist[1];
	static char namebuf[128];
	int inet_addr();
	
	defaddr.s_addr = inet_addr(host);
	if (defaddr.s_addr == -1) {	    printf("unknown host: %s\n", host);
	    return (1);
	}
	strcpy(namebuf, host);
	def.h_name = namebuf;
	def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
	def.h_length = sizeof (struct in_addr);
	def.h_addrtype = AF_INET;
	def.h_aliases = 0;
	hp = &def;
    }
    sp = getservbyname("finger", "tcp");
    if (sp == 0) {
	printf("tcp/finger: unknown service\n");
	return (1);
    }
    sin.sin_family = hp->h_addrtype;
    bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
    sin.sin_port = sp->s_port;
    s = socket(hp->h_addrtype, SOCK_STREAM, 0);
    if (s < 0) {
	perror("socket");
	return (1);
    }
/*    printf("[%s]\n", hp->h_name); */
    fflush(stdout);
    if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
	perror("connect");
	close(s);
	return (1);
    }
    if (strcasecmp(host,"salamander.MIT.EDU")) {
	if (large) write(s, "/W ", 3);
	write(s, name, strlen(name));
    } else 
	write (s, "finger", 6);
    write(s, "\r\n", 2);
    f = fdopen(s, "r");
    while ((c = getc(f)) != EOF) {
	switch(c) {
	case 0210:
	case 0211:
	case 0212:
	case 0214:
	    c -= 0200;
	    break;
	case 0215:
	    c = '\n';
	    break;
	}
	lastc = c;
/*	if (isprint(c) || isspace(c)) { */
/*	    putchar(c); */
	    if ((line[x++] = c) == '\n') {
	      line[x++] = '\0';
/*	      printf("%s",line); */
	      num_count = 0;
	      parse_line(line);
	      linenum++;
	      x = 0;
	  } 
/*	} else  { */
/*	    putchar(c ^ 100); */
/*	    if ((line[x++] = c ^ 100) == '\n') {
 *	      line[x] = '\0';
 *	      printf("%s",line);
 *	      linenum++;
 *	      x = 0;
 *	    }
 *	}
 */
    }
    if (lastc != '\n') { 
        line[x++] = '\n';
    }
    line[x++] = '\0';
/*    printf("%s",line); */
/*    printf("%d lines\n",linenum); */
    printf("\t");
    ltimeprint(small_time);
    if ((v_flag || exactmach) && linenum > 1)
	printf(" Idle Time\n");
    (void)fclose(f);
    (void)close(s);
    return (1);
}

parse_line(line)
char line[];

{
    char word[BUFSIZ];
    int result;

    /* parse line from fingerd */
    /* printf("parsing: %s\n", line); */
    while (line[num_count] != '\n') {
	get_word(line, word);
	if ((result = check_word(word)) != 0)
	    find_info(result, line, word);
    }
}

int check_word(word)
char word[];

{
    /* Search 'word' for intersting objects, and
     * will return an integer pertaining to which field
     * this word pertains to...  Maybe a hack?
     */
    int result = 0, i = 0;
    char my_word[BUFSIZ];

    while ((my_word[i] = word[i]) != '\0')
	i++;
    i = 0;

    downcase(my_word);

    if (!strcmp(my_word, "login")) {
/*	printf("Login\n"); */
	result = 1;
    } else if (p_flag == 0) {
	if (!strcmp(my_word, "on")) {
/*	    printf("on\n"); */
	    result = 2;
	} else if (!strcmp(my_word, "idle")) {
/*	    printf("idle\n"); */
	    result = 3;
	} else if (!strcmp(my_word, "from")) {
/*	    printf("from\n"); */
	    result = 4;
	} else if ((strcmp(my_word,"0") >= 0) && (strcmp(my_word, ":") < 0)) {
/*	    printf("digit\n"); */
	    result = 5;
	} else if ((!strcmp(my_word, "plan")) || (!strcmp(my_word, "plan.")) ||
		  (!strcmp(my_word,"plan.\r")) || 
		   (!strcmp(my_word, "project"))) {
/*	    printf("plan or project\n"); */
	    result = 6;
	}
    }

    return(result);
}

find_info(result, line, last_word)
int result;
register char line[];
char last_word[];

{
    int mo = 0;
    char word[BUFSIZ], word_buf[BUFSIZ];
    static char *month[] = { 
	"Jan", "Feb", "Mar", "Apr", "May", 
	"Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 
	};

    if (result == 1) {
	while ((line[num_count] != ':') && (line[num_count] != '\0'))
	    num_count++;
	get_word(line,word);
	if (strcmp(word, user_name))
	    return(1);
	p_flag = idle_time = 0;
/*	printf("Login name = %s\n", word); */
	return(0);
    }
    if (result == 2) {
	get_word(line,word);
	for (mo = 0; (mo <= 11) && strcmp(word,month[mo]); mo++)
	    ;
	if (mo != 12) {
	    get_word(line,word);
	    get_word(line,word);
/*	    printf("Got a time ok...\n"); */
	    return(0);
	} else if (((strcmp(word,"tty") > 0) && (strcmp(word,"ttz") < 0)) ||
		   (!strcmp(word,"console")))
/*	  printf("tty = %s\n",word); */
	    ;
	else 
	  find_info(result, line, word);
    }
    if (result == 3) {
	if (idle_time != 0) {
/*	    printf("Idle time = "); */
	    if (small_time < 0 || idle_time < small_time)
		small_time = idle_time;
	}
    }
    if (result == 4) {
        get_word(line, word);
	if (v_flag && strcasecmp(word, "unix") && *word != '0' &&
	    strlen(word) > 3) {

	    strcpy(host_name_string, hp->h_name);
	    strcpy(word_buf, word);
	    hp = gethostbyname(word_buf);

	    while ((!hp) && 
		   (strlen(word_buf) > 3 )) {
		word_buf[strlen(word_buf) - 1] = '\0';
		hp = gethostbyname(word_buf);
	    }
	    
	    if (hp != NULL && strcasecmp(hp->h_name, host_name_string)) {
		    printf("\ton from remote host %s\n",
			   hp->h_name);
	    }
	    if (hp == NULL) {
		printf("\ton from remote host %s\n",
		       word);
	    }
	    /* Is the pinged user on local or remote login? 
	       and does the caller want the verbose printout? */
	}
    }

    if (result == 5) {
	get_word(line, word);
	downcase(word);
	if ((!strcmp(word, "day")) || (!strcmp(word, "days"))) {
/*	    printf("got a day or more\n"); */
	    idle_time += atol(last_word) * 60 * 60 * 24;
	    }
	if ((!strcmp(word, "hour")) || (!strcmp(word, "hours"))) {
/*	    printf("got an hour or more\n"); */
	    idle_time += atol(last_word) * 60 * 60;
	}
	if ((!strcmp(word, "minute")) || (!strcmp(word, "minutes"))) {
/*	    printf("got a minute or more\n"); */
	    idle_time += atol(last_word) * 60;
	}
	if ((!strcmp(word, "second")) || (!strcmp(word, "seconds"))) {
/*	    printf("got a second or more\n"); */
	    idle_time += atol(last_word);
	}
    }
    if (result == 6) {
	p_flag = 1;
	if (idle_time == -1)
	    small_time = 0;
    }
    return(1);
}


get_word(line, word)
char line[];
char word[];
{
    int i;
    char my_word[BUFSIZ];

    while ((line[num_count] == ' ') || (line[num_count] == '\t') ||
		 (line[num_count] == ':'))
	++num_count;

    for (i = 0; ((line[num_count] != ' ') && (line[num_count] != '\t') &&
		 (line[num_count] != ':') && (line[num_count] != '\n')); )
	my_word[i++] = line[num_count++];
    my_word[i++] = '\0';
    strcpy(word,my_word);
}

downcase(word)
char word[];
{
    int i;

    for (i = 0; word[i] != '\0'; i++)
	if (isupper(word[i]))
	    word[i] = tolower(word[i]);

}

ltimeprint(secs)
long int secs;

{
    long int secs_min = 60, secs_hour = 60 * 60, secs_day = 60 * 60 * 24;
    long int days, hours, mins;
    char time_out[10];

    days = hours = mins = 0;

/*    printf("secs = %d\n", secs); */

    if (secs >= secs_day) {
	days = secs / secs_day;
	secs = secs - secs_day * days;
    }
    if (secs >= secs_hour) {
	hours = secs / secs_hour;
	secs = secs - secs_hour * hours;
    }
    if (secs >= secs_min) {
	mins = secs / secs_min;
	secs = secs - secs_min * mins;
    }
    if (exactmach) {
	printf("  %s:  ", host_name_string);
    }
    if (linenum == 1) {
	printf(" %s not logged on.\n", user_name);
    } else {
	if (v_flag || exactmach) {
	    if (secs != -1) {
		if (days >= 10)
		    printf("%d days", days);
		else if (days > 0)
		    printf("%d day%s %d hour%s",
			   days, days == 1 ? "" : "s",
			   hours, hours == 1 ? "" : "s");
		else if (hours >= 10)
		    printf("%d hours", hours);
		else if (hours > 0)
		    printf("%d hour%s %d minute%s",
			   hours, hours == 1 ? "" : "s",
			   mins, mins == 1 ? "" : "s");
		else if (mins >= 10)
		    printf("%2d minutes", mins);
		else if (mins == 0)
		    printf("%2d seconds", secs);
		else
		    printf("%d minute%s %d second%s",
			   mins, mins == 1 ? "" : "s",
			   secs, secs == 1 ? "" : "s");
	    } else
		if (linenum > 1) {
		    printf("Error getting");
		}
	} else {
	    if (secs != -1) {
		if (days) {
		    sprintf(time_out,"%dd",days);
		} else if (hours) {
		    if (mins < 10)
			sprintf(time_out,"%d:0%d",hours,mins);
		    else
			sprintf(time_out,"%d:%d",hours,mins);
		} else if (mins) {
		    sprintf(time_out,"%d",mins);
		}
	    } else {
		if (linenum > 1) {
		    sprintf(time_out,"idle?");
		}
	    }
	    printf("%+*s",5,time_out);
	}
    }
}

allstation(station)
char *station;

{
    char *host;
    struct servent *sp;
    struct sockaddr_in sin;
    int s;
    char *rindex();
    char line[BUFSIZ];		/* Having problems setting this and passing */
    register FILE *f;
    register int c;
    register int lastc = 0;
    int x = 0;

    namelist = NULL;
    hp = NULL;
    lf = linenum = 0;
    p_flag = 1;
    small_time = -1;

    if (station == NULL)
	return (0);
    host = rindex(station, '@');
    if (host == NULL)
	return (0);
    *host++ = 0;
    hp = gethostbyname(host);
    if (hp == NULL) {
	static struct hostent def;
	static struct in_addr defaddr;
	static char *alist[1];
	static char namebuf[128];
	int inet_addr();
	
	defaddr.s_addr = inet_addr(host);
	if (defaddr.s_addr == -1) {	    printf("unknown host: %s\n", host);
	    return (1);
	}
	strcpy(namebuf, host);
	def.h_name = namebuf;
	def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
	def.h_length = sizeof (struct in_addr);
	def.h_addrtype = AF_INET;
	def.h_aliases = 0;
	hp = &def;
    }
    sp = getservbyname("finger", "tcp");
    if (sp == 0) {
	printf("tcp/finger: unknown service\n");
	return (1);
    }
    sin.sin_family = hp->h_addrtype;
    bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
    sin.sin_port = sp->s_port;
    s = socket(hp->h_addrtype, SOCK_STREAM, 0);
    if (s < 0) {
	perror("socket");
	return (1);
    }
/*    printf("[%s]\n", hp->h_name); */
    fflush(stdout);
    if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
	perror("connect");
	close(s);
	return (1);
    }
    printf("Checking %s. . .\n", hp->h_name);
    write(s, "\r\n", 2);
    f = fdopen(s, "r");
    while ((c = getc(f)) != EOF) {
	switch(c) {
	case 0210:
	case 0211:
	case 0212:
	case 0214:
	    c -= 0200;
	    break;
	case 0215:
	    c = '\n';
	    break;
	}
	lastc = c;
/*	if (isprint(c) || isspace(c)) { */
/*	    putchar(c); */
	    if ((line[x++] = c) == '\n') {
	      line[x++] = '\0';
/*	      printf("%s",line); */
	      num_count = 0;
	      if (x > 3) {
		  get_logins(line);
		  linenum++;
		  x = 0;
	      } else {
		  linenum = 0;
	      }
	    }
      }
    linenum = 0;
    if (lastc != '\n') {
	line[x++] = '\n';
	line[x++] = '\0';
    }
    get_logins(line);
    (void)fclose(f);
    (void)close(s);
    return (1);
}

get_logins(line)
char line[];
{
    char word[BUFSIZ], *buf[2];
    names *new, *temp;

    /* parse line..  If linenum == 0 and the first word != login,
     * then there is SOMETHING wrong, so exit...
     */

    if (linenum == 0) {
	get_word(line, word);
	if (strcasecmp(word, "login")) {
	    new = namelist;
	    while (new != NULL) {
		temp = new;
		strcpy(word, new->name);
		buf[0] = whoami;
		buf[1] = word;
		main(2, buf);
		new = temp->next;
	    }
	    printf("\n");
	    return(1);
	} else {
	    return(0);
	}
    }
    get_word(line, word);
    if (!lf) {
	add_to_list(word);
    } else {
	if (!check_list(word)) {
	    add_to_list(word);
	}
    }
    return(0);
}

add_to_list(word)
char *word;
{
    names *new;
    
    new = (names *) malloc(sizeof(names));
    new->name = malloc(strlen(word)+1);
    strcpy (new->name, word);
    new->next = namelist;
    namelist = new;
    lf++;
}

check_list(word)
char *word;
{
    int x;
    names *new, *buf;
    
    new = namelist;
    for (x = 0; x < lf; x++) {
	if (!strcmp(word, new->name)) {
	    return(1);
	} else {
	    buf = new;
	    new = buf->next;
	}
    }
    return(0);
}


