This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | use MediaWiki\MediaWikiServices; |
||
3 | |||
4 | /** |
||
5 | * Implements Special:FileDuplicateSearch |
||
6 | * |
||
7 | * This program is free software; you can redistribute it and/or modify |
||
8 | * it under the terms of the GNU General Public License as published by |
||
9 | * the Free Software Foundation; either version 2 of the License, or |
||
10 | * (at your option) any later version. |
||
11 | * |
||
12 | * This program is distributed in the hope that it will be useful, |
||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
15 | * GNU General Public License for more details. |
||
16 | * |
||
17 | * You should have received a copy of the GNU General Public License along |
||
18 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
20 | * http://www.gnu.org/copyleft/gpl.html |
||
21 | * |
||
22 | * @file |
||
23 | * @ingroup SpecialPage |
||
24 | * @author Raimond Spekking, based on Special:MIMESearch by Ævar Arnfjörð Bjarmason |
||
25 | */ |
||
26 | |||
27 | /** |
||
28 | * Searches the database for files of the requested hash, comparing this with the |
||
29 | * 'img_sha1' field in the image table. |
||
30 | * |
||
31 | * @ingroup SpecialPage |
||
32 | */ |
||
33 | class FileDuplicateSearchPage extends QueryPage { |
||
34 | protected $hash = '', $filename = ''; |
||
0 ignored issues
–
show
|
|||
35 | |||
36 | /** |
||
37 | * @var File $file selected reference file, if present |
||
38 | */ |
||
39 | protected $file = null; |
||
40 | |||
41 | function __construct( $name = 'FileDuplicateSearch' ) { |
||
42 | parent::__construct( $name ); |
||
43 | } |
||
44 | |||
45 | function isSyndicated() { |
||
46 | return false; |
||
47 | } |
||
48 | |||
49 | function isCacheable() { |
||
50 | return false; |
||
51 | } |
||
52 | |||
53 | public function isCached() { |
||
54 | return false; |
||
55 | } |
||
56 | |||
57 | function linkParameters() { |
||
58 | return [ 'filename' => $this->filename ]; |
||
59 | } |
||
60 | |||
61 | /** |
||
62 | * Fetch dupes from all connected file repositories. |
||
63 | * |
||
64 | * @return array Array of File objects |
||
65 | */ |
||
66 | function getDupes() { |
||
67 | return RepoGroup::singleton()->findBySha1( $this->hash ); |
||
68 | } |
||
69 | |||
70 | /** |
||
71 | * |
||
72 | * @param array $dupes Array of File objects |
||
73 | */ |
||
74 | function showList( $dupes ) { |
||
75 | $html = []; |
||
76 | $html[] = $this->openList( 0 ); |
||
77 | |||
78 | foreach ( $dupes as $dupe ) { |
||
79 | $line = $this->formatResult( null, $dupe ); |
||
80 | $html[] = "<li>" . $line . "</li>"; |
||
81 | } |
||
82 | $html[] = $this->closeList(); |
||
83 | |||
84 | $this->getOutput()->addHTML( implode( "\n", $html ) ); |
||
85 | } |
||
86 | |||
87 | public function getQueryInfo() { |
||
88 | return [ |
||
89 | 'tables' => [ 'image' ], |
||
90 | 'fields' => [ |
||
91 | 'title' => 'img_name', |
||
92 | 'value' => 'img_sha1', |
||
93 | 'img_user_text', |
||
94 | 'img_timestamp' |
||
95 | ], |
||
96 | 'conds' => [ 'img_sha1' => $this->hash ] |
||
97 | ]; |
||
98 | } |
||
99 | |||
100 | public function execute( $par ) { |
||
101 | $this->setHeaders(); |
||
102 | $this->outputHeader(); |
||
103 | |||
104 | $this->filename = $par !== null ? $par : $this->getRequest()->getText( 'filename' ); |
||
105 | $this->file = null; |
||
106 | $this->hash = ''; |
||
107 | $title = Title::newFromText( $this->filename, NS_FILE ); |
||
108 | if ( $title && $title->getText() != '' ) { |
||
109 | $this->file = wfFindFile( $title ); |
||
0 ignored issues
–
show
It seems like
wfFindFile($title) can also be of type boolean . However, the property $file is declared as type object<File> . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
110 | } |
||
111 | |||
112 | $out = $this->getOutput(); |
||
113 | |||
114 | # Create the input form |
||
115 | $formFields = [ |
||
116 | 'filename' => [ |
||
117 | 'type' => 'text', |
||
118 | 'name' => 'filename', |
||
119 | 'label-message' => 'fileduplicatesearch-filename', |
||
120 | 'id' => 'filename', |
||
121 | 'size' => 50, |
||
122 | 'value' => $this->filename, |
||
123 | ], |
||
124 | ]; |
||
125 | $hiddenFields = [ |
||
126 | 'title' => $this->getPageTitle()->getPrefixedDBkey(), |
||
127 | ]; |
||
128 | $htmlForm = HTMLForm::factory( 'ooui', $formFields, $this->getContext() ); |
||
129 | $htmlForm->addHiddenFields( $hiddenFields ); |
||
130 | $htmlForm->setAction( wfScript() ); |
||
131 | $htmlForm->setMethod( 'get' ); |
||
132 | $htmlForm->setSubmitProgressive(); |
||
133 | $htmlForm->setSubmitTextMsg( $this->msg( 'fileduplicatesearch-submit' ) ); |
||
134 | |||
135 | // The form should be visible always, even if it was submitted (e.g. to perform another action). |
||
136 | // To bypass the callback validation of HTMLForm, use prepareForm() and displayForm(). |
||
137 | $htmlForm->prepareForm()->displayForm( false ); |
||
138 | |||
139 | if ( $this->file ) { |
||
140 | $this->hash = $this->file->getSha1(); |
||
141 | } elseif ( $this->filename !== '' ) { |
||
142 | $out->wrapWikiMsg( |
||
143 | "<p class='mw-fileduplicatesearch-noresults'>\n$1\n</p>", |
||
144 | [ 'fileduplicatesearch-noresults', wfEscapeWikiText( $this->filename ) ] |
||
145 | ); |
||
146 | } |
||
147 | |||
148 | if ( $this->hash != '' ) { |
||
149 | # Show a thumbnail of the file |
||
150 | $img = $this->file; |
||
151 | if ( $img ) { |
||
152 | $thumb = $img->transform( [ 'width' => 120, 'height' => 120 ] ); |
||
153 | if ( $thumb ) { |
||
154 | $out->addModuleStyles( 'mediawiki.special' ); |
||
155 | $out->addHTML( '<div id="mw-fileduplicatesearch-icon">' . |
||
156 | $thumb->toHtml( [ 'desc-link' => false ] ) . '<br />' . |
||
157 | $this->msg( 'fileduplicatesearch-info' )->numParams( |
||
158 | $img->getWidth(), $img->getHeight() )->params( |
||
159 | $this->getLanguage()->formatSize( $img->getSize() ), |
||
160 | $img->getMimeType() )->parseAsBlock() . |
||
161 | '</div>' ); |
||
162 | } |
||
163 | } |
||
164 | |||
165 | $dupes = $this->getDupes(); |
||
166 | $numRows = count( $dupes ); |
||
167 | |||
168 | # Show a short summary |
||
169 | if ( $numRows == 1 ) { |
||
170 | $out->wrapWikiMsg( |
||
171 | "<p class='mw-fileduplicatesearch-result-1'>\n$1\n</p>", |
||
172 | [ 'fileduplicatesearch-result-1', wfEscapeWikiText( $this->filename ) ] |
||
173 | ); |
||
174 | } elseif ( $numRows ) { |
||
175 | $out->wrapWikiMsg( |
||
176 | "<p class='mw-fileduplicatesearch-result-n'>\n$1\n</p>", |
||
177 | [ 'fileduplicatesearch-result-n', wfEscapeWikiText( $this->filename ), |
||
178 | $this->getLanguage()->formatNum( $numRows - 1 ) ] |
||
179 | ); |
||
180 | } |
||
181 | |||
182 | $this->doBatchLookups( $dupes ); |
||
183 | $this->showList( $dupes ); |
||
184 | } |
||
185 | } |
||
186 | |||
187 | function doBatchLookups( $list ) { |
||
188 | $batch = new LinkBatch(); |
||
189 | /** @var File $file */ |
||
190 | foreach ( $list as $file ) { |
||
191 | $batch->addObj( $file->getTitle() ); |
||
192 | if ( $file->isLocal() ) { |
||
193 | $userName = $file->getUser( 'text' ); |
||
194 | $batch->add( NS_USER, $userName ); |
||
195 | $batch->add( NS_USER_TALK, $userName ); |
||
196 | } |
||
197 | } |
||
198 | |||
199 | $batch->execute(); |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * |
||
204 | * @param Skin $skin |
||
205 | * @param File $result |
||
206 | * @return string HTML |
||
207 | */ |
||
208 | function formatResult( $skin, $result ) { |
||
209 | global $wgContLang; |
||
210 | |||
211 | $nt = $result->getTitle(); |
||
212 | $text = $wgContLang->convert( $nt->getText() ); |
||
213 | $plink = Linker::link( |
||
214 | $nt, |
||
215 | htmlspecialchars( $text ) |
||
216 | ); |
||
217 | |||
218 | $userText = $result->getUser( 'text' ); |
||
219 | if ( $result->isLocal() ) { |
||
220 | $userId = $result->getUser( 'id' ); |
||
221 | $user = Linker::userLink( $userId, $userText ); |
||
222 | $user .= '<span style="white-space: nowrap;">'; |
||
223 | $user .= Linker::userToolLinks( $userId, $userText ); |
||
224 | $user .= '</span>'; |
||
225 | } else { |
||
226 | $user = htmlspecialchars( $userText ); |
||
227 | } |
||
228 | |||
229 | $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate( |
||
230 | $result->getTimestamp(), $this->getUser() ) ); |
||
231 | |||
232 | return "$plink . . $user . . $time"; |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Return an array of subpages beginning with $search that this special page will accept. |
||
237 | * |
||
238 | * @param string $search Prefix to search for |
||
239 | * @param int $limit Maximum number of results to return (usually 10) |
||
240 | * @param int $offset Number of results to skip (usually 0) |
||
241 | * @return string[] Matching subpages |
||
242 | */ |
||
243 | public function prefixSearchSubpages( $search, $limit, $offset ) { |
||
244 | $title = Title::newFromText( $search, NS_FILE ); |
||
245 | if ( !$title || $title->getNamespace() !== NS_FILE ) { |
||
246 | // No prefix suggestion outside of file namespace |
||
247 | return []; |
||
248 | } |
||
249 | $searchEngine = MediaWikiServices::getInstance()->newSearchEngine(); |
||
250 | $searchEngine->setLimitOffset( $limit, $offset ); |
||
251 | // Autocomplete subpage the same as a normal search, but just for files |
||
252 | $searchEngine->setNamespaces( [ NS_FILE ] ); |
||
253 | $result = $searchEngine->defaultPrefixSearch( $search ); |
||
254 | |||
255 | return array_map( function ( Title $t ) { |
||
256 | // Remove namespace in search suggestion |
||
257 | return $t->getText(); |
||
258 | }, $result ); |
||
259 | } |
||
260 | |||
261 | protected function getGroupName() { |
||
262 | return 'media'; |
||
263 | } |
||
264 | } |
||
265 |
Only declaring a single property per statement allows you to later on add doc comments more easily.
It is also recommended by PSR2, so it is a common style that many people expect.