PageRenderTime 121ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-app.php

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