BotCore.php 67.6 KB
Newer Older
1
<?php
Luke081515's avatar
Luke081515 committed
2
require_once __DIR__ . "/Password.php";
Luke081515's avatar
Luke081515 committed
3
/** BotCore.php
Luke081515's avatar
Luke081515 committed
4 5
* Central file of the Cygnus-Framework
* Most functions get loaded from this file.
6
* @author Freddy2001 <freddy2001@wikipedia.de>, Hgzh, Luke081515 <luke081515@tools.wmflabs.org>, MGChecker <hgasuser@gmail.com>
7
* @requires extensions: JSON
Luke081515's avatar
Luke081515 committed
8
* @version V2.1 alpha
Luke081515's avatar
Luke081515 committed
9
*/
Freddy2001's avatar
Freddy2001 committed
10
class Core extends Password {
11
	protected $username;
Luke081515's avatar
Luke081515 committed
12
	protected $cleanUsername;
13 14 15 16 17
	protected $password;
	protected $curlHandle;
	protected $site;
	protected $protocol;
	protected $job;
18
	protected $assert;
19 20
	protected $mail;
	protected $mailcontent;
Luke081515's avatar
Luke081515 committed
21
	private $version = "Cygnus-Framework V2.1 alpha";
22
	private $ua;
Freddy2001's avatar
Freddy2001 committed
23
	private $maxlag;
Freddy's avatar
Freddy committed
24
	private $target;
Luke081515's avatar
Luke081515 committed
25
	private $debugMode = false;
Luke081515's avatar
Luke081515 committed
26
	private $failedLoginCounter = 0;
Luke081515's avatar
Luke081515 committed
27
	private $passwordVersion = "2.1.0"; // Should be the same as in Password.php, when you are changing the file.
28

29
	/** initcurl
30 31 32
	* initializes curl
	* this function usually should be called first
	* creates the connection object
Luke081515's avatar
Luke081515 committed
33 34
	* does the login if the bot isn"t explicitly told not to by $loadSettings
	* if that"s not the case, it reads the login data of settings.json and does the login afterwards
35 36 37
	* @author Hgzh / MGChecker
	* @param $job - name of the job; used for the internal storage of cookies
	* @param $account - name of the accounts in settings.json
Luke081515's avatar
Luke081515 committed
38
	* @param $assert - [optional: bot] if set to "user" edits can be made without botflag
39
	* @param $loadSettings - [optional: true] if the settings shall be loaded or another way will be used to login
40
	*/
Luke081515's avatar
Luke081515 committed
41
	public function initcurl($account, $job, $pUseHTTPS = true, $assert = "bot") {
Luke081515's avatar
Luke081515 committed
42
		if ($assert !== "bot" && $assert !== "user") {
Luke081515's avatar
Luke081515 committed
43
			throw new Exception("assert has to be 'bot' or 'user'");
Luke081515's avatar
Luke081515 committed
44
		}
45
		$this->assert = $assert;
46
		$this->setMaxlag(5);
47 48
		$this->start($account);
		$this->job = $job;
Luke081515's avatar
Luke081515 committed
49
		if ($pUseHTTPS === true) {
Luke081515's avatar
Luke081515 committed
50
			$this->protocol = "https";
Luke081515's avatar
Luke081515 committed
51
		} else {
Luke081515's avatar
Luke081515 committed
52
			$this->protocol = "http";
Luke081515's avatar
Luke081515 committed
53
		}
54 55
		// init curl
		$curl = curl_init();
Luke081515's avatar
Luke081515 committed
56
		if ($curl === false) {
Luke081515's avatar
Luke081515 committed
57
			throw new Exception("Curl initialization failed.");
Luke081515's avatar
Luke081515 committed
58
		} else {
59
			$this->curlHandle = $curl;
Luke081515's avatar
Luke081515 committed
60
		}
61
		$this->login();
Luke081515's avatar
Luke081515 committed
62
		$this->createCleanUsername();
Luke081515's avatar
Luke081515 committed
63
		$this->echoMsg("***** Starting up....\nVersion: " . $this->version . " *****", "notice");
Luke081515's avatar
Luke081515 committed
64
		$this->ua = "User:" . $this->cleanUsername . " - " . $this->job . " - " . $this->version;
Luke081515's avatar
Luke081515 committed
65
		$this->echoMsg("Used UserAgent: '" . $this->ua . "'\n", "notice");
66
		if ($this->cleanUsername === $this->username) {
Luke081515's avatar
Luke081515 committed
67 68
			$this->echoMsg("Warning: Main-account login via \"action=login\" is deprecated and may stop working without warning. ", "warning");
			$this->echoMsg("To continue login with \"action=login\", see [[Special:BotPasswords]].", "warning");
69
		}
70
	}
71
	/** initcurlArgs
Luke081515's avatar
Luke081515 committed
72
	* Use this function instead of initcurl if you want to use args or console to tell the bot the password
Luke081515's avatar
Luke081515 committed
73
	* @author Luke081515
Luke081515's avatar
Luke081515 committed
74 75
	* @param $job - name of the job; useful for saving the cookies
	* @param $pUseHTTPS - [optional: true] if false, http will be used
Luke081515's avatar
Luke081515 committed
76
	* @param $assert - [optional: bot] if set to "user" instead, you can use a bot without flag
77
	*/
Luke081515's avatar
Luke081515 committed
78
	public function initcurlArgs($job, $pUseHTTPS = true, $assert = "bot", $supress = false, $debugMode = false) {
Luke081515's avatar
Luke081515 committed
79 80 81
		if ($debugMode) {
			$this->debugMode = true;
		}
Luke081515's avatar
Luke081515 committed
82
		if ($assert !== "bot" && $assert !== "user") {
Luke081515's avatar
Luke081515 committed
83
			exit(1);
Luke081515's avatar
Luke081515 committed
84
		}
85
		$this->assert = $assert;
86
		$this->job = $job;
Luke081515's avatar
Luke081515 committed
87
		if ($pUseHTTPS === true) {
Luke081515's avatar
Luke081515 committed
88
			$this->protocol = "https";
Luke081515's avatar
Luke081515 committed
89
		} else {
Luke081515's avatar
Luke081515 committed
90
			$this->protocol = "http";
Luke081515's avatar
Luke081515 committed
91
		}
92 93
		// init curl
		$curl = curl_init();
Luke081515's avatar
Luke081515 committed
94
		if ($curl === false) {
Luke081515's avatar
Luke081515 committed
95
			throw new Exception("Curl initialization failed.");
Luke081515's avatar
Luke081515 committed
96
		} else {
97
			$this->curlHandle = $curl;
Luke081515's avatar
Luke081515 committed
98
		}
Luke081515's avatar
Luke081515 committed
99
		$this->createCleanUsername();
Luke081515's avatar
Luke081515 committed
100
		$this->ua = "User:" . $this->cleanUsername . " - " . $this->job . " - " . $this->version;
Luke081515's avatar
Luke081515 committed
101
		if (!$supress) {
Luke081515's avatar
Luke081515 committed
102 103
			$this->echoMsg("***** Starting up....\nVersion: " . $this->version . " *****", "notice");
			$this->echoMsg("Used UserAgent: '" . $this->ua . "'\n", "notice");
Luke081515's avatar
Luke081515 committed
104
		}
105
		if ($this->cleanUsername === $this->username) {
Luke081515's avatar
Luke081515 committed
106 107
			$this->echoMsg("Warning: Main-account login via \"action=login\" is deprecated and may stop working without warning. ", "warning");
			$this->echoMsg("To continue login with \"action=login\", see [[Special:BotPasswords]].", "warning");
108
		}
109 110
		// change if you need more, default is 5
		$this->setMaxlag(5);
Freddy2001's avatar
Freddy2001 committed
111
	}
Luke081515's avatar
Luke081515 committed
112
	public function __construct($account, $job, $pUseHTTPS = true) {}
113 114 115
	public function __destruct() {
		curl_close($this->curlHandle);
	}
Luke081515's avatar
Luke081515 committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129
	/** createCleanUsername
	* Looks up the username for a botpassword
	* If username is a botpassword, the botpasswordname will get removed
	* Otherwise $this->cleanUsername will get set to the same value
	* @author Luke081515
	*/
	private function createCleanUsername() {
		$cleanUsername = strstr($this->username, "@", true);
		if ($cleanUsername === false) {
			$this->cleanUsername = $this->username;
		} else {
			$this->cleanUsername = $cleanUsername;
		}
	}
130
	/** httpRequest
Luke081515's avatar
Luke081515 committed
131 132 133 134 135 136 137
	* does http(s) requests
	* mostly used to communicate with the API
	* @param $pArguments - API params you want to exectute (starts normally with action=)
	* @param $job - used to find the right cookies. just use $this->job
	* @param $pMethod - [optional: POST] Method of the request. For querys, you just use GET
	* @param $pTarget - [optional: w/api.php] use this,
		if Special:Version shows something different than w/api.php for using the api
138
	* @author Hgzh
Freddy's avatar
Freddy committed
139
	* @return answer of the API
140
	*/
Freddy's avatar
Freddy committed
141 142 143 144 145
	protected function httpRequest($arguments, $job, $method = 'POST', $target = '') {
		if($target == '') {
			// Kept for legacy support
			// ToDo: Remove in next major release
			$target = $this->target;
146 147 148
		} else {
			echo ("\nThis way of setting the path has been deprecated. "
				. "Please migrate it to Password.php. See release notes for details");
Freddy's avatar
Freddy committed
149 150 151
		}
		$baseURL = $this->protocol . '://' .
				   $this->site . '/' .
152 153
				   $target;
		$method = strtoupper($method);
Luke081515's avatar
Luke081515 committed
154 155
		if ($arguments != "") {
			if ($method === "POST") {
156
				$requestURL = $baseURL;
157
				$postFields = $arguments;
Luke081515's avatar
Luke081515 committed
158 159
			} else if ($method === "GET") {
				$requestURL = $baseURL . "?" .
160
							  $arguments;
Luke081515's avatar
Luke081515 committed
161
			} else {
Luke081515's avatar
Luke081515 committed
162
				throw new Exception("Unknown http request method.");
Luke081515's avatar
Luke081515 committed
163
			}
164
		}
Luke081515's avatar
Luke081515 committed
165
		if (!$requestURL) {
Luke081515's avatar
Luke081515 committed
166
			throw new Exception("No arguments for http request found.");
Luke081515's avatar
Luke081515 committed
167
		}
168
		// set curl options
MGChecker's avatar
MGChecker committed
169
		curl_setopt($this->curlHandle, CURLOPT_USERAGENT, $this->ua);
170
		curl_setopt($this->curlHandle, CURLOPT_URL, $requestURL);
Luke081515's avatar
Luke081515 committed
171
		curl_setopt($this->curlHandle, CURLOPT_ENCODING, "UTF-8");
172
		curl_setopt($this->curlHandle, CURLOPT_RETURNTRANSFER, true);
Luke081515's avatar
Luke081515 committed
173 174
		curl_setopt($this->curlHandle, CURLOPT_COOKIEFILE, realpath("Cookies" . $job . ".tmp"));
		curl_setopt($this->curlHandle, CURLOPT_COOKIEJAR, realpath("Cookies" . $job . ".tmp"));
175
		// if posted, add post fields
Luke081515's avatar
Luke081515 committed
176
		if ($method === "POST" && $postFields != "") {
177 178 179 180
			curl_setopt($this->curlHandle, CURLOPT_POST, 1);
			curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, $postFields);
		} else {
			curl_setopt($this->curlHandle, CURLOPT_POST, 0);
Luke081515's avatar
Luke081515 committed
181
			curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, "");
182 183
		}
		// perform request
