Package base :: Package includes :: Module bootstrap
[hide private]

Source Code for Module base.includes.bootstrap

   1  #!/usr/bin/env python 
   2  # $Id: bootstrap.inc,v 1.218 2008/08/02 19:01:02 dries Exp $ 
   3   
   4  """ 
   5    Functions that need to be loaded on every Drupal requst 
   6   
   7    @package base.includes 
   8    @see <a href='http://drupy.net'>Drupy Homepage</a> 
   9    @see <a href='http://drupal.org'>Drupal Homepage</a> 
  10    @note Drupy is a port of the Drupal project. 
  11    @note This file was ported from Drupal's includes/bootstrap.inc 
  12    @author Brendon Crawford 
  13    @copyright 2008 Brendon Crawford 
  14    @contact message144 at users dot sourceforge dot net 
  15    @created 2008-01-10 
  16    @version 0.1 
  17    @note License: 
  18   
  19      This program is free software; you can redistribute it and/or 
  20      modify it under the terms of the GNU General Public License 
  21      as published by the Free Software Foundation; either version 2 
  22      of the License, or (at your option) any later version. 
  23   
  24      This program is distributed in the hope that it will be useful, 
  25      but WITHOUT ANY WARRANTY; without even the implied warranty of 
  26      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  27      GNU General Public License for more details. 
  28   
  29      You should have received a copy of the GNU General Public License 
  30      along with this program; if not, write to: 
  31       
  32      The Free Software Foundation, Inc., 
  33      51 Franklin Street, Fifth Floor, 
  34      Boston, MA  02110-1301, 
  35      USA 
  36  """ 
  37   
  38  __version__ = "$Revision: 1 $" 
  39   
  40  # 
  41  # Includes 
  42  # 
  43  from lib.drupy import DrupyPHP as php 
  44  from lib.drupy import DrupyImport 
  45  from sites.default import settings 
  46  import appglobals as lib_appglobals 
  47  import cache as lib_cache 
  48  import database as lib_database 
  49  import session as lib_session 
  50  import theme_maintenance as lib_theme_maintenance 
  51  import plugin as lib_plugin 
  52  import path as lib_path 
  53  import common as lib_common 
  54  import language as lib_language 
  55  #import registry as lib_registry 
  56   
  57  # 
  58  # Maintenance Mode 
  59  # 
  60  MAINTENANCE_MODE = False 
  61   
  62  # 
  63  # Indicates that the item should never be removed unless explicitly told to 
  64  # using cache_clear_all() with a cache ID. 
  65  # 
  66  CACHE_PERMANENT = 0 
  67   
  68  # 
  69  # Indicates that the item should be removed at the next general cache wipe. 
  70  # 
  71  CACHE_TEMPORARY = -1 
  72   
  73  # 
  74  # Indicates that page caching is disabled. 
  75  # 
  76  CACHE_DISABLED = 0 
  77   
  78  # 
  79  # Indicates that page caching is enabled, using "normal" mode. 
  80  # 
  81  CACHE_NORMAL = 1 
  82   
  83  # 
  84  # Indicates that page caching is using "aggressive" mode. This bypasses 
  85  # loading any plugins for additional speed, which may break functionality in 
  86  # plugins that expect to be run on each page load. 
  87  # 
  88  CACHE_AGGRESSIVE = 2 
  89   
  90  # 
  91  # Log message severity -- Alert: action must be taken immediately. 
  92  # 
  93  # @see watchdog() 
  94  # @see watchdog_severity_levels() 
  95  # 
  96   
  97  WATCHDOG_ALERT = 1 
  98   
  99  # 
 100  # Log message severity -- Critical: critical conditions. 
 101  # 
 102  # @see watchdog() 
 103  # @see watchdog_severity_levels() 
 104  # 
 105  WATCHDOG_CRITICAL = 2 
 106   
 107  # 
 108  # Log message severity -- Error: error conditions. 
 109  # 
 110  # @see watchdog() 
 111  # @see watchdog_severity_levels() 
 112  # 
 113   
 114  WATCHDOG_ERROR = 3 
 115   
 116  # 
 117  # Log message severity -- Warning: warning conditions. 
 118  # 
 119  # @see watchdog() 
 120  # @see watchdog_severity_levels() 
 121  # 
 122   
 123  WATCHDOG_WARNING = 4 
 124   
 125  # 
 126  # Log message severity -- Notice: normal but significant condition. 
 127  # 
 128  # @see watchdog() 
 129  # @see watchdog_severity_levels() 
 130  # 
 131  WATCHDOG_NOTICE = 5 
 132   
 133  # 
 134  # Log message severity -- Informational: informational messages. 
 135  # 
 136  # @see watchdog() 
 137  # @see watchdog_severity_levels() 
 138  # 
 139   
 140  WATCHDOG_INFO = 6 
 141   
 142  # 
 143  # Log message severity -- Debug: debug-level messages. 
 144  # 
 145  # @see watchdog() 
 146  # @see watchdog_severity_levels() 
 147  # 
 148   
 149  WATCHDOG_DEBUG = 7 
 150   
 151  # 
 152  # First bootstrap phase: initialize configuration. 
 153  # 
 154  DRUPAL_BOOTSTRAP_CONFIGURATION = 0 
 155   
 156  # 
 157  # Second bootstrap phase: try to call a non-database cache 
 158  # fetch routine. 
 159  # 
 160  DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE = 1 
 161   
 162  # 
 163  # Third bootstrap phase: initialize database layer. 
 164  # 
 165  DRUPAL_BOOTSTRAP_DATABASE = 2 
 166   
 167  # 
 168  # Fourth bootstrap phase: identify and reject banned hosts. 
 169  # 
 170  DRUPAL_BOOTSTRAP_ACCESS = 3 
 171   
 172  # 
 173  # Fifth bootstrap phase: initialize session handling. 
 174  # 
 175  DRUPAL_BOOTSTRAP_SESSION = 4 
 176   
 177  # 
 178  # Sixth bootstrap phase: load bootstrap.inc and plugin.inc, start 
 179  # the variable system and try to serve a page from the cache. 
 180  # 
 181  DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE = 5 
 182   
 183  # 
 184  # Seventh bootstrap phase: find out language of the page. 
 185  # 
 186  DRUPAL_BOOTSTRAP_LANGUAGE = 6 
 187   
 188  # 
 189  # Eighth bootstrap phase: set php.GET['q'] to Drupal path of request. 
 190  # 
 191  DRUPAL_BOOTSTRAP_PATH = 7 
 192   
 193  # 
 194  # Final bootstrap phase: Drupal is fully loaded; validate and fix 
 195  # input data. 
 196  # 
 197  DRUPAL_BOOTSTRAP_FULL = 8 
 198   
 199  # 
 200  # Role ID for anonymous users; should match what's in the "role" table. 
 201  # 
 202  DRUPAL_ANONYMOUS_RID = 9 
 203   
 204  # 
 205  # Role ID for authenticated users; should match what's in the "role" table. 
 206  # 
 207  DRUPAL_AUTHENTICATED_RID = 10 
 208   
 209  # 
 210  # No language negotiation. The default language is used. 
 211  # 
 212  LANGUAGE_NEGOTIATION_NONE = 0 
 213   
 214  # 
 215  # Path based negotiation with fallback to default language 
 216  # if no defined path prefix identified. 
 217  # 
 218  LANGUAGE_NEGOTIATION_PATH_DEFAULT = 1 
 219   
 220  # 
 221  # Path based negotiation with fallback to user preferences 
 222  # and browser language detection if no defined path prefix 
 223  # identified. 
 224  # 
 225  LANGUAGE_NEGOTIATION_PATH = 2 
 226   
 227  # 
 228  # Domain based negotiation with fallback to default language 
 229  # if no language identified by domain. 
 230  # 
 231  LANGUAGE_NEGOTIATION_DOMAIN = 3 
 232   
 233   
