Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mrDoctorWho/vk4xmpp.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormrDoctorWho <mrdoctorwho@gmail.com>2015-03-13 23:58:20 +0300
committermrDoctorWho <mrdoctorwho@gmail.com>2015-03-13 23:58:20 +0300
commit5e9e194ab1dfaf13503d9ebda8eceb2fca202884 (patch)
tree21c29c7f5d7f509600f4508a87c3008c6b737a01
parenta80ae5448da369022481fe15e65b7fcd7ad64979 (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.txt14
-rw-r--r--extensions/groupchats.py6
-rw-r--r--extensions/status-to-vk.py9
-rw-r--r--gateway.py291
-rw-r--r--library/vkapi.py18
-rw-r--r--modules/mod_iq_captcha.py10
-rw-r--r--modules/mod_iq_disco.py9
-rw-r--r--modules/mod_iq_gateway.py7
-rw-r--r--modules/mod_iq_last.py11
-rw-r--r--modules/mod_iq_main.py5
-rw-r--r--modules/mod_iq_register.py11
-rw-r--r--modules/mod_iq_stats.py8
-rw-r--r--modules/mod_iq_vcard.py89
-rw-r--r--modules/mod_iq_version.py8
-rw-r--r--modules/mod_msg_main.py11
-rw-r--r--modules/mod_msg_xhtml.py8
-rw-r--r--modules/mod_prs_main.py8
-rw-r--r--modules/mod_xhtml.py13
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
diff --git a/gateway.py b/gateway.py
index 78977ed..e28a9b0 100644
--- a/gateway.py
+++ b/gateway.py
@@ -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.")