184
		$success = false;
Luke081515's avatar
Luke081515 committed
185
		for ($i = 1; $i <= 20; $i++) {
186 187 188 189 190
			$rqResult = curl_exec($this->curlHandle);
			if ($rqResult !== false) {
				$success = true;
				break 1;
			} else {
Luke081515's avatar
Luke081515 committed
191
				$this->echoMsg("Curl request with arguments \"" . $arguments . "\" to " . $this->site . " failed ($i/20): " . curl_error($this->curlHandle), "error");
192 193 194
				sleep(10);
			}
		}
Luke081515's avatar
Luke081515 committed
195
		if ($success === true) {
Luke081515's avatar
Luke081515 committed
196
			if ($this->debugMode) {
Luke081515's avatar
Luke081515 committed
197
				$this->echoMsg("Result for '" . $arguments . "':\n'" . $rqResult . "'", "none");
Luke081515's avatar
Luke081515 committed
198
			}
199
			return $rqResult;
Luke081515's avatar
Luke081515 committed
200
		} else {
Luke081515's avatar
Luke081515 committed
201
			throw new Exception("Curl request definitively failed: " . curl_error($this->curlHandle));
Luke081515's avatar
Luke081515 committed
202
		}
203
	}
204
	/** requireToken
Luke081515's avatar
Luke081515 committed
205
	* query the api for the token
206
	* @author Hgzh / Luke081515 / MGChecker
Luke081515's avatar
Luke081515 committed
207
	* @param $type - [optional: csrf] - type of the token (see the api docs for details)
Freddy's avatar
Freddy committed
208
	* @return requested token
209
	*/
Luke081515's avatar
Luke081515 committed
210
	public function requireToken($type = "csrf") {
Luke081515's avatar
Luke081515 committed
211
		if ($type === "login") { // No assert on login
Luke081515's avatar
Luke081515 committed
212
			$result = $this->httpRequest("action=query&format=json&maxlag=" . $this->maxlag . "&meta=tokens&type=" . $type, $this->job, "GET");
Luke081515's avatar
Luke081515 committed
213
		} else {
Luke081515's avatar
Luke081515 committed
214
			$result = $this->httpRequest("action=query&format=json&assert=" . $this->assert . "&maxlag=" . $this->maxlag . "&meta=tokens&type=" . $type, $this->job, "GET");
Luke081515's avatar
Luke081515 committed
215
		}
Luke081515's avatar
Luke081515 committed
216
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
217
		$tokenname = $type . "token";
218
		try {
Luke081515's avatar
Luke081515 committed
219
			$token = $result["query"]["tokens"][$tokenname];
220
		} catch (Exception $e) {
Luke081515's avatar
Luke081515 committed
221
			throw new Excpetion("You filed an invalid token request." . $e->getMessage());
222
		}
Luke081515's avatar
Luke081515 committed
223
		if ($token === "") {
Luke081515's avatar
Luke081515 committed
224
			throw new Exception("Could not receive login token.");
Luke081515's avatar
Luke081515 committed
225
		}
226 227 228
		return $token;
	}
	/** login
Luke081515's avatar
Luke081515 committed
229 230
	* used for login
	* do not use this function, use initcurl/initcurlArgs instead
231 232 233
	* @author Hgzh
	*/
	protected function login() {
Luke081515's avatar
Luke081515 committed
234
		$lgToken = $this->requireToken("login");
235
		// perform login
Luke081515's avatar
Luke081515 committed
236 237 238
		$result = $this->httpRequest("action=login&maxlag=" . $this->maxlag . "&format=json&lgname=" . urlencode($this->username) .
			"&lgpassword=" . urlencode($this->password) .
			"&lgtoken=" . urlencode($lgToken), $this->job);
Luke081515's avatar
Luke081515 committed
239
		$result = json_decode($result, true);
Freddy's avatar
Freddy committed
240
		$lgResult = $result['login']['result'];
241
		// manage result
Luke081515's avatar
Luke081515 committed
242
		if ($lgResult == "Success") {
243
			return true;
Luke081515's avatar
Luke081515 committed
244
		} else {
Freddy's avatar
Freddy committed
245 246 247 248 249 250
			if(isset($result['login']['reason'])) {
				$lgReason = $result['login']['reason'];
			} else {
				$lgReason = $lgResult;
			}
			throw new Exception("Login failed with message: " . $lgReason);
Luke081515's avatar
Luke081515 committed
251
		}
252
	}
