/* Clone an object. The new object is owned by the cloning player */ static dbref clone_object(dbref player, dbref thing, const char *newname, int preserve) { dbref clone; clone = new_object(); Owner(clone) = Owner(player); Name(clone) = NULL; if (newname && *newname) set_name(clone, newname); else set_name(clone, Name(thing)); s_Pennies(clone, Pennies(thing)); AttrCount(clone) = 0; List(clone) = NULL; Locks(clone) = NULL; clone_locks(player, thing, clone); Zone(clone) = Zone(thing); Parent(clone) = Parent(thing); Flags(clone) = clone_flag_bitmask("FLAG", Flags(thing)); if (!preserve) { clear_flag_internal(clone, "WIZARD"); clear_flag_internal(clone, "ROYALTY"); Warnings(clone) = 0; /* zap warnings */ Powers(clone) = new_flag_bitmask("POWER"); /* zap powers */ } else { Powers(clone) = clone_flag_bitmask("POWER", Powers(thing)); Warnings(clone) = Warnings(thing); if (Wizard(clone) || Royalty(clone) || Warnings(clone) || !null_flagmask("POWER", Powers(clone))) notify(player, T ("Warning: @CLONE/PRESERVE on an object with WIZ, ROY, @powers, or @warnings.")); } /* We give the clone the same modification time that its * other clone has, but update the creation time */ ModTime(clone) = ModTime(thing); CreTime(clone) = mudtime; Type(clone) = Type(thing); Contents(clone) = Location(clone) = Next(clone) = NOTHING; if (IsRoom(thing)) Exits(clone) = NOTHING; else Home(clone) = Home(thing); atr_cpy(clone, thing); queue_event(player, "OBJECT`CREATE", "%s,%s", unparse_objid(clone), unparse_objid(thing)); return clone; }
/** Create an exit. * This function opens an exit and optionally links it. * \param player the enactor. * \param direction the name of the exit. * \param linkto the room to link to, as a string. * \param pseudo a phony location for player if a back exit is needed. This is bpass by do_open() as the source room of the back exit. * \return dbref of the new exit, or NOTHING. */ dbref do_real_open(dbref player, const char *direction, const char *linkto, dbref pseudo) { dbref loc = (pseudo != NOTHING) ? pseudo : (IsExit(player) ? Source(player) : (IsRoom(player) ? player : Location(player))); dbref new_exit; char *flaglist, *flagname; char flagbuff[BUFFER_LEN]; char *name = NULL; char *alias = NULL; if (!command_check_byname(player, "@dig")) { notify(player, T("Permission denied.")); return NOTHING; } if ((loc == NOTHING) || (!IsRoom(loc))) { notify(player, T("Sorry, you can only make exits out of rooms.")); return NOTHING; } if (Going(loc)) { notify(player, T("You can't make an exit in a place that's crumbling.")); return NOTHING; } if (!*direction) { notify(player, T("Open where?")); return NOTHING; } else if (ok_object_name ((char *) direction, player, NOTHING, TYPE_EXIT, &name, &alias) < 1) { notify(player, T("That's a strange name for an exit!")); if (name) mush_free(name, "name.newname"); if (alias) mush_free(alias, "name.newname"); return NOTHING; } if (!Open_Anywhere(player) && !controls(player, loc)) { notify(player, T("Permission denied.")); } else if (can_pay_fees(player, EXIT_COST)) { /* create the exit */ new_exit = new_object(); /* initialize everything */ set_name(new_exit, name); if (alias && *alias != ALIAS_DELIMITER) atr_add(new_exit, "ALIAS", alias, player, 0); Owner(new_exit) = Owner(player); Zone(new_exit) = Zone(player); Source(new_exit) = loc; Type(new_exit) = TYPE_EXIT; Flags(new_exit) = new_flag_bitmask("FLAG"); strcpy(flagbuff, options.exit_flags); flaglist = trim_space_sep(flagbuff, ' '); if (*flaglist != '\0') { while (flaglist) { flagname = split_token(&flaglist, ' '); twiddle_flag_internal("FLAG", new_exit, flagname, 0); } } mush_free(name, "name.newname"); if (alias) mush_free(alias, "name.newname"); /* link it in */ PUSH(new_exit, Exits(loc)); /* and we're done */ notify_format(player, T("Opened exit %s"), unparse_dbref(new_exit)); /* check second arg to see if we should do a link */ if (linkto && *linkto != '\0') { notify(player, T("Trying to link...")); if ((loc = check_var_link(linkto)) == NOTHING) loc = parse_linkable_room(player, linkto); if (loc != NOTHING) { if (!payfor(player, LINK_COST)) { notify_format(player, T("You don't have enough %s to link."), MONIES); } else { /* it's ok, link it */ Location(new_exit) = loc; notify_format(player, T("Linked exit #%d to #%d"), new_exit, loc); } } } current_state.exits++; local_data_create(new_exit); queue_event(player, "OBJECT`CREATE", "%s", unparse_objid(new_exit)); return new_exit; } if (name) mush_free(name, "name.newname"); if (alias) mush_free(alias, "name.newname"); return NOTHING; }
/** Create a thing. * \verbatim * This is the top-level function for @create. * \endverbatim * \param player the enactor. * \param name name of thing to create. * \param cost pennies spent in creation. * \paran newdbref the (unparsed) dbref to give the object, or NULL to use the next free * \return dbref of new thing, or NOTHING. */ dbref do_create(dbref player, char *name, int cost, char *newdbref) { dbref loc; dbref thing; char *flaglist, *flagname; char flagbuff[BUFFER_LEN]; if (*name == '\0') { notify(player, T("Create what?")); return NOTHING; } else if (!ok_name(name, 0)) { notify(player, T("That's a silly name for a thing!")); return NOTHING; } else if (cost < OBJECT_COST) { cost = OBJECT_COST; } if (!make_first_free_wrapper(player, newdbref)) { return NOTHING; } if (can_pay_fees(player, cost)) { /* create the object */ thing = new_object(); /* initialize everything */ set_name(thing, name); if (!IsExit(player)) /* Exits shouldn't have contents! */ Location(thing) = player; else Location(thing) = Source(player); Owner(thing) = Owner(player); Zone(thing) = Zone(player); s_Pennies(thing, cost); Type(thing) = TYPE_THING; Flags(thing) = new_flag_bitmask("FLAG"); strcpy(flagbuff, options.thing_flags); flaglist = trim_space_sep(flagbuff, ' '); if (*flaglist != '\0') { while (flaglist) { flagname = split_token(&flaglist, ' '); twiddle_flag_internal("FLAG", thing, flagname, 0); } } /* home is here (if we can link to it) or player's home */ if ((loc = Location(player)) != NOTHING && (controls(player, loc) || Abode(loc))) { Home(thing) = loc; /* home */ } else { Home(thing) = Home(player); /* home */ } /* link it in */ if (!IsExit(player)) PUSH(thing, Contents(player)); else PUSH(thing, Contents(Source(player))); /* and we're done */ notify_format(player, T("Created: Object %s."), unparse_dbref(thing)); current_state.things++; local_data_create(thing); queue_event(player, "OBJECT`CREATE", "%s", unparse_objid(thing)); return thing; } return NOTHING; }
/** Create a room. * \verbatim * This is the top-level interface for @dig. * \endverbatim * \param player the enactor. * \param name the name of the room to create. * \param argv array of additional arguments to command * (exit forward,exit back,newdbref) * \param tport if 1, teleport the player to the new room. * \return dbref of new room, or NOTHING. */ dbref do_dig(dbref player, const char *name, char **argv, int tport) { dbref room; char *flaglist, *flagname; char flagbuff[BUFFER_LEN]; char *newdbref = NULL; if (argv[3] && *argv[3]) { newdbref = argv[3]; } /* we don't need to know player's location! hooray! */ if (*name == '\0') { notify(player, T("Dig what?")); } else if (!ok_name(name, 0)) { notify(player, T("That's a silly name for a room!")); } else if (can_pay_fees(player, ROOM_COST)) { if (!make_first_free_wrapper(player, newdbref)) { return NOTHING; } room = new_object(); /* Initialize everything */ set_name(room, name); Owner(room) = Owner(player); Zone(room) = Zone(player); Type(room) = TYPE_ROOM; Flags(room) = new_flag_bitmask("FLAG"); strcpy(flagbuff, options.room_flags); flaglist = trim_space_sep(flagbuff, ' '); if (*flaglist != '\0') { while (flaglist) { flagname = split_token(&flaglist, ' '); twiddle_flag_internal("FLAG", room, flagname, 0); } } notify_format(player, T("%s created with room number %d."), name, room); if (argv[1] && *argv[1]) { char nbuff[MAX_COMMAND_LEN]; sprintf(nbuff, "#%d", room); do_real_open(player, argv[1], nbuff, NOTHING); } if (argv[2] && *argv[2]) { do_real_open(player, argv[2], "here", room); } current_state.rooms++; local_data_create(room); if (tport) { /* We need to use the full command, because we need NO_TEL * and Z_TEL checking */ char roomstr[MAX_COMMAND_LEN]; sprintf(roomstr, "#%d", room); do_teleport(player, "me", roomstr, 0, 0); /* if flag, move the player */ } queue_event(player, "OBJECT`CREATE", "%s", unparse_objid(room)); return room; } return NOTHING; }
/** Rename something. * \verbatim * This implements @name. * \endverbatim * \param player the enactor. * \param name current name of object to rename. * \param newname_ new name for object. */ void do_name(dbref player, const char *name, char *newname_) { dbref thing; char oldname[BUFFER_LEN]; char *newname = NULL; char *alias = NULL; PE_REGS *pe_regs; if ((thing = match_controlled(player, name)) == NOTHING) return; /* check for bad name */ if ((*newname_ == '\0') || strchr(newname_, '[')) { notify(player, T("Give it what new name?")); return; } switch (Typeof(thing)) { case TYPE_PLAYER: switch (ok_object_name (newname_, player, thing, TYPE_PLAYER, &newname, &alias)) { case OPAE_INVALID: case OPAE_NULL: notify(player, T("You can't give a player that name or alias.")); if (newname) mush_free(newname, "name.newname"); if (alias) mush_free(alias, "name.newname"); return; case OPAE_TOOMANY: notify(player, T("Too many aliases.")); mush_free(newname, "name.newname"); return; case OPAE_SUCCESS: break; } break; case TYPE_EXIT: if (ok_object_name(newname_, player, thing, TYPE_EXIT, &newname, &alias) != OPAE_SUCCESS) { notify(player, T("That is not a reasonable name.")); if (newname) mush_free(newname, "name.newname"); if (alias) mush_free(alias, "name.newname"); return; } break; case TYPE_THING: case TYPE_ROOM: if (!ok_name(newname_, 0)) { notify(player, T("That is not a reasonable name.")); return; } newname = mush_strdup(trim_space_sep(newname_, ' '), "name.newname"); break; default: /* Should never occur */ notify(player, T("I don't see that here.")); return; } /* Actually change it */ mush_strncpy(oldname, Name(thing), BUFFER_LEN); if (IsPlayer(thing)) { do_log(LT_CONN, 0, 0, "Name change by %s(#%d) to %s", Name(thing), thing, newname); if (Suspect(thing) && strcmp(Name(thing), newname) != 0) flag_broadcast("WIZARD", 0, T("Broadcast: Suspect %s changed name to %s."), Name(thing), newname); reset_player_list(thing, Name(thing), NULL, newname, NULL); } set_name(thing, newname); if (alias) { if (*alias == ALIAS_DELIMITER) { do_set_atr(thing, "ALIAS", NULL, player, 0); } else { /* New alias to set */ do_set_atr(thing, "ALIAS", alias, player, 0); } mush_free(alias, "name.newname"); } queue_event(player, "OBJECT`RENAME", "%s,%s,%s", unparse_objid(thing), newname, oldname); if (!AreQuiet(player, thing)) notify(player, T("Name set.")); pe_regs = pe_regs_create(PE_REGS_ARG, "do_name"); pe_regs_setenv_nocopy(pe_regs, 0, oldname); pe_regs_setenv_nocopy(pe_regs, 1, newname); real_did_it(player, thing, NULL, NULL, "ONAME", NULL, "ANAME", NOTHING, pe_regs, NA_INTER_PRESENCE, AN_SYS); pe_regs_free(pe_regs); mush_free(newname, "name.newname"); }
/** 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; } }