PageRenderTime 69ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-app.php

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