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

/phase3/includes/upload/UploadFromUrl.php

https://github.com/sowawa/mediawiki-svn
PHP | 243 lines | 134 code | 31 blank | 78 comment | 21 complexity | a9cd3c8edfded7b98d5365f892622736 MD5 | raw file
  1. <?php
  2. /**
  3. * Implements uploading from a HTTP resource.
  4. *
  5. * @file
  6. * @ingroup upload
  7. * @author Bryan Tong Minh
  8. * @author Michael Dale
  9. */
  10. class UploadFromUrl extends UploadBase {
  11. protected $mAsync, $mUrl;
  12. protected $mIgnoreWarnings = true;
  13. protected $mTempPath;
  14. /**
  15. * Checks if the user is allowed to use the upload-by-URL feature. If the
  16. * user is allowed, pass on permissions checking to the parent.
  17. *
  18. * @param $user User
  19. */
  20. public static function isAllowed( $user ) {
  21. if ( !$user->isAllowed( 'upload_by_url' ) )
  22. return 'upload_by_url';
  23. return parent::isAllowed( $user );
  24. }
  25. /**
  26. * Checks if the upload from URL feature is enabled
  27. * @return bool
  28. */
  29. public static function isEnabled() {
  30. global $wgAllowCopyUploads;
  31. return $wgAllowCopyUploads && parent::isEnabled();
  32. }
  33. /**
  34. * Entry point for API upload
  35. *
  36. * @param $name string
  37. * @param $url string
  38. * @param $async mixed Whether the download should be performed
  39. * asynchronous. False for synchronous, async or async-leavemessage for
  40. * asynchronous download.
  41. */
  42. public function initialize( $name, $url, $async = false ) {
  43. global $wgAllowAsyncCopyUploads;
  44. $this->mUrl = $url;
  45. $this->mAsync = $wgAllowAsyncCopyUploads ? $async : false;
  46. if ( $async ) {
  47. throw new MWException( 'Asynchronous copy uploads are no longer possible as of r81612.' );
  48. }
  49. $tempPath = $this->mAsync ? null : $this->makeTemporaryFile();
  50. # File size and removeTempFile will be filled in later
  51. $this->initializePathInfo( $name, $tempPath, 0, false );
  52. }
  53. /**
  54. * Entry point for SpecialUpload
  55. * @param $request WebRequest object
  56. */
  57. public function initializeFromRequest( &$request ) {
  58. $desiredDestName = $request->getText( 'wpDestFile' );
  59. if ( !$desiredDestName )
  60. $desiredDestName = $request->getText( 'wpUploadFileURL' );
  61. return $this->initialize(
  62. $desiredDestName,
  63. trim( $request->getVal( 'wpUploadFileURL' ) ),
  64. false
  65. );
  66. }
  67. /**
  68. * @param $request WebRequest object
  69. */
  70. public static function isValidRequest( $request ) {
  71. global $wgUser;
  72. $url = $request->getVal( 'wpUploadFileURL' );
  73. return !empty( $url )
  74. && Http::isValidURI( $url )
  75. && $wgUser->isAllowed( 'upload_by_url' );
  76. }
  77. public function getSourceType() { return 'url'; }
  78. public function fetchFile() {
  79. if ( !Http::isValidURI( $this->mUrl ) ) {
  80. return Status::newFatal( 'http-invalid-url' );
  81. }
  82. if ( !$this->mAsync ) {
  83. return $this->reallyFetchFile();
  84. }
  85. return Status::newGood();
  86. }
  87. /**
  88. * Create a new temporary file in the URL subdirectory of wfTempDir().
  89. *
  90. * @return string Path to the file
  91. */
  92. protected function makeTemporaryFile() {
  93. return tempnam( wfTempDir(), 'URL' );
  94. }
  95. /**
  96. * Callback: save a chunk of the result of a HTTP request to the temporary file
  97. *
  98. * @param $req mixed
  99. * @param $buffer string
  100. * @return int number of bytes handled
  101. */
  102. public function saveTempFileChunk( $req, $buffer ) {
  103. $nbytes = fwrite( $this->mTmpHandle, $buffer );
  104. if ( $nbytes == strlen( $buffer ) ) {
  105. $this->mFileSize += $nbytes;
  106. } else {
  107. // Well... that's not good!
  108. fclose( $this->mTmpHandle );
  109. $this->mTmpHandle = false;
  110. }
  111. return $nbytes;
  112. }
  113. /**
  114. * Download the file, save it to the temporary file and update the file
  115. * size and set $mRemoveTempFile to true.
  116. */
  117. protected function reallyFetchFile() {
  118. if ( $this->mTempPath === false ) {
  119. return Status::newFatal( 'tmp-create-error' );
  120. }
  121. // Note the temporary file should already be created by makeTemporaryFile()
  122. $this->mTmpHandle = fopen( $this->mTempPath, 'wb' );
  123. if ( !$this->mTmpHandle ) {
  124. return Status::newFatal( 'tmp-create-error' );
  125. }
  126. $this->mRemoveTempFile = true;
  127. $this->mFileSize = 0;
  128. $req = MWHttpRequest::factory( $this->mUrl );
  129. $req->setCallback( array( $this, 'saveTempFileChunk' ) );
  130. $status = $req->execute();
  131. if ( $this->mTmpHandle ) {
  132. // File got written ok...
  133. fclose( $this->mTmpHandle );
  134. $this->mTmpHandle = null;
  135. } else {
  136. // We encountered a write error during the download...
  137. return Status::newFatal( 'tmp-write-error' );
  138. }
  139. if ( !$status->isOk() ) {
  140. return $status;
  141. }
  142. return $status;
  143. }
  144. /**
  145. * Wrapper around the parent function in order to defer verifying the
  146. * upload until the file really has been fetched.
  147. */
  148. public function verifyUpload() {
  149. if ( $this->mAsync ) {
  150. return array( 'status' => UploadBase::OK );
  151. }
  152. return parent::verifyUpload();
  153. }
  154. /**
  155. * Wrapper around the parent function in order to defer checking warnings
  156. * until the file really has been fetched.
  157. */
  158. public function checkWarnings() {
  159. if ( $this->mAsync ) {
  160. $this->mIgnoreWarnings = false;
  161. return array();
  162. }
  163. return parent::checkWarnings();
  164. }
  165. /**
  166. * Wrapper around the parent function in order to defer checking protection
  167. * until we are sure that the file can actually be uploaded
  168. */
  169. public function verifyTitlePermissions( $user ) {
  170. if ( $this->mAsync ) {
  171. return true;
  172. }
  173. return parent::verifyTitlePermissions( $user );
  174. }
  175. /**
  176. * Wrapper around the parent function in order to defer uploading to the
  177. * job queue for asynchronous uploads
  178. */
  179. public function performUpload( $comment, $pageText, $watch, $user ) {
  180. if ( $this->mAsync ) {
  181. $sessionKey = $this->insertJob( $comment, $pageText, $watch, $user );
  182. $status = new Status;
  183. $status->error( 'async', $sessionKey );
  184. return $status;
  185. }
  186. return parent::performUpload( $comment, $pageText, $watch, $user );
  187. }
  188. /**
  189. * @param $comment
  190. * @param $pageText
  191. * @param $watch
  192. * @param $user User
  193. * @return
  194. */
  195. protected function insertJob( $comment, $pageText, $watch, $user ) {
  196. $sessionKey = $this->stashSession();
  197. $job = new UploadFromUrlJob( $this->getTitle(), array(
  198. 'url' => $this->mUrl,
  199. 'comment' => $comment,
  200. 'pageText' => $pageText,
  201. 'watch' => $watch,
  202. 'userName' => $user->getName(),
  203. 'leaveMessage' => $this->mAsync == 'async-leavemessage',
  204. 'ignoreWarnings' => $this->mIgnoreWarnings,
  205. 'sessionId' => session_id(),
  206. 'sessionKey' => $sessionKey,
  207. ) );
  208. $job->initializeSessionData();
  209. $job->insert();
  210. return $sessionKey;
  211. }
  212. }