Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
UploadStashFile
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 13
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
30
 getSha1
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getDescriptionUrl
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getThumbPath
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 thumbName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSpecialUrl
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getThumbUrl
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getUrlName
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getUrl
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getFullUrl
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFileKey
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 remove
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 exists
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Upload;
8
9use MediaWiki\FileRepo\File\UnregisteredLocalFile;
10use MediaWiki\FileRepo\FileRepo;
11use MediaWiki\FileRepo\LocalRepo;
12use MediaWiki\SpecialPage\SpecialPage;
13use MediaWiki\Upload\Exception\UploadStashBadPathException;
14use MediaWiki\Upload\Exception\UploadStashFileNotFoundException;
15
16/**
17 * @ingroup Upload
18 */
19class UploadStashFile extends UnregisteredLocalFile {
20    /** @var string */
21    private $fileKey;
22    /** @var string|null Lazy set as in-memory cache */
23    private $urlName;
24    /** @var string|null Lazy set as in-memory cache */
25    protected $url;
26    /** @var string|null */
27    private $sha1;
28
29    /**
30     * A LocalFile wrapper around a file that has been temporarily stashed,
31     * so we can do things like create thumbnails for it. Arguably
32     * UnregisteredLocalFile should be handling its own file repo but that
33     * class is a bit retarded currently.
34     *
35     * @param LocalRepo $repo Repository where we should find the path
36     * @param string $path Path to file
37     * @param string $key Key to store the path and any stashed data under
38     * @param string|null $sha1 SHA1 of file. Will calculate if not set
39     * @param string|false $mime Mime type of file. Will calculate if not set
40     * @throws UploadStashBadPathException
41     * @throws UploadStashFileNotFoundException
42     */
43    public function __construct( $repo, $path, $key, $sha1 = null, $mime = false ) {
44        $this->fileKey = $key;
45        $this->sha1 = $sha1;
46
47        // resolve mwrepo:// urls
48        if ( FileRepo::isVirtualUrl( $path ) ) {
49            $path = $repo->resolveVirtualUrl( $path );
50        } else {
51            // check if path appears to be correct, no parent traversals,
52            // and is in this repo's temp zone.
53            $repoTempPath = $repo->getZonePath( 'temp' );
54            if ( ( !$repo->validateFilename( $path ) ) ||
55                !str_starts_with( $path, $repoTempPath )
56            ) {
57                wfDebug( "UploadStash: tried to construct an UploadStashFile "
58                    . "from a file that should already exist at '$path', but path is not valid" );
59                throw new UploadStashBadPathException(
60                    wfMessage( 'uploadstash-bad-path-invalid' )
61                );
62            }
63
64            // check if path exists! and is a plain file.
65            if ( !$repo->fileExists( $path ) ) {
66                wfDebug( "UploadStash: tried to construct an UploadStashFile from "
67                    . "a file that should already exist at '$path', but path is not found" );
68                throw new UploadStashFileNotFoundException(
69                    wfMessage( 'uploadstash-file-not-found-not-exists' )
70                );
71            }
72        }
73
74        parent::__construct( false, $repo, $path, $mime );
75
76        $this->name = basename( $this->path );
77    }
78
79    /**
80     * Get the SHA-1 base 36 hash
81     *
82     * This can be expensive on large files, so cache the value
83     * @return string|false
84     */
85    public function getSha1() {
86        if ( !$this->sha1 ) {
87            $this->sha1 = parent::getSha1();
88        }
89        return $this->sha1;
90    }
91
92    /**
93     * A method needed by the file transforming and scaling routines in File.php
94     * We do not necessarily care about doing the description at this point
95     * However, we also can't return the empty string, as the rest of MediaWiki
96     * demands this (and calls to imagemagick convert require it to be there)
97     *
98     * @return string Dummy value
99     */
100    public function getDescriptionUrl() {
101        return $this->getUrl();
102    }
103
104    /**
105     * Get the path for the thumbnail (actually any transformation of this file)
106     * The actual argument is the result of thumbName although we seem to have
107     * buggy code elsewhere that expects a boolean 'suffix'
108     *
109     * @param string|false $thumbName Name of thumbnail (e.g. "120px-123456.jpg" ),
110     *   or false to just get the path
111     * @return string Path thumbnail should take on filesystem, or containing
112     *   directory if thumbname is false
113     */
114    public function getThumbPath( $thumbName = false ) {
115        $path = dirname( $this->path );
116        if ( $thumbName !== false ) {
117            $path .= "/$thumbName";
118        }
119
120        return $path;
121    }
122
123    /**
124     * Return the file/url base name of a thumbnail with the specified parameters.
125     * We override this because we want to use the pretty url name instead of the
126     * ugly file name.
127     *
128     * @param array $params Handler-specific parameters
129     * @param int $flags Bitfield that supports THUMB_* constants
130     * @return string|null Base name for URL, like '120px-12345.jpg', or null if there is no handler
131     */
132    public function thumbName( $params, $flags = 0 ) {
133        return $this->generateThumbName( $this->getUrlName(), $params );
134    }
135
136    /**
137     * Helper function -- given a 'subpage', return the local URL,
138     * e.g. /wiki/Special:UploadStash/subpage
139     * @param string $subPage
140     * @return string Local URL for this subpage in the Special:UploadStash space.
141     */
142    private function getSpecialUrl( $subPage ) {
143        return SpecialPage::getTitleFor( 'UploadStash', $subPage )->getLocalURL();
144    }
145
146    /**
147     * Get a URL to access the thumbnail
148     * This is required because the model of how files work requires that
149     * the thumbnail urls be predictable. However, in our model the URL is
150     * not based on the filename (that's hidden in the db)
151     *
152     * @param string|false $thumbName Basename of thumbnail file -- however, we don't
153     *   want to use the file exactly
154     * @return string URL to access thumbnail, or URL with partial path
155     */
156    public function getThumbUrl( $thumbName = false ) {
157        wfDebug( __METHOD__ . " getting for $thumbName" );
158
159        return $this->getSpecialUrl( 'thumb/' . $this->getUrlName() . '/' . $thumbName );
160    }
161
162    /**
163     * The basename for the URL, which we want to not be related to the filename.
164     * Will also be used as the lookup key for a thumbnail file.
165     *
166     * @return string Base url name, like '120px-123456.jpg'
167     */
168    public function getUrlName() {
169        if ( !$this->urlName ) {
170            $this->urlName = $this->fileKey;
171        }
172
173        return $this->urlName;
174    }
175
176    /**
177     * Return the URL of the file, if for some reason we wanted to download it
178     * We tend not to do this for the original file, but we do want thumb icons
179     *
180     * @return string Url
181     */
182    public function getUrl() {
183        if ( $this->url === null ) {
184            $this->url = $this->getSpecialUrl( 'file/' . $this->getUrlName() );
185        }
186
187        return $this->url;
188    }
189
190    /**
191     * Parent classes use this method, for no obvious reason, to return the path
192     * (relative to wiki root, I assume). But with this class, the URL is
193     * unrelated to the path.
194     *
195     * @return string Url
196     */
197    public function getFullUrl() {
198        return $this->getUrl();
199    }
200
201    /**
202     * Getter for file key (the unique id by which this file's location &
203     * metadata is stored in the db)
204     *
205     * @return string File key
206     */
207    public function getFileKey() {
208        return $this->fileKey;
209    }
210
211    /**
212     * Remove the associated temporary file
213     * @return bool Success
214     */
215    public function remove() {
216        if ( !$this->repo->fileExists( $this->path ) ) {
217            // Maybe the file's already been removed? This could totally happen in UploadBase.
218            return true;
219        }
220
221        return $this->repo->freeTemp( $this->path );
222    }
223
224    /** @inheritDoc */
225    public function exists() {
226        return $this->repo->fileExists( $this->path );
227    }
228}
229
230/** @deprecated class alias since 1.46 */
231class_alias( UploadStashFile::class, 'UploadStashFile' );