Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImportableUploadRevisionImporter
0.00% covered (danger)
0.00%
0 / 82
0.00% covered (danger)
0.00%
0 / 5
420
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setNullRevisionCreation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 newNotOkStatus
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 import
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 1
182
 downloadSource
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3use MediaWiki\FileRepo\File\OldLocalFile;
4use MediaWiki\MediaWikiServices;
5use MediaWiki\User\User;
6use Psr\Log\LoggerInterface;
7use Wikimedia\FileBackend\FSFile\TempFSFile;
8use Wikimedia\Rdbms\IDBAccessObject;
9
10/**
11 * @since 1.31
12 */
13class ImportableUploadRevisionImporter implements UploadRevisionImporter {
14
15    private bool $enableUploads;
16    private LoggerInterface $logger;
17
18    private bool $shouldCreateNullRevision = true;
19
20    public function __construct(
21        $enableUploads,
22        LoggerInterface $logger
23    ) {
24        $this->enableUploads = $enableUploads;
25        $this->logger = $logger;
26    }
27
28    /**
29     * Setting this to false will deactivate the creation of a null revision as part of the upload
30     * process logging in LocalFile::recordUpload3, see T193621
31     *
32     * @param bool $shouldCreateNullRevision
33     */
34    public function setNullRevisionCreation( $shouldCreateNullRevision ) {
35        $this->shouldCreateNullRevision = $shouldCreateNullRevision;
36    }
37
38    /**
39     * @return StatusValue
40     */
41    private function newNotOkStatus() {
42        $statusValue = new StatusValue();
43        $statusValue->setOK( false );
44        return $statusValue;
45    }
46
47    /** @inheritDoc */
48    public function import( ImportableUploadRevision $importableRevision ) {
49        # Construct a file
50        $archiveName = $importableRevision->getArchiveName();
51        $localRepo = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo();
52        if ( $archiveName ) {
53            $this->logger->debug( __METHOD__ . ": Importing archived file as $archiveName" );
54            $file = OldLocalFile::newFromArchiveName( $importableRevision->getTitle(),
55                $localRepo, $archiveName );
56        } else {
57            $file = $localRepo->newFile( $importableRevision->getTitle() );
58            $file->load( IDBAccessObject::READ_LATEST );
59            $this->logger->debug( __METHOD__ . ': Importing new file as ' . $file->getName() );
60            if ( $file->exists() && $file->getTimestamp() > $importableRevision->getTimestamp() ) {
61                $archiveName = $importableRevision->getTimestamp() . '!' . $file->getName();
62                $file = OldLocalFile::newFromArchiveName( $importableRevision->getTitle(),
63                    $localRepo, $archiveName );
64                $this->logger->debug( __METHOD__ . ": File already exists; importing as $archiveName" );
65            }
66        }
67        if ( !$file ) {
68            $this->logger->debug( __METHOD__ . ': Bad file for ' . $importableRevision->getTitle() );
69            return $this->newNotOkStatus();
70        }
71
72        # Get the file source or download if necessary
73        $source = $importableRevision->getFileSrc();
74        $autoDeleteSource = $importableRevision->isTempSrc();
75        if ( $source === '' ) {
76            $source = $this->downloadSource( $importableRevision );
77            $autoDeleteSource = true;
78        }
79        if ( $source === '' ) {
80            $this->logger->debug( __METHOD__ . ": Could not fetch remote file." );
81            return $this->newNotOkStatus();
82        }
83
84        $tmpFile = new TempFSFile( $source );
85        if ( $autoDeleteSource ) {
86            $tmpFile->autocollect();
87        }
88
89        $sha1File = ltrim( sha1_file( $source ), '0' );
90        $sha1 = $importableRevision->getSha1();
91        if ( $sha1 && ( $sha1 !== $sha1File ) ) {
92            $this->logger->debug( __METHOD__ . ": Corrupt file $source." );
93            return $this->newNotOkStatus();
94        }
95
96        $user = $importableRevision->getUserObj()
97            ?: User::newFromName( $importableRevision->getUser(), false );
98
99        # Do the actual upload
100        if ( $file instanceof OldLocalFile ) {
101            $status = $file->uploadOld(
102                $source,
103                $importableRevision->getTimestamp(),
104                $importableRevision->getComment(),
105                $user
106            );
107        } else {
108            $flags = 0;
109            $status = $file->upload(
110                $source,
111                $importableRevision->getComment(),
112                $importableRevision->getComment(),
113                $flags,
114                false,
115                $importableRevision->getTimestamp(),
116                $user,
117                [],
118                $this->shouldCreateNullRevision
119            );
120        }
121
122        if ( $status->isGood() ) {
123            $this->logger->debug( __METHOD__ . ": Successful" );
124        } else {
125            $this->logger->debug( __METHOD__ . ': failed: ' . $status->getHTML() );
126        }
127
128        return $status;
129    }
130
131    /**
132     * @param ImportableUploadRevision $wikiRevision
133     *
134     * @return string|false
135     */
136    private function downloadSource( ImportableUploadRevision $wikiRevision ) {
137        if ( !$this->enableUploads ) {
138            return false;
139        }
140
141        $tempo = tempnam( wfTempDir(), 'download' );
142        $f = fopen( $tempo, 'wb' );
143        if ( !$f ) {
144            $this->logger->debug( "IMPORT: couldn't write to temp file $tempo" );
145            return false;
146        }
147
148        // @todo FIXME!
149        $src = $wikiRevision->getSrc();
150        $data = MediaWikiServices::getInstance()->getHttpRequestFactory()->
151            get( $src, [], __METHOD__ );
152        if ( !$data ) {
153            $this->logger->debug( "IMPORT: couldn't fetch source $src" );
154            fclose( $f );
155            unlink( $tempo );
156            return false;
157        }
158
159        fwrite( $f, $data );
160        fclose( $f );
161
162        return $tempo;
163    }
164
165}