Merge "Follow-up I0b781c11 (2a55449): use User::getAutomaticGroups()."
[lhc/web/wiklou.git] / includes / upload / UploadFromChunks.php
index b091619..54a68af 100644 (file)
@@ -1,18 +1,38 @@
 <?php
 /**
- * Implements uploading from chunks
+ * Backend for uploading files from chunks.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
  *
  * @file
- * @ingroup upload
- * @author Michael Dale
+ * @ingroup Upload
  */
 
+/**
+ * Implements uploading from chunks
+ *
+ * @ingroup Upload
+ * @author Michael Dale
+ */
 class UploadFromChunks extends UploadFromFile {
        protected $mOffset, $mChunkIndex, $mFileKey, $mVirtualTempPath;
-       
+
        /**
         * Setup local pointers to stash, repo and user ( similar to UploadFromStash )
-        * 
+        *
         * @param $user User
         * @param $stash UploadStash
         * @param $repo FileRepo
@@ -57,12 +77,12 @@ class UploadFromChunks extends UploadFromFile {
 
                // Output a copy of this first to chunk 0 location:
                $status = $this->outputChunk( $this->mLocalFile->getPath() );
-               
+
                // Update db table to reflect initial "chunk" state 
                $this->updateChunkStatus();
                return $this->mLocalFile;
        }
-       
+
        /**
         * Continue chunk uploading
         */     
@@ -71,23 +91,23 @@ class UploadFromChunks extends UploadFromFile {
                $this->mUpload = $webRequestUpload;
                // Get the chunk status form the db: 
                $this->getChunkStatus();
-               
+
                $metadata = $this->stash->getMetadata( $key );
                $this->initializePathInfo( $name,
-                       $this->getRealPath ( $metadata['us_path'] ),
+                       $this->getRealPath( $metadata['us_path'] ),
                        $metadata['us_size'],
                        false
                );
        }
-       
+
        /**
         * Append the final chunk and ready file for parent::performUpload()
-        * @return void
+        * @return FileRepoStatus
         */
        public function concatenateChunks() {
                wfDebug( __METHOD__ . " concatenate {$this->mChunkIndex} chunks:" . 
-                                       $this->getOffset() . ' inx:' . $this->getChunkIndex() . "\n" );
-                                       
+                       $this->getOffset() . ' inx:' . $this->getChunkIndex() . "\n" );
+
                // Concatenate all the chunks to mVirtualTempPath
                $fileList = Array();
                // The first chunk is stored at the mVirtualTempPath path so we start on "chunk 1"
@@ -95,18 +115,43 @@ class UploadFromChunks extends UploadFromFile {
                        $fileList[] = $this->getVirtualChunkLocation( $i );
                }
 
-               // Concatinate into the mVirtualTempPath location;
-               $status = $this->repo->concatenate( $fileList,  $this->mVirtualTempPath, FileRepo::DELETE_SOURCE );
+               // Get the file extension from the last chunk
+               $ext = FileBackend::extensionFromPath( $this->mVirtualTempPath );
+               // Get a 0-byte temp file to perform the concatenation at
+               $tmpFile = TempFSFile::factory( 'chunkedupload_', $ext );
+               $tmpPath = $tmpFile
+                       ? $tmpFile->getPath()
+                       : false; // fail in concatenate()
+               // Concatenate the chunks at the temp file
+               $status = $this->repo->concatenate( $fileList, $tmpPath, FileRepo::DELETE_SOURCE );
                if( !$status->isOk() ){
                        return $status; 
                }
-               // Update the mTempPath variable ( for FileUpload or normal Stash to take over )  
-               $this->mTempPath = $this->getRealPath( $this->mVirtualTempPath );
+               // Update the mTempPath and mLocalFile
+               // ( for FileUpload or normal Stash to take over )  
+               $this->mTempPath = $tmpPath; // file system path
+               $this->mLocalFile = parent::stashFile();
+
                return $status;
        }
