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

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

added public and private keywords to all properties and methods, changed old classname constructor function to construct, removed more ?> closing tags

File size: 53.8 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
24 * App.inc.php
25 *
26 * Primary application framework class.
27 *
28 * @author  Quinn Comendant <quinn@strangecode.com>
29 * @version 2.1
30 */
31
32// 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        restore_include_path();
400        $this->running = false;
401        $num_queries = 0;
402        if (true === $this->getParam('enable_db')) {
403            $num_queries = $this->db->numQueries();
404            $this->db->close();
405        }
406        $this->timer->stop('_app');
407        $this->logMsg(sprintf('Script ended gracefully. Execution time: %s. Number of db queries: %s.', $this->timer->getTime('_app'), $num_queries), LOG_DEBUG, __FILE__, __LINE__);
408    }
409
410
411    /**
412     * Add a message to the session, which is printed in the header.
413     * Just a simple way to print messages to the user.
414     *
415     * @access public
416     *
417     * @param string $message The text description of the message.
418     * @param int    $type    The type of message: MSG_NOTICE,
419     *                        MSG_SUCCESS, MSG_WARNING, or MSG_ERR.
420     * @param string $file    __FILE__.
421     * @param string $line    __LINE__.
422     */
423    public function raiseMsg($message, $type=MSG_NOTICE, $file=null, $line=null)
424    {
425        $message = trim($message);
426
427        if (!$this->running) {
428            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
429            return false;
430        }
431
432        if ('' == trim($message)) {
433            $this->logMsg(sprintf('Raised message is an empty string.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
434            return false;
435        }
436       
437        // Avoid duplicate full-stops..
438        $message = trim(preg_replace('/\.{2}$/', '.', $message));
439
440        // Save message in session under unique key to avoid duplicate messages.
441        $msg_id = md5($type . $message);
442        if (!isset($_SESSION['_app'][$this->_ns]['messages'][$msg_id])) {
443            $_SESSION['_app'][$this->_ns]['messages'][$msg_id] = array(
444                'type'    => $type,
445                'message' => $message,
446                'file'    => $file,
447                'line'    => $line,
448                'count'   => (isset($_SESSION['_app'][$this->_ns]['messages'][$msg_id]['count']) ? (1 + $_SESSION['_app'][$this->_ns]['messages'][$msg_id]['count']) : 1)
449            );
450        }
451
452        if (!in_array($type, array(MSG_NOTICE, MSG_SUCCESS, MSG_WARNING, MSG_ERR))) {
453            $this->logMsg(sprintf('Invalid MSG_* type: %s', $type), LOG_NOTICE, __FILE__, __LINE__);
454        }
455    }
456   
457    /**
458     * Returns an array of the raised messages.
459     *
460     * @access  public
461     * @return  array   List of messages in FIFO order.
462     * @author  Quinn Comendant <quinn@strangecode.com>
463     * @since   21 Dec 2005 13:09:20
464     */
465    public function getRaisedMessages()
466    {
467        if (!$this->running) {
468            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
469            return false;
470        }
471
472        return isset($_SESSION['_app'][$this->_ns]['messages']) ? $_SESSION['_app'][$this->_ns]['messages'] : array();
473    }
474   
475    /**
476     * Resets the message list.
477     *
478     * @access  public
479     * @author  Quinn Comendant <quinn@strangecode.com>
480     * @since   21 Dec 2005 13:21:54
481     */
482    public function clearRaisedMessages()
483    {
484        if (!$this->running) {
485            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
486            return false;
487        }
488       
489        $_SESSION['_app'][$this->_ns]['messages'] = array();
490    }
491
492    /**
493     * Prints the HTML for displaying raised messages.
494     *
495     * @param   string  $above    Additional message to print above error messages (e.g. "Oops!").
496     * @param   string  $below    Additional message to print below error messages (e.g. "Please fix and resubmit").
497     * @param   string  $print_gotohash_js  Print a line of javascript that scrolls the browser window down to view any error messages.
498     * @param   string  $hash     The #hashtag to scroll to.
499     * @access  public
500     * @author  Quinn Comendant <quinn@strangecode.com>
501     * @since   15 Jul 2005 01:39:14
502     */
503    public function printRaisedMessages($above='', $below='', $print_gotohash_js=false, $hash='sc-msg')
504    {
505        if (!$this->running) {
506            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
507            return false;
508        }
509       
510        $messages = $this->getRaisedMessages();
511        if (!empty($messages)) {
512            ?><div id="sc-msg" class="sc-msg"><?php
513            if ('' != $above) {
514                ?><div class="sc-above"><?php echo oTxt($above); ?></div><?php
515            }
516            foreach ($messages as $m) {
517                if (error_reporting() > 0 && $this->getParam('display_errors') && isset($m['file']) && isset($m['line'])) {
518                    echo "\n<!-- [" . $m['file'] . ' : ' . $m['line'] . '] -->';
519                }
520                switch ($m['type']) {
521                case MSG_ERR:
522                    echo '<div class="sc-msg-error">' . $m['message'] . '</div>';
523                    break;
524
525                case MSG_WARNING:
526                    echo '<div class="sc-msg-warning">' . $m['message'] . '</div>';
527                    break;
528
529                case MSG_SUCCESS:
530                    echo '<div class="sc-msg-success">' . $m['message'] . '</div>';
531                    break;
532
533                case MSG_NOTICE:
534                default:
535                    echo '<div class="sc-msg-notice">' . $m['message'] . '</div>';
536                    break;
537
538                }
539            }
540            if ('' != $below) {
541                ?><div class="sc-below"><?php echo oTxt($below); ?></div><?php
542            }
543            ?></div><?php
544            if ($print_gotohash_js) {
545                ?>
546                <script type="text/javascript">
547                /* <![CDATA[ */
548                window.location.hash = '#<?php echo urlencode($hash); ?>';
549                /* ]]> */
550                </script>
551                <?php
552            }
553        }
554        $this->clearRaisedMessages();
555    }
556
557    /**
558     * Logs messages to defined channels: file, email, sms, and screen. Repeated messages are
559     * not repeated but printed once with count. Log events that match a sendable channel (email or SMS)
560     * are sent once per 'log_multiple_timeout' setting (to avoid a flood of error emails).
561     *
562     * @access public
563     * @param string $message   The text description of the message.
564     * @param int    $priority  The type of message priority (in descending order):
565     *                          LOG_EMERG     0 system is unusable
566     *                          LOG_ALERT     1 action must be taken immediately
567     *                          LOG_CRIT      2 critical conditions
568     *                          LOG_ERR       3 error conditions
569     *                          LOG_WARNING   4 warning conditions
570     *                          LOG_NOTICE    5 normal, but significant, condition
571     *                          LOG_INFO      6 informational message
572     *                          LOG_DEBUG     7 debug-level message
573     * @param string $file      The file where the log event occurs.
574     * @param string $line      The line of the file where the log event occurs.
575     */
576    public function logMsg($message, $priority=LOG_INFO, $file=null, $line=null)
577    {
578        static $previous_events = array();
579
580        // If priority is not specified, assume the worst.
581        if (!$this->logPriorityToString($priority)) {
582            $this->logMsg(sprintf('Log priority %s not defined. (Message: %s)', $priority, $message), LOG_EMERG, $file, $line);
583            $priority = LOG_EMERG;
584        }
585
586        // If log file is not specified, don't log to a file.
587        if (!$this->getParam('log_directory') || !$this->getParam('log_filename') || !is_dir($this->getParam('log_directory')) || !is_writable($this->getParam('log_directory'))) {
588            $this->setParam(array('log_file_priority' => false));
589            // We must use trigger_error to report this problem rather than calling $app->logMsg, which might lead to an infinite loop.
590            trigger_error(sprintf('Codebase error: log directory (%s) not found or writable.', $this->getParam('log_directory')), E_USER_NOTICE);
591        }
592       
593        // Before we get any further, let's see if ANY log events are configured to be reported.
594        if ((false === $this->getParam('log_file_priority') || $priority > $this->getParam('log_file_priority'))
595        && (false === $this->getParam('log_email_priority') || $priority > $this->getParam('log_email_priority'))
596        && (false === $this->getParam('log_sms_priority') || $priority > $this->getParam('log_sms_priority'))
597        && (false === $this->getParam('log_screen_priority') || $priority > $this->getParam('log_screen_priority'))) {
598            // This event would not be recorded, skip it entirely.
599            return false;
600        }
601
602        // Make sure to log in the system's locale.
603        $locale = setlocale(LC_TIME, 0);
604        setlocale(LC_TIME, 'C');
605
606        // Strip HTML tags except any with more than 7 characters because that's probably not a HTML tag, e.g. <email@address.com>.
607        preg_match_all('/(<[^>\s]{7,})[^>]*>/', $message, $strip_tags_allow);
608        $message = strip_tags(preg_replace('/\s+/', ' ', $message), (!empty($strip_tags_allow[1]) ? join('> ', $strip_tags_allow[1]) . '>' : null));
609
610        // Store this event under a unique key, counting each time it occurs so that it only gets reported a limited number of times.
611        $msg_id = md5($message . $priority . $file . $line);
612        if ($this->getParam('log_ignore_repeated_events') && isset($previous_events[$msg_id])) {
613            $previous_events[$msg_id]++;
614            if ($previous_events[$msg_id] == 2) {
615                $this->logMsg(sprintf('%s (Event repeated %s or more times)', $message, $previous_events[$msg_id]), $priority, $file, $line);
616            }
617            return false;
618        } else {
619            $previous_events[$msg_id] = 1;
620        }
621
622        // For email and SMS notification types use "lock" files to prevent sending email and SMS notices ad infinitum.
623        if ((false !== $this->getParam('log_email_priority') && $priority <= $this->getParam('log_email_priority'))
624        || (false !== $this->getParam('log_sms_priority') && $priority <= $this->getParam('log_sms_priority'))) {
625            // This event will generate a "send" notification. Prepare lock file.
626            $site_hash = md5(empty($_SERVER['SERVER_NAME']) ? $_SERVER['SCRIPT_FILENAME'] : $_SERVER['SERVER_NAME']);
627            $lock_dir = $this->getParam('tmp_dir') . "/codebase_msgs_$site_hash/";
628            // Just use the file and line for the msg_id to limit the number of possible messages
629            // (the message string itself shan't be used as it may contain innumerable combinations).
630            $lock_file = $lock_dir . md5($file . ':' . $line);
631            if (!is_dir($lock_dir)) {
632                mkdir($lock_dir);
633            }
634            $send_notifications = true;
635            if (is_file($lock_file)) {
636                $msg_last_sent = filectime($lock_file);
637                // Has this message been sent more recently than the timeout?
638                if ((time() - $msg_last_sent) <= $this->getParam('log_multiple_timeout')) {
639                    // This message was already sent recently.
640                    $send_notifications = false;
641                } else {
642                    // Timeout has expired; send notifications again and reset timeout.
643                    touch($lock_file);
644                }
645            } else {
646                touch($lock_file);
647            }
648        }
649       
650        // Data to be stored for a log event.
651        $event = array(
652            'date'      => date('Y-m-d H:i:s'),
653            'remote ip' => getRemoteAddr(),
654            'pid'       => getmypid(),
655            'type'      => $this->logPriorityToString($priority),
656            'file:line' => "$file : $line",
657            'url'       => mb_substr(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '', 0, 128),
658            'message'   => $message
659        );
660
661        // FILE ACTION
662        if (false !== $this->getParam('log_file_priority') && $priority <= $this->getParam('log_file_priority')) {
663            $event_str = '[' . join('] [', $event) . ']';
664            error_log(mb_substr($event_str, 0, 1024) . "\n", 3, $this->getParam('log_directory') . '/' . $this->getParam('log_filename'));
665        }
666
667        // EMAIL ACTION
668        if (false !== $this->getParam('log_email_priority') && $priority <= $this->getParam('log_email_priority') && $send_notifications) {
669            $hostname = (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n');
670            $subject = sprintf('[%s %s] %s', $hostname, $event['type'], mb_substr($message, 0, 64));
671            $email_msg = sprintf("A %s log event occurred on %s\n\n", $event['type'], $hostname);
672            $headers = 'From: ' . $this->getParam('site_email');
673            foreach ($event as $k=>$v) {
674                $email_msg .= sprintf("%-11s%s\n", $k, $v);
675            }
676            mb_send_mail($this->getParam('log_to_email_address'), $subject, $email_msg, $headers);
677        }
678
679        // SMS ACTION
680        if (false !== $this->getParam('log_sms_priority') && $priority <= $this->getParam('log_sms_priority') && $send_notifications) {
681            $hostname = (isset($_SERVER['HTTP_HOST']) && '' != $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : php_uname('n');
682            $subject = sprintf('[%s %s]', $hostname, $priority);
683            $sms_msg = sprintf('%s [%s:%s]', mb_substr($event['message'], 0, 64), basename($file), $line);
684            $headers = 'From: ' . $this->getParam('site_email');
685            mb_send_mail($this->getParam('log_to_sms_address'), $subject, $sms_msg, $headers);
686        }
687   
688        // SCREEN ACTION
689        if (false !== $this->getParam('log_screen_priority') && $priority <= $this->getParam('log_screen_priority')) {
690            echo "[{$event['type']}] [{$event['message']}]\n";
691        }
692
693        // Restore original locale.
694        setlocale(LC_TIME, $locale);
695       
696        return true;
697    }
698
699    /**
700     * Returns the string representation of a LOG_* integer constant.
701     *
702     * @param int  $priority  The LOG_* integer constant.
703     *
704     * @return                The string representation of $priority.
705     */
706    public function logPriorityToString($priority) {
707        $priorities = array(
708            LOG_EMERG   => 'emergency',
709            LOG_ALERT   => 'alert',
710            LOG_CRIT    => 'critical',
711            LOG_ERR     => 'error',
712            LOG_WARNING => 'warning',
713            LOG_NOTICE  => 'notice',
714            LOG_INFO    => 'info',
715            LOG_DEBUG   => 'debug'
716        );
717        if (isset($priorities[$priority])) {
718            return $priorities[$priority];
719        } else {
720            return false;
721        }
722    }
723
724    /**
725     * Forcefully set a query argument even if one currently exists in the request.
726     * Values in the _carry_queries array will be copied to URLs (via $app->url()) and
727     * to hidden input values (via printHiddenSession()).
728     *
729     * @access  public
730     * @param   mixed   $query_key  The key (or keys, as an array) of the query argument to save.
731     * @param   mixed   $val        The new value of the argument key.
732     * @author  Quinn Comendant <quinn@strangecode.com>
733     * @since   13 Oct 2007 11:34:51
734     */
735    public function setQuery($query_key, $val)
736    {
737        if (!is_array($query_key)) {
738            $query_key = array($query_key);
739        }
740        foreach ($query_key as $k) {
741            // Set the value of the specified query argument into the _carry_queries array.
742            $this->_carry_queries[$k] = $val;
743        }
744    }
745
746    /**
747     * Specify which query arguments will be carried persistently between requests.
748     * Values in the _carry_queries array will be copied to URLs (via $app->url()) and
749     * to hidden input values (via printHiddenSession()).
750     *
751     * @access  public
752     * @param   mixed   $query_key   The key (or keys, as an array) of the query argument to save.
753     * @param   mixed   $default    If the key is not available, set to this default value.
754     * @author  Quinn Comendant <quinn@strangecode.com>
755     * @since   14 Nov 2005 19:24:52
756     */
757    public function carryQuery($query_key, $default=false)
758    {
759        if (!is_array($query_key)) {
760            $query_key = array($query_key);
761        }
762        foreach ($query_key as $k) {
763            // If not already set, and there is a non-empty value provided in the request...
764            if (!isset($this->_carry_queries[$k]) && false !== getFormData($k, $default)) {
765                // Copy the value of the specified query argument into the _carry_queries array.
766                $this->_carry_queries[$k] = getFormData($k, $default);
767                $this->logMsg(sprintf('Carrying query: %s => %s', $k, truncate(getDump($this->_carry_queries[$k], true), 128, 'end')), LOG_DEBUG, __FILE__, __LINE__);
768            }
769        }
770    }
771
772    /**
773     * dropQuery() is the opposite of carryQuery(). The specified value will not appear in
774     * url()/ohref()/printHiddenSession() modified URLs unless explicitly written in.
775     *
776     * @access  public
777     * @param   mixed   $query_key  The key (or keys, as an array) of the query argument to remove.
778     * @param   bool    $unset      Remove any values set in the request matching the given $query_key.
779     * @author  Quinn Comendant <quinn@strangecode.com>
780     * @since   18 Jun 2007 20:57:29
781     */
782    public function dropQuery($query_key, $unset=false)
783    {
784        if (!is_array($query_key)) {
785            $query_key = array($query_key);
786        }
787        foreach ($query_key as $k) {
788            if (isset($this->_carry_queries[$k])) {
789                // Remove the value of the specified query argument from the _carry_queries array.
790                $this->logMsg(sprintf('Dropping carried query: %s => %s', $k, $this->_carry_queries[$k]), LOG_DEBUG, __FILE__, __LINE__);
791                unset($this->_carry_queries[$k]);
792            }
793            if ($unset && isset($_REQUEST[$k])) {
794                unset($_REQUEST[$k], $_GET[$k], $_POST[$k], $_COOKIE[$k]);
795            }
796        }
797    }
798
799    /**
800     * Outputs a fully qualified URL with a query of all the used (ie: not empty)
801     * keys and values, including optional queries. This allows mindless retention
802     * of query arguments across page requests. If cookies are not
803     * used, the session id will be propagated in the URL.
804     *
805     * @param  string $url              The initial url
806     * @param  mixed  $carry_args       Additional url arguments to carry in the query,
807     *                                  or FALSE to prevent carrying queries. Can be any of the following formats:
808     *                                      array('key1', key2', key3')  <-- to save these keys if in the form data.
809     *                                      array('key1'=>'value', key2'='value')  <-- to set keys to default values if not present in form data.
810     *                                      false  <-- To not carry any queries. If URL already has queries those will be retained.
811     *
812     * @param  mixed  $always_include_sid  Always add the session id, even if using_trans_sid = true. This is required when
813     *                                     URL starts with http, since PHP using_trans_sid doesn't do those and also for
814     *                                     header('Location...') redirections.
815     *
816     * @return string url with attached queries and, if not using cookies, the session id
817     */
818    public function url($url, $carry_args=null, $always_include_sid=false)
819    {
820        if (!$this->running) {
821            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
822            return false;
823        }
824
825        // Get any provided query arguments to include in the final URL.
826        // If FALSE is a provided here, DO NOT carry the queries.
827        $do_carry_queries = true;
828        $one_time_carry_queries = array();
829        if (!is_null($carry_args)) {
830            if (is_array($carry_args) && !empty($carry_args)) {
831                foreach ($carry_args as $key=>$arg) {
832                    // Get query from appropriate source.
833                    if (false === $arg) {
834                        $do_carry_queries = false;
835                    } else if (false !== getFormData($arg, false)) {
836                        $one_time_carry_queries[$arg] = getFormData($arg); // Set arg to form data if available.
837                    } else if (!is_numeric($key) && '' != $arg) {
838                        $one_time_carry_queries[$key] = getFormData($key, $arg); // Set to arg to default if specified (overwritten by form data).
839                    }
840                }
841            } else if (false !== getFormData($carry_args, false)) {
842                $one_time_carry_queries[$carry_args] = getFormData($carry_args);
843            } else if (false === $carry_args) {
844                $do_carry_queries = false;
845            }
846        }
847
848        // Get the first delimiter that is needed in the url.
849        $delim = mb_strpos($url, '?') !== false ? ini_get('arg_separator.output') : '?';
850
851        $q = '';
852        if ($do_carry_queries) {
853            // Join the global _carry_queries and local one_time_carry_queries.
854            $query_args = urlEncodeArray(array_merge($this->_carry_queries, $one_time_carry_queries));
855            foreach ($query_args as $key=>$val) {
856                // Check value is set and value does not already exist in the url.
857                if (!preg_match('/[?&]' . preg_quote($key) . '=/', $url)) {
858                    $q .= $delim . $key . '=' . $val;
859                    $delim = ini_get('arg_separator.output');
860                }
861            }
862        }
863
864        // Include the necessary SID if the following is true:
865        // - no cookie in http request OR cookies disabled in App
866        // - sessions are enabled
867        // - the link stays on our site
868        // - transparent SID propagation with session.use_trans_sid is not being used OR url begins with protocol (using_trans_sid has no effect here)
869        // OR
870        // - 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)
871        // AND
872        // - the SID is not already in the query.
873        if (
874            (
875                (
876                    (
877                        !isset($_COOKIE[session_name()])
878                        || !$this->getParam('session_use_cookies')
879                    )
880                    && $this->getParam('session_use_trans_sid')
881                    && $this->getParam('enable_session')
882                    && isMyDomain($url)
883                    && (
884                        !ini_get('session.use_trans_sid')
885                        || preg_match('!^(http|https)://!i', $url)
886                    )
887                )
888                || $always_include_sid
889            )
890            && !preg_match('/[?&]' . preg_quote(session_name()) . '=/', $url)
891        ) {
892            $url .= $q . $delim . session_name() . '=' . session_id();
893            return $url;
894        } else {
895            $url .= $q;
896            return $url;
897        }
898    }
899
900    /**
901     * Returns a HTML-friendly URL processed with $app->url and & replaced with &amp;
902     *
903     * @access  public
904     * @param   string  $url    Input URL to parse.
905     * @return  string          URL passed through $app->url() and then & turned to $amp;.
906     * @author  Quinn Comendant <quinn@strangecode.com>
907     * @since   09 Dec 2005 17:58:45
908     */
909    public function oHREF($url, $carry_args=null, $always_include_sid=false)
910    {
911        $url = $this->url($url, $carry_args, $always_include_sid);
912
913        // Replace any & not followed by an html or unicode entity with it's &amp; equivalent.
914        $url = preg_replace('/&(?![\w\d#]{1,10};)/', '&amp;', $url);
915
916        return $url;
917    }
918
919    /**
920     * Prints a hidden form element with the PHPSESSID when cookies are not used, as well
921     * as hidden form elements for GET_VARS that might be in use.
922     *
923     * @param  mixed  $carry_args        Additional url arguments to carry in the query,
924     *                                   or FALSE to prevent carrying queries. Can be any of the following formats:
925     *                                      array('key1', key2', key3')  <-- to save these keys if in the form data.
926     *                                      array('key1'=>'value', key2'='value')  <-- to set keys to default values if not present in form data.
927     *                                      false  <-- To not carry any queries. If URL already has queries those will be retained.
928     */
929    public function printHiddenSession($carry_args=null)
930    {
931        if (!$this->running) {
932            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
933            return false;
934        }
935
936        // Get any provided query arguments to include in the final hidden form data.
937        // If FALSE is a provided here, DO NOT carry the queries.
938        $do_carry_queries = true;
939        $one_time_carry_queries = array();
940        if (!is_null($carry_args)) {
941            if (is_array($carry_args) && !empty($carry_args)) {
942                foreach ($carry_args as $key=>$arg) {
943                    // Get query from appropriate source.
944                    if (false === $arg) {
945                        $do_carry_queries = false;
946                    } else if (false !== getFormData($arg, false)) {
947                        $one_time_carry_queries[$arg] = getFormData($arg); // Set arg to form data if available.
948                    } else if (!is_numeric($key) && '' != $arg) {
949                        $one_time_carry_queries[$key] = getFormData($key, $arg); // Set to arg to default if specified (overwritten by form data).
950                    }
951                }
952            } else if (false !== getFormData($carry_args, false)) {
953                $one_time_carry_queries[$carry_args] = getFormData($carry_args);
954            } else if (false === $carry_args) {
955                $do_carry_queries = false;
956            }
957        }
958
959        // For each existing request value, we create a hidden input to carry it through a form.
960        if ($do_carry_queries) {
961            // Join the global _carry_queries and local one_time_carry_queries.
962            // urlencode is not used here, not for form data!
963            $query_args = array_merge($this->_carry_queries, $one_time_carry_queries);
964            foreach ($query_args as $key=>$val) {
965                printf('<input type="hidden" name="%s" value="%s" />', $key, $val);
966            }
967        }
968
969        // Include the SID if cookies are disabled.
970        if (!isset($_COOKIE[session_name()]) && !ini_get('session.use_trans_sid')) {
971            printf('<input type="hidden" name="%s" value="%s" />', session_name(), session_id());
972        }
973    }
974
975    /**
976     * Uses an http header to redirect the client to the given $url. If sessions are not used
977     * and the session is not already defined in the given $url, the SID is appended as a URI query.
978     * As with all header generating functions, make sure this is called before any other output.
979     *
980     * @param   string  $url                    The URL the client will be redirected to.
981     * @param   mixed   $carry_args             Additional url arguments to carry in the query,
982     *                                          or FALSE to prevent carrying queries. Can be any of the following formats:
983     *                                          -array('key1', key2', key3')  <-- to save these keys if in the form data.
984     *                                          -array('key1' => 'value', key2' => 'value')  <-- to set keys to default values if not present in form data.
985     *                                          -false  <-- To not carry any queries. If URL already has queries those will be retained.
986     * @param   bool    $always_include_sid     Force session id to be added to Location header.
987     */
988    public function dieURL($url, $carry_args=null, $always_include_sid=false)
989    {
990        if (!$this->running) {
991            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
992            return false;
993        }
994
995        if ('' == $url) {
996            // If URL is not specified, use the redirect_home_url.
997            $url = $this->getParam('redirect_home_url');
998        }
999
1000        if (preg_match('!^/!', $url)) {
1001            // If relative URL is given, prepend correct local hostname.
1002            $scheme = 'on' == getenv('HTTPS') ? 'https' : 'http';
1003            $host = getenv('HTTP_HOST');
1004            $url = sprintf('%s://%s%s', $scheme, $host, $url);
1005        }
1006
1007        $url = $this->url($url, $carry_args, $always_include_sid);
1008
1009        // Should we send a "303 See Other" header here instead of relying on the 302 sent automatically by PHP?
1010        header(sprintf('Location: %s', $url));
1011        $this->logMsg(sprintf('dieURL: %s', $url), LOG_DEBUG, __FILE__, __LINE__);
1012
1013        // End application.
1014        // Recommended, although I'm not sure it's necessary: http://cn2.php.net/session_write_close
1015        $this->stop();
1016        die;
1017    }
1018
1019    /*
1020    * Redirects a user by calling $app->dieURL(). It will use:
1021    * 1. the stored boomerang URL, it it exists
1022    * 2. a specified $default_url, it it exists
1023    * 3. the referring URL, it it exists.
1024    * 4. redirect_home_url configuration variable.
1025    *
1026    * @access   public
1027    * @param    string  $id             Identifier for this script.
1028    * @param    mixed   $carry_args     Additional arguments to carry in the URL automatically (see $app->oHREF()).
1029    * @param    string  $default_url    A default URL if there is not a valid specified boomerang URL.
1030    * @param    bool    $queryless_referrer_comparison   Exclude the URL query from the refererIsMe() comparison.
1031    * @return   bool                    False if the session is not running. No return otherwise.
1032    * @author   Quinn Comendant <quinn@strangecode.com>
1033    * @since    31 Mar 2006 19:17:00
1034    */
1035    public function dieBoomerangURL($id=null, $carry_args=null, $default_url=null, $queryless_referrer_comparison=false)
1036    {
1037        if (!$this->running) {
1038            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1039            return false;
1040        }
1041
1042        // Get URL from stored boomerang. Allow non specific URL if ID not valid.
1043        if ($this->validBoomerangURL($id, true)) {
1044            if (isset($id) && isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1045                $url = $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id];
1046                $this->logMsg(sprintf('dieBoomerangURL(%s) found: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1047            } else {
1048                $url = end($_SESSION['_app'][$this->_ns]['boomerang']['url']);
1049                $this->logMsg(sprintf('dieBoomerangURL(%s) using: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1050            }
1051            // Delete stored boomerang.
1052            $this->deleteBoomerangURL($id);
1053        } else if (isset($default_url)) {
1054            $url = $default_url;
1055        } else if (!refererIsMe(true === $queryless_referrer_comparison)) {
1056            // Ensure that the redirecting page is not also the referrer.
1057            $url = getenv('HTTP_REFERER');
1058            $this->logMsg(sprintf('dieBoomerangURL(%s) using referrer: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1059        } else {
1060            // If URL is not specified, use the redirect_home_url.
1061            $url = $this->getParam('redirect_home_url');
1062            $this->logMsg(sprintf('dieBoomerangURL(%s) using redirect_home_url: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1063        }
1064
1065        // A redirection will never happen immediately twice.
1066        // Set the time so ensure this doesn't happen.
1067        $_SESSION['_app'][$this->_ns]['boomerang']['time'] = time();
1068        $this->dieURL($url, $carry_args);
1069    }
1070
1071    /**
1072     * Set the URL to return to when $app->dieBoomerangURL() is called.
1073     *
1074     * @param string  $url  A fully validated URL.
1075     * @param bool  $id     An identification tag for this url.
1076     * FIXME: url garbage collection?
1077     */
1078    public function setBoomerangURL($url=null, $id=null)
1079    {
1080        if (!$this->running) {
1081            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1082            return false;
1083        }
1084        // A redirection will never happen immediately after setting the boomerangURL.
1085        // Set the time so ensure this doesn't happen. See $app->validBoomerangURL for more.
1086        /// FIXME: Why isn't the time set here under setBoomerangURL() and only under dieBoomerangURL()?
1087
1088        if ('' != $url && is_string($url)) {
1089            // Delete any boomerang request keys in the query string (along with any trailing delimiters after the deletion).
1090            $url = preg_replace(array('/([&?])boomerang=\w+[&?]?/', '/[&?]$/'), array('$1', ''), $url);
1091
1092            if (isset($_SESSION['_app'][$this->_ns]['boomerang']['url']) && is_array($_SESSION['_app'][$this->_ns]['boomerang']['url']) && !empty($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1093                // If the URL currently exists in the boomerang array, delete.
1094                while ($existing_key = array_search($url, $_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1095                    unset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$existing_key]);
1096                }
1097            }
1098
1099            if (isset($id)) {
1100                $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id] = $url;
1101            } else {
1102                $_SESSION['_app'][$this->_ns]['boomerang']['url'][] = $url;
1103            }
1104            $this->logMsg(sprintf('setBoomerangURL(%s): %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1105            return true;
1106        } else {
1107            $this->logMsg(sprintf('setBoomerangURL(%s) is empty!', $id, $url), LOG_NOTICE, __FILE__, __LINE__);
1108            return false;
1109        }
1110    }
1111
1112    /**
1113     * Return the URL set for the specified $id, or an empty string if one isn't set.
1114     *
1115     * @param string  $id     An identification tag for this url.
1116     */
1117    public function getBoomerangURL($id=null)
1118    {
1119        if (!$this->running) {
1120            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1121            return false;
1122        }
1123
1124        if (isset($id)) {
1125            if (isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1126                return $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id];
1127            } else {
1128                return '';
1129            }
1130        } else if (is_array($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1131            return end($_SESSION['_app'][$this->_ns]['boomerang']['url']);
1132        } else {
1133            return false;
1134        }
1135    }
1136
1137    /**
1138     * Delete the URL set for the specified $id.
1139     *
1140     * @param string  $id     An identification tag for this url.
1141     */
1142    public function deleteBoomerangURL($id=null)
1143    {
1144        if (!$this->running) {
1145            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1146            return false;
1147        }
1148
1149        $this->logMsg(sprintf('deleteBoomerangURL(%s): %s', $id, $this->getBoomerangURL($id)), LOG_DEBUG, __FILE__, __LINE__);
1150
1151        if (isset($id) && isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1152            unset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id]);
1153        } else if (is_array($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1154            array_pop($_SESSION['_app'][$this->_ns]['boomerang']['url']);
1155        }
1156    }
1157
1158    /**
1159     * Check if a valid boomerang URL value has been set. A boomerang URL is considered
1160     * valid if: 1) it is not empty, 2) it is not the current URL, and 3) has not been accessed within n seconds.
1161     *
1162     * @return bool  True if it is set and valid, false otherwise.
1163     */
1164    public function validBoomerangURL($id=null, $use_nonspecificboomerang=false)
1165    {
1166        if (!$this->running) {
1167            $this->logMsg(sprintf('Canceled method call %s, application not running.', __FUNCTION__), LOG_NOTICE, __FILE__, __LINE__);
1168            return false;
1169        }
1170
1171        if (!isset($_SESSION['_app'][$this->_ns]['boomerang']['url'])) {
1172            $this->logMsg(sprintf('validBoomerangURL(%s) no boomerang URL set.', $id), LOG_DEBUG, __FILE__, __LINE__);
1173            return false;
1174        }
1175
1176        // Time is the time stamp of a boomerangURL redirection, or setting of a boomerangURL.
1177        // a boomerang redirection will always occur at least several seconds after the last boomerang redirect
1178        // or a boomerang being set.
1179        $boomerang_time = isset($_SESSION['_app'][$this->_ns]['boomerang']['time']) ? $_SESSION['_app'][$this->_ns]['boomerang']['time'] : 0;
1180
1181        $url = '';
1182        if (isset($id) && isset($_SESSION['_app'][$this->_ns]['boomerang']['url'][$id])) {
1183            $url = $_SESSION['_app'][$this->_ns]['boomerang']['url'][$id];
1184        } else if (!isset($id) || $use_nonspecificboomerang) {
1185            // Use non specific boomerang if available.
1186            $url = end($_SESSION['_app'][$this->_ns]['boomerang']['url']);
1187        }
1188
1189        $this->logMsg(sprintf('validBoomerangURL(%s) testing: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1190
1191        if ('' == $url) {
1192            $this->logMsg(sprintf('validBoomerangURL(%s) not valid, empty!', $id), LOG_DEBUG, __FILE__, __LINE__);
1193            return false;
1194        }
1195        if ($url == absoluteMe()) {
1196            // The URL we are directing to is the current page.
1197            $this->logMsg(sprintf('validBoomerangURL(%s) not valid, same as absoluteMe: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1198            return false;
1199        }
1200        if ($boomerang_time >= (time() - 2)) {
1201            // Last boomerang direction was less than 2 seconds ago.
1202            $this->logMsg(sprintf('validBoomerangURL(%s) not valid, boomerang_time too short: %s seconds', $id, time() - $boomerang_time), LOG_DEBUG, __FILE__, __LINE__);
1203            return false;
1204        }
1205
1206        $this->logMsg(sprintf('validBoomerangURL(%s) is valid: %s', $id, $url), LOG_DEBUG, __FILE__, __LINE__);
1207        return true;
1208    }
1209
1210    /**
1211     * Force the user to connect via https (port 443) by redirecting them to
1212     * the same page but with https.
1213     */
1214    public function sslOn()
1215    {
1216        if (function_exists('apache_get_modules')) {
1217            $modules = apache_get_modules();
1218        } else {
1219            // It's safe to assume we have mod_ssl if we can't determine otherwise.
1220            $modules = array('mod_ssl');
1221        }
1222
1223        if ('' == getenv('HTTPS') && $this->getParam('ssl_enabled') && in_array('mod_ssl', $modules)) {
1224            $this->raiseMsg(sprintf(_("Secure SSL connection made to %s"), $this->getParam('ssl_domain')), MSG_NOTICE, __FILE__, __LINE__);
1225            // Always append session because some browsers do not send cookie when crossing to SSL URL.
1226            $this->dieURL('https://' . $this->getParam('ssl_domain') . getenv('REQUEST_URI'), null, true);
1227        }
1228    }
1229
1230
1231    /**
1232     * to enforce the user to connect via http (port 80) by redirecting them to
1233     * a http version of the current url.
1234     */
1235    public function sslOff()
1236    {
1237        if ('' != getenv('HTTPS')) {
1238            $this->dieURL('http://' . getenv('HTTP_HOST') . getenv('REQUEST_URI'), null, true);
1239        }
1240    }
1241
1242
1243} // End.
Note: See TracBrowser for help on using the repository browser.