234 -def timer_start(name):
235 """ 236 Start the timer with the specified name. If you start and stop 237 the same timer multiple times, the measured intervals will be 238 accumulated. 239 240 @param name 241 The name of the timer. 242 """ 243 if lib_appglobals.timers == None: 244 lib_appglobals.timers = {}; 245 if not php.isset(lib_appglobals.timers, name): 246 lib_appglobals.timers[name] = {} 247 (usec, sec) = php.explode(' ', php.microtime()); 248 lib_appglobals.timers[name]['start'] = float(usec) + float(sec); 249 lib_appglobals.timers[name]['count'] = \ 250 ((lib_appglobals.timers[name]['count'] + 1) if \ 251 php.isset(lib_appglobals.timers[name],'count') else 1);
252 253
254 -def timer_read(name):
255 """ 256 Read the current timer value without stopping the timer. 257 258 @param name 259 The name of the timer. 260 @return 261 The current timer value in ms. 262 """ 263 if (php.isset(lib_appglobals.timers[name], 'start')): 264 (usec, sec) = php.explode(' ', php.microtime()); 265 stop = float(usec) + float(sec); 266 diff = round((stop - lib_appglobals.timers[name]['start']) * 1000, 2); 267 if (php.isset(lib_appglobals.timers[name], 'time')): 268 diff += lib_appglobals.timers[name]['time']; 269 return diff;
270 271
272 -def timer_stop(name):
273 """ 274 Stop the timer with the specified name. 275 276 @param name 277 The name of the timer. 278 @return 279 A timer array. The array contains the number of times the 280 timer has been started and stopped (count) and the accumulated 281 timer value in ms (time). 282 """ 283 lib_appglobals.timers[name]['time'] = lib_appglobals.timer_read(name); 284 del(lib_appglobals.timers[name]['start']); 285 return lib_appglobals.timers[name];
286 287
288 -def conf_path(require_settings = True, reset = False):
289 """ 290 Find the appropriate configuration directory. 291 292 Try finding a matching configuration directory by stripping the website's 293 hostname from left to right and pathname from right to left. The first 294 configuration file found will be used; the remaining will ignored. If no 295 configuration file is found, return a default value 'confdir/default'. 296 297 Example for a fictitious site installed at 298 http://www.drupal.org:8080/mysite/test/ the 'settings.php' is searched in 299 the following directories: 300 301 1. confdir/8080.www.drupal.org.mysite.test 302 2. confdir/www.drupal.org.mysite.test 303 3. confdir/drupal.org.mysite.test 304 4. confdir/org.mysite.test 305 306 5. confdir/8080.www.drupal.org.mysite 307 6. confdir/www.drupal.org.mysite 308 7. confdir/drupal.org.mysite 309 8. confdir/org.mysite 310 311 9. confdir/8080.www.drupal.org 312 10. confdir/www.drupal.org 313 11. confdir/drupal.org 314 12. confdir/org 315 316 13. confdir/default 317 318 @param require_settings 319 Only configuration directories with an existing settings.php file 320 will be recognized. Defaults to TRUE. During initial installation, 321 this is set to FALSE so that Drupal can detect a matching directory, 322 then create a new settings.php file in it. 323 @param reset 324 Force a full search for matching directories even if one had been 325 found previously. 326 @return 327 The path of the matching directory. 328 """ 329 return 'sites/default'
330 331
332 -def drupal_unset_globals():
333 """ 334 Unsets all disallowed global variables. See allowed for what's allowed. 335 """ 336 # Do nothing 337 pass;
338 339
340 -def conf_init():
341 """ 342 Loads the configuration and sets the base URL, cookie domain, and 343 session name correctly. 344 """ 345 # These will come from settings 346 # db_url, db_prefix, cookie_domain, conf, installed_profile, update_free_access 347 if (lib_appglobals.base_url != None): 348 # Parse fixed base URL from settings.php. 349 parts = php.parse_url(lib_appglobals.base_url); 350 if (not php.isset(parts, 'path')): 351 parts['path'] = ''; 352 lib_appglobals.base_path = parts['path'] + '/'; 353 # Build base_root (everything until first slash after "scheme://"). 354 lib_appglobals.base_root = \ 355 php.substr(lib_appglobals.base_url, 0, \ 356 php.strlen(lib_appglobals.base_url) - \ 357 php.strlen(parts['path'])); 358 else: 359 # Create base URL 360 lib_appglobals.base_root = \ 361 ('https' if (php.isset(php.SERVER, 'HTTPS') and \ 362 php.SERVER['HTTPS'] == 'on') else 'http'); 363 # As php.SERVER['HTTP_HOST'] is user input, ensure it only contains 364 # characters allowed in hostnames. 365 lib_appglobals.base_root += '://' + \ 366 php.preg_replace('/[^a-z0-9-:._]/i', '', \ 367 php.SERVER['HTTP_HOST']); 368 lib_appglobals.base_url = lib_appglobals.base_root; 369 # php.SERVER['SCRIPT_NAME'] can, in contrast to php.SERVER['PHP_SELF'], not 370 # be modified by a visitor. 371 dir = php.trim(php.dirname(php.SERVER['SCRIPT_NAME']), '\,/'); 372 if (len(dir) > 0): 373 lib_appglobals.base_path = "/dir"; 374 lib_appglobals.base_url += lib_appglobals.base_path 375 lib_appglobals.base_path += '/'; 376 else: 377 lib_appglobals.base_path = '/'; 378 if (settings.cookie_domain != None): 379 # If the user specifies the cookie domain, also use it for session name. 380 session_name_ = settings.cookie_domain; 381 else: 382 # Otherwise use base_url as session name, without the protocol 383 # to use the same session identifiers across http and https. 384 session_name_ = php.explode('://', lib_appglobals.base_url, 2)[1] 385 # We escape the hostname because it can be modified by a visitor. 386 if (not php.empty(php.SERVER['HTTP_HOST'])): 387 settings.cookie_domain = check_plain(php.SERVER['HTTP_HOST']); 388 # Strip leading periods, www., and port numbers from cookie domain. 389 settings.cookie_domain = php.ltrim(settings.cookie_domain, '.'); 390 if (php.strpos(settings.cookie_domain, 'www.') == 0): 391 settings.cookie_domain = php.substr(settings.cookie_domain, 4); 392 settings.cookie_domain = php.explode(':', settings.cookie_domain); 393 settings.cookie_domain = '.' + settings.cookie_domain[0]; 394 # Per RFC 2109, cookie domains must contain at least one dot other than the 395 # first. For hosts such as 'localhost' or IP Addresses we don't set a 396 # cookie domain. 397 if (php.count(php.explode('.', settings.cookie_domain)) > 2 and not \ 398 php.is_numeric(php.str_replace('.', '', settings.cookie_domain))): 399 php.ini_set('session.cookie_domain', settings.cookie_domain); 400 #print session_name; 401 lib_session.name('SESS' + php.md5(session_name_));
402 403 404
405 -def drupal_get_filename(type_, name, filename = None):
406 """ 407 Returns and optionally sets the filename for a system item (plugin, 408 theme, etc.). The filename, whether provided, cached, or retrieved 409 from the database, is only returned if the file exists. 410 411 This def plays a key role in allowing Drupal's resources (plugins 412 and themes) to be located in different places depending on a site's 413 configuration. For example, a plugin 'foo' may legally be be located 414 in any of these three places: 415 416 plugins/foo/__init__.py 417 sites/all/plugins/foo/__init__.py 418 sites/example.com/plugins/foo/__init__.py 419 420 Calling drupal_get_filename('plugin', 'foo') will give you one of 421 the above, depending on where the plugin is located. 422 423 @param type 424 The type of the item (i.e. theme, theme_engine, plugin). 425 @param name 426 The name of the item for which the filename is requested. 427 @param filename 428 The filename of the item if it is to be set explicitly rather 429 than by consulting the database. 430 431 @return 432 The filename of the requested item. 433 """ 434 php.static(drupal_get_filename, 'files', {}) 435 file = lib_database.result(lib_database.query(\ 436 "SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", \ 437 name, type_)) 438 if (not php.isset(drupal_get_filename.files, type_)): 439 drupal_get_filename.files[type_] = {} 440 if (filename is not None and php.file_exists(filename)): 441 drupal_get_filename.files[type_][name] = filename; 442 elif (php.isset(drupal_get_filename.files[type_], name)): 443 # nothing 444 pass; 445 # Verify that we have an active database connection, before querying 446 # the database. This is required because this def is called both 447 # before we have a database connection (i.e. during installation) and 448 # when a database connection fails. 449 elif (lib_database.is_active() and (file and php.file_exists(file))): 450 drupal_get_filename.files[type_][name] = file; 451 else: 452 # Fallback to searching the filesystem if the database connection is 453 # not established or the requested file is not found. 454 config = conf_path(); 455 if type_ == 'theme_engine': 456 dir_ = 'themes/engines' 457 file = "%s.engine" % name 458 else: 459 dir_ = '%ss' % type_ 460 file = "__init__.py" 461 fileVals = {'name':name, 'file':file, 'dir':dir_, 'config':config}; 462 fileChecker = ( 463 # DRUPY: This is not used 464 # "%(config)s/%(dir)s/%(file)s" % fileVals, 465 "%(config)s/%(dir)s/%(name)s/%(file)s" % fileVals, 466 # DRUPY: This is not used 467 #"%(dir)s/%(file)s" % fileVals, 468 "%(dir)s/%(name)s/%(file)s" % fileVals 469 ) 470 for file_ in fileChecker: 471 if (php.file_exists(file_)): 472 drupal_get_filename.files[type_][name] = file_; 473 break; 474 if (php.isset(drupal_get_filename.files[type_], name)): 475 return drupal_get_filename.files[type_][name];
476 477 478
479 -def variable_init(conf_ = {}):
480 """ 481 Load the persistent variable table. 482 483 The variable table is composed of values that have been saved in the table 484 with variable_set() as well as those explicitly specified 485 in the configuration file. 486 """ 487 # NOTE: caching the variables improves performance by 20% when serving 488 # cached pages. 489 cached = lib_cache.get('variables', 'cache'); 490 if (cached): 491 variables = cached.data; 492 else: 493 variables = {} 494 result = lib_database.query('SELECT * FROM {variable}'); 495 while True: 496 variable = lib_database.fetch_object(result); 497 if (not variable): 498 break; 499 variables[variable.name] = php.unserialize(variable.value); 500 lib_cache.set('variables', variables); 501 for name,value in conf_.items(): 502 variables[name] = value; 503 return variables;
504 505 506
507 -def variable_get(name, default_):
508 """ 509 Return a persistent variable. 510 511 @param name 512 The name of the variable to return. 513 @param default 514 The default value to use if this variable has never been set. 515 @return 516 The value of the variable. 517 """ 518 return (settings.conf[name] if php.isset(settings.conf, name) else default_);
519 520
521 -def variable_set(name, value):
522 """ 523 Set a persistent variable. 524 525 @param name 526 The name of the variable to set. 527 @param value 528 The value to set. This can be any PHP data type; these functions take care 529 of serialization as necessary. 530 """ 531 serialized_value = php.serialize(value); 532 db_query("UPDATE {variable} SET value = '%s' WHERE name = '%s'", \ 533 serialized_value, name); 534 if (db_affected_rows() == 0): 535 db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", \ 536 name, serialized_value); 537 cache_clear_all('variables', 'cache'); 538 settings.conf[name] = value;
539 540
541 -def variable_del(name):
542 """ 543 Unset a persistent variable. 544 545 @param name 546 The name of the variable to undefine. 547 """ 548 db_query("DELETE FROM {variable} WHERE name = '%s'", name); 549 cache_clear_all('variables', 'cache'); 550 del(settings.conf[name]);
551 552 553
554 -def page_get_cache():
555 """ 556 Retrieve the current page from the cache. 557 558 Note: we do not serve cached pages when status messages are waiting (from 559 a redirected form submission which was completed). 560 561 @param status_only 562 When set to TRUE, retrieve the status of the page cache only 563 (whether it was started in this request or not). 564 """ 565 cache = None; 566 if (not lib_appglobals.user.uid and \ 567 (php.SERVER['REQUEST_METHOD'] == 'GET' or \ 568 php.SERVER['REQUEST_METHOD'] == 'HEAD') and \ 569 php.count(drupal_set_message()) == 0): 570 cache = lib_cache.get(lib_appglobals.base_root + request_uri(), \ 571 'cache_page'); 572 if (php.empty(cache)): 573 ob_start() 574 return cache;
575 576 577
578 -def invoke_all(hook):
579 """ 580 Call all init or exit hooks without including all plugins. 581 582 @param hook 583 The name of the bootstrap hook we wish to invoke. 584 """ 585 for plugin_ in lib_plugin.list_(True, True): 586 lib_plugin.invoke(plugin_, hook);
587 588
589 -def drupal_load(type_, name):
590 """ 591 Includes a file with the provided type and name. This prevents 592 including a theme, engine, plugin, etc., more than once. 593 594 @param type 595 The type of item to load (i.e. theme, theme_engine, plugin). 596 @param name 597 The name of the item to load. 598 599 @return 600 TRUE if the item is loaded or has already been loaded. 601 """ 602 php.static(drupal_load, 'files', {}) 603 if (not php.isset(drupal_load.files, type)): 604 drupal_load.files[type_] = {} 605 if (php.isset(drupal_load.files[type_], name)): 606 return True 607 else: 608 filename = drupal_get_filename(type_, name); 609 if (filename != False): 610 lib_plugin.plugins[name] = DrupyImport.import_file(filename) 611 drupal_load.files[type_][name] = True; 612 return True; 613 else: 614 return False;
615 616
617 -def drupal_page_header():
618 """ 619 Set HTTP headers in preparation for a page response. 620 621 Authenticated users are always given a 'no-cache' php.header, and will 622 fetch a fresh page on every request. This prevents authenticated 623 users seeing locally cached pages that show them as logged out. 624 625 @see page_set_cache() 626 """ 627 php.header("Expires: Sun, 19 Nov 1978 05:00:00 GMT"); 628 php.header("Last-Modified: " + php.gmdate("%D, %d %M %Y %H:%i:%s") + " GMT"); 629 php.header("Cache-Control: store, no-cache, must-revalidate"); 630 php.header("Cache-Control: post-check=0, pre-check=0", False);
631 632 633
634 -def drupal_page_cache_header(cache):
635 """ 636 Set HTTP headers in preparation for a cached page response. 637 638 The general approach here is that anonymous users can keep a local 639 cache of the page, but must revalidate it on every request. Then, 640 they are given a '304 Not Modified' response as long as they stay 641 logged out and the page has not been modified. 642 """ 643 # Set default values: 644 last_modified = php.gmdate('D, d M Y H:i:s', cache.created) + ' GMT'; 645 etag = '"' + drupy_md5(last_modified) + '"'; 646 # See if the client has provided the required HTTP headers: 647 if_modified_since = (php.stripslashes(php.SERVER['HTTP_IF_MODIFIED_SINCE']) \ 648 if php.isset(php.SERVER, 'HTTP_IF_MODIFIED_SINCE') else False); 649 if_none_match = (php.stripslashes(php.SERVER['HTTP_IF_NONE_MATCH']) \ 650 if php.isset(php.SERVER, 'HTTP_IF_NONE_MATCH') else False); 651 if (if_modified_since and if_none_match 652 and if_none_match == etag # etag must match 653 and if_modified_since == last_modified): # if-modified-since must match 654 php.header('HTTP/1.1 304 Not Modified'); 655 # All 304 responses must send an etag if the 200 response for the same 656 # object contained an etag 657 php.header("Etag: %(etag)s" % {'etag':etag}); 658 exit(); 659 # Send appropriate response: 660 php.header("Last-Modified: %(last_modified)s" % \ 661 {'last_modified':last_modified}); 662 php.header("Etag: %(etag)s" % {'etag':etag}); 663 # The following headers force validation of cache: 664 php.header("Expires: Sun, 19 Nov 1978 05:00:00 GMT"); 665 php.header("Cache-Control: must-revalidate"); 666 if (variable_get('page_compression', True)): 667 # Determine if the browser accepts gzipped data. 668 if (php.strpos(php.SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') == False and \ 669 php.function_exists('gzencode')): 670 # Strip the gzip php.header and run uncompress. 671 cache.data = php.gzinflate(php.substr(php.substr(cache.data, 10), 0, -8)); 672 elif (php.function_exists('gzencode')): 673 php.header('Content-Encoding: gzip'); 674 # Send the original request's headers. We send them one after 675 # another so PHP's php.header() def can deal with duplicate 676 # headers. 677 headers = php.explode("\n", cache.headers); 678 for php.header_ in headers: 679 php.header(php.header_); 680 print cache.data;
681 682
683 -def hooks():
684 """ 685 Define the critical hooks that force plugins to always be loaded. 686 """ 687 return ['boot', 'exit'];
688 689
690 -def drupal_unpack(obj, field = 'data'):
691 """ 692 Unserializes and appends elements from a serialized string. 693 694 @param obj 695 The object to which the elements are appended. 696 @param field 697 The attribute of obj whose value should be unserialized. 698 """ 699 if hasattr(obj, field) and not php.empty(getattr(obj, field)): 700 data = php.unserialize(getattr(obj, field)) 701 else: 702 data = None 703 if (hasattr(obj, field) and not php.empty(data)): 704 for key,value in data.items(): 705 if (not php.isset(obj, key)): 706 setattr(obj, key, value) 707 return obj;
708 709
710 -def referer_uri():
711 """ 712 Return the URI of the referring page. 713 """ 714 if (php.isset(php.SERVER, 'HTTP_REFERER')): 715 return php.SERVER['HTTP_REFERER'];
716 717
718 -def check_plain(text):
719 """ 720 Encode special characters in a plain-text string for display as HTML. 721 722 Uses drupal_validate_utf8 to prevent cross site scripting attacks on 723 Internet Explorer 6. 724 """ 725 return (php.htmlspecialchars(text, php.ENT_QUOTES) if \ 726 drupal_validate_utf8(text) else '');
727 728
729 -def drupal_validate_utf8(text):
730 """ 731 Checks whether a string is valid UTF-8. 732 733 All functions designed to filter input should use drupal_validate_utf8 734 to ensure they operate on valid UTF-8 strings to prevent bypass of the 735 filter. 736 737 When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented 738 as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent 739 bytes. When these subsequent bytes are HTML control characters such as 740 quotes or angle brackets, parts of the text that were deemed safe by filters 741 end up in locations that are potentially unsafe; An onerror attribute that 742 is outside of a tag, and thus deemed safe by a filter, can be interpreted 743 by the browser as if it were inside the tag. 744 745 This def exploits preg_match behaviour (since PHP 4.3.5) when used 746 with the u modifier, as a fast way to find invalid UTF-8. When the matched 747 string contains an invalid byte sequence, it will fail silently. 748 749 preg_match may not fail on 4 and 5 octet sequences, even though they 750 are not supported by the specification. 751 752 The specific preg_match behaviour is present since PHP 4.3.5. 753 754 @param text 755 The text to check. 756 @return 757 TRUE if the text is valid UTF-8, FALSE if not. 758 """ 759 if (php.strlen(text) == 0): 760 return True; 761 return (php.preg_match('/^./us', text) == 1);
762 763
764 -def request_uri():
765 """ 766 Since php.SERVER['php.REQUEST_URI'] is only available on Apache, we 767 generate an equivalent using other environment variables. 768 """ 769 if (php.isset(php.SERVER, 'REQUEST_URI')): 770 uri = php.SERVER['REQUEST_URI']; 771 else: 772 if (php.isset(php.SERVER, 'argv')): 773 uri = php.SERVER['SCRIPT_NAME'] + '?' + php.SERVER['argv'][0]; 774 elif (php.isset(php.SERVER, 'QUERY_STRING')): 775 uri = php.SERVER['SCRIPT_NAME'] + '?' + php.SERVER['QUERY_STRING']; 776 else: 777 uri = php.SERVER['SCRIPT_NAME']; 778 return uri;
779 780 781
782 -def watchdog(type, message, variables = [], severity = WATCHDOG_NOTICE, \ 783 link = None):
784 """ 785 Log a system message. 786 787 @param type 788 The category to which this message belongs. 789 @param message 790 The message to store in the log. See t() for documentation 791 on how message and variables interact. Keep message 792 translatable by not concatenating dynamic values into it! 793 @param variables 794 Array of variables to replace in the message on display or 795 NULL if message is already translated or not possible to 796 translate. 797 @param severity 798 The severity of the message, as per RFC 3164 799 @param link 800 A link to associate with the message. 801 802 @see watchdog_severity_levels() 803 """ 804 # Prepare the fields to be logged 805 log_message = { 806 'type' : type, 807 'message' : message, 808 'variables' : variables, 809 'severity' : severity, 810 'link' : link, 811 'user' : lib_appglobals.user, 812 'request_uri' : lib_appglobals.base_root + request_uri(), 813 'referer' : referer_uri(), 814 'ip' : ip_address(), 815 'timestamp' : php.time_(), 816 } 817 # Call the logging hooks to log/process the message 818 for plugin_ in lib_plugin.implements('watchdog', True): 819 lib_plugin.invoke(plugin_, 'watchdog', log_message);
820 821
822 -def drupal_set_message(message = None, type = 'status', repeat = True):
823 """ 824 Set a message which reflects the status of the performed operation. 825 826 If the def is called with no arguments, this def returns all set 827 messages without clearing them. 828 829 @param message 830 The message should begin with a capital letter and always ends with a 831 period '.'. 832 @param type 833 The type of the message. One of the following values are possible: 834 - 'status' 835 - 'warning' 836 - 'error' 837 @param repeat 838 If this is FALSE and the message is already set, then the message won't 839 be repeated. 840 """ 841 if (message): 842 if (not php.isset(php.SESSION, 'messages')): 843 php.SESSION['messages'] = {}; 844 if (not php.isset(php.SESSION['messages'], type)): 845 php.SESSION['messages'][type] = []; 846 if (repeat or not php.in_array(message, php.SESSION['messages'][type])): 847 php.SESSION['messages'][type].append( message ); 848 # messages not set when DB connection fails 849 return (php.SESSION['messages'] if php.isset(php.SESSION, 'messages') else \ 850 None);
851 852
853 -def drupal_get_messages(type = None, clear_queue = True):
854 """ 855 Return all messages that have been set. 856 857 @param type 858 (optional) Only return messages of this type. 859 @param clear_queue 860 (optional) Set to FALSE if you do not want to clear the messages queue 861 @return 862 An associative array, the key is the message type, the value an array 863 of messages. If the type parameter is passed, you get only that type, 864 or an empty array if there are no such messages. If type is not passed, 865 all message types are returned, or an empty array if none exist. 866 """ 867 messages = drupal_set_message(); 868 if (not php.empty('messages')): 869 if (type != None and type != False): 870 if (clear_queue): 871 del(php.SESSION['messages'][type]); 872 if (php.isset(messages, type)): 873 return {type : messages[type]}; 874 else: 875 if (clear_queue): 876 del(php.SESSION['messages']); 877 return messages; 878 return {};
879 880
881 -def drupal_is_denied(ip):
882 """ 883 Check to see if an IP address has been blocked. 884 885 Blocked IP addresses are stored in the database by default. However for 886 performance reasons we allow an override in settings.php. This allows us 887 to avoid querying the database at this critical stage of the bootstrap if 888 an administrative interface for IP address blocking is not required. 889 890 @param $ip string 891 IP address to check. 892 @return bool 893 TRUE if access is denied, FALSE if access is allowed. 894 """ 895 # Because this function is called on every page request, we first check 896 # for an array of IP addresses in settings.php before querying the 897 # database. 898 blocked_ips = variable_get('blocked_ips', None); 899 if (blocked_ips != None and php.is_array(blocked_ips)): 900 return php.in_array(ip, blocked_ips) 901 else: 902 sql = "SELECT 1 FROM {blocked_ips} WHERE ip = '%s'"; 903 return (lib_database.result(lib_database.query(sql, ip)) != False)
904 905
906 -def drupal_anonymous_user(session = ''):
907 """ 908 Generates a default anonymous user object. 909 910 @return Object - the user object. 911 """ 912 user = php.stdClass(); 913 user.uid = 0; 914 user.hostname = ip_address(); 915 user.roles = {}; 916 user.roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; 917 user.session = session; 918 user.cache = 0; 919 return user;
920 921 922
923 -def drupal_bootstrap(phase):
924 """ 925 A string describing a phase of Drupal to load. Each phase adds to the 926 previous one, so invoking a later phase automatically runs the earlier 927 phases too. The most important usage is that if you want to access the 928 Drupal database from a script without loading anything else, you can 929 include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE). 930 931 @param phase 932 A constant. Allowed values are: 933 DRUPAL_BOOTSTRAP_CONFIGURATION: initialize configuration. 934 DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: try to call a non-database cache 935 fetch routine. 936 DRUPAL_BOOTSTRAP_DATABASE: initialize database layer. 937 DRUPAL_BOOTSTRAP_ACCESS: identify and reject banned hosts. 938 DRUPAL_BOOTSTRAP_SESSION: initialize session handling. 939 DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: load bootstrap.inc and plugin.inc, 940 start the variable system and try to serve a page from the cache. 941 DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page. 942 DRUPAL_BOOTSTRAP_PATH: set php.GET['q'] to Drupal path of request. 943 DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix 944 input data. 945 """ 946 # DRUPY: Before doing anything else, set the q request var if it doesnt 947 # exist 948 if 'q' not in php.GET: 949 php.GET['q'] = 'node' 950 # DRUPY(BC): Why were these set as static vars? 951 # No longer needed 952 phase_index = 0; 953 phases = range(DRUPAL_BOOTSTRAP_CONFIGURATION, DRUPAL_BOOTSTRAP_FULL+1); 954 while (phase >= phase_index and php.isset(phases, phase_index)): 955 current_phase = phases[phase_index]; 956 #Drupal was unsetting the phase var here. 957 #This was completely unnecessary and most likely the cause of some bugs 958 phase_index += 1; 959 _drupal_bootstrap(current_phase);
960 961 962
963 -def _drupal_bootstrap(phase):
964 if phase == DRUPAL_BOOTSTRAP_CONFIGURATION: 965 # Start a page timer: 966 timer_start('page'); 967 # Initialize the configuration 968 conf_init(); 969 elif phase == DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: 970 # Allow specifying special cache handlers in settings.php, like 971 # using memcached or files for storing cache information. 972 # If the page_cache_fastpath is set to TRUE in settings.php and 973 # page_cache_fastpath (implemented in the special implementation of 974 # cache.inc) printed the page and indicated this with a returned TRUE 975 # then we are done. 976 if (variable_get('page_cache_fastpath', False) and page_cache_fastpath()): 977 exit(); 978 elif phase == DRUPAL_BOOTSTRAP_DATABASE: 979 # Initialize the default database. 980 lib_database.set_active(); 981 # Register autoload functions so that we can access classes and interfaces. 982 # spl_autoload_register('drupal_autoload_class') 983 # spl_autoload_register('drupal_autoload_interface') 984 elif phase == DRUPAL_BOOTSTRAP_ACCESS: 985 # Deny access to blocked IP addresses - t() is not yet available 986 if (drupal_is_denied(ip_address())): 987 php.header('HTTP/1.1 403 Forbidden'); 988 print 'Sorry, ' + check_plain(ip_address()) + ' has been banned.'; 989 exit() 990 elif phase == DRUPAL_BOOTSTRAP_SESSION: 991 php.session_set_save_handler('sess_open', 'sess_close', 'sess_read', \ 992 'sess_write', 'sess_destroy_sid', 'sess_gc'); 993 php.session_start(); 994 elif phase == DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: 995 # Initialize configuration variables, using values from settings.php 996 # if available. 997 settings.conf = variable_init( ({} if (settings.conf == None) else \ 998 settings.conf) ); 999 # Load plugin handling. 1000 cache_mode = variable_get('cache', CACHE_DISABLED); 1001 # Get the page from the cache. 1002 cache = ('' if (cache_mode == CACHE_DISABLED) else page_get_cache()); 1003 # If the skipping of the bootstrap hooks is not enforced, call hook_boot. 1004 if (cache_mode != CACHE_AGGRESSIVE): 1005 invoke_all('boot'); 1006 # If there is a cached page, display it. 1007 if (cache): 1008 drupal_page_cache_header(cache); 1009 # If the skipping of the bootstrap hooks is not enforced, call hook_exit. 1010 if (cache_mode != CACHE_AGGRESSIVE): 1011 bootstrap_invoke_all('exit'); 1012 # We are done. 1013 exit(); 1014 # Prepare for non-cached page workflow. 1015 drupal_page_header(); 1016 elif phase == DRUPAL_BOOTSTRAP_LANGUAGE: 1017 drupal_init_language(); 1018 elif phase == DRUPAL_BOOTSTRAP_PATH: 1019 # Initialize php.GET['q'] prior to loading plugins and invoking hook_init(). 1020 #lib_path.drupal_init_path(); 1021 pass 1022 elif phase == DRUPAL_BOOTSTRAP_FULL: 1023 lib_common._drupal_bootstrap_full();
1024 1025
1026 -def drupal_maintenance_theme():
1027 """ 1028 Enables use of the theme system without requiring database access. 1029 1030 Loads and initializes the theme system for site installs, updates and when 1031 the site is in offline mode. This also applies when the database fails. 1032 1033 @see _drupal_maintenance_theme() 1034 """ 1035 lib_theme_maintenance._drupal_maintenance_theme();
1036 1037
1038 -def get_t():
1039 """ 1040 Return the name of the localisation function. Use in code that needs to 1041 run both during installation and normal operation. 1042 """ 1043 php.static(get_t, 't') 1044 if (get_t.t == None): 1045 get_t.t = ('st' if php.function_exists('install_main') else 't'); 1046 return get_t.t;
1047 1048 1049
1050 -def drupal_init_language():
1051 """ 1052 Choose a language for the current page, based on site and user preferences. 1053 """ 1054 # Ensure the language is correctly returned, even without 1055 # multilanguage support. 1056 # Useful for eg. XML/HTML 'lang' attributes. 1057 if (variable_get('language_count', 1) == 1): 1058 lib_appglobals.language = language_default(); 1059 else: 1060 lib_appglobals.language = lib_language.initialize();
1061 1062
1063 -def language_list(field = 'language', reset = False):
1064 """ 1065 Get a list of languages set up indexed by the specified key 1066 1067 @param field The field to index the list with. 1068 @param reset Boolean to request a reset of the list. 1069 """ 1070 php.static(language_list, 'languages') 1071 # Reset language list 1072 if (reset): 1073 languages_list.languages = {}; 1074 # Init language list 1075 if (languages_list.languages == None): 1076 if (variable_get('language_count', 1) > 1 or plugin_exists('locale')): 1077 result = db_query(\ 1078 'SELECT# FROM {languages} ORDER BY weight ASC, name ASC'); 1079 while True: 1080 row = db_fetch_object(result); 1081 if row == None: 1082 break; 1083 languages_list.languages['language'][row.language] = row; 1084 else: 1085 # No locale plugin, so use the default language only. 1086 default_ = language_default(); 1087 languages_list.languages['language'][default_.language] = default_; 1088 # Return the array indexed by the right field 1089 if (not php.isset(languages_list.languages, field)): 1090 languages_list.languages[field] = {}; 1091 for lang in languages_list.languages['language']: 1092 # Some values should be collected into an array 1093 if (php.in_array(field, ['enabled', 'weight'])): 1094 languages_list.languages[field][lang.field][lang.language] = lang; 1095 else: 1096 languages_list.languages[field][lang.field] = lang; 1097 return languages_list.languages[field];
1098 1099 1100
1101 -def language_default(property = None):
1102 """ 1103 Default language used on the site 1104 1105 @param property 1106 Optional property of the language object to return 1107 """ 1108 language_local = variable_get('language_default', php.object_({ 1109 'language' : 'en', 1110 'name' : 'English', 1111 'native' : 'English', 1112 'direction' : 0, 1113 'enabled' : 1, 1114 'plurals' : 0, 1115 'formula' : '', 1116 'domain' : '', 1117 'prefix' : '', 1118 'weight' : 0, 1119 'javascript' : '' 1120 })); 1121 return (getattr(language_local, property) if (property != None) else \ 1122 language_local);
1123 1124
1125 -def ip_address(reset=False):
1126 """ 1127 If Drupal is behind a reverse proxy, we use the X-Forwarded-For header 1128 instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of 1129 the proxy server, and not the client's. If Drupal is run in a cluster 1130 we use the X-Cluster-Client-Ip header instead. 1131 1132 @param $reset 1133 Reset the current IP address saved in static. 1134 @return 1135 IP address of client machine, adjusted for reverse proxy and/or cluster 1136 environments. 1137 """ 1138 php.static(ip_address, 'ip_address') 1139 if (ip_address.ip_address is None or reset): 1140 ip_address.ip_address = php.SERVER['REMOTE_ADDR']; 1141 if (variable_get('reverse_proxy', 0)): 1142 if (php.array_key_exists('HTTP_X_FORWARDED_FOR', php.SERVER)): 1143 # If an array of known reverse proxy IPs is provided, then trust 1144 # the XFF header if request really comes from one of them. 1145 reverse_proxy_addresses = variable_get('reverse_proxy_addresses', \ 1146 tuple()); 1147 if (not php.empty(reverse_proxy_addresses) and \ 1148 php.in_array(ip_address.ip_address, reverse_proxy_addresses, \ 1149 True)): 1150 # If there are several arguments, we need to check the most 1151 # recently added one, i.e. the last one. 1152 ip_address.ip_address = php.array_pop(\ 1153 php.explode(',', php.SERVER['HTTP_X_FORWARDED_FOR'])); 1154 # When Drupal is run in a cluster environment, 1155 # REMOTE_ADDR contains the IP 1156 # address of a server in the cluster, while the IP address 1157 # of the client is 1158 # stored in HTTP_X_CLUSTER_CLIENT_IP. 1159 if (php.array_key_exists('HTTP_X_CLUSTER_CLIENT_IP', php.SERVER)): 1160 ip_address.ip_address = php.SERVER['HTTP_X_CLUSTER_CLIENT_IP']; 1161 return ip_address.ip_address;
1162 1163 1164
1165 -def drupal_function_exists(function, scope=None):
1166 """ 1167 Confirm that a function is available. 1168 1169 If the function is already available, this function does nothing. 1170 If the function is not available, it tries to load the file where the 1171 function lives. If the file is not available, it returns False, so that it 1172 can be used as a drop-in replacement for php.function_exists(). 1173 1174 DRUPY(BC): This function needs to be heavily modified 1175 1176 @param function 1177 The name of the function to check or load. 1178 @param scope 1179 Scope to check 1180 @return 1181 True if the function is now available, False otherwise. 1182 """ 1183 # We arent using the registry, so lets just return a simple function_exists 1184 return php.function_exists(function, scope)
1185 1186 1187
1188 -def drupal_autoload_interface(interface):
1189 """ 1190 Confirm that an interface is available. 1191 1192 This function parallels drupal_function_exists(), but is rarely 1193 called directly. Instead, it is registered as an spl_autoload() 1194 handler, and PHP calls it for us when necessary. 1195 1196 @param interface 1197 The name of the interface to check or load. 1198 @return 1199 True if the interface is currently available, False otherwise. 1200 """ 1201 return _registry_check_code('interface', interface)
1202 1203
1204 -def drupal_autoload_class(class_):
1205 """ 1206 Confirm that a class is available. 1207 1208 This function parallels drupal_function_exists(), but is rarely 1209 called directly. Instead, it is registered as an spl_autoload() 1210 handler, and PHP calls it for us when necessary. 1211 1212 @param class 1213 The name of the class to check or load. 1214 @return 1215 True if the class is currently available, False otherwise. 1216 """ 1217 return _registry_check_code('class', class_)
1218 1219
1220 -def _registry_check_code(type_, name):
1221 """ 1222 Helper for registry_check_{interface, class}. 1223 """ 1224 file = db_result(db_query(\ 1225 "SELECT filename FROM {registry} WHERE name = '%s' AND type = '%s'", \ 1226 name, type_)) 1227 if (file): 1228 php.require_once(file) 1229 registry_mark_code(type_, name) 1230 return True
1231 1232
1233 -def registry_mark_code(type_, name, return_ = False):
1234 """ 1235 Collect the resources used for this request. 1236 1237 @param type 1238 The type of resource. 1239 @param name 1240 The name of the resource. 1241 @param return 1242 Boolean flag to indicate whether to return the resources. 1243 """ 1244 php.static(registry_mark_code, 'resources', []) 1245 if (type_ and name): 1246 if (not php.isset(registry_mark_code.resources, type_, )): 1247 registry_mark_code.resources[type_] = [] 1248 if (not php.in_array(name, registry_mark_code.resources[type_])): 1249 registry_mark_code.resources[type].append( name ) 1250 if (return_): 1251 return registry_mark_code.resources
1252 1253 1254
1255 -def registry_rebuild():
1256 """ 1257 Rescan all enabled plugins and rebuild the registry. 1258 1259 Rescans all code in plugins or includes directory, storing a mapping of 1260 each function, file, and hook implementation in the database. 1261 """ 1262 _registry_rebuild()
1263 1264 1265
1266 -def registry_cache_hook_implementations(hook, \ 1267 write_to_persistent_cache = False):
1268 """ 1269 Save hook implementations cache. 1270 1271 @param hook 1272 Array with the hook name and list of plugins that implement it. 1273 @param write_to_persistent_cache 1274 Whether to write to the persistent cache. 1275 """ 1276 php.static(registry_cache_hook_implementations, 'implementations', {}) 1277 if (hook): 1278 # Newer is always better, so overwrite anything that's come before. 1279 registry_cache_hook_implementations.implementations[hook['hook']] = \ 1280 hook['plugins'] 1281 if (write_to_persistent_cache == True): 1282 # Only write this to cache if the implementations data we are going to cache 1283 # is different to what we loaded earlier in the request. 1284 if (registry_cache_hook_implementations.implementations != \ 1285 registry_get_hook_implementations_cache()): 1286 cache_set('hooks', implementations, 'cache_registry');
1287 1288
1289 -def registry_cache_path_files():
1290 """ 1291 Save the files required by the registry for this path. 1292 """ 1293 used_code = registry_mark_code(None, None, True) 1294 if (used_code): 1295 files = [] 1296 type_sql = [] 1297 params = [] 1298 for type,names in used_code.items(): 1299 type_sql.append( "(name IN (" + db_placeholders(names, 'varchar') + \ 1300 ") AND type = '%s')" ) 1301 params = php.array_merge(params, names) 1302 params.append( type ) 1303 res = db_query("SELECT DISTINCT filename FROM {registry} WHERE " + \ 1304 php.implode(' OR ', type_sql), params) 1305 while True: 1306 row = db_fetch_object(res) 1307 if (row == None): 1308 break 1309 files.append( row.filename ) 1310 if (files): 1311 sort(files); 1312 # Only write this to cache if the file list we are going to cache 1313 # is different to what we loaded earlier in the request. 1314 if (files != registry_load_path_files(True)): 1315 menu = menu_get_item(); 1316 cache_set('registry:' + menu['path'], php.implode(';', files), \ 1317 'cache_registry');
1318 1319
1320 -def registry_load_path_files(return_ = False):
1321 """ 1322 registry_load_path_files 1323 """ 1324 php.static(registry_load_path_files, 'file_cache_data', []) 1325 if (return_): 1326 sort(registry_load_path_files.file_cache_data); 1327 return registry_load_path_files.file_cache_data; 1328 menu = menu_get_item(); 1329 cache = cache_get('registry:' + menu['path'], 'cache_registry'); 1330 if (not php.empty(cache.data)): 1331 for file in php.explode(';', cache.data): 1332 php.require_once(file); 1333 registry_load_path_files.file_cache_data.append( file );
1334 1335
1336 -def registry_get_hook_implementations_cache():
1337 """ 1338 registry_get_hook_implementations_cache 1339 """ 1340 php.static(registry_get_hook_implementations_cache, 'implementations') 1341 if (registry_get_hook_implementations_cache.implementations == None): 1342 cache = lib_cache.get('hooks', 'cache_registry') 1343 if (cache): 1344 registry_get_hook_implementations_cache.implementations = cache.data; 1345 else: 1346 registry_get_hook_implementations_cache.implementations = {}; 1347 return registry_get_hook_implementations_cache.implementations;
1348