diff options
-rw-r--r-- | CONTRIBUTORS | 6 | ||||
-rw-r--r-- | COPYING | 18 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | pluginpackage.php | 288 | ||||
-rwxr-xr-x | r7r-plugin-packer.php | 101 |
5 files changed, 417 insertions, 0 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 0000000..26812dd --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,6 @@ +People who have worked on r7r-plugin-packer +=========================================== + +If you modified something, feel free to append your name to this list. + +* Kevin Chabowski <kevin@kch42.de> @@ -0,0 +1,18 @@ +r7r-plugin-packer: Copyright (c) 2012 The Ratatöskr Team + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e3607a7 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +r7r-plugin-packer.php +===================== + +A Command line tool for packing a plugin for Ratatöskr.
\ No newline at end of file diff --git a/pluginpackage.php b/pluginpackage.php new file mode 100644 index 0000000..cf667cc --- /dev/null +++ b/pluginpackage.php @@ -0,0 +1,288 @@ +<?php + +/* + * File: ratatoeskr/sys/pluginpackage.php + * Handle plugin packages easily. + * + * License: + * This file is part of Ratatöskr. + * Unlike the other parts of Ratatöskr, *this* file ist *not* licensed under the + * MIT / X11 License, but under the WTFPL, to make it even easier to use this + * file in other projects. + * See "ratatoeskr/licenses/wtfpl" for more information. + */ + +/* + * Function: dir2array + * Pack a directory into an array. + * + * Parameters: + * $dir - The directory to pack. + * + * Returns: + * Associative array. Keys are filenames, values are either the file's content as a string or another array, if it's a directory. + */ +function dir2array($dir) +{ + $rv = array(); + foreach(scandir($dir) as $fn) + { + if(($fn == ".") or ($fn == "..")) + continue; + $fn_new = $dir . "/" . $fn; + if(is_dir($fn_new)) + $rv[$fn] = dir2array($fn_new); + elseif(is_file($fn_new)) + $rv[$fn] = file_get_contents($fn_new); + } + return $rv; +} + +/* + * Function: array2dir + * Unpack an array into a directory. + * + * Parameters: + * $a - Array to unpack. + * $dir - Directory to unpack to. + */ +function array2dir($a, $dir) +{ + if(!is_dir($dir)) + mkdir($dir); + + foreach($a as $k => $v) + { + $k = "$dir/$k"; + if(is_array($v)) + array2dir($v, $k); + else + file_put_contents($k, $v); + } +} + +function validate_url ($u) { return preg_match("/^http[s]{0,1}:\\/\\/.*$/", $u) != 0; } +function validate_arraydir($a) +{ + if(!is_array($a)) + return False; + foreach($a as $k=>$v) + { + if(!is_string($k)) + return False; + if(is_array($v) and (!validate_arraydir($v))) + return False; + elseif(!is_string($v)) + return False; + } + return True; +} + +/* + * Class: InvalidPackage + * An Exception that <PluginPackage>'s function can throw, if the package is invalid. + */ +class InvalidPackage extends Exception {} + +/* + * Class: PluginPackage + * A plugin package representation. + */ +class PluginPackage +{ + public static $magic = "R7RPLGPACKV001"; + + /* + * Variables: Mandatory values + * + * $code - The plugin code + * $classname - The name of the plugins main class + * $name - Name of the plugin (must be at least one character, allowed chars: a-z A-Z 0-9 - _) + * $author - The author of the plugin (preferably in the format: Name<mail@address>) + * $versiontext - A text to describe the current version, something like "1.1 Beta" + * $versioncount - A number for this version, should be increased with every release + * $api - The used API version + * $short_description - A short description. + */ + public $code = NULL; + public $classname = NULL; + public $name = NULL; + public $author = NULL; + public $versiontext = NULL; + public $versioncount = NULL; + public $api = NULL; + public $short_description = NULL; + + /* + * Variables: Optional values + * + * $updatepath - A URL that points to a update information resource (serialize'd array("current-version" => VERSIONCOUNT, "dl-path" => DOWNLOAD PATH); will get overwritten/set by the default repository software. + * $web - A URL to the webpage for the plugin. If left empty, the default repository software will set this to the description page of your plugin. + * $license - The license text of your plugin. + * $help - A help / manual (formatted in HTML) for your plugin. + * $custompub - <dir2array> 'd directory that contains custom public(i.e. can later be accessed from the web) data. + * $custompriv - <dir2array> 'd directory that contains custom private data. + * $tpls - <dir2array> 'd directory containing custom STE templates. + */ + public $updatepath = NULL; + public $web = NULL; + public $license = NULL; + public $help = NULL; + public $custompub = NULL; + public $custompriv = NULL; + public $tpls = NULL; + + /* + * Function: validate + * Validate, if the variables are set correctly. + * Will throw an <InvalidPackage> exception if invalid. + */ + public function validate() + { + if(!is_string($this->code)) + throw new InvalidPackage("Invalid code value."); + if(!is_string($this->classname)) + throw new InvalidPackage("Invalid classname value."); + if(preg_match("/^[a-zA-Z0-9_\\-]+$/", $this->name) == 0) + throw new InvalidPackage("Invalid name value (must be at least 1 character, accepted chars: a-z A-Z 0-9 - _)."); + if(!is_string($this->author)) + throw new InvalidPackage("Invalid author value."); + if(!is_string($this->versiontext)) + throw new InvalidPackage("Invalid versiontext value."); + if(!is_numeric($this->versioncount)) + throw new InvalidPackage("Invalid versioncount value. Must be a number."); + if(!is_numeric($this->api)) + throw new InvalidPackage("Invalid api value. Must be a number."); + if(!is_string($this->short_description)) + throw new InvalidPackage("Invalid short_description value."); + + if((!empty($this->updatepath)) and (!validate_url($this->updatepath))) + throw new InvalidPackage("Invalid updatepath value. Must be an URL. " .$this->updatepath); + if((!empty($this->web)) and (!validate_url($this->web))) + throw new InvalidPackage("Invalid web value. Must be an URL."); + if(($this->license !== NULL) and (!is_string($this->license))) + throw new InvalidPackage("Invalid license value."); + if(($this->help !== NULL) and (!is_string($this->help))) + throw new InvalidPackage("Invalid help value."); + if(($this->custompub !== NULL) and (!validate_arraydir($this->custompub))) + throw new InvalidPackage("Invalid custompub value."); + if(($this->custompriv !== NULL) and (!validate_arraydir($this->custompriv))) + throw new InvalidPackage("Invalid custompriv value."); + if(($this->tpls !== NULL) and (!validate_arraydir($this->tpls))) + throw new InvalidPackage("Invalid tpls value."); + return True; + } + + /* + * Function: load + * Load a plugin package from binary data. + * + * Parameters: + * $plugin_raw - The raw package to load. + * + * Returns: + * The <PluginPackage> object. + * + * Throws: + * <InvalidPackage> if package is invalid. + */ + public static function load($plugin_raw) + { + /* Read and compare magic number */ + $magic = substr($plugin_raw, 0, strlen(self::$magic)); + if($magic != self::$magic) + throw new InvalidPackage("Wrong magic number"); + + /* Read sha1sum and uncompress serialized plugin, then compare the hash */ + $sha1sum = substr($plugin_raw, strlen(self::$magic), 20); + $pluginser = gzuncompress(substr($plugin_raw, strlen(self::$magic) + 20)); + if(sha1($pluginser, True) != $sha1sum) + throw new InvalidPackage("Wrong SHA1 hash"); + + $plugin = @unserialize($pluginser); + if(!($plugin instanceof self)) + throw new InvalidPackage("Not the correct class or not unserializeable."); + + $plugin->validate(); + + return $plugin; + } + + /* + * Function: save + * Save the plugin. + * + * Returns: + * A binary plugin package. + * + * Throws: + * <InvalidPackage> if package is invalid. + */ + public function save() + { + $this->validate(); + $ser = serialize($this); + return self::$magic . sha1($ser, True) . gzcompress($ser, 9); + } + + /* + * Function: extract_meta + * Get just the metadata of this package. + * + * Returns: + * A <PluginPackageMeta> object. + */ + public function extract_meta() + { + $meta = new PluginPackageMeta(); + + $meta->name = $this>name; + $meta->author = $this>author; + $meta->versiontext = $this>versiontext; + $meta->versioncount = $this>versioncount; + $meta->api = $this>api; + $meta->short_description = $this>short_description; + $meta->updatepath = $this>updatepath; + $meta->web = $this>web; + $meta->license = $this>license; + + return $meta; + } +} + +/* + * Class: PluginPackageMeta + * Only the metadata of a <PluginPackage>. + */ +class PluginPackageMeta +{ + /* + * Variables: Mandatory values + * + * $name - Name of the plugin (must be at least one character, allowed chars: a-z A-Z 0-9 - _) + * $author - The author of the plugin (preferably in the format: Name<mail@address>) + * $versiontext - A text to describe the current version, something like "1.1 Beta" + * $versioncount - A number for this version, should be increased with every release + * $api - The used API version + * $short_description - A short description. + */ + public $name = NULL; + public $author = NULL; + public $versiontext = NULL; + public $versioncount = NULL; + public $api = NULL; + public $short_description = NULL; + + /* + * Variables: Optional values + * + * $updatepath - A URL that points to a update information resource (serialize'd array("current-version" => VERSIONCOUNT, "dl-path" => DOWNLOAD PATH); will get overwritten/set by the default repository software. + * $web - A URL to the webpage for the plugin. If left empty, the default repository software will set this to the description page of your plugin. + * $license - The license text of your plugin. + */ + public $updatepath = NULL; + public $web = NULL; + public $license = NULL; +} + +?> diff --git a/r7r-plugin-packer.php b/r7r-plugin-packer.php new file mode 100755 index 0000000..6baef7a --- /dev/null +++ b/r7r-plugin-packer.php @@ -0,0 +1,101 @@ +#!/usr/bin/env php +<?php + +/* + * File: r7r-plugin-packer.php + * Packaging a Ratatöskr plugin. + */ + +require_once(dirname(__FILE__) . "/pluginpackage.php"); + +/* Parse options */ +$options = getopt("", array( + "codefile:", + "classname:", + "pluginname:", + "author:", + "versiontext:", + "versioncount:", + "updatepath:", + "web:", + "api:", + "licensefile:", + "helpfile:", + "shortdesc:", + "custompub:", + "custompriv:", + "tpldir:", + "output:" +)); + +$usage = <<<USAGE +Usage: +${argv[0]} options... + +Mandatory options: + --output=FILE Where should the output be saved? + --codefile=FILE The PHP file with the plugin code. + --classname=CLASS The name of the RatatoeskrPlugin implementation. + --pluginname=NAME The name of the plugin (it is recommended to use some kind of developer prefix to make the name more unique). + --author=AUTHOR Your name (preferably in the form: My Name<my-mail-address@example.com>). + --versiontext=VER A short text, that describes this version (something like: 1.0 beta). + --versioncount=C A number that increases with every release. + --api=APIVER The version number of the plugin API. + --shortdesc=DESC A short description of your plugin. You can use #hashtags. + +Optional options: + --updatepath=URL A URL where Ratatöskr can check, if there is a new version (URL should point to a serialize()'d array("current-version" => VERSIONCOUNT, "dl-path" => DOWNLOAD PATH); Will get overwritten by the default repository software). + --web=HOMEPAGE Homepage of the Plugin. + --licensefile=FILE Should a license be included? + --helpfile=FILE A HTML file that acts as a help/manual for your plugin. + --custompub=DIR Directory that contains custom public(i.e. can later be accessed from the web) data. + --custompriv=DIR Directory that contains custom private data. + --tpldir=DIR Directory that contains templates used by this plugin. +USAGE +; + +if(!(isset($options["output"]) and isset($options["codefile"]) and isset($options["classname"]) and isset($options["pluginname"]) and isset($options["author"]) and isset($options["versiontext"]) and isset($options["versioncount"]) and isset($options["api"]) and isset($options["shortdesc"]))) +{ + fprintf(STDERR, "Missing options\n\n" . $usage); + exit(1); +} + +$code = file_get_contents($options["codefile"]); +if($code === FALSE) +{ + fprintf(STDERR, "Can not open '${options['codefile']}'.\n"); + exit(1); +} + +/* Remove trailing <?php ?> delimiters */ +$code = preg_replace("/^\\<\\?php(.*)\\?\\>\\s*?$/s", "\\1", $code); + +$plugin = new PluginPackage(); + +$plugin->code = $code; +$plugin->classname = $options["classname"]; +$plugin->name = $options["pluginname"]; +$plugin->author = $options["author"]; +$plugin->versiontext = $options["versiontext"]; +$plugin->versioncount = $options["versioncount"]; +$plugin->api = $options["api"]; +$plugin->short_description = $options["shortdesc"]; + +if(isset($options["updatepath"])) + $plugin->updatepath = $options["updatepath"]; +if(isset($options["web"])) + $plugin->web = $options["web"]; +if(isset($options["licensefile"])) + $plugin->license = @file_get_contents($options["licensefile"]); +if(isset($options["helpfile"])) + $plugin->help = @file_get_contents($options["helpfile"]); +if(isset($options["custompub"])) + $plugin->custompub = dir2array($options["custompub"]); +if(isset($options["custompriv"])) + $plugin->custompriv = dir2array($options["custompriv"]); +if(isset($options["tpldir"])) + $plugin->tpls = dir2array($options["tpldir"]); + +file_put_contents($options["output"], $plugin->save()); + +?> |