/*
 * CFINGERD
 * Standard finger handler
 *
 * 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 1, 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.
 */

#include "cfingerd.h"
#include "proto.h"
#ifdef	BSD
#include "getutent.h"
#endif
#ifndef __FreeBSD__
#include <lastlog.h>
#endif /* !__FreeBSD__ */
#include <pwd.h>
#include <sys/types.h>

#include "privs.h"

int columns;
int times_on;

typedef struct {
    char tty[UT_LINESIZE];
    char locale[UT_HOSTSIZE];
    long ip_addr;
    time_t time;
    BOOL writable;
} TTY_FROM;

#define MAX_TTYS	64
TTY_FROM tty_list[MAX_TTYS];
static int uid, nuid, ngid; 

/*
 * DO_COLUMNS
 *
 * This simply sends a carriage return if there was more than one column
 * already displayed on the screen.
 */
void do_columns(void)
{
    if (columns > 1) {
	printf("\n");
	columns = 0;
    }
}

/*
 * CHECK_ILLEGAL
 *
 * Check to make sure that the specified file given for the user 
 * isn't an illegal file.
 */
BOOL check_illegal(char *str, char *user)
{
    struct stat statbuf;

    if ((lstat(str, (struct stat *) &statbuf)) < 0)
	return(FALSE);

    if (!S_ISREG(statbuf.st_mode)) {
	syslog(LOG_NOTICE, "%s's file %s is NOT A REGULAR FILE.",
		user, str);
	return(FALSE);
    }

    return(TRUE);
}

/*
 * SHOW_STUFF
 *
 * This shows data that is parsed correctly to the number of characters
 * specified in t2.  T1 is the actual bit that is checked for.
 */
void show_stuff(char *user, int t1, int t2)
{
    BOOL can_show = FALSE;
    char formatter[80];

    if (local_finger) {
	if (prog_config.local_config_bits1 & t1)
	    can_show = TRUE;
    } else {
	if (prog_config.config_bits1 & t1)
	    can_show = TRUE;
    }

    if (can_show) {
	columns++;
	snprintf(formatter, sizeof(formatter), "%%s%%-%d.%ds", (36 - strlen(prog_config.p_strings[t2])),
		36);
	printf(formatter, prog_config.p_strings[t2], user?user:"");
	do_columns();
    }
}

/*
 * EXIST
 *
 * Check whether the specified filename exists or not.
 */
BOOL exist(char *filename)
{
    FILE *f;

    if (!filename)
	return(FALSE);

    f = fopen(filename, "r");
    if (f) {
	fclose(f);
	return(TRUE);
    } else
	return(FALSE);
}

/*
 * SHOW_PFILE
 *
 * This simply shows specified data for each option that users would
 * normally expect.  (What else was I supposed to say?  :)
 */
void show_pfile(uid_t uid, gid_t gid, int t1, int t2, int t3, char *dir, char *disp, BOOL send_ret)
{
    BOOL can_show = FALSE;
    FILE *file = NULL;

    if (local_finger) {
	if (prog_config.local_config_bits2 & t1)
	    can_show = TRUE;
    } else {
	if (prog_config.config_bits2 & t1)
	    can_show = TRUE;
    }

    if (can_show) {
	char d[128];

	memset(d, 0, 128);
	snprintf(d, sizeof(d), "%s/%s", dir, disp);

	if (!(prog_config.config_bits3 & SHOW_HEADERS_FE)) {
	    printf("%s\n", prog_config.p_strings[t2]);
	    fflush(stdout);
	}

	if (exist(d)) {
	    NOBODY_PRIVS;

	    if (prog_config.config_bits3 & SHOW_HEADERS_FE) {
		printf("%s\n", prog_config.p_strings[t2]);
		fflush(stdout);
	    }

	    if ((file = open_file(d)) != NULL)
		display_file(uid, gid, file);
	} else {
	    if (!(prog_config.config_bits3 & SHOW_HEADERS_FE))
		printf("%s\n", prog_config.p_strings[t3]);
	    else
		send_ret = 0;
	}
    }

    if (send_ret)
	SEND_RAW_RETURN;
}

