# | Code | Descriptions |
1 | <?php | |
2 | /** | |
3 | * Atom Publishing Protocol support for WordPress | |
4 | * | |
5 | * @version 1.0.5-dc | |
6 | */ | |
7 | ||
8 | /** | |
9 | * WordPress is handling an Atom Publishing Protocol request. | |
10 | * | |
11 | * @var bool | |
12 | */ | |
13 | define('APP_REQUEST', true); | |
14 | ||
15 | /** Set up WordPress environment */ | |
16 | require_once('./wp-load.php'); | |
17 | ||
18 | /** Atom Publishing Protocol Class */ | |
19 | require_once(ABSPATH . WPINC . '/atomlib.php'); | |
20 | ||
21 | /** Admin Image API for metadata updating */ | |
22 | require_once(ABSPATH . '/wp-admin/includes/image.php'); | |
23 | ||
24 | $_SERVER['PATH_INFO'] = preg_replace( '/.*\/wp-app\.php/', '', $_SERVER['REQUEST_URI'] ); | |
25 | ||
26 | /** | |
27 | * Whether to enable Atom Publishing Protocol Logging. | |
28 | * | |
29 | * @name app_logging | |
30 | * @var int|bool | |
31 | */ | |
32 | $app_logging = 0; | |
33 | ||
34 | /** | |
35 | * Whether to always authenticate user. Permanently set to true. | |
36 | * | |
37 | * @name always_authenticate | |
38 | * @var int|bool | |
39 | * @todo Should be an option somewhere | |
40 | */ | |
41 | $always_authenticate = 1; | |
42 | ||
43 | /** | |
44 | * Writes logging info to a file. | |
45 | * | |
46 | * @since 2.2.0 | |
47 | * @uses $app_logging | |
48 | * @package WordPress | |
49 | * @subpackage Logging | |
50 | * | |
51 | * @param string $label Type of logging | |
52 | * @param string $msg Information describing logging reason. | |
53 | */ | |
54 | function log_app($label,$msg) { | |
55 | global $app_logging; | |
56 | if ($app_logging) { | |
57 | $fp = fopen( 'wp-app.log', 'a+'); | |
58 | $date = gmdate( 'Y-m-d H:i:s' ); | |
59 | fwrite($fp, "\n\n$date - $label\n$msg\n"); | //Arbitrary file manipulations |
60 | fclose($fp); | |
61 | } | |
62 | } | |
63 | ||
64 | /** | |
65 | * Filter to add more post statuses. | |
66 | * | |
67 | * @since 2.2.0 | |
68 | * | |
69 | * @param string $where SQL statement to filter. | |
70 | * @return string Filtered SQL statement with added post_status for where clause. | |
71 | */ | |
72 | function wa_posts_where_include_drafts_filter($where) { | |
73 | $where = str_replace("post_status = 'publish'","post_status = 'publish' OR post_status = 'future' OR post_status = 'draft' OR post_status = 'inherit'", $where); | |
74 | return $where; | |
75 | ||
76 | } | |
77 | add_filter('posts_where', 'wa_posts_where_include_drafts_filter'); | |
78 | ||
79 | /** | |
80 | * WordPress AtomPub API implementation. | |
81 | * | |
82 | * @package WordPress | |
83 | * @subpackage Publishing | |
84 | * @since 2.2.0 | |
85 | */ | |
86 | class AtomServer { | |
87 | ||
88 | /** | |
89 | * ATOM content type. | |
90 | * | |
91 | * @since 2.2.0 | |
92 | * @var string | |
93 | */ | |
94 | var $ATOM_CONTENT_TYPE = 'application/atom+xml'; | |
95 | ||
96 | /** | |
97 | * Categories ATOM content type. | |
98 | * | |
99 | * @since 2.2.0 | |
100 | * @var string | |
101 | */ | |
102 | var $CATEGORIES_CONTENT_TYPE = 'application/atomcat+xml'; | |
103 | ||
104 | /** | |
105 | * Service ATOM content type. | |
106 | * | |
107 | * @since 2.3.0 | |
108 | * @var string | |
109 | */ | |
110 | var $SERVICE_CONTENT_TYPE = 'application/atomsvc+xml'; | |
111 | ||
112 | /** | |
113 | * ATOM XML namespace. | |
114 | * | |
115 | * @since 2.3.0 | |
116 | * @var string | |
117 | */ | |
118 | var $ATOM_NS = 'http://www.w3.org/2005/Atom'; | |
119 | ||
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 | /** | |
129 | * Entries path. | |
130 | * | |
131 | * @since 2.2.0 | |
132 | * @var string | |
133 | */ | |
134 | var $ENTRIES_PATH = "posts"; | |
135 | ||
136 | /** | |
137 | * Categories path. | |
138 | * | |
139 | * @since 2.2.0 | |
140 | * @var string | |
141 | */ | |
142 | var $CATEGORIES_PATH = "categories"; | |
143 | ||
144 | /** | |
145 | * Media path. | |
146 | * | |
147 | * @since 2.2.0 | |
148 | * @var string | |
149 | */ | |
150 | var $MEDIA_PATH = "attachments"; | |
151 | ||
152 | /** | |
153 | * Entry path. | |
154 | * | |
155 | * @since 2.2.0 | |
156 | * @var string | |
157 | */ | |
158 | var $ENTRY_PATH = "post"; | |
159 | ||
160 | /** | |
161 | * Service path. | |
162 | * | |
163 | * @since 2.2.0 | |
164 | * @var string | |
165 | */ | |
166 | var $SERVICE_PATH = "service"; | |
167 | ||
168 | /** | |
169 | * Media single path. | |
170 | * | |
171 | * @since 2.2.0 | |
172 | * @var string | |
173 | */ | |
174 | var $MEDIA_SINGLE_PATH = "attachment"; | |
175 | ||
176 | /** | |
177 | * ATOMPUB parameters. | |
178 | * | |
179 | * @since 2.2.0 | |
180 | * @var array | |
181 | */ | |
182 | var $params = array(); | |
183 | ||
184 | /** | |
185 | * Supported ATOMPUB media types. | |
186 | * | |
187 | * @since 2.3.0 | |
188 | * @var array | |
189 | */ | |
190 | var $media_content_types = array('image/*','audio/*','video/*'); | |
191 | ||
192 | /** | |
193 | * ATOMPUB content type(s). | |
194 | * | |
195 | * @since 2.2.0 | |
196 | * @var array | |
197 | */ | |
198 | var $atom_content_types = array('application/atom+xml'); | |
199 | ||
200 | /** | |
201 | * ATOMPUB methods. | |
202 | * | |
203 | * @since 2.2.0 | |
204 | * @var unknown_type | |
205 | */ | |
206 | var $selectors = array(); | |
207 | ||
208 | /** | |
209 | * Whether to do output. | |
210 | * | |
211 | * Support for head. | |
212 | * | |
213 | * @since 2.2.0 | |
214 | * @var bool | |
215 | */ | |
216 | var $do_output = true; | |
217 | ||
218 | /** | |
219 | * PHP4 constructor - Sets up object properties. | |
220 | * | |
221 | * @since 2.2.0 | |
222 | * @return AtomServer | |
223 | */ | |
224 | function AtomServer() { | |
225 | ||
226 | $this->script_name = array_pop( $var_by_ref = explode( '/', $_SERVER['SCRIPT_NAME'] ) ); | |
227 | $this->app_base = site_url( $this->script_name . '/' ); | |
228 | ||
229 | $this->selectors = array( | |
230 | '@/service$@' => | |
231 | array('GET' => 'get_service'), | |
232 | '@/categories$@' => | |
233 | array('GET' => 'get_categories_xml'), | |
234 | '@/post/(\d+)$@' => | |
235 | array('GET' => 'get_post', | |
236 | 'PUT' => 'put_post', | |
237 | 'DELETE' => 'delete_post'), | |
238 | '@/posts/?(\d+)?$@' => | |
239 | array('GET' => 'get_posts', | |
240 | 'POST' => 'create_post'), | |
241 | '@/attachments/?(\d+)?$@' => | |
242 | array('GET' => 'get_attachment', | |
243 | 'POST' => 'create_attachment'), | |
244 | '@/attachment/file/(\d+)$@' => | |
245 | array('GET' => 'get_file', | |
246 | 'PUT' => 'put_file', | |
247 | 'DELETE' => 'delete_file'), | |
248 | '@/attachment/(\d+)$@' => | |
249 | array('GET' => 'get_attachment', | |
250 | 'PUT' => 'put_attachment', | |
251 | 'DELETE' => 'delete_attachment'), | |
252 | ); | |
253 | } | |
254 | ||
255 | /** | |
256 | * Handle ATOMPUB request. | |
257 | * | |
258 | * @since 2.2.0 | |
259 | */ | |
260 | function handle_request() { | |
261 | global $always_authenticate; | |
262 | ||
263 | if ( !empty( $_SERVER['ORIG_PATH_INFO'] ) ) | |
264 | $path = $_SERVER['ORIG_PATH_INFO']; | |
265 | else | |
266 | $path = $_SERVER['PATH_INFO']; | |
267 | ||
268 | $method = $_SERVER['REQUEST_METHOD']; | |
269 | ||
270 | log_app('REQUEST',"$method $path\n================"); | |
271 | ||
272 | $this->process_conditionals(); | |
273 | //$this->process_conditionals(); | |
274 | ||
275 | // exception case for HEAD (treat exactly as GET, but don't output) | |
276 | if ($method == 'HEAD') { | |
277 | $this->do_output = false; | |
278 | $method = 'GET'; | |
279 | } | |
280 | ||
281 | // redirect to /service in case no path is found. | |
282 | if (strlen($path) == 0 || $path == '/') | |
283 | $this->redirect($this->get_service_url()); | |
284 | ||
285 | // check to see if AtomPub is enabled | |
286 | if ( !get_option( 'enable_app' ) ) | |
287 | $this->forbidden( sprintf( __( 'AtomPub services are disabled on this site. An admin user can enable them at %s' ), admin_url('options-writing.php') ) ); | |
288 | ||
289 | // dispatch | |
290 | foreach ( $this->selectors as $regex => $funcs ) { | |
291 | if ( preg_match($regex, $path, $matches) ) { | |
292 | if ( isset($funcs[$method]) ) { | |
293 | ||
294 | // authenticate regardless of the operation and set the current | |
295 | // user. each handler will decide if auth is required or not. | |
296 | if ( !$this->authenticate() ) { | |
297 | if ( $always_authenticate ) | |
298 | $this->auth_required('Credentials required.'); | |
299 | } | |
300 | ||
301 | array_shift($matches); | |
302 | call_user_func_array(array(&$this,$funcs[$method]), $matches); | |
303 | exit(); | |
304 | } else { | |
305 | // only allow what we have handlers for... | |
306 | $this->not_allowed(array_keys($funcs)); | |
307 | } | |
308 | } | |
309 | } | |
310 | ||
311 | // oops, nothing found | |
312 | $this->not_found(); | |
313 | } | |
314 | ||
315 | /** | |
316 | * Retrieve XML for ATOMPUB service. | |
317 | * | |
318 | * @since 2.2.0 | |
319 | */ | |
320 | function get_service() { | |
321 | log_app('function','get_service()'); | |
322 | ||
323 | if ( !current_user_can( 'edit_posts' ) ) | |
324 | $this->auth_required( __( 'Sorry, you do not have the right to access this site.' ) ); | |
325 | ||
326 | $entries_url = esc_attr($this->get_entries_url()); | |
327 | $categories_url = esc_attr($this->get_categories_url()); | |
328 | $media_url = esc_attr($this->get_attachments_url()); | |
329 | $accepted_media_types = ''; | |
330 | foreach ($this->media_content_types as $med) { | |
331 | $accepted_media_types = $accepted_media_types . "<accept>" . $med . "</accept>"; | |
332 | } | |
333 | $atom_prefix="atom"; | |
334 | $atom_blogname = get_bloginfo('name'); | |
335 | $service_doc = <<<EOD | |
336 | <service xmlns="$this->ATOMPUB_NS" xmlns:$atom_prefix="$this->ATOM_NS"> | |
337 | <workspace> | |
338 | <$atom_prefix:title>$atom_blogname Workspace</$atom_prefix:title> | |
339 | <collection href="$entries_url"> | |
340 | <$atom_prefix:title>$atom_blogname Posts</$atom_prefix:title> | |
341 | <accept>$this->ATOM_CONTENT_TYPE;type=entry</accept> | |
342 | <categories href="$categories_url" /> | |
343 | </collection> | |
344 | <collection href="$media_url"> | |
345 | <$atom_prefix:title>$atom_blogname Media</$atom_prefix:title> | |
346 | $accepted_media_types | |
347 | </collection> | |
348 | </workspace> | |
349 | </service> | |
350 | ||
351 | EOD; | |
352 | ||
353 | $this->output($service_doc, $this->SERVICE_CONTENT_TYPE); | |
354 | } | |
355 | ||
356 | /** | |
357 | * Retrieve categories list in XML format. | |
358 | * | |
359 | * @since 2.2.0 | |
360 | */ | |
361 | function get_categories_xml() { | |
362 | log_app('function','get_categories_xml()'); | |
363 | ||
364 | if ( !current_user_can( 'edit_posts' ) ) | |
365 | $this->auth_required( __( 'Sorry, you do not have the right to access this site.' ) ); | |
366 | ||
367 | $home = esc_attr(get_bloginfo_rss('url')); | |
368 | ||
369 | $categories = ""; | |
370 | $cats = get_categories(array('hierarchical' => 0, 'hide_empty' => 0)); | |
371 | foreach ( (array) $cats as $cat ) { | |
372 | $categories .= " <category term=\"" . esc_attr($cat->name) . "\" />\n"; | |
373 | } | |
374 | $output = <<<EOD | |
375 | <app:categories xmlns:app="$this->ATOMPUB_NS" | |
376 | xmlns="$this->ATOM_NS" | |
377 | fixed="yes" scheme="$home"> | |
378 | $categories | |
379 | </app:categories> | |
380 | EOD; | |
381 | $this->output($output, $this->CATEGORIES_CONTENT_TYPE); | |
382 | } | |
383 | ||
384 | /** | |
385 | * Create new post. | |
386 | * | |
387 | * @since 2.2.0 | |
388 | */ | |
389 | function create_post() { | |
390 | global $blog_id, $user_ID; | |
391 | $this->get_accepted_content_type($this->atom_content_types); | |
392 | ||
393 | $parser = new AtomParser(); | |
394 | if ( !$parser->parse() ) | |
395 | $this->client_error(); | |
396 | ||
397 | $entry = array_pop($parser->feed->entries); | |
398 | ||
399 | log_app('Received entry:', print_r($entry,true)); | |
400 | ||
401 | $catnames = array(); | |
402 | foreach ( $entry->categories as $cat ) { | |
403 | array_push($catnames, $cat["term"]); | |
404 | } | |
405 | ||
406 | $wp_cats = get_categories(array('hide_empty' => false)); | |
407 | ||
408 | $post_category = array(); | |
409 | ||
410 | foreach ( $wp_cats as $cat ) { | |
411 | if ( in_array($cat->name, $catnames) ) | |
412 | array_push($post_category, $cat->term_id); | |
413 | } | |
414 | ||
415 | $publish = ! ( isset( $entry->draft ) && 'yes' == trim( $entry->draft ) ); | |
416 | ||
417 | $cap = ($publish) ? 'publish_posts' : 'edit_posts'; | |
418 | ||
419 | if ( !current_user_can($cap) ) | |
420 | $this->auth_required(__('Sorry, you do not have the right to edit/publish new posts.')); | |
421 | ||
422 | $blog_ID = (int ) $blog_id; | |
423 | $post_status = ($publish) ? 'publish' : 'draft'; | |
424 | $post_author = (int) $user_ID; | |
425 | $post_title = $entry->title[1]; | |
426 | $post_content = $entry->content[1]; | |
427 | $post_excerpt = $entry->summary[1]; | |
428 | $pubtimes = $this->get_publish_time($entry->published); | |
429 | $post_date = $pubtimes[0]; | |
430 | $post_date_gmt = $pubtimes[1]; | |
431 | ||
432 | if ( isset( $_SERVER['HTTP_SLUG'] ) ) | |
433 | $post_name = $_SERVER['HTTP_SLUG']; | |
434 | ||
435 | $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_name'); | |
436 | ||
437 | $this->escape($post_data); | |
438 | log_app('Inserting Post. Data:', print_r($post_data,true)); | |
439 | ||
440 | $postID = wp_insert_post($post_data); | |
441 | if ( is_wp_error( $postID ) ) | |
442 | $this->internal_error($postID->get_error_message()); | |
443 | ||
444 | if ( !$postID ) | |
445 | $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.')); | |
446 | ||
447 | // getting warning here about unable to set headers | |
448 | // because something in the cache is printing to the buffer | |
449 | // could we clean up wp_set_post_categories or cache to not print | |
450 | // this could affect our ability to send back the right headers | |
451 | @wp_set_post_categories($postID, $post_category); | |
452 | ||
453 | do_action( 'atompub_create_post', $postID, $entry ); | |
454 | ||
455 | $output = $this->get_entry($postID); | |
456 | ||
457 | log_app('function',"create_post($postID)"); | |
458 | $this->created($postID, $output); | |
459 | } | |
460 | ||
461 | /** | |
462 | * Retrieve post. | |
463 | * | |
464 | * @since 2.2.0 | |
465 | * | |
466 | * @param int $postID Post ID. | |
467 | */ | |
468 | function get_post($postID) { | |
469 | global $entry; | |
470 | ||
471 | if ( !current_user_can( 'edit_post', $postID ) ) | |
472 | $this->auth_required( __( 'Sorry, you do not have the right to access this post.' ) ); | |
473 | ||
474 | $this->set_current_entry($postID); | |
475 | $output = $this->get_entry($postID); | |
476 | log_app('function',"get_post($postID)"); | |
477 | $this->output($output); | |
478 | ||
479 | } | |
480 | ||
481 | /** | |
482 | * Update post. | |
483 | * | |
484 | * @since 2.2.0 | |
485 | * | |
486 | * @param int $postID Post ID. | |
487 | */ | |
488 | function put_post($postID) { | |
489 | // checked for valid content-types (atom+xml) | |
490 | // quick check and exit | |
491 | $this->get_accepted_content_type($this->atom_content_types); | |
492 | ||
493 | $parser = new AtomParser(); | |
494 | if ( !$parser->parse() ) | |
495 | $this->bad_request(); | |
496 | ||
497 | $parsed = array_pop($parser->feed->entries); | |
498 | ||
499 | log_app('Received UPDATED entry:', print_r($parsed,true)); | |
500 | ||
501 | // check for not found | |
502 | global $entry; | |
503 | $this->set_current_entry($postID); | |
504 | ||
505 | if ( !current_user_can('edit_post', $entry['ID']) ) | |
506 | $this->auth_required(__('Sorry, you do not have the right to edit this post.')); | |
507 | ||
508 | $publish = ! ( isset($parsed->draft) && 'yes' == trim($parsed->draft) ); | |
509 | $post_status = ($publish) ? 'publish' : 'draft'; | |
510 | ||
511 | extract($entry); | //Possible Control Flow |
512 | ||
513 | $post_title = $parsed->title[1]; | |
514 | $post_content = $parsed->content[1]; | |
515 | $post_excerpt = $parsed->summary[1]; | |
516 | $pubtimes = $this->get_publish_time($entry->published); | |
517 | $post_date = $pubtimes[0]; | |
518 | $post_date_gmt = $pubtimes[1]; | |
519 | $pubtimes = $this->get_publish_time($parsed->updated); | |
520 | $post_modified = $pubtimes[0]; | |
521 | $post_modified_gmt = $pubtimes[1]; | |
522 | ||
523 | $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt'); | |
524 | $this->escape($postdata); | |
525 | ||
526 | $result = wp_update_post($postdata); | |
527 | ||
528 | if ( !$result ) | |
529 | $this->internal_error(__('For some strange yet very annoying reason, this post could not be edited.')); | |
530 | ||
531 | do_action( 'atompub_put_post', $ID, $parsed ); | |
532 | ||
533 | log_app('function',"put_post($postID)"); | |
534 | $this->ok(); | |
535 | } | |
536 | ||
537 | /** | |
538 | * Remove post. | |
539 | * | |
540 | * @since 2.2.0 | |
541 | * | |
542 | * @param int $postID Post ID. | |
543 | */ | |
544 | function delete_post($postID) { | |
545 | ||
546 | // check for not found | |
547 | global $entry; | |
548 | $this->set_current_entry($postID); | |
549 | ||
550 | if ( !current_user_can('edit_post', $postID) ) | |
551 | $this->auth_required(__('Sorry, you do not have the right to delete this post.')); | |
552 | ||
553 | if ( $entry['post_type'] == 'attachment' ) { | |
554 | $this->delete_attachment($postID); | |
555 | } else { | |
556 | $result = wp_delete_post($postID); | |
557 | ||
558 | if ( !$result ) { | |
559 | $this->internal_error(__('For some strange yet very annoying reason, this post could not be deleted.')); | |
560 | } | |
561 | ||
562 | log_app('function',"delete_post($postID)"); | |
563 | $this->ok(); | |
564 | } | |
565 | ||
566 | } | |
567 | ||
568 | /** | |
569 | * Retrieve attachment. | |
570 | * | |
571 | * @since 2.2.0 | |
572 | * | |
573 | * @param int $postID Optional. Post ID. | |
574 | */ | |
575 | function get_attachment($postID = null) { | |
576 | if ( !current_user_can( 'upload_files' ) ) | |
577 | $this->auth_required( __( 'Sorry, you do not have permission to upload files.' ) ); | |
578 | ||
579 | if ( !isset($postID) ) { | |
580 | $this->get_attachments(); | |
581 | } else { | |
582 | $this->set_current_entry($postID); | |
583 | $output = $this->get_entry($postID, 'attachment'); | |
584 | log_app('function',"get_attachment($postID)"); | |
585 | $this->output($output); | |
586 | } | |
587 | } | |
588 | ||
589 | /** | |
590 | * Create new attachment. | |
591 | * | |
592 | * @since 2.2.0 | |
593 | */ | |
594 | function create_attachment() { | |
595 | ||
596 | $type = $this->get_accepted_content_type(); | |
597 | ||
598 | if ( !current_user_can('upload_files') ) | |
599 | $this->auth_required(__('You do not have permission to upload files.')); | |
600 | ||
601 | $fp = fopen("php://input", "rb"); | |
602 | $bits = null; | |
603 | while ( !feof($fp) ) { | |
604 | $bits .= fread($fp, 4096); | //Arbitrary file disclosing |
605 | } | |
606 | fclose($fp); | |
607 | ||
608 | $slug = ''; | |
609 | if ( isset( $_SERVER['HTTP_SLUG'] ) ) | |
610 | $slug = sanitize_file_name( $_SERVER['HTTP_SLUG'] ); | |
611 | elseif ( isset( $_SERVER['HTTP_TITLE'] ) ) | |
612 | $slug = sanitize_file_name( $_SERVER['HTTP_TITLE'] ); | |
613 | elseif ( empty( $slug ) ) // just make a random name | |
614 | $slug = substr( md5( uniqid( microtime() ) ), 0, 7); | |
615 | $ext = preg_replace( '|.*/([a-z0-9]+)|', '$1', $_SERVER['CONTENT_TYPE'] ); | |
616 | $slug = "$slug.$ext"; | |
617 | $file = wp_upload_bits( $slug, NULL, $bits); | |
618 | ||
619 | log_app('wp_upload_bits returns:',print_r($file,true)); | |
620 | ||
621 | $url = $file['url']; | |
622 | $file = $file['file']; | //Arbitrary file disclosing |
623 | ||
624 | do_action('wp_create_file_in_uploads', $file); // replicate | |
625 | ||
626 | // Construct the attachment array | |
627 | $attachment = array( | |
628 | 'post_title' => $slug, | |
629 | 'post_content' => $slug, | |
630 | 'post_status' => 'attachment', | |
631 | 'post_parent' => 0, | |
632 | 'post_mime_type' => $type, | |
633 | 'guid' => $url | |
634 | ); | |
635 | ||
636 | // Save the data | |
637 | $postID = wp_insert_attachment($attachment, $file); | |
638 | ||
639 | if (!$postID) | |
640 | $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.')); | |
641 | ||
642 | $output = $this->get_entry($postID, 'attachment'); | |
643 | ||
644 | $this->created($postID, $output, 'attachment'); | |
645 | log_app('function',"create_attachment($postID)"); | |
646 | } | |
647 | ||
648 | /** | |
649 | * Update attachment. | |
650 | * | |
651 | * @since 2.2.0 | |
652 | * | |
653 | * @param int $postID Post ID. | |
654 | */ | |
655 | function put_attachment($postID) { | |
656 | // checked for valid content-types (atom+xml) | |
657 | // quick check and exit | |
658 | $this->get_accepted_content_type($this->atom_content_types); | |
659 | ||
660 | $parser = new AtomParser(); | |
661 | if (!$parser->parse()) { | |
662 | $this->bad_request(); | |
663 | } | |
664 | ||
665 | $parsed = array_pop($parser->feed->entries); | |
666 | ||
667 | // check for not found | |
668 | global $entry; | |
669 | $this->set_current_entry($postID); | |
670 | ||
671 | if ( !current_user_can('edit_post', $entry['ID']) ) | |
672 | $this->auth_required(__('Sorry, you do not have the right to edit this post.')); | |
673 | ||
674 | extract($entry); | //Possible Control Flow |
675 | ||
676 | $post_title = $parsed->title[1]; | |
677 | $post_content = $parsed->summary[1]; | |
678 | $pubtimes = $this->get_publish_time($parsed->updated); | |
679 | $post_modified = $pubtimes[0]; | |
680 | $post_modified_gmt = $pubtimes[1]; | |
681 | ||
682 | $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'post_modified', 'post_modified_gmt'); | |
683 | $this->escape($postdata); | |
684 | ||
685 | $result = wp_update_post($postdata); | |
686 | ||
687 | if ( !$result ) | |
688 | $this->internal_error(__('For some strange yet very annoying reason, this post could not be edited.')); | |
689 | ||
690 | log_app('function',"put_attachment($postID)"); | |
691 | $this->ok(); | |
692 | } | |
693 | ||
694 | /** | |
695 | * Remove attachment. | |
696 | * | |
697 | * @since 2.2.0 | |
698 | * | |
699 | * @param int $postID Post ID. | |
700 | */ | |
701 | function delete_attachment($postID) { | |
702 | log_app('function',"delete_attachment($postID). File '$location' deleted."); | |
703 | ||
704 | // check for not found | |
705 | global $entry; | |
706 | $this->set_current_entry($postID); | |
707 | ||
708 | if ( !current_user_can('edit_post', $postID) ) | |
709 | $this->auth_required(__('Sorry, you do not have the right to delete this post.')); | |
710 | ||
711 | $location = get_post_meta($entry['ID'], '_wp_attached_file', true); | |
712 | $filetype = wp_check_filetype($location); | |
713 | ||
714 | if ( !isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext']) ) | |
715 | $this->internal_error(__('Error ocurred while accessing post metadata for file location.')); | |
716 | ||
717 | // delete file | |
718 | @unlink($location); | //Arbitrary file manipulations |
719 | ||
720 | // delete attachment | |
721 | $result = wp_delete_post($postID); | |
722 | ||
723 | if ( !$result ) | |
724 | $this->internal_error(__('For some strange yet very annoying reason, this post could not be deleted.')); | |
725 | ||
726 | log_app('function',"delete_attachment($postID). File '$location' deleted."); | |
727 | $this->ok(); | |
728 | } | |
729 | ||
730 | /** | |
731 | * Retrieve attachment from post. | |
732 | * | |
733 | * @since 2.2.0 | |
734 | * | |
735 | * @param int $postID Post ID. | |
736 | */ | |
737 | function get_file($postID) { | |
738 | ||
739 | // check for not found | |
740 | global $entry; | |
741 | $this->set_current_entry($postID); | |
742 | ||
743 | // then whether user can edit the specific post | |
744 | if ( !current_user_can('edit_post', $postID) ) | |
745 | $this->auth_required(__('Sorry, you do not have the right to edit this post.')); | |
746 | ||
747 | $location = get_post_meta($entry['ID'], '_wp_attached_file', true); | |
748 | $location = get_option ('upload_path') . '/' . $location; | |
749 | $filetype = wp_check_filetype($location); | |
750 | ||
751 | if ( !isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext']) ) | |
752 | $this->internal_error(__('Error ocurred while accessing post metadata for file location.')); | |
753 | ||
754 | status_header('200'); | |
755 | header('Content-Type: ' . $entry['post_mime_type']); | |
756 | header('Connection: close'); | |
757 | ||
758 | if ( $fp = fopen($location, "rb") ) { | |
759 | status_header('200'); | |
760 | header('Content-Type: ' . $entry['post_mime_type']); | |
761 | header('Connection: close'); | |
762 | ||
763 | while ( !feof($fp) ) { | |
764 | echo fread($fp, 4096); | //Arbitrary file disclosing |
765 | } | |
766 | ||
767 | fclose($fp); | |
768 | } else { | |
769 | status_header ('404'); | |
770 | } | |
771 | ||
772 | log_app('function',"get_file($postID)"); | //Arbitrary file disclosing |
773 | exit; | |
774 | } | |
775 | ||
776 | /** | |
777 | * Upload file to blog and add attachment to post. | |
778 | * | |
779 | * @since 2.2.0 | |
780 | * | |
781 | * @param int $postID Post ID. | |
782 | */ | |
783 | function put_file($postID) { | |
784 | ||
785 | // first check if user can upload | |
786 | if ( !current_user_can('upload_files') ) | |
787 | $this->auth_required(__('You do not have permission to upload files.')); | |
788 | ||
789 | // check for not found | |
790 | global $entry; | |
791 | $this->set_current_entry($postID); | |
792 | ||
793 | // then whether user can edit the specific post | |
794 | if ( !current_user_can('edit_post', $postID) ) | |
795 | $this->auth_required(__('Sorry, you do not have the right to edit this post.')); | |
796 | ||
797 | $upload_dir = wp_upload_dir( ); | |
798 | $location = get_post_meta($entry['ID'], '_wp_attached_file', true); | |
799 | $filetype = wp_check_filetype($location); | |
800 | ||
801 | $location = "{$upload_dir['basedir']}/{$location}"; | |
802 | ||
803 | if (!isset($location) || 'attachment' != $entry['post_type'] || empty($filetype['ext'])) | |
804 | $this->internal_error(__('Error ocurred while accessing post metadata for file location.')); | |
805 | ||
806 | $fp = fopen("php://input", "rb"); | |
807 | $localfp = fopen($location, "w+"); | |
808 | while ( !feof($fp) ) { | |
809 | fwrite($localfp,fread($fp, 4096)); | //Arbitrary file disclosing //Arbitrary file manipulations |
810 | } | |
811 | fclose($fp); | |
812 | fclose($localfp); | |
813 | ||
814 | $ID = $entry['ID']; | |
815 | $pubtimes = $this->get_publish_time($entry->published); | |
816 | $post_date = $pubtimes[0]; | |
817 | $post_date_gmt = $pubtimes[1]; | |
818 | $pubtimes = $this->get_publish_time($parsed->updated); | |
819 | $post_modified = $pubtimes[0]; | |
820 | $post_modified_gmt = $pubtimes[1]; | |
821 | ||
822 | $post_data = compact('ID', 'post_date', 'post_date_gmt', 'post_modified', 'post_modified_gmt'); | |
823 | $result = wp_update_post($post_data); | |
824 | ||
825 | if ( !$result ) | |
826 | $this->internal_error(__('Sorry, your entry could not be posted. Something wrong happened.')); | |
827 | ||
828 | wp_update_attachment_metadata( $postID, wp_generate_attachment_metadata( $postID, $location ) ); | |
829 | ||
830 | log_app('function',"put_file($postID)"); | //Arbitrary file disclosing |
831 | $this->ok(); | |
832 | } | |
833 | ||
834 | /** | |
835 | * Retrieve entries URL. | |
836 | * | |
837 | * @since 2.2.0 | |
838 | * | |
839 | * @param int $page Page ID. | |
840 | * @return string | |
841 | */ | |
842 | function get_entries_url($page = null) { | |
843 | if ( isset($GLOBALS['post_type']) && ( $GLOBALS['post_type'] == 'attachment' ) ) | |
844 | $path = $this->MEDIA_PATH; | |
845 | else | |
846 | $path = $this->ENTRIES_PATH; | |
847 | $url = $this->app_base . $path; | |
848 | if ( isset($page) && is_int($page) ) | |
849 | $url .= "/$page"; | |
850 | return $url; | |
851 | } | |
852 | ||
853 | /** | |
854 | * Display entries URL. | |
855 | * | |
856 | * @since 2.2.0 | |
857 | * | |
858 | * @param int $page Page ID. | |
859 | */ | |
860 | function the_entries_url($page = null) { | |
861 | echo $this->get_entries_url($page); | //Cross Site Scripting |
862 | } | |
863 | ||
864 | /** | |
865 | * Retrieve categories URL. | |
866 | * | |
867 | * @since 2.2.0 | |
868 | * | |
869 | * @param mixed $deprecated Not used. | |
870 | * @return string | |
871 | */ | |
872 | function get_categories_url($deprecated = '') { | |
873 | if ( !empty( $deprecated ) ) | |
874 | _deprecated_argument( __FUNCTION__, '2.5' ); | |
875 | return $this->app_base . $this->CATEGORIES_PATH; | |
876 | } | |
877 | ||
878 | /** | |
879 | * Display category URL. | |
880 | * | |
881 | * @since 2.2.0 | |
882 | */ | |
883 | function the_categories_url() { | |
884 | echo $this->get_categories_url(); | //Cross Site Scripting |
885 | } | |
886 | ||
887 | /** | |
888 | * Retrieve attachment URL. | |
889 | * | |
890 | * @since 2.2.0 | |
891 | * | |
892 | * @param int $page Page ID. | |
893 | * @return string | |
894 | */ | |
895 | function get_attachments_url($page = null) { | |
896 | $url = $this->app_base . $this->MEDIA_PATH; | |
897 | if (isset($page) && is_int($page)) { | |
898 | $url .= "/$page"; | |
899 | } | |
900 | return $url; | |
901 | } | |
902 | ||
903 | /** | |
904 | * Display attachment URL. | |
905 | * | |
906 | * @since 2.2.0 | |
907 | * | |
908 | * @param int $page Page ID. | |
909 | */ | |
910 | function the_attachments_url($page = null) { | |
911 | echo $this->get_attachments_url($page); | //Cross Site Scripting |
912 | } | |
913 | ||
914 | /** | |
915 | * Retrieve service URL. | |
916 | * | |
917 | * @since 2.3.0 | |
918 | * | |
919 | * @return string | |
920 | */ | |
921 | function get_service_url() { | |
922 | return $this->app_base . $this->SERVICE_PATH; | |
923 | } | |
924 | ||
925 | /** | |
926 | * Retrieve entry URL. | |
927 | * | |
928 | * @since 2.7.0 | |
929 | * | |
930 | * @param int $postID Post ID. | |
931 | * @return string | |
932 | */ | |
933 | function get_entry_url($postID = null) { | |
934 | if (!isset($postID)) { | |
935 | global $post; | |
936 | $postID = (int) $post->ID; | |
937 | } | |
938 | ||
939 | $url = $this->app_base . $this->ENTRY_PATH . "/$postID"; | |
940 | ||
941 | log_app('function',"get_entry_url() = $url"); | |
942 | return $url; | |
943 | } | |
944 | ||
945 | /** | |
946 | * Display entry URL. | |
947 | * | |
948 | * @since 2.7.0 | |
949 | * | |
950 | * @param int $postID Post ID. | |
951 | */ | |
952 | function the_entry_url($postID = null) { | |
953 | echo $this->get_entry_url($postID); | //Cross Site Scripting |
954 | } | |
955 | ||
956 | /** | |
957 | * Retrieve media URL. | |
958 | * | |
959 | * @since 2.2.0 | |
960 | * | |
961 | * @param int $postID Post ID. | |
962 | * @return string | |
963 | */ | |
964 | function get_media_url($postID = null) { | |
965 | if (!isset($postID)) { | |
966 | global $post; | |
967 | $postID = (int) $post->ID; | |
968 | } | |
969 | ||
970 | $url = $this->app_base . $this->MEDIA_SINGLE_PATH ."/file/$postID"; | //Arbitrary file disclosing |
971 | ||
972 | log_app('function',"get_media_url() = $url"); | |
973 | return $url; | |
974 | } | |
975 | ||
976 | /** | |
977 | * Display the media URL. | |
978 | * | |
979 | * @since 2.2.0 | |
980 | * | |
981 | * @param int $postID Post ID. | |
982 | */ | |
983 | function the_media_url($postID = null) { | |
984 | echo $this->get_media_url($postID); | //Cross Site Scripting |
985 | } | |
986 | ||
987 | /** | |
988 | * Set the current entry to post ID. | |
989 | * | |
990 | * @since 2.2.0 | |
991 | * | |
992 | * @param int $postID Post ID. | |
993 | */ | |
994 | function set_current_entry($postID) { | |
995 | global $entry; | |
996 | log_app('function',"set_current_entry($postID)"); | |
997 | ||
998 | if (!isset($postID)) { | |
999 | // $this->bad_request(); | |
1000 | $this->not_found(); |