| 1 | <?php |
|---|
| 2 | |
|---|
| 3 | /** |
|---|
| 4 | * API for MediaWiki 1.8+ |
|---|
| 5 | * |
|---|
| 6 | * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com |
|---|
| 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 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|---|
| 21 | * http://www.gnu.org/copyleft/gpl.html |
|---|
| 22 | * |
|---|
| 23 | * @file |
|---|
| 24 | */ |
|---|
| 25 | |
|---|
| 26 | /** |
|---|
| 27 | * This file is the entry point for all API queries. It begins by checking |
|---|
| 28 | * whether the API is enabled on this wiki; if not, it informs the user that |
|---|
| 29 | * s/he should set $wgEnableAPI to true and exits. Otherwise, it constructs |
|---|
| 30 | * a new ApiMain using the parameter passed to it as an argument in the URL |
|---|
| 31 | * ('?action=') and with write-enabled set to the value of $wgEnableWriteAPI |
|---|
| 32 | * as specified in LocalSettings.php. It then invokes "execute()" on the |
|---|
| 33 | * ApiMain object instance, which produces output in the format sepecified |
|---|
| 34 | * in the URL. |
|---|
| 35 | */ |
|---|
| 36 | |
|---|
| 37 | // Initialise common code |
|---|
| 38 | require ( dirname( __FILE__ ) . '/includes/WebStart.php' ); |
|---|
| 39 | |
|---|
| 40 | wfProfileIn( 'api.php' ); |
|---|
| 41 | $starttime = microtime( true ); |
|---|
| 42 | |
|---|
| 43 | // URL safety checks |
|---|
| 44 | // |
|---|
| 45 | // See RawPage.php for details; summary is that MSIE can override the |
|---|
| 46 | // Content-Type if it sees a recognized extension on the URL, such as |
|---|
| 47 | // might be appended via PATH_INFO after 'api.php'. |
|---|
| 48 | // |
|---|
| 49 | // Some data formats can end up containing unfiltered user-provided data |
|---|
| 50 | // which will end up triggering HTML detection and execution, hence |
|---|
| 51 | // XSS injection and all that entails. |
|---|
| 52 | // |
|---|
| 53 | if ( $wgRequest->isPathInfoBad() ) { |
|---|
| 54 | wfHttpError( 403, 'Forbidden', |
|---|
| 55 | 'Invalid file extension found in PATH_INFO or QUERY_STRING.' ); |
|---|
| 56 | return; |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | // Verify that the API has not been disabled |
|---|
| 60 | if ( !$wgEnableAPI ) { |
|---|
| 61 | echo 'MediaWiki API is not enabled for this site. Add the following line to your LocalSettings.php'; |
|---|
| 62 | echo '<pre><b>$wgEnableAPI=true;</b></pre>'; |
|---|
| 63 | die( 1 ); |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | // Selectively allow cross-site AJAX |
|---|
| 67 | |
|---|
| 68 | /* |
|---|
| 69 | * Helper function to convert wildcard string into a regex |
|---|
| 70 | * '*' => '.*?' |
|---|
| 71 | * '?' => '.' |
|---|
| 72 | * @ return string |
|---|
| 73 | */ |
|---|
| 74 | function convertWildcard( $search ) { |
|---|
| 75 | $search = preg_quote( $search, '/' ); |
|---|
| 76 | $search = str_replace( |
|---|
| 77 | array( '\*', '\?' ), |
|---|
| 78 | array( '.*?', '.' ), |
|---|
| 79 | $search |
|---|
| 80 | ); |
|---|
| 81 | return "/$search/"; |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | if ( $wgCrossSiteAJAXdomains && isset( $_SERVER['HTTP_ORIGIN'] ) ) { |
|---|
| 85 | $exceptions = array_map( 'convertWildcard', $wgCrossSiteAJAXdomainExceptions ); |
|---|
| 86 | $regexes = array_map( 'convertWildcard', $wgCrossSiteAJAXdomains ); |
|---|
| 87 | foreach ( $regexes as $regex ) { |
|---|
| 88 | if ( preg_match( $regex, $_SERVER['HTTP_ORIGIN'] ) ) { |
|---|
| 89 | foreach ( $exceptions as $exc ) { // Check against exceptions |
|---|
| 90 | if ( preg_match( $exc, $_SERVER['HTTP_ORIGIN'] ) ) { |
|---|
| 91 | break 2; |
|---|
| 92 | } |
|---|
| 93 | } |
|---|
| 94 | header( "Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}" ); |
|---|
| 95 | header( 'Access-Control-Allow-Credentials: true' ); |
|---|
| 96 | break; |
|---|
| 97 | } |
|---|
| 98 | } |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | // So extensions can check whether they're running in API mode |
|---|
| 102 | define( 'MW_API', true ); |
|---|
| 103 | |
|---|
| 104 | // Set a dummy $wgTitle, because $wgTitle == null breaks various things |
|---|
| 105 | // In a perfect world this wouldn't be necessary |
|---|
| 106 | $wgTitle = Title::makeTitle( NS_MAIN, 'API' ); |
|---|
| 107 | |
|---|
| 108 | /* Construct an ApiMain with the arguments passed via the URL. What we get back |
|---|
| 109 | * is some form of an ApiMain, possibly even one that produces an error message, |
|---|
| 110 | * but we don't care here, as that is handled by the ctor. |
|---|
| 111 | */ |
|---|
| 112 | $processor = new ApiMain( $wgRequest, $wgEnableWriteAPI ); |
|---|
| 113 | |
|---|
| 114 | // Process data & print results |
|---|
| 115 | $processor->execute(); |
|---|
| 116 | |
|---|
| 117 | // Execute any deferred updates |
|---|
| 118 | wfDoUpdates(); |
|---|
| 119 | |
|---|
| 120 | // Log what the user did, for book-keeping purposes. |
|---|
| 121 | $endtime = microtime( true ); |
|---|
| 122 | wfProfileOut( 'api.php' ); |
|---|
| 123 | wfLogProfilingData(); |
|---|
| 124 | |
|---|
| 125 | // Log the request |
|---|
| 126 | if ( $wgAPIRequestLog ) { |
|---|
| 127 | $items = array( |
|---|
| 128 | wfTimestamp( TS_MW ), |
|---|
| 129 | $endtime - $starttime, |
|---|
| 130 | wfGetIP(), |
|---|
| 131 | $_SERVER['HTTP_USER_AGENT'] |
|---|
| 132 | ); |
|---|
| 133 | $items[] = $wgRequest->wasPosted() ? 'POST' : 'GET'; |
|---|
| 134 | if ( $processor->getModule()->mustBePosted() ) { |
|---|
| 135 | $items[] = "action=" . $wgRequest->getVal( 'action' ); |
|---|
| 136 | } else { |
|---|
| 137 | $items[] = wfArrayToCGI( $wgRequest->getValues() ); |
|---|
| 138 | } |
|---|
| 139 | wfErrorLog( implode( ',', $items ) . "\n", $wgAPIRequestLog ); |
|---|
| 140 | wfDebug( "Logged API request to $wgAPIRequestLog\n" ); |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | // Shut down the database |
|---|
| 144 | wfGetLBFactory()->shutdown(); |
|---|
| 145 | |
|---|