// WFM File I/O Routines #include "wfm.h" /* // Debug dump vars //void debugdumpvars(void) { cgiHeaderContentType("text/plain"); printf( "virt_dirname=%s\n" "phys_dirname=%s\n" "virt_filename=%s\n" "phys_filename=%s\n" "virt_destination=%s\n" "phys_destination=%s\n" // "final_destination=%s\n" "virt_parent=%s\n", virt_dirname, phys_dirname, virt_filename, phys_filename, virt_destination, phys_destination, // final_destination, virt_parent ); exit(1); } */ // // Send file to client browser // Called by cgiMain action=sendfile // void sendfile(void) { char buff[1024]={0}; FILE *in; int rd=0, tot=0, size=0, pos=0, blk=0; checkfilename(NULL); // TODO: 2gb file limit? in=fopen(phys_filename, "rb"); if(!in) error("Unable to open file.
%s", strerror(errno)); fseek(in, 0, SEEK_END); size=ftell(in); fseek(in, 0, SEEK_SET); fprintf(cgiOut, "Content-Type: application/octet-stream\r\n" "Content-Disposition: attachment; filename=\"%s\"; size=%d\r\n" "Content-Length: %d\r\n\r\n", virt_filename, size, size ); blk=sizeof(buff); reread: pos=ftell(in); rd=fread(buff, blk, 1, in); if(rd) { tot+=rd*blk; //fprintf(cgiOut, "rw=%u size=%u total=%u remaining=%u\n", rd*blk, size, tot, size-pos-(rd*blk)); fwrite(buff, blk, 1, cgiOut); goto reread; } if(pos%s", virt_filename, strerror(errno)); while(cgiFormFileRead(input, buff, sizeof(buff), &got) == cgiFormSuccess) if(got) if(fwrite(buff, got, 1, output) != 1) error("While writing file.
%s", strerror(errno)); cgiFormFileClose(input); fclose(output); wfm_commit(CHANGE, NULL); redirect("%s?highlight=%s&directory=%s&token=%s", cgiScriptName, virt_filename_urlencoded, virt_dirname_urlencoded, token); } // // Create a new new empty file // Called by cgiMain action=mkfile // void mkfile(void) { FILE *output; checkfilename(NULL); output=fopen(phys_filename, "a"); //TODO: should probably give error if file already exists... if(!output) error("Unable to create file.
%s", strerror(errno)); fclose(output); wfm_commit(CHANGE, NULL); redirect("%s?highlight=%s&directory=%s&token=%s", cgiScriptName, virt_filename_urlencoded, virt_dirname_urlencoded, token); } // // Create a new empty folder // Called by cgiMain action=newdir // void newdir(void) { checkfilename(NULL); if(mkdir(phys_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH )!=0) error("Unable to create directory.
%s", strerror(errno)); redirect("%s?highlight=%s&directory=%s&token=%s", cgiScriptName, virt_filename_urlencoded, virt_dirname_urlencoded, token); } // // Save file from textarea editor // Called by cgiMain action=edit_save // void edit_save(void) { int size=0; int tmpfd; char *buff; char tempname[64]={0}; //FILE *output; FILE *tempf; #ifndef WFMGIT char backup[4]={0}; char backup_filename[PHYS_DESTINATION_SIZE]={0}; regex_t re; regmatch_t pmatch; #endif struct stat tmpstat; checkfilename(NULL); cgiFormStringSpaceNeeded("content", &size); if(size>=5*1024*1024) error("The file is too large for online editing.
"); buff=(char *) malloc(size); if(buff==NULL) error("Unable to allocate memory."); memset(buff, 0, size); cgiFormString("content", buff, size); #ifndef WFMGIT // rename to .bak if requested cgiFormStringNoNewlines("backup", backup, sizeof(backup)); if(strcmp(backup, "yes")==0) { regcomp(&re, "\\.(.+)$", REG_EXTENDED|REG_ICASE); if(regexec(&re, phys_filename, 1, &pmatch, 0)==0) { if(pmatch.rm_so+4 < PHYS_DESTINATION_SIZE) { strcpy(backup_filename, phys_filename); strcpy(backup_filename+pmatch.rm_so+1, "bak\0"); if(rename(phys_filename, backup_filename)!=0) error("Unable to create .bak file.
%s was not modified.
%s", virt_filename, strerror(errno)); } } } #endif // write to temporary file snprintf(tempname, sizeof(tempname), "%s/.wfmXXXXXX", phys_dirname); tmpfd=mkstemp(tempname); if(!tmpfd) error("Unable to create temporary file %s.
%s", basename(tempname), strerror(errno)); if(chmod(tempname, 00644)!=0) error("Unable to set file permissions.
%s", strerror(errno)); tempf=fdopen(tmpfd, "w"); if(!tempf) error("Unable to open temporary file %s.
%s", basename(tempname), strerror(errno)); if(fwrite(buff, strlen(buff), 1, tempf) != 1) error("Unable to write to temporary file %s.
%s", basename(tempname), strerror(errno)); fclose(tempf); if(stat(tempname, &tmpstat)!=0) error("Unable to check temporary file size.
%s
%s", basename(tempname), strerror(errno)); if(tmpstat.st_size != strlen(buff)) error("Temprary file has a wrong length. Giving up.
%s size=%d, buff len=%d", virt_filename, tmpstat.st_size); // finally rename to desination file if(rename(tempname, phys_filename)!=0) error("Unable to rename temp file.
%s - %s
%s
", basename(tempname), virt_filename, strerror(errno)); free(buff); wfm_commit(CHANGE, NULL); redirect("%s?highlight=%s&directory=%s&token=%s", cgiScriptName, virt_filename_urlencoded, virt_dirname_urlencoded, token); } // // Recursively Delete Folders - Internal Routine // Called by fileio_delete when directory is encountered // void fileio_re_rmdir(char *dirname) { DIR *dir; struct dirent *direntry; struct stat fileinfo; char tempfullpath[PHYS_FILENAME_SIZE]={0}; dir=opendir(dirname); if(!dir) error("Unable to remove directory.."); direntry=readdir(dir); while(direntry!=0) { if(strncmp(direntry->d_name, ".", 1) && strncmp(direntry->d_name, "..", 2)) { snprintf(tempfullpath, PHYS_FILENAME_SIZE, "%s/%s", dirname, direntry->d_name); if(lstat(tempfullpath, &fileinfo)!=0) error("Unable to get file status.
%s", strerror(errno)); if(S_ISDIR(fileinfo.st_mode)) { fileio_re_rmdir(tempfullpath); if(rmdir(tempfullpath)!=0) error("Unable to remove directory...
%s", strerror(errno)); } else { if(unlink(tempfullpath)!=0) error("Unable to remove file....
%s", strerror(errno)); wfm_commit(DELETE, tempfullpath); } } direntry=readdir(dir); } closedir(dir); } // // Delete Files Internal Routine // Called by delete() // void fileio_delete(void) { struct stat fileinfo; if(lstat(phys_filename, &fileinfo)==0) { if(S_ISDIR(fileinfo.st_mode)) { fileio_re_rmdir(phys_filename); if(rmdir(phys_filename)!=0) error("Unable to remove directory.
%s", strerror(errno)); } else { if(unlink(phys_filename)!=0) error("Unable to remove file.
%s", strerror(errno)); wfm_commit(DELETE, NULL); } } } // // Delete File or Directory Handler - Allows Multiselect // Called by cgiMain action=delete // void delete(void) { int i; char **responses; // Single if(cgiFormStringMultiple("multiselect_filename", &responses) == cgiFormNotFound) { checkfilename(NULL); fileio_delete(); } // Multi else { for(i=0; responses[i]; i++) { checkfilename(responses[i]); fileio_delete(); } } redirect("%s?directory=%s&token=%s", cgiScriptName, virt_dirname_urlencoded, token); } // // Move File/Directory Internal Routine // Called by move() // void fileio_move(void) { struct stat fileinfo; // If moving file to a different directory we need to append the original file name to destination if( stat(phys_destination, &fileinfo)==0 && S_ISDIR(fileinfo.st_mode) ) snprintf(final_destination, sizeof(final_destination), "%s/%s", phys_destination, virt_filename); else strncpy(final_destination, phys_destination, sizeof(final_destination)); if(rename(phys_filename, final_destination)!=0) error("Unable to move file.
[%d: %s]
[SRC=%s] [DST=%s]", errno, strerror(errno), phys_filename, final_destination); wfm_commit(MOVE, NULL); } // // Move File/Directory - Handler // Called by cgiMain action=move // void move(void) { int i; char **responses; checkdestination(); // Single if(cgiFormStringMultiple("multiselect_filename", &responses) == cgiFormNotFound) { checkfilename(NULL); fileio_move(); } // Multi else { for(i=0; responses[i]; i++) { checkfilename(responses[i]); fileio_move(); } } redirect("%s?highlight=%s&directory=%s&token=%s", cgiScriptName, url_encode(virt_destination), virt_dirname_urlencoded, token); } // // Recursive Dir Size // // NOTE: will not count directories starting with . (dot) // so size of git repo will not be included off_t du(char *pdir) { DIR *dir; struct dirent *direntry; struct stat fileinfo; char child[PHYS_DIRNAME_SIZE]={0}; off_t tot=0; if(lstat(pdir, &fileinfo)==0) if(S_ISLNK(fileinfo.st_mode)) return -1; dir=opendir(pdir); if(dir) { direntry=readdir(dir); while(direntry) { snprintf(child, PHYS_DIRNAME_SIZE, "%s/%s", pdir, direntry->d_name); if(lstat(child, &fileinfo)==0) { if(S_ISDIR(fileinfo.st_mode) && (direntry->d_name[0]!='.')) //TODO: count other ".files" except . & .. tot+=du(child); else tot+=fileinfo.st_size; } direntry=readdir(dir); } closedir(dir); } return tot; } // // Recursive folder list // Called by for move_ui() // void re_dir_ui(char *vdir, int level) { struct dirent **direntry; struct stat fileinfo; char child[VIRT_DIRNAME_SIZE]={0}; char phy_child[PHYS_DIRNAME_SIZE]={0}; char re_phys_dirname[PHYS_DIRNAME_SIZE]={0}; int n; int nentr, e; snprintf(re_phys_dirname, PHYS_DIRNAME_SIZE, "%s/%s", HOMEDIR, vdir); if(strlen(re_phys_dirname)<2 || strlen(re_phys_dirname)>(PHYS_DIRNAME_SIZE-2)) error("Invalid directory name."); if(regexec(&dotdot, re_phys_dirname, 0, 0, 0)==0) error("Invalid directory name."); if(strlen(re_phys_dirname) < strlen(HOMEDIR)) error("Invalid directory name."); nentr=scandir(re_phys_dirname, &direntry, 0, alphasort); for(e=0; ed_name); if((direntry[e]->d_name[0]!='.') && (lstat(phy_child, &fileinfo)==0) && S_ISDIR(fileinfo.st_mode)) { snprintf(child, VIRT_DIRNAME_SIZE, "%s/%s", vdir, direntry[e]->d_name); fprintf(cgiOut, "\n", (js) ? "⌊" : "-", direntry[e]->d_name); // recurse re_dir_ui(child,level+1); } free(direntry[e]); } } // // Scandir replacement function // int namesort(const void *d1, const void *d2) { return(strcasecmp(((ASDIR*)d1)->name, ((ASDIR*)d2)->name)); } int rnamesort(const void *d1, const void *d2) { return(strcasecmp(((ASDIR*)d2)->name, ((ASDIR*)d1)->name)); } int sizesort(const void *d1, const void *d2) { if(((ASDIR*)d1)->size < ((ASDIR*)d2)->size) return -1; else if(((ASDIR*)d1)->size > ((ASDIR*)d2)->size) return 1; else return 0; } int rsizesort(const void *d1, const void *d2) { if(((ASDIR*)d1)->size > ((ASDIR*)d2)->size) return -1; else if(((ASDIR*)d1)->size < ((ASDIR*)d2)->size) return 1; else return 0; } int timesort(const void *d1, const void *d2) { if(((ASDIR*)d1)->mtime < ((ASDIR*)d2)->mtime) return -1; else if(((ASDIR*)d1)->mtime > ((ASDIR*)d2)->mtime) return 1; else return 0; } int rtimesort(const void *d1, const void *d2) { if(((ASDIR*)d1)->mtime > ((ASDIR*)d2)->mtime) return -1; else if(((ASDIR*)d1)->mtime < ((ASDIR*)d2)->mtime) return 1; else return 0; } int asscandir(const char *dir, ASDIR **namelist, int (*compar)(const void *, const void *)) { DIR *dirh; ASDIR *names; struct dirent *entry; struct stat fileinfo; char filename[PATH_MAX]={0}; int entries=0; dirh=opendir(dir); if(dirh==NULL) return -1; names=(ASDIR*)malloc(sizeof(ASDIR)); if(names==NULL) return -1; entry=readdir(dirh); while(entry!=NULL) { snprintf(filename, sizeof(filename), "%s/%s", dir, entry->d_name); if(stat(filename, &fileinfo)!=0) return -1; memset(&names[entries], 0, sizeof(ASDIR)); strcpy(names[entries].name, entry->d_name); names[entries].type=fileinfo.st_mode; if(S_ISDIR(fileinfo.st_mode) && recursive_du) names[entries].size=du(filename); else names[entries].size=fileinfo.st_size; names[entries].atime=fileinfo.st_atime; names[entries].mtime=fileinfo.st_mtime; names[entries].rtime=fileinfo.st_ctime; names=(ASDIR*)realloc((ASDIR*)names, sizeof(ASDIR)*(entries+2)); if(names==NULL) return -1; entries++; entry=readdir(dirh); } closedir(dirh); if(entries) qsort(&names[0], entries, sizeof(ASDIR), compar); *namelist=names; return entries; }