User:QuietBot/src

From TheKolWiki
Jump to: navigation, search
<?
require("Snoopy.class.php");

class wikibot
{
	protected $snoopy;
	protected $root, $user, $pass;
	protected $title;
	protected $loggedin;
	protected $edittoken;

	protected $debug;

	public function __construct ($root, $user, $pass, $debug = FALSE)
	{
		$this->snoopy = new Snoopy;

		$this->root = $root;
		$this->user = $user;
		$this->pass = $pass;

		$this->debug = $debug;

		$this->title = '';
		$this->edittoken = '';

		$this->loggedin = FALSE;
	}

	public function __destruct ()
	{
		if ($this->loggedin)
			$this->logout();
	}

	protected function post ($action, $parms = array())
	{
		if ($this->snoopy->submit($this->root . "/api.php?format=php&action=". $action, $parms))
		{
			$result = unserialize($this->snoopy->results);
			return $this->checkError($result, $action);
		}
		else	return 'HTTP request failed';
	}
	protected function checkError ($data, $action)
	{
		if (isset($data[$action]))
			return $data[$action];
		if (!isset($data['error']))
			return 'unknown error';
		return $data['error']['code'] .' ('. $data['error']['info'] .')';
	}

	// Establish session for editing wiki pages
	public function login ()
	{
		if ($this->loggedin)
			return $this->error("ERROR: already logged in!", 1);

		$login = $this->post('login', array('lgname' => $this->user, 'lgpassword' => $this->pass));
		if (!is_array($login))
			return $this->error("ERROR: Login failed (1) - $login", 2);

		if ($login['result'] == 'NeedToken')
		{
			$login = $this->post('login', array('lgname' => $this->user,
				'lgpassword' => $this->pass, 'lgtoken' => $login['token']));
			if (!is_array($login))
				return $this->error("FATAL ERROR: Login failed (2) - $login", 3);
		}
		if ($login['result'] != 'Success')
			return $this->error("Login failed: ". $login['result'], 3);
		$this->loggedin = TRUE;
		return 0;
	}

	// Terminate active session
	public function logout ()
	{
		if (!$this->loggedin)
			return $this->error("ERROR: not logged in!", 1);
 
		$logout = $this->post('logout');

		$this->loggedin = FALSE;
		$this->notice("Successfully logged out.");
		return 0;
	}

	// Load page contents. Required for any sort of editing.
	public function loadPage ($title)
	{
		if (!$this->loggedin)
			return $this->error("ERROR: not logged in!", 1);

		$query = $this->post('query', array('prop' => 'info|revisions',
			'rvprop' => 'content', 'intoken' => 'edit', 'titles' => $title));
		if (!is_array($query))
			return $this->error("ERROR in loadPage - $query", 2);

		list($page_id) = array_keys($query['pages']);
		if (!$page_id)
			return $this->error("Failed to load page $this->title", 3);

		$data = $query['pages'][$page_id]['revisions'][0]['*'];
		$this->edittoken = $query['pages'][$page_id]['edittoken'];
		$this->title = $title;

		$this->notice("Successfully loaded page $this->title");
		return array('text' => $data);
	}

	// Create new page with specified contents and reason. Fails if page already exists
	public function createPage ($title, $data, $reason, $minor = false)
	{
		if (!$this->loggedin)
			return $this->error("ERROR: not logged in!", 1);

		$query = $this->post('query', array('prop' => 'info', 'intoken' => 'edit', 'titles' => $title));
		if (!is_array($query))
			return $this->error("ERROR in createPage (1) - $query", 2);
		if (!isset($query['pages'][-1]))
			return $this->error("ERROR: page $title already exists!", 3);

		$edittoken = $query['pages'][-1]['edittoken'];

		$edit = $this->post('edit', array('title' => $title, 'text' => $data, 'bot' => 1,
			'summary' => $reason, ($minor ? 'minor' : 'notminor') => 1, 'createonly' => 1, 'token' => $edittoken));
		if (!is_array($edit))
			return $this->error("FATAL ERROR in createPage (2)  - $edit", 4);

		if ($edit['result'] != 'Success')
			return $this->error("Failed to create page $title: ". $edit['result'], 5);
		$this->notice("Successfully created page $title");
		return 0;
	}

	// Saves new data into previously loaded page. Cannot be called twice in a row - must call loadPage() afterwards
	public function savePage ($data, $reason, $minor = false)
	{
		if (!$this->loggedin)
			return $this->error("ERROR: not logged in!", 1);

		if (!strlen($this->title) || !strlen($this->edittoken))
			return $this->error("ERROR: attempted to save page without first loading one", 2);

		$edit = $this->post('edit', array('title' => $this->title, 'text' => $data, 'bot' => 1,
			'summary' => $reason, ($minor ? 'minor' : 'notminor') => 1, 'token' => $this->edittoken));
		if (!is_array($edit))
			return $this->error("ERROR in savePage - $edit", 3);

		if ($edit['result'] != 'Success')
			return $this->error("Failed to save page $this->title: ". $edit['result'], 4);
		$this->notice("Successfully saved page $this->title");
		$this->title = '';
		$this->edittoken = '';
		return 0;
	}

