/** * Get an update for the current user. */ public function update() { if (!Session::isLoggedIn()) { exit; } header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('Access-Control-Allow-Origin: *'); $lastEventId = floatval(isset($_SERVER['HTTP_LAST_EVENT_ID']) ? $_SERVER['HTTP_LAST_EVENT_ID'] : 0); if ($lastEventId == 0) { $lastEventId = floatval(isset($_GET['lastEventId']) ? $_GET['lastEventId'] : 0); } // 2kB padding for IE // @codingStandardsIgnoreStart # echo ':'.str_repeat(' ', 2048)."\n"; echo 'retry: ' . self::$RETRY * 1000 . PHP_EOL; // event-stream $started = time(); //while (true) { $notification = NotificationBrowser::getNextByUserId(Session::getUserId()); if ($notification !== false) { $data = ['type' => 'notification', 'title' => $notification['title'], 'payload' => $notification['content']]; NotificationBrowser::deleteByPK($notification['id']); $lastEventId++; echo 'id: ' . $lastEventId . PHP_EOL; echo 'data: ' . str_replace("\n", "\ndata: ", json_encode($data)) . PHP_EOL . PHP_EOL; ob_flush(); flush(); // @codingStandardsIgnoreEnd // } elseif ((time() - $started) % self::$KEEP_ALIVE == 0) { // // send keep alive comment // echo ': '.sha1(mt_rand())."\n\n"; } // if (time() - $started > self::$TIMEOUT) { // break; // } // usleep(self::$RETRY * 1000000); //} exit; }
/** * Get all members of a group. * * @api * * @param int $group_id * @return array * @todo get rid of Session here */ public static function getMembersById($group_id) { $stmt = self::createStatement("\n SELECT\n u.id AS user_id,\n u.name AS user_name,\n u.firstname,\n u.lastname,\n u.lastupdate,\n u.email,\n u.email AS email_uni,\n b.target_user_id AS buddy,\n (\n SELECT d.title\n FROM tbl_diary d\n WHERE d.user_id=u.id\n AND d.disabled IS FALSE\n ORDER BY d.creation DESC\n LIMIT 1\n ) AS diary_title,\n (\n SELECT string_agg(class_names, ' ')\n FROM tbl_role ro\n JOIN rel_group_user_role rr ON (rr.role_id=ro.id AND rr.group_id=r.group_id)\n WHERE rr.user_id=u.id AND class_names IS NOT NULL\n ) AS class_names\n FROM tbl_user u\n JOIN rel_user_group r ON r.user_id=u.id\n LEFT JOIN tbl_buddy b ON b.target_user_id=u.id AND b.owner_user_id=:user_id\n WHERE\n r.group_id=:group_id\n AND r.requested IS FALSE\n AND r.visible IS TRUE\n AND r.disabled IS FALSE\n ORDER BY u.name ASC\n "); $stmt->assignId('group_id', $group_id); $stmt->assignId('user_id', Session::getUserId()); return $stmt->fetchAll(); }
/** * 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; } }
/** * * @internal * * @param array $reference reference ids * @param string $description (optional) additional information * @param bool $success (optional) */ protected final function logEvent(array $reference, $description = null, $success = false) { LogEvent::add(['event' => Router::getModule() . '.' . $this->command . '.' . ($success ? 'Success' : 'Fail'), 'user_id' => Session::getUserId(), 'reference_parameters' => json_encode($reference), 'description' => $description]); }