BotCore.php 67.7 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 373
			return null;
		}
		return $page['query']['pages'][$pageID]['revisions'][0]['*'];
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 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) {
Luke081515's avatar
Luke081515 committed
384 385
		$request = "action=query&prop=revisions&format=json&rvprop=content&rvlimit=1&rvcontentformat=text%2Fx-wiki&titles=" . urlencode($title) .
			"&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 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) {
Luke081515's avatar
Luke081515 committed
397 398
		$request = "action=query&prop=revisions&format=json&rvprop=content&rvlimit=1&rvcontentformat=text%2Fx-wiki&pageids=" . urlencode($pageID) .
			"&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 403
	* Returns the content of a JS page
	* @param $title - title of the page
404
	* @author MGChecker
Freddy's avatar
Freddy committed
405
	* @return text of the page
Luke081515's avatar
Luke081515 committed
406 407
	** false if there is an unknown error
	** null if the page does not exist
408
	*/
409
	public function readPageJs($title) {
Luke081515's avatar
Luke081515 committed
410 411
		$request = "action=query&prop=revisions&format=json&rvprop=content&rvlimit=1&rvcontentformat=text%2Fjavascript&titles=" . urlencode($title) .
			"&rvdir=older&assert=" . $this->assert . "&maxlag=" . $this->maxlag . "&rawcontinue=&indexpageids=1";
412
		return $this->readPageEngine($request);
413 414
	}
	/** readPageCss
Luke081515's avatar
Luke081515 committed
415 416
	* Returns the content of a CSS page
	* @param $title - title of the page
417
	* @author MGChecker
Freddy's avatar
Freddy committed
418
	* @return text of the page
Luke081515's avatar
Luke081515 committed
419 420
	** false if there is an unknown error
	** null if the page does not exist
421
	*/
422
	public function readPageCss($title) {
Luke081515's avatar
Luke081515 committed
423 424
		$request = "action=query&prop=revisions&format=json&rvprop=content&rvlimit=1&rvcontentformat=text%2Fcss&titles=" . urlencode($title) .
			"&rvdir=older&assert=" . $this->assert . "&maxlag=" . $this->maxlag . "&rawcontinue=&indexpageids=1";
425
		return $this->readPageEngine($request);
426
	}
427
	/** readSection
Luke081515's avatar
Luke081515 committed
428
	* returns the content of a specified section
Luke081515's avatar
Luke081515 committed
429 430
	* @param $title - name of the page
	* @param $section - number of the section
431
	* @author MGChecker
Freddy's avatar
Freddy committed
432
	* @return text of the section
Luke081515's avatar
Luke081515 committed
433 434
	** false if there is an unknown error
	** null if the page does not exist
435
	*/
436
	public function readSection($title, $section) {
Luke081515's avatar
Luke081515 committed
437 438
		$request = "action=query&prop=revisions&format=json&rvprop=content&rvlimit=1&rvcontentformat=text%2Fx-wiki&rvdir=older&indexpageids=1&rvsection=" . urlencode($section) .
			"&assert=" . $this->assert . "&maxlag=" . $this->maxlag . "&titles=" . urlencode($title);
439
		return $this->readPageEngine($request);
440
	}
441
	/** getTableOfContents
Luke081515's avatar
Luke081515 committed
442 443
	* returns the Table of Contents of a page
	* @param $page - Title of the page
444
	* @author Luke081515
Freddy's avatar
Freddy committed
445 446
	* @return two-dimensional array
	* @return first dimension: the section
Luke081515's avatar
Luke081515 committed
447
	* @retuns second dimension:
448
	* 	[0] => level;
Luke081515's avatar
Luke081515 committed
449 450 451
	* 	[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;
452
	*/
Luke081515's avatar
Luke081515 committed
453
	public function getTableOfContents($title) {
Luke081515's avatar
Luke081515 committed
454 455
		$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
456 457 458 459 460 461
		$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"];
462 463 464
		}
		return $ret;
	}
