/*
// File: /cmds/xtra/_eval.c
// Purpose: Evaluates a string as LPC commands
// Credits:
// 92-12-07 Pallando (aka Douglas Reay) created this command.
// This file is a part of the TMI distribution mudlib.
// Please retain this header if you use this code.
// 92-12-08 Pallando changed it not to use /tmp on idea from Watcher
// 92-12-12 Pallando changed it to use the identify() simul_efun
// 93-04-15 Pallando added final desting of object on idea from Robocoder
// 93-05-28 Artagel added strict typing
// 93-06-24 Pallando changed it to use /open on idea from Mobydick
// 93-07-16 Robocoder added some evil admin logging
// 93-10-01 Rust added doith() to make debugging easier.
// 93-10-23 Mobydick added the ADMIN_ONLY define.
// 94-04-27 Robocoder added (liberally hacked in) Grendel's set euid code
// 94-06-29 Yavie added ansi.h on a suggestion from Crunch.
// 95-01-23 Fixed a type check for when we hit 20.26 Leto
*/
#include <config.h>
#include <uid.h>
#include <mudlib.h>
#include <commands.h>
#include <logs.h>
#include <ansi.h>
// #define ADMIN_ONLY 1
inherit DAEMON;
#define SYNTAX "Syntax: \"eval [-uid] <lpc commands>\".\n"
#define LOGS ({ \
user_path( geteuid( this_player() ) ) + "log", \
LOG_DIR "log", \
LOG_DIR "debug.log", \
})
//prototype
string doith(string arg);
// If your mud allows objects in /tmp to load, use that, otherwise use /open
// If you have no loadable public directories, undefine TMP_FILE
// #define TMP_FILE temp_file( "eval", this_player() ) + ".c"
#define TMP_FILE OPEN_DIR + "eval." + geteuid( this_player() ) + ".c"
#define TMP_EUID_FILE SECURE_DIR + "tmp/eval." + geteuid( this_player() ) + ".c"
// Comment out the next line if you want failed evals to be deleted.
// #define KEEP_FAILED_EVALS
int cmd_eval( string a )
{
string file, filename;
mixed err, ret;
mapping logs;
string *names;
int loop, i;
string euid;
if( !a ) { notify_fail( SYNTAX ); return 0; }
/* Note that this only protects you against stupid wizzes if the wiz can
* write and load an object in some dir in the mudlib.
*/
#ifdef ADMIN_ONLY
if (!adminp(geteuid(this_player()))) {
write ("Sorry, this command can only be used by admins.\n") ;
return 1 ;
}
#endif
if(a[0] == '-')
{
if(a[1] == '-')
a = a[2..<1];
else if (geteuid(previous_object())
&& adminp(geteuid(previous_object()))
&& previous_object() == this_player()
&& this_player() == this_player(1))
{
i = strsrch(a, ' ');
if(i != -1)
{
euid = a[1..(i-1)];
a = a[(i+1)..<1];
} else {
notify_fail("call: seteuid: bad euid\n");
return 0;
}
} else {
notify_fail("call: seteuid: permission denied\n");
return 0;
}
}
// The includes in the file aren't necessary (though inheriting OBJECT
// allows remove() to work). You should change them as necessary for
// your own mud. They do allow shorter eval commands though. Eg:
// eval return children( USER )
file = @EndText
#include <config.h>
#include <daemons.h>
#include <net/daemons.h>
#include <commands.h>
#include <uid.h>
#include <mudlib.h>
#include <driver/runtime_config.h>
#include <net/i3.h>
inherit OBJECT;
EndText
;
// It is wise to prevent shadowing of this object, since its UID/EUID
// is changed.
if (euid) {
file += @EndEuid
void create() { }
int query_prevent_shadow() { return 1; }
mixed eval()
{
if (file_name(previous_object()) != CMD_EVAL) {
seteuid("Anonymous");
return;
}
seteuid(
EndEuid
"\"" + euid + "\");";
} else {
file += @EndNoEuid
void create() { seteuid( getuid() ); }
mixed eval()
{
EndNoEuid
;
}
file += (string) doith(a) + ";\n}\n";
#ifdef EVAL_CALL_LOG
seteuid(ROOT_UID);
if (!adminp(geteuid(this_player())))
log_file(EVAL_CALL_LOG, "************\n" + extract(ctime(time()), 4, 15) +
" [" + geteuid(this_player()) + "] eval'd: " + a + "\n");
#endif
seteuid( geteuid( previous_object() ) );
if (euid)
filename = TMP_EUID_FILE;
else {
// Use a wizard's directory if it exists, a public directory if not.
filename = user_path( geteuid( this_player() ) );
if( file_size( filename ) == -2 )
{
filename += "CMD_EVAL_TMP_FILE.c";
} else {
#ifdef TMP_FILE
filename = TMP_FILE;
#else
notify_fail( "You need a home directory to use the eval command.\n" );
return 0;
#endif
}
}
// If anything has been left from a previous eval, remove it
rm( filename );
if( ret = find_object( filename ) ) ret-> remove();
write_file( filename, file );
logs = ([]);
loop = sizeof( LOGS );
while( loop-- )
logs[LOGS[loop]] = file_size( LOGS[loop] );
if( err = catch( ret = (mixed)call_other( filename, "eval" ) ) )
{
write( "Error = " + err );
#ifdef KEEP_FAILED_EVALS
write( "The file is in " + filename + "\n" );
#else
rm( filename );
#endif
loop = sizeof( names = keys( logs ) );
while( loop-- )
CMD_UPDATE-> display_errs( logs[names[loop]], names[loop] );
} else {
// If you don't have the identify() simul_efun, use dump_variable()
write( wrap( "Result = " + identify( ret ) ) );
rm( filename );
if( ret = find_object( filename ) ) ret-> remove();
}
return 1;
}
string help()
{
return( SYNTAX + @Endtext
Effect: calls a function containing <lpc commands>
Example: if you type:
eval return function_exists("set",this_player())
this creates a temporary file in your home dir containing the line:
eval() { return function_exists( "set", this_player() ); }
and then does call_other on the files's eval() and displays the returned value
Endtext
);
}
/* alog to take a string of LPC and explode it (safely) on its ;'s. */
string doith(string arg) {
int i; // pointer to string within array containing an exploded bit
int j; // pointer to character within a given string
int cnt; // current count of (+1) ('s and (-1) )'s
int poq; // (waiting on) parethesisisis or quote; dual state
string *inp;
inp = explode(arg, ";");
if(arg[<1..<1] == ";")
inp[sizeof(inp)-1] += ";";
for(i=0;i<sizeof(inp); i++) {
for(j=0;j<strlen(inp[i]); j++) {
if(!poq && inp[i][j] == ')') cnt --;
if(!poq && inp[i][j] == '(') cnt ++;
if(inp[i][j] == 92 && inp[i][j+1] == 34) j+=2;
if(inp[i][j] == 34) poq = (!poq); // toggle poq state
if(strlen(inp[i])==j+1) {
if(sizeof(inp) == i+1) {
if (cnt>0) write("eval: You have "+cnt+" too many left parenthesis. (().\n");
else if(cnt<0) write("eval: You have "+(-cnt)+" too many right parenthesis. ()).\n");
else if(poq) write("eval: CR in middle of stinking string.\n");
// error or no error we return here at the end
return implode(inp, ";\n");
}
else if (poq || cnt)
inp[i] = inp[i] + ";"+ inp[i+1], inp -= ({inp[i+1]});
}
}
}
}