[Из песочницы] Управляем компьютером через браузер

Компьютер давно многим заменил телевизор, а что не хватает компьютеру для комфортного просмотра кино, сериалов и прочего? Мне лично не хватало пульта дистанционного управления.
image

Я не много увлекаюсь веб-программированием в свободное время. Было решено написать свой велосипед и заодно совместить приятное с полезным. Была изучена возможность с помощью консоли управлять компьютером, в первую очередь меня интересовал следующий минимальный объем задач (который немного увеличился в последствии):
  • Возможность отправить ПК в режим сна
  • Возможность выключить громкость

С помощью известного поисковика нашел консольную программу, NirCmd, программа позволяет осуществлять достаточно много действий кроме указанных выше. С основной частью мы определились. Как я говорил, не много изучаю php, веб-сервер у меня как правило запущен постоянно, поэтому ничего специфичного в плане сервера я придумывать не стал. Сервер написан на php и состоит из двух классов: Control, который представляет собой методы которые исполняют консольные команды и Route, который делает валидацию приходящих запросов и исполняет методы класса Control.

Control выгладит вот так (код однотипный, поэтому я сократил):

class Control implements ActionControl {
		protected $Path; //путь до программы nirCmd;
		
		function __construct($p = 'C:/nircmd/nircmd.exe') {
			$this->Path = $p;
		}
		function standby() {
			`{$this->Path} standby`;
		}
		function hibernate() {
			`rundll32 powrprof.dll,SetSuspendState 0,1,0`;
		}
		function reboot() {
			`{$this->Path} exitwin reboot`;
		}
		function turnOff() {
			`{$this->Path} exitwin poweroff`;
		}
		function logout() {
			`{$this->Path} exitwin logoff`;
		}
}

Второй класс, — Route. Собственно к нему обращается клиент. Класс, в первую очередь осуществляет валидацию запроса, и если запрос правильный вызывает методы класса Control.
Выглядит это так:
class Route implements ActionRoute {
		protected $possible = []; 		
		protected $ControlObj;
	
		function __construct($obj) {
			$this->possible = get_class_methods($obj); 
			$this->ControlObj = $obj;
		}
		function route($arr) { 
			forEach($arr as $key => $value) {	
		   if (in_array($key, $this->possible) && $value == 'true') {
		   	$this->execute($key);
		   } else 
		   	{
		   		Message::sent('wrong method');
		   	}	
			}
		}
		function execute($c) {
			$this->ControlObj->$c();
			Message::sent('executed');
		}
	}

Чтобы получилось в случае чего легко расширить количество методов в классе Control, валидация в классе Route жестко не привязана к определенному списку методов класса Control, точнее привязана, но все возможные методы извлекаются из самого объекта, с ними и идет сравнение пришедших данных.

Сам файл к которому обращается клиент выглядит вот так:

route($_GET);
	}
?>

Если приходит GET запрос, то весь массив отдаем методу route.

Клиентская часть представляет собой кнопки управления и один обработчик, по которому мы шлем данные при помощи ajax на сервер.

var wrapper = document.querySelector(".wrapper");

function getXmlHttpRequest(){
	if (window.XMLHttpRequest) {
		try {
			return new XMLHttpRequest();
		} catch (e){ }
	} else if (window.ActiveXObject) {
		try {
			return new ActiveXObject('Msxml2.XMLHTTP');
		} catch (e){}
		try {
			return new ActiveXObject('Microsoft.XMLHTTP');
		} catch (e){}
		}
	return null;
}

wrapper.addEventListener('click', function(e){
	var target = e.target;

	if(target.tagName!= 'BUTTON') return;
	if (target.getAttribute('data')=='qestion') {
		var yn = confirm("Уверен?");
		if (!yn) return
			ajaxf(target.id);
	}
	ajaxf(target.id);
})

	function ajaxf($com) {
	 	var command = $com+'=true';
		var xhr = new getXmlHttpRequest();
		xhr.open('GET', 'remote.php?'+command, true);
		xhr.send();
		xhr.onreadystatechange = function() { 
  			if (xhr.readyState != 4) return;
  			if (xhr.status != 200) {
    			console.log(xhr.status + ': ' + xhr.statusText);
  			} else {
   	 		console.log(xhr.responseText);
  			}
		}
	}

На сегодня реализован следующий функционал управления:
  • спящий режим
  • гибернация
  • перезагрузится
  • выключить
  • выйти
  • выключить монитор
  • включить/отключить звук по триггеру
  • громкость больше
  • громкость меньше
  • Медиа кнопки, stop/play next/prev

Все, что я хотел получить, получил, но, как говорится, аппетит приходит во время еды. Хотелось бы иметь обратную связь от сервера, т.е. при первом заходе на страницу делать запрос текущего уровня громкости например. К сожалению, nirCmd не возвращает значения при выполнении, поэтому с тем, что имеем сейчас, я не знаю, как это осуществить.

Вопрос безопасности. Хотя это все и крутится на локальной машине, будет очень печально, если доступ к исполнению команд nirCmd будет у злоумышленника, т.к. можно сотворить много деструктивных действий.

Готовый проект на GIthab.

Комментарии (0)

© Habrahabr.ru