Freddy's avatar
Freddy committed
465 466
	/** getEchoNotifications
	 * gets Notifications
Luke081515's avatar
Luke081515 committed
467
	 * requires an installed echo extension in MediaWiki
Freddy's avatar
Freddy committed
468 469 470 471
	 * @author Freddy2001
	 * @returns Array with all notifications
	 */
	public function getEchoNotifications() {
Luke081515's avatar
Luke081515 committed
472 473 474
		$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
475
	}
476
	/** editPageEngine
Luke081515's avatar
Luke081515 committed
477 478 479
	* 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
480
	* @param $summary - summary
Luke081515's avatar
Luke081515 committed
481 482 483 484 485
	* @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?
486
	* @author Hgzh / Luke081515 / MGChecker
Freddy's avatar
Freddy committed
487
	* @return unserialized answer of the api, if successful
488
	*/
489
	private function editPageEngine($title, $content, $summary, $botflag, $minorflag, $noCreate = 1, $sectionnumber = -1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
490 491 492 493 494
		while (true) {
			if ($overrideNobots !== true) {
				if ($this->allowBots($this->readPage($title)) === false) {
					return "nobots";
				}
Luke081515's avatar
Luke081515 committed
495
			}
Luke081515's avatar
Luke081515 committed
496 497 498 499 500
			$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) .
501 502 503 504 505 506 507 508 509
				"&summary=" . urlencode($summary);
			if ($botflag) {
				$request .= "&bot=";
			}
			if ($minorflag) {
				$request .= "&minor=";
			}
			if ($noCreate) {
				$request .= "&nocreate=";
Luke081515's avatar
Luke081515 committed
510 511 512 513 514 515 516 517 518 519 520 521 522 523
			}
			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
524
			} else {
Luke081515's avatar
Luke081515 committed
525
				return array($result["edit"]["oldrevid"], $result["edit"]["newrevid"]);
tools.freddy2001's avatar
tools.freddy2001 committed
526
			}
527
		}
528
	}
529
	/** editPage
Luke081515's avatar
Luke081515 committed
530 531 532 533 534 535
	* 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
536
	* @author Freddy2001 / Luke081515
Freddy's avatar
Freddy committed
537
	* @return unserialized answer of the api, if successful
538
	*/
539
	public function editPage($title, $content, $summary, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
540
		if ($this->assert == "bot") {
541
			$botflag = true;
Luke081515's avatar
Luke081515 committed
542
		} else {
543
			$botflag = false;
Luke081515's avatar
Luke081515 committed
544
		}
545
		return $this->editPageEngine($title, $content, $summary, $botflag, false, $noCreate, -1, $overrideNobots);
546 547
	}
	/** editPageMinor
Luke081515's avatar
Luke081515 committed
548 549 550 551 552 553
	* 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
554
	* @author Freddy2001 / Luke081515
Freddy's avatar
Freddy committed
555
	* @return unserialized answer of the api, if successful
556
	*/
557
	public function editPageMinor($title, $content, $summary, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
558
		if ($this->assert == "bot") {
559
			$botflag = true;
Luke081515's avatar
Luke081515 committed
560
		} else {
561
			$botflag = false;
Luke081515's avatar
Luke081515 committed
562
		}
563
		return $this->editPageEngine($title, $content, $summary, $botflag, true, $noCreate, -1, $overrideNobots);
564
	}
565
	/** editPageD
Luke081515's avatar
Luke081515 committed
566 567 568 569 570 571 572 573
	* 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
574
	* @author MGChecker / Luke081515
Freddy's avatar
Freddy committed
575
	* @return unserialized answer of the api, if successful
576
	*/
577 578
	public function editPageD($title, $content, $summary, $botflag, $minorflag, $noCreate = 1, $overrideNobots = false) {
		return $this->editPageEngine($title, $content, $summary, $botflag, $minorflag, $noCreate, -1, $overrideNobots);
579
	}
