PageRenderTime 64ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-app.php

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