Commit b43140e6 by Bagus Pambudi

tambah captcha dan menghapus file di public

parent e7912e43
...@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Auth; ...@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Validator;
class LoginController extends Controller class LoginController extends Controller
{ {
...@@ -36,4 +37,17 @@ class LoginController extends Controller ...@@ -36,4 +37,17 @@ class LoginController extends Controller
{ {
$this->middleware('guest')->except('logout'); $this->middleware('guest')->except('logout');
} }
protected function validator(array $data)
{
return Validator::make($data, [
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
'captcha' => ['required','captcha'],
]);
}
public function reloadCaptcha()
{
return response()->json('captcha', captcha_img());
}
} }
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
"laravel/tinker": "^1.0", "laravel/tinker": "^1.0",
"laravelcollective/html": "^5.8.0", "laravelcollective/html": "^5.8.0",
"maatwebsite/excel": "^3.1", "maatwebsite/excel": "^3.1",
"mews/captcha": "^3.2",
"milon/barcode": "^6.0", "milon/barcode": "^6.0",
"statickidz/php-google-translate-free": "^1.1", "statickidz/php-google-translate-free": "^1.1",
"toolkito/larasap": "master", "toolkito/larasap": "master",
......
...@@ -175,6 +175,7 @@ return [ ...@@ -175,6 +175,7 @@ return [
// App\Providers\BroadcastServiceProvider::class, // App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class, App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class, App\Providers\RouteServiceProvider::class,
Mews\Captcha\CaptchaServiceProvider::class,
], ],
...@@ -228,6 +229,7 @@ return [ ...@@ -228,6 +229,7 @@ return [
'View' => Illuminate\Support\Facades\View::class, 'View' => Illuminate\Support\Facades\View::class,
'Image' => Intervention\Image\Facades\Image::class, 'Image' => Intervention\Image\Facades\Image::class,
'InseoHelper' => App\Helpers\InseoHelper::class, 'InseoHelper' => App\Helpers\InseoHelper::class,
'Captcha' => Mews\Captcha\Facades\Captcha::class,
], ],
......
<?php
return [
'disable' => env('CAPTCHA_DISABLE', false),
'characters' => ['2', '3', '4', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'm', 'n', 'p', 'q', 'r', 't', 'u', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'X', 'Y', 'Z'],
'default' => [
'length' => 9,
'width' => 120,
'height' => 36,
'quality' => 90,
'math' => true,
'expire' => 60,
'encrypt' => false,
],
'math' => [
'length' => 9,
'width' => 120,
'height' => 36,
'quality' => 90,
'math' => true,
],
'flat' => [
'length' => 6,
'width' => 160,
'height' => 46,
'quality' => 90,
'lines' => 6,
'bgImage' => false,
'bgColor' => '#ecf2f4',
'fontColors' => ['#2c3e50', '#c0392b', '#16a085', '#c0392b', '#8e44ad', '#303f9f', '#f57c00', '#795548'],
'contrast' => -5,
],
'mini' => [
'length' => 3,
'width' => 60,
'height' => 32,
],
'inverse' => [
'length' => 5,
'width' => 120,
'height' => 36,
'quality' => 90,
'sensitive' => true,
'angle' => 12,
'sharpen' => 10,
'blur' => 2,
'invert' => true,
'contrast' => -5,
]
];
<%
'
' jQuery File Tree ASP (VBS) Connector
' Copyright 2008 Chazzuka
' programmer@chazzuka.com
' http://www.chazzuka.com/
'
' retrive base directory
dim BaseFileDir:BaseFileDir=Request.Form("dir")
' if blank give default value
if len(BaseFileDir)=0 then BaseFileDir="/userfiles/"
dim ObjFSO,BaseFile,Html
' resolve the absolute path
BaseFile = Server.MapPath(BaseFileDir)&"\"
' create FSO
Set ObjFSO = Server.CreateObject("Scripting.FileSystemObject")
' if given folder is exists
if ObjFSO.FolderExists(BaseFile) then
dim ObjFolder,ObjSubFolder,ObjFile,i__Name,i__Ext
Html = Html + "<ul class=""jqueryFileTree"" style=""display: none;"">"&VBCRLF
Set ObjFolder = ObjFSO.GetFolder(BaseFile)
' LOOP THROUGH SUBFOLDER
For Each ObjSubFolder In ObjFolder.SubFolders
i__Name=ObjSubFolder.name
Html = Html + "<li class=""directory collapsed"">"&_
"<a href=""#"" rel="""+(BaseFileDir+i__Name+"/")+""">"&_
(i__Name)+"</a></li>"&VBCRLF
Next
'LOOP THROUGH FILES
For Each ObjFile In ObjFolder.Files
' name
i__Name=ObjFile.name
' extension
i__Ext = LCase(Mid(i__Name, InStrRev(i__Name, ".", -1, 1) + 1))
Html = Html + "<li class=""file ext_"&i__Ext&""">"&_
"<a href=""#"" rel="""+(BaseFileDir+i__Name)+""">"&_
(i__name)+"</a></li>"&VBCRLF
Next
Html = Html + "</ul>"&VBCRLF
end if
Response.Write Html
%>
\ No newline at end of file
<%@ Page Language="C#" AutoEventWireup="true" %>
<%
//
// jQuery File Tree ASP Connector
//
// Version 1.0
//
// Copyright (c)2008 Andrew Sweeny
// asweeny@fit.edu
// 24 March 2008
//
string dir;
if(Request.Form["dir"] == null || Request.Form["dir"].Length <= 0)
dir = "/";
else
dir = Server.UrlDecode(Request.Form["dir"]);
System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(dir);
Response.Write("<ul class=\"jqueryFileTree\" style=\"display: none;\">\n");
foreach (System.IO.DirectoryInfo di_child in di.GetDirectories())
Response.Write("\t<li class=\"directory collapsed\"><a href=\"#\" rel=\"" + dir + di_child.Name + "/\">" + di_child.Name + "</a></li>\n");
foreach (System.IO.FileInfo fi in di.GetFiles())
{
string ext = "";
if(fi.Extension.Length > 1)
ext = fi.Extension.Substring(1).ToLower();
Response.Write("\t<li class=\"file ext_" + ext + "\"><a href=\"#\" rel=\"" + dir + fi.Name + "\">" + fi.Name + "</a></li>\n");
}
Response.Write("</ul>");
%>
\ No newline at end of file
<!---
jQuery File Tree
ColdFusion connector script
By Tjarko Rikkerink (http://carlosgallupa.com/)
--->
<cfparam name="form.dir" default="/somedir" />
<cfdirectory action="LIST" directory="#expandpath('#URLDecode(form.dir)#')#" name="qDir" sort="type, name" type="all" listinfo="all" recurse="no">
<ul class="jqueryFileTree" style="display: none;">
<cfoutput query="qDir">
<cfif type eq "dir">
<li class="directory collapsed"><a href="##" rel="#URLDecode(form.dir)##name#/">#name#</a></li>
<cfelseif type eq "file">
<li class="file ext_#listLast(name,'.')#"><a href="##" rel="#URLDecode(form.dir)##name#">#name# (#round(size/1024)#KB)</a></li>
</cfif>
</cfoutput>
</ul>
\ No newline at end of file
<%@ page
import="java.io.File,java.io.FilenameFilter,java.util.Arrays"%>
<%
/**
* jQuery File Tree JSP Connector
* Version 1.0
* Copyright 2008 Joshua Gould
* 21 April 2008
*/
String dir = request.getParameter("dir");
if (dir == null) {
return;
}
if (dir.charAt(dir.length()-1) == '\\') {
dir = dir.substring(0, dir.length()-1) + "/";
} else if (dir.charAt(dir.length()-1) != '/') {
dir += "/";
}
dir = java.net.URLDecoder.decode(dir, "UTF-8");
if (new File(dir).exists()) {
String[] files = new File(dir).list(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.charAt(0) != '.';
}
});
Arrays.sort(files, String.CASE_INSENSITIVE_ORDER);
out.print("<ul class=\"jqueryFileTree\" style=\"display: none;\">");
// All dirs
for (String file : files) {
if (new File(dir, file).isDirectory()) {
out.print("<li class=\"directory collapsed\"><a href=\"#\" rel=\"" + dir + file + "/\">"
+ file + "</a></li>");
}
}
// All files
for (String file : files) {
if (!new File(dir, file).isDirectory()) {
int dotIndex = file.lastIndexOf('.');
String ext = dotIndex > 0 ? file.substring(dotIndex + 1) : "";
out.print("<li class=\"file ext_" + ext + "\"><a href=\"#\" rel=\"" + dir + file + "\">"
+ file + "</a></li>");
}
}
out.print("</ul>");
}
%>
\ No newline at end of file
<?php
//
// jQuery File Tree PHP Connector
//
// Version 1.01
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
// 24 March 2008
//
// History:
//
// 1.01 - updated to work with foreign characters in directory/file names (12 April 2008)
// 1.00 - released (24 March 2008)
//
// Output a list of files for jQuery File Tree
//
$root = '/var/www/aqvatariusv/aqvatarius.com/development/s/assets/uploads';
$_POST['dir'] = urldecode($_POST['dir']);
if( file_exists($root . $_POST['dir']) ) {
$files = scandir($root . $_POST['dir']);
natcasesort($files);
if( count($files) > 2 ) { /* The 2 accounts for . and .. */
echo "<ul class=\"jqueryFileTree\" style=\"display: none;\">";
// All dirs
foreach( $files as $file ) {
if( file_exists($root . $_POST['dir'] . $file) && $file != '.' && $file != '..' && is_dir($root . $_POST['dir'] . $file) && substr($file, 0, 1) != '.') {
echo "<li class=\"directory collapsed\"><a href=\"#\" rel=\"" . htmlentities($_POST['dir'] . $file) . "/\">" . htmlentities($file) . "</a></li>";
}
}
// All files
foreach( $files as $file ) {
if( file_exists($root . $_POST['dir'] . $file) && $file != '.' && $file != '..' && !is_dir($root . $_POST['dir'] . $file) ) {
$ext = preg_replace('/^.*\./', '', $file);
echo "<li class=\"file ext_$ext\"><a href=\"#\" rel=\"" . htmlentities($_POST['dir'] . $file) . "\">" . htmlentities($file) . "</a></li>";
}
}
echo "</ul>";
}
}
?>
\ No newline at end of file
#!/usr/bin/perl
use strict;
use HTML::Entities ();
#-----------------------------------------------------------
# jQuery File Tree Perl Connector
#
# Version 1.0
#
# Oleg Burlaca
# http://www.burlaca.com/2009/02/jquery-file-tree-connector/
# 12 February 2009
#-----------------------------------------------------------
# for security reasons, specify a root folder
# to prevent the whole filesystem to be shown
# for ex: the root folder of your webbrowser
my $root = "/var/www/html/";
#----------------------------------------------------------
my $params = &getCGIParams();
print "Content-type: text/html\n\n";
my $dir = $params->{dir};
my $fullDir = $root . $dir;
exit if ! -e $fullDir;
opendir(BIN, $fullDir) or die "Can't open $dir: $!";
my (@folders, @files);
my $total = 0;
while( defined (my $file = readdir BIN) ) {
next if $file eq '.' or $file eq '..';
$total++;
if (-d "$fullDir/$file") {
push (@folders, $file);
} else {
push (@files, $file);
}
}
closedir(BIN);
return if $total == 0;
print "<ul class=\"jqueryFileTree\" style=\"display: none;\">";
# print Folders
foreach my $file (sort @folders) {
next if ! -e $fullDir . $file;
print '<li class="directory collapsed"><a href="#" rel="' .
&HTML::Entities::encode($dir . $file) . '/">' .
&HTML::Entities::encode($file) . '</a></li>';
}
# print Files
foreach my $file (sort @files) {
next if ! -e $fullDir . $file;
$file =~ /\.(.+)$/;
my $ext = $1;
print '<li class="file ext_' . $ext . '"><a href="#" rel="' .
&HTML::Entities::encode($dir . $file) . '/">' .
&HTML::Entities::encode($file) . '</a></li>';
}
print "</ul>\n";
#--------------------------------------------------------------------------------------------------
sub getCGIParams {
my $line;
if ($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN, $line, $ENV{'CONTENT_LENGTH'});
} else {
$line = $ENV{'QUERY_STRING'};
}
my (@pairs) = split(/&/, $line);
my ($name, $value, %F);
foreach (@pairs) {
($name, $value) = split(/=/);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
if (! exists $F{$name}) {
$F{$name} = $value;
} elsif (exists $F{$name} and ref($F{$name}) ne 'ARRAY') {
my $prev_value = $F{$name};
delete $F{$name};
$F{$name} = [ $prev_value, $value ];
} else { push @{ $F{$name} }, $value }
}
return \%F;
}
#--------------------------------------------------------------------------------------------------
\ No newline at end of file
#
# jQuery File Tree
# Python/Django connector script
# By Martin Skou
#
import os
import urllib
def dirlist(request):
r=['<ul class="jqueryFileTree" style="display: none;">']
try:
r=['<ul class="jqueryFileTree" style="display: none;">']
d=urllib.unquote(request.POST.get('dir','c:\\temp'))
for f in os.listdir(d):
ff=os.path.join(d,f)
if os.path.isdir(ff):
r.append('<li class="directory collapsed"><a href="#" rel="%s/">%s</a></li>' % (ff,f))
else:
e=os.path.splitext(f)[1][1:] # get .ext and remove dot
r.append('<li class="file ext_%s"><a href="#" rel="%s">%s</a></li>' % (e,ff,f))
r.append('</ul>')
except Exception,e:
r.append('Could not load directory: %s' % str(e))
r.append('</ul>')
return HttpResponse(''.join(r))
\ No newline at end of file
#
# jQuery File Tree Ruby Connector
#
# Version 1.01
#
# Erik Lax
# http://datahack.se
# 13 July 2008
#
# History
#
# 1.01 Initial Release
#
# Output a list of files for jQuery File Tree
#
#<settings>
#root = "/absolute/path/"
# or
root = File.expand_path(".")
#</settings>
#<code>
require "cgi"
cgi = CGI.new
cgi.header("type" => "text/html")
dir = cgi.params["dir"].to_s
puts "<ul class=\"jqueryFileTree\" style=\"display: none;\">"
begin
path = root + "/" + dir
# chdir() to user requested dir (root + "/" + dir)
Dir.chdir(File.expand_path(path).untaint);
# check that our base path still begins with root path
if Dir.pwd[0,root.length] == root then
#loop through all directories
Dir.glob("*") {
|x|
if not File.directory?(x.untaint) then next end
puts "<li class=\"directory collapsed\"><a href=\"#\" rel=\"#{dir}#{x}/\">#{x}</a></li>";
}
#loop through all files
Dir.glob("*") {
|x|
if not File.file?(x.untaint) then next end
ext = File.extname(x)[1..-1]
puts "<li class=\"file ext_#{ext}\"><a href=\"#\" rel=\"#{dir}#{x}\">#{x}</a></li>"
}
else
#only happens when someone tries to go outside your root directory...
puts "You are way out of your league"
end
rescue
puts "Internal Error"
end
puts "</ul>"
#</code>
[
//
// jQuery File Tree Lasso Connector
//
// Version 1.00
//
// Jason Huck
// http://devblog.jasonhuck.com/
// 1 May 2008
//
// History:
//
// 1.00 - released (1 May 2008)
//
// Output a list of files for jQuery File Tree
//
!action_param('dir') ? abort;
var('dir') = action_param('dir');
var('files') = file_listdirectory($dir);
'<ul class="jqueryFileTree" style="display: none;">';
iterate($files, local('file'));
#file->beginswith('.') ? loop_continue;
if(#file->endswith('/'));
'<li class="directory collapsed"><a href="#" rel="' + $dir + #file + '">' + #file + '</a></li>';
else;
local('ext') = #file->split('.')->last;
'<li class="file ext_' + #ext + '"><a href="#" rel="' + $dir + #file + '">' + #file + '</a></li>';
/if;
/iterate;
'</ul>';
]
<?LassoScript
//
// jQuery File Tree LASSO Connector
//
// Version 1.00
//
// Marc Sabourdin
// CysNET (http://www.marcsabourdin.com/)
// 23 May 2008
//
// History:
//
// 1.00 - released (23 May 2008)
//
// Output a list of files for jQuery File Tree
//
Encode_set:-EncodeNone;
Variable:'root' = 'path_to_desired_and_Lasso_allowed_root';
Variable:'_POST.dir' = (action_param:'dir');
Variable:'files';
if:( file_exists: ($root + $_POST.dir) )&&( File_IsDirectory:($root + $_POST.dir) );
$files = (File_ListDirectory:($root + $_POST.dir));
$files->(Sort);
if:( $files->(Size) > 0 );
output:'<ul class="jqueryFileTree" style="display: none;">';
// All dirs
Iterate:($files),(Local:'file');
if:( file_exists:($root + $_POST.dir + #file) )&&( #file != '.' )&&( #file != '..' )&&( File_IsDirectory:($root + $_POST.dir + #file) );
output:'<li class="directory collapsed"><a href="#" rel="' + (String_replace:($_POST.dir + #file),-Find=' ',-Replace='__') + '">' + (Encode_HTML:(#file)) + '</a></li>';
/if;
/Iterate;
// All files
Local:'ext';
Iterate:($files),(Local:'file');
if:( file_exists:($root + $_POST.dir + #file) )&&( #file != '.' )&&( #file != '..' )&&( (File_IsDirectory:($root + $_POST.dir + #file))==false );
#ext = (#file)->(Split:'.')->Last;
output:'<li class="file ext_' + (#ext) + '"><a href="' + ($_POST.dir + #file) + '">' + (Encode_HTML:(#file)) + '</a></li>';
/if;
/Iterate;
output:'</ul>';
/if;
/if;
/Encode_set;
?>
\ No newline at end of file
<?php
/*
* jQuery File Upload Plugin PHP Class 8.1.0
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2010, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
class UploadHandler
{
protected $options;
// PHP File Upload error message codes:
// http://php.net/manual/en/features.file-upload.errors.php
protected $error_messages = array(
1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
3 => 'The uploaded file was only partially uploaded',
4 => 'No file was uploaded',
6 => 'Missing a temporary folder',
7 => 'Failed to write file to disk',
8 => 'A PHP extension stopped the file upload',
'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
'max_file_size' => 'File is too big',
'min_file_size' => 'File is too small',
'accept_file_types' => 'Filetype not allowed',
'max_number_of_files' => 'Maximum number of files exceeded',
'max_width' => 'Image exceeds maximum width',
'min_width' => 'Image requires a minimum width',
'max_height' => 'Image exceeds maximum height',
'min_height' => 'Image requires a minimum height',
'abort' => 'File upload aborted',
'image_resize' => 'Failed to resize image'
);
protected $image_objects = array();
function __construct($options = null, $initialize = true, $error_messages = null) {
$this->options = array(
'script_url' => $this->get_full_url().'/',
'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/files/',
'upload_url' => $this->get_full_url().'/files/',
'user_dirs' => false,
'mkdir_mode' => 0755,
'param_name' => 'files',
// Set the following option to 'POST', if your server does not support
// DELETE requests. This is a parameter sent to the client:
'delete_type' => 'DELETE',
'access_control_allow_origin' => '*',
'access_control_allow_credentials' => false,
'access_control_allow_methods' => array(
'OPTIONS',
'HEAD',
'GET',
'POST',
'PUT',
'PATCH',
'DELETE'
),
'access_control_allow_headers' => array(
'Content-Type',
'Content-Range',
'Content-Disposition'
),
// Enable to provide file downloads via GET requests to the PHP script:
// 1. Set to 1 to download files via readfile method through PHP
// 2. Set to 2 to send a X-Sendfile header for lighttpd/Apache
// 3. Set to 3 to send a X-Accel-Redirect header for nginx
// If set to 2 or 3, adjust the upload_url option to the base path of
// the redirect parameter, e.g. '/files/'.
'download_via_php' => false,
// Read files in chunks to avoid memory limits when download_via_php
// is enabled, set to 0 to disable chunked reading of files:
'readfile_chunk_size' => 10 * 1024 * 1024, // 10 MiB
// Defines which files can be displayed inline when downloaded:
'inline_file_types' => '/\.(gif|jpe?g|png)$/i',
// Defines which files (based on their names) are accepted for upload:
'accept_file_types' => '/.+$/i',
// The php.ini settings upload_max_filesize and post_max_size
// take precedence over the following max_file_size setting:
'max_file_size' => null,
'min_file_size' => 1,
// The maximum number of files for the upload directory:
'max_number_of_files' => null,
// Defines which files are handled as image files:
'image_file_types' => '/\.(gif|jpe?g|png)$/i',
// Use exif_imagetype on all files to correct file extensions:
'correct_image_extensions' => false,
// Image resolution restrictions:
'max_width' => null,
'max_height' => null,
'min_width' => 1,
'min_height' => 1,
// Set the following option to false to enable resumable uploads:
'discard_aborted_uploads' => true,
// Set to 0 to use the GD library to scale and orient images,
// set to 1 to use imagick (if installed, falls back to GD),
// set to 2 to use the ImageMagick convert binary directly:
'image_library' => 1,
// Uncomment the following to define an array of resource limits
// for imagick:
/*
'imagick_resource_limits' => array(
imagick::RESOURCETYPE_MAP => 32,
imagick::RESOURCETYPE_MEMORY => 32
),
*/
// Command or path for to the ImageMagick convert binary:
'convert_bin' => 'convert',
// Uncomment the following to add parameters in front of each
// ImageMagick convert call (the limit constraints seem only
// to have an effect if put in front):
/*
'convert_params' => '-limit memory 32MiB -limit map 32MiB',
*/
// Command or path for to the ImageMagick identify binary:
'identify_bin' => 'identify',
'image_versions' => array(
// The empty image version key defines options for the original image:
'' => array(
// Automatically rotate images based on EXIF meta data:
'auto_orient' => true
),
// Uncomment the following to create medium sized images:
/*
'medium' => array(
'max_width' => 800,
'max_height' => 600
),
*/
'thumbnail' => array(
// Uncomment the following to use a defined directory for the thumbnails
// instead of a subdirectory based on the version identifier.
// Make sure that this directory doesn't allow execution of files if you
// don't pose any restrictions on the type of uploaded files, e.g. by
// copying the .htaccess file from the files directory for Apache:
//'upload_dir' => dirname($this->get_server_var('SCRIPT_FILENAME')).'/thumb/',
//'upload_url' => $this->get_full_url().'/thumb/',
// Uncomment the following to force the max
// dimensions and e.g. create square thumbnails:
//'crop' => true,
'max_width' => 80,
'max_height' => 80
)
)
);
if ($options) {
$this->options = $options + $this->options;
}
if ($error_messages) {
$this->error_messages = $error_messages + $this->error_messages;
}
if ($initialize) {
$this->initialize();
}
}
protected function initialize() {
switch ($this->get_server_var('REQUEST_METHOD')) {
case 'OPTIONS':
case 'HEAD':
$this->head();
break;
case 'GET':
$this->get();
break;
case 'PATCH':
case 'PUT':
case 'POST':
$this->post();
break;
case 'DELETE':
$this->delete();
break;
default:
$this->header('HTTP/1.1 405 Method Not Allowed');
}
}
protected function get_full_url() {
$https = !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'on') === 0 ||
!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') === 0;
return
($https ? 'https://' : 'http://').
(!empty($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : '').
(isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME'].
($https && $_SERVER['SERVER_PORT'] === 443 ||
$_SERVER['SERVER_PORT'] === 80 ? '' : ':'.$_SERVER['SERVER_PORT']))).
substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/'));
}
protected function get_user_id() {
@session_start();
return session_id();
}
protected function get_user_path() {
if ($this->options['user_dirs']) {
return $this->get_user_id().'/';
}
return '';
}
protected function get_upload_path($file_name = null, $version = null) {
$file_name = $file_name ? $file_name : '';
if (empty($version)) {
$version_path = '';
} else {
$version_dir = @$this->options['image_versions'][$version]['upload_dir'];
if ($version_dir) {
return $version_dir.$this->get_user_path().$file_name;
}
$version_path = $version.'/';
}
return $this->options['upload_dir'].$this->get_user_path()
.$version_path.$file_name;
}
protected function get_query_separator($url) {
return strpos($url, '?') === false ? '?' : '&';
}
protected function get_download_url($file_name, $version = null, $direct = false) {
if (!$direct && $this->options['download_via_php']) {
$url = $this->options['script_url']
.$this->get_query_separator($this->options['script_url'])
.$this->get_singular_param_name()
.'='.rawurlencode($file_name);
if ($version) {
$url .= '&version='.rawurlencode($version);
}
return $url.'&download=1';
}
if (empty($version)) {
$version_path = '';
} else {
$version_url = @$this->options['image_versions'][$version]['upload_url'];
if ($version_url) {
return $version_url.$this->get_user_path().rawurlencode($file_name);
}
$version_path = rawurlencode($version).'/';
}
return $this->options['upload_url'].$this->get_user_path()
.$version_path.rawurlencode($file_name);
}
protected function set_additional_file_properties($file) {
$file->deleteUrl = $this->options['script_url']
.$this->get_query_separator($this->options['script_url'])
.$this->get_singular_param_name()
.'='.rawurlencode($file->name);
$file->deleteType = $this->options['delete_type'];
if ($file->deleteType !== 'DELETE') {
$file->deleteUrl .= '&_method=DELETE';
}
if ($this->options['access_control_allow_credentials']) {
$file->deleteWithCredentials = true;
}
}
// Fix for overflowing signed 32 bit integers,
// works for sizes up to 2^32-1 bytes (4 GiB - 1):
protected function fix_integer_overflow($size) {
if ($size < 0) {
$size += 2.0 * (PHP_INT_MAX + 1);
}
return $size;
}
protected function get_file_size($file_path, $clear_stat_cache = false) {
if ($clear_stat_cache) {
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
clearstatcache(true, $file_path);
} else {
clearstatcache();
}
}
return $this->fix_integer_overflow(filesize($file_path));
}
protected function is_valid_file_object($file_name) {
$file_path = $this->get_upload_path($file_name);
if (is_file($file_path) && $file_name[0] !== '.') {
return true;
}
return false;
}
protected function get_file_object($file_name) {
if ($this->is_valid_file_object($file_name)) {
$file = new \stdClass();
$file->name = $file_name;
$file->size = $this->get_file_size(
$this->get_upload_path($file_name)
);
$file->url = $this->get_download_url($file->name);
foreach($this->options['image_versions'] as $version => $options) {
if (!empty($version)) {
if (is_file($this->get_upload_path($file_name, $version))) {
$file->{$version.'Url'} = $this->get_download_url(
$file->name,
$version
);
}
}
}
$this->set_additional_file_properties($file);
return $file;
}
return null;
}
protected function get_file_objects($iteration_method = 'get_file_object') {
$upload_dir = $this->get_upload_path();
if (!is_dir($upload_dir)) {
return array();
}
return array_values(array_filter(array_map(
array($this, $iteration_method),
scandir($upload_dir)
)));
}
protected function count_file_objects() {
return count($this->get_file_objects('is_valid_file_object'));
}
protected function get_error_message($error) {
return array_key_exists($error, $this->error_messages) ?
$this->error_messages[$error] : $error;
}
function get_config_bytes($val) {
$val = trim($val);
$last = strtolower($val[strlen($val)-1]);
switch($last) {
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}
return $this->fix_integer_overflow($val);
}
protected function validate($uploaded_file, $file, $error, $index) {
if ($error) {
$file->error = $this->get_error_message($error);
return false;
}
$content_length = $this->fix_integer_overflow(intval(
$this->get_server_var('CONTENT_LENGTH')
));
$post_max_size = $this->get_config_bytes(ini_get('post_max_size'));
if ($post_max_size && ($content_length > $post_max_size)) {
$file->error = $this->get_error_message('post_max_size');
return false;
}
if (!preg_match($this->options['accept_file_types'], $file->name)) {
$file->error = $this->get_error_message('accept_file_types');
return false;
}
if ($uploaded_file && is_uploaded_file($uploaded_file)) {
$file_size = $this->get_file_size($uploaded_file);
} else {
$file_size = $content_length;
}
if ($this->options['max_file_size'] && (
$file_size > $this->options['max_file_size'] ||
$file->size > $this->options['max_file_size'])
) {
$file->error = $this->get_error_message('max_file_size');
return false;
}
if ($this->options['min_file_size'] &&
$file_size < $this->options['min_file_size']) {
$file->error = $this->get_error_message('min_file_size');
return false;
}
if (is_int($this->options['max_number_of_files']) &&
($this->count_file_objects() >= $this->options['max_number_of_files']) &&
// Ignore additional chunks of existing files:
!is_file($this->get_upload_path($file->name))) {
$file->error = $this->get_error_message('max_number_of_files');
return false;
}
$max_width = @$this->options['max_width'];
$max_height = @$this->options['max_height'];
$min_width = @$this->options['min_width'];
$min_height = @$this->options['min_height'];
if (($max_width || $max_height || $min_width || $min_height)
&& preg_match($this->options['image_file_types'], $file->name)) {
list($img_width, $img_height) = $this->get_image_size($uploaded_file);
}
if (!empty($img_width)) {
if ($max_width && $img_width > $max_width) {
$file->error = $this->get_error_message('max_width');
return false;
}
if ($max_height && $img_height > $max_height) {
$file->error = $this->get_error_message('max_height');
return false;
}
if ($min_width && $img_width < $min_width) {
$file->error = $this->get_error_message('min_width');
return false;
}
if ($min_height && $img_height < $min_height) {
$file->error = $this->get_error_message('min_height');
return false;
}
}
return true;
}
protected function upcount_name_callback($matches) {
$index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
$ext = isset($matches[2]) ? $matches[2] : '';
return ' ('.$index.')'.$ext;
}
protected function upcount_name($name) {
return preg_replace_callback(
'/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/',
array($this, 'upcount_name_callback'),
$name,
1
);
}
protected function get_unique_filename($file_path, $name, $size, $type, $error,
$index, $content_range) {
while(is_dir($this->get_upload_path($name))) {
$name = $this->upcount_name($name);
}
// Keep an existing filename if this is part of a chunked upload:
$uploaded_bytes = $this->fix_integer_overflow(intval($content_range[1]));
while(is_file($this->get_upload_path($name))) {
if ($uploaded_bytes === $this->get_file_size(
$this->get_upload_path($name))) {
break;
}
$name = $this->upcount_name($name);
}
return $name;
}
protected function fix_file_extension($file_path, $name, $size, $type, $error,
$index, $content_range) {
// Add missing file extension for known image types:
if (strpos($name, '.') === false &&
preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) {
$name .= '.'.$matches[1];
}
if ($this->options['correct_image_extensions'] &&
function_exists('exif_imagetype')) {
switch(@exif_imagetype($file_path)){
case IMAGETYPE_JPEG:
$extensions = array('jpg', 'jpeg');
break;
case IMAGETYPE_PNG:
$extensions = array('png');
break;
case IMAGETYPE_GIF:
$extensions = array('gif');
break;
}
// Adjust incorrect image file extensions:
if (!empty($extensions)) {
$parts = explode('.', $name);
$extIndex = count($parts) - 1;
$ext = strtolower(@$parts[$extIndex]);
if (!in_array($ext, $extensions)) {
$parts[$extIndex] = $extensions[0];
$name = implode('.', $parts);
}
}
}
return $name;
}
protected function trim_file_name($file_path, $name, $size, $type, $error,
$index, $content_range) {
// Remove path information and dots around the filename, to prevent uploading
// into different directories or replacing hidden system files.
// Also remove control characters and spaces (\x00..\x20) around the filename:
$name = trim(basename(stripslashes($name)), ".\x00..\x20");
// Use a timestamp for empty filenames:
if (!$name) {
$name = str_replace('.', '-', microtime(true));
}
return $name;
}
protected function get_file_name($file_path, $name, $size, $type, $error,
$index, $content_range) {
$name = $this->trim_file_name($file_path, $name, $size, $type, $error,
$index, $content_range);
return $this->get_unique_filename(
$file_path,
$this->fix_file_extension($file_path, $name, $size, $type, $error,
$index, $content_range),
$size,
$type,
$error,
$index,
$content_range
);
}
protected function handle_form_data($file, $index) {
// Handle form data, e.g. $_REQUEST['description'][$index]
}
protected function get_scaled_image_file_paths($file_name, $version) {
$file_path = $this->get_upload_path($file_name);
if (!empty($version)) {
$version_dir = $this->get_upload_path(null, $version);
if (!is_dir($version_dir)) {
mkdir($version_dir, $this->options['mkdir_mode'], true);
}
$new_file_path = $version_dir.'/'.$file_name;
} else {
$new_file_path = $file_path;
}
return array($file_path, $new_file_path);
}
protected function gd_get_image_object($file_path, $func, $no_cache = false) {
if (empty($this->image_objects[$file_path]) || $no_cache) {
$this->gd_destroy_image_object($file_path);
$this->image_objects[$file_path] = $func($file_path);
}
return $this->image_objects[$file_path];
}
protected function gd_set_image_object($file_path, $image) {
$this->gd_destroy_image_object($file_path);
$this->image_objects[$file_path] = $image;
}
protected function gd_destroy_image_object($file_path) {
$image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null ;
return $image && imagedestroy($image);
}
protected function gd_imageflip($image, $mode) {
if (function_exists('imageflip')) {
return imageflip($image, $mode);
}
$new_width = $src_width = imagesx($image);
$new_height = $src_height = imagesy($image);
$new_img = imagecreatetruecolor($new_width, $new_height);
$src_x = 0;
$src_y = 0;
switch ($mode) {
case '1': // flip on the horizontal axis
$src_y = $new_height - 1;
$src_height = -$new_height;
break;
case '2': // flip on the vertical axis
$src_x = $new_width - 1;
$src_width = -$new_width;
break;
case '3': // flip on both axes
$src_y = $new_height - 1;
$src_height = -$new_height;
$src_x = $new_width - 1;
$src_width = -$new_width;
break;
default:
return $image;
}
imagecopyresampled(
$new_img,
$image,
0,
0,
$src_x,
$src_y,
$new_width,
$new_height,
$src_width,
$src_height
);
return $new_img;
}
protected function gd_orient_image($file_path, $src_img) {
if (!function_exists('exif_read_data')) {
return false;
}
$exif = @exif_read_data($file_path);
if ($exif === false) {
return false;
}
$orientation = intval(@$exif['Orientation']);
if ($orientation < 2 || $orientation > 8) {
return false;
}
switch ($orientation) {
case 2:
$new_img = $this->gd_imageflip(
$src_img,
defined('IMG_FLIP_VERTICAL') ? IMG_FLIP_VERTICAL : 2
);
break;
case 3:
$new_img = imagerotate($src_img, 180, 0);
break;
case 4:
$new_img = $this->gd_imageflip(
$src_img,
defined('IMG_FLIP_HORIZONTAL') ? IMG_FLIP_HORIZONTAL : 1
);
break;
case 5:
$tmp_img = $this->gd_imageflip(
$src_img,
defined('IMG_FLIP_HORIZONTAL') ? IMG_FLIP_HORIZONTAL : 1
);
$new_img = imagerotate($tmp_img, 270, 0);
imagedestroy($tmp_img);
break;
case 6:
$new_img = imagerotate($src_img, 270, 0);
break;
case 7:
$tmp_img = $this->gd_imageflip(
$src_img,
defined('IMG_FLIP_VERTICAL') ? IMG_FLIP_VERTICAL : 2
);
$new_img = imagerotate($tmp_img, 270, 0);
imagedestroy($tmp_img);
break;
case 8:
$new_img = imagerotate($src_img, 90, 0);
break;
default:
return false;
}
$this->gd_set_image_object($file_path, $new_img);
return true;
}
protected function gd_create_scaled_image($file_name, $version, $options) {
if (!function_exists('imagecreatetruecolor')) {
error_log('Function not found: imagecreatetruecolor');
return false;
}
list($file_path, $new_file_path) =
$this->get_scaled_image_file_paths($file_name, $version);
$type = strtolower(substr(strrchr($file_name, '.'), 1));
switch ($type) {
case 'jpg':
case 'jpeg':
$src_func = 'imagecreatefromjpeg';
$write_func = 'imagejpeg';
$image_quality = isset($options['jpeg_quality']) ?
$options['jpeg_quality'] : 75;
break;
case 'gif':
$src_func = 'imagecreatefromgif';
$write_func = 'imagegif';
$image_quality = null;
break;
case 'png':
$src_func = 'imagecreatefrompng';
$write_func = 'imagepng';
$image_quality = isset($options['png_quality']) ?
$options['png_quality'] : 9;
break;
default:
return false;
}
$src_img = $this->gd_get_image_object(
$file_path,
$src_func,
!empty($options['no_cache'])
);
$image_oriented = false;
if (!empty($options['auto_orient']) && $this->gd_orient_image(
$file_path,
$src_img
)) {
$image_oriented = true;
$src_img = $this->gd_get_image_object(
$file_path,
$src_func
);
}
$max_width = $img_width = imagesx($src_img);
$max_height = $img_height = imagesy($src_img);
if (!empty($options['max_width'])) {
$max_width = $options['max_width'];
}
if (!empty($options['max_height'])) {
$max_height = $options['max_height'];
}
$scale = min(
$max_width / $img_width,
$max_height / $img_height
);
if ($scale >= 1) {
if ($image_oriented) {
return $write_func($src_img, $new_file_path, $image_quality);
}
if ($file_path !== $new_file_path) {
return copy($file_path, $new_file_path);
}
return true;
}
if (empty($options['crop'])) {
$new_width = $img_width * $scale;
$new_height = $img_height * $scale;
$dst_x = 0;
$dst_y = 0;
$new_img = imagecreatetruecolor($new_width, $new_height);
} else {
if (($img_width / $img_height) >= ($max_width / $max_height)) {
$new_width = $img_width / ($img_height / $max_height);
$new_height = $max_height;
} else {
$new_width = $max_width;
$new_height = $img_height / ($img_width / $max_width);
}
$dst_x = 0 - ($new_width - $max_width) / 2;
$dst_y = 0 - ($new_height - $max_height) / 2;
$new_img = imagecreatetruecolor($max_width, $max_height);
}
// Handle transparency in GIF and PNG images:
switch ($type) {
case 'gif':
case 'png':
imagecolortransparent($new_img, imagecolorallocate($new_img, 0, 0, 0));
case 'png':
imagealphablending($new_img, false);
imagesavealpha($new_img, true);
break;
}
$success = imagecopyresampled(
$new_img,
$src_img,
$dst_x,
$dst_y,
0,
0,
$new_width,
$new_height,
$img_width,
$img_height
) && $write_func($new_img, $new_file_path, $image_quality);
$this->gd_set_image_object($file_path, $new_img);
return $success;
}
protected function imagick_get_image_object($file_path, $no_cache = false) {
if (empty($this->image_objects[$file_path]) || $no_cache) {
$this->imagick_destroy_image_object($file_path);
$image = new \Imagick();
if (!empty($this->options['imagick_resource_limits'])) {
foreach ($this->options['imagick_resource_limits'] as $type => $limit) {
$image->setResourceLimit($type, $limit);
}
}
$image->readImage($file_path);
$this->image_objects[$file_path] = $image;
}
return $this->image_objects[$file_path];
}
protected function imagick_set_image_object($file_path, $image) {
$this->imagick_destroy_image_object($file_path);
$this->image_objects[$file_path] = $image;
}
protected function imagick_destroy_image_object($file_path) {
$image = (isset($this->image_objects[$file_path])) ? $this->image_objects[$file_path] : null ;
return $image && $image->destroy();
}
protected function imagick_orient_image($image) {
$orientation = $image->getImageOrientation();
$background = new \ImagickPixel('none');
switch ($orientation) {
case \imagick::ORIENTATION_TOPRIGHT: // 2
$image->flopImage(); // horizontal flop around y-axis
break;
case \imagick::ORIENTATION_BOTTOMRIGHT: // 3
$image->rotateImage($background, 180);
break;
case \imagick::ORIENTATION_BOTTOMLEFT: // 4
$image->flipImage(); // vertical flip around x-axis
break;
case \imagick::ORIENTATION_LEFTTOP: // 5
$image->flopImage(); // horizontal flop around y-axis
$image->rotateImage($background, 270);
break;
case \imagick::ORIENTATION_RIGHTTOP: // 6
$image->rotateImage($background, 90);
break;
case \imagick::ORIENTATION_RIGHTBOTTOM: // 7
$image->flipImage(); // vertical flip around x-axis
$image->rotateImage($background, 270);
break;
case \imagick::ORIENTATION_LEFTBOTTOM: // 8
$image->rotateImage($background, 270);
break;
default:
return false;
}
$image->setImageOrientation(\imagick::ORIENTATION_TOPLEFT); // 1
return true;
}
protected function imagick_create_scaled_image($file_name, $version, $options) {
list($file_path, $new_file_path) =
$this->get_scaled_image_file_paths($file_name, $version);
$image = $this->imagick_get_image_object(
$file_path,
!empty($options['no_cache'])
);
if ($image->getImageFormat() === 'GIF') {
// Handle animated GIFs:
$images = $image->coalesceImages();
foreach ($images as $frame) {
$image = $frame;
$this->imagick_set_image_object($file_name, $image);
break;
}
}
$image_oriented = false;
if (!empty($options['auto_orient'])) {
$image_oriented = $this->imagick_orient_image($image);
}
$new_width = $max_width = $img_width = $image->getImageWidth();
$new_height = $max_height = $img_height = $image->getImageHeight();
if (!empty($options['max_width'])) {
$new_width = $max_width = $options['max_width'];
}
if (!empty($options['max_height'])) {
$new_height = $max_height = $options['max_height'];
}
if (!($image_oriented || $max_width < $img_width || $max_height < $img_height)) {
if ($file_path !== $new_file_path) {
return copy($file_path, $new_file_path);
}
return true;
}
$crop = !empty($options['crop']);
if ($crop) {
$x = 0;
$y = 0;
if (($img_width / $img_height) >= ($max_width / $max_height)) {
$new_width = 0; // Enables proportional scaling based on max_height
$x = ($img_width / ($img_height / $max_height) - $max_width) / 2;
} else {
$new_height = 0; // Enables proportional scaling based on max_width
$y = ($img_height / ($img_width / $max_width) - $max_height) / 2;
}
}
$success = $image->resizeImage(
$new_width,
$new_height,
isset($options['filter']) ? $options['filter'] : \imagick::FILTER_LANCZOS,
isset($options['blur']) ? $options['blur'] : 1,
$new_width && $new_height // fit image into constraints if not to be cropped
);
if ($success && $crop) {
$success = $image->cropImage(
$max_width,
$max_height,
$x,
$y
);
if ($success) {
$success = $image->setImagePage($max_width, $max_height, 0, 0);
}
}
$type = strtolower(substr(strrchr($file_name, '.'), 1));
switch ($type) {
case 'jpg':
case 'jpeg':
if (!empty($options['jpeg_quality'])) {
$image->setImageCompression(\imagick::COMPRESSION_JPEG);
$image->setImageCompressionQuality($options['jpeg_quality']);
}
break;
}
if (!empty($options['strip'])) {
$image->stripImage();
}
return $success && $image->writeImage($new_file_path);
}
protected function imagemagick_create_scaled_image($file_name, $version, $options) {
list($file_path, $new_file_path) =
$this->get_scaled_image_file_paths($file_name, $version);
$resize = @$options['max_width']
.(empty($options['max_height']) ? '' : 'X'.$options['max_height']);
if (!$resize && empty($options['auto_orient'])) {
if ($file_path !== $new_file_path) {
return copy($file_path, $new_file_path);
}
return true;
}
$cmd = $this->options['convert_bin'];
if (!empty($this->options['convert_params'])) {
$cmd .= ' '.$this->options['convert_params'];
}
$cmd .= ' '.escapeshellarg($file_path);
if (!empty($options['auto_orient'])) {
$cmd .= ' -auto-orient';
}
if ($resize) {
// Handle animated GIFs:
$cmd .= ' -coalesce';
if (empty($options['crop'])) {
$cmd .= ' -resize '.escapeshellarg($resize.'>');
} else {
$cmd .= ' -resize '.escapeshellarg($resize.'^');
$cmd .= ' -gravity center';
$cmd .= ' -crop '.escapeshellarg($resize.'+0+0');
}
// Make sure the page dimensions are correct (fixes offsets of animated GIFs):
$cmd .= ' +repage';
}
if (!empty($options['convert_params'])) {
$cmd .= ' '.$options['convert_params'];
}
$cmd .= ' '.escapeshellarg($new_file_path);
exec($cmd, $output, $error);
if ($error) {
error_log(implode('\n', $output));
return false;
}
return true;
}
protected function get_image_size($file_path) {
if ($this->options['image_library']) {
if (extension_loaded('imagick')) {
$image = new \Imagick();
try {
if (@$image->pingImage($file_path)) {
$dimensions = array($image->getImageWidth(), $image->getImageHeight());
$image->destroy();
return $dimensions;
}
return false;
} catch (Exception $e) {
error_log($e->getMessage());
}
}
if ($this->options['image_library'] === 2) {
$cmd = $this->options['identify_bin'];
$cmd .= ' -ping '.escapeshellarg($file_path);
exec($cmd, $output, $error);
if (!$error && !empty($output)) {
// image.jpg JPEG 1920x1080 1920x1080+0+0 8-bit sRGB 465KB 0.000u 0:00.000
$infos = preg_split('/\s+/', $output[0]);
$dimensions = preg_split('/x/', $infos[2]);
return $dimensions;
}
return false;
}
}
if (!function_exists('getimagesize')) {
error_log('Function not found: getimagesize');
return false;
}
return @getimagesize($file_path);
}
protected function create_scaled_image($file_name, $version, $options) {
if ($this->options['image_library'] === 2) {
return $this->imagemagick_create_scaled_image($file_name, $version, $options);
}
if ($this->options['image_library'] && extension_loaded('imagick')) {
return $this->imagick_create_scaled_image($file_name, $version, $options);
}
return $this->gd_create_scaled_image($file_name, $version, $options);
}
protected function destroy_image_object($file_path) {
if ($this->options['image_library'] && extension_loaded('imagick')) {
return $this->imagick_destroy_image_object($file_path);
}
}
protected function is_valid_image_file($file_path) {
if (!preg_match($this->options['image_file_types'], $file_path)) {
return false;
}
if (function_exists('exif_imagetype')) {
return @exif_imagetype($file_path);
}
$image_info = $this->get_image_size($file_path);
return $image_info && $image_info[0] && $image_info[1];
}
protected function handle_image_file($file_path, $file) {
$failed_versions = array();
foreach($this->options['image_versions'] as $version => $options) {
if ($this->create_scaled_image($file->name, $version, $options)) {
if (!empty($version)) {
$file->{$version.'Url'} = $this->get_download_url(
$file->name,
$version
);
} else {
$file->size = $this->get_file_size($file_path, true);
}
} else {
$failed_versions[] = $version ? $version : 'original';
}
}
if (count($failed_versions)) {
$file->error = $this->get_error_message('image_resize')
.' ('.implode($failed_versions,', ').')';
}
// Free memory:
$this->destroy_image_object($file_path);
}
protected function handle_file_upload($uploaded_file, $name, $size, $type, $error,
$index = null, $content_range = null) {
$file = new \stdClass();
$file->name = $this->get_file_name($uploaded_file, $name, $size, $type, $error,
$index, $content_range);
$file->size = $this->fix_integer_overflow(intval($size));
$file->type = $type;
if ($this->validate($uploaded_file, $file, $error, $index)) {
$this->handle_form_data($file, $index);
$upload_dir = $this->get_upload_path();
if (!is_dir($upload_dir)) {
mkdir($upload_dir, $this->options['mkdir_mode'], true);
}
$file_path = $this->get_upload_path($file->name);
$append_file = $content_range && is_file($file_path) &&
$file->size > $this->get_file_size($file_path);
if ($uploaded_file && is_uploaded_file($uploaded_file)) {
// multipart/formdata uploads (POST method uploads)
if ($append_file) {
file_put_contents(
$file_path,
fopen($uploaded_file, 'r'),
FILE_APPEND
);
} else {
move_uploaded_file($uploaded_file, $file_path);
}
} else {
// Non-multipart uploads (PUT method support)
file_put_contents(
$file_path,
fopen('php://input', 'r'),
$append_file ? FILE_APPEND : 0
);
}
$file_size = $this->get_file_size($file_path, $append_file);
if ($file_size === $file->size) {
$file->url = $this->get_download_url($file->name);
if ($this->is_valid_image_file($file_path)) {
$this->handle_image_file($file_path, $file);
}
} else {
$file->size = $file_size;
if (!$content_range && $this->options['discard_aborted_uploads']) {
unlink($file_path);
$file->error = $this->get_error_message('abort');
}
}
$this->set_additional_file_properties($file);
}
return $file;
}
protected function readfile($file_path) {
$file_size = $this->get_file_size($file_path);
$chunk_size = $this->options['readfile_chunk_size'];
if ($chunk_size && $file_size > $chunk_size) {
$handle = fopen($file_path, 'rb');
while (!feof($handle)) {
echo fread($handle, $chunk_size);
@ob_flush();
@flush();
}
fclose($handle);
return $file_size;
}
return readfile($file_path);
}
protected function body($str) {
echo $str;
}
protected function header($str) {
header($str);
}
protected function get_server_var($id) {
return isset($_SERVER[$id]) ? $_SERVER[$id] : '';
}
protected function generate_response($content, $print_response = true) {
if ($print_response) {
$json = json_encode($content);
$redirect = isset($_REQUEST['redirect']) ?
stripslashes($_REQUEST['redirect']) : null;
if ($redirect) {
$this->header('Location: '.sprintf($redirect, rawurlencode($json)));
return;
}
$this->head();
if ($this->get_server_var('HTTP_CONTENT_RANGE')) {
$files = isset($content[$this->options['param_name']]) ?
$content[$this->options['param_name']] : null;
if ($files && is_array($files) && is_object($files[0]) && $files[0]->size) {
$this->header('Range: 0-'.(
$this->fix_integer_overflow(intval($files[0]->size)) - 1
));
}
}
$this->body($json);
}
return $content;
}
protected function get_version_param() {
return isset($_GET['version']) ? basename(stripslashes($_GET['version'])) : null;
}
protected function get_singular_param_name() {
return substr($this->options['param_name'], 0, -1);
}
protected function get_file_name_param() {
$name = $this->get_singular_param_name();
return isset($_REQUEST[$name]) ? basename(stripslashes($_REQUEST[$name])) : null;
}
protected function get_file_names_params() {
$params = isset($_REQUEST[$this->options['param_name']]) ?
$_REQUEST[$this->options['param_name']] : array();
foreach ($params as $key => $value) {
$params[$key] = basename(stripslashes($value));
}
return $params;
}
protected function get_file_type($file_path) {
switch (strtolower(pathinfo($file_path, PATHINFO_EXTENSION))) {
case 'jpeg':
case 'jpg':
return 'image/jpeg';
case 'png':
return 'image/png';
case 'gif':
return 'image/gif';
default:
return '';
}
}
protected function download() {
switch ($this->options['download_via_php']) {
case 1:
$redirect_header = null;
break;
case 2:
$redirect_header = 'X-Sendfile';
break;
case 3:
$redirect_header = 'X-Accel-Redirect';
break;
default:
return $this->header('HTTP/1.1 403 Forbidden');
}
$file_name = $this->get_file_name_param();
if (!$this->is_valid_file_object($file_name)) {
return $this->header('HTTP/1.1 404 Not Found');
}
if ($redirect_header) {
return $this->header(
$redirect_header.': '.$this->get_download_url(
$file_name,
$this->get_version_param(),
true
)
);
}
$file_path = $this->get_upload_path($file_name, $this->get_version_param());
// Prevent browsers from MIME-sniffing the content-type:
$this->header('X-Content-Type-Options: nosniff');
if (!preg_match($this->options['inline_file_types'], $file_name)) {
$this->header('Content-Type: application/octet-stream');
$this->header('Content-Disposition: attachment; filename="'.$file_name.'"');
} else {
$this->header('Content-Type: '.$this->get_file_type($file_path));
$this->header('Content-Disposition: inline; filename="'.$file_name.'"');
}
$this->header('Content-Length: '.$this->get_file_size($file_path));
$this->header('Last-Modified: '.gmdate('D, d M Y H:i:s T', filemtime($file_path)));
$this->readfile($file_path);
}
protected function send_content_type_header() {
$this->header('Vary: Accept');
if (strpos($this->get_server_var('HTTP_ACCEPT'), 'application/json') !== false) {
$this->header('Content-type: application/json');
} else {
$this->header('Content-type: text/plain');
}
}
protected function send_access_control_headers() {
$this->header('Access-Control-Allow-Origin: '.$this->options['access_control_allow_origin']);
$this->header('Access-Control-Allow-Credentials: '
.($this->options['access_control_allow_credentials'] ? 'true' : 'false'));
$this->header('Access-Control-Allow-Methods: '
.implode(', ', $this->options['access_control_allow_methods']));
$this->header('Access-Control-Allow-Headers: '
.implode(', ', $this->options['access_control_allow_headers']));
}
public function head() {
$this->header('Pragma: no-cache');
$this->header('Cache-Control: no-store, no-cache, must-revalidate');
$this->header('Content-Disposition: inline; filename="files.json"');
// Prevent Internet Explorer from MIME-sniffing the content-type:
$this->header('X-Content-Type-Options: nosniff');
if ($this->options['access_control_allow_origin']) {
$this->send_access_control_headers();
}
$this->send_content_type_header();
}
public function get($print_response = true) {
if ($print_response && isset($_GET['download'])) {
return $this->download();
}
$file_name = $this->get_file_name_param();
if ($file_name) {
$response = array(
$this->get_singular_param_name() => $this->get_file_object($file_name)
);
} else {
$response = array(
$this->options['param_name'] => $this->get_file_objects()
);
}
return $this->generate_response($response, $print_response);
}
public function post($print_response = true) {
if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') {
return $this->delete($print_response);
}
$upload = isset($_FILES[$this->options['param_name']]) ?
$_FILES[$this->options['param_name']] : null;
// Parse the Content-Disposition header, if available:
$file_name = $this->get_server_var('HTTP_CONTENT_DISPOSITION') ?
rawurldecode(preg_replace(
'/(^[^"]+")|("$)/',
'',
$this->get_server_var('HTTP_CONTENT_DISPOSITION')
)) : null;
// Parse the Content-Range header, which has the following form:
// Content-Range: bytes 0-524287/2000000
$content_range = $this->get_server_var('HTTP_CONTENT_RANGE') ?
preg_split('/[^0-9]+/', $this->get_server_var('HTTP_CONTENT_RANGE')) : null;
$size = $content_range ? $content_range[3] : null;
$files = array();
if ($upload && is_array($upload['tmp_name'])) {
// param_name is an array identifier like "files[]",
// $_FILES is a multi-dimensional array:
foreach ($upload['tmp_name'] as $index => $value) {
$files[] = $this->handle_file_upload(
$upload['tmp_name'][$index],
$file_name ? $file_name : $upload['name'][$index],
$size ? $size : $upload['size'][$index],
$upload['type'][$index],
$upload['error'][$index],
$index,
$content_range
);
}
} else {
// param_name is a single object identifier like "file",
// $_FILES is a one-dimensional array:
$files[] = $this->handle_file_upload(
isset($upload['tmp_name']) ? $upload['tmp_name'] : null,
$file_name ? $file_name : (isset($upload['name']) ?
$upload['name'] : null),
$size ? $size : (isset($upload['size']) ?
$upload['size'] : $this->get_server_var('CONTENT_LENGTH')),
isset($upload['type']) ?
$upload['type'] : $this->get_server_var('CONTENT_TYPE'),
isset($upload['error']) ? $upload['error'] : null,
null,
$content_range
);
}
return $this->generate_response(
array($this->options['param_name'] => $files),
$print_response
);
}
public function delete($print_response = true) {
$file_names = $this->get_file_names_params();
if (empty($file_names)) {
$file_names = array($this->get_file_name_param());
}
$response = array();
foreach($file_names as $file_name) {
$file_path = $this->get_upload_path($file_name);
$success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
if ($success) {
foreach($this->options['image_versions'] as $version => $options) {
if (!empty($version)) {
$file = $this->get_upload_path($file_name, $version);
if (is_file($file)) {
unlink($file);
}
}
}
}
$response[$file_name] = $success;
}
return $this->generate_response($response, $print_response);
}
}
<!DOCTYPE HTML>
<!--
/*
* jQuery File Upload Plugin postMessage API 1.2.1
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2011, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
-->
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery File Upload Plugin postMessage API</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
</head>
<body>
<script>
/*jslint unparam: true, regexp: true */
/*global $, Blob, FormData, location */
'use strict';
var origin = /^http:\/\/example.org/,
target = new RegExp('^(http(s)?:)?\\/\\/' + location.host + '\\/');
$(window).on('message', function (e) {
e = e.originalEvent;
var s = e.data,
xhr = $.ajaxSettings.xhr(),
f;
if (!origin.test(e.origin)) {
throw new Error('Origin "' + e.origin + '" does not match ' + origin);
}
if (!target.test(e.data.url)) {
throw new Error('Target "' + e.data.url + '" does not match ' + target);
}
$(xhr.upload).on('progress', function (ev) {
ev = ev.originalEvent;
e.source.postMessage({
id: s.id,
type: ev.type,
timeStamp: ev.timeStamp,
lengthComputable: ev.lengthComputable,
loaded: ev.loaded,
total: ev.total
}, e.origin);
});
s.xhr = function () {
return xhr;
};
if (!(s.data instanceof Blob)) {
f = new FormData();
$.each(s.data, function (i, v) {
f.append(v.name, v.value);
});
s.data = f;
}
$.ajax(s).always(function (result, statusText, jqXHR) {
if (!jqXHR.done) {
jqXHR = result;
result = null;
}
e.source.postMessage({
id: s.id,
status: jqXHR.status,
statusText: statusText,
result: result,
headers: jqXHR.getAllResponseHeaders()
}, e.origin);
});
});
</script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE HTML>
<!--
/*
* jQuery Iframe Transport Plugin Redirect Page 2.0.1
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2010, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
-->
<html lang="en">
<head>
<meta charset="utf-8">
<title>jQuery Iframe Transport Plugin Redirect Page</title>
</head>
<body>
<script>
document.body.innerText=document.body.textContent=decodeURIComponent(window.location.search.slice(1));
</script>
</body>
</html>
# The following directives force the content-type application/octet-stream
# and force browsers to display a download dialog for non-image files.
# This prevents the execution of script files in the context of the website:
ForceType application/octet-stream
Header set Content-Disposition attachment
<FilesMatch "(?i)\.(gif|jpe?g|png)$">
ForceType none
Header unset Content-Disposition
</FilesMatch>
# The following directive prevents browsers from MIME-sniffing the content-type.
# This is an important complement to the ForceType directive above:
Header set X-Content-Type-Options nosniff
# Uncomment the following lines to prevent unauthorized download of files:
#AuthName "Authorization required"
#AuthType Basic
#require valid-user
<?php
/*
* jQuery File Upload Plugin PHP Example 5.14
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2010, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/MIT
*/
error_reporting(E_ALL | E_STRICT);
require('UploadHandler.php');
$upload_handler = new UploadHandler();
<?php
$ds = DIRECTORY_SEPARATOR;
$storeFolder = 'uploads';
if (!empty($_FILES)) {
$tempFile = $_FILES['file']['tmp_name'];
$targetPath = dirname( __FILE__ ) . $ds. $storeFolder . $ds;
$targetFile = $targetPath. $_FILES['file']['name'];
move_uploaded_file($tempFile,$targetFile);
}
?>
/*
* YUI Compressor
* Author: Julien Lecomte <jlecomte@yahoo-inc.com>
* Copyright (c) 2007, Yahoo! Inc. All rights reserved.
* Code licensed under the BSD License:
* http://developer.yahoo.net/yui/license.txt
*/
package com.yahoo.platform.yui.compressor;
import jargs.gnu.CmdLineParser;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;
import java.io.*;
import java.nio.charset.Charset;
public class YUICompressor {
public static void main(String args[]) {
CmdLineParser parser = new CmdLineParser();
CmdLineParser.Option typeOpt = parser.addStringOption("type");
CmdLineParser.Option verboseOpt = parser.addBooleanOption('v', "verbose");
CmdLineParser.Option nomungeOpt = parser.addBooleanOption("nomunge");
CmdLineParser.Option linebreakOpt = parser.addStringOption("line-break");
CmdLineParser.Option preserveSemiOpt = parser.addBooleanOption("preserve-semi");
CmdLineParser.Option disableOptimizationsOpt = parser.addBooleanOption("disable-optimizations");
CmdLineParser.Option helpOpt = parser.addBooleanOption('h', "help");
CmdLineParser.Option charsetOpt = parser.addStringOption("charset");
CmdLineParser.Option outputFilenameOpt = parser.addStringOption('o', "output");
Reader in = null;
Writer out = null;
try {
parser.parse(args);
Boolean help = (Boolean) parser.getOptionValue(helpOpt);
if (help != null && help.booleanValue()) {
usage();
System.exit(0);
}
boolean verbose = parser.getOptionValue(verboseOpt) != null;
String charset = (String) parser.getOptionValue(charsetOpt);
if (charset == null || !Charset.isSupported(charset)) {
charset = System.getProperty("file.encoding");
if (charset == null) {
charset = "UTF-8";
}
if (verbose) {
System.err.println("\n[INFO] Using charset " + charset);
}
}
String[] fileArgs = parser.getRemainingArgs();
String type = (String) parser.getOptionValue(typeOpt);
if (fileArgs.length == 0) {
if (type == null || !type.equalsIgnoreCase("js") && !type.equalsIgnoreCase("css")) {
usage();
System.exit(1);
}
in = new InputStreamReader(System.in, charset);
} else {
if (type != null && !type.equalsIgnoreCase("js") && !type.equalsIgnoreCase("css")) {
usage();
System.exit(1);
}
String inputFilename = fileArgs[0];
if (type == null) {
int idx = inputFilename.lastIndexOf('.');
if (idx >= 0 && idx < inputFilename.length() - 1) {
type = inputFilename.substring(idx + 1);
}
}
if (type == null || !type.equalsIgnoreCase("js") && !type.equalsIgnoreCase("css")) {
usage();
System.exit(1);
}
in = new InputStreamReader(new FileInputStream(inputFilename), charset);
}
int linebreakpos = -1;
String linebreakstr = (String) parser.getOptionValue(linebreakOpt);
if (linebreakstr != null) {
try {
linebreakpos = Integer.parseInt(linebreakstr, 10);
} catch (NumberFormatException e) {
usage();
System.exit(1);
}
}
String outputFilename = (String) parser.getOptionValue(outputFilenameOpt);
if (type.equalsIgnoreCase("js")) {
try {
JavaScriptCompressor compressor = new JavaScriptCompressor(in, new ErrorReporter() {
public void warning(String message, String sourceName,
int line, String lineSource, int lineOffset) {
if (line < 0) {
System.err.println("\n[WARNING] " + message);
} else {
System.err.println("\n[WARNING] " + line + ':' + lineOffset + ':' + message);
}
}
public void error(String message, String sourceName,
int line, String lineSource, int lineOffset) {
if (line < 0) {
System.err.println("\n[ERROR] " + message);
} else {
System.err.println("\n[ERROR] " + line + ':' + lineOffset + ':' + message);
}
}
public EvaluatorException runtimeError(String message, String sourceName,
int line, String lineSource, int lineOffset) {
error(message, sourceName, line, lineSource, lineOffset);
return new EvaluatorException(message);
}
});
// Close the input stream first, and then open the output stream,
// in case the output file should override the input file.
in.close(); in = null;
if (outputFilename == null) {
out = new OutputStreamWriter(System.out, charset);
} else {
out = new OutputStreamWriter(new FileOutputStream(outputFilename), charset);
}
boolean munge = parser.getOptionValue(nomungeOpt) == null;
boolean preserveAllSemiColons = parser.getOptionValue(preserveSemiOpt) != null;
boolean disableOptimizations = parser.getOptionValue(disableOptimizationsOpt) != null;
compressor.compress(out, linebreakpos, munge, verbose,
preserveAllSemiColons, disableOptimizations);
} catch (EvaluatorException e) {
e.printStackTrace();
// Return a special error code used specifically by the web front-end.
System.exit(2);
}
} else if (type.equalsIgnoreCase("css")) {
CssCompressor compressor = new CssCompressor(in);
// Close the input stream first, and then open the output stream,
// in case the output file should override the input file.
in.close(); in = null;
if (outputFilename == null) {
out = new OutputStreamWriter(System.out, charset);
} else {
out = new OutputStreamWriter(new FileOutputStream(outputFilename), charset);
}
compressor.compress(out, linebreakpos);
}
} catch (CmdLineParser.OptionException e) {
usage();
System.exit(1);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void usage() {
System.out.println(
"\nUsage: java -jar yuicompressor-x.y.z.jar [options] [input file]\n\n"
+ "Global Options\n"
+ " -h, --help Displays this information\n"
+ " --type <js|css> Specifies the type of the input file\n"
+ " --charset <charset> Read the input file using <charset>\n"
+ " --line-break <column> Insert a line break after the specified column number\n"
+ " -v, --verbose Display informational messages and warnings\n"
+ " -o <file> Place the output into <file>. Defaults to stdout.\n\n"
+ "JavaScript Options\n"
+ " --nomunge Minify only, do not obfuscate\n"
+ " --preserve-semi Preserve all semicolons\n"
+ " --disable-optimizations Disable all micro optimizations\n\n"
+ "If no input file is specified, it defaults to stdin. In this case, the 'type'\n"
+ "option is required. Otherwise, the 'type' option is required only if the input\n"
+ "file extension is neither 'js' nor 'css'.");
}
}
<?xml version="1.0"?>
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.</description>
</book>
<book id="bk104">
<author>Corets, Eva</author>
<title>Oberon's Legacy</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2001-03-10</publish_date>
<description>In post-apocalypse England, the mysterious
agent known only as Oberon helps to create a new life
for the inhabitants of London. Sequel to Maeve
Ascendant.</description>
</book>
<book id="bk105">
<author>Corets, Eva</author>
<title>The Sundered Grail</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2001-09-10</publish_date>
<description>The two daughters of Maeve, half-sisters,
battle one another for control of England. Sequel to
Oberon's Legacy.</description>
</book>
<book id="bk106">
<author>Randall, Cynthia</author>
<title>Lover Birds</title>
<genre>Romance</genre>
<price>4.95</price>
<publish_date>2000-09-02</publish_date>
<description>When Carla meets Paul at an ornithology
conference, tempers fly as feathers get ruffled.</description>
</book>
<book id="bk107">
<author>Thurman, Paula</author>
<title>Splish Splash</title>
<genre>Romance</genre>
<price>4.95</price>
<publish_date>2000-11-02</publish_date>
<description>A deep sea diver finds true love twenty
thousand leagues beneath the sea.</description>
</book>
<book id="bk108">
<author>Knorr, Stefan</author>
<title>Creepy Crawlies</title>
<genre>Horror</genre>
<price>4.95</price>
<publish_date>2000-12-06</publish_date>
<description>An anthology of horror stories about roaches,
centipedes, scorpions and other insects.</description>
</book>
<book id="bk109">
<author>Kress, Peter</author>
<title>Paradox Lost</title>
<genre>Science Fiction</genre>
<price>6.95</price>
<publish_date>2000-11-02</publish_date>
<description>After an inadvertant trip through a Heisenberg
Uncertainty Device, James Salway discovers the problems
of being quantum.</description>
</book>
<book id="bk110">
<author>O'Brien, Tim</author>
<title>Microsoft .NET: The Programming Bible</title>
<genre>Computer</genre>
<price>36.95</price>
<publish_date>2000-12-09</publish_date>
<description>Microsoft's .NET initiative is explored in
detail in this deep programmer's reference.</description>
</book>
<book id="bk111">
<author>O'Brien, Tim</author>
<title>MSXML3: A Comprehensive Guide</title>
<genre>Computer</genre>
<price>36.95</price>
<publish_date>2000-12-01</publish_date>
<description>The Microsoft MSXML3 parser is covered in
detail, with attention to XML DOM interfaces, XSLT processing,
SAX and more.</description>
</book>
<book id="bk112">
<author>Galos, Mike</author>
<title>Visual Studio 7: A Comprehensive Guide</title>
<genre>Computer</genre>
<price>49.95</price>
<publish_date>2001-04-16</publish_date>
<description>Microsoft Visual Studio 7 is explored in depth,
looking at how Visual Basic, Visual C++, C#, and ASP+ are
integrated into a comprehensive development
environment.</description>
</book>
</catalog>
<?php
// set locale to UTF8 to correct multibyte characters
setlocale(LC_ALL, 'en_US.UTF8');
interface elFinderILogger {
public function log($cmd, $ok, $context, $err='', $errorData = array());
}
class elFinder {
/**
* object options
*
* @var array
**/
private $_options = array(
'root' => '', // path to root directory
'URL' => '', // root directory URL
'rootAlias' => 'Home', // display this instead of root directory name
'disabled' => array(), // list of not allowed commands
'dotFiles' => false, // display dot files
'dirSize' => true, // count total directories sizes
'fileMode' => 0666, // new files mode
'dirMode' => 0777, // new folders mode
'mimeDetect' => 'auto', // files mimetypes detection method (finfo, mime_content_type, linux (file -ib), bsd (file -Ib), internal (by extensions))
'uploadAllow' => array(), // mimetypes which allowed to upload
'uploadDeny' => array(), // mimetypes which not allowed to upload
'uploadOrder' => 'deny,allow', // order to proccess uploadAllow and uploadAllow options
'imgLib' => 'auto', // image manipulation library (imagick, mogrify, gd)
'tmbDir' => '.tmb', // directory name for image thumbnails. Set to "" to avoid thumbnails generation
'tmbCleanProb' => 1, // how frequiently clean thumbnails dir (0 - never, 200 - every init request)
'tmbAtOnce' => 5, // number of thumbnails to generate per request
'tmbSize' => 48, // images thumbnails size (px)
'fileURL' => true, // display file URL in "get info"
'dateFormat' => 'j M Y H:i', // file modification date format
'logger' => null, // object logger
'aclObj' => null, // acl object (not implemented yet)
'aclRole' => 'user', // role for acl
'defaults' => array( // default permisions
'read' => true,
'write' => true,
'rm' => true
),
'perms' => array(), // individual folders/files permisions
'debug' => false, // send debug to client
'archiveMimes' => array(), // allowed archive's mimetypes to create. Leave empty for all available types.
'archivers' => array() // info about archivers to use. See example below. Leave empty for auto detect
// 'archivers' => array(
// 'create' => array(
// 'application/x-gzip' => array(
// 'cmd' => 'tar',
// 'argc' => '-czf',
// 'ext' => 'tar.gz'
// )
// ),
// 'extract' => array(
// 'application/x-gzip' => array(
// 'cmd' => 'tar',
// 'argc' => '-xzf',
// 'ext' => 'tar.gz'
// ),
// 'application/x-bzip2' => array(
// 'cmd' => 'tar',
// 'argc' => '-xjf',
// 'ext' => 'tar.bz'
// )
// )
// )
);
/**
* mapping $_GET['cmd]/$_POST['cmd] to class methods
*
* @var array
**/
private $_commands = array(
'open' => '_open',
'reload' => '_reload',
'mkdir' => '_mkdir',
'mkfile' => '_mkfile',
'rename' => '_rename',
'upload' => '_upload',
'paste' => '_paste',
'rm' => '_rm',
'duplicate' => '_duplicate',
'read' => '_fread',
'edit' => '_edit',
'archive' => '_archive',
'extract' => '_extract',
'resize' => '_resize',
'tmb' => '_thumbnails',
'ping' => '_ping'
);
/**
* List of commands to log
*
* @var string
**/
public $_loggedCommands = array('mkdir', 'mkfile', 'rename', 'upload', 'paste', 'rm', 'duplicate', 'edit', 'resize');
/**
* Context to log command
*
* @var string
**/
private $_logContext = array();
/**
* extensions/mimetypes for _mimetypeDetect = 'internal'
*
* @var array
**/
private $_mimeTypes = array(
//applications
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'exe' => 'application/octet-stream',
'doc' => 'application/vnd.ms-word',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'pps' => 'application/vnd.ms-powerpoint',
'pdf' => 'application/pdf',
'xml' => 'application/xml',
'odt' => 'application/vnd.oasis.opendocument.text',
'swf' => 'application/x-shockwave-flash',
// archives
'gz' => 'application/x-gzip',
'tgz' => 'application/x-gzip',
'bz' => 'application/x-bzip2',
'bz2' => 'application/x-bzip2',
'tbz' => 'application/x-bzip2',
'zip' => 'application/zip',
'rar' => 'application/x-rar',
'tar' => 'application/x-tar',
'7z' => 'application/x-7z-compressed',
// texts
'txt' => 'text/plain',
'php' => 'text/x-php',
'html' => 'text/html',
'htm' => 'text/html',
'js' => 'text/javascript',
'css' => 'text/css',
'rtf' => 'text/rtf',
'rtfd' => 'text/rtfd',
'py' => 'text/x-python',
'java' => 'text/x-java-source',
'rb' => 'text/x-ruby',
'sh' => 'text/x-shellscript',
'pl' => 'text/x-perl',
'sql' => 'text/x-sql',
// images
'bmp' => 'image/x-ms-bmp',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'tga' => 'image/x-targa',
'psd' => 'image/vnd.adobe.photoshop',
//audio
'mp3' => 'audio/mpeg',
'mid' => 'audio/midi',
'ogg' => 'audio/ogg',
'mp4a' => 'audio/mp4',
'wav' => 'audio/wav',
'wma' => 'audio/x-ms-wma',
// video
'avi' => 'video/x-msvideo',
'dv' => 'video/x-dv',
'mp4' => 'video/mp4',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mov' => 'video/quicktime',
'wm' => 'video/x-ms-wmv',
'flv' => 'video/x-flv',
'mkv' => 'video/x-matroska'
);
/**
* undocumented class variable
*
* @var string
**/
private $_time = 0;
/**
* Additional data about error
*
* @var array
**/
private $_errorData = array();
/**
* undocumented class variable
*
* @var string
**/
private $_fakeRoot = '';
/**
* Command result to send to client
*
* @var array
**/
private $_result = array();
/**
* undocumented class variable
*
* @var string
**/
private $_today = 0;
/**
* undocumented class variable
*
* @var string
**/
private $_yesterday = 0;
/**
* constructor
*
* @param array object options
* @return void
**/
public function __construct($options=array()) {
foreach ($this->_options as $k=>$v) {
if (isset($options[$k])) {
$this->_options[$k] = is_array($this->_options[$k])
? array_merge($this->_options[$k], $options[$k])
: $options[$k];
}
}
if (substr($this->_options['root'], -1) == DIRECTORY_SEPARATOR) {
$this->_options['root'] = substr($this->_options['root'], 0, -1);
}
$this->_time = $this->_options['debug'] ? $this->_utime() : 0;
$this->_fakeRoot = !$this->_options['rootAlias']
? $this->_options['root']
: dirname($this->_options['root']).DIRECTORY_SEPARATOR.$this->_options['rootAlias'];
if (!empty($this->_options['disabled'])) {
$no = array('open', 'reload', 'tmb', 'ping');
foreach ($this->_options['disabled'] as $k => $c) {
if (!isset($this->_commands[$c]) || in_array($c, $no)) {
unset($this->_options['disabled'][$k]);
} else {
unset($this->_commands[$c]);
}
}
}
if ($this->_options['tmbDir']) {
$tmbDir = $this->_options['root'].DIRECTORY_SEPARATOR.$this->_options['tmbDir'];
$this->_options['tmbDir'] = is_dir($tmbDir) || @mkdir($tmbDir, $this->_options['dirMode']) ? $tmbDir : '';
}
if ($this->_options['tmbDir']) {
if (!in_array($this->_options['imgLib'], array('imagick', 'mogrify', 'gd'))) {
$this->_options['imgLib'] = $this->_getImgLib();
}
}
$this->_today = mktime(0,0,0, date('m'), date('d'), date('Y'));
$this->_yesterday = $this->_today-86400;
}
/**
* Proccess client request and output json
*
* @return void
**/
public function run() {
if (empty($this->_options['root']) || !is_dir($this->_options['root'])) {
exit(json_encode(array('error' => 'Invalid backend configuration')));
}
if (!$this->_isAllowed($this->_options['root'], 'read')) {
exit(json_encode(array('error' => 'Access denied')));
}
$cmd = '';
if (!empty($_POST['cmd'])) {
$cmd = trim($_POST['cmd']);
} elseif (!empty($_GET['cmd'])) {
$cmd = trim($_GET['cmd']);
}
if (!$cmd && $_SERVER["REQUEST_METHOD"] == 'POST') {
header("Content-Type: text/html");
$this->_result['error'] = 'Data exceeds the maximum allowed size';
exit(json_encode($this->_result));
}
if ($cmd && (empty($this->_commands[$cmd]) || !method_exists($this, $this->_commands[$cmd]))) {
exit(json_encode(array('error' => 'Unknown command')));
}
if (isset($_GET['init'])) {
$ts = $this->_utime();
$this->_result['disabled'] = $this->_options['disabled'];
$this->_result['params'] = array(
'dotFiles' => $this->_options['dotFiles'],
'uplMaxSize' => ini_get('upload_max_filesize'),
'archives' => array(),
'extract' => array(),
'url' => $this->_options['fileURL'] ? $this->_options['URL'] : ''
);
if (isset($this->_commands['archive']) || isset($this->_commands['extract'])) {
$this->_checkArchivers();
if (isset($this->_commands['archive'])) {
$this->_result['params']['archives'] = $this->_options['archiveMimes'];
}
if (isset($this->_commands['extract'])) {
$this->_result['params']['extract'] = array_keys($this->_options['archivers']['extract']);
}
}
// clean thumbnails dir
if ($this->_options['tmbDir']) {
srand((double) microtime() * 1000000);
if (rand(1, 200) <= $this->_options['tmbCleanProb']) {
$ts2 = $this->_utime();
$ls = scandir($this->_options['tmbDir']);
for ($i=0, $s = count($ls); $i < $s; $i++) {
if ('.' != $ls[$i] && '..' != $ls[$i]) {
@unlink($this->_options['tmbDir'].DIRECTORY_SEPARATOR.$ls[$i]);
}
}
}
}
}
if ($cmd) {
$this->{$this->_commands[$cmd]}();
} else {
$this->_open();
}
if ($this->_options['debug']) {
$this->_result['debug'] = array(
'time' => $this->_utime() - $this->_time,
'mimeDetect' => $this->_options['mimeDetect'],
'imgLib' => $this->_options['imgLib']
);
if ($this->_options['dirSize']) {
$this->_result['debug']['dirSize'] = true;
$this->_result['debug']['du'] = @$this->_options['du'];
}
}
header("Content-Type: ".($cmd == 'upload' ? 'text/html' : 'application/json'));
header("Connection: close");
echo json_encode($this->_result);
if (!empty($this->_options['logger']) && in_array($cmd, $this->_loggedCommands)) {
$this->_options['logger']->log($cmd, empty($this->_result['error']), $this->_logContext, !empty($this->_result['error']) ? $this->_result['error'] : '', !empty($this->_result['errorData']) ? $this->_result['errorData'] : array());
}
exit();
}
/************************************************************/
/** elFinder commands **/
/************************************************************/
/**
* Return current dir content to client or output file content to browser
*
* @return void
**/
private function _open()
{
if (isset($_GET['current'])) { // read file
if (empty($_GET['current'])
|| empty($_GET['target'])
|| false == ($dir = $this->_findDir(trim($_GET['current'])))
|| false == ($file = $this->_find(trim($_GET['target']), $dir))
|| is_dir($file)
) {
header('HTTP/1.x 404 Not Found');
exit('File not found');
}
if (!$this->_isAllowed($dir, 'read') || !$this->_isAllowed($file, 'read')) {
header('HTTP/1.x 403 Access Denied');
exit('Access denied');
}
if (filetype($file) == 'link') {
$file = $this->_readlink($file);
if (!$file || is_dir($file)) {
header('HTTP/1.x 404 Not Found');
exit('File not found');
}
if (!$this->_isAllowed(dirname($file), 'read') || !$this->_isAllowed($file, 'read')) {
header('HTTP/1.x 403 Access Denied');
exit('Access denied');
}
}
$mime = $this->_mimetype($file);
$parts = explode('/', $mime);
$disp = $parts[0] == 'image' || $parts[0] == 'text' ? 'inline' : 'attachments';
header("Content-Type: ".$mime);
header("Content-Disposition: ".$disp."; filename=".basename($file));
header("Content-Location: ".str_replace($this->_options['root'], '', $file));
header('Content-Transfer-Encoding: binary');
header("Content-Length: ".filesize($file));
header("Connection: close");
readfile($file);
exit();
} else { // enter directory
$path = $this->_options['root'];
if (!empty($_GET['target'])) {
if (false == ($p = $this->_findDir(trim($_GET['target'])))) {
if (!isset($_GET['init'])) {
$this->_result['error'] = 'Invalid parameters';
}
} elseif (!$this->_isAllowed($p, 'read')) {
if (!isset($_GET['init'])) {
$this->_result['error'] = 'Access denied';
}
} else {
$path = $p;
}
}
$this->_content($path, isset($_GET['tree']));
}
}
/**
* Rename file/folder
*
* @return void
**/
private function _rename()
{
if (empty($_GET['current'])
|| empty($_GET['target'])
|| false == ($dir = $this->_findDir(trim($_GET['current'])))
|| false == ($target = $this->_find(trim($_GET['target']), $dir))
) {
$this->_result['error'] = 'File not found';
} elseif (false == ($name = $this->_checkName($_GET['name'])) ) {
$this->_result['error'] = 'Invalid name';
} elseif (!$this->_isAllowed($dir, 'write')) {
$this->_result['error'] = 'Access denied';
} elseif (file_exists($dir.DIRECTORY_SEPARATOR.$name)) {
$this->_result['error'] = 'File or folder with the same name already exists';
} elseif (!rename($target, $dir.DIRECTORY_SEPARATOR.$name)) {
$this->_result['error'] = 'Unable to rename file';
} else {
$this->_rmTmb($target);
$this->_logContext['from'] = $target;
$this->_logContext['to'] = $dir.DIRECTORY_SEPARATOR.$name;
$this->_result['select'] = array($this->_hash($dir.DIRECTORY_SEPARATOR.$name));
$this->_content($dir, is_dir($dir.DIRECTORY_SEPARATOR.$name));
}
}
/**
* Create new folder
*
* @return void
**/
private function _mkdir()
{
if (empty($_GET['current']) || false == ($dir = $this->_findDir(trim($_GET['current'])))) {
return $this->_result['error'] = 'Invalid parameters';
}
$this->_logContext['dir'] = $dir.DIRECTORY_SEPARATOR.$_GET['name'];
if (!$this->_isAllowed($dir, 'write')) {
$this->_result['error'] = 'Access denied';
} elseif (false == ($name = $this->_checkName($_GET['name'])) ) {
$this->_result['error'] = 'Invalid name';
} elseif (file_exists($dir.DIRECTORY_SEPARATOR.$name)) {
$this->_result['error'] = 'File or folder with the same name already exists';
} elseif (!@mkdir($dir.DIRECTORY_SEPARATOR.$name, $this->_options['dirMode'])) {
$this->_result['error'] = 'Unable to create folder';
} else {
$this->_logContext['dir'] = $dir.DIRECTORY_SEPARATOR.$name;
$this->_result['select'] = array($this->_hash($dir.DIRECTORY_SEPARATOR.$name));
$this->_content($dir, true);
}
}
/**
* Create new empty file
*
* @return void
**/
private function _mkfile()
{
if (empty($_GET['current'])
|| false == ($dir = $this->_findDir(trim($_GET['current'])))) {
return $this->_result['error'] = 'Invalid parameters';
}
$this->_logContext['file'] = $dir.DIRECTORY_SEPARATOR.$_GET['name'];
if (!$this->_isAllowed($dir, 'write')) {
$this->_result['error'] = 'Access denied';
} elseif (false == ($name = $this->_checkName($_GET['name'])) ) {
$this->_result['error'] = 'Invalid name';
} elseif (file_exists($dir.DIRECTORY_SEPARATOR.$name)) {
$this->_result['error'] = 'File or folder with the same name already exists';
} else {
$f = $dir.DIRECTORY_SEPARATOR.$name;
$this->_logContext['file'] = $f;
if (false != ($fp = @fopen($f, 'wb'))) {
fwrite($fp, "");
fclose($fp);
$this->_result['select'] = array($this->_hash($dir.DIRECTORY_SEPARATOR.$name));
$this->_content($dir);
} else {
$this->_result['error'] = 'Unable to create file';
}
}
}
/**
* Remove files/folders
*
* @return void
**/
private function _rm()
{
if (empty($_GET['current'])
|| false == ($dir = $this->_findDir(trim($_GET['current'])))
|| (empty($_GET['targets']) || !is_array($_GET['targets']))) {
return $this->_result['error'] = 'Invalid parameters';
}
$this->_logContext['targets'] = array();
foreach ($_GET['targets'] as $hash) {
if (false != ($f = $this->_find($hash, $dir))) {
$this->_remove($f);
$this->_logContext['targets'][] = $f;
}
}
if (!empty($this->_result['errorData'])) {
$this->_result['error'] = 'Unable to remove file';
}
$this->_content($dir, true);
}
/**
* Upload files
*
* @return void
**/
private function _upload()
{
if (empty($_POST['current'])
|| false == ($dir = $this->_findDir(trim($_POST['current'])))) {
return $this->_result['error'] = 'Invalid parameters';
}
if (!$this->_isAllowed($dir, 'write')) {
return $this->_result['error'] = 'Access denied';
}
if (empty($_FILES['upload']))
{
return $this->_result['error'] = 'No file to upload';
}
$this->_logContext['upload'] = array();
$this->_result['select'] = array();
$total = 0;
for ($i=0, $s = count($_FILES['upload']['name']); $i < $s; $i++) {
if (!empty($_FILES['upload']['name'][$i])) {
$total++;
$this->_logContext['upload'][] = $_FILES['upload']['name'][$i];
if ($_FILES['upload']['error'][$i] > 0) {
$error = 'Unable to upload file';
switch ($_FILES['upload']['error'][$i]) {
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$error = 'File exceeds the maximum allowed filesize';
break;
case UPLOAD_ERR_EXTENSION:
$error = 'Not allowed file type';
break;
}
$this->_errorData($_FILES['upload']['name'][$i], $error);
} elseif (false == ($name = $this->_checkName($_FILES['upload']['name'][$i]))) {
$this->_errorData($_FILES['upload']['name'][$i], 'Invalid name');
} elseif (!$this->_isUploadAllow($_FILES['upload']['name'][$i], $_FILES['upload']['tmp_name'][$i])) {
$this->_errorData($_FILES['upload']['name'][$i], 'Not allowed file type');
} else {
$file = $dir.DIRECTORY_SEPARATOR.$_FILES['upload']['name'][$i];
if (!@move_uploaded_file($_FILES['upload']['tmp_name'][$i], $file)) {
$this->_errorData($_FILES['upload']['name'][$i], 'Unable to save uploaded file');
} else {
@chmod($file, $this->_options['fileMode']);
$this->_result['select'][] = $this->_hash($file);
}
}
}
}
$errCnt = !empty($this->_result['errorData']) ? count($this->_result['errorData']) : 0;
if ($errCnt == $total) {
$this->_result['error'] = 'Unable to upload files';
} else {
if ($errCnt>0) {
$this->_result['error'] = 'Some files was not uploaded';
}
$this->_content($dir);
}
}
/**
* Copy/move files/folders
*
* @return void
**/
private function _paste()
{
if (empty($_GET['current'])
|| false == ($current = $this->_findDir(trim($_GET['current'])))
|| empty($_GET['src'])
|| false == ($src = $this->_findDir(trim($_GET['src'])))
|| empty($_GET['dst'])
|| false == ($dst = $this->_findDir(trim($_GET['dst'])))
|| empty($_GET['target']) || !is_array($_GET['target'])
) {
return $this->_result['error'] = 'Invalid parameters';
}
$cut = !empty($_GET['cut']);
$this->_logContext['src'] = array();
$this->_logContext['dest'] = $dst;
$this->_logContext['cut'] = $cut;
if (!$this->_isAllowed($dst, 'write') || !$this->_isAllowed($src, 'read')) {
return $this->_result['error'] = 'Access denied';
}
foreach ($_GET['target'] as $hash) {
if (false == ($f = $this->_find($hash, $src))) {
return $this->_result['error'] = 'File not found' && $this->_content($current, true);
}
$this->_logContext['src'][] = $f;
$_dst = $dst.DIRECTORY_SEPARATOR.basename($f);
if (0 === strpos($dst, $f)) {
return $this->_result['error'] = 'Unable to copy into itself' && $this->_content($current, true);
} elseif (file_exists($_dst)) {
return $this->_result['error'] = 'File or folder with the same name already exists' && $this->_content($current, true);
} elseif ($cut && !$this->_isAllowed($f, 'rm')) {
return $this->_result['error'] = 'Access denied' && $this->_content($current, true);
}
if ($cut) {
if (!@rename($f, $_dst)) {
return $this->_result['error'] = 'Unable to move files' && $this->_content($current, true);
} elseif (!is_dir($f)) {
$this->_rmTmb($f);
}
} elseif (!$this->_copy($f, $_dst)) {
return $this->_result['error'] = 'Unable to copy files' && $this->_content($current, true);
}
}
$this->_content($current, true);
}
/**
* Create file/folder copy with suffix - "copy"
*
* @return void
**/
private function _duplicate()
{
if (empty($_GET['current'])
|| false == ($current = $this->_findDir(trim($_GET['current'])))
|| empty($_GET['target'])
|| false == ($target = $this->_find(trim($_GET['target']), $current))
) {
return $this->_result['error'] = 'Invalid parameters';
}
$this->_logContext['target'] = $target;
if (!$this->_isAllowed($current, 'write') || !$this->_isAllowed($target, 'read')) {
return $this->_result['error'] = 'Access denied';
}
$dup = $this->_uniqueName($target);
if (!$this->_copy($target, $dup)) {
return $this->_result['error'] = 'Unable to create file copy';
}
$this->_result['select'] = array($this->_hash($dup));
$this->_content($current, is_dir($target));
}
/**
* Resize image
*
* @return void
**/
private function _resize()
{
if (empty($_GET['current'])
|| false == ($current = $this->_findDir(trim($_GET['current'])))
|| empty($_GET['target'])
|| false == ($target = $this->_find(trim($_GET['target']), $current))
|| empty($_GET['width']) || 0 >= ($width = intval($_GET['width']))
|| empty($_GET['height']) || 0 >= ($height = intval($_GET['height']))
) {
return $this->_result['error'] = 'Invalid parameters';
}
$this->_logContext = array(
'target' => $target,
'width' => $width,
'height' => $height
);
if (!$this->_isAllowed($target, 'write')) {
return $this->_result['error'] = 'Access denied';
}
if (0 !== strpos($this->_mimetype($target), 'image')) {
return $this->_result['error'] = 'File is not an image';
}
if (!$this->_resizeImg($target, $width, $height)) {
return $this->_result['error'] = 'Unable to resize image';
}
$this->_result['select'] = array($this->_hash($target));
$this->_content($current);
}
/**
* Create images thumbnails
*
* @return void
**/
private function _thumbnails()
{
if (!empty($this->_options['tmbDir']) && !empty($_GET['current']) && false != ($current = $this->_findDir(trim($_GET['current'])))) {
$this->_result['current'] = $this->_hash($current);
$this->_result['images'] = array();
$ls = scandir($current);
$cnt = 0;
$max = $this->_options['tmbAtOnce'] > 0 ? intval($this->_options['tmbAtOnce']) : 5;
for ($i=0; $i < count($ls); $i++) {
if ($this->_isAccepted($ls[$i])) {
$path = $current.DIRECTORY_SEPARATOR.$ls[$i];
if (is_readable($path) && $this->_canCreateTmb($this->_mimetype($path))) {
$tmb = $this->_tmbPath($path);
if (!file_exists($tmb)) {
if ($cnt>=$max) {
return $this->_result['tmb'] = true;
} elseif ($this->_tmb($path, $tmb)) {
$this->_result['images'][$this->_hash($path)] = $this->_path2url($tmb);
$cnt++;
}
}
}
}
}
}
}
/**
* Return file content to client
*
* @return void
**/
private function _fread()
{
if (empty($_GET['current'])
|| false == ($current = $this->_findDir(trim($_GET['current'])))
|| empty($_GET['target'])
|| false == ($target = $this->_find(trim($_GET['target']), $current))
) {
return $this->_result['error'] = 'Invalid parameters';
}
if (!$this->_isAllowed($target, 'read')) {
return $this->_result['error'] = 'Access denied';
}
$this->_result['content'] = @file_get_contents($target);
}
/**
* Save data into text file.
*
* @return void
**/
private function _edit()
{
if (empty($_POST['current'])
|| false == ($current = $this->_findDir(trim($_POST['current'])))
|| empty($_POST['target'])
|| false == ($target = $this->_find(trim($_POST['target']), $current))
|| !isset($_POST['content'])
) {
return $this->_result['error'] = 'Invalid parameters';
}
$this->_logContext['target'] = $target;
if (!$this->_isAllowed($target, 'write')) {
return $this->_result['error'] = 'Access denied';
}
if (false === file_put_contents($target, trim($_POST['content']))) {
return $this->_result['error'] = 'Unable to write to file';
}
$this->_result['target'] = $this->_info($target);
// $this->_result['select'] = array($this->_hash($target));
}
/**
* Create archive of selected type
*
* @return void
**/
private function _archive()
{
$this->_checkArchivers();
if (empty($this->_options['archivers']['create'])
|| empty($_GET['type'])
|| empty($this->_options['archivers']['create'][$_GET['type']])
|| !in_array($_GET['type'], $this->_options['archiveMimes'])) {
return $this->_result['error'] = 'Invalid parameters';
}
if (empty($_GET['current'])
|| empty($_GET['targets'])
|| !is_array($_GET['targets'])
|| false == ($dir = $this->_findDir(trim($_GET['current'])))
) {
return $this->_result['error'] = 'Invalid parameters';
}
$files = array();
$argc = '';
foreach ($_GET['targets'] as $hash) {
if (false == ($f = $this->_find($hash, $dir))) {
return $this->_result['error'] = 'File not found';
}
$files[] = $f;
$argc .= escapeshellarg(basename($f)).' ';
}
$arc = $this->_options['archivers']['create'][$_GET['type']];
$name = count($files) == 1 ? basename($files[0]) : $_GET['name'];
$name = basename($this->_uniqueName($name.'.'.$arc['ext'], ''));
$cwd = getcwd();
chdir($dir);
$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg($name).' '.$argc;
exec($cmd, $o, $c);
chdir($cwd);
if (file_exists($dir.DIRECTORY_SEPARATOR.$name)) {
$this->_content($dir);
$this->_result['select'] = array($this->_hash($dir.DIRECTORY_SEPARATOR.$name));
} else {
$this->_result['error'] = 'Unable to create archive';
}
}
/**
* Extract files from archive
*
* @return void
**/
private function _extract()
{
if (empty($_GET['current'])
|| false == ($current = $this->_findDir(trim($_GET['current'])))
|| empty($_GET['target'])
|| false == ($file = $this->_find(trim($_GET['target']), $current))
) {
return $this->_result['error'] = 'Invalid parameters';
}
$this->_checkArchivers();
$mime = $this->_mimetype($file);
if (empty($this->_options['archivers']['extract'][$mime])) {
return $this->_result['error'] = 'Invalid parameters';
}
$cwd = getcwd();
$arc = $this->_options['archivers']['extract'][$mime];
$cmd = $arc['cmd'].' '.$arc['argc'].' '.escapeshellarg(basename($file));
chdir(dirname($file));
exec($cmd, $o, $c);
chdir($cwd);
if ($c == 0) {
$this->_content($current, true);
} else {
$this->_result['error'] = 'Unable to extract files from archive';
}
}
/**
* Send header Connection: close. Required by safari to fix bug http://www.webmasterworld.com/macintosh_webmaster/3300569.htm
*
* @return void
**/
private function _ping()
{
exit(header("Connection: close"));
}
/************************************************************/
/** "content" methods **/
/************************************************************/
/**
* Set current dir info, content and [dirs tree]
*
* @param string $path current dir path
* @param bool $tree set dirs tree?
* @return void
**/
private function _content($path, $tree=false)
{
$this->_cwd($path);
$this->_cdc($path);
if ($tree) {
$this->_result['tree'] = $this->_tree($this->_options['root']);
}
}
/**
* Set current dir info
*
* @param string $path current dir path
* @return void
**/
private function _cwd($path)
{
$rel = $this->_options['rootAlias'] ? $this->_options['rootAlias'] : basename($this->_options['root']);
if ($path == $this->_options['root']) {
$name = $rel;
} else {
$name = basename($path);
$rel .= DIRECTORY_SEPARATOR.substr($path, strlen($this->_options['root'])+1);
}
$this->_result['cwd'] = array(
'hash' => $this->_hash($path),
'name' => $name,
'mime' => 'directory',
'rel' => $rel,
'size' => 0,
'date' => date($this->_options['dateFormat'], filemtime($path)),
'read' => true,
'write' => $this->_isAllowed($path, 'write'),
'rm' => $path == $this->_options['root'] ? false : $this->_isAllowed($path, 'rm')
);
}
/**
* Set current dir content
*
* @param string $path current dir path
* @return void
**/
private function _cdc($path)
{
$dirs = $files = array();
$ls = scandir($path);
for ($i=0; $i < count($ls); $i++) {
if ($this->_isAccepted($ls[$i])) {
$info = $this->_info($path.DIRECTORY_SEPARATOR.$ls[$i]);
if ($info['mime'] == 'directory') {
$dirs[] = $info;
} else {
$files[] = $info;
}
}
}
$this->_result['cdc'] = array_merge($dirs, $files);
}
/**
* Return file/folder info
*
* @param string $path file path
* @return array
**/
private function _info($path)
{
$type = filetype($path);
$stat = $type == 'link' ? lstat($path) : stat($path);
if ($stat['mtime'] > $this->_today) {
$d = 'Today '.date('H:i', $stat['mtime']);
} elseif ($stat['mtime'] > $this->_yesterday) {
$d = 'Yesterday '.date('H:i', $stat['mtime']);
} else {
$d = date($this->_options['dateFormat'], $stat['mtime']);
}
$info = array(
'name' => htmlspecialchars(basename($path)),
'hash' => $this->_hash($path),
'mime' => $type == 'dir' ? 'directory' : $this->_mimetype($path),
'date' => $d,
'size' => $type == 'dir' ? $this->_dirSize($path) : $stat['size'],
'read' => $this->_isAllowed($path, 'read'),
'write' => $this->_isAllowed($path, 'write'),
'rm' => $this->_isAllowed($path, 'rm'),
);
if ($type == 'link') {
if (false == ($lpath = $this->_readlink($path))) {
$info['mime'] = 'symlink-broken';
return $info;
}
if (is_dir($lpath)) {
$info['mime'] = 'directory';
} else {
$info['parent'] = $this->_hash(dirname($lpath));
$info['mime'] = $this->_mimetype($lpath);
}
$info['link'] = $this->_hash($lpath);
$info['linkTo'] = ($this->_options['rootAlias'] ? $this->_options['rootAlias'] : basename($this->_options['root'])).substr($lpath, strlen($this->_options['root']));
$info['read'] = $this->_isAllowed($lpath, 'read');
$info['write'] = $this->_isAllowed($lpath, 'write');
$info['rm'] = $this->_isAllowed($lpath, 'rm');
} else {
$lpath = '';
}
if ($info['mime'] != 'directory') {
if ($this->_options['fileURL'] && $info['read']) {
$info['url'] = $this->_path2url($lpath ? $lpath : $path);
}
if (0 === ($p = strpos($info['mime'], 'image'))) {
if (false != ($s = getimagesize($path))) {
$info['dim'] = $s[0].'x'.$s[1];
}
if ($info['read']) {
$info['resize'] = isset($info['dim']) && $this->_canCreateTmb($info['mime']);
$tmb = $this->_tmbPath($path);
if (file_exists($tmb)) {
$info['tmb'] = $this->_path2url($tmb);
} elseif ($info['resize']) {
$this->_result['tmb'] = true;
}
}
}
}
return $info;
}
/**
* Return directory tree (multidimensional array)
*
* @param string $path directory path
* @return array
**/
private function _tree($path)
{
$dir = array(
'hash' => $this->_hash($path),
'name' => $path == $this->_options['root'] && $this->_options['rootAlias'] ? $this->_options['rootAlias'] : basename($path),
'read' => $this->_isAllowed($path, 'read'),
'write' => $this->_isAllowed($path, 'write'),
'dirs' => array()
);
if ($dir['read'] && false != ($ls = scandir($path))) {
for ($i=0; $i < count($ls); $i++) {
$p = $path.DIRECTORY_SEPARATOR.$ls[$i];
if ($this->_isAccepted($ls[$i]) && filetype($p) == 'dir') {
$dir['dirs'][] = $this->_tree($p);
}
}
}
return $dir;
}
/************************************************************/
/** fs methods **/
/************************************************************/
/**
* Return name for duplicated file/folder or new archive
*
* @param string $f file/folder name
* @param string $suffix file name suffix
* @return string
**/
private function _uniqueName($f, $suffix=' copy')
{
$dir = dirname($f);
$name = basename($f);
$ext = '';
if (!is_dir($f)) {
if (preg_match('/\.(tar\.gz|tar\.bz|tar\.bz2|[a-z0-9]{1,4})$/i', $name, $m)) {
$ext = '.'.$m[1];
$name = substr($name, 0, strlen($name)-strlen($m[0]));
}
}
if (preg_match('/('.$suffix.')(\d*)$/i', $name, $m)) {
$i = (int)$m[2];
$name = substr($name, 0, strlen($name)-strlen($m[2]));
} else {
$name .= $suffix;
$i = 0;
$n = $dir.DIRECTORY_SEPARATOR.$name.$ext;
if (!file_exists($n)) {
return $n;
}
}
while ($i++ <= 10000) {
$n = $dir.DIRECTORY_SEPARATOR.$name.$i.$ext;
if (!file_exists($n)) {
return $n;
}
}
return $dir.DIRECTORY_SEPARATOR.$name.md5($f).$ext;
}
/**
* Remove file or folder (recursively)
*
* @param string $path fole/folder path
* @return void
**/
private function _remove($path)
{
if (!$this->_isAllowed($path, 'rm')) {
return $this->_errorData($path, 'Access denied');
}
if (!is_dir($path)) {
if (!@unlink($path)) {
$this->_errorData($path, 'Unable to remove file');
} else {
$this->_rmTmb($path);
}
} else {
$ls = scandir($path);
for ($i=0; $i < count($ls); $i++) {
if ('.' != $ls[$i] && '..' != $ls[$i]) {
$this->_remove($path.DIRECTORY_SEPARATOR.$ls[$i]);
}
}
if (!@rmdir($path)) {
return $this->_errorData($path, 'Unable to remove file');
}
}
return true;
}
/**
* Copy file/folder (recursively)
*
* @param string $src file/folder to copy
* @param string $trg destination name
* @return bool
**/
private function _copy($src, $trg)
{
if (!$this->_isAllowed($src, 'read')) {
return $this->_errorData($src, 'Access denied');
}
$dir = dirname($trg);
if (!$this->_isAllowed($dir, 'write')) {
return $this->_errorData($dir, 'Access denied');
}
if (file_exists($trg)) {
return $this->_errorData($src, 'File or folder with the same name already exists');
}
if (!is_dir($src)) {
if (!@copy($src, $trg)) {
return $this->_errorData($src, 'Unable to copy files');
}
@chmod($trg, $this->_options['fileMode']);
} else {
if (!@mkdir($trg, $this->_options['dirMode'])) {
return $this->_errorData($src, 'Unable to copy files');
}
$ls = scandir($src);
for ($i=0; $i < count($ls); $i++) {
if ('.' != $ls[$i] && '..' != $ls[$i]) {
$_src = $src.DIRECTORY_SEPARATOR.$ls[$i];
$_trg = $trg.DIRECTORY_SEPARATOR.$ls[$i];
if (is_dir($_src)) {
if (!$this->_copy($_src, $_trg)) {
return $this->_errorData($_src, 'Unable to copy files');
}
} else {
if (!@copy($_src, $_trg)) {
return $this->_errorData($_src, 'Unable to copy files');
}
@chmod($_trg, $this->_options['fileMode']);
}
}
}
}
return true;
}
/**
* Check new file name for invalid simbols. Return name if valid
*
* @return string $n file name
* @return string
**/
private function _checkName($n)
{
$n = strip_tags(trim($n));
if (!$this->_options['dotFiles'] && '.' == substr($n, 0, 1)) {
return false;
}
return preg_match('|^[^\\/\<\>:]+$|', $n) ? $n : false;
}
/**
* Find folder by hash in required folder and subfolders
*
* @param string $hash folder hash
* @param string $path folder path to search in
* @return string
**/
private function _findDir($hash, $path='')
{
if (!$path) {
$path = $this->_options['root'];
if ($this->_hash($path) == $hash) {
return $path;
}
}
if (false != ($ls = scandir($path))) {
for ($i=0; $i < count($ls); $i++) {
$p = $path.DIRECTORY_SEPARATOR.$ls[$i];
if ($this->_isAccepted($ls[$i]) && is_dir($p)) {
if ($this->_hash($p) == $hash || false != ($p = $this->_findDir($hash, $p))) {
return $p;
}
}
}
}
}
/**
* Find file/folder by hash in required folder
*
* @param string $hash file/folder hash
* @param string $path folder path to search in
**/
private function _find($hash, $path)
{
if (false != ($ls = scandir($path))) {
for ($i=0; $i < count($ls); $i++) {
if ($this->_isAccepted($ls[$i])) {
$p = $path.DIRECTORY_SEPARATOR.$ls[$i];
if ($this->_hash($p) == $hash) {
return $p;
}
}
}
}
}
/**
* Return path of file on which link point to, if exists in root directory
*
* @param string $path symlink path
* @return string
**/
private function _readlink($path)
{
$target = readlink($path);
if ('/' != substr($target, 0, 1)) {
$target = dirname($path).DIRECTORY_SEPARATOR.$target;
}
$target = realpath($target);
$root = realpath($this->_options['root']);
return $target && file_exists($target) && 0 === strpos($target, $root) ? $target : false;
}
/**
* Count total directory size if this allowed in options
*
* @param string $path directory path
* @return int
**/
private function _dirSize($path)
{
$size = 0;
if (!$this->_options['dirSize'] || !$this->_isAllowed($path, 'read')) {
return filesize($path);
}
if (!isset($this->_options['du'])) {
$this->_options['du'] = function_exists('exec')
? exec('du -h '.escapeshellarg(__FILE__), $o, $s) > 0 && $s == 0
: false;
}
if ($this->_options['du']) {
$size = intval(exec('du -k '.escapeshellarg($path)))*1024;
} else {
$ls = scandir($path);
for ($i=0; $i < count($ls); $i++) {
if ($this->_isAccepted($ls[$i])) {
$p = $path.DIRECTORY_SEPARATOR.$ls[$i];
$size += filetype($p) == 'dir' && $this->_isAllowed($p, 'read') ? $this->_dirSize($p) : filesize($p);
}
}
}
return $size;
}
/**
* Return file mimetype
*
* @param string $path file path
* @return string
**/
private function _mimetype($path)
{
if (empty($this->_options['mimeDetect']) || $this->_options['mimeDetect'] == 'auto') {
$this->_options['mimeDetect'] = $this->_getMimeDetect();
}
switch ($this->_options['mimeDetect']) {
case 'finfo':
if (empty($this->_finfo)) {
$this->_finfo = finfo_open(FILEINFO_MIME);
}
$type = @finfo_file($this->_finfo, $path);
break;
case 'php':
$type = mime_content_type($path);
break;
case 'linux':
$type = exec('file -ib '.escapeshellarg($path));
break;
case 'bsd':
$type = exec('file -Ib '.escapeshellarg($path));
break;
default:
$pinfo = pathinfo($path);
$ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
$type = isset($this->_mimeTypes[$ext]) ? $this->_mimeTypes[$ext] : 'unknown;';
}
$type = explode(';', $type);
if ($this->_options['mimeDetect'] != 'internal' && $type[0] == 'application/octet-stream') {
$pinfo = pathinfo($path);
$ext = isset($pinfo['extension']) ? strtolower($pinfo['extension']) : '';
if (!empty($ext) && !empty($this->_mimeTypes[$ext])) {
$type[0] = $this->_mimeTypes[$ext];
}
}
return $type[0];
}
/************************************************************/
/** image manipulation **/
/************************************************************/
/**
* Create image thumbnail
*
* @param string $img image file
* @param string $tmb thumbnail name
* @return bool
**/
private function _tmb($img, $tmb)
{
if (false == ($s = getimagesize($img))) {
return false;
}
$tmbSize = $this->_options['tmbSize'];
switch ($this->_options['imgLib']) {
case 'imagick':
try {
$_img = new imagick($img);
} catch (Exception $e) {
return false;
}
$_img->contrastImage(1);
return $_img->cropThumbnailImage($tmbSize, $tmbSize) && $_img->writeImage($tmb);
break;
case 'mogrify':
if (@copy($img, $tmb)) {
list($x, $y, $size) = $this->_cropPos($s[0], $s[1]);
// exec('mogrify -crop '.$size.'x'.$size.'+'.$x.'+'.$y.' -scale '.$tmbSize.'x'.$tmbSize.'! '.escapeshellarg($tmb), $o, $c);
exec('mogrify -resize '.$tmbSize.'x'.$tmbSize.'^ -gravity center -extent '.$tmbSize.'x'.$tmbSize.' '.escapeshellarg($tmb), $o, $c);
if (file_exists($tmb)) {
return true;
} elseif ($c == 0) {
// find tmb for psd and animated gif
$mime = $this->_mimetype($img);
if ($mime == 'image/vnd.adobe.photoshop' || $mime = 'image/gif') {
$pinfo = pathinfo($tmb);
$test = $pinfo['dirname'].DIRECTORY_SEPARATOR.$pinfo['filename'].'-0.'.$pinfo['extension'];
if (file_exists($test)) {
return rename($test, $tmb);
}
}
}
}
break;
case 'gd':
if ($s['mime'] == 'image/jpeg') {
$_img = imagecreatefromjpeg($img);
} elseif ($s['mime'] == 'image/png') {
$_img = imagecreatefrompng($img);
} elseif ($s['mime'] == 'image/gif') {
$_img = imagecreatefromgif($img);
}
if (!$_img || false == ($_tmb = imagecreatetruecolor($tmbSize, $tmbSize))) {
return false;
}
list($x, $y, $size) = $this->_cropPos($s[0], $s[1]);
if (!imagecopyresampled($_tmb, $_img, 0, 0, $x, $y, $tmbSize, $tmbSize, $size, $size)) {
return false;
}
$r = imagepng($_tmb, $tmb, 7);
imagedestroy($_img);
imagedestroy($_tmb);
return $r;
break;
}
}
/**
* Remove image thumbnail
*
* @param string $img image file
* @return void
**/
private function _rmTmb($img)
{
if ($this->_options['tmbDir'] && false != ($tmb = $this->_tmbPath($img)) && file_exists($tmb)) {
@unlink($tmb);
}
}
/**
* Return x/y coord for crop image thumbnail
*
* @param int $w image width
* @param int $h image height
* @return array
**/
private function _cropPos($w, $h)
{
$x = $y = 0;
$size = min($w, $h);
if ($w > $h) {
$x = ceil(($w - $h)/2);
} else {
$y = ceil(($h - $w)/2);
}
return array($x, $y, $size);
}
/**
* Resize image
*
* @param string $img image path
* @param int $w image width
* @param int $h image height
* @return bool
**/
private function _resizeImg($img, $w, $h)
{
if (false == ($s = getimagesize($img))) {
return false;
}
switch ($this->_options['imgLib']) {
case 'imagick':
if (false != ($_img = new imagick($img))) {
return $_img->cropThumbnailImage($w, $h) && $_img->writeImage($img);
}
break;
case 'mogrify':
exec('mogrify -scale '.$w.'x'.$h.'! '.escapeshellarg($img), $o, $c);
return 0 == $c;
break;
case 'gd':
if ($s['mime'] == 'image/jpeg') {
$_img = imagecreatefromjpeg($img);
} elseif ($s['mime'] = 'image/png') {
$_img = imagecreatefrompng($img);
} elseif ($s['mime'] = 'image/gif') {
$_img = imagecreatefromgif($img);
}
if (!$_img || false == ($_out = imagecreatetruecolor($w, $h))) {
return false;
}
if (!imagecopyresampled($_out, $_img, 0, 0, 0, 0, $w, $h, $s[0], $s[1])) {
return false;
}
if ($s['mime'] == 'image/jpeg') {
$r = imagejpeg($_out, $img, 100);
} else if ($s['mime'] = 'image/png') {
$r = imagepng($_out, $img, 7);
} else {
$r = imagegif($_out, $img, 7);
}
imagedestroy($_img);
imagedestroy($_out);
return $r;
break;
}
}
/**
* Return true if we can create thumbnail for file with this mimetype
*
* @param string $mime file mimetype
* @return bool
**/
private function _canCreateTmb($mime)
{
if ($this->_options['tmbDir'] && $this->_options['imgLib'] && 0 === strpos($mime, 'image')) {
if ('gd' == $this->_options['imgLib']) {
return $mime == 'image/jpeg' || $mime == 'image/png' || $mime == 'image/gif';
}
return true;
}
}
/**
* Return image thumbnail path. For thumbnail return itself
*
* @param string $path image path
* @return string
**/
private function _tmbPath($path)
{
$tmb = '';
if ($this->_options['tmbDir']) {
$tmb = dirname($path) != $this->_options['tmbDir']
? $this->_options['tmbDir'].DIRECTORY_SEPARATOR.$this->_hash($path).'.png'
: $path;
}
return $tmb;
}
/************************************************************/
/** access control **/
/************************************************************/
/**
* Return true if file's mimetype is allowed for upload
*
* @param string $name file name
* @param string $tmpName uploaded file tmp name
* @return bool
**/
private function _isUploadAllow($name, $tmpName)
{
$mime = $this->_mimetype($this->_options['mimeDetect'] != 'internal' ? $tmpName : $name);
$allow = false;
$deny = false;
if (in_array('all', $this->_options['uploadAllow'])) {
$allow = true;
} else {
foreach ($this->_options['uploadAllow'] as $type) {
if (0 === strpos($mime, $type)) {
$allow = true;
break;
}
}
}
if (in_array('all', $this->_options['uploadDeny'])) {
$deny = true;
} else {
foreach ($this->_options['uploadDeny'] as $type) {
if (0 === strpos($mime, $type)) {
$deny = true;
break;
}
}
}
return 0 === strpos($this->_options['uploadOrder'], 'allow') ? $allow && !$deny : $allow || !$deny;
}
/**
* Return true if file name is not . or ..
* If file name begins with . return value according to $this->_options['dotFiles']
*
* @param string $file file name
* @return bool
**/
private function _isAccepted($file)
{
if ('.' == $file || '..' == $file) {
return false;
}
if (!$this->_options['dotFiles'] && '.' == substr($file, 0, 1)) {
return false;
}
return true;
}
/**
* Return true if requeired action allowed to file/folder
*
* @param string $path file/folder path
* @param string $action action name (read/write/rm)
* @return void
**/
private function _isAllowed($path, $action) {
switch ($action) {
case 'read':
if (!is_readable($path)) {
return false;
}
break;
case 'write':
if (!is_writable($path)) {
return false;
}
break;
case 'rm':
if (!is_writable(dirname($path))) {
return false;
}
break;
}
// if ($this->_options['aclObj']) {
//
// }
$path = substr($path, strlen($this->_options['root'])+1);
// echo "$path\n";
foreach ($this->_options['perms'] as $regex => $rules) {
if (preg_match($regex, $path)) {
if (isset($rules[$action])) {
return $rules[$action];
}
}
}
return isset($this->_options['defaults'][$action]) ? $this->_options['defaults'][$action] : false;
}
/************************************************************/
/** utilites **/
/************************************************************/
/**
* Return image manipalation library name
*
* @return string
**/
private function _getImgLib()
{
if (extension_loaded('imagick')) {
return 'imagick';
} elseif (function_exists('exec')) {
exec('mogrify --version', $o, $c);
if ($c == 0) {
return 'mogrify';
}
}
return function_exists('gd_info') ? 'gd' : '';
}
/**
* Return list of available archivers
*
* @return array
**/
private function _checkArchivers()
{
if (!function_exists('exec')) {
$this->_options['archivers'] = $this->_options['archive'] = array();
return;
}
$arcs = array(
'create' => array(),
'extract' => array()
);
exec('tar --version', $o, $ctar);
if ($ctar == 0) {
$arcs['create']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-cf', 'ext' => 'tar');
$arcs['extract']['application/x-tar'] = array('cmd' => 'tar', 'argc' => '-xf', 'ext' => 'tar');
$test = exec('gzip --version', $o, $c);
if ($c == 0) {
$arcs['create']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-czf', 'ext' => 'tgz');
$arcs['extract']['application/x-gzip'] = array('cmd' => 'tar', 'argc' => '-xzf', 'ext' => 'tgz');
}
$test = exec('bzip2 --version', $o, $c);
if ($c == 0) {
$arcs['create']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-cjf', 'ext' => 'tbz');
$arcs['extract']['application/x-bzip2'] = array('cmd' => 'tar', 'argc' => '-xjf', 'ext' => 'tbz');
}
}
exec('zip --version', $o, $c);
if ($c == 0) {
$arcs['create']['application/zip'] = array('cmd' => 'zip', 'argc' => '-r9', 'ext' => 'zip');
}
exec('unzip --help', $o, $c);
if ($c == 0) {
$arcs['extract']['application/zip'] = array('cmd' => 'unzip', 'argc' => '', 'ext' => 'zip');
}
exec('rar --version', $o, $c);
if ($c == 0) {
$arcs['create']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'a inul', 'ext' => 'rar');
$arcs['extract']['application/x-rar'] = array('cmd' => 'rar', 'argc' => 'x', 'ext' => 'rar');
} else {
$test = exec('unrar', $o, $c);
if ($c==0 || $c == 7) {
$arcs['extract']['application/x-rar'] = array('cmd' => 'unrar', 'argc' => 'x', 'ext' => 'rar');
}
}
exec('7za --help', $o, $c);
if ($c == 0) {
$arcs['create']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'a', 'ext' => '7z');
$arcs['extract']['application/x-7z-compressed'] = array('cmd' => '7za', 'argc' => 'e -y', 'ext' => '7z');
if (empty($arcs['create']['application/x-gzip'])) {
$arcs['create']['application/x-gzip'] = array('cmd' => '7za', 'argc' => 'a -tgzip', 'ext' => 'tar.gz');
}
if (empty($arcs['extract']['application/x-gzip'])) {
$arcs['extract']['application/x-gzip'] = array('cmd' => '7za', 'argc' => 'e -tgzip -y', 'ext' => 'tar.gz');
}
if (empty($arcs['create']['application/x-bzip2'])) {
$arcs['create']['application/x-bzip2'] = array('cmd' => '7za', 'argc' => 'a -tbzip2', 'ext' => 'tar.bz');
}
if (empty($arcs['extract']['application/x-bzip2'])) {
$arcs['extract']['application/x-bzip2'] = array('cmd' => '7za', 'argc' => 'a -tbzip2 -y', 'ext' => 'tar.bz');
}
if (empty($arcs['create']['application/zip'])) {
$arcs['create']['application/zip'] = array('cmd' => '7za', 'argc' => 'a -tzip -l', 'ext' => 'zip');
}
if (empty($arcs['extract']['application/zip'])) {
$arcs['extract']['application/zip'] = array('cmd' => '7za', 'argc' => 'e -tzip -y', 'ext' => 'zip');
}
if (empty($arcs['create']['application/x-tar'])) {
$arcs['create']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'a -ttar -l', 'ext' => 'tar');
}
if (empty($arcs['extract']['application/x-tar'])) {
$arcs['extract']['application/x-tar'] = array('cmd' => '7za', 'argc' => 'e -ttar -y', 'ext' => 'tar');
}
}
$this->_options['archivers'] = $arcs;
foreach ($this->_options['archiveMimes'] as $k=>$mime) {
if (!isset($this->_options['archivers']['create'][$mime])) {
unset($this->_options['archiveMimes'][$k]);
}
}
if (empty($this->_options['archiveMimes'])) {
$this->_options['archiveMimes'] = array_keys($this->_options['archivers']['create']);
}
}
/**
* Return mimetype detect method name
*
* @return string
**/
private function _getMimeDetect()
{
if (class_exists('finfo')) {
return 'finfo';
} elseif (function_exists('mime_content_type') && (mime_content_type(__FILE__) == 'text/x-php' || mime_content_type(__FILE__) == 'text/x-c++')) {
return 'mime_content_type';
} elseif (function_exists('exec')) {
$type = exec('file -ib '.escapeshellarg(__FILE__));
if (0 === strpos($type, 'text/x-php') || 0 === strpos($type, 'text/x-c++'))
{
return 'linux';
}
$type = exec('file -Ib '.escapeshellarg(__FILE__));
if (0 === strpos($type, 'text/x-php') || 0 === strpos($type, 'text/x-c++'))
{
return 'bsd';
}
}
return 'internal';
}
/**
* Return file path hash
*
* @param string $path
* @return string
**/
private function _hash($path)
{
return md5($path);
}
/**
* Return file URL
*
* @param string $path
* @return string
**/
private function _path2url($path)
{
$dir = substr(dirname($path), strlen($this->_options['root'])+1);
$file = rawurlencode(basename($path));
return $this->_options['URL'].($dir ? str_replace(DIRECTORY_SEPARATOR, '/', $dir).'/' : '').$file;
}
/**
* Paack error message in $this->_result['errorData']
*
* @param string $path path to file
* @param string $msg error message
* @return bool always false
**/
private function _errorData($path, $msg)
{
$path = preg_replace('|^'.preg_quote($this->_options['root']).'|', $this->_fakeRoot, $path);
if (!isset($this->_result['errorData'])) {
$this->_result['errorData'] = array();
}
$this->_result['errorData'][$path] = $msg;
return false;
}
private function _utime()
{
$time = explode(" ", microtime());
return (double)$time[1] + (double)$time[0];
}
}
?>
\ No newline at end of file
#!/usr/bin/env python
#
# Connector for elFinder File Manager
# author Troex Nevelin <troex@fury.scancode.ru>
import cgi
import hashlib
import json
import mimetypes
import os
import os.path
import re
import shutil
import sys
import time
from datetime import datetime
class connector():
"""Connector for elFinder"""
_options = {
'root': '',
'URL': '',
'rootAlias': 'Home',
'dotFiles': False,
'dirSize': True,
'fileMode': 0644,
'dirMode': 0755,
'imgLib': 'auto',
'tmbDir': '.tmb',
'tmbAtOnce': 5,
'tmbSize': 48,
'fileURL': True,
'uploadMaxSize': 256,
'uploadWriteChunk': 8192,
'uploadAllow': [],
'uploadDeny': [],
'uploadOrder': ['deny', 'allow'],
# 'aclObj': None, # TODO
# 'aclRole': 'user', # TODO
'defaults': {
'read': True,
'write': True,
'rm': True
},
'perms': {},
'archiveMimes': {},
'archivers': {},
'disabled': [],
'debug': False
}
_commands = {
'open': '__open',
'reload': '__reload',
'mkdir': '__mkdir',
'mkfile': '__mkfile',
'rename': '__rename',
'upload': '__upload',
'paste': '__paste',
'rm': '__rm',
'duplicate': '__duplicate',
'read': '__read',
'edit': '__edit',
'extract': '__extract',
'archive': '__archive',
'resize': '__resize',
'tmb': '__thumbnails',
'ping': '__ping'
}
_mimeType = {
# text
'txt': 'text/plain',
'php': 'text/x-php',
'html': 'text/html',
'htm': 'text/html',
'js' : 'text/javascript',
'css': 'text/css',
'rtf': 'text/rtf',
'rtfd': 'text/rtfd',
'py' : 'text/x-python',
'java': 'text/x-java-source',
'rb' : 'text/x-ruby',
'sh' : 'text/x-shellscript',
'pl' : 'text/x-perl',
'sql': 'text/x-sql',
# apps
'doc': 'application/msword',
'ogg': 'application/ogg',
'7z': 'application/x-7z-compressed',
# video
'ogm': 'appllication/ogm',
'mkv': 'video/x-matroska'
}
_time = 0
_request = {}
_response = {}
_errorData = {}
_form = {}
_im = None
_sp = None
_today = 0
_yesterday = 0
def __init__(self, opts):
self._response['debug'] = {}
if self._options['debug']:
self._time = time.time()
t = datetime.fromtimestamp(self._time)
self._today = time.mktime(datetime(t.year, t.month, t.day).timetuple())
self._yesterday = self._today - 86400
import cgitb
cgitb.enable()
for opt in opts:
self._options[opt] = opts.get(opt)
self._options['URL'] = self._options['URL'].rstrip('/')
self._options['root'] = self._options['root'].rstrip(os.sep)
self.__debug('URL', self._options['URL'])
self.__debug('root', self._options['root'])
for cmd in self._options['disabled']:
if cmd in self._commands:
del self._commands[cmd]
if self._options['tmbDir']:
self._options['tmbDir'] = os.path.join(self._options['root'], self._options['tmbDir'])
if not os.path.exists(self._options['tmbDir']):
self._options['tmbDir'] = False
def run(self):
rootOk = True
if not os.path.exists(self._options['root']) or self._options['root'] == '':
rootOk = False
self._response['error'] = 'Invalid backend configuration'
elif not self.__isAllowed(self._options['root'], 'read'):
rootOk = False
self._response['error'] = 'Access denied'
possible_fields = ['cmd', 'target', 'targets[]', 'current', 'tree', 'name',
'content', 'src', 'dst', 'cut', 'init', 'type', 'width', 'height']
self._form = cgi.FieldStorage()
for field in possible_fields:
if field in self._form:
self._request[field] = self._form.getvalue(field)
if rootOk is True:
if 'cmd' in self._request:
if self._request['cmd'] in self._commands:
cmd = self._commands[self._request['cmd']]
func = getattr(self, '_' + self.__class__.__name__ + cmd, None)
if callable(func):
try:
func()
except Exception, e:
self._response['error'] = 'Command Failed'
self.__debug('exception', e)
else:
self._response['error'] = 'Unknown command'
else:
self.__open()
if 'init' in self._request:
self.__checkArchivers()
self._response['disabled'] = self._options['disabled']
if not self._options['fileURL']:
url = ''
else:
url = self._options['URL']
self._response['params'] = {
'dotFiles': self._options['dotFiles'],
'uplMaxSize': str(self._options['uploadMaxSize']) + 'M',
'archives': self._options['archiveMimes'],
'extract': self._options['archivers']['extract'].keys(),
'url': url
}
if self._errorData:
self._response['errorData'] = self._errorData
if self._options['debug']:
self.__debug('time', (time.time() - self._time))
else:
if 'debug' in self._response:
del self._response['debug']
if ('cmd' in self._request and self._request['cmd'] == 'upload') or self._options['debug']:
print 'Content-type: text/html\n'
else:
print 'Content-type: application/json\n'
print json.dumps(self._response, indent = bool(self._options['debug']))
return # sys.exit(0)
def __open(self):
"""Open file or directory"""
# try to open file
if 'current' in self._request:
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
if not curDir or not curFile or os.path.isdir(curFile):
print 'HTTP/1.x 404 Not Found\n\n'
sys.exit('File not found')
if not self.__isAllowed(curDir, 'read') or not self.__isAllowed(curFile, 'read'):
print 'HTTP/1.x 403 Access Denied\n\n'
sys.exit('Access denied')
if os.path.islink(curFile):
curFile = self.__readlink(curFile)
if not curFile or os.path.isdir(curFile):
print 'HTTP/1.x 404 Not Found\n\n'
sys.exit('File not found')
if (
not self.__isAllowed(os.path.dirname(curFile), 'read')
or not self.__isAllowed(curFile, 'read')
):
print 'HTTP/1.x 403 Access Denied\n\n'
sys.exit('Access denied')
mime = self.__mimetype(curFile)
parts = mime.split('/', 2)
if parts[0] == 'image': disp = 'image'
elif parts[0] == 'text': disp = 'inline'
else: disp = 'attachments'
print 'Content-Type: ' + mime
print 'Content-Disposition: ' + disp + '; filename=' + os.path.basename(curFile)
print 'Content-Location: ' + curFile.replace(self._options['root'], '')
print 'Content-Transfer-Encoding: binary'
print 'Content-Length: ' + str(os.lstat(curFile).st_size)
print 'Connection: close\n'
print open(curFile, 'r').read()
sys.exit(0);
# try dir
else:
path = self._options['root']
if 'target' in self._request:
target = self.__findDir(self._request['target'], None)
if not target:
self._response['error'] = 'Invalid parameters'
elif not self.__isAllowed(target, 'read'):
self._response['error'] = 'Access denied'
else:
path = target
self.__content(path, 'tree' in self._request)
pass
def __rename(self):
"""Rename file or dir"""
current = name = target = None
curDir = curName = newName = None
if 'name' in self._request and 'current' in self._request and 'target' in self._request:
name = self._request['name']
current = self._request['current']
target = self._request['target']
curDir = self.__findDir(current, None)
curName = self.__find(target, curDir)
newName = os.path.join(curDir, name)
if not curDir or not curName:
self._response['error'] = 'File not found'
elif not self.__isAllowed(curDir, 'write') and self.__isAllowed(curName, 'rm'):
self._response['error'] = 'Access denied'
elif not self.__checkName(name):
self._response['error'] = 'Invalid name'
elif os.path.exists(newName):
self._response['error'] = 'File or folder with the same name already exists'
else:
self.__rmTmb(curName)
try:
os.rename(curName, newName)
self._response['select'] = [self.__hash(newName)]
self.__content(curDir, os.path.isdir(newName))
except:
self._response['error'] = 'Unable to rename file'
def __mkdir(self):
"""Create new directory"""
current = None
path = None
newDir = None
if 'name' in self._request and 'current' in self._request:
name = self._request['name']
current = self._request['current']
path = self.__findDir(current, None)
newDir = os.path.join(path, name)
if not path:
self._response['error'] = 'Invalid parameters'
elif not self.__isAllowed(path, 'write'):
self._response['error'] = 'Access denied'
elif not self.__checkName(name):
self._response['error'] = 'Invalid name'
elif os.path.exists(newDir):
self._response['error'] = 'File or folder with the same name already exists'
else:
try:
os.mkdir(newDir, int(self._options['dirMode']))
self._response['select'] = [self.__hash(newDir)]
self.__content(path, True)
except:
self._response['error'] = 'Unable to create folder'
def __mkfile(self):
"""Create new file"""
name = current = None
curDir = newFile = None
if 'name' in self._request and 'current' in self._request:
name = self._request['name']
current = self._request['current']
curDir = self.__findDir(current, None)
newFile = os.path.join(curDir, name)
if not curDir or not name:
self._response['error'] = 'Invalid parameters'
elif not self.__isAllowed(curDir, 'write'):
self._response['error'] = 'Access denied'
elif not self.__checkName(name):
self._response['error'] = 'Invalid name'
elif os.path.exists(newFile):
self._response['error'] = 'File or folder with the same name already exists'
else:
try:
open(newFile, 'w').close()
self._response['select'] = [self.__hash(newFile)]
self.__content(curDir, False)
except:
self._response['error'] = 'Unable to create file'
def __rm(self):
current = rmList = None
curDir = rmFile = None
if 'current' in self._request and 'targets[]' in self._request:
current = self._request['current']
rmList = self._request['targets[]']
curDir = self.__findDir(current, None)
if not rmList or not curDir:
self._response['error'] = 'Invalid parameters'
return False
if not isinstance(rmList, list):
rmList = [rmList]
for rm in rmList:
rmFile = self.__find(rm, curDir)
if not rmFile: continue
self.__remove(rmFile)
# TODO if errorData not empty return error
self.__content(curDir, True)
def __upload(self):
try: # Windows needs stdio set for binary mode.
import msvcrt
msvcrt.setmode (0, os.O_BINARY) # stdin = 0
msvcrt.setmode (1, os.O_BINARY) # stdout = 1
except ImportError:
pass
if 'current' in self._request:
curDir = self.__findDir(self._request['current'], None)
if not curDir:
self._response['error'] = 'Invalid parameters'
return
if not self.__isAllowed(curDir, 'write'):
self._response['error'] = 'Access denied'
return
if not 'upload[]' in self._form:
self._response['error'] = 'No file to upload'
return
upFiles = self._form['upload[]']
if not isinstance(upFiles, list):
upFiles = [upFiles]
self._response['select'] = []
total = 0
upSize = 0
maxSize = self._options['uploadMaxSize'] * 1024 * 1024
for up in upFiles:
name = up.filename
if name:
total += 1
name = os.path.basename(name)
if not self.__checkName(name):
self.__errorData(name, 'Invalid name')
else:
name = os.path.join(curDir, name)
try:
f = open(name, 'wb', self._options['uploadWriteChunk'])
for chunk in self.__fbuffer(up.file):
f.write(chunk)
f.close()
upSize += os.lstat(name).st_size
if self.__isUploadAllow(name):
os.chmod(name, self._options['fileMode'])
self._response['select'].append(self.__hash(name))
else:
self.__errorData(name, 'Not allowed file type')
try:
os.unlink(name)
except:
pass
except:
self.__errorData(name, 'Unable to save uploaded file')
if upSize > maxSize:
try:
os.unlink(name)
self.__errorData(name, 'File exceeds the maximum allowed filesize')
except:
pass
# TODO ?
self.__errorData(name, 'File was only partially uploaded')
break
if self._errorData:
if len(self._errorData) == total:
self._response['error'] = 'Unable to upload files'
else:
self._response['error'] = 'Some files was not uploaded'
self.__content(curDir, False)
return
def __paste(self):
if 'current' in self._request and 'src' in self._request and 'dst' in self._request:
curDir = self.__findDir(self._request['current'], None)
src = self.__findDir(self._request['src'], None)
dst = self.__findDir(self._request['dst'], None)
if not curDir or not src or not dst or not 'targets[]' in self._request:
self._response['error'] = 'Invalid parameters'
return
files = self._request['targets[]']
if not isinstance(files, list):
files = [files]
cut = False
if 'cut' in self._request:
if self._request['cut'] == '1':
cut = True
if not self.__isAllowed(src, 'read') or not self.__isAllowed(dst, 'write'):
self._response['error'] = 'Access denied'
return
for fhash in files:
f = self.__find(fhash, src)
if not f:
self._response['error'] = 'File not found'
return
newDst = os.path.join(dst, os.path.basename(f))
if dst.find(f) == 0:
self._response['error'] = 'Unable to copy into itself'
return
if cut:
if not self.__isAllowed(f, 'rm'):
self._response['error'] = 'Move failed'
self._errorData(f, 'Access denied')
self.__content(curDir, True)
return
# TODO thumbs
if os.path.exists(newDst):
self._response['error'] = 'Unable to move files'
self._errorData(f, 'File or folder with the same name already exists')
self.__content(curDir, True)
return
try:
os.rename(f, newDst)
self.__rmTmb(f)
continue
except:
self._response['error'] = 'Unable to move files'
self._errorData(f, 'Unable to move')
self.__content(curDir, True)
return
else:
if not self.__copy(f, newDst):
self._response['error'] = 'Unable to copy files'
self.__content(curDir, True)
return
continue
self.__content(curDir, True)
else:
self._response['error'] = 'Invalid parameters'
return
def __duplicate(self):
if 'current' in self._request and 'target' in self._request:
curDir = self.__findDir(self._request['current'], None)
target = self.__find(self._request['target'], curDir)
if not curDir or not target:
self._response['error'] = 'Invalid parameters'
return
if not self.__isAllowed(target, 'read') or not self.__isAllowed(curDir, 'write'):
self._response['error'] = 'Access denied'
newName = self.__uniqueName(target)
if not self.__copy(target, newName):
self._response['error'] = 'Unable to create file copy'
return
self.__content(curDir, True)
return
def __resize(self):
if not (
'current' in self._request and 'target' in self._request
and 'width' in self._request and 'height' in self._request
):
self._response['error'] = 'Invalid parameters'
return
width = int(self._request['width'])
height = int(self._request['height'])
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
if width < 1 or height < 1 or not curDir or not curFile:
self._response['error'] = 'Invalid parameters'
return
if not self.__isAllowed(curFile, 'write'):
self._response['error'] = 'Access denied'
return
if not self.__mimetype(curFile).find('image') == 0:
self._response['error'] = 'File is not an image'
return
self.__debug('resize ' + curFile, str(width) + ':' + str(height))
self.__initImgLib()
try:
im = self._im.open(curFile)
imResized = im.resize((width, height), self._im.ANTIALIAS)
imResized.save(curFile)
except Exception, e:
self.__debug('resizeFailed_' + path, str(e))
self._response['error'] = 'Unable to resize image'
return
self._response['select'] = [self.__hash(curFile)]
self.__content(curDir, False)
return
def __thumbnails(self):
if 'current' in self._request:
curDir = self.__findDir(self._request['current'], None)
if not curDir or curDir == self._options['tmbDir']:
return False
else:
return False
self.__initImgLib()
if self.__canCreateTmb():
if self._options['tmbAtOnce'] > 0:
tmbMax = self._options['tmbAtOnce']
else:
tmbMax = 5
self._response['current'] = self.__hash(curDir)
self._response['images'] = {}
i = 0
for f in os.listdir(curDir):
path = os.path.join(curDir, f)
fhash = self.__hash(path)
if self.__canCreateTmb(path) and self.__isAllowed(path, 'read'):
tmb = os.path.join(self._options['tmbDir'], fhash + '.png')
if not os.path.exists(tmb):
if self.__tmb(path, tmb):
self._response['images'].update({
fhash: self.__path2url(tmb)
})
i += 1
if i >= tmbMax:
self._response['tmb'] = True
break
else:
return False
return
def __content(self, path, tree):
"""CWD + CDC + maybe(TREE)"""
self.__cwd(path)
self.__cdc(path)
if tree:
self._response['tree'] = self.__tree(self._options['root'])
def __cwd(self, path):
"""Current Working Directory"""
name = os.path.basename(path)
if path == self._options['root']:
name = self._options['rootAlias']
root = True
else:
root = False
if self._options['rootAlias']:
basename = self._options['rootAlias']
else:
basename = os.path.basename(self._options['root'])
rel = basename + path[len(self._options['root']):]
self._response['cwd'] = {
'hash': self.__hash(path),
'name': name,
'mime': 'directory',
'rel': rel,
'size': 0,
'date': datetime.fromtimestamp(os.stat(path).st_mtime).strftime("%d %b %Y %H:%M"),
'read': True,
'write': self.__isAllowed(path, 'write'),
'rm': not root and self.__isAllowed(path, 'rm')
}
def __cdc(self, path):
"""Current Directory Content"""
files = []
dirs = []
for f in sorted(os.listdir(path)):
if not self.__isAccepted(f): continue
pf = os.path.join(path, f)
info = {}
info = self.__info(pf)
info['hash'] = self.__hash(pf)
if info['mime'] == 'directory':
dirs.append(info)
else:
files.append(info)
dirs.extend(files)
self._response['cdc'] = dirs
def __info(self, path):
mime = ''
filetype = 'file'
if os.path.isfile(path): filetype = 'file'
if os.path.isdir(path): filetype = 'dir'
if os.path.islink(path): filetype = 'link'
stat = os.lstat(path)
statDate = datetime.fromtimestamp(stat.st_mtime)
fdate = ''
if stat.st_mtime >= self._today:
fdate = 'Today ' + statDate.strftime("%H:%M")
elif stat.st_mtime >= self._yesterday and stat.st_mtime < self._today:
fdate = 'Yesterday ' + statDate.strftime("%H:%M")
else:
fdate = statDate.strftime("%d %b %Y %H:%M")
info = {
'name': os.path.basename(path),
'hash': self.__hash(path),
'mime': 'directory' if filetype == 'dir' else self.__mimetype(path),
'date': fdate,
'size': self.__dirSize(path) if filetype == 'dir' else stat.st_size,
'read': self.__isAllowed(path, 'read'),
'write': self.__isAllowed(path, 'write'),
'rm': self.__isAllowed(path, 'rm')
}
if filetype == 'link':
lpath = self.__readlink(path)
if not lpath:
info['mime'] = 'symlink-broken'
return info
if os.path.isdir(lpath):
info['mime'] = 'directory'
else:
info['parent'] = self.__hash(os.path.dirname(lpath))
info['mime'] = self.__mimetype(lpath)
if self._options['rootAlias']:
basename = self._options['rootAlias']
else:
basename = os.path.basename(self._options['root'])
info['link'] = self.__hash(lpath)
info['linkTo'] = basename + lpath[len(self._options['root']):]
info['read'] = info['read'] and self.__isAllowed(lpath, 'read')
info['write'] = info['write'] and self.__isAllowed(lpath, 'write')
info['rm'] = self.__isAllowed(lpath, 'rm')
else:
lpath = False
if not info['mime'] == 'directory':
if self._options['fileURL'] and info['read'] is True:
if lpath:
info['url'] = self.__path2url(lpath)
else:
info['url'] = self.__path2url(path)
if info['mime'][0:5] == 'image':
if self.__canCreateTmb():
dim = self.__getImgSize(path)
if dim:
info['dim'] = dim
info['resize'] = True
# if we are in tmb dir, files are thumbs itself
if os.path.dirname(path) == self._options['tmbDir']:
info['tmb'] = self.__path2url(path)
return info
tmb = os.path.join(self._options['tmbDir'], info['hash'] + '.png')
if os.path.exists(tmb):
tmbUrl = self.__path2url(tmb)
info['tmb'] = tmbUrl
else:
self._response['tmb'] = True
return info
def __tree(self, path):
"""Return directory tree starting from path"""
if not os.path.isdir(path): return ''
if os.path.islink(path): return ''
if path == self._options['root'] and self._options['rootAlias']:
name = self._options['rootAlias']
else:
name = os.path.basename(path)
tree = {
'hash': self.__hash(path),
'name': name,
'read': self.__isAllowed(path, 'read'),
'write': self.__isAllowed(path, 'write'),
'dirs': []
}
if self.__isAllowed(path, 'read'):
for d in sorted(os.listdir(path)):
pd = os.path.join(path, d)
if os.path.isdir(pd) and not os.path.islink(pd) and self.__isAccepted(d):
tree['dirs'].append(self.__tree(pd))
return tree
def __uniqueName(self, path, copy = ' copy'):
curDir = os.path.dirname(path)
curName = os.path.basename(path)
lastDot = curName.rfind('.')
ext = newName = ''
if not os.path.isdir(path) and re.search(r'\..{3}\.(gz|bz|bz2)$', curName):
pos = -7
if curName[-1:] == '2':
pos -= 1
ext = curName[pos:]
oldName = curName[0:pos]
newName = oldName + copy
elif os.path.isdir(path) or lastDot <= 0:
oldName = curName
newName = oldName + copy
pass
else:
ext = curName[lastDot:]
oldName = curName[0:lastDot]
newName = oldName + copy
pos = 0
if oldName[-len(copy):] == copy:
newName = oldName
elif re.search(r'' + copy +'\s\d+$', oldName):
pos = oldName.rfind(copy) + len(copy)
newName = oldName[0:pos]
else:
newPath = os.path.join(curDir, newName + ext)
if not os.path.exists(newPath):
return newPath
# if we are here then copy already exists or making copy of copy
# we will make new indexed copy *black magic*
idx = 1
if pos > 0: idx = int(oldName[pos:])
while True:
idx += 1
newNameExt = newName + ' ' + str(idx) + ext
newPath = os.path.join(curDir, newNameExt)
if not os.path.exists(newPath):
return newPath
# if idx >= 1000: break # possible loop
return
def __remove(self, target):
if not self.__isAllowed(target, 'rm'):
self.__errorData(target, 'Access denied')
if not os.path.isdir(target):
try:
os.unlink(target)
return True
except:
self.__errorData(target, 'Remove failed')
return False
else:
for i in os.listdir(target):
if self.__isAccepted(i):
self.__remove(os.path.join(target, i))
try:
os.rmdir(target)
return True
except:
self.__errorData(target, 'Remove failed')
return False
pass
def __copy(self, src, dst):
dstDir = os.path.dirname(dst)
if not self.__isAllowed(src, 'read'):
self.__errorData(src, 'Access denied')
return False
if not self.__isAllowed(dstDir, 'write'):
self.__errorData(dstDir, 'Access denied')
return False
if os.path.exists(dst):
self.__errorData(dst, 'File or folder with the same name already exists')
return False
if not os.path.isdir(src):
try:
shutil.copyfile(src, dst)
shutil.copymode(src, dst)
return True
except:
self.__errorData(src, 'Unable to copy files')
return False
else:
try:
os.mkdir(dst)
shutil.copymode(src, dst)
except:
self.__errorData(src, 'Unable to copy files')
return False
for i in os.listdir(src):
newSrc = os.path.join(src, i)
newDst = os.path.join(dst, i)
if not self.__copy(newSrc, newDst):
self.__errorData(newSrc, 'Unable to copy files')
return False
return True
def __checkName(self, name):
"""Check for valid file/dir name"""
pattern = r'[\/\\\:\<\>]'
if re.search(pattern, name):
return False
return True
def __findDir(self, fhash, path):
"""Find directory by hash"""
fhash = str(fhash)
if not path:
path = self._options['root']
if fhash == self.__hash(path):
return path
if not os.path.isdir(path):
return None
for d in os.listdir(path):
pd = os.path.join(path, d)
if os.path.isdir(pd) and not os.path.islink(pd):
if fhash == self.__hash(pd):
return pd
else:
ret = self.__findDir(fhash, pd)
if ret:
return ret
return None
def __find(self, fhash, parent):
"""Find file/dir by hash"""
fhash = str(fhash)
if os.path.isdir(parent):
for i in os.listdir(parent):
path = os.path.join(parent, i)
if fhash == self.__hash(path):
return path
return None
def __read(self):
if 'current' in self._request and 'target' in self._request:
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
if curDir and curFile:
if self.__isAllowed(curFile, 'read'):
self._response['content'] = open(curFile, 'r').read()
else:
self._response['error'] = 'Access denied'
return
self._response['error'] = 'Invalid parameters'
return
def __edit(self):
error = ''
if 'current' in self._request and 'target' in self._request and 'content' in self._request:
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
error = curFile
if curFile and curDir:
if self.__isAllowed(curFile, 'write'):
try:
f = open(curFile, 'w+')
f.write(self._request['content'])
f.close()
self._response['target'] = self.__info(curFile)
except:
self._response['error'] = 'Unable to write to file'
else:
self._response['error'] = 'Access denied'
return
self._response['error'] = 'Invalid parameters'
return
def __archive(self):
self.__checkArchivers()
if (
not self._options['archivers']['create'] or not 'type' in self._request
or not 'current' in self._request
or not 'targets[]' in self._request
or not 'name' in self._request
):
self._response['error'] = 'Invalid parameters'
return
curDir = self.__findDir(self._request['current'], None)
archiveType = self._request['type']
if (
not archiveType in self._options['archivers']['create']
or not archiveType in self._options['archiveMimes']
or not curDir
):
self._response['error'] = 'Unable to create archive'
return
files = self._request['targets[]']
if not isinstance(files, list):
files = [files]
realFiles = []
for fhash in files:
curFile = self.__find(fhash, curDir)
if not curFile:
self._response['error'] = 'File not found'
return
realFiles.append(os.path.basename(curFile))
arc = self._options['archivers']['create'][archiveType]
if len(realFiles) > 1:
archiveName = self._request['name']
else:
archiveName = realFiles[0]
archiveName += '.' + arc['ext']
archiveName = self.__uniqueName(archiveName, '')
archivePath = os.path.join(curDir, archiveName)
cmd = [arc['cmd']]
for a in arc['argc'].split():
cmd.append(a)
cmd.append(archiveName)
for f in realFiles:
cmd.append(f)
curCwd = os.getcwd()
os.chdir(curDir)
self.__runSubProcess(cmd)
os.chdir(curCwd)
if os.path.exists(archivePath):
self.__content(curDir, False)
self._response['select'] = [self.__hash(archivePath)]
else:
self._response['error'] = 'Unable to create archive'
return
def __extract(self):
if not 'current' in self._request or not 'target' in self._request:
self._response['error'] = 'Invalid parameters'
return
curDir = self.__findDir(self._request['current'], None)
curFile = self.__find(self._request['target'], curDir)
mime = self.__mimetype(curFile)
self.__checkArchivers()
if not mime in self._options['archivers']['extract'] or not curDir or not curFile:
self._response['error'] = 'Invalid parameters'
return
arc = self._options['archivers']['extract'][mime]
cmd = [arc['cmd']]
for a in arc['argc'].split():
cmd.append(a)
cmd.append(curFile)
curCwd = os.getcwd()
os.chdir(curDir)
ret = self.__runSubProcess(cmd)
os.chdir(curCwd)
if ret:
self.__content(curDir, True)
else:
self._response['error'] = 'Unable to extract files from archive'
return
def __ping(self):
"""Workaround for Safari"""
print 'Connection: close\n'
sys.exit(0)
def __mimetype(self, path):
mime = mimetypes.guess_type(path)[0] or 'unknown'
ext = path[path.rfind('.') + 1:]
if mime == 'unknown' and ('.' + ext) in mimetypes.types_map:
mime = mimetypes.types_map['.' + ext]
if mime == 'text/plain' and ext == 'pl':
mime = self._mimeType[ext]
if mime == 'application/vnd.ms-office' and ext == 'doc':
mime = self._mimeType[ext]
if mime == 'unknown':
if os.path.basename(path) in ['README', 'ChangeLog']:
mime = 'text/plain'
else:
if ext in self._mimeType:
mime = self._mimeType[ext]
# self.__debug('mime ' + os.path.basename(path), ext + ' ' + mime)
return mime
def __tmb(self, path, tmb):
try:
im = self._im.open(path).copy()
size = self._options['tmbSize'], self._options['tmbSize']
box = self.__cropTuple(im.size)
if box:
im = im.crop(box)
im.thumbnail(size, self._im.ANTIALIAS)
im.save(tmb, 'PNG')
except Exception, e:
self.__debug('tmbFailed_' + path, str(e))
return False
return True
def __rmTmb(self, path):
tmb = self.__tmbPath(path)
if self._options['tmbDir']:
if os.path.exists(tmb):
try:
os.unlink(tmb)
except:
pass
def __cropTuple(self, size):
w, h = size
if w > h: # landscape
l = int((w - h) / 2)
u = 0
r = l + h
d = h
return (l, u, r, d)
elif h > w: # portrait
l = 0
u = int((h - w) / 2)
r = w
d = u + w
return (l, u, r, d)
else: # cube
pass
return False
def __readlink(self, path):
"""Read link and return real path if not broken"""
target = os.readlink(path);
if not target[0] == '/':
target = os.path.join(os.path.dirname(path), target)
target = os.path.normpath(target)
if os.path.exists(target):
if not target.find(self._options['root']) == -1:
return target
return False
def __dirSize(self, path):
total_size = 0
if self._options['dirSize']:
for dirpath, dirnames, filenames in os.walk(path):
for f in filenames:
fp = os.path.join(dirpath, f)
if os.path.exists(fp):
total_size += os.stat(fp).st_size
else:
total_size = os.lstat(path).st_size
return total_size
def __fbuffer(self, f, chunk_size = _options['uploadWriteChunk']):
while True:
chunk = f.read(chunk_size)
if not chunk: break
yield chunk
def __canCreateTmb(self, path = None):
if self._options['imgLib'] and self._options['tmbDir']:
if path is not None:
mime = self.__mimetype(path)
if not mime[0:5] == 'image':
return False
return True
else:
return False
def __tmbPath(self, path):
tmb = False
if self._options['tmbDir']:
if not os.path.dirname(path) == self._options['tmbDir']:
tmb = os.path.join(self._options['tmbDir'], self.__hash(path) + '.png')
return tmb
def __isUploadAllow(self, name):
allow = False
deny = False
mime = self.__mimetype(name)
if 'all' in self._options['uploadAllow']:
allow = True
else:
for a in self._options['uploadAllow']:
if mime.find(a) == 0:
allow = True
if 'all' in self._options['uploadDeny']:
deny = True
else:
for d in self._options['uploadDeny']:
if mime.find(d) == 0:
deny = True
if self._options['uploadOrder'][0] == 'allow': # ,deny
if deny is True:
return False
elif allow is True:
return True
else:
return False
else: # deny,allow
if allow is True:
return True
elif deny is True:
return False
else:
return True
def __isAccepted(self, target):
if target == '.' or target == '..':
return False
if target[0:1] == '.' and not self._options['dotFiles']:
return False
return True
def __isAllowed(self, path, access):
if not os.path.exists(path):
return False
if access == 'read':
if not os.access(path, os.R_OK):
self.__errorData(path, access)
return False
elif access == 'write':
if not os.access(path, os.W_OK):
self.__errorData(path, access)
return False
elif access == 'rm':
if not os.access(os.path.dirname(path), os.W_OK):
self.__errorData(path, access)
return False
else:
return False
path = path[len(os.path.normpath(self._options['root'])):]
for ppath in self._options['perms']:
regex = r'' + ppath
if re.search(regex, path) and access in self._options['perms'][ppath]:
return self._options['perms'][ppath][access]
return self._options['defaults'][access]
def __hash(self, path):
"""Hash of the path"""
m = hashlib.md5()
m.update(path)
return str(m.hexdigest())
def __path2url(self, path):
curDir = path
length = len(self._options['root'])
url = str(self._options['URL'] + curDir[length:]).replace(os.sep, '/')
try:
import urllib
url = urllib.quote(url, '/:~')
except:
pass
return url
def __errorData(self, path, msg):
"""Collect error/warning messages"""
self._errorData[path] = msg
def __initImgLib(self):
if not self._options['imgLib'] is False and self._im is None:
try:
import Image
Image
self._im = Image
self._options['imgLib'] = 'PIL'
except:
self._options['imgLib'] = False
self._im = False
self.__debug('imgLib', self._options['imgLib'])
return self._options['imgLib']
def __getImgSize(self, path):
self.__initImgLib();
if self.__canCreateTmb():
try:
im = self._im.open(path)
return str(im.size[0]) + 'x' + str(im.size[1])
except:
pass
return False
def __debug(self, k, v):
if self._options['debug']:
self._response['debug'].update({k: v})
return
def __checkArchivers(self):
# import subprocess
# sp = subprocess.Popen(['tar', '--version'], shell = False,
# stdout = subprocess.PIPE, stderr=subprocess.PIPE)
# out, err = sp.communicate()
# print 'out:', out, '\nerr:', err, '\n'
archive = { 'create': {}, 'extract': {} }
c = archive['create']
e = archive['extract']
tar = self.__runSubProcess(['tar', '--version'])
gzip = self.__runSubProcess(['gzip', '--version'])
bzip2 = self.__runSubProcess(['bzip2', '--version'])
zipc = self.__runSubProcess(['zip', '--version'])
unzip = self.__runSubProcess(['unzip', '--help'])
rar = self.__runSubProcess(['rar', '--version'], validReturn = [0, 7])
unrar = self.__runSubProcess(['unrar'], validReturn = [0, 7])
p7z = self.__runSubProcess(['7z', '--help'])
p7za = self.__runSubProcess(['7za', '--help'])
p7zr = self.__runSubProcess(['7zr', '--help'])
# tar = False
tar = gzip = bzip2 = zipc = unzip = rar = unrar = False
# print tar, gzip, bzip2, zipc, unzip, rar, unrar, p7z, p7za, p7zr
if tar:
mime = 'application/x-tar'
c.update({mime: {'cmd': 'tar', 'argc': '-cf', 'ext': 'tar'}})
e.update({mime: {'cmd': 'tar', 'argc': '-xf', 'ext': 'tar'}})
if tar and gzip:
mime = 'application/x-gzip'
c.update({mime: {'cmd': 'tar', 'argc': '-czf', 'ext': 'tar.gz'}})
e.update({mime: {'cmd': 'tar', 'argc': '-xzf', 'ext': 'tar.gz'}})
if tar and bzip2:
mime = 'application/x-bzip2'
c.update({mime: {'cmd': 'tar', 'argc': '-cjf', 'ext': 'tar.bz2'}})
e.update({mime: {'cmd': 'tar', 'argc': '-xjf', 'ext': 'tar.bz2'}})
mime = 'application/zip'
if zipc:
c.update({mime: {'cmd': 'zip', 'argc': '-r9', 'ext': 'zip'}})
if unzip:
e.update({mime: {'cmd': 'unzip', 'argc': '', 'ext': 'zip'}})
mime = 'application/x-rar'
if rar:
c.update({mime: {'cmd': 'rar', 'argc': 'a', 'ext': 'rar'}})
e.update({mime: {'cmd': 'rar', 'argc': 'x', 'ext': 'rar'}})
elif unrar:
e.update({mime: {'cmd': 'unrar', 'argc': 'x', 'ext': 'rar'}})
p7zip = None
if p7z:
p7zip = '7z'
elif p7za:
p7zip = '7za'
elif p7zr:
p7zip = '7zr'
if p7zip:
mime = 'application/x-7z-compressed'
c.update({mime: {'cmd': p7zip, 'argc': 'a -t7z', 'ext': '7z'}})
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': '7z'}})
mime = 'application/x-tar'
if not mime in c:
c.update({mime: {'cmd': p7zip, 'argc': 'a -ttar', 'ext': 'tar'}})
if not mime in e:
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': 'tar'}})
mime = 'application/x-gzip'
if not mime in c:
c.update({mime: {'cmd': p7zip, 'argc': 'a -tgzip', 'ext': 'gz'}})
if not mime in e:
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': 'tar.gz'}})
mime = 'application/x-bzip2'
if not mime in c:
c.update({mime: {'cmd': p7zip, 'argc': 'a -tbzip2', 'ext': 'bz2'}})
if not mime in e:
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': 'tar.bz2'}})
mime = 'application/zip'
if not mime in c:
c.update({mime: {'cmd': p7zip, 'argc': 'a -tzip', 'ext': 'zip'}})
if not mime in e:
e.update({mime: {'cmd': p7zip, 'argc': 'e -y', 'ext': 'zip'}})
if not self._options['archiveMimes']:
self._options['archiveMimes'] = c.keys()
else:
pass
self._options['archivers'] = archive
pass
def __runSubProcess(self, cmd, validReturn = [0]):
if self._sp is None:
import subprocess
self._sp = subprocess
try:
sp = self._sp.Popen(cmd, shell = False, stdout = self._sp.PIPE, stderr = self._sp.PIPE)
out, err = sp.communicate()
ret = sp.returncode
# print cmd, ret, out, err
except:
return False
if not ret in validReturn:
return False
return True
/* file manager window */
.el-finder {
width:100%;
min-width:400px;
border:1px solid #ccc;
background-color:#eee;
font:12px trebuchet ms,lucida grande,verdana,sans-serif;
border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px;
}
.el-finder-undocked {
position:absolute;
min-width:400px;
border:1px solid #ccc;
padding:5px;
}
/* error messages */
.el-finder-err {
padding: 15px;
text-align:center;
background: #fee;
color: #cc0509;
border: 2px #844 solid;
border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px;
}
/* disabled */
.el-finder .noaccess,
.el-finder-disabled .el-finder-toolbar li,
.el-finder-disabled .el-finder-nav,
.el-finder-disabled .el-finder-cwd {
opacity:0.35; filter:Alpha(Opacity=35);
}
.el-finder .el-finder-droppable {
background-color:#99ccff;
}
.el-finder .ui-selected {
background-color:#ccc;
}
/************************************/
/* toolbar */
/************************************/
.el-finder-toolbar ul {
padding:5px 7px;
margin:0;
list-style:none;
}
.el-finder-toolbar ul li {
display: -moz-inline-stack;
display: inline-block;
zoom: 1;
*display: inline;
vertical-align: top;
height:22px;
width:23px;
margin:0 2px;
background:url('../images/toolbar.png') no-repeat;
border:1px solid #ccc;
border-radius:3px;
-moz-border-radius:3px;
-webkit-border-radius:3px;
}
.el-finder-toolbar ul li.delim {
border:none;
width:3px;
background-position: 1px -610px;
}
.el-finder-toolbar ul li.el-finder-tb-hover {
border:1px solid #fff;
background-color:#ccc;
}
.el-finder-toolbar ul li.disabled { opacity:0.35; filter:Alpha(Opacity=35); }
.el-finder-toolbar ul li.back { background-position: 3px -171px; }
.el-finder-toolbar ul li.reload { background-position: 3px -192px; }
.el-finder-toolbar ul li.select { background-position: 3px -214px; }
.el-finder-toolbar ul li.open { background-position: 4px -235px; }
.el-finder-toolbar ul li.mkdir { background-position: 4px -258px; }
.el-finder-toolbar ul li.mkfile { background-position: 4px -280px; }
.el-finder-toolbar ul li.upload { background-position: 3px -305px; }
.el-finder-toolbar ul li.rm { background-position: 3px -330px; }
.el-finder-toolbar ul li.copy { background-position: 3px -356px; }
.el-finder-toolbar ul li.paste { background-position: 3px -381px; }
.el-finder-toolbar ul li.rename { background-position: 3px -407px; }
.el-finder-toolbar ul li.edit { background-position: 4px -435px; }
.el-finder-toolbar ul li.info { background-position: 3px -462px; }
.el-finder-toolbar ul li.help { background-position: 3px -487px; }
.el-finder-toolbar ul li.icons { background-position: 3px -537px; }
.el-finder-toolbar ul li.list { background-position: 3px -557px; }
.el-finder-toolbar ul li.uncompress { background-position: 3px -583px; }
.el-finder-toolbar ul li.resize { background-position: 3px -647px; }
.el-finder-toolbar ul li.quicklook { background-position: 3px -726px; }
.el-finder-dock-button {
width:19px;
height:19px;
float:right;
margin: 2px;
border:1px solid #ccc;
border-radius:3px;
-moz-border-radius:3px;
-webkit-border-radius:3px;
background:url('../images/toolbar.png') 2px -705px no-repeat;
}
.ui-dialog .el-finder-dock-button {
background-position:2px -681px;
}
.el-finder-dock-button-hover {
background-color:#ccc;
border:1px solid #fff;
}
/**********************************************************/
/* workzone, container for navigation and current folder */
/**********************************************************/
.el-finder-workzone {
background-color:#fff;
border-top:1px solid #ccc;
border-bottom:1px solid #ccc;
position:relative;
}
.el-finder-spinner {
position:absolute;
top:37%;
left:37%;
width:250px;
height:50px;
background:#fff url(../images/spinner.gif) 50% 50% no-repeat;
display:none;
}
/* error in workzone */
.el-finder-workzone p.el-finder-err {
display:none;
position:absolute;
left:37%;
top:20px;
}
/* navigation and current directory */
.el-finder-nav, .el-finder-cwd {
height:350px;
overflow:auto;
padding:3px 1px;
}
/************************************/
/* navigation */
/************************************/
.el-finder-nav {
float:left;
width : 200px;
}
.el-finder-nav .ui-resizable-e {
right:0;
}
/* folders tree */
.el-finder-nav ul {
list-style:none;
margin:0;
padding:0;
}
.el-finder-nav ul li {
clear:both;
}
ul.el-finder-tree, ul.el-finder-places {
margin-bottom:1em;
}
.el-finder-nav ul li ul {
margin-left:12px;
}
.el-finder-nav ul div {
width:12px;
height:20px;
float:left;
margin-right:23px;
}
.el-finder-nav a, .el-finder-nav div.collapsed {
background-image:url(../images/toolbar.png);
background-repeat:no-repeat;
}
.el-finder-nav div.collapsed {
background-position: -1px 7px;
}
.el-finder-nav div.expanded {
background-position: -1px -9px;
}
.el-finder-nav a {
display: block;
white-space:nowrap;
line-height:20px;
color:#444;
cursor:default;
text-decoration:none;
outline:none;
border-radius:3px;
-moz-border-radius:3px;
-webkit-border-radius:3px;
background-position: 15px -56px;
}
.el-finder-nav a.dropbox {
background-position: 15px -80px;
}
.el-finder-nav a.readonly {
background-position: 15px -104px;
}
.el-finder-nav a.selected {
background-color:#ccc;
background-position: 15px -128px;
}
.el-finder-nav a.el-finder-tree-root {
background-position: 15px -30px;
font-weight:bold;
}
.el-finder-nav a.el-finder-places-root {
background-position: 15px -152px;
font-weight:bold;
}
.el-finder-nav ul.el-finder-tree .el-finder-droppable {
background-position: 15px -237px;
}
/***********************************/
/* current working directory */
/************************************/
.el-finder-cwd {
border-left:1px solid #ccc;
padding:3px;
}
/********** view: icons ************/
.el-finder-cwd div {
width: 81px;
display: -moz-inline-stack;
display: inline-block;
vertical-align: top;
zoom: 1;
*display: inline;
margin:0 3px 3px 0;
padding:1px 0;
text-align:center;
border-radius:5px;
-moz-border-radius:5px;
-webkit-border-radius:5px;
}
.el-finder-cwd p,
.el-finder-ql p {
width:48px;
height:48px;
margin:1px auto;
padding:0;
border-radius:5px;
-moz-border-radius:5px;
-webkit-border-radius:5px;
background: url('../images/icons-big.png') -1px 1px no-repeat;
}
/* mimetypes */
.directory p { background-position: 0 -45px; }
.application p { background-position: -1px -134px; }
.audio p { background-position: -1px -1082px; }
.image p { background-position: -1px -1132px; }
.text p,
.x-empty p { background-position: -1px -524px; }
.video p { background-position: -1px -1032px; }
/* images */
.gif p { background-position: 0 -1183px; }
.jpeg p { background-position: 0 -1234px; }
.vnd-adobe-photoshop p,
.postscript p { background-position: 0 -1132px; }
/* video */
.ogg p { background-position: -1px -1032px; }
/* audio */
.ogm p { background-position: -1px -1082px; }
/* texts */
.rtf p,
.rtfd p { background-position: 0 -573px; }
.javascript p,
.x-javascript p { background-position: 0 -184px; }
.x-c p,
.css p,
.x-sql p,
.x-python p,
.x-java-source p { background-position: 0 -624px; }
.html p { background-position: 0 -777px; }
.x-java p { background-position: 0 -827px; }
.x-php p { background-position: 0 -726px; }
.x-perl p { background-position: 0 -675px; }
.x-ruby p { background-position: 0 -877px; }
.x-shellscript p { background-position: 0 -930px; }
/* applications */
.vnd-ms-office p,
.msword p,
.vnd-ms-word p,
.vnd-oasis-opendocument-text p { background-position: -1px -232px; }
.ms-excel p,
.vnd-ms-excel p,
.vnd-oasis-opendocument-spreadsheet p { background-position: -1px -330px; }
.vnd-ms-powerpoint p,
.vnd-oasis-opendocument-presentation p { background-position: -1px -380px; }
.pdf p { background-position: -1px -282px; }
.xml p { background-position: 0 -980px; }
.x-shockwave-flash p { background-position: 0 -1032px; }
.x-sh p { background-position: 0 -930px; }
/* archives */
.x-bzip p,
.x-bzip2 p,
.zip p,
.x-rar p,
.x-rar-compressed p,
.x-tar p,
.x-7z-compressed p { background-position: -1px -430px; }
.x-gzip p { background-position: -1px -477px; }
.el-finder-cwd div.el-finder-droppable p {
background-position: 0 -87px;
}
.el-finder-cwd label {
display:block;
font-size:11px;
line-height:13px;
padding:0 1px;
margin:0;
height:25px;
overflow:hidden;
cursor:default;
}
.el-finder-cwd div input {
background:#fff;
width:81px;
margin-left:-2px;
outline:none;
border:1px solid #ccc;
text-align:center;
}
.el-finder-cwd div em {
float:left;
margin-top:-40px;
margin-left:9px;
width:15px;
height:16px;
background:url(../images/icons-big.png) -17px -1290px no-repeat;
}
.el-finder-cwd div em.dropbox {
float:right;
margin-right:9px;
background-position: 0px -1290px;
}
.el-finder-cwd div em.noread {
float:right;
margin-right:9px;
background-position: 0px -1415px;
}
.el-finder-cwd div em.readonly {
float:right;
margin-right:9px;
background-position: -34px -1287px;
}
/********** view: list ************/
.el-finder-cwd table {
width:100%;
*width:99%;
border-collapse: collapse;
border-spacing: 0;
border:1px solid #ccc;
margin:-3px -3px;
}
.el-finder-row-odd {
background-color:#eee;
}
.el-finder-cwd table th,
.el-finder-cwd table td {
padding:3px 5px;
border-left:1px solid #ccc;
cursor:default;
white-space:nowrap;
}
.el-finder-cwd table th {
text-align:left;
background:#fbf9ee;
font-size:.86em;
}
.el-finder-cwd table td.icon {
width:24px;
}
.el-finder-cwd table p {
width:24px;
height:16px;
margin:0;
padding:0;
background:url(../images/icons-small.png) 4px 0 no-repeat;
}
.el-finder-cwd table .size {
text-align:right;
}
tr.directory p { background-position:4px -16px; }
tr.text p { background-position:5px -34px; }
tr.image p { background-position:4px -51px; }
tr.audio p { background-position:4px -70px; }
tr.video p { background-position:5px -89px; }
tr.application p { background-position:4px -108px; }
/* text */
tr.html p { background-position:5px -188px; }
tr.javascript p,
tr.x-javascript p,
tr.css p,
tr.x-sql p,
tr.xml p,
tr.x-python p,
tr.x-java-source p,
tr.x-perl p,
tr.x-ruby p { background-position:5px -228px; }
tr.x-php p { background-position:5px -247px; }
tr.x-c p { background-position:5px -208px; }
tr.x-shellscript p,
tr.x-sh p { background-position:5px -168px; }
tr.rtf p, tr.rtfd p { background-position:5px -148px; }
/* application */
tr.x-shockwave-flash p { background-position:4px -266px; }
tr.pdf p { background-position:4px -285px; }
tr.vnd-ms-office p { background-position:4px -325px; }
tr.msword p,
tr.vnd-oasis-opendocument-text p,
tr.vnd-ms-word p { background-position:4px -346px; }
tr.vnd-ms-excel p,
tr.ms-excel p,
tr.vnd-oasis-opendocument-spreadsheet { background-position:4px -365px; }
tr.vnd-ms-powerpoint p,
tr.vnd-oasis-opendocument-presentation { background-position:4px -385px; }
/* archives */
tr.x-tar p,
tr.x-gzip p,
tr.x-bzip p,
tr.x-bzip2 p,
tr.zip p,
tr.x-rar p,
tr.x-rar-compressed p,
tr.x-7z-compressed p { background-position:4px -305px; }
tr.el-finder-droppable td.icon p { background-position:5px -450px; }
.el-finder-cwd table td p em {
float:left;
width:10px;
height:12px;
margin-top:5px;
background:url(../images/icons-small.png) 0px -405px no-repeat;
}
.el-finder-cwd table p em.readonly { background-position:0px -433px; }
.el-finder-cwd table p em.dropbox { background-position:0px -418px; }
.el-finder-cwd table p em.noread { background-position:0px -470px; }
/************************************/
/* statusbar */
/************************************/
.el-finder-statusbar {
height:20px;
}
.el-finder-stat,
.el-finder-path,
.el-finder-sel {
padding:3px 9px 1px 9px;
font-size:11px;
color:#555;
}
/* current directory path */
.el-finder-path {
float:left;
}
/* number folders/files in current directory and size */
.el-finder-stat {
float:right;
}
/* info about selected files */
.el-finder-sel {
text-align:center;
}
/************************************/
/* dialog window */
/************************************/
.el-finder-dialog {
font-size:.84em;
}
.el-finder-dialog form p, .el-finder-dialog .ui-tabs p {
margin:.5em;
}
.el-finder-dialog .ui-dialog-titlebar {
padding: .2em .1em .1em .8em;
}
.el-finder-dialog .ui-dialog-buttonpane {
padding: .1em 1em .1em .4em;
font-size:.9em;
}
.el-finder-dialog .ui-dialog-content {
padding:5px;
}
.el-finder-dialog hr {
border:0;
border-bottom: 1px #ccc solid;
clear:both
}
.el-finder-dialog ul {
margin-top:0;
}
.el-finder-dialog kbd { font-size:1.2em;}
.el-finder-dialog a { outline: none;}
.el-finder-dialog textarea {
width:100%;
height:400px;
outline:none;
border:1px solid #ccc;
}
.ui-state-error {
margin: 5px 0;
padding:.5em;
clear:both;
}
.el-finder-dialog .ui-state-error .ui-icon {
float: left;
margin-right: .3em;
}
.el-finder-add-field {
cursor:pointer;
}
.el-finder-add-field span {
float:left;
margin-right:.7em;
}
.el-finder-dialog table {
width : 100%;
}
.el-finder-dialog table td {
padding:2px 5px;
}
.el-finder-dialog .ui-tabs {
font-size:.98em;
}
.el-finder-dialog .ui-tabs div {
padding:0 .5em;
}
.el-finder-dialog .ui-tabs-nav li a {
padding:.2em 1em;
}
/************************************/
/* contextmenu */
/************************************/
.el-finder-contextmenu {
position:absolute;
width:200px;
background:#fff;
cursor:default;
border:1px solid #ccc;
border-radius:5px;
-moz-border-radius:5px;
-webkit-border-radius:5px;
padding:5px 0;
}
.el-finder-contextmenu div {
position:relative;
display:block;
margin:0;
padding:4px 29px 4px 29px;
white-space:nowrap;
font:12px trebuchet ms,lucida grande,verdana,sans-serif;
background:url('../images/toolbar.png') 0 0 no-repeat;
}
.el-finder-contextmenu span {
float:right;
width:9px;
height:18px;
margin-right:-27px;
background:url(../images/toolbar.png) -4px 5px no-repeat;
}
.el-finder-contextmenu div.el-finder-contextmenu-sub {
position:absolute;
top:0;
display:none;
margin:0;
padding:5px 0;
background:#fff;
border:1px solid #ccc;
border-radius:5px;
-moz-border-radius:5px;
-webkit-border-radius:5px;
}
.el-finder-contextmenu div.reload { background-position: 5px -192px; }
.el-finder-contextmenu div.select { background-position: 5px -214px; }
.el-finder-contextmenu div.open { background-position: 6px -235px; }
.el-finder-contextmenu div.mkdir { background-position: 6px -258px; }
.el-finder-contextmenu div.mkfile { background-position: 6px -280px; }
.el-finder-contextmenu div.upload { background-position: 5px -305px; }
.el-finder-contextmenu div.rm { background-position: 5px -330px; }
.el-finder-contextmenu div.copy { background-position: 5px -356px; }
.el-finder-contextmenu div.cut { background-position: 5px -631px; }
.el-finder-contextmenu div.duplicate { background-position: 5px -356px; }
.el-finder-contextmenu div.paste { background-position: 5px -381px; }
.el-finder-contextmenu div.rename { background-position: 5px -407px; }
.el-finder-contextmenu div.edit { background-position: 6px -435px; }
.el-finder-contextmenu div.info { background-position: 5px -462px; }
.el-finder-contextmenu div.help { background-position: 5px -487px; }
.el-finder-contextmenu div.icons { background-position: 5px -537px; }
.el-finder-contextmenu div.list { background-position: 5px -557px; }
.el-finder-contextmenu div.archive { background-position: 5px -583px; }
.el-finder-contextmenu div.extract { background-position: 5px -583px; }
.el-finder-contextmenu div.resize { background-position: 5px -655px; }
.el-finder-contextmenu div.quicklook { background-position: 5px -727px; }
.el-finder-contextmenu div.delim {
margin:0;
padding:0;
height:1px;
border-top:1px solid #eee;
background:transparent;
display:block;
}
.el-finder-contextmenu div.hover { background-color:#99ccff; }
.el-finder-places {
margin-top:.5em;
}
.el-finder-drag-helper {
padding:0;
cursor:move;
zoom:1;
}
.el-finder-drag-helper div {
margin-left:-57px;
}
.el-finder-drag-copy {
background:url('../images/toolbar.png') 0 -751px no-repeat;
}
.el-finder-drag-helper label {
border:1px solid #ccc;
background-color:#eee;
border-radius:5px;
-moz-border-radius:5px;
-webkit-border-radius:5px;
}
/************************************/
/* QuickLook */
/************************************/
.el-finder-ql {
position:absolute;
width:420px;
height:auto;
padding:12px 9px;
text-align:center;
border-radius:9px;
-moz-border-radius:9px;
-webkit-border-radius:9px;
background:url(../images/ql.png);
overflow: inherit !important;
}
/* toolbar */
.el-finder-ql div.el-finder-ql-drag-handle {
height:18px;
font-size:14px;
background-color:#777;
margin:-12px -9px 12px -9px;
padding:3px 0 0 19px;
opacity:.8;
text-align:center;
white-space: nowrap;
overflow:hidden;
-moz-border-radius-topleft:9px;
-moz-border-radius-topright:9px;
-webkit-border-top-left-radius: 9px;
-webkit-border-top-right-radius: 9px;
border-top-left-radius: 9px;
border-top-right-radius: 9px;
}
/* close button */
.el-finder-ql div.el-finder-ql-drag-handle span {
float:left;
margin:0 19px 0 -15px;
}
/* title in tolbar */
.el-finder-ql div.el-finder-ql-drag-handle strong {
line-height:18px;
margin-left:-17px;
color:#fff;
}
.el-finder-ql div.el-finder-ql-media {
width:100%;
padding:0;
}
.el-finder-ql div.el-finder-ql-content {
width:100%;
font:.82em/1.3em trebuchet ms,lucida grande,verdana,sans-serif;
padding:5px 0;
overflow:hidden;
}
.el-finder-ql div.el-finder-ql-content span,
.el-finder-ql div.el-finder-ql-content a {
display:block;
color: #fff;
}
/* text files preview */
.el-finder-ql iframe {
background:#fff;
width:100%;
height:315px;
padding:0;
margin:0;
border:none;
outline:none;
}
/* images preview */
.el-finder-ql img {
margin:0 auto;
border:1px solid #fff;
}
/* button help */
.el-finder-help-std {
background: url(../images/icons-big.png) 0 -1315px no-repeat;
width:48px;
height:48px;
float:right;
}
.el-finder-logo {
background: url(../images/icons-big.png) 0 -1365px no-repeat;
width:48px;
height:48px;
float:left;
}
(function($) {
/**
* @class File manager (main controller)
* @author dio dio@std42.ru
**/
elFinder = function(el, o) {
var self = this, id;
this.log = function(m) {
window.console && window.console.log && window.console.log(m);
}
/**
* Object. File manager configuration
**/
this.options = $.extend({}, this.options, o||{});
if (!this.options.url) {
alert('Invalid configuration! You have to set URL option.');
return;
}
/**
* String. element id, create random if not set;
**/
this.id = '';
if ((id = $(el).attr('id'))) {
this.id = id;
} else {
this.id = 'el-finder-'+Math.random().toString().substring(2);
}
/**
* String. Version number;
**/
this.version = '1.1 RC3';
/**
* String. jQuery version;
**/
this.jquery = $.fn.jquery.split('.').join('');
/**
* Object. Current Working Dir info
**/
this.cwd = {};
/**
* Object. Current Dir Content. Files/folders info
**/
this.cdc = {};
/**
* Object. Buffer for copied files
**/
this.buffer = {};
/**
* Array. Selected files IDs
**/
this.selected = [];
/**
* Array. Folder navigation history
**/
this.history = [];
/**
* Boolean. Enable/disable actions
**/
this.locked = false;
/**
* Number. Max z-index on page + 1, need for contextmenu and quicklook
**/
this.zIndex = 2;
/**
* DOMElement. jQueryUI dialog
**/
this.dialog = null;
/**
* DOMElement. For docked mode - place where fm is docked
**/
this.anchor = this.options.docked ? $('<div/>').hide().insertBefore(el) : null;
/**
* Object. Some options get from server
**/
this.params = { dotFiles : false, arc : '', uplMaxSize : '' };
this.vCookie = 'el-finder-view-'+this.id;
this.pCookie = 'el-finder-places-'+this.id;
this.lCookie = 'el-finder-last-'+this.id;
/**
* Object. View. After init we can accessel as this.view.win
**/
this.view = new this.view(this, el);
/**
* Object. User Iterface. Controller for commands/buttons/contextmenu
**/
this.ui = new this.ui(this);
/**
* Object. Set/update events
**/
this.eventsManager = new this.eventsManager(this);
/**
* Object. Quick Look like in MacOS X :)
**/
this.quickLook = new this.quickLook(this);
/**
* Set/get cookie value
*
* @param String name cookie name
* @param String value cookie value, null to unset
**/
this.cookie = function(name, value) {
if (typeof value == 'undefined') {
if (document.cookie && document.cookie != '') {
var i, c = document.cookie.split(';');
name += '=';
for (i=0; i<c.length; i++) {
c[i] = $.trim(c[i]);
if (c[i].substring(0, name.length) == name) {
return decodeURIComponent(c[i].substring(name.length));
}
}
}
return '';
} else {
var d, o = $.extend({}, this.options.cookie);
if (value===null) {
value = '';
o.expires = -1;
}
if (typeof(o.expires) == 'number') {
d = new Date();
d.setTime(d.getTime()+(o.expires * 24 * 60 * 60 * 1000));
o.expires = d;
}
document.cookie = name+'='+encodeURIComponent(value)+'; expires='+o.expires.toUTCString()+(o.path ? '; path='+o.path : '')+(o.domain ? '; domain='+o.domain : '')+(o.secure ? '; secure' : '');
}
}
/**
* Set/unset this.locked flag
*
* @param Boolean state
**/
this.lock = function(l) {
this.view.spinner((this.locked = l||false));
this.eventsManager.lock = this.locked;
}
/**
* Set/unset lock for keyboard shortcuts
*
* @param Boolean state
**/
this.lockShortcuts = function(l) {
this.eventsManager.lock = l;
}
/**
* Set file manager view type (list|icons)
*
* @param String v view name
**/
this.setView = function(v) {
if (v == 'list' || v == 'icons') {
this.options.view = v;
this.cookie(this.vCookie, v);
}
}
/**
* make ajax request, show message on error, call callback on success
*
* @param Object. data for ajax request
* @param Function
* @param Object overrwrite some options
*/
this.ajax = function(data, callback, options) {
var opts = {
url : this.options.url,
async : true,
type : 'GET',
data : data,
dataType : 'json',
cache : false,
lock : true,
force : false,
silent : false
}
if (typeof(options) == 'object') {
opts = $.extend({}, opts, options);
}
if (!opts.silent) {
opts.error = self.view.fatal;
}
opts.success = function(data) {
opts.lock && self.lock();
data.debug && self.log(data.debug);
if (data.error) {
!opts.silent && self.view.error(data.error, data.errorData);
if (!opts.force) {
return;
}
}
callback(data);
delete data;
}
opts.lock && this.lock(true);
$.ajax(opts);
}
/**
* Load generated thumbnails in background
*
**/
this.tmb = function() {
this.ajax({cmd : 'tmb', current : self.cwd.hash}, function(data) {
if (self.options.view == 'icons' && data.images && data.current == self.cwd.hash) {
for (var i in data.images) {
if (self.cdc[i]) {
self.cdc[i].tmb = data.images[i];
$('div[key="'+i+'"]>p', self.view.cwd).css('background', ' url("'+data.images[i]+'") 0 0 no-repeat');
}
}
data.tmb && self.tmb();
}
}, {lock : false, silent : true});
}
/**
* Return folders in places IDs
*
* @return Array
**/
this.getPlaces = function() {
var pl = [], p = this.cookie(this.pCookie);
if (p.length) {
if (p.indexOf(':')!=-1) {
pl = p.split(':');
} else {
pl.push(p);
}
}
return pl;
}
/**
* Add new folder to places
*
* @param String Folder ID
* @return Boolean
**/
this.addPlace = function(id) {
var p = this.getPlaces();
if ($.inArray(id, p) == -1) {
p.push(id);
this.savePlaces(p);
return true;
}
}
/**
* Remove folder from places
*
* @param String Folder ID
* @return Boolean
**/
this.removePlace = function(id) {
var p = this.getPlaces();
if ($.inArray(id, p) != -1) {
this.savePlaces($.map(p, function(o) { return o == id?null:o; }));
return true;
}
}
/**
* Save new places data in cookie
*
* @param Array Folders IDs
**/
this.savePlaces = function(p) {
this.cookie(this.pCookie, p.join(':'));
}
/**
* Update file manager content
*
* @param Object Data from server
**/
this.reload = function(data) {
var i;
this.cwd = data.cwd;
this.cdc = {};
for (i=0; i<data.cdc.length ; i++) {
this.cdc[data.cdc[i].hash] = data.cdc[i];
this.cwd.size += data.cdc[i].size;
}
if (data.tree) {
this.view.renderNav(data.tree);
this.eventsManager.updateNav();
}
this.updateCwd();
/* tell connector to generate thumbnails */
if (data.tmb && !self.locked && self.options.view == 'icons') {
self.tmb();
}
/* have to select some files */
if (data.select && data.select.length) {
var l = data.select.length;
while (l--) {
this.cdc[data.select[l]] && this.selectById(data.select[l]);
}
}
this.lastDir(this.cwd.hash);
if (this.options.autoReload>0) {
this.iID && clearInterval(this.iID);
this.iID = setInterval(function() { !self.locked && self.ui.exec('reload'); }, this.options.autoReload*60000);
}
}
/**
* Redraw current directory
*
*/
this.updateCwd = function() {
this.lockShortcuts();
this.selected = [];
this.view.renderCwd();
this.eventsManager.updateCwd();
this.view.tree.find('a[key="'+this.cwd.hash+'"]').trigger('select');
}
/**
* Execute after files was dropped onto folder
*
* @param Object drop event
* @param Object drag helper object
* @param String target folder ID
*/
this.drop = function(e, ui, target) {
if (ui.helper.find('[key="'+target+'"]').length) {
return self.view.error('Unable to copy into itself');
}
var ids = [];
ui.helper.find('div:not(.noaccess):has(>label):not(:has(em[class="readonly"],em[class=""]))').each(function() {
ids.push($(this).hide().attr('key'));
});
if (!ui.helper.find('div:has(>label):visible').length) {
ui.helper.hide();
}
if (ids.length) {
self.setBuffer(ids, e.shiftKey?0:1, target);
if (self.buffer.files) {
/* some strange jquery ui bug (in list view) */
setTimeout(function() {self.ui.exec('paste'); self.buffer = {}}, 300);
}
} else {
$(this).removeClass('el-finder-droppable');
}
}
/**
* Return selected files data
*
* @param Number if set, returns only element with this index or empty object
* @return Array|Object
*/
this.getSelected = function(ndx) {
var i, s = [];
if (ndx>=0) {
return this.cdc[this.selected[ndx]]||{};
}
for (i=0; i<this.selected.length; i++) {
this.cdc[this.selected[i]] && s.push(this.cdc[this.selected[i]]);
}
return s;
}
this.select = function(el, reset) {
reset && $('.ui-selected', self.view.cwd).removeClass('ui-selected');
el.addClass('ui-selected');
self.updateSelect();
}
this.selectById = function(id) {
var el = $('[key="'+id+'"]', this.view.cwd);
if (el.length) {
this.select(el);
this.checkSelectedPos();
}
}
this.unselect = function(el) {
el.removeClass('ui-selected');
self.updateSelect();
}
this.toggleSelect = function(el) {
el.toggleClass('ui-selected');
this.updateSelect();
}
this.selectAll = function() {
$('[key]', self.view.cwd).addClass('ui-selected')
self.updateSelect();
}
this.unselectAll = function() {
$('.ui-selected', self.view.cwd).removeClass('ui-selected');
self.updateSelect();
}
this.updateSelect = function() {
self.selected = [];
$('.ui-selected', self.view.cwd).each(function() {
self.selected.push($(this).attr('key'));
});
self.view.selectedInfo();
self.ui.update();
self.quickLook.update();
}
/**
* Scroll selected element in visible position
*
* @param Boolean check last or first selected element?
*/
this.checkSelectedPos = function(last) {
var s = self.view.cwd.find('.ui-selected:'+(last ? 'last' : 'first')).eq(0),
p = s.position(),
h = s.outerHeight(),
ph = self.view.cwd.height();
if (p.top < 0) {
self.view.cwd.scrollTop(p.top+self.view.cwd.scrollTop()-2);
} else if (ph - p.top < h) {
self.view.cwd.scrollTop(p.top+h-ph+self.view.cwd.scrollTop());
}
}
/**
* Add files to clipboard buffer
*
* @param Array files IDs
* @param Boolean copy or cut files?
* @param String destination folder ID
*/
this.setBuffer = function(files, cut, dst) {
var i, id, f;
this.buffer = {
src : this.cwd.hash,
dst : dst,
files : [],
names : [],
cut : cut||0
};
for (i=0; i<files.length; i++) {
id = files[i];
f = this.cdc[id];
if (f && f.read && f.type != 'link') {
this.buffer.files.push(f.hash);
this.buffer.names.push(f.name);
}
}
if (!this.buffer.files.length) {
this.buffer = {};
}
}
/**
* Return true if file name is acceptable
*
* @param String file/folder name
* @return Boolean
*/
this.isValidName = function(n) {
if (!this.cwd.dotFiles && n.indexOf('.') == 0) {
return false;
}
return n.match(/^[^\\\/\<\>:]+$/);
}
/**
* Return true if file with this name exists
*
* @param String file/folder name
* @return Boolean
*/
this.fileExists = function(n) {
for (var i in this.cdc) {
if (this.cdc[i].name == n) {
return i;
}
}
return false;
}
/**
* Return name for new file/folder
*
* @param String base name (i18n)
* @param String extension for file
* @return String
*/
this.uniqueName = function(n, ext) {
n = self.i18n(n);
var name = n, i = 0, ext = ext||'';
if (!this.fileExists(name+ext)) {
return name+ext;
}
while (i++<100) {
if (!this.fileExists(name+i+ext)) {
return name+i+ext;
}
}
return name.replace('100', '')+Math.random()+ext;
}
/**
* Get/set last opened dir
*
* @param String dir hash
* @return String
*/
this.lastDir = function(dir) {
if (this.options.rememberLastDir) {
return dir ? this.cookie(this.lCookie, dir) : this.cookie(this.lCookie);
}
}
/**
* Resize file manager
*
* @param Number width
* @param Number height
*/
function resize(w, h) {
w && self.view.win.width(w);
h && self.view.nav.add(self.view.cwd).height(h);
}
/**
* Resize file manager in dialog window while it resize
*
*/
function dialogResize() {
resize(null, self.dialog.height()-self.view.tlb.parent().height()-($.browser.msie ? 47 : 32))
}
this.time = function() {
return new Date().getMilliseconds();
}
/* here we init file manager */
this.setView(this.cookie(this.vCookie));
resize(self.options.width, self.options.height);
/* dialog or docked mode */
if (this.options.dialog || this.options.docked) {
this.options.dialog = $.extend({width : 570, dialogClass : '', minWidth : 480, minHeight: 330}, this.options.dialog || {});
this.options.dialog.dialogClass += 'el-finder-dialog';
this.options.dialog.resize = dialogResize;
if (this.options.docked) {
/* docked mode - create dialog and store size */
this.options.dialog.close = function() { self.dock(); };
this.view.win.data('size', {width : this.view.win.width(), height : this.view.nav.height()});
} else {
this.dialog = $('<div/>').append(this.view.win).dialog(this.options.dialog);
}
}
this.ajax({
cmd : 'open',
target : this.lastDir()||'',
init : true,
tree : true
},
function(data) {
if (data.cwd) {
self.eventsManager.init();
self.reload(data);
self.params = data.params;
// self.log(self.params)
$('*', document.body).each(function() {
var z = parseInt($(this).css('z-index'));
if (z >= self.zIndex) {
self.zIndex = z+1;
}
});
self.ui.init(data.disabled);
}
}, {force : true});
this.open = function() {
this.dialog ? this.dialog.dialog('open') : this.view.win.show();
this.eventsManager.lock = false;
}
this.close = function() {
if (this.options.docked && this.view.win.attr('undocked')) {
this.dock();
} else {
this.dialog ? this.dialog.dialog('close') : this.view.win.hide();
}
this.eventsManager.lock = true;
}
this.dock = function() {
if (this.options.docked && this.view.win.attr('undocked')) {
var s =this.view.win.data('size');
this.view.win.insertAfter(this.anchor).removeAttr('undocked');
resize(s.width, s.height);
this.dialog.dialog('destroy');
this.dialog = null;
}
}
this.undock = function() {
if (this.options.docked && !this.view.win.attr('undocked')) {
this.dialog = $('<div/>').append(this.view.win.css('width', '100%').attr('undocked', true).show()).dialog(this.options.dialog);
dialogResize();
}
}
}
/**
* Translate message into selected language
*
* @param String message in english
* @param String translated or original message
*/
elFinder.prototype.i18n = function(m) {
return this.options.i18n[this.options.lang] && this.options.i18n[this.options.lang][m] ? this.options.i18n[this.options.lang][m] : m;
}
/**
* Default config
*
*/
elFinder.prototype.options = {
/* connector url. Required! */
url : '',
/* interface language */
lang : 'en',
/* additional css class for filemanager container */
cssClass : '',
/* characters number to wrap file name in icons view. set to 0 to disable wrap */
wrap : 14,
/* Name for places/favorites (i18n), set to '' to disable places */
places : 'Places',
/* show places before navigation? */
placesFirst : true,
/* callback to get file url (for wswing editors) */
editorCallback : null,
/* string to cut from file url begin before pass it to editorCallback. variants: '' - nothing to cut, 'root' - cut root url, 'http://...' - string if it exists in the beginig of url */
cutURL : '',
/* close elfinder after editorCallback */
closeOnEditorCallback : true,
/* i18 messages. not set manually! */
i18n : {},
/* fm view (icons|list) */
view : 'icons',
/* width to overwrite css options */
width : '',
/* height to overwrite css options. Attenion! this is heigt of navigation/cwd panels! not total fm height */
height : '',
/* disable shortcuts exclude arrows/space */
disableShortcuts : false,
/* open last visited dir after reload page or close and open browser */
rememberLastDir : true,
/* cookie options */
cookie : {
expires : 30,
domain : '',
path : '/',
secure : false
},
/* buttons on toolbar */
toolbar : [
['back', 'reload'],
['select', 'open'],
['mkdir', 'mkfile', 'upload'],
['copy', 'paste', 'rm'],
['rename', 'edit'],
['info', 'quicklook'],
['icons', 'list'],
['help']
],
/* contextmenu commands */
contextmenu : {
'cwd' : ['reload', 'delim', 'mkdir', 'mkfile', 'upload', 'delim', 'paste', 'delim', 'info'],
'file' : ['select', 'open', 'quicklook', 'delim', 'copy', 'cut', 'rm', 'delim', 'duplicate', 'rename', 'edit', 'resize', 'archive', 'extract', 'delim', 'info'],
'group' : ['copy', 'cut', 'rm', 'delim', 'archive', 'extract', 'delim', 'info']
},
/* jqueryUI dialog options */
dialog : null,
/* docked mode */
docked : false,
/* auto reload time (min) */
autoReload : 0
}
$.fn.elfinder = function(o) {
return this.each(function() {
var cmd = typeof(o) == 'string' ? o : '';
if (!this.elfinder) {
this.elfinder = new elFinder(this, typeof(o) == 'object' ? o : {})
}
switch(cmd) {
case 'close':
case 'hide':
this.elfinder.close();
break;
case 'open':
case 'show':
this.elfinder.open();
break;
case 'dock':
this.elfinder.dock();
break;
case 'undock':
this.elfinder.undock();
break;
}
})
}
})(jQuery);(function($) {
elFinder.prototype.view = function(fm, el) {
var self = this;
this.fm = fm;
/**
* Object. Mimetypes to kinds mapping
**/
this.kinds = {
'unknown' : 'Unknown',
'directory' : 'Folder',
'symlink' : 'Alias',
'symlink-broken' : 'Broken alias',
'application/x-empty' : 'Plain text',
'application/postscript' : 'Postscript document',
'application/octet-stream' : 'Application',
'application/vnd.ms-office' : 'Microsoft Office document',
'application/vnd.ms-word' : 'Microsoft Word document',
'application/vnd.ms-excel' : 'Microsoft Excel document',
'application/vnd.ms-powerpoint' : 'Microsoft Powerpoint presentation',
'application/pdf' : 'Portable Document Format (PDF)',
'application/vnd.oasis.opendocument.text' : 'Open Office document',
'application/x-shockwave-flash' : 'Flash application',
'application/xml' : 'XML document',
'application/x-bittorrent' : 'Bittorrent file',
'application/x-7z-compressed' : '7z archive',
'application/x-tar' : 'TAR archive',
'application/x-gzip' : 'GZIP archive',
'application/x-bzip2' : 'BZIP archive',
'application/zip' : 'ZIP archive',
'application/x-rar' : 'RAR archive',
'application/javascript' : 'Javascript application',
'text/plain' : 'Plain text',
'text/x-php' : 'PHP source',
'text/html' : 'HTML document',
'text/javascript' : 'Javascript source',
'text/css' : 'CSS style sheet',
'text/rtf' : 'Rich Text Format (RTF)',
'text/rtfd' : 'RTF with attachments (RTFD)',
'text/x-c' : 'C source',
'text/x-c++' : 'C++ source',
'text/x-shellscript' : 'Unix shell script',
'text/x-python' : 'Python source',
'text/x-java' : 'Java source',
'text/x-ruby' : 'Ruby source',
'text/x-perl' : 'Perl script',
'text/xml' : 'XML document',
'image/x-ms-bmp' : 'BMP image',
'image/jpeg' : 'JPEG image',
'image/gif' : 'GIF Image',
'image/png' : 'PNG image',
'image/x-targa' : 'TGA image',
'image/tiff' : 'TIFF image',
'image/vnd.adobe.photoshop' : 'Adobe Photoshop image',
'audio/mpeg' : 'MPEG audio',
'audio/midi' : 'MIDI audio',
'audio/ogg' : 'Ogg Vorbis audio',
'audio/mp4' : 'MP4 audio',
'audio/wav' : 'WAV audio',
'video/x-dv' : 'DV video',
'video/mp4' : 'MP4 video',
'video/mpeg' : 'MPEG video',
'video/x-msvideo' : 'AVI video',
'video/quicktime' : 'Quicktime video',
'video/x-ms-wmv' : 'WM video',
'video/x-flv' : 'Flash video',
'video/x-matroska' : 'Matroska video'
}
this.tlb = $('<ul />');
this.nav = $('<div class="el-finder-nav"/>').resizable({handles : 'e', autoHide : true, minWidth : 200, maxWidth: 500});
this.cwd = $('<div class="el-finder-cwd"/>').attr('unselectable', 'on');
this.spn = $('<div class="el-finder-spinner"/>');
this.err = $('<p class="el-finder-err"><strong/></p>').click(function() { $(this).hide(); });
this.nfo = $('<div class="el-finder-stat"/>');
this.pth = $('<div class="el-finder-path"/>');
this.sel = $('<div class="el-finder-sel"/>');
this.stb = $('<div class="el-finder-statusbar"/>')
.append(this.pth)
.append(this.nfo)
.append(this.sel);
this.wrz = $('<div class="el-finder-workzone" />')
.append(this.nav)
.append(this.cwd)
.append(this.spn)
.append(this.err)
.append('<div style="clear:both" />');
this.win = $(el).empty().attr('id', this.fm.id).addClass('el-finder '+(fm.options.cssClass||''))
.append($('<div class="el-finder-toolbar" />').append(this.tlb))
.append(this.wrz)
.append(this.stb);
this.tree = $('<ul class="el-finder-tree"></ul>').appendTo(this.nav);
this.plc = $('<ul class="el-finder-places"><li><a href="#" class="el-finder-places-root"><div/>'+this.fm.i18n(this.fm.options.places)+'</a><ul/></li></ul>').hide();
this.nav[this.fm.options.placesFirst ? 'prepend' : 'append'](this.plc);
/*
* Render ajax spinner
*/
this.spinner = function(show) {
this.win.toggleClass('el-finder-disabled', show);
this.spn.toggle(show);
}
/*
* Display ajax error
*/
this.fatal = function(t) {
self.error(t.status != '404' ? 'Invalid backend configuration' : 'Unable to connect to backend')
}
/*
* Render error
*/
this.error = function(err, data) {
this.fm.lock();
this.err.show().children('strong').html(this.fm.i18n(err)+'!'+this.formatErrorData(data));
setTimeout(function() { self.err.fadeOut('slow'); }, 4000);
}
/*
* Render navigation panel with dirs tree
*/
this.renderNav = function(tree) {
var d = tree.dirs.length ? traverse(tree.dirs) : '',
li = '<li><a href="#" class="el-finder-tree-root" key="'+tree.hash+'"><div'+(d ? ' class="collapsed expanded"' : '')+'/>'+tree.name+'</a>'+d+'</li>';
this.tree.html(li);
this.fm.options.places && this.renderPlaces();
function traverse(tree) {
var i, hash, c, html = '<ul style="display:none">';
for (i=0; i < tree.length; i++) {
c = '';
if (!tree[i].read && !tree[i].write) {
c = 'noaccess';
} else if (!tree[i].read) {
c = 'dropbox';
} else if (!tree[i].write) {
c = 'readonly';
}
html += '<li><a href="#" class="'+c+'" key="'+tree[i].hash+'"><div'+(tree[i].dirs.length ? ' class="collapsed"' : '')+'/>'+tree[i].name+'</a>';
if (tree[i].dirs.length) {
html += traverse(tree[i].dirs);
}
html += '</li>';
}
return html +'</ul>';
}
}
/*
* Render places
*/
this.renderPlaces = function() {
var i, c,
pl = this.fm.getPlaces(),
ul = this.plc.show().find('ul').empty().hide();
$('div:first', this.plc).removeClass('collapsed expanded');
if (pl.length) {
pl.sort(function(a, b) {
var _a = self.tree.find('a[key="'+a+'"]').text()||'',
_b = self.tree.find('a[key="'+b+'"]').text()||'';
return _a.localeCompare(_b);
});
for (i=0; i < pl.length; i++) {
if ((c = this.tree.find('a[key="'+pl[i]+'"]:not(.dropbox)').parent()) && c.length) {
ul.append(c.clone().children('ul').remove().end().find('div').removeClass('collapsed expanded').end());
} else {
this.fm.removePlace(pl[i]);
}
};
ul.children().length && $('div:first', this.plc).addClass('collapsed');
}
}
/*
* Render current directory
*/
this.renderCwd = function() {
this.cwd.empty();
var num = 0, size = 0, html = '';
for (var hash in this.fm.cdc) {
num++;
size += this.fm.cdc[hash].size;
html += this.fm.options.view == 'icons'
? this.renderIcon(this.fm.cdc[hash])
: this.renderRow(this.fm.cdc[hash], num%2);
}
if (this.fm.options.view == 'icons') {
this.cwd.append(html);
} else {
this.cwd.append('<table><tr><th colspan="2">'+this.fm.i18n('Name')+'</th><th>'+this.fm.i18n('Permissions')+'</th><th>'+this.fm.i18n('Modified')+'</th><th class="size">'+this.fm.i18n('Size')+'</th><th>'+this.fm.i18n('Kind')+'</th></tr>'+html+'</table>');
}
this.pth.text(fm.cwd.rel);
this.nfo.text(fm.i18n('items')+': '+num+', '+this.formatSize(size));
this.sel.empty();
}
/*
* Render one file as icon
*/
this.renderIcon = function(f) {
var str = '<p'+(f.tmb ? ' style="'+"background:url('"+f.tmb+"') 0 0 no-repeat"+'"' : '')+'/><label>'+this.formatName(f.name)+'</label>';
if (f.link || f.mime == 'symlink-broken') {
str += '<em/>';
}
if (!f.read && f.write) {
str += '<em class="'+(f.mime == 'directory' ? 'dropbox' :'noread')+'" />';
} else if (!f.write && f.read) {
str += '<em class="readonly" />';
}
return '<div class="'+this.mime2class(f.mime)+(!f.read && !f.write ? ' noaccess' : '')+'" key="'+f.hash+'">'+str+'</div>';
}
/*
* Render one file as table row
*/
this.renderRow = function(f, odd) {
var str = f.link || f.mime =='symlink-broken' ? '<em/>' : '';
if (!f.read && f.write) {
str += '<em class="'+(f.mime == 'directory' ? 'dropbox' :'noread')+'" />';
} else if (!f.write && f.read) {
str += '<em class="readonly" />';
}
return '<tr key="'+f.hash+'" class="'+self.mime2class(f.mime)+(odd ? ' el-finder-row-odd' : '')+'"><td class="icon"><p>'+str+'</p></td><td>'+f.name+'</td><td>'+self.formatPermissions(f.read, f.write, f.rm)+'</td><td>'+self.formatDate(f.date)+'</td><td class="size">'+self.formatSize(f.size)+'</td><td>'+self.mime2kind(f.link ? 'symlink' : f.mime)+'</td></tr>';
}
/*
* Re-render file (after editing)
*/
this.updateFile = function(f) {
var e = this.cwd.find('[key="'+f.hash+'"]');
e.replaceWith(e[0].nodeName == 'DIV' ? this.renderIcon(f) : this.renderRow(f));
}
/*
* Update info about selected files
*/
this.selectedInfo = function() {
var i, s = 0, sel;
if (self.fm.selected.length) {
sel = this.fm.getSelected();
for (i=0; i<sel.length; i++) {
s += sel[i].size;
}
}
this.sel.text(i>0 ? this.fm.i18n('selected items')+': '+sel.length+', '+this.formatSize(s) : '');
}
/*
* Return wraped file name if needed
*/
this.formatName = function(n) {
var w = self.fm.options.wrap;
if (w>0) {
if (n.length > w*2) {
return n.substr(0, w)+"&shy;"+n.substr(w, w-5)+"&hellip;"+n.substr(n.length-3);
} else if (n.length > w) {
return n.substr(0, w)+"&shy;"+n.substr(w);
}
}
return n;
}
/*
* Return error message
*/
this.formatErrorData = function(data) {
var i, err = ''
if (typeof(data) == 'object') {
err = '<br />';
for (i in data) {
err += i+' '+self.fm.i18n(data[i])+'<br />';
}
}
return err;
}
/*
* Convert mimetype into css class
*/
this.mime2class = function(mime) {
return mime.replace('/' , ' ').replace(/\./g, '-');
}
/*
* Return localized date
*/
this.formatDate = function(d) {
return d.replace(/([a-z]+)\s/i, function(a1, a2) { return self.fm.i18n(a2)+' '; });
}
/*
* Return formated file size
*/
this.formatSize = function(s) {
var n = 1, u = '';
if (s > 1073741824) {
n = 1073741824;
u = 'Gb';
} else if (s > 1048576) {
n = 1048576;
u = 'Mb';
} else if (s > 1024) {
n = 1024;
u = 'Kb';
}
return Math.round(s/n)+' '+u;
}
/*
* Return localized string with file permissions
*/
this.formatPermissions = function(r, w, rm) {
var p = [];
r && p.push(self.fm.i18n('read'));
w && p.push(self.fm.i18n('write'));
rm && p.push(self.fm.i18n('remove'));
return p.join('/');
}
/*
* Return kind of file
*/
this.mime2kind = function(mime) {
return this.fm.i18n(this.kinds[mime]||'unknown');
}
}
})(jQuery);/**
* @class elFinder user Interface.
* @author dio dio@std42.ru
**/
(function($) {
elFinder.prototype.ui = function(fm) {
var self = this;
this.fm = fm;
this.cmd = {};
this.buttons = {};
this.menu = $('<div class="el-finder-contextmenu" />').appendTo(document.body).hide();
this.dockButton = $('<div class="el-finder-dock-button" title="'+self.fm.i18n('Dock/undock filemanger window')+'" />');
this.exec = function(cmd, arg) {
if (this.cmd[cmd]) {
if (cmd != 'open' && !this.cmd[cmd].isAllowed()) {
return this.fm.view.error('Command not allowed');
}
if (!this.fm.locked) {
this.fm.quickLook.hide();
$('.el-finder-info').remove();
this.cmd[cmd].exec(arg);
this.update();
}
}
}
this.cmdName = function(cmd) {
if (this.cmd[cmd] && this.cmd[cmd].name) {
return cmd == 'archive' && this.fm.params.archives.length == 1
? this.fm.i18n('Create')+' '+this.fm.view.mime2kind(this.fm.params.archives[0]).toLowerCase()
: this.fm.i18n(this.cmd[cmd].name);
}
return cmd;
}
this.isCmdAllowed = function(cmd) {
return self.cmd[cmd] && self.cmd[cmd].isAllowed();
}
this.execIfAllowed = function(cmd) {
this.isCmdAllowed(cmd) && this.exec(cmd);
}
this.includeInCm = function(cmd, t) {
return this.isCmdAllowed(cmd) && this.cmd[cmd].cm(t);
}
this.showMenu = function(e) {
var t, win, size, id = '';
this.hideMenu();
if (!self.fm.selected.length) {
t = 'cwd';
} else if (self.fm.selected.length == 1) {
t = 'file';
} else {
t = 'group';
}
menu(t);
win = $(window);
size = {
height : win.height(),
width : win.width(),
sT : win.scrollTop(),
cW : this.menu.width(),
cH : this.menu.height()
};
this.menu.css({
left : ((e.clientX + size.cW) > size.width ? ( e.clientX - size.cW) : e.clientX),
top : ((e.clientY + size.cH) > size.height && e.clientY > size.cH ? (e.clientY + size.sT - size.cH) : e.clientY + size.sT)
})
.show()
.find('div[name]')
.hover(
function() {
var t = $(this), s = t.children('div'), w;
t.addClass('hover');
if (s.length) {
if (!s.attr('pos')) {
w = t.outerWidth();
s.css($(window).width() - w - t.offset().left > s.width() ? 'left' : 'right', w-5).attr('pos', true);
}
s.show();
}
},
function() { $(this).removeClass('hover').children('div').hide(); }
).click(function(e) {
e.stopPropagation();
var t = $(this);
if (!t.children('div').length) {
self.hideMenu();
self.exec(t.attr('name'), t.attr('argc'));
}
});
// self.fm.log(self.menu.html())
function menu(t) {
var i, j, a, html, l, src = self.fm.options.contextmenu[t]||[];
for (i=0; i < src.length; i++) {
if (src[i] == 'delim') {
self.menu.children().length && !self.menu.children(':last').hasClass('delim') && self.menu.append('<div class="delim" />');
} else if (self.fm.ui.includeInCm(src[i], t)) {
a = self.cmd[src[i]].argc();
html = '';
if (a.length) {
html = '<span/><div class="el-finder-contextmenu-sub" style="z-index:'+(parseInt(self.menu.css('z-index'))+1)+'">';
for (var j=0; j < a.length; j++) {
html += '<div name="'+src[i]+'" argc="'+a[j].argc+'" class="'+a[j]['class']+'">'+a[j].text+'</div>';
};
html += '</div>';
}
self.menu.append('<div class="'+src[i]+'" name="'+src[i]+'">'+html+self.cmdName(src[i])+'</div>');
}
};
}
}
this.hideMenu = function() {
this.menu.hide().empty();
}
this.update = function() {
for (var i in this.buttons) {
this.buttons[i].toggleClass('disabled', !this.cmd[i].isAllowed());
}
}
this.init = function(disabled) {
var i, j, n, c=false, zindex = 2, z, t = this.fm.options.toolbar;
/* disable select command if there is no callback for it */
if (!this.fm.options.editorCallback) {
disabled.push('select');
}
/* disable archive command if no archivers enabled */
if (!self.fm.params.archives.length && $.inArray('archive', disabled) == -1) {
disabled.push('archive');
}
for (i in this.commands) {
if ($.inArray(i, disabled) == -1) {
this.commands[i].prototype = this.command.prototype;
this.cmd[i] = new this.commands[i](this.fm);
}
}
for (i=0; i<t.length; i++) {
if (c) {
this.fm.view.tlb.append('<li class="delim" />');
}
c = false;
for (j=0; j<t[i].length; j++) {
n = t[i][j];
if (this.cmd[n]) {
c = true;
this.buttons[n] = $('<li class="'+n+'" title="'+this.cmdName(n)+'" name="'+n+'" />')
.appendTo(this.fm.view.tlb)
.click(function(e) { e.stopPropagation(); })
.bind('click', (function(ui){ return function() {
!$(this).hasClass('disabled') && ui.exec($(this).attr('name'));
} })(this)
).hover(
function() { !$(this).hasClass('disabled') && $(this).addClass('el-finder-tb-hover')},
function() { $(this).removeClass('el-finder-tb-hover')}
);
}
}
}
this.update();
/* set z-index for context menu */
this.menu.css('z-index', this.fm.zIndex);
if (this.fm.options.docked) {
this.dockButton.hover(
function() { $(this).addClass('el-finder-dock-button-hover')},
function() { $(this).removeClass('el-finder-dock-button-hover')}
).click(function() {
self.fm.view.win.attr('undocked') ? self.fm.dock() : self.fm.undock();
$(this).trigger('mouseout');
}).prependTo(this.fm.view.tlb);
}
}
}
/**
* @class elFinder user Interface Command.
* @author dio dio@std42.ru
**/
elFinder.prototype.ui.prototype.command = function(fm) { }
/**
* Return true if command can be applied now
* @return Boolean
**/
elFinder.prototype.ui.prototype.command.prototype.isAllowed = function() {
return true;
}
/**
* Return true if command can be included in contextmenu of required type
* @param String contextmenu type (cwd|group|file)
* @return Boolean
**/
elFinder.prototype.ui.prototype.command.prototype.cm = function(t) {
return false;
}
/**
* Return not empty array if command required submenu in contextmenu
* @return Array
**/
elFinder.prototype.ui.prototype.command.prototype.argc = function(t) {
return [];
}
elFinder.prototype.ui.prototype.commands = {
/**
* @class Go into previous folder
* @param Object elFinder
**/
back : function(fm) {
var self = this;
this.name = 'Back';
this.fm = fm;
this.exec = function() {
if (this.fm.history.length) {
this.fm.ajax({ cmd : 'open', target : this.fm.history.pop() }, function(data) {
self.fm.reload(data);
});
}
}
this.isAllowed = function() {
return this.fm.history.length
}
},
/**
* @class Reload current directory and navigation panel
* @param Object elFinder
**/
reload : function(fm) {
var self = this;
this.name = 'Reload';
this.fm = fm;
this.exec = function() {
this.fm.ajax({ cmd : 'open', target : this.fm.cwd.hash, tree : true }, function(data) {
self.fm.reload(data);
});
}
this.cm = function(t) {
return t == 'cwd';
}
},
/**
* @class Open file/folder
* @param Object elFinder
**/
open : function(fm) {
var self = this;
this.name = 'Open';
this.fm = fm;
/**
* Open file/folder
* @param String file/folder id (only from click on nav tree)
**/
this.exec = function(dir) {
var t = null;
if (dir) {
t = {
hash : $(dir).attr('key'),
mime : 'directory',
read : !$(dir).hasClass('noaccess') && !$(dir).hasClass('dropbox')
}
} else {
t = this.fm.getSelected(0);
}
if (!t.hash) {
return;
}
if (!t.read) {
return this.fm.view.error('Access denied');
}
if (t.type == 'link' && !t.link) {
return this.fm.view.error('Unable to open broken link');
}
if (t.mime == 'directory') {
openDir(t.link||t.hash);
} else {
openFile(t);
}
function openDir(id) {
self.fm.history.push(self.fm.cwd.hash);
self.fm.ajax({ cmd : 'open', target : id }, function(data) {
self.fm.reload(data);
});
}
function openFile(f) {
var s, ws = '';
if (f.dim) {
s = f.dim.split('x');
ws = 'width='+(parseInt(s[0])+20)+',height='+(parseInt(s[1])+20)+',';
}
window.open(f.url||self.fm.options.url+'?cmd=open&current='+(f.parent||self.fm.cwd.hash)+'&target='+(f.link||f.hash), false, 'top=50,left=50,'+ws+'scrollbars=yes,resizable=yes');
}
}
this.isAllowed = function() {
return this.fm.selected.length == 1 && this.fm.getSelected(0).read;
}
this.cm = function(t) {
return t == 'file';
}
},
/**
* @class. Return file url
* @param Object elFinder
**/
select : function(fm) {
this.name = 'Select file';
this.fm = fm;
this.exec = function() {
var f = this.fm.getSelected(0);
if (!f.url) {
return this.fm.view.error('File URL disabled by connector config');
}
this.fm.options.editorCallback(this.fm.options.cutURL == 'root' ? f.url.substr(this.fm.params.url.length) : f.url.replace(new RegExp('^('+this.fm.options.cutURL+')'), ''));
if (this.fm.options.closeOnEditorCallback) {
this.fm.dock();
this.fm.close();
}
}
this.isAllowed = function() {
return this.fm.selected.length == 1 && !/(symlink\-broken|directory)/.test(this.fm.getSelected(0).mime);
}
this.cm = function(t) {
return t == 'file';
}
},
/**
* @class. Open/close quickLook window
* @param Object elFinder
**/
quicklook : function(fm) {
var self = this;
this.name = 'Preview with Quick Look';
this.fm = fm;
this.exec = function() {
self.fm.quickLook.toggle();
}
this.isAllowed = function() {
return this.fm.selected.length == 1;
}
this.cm = function() {
return true;
}
},
/**
* @class Display files/folders info in dialog window
* @param Object elFinder
**/
info : function(fm) {
var self = this;
this.name = 'Get info';
this.fm = fm;
/**
* Open dialog windows for each selected file/folder or for current folder
**/
this.exec = function() {
var f, s, cnt = this.fm.selected.length, w = $(window).width(), h = $(window).height();
this.fm.lockShortcuts(true);
if (!cnt) {
/** nothing selected - show cwd info **/
info(self.fm.cwd);
} else {
/** show info for each selected obj **/
$.each(this.fm.getSelected(), function() {
info(this);
});
}
function info(f) {
var p = ['50%', '50%'], x, y, d,
tb = '<table cellspacing="0"><tr><td>'+self.fm.i18n('Name')+'</td><td>'+f.name+'</td></tr><tr><td>'+self.fm.i18n('Kind')+'</td><td>'+self.fm.view.mime2kind(f.link ? 'symlink' : f.mime)+'</td></tr><tr><td>'+self.fm.i18n('Size')+'</td><td>'+self.fm.view.formatSize(f.size)+'</td></tr><tr><td>'+self.fm.i18n('Modified')+'</td><td>'+self.fm.view.formatDate(f.date)+'</td></tr><tr><td>'+self.fm.i18n('Permissions')+'</td><td>'+self.fm.view.formatPermissions(f.read, f.write, f.rm)+'</td></tr>';
if (f.link) {
tb += '<tr><td>'+self.fm.i18n('Link to')+'</td><td>'+f.linkTo+'</td></tr>';
}
if (f.dim) {
tb += '<tr><td>'+self.fm.i18n('Dimensions')+'</td><td>'+f.dim+' px.</td></tr>';
}
if (f.url) {
tb += '<tr><td>'+self.fm.i18n('URL')+'</td><td><a href="'+f.url+'" target="_blank">'+f.url+'</a></td></tr>';
}
if (cnt>1) {
d = $('.el-finder-dialog-info:last');
if (!d.length) {
x = Math.round(((w-350)/2)-(cnt*10));
y = Math.round(((h-300)/2)-(cnt*10));
p = [x>20?x:20, y>20?y:20];
} else {
x = d.offset().left+10;
y = d.offset().top+10;
p = [x<w-350 ? x : 20, y<h-300 ? y : 20];
}
}
$('<div />').append(tb+'</table>').dialog({
dialogClass : 'el-finder-dialog el-finder-dialog-info',
width : 390,
position : p,
title : self.fm.i18n(f.mime == 'directory' ? 'Folder info' : 'File info'),
close : function() { if (--cnt <= 0) { self.fm.lockShortcuts(); } $(this).dialog('destroy'); },
buttons : { Ok : function() { $(this).dialog('close'); }}
});
}
}
this.cm = function(t) {
return true;
}
},
/**
* @class Rename file/folder
* @param Object elFinder
**/
rename : function(fm) {
var self = this;
this.name = 'Rename';
this.fm = fm;
this.exec = function() {
var s = this.fm.getSelected(), el, c, input, f, n;
if (s.length == 1) {
f = s[0];
el = this.fm.view.cwd.find('[key="'+f.hash+'"]');
c = this.fm.options.view == 'icons' ? el.children('label') : el.find('td').eq(1);
n = c.html();
input = $('<input type="text" />').val(f.name).appendTo(c.empty())
.bind('change blur', rename)
.keydown(function(e) {
e.stopPropagation();
if (e.keyCode == 27) {
restore();
} else if (e.keyCode == 13) {
if (f.name == input.val()) {
restore();
} else {
$(this).trigger('change');
}
}
})
.click(function(e) { e.stopPropagation(); })
.select()
.focus();
this.fm.lockShortcuts(true);
}
function restore() {
c.html(n);
self.fm.lockShortcuts();
}
function rename() {
if (!self.fm.locked) {
var err, name = input.val();
if (f.name == input.val()) {
return restore();
}
if (!self.fm.isValidName(name)) {
err = 'Invalid name';
} else if (self.fm.fileExists(name)) {
err = 'File or folder with the same name already exists';
}
if (err) {
self.fm.view.error(err);
el.addClass('ui-selected');
self.fm.lockShortcuts(true);
return input.select().focus();
}
self.fm.ajax({cmd : 'rename', current : self.fm.cwd.hash, target : f.hash, name : name}, function(data) {
if (data.error) {
restore();
} else {
f.mime == 'directory' && self.fm.removePlace(f.hash) && self.fm.addPlace(data.target);
self.fm.reload(data);
}
}, { force : true });
}
}
}
/**
* Return true if only one file selected and has write perms and current dir has write perms
* @return Boolean
*/
this.isAllowed = function() {
return this.fm.cwd.write && this.fm.getSelected(0).write;
}
this.cm = function(t) {
return t == 'file';
}
},
/**
* @class Copy file/folder to "clipboard"
* @param Object elFinder
**/
copy : function(fm) {
this.name = 'Copy';
this.fm = fm;
this.exec = function() {
this.fm.setBuffer(this.fm.selected);
}
this.isAllowed = function() {
if (this.fm.selected.length) {
var s = this.fm.getSelected(), l = s.length;
while (l--) {
if (s[l].read) {
return true;
}
}
}
return false;
}
this.cm = function(t) {
return t != 'cwd';
}
},
/**
* @class Cut file/folder to "clipboard"
* @param Object elFinder
**/
cut : function(fm) {
this.name = 'Cut';
this.fm = fm;
this.exec = function() {
this.fm.setBuffer(this.fm.selected, 1);
}
this.isAllowed = function() {
if (this.fm.selected.length) {
var s = this.fm.getSelected(), l = s.length;
while (l--) {
if (s[l].read && s[l].rm) {
return true;
}
}
}
return false;
}
this.cm = function(t) {
return t != 'cwd';
}
},
/**
* @class Paste file/folder from "clipboard"
* @param Object elFinder
**/
paste : function(fm) {
var self = this;
this.name = 'Paste';
this.fm = fm;
this.exec = function() {
var i, d, f, r, msg = '';
if (!this.fm.buffer.dst) {
this.fm.buffer.dst = this.fm.cwd.hash;
}
d = this.fm.view.tree.find('[key="'+this.fm.buffer.dst+'"]');
if (!d.length || d.hasClass('noaccess') || d.hasClass('readonly')) {
return this.fm.view.error('Access denied');
}
if (this.fm.buffer.src == this.fm.buffer.dst) {
return this.fm.view.error('Unable to copy into itself');
}
var o = {
cmd : 'paste',
current : this.fm.cwd.hash,
src : this.fm.buffer.src,
dst : this.fm.buffer.dst,
cut : this.fm.buffer.cut
};
if (this.fm.jquery>132) {
o.target = this.fm.buffer.files;
} else {
o['target[]'] = this.fm.buffer.files;
}
this.fm.ajax(o, function(data) {
data.cdc && self.fm.reload(data);
}, {force : true});
}
this.isAllowed = function() {
return this.fm.buffer.files;
}
this.cm = function(t) {
return t == 'cwd';
}
},
/**
* @class Remove files/folders
* @param Object elFinder
**/
rm : function(fm) {
var self = this;
this.name = 'Remove';
this.fm = fm;
this.exec = function() {
var i, ids = [], s =this.fm.getSelected();
for (var i=0; i < s.length; i++) {
if (!s[i].rm) {
return this.fm.view.error(s[i].name+': '+this.fm.i18n('Access denied'));
}
ids.push(s[i].hash);
};
if (ids.length) {
this.fm.lockShortcuts(true);
$('<div><div class="ui-state-error ui-corner-all"><span class="ui-icon ui-icon-alert"/><strong>'+this.fm.i18n('Are you shure you want to remove files?<br /> This cannot be undone!')+'</strong></div></div>')
.dialog({
title : this.fm.i18n('Confirmation required'),
dialogClass : 'el-finder-dialog',
width : 350,
close : function() { self.fm.lockShortcuts(); },
buttons : {
Cancel : function() { $(this).dialog('close'); },
Ok : function() {
$(this).dialog('close');
var o = { cmd : 'rm', current : self.fm.cwd.hash };
if (self.fm.jquery > 132) {
o.targets = ids;
} else {
o['targets[]'] = ids;
}
self.fm.ajax(o, function(data) { data.tree && self.fm.reload(data); }, {force : true});
}
}
});
}
}
this.isAllowed = function(f) {
if (this.fm.selected.length) {
var s = this.fm.getSelected(), l = s.length;
while (l--) {
if (s[l].rm) {
return true;
}
}
}
return false;
}
this.cm = function(t) {
return t != 'cwd';
}
},
/**
* @class Create new folder
* @param Object elFinder
**/
mkdir : function(fm) {
var self = this;
this.name = 'New folder';
this.fm = fm;
this.exec = function() {
self.fm.unselectAll();
var n = this.fm.uniqueName('untitled folder');
input = $('<input type="text"/>').val(n);
prev = this.fm.view.cwd.find('.directory:last');
f = {name : n, hash : '', mime :'directory', read : true, write : true, date : '', size : 0},
el = this.fm.options.view == 'list'
? $(this.fm.view.renderRow(f)).children('td').eq(1).empty().append(input).end().end()
: $(this.fm.view.renderIcon(f)).children('label').empty().append(input).end()
el.addClass('directory ui-selected');
if (prev.length) {
el.insertAfter(prev);
} else if (this.fm.options.view == 'list') {
el.insertAfter(this.fm.view.cwd.find('tr').eq(0))
} else {
el.prependTo(this.fm.view.cwd)
}
self.fm.checkSelectedPos();
input.select().focus()
.click(function(e) { e.stopPropagation(); })
.bind('change blur', mkdir)
.keydown(function(e) {
e.stopPropagation();
if (e.keyCode == 27) {
el.remove();
self.fm.lockShortcuts();
} else if (e.keyCode == 13) {
mkdir();
}
});
self.fm.lockShortcuts(true);
function mkdir() {
if (!self.fm.locked) {
var err, name = input.val();
if (!self.fm.isValidName(name)) {
err = 'Invalid name';
} else if (self.fm.fileExists(name)) {
err = 'File or folder with the same name already exists';
}
if (err) {
self.fm.view.error(err);
self.fm.lockShortcuts(true);
el.addClass('ui-selected');
return input.select().focus();
}
self.fm.ajax({cmd : 'mkdir', current : self.fm.cwd.hash, name : name}, function(data) {
if (data.error) {
el.addClass('ui-selected');
return input.select().focus();
}
self.fm.reload(data);
}, {force : true});
}
}
}
this.isAllowed = function() {
return this.fm.cwd.write;
}
this.cm = function(t) {
return t == 'cwd';
}
},
/**
* @class Create new text file
* @param Object elFinder
**/
mkfile : function(fm) {
var self = this;
this.name = 'New text file';
this.fm = fm;
this.exec = function() {
self.fm.unselectAll();
var n = this.fm.uniqueName('untitled file', '.txt'),
input = $('<input type="text"/>').val(n),
f = {name : n, hash : '', mime :'text/plain', read : true, write : true, date : '', size : 0},
el = this.fm.options.view == 'list'
? $(this.fm.view.renderRow(f)).children('td').eq(1).empty().append(input).end().end()
: $(this.fm.view.renderIcon(f)).children('label').empty().append(input).end();
el.addClass('text ui-selected').appendTo(this.fm.options.view == 'list' ? self.fm.view.cwd.children('table') : self.fm.view.cwd);
input.select().focus()
.bind('change blur', mkfile)
.click(function(e) { e.stopPropagation(); })
.keydown(function(e) {
e.stopPropagation();
if (e.keyCode == 27) {
el.remove();
self.fm.lockShortcuts();
} else if (e.keyCode == 13) {
mkfile();
}
});
self.fm.lockShortcuts(true);
function mkfile() {
if (!self.fm.locked) {
var err, name = input.val();
if (!self.fm.isValidName(name)) {
err = 'Invalid name';
} else if (self.fm.fileExists(name)) {
err = 'File or folder with the same name already exists';
}
if (err) {
self.fm.view.error(err);
self.fm.lockShortcuts(true);
el.addClass('ui-selected');
return input.select().focus();
}
self.fm.ajax({cmd : 'mkfile', current : self.fm.cwd.hash, name : name}, function(data) {
if (data.error) {
el.addClass('ui-selected');
return input.select().focus();
}
self.fm.reload(data);
}, {force : true });
}
}
}
this.isAllowed = function(f) {
return this.fm.cwd.write;
}
this.cm = function(t) {
return t == 'cwd';
}
},
/**
* @class Upload files
* @param Object elFinder
**/
upload : function(fm) {
var self = this;
this.name = 'Upload files';
this.fm = fm;
this.exec = function() {
var id = 'el-finder-io-'+(new Date().getTime()),
e = $('<div class="ui-state-error ui-corner-all"><span class="ui-icon ui-icon-alert"/><div/></div>'),
m = this.fm.params.uplMaxSize ? '<p>'+this.fm.i18n('Maximum allowed files size')+': '+this.fm.params.uplMaxSize+'</p>' : '',
b = $('<p class="el-finder-add-field"><span class="ui-state-default ui-corner-all"><em class="ui-icon ui-icon-circle-plus"/></span>'+this.fm.i18n('Add field')+'</p>')
.click(function() { $(this).before('<p><input type="file" name="upload[]"/></p>'); }),
f = '<form method="post" enctype="multipart/form-data" action="'+self.fm.options.url+'" target="'+id+'"><input type="hidden" name="cmd" value="upload" /><input type="hidden" name="current" value="'+self.fm.cwd.hash+'" />',
d = $('<div/>'),
i = 3;
while (i--) { f += '<p><input type="file" name="upload[]"/></p>'; }
f = $(f+'</form>');
d.append(f.append(e.hide()).prepend(m).append(b)).dialog({
dialogClass : 'el-finder-dialog',
title : self.fm.i18n('Upload files'),
modal : true,
resizable : false,
close : function() { self.fm.lockShortcuts(); },
buttons : {
Cancel : function() { $(this).dialog('close'); },
Ok : function() {
if (!$(':file[value]', f).length) {
return error(self.fm.i18n('Select at least one file to upload'));
}
setTimeout(function() {
self.fm.lock();
if ($.browser.safari) {
$.ajax({
url : self.fm.options.url,
data : {cmd : 'ping'},
error : submit,
success : submit
});
} else {
submit();
}
});
$(this).dialog('close');
}
}
});
self.fm.lockShortcuts(true);
function error(err) {
e.show().find('div').empty().text(err);
}
function submit() {
var $io = $('<iframe name="'+id+'" name="'+id+'" src="about:blank"/>'),
io = $io[0],
cnt = 50,
doc, html, data;
$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' })
.appendTo('body').bind('load', function() {
$io.unbind('load');
result();
});
self.fm.lock(true);
f.submit();
function result() {
try {
doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
/* opera */
if (doc.body == null || doc.body.innerHTML == '') {
if (--cnt) {
return setTimeout(result, 100);
} else {
complite();
return self.fm.view.error('Unable to access iframe DOM after 50 tries');
}
}
/* get server response */
html = $(doc.body).html();
if (self.fm.jquery>=141) {
data = $.parseJSON(html);
} else if ( /^[\],:{}\s]*$/.test(html.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
.replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
/* get from jQuery 1.4 */
data = window.JSON && window.JSON.parse ? window.JSON.parse(html) : (new Function("return " + html))();
} else {
data = { error : 'Unable to parse server response' };
}
} catch(e) {
data = { error : 'Unable to parse server response' };
}
complite();
data.error && self.fm.view.error(data.error, data.errorData);
data.cwd && self.fm.reload(data);
data.tmb && self.fm.tmb();
}
function complite() {
self.fm.lock();
$io.remove();
}
}
}
this.isAllowed = function() {
return this.fm.cwd.write;
}
this.cm = function(t) {
return t == 'cwd';
}
},
/**
* @class Make file/folder copy
* @param Object elFinder
**/
duplicate : function(fm) {
var self = this;
this.name = 'Duplicate';
this.fm = fm;
this.exec = function() {
this.fm.ajax({
cmd : 'duplicate',
current : this.fm.cwd.hash,
target : this.fm.selected[0]
},
function(data) {
self.fm.reload(data);
});
}
this.isAllowed = function() {
return this.fm.cwd.write && this.fm.selected.length == 1 && this.fm.getSelected()[0].read;
}
this.cm = function(t) {
return t == 'file';
}
},
/**
* @class Edit text file
* @param Object elFinder
**/
edit : function(fm) {
var self = this;
this.name = 'Edit text file';
this.fm = fm;
this.exec = function() {
var f = this.fm.getSelected(0);
this.fm.lockShortcuts(true);
this.fm.ajax({
cmd : 'read',
current : this.fm.cwd.hash,
target : f.hash
}, function(data) {
self.fm.lockShortcuts(true);
var ta= $('<textarea/>').val(data.content||'').keydown(function(e) { e.stopPropagation(); });
$('<div/>').append(ta)
.dialog({
dialogClass : 'el-finder-dialog',
title : self.fm.i18n(self.name),
modal : true,
width : 500,
close : function() { self.fm.lockShortcuts(); },
buttons : {
Cancel : function() { $(this).dialog('close'); },
Ok : function() {
var c = ta.val();
$(this).dialog('close');
self.fm.ajax({
cmd : 'edit',
current : self.fm.cwd.hash,
target : f.hash,
content : c
}, function(data) {
if (data.target) {
self.fm.cdc[data.target.hash] = data.target;
self.fm.view.updateFile(data.target);
self.fm.selectById(data.target.hash);
}
}, {type : 'POST'});
}
}
});
});
}
this.isAllowed = function() {
if (self.fm.selected.length == 1) {
var f = this.fm.getSelected()[0];
return f.write && (f.mime.indexOf('text') == 0 || f.mime == 'application/x-empty' || f.mime == 'application/xml');
}
}
this.cm = function(t) {
return t == 'file';
}
},
/**
* @class Create archive
* @param Object elFinder
**/
archive : function(fm) {
var self = this;
this.name = 'Create archive';
this.fm = fm;
this.exec = function(t) {
var o = {
cmd : 'archive',
current : self.fm.cwd.hash,
type : $.inArray(t, this.fm.params.archives) != -1 ? t : this.fm.params.archives[0],
name : self.fm.i18n('Archive')
};
if (this.fm.jquery>132) {
o.targets = self.fm.selected;
} else {
o['targets[]'] = self.fm.selected;
}
this.fm.ajax(o, function(data) { self.fm.reload(data); });
}
this.isAllowed = function() {
if (this.fm.selected.length) {
var s = this.fm.getSelected(), l = s.length;
while (l--) {
if (s[l].read) {
return true;
}
}
}
return false;
}
this.cm = function(t) {
return t != 'cwd';
}
this.argc = function() {
var i, v = [];
for (i=0; i < self.fm.params.archives.length; i++) {
v.push({
'class' : 'archive',
'argc' : self.fm.params.archives[i],
'text' : self.fm.view.mime2kind(self.fm.params.archives[i])
});
};
return v;
}
},
/**
* @class Extract files from archive
* @param Object elFinder
**/
extract : function(fm) {
var self = this;
this.name = 'Uncompress archive';
this.fm = fm;
this.exec = function() {
this.fm.ajax({
cmd : 'extract',
current : this.fm.cwd.hash,
target : this.fm.getSelected(0).hash
}, function(data) {
self.fm.reload(data);
})
}
this.isAllowed = function() {
return this.fm.selected.length == 1 && this.fm.getSelected(0).read && this.fm.params.extract.length && $.inArray(this.fm.getSelected(0).mime, this.fm.params.extract) != -1;
}
this.cm = function(t) {
return t == 'file';
}
},
/**
* @class Resize image
* @param Object elFinder
**/
resize : function(fm) {
var self = this;
this.name = 'Resize image';
this.fm = fm;
this.exec = function() {
var s = this.fm.getSelected();
if (s[0] && s[0].write && s[0].dim) {
var size = s[0].dim.split('x'),
w = parseInt(size[0]),
h = parseInt(size[1]), rel = w/h
iw = $('<input type="text" size="9" value="'+w+'" name="width"/>'),
ih = $('<input type="text" size="9" value="'+h+'" name="height"/>'),
f = $('<form/>').append(iw).append(' x ').append(ih).append(' px');
iw.add(ih).bind('change', calc);
self.fm.lockShortcuts(true);
var d = $('<div/>').append($('<div/>').text(self.fm.i18n('Dimensions')+':')).append(f).dialog({
title : self.fm.i18n('Resize image'),
dialogClass : 'el-finder-dialog',
width : 230,
modal : true,
close : function() { self.fm.lockShortcuts(); },
buttons : {
Cancel : function() { $(this).dialog('close'); },
Ok : function() {
var _w = parseInt(iw.val()) || 0,
_h = parseInt(ih.val()) || 0;
if (_w>0 && _w != w && _h>0 && _h != h) {
self.fm.ajax({
cmd : 'resize',
current : self.fm.cwd.hash,
target : s[0].hash,
width : _w,
height : _h
},
function (data) {
self.fm.reload(data);
});
}
$(this).dialog('close');
}
}
});
}
function calc() {
var _w = parseInt(iw.val()) || 0,
_h = parseInt(ih.val()) || 0;
if (_w<=0 || _h<=0) {
_w = w;
_h = h;
} else if (this == iw.get(0)) {
_h = parseInt(_w/rel);
} else {
_w = parseInt(_h*rel);
}
iw.val(_w);
ih.val(_h);
}
}
this.isAllowed = function() {
return this.fm.selected.length == 1 && this.fm.cdc[this.fm.selected[0]].write && this.fm.cdc[this.fm.selected[0]].resize;
}
this.cm = function(t) {
return t == 'file';
}
},
/**
* @class Switch elFinder into icon view
* @param Object elFinder
**/
icons : function(fm) {
this.name = 'View as icons';
this.fm = fm;
this.exec = function() {
this.fm.view.win.addClass('el-finder-disabled');
this.fm.setView('icons');
this.fm.updateCwd();
this.fm.view.win.removeClass('el-finder-disabled');
$('div.image', this.fm.view.cwd).length && this.fm.tmb();
}
this.isAllowed = function() {
return this.fm.options.view != 'icons';
}
this.cm = function(t) {
return t == 'cwd';
}
},
/**
* @class Switch elFinder into list view
* @param Object elFinder
**/
list : function(fm) {
this.name = 'View as list';
this.fm = fm;
this.exec = function() {
this.fm.view.win.addClass('el-finder-disabled');
this.fm.setView('list');
this.fm.updateCwd();
this.fm.view.win.removeClass('el-finder-disabled');
}
this.isAllowed = function() {
return this.fm.options.view != 'list';
}
this.cm = function(t) {
return t == 'cwd';
}
},
help : function(fm) {
this.name = 'Help';
this.fm = fm;
this.exec = function() {
var h, ht = this.fm.i18n('helpText'), a, s, tabs;
h = '<div class="el-finder-logo"/><strong>'+this.fm.i18n('elFinder: Web file manager')+'</strong><br/>'+this.fm.i18n('Version')+': '+this.fm.version+'<br clear="all"/>'
+'<p><strong><a href="http://www.elrte.ru" target="_blank">'+this.fm.i18n('Donate to support project development')+'</a></strong></p>'
+ '<p><a href="http://dev.std42.ru/redmine/wiki/elfinder/" target="_blank">'+this.fm.i18n('elFinder documentation')+'</a></p>';
h += '<p>'+(ht != 'helpText' ? ht : 'elFinder works similar to file manager on your computer. <br /> To make actions on files/folders use icons on top panel. If icon action it is not clear for you, hold mouse cursor over it to see the hint. <br /> Manipulations with existing files/folders can be done through the context menu (mouse right-click).<br/> To copy/delete a group of files/folders, select them using Shift/Alt(Command) + mouse left-click.')+'</p>';
h += '<p>'
+ '<strong>'+this.fm.i18n('elFinder support following shortcuts')+':</strong><ul>'
+ '<li><kbd>Ctrl+A</kbd> - '+this.fm.i18n('Select all files')+'</li>'
+ '<li><kbd>Ctrl+C/Ctrl+X/Ctrl+V</kbd> - '+this.fm.i18n('Copy/Cut/Paste files')+'</li>'
+ '<li><kbd>Enter</kbd> - '+this.fm.i18n('Open selected file/folder')+'</li>'
+ '<li><kbd>Space</kbd> - '+this.fm.i18n('Open/close QuickLook window')+'</li>'
+ '<li><kbd>Delete/Cmd+Backspace</kbd> - '+this.fm.i18n('Remove selected files')+'</li>'
+ '<li><kbd>Ctrl+I</kbd> - '+this.fm.i18n('Selected files or current directory info')+'</li>'
+ '<li><kbd>Ctrl+N</kbd> - '+this.fm.i18n('Create new directory')+'</li>'
+ '<li><kbd>Ctrl+U</kbd> - '+this.fm.i18n('Open upload files form')+'</li>'
+ '<li><kbd>Left arrow</kbd> - '+this.fm.i18n('Select previous file')+'</li>'
+ '<li><kbd>Right arrow </kbd> - '+this.fm.i18n('Select next file')+'</li>'
+ '<li><kbd>Ctrl+Right arrow</kbd> - '+this.fm.i18n('Open selected file/folder')+'</li>'
+ '<li><kbd>Ctrl+Left arrow</kbd> - '+this.fm.i18n('Return into previous folder')+'</li>'
+ '<li><kbd>Shift+arrows</kbd> - '+this.fm.i18n('Increase/decrease files selection')+'</li></ul>'
+ '</p><p>'
+ this.fm.i18n('Contacts us if you need help integrating elFinder in you products')+': dev@std42.ru</p>';
a = '<div class="el-finder-help-std"/>'
+'<p>'+this.fm.i18n('Javascripts/PHP programming: Dmitry (dio) Levashov, dio@std42.ru')+'</p>'
+'<p>'+this.fm.i18n('Python programming, techsupport: Troex Nevelin, troex@fury.scancode.ru')+'</p>'
+'<p>'+this.fm.i18n('Design: Valentin Razumnih')+'</p>'
+'<p>'+this.fm.i18n('Spanish localization')+': Alex (xand) Vavilin, xand@xand.es, <a href="http://xand.es" target="_blank">http://xand.es</a></p>'
+'<p>'+this.fm.i18n('Icons')+': <a href="http://www.famfamfam.com/lab/icons/silk/" target="_blank">Famfam silk icons</a>, <a href="http://www.fatcow.com/free-icons/" target="_blank">Fatcow icons</a>'+'</p>'
+'<p>'+this.fm.i18n('Copyright: <a href="http://www.std42.ru" target="_blank">Studio 42 LTD</a>')+'</p>'
+'<p>'+this.fm.i18n('License: BSD License')+'</p>'
+'<p>'+this.fm.i18n('Web site: <a href="http://www.elrte.ru/elfinder/" target="_blank">elrte.ru</a>')+'</p>';
s = '<div class="el-finder-logo"/><strong><a href="http://www.eldorado-cms.ru" target="_blank">ELDORADO.CMS</a></strong><br/>'
+this.fm.i18n('Simple and usefull Content Management System')
+'<hr/>'
+ this.fm.i18n('Support project development and we will place here info about you');
tabs = '<ul><li><a href="#el-finder-help-h">'+this.fm.i18n('Help')+'</a></li><li><a href="#el-finder-help-a">'+this.fm.i18n('Authors')+'</a><li><a href="#el-finder-help-sp">'+this.fm.i18n('Sponsors')+'</a></li></ul>'
+'<div id="el-finder-help-h"><p>'+h+'</p></div>'
+'<div id="el-finder-help-a"><p>'+a+'</p></div>'
+'<div id="el-finder-help-sp"><p>'+s+'</p></div>';
var d = $('<div/>').html(tabs).dialog({
width : 617,
title : this.fm.i18n('Help'),
dialogClass : 'el-finder-dialog',
modal : true,
close : function() { d.tabs('destroy').dialog('destroy').remove() },
buttons : {
Ok : function() { $(this).dialog('close'); }
}
}).tabs()
}
this.cm = function(t) {
return t == 'cwd';
}
}
}
})(jQuery);
/**
* @class Create quick look window (similar to MacOS X Quick Look)
* @author dio dio@std42.ru
**/
(function($) {
elFinder.prototype.quickLook = function(fm, el) {
var self = this;
this.fm = fm;
this._hash = '';
this.title = $('<strong/>');
this.ico = $('<p/>');
this.info = $('<label/>');
this.media = $('<div class="el-finder-ql-media"/>').hide()
this.name = $('<span class="el-finder-ql-name"/>')
this.kind = $('<span class="el-finder-ql-kind"/>')
this.size = $('<span class="el-finder-ql-size"/>')
this.date = $('<span class="el-finder-ql-date"/>')
this.url = $('<a href="#"/>').hide().click(function(e) {
e.preventDefault();
window.open($(this).attr('href'));
self.hide();
});
this.add = $('<div/>')
this.content = $('<div class="el-finder-ql-content"/>')
this.win = $('<div class="el-finder-ql"/>').hide()
.append($('<div class="el-finder-ql-drag-handle"/>').append($('<span class="ui-icon ui-icon-circle-close"/>').click(function() { self.hide(); })).append(this.title))
.append(this.ico)
.append(this.media)
.append(this.content.append(this.name).append(this.kind).append(this.size).append(this.date).append(this.url).append(this.add))
.appendTo(document.body)
.draggable({handle : '.el-finder-ql-drag-handle'})
.resizable({
minWidth : 420,
minHeight : 150,
resize : function() {
if (self.media.children().length) {
var t = self.media.children(':first');
switch (t[0].nodeName) {
case 'IMG':
var w = t.width(),
h = t.height(),
_w = self.win.width(),
_h = self.win.css('height') == 'auto' ? 350 : self.win.height()-self.content.height()-self.th,
r = w>_w || h>_h
? Math.min(Math.min(_w, w)/w, Math.min(_h, h)/h)
: Math.min(Math.max(_w, w)/w, Math.max(_h, h)/h);
t.css({
width : Math.round(t.width()*r),
height : Math.round(t.height()*r)
})
break;
case 'IFRAME':
case 'EMBED':
t.css('height', self.win.height()-self.content.height()-self.th);
break;
case 'OBJECT':
t.children('embed').css('height', self.win.height()-self.content.height()-self.th);
}
}
}
});
this.th = parseInt(this.win.children(':first').css('height'))||18;
/* All browsers do it, but some is shy to says about it. baka da ne! */
this.mimes = {
'image/jpeg' : 'jpg',
'image/gif' : 'gif',
'image/png' : 'png'
};
for (var i=0; i<navigator.mimeTypes.length; i++) {
var t = navigator.mimeTypes[i].type;
if (t && t != '*') {
this.mimes[t] = navigator.mimeTypes[i].suffixes;
}
}
if (($.browser.safari && navigator.platform.indexOf('Mac') != -1) || $.browser.msie) {
/* not booletproof, but better then nothing */
this.mimes['application/pdf'] = 'pdf';
} else {
for (var n=0; n < navigator.plugins.length; n++) {
for (var m=0; m < navigator.plugins[n].length; m++) {
var e = navigator.plugins[n][m].description.toLowerCase();
if (e.substring(0, e.indexOf(" ")) == 'pdf') {
this.mimes['application/pdf'] = 'pdf';
break;
}
}
}
}
if (this.mimes['image/x-bmp']) {
this.mimes['image/x-ms-bmp'] = 'bmp';
}
if ($.browser.msie && !this.mimes['application/x-shockwave-flash']) {
this.mimes['application/x-shockwave-flash'] = 'swf';
}
// self.fm.log(this.mimes)
/**
* Open quickLook window
**/
this.show = function() {
if (this.win.is(':hidden') && self.fm.selected.length == 1) {
update();
var id = self.fm.selected[0],
el = self.fm.view.cwd.find('[key="'+id+'"]'),
o = el.offset();
self.fm.lockShortcuts(true);
this.win.css({
width : el.width()-20,
height : el.height(),
left : o.left,
top : o.top,
opacity : 0
}).animate({
width : 420,
height : 150,
opacity : 1,
top : Math.round($(window).height()/5),
left : $(window).width()/2-210
}, 450, function() {
self.win.css({height: 'auto'});
self.fm.lockShortcuts();
});
}
}
/**
* Close quickLook window
**/
this.hide = function() {
if (this.win.is(':visible')) {
var o, el = self.fm.view.cwd.find('[key="'+this._hash+'"]');
if (el) {
o = el.offset();
this.media.hide(200)//.empty()
this.win.animate({
width : el.width()-20,
height : el.height(),
left : o.left,
top : o.top,
opacity : 0
}, 350, function() {
self.fm.lockShortcuts();
reset();
self.win.hide().css('height', 'auto');
});
} else {
this.win.fadeOut(200);
reset();
self.fm.lockShortcuts();
}
}
}
/**
* Open/close quickLook window
**/
this.toggle = function() {
if (this.win.is(':visible')) {
this.hide();
} else {
this.show();
}
}
/**
* Update quickLook window content if only one file selected,
* otherwise close window
**/
this.update = function() {
if (this.fm.selected.length != 1) {
this.hide();
} else if (this.win.is(':visible') && this.fm.selected[0] != this._hash) {
update();
}
}
/**
* Return height of this.media block
* @return Number
**/
this.mediaHeight = function() {
return this.win.is(':animated') || this.win.css('height') == 'auto' ? 315 : this.win.height()-this.content.height()-this.th;
}
/**
* Clean quickLook window DOM elements
**/
function reset() {
self.media.hide().empty();
self.win.attr('class', 'el-finder-ql').css('z-index', self.fm.zIndex);
self.title.empty();
self.ico.removeAttr('style').show();
self.add.hide().empty();
self._hash = '';
}
/**
* Update quickLook window content
**/
function update() {
var f = self.fm.getSelected(0);
reset();
self._hash = f.hash;
self.title.text(f.name);
self.win.addClass(self.fm.view.mime2class(f.mime));
self.name.text(f.name);
self.kind.text(self.fm.view.mime2kind(f.link ? 'symlink' : f.mime));
self.size.text(self.fm.view.formatSize(f.size));
self.date.text(self.fm.i18n('Modified')+': '+self.fm.view.formatDate(f.date));
f.dim && self.add.append('<span>'+f.dim+' px</span>').show();
f.tmb && self.ico.css('background', 'url("'+f.tmb+'") 0 0 no-repeat');
if (f.url) {
self.url.text(f.url).attr('href', f.url).show();
for (var i in self.plugins) {
if (self.plugins[i].test && self.plugins[i].test(f.mime, self.mimes, f.name)) {
self.plugins[i].show(self, f);
return;
}
}
} else {
self.url.hide();
}
self.win.css({
width : '420px',
height : 'auto'
});
}
}
elFinder.prototype.quickLook.prototype.plugins = {
image : new function() {
this.test = function(mime, mimes) {
return mime.match(/^image\//);
}
this.show = function(ql, f) {
var url, t;
if (ql.mimes[f.mime] && f.hash == ql._hash) {
$('<img/>').hide().appendTo(ql.media.show()).attr('src', f.url+($.browser.msie || $.browser.opera ? '?'+Math.random() : '')).load(function() {
t = $(this).unbind('load');
if (f.hash == ql._hash) {
ql.win.is(':animated') ? setTimeout(function() { preview(t); }, 330) : preview(t);
}
});
}
function preview(img) {
var w = img.width(),
h = img.height(),
a = ql.win.is(':animated'),
_w = a ? 420 : ql.win.width(),
_h = a || ql.win.css('height') == 'auto' ? 315 : ql.win.height()-ql.content.height()-ql.th,
r = w>_w || h>_h
? Math.min(Math.min(_w, w)/w, Math.min(_h, h)/h)
: Math.min(Math.max(_w, w)/w, Math.max(_h, h)/h);
ql.fm.lockShortcuts(true);
ql.ico.hide();
img.css({
width : ql.ico.width(),
height : ql.ico.height()
}).animate({
width : Math.round(r*w),
height : Math.round(r*h)
}, 450, function() {
ql.fm.lockShortcuts();
});
}
}
},
text : new function() {
this.test = function(mime, mimes) {
return (mime.indexOf('text') == 0 && mime.indexOf('rtf') == -1) || mime.match(/application\/(xml|javascript|json)/);
}
this.show = function(ql, f) {
if (f.hash == ql._hash) {
ql.ico.hide();
ql.media.append('<iframe src="'+f.url+'" style="height:'+ql.mediaHeight()+'px" />').show();
}
}
},
swf : new function() {
this.test = function(mime, mimes) {
return mime == 'application/x-shockwave-flash' && mimes[mime];
}
this.show = function(ql, f) {
if (f.hash == ql._hash) {
ql.ico.hide();
// ql.media.append('<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="quality" value="high" /><param name="movie" value="'+url+'" /><embed pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="'+url+'" type="application/x-shockwave-flash" style="width:100%;height:'+ql.mediaHeight()+'px"></embed></object>')
// .slideDown(400);
var e = ql.media.append('<embed pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" src="'+f.url+'" style="width:100%;height:'+ql.mediaHeight()+'px" type="application/x-shockwave-flash" />');
if (ql.win.is(':animated')) {
e.slideDown(450)
} else {
e.show()
}
}
}
},
audio : new function() {
this.test = function(mime, mimes) {
return mime.indexOf('audio') == 0 && mimes[mime];
}
this.show = function(ql, f) {
if (f.hash == ql._hash) {
ql.ico.hide();
var h = ql.win.is(':animated') || ql.win.css('height') == 'auto' ? 100 : ql.win.height()-ql.content.height()-ql.th;
ql.media.append('<embed src="'+f.url+'" style="width:100%;height:'+h+'px" />').show();
}
}
},
video : new function() {
this.test = function(mime, mimes) {
return mime.indexOf('video') == 0 && mimes[mime];
}
this.show = function(ql, f) {
if (f.hash == ql._hash) {
ql.ico.hide();
ql.media.append('<embed src="'+f.url+'" style="width:100%;height:'+ql.mediaHeight()+'px" />').show();
}
}
},
pdf : new function() {
this.test = function(mime, mimes) {
return mime == 'application/pdf' && mimes[mime];
}
this.show = function(ql, f) {
if (f.hash == ql._hash) {
ql.ico.hide();
ql.media.append('<embed src="'+f.url+'" style="width:100%;height:'+ql.mediaHeight()+'px" />').show();
}
}
}
}
})(jQuery);/**
* @class Bind/update events
* @author dio dio@std42.ru
**/
(function($) {
elFinder.prototype.eventsManager = function(fm, el) {
var self = this;
this.lock = false;
this.fm = fm;
this.ui = fm.ui;
this.tree = fm.view.tree
this.cwd = fm.view.cwd;
this.pointer = '';
/**
* Initial events binding
*
**/
this.init = function() {
this.cwd
.bind('click', function(e) {
var t = $(e.target);
if (t.hasClass('ui-selected')) {
self.fm.unselectAll();
} else {
if (!t.attr('key')) {
t = t.parent('[key]');
}
if (e.ctrlKey || e.metaKey) {
self.fm.toggleSelect(t);
} else {
self.fm.select(t, true);
}
}
})
.bind(window.opera?'click':'contextmenu', function(e) {
if (window.opera && !e.ctrlKey) {
return;
}
var t = $(e.target);
e.preventDefault();
e.stopPropagation()
if (t.hasClass('el-finder-cwd')) {
self.fm.unselectAll();
} else {
self.fm.select(t.attr('key') ? t : t.parent('[key]'));
}
self.fm.quickLook.hide();
self.fm.ui.showMenu(e);
})
.selectable({
filter : '[key]',
delay : 300,
stop : function() { self.fm.updateSelect() }
});
$(document).bind('click', function() {
self.fm.ui.hideMenu();
$('input', self.cwd).trigger('change');
});
/* open parents dir in tree */
this.tree.bind('select', function(e) {
self.tree.find('a').removeClass('selected');
$(e.target).addClass('selected').parents('li:has(ul)').children('ul').show().prev().children('div').addClass('expanded');
});
/* make places droppable */
if (this.fm.options.places) {
this.fm.view.plc.click(function(e) {
e.preventDefault();
var t = $(e.target),
h = t.attr('key'), ul;
if (h) {
h != self.fm.cwd.hash && self.ui.exec('open', e.target)
} else if (e.target.nodeName == 'A' || e.target.nodeName == 'DIV') {
ul = self.fm.view.plc.find('ul');
if (ul.children().length) {
ul.toggle(300);
self.fm.view.plc.children('li').find('div').toggleClass('expanded');
}
}
});
this.fm.view.plc.droppable({
accept : '(div,tr).directory',
tolerance : 'pointer',
over : function() { $(this).addClass('el-finder-droppable'); },
out : function() { $(this).removeClass('el-finder-droppable'); },
drop : function(e, ui) {
$(this).removeClass('el-finder-droppable');
var upd = false;
/* accept only folders with read access */
ui.helper.children('.directory:not(.noaccess,.dropbox)').each(function() {
if (self.fm.addPlace($(this).attr('key'))) {
upd = true;
$(this).hide();
}
});
/* update places id's and view */
if (upd) {
self.fm.view.renderPlaces();
self.updatePlaces();
self.fm.view.plc.children('li').children('div').trigger('click');
}
/* hide helper if empty */
if (!ui.helper.children('div:visible').length) {
ui.helper.hide();
}
}
});
}
/* bind shortcuts */
$(document).bind($.browser.mozilla || $.browser.opera ? 'keypress' : 'keydown', function(e) {
var meta = e.ctrlKey||e.metaKey;
if (self.lock) {
return;
}
switch(e.keyCode) {
/* arrows left/up. with Ctrl - exec "back", w/o - move selection */
case 37:
case 38:
e.stopPropagation();
e.preventDefault();
if (e.keyCode == 37 && meta) {
self.ui.execIfAllowed('back');
} else {
moveSelection(false, !e.shiftKey);
}
break;
/* arrows right/down. with Ctrl - exec "open", w/o - move selection */
case 39:
case 40:
e.stopPropagation();
e.preventDefault();
if (meta) {
self.ui.execIfAllowed('open');
} else {
moveSelection(true, !e.shiftKey);
}
break;
}
});
$(document).bind($.browser.opera ? 'keypress' : 'keydown', function(e) {
if (self.lock) {
return;
}
switch(e.keyCode) {
/* Space - QuickLook */
case 32:
e.preventDefault();
e.stopPropagation();
self.fm.quickLook.toggle();
break;
/* Esc */
case 27:
self.fm.quickLook.hide();
break;
}
});
if (!this.fm.options.disableShortcuts) {
$(document).bind('keydown', function(e) {
var meta = e.ctrlKey||e.metaKey;
if (self.lock) {
return;
}
switch (e.keyCode) {
/* Meta+Backspace - delete */
case 8:
if (meta && self.ui.isCmdAllowed('rm')) {
e.preventDefault();
self.ui.exec('rm');
}
break;
/* Enter - exec "select" command if enabled, otherwise exec "open" */
case 13:
if (self.ui.isCmdAllowed('select')) {
return self.ui.exec('select');
}
self.ui.execIfAllowed('open');
break;
/* Delete */
case 46:
self.ui.execIfAllowed('rm');
break;
/* Ctrl+A */
case 65:
if (meta) {
e.preventDefault();
self.fm.selectAll();
}
break;
/* Ctrl+C */
case 67:
meta && self.ui.execIfAllowed('copy');
break;
/* Ctrl+I - get info */
case 73:
if (meta) {
e.preventDefault();
self.ui.exec('info');
}
break;
/* Ctrl+N - new folder */
case 78:
if (meta) {
e.preventDefault();
self.ui.execIfAllowed('mkdir');
}
break;
/* Ctrl+U - upload files */
case 85:
if (meta) {
e.preventDefault();
self.ui.execIfAllowed('upload');
}
break;
/* Ctrl+V */
case 86:
meta && self.ui.execIfAllowed('paste');
break;
/* Ctrl+X */
case 88:
meta && self.ui.execIfAllowed('cut');
break;
}
});
}
}
/**
* Update navigation droppable/draggable
*
**/
this.updateNav = function() {
$('a', this.tree).click(function(e) {
e.preventDefault();
var t = $(this), c;
if (e.target.nodeName == 'DIV' && $(e.target).hasClass('collapsed')) {
$(e.target).toggleClass('expanded').parent().next('ul').toggle(300);
} else if (t.attr('key') != self.fm.cwd.hash) {
self.ui.exec('open', t.trigger('select')[0]);
} else {
c = t.children('.collapsed');
if (c.length) {
c.toggleClass('expanded');
t.next('ul').toggle(300);
}
}
})
$('a:not(.noaccess,.readonly)', this.tree).droppable({
tolerance : 'pointer',
accept : '(div,tr)[key]',
over : function() { $(this).addClass('el-finder-droppable'); },
out : function() { $(this).removeClass('el-finder-droppable'); },
drop : function(e, ui) { $(this).removeClass('el-finder-droppable'); self.fm.drop(e, ui, $(this).attr('key')); }
});
this.fm.options.places && this.updatePlaces();
}
/**
* Update places draggable
*
**/
this.updatePlaces = function() {
this.fm.view.plc.children('li').find('li').draggable({
scroll : false,
stop : function() {
if (self.fm.removePlace($(this).children('a').attr('key'))) {
$(this).remove();
if (!$('li', self.fm.view.plc.children('li')).length) {
self.fm.view.plc.children('li').find('div').removeClass('collapsed expanded').end().children('ul').hide();
}
}
}
});
}
/**
* Update folders droppable & files/folders draggable
**/
this.updateCwd = function() {
$('[key]', this.cwd)
.bind('dblclick', function(e) {
self.fm.select($(this), true);
self.ui.exec(self.ui.isCmdAllowed('select') ? 'select' : 'open');
})
.draggable({
delay : 3,
addClasses : false,
appendTo : '.el-finder-cwd',
revert : true,
drag : function(e, ui) {
ui.helper.toggleClass('el-finder-drag-copy', e.shiftKey||e.ctrlKey);
},
helper : function() {
var t = $(this),
h = $('<div class="el-finder-drag-helper"/>'),
c = 0;
!t.hasClass('ui-selected') && self.fm.select(t, true);
self.cwd.find('.ui-selected').each(function(i) {
var el = self.fm.options.view == 'icons' ? $(this).clone().removeClass('ui-selected') : $(self.fm.view.renderIcon(self.fm.cdc[$(this).attr('key')]))
if (c++ == 0 || c%12 == 0) {
el.css('margin-left', 0);
}
h.append(el);
});
return h.css('width', (c<=12 ? 85+(c-1)*29 : 387)+'px');
}
})
.filter('.directory')
.droppable({
tolerance : 'pointer',
accept : '(div,tr)[key]',
over : function() { $(this).addClass('el-finder-droppable'); },
out : function() { $(this).removeClass('el-finder-droppable'); },
drop : function(e, ui) { $(this).removeClass('el-finder-droppable'); self.fm.drop(e, ui, $(this).attr('key')); }
});
if ($.browser.msie) {
$('*', this.cwd).attr('unselectable', 'on')
.filter('[key]')
.bind('dragstart', function() { self.cwd.selectable('disable').removeClass('ui-state-disabled ui-selectable-disabled'); })
.bind('dragstop', function() { self.cwd.selectable('enable'); });
}
}
/**
* Move selection in current dir
*
* @param Boolean move forward?
* @param Boolean clear current selection?
**/
function moveSelection(forward, reset) {
var p, _p, cur;
if (!$('[key]', self.cwd).length) {
return;
}
if (self.fm.selected.length == 0) {
p = $('[key]:'+(forward ? 'first' : 'last'), self.cwd);
self.fm.select(p);
} else if (reset) {
p = $('.ui-selected:'+(forward ? 'last' : 'first'), self.cwd);
_p = p[forward ? 'next' : 'prev']('[key]');
if (_p.length) {
p = _p;
}
self.fm.select(p, true);
} else {
if (self.pointer) {
cur = $('[key="'+self.pointer+'"].ui-selected', self.cwd);
}
if (!cur || !cur.length) {
cur = $('.ui-selected:'+(forward ? 'last' : 'first'), self.cwd);
}
p = cur[forward ? 'next' : 'prev']('[key]');
if (!p.length) {
p = cur;
} else {
if (!p.hasClass('ui-selected')) {
self.fm.select(p);
} else {
if (!cur.hasClass('ui-selected')) {
self.fm.unselect(p);
} else {
_p = cur[forward ? 'prev' : 'next']('[key]')
if (!_p.length || !_p.hasClass('ui-selected')) {
self.fm.unselect(cur);
} else {
while ((_p = forward ? p.next('[key]') : p.prev('[key]')) && p.hasClass('ui-selected')) {
p = _p;
}
self.fm.select(p);
}
}
}
}
}
self.pointer = p.attr('key');
self.fm.checkSelectedPos(forward);
}
}
})(jQuery);
\ No newline at end of file
<!doctype html><html><head><meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"><title>Google</title><script>window.google={kEI:"k9KXS8-NG8iQ-Aau-rnpDA",kEXPI:"24030,24059",kCSI:{e:"24030,24059",ei:"k9KXS8-NG8iQ-Aau-rnpDA",expi:"24030,24059"},ml:function(){},kHL:"en",time:function(){return(new Date).getTime()},log:function(b,d,c){var a=new Image,e=google,g=e.lc,f=e.li;a.onerror=(a.onload=(a.onabort=function(){delete g[f]}));g[f]=a;c=c||"/gen_204?atyp=i&ct="+b+"&cad="+d+"&zx="+google.time();a.src=c;e.li=f+1},lc:[],li:0,Toolbelt:{}};
window.google.sn="webhp";window.google.timers={load:{t:{start:(new Date).getTime()}}};try{}catch(u){}window.google.jsrt_kill=1;
var _gjwl=location;function _gjuc(){var e=_gjwl.href.indexOf("#");if(e>=0){var a=_gjwl.href.substring(e);if(a.indexOf("&q=")>0||a.indexOf("#q=")>=0){a=a.substring(1);if(a.indexOf("#")==-1){for(var c=0;c<a.length;){var d=c;if(a.charAt(d)=="&")++d;var b=a.indexOf("&",d);if(b==-1)b=a.length;var f=a.substring(d,b);if(f.indexOf("fp=")==0){a=a.substring(0,c)+a.substring(b,a.length);b=c}else if(f=="cad=h")return 0;c=b}_gjwl.href="/search?"+a+"&cad=h";return 1}}}return 0}function _gjp(){!(window._gjwl.hash&&
window._gjuc())&&setTimeout(_gjp,500)};
window._gjp && _gjp()</script><style>td{line-height:.8em;}.gac_m td{line-height:17px;}form{margin-bottom:20px;}body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#36c;font-size:20px}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}em{font-weight:bold;font-style:normal}.lst{font:17px arial,sans-serif;margin-bottom:.2em;vertical-align:bottom;}input{font-family:inherit}.lsb,.gac_sb{font-size:15px;height:1.85em!important;margin:.2em;}#gbar{height:22px}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}#guser{padding-bottom:7px !important;text-align:right}#gbar,#guser{font-size:13px;padding-top:1px !important}@media all{.gb1{height:22px;margin-right:.5em;vertical-align:top}#gbar{float:left}}a.gb1,a.gb4{color:#00c !important}</style><script>google.y={};google.x=function(e,g){google.y[e.id]=[e,g];return false};</script></head><body bgcolor=#ffffff text=#000000 link=#0000cc vlink=#551a8b alink=#ff0000 onload="document.f.q.focus();if(document.images)new Image().src='/images/nav_logo7.png'" topmargin=3 marginheight=3><textarea id=csi style=display:none></textarea><iframe name=wgjf style=display:none></iframe><div id=ghead><div id=gbar><nobr><b class=gb1>Web</b> <a onclick=gbar.qs(this) href="http://images.google.com/imghp?hl=en&tab=wi" class=gb1>Images</a> <a onclick=gbar.qs(this) href="http://video.google.com/?hl=en&tab=wv" class=gb1>Videos</a> <a onclick=gbar.qs(this) href="http://maps.google.com/maps?hl=en&tab=wl" class=gb1>Maps</a> <a onclick=gbar.qs(this) href="http://news.google.com/nwshp?hl=en&tab=wn" class=gb1>News</a> <a onclick=gbar.qs(this) href="http://www.google.com/prdhp?hl=en&tab=wf" class=gb1>Shopping</a> <a href="http://mail.google.com/mail/?hl=en&tab=wm" class=gb1>Gmail</a> <a href="http://www.google.com/intl/en/options/" class=gb1 style="text-decoration:none"><u>more</u> &raquo;</a></nobr></div><div id=guser width=100%><nobr><a href="/url?sa=p&pref=ig&pval=3&q=http://www.google.com/ig%3Fhl%3Den%26source%3Diglk&usg=AFQjCNFA18XPfgb7dKnXfKz7x7g1GDH1tg" class=gb4>iGoogle</a> | <a href="/preferences?hl=en" class=gb4>Search settings</a> | <a href="https://www.google.com/accounts/Login?hl=en&continue=http://www.google.com/intl/en/" class=gb4>Sign in</a></nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div></div> <center><br clear=all id=lgpd><img alt="Google" height=110 src="http://www.google.com/intl/en_ALL/images/logo.gif" width=276 id=logo onload="window.lol&&lol()"><br><br><form action="http://www.google.com/search" name=f><table cellpadding=0 cellspacing=0><tr valign=top><td width=25%>&nbsp;</td><td align=center nowrap><input name=hl type=hidden value=en><input name=source type=hidden value=hp><input type=hidden name=ie value="ISO-8859-1"><input autocomplete="off" maxlength=2048 name=q size=55 class=lst title="Google Search" value=""><br><input name=btnG type=submit value="Google Search" class=lsb><input name=btnI type=submit value="I&#39;m Feeling Lucky" class=lsb></td><td nowrap width=25% align=left><font size=-2>&nbsp;&nbsp;<a href="/advanced_search?hl=en">Advanced Search</a><br>&nbsp;&nbsp;<a href="/language_tools?hl=en">Language Tools</a></font></td></tr></table></form><br><span id=footer><center id=fctr><br><font size=-1><a href="/intl/en/ads/">Advertising&nbsp;Programs</a> - <a href="/services/">Business Solutions</a> - <a href="/intl/en/about.html">About Google</a> - <b><a href="http://www.google.ru/">Go to Google Russia</a></b></font><p><font size=-2>&copy;2010 - <a href="/intl/en/privacy.html">Privacy</a></font></p></center></span> <div id=xjsd></div><div id=xjsi><script>if(google.y)google.y.first=[];if(google.y)google.y.first=[];google.dstr=[];google.rein=[];window.setTimeout(function(){var a=document.createElement("script");a.src="/extern_js/f/CgJlbiAAKzAKOF1ACCwrMA44DCwrMBc4BiwrMBg4BSwrMCU4yogBLCswJjgJLCswJzgELCswPDgCLCswQDgLLCswRDgCLCswRTgBLA/OamgJXCMX7I.js";(document.getElementById("xjsd")||document.body).appendChild(a);if(google.timers&&google.timers.load.t)google.timers.load.t.xjsls=(new Date).getTime();},0);
;google.neegg=1;google.y.first.push(function(){google.ac.m=1;google.ac.b=true;google.ac.i(document.f,document.f.q,'','','',{o:1});google.History&&google.History.initialize('/intl/en/')});if(google.j&&google.j.en&&google.j.xi){window.setTimeout(google.j.xi,0);google.fade=null;}</script></div><script>(function(){
var b,d,e,f;function g(a,c){if(a.removeEventListener){a.removeEventListener("load",c,false);a.removeEventListener("error",c,false)}else{a.detachEvent("onload",c);a.detachEvent("onerror",c)}}function h(a){f=(new Date).getTime();++d;a=a||window.event;var c=a.target||a.srcElement;g(c,h)}var i=document.getElementsByTagName("img");b=i.length;d=0;for(var j=0,k;j<b;++j){k=i[j];g(k,h);if(k.complete||typeof k.src!="string"||!k.src)++d;else if(k.addEventListener){k.addEventListener("load",h,false);k.addEventListener("error",
h,false)}else{k.attachEvent("onload",h);k.attachEvent("onerror",h)}}e=b-d;function l(){google.timers.load.t.ol=(new Date).getTime();google.timers.load.t.iml=f;google.kCSI.imc=d;google.kCSI.imn=b;google.kCSI.imp=e;google.report&&google.report(google.timers.load,google.kCSI)}if(window.addEventListener)window.addEventListener("load",l,false);else if(window.attachEvent)window.attachEvent("onload",l);google.timers.load.t.prt=(f=(new Date).getTime());
})();
</script>
#include "config.h"
#ifdef HAVE_SYS_CDEFS_H
#include <sys/cdefs.h>
#ifdef __RCSID
__RCSID("$Id: milter-greylist.c,v 1.137.2.7 2006/11/07 05:12:11 manu Exp $");
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <stdarg.h>
/* On IRIX, <unistd.h> defines a EX_OK that clashes with <sysexits.h> */
#ifdef EX_OK
#undef EX_OK
#endif
#include <sysexits.h>
#if HAVE_GETOPT_H
#include <getopt.h>
#endif
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#ifdef USE_DRAC
#ifdef USE_DB185_EMULATION
#include <db_185.h>
#else
#include <db.h>
#endif
static int check_drac(char *dotted_ip);
#endif
#include <libmilter/mfapi.h>
#include "dump.h"
#include "acl.h"
#include "list.h"
#include "conf.h"
#include "pending.h"
#include "sync.h"
#include "spf.h"
#include "autowhite.h"
#include "milter-greylist.h"
#ifdef USE_DNSRBL
#include "dnsrbl.h"
#endif
#include "macro.h"
static char *gmtoffset(time_t *, char *, size_t);
static void writepid(char *);
static void log_and_report_greylisting(SMFICTX *, struct mlfi_priv *, char *);
static void reset_acl_values(struct mlfi_priv *);
struct smfiDesc smfilter =
{
"greylist", /* filter name */
SMFI_VERSION, /* version code */
SMFIF_ADDHDRS, /* flags */
mlfi_connect, /* connection info filter */
MLFI_HELO, /* SMTP HELO command filter */
mlfi_envfrom, /* envelope sender filter */
mlfi_envrcpt, /* envelope recipient filter */
NULL, /* header filter */
NULL, /* end of header */
NULL, /* body block filter */
mlfi_eom, /* end of message */
NULL, /* message aborted */
mlfi_close, /* connection cleanup */
};
sfsistat
mlfi_connect(ctx, hostname, addr)
SMFICTX *ctx;
char *hostname;
_SOCK_ADDR *addr;
{
struct mlfi_priv *priv;
if ((priv = malloc(sizeof(*priv))) == NULL)
return SMFIS_TEMPFAIL;
smfi_setpriv(ctx, priv);
bzero((void *)priv, sizeof(*priv));
priv->priv_whitelist = EXF_UNSET;
strncpy(priv->priv_hostname, hostname, ADDRLEN);
priv->priv_hostname[ADDRLEN] = '\0';
if (addr != NULL) {
switch (addr->sa_family) {
case AF_INET:
priv->priv_addrlen = sizeof(struct sockaddr_in);
memcpy(&priv->priv_addr, addr, priv->priv_addrlen);
#ifdef HAVE_SA_LEN
/* XXX: sendmail doesn't set sa_len */
SA4(&priv->priv_addr)->sin_len = priv->priv_addrlen;
#endif
break;
#ifdef AF_INET6
case AF_INET6:
priv->priv_addrlen = sizeof(struct sockaddr_in6);
memcpy(&priv->priv_addr, addr, priv->priv_addrlen);
#ifdef SIN6_LEN
/* XXX: sendmail doesn't set sa_len */
SA6(&priv->priv_addr)->sin6_len = priv->priv_addrlen;
#endif
unmappedaddr(SA(&priv->priv_addr),
&priv->priv_addrlen);
break;
#endif
default:
priv->priv_elapsed = 0;
priv->priv_whitelist = EXF_WHITELIST | EXF_NONIP;
break;
}
} else {
priv->priv_elapsed = 0;
priv->priv_whitelist = EXF_WHITELIST | EXF_NONIP;
}
return SMFIS_CONTINUE;
}
sfsistat
mlfi_helo(ctx, helostr)
SMFICTX *ctx;
char *helostr;
{
struct mlfi_priv *priv;
priv = (struct mlfi_priv *) smfi_getpriv(ctx);
#if (defined(HAVE_SPF) || defined(HAVE_SPF_ALT) || \
defined(HAVE_SPF2_10) || defined(HAVE_SPF2))
strncpy_rmsp(priv->priv_helo, helostr, ADDRLEN);
priv->priv_helo[ADDRLEN] = '\0';
#endif
return SMFIS_CONTINUE;
}
sfsistat
mlfi_envfrom(ctx, envfrom)
SMFICTX *ctx;
char **envfrom;
{
char tmpfrom[ADDRLEN + 1];
char *idx;
struct mlfi_priv *priv;
char *auth_authen;
char *verify;
char *cert_subject;
priv = (struct mlfi_priv *) smfi_getpriv(ctx);
if ((priv->priv_queueid = smfi_getsymval(ctx, "{i}")) == NULL) {
mg_log(LOG_DEBUG, "smfi_getsymval failed for {i}");
priv->priv_queueid = "(unknown id)";
}
/*
* Strip spaces from the source address
*/
strncpy_rmsp(tmpfrom, *envfrom, ADDRLEN);
tmpfrom[ADDRLEN] = '\0';
/*
* Strip anything before the last '=' in the
* source address. This avoid problems with
* mailing lists using a unique sender address
* for each retry.
*/
if ((idx = rindex(tmpfrom, '=')) == NULL)
idx = tmpfrom;
strncpy(priv->priv_from, idx, ADDRLEN);
priv->priv_from[ADDRLEN] = '\0';
/*
* Reload the config file if it has been touched
*/
conf_update();
/*
* Is the sender non-IP?
*/
if (priv->priv_whitelist & EXF_NONIP)
return SMFIS_CONTINUE;
/*
* Is the user authenticated?
*/
if ((conf.c_noauth == 0) &&
((auth_authen = smfi_getsymval(ctx, "{auth_authen}")) != NULL)) {
mg_log(LOG_DEBUG,
"User %s authenticated, bypassing greylisting",
auth_authen);
priv->priv_elapsed = 0;
priv->priv_whitelist = EXF_WHITELIST | EXF_AUTH;
return SMFIS_CONTINUE;
}
/*
* STARTTLS authentication?
*/
if ((conf.c_noauth == 0) &&
((verify = smfi_getsymval(ctx, "{verify}")) != NULL) &&
(strcmp(verify, "OK") == 0) &&
((cert_subject = smfi_getsymval(ctx, "{cert_subject}")) != NULL)) {
mg_log(LOG_DEBUG,
"STARTTLS succeeded for DN=\"%s\", bypassing greylisting",
cert_subject);
priv->priv_elapsed = 0;
priv->priv_whitelist = EXF_WHITELIST | EXF_STARTTLS;
return SMFIS_CONTINUE;
}
/*
* Is the sender address SPF-compliant?
*/
if ((conf.c_nospf == 0) &&
(SPF_CHECK(SA(&priv->priv_addr), priv->priv_addrlen,
priv->priv_helo, *envfrom) != EXF_NONE)) {
char ipstr[IPADDRSTRLEN];
if (iptostring(SA(&priv->priv_addr),
priv->priv_addrlen, ipstr, sizeof(ipstr))) {
mg_log(LOG_DEBUG,
"Sender IP %s and address %s are SPF-compliant, "
"bypassing greylist", ipstr, *envfrom);
}
priv->priv_elapsed = 0;
priv->priv_whitelist = EXF_WHITELIST | EXF_SPF;
return SMFIS_CONTINUE;
}
return SMFIS_CONTINUE;
}
sfsistat
mlfi_envrcpt(ctx, envrcpt)
SMFICTX *ctx;
char **envrcpt;
{
struct mlfi_priv *priv;
time_t remaining;
char *greylist;
char addrstr[IPADDRSTRLEN];
char rcpt[ADDRLEN + 1];
priv = (struct mlfi_priv *) smfi_getpriv(ctx);
if (!iptostring(SA(&priv->priv_addr), priv->priv_addrlen, addrstr,
sizeof(addrstr)))
return SMFIS_CONTINUE;
if (conf.c_debug)
mg_log(LOG_DEBUG, "%s: addr = %s[%s], from = %s, rcpt = %s",
priv->priv_queueid, priv->priv_hostname, addrstr, priv->priv_from, *envrcpt);
/*
* For multiple-recipients messages, if the sender IP or the
* sender e-mail address is whitelisted, authenticated, or
* SPF compliant, then there is no need to check again,
* it is whitelisted for all the recipients.
*
* Moreover, this will prevent a wrong X-Greylist header display
* if the {IP, sender e-mail} address was whitelisted and the
* last recipient was also whitelisted. If we would set priv_whitelist
* on the last recipient, all recipient would have a X-Greylist
* header explaining that they were whitelisted, whereas some
* of them would not.
*/
if ((priv->priv_whitelist & EXF_ADDR) ||
(priv->priv_whitelist & EXF_DOMAIN) ||
(priv->priv_whitelist & EXF_FROM) ||
(priv->priv_whitelist & EXF_AUTH) ||
(priv->priv_whitelist & EXF_SPF) ||
(priv->priv_whitelist & EXF_NONIP) ||
(priv->priv_whitelist & EXF_DRAC) ||
(priv->priv_whitelist & EXF_ACCESSDB) ||
(priv->priv_whitelist & EXF_MACRO) ||
(priv->priv_whitelist & EXF_STARTTLS))
return SMFIS_CONTINUE;
#ifdef USE_DRAC
if ((SA(&priv->priv_addr)->sa_family == AF_INET) &&
(conf.c_nodrac == 0) &&
check_drac(addrstr)) {
mg_log(LOG_DEBUG, "whitelisted by DRAC");
priv->priv_elapsed = 0;
priv->priv_whitelist = EXF_DRAC;
return SMFIS_CONTINUE;
}
#endif
/*
* If sendmail rules have defined a ${greylist} macro
* with value WHITE, then it is whitelisted
*/
if ((conf.c_noaccessdb == 0) &&
((greylist = smfi_getsymval(ctx, "{greylist}")) != NULL) &&
(strcmp(greylist, "WHITE") == 0)) {
mg_log(LOG_DEBUG,
"whitelisted by {greylist}");
priv->priv_elapsed = 0;
priv->priv_whitelist = EXF_ACCESSDB;
return SMFIS_CONTINUE;
}
/*
* Restart the sync master thread if nescessary
*/
sync_master_restart();
/*
* Strip spaces from the recipient address
*/
strncpy_rmsp(rcpt, *envrcpt, ADDRLEN);
rcpt[ADDRLEN] = '\0';
/*
* Check the ACL
*/
reset_acl_values(priv);
if ((priv->priv_whitelist = acl_filter(ctx, priv, rcpt)) & EXF_WHITELIST) {
priv->priv_elapsed = 0;
return SMFIS_CONTINUE;
}
/*
* Blacklist overrides autowhitelisting...
*/
if (priv->priv_whitelist & EXF_BLACKLIST) {
char aclstr[16];
char *code = "551";
char *ecode = "5.7.1";
char *msg = "Go away!";
if (priv->priv_acl_line != 0)
snprintf(aclstr, sizeof(aclstr), " (ACL %d)",
priv->priv_acl_line);
mg_log(LOG_INFO,
"%s: addr %s[%s] from %s to %s blacklisted%s",
priv->priv_queueid, priv->priv_hostname, addrstr,
priv->priv_from, rcpt, aclstr);
code = (priv->priv_code) ? priv->priv_code : code;
ecode = (priv->priv_ecode) ? priv->priv_ecode : ecode;
msg = (priv->priv_msg) ? priv->priv_msg : msg;
(void)smfi_setreply(ctx, code, ecode, msg);
return *code == '4' ? SMFIS_TEMPFAIL : SMFIS_REJECT;
}
/*
* Check if the tuple {sender IP, sender e-mail, recipient e-mail}
* was autowhitelisted
*/
if ((priv->priv_whitelist = autowhite_check(SA(&priv->priv_addr),
priv->priv_addrlen, priv->priv_from, rcpt, priv->priv_queueid,
priv->priv_delay, priv->priv_autowhite)) != EXF_NONE) {
priv->priv_elapsed = 0;
return SMFIS_CONTINUE;
}
/*
* On a multi-recipient message, one message can be whitelisted,
* and the next ones be greylisted. The first one would
* pass through immediatly (priv->priv_delay = 0) with a
* priv->priv_whitelist = EXF_NONE. This would cause improper
* X-Greylist header display in mlfi_eom()
*
* The fix: if we make it to mlfi_eom() with priv_elapsed = 0, this
* means that some recipients were whitelisted.
* We can set priv_whitelist now, because if the message is greylisted
* for everyone, it will not go to mlfi_eom(), and priv_whitelist
* will not be used.
*/
priv->priv_whitelist = EXF_WHITELIST | EXF_RCPT;
/*
* Check if the tuple {sender IP, sender e-mail, recipient e-mail}
* is in the greylist and if it ca now be accepted. If it is not
* in the greylist, it will be added.
*/
if (pending_check(SA(&priv->priv_addr), priv->priv_addrlen,
priv->priv_from, rcpt, &remaining, &priv->priv_elapsed,
priv->priv_queueid, priv->priv_delay, priv->priv_autowhite) != 0)
return SMFIS_CONTINUE;
priv->priv_remaining = remaining;
/*
* The message has been added to the greylist and will be delayed.
* If the sender address is null, this will be done after the DATA
* phase, otherwise immediately.
* Delayed reject with per-recipient delays or messages
* will use the last match.
*/
if ((conf.c_delayedreject == 1) &&
(strcmp(priv->priv_from, "<>") == 0)) {
priv->priv_delayed_reject = 1;
if (*priv->priv_rcpt == 0)
strcpy(priv->priv_rcpt, rcpt);
else
strcpy(priv->priv_rcpt, "(multiple recipients)");
return SMFIS_CONTINUE;
}
/*
* Log temporary failure and report to the client.
*/
log_and_report_greylisting(ctx, priv, *envrcpt);
return SMFIS_TEMPFAIL;
}
sfsistat
mlfi_eom(ctx)
SMFICTX *ctx;
{
struct mlfi_priv *priv;
char hdr[HDRLEN + 1];
int h, mn, s;
char *fqdn = NULL;
char *ip = NULL;
char timestr[HDRLEN + 1];
char tzstr[HDRLEN + 1];
char tznamestr[HDRLEN + 1];
char whystr [HDRLEN + 1];
char host[ADDRLEN + 1];
time_t t;
struct tm ltm;
priv = (struct mlfi_priv *) smfi_getpriv(ctx);
if (priv->priv_delayed_reject) {
log_and_report_greylisting(ctx, priv, priv->priv_rcpt);
return SMFIS_TEMPFAIL;
}
if ((fqdn = smfi_getsymval(ctx, "{j}")) == NULL) {
mg_log(LOG_DEBUG, "smfi_getsymval failed for {j}");
gethostname(host, ADDRLEN);
fqdn = host;
}
ip = smfi_getsymval(ctx, "{if_addr}");
#ifdef AF_INET6
/*
* XXX: sendmail doesn't return {if_addr} when connection is
* from ::1
*/
if (ip == NULL && SA(&priv->priv_addr)->sa_family == AF_INET6) {
char buf[IPADDRSTRLEN];
if (iptostring(SA(&priv->priv_addr), priv->priv_addrlen, buf,
sizeof(buf)) != NULL &&
strcmp(buf, "::1") == 0)
ip = "IPv6:::1";
}
#endif
if (ip == NULL) {
mg_log(LOG_DEBUG, "smfi_getsymval failed for {if_addr}");
ip = "0.0.0.0";
}
t = time(NULL);
localtime_r(&t, &ltm);
strftime(timestr, HDRLEN, "%a, %d %b %Y %T", &ltm);
gmtoffset(&t, tzstr, HDRLEN);
strftime(tznamestr, HDRLEN, "%Z", &ltm);
if (priv->priv_elapsed == 0) {
if ((conf.c_report & C_NODELAYS) == 0)
return SMFIS_CONTINUE;
whystr[0] = '\0';
if (priv->priv_whitelist & EXF_DOMAIN) {
ADD_REASON(whystr, "Sender DNS name whitelisted");
priv->priv_whitelist &= ~EXF_DOMAIN;
}
if (priv->priv_whitelist & EXF_ADDR) {
ADD_REASON(whystr, "Sender IP whitelisted");
priv->priv_whitelist &= ~EXF_ADDR;
}
if (priv->priv_whitelist & EXF_FROM) {
ADD_REASON(whystr, "Sender e-mail whitelisted");
priv->priv_whitelist &= ~EXF_FROM;
}
if (priv->priv_whitelist & EXF_AUTH) {
ADD_REASON(whystr,
"Sender succeeded SMTP AUTH authentication");
priv->priv_whitelist &= ~EXF_AUTH;
}
if (priv->priv_whitelist & EXF_ACCESSDB) {
ADD_REASON(whystr,
"Message whitelisted by Sendmail access database");
priv->priv_whitelist &= ~EXF_ACCESSDB;
}
if (priv->priv_whitelist & EXF_DRAC) {
ADD_REASON(whystr,
"Message whitelisted by DRAC access database");
priv->priv_whitelist &= ~EXF_DRAC;
}
if (priv->priv_whitelist & EXF_SPF) {
ADD_REASON(whystr, "Sender is SPF-compliant");
priv->priv_whitelist &= ~EXF_SPF;
}
if (priv->priv_whitelist & EXF_NONIP) {
#ifdef AF_INET6
ADD_REASON(whystr,
"Message not sent from an IPv4 neither IPv6 address");
#else
ADD_REASON(whystr,
"Message not sent from an IPv4 address");
#endif
priv->priv_whitelist &= ~EXF_NONIP;
}
if (priv->priv_whitelist & EXF_STARTTLS) {
ADD_REASON(whystr, "Sender succeeded STARTTLS authentication");
priv->priv_whitelist &= ~EXF_STARTTLS;
}
if (priv->priv_whitelist & EXF_RCPT) {
ADD_REASON(whystr, "Recipient e-mail whitelisted");
priv->priv_whitelist &= ~EXF_RCPT;
}
if (priv->priv_whitelist & EXF_AUTO) {
ADD_REASON(whystr, "IP, sender and recipient auto-whitelisted");
priv->priv_whitelist &= ~EXF_AUTO;
}
if (priv->priv_whitelist & EXF_DNSRBL) {
ADD_REASON(whystr, "Sender IP whitelisted by DNSRBL");
priv->priv_whitelist &= ~EXF_DNSRBL;
}
if (priv->priv_whitelist & EXF_DEFAULT) {
ADD_REASON(whystr, "Default is to whitelist mail");
priv->priv_whitelist &= ~EXF_DEFAULT;
}
priv->priv_whitelist &= ~(EXF_GREYLIST | EXF_WHITELIST);
if (priv->priv_whitelist != 0) {
mg_log(LOG_ERR, "%s: unexpected priv_whitelist = %d",
priv->priv_queueid, priv->priv_whitelist);
mystrlcat (whystr, "Internal error ", HDRLEN);
}
snprintf(hdr, HDRLEN, "%s, not delayed by "
"milter-greylist-%s (%s [%s]); %s %s (%s)",
whystr, PACKAGE_VERSION, fqdn,
ip, timestr, tzstr, tznamestr);
smfi_addheader(ctx, HEADERNAME, hdr);
return SMFIS_CONTINUE;
}
h = priv->priv_elapsed / 3600;
priv->priv_elapsed = priv->priv_elapsed % 3600;
mn = (priv->priv_elapsed / 60);
priv->priv_elapsed = priv->priv_elapsed % 60;
s = priv->priv_elapsed;
snprintf(hdr, HDRLEN,
"Delayed for %02d:%02d:%02d by milter-greylist-%s "
"(%s [%s]); %s %s (%s)",
h, mn, s, PACKAGE_VERSION, fqdn, ip, timestr, tzstr, tznamestr);
if (conf.c_report & C_DELAYS)
smfi_addheader(ctx, HEADERNAME, hdr);
return SMFIS_CONTINUE;
}
sfsistat
mlfi_close(ctx)
SMFICTX *ctx;
{
struct mlfi_priv *priv;
if ((priv = (struct mlfi_priv *) smfi_getpriv(ctx)) != NULL) {
if (priv->priv_code)
free(priv->priv_code);
if (priv->priv_ecode)
free(priv->priv_ecode);
if (priv->priv_msg)
free(priv->priv_msg);
free(priv);
smfi_setpriv(ctx, NULL);
}
/*
* If we need to dump on each change and something changed, dump
*/
if ((dump_dirty != 0) && (conf.c_dumpfreq == 0))
dump_flush();
return SMFIS_CONTINUE;
}
int
main(argc, argv)
int argc;
char *argv[];
{
int ch;
int checkonly = 0;
/*
* Load configuration defaults
*/
conf_defaults(&defconf);
memcpy(&conf, &defconf, sizeof(conf));
/*
* Process command line options
*/
while ((ch = getopt(argc, argv, "Aa:cvDd:qw:f:hp:P:Tu:rSL:M:l")) != -1) {
switch (ch) {
case 'A':
defconf.c_noauth = 1;
defconf.c_forced |= C_NOAUTH;
break;
case 'a':
if (optarg == NULL) {
mg_log(LOG_ERR, "%s: -a needs an argument",
argv[0]);
usage(argv[0]);
}
defconf.c_autowhite_validity =
(time_t)humanized_atoi(optarg);
defconf.c_forced |= C_AUTOWHITE;
break;
case 'c':
checkonly = 1;
break;
case 'D':
conf_nodetach = 1;
defconf.c_forced |= C_NODETACH;
break;
case 'q':
defconf.c_quiet = 1;
defconf.c_forced |= C_QUIET;
break;
case 'r':
mg_log(LOG_INFO, "milter-greylist-%s %s",
PACKAGE_VERSION, BUILD_ENV);
exit(EX_OK);
break;
case 'S':
defconf.c_nospf = 1;
defconf.c_forced |= C_NOSPF;
break;
case 'u': {
if (geteuid() != 0) {
mg_log(LOG_ERR, "%s: only root can use -u",
argv[0]);
exit(EX_USAGE);
}
if (optarg == NULL) {
mg_log(LOG_ERR,
"%s: -u needs a valid user as argument",
argv[0]);
usage(argv[0]);
}
defconf.c_user = optarg;
defconf.c_forced |= C_USER;
break;
}
case 'v':
defconf.c_debug = 1;
defconf.c_forced |= C_DEBUG;
break;
case 'w':
if ((optarg == NULL) ||
((defconf.c_delay = humanized_atoi(optarg)) == 0)) {
mg_log(LOG_ERR,
"%s: -w needs a positive argument",
argv[0]);
usage(argv[0]);
}
defconf.c_forced |= C_DELAY;
break;
case 'f':
if (optarg == NULL) {
mg_log(LOG_ERR, "%s: -f needs an argument",
argv[0]);
usage(argv[0]);
}
conffile = optarg;
break;
case 'd':
if (optarg == NULL) {
mg_log(LOG_ERR, "%s: -d needs an argument",
argv[0]);
usage(argv[0]);
}
defconf.c_dumpfile = optarg;
defconf.c_forced |= C_DUMPFILE;
break;
case 'P':
if (optarg == NULL) {
mg_log(LOG_ERR, "%s: -P needs an argument",
argv[0]);
usage(argv[0]);
}
defconf.c_pidfile = optarg;
defconf.c_forced |= C_PIDFILE;
break;
case 'p':
if (optarg == NULL) {
mg_log(LOG_ERR, "%s: -p needs an argument",
argv[0]);
usage(argv[0]);
}
defconf.c_socket = optarg;
defconf.c_forced |= C_SOCKET;
break;
case 'L': {
int cidr;
char maskstr[IPADDRLEN + 1];
if (optarg == NULL) {
mg_log(LOG_ERR,
"%s: -L requires a CIDR mask", argv[0]);
usage(argv[0]);
}
cidr = atoi(optarg);
if ((cidr > 32) || (cidr < 0)) {
mg_log(LOG_ERR,
"%s: -L requires a CIDR mask", argv[0]);
usage(argv[0]);
}
prefix2mask4(cidr, &defconf.c_match_mask);
defconf.c_forced |= C_MATCHMASK;
if (defconf.c_debug)
mg_log(LOG_DEBUG, "match mask: %s",
inet_ntop(AF_INET, &defconf.c_match_mask,
maskstr, IPADDRLEN));
break;
}
case 'M': {
int plen;
#ifdef AF_INET6
char maskstr[INET6_ADDRSTRLEN + 1];
#endif
if (optarg == NULL) {
mg_log(LOG_ERR,
"%s: -M requires a prefix length",
argv[0]);
usage(argv[0]);
}
plen = atoi(optarg);
if ((plen > 128) || (plen < 0)) {
mg_log(LOG_ERR,
"%s: -M requires a prefix length",
argv[0]);
usage(argv[0]);
}
#ifdef AF_INET6
prefix2mask6(plen, &defconf.c_match_mask6);
defconf.c_forced |= C_MATCHMASK6;
if (defconf.c_debug)
mg_log(LOG_DEBUG, "match mask: %s",
inet_ntop(AF_INET6, &defconf.c_match_mask6,
maskstr, INET6_ADDRSTRLEN));
#endif
break;
}
case 'T':
defconf.c_testmode = 1;
defconf.c_forced |= C_TESTMODE;
break;
case 'l':
defconf.c_acldebug = 1;
defconf.c_forced |= C_ACLDEBUG;
break;
case 'h':
default:
usage(argv[0]);
break;
}
}
/*
* Various init
*/
conf_init();
all_list_init();
acl_init ();
pending_init();
peer_init();
autowhite_init();
dump_init();
#ifdef USE_DNSRBL
dnsrbl_init();
#endif
macro_init();
/*
* Load config file
* We can do this without locking exceptlist, as
* normal operation has not started: no other thread
* can access the list yet.
*/
conf_load();
if (checkonly) {
mg_log(LOG_INFO, "config file \"%s\" is okay", conffile);
exit(EX_OK);
}
openlog("milter-greylist", 0, LOG_MAIL);
conf_cold = 0;
if (conf.c_socket == NULL) {
mg_log(LOG_ERR, "%s: No socket provided, exiting", argv[0]);
usage(argv[0]);
}
cleanup_sock(conf.c_socket);
(void)smfi_setconn(conf.c_socket);
/*
* Reload a saved greylist
* No lock needed here either.
*/
dump_reload();
/*
* Register our callbacks
*/
if (smfi_register(smfilter) == MI_FAILURE) {
mg_log(LOG_ERR, "%s: smfi_register failed", argv[0]);
exit(EX_UNAVAILABLE);
}
/*
* Turn into a daemon
*/
if (conf_nodetach == 0) {
(void)close(0);
(void)open("/dev/null", O_RDONLY, 0);
(void)close(1);
(void)open("/dev/null", O_WRONLY, 0);
(void)close(2);
(void)open("/dev/null", O_WRONLY, 0);
if (chdir("/") != 0) {
mg_log(LOG_ERR, "%s: cannot chdir to root: %s",
argv[0], strerror(errno));
exit(EX_OSERR);
}
switch (fork()) {
case -1:
mg_log(LOG_ERR, "%s: cannot fork: %s",
argv[0], strerror(errno));
exit(EX_OSERR);
break;
case 0:
break;
default:
exit(EX_OK);
break;
}
if (setsid() == -1) {
mg_log(LOG_ERR, "%s: setsid failed: %s",
argv[0], strerror(errno));
exit(EX_OSERR);
}
}
/*
* Write down our PID to a file
*/
if (conf.c_pidfile != NULL)
writepid(conf.c_pidfile);
/*
* Drop root privs
*/
if (conf.c_user != NULL) {
struct passwd *pw = NULL;
if ((pw = getpwnam(conf.c_user)) == NULL) {
mg_log(LOG_ERR, "%s: cannot get user %s data: %s",
argv[0], conf.c_user, strerror(errno));
exit(EX_OSERR);
}
#ifdef HAVE_INITGROUPS
if (initgroups(conf.c_user, pw->pw_gid) != 0) {
mg_log(LOG_ERR, "%s: cannot change "
"supplementary groups: %s",
argv[0], strerror(errno));
exit(EX_OSERR);
}
#endif
if (setgid(pw->pw_gid) != 0 ||
setegid(pw->pw_gid) != 0) {
mg_log(LOG_ERR, "%s: cannot change GID: %s",
argv[0], strerror(errno));
exit(EX_OSERR);
}
if ((setuid(pw->pw_uid) != 0) ||
(seteuid(pw->pw_uid) != 0)) {
mg_log(LOG_ERR, "%s: cannot change UID: %s",
argv[0], strerror(errno));
exit(EX_OSERR);
}
}
/*
* Start the dumper thread
*/
dumper_start();
/*
* Run the peer MX greylist sync threads
*/
sync_master_restart();
sync_sender_start();
/*
* Install an atexit() callback to perform
* a dump when milter-greylist exits.
*/
if (atexit(*final_dump) != 0) {
mg_log(LOG_ERR, "atexit() failed: %s", strerror(errno));
exit(EX_OSERR);
}
/*
* Dump the ACL for debugging purposes
*/
if (conf.c_debug || conf.c_acldebug)
acl_dump();
/*
* Here we go!
*/
return smfi_main();
}
void
usage(progname)
char *progname;
{
mg_log(LOG_ERR,
"usage: %s [-A] [-a autowhite_delay] [-c] [-D] [-d dumpfile]",
progname);
mg_log(LOG_ERR,
" [-f configfile] [-h] [-l] [-q] [-r] [-S] [-T]");
mg_log(LOG_ERR,
" [-u username] [-v] [-w greylist_delay] [-L cidrmask]");
mg_log(LOG_ERR,
" [-M prefixlen] [-P pidfile] -p socket");
exit(EX_USAGE);
}
void
cleanup_sock(path)
char *path;
{
struct stat st;
/* Does it exists? Get information on it if it does */
if (stat(path, &st) != 0)
return;
/* Is it a socket? */
if ((st.st_mode & S_IFSOCK) == 0)
return;
/* Remove the beast */
(void)unlink(path);
return;
}
char *
strncpy_rmsp(dst, src, len)
char *dst;
char *src;
size_t len;
{
unsigned int i;
for (i = 0; src[i] && (i < len); i++) {
if (isgraph((int)(unsigned char)src[i]))
dst[i] = src[i];
else
dst[i] = '_';
}
if (i < len)
dst[i] = '\0';
return dst;
}
int
humanized_atoi(str) /* *str is modified */
char *str;
{
unsigned int unit;
size_t len;
char numstr[NUMLEN + 1];
if (((len = strlen(str)) || (len > NUMLEN)) == 0)
return 0;
switch(str[len - 1]) {
case 's':
unit = 1;
break;
case 'm':
unit = 60;
break;
case 'h':
unit = 60 * 60;
break;
case 'd':
unit = 24 * 60 * 60;
break;
case 'w':
unit = 7 * 24 * 60 * 60;
break;
default:
return atoi(str);
break;
}
strncpy(numstr, str, NUMLEN);
numstr[len - 1] = '\0';
return (atoi(numstr) * unit);
}
static char *
gmtoffset(date, buf, size)
time_t *date;
char *buf;
size_t size;
{
struct tm gmt;
struct tm local;
int offset;
char *sign;
int h, mn;
gmtime_r(date, &gmt);
localtime_r(date, &local);
offset = local.tm_min - gmt.tm_min;
offset += (local.tm_hour - gmt.tm_hour) * 60;
/* Offset cannot be greater than a day */
if (local.tm_year < gmt.tm_year)
offset -= 24 * 60;
else
offset += (local.tm_yday - gmt.tm_yday) * 60 * 24;
if (offset >= 0) {
sign = "+";
} else {
sign = "-";
offset = -offset;
}
h = offset / 60;
mn = offset % 60;
snprintf(buf, size, "%s%02d%02d", sign, h, mn);
return buf;
}
static void
writepid(pidfile)
char *pidfile;
{
FILE *stream;
if ((stream = fopen(pidfile, "w")) == NULL) {
mg_log(LOG_ERR, "Cannot open pidfile \"%s\" for writing: %s",
pidfile, strerror(errno));
return;
}
fprintf(stream, "%ld\n", (long)getpid());
fclose(stream);
return;
}
struct in_addr *
prefix2mask4(cidr, mask)
int cidr;
struct in_addr *mask;
{
if ((cidr == 0) || (cidr > 32)) {
bzero((void *)mask, sizeof(*mask));
} else {
cidr = 32 - cidr;
mask->s_addr = htonl(~((1UL << cidr) - 1));
}
return mask;
}
#ifdef AF_INET6
struct in6_addr *
prefix2mask6(plen, mask)
int plen;
struct in6_addr *mask;
{
int i;
uint32_t m;
if (plen == 0 || plen > 128)
bzero((void *)mask, sizeof(*mask));
else {
for (i = 0; i < 16; i += 4) {
if (plen < 32)
m = ~(0xffffffff >> plen);
else
m = 0xffffffff;
*(uint32_t *)&mask->s6_addr[i] = htonl(m);
plen -= 32;
if (plen < 0)
plen = 0;
}
}
return mask;
}
#endif
void
unmappedaddr(sa, salen)
struct sockaddr *sa;
socklen_t *salen;
{
#ifdef AF_INET6
struct in_addr addr4;
int port;
if (SA6(sa)->sin6_family != AF_INET6 ||
!IN6_IS_ADDR_V4MAPPED(SADDR6(sa)))
return;
addr4.s_addr = *(uint32_t *)&SADDR6(sa)->s6_addr[12];
port = SA6(sa)->sin6_port;
bzero(sa, sizeof(struct sockaddr_in));
SADDR4(sa)->s_addr = addr4.s_addr;
SA4(sa)->sin_port = port;
SA4(sa)->sin_family = AF_INET;
#ifdef HAVE_SA_LEN
SA4(sa)->sin_len = sizeof(struct sockaddr_in);
#endif
*salen = sizeof(struct sockaddr_in);
#endif
return;
}
void
log_and_report_greylisting(ctx, priv, rcpt)
SMFICTX *ctx;
struct mlfi_priv *priv;
char *rcpt;
{
int h, mn, s;
char hdr[HDRLEN + 1];
char addrstr[IPADDRSTRLEN];
time_t remaining;
char *delayed_rj;
char aclstr[16];
char *code = "451";
char *ecode = "4.7.1";
char *msg = "Greylisting in action, please come back later";
/*
* The message has been added to the greylist and will be delayed.
* Log this and report to the client.
*/
iptostring(SA(&priv->priv_addr), priv->priv_addrlen, addrstr,
sizeof(addrstr));
remaining = priv->priv_remaining;
h = remaining / 3600;
remaining = remaining % 3600;
mn = (remaining / 60);
remaining = remaining % 60;
s = remaining;
if (priv->priv_delayed_reject)
delayed_rj = " after DATA phase";
else
delayed_rj = "";
if (priv->priv_acl_line != 0)
snprintf(aclstr, sizeof(aclstr), " (ACL %d)",
priv->priv_acl_line);
else
aclstr[0] = '\0';
mg_log(LOG_INFO,
"%s: addr %s[%s] from %s to %s delayed%s for %02d:%02d:%02d%s",
priv->priv_queueid, priv->priv_hostname, addrstr,
priv->priv_from, rcpt, delayed_rj, h, mn, s, aclstr);
code = (priv->priv_code) ? priv->priv_code : code;
ecode = (priv->priv_ecode) ? priv->priv_ecode : ecode;
if (conf.c_quiet) {
msg = (priv->priv_msg) ? priv->priv_msg : msg;
} else {
snprintf(hdr, HDRLEN,
"Greylisting in action, please come "
"back in %02d:%02d:%02d", h, mn, s);
msg = (priv->priv_msg) ? priv->priv_msg : hdr;
}
(void)smfi_setreply(ctx, code, ecode, msg);
return;
}
void
final_dump(void) {
if (dump_dirty != 0) {
mg_log(LOG_INFO, "Final database dump");
dump_perform();
} else {
mg_log(LOG_INFO, "Final database dump: no change to dump");
}
mg_log(LOG_INFO, "Exiting");
return;
}
#ifdef USE_DRAC
static int
check_drac(dotted_ip)
char *dotted_ip;
{
DB *ddb;
DBT key, data;
char ipkey[16];
int rc;
ddb = dbopen(conf.c_dracdb, O_RDONLY | O_SHLOCK, 0666, DB_BTREE, NULL);
if (ddb == NULL) {
mg_log(LOG_DEBUG, "dbopen \"%s\" failed", conf.c_dracdb);
return 0;
}
key.data = strncpy(ipkey, dotted_ip, sizeof(ipkey));
key.size = strlen(ipkey);
rc = ddb->get(ddb, &key, &data, 0);
ddb->close(ddb);
switch (rc) {
case 0:
#ifdef TEST
mg_log(LOG_DEBUG, "key.data=%.*s (len=%d) "
"data.data=%.*s (len=%d)",
key.size, key.data, key.size,
data.size, data.data, data.size);
#endif /* TEST */
return 1;
break;
case 1:
return 0;
break;
default:
mg_log(LOG_ERR, "check_drack: errno=%d", errno);
break;
}
return 0;
}
#endif /* USE_DRAC */
static void
reset_acl_values(priv)
struct mlfi_priv *priv;
{
priv->priv_delay = conf.c_delay;
priv->priv_autowhite = conf.c_autowhite_validity;
if (priv->priv_code != NULL) {
free(priv->priv_code);
priv->priv_code = NULL;
}
if (priv->priv_ecode != NULL) {
free(priv->priv_ecode);
priv->priv_ecode = NULL;
}
if (priv->priv_msg != NULL) {
free(priv->priv_msg);
priv->priv_msg = NULL;
}
return;
}
#ifndef HAVE_STRLCAT
size_t
mystrlcat(dst, src, len)
char *dst;
const char *src;
size_t len;
{
size_t srclen = strlen(src);
size_t dstlen;
for (dstlen = 0; dstlen != len && dst[dstlen]; ++dstlen)
;
if (dstlen == len) {
#if 0
/* BSD's strlcat leaves the string not NUL-terminated. */
return dstlen + srclen;
#else
/* This situation is a bug. We make core dump. */
abort();
#endif
}
strncpy(dst + dstlen, src, len - dstlen - 1);
dst[len - 1] = '\0';
return dstlen + srclen;
}
#endif
#ifndef HAVE_VSYSLOG
#ifndef LINE_MAX
#define LINE_MAX 1024
#endif /* LINE_MAX */
void
vsyslog(level, fmt, ap)
int level;
char *fmt;
va_list ap;
{
char messagebuf[LINE_MAX];
vsnprintf(messagebuf, sizeof(messagebuf), fmt, ap);
messagebuf[sizeof(messagebuf) - 1] = '\0';
syslog(level, "%s", messagebuf);
return;
}
#endif /* HAVE_VSYSLOG */
/* VARARGS */
void
mg_log(int level, char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
if (conf_cold || conf_nodetach) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
if (!conf_cold)
vsyslog(level, fmt, ap);
va_end(ap);
return;
}
#!/bin/bash
#
# Plugin to monitor http load time for sites
#
# Parameters:
#
# config
# autoconf
#
# Configuration variables
#
# sites - site names seperated by space e.g. "google.com mail.ru"
#
#
#%# family=auto
#%# capabilities=autoconf
CURL=${curl:-curl}
if [ "$1" = "autoconf" ]; then
if [ -x $CURL ]; then
echo no
exit 1
fi
if [ "X${SITES}" = "X" ]; then
echo no
exit 1
fi
echo yes
exit 0
fi
if [ "$1" = "config" ]; then
echo "graph_title Page load time"
echo "graph_args --base 1000"
#echo "graph_args --base 1000 --upper-limit 5"
echo "graph_vlabel time in seconds"
echo "graph_category http"
echo "graph_scale no"
echo "graph_info Amount on seconds taken to load website from this host."
I=0
for S in $SITES
do
let I+=1
echo "site${I}.label ${S}"
echo "site${I}.min 0"
echo "site${I}.draw LINE2"
echo "site${I}.max 800"
done
exit 0
fi
I=0
for S in $SITES
do
let I+=1
#echo $CURL -s -w "site${I}.value %{time_total}\n" -o /dev/null http://${S}/
$CURL -L -s -w "site${I}.value %{time_total}\n" -o /dev/null http://${S}/
done
exit 0
CREATE OR REPLACE VIEW el_user_view AS
SELECT m.uid AS uid,
a1.value AS login,
a2.value AS pass,
a3.value AS f_name,
a4.value AS l_name,
a5.value AS s_name,
a6.value AS email,
a7.value AS phone_mobile,
a8.value AS phone_home,
a9.value AS city,
a10.value AS metro,
a11.value AS crtime,
a12.value AS mtime,
a13.value AS atime,
a14.value AS visits
FROM el_user_profile_data AS m
INNER JOIN el_user_profile_data AS a1 ON (m.uid=a1.uid AND a1.field=\'login\')
INNER JOIN el_user_profile_data AS a2 ON (m.uid=a2.uid AND a2.field=\'pass\')
INNER JOIN el_user_profile_data AS a3 ON (m.uid=a3.uid AND a3.field=\'f_name\')
INNER JOIN el_user_profile_data AS a4 ON (m.uid=a4.uid AND a4.field=\'l_name\')
INNER JOIN el_user_profile_data AS a5 ON (m.uid=a5.uid AND a5.field=\'s_name\')
INNER JOIN el_user_profile_data AS a6 ON (m.uid=a6.uid AND a6.field=\'email\')
INNER JOIN el_user_profile_data AS a7 ON (m.uid=a7.uid AND a7.field=\'phone_mobile\')
INNER JOIN el_user_profile_data AS a8 ON (m.uid=a8.uid AND a8.field=\'phone_home\')
INNER JOIN el_user_profile_data AS a9 ON (m.uid=a9.uid AND a9.field=\'city\')
INNER JOIN el_user_profile_data AS a10 ON (m.uid=a10.uid AND a10.field=\'metro\')
INNER JOIN el_user_profile_data AS a11 ON (m.uid=a11.uid AND a11.field=\'crtime\')
INNER JOIN el_user_profile_data AS a12 ON (m.uid=a12.uid AND a12.field=\'mtime\')
INNER JOIN el_user_profile_data AS a13 ON (m.uid=a13.uid AND a13.field=\'atime\')
INNER JOIN el_user_profile_data AS a14 ON (m.uid=a14.uid AND a14.field=\'visits\')
GROUP by m.uid
SELECT uid, MAX( IF( b = \'login\', val, NULL ) ) login, MAX( IF( b = \'email\', val, NULL ) ) email, MAX( IF( b = \'f_name\', val, NULL ) ) f_name, MAX( IF( b = \'mtime\', val, NULL ) ) mtime
FROM (
SELECT uid, field b, value val
FROM el_user_profile_data_test
) AS t
GROUP BY uid
Спасибо за подробный ответ.
1. На сколько я понимаю, приминимо к моей ситуации запрос будет выглядеть таким образом:
SELECT uid,
MAX(IF(field=\'login\', value, NULL)) login,
MAX(IF(field=\'email\', value, NULL)) email,
MAX(IF(field=\'f_name\', value, NULL)) f_name,
MAX(IF(field=\'mtime\', value, NULL)) mtime
FROM el_user_profile_data_test
GROUP BY uid
Есть создать VIEW от этого запроса, то случайные выборки или объединения уже занимаю 6 секунд вместо 4, смущает что не используются идексы и explain говорит Using temporary; Using filesort. Будут ли вообще в такой ситуации индексы работать?
Почитав отзывы о VIEW в MySQL складывается мнение, что он ещё пока сыроват.
2. По поводу того что это религиозная война я полностью согласна, но одно дело работать с одним объектом данных или с 3мя и более (User, Address, Stats...), может есть ещё какой-то вариант оптимизировать скорость работы не разбивая структуры на отдельные сущности? Может ввести отдельную таблицу где будут храниться ID пользователей
\ No newline at end of file
...@@ -30,6 +30,7 @@ return [ ...@@ -30,6 +30,7 @@ return [
'array' => 'The :attribute must have between :min and :max items.', 'array' => 'The :attribute must have between :min and :max items.',
], ],
'boolean' => 'The :attribute field must be true or false.', 'boolean' => 'The :attribute field must be true or false.',
'captcha' => 'Invalid captcha!',
'confirmed' => 'The :attribute confirmation does not match.', 'confirmed' => 'The :attribute confirmation does not match.',
'date' => 'The :attribute is not a valid date.', 'date' => 'The :attribute is not a valid date.',
'date_equals' => 'The :attribute must be a date equal to :date.', 'date_equals' => 'The :attribute must be a date equal to :date.',
......
...@@ -51,6 +51,28 @@ ...@@ -51,6 +51,28 @@
</div> </div>
</div> </div>
<div class="form-group row">
<label for="captcha" class="col-md-4 col-form-label text-md-right">Captcha</label>
<div class="col-md-6 captcha">
<span>{!! captcha_img() !!}</span>
<button type="button" class="btn btn-danger" class="reload" id="reload">
&#x21bb;
</button>
</div>
</div>
<div class="form-group row">
<label for="captcha" class="col-md-4 col-form-label text-md-right">Enter Captcha</label>
<div class="col-md-6">
<input id="captcha" type="text" class="form-control @error('captcha') is-invalid @enderror" name="captcha" required autocomplete="" placeholder="Enter Captcha" name="captcha">
@error('captcha')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0"> <div class="form-group row mb-0">
<div class="col-md-8 offset-md-4"> <div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
...@@ -71,3 +93,18 @@ ...@@ -71,3 +93,18 @@
</div> </div>
</div> </div>
@endsection @endsection
@push('scripts')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script type="text/javascript">
$('.reload').click(function () {
console.log('tes');
$.ajax({
type: 'GET',
url: 'reload-captcha',
success: function (data) {
$(".captcha span").html(data.captcha);
}
});
});
</script>
@endpush
\ No newline at end of file
...@@ -76,5 +76,6 @@ ...@@ -76,5 +76,6 @@
@yield('content') @yield('content')
</main> </main>
</div> </div>
@stack('scripts')
</body> </body>
</html> </html>
...@@ -11,7 +11,8 @@ ...@@ -11,7 +11,8 @@
| |
*/ */
Auth::routes(); Auth::routes(['register' => false]);
// Route::get('reload-captcha', 'App\Http\Controllers\Auth\LoginController@reloadCaptcha')->name('reload-captcha');
Route::post('/change-language', 'TranslateController@changeLocale')->name('language'); Route::post('/change-language', 'TranslateController@changeLocale')->name('language');
Route::get('/home', 'HomeController@index')->name('home'); Route::get('/home', 'HomeController@index')->name('home');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment