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
RevLine 
[1]1<?php
2/**
[362]3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
[396]5 * Copyright 2001-2012 Strangecode, LLC
[446]6 *
[362]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.
[446]13 *
[362]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.
[446]18 *
[362]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/**
[1]24 * App.inc.php
25 *
26 * Primary application framework class.
27 *
28 * @author  Quinn Comendant <quinn@strangecode.com>
[136]29 * @version 2.1
[1]30 */
[42]31
[37]32// Message Types.
33define('MSG_ERR', 1);
34define('MSG_ERROR', MSG_ERR);
[1]35define('MSG_WARNING', 2);
[37]36define('MSG_NOTICE', 4);
37define('MSG_SUCCESS', 8);
[119]38define('MSG_ALL', MSG_SUCCESS | MSG_NOTICE | MSG_WARNING | MSG_ERROR);
[1]39
40require_once dirname(__FILE__) . '/Utilities.inc.php';
41
42class App {
[42]43
[136]44    // Namespace of this application instance.
[439]45    private $_ns;
[1]46
[136]47    // If $app->start has run successfully.
[439]48    public $running = false;
[1]49
50    // Instance of database object.
[439]51    public $db;
[42]52
[20]53    // Array of query arguments will be carried persistently between requests.
[439]54    private $_carry_queries = array();
[1]55
[136]56    // Dictionary of global application parameters.
[439]57    private $_params = array();
[1]58
59    // Default parameters.
[439]60    private $_param_defaults = array(
[1]61
62        // Public name and email address for this application.
63        'site_name' => null,
[390]64        'site_email' => '', // Set to no-reply@HTTP_HOST if not set here.
[39]65        'site_url' => '', // URL automatically determined by _SERVER['HTTP_HOST'] if not set here.
[318]66        'images_path' => '', // Location for codebase-generated interface widgets (ex: "/admin/i").
[1]67
[136]68        // The location the user will go if the system doesn't know where else to send them.
[1]69        'redirect_home_url' => '/',
[42]70
[136]71        // SSL URL used when redirecting with $app->sslOn().
[1]72        'ssl_domain' => null,
73        'ssl_enabled' => false,
[42]74
[20]75        // Character set for page output. Used in the Content-Type header and the HTML <meta content-type> tag.
[1]76        'character_set' => 'utf-8',
77
78        // Human-readable format used to display dates.
79        'date_format' => 'd M Y',
[101]80        'time_format' => 'h:i:s A',
[1]81        'sql_date_format' => '%e %b %Y',
82        'sql_time_format' => '%k:%i',
83
84        // Use php sessions?
85        'enable_session' => false,
[242]86        'session_name' => '_session',
[1]87        'session_use_cookies' => true,
[446]88
[293]89        // Pass the session-id through URLs if cookies are not enabled?
90        // Disable this to prevent session ID theft.
[242]91        'session_use_trans_sid' => false,
[42]92
[1]93        // Use database?
94        'enable_db' => false,
95
96        // Use db-based sessions?
97        'enable_db_session_handler' => false,
[42]98
[1]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.
[42]109
[1]110        // For classes that require db tables, do we check that a table exists and create if missing?
[32]111        'db_create_tables' => true,
[1]112
[136]113        // The level of error reporting. Don't change this to suppress messages, instead use display_errors to control display.
[1]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,
[42]118
[1]119        // Directory in which to store log files.
[19]120        'log_directory' => '',
[1]121
122        // PHP error log.
123        'php_error_log' => 'php_error_log',
124
125        // General application log.
[136]126        'log_filename' => 'app_log',
[1]127
[390]128        // Don't email or SMS duplicate messages that happen more often than this value (in seconds).
129        'log_multiple_timeout' => 3600, // Hourly
[341]130
[1]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
[174]140        'log_file_priority' => LOG_INFO,
[342]141        'log_email_priority' => false,
[1]142        'log_sms_priority' => false,
143        'log_screen_priority' => false,
[42]144
[390]145        // Email address to receive log event emails. Use multiple addresses by separating them with commas.
[342]146        'log_to_email_address' => null,
[42]147
[392]148        // SMS Email address to receive log event SMS messages. Use multiple addresses by separating them with commas.
[1]149        'log_to_sms_address' => null,
[42]150
[406]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
[348]154        // Temporary files directory.
155        'tmp_dir' => '/tmp',
[343]156
[19]157        // A key for calculating simple cryptographic signatures. Set using as an environment variables in the httpd.conf with 'SetEnv SIGNING_KEY <key>'.
[136]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!
[1]160        'signing_key' => 'aae6abd6209d82a691a9f96384a7634a',
161    );
[42]162
[1]163    /**
164     * This method enforces the singleton pattern for this class. Only one application is running at a time.
165     *
[136]166     * $param   string  $namespace  Name of this application.
167     * @return  object  Reference to the global Cache object.
[1]168     * @access  public
169     * @static
170     */
[439]171    public static function &getInstance($namespace='')
[1]172    {
173        static $instance = null;
174
175        if ($instance === null) {
[136]176            $instance = new App($namespace);
[1]177        }
178
179        return $instance;
180    }
[42]181
[1]182    /**
183     * Constructor.
184     */
[439]185    public function __construct($namespace='')
[1]186    {
[136]187        // Set namespace of application instance.
[154]188        $this->_ns = $namespace;
[42]189
[1]190        // Initialize default parameters.
191        $this->_params = array_merge($this->_params, $this->_param_defaults);
[446]192
[172]193        // Begin timing script.
194        require_once dirname(__FILE__) . '/ScriptTimer.inc.php';
195        $this->timer = new ScriptTimer();
196        $this->timer->start('_app');
[1]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     */
[439]205    public function setParam($param=null)
[1]206    {
207        if (isset($param) && is_array($param)) {
208            // Merge new parameters with old overriding only those passed.
[136]209            $this->_params = array_merge($this->_params, $param);
[1]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     */
[439]220    public function getParam($param=null)
[1]221    {
222        if ($param === null) {
[136]223            return $this->_params;
224        } else if (isset($this->_params[$param])) {
225            return $this->_params[$param];
[1]226        } else {
[19]227            trigger_error(sprintf('Parameter is not set: %s', $param), E_USER_NOTICE);
[1]228            return null;
229        }
230    }
[42]231
[1]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     */
[439]239    public function start()
[1]240    {
241        if ($this->running) {
242            return false;
243        }
[42]244
[1]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        }
[446]252
[248]253        // Set character set to use for multi-byte string functions.
254        mb_internal_encoding($this->getParam('character_set'));
[249]255        switch (mb_strtolower($this->getParam('character_set'))) {
256        case 'utf-8' :
257            mb_language('uni');
258            break;
[42]259
[249]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
[1]270        /**
271         * 1. Start Database.
272         */
[42]273
[103]274        if (true === $this->getParam('enable_db')) {
[446]275
[1]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            }
[42]289
[136]290            // There will ever only be one instance of the DB object, and here is where it is instantiated.
[1]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        }
[42]306
307
[1]308        /**
309         * 2. Start PHP session.
310         */
[42]311
[1]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        }
[42]316
[433]317        // Skip sessions if disabled or automatically skip if run in a CLI script.
318        if (true === $this->getParam('enable_session') && !defined('_CLI')) {
[42]319
[373]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');
[410]328            ini_set('session.cookie_httponly', true);
[373]329            session_name($this->getParam('session_name'));
330
[1]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            }
[42]339
[22]340            // Start the session.
[447]341            session_start();
[42]342
[154]343            if (!isset($_SESSION['_app'][$this->_ns])) {
[22]344                // Access session data using: $_SESSION['...'].
345                // Initialize here _after_ session has started.
[154]346                $_SESSION['_app'][$this->_ns] = array(
[22]347                    'messages' => array(),
348                    'boomerang' => array('url'),
349                );
350            }
[1]351        }
[42]352
353
[1]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.
[41]361        if (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST'] && '' == $this->getParam('site_url')) {
[14]362            $this->setParam(array('site_url' => sprintf('%s://%s', ('on' == getenv('HTTPS') ? 'https' : 'http'), getenv('HTTP_HOST'))));
[1]363        }
[446]364
[390]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        }
[1]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        }
[42]374
[1]375        // Character set. This should also be printed in the html header template.
[447]376        header('Content-type: text/html; charset=' . $this->getParam('character_set'));
[446]377
[136]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));
[144]382            $this->setParam(array('codebase_version' => $codebase_version));
[447]383            header('X-Codebase-Version: ' . $codebase_version);
[136]384        }
[42]385
[1]386        $this->running = true;
387    }
[42]388
[1]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     */
[439]396    public function stop()
[1]397    {
398        session_write_close();
399        $this->running = false;
[172]400        $num_queries = 0;
[103]401        if (true === $this->getParam('enable_db')) {
[172]402            $num_queries = $this->db->numQueries();
[103]403            $this->db->close();
404        }
[172]405        $this->timer->stop('_app');
[202]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__);
[1]407    }
[42]408
409
[1]410    /**
[84]411     * Add a message to the session, which is printed in the header.
[1]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     */
[439]422    public function raiseMsg($message, $type=MSG_NOTICE, $file=null, $line=null)
[1]423    {
[32]424        $message = trim($message);
[1]425
[203]426        if (!$this->running) {
427            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]428            return false;
429        }
[42]430
[203]431        if ('' == trim($message)) {
432            $this->logMsg(sprintf('Raised message is an empty string.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
433            return false;
434        }
[446]435
[406]436        // Avoid duplicate full-stops..
437        $message = trim(preg_replace('/\.{2}$/', '.', $message));
[203]438
[37]439        // Save message in session under unique key to avoid duplicate messages.
[177]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        }
[446]450
[1]451        if (!in_array($type, array(MSG_NOTICE, MSG_SUCCESS, MSG_WARNING, MSG_ERR))) {
[136]452            $this->logMsg(sprintf('Invalid MSG_* type: %s', $type), LOG_NOTICE, __FILE__, __LINE__);
[1]453        }
454    }
[446]455
[46]456    /**
457     * Returns an array of the raised messages.
458     *
459     * @access  public
[334]460     * @return  array   List of messages in FIFO order.
[46]461     * @author  Quinn Comendant <quinn@strangecode.com>
462     * @since   21 Dec 2005 13:09:20
463     */
[439]464    public function getRaisedMessages()
[46]465    {
[136]466        if (!$this->running) {
467            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
468            return false;
[46]469        }
[154]470        return isset($_SESSION['_app'][$this->_ns]['messages']) ? $_SESSION['_app'][$this->_ns]['messages'] : array();
[46]471    }
[446]472
[1]473    /**
[46]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     */
[439]480    public function clearRaisedMessages()
[46]481    {
[136]482        if (!$this->running) {
483            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[46]484            return false;
485        }
[446]486
[154]487        $_SESSION['_app'][$this->_ns]['messages'] = array();
[46]488    }
489
490    /**
[1]491     * Prints the HTML for displaying raised messages.
492     *
[413]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.
[1]497     * @access  public
498     * @author  Quinn Comendant <quinn@strangecode.com>
499     * @since   15 Jul 2005 01:39:14
500     */
[439]501    public function printRaisedMessages($above='', $below='', $print_gotohash_js=false, $hash='sc-msg')
[1]502    {
[446]503
[136]504        if (!$this->running) {
505            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]506            return false;
507        }
[446]508
[136]509        $messages = $this->getRaisedMessages();
[167]510        if (!empty($messages)) {
[163]511            ?><div id="sc-msg" class="sc-msg"><?php
[413]512            if ('' != $above) {
513                ?><div class="sc-above"><?php echo oTxt($above); ?></div><?php
514            }
[167]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;
[42]523
[167]524                case MSG_WARNING:
525                    echo '<div class="sc-msg-warning">' . $m['message'] . '</div>';
526                    break;
[42]527
[167]528                case MSG_SUCCESS:
529                    echo '<div class="sc-msg-success">' . $m['message'] . '</div>';
530                    break;
[42]531
[167]532                case MSG_NOTICE:
533                default:
534                    echo '<div class="sc-msg-notice">' . $m['message'] . '</div>';
535                    break;
[42]536
[167]537                }
[1]538            }
[413]539            if ('' != $below) {
540                ?><div class="sc-below"><?php echo oTxt($below); ?></div><?php
541            }
[1]542            ?></div><?php
[413]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            }
[1]552        }
[136]553        $this->clearRaisedMessages();
[1]554    }
[42]555
[1]556    /**
[44]557     * Logs messages to defined channels: file, email, sms, and screen. Repeated messages are
[390]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).
[1]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):
[390]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
[1]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     */
[439]575    public function logMsg($message, $priority=LOG_INFO, $file=null, $line=null)
[1]576    {
[44]577        static $previous_events = array();
578
[1]579        // If priority is not specified, assume the worst.
[136]580        if (!$this->logPriorityToString($priority)) {
581            $this->logMsg(sprintf('Log priority %s not defined. (Message: %s)', $priority, $message), LOG_EMERG, $file, $line);
[1]582            $priority = LOG_EMERG;
583        }
[42]584
[15]585        // If log file is not specified, don't log to a file.
[136]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);
[1]590        }
[446]591
[390]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        }
[42]600
[1]601        // Make sure to log in the system's locale.
602        $locale = setlocale(LC_TIME, 0);
603        setlocale(LC_TIME, 'C');
[42]604
[44]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);
[406]611        if ($this->getParam('log_ignore_repeated_events') && isset($previous_events[$msg_id])) {
[44]612            $previous_events[$msg_id]++;
613            if ($previous_events[$msg_id] == 2) {
[136]614                $this->logMsg(sprintf('%s (Event repeated %s or more times)', $message, $previous_events[$msg_id]), $priority, $file, $line);
[44]615            }
616            return false;
617        } else {
618            $previous_events[$msg_id] = 1;
619        }
[341]620
[390]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/";
[446]627            // Just use the file and line for the msg_id to limit the number of possible messages
[390]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                }
[341]644            } else {
[390]645                touch($lock_file);
[341]646            }
647        }
[446]648
[1]649        // Data to be stored for a log event.
[44]650        $event = array(
651            'date'      => date('Y-m-d H:i:s'),
652            'remote ip' => getRemoteAddr(),
[414]653            'pid'       => getmypid(),
[136]654            'type'      => $this->logPriorityToString($priority),
[44]655            'file:line' => "$file : $line",
[247]656            'url'       => mb_substr(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '', 0, 128),
[44]657            'message'   => $message
658        );
[42]659
[1]660        // FILE ACTION
[390]661        if (false !== $this->getParam('log_file_priority') && $priority <= $this->getParam('log_file_priority')) {
[44]662            $event_str = '[' . join('] [', $event) . ']';
[247]663            error_log(mb_substr($event_str, 0, 1024) . "\n", 3, $this->getParam('log_directory') . '/' . $this->getParam('log_filename'));
[1]664        }
[42]665
[390]666        // EMAIL ACTION
667        if (false !== $this->getParam('log_email_priority') && $priority <= $this->getParam('log_email_priority') && $send_notifications) {
[422]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);
[390]671            $headers = 'From: ' . $this->getParam('site_email');
672            foreach ($event as $k=>$v) {
673                $email_msg .= sprintf("%-11s%s\n", $k, $v);
[1]674            }
[390]675            mb_send_mail($this->getParam('log_to_email_address'), $subject, $email_msg, $headers);
[1]676        }
[390]677
678        // SMS ACTION
679        if (false !== $this->getParam('log_sms_priority') && $priority <= $this->getParam('log_sms_priority') && $send_notifications) {
[422]680            $hostname = (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n');
681            $subject = sprintf('[%s %s]', $hostname, $priority);
[390]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        }
[446]686
[1]687        // SCREEN ACTION
[390]688        if (false !== $this->getParam('log_screen_priority') && $priority <= $this->getParam('log_screen_priority')) {
[446]689            file_put_contents('php://stderr', "[{$event['type']}] [{$event['message']}]\n", FILE_APPEND);
[1]690        }
[42]691
[1]692        // Restore original locale.
693        setlocale(LC_TIME, $locale);
[446]694
[390]695        return true;
[1]696    }
[42]697
[1]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     */
[439]705    public function logPriorityToString($priority) {
[1]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    }
[42]722
[1]723    /**
[334]724     * Forcefully set a query argument even if one currently exists in the request.
[136]725     * Values in the _carry_queries array will be copied to URLs (via $app->url()) and
[20]726     * to hidden input values (via printHiddenSession()).
727     *
728     * @access  public
[282]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     */
[439]734    public function setQuery($query_key, $val)
[282]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
[259]751     * @param   mixed   $query_key   The key (or keys, as an array) of the query argument to save.
[170]752     * @param   mixed   $default    If the key is not available, set to this default value.
[20]753     * @author  Quinn Comendant <quinn@strangecode.com>
754     * @since   14 Nov 2005 19:24:52
755     */
[439]756    public function carryQuery($query_key, $default=false)
[20]757    {
[259]758        if (!is_array($query_key)) {
759            $query_key = array($query_key);
[20]760        }
[259]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);
[331]766                $this->logMsg(sprintf('Carrying query: %s => %s', $k, truncate(getDump($this->_carry_queries[$k], true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
[259]767            }
768        }
[20]769    }
[42]770
[20]771    /**
[446]772     * dropQuery() is the opposite of carryQuery(). The specified value will not appear in
[259]773     * url()/ohref()/printHiddenSession() modified URLs unless explicitly written in.
[446]774     *
[259]775     * @access  public
776     * @param   mixed   $query_key  The key (or keys, as an array) of the query argument to remove.
[407]777     * @param   bool    $unset      Remove any values set in the request matching the given $query_key.
[259]778     * @author  Quinn Comendant <quinn@strangecode.com>
779     * @since   18 Jun 2007 20:57:29
780     */
[439]781    public function dropQuery($query_key, $unset=false)
[259]782    {
783        if (!is_array($query_key)) {
784            $query_key = array($query_key);
785        }
786        foreach ($query_key as $k) {
[260]787            if (isset($this->_carry_queries[$k])) {
[259]788                // Remove the value of the specified query argument from the _carry_queries array.
[325]789                $this->logMsg(sprintf('Dropping carried query: %s => %s', $k, $this->_carry_queries[$k]), LOG_DEBUG, __FILE__, __LINE__);
[260]790                unset($this->_carry_queries[$k]);
[259]791            }
[325]792            if ($unset && isset($_REQUEST[$k])) {
793                unset($_REQUEST[$k], $_GET[$k], $_POST[$k], $_COOKIE[$k]);
794            }
[259]795        }
796    }
797
798    /**
[1]799     * Outputs a fully qualified URL with a query of all the used (ie: not empty)
[42]800     * keys and values, including optional queries. This allows mindless retention
[32]801     * of query arguments across page requests. If cookies are not
[325]802     * used, the session id will be propagated in the URL.
[1]803     *
[32]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.
[1]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     */
[439]817    public function url($url, $carry_args=null, $always_include_sid=false)
[1]818    {
[136]819        if (!$this->running) {
820            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]821            return false;
822        }
[42]823
[20]824        // Get any provided query arguments to include in the final URL.
825        // If FALSE is a provided here, DO NOT carry the queries.
[1]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        }
[42]846
[1]847        // Get the first delimiter that is needed in the url.
[247]848        $delim = mb_strpos($url, '?') !== false ? ini_get('arg_separator.output') : '?';
[32]849
[1]850        $q = '';
851        if ($do_carry_queries) {
[20]852            // Join the global _carry_queries and local one_time_carry_queries.
[136]853            $query_args = urlEncodeArray(array_merge($this->_carry_queries, $one_time_carry_queries));
[1]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        }
[42]862
[1]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
[334]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)
[42]868        // OR
[1]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                    (
[42]876                        !isset($_COOKIE[session_name()])
[136]877                        || !$this->getParam('session_use_cookies')
[42]878                    )
[242]879                    && $this->getParam('session_use_trans_sid')
[136]880                    && $this->getParam('enable_session')
[42]881                    && isMyDomain($url)
[242]882                    && (
[20]883                        !ini_get('session.use_trans_sid')
[1]884                        || preg_match('!^(http|https)://!i', $url)
885                    )
[42]886                )
[1]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    }
[32]898
899    /**
[136]900     * Returns a HTML-friendly URL processed with $app->url and & replaced with &amp;
[32]901     *
902     * @access  public
903     * @param   string  $url    Input URL to parse.
[334]904     * @return  string          URL passed through $app->url() and then & turned to $amp;.
[32]905     * @author  Quinn Comendant <quinn@strangecode.com>
906     * @since   09 Dec 2005 17:58:45
907     */
[439]908    public function oHREF($url, $carry_args=null, $always_include_sid=false)
[32]909    {
[136]910        $url = $this->url($url, $carry_args, $always_include_sid);
[42]911
[32]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);
[42]914
[32]915        return $url;
916    }
[42]917
[1]918    /**
919     * Prints a hidden form element with the PHPSESSID when cookies are not used, as well
[42]920     * as hidden form elements for GET_VARS that might be in use.
[1]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:
[32]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.
[1]927     */
[439]928    public function printHiddenSession($carry_args=null)
[32]929    {
[136]930        if (!$this->running) {
931            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]932            return false;
933        }
[42]934
[20]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.
[1]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        }
[42]957
[313]958        // For each existing request value, we create a hidden input to carry it through a form.
[1]959        if ($do_carry_queries) {
[20]960            // Join the global _carry_queries and local one_time_carry_queries.
961            // urlencode is not used here, not for form data!
[136]962            $query_args = array_merge($this->_carry_queries, $one_time_carry_queries);
[1]963            foreach ($query_args as $key=>$val) {
[136]964                printf('<input type="hidden" name="%s" value="%s" />', $key, $val);
[1]965            }
966        }
[42]967
[1]968        // Include the SID if cookies are disabled.
[20]969        if (!isset($_COOKIE[session_name()]) && !ini_get('session.use_trans_sid')) {
[136]970            printf('<input type="hidden" name="%s" value="%s" />', session_name(), session_id());
[1]971        }
972    }
[42]973
[1]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.
[136]983     *                                          -array('key1' => 'value', key2' => 'value')  <-- to set keys to default values if not present in form data.
[1]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     */
[439]987    public function dieURL($url, $carry_args=null, $always_include_sid=false)
[1]988    {
[136]989        if (!$this->running) {
990            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]991            return false;
992        }
[42]993
[1]994        if ('' == $url) {
995            // If URL is not specified, use the redirect_home_url.
[136]996            $url = $this->getParam('redirect_home_url');
[1]997        }
[42]998
[1]999        if (preg_match('!^/!', $url)) {
1000            // If relative URL is given, prepend correct local hostname.
[22]1001            $scheme = 'on' == getenv('HTTPS') ? 'https' : 'http';
1002            $host = getenv('HTTP_HOST');
1003            $url = sprintf('%s://%s%s', $scheme, $host, $url);
[1]1004        }
[22]1005
[136]1006        $url = $this->url($url, $carry_args, $always_include_sid);
[42]1007
[202]1008        // Should we send a "303 See Other" header here instead of relying on the 302 sent automatically by PHP?
[1]1009        header(sprintf('Location: %s', $url));
[136]1010        $this->logMsg(sprintf('dieURL: %s', $url), LOG_DEBUG, __FILE__, __LINE__);
[42]1011
[202]1012        // End application.
[1]1013        // Recommended, although I'm not sure it's necessary: http://cn2.php.net/session_write_close
[136]1014        $this->stop();
[1]1015        die;
1016    }
[42]1017
[84]1018    /*
[136]1019    * Redirects a user by calling $app->dieURL(). It will use:
[84]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.
[136]1027    * @param    mixed   $carry_args     Additional arguments to carry in the URL automatically (see $app->oHREF()).
[84]1028    * @param    string  $default_url    A default URL if there is not a valid specified boomerang URL.
[159]1029    * @param    bool    $queryless_referrer_comparison   Exclude the URL query from the refererIsMe() comparison.
[84]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    */
[439]1034    public function dieBoomerangURL($id=null, $carry_args=null, $default_url=null, $queryless_referrer_comparison=false)
[1]1035    {
[136]1036        if (!$this->running) {
1037            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]1038            return false;
1039        }
[42]1040
[1]1041        // Get URL from stored boomerang. Allow non specific URL if ID not valid.
[136]1042        if ($this->validBoomerangURL($id, true)) {
[154]1043            if (isset($id) && isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1044                $url = $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id];
[136]1045                $this->logMsg(sprintf('dieBoomerangURL(%s) found: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
[1]1046            } else {
[154]1047                $url = end($_SESSION['_app'][$this->_ns]['boomerang']['url']);
[136]1048                $this->logMsg(sprintf('dieBoomerangURL(%s) using: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
[1]1049            }
[22]1050            // Delete stored boomerang.
[136]1051            $this->deleteBoomerangURL($id);
[84]1052        } else if (isset($default_url)) {
1053            $url = $default_url;
[159]1054        } else if (!refererIsMe(true === $queryless_referrer_comparison)) {
[1]1055            // Ensure that the redirecting page is not also the referrer.
1056            $url = getenv('HTTP_REFERER');
[136]1057            $this->logMsg(sprintf('dieBoomerangURL(%s) using referrer: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
[1]1058        } else {
[22]1059            // If URL is not specified, use the redirect_home_url.
[136]1060            $url = $this->getParam('redirect_home_url');
[203]1061            $this->logMsg(sprintf('dieBoomerangURL(%s) using redirect_home_url: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
[1]1062        }
[42]1063
[84]1064        // A redirection will never happen immediately twice.
[1]1065        // Set the time so ensure this doesn't happen.
[154]1066        $_SESSION['_app'][$this->_ns]['boomerang']['time'] = time();
[136]1067        $this->dieURL($url, $carry_args);
[1]1068    }
[42]1069
[1]1070    /**
[136]1071     * Set the URL to return to when $app->dieBoomerangURL() is called.
[1]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     */
[439]1077    public function setBoomerangURL($url=null, $id=null)
[1]1078    {
[136]1079        if (!$this->running) {
1080            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]1081            return false;
1082        }
[84]1083        // A redirection will never happen immediately after setting the boomerangURL.
[136]1084        // Set the time so ensure this doesn't happen. See $app->validBoomerangURL for more.
[359]1085        /// FIXME: Why isn't the time set here under setBoomerangURL() and only under dieBoomerangURL()?
[42]1086
[22]1087        if ('' != $url && is_string($url)) {
[242]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);
[42]1090
[154]1091            if (isset($_SESSION['_app'][$this->_ns]['boomerang']['url']) && is_array($_SESSION['_app'][$this->_ns]['boomerang']['url']) && !empty($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
[1]1092                // If the URL currently exists in the boomerang array, delete.
[154]1093                while ($existing_key = array_search($url, $_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1094                    unset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$existing_key]);
[1]1095                }
1096            }
[42]1097
[1]1098            if (isset($id)) {
[154]1099                $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id] = $url;
[1]1100            } else {
[154]1101                $_SESSION['_app'][$this->_ns]['boomerang']['url'][] = $url;
[1]1102            }
[136]1103            $this->logMsg(sprintf('setBoomerangURL(%s): %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
[1]1104            return true;
1105        } else {
[136]1106            $this->logMsg(sprintf('setBoomerangURL(%s) is empty!', $id, $url), LOG_NOTICE, __FILE__, __LINE__);
[1]1107            return false;
1108        }
1109    }
[42]1110
[1]1111    /**
[333]1112     * Return the URL set for the specified $id, or an empty string if one isn't set.
[1]1113     *
1114     * @param string  $id     An identification tag for this url.
1115     */
[439]1116    public function getBoomerangURL($id=null)
[1]1117    {
[136]1118        if (!$this->running) {
1119            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]1120            return false;
1121        }
[42]1122
[1]1123        if (isset($id)) {
[154]1124            if (isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1125                return $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id];
[1]1126            } else {
1127                return '';
1128            }
[154]1129        } else if (is_array($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1130            return end($_SESSION['_app'][$this->_ns]['boomerang']['url']);
[1]1131        } else {
1132            return false;
1133        }
1134    }
[42]1135
[1]1136    /**
1137     * Delete the URL set for the specified $id.
1138     *
1139     * @param string  $id     An identification tag for this url.
1140     */
[439]1141    public function deleteBoomerangURL($id=null)
[1]1142    {
[136]1143        if (!$this->running) {
1144            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]1145            return false;
1146        }
[42]1147
[136]1148        $this->logMsg(sprintf('deleteBoomerangURL(%s): %s', $id, $this->getBoomerangURL($id)), LOG_DEBUG, __FILE__, __LINE__);
[22]1149
[154]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']);
[1]1154        }
1155    }
[42]1156
[1]1157    /**
[103]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.
[1]1160     *
[103]1161     * @return bool  True if it is set and valid, false otherwise.
[1]1162     */
[439]1163    public function validBoomerangURL($id=null, $use_nonspecificboomerang=false)
[1]1164    {
[136]1165        if (!$this->running) {
1166            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
[1]1167            return false;
1168        }
[42]1169
[154]1170        if (!isset($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
[136]1171            $this->logMsg(sprintf('validBoomerangURL(%s) no boomerang URL set.', $id), LOG_DEBUG, __FILE__, __LINE__);
[1]1172            return false;
1173        }
[42]1174
[334]1175        // Time is the time stamp of a boomerangURL redirection, or setting of a boomerangURL.
[1]1176        // a boomerang redirection will always occur at least several seconds after the last boomerang redirect
1177        // or a boomerang being set.
[154]1178        $boomerang_time = isset($_SESSION['_app'][$this->_ns]['boomerang']['time']) ? $_SESSION['_app'][$this->_ns]['boomerang']['time'] : 0;
[42]1179
[22]1180        $url = '';
[154]1181        if (isset($id) && isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1182            $url = $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id];
[1]1183        } else if (!isset($id) || $use_nonspecificboomerang) {
1184            // Use non specific boomerang if available.
[154]1185            $url = end($_SESSION['_app'][$this->_ns]['boomerang']['url']);
[1]1186        }
[42]1187
[136]1188        $this->logMsg(sprintf('validBoomerangURL(%s) testing: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
[22]1189
1190        if ('' == $url) {
[136]1191            $this->logMsg(sprintf('validBoomerangURL(%s) not valid, empty!', $id), LOG_DEBUG, __FILE__, __LINE__);
[1]1192            return false;
1193        }
1194        if ($url == absoluteMe()) {
1195            // The URL we are directing to is the current page.
[136]1196            $this->logMsg(sprintf('validBoomerangURL(%s) not valid, same as absoluteMe: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
[1]1197            return false;
1198        }
1199        if ($boomerang_time >= (time() - 2)) {
[159]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__);
[1]1202            return false;
1203        }
[42]1204
[136]1205        $this->logMsg(sprintf('validBoomerangURL(%s) is valid: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
[1]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     */
[439]1213    public function sslOn()
[1]1214    {
[38]1215        if (function_exists('apache_get_modules')) {
[42]1216            $modules = apache_get_modules();
[38]1217        } else {
1218            // It's safe to assume we have mod_ssl if we can't determine otherwise.
1219            $modules = array('mod_ssl');
1220        }
[42]1221
[136]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__);
[1]1224            // Always append session because some browsers do not send cookie when crossing to SSL URL.
[136]1225            $this->dieURL('https://' . $this->getParam('ssl_domain') . getenv('REQUEST_URI'), null, true);
[1]1226        }
1227    }
[42]1228
1229
[1]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     */
[439]1234    public function sslOff()
[1]1235    {
[53]1236        if ('' != getenv('HTTPS')) {
[1]1237            $this->dieURL('http://' . getenv('HTTP_HOST') . getenv('REQUEST_URI'), null, true);
1238        }
1239    }
1240
[42]1241
[439]1242} // End.
Note: See TracBrowser for help on using the repository browser.