Changeset 3692:9decf2422f49
- Timestamp:
- 01/31/18 14:48:07 (6 years ago)
- Branch:
- default
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
.hgsubstate
r3691 r3692 1 78ca5055b490dca43fa8fbcc780343eb9a23531binc/libs/clearbricks1 483f257f02e5236ed447e603e3496fdcd82676f8 inc/libs/clearbricks -
inc/public/lib.urlhandlers.php
r3526 r3692 10 10 # 11 11 # -- END LICENSE BLOCK ----------------------------------------- 12 if (!defined('DC_RC_PATH')) { return;}12 if (!defined('DC_RC_PATH')) {return;} 13 13 14 14 class dcUrlHandlers extends urlHandler 15 15 { 16 public $args; 17 18 public function getURLFor($type,$value='') { 19 $core =& $GLOBALS['core']; 20 $url = $core->callBehavior("publicGetURLFor",$type,$value); 21 if (!$url) { 22 $url = $this->getBase($type); 23 if ($value) { 24 if ($url) { 25 $url .= '/'; 26 } 27 $url .= $value; 28 } 29 } 30 return $url; 31 } 32 33 public function register($type,$url,$representation,$handler) 34 { 35 $core =& $GLOBALS['core']; 36 $t = new ArrayObject(array($type,$url,$representation,$handler)); 37 $core->callBehavior("publicRegisterURL",$t); 38 parent::register($t[0],$t[1],$t[2],$t[3]); 39 } 40 41 public static function p404() 42 { 43 throw new Exception ("Page not found",404); 44 } 45 46 public static function default404($args,$type,$e) 47 { 48 if ($e->getCode() != 404) { 49 throw $e; 50 } 51 $_ctx =& $GLOBALS['_ctx']; 52 $core = $GLOBALS['core']; 53 54 header('Content-Type: text/html; charset=UTF-8'); 55 http::head(404,'Not Found'); 56 $core->url->type = '404'; 57 $_ctx->current_tpl = '404.html'; 58 $_ctx->content_type = 'text/html'; 59 60 echo $core->tpl->getData($_ctx->current_tpl); 61 62 # --BEHAVIOR-- publicAfterDocument 63 $core->callBehavior('publicAfterDocument',$core); 64 exit; 65 } 66 67 protected static function getPageNumber(&$args) 68 { 69 if (preg_match('#(^|/)page/([0-9]+)$#',$args,$m)) { 70 $n = (integer) $m[2]; 71 if ($n > 0) { 72 $args = preg_replace('#(^|/)page/([0-9]+)$#','',$args); 73 return $n; 74 } 75 } 76 77 return false; 78 } 79 80 protected static function serveDocument($tpl,$content_type='text/html',$http_cache=true,$http_etag=true) 81 { 82 $_ctx =& $GLOBALS['_ctx']; 83 $core =& $GLOBALS['core']; 84 85 if ($_ctx->nb_entry_per_page === null) { 86 $_ctx->nb_entry_per_page = $core->blog->settings->system->nb_post_per_page; 87 } 88 if ($_ctx->nb_entry_first_page === null) { 89 $_ctx->nb_entry_first_page = $_ctx->nb_entry_per_page; 90 } 91 92 $tpl_file = $core->tpl->getFilePath($tpl); 93 94 if (!$tpl_file) { 95 throw new Exception('Unable to find template '); 96 } 97 98 $result = new ArrayObject; 99 100 $_ctx->current_tpl = $tpl; 101 $_ctx->content_type = $content_type; 102 $_ctx->http_cache = $http_cache; 103 $_ctx->http_etag = $http_etag; 104 $core->callBehavior('urlHandlerBeforeGetData',$_ctx); 105 106 if ($_ctx->http_cache) { 107 $GLOBALS['mod_files'][] = $tpl_file; 108 http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']); 109 } 110 111 header('Content-Type: '.$_ctx->content_type.'; charset=UTF-8'); 112 113 // Additional headers 114 $headers = new ArrayObject; 115 if ($core->blog->settings->system->prevents_clickjacking) { 116 if ($_ctx->exists('xframeoption')) { 117 $url = parse_url($_ctx->xframeoption); 118 $header = sprintf('X-Frame-Options: %s', 119 is_array($url)?("ALLOW-FROM ".$url['scheme'].'://'.$url['host']):'SAMEORIGIN'); 120 } else { 121 // Prevents Clickjacking as far as possible 122 $header = 'X-Frame-Options: SAMEORIGIN'; // FF 3.6.9+ Chrome 4.1+ IE 8+ Safari 4+ Opera 10.5+ 123 } 124 $headers[] = $header; 125 } 126 # --BEHAVIOR-- urlHandlerServeDocumentHeaders 127 $core->callBehavior('urlHandlerServeDocumentHeaders',$headers); 128 129 // Send additional headers if any 130 foreach ($headers as $header) { 131 header($header); 132 } 133 134 $result['content'] = $core->tpl->getData($_ctx->current_tpl); 135 $result['content_type'] = $_ctx->content_type; 136 $result['tpl'] = $_ctx->current_tpl; 137 $result['blogupddt'] = $core->blog->upddt; 138 $result['headers'] = $headers; 139 140 # --BEHAVIOR-- urlHandlerServeDocument 141 $core->callBehavior('urlHandlerServeDocument',$result); 142 143 if ($_ctx->http_cache && $_ctx->http_etag) { 144 http::etag($result['content'],http::getSelfURI()); 145 } 146 echo $result['content']; 147 } 148 149 public function getDocument() 150 { 151 $core =& $GLOBALS['core']; 152 153 $type = $args = ''; 154 155 if ($this->mode == 'path_info') 156 { 157 $part = substr($_SERVER['PATH_INFO'],1); 158 } 159 else 160 { 161 $part = ''; 162 163 $qs = $this->parseQueryString(); 164 165 # Recreates some _GET and _REQUEST pairs 166 if (!empty($qs)) 167 { 168 foreach ($_GET as $k => $v) { 169 if (isset($_REQUEST[$k])) { 170 unset($_REQUEST[$k]); 171 } 172 } 173 $_GET = $qs; 174 $_REQUEST = array_merge($qs,$_REQUEST); 175 176 list($k,$v) = each($qs); 177 if ($v === null) { 178 $part = $k; 179 unset($_GET[$k]); 180 unset($_REQUEST[$k]); 181 } 182 } 183 } 184 185 $_SERVER['URL_REQUEST_PART'] = $part; 186 187 $this->getArgs($part,$type,$this->args); 188 189 # --BEHAVIOR-- urlHandlerGetArgsDocument 190 $core->callBehavior('urlHandlerGetArgsDocument',$this); 191 192 if (!$type) 193 { 194 $this->type = 'default'; 195 $this->callDefaultHandler($this->args); 196 } 197 else 198 { 199 $this->type = $type; 200 $this->callHandler($type,$this->args); 201 } 202 } 203 204 public static function home($args) 205 { 206 $n = self::getPageNumber($args); 207 208 if ($args && !$n) 209 { 210 # "Then specified URL went unrecognized by all URL handlers and 211 # defaults to the home page, but is not a page number. 212 self::p404(); 213 } 214 else 215 { 216 $_ctx =& $GLOBALS['_ctx']; 217 $core =& $GLOBALS['core']; 218 219 if ($n) { 220 $GLOBALS['_page_number'] = $n; 221 $core->url->type = $n > 1 ? 'default-page' : 'default'; 222 } 223 224 if (empty($_GET['q'])) { 225 if ($core->blog->settings->system->nb_post_for_home !== null) { 226 $_ctx->nb_entry_first_page = $core->blog->settings->system->nb_post_for_home; 227 } 228 self::serveDocument('home.html'); 229 $core->blog->publishScheduledEntries(); 230 } else { 231 self::search(); 232 } 233 } 234 } 235 236 public static function search() 237 { 238 $_ctx =& $GLOBALS['_ctx']; 239 $core =& $GLOBALS['core']; 240 241 if ($core->blog->settings->system->no_search) { 242 243 # Search is disabled for this blog. 244 self::p404(); 245 246 } else { 247 248 $core->url->type='search'; 249 250 $GLOBALS['_search'] = !empty($_GET['q']) ? rawurldecode($_GET['q']) : ''; 251 if ($GLOBALS['_search']) { 252 $params = new ArrayObject(array('search' => $GLOBALS['_search'])); 253 $core->callBehavior('publicBeforeSearchCount',$params); 254 $GLOBALS['_search_count'] = $core->blog->getPosts($params,true)->f(0); 255 } 256 257 self::serveDocument('search.html'); 258 } 259 } 260 261 public static function lang($args) 262 { 263 $_ctx =& $GLOBALS['_ctx']; 264 $core =& $GLOBALS['core']; 265 266 $n = self::getPageNumber($args); 267 $params = new ArrayObject(array( 268 'lang' => $args)); 269 270 $core->callBehavior('publicLangBeforeGetLangs',$params,$args); 271 272 $_ctx->langs = $core->blog->getLangs($params); 273 274 if ($_ctx->langs->isEmpty()) { 275 # The specified language does not exist. 276 self::p404(); 277 } 278 else 279 { 280 if ($n) { 281 $GLOBALS['_page_number'] = $n; 282 } 283 $_ctx->cur_lang = $args; 284 self::home(null); 285 } 286 } 287 288 public static function category($args) 289 { 290 $_ctx =& $GLOBALS['_ctx']; 291 $core =& $GLOBALS['core']; 292 293 $n = self::getPageNumber($args); 294 295 if ($args == '' && !$n) { 296 # No category was specified. 297 self::p404(); 298 } 299 else 300 { 301 $params = new ArrayObject(array( 302 'cat_url' => $args, 303 'post_type' => 'post', 304 'without_empty' => false)); 305 306 $core->callBehavior('publicCategoryBeforeGetCategories',$params,$args); 307 308 $_ctx->categories = $core->blog->getCategories($params); 309 310 if ($_ctx->categories->isEmpty()) { 311 # The specified category does no exist. 312 self::p404(); 313 } 314 else 315 { 316 if ($n) { 317 $GLOBALS['_page_number'] = $n; 318 } 319 self::serveDocument('category.html'); 320 } 321 } 322 } 323 324 public static function archive($args) 325 { 326 $_ctx =& $GLOBALS['_ctx']; 327 $core =& $GLOBALS['core']; 328 329 $year = $month = $cat_url = null; 330 # Nothing or year and month 331 if ($args == '') 332 { 333 self::serveDocument('archive.html'); 334 } 335 elseif (preg_match('|^/([0-9]{4})/([0-9]{2})$|',$args,$m)) 336 { 337 $params = new ArrayObject(array( 338 'year' => $m[1], 339 'month' => $m[2], 340 'type' => 'month')); 341 342 $core->callBehavior('publicArchiveBeforeGetDates',$params,$args); 343 344 $_ctx->archives = $core->blog->getDates($params); 345 346 if ($_ctx->archives->isEmpty()) { 347 # There is no entries for the specified period. 348 self::p404(); 349 } 350 else 351 { 352 self::serveDocument('archive_month.html'); 353 } 354 } 355 else { 356 # The specified URL is not a date. 357 self::p404(); 358 } 359 } 360 361 public static function post($args) 362 { 363 if ($args == '') { 364 # No entry was specified. 365 self::p404(); 366 } 367 else 368 { 369 $_ctx =& $GLOBALS['_ctx']; 370 $core =& $GLOBALS['core']; 371 372 $core->blog->withoutPassword(false); 373 374 $params = new ArrayObject(array( 375 'post_url' => $args)); 376 377 $core->callBehavior('publicPostBeforeGetPosts',$params,$args); 378 379 $_ctx->posts = $core->blog->getPosts($params); 380 381 $_ctx->comment_preview = new ArrayObject(); 382 $_ctx->comment_preview['content'] = ''; 383 $_ctx->comment_preview['rawcontent'] = ''; 384 $_ctx->comment_preview['name'] = ''; 385 $_ctx->comment_preview['mail'] = ''; 386 $_ctx->comment_preview['site'] = ''; 387 $_ctx->comment_preview['preview'] = false; 388 $_ctx->comment_preview['remember'] = false; 389 390 $core->blog->withoutPassword(true); 391 392 if ($_ctx->posts->isEmpty()) 393 { 394 # The specified entry does not exist. 395 self::p404(); 396 } 397 else 398 { 399 $post_id = $_ctx->posts->post_id; 400 $post_password = $_ctx->posts->post_password; 401 402 # Password protected entry 403 if ($post_password != '' && !$_ctx->preview) 404 { 405 # Get passwords cookie 406 if (isset($_COOKIE['dc_passwd'])) { 407 $pwd_cookie = json_decode($_COOKIE['dc_passwd']); 408 if ($pwd_cookie === NULL) { 409 $pwd_cookie = array(); 410 } else { 411 $pwd_cookie = (array) $pwd_cookie; 412 } 413 } else { 414 $pwd_cookie = array(); 415 } 416 417 # Check for match 418 # Note: We must prefix post_id key with '#'' in pwd_cookie array in order to avoid integer conversion 419 # because MyArray["12345"] is treated as MyArray[12345] 420 if ((!empty($_POST['password']) && $_POST['password'] == $post_password) 421 || (isset($pwd_cookie['#'.$post_id]) && $pwd_cookie['#'.$post_id] == $post_password)) 422 { 423 $pwd_cookie['#'.$post_id] = $post_password; 424 setcookie('dc_passwd',json_encode($pwd_cookie),0,'/'); 425 } 426 else 427 { 428 self::serveDocument('password-form.html','text/html',false); 429 return; 430 } 431 } 432 433 $post_comment = 434 isset($_POST['c_name']) && isset($_POST['c_mail']) && 435 isset($_POST['c_site']) && isset($_POST['c_content']) && 436 $_ctx->posts->commentsActive(); 437 438 # Posting a comment 439 if ($post_comment) 440 { 441 # Spam trap 442 if (!empty($_POST['f_mail'])) { 443 http::head(412,'Precondition Failed'); 444 header('Content-Type: text/plain'); 445 echo "So Long, and Thanks For All the Fish"; 446 # Exits immediately the application to preserve the server. 447 exit; 448 } 449 450 $name = $_POST['c_name']; 451 $mail = $_POST['c_mail']; 452 $site = $_POST['c_site']; 453 $content = $_POST['c_content']; 454 $preview = !empty($_POST['preview']); 455 456 if ($content != '') 457 { 458 # --BEHAVIOR-- publicBeforeCommentTransform 459 $buffer = $core->callBehavior('publicBeforeCommentTransform',$content); 460 if ($buffer != '') { 461 $content = $buffer; 462 } else { 463 if ($core->blog->settings->system->wiki_comments) { 464 $core->initWikiComment(); 465 } else { 466 $core->initWikiSimpleComment(); 467 } 468 $content = $core->wikiTransform($content); 469 } 470 $content = $core->HTMLfilter($content); 471 } 472 473 $_ctx->comment_preview['content'] = $content; 474 $_ctx->comment_preview['rawcontent'] = $_POST['c_content']; 475 $_ctx->comment_preview['name'] = $name; 476 $_ctx->comment_preview['mail'] = $mail; 477 $_ctx->comment_preview['site'] = $site; 478 479 if ($preview) 480 { 481 # --BEHAVIOR-- publicBeforeCommentPreview 482 $core->callBehavior('publicBeforeCommentPreview',$_ctx->comment_preview); 483 484 $_ctx->comment_preview['preview'] = true; 485 } 486 else 487 { 488 # Post the comment 489 $cur = $core->con->openCursor($core->prefix.'comment'); 490 $cur->comment_author = $name; 491 $cur->comment_site = html::clean($site); 492 $cur->comment_email = html::clean($mail); 493 $cur->comment_content = $content; 494 $cur->post_id = $_ctx->posts->post_id; 495 $cur->comment_status = $core->blog->settings->system->comments_pub ? 1 : -1; 496 $cur->comment_ip = http::realIP(); 497 498 $redir = $_ctx->posts->getURL(); 499 $redir .= $core->blog->settings->system->url_scan == 'query_string' ? '&' : '?'; 500 501 try 502 { 503 if (!text::isEmail($cur->comment_email)) { 504 throw new Exception(__('You must provide a valid email address.')); 505 } 506 507 # --BEHAVIOR-- publicBeforeCommentCreate 508 $core->callBehavior('publicBeforeCommentCreate',$cur); 509 if ($cur->post_id) { 510 $comment_id = $core->blog->addComment($cur); 511 512 # --BEHAVIOR-- publicAfterCommentCreate 513 $core->callBehavior('publicAfterCommentCreate',$cur,$comment_id); 514 } 515 516 if ($cur->comment_status == 1) { 517 $redir_arg = 'pub=1'; 518 } else { 519 $redir_arg = 'pub=0'; 520 } 521 522 header('Location: '.$redir.$redir_arg); 523 } 524 catch (Exception $e) 525 { 526 $_ctx->form_error = $e->getMessage(); 527 $_ctx->form_error; 528 } 529 } 530 } 531 532 # The entry 533 if ($_ctx->posts->trackbacksActive()) { 534 header('X-Pingback: '.$core->blog->url.$core->url->getURLFor("xmlrpc",$core->blog->id)); 535 header('Link: <'.$core->blog->url.$core->url->getURLFor('webmention').'>; rel="webmention"'); 536 } 537 self::serveDocument('post.html'); 538 } 539 } 540 } 541 542 public static function preview($args) 543 { 544 $core = $GLOBALS['core']; 545 $_ctx = $GLOBALS['_ctx']; 546 547 if (!preg_match('#^(.+?)/([0-9a-z]{40})/(.+?)$#',$args,$m)) { 548 # The specified Preview URL is malformed. 549 self::p404(); 550 } 551 else 552 { 553 $user_id = $m[1]; 554 $user_key = $m[2]; 555 $post_url = $m[3]; 556 if (!$core->auth->checkUser($user_id,null,$user_key)) { 557 # The user has no access to the entry. 558 self::p404(); 559 } 560 else 561 { 562 $_ctx->preview = true; 563 if (defined ("DC_ADMIN_URL")) { 564 $_ctx->xframeoption=DC_ADMIN_URL; 565 } 566 self::post($post_url); 567 } 568 } 569 } 570 571 public static function feed($args) 572 { 573 $type = null; 574 $comments = false; 575 $cat_url = false; 576 $post_id = null; 577 $subtitle = ''; 578 579 $mime = 'application/xml'; 580 581 $_ctx =& $GLOBALS['_ctx']; 582 $core =& $GLOBALS['core']; 583 584 if (preg_match('!^([a-z]{2}(-[a-z]{2})?)/(.*)$!',$args,$m)) { 585 $params = new ArrayObject(array('lang' => $m[1])); 586 587 $args = $m[3]; 588 589 $core->callBehavior('publicFeedBeforeGetLangs',$params,$args); 590 591 $_ctx->langs = $core->blog->getLangs($params); 592 593 if ($_ctx->langs->isEmpty()) { 594 # The specified language does not exist. 595 self::p404(); 596 return; 597 } else { 598 $_ctx->cur_lang = $m[1]; 599 } 600 } 601 602 if (preg_match('#^rss2/xslt$#',$args,$m)) 603 { 604 # RSS XSLT stylesheet 605 self::serveDocument('rss2.xsl','text/xml'); 606 return; 607 } 608 elseif (preg_match('#^(atom|rss2)/comments/([0-9]+)$#',$args,$m)) 609 { 610 # Post comments feed 611 $type = $m[1]; 612 $comments = true; 613 $post_id = (integer) $m[2]; 614 } 615 elseif (preg_match('#^(?:category/(.+)/)?(atom|rss2)(/comments)?$#',$args,$m)) 616 { 617 # All posts or comments feed 618 $type = $m[2]; 619 $comments = !empty($m[3]); 620 if (!empty($m[1])) { 621 $cat_url = $m[1]; 622 } 623 } 624 else 625 { 626 # The specified Feed URL is malformed. 627 self::p404(); 628 return; 629 } 630 631 if ($cat_url) 632 { 633 $params = new ArrayObject(array( 634 'cat_url' => $cat_url, 635 'post_type' => 'post')); 636 637 $core->callBehavior('publicFeedBeforeGetCategories',$params,$args); 638 639 $_ctx->categories = $core->blog->getCategories($params); 640 641 if ($_ctx->categories->isEmpty()) { 642 # The specified category does no exist. 643 self::p404(); 644 return; 645 } 646 647 $subtitle = ' - '.$_ctx->categories->cat_title; 648 } 649 elseif ($post_id) 650 { 651 $params = new ArrayObject(array( 652 'post_id' => $post_id, 653 'post_type' => '')); 654 655 $core->callBehavior('publicFeedBeforeGetPosts',$params,$args); 656 657 $_ctx->posts = $core->blog->getPosts($params); 658 659 if ($_ctx->posts->isEmpty()) { 660 # The specified post does not exist. 661 self::p404(); 662 return; 663 } 664 665 $subtitle = ' - '.$_ctx->posts->post_title; 666 } 667 668 $tpl = $type; 669 if ($comments) { 670 $tpl .= '-comments'; 671 $_ctx->nb_comment_per_page = $core->blog->settings->system->nb_comment_per_feed; 672 } else { 673 $_ctx->nb_entry_per_page = $core->blog->settings->system->nb_post_per_feed; 674 $_ctx->short_feed_items = $core->blog->settings->system->short_feed_items; 675 } 676 $tpl .= '.xml'; 677 678 if ($type == 'atom') { 679 $mime = 'application/atom+xml'; 680 } 681 682 $_ctx->feed_subtitle = $subtitle; 683 684 header('X-Robots-Tag: '.context::robotsPolicy($core->blog->settings->system->robots_policy,'')); 685 self::serveDocument($tpl,$mime); 686 if (!$comments && !$cat_url) { 687 $core->blog->publishScheduledEntries(); 688 } 689 } 690 691 public static function trackback($args) 692 { 693 if (!preg_match('/^[0-9]+$/',$args)) { 694 # The specified trackback URL is not an number 695 self::p404(); 696 } else { 697 $core =& $GLOBALS['core']; 698 699 // Save locally post_id from args 700 $post_id = (integer) $args; 701 702 if (!is_array($args)) $args = array(); 703 $args['post_id'] = $post_id; 704 $args['type'] = 'trackback'; 705 706 # --BEHAVIOR-- publicBeforeReceiveTrackback 707 $core->callBehavior('publicBeforeReceiveTrackback',$core,$args); 708 709 $tb = new dcTrackback($core); 710 $tb->receiveTrackback($post_id); 711 } 712 } 713 714 public static function webmention($args) 715 { 716 $core =& $GLOBALS['core']; 717 if (!is_array($args)) $args = array(); 718 $args['type'] = 'webmention'; 719 720 # --BEHAVIOR-- publicBeforeReceiveTrackback 721 $core->callBehavior('publicBeforeReceiveTrackback',$core,$args); 722 723 $tb = new dcTrackback($core); 724 $tb->receiveWebmention(); 725 } 726 727 public static function rsd($args) 728 { 729 $core =& $GLOBALS['core']; 730 http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']); 731 732 header('Content-Type: text/xml; charset=UTF-8'); 733 echo 734 '<?xml version="1.0" encoding="UTF-8"?>'."\n". 735 '<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">'."\n". 736 "<service>\n". 737 " <engineName>Dotclear</engineName>\n". 738 " <engineLink>http://www.dotclear.org/</engineLink>\n". 739 ' <homePageLink>'.html::escapeHTML($core->blog->url)."</homePageLink>\n"; 740 741 if ($core->blog->settings->system->enable_xmlrpc) 742 { 743 $u = sprintf(DC_XMLRPC_URL,$core->blog->url,$core->blog->id); 744 745 echo 746 " <apis>\n". 747 ' <api name="WordPress" blogID="1" preferred="true" apiLink="'.$u.'"/>'."\n". 748 ' <api name="Movable Type" blogID="1" preferred="false" apiLink="'.$u.'"/>'."\n". 749 ' <api name="MetaWeblog" blogID="1" preferred="false" apiLink="'.$u.'"/>'."\n". 750 ' <api name="Blogger" blogID="1" preferred="false" apiLink="'.$u.'"/>'."\n". 751 " </apis>\n"; 752 } 753 754 echo 755 "</service>\n". 756 "</rsd>\n"; 757 } 758 759 public static function xmlrpc($args) 760 { 761 $core =& $GLOBALS['core']; 762 $blog_id = preg_replace('#^([^/]*).*#','$1',$args); 763 $server = new dcXmlRpc($core,$blog_id); 764 $server->serve(); 765 } 16 public $args; 17 18 public function getURLFor($type, $value = '') 19 { 20 $core = &$GLOBALS['core']; 21 $url = $core->callBehavior("publicGetURLFor", $type, $value); 22 if (!$url) { 23 $url = $this->getBase($type); 24 if ($value) { 25 if ($url) { 26 $url .= '/'; 27 } 28 $url .= $value; 29 } 30 } 31 return $url; 32 } 33 34 public function register($type, $url, $representation, $handler) 35 { 36 $core = &$GLOBALS['core']; 37 $t = new ArrayObject(array($type, $url, $representation, $handler)); 38 $core->callBehavior("publicRegisterURL", $t); 39 parent::register($t[0], $t[1], $t[2], $t[3]); 40 } 41 42 public static function p404() 43 { 44 throw new Exception("Page not found", 404); 45 } 46 47 public static function default404($args, $type, $e) 48 { 49 if ($e->getCode() != 404) { 50 throw $e; 51 } 52 $_ctx = &$GLOBALS['_ctx']; 53 $core = $GLOBALS['core']; 54 55 header('Content-Type: text/html; charset=UTF-8'); 56 http::head(404, 'Not Found'); 57 $core->url->type = '404'; 58 $_ctx->current_tpl = '404.html'; 59 $_ctx->content_type = 'text/html'; 60 61 echo $core->tpl->getData($_ctx->current_tpl); 62 63 # --BEHAVIOR-- publicAfterDocument 64 $core->callBehavior('publicAfterDocument', $core); 65 exit; 66 } 67 68 protected static function getPageNumber(&$args) 69 { 70 if (preg_match('#(^|/)page/([0-9]+)$#', $args, $m)) { 71 $n = (integer) $m[2]; 72 if ($n > 0) { 73 $args = preg_replace('#(^|/)page/([0-9]+)$#', '', $args); 74 return $n; 75 } 76 } 77 78 return false; 79 } 80 81 protected static function serveDocument($tpl, $content_type = 'text/html', $http_cache = true, $http_etag = true) 82 { 83 $_ctx = &$GLOBALS['_ctx']; 84 $core = &$GLOBALS['core']; 85 86 if ($_ctx->nb_entry_per_page === null) { 87 $_ctx->nb_entry_per_page = $core->blog->settings->system->nb_post_per_page; 88 } 89 if ($_ctx->nb_entry_first_page === null) { 90 $_ctx->nb_entry_first_page = $_ctx->nb_entry_per_page; 91 } 92 93 $tpl_file = $core->tpl->getFilePath($tpl); 94 95 if (!$tpl_file) { 96 throw new Exception('Unable to find template '); 97 } 98 99 $result = new ArrayObject; 100 101 $_ctx->current_tpl = $tpl; 102 $_ctx->content_type = $content_type; 103 $_ctx->http_cache = $http_cache; 104 $_ctx->http_etag = $http_etag; 105 $core->callBehavior('urlHandlerBeforeGetData', $_ctx); 106 107 if ($_ctx->http_cache) { 108 $GLOBALS['mod_files'][] = $tpl_file; 109 http::cache($GLOBALS['mod_files'], $GLOBALS['mod_ts']); 110 } 111 112 header('Content-Type: ' . $_ctx->content_type . '; charset=UTF-8'); 113 114 // Additional headers 115 $headers = new ArrayObject; 116 if ($core->blog->settings->system->prevents_clickjacking) { 117 if ($_ctx->exists('xframeoption')) { 118 $url = parse_url($_ctx->xframeoption); 119 $header = sprintf('X-Frame-Options: %s', 120 is_array($url) ? ("ALLOW-FROM " . $url['scheme'] . '://' . $url['host']) : 'SAMEORIGIN'); 121 } else { 122 // Prevents Clickjacking as far as possible 123 $header = 'X-Frame-Options: SAMEORIGIN'; // FF 3.6.9+ Chrome 4.1+ IE 8+ Safari 4+ Opera 10.5+ 124 } 125 $headers[] = $header; 126 } 127 # --BEHAVIOR-- urlHandlerServeDocumentHeaders 128 $core->callBehavior('urlHandlerServeDocumentHeaders', $headers); 129 130 // Send additional headers if any 131 foreach ($headers as $header) { 132 header($header); 133 } 134 135 $result['content'] = $core->tpl->getData($_ctx->current_tpl); 136 $result['content_type'] = $_ctx->content_type; 137 $result['tpl'] = $_ctx->current_tpl; 138 $result['blogupddt'] = $core->blog->upddt; 139 $result['headers'] = $headers; 140 141 # --BEHAVIOR-- urlHandlerServeDocument 142 $core->callBehavior('urlHandlerServeDocument', $result); 143 144 if ($_ctx->http_cache && $_ctx->http_etag) { 145 http::etag($result['content'], http::getSelfURI()); 146 } 147 echo $result['content']; 148 } 149 150 public function getDocument() 151 { 152 $core = &$GLOBALS['core']; 153 154 $type = $args = ''; 155 156 if ($this->mode == 'path_info') { 157 $part = substr($_SERVER['PATH_INFO'], 1); 158 } else { 159 $part = ''; 160 161 $qs = $this->parseQueryString(); 162 163 # Recreates some _GET and _REQUEST pairs 164 if (!empty($qs)) { 165 foreach ($_GET as $k => $v) { 166 if (isset($_REQUEST[$k])) { 167 unset($_REQUEST[$k]); 168 } 169 } 170 $_GET = $qs; 171 $_REQUEST = array_merge($qs, $_REQUEST); 172 173 foreach ($qs as $k => $v) { 174 if ($v === null) { 175 $part = $k; 176 unset($_GET[$k]); 177 unset($_REQUEST[$k]); 178 } 179 break; 180 } 181 } 182 } 183 184 $_SERVER['URL_REQUEST_PART'] = $part; 185 186 $this->getArgs($part, $type, $this->args); 187 188 # --BEHAVIOR-- urlHandlerGetArgsDocument 189 $core->callBehavior('urlHandlerGetArgsDocument', $this); 190 191 if (!$type) { 192 $this->type = 'default'; 193 $this->callDefaultHandler($this->args); 194 } else { 195 $this->type = $type; 196 $this->callHandler($type, $this->args); 197 } 198 } 199 200 public static function home($args) 201 { 202 $n = self::getPageNumber($args); 203 204 if ($args && !$n) { 205 # "Then specified URL went unrecognized by all URL handlers and 206 # defaults to the home page, but is not a page number. 207 self::p404(); 208 } else { 209 $_ctx = &$GLOBALS['_ctx']; 210 $core = &$GLOBALS['core']; 211 212 if ($n) { 213 $GLOBALS['_page_number'] = $n; 214 $core->url->type = $n > 1 ? 'default-page' : 'default'; 215 } 216 217 if (empty($_GET['q'])) { 218 if ($core->blog->settings->system->nb_post_for_home !== null) { 219 $_ctx->nb_entry_first_page = $core->blog->settings->system->nb_post_for_home; 220 } 221 self::serveDocument('home.html'); 222 $core->blog->publishScheduledEntries(); 223 } else { 224 self::search(); 225 } 226 } 227 } 228 229 public static function search() 230 { 231 $_ctx = &$GLOBALS['_ctx']; 232 $core = &$GLOBALS['core']; 233 234 if ($core->blog->settings->system->no_search) { 235 236 # Search is disabled for this blog. 237 self::p404(); 238 239 } else { 240 241 $core->url->type = 'search'; 242 243 $GLOBALS['_search'] = !empty($_GET['q']) ? rawurldecode($_GET['q']) : ''; 244 if ($GLOBALS['_search']) { 245 $params = new ArrayObject(array('search' => $GLOBALS['_search'])); 246 $core->callBehavior('publicBeforeSearchCount', $params); 247 $GLOBALS['_search_count'] = $core->blog->getPosts($params, true)->f(0); 248 } 249 250 self::serveDocument('search.html'); 251 } 252 } 253 254 public static function lang($args) 255 { 256 $_ctx = &$GLOBALS['_ctx']; 257 $core = &$GLOBALS['core']; 258 259 $n = self::getPageNumber($args); 260 $params = new ArrayObject(array( 261 'lang' => $args)); 262 263 $core->callBehavior('publicLangBeforeGetLangs', $params, $args); 264 265 $_ctx->langs = $core->blog->getLangs($params); 266 267 if ($_ctx->langs->isEmpty()) { 268 # The specified language does not exist. 269 self::p404(); 270 } else { 271 if ($n) { 272 $GLOBALS['_page_number'] = $n; 273 } 274 $_ctx->cur_lang = $args; 275 self::home(null); 276 } 277 } 278 279 public static function category($args) 280 { 281 $_ctx = &$GLOBALS['_ctx']; 282 $core = &$GLOBALS['core']; 283 284 $n = self::getPageNumber($args); 285 286 if ($args == '' && !$n) { 287 # No category was specified. 288 self::p404(); 289 } else { 290 $params = new ArrayObject(array( 291 'cat_url' => $args, 292 'post_type' => 'post', 293 'without_empty' => false)); 294 295 $core->callBehavior('publicCategoryBeforeGetCategories', $params, $args); 296 297 $_ctx->categories = $core->blog->getCategories($params); 298 299 if ($_ctx->categories->isEmpty()) { 300 # The specified category does no exist. 301 self::p404(); 302 } else { 303 if ($n) { 304 $GLOBALS['_page_number'] = $n; 305 } 306 self::serveDocument('category.html'); 307 } 308 } 309 } 310 311 public static function archive($args) 312 { 313 $_ctx = &$GLOBALS['_ctx']; 314 $core = &$GLOBALS['core']; 315 316 $year = $month = $cat_url = null; 317 # Nothing or year and month 318 if ($args == '') { 319 self::serveDocument('archive.html'); 320 } elseif (preg_match('|^/([0-9]{4})/([0-9]{2})$|', $args, $m)) { 321 $params = new ArrayObject(array( 322 'year' => $m[1], 323 'month' => $m[2], 324 'type' => 'month')); 325 326 $core->callBehavior('publicArchiveBeforeGetDates', $params, $args); 327 328 $_ctx->archives = $core->blog->getDates($params); 329 330 if ($_ctx->archives->isEmpty()) { 331 # There is no entries for the specified period. 332 self::p404(); 333 } else { 334 self::serveDocument('archive_month.html'); 335 } 336 } else { 337 # The specified URL is not a date. 338 self::p404(); 339 } 340 } 341 342 public static function post($args) 343 { 344 if ($args == '') { 345 # No entry was specified. 346 self::p404(); 347 } else { 348 $_ctx = &$GLOBALS['_ctx']; 349 $core = &$GLOBALS['core']; 350 351 $core->blog->withoutPassword(false); 352 353 $params = new ArrayObject(array( 354 'post_url' => $args)); 355 356 $core->callBehavior('publicPostBeforeGetPosts', $params, $args); 357 358 $_ctx->posts = $core->blog->getPosts($params); 359 360 $_ctx->comment_preview = new ArrayObject(); 361 $_ctx->comment_preview['content'] = ''; 362 $_ctx->comment_preview['rawcontent'] = ''; 363 $_ctx->comment_preview['name'] = ''; 364 $_ctx->comment_preview['mail'] = ''; 365 $_ctx->comment_preview['site'] = ''; 366 $_ctx->comment_preview['preview'] = false; 367 $_ctx->comment_preview['remember'] = false; 368 369 $core->blog->withoutPassword(true); 370 371 if ($_ctx->posts->isEmpty()) { 372 # The specified entry does not exist. 373 self::p404(); 374 } else { 375 $post_id = $_ctx->posts->post_id; 376 $post_password = $_ctx->posts->post_password; 377 378 # Password protected entry 379 if ($post_password != '' && !$_ctx->preview) { 380 # Get passwords cookie 381 if (isset($_COOKIE['dc_passwd'])) { 382 $pwd_cookie = json_decode($_COOKIE['dc_passwd']); 383 if ($pwd_cookie === null) { 384 $pwd_cookie = array(); 385 } else { 386 $pwd_cookie = (array) $pwd_cookie; 387 } 388 } else { 389 $pwd_cookie = array(); 390 } 391 392 # Check for match 393 # Note: We must prefix post_id key with '#'' in pwd_cookie array in order to avoid integer conversion 394 # because MyArray["12345"] is treated as MyArray[12345] 395 if ((!empty($_POST['password']) && $_POST['password'] == $post_password) 396 || (isset($pwd_cookie['#' . $post_id]) && $pwd_cookie['#' . $post_id] == $post_password)) { 397 $pwd_cookie['#' . $post_id] = $post_password; 398 setcookie('dc_passwd', json_encode($pwd_cookie), 0, '/'); 399 } else { 400 self::serveDocument('password-form.html', 'text/html', false); 401 return; 402 } 403 } 404 405 $post_comment = 406 isset($_POST['c_name']) && isset($_POST['c_mail']) && 407 isset($_POST['c_site']) && isset($_POST['c_content']) && 408 $_ctx->posts->commentsActive(); 409 410 # Posting a comment 411 if ($post_comment) { 412 # Spam trap 413 if (!empty($_POST['f_mail'])) { 414 http::head(412, 'Precondition Failed'); 415 header('Content-Type: text/plain'); 416 echo "So Long, and Thanks For All the Fish"; 417 # Exits immediately the application to preserve the server. 418 exit; 419 } 420 421 $name = $_POST['c_name']; 422 $mail = $_POST['c_mail']; 423 $site = $_POST['c_site']; 424 $content = $_POST['c_content']; 425 $preview = !empty($_POST['preview']); 426 427 if ($content != '') { 428 # --BEHAVIOR-- publicBeforeCommentTransform 429 $buffer = $core->callBehavior('publicBeforeCommentTransform', $content); 430 if ($buffer != '') { 431 $content = $buffer; 432 } else { 433 if ($core->blog->settings->system->wiki_comments) { 434 $core->initWikiComment(); 435 } else { 436 $core->initWikiSimpleComment(); 437 } 438 $content = $core->wikiTransform($content); 439 } 440 $content = $core->HTMLfilter($content); 441 } 442 443 $_ctx->comment_preview['content'] = $content; 444 $_ctx->comment_preview['rawcontent'] = $_POST['c_content']; 445 $_ctx->comment_preview['name'] = $name; 446 $_ctx->comment_preview['mail'] = $mail; 447 $_ctx->comment_preview['site'] = $site; 448 449 if ($preview) { 450 # --BEHAVIOR-- publicBeforeCommentPreview 451 $core->callBehavior('publicBeforeCommentPreview', $_ctx->comment_preview); 452 453 $_ctx->comment_preview['preview'] = true; 454 } else { 455 # Post the comment 456 $cur = $core->con->openCursor($core->prefix . 'comment'); 457 $cur->comment_author = $name; 458 $cur->comment_site = html::clean($site); 459 $cur->comment_email = html::clean($mail); 460 $cur->comment_content = $content; 461 $cur->post_id = $_ctx->posts->post_id; 462 $cur->comment_status = $core->blog->settings->system->comments_pub ? 1 : -1; 463 $cur->comment_ip = http::realIP(); 464 465 $redir = $_ctx->posts->getURL(); 466 $redir .= $core->blog->settings->system->url_scan == 'query_string' ? '&' : '?'; 467 468 try 469 { 470 if (!text::isEmail($cur->comment_email)) { 471 throw new Exception(__('You must provide a valid email address.')); 472 } 473 474 # --BEHAVIOR-- publicBeforeCommentCreate 475 $core->callBehavior('publicBeforeCommentCreate', $cur); 476 if ($cur->post_id) { 477 $comment_id = $core->blog->addComment($cur); 478 479 # --BEHAVIOR-- publicAfterCommentCreate 480 $core->callBehavior('publicAfterCommentCreate', $cur, $comment_id); 481 } 482 483 if ($cur->comment_status == 1) { 484 $redir_arg = 'pub=1'; 485 } else { 486 $redir_arg = 'pub=0'; 487 } 488 489 header('Location: ' . $redir . $redir_arg); 490 } catch (Exception $e) { 491 $_ctx->form_error = $e->getMessage(); 492 $_ctx->form_error; 493 } 494 } 495 } 496 497 # The entry 498 if ($_ctx->posts->trackbacksActive()) { 499 header('X-Pingback: ' . $core->blog->url . $core->url->getURLFor("xmlrpc", $core->blog->id)); 500 header('Link: <' . $core->blog->url . $core->url->getURLFor('webmention') . '>; rel="webmention"'); 501 } 502 self::serveDocument('post.html'); 503 } 504 } 505 } 506 507 public static function preview($args) 508 { 509 $core = $GLOBALS['core']; 510 $_ctx = $GLOBALS['_ctx']; 511 512 if (!preg_match('#^(.+?)/([0-9a-z]{40})/(.+?)$#', $args, $m)) { 513 # The specified Preview URL is malformed. 514 self::p404(); 515 } else { 516 $user_id = $m[1]; 517 $user_key = $m[2]; 518 $post_url = $m[3]; 519 if (!$core->auth->checkUser($user_id, null, $user_key)) { 520 # The user has no access to the entry. 521 self::p404(); 522 } else { 523 $_ctx->preview = true; 524 if (defined("DC_ADMIN_URL")) { 525 $_ctx->xframeoption = DC_ADMIN_URL; 526 } 527 self::post($post_url); 528 } 529 } 530 } 531 532 public static function feed($args) 533 { 534 $type = null; 535 $comments = false; 536 $cat_url = false; 537 $post_id = null; 538 $subtitle = ''; 539 540 $mime = 'application/xml'; 541 542 $_ctx = &$GLOBALS['_ctx']; 543 $core = &$GLOBALS['core']; 544 545 if (preg_match('!^([a-z]{2}(-[a-z]{2})?)/(.*)$!', $args, $m)) { 546 $params = new ArrayObject(array('lang' => $m[1])); 547 548 $args = $m[3]; 549 550 $core->callBehavior('publicFeedBeforeGetLangs', $params, $args); 551 552 $_ctx->langs = $core->blog->getLangs($params); 553 554 if ($_ctx->langs->isEmpty()) { 555 # The specified language does not exist. 556 self::p404(); 557 return; 558 } else { 559 $_ctx->cur_lang = $m[1]; 560 } 561 } 562 563 if (preg_match('#^rss2/xslt$#', $args, $m)) { 564 # RSS XSLT stylesheet 565 self::serveDocument('rss2.xsl', 'text/xml'); 566 return; 567 } elseif (preg_match('#^(atom|rss2)/comments/([0-9]+)$#', $args, $m)) { 568 # Post comments feed 569 $type = $m[1]; 570 $comments = true; 571 $post_id = (integer) $m[2]; 572 } elseif (preg_match('#^(?:category/(.+)/)?(atom|rss2)(/comments)?$#', $args, $m)) { 573 # All posts or comments feed 574 $type = $m[2]; 575 $comments = !empty($m[3]); 576 if (!empty($m[1])) { 577 $cat_url = $m[1]; 578 } 579 } else { 580 # The specified Feed URL is malformed. 581 self::p404(); 582 return; 583 } 584 585 if ($cat_url) { 586 $params = new ArrayObject(array( 587 'cat_url' => $cat_url, 588 'post_type' => 'post')); 589 590 $core->callBehavior('publicFeedBeforeGetCategories', $params, $args); 591 592 $_ctx->categories = $core->blog->getCategories($params); 593 594 if ($_ctx->categories->isEmpty()) { 595 # The specified category does no exist. 596 self::p404(); 597 return; 598 } 599 600 $subtitle = ' - ' . $_ctx->categories->cat_title; 601 } elseif ($post_id) { 602 $params = new ArrayObject(array( 603 'post_id' => $post_id, 604 'post_type' => '')); 605 606 $core->callBehavior('publicFeedBeforeGetPosts', $params, $args); 607 608 $_ctx->posts = $core->blog->getPosts($params); 609 610 if ($_ctx->posts->isEmpty()) { 611 # The specified post does not exist. 612 self::p404(); 613 return; 614 } 615 616 $subtitle = ' - ' . $_ctx->posts->post_title; 617 } 618 619 $tpl = $type; 620 if ($comments) { 621 $tpl .= '-comments'; 622 $_ctx->nb_comment_per_page = $core->blog->settings->system->nb_comment_per_feed; 623 } else { 624 $_ctx->nb_entry_per_page = $core->blog->settings->system->nb_post_per_feed; 625 $_ctx->short_feed_items = $core->blog->settings->system->short_feed_items; 626 } 627 $tpl .= '.xml'; 628 629 if ($type == 'atom') { 630 $mime = 'application/atom+xml'; 631 } 632 633 $_ctx->feed_subtitle = $subtitle; 634 635 header('X-Robots-Tag: ' . context::robotsPolicy($core->blog->settings->system->robots_policy, '')); 636 self::serveDocument($tpl, $mime); 637 if (!$comments && !$cat_url) { 638 $core->blog->publishScheduledEntries(); 639 } 640 } 641 642 public static function trackback($args) 643 { 644 if (!preg_match('/^[0-9]+$/', $args)) { 645 # The specified trackback URL is not an number 646 self::p404(); 647 } else { 648 $core = &$GLOBALS['core']; 649 650 // Save locally post_id from args 651 $post_id = (integer) $args; 652 653 if (!is_array($args)) { 654 $args = array(); 655 } 656 657 $args['post_id'] = $post_id; 658 $args['type'] = 'trackback'; 659 660 # --BEHAVIOR-- publicBeforeReceiveTrackback 661 $core->callBehavior('publicBeforeReceiveTrackback', $core, $args); 662 663 $tb = new dcTrackback($core); 664 $tb->receiveTrackback($post_id); 665 } 666 } 667 668 public static function webmention($args) 669 { 670 $core = &$GLOBALS['core']; 671 if (!is_array($args)) { 672 $args = array(); 673 } 674 675 $args['type'] = 'webmention'; 676 677 # --BEHAVIOR-- publicBeforeReceiveTrackback 678 $core->callBehavior('publicBeforeReceiveTrackback', $core, $args); 679 680 $tb = new dcTrackback($core); 681 $tb->receiveWebmention(); 682 } 683 684 public static function rsd($args) 685 { 686 $core = &$GLOBALS['core']; 687 http::cache($GLOBALS['mod_files'], $GLOBALS['mod_ts']); 688 689 header('Content-Type: text/xml; charset=UTF-8'); 690 echo 691 '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . 692 '<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">' . "\n" . 693 "<service>\n" . 694 " <engineName>Dotclear</engineName>\n" . 695 " <engineLink>http://www.dotclear.org/</engineLink>\n" . 696 ' <homePageLink>' . html::escapeHTML($core->blog->url) . "</homePageLink>\n"; 697 698 if ($core->blog->settings->system->enable_xmlrpc) { 699 $u = sprintf(DC_XMLRPC_URL, $core->blog->url, $core->blog->id); 700 701 echo 702 " <apis>\n" . 703 ' <api name="WordPress" blogID="1" preferred="true" apiLink="' . $u . '"/>' . "\n" . 704 ' <api name="Movable Type" blogID="1" preferred="false" apiLink="' . $u . '"/>' . "\n" . 705 ' <api name="MetaWeblog" blogID="1" preferred="false" apiLink="' . $u . '"/>' . "\n" . 706 ' <api name="Blogger" blogID="1" preferred="false" apiLink="' . $u . '"/>' . "\n" . 707 " </apis>\n"; 708 } 709 710 echo 711 "</service>\n" . 712 "</rsd>\n"; 713 } 714 715 public static function xmlrpc($args) 716 { 717 $core = &$GLOBALS['core']; 718 $blog_id = preg_replace('#^([^/]*).*#', '$1', $args); 719 $server = new dcXmlRpc($core, $blog_id); 720 $server->serve(); 721 } 766 722 }
Note: See TracChangeset
for help on using the changeset viewer.