/*
* finger.c -- show the owner of a login id or the login id of a person
*
* v2.12 94.02.15 by Curt Sampson
* Copyright 1991-1994 by Fluor Daniel, Inc.
*
* Usage: finger [server/]
* finger [server/] [user ...] ...
*
* See the manual page for complete usage details.
*
* This program is free software; you may redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* 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; with even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public Licence for more details.
*
* You should have received a copy of the GNU General Public Licence
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
*
* This program was compiled under Borland's Turbo C++ 3.00 with the
* Novell NetWare C Interface--DOS libraries. If you make changes to
* this program, please send me a copy. If they seem relevant to the
* community at large, I'll integrate them into the standard release.
*
* Curt Sampson email: a09878@giant.rsoft.bc.ca
* Fluor Daniel
* 1444 Alberni Street Tel: 604 691 5458
* Vancouver, B.C. CANADA Fax: 604 687 6130
* V6G 2Z4
*
* Revision History:
* 91.10.30 cjs Initial version (1.0).
* 92.06.19 cjs Rewritten (2.0).
* 92.06.25 cjs Added last login time field.
* 92.07.02 cjs Now check LOGIN_CONTROL before MISC_LOGIN_INFO for last
* login time.
* 93.09.17 cjs Prints out object-id of user as well (to help find the
* Novell mail directory); "department" is now called "org"
* and the number is printed as well; checks mail directory
* for plan and profile files if it can't find them in the
* user directory. (v2.02)
* 93.09.20 cjs (2.02) Fixed it so that it displays "never logged in"
* instead of "Sun 0 00:00:00 2000" when the user has never
* logged in.
* 93.10.21 cjs (2.10) Added finger.ini file for locations of home dirs,
* moved .plan and .project out of /lib up to root of home dir.
* 93.10.25 cjs (2.11) Now looks at S_SERVER environment variable to
* determine the server to finger on if no server is specified.
* Also prints out month/day of login if >5 days ago.
* 93.10.28 cjs 2.11 release.
* 93.11.16 cjs Fixed it so that it doesn't warn about volumes not found
* on servers to which one is not attached.
* 93.12.01 cjs Moved plan and project out of /lib up to root of home dir,
* but for real this time. :-)
* 94.01.17 cjs Now prints an asterisk before the user-id in the -a listing
* and after the id in other listings if the user-id is
* security-equivalant to SUPERVISOR.
* 94.02.15 cjs (2.12) Fixed bug in display of network address; removed
* code to print out only last two digits of network address
* if first four digits were zero (a relic of the ARCNet days).
*/
char id[]="@(#)finger.c 2.12 94.02.15 by Curt Sampson (FDW Vancouver)";
char copyright[]="Copyright 1991-1994 by Fluor Daniel, Inc.";
#include <dos.h>
#include <cslib/error.h>
#include <cslib/getopt.h>
#include <netware/nit.h>
#include <netware/ntt.h>
#include <netware/niterror.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
/*
* useful types
*/
typedef enum { False = 0, True } Boolean;
struct arg {
enum { Server, User } type;
char *name;
struct arg *next;
};
struct userDirStruct { /* info for the user directories */
char *server; /* server the dir is on */
char *path; /* path to get to the dir */
WORD conn; /* connection no. of the server */
BYTE dir; /* dir handle on that server for that path */
};
#define OBJECT_NAME_LENGTH 48
#define OBJECT_PROPERTY_LENGTH 16
#define OBJECT_VALUE_LENGTH 128
#define INITFILE "finger.ini" /* location of init file */
#define CLIST_SIZE 1000 /* maximum number of users on a server */
#define MAXNAMES 64 /* maximum number of individuals one can */
/* finger on a server at once */
#define MAX_USER_DIRS 64 /* max. no of places to search for user dir */
/* (anybody with more than 64 user dirs */
/* is just silly, anyway.) */
/*
* Global vars
*/
struct userDirStruct userDir[MAX_USER_DIRS]; /* paths to searh for user dirs */
/************************************************************************/
/* */
/* Utility subroutines */
/* */
/************************************************************************/
/*
* containsWord(s, w) -- return true if a word w is contained in
* string s (non case-sensitive comparison)
* (Note that this will match prefixes as well, e.g.,
* the word "jeff" will match "jeffrey" in the string;
* this is intentional.)
*/
Boolean containsWord(char *s, char *w)
{
int si = 0, wi = 0, /* indices into strings s and w */
sl, wl; /* lengths of the two strings */
char lcw[128]; /* lowercase version of w */
sl = strlen(s);
wl = strlen(w);
/* if w is longer than s, w can't be in s */
if ( wl > sl )
return False;
/* make a lower-case copy of string w in advance--more efficient */
for ( wi=0; wi < wl; wi++ )
lcw[wi] = tolower(w[wi]);
lcw[wi] = '\000';
wi = 0;
while ( si < sl ) {
if ( tolower(s[si++]) == lcw[wi] ) {
wi++; /* matches, keep trying rest of word */
} else {
wi = 0; /* reset */
do /* skip to next word */
si++;
while ( ! (s[si] == ' ' || s[si] == '\t' || s[si] == '\000') );
while ( s[si] == ' ' || s[si] == '\t' )
si++;
}
if ( wi == wl )
return True;
}
return False;
}
/*
* itomonth() -- returns a string with the name of the month given as an
* integer (1 = Jan, 2 = Feb, etc.) Returns a null string
* if the integer is not a valid month. The string is
* static and is overwritten with each call.
*/
char *itomonth(int imonth)
{
static char month[4];
switch ( imonth ) {
case 1 : strcpy(month, "Jan"); break;
case 2 : strcpy(month, "Feb"); break;
case 3 : strcpy(month, "Mar"); break;
case 4 : strcpy(month, "Apr"); break;
case 5 : strcpy(month, "May"); break;
case 6 : strcpy(month, "Jun"); break;
case 7 : strcpy(month, "Jul"); break;
case 8 : strcpy(month, "Aug"); break;
case 9 : strcpy(month, "Sep"); break;
case 10 : strcpy(month, "Oct"); break;
case 11 : strcpy(month, "Nov"); break;
case 12 : strcpy(month, "Dec"); break;
default : month[0] = '\0'; break;
}
return month;
}
/*
* itowday() -- returns a string with the name of the weekday given as an
* integer (0 = Sun, 1 = Mon, etc.). Returns a null string
* if the integer is not a valid weekday. The string is
* static and is overwritten with each call.
*/
char *itowday(int iwday)
{
static char wday[4];
switch ( iwday ) {
case 0 : strcpy(wday, "Sun"); break;
case 1 : strcpy(wday, "Mon"); break;
case 2 : strcpy(wday, "Tue"); break;
case 3 : strcpy(wday, "Wed"); break;
case 4 : strcpy(wday, "Thu"); break;
case 5 : strcpy(wday, "Fri"); break;
case 6 : strcpy(wday, "Sat"); break;
case 7 : strcpy(wday, "Sun"); break;
default: wday[0] = '\0'; break;
}
return wday;
}
/*
* messagesDisabled() -- return False if messages enabled (caston), True
* if messages disabled (castoff) for the connection
* on preferred server.
*/
Boolean messagesDisabled(WORD connection)
{
BYTE status;
SendBroadcastMessage("", &connection, &status, 1);
return( status == 0xFF ? True : False );
}
/*
* isSupervisorEquivalant() -- return True if the user is security-equivalant
* to SUPERVISOR, False if not. (If the caller is
* not also security-equivalant to SUPERVISOR this
* will always return False.)
*/
Boolean isSupervisorEquivalant(char *name, WORD type)
{
if ( IsBinderyObjectInSet(name, type, "SECURITY_EQUALS", "SUPERVISOR",
(WORD) 1) )
return False;
else
return True;
}
/*
* getAddress() -- return a pointer to a string contatining the internet
* address of a connection on the current preferred
* server in a nice, printable hex form.
*/
char *getAddress(int connection)
{
BYTE net[4], node[6];
static char output[22];
WORD dummyw;
output[0] = '\000';
if ( GetInternetAddress(connection, net, node, &dummyw) == 0 ) {
sprintf(output, "%08lx", LongSwap(*((long *) net)) );
sprintf(output + 8, ":%08lx%04x", LongSwap(*((long *) node)),
IntSwap(*((int *) (node+4))));
} else {
sprintf(output, "(unknown)");
}
return(output);
}
/*
* getHomeDir(user) -- find and return the home directory of a user; NULL
* if it doesn't seem to exist. Conn is the connection
* no. of the server the user is on.
*/
char *getHomeDir(WORD conn, char *user)
{
char name[9]; /* name of dir is user name trunc to 8 chars */
static char path[256]; /* buffer for path we construct & return */
int i;
WORD prefConn;
long seq;
NWDIR_ENTRY dummy;
BYTE dh;
/* initalisations */
strncpy(name, user, 8); /* truncate user name */
name[8] = '\0';
prefConn = GetPreferredConnectionID();
/* go though our list of handles, trying to stat the dir */
for ( i = 0; i < MAX_USER_DIRS && userDir[i].dir != 0; i++ ) {
if ( (conn == userDir[i].conn) ) {
if ( conn != prefConn ) {
SetPreferredConnectionID(conn);
prefConn = conn;
}
if ( ! AllocTemporaryDirectoryHandle(userDir[i].dir, name, '_',
&dh, 0) ) {
DeallocateDirectoryHandle(dh);
strcpy(path, userDir[i].path);
if ( path[strlen(path)-1] != '/' )
strcat(path, "/");
strcat(path, name);
strlwr(path); /* lowercase the whole thing */
return path;
}
}
}
return NULL; /* didn't find it--sigh */
}
/*
* getOrg(objectID) -- find the department of a user by checking for
* membership in a group beginning with a three
* digit number and an underline. Return a
* pointer to an ASCII representation of that
* department.
*
* Kinda site-specific, I know.
*/
char *getOrg(long objectID)
{
char objectName[OBJECT_NAME_LENGTH]; /* for the objectID we are passed */
WORD objectType;
static char groupName[OBJECT_NAME_LENGTH]; /* and for the groups we check */
WORD groupType;
BYTE objectValue[OBJECT_VALUE_LENGTH];
long *setItem = (long *) objectValue; /* a pointer to our array of groups */
int segno = 1;
BYTE moreseg = 1;
BYTE propflags;
int i;
/* get name and type for next call */
(void) GetBinderyObjectName(objectID, objectName, &objectType);
/*
* find the first group that starts with ###_ (# = an integer),
* what follows will be the department name
*/
/* go though all the segments */
while ( moreseg ) {
/* grab the list of groups from each segment */
if ( ReadPropertyValue(objectName, objectType, "GROUPS_I'M_IN",
segno++, objectValue, &moreseg, &propflags) ) {
return NULL; /* error */
}
if ( ! (propflags & BF_SET) )
return NULL; /* it's not a set--something very wrong here */
/* loop though all ids in a set */
for ( i=0; i<32 && setItem[i]; i++ ) {
if ( GetBinderyObjectName(LongSwap(setItem[i]),
groupName, &groupType) )
continue; /* error, just go on to next */
/* if group name starts with ###_, it's our group */
if ( isdigit(groupName[0]) && isdigit(groupName[1]) &&
isdigit(groupName[2]) && (groupName[3] == '_') ) {
groupName[3] = ' ';
return(groupName);
}
}
}
return NULL; /* went though them all, found nothing */
}
/************************************************************************/
/* */
/* Main subroutines */
/* */
/************************************************************************/
/*
* readInitFile() -- read the finger.ini file (in the dir we started in)
* myName contains argv[0]
*/
void readInitFile(char *myName)
{
WORD conn;
BYTE dirH;
FILE *f;
char s[256];
char *p, *p2;
int i, line;
/* strip our name off the path given in argv[0] and add init file name */
strcpy(s, myName);
#pragma option -w-pia
if ( p = strrchr(s, '\\') )
#pragma option -w.pia
strcpy(p+1, INITFILE); /* put it on the end of the path */
else
strcpy(s, INITFILE); /* no path? try current dir
/* token check for fuck-up (can't see how it could happen under DOS) */
if ( strlen(s) > 255 )
error(3, "Filename path for init file too long--call programmer!");
/* open the file or give up if we can't find it */
if ( ! (f = fopen(s, "r")) )
return;
/* ditch first line ("[USERDIRS]", one hopes!) */
fgets(s, 255, f);
/* and read the servers/dirs, 1 per line */
for ( i = 0, line = 2; i < MAX_USER_DIRS && fgets(s, 255, f); i++, line++ ) {
/* sort of a check of the format */
if ( ! (p = strchr(s, '/')) ||
! (p2 = strchr(p, ':')) ||
! strchr(p2, '/') )
{
fprintf(stderr, "finger: error in format of line %d of %s\n",
line, INITFILE);
i--; continue; /* modifying loop var--naughty! */
} /* which leaves p at the / between server and volume */
*(p++) = '\0'; /* split server (s) and volume/path (p) */
p[strlen(p)-1] = '\0'; /* remove newline */
/* check to see the volume/path exists */
if ( ! GetConnectionID(s, &conn) )
SetPreferredConnectionID(conn);
else
conn = 0;
if ( conn && ! AllocTemporaryDirectoryHandle(0, p, '_', &dirH, 0) ) {
/* store the thing in our list */
#pragma option -w-pia
if ( ! ((userDir[i].server = strdup(s)) &&
(userDir[i].path = strdup(p))) )
#pragma option -w.pia
error(1, "out of memory while reading init file");
userDir[i].conn = conn;
userDir[i].dir = dirH;
} else {
/* warn if we're on that server and couldn't find that dir */
if ( conn ) {
fprintf(stderr,
"finger: warning: can't find %s/%s (from finger.ini)\n",
s, p);
i--; /* (continue;) modifying loop var--naughty! */
}
}
}
/* put an end marker on the arrays, if necessary */
if ( i < MAX_USER_DIRS ) {
userDir[i].server = userDir[i].path = NULL;
userDir[i].conn = userDir[i].dir = 0;
}
/* clean up and get out */
fclose(f);
return;
}
/*
* printFingerInfo(objectID) -- print out finger information on user objectID
* on the server on connection connID. Print out
* the .plan file if printPlan is true.
*
* returns -1 on error, 0 if ok
*/
int printFingerInfo(WORD connID, long objectID, Boolean printPlan)
{
char objectName[OBJECT_NAME_LENGTH];
char objectName2[OBJECT_NAME_LENGTH+1];
WORD objectType;
char objectValue[OBJECT_VALUE_LENGTH];
char server[49];
char *department;
WORD clist[CLIST_SIZE]; /* connection list */
BYTE logintime[7];
char *homedir;
FILE *f;
char filename[128];
int c, found;
WORD i;
BYTE dummyb;
/*
* get name and type, we'll need them later
*/
(void) GetBinderyObjectName(objectID, objectName, &objectType);
/*
* get object name and user name and print them out
* (append asterisk to object name if su'd)
*/
strcpy(objectName2, objectName);
if ( isSupervisorEquivalant(objectName, objectType) )
strcat(objectName2, "*");
printf("\nLogin name: %-19s ", objectName2);
if ( ! ReadPropertyValue(objectName, OT_USER, "IDENTIFICATION", 1,
(BYTE *) objectValue, &dummyb, &dummyb) )
printf("In real life: %s\n", objectValue);
else
printf("\n");
/*
* print out the object-id of the user and the server the ID is on
*/
printf("Object ID: %8lX ", objectID);
GetFileServerName(connID, server);
printf("Server: %-23.23s\n", server);
/*
* print out office and phone number (well, eventually)
*/
department = getOrg(objectID);
if ( department )
printf("Org: %-26.26s ", department);
/*
* print out home directory information
*/
homedir = getHomeDir(connID, objectName);
if ( homedir )
printf("Directory: %s\n", homedir);
else if ( department )
printf("\n");
/*
* print out login information
*/
(void) GetObjectConnectionNumbers(objectName, objectType, (WORD *) &c,
clist, CLIST_SIZE);
if ( c != 0 ) {
/*
* if we have connections, print info on them
*/
for ( i=0; i<c; i++ ) {
(void) GetConnectionInformation(clist[i], objectName, &objectType,
&objectID, logintime);
printf("On since %s %d %02d:%02d:%02d, connection %d",
itomonth(logintime[1]), (int) logintime[2], (int) logintime[3],
(int) logintime[4], (int) logintime[5], (int) clist[i]);
/* print workstation address */
printf(", addr %s", getAddress(clist[i]));
/* check message (caston/castoff) status */
printf("%s\n", messagesDisabled(clist[i]) ? " (castoff)"
: "" );
}
} else {
/*
* No connections, print last login time, which is the format of
* GetFileServerDateAndTime in bytes 56-62 of LOGIN_CONTROL.
*/
if ( ReadPropertyValue(objectName, OT_USER, "LOGIN_CONTROL",
1, (BYTE *) objectValue, &dummyb, &dummyb) == 0 ) {
if ( objectValue[57] )
printf("Last login:%s %s %d %02d:%02d:%02d %d%02d\n",
itowday(objectValue[61]), itomonth(objectValue[57]),
(int) objectValue[58], (int) objectValue[59],
(int) objectValue[60], (int) objectValue[61],
objectValue[56] < 80 ? 20 : 19, objectValue[56]);
else
printf("Never logged in.\n");
} else {
printf("Last login: unknown.\n");
}
}
/*
* print out .project and .plan files
*/
/* avoid complaints on "if ( f=fopen() )" assignments */
#pragma option -w-pia
/* map the user's mail dir to the temporary drive handle [: */
/* if it doesn't map that's ok; fopen will just fail later */
strcpy(filename, "SYS:/MAIL/");
ltoa(objectID, filename + strlen(filename), 16);
AllocTemporaryDirectoryHandle(NULL, filename, '[', &dummyb, &dummyb);
found = 0;
if ( homedir ) { /* check for .project in the home dir */
strcpy(filename, homedir);
strcat(filename, "/project");
if ( f = fopen(filename, "rt") ) {
found = 1;
printf("Project: ");
c = getc(f);
while ( (c != '\n') && (c != EOF) ) {
putchar((char) c);
c = getc(f);
}
fclose(f);
putchar('\n');
}
}
if ( ! found ) { /* if no user dir or not in user dir, try mail dir */
if ( f = fopen("[:project", "rt") ) {
printf("Project: ");
c = getc(f);
while ( (c != '\n') && (c != EOF) ) {
putchar((char) c);
c = getc(f);
}
fclose(f);
putchar('\n');
}
}
if ( printPlan ) {
found = 0;
if ( homedir ) {
strcpy(filename, homedir);
strcat(filename, "/PLAN");
if ( f = fopen(filename, "rt") ) {
found = 1;
printf("Plan:\n");
while ( (c = getc(f)) != EOF ) {
putchar((char) c);
}
fclose(f);
if ( c != '\n' )
putchar('\n');
}
}
if ( ! found ) {
if ( f = fopen("[:plan", "rt") ) {
printf("Plan:\n");
while ( (c = getc(f)) != EOF ) {
putchar((char) c);
}
fclose(f);
if ( c != '\n' )
putchar('\n');
}
}
}
#pragma option -w.pia
/*&*/
return 0;
}
/*
* list all attached servers
* returns 0 on success, 1 on failure of some sort
*/
int listAttachedServers()
{
char name[49];
WORD type;
FILE_SERV_INFO serverInfo;
SERVER_MISC_INFO miscInfo;
BYTE date[7];
int i;
printf("File-Server Name Ver Users/Max Date/Time User \n");
/* FILE-SERVER 3.11 35/250 Jul 13 14:45 GUEST */
/* loop though connection IDs, print out info for those which are in use */
for ( i = 1; i <= 8; i++ ) {
if ( IsConnectionIDInUse(i) ) {
SetPreferredConnectionID(i);
GetFileServerName(i, name);
printf("%-25.25s ", name);
if ( GetServerInformation(sizeof(serverInfo), &serverInfo) == 0 ) {
printf("%1d.%02d ", (int) serverInfo.netwareVersion,
(int) serverInfo.netwareSubVersion);
printf(" %3d/%-3d ", (int) serverInfo.connectionsInUse,
(int) serverInfo.maxConnectionsSupported);
} else {
printf(" ");
}
GetFileServerDateAndTime(date);
printf("%s %02d %2d:%02d ", itomonth(date[1]),
(int) date[2], (int) date[3], (int) date[4]);
if ( GetConnectionInformation(GetConnectionNumber(), name,
&type, (long *) 0, (BYTE *) 0) == 0 ) {
printf("%c%-17s",
isSupervisorEquivalant(name, type) ? '*' : ' ',
name);
}
printf("\n");
}
} printf("\n");
return 0;
}
/*
* display a list of the users on a given server, one user per line
* returns 0 if successful, -1 if not connected to server
*/
int fingerServer(const char *serverName, Boolean printHeader)
{
WORD connectionID;
char serverName2[49];
long objectID;
char objectName[OBJECT_NAME_LENGTH];
char objectValue[OBJECT_VALUE_LENGTH];
WORD objectType;
BYTE loginTime[7];
int loginYear;
struct date curDate;
long daysSince;
char day[4];
char *department;
FILE_SERV_INFO serverInfo;
int i;
BYTE dummyb;
/*
* get the current date
*/
getdate(&curDate);
/*
* send our requests to the requested server
*/
strncpy(serverName2, serverName, 48);
if ( GetConnectionID(serverName2, &connectionID) )
return -1; /* not connected to that server */
SetPreferredConnectionID(connectionID);
/*
* print header line
*/
if ( printHeader ) {
strupr(serverName2); /* to look better in header */
printf("%-23.23s Name Conn When Address\n",
serverName2);
}
/*
* now go though each connection, getting the user info
*/
(void) GetServerInformation(sizeof(serverInfo), &serverInfo);
for ( i = 1; i <= serverInfo.maxConnectionsSupported; i++ ) {
(void) GetConnectionInformation((WORD) i, objectName, &objectType,
&objectID, loginTime);
if ( objectID ) {
/* get info on the object that has the connection */
objectValue[0] = '\0'; /* Novell bug: name unchanged if not set in bindery */
ReadPropertyValue(objectName, objectType, "IDENTIFICATION",
(WORD) 1, (BYTE *) objectValue, &dummyb, &dummyb);
/* print login name (followed by asterisk if su'd) and full name */
strlwr(objectName);
if ( isSupervisorEquivalant(objectName, objectType) )
strcat(objectName, "*");
printf("%-13.13s %-25.25s ", objectName, objectValue);
/* print a '*' before con. no. if messages are disabled (castoff) */
printf("%c", messagesDisabled(i) ? '*' : ' ');
/* print connection number */
printf("%-3d ", i);
/* figure out how many days before now the object logged in */
if ( loginTime[0] < 80 )
loginYear = 2000 + loginTime[0];
else
loginYear = 1900 + loginTime[0];
daysSince = 365 * (curDate.da_year - loginYear);
daysSince += 31 * (curDate.da_mon - loginTime[1]);
daysSince += (curDate.da_day - loginTime[2]);
/* and print when it logged in */
if ( daysSince > 5 ) { /* > 5 days, print month/day */
switch ( loginTime[1] ) { /* turn month to ASCII */
case 1: strcpy(day, "Jan"); break;
case 2: strcpy(day, "Feb"); break;
case 3: strcpy(day, "Mar"); break;
case 4: strcpy(day, "Apr"); break;
case 5: strcpy(day, "May"); break;
case 6: strcpy(day, "Jun"); break;
case 7: strcpy(day, "Jul"); break;
case 8: strcpy(day, "Aug"); break;
case 9: strcpy(day, "Sep"); break;
case 10: strcpy(day, "Oct"); break;
case 11: strcpy(day, "Nov"); break;
case 12: strcpy(day, "Dec"); break;
}
printf(" %s %2.2d ", day, (int) loginTime[2]);
} else { /* < 7 days, print weekday/time */
/* convert day to ascii */
switch ( loginTime[6] ) { /* turn day to ASCII */
case 0: strcpy(day, "Sun"); break;
case 1: strcpy(day, "Mon"); break;
case 2: strcpy(day, "Tue"); break;
case 3: strcpy(day, "Wed"); break;
case 4: strcpy(day, "Thu"); break;
case 5: strcpy(day, "Fri"); break;
case 6: strcpy(day, "Sat"); break;
}
printf("%s %2.2d:%2.2d", day, (int) loginTime[3],
(int) loginTime[4]);
}
/* print address */
printf(" %s\n", getAddress(i));
}
}
return 0;
}
/*
* fingerUsers() -- finger individual users
* iniConn is the initial connection to start on
* if printPlan is false, the .plan file won't be printed
*/
void fingerUsers(WORD iniConn, struct arg *arglist, Boolean printPlan)
{
struct arg *a, *a2; /* pointer in our arg list */
long objectID[MAXNAMES]; /* list of object IDs to finger */
int objectIdx = 0; /* index into objectID */
/* (points just past last object in array) */
WORD connectionID;
char objectName[OBJECT_NAME_LENGTH];
char objectValue[OBJECT_VALUE_LENGTH];
int i;
long lastObjID;
SetPreferredConnectionID(connectionID = iniConn);
a = arglist;
while ( a != 0 ) {
if ( a->type == Server ) {
/*
* finger current list of users, if any
*/
if ( objectIdx > 0 ) {
for ( i=0; i < objectIdx; i++ )
printFingerInfo(connectionID, objectID[i], printPlan);
objectIdx = 0;
}
/*
* set new preferred server
*/
if ( GetConnectionID(a->name, &connectionID) ) {
warn(1, "finger: not connected to server %s", a->name);
/* skip past all users on that server */
if ( ! a->next )
return;
for ( a=a->next; (a->type == User); a=a->next )
if ( ! a->next )
return;
continue;
} else {
SetPreferredConnectionID(connectionID);
}
} else {
/*
* look though all objects on this server for the user names
* we have
*/
lastObjID = -1;
while ( True ) {
i = ScanBinderyObject("*", OT_USER, &lastObjID, objectName,
0, 0, 0, 0);
if ( i == SUCCESSFUL ) { /* check object for match */
(void) ReadPropertyValue(objectName, OT_USER, "IDENTIFICATION",
(WORD) 1, (BYTE *) objectValue, 0, 0);
/* loop though all user args for this server */
for ( a2 = a; a2 && (a2->type == User); a2 = a2->next ) {
if ( containsWord(objectName, a2->name) ||
containsWord(objectValue, a2->name) )
objectID[objectIdx++] = lastObjID;
if ( objectIdx >= MAXNAMES ) {
warn(1, "finger: name table overflow");
return;
}
}
}
else if ( i == NO_SUCH_OBJECT ) { /* last object, exit */
break;
} else { /* bindery error */
warn(1, "finger: error reading bindery: cod 0x%02x", i);
}
}
/* since we just went though all the names up to the next
server, skip past them now */
while ( a->next && (a->next->type == User) )
a = a->next;
}
a = a->next;
}
/*
* and print out the final list of users
*/
if ( objectIdx > 0 ) {
for ( i=0; i < objectIdx; i++ )
printFingerInfo(connectionID, objectID[i], printPlan);
objectIdx = 0;
}
}
/************************************************************************/
/* */
/* Main program */
/* */
/************************************************************************/
main(int argc, char *argv[])
{
int errflag = 0;
struct arg *arglist, *a;
Boolean usersInArgs;
Boolean printHeader = True;
Boolean printPlan = True;
WORD oldPrefID, connectionID;
char serverName[49];
int i;
char *s;
/*
* Save preferred connection ID
*/
oldPrefID = GetPreferredConnectionID();
/*
* Set preferred server
*/
if ( getenv("S_SERVER") &&
! GetConnectionID(getenv("S_SERVER"), &connectionID) )
SetPreferredConnectionID(connectionID);
else
connectionID = 0;
/*
* Parse options
*/
while ( (i = getopt(argc, argv, "afp")) != -1 )
switch ( i ) {
case 'a' : /* list attached servers */
return listAttachedServers();
/* not reached */
case 'f' : /* don't print header line */
printHeader = False;
break;
case 'p' : /* don't print .plan file */
printPlan = False;
break;
case '?' :
errflag++;
}
if ( errflag )
error(2, "Usage: finger [server/] [user ...] ...\n"
" finger [server/] [user ...] ...\n"
" finger -a\n");
/*
* Build argument list
*/
if ( optind >= argc ) {
arglist = 0;
} else {
/* allocate a new argument on our list */
arglist = (struct arg *) malloc(sizeof(struct arg));
if ( ! arglist )
error(2, "finger: out of memory");
arglist->next = 0;
a = arglist; i = optind; goto start_loop;
continue_loop:
/* allocate a new argument on our list */
a->next = (struct arg *) malloc(sizeof(struct arg));
if ( ! a->next )
error(2, "finger: out of memory");
a = a->next; a->next = 0;
start_loop:
/* check for a server */
for ( s=argv[i]; *s != '\0'; s++ ) /* convert \ to / */
if ( *s == '\\' )
*s = '/';
if ( strchr(argv[i], '/') ) { /* yep, server name */
a->type = Server;
if ( argv[i][strlen(argv[i])-1] == '/' ) {
argv[i][strlen(argv[i])-1] = '\0'; /* remove terminating / */
a->name = argv[i];
} else {
a->name = argv[i]; /* store server name */
/* we have to allocate another struct arg for the name part */
a->next = (struct arg *) malloc(sizeof(struct arg));
if ( ! a->next )
error(2, "finger: out of memory");
a->next->next = 0;
a->next->type = User;
a->next->name = (char *) (strchr(argv[i], '/') + 1);
/* and split the server name and user name into two strings */
*(strchr(argv[i], '/')) = '\0';
a = a->next;
}
} else { /* else it's just a user name */
a->type = User;
a->name = argv[i];
}
/* check loop conditions and loop again if necessary */
i++;
if ( i < argc )
goto continue_loop;
}
/*
* Load up the init file
*/
readInitFile(argv[0]);
/*
* If there are no arguments, just finger the preferred server.
*/
if ( ! arglist ) {
if ( ! connectionID )
connectionID = GetPrimaryConnectionID();
if ( ! connectionID )
connectionID = GetDefaultConnectionID();
GetFileServerName(connectionID, serverName);
if ( serverName[0] != '\0' ) {
fingerServer(serverName, printHeader);
return 0;
} else {
error(1, "finger: Can't find preferred file server.");
}
}
/*
* Check for users in list. If there are none, list all users logged
* in to the specified servers. If there are users given, finger the
* users in long form instead.
*/
usersInArgs = False;
for ( a=arglist; a; a = a->next ) {
if ( a->type == User ) {
usersInArgs = True;
break;
}
}
if ( ! usersInArgs ) {
/*
* go though all servers and print the list of users on each
*/
for ( a=arglist; a; a = a->next ) {
if ( fingerServer(a->name, printHeader) )
fprintf(stderr, "finger: Not attached to server %s\n\n", a->name);
else
printf("\n");
}
} else {
fingerUsers(connectionID, arglist, printPlan);
}
/*
* Restore saved prefered connection ID and leave
*/
SetPreferredConnectionID(oldPrefID);
return 0;
}