PageRenderTime 115ms CodeModel.GetById 50ms RepoModel.GetById 14ms app.codeStats 0ms

/wp-app.php

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