253
	/** logout
Luke081515's avatar
Luke081515 committed
254
	* Does a logout
255
	*/
Luke081515's avatar
Luke081515 committed
256
	public function logout() {
Luke081515's avatar
Luke081515 committed
257
		$this->httpRequest("action=logout", $this->job);
258 259
	}
	/** DO NOT USE this function
260
	* in case you are not using initcurlArgs
261
	*/
Luke081515's avatar
Luke081515 committed
262
	public function setSite($site) {
263 264 265
		$this->site = $site;
	}
	/** DO NOT USE this function
266
	* in case you are not using initcurlArgs
267
	*/
268 269
	public function setUsername($username) {
		$this->username = $username;
Luke081515's avatar
Luke081515 committed
270
		$this->createCleanUsername();
271 272
	}
	/** DO NOT USE this function
273
	* in case you are not using initcurlArgs
274
	*/
275 276
	public function setPassword($password) {
		$this->password = $password;
277
	}
278 279 280 281 282 283
	/** DO NOT USE this function
	* in case you are not using initcurlArgs
	*/
	public function setTarget($target) {
		$this->target = $target;
	}
284
	/** start
Luke081515's avatar
Luke081515 committed
285 286
	* Searches for the data from Password.php, does the login after that
	* this function is used by initcurl, use initcurl instead
287 288
	* @author Luke081515
	*/
289
	public function start($account) {
290 291
		$Found = false;
		$this->init();
Luke081515's avatar
Luke081515 committed
292
		if (method_exists($this, 'getPasswordVersion')) {
Luke081515's avatar
Luke081515 committed
293
			$passwordVersion = $this->getPasswordVersion();
Luke081515's avatar
Luke081515 committed
294
		} else {
Luke081515's avatar
Luke081515 committed
295 296 297 298 299
			throw new Exception("You are using an old version of Password.php. Please upgrade.");
		}
		if ($this->passwordVersion !== $passwordVersion) { // Ensuring no old version is used
			throw new Exception("You are using an old version of Password.php. Please upgrade.");
		}
Luke081515's avatar
Luke081515 committed
300
		$LoginName = unserialize($this->getLoginName());
301 302 303
		$LoginHost = unserialize($this->getLoginHost());
		$LoginAccount = unserialize($this->getLoginAccount());
		$LoginPassword = unserialize($this->getLoginPassword());
304
		$Mail = unserialize($this->getMail());
Freddy's avatar
Freddy committed
305
		$Target = unserialize($this->getApiPath());
Luke081515's avatar
Luke081515 committed
306
		for ($a = 0; isset($LoginName[$a]); $a++) {
307 308 309 310 311
			if ($LoginName[$a] === $account) {
				$this->site = $LoginHost[$a];
				$this->username = $LoginAccount[$a];
				$this->password = $LoginPassword[$a];
				$this->mail = $Mail[$a];
Freddy's avatar
Freddy committed
312
				$this->target = $Target[$a];
313 314 315 316
				$Found = true;
			}
		}
		if (!$Found) {
Luke081515's avatar
Luke081515 committed
317
			throw new Exception("No matching credentials available.");
318 319
		}
	}
320
	/** checkResult
Luke081515's avatar
Luke081515 committed
321 322 323
	* this is an internal method
	* this method gets called if there is an error in executing an action
	* depending on the code, it executes different actions
324
	* @author Luke081515
Luke081515's avatar
Luke081515 committed
325
	* @param $result - errorcode of the api
Freddy's avatar
Freddy committed
326 327 328
	* @return fail - edit failed, a retry would not be useful
	* @return retry - try it again, it may work
	* @return conflict - there is an edit conflict
329
	*/
330
	private function checkResult($result) {
Luke081515's avatar
Luke081515 committed
331
		if ($result === 'maxlag' || $result === 'readonly' || $result === 'unknownerror-nocode' || $result === 'unknownerror' || $result === 'ratelimited') {
Luke081515's avatar
Luke081515 committed
332
			$this->echoMsg("Action failed. Reason: " . $result . ". Please try again", "error");
Luke081515's avatar
Luke081515 committed
333 334
			return 'retry';
		} else if ($result === 'blocked' || $result === 'confirmemail' || $result === 'autoblocked') {
335
			throw new Exception("You will not be able to execute writing actions soon. Reason: " . $result);
Luke081515's avatar
Luke081515 committed
336
		} else if ($result === 'assertuserfailed' || $result === 'assertbotfailed') {
Luke081515's avatar
Luke081515 committed
337
			if($this->failedLoginCounter > 5) {
Luke081515's avatar
Luke081515 committed
338 339
				throw new Exception("MaxLoginTrysExceeded"); // ToDo: Find a way to reset this on succesful actions without putting that into every function
			}
Luke081515's avatar
Luke081515 committed
340
			$this->failedLoginCounter++;
341
			$this->login();
Luke081515's avatar
Luke081515 committed
342 343
			return "retry";
		} else if ($result === "editconflict") {
Luke081515's avatar
Luke081515 committed
344
			$this->echoMsg("Editconflict detected", "error");
Luke081515's avatar
Luke081515 committed
345
			return "conflict";
346 347
		} else if ($result === "nosuchsection") {
			return "nosuchsection";
348
		} else {
Luke081515's avatar
Luke081515 committed
349
			$this->echoMsg("Action failed. Error: " . $result, "error");
Luke081515's avatar
Luke081515 committed
350
			return "fail";
351 352
		}
	}
353
	/** readPageEngine
Luke081515's avatar
Luke081515 committed
354 355
	* for internal use only, used for readPage/readSection functions
	* @param $request - the data for the api to get the content of the page
356
	* @author Luke081515
Freddy's avatar
Freddy committed
357
	* @return text of the page
358
	*/
359
	private function readPageEngine($request) {
Luke081515's avatar
Luke081515 committed
360 361 362 363
		do {
			$page = json_decode($this->httpRequest($request, $this->job, 'GET'), true);
			if (isset($page['error'])) {
				$errorcode = $this->checkResult($page['error']['code']);
364
				if($errorcode === "fail" || $errorcode === "nosuchsection") {
Luke081515's avatar
Luke081515 committed
365 366 367
					return false;
				}
			}
Luke081515's avatar
Luke081515 committed
368
		} while (isset($errorcode) && $errorcode === "retry");
Luke081515's avatar
Luke081515 committed
369
		$pageID = $page['query']['pageids'][0];
Luke081515's avatar
Luke081515 committed
370
		if ($pageID == -1) {
Luke081515's avatar
Luke081515 committed
371 372
			return null;
		}
373
		return $page['query']['pages'][$pageID]['revisions'][0]['slots']['main']['*'];
374
	}
375
	/** readPage
Luke081515's avatar
Luke081515 committed
376
	* Returns the content of a page
Luke081515's avatar
Luke081515 committed
377
	* @param $title - name of the page including namespaces
378
	* @author Luke081515, MGChecker
Freddy's avatar
Freddy committed
379
	* @return content of the page
Luke081515's avatar
Luke081515 committed
380 381
	** false if there is an unknown error
	** null if the page does not exist
382
	*/
383
	public function readPage($title) {
384
		$request = "action=query&prop=revisions&format=json&rvprop=content&rvlimit=1&rvslots=main&titles=" . urlencode($title) .
Luke081515's avatar
Luke081515 committed
385
			"&rvdir=older&assert=" . $this->assert . "&maxlag=" . $this->maxlag . "&rawcontinue=&indexpageids=1";
386
		return $this->readPageEngine($request);
387
	}
