| [ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Atom Publishing Protocol support for WordPress 4 * 5 * @version 1.0.5-dc 6 */ 7 8 /** 9 * WordPress is handling an Atom Publishing Protocol request. 10 * 11 * @var bool 12 */ 13 define('APP_REQUEST', true); 14 15 /** Set up WordPress environment */ 16 require_once ('./wp-load.php'); 17 18 /** Atom Publishing Protocol Class */ 19 require_once(ABSPATH . WPINC . '/atomlib.php'); 20 21 /** Admin Image API for metadata updating */ 22 require_once (ABSPATH . '/wp-admin/includes/image.php'); 23 24 $_SERVER['PATH_INFO'] = preg_replace( '/.*\/wp-app\.php/', '', $_SERVER['REQUEST_URI'] ); 25 26 /** 27 * Whether to enable Atom Publishing Protocol Logging. 28 * 29 * @name app_logging 30 * @var int|bool 31 */ 32 $app_logging = 0; 33 34 /** 35 * Whether to always authenticate user. Permanently set to true. 36 * 37 * @name always_authenticate 38 * @var int|bool 39 * @todo Should be an option somewhere 40 */ 41 $always_authenticate = 1; 42 43 /** 44 * Writes logging info to a file. 45 * 46 * @since 2.2.0 47 * @uses $app_logging 48 * @package WordPress 49 * @subpackage Logging 50 * 51 * @param string $label Type of logging 52 * @param string $msg Information describing logging reason. 53 */ 54 function log_app($label,$msg) { 55 global $app_logging; 56 if ($app_logging) { 57 $fp = fopen( 'wp-app.log', 'a+'); 58 $date = gmdate( 'Y-m-d H:i:s' ); 59 fwrite($fp, "\n\n$date - $label\n$msg\n"); 60 fclose($fp); 61 } 62 } 63 64 /** 65 * Filter to add more post statuses. 66 * 67 * @since 2.2.0 68 * 69 * @param string $where SQL statement to filter. 70 * @return string Filtered SQL statement with added post_status for where clause. 71 */ 72 function wa_posts_where_include_drafts_filter($where) { 73 $where = str_replace("post_status = 'publish'","post_status = 'publish' OR post_status = 'future' OR post_status = 'draft' OR post_status = 'inherit'", $where); 74 return $where; 75 76 } 77 add_filter('posts_where', 'wa_posts_where_include_drafts_filter'); 78 79 /** 80 * WordPress AtomPub API implementation. 81 * 82 * @package WordPress 83 * @subpackage Publishing 84 * @since 2.2.0 85 */ 86 class AtomServer { 87 88 /** 89 * ATOM content type. 90 * 91 * @since 2.2.0 92 * @var string 93 */ 94 var $ATOM_CONTENT_TYPE = 'application/atom+xml'; 95 96 /** 97 * Categories ATOM content type. 98 * 99 * @since 2.2.0 100 * @var string 101 */ 102 var $CATEGORIES_CONTENT_TYPE = 'application/atomcat+xml'; 103 104 /** 105 * Service ATOM content type. 106 * 107 * @since 2.3.0 108 * @var string 109 */ 110 var $SERVICE_CONTENT_TYPE = 'application/atomsvc+xml'; 111 112 /** 113 * ATOM XML namespace. 114 * 115 * @since 2.3.0 116 * @var string 117 */ 118 var $ATOM_NS = 'http://www.w3.org/2005/Atom'; 119 120 /** 121 * ATOMPUB XML namespace. 122 * 123 * @since 2.3.0 124 * @var string 125 */ 126 var $ATOMPUB_NS = 'http://www.w3.org/2007/app'; 127 128 /** 129 * Entries path. 130 * 131 * @since 2.2.0 132 * @var string 133 */ 134 var $ENTRIES_PATH = "posts"; 135 136 /** 137 * Categories path. 138 * 139 * @since 2.2.0 140 * @var string 141 */ 142 var $CATEGORIES_PATH = "categories"; 143 144 /** 145 * Media path. 146 * 147 * @since 2.2.0 148 * @var string 149 */ 150 var $MEDIA_PATH = "attachments"; 151 152 /** 153 * Entry path. 154 * 155 * @since 2.2.0 156 * @var string 157 */ 158 var $ENTRY_PATH = "post"; 159 160 /** 161 * Service path. 162 * 163 * @since 2.2.0 164 * @var string 165 */ 166 var $SERVICE_PATH = "service"; 167 168 /** 169 * Media single path. 170 * 171 * @since 2.2.0 172 * @var string 173 */ 174 var $MEDIA_SINGLE_PATH = "attachment"; 175 176 /** 177 * ATOMPUB parameters. 178 * 179 * @since 2.2.0 180 * @var array 181 */ 182 var $params = array(); 183 184 /** 185 * Supported ATOMPUB media types. 186 * 187 * @since 2.3.0 188 * @var array 189 */ 190 var $media_content_types = array('image/*','audio/*','video/*'); 191 192 /** 193 * ATOMPUB content type(s). 194 * 195 * @since 2.2.0 196 * @var array 197 */ 198 var $atom_content_types = array('application/atom+xml'); 199 200 /** 201 * ATOMPUB methods. 202 * 203 * @since 2.2.0 204 * @var unknown_type 205 */ 206 var $selectors = array(); 207 208 /** 209 * Whether to do output. 210 * 211 * Support for head. 212 * 213 * @since 2.2.0 214 * @var bool 215 */ 216 var $do_output = true; 217 218 /** 219 * Constructor - Sets up object properties. 220 * 221 * @since 2.2.0 222 * @return AtomServer 223 */ 224 function __construct() { 225 226 $var_by_ref = explode( '/', $_SERVER['SCRIPT_NAME'] ); 227 $this->script_name = array_pop( $var_by_ref ); 228 $this->app_base = site_url( $this->script_name . '/' ); 229 230 $this->selectors = array( 231 '@/service$@' => 232 array('GET' => 'get_service'), 233 '@/categories$@' => 234 array('GET' => 'get_categories_xml'), 235 '@/post/(\d+)$@' => 236 array('GET' => 'get_post', 237 'PUT' => 'put_post', 238 'DELETE' => 'delete_post'), 239 '@/posts/?(\d+)?$@' => 240 array('GET' => 'get_posts', 241 'POST' => 'create_post'), 242 '@/attachments/?(\d+)?$@' => 243 array('GET' => 'get_attachment', 244 'POST' => 'create_attachment'), 245 '@/attachment/file/(\d+)$@' => 246 array('GET' => 'get_file', 247 'PUT' => 'put_file', 248 'DELETE' => 'delete_file'), 249 '@/attachment/(\d+)$@' => 250 array('GET' => 'get_attachment', 251 'PUT' => 'put_attachment', 252 'DELETE' => 'delete_attachment'), 253 ); 254 } 255 256 /** 257 * Handle ATOMPUB request. 258 * 259 * @since 2.2.0 260 */ 261 function handle_request() { 262 global $always_authenticate; 263 264 if ( !empty( $_SERVER['ORIG_PATH_INFO'] ) ) 265 $path = $_SERVER['ORIG_PATH_INFO']; 266 else 267 $path = $_SERVER['PATH_INFO']; 268 269 $method = $_SERVER['REQUEST_METHOD']; 270 271 log_app('REQUEST',"$method $path\n================"); 272 273 $this->process_conditionals(); 274 //$this->process_conditionals(); 275 276 // exception case for HEAD (treat exactly as GET, but don't output) 277 if ($method == 'HEAD') { 278 $this->do_output = false; 279 $method = 'GET'; 280 } 281 282 // redirect to /service in case no path is found. 283 if (strlen($path) == 0 || $path == '/') 284 $this->redirect($this->get_service_url()); 285 286 // check to see if AtomPub is enabled 287 if ( !get_option( 'enable_app' ) ) 288 $this->forbidden( sprintf( __( 'AtomPub services are disabled on this site. An admin user can enable them at %s' ), admin_url('options-writing.php') ) ); 289 290 // dispatch 291 foreach ( $this->selectors as $regex => $funcs ) { 292 if ( preg_match($regex, $path, $matches) ) { 293 if ( isset($funcs[$method]) ) { 294 295 // authenticate regardless of the operation and set the current 296 // user. each handler will decide if auth is required or not. 297 if ( !$this->authenticate() ) { 298 if ( $always_authenticate ) 299 $this->auth_required('Credentials required.'); 300 } 301 302 array_shift($matches); 303 call_user_func_array(array(&$this,$funcs[$method]), $matches); 304 exit(); 305 } else { 306 // only allow what we have handlers for... 307 $this->not_allowed(array_keys($funcs)); 308 } 309 } 310 } 311 312 // oops, nothing found 313 $this->not_found(); 314 } 315 316 /** 317 * Retrieve XML for ATOMPUB service. 318 * 319 * @since 2.2.0 320 */ 321 function get_service() { 322 log_app('function','get_service()'); 323 324 if ( !current_user_can( 'edit_posts' ) ) 325 $this->auth_required( __( 'Sorry, you do not have the right to access this site.' ) ); 326 327 $entries_url = esc_attr($this->get_entries_url()); 328 $categories_url = esc_attr($this->get_categories_url()); 329 $media_url = esc_attr($this->get_attachments_url()); 330 $accepted_media_types = ''; 331 foreach ($this->media_content_types as $med) { 332 $accepted_media_types = $accepted_media_types . "<accept>" . $med . "</accept>"; 333 } 334 $atom_prefix="atom"; 335 $atom_blogname = get_bloginfo('name'); 336 $service_doc = <<<EOD 337 <service xmlns="$this->ATOMPUB_NS" xmlns:$atom_prefix="$this->ATOM_NS"> 338 <workspace> 339 <$atom_prefix:title>$atom_blogname Workspace</$atom_prefix:title> 340 <collection href="$entries_url"> 341 <$atom_prefix:title>$atom_blogname Posts</$atom_prefix:title> 342 <accept>$this->ATOM_CONTENT_TYPE;type=entry</accept> 343 <categories href="$categories_url" /> 344 </collection> 345 <collection href="$media_url"> 346 <$atom_prefix:title>$atom_blogname Media</$atom_prefix:title> 347 $accepted_media_types 348 </collection> 349 </workspace> 350 </service> 351 352 EOD; 353 354 $this->output($service_doc, $this->SERVICE_CONTENT_TYPE); 355 } 356 357 /** 358 * Retrieve categories list in XML format. 359 * 360 * @since 2.2.0 361 */ 362 function get_categories_xml() { 363 log_app('function','get_categories_xml()'); 364 365 if ( !current_user_can( 'edit_posts' ) ) 366 $this->auth_required( __( 'Sorry, you do not have the right to access this site.' ) ); 367 368 $home = esc_attr(get_bloginfo_rss('url')); 369 370 $categories = ""; 371 $cats = get_categories(array('hierarchical' => 0, 'hide_empty' => 0)); 372 foreach ( (array) $cats as $cat ) { 373 $categories .= " <category term=\"" . esc_attr($cat->name) . "\" />\n"; 374 } 375 $output = <<<EOD 376 <app:categories xmlns:app="$this->ATOMPUB_NS" 377 xmlns="$this->ATOM_NS" 378 fixed="yes" scheme="$home"> 379 $categories 380 </app:categories> 381 EOD; 382 $this->output($output, $this->CATEGORIES_CONTENT_TYPE); 383 } 384 385 /** 386 * Create new post. 387 * 388 * @since 2.2.0 389 */ 390 function create_post() { 391 global $user_ID; 392 $this->get_accepted_content_type($this->atom_content_types); 393 394 $parser = new AtomParser(); 395 if ( !$parser->parse() ) 396 $this->client_error(); 397 398 $entry = array_pop($parser->feed->entries); 399 400 log_app('Received entry:', print_r($entry,true)); 401 402 $catnames = array(); 403 foreach ( $entry->categories as $cat ) { 404 array_push($catnames, $cat["term"]); 405 } 406 407 $wp_cats = get_categories(array('hide_empty' => false)); 408 409 $post_category = array(); 410 411 foreach ( $wp_cats as $cat ) { 412 if ( in_array($cat->name, $catnames) ) 413 array_push($post_category, $cat->term_id); 414 } 415 416 $publish = ! ( isset( $entry->draft ) && 'yes' == trim( $entry->draft ) ); 417 418 $cap = ($publish) ? 'publish_posts' : 'edit_posts'; 419 420 if ( !current_user_can($cap) ) 421 $this->auth_required(__('Sorry, you do not have the right to edit/publish new posts.')); 422 423 $blog_ID = get_current_blog_id(); 424 $post_status = ($publish) ? 'publish' : 'draft'; 425 $post_author = (int) $user_ID; 426 $post_title = $entry->title[1]; 427 $post_content = $entry->content[1]; 428 $post_excerpt = $entry->summary[1]; 429 $pubtimes = $this->get_publish_time($entry->published); 430 $post_date = $pubtimes[0]; 431 $post_date_gmt = $pubtimes[1]; 432 433 if ( isset( $_SERVER['HTTP_SLUG'] ) ) 434 $post_name = $_SERVER['HTTP_SLUG']; 435 436 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_name'); 437 438 $this->escape($post_data); 439 log_app('Inserting Post. Data:', print_r($post_data,true)); 440 441 $postID = wp_insert_post($post_data); 442 if ( is_wp_error( $postID ) ) 443 $this->internal_error($postID->get_error_message()); 444 445 if ( !$postID ) 446 $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.')); 447 448 // getting warning here about unable to set headers 449 // because something in the cache is printing to the buffer 450 // could we clean up wp_set_post_categories or cache to not print 451 // this could affect our ability to send back the right headers 452 @wp_set_post_categories($postID, $post_category); 453 454 do_action( 'atompub_create_post', $postID, $entry ); 455 456 $output = $this->get_entry($postID); 457 458 log_app('function',"create_post($postID)"); 459 $this->created($postID, $output); 460 } 461 462 /** 463 * Retrieve post. 464 * 465 * @since 2.2.0 466 * 467 * @param int $postID Post ID. 468 */ 469 function get_post($postID) { 470 global $entry; 471 472 if ( !current_user_can( 'edit_post', $postID ) ) 473 $this->auth_required( __( 'Sorry, you do not have the right to access this post.' ) ); 474 475 $this->set_current_entry($postID); 476 $output = $this->get_entry($postID); 477 log_app('function',"get_post($postID)"); 478 $this->output($output); 479 480 } 481 482 /** 483 * Update post. 484 * 485 * @since 2.2.0 486 * 487 * @param int $postID Post ID. 488 */ 489 function put_post($postID) { 490 // checked for valid content-types (atom+xml) 491 // quick check and exit 492 $this->get_accepted_content_type($this->atom_content_types); 493 494 $parser = new AtomParser(); 495 if ( !$parser->parse() ) 496 $this->bad_request(); 497 498 $parsed = array_pop($parser->feed->entries); 499 500 log_app('Received UPDATED entry:', print_r($parsed,true)); 501 502 // check for not found 503 global $entry; 504 $this->set_current_entry($postID); 505 506 if ( !current_user_can('edit_post', $entry['ID']) ) 507 $this->auth_required(__('Sorry, you do not have the right to edit this post.')); 508 509 $publish = ! ( isset($parsed->draft) && 'yes' == trim($parsed->draft) ); 510 $post_status = ($publish) ? 'publish' : 'draft'; 511 512 extract($entry); 513 514 $post_title = $parsed->title[1]; 515 $post_content = $parsed->content[1]; 516 $post_excerpt = $parsed->summary[1]; 517 $pubtimes = $this->get_publish_time($entry->published); 518 $post_date = $pubtimes[0]; 519 $post_date_gmt = $pubtimes[1]; 520 $pubtimes = $this->get_publish_time($parsed->updated); 521 $post_modified = $pubtimes[0]; 522 $post_modified_gmt = $pubtimes[1]; 523 524 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt'); 525 $this->escape($postdata); 526 527 $result = wp_update_post($postdata); 528 529 if ( !$result ) 530 $this->internal_error(__('For some strange yet very annoying reason, this post could not be edited.')); 531 532 do_action( 'atompub_put_post', $ID, $parsed ); 533 534 log_app('function',"put_post($postID)"); 535 $this->ok(); 536 } 537 538 /** 539 * Remove post. 540 * 541 * @since 2.2.0 542 * 543 * @param int $postID Post ID. 544 */ 545 function delete_post($postID) { 546 547 // check for not found 548 global $entry; 549 $this->set_current_entry($postID); 550 551 if ( !current_user_can('edit_post', $postID) ) 552 $this->auth_required(__('Sorry, you do not have the right to delete this post.')); 553 554 if ( $entry['post_type'] == 'attachment' ) { 555 $this->delete_attachment($postID); 556 } else { 557 $result = wp_delete_post($postID); 558 559 if ( !$result ) { 560 $this->internal_error(__('For some strange yet very annoying reason, this post could not be deleted.')); 561 } 562 563 log_app('function',"delete_post($postID)"); 564 $this->ok(); 565 } 566 567 } 568 569 /** 570 * Retrieve attachment. 571 * 572 * @since 2.2.0 573 * 574 * @param int $postID Optional. Post ID. 575 */ 576 function get_attachment($postID = null) { 577 if ( !current_user_can( 'upload_files' ) ) 578 $this->auth_required( __( 'Sorry, you do not have permission to upload files.' ) ); 579 580 if ( !isset($postID) ) { 581 $this->get_attachments(); 582 } else { 583 $this->set_current_entry($postID); 584 $output = $this->get_entry($postID, 'attachment'); 585 log_app('function',"get_attachment($postID)"); 586 $this->output($output); 587 } 588 } 589 590 /** 591 * Create new attachment. 592 * 593 * @since 2.2.0 594 */ 595 function create_attachment() { 596 597 $type = $this->get_accepted_content_type(); 598 599 if ( !current_user_can('upload_files') ) 600 $this->auth_required(__('You do not have permission to upload files.')); 601 602 $fp = fopen("php://input", "rb"); 603 $bits = null; 604 while ( !feof($fp) ) { 605 $bits .= fread($fp, 4096); 606 } 607 fclose($fp); 608 609 $slug = ''; 610 if ( isset( $_SERVER['HTTP_SLUG'] ) ) 611 $slug = $_SERVER['HTTP_SLUG']; 612 elseif ( isset( $_SERVER['HTTP_TITLE'] ) ) 613 $slug = $_SERVER['HTTP_TITLE']; 614 elseif ( empty( $slug ) ) // just make a random name 615 $slug = substr( md5( uniqid( microtime() ) ), 0, 7); 616 $ext = preg_replace( '|.*/([a-z0-9]+)|', '$1', $_SERVER['CONTENT_TYPE'] ); 617 $slug = sanitize_file_name( "$slug.$ext" ); 618 $file = wp_upload_bits( $slug, null, $bits); 619 620 log_app('wp_upload_bits returns:',print_r($file,true)); 621 622 $url = $file['url']; 623 $file = $file['file']; 624 625 do_action('wp_create_file_in_uploads', $file); // replicate 626 627 // Construct the attachment array 628 $attachment = array( 629 'post_title' => $slug, 630 'post_content' => $slug, 631 'post_status' => 'attachment', 632 'post_parent' => 0, 633 'post_mime_type' => $type, 634 'guid' => $url 635 ); 636 637 // Save the data 638 $postID = wp_insert_attachment($attachment, $file); 639 640 if (!$postID) 641 $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.')); 642 643 $output = $this->get_entry($postID, 'attachment'); 644 645 $this->created($postID, $output, 'attachment'); 646 log_app('function',"create_attachment($postID)"); 647 } 648 649 /** 650 * Update attachment. 651 * 652 * @since 2.2.0 653 * 654 * @param int $postID Post ID. 655 */ 656 function put_attachment($postID) { 657 // checked for valid content-types (atom+xml) 658 // quick check and exit 659 $this->get_accepted_content_type($this->atom_content_types); 660 661 $parser = new AtomParser(); 662 if (!$parser->parse()) { 663 $this->bad_request(); 664 } 665 666 $parsed = array_pop($parser->feed->entries); 667 668 // check for not found 669 global $entry; 670 $this->set_current_entry($postID); 671 672 if ( !current_user_can('edit_post', $entry['ID']) ) 673 $this->auth_required(__('Sorry, you do not have the right to edit this post.')); 674 675 extract($entry); 676 677 $post_title = $parsed->title[1]; 678 $post_content = $parsed->summary[1]; 679 $pubtimes = $this->get_publish_time($parsed->updated); 680 $post_modified = $pubtimes[0]; 681 $post_modified_gmt = $pubtimes[1]; 682 683 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_modified', 'post_modified_gmt'); 684 $this->escape($postdata); 685 686 $result = wp_update_post($postdata); 687 688 if ( !$result ) 689 $this->internal_error(__('For some strange yet very annoying reason, this post could not be edited.')); 690 691 log_app('function',"put_attachment($postID)"); 692 $this->ok(); 693 } 694 695 /** 696 * Remove attachment. 697 * 698 * @since 2.2.0 699 * 700 * @param int $postID Post ID. 701 */ 702 function delete_attachment($postID) { 703 log_app('function',"delete_attachment($postID). File '$location' deleted."); 704 705 // check for not found 706 global $entry; 707 $this->set_current_entry($postID); 708 709 if ( !current_user_can('edit_post', $postID) ) 710 $this->auth_required(__('Sorry, you do not have the right to delete this post.')); 711 712 $location = get_post_meta($entry['ID'], '_wp_attached_file', true); 713 $filetype = wp_check_filetype($location); 714 715 if ( !isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext']) ) 716 $this->internal_error(__('Error occurred while accessing post metadata for file location.')); 717 718 // delete file 719 @unlink($location); 720 721 // delete attachment 722 $result = wp_delete_post($postID); 723 724 if ( !$result ) 725 $this->internal_error(__('For some strange yet very annoying reason, this post could not be deleted.')); 726 727 log_app('function',"delete_attachment($postID). File '$location' deleted."); 728 $this->ok(); 729 } 730 731 /** 732 * Retrieve attachment from post. 733 * 734 * @since 2.2.0 735 * 736 * @param int $postID Post ID. 737 */ 738 function get_file($postID) { 739 740 // check for not found 741 global $entry; 742 $this->set_current_entry($postID); 743 744 // then whether user can edit the specific post 745 if ( !current_user_can('edit_post', $postID) ) 746 $this->auth_required(__('Sorry, you do not have the right to edit this post.')); 747 748 $location = get_post_meta($entry['ID'], '_wp_attached_file', true); 749 $location = get_option ('upload_path') . '/' . $location; 750 $filetype = wp_check_filetype($location); 751 752 if ( !isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext']) ) 753 $this->internal_error(__('Error occurred while accessing post metadata for file location.')); 754 755 status_header('200'); 756 header('Content-Type: ' . $entry['post_mime_type']); 757 header('Connection: close'); 758 759 if ( $fp = fopen($location, "rb") ) { 760 status_header('200'); 761 header('Content-Type: ' . $entry['post_mime_type']); 762 header('Connection: close'); 763 764 while ( !feof($fp) ) { 765 echo fread($fp, 4096); 766 } 767 768 fclose($fp); 769 } else { 770 status_header ('404'); 771 } 772 773 log_app('function',"get_file($postID)"); 774 exit; 775 } 776 777 /** 778 * Upload file to blog and add attachment to post. 779 * 780 * @since 2.2.0 781 * 782 * @param int $postID Post ID. 783 */ 784 function put_file($postID) { 785 786 // first check if user can upload 787 if ( !current_user_can('upload_files') ) 788 $this->auth_required(__('You do not have permission to upload files.')); 789 790 // check for not found 791 global $entry; 792 $this->set_current_entry($postID); 793 794 // then whether user can edit the specific post 795 if ( !current_user_can('edit_post', $postID) ) 796 $this->auth_required(__('Sorry, you do not have the right to edit this post.')); 797 798 $upload_dir = wp_upload_dir( ); 799 $location = get_post_meta($entry['ID'], '_wp_attached_file', true); 800 $filetype = wp_check_filetype($location); 801 802 $location = "{$upload_dir['basedir']}/{$location}"; 803 804 if (!isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext'])) 805 $this->internal_error(__('Error occurred while accessing post metadata for file location.')); 806 807 $fp = fopen("php://input", "rb"); 808 $localfp = fopen($location, "w+"); 809 while ( !feof($fp) ) { 810 fwrite($localfp,fread($fp, 4096)); 811 } 812 fclose($fp); 813 fclose($localfp); 814 815 $ID = $entry['ID']; 816 $pubtimes = $this->get_publish_time($entry->published); 817 $post_date = $pubtimes[0]; 818 $post_date_gmt = $pubtimes[1]; 819 $pubtimes = $this->get_publish_time($parsed->updated); 820 $post_modified = $pubtimes[0]; 821 $post_modified_gmt = $pubtimes[1]; 822 823 $post_data = compact('ID', 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt'); 824 $result = wp_update_post($post_data); 825 826 if ( !$result ) 827 $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.')); 828 829 wp_update_attachment_metadata( $postID, wp_generate_attachment_metadata( $postID, $location ) ); 830 831 log_app('function',"put_file($postID)"); 832 $this->ok(); 833 } 834 835 /** 836 * Retrieve entries URL. 837 * 838 * @since 2.2.0 839 * 840 * @param int $page Page ID. 841 * @return string 842 */ 843 function get_entries_url($page = null) { 844 if ( isset($GLOBALS['post_type']) && ( $GLOBALS['post_type'] == 'attachment' ) ) 845 $path = $this->MEDIA_PATH; 846 else 847 $path = $this->ENTRIES_PATH; 848 $url = $this->app_base . $path; 849 if ( isset($page) && is_int($page) ) 850 $url .= "/$page"; 851 return $url; 852 } 853 854 /** 855 * Display entries URL. 856 * 857 * @since 2.2.0 858 * 859 * @param int $page Page ID. 860 */ 861 function the_entries_url($page = null) { 862 echo $this->get_entries_url($page); 863 } 864 865 /** 866 * Retrieve categories URL. 867 * 868 * @since 2.2.0 869 * 870 * @param mixed $deprecated Not used. 871 * @return string 872 */ 873 function get_categories_url($deprecated = '') { 874 if ( !empty( $deprecated ) ) 875 _deprecated_argument( __FUNCTION__, '2.5' ); 876 return $this->app_base . $this->CATEGORIES_PATH; 877 } 878 879 /** 880 * Display category URL. 881 * 882 * @since 2.2.0 883 */ 884 function the_categories_url() { 885 echo $this->get_categories_url(); 886 } 887 888 /** 889 * Retrieve attachment URL. 890 * 891 * @since 2.2.0 892 * 893 * @param int $page Page ID. 894 * @return string 895 */ 896 function get_attachments_url($page = null) { 897 $url = $this->app_base . $this->MEDIA_PATH; 898 if (isset($page) && is_int($page)) { 899 $url .= "/$page"; 900 } 901 return $url; 902 } 903 904 /** 905 * Display attachment URL. 906 * 907 * @since 2.2.0 908 * 909 * @param int $page Page ID. 910 */ 911 function the_attachments_url($page = null) { 912 echo $this->get_attachments_url($page); 913 } 914 915 /** 916 * Retrieve service URL. 917 * 918 * @since 2.3.0 919 * 920 * @return string 921 */ 922 function get_service_url() { 923 return $this->app_base . $this->SERVICE_PATH; 924 } 925 926 /** 927 * Retrieve entry URL. 928 * 929 * @since 2.7.0 930 * 931 * @param int $postID Post ID. 932 * @return string 933 */ 934 function get_entry_url($postID = null) { 935 if (!isset($postID)) { 936 global $post; 937 $postID = (int) $post->ID; 938 } 939 940 $url = $this->app_base . $this->ENTRY_PATH . "/$postID"; 941 942 log_app('function',"get_entry_url() = $url"); 943 return $url; 944 } 945 946 /** 947 * Display entry URL. 948 * 949 * @since 2.7.0 950 * 951 * @param int $postID Post ID. 952 */ 953 function the_entry_url($postID = null) { 954 echo $this->get_entry_url($postID); 955 } 956 957 /** 958 * Retrieve media URL. 959 * 960 * @since 2.2.0 961 * 962 * @param int $postID Post ID. 963 * @return string 964 */ 965 function get_media_url($postID = null) { 966 if (!isset($postID)) { 967 global $post; 968 $postID = (int) $post->ID; 969 } 970 971 $url = $this->app_base . $this->MEDIA_SINGLE_PATH ."/file/$postID"; 972 973 log_app('function',"get_media_url() = $url"); 974 return $url; 975 } 976 977 /** 978 * Display the media URL. 979 * 980 * @since 2.2.0 981 * 982 * @param int $postID Post ID. 983 */ 984 function the_media_url($postID = null) { 985 echo $this->get_media_url($postID); 986 } 987 988 /** 989 * Set the current entry to post ID. 990 * 991 * @since 2.2.0 992 * 993 * @param int $postID Post ID. 994 */ 995 function set_current_entry($postID) { 996 global $entry; 997 log_app('function',"set_current_entry($postID)"); 998 999 if (!isset($postID)) { 1000 // $this->bad_request(); 1001 $this->not_found(); 1002 } 1003 1004 $entry = wp_get_single_post($postID,ARRAY_A); 1005 1006 if (!isset($entry) || !isset($entry['ID'])) 1007 $this->not_found(); 1008 1009 return; 1010 } 1011 1012 /** 1013 * Display posts XML. 1014 * 1015 * @since 2.2.0 1016 * 1017 * @param int $page Optional. Page ID. 1018 * @param string $post_type Optional, default is 'post'. Post Type. 1019 */ 1020 function get_posts($page = 1, $post_type = 'post') { 1021 log_app('function',"get_posts($page, '$post_type')"); 1022 $feed = $this->get_feed($page, $post_type); 1023 $this->output($feed); 1024 } 1025 1026 /** 1027 * Display attachment XML. 1028 * 1029 * @since 2.2.0 1030 * 1031 * @param int $page Page ID. 1032 * @param string $post_type Optional, default is 'attachment'. Post type. 1033 */ 1034 function get_attachments($page = 1, $post_type = 'attachment') { 1035 log_app('function',"get_attachments($page, '$post_type')"); 1036 $GLOBALS['post_type'] = $post_type; 1037 $feed = $this->get_feed($page, $post_type); 1038 $this->output($feed); 1039 } 1040 1041 /** 1042 * Retrieve feed XML. 1043 * 1044 * @since 2.2.0 1045 * 1046 * @param int $page Page ID. 1047 * @param string $post_type Optional, default is post. Post type. 1048 * @return string 1049 */ 1050 function get_feed($page = 1, $post_type = 'post') { 1051 global $post, $wp, $wp_query, $posts, $wpdb, $blog_id; 1052 log_app('function',"get_feed($page, '$post_type')"); 1053 ob_start(); 1054 1055 $this->ENTRY_PATH = $post_type; 1056 1057 if (!isset($page)) { 1058 $page = 1; 1059 } 1060 $page = (int) $page; 1061 1062 $count = get_option('posts_per_rss'); 1063 1064 wp('posts_per_page=' . $count . '&offset=' . ($count * ($page-1) . '&orderby=modified')); 1065 1066 $post = $GLOBALS['post']; 1067 $posts = $GLOBALS['posts']; 1068 $wp = $GLOBALS['wp']; 1069 $wp_query = $GLOBALS['wp_query']; 1070 $wpdb = $GLOBALS['wpdb']; 1071 $blog_id = (int) $GLOBALS['blog_id']; 1072 log_app('function',"query_posts(# " . print_r($wp_query, true) . "#)"); 1073 1074 log_app('function',"total_count(# $wp_query->max_num_pages #)"); 1075 $last_page = $wp_query->max_num_pages; 1076 $next_page = (($page + 1) > $last_page) ? null : $page + 1; 1077 $prev_page = ($page - 1) < 1 ? null : $page - 1; 1078 $last_page = ((int)$last_page == 1 || (int)$last_page == 0) ? null : (int) $last_page; 1079 $self_page = $page > 1 ? $page : null; 1080 ?><feed xmlns="<?php echo $this->ATOM_NS ?>" xmlns:app="<?php echo $this->ATOMPUB_NS ?>" xml:lang="<?php bloginfo_rss( 'language' ); ?>" <?php do_action('app_ns'); ?> > 1081 <id><?php $this->the_entries_url() ?></id> 1082 <updated><?php echo mysql2date('Y-m-d\TH:i:s\Z', get_lastpostmodified('GMT'), false); ?></updated> 1083 <title type="text"><?php bloginfo_rss('name') ?></title> 1084 <subtitle type="text"><?php bloginfo_rss("description") ?></subtitle> 1085 <link rel="first" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url() ?>" /> 1086 <?php if (isset($prev_page)): ?> 1087 <link rel="previous" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($prev_page) ?>" /> 1088 <?php endif; ?> 1089 <?php if (isset($next_page)): ?> 1090 <link rel="next" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($next_page) ?>" /> 1091 <?php endif; ?> 1092 <link rel="last" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($last_page) ?>" /> 1093 <link rel="self" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($self_page) ?>" /> 1094 <rights type="text">Copyright <?php echo date('Y'); ?></rights> 1095 <?php do_action('app_head'); ?> 1096 <?php if ( have_posts() ) { 1097 while ( have_posts() ) { 1098 the_post(); 1099 $this->echo_entry(); 1100 } 1101 } 1102 ?></feed> 1103 <?php 1104 $feed = ob_get_contents(); 1105 ob_end_clean(); 1106 return $feed; 1107 } 1108 1109 /** 1110 * Display entry XML. 1111 * 1112 * @since 2.2.0 1113 * 1114 * @param int $postID Post ID. 1115 * @param string $post_type Optional, default is post. Post type. 1116 * @return string. 1117 */ 1118 function get_entry($postID, $post_type = 'post') { 1119 log_app('function',"get_entry($postID, '$post_type')"); 1120 ob_start(); 1121 switch($post_type) { 1122 case 'post': 1123 $varname = 'p'; 1124 break; 1125 case 'attachment': 1126 $this->ENTRY_PATH = 'attachment'; 1127 $varname = 'attachment_id'; 1128 break; 1129 } 1130 query_posts($varname . '=' . $postID); 1131 if ( have_posts() ) { 1132 while ( have_posts() ) { 1133 the_post(); 1134 $this->echo_entry(); 1135 log_app('$post',print_r($GLOBALS['post'],true)); 1136 $entry = ob_get_contents(); 1137 break; 1138 } 1139 } 1140 ob_end_clean(); 1141 1142 log_app('get_entry returning:',$entry); 1143 return $entry; 1144 } 1145 1146 /** 1147 * Display post content XML. 1148 * 1149 * @since 2.3.0 1150 */ 1151 function echo_entry() { ?> 1152 <entry xmlns="<?php echo $this->ATOM_NS ?>" 1153 xmlns:app="<?php echo $this->ATOMPUB_NS ?>" xml:lang="<?php bloginfo_rss( 'language' ); ?>"> 1154 <id><?php the_guid( $GLOBALS['post']->ID ); ?></id> 1155 <?php list($content_type, $content) = prep_atom_text_construct(get_the_title()); ?> 1156 <title type="<?php echo $content_type ?>"><?php echo $content ?></title> 1157 <updated><?php echo get_post_modified_time('Y-m-d\TH:i:s\Z', true); ?></updated> 1158 <published><?php echo get_post_time('Y-m-d\TH:i:s\Z', true); ?></published> 1159 <app:edited><?php echo get_post_modified_time('Y-m-d\TH:i:s\Z', true); ?></app:edited> 1160 <app:control> 1161 <app:draft><?php echo ($GLOBALS['post']->post_status == 'draft' ? 'yes' : 'no') ?></app:draft> 1162 </app:control> 1163 <author> 1164 <name><?php the_author()?></name> 1165 <?php if ( get_the_author_meta('url') && get_the_author_meta('url') != 'http://' ) { ?> 1166 <uri><?php the_author_meta('url') ?></uri> 1167 <?php } ?> 1168 </author> 1169 <?php if ($GLOBALS['post']->post_type == 'attachment') { ?> 1170 <link rel="edit-media" href="<?php $this->the_media_url() ?>" /> 1171 <content type="<?php echo $GLOBALS['post']->post_mime_type ?>" src="<?php the_guid() ; ?>"/> 1172 <?php } else { ?> 1173 <link href="<?php the_permalink_rss() ?>" /> 1174 <?php if ( strlen( $GLOBALS['post']->post_content ) ) : 1175 list($content_type, $content) = prep_atom_text_construct(get_the_content()); ?> 1176 <content type="<?php echo $content_type ?>"><?php echo $content ?></content> 1177 <?php endif; ?> 1178 <?php } ?> 1179 <link rel="edit" href="<?php $this->the_entry_url() ?>" /> 1180 <?php the_category_rss( 'atom' ); ?> 1181 <?php list($content_type, $content) = prep_atom_text_construct(get_the_excerpt()); ?> 1182 <summary type="<?php echo $content_type ?>"><?php echo $content ?></summary> 1183 <?php do_action('app_entry'); ?> 1184 </entry> 1185 <?php } 1186 1187 /** 1188 * Set 'OK' (200) status header. 1189 * 1190 * @since 2.2.0 1191 */ 1192 function ok() { 1193 log_app('Status','200: OK'); 1194 header('Content-Type: text/plain'); 1195 status_header('200'); 1196 exit; 1197 } 1198 1199 /** 1200 * Set 'No Content' (204) status header. 1201 * 1202 * @since 2.2.0 1203 */ 1204 function no_content() { 1205 log_app('Status','204: No Content'); 1206 header('Content-Type: text/plain'); 1207 status_header('204'); 1208 echo "Moved to Trash."; 1209 exit; 1210 } 1211 1212 /** 1213 * Display 'Internal Server Error' (500) status header. 1214 * 1215 * @since 2.2.0 1216 * 1217 * @param string $msg Optional. Status string. 1218 */ 1219 function internal_error($msg = 'Internal Server Error') { 1220 log_app('Status','500: Server Error'); 1221 header('Content-Type: text/plain'); 1222 status_header('500'); 1223 echo $msg; 1224 exit; 1225 } 1226 1227 /** 1228 * Set 'Bad Request' (400) status header. 1229 * 1230 * @since 2.2.0 1231 */ 1232 function bad_request() { 1233 log_app('Status','400: Bad Request'); 1234 header('Content-Type: text/plain'); 1235 status_header('400'); 1236 exit; 1237 } 1238 1239 /** 1240 * Set 'Length Required' (411) status header. 1241 * 1242 * @since 2.2.0 1243 */ 1244 function length_required() { 1245 log_app('Status','411: Length Required'); 1246 header("HTTP/1.1 411 Length Required"); 1247 header('Content-Type: text/plain'); 1248 status_header('411'); 1249 exit; 1250 } 1251 1252 /** 1253 * Set 'Unsupported Media Type' (415) status header. 1254 * 1255 * @since 2.2.0 1256 */ 1257 function invalid_media() { 1258 log_app('Status','415: Unsupported Media Type'); 1259 header("HTTP/1.1 415 Unsupported Media Type"); 1260 header('Content-Type: text/plain'); 1261 exit; 1262 } 1263 1264 /** 1265 * Set 'Forbidden' (403) status header. 1266 * 1267 * @since 2.6.0 1268 */ 1269 function forbidden($reason='') { 1270 log_app('Status','403: Forbidden'); 1271 header('Content-Type: text/plain'); 1272 status_header('403'); 1273 echo $reason; 1274 exit; 1275 } 1276 1277 /** 1278 * Set 'Not Found' (404) status header. 1279 * 1280 * @since 2.2.0 1281 */ 1282 function not_found() { 1283 log_app('Status','404: Not Found'); 1284 header('Content-Type: text/plain'); 1285 status_header('404'); 1286 exit; 1287 } 1288 1289 /** 1290 * Set 'Not Allowed' (405) status header. 1291 * 1292 * @since 2.2.0 1293 */ 1294 function not_allowed($allow) { 1295 log_app('Status','405: Not Allowed'); 1296 header('Allow: ' . join(',', $allow)); 1297 status_header('405'); 1298 exit; 1299 } 1300 1301 /** 1302 * Display Redirect (302) content and set status headers. 1303 * 1304 * @since 2.3.0 1305 */ 1306 function redirect($url) { 1307 1308 log_app('Status','302: Redirect'); 1309 $escaped_url = esc_attr($url); 1310 $content = <<<EOD 1311 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> 1312 <html> 1313 <head> 1314 <title>302 Found</title> 1315 </head> 1316 <body> 1317 <h1>Found</h1> 1318 <p>The document has moved <a href="$escaped_url">here</a>.</p> 1319 </body> 1320 </html> 1321 1322 EOD; 1323 header('HTTP/1.1 302 Moved'); 1324 header('Content-Type: text/html'); 1325 header('Location: ' . $url); 1326 echo $content; 1327 exit; 1328 1329 } 1330 1331 /** 1332 * Set 'Client Error' (400) status header. 1333 * 1334 * @since 2.2.0 1335 */ 1336 function client_error($msg = 'Client Error') { 1337 log_app('Status','400: Client Error'); 1338 header('Content-Type: text/plain'); 1339 status_header('400'); 1340 exit; 1341 } 1342 1343 /** 1344 * Set created status headers (201). 1345 * 1346 * Sets the 'content-type', 'content-location', and 'location'. 1347 * 1348 * @since 2.2.0 1349 */ 1350 function created($post_ID, $content, $post_type = 'post') { 1351 log_app('created()::$post_ID',"$post_ID, $post_type"); 1352 $edit = $this->get_entry_url($post_ID); 1353 switch($post_type) { 1354 case 'post': 1355 $ctloc = $this->get_entry_url($post_ID); 1356 break; 1357 case 'attachment': 1358 $edit = $this->app_base . "attachments/$post_ID"; 1359 break; 1360 } 1361 header("Content-Type: $this->ATOM_CONTENT_TYPE"); 1362 if (isset($ctloc)) 1363 header('Content-Location: ' . $ctloc); 1364 header('Location: ' . $edit); 1365 status_header('201'); 1366 echo $content; 1367 exit; 1368 } 1369 1370 /** 1371 * Set 'Auth Required' (401) headers. 1372 * 1373 * @since 2.2.0 1374 * 1375 * @param string $msg Status header content and HTML content. 1376 */ 1377 function auth_required($msg) { 1378 log_app('Status','401: Auth Required'); 1379 nocache_headers(); 1380 header('WWW-Authenticate: Basic realm="WordPress Atom Protocol"'); 1381 header("HTTP/1.1 401 $msg"); 1382 header('Status: 401 ' . $msg); 1383 header('Content-Type: text/html'); 1384 $content = <<<EOD 1385 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> 1386 <html> 1387 <head> 1388 <title>401 Unauthorized</title> 1389 </head> 1390 <body> 1391 <h1>401 Unauthorized</h1> 1392 <p>$msg</p> 1393 </body> 1394 </html> 1395 1396 EOD; 1397 echo $content; 1398 exit; 1399 } 1400 1401 /** 1402 * Display XML and set headers with content type. 1403 * 1404 * @since 2.2.0 1405 * 1406 * @param string $xml Display feed content. 1407 * @param string $ctype Optional, default is 'atom+xml'. Feed content type. 1408 */ 1409 function output($xml, $ctype = 'application/atom+xml') { 1410 status_header('200'); 1411 $xml = '<?xml version="1.0" encoding="' . strtolower(get_option('blog_charset')) . '"?>'."\n".$xml; 1412 header('Connection: close'); 1413 header('Content-Length: '. strlen($xml)); 1414 header('Content-Type: ' . $ctype); 1415 header('Content-Disposition: attachment; filename=atom.xml'); 1416 header('Date: '. date('r')); 1417 if ($this->do_output) 1418 echo $xml; 1419 log_app('function', "output:\n$xml"); 1420 exit; 1421 } 1422 1423 /** 1424 * Sanitize content for database usage. 1425 * 1426 * @since 2.2.0 1427 * 1428 * @param array $array Sanitize array and multi-dimension array. 1429 */ 1430 function escape(&$array) { 1431 global $wpdb; 1432 1433 foreach ($array as $k => $v) { 1434 if (is_array($v)) { 1435 $this->escape($array[$k]); 1436 } else if (is_object($v)) { 1437 //skip 1438 } else { 1439 $array[$k] = $wpdb->escape($v); 1440 } 1441 } 1442 } 1443 1444 /** 1445 * Access credential through various methods and perform login. 1446 * 1447 * @since 2.2.0 1448 * 1449 * @return bool 1450 */ 1451 function authenticate() { 1452 log_app("authenticate()",print_r($_ENV, true)); 1453 1454 // if using mod_rewrite/ENV hack 1455 // http://www.besthostratings.com/articles/http-auth-php-cgi.html 1456 if (isset($_SERVER['HTTP_AUTHORIZATION'])) { 1457 list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = 1458 explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6))); 1459 } else if (isset($_SERVER['REDIRECT_REMOTE_USER'])) { 1460 // Workaround for setups that do not forward HTTP_AUTHORIZATION 1461 // See http://trac.wordpress.org/ticket/7361 1462 list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = 1463 explode(':', base64_decode(substr($_SERVER['REDIRECT_REMOTE_USER'], 6))); 1464 } 1465 1466 // If Basic Auth is working... 1467 if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) { 1468 log_app("Basic Auth",$_SERVER['PHP_AUTH_USER']); 1469 1470 $user = wp_authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']); 1471 if ( $user && !is_wp_error($user) ) { 1472 wp_set_current_user($user->ID); 1473 log_app("authenticate()", $user->user_login); 1474 return true; 1475 } 1476 } 1477 1478 return false; 1479 } 1480 1481 /** 1482 * Retrieve accepted content types. 1483 * 1484 * @since 2.2.0 1485 * 1486 * @param array $types Optional. Content Types. 1487 * @return string 1488 */ 1489 function get_accepted_content_type($types = null) { 1490 1491 if (!isset($types)) { 1492 $types = $this->media_content_types; 1493 } 1494 1495 if (!isset($_SERVER['CONTENT_LENGTH']) || !isset($_SERVER['CONTENT_TYPE'])) { 1496 $this->length_required(); 1497 } 1498 1499 $type = $_SERVER['CONTENT_TYPE']; 1500 list($type,$subtype) = explode('/',$type); 1501 list($subtype) = explode(";",$subtype); // strip MIME parameters 1502 log_app("get_accepted_content_type", "type=$type, subtype=$subtype"); 1503 1504 foreach($types as $t) { 1505 list($acceptedType,$acceptedSubtype) = explode('/',$t); 1506 if ($acceptedType == '*' || $acceptedType == $type) { 1507 if ($acceptedSubtype == '*' || $acceptedSubtype == $subtype) 1508 return $type . "/" . $subtype; 1509 } 1510 } 1511 1512 $this->invalid_media(); 1513 } 1514 1515 /** 1516 * Process conditionals for posts. 1517 * 1518 * @since 2.2.0 1519 */ 1520 function process_conditionals() { 1521 1522 if (empty($this->params)) return; 1523 if ($_SERVER['REQUEST_METHOD'] == 'DELETE') return; 1524 1525 switch($this->params[0]) { 1526 case $this->ENTRY_PATH: 1527 global $post; 1528 $post = wp_get_single_post($this->params[1]); 1529 $wp_last_modified = get_post_modified_time('D, d M Y H:i:s', true); 1530 $post = null; 1531 break; 1532 case $this->ENTRIES_PATH: 1533 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT'; 1534 break; 1535 default: 1536 return; 1537 } 1538 $wp_etag = md5($wp_last_modified); 1539 @header("Last-Modified: $wp_last_modified"); 1540 @header("ETag: $wp_etag"); 1541 1542 // Support for Conditional GET 1543 if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) 1544 $client_etag = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']); 1545 else 1546 $client_etag = false; 1547 1548 $client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE']); 1549 // If string is empty, return 0. If not, attempt to parse into a timestamp 1550 $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0; 1551 1552 // Make a timestamp for our most recent modification... 1553 $wp_modified_timestamp = strtotime($wp_last_modified); 1554 1555 if ( ($client_last_modified && $client_etag) ? 1556 (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) : 1557 (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) { 1558 status_header( 304 ); 1559 exit; 1560 } 1561 } 1562 1563 /** 1564 * Convert RFC3339 time string to timestamp. 1565 * 1566 * @since 2.3.0 1567 * 1568 * @param string $str String to time. 1569 * @return bool|int false if format is incorrect. 1570 */ 1571 function rfc3339_str2time($str) { 1572 1573 $match = false; 1574 if (!preg_match("/(\d{4}-\d{2}-\d{2})T(\d{2}\:\d{2}\:\d{2})\.?\d{0,3}(Z|[+-]+\d{2}\:\d{2})/", $str, $match)) 1575 return false; 1576 1577 if ($match[3] == 'Z') 1578 $match[3] = '+0000'; 1579 1580 return strtotime($match[1] . " " . $match[2] . " " . $match[3]); 1581 } 1582 1583 /** 1584 * Retrieve published time to display in XML. 1585 * 1586 * @since 2.3.0 1587 * 1588 * @param string $published Time string. 1589 * @return string 1590 */ 1591 function get_publish_time($published) { 1592 1593 $pubtime = $this->rfc3339_str2time($published); 1594 1595 if (!$pubtime) { 1596 return array(current_time('mysql'),current_time('mysql',1)); 1597 } else { 1598 return array(date("Y-m-d H:i:s", $pubtime), gmdate("Y-m-d H:i:s", $pubtime)); 1599 } 1600 } 1601 1602 } 1603 1604 /** 1605 * AtomServer 1606 * @var AtomServer 1607 * @global object $server 1608 */ 1609 $server = new AtomServer(); 1610 $server->handle_request();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Sat Feb 4 03:55:55 2012 | Hosted by follow the white rabbit. |