aboutsummaryrefslogtreecommitdiff
path: root/src/phorkie
diff options
context:
space:
mode:
authorChristian Weiske <cweiske@cweiske.de>2012-04-03 20:15:57 +0200
committerChristian Weiske <cweiske@cweiske.de>2012-04-03 20:15:57 +0200
commit2b4b34a76f42841e964a549fc64c02ba4f60a3f4 (patch)
treec83a973cae8d45e01384dabe4ad33dc40c09a5d1 /src/phorkie
parent7dcd592544ae0b55d0e205ff83631067a0d0aa6b (diff)
downloadphorkie-2b4b34a76f42841e964a549fc64c02ba4f60a3f4.tar.gz
phorkie-2b4b34a76f42841e964a549fc64c02ba4f60a3f4.zip
rename Phorkie to phorkie (lowercase)
Diffstat (limited to 'src/phorkie')
-rw-r--r--src/phorkie/Exception.php9
-rw-r--r--src/phorkie/Exception/Input.php17
-rw-r--r--src/phorkie/Exception/NotFound.php16
-rw-r--r--src/phorkie/File.php116
-rw-r--r--src/phorkie/HtmlHelper.php28
-rw-r--r--src/phorkie/Repositories.php55
-rw-r--r--src/phorkie/Repository.php157
-rw-r--r--src/phorkie/Repository/Post.php130
-rw-r--r--src/phorkie/Tools.php26
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