golden hour
/home/doctorbruno/public_html/taeionline.com/support/include
⬆️ Go Up
Upload
File/Folder
Size
Actions
.MANIFEST
194.97 KB
Del
OK
.htaccess
14 B
Del
OK
JSON.php
33.13 KB
Del
OK
PasswordHash.php
6.92 KB
Del
OK
Spyc.php
31.74 KB
Del
OK
UniversalClassLoader.php
8.61 KB
Del
OK
ajax.admin.php
7.29 KB
Del
OK
ajax.config.php
4.61 KB
Del
OK
ajax.content.php
9.76 KB
Del
OK
ajax.draft.php
13.03 KB
Del
OK
ajax.email.php
2.33 KB
Del
OK
ajax.export.php
1006 B
Del
OK
ajax.filter.php
874 B
Del
OK
ajax.forms.php
13.84 KB
Del
OK
ajax.i18n.php
5.1 KB
Del
OK
ajax.kbase.php
2.93 KB
Del
OK
ajax.note.php
2.05 KB
Del
OK
ajax.orgs.php
11.73 KB
Del
OK
ajax.plugins.php
2.15 KB
Del
OK
ajax.schedule.php
4.29 KB
Del
OK
ajax.search.php
12.72 KB
Del
OK
ajax.sequence.php
3.2 KB
Del
OK
ajax.staff.php
11.62 KB
Del
OK
ajax.tasks.php
33.87 KB
Del
OK
ajax.thread.php
8.81 KB
Del
OK
ajax.tickets.php
79.7 KB
Del
OK
ajax.tips.php
1.66 KB
Del
OK
ajax.upgrader.php
2.24 KB
Del
OK
ajax.users.php
18.33 KB
Del
OK
api.cron.php
1.06 KB
Del
OK
api.tickets.php
10.36 KB
Del
OK
class.2fa.php
7.76 KB
Del
OK
class.ajax.php
1.45 KB
Del
OK
class.api.php
15.31 KB
Del
OK
class.app.php
1.49 KB
Del
OK
class.attachment.php
6.95 KB
Del
OK
class.auth.php
50.73 KB
Del
OK
class.avatar.php
6.44 KB
Del
OK
class.banlist.php
2.58 KB
Del
OK
class.base32.php
4.07 KB
Del
OK
class.businesshours.php
7.08 KB
Del
OK
class.canned.php
9.05 KB
Del
OK
class.captcha.php
1.73 KB
Del
OK
class.category.php
11.14 KB
Del
OK
class.charset.php
3.41 KB
Del
OK
class.cli.php
9.48 KB
Del
OK
class.client.php
15.39 KB
Del
OK
class.collaborator.php
5.65 KB
Del
OK
class.company.php
2.65 KB
Del
OK
class.config.php
60.36 KB
Del
OK
class.controller.php
1.15 KB
Del
OK
class.cron.php
3.6 KB
Del
OK
class.crypto.php
18.92 KB
Del
OK
class.csrf.php
2.38 KB
Del
OK
class.dept.php
33.21 KB
Del
OK
class.dispatcher.php
7.12 KB
Del
OK
class.draft.php
6.23 KB
Del
OK
class.dynamic_forms.php
64.98 KB
Del
OK
class.email.php
49 KB
Del
OK
class.error.php
1.69 KB
Del
OK
class.export.php
32.92 KB
Del
OK
class.faq.php
15.05 KB
Del
OK
class.file.php
34.59 KB
Del
OK
class.filter.php
31.9 KB
Del
OK
class.filter_action.php
23.67 KB
Del
OK
class.format.php
44.3 KB
Del
OK
class.forms.php
195.58 KB
Del
OK
class.http.php
7.16 KB
Del
OK
class.i18n.php
24.28 KB
Del
OK
class.import.php
6.9 KB
Del
OK
class.json.php
2.71 KB
Del
OK
class.knowledgebase.php
5.8 KB
Del
OK
class.list.php
42 KB
Del
OK
class.lock.php
4.05 KB
Del
OK
class.log.php
1.55 KB
Del
OK
class.mail.php
35.76 KB
Del
OK
class.mailer.php
25.75 KB
Del
OK
class.mailfetch.php
9.72 KB
Del
OK
class.mailparse.php
30.99 KB
Del
OK
class.message.php
6.42 KB
Del
OK
class.migrater.php
5.2 KB
Del
OK
class.misc.php
7.5 KB
Del
OK
class.model.php
2.3 KB
Del
OK
class.nav.php
14.15 KB
Del
OK
class.note.php
2.39 KB
Del
OK
class.oauth2.php
4.04 KB
Del
OK
class.organization.php
22.45 KB
Del
OK
class.orm.php
120.9 KB
Del
OK
class.osticket.php
20.64 KB
Del
OK
class.ostsession.php
21.86 KB
Del
OK
class.page.php
10.59 KB
Del
OK
class.pagenate.php
5.53 KB
Del
OK
class.passwd.php
1.21 KB
Del
OK
class.pdf.php
3.83 KB
Del
OK
class.plugin.php
36.13 KB
Del
OK
class.priority.php
1.81 KB
Del
OK
class.queue.php
103.51 KB
Del
OK
class.report.php
11.76 KB
Del
OK
class.role.php
11.27 KB
Del
OK
class.schedule.php
46.28 KB
Del
OK
class.search.php
61.66 KB
Del
OK
class.sequence.php
7.27 KB
Del
OK
class.session.php
19.25 KB
Del
OK
class.setup.php
3.55 KB
Del
OK
class.signal.php
4.16 KB
Del
OK
class.sla.php
9.11 KB
Del
OK
class.staff.php
60.21 KB
Del
OK
class.task.php
56.21 KB
Del
OK
class.team.php
12.31 KB
Del
OK
class.template.php
23.45 KB
Del
OK
class.thread.php
108.96 KB
Del
OK
class.thread_actions.php
17.08 KB
Del
OK
class.ticket.php
167.39 KB
Del
OK
class.timezone.php
21.94 KB
Del
OK
class.topic.php
20.06 KB
Del
OK
class.translation.php
34.79 KB
Del
OK
class.upgrader.php
13.76 KB
Del
OK
class.user.php
43.4 KB
Del
OK
class.usersession.php
7.55 KB
Del
OK
class.util.php
10.07 KB
Del
OK
class.validator.php
12.94 KB
Del
OK
class.variable.php
11.93 KB
Del
OK
class.xml.php
3.23 KB
Del
OK
class.yaml.php
1.15 KB
Del
OK
cli
-
Del
OK
client
-
Del
OK
config
-
Del
OK
fpdf
-
Del
OK
htmLawed.php
53.53 KB
Del
OK
html2text.php
33.71 KB
Del
OK
i18n
-
Del
OK
index.php
37 B
Del
OK
laminas-mail
-
Del
OK
mpdf
-
Del
OK
mysqli.php
9.55 KB
Del
OK
ost-config.php
5.63 KB
Del
OK
ost-sampleconfig.php
6.24 KB
Del
OK
pear
-
Del
OK
plugins
-
Del
OK
staff
-
Del
OK
tnef_decoder.php
19.82 KB
Del
OK
upgrader
-
Del
OK
Edit: class.file.php
<?php /********************************************************************* class.file.php Peter Rotich <peter@osticket.com> Copyright (c) 2006-2013 osTicket http://www.osticket.com Released under the GNU General Public License WITHOUT ANY WARRANTY. See LICENSE.TXT for details. vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ require_once(INCLUDE_DIR.'class.signal.php'); require_once(INCLUDE_DIR.'class.error.php'); /** * FileObject Interface * * Methods File Objects should support **/ interface FileObjectInterface { function getUId(); function getKey(); function getName(); function getData(); function getMimeType(); } /** * Represents a file stored in a storage backend. It is generally attached * to something; however company logos, login page backdrops, and other * items are also stored in the database for various purposes. * * FileType-Definitions: * The `ft` field is used to represent the type or purpose of the file * with respect to the system. These are the defined file types (placed * here as the definitions are not needed in code). * * - 'T' => Attachments * - 'L' => Logo * - 'B' => Backdrop */ class AttachmentFile extends VerySimpleModel implements FileObjectInterface { static $meta = array( 'table' => FILE_TABLE, 'pk' => array('id'), 'joins' => array( 'attachments' => array( 'reverse' => 'Attachment.file' ), ), ); static $keyCache = array(); function __onload() { // Cache for lookup in the ::lookupByHash method below static::$keyCache[$this->key] = $this; } function getHashtable() { return $this->ht; } function getInfo() { return $this->getHashtable(); } function getNumEntries() { return $this->attachments->count(); } function isCanned() { return $this->getNumEntries(); } function isInUse() { return $this->getNumEntries(); } function getId() { return $this->id; } function getUId() { return $this->getId(); } function getType() { return $this->type; } function getMimeType() { return $this->getType(); } function getBackend() { return $this->bk; } function getSize() { return $this->size; } function getName() { return $this->name; } function getKey() { return $this->key; } function getAttrs() { return $this->attrs; } function getSignature($cascade=false) { $sig = $this->signature; if (!$sig && $cascade) return $this->getKey(); return $sig; } function lastModified() { return $this->created; } function open() { return FileStorageBackend::getInstance($this); } function sendData($redirect=true, $ttl=false, $disposition='inline') { $bk = $this->open(); if ($redirect && $bk->sendRedirectUrl($disposition, $ttl)) return; @ini_set('zlib.output_compression', 'Off'); try { $bk->passthru(); } catch (IOException $ex) { Http::response(404, 'File not found'); } } function getData() { # XXX: This is horrible, and is subject to php's memory # restrictions, etc. Don't use this function! ob_start(); try { $this->sendData(false); } catch (IOException $ex) { Http::response(404, 'File not found'); } $data = &ob_get_contents(); ob_end_clean(); return $data; } function delete() { if (!parent::delete()) return false; if ($bk = $this->open()) $bk->unlink(); return true; } function makeCacheable($ttl=86400) { Http::cacheable($this->getSignature(true), $this->lastModified(), $ttl); } function display($scale=false, $ttl=86400) { $this->makeCacheable($ttl); if ($scale && extension_loaded('gd') && ($image = imagecreatefromstring($this->getData()))) { $width = imagesx($image); if ($scale <= $width) { $height = imagesy($image); if ($width > $height) { $heightp = $height * (int)$scale / $width; $widthp = $scale; } else { $widthp = $width * (int)$scale / $height; $heightp = $scale; } $thumb = imagecreatetruecolor($widthp, $heightp); $white = imagecolorallocate($thumb, 255,255,255); imagefill($thumb, 0, 0, $white); imagecopyresized($thumb, $image, 0, 0, 0, 0, $widthp, $heightp, $width, $height); header('Content-Type: image/png'); imagepng($thumb); return; } } header('Content-Type: '.($this->getType()?$this->getType():'application/octet-stream')); header('Content-Length: '.$this->getSize()); header("Content-Security-Policy: default-src 'self'"); $this->sendData(); exit(); } function getDownloadUrl($options=array()) { // Add attachment ref id if object type is set if (isset($options['type']) && !isset($options['id']) && ($a=$this->attachments->findFirst(array( 'type' => $options['type'])))) $options['id'] = $a->getId(); return static::generateDownloadUrl($this->getId(), strtolower($this->getKey()), $this->getSignature(), $options); } // Generates full download URL for external sources. // e.g. https://domain.tld/file.php?args=123 function getExternalDownloadUrl($options=array()) { global $cfg; $download = $this->getDownloadUrl($options); // Separate URL handle and args list($handle, $args) = explode('file.php?', $download); return (string) rtrim($cfg->getBaseUrl(), '/').'/file.php?'.$args; } static function generateDownloadUrl($id, $key, $hash, $options = array()) { // Expire at the nearest midnight, allow at least12 hrs access $minage = @$options['minage'] ?: 43200; $gmnow = Misc::gmtime() + $options['minage']; $expires = $gmnow + 86400 - ($gmnow % 86400); // Generate a signature based on secret content $signature = static::_genUrlSignature($id, $key, $hash, $expires); // Handler / base url $handler = @$options['handler'] ?: ROOT_PATH . 'file.php'; // Return sanitized query string $args = array( 'key' => $key, 'expires' => $expires, 'signature' => $signature, ); if (isset($options['disposition'])) $args['disposition'] = $options['disposition']; if (isset($options['id'])) $args['id'] = $options['id']; return sprintf('%s?%s', $handler, http_build_query($args)); } function verifySignature($signature, $expires) { $gmnow = Misc::gmtime(); if ($expires < $gmnow) return false; $check = static::_genUrlSignature($this->getId(), $this->getKey(), $this->getSignature(), $expires); return $signature == $check; } static function _genUrlSignature($id, $key, $signature, $expires) { $pieces = array( 'Host='.$_SERVER['HTTP_HOST'], 'Path='.ROOT_PATH, 'Id='.$id, 'Key='.strtolower($key), 'Hash='.$signature, 'Expires='.$expires, ); return hash_hmac('sha1', implode("\n", $pieces), SECRET_SALT); } function download($name=false, $disposition=false, $expires=false) { $thisstaff = StaffAuthenticationBackend::getUser(); $inline = ($thisstaff ? ($thisstaff->getImageAttachmentView() === 'inline') : false); $disposition = ((($disposition && strcasecmp($disposition, 'inline') == 0) || $inline) && strpos($this->getType(), 'image/') !== false) ? 'inline' : 'attachment'; $ttl = ($expires) ? $expires - Misc::gmtime() : false; $bk = $this->open(); if ($bk->sendRedirectUrl($disposition, $ttl)) return; $this->makeCacheable($ttl); $type = $this->getType() ?: 'application/octet-stream'; Http::download($name ?: $this->getName(), $type, null, $disposition); header('Content-Length: '.$this->getSize()); $this->sendData(false); exit(); } static function _getKeyAndHash($data=false, $file=false) { if ($file) { $sha1 = base64_encode(sha1_file($data, true)); $md5 = base64_encode(md5_file($data, true)); } else { $sha1 = base64_encode(sha1($data, true)); $md5 = base64_encode(md5($data, true)); } // Use 5 chars from the microtime() prefix and 27 chars from the // sha1 hash. This should make a sufficiently strong unique key for // file content. In the event there is a sha1 collision for data, it // should be unlikely that there will be a collision for the // microtime hash coincidently. Remove =, change + and / to chars // better suited for URLs and filesystem paths $prefix = base64_encode(sha1(microtime(), true)); $key = str_replace( array('=','+','/'), array('','-','_'), substr($prefix, 0, 5) . $sha1); // The hash is a 32-char value where the first half is from the last // 16 chars from the SHA1 hash and the last 16 chars are the last 16 // chars from the MD5 hash. This should provide for better // resiliance against hash collisions and attacks against any one // hash algorithm. Since we're using base64 encoding, with 6-bits // per char, we should have a total hash strength of 192 bits. $hash = str_replace( array('=','+','/'), array('','-','_'), substr($sha1, 0, 16) . substr($md5, 0, 16)); return array($key, $hash); } /* Function assumes the files types have been validated */ static function upload($file, $ft='T', $deduplicate=true) { if(!$file['name'] || $file['error'] || !is_uploaded_file($file['tmp_name'])) return false; list($key, $sig) = self::_getKeyAndHash($file['tmp_name'], true); $info=array('type'=>$file['type'], 'filetype'=>$ft, 'size'=>$file['size'], 'name'=>$file['name'], 'key'=>$key, 'signature'=>$sig, 'tmp_name'=>$file['tmp_name'], ); return static::create($info, $ft, $deduplicate); } static function uploadBackdrop(array $file, &$error) { if (extension_loaded('gd')) { $source_path = $file['tmp_name']; list($source_width, $source_height, $source_type) = getimagesize($source_path); switch ($source_type) { case IMAGETYPE_GIF: case IMAGETYPE_JPEG: case IMAGETYPE_PNG: break; default: $error = __('Invalid image file type'); return false; } } return self::upload($file, 'B', false); } static function uploadLogo($file, &$error, $aspect_ratio=2) { /* Borrowed in part from * http://salman-w.blogspot.com/2009/04/crop-to-fit-image-using-aspphp.html */ if (extension_loaded('gd')) { $source_path = $file['tmp_name']; list($source_width, $source_height, $source_type) = getimagesize($source_path); switch ($source_type) { case IMAGETYPE_GIF: case IMAGETYPE_JPEG: case IMAGETYPE_PNG: break; default: $error = __('Invalid image file type'); return false; } $source_aspect_ratio = $source_width / $source_height; if ($source_aspect_ratio < $aspect_ratio) { $error = __('Image is too square. Upload a wider image'); return false; } } return self::upload($file, 'L', false); } static function create(&$file, $ft='T', $deduplicate=true) { if (isset($file['encoding'])) { switch ($file['encoding']) { case 'base64': $file['data'] = base64_decode($file['data']); } } if (!isset($file['data']) && isset($file['data_cbk']) && is_callable($file['data_cbk'])) { // Allow a callback function to delay or avoid reading or // fetching ihe file contents $file['data'] = $file['data_cbk'](); } if (isset($file['data'])) { list($key, $file['signature']) = self::_getKeyAndHash($file['data']); if (!$file['key']) $file['key'] = $key; } if (isset($file['size']) && $file['size'] > 0) { // Check and see if the file is already on record $existing = static::objects()->filter(array( 'signature' => $file['signature'], 'size' => $file['size'] ))->first(); // If the record exists in the database already, a file with // the same hash and size is already on file -- just return // the file if ($deduplicate && $existing) { $file['key'] = $existing->key; return $existing; } } elseif (!isset($file['data'])) { // Unable to determine the file's size return false; } if (!$file['type'] && extension_loaded('fileinfo')) { $finfo = new finfo(FILEINFO_MIME_TYPE); if ($file['data']) $type = $finfo->buffer($file['data']); elseif ($file['tmp_name']) $type = $finfo->file($file['tmp_name']); if ($type) $file['type'] = $type; } if (!$file['type']) $file['type'] = 'application/octet-stream'; $f = new static(array( 'type' => strtolower($file['type']), 'name' => $file['name'], 'key' => $file['key'], 'ft' => $ft ?: 'T', 'signature' => $file['signature'], 'created' => SqlFunction::NOW(), )); if (isset($file['size'])) $f->size = $file['size']; if (!$f->save()) return false; // Note that this is preferred over $f->open() because the file does // not have a valid backend configured yet. ::getBackendForFile() // will consider the system configuration for storing the file $bks = array(self::getBackendForFile($f)); if (!$bks[0]->getBkChar() !== 'D') $bks[] = new AttachmentChunkedData($f); // Consider the selected backen first and then save to database // otherwise. $succeeded = false; foreach ($bks as $bk) { try { if (isset($file['tmp_name'])) { if ($bk->upload($file['tmp_name'])) { $succeeded = true; break; } } elseif ($bk->write($file['data']) && $bk->flush()) { $succeeded = true; break; } } catch (Throwable $t) { // Try next backend // Backends can throw an exception or error. // TODO: Log any exceptions and errors for debugging // purposes. } // Fallthrough to default backend if different? } if (!$succeeded) { // Unable to save data (weird) return false; } $f->bk = $bk->getBkChar(); $f->attrs = $bk->getAttrs() ?: NULL; if (!isset($file['size'])) { if ($size = $bk->getSize()) $f->size = $size; // Prefer mb_strlen, because mbstring.func_overload will // automatically prefer it if configured. elseif (extension_loaded('mbstring')) $f->size = mb_strlen($file['data'], '8bit'); // bootstrap.php include a compat version of mb_strlen else $f->size = strlen($file['data']); } $f->save(); return $f; } static function __create($file, &$errors) { return static::create($file); } /** * Migrate this file from the current backend to the backend specified. * * Parameters: * $bk - (string) type char of the target storage backend. Use * AttachmentStorageBackend::allRegistered() to get a list of type * chars and associated class names * * Returns: * True if the migration was successful and false otherwise. */ function migrate($bk) { // Copy the file to the new backend and hash the contents $target = FileStorageBackend::lookup($bk, $this); $source = $this->open(); // Initialize hashing algorithm to verify uploaded contents $algos = $target->getNativeHashAlgos(); $common_algo = 'sha1'; if ($algos && is_array($algos)) { $supported = hash_algos(); foreach ($algos as $a) { if (in_array(strtolower($a), $supported)) { $common_algo = strtolower($a); break; } } } $before = hash_init($common_algo); // TODO: Make this resumable so that if the file cannot be migrated // in the max_execution_time, the migration can be continued // the next time the cron runs try { while ($block = $source->read($target->getBlockSize())) { hash_update($before, $block); $target->write($block); } $target->flush(); } catch (Exception $e) { // Migration failed return false; } // Ask the backend to generate its own hash if at all possible if (!($target_hash = $target->getHashDigest($common_algo))) { $after = hash_init($common_algo); // Verify that the hash of the target file matches the hash of // the source file $target = FileStorageBackend::lookup($bk, $this); while ($block = $target->read()) hash_update($after, $block); $target_hash = hash_final($after); } if (hash_final($before) != $target_hash) { $target->unlink(); return false; } $this->bk = $target->getBkChar(); $this->attrs = $target->getAttrs() ?: NULL; if (!$this->save()) return false; return $source->unlink(); } /** * Considers the system's configuration for file storage selection based * on the file information and purpose (FAQ attachment, image, etc). * * Parameters: * $file - (hasharray) file information which would be passed to * ::save() for instance. * * Returns: * Instance<FileStorageBackend> backend selected based on the file * received. */ static function getBackendForFile($file) { global $cfg; $char = null; if ($cfg) { $char = $cfg->getDefaultStorageBackendChar(); } try { return FileStorageBackend::lookup($char ?: 'D', $file); } catch (Exception $x) { return new AttachmentChunkedData($file); } } static function lookupByHash($hash) { if (isset(static::$keyCache[$hash])) return static::$keyCache[$hash]; // Cache a negative lookup if no such file exists try { return parent::lookup(array('key' => $hash)); } catch (ObjectNotUnique $e) { // TODO: Figure out why key collission might be happening AND // make key (hash) unique field in the file table as a // protection measure. For now we're returning null to avoid possible wrong file // being displayed. return null; } } static function lookup($id) { return is_string($id) ? static::lookupByHash($id) : parent::lookup($id); } /* Method formats http based $_FILE uploads - plus basic validation. */ static function format($files) { global $ost; if(!$files || !is_array($files)) return null; //Reformat $_FILE for the sane. $attachments = array(); foreach($files as $k => $a) { if(is_array($a)) foreach($a as $i => $v) $attachments[$i][$k] = $v; } //Basic validation. foreach($attachments as $i => &$file) { $file['name'] = Format::sanitize($file['name']); //skip no file upload "error" - why PHP calls it an error is beyond me. if($file['error'] && $file['error']==UPLOAD_ERR_NO_FILE) { unset($attachments[$i]); continue; } if($file['error']) //PHP defined error! $file['error'] = 'File upload error #'.$file['error']; elseif(!$file['tmp_name'] || !is_uploaded_file($file['tmp_name'])) $file['error'] = 'Invalid or bad upload POST'; } unset($file); return array_filter($attachments); } /** * Removes files and associated meta-data for files which no ticket, * canned-response, or faq point to any more. */ static function deleteOrphans() { // XXX: Allow plugins to define filetypes which do not represent // files attached to tickets or other things in the attachment // table and are not logos $files = static::objects() ->filter(array( 'attachments__object_id__isnull' => true, 'ft' => 'T', 'created__lt' => SqlFunction::NOW()->minus(SqlInterval::DAY(1)), )); foreach ($files as $f) { if (!$f->delete()) break; } return true; } static function allLogos() { return static::objects() ->filter(array('ft' => 'L')) ->order_by('created'); } static function allBackdrops() { return static::objects() ->filter(array('ft' => 'B')) ->order_by('created'); } } class FileStorageBackend { var $meta; static $desc = false; static $registry; static $blocksize = 131072; static $private = false; /** * All storage backends should call this function during the request * bootstrap phase. */ static function register($typechar, $class) { self::$registry[$typechar] = $class; } static function allRegistered($private=false) { $R = self::$registry; if (!$private) { foreach ($R as $i=>$bk) { if ($bk::$private) unset($R[$i]); } } return $R; } /** * Retrieves the type char registered for this storage backend's class. * Null is returned if the backend is not properly registered. */ function getBkChar() { foreach (self::$registry as $tc=>$class) if ($this instanceof $class) return $tc; } static function isRegistered($type) { return isset(self::$registry[$type]); } static function lookup($type, $file=null) { if (!isset(self::$registry[$type])) throw new Exception("No such backend registered"); $class = self::$registry[$type]; return new $class($file); } static function getInstance($file) { if (!isset(self::$registry[$file->getBackend()])) throw new Exception("No such backend registered"); $class = self::$registry[$file->getBackend()]; return new $class($file); } /** * Returns the optimal block size for the backend. When migrating, this * size blocks would be best for sending to the ::write() method */ function getBlockSize() { return static::$blocksize; } /** * Create an instance of the storage backend linking the related file. * Information about the file metadata is accessible via the received * filed object. */ function __construct($meta) { $this->meta = $meta; } /** * Commit file to the storage backend. This method is used if the * backend cannot support writing a file directly. Otherwise, the * ::upload($file) method is preferred. * * Parameters: * $data - (string|binary) file contents to be written to the backend */ function write($data) { return false; } /** * Called after all the blocks are sent to the ::write() method. This * method should return boolean FALSE if flushing the data was * somehow inhibited. */ function flush() { return true; } /** * Upload a file to the backend. This method is preferred over ::write() * for files which are uploaded or are otherwise available out of * memory. The backend is encouraged to avoid reading the entire * contents into memory. */ function upload($filepath) { return $this->write(file_get_contents($filepath)); } /** * Returns data from the backend, optionally returning only the number * of bytes indicated at the specified offset. If the data is available * in chunks, one chunk may be returned at a time. The backend should * return boolean false when no more chunks are available. */ function read($amount=0, $offset=0) { return false; } /** * Convenience method to send all the file to standard output */ function passthru() { while ($block = $this->read()) echo $block; } /** * If the data is not stored or not available locally, a redirect * response can be sent to the user agent indicating the actual HTTP * location of the data. * * If the data is available locally, this method should return boolean * false to indicate that the read() method should be used to retrieve * the data and broker it to the user agent. */ function sendRedirectUrl($disposition='inline', $ttl=false) { return false; } /** * Requests the backend to remove the file contents. */ function unlink() { return false; } /** * Fetches a list of hash algorithms that are supported transparently * through the ::write() and ::upload() methods. After writing or * uploading file content, the ::getHashDigest($algo) method can be * called to get a hash of the remote content without fetching the * entire data stream to verify the content locally. */ function getNativeHashAlgos() { return array(); } /** * Returns a hash of the content calculated remotely by the storage * backend. If this method fails, the hash chould be calculated by * downloading the content and hashing locally */ function getHashDigest($algo) { return false; } /** * getSize * * Retrieves the size of the contents written or available to be read. * The backend should optimize this process if possible by keeping track * of the bytes written in a way apart from `strlen`. This value will be * used instead of inspecting the contents using `strlen`. */ function getSize() { return false; } /** * getAttrs * * Get backend storage attributes. * */ function getAttrs() { return false; } } /** * Attachments stored in the database are cut into 500kB chunks and stored * in the FILE_CHUNK_TABLE to overcome the max_allowed_packet limitation of * LOB fields in the MySQL database */ define('CHUNK_SIZE', 500*1024); # Beware if you change this... class AttachmentFileChunk extends VerySimpleModel { static $meta = array( 'table' => FILE_CHUNK_TABLE, 'pk' => array('file_id', 'chunk_id'), 'joins' => array( 'file' => array( 'constraint' => array('file_id' => 'AttachmentFile.id'), ), ), ); } class AttachmentChunkedData extends FileStorageBackend { static $desc = /* @trans */ "In the database"; static $blocksize = CHUNK_SIZE; function __construct($file) { $this->file = $file; $this->_chunk = 0; $this->_buffer = false; $this->eof = false; } function getSize() { $row = AttachmentFileChunk::objects() ->filter(array('file' => $this->file)) ->aggregate(array('length' => SqlAggregate::SUM(SqlFunction::LENGTH(new SqlField('filedata'))))) ->one(); return $row['length']; } function read($amount=CHUNK_SIZE, $offset=0) { # Read requested length of data from attachment chunks if ($this->eof) return false; while (strlen($this->_buffer) < $amount + $offset) { try { list($buf) = AttachmentFileChunk::objects() ->filter(array('file' => $this->file, 'chunk_id' => $this->_chunk++)) ->values_flat('filedata') ->one(); } catch (DoesNotExist $e) { $this->eof = true; break; } $this->_buffer .= $buf; } $chunk = substr($this->_buffer, $offset, $amount); $this->_buffer = substr($this->_buffer, $offset + $amount); return $chunk; } function write($what, $chunk_size=CHUNK_SIZE) { $offset=0; while ($block = substr($what, $offset, $chunk_size)) { // Chunks are considered immutable. Importing chunks should // forceable remove the contents of a file before write()ing new // chunks. Therefore, inserts should be safe. $chunk = new AttachmentFileChunk(array( 'file' => $this->file, 'chunk_id' => $this->_chunk++, 'filedata' => $block )); if (!$chunk->save()) return false; $offset += strlen($block); } return $this->_chunk; } function unlink() { return AttachmentFileChunk::objects() ->filter(array('file' => $this->file)) ->delete(); } } FileStorageBackend::register('D', 'AttachmentChunkedData'); /** * This class provides an interface for files attached on the filesystem in * versions previous to v1.7. The upgrader will keep the attachments on the * disk where they were and write the path into the `attrs` field of the * %file table. This module will continue to serve those files until they * are migrated with the `file` cli app */ class OneSixAttachments extends FileStorageBackend { static $desc = "upload_dir folder (from osTicket v1.6)"; static $private = true; function read($bytes=32768, $offset=false) { $filename = $this->meta->attrs; if (!$this->fp) $this->fp = @fopen($filename, 'rb'); if (!$this->fp) throw new IOException($filename.': Unable to open for reading'); if ($offset) fseek($this->fp, $offset); if (($status = @fread($this->fp, $bytes)) === false) throw new IOException($filename.': Unable to read from file'); return $status; } function passthru() { $filename = $this->meta->attrs; if (($status = @readfile($filename)) === false) throw new IOException($filename.': Unable to read from file'); return $status; } function write($data) { throw new IOException('This backend does not support new files'); } function upload($filepath) { throw new IOException('This backend does not support new files'); } function unlink() { $filename = $this->meta->attrs; if (!@unlink($filename)) throw new IOException($filename.': Unable to delete file'); // Drop usage of the `attrs` field $this->meta->attrs = null; $this->meta->save(); return true; } } FileStorageBackend::register('6', 'OneSixAttachments'); // FileObject - wrapper for SplFileObject class class FileObject extends SplFileObject implements FileObjectInterface { protected $_key; protected $_sig; protected $_filename; function __construct($file, $mode='r') { parent::__construct($file, $mode); } function getUId() { return $this->getKey(); } function getKey() { if (!isset($this->_key)) list($this->_key, $this->_sig) = AttachmentFile::_getKeyAndHash( $this->getContents()); return $this->_key; } /* This allows us to set REAL file name as opposed to basename of the * FS file in question */ function setFilename($filename) { $this->_filename = $filename; } function getFilename() { return $this->_filename ?: parent::getFilename(); } function getName() { return $this->getFilename(); } /* * Set mime type - well formated mime is expected. */ function setMimeType($type) { $this->_mimetype = $type; } function getMimeType() { if (!isset($this->_mimetype)) $this->_mimetype = self::mime_type($this->getRealPath()); return $this->_mimetype; } function getContents() { $this->fseek(0); return $this->fread($this->getSize()); } /* * XXX: Needed for mailer attachments interface */ function getData() { return $this->getContents(); } /* * Given a filepath - auto detect the mime type * */ static function mime_type($filepath) { // Try to to auto-detect mime type $type = null; if (function_exists('finfo_open')) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $type = finfo_file($finfo, $filepath); finfo_close($finfo); } return $type ?: mime_content_type($filepath); } /* * Compare mime type of file content to a given mime */ static function mimecmp($filepath, $mime) { return strcasecmp(self::mime_type($filepath), $mime) !== 0; } } ?>
Save