source: branches/eli_branch/lib/App.inc.php @ 446

Last change on this file since 446 was 446, checked in by anonymous, 11 years ago

Wrapper script for phpunit. Changed logMsg SCREEN action to print to stderr.

File size: 53.8 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
24 * App.inc.php
25 *
26 * Primary application framework class.
27 *
28 * @author  Quinn Comendant <quinn@strangecode.com>
29 * @version 2.1
30 */
31
32//ob_start();
33
34// Message Types.
35define('MSG_ERR', 1);
36define('MSG_ERROR', MSG_ERR);
37define('MSG_WARNING', 2);
38define('MSG_NOTICE', 4);
39define('MSG_SUCCESS', 8);
40define('MSG_ALL', MSG_SUCCESS | MSG_NOTICE | MSG_WARNING | MSG_ERROR);
41
42require_once dirname(__FILE__) . '/Utilities.inc.php';
43
44class App {
45
46    // Namespace of this application instance.
47    private $_ns;
48
49    // If $app->start has run successfully.
50    public $running = false;
51
52    // Instance of database object.
53    public $db;
54
55    // Array of query arguments will be carried persistently between requests.
56    private $_carry_queries = array();
57
58    // Dictionary of global application parameters.
59    private $_params = array();
60
61    // Default parameters.
62    private $_param_defaults = array(
63
64        // Public name and email address for this application.
65        'site_name' => null,
66        'site_email' => '', // Set to no-reply@HTTP_HOST if not set here.
67        'site_url' => '', // URL automatically determined by _SERVER['HTTP_HOST'] if not set here.
68        'images_path' => '', // Location for codebase-generated interface widgets (ex: "/admin/i").
69
70        // The location the user will go if the system doesn't know where else to send them.
71        'redirect_home_url' => '/',
72
73        // SSL URL used when redirecting with $app->sslOn().
74        'ssl_domain' => null,
75        'ssl_enabled' => false,
76
77        // Character set for page output. Used in the Content-Type header and the HTML <meta content-type> tag.
78        'character_set' => 'utf-8',
79
80        // Human-readable format used to display dates.
81        'date_format' => 'd M Y',
82        'time_format' => 'h:i:s A',
83        'sql_date_format' => '%e %b %Y',
84        'sql_time_format' => '%k:%i',
85
86        // Use php sessions?
87        'enable_session' => false,
88        'session_name' => '_session',
89        'session_use_cookies' => true,
90
91        // Pass the session-id through URLs if cookies are not enabled?
92        // Disable this to prevent session ID theft.
93        'session_use_trans_sid' => false,
94
95        // Use database?
96        'enable_db' => false,
97
98        // Use db-based sessions?
99        'enable_db_session_handler' => false,
100
101        // DB passwords should be set as apache environment variables in httpd.conf, readable only by root.
102        'db_server' => 'localhost',
103        'db_name' => null,
104        'db_user' => null,
105        'db_pass' => null,
106
107        // Database debugging.
108        'db_always_debug' => false, // TRUE = display all SQL queries.
109        'db_debug' => false, // TRUE = display db errors.
110        'db_die_on_failure' => false, // TRUE = script stops on db error.
111
112        // For classes that require db tables, do we check that a table exists and create if missing?
113        'db_create_tables' => true,
114
115        // The level of error reporting. Don't change this to suppress messages, instead use display_errors to control display.
116        'error_reporting' => E_ALL,
117
118        // Don't display errors by default; it is preferable to log them to a file.
119        'display_errors' => false,
120
121        // Directory in which to store log files.
122        'log_directory' => '',
123
124        // PHP error log.
125        'php_error_log' => 'php_error_log',
126
127        // General application log.
128        'log_filename' => 'app_log',
129
130        // Don't email or SMS duplicate messages that happen more often than this value (in seconds).
131        'log_multiple_timeout' => 3600, // Hourly
132
133        // Logging priority can be any of the following, or false to deactivate:
134        // LOG_EMERG     system is unusable
135        // LOG_ALERT     action must be taken immediately
136        // LOG_CRIT      critical conditions
137        // LOG_ERR       error conditions
138        // LOG_WARNING   warning conditions
139        // LOG_NOTICE    normal, but significant, condition
140        // LOG_INFO      informational message
141        // LOG_DEBUG     debug-level message
142        'log_file_priority' => LOG_INFO,
143        'log_email_priority' => false,
144        'log_sms_priority' => false,
145        'log_screen_priority' => false,
146
147        // Email address to receive log event emails. Use multiple addresses by separating them with commas.
148        'log_to_email_address' => null,
149
150        // SMS Email address to receive log event SMS messages. Use multiple addresses by separating them with commas.
151        'log_to_sms_address' => null,
152
153        // Should we avoid logging repeated logMsg() events? You might want to set this false if you need to see more accurate logging, particularly for long-running scripts.
154        'log_ignore_repeated_events' => true,
155
156        // Temporary files directory.
157        'tmp_dir' => '/tmp',
158
159        // A key for calculating simple cryptographic signatures. Set using as an environment variables in the httpd.conf with 'SetEnv SIGNING_KEY <key>'.
160        // Existing password hashes rely on the same key/salt being used to compare encryptions.
161        // Don't change this unless you know existing hashes or signatures will not be affected!
162        'signing_key' => 'aae6abd6209d82a691a9f96384a7634a',
163    );
164
165    /**
166     * This method enforces the singleton pattern for this class. Only one application is running at a time.
167     *
168     * $param   string  $namespace  Name of this application.
169     * @return  object  Reference to the global Cache object.
170     * @access  public
171     * @static
172     */
173    public static function &getInstance($namespace='')
174    {
175        static $instance = null;
176
177        if ($instance === null) {
178            $instance = new App($namespace);
179        }
180
181        return $instance;
182    }
183
184    /**
185     * Constructor.
186     */
187    public function __construct($namespace='')
188    {
189        // Set namespace of application instance.
190        $this->_ns = $namespace;
191
192        // Initialize default parameters.
193        $this->_params = array_merge($this->_params, $this->_param_defaults);
194
195        // Begin timing script.
196        require_once dirname(__FILE__) . '/ScriptTimer.inc.php';
197        $this->timer = new ScriptTimer();
198        $this->timer->start('_app');
199    }
200
201    /**
202     * Set (or overwrite existing) parameters by passing an array of new parameters.
203     *
204     * @access public
205     * @param  array    $param     Array of parameters (key => val pairs).
206     */
207    public function setParam($param=null)
208    {
209        if (isset($param) && is_array($param)) {
210            // Merge new parameters with old overriding only those passed.
211            $this->_params = array_merge($this->_params, $param);
212        }
213    }
214
215    /**
216     * Return the value of a parameter.
217     *
218     * @access  public
219     * @param   string  $param      The key of the parameter to return.
220     * @return  mixed               Parameter value, or null if not existing.
221     */
222    public function getParam($param=null)
223    {
224        if ($param === null) {
225            return $this->_params;
226        } else if (isset($this->_params[$param])) {
227            return $this->_params[$param];
228        } else {
229            trigger_error(sprintf('Parameter is not set: %s', $param), E_USER_NOTICE);
230            return null;
231        }
232    }
233
234    /**
235     * Begin running this application.
236     *
237     * @access  public
238     * @author  Quinn Comendant <quinn@strangecode.com>
239     * @since   15 Jul 2005 00:32:21
240     */
241    public function start()
242    {
243        if ($this->running) {
244            return false;
245        }
246
247        // Error reporting.
248        ini_set('error_reporting', $this->getParam('error_reporting'));
249        ini_set('display_errors', $this->getParam('display_errors'));
250        ini_set('log_errors', true);
251        if (is_dir($this->getParam('log_directory')) && is_writable($this->getParam('log_directory'))) {
252            ini_set('error_log', $this->getParam('log_directory') . '/' . $this->getParam('php_error_log'));
253        }
254
255        // Set character set to use for multi-byte string functions.
256        mb_internal_encoding($this->getParam('character_set'));
257        switch (mb_strtolower($this->getParam('character_set'))) {
258        case 'utf-8' :
259            mb_language('uni');
260            break;
261
262        case 'iso-2022-jp' :
263            mb_language('ja');
264            break;
265
266        case 'iso-8859-1' :
267        default :
268            mb_language('en');
269            break;
270        }
271
272        /**
273         * 1. Start Database.
274         */
275
276        if (true === $this->getParam('enable_db')) {
277
278            // DB connection parameters taken from environment variables in the httpd.conf file, readable only by root.
279            if (!empty($_SERVER['DB_SERVER'])) {
280                $this->setParam(array('db_server' => $_SERVER['DB_SERVER']));
281            }
282            if (!empty($_SERVER['DB_NAME'])) {
283                $this->setParam(array('db_name' => $_SERVER['DB_NAME']));
284            }
285            if (!empty($_SERVER['DB_USER'])) {
286                $this->setParam(array('db_user' => $_SERVER['DB_USER']));
287            }
288            if (!empty($_SERVER['DB_PASS'])) {
289                $this->setParam(array('db_pass' => $_SERVER['DB_PASS']));
290            }
291
292            // There will ever only be one instance of the DB object, and here is where it is instantiated.
293            require_once dirname(__FILE__) . '/DB.inc.php';
294            $this->db =& DB::getInstance();
295            $this->db->setParam(array(
296                'db_server' => $this->getParam('db_server'),
297                'db_name' => $this->getParam('db_name'),
298                'db_user' => $this->getParam('db_user'),
299                'db_pass' => $this->getParam('db_pass'),
300                'db_always_debug' => $this->getParam('db_always_debug'),
301                'db_debug' => $this->getParam('db_debug'),
302                'db_die_on_failure' => $this->getParam('db_die_on_failure'),
303            ));
304
305            // Connect to database.
306            $this->db->connect();
307        }
308
309
310        /**
311         * 2. Start PHP session.
312         */
313
314        // Skip session for some user agents.
315        if (preg_match('/Atomz|ApacheBench|Wget/i', getenv('HTTP_USER_AGENT'))) {
316            $this->setParam(array('enable_session' => false));
317        }
318
319        // Skip sessions if disabled or automatically skip if run in a CLI script.
320        if (true === $this->getParam('enable_session') && !defined('_CLI')) {
321
322            // Session parameters.
323            /*
324            ini_set('session.gc_probability', 1);
325            ini_set('session.gc_divisor', 1000);
326            ini_set('session.gc_maxlifetime', 43200); // 12 hours
327            ini_set('session.use_cookies', $this->getParam('session_use_cookies'));
328            ini_set('session.use_trans_sid', false);
329            ini_set('session.entropy_file', '/dev/urandom');
330            ini_set('session.entropy_length', '512');
331            ini_set('session.cookie_httponly', true);
332             * */
333            session_name($this->getParam('session_name'));
334
335            if (true === $this->getParam('enable_db_session_handler') && true === $this->getParam('enable_db')) {
336                // Database session handling.
337                require_once dirname(__FILE__) . '/DBSessionHandler.inc.php';
338                $db_save_handler = new DBSessionHandler($this->db, array(
339                    'db_table' => 'session_tbl',
340                    'create_table' => $this->getParam('db_create_tables'),
341                ));
342            }
343
344            // Start the session.
345            //session_start();
346
347            if (!isset($_SESSION['_app'][$this->_ns])) {
348                // Access session data using: $_SESSION['...'].
349                // Initialize here _after_ session has started.
350                $_SESSION['_app'][$this->_ns] = array(
351                    'messages' => array(),
352                    'boomerang' => array('url'),
353                );
354            }
355        }
356
357
358        /**
359         * 3. Misc setup.
360         */
361
362        // Script URI will be something like http://host.name.tld (no ending slash)
363        // and is used whenever a URL need be used to the current site.
364        // Not available on cli scripts obviously.
365        if (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST'] && '' == $this->getParam('site_url')) {
366            $this->setParam(array('site_url' => sprintf('%s://%s', ('on' == getenv('HTTPS') ? 'https' : 'http'), getenv('HTTP_HOST'))));
367        }
368
369        // In case site_email isn't set, use something halfway presentable.
370        if (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST'] && '' == $this->getParam('site_email')) {
371            $this->setParam(array('site_email' => sprintf('no-reply@%s', getenv('HTTP_HOST'))));
372        }
373
374        // A key for calculating simple cryptographic signatures.
375        if (isset($_SERVER['SIGNING_KEY'])) {
376            $this->setParam(array('signing_key' => $_SERVER['SIGNING_KEY']));
377        }
378
379        // Character set. This should also be printed in the html header template.
380        //header('Content-type: text/html; charset=' . $this->getParam('character_set'));
381
382        // Set the version of the codebase we're using.
383        $codebase_version_file = dirname(__FILE__) . '/../docs/version.txt';
384        if (is_readable($codebase_version_file)) {
385            $codebase_version = trim(file_get_contents($codebase_version_file));
386            $this->setParam(array('codebase_version' => $codebase_version));
387            //header('X-Codebase-Version: ' . $codebase_version);
388        }
389
390        $this->running = true;
391    }
392
393    /**
394     * Stop running this application.
395     *
396     * @access  public
397     * @author  Quinn Comendant <quinn@strangecode.com>
398     * @since   17 Jul 2005 17:20:18
399     */
400    public function stop()
401    {
402        session_write_close();
403        $this->running = false;
404        $num_queries = 0;
405        if (true === $this->getParam('enable_db')) {
406            $num_queries = $this->db->numQueries();
407            $this->db->close();
408        }
409        $this->timer->stop('_app');
410        $this->logMsg(sprintf('Script ended gracefully. Execution time: %s. Number of db queries: %s.', $this->timer->getTime('_app'), $num_queries), LOG_DEBUG, __FILE__, __LINE__);
411    }
412
413
414    /**
415     * Add a message to the session, which is printed in the header.
416     * Just a simple way to print messages to the user.
417     *
418     * @access public
419     *
420     * @param string $message The text description of the message.
421     * @param int    $type    The type of message: MSG_NOTICE,
422     *                        MSG_SUCCESS, MSG_WARNING, or MSG_ERR.
423     * @param string $file    __FILE__.
424     * @param string $line    __LINE__.
425     */
426    public function raiseMsg($message, $type=MSG_NOTICE, $file=null, $line=null)
427    {
428        $message = trim($message);
429
430        if (!$this->running) {
431            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
432            return false;
433        }
434
435        if ('' == trim($message)) {
436            $this->logMsg(sprintf('Raised message is an empty string.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
437            return false;
438        }
439
440        // Avoid duplicate full-stops..
441        $message = trim(preg_replace('/\.{2}$/', '.', $message));
442
443        // Save message in session under unique key to avoid duplicate messages.
444        $msg_id = md5($type . $message);
445        if (!isset($_SESSION['_app'][$this->_ns]['messages'][$msg_id])) {
446            $_SESSION['_app'][$this->_ns]['messages'][$msg_id] = array(
447                'type'    => $type,
448                'message' => $message,
449                'file'    => $file,
450                'line'    => $line,
451                'count'   => (isset($_SESSION['_app'][$this->_ns]['messages'][$msg_id]['count']) ? (1 + $_SESSION['_app'][$this->_ns]['messages'][$msg_id]['count']) : 1)
452            );
453        }
454
455        if (!in_array($type, array(MSG_NOTICE, MSG_SUCCESS, MSG_WARNING, MSG_ERR))) {
456            $this->logMsg(sprintf('Invalid MSG_* type: %s', $type), LOG_NOTICE, __FILE__, __LINE__);
457        }
458    }
459
460    /**
461     * Returns an array of the raised messages.
462     *
463     * @access  public
464     * @return  array   List of messages in FIFO order.
465     * @author  Quinn Comendant <quinn@strangecode.com>
466     * @since   21 Dec 2005 13:09:20
467     */
468    public function getRaisedMessages()
469    {
470        if (!$this->running) {
471            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
472            return false;
473        }
474        //die($_SESSION['_app'][$this->_ns]['messages']);
475        return isset($_SESSION['_app'][$this->_ns]['messages']) ? $_SESSION['_app'][$this->_ns]['messages'] : array();
476    }
477
478    /**
479     * Resets the message list.
480     *
481     * @access  public
482     * @author  Quinn Comendant <quinn@strangecode.com>
483     * @since   21 Dec 2005 13:21:54
484     */
485    public function clearRaisedMessages()
486    {
487        if (!$this->running) {
488            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
489            return false;
490        }
491
492        $_SESSION['_app'][$this->_ns]['messages'] = array();
493    }
494
495    /**
496     * Prints the HTML for displaying raised messages.
497     *
498     * @param   string  $above    Additional message to print above error messages (e.g. "Oops!").
499     * @param   string  $below    Additional message to print below error messages (e.g. "Please fix and resubmit").
500     * @param   string  $print_gotohash_js  Print a line of javascript that scrolls the browser window down to view any error messages.
501     * @param   string  $hash     The #hashtag to scroll to.
502     * @access  public
503     * @author  Quinn Comendant <quinn@strangecode.com>
504     * @since   15 Jul 2005 01:39:14
505     */
506    public function printRaisedMessages($above='', $below='', $print_gotohash_js=false, $hash='sc-msg')
507    {
508
509        if (!$this->running) {
510            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
511            return false;
512        }
513
514        $messages = $this->getRaisedMessages();
515        if (!empty($messages)) {
516            ?><div id="sc-msg" class="sc-msg"><?php
517            if ('' != $above) {
518                ?><div class="sc-above"><?php echo oTxt($above); ?></div><?php
519            }
520            foreach ($messages as $m) {
521                if (error_reporting() > 0 && $this->getParam('display_errors') && isset($m['file']) && isset($m['line'])) {
522                    echo "\n<!-- [" . $m['file'] . ' : ' . $m['line'] . '] -->';
523                }
524                switch ($m['type']) {
525                case MSG_ERR:
526                    echo '<div class="sc-msg-error">' . $m['message'] . '</div>';
527                    break;
528
529                case MSG_WARNING:
530                    echo '<div class="sc-msg-warning">' . $m['message'] . '</div>';
531                    break;
532
533                case MSG_SUCCESS:
534                    echo '<div class="sc-msg-success">' . $m['message'] . '</div>';
535                    break;
536
537                case MSG_NOTICE:
538                default:
539                    echo '<div class="sc-msg-notice">' . $m['message'] . '</div>';
540                    break;
541
542                }
543            }
544            if ('' != $below) {
545                ?><div class="sc-below"><?php echo oTxt($below); ?></div><?php
546            }
547            ?></div><?php
548            if ($print_gotohash_js) {
549                ?>
550                <script type="text/javascript">
551                /* <![CDATA[ */
552                window.location.hash = '#<?php echo urlencode($hash); ?>';
553                /* ]]> */
554                </script>
555                <?php
556            }
557        }
558        $this->clearRaisedMessages();
559    }
560
561    /**
562     * Logs messages to defined channels: file, email, sms, and screen. Repeated messages are
563     * not repeated but printed once with count. Log events that match a sendable channel (email or SMS)
564     * are sent once per 'log_multiple_timeout' setting (to avoid a flood of error emails).
565     *
566     * @access public
567     * @param string $message   The text description of the message.
568     * @param int    $priority  The type of message priority (in descending order):
569     *                          LOG_EMERG     0 system is unusable
570     *                          LOG_ALERT     1 action must be taken immediately
571     *                          LOG_CRIT      2 critical conditions
572     *                          LOG_ERR       3 error conditions
573     *                          LOG_WARNING   4 warning conditions
574     *                          LOG_NOTICE    5 normal, but significant, condition
575     *                          LOG_INFO      6 informational message
576     *                          LOG_DEBUG     7 debug-level message
577     * @param string $file      The file where the log event occurs.
578     * @param string $line      The line of the file where the log event occurs.
579     */
580    public function logMsg($message, $priority=LOG_INFO, $file=null, $line=null)
581    {
582        static $previous_events = array();
583
584        // If priority is not specified, assume the worst.
585        if (!$this->logPriorityToString($priority)) {
586            $this->logMsg(sprintf('Log priority %s not defined. (Message: %s)', $priority, $message), LOG_EMERG, $file, $line);
587            $priority = LOG_EMERG;
588        }
589
590        // If log file is not specified, don't log to a file.
591        if (!$this->getParam('log_directory') || !$this->getParam('log_filename') || !is_dir($this->getParam('log_directory')) || !is_writable($this->getParam('log_directory'))) {
592            $this->setParam(array('log_file_priority' => false));
593            // We must use trigger_error to report this problem rather than calling $app->logMsg, which might lead to an infinite loop.
594            trigger_error(sprintf('Codebase error: log directory (%s) not found or writable.', $this->getParam('log_directory')), E_USER_NOTICE);
595        }
596
597        // Before we get any further, let's see if ANY log events are configured to be reported.
598        if ((false === $this->getParam('log_file_priority') || $priority > $this->getParam('log_file_priority'))
599        && (false === $this->getParam('log_email_priority') || $priority > $this->getParam('log_email_priority'))
600        && (false === $this->getParam('log_sms_priority') || $priority > $this->getParam('log_sms_priority'))
601        && (false === $this->getParam('log_screen_priority') || $priority > $this->getParam('log_screen_priority'))) {
602            // This event would not be recorded, skip it entirely.
603            return false;
604        }
605
606        // Make sure to log in the system's locale.
607        $locale = setlocale(LC_TIME, 0);
608        setlocale(LC_TIME, 'C');
609
610        // Strip HTML tags except any with more than 7 characters because that's probably not a HTML tag, e.g. <email@address.com>.
611        preg_match_all('/(<[^>\s]{7,})[^>]*>/', $message, $strip_tags_allow);
612        $message = strip_tags(preg_replace('/\s+/', ' ', $message), (!empty($strip_tags_allow[1]) ? join('> ', $strip_tags_allow[1]) . '>' : null));
613
614        // Store this event under a unique key, counting each time it occurs so that it only gets reported a limited number of times.
615        $msg_id = md5($message . $priority . $file . $line);
616        if ($this->getParam('log_ignore_repeated_events') && isset($previous_events[$msg_id])) {
617            $previous_events[$msg_id]++;
618            if ($previous_events[$msg_id] == 2) {
619                $this->logMsg(sprintf('%s (Event repeated %s or more times)', $message, $previous_events[$msg_id]), $priority, $file, $line);
620            }
621            return false;
622        } else {
623            $previous_events[$msg_id] = 1;
624        }
625
626        // For email and SMS notification types use "lock" files to prevent sending email and SMS notices ad infinitum.
627        if ((false !== $this->getParam('log_email_priority') && $priority <= $this->getParam('log_email_priority'))
628        || (false !== $this->getParam('log_sms_priority') && $priority <= $this->getParam('log_sms_priority'))) {
629            // This event will generate a "send" notification. Prepare lock file.
630            $site_hash = md5(empty($_SERVER['SERVER_NAME']) ? $_SERVER['SCRIPT_FILENAME'] : $_SERVER['SERVER_NAME']);
631            $lock_dir = $this->getParam('tmp_dir') . "/codebase_msgs_$site_hash/";
632            // Just use the file and line for the msg_id to limit the number of possible messages
633            // (the message string itself shan't be used as it may contain innumerable combinations).
634            $lock_file = $lock_dir . md5($file . ':' . $line);
635            if (!is_dir($lock_dir)) {
636                mkdir($lock_dir);
637            }
638            $send_notifications = true;
639            if (is_file($lock_file)) {
640                $msg_last_sent = filectime($lock_file);
641                // Has this message been sent more recently than the timeout?
642                if ((time() - $msg_last_sent) <= $this->getParam('log_multiple_timeout')) {
643                    // This message was already sent recently.
644                    $send_notifications = false;
645                } else {
646                    // Timeout has expired; send notifications again and reset timeout.
647                    touch($lock_file);
648                }
649            } else {
650                touch($lock_file);
651            }
652        }
653
654        // Data to be stored for a log event.
655        $event = array(
656            'date'      => date('Y-m-d H:i:s'),
657            'remote ip' => getRemoteAddr(),
658            'pid'       => getmypid(),
659            'type'      => $this->logPriorityToString($priority),
660            'file:line' => "$file : $line",
661            'url'       => mb_substr(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '', 0, 128),
662            'message'   => $message
663        );
664
665        // FILE ACTION
666        if (false !== $this->getParam('log_file_priority') && $priority <= $this->getParam('log_file_priority')) {
667            $event_str = '[' . join('] [', $event) . ']';
668            error_log(mb_substr($event_str, 0, 1024) . "\n", 3, $this->getParam('log_directory') . '/' . $this->getParam('log_filename'));
669        }
670
671        // EMAIL ACTION
672        if (false !== $this->getParam('log_email_priority') && $priority <= $this->getParam('log_email_priority') && $send_notifications) {
673            $hostname = (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n');
674            $subject = sprintf('[%s %s] %s', $hostname, $event['type'], mb_substr($message, 0, 64));
675            $email_msg = sprintf("A %s log event occurred on %s\n\n", $event['type'], $hostname);
676            $headers = 'From: ' . $this->getParam('site_email');
677            foreach ($event as $k=>$v) {
678                $email_msg .= sprintf("%-11s%s\n", $k, $v);
679            }
680            mb_send_mail($this->getParam('log_to_email_address'), $subject, $email_msg, $headers);
681        }
682
683        // SMS ACTION
684        if (false !== $this->getParam('log_sms_priority') && $priority <= $this->getParam('log_sms_priority') && $send_notifications) {
685            $hostname = (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n');
686            $subject = sprintf('[%s %s]', $hostname, $priority);
687            $sms_msg = sprintf('%s [%s:%s]', mb_substr($event['message'], 0, 64), basename($file), $line);
688            $headers = 'From: ' . $this->getParam('site_email');
689            mb_send_mail($this->getParam('log_to_sms_address'), $subject, $sms_msg, $headers);
690        }
691
692        // SCREEN ACTION
693        if (false !== $this->getParam('log_screen_priority') && $priority <= $this->getParam('log_screen_priority')) {
694            file_put_contents('php://stderr', "[{$event['type']}] [{$event['message']}]\n", FILE_APPEND);
695        }
696
697        // Restore original locale.
698        setlocale(LC_TIME, $locale);
699
700        return true;
701    }
702
703    /**
704     * Returns the string representation of a LOG_* integer constant.
705     *
706     * @param int  $priority  The LOG_* integer constant.
707     *
708     * @return                The string representation of $priority.
709     */
710    public function logPriorityToString($priority) {
711        $priorities = array(
712            LOG_EMERG   => 'emergency',
713            LOG_ALERT   => 'alert',
714            LOG_CRIT    => 'critical',
715            LOG_ERR     => 'error',
716            LOG_WARNING => 'warning',
717            LOG_NOTICE  => 'notice',
718            LOG_INFO    => 'info',
719            LOG_DEBUG   => 'debug'
720        );
721        if (isset($priorities[$priority])) {
722            return $priorities[$priority];
723        } else {
724            return false;
725        }
726    }
727
728    /**
729     * Forcefully set a query argument even if one currently exists in the request.
730     * Values in the _carry_queries array will be copied to URLs (via $app->url()) and
731     * to hidden input values (via printHiddenSession()).
732     *
733     * @access  public
734     * @param   mixed   $query_key  The key (or keys, as an array) of the query argument to save.
735     * @param   mixed   $val        The new value of the argument key.
736     * @author  Quinn Comendant <quinn@strangecode.com>
737     * @since   13 Oct 2007 11:34:51
738     */
739    public function setQuery($query_key, $val)
740    {
741        if (!is_array($query_key)) {
742            $query_key = array($query_key);
743        }
744        foreach ($query_key as $k) {
745            // Set the value of the specified query argument into the _carry_queries array.
746            $this->_carry_queries[$k] = $val;
747        }
748    }
749
750    /**
751     * Specify which query arguments will be carried persistently between requests.
752     * Values in the _carry_queries array will be copied to URLs (via $app->url()) and
753     * to hidden input values (via printHiddenSession()).
754     *
755     * @access  public
756     * @param   mixed   $query_key   The key (or keys, as an array) of the query argument to save.
757     * @param   mixed   $default    If the key is not available, set to this default value.
758     * @author  Quinn Comendant <quinn@strangecode.com>
759     * @since   14 Nov 2005 19:24:52
760     */
761    public function carryQuery($query_key, $default=false)
762    {
763        if (!is_array($query_key)) {
764            $query_key = array($query_key);
765        }
766        foreach ($query_key as $k) {
767            // If not already set, and there is a non-empty value provided in the request...
768            if (!isset($this->_carry_queries[$k]) && false !== getFormData($k, $default)) {
769                // Copy the value of the specified query argument into the _carry_queries array.
770                $this->_carry_queries[$k] = getFormData($k, $default);
771                $this->logMsg(sprintf('Carrying query: %s => %s', $k, truncate(getDump($this->_carry_queries[$k], true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
772            }
773        }
774    }
775
776    /**
777     * dropQuery() is the opposite of carryQuery(). The specified value will not appear in
778     * url()/ohref()/printHiddenSession() modified URLs unless explicitly written in.
779     *
780     * @access  public
781     * @param   mixed   $query_key  The key (or keys, as an array) of the query argument to remove.
782     * @param   bool    $unset      Remove any values set in the request matching the given $query_key.
783     * @author  Quinn Comendant <quinn@strangecode.com>
784     * @since   18 Jun 2007 20:57:29
785     */
786    public function dropQuery($query_key, $unset=false)
787    {
788        if (!is_array($query_key)) {
789            $query_key = array($query_key);
790        }
791        foreach ($query_key as $k) {
792            if (isset($this->_carry_queries[$k])) {
793                // Remove the value of the specified query argument from the _carry_queries array.
794                $this->logMsg(sprintf('Dropping carried query: %s => %s', $k, $this->_carry_queries[$k]), LOG_DEBUG, __FILE__, __LINE__);
795                unset($this->_carry_queries[$k]);
796            }
797            if ($unset && isset($_REQUEST[$k])) {
798                unset($_REQUEST[$k], $_GET[$k], $_POST[$k], $_COOKIE[$k]);
799            }
800        }
801    }
802
803    /**
804     * Outputs a fully qualified URL with a query of all the used (ie: not empty)
805     * keys and values, including optional queries. This allows mindless retention
806     * of query arguments across page requests. If cookies are not
807     * used, the session id will be propagated in the URL.
808     *
809     * @param  string $url              The initial url
810     * @param  mixed  $carry_args       Additional url arguments to carry in the query,
811     *                                  or FALSE to prevent carrying queries. Can be any of the following formats:
812     *                                      array('key1', key2', key3')  <-- to save these keys if in the form data.
813     *                                      array('key1'=>'value', key2'='value')  <-- to set keys to default values if not present in form data.
814     *                                      false  <-- To not carry any queries. If URL already has queries those will be retained.
815     *
816     * @param  mixed  $always_include_sid  Always add the session id, even if using_trans_sid = true. This is required when
817     *                                     URL starts with http, since PHP using_trans_sid doesn't do those and also for
818     *                                     header('Location...') redirections.
819     *
820     * @return string url with attached queries and, if not using cookies, the session id
821     */
822    public function url($url, $carry_args=null, $always_include_sid=false)
823    {
824        if (!$this->running) {
825            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
826            return false;
827        }
828
829        // Get any provided query arguments to include in the final URL.
830        // If FALSE is a provided here, DO NOT carry the queries.
831        $do_carry_queries = true;
832        $one_time_carry_queries = array();
833        if (!is_null($carry_args)) {
834            if (is_array($carry_args) && !empty($carry_args)) {
835                foreach ($carry_args as $key=>$arg) {
836                    // Get query from appropriate source.
837                    if (false === $arg) {
838                        $do_carry_queries = false;
839                    } else if (false !== getFormData($arg, false)) {
840                        $one_time_carry_queries[$arg] = getFormData($arg); // Set arg to form data if available.
841                    } else if (!is_numeric($key) && '' != $arg) {
842                        $one_time_carry_queries[$key] = getFormData($key, $arg); // Set to arg to default if specified (overwritten by form data).
843                    }
844                }
845            } else if (false !== getFormData($carry_args, false)) {
846                $one_time_carry_queries[$carry_args] = getFormData($carry_args);
847            } else if (false === $carry_args) {
848                $do_carry_queries = false;
849            }
850        }
851
852        // Get the first delimiter that is needed in the url.
853        $delim = mb_strpos($url, '?') !== false ? ini_get('arg_separator.output') : '?';
854
855        $q = '';
856        if ($do_carry_queries) {
857            // Join the global _carry_queries and local one_time_carry_queries.
858            $query_args = urlEncodeArray(array_merge($this->_carry_queries, $one_time_carry_queries));
859            foreach ($query_args as $key=>$val) {
860                // Check value is set and value does not already exist in the url.
861                if (!preg_match('/[?&]' . preg_quote($key) . '=/', $url)) {
862                    $q .= $delim . $key . '=' . $val;
863                    $delim = ini_get('arg_separator.output');
864                }
865            }
866        }
867
868        // Include the necessary SID if the following is true:
869        // - no cookie in http request OR cookies disabled in App
870        // - sessions are enabled
871        // - the link stays on our site
872        // - transparent SID propagation with session.use_trans_sid is not being used OR url begins with protocol (using_trans_sid has no effect here)
873        // OR
874        // - we must include the SID because we say so (it's used in a context where cookies will not be effective, ie. moving from http to https)
875        // AND
876        // - the SID is not already in the query.
877        if (
878            (
879                (
880                    (
881                        !isset($_COOKIE[session_name()])
882                        || !$this->getParam('session_use_cookies')
883                    )
884                    && $this->getParam('session_use_trans_sid')
885                    && $this->getParam('enable_session')
886                    && isMyDomain($url)
887                    && (
888                        !ini_get('session.use_trans_sid')
889                        || preg_match('!^(http|https)://!i', $url)
890                    )
891                )
892                || $always_include_sid
893            )
894            && !preg_match('/[?&]' . preg_quote(session_name()) . '=/', $url)
895        ) {
896            $url .= $q . $delim . session_name() . '=' . session_id();
897            return $url;
898        } else {
899            $url .= $q;
900            return $url;
901        }
902    }
903
904    /**
905     * Returns a HTML-friendly URL processed with $app->url and & replaced with &amp;
906     *
907     * @access  public
908     * @param   string  $url    Input URL to parse.
909     * @return  string          URL passed through $app->url() and then & turned to $amp;.
910     * @author  Quinn Comendant <quinn@strangecode.com>
911     * @since   09 Dec 2005 17:58:45
912     */
913    public function oHREF($url, $carry_args=null, $always_include_sid=false)
914    {
915        $url = $this->url($url, $carry_args, $always_include_sid);
916
917        // Replace any & not followed by an html or unicode entity with it's &amp; equivalent.
918        $url = preg_replace('/&(?![\w\d#]{1,10};)/', '&amp;', $url);
919
920        return $url;
921    }
922
923    /**
924     * Prints a hidden form element with the PHPSESSID when cookies are not used, as well
925     * as hidden form elements for GET_VARS that might be in use.
926     *
927     * @param  mixed  $carry_args        Additional url arguments to carry in the query,
928     *                                   or FALSE to prevent carrying queries. Can be any of the following formats:
929     *                                      array('key1', key2', key3')  <-- to save these keys if in the form data.
930     *                                      array('key1'=>'value', key2'='value')  <-- to set keys to default values if not present in form data.
931     *                                      false  <-- To not carry any queries. If URL already has queries those will be retained.
932     */
933    public function printHiddenSession($carry_args=null)
934    {
935        if (!$this->running) {
936            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
937            return false;
938        }
939
940        // Get any provided query arguments to include in the final hidden form data.
941        // If FALSE is a provided here, DO NOT carry the queries.
942        $do_carry_queries = true;
943        $one_time_carry_queries = array();
944        if (!is_null($carry_args)) {
945            if (is_array($carry_args) && !empty($carry_args)) {
946                foreach ($carry_args as $key=>$arg) {
947                    // Get query from appropriate source.
948                    if (false === $arg) {
949                        $do_carry_queries = false;
950                    } else if (false !== getFormData($arg, false)) {
951                        $one_time_carry_queries[$arg] = getFormData($arg); // Set arg to form data if available.
952                    } else if (!is_numeric($key) && '' != $arg) {
953                        $one_time_carry_queries[$key] = getFormData($key, $arg); // Set to arg to default if specified (overwritten by form data).
954                    }
955                }
956            } else if (false !== getFormData($carry_args, false)) {
957                $one_time_carry_queries[$carry_args] = getFormData($carry_args);
958            } else if (false === $carry_args) {
959                $do_carry_queries = false;
960            }
961        }
962
963        // For each existing request value, we create a hidden input to carry it through a form.
964        if ($do_carry_queries) {
965            // Join the global _carry_queries and local one_time_carry_queries.
966            // urlencode is not used here, not for form data!
967            $query_args = array_merge($this->_carry_queries, $one_time_carry_queries);
968            foreach ($query_args as $key=>$val) {
969                printf('<input type="hidden" name="%s" value="%s" />', $key, $val);
970            }
971        }
972
973        // Include the SID if cookies are disabled.
974        if (!isset($_COOKIE[session_name()]) && !ini_get('session.use_trans_sid')) {
975            printf('<input type="hidden" name="%s" value="%s" />', session_name(), session_id());
976        }
977    }
978
979    /**
980     * Uses an http header to redirect the client to the given $url. If sessions are not used
981     * and the session is not already defined in the given $url, the SID is appended as a URI query.
982     * As with all header generating functions, make sure this is called before any other output.
983     *
984     * @param   string  $url                    The URL the client will be redirected to.
985     * @param   mixed   $carry_args             Additional url arguments to carry in the query,
986     *                                          or FALSE to prevent carrying queries. Can be any of the following formats:
987     *                                          -array('key1', key2', key3')  <-- to save these keys if in the form data.
988     *                                          -array('key1' => 'value', key2' => 'value')  <-- to set keys to default values if not present in form data.
989     *                                          -false  <-- To not carry any queries. If URL already has queries those will be retained.
990     * @param   bool    $always_include_sid     Force session id to be added to Location header.
991     */
992    public function dieURL($url, $carry_args=null, $always_include_sid=false)
993    {
994        if (!$this->running) {
995            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
996            return false;
997        }
998
999        if ('' == $url) {
1000            // If URL is not specified, use the redirect_home_url.
1001            $url = $this->getParam('redirect_home_url');
1002        }
1003
1004        if (preg_match('!^/!', $url)) {
1005            // If relative URL is given, prepend correct local hostname.
1006            $scheme = 'on' == getenv('HTTPS') ? 'https' : 'http';
1007            $host = getenv('HTTP_HOST');
1008            $url = sprintf('%s://%s%s', $scheme, $host, $url);
1009        }
1010
1011        $url = $this->url($url, $carry_args, $always_include_sid);
1012
1013        // Should we send a "303 See Other" header here instead of relying on the 302 sent automatically by PHP?
1014        header(sprintf('Location: %s', $url));
1015        $this->logMsg(sprintf('dieURL: %s', $url), LOG_DEBUG, __FILE__, __LINE__);
1016
1017        // End application.
1018        // Recommended, although I'm not sure it's necessary: http://cn2.php.net/session_write_close
1019        $this->stop();
1020        die;
1021    }
1022
1023    /*
1024    * Redirects a user by calling $app->dieURL(). It will use:
1025    * 1. the stored boomerang URL, it it exists
1026    * 2. a specified $default_url, it it exists
1027    * 3. the referring URL, it it exists.
1028    * 4. redirect_home_url configuration variable.
1029    *
1030    * @access   public
1031    * @param    string  $id             Identifier for this script.
1032    * @param    mixed   $carry_args     Additional arguments to carry in the URL automatically (see $app->oHREF()).
1033    * @param    string  $default_url    A default URL if there is not a valid specified boomerang URL.
1034    * @param    bool    $queryless_referrer_comparison   Exclude the URL query from the refererIsMe() comparison.
1035    * @return   bool                    False if the session is not running. No return otherwise.
1036    * @author   Quinn Comendant <quinn@strangecode.com>
1037    * @since    31 Mar 2006 19:17:00
1038    */
1039    public function dieBoomerangURL($id=null, $carry_args=null, $default_url=null, $queryless_referrer_comparison=false)
1040    {
1041        if (!$this->running) {
1042            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1043            return false;
1044        }
1045
1046        // Get URL from stored boomerang. Allow non specific URL if ID not valid.
1047        if ($this->validBoomerangURL($id, true)) {
1048            if (isset($id) && isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1049                $url = $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id];
1050                $this->logMsg(sprintf('dieBoomerangURL(%s) found: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1051            } else {
1052                $url = end($_SESSION['_app'][$this->_ns]['boomerang']['url']);
1053                $this->logMsg(sprintf('dieBoomerangURL(%s) using: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1054            }
1055            // Delete stored boomerang.
1056            $this->deleteBoomerangURL($id);
1057        } else if (isset($default_url)) {
1058            $url = $default_url;
1059        } else if (!refererIsMe(true === $queryless_referrer_comparison)) {
1060            // Ensure that the redirecting page is not also the referrer.
1061            $url = getenv('HTTP_REFERER');
1062            $this->logMsg(sprintf('dieBoomerangURL(%s) using referrer: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1063        } else {
1064            // If URL is not specified, use the redirect_home_url.
1065            $url = $this->getParam('redirect_home_url');
1066            $this->logMsg(sprintf('dieBoomerangURL(%s) using redirect_home_url: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1067        }
1068
1069        // A redirection will never happen immediately twice.
1070        // Set the time so ensure this doesn't happen.
1071        $_SESSION['_app'][$this->_ns]['boomerang']['time'] = time();
1072        $this->dieURL($url, $carry_args);
1073    }
1074
1075    /**
1076     * Set the URL to return to when $app->dieBoomerangURL() is called.
1077     *
1078     * @param string  $url  A fully validated URL.
1079     * @param bool  $id     An identification tag for this url.
1080     * FIXME: url garbage collection?
1081     */
1082    public function setBoomerangURL($url=null, $id=null)
1083    {
1084        if (!$this->running) {
1085            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1086            return false;
1087        }
1088        // A redirection will never happen immediately after setting the boomerangURL.
1089        // Set the time so ensure this doesn't happen. See $app->validBoomerangURL for more.
1090        /// FIXME: Why isn't the time set here under setBoomerangURL() and only under dieBoomerangURL()?
1091
1092        if ('' != $url && is_string($url)) {
1093            // Delete any boomerang request keys in the query string (along with any trailing delimiters after the deletion).
1094            $url = preg_replace(array('/([&?])boomerang=\w+[&?]?/', '/[&?]$/'), array('$1', ''), $url);
1095
1096            if (isset($_SESSION['_app'][$this->_ns]['boomerang']['url']) && is_array($_SESSION['_app'][$this->_ns]['boomerang']['url']) && !empty($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1097                // If the URL currently exists in the boomerang array, delete.
1098                while ($existing_key = array_search($url, $_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1099                    unset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$existing_key]);
1100                }
1101            }
1102
1103            if (isset($id)) {
1104                $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id] = $url;
1105            } else {
1106                $_SESSION['_app'][$this->_ns]['boomerang']['url'][] = $url;
1107            }
1108            $this->logMsg(sprintf('setBoomerangURL(%s): %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1109            return true;
1110        } else {
1111            $this->logMsg(sprintf('setBoomerangURL(%s) is empty!', $id, $url), LOG_NOTICE, __FILE__, __LINE__);
1112            return false;
1113        }
1114    }
1115
1116    /**
1117     * Return the URL set for the specified $id, or an empty string if one isn't set.
1118     *
1119     * @param string  $id     An identification tag for this url.
1120     */
1121    public function getBoomerangURL($id=null)
1122    {
1123        if (!$this->running) {
1124            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1125            return false;
1126        }
1127
1128        if (isset($id)) {
1129            if (isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1130                return $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id];
1131            } else {
1132                return '';
1133            }
1134        } else if (is_array($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1135            return end($_SESSION['_app'][$this->_ns]['boomerang']['url']);
1136        } else {
1137            return false;
1138        }
1139    }
1140
1141    /**
1142     * Delete the URL set for the specified $id.
1143     *
1144     * @param string  $id     An identification tag for this url.
1145     */
1146    public function deleteBoomerangURL($id=null)
1147    {
1148        if (!$this->running) {
1149            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1150            return false;
1151        }
1152
1153        $this->logMsg(sprintf('deleteBoomerangURL(%s): %s', $id, $this->getBoomerangURL($id)), LOG_DEBUG, __FILE__, __LINE__);
1154
1155        if (isset($id) && isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1156            unset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id]);
1157        } else if (is_array($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1158            array_pop($_SESSION['_app'][$this->_ns]['boomerang']['url']);
1159        }
1160    }
1161
1162    /**
1163     * Check if a valid boomerang URL value has been set. A boomerang URL is considered
1164     * valid if: 1) it is not empty, 2) it is not the current URL, and 3) has not been accessed within n seconds.
1165     *
1166     * @return bool  True if it is set and valid, false otherwise.
1167     */
1168    public function validBoomerangURL($id=null, $use_nonspecificboomerang=false)
1169    {
1170        if (!$this->running) {
1171            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1172            return false;
1173        }
1174
1175        if (!isset($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1176            $this->logMsg(sprintf('validBoomerangURL(%s) no boomerang URL set.', $id), LOG_DEBUG, __FILE__, __LINE__);
1177            return false;
1178        }
1179
1180        // Time is the time stamp of a boomerangURL redirection, or setting of a boomerangURL.
1181        // a boomerang redirection will always occur at least several seconds after the last boomerang redirect
1182        // or a boomerang being set.
1183        $boomerang_time = isset($_SESSION['_app'][$this->_ns]['boomerang']['time']) ? $_SESSION['_app'][$this->_ns]['boomerang']['time'] : 0;
1184
1185        $url = '';
1186        if (isset($id) && isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1187            $url = $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id];
1188        } else if (!isset($id) || $use_nonspecificboomerang) {
1189            // Use non specific boomerang if available.
1190            $url = end($_SESSION['_app'][$this->_ns]['boomerang']['url']);
1191        }
1192
1193        $this->logMsg(sprintf('validBoomerangURL(%s) testing: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1194
1195        if ('' == $url) {
1196            $this->logMsg(sprintf('validBoomerangURL(%s) not valid, empty!', $id), LOG_DEBUG, __FILE__, __LINE__);
1197            return false;
1198        }
1199        if ($url == absoluteMe()) {
1200            // The URL we are directing to is the current page.
1201            $this->logMsg(sprintf('validBoomerangURL(%s) not valid, same as absoluteMe: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1202            return false;
1203        }
1204        if ($boomerang_time >= (time() - 2)) {
1205            // Last boomerang direction was less than 2 seconds ago.
1206            $this->logMsg(sprintf('validBoomerangURL(%s) not valid, boomerang_time too short: %s seconds', $id, time() - $boomerang_time), LOG_DEBUG, __FILE__, __LINE__);
1207            return false;
1208        }
1209
1210        $this->logMsg(sprintf('validBoomerangURL(%s) is valid: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1211        return true;
1212    }
1213
1214    /**
1215     * Force the user to connect via https (port 443) by redirecting them to
1216     * the same page but with https.
1217     */
1218    public function sslOn()
1219    {
1220        if (function_exists('apache_get_modules')) {
1221            $modules = apache_get_modules();
1222        } else {
1223            // It's safe to assume we have mod_ssl if we can't determine otherwise.
1224            $modules = array('mod_ssl');
1225        }
1226
1227        if ('' == getenv('HTTPS') && $this->getParam('ssl_enabled') && in_array('mod_ssl', $modules)) {
1228            $this->raiseMsg(sprintf(_("Secure SSL connection made to %s"), $this->getParam('ssl_domain')), MSG_NOTICE, __FILE__, __LINE__);
1229            // Always append session because some browsers do not send cookie when crossing to SSL URL.
1230            $this->dieURL('https://' . $this->getParam('ssl_domain') . getenv('REQUEST_URI'), null, true);
1231        }
1232    }
1233
1234
1235    /**
1236     * to enforce the user to connect via http (port 80) by redirecting them to
1237     * a http version of the current url.
1238     */
1239    public function sslOff()
1240    {
1241        if ('' != getenv('HTTPS')) {
1242            $this->dieURL('http://' . getenv('HTTP_HOST') . getenv('REQUEST_URI'), null, true);
1243        }
1244    }
1245
1246
1247} // End.
Note: See TracBrowser for help on using the repository browser.