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