#include #include NETWORK_H #include inherit LIB_DAEMON; string gfile, ghost; int upgrading = 0; int max_outbound = 5; int max_retries = 200; mapping FilesMap = ([]); mapping FileQueue = ([]); string lucmd = CMD_LIVEUPGRADE; void validate(){ if(!master()->valid_apply(({ "SECURE" })) ){ string offender = identify(previous_object(-1)); debug("LUGET_D SECURITY VIOLATION: "+offender+" ",get_stack(),"red"); log_file("security", "\n"+timestamp()+" LUGET_D breach: "+offender+" "+get_stack()); error("LUGET_D SECURITY VIOLATION: "+offender+" "+get_stack()); } } static void create() { daemon::create(); set_heart_beat(1); } varargs string convname(string raw, int rev){ string ret = replace_string(raw,"/","0^0"); if(rev){ ret = replace_string(raw,"0^0","/"); ret = replace_string(ret,"/secure/upgrades/files/",""); } else ret = "/secure/upgrades/files/"+ret; return ret; } static void LUReport(string str){ if(upgrading) lucmd->eventReceiveReport(str); } static int ProcessData(int fd){ int i; int nullify = 0; string where; string tmp=""; string data= FilesMap[fd]["contents"]; mixed tmp2 = ""; string *nullifiers = ({ "HTTP/", "HTTP0^0", "Date:", "Server:", "Last-Modified:", "ETag:", "Accept-Ranges:", "Content-Length:", "Connection:", "Content-Type:", "X-Pad:" }); string *tmparr = ({}); FilesMap[fd]["last_char"] = last(data,1); FilesMap[fd]["first_char"] = first(data,1); tmparr = explode(data,"\n"); i = sizeof(tmparr); foreach(string element in tmparr){ if(!strsrch(element,"404 Not Found")) nullify = 1; foreach(string nully in nullifiers){ if(!strsrch(element,nully)){ tmparr -= ({ element }); } } } if(!strsrch(tmparr[0],"Location: ")) nullify = 1; if(!nullify && sizeof(tmparr)){ while(!sizeof(tmparr[0]) || sizeof(tmparr[0]) < 2){ tmparr = tmparr[1..]; } } data = implode(tmparr,"\n"); if(!FilesMap[fd]["started"]) FilesMap[fd]["started"] = 1; where = FilesMap[fd]["where"]; FilesMap[fd]["where"] = replace_string(where,"0^0code0^0upgrades0^0"+mudlib_version(),""); foreach(string nully in nullifiers){ if(grepp(where, nully)) nullify = 1; } FilesMap[fd]["normalname"] = convname(FilesMap[fd]["where"],1); if(directory_exists(FilesMap[fd]["normalname"])) nullify = 1; if(!nullify){ if(data && (data[0] == 10 || data[0] == 13)) data = data[1..]; if(last(data,1) != "\n") data += "\n"; FilesMap[fd]["contents"] = data; } else FilesMap[fd]["contents"] = 0; } void close_callback(int fd){ validate(); if(FilesMap[fd]){ LUReport("Received "+FilesMap[fd]["file"]); ProcessData(fd); if(FilesMap[fd]["contents"] && sizeof(FilesMap[fd]["contents"])){ write_file(FilesMap[fd]["where"], FilesMap[fd]["contents"],1); } FilesMap[fd] = 0; map_delete(FilesMap,fd); } } varargs int GetFile(string source, string file, string host, string where, int port){ int fd, status; validate(); if(sizeof(FilesMap) > max_outbound){ if(!FileQueue) FileQueue = ([]); if(!FileQueue[file]) FileQueue[file] = ([ "source" : source, "file" : file, "host" : host, "where" : where, "port" : (port || 80), "creation" : time(), "started" : 0, "user" : (this_player() ? this_player()->GetName() : identify(this_object())), "first_char" : "", "last_char" : "" ]); return 0; } fd = socket_create(STREAM, "read_callback", "close_callback"); if( fd < 0 ){ return 0; } FilesMap[fd] = ([ "source" : source, "file" : file, "host" : host, "where" : where, "port" : (port || 80), "creation" : time(), "started" : 0, "user" : (this_player() ? this_player()->GetName() : identify(this_object())) ]); status = socket_connect(fd, source +" "+port, "read_callback", "write_callback"); if( status < 0 ){ return 0; } return fd; } static varargs void RetryGet(int i){ mixed *queue = keys(FileQueue); if(!i) i = 2; if(i > max_outbound) i = max_outbound; if(!FileQueue || !sizeof(FileQueue)){ return 0; } foreach(mixed element in queue[0..i]){ mapping TmpMap = FileQueue[element]; mixed foo = GetFile(TmpMap["source"],TmpMap["file"],TmpMap["host"],TmpMap["where"],TmpMap["port"]); if(foo){ LUReport("%^CYAN%^Requesting: file: "+TmpMap["file"]+"%^RESET%^"); map_delete(FileQueue, element); } } } static void CleanFD(int fd){ if(!FilesMap[fd]) socket_close(fd); else if(!socket_status(fd)) map_delete(FilesMap,fd); else if(socket_status(fd)[1] != "DATA_XFER"){ map_delete(FilesMap,fd); socket_close(fd); } } void heart_beat(){ if((!FileQueue || !sizeof(FileQueue)) && (!FilesMap || !sizeof(FilesMap))){ if(upgrading){ LUReport("\nFile download complete. After backing up your mud, issue the command: liveupgrade apply"); upgrading = 0; } return; } RetryGet(); } void write_callback(int fd){ string str; int result = 0; validate(); if(!FilesMap[fd] || !socket_status(fd)){ CleanFD(fd); return; } str ="GET "+FilesMap[fd]["file"]+" HTTP/1.0"+CARRIAGE_RETURN+"\n"+ "Host: "+FilesMap[fd]["host"]+CARRIAGE_RETURN+"\n" + "User-Agent: "+ FilesMap[fd]["user"] + "@" + mud_name() + " " + mudlib()+ "/" + mudlib_version() +" ("+ query_os_type()+";) "+ version() + " "+ CARRIAGE_RETURN+"\n"+CARRIAGE_RETURN+"\n"; result = socket_write(fd, str); } void read_callback(int fd, mixed data){ validate(); if(!FilesMap[fd]){ return; } if(!FilesMap[fd]["contents"]) FilesMap[fd]["contents"] = data; else FilesMap[fd]["contents"] += data; } varargs int eventMajorUpgrade(string source, string *files, string host, int port){ string *allfiles = sort_array(files,1); validate(); if(upgrading){ LUReport("There is a liveupgrade already in progress."); return 0; } upgrading = 1; foreach(string element in allfiles){ //if(last_string_element(element,1) == "/") continue; GetFile(source, element, host, convname(element), (port || 80)); } return 1; } int eventDestruct(){ int *fds = keys(FilesMap); validate(); if(sizeof(fds)){ foreach(int fd in fds){ socket_close(fd); } } return ::eventDestruct(); } int GetUpgrading(){ return upgrading; }