1<?php
2/**
3 * Upgrade API: File_Upload_Upgrader class
4 *
5 * @package WordPress
6 * @subpackage Upgrader
7 * @since 4.6.0
8 */
9
10/**
11 * Core class used for handling file uploads.
12 *
13 * This class handles the upload process and passes it as if it's a local file
14 * to the Upgrade/Installer functions.
15 *
16 * @since 2.8.0
17 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader.php.
18 */
19class File_Upload_Upgrader {
20
21	/**
22	 * The full path to the file package.
23	 *
24	 * @since 2.8.0
25	 * @var string $package
26	 */
27	public $package;
28
29	/**
30	 * The name of the file.
31	 *
32	 * @since 2.8.0
33	 * @var string $filename
34	 */
35	public $filename;
36
37	/**
38	 * The ID of the attachment post for this file.
39	 *
40	 * @since 3.3.0
41	 * @var int $id
42	 */
43	public $id = 0;
44
45	/**
46	 * Construct the upgrader for a form.
47	 *
48	 * @since 2.8.0
49	 *
50	 * @param string $form      The name of the form the file was uploaded from.
51	 * @param string $urlholder The name of the `GET` parameter that holds the filename.
52	 */
53	public function __construct( $form, $urlholder ) {
54
55		if ( empty( $_FILES[ $form ]['name'] ) && empty( $_GET[ $urlholder ] ) ) {
56			wp_die( __( 'Please select a file' ) );
57		}
58
59		// Handle a newly uploaded file. Else, assume it's already been uploaded.
60		if ( ! empty( $_FILES ) ) {
61			$overrides = array(
62				'test_form' => false,
63				'test_type' => false,
64			);
65			$file      = wp_handle_upload( $_FILES[ $form ], $overrides );
66
67			if ( isset( $file['error'] ) ) {
68				wp_die( $file['error'] );
69			}
70
71			$this->filename = $_FILES[ $form ]['name'];
72			$this->package  = $file['file'];
73
74			// Construct the object array.
75			$object = array(
76				'post_title'     => $this->filename,
77				'post_content'   => $file['url'],
78				'post_mime_type' => $file['type'],
79				'guid'           => $file['url'],
80				'context'        => 'upgrader',
81				'post_status'    => 'private',
82			);
83
84			// Save the data.
85			$this->id = wp_insert_attachment( $object, $file['file'] );
86
87			// Schedule a cleanup for 2 hours from now in case of failed installation.
88			wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $this->id ) );
89
90		} elseif ( is_numeric( $_GET[ $urlholder ] ) ) {
91			// Numeric Package = previously uploaded file, see above.
92			$this->id   = (int) $_GET[ $urlholder ];
93			$attachment = get_post( $this->id );
94			if ( empty( $attachment ) ) {
95				wp_die( __( 'Please select a file' ) );
96			}
97
98			$this->filename = $attachment->post_title;
99			$this->package  = get_attached_file( $attachment->ID );
100		} else {
101			// Else, It's set to something, Back compat for plugins using the old (pre-3.3) File_Uploader handler.
102			$uploads = wp_upload_dir();
103			if ( ! ( $uploads && false === $uploads['error'] ) ) {
104				wp_die( $uploads['error'] );
105			}
106
107			$this->filename = sanitize_file_name( $_GET[ $urlholder ] );
108			$this->package  = $uploads['basedir'] . '/' . $this->filename;
109
110			if ( 0 !== strpos( realpath( $this->package ), realpath( $uploads['basedir'] ) ) ) {
111				wp_die( __( 'Please select a file' ) );
112			}
113		}
114	}
115
116	/**
117	 * Delete the attachment/uploaded file.
118	 *
119	 * @since 3.2.2
120	 *
121	 * @return bool Whether the cleanup was successful.
122	 */
123	public function cleanup() {
124		if ( $this->id ) {
125			wp_delete_attachment( $this->id );
126
127		} elseif ( file_exists( $this->package ) ) {
128			return @unlink( $this->package );
129		}
130
131		return true;
132	}
133}
134