config = $config + [ 'basedir' => '', 'baseurl' => '', 'mime_blacklist' => '#'. // HTML may contain cookie-stealing JavaScript and web bugs '^text/(html|(x-)?javascript)$|^application/x-shellscript$'. // PHP/Perl/Bash/etc scripts may execute arbitrary code on the server '|php|perl|python|bash|x-c?sh(e|$)'. // Client-side hazards on Internet Explorer '|^text/scriptlet$|^application/x-msdownload$'. // Windows metafile, client-side vulnerability on some systems '|^application/x-msmetafile$'. '#is', 'thumb_path' => 'thumb/', ]; } public function getPath($web_or_fs, $file) { $s = $file['sha1']; return $this->config[$web_or_fs ? 'baseurl' : 'basedir'] . '/' . substr($s, 0, 1) . '/' . substr($s, 0, 2) . '/' . $s . '.' . $file['format']; } public function getThumbPath($web_or_fs, $file, $type) { $s = $file['sha1']; $ext = $file['format'] == 'jpg' || $file['format'] == 'png' || $file['format'] == 'gif' ? $file['format'] : 'jpg'; return $this->config[$web_or_fs ? 'baseurl' : 'basedir'] . '/' . $this->config['thumb_path'] . '/' . $type . '/' .substr($s, 0, 1) . '/' . substr($s, 0, 2) . '/' . $s . '.' . $ext; } public function upload($localFile, $allowedFormats = FileUtils::ANYTHING) { $tmp_name = $localFile->getLocalPath(); $props = FileUtils::getProps($allowedFormats, $this->config['mime_blacklist'], $tmp_name, $localFile->getFileName()); $fn = $this->getPath(false, $props); if (file_exists($fn) && filesize($fn) == $props['size']) { return $props; } FileUtils::mkpath(dirname($fn), true); $m = $localFile->shouldMove ? 'rename' : 'copy'; if (!@$m($tmp_name, $fn)) { $error = error_get_last(); throw new Exception($error['message']); } chmod($fn, 0666 & ~umask()); return $props; } public function uploadUrl($url, $flags = FileUtils::ONLY_IMAGES, $curl_options = []) { if (!$url) { return NULL; } $file = NULL; if (substr($url, 0, 2) == '//') { $url = "http:$url"; } if (!self::$uploadCurl) { // Reuse handle to use keepalive when possible self::$uploadCurl = curl_init(); } curl_setopt_array(self::$uploadCurl, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, ] + $curl_options); $s = curl_exec(self::$uploadCurl); if ($s) { $tmp = tempnam(sys_get_temp_dir(), 'upl'); file_put_contents($tmp, $s); unset($s); $file = $this->upload(new LocalFile($tmp, true), $flags); @unlink($tmp); } else { // Log it as E_USER_NOTICE trigger_error(curl_error(self::$uploadCurl)); } return $file; } public function deleteFile($e) { $disk_name = $this->getPath(false, $e); if (file_exists($disk_name)) { // Remove old file unlink($disk_name); } $thumb_glob = $this->getThumbPath(false, $e, '*'); foreach (glob($thumb_glob) as $thumb) { if (file_exists($thumb)) { unlink($thumb); } } } /** * Get or generate a thumbnail and return its URL */ public function getThumb($file, $width, $height, $force = false, $crop = false, $alignY = 0.5) { $type = FileUtils::getThumbType($file, $width, $height, $crop, $alignY); if (!$type) { return NULL; } $fn = $this->getThumbPath(false, $file, $type['type']); if (!file_exists($fn) || $force) { if (substr($file['mimetype'], 0, 6) === 'video/') { $sourcefn = $this->getThumbPath(false, $file, 'src'); } else { $sourcefn = $this->getPath(false, $file); } if (!FileUtils::generateThumbnail($sourcefn, $fn, $file, $type['width'], $type['height'], $crop, $alignY)) { return NULL; } } return $this->getThumbPath(true, $file, $type); } }