private function _initialize($unconditional_start = false) { static $initialized = false; if ($initialized) { return true; } if (!$GLOBALS['midcom_config']['sessioning_service_enable'] && !($GLOBALS['midcom_config']['sessioning_service_always_enable_for_users'] && midcom_connection::get_user())) { return false; } // Try to start session only if the client sends the id OR we need to set data if (!isset($_REQUEST[session_name()]) && !$unconditional_start) { return false; } if (_midcom_headers_sent()) { // Don't try starting a session if we're past the headers phase debug_add("Aborting session start, headers have already been sent", MIDCOM_LOG_WARN); return; } $track_state = ini_get('track_errors'); ini_set('track_errors', true); @session_start(); $session_err = null; if (isset($php_errormsg)) { $session_err = (string) $php_errormsg; } ini_set('track_errors', $track_state); unset($track_state); if (!isset($_SESSION)) { debug_add("\$_SESSION is not set, error message was: {$session_err}", MIDCOM_LOG_ERROR); unset($session_err, $php_errormsg); return false; } unset($session_err); /* Cache disabling made conditional based on domain/key existence */ // Check for session data and load or initialize it, if necessary if (!array_key_exists('midcom_session_data', $_SESSION)) { $_SESSION['midcom_session_data'] = array(); $_SESSION['midcom_session_data']['midcom.service.sessioning']['startup'] = serialize(time()); } $initialized = true; return true; }
/** * Log a message * * @param string $message The message to be logged * @param int $loglevel The log level */ function log($message, $loglevel = MIDCOM_LOG_DEBUG) { if (!$this->_enabled || $this->_loglevel < $loglevel) { return; } $file = fopen($this->_filename, 'a+'); if (function_exists('xdebug_memory_usage')) { static $lastmem = 0; $curmem = xdebug_memory_usage(); $delta = $curmem - $lastmem; $lastmem = $curmem; $prefix = sprintf("%s (%012.9f, %9s, %7s):\t", date('M d Y H:i:s'), xdebug_time_index(), number_format($curmem, 0, ',', '.'), number_format($delta, 0, ',', '.')); } else { $prefix = date('M d Y H:i:s') . "\t"; } if (array_key_exists($loglevel, $this->_loglevels)) { $prefix .= '[' . $this->_loglevels[$loglevel] . '] '; } //find the proper caller $bt = debug_backtrace(false); $prefix .= $this->_get_caller($bt); fputs($file, $prefix . trim($message) . "\n"); fclose($file); if ($this->firephp && !_midcom_headers_sent()) { try { $log_method = $this->_loglevels[$loglevel]; if ($loglevel == MIDCOM_LOG_DEBUG) { $log_method = 'log'; } if ($loglevel == MIDCOM_LOG_CRIT) { $log_method = 'error'; } $this->firephp->{$log_method}($message); } catch (Exception $e) { // Ignore FirePHP errors for now } } }
/** * Checks, whether the browser supplied if-modified-since or if-none-match headers * match the passed etag/last modified timestamp. If yes, a 304 not modified header * is emitted and true is returned. Otherwise the function will return false * without modifications to the current runtime state. * * If the headers have already been sent, something is definitely wrong, so we * ignore the request silently returning false. * * Note, that if both If-Modified-Since and If-None-Match are present, both must * actually match the given stamps to allow for a 304 Header to be emitted. * * @param int $last_modified The last modified timestamp of the current document. This timestamp * is assumed to be in <i>local time</i>, and will be implicitly converted to a GMT time for * correct HTTP header comparisons. * @param string $etag The etag header associated with the current document. * @return boolean True, if an 304 match was detected and the appropriate headers were sent. */ function _check_not_modified($last_modified, $etag, $additional_headers = array()) { if (_midcom_headers_sent()) { debug_add("The headers have already been sent, cannot do a not modified check.", MIDCOM_LOG_INFO); return false; } // These variables are set to true if the corresponding header indicates a 403 is // possible. $if_modified_since = false; $if_none_match = false; if (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER)) { if ($_SERVER['HTTP_IF_NONE_MATCH'] != $etag) { // The E-Tag is different, so we cannot 304 here. debug_add("The HTTP supplied E-Tag requirement does not match: {$_SERVER['HTTP_IF_NONE_MATCH']} (!= {$etag})"); return false; } $if_none_match = true; } if (array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) { $tmp = $_SERVER['HTTP_IF_MODIFIED_SINCE']; if (strpos($tmp, 'GMT') === false) { $tmp .= ' GMT'; } $modified_since = strtotime($tmp); if ($modified_since < $last_modified) { // Last Modified does not match, so we cannot 304 here. debug_add("The supplied HTTP Last Modified requirement does not match: {$_SERVER['HTTP_IF_MODIFIED_SINCE']}."); debug_add("If-Modified-Since: ({$modified_since}) " . gmdate("D, d M Y H:i:s", $modified_since) . ' GMT'); debug_add("Last-Modified: ({$last_modified}) " . gmdate("D, d M Y H:i:s", $last_modified) . ' GMT'); return false; } $if_modified_since = true; } if (!$if_modified_since && !$if_none_match) { return false; } if ($this->_obrunning) { // Drop the output buffer, if any. ob_end_clean(); } // Emit the 304 header, then exit. _midcom_header('HTTP/1.0 304 Not Modified'); _midcom_header("ETag: {$etag}"); foreach ($additional_headers as $header) { _midcom_header($header); } return true; }
/** * Show an error page. * * This function is a small helper, that will display a simple HTML Page reporting * the error described by $httpcode and $message. The $httpcode is also used to * send an appropriate HTTP Response. * * The error pages can be customized by creating style elements named midcom_error_$httpcode. * * For a list of the allowed HTTP codes see the MIDCOM_ERR... constants * * <b>Note:</b> This function will call _midcom_stop_request() after it is finished. * * @link http://www.midgard-project.org/documentation/styling-midcom-error-pages/ Styling MidCOM error pages * @param int $httpcode The error code to send. * @param string $message The message to print. */ public function show($httpcode, $message) { if (!$this->_exception) { debug_add("An error has been generated: Code: {$httpcode}, Message: {$message}"); debug_print_function_stack('Stacktrace:'); } // Send error to special log or recipient as per in configuration. $this->send($httpcode, $message); if (_midcom_headers_sent()) { debug_add("Generate-Error was called after sending the HTTP Headers!", MIDCOM_LOG_ERROR); debug_add("Unexpected Error: {$httpcode} - {$message}", MIDCOM_LOG_ERROR); _midcom_stop_request("Unexpected Error, this should display an HTTP {$httpcode} - " . htmlentities($message)); } switch ($httpcode) { case MIDCOM_ERROK: $header = "HTTP/1.0 200 OK"; $title = "OK"; $code = 200; break; case MIDCOM_ERRNOTFOUND: $header = "HTTP/1.0 404 Not Found"; $title = "Not Found"; $code = 404; break; case MIDCOM_ERRFORBIDDEN: // show access denied $this->access_denied($message); $header = "HTTP/1.0 403 Forbidden"; $title = "Forbidden"; $code = 403; break; case MIDCOM_ERRAUTH: $header = "HTTP/1.0 401 Unauthorized"; $title = "Unauthorized"; $code = 401; break; default: debug_add("Unknown Errorcode {$httpcode} encountered, assuming 500"); // Fall-through // Fall-through case MIDCOM_ERRCRIT: $header = "HTTP/1.0 500 Server Error"; $title = "Server Error"; $code = 500; break; } _midcom_header($header); _midcom_header('Content-Type: text/html'); $style = midcom::get('style'); $style->data['error_title'] = $title; $style->data['error_message'] = $message; $style->data['error_code'] = $code; $style->data['error_exception'] = $this->_exception; $style->data['error_handler'] = $this; if (!$style->show_midcom('midcom_error_' . $code)) { $style->show_midcom('midcom_error'); } debug_add("Error Page output finished, exiting now"); midcom::get('cache')->content->no_cache(); if (midcom::get()) { midcom::get()->finish(); } _midcom_stop_request(); }
/** * Deliver a blob to the client. * * This is a replacement for mgd_serve_attachment that should work around most of * its bugs: It is missing all important HTTP Headers concerning file size, * modification date and expiration. It will not call _midcom_stop_request() when it is finished, * you still have to do that yourself. It will add the following HTTP Headers: * * - Cache-Control: public max-age=$expires * - Expires: GMT Date $now+$expires * - Last-Modified: GMT Date of the last modified timestamp of the Attachment * - Content-Length: The Length of the Attachment in Bytes * - Accept-Ranges: none * * This should enable caching of browsers for Navigation images and so on. You can * influence the expiration of the served attachment with the parameter $expires. * It is the time in seconds till the client should refetch the file. The default * for this is 24 hours. If you set it to "0" caching will be prohibited by * changing the sent headers like this: * * - Pragma: no-cache * - Cache-Control: no-cache * - Expires: Current GMT Date * * If expires is set to -1, no expires header gets sent. * * @param MidgardAttachment &$attachment A reference to the attachment to be delivered. * @param int $expires HTTP-Expires timeout in seconds, set this to 0 for uncacheable pages, or to -1 for no Expire header. */ public function serve_attachment(&$attachment, $expires = -1) { if ($GLOBALS['midcom_config']['attachment_cache_enabled']) { $path = '/' . substr($attachment->guid, 0, 1) . "/{$attachment->guid}_{$attachment->name}"; if (file_exists($GLOBALS['midcom_config']['attachment_cache_root'] . $path)) { $response = new midcom_response_relocate($GLOBALS['midcom_config']['attachment_cache_url'] . $path, 301); $response->send(); } } // Sanity check expires if (!is_int($expires) || $expires < -1) { throw new midcom_error("\$expires has to be a positive integer or zero or -1, is now {$expires}."); } // Doublecheck that this is registered $cache = midcom::get('cache'); $cache->content->register($attachment->guid); $stats = $attachment->stat(); $last_modified =& $stats[9]; $app = midcom::get(); $etag = md5("{$last_modified}{$attachment->name}{$attachment->mimetype}{$attachment->guid}"); // Check etag and return 304 if necessary if ($expires != 0 && $cache->content->_check_not_modified($last_modified, $etag)) { if (!_midcom_headers_sent()) { $cache->content->cache_control_headers(); // Doublemakesure these are present $app->header('HTTP/1.0 304 Not Modified', 304); $app->header("ETag: {$etag}"); } while (@ob_end_flush()) { } debug_add("End of MidCOM run: {$_SERVER['REQUEST_URI']}"); _midcom_stop_request(); } $f = $attachment->open('r'); if (!$f) { throw new midcom_error('Failed to open attachment for reading: ' . midcom_connection::get_error_string()); } $app->header("ETag: {$etag}"); $cache->content->content_type($attachment->mimetype); $cache->content->register_sent_header("Content-Type: {$attachment->mimetype}"); $app->header("Last-Modified: " . gmdate("D, d M Y H:i:s", $last_modified) . ' GMT'); $app->header("Content-Length: " . $stats[7]); $app->header("Content-Description: {$attachment->title}"); // PONDER: Support ranges ("continue download") somehow ? $app->header("Accept-Ranges: none"); if ($expires > 0) { // If custom expiry now+expires is set use that $cache->content->expires(time() + $expires); } else { if ($expires == 0) { // expires set to 0 means disable cache, so we shall $cache->content->no_cache(); } } // TODO: Check metadata service for the real expiry timestamp ? $cache->content->cache_control_headers(); $send_att_body = true; if ($GLOBALS['midcom_config']['attachment_xsendfile_enable']) { $blob = new midgard_blob($attachment->__object); $att_local_path = $blob->get_path(); debug_add("Checking is_readable({$att_local_path})"); if (is_readable($att_local_path)) { $app->header("X-Sendfile: {$att_local_path}"); $send_att_body = false; } } // Store metadata in cache so _check_hit() can help us if (!$cache->content->_uncached && !$cache->content->_no_cache) { $cache->content->write_meta_cache('A-' . $etag, $etag); } while (@ob_end_flush()) { } if (!$send_att_body) { debug_add('NOT sending file (X-Sendfile will take care of that, _midcom_stop_request()ing so nothing has a chance the mess things up anymore'); _midcom_stop_request(); } fpassthru($f); $attachment->close(); debug_add("End of MidCOM run: {$_SERVER['REQUEST_URI']}"); _midcom_stop_request(); }