580
	/** editSection
Luke081515's avatar
Luke081515 committed
581 582 583 584 585 586 587
	* 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
588
	* @author Freddy2001 / MGChecker / Luke081515
Freddy's avatar
Freddy committed
589
	* @return unserialized answer of the api, if successful
590
	*/
591
	public function editSection($title, $content, $summary, $sectionnumber, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
592
		if ($this->assert == "bot") {
593
			$botflag = true;
Luke081515's avatar
Luke081515 committed
594
		} else {
595
			$botflag = false;
Luke081515's avatar
Luke081515 committed
596 597
		}
		if ($sectionnumber < 0) {
Luke081515's avatar
Luke081515 committed
598
			throw new Exception("You selected a invalid section number. To edit a whole page, use editPage().");
Luke081515's avatar
Luke081515 committed
599
		}
600
		return $this->editPageEngine($title, $content, $summary, $botflag, false, $noCreate, $sectionnumber, $overrideNobots);
601
	}
Luke081515's avatar
Luke081515 committed
602 603 604 605 606 607 608 609
	/** 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
610
	* @author Freddy2001 / MGChecker / Luke081515
Freddy's avatar
Freddy committed
611
	* @return unserialized answer of the api, if successful
612
	*/
613
	public function editSectionMinor($title, $content, $summary, $sectionnumber, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
614
		if ($this->assert == "bot") {
615
			$botflag = true;
Luke081515's avatar
Luke081515 committed
616
		} else {
617
			$botflag = false;
Luke081515's avatar
Luke081515 committed
618 619
		}
		if ($sectionnumber < 0) {
Luke081515's avatar
Luke081515 committed
620
			throw new Exception("You selected a invalid section number. To edit a whole page, use editPageMinor().");
Luke081515's avatar
Luke081515 committed
621
		}
622
		return $this->editPageEngine($title, $content, $summary, $botflag, true, $noCreate, $sectionnumber, $overrideNobots);
623
	}
Luke081515's avatar
Luke081515 committed
624 625 626 627 628 629 630 631 632 633
	/** 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
634
	* @author MGChecker / Luke081515
Freddy's avatar
Freddy committed
635
	* @return unserialized answer of the api, if successful
636
	*/
637
	public function editSectionD($title, $content, $summary, $sectionnumber, $botflag, $minorflag, $noCreate = 1, $overrideNobots = false) {
Luke081515's avatar
Luke081515 committed
638
		if ($sectionnumber < 0) {
Luke081515's avatar
Luke081515 committed
639
			throw new Exception("You selected a invalid section number. To edit a whole page, use editPageD().");
Luke081515's avatar
Luke081515 committed
640
		}
641
		return $this->editPageEngine($title, $content, $summary, $botflag,  $minorflag, $noCreate, $sectionnumber, $overrideNobots);
642
	}
643
	/** movePage
Luke081515's avatar
Luke081515 committed
644 645 646 647 648 649 650
	* 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
651
	* @return serialized answer of the API
652
	*/
653
	public function movePage($oldTitle, $newTitle, $reason, $bot = 0, $movetalk = 1, $noredirect = 1) {
654
		$token = $this->requireToken();
Luke081515's avatar
Luke081515 committed
655
		$request = "action=move&format=json&assert=" . $this->assert . "&maxlag=" . $this->maxlag .
Luke081515's avatar
Luke081515 committed
656 657 658 659 660 661 662
			"&from=" . urlencode($oldTitle) .
			"&to=" . urlencode($newTitle) .
			"&reason=" . urlencode($reason) .
			"&bot=" . $bot .
			"&movetalk=" . $movetalk .
			"&noredirect=" . $noredirect .
			"&token=" . urlencode($token);
Luke081515's avatar
Luke081515 committed
663
		$result = $this->httpRequest($request, $this->job);
664
		return serialize(json_decode($result, true));
665
	}
