Example #1
0
	function run($command_vs_args) {
		global $global;
		foreach ($global as $var) {
			global $$var;
		}
		$_SESSION['cmds_counter']++;

		ob_start();

		# для однострочных запросов с несколькими командами разделёнными точкой с запятой и пробелом после неё
		# в идеале нужно пробежаться такой же функцией как это сделано чуть ниже, str_split, чтобы учесть влияние кавычек
		// if (strpos($command_vs_args, '; ') !== false) {
		// 	$commands = explode('; ', $command_vs_args);

		// 	foreach ($commands as $command) {
		// 		run($command);
		// 	}
		// }

		# бывш.: $argv = explode(' ', $command_vs_args);
		# нужно разбивать более грамотно, с учетом также одинарных, косых кавычек и символов экранирования \
		# кроме того, косые кавычки нужно зарезервировать для исполнения команды, которая в них заключена и возврата ответа, чтобы some do `command arg1 arg2` соответствовало some do run('command arg1 arg2');
		$signs = str_split($command_vs_args);
		$cmds = [];	# двумерный массив комманд, [[команда, аргумент1, аргумент2...], [команда2, арг1, арг2...]]
		$cmdc = 0;	# номер команды	# command counter
		$argc = 0;	# номер аргумента для этой команды, где 0 аргумент - имя команды	# argument counter
		$quoted_str = 0;	# флаг, определяемый является ли обрабатываемый символ частью выражения заключенного в кавычки или нет (заодно хранит символ самих кавычек для отеделния одинарных от двойных и косых)
		$prev_sign = null;
		$escape_character = '\\';			# экранирующий символ
		$quotemarks = '\'"`';				# варианты написания кавычек
		$argument_key_identifier = '-';		# символ определяющий то, что начинающийся с него аргумент является ключом аргумента следующего за этим ключем
		# « »
		# „ “, « », “ ”, ‘ ’ 
		# ’ '
		$arguments_separator = ' ';		# символ разделяющий аргументы функции
		$executing_quotemarks = '`';	# символ подстановки результата выполненного выражения в место выражения
		$statements_separator = ';';	# символ разделяющий команды для выполнения (общепринят в линуксе символ |, а хотелось бы также обратные кавычки использовать для этого дела)
		foreach ($signs as $sign) {
			// $its_not_first_sign_of_argument_value = isset($cmds[$cmdc][$argc]);

			if (!$prev_sign || $prev_sign == $arguments_separator || $prev_sign == $statements_separator) {
				$its_first_sign_of_argument_value = true;
			} else {
				$its_first_sign_of_argument_value = false;
			}

			$its_not_first_sign_of_argument_value = !$its_first_sign_of_argument_value;

			# этот блок выполняется каждую итерацию пока не будет обнаружено выражение заключенное в кавычки
			if (!$quoted_str) {
				# если это первый символ аргумента а не где-то посередине
				if ($its_first_sign_of_argument_value /* && $prev_sign != $escape_character */) {
					# если открылись кавычки, причем кавычки не экранированы предварительно
					if ($prev_sign != $escape_character && strpbrk($sign, $quotemarks)) {
						$quoted_str = $sign;
						# do nothing, becouse all we need - switch $quoted_str to new sign
					# а если мы имеем дело с ключем аргумента, а не самим аргументом
					} elseif ($sign == $argument_key_identifier && $prev_sign != $escape_character /* && $prev_sign = $argument_key_identifier */) {
						# обрезаем оба символа дефиса, значение параметра после считывания дублируется как ключ ассоциативного массива, а следующий за ним параметр интерпретируется как значение элемента массива параметров с этим ключем
						$arg_is_its_key = true;
						# $cmds[$cmdc][$argc] = '';
						// echo $sign . $quoted_str;
					} else {
						if ($sign != $arguments_separator) {
							$cmds[$cmdc][$argc] .= $sign;
						}
					}
				# a если это не первый символ аргумента
				} else {
					if ($sign == $statements_separator /* && $prev_sign != $escape_character */) {
						$cmdc++;
						$argc=0;
					} elseif ($sign == $arguments_separator /* && $prev_sign != $escape_character */) {
						if ($arg_is_its_key) {
							$arg_is_its_key = false;
							$arg_key = $cmds[$cmdc][$argc];
							$cmds[$cmdc][$argc] = '';
							// $arg_has_key = true;
							// echo '$arg_val = ' . $cmds[$cmdc][$argc];
							# unset($cmds[$cmdc][$argc]);
						} elseif ($arg_key) {
							$cmds[$cmdc][ $arg_key ] = $cmds[$cmdc][$argc];
							$arg_key = '';
							$argc++;
						} else {
							$argc++;
						}
					} else {
						$cmds[$cmdc][$argc] .= $sign;
					}
				}
			# а этот блок выполняется при каждой итерации пока кавычки не закроются
			} else {	# if $quoted_str (этот блок говорит как обрабатывать символы выражения заключенного в кавычки)
				# если символ кавычек соответсвует тому символу, с которого начиналась заключенная в кавычки строка, выходим из блока, при необходимости исполняем заключенную в косые кавычки строку, результат записываем в аргумент
				if ($sign == $quoted_str && $prev_sign != $escape_character) {	# теперь $prev_sign - это надёжное решение, так как только НЕЧЕТНОЕ ЧИСЛО обратных слешей перед кавычкой не даёт закрыть кавычки, четное число считается экранированием слешем самого себя и позволяется использовать в конце строки.
					$quoted_str = 0;
					if ($sign == $executing_quotemarks) {
						# run($argv, $argc);	# $command_name
						# выполняем выражение и подтавляем результат на его место
						$cmds[$cmdc][$argc] = run($cmds[$cmdc][$argc]);
					}
				# а если символ соответсвует, но предварительно экранирован, не выходим из блока, а сам символ экранирования заменяем на символ кавычек (то же самое что добавить символ кавычек к строке, но мы перед этим записали символ экранирования, его надо стереть)
				} elseif ($sign == $quoted_str && $prev_sign == $escape_character) {	# определяет поведение для \" в "строке"
					$cmds[$cmdc][$argc] = preg_replace("!(.)$!", $sign, $cmds[$cmdc][$argc]);
				# а если это экранированный символ экранирования строки (\\), то дописываем его как есть, НО не записываем текущий символ предыдущим - то бишь текущий символ экранирования не будет экранировать следующий за ним символ теперь
				} elseif ($sign == $escape_character && $prev_sign == $escape_character) {	# 
					if (!$module['cmd']['halve_escape_character']) {
						$cmds[$cmdc][$argc] .= $sign;		# эта строка определяет отличия между стандартной интерпретацией обратных слешей и моей. у меня все обратные слеши останутся в строке не сьеденными, обычно же их нужно двойное количество. в моём случае это не требуется нигде, кроме конца строки, там поведение не отличается от стандартного - два слеша превращаются в один
					}
					$sign = null;
				} else {	# 
					$cmds[$cmdc][$argc] .= $sign;
				}
			}
			$prev_sign = $sign;
		}

		foreach ($cmds as $cmdc => $argv) {
			$command_name = $argv[0];
			// var_dump($cmds);

			# следует читать как "если запрошенное имя команды пусто", не путать с "если у команды нет аргументов" - первым аргументом всегда идёт имя вызываемой команды
			if (empty($argv)) continue;
			# если запрошенная команда начинается с ./ искать будем в текущей директории
			if (preg_match('!^\./!', $command_name)) {
				$commands_folder = '';
				# chdir($_SESSION['cd']['user']);
				$command_directory = $_SESSION['cd']['user'];
			}
			else {
				$command_directory = $_SESSION['cd']['default'];
				$commands_folder = $GLOBALS['fold']['cmd'];
			}

			# если расширение файла исполняемого скрипта указано, не дописываем его вторично (полезно для вызова на исполнение некоего стороннего скриптика, например ./some.py или даже ./some.js, правила для их исполннения пока не написаны, однако позже мы это исправим)
			if (preg_match('!\\.(php|py|pl|c)$!', $command_name)) {
				$command_extension = '';
			}
			else {
				$command_extension = $GLOBALS['site']['extensions'];
			}

			# Что очень важно, на локалке и на хостинге $command_directory сильно отличаются (на хостинге пусто, на локали адрес от корневой директории системы), так что нужно максимально нивелировать различия
			if ($command_directory) $command_directory .= '/';
			$cmd_file = $command_directory . $commands_folder . $command_name . $command_extension;

			# если команда по прямому имени не найдена, пробуем прогрузить все известные синонимы всех команд и найти реальное имя команы по синонимуму, чтобы позже выполнить команду по её реальному имени
			$true_name = $command_name;
			if (!is_readable($cmd_file)) {
				$all_aliases = include_once $GLOBALS['path']['cmd']['aliases'];

				foreach ($all_aliases as $true_name => $command_aliases) {
					# удаляем из имени запрошенной команды все посторонние символы, которые могут интерпретироваться как часть регулярного выражения
					$command_name = preg_replace('/`[|\'\\\\]/', '', $command_name);
				# 	с одной стороны нужно и сохнаить возможность использования символов в псевдонимах, вроде "цитата дня" или исправляющие некорректный набор из-за раскладки
				#	$command_name = preg_replace('![\^\/\|"\'\\\[\]\(\)\?\.\*\&\%\#\{\}\+\$]!', '', $command_name);
					# а вообще по хорошему следует оставить в наименованиях досутпными только цифро-буквенные знаки.. так и сделаю
				#	if (!preg_match('!^[\w\d-_]+$!', $command_name)) {
				#		$command_name = 'motherofgod';
				#	}
					# проверяет есть ли вхождение |АЛИАС| в строке
					if (preg_match('`\|'.$command_name.'\|`', '|' . $command_aliases . '|')) {
						$cmd_file = $commands_folder . $true_name . $command_extension;
						break;
					}
				}
			}
			# никаких else в этом месте, после первого выполняется изменение $cmd_file и неоходимо второй раз проверить на читабельность

			# запуск команды
			if (function_exists($true_name)) {
				try {
					customization('user');		# on
					# нужно передвать всем массивом $argv, для этого все функции нужно переписать на получения массива с неопределенным количеством переменных
					# argc - arguments counter
					$result .= $true_name($argv, $argc);
					customization('default');	# off
					$result .= history_add_cmd($command_vs_args);
					$result .= cmd_log($command_vs_args, $result);
				}
				catch(Exception $e) {
					$error->report($e->getMessage(), __LINE__, '`' . $true_name . '` Command Exception', __FILE__, $e->getCode());
				}

			} else {
				# проверка существования и доступности для выполнения файла с инициализацией функции / описанием кода для выполнения
				if (is_readable($cmd_file)) {
					try {
						customization('user');
						$result .= include $cmd_file;
						customization('default');
						$result .= history_add_cmd($command_vs_args);
						$result .= cmd_log($command_vs_args, $result);
					}
					catch(Exception $e) {
						# элементарная многоязычеость ошибок: заменяем $e->getMessage() на выборку из БД по этой фразе в текущей языковой таблице
						# как вариант ещё можно сделать двойной запрос - сначала получать значение ID для фразы на разных языках, а потом по ID найти перевод на соответствующий язык. Ну, это требует и соотв. структуры БД
						global $db, $database;
						$query = sprintf('SELECT `%s` FROM `%s` WHERE noconflict_hash="adeptx.cmd.exception" AND unique_key="%s" OR ru="%s" limit 1',
							$db->escape($_SESSION['lang']),
							$database['prefix'] . $database['table']['phrase'],
							$db->escape($e->getCode()),
							$db->escape($e->getMessage())
						);
						$db->call($query);
						if ($res) {
							$res = $db->fetch_assoc($res);
						}

						$msg = $res[0][ $_SESSION['lang'] ];
						if (!$msg) {
							$msg = $e->getMessage();
						}
						$error->report($msg, __LINE__, '`' . (($true_name)?$true_name:$command_name) . '` Command Exception', __FILE__, $e->getCode());
					}
				} else {
					$result .= "RU: Комманда \"$command_name\" не существует или её файл ($command_directory$cmd_file) переименован, перемещён, недоступен, скрыт настройками приватности или повреждён. Воспользуйтесь командой help чтобы узнать список доступных команд.\nEN: Command \"$command_name\" not useful for this site/profile, command-package not install, rename or moved. Use command \"help\" for access commands list.";

					// var_dump($cmds);
					// var_dump($argv);

					if (is_readable($GLOBALS['fold']['cmd'] . 'error.log' . $GLOBALS['site']['extentions'])) {
						$err_log = fopen($GLOBALS['fold']['cmd'] . 'error.log' . $GLOBALS['site']['extentions'], 'a');
						fwrite($err_log, $command_vs_args . "\n");
						fclose($err_log);
					}
				}
			}
			# no-no-no, ни за что!
			// if (substr($result, -1) != "\n") {
			// 	$result .= "\n";
			// }
		}

		$result = 	'<div id="cmd-' . $_SESSION['cmds_counter'] . '-result-output">' .
						ob_get_contents() . $result .
					'</div>';

		# только для почты, вот эту часть категорически надо переписать (не только из-за трафика, но мы ещё и при каждом запросе смотрим "а не почтой ли интересуются", а надо чтобы нам говорили "покажите почту", а до того не шевелиться)
		if ($_POST['cmd'] == 'select mail') {
			$result['#user-new-messages'] = $result;		# так мы посылаем одни и те же данные дважды, нагружая траффик в двойной мере! нужно предпринять что-то, чтобы не дублировать данные
		}
		ob_end_clean();

		return $result;
	}
