PageRenderTime 77ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/www/index.php

https://github.com/scoates/blogcollab
PHP | 211 lines | 117 code | 19 blank | 75 comment | 36 complexity | a9e3738275076b22c5700b9490dc1399 MD5 | raw file
  1. <?php
  2. /**
  3. * Really simple blog collaboration system.
  4. * The code is nothing special (I whipped it up in an hour or so, one day, and
  5. * spent a whole bunch more time making it ready for public consumption), but
  6. * the app is really useful for collaboration/preview.
  7. *
  8. * Put user templates in ../templates/{username}
  9. * Put content in ../content/{username}/{Title Goes Here}.html
  10. */
  11. /**
  12. * Authentication constants
  13. */
  14. define('AUTH_USER', 'collab'); // set to false to skip auth check
  15. define('AUTH_PASS', 'd5029374377771fd628239fd1f4e9d02'); // md5('collab');
  16. $AUTHENTICATED = false;
  17. /**
  18. * Library code
  19. */
  20. require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'blogcollab.php';
  21. /**
  22. * Check auth
  23. */
  24. if (AUTH_USER) {
  25. if (
  26. isset($_SERVER['PHP_AUTH_USER'])
  27. && AUTH_USER == $_SERVER['PHP_AUTH_USER']
  28. && isset($_SERVER['PHP_AUTH_PW'])
  29. && AUTH_PASS == md5($_SERVER['PHP_AUTH_PW'])
  30. ) {
  31. $AUTHENTICATED = true;
  32. } else {
  33. $AUTHENTICATED = false;
  34. if (isset($_GET['dologin']) && $_GET['dologin']) {
  35. header('WWW-Authenticate: Basic realm="Blogcollab"');
  36. header('HTTP/1.0 401 Unauthorized');
  37. exit;
  38. }
  39. }
  40. } else {
  41. // AUTH_USER is false, pretend we're authenticated
  42. $AUTHENTICATED = true;
  43. }
  44. /**
  45. * Ensure we're in UTF-8.. Unicode is haaaaard.
  46. */
  47. header('Content-type: text/html;charset=UTF-8');
  48. /**
  49. * When not authenticated, show the instructions
  50. */
  51. if (!$AUTHENTICATED) {
  52. render('_instructions', array());
  53. exit();
  54. }
  55. /**
  56. * Collect users
  57. */
  58. $users = array();
  59. foreach (new DirectoryIterator(USERDIR) as $fi) {
  60. if (!$fi->isDot()) {
  61. $users[] = $fi->getFilename();
  62. }
  63. }
  64. /**
  65. * $who will be the current user, if valid
  66. */
  67. $who = null;
  68. /**
  69. * $entry will be the currently-selected template, if valid and set
  70. */
  71. $entry = null;
  72. /**
  73. * Split out the path
  74. */
  75. if (isset($_SERVER['PATH_INFO'])) {
  76. // apache
  77. $parts = explode('/', preg_replace('/\?.*$/', '', $_SERVER['PATH_INFO']));
  78. } elseif (isset($_SERVER['REQUEST_URI'])) {
  79. // nginx (orchestra.io)
  80. // massage the script name's directory out of the path
  81. $requestUri = preg_replace('/\?.*$/', '', $_SERVER['REQUEST_URI']);
  82. $scriptDir = dirname($_SERVER['SCRIPT_NAME']);
  83. if (isset($_SERVER['SCRIPT_NAME']) && 0 === strpos($requestUri, $scriptDir)) {
  84. $requestUri = substr($requestUri, strlen($scriptDir));
  85. }
  86. $parts = explode('/', urldecode($requestUri));
  87. } else {
  88. $parts = array();
  89. }
  90. if (isset($parts[0]) && '' === $parts[0]) {
  91. // trim off the first (empty) element
  92. array_shift($parts);
  93. }
  94. $size = count($parts);
  95. if ($size > 0) {
  96. $who = $parts[0];
  97. }
  98. if ($size > 1) {
  99. $entry = $parts[1];
  100. }
  101. /**
  102. * Ensure there are no path shenanigans
  103. */
  104. if (false !== strpos($who, '../') || false !== strpos($entry, '../')) {
  105. do_404();
  106. $title = $content = 'Invalid path';
  107. render(null, compact('content', 'title'));
  108. }
  109. /**
  110. * Trap invalid users -> 404
  111. */
  112. if ($who && !in_array($who, $users)) {
  113. do_404();
  114. $content = 'invalid user <a href="' . escape($_SERVER['SCRIPT_NAME']) . '">try again?</a>';
  115. $title = 'Invalid user';
  116. render(null, compact('content', 'title'));
  117. }
  118. /**
  119. * no user or entry; have the viewer select a user
  120. */
  121. if (!$who) {
  122. $title = 'Select a user';
  123. render('_users', compact('title', 'users'));
  124. }
  125. /**
  126. * visitor has selected a user, but not an entry
  127. */
  128. $contentdir = USERDIR . DIRECTORY_SEPARATOR . $who;
  129. if (!$entry) {
  130. if (!file_exists($contentdir)) {
  131. $content = "User " . escape($who) . " has no content directory.";
  132. $title = 'Error';
  133. render(null, compact('title', 'content'));
  134. }
  135. $entries = array();
  136. foreach (new DirectoryIterator($contentdir) as $fi) {
  137. if (!$fi->isDot()) {
  138. $fName = $fi->getFilename();
  139. if ($fName[0] != '.') { // ignore hidden files
  140. $entries[] = $fName;
  141. }
  142. }
  143. }
  144. $title = 'Select an entry for ' . escape($who);
  145. $user = $who;
  146. render('_entries', compact('title', 'entries', 'user'));
  147. }
  148. /**
  149. * collect templates
  150. */
  151. $templates = array();
  152. foreach (new DirectoryIterator(TEMPLATEDIR) as $fi) {
  153. if (!$fi->isDot()) {
  154. $name = $fi->getFilename();
  155. if ($name[0] != '_') {
  156. $templates[] = $name;
  157. }
  158. }
  159. }
  160. /**
  161. * determine display template
  162. */
  163. $displayTemplate = $who;
  164. if (!in_array($displayTemplate . '.html.php', $templates)) {
  165. $displayTemplate = null;
  166. }
  167. /**
  168. * validate requested content
  169. */
  170. $contentFile = $contentdir . DIRECTORY_SEPARATOR . $entry;
  171. if (!is_readable($contentFile)) {
  172. do_404();
  173. $title = 'Entry not found';
  174. $content = $title;
  175. render(null, compact('title', 'content'));
  176. }
  177. /**
  178. * actually load content from disk (the repository)
  179. *
  180. * Note: this content is often intentionally not escaped (depends on the
  181. * template), so it opens the door to XSS and other nastiness. The assumption
  182. * here is that authors (committers) are to be trusted; visitors are not
  183. */
  184. $content = file_get_contents($contentFile);
  185. /**
  186. * Set title from the file name
  187. */
  188. $title = escape(substr($entry, 0, strrpos($entry, '.')));
  189. /**
  190. * and finally, render the template
  191. */
  192. render($displayTemplate, compact('title', 'content'), false);