WordPress.org

Make WordPress Core

Ticket #20042: 20042.patch

File 20042.patch, 57.7 KB (added by kurtpayne, 6 years ago)
  • wp-includes/class-wp-atom-server.php

     
    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 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');
    782
    793/**
    804 * WordPress AtomPub API implementation.
     
    837 * @subpackage Publishing
    848 * @since 2.2.0
    859 */
    86 class AtomServer {
     10class wp_atom_server {
    8711
    8812        /**
    8913         * ATOM content type.
     
    251175                                                'PUT' => 'put_attachment',
    252176                                                'DELETE' => 'delete_attachment'),
    253177                );
     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();
    254199        }
    255200
    256201        /**
     
    259204         * @since 2.2.0
    260205         */
    261206        function handle_request() {
    262                 global $always_authenticate;
    263207
    264208                if ( !empty( $_SERVER['ORIG_PATH_INFO'] ) )
    265209                        $path = $_SERVER['ORIG_PATH_INFO'];
     
    268212
    269213                $method = $_SERVER['REQUEST_METHOD'];
    270214
    271                 log_app('REQUEST',"$method $path\n================");
    272 
    273215                $this->process_conditionals();
    274216                //$this->process_conditionals();
    275217
     
    295237                                        // authenticate regardless of the operation and set the current
    296238                                        // user. each handler will decide if auth is required or not.
    297239                                        if ( !$this->authenticate() ) {
    298                                                 if ( $always_authenticate )
    299                                                         $this->auth_required('Credentials required.');
     240                                                $this->auth_required('Credentials required.');
    300241                                        }
    301242
    302243                                        array_shift($matches);
    303                                         call_user_func_array(array(&$this,$funcs[$method]), $matches);
    304                                         exit();
     244                                        call_user_func_array(array($this,$funcs[$method]), $matches);
     245                                        wp_die();
    305246                                } else {
    306247                                        // only allow what we have handlers for...
    307248                                        $this->not_allowed(array_keys($funcs));
     
    319260         * @since 2.2.0
    320261         */
    321262        function get_service() {
    322                 log_app('function','get_service()');
    323263
    324264                if ( !current_user_can( 'edit_posts' ) )
    325265                        $this->auth_required( __( 'Sorry, you do not have the right to access this site.' ) );
     
    360300         * @since 2.2.0
    361301         */
    362302        function get_categories_xml() {
    363                 log_app('function','get_categories_xml()');
    364303
    365304                if ( !current_user_can( 'edit_posts' ) )
    366305                        $this->auth_required( __( 'Sorry, you do not have the right to access this site.' ) );
     
    397336
    398337                $entry = array_pop($parser->feed->entries);
    399338
    400                 log_app('Received entry:', print_r($entry,true));
    401 
    402339                $catnames = array();
    403340                if ( !empty( $entry->categories ) ) {
    404341                        foreach ( $entry->categories as $cat ) {
     
    451388                $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_name');
    452389
    453390                $this->escape($post_data);
    454                 log_app('Inserting Post. Data:', print_r($post_data,true));
    455391
    456392                $postID = wp_insert_post($post_data);
    457393                if ( is_wp_error( $postID ) )
     
    470406
    471407                $output = $this->get_entry($postID);
    472408
    473                 log_app('function',"create_post($postID)");
    474409                $this->created($postID, $output);
    475410        }
    476411
     
    489424
    490425                $this->set_current_entry($postID);
    491426                $output = $this->get_entry($postID);
    492                 log_app('function',"get_post($postID)");
    493427                $this->output($output);
    494428
    495429        }
     
    512446
    513447                $parsed = array_pop($parser->feed->entries);
    514448
    515                 log_app('Received UPDATED entry:', print_r($parsed,true));
    516 
    517449                // check for not found
    518450                global $entry;
    519451                $this->set_current_entry($postID);
     
    546478
    547479                do_action( 'atompub_put_post', $ID, $parsed );
    548480
    549                 log_app('function',"put_post($postID)");
    550481                $this->ok();
    551482        }
    552483
     
    575506                                $this->internal_error(__('For some strange yet very annoying reason, this post could not be deleted.'));
    576507                        }
    577508
    578                         log_app('function',"delete_post($postID)");
    579509                        $this->ok();
    580510                }
    581511
     
    597527                } else {
    598528                        $this->set_current_entry($postID);
    599529                        $output = $this->get_entry($postID, 'attachment');
    600                         log_app('function',"get_attachment($postID)");
    601530                        $this->output($output);
    602531                }
    603532        }
     
    632561                $slug = sanitize_file_name( "$slug.$ext" );
    633562                $file = wp_upload_bits( $slug, null, $bits);
    634563
    635                 log_app('wp_upload_bits returns:',print_r($file,true));
    636 
    637564                $url = $file['url'];
    638565                $file = $file['file'];
    639566
     
    658585                $output = $this->get_entry($postID, 'attachment');
    659586
    660587                $this->created($postID, $output, 'attachment');
    661                 log_app('function',"create_attachment($postID)");
    662588        }
    663589
    664590        /**
     
    703629                if ( !$result )
    704630                        $this->internal_error(__('For some strange yet very annoying reason, this post could not be edited.'));
    705631
    706                 log_app('function',"put_attachment($postID)");
    707632                $this->ok();
    708633        }
    709634
     
    715640         * @param int $postID Post ID.
    716641         */
    717642        function delete_attachment($postID) {
    718                 log_app('function',"delete_attachment($postID). File '$location' deleted.");
    719643
    720644                // check for not found
    721645                global $entry;
     
    739663                if ( !$result )
    740664                        $this->internal_error(__('For some strange yet very annoying reason, this post could not be deleted.'));
    741665
    742                 log_app('function',"delete_attachment($postID). File '$location' deleted.");
    743666                $this->ok();
    744667        }
    745668
     
    785708                        status_header ('404');
    786709                }
    787710
    788                 log_app('function',"get_file($postID)");
    789                 exit;
     711                wp_die();
    790712        }
    791713
    792714        /**
     
    843765
    844766                wp_update_attachment_metadata( $postID, wp_generate_attachment_metadata( $postID, $location ) );
    845767
    846                 log_app('function',"put_file($postID)");
    847768                $this->ok();
    848769        }
    849770
     
    954875
    955876                $url = $this->app_base . $this->ENTRY_PATH . "/$postID";
    956877
    957                 log_app('function',"get_entry_url() = $url");
    958878                return $url;
    959879        }
    960880
     
    985905
    986906                $url = $this->app_base . $this->MEDIA_SINGLE_PATH ."/file/$postID";
    987907
    988                 log_app('function',"get_media_url() = $url");
    989908                return $url;
    990909        }
    991910
     
    1009928         */
    1010929        function set_current_entry($postID) {
    1011930                global $entry;
    1012                 log_app('function',"set_current_entry($postID)");
    1013931
    1014932                if (!isset($postID)) {
    1015933                        // $this->bad_request();
     
    1033951         * @param string $post_type Optional, default is 'post'. Post Type.
    1034952         */
    1035953        function get_posts($page = 1, $post_type = 'post') {
    1036                         log_app('function',"get_posts($page, '$post_type')");
    1037                         $feed = $this->get_feed($page, $post_type);
    1038                         $this->output($feed);
     954                $feed = $this->get_feed($page, $post_type);
     955                $this->output($feed);
    1039956        }
    1040957
    1041958        /**
     
    1047964         * @param string $post_type Optional, default is 'attachment'. Post type.
    1048965         */
    1049966        function get_attachments($page = 1, $post_type = 'attachment') {
    1050                 log_app('function',"get_attachments($page, '$post_type')");
    1051967                $GLOBALS['post_type'] = $post_type;
    1052968                $feed = $this->get_feed($page, $post_type);
    1053969                $this->output($feed);
     
    1064980         */
    1065981        function get_feed($page = 1, $post_type = 'post') {
    1066982                global $post, $wp, $wp_query, $posts, $wpdb, $blog_id;
    1067                 log_app('function',"get_feed($page, '$post_type')");
    1068983                ob_start();
    1069984
    1070985                $this->ENTRY_PATH = $post_type;
     
    1076991
    1077992                $count = get_option('posts_per_rss');
    1078993
    1079                 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');
    1080995
    1081996                $post = $GLOBALS['post'];
    1082997                $posts = $GLOBALS['posts'];
     
    1084999                $wp_query = $GLOBALS['wp_query'];
    10851000                $wpdb = $GLOBALS['wpdb'];
    10861001                $blog_id = (int) $GLOBALS['blog_id'];
    1087                 log_app('function',"query_posts(# " . print_r($wp_query, true) . "#)");
    10881002
    1089                 log_app('function',"total_count(# $wp_query->max_num_pages #)");
    10901003                $last_page = $wp_query->max_num_pages;
    10911004                $next_page = (($page + 1) > $last_page) ? null : $page + 1;
    10921005                $prev_page = ($page - 1) < 1 ? null : $page - 1;
     
    11311044         * @return string.
    11321045         */
    11331046        function get_entry($postID, $post_type = 'post') {
    1134                 log_app('function',"get_entry($postID, '$post_type')");
    11351047                ob_start();
    11361048                switch($post_type) {
    11371049                        case 'post':
     
    11471059                        while ( have_posts() ) {
    11481060                                the_post();
    11491061                                $this->echo_entry();
    1150                                 log_app('$post',print_r($GLOBALS['post'],true));
    11511062                                $entry = ob_get_contents();
    11521063                                break;
    11531064                        }
    11541065                }
    11551066                ob_end_clean();
    11561067
    1157                 log_app('get_entry returning:',$entry);
    11581068                return $entry;
    11591069        }
    11601070
     
    12051115         * @since 2.2.0
    12061116         */
    12071117        function ok() {
    1208                 log_app('Status','200: OK');
    12091118                header('Content-Type: text/plain');
    12101119                status_header('200');
    1211                 exit;
     1120                wp_die();
    12121121        }
    12131122
    12141123        /**
     
    12171126         * @since 2.2.0
    12181127         */
    12191128        function no_content() {
    1220                 log_app('Status','204: No Content');
    12211129                header('Content-Type: text/plain');
    12221130                status_header('204');
    12231131                echo "Moved to Trash.";
    1224                 exit;
     1132                wp_die();
    12251133        }
    12261134
    12271135        /**
     
    12321140         * @param string $msg Optional. Status string.
    12331141         */
    12341142        function internal_error($msg = 'Internal Server Error') {
    1235                 log_app('Status','500: Server Error');
    12361143                header('Content-Type: text/plain');
    12371144                status_header('500');
    12381145                echo $msg;
    1239                 exit;
     1146                wp_die();
    12401147        }
    12411148
    12421149        /**
     
    12451152         * @since 2.2.0
    12461153         */
    12471154        function bad_request() {
    1248                 log_app('Status','400: Bad Request');
    12491155                header('Content-Type: text/plain');
    12501156                status_header('400');
    1251                 exit;
     1157                wp_die();
    12521158        }
    12531159
    12541160        /**
     
    12571163         * @since 2.2.0
    12581164         */
    12591165        function length_required() {
    1260                 log_app('Status','411: Length Required');
    12611166                header("HTTP/1.1 411 Length Required");
    12621167                header('Content-Type: text/plain');
    12631168                status_header('411');
    1264                 exit;
     1169                wp_die();
    12651170        }
    12661171
    12671172        /**
     
    12701175         * @since 2.2.0
    12711176         */
    12721177        function invalid_media() {
    1273                 log_app('Status','415: Unsupported Media Type');
    12741178                header("HTTP/1.1 415 Unsupported Media Type");
    12751179                header('Content-Type: text/plain');
    1276                 exit;
     1180                wp_die();
    12771181        }
    12781182
    12791183        /**
     
    12821186         * @since 2.6.0
    12831187         */
    12841188        function forbidden($reason='') {
    1285                 log_app('Status','403: Forbidden');
    12861189                header('Content-Type: text/plain');
    12871190                status_header('403');
    12881191                echo $reason;
    1289                 exit;
     1192                wp_die();
    12901193        }
    12911194
    12921195        /**
     
    12951198         * @since 2.2.0
    12961199         */
    12971200        function not_found() {
    1298                 log_app('Status','404: Not Found');
    12991201                header('Content-Type: text/plain');
    13001202                status_header('404');
    1301                 exit;
     1203                wp_die();
    13021204        }
    13031205
    13041206        /**
     
    13071209         * @since 2.2.0
    13081210         */
    13091211        function not_allowed($allow) {
    1310                 log_app('Status','405: Not Allowed');
    13111212                header('Allow: ' . join(',', $allow));
    13121213                status_header('405');
    1313                 exit;
     1214                wp_die();
    13141215        }
    13151216
    13161217        /**
     
    13191220         * @since 2.3.0
    13201221         */
    13211222        function redirect($url) {
    1322 
    1323                 log_app('Status','302: Redirect');
    13241223                $escaped_url = esc_attr($url);
    13251224                $content = <<<EOD
    13261225<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
     
    13391238                header('Content-Type: text/html');
    13401239                header('Location: ' . $url);
    13411240                echo $content;
    1342                 exit;
     1241                wp_die();
    13431242
    13441243        }
    13451244
     
    13491248         * @since 2.2.0
    13501249         */
    13511250        function client_error($msg = 'Client Error') {
    1352                 log_app('Status','400: Client Error');
    13531251                header('Content-Type: text/plain');
    13541252                status_header('400');
    1355                 exit;
     1253                wp_die();
    13561254        }
    13571255
    13581256        /**
     
    13631261         * @since 2.2.0
    13641262         */
    13651263        function created($post_ID, $content, $post_type = 'post') {
    1366                 log_app('created()::$post_ID',"$post_ID, $post_type");
    13671264                $edit = $this->get_entry_url($post_ID);
    13681265                switch($post_type) {
    13691266                        case 'post':
     
    13791276                header('Location: ' . $edit);
    13801277                status_header('201');
    13811278                echo $content;
    1382                 exit;
     1279                wp_die();
    13831280        }
    13841281
    13851282        /**
     
    13901287         * @param string $msg Status header content and HTML content.
    13911288         */
    13921289        function auth_required($msg) {
    1393                 log_app('Status','401: Auth Required');
    13941290                nocache_headers();
    13951291                header('WWW-Authenticate: Basic realm="WordPress Atom Protocol"');
    13961292                header("HTTP/1.1 401 $msg");
     
    14101306
    14111307EOD;
    14121308                echo $content;
    1413                 exit;
     1309                wp_die();
    14141310        }
    14151311
    14161312        /**
     
    14221318         * @param string $ctype Optional, default is 'atom+xml'. Feed content type.
    14231319         */
    14241320        function output($xml, $ctype = 'application/atom+xml') {
    1425                         status_header('200');
    1426                         $xml = '<?xml version="1.0" encoding="' . strtolower(get_option('blog_charset')) . '"?>'."\n".$xml;
    1427                         header('Connection: close');
    1428                         header('Content-Length: '. strlen($xml));
    1429                         header('Content-Type: ' . $ctype);
    1430                         header('Content-Disposition: attachment; filename=atom.xml');
    1431                         header('Date: '. date('r'));
    1432                         if ($this->do_output)
    1433                                 echo $xml;
    1434                         log_app('function', "output:\n$xml");
    1435                         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();
    14361331        }
    14371332
    14381333        /**
     
    14641359         * @return bool
    14651360         */
    14661361        function authenticate() {
    1467                 log_app("authenticate()",print_r($_ENV, true));
    14681362
    14691363                // if using mod_rewrite/ENV hack
    14701364                // http://www.besthostratings.com/articles/http-auth-php-cgi.html
     
    14801374
    14811375                // If Basic Auth is working...
    14821376                if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
    1483                         log_app("Basic Auth",$_SERVER['PHP_AUTH_USER']);
    14841377
    14851378                        $user = wp_authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
    14861379                        if ( $user && !is_wp_error($user) ) {
    14871380                                wp_set_current_user($user->ID);
    1488                                 log_app("authenticate()", $user->user_login);
    14891381                                return true;
    14901382                        }
    14911383                }
     
    15141406                $type = $_SERVER['CONTENT_TYPE'];
    15151407                list($type,$subtype) = explode('/',$type);
    15161408                list($subtype) = explode(";",$subtype); // strip MIME parameters
    1517                 log_app("get_accepted_content_type", "type=$type, subtype=$subtype");
    15181409
    15191410                foreach($types as $t) {
    15201411                        list($acceptedType,$acceptedSubtype) = explode('/',$t);
     
    15711462                (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :
    15721463                (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {
    15731464                        status_header( 304 );
    1574                         exit;
     1465                        wp_die();
    15751466                }
    15761467        }
    15771468
     
    16151506        }
    16161507
    16171508}
    1618 
    1619 /**
    1620  * AtomServer
    1621  * @var AtomServer
    1622  * @global object $server
    1623  */
    1624 $server = new AtomServer();
    1625 $server->handle_request();
  • wp-app.php

     
    1818/** Atom Publishing Protocol Class */
    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'] );
    2528
    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                 if ( !empty( $entry->categories ) ) {
    404                         foreach ( $entry->categories as $cat ) {
    405                                 array_push($catnames, $cat["term"]);
    406                         }
    407                 }
    408 
    409                 $wp_cats = get_categories(array('hide_empty' => false));
    410 
    411                 $post_category = array();
    412 
    413                 foreach ( $wp_cats as $cat ) {
    414                         if ( in_array($cat->name, $catnames) )
    415                                 array_push($post_category, $cat->term_id);
    416                 }
    417 
    418                 $publish = ! ( isset( $entry->draft ) && 'yes' == trim( $entry->draft ) );
    419 
    420                 $cap = ($publish) ? 'publish_posts' : 'edit_posts';
    421 
    422                 if ( !current_user_can($cap) )
    423                         $this->auth_required(__('Sorry, you do not have the right to edit/publish new posts.'));
    424 
    425                 $blog_ID = get_current_blog_id();
    426                 $post_status = ($publish) ? 'publish' : 'draft';
    427                 $post_author = (int) $user_ID;
    428 
    429                 $post_title   = '';
    430                 $post_content = '';
    431                 $post_excerpt = '';
    432                 $pubtimes     = '';
    433                
    434                 if ( isset( $entry->title ) && is_array( $entry->title ) && !empty( $entry->title[1] ) )
    435                         $post_title = (string) $entry->title[1];
    436                 if ( isset( $entry->content ) && is_array( $entry->content ) && !empty( $entry->content[1] ) )
    437                         $post_content = (string) $entry->content[1];
    438                 if ( isset( $entry->summary ) && is_array( $entry->summary ) && !empty( $entry->summary[1] ) )
    439                         $post_excerpt = (string) $entry->summary[1];
    440                 if ( !empty( $entry->published ) )
    441                         $pubtimes = (string) $entry->published;
    442                
    443                 $pubtimes = $this->get_publish_time( $pubtimes );
    444 
    445                 $post_date = $pubtimes[0];
    446                 $post_date_gmt = $pubtimes[1];
    447 
    448                 if ( isset( $_SERVER['HTTP_SLUG'] ) )
    449                         $post_name = $_SERVER['HTTP_SLUG'];
    450 
    451                 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_name');
    452 
    453                 $this->escape($post_data);
    454                 log_app('Inserting Post. Data:', print_r($post_data,true));
    455 
    456                 $postID = wp_insert_post($post_data);
    457                 if ( is_wp_error( $postID ) )
    458                         $this->internal_error($postID->get_error_message());
    459 
    460                 if ( !$postID )
    461                         $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.'));
    462 
    463                 // getting warning here about unable to set headers
    464                 // because something in the cache is printing to the buffer
    465                 // could we clean up wp_set_post_categories or cache to not print
    466                 // this could affect our ability to send back the right headers
    467                 @wp_set_post_categories($postID, $post_category);
    468 
    469                 do_action( 'atompub_create_post', $postID, $entry );
    470 
    471                 $output = $this->get_entry($postID);
    472 
    473                 log_app('function',"create_post($postID)");
    474                 $this->created($postID, $output);
    475         }
    476 
    477         /**
    478          * Retrieve post.
    479          *
    480          * @since 2.2.0
    481          *
    482          * @param int $postID Post ID.
    483          */
    484         function get_post($postID) {
    485                 global $entry;
    486 
    487                 if ( !current_user_can( 'edit_post', $postID ) )
    488                         $this->auth_required( __( 'Sorry, you do not have the right to access this post.' ) );
    489 
    490                 $this->set_current_entry($postID);
    491                 $output = $this->get_entry($postID);
    492                 log_app('function',"get_post($postID)");
    493                 $this->output($output);
    494 
    495         }
    496 
    497         /**
    498          * Update post.
    499          *
    500          * @since 2.2.0
    501          *
    502          * @param int $postID Post ID.
    503          */
    504         function put_post($postID) {
    505                 // checked for valid content-types (atom+xml)
    506                 // quick check and exit
    507                 $this->get_accepted_content_type($this->atom_content_types);
    508 
    509                 $parser = new AtomParser();
    510                 if ( !$parser->parse() )
    511                         $this->bad_request();
    512 
    513                 $parsed = array_pop($parser->feed->entries);
    514 
    515                 log_app('Received UPDATED entry:', print_r($parsed,true));
    516 
    517                 // check for not found
    518                 global $entry;
    519                 $this->set_current_entry($postID);
    520 
    521                 if ( !current_user_can('edit_post', $entry['ID']) )
    522                         $this->auth_required(__('Sorry, you do not have the right to edit this post.'));
    523 
    524                 $publish = ! ( isset($parsed->draft) && 'yes' == trim($parsed->draft) );
    525                 $post_status = ($publish) ? 'publish' : 'draft';
    526 
    527                 extract($entry);
    528 
    529                 $post_title = $parsed->title[1];
    530                 $post_content = $parsed->content[1];
    531                 $post_excerpt = $parsed->summary[1];
    532                 $pubtimes = $this->get_publish_time($entry->published);
    533                 $post_date = $pubtimes[0];
    534                 $post_date_gmt = $pubtimes[1];
    535                 $pubtimes = $this->get_publish_time($parsed->updated);
    536                 $post_modified = $pubtimes[0];
    537                 $post_modified_gmt = $pubtimes[1];
    538 
    539                 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt');
    540                 $this->escape($postdata);
    541 
    542                 $result = wp_update_post($postdata);
    543 
    544                 if ( !$result )
    545                         $this->internal_error(__('For some strange yet very annoying reason, this post could not be edited.'));
    546 
    547                 do_action( 'atompub_put_post', $ID, $parsed );
    548 
    549                 log_app('function',"put_post($postID)");
    550                 $this->ok();
    551         }
    552 
    553         /**
    554          * Remove post.
    555          *
    556          * @since 2.2.0
    557          *
    558          * @param int $postID Post ID.
    559          */
    560         function delete_post($postID) {
    561 
    562                 // check for not found
    563                 global $entry;
    564                 $this->set_current_entry($postID);
    565 
    566                 if ( !current_user_can('edit_post', $postID) )
    567                         $this->auth_required(__('Sorry, you do not have the right to delete this post.'));
    568 
    569                 if ( $entry['post_type'] == 'attachment' ) {
    570                         $this->delete_attachment($postID);
    571                 } else {
    572                         $result = wp_delete_post($postID);
    573 
    574                         if ( !$result ) {
    575                                 $this->internal_error(__('For some strange yet very annoying reason, this post could not be deleted.'));
    576                         }
    577 
    578                         log_app('function',"delete_post($postID)");
    579                         $this->ok();
    580                 }
    581 
    582         }
    583 
    584         /**
    585          * Retrieve attachment.
    586          *
    587          * @since 2.2.0
    588          *
    589          * @param int $postID Optional. Post ID.
    590          */
    591         function get_attachment($postID = null) {
    592                 if ( !current_user_can( 'upload_files' ) )
    593                         $this->auth_required( __( 'Sorry, you do not have permission to upload files.' ) );
    594 
    595                 if ( !isset($postID) ) {
    596                         $this->get_attachments();
    597                 } else {
    598                         $this->set_current_entry($postID);
    599                         $output = $this->get_entry($postID, 'attachment');
    600                         log_app('function',"get_attachment($postID)");
    601                         $this->output($output);
    602                 }
    603         }
    604 
    605         /**
    606          * Create new attachment.
    607          *
    608          * @since 2.2.0
    609          */
    610         function create_attachment() {
    611 
    612                 $type = $this->get_accepted_content_type();
    613 
    614                 if ( !current_user_can('upload_files') )
    615                         $this->auth_required(__('You do not have permission to upload files.'));
    616 
    617                 $fp = fopen("php://input", "rb");
    618                 $bits = null;
    619                 while ( !feof($fp) ) {
    620                         $bits .= fread($fp, 4096);
    621                 }
    622                 fclose($fp);
    623 
    624                 $slug = '';
    625                 if ( isset( $_SERVER['HTTP_SLUG'] ) )
    626                         $slug = $_SERVER['HTTP_SLUG'];
    627                 elseif ( isset( $_SERVER['HTTP_TITLE'] ) )
    628                         $slug = $_SERVER['HTTP_TITLE'];
    629                 elseif ( empty( $slug ) ) // just make a random name
    630                         $slug = substr( md5( uniqid( microtime() ) ), 0, 7);
    631                 $ext = preg_replace( '|.*/([a-z0-9]+)|', '$1', $_SERVER['CONTENT_TYPE'] );
    632                 $slug = sanitize_file_name( "$slug.$ext" );
    633                 $file = wp_upload_bits( $slug, null, $bits);
    634 
    635                 log_app('wp_upload_bits returns:',print_r($file,true));
    636 
    637                 $url = $file['url'];
    638                 $file = $file['file'];
    639 
    640                 do_action('wp_create_file_in_uploads', $file); // replicate
    641 
    642                 // Construct the attachment array
    643                 $attachment = array(
    644                         'post_title' => $slug,
    645                         'post_content' => $slug,
    646                         'post_status' => 'attachment',
    647                         'post_parent' => 0,
    648                         'post_mime_type' => $type,
    649                         'guid' => $url
    650                         );
    651 
    652                 // Save the data
    653                 $postID = wp_insert_attachment($attachment, $file);
    654 
    655                 if (!$postID)
    656                         $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.'));
    657 
    658                 $output = $this->get_entry($postID, 'attachment');
    659 
    660                 $this->created($postID, $output, 'attachment');
    661                 log_app('function',"create_attachment($postID)");
    662         }
    663 
    664         /**
    665          * Update attachment.
    666          *
    667          * @since 2.2.0
    668          *
    669          * @param int $postID Post ID.
    670          */
    671         function put_attachment($postID) {
    672                 // checked for valid content-types (atom+xml)
    673                 // quick check and exit
    674                 $this->get_accepted_content_type($this->atom_content_types);
    675 
    676                 $parser = new AtomParser();
    677                 if (!$parser->parse()) {
    678                         $this->bad_request();
    679                 }
    680 
    681                 $parsed = array_pop($parser->feed->entries);
    682 
    683                 // check for not found
    684                 global $entry;
    685                 $this->set_current_entry($postID);
    686 
    687                 if ( !current_user_can('edit_post', $entry['ID']) )
    688                         $this->auth_required(__('Sorry, you do not have the right to edit this post.'));
    689 
    690                 extract($entry);
    691 
    692                 $post_title = $parsed->title[1];
    693                 $post_content = $parsed->summary[1];
    694                 $pubtimes = $this->get_publish_time($parsed->updated);
    695                 $post_modified = $pubtimes[0];
    696                 $post_modified_gmt = $pubtimes[1];
    697 
    698                 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_modified', 'post_modified_gmt');
    699                 $this->escape($postdata);
    700 
    701                 $result = wp_update_post($postdata);
    702 
    703                 if ( !$result )
    704                         $this->internal_error(__('For some strange yet very annoying reason, this post could not be edited.'));
    705 
    706                 log_app('function',"put_attachment($postID)");
    707                 $this->ok();
    708         }
    709 
    710         /**
    711          * Remove attachment.
    712          *
    713          * @since 2.2.0
    714          *
    715          * @param int $postID Post ID.
    716          */
    717         function delete_attachment($postID) {
    718                 log_app('function',"delete_attachment($postID). File '$location' deleted.");
    719 
    720                 // check for not found
    721                 global $entry;
    722                 $this->set_current_entry($postID);
    723 
    724                 if ( !current_user_can('edit_post', $postID) )
    725                         $this->auth_required(__('Sorry, you do not have the right to delete this post.'));
    726 
    727                 $location = get_post_meta($entry['ID'], '_wp_attached_file', true);
    728                 $filetype = wp_check_filetype($location);
    729 
    730                 if ( !isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext']) )
    731                         $this->internal_error(__('Error occurred while accessing post metadata for file location.'));
    732 
    733                 // delete file
    734                 @unlink($location);
    735 
    736                 // delete attachment
    737                 $result = wp_delete_post($postID);
    738 
    739                 if ( !$result )
    740                         $this->internal_error(__('For some strange yet very annoying reason, this post could not be deleted.'));
    741 
    742                 log_app('function',"delete_attachment($postID). File '$location' deleted.");
    743                 $this->ok();
    744         }
    745 
    746         /**
    747          * Retrieve attachment from post.
    748          *
    749          * @since 2.2.0
    750          *
    751          * @param int $postID Post ID.
    752          */
    753         function get_file($postID) {
    754 
    755                 // check for not found
    756                 global $entry;
    757                 $this->set_current_entry($postID);
    758 
    759                 // then whether user can edit the specific post
    760                 if ( !current_user_can('edit_post', $postID) )
    761                         $this->auth_required(__('Sorry, you do not have the right to edit this post.'));
    762 
    763                 $location = get_post_meta($entry['ID'], '_wp_attached_file', true);
    764                 $location = get_option ('upload_path') . '/' . $location;
    765                 $filetype = wp_check_filetype($location);
    766 
    767                 if ( !isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext']) )
    768                         $this->internal_error(__('Error occurred while accessing post metadata for file location.'));
    769 
    770                 status_header('200');
    771                 header('Content-Type: ' . $entry['post_mime_type']);
    772                 header('Connection: close');
    773 
    774                 if ( $fp = fopen($location, "rb") ) {
    775                         status_header('200');
    776                         header('Content-Type: ' . $entry['post_mime_type']);
    777                         header('Connection: close');
    778 
    779                         while ( !feof($fp) ) {
    780                                 echo fread($fp, 4096);
    781                         }
    782 
    783                         fclose($fp);
    784                 } else {
    785                         status_header ('404');
    786                 }
    787 
    788                 log_app('function',"get_file($postID)");
    789                 exit;
    790         }
     29// Allow for a plugin to insert a different class to handle requests.
     30$wp_atom_server_class = apply_filters('wp_atom_server_class', 'wp_atom_server');
     31$wp_atom_server = new $wp_atom_server_class;
    79132
    792         /**
    793          * Upload file to blog and add attachment to post.
    794          *
    795          * @since 2.2.0
    796          *
    797          * @param int $postID Post ID.
    798          */
    799         function put_file($postID) {
    800 
    801                 // first check if user can upload
    802                 if ( !current_user_can('upload_files') )
    803                         $this->auth_required(__('You do not have permission to upload files.'));
    804 
    805                 // check for not found
    806                 global $entry;
    807                 $this->set_current_entry($postID);
    808 
    809                 // then whether user can edit the specific post
    810                 if ( !current_user_can('edit_post', $postID) )
    811                         $this->auth_required(__('Sorry, you do not have the right to edit this post.'));
    812 
    813                 $upload_dir = wp_upload_dir( );
    814                 $location = get_post_meta($entry['ID'], '_wp_attached_file', true);
    815                 $filetype = wp_check_filetype($location);
    816 
    817                 $location = "{$upload_dir['basedir']}/{$location}";
    818 
    819                 if (!isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext']))
    820                         $this->internal_error(__('Error occurred while accessing post metadata for file location.'));
    821 
    822                 $fp = fopen("php://input", "rb");
    823                 $localfp = fopen($location, "w+");
    824                 while ( !feof($fp) ) {
    825                         fwrite($localfp,fread($fp, 4096));
    826                 }
    827                 fclose($fp);
    828                 fclose($localfp);
    829 
    830                 $ID = $entry['ID'];
    831                 $pubtimes = $this->get_publish_time($entry->published);
    832                 $post_date = $pubtimes[0];
    833                 $post_date_gmt = $pubtimes[1];
    834                 $pubtimes = $this->get_publish_time($parsed->updated);
    835                 $post_modified = $pubtimes[0];
    836                 $post_modified_gmt = $pubtimes[1];
    837 
    838                 $post_data = compact('ID', 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt');
    839                 $result = wp_update_post($post_data);
    840 
    841                 if ( !$result )
    842                         $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.'));
    843 
    844                 wp_update_attachment_metadata( $postID, wp_generate_attachment_metadata( $postID, $location ) );
    845 
    846                 log_app('function',"put_file($postID)");
    847                 $this->ok();
    848         }
    849 
    850         /**
    851          * Retrieve entries URL.
    852          *
    853          * @since 2.2.0
    854          *
    855          * @param int $page Page ID.
    856          * @return string
    857          */
    858         function get_entries_url($page = null) {
    859                 if ( isset($GLOBALS['post_type']) && ( $GLOBALS['post_type'] == 'attachment' ) )
    860                         $path = $this->MEDIA_PATH;
    861                 else
    862                         $path = $this->ENTRIES_PATH;
    863                 $url = $this->app_base . $path;
    864                 if ( isset($page) && is_int($page) )
    865                         $url .= "/$page";
    866                 return $url;
    867         }
    868 
    869         /**
    870          * Display entries URL.
    871          *
    872          * @since 2.2.0
    873          *
    874          * @param int $page Page ID.
    875          */
    876         function the_entries_url($page = null) {
    877                 echo $this->get_entries_url($page);
    878         }
    879 
    880         /**
    881          * Retrieve categories URL.
    882          *
    883          * @since 2.2.0
    884          *
    885          * @param mixed $deprecated Not used.
    886          * @return string
    887          */
    888         function get_categories_url($deprecated = '') {
    889                 if ( !empty( $deprecated ) )
    890                         _deprecated_argument( __FUNCTION__, '2.5' );
    891                 return $this->app_base . $this->CATEGORIES_PATH;
    892         }
    893 
    894         /**
    895          * Display category URL.
    896          *
    897          * @since 2.2.0
    898          */
    899         function the_categories_url() {
    900                 echo $this->get_categories_url();
    901         }
    902 
    903         /**
    904          * Retrieve attachment URL.
    905          *
    906          * @since 2.2.0
    907          *
    908          * @param int $page Page ID.
    909          * @return string
    910          */
    911         function get_attachments_url($page = null) {
    912                 $url = $this->app_base . $this->MEDIA_PATH;
    913                 if (isset($page) && is_int($page)) {
    914                         $url .= "/$page";
    915                 }
    916                 return $url;
    917         }
    918 
    919         /**
    920          * Display attachment URL.
    921          *
    922          * @since 2.2.0
    923          *
    924          * @param int $page Page ID.
    925          */
    926         function the_attachments_url($page = null) {
    927                 echo $this->get_attachments_url($page);
    928         }
    929 
    930         /**
    931          * Retrieve service URL.
    932          *
    933          * @since 2.3.0
    934          *
    935          * @return string
    936          */
    937         function get_service_url() {
    938                 return $this->app_base . $this->SERVICE_PATH;
    939         }
    940 
    941         /**
    942          * Retrieve entry URL.
    943          *
    944          * @since 2.7.0
    945          *
    946          * @param int $postID Post ID.
    947          * @return string
    948          */
    949         function get_entry_url($postID = null) {
    950                 if (!isset($postID)) {
    951                         global $post;
    952                         $postID = (int) $post->ID;
    953                 }
    954 
    955                 $url = $this->app_base . $this->ENTRY_PATH . "/$postID";
    956 
    957                 log_app('function',"get_entry_url() = $url");
    958                 return $url;
    959         }
    960 
    961         /**
    962          * Display entry URL.
    963          *
    964          * @since 2.7.0
    965          *
    966          * @param int $postID Post ID.
    967          */
    968         function the_entry_url($postID = null) {
    969                 echo $this->get_entry_url($postID);
    970         }
    971 
    972         /**
    973          * Retrieve media URL.
    974          *
    975          * @since 2.2.0
    976          *
    977          * @param int $postID Post ID.
    978          * @return string
    979          */
    980         function get_media_url($postID = null) {
    981                 if (!isset($postID)) {
    982                         global $post;
    983                         $postID = (int) $post->ID;
    984                 }
    985 
    986                 $url = $this->app_base . $this->MEDIA_SINGLE_PATH ."/file/$postID";
    987 
    988                 log_app('function',"get_media_url() = $url");
    989                 return $url;
    990         }
    991 
    992         /**
    993          * Display the media URL.
    994          *
    995          * @since 2.2.0
    996          *
    997          * @param int $postID Post ID.
    998          */
    999         function the_media_url($postID = null) {
    1000                 echo $this->get_media_url($postID);
    1001         }
    1002 
    1003         /**
    1004          * Set the current entry to post ID.
    1005          *
    1006          * @since 2.2.0
    1007          *
    1008          * @param int $postID Post ID.
    1009          */
    1010         function set_current_entry($postID) {
    1011                 global $entry;
    1012                 log_app('function',"set_current_entry($postID)");
    1013 
    1014                 if (!isset($postID)) {
    1015                         // $this->bad_request();
    1016                         $this->not_found();
    1017                 }
    1018 
    1019                 $entry = wp_get_single_post($postID,ARRAY_A);
    1020 
    1021                 if (!isset($entry) || !isset($entry['ID']))
    1022                         $this->not_found();
    1023 
    1024                 return;
    1025         }
    1026 
    1027         /**
    1028          * Display posts XML.
    1029          *
    1030          * @since 2.2.0
    1031          *
    1032          * @param int $page Optional. Page ID.
    1033          * @param string $post_type Optional, default is 'post'. Post Type.
    1034          */
    1035         function get_posts($page = 1, $post_type = 'post') {
    1036                         log_app('function',"get_posts($page, '$post_type')");
    1037                         $feed = $this->get_feed($page, $post_type);
    1038                         $this->output($feed);
    1039         }
    1040 
    1041         /**
    1042          * Display attachment XML.
    1043          *
    1044          * @since 2.2.0
    1045          *
    1046          * @param int $page Page ID.
    1047          * @param string $post_type Optional, default is 'attachment'. Post type.
    1048          */
    1049         function get_attachments($page = 1, $post_type = 'attachment') {
    1050                 log_app('function',"get_attachments($page, '$post_type')");
    1051                 $GLOBALS['post_type'] = $post_type;
    1052                 $feed = $this->get_feed($page, $post_type);
    1053                 $this->output($feed);
    1054         }
    1055 
    1056         /**
    1057          * Retrieve feed XML.
    1058          *
    1059          * @since 2.2.0
    1060          *
    1061          * @param int $page Page ID.
    1062          * @param string $post_type Optional, default is post. Post type.
    1063          * @return string
    1064          */
    1065         function get_feed($page = 1, $post_type = 'post') {
    1066                 global $post, $wp, $wp_query, $posts, $wpdb, $blog_id;
    1067                 log_app('function',"get_feed($page, '$post_type')");
    1068                 ob_start();
    1069 
    1070                 $this->ENTRY_PATH = $post_type;
    1071 
    1072                 if (!isset($page)) {
    1073                         $page = 1;
    1074                 }
    1075                 $page = (int) $page;
    1076 
    1077                 $count = get_option('posts_per_rss');
    1078 
    1079                 wp('posts_per_page=' . $count . '&offset=' . ($count * ($page-1) . '&orderby=modified'));
    1080 
    1081                 $post = $GLOBALS['post'];
    1082                 $posts = $GLOBALS['posts'];
    1083                 $wp = $GLOBALS['wp'];
    1084                 $wp_query = $GLOBALS['wp_query'];
    1085                 $wpdb = $GLOBALS['wpdb'];
    1086                 $blog_id = (int) $GLOBALS['blog_id'];
    1087                 log_app('function',"query_posts(# " . print_r($wp_query, true) . "#)");
    1088 
    1089                 log_app('function',"total_count(# $wp_query->max_num_pages #)");
    1090                 $last_page = $wp_query->max_num_pages;
    1091                 $next_page = (($page + 1) > $last_page) ? null : $page + 1;
    1092                 $prev_page = ($page - 1) < 1 ? null : $page - 1;
    1093                 $last_page = ((int)$last_page == 1 || (int)$last_page == 0) ? null : (int) $last_page;
    1094                 $self_page = $page > 1 ? $page : null;
    1095 ?><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'); ?> >
    1096 <id><?php $this->the_entries_url() ?></id>
    1097 <updated><?php echo mysql2date('Y-m-d\TH:i:s\Z', get_lastpostmodified('GMT'), false); ?></updated>
    1098 <title type="text"><?php bloginfo_rss('name') ?></title>
    1099 <subtitle type="text"><?php bloginfo_rss("description") ?></subtitle>
    1100 <link rel="first" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url() ?>" />
    1101 <?php if (isset($prev_page)): ?>
    1102 <link rel="previous" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($prev_page) ?>" />
    1103 <?php endif; ?>
    1104 <?php if (isset($next_page)): ?>
    1105 <link rel="next" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($next_page) ?>" />
    1106 <?php endif; ?>
    1107 <link rel="last" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($last_page) ?>" />
    1108 <link rel="self" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($self_page) ?>" />
    1109 <rights type="text">Copyright <?php echo date('Y'); ?></rights>
    1110 <?php do_action('app_head'); ?>
    1111 <?php if ( have_posts() ) {
    1112                         while ( have_posts() ) {
    1113                                 the_post();
    1114                                 $this->echo_entry();
    1115                         }
    1116                 }
    1117 ?></feed>
    1118 <?php
    1119                 $feed = ob_get_contents();
    1120                 ob_end_clean();
    1121                 return $feed;
    1122         }
    1123 
    1124         /**
    1125          * Display entry XML.
    1126          *
    1127          * @since 2.2.0
    1128          *
    1129          * @param int $postID Post ID.
    1130          * @param string $post_type Optional, default is post. Post type.
    1131          * @return string.
    1132          */
    1133         function get_entry($postID, $post_type = 'post') {
    1134                 log_app('function',"get_entry($postID, '$post_type')");
    1135                 ob_start();
    1136                 switch($post_type) {
    1137                         case 'post':
    1138                                 $varname = 'p';
    1139                                 break;
    1140                         case 'attachment':
    1141                                 $this->ENTRY_PATH = 'attachment';
    1142                                 $varname = 'attachment_id';
    1143                                 break;
    1144                 }
    1145                 query_posts($varname . '=' . $postID);
    1146                 if ( have_posts() ) {
    1147                         while ( have_posts() ) {
    1148                                 the_post();
    1149                                 $this->echo_entry();
    1150                                 log_app('$post',print_r($GLOBALS['post'],true));
    1151                                 $entry = ob_get_contents();
    1152                                 break;
    1153                         }
    1154                 }
    1155                 ob_end_clean();
    1156 
    1157                 log_app('get_entry returning:',$entry);
    1158                 return $entry;
    1159         }
    1160 
    1161         /**
    1162          * Display post content XML.
    1163          *
    1164          * @since 2.3.0
    1165          */
    1166         function echo_entry() { ?>
    1167 <entry xmlns="<?php echo $this->ATOM_NS ?>"
    1168        xmlns:app="<?php echo $this->ATOMPUB_NS ?>" xml:lang="<?php bloginfo_rss( 'language' ); ?>">
    1169         <id><?php the_guid( $GLOBALS['post']->ID ); ?></id>
    1170 <?php list($content_type, $content) = prep_atom_text_construct(get_the_title()); ?>
    1171         <title type="<?php echo $content_type ?>"><?php echo $content ?></title>
    1172         <updated><?php echo get_post_modified_time('Y-m-d\TH:i:s\Z', true); ?></updated>
    1173         <published><?php echo get_post_time('Y-m-d\TH:i:s\Z', true); ?></published>
    1174         <app:edited><?php echo get_post_modified_time('Y-m-d\TH:i:s\Z', true); ?></app:edited>
    1175         <app:control>
    1176                 <app:draft><?php echo ($GLOBALS['post']->post_status == 'draft' ? 'yes' : 'no') ?></app:draft>
    1177         </app:control>
    1178         <author>
    1179                 <name><?php the_author()?></name>
    1180 <?php if ( get_the_author_meta('url') && get_the_author_meta('url') != 'http://' ) { ?>
    1181                 <uri><?php the_author_meta('url') ?></uri>
    1182 <?php } ?>
    1183         </author>
    1184 <?php if ($GLOBALS['post']->post_type == 'attachment') { ?>
    1185         <link rel="edit-media" href="<?php $this->the_media_url() ?>" />
    1186         <content type="<?php echo $GLOBALS['post']->post_mime_type ?>" src="<?php the_guid() ; ?>"/>
    1187 <?php } else { ?>
    1188         <link href="<?php the_permalink_rss() ?>" />
    1189 <?php if ( strlen( $GLOBALS['post']->post_content ) ) :
    1190 list($content_type, $content) = prep_atom_text_construct(get_the_content()); ?>
    1191         <content type="<?php echo $content_type ?>"><?php echo $content ?></content>
    1192 <?php endif; ?>
    1193 <?php } ?>
    1194         <link rel="edit" href="<?php $this->the_entry_url() ?>" />
    1195         <?php the_category_rss( 'atom' ); ?>
    1196 <?php list($content_type, $content) = prep_atom_text_construct(get_the_excerpt()); ?>
    1197         <summary type="<?php echo $content_type ?>"><?php echo $content ?></summary>
    1198         <?php do_action('app_entry'); ?>
    1199 </entry>
    1200 <?php }
    1201 
    1202         /**
    1203          * Set 'OK' (200) status header.
    1204          *
    1205          * @since 2.2.0
    1206          */
    1207         function ok() {
    1208                 log_app('Status','200: OK');
    1209                 header('Content-Type: text/plain');
    1210                 status_header('200');
    1211                 exit;
    1212         }
    1213 
    1214         /**
    1215          * Set 'No Content' (204) status header.
    1216          *
    1217          * @since 2.2.0
    1218          */
    1219         function no_content() {
    1220                 log_app('Status','204: No Content');
    1221                 header('Content-Type: text/plain');
    1222                 status_header('204');
    1223                 echo "Moved to Trash.";
    1224                 exit;
    1225         }
    1226 
    1227         /**
    1228          * Display 'Internal Server Error' (500) status header.
    1229          *
    1230          * @since 2.2.0
    1231          *
    1232          * @param string $msg Optional. Status string.
    1233          */
    1234         function internal_error($msg = 'Internal Server Error') {
    1235                 log_app('Status','500: Server Error');
    1236                 header('Content-Type: text/plain');
    1237                 status_header('500');
    1238                 echo $msg;
    1239                 exit;
    1240         }
    1241 
    1242         /**
    1243          * Set 'Bad Request' (400) status header.
    1244          *
    1245          * @since 2.2.0
    1246          */
    1247         function bad_request() {
    1248                 log_app('Status','400: Bad Request');
    1249                 header('Content-Type: text/plain');
    1250                 status_header('400');
    1251                 exit;
    1252         }
    1253 
    1254         /**
    1255          * Set 'Length Required' (411) status header.
    1256          *
    1257          * @since 2.2.0
    1258          */
    1259         function length_required() {
    1260                 log_app('Status','411: Length Required');
    1261                 header("HTTP/1.1 411 Length Required");
    1262                 header('Content-Type: text/plain');
    1263                 status_header('411');
    1264                 exit;
    1265         }
    1266 
    1267         /**
    1268          * Set 'Unsupported Media Type' (415) status header.
    1269          *
    1270          * @since 2.2.0
    1271          */
    1272         function invalid_media() {
    1273                 log_app('Status','415: Unsupported Media Type');
    1274                 header("HTTP/1.1 415 Unsupported Media Type");
    1275                 header('Content-Type: text/plain');
    1276                 exit;
    1277         }
    1278 
    1279         /**
    1280          * Set 'Forbidden' (403) status header.
    1281          *
    1282          * @since 2.6.0
    1283          */
    1284         function forbidden($reason='') {
    1285                 log_app('Status','403: Forbidden');
    1286                 header('Content-Type: text/plain');
    1287                 status_header('403');
    1288                 echo $reason;
    1289                 exit;
    1290         }
    1291 
    1292         /**
    1293          * Set 'Not Found' (404) status header.
    1294          *
    1295          * @since 2.2.0
    1296          */
    1297         function not_found() {
    1298                 log_app('Status','404: Not Found');
    1299                 header('Content-Type: text/plain');
    1300                 status_header('404');
    1301                 exit;
    1302         }
    1303 
    1304         /**
    1305          * Set 'Not Allowed' (405) status header.
    1306          *
    1307          * @since 2.2.0
    1308          */
    1309         function not_allowed($allow) {
    1310                 log_app('Status','405: Not Allowed');
    1311                 header('Allow: ' . join(',', $allow));
    1312                 status_header('405');
    1313                 exit;
    1314         }
    1315 
    1316         /**
    1317          * Display Redirect (302) content and set status headers.
    1318          *
    1319          * @since 2.3.0
    1320          */
    1321         function redirect($url) {
    1322 
    1323                 log_app('Status','302: Redirect');
    1324                 $escaped_url = esc_attr($url);
    1325                 $content = <<<EOD
    1326 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
    1327 <html>
    1328   <head>
    1329     <title>302 Found</title>
    1330   </head>
    1331 <body>
    1332   <h1>Found</h1>
    1333   <p>The document has moved <a href="$escaped_url">here</a>.</p>
    1334   </body>
    1335 </html>
    1336 
    1337 EOD;
    1338                 header('HTTP/1.1 302 Moved');
    1339                 header('Content-Type: text/html');
    1340                 header('Location: ' . $url);
    1341                 echo $content;
    1342                 exit;
    1343 
    1344         }
    1345 
    1346         /**
    1347          * Set 'Client Error' (400) status header.
    1348          *
    1349          * @since 2.2.0
    1350          */
    1351         function client_error($msg = 'Client Error') {
    1352                 log_app('Status','400: Client Error');
    1353                 header('Content-Type: text/plain');
    1354                 status_header('400');
    1355                 exit;
    1356         }
    1357 
    1358         /**
    1359          * Set created status headers (201).
    1360          *
    1361          * Sets the 'content-type', 'content-location', and 'location'.
    1362          *
    1363          * @since 2.2.0
    1364          */
    1365         function created($post_ID, $content, $post_type = 'post') {
    1366                 log_app('created()::$post_ID',"$post_ID, $post_type");
    1367                 $edit = $this->get_entry_url($post_ID);
    1368                 switch($post_type) {
    1369                         case 'post':
    1370                                 $ctloc = $this->get_entry_url($post_ID);
    1371                                 break;
    1372                         case 'attachment':
    1373                                 $edit = $this->app_base . "attachments/$post_ID";
    1374                                 break;
    1375                 }
    1376                 header("Content-Type: $this->ATOM_CONTENT_TYPE");
    1377                 if (isset($ctloc))
    1378                         header('Content-Location: ' . $ctloc);
    1379                 header('Location: ' . $edit);
    1380                 status_header('201');
    1381                 echo $content;
    1382                 exit;
    1383         }
    1384 
    1385         /**
    1386          * Set 'Auth Required' (401) headers.
    1387          *
    1388          * @since 2.2.0
    1389          *
    1390          * @param string $msg Status header content and HTML content.
    1391          */
    1392         function auth_required($msg) {
    1393                 log_app('Status','401: Auth Required');
    1394                 nocache_headers();
    1395                 header('WWW-Authenticate: Basic realm="WordPress Atom Protocol"');
    1396                 header("HTTP/1.1 401 $msg");
    1397                 header('Status: 401 ' . $msg);
    1398                 header('Content-Type: text/html');
    1399                 $content = <<<EOD
    1400 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
    1401 <html>
    1402   <head>
    1403     <title>401 Unauthorized</title>
    1404   </head>
    1405 <body>
    1406     <h1>401 Unauthorized</h1>
    1407     <p>$msg</p>
    1408   </body>
    1409 </html>
    1410 
    1411 EOD;
    1412                 echo $content;
    1413                 exit;
    1414         }
    1415 
    1416         /**
    1417          * Display XML and set headers with content type.
    1418          *
    1419          * @since 2.2.0
    1420          *
    1421          * @param string $xml Display feed content.
    1422          * @param string $ctype Optional, default is 'atom+xml'. Feed content type.
    1423          */
    1424         function output($xml, $ctype = 'application/atom+xml') {
    1425                         status_header('200');
    1426                         $xml = '<?xml version="1.0" encoding="' . strtolower(get_option('blog_charset')) . '"?>'."\n".$xml;
    1427                         header('Connection: close');
    1428                         header('Content-Length: '. strlen($xml));
    1429                         header('Content-Type: ' . $ctype);
    1430                         header('Content-Disposition: attachment; filename=atom.xml');
    1431                         header('Date: '. date('r'));
    1432                         if ($this->do_output)
    1433                                 echo $xml;
    1434                         log_app('function', "output:\n$xml");
    1435                         exit;
    1436         }
    1437 
    1438         /**
    1439          * Sanitize content for database usage.
    1440          *
    1441          * @since 2.2.0
    1442          *
    1443          * @param array $array Sanitize array and multi-dimension array.
    1444          */
    1445         function escape(&$array) {
    1446                 global $wpdb;
    1447 
    1448                 foreach ($array as $k => $v) {
    1449                                 if (is_array($v)) {
    1450                                                 $this->escape($array[$k]);
    1451                                 } else if (is_object($v)) {
    1452                                                 //skip
    1453                                 } else {
    1454                                                 $array[$k] = $wpdb->escape($v);
    1455                                 }
    1456                 }
    1457         }
    1458 
    1459         /**
    1460          * Access credential through various methods and perform login.
    1461          *
    1462          * @since 2.2.0
    1463          *
    1464          * @return bool
    1465          */
    1466         function authenticate() {
    1467                 log_app("authenticate()",print_r($_ENV, true));
    1468 
    1469                 // if using mod_rewrite/ENV hack
    1470                 // http://www.besthostratings.com/articles/http-auth-php-cgi.html
    1471                 if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
    1472                         list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
    1473                                 explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
    1474                 } else if (isset($_SERVER['REDIRECT_REMOTE_USER'])) {
    1475                         // Workaround for setups that do not forward HTTP_AUTHORIZATION
    1476                         // See http://trac.wordpress.org/ticket/7361
    1477                         list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
    1478                                 explode(':', base64_decode(substr($_SERVER['REDIRECT_REMOTE_USER'], 6)));
    1479                 }
    1480 
    1481                 // If Basic Auth is working...
    1482                 if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
    1483                         log_app("Basic Auth",$_SERVER['PHP_AUTH_USER']);
    1484 
    1485                         $user = wp_authenticate($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
    1486                         if ( $user && !is_wp_error($user) ) {
    1487                                 wp_set_current_user($user->ID);
    1488                                 log_app("authenticate()", $user->user_login);
    1489                                 return true;
    1490                         }
    1491                 }
    1492 
    1493                 return false;
    1494         }
    1495 
    1496         /**
    1497          * Retrieve accepted content types.
    1498          *
    1499          * @since 2.2.0
    1500          *
    1501          * @param array $types Optional. Content Types.
    1502          * @return string
    1503          */
    1504         function get_accepted_content_type($types = null) {
    1505 
    1506                 if (!isset($types)) {
    1507                         $types = $this->media_content_types;
    1508                 }
    1509 
    1510                 if (!isset($_SERVER['CONTENT_LENGTH']) || !isset($_SERVER['CONTENT_TYPE'])) {
    1511                         $this->length_required();
    1512                 }
    1513 
    1514                 $type = $_SERVER['CONTENT_TYPE'];
    1515                 list($type,$subtype) = explode('/',$type);
    1516                 list($subtype) = explode(";",$subtype); // strip MIME parameters
    1517                 log_app("get_accepted_content_type", "type=$type, subtype=$subtype");
    1518 
    1519                 foreach($types as $t) {
    1520                         list($acceptedType,$acceptedSubtype) = explode('/',$t);
    1521                         if ($acceptedType == '*' || $acceptedType == $type) {
    1522                                 if ($acceptedSubtype == '*' || $acceptedSubtype == $subtype)
    1523                                         return $type . "/" . $subtype;
    1524                         }
    1525                 }
    1526 
    1527                 $this->invalid_media();
    1528         }
    1529 
    1530         /**
    1531          * Process conditionals for posts.
    1532          *
    1533          * @since 2.2.0
    1534          */
    1535         function process_conditionals() {
    1536 
    1537                 if (empty($this->params)) return;
    1538                 if ($_SERVER['REQUEST_METHOD'] == 'DELETE') return;
    1539 
    1540                 switch($this->params[0]) {
    1541                         case $this->ENTRY_PATH:
    1542                                 global $post;
    1543                                 $post = wp_get_single_post($this->params[1]);
    1544                                 $wp_last_modified = get_post_modified_time('D, d M Y H:i:s', true);
    1545                                 $post = null;
    1546                                 break;
    1547                         case $this->ENTRIES_PATH:
    1548                                 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT';
    1549                                 break;
    1550                         default:
    1551                                 return;
    1552                 }
    1553                 $wp_etag = md5($wp_last_modified);
    1554                 @header("Last-Modified: $wp_last_modified");
    1555                 @header("ETag: $wp_etag");
    1556 
    1557                 // Support for Conditional GET
    1558                 if (isset($_SERVER['HTTP_IF_NONE_MATCH']))
    1559                         $client_etag = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
    1560                 else
    1561                         $client_etag = false;
    1562 
    1563                 $client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE']);
    1564                 // If string is empty, return 0. If not, attempt to parse into a timestamp
    1565                 $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;
    1566 
    1567                 // Make a timestamp for our most recent modification...
    1568                 $wp_modified_timestamp = strtotime($wp_last_modified);
    1569 
    1570                 if ( ($client_last_modified && $client_etag) ?
    1571                 (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :
    1572                 (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {
    1573                         status_header( 304 );
    1574                         exit;
    1575                 }
    1576         }
    1577 
    1578         /**
    1579          * Convert RFC3339 time string to timestamp.
    1580          *
    1581          * @since 2.3.0
    1582          *
    1583          * @param string $str String to time.
    1584          * @return bool|int false if format is incorrect.
    1585          */
    1586         function rfc3339_str2time($str) {
    1587 
    1588                 $match = false;
    1589                 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))
    1590                         return false;
    1591 
    1592                 if ($match[3] == 'Z')
    1593                         $match[3] = '+0000';
    1594 
    1595                 return strtotime($match[1] . " " . $match[2] . " " . $match[3]);
    1596         }
    1597 
    1598         /**
    1599          * Retrieve published time to display in XML.
    1600          *
    1601          * @since 2.3.0
    1602          *
    1603          * @param string $published Time string.
    1604          * @return string
    1605          */
    1606         function get_publish_time($published) {
    1607 
    1608                 $pubtime = $this->rfc3339_str2time($published);
    1609 
    1610                 if (!$pubtime) {
    1611                         return array(current_time('mysql'),current_time('mysql',1));
    1612                 } else {
    1613                         return array(date("Y-m-d H:i:s", $pubtime), gmdate("Y-m-d H:i:s", $pubtime));
    1614                 }
    1615         }
    1616 
    1617 }
    1618 
    1619 /**
    1620  * AtomServer
    1621  * @var AtomServer
    1622  * @global object $server
    1623  */
    1624 $server = new AtomServer();
    1625 $server->handle_request();
     33// Handle the request
     34$wp_atom_server->handle_request();