diff --git a/filemin/CHANGELOG b/filemin/CHANGELOG new file mode 100644 index 000000000..6c04ca2af --- /dev/null +++ b/filemin/CHANGELOG @@ -0,0 +1,194 @@ +-- Version 0.9.6 -- + - Fixed `Undefined subroutine &filemin::ceil + Caused on some systems by not including POSIX package in filemin-lib.pl. + - Major Authentic interface improvement by Ilia Rostovtsev + 1. Working sorting files by size (with next Authentic Theme - perfectly, now just alright). + This fix will also prevent fatal code breaks despite of user settings + 2. Store user chose on columns sorting upon page refresh + 3. Hide paginations when there is nothing to paginate. + 4. Let user navigate with arrows (left/right) when trying to do pagination + - Code cleanup and security tightening by Jamie Cameron + - Add ACL options to allowing running as a specific user, thanks Jamie :) + - WARNING: work as root is now DEFAULT behavior. + IF YOU GRANTED FILEMIN ACCESS TO ANY USERS NOT SUPPOSED TO WORK AS ROOT - GO AND CHECK ACL!!! + + #42 fixed: + - Fixing conflict with Authentic Theme codeMirror + By Ilia. + + #46 fixed: + - Module is now installable from Usermin. + When running in Usermin, access is always as the connected user. + + #52 fixed: + - Working as non UNIX user is now possible. By Jamie Cameron. + + #54 fixed: + - Pasting a directory either by copy or cut pasted not the directory, but it's content. + +-- Version 0.9.5 -- + - Made top level menu items translatable. + - Added https://github.com/Real-Gecko/filemin/pull/29 + Selected rows also highlight on hover + color tweaks, by https://github.com/qooob + - Some code cleanup, got rid of Regexp::Common and URI dependencies. + Thanks to Jamie Cameron. + - Added error message if file saving fails. + - Removed not working "Help" link for now. + + #35 fixed: + - Permissions column configurable and displayable. + + #36 fixed: + - README instructions for Debian/Ubuntu fixed. + +-- Version 0.9.4 -- + - Implemented "Search". + Users may search files and folders by name, wildcards supported. + - Symlinked inode-mount-point.png to inode-directory.png. + Mount points displayed with 'directory' icon. + - Made toolbar more compact under modern themes. + - Added per user configuration. + Users now can individually configure some display options. + * Items per page - how many entries to display in one page, OLD THEMES ONLY!. + * Columns - users can check which columns he/she wants to see. + * Authentic theme users may disable pagination completely. + * Choose toolbar style between menu with dropdowns or good old "all in one". + * Manage personal bookmarks + - Implemented "Bookmarks" functionality. + Users can now bookmark current directory. + Bookmarks are managed in per user module settings. + - Added "Save and close" button on file edit page. + Save button now saves and reopens file for editing again, + while save and close saves and redirects back to originated folder. + - Update Russian translation. + + #21 fixed: + - Added Codemirror to module for syntax highlighting, thanks to https://github.com/pabloko for solution. + - Under Authentic 14.01 now works too, thanks to https://github.com/qooob for fix. + + #24 fixed: + - Added zip compression method, user now can select between 'zip' and 'tar' compression methods. + + #27 fixed: + - Chmodding now have new option to apply new permissions to: + * Selected directories and files only + * Selected files and directorires and files in selected directories + * All (recursive) + * Selected files and files under selected directories and subdirectories + * Selected directories and subdirectories + + #28 fixed: + - Added new toolbar style - menu-like with dropdowns (Authentic only). + + #31 fixed: + - Removed Data::Dumper declaration. + +-- Version 0.9.3 -- + - Removed unused CGI dependency. + - "Increased" download speed by increasing buffer length in download.cgi. + - Some syntax errors fixed in english translation, thanks to https://github.com/Zen4All + - Specially for Fireserver developers http://www.fireserver.com.br/index_en.html + Added "Work as root" option to ACL, if enabled for any particular user he/she will work as 'root', + but directory access will still be limited to those listed in ACL. + BEWARE!!! DO NOT ENABLE THIS OPTION FOR ANYONE UNLESS YOU'RE REALLY SURE WHAT ARE YOU DOING!!! + + #17 fixed: + - YAML files are now editable. + + #20 fixed: + - Using Perl's `-d` to determine if entry is file or folder. + +-- Version 0.9.2 -- + - Switched to Webmin functions for HTTP/FTP Download, so download progress is shown now. Got rid of File::Fetch dependecy in module code. + - HTTP/FTP Download now prompts for username and password on remote server, if any required user can provide it. + - Removed "openlayers" folder. How did it get in here???? :D + - Completely rewrote upload.cgi. Now upload progress is shown with Webmin`s upload tracker. + Memory usage is low due to direct HD write, even with REALLY big uploads(tested with 3 files totalling 1.61 GB). + BEWARE: if user reaches his/her disk quota then Webmin simply resets connection with no errors, can do nothing with it right now. + - Fixed "select-unselect" checkbox behavior for old themes. + - Updated russian translation. + - THE LONG WAITED! THE MOST WANTED! THE ONE AND ONLY - ACL!!! + Default ACL behavior sets only one ACL entry '$HOME' for each user. This locks user in $HOME directory. + To grant full FS access to user replace '$HOME' with '$ROOT' in user`s ACL. + If there`s only one entry in ACL then it counts as "home" or "chroot", otherwise user will see accessible dirs relative to "/", just like it is in old FM. + Avoid trailing slash in ACL entries: "/usr/share/webmin" - gooooood, "/home/test/" - baaaaad. + Root bypasses ACL completely. + Note that even though user can be granted full FS access, he still works with his own privileges. + So anything that requires "root" permissions or "sudo" won't work. + - Folders go first. + Rewrote directory listing procedure, now folders appear before files in list as in any other adequate file manager. + As a result first and second entry in '/' are now visible :) + Note that sorting table with javascript under Authentic or Framed themes will mix everything again until page reload ;-) + - Major code cleanup of rendering procedure. + - Fixed copy/cut/paste functionality. + - FreeBSD support added. + Module uses Regexp::Common and URI packages to validate URIs in HTTP/FTP download functionality. + Be sure to install theese to make functionality work. + Admin user created during Webmin installation is not UNIX user, so module won't work for him. + If you want to provide some "system" file operations then convert "root" to Webmin user. + + #11 fixed: + - CRLF is replaced with UNIX style LF while saving a file. + +-- Version 0.9.1 -- +#8 fixed: + - Not sure if it was an issue, cause I was unable to reproduce "User without $HOME" scenario. However added some code to handle this, just in case. + +#7 fixed: + - Removed Archive::* dependencies, all archive extraction procedures now go through system calls. *.tar.gz, *.tar.xz, *.tar.bz2, *.zip archive types tested. + +#6 fixed: + - Switched to Regexp::Common for URI validation in "HTTP Download" operation to avoid additional dependencies. + +#5 fixed: + - Added HDD icon for "/" while under root for legacy themes. + + - Added BWTheme support. + - Changed license to BSD style. + - Chown now asks for group too. + - Slight interface improvements for modern dialogs. + - Major interface imprevements for legacy dialogs, jQuery and jQueryUI shipped with the module for this purpose. + - Added $in{'...'} parameters check in some operations to prevent errors if user submits emtpy form by hitting "Enter". + - Changed textarea font to monospace. + - Added sticky bit and setgid to chmod. + - Chmod and chown can be done recursively. + - Made list table more compact for Bootstrap enabled themes. + - Removed group column, group is displayed now within "Owner User" column: "root:root", "realgecko:users" etc. + - Modified date/time display format for better sorting under Authentic theme. + - Added "Refresh" quick button for modern themes, just for fun :D + - Added a work around to fix JS hell provided by Framed Theme family with "onmouseover" and "onmouseout" events, that led to unselectable rows by "Select All" and "Inverse Selection" operations. + - Using the same class to highlight rows as Authentic. + - Moved icons to images folder, as Authentic Theme author improved third party modules' files handling. + - Added .deb package for distribution flexibility. + +-- Version 0.9 -- +Mielstone reached: + - Checks for overwrites while using paste, new folder, new file, upload, http download ... at last :D + - If file exists it will not be overwritten. + - BEWARE: uncompressing archive still overwrites existing files! + +#1 fixed: + - After long discussion upper-left toolbar finally looks good in modern interface + - Thanks to https://github.com/Goeny for his brilliant solution and https://github.com/qooob for debugging + +#3 fixed: + - Some misspellings fixed + +#5 fix suggestion: + - Replaced '~' for '/' directory with FontAwesome's HDD icon, still open for discussion... + +Minor tweaks for modern interface +More verbose output on errors + +-- Version 0.8.3 -- +Added tooltips for icons on toolbar +Message appears if nothing selected and user tries copy/cut/compress/chmod/chown/delete + +-- Version 0.8.2 -- +Various bug fixes +Added Authentic Theme support +"root" is not locked in his "~" dir anymore +Check for overwrites during http download, and only there for now... + +-- Version 0.8.1 -- +Initial release diff --git a/filemin/acl_security.pl b/filemin/acl_security.pl new file mode 100644 index 000000000..751e0c73e --- /dev/null +++ b/filemin/acl_security.pl @@ -0,0 +1,61 @@ +require 'filemin-lib.pl'; + +sub acl_security_form { + my ($access) = @_; + + # Directories the user can access + print &ui_table_row($text{'acl_allowed_paths'}, + ui_textarea("allowed_paths", + join("\n", split(/\s+/, $access->{'allowed_paths'})), + 10, 80, undef, undef, "style='width: 100%'"), 2); + + # Mimetypes allowed to be edited + print &ui_table_row($text{'acl_allowed_for_edit'}, + ui_textarea("allowed_for_edit", + join("\n", split(/\s+/, $access->{'allowed_for_edit'})), + 10, 80, undef, undef, "style='width: 100%'"), 2); + + # Run as Unix user + print &ui_table_row($text{'acl_work_as'}, + ui_radio_table("user_mode", $access->{'work_as_root'} ? 0 : + $access->{'work_as_user'} ? 2 : 1, + [ [ 0, $text{'acl_root'} ], + [ 1, $text{'acl_same'} ], + [ 2, $text{'acl_user'}, + ui_user_textbox("acl_user", $access->{'work_as_user'}) ] ]), + 3); + + # Upload max + print &ui_table_row($text{'acl_max'}, + &ui_opt_textbox("max", $access->{'max'}, 10, $text{'acl_unlimited'}). + " ".$text{'acl_bytes'}, 3); +} + +sub acl_security_save { + my ($access, $in) = @_; + local @allowed_paths = split(/\s+/, $in->{'allowed_paths'}); + if (scalar(@allowed_paths) == 0) { &error("No allowed paths defined"); } + for $path(@allowed_paths) { + if (!-e $path && $path ne '$HOME' && $path ne '$ROOT') { + &error(&text('acl_epath', &html_escape($path))); + } + } + $access->{'allowed_paths'} = join(" ", @allowed_paths); + + local @allowed_for_edit = split(/\s+/, $in->{'allowed_for_edit'}); + if (scalar(@allowed_for_edit) == 0) { &error("No mimetypes allowed for edit defined"); } + $access->{'allowed_for_edit'} = join(" ", @allowed_for_edit); + + if ($in->{'user_mode'} == 0) { + $access->{'work_as_root'} = 1; + $access->{'work_as_user'} = undef; + } elsif ($in->{'user_mode'} == 1) { + $access->{'work_as_root'} = 0; + $access->{'work_as_user'} = undef; + } else { + defined(getpwnam($in->{'acl_user'})) || &error($text{'acl_euser'}); + $access->{'work_as_root'} = 0; + $access->{'work_as_user'} = $in->{'acl_user'}; + } + $access->{'max'} = $in->{'max_def'} ? undef : $in{'max'}; +} diff --git a/filemin/bookmark.cgi b/filemin/bookmark.cgi new file mode 100644 index 000000000..5384a5c99 --- /dev/null +++ b/filemin/bookmark.cgi @@ -0,0 +1,23 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +use lib './lib'; + +&ReadParse(); + +get_paths(); + +$confdir = "$remote_user_info[7]/.filemin"; +if(!-e $confdir) { + mkdir $confdir or &error("$text{'error_creating_conf'}: $!"); +} + +if(!-e "$confdir/.bookmarks") { + utime time, time, "$configdir/.bookmarks"; +} + +$bookmarks = &read_file_lines($confdir.'/.bookmarks'); +push @$bookmarks, $path; +&flush_file_lines("$confdir/.bookmarks"); + +&redirect("index.cgi?path=$path"); diff --git a/filemin/chmod.cgi b/filemin/chmod.cgi new file mode 100644 index 000000000..dc1588728 --- /dev/null +++ b/filemin/chmod.cgi @@ -0,0 +1,78 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; + +&ReadParse(); + +get_paths(); + +my @errors; + +my $perms = $in{'perms'}; + +# Selected directories and files only +if($in{'applyto'} eq '1') { + foreach $name (split(/\0/, $in{'name'})) { + if (system_logged("chmod ".quotemeta($perms)." ".quotemeta("$cwd/$name")) != 0) { + push @errors, "$name - $text{'error_chmod'}: $?"; + } + } +} + +# Selected files and directories and files in selected directories +if($in{'applyto'} eq '2') { + foreach $name (split(/\0/, $in{'name'})) { + if(system_logged("chmod ".quotemeta($perms)." ".quotemeta("$cwd/$name")) != 0) { + push @errors, "$name - $text{'error_chmod'}: $?"; + } + if(-d "$cwd/$name") { + if(system_logged("find ".quotemeta("$cwd/$name")." -maxdepth 1 -type f -exec chmod ".quotemeta($perms)." {} \\;") != 0) { + push @errors, "$name - $text{'error_chmod'}: $?"; + } + } + } +} + +# All (recursive) +if($in{'applyto'} eq '3') { + foreach $name (split(/\0/, $in{'name'})) { + if(system_logged("chmod -R ".quotemeta($perms)." ".quotemeta("$cwd/$name")) != 0) { + push @errors, "$name - $text{'error_chmod'}: $?"; + } + } +} + +# Selected files and files under selected directories and subdirectories +if($in{'applyto'} eq '4') { + foreach $name (split(/\0/, $in{'name'})) { + if(-f "$cwd/$name") { + if(system_logged("chmod ".quotemeta($perms)." ".quotemeta("$cwd/$name")) != 0) { + push @errors, "$name - $text{'error_chmod'}: $?"; + } + } else { + if(system_logged("find ".quotemeta("$cwd/$name")." -type f -exec chmod ".quotemeta($perms)." {} \\;") != 0) { + push @errors, "$name - $text{'error_chmod'}: $?"; + } + } + } +} + +# Selected directories and subdirectories +if($in{'applyto'} eq '5') { + foreach $name (split(/\0/, $in{'name'})) { + if(-d "$cwd/$name") { + if(system_logged("chmod ".quotemeta($perms)." ".quotemeta("$cwd/$name")) != 0) { + push @errors, "$name - $text{'error_chmod'}: $?"; + } + if(system_logged("find ".quotemeta("$cwd/$name")." -type d -exec chmod ".quotemeta($perms)." {} \\;") != 0) { + push @errors, "$name - $text{'error_chmod'}: $?"; + } + } + } +} + +if (scalar(@errors) > 0) { + print_errors(@errors); +} else { + &redirect("index.cgi?path=$path"); +} diff --git a/filemin/chown.cgi b/filemin/chown.cgi new file mode 100644 index 000000000..31d65e4d4 --- /dev/null +++ b/filemin/chown.cgi @@ -0,0 +1,42 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; + +&ReadParse(); + +get_paths(); + +if(!$in{'owner'} or !$in{'group'}) { + &redirect("index.cgi?path=$path"); +} + +(my $login, my $pass, my $uid, my $gid) = getpwnam($in{'owner'}); +my $grid = getgrnam($in{'group'}); +my $recursive; +if($in{'recursive'} eq 'true') { $recursive = '-R'; } else { $recursive = ''; } + +my @errors; + +if(! defined $login) { + push @errors, "$in{'owner'} $text{'error_user_not_found'}"; +} + +if(! defined $grid) { + push @errors, "$in{'group'} $text{'error_group_not_found'}"; +} + +if (scalar(@errors) > 0) { + print_errors(@errors); +} else { + foreach $name (split(/\0/, $in{'name'})) { +# if(!chown $uid, $grid, $cwd.'/'.$name) { + if(system_logged("chown $recursive $uid:$grid ".quotemeta("$cwd/$name")) != 0) { + push @errors, "$name - $text{'error_chown'}: $?"; + } + } + if (scalar(@errors) > 0) { + print_errors(@errors); + } else { + &redirect("index.cgi?path=$path"); + } +} diff --git a/filemin/compress.cgi b/filemin/compress.cgi new file mode 100644 index 000000000..e68ebead8 --- /dev/null +++ b/filemin/compress.cgi @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +&ReadParse(); +get_paths(); + +if(!$in{'arch'}) { + &redirect("index.cgi?path=$path"); +} + +my $command; + +if($in{'method'} eq 'tar') { + $command = "tar czf ".quotemeta("$cwd/$in{'arch'}.tar.gz"). + " -C ".quotemeta($cwd); +} elsif($in{'method'} eq 'zip') { + $command = "cd ".quotemeta($cwd)." && zip -r ". + quotemeta("$cwd/$in{'arch'}.zip"); +} + +foreach my $name(split(/\0/, $in{'name'})) +{ + $name =~ s/$in{'cwd'}\///ig; + $command .= " ".quotemeta($name); +} + +system_logged($command); + +&redirect("index.cgi?path=$path"); diff --git a/filemin/config b/filemin/config new file mode 100644 index 000000000..add87f6e4 --- /dev/null +++ b/filemin/config @@ -0,0 +1 @@ +allowed_paths=$HOME diff --git a/filemin/config.cgi b/filemin/config.cgi new file mode 100644 index 000000000..531198cd6 --- /dev/null +++ b/filemin/config.cgi @@ -0,0 +1,53 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +use lib './lib'; +use File::MimeInfo; + +&ReadParse(); + +get_paths(); + +&ui_print_header(undef, $text{'module_config'}, ""); +$head = ""; +print $head; + +$confdir = "$remote_user_info[7]/.filemin"; +if(!-e $confdir) { + mkdir $confdir or &error("$text{'error_creating_conf'}: $!"); +} + +if(!-e "$confdir/.config") { + &read_file("$module_root_directory/defaultuconf", \%config); +} else { + &read_file("$confdir/.config", \%config); +} + +if(!-e "$confdir/.bookmarks") { + $bookmarks = ''; +} else { + $bookmarks = &read_file_contents($confdir.'/.bookmarks', 1); +} + +print &ui_form_start("save_config.cgi", "post"); + +print &ui_table_start($text{'module_config'}, undef, 2); +print &ui_table_row($text{'config_columns_to_display'}, + &ui_checkbox('columns', 'type', $text{'type'}, $config{'columns'} =~ /type/). + &ui_checkbox('columns', 'size', $text{'size'}, $config{'columns'} =~ /size/). + &ui_checkbox('columns', 'owner_user', $text{'owner_user'}, $config{'columns'} =~ /owner_user/). + &ui_checkbox('columns', 'permissions', $text{'permissions'}, $config{'columns'} =~ /permissions/). + &ui_checkbox('columns', 'last_mod_time', $text{'last_mod_time'}, $config{'columns'} =~ /last_mod_time/) +); +print &ui_table_row($text{'config_per_page'}, ui_textbox("per_page", $config{'per_page'}, 80)); +print &ui_table_row($text{'config_disable_pagination'}, &ui_checkbox('disable_pagination', 1, '', $config{'disable_pagination'})); +print &ui_table_row($text{'config_toolbar_style'}, &ui_yesno_radio('menu_style', $config{'menu_style'}, 1, 0)); +print &ui_table_row($text{'config_bookmarks'}, &ui_textarea("bookmarks", $bookmarks, 5, 40)); + +print &ui_table_end(); + +print &ui_hidden('path', $path); + +print &ui_form_end([ [ save, $text{'save'} ] ]); + +&ui_print_footer("index.cgi?path=$path", $text{'previous_page'}); diff --git a/filemin/config.info b/filemin/config.info new file mode 100644 index 000000000..a8f52bf31 --- /dev/null +++ b/filemin/config.info @@ -0,0 +1,2 @@ +allowed_paths=Directories accessible to Usermin users,9,60,5,\t +max=Maximum size for uploaded files,3,Unlimited diff --git a/filemin/copy.cgi b/filemin/copy.cgi new file mode 100644 index 000000000..100d652d4 --- /dev/null +++ b/filemin/copy.cgi @@ -0,0 +1,19 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +&ReadParse(); + +get_paths(); + +open(my $fh, ">", &get_paste_buffer_file()) or die "Error: $!"; +print $fh "copy\n"; +print $fh "$path\n"; +#$info = "Copied ".scalar(@list)." files to buffer"; + +foreach $name (split(/\0/, $in{'name'})) { + print $fh "$name\n"; +} + +close($fh); + +&redirect("index.cgi?path=$path"); diff --git a/filemin/create_file.cgi b/filemin/create_file.cgi new file mode 100644 index 000000000..a4a4d84e0 --- /dev/null +++ b/filemin/create_file.cgi @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +&ReadParse(); + +get_paths(); + +if(!$in{'name'}) { + &redirect("index.cgi?path=$path"); +} + +if (-e "$cwd/$in{'name'}") { + print_errors("$in{'name'} $text{'error_exists'}"); +} else { + if (open my $fh, "> $cwd/$in{'name'}") { + close($fh); + &redirect("index.cgi?path=$path"); + } else { + print_errors("$in{'name'} - $text{'error_create'} $!"); + } +} diff --git a/filemin/create_folder.cgi b/filemin/create_folder.cgi new file mode 100644 index 000000000..fbbf9d476 --- /dev/null +++ b/filemin/create_folder.cgi @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +&ReadParse(); + +get_paths(); + +if(!$in{'name'}) { + &redirect("index.cgi?path=$path"); +} + +if (-e "$cwd/$in{'name'}") { + print_errors("$in{'name'} $text{'error_exists'}"); +} else { + if( mkdir ("$cwd/$in{'name'}", oct(755)) ) { + &redirect("index.cgi?path=$path"); + } else { + print_errors("$text{'error_create'} $in{'name'}: $!"); + } +} diff --git a/filemin/cut.cgi b/filemin/cut.cgi new file mode 100644 index 000000000..e00318367 --- /dev/null +++ b/filemin/cut.cgi @@ -0,0 +1,19 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +&ReadParse(); + +get_paths(); + +open(my $fh, ">", &get_paste_buffer_file()) or die "Error: $!"; +print $fh "cut\n"; +print $fh "$path\n"; +#$info = "Copied ".scalar(@list)." files to buffer"; + +foreach $name (split(/\0/, $in{'name'})) { + print $fh "$name\n"; +} + +close($fh); + +&redirect("index.cgi?path=$path"); diff --git a/filemin/defaultacl b/filemin/defaultacl new file mode 100644 index 000000000..50f91dd82 --- /dev/null +++ b/filemin/defaultacl @@ -0,0 +1,3 @@ +allowed_paths=$HOME +work_as_root=1 +allowed_for_edit=application-x-php application-x-ruby application-xml application-javascript application-x-shellscript application-x-perl application-x-yaml diff --git a/filemin/defaultuconf b/filemin/defaultuconf new file mode 100644 index 000000000..af6c83736 --- /dev/null +++ b/filemin/defaultuconf @@ -0,0 +1,4 @@ +columns=size,owner_user,permissions,last_mod_time +per_page=50 +disable_pagination=0 +menu_style=1 \ No newline at end of file diff --git a/filemin/delete.cgi b/filemin/delete.cgi new file mode 100644 index 000000000..639864908 --- /dev/null +++ b/filemin/delete.cgi @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +&ReadParse(); + +get_paths(); + +my @errors; + +foreach $name (split(/\0/, $in{'name'})) { + if(!&unlink_logged($cwd.'/'.$name)) { + push @errors, "$name - $text{'error_delete'}: $!"; + } +} + +if (scalar(@errors) > 0) { + print_errors(@errors); +} else { + &redirect("index.cgi?path=$path"); +} diff --git a/filemin/download.cgi b/filemin/download.cgi new file mode 100644 index 000000000..bcd368384 --- /dev/null +++ b/filemin/download.cgi @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +use lib './lib'; + +use File::Basename; +use Cwd 'abs_path'; + +&ReadParse(); + +get_paths(); + +my $file = $cwd.'/'.$in{'file'}; +my $size = -s "$file"; +(my $name, my $dir, my $ext) = fileparse($file, qr/\.[^.]*/); +print "Content-Type: application/x-download\n"; +print "Content-Disposition: attachment; filename=\"$name$ext\"\n"; +print "Content-Length: $size\n\n"; +open (FILE, "< $file") or die "can't open $file: $!"; +binmode FILE; +local $/ = \102400; +while () { + print $_; +} +close FILE; diff --git a/filemin/edit_file.cgi b/filemin/edit_file.cgi new file mode 100644 index 000000000..55f30ab07 --- /dev/null +++ b/filemin/edit_file.cgi @@ -0,0 +1,43 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +&ReadParse(); + +get_paths(); + +$data = &read_file_contents($cwd.'/'.$in{file}); + +&ui_print_header(undef, $text{'edit_file'}, ""); +$head = ""; + +if ($current_theme ne 'authentic-theme') { + $head.= ""; + $head.= ""; + $head.= ""; + + # Include Codemirror specific files + $head.= ""; + $head.= ""; + $head.= ""; + $head.= ""; + $head.= ""; + $head.= ""; + $head.= ""; +} + +print $head; + +print ui_table_start("$path/$in{'file'}", undef, 1); + +print &ui_form_start("save_file.cgi", "post"); +print &ui_hidden("file", $in{'file'}),"\n"; +print &ui_textarea("data", $data, 20, 80, undef, undef, "style='width: 100%' id='data'"); +print &ui_hidden("path", $path); +print &ui_form_end([ [ save, $text{'save'} ], [ save_close, $text{'save_close'} ] ]); + +print ui_table_end(); + +print ""; +print ""; + +&ui_print_footer("index.cgi?path=$path", $text{'previous_page'}); diff --git a/filemin/extract.cgi b/filemin/extract.cgi new file mode 100644 index 000000000..12f230794 --- /dev/null +++ b/filemin/extract.cgi @@ -0,0 +1,25 @@ +#!/usr/bin/perl + +require './filemin-lib.pl'; +use lib './lib'; +use File::MimeInfo; + +&ReadParse(); + +get_paths(); + +$archive_type = mimetype($cwd.'/'.$in{'file'}); + +if ($archive_type eq 'application/zip') { + &backquote_logged("unzip ".quotemeta("$cwd/$in{'file'}"). + " -d ".quotemeta($cwd)); + &redirect("index.cgi?path=$path"); +} elsif (index($archive_type, "tar") != -1) { + &backquote_logged("tar xf ".quotemeta("$cwd/$in{'file'}"). + " -C ".quotemeta($cwd)); + &redirect("index.cgi?path=$path"); +} else { + &ui_print_header(undef, "Filemin", ""); + print "$archive_type $text{'error_archive_type_not_supported'}"; + &ui_print_footer("index.cgi?path=$path", $text{'previous_page'}); +} diff --git a/filemin/filemin-lib.pl b/filemin/filemin-lib.pl new file mode 100644 index 000000000..529abf6f8 --- /dev/null +++ b/filemin/filemin-lib.pl @@ -0,0 +1,404 @@ +# filemin-lib.pl + +BEGIN { push(@INC, ".."); }; +use WebminCore; +&init_config(); +use Encode qw(decode encode); +use File::Basename; +use POSIX; + +sub get_paths { + %access = &get_module_acl(); + + # Switch to the correct user + if (&get_product_name() eq 'usermin') { + # In Usermin, the module only ever runs as the connected user + &switch_to_remote_user(); + &create_user_config_dirs(); + } + elsif ($access{'work_as_root'}) { + # Root user, so no switching + @remote_user_info = getpwnam('root'); + } + elsif ($access{'work_as_user'}) { + # A specific user + @remote_user_info = getpwnam($access{'work_as_user'}); + @remote_user_info || + &error("Unix user $access{'work_as_user'} does not exist!"); + &switch_to_unix_user(\@remote_user_info); + } + else { + # The Webmin user we are connected as + &switch_to_remote_user(); + } + + # Get and check allowed paths + @allowed_paths = split(/\s+/, $access{'allowed_paths'}); + if (&get_product_name() eq 'usermin') { + # Add paths from Usermin config + push(@allowed_paths, split(/\t+/, $config{'allowed_paths'})); + } + if($remote_user_info[0] eq 'root' || $allowed_paths[0] eq '$ROOT') { + # Assume any directory can be accessed + $base = "/"; + @allowed_paths = ( $base ); + } else { + @allowed_paths = map { $_ eq '$HOME' ? @remote_user_info[7] : $_ } + @allowed_paths; + @allowed_paths = map { s/\$USER/$remote_user/g; $_ } @allowed_paths; + if (scalar(@allowed_paths == 1)) { + $base = $allowed_paths[0]; + } else { + $base = '/'; + } + } + $path = $in{'path'} ? $in{'path'} : ''; + $cwd = &simplify_path($base.$path); + + # Work out max upload size + if (&get_product_name() eq 'usermin') { + $upload_max = $config{'max'}; + } else { + $upload_max = $access{'max'}; + } + + # Check that current directory is one of those that is allowed + my $error = 1; + for $allowed_path (@allowed_paths) { + if (&is_under_directory($allowed_path, $cwd) || + $allowed_path =~ /^$cwd/) { + $error = 0; + } + } + if ($error) { + &error(&text('notallowed', &html_escape($cwd), + &html_escape(join(" , ", @allowed_paths)))); + } + + if (index($cwd, $base) == -1) + { + $cwd = $base; + } + + # Initiate per user config + $confdir = "$remote_user_info[7]/.filemin"; + if(!-e "$confdir/.config") { + &read_file_cached("$module_root_directory/defaultuconf", \%userconfig); + } else { + &read_file_cached("$confdir/.config", \%userconfig); + } +} + +sub print_template { + $template_name = @_[0]; + if (open(my $fh, '<:encoding(UTF-8)', $template_name)) { + while (my $row = <$fh>) { + print (eval "qq($row)"); + } + } else { + print "$text{'error_load_template'} '$template_name' $!"; + } +} + +sub print_errors { + my @errors = @_; + &ui_print_header(undef, "Filemin", ""); + print $text{'errors_occured'}; + print "