diff options
Diffstat (limited to 'ratatoeskr')
-rw-r--r-- | ratatoeskr/licenses/wtfpl | 13 | ||||
-rw-r--r-- | ratatoeskr/setup/create_tables.php | 9 | ||||
-rw-r--r-- | ratatoeskr/sys/models.php | 224 | ||||
-rw-r--r-- | ratatoeskr/sys/pluginpackage.php | 265 |
4 files changed, 511 insertions, 0 deletions
diff --git a/ratatoeskr/licenses/wtfpl b/ratatoeskr/licenses/wtfpl new file mode 100644 index 0000000..8b1a9d8 --- /dev/null +++ b/ratatoeskr/licenses/wtfpl @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/ratatoeskr/setup/create_tables.php b/ratatoeskr/setup/create_tables.php index 1be8855..8405ade 100644 --- a/ratatoeskr/setup/create_tables.php +++ b/ratatoeskr/setup/create_tables.php @@ -125,6 +125,15 @@ CREATE TABLE `PREFIX_users` ( `language` varchar(10) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +CREATE TABLE `s_db_47`.`PREFIX_repositories` ( +`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , +`baseurl` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , +`name` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , +`description` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , +`pkgcache` TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL , +`lastrefresh` BIGINT NOT NULL +) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_unicode_ci; SQL; ?> diff --git a/ratatoeskr/sys/models.php b/ratatoeskr/sys/models.php index c3e112c..2dadb22 100644 --- a/ratatoeskr/sys/models.php +++ b/ratatoeskr/sys/models.php @@ -13,6 +13,7 @@ require_once(dirname(__FILE__) . "/db.php"); require_once(dirname(__FILE__) . "/utils.php"); require_once(dirname(__FILE__) . "/../libs/kses.php"); require_once(dirname(__FILE__) . "/textprocessors.php"); +require_once(dirname(__FILE__) . "/pluginpackage.php"); db_connect(); @@ -1795,6 +1796,229 @@ class Image } /* + * Class RepositoryUnreachableOrInvalid + * A Exception that will be thrown, if the repository is aunreachable or seems to be an invalid repository. + */ +class RepositoryUnreachableOrInvalid extends Exception { } + +/* + * Class: Repository + * Representation of an plugin repository. + */ +class Repository +{ + private $id; + private $baseurl; + private $name; + private $description; + private $lastrefresh; + + private $stream_ctx; + + /* + * Variables: Public class variables + * $packages - Array with all packages from this repository. A entry itself is an array: array(name, versioncounter, description) + */ + public $packages; + + private function __construct() + { + $this->stream_ctx = stream_context_create(array("http" => array("timeout" => 5))); + } + + /* + * Functions: Getters + * get_id - Get internal ID. + * get_baseurl - Get the baseurl of the repository. + * get_name - Get repository name. + * get_description - Get repository description. + */ + public function get_id() { return $this->id; } + public function get_baseurl() { return $this->baseurl; } + public function get_name() { return $this->name; } + public function get_description() { return $this->description; } + + /* + * Constructor: create + * Create a new repository entry from a base url. + * + * Parameters: + * $baseurl - The baseurl of the repository. + * + * Throws: + * Could throw a <RepositoryUnreachableOrInvalid> exception. In this case, nothing will be written to the database. + * + * Returns: + * A <Repository> object. + */ + public static function create($baseurl) + { + $obj = new self(); + + if(preg_match('/^(http[s]?:\\/\\/.*?)[\\/]?$/', $baseurl, $matches) == 0) + throw new RepositoryUnreachableOrInvalid(); + + $obj->baseurl = $$matches[1]; + $obj->refresh(True); + + qdb("INSERT INTO `ratatoeskr_repositories` () VALUES ()"); + $obj->id = mysql_insert_id(); + $obj->save(); + return $obj; + } + + /* + * Constructor: by_id + * Get a repository entry by ID. + * + * Parameters: + * $id - ID. + * + * Returns: + * A <Repository> object. + */ + public static function by_id($id) + { + $result = qdb("SELECT `name`, `description`, `baseurl`, `pkgcache`, `lastrefresh` WHERE id` = %d", $this->id); + $sqlrow = mysql_fetch_assoc($result); + if(!$sqlrow) + throw new DoesNotExistError(); + + $obj = new self(); + $obj->id = $id; + $obj->name = $sqlrow["name"]; + $obj->description = $sqlrow["description"]; + $obj->baseurl = $sqlrow["baseurl"]; + $obj->packages = unserialize(base64_decode($sqlrow["pkgcache"])); + $obj->lastrefresh = $sqlrow["lastrefresh"]; + + return $obj; + } + + private function save() + { + qdb("UPDATE `PREFIX_repositories` SET `baseurl` => '%s', `name` = '%s', `description` = '%s', `pkgcache` = '%s', `lastrefresh` = %d WHERE `id` = %d", + $this->baseurl, + $this->name, + $this->description, + base64_encode(serialize($this->packages)), + $this->lastrefresh, + $this->id); + } + + /* + * Function: delete + * Delete the repository entry from the database. + */ + public function delete() + { + qdb("DELETE FROM `PREFIX_repositories` WHERE `id` = %d", $this->id); + } + + /* + * Function: refresh + * Refresh the package cache and the name and description. + * + * Parameters: + * $force - Force a refresh, even if the data was already fetched in the last 6 hours (default: False). + * + * Throws: + * <RepositoryUnreachableOrInvalid> + */ + public function refresh($force = False) + { + if(($this->lastrefresh > (time() - (60*60*4))) and (!$force)) + return; + + $repometa = @file_get_contents($this->baseurl . "/repometa", False, $this->stream_ctx); + if($repometa === FALSE) + throw new RepositoryUnreachableOrInvalid(); + $repometa = @unserialize($repometa); + if((!is_array($repometa)) or (!isset($repometa["name"])) or (!isset($repometa["description"]))) + throw new RepositoryUnreachableOrInvalid(); + + $this->name = $repometa["name"]; + $this->description = $repometa["description"]; + $this->packages = @unserialize(@file_get_contents($this->baseurl . "/packagelist", False, $ctx)); + + $this->lastrefresh = time(); + + $this->save(); + } + + /* + * Function: get_package_meta + * Get metadata of a plugin package from this repository. + * + * Parameters: + * $pkgname - The name of the package. + * + * Throws: + * A <DoesNotExistError> Exception, if the package was not found. + * + * Returns: + * A <PluginPackageMeta> object + */ + public function get_package_meta($pkgname) + { + $found = False; + foreach($this->packages as $p) + { + if($p[0] == $pkgname) + { + $found = True; + break; + } + } + if(!$found) + throw new DoesNotExistError("Package not in package cache."); + + $pkgmeta = @unserialize(@file_get_contents($this->baseurl . "/packages/" . urlencode($pkgname) . "/meta", False, $this->stream_ctx)); + + if(!($pkgmeta instanceof PluginPackageMeta)) + throw new DoesNotExistError(); + + return $pkgmeta; + } + + /* + * Function: download_package + * Download a package from the repository + * + * Parameters: + * $pkgname - Name of the package. + * $version - The version to download (defaults to "current"). + * + * Throws: + * * A <DoesNotExistError> Exception, if the package was not found. + * * A <InvalidPackage> Exception, if the package was malformed. + * + * Returns: + * A <PluginPackage> object. + */ + public function download_package($pkgname, $version = "current") + { + $found = False; + foreach($this->packages as $p) + { + if($p[0] == $pkgname) + { + $found = True; + break; + } + } + if(!$found) + throw new DoesNotExistError("Package not in package cache."); + + $raw = @file_get_contents($this->baseurl . "/packages/" . urlencode($pkgname) . "/versions/" . urlencode($version), False, $this->stream_ctx); + if($raw === False) + throw new DoesNotExistError(); + + return PluginPackage::load($raw); + } +} + +/* * Class: Article * Representation of an article */ diff --git a/ratatoeskr/sys/pluginpackage.php b/ratatoeskr/sys/pluginpackage.php new file mode 100644 index 0000000..0200c79 --- /dev/null +++ b/ratatoeskr/sys/pluginpackage.php @@ -0,0 +1,265 @@ +<?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; +} + +/* + * 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() + { + 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; + } + + 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(($this->updatepath !== NULL) and (!validate_url($this->updatepath))) + throw new InvalidPackage("Invalid updatepath value. Must be an URL."); + if(($this->web !== NULL) 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 this)) + 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($self); + return this::$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; +} + +?> |