· 9 years ago · Jan 28, 2017, 07:56 PM
1<?php
2/**
3 * Plugin Uploader 1.2.1
4
5 * Copyright 2016 Matthew Rogowski
6
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10
11 ** http://www.apache.org/licenses/LICENSE-2.0
12
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18**/
19
20if(!defined("IN_MYBB"))
21{
22 header("HTTP/1.0 404 Not Found");
23 exit;
24}
25
26define('PLUGINUPLOADER_VERSION', '1.2.1');
27
28$plugins->add_hook("admin_config_plugins_activate_commit", "pluginuploader_admin_config_plugins_activate_commit");
29$plugins->add_hook("admin_config_permissions", "pluginuploader_admin_config_permissions");
30$plugins->add_hook("admin_config_plugins_tabs", "pluginuploader_add_pluginuploader_tab");
31$plugins->add_hook("admin_page_output_nav_tabs_start", "pluginuploader_add_pluginuploader_tab");
32$plugins->add_hook("admin_config_plugins_begin", "pluginuploader_admin_config_plugins_begin");
33$plugins->add_hook("admin_config_plugins_plugin_list", "pluginuploader_admin_config_plugins_plugin_list");
34$plugins->add_hook("admin_config_plugins_begin", "pluginuploader_admin_config_plugins_check_browse");
35$plugins->add_hook("admin_page_output_footer", "pluginuploader_admin_page_output_footer");
36
37function pluginuploader_info()
38{
39 return array(
40 'name' => 'Plugin Uploader',
41 'description' => 'Allows you to import .zip plugin archives directly and have the files extracted to their correct locations automatically.',
42 'website' => 'https://github.com/MattRogowski/Plugin-Uploader',
43 'author' => 'Matt Rogowski',
44 'authorsite' => 'https://matt.rogow.ski',
45 'version' => PLUGINUPLOADER_VERSION,
46 'compatibility' => '18*',
47 'codename' => 'pluginuploader'
48 );
49}
50
51function pluginuploader_install()
52{
53 global $db, $plugins, $pluginuploader_uninstall_confirm_override;
54
55 // this is so we override the confirmation when trying to uninstall, so we can just run the uninstall code
56 $pluginuploader_uninstall_confirm_override = true;
57 pluginuploader_uninstall();
58
59 if(!$db->table_exists("pluginuploader"))
60 {
61 $db->write_query("
62 CREATE TABLE " . TABLE_PREFIX . "pluginuploader (
63 `pid` SMALLINT(5) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
64 `name` VARCHAR(255) NOT NULL UNIQUE KEY,
65 `version` VARCHAR(25) NOT NULL ,
66 `files` TEXT NOT NULL
67 ) ENGINE = MYISAM ;
68 ");
69 }
70
71 if(!$db->field_exists("pluginuploader_key", "users"))
72 {
73 $db->add_column("users", "pluginuploader_key", "VARCHAR(120)");
74 }
75
76 chdir(MYBB_ROOT . "inc/plugins/");
77 $plugin_files = glob("*.php");
78
79 if(!empty($plugin_files))
80 {
81 foreach($plugin_files as $plugin_file)
82 {
83 $plugin_name = substr($plugin_file, 0, -4);
84 $info_func = $plugin_name . "_info";
85
86 if(!function_exists($info_func))
87 {
88 require_once MYBB_ROOT . "inc/plugins/" . $plugin_file;
89 }
90 if(!function_exists($info_func))
91 {
92 continue;
93 }
94
95 $info = $info_func();
96
97 $insert = array(
98 "name" => $db->escape_string($plugin_name),
99 "version" => $db->escape_string($info['version'])
100 );
101 $db->insert_query("pluginuploader", $insert);
102 }
103 }
104
105 change_admin_permission("config", "pluginuploader", 0);
106}
107
108function pluginuploader_is_installed()
109{
110 global $db;
111
112 return $db->table_exists("pluginuploader");
113}
114
115function pluginuploader_uninstall()
116{
117 global $mybb, $db, $lang, $page, $pluginuploader_uninstall_confirm_override;
118
119 $lang->load("config_pluginuploader");
120
121 if($mybb->request_method == "post" || $pluginuploader_uninstall_confirm_override === true)
122 {
123 if(!$pluginuploader_uninstall_confirm_override)
124 {
125 $query = $db->simple_select("pluginuploader", "version AS salt, files AS password", "name = '_password'");
126 $password = $db->fetch_array($query);
127
128 if(md5(md5($mybb->input['password']) . md5($password['salt'])) != $password['password'])
129 {
130 flash_message($lang->pluginuploader_uninstall_password_incorrect, 'error');
131 admin_redirect("index.php?module=config-plugins&action=deactivate&uninstall=1&plugin=pluginuploader&my_post_key={$mybb->post_code}");
132 }
133 }
134
135 if($db->table_exists("pluginuploader"))
136 {
137 $db->drop_table("pluginuploader");
138 }
139
140 if($db->field_exists("pluginuploader_key", "users"))
141 {
142 $db->drop_column("users", "pluginuploader_key");
143 }
144 }
145 else
146 {
147 $page->output_header($lang->pluginuploader);
148
149 $form = new Form("index.php?module=config-plugins&action=deactivate&uninstall=1&plugin=pluginuploader", "post");
150 $form_container = new FormContainer($lang->pluginuploader_uninstall_message_title);
151
152 $form_container->output_row("", "", $lang->pluginuploader_uninstall_warning);
153 $form_container->output_row($lang->pluginuploader_password . " <em>*</em>", $lang->pluginuploader_password_desc, $form->generate_password_box("password"));
154 $form_container->end();
155
156 $buttons[] = $form->generate_submit_button($lang->submit);
157 $form->output_submit_wrapper($buttons);
158 $form->end();
159
160 $page->output_footer();
161 exit;
162 }
163}
164
165function pluginuploader_activate()
166{
167
168}
169
170function pluginuploader_deactivate()
171{
172 my_unsetcookie("mybb_pluginuploader_key");
173}
174
175function pluginuploader_admin_config_plugins_activate_commit()
176{
177 global $codename, $install_uninstall;
178
179 if($codename == "pluginuploader" && $install_uninstall)
180 {
181 admin_redirect("index.php?module=config-plugins&action=pluginuploader");
182 }
183}
184
185function pluginuploader_admin_config_permissions($admin_permissions)
186{
187 global $lang;
188
189 $lang->load("config_pluginuploader");
190
191 $admin_permissions['pluginuploader'] = $lang->can_upload_plugins;
192
193 return $admin_permissions;
194}
195
196function pluginuploader_add_pluginuploader_tab(&$tabs)
197{
198 global $lang;
199
200 $lang->load("config_pluginuploader");
201
202 if(array_key_exists("plugins", $tabs) && array_key_exists("update_plugins", $tabs) && array_key_exists("browse_plugins", $tabs) && !array_key_exists("upload_plugin", $tabs))
203 {
204 $tabs['upload_plugin'] = array(
205 'title' => $lang->pluginuploader_upload_plugin,
206 'link' => "index.php?module=config-plugins&action=pluginuploader",
207 'description' => $lang->pluginuploader_upload_plugin_desc
208 );
209 }
210}
211
212function pluginuploader_admin_config_plugins_begin()
213{
214 global $mybb, $db, $cache, $lang, $plugins, $page, $pluginuploader;
215
216 $lang->load("config_pluginuploader");
217
218 if($mybb->input['action'] == "pluginuploader")
219 {
220 check_admin_permissions(array("module" => "config", "action" => "pluginuploader"));
221
222 require_once MYBB_ADMIN_DIR . "modules/config/pluginuploader.php";
223 }
224 elseif($mybb->input['action'] == "do_delete")
225 {
226 check_admin_permissions(array("module" => "config", "action" => "pluginuploader"));
227
228 if(!verify_post_check($mybb->input['my_post_key']))
229 {
230 flash_message($lang->invalid_post_verify_key2, 'error');
231 admin_redirect("index.php?module=config-plugins");
232 }
233
234 if($mybb->input['no'])
235 {
236 admin_redirect("index.php?module=config-plugins");
237 }
238
239 $plugin_name = $mybb->input['plugin'];
240 if($plugin_name)
241 {
242 $query = $db->simple_select("pluginuploader", "files", "name = '" . $db->escape_string($plugin_name) . "'");
243 if($db->num_rows($query) == 1 || file_exists(MYBB_ROOT . "inc/plugins/" . $plugin_name . ".php"))
244 {
245 $info_func = $plugin_name . "_info";
246 $deactivate_func = $plugin_name . "_deactivate";
247 $uninstall_func = $plugin_name . "_uninstall";
248 if(!function_exists($info_func))
249 {
250 // plugin isn't currently active, otherwise the info function would be available, but include it anyway to check for an uninstall function
251 require_once MYBB_ROOT . "inc/plugins/" . $plugin_name . ".php";
252 }
253
254 if(function_exists($deactivate_func))
255 {
256 $deactivate_func();
257 }
258 if(function_exists($uninstall_func))
259 {
260 $uninstall_func();
261 }
262 $plugins_cache = $cache->read("plugins");
263 $active_plugins = $plugins_cache['active'];
264 unset($active_plugins[$plugin_name]);
265 $plugins_cache['active'] = $active_plugins;
266 $cache->update("plugins", $plugins_cache);
267
268 $errors = array();
269 if($db->num_rows($query) == 1)
270 {
271 $files = $db->fetch_field($query, "files");
272 $files = unserialize($files);
273 foreach($files as $file)
274 {
275 $file = $pluginuploader->replace_admin_dir($file);
276 if(@is_dir(MYBB_ROOT . $file))
277 {
278 @$pluginuploader->rmdir(MYBB_ROOT . $file);
279 if(@is_dir(MYBB_ROOT . $file))
280 {
281 $errors[] = "./" . $file;
282 }
283 }
284 else
285 {
286 if(@file_exists(MYBB_ROOT . $file))
287 {
288 @$pluginuploader->unlink(MYBB_ROOT . $file);
289 if(@file_exists(MYBB_ROOT . $file))
290 {
291 $errors[] = "./" . $file;
292 }
293 }
294 }
295 }
296 }
297 elseif(@file_exists(MYBB_ROOT . "inc/plugins/" . $plugin_name . ".php"))
298 {
299 if(!@$pluginuploader->unlink(MYBB_ROOT . "inc/plugins/" . $plugin_name . ".php"))
300 {
301 $errors[] = "./inc/plugins/" . $plugin_name . ".php";
302 }
303 }
304
305 if(!empty($errors))
306 {
307 $errors = "<li>" . str_replace("MYBB_ADMIN_DIR", $mybb->config['admin_dir'], implode("</li><li>", $errors)) . "</li>";
308 flash_message($lang->sprintf($lang->pluginuploader_delete_errors, $errors), 'error');
309 admin_redirect("index.php?module=config-plugins");
310 }
311 else
312 {
313 $db->delete_query("pluginuploader", "name = '" . $db->escape_string($plugin_name) . "'");
314 flash_message($lang->pluginuploader_delete_success, 'success');
315 admin_redirect("index.php?module=config-plugins");
316 }
317 }
318 else
319 {
320 flash_message($lang->pluginuploader_delete_invalid_plugin, 'error');
321 admin_redirect("index.php?module=config-plugins");
322 }
323 }
324 else
325 {
326 flash_message($lang->pluginuploader_delete_invalid_plugin, 'error');
327 admin_redirect("index.php?module=config-plugins");
328 }
329 }
330 elseif($mybb->input['action'] == "delete")
331 {
332 check_admin_permissions(array("module" => "config", "action" => "pluginuploader"));
333
334 $lang->load("config_pluginuploader");
335
336 $plugin = $mybb->input['plugin'];
337 $delete_message = "";
338
339 $query = $db->simple_select("pluginuploader", "files", "name = '" . $db->escape_string($plugin) . "'");
340 if($db->num_rows($query) == 1)
341 {
342 $files = $db->fetch_field($query, "files");
343 if(!empty($files))
344 {
345 $files = unserialize($files);
346 foreach($files as &$file)
347 {
348 $file = $pluginuploader->replace_admin_dir($file);
349 $file = "./" . $file;
350 if(is_dir(MYBB_ROOT . $file))
351 {
352 $file .= "/";
353 }
354 }
355 $files = "<li>" . str_replace("MYBB_ADMIN_DIR", $mybb->config['admin_dir'], implode("</li><li>", $files)) . "</li>";
356 $delete_message = $lang->sprintf($lang->pluginuploader_delete_warning, $files);
357 }
358 }
359 if(!$delete_message)
360 {
361 $delete_message = $lang->pluginuploader_delete_warning_no_files;
362 }
363
364 $page->output_confirm_action("index.php?module=config-plugins&action=do_delete&plugin=" . $mybb->input['plugin'] . "&my_post_key={$mybb->post_code}", $delete_message);
365 }
366}
367
368function pluginuploader_admin_config_plugins_plugin_list()
369{
370 global $mybb, $lang, $plugins, $plugin_urls, $pluginuploader_js;
371
372 $plugins_list = get_plugins_list();
373 $plugin_codenames = $info = array();
374
375 if($plugins_list)
376 {
377 $active_hooks = $plugins->hooks;
378 foreach($plugins_list as $plugin_file)
379 {
380 require_once MYBB_ROOT."inc/plugins/".$plugin_file;
381 $codename = str_replace(".php", "", $plugin_file);
382 $plugin_codenames[] = $codename;
383 $infofunc = $codename."_info";
384 if(!function_exists($infofunc))
385 {
386 continue;
387 }
388 $plugininfo = $infofunc();
389 $plugininfo['guid'] = trim($plugininfo['guid']);
390 $plugininfo['codename'] = trim($plugininfo['codename']);
391
392 if($plugininfo['codename'] != "")
393 {
394 $info[] = $plugininfo['codename'];
395 $names[$plugininfo['codename']] = array('name' => $plugininfo['name'], 'version' => $plugininfo['version']);
396 }
397 elseif($plugininfo['guid'] != "")
398 {
399 $info[] = $plugininfo['guid'];
400 $names[$plugininfo['guid']] = array('name' => $plugininfo['name'], 'version' => $plugininfo['version']);
401 }
402 }
403 $plugins->hooks = $active_hooks;
404 }
405
406 if(empty($info))
407 {
408 return;
409 }
410
411 $url = "https://community.mybb.com/version_check.php?";
412 $url .= http_build_query(array("info" => $info))."&";
413 require_once MYBB_ROOT."inc/class_xml.php";
414 $contents = fetch_remote_file($url);
415
416 if(!$contents)
417 {
418 return;
419 }
420
421 $parser = new XMLParser($contents);
422 $tree = $parser->get_tree();
423
424 if(!is_array($tree) || !isset($tree['plugins']) || array_key_exists('error', $tree['plugins']))
425 {
426 return;
427 }
428
429 $lang->load("config_pluginuploader");
430
431 $plugins_info = $tree['plugins']['plugin'];
432 $plugin_urls = array();
433 if(!empty($plugins_info))
434 {
435 if(isset($plugins_info[0]))
436 {
437 foreach($plugins_info as $item)
438 {
439 $plugin_urls[$names[$item['attributes']['codename']]['name']] = array('codename' => $item['attributes']['codename'], 'url' => $item['download_url']['value'], 'version' => $item['version']['value']);
440 }
441 }
442 else
443 {
444 $plugin_urls[$names[$plugins_info['attributes']['codename']]['name']] = array('codename' => $item['attributes']['codename'], 'url' => $plugins_info['download_url']['value'], 'version' => $item['version']['value']);
445 }
446 }
447
448 foreach($plugin_urls as $name => &$info)
449 {
450 $info['update_available'] = false;
451 if(in_array($info['codename'], $plugin_codenames))
452 {
453 $infofunc = $info['codename']."_info";
454 if(!function_exists($infofunc))
455 {
456 continue;
457 }
458 $plugininfo = $infofunc();
459 if(version_compare($plugininfo['version'], $info['version']))
460 {
461 $info['update_available'] = true;
462 }
463 }
464 }
465
466 $pluginuploader_js = '<script src="jscripts/pluginuploader.js?version='.PLUGINUPLOADER_VERSION.'"></script>';
467 $pluginuploader_js .= '<script>
468 var pluginuploader_section = \'list\';
469 var plugin_download_urls = '.json_encode($plugin_urls).';
470 var can_use_mods_site = '.(int)pluginuploader_can_use_mods_site().';
471 var mybb_post_key = \''.$mybb->post_code.'\';
472 var lang_delete = \''.$lang->delete.'\';
473 var lang_pluginuploader_reimport = \''.$lang->pluginuploader_reimport.'\';
474 var lang_pluginuploader_reimport_update = \''.$lang->pluginuploader_reimport_update.'\';
475 </script>';
476}
477
478function pluginuploader_admin_config_plugins_check_browse()
479{
480 global $mybb, $lang, $pluginuploader_js;
481
482 $lang->load("config_pluginuploader");
483
484 switch($mybb->input['action'])
485 {
486 case 'check':
487 $lang_key = $lang->pluginuploader_upgrade;
488 break;
489 case 'browse':
490 $lang_key = $lang->pluginuploader_install;
491 break;
492 }
493
494 $pluginuploader_js = '<script src="jscripts/pluginuploader.js?version='.PLUGINUPLOADER_VERSION.'"></script>';
495 $pluginuploader_js .= '<script>
496 var pluginuploader_section = \''.$mybb->input['action'].'\';
497 var mybb_post_key = \''.$mybb->post_code.'\';
498 var lang_upgrade = \''.$lang_key.'\';
499 </script>';
500}
501
502function pluginuploader_admin_page_output_footer(&$args)
503{
504 global $pluginuploader_js;
505
506 if($pluginuploader_js)
507 {
508 echo $pluginuploader_js;
509 }
510}
511
512function pluginuploader_can_use_mods_site()
513{
514 if(function_exists('curl_init'))
515 {
516 return true;
517 }
518 else
519 {
520 return false;
521 }
522}
523
524global $pluginuploader;
525if(defined('IN_ADMINCP'))
526{
527 $pluginuploader = new PluginUploader;
528}
529class PluginUploader
530{
531 public $use_ftp = false;
532 public $ftp_connected = false;
533 private $ftp_connection;
534 private $ftp_host;
535 private $ftp_user;
536 private $ftp_password;
537 public $using_ssl = false;
538 public $details_storage_location;
539 public $changing_details = false;
540
541 public function __construct()
542 {
543 global $mybb, $db, $cache;
544
545 $plugins_cache = $cache->read('plugins');
546 if(!is_array($plugins_cache['active']) || !in_array('pluginuploader', $plugins_cache['active']))
547 {
548 return;
549 }
550
551 if($mybb->cookies['mybb_pluginuploader_ftp'])
552 {
553 $this->details_storage_location = 'cookie';
554 $ftp_details = unserialize(base64_decode($mybb->cookies['mybb_pluginuploader_ftp']));
555 $this->set_ftp_details($this->decrypt($ftp_details['ftp_host']), $this->decrypt($ftp_details['ftp_user']), $this->decrypt($ftp_details['ftp_password']));
556 }
557 else
558 {
559 $this->details_storage_location = 'database';
560 $query = $db->simple_select("pluginuploader", "files AS ftp_details", "name = '_ftp'");
561 if($db->num_rows($query) == 1)
562 {
563 $ftp_details = $db->fetch_field($query, "ftp_details");
564 $ftp_details = unserialize(base64_decode($ftp_details));
565 $this->set_ftp_details($this->decrypt($ftp_details['ftp_host']), $this->decrypt($ftp_details['ftp_user']), $this->decrypt($ftp_details['ftp_password']));
566 }
567 }
568
569 if(!$this->pluginuploader_copy_test() || $mybb->cookies['mybb_pluginuploader_use_ftp'])
570 {
571 $this->use_ftp = true;
572 }
573 else
574 {
575 $this->use_ftp = false;
576 }
577 }
578
579 public function set_ftp_details($host, $user, $password)
580 {
581 $this->ftp_host = $host;
582 $this->ftp_user = $user;
583 $this->ftp_password = $password;
584 }
585
586 public function clear_ftp_details($what = 'all')
587 {
588 global $db;
589
590 if($what == 'cookie' || $what == 'all')
591 {
592 my_unsetcookie("mybb_pluginuploader_ftp");
593 my_unsetcookie("mybb_pluginuploader_ftp_test");
594 }
595 if($what == 'database' || $what == 'all')
596 {
597 $db->delete_query("pluginuploader", "name = '_ftp'");
598 $db->delete_query("pluginuploader", "name = '_ftp_test'");
599 }
600 }
601
602 public function ftp_connect($return_error = false)
603 {
604 global $mybb, $db;
605
606 if($this->ftp_connected)
607 {
608 return true;
609 }
610
611 if(is_resource($this->ftp_connection))
612 {
613 $this->ftp_connected = true;
614 return true;
615 }
616
617 if(!function_exists("ftp_connect"))
618 {
619 if($return_error)
620 {
621 return "no_ftp";
622 }
623 return false;
624 }
625
626 // check if the FTP key exists; if not, try and add it automatically
627 if(!$mybb->config['pluginuploader_ftp_key'])
628 {
629 $this->add_config_ftp_key();
630 }
631 // if it's still not there, then error
632 if(!$mybb->config['pluginuploader_ftp_key'])
633 {
634 if($return_error)
635 {
636 return "missing_config";
637 }
638 return false;
639 }
640 else
641 {
642 if(!$this->changing_details)
643 {
644 // now we have to check if the encryption key used to encrypt the FTP details hasn't been changed
645 // if it has, we can't decrypt the details
646 $test_string = '';
647 if($this->details_storage_location == 'cookie')
648 {
649 $test_string = $mybb->cookies['mybb_pluginuploader_ftp_test'];
650 }
651 elseif($this->details_storage_location == 'database')
652 {
653 $query = $db->simple_select("pluginuploader", "files AS ftp_test", "name = '_ftp_test'");
654 if($db->num_rows($query) == 1)
655 {
656 $test_string = $db->fetch_field($query, "ftp_test");
657 }
658 }
659
660 if($test_string)
661 {
662 if($this->decrypt(base64_decode($test_string)) != 'test')
663 {
664 if($return_error)
665 {
666 return "config_wrong";
667 }
668 return false;
669 }
670 }
671 }
672 }
673
674 if(!$this->ftp_host || !$this->ftp_user || !$this->ftp_password)
675 {
676 if($return_error)
677 {
678 return "missing_details";
679 }
680 return false;
681 }
682
683 if(function_exists('ftp_ssl_connect'))
684 {
685 // we need to connect twice because if the server is not configured to understand the SSL encryption, the FTP login will fail, even though ftp_ssl_connect() is available and returns a valid connection
686 // just helps the flow a bit if the backup connection is made here
687 // http://uk.php.net/manual/en/function.ftp-ssl-connect.php#106931
688 $ftp_connection = @ftp_ssl_connect($this->ftp_host);
689 $ftp_connection_standard = @ftp_connect($this->ftp_host);
690 $this->using_ssl = true;
691 }
692 else
693 {
694 $ftp_connection = @ftp_connect($this->ftp_host);
695 }
696
697 if($ftp_connection)
698 {
699 // we need the second bit of this condition because even though ftp_ssl_connect() is available and returns a valid connection, the login will fail if the server is not configured to understand the SSL encryption
700 // instead, a standard FTP connection is made at the same time as the SSL connection, and if the SSL one can't login it tries to login with the standard one
701 // http://uk.php.net/manual/en/function.ftp-ssl-connect.php#106931
702 if(@ftp_login($ftp_connection, $this->ftp_user, $this->ftp_password))
703 {
704 $this->ftp_connection = $ftp_connection;
705 $this->ftp_connected = true;
706 return true;
707 }
708 elseif($this->using_ssl && @ftp_login($ftp_connection_standard, $this->ftp_user, $this->ftp_password))
709 {
710 $this->ftp_connection = $ftp_connection_standard;
711 $this->ftp_connected = true;
712 return true;
713 }
714 else
715 {
716 if($return_error)
717 {
718 return "failed_login";
719 }
720 return false;
721 }
722 }
723 else
724 {
725 if($return_error)
726 {
727 return "failed_host";
728 }
729 return false;
730 }
731
732 return false;
733 }
734
735 /**
736 * This is a version of glob() that overcomes a stupid piece of PHP behaviour. If open_basedir is enabled, glob() may return false when there are no matches, instead of the expected empty array.
737 * This can cause problems as code like count(glob("*")) will misbehave; if glob("*") returns false, then it'll be running count(false), which will return 1, so the code will think it found a match when it actually didn't.
738 * Instead, this function simply checks if the return value of glob() is an array, and if it's not, it returns an empty array like it should so the code knows there weren't any matches.
739 *
740 * @param string The pattern to search for
741 * @param Any flags to pass to glob()
742 * @return array Array of files/folders or empty array if no matches
743 **/
744 public function glob($pattern, $flags = 0)
745 {
746 $glob = glob($pattern, $flags);
747 if(is_array($glob))
748 {
749 return $glob;
750 }
751 else
752 {
753 return array();
754 }
755 }
756
757 /**
758 * Function to attempt to copy a file from the temporary folder to it's proper destination. Tries numerous methods if previous methods fail.
759 *
760 * @param string The path of the source file
761 * @param string The path of the destination file
762 * @return bool Whether or not the file was copied successfully
763 **/
764 public function copy($from, $to)
765 {
766 global $pluginuploader;
767
768 $to = $this->replace_admin_dir($to);
769
770 if(!$this->use_ftp)
771 {
772 if($this->do_copy_php($from, $to))
773 {
774 return true;
775 }
776 else
777 {
778 $return = false;
779 if(@chmod('path', 0777))
780 {
781 if($this->do_copy_php($from, $to))
782 {
783 $return = true;
784 }
785 @chmod('path', 0755);
786 }
787
788 return $return;
789 }
790 }
791 else
792 {
793 if($this->ftp_connect())
794 {
795 //echo MYBB_ROOT . "<br />";
796 //echo $from . "<br />";
797 //echo $to . "<br />";
798 //echo ftp_pwd($ftp) . "<br />";
799 $ftp_dir = MYBB_ROOT;
800 $ftp_to = $to;
801 $ftp_from = $from;
802 while(!empty($ftp_dir) && !@ftp_chdir($this->ftp_connection, $ftp_dir))
803 {
804 $ftp_dir = strstr(ltrim($ftp_dir, "/"), "/");
805 $ftp_from = strstr(ltrim($ftp_from, "/"), "/");
806 $ftp_to = strstr(ltrim($ftp_to, "/"), "/");
807 //echo $ftp_dir . "<br />";
808 }
809 if(empty($ftp_dir))
810 {
811 return false;
812 }
813 //echo ftp_pwd($ftp) . "<br />";
814 //echo $ftp_from . "<br />";
815 //echo $ftp_to . "<br />";
816 $ftp_to_info = pathinfo($ftp_to);
817 $ftp_to_dir = $ftp_to_info['dirname'];
818 //exit;
819
820 if($this->do_copy_ftp($ftp_from, $ftp_to, $from, $to))
821 {
822 return true;
823 }
824 else
825 {
826 $return = false;
827 if(@ftp_chmod($this->ftp_connection, $this->get_ftp_chmod(777), $ftp_to_dir))
828 {
829 if($this->do_copy_ftp($ftp_from, $ftp_to, $from, $to))
830 {
831 $return = true;
832 }
833 @ftp_chmod($this->ftp_connection, $this->get_ftp_chmod(755), $ftp_to_dir);
834 }
835
836 return $return;
837 }
838 }
839 }
840
841 return false;
842 }
843
844 public function do_copy_php($from, $to)
845 {
846 if(copy($from, $to))
847 {
848 if(@file_exists($to))
849 {
850 return true;
851 }
852 }
853
854 // copy() has failed, try rename()
855 if(@rename($from, $to))
856 {
857 if(@file_exists($to))
858 {
859 return true;
860 }
861 }
862
863 // rename() has failed, try fopen()
864 $file = @fopen($to, "w");
865 if($file)
866 {
867 $contents = @file_get_contents($from);
868 if($contents)
869 {
870 if(@fwrite($file, $contents) || @file_put_contents($file, $contents))
871 {
872 @fclose($file);
873 if(@file_exists($to))
874 {
875 return true;
876 }
877 }
878 }
879 }
880 }
881
882 public function do_copy_ftp($ftp_from, $ftp_to, $from, $to)
883 {
884 if(@ftp_rename($this->ftp_connection, $ftp_from, $ftp_to))
885 {
886 if(@file_exists($to))
887 {
888 return true;
889 }
890 }
891
892 if(@ftp_put($this->ftp_connection, $ftp_to, $from, FTP_BINARY))
893 {
894 if(@file_exists($to))
895 {
896 return true;
897 }
898 }
899
900 if(@ftp_fput($this->ftp_connection, $ftp_to, fopen($from, 'r'), FTP_BINARY))
901 {
902 if(@file_exists($to))
903 {
904 return true;
905 }
906 }
907 }
908
909 /**
910 * Function to attempt to create a directory.
911 *
912 * @param string The path of the new directory
913 * @return bool Whether or not the directory was created successfully
914 **/
915 public function mkdir($name)
916 {
917 global $pluginuploader;
918
919 $name = $this->replace_admin_dir($name);
920
921 if(@mkdir($name))
922 {
923 @chmod($name, 0755);
924 if(is_dir($name))
925 {
926 return true;
927 }
928 }
929
930 if($this->ftp_connect())
931 {
932 $ftp_name = $name;
933 while(!empty($ftp_name) && !@ftp_mkdir($this->ftp_connection, $ftp_name))
934 {
935 $ftp_name = strstr(ltrim($ftp_name, "/"), "/");
936 }
937 if(empty($ftp_name))
938 {
939 return false;
940 }
941 @ftp_chmod($this->ftp_connection, $this->get_ftp_chmod(755), $ftp_name);
942 if(is_dir($ftp_name))
943 {
944 return true;
945 }
946 }
947 return false;
948 }
949
950 public function unlink($file)
951 {
952 $file = $this->replace_admin_dir($file);
953
954 if(file_exists($file))
955 {
956 if(@unlink($file))
957 {
958 if(!file_exists($file))
959 {
960 return true;
961 }
962 }
963
964 if($this->ftp_connect())
965 {
966 $ftp_file = $file;
967 while(!empty($ftp_file) && !@ftp_delete($this->ftp_connection, $ftp_file))
968 {
969 $ftp_file = strstr(ltrim($ftp_file, "/"), "/");
970 }
971 if(empty($ftp_file))
972 {
973 return false;
974 }
975 if(!file_exists($ftp_file))
976 {
977 return true;
978 }
979 }
980
981 return false;
982 }
983
984 return false;
985 }
986
987 public function rmdir($dir)
988 {
989 $dir = $this->replace_admin_dir($dir);
990
991 if(is_dir($dir))
992 {
993 $objects = @scandir($dir);
994 if(!empty($objects))
995 {
996 foreach($objects as $object)
997 {
998 if($object != "." && $object != "..")
999 {
1000 if(is_dir($dir . "/" . $object))
1001 {
1002 @$this->rmdir($dir . "/" . $object);
1003 }
1004 else
1005 {
1006 @$this->unlink($dir . "/" . $object);
1007 }
1008 }
1009 reset($objects);
1010 }
1011 }
1012
1013 if(@rmdir($dir))
1014 {
1015 if(!is_dir($dir))
1016 {
1017 return true;
1018 }
1019 }
1020
1021 if($this->ftp_connect())
1022 {
1023 $ftp_dir = $dir;
1024 while(!empty($ftp_dir) && !@ftp_rmdir($this->ftp_connection, $ftp_dir))
1025 {
1026 $ftp_dir = strstr(ltrim($ftp_dir, "/"), "/");
1027 }
1028 if(empty($ftp_dir))
1029 {
1030 return false;
1031 }
1032 if(!is_dir($ftp_dir))
1033 {
1034 return true;
1035 }
1036 }
1037
1038 return false;
1039 }
1040
1041 return false;
1042 }
1043
1044 /**
1045 * Generate an FTP key
1046 **/
1047 public function generate_config_ftp_key()
1048 {
1049 return random_str(32);
1050 }
1051
1052 /**
1053 * Tries to add the FTP key to config.php automatically
1054 **/
1055 public function add_config_ftp_key()
1056 {
1057 global $mybb;
1058
1059 if(!is_writable(MYBB_ROOT.'inc/config.php'))
1060 {
1061 return false;
1062 }
1063
1064 $ftp_key = $this->generate_config_ftp_key();
1065
1066 $config_lines = explode("\n", file_get_contents(MYBB_ROOT.'inc/config.php'));
1067 foreach($config_lines as $i => &$line)
1068 {
1069 if(strpos($line, 'pluginuploader_ftp_key') !== false)
1070 {
1071 $line = '$config[\'pluginuploader_ftp_key\'] = \''.$ftp_key.'\';';
1072 break;
1073 }
1074 elseif(++$i == count($config_lines))
1075 {
1076 $config_lines[] = '';
1077 $config_lines[] = '/* Plugin Uploader - FTP details encryption key */';
1078 $config_lines[] = '$config[\'pluginuploader_ftp_key\'] = \''.$ftp_key.'\';';
1079 }
1080 }
1081 if(file_put_contents(MYBB_ROOT.'inc/config.php', implode("\n", $config_lines)))
1082 {
1083 $mybb->config['pluginuploader_ftp_key'] = $ftp_key;
1084 return true;
1085 }
1086
1087 return false;
1088 }
1089
1090 /**
1091 * Replace 'admin' in the file path to compensate for renamed admin folders
1092 **/
1093 public function replace_admin_dir($name, $placeholder = false)
1094 {
1095 global $mybb;
1096
1097 $name = str_replace("MYBB_ADMIN_DIR", "admin", $name);
1098
1099 if($mybb->config['admin_dir'] == "admin")
1100 {
1101 return $name;
1102 }
1103
1104 if($placeholder)
1105 {
1106 $replace = "MYBB_ADMIN_DIR";
1107 }
1108 else
1109 {
1110 $replace = $mybb->config['admin_dir'];
1111 }
1112
1113 $remove_mybb_root = false;
1114 if(strpos($name, MYBB_ROOT) !== false)
1115 {
1116 $name = str_replace(MYBB_ROOT, "", $name);
1117 $remove_mybb_root = true;
1118 }
1119
1120 $in_admin_dir = false;
1121 if(strpos($name, "/") === false)
1122 {
1123 if($name == "admin")
1124 {
1125 $in_admin_dir = true;
1126 }
1127 }
1128 else
1129 {
1130 if(substr($name, 0, strpos($name, "/")) == "admin")
1131 {
1132 $in_admin_dir = true;
1133 }
1134 }
1135
1136 if($in_admin_dir)
1137 {
1138 $name = preg_replace("#admin#", $replace, $name, 1);
1139 }
1140
1141 if($remove_mybb_root)
1142 {
1143 $name = MYBB_ROOT . $name;
1144 }
1145
1146 return $name;
1147 }
1148
1149 /**
1150 * Tests whether or not PHP is able to copy files in the file system. If it can't, files will have to be moved with FTP.
1151 * This check will be performed on the plugin upload page, and request an FTP password for a pre-stored FTP account.
1152 **/
1153 public function pluginuploader_copy_test()
1154 {
1155 if(!@file_exists(MYBB_ROOT . "inc/plugins/temp/test.php"))
1156 {
1157 if(!@fopen(MYBB_ROOT . "inc/plugins/temp/test.php", "r"))
1158 {
1159 return false;
1160 }
1161 }
1162
1163 if(@file_exists(MYBB_ROOT . "inc/plugins/plugin_uploader_test.php"))
1164 {
1165 @$this->unlink(MYBB_ROOT . "inc/plugins/plugin_uploader_test.php");
1166 }
1167
1168 if(!@copy(MYBB_ROOT . "inc/plugins/temp/test.php", MYBB_ROOT . "inc/plugins/plugin_uploader_test.php"))
1169 {
1170 return false;
1171 }
1172
1173 if(@file_exists(MYBB_ROOT . "inc/plugins/plugin_uploader_test.php"))
1174 {
1175 @$this->unlink(MYBB_ROOT . "inc/plugins/plugin_uploader_test.php");
1176 }
1177
1178 return true;
1179 }
1180
1181 public function user_is_nobody($file)
1182 {
1183 $fileowner = @fileowner($file);
1184 if($fileowner === false)
1185 {
1186 return false;
1187 }
1188 $userinfo = @posix_getpwuid($fileowner);
1189 if(!$userinfo)
1190 {
1191 return false;
1192 }
1193 if($userinfo['name'] == "nobody" || !$userinfo['name'])
1194 {
1195 return true;
1196 }
1197 return false;
1198 }
1199
1200 public function get_ftp_chmod($perm)
1201 {
1202 return (int)octdec(str_pad($perm, 4, '0', STR_PAD_LEFT));
1203 }
1204
1205 /*
1206 * Encrypt a string - credit: http://stackoverflow.com/a/1289114
1207 */
1208 public function encrypt($string)
1209 {
1210 global $mybb;
1211
1212 $key = $mybb->config['pluginuploader_ftp_key'];
1213
1214 return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string, MCRYPT_MODE_CBC, md5(md5($key))));
1215 }
1216
1217 /*
1218 * Decrypt a string - credit: http://stackoverflow.com/a/1289114
1219 */
1220 public function decrypt($string)
1221 {
1222 global $mybb;
1223
1224 $key = $mybb->config['pluginuploader_ftp_key'];
1225
1226 return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($string), MCRYPT_MODE_CBC, md5(md5($key))), "\0");
1227 }
1228
1229 public function __destruct()
1230 {
1231 @ftp_close($this->ftp_connection);
1232 }
1233}
1234?>