/** Check for CHARGES on thing and, if present, lower. * \param thing object being used. * \retval 0 charges was set to 0 * \retval 1 charges not set, or was > 0 */ int charge_action(dbref thing) { ATTR *b; char tbuf2[BUFFER_LEN]; int num; /* check if object has # of charges */ b = atr_get_noparent(thing, "CHARGES"); if (!b) { return 1; /* no CHARGES */ } else { strcpy(tbuf2, atr_value(b)); num = atoi(tbuf2); if (num > 0) { /* charges left, decrement and execute */ (void) atr_add(thing, "CHARGES", tprintf("%d", num - 1), Owner(b->creator), 0); return 1; } else { /* no charges left, try to execute runout */ return 0; } } }
// Gets an attribute from an object and stores the value // in the buffer. It returns false if the attribute was // not found. Otherwise, true. HS_BOOL8 CHSInterface::AtrGet(int obj, const char *atrname) { #ifdef PENNMUSH ATTR *a; a = atr_get(obj, atrname); if (!a) { return false; } strcpy_s(m_buffer, atr_value(a)); return true; #endif #if defined(TM3) || defined(MUX) char *a; a = my_atr_get(obj, atrname); if (a && *a) { strcpy(m_buffer, a); return true; } else return false; #endif }
/** Retrieve the amount of quote remaining to a player. * Figure out a player's quota. Add the RQUOTA attribute if he doesn't * have one already. This function returns the REMAINING quota, not * the TOTAL limit. * \param who player to check. * \return player's remaining quota. */ int get_current_quota(dbref who) { ATTR *a; int i; int limit; int owned = 0; /* if he's got an RQUOTA attribute, his remaining quota is that */ a = atr_get_noparent(Owner(who), "RQUOTA"); if (a) return parse_integer(atr_value(a)); /* else, count up his objects. If he has less than the START_QUOTA, * then his remaining quota is that minus his number of current objects. * Otherwise, it's his current number of objects. Add the attribute * if he doesn't have it. */ for (i = 0; i < db_top; i++) if (Owner(i) == Owner(who)) owned++; owned--; /* don't count the player himself */ if (owned <= START_QUOTA) limit = START_QUOTA - owned; else limit = owned; (void) atr_add(Owner(who), "RQUOTA", tprintf("%d", limit), GOD, 0); return limit; }
/** Since enum adds a delim before and after the string, edit them out. */ const char * display_attr_limit(ATTR *ap) { char *ptr; char *s; if (ap->data && (ap->flags & AF_ENUM)) { ptr = atr_value(ap); *(ptr++) = '\0'; s = ptr + strlen(ptr); s--; *(s) = '\0'; return ptr; } else if (ap->data && (ap->flags & AF_RLIMIT)) { return atr_value(ap); } return "unset"; }
/** Processing related to players' last connections. * Here we check to see if a player gets a paycheck, tell them their * last connection site, and update all their LAST* attributes. * \param player dbref of player. * \param host hostname of player's current connection. * \param ip ip address of player's current connection. */ void check_last(dbref player, const char *host, const char *ip) { char *s; ATTR *a; ATTR *h; char last_time[MAX_COMMAND_LEN / 8]; char last_place[MAX_COMMAND_LEN]; /* compare to last connect see if player gets salary */ s = show_time(mudtime, 0); a = atr_get_noparent(player, "LAST"); if (a && (strncmp(atr_value(a), s, 10) != 0)) giveto(player, Paycheck(player)); /* tell the player where he last connected from */ if (!Guest(player)) { h = atr_get_noparent(player, "LASTSITE"); if (h && a) { strcpy(last_place, atr_value(h)); strcpy(last_time, atr_value(a)); notify_format(player, T("Last connect was from %s on %s."), last_place, last_time); } /* How about last failed connection */ h = atr_get_noparent(player, "LASTFAILED"); if (h && a) { strcpy(last_place, atr_value(h)); if (strlen(last_place) > 2) notify_format(player, T("Last FAILED connect was from %s."), last_place); } } /* if there is no Lastsite, then the player is newly created. * the extra variables are a kludge to work around some weird * behavior involving uncompress. */ /* set the new attributes */ (void) atr_add(player, "LAST", s, GOD, 0); (void) atr_add(player, "LASTSITE", host, GOD, 0); (void) atr_add(player, "LASTIP", ip, GOD, 0); (void) atr_add(player, "LASTFAILED", " ", GOD, 0); }
char *unparse_class (int x) { ATTR *a; static char buffer[20]; a = atr_get(sdb[x].object, CLASS_ATTR_NAME); if (a == NULL) return (char *) "#-1 BAD CLASS"; strncpy(buffer, atr_value(a), sizeof(buffer) - 1); return (buffer); }
/* board a ship through the ship object */ void board_ship(dbref player, char *which, char *code) { ATTR *a; char *bcode; dbref obj, nav, bay; int security; /* find the shipobj */ obj = match_result(player, which, TYPE_THING, MAT_NEAR_THINGS); if (!IsShipObj(obj)) { notify(player, "Can't find that ship."); return; } /* see if it's got an HSNAV */ nav = atr_parse_dbref(obj, "HSNAV"); if (!IsShip(nav)) { notify(player, "That is not a valid ship object."); return; } /* check the boarding code, if necessary */ security = atr_parse_integer(nav, "SECURITY", 0); if (security) { a = atr_get(nav, "BOARDING_CODE"); if (a) { bcode = atr_value(a); if (strcmp(bcode, code)) { notify(player, "Invalid boarding code."); return; } } } /* check for a BAY on the nav, otherwise go to the nav's location */ bay = atr_parse_dbref(nav, "BAY"); if (!RealGoodObject(bay)) { bay = Location(nav); } atr_add(player, "HSPACE", unparse_dbref(nav), hs_options.space_wiz, 0); notify_except(obj, Location(player), player, tprintf("%s boards the %s.", Name(player), Name(obj)), 0); notify_except(obj, bay, player, tprintf("%s boards through the main hatch.", Name(player)), 0); moveto(player, bay, hs_options.space_wiz, NULL); }
/** Check a player's password against a given string. * * First checks new-style formatted password strings * If that doesn't match, tries old-style SHA0 password * strings, and upgrades the stored password. * If that doesn't match, tries really-old-style crypt(3) * password strings, and upgrades the stored password. * If that doesn't work, you lose. * * \param player dbref of player. * \param password plaintext password string to check. * \retval 1 password matches (or player has no password). * \retval 0 password fails to match. */ bool password_check(dbref player, const char *password) { ATTR *a; char *saved; /* read the password and compare it */ if (!(a = atr_get_noparent(player, pword_attr))) return 1; /* No password attribute */ saved = strdup(atr_value(a)); if (!saved) return 0; if (!password_comp(saved, password)) { /* Nope. Try SHA0. */ char *passwd = mush_crypt_sha0(password); if (strcmp(saved, passwd) != 0) { /* Not SHA0 either. Try old-school crypt(); */ #ifdef HAVE_CRYPT if (strcmp(crypt(password, "XX"), saved) != 0) { /* Nope */ #endif /* HAVE_CRYPT */ /* See if it's a MUX password */ if (!check_mux_password(saved, password)) { /* As long as it's not obviously encrypted, check for a * plaintext password. */ if (strlen(password) < 4 || *password == '$' || (password[0] == 'X' && password[1] == 'X') || strcmp(saved, password)) { free(saved); return 0; } } #ifdef HAVE_CRYPT } #endif } /* Something worked. Change password to SHS-encrypted */ do_rawlog(LT_CONN, "Updating password format for player #%d", player); (void) atr_add(player, pword_attr, password_hash(password, NULL), GOD, 0); } /* Success! */ free(saved); return 1; }
void attr_write_all(PENNFILE *f) { int attrcount = 0, aliascount = 0; ATTR *a; const char *attrname; char *data; for (a = ptab_firstentry_new(&ptab_attrib, &attrname); a; a = ptab_nextentry_new(&ptab_attrib, &attrname)) { if (!strcmp(attrname, AL_NAME(a))) attrcount++; else aliascount++; } db_write_labeled_int(f, "attrcount", attrcount); for (a = ptab_firstentry_new(&ptab_attrib, &attrname); a; a = ptab_nextentry_new(&ptab_attrib, &attrname)) { if (strcmp(attrname, AL_NAME(a))) continue; /* skip aliases */ db_write_labeled_string(f, " name", AL_NAME(a)); db_write_labeled_string(f, " flags", privs_to_string(attr_privs_db, AL_FLAGS(a))); db_write_labeled_dbref(f, " creator", AL_CREATOR(a)); data = atr_value(a); db_write_labeled_string(f, " data", data); } db_write_labeled_int(f, "attraliascount", aliascount); for (a = ptab_firstentry_new(&ptab_attrib, &attrname); a; a = ptab_nextentry_new(&ptab_attrib, &attrname)) { if (!strcmp(attrname, AL_NAME(a))) continue; /* skip non-aliases */ db_write_labeled_string(f, " name", AL_NAME(a)); db_write_labeled_string(f, " alias", attrname); } }
/** Reset all of a player's player list entries (names/aliases). * This is called when a player changes name or alias. * We remove all their old entries, and add back their new ones. * \param player dbref of player * \param oldname player's former name (NULL if not changing) * \param oldalias player's former aliases (NULL if not changing) * \param name player's new name * \param alias player's new aliases */ void reset_player_list(dbref player, const char *oldname, const char *oldalias, const char *name, const char *alias) { char tbuf1[BUFFER_LEN]; char tbuf2[BUFFER_LEN]; if (!oldname) name = Name(player); if (oldalias) { mush_strncpy(tbuf1, oldalias, BUFFER_LEN); if (alias) { strncpy(tbuf2, alias, BUFFER_LEN - 1); tbuf2[BUFFER_LEN - 1] = '\0'; } else { tbuf2[0] = '\0'; } } else { /* We are not changing aliases, just name, but we need to get the * aliases anyway, since we may change name to something that's * in the alias, and thus must not be deleted. */ ATTR *a = atr_get_noparent(player, "ALIAS"); if (a) { mush_strncpy(tbuf1, atr_value(a), BUFFER_LEN); } else { tbuf1[0] = '\0'; } strcpy(tbuf2, tbuf1); } /* Delete all the old stuff */ delete_player(player, tbuf1); delete_player(player, NULL); /* Add in the new stuff */ add_player_alias(player, name); add_player_alias(player, tbuf2); }
static void ct_exit(dbref player, dbref i, warn_type flags) { dbref j, src, dst; int count = 0; int lt; /* i must be an exit, must be in a valid room, and must lead to a * different room * Remember, for exit i, Exits(i) = source room * and Location(i) = destination room */ dst = Destination(i); if ((flags & W_EXIT_UNLINKED) && (dst == NOTHING)) complain(player, i, "exit-unlinked", T("exit is unlinked; anyone can steal it")); if ((flags & W_EXIT_UNLINKED) && dst == AMBIGUOUS) { ATTR *a; const char *var = "DESTINATION"; a = atr_get(i, "DESTINATION"); if (!a) a = atr_get(i, "EXITTO"); if (a) var = "EXITTO"; if (!a) complain(player, i, "exit-unlinked", T("Variable exit has no %s attribute"), var); else { const char *x = atr_value(a); if (!x || !*x) complain(player, i, "exit-unlinked", T("Variable exit has empty %s attribute"), var); } } if (!Dark(i)) { if (flags & W_EXIT_MSGS) { lt = warning_lock_type(getlock(i, Basic_Lock)); if ((lt & W_UNLOCKED) && (!atr_get(i, "OSUCCESS") || !atr_get(i, "ODROP") || !atr_get(i, "SUCCESS"))) complain(player, i, "exit-msgs", T("possibly unlocked exit missing succ/osucc/odrop")); if ((lt & W_LOCKED) && !atr_get(i, "FAILURE")) complain(player, i, "exit-msgs", T("possibly locked exit missing fail")); } if (flags & W_EXIT_DESC) { if (!atr_get(i, "DESCRIBE")) complain(player, i, "exit-desc", T("exit is missing description")); } } src = Source(i); if (!GoodObject(src) || !IsRoom(src)) return; if (src == dst) return; /* Don't complain about exits linked to HOME or variable exits. */ if (!GoodObject(dst)) return; for (j = Exits(dst); GoodObject(j); j = Next(j)) if (Location(j) == src) { if (!(flags & W_EXIT_MULTIPLE)) return; else count++; } if ((count == 0) && (flags & W_EXIT_ONEWAY)) complain(player, i, "exit-oneway", T("exit has no return exit")); else if ((count > 1) && (flags & W_EXIT_MULTIPLE)) complain(player, i, "exit-multiple", T("exit has multiple (%d) return exits"), count); }
/* leave a flying ship through the escape pod */ void emergency_eject(dbref player) { dbref nav, pad; hship *ship; hcelestial *cel, *min_cel; ATTR *a; char *r, *s; char buff[512]; double dist, min_dist; dbref min_pad; hship *sptr, *min_ship; /* check for a BAY */ a = atr_get(Location(player), "BAY"); if (!a) { /* no BAY, see if we're next to the nav console */ ship = find_ship(player); if (ship) { if (Location(ship->objnum) != Location(player)) { notify(player, "You can't eject from here."); return; } } } else { /* there's a BAY, see if the ship is valid */ nav = parse_dbref(atr_value(a)); if (!IsShip(nav)) { notify(player, "You can't eject from here."); return; } ship = find_ship_by_nav(nav); } if (!ship) { notify(player, "You can't eject from here."); return; } /* only eject when flying, not when landing or docking */ if (!ship->uid || ship->landed || ship->docked) { notify(player, "You may only eject while flying."); return; } /* find a planet with a drop pad */ min_pad = NOTHING; min_dist = 1000000.0; min_cel = NULL; for (cel = ship->uid->head_celestial; cel; cel = cel->next) { if (!HasFlag(cel->type, HS_PLANET)) continue; pad = atr_parse_dbref(cel->objnum, "DROPPADS"); if (!RealGoodObject(pad)) continue; dist = ship_celestial_distance(ship, cel); if (dist < min_dist) { min_dist = dist; min_pad = pad; min_cel = cel; } } min_ship = NULL; for (sptr = ship->uid->head_ship; sptr; sptr = sptr->next) { if (min_cel) break; if (!HasFlag(sptr->type, HS_STATION | HS_CAPITAL)) continue; pad = atr_parse_dbref(sptr->objnum, "BAY"); if (!RealGoodObject(pad)) continue; dist = ship_distance(ship, sptr); if (dist < min_dist) { min_cel = NULL; min_ship = sptr; min_dist = dist; min_pad = pad; } } if (!RealGoodObject(min_pad)) { notify(player, "There is nowhere to eject to!"); return; } /* finish up by setting HSPACE and notifying everybody of the move */ if (min_ship) { atr_add(player, "HSPACE", unparse_dbref(min_ship->objnum), hs_options.space_wiz, 0); } else if (min_cel) { atr_add(player, "HSPACE", unparse_dbref(min_cel->objnum), hs_options.space_wiz, 0); } else { SPACEWALL("Weird problem in eject."); notify(player, "Bad space object. Contact an administrator."); return; } notify_except(Location(player), Location(player), player, tprintf("%s ejects in an emergency escape pod!", Name(player)), 0); notify_except(min_pad, min_pad, player, tprintf("%s crash lands in an emergency escape pod!", Name(player)), 0); if (min_ship) { notify_consoles(min_ship, tprintf("%s%s-%s Emergency ejection pod automatically tractored into the docking back.", ANSI_HILITE, ANSI_GREEN, ANSI_NORMAL)); } moveto(player, min_pad, hs_options.space_wiz, NULL); }
/* leave a landed/docked ship through the hatch */ void disembark(dbref player) { dbref nav, obj, newobj; hship *ship; ATTR *a; int security; /* check if we can disembark from here */ a = atr_get(Location(player), "BAY"); if (!a) { /* no BAY, check if we're near the nav console */ ship = find_ship(player); if (ship) { if (Location(ship->objnum) != Location(player)) { notify(player, "You can't disembark from here."); return; } } } else { /* there's a BAY here, make sure it's a good one */ nav = parse_dbref(atr_value(a)); if (!IsShip(nav)) { notify(player, "You can't disembark from here."); return; } ship = find_ship_by_nav(nav); } if (!ship) { notify(player, "You can't disembark from here."); return; } /* no ditching in space, or early after launching, or prematurely when landing */ if ((ship->uid || ship->landing || ship->launching) && !ship->linked) { notify(player, "You can't disembark while in space."); return; } obj = atr_parse_dbref(ship->objnum, "SHIPOBJ"); if (!RealGoodObject(obj)) { notify(player, "This ship can not be disembarked."); return; } /* check whether we're docking or landing, save the new space object */ if (ship->landed) { newobj = ship->landed->objnum; } else if (ship->docked) { newobj = ship->docked->objnum; } else if (ship->linked) { newobj = ship->linked->objnum; } else { notify(player, "You can't disembark while in space."); return; } if (ship->linked) { /* check the boarding code, if necessary */ security = atr_parse_integer(ship->linked->objnum, "SECURITY", 0); if (security) { notify(player, "Unable to use boarding link while the other ship has security enabled."); return; } security = atr_parse_integer(ship->objnum, "SECURITY", 0); if (security) { notify(player, "Unable to use boarding link while security is enabled."); return; } obj = atr_parse_dbref(ship->linked->objnum, "BAY"); if (!RealGoodObject(obj)) obj = Location(ship->linked->objnum); moveto(player, obj, hs_options.space_wiz, NULL); } else { moveto(player, Location(obj), hs_options.space_wiz, NULL); } /* finish up by setting HSPACE attribute and notifying everybody about the move */ atr_add(player, "HSPACE", unparse_dbref(newobj), hs_options.space_wiz, 0); notify_except(obj, Location(player), player, tprintf("%s disembarks through the main hatch.", Name(player)), 0); notify_except(obj, Location(obj), player, tprintf("%s disembarks from the %s.", Name(player), Name(obj)), 0); return; }
/* send a standard radio communication */ void send_com(dbref from, char *arg_left, char *arg_right) { dbref com, obj; hship *ship; hcelestial *cel; huniverse *uid; double xmit, rcv; char contact[32]; char *r, *s; char buff[128]; ATTR *a; double sx, sy, sz, tx, ty, tz, dist; char pre[128]; char *mesg; int sent_to_from, send_to_com; if (!IsComm(from)) { notify(from, "You do not have the HS_COMM flag."); return; } uid = NULL; obj = atr_parse_dbref(from, "HSPACE"); if (!RealGoodObject(obj)) { notify(from, "You do not have a valid space id. Board, disembark, eject, or man a console."); return; } if (IsShip(obj)) { ship = find_ship_by_nav(obj); if (!ship) { notify(from, "Your space id is not a valid ship."); return; } if (ship->uid) { sx = ship->x; sy = ship->y; sz = ship->z; uid = ship->uid; } else if (ship->landed) { sx = ship->landed->x; sy = ship->landed->y; sz = ship->landed->z; uid = ship->landed->uid; } else if (ship->docked) { sx = ship->docked->x; sy = ship->docked->y; sz = ship->docked->z; uid = ship->docked->uid; } strncpy(contact, ship_name(ship), 10); } else if (IsCelestial(obj)) { cel = find_celestial(obj); if (!cel) { notify(from, "Your space id is not a valid celestial."); return; } sx = cel->x; sy = cel->y; sz = cel->z; uid = cel->uid; strncpy(contact, celestial_name(cel), 10); } contact[10] = '\0'; if (!uid) { notify(from, "Your space id does not have a valid uid."); return; } if (arg_left && arg_right && *arg_right) { xmit = strtod(arg_left, &s); if (s && *s) { return; } mesg = arg_right; } else { xmit = atr_parse_double(from, "TRANSMIT", 0.0); mesg = arg_left; } if (xmit < 100.0 || xmit > 999.9) { notify(from, "Transmission frequency must be between 100 and 999 MHz."); return; } a = atr_get(from, "CALLSIGN"); if (!a) { snprintf(pre, 127, "%s%s[%s%5.1f MHz%s%s]-[%s%-10s%s]-[%s ", ANSI_HILITE, ANSI_BLUE, ANSI_NORMAL, xmit, ANSI_HILITE, ANSI_BLUE, ANSI_GREEN, contact, ANSI_BLUE, ANSI_NORMAL); } else { snprintf(pre, 127, "%s%s[%s%5.1f MHz%s%s]-[%s%-10s%s]-[%s %s<%s%s%s>%s ", ANSI_HILITE, ANSI_BLUE, ANSI_NORMAL, xmit, ANSI_HILITE, ANSI_BLUE, ANSI_GREEN, contact, ANSI_BLUE, ANSI_NORMAL, ANSI_CYAN, ANSI_NORMAL, atr_value(a), ANSI_CYAN, ANSI_NORMAL); } /* go through the comm list and check each one */ sent_to_from = 0; for (com = 0; com < db_top; com++) { if (!IsComm(com)) continue; /* check if the user is in the same uid */ obj = atr_parse_dbref(com, "HSPACE"); if (IsShip(obj)) { ship = find_ship_by_nav(obj); if (!ship) continue; if (ship->uid && ship->uid != uid) continue; else if (ship->landed && ship->landed->uid != uid) continue; else if (ship->docked && ship->docked->uid != uid) continue; if (ship->uid) { tx = ship->x; ty = ship->y; tz = ship->z; } else if (ship->landed) { tx = ship->landed->x; ty = ship->landed->y; tz = ship->landed->z; } else if (ship->docked) { tx = ship->docked->x; ty = ship->docked->y; tz = ship->docked->z; } } else if (IsCelestial(obj)) { cel = find_celestial(obj); if (!cel) continue; if (cel->uid != uid) continue; tx = cel->x; ty = cel->y; tz = cel->z; } else { continue; } dist = dist3d(sx, sy, sz, tx, ty, tz) / hs_options.max_comm_dist; if (dist > 1.0) continue; a = atr_get(com, "FREQUENCY"); if (!a) continue; /* check all frequencies to see if we need to send to this com */ send_to_com = 0; snprintf(buff, 127, atr_value(a)); s = buff; while (s) { r = split_token(&s, ' '); rcv = parse_number(r); /* check to see if we're on the right frequency */ if (fabs(rcv - xmit) < 0.1) { send_to_com = 1; break; } } if (send_to_com) { if (com == from) sent_to_from = 1; notify_format(com, "%s%s%s%s]%s", pre, decay_msg(mesg, dist), ANSI_HILITE, ANSI_BLUE, ANSI_NORMAL); } } if (!sent_to_from) { notify_format(from, "You send \"%s\" on frequency %5.1f.", mesg, xmit); } }
static int grep_helper(dbref player, dbref thing __attribute__ ((__unused__)), dbref parent __attribute__ ((__unused__)), char const *pattern __attribute__ ((__unused__)), ATTR *attr, void *args) { struct grep_data *gd = args; char *s; char buff[BUFFER_LEN]; char *bp = buff; int matched = 0; int cs; ansi_string *aval = NULL, *repl = NULL; cs = ((gd->flags & GREP_NOCASE) == 0); s = atr_value(attr); if (gd->flags & GREP_WILD) { if ((matched = quick_wild_new(gd->findstr, s, cs))) { /* Since, in order for a wildcard match to succeed, the _entire attribute_ value had to match the pattern, not just a substring, highlighting is totally pointless */ strcpy(buff, s); } } else { aval = parse_ansi_string(s); s = aval->text; repl = parse_ansi_string(tprintf("%s%s%s", ANSI_HILITE, gd->findstr, ANSI_END)); while (s && *s) { if (!
/** Attempt to register a new player at the connect screen. * If registration is allowed, a new player object is created with * a random password which is emailed to the registering player. * \param name name of player to register. * \param email email address to send registration details. * \param host host from which registration is being attempted. * \param ip ip address from which registration is being attempted. * \return dbref of created player or NOTHING if creation failed. */ dbref email_register_player(DESC *d, const char *name, const char *email, const char *host, const char *ip) { char *p; char passwd[20]; static char elems[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; int i, len; bool resend = 0; dbref player = NOTHING; FILE *fp; size_t NELEMS = sizeof(elems) - 1; char sbuff[260]; if (!check_fails(ip)) { return NOTHING; } if (strlen(options.sendmail_prog) == 0) return NOTHING; if (!ok_player_name(name, NOTHING, NOTHING)) { /* Check for re-registration request */ player = lookup_player(name); if (GoodObject(player)) { ATTR *a; a = atr_get(player, "LASTLOGOUT"); if (!a) { a = atr_get(player, "REGISTERED_EMAIL"); if (a && !strcasecmp(atr_value(a), email)) resend = 1; } } if (!resend) { do_log(LT_CONN, 0, 0, "Failed registration (bad name) from %s", host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, mark_failed(ip), "register: bad name", name); return NOTHING; } } if (!resend) { /* Make sure that the email address is kind of valid. A valid * address must contain a @. Let the mailer sort it out beyond * that. Also, to prevent someone from using the MUSH to mailbomb * another site, let's make sure that the site to which the user * wants the email sent is also allowed to use the register * command. If there's an @, we check whatever's after the last @ * (since @foo.bar:user@host is a valid email). */ if ((p = strrchr(email, '@'))) { p++; if (!Site_Can_Register(p)) { if (!Deny_Silent_Site(p, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "Failed registration (bad site in email: %s) from %s", email, host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, mark_failed(ip), "register: bad site in email", name); } return NOTHING; } } else { if (!Deny_Silent_Site(host, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "Failed registration (bad email: %s) from %s", email, host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, mark_failed(ip), "register: sitelocked host", name); } return NOTHING; } if (DBTOP_MAX && (db_top >= DBTOP_MAX + 1) && (first_free == NOTHING)) { /* Oops, out of db space! */ do_log(LT_CONN, 0, 0, "Failed registration (no db space) from %s", host); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s", d->descriptor, ip, count_failed(ip), "register: no db space left to create!", name); return NOTHING; } } /* Come up with a random password of length 7-12 chars */ len = get_random_u32(7, 12); for (i = 0; i < len; i++) passwd[i] = elems[get_random_u32(0, NELEMS - 1)]; passwd[len] = '\0'; /* If we've made it here, we can send the email and create the * character. Email first, since that's more likely to go bad. * Some security precautions we'll take: * 1) We'll use sendmail -t, so we don't pass user-given values to a shell. * 2) We'll cross our fingers and hope nobody uses this to spam. */ release_fd(); snprintf(sbuff, sizeof sbuff, "%s -t", options.sendmail_prog); if ((fp = popen(sbuff, "w")) == NULL) { do_log(LT_CONN, 0, 0, "Failed registration of %s by %s: unable to open sendmail", name, email); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s,%d", d->descriptor, ip, count_failed(ip), "register: Unable to open sendmail!", name, 1); reserve_fd(); return NOTHING; } fprintf(fp, "Subject: "); fprintf(fp, T("[%s] Registration of %s\n"), MUDNAME, name); fprintf(fp, "To: %s\n", email); fprintf(fp, "Precedence: junk\n"); fprintf(fp, "\n"); fprintf(fp, T("This is an automated message.\n")); fprintf(fp, "\n"); fprintf(fp, T("Your requested player, %s, has been created.\n"), name); fprintf(fp, T("The password is %s\n"), passwd); fprintf(fp, "\n"); fprintf(fp, T("To access this character, connect to %s and type:\n"), MUDNAME); fprintf(fp, "\tconnect \"%s\" %s\n", name, passwd); fprintf(fp, "\n"); i = pclose(fp); reserve_fd(); if (i != 0) { /* Mailer exited with an error code. Log it. */ do_rawlog( LT_CONN, "When attempting to email a password to a newly registered player,\n" "\tthe mailer exited with error code %d.\n" "\t(Check /usr/include/sysexits.h if present for the meaning.)", i); queue_event(SYSEVENT, "SOCKET`CREATEFAIL", "%d,%s,%d,%s,%s,%d", d->descriptor, ip, count_failed(ip), "register: Unable to send email", name, i); return NOTHING; } else if (resend) { /* Reset the password */ (void) atr_add(player, pword_attr, password_hash(passwd, NULL), GOD, 0); return player; } else { /* Ok, all's well, make a player */ player = make_player(name, passwd, host, ip); queue_event(SYSEVENT, "PLAYER`CREATE", "%s,%s,%s,%d,%s", unparse_objid(player), name, "register", d->descriptor, email); (void) atr_add(player, "REGISTERED_EMAIL", email, GOD, 0); return player; } }
/** Parse a softcode timezone request. * * \verbatim * * If arg is a objid, look up that object's @TZ attribute and parse * that. Otherwise, parse arg. * * If an object doesn't have a @TZ set, offset is set to 0 and tznotset to 1, to be able to tell * that case apart from a UTC timezone. * * If a timezone database is present, try to read the given zone from * it. Integers are treated as 'Etc/GMT[+-]N' first. * * If no tzinfo database, or reading the given zone from one fails, * and the arg is an integer, treat it as the number of hours * difference from GMT. Otherwise fail. * * \endverbatim * * \param arg The string to parse for a dbref, number or symbolic tz name * \param when When to calculate the offset for. * \param res Structure to store the parsed results in. * \return 1 for success, 0 for failure in parsing the time zone. */ bool parse_timezone_arg(const char *arg, time_t when, struct tz_result *res) { if (!res) return 0; memset(res, 0, sizeof *res); res->tz_when = when; if (strcasecmp(arg, "UTC") == 0) { res->tz_utc = 1; return 1; } else if (is_objid(arg)) { ATTR *a; dbref thing = parse_objid(arg); if (!RealGoodObject(thing)) return 0; a = atr_get(thing, "TZ"); if (!a) { /* No timezone attribute isn't an error. Just use the server's zone. */ res->tz_attr_missing = 1; return 1; } arg = atr_value(a); } #ifdef HAVE_ZONEINFO { struct tzinfo *tz = NULL; static char tz_path[BUFFER_LEN]; if (is_valid_tzname(arg)) { tz = read_tzfile(arg); snprintf(tz_path, sizeof tz_path, ":%s", arg); } else if (is_strict_integer(arg)) { int offset; char tzname[100]; offset = parse_integer(arg); /* GMT-8 is 8 hours ahead, GMT+8 is 8 hours behind, which makes no sense to me. */ offset = -offset; snprintf(tzname, sizeof tzname, "Etc/GMT%+d", offset); tz = read_tzfile(tzname); snprintf(tz_path, sizeof tz_path, ":%s", tzname); } if (tz) { res->tz_offset = offset_for_tzinfo(tz, when); free_tzinfo(tz); res->tz_name = tz_path; res->tz_has_file = 1; return 1; } /* Fall through to gross numeric offset on failure */ } #endif if (is_strict_number(arg)) { double n = parse_number(arg); if (fabs(n) >= 24.0) return 0; res->tz_offset = floor(n * 3600.0); return 1; } return 0; }
/** Populate a ufun_attrib struct from an obj/attr pair. * \verbatim Given an attribute [<object>/]<name> pair (which may include #lambda), * fetch its value, owner (thing), and pe_flags, and store in the struct * pointed to by ufun * \endverbatim * \param attrstring The obj/name of attribute. * \param executor Dbref of the executing object. * \param ufun Pointer to an allocated ufun_attrib struct to fill in. * \param flags A bitwise or of desired UFUN_* flags. * \return 0 on failure, true on success. */ bool fetch_ufun_attrib(const char *attrstring, dbref executor, ufun_attrib * ufun, int flags) { char *thingname, *attrname; char astring[BUFFER_LEN]; ATTR *attrib; if (!ufun) return 0; ufun->contents[0] = '\0'; ufun->errmess = (char *) ""; ufun->thing = executor; ufun->pe_flags = PE_UDEFAULT; ufun->ufun_flags = flags; ufun->thing = executor; thingname = NULL; if (!attrstring) return 0; strncpy(astring, attrstring, BUFFER_LEN); /* Split obj/attr */ if ((flags & UFUN_OBJECT) && ((attrname = strchr(astring, '/')) != NULL)) { thingname = astring; *(attrname++) = '\0'; } else { attrname = astring; } if (thingname && (flags & UFUN_LAMBDA) && (strcasecmp(thingname, "#lambda") == 0 || strncasecmp(thingname, "#apply", 6) == 0)) { /* It's a lambda. */ ufun->ufun_flags &= ~UFUN_NAME; ufun->thing = executor; if (strcasecmp(thingname, "#lambda") == 0) mush_strncpy(ufun->contents, attrname, BUFFER_LEN); else { /* #apply */ char *ucb = ufun->contents; unsigned nargs = 1, n; thingname += 6; if (*thingname) nargs = parse_uinteger(thingname); /* Limit between 1 and 10 arguments (%0-%9) */ if (nargs == 0) nargs = 1; if (nargs > 10) nargs = 10; safe_str(attrname, ufun->contents, &ucb); safe_chr('(', ufun->contents, &ucb); for (n = 0; n < nargs; n++) { if (n > 0) safe_chr(',', ufun->contents, &ucb); safe_format(ufun->contents, &ucb, "%%%u", n); } safe_chr(')', ufun->contents, &ucb); *ucb = '\0'; } ufun->attrname[0] = '\0'; return 1; } if (thingname) { /* Attribute is on something else. */ ufun->thing = noisy_match_result(executor, thingname, NOTYPE, MAT_EVERYTHING); if (!GoodObject(ufun->thing)) { ufun->errmess = (char *) "#-1 INVALID OBJECT"; return 0; } } attrib = (ATTR *) atr_get(ufun->thing, upcasestr(attrname)); if (attrib && AF_Internal(attrib)) { /* Regardless of whether we're doing permission checks, we should * never be showing internal attributes here */ attrib = NULL; } /* An empty attrib is the same as no attrib. */ if (attrib == NULL) { if (flags & UFUN_REQUIRE_ATTR) { if (!(flags & UFUN_IGNORE_PERMS) && !Can_Examine(executor, ufun->thing)) ufun->errmess = e_atrperm; return 0; } else { mush_strncpy(ufun->attrname, attrname, ATTRIBUTE_NAME_LIMIT + 1); return 1; } } if (!(flags & UFUN_IGNORE_PERMS) && !Can_Read_Attr(executor, ufun->thing, attrib)) { ufun->errmess = e_atrperm; return 0; } if (!(flags & UFUN_IGNORE_PERMS) && !CanEvalAttr(executor, ufun->thing, attrib)) { ufun->errmess = e_perm; return 0; } /* DEBUG attributes */ if (AF_NoDebug(attrib)) ufun->pe_flags |= PE_NODEBUG; /* No_Debug overrides Debug */ else if (AF_Debug(attrib)) ufun->pe_flags |= PE_DEBUG; if (flags & UFUN_NAME) { if (attrib->flags & AF_NONAME) ufun->ufun_flags &= ~UFUN_NAME; else if (attrib->flags & AF_NOSPACE) ufun->ufun_flags |= UFUN_NAME_NOSPACE; } /* Populate the ufun object */ mush_strncpy(ufun->contents, atr_value(attrib), BUFFER_LEN); mush_strncpy(ufun->attrname, AL_NAME(attrib), ATTRIBUTE_NAME_LIMIT + 1); /* We're good */ return 1; }
/** Check an attribute's value against /limit or /enum restrictions. * \param player Player attempting to set the attribute. Used for notify() * \param name the attribute name. * \param value The desired attribute value. * \retval The new value to set if valid, NULL if not. */ const char * check_attr_value(dbref player, const char *name, const char *value) { /* Check for attribute limits and enums. */ ATTR *ap; char *attrval; pcre *re; int subpatterns; const char *errptr; int erroffset; char *ptr, *ptr2; char delim; int len; static char buff[BUFFER_LEN]; char vbuff[BUFFER_LEN]; if (!name || !*name) return value; if (!value) return value; upcasestr((char *) name); ap = (ATTR *) ptab_find_exact(&ptab_attrib, name); if (!ap) return value; attrval = atr_value(ap); if (!attrval) { return value; } if (ap->flags & AF_RLIMIT) { re = pcre_compile(remove_markup(attrval, NULL), PCRE_CASELESS, &errptr, &erroffset, tables); if (!re) return value; subpatterns = pcre_exec(re, default_match_limit(), value, strlen(value), 0, 0, NULL, 0); free(re); if (subpatterns >= 0) { return value; } else { notify(player, T("Attribute value does not match the /limit regexp.")); return NULL; } } else if (ap->flags & AF_ENUM) { /* Delimiter is always the first character of the enum string. * and the value cannot have the delimiter in it. */ delim = *attrval; if (!*value || strchr(value, delim)) { notify_format(player, T("Value for %s needs to be one of: %s"), ap->name, display_attr_limit(ap)); return NULL; } /* We match the enum case-insensitively, BUT we use the case * that is defined in the enum, so we copy the attr value * to buff and use that. */ snprintf(buff, BUFFER_LEN, "%s", attrval); upcasestr(buff); len = strlen(value); snprintf(vbuff, BUFFER_LEN, "%c%s%c", delim, value, delim); upcasestr(vbuff); ptr = strstr(buff, vbuff); if (!ptr) { *(vbuff + len + 1) = '\0'; /* Remove the second delim */ ptr = strstr(buff, vbuff); } /* Do we have a match? */ if (ptr) { /* ptr is pointing at the delim before the value. */ ptr++; ptr2 = strchr(ptr, delim); if (!ptr2) return NULL; /* Shouldn't happen, but sanity check. */ /* Now we need to copy over the _original case_ version of the * enumerated string. Nasty pointer arithmetic. */ strncpy(buff, attrval + (ptr - buff), (int) (ptr2 - ptr)); buff[ptr2 - ptr] = '\0'; return buff; } else { notify_format(player, T("Value for %s needs to be one of: %s"), ap->name, display_attr_limit(ap)); return NULL; } } return value; }