| 1 |
<?php |
| 2 |
/* |
| 3 |
Plugin Name: Upload Larger Plugins |
| 4 |
Version: 1.5 |
| 5 |
Plugin URI: https://wordpress.org/plugins/upload-larger-plugins |
| 6 |
Description: Allow plugins larger than the PHP-defined limit to be uploaded. |
| 7 |
Author: David Anderson |
| 8 |
Donate: https://david.dw-perspective.org.uk/donate |
| 9 |
Author URI: https://david.dw-perspective.org.uk |
| 10 |
License: MIT |
| 11 |
*/ |
| 12 |
|
| 13 |
if (!defined('ABSPATH')) die('No direct access'); |
| 14 |
|
| 15 |
// Globals |
| 16 |
define('UPLOADLARGERPLUGINS_VERSION', '1.5'); |
| 17 |
define('UPLOADLARGERPLUGINS_SLUG', "upload-larger-plugins"); |
| 18 |
define('UPLOADLARGERPLUGINS_DIR', dirname(realpath(__FILE__))); |
| 19 |
define('UPLOADLARGERPLUGINS_URL', plugins_url('', __FILE__)); |
| 20 |
|
| 21 |
$simba_upload_larger_plugins = new Simba_Upload_Larger_Plugins(); |
| 22 |
|
| 23 |
class Simba_Upload_Larger_Plugins { |
| 24 |
|
| 25 |
private $upload_dir; |
| 26 |
private $upload_basedir; |
| 27 |
|
| 28 |
/** |
| 29 |
* Plugin constructor |
| 30 |
*/ |
| 31 |
public function __construct() { |
| 32 |
//add_filter('plugin_action_links', array($this, 'action_links'), 10, 2 ); |
| 33 |
add_action('install_plugins_upload', array($this, 'install_plugins_upload'), 9, 1); |
| 34 |
add_action('install_plugins_pre_upload', array($this, 'install_plugins_pre_upload')); |
| 35 |
add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts')); |
| 36 |
add_action('plugins_loaded', array($this, 'load_translations')); |
| 37 |
add_action('admin_head', array($this, 'admin_head')); |
| 38 |
add_action('wp_ajax_ulp_plupload_action', array($this, 'ulp_plupload_action')); |
| 39 |
add_action('admin_init', array($this, 'admin_init')); |
| 40 |
// This filter only exists on WP 3.7+. We used to use it then... but then WP 4.6.1 broke our method, so we've reverted to the pre-WP-3.7 method |
| 41 |
// add_filter('upgrader_pre_download', array($this, 'upgrader_pre_download'), 10, 3); |
| 42 |
// This action allows us to tweak the link URL on WP 5.5+'s |
| 43 |
add_filter('install_plugin_overwrite_actions', array($this, 'install_plugin_overwrite_actions')); |
| 44 |
} |
| 45 |
|
| 46 |
/** |
| 47 |
* Called by the WP filter install_plugin_overwrite_actions (WP 5.5+) |
| 48 |
* |
| 49 |
* @param Array $install_actions Array of plugin action links. |
| 50 |
* |
| 51 |
* @return Array - modified array |
| 52 |
*/ |
| 53 |
public function install_plugin_overwrite_actions($install_actions) { |
| 54 |
|
| 55 |
if (!empty($install_actions['overwrite_plugin']) && false !== strpos($install_actions['overwrite_plugin'], 'action=upload-plugin&')) { |
| 56 |
|
| 57 |
if (!empty($_GET['plugincksha1']) && !empty($_GET['overridebd']) && !empty($_GET['package']) && current_user_can('install_plugins')) { |
| 58 |
|
| 59 |
// WP 5.5 already uses "package=0"; so we have to replace that with the proper name that will work with our upload directory |
| 60 |
$install_actions['overwrite_plugin'] = str_replace('action=upload-plugin&', 'action=upload-plugin&plugincksha1='.urlencode($_GET['plugincksha1']).'&overridebd='.urlencode($_GET['overridebd']).'&package='.urlencode($_GET['package']).'&', $install_actions['overwrite_plugin']); |
| 61 |
|
| 62 |
$install_actions['overwrite_plugin'] = str_replace('package=0&', '', $install_actions['overwrite_plugin']); |
| 63 |
} |
| 64 |
|
| 65 |
} |
| 66 |
|
| 67 |
// error_log(print_r($install_actions, true)); |
| 68 |
|
| 69 |
return $install_actions; |
| 70 |
} |
| 71 |
|
| 72 |
/** |
| 73 |
* Called by the WP action admin_init. Used to continue when a completed upload from our widget has occurred. |
| 74 |
*/ |
| 75 |
public function admin_init() { |
| 76 |
|
| 77 |
// Check if parameters present indicate our action |
| 78 |
if (empty($_GET['plugincksha1']) || empty($_GET['overridebd']) || !isset($_GET['package']) || !current_user_can('install_plugins')) return; |
| 79 |
|
| 80 |
/* |
| 81 |
Old note: |
| 82 |
|
| 83 |
The rest of the code's purpose is to work-around the lack of the upgrader_pre_download filter before WP 3.7 |
| 84 |
The below would work on >= 3.7 too; but there, we use a more elegant/direct method. |
| 85 |
|
| 86 |
New situation: |
| 87 |
WP 4.6.1 - https://build.trac.wordpress.org/changeset/38466 - introduced a change which prevents upgrader_pre_download from working. So, this way is back. |
| 88 |
*/ |
| 89 |
|
| 90 |
// require(ABSPATH.WPINC.'/version.php'); |
| 91 |
// if (version_compare($wp_version, '3.7', '>=')) return; |
| 92 |
|
| 93 |
$package = (isset($_GET['fpackage']) && is_numeric($_GET['package'])) ? stripslashes($_GET['fpackage']) : stripslashes($_GET['package']); |
| 94 |
|
| 95 |
$upgrader = new stdClass; |
| 96 |
$upgrader->strings = array('download_failed' => __('Error when trying to find uploaded file', 'uploadlargerplugins')); |
| 97 |
$try_file = $this->upgrader_pre_download(false, $package, $upgrader); |
| 98 |
|
| 99 |
// The File_Upload_Upgrader object eventually gets constructed with this (where $urlholder = 'package', and $uploads = wp_upload_dir()) |
| 100 |
//File_Upload_Upgrader::filename = $_GET[$urlholder]; |
| 101 |
//File_Upload_Upgrader::package = $uploads['basedir'] . '/' . $this->filename; |
| 102 |
|
| 103 |
if (!(($uploads = wp_upload_dir()) && false === $uploads['error'])) return; |
| 104 |
|
| 105 |
if (is_string($try_file) && file_exists($try_file)) { |
| 106 |
$upload_dir = untrailingslashit(get_temp_dir()); |
| 107 |
// if (!is_writable($upload_dir)) return; |
| 108 |
$this->upload_basedir = $upload_dir; |
| 109 |
add_filter('upload_dir', array($this, 'upload_dir')); |
| 110 |
add_action('upgrader_process_complete', array($this, 'upgrader_process_complete')); |
| 111 |
} |
| 112 |
} |
| 113 |
|
| 114 |
// Only hooked on WP < 3.7 |
| 115 |
public function upgrader_process_complete() { |
| 116 |
remove_filter('upload_dir', array($this, 'upload_dir')); |
| 117 |
} |
| 118 |
|
| 119 |
public function upgrader_pre_download($result, $package, $upgrader) { |
| 120 |
|
| 121 |
if (empty($_GET['plugincksha1']) || empty($_GET['overridebd'])) return $result; |
| 122 |
$upload_dir = untrailingslashit(get_temp_dir()); |
| 123 |
|
| 124 |
// Sanity checks |
| 125 |
if ($upload_dir != $_GET['overridebd']) return new WP_Error('download_failed', $upgrader->strings['download_failed']); |
| 126 |
$try_file = $upload_dir.'/'.basename($package); |
| 127 |
|
| 128 |
if (!file_exists($try_file) || sha1_file($try_file) != $_GET['plugincksha1']) return new WP_Error('download_failed', $upgrader->strings['download_failed']); |
| 129 |
|
| 130 |
return $try_file; |
| 131 |
} |
| 132 |
|
| 133 |
/** |
| 134 |
* @return Boolean |
| 135 |
*/ |
| 136 |
private function is_our_page_and_authorised() { |
| 137 |
if (!current_user_can('install_plugins')) return false; |
| 138 |
|
| 139 |
require(ABSPATH.WPINC.'/version.php'); |
| 140 |
|
| 141 |
global $pagenow; |
| 142 |
// On WP 4.6, there is no longer an upload 'tab' - it's a slide-down instead |
| 143 |
|
| 144 |
return ($pagenow != 'plugin-install.php' || (version_compare($wp_version, '4.5.9999', '<') && (!isset($_REQUEST['tab']) || 'upload' != $_REQUEST['tab']))) ? false : true; |
| 145 |
|
| 146 |
} |
| 147 |
|
| 148 |
/** |
| 149 |
* Runs upon the WP action admin_enqueue_scripts |
| 150 |
*/ |
| 151 |
public function admin_enqueue_scripts() { |
| 152 |
|
| 153 |
if (!$this->is_our_page_and_authorised()) return; |
| 154 |
|
| 155 |
wp_enqueue_script('ulp-admin-ui', UPLOADLARGERPLUGINS_URL.'/admin.js', array('jquery', 'plupload-all'), '1'); |
| 156 |
|
| 157 |
wp_localize_script('ulp-admin-ui', 'ulplion', array( |
| 158 |
'notarchive' => __('This file does not appear to be a zip file.', 'uploadlargerplugins'), |
| 159 |
'notarchive2' => '<p>'.__('This file does not appear to be a zip file.', 'uploadlargerplugins').'</p>', |
| 160 |
'uploaderror' => __('Upload error:','uploadlargerplugins'), |
| 161 |
'makesure' => __('(make sure that you were trying to upload a zip file','uploadlargerplugins'), |
| 162 |
'uploaderr' => __('Upload error', 'uploadlargerplugins'), |
| 163 |
'jsonnotunderstood' => __('Error: the server sent us a response (JSON) which we did not understand.', 'uploadlargerplugins'), |
| 164 |
'error' => __('Error:','uploadlargerplugins') |
| 165 |
)); |
| 166 |
|
| 167 |
} |
| 168 |
|
| 169 |
/** |
| 170 |
* Runs upon the WP action plugins_loaded |
| 171 |
*/ |
| 172 |
public function load_translations() { |
| 173 |
// Tell WordPress where to find the translations |
| 174 |
load_plugin_textdomain('uploadlargerplugins', false, basename(dirname(__FILE__)).'/languages/'); |
| 175 |
} |
| 176 |
|
| 177 |
public function upload_dir($uploads) { |
| 178 |
if (!empty($this->upload_dir)) $uploads['path'] = $this->upload_dir; |
| 179 |
if (!empty($this->upload_basedir)) $uploads['basedir'] = $this->upload_basedir; |
| 180 |
return $uploads; |
| 181 |
} |
| 182 |
|
| 183 |
/** |
| 184 |
* Runs upon the AJAX event ulp_plupload_action |
| 185 |
*/ |
| 186 |
public function ulp_plupload_action() { |
| 187 |
|
| 188 |
@set_time_limit(900); |
| 189 |
|
| 190 |
if (!current_user_can('install_plugins')) return; |
| 191 |
check_ajax_referer('uploadlargerplugins-uploader'); |
| 192 |
|
| 193 |
$upload_dir = untrailingslashit(get_temp_dir()); |
| 194 |
if (!is_writable($upload_dir)) exit; |
| 195 |
$this->upload_dir = $upload_dir; |
| 196 |
|
| 197 |
add_filter('upload_dir', array($this, 'upload_dir')); |
| 198 |
// handle file upload |
| 199 |
|
| 200 |
$farray = array('test_form' => true, 'action' => 'ulp_plupload_action'); |
| 201 |
|
| 202 |
$farray['test_type'] = false; |
| 203 |
$farray['ext'] = 'zip'; |
| 204 |
$farray['type'] = 'application/zip'; |
| 205 |
|
| 206 |
// if (isset($_POST['chunks'])) { |
| 207 |
// |
| 208 |
// } else { |
| 209 |
// # Over-write - that's OK. |
| 210 |
// $farray['unique_filename_callback'] = array($this, 'unique_filename_callback'); |
| 211 |
// } |
| 212 |
|
| 213 |
$status = wp_handle_upload( |
| 214 |
$_FILES['async-upload'], |
| 215 |
$farray |
| 216 |
); |
| 217 |
remove_filter('upload_dir', array($this, 'upload_dir')); |
| 218 |
|
| 219 |
if (isset($status['error'])) { |
| 220 |
echo json_encode(array('e' => $status['error'])); |
| 221 |
exit; |
| 222 |
} |
| 223 |
|
| 224 |
// Should be a no-op |
| 225 |
$name = basename($_POST['name']); |
| 226 |
|
| 227 |
// If this was the chunk, then we should instead be concatenating onto the final file |
| 228 |
if (isset($_POST['chunks']) && isset($_POST['chunk']) && preg_match('/^[0-9]+$/',$_POST['chunk'])) { |
| 229 |
# A random element is added, because otherwise it is theoretically possible for another user to upload into a shared temporary directory in between the upload and install, and over-write |
| 230 |
$final_file = $name; |
| 231 |
rename($status['file'], $upload_dir.'/'.$final_file.'.'.$_POST['chunk'].'.zip.tmp'); |
| 232 |
$status['file'] = $upload_dir.'/'.$final_file.'.'.$_POST['chunk'].'.zip.tmp'; |
| 233 |
|
| 234 |
// Final chunk? If so, then stich it all back together |
| 235 |
if ($_POST['chunk'] == $_POST['chunks']-1) { |
| 236 |
if ($wh = fopen($upload_dir.'/'.$final_file, 'wb')) { |
| 237 |
for ($i=0 ; $i<$_POST['chunks']; $i++) { |
| 238 |
$rf = $upload_dir.'/'.$final_file.'.'.$i.'.zip.tmp'; |
| 239 |
if ($rh = fopen($rf, 'rb')) { |
| 240 |
while ($line = fread($rh, 32768)) fwrite($wh, $line); |
| 241 |
fclose($rh); |
| 242 |
@unlink($rf); |
| 243 |
} |
| 244 |
} |
| 245 |
fclose($wh); |
| 246 |
$status['file'] = $upload_dir.'/'.$final_file; |
| 247 |
} |
| 248 |
} |
| 249 |
|
| 250 |
} |
| 251 |
|
| 252 |
$response = array(); |
| 253 |
if (!isset($_POST['chunks']) || (isset($_POST['chunk']) && $_POST['chunk'] == $_POST['chunks']-1)) { |
| 254 |
$file = basename($status['file']); |
| 255 |
if (!preg_match('/\.zip$/i', $file, $matches)) { |
| 256 |
@unlink($status['file']); |
| 257 |
echo json_encode(array('e' => sprintf(__('Error: %s', 'uploadlargerplugins'), __('This file does not appear to be a zip file.', 'uploadlargerplugins')))); |
| 258 |
exit; |
| 259 |
} |
| 260 |
} |
| 261 |
|
| 262 |
// send the redirect URL |
| 263 |
$response['m'] = admin_url('update.php?action=upload-plugin&overridebd='.urlencode(dirname($status['file'])).'&plugincksha1='.sha1_file($status['file']).'&_wpnonce='.wp_create_nonce( 'plugin-upload' ).'&package='.urlencode(basename($status['file']))); |
| 264 |
echo json_encode($response); |
| 265 |
exit; |
| 266 |
} |
| 267 |
|
| 268 |
/** |
| 269 |
* Runs upon the WP action admin_head |
| 270 |
*/ |
| 271 |
public function admin_head() { |
| 272 |
|
| 273 |
if (!$this->is_our_page_and_authorised()) return; |
| 274 |
|
| 275 |
$chunk_size = min(wp_max_upload_size()-1024, 1024*1024*2-1024); |
| 276 |
|
| 277 |
# The multiple_queues argument is ignored in plupload 2.x (WP3.9+) - https://make.wordpress.org/core/2014/04/11/plupload-2-x-in-wordpress-3-9/ |
| 278 |
# max_file_size is also in filters as of plupload 2.x, but in its default position is still supported for backwards-compatibility. Likewise, our use of filters.extensions below is supported by a backwards-compatibility option (the current way is filters.mime-types.extensions |
| 279 |
|
| 280 |
$plupload_init = array( |
| 281 |
'runtimes' => 'html5,flash,silverlight,html4', |
| 282 |
'browse_button' => 'plupload-browse-button', |
| 283 |
'container' => 'plupload-upload-ui', |
| 284 |
'drop_element' => 'drag-drop-area', |
| 285 |
'file_data_name' => 'async-upload', |
| 286 |
'multiple_queues' => false, |
| 287 |
'max_file_count' => 1, |
| 288 |
'max_file_size' => '100Gb', |
| 289 |
'chunk_size' => $chunk_size.'b', |
| 290 |
'url' => admin_url('admin-ajax.php'), |
| 291 |
'filters' => array(array('title' => __('Allowed Files'), 'extensions' => 'zip')), |
| 292 |
'multipart' => true, |
| 293 |
'multi_selection' => false, |
| 294 |
'urlstream_upload' => true, |
| 295 |
// additional post data to send to our ajax hook |
| 296 |
'multipart_params' => array( |
| 297 |
'_ajax_nonce' => wp_create_nonce('uploadlargerplugins-uploader'), |
| 298 |
'action' => 'ulp_plupload_action' |
| 299 |
) |
| 300 |
); |
| 301 |
// 'flash_swf_url' => includes_url('js/plupload/plupload.flash.swf'), |
| 302 |
// 'silverlight_xap_url' => includes_url('js/plupload/plupload.silverlight.xap'), |
| 303 |
|
| 304 |
# WP 3.9 updated to plupload 2.0 - https://core.trac.wordpress.org/ticket/25663 |
| 305 |
if (is_file(ABSPATH.'wp-includes/js/plupload/Moxie.swf')) { |
| 306 |
$plupload_init['flash_swf_url'] = includes_url('js/plupload/Moxie.swf'); |
| 307 |
} else { |
| 308 |
$plupload_init['flash_swf_url'] = includes_url('js/plupload/plupload.flash.swf'); |
| 309 |
} |
| 310 |
|
| 311 |
if (is_file(ABSPATH.'wp-includes/js/plupload/Moxie.xap')) { |
| 312 |
$plupload_init['silverlight_xap_url'] = includes_url('js/plupload/Moxie.xap'); |
| 313 |
} else { |
| 314 |
$plupload_init['silverlight_xap_url'] = includes_url('js/plupload/plupload.silverlight.swf'); |
| 315 |
} |
| 316 |
|
| 317 |
?><script type="text/javascript"> |
| 318 |
var ulp_plupload_config=<?php echo json_encode($plupload_init); ?>; |
| 319 |
</script> |
| 320 |
<style type="text/css"> |
| 321 |
.drag-drop #drag-drop-area { |
| 322 |
border: 4px dashed #ddd; |
| 323 |
height: 200px; |
| 324 |
} |
| 325 |
#filelist { |
| 326 |
width: 100%; |
| 327 |
} |
| 328 |
#filelist .file { |
| 329 |
padding: 5px; |
| 330 |
background: #ececec; |
| 331 |
border: solid 1px #ccc; |
| 332 |
margin: 4px 0; |
| 333 |
} |
| 334 |
#filelist .fileprogress { |
| 335 |
width: 0%; |
| 336 |
background: #f6a828; |
| 337 |
height: 5px; |
| 338 |
} |
| 339 |
</style> |
| 340 |
<?php |
| 341 |
|
| 342 |
} |
| 343 |
|
| 344 |
public function install_plugins_pre_upload() { |
| 345 |
// Unhook the default uploader (works on WP < 4.6 only) |
| 346 |
remove_action('install_plugins_upload', 'install_plugins_upload'); |
| 347 |
} |
| 348 |
|
| 349 |
public function install_plugins_upload( $page = 1 ) { |
| 350 |
?> |
| 351 |
|
| 352 |
<div class="upload-plugin"> |
| 353 |
|
| 354 |
<?php |
| 355 |
|
| 356 |
require(ABSPATH.WPINC.'/version.php'); |
| 357 |
|
| 358 |
if (version_compare($wp_version, '4.5.9999', '<')) { ?> |
| 359 |
|
| 360 |
<!-- Upload form from Upload Larger Plugins --> |
| 361 |
<h4><?php _e('Install a plugin in .zip format'); ?></h4> |
| 362 |
|
| 363 |
<?php } ?> |
| 364 |
|
| 365 |
<p class="install-help" style="text-align:left; margin-bottom: 6px;"> |
| 366 |
<?php _e('If you have a plugin in a .zip format, you may install it by uploading it here.'); ?> |
| 367 |
</p> |
| 368 |
|
| 369 |
<?php |
| 370 |
|
| 371 |
if (version_compare($wp_version, '3.3', '<')) { |
| 372 |
echo '<em>'.sprintf(__('This feature requires %s version %s or later', 'uploadlargerplugins'), 'WordPress', '3.3').'</em>'; |
| 373 |
} else { |
| 374 |
?> |
| 375 |
<div id="plupload-upload-ui" class="drag-drop" style="width: 70%;"> |
| 376 |
<div id="drag-drop-area"> |
| 377 |
<div class="drag-drop-inside"> |
| 378 |
<p class="drag-drop-info"><?php _e('Drop plugin zip here', 'uploadlargerplugins'); ?></p> |
| 379 |
<p><?php _ex('or', 'Uploader: Drop plugin zip here - or - Select File'); ?></p> |
| 380 |
<p class="drag-drop-buttons"><input id="plupload-browse-button" type="button" value="<?php echo esc_attr(__('Select File', 'uploadlargerplugins')); ?>" class="button" /></p> |
| 381 |
</div> |
| 382 |
</div> |
| 383 |
<div id="filelist"> |
| 384 |
</div> |
| 385 |
</div> |
| 386 |
<?php |
| 387 |
} |
| 388 |
?> |
| 389 |
|
| 390 |
</div> |
| 391 |
|
| 392 |
<?php |
| 393 |
/* |
| 394 |
<div style="display:none;"> |
| 395 |
<form method="post" enctype="multipart/form-data" class="wp-upload-form" action="<?php echo self_admin_url('update.php?action=upload-plugin'); ?>"> |
| 396 |
<?php wp_nonce_field( 'plugin-upload'); ?> |
| 397 |
<input type="file" id="pluginzip" name="pluginzip" /> |
| 398 |
<?php submit_button( __( 'Install Now' ), 'button', 'install-plugin-submit', false ); ?> |
| 399 |
</form> |
| 400 |
</div> |
| 401 |
*/ |
| 402 |
} |
| 403 |
|
| 404 |
public function action_links($links, $file) { |
| 405 |
if ($file == UPLOADLARGERPLUGINS_SLUG."/".basename(__FILE__)) { |
| 406 |
array_unshift( $links, |
| 407 |
'<a href="options-general.php?page=upload_larger_plugins">'.__('Settings', 'uploadlargerplugins').'</a>' |
| 408 |
); |
| 409 |
} |
| 410 |
return $links; |
| 411 |
} |
| 412 |
|
| 413 |
} |
| 414 |
|