Luke081515's avatar
Luke081515 committed
666 667 668 669 670 671 672 673
	/** 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
674 675
	* @return false - if the rollback failed
	* @return null - if there was a conflict when doing a rollback
Luke081515's avatar
Luke081515 committed
676 677 678 679 680 681 682 683 684 685 686 687 688 689
	*/
	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
690 691 692
			$result = json_decode($result, true);
			if (array_key_exists("error", $result)) {
				$code = $this->checkResult($result["error"]["code"]);
Luke081515's avatar
Luke081515 committed
693
				if ($code === "fail") {
694
					return false;
Luke081515's avatar
Luke081515 committed
695 696 697
				} else if ($code === "retry") {
					sleep(5);
				} else if ($code === "conflict") {
698
					return null;
Luke081515's avatar
Luke081515 committed
699 700 701 702 703 704
				}
			} else {
				return true;
			}
		}
	}
Luke081515's avatar
Luke081515 committed
705 706
	/** watch
	* Allows to put a page on your watchlist, or remove it
Luke081515's avatar
Luke081515 committed
707
	* @param $title - title of the page
Luke081515's avatar
Luke081515 committed
708
	* @param $unwatch - default 0 - if 1, the page will get removed from the list
Freddy's avatar
Freddy committed
709
	* @return mixed - true if successful, otherwise the API error code
Luke081515's avatar
Luke081515 committed
710 711
	*/
	public function watch ($title, $unwatch = 0) {
Luke081515's avatar
Luke081515 committed
712
		$token = $this->requireToken("watch");
Luke081515's avatar
Luke081515 committed
713
		$request = "action=watch"
Luke081515's avatar
Luke081515 committed
714 715 716 717 718 719
			. "&format=json"
			. "&unwatch=" . $unwatch
			. "&titles=" . urlencode($title)
			. "&token=" . urlencode($token)
			. "&assert=" . $this->assert
			. "&maxlag=" . $this->maxlag;
Luke081515's avatar
Luke081515 committed
720
		$result = $this->httpRequest($request, $this->job);
Luke081515's avatar
Luke081515 committed
721
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
722
		if (array_key_exists("error", $result)) {
Luke081515's avatar
Luke081515 committed
723
			return $result["error"]["code"];
Luke081515's avatar
Luke081515 committed
724
		}
Luke081515's avatar
Luke081515 committed
725 726
		return true;
	}
Luke081515's avatar
Luke081515 committed
727 728 729 730 731 732 733 734 735 736
	/** 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
737
		$request = "action=purge"
Luke081515's avatar
Luke081515 committed
738 739 740 741 742 743
			. "&format=json"
			. "&forcelinkupdate=" . $forcelinkupdate
			. "&forcerecursivelinkupdate=" . $forcerecursivelinkupdate
			. "&titles=" . urlencode($title)
			. "&assert=" . $this->assert
			. "&maxlag=" . $this->maxlag;
Luke081515's avatar
Luke081515 committed
744
		$result = $this->httpRequest($request, $this->job);
Luke081515's avatar
Luke081515 committed
745 746 747 748 749 750 751
		$result = json_decode($result, true);
		//
		if (isset($result['purge'][0]['missing'])) {
			return false;
		}
		return true;
	}
Luke081515's avatar
Luke081515 committed
752 753 754 755 756
	/** 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
757
	* @return string / bool - true if successful, otherwise the API error-code
Luke081515's avatar
Luke081515 committed
758 759 760
	*/
	public function patrol($id, $revid = true) {
		$token = $this->requireToken("patrol");
Luke081515's avatar
Luke081515 committed
761
		$request = "action=patrol&format=json&assert=" . $this->assert . "&maxlag=" . $this->maxlag .
Luke081515's avatar
Luke081515 committed
762 763
			"&token=" . urlencode($token);
		if ($revid) {
Luke081515's avatar
Luke081515 committed
764
			$request = $request . "&revid=" . urlencode($id);
Luke081515's avatar
Luke081515 committed
765
		} else {
Luke081515's avatar
Luke081515 committed
766
			$request = $request . "&rcid=" . urlencode($id);
Luke081515's avatar
Luke081515 committed
767
		}
Luke081515's avatar
Luke081515 committed
768
		$result = $this->httpRequest($request, $this->job);
Luke081515's avatar
Luke081515 committed
769 770 771 772 773 774 775 776
		$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 {
777
			return true;
Luke081515's avatar
Luke081515 committed
778 779
		}
	}
