WordPress.org

Make WordPress Core

Ticket #20042: 20042.patch

File 20042.patch, 57.7 KB (added by kurtpayne, 2 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();