/* PIPServer - A deamon for finger protocol v2 * * Copyright (C) 1999 Michael Baumer * * 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 #include #include #include #ifdef HAVE_STRINGS_H #include #endif /* HAVE_STRINGS_H */ #include #include #include #include #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); }