388
	/** readPageId
Luke081515's avatar
Luke081515 committed
389 390
	* Returns the content of a page
	* @param $pageID - ID of the page
391
	* @author Luke081515, MGChecker
Freddy's avatar
Freddy committed
392
	* @return content of the page
Luke081515's avatar
Luke081515 committed
393 394
	** false if there is an unknown error
	** null if the page does not exist
395
	*/
396
	public function readPageID($pageID) {
397
		$request = "action=query&prop=revisions&format=json&rvprop=content&rvlimit=1&rvslots=main&pageids=" . urlencode($pageID) .
Luke081515's avatar
Luke081515 committed
398
			"&rvdir=older&assert=" . $this->assert . "&maxlag=" . $this->maxlag . "&rawcontinue=&indexpageids=1";
399
		return $this->readPageEngine($request);
Luke081515's avatar
Luke081515 committed
400 401
	}
	/** readPageJs
Luke081515's avatar
Luke081515 committed
402
	* Returns the content of a JS page
403 404
	* DEPRECATED, replaced with "readPage"
	* To be removed in V2.2
Luke081515's avatar
Luke081515 committed
405
	* @param $title - title of the page
406
	* @author MGChecker
Freddy's avatar
Freddy committed
407
	* @return text of the page
Luke081515's avatar
Luke081515 committed
408 409
	** false if there is an unknown error
	** null if the page does not exist
410
	*/
411
	public function readPageJs($title) {
412 413
		$this->echoMsg("This function is deprecated, please use \$this->readPage() instead. This function will get removed soon.", "warning");
		return $this->readPage($title);
414 415
	}
	/** readPageCss
Luke081515's avatar
Luke081515 committed
416
	* Returns the content of a CSS page
417 418
	* DEPRECATED, replaced with "readPage"
	* To be removed in V2.2
Luke081515's avatar
Luke081515 committed
419
	* @param $title - title of the page
420
	* @author MGChecker
Freddy's avatar
Freddy committed
421
	* @return text of the page
Luke081515's avatar
Luke081515 committed
422 423
	** false if there is an unknown error
	** null if the page does not exist
424
	*/
425
	public function readPageCss($title) {
426 427
		$this->echoMsg("This function is deprecated, please use \$this->readPage() instead. This function will get removed soon.", "warning");
		return $this->readPage($title);
428
	}
429
	/** readSection
Luke081515's avatar
Luke081515 committed
430
	* returns the content of a specified section
Luke081515's avatar
Luke081515 committed
431 432
	* @param $title - name of the page
	* @param $section - number of the section
433
	* @author Luke081515, MGChecker
Freddy's avatar
Freddy committed
434
	* @return text of the section
Luke081515's avatar
Luke081515 committed
435 436
	** false if there is an unknown error
	** null if the page does not exist
437
	*/
438
	public function readSection($title, $section) {
439
		$request = "action=query&prop=revisions&format=json&rvprop=content&rvlimit=1&rvslots=main&rvdir=older&indexpageids=1&rvsection=" . urlencode($section) .
Luke081515's avatar
Luke081515 committed
440
			"&assert=" . $this->assert . "&maxlag=" . $this->maxlag . "&titles=" . urlencode($title);
441
		return $this->readPageEngine($request);
442
	}
443
	/** getTableOfContents
Luke081515's avatar
Luke081515 committed
444 445
	* returns the Table of Contents of a page
	* @param $page - Title of the page
446
	* @author Luke081515
Freddy's avatar
Freddy committed
447 448
	* @return two-dimensional array
	* @return first dimension: the section
Luke081515's avatar
Luke081515 committed
449
	* @retuns second dimension:
450
	* 	[0] => level;
Luke081515's avatar
Luke081515 committed
451 452 453
	* 	[1] => title of the section
	* 	[2] => section number at the table of contents (e.g. something like 7.5 as well);
	* 	[3] => section number as int;
454
	*/
Luke081515's avatar
Luke081515 committed
455
	public function getTableOfContents($title) {
Luke081515's avatar
Luke081515 committed
456 457
		$result = $this->httpRequest("action=parse&format=json&maxlag=" . $this->maxlag . "&assert=" . $this->assert .
			"&page=" . urlencode($title) . "&prop=sections", $this->job, "GET");
Luke081515's avatar
Luke081515 committed
458 459 460 461 462 463
		$result = json_decode($result, true);
		for ($a = 0; isset($result["parse"]["sections"][$a]["level"]); $a++) {
			$ret[$a][0] = $result["parse"]["sections"][$a]["level"];
			$ret[$a][1] = $result["parse"]["sections"][$a]["line"];
			$ret[$a][2] = $result["parse"]["sections"][$a]["number"];
			$ret[$a][3] = $result["parse"]["sections"][$a]["index"];
464 465 466
		}
		return $ret;
	}
Freddy's avatar
Freddy committed
467 468
	/** getEchoNotifications
	 * gets Notifications
Luke081515's avatar
Luke081515 committed
469
	 * requires an installed echo extension in MediaWiki
Freddy's avatar
Freddy committed
470 471 472 473
	 * @author Freddy2001
	 * @returns Array with all notifications
	 */
	public function getEchoNotifications() {
Luke081515's avatar
Luke081515 committed
474 475 476
		$result = $this->httpRequest("action=query&format=json&meta=notifications", $this->job, 'GET');
		$result = json_decode($result, true);
		return $result['query']['notifications']['list'];
Freddy's avatar
Freddy committed
477
	}
478
	/** editPageEngine
Luke081515's avatar
Luke081515 committed
479 480 481
	* internal method which does the edit. please use one of the following functions instead
	* @param $title - name of the page
	* @param $content - new content
Luke081515's avatar
Luke081515 committed
482
	* @param $summary - summary
Luke081515's avatar
Luke081515 committed
483 484 485 486 487
	* @param $botflag - if true, the bot will use a botflag
	* @param $minorflag - if true the edit will get marked as minor
	* @param $noCreate - should the page get recreated in case that this is needed?
	* @param $sectionnumber - which section should get edited? (default => the whole page)
	* @param $overrideNobots - Should {{NoBots}} gets overriden?
488
	* @author Hgzh / Luke081515 / MGChecker
Freddy's avatar
Freddy committed
489
	* @return unserialized answer of the api, if successful
490
	*/
491
	private function editPageEngine($title, $content, $summary, $botflag, $minorflag, $noCreate = 1, $sectionnumber = -1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
492 493 494 495 496
		while (true) {
			if ($overrideNobots !== true) {
				if ($this->allowBots($this->readPage($title)) === false) {
					return "nobots";
				}
Luke081515's avatar
Luke081515 committed
497
			}
Luke081515's avatar
Luke081515 committed
498 499 500 501 502
			$token = $this->requireToken();
			// perform edit
			$request = "action=edit&assert=" . $this->assert . "&maxlag=" . $this->maxlag . "&format=json&bot=&title=" . urlencode($title) .
				"&text=" . urlencode($content) .
				"&token=" . urlencode($token) .
503 504 505 506 507 508 509 510 511
				"&summary=" . urlencode($summary);
			if ($botflag) {
				$request .= "&bot=";
			}
			if ($minorflag) {
				$request .= "&minor=";
			}
			if ($noCreate) {
				$request .= "&nocreate=";
Luke081515's avatar
Luke081515 committed
512 513 514 515 516 517 518 519 520 521 522 523 524 525
			}
			if ($sectionnumber !== -1) {
				$request .= "&section=" . urlencode($sectionnumber);
			}
			$result = $this->httpRequest($request, $this->job);
			$result = json_decode($result, true);
			$editres = $result["edit"]["result"];
			// manage result
			if ($editres == "Success") {
				if (array_key_exists("nochange", $result["edit"])) {
					return array("nochange");
				} else {
					return array($result["edit"]["oldrevid"], $result["edit"]["newrevid"]);
				}
tools.freddy2001's avatar
tools.freddy2001 committed
526
			} else {
Luke081515's avatar
Luke081515 committed
527
				return array($result["edit"]["oldrevid"], $result["edit"]["newrevid"]);
tools.freddy2001's avatar
tools.freddy2001 committed
528
			}
529
		}
530
	}