Example #2
0
	function get($argv, $argc) {
		global $fold;

		$object = $argv[1];
		// $epitet = $argv[2];	# $epitet = current, new, last etc
		$property = $argv[2];

		if (isset($argv['s'])) {
			$property = $argv['s'];
		}
		if (isset($argv['session'])) {
			$property = $argv['session'];
		}

		switch ($object) {
			case 'my':
			case 'current':
			case 'user':
				switch ($property) {
					case 'version':
						return '0.732';
					case 'mail':
						return run('select mail');
				}
				if (!isset($_SESSION[$property])) {
					echo $property;
					throw new Exception('Значение свойства не установлено. Возможно также, что такого свойства вообще нет у объекта. Возможно даже, что такого объекта вообще не существует.', 5796);
					# id, nickname, email, msg, timezone, new_mail_count, mail
				}
				return $_SESSION[$property];
				break;
			case 'bd':
			case 'db':
			case 'sql':
			case 'new':
			case 'mysql':
				switch ($property) {
					case 'dump':
						$return .= run('dump');

						customization('default');

						$dumps = glob($fold['sql'] . "*.sql", GLOB_MARK | GLOB_NOESCAPE);
						$dumps_count = count($dumps);

						if (!$dumps_count) {
							throw new Exception('Ошибка создания и открытия дампа, нет прав?', 5797);
						}
						
						$return .= file_get_contents($dumps[count($dumps) - 1]);

						customization('user');

						$return .= $fileContents;
						return $return;
						break;
				}
				break;
			case 'last':
			case 'latest':
				switch ($property) {
					case 'dump':
						customization('default');

						$dumps_list = glob($fold['sql'] . "*.sql", GLOB_MARK | GLOB_NOESCAPE);
						$last_dump = array_pop($dumps_list);

						if (!$last_dump) {
							throw new Exception('В папке хранения дампов не обнаружено ни одного дампа базы данных', 4978);
						}
						
						$return .= file_get_contents($last_dump);

						customization('user');

						$return .= $fileContents;
						return $return;
						break;
					case 'version':
						return '0.732';
						break;
				}
				break;
			case 'var':
				if (!isset($_SESSION['var'][ $property ])) {
					return '\\0';
				}
				return $_SESSION['var'][ $property ];
			default:
				switch ($property) {
					case 'code':
						$real_path  = str_replace(['../', '..'], '', $fold['cmd'] . $object . $site['extensions']);
						if (is_readable($real_path)) {
							$file = htmlspecialchars(file_get_contents($real_path));
							$return .= "Исходный код программы $object:\n $file";
						} else {
							$return .= "RU: Программа \"$file\" не обнаружена.";
						}
				}
				break;
		}
		return $return;
	}
Example #3
0
<?
	/**
	 * Дамп базы данных
	 *
	 **/
	
	set_time_limit(0);

	customization('default');
	return dump($argv, $argc);
	customization('user');		# это необходимо, потому что если подряд выполняется несколько команд, необходимо вернуться к текущему состоянию
	
	function dump($argv, $argc) {
		global $fold, $database, $site;

		$fix = $argv[1];
		
		$datestamp = date("Y-m-d_H-i-s");

		$dumpFileName = /* $page['base']['href'] . */ $fold['sql'] . $database['name'] . "_$datestamp.sql";
		$connection = mysql_connect($database['host'], $database['user'], $database['pass']);
		if (!$connection) {
			return "Святые одуванчики! У нас проблемы!";
		}

		include_once $fold['classes'] . 'MySQLDump' . $site['extensions'];
		$dumper = new MySQLDump($database['name'], $dumpFileName, false, false);
		mysql_query("set names utf8");
		$dumper->doDump();

		if ($fix) {