Controller_Snapshot.class.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. <?php
  2. /**
  3. * Controller for getting a snapshot
  4. *
  5. * @author Christopher Han <xiphux@gmail.com>
  6. * @copyright Copyright (c) 2010 Christopher Han
  7. * @package GitPHP
  8. * @subpackage Controller
  9. */
  10. class GitPHP_Controller_Snapshot extends GitPHP_ControllerBase
  11. {
  12. /**
  13. * Stores the archive object
  14. *
  15. * @var GitPHP_Archive
  16. */
  17. private $archive = null;
  18. /**
  19. * Snapshot cache directory
  20. *
  21. * @var string
  22. */
  23. private $cacheDir = null;
  24. /**
  25. * Initialize controller
  26. */
  27. public function Initialize()
  28. {
  29. $this->InitializeConfig();
  30. $this->InitializeUserList();
  31. $this->InitializeGitExe();
  32. $this->InitializeProjectList();
  33. // HACK: this needs to be done early because the snapshot controller modifies the headers before opening the archive
  34. if (!GitPHP_Util::FunctionAllowed('popen'))
  35. throw new GitPHP_DisabledFunctionException('popen');
  36. if (isset($this->params['project'])) {
  37. $project = $this->projectList->GetProject($this->params['project']);
  38. if (!$project) {
  39. throw new GitPHP_InvalidProjectParameterException($this->params['project']);
  40. }
  41. if ($this->userList && ($this->userList->GetCount() > 0)) {
  42. if (!$project->UserCanAccess((!empty($_SESSION['gitphpuser']) ? $_SESSION['gitphpuser'] : null))) {
  43. throw new GitPHP_UnauthorizedProjectException($this->params['project']);
  44. }
  45. }
  46. $this->project = $project->GetProject();
  47. }
  48. if (!$this->project) {
  49. throw new GitPHP_MissingProjectParameterException();
  50. }
  51. $this->preserveWhitespace = true;
  52. if (empty($this->params['format']))
  53. $this->params['format'] = $this->config->GetValue('compressformat');
  54. $this->InitializeArchive();
  55. if ($this->config->GetValue('cache')) {
  56. $this->cacheDir = GITPHP_CACHEDIR . 'snapshots/';
  57. if (file_exists($this->cacheDir)) {
  58. if (!is_dir($this->cacheDir)) {
  59. throw new Exception($this->cacheDir . ' exists but is not a directory');
  60. } else if (!is_writable($this->cacheDir)) {
  61. throw new Exception($this->cacheDir . ' is not writable');
  62. }
  63. } else {
  64. if (!mkdir($this->cacheDir, 0777))
  65. throw new Exception($this->cacheDir . ' could not be created');
  66. chmod($this->cacheDir, 0777);
  67. }
  68. }
  69. }
  70. /**
  71. * Gets the template for this controller
  72. *
  73. * @return string template filename
  74. */
  75. protected function GetTemplate()
  76. {
  77. }
  78. /**
  79. * Gets the cache key for this controller
  80. *
  81. * @return string cache key
  82. */
  83. protected function GetCacheKey()
  84. {
  85. return (isset($this->params['hash']) ? $this->params['hash'] : '') . '|' . (isset($this->params['file']) ? $this->params['file'] : '') . '|' . (isset($this->params['prefix']) ? $this->params['prefix'] : '') . '|' . $this->params['format'];
  86. }
  87. /**
  88. * Gets the name of this controller's action
  89. *
  90. * @param boolean $local true if caller wants the localized action name
  91. * @return string action name
  92. */
  93. public function GetName($local = false)
  94. {
  95. if ($local && $this->resource) {
  96. return $this->resource->translate('snapshot');
  97. }
  98. return 'snapshot';
  99. }
  100. /**
  101. * Loads headers for this template
  102. */
  103. protected function LoadHeaders()
  104. {
  105. $mimetype = $this->archive->GetMimeType();
  106. if (!empty($mimetype))
  107. $this->headers[] = 'Content-Type: ' . $mimetype;
  108. $this->headers[] = 'Content-Disposition: attachment; filename=' . $this->archive->GetFilename();
  109. if ($this->config->GetValue('cache')) {
  110. $cachedfile = $this->cacheDir . $this->CachedSnapshotFile();
  111. if (is_readable($cachedfile)) {
  112. $size = filesize($cachedfile);
  113. if ($size !== false)
  114. $this->headers[] = 'Content-Length: ' . $size;
  115. }
  116. }
  117. }
  118. /**
  119. * Loads data for this template
  120. */
  121. protected function LoadData()
  122. {
  123. }
  124. /**
  125. * Render this controller
  126. */
  127. public function Render()
  128. {
  129. $cache = $this->config->GetValue('cache');
  130. $cachedfile = null;
  131. if ($cache) {
  132. $cachedfile = $this->CachedSnapshotFile();
  133. $cachedfilepath = $this->cacheDir . $cachedfile;
  134. if (is_readable($cachedfilepath)) {
  135. $cachehandle = fopen($cachedfilepath, 'rb');
  136. if ($cachehandle) {
  137. while (!feof($cachehandle)) {
  138. print fread($cachehandle, 1048576);
  139. flush();
  140. }
  141. fclose($cachehandle);
  142. return;
  143. }
  144. }
  145. }
  146. if ($this->archive->Open()) {
  147. $tmpcachefile = null;
  148. $cachehandle = null;
  149. if ($cache && !empty($cachedfile)) {
  150. // write cached file too
  151. $pid = 0;
  152. if (function_exists('posix_getpid'))
  153. $pid = posix_getpid();
  154. else
  155. $pid = rand();
  156. $tmpcachefile = 'tmp-' . $pid . '-' . $cachedfile;
  157. $cachehandle = fopen($this->cacheDir . $tmpcachefile, 'wb');
  158. }
  159. while (($data = $this->archive->Read()) !== false) {
  160. print $data;
  161. flush();
  162. if ($cache && $cachehandle) {
  163. fwrite($cachehandle, $data);
  164. }
  165. }
  166. $this->archive->Close();
  167. if ($cachehandle) {
  168. fclose($cachehandle);
  169. sleep(1);
  170. rename($this->cacheDir . $tmpcachefile, $this->cacheDir . $cachedfile);
  171. }
  172. }
  173. }
  174. /**
  175. * Initialize archive for reading
  176. */
  177. private function InitializeArchive()
  178. {
  179. $strategy = null;
  180. if ($this->params['format'] == GITPHP_COMPRESS_TAR) {
  181. $strategy = new GitPHP_Archive_Tar();
  182. } else if ($this->params['format'] == GITPHP_COMPRESS_BZ2) {
  183. $strategy = new GitPHP_Archive_Bzip2($this->config->GetValue('compresslevel'));
  184. if (!$strategy->Valid())
  185. $strategy = new GitPHP_Archive_Tar();
  186. } else if ($this->params['format'] == GITPHP_COMPRESS_GZ) {
  187. $strategy = new GitPHP_Archive_Gzip($this->config->GetValue('compresslevel'));
  188. if (!$strategy->Valid())
  189. $strategy = new GitPHP_Archive_Tar();
  190. } else if ($this->params['format'] == GITPHP_COMPRESS_ZIP) {
  191. $strategy = new GitPHP_Archive_Zip($this->config->GetValue('compresslevel'));
  192. if (!$strategy->Valid())
  193. $strategy = new GitPHP_Archive_Tar();
  194. }
  195. $strategy->SetExe($this->exe);
  196. $this->archive = new GitPHP_Archive($this->GetProject(), null, $strategy, (isset($this->params['file']) ? $this->params['file'] : ''), (isset($this->params['prefix']) ? $this->params['prefix'] : ''));
  197. $commit = null;
  198. if (!isset($this->params['hash']))
  199. $commit = $this->GetProject()->GetHeadCommit();
  200. else
  201. $commit = $this->GetProject()->GetCommit($this->params['hash']);
  202. $this->archive->SetObject($commit);
  203. }
  204. /**
  205. * Gets the cached snapshot file name
  206. *
  207. * @return string cached file name
  208. */
  209. private function CachedSnapshotFile()
  210. {
  211. $key = ($this->archive->GetObject() ? $this->archive->GetObject()->GetHash() : '') . '|' . (isset($this->params['file']) ? $this->params['file'] : '') . '|' . (isset($this->params['prefix']) ? $this->params['prefix'] : '');
  212. $cachefile = sha1($key) . '-' . $this->archive->GetFilename();
  213. return $cachefile;
  214. }
  215. }