Luke081515's avatar
Luke081515 committed
780 781
	/** review
	* Marks the specified version as reviewed or not reviewed
Luke081515's avatar
Luke081515 committed
782
	* @param $revid - the revid of the revision to approve/unapprove
Luke081515's avatar
Luke081515 committed
783
	* @param $comment [optional: ""] - the comment to add for the log
Luke081515's avatar
Luke081515 committed
784
	* @param $unapprove [optional: 0] - if 1: mark the rev as unreviewed instead of reviewed
Freddy's avatar
Freddy committed
785
	* @return string - success if succesful, otherwise the API error-code
Luke081515's avatar
Luke081515 committed
786
	*/
Luke081515's avatar
Luke081515 committed
787
	public function review($revid, $comment = "", $unapprove = 0) {
Luke081515's avatar
Luke081515 committed
788
		$token = $this->requireToken();
Luke081515's avatar
Luke081515 committed
789
		$request = "action=review&format=json&assert=" . $this->assert .
Luke081515's avatar
Luke081515 committed
790
			"&maxlag=" . $this->maxlag .
Luke081515's avatar
Luke081515 committed
791 792 793
			"&revid=" . urlencode($revid) .
			"&comment=" . urlencode($comment) .
			"&token=" . urlencode($token);
Luke081515's avatar
Luke081515 committed
794 795 796
		if ($unapprove) {
			$request = $request . "&unapprove=1";
		}
Luke081515's avatar
Luke081515 committed
797
		$result = $this->httpRequest($request, $this->job);
Luke081515's avatar
Luke081515 committed
798
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
799
		if (array_key_exists("error", $result)) {
Luke081515's avatar
Luke081515 committed
800
			if ($result["error"]["code"] === "notreviewable") {
Luke081515's avatar
Luke081515 committed
801
				return $result["error"]["code"];
Luke081515's avatar
Luke081515 committed
802
			} else {
Luke081515's avatar
Luke081515 committed
803
				return $this->checkResult($result["error"]["code"]);
Luke081515's avatar
Luke081515 committed
804 805
			}
		} else {
Luke081515's avatar
Luke081515 committed
806
			return "success";
Luke081515's avatar
Luke081515 committed
807
		}
Luke081515's avatar
Luke081515 committed
808
	}
Luke081515's avatar
Luke081515 committed
809
	// User related functions