/*
 * SHOW_PFILE2
 *
 * This simply shows specified data for each option that users would
 * normally expect.  (What else was I supposed to say?  :)
 */
void show_pfile2(uid_t uid, gid_t gid, int t1, int t2, int t3, char *dir, char *disp, BOOL send_ret)
{
    BOOL can_show = FALSE;
    FILE *file = NULL;

    if (local_finger) {
	if (prog_config.local_config_bits3 & t1)
	    can_show = TRUE;
    } else {
	if (prog_config.config_bits3 & t1)
	    can_show = TRUE;
    }

    if (can_show) {
	char d[128];

	snprintf(d, sizeof(d), "%s/%s", dir, disp);

	if (!(prog_config.config_bits3 & SHOW_HEADERS_FE)) {
	    printf("%s\n", prog_config.p_strings[t2]);
	    fflush(stdout);
	}

	if (exist(d)) {
	    NOBODY_PRIVS;

	    if (prog_config.config_bits3 & SHOW_HEADERS_FE) {
		printf("%s\n", prog_config.p_strings[t2]);
		fflush(stdout);
	    }

	    if ((open_file (d)) != NULL)
		display_file(uid, gid, file);
	} else {
	    if (!(prog_config.config_bits3 & SHOW_HEADERS_FE))
		printf("%s\n", prog_config.p_strings[t3]);
	}
    }

    if (send_ret)
	SEND_RAW_RETURN;
}

/*
 * SHOW_LASTTIME_ON
 *
 * This routine simply shows the last time "user" was on.
 */
