#include "wfm.h" // // Dispense Common HTML Header // Used by all (?) functions that display HTML pages // void html_title(char *msg) { fprintf(cgiOut, HTML_HEADER "\n" "%s : %s\n", rt.iconsurl, cfg.favicon, cfg.tagline, msg); // (strlen(wp.virt_dirname)>0) ? ' ' : '/', TAGLINE, wp.virt_dirname } // // Dispense embedded icons // Called on early action=icon // int icon(void) { char icon_name[32]={0}; cgiFormStringNoNewlines("name", icon_name, sizeof(icon_name)); fprintf(cgiOut, "Cache-Control: max-age=29030400, public\r\n"); cgiHeaderContentType("image/gif"); #include "wfmicondis.h" exit(0); } // // Display upload status in SHM via key_id // Called by early action=upstat // /*void upload_status(void) { int shm_key=-1; int shm_id=-1; char *shm_val=NULL; time_t t; char *spin[]={"|", "/", "--", "\\"}; if(cgiFormInteger("upload_id", &shm_key, 0) == cgiFormSuccess && shm_key) { shm_id = shmget(shm_key, SHM_SIZE, 0666); if(shm_id >= 0) shm_val = shmat(shm_id, NULL, 0); } fprintf(cgiOut, "Cache-Control: no-cache\r\n"); cgiHeaderContentType("text/plain"); time(&t); if(shm_val) fprintf(cgiOut, "%s %s\r\n", spin[(int)t % 4], shm_val); else fprintf(cgiOut, "%s\r\n", spin[(int)t % 4]); if (shm_val) shmdt(shm_val); exit(0); }*/ // // Generate auth token // Used by access_check() to compare tokens and login() to generate token from a web form // char *md5hash(char *str, ...) { va_list ap; char buff[1024]={0}; md5_state_t state; md5_byte_t digest[16]={0}; char *outstr; int i; if(str) { va_start(ap, str); vsnprintf(buff, sizeof(buff), str, ap); va_end(ap); outstr=(char*) malloc((sizeof(digest)*2)+2); memset(outstr, 0, (sizeof(digest)*2)+2); md5_init(&state); md5_append(&state, (const md5_byte_t *)buff, strlen(buff)); md5_finish(&state, digest); for (i = 0; i < sizeof(digest); i++) sprintf(outstr + i * 2, "%02x", digest[i]); if(strlen(outstr)) return outstr; else return NULL; } else { return NULL; } } // // WFM Login Procedure // Called from WFM main procedure if no sufficient access permission available and no // JavaScript is available in the browser. Normally client side genrates the auth token. // void login(void) { char username[64]={0}; char password[64]={0}; cgiFormStringNoNewlines("username", username, sizeof(username)); cgiFormStringNoNewlines("password", password, sizeof(password)); if(strlen(username) && strlen(password)) redirect("%s?directory=%s&login=server&token=%s", cgiScriptName, wp.virt_dirname_urlencoded, md5hash("%s:%s", cgiRemoteAddr, md5hash("%s:%s", username, password))); // generate MD5 as if it was the client else login_ui(); // display actual login page, which normally generates token in JavaScript } // // Access_check // Called by cfg read routine during initialization // void access_check(char *access_string) { char ipaddr[32]={0}; char user[64]={0}; char pass[64]={0}; char type[4]={0}; //dbgprintf("access_check=%s", access_string); if(sscanf(access_string, "access-ip=%2s:%30s", type, ipaddr)==2) { if(ipaddr[0]=='*' || strcmp(cgiRemoteAddr, ipaddr)==0) { if(strcmp(type, "ro")==0) rt.access_level=PERM_RO; else if(strcmp(type, "rw")==0) rt.access_level=PERM_RW; rt.auth_method=AUTH_IP; } } else if(sscanf(access_string, "access-md5pw=%2[^':']:%30[^':']:%63s", type, user, pass)==3) { cfg.users_defined=1; // perform user auth by comparing user supplied token with system generated token if(strcmp(md5hash("%s:%s", cgiRemoteAddr, pass), rt.token)==0) { if(strcmp(type, "ro")==0) rt.access_level=PERM_RO; else if(strcmp(type, "rw")==0) rt.access_level=PERM_RW; rt.access_as_user=1; rt.auth_method=AUTH_MD5; strncpy(rt.loggedinuser, user, sizeof(rt.loggedinuser)); } } else if(sscanf(access_string, "access-htauth=%2[^':']:%30s", type, user)==2) { cfg.users_defined=1; if(user[0]=='*' || (getenv("REMOTE_USER") && strcmp(user, getenv("REMOTE_USER"))==0)) { if(strcmp(type, "ro")==0) rt.access_level=PERM_RO; else if(strcmp(type, "rw")==0) rt.access_level=PERM_RW; rt.access_as_user=1; snprintf(rt.loggedinuser, sizeof(rt.loggedinuser), "%s", getenv("REMOTE_USER") ); rt.auth_method=AUTH_HT; } } } // // Check filename // Should be called by every function that uses filename // Function can be passed implicit filename or use the global variable // void checkfilename(char *inp_filename) { char temp_dirname[sizeof(wp.phys_filename)]={0}; char temp_filename[sizeof(wp.virt_filename)]={0}; char *bname; if(inp_filename && strlen(inp_filename)) { snprintf(temp_filename, sizeof(temp_filename), "%s", inp_filename); } else if(cgiFormFileName("filename", temp_filename, sizeof(wp.virt_filename)) == cgiFormSuccess) { } else if(cgiFormStringNoNewlines("filename", temp_filename, sizeof(wp.virt_filename)) == cgiFormSuccess) { } else error("No filename specified."); // We only want basename from the client! bname=strrchr(temp_filename, '/'); if(!bname) bname=strrchr(temp_filename, '\\'); if(!bname) bname=temp_filename; else (void) *bname++; strip(bname, sizeof(wp.virt_filename), VALIDCHRS); snprintf(wp.virt_filename, sizeof(wp.virt_filename), "%s", bname); wp.virt_filename_urlencoded=url_encode(wp.virt_filename); snprintf(wp.phys_filename, sizeof(wp.phys_filename), "%s/%s", wp.phys_dirname, wp.virt_filename); // Do checks if(!strlen(wp.phys_filename) || strlen(wp.phys_filename)>(sizeof(wp.phys_filename)-2)) error("Invalid pfilename lenght [%d]", strlen(wp.phys_filename)); if(!strlen(wp.virt_filename) || strlen(wp.virt_filename)>(sizeof(wp.virt_filename)-2)) error("Invalid vfilename lenght [%d]", strlen(wp.virt_filename)); if(strstr(wp.phys_filename, "..")) error("Double dots in pfilename"); if(strstr(wp.virt_filename, "..")) error("Double dots in vfilename"); snprintf(temp_dirname, sizeof(temp_dirname), "%s", wp.phys_filename); if(strlen(dirname(temp_dirname)) < strlen(cfg.homedir)) error("Basename path too short"); } // // Check destination // Only called by move() // void checkdestination(void) { int absolute_destination; cgiFormStringNoNewlines("destination", wp.virt_destination, sizeof(wp.virt_destination)); strip(wp.virt_destination, sizeof(wp.virt_filename), VALIDCHRS_DIR); cgiFormInteger("absdst", &absolute_destination, 0); // move operation relies on absolute paths, rename does not if(absolute_destination) snprintf(wp.phys_destination, sizeof(wp.phys_destination), "%s/%s", cfg.homedir, wp.virt_destination); else snprintf(wp.phys_destination, sizeof(wp.phys_destination), "%s/%s", wp.phys_dirname, wp.virt_destination); if(strlen(wp.phys_destination)<1 || strlen(wp.phys_destination)>(sizeof(wp.phys_filename)-2)) error("Invalid pdestination lenght [%d]", strlen(wp.phys_destination)); if(strlen(wp.virt_destination)<1 || strlen(wp.virt_destination)>(sizeof(wp.virt_filename)-2)) error("Invalid vdestination lenght [%d]", strlen(wp.virt_destination)); if(strstr(wp.phys_destination, "..")) error("Double dots in pdestination"); if(strstr(wp.virt_destination, "..")) error("Double dots in vdestination"); } // // Check directory // Only called by cgiMain during initialization // void checkdirectory(void) { char temp[sizeof(wp.virt_dirname)]={0}; char *real; // virtual directory cgiFormStringNoNewlines("directory", wp.virt_dirname, sizeof(wp.virt_dirname)); strip(wp.virt_dirname, sizeof(wp.virt_dirname), VALIDCHRS_DIR); if(!strlen(wp.virt_dirname)) strcpy(wp.virt_dirname, "/"); wp.virt_dirname_urlencoded=url_encode(wp.virt_dirname); // parent strncpy(temp, wp.virt_dirname, sizeof(temp)); strncpy(wp.virt_parent, dirname(temp), sizeof(wp.virt_dirname)); wp.virt_parent_urlencoded=url_encode(wp.virt_parent); // physical directory snprintf(wp.phys_dirname, sizeof(wp.phys_dirname), "%s/%s", cfg.homedir, wp.virt_dirname); if(strlen(wp.phys_dirname)<2 || strlen(wp.phys_dirname)>(sizeof(wp.phys_dirname)-2)) error("Invalid directory name lenght 2"); if(strlen(wp.phys_dirname) < strlen(cfg.homedir)) error("Invalid directory name 3."); if(strstr(wp.phys_dirname, "..")) error("Double dots in dirname"); real=realpath(wp.phys_dirname, NULL); if(!real) error("Unable to resolve directory path.
%s", strerror(errno)); if(strlen(real) > sizeof(wp.phys_dirname)-2) error("Resolved path too long"); snprintf(wp.phys_dirname, sizeof(wp.phys_dirname), "%s", real); free(real); } void tstart(void) { gettimeofday(&mt, 0); t1=mt.tv_sec+(mt.tv_usec/1000000.0); } void tstop(void) { gettimeofday(&mt, 0); t2=mt.tv_sec+(mt.tv_usec/1000000.0); } // strip unwanted characters from string // deny all by default // allow a=alpha n=numeric u=spaces as underscore // use: allow="an.-@" for email // use: allow="anu.-_!" for filenames // ! is a cmd termination char // use !! to allow only ! char // use !a to allow only letter a int strip(char *str, int len, char *allow) { int n,a; int alpha=0, number=0, spctou=0; char *dst; if(!str || !strlen(str) || !allow || !strlen(allow)) return -1; if(*allow == 'a') { alpha=1; allow++; } if(*allow == 'n') { number=1; allow++; } if(*allow == 'u') { spctou=1; allow++; } if(*allow == '!') { allow++; } dst=str; for(n=0; n= P1024_1 && v < P1024_2 ) { size = v / P1024_1; unit = 'K'; } else if(v >= P1024_2 && v < P1024_3 ) { size = v / P1024_2; unit = 'M'; } else if(v >= P1024_3 && v < P1024_4 ) { size = v / P1024_3; unit = 'G'; } else { size = v; unit = ' '; } buffer=(char *)calloc(128, sizeof(char)); if(unit == 'G' && bold) snprintf(buffer, 128, " %5.1f %cB ", size, unit); else if(unit == 'M' && bold) snprintf(buffer, 128, "%5.1f %cB", size, unit); else if(bold) snprintf(buffer, 128, " %5.1f %cB ", size, unit); else snprintf(buffer, 128, " %5.1f %cB", size, unit); return (char *)buffer; } // // Debug print to a file // void dbgprintf(char *msg, ...) { va_list ap; char buff[1024]={0}; FILE *f; if(msg) { va_start(ap, msg); vsnprintf(buff, sizeof(buff), msg, ap); va_end(ap); f=fopen("/tmp/wfmdbg.log", "a"); if(!f) error("Unable to open debug file"); fprintf(f, "DEBUG: %s\n", buff); fflush(f); fclose(f); } } // // redirect browser // void redirect(char *location, ...) { va_list ap; char buff[1024]={0}; va_start(ap, location); vsnprintf(buff, sizeof(buff), location, ap); va_end(ap); cgiHeaderLocation(buff); exit(0); } // // Log off user from HTAUTH session // void logoff() { cgiHeaderStatus(401, "Unauthorized"); fprintf(cgiOut, "You have been logged out."); exit(0); } // // Load and process config file // Invoke Access Check // void cfgload(void) { FILE *cfgfile; char cfgname[128]={0}; char cfgline[256]={0}; char c_tagline[]="tagline="; char c_favicon[]="favicon="; char c_homeurl[]="browser-url="; char c_homedir[]="directory="; char c_editdef[]="txt-default-edit=true"; char c_editany[]="edit-any-file=true"; char c_du[]="recursive-du=true"; char c_largeset[]="large-file-set=true"; char c_access[]="access"; memset(&cfg, 0, sizeof(cfg)); memset(&rt, 0, sizeof(rt)); memset(&wp, 0, sizeof(wp)); cgiFormStringNoNewlines("token", rt.token, sizeof(rt.token)); snprintf(rt.iconsurl, sizeof(rt.iconsurl), "%s?ea=icon&name=", cgiScriptName); snprintf(cfgname, sizeof(cfgname), "%s.cfg", basename(cgiScriptName)); cfgfile=fopen(cfgname, "r"); if(!cfgfile) error("Unable to open configuration file %s.
%s", cfgname, strerror(errno)); while(fgets(cfgline, sizeof(cfgline), cfgfile)) { if((*cfgline==';')||(*cfgline=='/')||(*cfgline=='#')||(*cfgline=='\n')) continue; else if(strncmp(cfgline, c_homedir, strlen(c_homedir))==0) snprintf(cfg.homedir, sizeof(cfg.homedir), "%s", cfgline+strlen(c_homedir)); else if(strncmp(cfgline, c_homeurl, strlen(c_homeurl))==0) snprintf(cfg.homeurl, sizeof(cfg.homeurl), "%s", cfgline+strlen(c_homeurl)); else if(strncmp(cfgline, c_tagline, strlen(c_tagline))==0) snprintf(cfg.tagline, sizeof(cfg.tagline), "%s", cfgline+strlen(c_tagline)); else if(strncmp(cfgline, c_favicon, strlen(c_favicon))==0) snprintf(cfg.favicon, sizeof(cfg.favicon), "%s", cfgline+strlen(c_favicon)); else if(strncmp(cfgline, c_editdef, strlen(c_editdef))==0) cfg.edit_by_default=1; else if(strncmp(cfgline, c_editany, strlen(c_editany))==0) cfg.edit_any_file=1; else if(strncmp(cfgline, c_largeset, strlen(c_largeset))==0) cfg.largeset=1; else if(strncmp(cfgline, c_du, strlen(c_du))==0) cfg.recursive_du=1; else if(strncmp(cfgline, c_access, strlen(c_access))==0) access_check(cfgline); } fclose(cfgfile); // remove newlines if(strlen(cfg.homedir)>2) cfg.homedir[strlen(cfg.homedir)-1]='\0'; if(strlen(cfg.homeurl)>2) cfg.homeurl[strlen(cfg.homeurl)-1]='\0'; if(strlen(cfg.tagline)>2) cfg.tagline[strlen(cfg.tagline)-1]='\0'; if(strlen(cfg.favicon)>2) cfg.favicon[strlen(cfg.favicon)-1]='\0'; // do checks if(strlen(cfg.homedir) < 4) error("Home directory not defined."); if(cfg.homedir[0]!='/') error("Home directory must be absolute path."); if(!strlen(cfg.tagline)) strcpy(cfg.tagline, "Web File Manager"); if(!strlen(cfg.favicon)) strcpy(cfg.favicon, "wfmicon.gif"); checkdirectory(); // JavaScript check if(strncmp(cgiUserAgent, "Mozilla/5", 9)==0) rt.js=2; else if(strncmp(cgiUserAgent, "Mozilla/4.0 (compatible; MSIE 6", 31)==0) rt.js=2; else if(strncmp(cgiUserAgent, "Mozilla/4.0 (compatible; MSIE 7", 31)==0) rt.js=2; else if(strncmp(cgiUserAgent, "Mozilla/4.0 (compatible; MSIE 8", 31)==0) rt.js=2; else if(strncmp(cgiUserAgent, "Mozilla/4", 9)==0) rt.js=1; else rt.js=0; } // // WFM Entry // int cgiMain(void) { char action[32]={0}; char ea[8]={0}; // early action - simple actions before cfg is read or access check performed (no authentication!) // note that ea functions must exit() cgiFormStringNoNewlines("ea", ea, sizeof(ea)); if(strcmp(ea, "icon")==0) icon(); // if(strcmp(ea, "upstat")==0) upload_status(); if(strcmp(ea, "logoff")==0) logoff(); // normal initialization tstart(); fprintf(cgiOut, "Cache-Control: max-age=0, private\r\nExpires: -1\r\n"); cfgload(); cgiFormStringNoNewlines("action", action, sizeof(action)); if(cgiFormSubmitClicked("noop")==cgiFormSuccess && rt.access_level >= PERM_RO) dirlist(); else if(cgiFormSubmitClicked("multi_delete_prompt")==cgiFormSuccess && rt.access_level >= PERM_RO) multiprompt_ui("delete"); else if(cgiFormSubmitClicked("multi_delete_prompt.x")==cgiFormSuccess && rt.access_level >= PERM_RO) multiprompt_ui("delete"); else if(cgiFormSubmitClicked("multi_move_prompt")==cgiFormSuccess && rt.access_level >= PERM_RO) multiprompt_ui("move"); else if(cgiFormSubmitClicked("multi_move_prompt.x")==cgiFormSuccess && rt.access_level >= PERM_RO) multiprompt_ui("move"); else if(cgiFormSubmitClicked("upload")==cgiFormSuccess && rt.access_level >= PERM_RW) receivefile(); else if(strcmp(action, "save")==0 && rt.access_level >= PERM_RO) save(); else if(strcmp(action, "delete")==0 && rt.access_level >= PERM_RW) delete(); else if(strcmp(action, "delete_prompt")==0 && rt.access_level >= PERM_RW) multiprompt_ui("delete"); else if(strcmp(action, "move_prompt")==0 && rt.access_level >= PERM_RW) multiprompt_ui("move"); else if(strcmp(action, "rename_prompt")==0 && rt.access_level >= PERM_RW) singleprompt_ui("move"); else if(strcmp(action, "move")==0 && rt.access_level >= PERM_RW) move(); else if(strcmp(action, "edit")==0 && rt.access_level >= PERM_RO) edit_ui(); else if(strcmp(action, "edit_save")==0 && rt.access_level >= PERM_RW) edit_save(); else if(strcmp(action, "mkfile")==0 && rt.access_level >= PERM_RW) mkfile(); else if(strcmp(action, "mkfile_prompt")==0 && rt.access_level >= PERM_RW) singleprompt_ui("mkfile"); else if(strcmp(action, "mkdir")==0 && rt.access_level >= PERM_RW) newdir(); else if(strcmp(action, "mkdir_prompt")==0 && rt.access_level >= PERM_RW) singleprompt_ui("mkdir"); else if(strcmp(action, "mkurl")==0 && rt.access_level >= PERM_RW) mkurl(); else if(strcmp(action, "mkurl_prompt")==0 && rt.access_level >= PERM_RW) singleprompt_ui("mkurl"); else if(strcmp(action, "goto_url")==0 && rt.access_level >= PERM_RO) goto_url(); else if(strcmp(action, "about")==0 && rt.access_level >= PERM_RO) about(); else if(strcmp(action, "login")==0 ) login(); else if( rt.access_level >= PERM_RO) dirlist(); else if(cfg.users_defined) // if users present but supplied credentials didn't match, or credentials not specified redirect("%s?action=login", cgiScriptName); else error("Access Denied."); return 0; }