/** * Send mails. * * @api * * @param string $email * @param string $title email subject * @param string $message email content * @param array $header (optional) * * @return bool */ public static function send($email, $title, $message, array $header = []) { // Mime version header if (empty($header['MIME-Version'])) { $header['MIME-Version'] = '1.0'; } // content type header if (empty($header['Content-type'])) { $header['Content-type'] = 'text/plain; charset=UTF-8'; } // Message Id header if (empty($header['Message-ID'])) { $header['Message-ID'] = '<' . time() . rand(1, 1000) . '@' . $_SERVER['SERVER_NAME'] . '>'; } // concatenate prefix string with the subject $config = Config::get('email', self::$defaultConfig); $title = $config['subject_prefix'] . $title; // From header if (empty($header['From']) && isset($config['support'])) { $header['From'] = $config['support']; } // trim line length and remove spaces around line breaks $message = wordwrap($message, 76); $msgs = explode("\n", $message); foreach ($msgs as &$msg) { $msg = trim($msg); } $message = implode("\n", $msgs); array_walk($header, function (&$value, $key) { $value = $key . ': ' . ($key != 'Content-type' ? self::encodeMailAdress($value) : $value); }); Logger::getInstance()->info('email an: "' . $email . '" mit titel: "' . $title . '" und nachricht "' . $message . '" wurde mit "' . var_export($header, true) . '" als headern gesendet'); return mail(self::encodeMailAdress($email), mb_encode_mimeheader($title), $message, implode("\n", $header)); //return true; }
/** * @internal * * @todo get rid of registerClass */ public function __construct() { parent::__construct(); self::$defaultConfig['compile_dir'] = Application::$FILE_ROOT . 'tmp/smarty_compile'; self::$defaultConfig['cache_dir'] = Application::$FILE_ROOT . 'tmp/smarty_cache'; $config = Config::get('smarty', self::$defaultConfig); $this->setTemplateDir(dirname(dirname(__DIR__)) . '/template/html'); $this->addTemplateDir(dirname(Application::$WEB_ROOT) . '/template'); $this->addPluginsDir(__DIR__ . '/smarty_plugins/'); $this->error_reporting = E_ALL & ~E_NOTICE; $this->_file_perms = $config['file_perms']; $this->_dir_perms = $config['dir_perms']; // cache dir FileUtil::makedir($config['cache_dir'], $config['dir_perms']); $this->setCacheDir($config['cache_dir']); // compile dir FileUtil::makedir($config['compile_dir'], $config['dir_perms']); $this->setCompileDir($config['compile_dir']); // add classes for usage in templates $this->registerClass('Session', 'FeM\\sPof\\Session'); $this->registerClass('Group', 'FeM\\sPof\\model\\Group'); $this->registerClass('DBConnection', 'FeM\\sPof\\model\\DBConnection'); $this->registerClass('SessionRegister', 'FeM\\sPof\\model\\SessionRegister'); $this->registerClass('Config', 'FeM\\sPof\\Config'); $this->registerClass('Authorization', 'FeM\\sPof\\Authorization'); $this->registerClass('Auth', 'FeM\\sPof\\Authorization'); }
/** * Remove all outdated entries from list. * * @internal */ private static function cleanup() { $config = Config::get('whoisonline', self::$defaultConfig); // check if we have to update again $updated = Cache::fetch(self::PREFIX . 'users_updated'); if ($updated !== false && $updated < strtotime('-' . $config['min_update'] . ' seconds')) { return; } $users = Cache::fetch(self::PREFIX . 'users'); if (empty($users)) { return; } // remove old entries foreach ($users as $user_id => $time) { if ($time < strtotime('-' . $config['max_inactive'] . ' minutes')) { unset($users[$user_id]); } } Cache::store(self::PREFIX . 'users', $users); Cache::store(self::PREFIX . 'users_updated', time(), $config['min_update']); }
/** * Get the path where to store the cookie on the current domain. * * @internal * * @return string */ private static function getCookiePath() { return Config::getDetail('server', 'path'); }
/** * Download a file. * * @internal */ public function download() { // get and check sid if (empty($this->sid) || strlen($this->sid) > 32) { self::sendNotFound(); } $db = DBConnection::getInstance(); $db->beginTransaction(); // get file details $stmt = $db->prepare("\n SELECT\n id,\n content_oid,\n mimetype,\n size,\n name,\n to_char(modify,'Dy, DD Mon YYYY HH24:MI:SS TZ') AS modify\n FROM tbl_file\n WHERE\n sid=:sid\n AND visible IS TRUE\n AND disabled IS FALSE\n "); $stmt->bindParam('sid', $this->sid, \PDO::PARAM_INT); $stmt->execute(); if ($stmt->rowCount() == 0) { self::sendNotFound(); } $file = $stmt->fetch(\PDO::FETCH_ASSOC); // set path to unprocessed cache file $path_cachefile = sprintf(Application::$CACHE_ROOT . 'files/%s/%s.file', $this->sid[0], $this->sid); // load file from database if cache file doesn't exist $handle = false; if (file_exists($path_cachefile) === false) { // open db handle if no cached file available $handle = $db->pgsqlLOBOpen($file['content_oid'], 'r'); if (feof($handle)) { self::sendNotFound(); } // try to create cache if (!FileUtil::makedir(dirname($path_cachefile))) { error_log('cache' . $path_cachefile . ' not writeable'); } else { // create cache file $fcache = fopen($path_cachefile, 'w'); stream_copy_to_stream($handle, $fcache); fclose($fcache); // close db handle fclose($handle); $handle = false; } } // check wether path_sendfile is set explicitly if (!empty($this->path_sendfile)) { // update stat data if exists if (is_readable($this->path_sendfile)) { $stat = stat($this->path_sendfile); $file['modify'] = $stat['mtime']; $file['size'] = $stat['size']; } elseif (isset($this->processing) && method_exists($this, 'processCachefile')) { // check for processing $file['cachefile'] = $path_cachefile; $this->processCachefile($file); } else { self::sendNotFound(); } } else { $this->path_sendfile = $path_cachefile; } if (!is_readable($this->path_sendfile) && !is_resource($handle)) { self::sendNotFound(); } // check wether stats_disable is set if ($this->stats_disable === false) { // insert statistics $stmt = $db->prepare("\n INSERT INTO tbl_statistic_file (user_id, ip, file_id)\n VALUES (:user_id, :ip, :file_id)\n "); if (is_null(Session::getUserId())) { $stmt->bindValue('user_id', null, \PDO::PARAM_NULL); } else { $stmt->bindValue('user_id', Session::getUserId(), \PDO::PARAM_INT); } $stmt->bindValue('ip', Request::getIp(), \PDO::PARAM_STR); $stmt->bindValue('file_id', $file['id'], \PDO::PARAM_INT); $stmt->execute(); } $db->commit(); // there is nothing to write, tell the session it is no longer needed (and thus no longer blocking output // flushing) session_write_close(); // check if http_range is sent by browser if (isset($_SERVER['HTTP_RANGE'])) { $unit = explode('=', $_SERVER['HTTP_RANGE'])[0]; if ($unit == 'bytes') { $range_list = explode('=', $_SERVER['HTTP_RANGE'], 2)[1]; // multiple ranges could be specified at the same time, but for simplicity only serve the first range // http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt $ranges = explode(',', $range_list); foreach ($ranges as $range) { $seek_start = explode('-', $range)[0]; $seek_end = explode('-', $range)[1]; // set valid and in range for seek end if (empty($seek_end)) { $seek_end = $file['size'] - 1; } else { $seek_end = min(abs($seek_end), $file['size'] - 1); } // set valid and in range for seek start if (empty($seek_start) || $seek_end < abs($seek_start)) { $seek_start = 0; } else { $seek_start = max(abs($seek_start), 0); } if (!is_resource($handle)) { $handle = fopen($path_cachefile, 'rb'); } // seek to start of missing part if (($seek_start > 0 || $seek_end < $file['size'] - 1) && fseek($handle, $seek_start) !== -1) { $length = $seek_end - $seek_start + 1; header('HTTP/1.1 206 Partial Content'); header('Accept-Ranges: bytes'); header('Content-Range: bytes ' . $seek_start . '-' . $seek_end . '/' . $file['size']); header('Content-Type: ' . $file['mimetype']); header('Content-Disposition: attachment; filename="' . $file['name'] . '"'); header('Content-Length: ' . $length); // start buffered download of 8 KB chunks while the connection is alive $transfered = 0; while (!feof($handle) && connection_status() == CONNECTION_NORMAL && $transfered < $length) { // reset time limit for big files set_time_limit(0); // @codingStandardsIgnoreStart echo fread($handle, 8192); // @codingStandardsIgnoreEnd $transfered += 8192; flush(); ob_flush(); } // while fclose($handle); exit; } // if fseek } // foreach ranges } // if unit bytes } // if http_range // prepare headers header('Last-Modified: ' . $file['modify']); header('Accept-Ranges: bytes'); header('Content-Type: ' . $file['mimetype']); header('Content-Length: ' . $file['size']); header('Content-Transfer-Encoding: binary'); session_cache_limiter(false); if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= strtotime($file['modify'])) { header('HTTP/1.0 304 Not Modified'); exit; } if (is_resource($handle)) { // write to output buffer in small chunks to bypass memory limit of large files (which fpassthru would // exceed) while (!feof($handle) && connection_status() == CONNECTION_NORMAL) { // reset time limit for big files set_time_limit(0); // @codingStandardsIgnoreStart echo fread($handle, 8192); // @codingStandardsIgnoreEnd flush(); ob_flush(); } // while fclose($handle); } elseif (Config::get('use_sendfile') && in_array('mod_xsendfile', apache_get_modules())) { header('X-Sendfile: ' . $this->path_sendfile); } else { readfile($this->path_sendfile); exit; } }
/** * Create a new database connection. * * @internal */ public function __construct() { try { $config = array_merge(self::$defaultConfig, Config::get('database')); parent::__construct('pgsql:host=' . $config['host'] . ';' . 'dbname=' . $config['name'] . ';' . 'port=' . $config['port'], $config['user'], $config['pass']); # Config::unsetForDatabase(); $this->setAttribute(\PDO::ATTR_STATEMENT_CLASS, [__NAMESPACE__ . '\\DBStatement', [$this]]); $this->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->exec("SET CLIENT_ENCODING TO 'UTF8'"); $this->exec("SET search_path TO " . $config['namespace'] . ";"); } catch (\PDOException $e) { Logger::getInstance()->exception($e); } }
/** * log message to the session. * * @param string $message * @param string $severity */ private function sessionLog($message, $severity) { static $levels; if (!isset($levels)) { $levels = Config::get('log_level', self::$defaultConfig); } if (in_array($severity, $levels)) { Session::addErrorMsg($message); } }
/** * This function generates a new .js file from the existing files (if anything new happen to them or does not * exist). The name of the new file will be returned. * * @api * * @param array $files * * @return string|false false on error or nothing */ public static function combine(array $files) { if (empty($files)) { return false; } $target = self::getTargetPath(); $source = self::getSourcePath(); // identify file combinations by hash $jsHash = md5(serialize($files)); $targetfile = $target . $jsHash . '.js'; // check if any source file was modified $needUpdate = false; if (file_exists($targetfile)) { $hashtime = filemtime($targetfile); foreach ($files as $file) { if (substr($file['name'], 0, 1) === '/') { $filename = $file['name']; } elseif ($file['relative']) { // relative to public/js $filename = $target . $file['name'] . '.js'; } else { // use javascript folder $filename = $source . $file['name'] . '.js.tpl'; } if ($hashtime < filemtime($filename)) { $needUpdate = true; break; } } } else { // file does not exist, so we need an update anyway $needUpdate = true; } // we can abort if no update is required if ($needUpdate === false) { return $jsHash; } // make sure, that the target directory exists if (!is_dir($target)) { FileUtil::makedir($target); } // combine file contents $content = ''; foreach ($files as $file) { try { if (substr($file['name'], 0, 1) === '/') { if (strpos($file['name'], '.tpl') > 0) { $nextcontent = JavascriptTemplate::getInstance()->fetch($file['name']); } else { $nextcontent = file_get_contents($file['name']); } } elseif ($file['relative']) { $nextcontent = file_get_contents($target . $file['name'] . '.js'); } else { $nextcontent = JavascriptTemplate::getInstance()->fetch($source . $file['name'] . '.js.tpl'); } // do not double minify if (strpos($file['name'], '.min') > 0 || strpos($file['name'], '.pack') > 0 || !method_exists('\\JShrink\\Minifier', 'minify')) { $content .= $nextcontent . "\n"; } else { $content .= \JShrink\Minifier::minify($nextcontent) . "\n"; } } catch (\ErrorException $exception) { Logger::getInstance()->exception($exception); } } // foreach file // write minified version $success = file_put_contents($targetfile, $content); // adjust file permissions for webserver if ($success) { chmod($targetfile, Config::getDetail('smarty', 'file_perms', self::$defaultConfig)); } // foreach scssFiles return $jsHash; }
/** * Get current config setting. * * @internal * * @param $name * @return array */ private static function getConfig($name) { static $config; if (!isset($config)) { $config = Config::get('session', self::$defaultConfig); } return $config[$name]; }
/** * This function generates a new .css file from the existing files (if anything new happen to them or does not * exist). The name of the new file will be returned. * * @api * * @param array $files * * @return string|false false on error or nothing */ public static function combine(array $files) { if (empty($files)) { return false; } $target = self::getTargetPath(); $source = self::getSourcePath(); // identify file combinations by hash $cssHash = md5(serialize($files)); $targetfile = $target . $cssHash . '.css'; // check if any source file was modified $needUpdate = false; if (file_exists($targetfile)) { $hashtime = filemtime($targetfile); foreach ($files as $file) { if ($hashtime < filemtime($file['name'])) { $needUpdate = true; break; } } } else { // file does not exist, so we need an update anyway $needUpdate = true; } // we can abort if no update is required if ($needUpdate === false) { return $cssHash; } // combine file contents $handle = fopen($targetfile, 'w+'); foreach ($files as $file) { $content = file_get_contents($file['name']); if ($file['fixpaths']) { preg_match_all('/url\\(([^)]+)\\)/', $content, $matches, PREG_SET_ORDER); $replaces = []; $copy = []; foreach ($matches as $match) { if (strpos($match[1], 'data') === 0) { continue; } $filename = 'gen__' . md5($file['name'] . '-' . $match[1]) . preg_replace('/^[^.]+\\.(.+)$/', '.$1', $match[1]); $replaces[$match[0]] = 'url(../img/' . $filename . ')'; $copy[dirname($file['name']) . '/' . $match[1]] = Application::$WEB_ROOT . 'img/' . $filename; } // replace usage in stylesheet and copy file to be accessible via web $content = str_replace(array_keys($replaces), $replaces, $content); foreach ($copy as $source => $target) { try { copy($source, $target); } catch (\ErrorException $e) { Logger::getInstance()->exception($e); } } } fwrite($handle, self::minify($content)); } // foreach file fclose($handle); // adjust file permissions for webserver chmod($targetfile, Config::getDetail('stylesheet', 'file_perms', self::$defaultConfig)); return $cssHash; }
/** * Redirect to route. * * @internal * * @param string $route * @param array $arguments */ public static function redirect($route, array $arguments) { $server = Config::get('server'); self::urlRedirect('//' . $_SERVER['SERVER_NAME'] . $server['path'] . self::reverse($route, $arguments)); }