/**$Id:roxen.pike,v1.3291999/10/0623:07:36grubbaExp$**TheRoxenChallengermainprogram.**PerHedbor,HenrikGrubbström,PontusHagland,DavidHedborandothers.*///ABSands
uicidesystemscontributedfreelybyFrancescoChemolliconstantcvs_version="$Id:roxen.pike,v1.3291999/10/0623:07:36grubbaExp$";objectbackend_thread;objectargcache;//
Someheaderfiles#defineIN_ROXEN#includeroxen.h#includeconfig.h#includemodule.h#includevariables.h#includestat.h//Inheritsinherit"global_variables";inherit"hosts
";inherit"disk_cache";inherit"language";inherit"supports";/**Versioninformation*/constant__roxen_version__="1.4";constant__roxen_build__="0";#ifdef__NT__string
real_version="RoxenChallenger/"+__roxen_version__+"."+__roxen_build__+"NT";#elsestringreal_version="RoxenChallenger/"+__roxen_version__+"."+__roxen_build__;#en
dif//Prototypesforotherpartsofroxen.classRequestID{objectconf;//ReallyConfiguration,butthat'ssortofrecursive.inttime;stringraw_url;intdo_not_disconnect;mapping
(string:string)variables;mapping(string:mixed)misc;mapping(string:string)cookies;mapping(string:string)request_headers;multiset(string)prestate;multiset(string
)config;multiset(string)supports;multiset(string)pragma;array(string)client;array(string)referer;Stdio.Filemy_fd;stringprot;stringclientprot;stringmethod;strin
grealfile;stringvirtfile;stringrest_query;stringraw;stringquery;stringnot_query;stringextra_extension;stringdata;stringleftovers;array(int|string)auth;stringra
wauth;stringrealauth;stringsince;stringremoteaddr;stringhost;voidcreate(object|voidmaster_request_id);voidsend(string|objectwhat,int|voidlen);stringscan_for_qu
ery(stringin);voidend(string|voids,int|voidkeepit);voidready_to_receive();voidsend_result(mapping|voidresult);RequestIDclone_me();};stringfilename(objecto){ret
urnsearch(master()-programs,object_program(o));}#ifdefTHREADS//ThismutexisusedbyPrivsobjecteuid_egid_lock=Thread.Mutex();#endif/*THREADS*//**Theprivilegechange
r.**Basedonprivs.pike,v1.36.*/intprivs_level;staticclassPrivs{#ifefun(seteuid)intsaved_uid;intsaved_gid;intnew_uid;intnew_gid;#defineLOGP(variablesvariables-au
ditGLOBVAR(audit))#ifconstant(geteuid)constant(getegid)constant(seteuid)constant(setegid)#defineHAVE_EFFECTIVE_USER#endifstaticprivatestring_getcwd(){if(catch{
return(getcwd());}){return("Unknowndirectory(nox-bitoncurrentdirectory?)");}}staticprivatestringdbt(arrayt){if(!arrayp(t)||(sizeof(t)2))return"";return(((t[0]|
|"Unknownprogram")-(_getcwd()+"/"))-"base_server/")+":"+t[1]+"\n";}#ifdefTHREADSstaticmixedmutex_key;//Onlyonethreadmaymodifytheeuid/egidatatime.staticobjectth
reads_disabled;#endif/*THREADS*/intp_level;voidcreate(stringreason,int|string|voiduid,int|string|voidgid){#ifdefPRIVS_DEBUGwerror(sprintf("Privs(%O,%O,%O)\n""p
rivs_level:%O\n",reason,uid,gid,privs_level));#endif/*PRIVS_DEBUG*/#ifdefHAVE_EFFECTIVE_USERarrayu;#ifdefTHREADSif(euid_egid_lock){catch{mutex_key=euid_egid_lo
ck-lock();};}threads_disabled=_disable_threads();#endif/*THREADS*/p_level=privs_level++;if(getuid())return;/*Needstobeheresinceroot-priviligesmaybeneededto*use
getpw{uid,nam}.*/saved_uid=geteuid();saved_gid=getegid();seteuid(0);/*Astringofdigits?*/if(stringp(uid)(replace(uid,"0123456789"/"",({""})*10)==""))uid=(int)ui
d;if(stringp(gid)(replace(gid,"0123456789"/"",({""})*10=="")))gid=(int)gid;if(!stringp(uid))u=getpwuid(uid);else{u=getpwnam(uid);if(u)uid=u[2];}if(u!gid)gid=u[
3];if(!u){if(uid(uid!="root")){if(intp(uid)(uid=60000)){report_warning(sprintf("Privs:User%disnotinthepassworddatabase.\n""Assumingnobody.\n",uid));//Nobody.gi
d=gid||uid;//Fakeagidalso.u=({"fake-nobody","x",uid,gid,"Arealnobody","/","/sbin/sh"});}else{error("Unknownuser:"+uid+"\n");}}else{u=({"root","x",0,gid,"Thesup
er-user","/","/sbin/sh"});}}if(LOGP)report_notice(sprintf("Changeto%s(%d):%dprivswanted(%s),from%s",(string)u[0],(int)uid,(int)gid,(string)reason,(string)dbt(b
acktrace()[-2])));#ifefun(cleargroups)catch{cleargroups();};#endif/*cleargroups*/#ifefun(initgroups)catch{initgroups(u[0],u[3]);};#endifgid=gid||getgid();inter
r=(int)setegid(new_gid=gid);if(err0){report_debug(sprintf("Privs:WARNING:Failedtosettheeffectivegroupidto%d!\n""Checkthatyourpassworddatabaseiscorrectforuser%s
(%d),\n""andthatyourgroupdatabaseiscorrect.\n",gid,(string)u[0],(int)uid));intgid2=gid;#ifdefHPUX_KLUDGEif(gid=60000){/*HPUXhasdoesn'tlikegroupshigherthan60000
,*buthasassignednobodytogroup60001(whichisn'teven*in/etc/group!).**HPUX'slibcalsoinsistsonfillingnumericfieldsitdoesn'tlike*withthevalue60001!*/perror("Privs:W
ARNING:Assumingnobody-group.\n""Tryingsomealternatives...\n");//Assumewewantthenobodygroup,andtryacoupleofalternativesforeach(({60001,65534,-2}),gid2){perror("
%d...",gid2);if(initgroups(u[0],gid2)=0){if((err=setegid(new_gid=gid2))=0){perror("Success!\n");break;}}}}#endif/*HPUX_KLUDGE*/if(err0){perror("Privs:Failed\n"
);throw(({sprintf("FailedtosetEGIDto%d\n",gid),backtrace()}));}perror("Privs:WARNING:Setegidto%dinsteadof%d.\n",gid2,gid);gid=gid2;}if(getgid()!=gid)setgid(gid
||getgid());seteuid(new_uid=uid);#endif/*HAVE_EFFECTIVE_USER*/}voiddestroy(){#ifdefPRIVS_DEBUGwerror(sprintf("Privs-destroy()\n""privs_level:%O\n",privs_level)
);#endif/*PRIVS_DEBUG*/#ifdefHAVE_EFFECTIVE_USER/*Checkthatwedon'tincreasetheprivslevel*/if(p_level=privs_level){report_error(sprintf("Changebacktouid#%dgid#%d
fromuid#%dgid#%d\n""inwrongorder!Savedlevel:%dCurrentlevel:%d\n""Occursin:\n%s\n",saved_uid,saved_gid,new_uid,new_gid,p_level,privs_level,describe_backtrace(ba
cktrace())));return(0);}if(p_level!=privs_level-1){report_error(sprintf("Changebacktouid#%dgid#%dfromuid#%dgid#%d\n""Skipsprivslevel.Savedlevel:%dCurrentlevel:
%d\n""Occursin:\n%s\n",saved_uid,saved_gid,new_uid,new_gid,p_level,privs_level,describe_backtrace(backtrace())));}privs_level=p_level;if(LOGP){catch{arraybt=ba
cktrace();if(sizeof(bt)=2){report_notice(sprintf("Changebacktouid#%dgid#%d,from%s\n",saved_uid,saved_gid,dbt(bt[-2])));}else{report_notice(sprintf("Changebackt
ouid#%dgid#%d,frombackend\n",saved_uid,saved_gid));}};}if(getuid())return;#ifdefDEBUGintuid=geteuid();if(uid!=new_uid){report_warning(sprintf("Privs:UID#%ddiff
ersfromexpected#%d\n""%s\n",uid,new_uid,describe_backtrace(backtrace())));}intgid=getegid();if(gid!=new_gid){report_warning(sprintf("Privs:GID#%ddiffersfromexp
ected#%d\n""%s\n",gid,new_gid,describe_backtrace(backtrace())));}#endif/*DEBUG*/seteuid(0);arrayu=getpwuid(saved_uid);#ifefun(cleargroups)catch{cleargroups();}
;#endif/*cleargroups*/if(u(sizeof(u)3)){catch{initgroups(u[0],u[3]);};}setegid(saved_gid);seteuid(saved_uid);#endif/*HAVE_EFFECTIVE_USER*/}#endif/*efun(seteuid
)*/}/*Usedbyread_config.pike,sincethereseemstobeproblemswith*overloadingotherwise.*/staticobjectPRIVS(stringr,int|string|voidu,int|string|voidg){returnPrivs(r,
u,g);}#ifdefMODULE_DEBUG#defineMD_PERROR(X)roxen_perrorX;#else#defineMD_PERROR(X)#endif/*MODULE_DEBUG*/#ifndefTHREADSclasscontainer{mixedvalue;mixedset(mixedto
){returnvalue=to;}mixedget(){returnvalue;}}#endif//LocalesupportLocale.Roxen.standarddefault_locale=Locale.Roxen.standard;objectfonts;#ifconstant(thread_local)
objectlocale=thread_local();#elseobjectlocale=container();#endif/*THREADS*/#defineLOCALELOW_LOCALE-base_serverprogramConfiguration;/*setincreate*/arrayconfigur
ations=({});intdie_die_die;//FunctionthatactuallyshutsdownRoxen.(seelow_shutdown).privatestaticvoidreally_low_shutdown(intexit_code){//Dienicely.#ifdefTHREADSc
atch(stop_handler_threads());#endif/*THREADS*/exit(exit_code);//Nowwedie...}//ShutdownRoxen//exit_code=0Trueshutdown//exit_code=-1Restartprivatestaticvoidlow_s
hutdown(intexit_code){catch{configurations-stop();intpid;if(exit_code){roxen_perror("RestartingRoxen.\n");}else{roxen_perror("ShuttingdownRoxen.\n");//exit(0);
}};call_out(really_low_shutdown,0.01,exit_code);}//Perhapssomewhatmisnamed,really...Thisfunctionwillcloseall//listenportsandthenquit.The'start'scriptshouldthen
starta//newcopyofroxenautomatically.voidrestart(){low_shutdown(-1);}voidshutdown(){low_shutdown(0);}/**handle()stuff*/#ifndefTHREADS//handlefunctionusedwhenTHR
EADSisnotenabled.voidunthreaded_handle(functionf,mixed...args){f(@args);}functionhandle=unthreaded_handle;#elsefunctionhandle=threaded_handle;#endif/**THREADSc
odestartshere*/#ifdefTHREADS//#defineTHREAD_DEBUGobjectdo_thread_create(stringid,functionf,mixed...args){objectt=thread_create(f,@args);catch(t-set_name(id));#
ifdefTHREAD_DEBUGroxen_perror(id+"started\n");#endifreturnt;}//Queueofthingstohandle.//Anentryconsistsofanarray(functionfp,arrayargs)staticobject(Thread.Queue)
handle_queue=Thread.Queue();//Numberofhandlerthreadsthatarealive.staticintthread_reap_cnt;voidhandler_thread(intid){array(mixed)h,q;while(!die_die_die){if(q=ca
tch{do{#ifdefTHREAD_DEBUGwerror("Handlethread["+id+"]waitingfornextevent\n");#endif/*THREAD_DEBUG*/if((h=handle_queue-read())h[0]){#ifdefTHREAD_DEBUGwerror(spr
intf("Handlethread[%O]calling%O(@%O)...\n",id,h[0],h[1..]));#endif/*THREAD_DEBUG*/SET_LOCALE(default_locale);h[0](@h[1]);h=0;}elseif(!h){//Roxenisshuttingdown.
werror("Handlethread["+id+"]stopped\n");thread_reap_cnt--;return;}}while(1);}){report_error(/*LOCALE-uncaught_error(*/describe_backtrace(q)/*)*/);if(q=catch{h=
0;}){report_error(LOCALE-uncaught_error(describe_backtrace(q)));}}}}voidthreaded_handle(functionf,mixed...args){handle_queue-write(({f,args}));}intnumber_of_th
reads;voidstart_handler_threads(){if(QUERY(numthreads)=1){QUERY(numthreads)=1;report_debug("Startingonethreadtohandlerequests.\n");}else{report_debug("Starting
"+languages["en"]-number(QUERY(numthreads))+"threadstohandlerequests.\n");}for(;number_of_threadsQUERY(numthreads);number_of_threads++)do_thread_create("Handle
thread["+number_of_threads+"]",handler_thread,number_of_threads);}voidstop_handler_threads(){inttimeout=10;roxen_perror("Stoppingallrequesthandlerthreads.\n");
while(number_of_threads0){number_of_threads--;handle_queue-write(0);thread_reap_cnt++;}while(thread_reap_cnt){if(--timeout=0){roxen_perror("Givingupwaitingonth
reads!\n");return;}sleep(0.1);}}#endif/*THREADS*/#if0/*Grubbas*//**PortDBstuff.*///(["prot":(["ip":([port:protocol_handler,]),]),])staticmapping(string:mapping
(string:mapping(int:object)))handler_db=([]);//(["prot":protocol_program,])staticmapping(string:program)port_db=([]);//Isthereahandlerforthisport?objectlow_fin
d_handler(stringprot,stringip,intport){mixedres;return((res=handler_db[prot])(res=res[ip])res[port]);}//Registerahandlerforaport.voidregister_handler(stringpro
t,stringip,intport,objecthandler){mappingm;if(m=handler_db[prot]){mappingmm;if(mm=m[ip]){//FIXME:Whatifmm[port]alreadyexists?mm[port]=handler;}else{m[ip]=([por
t:handler]);}}else{handler_db[prot]=([ip:([port:handler])]);}}objectfind_handler(stringprot,stringip,intport){objecthandler=low_find_handler(prot,ip,port);if(!
handler){programprog=port_db[prot];if(!prog){return0;}mixederr=catch{handler=prog(prot,ip,port);};if(err){report_error(LOCALE-failed_to_open_port("?",sprintf("
%s://%s:%d/",prot,ip,port),describe_backtrace(err)));}else{register_handler(prot,ip,port,handler);}}returnhandler;}#endif#if1/*Pers*/classProtocol{inheritStdio
.Port;constantname="unknown";constantsupports_ipless=0;constantrequesthandlerfile="";constantdefault_port=4711;intport;intrefs;stringip;programrequesthandler;a
rray(string)sorted_urls=({});mapping(string:mapping)urls=([]);voidref(stringname,mappingdata){if(urls[name])return;refs++;urls[name]=data;sorted_urls=Array.sor
t_array(indices(urls),lambda(stringa,stringb){returnsizeof(a)sizeof(b);});}voidunref(stringname){if(!urls[name])return;m_delete(urls,name);sorted_urls-=({name}
);if(!--refs)destruct();//Closetheport.}voidgot_connection(){objectq=accept();if(!q);//..errnostuffhere..elserequesthandler(q,this_object());}objectfind_config
uration_for_url(stringurl,RequestIDid){werror("findconfigurationfor'"+url+"'\n");foreach(sorted_urls,stringin){if(glob(in+"*",url)){if(urls[in]-path)id-not_que
ry=id-not_query[strlen(urls[in]-path)..];returnurls[in]-conf;}}//Ouch.returnvalues(urls)[0]-conf;}voidcreate(intpn,stringi){if(!requesthandler)requesthandler=(
program)requesthandlerfile;port=pn;ip=i;::create();if(!bind(port,got_connection,ip))destruct();}}classHTTP{inheritProtocol;constantsupports_ipless=1;constantna
me="http";constantrequesthandlerfile="protocols/http.pike";constantdefault_port=80;}classHTTPS{inheritProtocol;constantsupports_ipless=0;constantname="https";c
onstantrequesthandlerfile="protocols/https.pike";constantdefault_port=443;}classFTP{inheritProtocol;constantsupports_ipless=0;constantname="ftp";constantreques
thandlerfile="protocols/ftp.pike";constantdefault_port=21;}classGOPHER{inheritProtocol;constantsupports_ipless=0;constantname="gopher";constantrequesthandlerfi
le="protocols/gopher.pike";constantdefault_port=70;}classTETRIS{inheritProtocol;constantsupports_ipless=0;constantname="tetris";constantrequesthandlerfile="pro
tocols/tetris.pike";constantdefault_port=2050;}mappingprotocols=(["http":HTTP,"https":HTTPS,"ftp":FTP,"gopher":GOPHER,"tetris":TETRIS,]);mapping(string:mapping
)open_ports=([]);mapping(string:object)urls=([]);arraysorted_urls=({});stringfind_ip_for(stringwhat){if(what=="*"||lower_case(what)=="any")return0;if(!strlen(r
eplace(what,"01234567890."/"",(""*11)/"")))returnwhat;arrayres=gethostbyname(what);if(!res||!sizeof(res[1]))report_error("Icannotpossiblybindto"+what+",thathos
tisunknown.""SubstitutingwithANY\n");elsereturnres[1][0];}voidunregister_url(stringurl){if(urls[url]urls[url]-port){urls[url]-port-unref(url);m_delete(urls,url
);sort_urls();}}voidsort_urls(){sorted_urls=indices(urls);sort(map(map(sorted_urls,strlen),`-),sorted_urls);}intregister_url(stringurl,objectconf){report_debug
("Register"+url+"for"+conf-name+"\n");stringprotocol;stringhost;intport;stringpath;Protocolprot;stringrequired_host;url=replace(url,"/ANY","/*");url=replace(ur
l,"/any","/*");sscanf(url,"%[^:]://%[^/]%s",protocol,host,path);sscanf(host,"%[^:]:%d",host,port);if(strlen(path)(path[-1]=='/'))path=path[..strlen(path)-2];if
(!strlen(path))path=0;if(urls[url]){if(urls[url]-