	// Renames one page to another, suppressing creation of a redirect.
	// Generally requires sufficient privileges to perform.
	public function movePage ($old, $new, $reason)
	{
		if (!$this->loggedin)
			return $this->error("ERROR: not logged in!", 1);

		$query = $this->post('query', array('prop' => 'info', 'intoken' => 'move', 'titles' => $old));
		if (!is_array($query))
			return $this->error("ERROR: Move failed (1) - $query", 2);

		list($page_id) = array_keys($query['pages']);
		$movetoken = $query['pages'][$page_id]['movetoken'];
		
		$move = $this->post('move', array('from' => $old, 'to' => $new,
			'reason' => $reason, 'token' => $movetoken, 'noredirect' => 1));
		if (!is_array($move))
			return $this->error("ERROR: Move failed (2) - $move", 3);
		$this->notice("Successfully moved page $old to $new");
		return 0;
	}

	// Deletes a page.
	// Generally requires sufficient privileges to perform.
	public function deletePage ($title, $reason)
	{
		if (!$this->loggedin)
			return $this->error("ERROR: not logged in!", 1);

		$query = $this->post('query', array('prop' => 'info', 'intoken' => 'delete', 'titles' => $title));
		if (!is_array($query))
			return $this->error("ERROR: Delete failed (1) - $query", 2);

		list($page_id) = array_keys($query['pages']);
		$deletetoken = $query['pages'][$page_id]['deletetoken'];
		
		$delete = $this->post('delete', array('title' => $title, 'reason' => $reason, 'token' => $deletetoken));
		if (!is_array($delete))
			return $this->error("ERROR: Delete failed (2) - $delete", 3);
		$this->notice("Successfully deleted page $title");
		return 0;
	}

	// Clears the cached state of a page, forcing it to be regenerated.
	public function purgePage ($title)
	{
		if (!$this->loggedin)
			return $this->error("ERROR: not logged in!", 1);

		$purge = $this->post('purge', array('titles' => $title));
		if (!is_array($purge))
			return $this->error("ERROR: Purge failed - $purge", 2);
		if (($purge[0]['title'] == $title) && isset($purge[0]['purged']))
		{
			$this->notice("Successfully purged page $title");
			return 0;
		}
		return $this->error("Failed to purge page $title", 2);
	}

	// Checks if any edits have been made to the bot's User Talk page and aborts execution.
	// Must be called manually so the bot can be remotely shut down if it runs out of control
	public function checkAbort ($abort = true)
	{
		if (!$this->loggedin)
			return $this->error("ERROR: not logged in!", 1);

		$query = $this->post('query', array('meta' => 'userinfo', 'uiprop' => 'hasmsg'));
		if (!is_array($query))
			return $this->fatal("FATAL ERROR: Could not check for new messages - $query");
		if (!isset($query['userinfo']['messages']))
			return NULL;

		// Acknowledge it
		$this->snoopy->fetch($this->root . '/index.php/User_talk:'. $this->user);

		if ($abort)
			return $this->fatal("\n\n\n*** Received abort signal!");
		else	return $this->error("***Received abort signal!", 2);
	}

	protected function fatal ($text)
	{
		die($text."\n");
		// this will never be reached
		return -1;
	}

	protected function error ($text, $errno = 0)
	{
		if ($this->debug)
			echo $text."\n";
		return $errno;
	}

	protected function notice ($text)
	{
		if ($this->debug)
			echo $text."\n";
	}
}

function pagenum ($num, $max)
{
	return str_pad($num + 1, strlen($max), '0', STR_PAD_LEFT);
}
?>

Note that you must also patch Snoopy.class.php (version 1.2.4) as follows:

<?
--- Snoopy.class.php	2010-03-07 17:16:14.000000000 +0000
+++ Snoopy.class.php.new	2010-03-07 17:20:27.000000000 +0000
@@ -191,8 +191,10 @@
 				}
 				else
 				{
+					$this->_redirectdepth = 0;
 					return false;
 				}
+				$this->_redirectdepth = 0;
 				return true;					
 				break;
 			case "https":
@@ -248,14 +250,17 @@
 							break;
 					}
 				}					
+				$this->_redirectdepth = 0;
 				return true;					
 				break;
 			default:
 				// not a valid protocol
 				$this->error	=	'Invalid protocol "'.$URI_PARTS["scheme"].'"\n';
+				$this->_redirectdepth = 0;
 				return false;
 				break;
 		}		
+		$this->_redirectdepth = 0;
 		return true;
 	}
 
@@ -350,8 +355,10 @@
 				}
 				else
 				{
+					$this->_redirectdepth = 0;
 					return false;
 				}
+				$this->_redirectdepth = 0;
 				return true;					
 				break;
 			case "https":
@@ -413,15 +420,18 @@
 							break;
 					}
 				}					
+				$this->_redirectdepth = 0;
 				return true;					
 				break;
 				
 			default:
 				// not a valid protocol
 				$this->error	=	'Invalid protocol "'.$URI_PARTS["scheme"].'"\n';
+				$this->_redirectdepth = 0;
 				return false;
 				break;
 		}		
+		$this->_redirectdepth = 0;
 		return true;
 	}
 
?>

If this patch is not applied, this bug will cause the redirect depth to eventually reach its limit (5) and cause all subsequent savePage operations to appear to fail.