531
	/** editPage
Luke081515's avatar
Luke081515 committed
532 533 534 535 536 537
	* edits a page
	* @param $title - title of the page
	* @param $content - new content
	* @param $summary - summary
	* @param $noCreate - should the page get recreated? default is no
	* @param $overrideNobots - should {{NoBots}} get overriden? default is no
Luke081515's avatar
Luke081515 committed
538
	* @author Freddy2001 / Luke081515
Freddy's avatar
Freddy committed
539
	* @return unserialized answer of the api, if successful
540
	*/
541
	public function editPage($title, $content, $summary, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
542
		if ($this->assert == "bot") {
543
			$botflag = true;
Luke081515's avatar
Luke081515 committed
544
		} else {
545
			$botflag = false;
Luke081515's avatar
Luke081515 committed
546
		}
547
		return $this->editPageEngine($title, $content, $summary, $botflag, false, $noCreate, -1, $overrideNobots);
548 549
	}
	/** editPageMinor
Luke081515's avatar
Luke081515 committed
550 551 552 553 554 555
	* edits a page and marks the edit as minor
	* @param $title - title of the page
	* @param $content - new content
	* @param $summary - summary
	* @param $noCreate - should the page get recreated? default is no
	* @param $overrideNobots - should {{NoBots}} get overriden? default is no
Luke081515's avatar
Luke081515 committed
556
	* @author Freddy2001 / Luke081515
Freddy's avatar
Freddy committed
557
	* @return unserialized answer of the api, if successful
558
	*/
559
	public function editPageMinor($title, $content, $summary, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
560
		if ($this->assert == "bot") {
561
			$botflag = true;
Luke081515's avatar
Luke081515 committed
562
		} else {
563
			$botflag = false;
Luke081515's avatar
Luke081515 committed
564
		}
565
		return $this->editPageEngine($title, $content, $summary, $botflag, true, $noCreate, -1, $overrideNobots);
566
	}
567
	/** editPageD
Luke081515's avatar
Luke081515 committed
568 569 570 571 572 573 574 575
	* edits a page
	* @param $title - title of the page
	* @param $content - new content
	* @param $summary - summary
	* @param $botflag - if true, the edit will get marked with a botflag
	* @param $minorflag - if true, the edit will get marked as minor
	* @param $noCreate - should the page get recreated? default is no
	* @param $overrideNobots - should {{NoBots}} get overriden? default is no
Luke081515's avatar
Luke081515 committed
576
	* @author MGChecker / Luke081515
Freddy's avatar
Freddy committed
577
	* @return unserialized answer of the api, if successful
578
	*/
579 580
	public function editPageD($title, $content, $summary, $botflag, $minorflag, $noCreate = 1, $overrideNobots = false) {
		return $this->editPageEngine($title, $content, $summary, $botflag, $minorflag, $noCreate, -1, $overrideNobots);
581
	}
582
	/** editSection
Luke081515's avatar
Luke081515 committed
583 584 585 586 587 588 589
	* edits a section
	* @param $title - title of the page
	* @param $content - new content
	* @param $summary - summary
	* @param $sectionnumber - number of the section
	* @param $noCreate - should the page get recreated? default is no
	* @param $overrideNobots - should {{NoBots}} get overriden? default is no
Luke081515's avatar
Luke081515 committed
590
	* @author Freddy2001 / MGChecker / Luke081515
Freddy's avatar
Freddy committed
591
	* @return unserialized answer of the api, if successful
592
	*/
593
	public function editSection($title, $content, $summary, $sectionnumber, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
594
		if ($this->assert == "bot") {
595
			$botflag = true;
Luke081515's avatar
Luke081515 committed
596
		} else {
597
			$botflag = false;
Luke081515's avatar
Luke081515 committed
598 599
		}
		if ($sectionnumber < 0) {
Luke081515's avatar
Luke081515 committed
600
			throw new Exception("You selected a invalid section number. To edit a whole page, use editPage().");
Luke081515's avatar
Luke081515 committed
601
		}
602
		return $this->editPageEngine($title, $content, $summary, $botflag, false, $noCreate, $sectionnumber, $overrideNobots);
603
	}
Luke081515's avatar
Luke081515 committed
604 605 606 607 608 609 610 611
	/** editSection
	* edits a section, marks the edit as minor
	* @param $title - title of the page
	* @param $content - new content
	* @param $summary - summary
	* @param $sectionnumber - number of the section
	* @param $noCreate - should the page get recreated? default is no
	* @param $overrideNobots - should {{NoBots}} get overriden? default is no
Luke081515's avatar
Luke081515 committed
612
	* @author Freddy2001 / MGChecker / Luke081515
Freddy's avatar
Freddy committed
613
	* @return unserialized answer of the api, if successful
614
	*/
615
	public function editSectionMinor($title, $content, $summary, $sectionnumber, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
616
		if ($this->assert == "bot") {
617
			$botflag = true;
Luke081515's avatar
Luke081515 committed
618
		} else {
619
			$botflag = false;
Luke081515's avatar
Luke081515 committed
620 621
		}
		if ($sectionnumber < 0) {
Luke081515's avatar
Luke081515 committed
622
			throw new Exception("You selected a invalid section number. To edit a whole page, use editPageMinor().");
Luke081515's avatar
Luke081515 committed
623
		}
624
		return $this->editPageEngine($title, $content, $summary, $botflag, true, $noCreate, $sectionnumber, $overrideNobots);
625
	}
Luke081515's avatar
Luke081515 committed
626 627 628 629 630 631 632 633 634 635
	/** editPageD
	* edits a page
	* @param $title - title of the page
	* @param $content - new content
	* @param $summary - summary
	* @param $botflag - if true, the edit will get marked with a botflag
	* @param $minorflag - if true, the edit will get marked as minor
	* @param $sectionnumber - number of the section
	* @param $noCreate - should the page get recreated? default is no
	* @param $overrideNobots - should {{NoBots}} get overriden? default is no
Luke081515's avatar
Luke081515 committed
636
	* @author MGChecker / Luke081515
Freddy's avatar
Freddy committed
637
	* @return unserialized answer of the api, if successful
638
	*/
639
	public function editSectionD($title, $content, $summary, $sectionnumber, $botflag, $minorflag, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
640
		if ($sectionnumber < 0) {
Luke081515's avatar
Luke081515 committed
641
			throw new Exception("You selected a invalid section number. To edit a whole page, use editPageD().");
Luke081515's avatar
Luke081515 committed
642
		}
643
		return $this->editPageEngine($title, $content, $summary, $botflag,  $minorflag, $noCreate, $sectionnumber, $overrideNobots);
644
	}
645
	/** movePage
Luke081515's avatar
Luke081515 committed
646 647 648 649 650 651 652
	* moves a page
	* @param $oldTitle - old title of a page
	* @param $newTitle - new title of a page
	* @param $reason - reason for the action
	* @param - $bot (default: 0) - use a botflag?
	* @param - $movetalk (default: 1) - move the talk page as well?
	* @param - $noredirect - (default: 1) - create a redirect?
Freddy's avatar
Freddy committed
653
	* @return serialized answer of the API
654
	*/
