diff --git a/CREDITS.md b/CREDITS.md index 97c1125..eb5f152 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -31,6 +31,7 @@ * Austin Huang - Oracle database support * Felix J. Ogris - S3 Storage backend * Mounir Idrassi & J. Mozdzen - secure YOURLS integration +* Felix J. Ogris - script for data backend migrations, dropped singleton behaviour of data backends ## Translations * Hexalyse - French diff --git a/INSTALL.md b/INSTALL.md index f35d68b..57777c9 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -38,10 +38,10 @@ install and configure PrivateBin on your server. It's available on ### Changing the Path In the index.php you can define a different `PATH`. This is useful to secure -your installation. You can move the configuration, data files, templates and PHP -libraries (directories cfg, doc, data, lib, tpl, tst and vendor) outside of your -document root. This new location must still be accessible to your webserver and -PHP process (see also +your installation. You can move the utilities, configuration, data files, +templates and PHP libraries (directories bin, cfg, doc, data, lib, tpl, tst and +vendor) outside of your document root. This new location must still be +accessible to your webserver and PHP process (see also [open_basedir setting](https://secure.php.net/manual/en/ini.core.php#ini.open-basedir)). > #### PATH Example diff --git a/bin/migrate.php b/bin/migrate.php new file mode 100644 index 0000000..193430e --- /dev/null +++ b/bin/migrate.php @@ -0,0 +1,194 @@ += $argc) { + dieerr("Missing source configuration directory"); +} +if ($delete_after && $delete_during) { + dieerr("--delete-after and --delete-during are mutually exclusive"); +} + +$srcconf = getConfig("source", $argv[$rest]); +$rest++; +$dstconf = getConfig("destination", ($rest < $argc ? $argv[$rest] : "")); + +if (($srcconf->getSection("model") == $dstconf->getSection("model")) && + ($srcconf->getSection("model_options") == $dstconf->getSection("model_options"))) { + dieerr("Source and destination storage configurations are identical"); +} + +$srcmodel = new Model($srcconf); +$srcstore = $srcmodel->getStore(); +$dstmodel = new Model($dstconf); +$dststore = $dstmodel->getStore(); +$ids = $srcstore->getAllPastes(); + +foreach ($ids as $id) { + debug("Reading paste id " . $id); + $paste = $srcstore->read($id); + $comments = $srcstore->readComments($id); + + savePaste($force_overwrite, $dryrun, $id, $paste, $dststore); + foreach ($comments as $comment) { + saveComment($force_overwrite, $dryrun, $id, $comment, $dststore); + } + if ($delete_during) { + deletePaste($dryrun, $id, $srcstore); + } +} + +if ($delete_after) { + foreach ($ids as $id) { + deletePaste($dryrun, $id, $srcstore); + } +} + +debug("Done."); + + +function deletePaste($dryrun, $pasteid, $srcstore) +{ + if (!$dryrun) { + debug("Deleting paste id " . $pasteid); + $srcstore->delete($pasteid); + } else { + debug("Would delete paste id " . $pasteid); + } +} + +function saveComment ($force_overwrite, $dryrun, $pasteid, $comment, $dststore) +{ + $parentid = $comment["parentid"]; + $commentid = $comment["id"]; + + if (!$dststore->existsComment($pasteid, $parentid, $commentid)) { + if (!$dryrun) { + debug("Saving paste id " . $pasteid . ", parent id " . + $parentid . ", comment id " . $commentid); + $dststore->createComment($pasteid, $parentid, $commentid, $comment); + } else { + debug("Would save paste id " . $pasteid . ", parent id " . + $parentid . ", comment id " . $commentid); + } + } else if ($force_overwrite) { + if (!$dryrun) { + debug("Overwriting paste id " . $pasteid . ", parent id " . + $parentid . ", comment id " . $commentid); + $dststore->createComment($pasteid, $parentid, $commentid, $comment); + } else { + debug("Would overwrite paste id " . $pasteid . ", parent id " . + $parentid . ", comment id " . $commentid); + } + } else { + if (!$dryrun) { + dieerr("Not overwriting paste id " . $pasteid . ", parent id " . + $parentid . ", comment id " . $commentid); + } else { + dieerr("Would not overwrite paste id " . $pasteid . ", parent id " . + $parentid . ", comment id " . $commentid); + } + } +} + +function savePaste ($force_overwrite, $dryrun, $pasteid, $paste, $dststore) +{ + if (!$dststore->exists($pasteid)) { + if (!$dryrun) { + debug("Saving paste id " . $pasteid); + $dststore->create($pasteid, $paste); + } else { + debug("Would save paste id " . $pasteid); + } + } else if ($force_overwrite) { + if (!$dryrun) { + debug("Overwriting paste id " . $pasteid); + $dststore->create($pasteid, $paste); + } else { + debug("Would overwrite paste id " . $pasteid); + } + } else { + if (!$dryrun) { + dieerr("Not overwriting paste id " . $pasteid); + } else { + dieerr("Would not overwrite paste id " . $pasteid); + } + } +} + +function getConfig ($target, $confdir) +{ + debug("Trying to load " . $target . " conf.php" . + ($confdir === "" ? "" : " from " . $confdir)); + + putenv("CONFIG_PATH=" . $confdir); + $conf = new Configuration; + putenv("CONFIG_PATH="); + + return $conf; +} + +function dieerr ($text) +{ + fprintf(STDERR, "ERROR: %s" . PHP_EOL, $text); + die(1); +} + +function debug ($text) { + if ($GLOBALS["verbose"]) { + printf("DEBUG: %s" . PHP_EOL, $text); + } +} + +function helpexit () +{ + print("migrate.php - Copy data between PrivateBin backends + +Usage: + php migrate.php [--delete-after] [--delete-during] [-f] [-n] [-v] srcconfdir + [] + php migrate.php [-h] + +Options: + --delete-after delete data from source after all pastes and comments have + successfully been copied to the destination + --delete-during delete data from source after the current paste and its + comments have successfully been copied to the destination + -f forcefully overwrite data which already exists at the + destination + -n dry run, do not copy data + -v be verbose + use storage backend configration from conf.php found in + this directory as source + optionally, use storage backend configration from conf.php + found in this directory as destination; defaults to: + " . PATH . "cfg" . DIRECTORY_SEPARATOR . "conf.php +"); + exit(); +} diff --git a/lib/Data/AbstractData.php b/lib/Data/AbstractData.php index 05353ce..eddab09 100644 --- a/lib/Data/AbstractData.php +++ b/lib/Data/AbstractData.php @@ -15,61 +15,17 @@ namespace PrivateBin\Data; /** * AbstractData * - * Abstract model for data access, implemented as a singleton. + * Abstract model for data access */ abstract class AbstractData { - /** - * Singleton instance - * - * @access protected - * @static - * @var AbstractData - */ - protected static $_instance = null; - /** * cache for the traffic limiter * - * @access private - * @static + * @access protected * @var array */ - protected static $_last_cache = array(); - - /** - * Enforce singleton, disable constructor - * - * Instantiate using {@link getInstance()}, this object implements the singleton pattern. - * - * @access protected - */ - protected function __construct() - { - } - - /** - * Enforce singleton, disable cloning - * - * Instantiate using {@link getInstance()}, this object implements the singleton pattern. - * - * @access private - */ - private function __clone() - { - } - - /** - * Get instance of singleton - * - * @access public - * @static - * @param array $options - * @return AbstractData - */ - public static function getInstance(array $options) - { - } + protected $_last_cache = array(); /** * Create a paste. @@ -150,9 +106,9 @@ abstract class AbstractData public function purgeValues($namespace, $time) { if ($namespace === 'traffic_limiter') { - foreach (self::$_last_cache as $key => $last_submission) { + foreach ($this->_last_cache as $key => $last_submission) { if ($last_submission <= $time) { - unset(self::$_last_cache[$key]); + unset($thi->_last_cache[$key]); } } } @@ -207,6 +163,14 @@ abstract class AbstractData } } + /** + * Returns all paste ids + * + * @access public + * @return array + */ + abstract public function getAllPastes(); + /** * Get next free slot for comment from postdate. * diff --git a/lib/Data/Database.php b/lib/Data/Database.php index 3ca7b75..05952a9 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -30,54 +30,45 @@ class Database extends AbstractData * * @var array */ - private static $_cache = array(); + private $_cache = array(); /** * instance of database connection * * @access private - * @static * @var PDO */ - private static $_db; + private $_db; /** * table prefix * * @access private - * @static * @var string */ - private static $_prefix = ''; + private $_prefix = ''; /** * database type * * @access private - * @static * @var string */ - private static $_type = ''; + private $_type = ''; /** - * get instance of singleton + * instantiates a new Database data backend * * @access public - * @static * @param array $options * @throws Exception - * @return Database + * @return */ - public static function getInstance(array $options) + public function __construct(array $options) { - // if needed initialize the singleton - if (!(self::$_instance instanceof self)) { - self::$_instance = new self; - } - // set table prefix if given if (array_key_exists('tbl', $options)) { - self::$_prefix = $options['tbl']; + $this->_prefix = $options['tbl']; } // initialize the db connection with new options @@ -94,16 +85,16 @@ class Database extends AbstractData $db_tables_exist = true; // setup type and dabase connection - self::$_type = strtolower( + $this->_type = strtolower( substr($options['dsn'], 0, strpos($options['dsn'], ':')) ); // MySQL uses backticks to quote identifiers by default, // tell it to expect ANSI SQL double quotes - if (self::$_type === 'mysql' && defined('PDO::MYSQL_ATTR_INIT_COMMAND')) { + if ($this->_type === 'mysql' && defined('PDO::MYSQL_ATTR_INIT_COMMAND')) { $options['opt'][PDO::MYSQL_ATTR_INIT_COMMAND] = "SET SESSION sql_mode='ANSI_QUOTES'"; } - $tableQuery = self::_getTableQuery(self::$_type); - self::$_db = new PDO( + $tableQuery = $this->_getTableQuery($this->_type); + $this->_db = new PDO( $options['dsn'], $options['usr'], $options['pwd'], @@ -111,43 +102,41 @@ class Database extends AbstractData ); // check if the database contains the required tables - $tables = self::$_db->query($tableQuery)->fetchAll(PDO::FETCH_COLUMN, 0); + $tables = $this->_db->query($tableQuery)->fetchAll(PDO::FETCH_COLUMN, 0); // create paste table if necessary - if (!in_array(self::_sanitizeIdentifier('paste'), $tables)) { - self::_createPasteTable(); + if (!in_array($this->_sanitizeIdentifier('paste'), $tables)) { + $this->_createPasteTable(); $db_tables_exist = false; } // create comment table if necessary - if (!in_array(self::_sanitizeIdentifier('comment'), $tables)) { - self::_createCommentTable(); + if (!in_array($this->_sanitizeIdentifier('comment'), $tables)) { + $this->_createCommentTable(); $db_tables_exist = false; } // create config table if necessary $db_version = Controller::VERSION; - if (!in_array(self::_sanitizeIdentifier('config'), $tables)) { - self::_createConfigTable(); + if (!in_array($this->_sanitizeIdentifier('config'), $tables)) { + $this->_createConfigTable(); // if we only needed to create the config table, the DB is older then 0.22 if ($db_tables_exist) { $db_version = '0.21'; } } else { - $db_version = self::_getConfig('VERSION'); + $db_version = $this->_getConfig('VERSION'); } // update database structure if necessary if (version_compare($db_version, Controller::VERSION, '<')) { - self::_upgradeDatabase($db_version); + $this->_upgradeDatabase($db_version); } } else { throw new Exception( 'Missing configuration for key dsn, usr, pwd or opt in the section model_options, please check your configuration file', 6 ); } - - return self::$_instance; } /** @@ -161,12 +150,12 @@ class Database extends AbstractData public function create($pasteid, array $paste) { if ( - array_key_exists($pasteid, self::$_cache) + array_key_exists($pasteid, $this->_cache) ) { - if (false !== self::$_cache[$pasteid]) { + if (false !== $this->_cache[$pasteid]) { return false; } else { - unset(self::$_cache[$pasteid]); + unset($this->_cache[$pasteid]); } } @@ -175,7 +164,7 @@ class Database extends AbstractData $attachment = $attachmentname = null; $meta = $paste['meta']; $isVersion1 = array_key_exists('data', $paste); - list($createdKey) = self::_getVersionedKeys($isVersion1 ? 1 : 2); + list($createdKey) = $this->_getVersionedKeys($isVersion1 ? 1 : 2); $created = (int) $meta[$createdKey]; unset($meta[$createdKey], $paste['meta']); if (array_key_exists('expire_date', $meta)) { @@ -204,8 +193,8 @@ class Database extends AbstractData $burnafterreading = $paste['adata'][3]; } try { - return self::_exec( - 'INSERT INTO "' . self::_sanitizeIdentifier('paste') . + return $this->_exec( + 'INSERT INTO "' . $this->_sanitizeIdentifier('paste') . '" VALUES(?,?,?,?,?,?,?,?,?)', array( $pasteid, @@ -233,14 +222,14 @@ class Database extends AbstractData */ public function read($pasteid) { - if (array_key_exists($pasteid, self::$_cache)) { - return self::$_cache[$pasteid]; + if (array_key_exists($pasteid, $this->_cache)) { + return $this->_cache[$pasteid]; } - self::$_cache[$pasteid] = false; + $this->_cache[$pasteid] = false; try { - $paste = self::_select( - 'SELECT * FROM "' . self::_sanitizeIdentifier('paste') . + $paste = $this->_select( + 'SELECT * FROM "' . $this->_sanitizeIdentifier('paste') . '" WHERE "dataid" = ?', array($pasteid), true ); } catch (Exception $e) { @@ -253,11 +242,11 @@ class Database extends AbstractData $data = Json::decode($paste['data']); $isVersion2 = array_key_exists('v', $data) && $data['v'] >= 2; if ($isVersion2) { - self::$_cache[$pasteid] = $data; - list($createdKey) = self::_getVersionedKeys(2); + $this->_cache[$pasteid] = $data; + list($createdKey) = $this->_getVersionedKeys(2); } else { - self::$_cache[$pasteid] = array('data' => $paste['data']); - list($createdKey) = self::_getVersionedKeys(1); + $this->_cache[$pasteid] = array('data' => $paste['data']); + list($createdKey) = $this->_getVersionedKeys(1); } try { @@ -266,31 +255,31 @@ class Database extends AbstractData $paste['meta'] = array(); } $paste = self::upgradePreV1Format($paste); - self::$_cache[$pasteid]['meta'] = $paste['meta']; - self::$_cache[$pasteid]['meta'][$createdKey] = (int) $paste['postdate']; + $this->_cache[$pasteid]['meta'] = $paste['meta']; + $this->_cache[$pasteid]['meta'][$createdKey] = (int) $paste['postdate']; $expire_date = (int) $paste['expiredate']; if ($expire_date > 0) { - self::$_cache[$pasteid]['meta']['expire_date'] = $expire_date; + $this->_cache[$pasteid]['meta']['expire_date'] = $expire_date; } if ($isVersion2) { - return self::$_cache[$pasteid]; + return $this->_cache[$pasteid]; } // support v1 attachments if (array_key_exists('attachment', $paste) && !empty($paste['attachment'])) { - self::$_cache[$pasteid]['attachment'] = $paste['attachment']; + $this->_cache[$pasteid]['attachment'] = $paste['attachment']; if (array_key_exists('attachmentname', $paste) && !empty($paste['attachmentname'])) { - self::$_cache[$pasteid]['attachmentname'] = $paste['attachmentname']; + $this->_cache[$pasteid]['attachmentname'] = $paste['attachmentname']; } } if ($paste['opendiscussion']) { - self::$_cache[$pasteid]['meta']['opendiscussion'] = true; + $this->_cache[$pasteid]['meta']['opendiscussion'] = true; } if ($paste['burnafterreading']) { - self::$_cache[$pasteid]['meta']['burnafterreading'] = true; + $this->_cache[$pasteid]['meta']['burnafterreading'] = true; } - return self::$_cache[$pasteid]; + return $this->_cache[$pasteid]; } /** @@ -301,18 +290,18 @@ class Database extends AbstractData */ public function delete($pasteid) { - self::_exec( - 'DELETE FROM "' . self::_sanitizeIdentifier('paste') . + $this->_exec( + 'DELETE FROM "' . $this->_sanitizeIdentifier('paste') . '" WHERE "dataid" = ?', array($pasteid) ); - self::_exec( - 'DELETE FROM "' . self::_sanitizeIdentifier('comment') . + $this->_exec( + 'DELETE FROM "' . $this->_sanitizeIdentifier('comment') . '" WHERE "pasteid" = ?', array($pasteid) ); if ( - array_key_exists($pasteid, self::$_cache) + array_key_exists($pasteid, $this->_cache) ) { - unset(self::$_cache[$pasteid]); + unset($this->_cache[$pasteid]); } } @@ -326,11 +315,11 @@ class Database extends AbstractData public function exists($pasteid) { if ( - !array_key_exists($pasteid, self::$_cache) + !array_key_exists($pasteid, $this->_cache) ) { - self::$_cache[$pasteid] = $this->read($pasteid); + $this->_cache[$pasteid] = $this->read($pasteid); } - return (bool) self::$_cache[$pasteid]; + return (bool) $this->_cache[$pasteid]; } /** @@ -352,7 +341,7 @@ class Database extends AbstractData $version = 2; $data = Json::encode($comment); } - list($createdKey, $iconKey) = self::_getVersionedKeys($version); + list($createdKey, $iconKey) = $this->_getVersionedKeys($version); $meta = $comment['meta']; unset($comment['meta']); foreach (array('nickname', $iconKey) as $key) { @@ -361,8 +350,8 @@ class Database extends AbstractData } } try { - return self::_exec( - 'INSERT INTO "' . self::_sanitizeIdentifier('comment') . + return $this->_exec( + 'INSERT INTO "' . $this->_sanitizeIdentifier('comment') . '" VALUES(?,?,?,?,?,?,?)', array( $commentid, @@ -388,8 +377,8 @@ class Database extends AbstractData */ public function readComments($pasteid) { - $rows = self::_select( - 'SELECT * FROM "' . self::_sanitizeIdentifier('comment') . + $rows = $this->_select( + 'SELECT * FROM "' . $this->_sanitizeIdentifier('comment') . '" WHERE "pasteid" = ?', array($pasteid) ); @@ -406,7 +395,7 @@ class Database extends AbstractData $version = 1; $comments[$i] = array('data' => $row['data']); } - list($createdKey, $iconKey) = self::_getVersionedKeys($version); + list($createdKey, $iconKey) = $this->_getVersionedKeys($version); $comments[$i]['id'] = $row['dataid']; $comments[$i]['parentid'] = $row['parentid']; $comments[$i]['meta'] = array($createdKey => (int) $row['postdate']); @@ -433,8 +422,8 @@ class Database extends AbstractData public function existsComment($pasteid, $parentid, $commentid) { try { - return (bool) self::_select( - 'SELECT "dataid" FROM "' . self::_sanitizeIdentifier('comment') . + return (bool) $this->_select( + 'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('comment') . '" WHERE "pasteid" = ? AND "parentid" = ? AND "dataid" = ?', array($pasteid, $parentid, $commentid), true ); @@ -455,15 +444,15 @@ class Database extends AbstractData public function setValue($value, $namespace, $key = '') { if ($namespace === 'traffic_limiter') { - self::$_last_cache[$key] = $value; + $this->_last_cache[$key] = $value; try { - $value = Json::encode(self::$_last_cache); + $value = Json::encode($this->_last_cache); } catch (Exception $e) { return false; } } - return self::_exec( - 'UPDATE "' . self::_sanitizeIdentifier('config') . + return $this->_exec( + 'UPDATE "' . $this->_sanitizeIdentifier('config') . '" SET "value" = ? WHERE "id" = ?', array($value, strtoupper($namespace)) ); @@ -483,8 +472,8 @@ class Database extends AbstractData $value = $this->_getConfig($configKey); if ($value === '') { // initialize the row, so that setValue can rely on UPDATE queries - self::_exec( - 'INSERT INTO "' . self::_sanitizeIdentifier('config') . + $this->_exec( + 'INSERT INTO "' . $this->_sanitizeIdentifier('config') . '" VALUES(?,?)', array($configKey, '') ); @@ -492,7 +481,8 @@ class Database extends AbstractData // migrate filesystem based salt into database $file = 'data' . DIRECTORY_SEPARATOR . 'salt.php'; if ($namespace === 'salt' && is_readable($file)) { - $value = Filesystem::getInstance(array('dir' => 'data'))->getValue('salt'); + $fs = new Filesystem(array('dir' => 'data')); + $value = $fs->getValue('salt'); $this->setValue($value, 'salt'); @unlink($file); return $value; @@ -500,12 +490,12 @@ class Database extends AbstractData } if ($value && $namespace === 'traffic_limiter') { try { - self::$_last_cache = Json::decode($value); + $this->_last_cache = Json::decode($value); } catch (Exception $e) { - self::$_last_cache = array(); + $this->_last_cache = array(); } - if (array_key_exists($key, self::$_last_cache)) { - return self::$_last_cache[$key]; + if (array_key_exists($key, $this->_last_cache)) { + return $this->_last_cache[$key]; } } return (string) $value; @@ -521,10 +511,10 @@ class Database extends AbstractData protected function _getExpiredPastes($batchsize) { $pastes = array(); - $rows = self::_select( - 'SELECT "dataid" FROM "' . self::_sanitizeIdentifier('paste') . + $rows = $this->_select( + 'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') . '" WHERE "expiredate" < ? AND "expiredate" != ? ' . - (self::$_type === 'oci' ? 'FETCH NEXT ? ROWS ONLY' : 'LIMIT ?'), + ($this->_type === 'oci' ? 'FETCH NEXT ? ROWS ONLY' : 'LIMIT ?'), array(time(), 0, $batchsize) ); if (is_array($rows) && count($rows)) { @@ -535,19 +525,36 @@ class Database extends AbstractData return $pastes; } + /** + * @inheritDoc + */ + public function getAllPastes() + { + $pastes = array(); + $rows = $this->_select( + 'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') . '"', + array() + ); + if (is_array($rows) && count($rows)) { + foreach ($rows as $row) { + $pastes[] = $row['dataid']; + } + } + return $pastes; + } + /** * execute a statement * * @access private - * @static * @param string $sql * @param array $params * @throws PDOException * @return bool */ - private static function _exec($sql, array $params) + private function _exec($sql, array $params) { - $statement = self::$_db->prepare($sql); + $statement = $this->_db->prepare($sql); foreach ($params as $key => &$parameter) { $position = $key + 1; if (is_int($parameter)) { @@ -567,20 +574,19 @@ class Database extends AbstractData * run a select statement * * @access private - * @static * @param string $sql * @param array $params * @param bool $firstOnly if only the first row should be returned * @throws PDOException * @return array|false */ - private static function _select($sql, array $params, $firstOnly = false) + private function _select($sql, array $params, $firstOnly = false) { - $statement = self::$_db->prepare($sql); + $statement = $this->_db->prepare($sql); $statement->execute($params); if ($firstOnly) { $result = $statement->fetch(PDO::FETCH_ASSOC); - } elseif (self::$_type === 'oci') { + } elseif ($this->_type === 'oci') { // workaround for https://bugs.php.net/bug.php?id=46728 $result = array(); while ($row = $statement->fetch(PDO::FETCH_ASSOC)) { @@ -590,7 +596,7 @@ class Database extends AbstractData $result = $statement->fetchAll(PDO::FETCH_ASSOC); } $statement->closeCursor(); - if (self::$_type === 'oci' && is_array($result)) { + if ($this->_type === 'oci' && is_array($result)) { // returned CLOB values are streams, convert these into strings $result = $firstOnly ? array_map('PrivateBin\Data\Database::_sanitizeClob', $result) : @@ -603,11 +609,10 @@ class Database extends AbstractData * get version dependent key names * * @access private - * @static * @param int $version * @return array */ - private static function _getVersionedKeys($version) + private function _getVersionedKeys($version) { if ($version === 1) { return array('postdate', 'vizhash'); @@ -619,12 +624,11 @@ class Database extends AbstractData * get table list query, depending on the database type * * @access private - * @static * @param string $type * @throws Exception * @return string */ - private static function _getTableQuery($type) + private function _getTableQuery($type) { switch ($type) { case 'ibm': @@ -675,15 +679,14 @@ class Database extends AbstractData * get a value by key from the config table * * @access private - * @static * @param string $key * @return string */ - private static function _getConfig($key) + private function _getConfig($key) { try { - $row = self::_select( - 'SELECT "value" FROM "' . self::_sanitizeIdentifier('config') . + $row = $this->_select( + 'SELECT "value" FROM "' . $this->_sanitizeIdentifier('config') . '" WHERE "id" = ?', array($key), true ); } catch (PDOException $e) { @@ -696,14 +699,13 @@ class Database extends AbstractData * get the primary key clauses, depending on the database driver * * @access private - * @static * @param string $key * @return array */ - private static function _getPrimaryKeyClauses($key = 'dataid') + private function _getPrimaryKeyClauses($key = 'dataid') { $main_key = $after_key = ''; - switch (self::$_type) { + switch ($this->_type) { case 'mysql': case 'oci': $after_key = ", PRIMARY KEY (\"$key\")"; @@ -721,12 +723,11 @@ class Database extends AbstractData * PostgreSQL and OCI uses a different API for BLOBs then SQL, hence we use TEXT and CLOB * * @access private - * @static * @return string */ - private static function _getDataType() + private function _getDataType() { - switch (self::$_type) { + switch ($this->_type) { case 'oci': return 'CLOB'; case 'pgsql': @@ -742,12 +743,11 @@ class Database extends AbstractData * PostgreSQL and OCI use different APIs for BLOBs then SQL, hence we use TEXT and CLOB * * @access private - * @static * @return string */ - private static function _getAttachmentType() + private function _getAttachmentType() { - switch (self::$_type) { + switch ($this->_type) { case 'oci': return 'CLOB'; case 'pgsql': @@ -763,12 +763,11 @@ class Database extends AbstractData * OCI doesn't accept TEXT so it has to be VARCHAR2(4000) * * @access private - * @static * @return string */ - private static function _getMetaType() + private function _getMetaType() { - switch (self::$_type) { + switch ($this->_type) { case 'oci': return 'VARCHAR2(4000)'; default: @@ -780,16 +779,15 @@ class Database extends AbstractData * create the paste table * * @access private - * @static */ - private static function _createPasteTable() + private function _createPasteTable() { - list($main_key, $after_key) = self::_getPrimaryKeyClauses(); - $dataType = self::_getDataType(); - $attachmentType = self::_getAttachmentType(); - $metaType = self::_getMetaType(); - self::$_db->exec( - 'CREATE TABLE "' . self::_sanitizeIdentifier('paste') . '" ( ' . + list($main_key, $after_key) = $this->_getPrimaryKeyClauses(); + $dataType = $this->_getDataType(); + $attachmentType = $this->_getAttachmentType(); + $metaType = $this->_getMetaType(); + $this->_db->exec( + 'CREATE TABLE "' . $this->_sanitizeIdentifier('paste') . '" ( ' . "\"dataid\" CHAR(16) NOT NULL$main_key, " . "\"data\" $attachmentType, " . '"postdate" INT, ' . @@ -806,14 +804,13 @@ class Database extends AbstractData * create the paste table * * @access private - * @static */ - private static function _createCommentTable() + private function _createCommentTable() { - list($main_key, $after_key) = self::_getPrimaryKeyClauses(); - $dataType = self::_getDataType(); - self::$_db->exec( - 'CREATE TABLE "' . self::_sanitizeIdentifier('comment') . '" ( ' . + list($main_key, $after_key) = $this->_getPrimaryKeyClauses(); + $dataType = $this->_getDataType(); + $this->_db->exec( + 'CREATE TABLE "' . $this->_sanitizeIdentifier('comment') . '" ( ' . "\"dataid\" CHAR(16) NOT NULL$main_key, " . '"pasteid" CHAR(16), ' . '"parentid" CHAR(16), ' . @@ -822,15 +819,15 @@ class Database extends AbstractData "\"vizhash\" $dataType, " . "\"postdate\" INT$after_key )" ); - if (self::$_type === 'oci') { - self::$_db->exec( + if ($this->_type === 'oci') { + $this->_db->exec( 'declare already_exists exception; columns_indexed exception; pragma exception_init( already_exists, -955 ); pragma exception_init(columns_indexed, -1408); begin - execute immediate \'create index "comment_parent" on "' . self::_sanitizeIdentifier('comment') . '" ("pasteid")\'; + execute immediate \'create index "comment_parent" on "' . $this->_sanitizeIdentifier('comment') . '" ("pasteid")\'; exception when already_exists or columns_indexed then NULL; @@ -838,10 +835,10 @@ class Database extends AbstractData ); } else { // CREATE INDEX IF NOT EXISTS not supported as of Oracle MySQL <= 8.0 - self::$_db->exec( + $this->_db->exec( 'CREATE INDEX "' . - self::_sanitizeIdentifier('comment_parent') . '" ON "' . - self::_sanitizeIdentifier('comment') . '" ("pasteid")' + $this->_sanitizeIdentifier('comment_parent') . '" ON "' . + $this->_sanitizeIdentifier('comment') . '" ("pasteid")' ); } } @@ -850,19 +847,18 @@ class Database extends AbstractData * create the paste table * * @access private - * @static */ - private static function _createConfigTable() + private function _createConfigTable() { - list($main_key, $after_key) = self::_getPrimaryKeyClauses('id'); - $charType = self::$_type === 'oci' ? 'VARCHAR2(16)' : 'CHAR(16)'; - $textType = self::_getMetaType(); - self::$_db->exec( - 'CREATE TABLE "' . self::_sanitizeIdentifier('config') . + list($main_key, $after_key) = $this->_getPrimaryKeyClauses('id'); + $charType = $this->_type === 'oci' ? 'VARCHAR2(16)' : 'CHAR(16)'; + $textType = $this->_getMetaType(); + $this->_db->exec( + 'CREATE TABLE "' . $this->_sanitizeIdentifier('config') . "\" ( \"id\" $charType NOT NULL$main_key, \"value\" $textType$after_key )" ); - self::_exec( - 'INSERT INTO "' . self::_sanitizeIdentifier('config') . + $this->_exec( + 'INSERT INTO "' . $this->_sanitizeIdentifier('config') . '" VALUES(?,?)', array('VERSION', Controller::VERSION) ); @@ -874,11 +870,10 @@ class Database extends AbstractData * From: https://stackoverflow.com/questions/36200534/pdo-oci-into-a-clob-field * * @access public - * @static * @param int|string|resource $value * @return int|string */ - public static function _sanitizeClob($value) + public function _sanitizeClob($value) { if (is_resource($value)) { $value = stream_get_contents($value); @@ -890,90 +885,88 @@ class Database extends AbstractData * sanitizes identifiers * * @access private - * @static * @param string $identifier * @return string */ - private static function _sanitizeIdentifier($identifier) + private function _sanitizeIdentifier($identifier) { - return preg_replace('/[^A-Za-z0-9_]+/', '', self::$_prefix . $identifier); + return preg_replace('/[^A-Za-z0-9_]+/', '', $this->_prefix . $identifier); } /** * upgrade the database schema from an old version * * @access private - * @static * @param string $oldversion */ - private static function _upgradeDatabase($oldversion) + private function _upgradeDatabase($oldversion) { - $dataType = self::_getDataType(); - $attachmentType = self::_getAttachmentType(); + $dataType = $this->_getDataType(); + $attachmentType = $this->_getAttachmentType(); switch ($oldversion) { case '0.21': // create the meta column if necessary (pre 0.21 change) try { - self::$_db->exec( - 'SELECT "meta" FROM "' . self::_sanitizeIdentifier('paste') . '" ' . - (self::$_type === 'oci' ? 'FETCH NEXT 1 ROWS ONLY' : 'LIMIT 1') + $this->_db->exec( + 'SELECT "meta" FROM "' . $this->_sanitizeIdentifier('paste') . '" ' . + ($this->_type === 'oci' ? 'FETCH NEXT 1 ROWS ONLY' : 'LIMIT 1') ); } catch (PDOException $e) { - self::$_db->exec('ALTER TABLE "' . self::_sanitizeIdentifier('paste') . '" ADD COLUMN "meta" TEXT'); + $this->_db->exec('ALTER TABLE "' . $this->_sanitizeIdentifier('paste') . '" ADD COLUMN "meta" TEXT'); } // SQLite only allows one ALTER statement at a time... - self::$_db->exec( - 'ALTER TABLE "' . self::_sanitizeIdentifier('paste') . + $this->_db->exec( + 'ALTER TABLE "' . $this->_sanitizeIdentifier('paste') . "\" ADD COLUMN \"attachment\" $attachmentType" ); - self::$_db->exec( - 'ALTER TABLE "' . self::_sanitizeIdentifier('paste') . "\" ADD COLUMN \"attachmentname\" $dataType" + $this->_db->exec( + 'ALTER TABLE "' . $this->_sanitizeIdentifier('paste') . "\" ADD COLUMN \"attachmentname\" $dataType" ); // SQLite doesn't support MODIFY, but it allows TEXT of similar // size as BLOB, so there is no need to change it there - if (self::$_type !== 'sqlite') { - self::$_db->exec( - 'ALTER TABLE "' . self::_sanitizeIdentifier('paste') . + if ($this->_type !== 'sqlite') { + $this->_db->exec( + 'ALTER TABLE "' . $this->_sanitizeIdentifier('paste') . "\" ADD PRIMARY KEY (\"dataid\"), MODIFY COLUMN \"data\" $dataType" ); - self::$_db->exec( - 'ALTER TABLE "' . self::_sanitizeIdentifier('comment') . + $this->_db->exec( + 'ALTER TABLE "' . $this->_sanitizeIdentifier('comment') . "\" ADD PRIMARY KEY (\"dataid\"), MODIFY COLUMN \"data\" $dataType, " . "MODIFY COLUMN \"nickname\" $dataType, MODIFY COLUMN \"vizhash\" $dataType" ); } else { - self::$_db->exec( + $this->_db->exec( 'CREATE UNIQUE INDEX IF NOT EXISTS "' . - self::_sanitizeIdentifier('paste_dataid') . '" ON "' . - self::_sanitizeIdentifier('paste') . '" ("dataid")' + $this->_sanitizeIdentifier('paste_dataid') . '" ON "' . + $this->_sanitizeIdentifier('paste') . '" ("dataid")' ); - self::$_db->exec( + $this->_db->exec( 'CREATE UNIQUE INDEX IF NOT EXISTS "' . - self::_sanitizeIdentifier('comment_dataid') . '" ON "' . - self::_sanitizeIdentifier('comment') . '" ("dataid")' + $this->_sanitizeIdentifier('comment_dataid') . '" ON "' . + $this->_sanitizeIdentifier('comment') . '" ("dataid")' ); } // CREATE INDEX IF NOT EXISTS not supported as of Oracle MySQL <= 8.0 - self::$_db->exec( + $this->_db->exec( 'CREATE INDEX "' . - self::_sanitizeIdentifier('comment_parent') . '" ON "' . - self::_sanitizeIdentifier('comment') . '" ("pasteid")' + $this->_sanitizeIdentifier('comment_parent') . '" ON "' . + $this->_sanitizeIdentifier('comment') . '" ("pasteid")' ); // no break, continue with updates for 0.22 and later case '1.3': // SQLite doesn't support MODIFY, but it allows TEXT of similar // size as BLOB and PostgreSQL uses TEXT, so there is no need // to change it there - if (self::$_type !== 'sqlite' && self::$_type !== 'pgsql') { - self::$_db->exec( - 'ALTER TABLE "' . self::_sanitizeIdentifier('paste') . + if ($this->_type !== 'sqlite' && $this->_type !== 'pgsql') { + $this->_db->exec( + 'ALTER TABLE "' . $this->_sanitizeIdentifier('paste') . "\" MODIFY COLUMN \"data\" $attachmentType" ); } // no break, continue with updates for all newer versions default: - self::_exec( - 'UPDATE "' . self::_sanitizeIdentifier('config') . + $this->_exec( + 'UPDATE "' . $this->_sanitizeIdentifier('config') . '" SET "value" = ? WHERE "id" = ?', array(Controller::VERSION, 'VERSION') ); diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index 9674513..3dd6957 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -40,33 +40,26 @@ class Filesystem extends AbstractData * path in which to persist something * * @access private - * @static * @var string */ - private static $_path = 'data'; + private $_path = 'data'; /** - * get instance of singleton + * instantiates a new Filesystem data backend * * @access public - * @static * @param array $options - * @return Filesystem + * @return */ - public static function getInstance(array $options) + public function __construct(array $options) { - // if needed initialize the singleton - if (!(self::$_instance instanceof self)) { - self::$_instance = new self; - } // if given update the data directory if ( is_array($options) && array_key_exists('dir', $options) ) { - self::$_path = $options['dir']; + $this->_path = $options['dir']; } - return self::$_instance; } /** @@ -79,7 +72,7 @@ class Filesystem extends AbstractData */ public function create($pasteid, array $paste) { - $storagedir = self::_dataid2path($pasteid); + $storagedir = $this->_dataid2path($pasteid); $file = $storagedir . $pasteid . '.php'; if (is_file($file)) { return false; @@ -87,7 +80,7 @@ class Filesystem extends AbstractData if (!is_dir($storagedir)) { mkdir($storagedir, 0700, true); } - return self::_store($file, $paste); + return $this->_store($file, $paste); } /** @@ -101,7 +94,7 @@ class Filesystem extends AbstractData { if ( !$this->exists($pasteid) || - !$paste = self::_get(self::_dataid2path($pasteid) . $pasteid . '.php') + !$paste = $this->_get($this->_dataid2path($pasteid) . $pasteid . '.php') ) { return false; } @@ -116,7 +109,7 @@ class Filesystem extends AbstractData */ public function delete($pasteid) { - $pastedir = self::_dataid2path($pasteid); + $pastedir = $this->_dataid2path($pasteid); if (is_dir($pastedir)) { // Delete the paste itself. if (is_file($pastedir . $pasteid . '.php')) { @@ -124,7 +117,7 @@ class Filesystem extends AbstractData } // Delete discussion if it exists. - $discdir = self::_dataid2discussionpath($pasteid); + $discdir = $this->_dataid2discussionpath($pasteid); if (is_dir($discdir)) { // Delete all files in discussion directory $dir = dir($discdir); @@ -148,20 +141,20 @@ class Filesystem extends AbstractData */ public function exists($pasteid) { - $basePath = self::_dataid2path($pasteid) . $pasteid; + $basePath = $this->_dataid2path($pasteid) . $pasteid; $pastePath = $basePath . '.php'; // convert to PHP protected files if needed if (is_readable($basePath)) { - self::_prependRename($basePath, $pastePath); + $this->_prependRename($basePath, $pastePath); // convert comments, too - $discdir = self::_dataid2discussionpath($pasteid); + $discdir = $this->_dataid2discussionpath($pasteid); if (is_dir($discdir)) { $dir = dir($discdir); while (false !== ($filename = $dir->read())) { if (substr($filename, -4) !== '.php' && strlen($filename) >= 16) { $commentFilename = $discdir . $filename . '.php'; - self::_prependRename($discdir . $filename, $commentFilename); + $this->_prependRename($discdir . $filename, $commentFilename); } } $dir->close(); @@ -182,7 +175,7 @@ class Filesystem extends AbstractData */ public function createComment($pasteid, $parentid, $commentid, array $comment) { - $storagedir = self::_dataid2discussionpath($pasteid); + $storagedir = $this->_dataid2discussionpath($pasteid); $file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid . '.php'; if (is_file($file)) { return false; @@ -190,7 +183,7 @@ class Filesystem extends AbstractData if (!is_dir($storagedir)) { mkdir($storagedir, 0700, true); } - return self::_store($file, $comment); + return $this->_store($file, $comment); } /** @@ -203,7 +196,7 @@ class Filesystem extends AbstractData public function readComments($pasteid) { $comments = array(); - $discdir = self::_dataid2discussionpath($pasteid); + $discdir = $this->_dataid2discussionpath($pasteid); if (is_dir($discdir)) { $dir = dir($discdir); while (false !== ($filename = $dir->read())) { @@ -212,7 +205,7 @@ class Filesystem extends AbstractData // - commentid is the comment identifier itself. // - parentid is the comment this comment replies to (It can be pasteid) if (is_file($discdir . $filename)) { - $comment = self::_get($discdir . $filename); + $comment = $this->_get($discdir . $filename); $items = explode('.', $filename); // Add some meta information not contained in file. $comment['id'] = $items[1]; @@ -243,7 +236,7 @@ class Filesystem extends AbstractData public function existsComment($pasteid, $parentid, $commentid) { return is_file( - self::_dataid2discussionpath($pasteid) . + $this->_dataid2discussionpath($pasteid) . $pasteid . '.' . $commentid . '.' . $parentid . '.php' ); } @@ -261,20 +254,20 @@ class Filesystem extends AbstractData { switch ($namespace) { case 'purge_limiter': - return self::_storeString( - self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php', + return $this->_storeString( + $this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php', '_storeString( + $this->_path . DIRECTORY_SEPARATOR . 'salt.php', '_last_cache[$key] = $value; + return $this->_storeString( + $this->_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php', + '_last_cache, true) . ';' ); } return false; @@ -292,14 +285,14 @@ class Filesystem extends AbstractData { switch ($namespace) { case 'purge_limiter': - $file = self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php'; + $file = $this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php'; if (is_readable($file)) { require $file; return $GLOBALS['purge_limiter']; } break; case 'salt': - $file = self::$_path . DIRECTORY_SEPARATOR . 'salt.php'; + $file = $this->_path . DIRECTORY_SEPARATOR . 'salt.php'; if (is_readable($file)) { $items = explode('|', file_get_contents($file)); if (is_array($items) && count($items) == 3) { @@ -308,12 +301,12 @@ class Filesystem extends AbstractData } break; case 'traffic_limiter': - $file = self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php'; + $file = $this->_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php'; if (is_readable($file)) { require $file; - self::$_last_cache = $GLOBALS['traffic_limiter']; - if (array_key_exists($key, self::$_last_cache)) { - return self::$_last_cache[$key]; + $this->_last_cache = $GLOBALS['traffic_limiter']; + if (array_key_exists($key, $this->_last_cache)) { + return $this->_last_cache[$key]; } } break; @@ -325,11 +318,10 @@ class Filesystem extends AbstractData * get the data * * @access public - * @static * @param string $filename * @return array|false $data */ - private static function _get($filename) + private function _get($filename) { return Json::decode( substr( @@ -350,7 +342,7 @@ class Filesystem extends AbstractData { $pastes = array(); $firstLevel = array_filter( - scandir(self::$_path), + scandir($this->_path), 'PrivateBin\Data\Filesystem::_isFirstLevelDir' ); if (count($firstLevel) > 0) { @@ -358,7 +350,7 @@ class Filesystem extends AbstractData for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) { $firstKey = array_rand($firstLevel); $secondLevel = array_filter( - scandir(self::$_path . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]), + scandir($this->_path . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]), 'PrivateBin\Data\Filesystem::_isSecondLevelDir' ); @@ -369,7 +361,7 @@ class Filesystem extends AbstractData } $secondKey = array_rand($secondLevel); - $path = self::$_path . DIRECTORY_SEPARATOR . + $path = $this->_path . DIRECTORY_SEPARATOR . $firstLevel[$firstKey] . DIRECTORY_SEPARATOR . $secondLevel[$secondKey]; if (!is_dir($path)) { @@ -412,6 +404,46 @@ class Filesystem extends AbstractData return $pastes; } + /** + * @inheritDoc + */ + public function getAllPastes() + { + $pastes = array(); + $subdirs = scandir($this->_path); + if ($subdirs === false) { + dieerr("Unable to list directory " . $this->_path); + } + $subdirs = preg_grep("/^[^.].$/", $subdirs); + + foreach ($subdirs as $subdir) { + $subpath = $this->_path . DIRECTORY_SEPARATOR . $subdir; + + $subsubdirs = scandir($subpath); + if ($subsubdirs === false) { + dieerr("Unable to list directory " . $subpath); + } + $subsubdirs = preg_grep("/^[^.].$/", $subsubdirs); + foreach ($subsubdirs as $subsubdir) { + $subsubpath = $subpath . DIRECTORY_SEPARATOR . $subsubdir; + + $files = scandir($subsubpath); + if ($files === false) { + dieerr("Unable to list directory " . $subsubpath); + } + $files = preg_grep("/\.php$/", $files); + + foreach ($files as $file) { + if (substr($file, 0, 4) === $subdir . $subsubdir) { + $pastes[] = substr($file, 0, strlen($file) - 4); + } + } + } + } + + return $pastes; + } + /** * Convert paste id to storage path. * @@ -423,13 +455,12 @@ class Filesystem extends AbstractData * eg. input 'e3570978f9e4aa90' --> output 'data/e3/57/' * * @access private - * @static * @param string $dataid * @return string */ - private static function _dataid2path($dataid) + private function _dataid2path($dataid) { - return self::$_path . DIRECTORY_SEPARATOR . + return $this->_path . DIRECTORY_SEPARATOR . substr($dataid, 0, 2) . DIRECTORY_SEPARATOR . substr($dataid, 2, 2) . DIRECTORY_SEPARATOR; } @@ -440,13 +471,12 @@ class Filesystem extends AbstractData * eg. input 'e3570978f9e4aa90' --> output 'data/e3/57/e3570978f9e4aa90.discussion/' * * @access private - * @static * @param string $dataid * @return string */ - private static function _dataid2discussionpath($dataid) + private function _dataid2discussionpath($dataid) { - return self::_dataid2path($dataid) . $dataid . + return $this->_dataid2path($dataid) . $dataid . '.discussion' . DIRECTORY_SEPARATOR; } @@ -454,25 +484,23 @@ class Filesystem extends AbstractData * Check that the given element is a valid first level directory. * * @access private - * @static * @param string $element * @return bool */ - private static function _isFirstLevelDir($element) + private function _isFirstLevelDir($element) { - return self::_isSecondLevelDir($element) && - is_dir(self::$_path . DIRECTORY_SEPARATOR . $element); + return $this->_isSecondLevelDir($element) && + is_dir($this->_path . DIRECTORY_SEPARATOR . $element); } /** * Check that the given element is a valid second level directory. * * @access private - * @static * @param string $element * @return bool */ - private static function _isSecondLevelDir($element) + private function _isSecondLevelDir($element) { return (bool) preg_match('/^[a-f0-9]{2}$/', $element); } @@ -481,15 +509,14 @@ class Filesystem extends AbstractData * store the data * * @access public - * @static * @param string $filename * @param array $data * @return bool */ - private static function _store($filename, array $data) + private function _store($filename, array $data) { try { - return self::_storeString( + return $this->_storeString( $filename, self::PROTECTION_LINE . PHP_EOL . Json::encode($data) ); @@ -502,20 +529,19 @@ class Filesystem extends AbstractData * store a string * * @access public - * @static * @param string $filename * @param string $data * @return bool */ - private static function _storeString($filename, $data) + private function _storeString($filename, $data) { // Create storage directory if it does not exist. - if (!is_dir(self::$_path)) { - if (!@mkdir(self::$_path, 0700)) { + if (!is_dir($this->_path)) { + if (!@mkdir($this->_path, 0700)) { return false; } } - $file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess'; + $file = $this->_path . DIRECTORY_SEPARATOR . '.htaccess'; if (!is_file($file)) { $writtenBytes = 0; if ($fileCreated = @touch($file)) { @@ -553,12 +579,11 @@ class Filesystem extends AbstractData * rename a file, prepending the protection line at the beginning * * @access public - * @static * @param string $srcFile * @param string $destFile * @return void */ - private static function _prependRename($srcFile, $destFile) + private function _prependRename($srcFile, $destFile) { // don't overwrite already converted file if (!is_readable($destFile)) { diff --git a/lib/Data/GoogleCloudStorage.php b/lib/Data/GoogleCloudStorage.php index e876325..e271e2f 100644 --- a/lib/Data/GoogleCloudStorage.php +++ b/lib/Data/GoogleCloudStorage.php @@ -14,54 +14,43 @@ class GoogleCloudStorage extends AbstractData * GCS client * * @access private - * @static * @var StorageClient */ - private static $_client = null; + private $_client = null; /** * GCS bucket * * @access private - * @static * @var Bucket */ - private static $_bucket = null; + private $_bucket = null; /** * object prefix * * @access private - * @static * @var string */ - private static $_prefix = 'pastes'; + private $_prefix = 'pastes'; /** * bucket acl type * * @access private - * @static * @var bool */ - private static $_uniformacl = false; + private $_uniformacl = false; /** - * returns a Google Cloud Storage data backend. + * instantiantes a new Google Cloud Storage data backend. * * @access public - * @static * @param array $options - * @return GoogleCloudStorage + * @return */ - public static function getInstance(array $options) + public function __construct(array $options) { - // if needed initialize the singleton - if (!(self::$_instance instanceof self)) { - self::$_instance = new self; - } - - $bucket = null; if (getenv('PRIVATEBIN_GCS_BUCKET')) { $bucket = getenv('PRIVATEBIN_GCS_BUCKET'); } @@ -69,24 +58,20 @@ class GoogleCloudStorage extends AbstractData $bucket = $options['bucket']; } if (is_array($options) && array_key_exists('prefix', $options)) { - self::$_prefix = $options['prefix']; + $this->_prefix = $options['prefix']; } if (is_array($options) && array_key_exists('uniformacl', $options)) { - self::$_uniformacl = $options['uniformacl']; + $this->_uniformacl = $options['uniformacl']; } - if (empty(self::$_client)) { - self::$_client = class_exists('StorageClientStub', false) ? - new \StorageClientStub(array()) : - new StorageClient(array('suppressKeyFileNotice' => true)); - } - self::$_bucket = self::$_client->bucket($bucket); - - return self::$_instance; + $this->_client = class_exists('StorageClientStub', false) ? + new \StorageClientStub(array()) : + new StorageClient(array('suppressKeyFileNotice' => true)); + $this->_bucket = $this->_client->bucket($bucket); } /** - * returns the google storage object key for $pasteid in self::$_bucket. + * returns the google storage object key for $pasteid in $this->_bucket. * * @access private * @param $pasteid string to get the key for @@ -94,14 +79,14 @@ class GoogleCloudStorage extends AbstractData */ private function _getKey($pasteid) { - if (self::$_prefix != '') { - return self::$_prefix . '/' . $pasteid; + if ($this->_prefix != '') { + return $this->_prefix . '/' . $pasteid; } return $pasteid; } /** - * Uploads the payload in the self::$_bucket under the specified key. + * Uploads the payload in the $this->_bucket under the specified key. * The entire payload is stored as a JSON document. The metadata is replicated * as the GCS object's metadata except for the fields attachment, attachmentname * and salt. @@ -126,12 +111,12 @@ class GoogleCloudStorage extends AbstractData 'metadata' => $metadata, ), ); - if (!self::$_uniformacl) { + if (!$this->_uniformacl) { $data['predefinedAcl'] = 'private'; } - self::$_bucket->upload(Json::encode($payload), $data); + $this->_bucket->upload(Json::encode($payload), $data); } catch (Exception $e) { - error_log('failed to upload ' . $key . ' to ' . self::$_bucket->name() . ', ' . + error_log('failed to upload ' . $key . ' to ' . $this->_bucket->name() . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); return false; } @@ -156,13 +141,13 @@ class GoogleCloudStorage extends AbstractData public function read($pasteid) { try { - $o = self::$_bucket->object($this->_getKey($pasteid)); + $o = $this->_bucket->object($this->_getKey($pasteid)); $data = $o->downloadAsString(); return Json::decode($data); } catch (NotFoundException $e) { return false; } catch (Exception $e) { - error_log('failed to read ' . $pasteid . ' from ' . self::$_bucket->name() . ', ' . + error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket->name() . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); return false; } @@ -176,9 +161,9 @@ class GoogleCloudStorage extends AbstractData $name = $this->_getKey($pasteid); try { - foreach (self::$_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) { + foreach ($this->_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) { try { - self::$_bucket->object($comment->name())->delete(); + $this->_bucket->object($comment->name())->delete(); } catch (NotFoundException $e) { // ignore if already deleted. } @@ -188,7 +173,7 @@ class GoogleCloudStorage extends AbstractData } try { - self::$_bucket->object($name)->delete(); + $this->_bucket->object($name)->delete(); } catch (NotFoundException $e) { // ignore if already deleted } @@ -199,7 +184,7 @@ class GoogleCloudStorage extends AbstractData */ public function exists($pasteid) { - $o = self::$_bucket->object($this->_getKey($pasteid)); + $o = $this->_bucket->object($this->_getKey($pasteid)); return $o->exists(); } @@ -223,8 +208,8 @@ class GoogleCloudStorage extends AbstractData $comments = array(); $prefix = $this->_getKey($pasteid) . '/discussion/'; try { - foreach (self::$_bucket->objects(array('prefix' => $prefix)) as $key) { - $comment = JSON::decode(self::$_bucket->object($key->name())->downloadAsString()); + foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) { + $comment = JSON::decode($this->_bucket->object($key->name())->downloadAsString()); $comment['id'] = basename($key->name()); $slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']); $comments[$slot] = $comment; @@ -241,7 +226,7 @@ class GoogleCloudStorage extends AbstractData public function existsComment($pasteid, $parentid, $commentid) { $name = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid; - $o = self::$_bucket->object($name); + $o = $this->_bucket->object($name); return $o->exists(); } @@ -252,7 +237,7 @@ class GoogleCloudStorage extends AbstractData { $path = 'config/' . $namespace; try { - foreach (self::$_bucket->objects(array('prefix' => $path)) as $object) { + foreach ($this->_bucket->objects(array('prefix' => $path)) as $object) { $name = $object->name(); if (strlen($name) > strlen($path) && substr($name, strlen($path), 1) !== '/') { continue; @@ -300,12 +285,12 @@ class GoogleCloudStorage extends AbstractData 'metadata' => $metadata, ), ); - if (!self::$_uniformacl) { + if (!$this->_uniformacl) { $data['predefinedAcl'] = 'private'; } - self::$_bucket->upload($value, $data); + $this->_bucket->upload($value, $data); } catch (Exception $e) { - error_log('failed to set key ' . $key . ' to ' . self::$_bucket->name() . ', ' . + error_log('failed to set key ' . $key . ' to ' . $this->_bucket->name() . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); return false; } @@ -323,7 +308,7 @@ class GoogleCloudStorage extends AbstractData $key = 'config/' . $namespace . '/' . $key; } try { - $o = self::$_bucket->object($key); + $o = $this->_bucket->object($key); return $o->downloadAsString(); } catch (NotFoundException $e) { return ''; @@ -338,12 +323,12 @@ class GoogleCloudStorage extends AbstractData $expired = array(); $now = time(); - $prefix = self::$_prefix; + $prefix = $this->_prefix; if ($prefix != '') { $prefix .= '/'; } try { - foreach (self::$_bucket->objects(array('prefix' => $prefix)) as $object) { + foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) { $metadata = $object->info()['metadata']; if ($metadata != null && array_key_exists('expire_date', $metadata)) { $expire_at = intval($metadata['expire_date']); @@ -361,4 +346,28 @@ class GoogleCloudStorage extends AbstractData } return $expired; } + + /** + * @inheritDoc + */ + public function getAllPastes() + { + $pastes = array(); + $prefix = $this->_prefix; + if ($prefix != '') { + $prefix .= '/'; + } + + try { + foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) { + $candidate = substr($object->name(), strlen($prefix)); + if (strpos($candidate, "/") === false) { + $pastes[] = $candidate; + } + } + } catch (NotFoundException $e) { + // no objects in the bucket yet + } + return $pastes; + } } diff --git a/lib/Data/S3Storage.php b/lib/Data/S3Storage.php index d741e09..c59aaae 100644 --- a/lib/Data/S3Storage.php +++ b/lib/Data/S3Storage.php @@ -45,86 +45,71 @@ class S3Storage extends AbstractData * S3 client * * @access private - * @static * @var S3Client */ - private static $_client = null; + private $_client = null; /** * S3 client options * * @access private - * @static * @var array */ - private static $_options = array(); + private $_options = array(); /** * S3 bucket * * @access private - * @static * @var string */ - private static $_bucket = null; + private $_bucket = null; /** * S3 prefix for all PrivateBin data in this bucket * * @access private - * @static * @var string */ - private static $_prefix = ''; + private $_prefix = ''; /** - * returns an S3 data backend. + * instantiates a new S3 data backend. * * @access public - * @static * @param array $options - * @return S3Storage + * @return */ - public static function getInstance(array $options) + public function __construct(array $options) { - // if needed initialize the singleton - if (!(self::$_instance instanceof self)) { - self::$_instance = new self; - } - - self::$_options = array(); - self::$_options['credentials'] = array(); + $this->_options['credentials'] = array(); if (is_array($options) && array_key_exists('region', $options)) { - self::$_options['region'] = $options['region']; + $this->_options['region'] = $options['region']; } if (is_array($options) && array_key_exists('version', $options)) { - self::$_options['version'] = $options['version']; + $this->_options['version'] = $options['version']; } if (is_array($options) && array_key_exists('endpoint', $options)) { - self::$_options['endpoint'] = $options['endpoint']; + $this->_options['endpoint'] = $options['endpoint']; } if (is_array($options) && array_key_exists('accesskey', $options)) { - self::$_options['credentials']['key'] = $options['accesskey']; + $this->_options['credentials']['key'] = $options['accesskey']; } if (is_array($options) && array_key_exists('secretkey', $options)) { - self::$_options['credentials']['secret'] = $options['secretkey']; + $this->_options['credentials']['secret'] = $options['secretkey']; } if (is_array($options) && array_key_exists('use_path_style_endpoint', $options)) { - self::$_options['use_path_style_endpoint'] = filter_var($options['use_path_style_endpoint'], FILTER_VALIDATE_BOOLEAN); + $this->_options['use_path_style_endpoint'] = filter_var($options['use_path_style_endpoint'], FILTER_VALIDATE_BOOLEAN); } if (is_array($options) && array_key_exists('bucket', $options)) { - self::$_bucket = $options['bucket']; + $this->_bucket = $options['bucket']; } if (is_array($options) && array_key_exists('prefix', $options)) { - self::$_prefix = $options['prefix']; + $this->_prefix = $options['prefix']; } - if (empty(self::$_client)) { - self::$_client = new S3Client(self::$_options); - } - - return self::$_instance; + $this->_client = new S3Client($this->_options); } /** @@ -138,12 +123,12 @@ class S3Storage extends AbstractData { $allObjects = array(); $options = array( - 'Bucket' => self::$_bucket, + 'Bucket' => $this->_bucket, 'Prefix' => $prefix, ); do { - $objectsListResponse = self::$_client->listObjects($options); + $objectsListResponse = $this->_client->listObjects($options); $objects = $objectsListResponse['Contents'] ?? array(); foreach ($objects as $object) { $allObjects[] = $object; @@ -155,7 +140,7 @@ class S3Storage extends AbstractData } /** - * returns the S3 storage object key for $pasteid in self::$_bucket. + * returns the S3 storage object key for $pasteid in $this->_bucket. * * @access private * @param $pasteid string to get the key for @@ -163,14 +148,14 @@ class S3Storage extends AbstractData */ private function _getKey($pasteid) { - if (self::$_prefix != '') { - return self::$_prefix . '/' . $pasteid; + if ($this->_prefix != '') { + return $this->_prefix . '/' . $pasteid; } return $pasteid; } /** - * Uploads the payload in the self::$_bucket under the specified key. + * Uploads the payload in the $this->_bucket under the specified key. * The entire payload is stored as a JSON document. The metadata is replicated * as the S3 object's metadata except for the fields attachment, attachmentname * and salt. @@ -187,15 +172,15 @@ class S3Storage extends AbstractData $metadata[$k] = strval($v); } try { - self::$_client->putObject(array( - 'Bucket' => self::$_bucket, + $this->_client->putObject(array( + 'Bucket' => $this->_bucket, 'Key' => $key, 'Body' => Json::encode($payload), 'ContentType' => 'application/json', 'Metadata' => $metadata, )); } catch (S3Exception $e) { - error_log('failed to upload ' . $key . ' to ' . self::$_bucket . ', ' . + error_log('failed to upload ' . $key . ' to ' . $this->_bucket . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); return false; } @@ -220,14 +205,14 @@ class S3Storage extends AbstractData public function read($pasteid) { try { - $object = self::$_client->getObject(array( - 'Bucket' => self::$_bucket, + $object = $this->_client->getObject(array( + 'Bucket' => $this->_bucket, 'Key' => $this->_getKey($pasteid), )); $data = $object['Body']->getContents(); return Json::decode($data); } catch (S3Exception $e) { - error_log('failed to read ' . $pasteid . ' from ' . self::$_bucket . ', ' . + error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); return false; } @@ -244,8 +229,8 @@ class S3Storage extends AbstractData $comments = $this->_listAllObjects($name . '/discussion/'); foreach ($comments as $comment) { try { - self::$_client->deleteObject(array( - 'Bucket' => self::$_bucket, + $this->_client->deleteObject(array( + 'Bucket' => $this->_bucket, 'Key' => $comment['Key'], )); } catch (S3Exception $e) { @@ -257,8 +242,8 @@ class S3Storage extends AbstractData } try { - self::$_client->deleteObject(array( - 'Bucket' => self::$_bucket, + $this->_client->deleteObject(array( + 'Bucket' => $this->_bucket, 'Key' => $name, )); } catch (S3Exception $e) { @@ -271,7 +256,7 @@ class S3Storage extends AbstractData */ public function exists($pasteid) { - return self::$_client->doesObjectExistV2(self::$_bucket, $this->_getKey($pasteid)); + return $this->_client->doesObjectExistV2($this->_bucket, $this->_getKey($pasteid)); } /** @@ -296,8 +281,8 @@ class S3Storage extends AbstractData try { $entries = $this->_listAllObjects($prefix); foreach ($entries as $entry) { - $object = self::$_client->getObject(array( - 'Bucket' => self::$_bucket, + $object = $this->_client->getObject(array( + 'Bucket' => $this->_bucket, 'Key' => $entry['Key'], )); $body = JSON::decode($object['Body']->getContents()); @@ -319,7 +304,7 @@ class S3Storage extends AbstractData public function existsComment($pasteid, $parentid, $commentid) { $name = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid; - return self::$_client->doesObjectExistV2(self::$_bucket, $name); + return $this->_client->doesObjectExistV2($this->_bucket, $name); } /** @@ -327,7 +312,7 @@ class S3Storage extends AbstractData */ public function purgeValues($namespace, $time) { - $path = self::$_prefix; + $path = $this->_prefix; if ($path != '') { $path .= '/'; } @@ -339,16 +324,16 @@ class S3Storage extends AbstractData if (strlen($name) > strlen($path) && substr($name, strlen($path), 1) !== '/') { continue; } - $head = self::$_client->headObject(array( - 'Bucket' => self::$_bucket, + $head = $this->_client->headObject(array( + 'Bucket' => $this->_bucket, 'Key' => $name, )); - if (array_key_exists('Metadata', $head) && array_key_exists('value', $head['Metadata'])) { - $value = $head['Metadata']['value']; + if ($head->get('Metadata') != null && array_key_exists('value', $head->get('Metadata'))) { + $value = $head->get('Metadata')['value']; if (is_numeric($value) && intval($value) < $time) { try { - self::$_client->deleteObject(array( - 'Bucket' => self::$_bucket, + $this->_client->deleteObject(array( + 'Bucket' => $this->_bucket, 'Key' => $name, )); } catch (S3Exception $e) { @@ -369,7 +354,7 @@ class S3Storage extends AbstractData */ public function setValue($value, $namespace, $key = '') { - $prefix = self::$_prefix; + $prefix = $this->_prefix; if ($prefix != '') { $prefix .= '/'; } @@ -385,15 +370,15 @@ class S3Storage extends AbstractData $metadata['value'] = strval($value); } try { - self::$_client->putObject(array( - 'Bucket' => self::$_bucket, + $this->_client->putObject(array( + 'Bucket' => $this->_bucket, 'Key' => $key, 'Body' => $value, 'ContentType' => 'application/json', 'Metadata' => $metadata, )); } catch (S3Exception $e) { - error_log('failed to set key ' . $key . ' to ' . self::$_bucket . ', ' . + error_log('failed to set key ' . $key . ' to ' . $this->_bucket . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); return false; } @@ -405,7 +390,7 @@ class S3Storage extends AbstractData */ public function getValue($namespace, $key = '') { - $prefix = self::$_prefix; + $prefix = $this->_prefix; if ($prefix != '') { $prefix .= '/'; } @@ -417,8 +402,8 @@ class S3Storage extends AbstractData } try { - $object = self::$_client->getObject(array( - 'Bucket' => self::$_bucket, + $object = $this->_client->getObject(array( + 'Bucket' => $this->_bucket, 'Key' => $key, )); return $object['Body']->getContents(); @@ -434,19 +419,19 @@ class S3Storage extends AbstractData { $expired = array(); $now = time(); - $prefix = self::$_prefix; + $prefix = $this->_prefix; if ($prefix != '') { $prefix .= '/'; } try { foreach ($this->_listAllObjects($prefix) as $object) { - $head = self::$_client->headObject(array( - 'Bucket' => self::$_bucket, + $head = $this->_client->headObject(array( + 'Bucket' => $this->_bucket, 'Key' => $object['Key'], )); - if (array_key_exists('Metadata', $head) && array_key_exists('expire_date', $head['Metadata'])) { - $expire_at = intval($head['Metadata']['expire_date']); + if ($head->get('Metadata') != null && array_key_exists('expire_date', $head->get('Metadata'))) { + $expire_at = intval($head->get('Metadata')['expire_date']); if ($expire_at != 0 && $expire_at < $now) { array_push($expired, $object['Key']); } @@ -461,4 +446,28 @@ class S3Storage extends AbstractData } return $expired; } + + /** + * @inheritDoc + */ + public function getAllPastes() + { + $pastes = array(); + $prefix = $this->_prefix; + if ($prefix != '') { + $prefix .= '/'; + } + + try { + foreach ($this->_listAllObjects($prefix) as $object) { + $candidate = substr($object["Key"], strlen($prefix)); + if (strpos($candidate, "/") === false) { + $pastes[] = $candidate; + } + } + } catch (S3Exception $e) { + // no objects in the bucket yet + } + return $pastes; + } } diff --git a/lib/Model.php b/lib/Model.php index 360cf68..94f7700 100644 --- a/lib/Model.php +++ b/lib/Model.php @@ -81,10 +81,8 @@ class Model public function getStore() { if ($this->_store === null) { - $this->_store = forward_static_call( - 'PrivateBin\\Data\\' . $this->_conf->getKey('class', 'model') . '::getInstance', - $this->_conf->getSection('model_options') - ); + $class = 'PrivateBin\\Data\\' . $this->_conf->getKey('class', 'model'); + $this->_store = new $class($this->_conf->getSection('model_options')); } return $this->_store; } diff --git a/tst/ConfigurationTestGenerator.php b/tst/ConfigurationTestGenerator.php index 945fc47..a0106a3 100755 --- a/tst/ConfigurationTestGenerator.php +++ b/tst/ConfigurationTestGenerator.php @@ -427,7 +427,7 @@ class ConfigurationCombinationsTest extends PHPUnit_Framework_TestCase /* Setup Routine */ Helper::confBackup(); $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; - $this->_model = Filesystem::getInstance(array('dir' => $this->_path)); + $this->_model = new Filesystem(array('dir' => $this->_path)); $this->reset(); } diff --git a/tst/ControllerTest.php b/tst/ControllerTest.php index e1637c1..698d5f8 100644 --- a/tst/ControllerTest.php +++ b/tst/ControllerTest.php @@ -16,7 +16,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase { /* Setup Routine */ $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; - $this->_data = Filesystem::getInstance(array('dir' => $this->_path)); + $this->_data = new Filesystem(array('dir' => $this->_path)); ServerSalt::setStore($this->_data); TrafficLimiter::setStore($this->_data); $this->reset(); diff --git a/tst/ControllerWithDbTest.php b/tst/ControllerWithDbTest.php index bc8cd7b..d5bbe53 100644 --- a/tst/ControllerWithDbTest.php +++ b/tst/ControllerWithDbTest.php @@ -25,7 +25,7 @@ class ControllerWithDbTest extends ControllerTest mkdir($this->_path); } $this->_options['dsn'] = 'sqlite:' . $this->_path . DIRECTORY_SEPARATOR . 'tst.sq3'; - $this->_data = Database::getInstance($this->_options); + $this->_data = new Database($this->_options); ServerSalt::setStore($this->_data); TrafficLimiter::setStore($this->_data); $this->reset(); diff --git a/tst/ControllerWithGcsTest.php b/tst/ControllerWithGcsTest.php index 3983341..5490db8 100644 --- a/tst/ControllerWithGcsTest.php +++ b/tst/ControllerWithGcsTest.php @@ -39,7 +39,7 @@ class ControllerWithGcsTest extends ControllerTest 'bucket' => self::$_bucket->name(), 'prefix' => 'pastes', ); - $this->_data = GoogleCloudStorage::getInstance($this->_options); + $this->_data = new GoogleCloudStorage($this->_options); ServerSalt::setStore($this->_data); TrafficLimiter::setStore($this->_data); $this->reset(); diff --git a/tst/Data/DatabaseTest.php b/tst/Data/DatabaseTest.php index 16e6fcb..3e65933 100644 --- a/tst/Data/DatabaseTest.php +++ b/tst/Data/DatabaseTest.php @@ -22,7 +22,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase { /* Setup Routine */ $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; - $this->_model = Database::getInstance($this->_options); + $this->_model = new Database($this->_options); } public function tearDown() @@ -35,7 +35,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase public function testSaltMigration() { - ServerSalt::setStore(Filesystem::getInstance(array('dir' => 'data'))); + ServerSalt::setStore(new Filesystem(array('dir' => 'data'))); $salt = ServerSalt::get(); $file = 'data' . DIRECTORY_SEPARATOR . 'salt.php'; $this->assertFileExists($file, 'ServerSalt got initialized and stored on disk'); @@ -141,7 +141,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetIbmInstance() { - Database::getInstance(array( + new Database(array( 'dsn' => 'ibm:', 'usr' => null, 'pwd' => null, 'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION), )); @@ -152,7 +152,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetInformixInstance() { - Database::getInstance(array( + new Database(array( 'dsn' => 'informix:', 'usr' => null, 'pwd' => null, 'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION), )); @@ -163,7 +163,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetMssqlInstance() { - Database::getInstance(array( + new Database(array( 'dsn' => 'mssql:', 'usr' => null, 'pwd' => null, 'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION), )); @@ -174,7 +174,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetMysqlInstance() { - Database::getInstance(array( + new Database(array( 'dsn' => 'mysql:', 'usr' => null, 'pwd' => null, 'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION), )); @@ -185,7 +185,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetOciInstance() { - Database::getInstance(array( + new Database(array( 'dsn' => 'oci:', 'usr' => null, 'pwd' => null, 'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION), )); @@ -196,7 +196,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetPgsqlInstance() { - Database::getInstance(array( + new Database(array( 'dsn' => 'pgsql:', 'usr' => null, 'pwd' => null, 'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION), )); @@ -208,7 +208,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase */ public function testGetFooInstance() { - Database::getInstance(array( + new Database(array( 'dsn' => 'foo:', 'usr' => null, 'pwd' => null, 'opt' => null, )); } @@ -221,7 +221,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase { $options = $this->_options; unset($options['dsn']); - Database::getInstance($options); + new Database($options); } /** @@ -232,7 +232,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase { $options = $this->_options; unset($options['usr']); - Database::getInstance($options); + new Database($options); } /** @@ -243,7 +243,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase { $options = $this->_options; unset($options['pwd']); - Database::getInstance($options); + new Database($options); } /** @@ -254,7 +254,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase { $options = $this->_options; unset($options['opt']); - Database::getInstance($options); + new Database($options); } public function testOldAttachments() @@ -266,7 +266,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase } $this->_options['dsn'] = 'sqlite:' . $path; $this->_options['tbl'] = 'bar_'; - $model = Database::getInstance($this->_options); + $model = new Database($this->_options); $original = $paste = Helper::getPasteWithAttachment(1, array('expire_date' => 1344803344)); $meta = $paste['meta']; @@ -311,7 +311,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase } $this->_options['dsn'] = 'sqlite:' . $path; $this->_options['tbl'] = 'baz_'; - $model = Database::getInstance($this->_options); + $model = new Database($this->_options); $paste = Helper::getPaste(1, array('expire_date' => 1344803344)); unset($paste['meta']['formatter'], $paste['meta']['opendiscussion'], $paste['meta']['salt']); $model->delete(Helper::getPasteId()); @@ -378,7 +378,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase 'vizhash BLOB, ' . 'postdate INT );' ); - $this->assertInstanceOf('PrivateBin\\Data\\Database', Database::getInstance($this->_options)); + $this->assertInstanceOf('PrivateBin\\Data\\Database', new Database($this->_options)); // check if version number was upgraded in created configuration table $statement = $db->prepare('SELECT value FROM foo_config WHERE id LIKE ?'); diff --git a/tst/Data/FilesystemTest.php b/tst/Data/FilesystemTest.php index 684c294..bc9dcd0 100644 --- a/tst/Data/FilesystemTest.php +++ b/tst/Data/FilesystemTest.php @@ -15,7 +15,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase /* Setup Routine */ $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; $this->_invalidPath = $this->_path . DIRECTORY_SEPARATOR . 'bar'; - $this->_model = Filesystem::getInstance(array('dir' => $this->_path)); + $this->_model = new Filesystem(array('dir' => $this->_path)); if (!is_dir($this->_path)) { mkdir($this->_path); } diff --git a/tst/Data/GoogleCloudStorageTest.php b/tst/Data/GoogleCloudStorageTest.php index 3b101c4..5f1389a 100644 --- a/tst/Data/GoogleCloudStorageTest.php +++ b/tst/Data/GoogleCloudStorageTest.php @@ -26,7 +26,7 @@ class GoogleCloudStorageTest extends PHPUnit_Framework_TestCase public function setUp() { ini_set('error_log', stream_get_meta_data(tmpfile())['uri']); - $this->_model = GoogleCloudStorage::getInstance(array( + $this->_model = new GoogleCloudStorage(array( 'bucket' => self::$_bucket->name(), 'prefix' => 'pastes', )); diff --git a/tst/JsonApiTest.php b/tst/JsonApiTest.php index 4ad629e..3fa0665 100644 --- a/tst/JsonApiTest.php +++ b/tst/JsonApiTest.php @@ -18,7 +18,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase if (!is_dir($this->_path)) { mkdir($this->_path); } - $this->_model = Filesystem::getInstance(array('dir' => $this->_path)); + $this->_model = new Filesystem(array('dir' => $this->_path)); ServerSalt::setStore($this->_model); $_POST = array(); diff --git a/tst/ModelTest.php b/tst/ModelTest.php index a88e029..0d715c2 100644 --- a/tst/ModelTest.php +++ b/tst/ModelTest.php @@ -38,7 +38,7 @@ class ModelTest extends PHPUnit_Framework_TestCase ); Helper::confBackup(); Helper::createIniFile(CONF, $options); - ServerSalt::setStore(Database::getInstance($options['model_options'])); + ServerSalt::setStore(new Database($options['model_options'])); $this->_conf = new Configuration; $this->_model = new Model($this->_conf); $_SERVER['REMOTE_ADDR'] = '::1'; @@ -156,10 +156,10 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testCommentDefaults() { + $class = 'PrivateBin\\Data\\' . $this->_conf->getKey('class', 'model'); $comment = new Comment( $this->_conf, - forward_static_call( - 'PrivateBin\\Data\\' . $this->_conf->getKey('class', 'model') . '::getInstance', + new $class( $this->_conf->getSection('model_options') ) ); @@ -445,7 +445,7 @@ class ModelTest extends PHPUnit_Framework_TestCase public function testPurge() { $conf = new Configuration; - $store = Database::getInstance($conf->getSection('model_options')); + $store = new Database($conf->getSection('model_options')); $store->delete(Helper::getPasteId()); $expired = Helper::getPaste(2, array('expire_date' => 1344803344)); $paste = Helper::getPaste(2, array('expire_date' => time() + 3600)); diff --git a/tst/Persistence/PurgeLimiterTest.php b/tst/Persistence/PurgeLimiterTest.php index adb96ff..899576f 100644 --- a/tst/Persistence/PurgeLimiterTest.php +++ b/tst/Persistence/PurgeLimiterTest.php @@ -15,7 +15,7 @@ class PurgeLimiterTest extends PHPUnit_Framework_TestCase mkdir($this->_path); } PurgeLimiter::setStore( - Filesystem::getInstance(array('dir' => $this->_path)) + new Filesystem(array('dir' => $this->_path)) ); } diff --git a/tst/Persistence/ServerSaltTest.php b/tst/Persistence/ServerSaltTest.php index 3db5f7d..ce4fc68 100644 --- a/tst/Persistence/ServerSaltTest.php +++ b/tst/Persistence/ServerSaltTest.php @@ -21,7 +21,7 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase mkdir($this->_path); } ServerSalt::setStore( - Filesystem::getInstance(array('dir' => $this->_path)) + new Filesystem(array('dir' => $this->_path)) ); $this->_otherPath = $this->_path . DIRECTORY_SEPARATOR . 'foo'; @@ -44,17 +44,17 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase { // generating new salt ServerSalt::setStore( - Filesystem::getInstance(array('dir' => $this->_path)) + new Filesystem(array('dir' => $this->_path)) ); $salt = ServerSalt::get(); // try setting a different path and resetting it ServerSalt::setStore( - Filesystem::getInstance(array('dir' => $this->_otherPath)) + new Filesystem(array('dir' => $this->_otherPath)) ); $this->assertNotEquals($salt, ServerSalt::get()); ServerSalt::setStore( - Filesystem::getInstance(array('dir' => $this->_path)) + new Filesystem(array('dir' => $this->_path)) ); $this->assertEquals($salt, ServerSalt::get()); } @@ -63,7 +63,7 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase { // try setting an invalid path chmod($this->_invalidPath, 0000); - $store = Filesystem::getInstance(array('dir' => $this->_invalidPath)); + $store = new Filesystem(array('dir' => $this->_invalidPath)); ServerSalt::setStore($store); $salt = ServerSalt::get(); ServerSalt::setStore($store); @@ -76,7 +76,7 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase chmod($this->_invalidPath, 0700); file_put_contents($this->_invalidFile, ''); chmod($this->_invalidFile, 0000); - $store = Filesystem::getInstance(array('dir' => $this->_invalidPath)); + $store = new Filesystem(array('dir' => $this->_invalidPath)); ServerSalt::setStore($store); $salt = ServerSalt::get(); ServerSalt::setStore($store); @@ -93,7 +93,7 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase } file_put_contents($this->_invalidPath . DIRECTORY_SEPARATOR . '.htaccess', ''); chmod($this->_invalidPath, 0500); - $store = Filesystem::getInstance(array('dir' => $this->_invalidPath)); + $store = new Filesystem(array('dir' => $this->_invalidPath)); ServerSalt::setStore($store); $salt = ServerSalt::get(); ServerSalt::setStore($store); @@ -105,9 +105,9 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase // try creating an invalid path chmod($this->_invalidPath, 0000); ServerSalt::setStore( - Filesystem::getInstance(array('dir' => $this->_invalidPath . DIRECTORY_SEPARATOR . 'baz')) + new Filesystem(array('dir' => $this->_invalidPath . DIRECTORY_SEPARATOR . 'baz')) ); - $store = Filesystem::getInstance(array('dir' => $this->_invalidPath)); + $store = new Filesystem(array('dir' => $this->_invalidPath)); ServerSalt::setStore($store); $salt = ServerSalt::get(); ServerSalt::setStore($store); diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index 84e0bae..011b9ee 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -12,7 +12,7 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase { /* Setup Routine */ $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'trafficlimit'; - $store = Filesystem::getInstance(array('dir' => $this->_path)); + $store = new Filesystem(array('dir' => $this->_path)); ServerSalt::setStore($store); TrafficLimiter::setStore($store); } diff --git a/tst/Vizhash16x16Test.php b/tst/Vizhash16x16Test.php index abfb8c4..cdc0ad3 100644 --- a/tst/Vizhash16x16Test.php +++ b/tst/Vizhash16x16Test.php @@ -18,7 +18,7 @@ class Vizhash16x16Test extends PHPUnit_Framework_TestCase mkdir($this->_path); } $this->_file = $this->_path . DIRECTORY_SEPARATOR . 'vizhash.png'; - ServerSalt::setStore(Filesystem::getInstance(array('dir' => $this->_path))); + ServerSalt::setStore(new Filesystem(array('dir' => $this->_path))); } public function tearDown()