Changeset 5951

Show
Ignore:
Timestamp:
08/27/07 19:52:58 (10 months ago)
Author:
ryan
Message:

Bring in atomlib 0.4 and update wp-app to use it. Props eliast and placey. fixes #4191

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/wp-app.php

    r5946 r5951  
    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 
     
    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; 
     36 
     37// TODO: Should be an option somewhere 
     38$always_authenticate = 1; 
    3539 
    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"); 
     
    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'; 
     75 
     76    var $ATOM_NS = 'http://www.w3.org/2005/Atom'; 
     77    var $ATOMPUB_NS = 'http://www.w3.org/2007/app'; 
    26578 
    26679    var $ENTRIES_PATH = "posts"; 
     
    26881    var $MEDIA_PATH = "attachments"; 
    26982    var $ENTRY_PATH = "post"; 
     83    var $SERVICE_PATH = "service"; 
    27084    var $MEDIA_SINGLE_PATH = "attachment"; 
    27185 
     
    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', 
     
    311125 
    312126    function handle_request() { 
     127        global $always_authenticate; 
    313128 
    314129        $path = $_SERVER['PATH_INFO']; 
     
    317132        log_app('REQUEST',"$method $path\n================"); 
    318133 
     134        $this->process_conditionals(); 
    319135        //$this->process_conditionals(); 
    320136 
     
    325141        } 
    326142 
    327         // lame
     143        // redirect to /service in case no path is found
    328144        if(strlen($path) == 0 || $path == '/') { 
    329             $path = '/service'; 
    330         } 
    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  
     145                $this->redirect($this->get_service_url()); 
     146        } 
     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)); 
     169            } 
    347170            } 
    348171        } 
     
    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); 
    378     } 
    379  
    380 function get_categories_xml() { 
    381     log_app('function','get_categories_xml()'); 
    382     $home = get_bloginfo_rss('home'); 
    383  
    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" 
     204        $this->output($service_doc, $this->SERVICE_CONTENT_TYPE); 
     205    } 
     206 
     207    function get_categories_xml() { 
     208 
     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 
     
    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 
     
    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; 
     
    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); 
    428  
    429         $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt'); 
    430  
     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']; 
     276 
     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'); 
     278 
     279        $this->escape($post_data); 
    431280        log_app('Inserting Post. Data:', print_r($post_data,true)); 
    432281 
     
    436285            $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.')); 
    437286        } 
     287 
     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); 
    438293 
    439294        $output = $this->get_entry($postID); 
     
    454309 
    455310    function put_post($postID) { 
     311        global $wpdb; 
    456312 
    457313        // checked for valid content-types (atom+xml) 
     
    464320        } 
    465321 
    466         $parsed = $parser->entry; 
     322        $parsed = array_pop($parser->feed->entries); 
     323 
     324        log_app('Received UPDATED entry:', print_r($parsed,true)); 
    467325 
    468326        // check for not found 
     
    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'])) 
     
    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'; 
    488         } 
    489  
    490         $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt'); 
     348        } elseif($publish) { 
     349            $post_status = 'publish'; 
     350        } 
     351 
     352        $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_date', 'post_date_gmt'); 
     353        $this->escape($postdata); 
     354 
     355        log_app('UPDATING ENTRY WITH:', print_r($postdata,true)); 
    491356 
    492357        $result = wp_update_post($postdata); 
     
    596461 
    597462    function put_attachment($postID) { 
     463        global $wpdb; 
    598464 
    599465        // checked for valid content-types (atom+xml) 
     
    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'])) 
     
    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); 
     
    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"); 
     
    688556 
    689557        log_app('function',"get_file($postID)"); 
    690         $this->ok()
     558        exit
    691559    } 
    692560 
     
    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(); 
     
    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"; 
     
    762647        $url = $this->get_categories_url(); 
    763648        echo $url; 
    764    
     649   
    765650 
    766651    function get_attachments_url($page = NULL) { 
     
    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) { 
     
    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 
     
    847742        return; 
    848743    } 
    849  
    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  
    856744 
    857745    function get_posts($page = 1, $post_type = 'post') { 
     
    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 
     
    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']; 
     
    890777        $blog_id = (int) $GLOBALS['blog_id']; 
    891778        $post_cache = $GLOBALS['post_cache']; 
    892  
    893  
    894         $total_count = $this->get_posts_count(); 
    895         $last_page = (int) ceil($total_count / $count)
     779        log_app('function',"query_posts(# " . print_r($wp_query, true) . "#)"); 
     780 
     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> 
     
    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> 
     
    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() ?>" /> 
     826        <?php if ( strlen( $GLOBALS['post']->post_content ) ) : ?> 
     827            <content type="html"><![CDATA[<?php echo get_the_content('', 0, '') ?>]]></content> 
     828        <?php endif; ?> 
     829    <?php } ?> 
    938830        <link rel="edit" href="<?php $this->the_entry_url() ?>" /> 
     831    <?php foreach(get_the_category() as $category) { ?> 
     832        <category scheme="<?php bloginfo_rss('home') ?>" term="<?php echo $category->cat_name?>" /> 
    939833    <?php } ?> 
    940     <?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; ?> 
     834        <summary type="html"><![CDATA[<?php the_excerpt_rss(); ?>]]></summary> 
    946835    </entry> 
    947836<?php 
     
    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> 
     
    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() ?>" /> 
     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; ?> 
     889<?php } ?> 
    994890    <link rel="edit" href="<?php $this->the_entry_url() ?>" /> 
     891<?php foreach(get_the_category() as $category) { ?> 
     892    <category scheme="<?php bloginfo_rss('home') ?>" term="<?php echo $category->cat_name?>" /> 
    995893<?php } ?> 
    996 <?php foreach(get_the_category() as $category) { ?> 
    997     <category scheme="<?php bloginfo_rss('home') ?>" term="<?php echo $category->name?>" /> 
    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(); 
     
    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'); 
     
    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); 
     
    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; 
     
    11461074    } 
    11471075 
    1148  
    1149  
    11501076    /* 
    11511077     * Access credential through various methods and perform login 
     
    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 
     
    12021129            if($acceptedType == '*' || $acceptedType == $type) { 
    12031130                if($acceptedSubtype == '*' || $acceptedSubtype == $subtype) 
    1204                     return $type
     1131                    return $type . "/" . $subtype
    12051132            } 
    12061133        } 
     
    12081135        $this->invalid_media(); 
    12091136    } 
    1210  
    1211  
    12121137 
    12131138    function process_conditionals() { 
     
    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    }        
     1192 
     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