655
	public function movePage($oldTitle, $newTitle, $reason, $bot = 0, $movetalk = 1, $noredirect = 1) {
656
		$token = $this->requireToken();
Luke081515's avatar
Luke081515 committed
657
		$request = "action=move&format=json&assert=" . $this->assert . "&maxlag=" . $this->maxlag .
Luke081515's avatar
Luke081515 committed
658 659 660 661 662 663 664
			"&from=" . urlencode($oldTitle) .
			"&to=" . urlencode($newTitle) .
			"&reason=" . urlencode($reason) .
			"&bot=" . $bot .
			"&movetalk=" . $movetalk .
			"&noredirect=" . $noredirect .
			"&token=" . urlencode($token);
Luke081515's avatar
Luke081515 committed
665
		$result = $this->httpRequest($request, $this->job);
666
		return serialize(json_decode($result, true));
667
	}
Luke081515's avatar
Luke081515 committed
668 669 670 671 672 673 674 675
	/** rollback
	* rollbacks an edit
	* @author Luke081515
	* @param $title - name of the page
	* @param $user - author of the revision that should get rollbacked
	* @param $summary - [optional: Defined summary of mediawiki] custom summary
	* @param $markbot - [optional: 0] - if true, the rollbacked edit and the rollback will be marked as bot
	* @return true - if rollback was successful
676 677
	* @return false - if the rollback failed
	* @return null - if there was a conflict when doing a rollback
Luke081515's avatar
Luke081515 committed
678 679 680 681 682 683 684 685 686 687 688 689 690 691
	*/
	public function rollback($title, $user, $summary = "", $markbot = 0) {
		while (true) {
			$token = $this->requireToken("rollback");
			$request = "action=rollback&format=json&assert=" . $this->assert . "&maxlag=" . $this->maxlag .
				"&title=" . urlencode($title) .
				"&user=" . urlencode($user) .
				"&markbot=" . urlencode($markbot) .
				"&watchlist=nochange" .
				"&token=" . urlencode($token);
			if ($summary !== "") {
				$request = $request . "&summary=" . urlencode($summary);
			}
			$result = $this->httpRequest($request, $this->job);
Luke081515's avatar
Luke081515 committed
692 693 694
			$result = json_decode($result, true);
			if (array_key_exists("error", $result)) {
				$code = $this->checkResult($result["error"]["code"]);
Luke081515's avatar
Luke081515 committed
695
				if ($code === "fail") {
696
					return false;
Luke081515's avatar
Luke081515 committed
697 698 699
				} else if ($code === "retry") {
					sleep(5);
				} else if ($code === "conflict") {
700
					return null;
Luke081515's avatar
Luke081515 committed
701 702 703 704 705 706
				}
			} else {
				return true;
			}
		}
	}
Luke081515's avatar
Luke081515 committed
707 708
	/** watch
	* Allows to put a page on your watchlist, or remove it
Luke081515's avatar
Luke081515 committed
709
	* @param $title - title of the page
Luke081515's avatar
Luke081515 committed
710
	* @param $unwatch - default 0 - if 1, the page will get removed from the list
Freddy's avatar
Freddy committed
711
	* @return mixed - true if successful, otherwise the API error code
Luke081515's avatar
Luke081515 committed
712 713
	*/
	public function watch ($title, $unwatch = 0) {
Luke081515's avatar
Luke081515 committed
714
		$token = $this->requireToken("watch");
Luke081515's avatar
Luke081515 committed
715
		$request = "action=watch"
Luke081515's avatar
Luke081515 committed
716 717 718 719 720 721
			. "&format=json"
			. "&unwatch=" . $unwatch
			. "&titles=" . urlencode($title)
			. "&token=" . urlencode($token)
			. "&assert=" . $this->assert
			. "&maxlag=" . $this->maxlag;
Luke081515's avatar
Luke081515 committed
722
		$result = $this->httpRequest($request, $this->job);
Luke081515's avatar
Luke081515 committed
723
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
724
		if (array_key_exists("error", $result)) {
Luke081515's avatar
Luke081515 committed
725
			return $result["error"]["code"];
Luke081515's avatar
Luke081515 committed
726
		}
Luke081515's avatar
Luke081515 committed
727 728
		return true;
	}
Luke081515's avatar
Luke081515 committed
729 730 731 732 733 734 735 736 737 738
	/** purge
	* Allows to put a page on your watchlist, or remove it
	* @author Luke081515
	* @param $title - title of the page
	* @param $forcelinkupdate - default 0 - if 1, updates the links tables.
	* @param $forcerecursivelinkupdate - default 0 - if 1, updates the links table,
	* @param $forcerecursivelinkupdate - and update the links tables for any page that uses this page as a template.
	* @return boolean - true if successful, false if page was not found
	*/
	public function purge ($title, $forcelinkupdate = 0, $forcerecursivelinkupdate = 0) {
Luke081515's avatar
Luke081515 committed
739
		$request = "action=purge"
Luke081515's avatar
Luke081515 committed
740 741 742 743 744 745
			. "&format=json"
			. "&forcelinkupdate=" . $forcelinkupdate
			. "&forcerecursivelinkupdate=" . $forcerecursivelinkupdate
			. "&titles=" . urlencode($title)
			. "&assert=" . $this->assert
			. "&maxlag=" . $this->maxlag;
Luke081515's avatar
Luke081515 committed
746
		$result = $this->httpRequest($request, $this->job);
Luke081515's avatar
Luke081515 committed
747 748 749 750 751 752 753
		$result = json_decode($result, true);
		//
		if (isset($result['purge'][0]['missing'])) {
			return false;
		}
		return true;
	}
Luke081515's avatar
Luke081515 committed
754 755 756 757 758
	/** patrol
	* Marks the specified version as reviewed or not reviewed
	* @author Luke081515
	* @param $id - the revid or rcid to patrol
	* @param $revid [optional: true] - if true, you use a revid, otherwise a rcid
759
	* @return string / bool - true if successful, otherwise the API error-code
Luke081515's avatar
Luke081515 committed
760 761 762
	*/
	public function patrol($id, $revid = true) {
		$token = $this->requireToken("patrol");
Luke081515's avatar
Luke081515 committed
763
		$request = "action=patrol&format=json&assert=" . $this->assert . "&maxlag=" . $this->maxlag .
Luke081515's avatar
Luke081515 committed
764 765
			"&token=" . urlencode($token);
		if ($revid) {
Luke081515's avatar
Luke081515 committed
766
			$request = $request . "&revid=" . urlencode($id);
Luke081515's avatar
Luke081515 committed
767
		} else {
Luke081515's avatar
Luke081515 committed
768
			$request = $request . "&rcid=" . urlencode($id);
Luke081515's avatar
Luke081515 committed
769
		}
Luke081515's avatar
Luke081515 committed
770
		$result = $this->httpRequest($request, $this->job);
Luke081515's avatar
Luke081515 committed
771 772 773 774 775 776 777 778
		$result = json_decode($result, true);
		if (array_key_exists("error", $result)) {
			if ($result["error"]["code"] === "patroldisabled" || $result["error"]["code"] === "nosuchrcid" || $result["error"]["code"] === "noautopatrol") {
				return $result["error"]["code"];
			} else {
				return $this->checkResult($result["error"]["code"]);
			}
		} else {
779
			return true;
Luke081515's avatar
Luke081515 committed
780 781
		}
	}
Luke081515's avatar
Luke081515 committed
782 783
	/** review
	* Marks the specified version as reviewed or not reviewed
Luke081515's avatar
Luke081515 committed
784
	* @param $revid - the revid of the revision to approve/unapprove
Luke081515's avatar
Luke081515 committed
785
	* @param $comment [optional: ""] - the comment to add for the log
Luke081515's avatar
Luke081515 committed
786
	* @param $unapprove [optional: 0] - if 1: mark the rev as unreviewed instead of reviewed
Freddy's avatar
Freddy committed
787
	* @return string - success if succesful, otherwise the API error-code
Luke081515's avatar
Luke081515 committed
788
	*/
