PageRenderTime 87ms CodeModel.GetById 45ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-app.php

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