Luke081515's avatar
Luke081515 committed
810 811 812 813 814 815
	/** 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
816
	public function checkUserExistence($username) {
817
		$result = $this->httpRequest('action=query&format=json&list=users&usprop=&ususers=' . urlencode($username), $this->job, 'GET');
Luke081515's avatar
Luke081515 committed
818
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
819
		if (isset($result['query']['users'][0]['missing'])) {
820
			return false;
Luke081515's avatar
Luke081515 committed
821
		}
822
		return true;
Luke081515's avatar
Luke081515 committed
823
	}
Luke081515's avatar
Luke081515 committed
824 825 826 827 828 829 830
	/** 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
831
		$result = $this->httpRequest("action=query&format=json&list=users&usprop=editcount&ususers=" . urlencode($username), $this->job, "GET");
Luke081515's avatar
Luke081515 committed
832
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
833
		if (isset($result['query']['users'][0]['missing'])) {
Luke081515's avatar
Luke081515 committed
834
			return false;
Luke081515's avatar
Luke081515 committed
835
		}
Luke081515's avatar
Luke081515 committed
836
		return $result['query']['users'][0]['editcount'];
Luke081515's avatar
Luke081515 committed
837
	}
Luke081515's avatar
Luke081515 committed
838 839 840 841 842 843 844
	/** 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
845
		$result = $this->httpRequest("action=query&format=json&list=blocks&bkusers=" . urlencode($username), $this->job, "GET");
Luke081515's avatar
Luke081515 committed
846
		if (strpos($result, "reason") !== false) {
Luke081515's avatar
Luke081515 committed
847
			return true;
Luke081515's avatar
Luke081515 committed
848
		}
Luke081515's avatar
Luke081515 committed
849 850
		return false;
	}
Luke081515's avatar
Luke081515 committed
851 852 853 854 855 856 857 858
	/** 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
859
		if (strpos($result, "emailable") !== false) {
Luke081515's avatar
Luke081515 committed
860
			return true;
Luke081515's avatar
Luke081515 committed
861
		}
Luke081515's avatar
Luke081515 committed
862 863
		return false;
	}
864 865 866 867 868 869 870 871 872 873 874 875 876
	/** 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
877
	/** getUserGender
~rs's avatar
~rs committed
878 879 880 881 882 883
	* 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
884 885 886 887 888 889 890
		$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
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
	/** 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
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
	/** 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;
	}
933
	/** getCatMembers
Luke081515's avatar
Luke081515 committed
934 935
	* reads out all category members of a category, including subcategories
	* works till you have more than 5000 subcategories per category
936
	* @author Luke081515
Luke081515's avatar
Luke081515 committed
937 938
	* @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
939
	* @param $excludeWls - [optional: false] if true, you won"t get categories with redirects
Freddy's avatar
Freddy committed
940
	* @return false if the categories has no members, otherwise a serialized array with page titles
941
	*/
