diff --git a/Server/bin/asksource.sh b/Server/bin/asksource.sh index 113c55e0d..ee249243c 100755 --- a/Server/bin/asksource.sh +++ b/Server/bin/asksource.sh @@ -52,7 +52,7 @@ DATE="$(date +"%m%d%y")" MORELIBS="-lrt" OPTIONS="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25" C_OPTIONS=$(echo $OPTIONS|wc -w) -BOPTIONS="1 2 3 4 5 6" +BOPTIONS="1 2 3 4 5 6 7" C_BOPTIONS=$(echo $BOPTIONS|wc -w) DOPTIONS="1 2 3" C_DOPTIONS=$(echo $DOPTIONS|wc -w) @@ -122,6 +122,7 @@ DEFB[2]="\$(DR_DEF)" DEFB[3]="-DSBUF64" DEFB[4]="-DSQLITE" DEFB[5]="-DQDBM" +DEFB[7]="-DRHOST_CURL" DEFD[1]="-DMUSH_DOORS" DEFD[2]="-DEMPIRE_DOORS" @@ -211,6 +212,7 @@ echo "[${X[25]}] 25. Pcre System Libs" echo "--------------------------- Beta/Unsupported Additions -----------------------" echo "[${XB[1]}] B1. 3rd Party MySQL [${XB[2]}] B2. Door Support(Menu) [${XB[3]}] B3. 64 Char attribs" echo "[${XB[4]}] B4. SQLite Support [${XB[5]}] B5. QDBM DB Support [#] B6. LBUF Settings (Menu)" +echo "[${XB[7]}] B7. libcurl Support" echo "------------------------------------------------------------------------------" echo "" echo "Keys: [h]elp [i]nfo [s]ave [l]oad [d]elete [c]lear [m]ark [b]rowse [r]un [q]uit" @@ -384,9 +386,15 @@ info() { echo "if you wish to use them." fi ;; - 7) echo "RhostMUSH allows you to use a plushelp.txt file for +help. This" - echo "supports MUX/TinyMUSH3 in how +help is hardcoded to a text file." - echo "Enable this if you wish to have a plushelp.txt file be used." + 7) if [ $RUNBETA -eq 1 ] + then + echo "Enable libcurl support, to allow the use of http_get, http_post," + echo "and http_request from within RhostMUSH" + else + echo "RhostMUSH allows you to use a plushelp.txt file for +help. This" + echo "supports MUX/TinyMUSH3 in how +help is hardcoded to a text file." + echo "Enable this if you wish to have a plushelp.txt file be used." + fi ;; 8) echo "RhostMUSH, by default, allows multiple arguments to be passed" echo "to @program. This, unfortunately, is not how MUX does it, so" @@ -421,11 +429,11 @@ info() { echo "flags, are essentially 'markers' that you can rename at leasure." echo "If you have a desire for marker flags, enable this option." ;; - 15) echo "Bang support. Very cool stuff. It allows you to use ! for false" - echo "and !! for true. An example would be [!match(this,that)]. It" - echo "also allows $! for 'not a string' and $!! for 'is a string'." - echo "Such an example would be [$!get(me/blah)]. If you like this" - echo "feature, enable this option. You want it. Really." + 15) echo 'Bang support. Very cool stuff. It allows you to use ! for false' + echo 'and !! for true. An example would be [!match(this,that)]. It' + echo 'also allows $! for "not a string" and $!! for "is a string".' + echo 'Such an example would be [$!get(me/blah)]. If you like this' + echo 'feature, enable this option. You want it. Really.' ;; 16) echo "This is an alternate WHO listing. It's a tad longer for the" echo "display and will switch ports to Total Cmds on the WHO listings." @@ -1262,6 +1270,14 @@ setdefaults() { MORELIBS="-lsqlite3 ${MORELIBS}" fi fi + + # Add definitions for libcurl + if [ "${XB[7]}" = "X" ] + then + echo "Patching curl libs with curl-config..." + MORELIBS="$(curl-config --libs) ${MORELIBS}" + fi + BOB1=$(uname -r|cut -f1 -d".") BOB2=$(uname -s) if [ -d /usr/ucbinclude -a "${BOB2}" = "SunOS" ] @@ -1293,7 +1309,6 @@ setdefaults() { DEFS="${DEFS} -DHAS_OPENSSL" fi fi - DEFS="DEFS = ${DEFS}" } ################################################################### @@ -1463,92 +1478,73 @@ setlibs() { echo "Compiling with system pcre library..." MORELIBS="${MORELIBS} -lpcre" fi - MORELIBS="MORELIBS = ${MORELIBS}" } ################################################################### # UPDATEMAKEFILE - Update the makefile with the changes ################################################################### updatemakefile() { - echo "Updating the DEFS section of the Makefile now. Please wait..." - cat ../src/Makefile|sed "s/$(grep ^DEF ../src/Makefile|sed "s/\//\\\\\//g")/${DEFS}/g" > /tmp/$$CONF$$ - mv -f ../src/Makefile ../src/Makefile.${DATE} 2>/dev/null - mv -f /tmp/$$CONF$$ ../src/Makefile 2>/dev/null - rm -f /tmp/$$CONF$$ 2>/dev/null + makedefs_path="" + for p in src/ ../src ../../src; do + if [ -d $p -a -e $p/Makefile ]; then + makedefs_path=$p/make.defs + compiledefs_path=$p/do_compile.defs + break + fi + done + if [ "z$makedefs_path" = "z" ] + then + echo '$0: error: could not locate Makefile, cannot determine where to generate make.defs' >&2 + exit 1 + fi + echo 'Updating the DEFS section of the Makefile now. Please wait...' + echo "DEFS = ${DEFS}" > $makedefs_path -# Let's do the door additions here - if [ "${XB[2]}" = "X" ] + echo '# Begin Door Configurations' >> $makedefs_path + if [ "${XB[2]}" = 'X' ] then - cat ../src/Makefile|sed "s/^#DR_DEF/DR_DEF/g" > /tmp/$$CONF$$ - mv -f /tmp/$$CONF$$ ../src/Makefile 2>/dev/null - rm -f /tmp/$$CONF$$ 2>/dev/null + echo 'DR_DEF = -DENABLE_DOORS -DEXAMPLE_DOOR_CODE' >> $makedefs_path if [ "${XD[1]}" = "X" ] then - cat ../src/Makefile|sed "s/^#DRMUSH/DRMUSH/g" > /tmp/$$CONF$$ - else - cat ../src/Makefile|sed "s/^DRMUSH/#DRMUSH/g" > /tmp/$$CONF$$ + echo 'DRMUSHSRC = door_mush.c' >> $makedefs_path + echo 'DRMUSHOBJ = door_mush.o' >> $makedefs_path fi - mv -f /tmp/$$CONF$$ ../src/Makefile 2>/dev/null - rm -f /tmp/$$CONF$$ 2>/dev/null if [ "${XD[2]}" = "X" ] then - cat ../src/Makefile|sed "s/^#DREMPIRE/DREMPIRE/g"|sed "s/^#DR_HDR/DR_HDR/g" > /tmp/$$CONF$$ - else - cat ../src/Makefile|sed "s/^DREMPIRE/#DREMPIRE/g"|sed "s/^DR_HDR/#DR_HDR/g" > /tmp/$$CONF$$ + echo 'DREMPIRESRC = empire.c' >> $makedefs_path + echo 'DREMPIREOBJ = empire.o' >> $makedefs_path + echo 'DREMPIREHDR = empire.h' >> $makedefs_path fi - mv -f /tmp/$$CONF$$ ../src/Makefile 2>/dev/null - rm -f /tmp/$$CONF$$ 2>/dev/null if [ "${XD[3]}" = "X" ] then - cat ../src/Makefile|sed "s/^#DRMAIL/DRMAIL/g" > /tmp/$$CONF$$ - else - cat ../src/Makefile|sed "s/^DRMAIL/#DRMAIL/g" > /tmp/$$CONF$$ + echo 'DRMAILSRC = door_mail.c' >> $makedefs_path + echo 'DRMAILOBJ = door_mail.o' >> $makedefs_path fi - mv -f /tmp/$$CONF$$ ../src/Makefile 2>/dev/null - rm -f /tmp/$$CONF$$ 2>/dev/null - else - cat ../src/Makefile|sed "s/^DR_DEF/#DR_DEF/g"|sed "s/^DRMUSH/#DRMUSH/g"| \ - sed "s/^DREMPIRE/#DREMPIRE/g"|sed "s/^DRMAIL/#DRMAIL/g" > /tmp/$$CONF$$ - mv -f /tmp/$$CONF$$ ../src/Makefile 2>/dev/null - rm -f /tmp/$$CONF$$ 2>/dev/null fi + echo '# End Door Configurations' >> $makedefs_path + if [ "${XB[5]}" = "X" ] then echo "Compiling to QDBM database." - sed "s~^$(grep "^LIBS " ../src/Makefile)~LIBS = -L./qdbm/ -lqdbm~g" ../src/Makefile > /tmp/$$CONF$$ - mv -f /tmp/$$CONF$$ ../src/Makefile - rm -f /tmp/$$CONF$$ - sed "s~^$(grep "^COMP=" ../src/do_compile.sh)~COMP=qdbm~g" ../src/do_compile.sh > /tmp/$$CONF$$ - mv -f /tmp/$$CONF$$ ../src/do_compile.sh - chmod 755 ../src/do_compile.sh - rm -f /tmp/$$CONF$$ + echo '# Use QDBM. See also do_compile.defs' >> $makedefs_path + echo 'LIBS = -L./qdbm/ -lqdbm' >> $makedefs_path + echo 'COMP=qdbm' > $compiledefs_path else echo "Compiling to GDBM database (default)." - sed "s~^$(grep "^LIBS " ../src/Makefile)~LIBS = -L./gdbm-1.8.3/.libs/ -lgdbm_compat -L./gdbm-1.8.3/ -lgdbm~g" ../src/Makefile > /tmp/$$CONF$$ - mv -f /tmp/$$CONF$$ ../src/Makefile - rm -f /tmp/$$CONF$$ - sed "s~^$(grep "^COMP=" ../src/do_compile.sh)~COMP=gdbm~g" ../src/do_compile.sh > /tmp/$$CONF$$ - mv -f /tmp/$$CONF$$ ../src/do_compile.sh - chmod 755 ../src/do_compile.sh - rm -f /tmp/$$CONF$$ + echo '# Use (default) GDBM. See also do_compile.defs' >> $makedefs_path + echo 'LIBS = -L./gdbm-1.8.3/.libs/ -lgdbm_compat -L./gdbm-1.8.3/ -lgdbm' >> $makedefs_path + echo 'COMP=gdbm' > $compiledefs_path fi # add CFLAGS for low memory if [ "${X[23]}" = "X" ] then echo "Adding CFLAG option for low memory compile..." - cat ../src/Makefile|sed "s/^#CFLAG/CFLAG/g" > /tmp/$$CONF$$ - else - cat ../src/Makefile|sed "s/^CFLAG/#CFLAG/g" > /tmp/$$CONF$$ + echo '# Low-memory compile' >> $makedefs_path + echo 'CFLAGS = --param ggc-min-expand=0 --param ggc-min-heapsize=8192' >> $makedefs_path fi - mv -f /tmp/$$CONF$$ ../src/Makefile 2>/dev/null - rm -f /tmp/$$CONF$$ 2>/dev/null echo "...completed." echo "Updating the MORELIBS section of the Makefile now. Please wait..." - cat ../src/Makefile|sed "s/$(grep ^MORELIBS ../src/Makefile| \ - sed "s/\//\\\\\//g")/${MORELIBS}/g" > /tmp/$$CONF$$ - mv -f ../src/Makefile ../src/Makefile.${DATE} 2>/dev/null - mv -f /tmp/$$CONF$$ ../src/Makefile 2>/dev/null - rm -f /tmp/$$CONF$$ 2>/dev/null + echo "MORELIBS = ${MORELIBS}" >> $makedefs_path echo "...completed." } diff --git a/Server/game/txt/help.indx b/Server/game/txt/help.indx index 93edeafbc..87d0222d6 100644 Binary files a/Server/game/txt/help.indx and b/Server/game/txt/help.indx differ diff --git a/Server/game/txt/help.txt b/Server/game/txt/help.txt index 11671e86b..2786987a1 100644 --- a/Server/game/txt/help.txt +++ b/Server/game/txt/help.txt @@ -25374,6 +25374,23 @@ Mail: Done lparent() - Returns all the parents of an object. die() - Rolls a set of dice for random number generation. +& CURL_GET() + Function: curl_get() + + This is a minimal implementation of libcURL bindings for RhostMUSH. This + function allows you to retrieve the result of an HTTP GET from the given URL. + + The implementation is currently quite bare. In particular, you cannot set + headers or cookies, nor can you access response headers. Expect this + implementation to improve with time. + + Example: + + th >> [curl_get(example.com)] + >> + + ... + & revisions All credits to players, codebases, or other is presented in the RHOST.CHANGES file in the readme directory in the source distribution. The credits are too diff --git a/Server/game/txt/wizhelp.indx b/Server/game/txt/wizhelp.indx index f5171b2a0..69d49087d 100644 Binary files a/Server/game/txt/wizhelp.indx and b/Server/game/txt/wizhelp.indx differ diff --git a/Server/game/txt/wizhelp.txt b/Server/game/txt/wizhelp.txt index 2fbc72867..7444dde7e 100644 --- a/Server/game/txt/wizhelp.txt +++ b/Server/game/txt/wizhelp.txt @@ -5062,12 +5062,12 @@ SNOOPY God Wiz conn_timeout connect_file connect_reg_file cpu_secure_lvl cpuintervalchk cputimechk crash_database create_max_cost create_min_cost - dark_sleepers descs_are_local default_home - dig_cost door_file door_index - down_file down_motd_message dump_interval - dump_message dump_offset earn_limit - empower_fulltel enable_tstamps enforce_unfindable - error_file + curl_request_limit dark_sleepers descs_are_local + default_home dig_cost door_file + door_index down_file down_motd_message + dump_interval dump_message dump_offset + earn_limit empower_fulltel enable_tstamps + enforce_unfindable error_file { 'wizhelp config parameters2' for more } @@ -5773,6 +5773,12 @@ SNOOPY God Wiz Specifies the minimum (and default) cost for creating an object. See Also: create_max_cost, sacrifice_adjust, sacrifice_factor. +& curl_request_limit + Config Parameter: curl_request_limit . Default: 5 + + This specifies the maximum number of seconds for which a cURL request may + run before it's cancelled. + & dark_sleepers Config directive: dark_sleepers . Default: yes Indicates whether or not disconnected players are to be considered 'dark', diff --git a/Server/hdrs/mudconf.h b/Server/hdrs/mudconf.h index 2f67930dc..fc7154fb3 100644 --- a/Server/hdrs/mudconf.h +++ b/Server/hdrs/mudconf.h @@ -427,7 +427,10 @@ struct confdata { int sqlite_query_limit; char sqlite_db_path[128]; #endif /* SQLITE */ -#else +#ifdef RHOST_CURL + int curl_request_limit; +#endif /* RHOST_CURL */ +#else /* STANDALONE */ int paylimit; /* getting money gets hard over this much */ int digcost; /* cost of @dig command */ int opencost; /* cost of @open command */ diff --git a/Server/src/Makefile b/Server/src/Makefile index 9fcad563d..695dcbab4 100644 --- a/Server/src/Makefile +++ b/Server/src/Makefile @@ -4,6 +4,8 @@ # Search for the text 'CONFIGURATION SECTION' and make any changes needed # there. +include make.defs + SHELL=/bin/sh srcdir = . bindir = ../bin @@ -17,13 +19,12 @@ else endif # CPP = gcc -E # This is broken in autoconf. Sigh. CPP = $(CC) -E -# Sometime, LIBS won't allow '-lndbm' here. If so, remove it. -# If using CYGWIN, comment out the whole line. -LIBS = -L./gdbm-1.8.3/.libs/ -lgdbm_compat -L./gdbm-1.8.3/ -lgdbm -#LIBS = -L./qdbm/ -lqdbm + +# LIBS is now written to make.defs by asksource.sh + LIBOBJS = -# for compiling on lowmem systems use this -#CFLAGS = --param ggc-min-expand=0 --param ggc-min-heapsize=8192 + +# CFLAGS (used only to enable low-mem compile) is now written to make.defs by asksource.sh # If you wish to debug the code, run under this OPTIM = -g @@ -47,18 +48,10 @@ UDB_INC = udb.h udb_defs.h COM_SRC = COM_OBJ = -# Door Configurations -#DRMUSHSRC = door_mush.c -#DRMUSHOBJ = door_mush.o -#DREMPIRESRC = empire.c -#DREMPIREOBJ = empire.o -#DREMPIREHDR = empire.h -#DRMAILSRC = door_mail.c -#DRMAILOBJ = door_mail.o +# Door Configurations -- "Options" now written to make.defs by asksource.sh DR_SRC = $(DRMUSHSRC) $(DREMPIRESRC) $(DRMAILSRC) DR_OBJ = $(DRMUSHOBJ) $(DREMPIREOBJ) $(DRMAILOBJ) DR_HDR = $(DREMPIREHDR) -#DR_DEF = -DENABLE_DOORS -DEXAMPLE_DOOR_CODE # Everything needed to use the database in standalone mode. SA_SRC = sa-db.c sa-db_rw.c sa-boolexp.c sa-unparse.c sa-compress.c \ @@ -78,7 +71,7 @@ D_SRC = door.c shs.c mushcrypt.c news.c mail.c mailfix.c debug.c senses.c \ db.c db_rw.c compress.c stringutil.c object.c conf.c flags.c htab.c \ compat.c file_c.c player_c.c bsd.c alloc.c autoreg.c levels.c \ local.c utils.c pcre.c pcre_extensions.c tprintf.c sha1.c 64btime.c \ - sqlite.c $(DR_SRC) + sqlite.c curl.c $(DR_SRC) D_OBJ = door.o shs.o mushcrypt.o news.o mail.o mailfix.o debug.o senses.o \ create.o game.o help.o look.o match.o move.o player.o predicates.o \ @@ -87,7 +80,7 @@ D_OBJ = door.o shs.o mushcrypt.o news.o mail.o mailfix.o debug.o senses.o \ db.o db_rw.o compress.o stringutil.o object.o conf.o flags.o htab.o \ compat.o file_c.o player_c.o bsd.o alloc.o autoreg.o levels.o \ local.o utils.o pcre.o pcre_extensions.o tprintf.o sha1.o 64btime.o \ - sqlite.o $(DR_OBJ) + sqlite.o curl.o $(DR_OBJ) D_INC = door.h shs.h news.h mail.h debug.h \ copyright.h flags.h help.h htab.h interface.h match.h functions.h \ @@ -156,7 +149,6 @@ VER_FLG = -DMUSH_BUILD_DATE="\"`cat date.txt`\"" \ # SYS_MALLOC - Used if malloc.h is in a borked spot. ########################################################################################################### -DEFS = -DBROKEN_NDBM -DBROKEN_ERRNO_SYS -DSQLITE -DSBUF64 -DSECURE_SIDEEFFECT -DOLD_SETQ -DBANGS -DMARKER_FLAGS -DZENTY_ANSI -DEXPANDED_QREGS -DREALITY_LEVELS -DATTR_HACK -DPROG_LIKEMUX -DPLUSHELP -DUSECRYPT -DSOFTCOM -DMUX_INCDEC -DTINY_U -DUSE_SIDEEFFECT -Wall -DHAS_OPENSSL # # Rhost supports two encryption schemes SHS (SHA-1), and the # unix crypt() function (DES). @@ -212,7 +204,8 @@ DEFS = -DBROKEN_NDBM -DBROKEN_ERRNO_SYS -DSQLITE -DSBUF64 -DSECURE_SIDEEFFECT -D # #CYGWIN = -I/usr/local/include -DCYGWIN -DBROKEN_ERRNO_SYS -DBROKEN_NDBM\ # -DSYSWAIT -DNODEBUGMONITOR -g - + +# IMPORTANT: MORELIBS is now written to make.defs by asksource.sh # Libraries. # The following extra libraries are required based on what system you have. # #1 - This is used for most libc5 distributions. This includes Slackware 7.0 @@ -242,8 +235,6 @@ DEFS = -DBROKEN_NDBM -DBROKEN_ERRNO_SYS -DSQLITE -DSBUF64 -DSECURE_SIDEEFFECT -D ################################################################ #5 #MORELIBS = -lm -lrt -L/usr/local/lib -lcygipc -MORELIBS = -lm -lrt -lnsl -lresolv -lcrypt -lssl - # Set this to the directory where the MUSH game lives. # This is not really used much GAME = /usr/games/lib/mush diff --git a/Server/src/conf.c b/Server/src/conf.c index 74a934412..b8b5b8e87 100644 --- a/Server/src/conf.c +++ b/Server/src/conf.c @@ -612,7 +612,9 @@ NDECL(cf_init) mudconf.sqlite_query_limit = 5; strcpy( mudconf.sqlite_db_path, "sqlite" ); #endif /* SQLITE */ - +#ifdef RHOST_CURL + mudconf.curl_request_limit = 5; +#endif /* RHOST_CURL */ /* maximum logs allowed per command */ mudconf.log_maximum = 1; mudconf.cluster_cap = 10; /* Cap of cluster wait in seconds for action */ @@ -4586,6 +4588,12 @@ CONF conftable[] = cf_bool, CA_GOD | CA_IMMORTAL, &mudconf.round_kludge, 0, 0, CA_PUBLIC, (char *) "Kludgy fix for rounding even numbers.\r\n"\ " Default: 0 Value: %d"}, /* [Loki] */ +#ifdef RHOST_CURL + {(char *) "curl_request_limit", + cf_int, CA_GOD | CA_IMMORTAL, &mudconf.curl_request_limit, 0, 0, CA_WIZARD, + (char *) "Maximum time in seconds that a libcurl request may run.\r\n"\ + " Default: 5 Value: %d"}, +#endif /*RHOST_CURL*/ #ifdef SQLITE {(char *) "sqlite_query_limit", cf_int, CA_GOD | CA_IMMORTAL, &mudconf.sqlite_query_limit, 0, 0, CA_WIZARD, diff --git a/Server/src/curl.c b/Server/src/curl.c new file mode 100644 index 000000000..1e5661ec0 --- /dev/null +++ b/Server/src/curl.c @@ -0,0 +1,240 @@ +#ifdef RHOST_CURL + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "copyright.h" +#include "autoconf.h" + +#include "config.h" +#include "db.h" +#include "interface.h" +#include "mudconf.h" +#include "command.h" +#include "functions.h" +#include "externs.h" +#include "match.h" +#include "flags.h" +#include "alloc.h" +#include "vattr.h" + + +char tempbuff[LBUF_SIZE]; +char tempbuff2[LBUF_SIZE]; +regex_t curl_has_protocol, curl_valid_url; +uint8_t overflow = 0; + +struct curl_options { + uint8_t response_code; + uint8_t response_headers; +}; + +FUNCTION(local_fun_curl_get); +static size_t write_callback( void * contents, size_t size, size_t nmemb, void *userp ); +void curl_normalize_line_endings( char * buf, size_t buffer_length ); +void curl_prepend_metadata( char * buf, struct curl_options * opts, CURL * curl ); + +static int ival(char *buff, char **bufcx, int result) { + sprintf(tempbuff, "%d", result); + safe_str(tempbuff, buff, bufcx); + return 0; +} + +void initialize_curl_options( struct curl_options * opts ) { + memset( opts, 0, sizeof( struct curl_options ) ); +} + +void local_curl_init() { + FUN *fp; + char *buff, *cp, *dp; + + regcomp( &curl_has_protocol, ".*://.*", REG_EXTENDED | REG_NOSUB ); + regcomp( &curl_valid_url, "^(https?)://.*", REG_EXTENDED | REG_NOSUB ); + + static FUN fun_table[] = { + {"CURL_GET", local_fun_curl_get, 0, FN_VARARGS, CA_WIZARD, 0 }, + {NULL, NULL, 0, 0, 0, 0} + }; + + /* Register the functions */ + buff = alloc_sbuf("init_curl_functab"); + for (fp = fun_table ; fp->name ; fp++) { + cp = (char *) fp->name; + dp = buff; + while (*cp) { + *dp = ToLower(*cp); + cp++; + dp++; + } + *dp = '\0'; + hashadd2(buff, (int *) fp, &mudstate.func_htab, 1); + } +} + +uint8_t parse_curl_options( char ** fargs, size_t nfargs, struct curl_options * opts, char * buff, char ** bufcx ) { + char * expressionlist_saveptr = NULL; + char * expression_saveptr = NULL; + char * expression = NULL; + char * key = NULL; + char * value = NULL; + size_t i; + for( i = 1; i < nfargs; i++ ) { + expression = strtok_r( fargs[i], " ", &expressionlist_saveptr ); + while( expression != NULL ) { + key = strtok_r( expression, "=", &expression_saveptr ); + if( key != NULL ) { + if( strcasecmp( key, "response_code" ) == 0 ) { + opts->response_code = 1; + } else if( strcasecmp( key, "response_headers" ) == 0 ) { + opts->response_headers = 1; + } else { + safe_str( "#-1 UNHANDLED OPTION ", buff, bufcx ); + safe_str( expression, buff, bufcx ); + return 1; + } + value = strtok_r( NULL, "=", &expression_saveptr ); + } + expression = strtok_r( NULL, " ", &expressionlist_saveptr ); + } + } + return 0; +} + +FUNCTION(local_fun_curl_get) +{ + CURL * curl = NULL; + CURLcode result; + struct curl_options opts; + + initialize_curl_options( &opts ); + + if( nfargs < 1 ) { + safe_str( "#-1 CURL_GET EXPECTS 1 OR MORE ARGUMENTS [RECEIVED ", buff, bufcx ); + ival( buff, bufcx, nfargs ); + safe_str( "]", buff, bufcx ); + return; + } + + if( nfargs >= 2 ) + if( parse_curl_options( fargs, nfargs, &opts, buff, bufcx ) != 0 ) + return; + + if( curl == NULL ) + curl = curl_easy_init(); + if( !curl ) { + STARTLOG(LOG_ALWAYS, "CURL", "FAIL") + log_text( "an error occurred initializing the cURL agent" ); + ENDLOG + safe_str( "#-1 INTERNAL ERROR", buff, bufcx ); + return; + } + + if( regexec( &curl_has_protocol, fargs[0], 0, NULL, 0 ) == 0 ) { + // Requested URL includes a protocol + snprintf( tempbuff, LBUF_SIZE, "%s", fargs[0] ); + } else { + // Requested URL does not include a protocol, assume http:// + snprintf( tempbuff, LBUF_SIZE, "http://%s", fargs[0] ); + } + + if( regexec( &curl_valid_url, tempbuff, 0, NULL, 0 ) != 0 ) { + safe_str( "#-1 ONLY HTTP:// AND HTTPS:// ALLOWED [RECEIVED ", buff, bufcx ); + safe_str( tempbuff, buff, bufcx ); + safe_str( "]", buff, bufcx ); + curl_easy_cleanup( curl ); + return; + } + + curl_easy_setopt( curl, CURLOPT_URL, tempbuff ); + curl_easy_setopt( curl, CURLOPT_FOLLOWLOCATION, 1L ); + + curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_callback ); + curl_easy_setopt( curl, CURLOPT_WRITEDATA, &opts ); + curl_easy_setopt( curl, CURLOPT_TIMEOUT, mudconf.curl_request_limit ); + if( opts.response_headers ) { + curl_easy_setopt( curl, CURLOPT_HEADER, 1 ); + } + + // truncate tempbuff2; this will hold our returned data + tempbuff2[0] = '\0'; + overflow = 0; + result = curl_easy_perform( curl ); + if( result == CURLE_OK ) { + curl_prepend_metadata( tempbuff2, &opts, curl ); + curl_normalize_line_endings( tempbuff2, LBUF_SIZE ); + safe_str( tempbuff2, buff, bufcx ); + } else { + if( overflow == 1 ) { + strncpy( tempbuff, tempbuff2, LBUF_SIZE ); + curl_prepend_metadata( tempbuff, &opts, curl ); + curl_normalize_line_endings( tempbuff, LBUF_SIZE ); + } else { + snprintf( tempbuff, LBUF_SIZE, "#-2 GET FAILED: %s", curl_easy_strerror( result ) ); + } + safe_str( tempbuff, buff, bufcx ); + } + + curl_easy_cleanup( curl ); +} + +void curl_prepend_metadata( char * buf, struct curl_options * opts, CURL * curl ) { + long resp_code; + char metabuff[LBUF_SIZE]; + char result[LBUF_SIZE]; + result[0] = '\0'; + if( opts->response_code ) { + curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &resp_code ); + snprintf( metabuff, LBUF_SIZE, "%ld\n", resp_code ); + strncat( result, metabuff, LBUF_SIZE ); + } + strncat( result, buf, LBUF_SIZE ); + strncpy( buf, result, LBUF_SIZE ); +} + +void curl_normalize_line_endings( char * buf, size_t buffer_length ) { + size_t pos = 0, dpos=0; + char normalized[LBUF_SIZE]; + size_t len = strlen( buf ); + for( pos = 0; pos < (len < LBUF_SIZE-1 ? len : LBUF_SIZE-1); pos++ ) { + // Basically, discard all \r, replace with \n with \r\n. + if( buf[pos] == '\r' ) + continue; + if( buf[pos] == '\n' ) { + normalized[dpos++] = '\r'; + } + // Avoid chance of buffer overflow + if( dpos == LBUF_SIZE-1 ) { + normalized[dpos] = '\0'; + break; + } + normalized[dpos++] = buf[pos]; + normalized[dpos] = '\0'; + } + strncpy( buf, normalized, LBUF_SIZE ); + return; +} + +static size_t write_callback( void * contents, size_t size, size_t nmemb, void *userp ) { + struct curl_options * opt = userp; + size_t realsize; + size_t bytesleft; + realsize = size * nmemb; + bytesleft = LBUF_SIZE - strnlen( tempbuff2, LBUF_SIZE ) - 1; + if( realsize < bytesleft ) { + strncat( tempbuff2, contents, realsize ); + return realsize; + } else { + strncat( tempbuff2, contents, bytesleft ); + overflow = 1; + return bytesleft; + } +} + +#endif diff --git a/Server/src/do_compile.sh b/Server/src/do_compile.sh index 779a3f8b3..ddd3914ad 100755 --- a/Server/src/do_compile.sh +++ b/Server/src/do_compile.sh @@ -1,7 +1,11 @@ #!/bin/sh # -COMP=gdbm -case ${COMP} in + +[ -f ../src/do_compile.defs ] && . ../src/do_compile.defs + +# ${COMP:=gdbm} uses COMP or, if COMP is unset, sets COMP to gdbm and uses gdbm + +case ${COMP:=gdbm} in gdbm) gdbmdir=./gdbm-1.8.3 ;; qdbm) gdbmdir=./qdbm diff --git a/Server/src/local.c b/Server/src/local.c index 8a1c711ab..57123d53f 100644 --- a/Server/src/local.c +++ b/Server/src/local.c @@ -26,11 +26,17 @@ #ifdef SQLITE extern void local_sqlite_init(void); #endif /* SQLITE */ +#ifdef RHOST_CURL + extern void local_curl_init(void); +#endif /* RHOST_CURL */ void local_startup(void) { #ifdef SQLITE local_sqlite_init(); #endif /* SQLITE */ +#ifdef RHOST_CURL + local_curl_init(); +#endif /* RHOST_CURL */ load_regexp_functions(); }