WordPress.org

Make WordPress Core

Changeset 20062


Ignore:
Timestamp:
03/01/2012 09:15:44 PM (8 years ago)
Author:
ryan
Message:

wp-app.php cleanup:

  • Put the atom server class in class-wp-atom-server.php
  • Remove log_api calls
  • Update the query filter
  • Replace exit calls with wp_die() for unit test compat
  • Add override handler for wp_die()
  • Allow plugins to replace the atom class
  • Whitespace cleanup

Props kurtpayne. fixes #20042

Location:
trunk
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/wp-app.php

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

    r20059 r20062  
    11<?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 always authenticate user. Permanently set to true.
    28  *
    29  * @name always_authenticate
    30  * @var int|bool
    31  * @todo Should be an option somewhere
    32  */
    33 $always_authenticate = 1;
    34 
    35 /**
    36  * Writes logging info to a file.
    37  *
    38  * @since 2.2.0
    39  * @deprecated 3.4.0
    40  * @deprecated Use error_log()
    41  * @link http://www.php.net/manual/en/function.error-log.php
    42  *
    43  * @param string $label Type of logging
    44  * @param string $msg Information describing logging reason.
    45  */
    46 function log_app( $label, $msg ) {
    47     _deprecated_function( __FUNCTION__, '3.4', 'error_log()' );
    48     if ( ! empty( $GLOBALS['app_logging'] ) )
    49         error_log( $label . ' - ' . $message );
    50 }
    51 
    52 /**
    53  * Filter to add more post statuses.
    54  *
    55  * @since 2.2.0
    56  *
    57  * @param string $where SQL statement to filter.
    58  * @return string Filtered SQL statement with added post_status for where clause.
    59  */
    60 function wa_posts_where_include_drafts_filter($where) {
    61     $where = str_replace("post_status = 'publish'","post_status = 'publish' OR post_status = 'future' OR post_status = 'draft' OR post_status = 'inherit'", $where);
    62     return $where;
    63 
    64 }
    65 add_filter('posts_where', 'wa_posts_where_include_drafts_filter');
    662
    673/**
     
    728 * @since 2.2.0
    739 */
    74 class AtomServer {
     10class wp_atom_server {
    7511
    7612    /**
     
    240176                        'DELETE' => 'delete_attachment'),
    241177        );
     178
     179        add_filter( 'wp_die_handler', array( $this, 'return_atom_die_handler' ) );
     180    }
     181
     182    /**
     183     * Override die handler
     184     * @return callback
     185     */
     186    public function return_atom_die_handler() {
     187        return array( $this, 'atom_die_handler' );
     188    }
     189
     190    /**
     191     * Die with a message.  Only accept strings, no WP_Error objects yet
     192     * @param string $message
     193     * @return void
     194     */
     195    public function atom_die_handler( $message ) {
     196        if ( is_scalar( $message ) )
     197            die( (string) $message );
     198        die();
    242199    }
    243200
     
    248205     */
    249206    function handle_request() {
    250         global $always_authenticate;
    251207
    252208        if ( !empty( $_SERVER['ORIG_PATH_INFO'] ) )
     
    282238                    // user. each handler will decide if auth is required or not.
    283239                    if ( !$this->authenticate() ) {
    284                         if ( $always_authenticate )
    285                             $this->auth_required('Credentials required.');
     240                        $this->auth_required('Credentials required.');
    286241                    }
    287242
    288243                    array_shift($matches);
    289                     call_user_func_array(array(&$this,$funcs[$method]), $matches);
    290                     exit();
     244                    call_user_func_array(array($this,$funcs[$method]), $matches);
     245                    wp_die();
    291246                } else {
    292247                    // only allow what we have handlers for...
     
    306261     */
    307262    function get_service() {
     263
    308264        if ( !current_user_can( 'edit_posts' ) )
    309265            $this->auth_required( __( 'Sorry, you do not have the right to access this site.' ) );
     
    345301     */
    346302    function get_categories_xml() {
     303
    347304        if ( !current_user_can( 'edit_posts' ) )
    348305            $this->auth_required( __( 'Sorry, you do not have the right to access this site.' ) );
     
    411368        $post_excerpt = '';
    412369        $pubtimes     = '';
    413 
     370       
    414371        if ( isset( $entry->title ) && is_array( $entry->title ) && !empty( $entry->title[1] ) )
    415372            $post_title = (string) $entry->title[1];
     
    420377        if ( !empty( $entry->published ) )
    421378            $pubtimes = (string) $entry->published;
    422 
     379       
    423380        $pubtimes = $this->get_publish_time( $pubtimes );
    424381
     
    684641     */
    685642    function delete_attachment($postID) {
     643
    686644        // check for not found
    687645        global $entry;
     
    751709        }
    752710
    753         exit;
     711        wp_die();
    754712    }
    755713
     
    994952     */
    995953    function get_posts($page = 1, $post_type = 'post') {
    996             $feed = $this->get_feed($page, $post_type);
    997             $this->output($feed);
     954        $feed = $this->get_feed($page, $post_type);
     955        $this->output($feed);
    998956    }
    999957
     
    1034992        $count = get_option('posts_per_rss');
    1035993
    1036         wp('posts_per_page=' . $count . '&offset=' . ($count * ($page-1) . '&orderby=modified'));
     994        wp('posts_per_page=' . $count . '&offset=' . ($count * ($page-1)) . '&orderby=modified&post_status=any');
    1037995
    1038996        $post = $GLOBALS['post'];
     
    11601118        header('Content-Type: text/plain');
    11611119        status_header('200');
    1162         exit;
     1120        wp_die();
    11631121    }
    11641122
     
    11721130        status_header('204');
    11731131        echo "Moved to Trash.";
    1174         exit;
     1132        wp_die();
    11751133    }
    11761134
     
    11861144        status_header('500');
    11871145        echo $msg;
    1188         exit;
     1146        wp_die();
    11891147    }
    11901148
     
    11971155        header('Content-Type: text/plain');
    11981156        status_header('400');
    1199         exit;
     1157        wp_die();
    12001158    }
    12011159
     
    12091167        header('Content-Type: text/plain');
    12101168        status_header('411');
    1211         exit;
     1169        wp_die();
    12121170    }
    12131171
     
    12201178        header("HTTP/1.1 415 Unsupported Media Type");
    12211179        header('Content-Type: text/plain');
    1222         exit;
     1180        wp_die();
    12231181    }
    12241182
     
    12321190        status_header('403');
    12331191        echo $reason;
    1234         exit;
     1192        wp_die();
    12351193    }
    12361194
     
    12431201        header('Content-Type: text/plain');
    12441202        status_header('404');
    1245         exit;
     1203        wp_die();
    12461204    }
    12471205
     
    12541212        header('Allow: ' . join(',', $allow));
    12551213        status_header('405');
    1256         exit;
     1214        wp_die();
    12571215    }
    12581216
     
    12811239        header('Location: ' . $url);
    12821240        echo $content;
    1283         exit;
     1241        wp_die();
    12841242
    12851243    }
     
    12931251        header('Content-Type: text/plain');
    12941252        status_header('400');
    1295         exit;
     1253        wp_die();
    12961254    }
    12971255
     
    13191277        status_header('201');
    13201278        echo $content;
    1321         exit;
     1279        wp_die();
    13221280    }
    13231281
     
    13491307EOD;
    13501308        echo $content;
    1351         exit;
     1309        wp_die();
    13521310    }
    13531311
     
    13611319     */
    13621320    function output($xml, $ctype = 'application/atom+xml') {
    1363             status_header('200');
    1364             $xml = '<?xml version="1.0" encoding="' . strtolower(get_option('blog_charset')) . '"?>'."\n".$xml;
    1365             header('Connection: close');
    1366             header('Content-Length: '. strlen($xml));
    1367             header('Content-Type: ' . $ctype);
    1368             header('Content-Disposition: attachment; filename=atom.xml');
    1369             header('Date: '. date('r'));
    1370             if ($this->do_output)
    1371                 echo $xml;
    1372             exit;
     1321        status_header('200');
     1322        $xml = '<?xml version="1.0" encoding="' . strtolower(get_option('blog_charset')) . '"?>'."\n".$xml;
     1323        header('Connection: close');
     1324        header('Content-Length: '. strlen($xml));
     1325        header('Content-Type: ' . $ctype);
     1326        header('Content-Disposition: attachment; filename=atom.xml');
     1327        header('Date: '. date('r'));
     1328        if ($this->do_output)
     1329            echo $xml;
     1330        wp_die();
    13731331    }
    13741332
     
    14021360     */
    14031361    function authenticate() {
     1362
    14041363        // if using mod_rewrite/ENV hack
    14051364        // http://www.besthostratings.com/articles/http-auth-php-cgi.html
     
    14161375        // If Basic Auth is working...
    14171376        if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
     1377
    14181378            $user = wp_authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
    14191379            if ( $user && !is_wp_error($user) ) {
     
    15031463        (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {
    15041464            status_header( 304 );
    1505             exit;
     1465            wp_die();
    15061466        }
    15071467    }
     
    15471507
    15481508}
    1549 
    1550 /**
    1551  * AtomServer
    1552  * @var AtomServer
    1553  * @global object $server
    1554  */
    1555 $server = new AtomServer();
    1556 $server->handle_request();
Note: See TracChangeset for help on using the changeset viewer.