629 lines
22 KiB
PHP
629 lines
22 KiB
PHP
<?php # KT_HANDLE # Transmitted information handler
|
|
|
|
use Katana\Seal as Seal;
|
|
|
|
import(Module::regex);
|
|
|
|
final class Handle extends Protector {
|
|
|
|
/**
|
|
* Escape name and generate tripcode if needed
|
|
*/
|
|
static function name($name){
|
|
if(preg_match(Regex::TRIPCODE, $name, $regs) && X::$mode&Seal::NAME){
|
|
$cap = y(mb_convert_encoding($regs[2],'SJIS','UTF-8'),$cap);
|
|
if(strpos($name,'#')===false) $cap_delimiter = '!';
|
|
elseif(strpos($name,'!')===false) $cap_delimiter = '#';
|
|
else $cap_delimiter = (strpos($name,'#') < strpos($name,'!'))?'#':'!';
|
|
$real = mb_substr($name,0,mb_strpos($name,$cap_delimiter));
|
|
if(preg_match("/(.*)(".$cap_delimiter.")(.*)/",$cap,$regs_secure)){
|
|
$cap = $regs_secure[1];
|
|
$cap_secure = $regs_secure[3];
|
|
$is_secure_trip = true;
|
|
} else $is_secure_trip = false;
|
|
$trip = "";
|
|
if(!empty($cap)){
|
|
$cap = strtr($cap, "&", "&");
|
|
$cap = strtr($cap, ",", ", ");
|
|
$salt = substr($cap . "H.", 1, 2);
|
|
$salt = preg_replace("/[^\.-z]/", ".", $salt);
|
|
$salt = strtr($salt, ":;<=>?@[\\]^_`", "ABCDEFGabcdef");
|
|
$trip = substr(crypt($cap, $salt), -10);
|
|
}
|
|
if($is_secure_trip){
|
|
if(!empty($cap)) $trip .= "!";
|
|
$trip .= "!".substr(md5($cap_secure.SEED),2,10);
|
|
}
|
|
return array($real,$trip);
|
|
}
|
|
return array($name,null);
|
|
}
|
|
|
|
/**
|
|
* Escape and format post text
|
|
*/
|
|
static function text($string,$literal){
|
|
if(mb_strlen($string) > X::$conf['kt']['textsize']){
|
|
$string = mb_substr($string, 0, X::$conf['kt']['textsize'] - 1).'…';
|
|
new Note('Text was too long, truncated.', Note::WARNING);
|
|
}
|
|
|
|
# FORMATTING
|
|
$layout = function($string) use ($literal){
|
|
$pat = array(
|
|
#Regex::GENERIC_DASH => ' — ',
|
|
Regex::GENERIC_REF_NUM => '<a class="ref" rel="$2" href="/'.$literal.'/post/$2">>>$2</a>',
|
|
Regex::GENERIC_REF_LIT => '<a class="ref" rev="$3" rel="$2" href="/$2/post/$3">>>>$2/$3</a>',
|
|
Regex::GENERIC_QUOTE => '<blockquote class="quotes">$0</blockquote>',
|
|
#Regex::TRIFORCE => '<pre> ▲\n▲ ▲</pre><br/>',
|
|
Regex::T_WM_SPOILER => '<mark class="spoiler">$2</mark>',
|
|
Regex::T_WM_STRIKEOUT => '<strike>$2</strike>',
|
|
Regex::T_WM_BOLD => '<strong class="bold">$2</strong>',
|
|
Regex::T_WM_ITALIC => '<em class="italic">$2</em>',
|
|
Regex::T_WM_UNDERLINE => '<ins class="underline">$2</ins>',
|
|
Regex::T_WM_HEADER => '<h3 class="header">$2</h3>',
|
|
Regex::T_BB_FORMAT => '<$1>$2</$1>',
|
|
Regex::T_BB_SPOILER => '<mark class="spoiler">$2</mark>',
|
|
Regex::T_BB_TEXTWALL => '<details class="textwall"><summary>Cut</summary>$2</details>',
|
|
Regex::T_BB_RED => '<span style="color:crimson">$2</span>',
|
|
Regex::T_BB_GREEN => '<span style="color:lightgreen">$2</span>',
|
|
Regex::T_BB_BLUE => '<span style="color:cornflowerblue">$2</span>'
|
|
);
|
|
return preg_replace(array_keys($pat),array_values($pat),$string);
|
|
};
|
|
|
|
# RICH TEXT FEATURES
|
|
$string = preg_replace_callback_array([
|
|
Regex::GENERIC_USERAGENT => function($match){ import(System::CLIENT); return '<span class="special">'.Client::browser().' on '.Client::OS().'</span>'; },
|
|
Regex::GENERIC_POSTCOUNT => function($match){ import(System::CLIENT); return '<span class="special">'.Client::postcount().'</span>'; },
|
|
Regex::GENERIC_DICE => function($match){
|
|
$dices = abs(intval($match[1]));
|
|
$sides = abs(intval($match[2]));
|
|
$count = abs(intval(y($match[4],0)))+1;
|
|
$final = 0; for($j=0;$j<$count;$j++) for($i=0;$i<$dices;$i++) $final += mt_rand(1,$sides);
|
|
return "<span class='dice' title='{$dices} dices, {$sides} sides, {$count} attempts'><i class='icon-stop'></i>{$final}</span>";
|
|
},
|
|
Regex::T_RAW => function($matches){
|
|
return '<pre>'.Regex::escape(str_replace('<br/>',"\r\n",$matches[2])).'</pre>';
|
|
},
|
|
Regex::T_LATEX => function($matchee){
|
|
if(X::$conf['xt']['latex']){
|
|
#
|
|
}
|
|
return '<pre class="code latex">'.Regex::escape(str_replace('<br/>',"\r\n",$matches[2])).'</pre>';
|
|
},
|
|
Regex::T_WM_CODE => function($matches){
|
|
if(X::$conf['xt']['geshi']){
|
|
#
|
|
}
|
|
return '<br/><code class="code">'.Regex::escape(trim(str_replace('<br/>',"\r\n",$matches[2]))).'</code>';
|
|
},
|
|
Regex::T_BB_CODE => function($matches){
|
|
if(X::$conf['xt']['geshi']){
|
|
#
|
|
}
|
|
return '<br/><code class="code">'.Regex::escape(trim(str_replace('<br/>',"\r\n",$matches[2]))).'</code>';
|
|
},
|
|
Regex::GENERIC_LINK => function($matches){
|
|
return $matches[1].$matches[2].'<noindex><a href="'.$matches[3].'" target="_blank" rel="noreferrer">'.$matches[3].'</a></noindex>';
|
|
#return '<a href="'.$matches[0].'" target="_blank" rel="nofollow">'.$matches[0].'</a>';
|
|
},
|
|
Regex::FS_GOOGLE => function($matches){
|
|
return '<a href="https://www.google.com/search?q='
|
|
.urlencode($matches[1])
|
|
.'" rel="nofollow" target="_blank" class="goto-google">'.$matches[1].'</a>';
|
|
},
|
|
Regex::FS_DDG => function($matches){
|
|
return '<a href="https://duckduckgo.com/?q='
|
|
.urlencode($matches[1])
|
|
.'" rel="nofollow" target="_blank" class="goto-ddg">'.$matches[1].'</a>';
|
|
},
|
|
Regex::FS_WIKI => function($matches){
|
|
return '<a href="https://ru.wikipedia.org/wiki/'
|
|
.urlencode($matches[1])
|
|
.'" rel="nofollow" target="_blank" class="goto-wiki">'.$matches[1].'</a>';
|
|
},
|
|
Regex::FS_WOLFRAM => function($matches){
|
|
return '<a href="https://www.wolframalpha.com/input/?i='
|
|
.urlencode($matches[1])
|
|
.'" rel="nofollow" target="_blank" class="goto-wolfram">'.$matches[1].'</a>';
|
|
},
|
|
Regex::FS_GIT => function($matches){
|
|
return '<a href="https://github.com/search?utf8=✓&q='
|
|
.urlencode($matches[1])
|
|
.'" rel="nofollow" target="_blank" class="goto-github">'.$matches[1].'</a>';
|
|
}
|
|
], $string);
|
|
$string = $layout($string);
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Escape and format external service links
|
|
*/
|
|
static function link($url){
|
|
$result = null;
|
|
if(preg_match(Regex::INTERNAL, $url) || preg_match(Regex::EXTERNAL, $url)){
|
|
$preg = [
|
|
'img' => Regex::TYPE_IMAGE,
|
|
'aud' => Regex::TYPE_AUDIO,
|
|
'vid' => Regex::TYPE_VIDEO,
|
|
'fla' => Regex::TYPE_FLASH,
|
|
'txt' => Regex::TYPE_PASTE,
|
|
'arc' => Regex::TYPE_ARCHIVE,
|
|
'bit' => Regex::TYPE_BITTORRENT,
|
|
'key' => Regex::TYPE_CIPHER,
|
|
'vlt' => Regex::TYPE_CONTAINER
|
|
];
|
|
foreach($preg as $lst => $reg){
|
|
if(preg_match($reg,$url,$ff)){
|
|
return array(
|
|
'link'=>$url,
|
|
'name'=>"<i class='icon-{$lst}'></i>",
|
|
'info'=>"{$ff[1]}.{$ff[2]} (external)"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
$type = 0;
|
|
if(preg_match(Regex::SERVICE_YT, $url)) $type = 1;
|
|
elseif(preg_match(Regex::SERVICE_VI, $url)) $type = 2;
|
|
elseif(preg_match(Regex::SERVICE_SC, $url)) $type = 3;
|
|
else deny(E::link__service_type());
|
|
$time = time().substr(microtime(),2,3);
|
|
$types = array(
|
|
1 => ["http://www.youtube.com/oembed?url=",Regex::SERVICE_YT_ID,"YouTube"],
|
|
2 => ["http://vimeo.com/api/oembed.json?url=",Regex::SERVICE_VI_ID,"Vimeo"],
|
|
3 => ["http://soundcloud.com/oembed?format=json&url=",Regex::SERVICE_SC_ID,"SoundCloud"]
|
|
);
|
|
$curl = function($svc){
|
|
$curl = curl_init($svc);
|
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
|
|
$result = curl_exec($curl);
|
|
curl_close($curl);
|
|
$result = json_decode($result, true);
|
|
return $result;
|
|
};
|
|
$save = function($from,$to){
|
|
$path = ROOT."static/node/".X::$node."/pre/";
|
|
$prev = $path.$to;
|
|
if(file_exists($prev)) return $to;
|
|
$ch = curl_init($from);
|
|
curl_setopt($ch,CURLOPT_HEADER,0);
|
|
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
|
|
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
|
|
curl_setopt($ch,CURLOPT_BINARYTRANSFER,1);
|
|
curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0');
|
|
$exec = curl_exec($ch);
|
|
curl_close($ch);
|
|
$fp = fopen($prev,'w+');
|
|
fwrite($fp, $exec);
|
|
fclose($fp);
|
|
return $to;
|
|
};
|
|
$result = $curl($types[$type][0].$url);
|
|
if(empty($result)){
|
|
deny(E::link__request());
|
|
}
|
|
if(!preg_match($types[$type][1],$result['html'],$id)){
|
|
deny(E::link__uri());
|
|
}
|
|
switch($type){
|
|
case 1:
|
|
return array(
|
|
'link'=>"https://www.youtube-nocookie.com/embed/{$id[2]}",
|
|
'name'=>$save($result['thumbnail_url'],"yt-{$id[2]}.jpg"),
|
|
'info'=>$result['title']." (YouTube)"
|
|
);
|
|
break;
|
|
case 2:
|
|
return array(
|
|
'link'=>"https://player.vimeo.com/video/{$id[2]}?color=bbd531&title=0&byline=0&portrait=0&badge=0",
|
|
'name'=>$save($result['thumbnail_url'],"vi-{$id[2]}.jpg"),
|
|
'info'=>$result['title']." (Vimeo)"
|
|
);
|
|
break;
|
|
case 3:
|
|
return array(
|
|
'link'=>"https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/{$id[2]}&auto_play=false&show_artwork=true",
|
|
'name'=>$save($result['thumbnail_url'],"sc-{$id[2]}.jpg"),
|
|
'info'=>$result['title']." (SoundCloud)"
|
|
);
|
|
break;
|
|
default: deny(E::link__response()); break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Copy and format files as many-to-many structure
|
|
*/
|
|
static function files($literal, $tid, $pid, $data){
|
|
if(!empty($data)){
|
|
$iterator = 0;
|
|
$lid = X::$tree[$literal]['id'];
|
|
while(!empty($data["name"][$iterator])){
|
|
if($iterator > X::$conf['kt']['maxfiles']){ break; }
|
|
$hash = md5_file($data["tmp_name"][$iterator]);
|
|
$existing = Q::file__find_hash($literal, $lid, $hash);
|
|
if(empty($existing)){
|
|
$temp = Handle::file(
|
|
$data["name"][$iterator],
|
|
$data["tmp_name"][$iterator],
|
|
$data["size"][$iterator],
|
|
$data["type"][$iterator],
|
|
$data["error"][$iterator]
|
|
);
|
|
$unique_name = addslashes($temp['unique_name']);
|
|
$original_name = addslashes($temp['original_name']);
|
|
$finfo = addslashes($temp['finfo']);
|
|
$size = $data["size"][$iterator];
|
|
} else {
|
|
$unique_name = $existing['name'];
|
|
$original_name = $existing['orig'];
|
|
$finfo = $existing['info'];
|
|
$size = $existing['size'];
|
|
}
|
|
|
|
Q::file__push($literal, $lid, $tid, $pid, $unique_name, $original_name, $finfo, $size, $hash);
|
|
|
|
$iterator++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move and convert the file
|
|
*/
|
|
static function file($original_name, $tmp_name, $size, $type, $error){
|
|
$WINDOWS = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
|
|
if(!static::validate($tmp_name, $size, $error)){
|
|
return false;
|
|
}
|
|
|
|
$name = mb_substr(Regex::get_file_name($original_name), 0, 96);
|
|
$type = mb_substr(mb_strtolower(Regex::get_extension($original_name)), 0, 8);
|
|
$mark = time().substr(microtime(),2,3);
|
|
if(!isset($type[1])){
|
|
deny(E::file__proc_name());
|
|
}
|
|
|
|
$unique_name = $mark.".".$type;
|
|
$dir = ROOT."static/node/".X::$node;
|
|
|
|
if(!file_exists($d = "{$dir}/src")) mkdir($d, 0770);
|
|
if(!file_exists($d = "{$dir}/pre")) mkdir($d, 0770);
|
|
|
|
$src = $dir."/src/".$unique_name;
|
|
$pre = $dir."/pre/".$unique_name;
|
|
|
|
if(!move_uploaded_file($tmp_name, $src)){
|
|
deny(E::file__proc_copy());
|
|
}
|
|
if(!file_exists($src) || !file_get_contents($src,0,NULL,0,1)){
|
|
deny(E::file__proc_read_size($original_name));
|
|
}
|
|
|
|
$mime = $WINDOWS
|
|
? mb_strtolower(trim(mime_content_type($src)))
|
|
: mb_strtolower(trim(array_pop(explode(' ',`mimetype -M "{$src}"`))));
|
|
$information = ""; $dim = ""; $duration = "";
|
|
#$size = Chrona::bytes($size);
|
|
$attach = function($finfo) use ($original_name, $unique_name){
|
|
return array(
|
|
'unique_name' => $unique_name,
|
|
'original_name' => $original_name,
|
|
'finfo' => $finfo
|
|
);
|
|
};
|
|
|
|
|
|
# image
|
|
if(preg_match(Regex::MIME_IMAGE_STRICT, $mime)){
|
|
$x = X::$conf['kt']['prevsize'];
|
|
$y = $x;
|
|
switch(X::$conf['gl']){
|
|
case "im":
|
|
$img = new Imagick($src);
|
|
$dim = $img->getImageGeometry();
|
|
$x = $dim['width'];
|
|
$y = $dim['height'];
|
|
break;
|
|
case "gd":
|
|
$temp = getimagesize($src);
|
|
$x = $temp[0];
|
|
$y = $temp[1];
|
|
break;
|
|
default:
|
|
if($WINDOWS){
|
|
# well, fuck
|
|
} else {
|
|
$dim = shell_exec("identify -format '%wx%h' ".escapeshellarg($src));
|
|
if(preg_match('/(\d+)x(\d+)/', $dim, $dim_matches)){
|
|
$x = $dim_matches[1];
|
|
$y = $dim_matches[2];
|
|
}
|
|
}
|
|
}
|
|
|
|
self::unexif($src, $WINDOWS);
|
|
if($x > X::$conf['kt']['prevsize'] || $y > X::$conf['kt']['prevsize'] || $size > 8 * 1024) {
|
|
self::thumbnail($src, $pre, $mime);
|
|
} else {
|
|
copy($src, Regex::get_file_name($pre).".jpg");
|
|
}
|
|
|
|
$information = "{$x}x{$y}";
|
|
return $attach($information);
|
|
}
|
|
# audio/video/flash
|
|
elseif(preg_match(Regex::MIME_MULTIMEDIA, $mime)){
|
|
$stderr = ($WINDOWS ? "" : "2>&1");
|
|
$ffprobe = empty(X::$conf['ff']['probe'])
|
|
? "ffprobe"
|
|
: X::$conf['ff']['probe'];
|
|
$ffprobe_flags = implode(' ', [
|
|
"-v quiet",
|
|
"-hide_banner",
|
|
"-print_format json",
|
|
"-show_format",
|
|
"-show_streams",
|
|
#"-read_frames"
|
|
]);
|
|
$filepath = $src;
|
|
#dump(`{$ffprobe} -i {$filepath} {$ffprobe_flags} {$stderr}`);
|
|
$output = `{$ffprobe} {$ffprobe_flags} {$filepath} {$stderr}`;
|
|
$info = json_decode($output, true);
|
|
if(empty($info)){
|
|
unlink($src);
|
|
deny(E::file__proc_corrupt(basename($src)));
|
|
}
|
|
$d = Chrona::time(abs(intval($info['streams'][0]['duration'])));
|
|
$ct = $info['streams'][0]['codec_type'];
|
|
$xy = X::$conf['kt']['prevsize'];
|
|
if(preg_match(Regex::MIME_AUDIO_STRICT, $mime)){
|
|
$br = Chrona::bits($info['streams'][0]['bit_rate']);
|
|
return $attach("{$d}<br>{$br}");
|
|
}
|
|
if(preg_match(Regex::MIME_VIDEO_STRICT, $mime)){
|
|
$w = $info['streams'][0]['width'];
|
|
$h = $info['streams'][0]['height'];
|
|
$pre = $dir."/pre/".$mark.".jpg";
|
|
$output = $pre;
|
|
if($WINDOWS){
|
|
self::thumbnail($src, $pre, $mime);
|
|
} else {
|
|
$ffmpeg = empty(X::$conf['ff']['mpeg'])
|
|
? "ffmpeg"
|
|
: X::$conf['ff']['mpeg'];
|
|
$ffmpeg_flags = implode(' ', [
|
|
"-frames:v 1",
|
|
"-q:v 2",
|
|
"-vf 'scale={$xy}:{$xy}:force_original_aspect_ratio=increase,crop={$xy}:{$xy}'"
|
|
]);
|
|
$ss = "";#-ss 00:00:01
|
|
shell_exec("{$ffmpeg} {$ss} -i {$filepath} {$ffmpeg_flags} {$output}");
|
|
if(!file_exists($output)){
|
|
$output = $dir."/pre/nani.jpg";
|
|
}
|
|
}
|
|
return $attach("{$d}<br>{$w}x{$h}");
|
|
}
|
|
if(preg_match(Regex::MIME_FLASH_STRICT, $mime)){
|
|
$w = y($info['streams'][1]['width'],'?');
|
|
$h = y($info['streams'][1]['height'], '?');
|
|
return $attach("{$d}<br>{$w}x{$h}");
|
|
}
|
|
unlink($src);
|
|
deny(E::file__proc_unknown(basename($src)));
|
|
}
|
|
# source
|
|
elseif(preg_match(Regex::MIME_PASTE, $mime, $format)){
|
|
return $attach("Source");
|
|
}
|
|
# archive
|
|
elseif(preg_match(Regex::MIME_ARCHIVE, $mime, $format)){
|
|
return $attach("{$type} archive");
|
|
}
|
|
# torrent
|
|
elseif(preg_match(Regex::MIME_BITTORRENT, $mime)){
|
|
return $attach("BitTorrent");
|
|
}
|
|
# binary # ATAC
|
|
else deny(E::file__mime($mime));
|
|
return false;
|
|
}
|
|
|
|
static function validate($tmp_name, $size, $error){
|
|
if($size > X::$conf['kt']['filesize']){
|
|
deny(E::file__size_config(X::$conf['kt']['filesize']));
|
|
}
|
|
$hash = decode(Data::HASHLIST);
|
|
if(in_array(md5_file($tmp_name), $hash['banned'])){
|
|
deny(E::file__banned());
|
|
}
|
|
switch($error){
|
|
case UPLOAD_ERR_OK: return true; break;
|
|
case UPLOAD_ERR_FORM_SIZE: deny(E::file__size_config(X::$conf['kt']['filesize'])); break;
|
|
case UPLOAD_ERR_INI_SIZE: deny(E::file__size_server(ini_get('upload_max_filesize'))); break;
|
|
case UPLOAD_ERR_PARTIAL: deny(E::file__partial()); break;
|
|
case UPLOAD_ERR_NO_FILE: deny(E::file__lost()); break;
|
|
case UPLOAD_ERR_NO_TMP_DIR: deny(E::file__temp_dir()); break;
|
|
case UPLOAD_ERR_CANT_WRITE: deny(E::file__write()); break;
|
|
default: return deny(E::file__default()); break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static function unexif($src, $WINDOWS){
|
|
if(X::$conf['gl'] == 'im'){
|
|
exec("convert ".escapeshellarg($source_image_path)." -strip ".escapeshellarg($source_image_path));
|
|
} elseif(X::$conf['gl'] == 'gd'){
|
|
|
|
}
|
|
}
|
|
|
|
static function thumbnail($source_image_path, $thumbnail_image_path, $mimetype){
|
|
if(X::$conf['gl'] == 'im'){ # easy way
|
|
$gif = (substr($source_image_path, -3) == 'gif');
|
|
$convert = 'convert '.escapeshellarg($source_image_path.($gif ? '[0]' : ''));
|
|
$convert .= ' -resize '.X::$conf['kt']['prevsize'].'x'.X::$conf['kt']['prevsize'].' -quality 50';
|
|
$convert .= ' '.escapeshellarg(Regex::get_file_name($thumbnail_image_path).".jpg");
|
|
exec($convert);
|
|
return is_file($thumbnail_image_path);
|
|
} elseif(X::$conf['gl'] == 'gd'){ # pierdole way
|
|
$e = function($msg = "unknown error"){ debug(E::file__proc_thumbnail($msg)); return false; };
|
|
$source_gd_image = false;
|
|
$preprocess = function() use (&$source_gd_image, &$thumbnail_gd_image){
|
|
if(false === $source_gd_image){
|
|
return $e("unable to create image");
|
|
} else try {
|
|
$source_image_width = imageSX($source_gd_image);
|
|
$source_image_height = imageSY($source_gd_image);
|
|
} catch (Exception $E) {
|
|
return $e("image sXY failed");
|
|
}
|
|
$aspect = 1; # aspect on percents
|
|
if($source_image_width > $source_image_height) $aspect = X::$conf['kt']['prevsize'] / $source_image_width;
|
|
else $aspect = X::$conf['kt']['prevsize'] / $source_image_height;
|
|
$thumbnail_image_width = round($source_image_width * $aspect);
|
|
$thumbnail_image_height = round($source_image_height * $aspect);
|
|
try {
|
|
$thumbnail_gd_image = imagecreatetruecolor($thumbnail_image_width, $thumbnail_image_height);
|
|
$white = imagecolorallocate($thumbnail_gd_image,0,0,0); # black background
|
|
imagefill($thumbnail_gd_image, 0, 0, $white);
|
|
self::fastimagecopyresampled($thumbnail_gd_image, $source_gd_image,
|
|
0, 0, 0, 0,
|
|
$thumbnail_image_width, $thumbnail_image_height,
|
|
$source_image_width, $source_image_height);
|
|
} catch (Exception $E) {
|
|
return $e("resampling failed");
|
|
}
|
|
};
|
|
try {
|
|
preg_match(Regex::MIME_IMAGE_STRICT,$mimetype,$type);
|
|
if(!isset($type[1])){
|
|
return $e("invalid mimetype");
|
|
}
|
|
$success = false;
|
|
switch($type[1]){
|
|
case 'jpg': case 'jpeg':
|
|
$source_gd_image = imagecreatefromjpeg($source_image_path);
|
|
$preprocess();
|
|
$success = imagejpeg($thumbnail_gd_image, $thumbnail_image_path, 50);
|
|
break;
|
|
case 'png': case 'apng':
|
|
$source_gd_image = imagecreatefrompng($source_image_path);
|
|
$preprocess();
|
|
#$success = imagepng($thumbnail_gd_image, $thumbnail_image_path,0,PNG_ALL_FILTERS);
|
|
$success = imagejpeg($thumbnail_gd_image, $thumbnail_image_path, 50);
|
|
break;
|
|
case 'gif':
|
|
$source_gd_image = imagecreatefromgif($source_image_path);
|
|
$preprocess();
|
|
#$success = imagegif($thumbnail_gd_image, $thumbnail_image_path);
|
|
$success = imagejpeg($thumbnail_gd_image, $thumbnail_image_path, 50);
|
|
break;
|
|
case 'webp':
|
|
$source_gd_image = imagecreatefromwebp($source_image_path);
|
|
$preprocess();
|
|
#$success = imagewebp($thumbnail_gd_image, $thumbnail_image_path);
|
|
$success = imagejpeg($thumbnail_gd_image, $thumbnail_image_path, 50);
|
|
break;
|
|
default: return $e("failed at failing and flailed"); break;
|
|
}
|
|
imagedestroy($source_gd_image);
|
|
imagedestroy($thumbnail_gd_image);
|
|
if(!$success){
|
|
return $e("postprocess");
|
|
}
|
|
} catch (Exception $E) { return $e('got an exception: '.$E->getMessage()); }
|
|
return true;
|
|
} else {
|
|
debug("Image library not specified, skipping thumbnail generation");
|
|
}
|
|
return false;
|
|
}
|
|
static function fastimagecopyresampled(&$dst_image,$src_image,$dst_x,$dst_y,$src_x,$src_y,$dst_w,$dst_h,$src_w,$src_h,$quality=2){
|
|
/**
|
|
* Between 0 and 1 = Fast, but mosaic results, closer to 0 increases the mosaic effect.
|
|
* 1 = Up to 350 times faster. Poor results, looks very similar to imagecopyresized.
|
|
* 2 = Up to 95 times faster. Images appear a little sharp, some prefer this over a quality of 3.
|
|
* 3 = Up to 60 times faster. Will give high quality smooth results very close to imagecopyresampled, just faster.
|
|
* 4 = Up to 25 times faster. Almost identical to imagecopyresampled for most images.
|
|
* 5 = No speedup. Just uses imagecopyresampled, no advantage over imagecopyresampled.
|
|
*/
|
|
if(empty($src_image) || empty($dst_image) || $quality <= 0) { return false; }
|
|
if($quality < 5 && (($dst_w*$quality) < $src_w || ($dst_h*$quality) < $src_h)) {
|
|
$temp = imagecreatetruecolor($dst_w*$quality+1,$dst_h*$quality+1);
|
|
$white = imagecolorallocate($temp,255,255,255);
|
|
imagefill($temp,0,0,$white);
|
|
imagecopyresized($temp,$src_image,0,0,$src_x,$src_y,$dst_w*$quality+1,$dst_h*$quality+1,$src_w,$src_h);
|
|
imagecopyresampled($dst_image,$temp,$dst_x,$dst_y,0,0,$dst_w,$dst_h,$dst_w*$quality,$dst_h*$quality);
|
|
imagedestroy($temp);
|
|
} else imagecopyresampled($dst_image,$src_image,$dst_x,$dst_y,$src_x,$src_y,$dst_w,$dst_h,$src_w,$src_h);
|
|
return true;
|
|
}
|
|
static function compare($fn1, $fn2){ # compares 2 files by headers
|
|
$RLEN = 4096; $same = true;
|
|
if((filetype($fn1)!==filetype($fn2))||(filesize($fn1)!==filesize($fn2))) return false;
|
|
if(!$fp1 = fopen($fn1, 'rb')) return false;
|
|
if(!$fp2 = fopen($fn2, 'rb')){ fclose($fp1); return false; }
|
|
while (!feof($fp1) and !feof($fp2)) if(fread($fp1, $RLEN) !== fread($fp2, $RLEN)){ $same = false; break; }
|
|
if(feof($fp1)!==feof($fp2)) $same = false;
|
|
fclose($fp1); fclose($fp2);
|
|
return $same;
|
|
}
|
|
static function references($literal, $tid, $pid, $text){
|
|
if(!X::$conf['kt']['wiremaps']){
|
|
return;
|
|
}
|
|
preg_match_all(Regex::GENERIC_REF_LIT, $text, $reflink_lit);
|
|
preg_match_all(Regex::GENERIC_REF_NUM, $text, $reflink_num);
|
|
$update = function($N, $W, $P) use ($literal, $tid, $pid){
|
|
if(isset(X::$tree[$N])){
|
|
if($W > 0){
|
|
if(!file_exists($f = ROOT."static/node/{$N}/map/{$W}.json")){
|
|
touch($f);
|
|
encode("static/node/{$N}/map/{$W}", []);
|
|
}
|
|
$path = "static/node/{$N}/map/{$W}";
|
|
$map = decode($path);
|
|
if(empty($map)){ $map = []; }
|
|
if(!isset($map[$P])){
|
|
$map[$P] = [];
|
|
}
|
|
$map[$P][] = [$literal, $tid, $pid];
|
|
encode($path, $map);
|
|
}
|
|
}
|
|
};
|
|
$i = 0;
|
|
if(!empty($reflink_lit)){ # cross-node reflinks >>>x/123
|
|
$n = "";
|
|
foreach($reflink_lit[3] as $i => $p){
|
|
$n = $reflink_lit[2][$i];
|
|
if(isset(X::$tree[$n])){
|
|
Katana::cata($n);
|
|
$w = intval(Request::parent($n, $p));
|
|
if($w > 0){
|
|
$update($n, $w, $p);
|
|
}
|
|
}
|
|
if(++$i > 10) break;
|
|
}
|
|
Katana::cata($literal);
|
|
}
|
|
if(!empty($reflink_num)){ # local-node reflinks >>123
|
|
foreach($reflink_num[2] as $p){
|
|
$w = intval(Request::parent($literal, $p));
|
|
if($w > 0){
|
|
$update($literal, $w, $p);
|
|
}
|
|
if(++$i > 10) break;
|
|
}
|
|
}
|
|
}
|
|
} |