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>2014-01-15 18:44:28 +0400
committermrDoctorWho <mrdoctorwho@gmail.com>2014-01-15 18:44:28 +0400
commitfc84166a006edd62f5d0267bf5e4622816b9bfb5 (patch)
tree8e1140984d5623dce5fb2361c34b9849d6404650
parente69216f422ac4828d5193aebc5bbf1914cefe728 (diff)
Fixed #44, added option SLICE_STEP into config, added handling errno 101 (when vk.com is down), xmpppy fixes
-rw-r--r--Config_example.txt3
-rw-r--r--gateway.py44
-rw-r--r--handlers/Presence.py46
-rw-r--r--library/vkApi.py23
-rw-r--r--library/webtools.py4
-rw-r--r--library/xmpp/auth.py25
-rw-r--r--library/xmpp/browser.py2
-rw-r--r--library/xmpp/commands.py34
-rw-r--r--library/xmpp/debug.py4
-rw-r--r--library/xmpp/dispatcher.py10
-rw-r--r--library/xmpp/protocol.py49
-rw-r--r--library/xmpp/simplexml.py48
-rw-r--r--library/xmpp/transports.py97
-rw-r--r--locales/locale.pl2
-rw-r--r--locales/locale.ru2
15 files changed, 226 insertions, 167 deletions
diff --git a/Config_example.txt b/Config_example.txt
index 5a4166e..75f2574 100644
--- a/Config_example.txt
+++ b/Config_example.txt
@@ -69,6 +69,9 @@ ROSTER_UPDATE_TIMEOUT = 4
## Maximum forwarded messages depth.
MAXIMUM_FORWARD_DEPTH = 5
+## Users per thread.
+SLICE_STEP = 8
+
## Image that will be used if transport can't recieve image from VK.
URL_VCARD_NO_IMAGE = "http://simpleapps.ru/vk4xmpp.png"
diff --git a/gateway.py b/gateway.py
index 5bfa6f1..35a5bec 100644
--- a/gateway.py
+++ b/gateway.py
@@ -168,6 +168,8 @@ class VKLogin(object):
self.jidFrom = jidFrom
logger.debug("VKLogin.__init__ with number:%s from jid:%s" % (number, jidFrom))
+ getToken = lambda self: self.engine.token
+
def auth(self, token = None):
logger.debug("VKLogin.auth %s token" % ("with" if token else "without"))
try:
@@ -229,6 +231,9 @@ class VKLogin(object):
msgSend(Component, self.jidFrom, _(e.message + " Please, register again"), TransportID)
self.Online = False
logger.error("VKLogin: apiError %s for user %s" % (e.message, self.jidFrom))
+ except api.NetworkNotFound:
+ logger.critical("VKLogin: network unavailable. Is vk down?")
+ self.Online = False
return result
def captchaChallenge(self):
@@ -273,9 +278,6 @@ class VKLogin(object):
self.method("account.setOffline")
self.Online = False
- def getToken(self):
- return self.engine.token
-
def getFriends(self, fields = None):
fields = fields or ["screen_name"]
friendsRaw = self.method("friends.get", {"fields": ",".join(fields)}) or {} # friends.getOnline
@@ -316,7 +318,6 @@ class tUser(object):
self.lastMsgID = None
self.rosterSet = None
self.existsInDB = None
- self.lastStatus = None
self.last_activity = time.time()
self.last_udate = time.time()
self.jidFrom = source
@@ -336,6 +337,11 @@ class tUser(object):
logger.debug("tUser: %s exists in db. Will be deleted." % self.jidFrom)
threadRun(self.deleteUser)
+ def __eq__(self, user):
+ if isinstance(user, tUser):
+ return user.jidFrom == self.jidFrom
+ return self.jidFrom == user
+
def deleteUser(self, roster = False):
logger.debug("tUser: deleting user %s from db." % self.jidFrom)
with Database(DatabaseFile) as db:
@@ -351,14 +357,14 @@ class tUser(object):
self.vk.Online = False
if self.jidFrom in Transport:
del Transport[self.jidFrom]
- try:
- updateTransportsList(self, False)
- except NameError:
- pass
+ try:
+ updateTransportsList(self, False)
+ except NameError:
+ pass
def msg(self, body, uID, mType = "user_id"):
+ self.last_activity = time.time()
try:
- self.last_activity = time.time()
Message = self.vk.method("messages.send", {mType: uID, "message": body, "type": 0})
except:
crashLog("messages.send")
@@ -396,10 +402,10 @@ class tUser(object):
with Database(DatabaseFile, Semaphore) as db:
db("update users set token=? where jid=?", (self.vk.getToken(), self.jidFrom))
try:
- _ = self.vk.method("users.get")
- self.UserID = _[0]["uid"]
+ json = self.vk.method("users.get")
+ self.UserID = json[0]["uid"]
except (KeyError, TypeError):
- logger.error("tUser: could not recieve user id. JSON: %s" % str(_))
+ logger.error("tUser: could not recieve user id. JSON: %s" % str(json))
self.UserID = 0
jidToID[self.UserID] = self.jidFrom
@@ -504,7 +510,7 @@ class tUser(object):
except:
crashLog("tryAgain")
-msgSort = lambda Br, Ba: Br["date"] - Ba["date"]
+msgSort = lambda msgOne, msgTwo: msgOne["date"] - msgTwo["date"]
def Sender(cl, stanza):
try:
@@ -543,17 +549,21 @@ def vk2xmpp(id):
id = u"%s@%s" % (id, TransportID)
return id
-DESC = _("© simpleApps, 2013."
+DESC = _("© simpleApps, 2013 — 2014."
"\nYou can support developing of any project"
" via donation by WebMoney:"
"\nZ405564701378 | R330257574689.")
def updateTransportsList(user, add=True):
global lengthOfTransportsList
- if add and user not in TransportsList:
- TransportsList.append(user)
- elif user in TransportsList:
+ if user in TransportsList:
+ if add:
+ return
TransportsList.remove(user)
+ elif add:
+ TransportsList.append(user)
+ else:
+ return
length = len(TransportsList)
if length > lengthOfTransportsList:
start = lengthOfTransportsList
diff --git a/handlers/Presence.py b/handlers/Presence.py
index 58a7eea..74b9d67 100644
--- a/handlers/Presence.py
+++ b/handlers/Presence.py
@@ -9,33 +9,30 @@ def prsHandler(cl, prs):
jidFromStr = jidFrom.getStripped()
jidToStr = jidTo.getStripped()
if jidFromStr in Transport:
- Class = Transport[jidFromStr]
+ user = Transport[jidFromStr]
Resource = jidFrom.getResource()
if pType in ("available", "probe", None):
- if jidTo == TransportID and Resource not in Class.resources:
+ if jidTo == TransportID and Resource not in user.resources:
logger.debug("%s from user %s, will send sendInitPresence" % (pType, jidFromStr))
- Class.resources.append(Resource)
- if Class.lastStatus == "unavailable" and len(Class.resources) == 1:
- if not Class.vk.Online:
- Class.vk.Online = True
- Class.sendInitPresence()
+ user.resources.append(Resource)
+ user.sendInitPresence()
elif pType == "unavailable":
- if jidTo == TransportID and Resource in Class.resources:
- Class.resources.remove(Resource)
- if Class.resources:
- Class.sendOutPresence(jidFrom)
- if not Class.resources:
+ if jidTo == TransportID and Resource in user.resources:
+ user.resources.remove(Resource)
+ if user.resources:
+ user.sendOutPresence(jidFrom)
+ if not user.resources:
Sender(cl, xmpp.Presence(jidFrom, "unavailable", frm = TransportID))
- Class.vk.disconnect()
+ user.vk.disconnect()
if jidFromStr in Transport:
del Transport[jidFromStr]
- updateTransportsList(jidFromStr, False)
+ updateTransportsList(user, False)
elif pType == "error":
eCode = prs.getErrorCode()
if eCode == "404":
- Class.vk.disconnect()
+ user.vk.disconnect()
elif pType == "subscribe":
if jidToStr == TransportID:
@@ -43,27 +40,26 @@ def prsHandler(cl, prs):
Sender(cl, xmpp.Presence(jidFrom, frm = TransportID))
else:
Sender(cl, xmpp.Presence(jidFromStr, "subscribed", frm = jidTo))
- if Class.friends:
+ if user.friends:
id = vk2xmpp(jidToStr)
- if id in Class.friends:
- if Class.friends[id]["online"]:
+ if id in user.friends:
+ if user.friends[id]["online"]:
Sender(cl, xmpp.Presence(jidFrom, frm = jidTo))
+
elif pType == "unsubscribe":
if jidFromStr in Transport and jidToStr == TransportID:
- Class.deleteUser(True)
+ user.deleteUser(True)
WatcherMsg(_("User removed registration: %s") % jidFromStr)
- if jidToStr == TransportID:
- Class.lastStatus = pType
elif pType in ("available", None):
logger.debug("User %s not in transport but want to be in" % jidFromStr)
with Database(DatabaseFile) as db:
- db("select * from users where jid=?", (jidFromStr,))
- user = db.fetchone()
- if user:
+ db("select jid,username from users where jid=?", (jidFromStr,))
+ data = db.fetchone()
+ if data:
logger.debug("User %s found in db" % jidFromStr)
- jid, phone = user[:2]
+ jid, phone = data
Transport[jid] = user = tUser((phone, None), jid)
try:
if user.connect():
diff --git a/library/vkApi.py b/library/vkApi.py
index ebdc217..8eb31df 100644
--- a/library/vkApi.py
+++ b/library/vkApi.py
@@ -1,7 +1,6 @@
# /* coding: utf-8 */
-# © simpleApps CodingTeam, 2013.
-# Warning: Code in this module is ugly,
-# but we can't do better.
+# © simpleApps CodingTeam, 2013 — 2014.
+
import time, ssl, urllib, urllib2, cookielib
import logging, json, webtools
@@ -27,12 +26,14 @@ def attemptTo(maxRetries, resultType, *errors):
while retries < maxRetries:
try:
data = func(*args, **kwargs)
- except errors as exc:
+ except errors, exc:
retries += 1
time.sleep(0.2)
else:
break
else:
+ if str(exc.reason) == "[Errno 101] Network is unreachable":
+ raise NetworkNotFound()
data = resultType()
logger.debug("Error %s occured on executing %s" % (exc, func))
return data
@@ -172,7 +173,7 @@ class APIBinding:
else:
postTarget = webtools.getTagArg("form method=\"post\"", "action", body, "form")
if postTarget:
- body, response = self.RIP.post(PostTarget)
+ body, response = self.RIP.post(postTarget)
token = response.url.split("=")[1].split("&")[0]
else:
raise AuthError("Couldn't execute confirmThisApp()!")
@@ -195,7 +196,7 @@ class APIBinding:
if (self.last.pop() - self.last.pop(0)) < 1.1:
time.sleep(0.3) # warn: it was 0.4 // does it matter?
- response = self.RIP.post(url, values)
+ response = self.RIP.post(url, values) # Next func should handle NetworkNotFound
if response:
body, response = response
if body:
@@ -226,13 +227,13 @@ class APIBinding:
elif eCode == 5: # auth failed
raise VkApiError("Logged out")
if eCode == 7:
- raise NotAllowed
+ raise NotAllowed()
elif eCode == 9:
return {}
if eCode == 14: # captcha
if "captcha_sid" in error:
self.captcha = {"sid": error["captcha_sid"], "img": error["captcha_img"]}
- raise CaptchaNeeded
+ raise CaptchaNeeded()
raise VkApiError(body["error"])
def retry(self):
@@ -240,6 +241,12 @@ class APIBinding:
return self.method(*self.lastMethod)
+class NetworkNotFound(Exception): ## maybe network is unreachable or vk is down (same as 10 jan 2014)
+ pass
+
+class UserGoesOffline(Exception):
+ pass
+
class VkApiError(Exception):
pass
diff --git a/library/webtools.py b/library/webtools.py
index 65cea0c..9c13e16 100644
--- a/library/webtools.py
+++ b/library/webtools.py
@@ -56,7 +56,7 @@ def regexp(reg, string, findall = 1):
def getTagData(tag, data, close_tag = 0):
if not close_tag:
close_tag = tag
- pattern = re.compile("<%(tag)s.*?>(.*?)</%(close_tag)s>" % vars(), flags=re.S+re.IGNORECASE)
+ pattern = re.compile("<%(tag)s.*?>(.*?)</%(close_tag)s>" % vars(), flags=re.DOTALL | re.IGNORECASE)
tagData = pattern.search(data)
if tagData:
tagData = tagData.group(1)
@@ -65,7 +65,7 @@ def getTagData(tag, data, close_tag = 0):
def getTagArg(tag, argv, data, close_tag = 0):
if not close_tag:
close_tag = tag
- pattern = re.compile("<%(tag)s.? %(argv)s=[\"']?(.*?)[\"']?\">(.*?)</%(close_tag)s>" % vars(), flags=re.DOTALL|re.IGNORECASE)
+ pattern = re.compile("<%(tag)s.? %(argv)s=[\"']?(.*?)[\"']?\">(.*?)</%(close_tag)s>" % vars(), flags=re.DOTALL | re.IGNORECASE)
tagData = pattern.search(data)
if tagData:
tagData = tagData.group(1)
diff --git a/library/xmpp/auth.py b/library/xmpp/auth.py
index ca21835..7042d42 100644
--- a/library/xmpp/auth.py
+++ b/library/xmpp/auth.py
@@ -20,20 +20,19 @@ Can be used both for client and transport authentication.
"""
import dispatcher
-import sha
+import hashlib
from base64 import encodestring, decodestring
-from hashlib import md5 as __md5
from plugin import PlugIn
from protocol import *
from random import random as _random
from re import findall as re_findall
def HH(some):
- return __md5(some).hexdigest()
+ return hashlib.md5(some).hexdigest()
def H(some):
- return __md5(some).digest()
+ return hashlib.md5(some).digest()
def C(some):
return ":".join(some)
@@ -70,7 +69,8 @@ class NonSASL(PlugIn):
query.setTagData("resource", self.resource)
if query.getTag("digest"):
self.DEBUG("Performing digest authentication", "ok")
- query.setTagData("digest", sha.new(owner.Dispatcher.Stream._document_attrs["id"] + self.password).hexdigest())
+ hash = hashlib.sha1(owner.Dispatcher.Stream._document_attrs["id"] + self.password).hexdigest()
+ query.setTagData("digest", hash)
if query.getTag("password"):
query.delChild("password")
method = "digest"
@@ -78,9 +78,9 @@ class NonSASL(PlugIn):
token = query.getTagData("token")
seq = query.getTagData("sequence")
self.DEBUG("Performing zero-k authentication", "ok")
- hash = sha.new(sha.new(self.password).hexdigest() + token).hexdigest()
- for foo in xrange(int(seq)):
- hash = sha.new(hash).hexdigest()
+ hash = hashlib.sha1(hashlib.sha1(self.password).hexdigest() + token).hexdigest()
+ for i in xrange(int(seq)):
+ hash = hashlib.sha1(hash).hexdigest()
query.setTagData("hash", hash)
method = "0k"
else:
@@ -101,7 +101,8 @@ class NonSASL(PlugIn):
Authenticate component. Send handshake stanza and wait for result. Returns "ok" on success.
"""
self.handshake = 0
- owner.send(Node(NS_COMPONENT_ACCEPT + " handshake", payload=[sha.new(owner.Dispatcher.Stream._document_attrs["id"] + self.password).hexdigest()]))
+ hash = hashlib.sha1(owner.Dispatcher.Stream._document_attrs["id"] + self.password).hexdigest()
+ owner.send(Node(NS_COMPONENT_ACCEPT + " handshake", payload=[hash]))
owner.RegisterHandler("handshake", self.handshakeHandler, xmlns=NS_COMPONENT_ACCEPT)
while not self.handshake:
self.DEBUG("waiting on handshake", "notify")
@@ -235,7 +236,7 @@ class SASL(PlugIn):
resp["realm"] = self._owner.Server
resp["nonce"] = chal["nonce"]
cnonce = ""
- for i in range(7):
+ for i in xrange(7):
cnonce += hex(int(_random() * 65536 * 4096))[2:]
resp["cnonce"] = cnonce
resp["nc"] = ("00000001")
@@ -247,8 +248,8 @@ class SASL(PlugIn):
resp["response"] = response
resp["charset"] = "utf-8"
sasl_data = ""
- for key in ["charset", "username", "realm", "nonce", "nc", "cnonce", "digest-uri", "response", "qop"]:
- if key in ["nc", "qop", "response", "charset"]:
+ for key in ("charset", "username", "realm", "nonce", "nc", "cnonce", "digest-uri", "response", "qop"):
+ if key in ("nc", "qop", "response", "charset"):
sasl_data += "%s=%s," % (key, resp[key])
else:
sasl_data += "%s=\"%s\"," % (key, resp[key])
diff --git a/library/xmpp/browser.py b/library/xmpp/browser.py
index d2ffdf0..db74131 100644
--- a/library/xmpp/browser.py
+++ b/library/xmpp/browser.py
@@ -122,7 +122,7 @@ class Browser(PlugIn):
get returns "" or None as the key or None as the dict.
Used internally.
"""
- if self._handlers.has_key(jid):
+ if jid in self._handlers:
cur = self._handlers[jid]
elif set:
self._handlers[jid] = {}
diff --git a/library/xmpp/commands.py b/library/xmpp/commands.py
index 9c75649..3d4f75c 100644
--- a/library/xmpp/commands.py
+++ b/library/xmpp/commands.py
@@ -95,13 +95,13 @@ class Commands(PlugIn):
except Exception:
conn.send(Error(request, ERR_BAD_REQUEST))
raise NodeProcessed()
- if self._handlers.has_key(jid):
- if self._handlers[jid].has_key(node):
+ if jid in self._handlers:
+ if node in self._handlers[jid]:
self._handlers[jid][node]["execute"](conn, request)
else:
conn.send(Error(request, ERR_ITEM_NOT_FOUND))
raise NodeProcessed()
- elif self._handlers[""].has_key(node):
+ elif node in self._handlers[""]:
self._handlers[""][node]["execute"](conn, request)
else:
conn.send(Error(request, ERR_ITEM_NOT_FOUND))
@@ -124,7 +124,7 @@ class Commands(PlugIn):
items = []
jid = str(request.getTo())
# Get specific jid based results
- if self._handlers.has_key(jid):
+ if jid in self._handlers:
for each in self._handlers[jid].keys():
items.append((jid, each))
else:
@@ -160,10 +160,10 @@ class Commands(PlugIn):
# We must:
# Add item into disco
# Add item into command list
- if not self._handlers.has_key(jid):
+ if jid not in self._handlers:
self._handlers[jid] = {}
self._browser.setDiscoHandler(self._DiscoHandler, node=NS_COMMANDS, jid=jid)
- if self._handlers[jid].has_key(name):
+ if name in self._handlers[jid]:
raise NameError("Command Exists")
self._handlers[jid][name] = {"disco": cmddisco, "execute": cmdexecute}
# Need to add disco stuff here
@@ -177,9 +177,9 @@ class Commands(PlugIn):
# We must:
# Remove item from disco
# Remove item from command list
- if not self._handlers.has_key(jid):
+ if jid not in self._handlers:
raise NameError("Jid not found")
- if not self._handlers[jid].has_key(name):
+ if name not in self._handlers[jid]:
raise NameError("Command not found")
# Do disco removal here
command = self.getCommand(name, jid)["disco"]
@@ -193,9 +193,9 @@ class Commands(PlugIn):
# This gets the command object with name
# We must:
# Return item that matches this name
- if not self._handlers.has_key(jid):
+ if jid not in self._handlers:
raise NameError("Jid not found")
- if not self._handlers[jid].has_key(name):
+ if name not in self._handlers[jid]:
raise NameError("Command not found")
return self._handlers[jid][name]
@@ -258,7 +258,7 @@ class Command_Handler_Prototype(PlugIn):
"""
Returns an id for the command session.
"""
- self.count = self.count + 1
+ self.count += 1
return "cmd-%s-%d" % (self.name, self.count)
def Execute(self, conn, request):
@@ -277,10 +277,10 @@ class Command_Handler_Prototype(PlugIn):
if action == None:
action = "execute"
# Check session is in session list
- if self.sessions.has_key(session):
+ if session in self.sessions:
if self.sessions[session]["jid"] == request.getFrom():
# Check action is vaild
- if self.sessions[session]["actions"].has_key(action):
+ if action in self.sessions[session]["actions"]:
# Execute next action
self.sessions[session]["actions"][action](conn, request)
else:
@@ -299,15 +299,15 @@ class Command_Handler_Prototype(PlugIn):
# New session
self.initial[action](conn, request)
- def _DiscoHandler(self, conn, request, type):
+ def _DiscoHandler(self, conn, request, typ):
"""
The handler for discovery events.
"""
- if type == "list":
+ if typ == "list":
result = (request.getTo(), self.name, self.description)
- elif type == "items":
+ elif typ == "items":
result = []
- elif type == "info":
+ elif typ == "info":
result = self.discoinfo
return result
diff --git a/library/xmpp/debug.py b/library/xmpp/debug.py
index e84b4c1..ccccd02 100644
--- a/library/xmpp/debug.py
+++ b/library/xmpp/debug.py
@@ -162,13 +162,13 @@ class Debug:
self._fh.write(output)
except Exception:
# unicode strikes again ;)
- s = u""
+ s = unicode()
for i in xrange(len(output)):
if ord(output[i]) < 128:
c = output[i]
else:
c = "?"
- s = s + c
+ s += c
self._fh.write("%s%s%s" % (pre, s, suf))
self._fh.flush()
diff --git a/library/xmpp/dispatcher.py b/library/xmpp/dispatcher.py
index 62151d3..5d020d0 100644
--- a/library/xmpp/dispatcher.py
+++ b/library/xmpp/dispatcher.py
@@ -27,6 +27,7 @@ import time
from plugin import PlugIn
from protocol import *
+from select import select
from xml.parsers.expat import ExpatError
DefaultTimeout = 25
@@ -151,9 +152,14 @@ class Dispatcher(PlugIn):
if self._pendingExceptions:
e = self._pendingExceptions.pop()
raise e[0], e[1], e[2]
- if self._owner.Connection.pending_data(timeout):
+ conn = self._owner.Connection
+ recv, send = select([conn._sock], [conn._sock], [], timeout)[:2]
+ if send:
+ while conn._send_queue:
+ conn.send_now(conn._send_queue.pop(0))
+ if recv:
try:
- data = self._owner.Connection.receive()
+ data = conn.receive()
except IOError:
return None
try:
diff --git a/library/xmpp/protocol.py b/library/xmpp/protocol.py
index f49dcba..36822c6 100644
--- a/library/xmpp/protocol.py
+++ b/library/xmpp/protocol.py
@@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
-# $Id: protocol.py, v1.63 2013/12/06 alkorgun Exp $
+# $Id: protocol.py, v1.64 2014/01/10 alkorgun Exp $
"""
Protocol module contains tools that is needed for processing of
@@ -109,13 +109,15 @@ NS_MUC_ROOMS = NS_MUC + "#rooms" # XEP-0045
NS_MUC_TRAFIC = NS_MUC + "#traffic" # XEP-0045
NS_NICK = "http://jabber.org/protocol/nick" # XEP-0172
NS_OFFLINE = "http://jabber.org/protocol/offline" # XEP-0013
+NS_OOB = "jabber:x:oob" # XEP-0066
NS_PHYSLOC = "http://jabber.org/protocol/physloc" # XEP-0112
NS_PRESENCE = "presence" # Jabberd2
NS_PRIVACY = "jabber:iq:privacy" # RFC 3921
NS_PRIVATE = "jabber:iq:private" # XEP-0049
NS_PUBSUB = "http://jabber.org/protocol/pubsub" # XEP-0060
-NS_REGISTER = "jabber:iq:register" # XEP-0077
NS_RC = "http://jabber.org/protocol/rc" # XEP-0146
+NS_REGISTER = "jabber:iq:register" # XEP-0077
+NS_RECEIPTS = "urn:xmpp:receipts" # XEP-0184
NS_ROSTER = "jabber:iq:roster" # RFC 3921
NS_ROSTERX = "http://jabber.org/protocol/rosterx" # XEP-0144
NS_RPC = "jabber:iq:rpc" # XEP-0009
@@ -126,10 +128,15 @@ NS_SESSION = "urn:ietf:params:xml:ns:xmpp-session" # RFC 3921
NS_SI = "http://jabber.org/protocol/si" # XEP-0096
NS_SI_PUB = "http://jabber.org/protocol/sipub" # XEP-0137
NS_SIGNED = "jabber:x:signed" # XEP-0027
+NS_SOFTWAREINFO = "urn:xmpp:dataforms:softwareinfo" # XEP-0155
NS_STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas" # RFC 3920
+NS_STATS = "http://jabber.org/protocol/stats" # XEP-0039
NS_STREAMS = "http://etherx.jabber.org/streams" # RFC 3920
NS_TIME = "jabber:iq:time" # XEP-0090 (deprecated)
NS_TLS = "urn:ietf:params:xml:ns:xmpp-tls" # RFC 3920
+NS_URN_ATTENTION = "urn:xmpp:attention:0" # XEP-0224
+NS_URN_OOB = "urn:xmpp:bob" # XEP-0158
+NS_URN_TIME = "urn:xmpp:time" # XEP-0202
NS_VACATION = "http://jabber.org/protocol/vacation" # XEP-0109
NS_VCARD = "vcard-temp" # XEP-0054
NS_VCARD_UPDATE = "vcard-temp:x:update" # XEP-0153
@@ -137,14 +144,9 @@ NS_VERSION = "jabber:iq:version" # XEP-0092
NS_WAITINGLIST = "http://jabber.org/protocol/waitinglist" # XEP-0130
NS_XHTML_IM = "http://jabber.org/protocol/xhtml-im" # XEP-0071
NS_XMPP_STREAMS = "urn:ietf:params:xml:ns:xmpp-streams" # RFC 3920
-NS_STATS = "http://jabber.org/protocol/stats" # XEP-0039
NS_PING = "urn:xmpp:ping" # XEP-0199
+
NS_MUC_FILTER = "http://jabber.ru/muc-filter"
-NS_URN_TIME = "urn:xmpp:time" # XEP-0202
-NS_RECEIPTS = "urn:xmpp:receipts" # XEP-0184
-NS_OOB = "jabber:x:oob" # XEP-0066
-NS_URN_ATTENTION = "urn:xmpp:attention:0" # XEP-0224
-NS_URN_OOB = "urn:xmpp:bob" # XEP-0158
STREAM_NOT_AUTHORIZED = NS_XMPP_STREAMS + " not-authorized"
STREAM_REMOTE_CONNECTION_FAILED = NS_XMPP_STREAMS + " remote-connection-failed"
@@ -234,7 +236,7 @@ ERRORS = {
"urn:ietf:params:xml:ns:xmpp-streams invalid-from": ["cancel", "", "The JID or hostname provided in a \"from\" address does not match an authorized JID or validated domain negotiated between servers via SASL or dialback, or between a client and a server via authentication and resource authorization."],
"urn:ietf:params:xml:ns:xmpp-streams bad-format": ["", "", "The entity has sent XML that cannot be processed."],
"urn:ietf:params:xml:ns:xmpp-streams resource-constraint": ["", "", "The server lacks the system resources necessary to service the stream."],
- "urn:ietf:params:xml:ns:xmpp-stanzas undefined-condition": ["500", "", ""],
+ "urn:ietf:params:xml:ns:xmpp-stanzas undefined-condition": ["500", "", "The condition is undefined."],
"urn:ietf:params:xml:ns:xmpp-stanzas redirect": ["302", "modify", "The recipient or server is redirecting requests for this information to another entity."],
"urn:ietf:params:xml:ns:xmpp-streams bad-namespace-prefix": ["", "", "The entity has sent a namespace prefix that is unsupported, or has sent no namespace prefix on an element that requires such a prefix."],
"urn:ietf:params:xml:ns:xmpp-streams system-shutdown": ["", "", "The server is being shut down and all active streams are being closed."],
@@ -704,7 +706,7 @@ class Message(Protocol):
def buildReply(self, text=None):
"""
Builds and returns another message object with specified text.
- The to, from and thread properties of new message are pre-set as reply to this message.
+ The to, from type and thread properties of new message are pre-set as reply to this message.
"""
msg = Message(to=self.getFrom(), frm=self.getTo(), body=text)
thr = self.getThread()
@@ -835,9 +837,15 @@ class Iq(Protocol):
if queryNS:
self.setQueryNS(queryNS)
+ def getQuery(self):
+ """
+ Returns the query node.
+ """
+ return self.getTag("query")
+
def getQueryNS(self):
"""
- Return the namespace of the "query" child element.
+ Returns the namespace of the "query" child element.
"""
tag = self.getTag("query")
if tag:
@@ -845,13 +853,13 @@ class Iq(Protocol):
def getQuerynode(self):
"""
- Return the "node" attribute value of the "query" child element.
+ Returns the "node" attribute value of the "query" child element.
"""
return self.getTagAttr("query", "node")
def getQueryPayload(self):
"""
- Return the "query" child element payload.
+ Returns the "query" child element payload.
"""
tag = self.getTag("query")
if tag:
@@ -859,12 +867,25 @@ class Iq(Protocol):
def getQueryChildren(self):
"""
- Return the "query" child element child nodes.
+ Returns the "query" child element child nodes.
"""
tag = self.getTag("query")
if tag:
return tag.getChildren()
+ def setQuery(self, name=None):
+ """
+ Changes the name of the query node, creates it if needed.
+ Keep the existing name if none is given (use "query" if it's a creation).
+ Returns the query node.
+ """
+ query = self.getQuery()
+ if query is None:
+ query = self.addChild("query")
+ if name is not None:
+ query.setName(name)
+ return query
+
def setQueryNS(self, namespace):
"""
Set the namespace of the "query" child element.
diff --git a/library/xmpp/simplexml.py b/library/xmpp/simplexml.py
index a416d24..ab2a3b4 100644
--- a/library/xmpp/simplexml.py
+++ b/library/xmpp/simplexml.py
@@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
-# $Id: simplexml.py, v1.35 2013/10/21 alkorgun Exp $
+# $Id: simplexml.py, v1.36 2014/01/10 alkorgun Exp $
"""
Simplexml module provides xmpppy library with all needed tools to handle
@@ -25,8 +25,6 @@ import xml.parsers.expat
XML_ls = (
("&", "&amp;"),
- ("\x0C", ""),
- ("\x1B", ""),
("<", "&lt;"),
(">", "&gt;"),
('"', "&quot;"),
@@ -108,7 +106,7 @@ class Node(object):
self.nsp_cache[k] = v
for attr, val in attrs.items():
if attr == "xmlns":
- self.nsd[u""] = val
+ self.nsd[""] = val
elif attr.startswith("xmlns:"):
self.nsd[attr[6:]] = val
self.attrs[attr] = attrs[attr]
@@ -149,39 +147,39 @@ class Node(object):
if self.namespace:
if not self.parent or self.parent.namespace != self.namespace:
if "xmlns" not in self.attrs:
- s = s + " xmlns=\"%s\"" % self.namespace
+ s += " xmlns=\"%s\"" % self.namespace
for key in self.attrs.keys():
val = ustr(self.attrs[key])
- s = s + " %s=\"%s\"" % (key, XMLescape(val))
- s = s + ">"
+ s += " %s=\"%s\"" % (key, XMLescape(val))
+ s += ">"
cnt = 0
if self.kids:
if fancy:
- s = s + "\n"
+ s += "\n"
for a in self.kids:
if not fancy and (len(self.data) - 1) >= cnt:
- s = s + XMLescape(self.data[cnt])
+ s += XMLescape(self.data[cnt])
elif (len(self.data) - 1) >= cnt:
- s = s + XMLescape(self.data[cnt].strip())
+ s += XMLescape(self.data[cnt].strip())
if isinstance(a, Node):
- s = s + a.__str__(fancy and fancy + 1)
+ s += a.__str__(fancy and fancy + 1)
elif a:
- s = s + a.__str__()
- cnt = cnt + 1
+ s += a.__str__()
+ cnt += 1
if not fancy and (len(self.data) - 1) >= cnt:
- s = s + XMLescape(self.data[cnt])
+ s += XMLescape(self.data[cnt])
elif (len(self.data) - 1) >= cnt:
- s = s + XMLescape(self.data[cnt].strip())
+ s += XMLescape(self.data[cnt].strip())
if not self.kids and s.endswith(">"):
s = s[:-1] + " />"
if fancy:
- s = s + "\n"
+ s += "\n"
else:
if fancy and not self.data:
- s = s + (fancy - 1) * 2 * " "
- s = s + "</" + self.name + ">"
+ s += (fancy - 1) * 2 * " "
+ s += "</" + self.name + ">"
if fancy:
- s = s + "\n"
+ s += "\n"
return s
def getCDATA(self):
@@ -193,12 +191,12 @@ class Node(object):
cnt = 0
if self.kids:
for a in self.kids:
- s = s + self.data[cnt]
+ s += self.data[cnt]
if a:
- s = s + a.getCDATA()
- cnt = cnt + 1
+ s += a.getCDATA()
+ cnt += 1
if (len(self.data) - 1) >= cnt:
- s = s + self.data[cnt]
+ s += self.data[cnt]
return s
def addChild(self, name=None, attrs={}, payload=[], namespace=None, node=None):
@@ -216,7 +214,7 @@ class Node(object):
if namespace:
newnode.setNamespace(namespace)
self.kids.append(newnode)
- self.data.append(u"")
+ self.data.append("")
return newnode
def addData(self, data):
@@ -301,7 +299,7 @@ class Node(object):
["text1", <nodea instance>, <nodeb instance>, " text2"].
"""
pl = []
- for i in range(max(len(self.data), len(self.kids))):
+ for i in xrange(max(len(self.data), len(self.kids))):
if i < len(self.data) and self.data[i]:
pl.append(self.data[i])
if i < len(self.kids) and self.kids[i]:
diff --git a/library/xmpp/transports.py b/library/xmpp/transports.py
index 13a2798..2d93686 100644
--- a/library/xmpp/transports.py
+++ b/library/xmpp/transports.py
@@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
-# $Id: transports.py, v1.36 2013/11/03 alkorgun Exp $
+# $Id: transports.py, v1.36 2014/01/10 alkorgun Exp $
"""
This module contains the low-level implementations of xmpppy connect methods or
@@ -29,6 +29,8 @@ Also exception 'error' is defined to allow capture of this module specific excep
import sys
import socket
+if sys.hexversion >= 0x20600F0:
+ import ssl
import dispatcher
from base64 import encodestring
@@ -80,6 +82,7 @@ class TCPsocket(PlugIn):
self.DBG_LINE = "socket"
self._exported_methods = [self.send, self.disconnect]
self._server, self.use_srv = server, use_srv
+ self._send_queue = []
def srv_lookup(self, server):
"""
@@ -92,7 +95,9 @@ class TCPsocket(PlugIn):
dns__ = dns.Request()
response = dns__.req(query, qtype="SRV")
if response.answers:
- (port, host) = response.answers[0]["data"][2:]
+ # Sort by priority, according to RFC 2782.
+ answers = sorted(response.answers, key=lambda a: a["data"][0])
+ (port, host) = answers[0]["data"][2:]
server = str(host), int(port)
except dns.DNSError:
self.DEBUG("An error occurred while looking up %s." % query, "warn")
@@ -130,41 +135,50 @@ class TCPsocket(PlugIn):
def connect(self, server=None):
"""
- Try to connect to the given host/port. Does not lookup for SRV record.
+ Try to connect to the given host/port.
Returns non-empty string on success.
"""
if not server:
server = self._server
host, port = server
- server = (host, int(port))
- if ":" in host:
- sock = socket.AF_INET6
- server = server.__add__((0, 0))
- else:
- sock = socket.AF_INET
+ socktype = socket.SOCK_STREAM
try:
- self._sock = socket.socket(sock, socket.SOCK_STREAM)
- self._sock.connect(server)
- self._send = self._sock.sendall
- self._recv = self._sock.recv
- except socket.error as error:
+ lookup = reversed(socket.getaddrinfo(host, int(port), 0, socktype))
+ except Exception:
+ addr = (host, int(port))
+ if ":" in host:
+ af = socket.AF_INET6
+ addr = addr.__add__((0, 0))
+ else:
+ af = socket.AF_INET
+ lookup = [(af, socktype, 1, 6, addr)]
+ for af, socktype, proto, cn, addr in lookup:
try:
- code, error = error
+ self._sock = socket.socket(af, socktype)
+ self._sock.connect(addr)
+ self._send = self._sock.sendall
+ self._recv = self._sock.recv
+ except socket.error as error:
+ if getattr(self, "_sock", None):
+ self._sock.close()
+ try:
+ code, error = error
+ except Exception:
+ code = -1
+ self.DEBUG("Failed to connect to remote host %s: %s (%s)" % (repr(server), error, code), "error")
except Exception:
- code = -1
- self.DEBUG("Failed to connect to remote host %s: %s (%s)" % (repr(server), error, code), "error")
- except Exception:
- pass
- else:
- self.DEBUG("Successfully connected to remote host %s." % repr(server), "start")
- return "ok"
+ pass
+ else:
+ self.DEBUG("Successfully connected to remote host %s." % repr(server), "start")
+ return "ok"
def plugout(self):
"""
Disconnect from the remote server and unregister self.disconnected method from
the owner's dispatcher.
"""
- self._sock.close()
+ if getattr(self, "_sock", None):
+ self._sock.close()
if hasattr(self._owner, "Connection"):
del self._owner.Connection
self._owner.UnregisterDisconnectHandler(self.disconnected)
@@ -206,7 +220,10 @@ class TCPsocket(PlugIn):
raise IOError("Disconnected!")
return data
- def send(self, data, timeout=0.002):
+ def send(self, data):
+ self._send_queue.append(data)
+
+ def send_now(self, data, timeout=0.002):
"""
Writes raw outgoing data. Blocks until done.
If supplied data is unicode string, encodes it to utf-8 before send.
@@ -215,20 +232,17 @@ class TCPsocket(PlugIn):
data = data.encode("utf-8")
elif not isinstance(data, str):
data = ustr(data).encode("utf-8")
- while not select((), [self._sock], (), 0.002)[1]:
- pass
+ try:
+ self._send(data)
+ except Exception:
+ self.DEBUG("Socket error while sending data.", "error")
+ self._owner.disconnected()
else:
- try:
- self._send(data)
- except Exception:
- self.DEBUG("Socket error while sending data.", "error")
- self._owner.disconnected()
- else:
- if not data.strip():
- data = repr(data)
- self.DEBUG(data, "sent")
- if hasattr(self._owner, "Dispatcher"):
- self._owner.Dispatcher.Event("", DATA_SENT, data)
+ if not data.strip():
+ data = repr(data)
+ self.DEBUG(data, "sent")
+ if hasattr(self._owner, "Dispatcher"):
+ self._owner.Dispatcher.Event("", DATA_SENT, data)
def pending_data(self, timeout=0):
"""
@@ -382,9 +396,12 @@ class TLS(PlugIn):
def _startSSL(self):
tcpsock = self._owner.Connection
- tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None)
- tcpsock._sslIssuer = tcpsock._sslObj.issuer()
- tcpsock._sslServer = tcpsock._sslObj.server()
+ if sys.hexversion >= 0x20600F0:
+ tcpsock._sslObj = ssl.wrap_socket(tcpsock._sock, None, None)
+ else:
+ tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None)
+ tcpsock._sslIssuer = tcpsock._sslObj.issuer()
+ tcpsock._sslServer = tcpsock._sslObj.server()
tcpsock._recv = tcpsock._sslObj.read
tcpsock._send = tcpsock._sslObj.write
tcpsock._seen_data = 1
diff --git a/locales/locale.pl b/locales/locale.pl
index a28eb4d..5235152 100644
--- a/locales/locale.pl
+++ b/locales/locale.pl
@@ -7,7 +7,7 @@ Null password=Puste hasło lub access-token!
Incorrect password or access token!=Błędne hasło lub access-token!
Initialization failed.=Błąd inicjalizacji.
Feature not implemented.=Funkcja nie realizowana.
-© simpleApps, 2013.\LYou can support developing of any project via donation by WebMoney:\LZ405564701378 | R330257574689.=© simpleApps, 2013.\LВы можете поддержать разработку с помощью пожертвования через WebMoney:\LZ405564701378 | R330257574689.
+© simpleApps, 2013 — 2014.\LYou can support developing of any project via donation by WebMoney:\LZ405564701378 | R330257574689.=© simpleApps, 2013 — 2014.\LВы можете поддержать разработку с помощью пожертвования через WebMoney:\LZ405564701378 | R330257574689.
If you found any problems, please contact us:\Lhttp://github.com/mrDoctorWho/vk4xmpp • xmpp:simpleapps@conference.jabber.ru=Если вы столкнулись с какой-либо проблемой и считаете, что сделали всё верно, то, пожалуйста, свяжитесь с нами:\Lhttp://github.com/mrDoctorWho/vk4xmpp • xmpp:simpleapps@conference.jabber.ru
Contact uses VK4XMPP Transport\L%s=Контакт использует транспорт VK4XMPP\L%s
User is not your friend.=Użytkownik nie jest twoim przyjacielem!
diff --git a/locales/locale.ru b/locales/locale.ru
index caa4f80..6ef9100 100644
--- a/locales/locale.ru
+++ b/locales/locale.ru
@@ -7,7 +7,7 @@ Null password=Пустой пароль или access-token!
Incorrect password or access token!=Неправильный пароль или access-token!
Initialization failed.=Ошибка инициализации.
Feature not implemented.=Возможность не реализована.
-© simpleApps, 2013.\LYou can support developing of any project via donation by WebMoney:\LZ405564701378 | R330257574689.=© simpleApps, 2013.\LВы можете поддержать разработку с помощью пожертвования через WebMoney:\LZ405564701378 | R330257574689.
+© simpleApps, 2013 — 2014.\LYou can support developing of any project via donation by WebMoney:\LZ405564701378 | R330257574689.=© simpleApps, 2013 — 2014.\LВы можете поддержать разработку с помощью пожертвования через WebMoney:\LZ405564701378 | R330257574689.
If you found any problems, please contact us:\Lhttp://github.com/mrDoctorWho/vk4xmpp • xmpp:simpleapps@conference.jabber.ru=Если вы столкнулись с какой-либо проблемой и считаете, что сделали всё верно, то, пожалуйста, свяжитесь с нами:\Lhttp://github.com/mrDoctorWho/vk4xmpp • xmpp:simpleapps@conference.jabber.ru
Contact uses VK4XMPP Transport\L%s=Контакт использует транспорт VK4XMPP\L%s
User is not your friend.=Пользователь не ваш друг!