+
+       /**
+        * Perform the upload, then remove the temp copy afterward
+        * @param $comment string
+        * @param $pageText string
+        * @param $watch bool
+        * @param $user User
+        * @return Status
+        */
+       public function performUpload( $comment, $pageText, $watch, $user ) {
+               $rv = parent::performUpload( $comment, $pageText, $watch, $user );
+               return $rv;
+       }
+
        /**
         * Returns the virtual chunk location:  
-        * @param unknown_type $index
+        * @param $index
+        * @return string
         */
        function getVirtualChunkLocation( $index ){
                return $this->repo->getVirtualUrl( 'temp' ) . 
@@ -116,12 +161,13 @@ class UploadFromChunks extends UploadFromFile {
                                ) . 
                                $this->getChunkFileKey( $index );
        }
+
        /**
         * Add a chunk to the temporary directory
         *
-        * @param $chunkPath path to temporary chunk file
-        * @param $chunkSize size of the current chunk
-        * @param $offset offset of current chunk ( mutch match database chunk offset ) 
+        * @param $chunkPath string path to temporary chunk file
+        * @param $chunkSize int size of the current chunk
+        * @param $offset int offset of current chunk ( mutch match database chunk offset )
         * @return Status
         */
        public function addChunk( $chunkPath, $chunkSize, $offset ) {
@@ -148,14 +194,14 @@ class UploadFromChunks extends UploadFromFile {
                }
                return $status;
        }
-       
+
        /**
         * Update the chunk db table with the current status: 
         */
        private function updateChunkStatus(){
                wfDebug( __METHOD__ . " update chunk status for {$this->mFileKey} offset:" . 
                                        $this->getOffset() . ' inx:' . $this->getChunkIndex() . "\n" );
-               
+
                $dbw = $this->repo->getMasterDb();
                $dbw->update(
                        'uploadstash',
@@ -168,12 +214,15 @@ class UploadFromChunks extends UploadFromFile {
                        __METHOD__
                );
        }
+
        /**
         * Get the chunk db state and populate update relevant local values
         */
        private function getChunkStatus(){
-               $dbr = $this->repo->getSlaveDb();
-               $row = $dbr->selectRow(
+               // get Master db to avoid race conditions. 
+               // Otherwise, if chunk upload time < replag there will be spurious errors
+               $dbw = $this->repo->getMasterDb();
+               $row = $dbw->selectRow(
                        'uploadstash', 
                        array( 
                                'us_chunk_inx',
@@ -190,6 +239,7 @@ class UploadFromChunks extends UploadFromFile {
                        $this->mVirtualTempPath = $row->us_path;
                }
        }
+
        /**
         * Get the current Chunk index 
         * @return Integer index of the current chunk
@@ -200,7 +250,7 @@ class UploadFromChunks extends UploadFromFile {
                }
                return 0;
        }
-       
+
        /**
         * Gets the current offset in fromt the stashedupload table 
         * @return Integer current byte offset of the chunk file set 
@@ -211,19 +261,20 @@ class UploadFromChunks extends UploadFromFile {
                }
                return 0;
        }
-       
+
        /**
         * Output the chunk to disk
-        * 
-        * @param $chunk 
-        * @param unknown_type $path
+        *
+        * @param $chunkPath string
+        * @throws UploadChunkFileException
+        * @return FileRepoStatus
         */
        private function outputChunk( $chunkPath ){
                // Key is fileKey + chunk index
                $fileKey = $this->getChunkFileKey();
                
                // Store the chunk per its indexed fileKey: 
-               $hashPath = $this->repo->getHashPath( $fileKey );               
+               $hashPath = $this->repo->getHashPath( $fileKey );
                $storeStatus = $this->repo->store( $chunkPath, 'temp', "$hashPath$fileKey" );
                
                // Check for error in stashing the chunk:
@@ -237,10 +288,11 @@ class UploadFromChunks extends UploadFromFile {
                                        $error = array( 'unknown', 'no error recorded' );
                                }
                        }
-                       throw new UploadChunkFileException( "error storing file in '$path': " . implode( '; ', $error ) );
+                       throw new UploadChunkFileException( "error storing file in '$chunkPath': " . implode( '; ', $error ) );
                }
                return $storeStatus;
        }
+
        private function getChunkFileKey( $index = null ){
                if( $index === null ){
                        $index = $this->getChunkIndex();