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

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

Changed some global constants to class constants (in cases where backwards compatability wouldn't break).

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