def _login(self, login): """Safely login through the API. Normally, this is called by __init__() if a username and password have been provided and no valid login cookies were found. The only other time it needs to be called is when those cookies expire, which is done automatically by api_query() if a query fails. *login* is a (username, password) tuple. Raises LoginError on login errors (duh), like bad passwords and nonexistent usernames. """ self._tokens.clear() name, password = login params = {"action": "query", "meta": "tokens", "type": "login"} with self._api_lock: result = self._api_query(params, no_assert=True) try: token = result["query"]["tokens"]["logintoken"] except KeyError: raise exceptions.LoginError("Couldn't get login token") params = { "action": "login", "lgname": name, "lgpassword": password, "lgtoken": token } with self._api_lock: result = self._api_query(params, no_assert=True) res = result["login"]["result"] if res == "Success": self._tokens.clear() self._save_cookiejar() return if res == "Illegal": e = "The provided username is illegal." elif res == "NotExists": e = "The provided username does not exist." elif res == "EmptyPass": e = "No password was given." elif res == "WrongPass" or res == "WrongPluginPass": e = "The given password is incorrect." else: e = "Couldn't login; server says '{0}'.".format(res) raise exceptions.LoginError(e)
def _handle_edit_errors(self, error, params, tries): """If our edit fails due to some error, try to handle it. We'll either raise an appropriate exception (for example, if the page is protected), or we'll try to fix it (for example, if we can't edit due to being logged out, we'll try to log in). """ if error.code in [ "noedit", "cantcreate", "protectedtitle", "noimageredirect" ]: raise exceptions.PermissionsError(error.info) elif error.code in [ "noedit-anon", "cantcreate-anon", "noimageredirect-anon" ]: if not all(self.site._login_info): # Insufficient login info: raise exceptions.PermissionsError(error.info) if tries == 0: # We have login info; try to login: self.site._login(self.site._login_info) self._token = None # Need a new token; old one is invalid now return self._edit(params=params, tries=1) else: # We already tried to log in and failed! e = "Although we should be logged in, we are not. This may be a cookie problem or an odd bug." raise exceptions.LoginError(e) elif error.code in ["editconflict", "pagedeleted", "articleexists"]: # These attributes are now invalidated: self._content = None self._basetimestamp = None self._exists = self.PAGE_UNKNOWN raise exceptions.EditConflictError(error.info) elif error.code in ["emptypage", "emptynewsection"]: raise exceptions.NoContentError(error.info) elif error.code == "contenttoobig": raise exceptions.ContentTooBigError(error.info) elif error.code == "spamdetected": raise exceptions.SpamDetectedError(error.info) elif error.code == "filtered": raise exceptions.FilteredError(error.info) raise exceptions.EditError(": ".join((error.code, error.info)))
def _login(self, login, token=None, attempt=0): """Safely login through the API. Normally, this is called by __init__() if a username and password have been provided and no valid login cookies were found. The only other time it needs to be called is when those cookies expire, which is done automatically by api_query() if a query fails. Recent versions of MediaWiki's API have fixed a CSRF vulnerability, requiring login to be done in two separate requests. If the response from from our initial request is "NeedToken", we'll do another one with the token. If login is successful, we'll try to save our cookiejar. Raises LoginError on login errors (duh), like bad passwords and nonexistent usernames. *login* is a (username, password) tuple. *token* is the token returned from our first request, and *attempt* is to prevent getting stuck in a loop if MediaWiki isn't acting right. """ self._tokens.clear() name, password = login params = {"action": "login", "lgname": name, "lgpassword": password} if token: params["lgtoken"] = token with self._api_lock: result = self._api_query(params, no_assert=True) res = result["login"]["result"] if res == "Success": self._save_cookiejar() elif res == "NeedToken" and attempt == 0: token = result["login"]["token"] return self._login(login, token, attempt=1) else: if res == "Illegal": e = "The provided username is illegal." elif res == "NotExists": e = "The provided username does not exist." elif res == "EmptyPass": e = "No password was given." elif res == "WrongPass" or res == "WrongPluginPass": e = "The given password is incorrect." else: e = "Couldn't login; server says '{0}'.".format(res) raise exceptions.LoginError(e)
def _handle_assert_edit(self, assertion, params, tries): """If we can't edit due to a failed AssertEdit assertion, handle that. If the assertion was 'user' and we have valid login information, try to log in. Otherwise, raise PermissionsError with details. """ if assertion == "user": if not all(self.site._login_info): # Insufficient login info: e = "AssertEdit: user assertion failed, and no login info was provided." raise exceptions.PermissionsError(e) if tries == 0: # We have login info; try to login: self.site._login(self.site._login_info) self._token = None # Need a new token; old one is invalid now return self._edit(params=params, tries=1) else: # We already tried to log in and failed! e = "Although we should be logged in, we are not. This may be a cookie problem or an odd bug." raise exceptions.LoginError(e) elif assertion == "bot": if not all(self.site._login_info): # Insufficient login info: e = "AssertEdit: bot assertion failed, and no login info was provided." raise exceptions.PermissionsError(e) if tries == 0: # Try to log in if we got logged out: self.site._login(self.site._login_info) self._token = None # Need a new token; old one is invalid now return self._edit(params=params, tries=1) else: # We already tried to log in, so we don't have a bot flag: e = "AssertEdit: bot assertion failed: we don't have a bot flag!" raise exceptions.PermissionsError(e) # Unknown assertion, maybe "true", "false", or "exists": e = "AssertEdit: assertion '{0}' failed.".format(assertion) raise exceptions.PermissionsError(e)