diff --git a/Makefile b/Makefile index 0f18b7a..1dd3779 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,3 @@ .phony: phar phar: - makephar -c makephar.conf + ./makephar -c makephar.conf diff --git a/bin/bootstrap.php b/bin/bootstrap.php index b3fbe85..6fefdcc 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -2,6 +2,8 @@ require_once __DIR__."/../vendor/autoload.php"; require_once __DIR__."/systemtest.php"; +if (file_exists(__DIR__."/banner.php")) + require_once __DIR__."/banner.php"; use NoccyLabs\Hotfix\HotfixApplication; diff --git a/bin/hotfix b/bin/hotfix old mode 100644 new mode 100755 diff --git a/makephar b/makephar new file mode 100755 index 0000000..ebb2ac9 Binary files /dev/null and b/makephar differ diff --git a/src/Command/ApplyCommand.php b/src/Command/ApplyCommand.php index d519a65..cc0cfef 100644 --- a/src/Command/ApplyCommand.php +++ b/src/Command/ApplyCommand.php @@ -9,6 +9,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\ChoiceQuestion; use NoccyLabs\Hotfix\Hotfix\Loader; class ApplyCommand extends Command @@ -20,6 +21,7 @@ class ApplyCommand extends Command $this->setDescription("Apply a hotfix from a file or the Internet"); $this->addOption("insecure", "I", InputOption::VALUE_NONE, "Disable signature checks. Don't use unless you know what you are doing!"); + $this->addOption("preview", "p", InputOption::VALUE_NONE, "Only preview the hotfix script, don't apply anything"); $this->addArgument("hotfix", InputArgument::OPTIONAL, "The identifier, path or filename of the hotfix"); } @@ -30,10 +32,20 @@ class ApplyCommand extends Command $fix = $input->getArgument("hotfix"); $insecure = $input->getOption("insecure"); + $loader = new Loader(); + + if (!$fix) { + + $loaders = $loader->getLoaders(); + $output->writeln("Supported loaders:\n"); + foreach ($loaders as $loader) { + $output->writeln(" * ".$loader->getInfo()); + } + return; + } $output->writeln("Reading hotfix {$fix}..."); - $loader = new Loader(); try { $hotfix = $loader->load($fix, $insecure); } catch (\Exception $e) { @@ -47,20 +59,27 @@ class ApplyCommand extends Command $output->writeln(""); if (($signer = $hotfix->getSignedBy())) { - $output->writeln("Hotfix has good signature by {$signer}"); + $keyid = $hotfix->getKeyId(); + $output->writeln("Hotfix has good signature from {$signer} (keyid {$keyid})"); } else { $output->writeln("Warning: Hotfix is not signed or signature not valid!"); } $output->writeln(""); - $output->writeln(" Hotfix: ".$hotfix->getName().""); + $output->writeln(" Hotfix: ".$hotfix->getName().""); $output->writeln(" Author: ".$hotfix->getAuthor().""); - $output->writeln(" Info:"); - $info = explode("\n",wordwrap(trim($hotfix->getInfo()), 70)); - foreach ($info as $line) { - $output->writeln(" | {$line}"); - } + $info = explode("\n",wordwrap(trim($hotfix->getInfo()), 60)); + $info = "".join("\n ", $info).""; + $output->writeln(" Info: ".$info); $output->writeln(""); + if ($input->getOption("preview")) { + $output->writeln("This is the script that will be executed:"); + $body = $hotfix->getBody(); + $body = " ".join("\n ", explode("\n",$body)).""; + $output->writeln($body); + return; + } + $helper = $this->getHelper("question"); $question = new ConfirmationQuestion("Do you want to apply this hotfix? [y/N] ", false); diff --git a/src/Hotfix/Hotfix.php b/src/Hotfix/Hotfix.php index 1fd5ed6..c827142 100644 --- a/src/Hotfix/Hotfix.php +++ b/src/Hotfix/Hotfix.php @@ -9,6 +9,8 @@ class Hotfix protected $signer; + protected $keyId; + protected $header; protected $body; @@ -16,7 +18,8 @@ class Hotfix public function __construct($hotfix, $signer) { $this->load($hotfix); - $this->signer = $signer; + $this->signer = $signer[0]; + $this->keyId = $signer[1]; } protected function load($hotfix) @@ -31,6 +34,11 @@ class Hotfix $this->body = $body; } + public function getBody() + { + return $this->body; + } + public function apply() { if (!array_key_exists('lang', $this->header)) { @@ -40,9 +48,12 @@ class Hotfix } $script = null; + $head = null; + $foot = null; switch ($lang) { case 'bash': $exec = "/bin/bash"; + $head = file_get_contents(__DIR__."/../stubs/bash.stub"); break; case 'php': $exec = "/usr/bin/env php"; @@ -53,7 +64,7 @@ class Hotfix } $tmpfile = sys_get_temp_dir()."/hotfix_".sha1($this->header['hotfix']); - file_put_contents($tmpfile, $this->body); + file_put_contents($tmpfile, $head."\n".$this->body."\n".$foot); passthru($exec." ".$tmpfile); unlink($tmpfile); } @@ -68,6 +79,11 @@ class Hotfix $this->signer['uids'][0]['email'] ); } + + public function getKeyId() + { + return $this->keyId; + } public function getName() { diff --git a/src/Hotfix/Loader.php b/src/Hotfix/Loader.php index be1ea47..ace3013 100644 --- a/src/Hotfix/Loader.php +++ b/src/Hotfix/Loader.php @@ -22,6 +22,11 @@ class Loader $this->loaders[] = $loader; } + public function getLoaders() + { + return $this->loaders; + } + public function load($fix, $insecure=false) { foreach ($this->loaders as $loader) { @@ -56,12 +61,22 @@ class Loader throw new \Exception("Hotfix signature is not valid!"); } - $keyInfo = gnupg_keyinfo($gpg, $sigInfo[0]['fingerprint']); + $fingerprint = $sigInfo[0]['fingerprint']; + $keyInfo = gnupg_keyinfo($gpg, $fingerprint); + + $subKeys = $keyInfo[0]['subkeys']; + $keyId = null; + foreach ($subKeys as $subKey) { + if ($subKey['fingerprint'] == $fingerprint) { + $keyId = $subKey['keyid']; + break; + } + } if (empty($keyInfo)) { throw new \Exception("Unknown signer (key id {$sigInfo[0]['fingerprint']})"); } - return $keyInfo[0]; + return [ $keyInfo[0], $keyId ]; } } diff --git a/src/Hotfix/Loader/FileLoader.php b/src/Hotfix/Loader/FileLoader.php index b70b3f7..4240525 100644 --- a/src/Hotfix/Loader/FileLoader.php +++ b/src/Hotfix/Loader/FileLoader.php @@ -15,4 +15,9 @@ class FileLoader implements LoaderInterface } return false; } + + public function getInfo() + { + return "filename - Read a hotfix from a file"; + } } diff --git a/src/Hotfix/Loader/GistLoader.php b/src/Hotfix/Loader/GistLoader.php index c9d13c9..85b099e 100644 --- a/src/Hotfix/Loader/GistLoader.php +++ b/src/Hotfix/Loader/GistLoader.php @@ -15,4 +15,9 @@ class GistLoader implements LoaderInterface return file_get_contents($url); } + + public function getInfo() + { + return "gist:user/gist-id - Read hotfix from a Gist"; + } } diff --git a/src/Hotfix/Loader/HttpLoader.php b/src/Hotfix/Loader/HttpLoader.php index a78dee3..7383b74 100644 --- a/src/Hotfix/Loader/HttpLoader.php +++ b/src/Hotfix/Loader/HttpLoader.php @@ -12,4 +12,9 @@ class HttpLoader implements LoaderInterface } return false; } + + public function getInfo() + { + return "http(s)://url - Read hotfix from a URL"; + } } diff --git a/src/Hotfix/Loader/PastebinLoader.php b/src/Hotfix/Loader/PastebinLoader.php index a6ddf71..43c5d3e 100644 --- a/src/Hotfix/Loader/PastebinLoader.php +++ b/src/Hotfix/Loader/PastebinLoader.php @@ -20,4 +20,9 @@ class PastebinLoader implements LoaderInterface return $body; } + + public function getInfo() + { + return "pastebin:paste-id - Read hotfix from Pastebin"; + } } diff --git a/src/stubs/bash.stub b/src/stubs/bash.stub new file mode 100644 index 0000000..a43f9a2 --- /dev/null +++ b/src/stubs/bash.stub @@ -0,0 +1,16 @@ +test -e /etc/lsb-release && source /etc/lsb-release + +function exec() { + echo -e "\e[36;1m[exec]\e[36;21m $*\e[0m" + LOG=$(tempfile -p exec) + $@ &>$LOG + EC=$? + if [ $EC -gt 0 ]; then + echo -e "\e[31;1m[warn]\e[31;21m Command completed with exitcode $EC\e[0m" + cat $LOG + fi + rm $LOG +} +function info() { + echo -e "\e[32;1m[info]\e[32;21m $*\e[0m" +}