diff options
| author | Christian Weiske <cweiske@cweiske.de> | 2012-04-03 20:15:57 +0200 |
|---|---|---|
| committer | Christian Weiske <cweiske@cweiske.de> | 2012-04-03 20:15:57 +0200 |
| commit | 2b4b34a76f42841e964a549fc64c02ba4f60a3f4 (patch) | |
| tree | c83a973cae8d45e01384dabe4ad33dc40c09a5d1 /src/phorkie | |
| parent | 7dcd592544ae0b55d0e205ff83631067a0d0aa6b (diff) | |
| download | phorkie-2b4b34a76f42841e964a549fc64c02ba4f60a3f4.tar.gz phorkie-2b4b34a76f42841e964a549fc64c02ba4f60a3f4.zip | |
rename Phorkie to phorkie (lowercase)
Diffstat (limited to 'src/phorkie')
| -rw-r--r-- | src/phorkie/Exception.php | 9 | ||||
| -rw-r--r-- | src/phorkie/Exception/Input.php | 17 | ||||
| -rw-r--r-- | src/phorkie/Exception/NotFound.php | 16 | ||||
| -rw-r--r-- | src/phorkie/File.php | 116 | ||||
| -rw-r--r-- | src/phorkie/HtmlHelper.php | 28 | ||||
| -rw-r--r-- | src/phorkie/Repositories.php | 55 | ||||
| -rw-r--r-- | src/phorkie/Repository.php | 157 | ||||
| -rw-r--r-- | src/phorkie/Repository/Post.php | 130 | ||||
| -rw-r--r-- | src/phorkie/Tools.php | 26 |
9 files changed, 554 insertions, 0 deletions
diff --git a/src/phorkie/Exception.php b/src/phorkie/Exception.php new file mode 100644 index 0000000..e553830 --- /dev/null +++ b/src/phorkie/Exception.php @@ -0,0 +1,9 @@ +<?php +namespace phorkie; + +class Exception extends \Exception +{ + public $httpStatusCode = 500; +} + +?> diff --git a/src/phorkie/Exception/Input.php b/src/phorkie/Exception/Input.php new file mode 100644 index 0000000..7f27b42 --- /dev/null +++ b/src/phorkie/Exception/Input.php @@ -0,0 +1,17 @@ +<?php +namespace phorkie; + +/** + * Input from e.g. the URL is invalid, like a non-numeric string when one was + * expected + */ +class Exception_Input extends Exception +{ + public function __construct($msg = '', $code = 0) + { + parent::__construct($msg, $code); + $this->httpStatusCode = 400; + } +} + +?> diff --git a/src/phorkie/Exception/NotFound.php b/src/phorkie/Exception/NotFound.php new file mode 100644 index 0000000..450b9f9 --- /dev/null +++ b/src/phorkie/Exception/NotFound.php @@ -0,0 +1,16 @@ +<?php +namespace phorkie; + +/** + * Something could not be found + */ +class Exception_NotFound extends Exception +{ + public function __construct($msg = '', $code = 0) + { + parent::__construct($msg, $code); + $this->httpStatusCode = 404; + } +} + +?> diff --git a/src/phorkie/File.php b/src/phorkie/File.php new file mode 100644 index 0000000..bad21e6 --- /dev/null +++ b/src/phorkie/File.php @@ -0,0 +1,116 @@ +<?php +namespace phorkie; + +class File +{ + /** + * Full path to the file + * + * @var string + */ + public $path; + + /** + * Repository this file belongs to + * + * @var string + */ + public $repo; + + public function __construct($path, Repository $repo = null) + { + $this->path = $path; + $this->repo = $repo; + } + + /** + * Get filename relative to the repository path + * + * @return string + */ + public function getFilename() + { + return basename($this->path); + } + + /** + * Return the full path to the file + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Get file extension without dot + * + * @return string + */ + public function getExt() + { + return substr($this->path, strrpos($this->path, '.') + 1); + } + + public function getContent() + { + return file_get_contents($this->path); + } + + public function getHighlightedContent() + { + /** + * Yes, geshi needs to be in your include path + * We use the mediawiki geshi extension package. + */ + require_once 'MediaWiki/geshi/geshi/geshi.php'; + $geshi = new \GeSHi($this->getContent(), $this->getGeshiType()); + $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS); + $geshi->set_header_type(GESHI_HEADER_DIV); + return $geshi->parse_code(); + } + + /** + * Get a link to the file + * + * @param string $type Link type. Supported are: + * - "raw" + * - "display" + * + * @return string + */ + public function getLink($type) + { + if ($type == 'raw') { + return '/' . $this->repo->id . '/raw/' . $this->getFilename(); + } + throw new Exception('Unknown type'); + } + + /** + * Returns the type of the file, as used by Geshi + * + * @return string + */ + public function getGeshiType() + { + $ext = $this->getExt(); + if (isset($GLOBALS['phorkie']['languages'][$ext]['geshi'])) { + $ext = $GLOBALS['phorkie']['languages'][$ext]['geshi']; + } + + return $ext; + } + + public function getMimeType() + { + $ext = $this->getExt(); + if (!isset($GLOBALS['phorkie']['languages'][$ext])) { + return null; + } + return $GLOBALS['phorkie']['languages'][$ext]['mime']; + } +} + +?>
\ No newline at end of file diff --git a/src/phorkie/HtmlHelper.php b/src/phorkie/HtmlHelper.php new file mode 100644 index 0000000..ebda58b --- /dev/null +++ b/src/phorkie/HtmlHelper.php @@ -0,0 +1,28 @@ +<?php +namespace phorkie; + +class HtmlHelper +{ + public function getLanguageOptions(File $file = null) + { + $html = ''; + $fileExt = null; + if ($file !== null) { + $fileExt = $file->getExt(); + } + foreach ($GLOBALS['phorkie']['languages'] as $ext => $arLang) { + if (isset($arLang['show']) && !$arLang['show']) { + continue; + } + $html .= sprintf( + '<option value="%s"%s>%s</option>', + $ext, + $fileExt == $ext ? ' selected="selected"' : '', + $arLang['title'] + ) . "\n"; + } + return $html; + } +} + +?>
\ No newline at end of file diff --git a/src/phorkie/Repositories.php b/src/phorkie/Repositories.php new file mode 100644 index 0000000..ef59dd7 --- /dev/null +++ b/src/phorkie/Repositories.php @@ -0,0 +1,55 @@ +<?php +namespace phorkie; + +class Repositories +{ + public function __construct() + { + $this->reposDir = $GLOBALS['phorkie']['cfg']['repos']; + } + + /** + * @return Repository + */ + public function createNew() + { + chdir($this->reposDir); + $dirs = glob('*', GLOB_ONLYDIR); + sort($dirs, SORT_NUMERIC); + $n = end($dirs) + 1; + unset($dirs); + + $dir = $this->reposDir . '/' . $n . '/'; + mkdir($dir, 0777);//FIXME + $r = new Repository(); + $r->id = $n; + $r->repoDir = $dir; + return $r; + } + + /** + * Get a list of repository objects + * + * @param integer $page Page number, beginning with 0 + * @param integer $perPage Number of repositories per page + * + * @return array Array of Repositories + */ + public function getList($page = 0, $perPage = 10) + { + chdir($this->reposDir); + $dirs = glob('*', GLOB_ONLYDIR); + sort($dirs, SORT_NUMERIC); + + $some = array_slice($dirs, $page * $perPage, $perPage); + $repos = array(); + foreach ($some as $oneDir) { + $r = new Repository(); + $r->loadById($oneDir); + $repos[] = $r; + } + return $repos; + } +} + +?> diff --git a/src/phorkie/Repository.php b/src/phorkie/Repository.php new file mode 100644 index 0000000..fef5656 --- /dev/null +++ b/src/phorkie/Repository.php @@ -0,0 +1,157 @@ +<?php +namespace phorkie; + + +class Repository +{ + /** + * Repository ID (number in repositories directory) + * + * @var integer + */ + public $id; + + /** + * Full path to the git repository + * + * @var string + */ + public $repoDir; + + /** + * Load Repository data from GET-Request + * + * @return void + * + * @throws Exception When something is wrong + */ + public function loadFromRequest() + { + if (!isset($_GET['id'])) { + throw new Exception_Input('Paste ID missing'); + } + if (!is_numeric($_GET['id'])) { + throw new Exception_Input('Paste ID not numeric'); + } + $this->id = (int)$_GET['id']; + + $repoDir = $GLOBALS['phorkie']['cfg']['repos'] . '/' . $this->id; + if (!is_dir($repoDir)) { + throw new Exception_NotFound('Paste not found'); + } + $this->repoDir = $repoDir; + } + + public function loadById($id) + { + if (!is_numeric($id)) { + throw new Exception_Input('Paste ID not numeric'); + } + $this->id = (int)$id; + + $repoDir = $GLOBALS['phorkie']['cfg']['repos'] . '/' . $this->id; + if (!is_dir($repoDir)) { + throw new Exception_NotFound('Paste not found'); + } + $this->repoDir = $repoDir; + } + + public function getVc() + { + return new \VersionControl_Git($this->repoDir); + } + + /** + * Loads the list of files in this repository + * + * @return File[] Array of files + */ + public function getFiles() + { + $files = glob($this->repoDir . '/*'); + $arFiles = array(); + foreach ($files as $path) { + $arFiles[] = new File($path, $this); + } + return $arFiles; + } + + public function getFileByName($name, $bHasToExist = true) + { + $base = basename($name); + if ($base != $name) { + throw new Exception('No directories supported for now'); + } + if ($name == '') { + throw new Exception_Input('Empty file name given'); + } + $path = $this->repoDir . '/' . $base; + if ($bHasToExist && !is_readable($path)) { + throw new Exception_Input('File does not exist'); + } + return new File($path, $this); + } + + public function hasFile($name) + { + try { + $this->getFileByName($name); + } catch (Exception $e) { + return false; + } + return true; + } + + /** + * Permanently deletes the paste repository without any way to get + * it back. + * + * @return boolean True if all went well, false if not + */ + public function delete() + { + return Tools::recursiveDelete($this->repoDir); + } + + public function getDescription() + { + if (!is_readable($this->repoDir . '/.git/description')) { + return null; + } + return file_get_contents($this->repoDir . '/.git/description'); + } + + public function setDescription($description) + { + file_put_contents($this->repoDir . '/.git/description', $description); + } + + /** + * Get a link to the repository + * + * @param string $type Link type. Supported are: + * - "edit" + * - "display" + * - "fork" + * + * @return string + */ + public function getLink($type) + { + if ($type == 'edit') { + return '/' . $this->id . '/edit'; + } else if ($type == 'display') { + return '/' . $this->id; + } else if ($type == 'fork') { + return '/' . $this->id . '/fork'; + } else if ($type == 'delete') { + return '/' . $this->id . '/delete'; + } else if ($type == 'delete-confirm') { + return '/' . $this->id . '/delete/confirm'; + } + throw new Exception('Unknown link type'); + } + +} + +?> diff --git a/src/phorkie/Repository/Post.php b/src/phorkie/Repository/Post.php new file mode 100644 index 0000000..627aa1f --- /dev/null +++ b/src/phorkie/Repository/Post.php @@ -0,0 +1,130 @@ +<?php +namespace phorkie; + +class Repository_Post +{ + public $repo; + + public function __construct(Repository $repo = null) + { + $this->repo = $repo; + } + + /** + * Processes the POST data, changes description and files + * + * @return boolean True if the post was successful + */ + public function process($postData) + { + if (!isset($postData['files'])) { + return false; + } + + if (!$this->repo) { + $this->repo = $this->createRepo(); + } + + $vc = $this->repo->getVc(); + $this->repo->setDescription($postData['description']); + + $bChanged = false; + foreach ($postData['files'] as $arFile) { + if ($arFile['content'] == '' && $arFile['name'] == '') { + //empty (new) file + continue; + } + + $orignalName = $this->sanitizeFilename($arFile['original_name']); + $name = $this->sanitizeFilename($arFile['name']); + + if ($name == '') { + $name = $this->getNextNumberedFile('phork') + . '.' . $arFile['type']; + } + + $bNew = false; + if (!isset($orignalName) || $orignalName == '') { + //new file + $bNew = true; + } else if (!$this->repo->hasFile($orignalName)) { + //unknown file + //FIXME: Show error message + continue; + } else if ($orignalName != $name) { + //FIXME: what to do with overwrites? + $vc->getCommand('mv') + ->addArgument($orignalName) + ->addArgument($name) + ->execute(); + $bChanged = true; + } + + $file = $this->repo->getFileByName($name, false); + if ($bNew || $file->getContent() != $arFile['content']) { + file_put_contents($file->getPath(), $arFile['content']); + $command = $vc->getCommand('add') + ->addArgument($file->getFilename()) + ->execute(); + $bChanged = true; + } + } + + if ($bChanged) { + $vc->getCommand('commit') + ->setOption('message', '') + ->setOption('allow-empty-message') + ->setOption('author', 'Anonymous <anonymous@phorkie>') + ->execute(); + } + + return true; + } + + public function createRepo() + { + $rs = new Repositories(); + $repo = $rs->createNew(); + $vc = $repo->getVc(); + $vc->initRepository(); + foreach (glob($repo->repoDir . '/.git/hooks/*') as $hookfile) { + unlink($hookfile); + } + return $repo; + } + + public function getNextNumberedFile($prefix) + { + $num = -1; + do { + ++$num; + $files = glob($this->repo->repoDir . '/' . $prefix . $num . '.*'); + } while (count($files)); + + return $prefix . $num; + } + + /** + * Removes malicious parts from a file name + * + * @param string $file File name from the user + * + * @return string Fixed and probably secure filename + */ + public function sanitizeFilename($file) + { + $file = trim($file); + $file = str_replace(array('\\', '//'), '/', $file); + $file = str_replace('/../', '/', $file); + if (substr($file, 0, 3) == '../') { + $file = substr($file, 3); + } + if (substr($file, 0, 1) == '../') { + $file = substr($file, 1); + } + + return $file; + } +} + +?> diff --git a/src/phorkie/Tools.php b/src/phorkie/Tools.php new file mode 100644 index 0000000..c6e4db5 --- /dev/null +++ b/src/phorkie/Tools.php @@ -0,0 +1,26 @@ +<?php +namespace phorkie; + + +class Tools +{ + public static function recursiveDelete($path) + { + if (!is_dir($path) || is_link($path)) { + return unlink($path); + } + foreach (scandir($path) as $file) { + if ($file == '.' || $file == '..') { + continue; + } + $filepath = $path . DIRECTORY_SEPARATOR . $file; + if (!static::recursiveDelete($filepath)) { + return false; + }; + } + return rmdir($path); + } + +} + +?>
\ No newline at end of file |
