#!/usr/bin/env php _store->exists($pasteId)) { self::_error('given ID does not exist, has expired or was already deleted', 6); } $this->_store->delete($pasteId); if ($this->_store->exists($pasteId)) { self::_error('document ID exists after deletion, permission problem?', 7); } exit("document $pasteId successfully deleted" . PHP_EOL); } /** * deletes all stored documents (regardless of expiration) * * @access private */ private function _delete_all() { $ids = $this->_store->getAllPastes(); foreach ($ids as $pasteid) { echo "Deleting document ID: $pasteid" . PHP_EOL; $this->_store->delete($pasteid); } exit("All documents successfully deleted" . PHP_EOL); } /** * deletes all unsupported v1 documents (regardless of expiration) * * @access private */ private function _delete_v1() { $ids = $this->_store->getAllPastes(); foreach ($ids as $pasteid) { try { $paste = $this->_store->read($pasteid); } catch (Exception $e) { echo "Error reading document {$pasteid}: ", $e->getMessage(), PHP_EOL; } if (array_key_exists('adata', $paste)) { continue; } echo "Deleting v1 document ID: $pasteid" . PHP_EOL; $this->_store->delete($pasteid); } exit("All unsupported legacy v1 documents successfully deleted" . PHP_EOL); } /** * removes empty directories, if current storage model uses Filesystem * * @access private */ private function _empty_dirs() { if ($this->_conf->getKey('class', 'model') !== 'Filesystem') { self::_error('instance not using Filesystem storage, no directories to empty', 4); } $dir = $this->_conf->getKey('dir', 'model_options'); passthru("find $dir -type d -empty -delete", $code); exit($code); } /** * display a message on STDERR and exits * * @access private * @static * @param string $message * @param int $code optional, defaults to 1 */ private static function _error($message, $code = 1) { self::_error_echo($message); exit($code); } /** * display a message on STDERR * * @access private * @static * @param string $message */ private static function _error_echo($message) { fwrite(STDERR, 'Error: ' . $message . PHP_EOL); } /** * display usage help on STDOUT and exits * * @access private * @static * @param int $code optional, defaults to 0 */ private static function _help($code = 0) { echo <<<'EOT' Usage: administration [--delete | --delete-all | --delete-v1 | --empty-dirs | --help | --list-ids | --purge | --statistics] Options: -d, --delete deletes the requested document ID --delete-all deletes all documents --delete-v1 deletes all unsupported v1 documents -e, --empty-dirs removes empty directories (only if Filesystem storage is configured) -h, --help displays this help message -l, --list-ids lists all document IDs -p, --purge purge all expired documents -s, --statistics reads all stored documents and reports statistics EOT, PHP_EOL; exit($code); } /** * lists all stored document IDs * * @access private */ private function _list_ids() { $ids = $this->_store->getAllPastes(); foreach ($ids as $pasteid) { echo $pasteid, PHP_EOL; } exit; } /** * return option for given short or long keyname, if it got set * * @access private * @static * @param string $short * @param string $long * @return string|null */ private function _option($short, $long) { foreach (array($short, $long) as $key) { if (array_key_exists($key, $this->_opts)) { return $this->_opts[$key]; } } return null; } /** * initialize options from given argument array * * @access private * @static * @param array $arguments */ private function _options_initialize($arguments) { if ($arguments > 3) { self::_error_echo('too many arguments given'); echo PHP_EOL; self::_help(1); } if ($arguments < 2) { self::_error_echo('missing arguments'); echo PHP_EOL; self::_help(2); } $this->_opts = getopt('hd:elps', array('help', 'delete:', 'delete-all', 'delete-v1', 'empty-dirs', 'list-ids', 'purge', 'statistics')); if (!$this->_opts) { self::_error_echo('unsupported arguments given'); echo PHP_EOL; self::_help(3); } } /** * reads all stored documents and reports statistics * * @access public */ private function _statistics() { $counters = array( 'burn' => 0, 'damaged' => 0, 'discussion' => 0, 'expired' => 0, 'legacy' => 0, 'md' => 0, 'percent' => 1, 'plain' => 0, 'progress' => 0, 'syntax' => 0, 'total' => 0, 'unknown' => 0, ); $time = time(); $ids = $this->_store->getAllPastes(); $counters['total'] = count($ids); $dots = $counters['total'] < 100 ? 10 : ( $counters['total'] < 1000 ? 50 : 100 ); $percentages = $counters['total'] < 100 ? 0 : ( $counters['total'] < 1000 ? 4 : 10 ); echo "Total:\t\t\t{$counters['total']}", PHP_EOL; foreach ($ids as $pasteid) { try { $paste = $this->_store->read($pasteid); } catch (Exception $e) { echo "Error reading document {$pasteid}: ", $e->getMessage(), PHP_EOL; ++$counters['damaged']; } ++$counters['progress']; if ( array_key_exists('expire_date', $paste['meta']) && $paste['meta']['expire_date'] < $time ) { ++$counters['expired']; } if (array_key_exists('adata', $paste)) { $format = $paste['adata'][Paste::ADATA_FORMATTER]; $discussion = $paste['adata'][Paste::ADATA_OPEN_DISCUSSION]; $burn = $paste['adata'][Paste::ADATA_BURN_AFTER_READING]; } else { echo "Unsupported v1 document ", $pasteid, PHP_EOL; ++$counters['legacy']; } if ($format === 'plaintext') { ++$counters['plain']; } elseif ($format === 'syntaxhighlighting') { ++$counters['syntax']; } elseif ($format === 'markdown') { ++$counters['md']; } else { ++$counters['unknown']; } $counters['discussion'] += (int) $discussion; $counters['burn'] += (int) $burn; // display progress if ($counters['progress'] % $dots === 0) { echo '.'; if ($percentages) { $progress = $percentages / $counters['total'] * $counters['progress']; if ($progress >= $counters['percent']) { printf(' %d%% ', 100 / $percentages * $progress); ++$counters['percent']; } } } } echo PHP_EOL, << 0) { echo "Legacy v1:\t\t{$counters['legacy']}", PHP_EOL; } if ($counters['damaged'] > 0) { echo "Damaged:\t\t{$counters['damaged']}", PHP_EOL; } if ($counters['unknown'] > 0) { echo "Unknown format:\t\t{$counters['unknown']}", PHP_EOL; } } /** * constructor * * initializes and runs administrative tasks * * @access public */ public function __construct() { $this->_options_initialize($_SERVER['argc']); if ($this->_option('h', 'help') !== null) { self::_help(); } $this->_conf = new Configuration; if ($this->_option('e', 'empty-dirs') !== null) { $this->_empty_dirs(); } $class = 'PrivateBin\\Data\\' . $this->_conf->getKey('class', 'model'); $this->_store = new $class($this->_conf->getSection('model_options')); if (($pasteId = $this->_option('d', 'delete')) !== null) { $this->_delete($pasteId); } if ($this->_option(null, 'delete-all') !== null) { $this->_delete_all(); } if ($this->_option(null, 'delete-v1') !== null) { $this->_delete_v1(); } if ($this->_option('l', 'list-ids') !== null) { $this->_list_ids(); } if ($this->_option('p', 'purge') !== null) { try { $this->_store->purge(PHP_INT_MAX); } catch (Exception $e) { echo 'Error purging documents: ', $e->getMessage(), PHP_EOL, 'Run the statistics to find damaged document IDs and either delete them or restore them from backup.', PHP_EOL; } exit('purging of expired documents concluded' . PHP_EOL); } if ($this->_option('s', 'statistics') !== null) { $this->_statistics(); } } } new Administration();