diff options
author | mrDoctorWho <mrdoctorwho@gmail.com> | 2015-03-13 23:58:20 +0300 |
---|---|---|
committer | mrDoctorWho <mrdoctorwho@gmail.com> | 2015-03-13 23:58:20 +0300 |
commit | 5e9e194ab1dfaf13503d9ebda8eceb2fca202884 (patch) | |
tree | 21c29c7f5d7f509600f4508a87c3008c6b737a01 | |
parent | a80ae5448da369022481fe15e65b7fcd7ad64979 (diff) |
Change simpleapps.ru to jabberon.ru
Fixed TypeError in groupchats.py in users list update
Fixed retrying of groupchats creation in groupcahts.py (now it shouldn't start dozens of threads)
Same as in groupchats as described in the line above fixed the same error in statustovk.py extension
Fixed fake user deletion after captcha challenge was completed by a user
Fixed transport freeze while working in longpoll
Fixed transport freeze while sending a message or handling any method in the main thread
Added feature to unload any module
Vcard requests will be highly limited for the transport stability and such
Most of the handlers were switched to threaded mode
-rw-r--r-- | Config_example.txt | 14 | ||||
-rw-r--r-- | extensions/groupchats.py | 6 | ||||
-rw-r--r-- | extensions/status-to-vk.py | 9 | ||||
-rw-r--r-- | gateway.py | 291 | ||||
-rw-r--r-- | library/vkapi.py | 18 | ||||
-rw-r--r-- | modules/mod_iq_captcha.py | 10 | ||||
-rw-r--r-- | modules/mod_iq_disco.py | 9 | ||||
-rw-r--r-- | modules/mod_iq_gateway.py | 7 | ||||
-rw-r--r-- | modules/mod_iq_last.py | 11 | ||||
-rw-r--r-- | modules/mod_iq_main.py | 5 | ||||
-rw-r--r-- | modules/mod_iq_register.py | 11 | ||||
-rw-r--r-- | modules/mod_iq_stats.py | 8 | ||||
-rw-r--r-- | modules/mod_iq_vcard.py | 89 | ||||
-rw-r--r-- | modules/mod_iq_version.py | 8 | ||||
-rw-r--r-- | modules/mod_msg_main.py | 11 | ||||
-rw-r--r-- | modules/mod_msg_xhtml.py | 8 | ||||
-rw-r--r-- | modules/mod_prs_main.py | 8 | ||||
-rw-r--r-- | modules/mod_xhtml.py | 13 |
18 files changed, 318 insertions, 218 deletions
diff --git a/Config_example.txt b/Config_example.txt index eef9105..856336d 100644 --- a/Config_example.txt +++ b/Config_example.txt @@ -51,10 +51,10 @@ USER_LIMIT = 0 # URL which will be used in registration form. See the example in js/vk4xmpp.html or in http://simpleapps.ru/vk4xmpp.html (if the site is still alive in your time) # URL MUST have "%d" somewhere, it's used for access definition. Since 01.08.2014 any plugin can modify the access, so it will be defined in the url. -URL_ACCEPT_APP = "http://simpleapps.ru/vk4xmpp.html#%d" +URL_ACCEPT_APP = "http://jabberon.ru/vk4xmpp.html#%d" #! Danger zone. -#! Change settings below ONLY IF YOU KNOW WHAT ARE YOU DOING! DEFAULT VALUES ARE RECOMMENDED! +#! Change the settings below ONLY IF YOU KNOW WHAT ARE YOU DOING! DEFAULT VALUES ARE RECOMMENDED! ## Thread stack size (WARNING: THIS MAY CAUSE TRANSPORT CRASH WITH SEGMENTATION FAULT ERROR). ## You may need to tune it to optimize memory consuming. ## Minimum value is 32768 bytes (32kb). @@ -73,7 +73,7 @@ ADMIN_JIDS = [] DEBUG_XMPPPY = False ## Database file (anything you like). -DatabaseFile = "users.db" +DatabaseFile = "vk4xmpp.db" ## File to store PID in. pidFile = "pidFile.txt" @@ -94,12 +94,12 @@ settingsDir = "settings" STANZA_SEND_INTERVAL = 0.03125 ## Enable photo hashes? This setting is probably can cause weird errors such as captcha, random disconnects (IOError: Disconnected) and others. -## But users like to see avatars in their roster. If it will cause disconnects, but you still want to use it, then just increase the STANZA_SEND_INTERVAL value -## But be careful, this value is global so transport will became slower. +## But users like to see avatars in their roster. If it will cause disconnects, but if you still want to use it, then just increase the STANZA_SEND_INTERVAL value +## But be careful, this value is global so transport will become slower. ENABLE_PHOTO_HASHES = False ## Debug longpoll queries. DEBUG_POLL = False -## Debug api requests. It should contain a list of methods which you would like to debug. -DEBUG_API = []
\ No newline at end of file +## Debug api requests. It should contain a list of methods which you would like to debug or "all" if you like to debug all methods. +DEBUG_API = [] diff --git a/extensions/groupchats.py b/extensions/groupchats.py index eedd5ec..78f8c71 100644 --- a/extensions/groupchats.py +++ b/extensions/groupchats.py @@ -72,6 +72,7 @@ def outgoingChatMessageHandler(self, vkChat): """ Handles outging messages (VK) and sends them to XMPP """ + if vkChat.has_key("chat_id"): if not self.settings.groupchats: return None @@ -177,7 +178,7 @@ class Chat(object): Uses two users list to prevent losing anyone """ all_users = vkChat["chat_active"].split(",") or [] - all_users = [int(user) for user in all_users] + all_users = [int(user) for user in all_users if user] if userObject.settings.show_all_chat_users: users = self.getVKChat(userObject, self.id) if users: @@ -255,7 +256,8 @@ class Chat(object): if userObject: source = userObject.source logger.debug("groupchats: chat %s wasn't created well, so trying to create it again (jid: %s)" % (self.jid, source)) - runThread(self.handleMessage, (user, vkChat, (retry - 1)), delay=(10 - retry)) + if retry: + runThread(self.handleMessage, (user, vkChat, (retry - 1)), delay=(10 - retry)) @api.attemptTo(3, dict, RuntimeError) def getVKChat(cls, user, id): diff --git a/extensions/status-to-vk.py b/extensions/status-to-vk.py index 03b62bf..96ab42d 100644 --- a/extensions/status-to-vk.py +++ b/extensions/status-to-vk.py @@ -1,16 +1,16 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2014 (30.08.14 08:08AM GMT) +# © simpleApps, 2014 (30.08.14 08:08AM GMT) — 2015. """ -This plugin allows user to publish their status in VK_ACCESS +This plugin allows users to publish their status in VK_ACCESS """ VK_ACCESS += 1024 GLOBAL_USER_SETTINGS["status_to_vk"] = {"label": "Publish my status in VK", "value": 0} -def statustovk_prs01(source, prs, retry=5): +def statustovk_prs01(source, prs, retry=3): if source in Transport and prs.getType() in ("available", None): if prs.getTo() == TransportID: user = Transport[source] @@ -30,6 +30,7 @@ def statustovk_prs01(source, prs, retry=5): user.last_status = status else: logger.debug("we didn't receive application permissions, so starting a timer (jid: %s)" % source) - runThread(statustovk_prs01, (source, prs, (retry -1)), delay=10) + if retry: + runThread(statustovk_prs01, (source, prs, (retry -1)), delay=10) registerHandler("prs01", statustovk_prs01)
\ No newline at end of file @@ -1,9 +1,9 @@ #!/usr/bin/env python2 # coding: utf-8 -# vk4xmpp gateway, v2.59 +# vk4xmpp gateway, v2.61 # © simpleApps, 2013 — 2015. -# Program published under MIT license. +# Program published under the MIT license. import gc import httplib @@ -40,12 +40,12 @@ from itypes import Database from stext import * from stext import _ from webtools import * -from writer import * Transport = {} +jidToID = {} + WatcherList = [] WhiteList = [] -jidToID = {} ADMIN_JIDS = [] TransportFeatures = [xmpp.NS_DISCO_ITEMS, @@ -98,14 +98,24 @@ args = argParser.parse_args() Daemon = args.daemon Config = args.config +# logger +logger = logging.getLogger("vk4xmpp") +logger.setLevel(LOG_LEVEL) +loggerHandler = logging.FileHandler(logFile) +formatter = logging.Formatter("%(asctime)s:%(levelname)s:%(name)s %(message)s", "[%d.%m.%Y %H:%M:%S]") +loggerHandler.setFormatter(formatter) +logger.addHandler(loggerHandler) + +# now writer can be imported +from writer import * + # config variables PhotoSize = "photo_100" DefLang = "ru" evalJID = "" AdditionalAbout = "" ConferenceServer = "" -URL_ACCEPT_APP = "http://simpleapps.ru/vk4xmpp.html#%d" - +URL_ACCEPT_APP = "http://jabberon.ru/vk4xmpp.html#%d" allowBePublic = True startTime = int(time.time()) @@ -128,17 +138,9 @@ try: except ImportError: import json -logger = logging.getLogger("vk4xmpp") -logger.setLevel(LOG_LEVEL) -loggerHandler = logging.FileHandler(logFile) -formatter = logging.Formatter("%(asctime)s:%(levelname)s:%(name)s %(message)s", "[%d.%m.%Y %H:%M:%S]") -loggerHandler.setFormatter(formatter) -logger.addHandler(loggerHandler) - -# Setting variables: DefLang for language id, root for translations directory +# Setting variables: DefLang for language id, root for the translations directory setVars(DefLang, root) - # Settings GLOBAL_USER_SETTINGS = {"keep_online": {"label": "Keep my status online", "value": 1}, "i_am_ghost": {"label": "I am a ghost", "value": 0}} @@ -187,9 +189,7 @@ def execute(handler, list=()): """ try: result = handler(*list) - except SystemExit: - result = 1 - except xmpp.NodeProcessed: + except (SystemExit, xmpp.NodeProcessed): result = 1 except Exception: result = -1 @@ -236,33 +236,30 @@ def runThread(func, args=(), name=None, att=3, delay=0): thr = threading.Timer(delay, execute, (func, args)) else: thr = threading.Thread(target=execute, args=(func, args)) - thr.name = name or func.func_name + name = name or func.__name__ + name = str(name) + "-" + str(time.time()) + thr.name = name try: thr.start() - except threading.ThreadError: + except (threading.ThreadError): if att: return runThread(func, args, name, (att - 1), delay) crashLog("runThread.%s" % name) -def runDatabaseQuery(query, args): - """ - Querying database in separate thread. - The function's purpose is to minimize a risk of main thread lock - """ - # TODO: We can easy return value by a callback, but is it really required? - def linear(query, args): - with Database(DatabaseFile, Semaphore) as db: - db(query, args) +def runDatabaseQuery(query, args, set=False): + with Database(DatabaseFile, Semaphore) as db: + db(query, args) + if set: db.commit() - runThread(linear, (query, args)) + return db def getGatewayRev(): """ Gets gateway revision using git or custom revision number """ - revNumber, rev = 259, 0 + revNumber, rev = 261, 0 shell = os.popen("git describe --always && git log --pretty=format:''").readlines() if shell: revNumber, rev = len(shell), shell[0] @@ -312,7 +309,7 @@ class Settings(object): if key in self.settings: self.settings[key]["value"] = values["value"] else: - self.settings[key] = values + self.settings[key] = values ## is it correct? self.keys = self.settings.keys self.items = self.settings.items @@ -456,11 +453,7 @@ class VK(object): """ if not self.pollInitialzed: raise api.LongPollError("The Poll wasn't initialized yet") - opener = self.engine.RIP.getOpener(self.pollServer, self.pollConfig) - if opener: - return opener - else: - raise api.LongPollError("Uknown Poll error") + return self.engine.RIP.getOpener(self.pollServer, self.pollConfig) def method(self, method, args=None, nodecode=False, force=False): """ @@ -484,7 +477,6 @@ class VK(object): # To prevent returning True from checkData() if force: raise - pass except api.NetworkNotFound as e: self.online = False @@ -511,7 +503,7 @@ class VK(object): self.online = False else: return result - logger.error("VK: error %s occurred while executing method(%s) (%s) (jid: %s)" % (method, e.__class__.__name__, e.message, self.source)) + logger.error("VK: error %s occurred while executing method(%s) (%s) (jid: %s)" % (e.__class__.__name__, method, e.message, self.source)) return False def captchaChallenge(self): @@ -521,16 +513,13 @@ class VK(object): """ if self.engine.captcha: executeHandlers("evt04", (self,)) - if self.source in Transport: - Poll.remove(Transport[self.source]) ## Do not foget to add user into poll again after the captcha challenge is done + self.online = False def disconnect(self): """ Stops all user handlers and removes himself from Poll """ logger.debug("VK: user %s has left" % self.source) - if self.source in Transport: - Poll.remove(Transport[self.source]) self.online = False runThread(executeHandlers, ("evt06", (self,))) runThread(self.method, ("account.setOffline", None, True, True)) @@ -600,42 +589,47 @@ class VK(object): """ Sends message to VK id Parameters: - body: obviously message's body + body: obviously the message body id: user id mType: message type (user_id is for dialogs, chat_id is for chats) more: for advanced features such as photos (attachments) """ + Stats["msgout"] += 1 + values = {mType: id, "message": body, "type": 0} + values.update(more) + result = None try: - Stats["msgout"] += 1 - values = {mType: id, "message": body, "type": 0} - values.update(more) - Message = self.method("messages.send", values) - except Exception: + result = self.method("messages.send", values) + except api.VkApiError: crashLog("messages.send") - Message = None - return Message + return result class User(object): """ - Main class contain the functions to connect xmpp & VK + Main class contains the functions to connect xmpp & VK """ def __init__(self, source=""): - self.password = None - self.username = None - self.source = source + self.auth = None - self.token = None self.exists = None - self.rosterSet = None - self.lastMsgID = 0 - self.typing = {} + self.friends = {} self.hashes = {} + + self.lastMsgID = 0 + self.password = None + self.rosterSet = None + + self.source = source + self.token = None + self.typing = {} + self.username = None + self.resources = set([]) self.settings = Settings(source) self.last_udate = time.time() - self.__sync = threading._allocate_lock() + self.sync = threading._allocate_lock() logger.debug("initializing User (jid: %s)" % self.source) def __eq__(self, user): @@ -676,7 +670,9 @@ class User(object): self.vk.captchaChallenge() return True except (api.TokenError, api.AuthError): - raise + if raise_exc: + raise + return False else: logger.debug("User: auth=%s (jid: %s)" % (self.auth, self.source)) @@ -684,10 +680,10 @@ class User(object): logger.debug("User: updating database because auth done (jid: %s)" % self.source) # User isn't exists so we gonna make a new record in the db if not self.exists: - runDatabaseQuery("insert into users values (?,?,?,?,?)", (self.source, "", self.vk.getToken(), self.lastMsgID, self.rosterSet)) + runDatabaseQuery("insert into users values (?,?,?,?,?)", (self.source, "", self.vk.getToken(), self.lastMsgID, self.rosterSet), True) elif self.password: - runDatabaseQuery("update users set token=? where jid=?", (self.vk.getToken(), self.source)) + runDatabaseQuery("update users set token=? where jid=?", (self.vk.getToken(), self.source), True) executeHandlers("evt07", (self,)) self.vk.online = True self.friends = self.vk.getFriends() @@ -731,8 +727,8 @@ class User(object): for uid, value in self.friends.iteritems(): if value["online"]: sendPresence(self.source, vk2xmpp(uid), None, value["name"], caps=True) - ## TODO: Add an option to display geeky stats in the status? - sendPresence(self.source, TransportID, None, IDENTIFIER["name"], caps=True) + if not self.vk.engine.captcha: + sendPresence(self.source, TransportID, None, IDENTIFIER["name"], caps=True) def sendOutPresence(self, destination, reason=None, all=False): """ @@ -762,7 +758,7 @@ class User(object): sendPresence(self.source, TransportID, "subscribe", IDENTIFIER["name"]) if dist: self.rosterSet = True - runDatabaseQuery("update users set rosterSet=? where jid=?", (self.rosterSet, self.source)) + runDatabaseQuery("update users set rosterSet=? where jid=?", (self.rosterSet, self.source), True) def sendMessages(self, init=False): """ @@ -773,38 +769,39 @@ class User(object): If plugin returs None then message will not be sent by transport's core, it shall be sent by plugin itself Otherwise, if plugin returns string, the message will be sent by transport's core """ - with self.__sync: + with self.sync: date = 0 messages = self.vk.getMessages(20, self.lastMsgID) if not messages or not messages[0]: return None messages = sorted(messages[1:], sortMsg) for message in messages: - if message["out"]: - continue - Stats["msgin"] += 1 - fromjid = vk2xmpp(message["uid"]) - body = uHTML(message["body"]) - iter = Handlers["msg01"].__iter__() - for func in iter: - try: - result = func(self, message) - except Exception: - result = "" - crashLog("handle.%s" % func.__name__) - if result is None: - for func in iter: - utils.apply(func, (self, message)) - break + # If message wasn't sent by our user + if not message["out"]: + Stats["msgin"] += 1 + fromjid = vk2xmpp(message["uid"]) + body = uHTML(message["body"]) + iter = Handlers["msg01"].__iter__() + for func in iter: + try: + result = func(self, message) + except Exception: + result = "" + crashLog("handle.%s" % func.__name__) + + if result is None: + for func in iter: + utils.apply(func, (self, message)) + break + else: + body += result else: - body += result - else: - if init: - date = message["date"] - sendMessage(Component, self.source, fromjid, escape("", body), date) + if init: + date = message["date"] + sendMessage(Component, self.source, fromjid, escape("", body), date) if messages: self.lastMsgID = messages[-1]["mid"] - runDatabaseQuery("update users set lastMsgID=? where jid=?", (self.lastMsgID, self.source)) + runDatabaseQuery("update users set lastMsgID=? where jid=?", (self.lastMsgID, self.source), True) if not self.vk.userID: self.vk.getUserID() @@ -815,26 +812,24 @@ class User(object): Retur codes: 0: need to reinit poll (add user to the poll buffer) 1: all is fine (request again) - -1: just continue iteration, ignoring this user + -1: just continue iteration, ignoring this user (user won't be added for the next iteration) """ if DEBUG_POLL: logger.debug("longpoll: processing result (jid: %s)" % self.source) - try: - with opener as sock: - data = sock.read() - except (httplib.BadStatusLine, socket.error): - return 1 if self.vk.engine.captcha: return -1 + try: + data = opener.read() + except (httplib.BadStatusLine, socket.error): + return 1 + if not data: logger.error("longpoll: no data. Will request again") return 1 - try: - data = json.loads(data) - except Exception: - return 1 + + data = json.loads(data) if "failed" in data: logger.debug("longpoll: failed. Searching for new server (jid: %s)" % self.source) @@ -905,6 +900,7 @@ class User(object): """ logger.debug("calling reauth for user %s" % self.source) if not self.vk.online: + self.token = None # we reset the token to prevent user deletion from the database self.connect() self.initialize(True) @@ -917,6 +913,7 @@ class Poll: __buff = set() __lock = threading._allocate_lock() + @classmethod def __add(cls, user): """ @@ -925,10 +922,11 @@ class Poll: Adds user in self.__list if no errors """ try: + # Getting socket for polling it by select() opener = user.vk.makePoll() except Exception as e: if not isinstance(e, api.LongPollError): - crashLog("poll.add") ## WARNING: We don't need to write crashlog when the poll is faled because the session is expired + crashLog("poll.add") logger.error("longpoll: failed to make poll (jid: %s)" % user.source) cls.__addToBuff(user) return False @@ -939,7 +937,7 @@ class Poll: @classmethod def __addToBuff(cls, user): """ - Adds user to list of "bad" users + Adds user to the list of "bad" users The list is mostly contain users whose poll request was failed for some reasons """ cls.__buff.add(user) @@ -960,20 +958,6 @@ class Poll: else: cls.__add(some_user) - @classmethod - def remove(cls, some_user): - """ - Removes the User class object from poll - """ - with cls.__lock: - if some_user in cls.__buff: - return cls.__buff.remove(some_user) - for sock, (user, opener) in cls.__list.iteritems(): - if some_user == user: - del cls.__list[sock] - opener.close() - break - clear = staticmethod(__list.clear) @classmethod @@ -1024,34 +1008,49 @@ class Poll: for sock in error: with cls.__lock: + # We will just re-add the user to poll in case if anything wrong happen to socket try: cls.__add(cls.__list.pop(sock)[0]) except KeyError: continue for sock in ready: + # We need to handle all synchronously with cls.__lock: try: user, opener = cls.__list.pop(sock) except KeyError: continue - ## We need to check if the user haven't left yet + user = Transport.get(user.source) # WHAT? if not user: continue + ## We need to check if the user hasn't left yet if not user.vk.online: - logger.debug("longpoll: user became offline, so removing him from the poll list (jid: %s)" % user.source) - cls.remove(user) - # In case if VK won't let us to read the socket, we're dead - result = execute(user.processPollResult, (opener,)) - if result == -1: continue - elif result: - cls.__add(user) - else: - cls.__addToBuff(user) - if DEBUG_POLL: - logger.debug("longpoll: result=%d (jid: %s)" % (result, user.source)) + # In case if VK won't let us to read the socket, we're dead + runThread(cls.processResult, (user, opener), "poll.processResult-%s" % user.source) + + with cls.__lock: + for sock, (user, opener) in cls.__list.items(): + if not user.vk.online: + logger.debug("longpoll: user is not online, so removing their from poll (jid: %s)" % user.source) + try: + del cls.__list[sock] + except KeyError: + pass + + @classmethod + def processResult(self, user, opener): + result = execute(user.processPollResult, (opener,)) + ## debug + if DEBUG_POLL: + logger.debug("longpoll: result=%d (jid: %s)" % (result, user.source)) + if result == -1: + return None + if not result: + user.vk.pollInitialzed = False + self.add(user) def sendPresence(destination, source, pType=None, nick=None, reason=None, caps=None, show=None): @@ -1162,8 +1161,10 @@ def removeUser(user, roster=False, notify=True): if notify: sendMessage(Component, source, TransportID, _("The record in database about you was EXTERMINATED! If you weren't asked for it, then let us know."), -1) ## Will russians understand this joke? logger.debug("User: removing user from db (jid: %s)" % source) - runDatabaseQuery("delete from users where jid=?", (source,)) + runDatabaseQuery("delete from users where jid=?", (source,), True) logger.debug("User: deleted (jid: %s)" % source) + if source in Transport: + del Transport[source] if roster and user: friends = user.friends user.exists = False ## Make the Daleks happy @@ -1175,9 +1176,7 @@ def removeUser(user, roster=False, notify=True): sendPresence(source, jid, "unsubscribed") user.settings.exterminate() executeHandlers("evt03", (user,)) - Poll.remove(user) user.vk.online = False - del Transport[source] def getPid(): @@ -1223,19 +1222,35 @@ def getModulesList(): return modules -def loadModules(reload_=False): +def loadModules(reload_=False, modules=[]): """ Loading modules from list made by getModulesList() Parameter "reload_" is needed to reload the modules """ - modules = getModulesList() + modules = modules or getModulesList() + + def _register(module): + if hasattr(module, "load"): + module.load() + + def _load(name): + module = __import__(name, globals, locals()) + _register(module) + + def _reload(name): + if name in sys.modules: + module = sys.modules[name] + if hasattr(module, "unload"): + module.unload() + module = reload(module) + _register(module) + for name in modules: try: - module = __import__(name, globals(), locals()) if reload_: - reload(module) - if hasattr(module, "load"): - module.load() + _reload(name) + else: + _load(name) except Exception: crashLog("loadmodules") @@ -1338,6 +1353,7 @@ def makeMeKnown(): """ That's such a weird function just makes post request to the vk4xmpp monitor which is located on http://xmppserv.ru/xmpp-monitor + You can check out the source of The VK4XMPP Monitor utilty over there: https://github.com/aawray/xmpp-monitor """ if WhiteList: WhiteList.append("anon.xmppserv.ru") @@ -1379,4 +1395,5 @@ if __name__ == "__main__": crashLog("component.iter") disconnectHandler(True) -# This is the end!
\ No newline at end of file + +# This is the end! diff --git a/library/vkapi.py b/library/vkapi.py index e102bea..5d0004f 100644 --- a/library/vkapi.py +++ b/library/vkapi.py @@ -5,10 +5,11 @@ import cookielib import httplib import logging import mimetools +import re import socket import ssl import time -import re +import threading import urllib import urllib2 import webtools @@ -108,7 +109,6 @@ class RequestProcessor(object): self.cookieJar = cookielib.CookieJar() cookieProcessor = urllib2.HTTPCookieProcessor(self.cookieJar) self.open = urllib2.build_opener(cookieProcessor).open - self.open.__func__.___defaults__ = (None, SOCKET_TIMEOUT) def getCookie(self, name): """ @@ -204,7 +204,6 @@ class APIBinding: self.lastMethod = () self.RIP = RequestProcessor() - self.attempts = 0 self.debug = debug or [] def loginByPassword(self): @@ -307,7 +306,11 @@ class APIBinding: self.last.append(time.time()) if len(self.last) > 2: if (self.last.pop() - self.last.pop(0)) <= 1.25: - time.sleep(0.34) + time.sleep(0.37) + + if method in self.debug or self.debug == "all": + start = time.time() + print "issuing method %s with values %s in thread: %s" % (method, str(values), threading.currentThread().name) response = self.RIP.post(url, values) if response and not nodecode: @@ -318,9 +321,8 @@ class APIBinding: except ValueError: return {} # Debug: - if method in self.debug: - print "method %s with values %s" % (method, str(values)) - print "response for method %s: %s" % (method, str(body)) + if method in self.debug or self.debug == "all": + print "response for method %s: %s in thread: %s (%0.2fs)" % (method, str(body), threading.currentThread().name, (time.time() - start)) if "response" in body: return body["response"] or {} @@ -431,4 +433,4 @@ class AccessDenied(VkApiError): This one should be ignored as well. Happens for an unknown reason with any method """ - pass
\ No newline at end of file + pass diff --git a/modules/mod_iq_captcha.py b/modules/mod_iq_captcha.py index 27d83cb..a00dbad 100644 --- a/modules/mod_iq_captcha.py +++ b/modules/mod_iq_captcha.py @@ -9,7 +9,7 @@ Module purpose is to accept the captcha value from iq from __main__ import * import mod_msg_main as mod_msg -def captcha_handler(cl, iq): +def captcha_handler_threaded(cl, iq): if iq.getTagAttr("captcha", "xmlns") == xmpp.NS_CAPTCHA: source = iq.getFrom().getStripped() if source in Transport: @@ -21,6 +21,14 @@ def captcha_handler(cl, iq): value = ocrTag.getTagData("value") mod_msg.acceptCaptcha(cl, value, jidTo, source) + +def captcha_handler(cl, iq): + runThread(captcha_handler_threaded, (cl, iq)) + + def load(): Component.RegisterHandler("iq", captcha_handler, "set") + +def unload(): + Component.UnregisterHandler("iq", captcha_handler, "set")
\ No newline at end of file diff --git a/modules/mod_iq_disco.py b/modules/mod_iq_disco.py index 882a3ed..b59a0a8 100644 --- a/modules/mod_iq_disco.py +++ b/modules/mod_iq_disco.py @@ -106,7 +106,7 @@ def checkAPIToken(token): userID = vk.getUserID() name = vk.getUserData(userID) data = {"auth": auth, "name": name, "id": str(userID), "friends_count": len(vk.getFriends())} - except Exception: + except (api.VkApiError, Exception): data = wException() return data @@ -203,7 +203,6 @@ def commands_handler(cl, iq): if form.get("token"): token = form["token"] _result = checkAPIToken(token) - # {'friends_count': 5, 'name': {u'uid': 218855826, 'name': u'Some User', u'screen_name': u'some_user'}, 'auth': True, 'id': 218855826} if isinstance(_result, dict): _fields = dictToDataForm(_result) @@ -273,3 +272,9 @@ def load(): Component.RegisterHandler("iq", disco_handler, "get", xmpp.NS_DISCO_INFO) Component.RegisterHandler("iq", disco_handler, "get", xmpp.NS_DISCO_ITEMS) Component.RegisterHandler("iq", commands_handler, "set") + + +def unload(): + Component.UnregisterHandler("iq", disco_handler, "get", xmpp.NS_DISCO_INFO) + Component.UnregisterHandler("iq", disco_handler, "get", xmpp.NS_DISCO_ITEMS) + Component.UnregisterHandler("iq", commands_handler, "set")
\ No newline at end of file diff --git a/modules/mod_iq_gateway.py b/modules/mod_iq_gateway.py index aed9b3b..127c3e2 100644 --- a/modules/mod_iq_gateway.py +++ b/modules/mod_iq_gateway.py @@ -1,6 +1,6 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2014. +# © simpleApps, 2014 — 2015. from __main__ import * @@ -34,4 +34,7 @@ def gateway_handler(cl, iq): def load(): - Component.RegisterHandler("iq", gateway_handler, "", xmpp.NS_GATEWAY)
\ No newline at end of file + Component.RegisterHandler("iq", gateway_handler, "", xmpp.NS_GATEWAY) + +def unload(): + Component.UnregisterHandler("iq", gateway_handler, "", xmpp.NS_GATEWAY)
\ No newline at end of file diff --git a/modules/mod_iq_last.py b/modules/mod_iq_last.py index 0437d48..4e0ca11 100644 --- a/modules/mod_iq_last.py +++ b/modules/mod_iq_last.py @@ -1,11 +1,11 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2013 — 2014. +# © simpleApps, 2013 — 2015. from __main__ import * -def last_handler(cl, iq): +def last_handler_threaded(cl, iq): jidFrom = iq.getFrom() jidTo = iq.getTo() source = jidFrom.getStripped() @@ -26,6 +26,11 @@ def last_handler(cl, iq): result.setTagData("query", name) sender(cl, result) +def last_handler(cl, iq): + runThread(last_handler_threaded, (cl, iq)) def load(): - Component.RegisterHandler("iq", last_handler, "get", xmpp.NS_LAST)
\ No newline at end of file + Component.RegisterHandler("iq", last_handler, "get", xmpp.NS_LAST) + +def unload(): + Component.UnregisterHandler("iq", last_handler, "get", xmpp.NS_LAST)
\ No newline at end of file diff --git a/modules/mod_iq_main.py b/modules/mod_iq_main.py index 32a262d..61a8a4c 100644 --- a/modules/mod_iq_main.py +++ b/modules/mod_iq_main.py @@ -1,6 +1,6 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2013 — 2014. +# © simpleApps, 2013 — 2015. from __main__ import * @@ -21,3 +21,6 @@ def main_handler(cl, iq): def load(): Component.RegisterHandler("iq", main_handler, makefirst=True) + +def unload(): + Component.UnregisterHandler("iq", main_handler)
\ No newline at end of file diff --git a/modules/mod_iq_register.py b/modules/mod_iq_register.py index 097d13f..480f98d 100644 --- a/modules/mod_iq_register.py +++ b/modules/mod_iq_register.py @@ -1,6 +1,6 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2014. +# © simpleApps, 2014 — 2015. ## TODO: Handle set/get in separate functions. from __main__ import * @@ -62,12 +62,12 @@ def register_handler(cl, iq): result.setQueryPayload([form]) elif iType == "set" and queryChildren: - phone, password, use_password, token, result = False, False, False, False, False + phone, password, use_password, token, result = False, False, False, False, False # Why result is here? query = iq.getTag("query") data = query.getTag("x", namespace=xmpp.NS_DATA) if data: form = xmpp.DataForm(node=data).asDict() - phone = str(form.get("phone", "")) + phone = str(form.get("phone", "")).lstrip("+") password = str(form.get("password", "")) use_password = utils.normalizeValue(form.get("use_password", "")) ## In case here comes some unknown crap @@ -115,6 +115,7 @@ def register_handler(cl, iq): if source in Transport: user = Transport[source] removeUser(user, True, False) + result = iq.buildReply("result") # Is it required? result.setPayload([], add = 0) watcherMsg(_("User has removed registration: %s") % source) else: @@ -127,3 +128,7 @@ def register_handler(cl, iq): def load(): Component.RegisterHandler("iq", register_handler, "", xmpp.NS_REGISTER) + + +def unload(): + Component.UnregisterHandler("iq", register_handler, "", xmpp.NS_REGISTER)
\ No newline at end of file diff --git a/modules/mod_iq_stats.py b/modules/mod_iq_stats.py index 41a27b7..d415db3 100644 --- a/modules/mod_iq_stats.py +++ b/modules/mod_iq_stats.py @@ -1,6 +1,6 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2013 — 2014. +# © simpleApps, 2013 — 2015. from __main__ import * @@ -55,4 +55,8 @@ def stats_handler(cl, iq): def load(): - Component.RegisterHandler("iq", stats_handler, "get", xmpp.NS_STATS)
\ No newline at end of file + Component.RegisterHandler("iq", stats_handler, "get", xmpp.NS_STATS) + + +def unload(): + Component.UnregisterHandler("iq", stats_handler, "get", xmpp.NS_STATS)
\ No newline at end of file diff --git a/modules/mod_iq_vcard.py b/modules/mod_iq_vcard.py index d55b0ed..84d034b 100644 --- a/modules/mod_iq_vcard.py +++ b/modules/mod_iq_vcard.py @@ -1,10 +1,14 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2014. +# © simpleApps, 2014 — 2015. from __main__ import * from __main__ import _ +del Semaphore +Semaphore = threading.Semaphore() + + def buildVcard(tags): vCard = xmpp.Node("vCard", {"xmlns": xmpp.NS_VCARD}) for key in tags.keys(): @@ -15,40 +19,57 @@ def buildVcard(tags): vCard.setTagData(key, tags[key]) return vCard -def vcard_handler(cl, iq): - jidFrom = iq.getFrom() - jidTo = iq.getTo() - source = jidFrom.getStripped() - destination = jidTo.getStripped() - result = iq.buildReply("result") - _DESC = '\n'.join((DESC, "_" * 16, AdditionalAbout)) if AdditionalAbout else DESC - if destination == TransportID: - vcard = buildVcard({"NICKNAME": "VK4XMPP Transport", - "DESC": _DESC, - "PHOTO": "https://raw.github.com/mrDoctorWho/vk4xmpp/master/vk4xmpp.png", - "URL": "http://simpleapps.ru" - }) - result.setPayload([vcard]) - - elif source in Transport: - user = Transport[source] - if user.friends: - id = vk2xmpp(destination) - json = user.vk.getUserData(id, ["screen_name", PhotoSize]) - values = {"NICKNAME": json.get("name", str(json)), - "URL": "http://vk.com/id%s" % id, - "DESC": _("Contact uses VK4XMPP Transport\n%s") % _DESC - } - if id in user.friends.keys(): - values["PHOTO"] = json.get(PhotoSize) or URL_VCARD_NO_IMAGE - vCard = buildVcard(values) - result.setPayload([vCard]) + +def vcard_handler_threaded(cl, iq): + # Vcard feature makes transport hang (especially the photo part) + # Many clients love to query vcards so much, so the solution was in adding a semaphore and sleep() here + # This is probably not a good idea, but for now this is the best one + with Semaphore: + jidFrom = iq.getFrom() + jidTo = iq.getTo() + source = jidFrom.getStripped() + destination = jidTo.getStripped() + result = iq.buildReply("result") + _DESC = str.join(chr(10), (DESC, "_" * 16, AdditionalAbout)) if AdditionalAbout else DESC + if destination == TransportID: + vcard = buildVcard({"NICKNAME": "VK4XMPP Transport", + "DESC": _DESC, + "PHOTO": "https://raw.github.com/mrDoctorWho/vk4xmpp/master/vk4xmpp.png", + "URL": "http://simpleapps.ru" + }) + result.setPayload([vcard]) + + elif source in Transport: + user = Transport[source] + if user.friends: + id = vk2xmpp(destination) + args = ["screen_name"] + if user.friends.has_key(id): + args.append(PhotoSize) + json = user.vk.getUserData(id, args) + values = {"NICKNAME": json.get("name", str(json)), + "URL": "http://vk.com/id%s" % id, + "DESC": _("Contact uses VK4XMPP Transport\n%s") % _DESC + } + if id in user.friends.keys(): + values["PHOTO"] = json.get(PhotoSize) or URL_VCARD_NO_IMAGE + vCard = buildVcard(values) + result.setPayload([vCard]) + else: + result = utils.buildIQError(iq, xmpp.ERR_BAD_REQUEST, _("Your friend-list is empty.")) else: - result = utils.buildIQError(iq, xmpp.ERR_BAD_REQUEST, _("Your friend-list is empty.")) - else: - result = utils.buildIQError(iq, xmpp.ERR_REGISTRATION_REQUIRED, _("You're not registered for this action.")) - sender(cl, result) + result = utils.buildIQError(iq, xmpp.ERR_REGISTRATION_REQUIRED, _("You're not registered for this action.")) + sender(cl, result) + time.sleep(1.5) + + +def vcard_handler(cl, iq): + runThread(vcard_handler_threaded, (cl, iq)) def load(): - Component.RegisterHandler("iq", vcard_handler, "get", xmpp.NS_VCARD)
\ No newline at end of file + Component.RegisterHandler("iq", vcard_handler, "get", xmpp.NS_VCARD) + + +def unload(): + Component.UnregisterHandler("iq", vcard_handler, "get", xmpp.NS_VCARD)
\ No newline at end of file diff --git a/modules/mod_iq_version.py b/modules/mod_iq_version.py index 9d722c2..e657e92 100644 --- a/modules/mod_iq_version.py +++ b/modules/mod_iq_version.py @@ -1,6 +1,6 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2014. +# © simpleApps, 2014 — 2015. from __main__ import * @@ -16,4 +16,8 @@ def version_handler(cl, iq): def load(): - Component.RegisterHandler("iq", version_handler, "get", xmpp.NS_VERSION)
\ No newline at end of file + Component.RegisterHandler("iq", version_handler, "get", xmpp.NS_VERSION) + + +def unload(): + Component.UnregisterHandler("iq", version_handler, "get", xmpp.NS_VERSION)
\ No newline at end of file diff --git a/modules/mod_msg_main.py b/modules/mod_msg_main.py index 8b30b4e..bf038ac 100644 --- a/modules/mod_msg_main.py +++ b/modules/mod_msg_main.py @@ -9,6 +9,7 @@ Module purpose is to receive and handle messages from __main__ import * from __main__ import _ + def reportReceived(msg, jidFrom, jidTo): """ Reports if message is received @@ -54,7 +55,7 @@ def acceptCaptcha(cl, args, jidTo, source): sendMessage(cl, source, jidTo, answer) -def message_handler(cl, msg): +def message_handler_threaded(cl, msg): body = msg.getBody() jidTo = msg.getTo() destination = jidTo.getStripped() @@ -101,5 +102,11 @@ def message_handler(cl, msg): sender(cl, answer) executeHandlers("msg02", (msg,)) +def message_handler(cl, msg): + runThread(message_handler_threaded, (cl, msg)) + def load(): - Component.RegisterHandler("message", message_handler)
\ No newline at end of file + Component.RegisterHandler("message", message_handler) + +def unload(): + Component.UnregisterHandler("message", message_handler)
\ No newline at end of file diff --git a/modules/mod_msg_xhtml.py b/modules/mod_msg_xhtml.py index bf37ada..ac8e021 100644 --- a/modules/mod_msg_xhtml.py +++ b/modules/mod_msg_xhtml.py @@ -1,6 +1,6 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2013 — 2014. +# © simpleApps, 2013 — 2015. # This module depends on mod_xhtml. from __main__ import * @@ -22,4 +22,8 @@ def xhtml_handler(cl, msg): raise xmpp.NodeProcessed() def load(): - Component.RegisterHandler("message", xhtml_handler, "chat")
\ No newline at end of file + Component.RegisterHandler("message", xhtml_handler, "chat") + + +def unload(): + Component.UnregisterHandler("message", xhtml_handler, "chat")
\ No newline at end of file diff --git a/modules/mod_prs_main.py b/modules/mod_prs_main.py index 1d5aea9..e96f08f 100644 --- a/modules/mod_prs_main.py +++ b/modules/mod_prs_main.py @@ -1,6 +1,6 @@ # coding: utf-8 # This file is a part of VK4XMPP transport -# © simpleApps, 2013 — 2014. +# © simpleApps, 2013 — 2015. """ Module purpose is to receive and handle presences @@ -100,4 +100,8 @@ def presence_handler(cl, prs): def load(): - Component.RegisterHandler("presence", presence_handler)
\ No newline at end of file + Component.RegisterHandler("presence", presence_handler) + + +def unload(): + Component.UnregisterHandler("presence", presence_handler)
\ No newline at end of file diff --git a/modules/mod_xhtml.py b/modules/mod_xhtml.py index 2347284..6fc9196 100644 --- a/modules/mod_xhtml.py +++ b/modules/mod_xhtml.py @@ -31,10 +31,15 @@ def sendPhoto(user, data, type, address, mType): user.vk.engine.RIP.multipart("photo", str(name), str(type), data), urlencode = False)[0]) - id = user.vk.method("photos.saveMessagesPhoto", response)[0].get("id", 0) - user.vk.sendMessage("", address, mType, {"attachment": id}) - logger.debug("sendPhoto: image was successfully sent by user %s" % user.source) - answer = _("Your image was successfully sent.") + id = user.vk.method("photos.saveMessagesPhoto", response) + if id: + id = id[0].get("id", 0) + user.vk.sendMessage("", address, mType, {"attachment": id}) + logger.debug("sendPhoto: image was successfully sent by user %s" % user.source) + answer = _("Your image was successfully sent.") + else: + answer = _("Sorry but we have failed to send this image." + " Seems you haven't enough permissions. Your token should be updated, register again.") else: answer = _("Sorry but we have failed to send this image." " Seems you haven't enough permissions. Your token should be updated, register again.") |