?@[\\]^_`", "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 => '>>$2', Regex::GENERIC_REF_LIT => '>>>$2/$3', Regex::GENERIC_QUOTE => '
$0
', #Regex::TRIFORCE => '
 ▲\n▲ ▲

', Regex::T_WM_SPOILER => '$2', Regex::T_WM_STRIKEOUT => '$2', Regex::T_WM_BOLD => '$2', Regex::T_WM_ITALIC => '$2', Regex::T_WM_UNDERLINE => '$2', Regex::T_WM_HEADER => '

$2

', Regex::T_BB_FORMAT => '<$1>$2', Regex::T_BB_SPOILER => '$2', Regex::T_BB_TEXTWALL => '
Cut$2
', Regex::T_BB_RED => '$2', Regex::T_BB_GREEN => '$2', Regex::T_BB_BLUE => '$2' ); 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 ''.Client::browser().' on '.Client::OS().''; }, Regex::GENERIC_POSTCOUNT => function($match){ import(System::CLIENT); return ''.Client::postcount().''; }, 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 "{$final}"; }, Regex::T_RAW => function($matches){ return '
'.Regex::escape(str_replace('
',"\r\n",$matches[2])).'
'; }, Regex::T_LATEX => function($matchee){ if(X::$conf['xt']['latex']){ # } return '
'.Regex::escape(str_replace('
',"\r\n",$matches[2])).'
'; }, Regex::T_WM_CODE => function($matches){ if(X::$conf['xt']['geshi']){ # } return '
'.Regex::escape(trim(str_replace('
',"\r\n",$matches[2]))).'
'; }, Regex::T_BB_CODE => function($matches){ if(X::$conf['xt']['geshi']){ # } return '
'.Regex::escape(trim(str_replace('
',"\r\n",$matches[2]))).'
'; }, Regex::GENERIC_LINK => function($matches){ return $matches[1].$matches[2].''.$matches[3].''; #return ''.$matches[0].''; }, Regex::FS_GOOGLE => function($matches){ return ''.$matches[1].''; }, Regex::FS_DDG => function($matches){ return ''.$matches[1].''; }, Regex::FS_WIKI => function($matches){ return ''.$matches[1].''; }, Regex::FS_WOLFRAM => function($matches){ return ''.$matches[1].''; }, Regex::FS_GIT => function($matches){ return ''.$matches[1].''; } ], $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'=>"", '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}"); } 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}
{$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}
{$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; } } } }