942
	public function getCatMembers($kat, $onlySubCats = false, $excludeWls = false) {
Luke081515's avatar
Luke081515 committed
943
		$b = 0;
944
		$subCat[0] = $kat;
Luke081515's avatar
Luke081515 committed
945 946 947
		$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
948
		$result = json_decode($result, true);
Luke081515's avatar
Luke081515 committed
949
		$a = 0;
Luke081515's avatar
Luke081515 committed
950
		if (isset($result["query"]["categorymembers"][$a]["title"])) {
951
			$Sub = true;
Luke081515's avatar
Luke081515 committed
952 953
			while (isset($result["query"]["categorymembers"][$a]["title"])) {
				$subCat[$b] = $result["query"]["categorymembers"][$a]["title"];
954 955 956 957
				$b++;
				$a++;
			}
		}
Luke081515's avatar
Luke081515 committed
958 959
		$b = 0;
		$c = 0;
Luke081515's avatar
Luke081515 committed
960
		if ($onlySubCats === true) {
961
			return $subCat;
Luke081515's avatar
Luke081515 committed
962
		}
Luke081515's avatar
Luke081515 committed
963
		if ($excludeWls === false) {
964
			while (isset($subCat[$b]))
965
			{
Luke081515's avatar
Luke081515 committed
966 967
				$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
968
				$result = json_decode($result, true);
969
				$Cont = false;
Luke081515's avatar
Luke081515 committed
970 971
				if (isset($result["query-continue"]["categorymembers"]["cmcontinue"])) {
					$Continue = $result["query-continue"]["categorymembers"]["cmcontinue"];
972 973 974
					$Cont = true;
				}
				while ($Cont === true) {
Luke081515's avatar
Luke081515 committed
975
					$a = 0;
Luke081515's avatar
Luke081515 committed
976 977 978
					if (isset($result["query"]["categorymembers"][$a]["title"])) {
						while (isset($result["query"]["categorymembers"][$a]["title"])) {
							$page[$c] = $result["query"]["categorymembers"][$a]["title"];
979 980 981
							$c++;
							$a++;
						}
982
					}
Luke081515's avatar
Luke081515 committed
983 984 985
					$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
986 987 988
					$result = json_decode($result, true);
					if (isset($result["query-continue"]["categorymembers"]["cmcontinue"])) {
						$Continue = $result["query-continue"]["categorymembers"]["cmcontinue"];
989
						$Cont = true;
Luke081515's avatar
Luke081515 committed
990
					} else {
991
						$Cont = false;
Luke081515's avatar
Luke081515 committed
992
					}
993
				}
Luke081515's avatar
Luke081515 committed
994
				$a = 0;
Luke081515's avatar
Luke081515 committed
995 996 997
				if (isset($result["query"]["categorymembers"][$a]["title"]) === true) {
					while (isset($result["query"]["categorymembers"][$a]["title"])) {
						$page[$c] = $result["query"]["categorymembers"][$a]["title"];
998 999 1000
						$c++;
						$a++;
					}
1001
				}
1002 1003 1004
				$b++;
			}
		} else {
1005
			while (isset($subCat[$b])) {
Luke081515's avatar
Luke081515 committed
1006 1007
				$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
1008
				$result = json_decode($result, true);
1009
				$Cont = false;
Luke081515's avatar
Luke081515 committed
1010 1011
				if (isset($result["query-continue"]["categorymembers"]["gcmcontinue"])) {
					$Continue = $result["query-continue"]["categorymembers"]["gcmcontinue"];
1012 1013
					$Cont = true;
				}
1014
				while ($Cont === true) {
Luke081515's avatar
Luke081515 committed
1015
					$a = 0;
Luke081515's avatar
Luke081515 committed
1016 1017 1018
					if (isset($result["query"]["pages"][$a]["title"])) {
						while (isset($result["query"]["pages"][$a]["title"])) {
							$page[$c] = $result["query"]["pages"][$a]["title"];
1019 1020 1021
							$c++;
							$a++;
						}
1022
					}
1023
					try {
Luke081515's avatar
Luke081515 committed
1024 1025 1026
						$result = $this->httpRequest("action=query&format=json&generator=categorymembers&gcmtitle=" . urlencode($subCat[$b]) .
							"&gmcontinue=" . $Continue .
							"&prop=info&gcmlimit=max&rawcontinue=&redirects", $this->job, "GET");
1027 1028 1029
					} catch (Exception $e) {
						throw $e;
					}
Luke081515's avatar
Luke081515 committed
1030 1031 1032
					$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
1033 1034 1035
					$result = json_decode($result, true);
					if (isset($result["query-continue"]["pages"]["gcmcontinue"])) {
						$Continue = $result["query-continue"]["pages"]["gcmcontinue"];
1036
						$Cont = true;
Luke081515's avatar
Luke081515 committed
1037
					} else {
1038
						$Cont = false;
Luke081515's avatar
Luke081515 committed
1039
					}
1040
				}
Luke081515's avatar
Luke081515 committed
1041
				$a = 0;
Luke081515's avatar
Luke081515 committed
1042 1043 1044
				if (isset($result["query"]["pages"][$a]["title"])) {
					while (isset($result["query"]["pages"][$a]["title"])) {
						$page[$c] = $result["query"]["pages"][$a]["title"];
1045 1046 1047
						$c++;
						$a++;
					}
1048
				}
1049 1050
				$b++;
			}
1051
		}
Luke081515's avatar
Luke081515 committed
1052
		if (!isset($page[0])) {
1053
				return false;
Luke081515's avatar
Luke081515 committed
1054
		} else {
1055
			return serialize($page);
Luke081515's avatar
Luke081515 committed
1056
		}
1057 1058
	}
	/** getPageCats
Luke081515's avatar
Luke081515 committed
1059