/**$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]-conf!=conf){report_error("CannotregisterURL"+url+",alreadyregisterdby"+urls[url]-conf-name+"!\n");return0;}url
s[url]-port-ref(url,urls[url]);return1;}if(!(prot=protocols[protocol])){report_error("CannotregisterURL"+url+",cannotfindtheprotocol"+protocol+"!\n");return0;}
if(!port)port=prot-default_port;if(!prot-supports_ipless)required_host=find_ip_for(host);mappingm;if(!(m=open_ports[protocol]))m=open_ports[protocol]=([]);urls
[url]=(["conf":conf,"path":path]);sorted_urls+=({url});if(m[required_host]m[required_host][port]){m[required_host][port]-ref(url,urls[url]);urls[url]-port=prot
;sort_urls();return1;/*Noneedtoopenanewport*/}if(!m[required_host])m[required_host]=([]);m[required_host][port]=prot(port,required_host);if(!(m[required_host][
port])){m_delete(urls,url);m_delete(m[required_host],port);report_error("CannotregisterURL"+url+",cannotbindtheport!\n");return0;}urls[url]-port=m[required_hos
t][port];m[required_host][port]-ref(url,urls[url]);sort_urls();return1;}#endifobjectfind_configuration(stringname){name=replace(lower_case(name)-"","/","-");fo
reach(configurations,objecto)if((lower_case(replace(o-name-"","/","-"))==name)||(lower_case(replace(o-query_name()-"","/","-"))==name))returno;}//Createanewcon
figurationfromscratch.//'type'isasintheform.'none'foraemptyconfiguration.intadd_new_configuration(stringname,stringtype){}mapping(string:array(int))error_log=(
[]);//Writeastringtotheconfigurationinterfaceerrorlogandtostderr.voidnwrite(strings,int|voidperr,int|voidtype){if(!error_log[type+","+s])error_log[type+","+s]=
({time()});elseerror_log[type+","+s]+=({time()});if(type=1)roxen_perror(s);}//WhenwasRoxenstarted?intboot_time=time();intstart_time=time();stringversion(){retu
rnQUERY(default_ident)?real_version:QUERY(ident);}publicvoidlog(mappingfile,objectrequest_id){if(!request_id-conf)return;request_id-conf-log(file,request_id);}
//Supportforuniqueuserid'sprivateobjectcurrent_user_id_file;privateintcurrent_user_id_number,current_user_id_file_last_mod;privatevoidrestore_current_user_id_n
umber(){if(!current_user_id_file)current_user_id_file=open(configuration_dir+"LASTUSER~","rwc");if(!current_user_id_file){call_out(restore_current_user_id_numb
er,2);return;}current_user_id_number=(int)current_user_id_file-read(100);current_user_id_file_last_mod=current_user_id_file-stat()[2];perror("Restoringuniqueus
erIDinformation.("+current_user_id_number+")\n");#ifdefFD_DEBUGmark_fd(current_user_id_file-query_fd(),LOCALE-unique_uid_logfile());#endif}intincrease_id(){if(
!current_user_id_file){restore_current_user_id_number();returncurrent_user_id_number+time();}if(current_user_id_file-stat()[2]!=current_user_id_file_last_mod)r
estore_current_user_id_number();current_user_id_number++;//perror("Newuniqueid:"+current_user_id_number+"\n");current_user_id_file-seek(0);current_user_id_file
-write((string)current_user_id_number);current_user_id_file_last_mod=current_user_id_file-stat()[2];returncurrent_user_id_number;}publicstringfull_status(){int
tmp;stringres="";arrayfoo=({0.0,0.0,0.0,0.0,0});if(!sizeof(configurations))returnLOCALE-no_servers_enabled();foreach(configurations,objectconf){if(!conf-sent||
!conf-received||!conf-hsent)continue;foo[0]+=conf-sent-mb()/(float)(time(1)-start_time+1);foo[1]+=conf-sent-mb();foo[2]+=conf-hsent-mb();foo[3]+=conf-received-
mb();foo[4]+=conf-requests;}for(tmp=1;tmp4;tmp++){//FIXME:LOCALE?if(foo[tmp]1024.0)foo[tmp]=sprintf("%.2fMB",foo[tmp]);elsefoo[tmp]=sprintf("%.2fGB",foo[tmp]/1
024.0);}intuptime=time()-start_time;intdays=uptime/(24*60*60);inthrs=uptime/(60*60);intmin=uptime/60-hrs*60;hrs-=days*24;tmp=(int)((foo[4]*600.0)/(uptime+1));r
eturn(LOCALE-full_status(real_version,boot_time,start_time-boot_time,days,hrs,min,uptime%60,foo[1],foo[0]*8192.0,foo[2],foo[4],(float)tmp/(float)10,foo[3]));}s
taticintabs_started;voidrestart_if_stuck(intforce){remove_call_out(restart_if_stuck);if(!(QUERY(abs_engage)||force))return;if(!abs_started){abs_started=1;roxen
_perror("Anti-BlockSystemEnabled.\n");}call_out(restart_if_stuck,10);signal(signum("SIGALRM"),lambda(intn){werror(sprintf("****%s:ABSengaged!\n""Tryingtodumpba
cklog:\n",ctime(time())-"\n"));catch{//Catchforparanoiareasons.describe_all_threads();};werror(sprintf("****%s:ABSexitingroxen!\n\n",ctime(time())));_exit(1);/
/Itmightnowquitcorrectlyotherwise,ifit's//lockedup});alarm(60*QUERY(abs_timeout)+10);}voidpost_create(){if(QUERY(abs_engage))call_out(restart_if_stuck,10);if(Q
UERY(suicide_engage))call_out(restart,60*60*24*QUERY(suicide_timeout));}//Cacheusedbythevariousconfigurationinterfacemodulesetc.//ItshouldbeOKtodeletethiscache
atanytime.classConfigIFCache{stringdir;voidcreate(stringname){dir="config_caches/"+replace(configuration_dir-".","/","-")+"/"+name+"/";mkdirhier(dir+"/foo");}m
ixedset(stringname,mixedto){Stdio.Filef=Stdio.File();if(!f-open(dir+replace(name,"/","-"),"wct")){mkdirhier(dir+"/foo");if(!f-open(dir+replace(name,"/","-"),"w
ct")){report_error("Failedtocreateconfigurationinterfacecachefile("+dir+replace(name,"/","-")+")"+strerror(errno())+"\n");returnto;}}f-write(encode_value(to));
returnto;}mixedget(stringname){Stdio.Filef=Stdio.File();if(!f-open(dir+replace(name,"/","-"),"r"))return0;returndecode_value(f-read());}voiddelete(stringname){
rm(dir+replace(name,"/","-"));}}classImageCache{stringname;stringdir;functiondraw_function;mappingdata_cache=([]);//notnormallyused.mappingmeta_cache=([]);stat
icmappingmeta_cache_insert(stringi,mappingwhat){returnmeta_cache[i]=what;}staticstringdata_cache_insert(stringi,stringwhat){returndata_cache[i]=what;}staticmix
edfrommapp(mappingwhat){if(what[""])returnwhat[""];returnwhat;}staticvoiddraw(stringname,RequestIDid){mixedargs=Array.map(Array.map(name/"$",argcache-lookup,id
-client),frommapp);mappingmeta;stringdata;mixedreply=draw_function(@copy_value(args),id);if(arrayp(args))args=args[0];if(objectp(reply)||(mappingp(reply)reply-
img)){intquant=(int)args-quant;stringformat=lower_case(args-format||"gif");stringdither=args-dither;Image.Colortablect;objectalpha;inttrue_alpha;if(args-fs||di
ther=="fs")dither="floyd_steinberg";if(dither=="random")dither="random_dither";if(format=="jpg")format="jpeg";if(mappingp(reply)){alpha=reply-alpha;reply=reply
-img;}if(args-gamma)reply=reply-gamma((float)args-gamma);if(args["true-alpha"])true_alpha=1;if(args["opaque-value"]){true_alpha=1;intov=(int)(((float)args["opa
que-value"])*2.55);if(ov0)ov=0;elseif(ov255)ov=255;if(alpha){objecti=Image.image(reply-xsize(),reply-ysize(),ov,ov,ov);i-paste_alpha(alpha,ov);alpha=i;}else{al

Way Cool. Check it Out Now

B A C K