aboutsummaryrefslogtreecommitdiff
path: root/wfm.c
diff options
context:
space:
mode:
authortenox <as@tenoware.com>2016-01-11 23:57:03 -0800
committertenox <as@tenoware.com>2016-01-11 23:57:03 -0800
commit245eaad379742cc0ba9992c858523664b02102fb (patch)
tree47eb4faed0a498e42aaf927ed24eb66bac601a9e /wfm.c
downloadwfm-245eaad379742cc0ba9992c858523664b02102fb.tar.gz
initial commit1.0.0
Diffstat (limited to 'wfm.c')
-rw-r--r--wfm.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/wfm.c b/wfm.c
new file mode 100644
index 0000000..622189a
--- /dev/null
+++ b/wfm.c
@@ -0,0 +1,462 @@
+#include "wfm.h"
+
+//
+// Dispense Common HTML Header
+// Used by all (?) functions that display HTML pages
+//
+void html_title(char *msg) {
+ fprintf(cgiOut,
+ HTML_HEADER
+ "<LINK REL=\"icon\" TYPE=\"image/gif\" HREF=\"%swfmicon.gif\">\n"
+ "<TITLE>%s : %s</TITLE>\n",
+ ICONSURL, TAGLINE, msg); // (strlen(virt_dirname)>0) ? ' ' : '/', TAGLINE, virt_dirname
+}
+
+
+//
+// Dispense embedded icons
+// Called on early action=icon
+//
+int icon(void) {
+ char icon_name[32];
+
+ 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=0;
+ int shm_id=0;
+ char *shm_addr='\0';
+
+ if(cgiFormInteger("upload_id", &shm_key, 0) == cgiFormSuccess && shm_key) {
+ shm_id = shmget(shm_key, SHM_SIZE, 0666);
+ if(shm_id > 0)
+ shm_addr = shmat(shm_id, NULL, 0);
+ }
+
+ fprintf(cgiOut, "Cache-Control: no-cache\r\n");
+ cgiHeaderContentType("text/plain");
+
+ if(shm_id && shm_addr && *shm_addr)
+ fprintf(cgiOut, "%s\r\n", shm_addr);
+ else
+ fprintf(cgiOut, "-------\r\n");
+
+ if (shm_id && shm_addr)
+ shmdt(shm_addr);
+
+ exit(0);
+}
+
+
+//
+// Generate auth token
+// Used by access_check() to compare tokens and login() to generate token from a web form
+//
+char *mktoken(char *str) {
+ md5_state_t state;
+ md5_byte_t digest[16];
+ char *outstr;
+ int i;
+
+ outstr=(char*) malloc((sizeof(digest)*2)+2);
+ memset(outstr, 0, (sizeof(digest)*2)+2);
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)str, strlen(str));
+ md5_finish(&state, digest);
+
+ for (i = 0; i < sizeof(digest); i++)
+ sprintf(outstr + i * 2, "%02x", digest[i]);
+
+ return outstr;
+}
+
+//
+// WFM Login Procedure
+// Called from WFM main procedure if no sufficient access permission available
+//
+void login(void) {
+ char username[64];
+ char password[64];
+ char token_inp[256];
+
+ cgiFormStringNoNewlines("username", username, sizeof(username)); // only used if JavaScript not
+ cgiFormStringNoNewlines("password", password, sizeof(password)); // available in the browser
+
+ if(strlen(username)) {
+ snprintf(token_inp, sizeof(token_inp), "%s:%s:%s", cgiRemoteAddr, username, password);
+ redirect("?directory=%s&login=server&token=%s", virt_dirname, mktoken(token_inp)); // 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];
+ char user[32];
+ char pass[32];
+ char type[4];
+ char token_inp[64];
+
+ memset(ipaddr, 0, sizeof(ipaddr));
+ memset(user, 0, sizeof(user));
+ memset(pass, 0, sizeof(pass));
+ memset(type, 0, sizeof(type));
+
+ if(sscanf(access_string, "access-ip=%2s:%30s", type, ipaddr)==2) {
+
+ if(ipaddr[0]=='*') {
+ if(strcmp(type, "ro")==0)
+ access_level=PERM_RO;
+ else if(strcmp(type, "rw")==0)
+ access_level=PERM_RW;
+ }
+ else if(strcmp(cgiRemoteAddr, ipaddr)==0) {
+ if(strcmp(type, "ro")==0)
+ access_level=PERM_RO;
+ else if(strcmp(type, "rw")==0)
+ access_level=PERM_RW;
+ }
+
+ }
+ else if(sscanf(access_string, "access-user=%2[^':']:%30[^':']:%30s", type, user, pass)==3) {
+ users_defined=1;
+
+ snprintf(token_inp, sizeof(token_inp), "%s:%s:%s", cgiRemoteAddr, user, pass);
+ // perform user auth by comparing user supplied token with system generated token
+ if(strcmp(mktoken(token_inp), token)==0) {
+ if(strcmp(type, "ro")==0) {
+ access_level=PERM_RO;
+ access_as_user=1;
+ strncpy(loggedinuser, user, sizeof(loggedinuser));
+ }
+ else if(strcmp(type, "rw")==0) {
+ access_level=PERM_RW;
+ access_as_user=1;
+ strncpy(loggedinuser, user, sizeof(loggedinuser));
+ }
+ }
+ }
+}
+
+
+//
+// Check filename
+// Should be used by every function that uses filename
+//
+void checkfilename(char *inp_filename) {
+ char temp_dirname[PHYS_FILENAME_SIZE];
+ char *bname;
+
+ if(inp_filename && strlen(inp_filename)) {
+ strncpy(virt_filename, inp_filename, VIRT_FILENAME_SIZE);
+ }
+ else if(cgiFormFileName("filename", virt_filename, VIRT_FILENAME_SIZE) == cgiFormSuccess) {
+
+ }
+ else if(cgiFormStringNoNewlines("filename", virt_filename, VIRT_FILENAME_SIZE) == cgiFormSuccess) {
+
+ }
+ else
+ error("No filename specified.");
+
+ // We only want basename from the client!
+ bname=strrchr(virt_filename, '/');
+ if(!bname)
+ bname=strrchr(virt_filename, '\\');
+
+ if(!bname)
+ bname=virt_filename;
+ else
+ (void) *bname++;
+
+ strip(bname, VIRT_FILENAME_SIZE, VALIDCHRS);
+ strncpy(virt_filename, bname, VIRT_FILENAME_SIZE);
+ snprintf(phys_filename, PHYS_FILENAME_SIZE, "%s/%s", phys_dirname, virt_filename);
+
+ if(!strlen(phys_filename) || strlen(phys_filename)>(PHYS_FILENAME_SIZE-2)) error("Invalid pfilename lenght");
+ if(!strlen(virt_filename) || strlen(virt_filename)>(VIRT_FILENAME_SIZE-2)) error("Invalid vfilename lenght");
+ if(regexec(&dotdot, phys_filename, 0, 0, 0)==0) error("Double dots in pfilename");
+ if(regexec(&dotdot, virt_filename, 0, 0, 0)==0) error("Double dots in vfilename");
+
+ strncpy(temp_dirname, phys_filename, PHYS_FILENAME_SIZE);
+ if(strlen(dirname(temp_dirname)) < strlen(HOMEDIR)) error("Invalid directory name.");
+}
+
+//
+// Check destination
+// Only used by move()
+//
+void checkdestination(void) {
+ int absolute_destination;
+
+ cgiFormStringNoNewlines("destination", virt_destination, VIRT_DESTINATION_SIZE);
+ strip(virt_destination, VIRT_DESTINATION_SIZE, VALIDCHRS_DST);
+ cgiFormInteger("absdst", &absolute_destination, 0); // move operation relies on absolute paths
+ if(absolute_destination)
+ snprintf(phys_destination, PHYS_DESTINATION_SIZE, "%s/%s", HOMEDIR, virt_destination);
+ else
+ snprintf(phys_destination, PHYS_DESTINATION_SIZE, "%s/%s", phys_dirname, virt_destination);
+
+ if(strlen(phys_destination)<1 || strlen(phys_destination)>(PHYS_DESTINATION_SIZE-2)) error("Invalid vfilename lenght");
+ if(strlen(virt_destination)<1 || strlen(virt_destination)>(VIRT_DESTINATION_SIZE-2)) error("Invalid pfilename lenght");
+ if(regexec(&dotdot, phys_destination, 0, 0, 0)==0) error("Double dots in pfilename");
+ if(regexec(&dotdot, virt_destination, 0, 0, 0)==0) error("Double dots in vfilename");
+}
+
+//
+// Check directory
+// Only used by cgiMain during initialization
+//
+void checkdirectory(void) {
+ char temp[VIRT_DIRNAME_SIZE];
+
+ cgiFormStringNoNewlines("directory", virt_dirname, VIRT_DIRNAME_SIZE);
+ strip(virt_dirname, VIRT_DIRNAME_SIZE, VALIDCHRS_DST);
+ snprintf(phys_dirname, PHYS_DIRNAME_SIZE, "%s/%s", HOMEDIR, virt_dirname);
+
+ if(strlen(phys_dirname)<2 || strlen(phys_dirname)>(PHYS_DIRNAME_SIZE-2))
+ error("Invalid directory name.");
+
+ if(regexec(&dotdot, phys_dirname, 0, 0, 0)==0) error("Invalid directory name.");
+ if(strlen(phys_dirname) < strlen(HOMEDIR)) error("Invalid directory name.");
+
+ if(!strlen(virt_dirname)) strcpy(virt_dirname, "/");
+
+ // parent
+ strncpy(temp, virt_dirname, VIRT_DIRNAME_SIZE);
+ strncpy(virt_parent, dirname(temp), VIRT_DIRNAME_SIZE);
+}
+
+
+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<len && *str!='\0'; n++, str++) {
+ if(alpha && isalpha(*str))
+ *(dst++)=*str;
+ else if(number && isdigit(*str))
+ *(dst++)=*str;
+ else if(spctou && *str==' ')
+ *(dst++)='_';
+ else if(strlen(allow))
+ for(a=0; a<strlen(allow); a++)
+ if(*str==allow[a])
+ *(dst++)=*str;
+ }
+
+ *dst='\0';
+
+ return 0; //strlen(dst);
+}
+
+//
+// Byte unit printf
+//
+char *buprintf(float v, int bold) {
+ float size;
+ char unit;
+ char *buffer;
+
+ if(v == -1)
+ return "&nbsp;";
+
+ if(v >= 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, "<B> %5.1f %cB </B>", size, unit);
+ else if(unit == 'M' && bold)
+ snprintf(buffer, 128, "%5.1f %cB", size, unit);
+ else if(bold)
+ snprintf(buffer, 128, "<FONT COLOR=\"#909090\"> %5.1f %cB </FONT>", size, unit);
+ else
+ snprintf(buffer, 128, " %5.1f %cB", size, unit);
+
+ return (char *)buffer;
+}
+
+//
+// redirect browser
+//
+void redirect(char *location, ...) {
+ va_list ap;
+ char buff[1024];
+
+ va_start(ap, location);
+ vsnprintf(buff, sizeof(buff), location, ap);
+ va_end(ap);
+
+ cgiHeaderLocation(buff);
+}
+
+//
+// CGI entry
+//
+int cgiMain(void) {
+ char action[32];
+ char ea[8];
+ FILE *cfgfile;
+ char cfgname[128];
+ char cfgline[256];
+ char c_tagline[]="tagline=";
+ 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_access[]="access";
+
+ // early action - simple actions before cfg is read or access check performed
+ cgiFormStringNoNewlines("ea", ea, sizeof(ea));
+ if(strcmp(ea, "icon")==0) icon();
+ if(strcmp(ea, "upstat")==0) upload_status();
+
+ // normal initialization
+ tstart();
+
+ if(regcomp(&dotdot, "\\.\\.", REG_EXTENDED | REG_ICASE)!=0)
+ error("Regex compilation problem.<BR>%s", strerror(errno));
+
+ fprintf(cgiOut, "Cache-Control: max-age=0, private\r\nExpires: -1\r\n");
+
+ snprintf(ICONSURL, sizeof(ICONSURL), "%s?ea=icon&amp;name=", cgiScriptName);
+
+
+ // read config file and authenticate
+ access_level=PERM_NO; // no access by default
+ access_as_user=0;
+ users_defined=0;
+ edit_by_default=0; // for .txt files
+ edit_any_file=0;
+ recursive_du=0;
+
+ memset(HOMEDIR, 0, sizeof(HOMEDIR));
+ memset(HOMEURL, 0, sizeof(HOMEURL));
+ memset(TAGLINE, 0, sizeof(TAGLINE));
+
+ cgiFormStringNoNewlines("token", token, sizeof(token));
+ snprintf(cfgname, sizeof(cfgname), "%s.cfg", basename(cgiScriptName));
+ cfgfile=fopen(cfgname, "r");
+ if(!cfgfile)
+ error("Unable to open configuration file %s.<BR>%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) strncpy(HOMEDIR, cfgline+strlen(c_homedir), sizeof(HOMEDIR));
+ else if(strncmp(cfgline, c_homeurl, strlen(c_homeurl))==0) strncpy(HOMEURL, cfgline+strlen(c_homeurl), sizeof(HOMEURL));
+ else if(strncmp(cfgline, c_tagline, strlen(c_tagline))==0) strncpy(TAGLINE, cfgline+strlen(c_tagline), sizeof(TAGLINE));
+ else if(strncmp(cfgline, c_editdef, strlen(c_editdef))==0) edit_by_default=1;
+ else if(strncmp(cfgline, c_editany, strlen(c_editany))==0) edit_any_file=1;
+ else if(strncmp(cfgline, c_du, strlen(c_du))==0) recursive_du=1;
+ else if(strncmp(cfgline, c_access, strlen(c_access))==0) access_check(cfgline);
+ }
+ fclose(cfgfile);
+
+ // remove newlines
+ if(strlen(HOMEDIR)>2) HOMEDIR[strlen(HOMEDIR)-1]='\0';
+ if(strlen(HOMEURL)>2) HOMEURL[strlen(HOMEURL)-1]='\0';
+ if(strlen(TAGLINE)>2) TAGLINE[strlen(TAGLINE)-1]='\0';
+
+ // do checks
+ if(strlen(HOMEDIR) < 4 || *HOMEDIR!='/')
+ error("Home directory not defined.");
+
+ if(!strlen(TAGLINE))
+ strcpy(TAGLINE, "Web File Manager");
+
+ checkdirectory();
+
+ // main routine - regular actions
+ cgiFormStringNoNewlines("action", action, sizeof(action));
+ if(cgiFormSubmitClicked("multi_delete_prompt")==cgiFormSuccess && access_level >= PERM_RO) multiprompt_ui("delete");
+ else if(cgiFormSubmitClicked("multi_move_prompt")==cgiFormSuccess && access_level >= PERM_RO) multiprompt_ui("move");
+ else if(cgiFormSubmitClicked("upload")==cgiFormSuccess && access_level >= PERM_RW) receivefile();
+ else if(strcmp(action, "sendfile")==0 && access_level >= PERM_RO) sendfile();
+ else if(strcmp(action, "delete")==0 && access_level >= PERM_RW) delete();
+ else if(strcmp(action, "delete_prompt")==0 && access_level >= PERM_RW) multiprompt_ui("delete");
+ else if(strcmp(action, "move_prompt")==0 && access_level >= PERM_RW) multiprompt_ui("move");
+ else if(strcmp(action, "rename_prompt")==0 && access_level >= PERM_RW) singleprompt_ui("move");
+ else if(strcmp(action, "move")==0 && access_level >= PERM_RW) move();
+ else if(strcmp(action, "edit")==0 && access_level >= PERM_RO) edit_ui();
+ else if(strcmp(action, "edit_save")==0 && access_level >= PERM_RW) edit_save();
+ else if(strcmp(action, "mkfile")==0 && access_level >= PERM_RW) mkfile();
+ else if(strcmp(action, "mkfile_prompt")==0 && access_level >= PERM_RW) singleprompt_ui("mkfile");
+ else if(strcmp(action, "mkdir")==0 && access_level >= PERM_RW) newdir();
+ else if(strcmp(action, "mkdir_prompt")==0 && access_level >= PERM_RW) singleprompt_ui("mkdir");
+ else if(strcmp(action, "about")==0 && access_level >= PERM_RO) about();
+ else if(strcmp(action, "login")==0 ) login();
+ else if( access_level >= PERM_RO) dirlist();
+ else
+ if(users_defined) // if users present but supplied credentials didn't match, or credentials not specified
+ redirect("?action=login");
+ else
+ error("Access Denied.");
+
+ return 0;
+}
+
+