/* utils.c */ #include "config.h" /* * This file is part of TeenyMUD II. * Copyright(C) 1993, 1994, 1995 by Jason Downs. * All rights reserved. * * TeenyMUD II 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. * * TeenyMUD II 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 (see the file 'COPYING'); if not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. * */ /* AIX requires this to be the first thing in the file. */ #ifdef __GNUC__ #define alloca __builtin_alloca #else /* not __GNUC__ */ #ifdef HAVE_ALLOCA_H #include <alloca.h> #else /* not HAVE_ALLOCA_H */ #ifdef _AIX #pragma alloca #endif /* not _AIX */ #endif /* not HAVE_ALLOCA_H */ #endif /* not __GNUC__ */ #include <stdio.h> #include <sys/types.h> #ifdef HAVE_STRING_H #include <string.h> #else #include <strings.h> #endif /* HAVE_STRING_H */ #include <ctype.h> #ifdef TM_IN_SYS_TIME #include <sys/time.h> #else #include <time.h> #endif /* TM_IN_SYS_TIME */ #include <sys/stat.h> #ifdef HAVE_MALLOC_H #include <malloc.h> #endif /* HAVE_MALLOC_H */ #ifdef HAVE_STDLIB_H #include <stdlib.h> #endif /* HAVE_STDLIB_H */ #include "conf.h" #include "teeny.h" #include "commands.h" #include "ptable.h" #include "sha/sha_wrap.h" #include "externs.h" /* Some generic utility functions for TeenyMUD */ int create_player(name, pwd, code) char *name, *pwd; int code; { int player, ret = 0; int flags[FLAGS_LEN]; /* No guests can be created from the greet screen. */ if ((code == -1) && (!ok_player_name(name) || (strncasecmp(name, "guest", 5) == 0) || (pwd == (char *)NULL))) return(-1); /* Create a player in object number STARTING_LOC */ player = create_obj(TYP_PLAYER); ret = set_str_elt(player, NAME, name); if ((ret != -1) && (pwd != (char *)NULL) && pwd[0]) ret = attr_add(player, PASSWORD, cryptpwd(pwd), PASSWORD_FLGS); if (code == -1) { if (ret != -1) { list_add(player, mudconf.starting_loc, CONTENTS); ret = set_int_elt(player, LOC, mudconf.starting_loc); } } else { if (ret != -1) { list_add(player, code, CONTENTS); ret = set_int_elt(player, LOC, code); } } if (ret != -1) ret = set_int_elt(player, HOME, mudconf.starting_loc); if (ret != -1) ret = set_int_elt(player, OWNER, player); if (ret != -1) ret = get_flags_elt(player, FLAGS, flags); if (ret != -1) { flags[0] |= (mudconf.newp_flags)[0]; flags[1] |= (mudconf.newp_flags)[1]; ret = set_flags_elt(player, FLAGS, flags); } if (ret != -1) ret = set_int_elt(player, QUOTA, mudconf.starting_quota); if (ret != -1) ret = set_int_elt(player, PENNIES, mudconf.starting_money); if (ret == -1) { destroy_obj(player); return (-1); } return (player); } int connect_player(name, pwd) char *name, *pwd; { char *realpwd; int player, aflags; /* * Special goo for multiple guest character support. */ if(strcasecmp(name, "guest") == 0) { /* They're connecting to guest. */ player = match_player(name); if(player == -1) { /* No "guest", look for "guestN". */ int gn; char sbuf[10]; for(gn = 1; gn < 256; gn++) { snprintf(sbuf, sizeof(sbuf), "guest%d", gn); player = match_player(sbuf); if(player == -1) return(-1); /* Is it already connected? */ if(isALIVE(player)) continue; /* Otherwise, we found the right one. */ break; } } } else { char *sptr; player = match_player(name); if (player == -1) { if((name[0] == '#') && isdigit(name[1])) { player = (int)strtol(&name[1], &sptr, 10); if((sptr != &name[1]) && exists_object(player) && isPLAYER(player)) return(player); } return (-1); } } /* Check the password */ if (attr_get(player, PASSWORD, &realpwd, &aflags) == -1) { logfile(LOG_ERROR, "connect_player: bad attribute reference on object #%d\n", player); return (-1); } if (((realpwd == (char *)NULL) || (realpwd[0] == '\0')) && (pwd == (char *)NULL)) return (player); if ((realpwd != (char *)NULL) && (realpwd[0] != '\0') && (pwd != (char *)NULL) && comp_password(realpwd, pwd)) return (player); return (-1); } void announce_connect(player, user, host) int player; char *user, *host; { int loc; char *name; char buf[256]; int wflags[FLAGS_LEN]; if (get_int_elt(player, LOC, &loc) != -1) { if (get_str_elt(player, NAME, &name) != -1) { if (!isDARK(player) && !isDARK(loc)) { snprintf(buf, sizeof(buf), "%s has %s.", name, isALIVE(player) ? "reconnected" : "connected"); notify_all(player, player, player, buf, NOT_NOPUPPET); } if(user != (char *)NULL) { snprintf(buf, sizeof(buf), "[ %s has connected from %s@%s. ]", name, user, host); } else { snprintf(buf, sizeof(buf), "[ %s has connected from %s. ]", name, host); } wflags[0] = RETENTIVE; wflags[1] = 0; tcp_wall(wflags, buf, -1); } } } void announce_disconnect(player) int player; { int loc; char *name; char buf[80]; int wflags[FLAGS_LEN]; if (get_int_elt(player, LOC, &loc) != -1) { if (get_str_elt(player, NAME, &name) != -1) { if (!isDARK(player) && !isDARK(loc)) { snprintf(buf, sizeof(buf), "%s has %s.", name, isALIVE(player) ? "partially disconnected" : "disconnected"); notify_all(player, player, player, buf, NOT_NOPUPPET); } snprintf(buf, sizeof(buf), "[ %s has disconnected. ]", name); wflags[0] = RETENTIVE; wflags[1] = 0; tcp_wall(wflags, buf, -1); } } if (isGUEST(player) && !isALIVE(player)) { /* send "GUEST" players to their home */ do_home(player, player, 0); } } /* Hack name/pwd pair up. */ int parse_name_pwd(str, name, pwd) char *str, **name, **pwd; { static char nbuf[MAXPNAMELEN + 1], pbuf[MAXPWDLEN + 1]; char *p; if (str == (char *)NULL) return(-1); while (*str && isspace(*str)) str++; if (*str == '\0') return (-1); if (*str != '\"') { /* the name isn't quoted, fuck */ /* assume the first word is name, all other is pword */ for (p = nbuf; *str && !isspace(*str) && (p - nbuf) < sizeof(nbuf); *p++ = *str++); *p = '\0'; while (isspace(*str) && *str) str++; if (*str == '\0') *pwd = (char *) NULL; else { for (p = pbuf; *str && (p - pbuf) < sizeof(pbuf); *p++ = *str++); *p = '\0'; *pwd = pbuf; /* Assume no trailing whitespace. */ } *name = nbuf; return (0); } else { /* quoted name! YAY! */ while (*str && *str == '\"') str++; while (*str && isspace(*str)) str++; p = nbuf; *name = nbuf; while (*str && *str != '\"' && (p - nbuf) < sizeof(nbuf)) { if (!isspace(*str)) { *p++ = *str++; continue; } for (; *str && isspace(*str); str++); if (*str && (*str != '\"')) *p++ = ' '; } *p = '\0'; while (*str && (*str == '\"' || isspace(*str))) str++; if (*str == '\0') *pwd = (char *) NULL; else { for (p = pbuf; *str && (p - pbuf) < sizeof(pbuf); *p++ = *str++); *p = '\0'; *pwd = pbuf; } return (0); } } void parse_type(str, ret) char *str; int *ret; { switch(str[0]) { case 'p': case 'P': *ret = TYP_PLAYER; break; case 'r': case 'R': *ret = TYP_ROOM; break; case 't': case 'T': *ret = TYP_THING; break; case 'e': case 'E': *ret = TYP_EXIT; break; default: *ret = -1; } } int ok_attr_name(name) char *name; { return (name && *name && (strlen(name) < MAXATTRNAME) && !strchr(name, '*') && !strchr(name, '?') && !strchr(name, '\\') && !strchr(name, '[') && !strchr(name, ']')); } int ok_attr_value(data) char *data; { register int i; for (i = 0; data[i] && (i < 4097); i++) { if (!isprint(data[i]) || (data[i] == '\r') || (data[i] == '\n')) return (0); } return (i < 4097); } int ok_name(name) char *name; { register int i; if (!name || !*name || (name[0] == '#') || (name[0] == '!')) return (0); for (i = 0; name[i]; i++) { if ((name[i] == '=') || (name[i] == '&') || (name[i] == '|') || (name[i] == '*') || (name[i] == '?') || (name[i] == '\\') || (name[i] == '[') || (name[i] == ']') || (name[i] == ':') || (name[i] == '<') || (name[i] == '>') || (name[i] == '&') || (name[i] == '\"') || !isprint(name[i])) return (0); } /* Hard coded bad object names. */ return (strcmp(name, "A") && strcmp(name, "An") && strcmp(name, "The") && strcmp(name, "You") && strcmp(name, "Your") && strcmp(name, "Going") && strcmp(name, "Huh?") && strcmp(name, "me") && strcmp(name, "home") && strcmp(name, "here")); } int ok_player_name(name) char *name; { /* Do the obvious checkeds, first. */ if (!ok_name(name) || (strlen(name) > MAXPNAMELEN) || (match_player(name) != -1)) return (0); else { int index; /* Check for configured bad player names. */ for(index = 0; index < 64; index++) { if(mudconf.bad_pnames[index] != (char *)NULL) { if(strcmp(mudconf.bad_pnames[index], name) == 0) return(0); } else break; } return (1); } } int ok_exit_name(name) char *name; { char *p, *q, *buf; if (!ok_name(name)) return (0); buf = (char *) alloca(strlen(name) + 1); if(buf == (char *)NULL) panic("ok_exit_name(): stack allocation failed\n"); strcpy(buf, name); /* we have to check each sub string... */ q = buf; while (q && *q) { for (p = q; *p && *p != ';'; p++); if (*p) *p++ = '\0'; if (!ok_name(q)) return (0); q = p; } return (1); } void reward_money(player, amount) int player, amount; { int pennies, owner; if(!mudconf.enable_money) return; if (get_int_elt(player, OWNER, &owner) == -1) { logfile(LOG_ERROR, "reward_money: bad owner reference on #%d\n", player); return; } if (isWIZARD(owner)) return; if (get_int_elt(owner, PENNIES, &pennies) == -1) { logfile(LOG_ERROR, "reward_money: bad pennies reference on #%d\n", owner); return; } if (set_int_elt(owner, PENNIES, pennies + amount) == -1) { logfile(LOG_ERROR, "reward_money: bad pennies reference on #%d\n", owner); return; } } int can_afford(player, cause, amount, quiet) int player, cause, amount, quiet; { int pennies, owner; if(!mudconf.enable_money) return(1); if (get_int_elt(player, OWNER, &owner) == -1) { logfile(LOG_ERROR, "can_afford: bad owner reference on #%d\n", player); return (0); } if (isWIZARD(owner)) return (1); if (get_int_elt(owner, PENNIES, &pennies) == -1) { logfile(LOG_ERROR, "can_afford: bad pennies reference on #%d\n", owner); return (0); } if (pennies > amount) { if (set_int_elt(owner, PENNIES, pennies - amount) == -1) { logfile(LOG_ERROR, "can_afford: bad pennies reference on #%d\n", owner); return (0); } return (1); } if(!quiet) notify_player(player, cause, player, "You don't have enough pennies.", NOT_QUIET); return (0); } void check_paycheck(player) int player; { time_t last; int pennies; if(!mudconf.enable_money) return; if (isWIZARD(player) || (get_int_elt(player, TIMESTAMP, (int *) &last) == -1) || (get_int_elt(player, PENNIES, &pennies) == -1)) return; if (last <= 0) return; if ((pennies < mudconf.max_pennies) && ((localtime(&mudstat.now))->tm_yday > (localtime(&last))->tm_yday)) { if (set_int_elt(player, PENNIES, pennies + mudconf.daily_paycheck) == -1) return; } } void check_last(player) int player; { char *host; time_t last; int aflags; char buf[MEDBUFFSIZ]; if ((get_int_elt(player, TIMESTAMP, (int *) &last) == -1) || (attr_get(player, SITE, &host, &aflags) == -1)) return; if (last <= 0) return; strftime(buf, MEDBUFFSIZ, "Last connection: %a %b %e %H:%M:%S %Z %Y from ", localtime(&last)); strcat(buf, (host == NULL) ? "???" : host); notify_player(player, player, player, buf, 0); } /* spew a file to a player, following special permissions. */ void dump_file(player, cause, thing, fname) int player, cause, thing; char *fname; { FILE *fp; char fbuf[128], mbuf[BUFFSIZ]; #ifndef __STDC__ char *strstr(); #endif /* If there is a leading slash, object must be set FILE_OK. */ if(fname[0] == '/') { if(!isFILE_OK(thing)) { snprintf(mbuf, sizeof(mbuf), "%s: Permission denied.", fname); notify_player(player, cause, thing, mbuf, 0); return; } snprintf(mbuf, sizeof(mbuf), "%s/%s", mudconf.files_path, fname); } else snprintf(mbuf, sizeof(mbuf), "%s/#%d/%s", mudconf.files_path, thing, fname); /* If it has a "../" in it, fail. */ if(strstr(fname, "../") != (char *)NULL) { snprintf(mbuf, sizeof(mbuf), "%s: Permission denied.", fname); notify_player(player, cause, thing, mbuf, 0); return; } fp = fopen(mbuf, "r"); if(fp == (FILE *)NULL) { snprintf(mbuf, sizeof(mbuf), "%s: Can't open file.", fname); notify_player(player, cause, thing, mbuf, 0); return; } while(!feof(fp) && (fgets(fbuf, sizeof(fbuf), fp) != (char *)NULL)) { remove_newline(fbuf); notify_player(player, cause, thing, fbuf, 0); } fclose(fp); } /* spew a shell command at the player, following special permissions. */ void dump_cmdoutput(player, cause, thing, fname) int player, cause, thing; char *fname; { FILE *fp; char fbuf[128], mbuf[BUFFSIZ]; #ifndef __STDC__ char *strstr(); #endif /* If there is a leading slash, object must be set FILE_OK. */ if(fname[0] == '/') { if(!isFILE_OK(thing)) { snprintf(mbuf, sizeof(mbuf), "%s: Permission denied.", fname); notify_player(player, cause, thing, mbuf, 0); return; } snprintf(mbuf, sizeof(mbuf), "%s/%s", mudconf.files_path, fname); } else snprintf(mbuf, sizeof(mbuf), "%s/#%d/%s", mudconf.files_path, thing, fname); /* If it has a "../" in it, fail. */ if(strstr(fname, "../") != (char *)NULL) { snprintf(mbuf, sizeof(mbuf), "%s: Permission denied.", fname); notify_player(player, cause, thing, mbuf, 0); return; } fp = popen(mbuf, "r"); if(fp == (FILE *)NULL) { snprintf(mbuf, sizeof(mbuf), "%s: Can not execute.", fname); notify_player(player, cause, thing, mbuf, 0); return; } while(!feof(fp) && (fgets(fbuf, sizeof(fbuf), fp) != (char *)NULL)) { remove_newline(fbuf); notify_player(player, cause, thing, fbuf, 0); } pclose(fp); } int legal_roomloc_check(source, test, check_depth) int source, test; int *check_depth; { int loc; (*check_depth)++; if (source == test) return (0); if (!exists_object(test) || !isROOM(test)) return (0); if (test == mudconf.root_location) return (1); if ((*check_depth) > mudconf.room_depth) return (0); if (get_int_elt(test, LOC, &loc) == -1) return (0); return (legal_roomloc_check(source, loc, check_depth)); } int legal_thingloc_check(obj, dest) int obj, dest; { int loc, depth; depth = 0; if (isROOM(obj) || isEXIT(obj)) return(1); if (obj == dest) return(0); loc = dest; while(!isROOM(loc) && (depth < mudconf.room_depth)) { if ((loc == obj) || ((depth > 0) && (loc == dest))) return(0); if (get_int_elt(loc, LOC, &loc) == -1) return(0); depth++; } if(isROOM(loc)) return(1); return(0); } int legal_parent_check(obj, test, check_depth) int obj, test; int *check_depth; { int parent; (*check_depth)++; if (test == -1) return (1); if ((obj == test) || !exists_object(test)) return (0); if ((*check_depth) > mudconf.parent_depth) return (0); if (get_int_elt(test, PARENT, &parent) == -1) return (0); return (legal_parent_check(obj, parent, check_depth)); } int legal_recursive_exit(exit, test, check_depth) int exit, test, *check_depth; { int *dests; register int i; (*check_depth)++; if ((test == -1) || (test == -3) || (exists_object(test) && !isEXIT(test))) return (1); if ((exit == test) || !exists_object(test)) return (0); if ((*check_depth) > mudconf.exit_depth) return (0); if (get_array_elt(test, DESTS, &dests) == -1) return (0); if (dests != (int *)NULL) { for(i = 1; i <= dests[0]; i++) { if(dests[i] == exit) { return(0); } if (Typeof(dests[i]) == TYP_EXIT) { if (!legal_recursive_exit(exit, dests[i], check_depth)) return(0); } } } return(1); } /* * HTML support code. * * html_anchor_exit() takes an exit name and returns an anchor for it. * * html_anchor_contents() takes an element from the contents list and * returns an anchor for it. * * html_anchor_location() checks the player's location for a URL, and sends * it to them. * * html_desc() checks for an Htdesc, and sends it. */ char *html_anchor_exit(name) char *name; { static char ret[2048]; char *sname, *ptr; for(ptr = name; *ptr && (*ptr != ';'); ptr++); if(((*ptr == '\0') || (*ptr == ';')) && ((ptr - name) > 0)) { sname = (char *)alloca((ptr - name)+1); if(name == (char *)NULL) { panic("html_anchor_exit: stack allocation failed!"); } strncpy(sname, name, (ptr - name)); sname[(ptr - name)] = '\0'; snprintf(ret, sizeof(ret), "<a xch_cmd=\"%s\" xch_hint=\"Go %s\">%s</a>", sname, sname, sname); return(ret); } else return(name); } char *html_anchor_contents(sname, name) char *sname, *name; { static char ret[2048]; snprintf(ret, sizeof(ret), "<a xch_cmd=\"look %s\" xch_hint=\"Look at %s\">%s</a>", sname, sname, name); return(ret); } void html_anchor_location(player, cause) int player, cause; { int loc, aflags, source; char *attr, *buf; if((get_int_elt(player, LOC, &loc) == -1) || !exists_object(loc)) return; if((attr_get_parent(loc, VRML_URL, &attr, &aflags, &source) == 0) && (attr != (char *)NULL)) { buf = (char *)alloca(strlen(attr)+30); if(buf == (char *)NULL) { panic("html_anchor_location: stack allocation failed!"); } strcpy(buf, "<img xch_graph=load href=\"<"); strcat(buf, attr); strcat(buf, "\">"); notify_player(player, cause, player, buf, NOT_RAW); } } void html_desc(player, cause, thing) int player, cause, thing; { int aflags, source; char *attr; if((attr_get_parent(thing, HTDESC, &attr, &aflags, &source) == 0) && (attr != (char *)NULL)) { notify_player(player, cause, player, attr, NOT_RAW); } } #ifdef UNIX_CRYPT /* * Extra GOO to invisibly support both crypt(3) and SHA passwords, for * old databases. */ int comp_password(encrypt, string) char *encrypt, *string; { /* If the encrypted string is 13 characters long, assume it's crypt(3). */ if(strlen(encrypt) == 13) { char salt[3]; salt[0] = encrypt[0]; salt[1] = encrypt[1]; salt[2] = '\0'; return(strcmp((char *)crypt(string, salt), encrypt) == 0); } return(strcmp(sha_crypt(string), encrypt) == 0); } #endif /* UNIX_CRYPT */