Luke081515's avatar
Luke081515 committed
789
	public function review($revid, $comment = "", $unapprove = 0) {
Luke081515's avatar
Luke081515 committed
790
		$token = $this->requireToken();
Luke081515's avatar
Luke081515 committed
791
		$request = "action=review&format=json&assert=" . $this->assert .
Luke081515's avatar
Luke081515 committed
792
			"&maxlag=" . $this->maxlag .
Luke081515's avatar
Luke081515 committed
793 794 795
			"&revid=" . urlencode($revid) .
			"&comment=" . urlencode($comment) .
			"&token=" . urlencode($token);
Luke081515's avatar
Luke081515 committed
796 797 798
		if ($unapprove) {
			$request = $request . "&unapprove=1";
		}
Luke081515's avatar
Luke081515 committed
799
		$result = $this->httpRequest($request, $this->job);
Luke081515's avatar
Luke081515 committed
800
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
801
		if (array_key_exists("error", $result)) {
Luke081515's avatar
Luke081515 committed
802
			if ($result["error"]["code"] === "notreviewable") {
Luke081515's avatar
Luke081515 committed
803
				return $result["error"]["code"];
Luke081515's avatar
Luke081515 committed
804
			} else {
Luke081515's avatar
Luke081515 committed
805
				return $this->checkResult($result["error"]["code"]);
Luke081515's avatar
Luke081515 committed
806 807
			}
		} else {
Luke081515's avatar
Luke081515 committed
808
			return "success";
Luke081515's avatar
Luke081515 committed
809
		}
Luke081515's avatar
Luke081515 committed
810
	}
Luke081515's avatar
Luke081515 committed
811
	// User related functions
Luke081515's avatar
Luke081515 committed
812 813 814 815 816 817
	/** checkUserExistence
	* checks if a user exists
	* @author Luke081515
	* @param $username - The username of the user
	* @returs true if the user does exist, false if not
	*/
Luke081515's avatar
Luke081515 committed
818
	public function checkUserExistence($username) {
819
		$result = $this->httpRequest('action=query&format=json&list=users&usprop=&ususers=' . urlencode($username), $this->job, 'GET');
Luke081515's avatar
Luke081515 committed
820
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
821
		if (isset($result['query']['users'][0]['missing'])) {
822
			return false;
Luke081515's avatar
Luke081515 committed
823
		}
824
		return true;
Luke081515's avatar
Luke081515 committed
825
	}
Luke081515's avatar
Luke081515 committed
826 827 828 829 830 831 832
	/** getUserEditcount
	* returns the editcount of a user, false if the user does not exist
	* @author Luke081515
	* @param $username - The username of the user
	* @returs editcount as int if the user does exist, false if not
	*/
	public function getUserEditcount ($username) {
Luke081515's avatar
Luke081515 committed
833
		$result = $this->httpRequest("action=query&format=json&list=users&usprop=editcount&ususers=" . urlencode($username), $this->job, "GET");
Luke081515's avatar
Luke081515 committed
834
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
835
		if (isset($result['query']['users'][0]['missing'])) {
Luke081515's avatar
Luke081515 committed
836
			return false;
Luke081515's avatar
Luke081515 committed
837
		}
Luke081515's avatar
Luke081515 committed
838
		return $result['query']['users'][0]['editcount'];
Luke081515's avatar
Luke081515 committed
839
	}
Luke081515's avatar
Luke081515 committed
840 841 842 843 844 845 846
	/** checkUserBlock
	* checks if a user is blocked
	* @author Luke081515
	* @param $username - The username of the user
	* @returs true if blocked, false if not
	*/
	public function checkUserBlock ($username) {
Luke081515's avatar
Luke081515 committed
847
		$result = $this->httpRequest("action=query&format=json&list=blocks&bkusers=" . urlencode($username), $this->job, "GET");
Luke081515's avatar
Luke081515 committed
848
		if (strpos($result, "reason") !== false) {
Luke081515's avatar
Luke081515 committed
849
			return true;
Luke081515's avatar
Luke081515 committed
850
		}
Luke081515's avatar
Luke081515 committed
851 852
		return false;
	}
Luke081515's avatar
Luke081515 committed
853 854 855 856 857 858 859 860
	/** checkUserMail
	* checks if the user has the mail feature active
	* @author Luke081515
	* @param $username - The username of the user
	* @returs false if not or if the user does not exist, true otherwise
	*/
	public function checkUserMail ($username) {
		$result = $this->httpRequest('action=query&format=json&list=users&usprop=emailable&ususers=' . urlencode($username), $this->job, 'GET');
Luke081515's avatar
Luke081515 committed
861
		if (strpos($result, "emailable") !== false) {
Luke081515's avatar
Luke081515 committed
862
			return true;
Luke081515's avatar
Luke081515 committed
863
		}
Luke081515's avatar
Luke081515 committed
864 865
		return false;
	}
866 867 868 869 870 871 872 873 874 875 876 877 878
	/** getUserGroups
	* returns the groups of a user, false if the user does not exist
	* @author Luke081515
	* @param $username - The username of the user
	* @returs the groups as array if the user does exist, false if not
	*/
	public function getUserGroups ($username) {
		$result = $this->httpRequest('action=query&format=json&list=users&usprop=groups&ususers=' . urlencode($username), $this->job, 'GET');
		if (strpos($result, "missing") !== false)
			return false;
		$result = json_decode($result, true);
		return $result['query']['users'][0]['groups'];
	}
Luke081515's avatar
Luke081515 committed
879
	/** getUserGender
~rs's avatar
~rs committed
880 881 882 883 884 885
	* returns the gender a user has set in the settings
	* @author KPFC
	* @param $username – The username of the user
	* @return the gender as string ('female', 'male' or 'unknown')
	*/
	public function getUserGender ($username) {
~rs's avatar
~rs committed
886 887 888 889 890 891 892
		$result = $this->httpRequest('action=query&format=json&list=users&usprop=gender&ususers=' . urlencode($username), $this->job, 'GET');
		$result = json_decode($result, true);
		if (isset($result['query']['users'][0]['missing'])) {
			return false;
		}
		return $result['query']['users'][0]['gender'];
	}
Luke081515's avatar
Luke081515 committed
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
	/** getCreatedPages
	* returns all created pages of a user
	* @author Luke081515
	* @param $username – The username of the user
	* @param [optional: null] - if set, restricts the results to the selected namespaces
	* @return an array with the pagetitles, or false if no pages got created yet
	*/
	public function getCreatedPages($user, $namespace = null) {
		if ($namespace === null) {
			$result = $this->httpRequest("action=query&format=json&list=usercontribs&uclimit=max&ucuser=" . urlencode($user)
				. "&ucdir=older&ucshow=new", $this->job, "GET");
		} else {
			$result = $this->httpRequest("action=query&format=json&list=usercontribs&uclimit=max&ucuser=" . urlencode($user)
				. "&ucdir=older&ucshow=new&ucnamespace=" . $namespace, $this->job, "GET");
		}
		$result = json_decode($result, true);
		for ($a = 0; isset($result["query"]["usercontribs"][$a]["title"]); $a++) {
			$createResults[$a] = $result["query"]["usercontribs"][$a]["title"];
		}
		if (!isset($createResults[0])) {
			return false;
		}
		return $createResults;
	}
Luke081515's avatar
Luke081515 committed
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
	/** checkTemplate
	* checks if a template is used at a page (transcluded)
	* @author Luke081515
	* @param $page - the page to check
	* @param $templat - the name of the template, including the prefix (e.g. Template:Test)
	* @return true if template is transcluded, false if not, or if the page does not exist
	*/
	public function checkTemplate($page, $template) {
		$page = str_replace(" ", "_", $page);
		$data = "action=query&format=json&prop=templates&tllimit=1&assert=" . $this->assert . "&maxlag=" . $this->maxlag
			. "&titles=" . urlencode($page)
			. "&tltemplates=" . urlencode($template);
		$result = $this->httpRequest($data, $this->job, "GET");
		if (strpos($result, ",\"templates\":[{\"ns\":") === false) {
			return false;
		}
		return true;
	}
