Make WordPress Core

Ticket #4191: wp-app.php.diff

File wp-app.php.diff, 33.2 KB (added by placey, 17 years ago)

Patch to bring wp-app.php up to conformance with final Atompub spec

  • wp-app.php

     
    1111
    1212require_once('./wp-config.php');
    1313require_once(ABSPATH . WPINC . '/post-template.php');
     14require_once(ABSPATH . WPINC . '/atomlib.php');
    1415
    1516// Attempt to automatically detect whether to use querystring
    1617// or PATH_INFO, based on our environment:
     
    2829                $_SERVER['PATH_INFO'] .= "/$eid";
    2930        }
    3031} else {
    31         $_SERVER['PATH_INFO'] = str_replace( '/wp-app.php', '', $_SERVER['REQUEST_URI'] );
     32        $_SERVER['PATH_INFO'] = str_replace( '.*/wp-app.php', '', $_SERVER['REQUEST_URI'] );
    3233}
    3334
    3435$app_logging = 0;
    3536
     37// TODO: Should be an option somewhere
     38$always_authenticate = 1;
     39
    3640function log_app($label,$msg) {
    3741        global $app_logging;
    3842        if ($app_logging) {
    39                 $fp = fopen( 'app.log', 'a+');
     43                $fp = fopen( 'wp-app.log', 'a+');
    4044                $date = gmdate( 'Y-m-d H:i:s' );
    4145                fwrite($fp, "\n\n$date - $label\n$msg\n");
    4246                fclose($fp);
     
    5761endif;
    5862
    5963function wa_posts_where_include_drafts_filter($where) {
    60         $where = ereg_replace("post_author = ([0-9]+) AND post_status != 'draft'","post_author = \\1 AND post_status = 'draft'", $where);
    61         return $where;
     64        $where = str_replace("post_status = 'publish'","post_status = 'publish' OR post_status = 'future' OR post_status = 'draft' OR post_status = 'inherit'", $where);
     65        return $where;
     66
    6267}
    6368add_filter('posts_where', 'wa_posts_where_include_drafts_filter');
    6469
    65 class AtomEntry {
    66         var $links = array();
    67         var $categories = array();
    68 }
    69 
    70 class AtomParser {
    71 
    72         var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights');
    73         var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft');
    74 
    75         var $depth = 0;
    76         var $indent = 2;
    77         var $in_content;
    78         var $ns_contexts = array();
    79         var $ns_decls = array();
    80         var $is_xhtml = false;
    81         var $skipped_div = false;
    82 
    83         var $entry;
    84 
    85         function AtomParser() {
    86 
    87                 $this->entry = new AtomEntry();
    88                 $this->map_attrs_func = create_function('$k,$v', 'return "$k=\"$v\"";');
    89                 $this->map_xmlns_func = create_function('$p,$n', '$xd = "xmlns"; if(strlen($n[0])>0) $xd .= ":{$n[0]}"; return "{$xd}=\"{$n[1]}\"";');
    90         }
    91 
    92         function parse() {
    93 
    94                 global $app_logging;
    95                 array_unshift($this->ns_contexts, array());
    96 
    97                 $parser = xml_parser_create_ns();
    98                 xml_set_object($parser, $this);
    99                 xml_set_element_handler($parser, "start_element", "end_element");
    100                 xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0);
    101                 xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0);
    102                 xml_set_character_data_handler($parser, "cdata");
    103                 xml_set_default_handler($parser, "_default");
    104                 xml_set_start_namespace_decl_handler($parser, "start_ns");
    105                 xml_set_end_namespace_decl_handler($parser, "end_ns");
    106 
    107                 $contents = "";
    108 
    109                 $fp = fopen("php://input", "r");
    110                 while(!feof($fp)) {
    111                         $line = fgets($fp, 4096);
    112 
    113                         if($app_logging) $contents .= $line;
    114 
    115                         if(!xml_parse($parser, $line)) {
    116                                 log_app("xml_parse_error", "line: $line");
    117                                 $this->error = sprintf(__('XML error: %s at line %d')."\n",
    118                                         xml_error_string(xml_get_error_code($xml_parser)),
    119                                         xml_get_current_line_number($xml_parser));
    120                                 log_app("xml_parse_error", $this->error);
    121                                 return false;
    122                         }
    123                 }
    124                 fclose($fp);
    125 
    126                 xml_parser_free($parser);
    127 
    128                 log_app("AtomParser->parse()",trim($contents));
    129 
    130                 return true;
    131         }
    132 
    133         function start_element($parser, $name, $attrs) {
    134 
    135                 $tag = array_pop(split(":", $name));
    136 
    137                 array_unshift($this->ns_contexts, $this->ns_decls);
    138 
    139                 $this->depth++;
    140 
    141                 #print str_repeat(" ", $this->depth * $this->indent) . "start_element('$name')" ."\n";
    142                 #print str_repeat(" ", $this->depth+1 * $this->indent) . print_r($this->ns_contexts,true) ."\n";
    143 
    144                 if(!empty($this->in_content)) {
    145                         $attrs_prefix = array();
    146 
    147                         // resolve prefixes for attributes
    148                         foreach($attrs as $key => $value) {
    149                                 $attrs_prefix[$this->ns_to_prefix($key)] = $this->xml_escape($value);
    150                         }
    151                         $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix)));
    152                         if(strlen($attrs_str) > 0) {
    153                                 $attrs_str = " " . $attrs_str;
    154                         }
    155 
    156                         $xmlns_str = join(' ', array_map($this->map_xmlns_func, array_keys($this->ns_contexts[0]), array_values($this->ns_contexts[0])));
    157                         if(strlen($xmlns_str) > 0) {
    158                                 $xmlns_str = " " . $xmlns_str;
    159                         }
    160 
    161                         // handle self-closing tags (case: a new child found right-away, no text node)
    162                         if(count($this->in_content) == 2) {
    163                                 array_push($this->in_content, ">");
    164                         }
    165 
    166                         array_push($this->in_content, "<". $this->ns_to_prefix($name) ."{$xmlns_str}{$attrs_str}");
    167                 } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) {
    168                         $this->in_content = array();
    169                         $this->is_xhtml = $attrs['type'] == 'xhtml';
    170                         array_push($this->in_content, array($tag,$this->depth));
    171                 } else if($tag == 'link') {
    172                         array_push($this->entry->links, $attrs);
    173                 } else if($tag == 'category') {
    174                         array_push($this->entry->categories, $attrs);
    175                 }
    176 
    177                 $this->ns_decls = array();
    178         }
    179 
    180         function end_element($parser, $name) {
    181 
    182                 $tag = array_pop(split(":", $name));
    183 
    184                 if(!empty($this->in_content)) {
    185                         if($this->in_content[0][0] == $tag &&
    186                         $this->in_content[0][1] == $this->depth) {
    187                                 array_shift($this->in_content);
    188                                 if($this->is_xhtml) {
    189                                         $this->in_content = array_slice($this->in_content, 2, count($this->in_content)-3);
    190                                 }
    191                                 $this->entry->$tag = join('',$this->in_content);
    192                                 $this->in_content = array();
    193                         } else {
    194                                 $endtag = $this->ns_to_prefix($name);
    195                                 if (strpos($this->in_content[count($this->in_content)-1], '<' . $endtag) !== false) {
    196                                         array_push($this->in_content, "/>");
    197                                 } else {
    198                                         array_push($this->in_content, "</$endtag>");
    199                                 }
    200                         }
    201                 }
    202 
    203                 array_shift($this->ns_contexts);
    204 
    205                 #print str_repeat(" ", $this->depth * $this->indent) . "end_element('$name')" ."\n";
    206 
    207                 $this->depth--;
    208         }
    209 
    210         function start_ns($parser, $prefix, $uri) {
    211                 #print str_repeat(" ", $this->depth * $this->indent) . "starting: " . $prefix . ":" . $uri . "\n";
    212                 array_push($this->ns_decls, array($prefix,$uri));
    213         }
    214 
    215         function end_ns($parser, $prefix) {
    216                 #print str_repeat(" ", $this->depth * $this->indent) . "ending: #" . $prefix . "#\n";
    217         }
    218 
    219         function cdata($parser, $data) {
    220                 #print str_repeat(" ", $this->depth * $this->indent) . "data: #" . $data . "#\n";
    221                 if(!empty($this->in_content)) {
    222                         // handle self-closing tags (case: text node found, need to close element started)
    223                         if (strpos($this->in_content[count($this->in_content)-1], '<') !== false) {
    224                                 array_push($this->in_content, ">");
    225                         }
    226                         array_push($this->in_content, $this->xml_escape($data));
    227                 }
    228         }
    229 
    230         function _default($parser, $data) {
    231                 # when does this gets called?
    232         }
    233 
    234 
    235         function ns_to_prefix($qname) {
    236                 $components = split(":", $qname);
    237                 $name = array_pop($components);
    238 
    239                 if(!empty($components)) {
    240                         $ns = join(":",$components);
    241                         foreach($this->ns_contexts as $context) {
    242                                 foreach($context as $mapping) {
    243                                         if($mapping[1] == $ns && strlen($mapping[0]) > 0) {
    244                                                 return "$mapping[0]:$name";
    245                                         }
    246                                 }
    247                         }
    248                 }
    249                 return $name;
    250         }
    251 
    252         function xml_escape($string)
    253         {
    254                          return str_replace(array('&','"',"'",'<','>'),
    255                                 array('&amp;','&quot;','&apos;','&lt;','&gt;'),
    256                                 $string );
    257         }
    258 }
    259 
    26070class AtomServer {
    26171
    26272        var $ATOM_CONTENT_TYPE = 'application/atom+xml';
    26373        var $CATEGORIES_CONTENT_TYPE = 'application/atomcat+xml';
    264         var $INTROSPECTION_CONTENT_TYPE = 'application/atomserv+xml';
     74        var $SERVICE_CONTENT_TYPE = 'application/atomsvc+xml';
    26575
     76        var $ATOM_NS = 'http://www.w3.org/2005/Atom';
     77        var $ATOMPUB_NS = 'http://www.w3.org/2007/app';
     78
    26679        var $ENTRIES_PATH = "posts";
    26780        var $CATEGORIES_PATH = "categories";
    26881        var $MEDIA_PATH = "attachments";
    26982        var $ENTRY_PATH = "post";
     83        var $SERVICE_PATH = "service";
    27084        var $MEDIA_SINGLE_PATH = "attachment";
    27185
    27286        var $params = array();
     
    28498                $this->script_name = array_pop(explode('/',$_SERVER['SCRIPT_NAME']));
    28599
    286100                $this->selectors = array(
    287                         '@/service@' =>
     101                        '@/service$@' =>
    288102                                array('GET' => 'get_service'),
    289                         '@/categories@' =>
     103                        '@/categories$@' =>
    290104                                array('GET' => 'get_categories_xml'),
    291                         '@/post/(\d+)@' =>
     105                        '@/post/(\d+)$@' =>
    292106                                array('GET' => 'get_post',
    293107                                                'PUT' => 'put_post',
    294108                                                'DELETE' => 'delete_post'),
    295                         '@/posts/?([^/]+)?@' =>
     109                        '@/posts/?(\d+)?$@' =>
    296110                                array('GET' => 'get_posts',
    297111                                                'POST' => 'create_post'),
    298                         '@/attachments/?(\d+)?@' =>
     112                        '@/attachments/?(\d+)?$@' =>
    299113                                array('GET' => 'get_attachment',
    300114                                                'POST' => 'create_attachment'),
    301                         '@/attachment/file/(\d+)@' =>
     115                        '@/attachment/file/(\d+)$@' =>
    302116                                array('GET' => 'get_file',
    303117                                                'PUT' => 'put_file',
    304118                                                'DELETE' => 'delete_file'),
    305                         '@/attachment/(\d+)@' =>
     119                        '@/attachment/(\d+)$@' =>
    306120                                array('GET' => 'get_attachment',
    307121                                                'PUT' => 'put_attachment',
    308122                                                'DELETE' => 'delete_attachment'),
     
    310124        }
    311125
    312126        function handle_request() {
     127                global $always_authenticate;
    313128
    314129                $path = $_SERVER['PATH_INFO'];
    315130                $method = $_SERVER['REQUEST_METHOD'];
    316131
    317132                log_app('REQUEST',"$method $path\n================");
    318133
     134                $this->process_conditionals();
    319135                //$this->process_conditionals();
    320136
    321137                // exception case for HEAD (treat exactly as GET, but don't output)
     
    324140                        $method = 'GET';
    325141                }
    326142
    327                 // lame.
     143                // redirect to /service in case no path is found.
    328144                if(strlen($path) == 0 || $path == '/') {
    329                         $path = '/service';
     145                                $this->redirect($this->get_service_url());
    330146                }
    331 
    332                 // authenticate regardless of the operation and set the current
    333                 // user. each handler will decide if auth is required or not.
    334                 $this->authenticate();
    335 
     147       
    336148                // dispatch
    337149                foreach($this->selectors as $regex => $funcs) {
    338150                        if(preg_match($regex, $path, $matches)) {
    339                                 if(isset($funcs[$method])) {
    340                                         array_shift($matches);
    341                                         call_user_func_array(array(&$this,$funcs[$method]), $matches);
    342                                         exit();
    343                                 } else {
    344                                         // only allow what we have handlers for...
    345                                         $this->not_allowed(array_keys($funcs));
    346                                 }
     151                        if(isset($funcs[$method])) {
     152
     153                                                // authenticate regardless of the operation and set the current
     154                                                // user. each handler will decide if auth is required or not.
     155                                                $this->authenticate();               
     156                                                $u = wp_get_current_user();
     157                                                if(!isset($u) || $u->ID == 0) {
     158                                                        if ($always_authenticate) {
     159                                                                $this->auth_required('Credentials required.');
     160                                                        }
     161                                                }
     162
     163                                                array_shift($matches);
     164                                                call_user_func_array(array(&$this,$funcs[$method]), $matches);
     165                                                exit();
     166                        } else {
     167                                                // only allow what we have handlers for...
     168                                                $this->not_allowed(array_keys($funcs));
    347169                        }
     170                        }
    348171                }
    349172
    350173                // oops, nothing found
     
    353176
    354177        function get_service() {
    355178                log_app('function','get_service()');
    356                 $entries_url = $this->get_entries_url();
    357                 $categories_url = $this->get_categories_url();
    358                 $media_url = $this->get_attachments_url();
    359                 $accepted_content_types = join(',',$this->media_content_types);
    360                 $introspection = <<<EOD
    361 <service xmlns="http://purl.org/atom/app#" xmlns:atom="http://www.w3.org/2005/Atom">
    362         <workspace title="WordPress Workspace">
    363             <collection href="$entries_url" title="Posts">
    364                 <atom:title>WordPress Posts</atom:title>
    365                 <accept>entry</accept>
    366                 <categories href="$categories_url" />
    367             </collection>
    368             <collection href="$media_url" title="Media">
    369                 <atom:title>WordPress Media</atom:title>
    370                 <accept>$accepted_content_types</accept>
    371             </collection>
    372         </workspace>
     179                $entries_url = attribute_escape($this->get_entries_url());
     180                $categories_url = attribute_escape($this->get_categories_url());
     181                $media_url = attribute_escape($this->get_attachments_url());
     182                foreach ($this->media_content_types as $med) {
     183                  $accepted_media_types = $accepted_media_types . "<accept>" . $med . "</accept>";
     184                }
     185                $atom_prefix="atom";
     186                $service_doc = <<<EOD
     187<service xmlns="$this->ATOMPUB_NS" xmlns:$atom_prefix="$this->ATOM_NS">
     188  <workspace>
     189    <$atom_prefix:title>WordPress Workspace</$atom_prefix:title>
     190    <collection href="$entries_url">
     191      <$atom_prefix:title>WordPress Posts</$atom_prefix:title>
     192      <accept>$this->ATOM_CONTENT_TYPE;type=entry</accept>
     193      <categories href="$categories_url" />
     194    </collection>
     195    <collection href="$media_url">
     196      <$atom_prefix:title>WordPress Media</$atom_prefix:title>
     197      $accepted_media_types
     198    </collection>
     199  </workspace>
    373200</service>
    374201
    375202EOD;
    376203
    377                 $this->output($introspection, $this->INTROSPECTION_CONTENT_TYPE);
     204                $this->output($service_doc, $this->SERVICE_CONTENT_TYPE);
    378205        }
    379206
    380 function get_categories_xml() {
    381         log_app('function','get_categories_xml()');
    382         $home = get_bloginfo_rss('home');
     207        function get_categories_xml() {
    383208
    384         $categories = "";
    385         $cats = get_categories("hierarchical=0&hide_empty=0");
    386         foreach ((array) $cats as $cat) {
    387                 $categories .= "    <category term=\"" . attribute_escape($cat->name) .  "\" />\n";
    388         }
    389         $output = <<<EOD
    390 <app:categories xmlns:app="http://purl.org/atom/app#"
    391         xmlns="http://www.w3.org/2005/Atom"
     209                log_app('function','get_categories_xml()');
     210                $home = attribute_escape(get_bloginfo_rss('home'));
     211
     212                $categories = "";
     213                $cats = get_categories("hierarchical=0&hide_empty=0");
     214                foreach ((array) $cats as $cat) {
     215                        $categories .= "    <category term=\"" . attribute_escape($cat->cat_name) .  "\" />\n";
     216}
     217                $output = <<<EOD
     218<app:categories xmlns:app="$this->ATOMPUB_NS"
     219        xmlns="$this->ATOM_NS"
    392220        fixed="yes" scheme="$home">
    393221        $categories
    394222</app:categories>
     
    400228         * Create Post (No arguments)
    401229         */
    402230        function create_post() {
    403                 global $blog_id;
     231                global $blog_id, $wpdb;
    404232                $this->get_accepted_content_type($this->atom_content_types);
    405233
    406234                $parser = new AtomParser();
     
    408236                        $this->client_error();
    409237                }
    410238
    411                 $entry = $parser->entry;
     239                $entry = array_pop($parser->feed->entries);
     240               
     241                log_app('Received entry:', print_r($entry,true));
     242               
     243                $catnames = array();
     244                foreach($entry->categories as $cat)
     245                        array_push($catnames, $cat["term"]);
     246               
     247                $wp_cats = get_categories(array('hide_empty' => false));
     248                log_app('CATEGORIES :', print_r($wp_cats,true));
     249               
     250                $post_category = array();
     251               
     252                foreach($wp_cats as $cat) {
     253                        if(in_array($cat->cat_name, $catnames))
     254                                array_push($post_category, $cat->cat_ID);
     255                }
    412256
    413257                $publish = (isset($entry->draft) && trim($entry->draft) == 'yes') ? false : true;
    414258
     
    420264                $blog_ID = (int ) $blog_id;
    421265                $post_status = ($publish) ? 'publish' : 'draft';
    422266                $post_author = (int) $user->ID;
    423                 $post_title = $this->escape($entry->title);
    424                 $post_content = $this->escape($entry->content);
    425                 $post_excerpt = $this->escape($entry->summary);
    426                 $post_date = current_time('mysql');
    427                 $post_date_gmt = current_time('mysql', 1);
     267                $post_title = $entry->title[1];
     268                $post_content = $entry->content[1];
     269                $post_excerpt = $entry->summary[1];
     270                $pubtimes = $this->get_publish_time($entry);
     271                $post_date = $pubtimes[0];
     272                $post_date_gmt = $pubtimes[1];
     273               
     274                if ( isset( $_SERVER['HTTP_SLUG'] ) )
     275                        $post_name = $_SERVER['HTTP_SLUG'];
    428276
    429                 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
     277                $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_name');
    430278
     279                $this->escape($post_data);
    431280                log_app('Inserting Post. Data:', print_r($post_data,true));
    432281
    433282                $postID = wp_insert_post($post_data);
     
    436285                        $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.'));
    437286                }
    438287
     288                // getting warning here about unable to set headers
     289                // because something in the cache is printing to the buffer
     290                // could we clean up wp_set_post_categories or cache to not print
     291                // this could affect our ability to send back the right headers
     292                @wp_set_post_categories($postID, $post_category);
     293
    439294                $output = $this->get_entry($postID);
    440295
    441296                log_app('function',"create_post($postID)");
     
    453308        }
    454309
    455310        function put_post($postID) {
     311                global $wpdb;
    456312
    457313                // checked for valid content-types (atom+xml)
    458314                // quick check and exit
     
    463319                        $this->bad_request();
    464320                }
    465321
    466                 $parsed = $parser->entry;
     322                $parsed = array_pop($parser->feed->entries);
    467323
     324                log_app('Received UPDATED entry:', print_r($parsed,true));
     325
    468326                // check for not found
    469327                global $entry;
    470328                $entry = $GLOBALS['entry'];
    471329                $this->set_current_entry($postID);
    472                 $this->escape($GLOBALS['entry']);
    473330
    474331                if(!current_user_can('edit_post', $entry['ID']))
    475332                        $this->auth_required(__('Sorry, you do not have the right to edit this post.'));
     
    478335
    479336                extract($entry);
    480337
    481                 $post_title = $this->escape($parsed->title);
    482                 $post_content = $this->escape($parsed->content);
    483                 $post_excerpt = $this->escape($parsed->summary);
     338                $post_title = $parsed->title[1];
     339                $post_content = $parsed->content[1];
     340                $post_excerpt = $parsed->summary[1];
     341                $pubtimes = $this->get_publish_time($entry);
     342                $post_date = $pubtimes[0];
     343                $post_date_gmt = $pubtimes[1];     
    484344
    485345                // let's not go backwards and make something draft again.
    486346                if(!$publish && $post_status == 'draft') {
    487347                        $post_status = ($publish) ? 'publish' : 'draft';
     348                } elseif($publish) {
     349                        $post_status = 'publish';
    488350                }
    489351
    490                 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
     352                $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_date', 'post_date_gmt');
     353                $this->escape($postdata);
    491354
     355                log_app('UPDATING ENTRY WITH:', print_r($postdata,true));
     356
    492357                $result = wp_update_post($postdata);
    493358
    494359                if (!$result) {
     
    595460        }
    596461
    597462        function put_attachment($postID) {
     463                global $wpdb;
    598464
    599465                // checked for valid content-types (atom+xml)
    600466                // quick check and exit
     
    605471                        $this->bad_request();
    606472                }
    607473
    608                 $parsed = $parser->entry;
     474                $parsed = array_pop($parser->feed->entries);
    609475
    610476                // check for not found
    611477                global $entry;
    612478                $this->set_current_entry($postID);
    613                 $this->escape($entry);
    614479
    615480                if(!current_user_can('edit_post', $entry['ID']))
    616481                        $this->auth_required(__('Sorry, you do not have the right to edit this post.'));
     
    619484
    620485                extract($entry);
    621486
    622                 $post_title = $this->escape($parsed->title);
    623                 $post_content = $this->escape($parsed->content);
     487                $post_title = $parsed->title[1];
     488                $post_content = $parsed->content[1];
    624489
    625490                $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
     491                $this->escape($postdata);
    626492
    627493                $result = wp_update_post($postdata);
    628494
     
    678544                if(!isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext']))
    679545                        $this->internal_error(__('Error ocurred while accessing post metadata for file location.'));
    680546
     547                status_header('200');
    681548                header('Content-Type: ' . $entry['post_mime_type']);
     549                header('Connection: close');
    682550
    683551                $fp = fopen($location, "rb");
    684552                while(!feof($fp)) {
     
    687555                fclose($fp);
    688556
    689557                log_app('function',"get_file($postID)");
    690                 $this->ok();
     558                exit;
    691559        }
    692560
    693561        function put_file($postID) {
     
    721589                fclose($fp);
    722590                fclose($localfp);
    723591
     592                $ID = $entry['ID'];
     593                $pubtimes = $this->get_publish_time($entry);
     594                $post_date = $pubtimes[0];
     595                $post_date_gmt = $pubtimes[1];
     596
     597                $post_data = compact('ID', 'post_date', 'post_date_gmt');
     598                $result = wp_update_post($post_data);
     599
     600                if (!$result) {
     601                        $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.'));
     602                }
     603
    724604                log_app('function',"put_file($postID)");
    725605                $this->ok();
    726606        }
    727607
    728608        function get_entries_url($page = NULL) {
    729609                global $use_querystring;
     610                if($GLOBALS['post_type'] == 'attachment') {
     611                        $path = $this->MEDIA_PATH;
     612                } else {
     613                        $path = $this->ENTRIES_PATH;
     614                }
    730615                $url = get_bloginfo('url') . '/' . $this->script_name;
    731616                if ($use_querystring) {
    732                         $url .= '?action=/' . $this->ENTRIES_PATH;
     617                        $url .= '?action=/' . $path;
    733618                        if(isset($page) && is_int($page)) {
    734619                                $url .= "&amp;eid=$page";
    735620                        }
    736621                } else {
    737                         $url .= '/' . $this->ENTRIES_PATH;
     622                        $url .= '/' . $path;
    738623                        if(isset($page) && is_int($page)) {
    739624                                $url .= "/$page";
    740625                        }
     
    761646        function the_categories_url() {
    762647                $url = $this->get_categories_url();
    763648                echo $url;
    764     }
     649        }
    765650
    766651        function get_attachments_url($page = NULL) {
    767652                global $use_querystring;
     
    785670                echo $url;
    786671        }
    787672
     673        function get_service_url() {
     674                global $use_querystring;
     675                $url = get_bloginfo('url') . '/' . $this->script_name;
     676                if ($use_querystring) {
     677                        $url .= '?action=/' . $this->SERVICE_PATH;
     678                } else {
     679                        $url .= '/' . $this->SERVICE_PATH;
     680                }
     681                return $url;
     682        }
    788683
    789684        function get_entry_url($postID = NULL) {
    790685                global $use_querystring;
     
    816711                }
    817712
    818713                if ($use_querystring) {
    819                         $url = get_bloginfo('url') . '/' . $this->script_name . '?action=/' . $this->MEDIA_SINGLE_PATH ."&amp;eid=$postID";
     714                        $url = get_bloginfo('url') . '/' . $this->script_name . '?action=/' . $this->MEDIA_SINGLE_PATH ."/file&amp;eid=$postID";
    820715                } else {
    821                         $url = get_bloginfo('url') . '/' . $this->script_name . '/' . $this->MEDIA_SINGLE_PATH ."/$postID";
     716                        $url = get_bloginfo('url') . '/' . $this->script_name . '/' . $this->MEDIA_SINGLE_PATH ."/file/$postID";
    822717                }
    823718
    824719                log_app('function',"get_media_url() = $url");
     
    847742                return;
    848743        }
    849744
    850         function get_posts_count() {
    851                 global $wpdb;
    852                 log_app('function',"get_posts_count()");
    853                 return $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE post_date_gmt < '" . gmdate("Y-m-d H:i:s",time()) . "'");
    854         }
    855 
    856 
    857745        function get_posts($page = 1, $post_type = 'post') {
    858746                        log_app('function',"get_posts($page, '$post_type')");
    859747                        $feed = $this->get_feed($page, $post_type);
     
    861749        }
    862750
    863751        function get_attachments($page = 1, $post_type = 'attachment') {
    864                         log_app('function',"get_attachments($page, '$post_type')");
    865                         $feed = $this->get_feed($page, $post_type);
    866                         $this->output($feed);
     752            log_app('function',"get_attachments($page, '$post_type')");
     753            $GLOBALS['post_type'] = $post_type;
     754            $feed = $this->get_feed($page, $post_type);
     755            $this->output($feed);
    867756        }
    868757
    869758        function get_feed($page = 1, $post_type = 'post') {
     
    877766                $page = (int) $page;
    878767
    879768                $count = get_option('posts_per_rss');
    880                 $query = "paged=$page&posts_per_page=$count&order=DESC";
    881                 if($post_type == 'attachment') {
    882                         $query .= "&post_type=$post_type";
    883                 }
    884                 query_posts($query);
     769
     770                wp('what_to_show=posts&posts_per_page=' . $count . '&offset=' . ($page-1));
     771
    885772                $post = $GLOBALS['post'];
    886773                $posts = $GLOBALS['posts'];
    887774                $wp = $GLOBALS['wp'];
     
    889776                $wpdb = $GLOBALS['wpdb'];
    890777                $blog_id = (int) $GLOBALS['blog_id'];
    891778                $post_cache = $GLOBALS['post_cache'];
     779                log_app('function',"query_posts(# " . print_r($wp_query, true) . "#)");
    892780
    893 
    894                 $total_count = $this->get_posts_count();
    895                 $last_page = (int) ceil($total_count / $count);
     781                log_app('function',"total_count(# $wp_query->max_num_pages #)");
     782                $last_page = $wp_query->max_num_pages;
    896783                $next_page = (($page + 1) > $last_page) ? NULL : $page + 1;
    897784                $prev_page = ($page - 1) < 1 ? NULL : $page - 1;
    898785                $last_page = ((int)$last_page == 1 || (int)$last_page == 0) ? NULL : (int) $last_page;
    899 ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://purl.org/atom/app#" xml:lang="<?php echo get_option('rss_language'); ?>">
     786?><feed xmlns="<?php echo $this->ATOM_NS ?>" xmlns:app="<?php echo $this->ATOMPUB_NS ?>" xml:lang="<?php echo get_option('rss_language'); ?>">
    900787<id><?php $this->the_entries_url() ?></id>
    901788<updated><?php echo mysql2date('Y-m-d\TH:i:s\Z', get_lastpostmodified('GMT')); ?></updated>
    902789<title type="text"><?php bloginfo_rss('name') ?></title>
    903790<subtitle type="text"><?php bloginfo_rss("description") ?></subtitle>
    904 <link rel="first" type="application/atom+xml" href="<?php $this->the_entries_url() ?>" />
     791<link rel="first" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url() ?>" />
    905792<?php if(isset($prev_page)): ?>
    906 <link rel="previous" type="application/atom+xml" href="<?php $this->the_entries_url($prev_page) ?>" />
     793<link rel="previous" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($prev_page) ?>" />
    907794<?php endif; ?>
    908795<?php if(isset($next_page)): ?>
    909 <link rel="next" type="application/atom+xml" href="<?php $this->the_entries_url($next_page) ?>" />
     796<link rel="next" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($next_page) ?>" />
    910797<?php endif; ?>
    911 <link rel="last" type="application/atom+xml" href="<?php $this->the_entries_url($last_page) ?>" />
    912 <link rel="self" type="application/atom+xml" href="<?php $this->the_entries_url() ?>" />
     798<link rel="last" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url($last_page) ?>" />
     799<link rel="self" type="<?php echo $this->ATOM_CONTENT_TYPE ?>" href="<?php $this->the_entries_url() ?>" />
    913800<rights type="text">Copyright <?php echo mysql2date('Y', get_lastpostdate('blog')); ?></rights>
    914801<generator uri="http://wordpress.com/" version="1.0.5-dc">WordPress.com Atom API</generator>
    915802<?php if ( have_posts() ) : while ( have_posts() ) : the_post();
     
    917804?>
    918805<entry>
    919806                <id><?php the_guid($post->ID); ?></id>
    920                 <title type="html"><![CDATA[<?php the_title() ?>]]></title>
     807                <title type="text"><![CDATA[<?php the_title_rss() ?>]]></title>
    921808                <updated><?php echo get_post_modified_time('Y-m-d\TH:i:s\Z', true); ?></updated>
    922809                <published><?php echo get_post_time('Y-m-d\TH:i:s\Z', true); ?></published>
     810                <app:edited><?php echo get_post_modified_time('Y-m-d\TH:i:s\Z', true); ?></app:edited>
    923811                <app:control>
    924812                        <app:draft><?php echo ($GLOBALS['post']->post_status == 'draft' ? 'yes' : 'no') ?></app:draft>
    925813                </app:control>
    926814                <author>
    927815                        <name><?php the_author()?></name>
    928816                        <email><?php the_author_email()?></email>
    929         <?php if (get_the_author_url() && get_the_author_url() != 'http://') { ?>
    930                  <uri><?php the_author_url()?></uri>
    931         <?php } ?>
     817                <?php if (get_the_author_url() && get_the_author_url() != 'http://') { ?>
     818                        <uri><?php the_author_url()?></uri>
     819                <?php } ?>
    932820                </author>
    933         <?php if($GLOBALS['post']->post_status == 'attachment') { ?>
    934                 <link rel="edit" href="<?php $this->the_entry_url() ?>" />
     821        <?php if($GLOBALS['post']->post_type == 'attachment') { ?>
    935822                <link rel="edit-media" href="<?php $this->the_media_url() ?>" />
     823                <content type="<?php echo $GLOBALS['post']->post_mime_type ?>" src="<?php the_guid(); ?>"/>
    936824        <?php } else { ?>
    937825                <link href="<?php the_permalink_rss() ?>" />
    938                 <link rel="edit" href="<?php $this->the_entry_url() ?>" />
     826                <?php if ( strlen( $GLOBALS['post']->post_content ) ) : ?>
     827                        <content type="html"><![CDATA[<?php echo get_the_content('', 0, '') ?>]]></content>
     828                <?php endif; ?>
    939829        <?php } ?>
     830                <link rel="edit" href="<?php $this->the_entry_url() ?>" />
    940831        <?php foreach(get_the_category() as $category) { ?>
    941          <category scheme="<?php bloginfo_rss('home') ?>" term="<?php echo $category->name?>" />
    942         <?php } ?>   <summary type="html"><![CDATA[<?php the_excerpt_rss(); ?>]]></summary>
    943         <?php if ( strlen( $GLOBALS['post']->post_content ) ) : ?>
    944         <content type="html"><![CDATA[<?php echo get_the_content('', 0, '') ?>]]></content>
    945 <?php endif; ?>
     832                <category scheme="<?php bloginfo_rss('home') ?>" term="<?php echo $category->cat_name?>" />
     833        <?php } ?>
     834                <summary type="html"><![CDATA[<?php the_excerpt_rss(); ?>]]></summary>
    946835        </entry>
    947836<?php
    948837        endwhile;
     
    971860                $post = $GLOBALS['post'];
    972861                ?>
    973862                <?php log_app('$post',print_r($GLOBALS['post'],true)); ?>
    974 <entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://purl.org/atom/app#" xml:lang="<?php echo get_option('rss_language'); ?>">
     863<entry xmlns="<?php echo $this->ATOM_NS ?>"
     864       xmlns:app="<?php echo $this->ATOMPUB_NS ?>" xml:lang="<?php echo get_option('rss_language'); ?>">
    975865        <id><?php the_guid($post->ID); ?></id>
    976         <title type="html"><![CDATA[<?php the_title_rss() ?>]]></title>
     866        <title type="text"><![CDATA[<?php the_title_rss() ?>]]></title>
    977867
    978868        <updated><?php echo get_post_modified_time('Y-m-d\TH:i:s\Z', true); ?></updated>
    979869        <published><?php echo get_post_time('Y-m-d\TH:i:s\Z', true); ?></published>
     870        <app:edited><?php echo get_post_modified_time('Y-m-d\TH:i:s\Z', true); ?></app:edited>
    980871        <app:control>
    981872                <app:draft><?php echo ($GLOBALS['post']->post_status == 'draft' ? 'yes' : 'no') ?></app:draft>
    982873        </app:control>
    983874        <author>
    984875                <name><?php the_author()?></name>
    985876                <email><?php the_author_email()?></email>
     877        <?php if (get_the_author_url() && get_the_author_url() != 'http://') { ?>
    986878                <uri><?php the_author_url()?></uri>
     879        <?php } ?>
    987880        </author>
    988881<?php if($GLOBALS['post']->post_type == 'attachment') { ?>
    989         <link rel="edit" href="<?php $this->the_entry_url() ?>" />
    990882        <link rel="edit-media" href="<?php $this->the_media_url() ?>" />
    991883        <content type="<?php echo $GLOBALS['post']->post_mime_type ?>" src="<?php the_guid(); ?>"/>
    992884<?php } else { ?>
    993885        <link href="<?php the_permalink_rss() ?>" />
    994         <link rel="edit" href="<?php $this->the_entry_url() ?>" />
     886        <?php if ( strlen( $GLOBALS['post']->post_content ) ) : ?>
     887                <content type="<?php echo $GLOBALS['post']->post_mime_tpye ?>"><![CDATA[<?php echo get_the_content('', 0, '') ?>]]></content>
     888        <?php endif; ?>
    995889<?php } ?>
     890        <link rel="edit" href="<?php $this->the_entry_url() ?>" />
    996891<?php foreach(get_the_category() as $category) { ?>
    997         <category scheme="<?php bloginfo_rss('home') ?>" term="<?php echo $category->name?>" />
     892        <category scheme="<?php bloginfo_rss('home') ?>" term="<?php echo $category->cat_name?>" />
     893<?php } ?>
    998894        <summary type="html"><![CDATA[<?php the_excerpt_rss(); ?>]]></summary>
    999 <?php }
    1000         if ( strlen( $GLOBALS['post']->post_content ) ) : ?>
    1001         <content type="html"><![CDATA[<?php echo get_the_content('', 0, '') ?>]]></content>
    1002 <?php endif; ?>
    1003 </entry>
     895        </entry>
    1004896<?php
    1005897                $entry = ob_get_contents();
    1006898                break;
    1007899                endwhile;
    1008                 else:
    1009                         $this->auth_required(__("Access Denied."));
    1010900                endif;
    1011901                ob_end_clean();
    1012902
     
    1073963                exit;
    1074964        }
    1075965
     966        function redirect($url) {
     967
     968                log_app('Status','302: Redirect');
     969                $escaped_url = attribute_escape($url);
     970                $content = <<<EOD
     971<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
     972<html>
     973  <head>
     974    <title>302 Found</title>
     975  </head>
     976<body>
     977  <h1>Found</h1>
     978  <p>The document has moved <a href="$escaped_url">here</a>.</p>
     979  </body>
     980</html>
     981
     982EOD;
     983                header('HTTP/1.1 302 Moved');
     984                header('Content-Type: text/html');
     985                header('Location: ' . $url);
     986                echo $content;
     987                exit;
     988
     989        }
     990
     991
    1076992        function client_error($msg = 'Client Error') {
    1077                 log_app('Status','400: Client Errir');
     993                log_app('Status','400: Client Error');
    1078994                header('Content-Type: text/plain');
    1079995                status_header('400');
    1080996                exit;
     
    10961012                                }
    10971013                                break;
    10981014                }
    1099                 header('Content-Type: application/atom+xml');
     1015                header("Content-Type: $this->ATOM_CONTENT_TYPE");
    11001016                if(isset($ctloc))
    11011017                        header('Content-Location: ' . $ctloc);
    11021018                header('Location: ' . $edit);
     
    11091025                log_app('Status','401: Auth Required');
    11101026                nocache_headers();
    11111027                header('WWW-Authenticate: Basic realm="WordPress Atom Protocol"');
    1112                 header('WWW-Authenticate: Form action="' . get_option('siteurl') . '/wp-login.php"', false);
    11131028                header("HTTP/1.1 401 $msg");
    11141029                header('Status: ' . $msg);
    1115                 header('Content-Type: plain/text');
    1116                 echo $msg;
     1030                header('Content-Type: text/html');
     1031                $content = <<<EOD
     1032<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
     1033<html>
     1034  <head>
     1035    <title>401 Unauthorized</title>
     1036  </head>
     1037<body>
     1038    <h1>401 Unauthorized</h1>
     1039    <p>$msg</p>
     1040  </body>
     1041</html>
     1042
     1043EOD;
     1044                echo $content;
    11171045                exit;
    11181046        }
    11191047
    1120         function output($xml, $ctype = "application/atom+xml") {
     1048        function output($xml, $ctype = 'application/atom+xml') {
    11211049                        status_header('200');
    11221050                        $xml = '<?xml version="1.0" encoding="' . strtolower(get_option('blog_charset')) . '"?>'."\n".$xml;
    11231051                        header('Connection: close');
     
    11451073                }
    11461074        }
    11471075
    1148 
    1149 
    11501076        /*
    11511077         * Access credential through various methods and perform login
    11521078         */
     
    11661092                // If Basic Auth is working...
    11671093                if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
    11681094                        $login_data = array('login' => $_SERVER['PHP_AUTH_USER'],       'password' => $_SERVER['PHP_AUTH_PW']);
     1095                        log_app("Basic Auth",$login_data['login']);
    11691096                } else {
    11701097                        // else, do cookie-based authentication
    11711098                        if (function_exists('wp_get_cookie_login')) {
     
    12011128                        list($acceptedType,$acceptedSubtype) = explode('/',$t);
    12021129                        if($acceptedType == '*' || $acceptedType == $type) {
    12031130                                if($acceptedSubtype == '*' || $acceptedSubtype == $subtype)
    1204                                         return $type;
     1131                                        return $type . "/" . $subtype;
    12051132                        }
    12061133                }
    12071134
    12081135                $this->invalid_media();
    12091136        }
    12101137
    1211 
    1212 
    12131138        function process_conditionals() {
    12141139
    12151140                if(empty($this->params)) return;
     
    12531178                }
    12541179        }
    12551180
     1181        function rfc3339_str2time($str) {
     1182           
     1183            $match = false;
     1184            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))
     1185                        return false;
     1186       
     1187            if($match[3] == 'Z')
     1188                        $match[3] == '+0000';
     1189       
     1190            return strtotime($match[1] . " " . $match[2] . " " . $match[3]);
     1191        }       
    12561192
     1193        function get_publish_time($entry) {
     1194
     1195            $pubtime = $this->rfc3339_str2time($entry->published);
     1196           
     1197            if(!$pubtime) {
     1198                        return array(current_time('mysql'),current_time('mysql',1));
     1199            } else {
     1200                        return array(date("Y-m-d H:i:s", $pubtime), gmdate("Y-m-d H:i:s", $pubtime));
     1201            }
     1202        }
     1203
    12571204}
    12581205
    12591206$server = new AtomServer();