/php/mt.php

http://github.com/movabletype/movabletype · PHP · 1064 lines · 859 code · 111 blank · 94 comment · 194 complexity · 82f1465a29458b10aaba1e3dcc0e7633 MD5 · raw file

  1. <?php
  2. # Movable Type (r) (C) 2004-2019 Six Apart Ltd. All Rights Reserved.
  3. # This code cannot be redistributed without permission from www.sixapart.com.
  4. # For more information, consult your Movable Type license.
  5. #
  6. # $Id$
  7. /***
  8. * Loading exception classes
  9. */
  10. require_once('lib/class.exception.php');
  11. define('VERSION', '7.2');
  12. define('PRODUCT_VERSION', '7.2');
  13. define('DATA_API_DEFAULT_VERSION', '4');
  14. $PRODUCT_NAME = '__PRODUCT_NAME__';
  15. if($PRODUCT_NAME == '__PRODUCT' . '_NAME__')
  16. $PRODUCT_NAME = 'Movable Type';
  17. define('PRODUCT_NAME', $PRODUCT_NAME);
  18. $RELEASE_NUMBER = '__RELEASE_NUMBER__';
  19. if ( $RELEASE_NUMBER == '__RELEASE_' . 'NUMBER__' )
  20. $RELEASE_NUMBER = 0;
  21. define('RELEASE_NUMBER', $RELEASE_NUMBER);
  22. $PRODUCT_VERSION_ID = '__PRODUCT_VERSION_ID__';
  23. if ( $PRODUCT_VERSION_ID == '__PRODUCT_' . 'VERSION_ID__' )
  24. $PRODUCT_VERSION_ID = PRODUCT_VERSION;
  25. $VERSION_STRING;
  26. if ( $RELEASE_NUMBER > 0 )
  27. $VERSION_STRING = $PRODUCT_VERSION_ID . "." . $RELEASE_NUMBER;
  28. else
  29. $VERSION_STRING = $PRODUCT_VERSION_ID;
  30. define('VERSION_ID', $PRODUCT_VERSION_ID);
  31. global $Lexicon;
  32. $Lexicon = array();
  33. class MT {
  34. protected $mime_types = array(
  35. '__default__' => 'text/html',
  36. 'css' => 'text/css',
  37. 'txt' => 'text/plain',
  38. 'rdf' => 'text/xml',
  39. 'rss' => 'text/xml',
  40. 'xml' => 'text/xml',
  41. );
  42. protected $blog_id;
  43. protected $db;
  44. protected $config;
  45. protected $debugging = false;
  46. protected $caching = false;
  47. protected $conditional = false;
  48. protected $log = array();
  49. protected $warning = array();
  50. protected $id;
  51. protected $request;
  52. protected $http_error;
  53. protected $cfg_file;
  54. private $cache_driver = null;
  55. private static $_instance = null;
  56. static public $config_type_array = array('pluginpath', 'alttemplate', 'outboundtrackbackdomains', 'memcachedservers', 'userpasswordvalidation');
  57. static public $config_type_hash = array('pluginswitch', 'pluginalias', 'pluginschemaversion', 'commenterregistration');
  58. /***
  59. * Constructor for MT class.
  60. * Currently, constructor moved to private method because this class implemented Singleton Design Pattern.
  61. * You can get instance as following code.
  62. *
  63. * $mt = MT::get_instance();
  64. */
  65. private function __construct($blog_id = null, $cfg_file = null) {
  66. error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
  67. try {
  68. $this->id = md5(uniqid('MT',true));
  69. $this->init($blog_id, $cfg_file);
  70. } catch (Exception $e ) {
  71. throw new MTInitException( $e, $this->debugging );
  72. }
  73. }
  74. public function __destruct() {
  75. if ( isset($this->db) ) {
  76. $this->db()->db()->Close();
  77. $this->db = null;
  78. }
  79. }
  80. public static function get_instance($blog_id = null, $cfg_file = null) {
  81. if (is_null(MT::$_instance)) {
  82. MT::$_instance = new MT($blog_id, $cfg_file);
  83. }
  84. return MT::$_instance;
  85. }
  86. public function caching($val = null) {
  87. if ( !is_null($val) ) {
  88. $this->caching = $val;
  89. }
  90. return $this->caching;
  91. }
  92. public function conditional($val = null) {
  93. if ( !is_null($val) ) {
  94. $this->conditional = $val;
  95. }
  96. return $this->conditional;
  97. }
  98. public function blog_id() {
  99. return $this->blog_id;
  100. }
  101. function init($blog_id = null, $cfg_file = null) {
  102. if (isset($blog_id)) {
  103. $this->blog_id = $blog_id;
  104. }
  105. if (!file_exists($cfg_file)) {
  106. $mtdir = dirname(dirname(__FILE__));
  107. $cfg_file = $mtdir . DIRECTORY_SEPARATOR . "mt-config.cgi";
  108. }
  109. $this->configure($cfg_file);
  110. $this->init_addons();
  111. $this->configure_from_db();
  112. if (isset($blog_id)) {
  113. $db =& $this->db();
  114. $blog = $db->fetch_blog($this->blog_id);
  115. if ($blog) {
  116. $ctx =& $this->context();
  117. $ctx->stash('blog', $blog);
  118. $ctx->stash('blog_id',$this->blog_id);
  119. $ctx->stash('local_blog_id',$this->blog_id);
  120. }
  121. $lang = substr(strtolower(
  122. $blog && $blog->blog_language
  123. ? $blog->blog_language
  124. : $mt->config('DefaultLanguage')
  125. ), 0, 2);
  126. }
  127. else {
  128. $lang = substr(strtolower($this->config('DefaultLanguage')), 0, 2);
  129. }
  130. if (!@include_once("l10n_$lang.php"))
  131. include_once("l10n_en.php");
  132. if (extension_loaded('mbstring')) {
  133. $charset = $this->config('PublishCharset');
  134. mb_internal_encoding($charset);
  135. mb_http_output($charset);
  136. }
  137. }
  138. function init_addons() {
  139. $mtdir = dirname(dirname(__FILE__));
  140. $path = $mtdir . DIRECTORY_SEPARATOR . "addons";
  141. if (is_dir($path)) {
  142. $ctx =& $this->context();
  143. if ($dh = opendir($path)) {
  144. while (($file = readdir($dh)) !== false) {
  145. if ($file == "." || $file == "..") {
  146. continue;
  147. }
  148. $plugin_dir = $path . DIRECTORY_SEPARATOR . $file
  149. . DIRECTORY_SEPARATOR . 'php';
  150. if (is_dir($plugin_dir))
  151. $ctx->add_plugin_dir($plugin_dir);
  152. }
  153. closedir($dh);
  154. }
  155. }
  156. }
  157. function init_plugins() {
  158. $plugin_paths = $this->config('PluginPath');
  159. $ctx =& $this->context();
  160. foreach ($plugin_paths as $path) {
  161. if ( !is_dir($path) )
  162. $path = $this->config('MTDir') . DIRECTORY_SEPARATOR . $path;
  163. if ($dh = @opendir($path)) {
  164. while (($file = readdir($dh)) !== false) {
  165. if ($file == "." || $file == "..")
  166. continue;
  167. $plugin_dir = $path . DIRECTORY_SEPARATOR . $file
  168. . DIRECTORY_SEPARATOR . 'php';
  169. if (is_dir($plugin_dir))
  170. $ctx->add_plugin_dir($plugin_dir);
  171. }
  172. closedir($dh);
  173. }
  174. }
  175. $plugin_dir = $this->config('PHPDir') . DIRECTORY_SEPARATOR
  176. . 'plugins';
  177. if (is_dir($plugin_dir))
  178. $ctx->add_plugin_dir($plugin_dir);
  179. # Load any php directories found during the 'init_addons' loop
  180. foreach ($ctx->plugins_dir as $plugin_dir)
  181. if (is_dir($plugin_dir))
  182. $this->load_plugin($plugin_dir);
  183. }
  184. function load_plugin($plugin_dir) {
  185. $ctx =& $this->context();
  186. // global filters have to be handled differently from
  187. // tag attributes, so this causes them to be recognized
  188. // as they should...
  189. if ($dh = opendir($plugin_dir)) {
  190. while (($file = readdir($dh)) !== false) {
  191. if (preg_match('/^modifier\.(.+?)\.php$/', $file, $matches)) {
  192. $ctx->add_global_filter($matches[1]);
  193. } elseif (preg_match('/^init\.(.+?)\.php$/', $file, $matches)) {
  194. // load 'init' plugin file
  195. require_once($file);
  196. } else {
  197. // override plugin function
  198. if(preg_match('/^block\.(mt.+?)\.php$/', $file, $matches)) {
  199. $ctx->register_tag_handler($matches[1],'','block');
  200. } elseif(preg_match('/^function\.(mt.+?)\.php$/', $file, $matches)){
  201. $ctx->register_tag_handler($matches[1],'','function');
  202. }
  203. }
  204. }
  205. closedir($dh);
  206. }
  207. }
  208. public function cfg_file() {
  209. return $this->cfg_file;
  210. }
  211. /***
  212. * Retreives a handle to the database and assigns it to
  213. * the member variable 'db'.
  214. */
  215. function &db() {
  216. if (!isset($this->db)) {
  217. require_once("mtdb.".$this->config('DBDriver').".php");
  218. $mtdbclass = 'MTDatabase'.$this->config('DBDriver');
  219. $this->db = new $mtdbclass($this->config('DBUser'),
  220. $this->config('DBPassword'),
  221. $this->config('Database'),
  222. $this->config('DBHost'),
  223. $this->config('DBPort'),
  224. $this->config('DBSocket'),
  225. $this->config('DBMaxRetries'),
  226. $this->config('DBRetryInterval'));
  227. }
  228. return $this->db;
  229. }
  230. /***
  231. * Retreives a handle to the cache driver.
  232. */
  233. public function cache_driver() {
  234. if (isset($this->cache_driver)) return $this->cache_driver;
  235. # Check for memcached enabled
  236. require_once("class.basecache.php");
  237. try {
  238. $this->cache_driver = CacheProviderFactory::get_provider('memcached');
  239. } catch (Exception $e) {
  240. # Memcached not supported.
  241. $this->cache_driver = CacheProviderFactory::get_provider('session');
  242. }
  243. return $this->cache_driver;
  244. }
  245. public function config($id, $value = null) {
  246. $id = strtolower($id);
  247. if (isset($value))
  248. $this->config[$id] = $value;
  249. return isset($this->config[$id]) ? $this->config[$id] : null;
  250. }
  251. /***
  252. * Loads configuration data from mt.cfg and mt-db-pass.cgi files.
  253. * Stores content in the 'config' member variable.
  254. */
  255. function configure($file = null) {
  256. if (isset($this->config)) return $config;
  257. $this->cfg_file = $file;
  258. $cfg = array();
  259. if ($fp = file($file)) {
  260. foreach ($fp as $line) {
  261. // search through the file
  262. if (!preg_match('/^\s*\#/i',$line)) {
  263. // ignore lines starting with the hash symbol
  264. if (preg_match('/^\s*(\S+)\s+(.*)$/', $line, $regs)) {
  265. $key = strtolower(trim($regs[1]));
  266. $value = trim($regs[2]);
  267. if (in_array($key, self::$config_type_array)) {
  268. $cfg[$key][] = $value;
  269. }
  270. elseif (in_array($key, self::$config_type_hash)) {
  271. $hash = preg_split('/\=/', $value, 2);
  272. $cfg[$key][strtolower(trim($hash[0]))] = trim($hash[1]);
  273. } else {
  274. $cfg[$key] = $value;
  275. }
  276. }
  277. }
  278. }
  279. } else {
  280. die("Unable to open configuration file $file");
  281. }
  282. // setup directory locations
  283. // location of mt.php
  284. $cfg['phpdir'] = realpath(dirname(__FILE__));
  285. // path to MT directory
  286. $cfg['mtdir'] = realpath(dirname($file));
  287. // path to handlers
  288. $cfg['phplibdir'] = $cfg['phpdir'] . DIRECTORY_SEPARATOR . 'lib';
  289. $driver = $cfg['objectdriver'];
  290. $driver = preg_replace('/^DB[ID]::/', '', $driver);
  291. $driver or $driver = 'mysql';
  292. $cfg['dbdriver'] = strtolower($driver);
  293. if ((strlen($cfg['database'])<1 || strlen($cfg['dbuser'])<1)) {
  294. if (($cfg['dbdriver'] != 'sqlite') && ($cfg['dbdriver'] != 'mssqlserver') && ($cfg['dbdriver'] != 'umssqlserver')) {
  295. die("Unable to read database or username");
  296. }
  297. }
  298. if ( !empty( $cfg['debugmode'] ) && intval($cfg['debugmode']) > 0 ) {
  299. $this->debugging = true;
  300. }
  301. $this->config =& $cfg;
  302. $this->config_defaults();
  303. // read in the database password
  304. if (!isset($cfg['dbpassword'])) {
  305. $db_pass_file = $cfg['mtdir'] . DIRECTORY_SEPARATOR . 'mt-db-pass.cgi';
  306. if (file_exists($db_pass_file)) {
  307. $password = implode('', file($db_pass_file));
  308. $password = trim($password, "\n\r\0");
  309. $cfg['dbpassword'] = $password;
  310. }
  311. }
  312. // set up include path
  313. // add MT-PHP 'plugins' and 'lib' directories to the front
  314. // of the existing PHP include path:
  315. if (strtoupper(substr(PHP_OS, 0,3) == 'WIN')) {
  316. $path_sep = ';';
  317. } else {
  318. $path_sep = ':';
  319. }
  320. ini_set('include_path',
  321. $cfg['phpdir'] . DIRECTORY_SEPARATOR . "lib" . $path_sep .
  322. $cfg['phpdir'] . DIRECTORY_SEPARATOR . "extlib" . $path_sep .
  323. $cfg['phpdir'] . DIRECTORY_SEPARATOR . "extlib" . DIRECTORY_SEPARATOR . "smarty" . DIRECTORY_SEPARATOR . "libs" . $path_sep .
  324. $cfg['phpdir'] . DIRECTORY_SEPARATOR . "extlib" . DIRECTORY_SEPARATOR . "adodb5" . $path_sep .
  325. $cfg['phpdir'] . DIRECTORY_SEPARATOR . "extlib" . DIRECTORY_SEPARATOR . "FirePHPCore" . $path_sep .
  326. ini_get('include_path')
  327. );
  328. // assign i18n defaults:
  329. $lang = strtolower($cfg['defaultlanguage']);
  330. if (! @include_once("i18n_$lang.php")) {
  331. include_once("i18n_en_us.php");
  332. }
  333. foreach ($GLOBALS['i18n_default_settings'] as $k => $v) {
  334. if (! isset($cfg[$k])) {
  335. $cfg[$k] = $v;
  336. }
  337. }
  338. }
  339. function configure_from_db() {
  340. $cfg =& $this->config;
  341. $mtdb =& $this->db();
  342. $db_config = $mtdb->fetch_config();
  343. if ($db_config) {
  344. $data = $db_config->data();
  345. foreach ($data as $key => $value) {
  346. $cfg[$key] = $value;
  347. }
  348. $mtdb->set_names($this);
  349. }
  350. if ( !empty( $cfg['debugmode'] ) && intval($cfg['debugmode']) > 0 ) {
  351. $this->debugging = true;
  352. }
  353. }
  354. function config_defaults() {
  355. $cfg =& $this->config;
  356. // assign defaults:
  357. isset($cfg['cgipath']) or
  358. $cfg['cgipath'] = '/cgi-bin/';
  359. if (substr($cfg['cgipath'], strlen($cfg['cgipath']) - 1, 1) != '/')
  360. $cfg['cgipath'] .= '/';
  361. isset($cfg['staticwebpath']) or
  362. $cfg['staticwebpath'] = $cfg['cgipath'] . 'mt-static/';
  363. isset($cfg['trackbackscript']) or
  364. $cfg['trackbackscript'] = 'mt-tb.cgi';
  365. isset($cfg['adminscript']) or
  366. $cfg['adminscript'] = 'mt.cgi';
  367. isset($cfg['commentscript']) or
  368. $cfg['commentscript'] = 'mt-comments.cgi';
  369. isset($cfg['atomscript']) or
  370. $cfg['atomscript'] = 'mt-atom.cgi';
  371. isset($cfg['xmlrpcscript']) or
  372. $cfg['xmlrpcscript'] = 'mt-xmlrpc.cgi';
  373. isset($cfg['searchscript']) or
  374. $cfg['searchscript'] = 'mt-search.cgi';
  375. isset($cfg['defaultlanguage']) or
  376. $cfg['defaultlanguage'] = 'en_US';
  377. isset($cfg['globalsanitizespec']) or
  378. $cfg['globalsanitizespec'] = 'a href,b,i,br/,p,strong,em,ul,ol,li,blockquote,pre';
  379. isset($cfg['signonurl']) or
  380. $cfg['signonurl'] = 'https://www.typekey.com/t/typekey/login?';
  381. isset($cfg['signoffurl']) or
  382. $cfg['signoffurl'] = 'https://www.typekey.com/t/typekey/logout?';
  383. isset($cfg['publishcommentericon']) or
  384. $cfg['publishcommentericon'] = '1';
  385. isset($cfg['allowcomments']) or
  386. $cfg['allowcomments'] = '1';
  387. isset($cfg['allowpings']) or
  388. $cfg['allowpings'] = '1';
  389. isset($cfg['indexbasename']) or
  390. $cfg['indexbasename'] = 'index';
  391. isset($cfg['typekeyversion']) or
  392. $cfg['typekeyversion'] = '1.1';
  393. isset($cfg['assetcachedir']) or
  394. $cfg['assetcachedir'] = 'assets_c';
  395. isset($cfg['userpicthumbnailsize']) or
  396. $cfg['userpicthumbnailsize'] = '100';
  397. isset($cfg['pluginpath']) or
  398. $cfg['pluginpath'] = array($this->config('MTDir') . DIRECTORY_SEPARATOR . 'plugins');
  399. isset($cfg['includesdir']) or
  400. $cfg['includesdir'] = 'includes_c';
  401. isset($cfg['searchmaxresults']) or
  402. $cfg['searchmaxresults'] = '20';
  403. isset($cfg['maxresults']) or
  404. $cfg['maxresults'] = $cfg['searchmaxresults'];
  405. isset($cfg['singlecommunity']) or
  406. $cfg['singlecommunity'] = '1';
  407. isset($cfg['usersessioncookiename']) or
  408. $cfg['usersessioncookiename'] = 'DEFAULT';
  409. isset($cfg['usersessioncookiedomain']) or
  410. $cfg['usersessioncookiedomain'] = '<$MTBlogHost exclude_port="1"$>';
  411. isset($cfg['usersessioncookiepath']) or
  412. $cfg['usersessioncookiepath'] = 'DEFAULT';
  413. isset($cfg['usersessioncookietimeout']) or
  414. $cfg['usersessioncookietimeout'] = 60*60*4;
  415. isset($cfg['commenterregistration']) or
  416. $cfg['commenterregistration'] = array('Allow' => 1 );
  417. isset($cfg['userpasswordminlength']) or
  418. $cfg['userpasswordminlength'] = 8;
  419. isset($cfg['bulkloadmetaobjectslimit']) or
  420. $cfg['bulkloadmetaobjectslimit'] = 100;
  421. isset($cfg['dbmaxretries']) or
  422. $cfg['dbmaxretries'] = 3;
  423. isset($cfg['dbretryintercal']) or
  424. $cfg['dbretryinterval'] = 1;
  425. isset($cfg['dataapiscript']) or
  426. $cfg['dataapiscript'] = 'mt-data-api.cgi';
  427. }
  428. function configure_paths($blog_site_path) {
  429. if (preg_match('/^\./', $blog_site_path)) {
  430. // relative address, so tack on the MT dir in front
  431. $blog_site_path = $this->config('MTDir') .
  432. DIRECTORY_SEPARATOR . $blog_site_path;
  433. }
  434. $this->config('PHPTemplateDir') or
  435. $this->config('PHPTemplateDir', $blog_site_path .
  436. DIRECTORY_SEPARATOR . 'templates');
  437. $this->config('PHPCacheDir') or
  438. $this->config('PHPCacheDir', $blog_site_path .
  439. DIRECTORY_SEPARATOR . 'cache');
  440. $ctx =& $this->context();
  441. $ctx->setTemplateDir($this->config('PHPTemplateDir'));
  442. $ctx->setCompileDir($this->config('PHPTemplateDir') . '_c');
  443. $ctx->setCacheDir($this->config('PHPCacheDir'));
  444. }
  445. /***
  446. * Mainline handler function.
  447. */
  448. function view($blog_id = null) {
  449. set_error_handler(array(&$this, 'error_handler'));
  450. require_once("MTUtil.php");
  451. $blog_id or $blog_id = $this->blog_id;
  452. try {
  453. $ctx =& $this->context();
  454. $this->init_plugins();
  455. $ctx->caching = $this->caching;
  456. // Some defaults...
  457. $mtdb =& $this->db();
  458. $ctx->mt->db =& $mtdb;
  459. } catch (Exception $e ) {
  460. if ( $this->debugging ) {
  461. $msg = "<b>Error:</b> ". $e->getMessage() ."<br>\n" .
  462. "<pre>".$e->getTraceAsString()."</pre>";
  463. return trigger_error( $msg, E_USER_ERROR);
  464. }
  465. header( "503 Service Unavailable" );
  466. return false;
  467. }
  468. // User-specified request through request variable
  469. $path = $this->request;
  470. // Apache request
  471. if (!$path && $_SERVER['REQUEST_URI']) {
  472. $path = $_SERVER['REQUEST_URI'];
  473. // strip off any query string...
  474. $path = preg_replace('/\?.*/', '', $path);
  475. // strip any duplicated slashes...
  476. $path = preg_replace('!/+!', '/', $path);
  477. }
  478. // IIS request by error document...
  479. if (preg_match('/IIS/', $_SERVER['SERVER_SOFTWARE'])) {
  480. // assume 404 handler
  481. if (preg_match('/^\d+;(.*)$/', $_SERVER['QUERY_STRING'], $matches)) {
  482. $path = $matches[1];
  483. $path = preg_replace('!^http://[^/]+!', '', $path);
  484. if (preg_match('/\?(.+)?/', $path, $matches)) {
  485. $_SERVER['QUERY_STRING'] = $matches[1];
  486. $path = preg_replace('/\?.*$/', '', $path);
  487. }
  488. }
  489. }
  490. // now set the path so it may be queried
  491. $path = preg_replace('/\\\\/', '\\\\\\\\', $path );
  492. $this->request = $path;
  493. $pathinfo = pathinfo($path);
  494. $ctx->stash('_basename', $pathinfo['filename']);
  495. // When we are invoked as an ErrorDocument, the parameters are
  496. // in the environment variables REDIRECT_*
  497. if (isset($_SERVER['REDIRECT_QUERY_STRING'])) {
  498. // todo: populate $_GET and QUERY_STRING with REDIRECT_QUERY_STRING
  499. $_SERVER['QUERY_STRING'] = getenv('REDIRECT_QUERY_STRING');
  500. }
  501. if (preg_match('/\.(\w+)$/', $path, $matches)) {
  502. $req_ext = strtolower($matches[1]);
  503. }
  504. $this->blog_id = $blog_id;
  505. $data = $this->resolve_url($path);
  506. if (!$data) {
  507. // 404!
  508. $this->http_error = 404;
  509. header("HTTP/1.1 404 Not found");
  510. return $ctx->error($this->translate("Page not found - [_1]", $path), E_USER_ERROR);
  511. }
  512. $ctx->stash('_fileinfo', $data);
  513. $fi_path = $data->fileinfo_url;
  514. $fid = $data->fileinfo_id;
  515. $at = $data->fileinfo_archive_type;
  516. $ts = $data->fileinfo_startdate;
  517. $tpl_id = $data->fileinfo_template_id;
  518. $cat = $data->fileinfo_category_id;
  519. $auth = $data->fileinfo_author_id;
  520. $entry_id = $data->fileinfo_entry_id;
  521. $cd_id = $data->fileinfo_cd_id;
  522. $blog_id = $data->fileinfo_blog_id;
  523. $blog = $data->blog();
  524. if ($at == 'index') {
  525. $at = null;
  526. $ctx->stash('index_archive', true);
  527. } else {
  528. $ctx->stash('index_archive', false);
  529. }
  530. $tmpl = $data->template();
  531. $ctx->stash('template', $tmpl);
  532. $tts = $tmpl->template_modified_on;
  533. if ($tts) {
  534. $tts = offset_time(datetime_to_timestamp($tts), $blog);
  535. }
  536. $ctx->stash('template_timestamp', $tts);
  537. $ctx->stash('template_created_on', $tmpl->template_created_on);
  538. $page_layout = $blog->blog_page_layout;
  539. $columns = get_page_column($page_layout);
  540. $vars =& $ctx->__stash['vars'];
  541. $vars['page_columns'] = $columns;
  542. $vars['page_layout'] = $page_layout;
  543. if (isset($tmpl->template_identifier))
  544. $vars[$tmpl->template_identifier] = 1;
  545. $this->configure_paths($blog->site_path());
  546. // start populating our stash
  547. $ctx->stash('blog_id', $blog_id);
  548. $ctx->stash('local_blog_id', $blog_id);
  549. $ctx->stash('blog', $blog);
  550. $ctx->stash('build_template_id', $tpl_id);
  551. // conditional get support...
  552. if ($this->caching) {
  553. $this->cache_modified_check = true;
  554. }
  555. if ($this->conditional) {
  556. $local_last_datetime = $blog->blog_children_modified_on;
  557. $gmt_last_ts = datetime_to_timestamp($local_last_datetime);
  558. $gmt_last_datetime = gmdate('YmdHis', $gmt_last_ts);
  559. $last_modified = $ctx->_hdlr_date(array('ts' => $gmt_last_datetime, 'format' => '%a, %d %b %Y %H:%M:%S GMT', 'language' => 'en'), $ctx);
  560. $this->doConditionalGet($last_modified);
  561. }
  562. $cache_id = $blog_id.';'.$fi_path;
  563. if (!$ctx->is_cached('mt:'.$tpl_id, $cache_id)) {
  564. if (isset($at) && $at) {
  565. require_once("archive_lib.php");
  566. try {
  567. $archiver = ArchiverFactory::get_archiver($at);
  568. } catch (Exception $e) {
  569. // 404
  570. $this->http_errr = 404;
  571. header("HTTP/1.1 404 Not Found");
  572. return $ctx->error($this->translate("Page not found - [_1]", $at), E_USER_ERROR);
  573. }
  574. $archiver->template_params($ctx);
  575. }
  576. if ($cat) {
  577. $archive_category = $mtdb->fetch_category($cat);
  578. $ctx->stash('category', $archive_category);
  579. $ctx->stash('archive_category', $archive_category);
  580. }
  581. if ($auth) {
  582. $archive_author = $mtdb->fetch_author($auth);
  583. $ctx->stash('author', $archive_author);
  584. $ctx->stash('archive_author', $archive_author);
  585. }
  586. if (isset($at)) {
  587. if ($at != 'Category' && $at != 'ContentType-Category' && isset($ts)) {
  588. list($ts_start, $ts_end) = $archiver->get_range($ts);
  589. $ctx->stash('current_timestamp', $ts_start);
  590. $ctx->stash('current_timestamp_end', $ts_end);
  591. }
  592. $ctx->stash('current_archive_type', $at);
  593. }
  594. if (isset($entry_id) && ($entry_id) && ($at == 'Individual' || $at == 'Page')) {
  595. if ($at == 'Individual') {
  596. $entry = $mtdb->fetch_entry($entry_id);
  597. } elseif($at == 'Page') {
  598. $entry = $mtdb->fetch_page($entry_id);
  599. }
  600. $ctx->stash('entry', $entry);
  601. $ctx->stash('current_timestamp', $entry->entry_authored_on);
  602. }
  603. if (isset($cd_id) && ($cd_id) && $at == 'ContentType') {
  604. $cd = $mtdb->fetch_content($cd_id);
  605. $ct = $mtdb->fetch_content_type($cd->content_type_id);
  606. $ctx->stash('content', $cd);
  607. $ctx->stash('content_type', $ct);
  608. $ctx->stash('current_timestamp', $cd->cd_authored_on);
  609. }
  610. if (preg_match('/^ContentType/', $at) && !$ctx->stash('content_type') && $tmpl && $tmpl->content_type_id) {
  611. $ct = $mtdb->fetch_content_type($tmpl->content_type_id);
  612. if ($ct) {
  613. $ctx->stash('content_type', $ct);
  614. }
  615. }
  616. if(preg_match('/ContentType-Category/', $at)){
  617. if($archive_category){
  618. $category_set = $ctx->mt->db()->fetch_category_set($archive_category->category_category_set_id);
  619. if($category_set)
  620. $ctx->stash('category_set', $category_set);
  621. }
  622. }
  623. }
  624. $this->set_canonical_url($ctx, $blog, $data);
  625. $output = $this->fetch('mt:'.$tpl_id, $cache_id);
  626. $this->http_error = 200;
  627. header("HTTP/1.1 200 OK");
  628. // content-type header-- need to supplement with charset
  629. $content_type = $ctx->stash('http_content_type');
  630. if (!isset($content_type)) {
  631. $content_type = $this->mime_types['__default__'];
  632. if ($req_ext && (isset($this->mime_types[$req_ext]))) {
  633. $content_type = $this->mime_types[$req_ext];
  634. }
  635. }
  636. $charset = $this->config('PublishCharset');
  637. if (isset($charset)) {
  638. if (!preg_match('/charset=/', $content_type))
  639. $content_type .= '; charset=' . $charset;
  640. }
  641. header("Content-Type: $content_type");
  642. // finally, issue output
  643. $output = preg_replace('/^\s*/', '', $output);
  644. echo $output;
  645. // if warnings found, show it.
  646. if (!empty($this->warning)) {
  647. $this->_dump($this->warning);
  648. }
  649. # if ($this->debugging) {
  650. # $this->log("Queries: ".$mtdb->num_queries);
  651. # $this->log("Queries executed:");
  652. # $queries = $mtdb->savedqueries;
  653. # foreach ($queries as $q) {
  654. # $this->log($q);
  655. # }
  656. # $this->log_dump();
  657. # }
  658. restore_error_handler();
  659. }
  660. function set_canonical_url($ctx, $blog, $fileinfo) {
  661. if (preg_match('#(\A[^:]*://[^/]*)#', $blog->site_url(), $m)) {
  662. $url = $m[1] . $fileinfo->url;
  663. }
  664. else {
  665. $url = $fileinfo->url;
  666. }
  667. $ctx->stash('current_mapping_url', $url);
  668. $templatemap = $fileinfo->templatemap();
  669. if ($templatemap && ! $templatemap->is_preferred) {
  670. $url = $ctx->tag('archivelink', array());
  671. $ctx->stash('preferred_mapping_url', $url);
  672. }
  673. }
  674. function resolve_url($path, $build_type = 3) {
  675. $data = $this->db->resolve_url($path, $this->blog_id, $build_type);
  676. if (isset($data)) {
  677. $tmpl_map = $data->templatemap();
  678. if (strtolower($tmpl_map->templatemap_archive_type) == 'contenttype') {
  679. if ( isset($data->fileinfo_cd_id)
  680. && is_numeric($data->fileinfo_cd_id)
  681. ) {
  682. $tmpl_map = $data->templatemap();
  683. $cd = $this->db->fetch_content($data->fileinfo_cd_id);
  684. if (!isset($cd) || $cd->cd_status != 2)
  685. return;
  686. }
  687. }
  688. else {
  689. if ( isset($data->fileinfo_entry_id)
  690. && is_numeric($data->fileinfo_entry_id)
  691. ) {
  692. if (strtolower($tmpl_map->templatemap_archive_type) == 'page') {
  693. $entry = $this->db->fetch_page($data->fileinfo_entry_id);
  694. } else {
  695. $entry = $this->db->fetch_entry($data->fileinfo_entry_id);
  696. }
  697. if (!isset($entry) || $entry->entry_status != 2)
  698. return;
  699. }
  700. }
  701. }
  702. return $data;
  703. }
  704. function doConditionalGet($last_modified) {
  705. // Thanks to Simon Willison...
  706. // http://simon.incutio.com/archive/2003/04/23/conditionalGet
  707. // A PHP implementation of conditional get, see
  708. // http://fishbowl.pastiche.org/archives/001132.html
  709. $etag = '"'.md5($last_modified).'"';
  710. // Send the headers
  711. header("Last-Modified: $last_modified");
  712. header("ETag: $etag");
  713. // See if the client has provided the required headers
  714. $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ?
  715. stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) :
  716. false;
  717. $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
  718. stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) :
  719. false;
  720. if (!$if_modified_since && !$if_none_match) {
  721. return;
  722. }
  723. // At least one of the headers is there - check them
  724. if ($if_none_match && $if_none_match != $etag) {
  725. return; // etag is there but doesn't match
  726. }
  727. if ($if_modified_since && $if_modified_since != $last_modified) {
  728. return; // if-modified-since is there but doesn't match
  729. }
  730. // Nothing has changed since their last request - serve a 304 and exit
  731. header('HTTP/1.1 304 Not Modified');
  732. exit;
  733. }
  734. function display($tpl, $cid = null) {
  735. $ctx =& $this->context();
  736. $this->init_plugins();
  737. $blog =& $ctx->stash('blog');
  738. if (!$blog) {
  739. $db =& $this->db();
  740. $ctx->mt->db =& $db;
  741. $blog = $db->fetch_blog($this->blog_id);
  742. $ctx->stash('blog', $blog);
  743. $ctx->stash('blog_id', $this->blog_id);
  744. $ctx->stash('local_blog_id', $this->blog_id);
  745. $this->configure_paths($blog->site_path());
  746. }
  747. return $ctx->display($tpl, $cid);
  748. }
  749. function fetch($tpl, $cid = null) {
  750. $ctx =& $this->context();
  751. $this->init_plugins();
  752. $blog =& $ctx->stash('blog');
  753. if (!$blog) {
  754. $db =& $this->db();
  755. $ctx->mt->db =& $db;
  756. $blog = $db->fetch_blog($this->blog_id);
  757. $ctx->stash('blog', $blog);
  758. $ctx->stash('blog_id', $this->blog_id);
  759. $ctx->stash('local_blog_id', $this->blog_id);
  760. $this->configure_paths($blog->site_path());
  761. }
  762. return $ctx->fetch($tpl, $cid);
  763. }
  764. function _dump($dump) {
  765. if ($_SERVER['REMOTE_ADDR']) {
  766. // web view...
  767. echo "<div class=\"debug\" style=\"border:1px solid red; margin:0.5em; padding: 0 1em; text-align:left; background-color:#ddd; color:#000\"><pre>";
  768. echo implode("\n", $dump);
  769. echo "</pre></div>\n\n";
  770. } else {
  771. // console view...
  772. $stderr = fopen('php://stderr', 'w');
  773. fwrite($stderr,implode("\n", $dump));
  774. echo (implode("\n", $dump));
  775. fclose($stderr);
  776. }
  777. }
  778. function log_dump() {
  779. $this->_dump($this->log);
  780. }
  781. function error_handler($errno, $errstr, $errfile, $errline) {
  782. if ($errno & (E_ALL ^ E_NOTICE ^ E_WARNING)) {
  783. if ( !empty( $this->db ) ) {
  784. $errstr = encode_html_entities($errstr, ENT_QUOTES);
  785. $errfile = encode_html_entities($errfile, ENT_QUOTES);
  786. $mtphpdir = $this->config('PHPDir');
  787. $ctx =& $this->context();
  788. $ctx->stash('blog_id', $this->blog_id);
  789. $ctx->stash('local_blog_id', $this->blog_id);
  790. $ctx->stash('blog', $this->db->fetch_blog($this->blog_id));
  791. if ( $this->debugging ) {
  792. $ctx->stash('error_message', $errstr."<!-- file: $errfile; line: $errline; code: $errno -->");
  793. $ctx->stash('error_code', $errno);
  794. } else {
  795. if ( 404 == $this->http_error) {
  796. $ctx->stash('error_message', $errstr);
  797. } else {
  798. $ctx->stash('error_message', 'An error occurs.');
  799. }
  800. }
  801. $http_error = $this->http_error;
  802. if (!$http_error) {
  803. $http_error = 500;
  804. }
  805. $ctx->stash('http_error', $http_error);
  806. $ctx->stash('error_file', $errfile);
  807. $ctx->stash('error_line', $errline);
  808. $ctx->setTemplateDir($mtphpdir . DIRECTORY_SEPARATOR . 'tmpl');
  809. $ctx->caching = 0;
  810. $ctx->stash('StaticWebPath', $this->config('StaticWebPath'));
  811. $ctx->stash('PublishCharset', $this->config('PublishCharset'));
  812. $charset = $this->config('PublishCharset');
  813. $out = $ctx->tag('Include', array('type' => 'dynamic_error', 'dynamic_error' => 1, 'system_template' => 1));
  814. if (isset($out)) {
  815. header("Content-type: text/html; charset=".$charset);
  816. echo $out;
  817. } else {
  818. header("HTTP/1.1 500 Server Error");
  819. header("Content-type: text/plain");
  820. echo "Error executing error template.";
  821. }
  822. exit;
  823. } else {
  824. header( "HTTP/1.1 503 Service Unavailable" );
  825. header("Content-type: text/plain");
  826. echo "503 Service Unavailable\n\n";
  827. if ( $this->debugging ) {
  828. echo "Errno: $errno\n";
  829. echo "Error: $errstr\n";
  830. echo "File: $errfile Line: $errline\n";
  831. }
  832. exit;
  833. }
  834. }
  835. }
  836. /***
  837. * Retrieves a context and rendering object.
  838. */
  839. public function &context() {
  840. static $ctx;
  841. if (isset($ctx)) return $ctx;
  842. require_once('MTViewer.php');
  843. $ctx = new MTViewer($this);
  844. $ctx->mt =& $this;
  845. $mtphpdir = $this->config('PHPDir');
  846. $mtlibdir = $this->config('PHPLibDir');
  847. $ctx->setCompileCheck(1);
  848. $ctx->caching = false;
  849. $ctx->add_plugin_dir($mtlibdir);
  850. $ctx->add_plugin_dir($mtphpdir . DIRECTORY_SEPARATOR . "plugins");
  851. if ($this->debugging) {
  852. $ctx->debugging_ctrl = 'URL';
  853. $ctx->debug_tpl = $mtphpdir . DIRECTORY_SEPARATOR .
  854. 'extlib' . DIRECTORY_SEPARATOR .
  855. 'smarty' . DIRECTORY_SEPARATOR . "libs" . DIRECTORY_SEPARATOR .
  856. 'debug.tpl';
  857. }
  858. #if (isset($this->config('SafeMode')) && ($this->config('SafeMode'))) {
  859. # // disable PHP support
  860. # $ctx->php_handling = SMARTY_PHP_REMOVE;
  861. #}
  862. return $ctx;
  863. }
  864. function log($msg = null) {
  865. $this->log[] = $msg;
  866. }
  867. function translate($str, $params = null) {
  868. if ( ( $params !== null ) && ( !is_array($params) ) )
  869. $params = array( $params );
  870. return translate_phrase($str, $params);
  871. }
  872. function translate_templatized_item($str) {
  873. return translate_phrase($str[1]);
  874. }
  875. function translate_templatized($tmpl) {
  876. $cb = array($this, 'translate_templatized_item');
  877. $out = preg_replace_callback('/<(?:_|mt)_trans phrase="(.+?)".*?>/i', $cb, $tmpl);
  878. return $out;
  879. }
  880. function warning_log($str) {
  881. $this->warning[] = $str;
  882. }
  883. function get_current_blog_id() {
  884. return $this->blog_id;
  885. }
  886. function mode() {
  887. $mode = $_GET['__mode'];
  888. if (!isset($mode)) $mode = 'default';
  889. preg_replace('/[<>"\']/', '', $mode);
  890. return $mode;
  891. }
  892. }
  893. function is_valid_email($addr) {
  894. if (preg_match('/[ |\t|\r|\n]*\"?([^\"]+\"?@[^ <>\t]+\.[^ <>\t][^ <>\t]+)[ |\t|\r|\n]*/', $addr, $matches)) {
  895. return $matches[1];
  896. } else {
  897. return 0;
  898. }
  899. }
  900. $spam_protect_map = array(':' => '&#58;', '@' => '&#64;', '.' => '&#46;');
  901. function spam_protect($str) {
  902. global $spam_protect_map;
  903. return strtr($str, $spam_protect_map);
  904. }
  905. function offset_time($ts, $blog = null, $dir = null) {
  906. if (isset($blog)) {
  907. if (!is_object($blog)) {
  908. global $mt;
  909. $blog = $mt->db()->fetch_blog($blog);
  910. }
  911. $offset = $blog->blog_server_offset;
  912. } else {
  913. global $mt;
  914. $offset = $mt->config('TimeOffset');
  915. }
  916. intval($offset) or $offset = 0;
  917. $tsa = localtime($ts);
  918. if ($tsa[8]) { // daylight savings offset
  919. $offset++;
  920. }
  921. if ($dir == '-') {
  922. $offset *= -1;
  923. }
  924. $ts += $offset * 3600;
  925. return $ts;
  926. }
  927. function translate_phrase_param($str, $params = null) {
  928. if (is_array($params)) {
  929. if (strpos($str, '[_') !== false) {
  930. for ($i = 1; $i <= count($params); $i++) {
  931. $str = preg_replace("/\\[_$i\\]/", $params[$i-1], $str);
  932. }
  933. }
  934. $start = 0;
  935. while (preg_match("/\\[quant,_(\d+),([^\\],]*)(?:,([^\\],]*))?(?:,([^\\],]*))?\\]/", $str, $matches, PREG_OFFSET_CAPTURE, $start)) {
  936. $id = $matches[1][0];
  937. $num = $params[$id-1];
  938. if ( ($num === 0) && (count($matches) > 4) ) {
  939. $part = $matches[4][0];
  940. }
  941. elseif ( $num === 1 ) {
  942. $part = $num . ' ' . $matches[2][0];
  943. }
  944. else {
  945. $part = $num . ' ' . ( count($matches) > 3 ? $matches[3][0] : ( $matches[2][0] . 's' ) );
  946. }
  947. $str = substr_replace($str, $part, $matches[0][1], strlen($matches[0][0]));
  948. $start = $matches[0][1] + strlen($part);
  949. }
  950. }
  951. return $str;
  952. }
  953. ?>