935
	/** getCatMembers
Luke081515's avatar
Luke081515 committed
936 937
	* reads out all category members of a category, including subcategories
	* works till you have more than 5000 subcategories per category
938
	* @author Luke081515
Luke081515's avatar
Luke081515 committed
939 940
	* @param $kat - category, which should get analyzed
	* @param $onlySubCats - [optional: false] if true, only the subcategories will returned, not the pagetitles
Luke081515's avatar
Luke081515 committed
941
	* @param $excludeWls - [optional: false] if true, you won"t get categories with redirects
Freddy's avatar
Freddy committed
942
	* @return false if the categories has no members, otherwise a serialized array with page titles
943
	*/
944
	public function getCatMembers($kat, $onlySubCats = false, $excludeWls = false) {
Luke081515's avatar
Luke081515 committed
945
		$b = 0;
946
		$subCat[0] = $kat;
Luke081515's avatar
Luke081515 committed
947 948 949
		$result = $this->httpRequest("action=query&list=categorymembers&format=json&cmtitle=" . urlencode($kat) .
			"&cmprop=title&cmtype=subcat&cmlimit=max&assert=" . $this->assert . "&maxlag=" . $this->maxlag .
			"&cmsort=sortkey&cmdir=ascending&rawcontinue=", $this->job, "GET");
Luke081515's avatar
Luke081515 committed
950
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
951
		$a = 0;
Luke081515's avatar
Luke081515 committed
952
		if (isset($result["query"]["categorymembers"][$a]["title"])) {
953
			$Sub = true;
Luke081515's avatar
Luke081515 committed
954 955
			while (isset($result["query"]["categorymembers"][$a]["title"])) {
				$subCat[$b] = $result["query"]["categorymembers"][$a]["title"];
956 957 958 959
				$b++;
				$a++;
			}
		}
Luke081515's avatar
Luke081515 committed
960 961
		$b = 0;
		$c = 0;
Luke081515's avatar
Luke081515 committed
962
		if ($onlySubCats === true) {
963
			return $subCat;
Luke081515's avatar
Luke081515 committed
964
		}
Luke081515's avatar
Luke081515 committed
965
		if ($excludeWls === false) {
966
			while (isset($subCat[$b]))
967
			{
Luke081515's avatar
Luke081515 committed
968 969
				$result = $this->httpRequest("action=query&list=categorymembers&format=json&cmtitle=" . urlencode($subCat[$b]) .
					"&cmprop=title&cmtype=page&cmlimit=max&cmsort=sortkey&cmdir=ascending&rawcontinue=", $this->job, "GET");
Luke081515's avatar
Luke081515 committed
970
				$result = json_decode($result, true);
971
				$Cont = false;
Luke081515's avatar
Luke081515 committed
972 973
				if (isset($result["query-continue"]["categorymembers"]["cmcontinue"])) {
					$Continue = $result["query-continue"]["categorymembers"]["cmcontinue"];
974 975 976
					$Cont = true;
				}
				while ($Cont === true) {
Luke081515's avatar
Luke081515 committed
977
					$a = 0;
Luke081515's avatar
Luke081515 committed
978 979 980
					if (isset($result["query"]["categorymembers"][$a]["title"])) {
						while (isset($result["query"]["categorymembers"][$a]["title"])) {
							$page[$c] = $result["query"]["categorymembers"][$a]["title"];
981 982 983
							$c++;
							$a++;
						}
984
					}
Luke081515's avatar
Luke081515 committed
985 986 987
					$result = $this->httpRequest("action=query&list=categorymembers&format=json&cmcontinue=" . $Continue
						. "&cmtitle=" . urlencode($subCat[$b])
						. "&cmprop=title&cmtype=page&cmlimit=max&cmsort=sortkey&cmdir=ascending&rawcontinue=", $this->job, "GET");
Luke081515's avatar
Luke081515 committed
988 989 990
					$result = json_decode($result, true);
					if (isset($result["query-continue"]["categorymembers"]["cmcontinue"])) {
						$Continue = $result["query-continue"]["categorymembers"]["cmcontinue"];
991
						$Cont = true;
Luke081515's avatar
Luke081515 committed
992
					} else {
993
						$Cont = false;
Luke081515's avatar
Luke081515 committed
994
					}
995
				}
Luke081515's avatar
Luke081515 committed
996
				$a = 0;
Luke081515's avatar
Luke081515 committed
997 998 999
				if (isset($result["query"]["categorymembers"][$a]["title"]) === true) {
					while (isset($result["query"]["categorymembers"][$a]["title"])) {
						$page[$c] = $result["query"]["categorymembers"][$a]["title"];
1000 1001 1002
						$c++;
						$a++;
					}
1003
				}
1004 1005 1006
				$b++;
			}
		} else {
1007
			while (isset($subCat[$b])) {
Luke081515's avatar
Luke081515 committed
1008 1009
				$result = $this->httpRequest("action=query&format=json&generator=categorymembers&gcmtitle=" . urlencode($subCat[$b]) .
					"&prop=info&gcmlimit=max&rawcontinue=&redirects", $this->job, "GET");
Luke081515's avatar
Luke081515 committed
1010
				$result = json_decode($result, true);
1011
				$Cont = false;
Luke081515's avatar
Luke081515 committed
1012 1013
				if (isset($result["query-continue"]["categorymembers"]["gcmcontinue"])) {
					$Continue = $result["query-continue"]["categorymembers"]["gcmcontinue"];
1014 1015
					$Cont = true;
				}
1016
				while ($Cont === true) {
Luke081515's avatar
Luke081515 committed
1017
					$a = 0;
Luke081515's avatar
Luke081515 committed
1018 1019 1020
					if (isset($result["query"]["pages"][$a]["title"])) {
						while (isset($result["query"]["pages"][$a]["title"])) {
							$page[$c] = $result["query"]["pages"][$a]["title"];
1021 1022 1023
							$c++;
							$a++;
						}
1024
					}
1025
					try {
Luke081515's avatar
Luke081515 committed
1026 1027 1028
						$result = $this->httpRequest("action=query&format=json&generator=categorymembers&gcmtitle=" . urlencode($subCat[$b]) .
							"&gmcontinue=" . $Continue .
							"&prop=info&gcmlimit=max&rawcontinue=&redirects", $this->job, "GET");
1029 1030 1031
					} catch (Exception $e) {
						throw $e;
					}
Luke081515's avatar
Luke081515 committed
1032 1033 1034
					$result = $this->httpRequest("action=query&format=json&generator=categorymembers&gcmtitle=" . urlencode($subCat[$b]) .
						"&gmcontinue=" . $Continue .
						"&prop=info&gcmlimit=max&rawcontinue=&redirects", $this->job, "GET");
Luke081515's avatar
Luke081515 committed
1035 1036 1037
					$result = json_decode($result, true);
					if (isset($result["query-continue"]["pages"]["gcmcontinue"])) {
						$Continue = $result["query-continue"]["pages"]["gcmcontinue"];
1038
						$Cont = true;
Luke081515's avatar
Luke081515 committed
1039
					} else {
1040
						$Cont = false;
Luke081515's avatar
Luke081515 committed
1041
					}
1042
				}
Luke081515's avatar
Luke081515 committed
1043
				$a = 0;
Luke081515's avatar
Luke081515 committed
1044 1045 1046
				if (isset($result["query"]["pages"][$a]["title"])) {
					while (isset($result["query"]["pages"][$a]["title"])) {
						$page[$c] = $result["query"]["pages"][$a]["title"];
1047 1048 1049
						$c++;
						$a++;
					}
1050
				}
1051 1052