void show_lasttime_on(char *user)
{
    int fd;
    BOOL found = FALSE, can_show_orig = FALSE;
    char formatted[80];

#ifndef	USE_LASTLOG
    struct utmp ut;
#else
    struct lastlog lastlog;
#endif

    TTY_FROM last_tty;

    NOBODY_PRIVS;
    fd = open(WTMPFILE, O_RDONLY);

#ifndef	USE_LASTLOG
    if (fd != -1) {
	while (!found) {
	    if (read(fd, &ut, sizeof(struct utmp)) != sizeof (struct utmp))
		found = TRUE;

#ifdef	BSD
	    if (!strncmp(user, (char *) ut.ut_name, strlen(user))) {
		last_tty.ip_addr = 0;
#else
	    if (!strncmp(user, (char *) ut.ut_user, strlen(user))) {
		strncpy(last_tty.tty, ut.ut_line, UT_LINESIZE - 1);
		last_tty.tty[UT_LINESIZE - 1] = 0;
#endif
		strncpy(last_tty.locale, ut.ut_host, UT_HOSTSIZE - 1);
		last_tty.locale[UT_HOSTSIZE - 1] = 0;
#ifdef SUNOS
		last_tty.ip_addr = 0;
#else
 		last_tty.ip_addr = ut.ut_addr;
#endif
		last_tty.time = ut.ut_time;
	    }
	}
    }
#else
    if (fd != -1) {
	/* Since in lastlog, you can seek to the user's UID and get info,
	   we do that here. */
	if (lseek(fd, (sizeof(lastlog) * uid), SEEK_SET) == -1) {
	    printf("Seek: %s\n", strerror(errno));
	    fflush(stdout);
	}

	if (read(fd, &lastlog, sizeof(lastlog)) == sizeof(lastlog)) {
	    found = TRUE;
	    strncpy(last_tty.tty, lastlog.ll_line, UT_LINESIZE - 1);
	    last_tty.tty[UT_LINESIZE - 1] = 0;
	    strncpy(last_tty.locale, lastlog.ll_host, UT_HOSTSIZE - 1);
	    last_tty.locale[UT_HOSTSIZE - 1] = 0;
	    last_tty.time = lastlog.ll_time;
	}
    }
#endif

    close(fd);

    if (local_finger) {
	if (prog_config.local_config_bits1 & SHOW_FROM)
	    can_show_orig = TRUE;
    } else {
	if (prog_config.config_bits1 & SHOW_FROM)
	    can_show_orig = TRUE;
    }

    if (last_tty.time > 0L) {
	strftime(formatted, 80, prog_config.ltime_format,
	    localtime(&last_tty.time));
	printf("Last seen %s", (char *) formatted);
    } else {
	printf("This user has never logged in.");
    }

    if ((can_show_orig) && (last_tty.time > 0L))
	printf("%s %s",
	    (strlen((char *) last_tty.locale) > 1) ? " from" : " ",
	    (strlen((char *) last_tty.locale) > 1) ?
		    (char *) last_tty.locale : " ");

    if (!(prog_config.config_bits2 & SHOW_STRICTFMT))
	printf("\n");

    fflush(stdout);
}

/*
 * SHOW_NOT_ONLINE
 *
 * This shows whether or not the user is online.
 */
void show_not_online(char *user)
{
    BOOL can_show = FALSE, can_show_lasttime = FALSE;

    NOBODY_PRIVS;

    if (local_finger) {
	if (prog_config.local_config_bits1 & SHOW_IFON)
	    can_show = TRUE;
    } else {
	if (prog_config.config_bits1 & SHOW_IFON)
	    can_show = TRUE;
    }

    if (local_finger) {
	if (prog_config.local_config_bits1 & SHOW_LTON)
	    can_show_lasttime = TRUE;
    } else {
	if (prog_config.config_bits1 & SHOW_LTON)
	    can_show_lasttime = TRUE;
    }

    if (can_show) {
	if (can_show_lasttime)
	    show_lasttime_on(user);
	else {
	    printf("This user is not currently logged in.\n");
	    fflush(stdout);
	}
    }
}

/*
 * SHOW_IDLE
 *
 * This routine shows how long the user has been idle on the "tty"
 */
void show_idle(char *tty)
{
    struct stat buf;
    time_t cur_time;
    long diff_time;
    int sec, min, hour, day, has;
    char dev_file[80];

    sprintf(dev_file, "/dev/%s", tty);

    USER_PRIVS(nuid, ngid);
    stat((char *) dev_file, &buf);
    NOBODY_PRIVS;

    cur_time = time(NULL);
    diff_time = (long) cur_time - (long) buf.st_mtime;

    sec = min = hour = day = has = 0;

    if (diff_time > 86400)
	day = hour = has = 1;
    else if (diff_time > 3600)
	hour = min = has = 1;
    else if (diff_time > 59)
	min = sec = has = 1;
    else if (diff_time > 0)
	sec = has = 1;

    if (has)
	printf(" Idle ");

    if (day) {
	day = diff_time / 86400;
	diff_time -= day * 86400;
	printf("%d %s ", day, (day > 1) ? "days" : "day");
    }

    if (hour) {
	hour = diff_time / 3600;
	diff_time -= hour * 3600;
	if (prog_config.config_bits2 & SHOW_TIMESTAMP)
	    printf("%d %s ", hour, (hour > 1) ? "hours" : "hour");
	else
	    printf("%02d:", hour);
    } else
	if (!(prog_config.config_bits2 & SHOW_TIMESTAMP) && has)
	    printf("00:");

    if (min) {
	min = diff_time / 60;
	diff_time -= min * 60;
	if (prog_config.config_bits2 & SHOW_TIMESTAMP)
	    printf("%d %s ", min, (min > 1) ? "minutes" : "minute");
	else
	    printf("%02d:", min);
    } else
	if (!(prog_config.config_bits2 & SHOW_TIMESTAMP) && has)
	    printf("00:");

    if (sec) {
	sec = diff_time;
	if (prog_config.config_bits2 & SHOW_TIMESTAMP)
	    printf("%d %s", sec, (sec > 1) ? "seconds" : "second");
	else
	    printf("%02d", sec);
    } else
	if (!(prog_config.config_bits2 & SHOW_TIMESTAMP) && has)
	    printf("00");
}

/*
 * SHOW_TIMES_ON
 *
 * This shows the number of times the user has logged in, and how long the
 * user has been on on each TTY.  Don't ask why it was a void routine.
 */
void show_times_on(void)
{
    int i;
    BOOL can_show = FALSE, can_show_orig = FALSE;

    NOBODY_PRIVS;

    if (local_finger) {
	if (prog_config.local_config_bits1 & SHOW_IFON)
	    can_show = TRUE;
    } else {
	if (prog_config.config_bits1 & SHOW_IFON)
	    can_show = TRUE;
    }

    if (local_finger) {
	if (prog_config.local_config_bits1 & SHOW_FROM)
	    can_show_orig = TRUE;
    } else {
	if (prog_config.config_bits1 & SHOW_FROM)
	    can_show_orig = TRUE;
    }

    if (can_show) {
	if (!(prog_config.config_bits2 & SHOW_STRICTFMT))
	    SEND_RAW_RETURN;

	for (i = 0; (i < times_on) && (i < MAX_TTYS) ; i++) {
	    char formatted[80];

	    strftime(formatted, 80, prog_config.ltime_format,
		    localtime(&tty_list[i].time));
	    printf("On since %s on %s",
		    formatted, (char *) tty_list[i].tty);

	    show_idle((char *) tty_list[i].tty);

	    if (can_show_orig)
		printf("%s %s",
		    (strlen((char *) tty_list[i].locale) > 1) ? " from" : "",
		    (strlen((char *) tty_list[i].locale) > 1) ?
			    (char *) tty_list[i].locale : "");

	    if (!(tty_list[i].writable))
		printf(" [MSG-N]");

	    SEND_RAW_RETURN_NO_FLUSH;
	}
    }

    fflush(stdout);
}

/*
 * SHOW_LOGIN_STATUS
 *
 * This shows the login status of the specified user, showing how many times
 * they are logged in, how long they've been idle, and other necessities.
 */
void show_login_stats(char *user)
{
    struct utmp *ut;

    times_on = 0;

    while((ut = getutent()) != NULL) {
#ifdef	BSD
	if (!strncmp(user, (char *) ut->ut_name, strlen(user)) &&
		(strlen((char *) ut->ut_name) == strlen(user))) {
#else
	if (ut->ut_type == USER_PROCESS)
	    if (!strncmp(user, (char *) ut->ut_user, strlen(user)) &&
		(strlen((char *) ut->ut_user) == strlen(user))) {
#endif
		struct stat buf;
		char devfile[80];

		if (times_on < MAX_TTYS) {
		    strncpy(tty_list[times_on].tty, (char *) ut->ut_line, sizeof(tty_list[times_on].tty));
		    strncpy(tty_list[times_on].tty, (char *) ut->ut_line, UT_LINESIZE - 1);
		    tty_list[times_on].tty[UT_LINESIZE-1] = 0;

		    sprintf(devfile, "/dev/%s", (char *) ut->ut_line);

		    USER_PRIVS(nuid, ngid);
		    stat(devfile, &buf);
		    NOBODY_PRIVS;

#ifdef	HAS_TTY_GROUP
		    if (buf.st_mode & S_IWGRP)
#else
			if ((buf.st_mode & S_IWGRP) && (buf.st_mode & S_IWOTH))
#endif
			    tty_list[times_on].writable = TRUE;
			else
			    tty_list[times_on].writable = FALSE;

#if	defined(BSD) || defined(SUNOS)
		    tty_list[times_on].ip_addr = 0;
#else
		    tty_list[times_on].ip_addr = ut->ut_addr;
#endif

		    strncpy(tty_list[times_on].locale, (char *) ut->ut_host, UT_HOSTSIZE - 1);
		    tty_list[times_on].locale[UT_HOSTSIZE - 1] = 0;
		    tty_list[times_on].time = ut->ut_time;
		}
		times_on++;
	    }
    }

    if (times_on > 0) {
	show_times_on();

	if (!(prog_config.config_bits2 & SHOW_STRICTFMT))
	    SEND_RAW_RETURN;
    } else {
	show_not_online(user);
	if (!(prog_config.config_bits2 & SHOW_STRICTFMT))
	    SEND_RAW_RETURN;
    }

    fflush(stdout);
}

/*
 * SHOW_MAIL_STATS
 *
 * This shows whether the specified user has mail, and how long ago the
 * user read mail.
 */
void show_mail_stats(char *user)
{
    struct stat buf;
    char mailfile[200];
    BOOL can_show = FALSE, can_show_date = FALSE;

    if (local_finger) {
	if (prog_config.local_config_bits1 & SHOW_LRMAIL)
	    can_show = TRUE;
    } else {
	if (prog_config.config_bits1 & SHOW_LRMAIL)
	    can_show = TRUE;
    }

    if (local_finger) {
	if (prog_config.local_config_bits1 & SHOW_MRDATE)
	    can_show_date = TRUE;
    } else {
	if (prog_config.config_bits1 & SHOW_MRDATE)
	    can_show_date = TRUE;
    }

    if (can_show) {
	char formatted[80];
	int fm;
	char *cp, *xp, *yp;
	struct passwd *pwent = NULL;

	/*
	 *  Expand mailbox, $HOME and $USER are supported.
	 */
	memset (mailfile, 0, sizeof(mailfile));
	for (cp=prog_config.mailbox_file,xp=mailfile;*cp&&strlen(mailfile)<sizeof(mailfile)-1;cp++) {
	    if (*cp == '$') {
		cp++;
		if (*cp == '$') {
		    *(xp++) = *cp;
		} else if (!strncasecmp(cp,"USER", 4)) {
		    for (yp=user;*yp&&strlen(mailfile)<sizeof(mailfile)-1;yp++)
			*(xp++) = *yp;
		    cp+=3;
		} else if (!strncasecmp(cp,"HOME", 4)) {
		    if (pwent == NULL) {
			pwent = getpwnam (user);
		    }
		    if (pwent)
			for (yp=pwent->pw_dir;*yp&&strlen(mailfile)<sizeof(mailfile)-1;yp++)
			    *(xp++) = *yp;
		    cp+=3;
		}
	    } else
		*(xp++) = *cp;
	}

	USER_PRIVS(nuid, ngid);
	fm = stat(mailfile, &buf);
	NOBODY_PRIVS;

	if (fm != -1) {
	    if (can_show_date)
		strftime(formatted, 80, prog_config.ltime_format,
		    localtime(&buf.st_atime));
	    else
		strftime(formatted, 80, prog_config.stime_format,
		    localtime(&buf.st_atime));

	    if ((int) buf.st_size == 0)
		printf("This user has no mail.\n");
	    else {
		printf("This user last read mail %s\n", (char *) formatted);
		if (buf.st_mtime > buf.st_atime) {
		    char formatted2[80];

		    strftime(formatted2, 80, prog_config.ltime_format,
			localtime(&buf.st_mtime));
		    printf("This user has unread mail since %s\n",
			(char *) formatted2);
		}
	    }
	} else
	    printf("This user has no mail or mail spool.\n");
    }

}

  /*
  * SHOW_QMAIL_STATS
  *
  * This shows whether the specified user has mail, and how long ago the
  * user read mail.
  */
void show_qmail_stats(char *user, char *homedir, char *alias)
    {
	struct stat newbuf;
	struct stat tmpbuf;
	char mailfile[80];
	BOOL can_show = FALSE, can_show_date = FALSE;
	char *nomailmsg = "This user has no mail or mail spool.\n";
 
	if (local_finger) {
	    if (prog_config.local_config_bits1 & SHOW_LRMAIL)
		can_show = TRUE;
	} else {
	    if (prog_config.config_bits1 & SHOW_LRMAIL)
		can_show = TRUE;
	}
 
	if (local_finger) {
	    if (prog_config.local_config_bits1 & SHOW_MRDATE)
		can_show_date = TRUE;
	} else {
	    if (prog_config.config_bits1 & SHOW_MRDATE)
		can_show_date = TRUE;
	}
 
	if (can_show) {
	    char formatted[80];
	    char line[80];
	    int fm;
	    FILE *qmf = NULL;
 
	    /* open ~/.qmail-alias or ~/.qmail */
	    if(alias != NULL)
		snprintf(mailfile, sizeof(mailfile), "%s/.qmail-%s", homedir, alias);
	    else
		snprintf(mailfile, sizeof(mailfile), "%s/.qmail", homedir);

	    USER_PRIVS(nuid, ngid);
	    qmf = fopen(mailfile, "r");
	    NOBODY_PRIVS;

	    if(qmf == NULL) {
		printf("No .qmail file.\n");
		return;
	    }
	    line[0] = '\0';
 
	    /* We get the first line starting with '.' or '/' */
	    USER_PRIVS(nuid, ngid);
	    while(qmf != NULL && fgets(line, sizeof(line), qmf)) {
		if(line[0] != '.' && line[0] != '/')
		    line[0] = '\0';
		else
		    break;
	    }
	    fclose(qmf);
	    NOBODY_PRIVS;

	    if(line[0] == '\0') {
		printf("No local mail storage.\n");
		return;
	    }
 
	    /* chdir() to home dir for paths relative to ~ */
	    if(chdir(homedir) == -1)
		return;
 
	    if(line[strlen(line) - 1] == '\n')
		line[strlen(line) - 1] = '\0';
 
	    /* If the last char is not '/' then it's a sendmail format mail
	       file and we use the old routines. */
	    if(line[strlen(line) - 1] != '/') {
		USER_PRIVS(nuid, ngid);
		fm = stat(line, &newbuf);
		NOBODY_PRIVS;

		if (fm != -1) {
		    if (can_show_date) {
			strftime(formatted, 80, prog_config.ltime_format,
				 localtime(&newbuf.st_atime));
		    } else {
			strftime(formatted, 80, prog_config.stime_format,
				 localtime(&newbuf.st_atime));
		    }
 
		    if ((int) newbuf.st_size == 0)
			printf("This user has no mail.\n");
		    else {
			printf("This user last read mail %s\n", (char *) formatted);
			if (newbuf.st_mtime > newbuf.st_atime) {
			    char formatted2[80];
 
			    strftime(formatted2, 80, prog_config.ltime_format,
				     localtime(&newbuf.st_mtime));
			    printf("This user has unread mail since %s\n",
				   (char *) formatted2);
			}
		    }
		} else
		    printf(nomailmsg);
	    } else {
		snprintf(mailfile, sizeof(mailfile), "%snew", line);
		USER_PRIVS(nuid, ngid);
		fm = stat(mailfile, &newbuf);
		if(fm == -1) {
		    printf(nomailmsg);
		    return;
		}
		snprintf(mailfile, sizeof(mailfile), "%stmp", line);
		fm = stat(mailfile, &tmpbuf);
		if(fm == -1) {
		    printf(nomailmsg);
		    return;
		}

		NOBODY_PRIVS;
 
		if (can_show_date) {
		    strftime(formatted, 80, prog_config.ltime_format,
			     localtime(&tmpbuf.st_atime));
		} else {
		    strftime(formatted, 80, prog_config.stime_format,
			     localtime(&tmpbuf.st_atime));
		}
 
		if ((int) newbuf.st_size == 0)
		    printf("This user has no mail.\n");
		else {
		    printf("This user last read mail %s\n", (char *) formatted);
		    if (newbuf.st_mtime > tmpbuf.st_atime) {
			char formatted2[80];
 
			strftime(formatted2, 80, prog_config.ltime_format,
				 localtime(&newbuf.st_mtime));
			printf("This user has unread mail since %s\n",
			       (char *) formatted2);
		    }
		}
	    }
	}
    }

/*
 * SHOW_FINGERINFO
 *
 * This shows correct finger information for the specified "user"
 */
void show_fingerinfo(char *user)
{
    struct passwd *pwent;
    FILE *file;
    char *username=NULL, *room=NULL, *work_phone=NULL, *home_phone=NULL, *other=NULL;
    char fn[80];
    BOOL qmail;
    char acctname[100];
    char *alias = NULL;
    char *cp, *x;

    columns = 0;
    qmail = ( strcasecmp(prog_config.mailbox_file, "QMAIL") == 0);

    if(qmail) {
        strncpy(acctname, user, sizeof(acctname) -1);
        acctname[sizeof(acctname) -1] = '\0';
        strtok(acctname, "-");
	alias = strtok(NULL, "-");
        pwent = getpwnam(acctname);
    } else {
        pwent = getpwnam(user);
    }

    /* Do a quick/dirty sanity check. */
    if(pwent == NULL) {
        show_bottom();
        return;
    }

    uid = pwent->pw_uid;

    /* For changing to the correct UID/GID */
    nuid = pwent->pw_uid;
    ngid = pwent->pw_gid;

    cp = pwent->pw_gecos;
    if ((x = index (cp, ',')) != NULL) { /* username */
	if ((username = (char *)malloc (x-cp+1)) != NULL) {
	    memset (username, 0, x-cp+1);
	    strncpy (username, cp, x-cp);
	}
	cp = ++x;
	if ((x = index (cp, ',')) != NULL) { /* room */
	    if ((room = (char *)malloc (x-cp+1)) != NULL) {
		memset (room, 0, x-cp+1);
		strncpy (room, cp, x-cp);
	    }
	    cp = ++x;
	    if ((x = index (cp, ',')) != NULL) { /* work phone */
		if ((work_phone = (char *)malloc (x-cp+1)) != NULL) {
		    memset (work_phone, 0, x-cp+1);
		    strncpy (work_phone, cp, x-cp);
		}
		cp = ++x;
		if ((x = index (cp, ',')) != NULL) { /* home phone */
		    if ((home_phone = (char *)malloc (x-cp+1)) != NULL) {
			memset (home_phone, 0, x-cp+1);
			strncpy (home_phone, cp, x-cp);
		    }
		    cp = ++x;
		    if ((x = index (cp, ',')) != NULL) /* other */
			*x = '\0';
		    if ((other = (char *)malloc (strlen(cp)+1)) != NULL) {
			memset (other, 0, strlen(cp)+1);
			strcpy (other, cp);
		    }
		} else { /* no comma, thus string is home phone */
		    if ((home_phone = (char *)malloc (strlen(cp)+1)) != NULL) {
			memset (home_phone, 0, strlen(cp)+1);
			strcpy (home_phone, cp);
		    }
		}
	    }
	}
    } else { /* no comma, thus whole string is username */
	if ((username = (char *)malloc (strlen(cp)+1)) != NULL) {
	    memset (username, 0, strlen(cp)+1);
	    strcpy (username, cp);
	}
    }

    if ((prog_config.no_finger_file != NULL) 
	&& (strlen(pwent->pw_dir)+strlen(prog_config.no_finger_file)+1 < sizeof(fn)))
        sprintf(fn, "%s/%s", pwent->pw_dir, prog_config.no_finger_file);
    else
      if (strlen(pwent->pw_dir)+10 < sizeof(fn))
	sprintf(fn, "%s/.nofinger", pwent->pw_dir);
      else
	fn[0] = '\0';

    if (exist(fn)) {
	if (!check_illegal((char *) fn, user)) {
	    show_bottom();
	    exit(PROGRAM_SYSLOG);
	}
    }

    USER_PRIVS(nuid, ngid);
    if ((file = fopen(fn, "r")) != NULL) {
	NOBODY_PRIVS;
	display_file(nuid, ngid, file);
    } else {
	NOBODY_PRIVS;

	show_stuff(user, SHOW_UNAME, D_USERNAME);
	show_stuff(username, SHOW_REALNAME, D_REALNAME);
	show_stuff(pwent->pw_dir, SHOW_DIR, D_DIRECTORY);
	show_stuff(pwent->pw_shell, SHOW_SHELL, D_SHELL);
	show_stuff((char *) room, SHOW_ROOM, D_ROOM);
	show_stuff((char *) work_phone, SHOW_WPHONE, D_WORK_PHONE);
	show_stuff((char *) home_phone, SHOW_HPHONE, D_HOME_PHONE);
	show_stuff((char *) other, SHOW_OTHER, D_OTHER);

	if ((prog_config.config_bits3 & SHOW_USERLOG) &&
	    (prog_config.userlog_file != NULL))
	    userlog(pwent->pw_uid, pwent->pw_gid, pwent->pw_dir, ident_user);

	/* For sanity sake, and to make the display look prettier... */
	columns++;
	do_columns();

	if (!(prog_config.config_bits2 & SHOW_STRICTFMT))
	    SEND_RAW_RETURN;

        if(qmail)
            show_qmail_stats(acctname, pwent->pw_dir, alias);
        else
	    show_mail_stats(user);
	show_login_stats(user);

	if (prog_config.config_bits2 & SHOW_STRICTFMT)
	    SEND_RAW_RETURN;

	show_pfile(nuid, ngid, SHOW_PROJECT, D_PROJECT, D_NO_PROJECT, pwent->pw_dir, 
		prog_config.project_file, 
	(prog_config.config_bits2 & SHOW_STRICTFMT) ? FALSE : TRUE);

	show_pfile(nuid, ngid, SHOW_PLAN, D_PLAN, D_NO_PLAN, pwent->pw_dir,
		prog_config.plan_file,
	(prog_config.config_bits2 & SHOW_STRICTFMT) ? FALSE : TRUE);

	/* Thanks to Andy Smith */
	show_pfile(nuid, ngid, SHOW_PGPKEY, D_PGPKEY, D_NO_PGPKEY, pwent->pw_dir, 
		prog_config.pgpkey_file,
	(prog_config.config_bits2 & SHOW_STRICTFMT) ? FALSE : TRUE);

	show_pfile2(nuid, ngid, SHOW_XFACE, D_XFACE, D_NO_XFACE, pwent->pw_dir,
		prog_config.xface_file, FALSE);
    }
}

/*
 * HANDLE_STANDARD
 *
 * This handles the standard fingering of a user, and checks whether or not
 * the user actually exists.
 */
void handle_standard(char *username)
{
    char uname[600];
    int nums = 0;
    char *buf;

    /*
    ** Support to change the "/W" or "-L" listings from Microsoft's
    ** bloated finger program.  This combats their options, and ignores
    ** them.
    **
    ** Ken Hollis 08/06/96
    */
    if (username[0] == '/' || username[0] == '-') {
	nums=1;
	if (username[2] == ' ')
	    nums++;
	strcpy(uname, username + nums);
	strcpy(username, uname);
    }

    if ((!(prog_config.config_bits2 & SHOW_FINGERFWD)) || 
	check_exist(username))
	show_top();

    if( !( strcmp(prog_config.mailbox_file, "QMAIL") == 0
	&& check_exist_alias(username) )
	&& !check_exist(username))
    {
	if (prog_config.config_bits2 & SHOW_FINGERFWD) {
	    while(prog_config.forward[nums] != NULL) {
		memset(uname, 0, 600);
		printf("\nChecking site %s for the specified user.\n", prog_config.forward[nums]);
		fflush(stdout);
		snprintf(uname, sizeof(uname), "/usr/bin/finger \"%s\100%s\" | /usr/bin/tail +2", username, prog_config.forward[nums]);
		if ((buf = safe_exec(NOBODY_UID, NOBODY_GID, uname)) != NULL) {
		    printf ("%s", buf);
		    fflush(stdout);
		    free (buf);
		}
		nums++;
		log(LOG_USER, "Forwarded: ", username);
	    }
	} else {
	    show_notexist();
	}
    } else {
	show_fingerinfo(username);
	log(LOG_USER, "Normal: ", username);
    }

    if ((!(prog_config.config_bits2 & SHOW_FINGERFWD)) || 
	check_exist(username)) {
	show_bottom();
    }
}

/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 8
 * End:
 */
