/* PIPServer - A deamon for finger protocol v2
*
* Copyright (C) 1999 Michael Baumer <baumi@vis.ethz.ch>
*
* 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 2 of the License, 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* $Id: finger1.c,v 1.36 2001/07/06 05:58:38 baumi Exp $
*
* Handle the anwser if the incomming request was sent in the
* finger protocol version 1.
*
* This is not as fancy as in GNU Finger v1.x for example, but keep in mind
* that most important info is accessible by a local user only, who probably
* has the new protocol anyway
*
* The pinciple is:
* 1. Create login_info for all poeple online form utmp, gnufinger hostdata
* or db.
* 2a. If the user is online print it else look the user up in the passwd
* file or in the db.
* 2b. (Long Output) Do special lookup for information to be printed.
*/
#include <config.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif /* HAVE_STRINGS_H */
#include <stdlib.h>
#include <ctype.h>
#include <pwd.h>
#include <sys/types.h>
#include "in.fingerd.h"
#include "local_user.h"
#include "readutmp.h"
#include "file.h"
#include "finger.h"
#include "parser.h"
#include "mail.h"
#include "configfile.h"
#include "acl.h"
/* prefix for inter-daemon calls made by GNU Finger */
#define GNU_INFINGERD_FLAG "FiNgEr"
void finger1_loginline(login_info *linfo);
char header_string[126];
int fromhostlen;
/* print the resulting info
* - in "long" format
* - Offline information
*
* Note that the .plan (and similar) files are returned in another form
* in version 2 of the finger protocol
*/
void finger1_show(int longflag, finger_userinfo *pwd, login_info *linfo)
{
char *filename;
login_info *current = linfo;
Node *node = NULL;
Node *cur_node;
time_t access_time;
/* Seach for matching online information */
while (current) {
/* Check login name */
if (!strncmp(current->login_user, pwd->pw_name, UT_NAMESIZE-1)) {
break;
}
/* Check Real name */
current = current->next;
}
if (!longflag) {
if (!current)
fprintf(stdout, "%-8s %-20s\tOffline \t< . . . >\r\n",
pwd->pw_name,
pwd->pw_realname);
else
finger1_loginline(current);
}
else {
/* Long output requested */
/* Login name and real name */
fprintf(stdout, "Login Name: %-8s\t\t\tIn real life: %s\r\n",
pwd->pw_name,
pwd->pw_realname);
/* Try to open .fingerconf */
filename = findUserFile(pwd, "fingerconf");
if (filename && (node = readFingerconf(filename))) {
/* Output everything */
cur_node = node->child;
while (cur_node) {
if (!strcmp(cur_node->id, "userinfo")) {
cur_node = cur_node->child;
while(cur_node) {
/* Handle specials */
if (strcmp(cur_node->id, "first") &&
strcmp(cur_node->id, "last")) {
/* Make it simple */
/* this is probably a hack */
cur_node->id[0] = toupper(cur_node->id[0]);
if (!*cur_node->value)
printf("*%s*\r\n", cur_node->id);
else {
if (index(cur_node->value, '\n'))
printf("%-10s:\r\n%s\r\n", cur_node->id, cur_node->value);
else
printf("%-10s: %s\r\n", cur_node->id, cur_node->value);
}
}
if (cur_node->child)
cur_node = cur_node->child;
else {
while (!cur_node->next) {
if (!cur_node->parent)
break;
cur_node = cur_node->parent;
}
if (!strcmp(cur_node->id, "userinfo"))
cur_node = NULL;
else
cur_node = cur_node->next;
}
}
} else
cur_node = cur_node->next;
}
/* free parse tree */
FreeParseTree(node);
}
else {
/* No .fingerconf, use GECOS fields instead */
/* Office and Office phone */
if (pwd->office) {
if (pwd->office_phone)
fprintf(stdout, "Office: %s, %s\r\n",
pwd->office, pwd->office_phone);
else
fprintf(stdout, "Office: %s\r\n", pwd->office);
}
else
if (pwd->office_phone)
fprintf(stdout, "Office phone: %s\r\n", pwd->office_phone);
/* Home phone */
if (pwd->home_phone)
fprintf(stdout, "Home phone: %s\r\n", pwd->home_phone);
}
/* Print login information */
if (current) {
fprintf(stdout, header_string);
finger1_loginline(current);
}
else {
fprintf(stdout, "%s (%s) is not currently logged in\r\n",
pwd->pw_realname,
pwd->pw_name);
}
if (config.acl->options & ACL_MAIL) {
/* Print users mail status
* Missing: - Mail forwarding
* - security
*/
switch (checkmail(pwd, &access_time)) {
case NO_MAIL:
fprintf(stdout, "No mail.\r\n");
break;
case UNREAD_MAIL:
fprintf(stdout, "Unread mail since %.24s.\r\n", ctime(&access_time));
break;
case NEW_MAIL:
fprintf(stdout, "New mail since %.24s.\r\n", ctime(&access_time));
break;
case READ_MAIL:
fprintf(stdout, "No new mail.\r\n");
break;
}
}
/* Print if the user has a .key or even a .pgpkey file
* The difference is that .key is generic and .pgpkey is clear.
*/
if (findUserFile(pwd, "key"))
fprintf(stdout,
"Public key available: Use \"finger %s.key\" to get it.\r\n",
pwd->pw_name);
if (findUserFile(pwd, "pgpkey"))
fprintf(stdout,
"PGP public key available: Use \"finger %s.pgpkey\" "
"to get it.\r\n",
pwd->pw_name);
/* printing of the users .project file is not done yet.
* It will only be included if someone really wants it.
* Since the project file is only a single line this should
* _really_ go in a more general rc-file.
*/
/* print .plan file
*
* Note: GNU Finger 1.x allows the plan file to be parsed by cpp
* for replacing some strings (e.g. Date, Time).
* I don't think that is absolutely necessary and is non-standard.
*/
if ((filename = findUserFile(pwd, "plan"))) {
/* send the file */
fprintf (stdout, "Plan:\r\n");
print_file(filename, 0);
}
else
if (!node)
/* Only say that if we hav no fingerconf */
fprintf(stdout, "No plan.\r\n");
/* exec .fingerc: Not implemented */
/* make an empty line */
fprintf(stdout, "\n");
}
}
/* Handle target on user */
void target1(finger_userinfo *pwd, char *target)
{
char *filename;
int i = 0;
while(config.validfile[i] && strcmp(config.validfile[i], target))
i++;
if (config.validfile[i]) {
filename = findUserFile(pwd, config.validfile[i]);
if (filename)
/* send the file */
print_file(filename, 0);
}
}
/* answer to a finger request
*
* 1. match online login, if found print output and exit
* 2. match login, if found print output and exit
* 3. match remote logins, if found print output and exit
* 4. match local users and their realnames
* 5. match remote users and their realnames
*
* That way we are able to provide on/offline information for matching
* Users and are able to search for non-matching users.
* The problem is that we can't provide a useful search that way, if the login
* name exists. Abbreviations must be used in that case.
*/
void finger_reply(int longflag, char *username, login_info *linfo)
{
finger_userinfo *pwd;
char *target;
/* special targets */
target = index(username, '.');
if (target) {
*target='\0';
target++;
if (!*target)
target = NULL;
}
else
if (!longflag)
fprintf(stdout, header_string);
/* search for local users by login name */
pwd = pwd_search_login(username, 1);
if (pwd) {
if (!target)
finger1_show(longflag, pwd, linfo);
else
target1(pwd, target);
return;
}
/* search for users on other hosts by login name*/
/* db_search(username); */
/* search for local users by real name */
/* rewind passwd file */
setpwent();
while ((pwd = pwd_search_realname(username)))
if (!target)
finger1_show(longflag, pwd, linfo);
else
target1(pwd, target);
}
/* Print one line for a login
*/
void finger1_loginline(login_info *linfo)
{
char timestr[20];
char idlestr[20];
char *idx;
if (time(0) - linfo->login_time > 30758400)
/* more than a year ago (approx.) */
strftime(timestr, 20, "%d %b %y", localtime(&linfo->login_time));
else
if (time(0) - linfo->login_time > 604800)
/* more than a week ago */
strftime(timestr, 20, "%a %d %b", localtime(&linfo->login_time));
else
strftime(timestr, 20, "%a %H:%M", localtime(&linfo->login_time));
linfo->hostname[9]='\0';
if ((idx = index(linfo->hostname, '.')))
*idx = '\0';
if (!(config.acl->options & ACL_IDLETIME))
fprintf(stdout, "%-8s %-20s\t%-6s %-10s %-10s",
linfo->login_user,
linfo->realname,
linfo->status,
timestr,
linfo->hostname);
else {
if (linfo->idle_time < 3600)
sprintf(idlestr, "%d:%02dm", (linfo->idle_time / 60),
(linfo->idle_time % 60));
else if (linfo->idle_time < 24*3600)
sprintf(idlestr, "%d:%02dh", (linfo->idle_time / 3600),
((linfo->idle_time % 3600) / 60));
else
sprintf(idlestr, "%ddays", (linfo->idle_time / (24*3600)));
fprintf(stdout, "%-8s %-20s\t%-6s %6s %-10s %-10s",
linfo->login_user,
linfo->realname,
linfo->status,
idlestr,
timestr,
linfo->hostname);
}
if (config.acl->options & ACL_FROMHOST) {
linfo->fromhost[fromhostlen] = '\0';
printf("%s\r\n", linfo->fromhost);
}
else
printf("\r\n");
}
/* Handle one line of finger protocol v1 input
* (called recursively for each user)
*
* - Read utmp/gnufinger hostdata/db for online users
*
* NOTE: we do not provide tty information:
* - it is not necessary (even if you want to use talk)
* - it is senseless in a distributed environment
*/
void finger1(char *input)
{
char *username;
static int longflag = FALSE;
static login_info *linfo = NULL;
/* skip trailing white space */
while (*input && isspace(*input))
input++;
/* Read online information once */
if (!linfo)
linfo = read_utmp(1);
/* empty line */
if (!*input) {
/* only do that if we are allowed to */
if (config.acl->options & ACL_USERLIST) {
/* Print a nice title */
fprintf(stdout, header_string);
/* Output the online information */
while(linfo) {
finger1_loginline(linfo);
linfo = linfo->next;
}
}
else {
fprintf(stdout, "Sorry, the request-type 'finger @host' is disabled.\r\n"
"Try finger .help for a list of available commands.\r\n");
}
return;
}
if ((input[0]=='.') || (input[0]=='/') || (input[0]=='-')) {
if (toupper(input[1])=='W') {
/* somebody asked for the "longflag" format */
/* skip trailing white space */
input += 2;
while (*input && isspace(*input))
input++;
longflag = TRUE;
}
#if 0
/* not used at the moment */
else if (!strncmp(&input[1], GNU_INFINGERD_FLAG,
strlen(GNU_INFINGERD_FLAG))) {
/* go to next token */
input += strlen(GNU_INFINGERD_FLAG) + 1;
while (*input && isspace(*input))
input++;
}
#endif
else if (!strncmp(&input[1], "all", 3)) {
finger1("");
/* go to next token */
input += 4;
while (*input && isspace(*input))
input++;
}
else if (!strncmp(&input[1], "help", 4)) {
/* Print some basic usage information
* This is the default text. It will only be printed if
* not overwritten.
*/
if (!config.help) {
fprintf(stdout, "\t\tThis is the finger service\r\n\r\n");
fprintf(stdout, "Normally you just want to do a\r\n\r\n"
"\t\"finger username\"\tfor a short information\r\n"
"or"
"\t\"finger -l username\"\tfor a more detailed information"
"\r\n\r\n"
"If the user has a public key or a PGP public key you can use"
"\r\n\r\n"
"\tfinger username.key \r\nor\tfinger username.pgpkey\t"
"to get it.\r\n\r\n");
fprintf(stdout, "This PFinger server "
"accepts several special user names.\r\n\r\n"
"\t.version\tReturn server version\r\n"
"%s"
"\t.help\t\tThis message\r\n\r\n",
(config.siteinfo) ?
"\t.site\t\tList site information\r\n" : "");
}
else {
/* todo: make correct CR/LF line endings */
fprintf(stdout, "%s\r\n", config.help);
}
/* go to next token */
input += 5;
while (*input && isspace(*input))
input++;
}
else if (!strncmp(&input[1], "version", 7)) {
/* Print out version */
fprintf(stdout, "PFinger version %s\r\n", VERSION);
/* go to next token */
input += 8;
while (*input && isspace(*input))
input++;
}
else if ((config.siteinfo) && (!strncmp(&input[1], "site", 4))) {
/* todo: make correct CR/LF line endings */
fprintf(stdout, "%s\r\n", config.siteinfo + 1);
/* go to next token */
input += 5;
while (*input && isspace(*input))
input++;
}
else {
/* unknown option - skip */
/* skip trailing option */
input++;
while (*input && !isspace(*input))
input++;
/* skip trailing white space */
while (*input && isspace(*input))
input++;
}
}
else {
/* string should be "plain" username */
username = input;
/* search for end of username */
while(*input && !isspace(*input)) {
input++;
}
/* we have reached the end of line */
if (username == input)
return;
if (*input) {
*input = '\0';
finger_reply(longflag, username, linfo);
/* make shure we do not call ourself with an empty line */
input++;
while (*input && isspace(*input))
input++;
}
else
finger_reply(longflag, username, linfo);
}
if (*input)
finger1(input);
}
/* init_finger1
*
* Create the various format strings
*/
void init_finger1()
{
strcpy (header_string, "Login\t Name\t\t\tStatus ");
if (config.acl->options & ACL_IDLETIME)
strcat(header_string, "Idle ");
strcat(header_string, " Login time Host");
if (config.acl->options & ACL_FROMHOST)
strcat(header_string, " From\r\n");
else
strcat(header_string, "\r\n");
fromhostlen = 68-strlen(header_string);
}
syntax highlighted by Code2HTML, v. 0.9.1