Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
15.62% |
10 / 64 |
|
55.56% |
5 / 9 |
CRAP | |
0.00% |
0 / 1 |
ImportStreamSource | |
15.62% |
10 / 64 |
|
55.56% |
5 / 9 |
400.42 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
atEnd | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
readChunk | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isSeekable | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
seek | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
newFromFile | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
2.02 | |||
newFromUpload | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
90 | |||
newFromURL | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
6 | |||
newFromInterwiki | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
56 |
1 | <?php |
2 | /** |
3 | * MediaWiki page data importer. |
4 | * |
5 | * Copyright © 2003,2005 Brooke Vibber <bvibber@wikimedia.org> |
6 | * https://www.mediawiki.org/ |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License along |
19 | * with this program; if not, write to the Free Software Foundation, Inc., |
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
21 | * http://www.gnu.org/copyleft/gpl.html |
22 | * |
23 | * @file |
24 | * @ingroup SpecialPage |
25 | */ |
26 | |
27 | use MediaWiki\MainConfigNames; |
28 | use MediaWiki\MediaWikiServices; |
29 | use MediaWiki\Status\Status; |
30 | use Wikimedia\AtEase\AtEase; |
31 | |
32 | /** |
33 | * Imports a XML dump from a file (either from file upload, files on disk, or HTTP) |
34 | * @ingroup SpecialPage |
35 | */ |
36 | class ImportStreamSource implements ImportSource { |
37 | /** @var resource */ |
38 | private $mHandle; |
39 | |
40 | /** |
41 | * @param resource $handle |
42 | */ |
43 | public function __construct( $handle ) { |
44 | $this->mHandle = $handle; |
45 | } |
46 | |
47 | /** |
48 | * @return bool |
49 | */ |
50 | public function atEnd() { |
51 | return feof( $this->mHandle ); |
52 | } |
53 | |
54 | /** |
55 | * @return string |
56 | */ |
57 | public function readChunk() { |
58 | return fread( $this->mHandle, 32768 ); |
59 | } |
60 | |
61 | /** |
62 | * @return bool |
63 | */ |
64 | public function isSeekable() { |
65 | return stream_get_meta_data( $this->mHandle )['seekable'] ?? false; |
66 | } |
67 | |
68 | /** |
69 | * @param int $offset |
70 | * @return int |
71 | */ |
72 | public function seek( int $offset ) { |
73 | return fseek( $this->mHandle, $offset ); |
74 | } |
75 | |
76 | /** |
77 | * @param string $filename |
78 | * @return Status |
79 | */ |
80 | public static function newFromFile( $filename ) { |
81 | AtEase::suppressWarnings(); |
82 | $file = fopen( $filename, 'rt' ); |
83 | AtEase::restoreWarnings(); |
84 | if ( !$file ) { |
85 | return Status::newFatal( "importcantopen" ); |
86 | } |
87 | return Status::newGood( new ImportStreamSource( $file ) ); |
88 | } |
89 | |
90 | /** |
91 | * @param string $fieldname |
92 | * @return Status |
93 | */ |
94 | public static function newFromUpload( $fieldname = "xmlimport" ) { |
95 | // phpcs:ignore MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals |
96 | $upload =& $_FILES[$fieldname]; |
97 | |
98 | if ( $upload === null || !$upload['name'] ) { |
99 | return Status::newFatal( 'importnofile' ); |
100 | } |
101 | if ( !empty( $upload['error'] ) ) { |
102 | switch ( $upload['error'] ) { |
103 | case UPLOAD_ERR_INI_SIZE: |
104 | // The uploaded file exceeds the upload_max_filesize directive in php.ini. |
105 | return Status::newFatal( 'importuploaderrorsize' ); |
106 | case UPLOAD_ERR_FORM_SIZE: |
107 | // The uploaded file exceeds the MAX_FILE_SIZE directive that |
108 | // was specified in the HTML form. |
109 | // FIXME This is probably never used since that directive was removed in 8e91c520? |
110 | return Status::newFatal( 'importuploaderrorsize' ); |
111 | case UPLOAD_ERR_PARTIAL: |
112 | // The uploaded file was only partially uploaded |
113 | return Status::newFatal( 'importuploaderrorpartial' ); |
114 | case UPLOAD_ERR_NO_TMP_DIR: |
115 | // Missing a temporary folder. |
116 | return Status::newFatal( 'importuploaderrortemp' ); |
117 | // Other error codes get the generic 'importnofile' error message below |
118 | } |
119 | |
120 | } |
121 | $fname = $upload['tmp_name']; |
122 | if ( is_uploaded_file( $fname ) ) { |
123 | return self::newFromFile( $fname ); |
124 | } else { |
125 | return Status::newFatal( 'importnofile' ); |
126 | } |
127 | } |
128 | |
129 | /** |
130 | * @param string $url |
131 | * @param string $method |
132 | * @return Status |
133 | */ |
134 | public static function newFromURL( $url, $method = 'GET' ) { |
135 | $httpImportTimeout = MediaWikiServices::getInstance()->getMainConfig()->get( |
136 | MainConfigNames::HTTPImportTimeout ); |
137 | wfDebug( __METHOD__ . ": opening $url" ); |
138 | # Use the standard HTTP fetch function; it times out |
139 | # quicker and sorts out user-agent problems which might |
140 | # otherwise prevent importing from large sites, such |
141 | # as the Wikimedia cluster, etc. |
142 | $data = MediaWikiServices::getInstance()->getHttpRequestFactory()->request( |
143 | $method, |
144 | $url, |
145 | [ |
146 | 'followRedirects' => true, |
147 | 'timeout' => $httpImportTimeout |
148 | ], |
149 | __METHOD__ |
150 | ); |
151 | if ( $data !== null ) { |
152 | $file = tmpfile(); |
153 | fwrite( $file, $data ); |
154 | fflush( $file ); |
155 | fseek( $file, 0 ); |
156 | return Status::newGood( new ImportStreamSource( $file ) ); |
157 | } else { |
158 | return Status::newFatal( 'importcantopen' ); |
159 | } |
160 | } |
161 | |
162 | /** |
163 | * @param string $interwiki |
164 | * @param string $page |
165 | * @param bool $history |
166 | * @param bool $templates |
167 | * @param int $pageLinkDepth |
168 | * @return Status |
169 | */ |
170 | public static function newFromInterwiki( $interwiki, $page, $history = false, |
171 | $templates = false, $pageLinkDepth = 0 |
172 | ) { |
173 | if ( $page == '' ) { |
174 | return Status::newFatal( 'import-noarticle' ); |
175 | } |
176 | |
177 | # Look up the first interwiki prefix, and let the foreign site handle |
178 | # subsequent interwiki prefixes |
179 | $firstIwPrefix = strtok( $interwiki, ':' ); |
180 | $interwikiLookup = MediaWikiServices::getInstance()->getInterwikiLookup(); |
181 | $firstIw = $interwikiLookup->fetch( $firstIwPrefix ); |
182 | if ( !$firstIw ) { |
183 | return Status::newFatal( 'importbadinterwiki' ); |
184 | } |
185 | |
186 | $additionalIwPrefixes = strtok( '' ); |
187 | if ( $additionalIwPrefixes ) { |
188 | $additionalIwPrefixes .= ':'; |
189 | } |
190 | # Have to do a DB-key replacement ourselves; otherwise spaces get |
191 | # URL-encoded to +, which is wrong in this case. Similar to logic in |
192 | # Title::getLocalURL |
193 | $link = $firstIw->getURL( strtr( "{$additionalIwPrefixes}Special:Export/$page", |
194 | ' ', '_' ) ); |
195 | |
196 | $params = []; |
197 | if ( $history ) { |
198 | $params['history'] = 1; |
199 | } |
200 | if ( $templates ) { |
201 | $params['templates'] = 1; |
202 | } |
203 | if ( $pageLinkDepth ) { |
204 | $params['pagelink-depth'] = $pageLinkDepth; |
205 | } |
206 | |
207 | $url = wfAppendQuery( $link, $params ); |
208 | # For interwikis, use POST to avoid redirects. |
209 | return self::newFromURL( $url, "POST" ); |
210 | } |
211 | } |