fixed link-local messaging (broken by session-centric) and removed a ton of duplicated/unused code

This commit is contained in:
Brendan Taylor 2008-05-17 02:23:46 +00:00
parent 75ad801f62
commit 517d962221
3 changed files with 217 additions and 673 deletions

View File

@ -82,7 +82,7 @@ class ConnectionBytestream:
if not file_props.has_key('stopped') or not file_props['stopped']:
return False
return True
def send_success_connect_reply(self, streamhost):
''' send reply to the initiator of FT that we
made a connection
@ -97,7 +97,7 @@ class ConnectionBytestream:
stream_tag = query.setTag('streamhost-used')
stream_tag.setAttr('jid', streamhost['jid'])
self.connection.send(iq)
def remove_transfers_for_contact(self, contact):
''' stop all active transfer for contact '''
for file_props in self.files_props.values():
@ -112,14 +112,14 @@ class ConnectionBytestream:
if contact.jid == sender_jid:
file_props['error'] = -3
self.remove_transfer(file_props)
def remove_all_transfers(self):
''' stops and removes all active connections from the socks5 pool '''
for file_props in self.files_props.values():
self.remove_transfer(file_props, remove_from_list = False)
del(self.files_props)
self.files_props = {}
def remove_transfer(self, file_props, remove_from_list = True):
if file_props is None:
return
@ -130,7 +130,7 @@ class ConnectionBytestream:
if remove_from_list:
if self.files_props.has_key('sid'):
del(self.files_props['sid'])
def disconnect_transfer(self, file_props):
if file_props is None:
return
@ -142,7 +142,7 @@ class ConnectionBytestream:
if host.has_key('idx') and host['idx'] > 0:
gajim.socks5queue.remove_receiver(host['idx'])
gajim.socks5queue.remove_sender(host['idx'])
def send_socks5_info(self, file_props, fast = True, receiver = None,
sender = None):
''' send iq for the present streamhosts and proxies '''
@ -165,7 +165,7 @@ class ConnectionBytestream:
if proxies.__contains__(default):
proxies.remove(default)
proxies.insert(0, default)
for proxy in proxies:
(host, _port, jid) = gajim.proxy65_manager.get_proxy(proxy, self.name)
if host is None:
@ -313,7 +313,7 @@ class ConnectionBytestream:
field.setAttr('type', 'list-single')
field.addOption(common.xmpp.NS_BYTESTREAM)
self.connection.send(iq)
def _result_socks5_sid(self, sid, hash_id):
''' store the result of sha message from auth. '''
if not self.files_props.has_key(sid):
@ -321,7 +321,7 @@ class ConnectionBytestream:
file_props = self.files_props[sid]
file_props['hash'] = hash_id
return
def _connect_error(self, to, _id, sid, code = 404):
''' cb, when there is an error establishing BS connection, or
when connection is rejected'''
@ -360,7 +360,7 @@ class ConnectionBytestream:
activate.setData(file_props['proxy_receiver'])
iq.setID(auth_id)
self.connection.send(iq)
# register xmpppy handlers for bytestream and FT stanzas
def _bytestreamErrorCB(self, con, iq_obj):
gajim.log.debug('_bytestreamErrorCB')
@ -376,7 +376,7 @@ class ConnectionBytestream:
file_props['error'] = -4
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
raise common.xmpp.NodeProcessed
def _bytestreamSetCB(self, con, iq_obj):
gajim.log.debug('_bytestreamSetCB')
target = unicode(iq_obj.getAttr('to'))
@ -440,14 +440,14 @@ class ConnectionBytestream:
if host['initiator'] == frm and host.has_key('idx'):
gajim.socks5queue.activate_proxy(host['idx'])
raise common.xmpp.NodeProcessed
def _bytestreamResultCB(self, con, iq_obj):
gajim.log.debug('_bytestreamResultCB')
frm = helpers.get_full_jid_from_iq(iq_obj)
real_id = unicode(iq_obj.getAttr('id'))
query = iq_obj.getTag('query')
gajim.proxy65_manager.resolve_result(frm, query)
try:
streamhost = query.getTag('streamhost-used')
except: # this bytestream result is not what we need
@ -512,9 +512,9 @@ class ConnectionBytestream:
if len(fasts) > 0:
self._connect_error(frm, fasts[0]['id'], file_props['sid'],
code = 406)
raise common.xmpp.NodeProcessed
def _siResultCB(self, con, iq_obj):
gajim.log.debug('_siResultCB')
id = iq_obj.getAttr('id')
@ -551,7 +551,7 @@ class ConnectionBytestream:
return
self.send_socks5_info(file_props, fast = True)
raise common.xmpp.NodeProcessed
def _siSetCB(self, con, iq_obj):
gajim.log.debug('_siSetCB')
jid = helpers.get_jid_from_iq(iq_obj)
@ -616,7 +616,7 @@ class ConnectionDisco:
For identity: category, type is mandatory, name is optional.
For feature: var is mandatory'''
self._discover(common.xmpp.NS_DISCO_INFO, jid, node, id_prefix)
def request_register_agent_info(self, agent):
if not self.connection:
return None
@ -642,8 +642,8 @@ class ConnectionDisco:
else:
# fixed: blocking
common.xmpp.features_nb.register(self.connection, agent, info, None)
def _discover(self, ns, jid, node = None, id_prefix = None):
if not self.connection:
return
@ -654,11 +654,11 @@ class ConnectionDisco:
if node:
iq.setQuerynode(node)
self.connection.send(iq)
def _ReceivedRegInfo(self, con, resp, agent):
common.xmpp.features_nb._ReceivedRegInfo(con, resp, agent)
self._IqCB(con, resp)
def _discoGetCB(self, con, iq_obj):
''' get disco info '''
frm = helpers.get_full_jid_from_iq(iq_obj)
@ -675,10 +675,10 @@ class ConnectionDisco:
feature = common.xmpp.Node('feature')
feature.setAttr('var', f)
query.addChild(node=feature)
self.connection.send(iq)
raise common.xmpp.NodeProcessed
def _DiscoverItemsErrorCB(self, con, iq_obj):
gajim.log.debug('DiscoverItemsErrorCB')
jid = helpers.get_full_jid_from_iq(iq_obj)
@ -710,7 +710,7 @@ class ConnectionDisco:
continue
items.append(attr)
jid = helpers.get_full_jid_from_iq(iq_obj)
hostname = gajim.config.get_per('accounts', self.name,
hostname = gajim.config.get_per('accounts', self.name,
'hostname')
id = iq_obj.getID()
if jid == hostname and id[0] == 'p':
@ -739,7 +739,7 @@ class ConnectionDisco:
if self.commandInfoQuery(con, iq_obj):
raise common.xmpp.NodeProcessed
else:
iq = iq_obj.buildReply('result')
q = iq.getTag('query')
@ -851,7 +851,7 @@ class ConnectionVcard:
if send_caps:
return self.add_caps(p)
return p
def add_caps(self, p):
''' advertise our capabilities in presence stanza (xep-0115)'''
c = p.setTag('c', namespace = common.xmpp.NS_CAPS)
@ -859,7 +859,7 @@ class ConnectionVcard:
c.setAttr('node', 'http://gajim.org')
c.setAttr('ver', gajim.caps_hash)
return p
def node_to_dict(self, node):
dict = {}
for info in node.getChildren():
@ -902,7 +902,7 @@ class ConnectionVcard:
fil.close()
except IOError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
def get_cached_vcard(self, fjid, is_fake_jid = False):
'''return the vcard as a dict
return {} if vcard was too old
@ -930,6 +930,12 @@ class ConnectionVcard:
if vcard.has_key('PHOTO'):
if not isinstance(vcard['PHOTO'], dict):
del vcard['PHOTO']
elif vcard['PHOTO'].has_key('SHA'):
cached_sha = vcard['PHOTO']['SHA']
if self.vcard_shas.has_key(jid) and self.vcard_shas[jid] != \
cached_sha:
# user change his vcard so don't use the cached one
return {}
vcard['jid'] = jid
vcard['resource'] = gajim.get_resource_from_jid(fjid)
return vcard
@ -1195,13 +1201,9 @@ class ConnectionVcard:
#('VCARD', {entry1: data, entry2: {entry21: data, ...}, ...})
self.dispatch('VCARD', vcard)
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionCaps):
# basic connection handlers used here and in zeroconf
class ConnectionHandlersBase:
def __init__(self):
ConnectionVcard.__init__(self)
ConnectionBytestream.__init__(self)
ConnectionCommands.__init__(self)
ConnectionPubSub.__init__(self)
self.gmail_url = None
# List of IDs we are waiting answers for {id: (type_of_request, data), }
self.awaiting_answers = {}
# List of IDs that will produce a timeout is answer doesn't arrive
@ -1210,43 +1212,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
# keep the jids we auto added (transports contacts) to not send the
# SUBSCRIBED event to gui
self.automatically_added = []
# keep the latest subscribed event for each jid to prevent loop when we
# acknoledge presences
self.subscribed_events = {}
# IDs of jabber:iq:last requests
self.last_ids = []
# IDs of jabber:iq:version requests
self.version_ids = []
# ID of urn:xmpp:ping requests
self.awaiting_xmpp_ping_id = None
# keep track of sessions this connection has with other JIDs
self.sessions = {}
try:
idle.init()
except:
HAS_IDLE = False
def build_http_auth_answer(self, iq_obj, answer):
if answer == 'yes':
self.connection.send(iq_obj.buildReply('result'))
elif answer == 'no':
err = common.xmpp.Error(iq_obj,
common.xmpp.protocol.ERR_NOT_AUTHORIZED)
self.connection.send(err)
def _HttpAuthCB(self, con, iq_obj):
gajim.log.debug('HttpAuthCB')
opt = gajim.config.get_per('accounts', self.name, 'http_auth')
if opt in ('yes', 'no'):
self.build_http_auth_answer(iq_obj, opt)
else:
id = iq_obj.getTagAttr('confirm', 'id')
method = iq_obj.getTagAttr('confirm', 'method')
url = iq_obj.getTagAttr('confirm', 'url')
msg = iq_obj.getTagData('body') # In case it's a message with a body
self.dispatch('HTTP_AUTH', (method, url, id, iq_obj, msg));
raise common.xmpp.NodeProcessed
def _FeatureNegCB(self, con, stanza, session):
gajim.log.debug('FeatureNegCB')
@ -1275,6 +1243,133 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
raise common.xmpp.NodeProcessed
def get_or_create_session(self, jid, thread_id):
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
pm = True
if not gajim.interface.is_pm_contact(jid, self.name):
pm = False
jid = gajim.get_jid_without_resource(jid)
session = self.find_session(jid, thread_id)
if session:
return session
if pm:
return self.make_new_session(jid, thread_id, type = 'pm')
else:
return self.make_new_session(jid, thread_id)
def find_session(self, jid, thread_id):
try:
if not thread_id:
return self.find_null_session(jid)
else:
return self.sessions[jid][thread_id]
except KeyError:
return None
def terminate_sessions(self):
'''send termination messages and delete all active sessions'''
for jid in self.sessions:
for thread_id in self.sessions[jid]:
self.sessions[jid][thread_id].terminate()
self.sessions = {}
def delete_session(self, jid, thread_id):
try:
del self.sessions[jid][thread_id]
if not self.sessions[jid]:
del self.sessions[jid]
except KeyError:
pass
def find_null_session(self, jid):
'''finds all of the sessions between us and a remote jid in which we
haven't received a thread_id yet and returns the session that we last
sent a message to.'''
sessions = self.sessions[jid].values()
# sessions that we haven't received a thread ID in
idless = filter(lambda s: not s.received_thread_id, sessions)
# filter out everything exceptthe default session type
chat_sessions = filter(lambda s: isinstance(s, ChatControlSession), idless)
if chat_sessions:
# return the session that we last sent a message in
chat_sessions.sort(key=lambda s: s.last_send)
return chat_sessions[-1]
else:
return None
def make_new_session(self, jid, thread_id=None, type='chat', klass=None):
if not klass:
klass = ChatControlSession
# determine if this session is a pm session
# if not, discard the resource
if not type == 'pm':
jid = gajim.get_jid_without_resource(jid)
sess = klass(self, common.xmpp.JID(jid), thread_id, type)
if not jid in self.sessions:
self.sessions[jid] = {}
self.sessions[jid][sess.thread_id] = sess
return sess
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionCaps, ConnectionHandlersBase):
def __init__(self):
ConnectionVcard.__init__(self)
ConnectionBytestream.__init__(self)
ConnectionCommands.__init__(self)
ConnectionPubSub.__init__(self)
ConnectionHandlersBase.__init__(self)
self.gmail_url = None
# keep the latest subscribed event for each jid to prevent loop when we
# acknowledge presences
self.subscribed_events = {}
# IDs of jabber:iq:last requests
self.last_ids = []
# IDs of jabber:iq:version requests
self.version_ids = []
# ID of urn:xmpp:ping requests
self.awaiting_xmpp_ping_id = None
try:
idle.init()
except:
HAS_IDLE = False
def build_http_auth_answer(self, iq_obj, answer):
if answer == 'yes':
self.connection.send(iq_obj.buildReply('result'))
elif answer == 'no':
err = common.xmpp.Error(iq_obj,
common.xmpp.protocol.ERR_NOT_AUTHORIZED)
self.connection.send(err)
def _HttpAuthCB(self, con, iq_obj):
gajim.log.debug('HttpAuthCB')
opt = gajim.config.get_per('accounts', self.name, 'http_auth')
if opt in ('yes', 'no'):
self.build_http_auth_answer(iq_obj, opt)
else:
id = iq_obj.getTagAttr('confirm', 'id')
method = iq_obj.getTagAttr('confirm', 'method')
url = iq_obj.getTagAttr('confirm', 'url')
msg = iq_obj.getTagData('body') # In case it's a message with a body
self.dispatch('HTTP_AUTH', (method, url, id, iq_obj, msg));
raise common.xmpp.NodeProcessed
def _ErrorCB(self, con, iq_obj):
gajim.log.debug('ErrorCB')
jid_from = helpers.get_full_jid_from_iq(iq_obj)
@ -1727,88 +1822,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('GC_INVITATION',(frm, jid_from, reason, password,
is_continued))
def get_or_create_session(self, jid, thread_id):
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
pm = True
if not gajim.interface.is_pm_contact(jid, self.name):
pm = False
jid = gajim.get_jid_without_resource(jid)
session = self.find_session(jid, thread_id)
if session:
return session
if pm:
return self.make_new_session(jid, thread_id, type = 'pm')
else:
return self.make_new_session(jid, thread_id)
def find_session(self, jid, thread_id):
try:
if not thread_id:
return self.find_null_session(jid)
else:
return self.sessions[jid][thread_id]
except KeyError:
return None
def terminate_sessions(self):
'''send termination messages and delete all active sessions'''
for jid in self.sessions:
for thread_id in self.sessions[jid]:
self.sessions[jid][thread_id].terminate()
self.sessions = {}
def delete_session(self, jid, thread_id):
try:
del self.sessions[jid][thread_id]
if not self.sessions[jid]:
del self.sessions[jid]
except KeyError:
pass
def find_null_session(self, jid):
'''finds all of the sessions between us and a remote jid in which we
haven't received a thread_id yet and returns the session that we last
sent a message to.'''
sessions = self.sessions[jid].values()
# sessions that we haven't received a thread ID in
idless = filter(lambda s: not s.received_thread_id, sessions)
# filter out everything exceptthe default session type
chat_sessions = filter(lambda s: isinstance(s, ChatControlSession), idless)
if chat_sessions:
# return the session that we last sent a message in
chat_sessions.sort(key=lambda s: s.last_send)
return chat_sessions[-1]
else:
return None
def make_new_session(self, jid, thread_id=None, type='chat', klass=None):
if not klass:
klass = ChatControlSession
# determine if this session is a pm session
# if not, discard the resource
if not type == 'pm':
jid = gajim.get_jid_without_resource(jid)
sess = klass(self, common.xmpp.JID(jid), thread_id, type)
if not jid in self.sessions:
self.sessions[jid] = {}
self.sessions[jid][sess.thread_id] = sess
return sess
def _pubsubEventCB(self, con, msg):
''' Called when we receive <message/> with pubsub event. '''
# TODO: Logging? (actually services where logging would be useful, should

View File

@ -35,6 +35,7 @@ import common.xmpp
from common import helpers
from common import gajim
from common.zeroconf import zeroconf
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible']
# kind of events we can wait for an answer
@ -48,173 +49,23 @@ except:
gajim.log.debug(_('Unable to load idle module'))
HAS_IDLE = False
from common.stanza_session import EncryptedStanzaSession
from common import connection_handlers
from session import ChatControlSession
class ConnectionVcard:
def __init__(self):
self.vcard_sha = None
self.vcard_shas = {} # sha of contacts
self.room_jids = [] # list of gc jids so that vcard are saved in a folder
class ConnectionVcard(connection_handlers.ConnectionVcard):
def add_sha(self, p, send_caps = True):
pass
def add_sha(self, p, send_caps = True):
pass
def add_caps(self, p):
pass
def node_to_dict(self, node):
dict = {}
for info in node.getChildren():
name = info.getName()
if name in ('ADR', 'TEL', 'EMAIL'): # we can have several
if not dict.has_key(name):
dict[name] = []
entry = {}
for c in info.getChildren():
entry[c.getName()] = c.getData()
dict[name].append(entry)
elif info.getChildren() == []:
dict[name] = info.getData()
else:
dict[name] = {}
for c in info.getChildren():
dict[name][c.getName()] = c.getData()
return dict
def save_vcard_to_hd(self, full_jid, card):
jid, nick = gajim.get_room_and_nick_from_fjid(full_jid)
puny_jid = helpers.sanitize_filename(jid)
path = os.path.join(gajim.VCARD_PATH, puny_jid)
if jid in self.room_jids or os.path.isdir(path):
# remove room_jid file if needed
if os.path.isfile(path):
os.remove(path)
# create folder if needed
if not os.path.isdir(path):
os.mkdir(path, 0700)
puny_nick = helpers.sanitize_filename(nick)
path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
else:
path_to_file = path
fil = open(path_to_file, 'w')
fil.write(str(card))
fil.close()
def get_cached_vcard(self, fjid, is_fake_jid = False):
'''return the vcard as a dict
return {} if vcard was too old
return None if we don't have cached vcard'''
jid, nick = gajim.get_room_and_nick_from_fjid(fjid)
puny_jid = helpers.sanitize_filename(jid)
if is_fake_jid:
puny_nick = helpers.sanitize_filename(nick)
path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
else:
path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid)
if not os.path.isfile(path_to_file):
return None
# We have the vcard cached
f = open(path_to_file)
c = f.read()
f.close()
card = common.xmpp.Node(node = c)
vcard = self.node_to_dict(card)
if vcard.has_key('PHOTO'):
if not isinstance(vcard['PHOTO'], dict):
del vcard['PHOTO']
elif vcard['PHOTO'].has_key('SHA'):
cached_sha = vcard['PHOTO']['SHA']
if self.vcard_shas.has_key(jid) and self.vcard_shas[jid] != \
cached_sha:
# user change his vcard so don't use the cached one
return {}
vcard['jid'] = jid
vcard['resource'] = gajim.get_resource_from_jid(fjid)
return vcard
def request_vcard(self, jid = None, is_fake_jid = False):
pass
def send_vcard(self, vcard):
pass
class ConnectionBytestream:
def __init__(self):
self.files_props = {}
def is_transfer_stopped(self, file_props):
if file_props.has_key('error') and file_props['error'] != 0:
return True
if file_props.has_key('completed') and file_props['completed']:
return True
if file_props.has_key('connected') and file_props['connected'] == False:
return True
if not file_props.has_key('stopped') or not file_props['stopped']:
return False
return True
def send_success_connect_reply(self, streamhost):
''' send reply to the initiator of FT that we
made a connection
'''
if streamhost is None:
return None
iq = common.xmpp.Iq(to = streamhost['initiator'], typ = 'result',
frm = streamhost['target'])
iq.setAttr('id', streamhost['id'])
query = iq.setTag('query')
query.setNamespace(common.xmpp.NS_BYTESTREAM)
stream_tag = query.setTag('streamhost-used')
stream_tag.setAttr('jid', streamhost['jid'])
self.connection.send(iq)
def remove_transfers_for_contact(self, contact):
''' stop all active transfer for contact '''
for file_props in self.files_props.values():
if self.is_transfer_stopped(file_props):
continue
receiver_jid = unicode(file_props['receiver']).split('/')[0]
if contact.jid == receiver_jid:
file_props['error'] = -5
self.remove_transfer(file_props)
self.dispatch('FILE_REQUEST_ERROR', (contact.jid, file_props, ''))
sender_jid = unicode(file_props['sender'])
if contact.jid == sender_jid:
file_props['error'] = -3
self.remove_transfer(file_props)
def remove_all_transfers(self):
''' stops and removes all active connections from the socks5 pool '''
for file_props in self.files_props.values():
self.remove_transfer(file_props, remove_from_list = False)
del(self.files_props)
self.files_props = {}
def remove_transfer(self, file_props, remove_from_list = True):
if file_props is None:
return
self.disconnect_transfer(file_props)
sid = file_props['sid']
gajim.socks5queue.remove_file_props(self.name, sid)
if remove_from_list:
if self.files_props.has_key('sid'):
del(self.files_props['sid'])
def disconnect_transfer(self, file_props):
if file_props is None:
return
if file_props.has_key('hash'):
gajim.socks5queue.remove_sender(file_props['hash'])
if file_props.has_key('streamhosts'):
for host in file_props['streamhosts']:
if host.has_key('idx') and host['idx'] > 0:
gajim.socks5queue.remove_receiver(host['idx'])
gajim.socks5queue.remove_sender(host['idx'])
class ConnectionBytestream(connection_handlers.ConnectionBytestream):
def send_socks5_info(self, file_props, fast = True, receiver = None,
sender = None):
''' send iq for the present streamhosts and proxies '''
@ -274,43 +125,6 @@ class ConnectionBytestream:
streamhost.setAttr('jid', sender)
self.connection.send(iq)
def send_file_rejection(self, file_props):
''' informs sender that we refuse to download the file '''
# user response to ConfirmationDialog may come after we've disconneted
if not self.connection or self.connected < 2:
return
iq = common.xmpp.Protocol(name = 'iq',
to = unicode(file_props['sender']), typ = 'error')
iq.setAttr('id', file_props['request-id'])
err = common.xmpp.ErrorNode(code = '403', typ = 'cancel', name =
'forbidden', text = 'Offer Declined')
iq.addChild(node=err)
self.connection.send(iq)
def send_file_approval(self, file_props):
''' send iq, confirming that we want to download the file '''
# user response to ConfirmationDialog may come after we've disconneted
if not self.connection or self.connected < 2:
return
iq = common.xmpp.Protocol(name = 'iq',
to = unicode(file_props['sender']), typ = 'result')
iq.setAttr('id', file_props['request-id'])
si = iq.setTag('si')
si.setNamespace(common.xmpp.NS_SI)
if file_props.has_key('offset') and file_props['offset']:
file_tag = si.setTag('file')
file_tag.setNamespace(common.xmpp.NS_FILE)
range_tag = file_tag.setTag('range')
range_tag.setAttr('offset', file_props['offset'])
feature = si.setTag('feature')
feature.setNamespace(common.xmpp.NS_FEATURE)
_feature = common.xmpp.DataForm(typ='submit')
feature.addChild(node=_feature)
field = _feature.setField('stream-method')
field.delAttr('type')
field.setValue(common.xmpp.NS_BYTESTREAM)
self.connection.send(iq)
def send_file_request(self, file_props):
''' send iq for new FT request '''
if not self.connection or self.connected < 2:
@ -318,7 +132,7 @@ class ConnectionBytestream:
our_jid = gajim.get_jid_from_account(self.name)
frm = our_jid
file_props['sender'] = frm
fjid = file_props['receiver'].jid
fjid = file_props['receiver'].jid
iq = common.xmpp.Protocol(name = 'iq', to = fjid,
typ = 'set')
iq.setID(file_props['sid'])
@ -343,70 +157,7 @@ class ConnectionBytestream:
field.setAttr('type', 'list-single')
field.addOption(common.xmpp.NS_BYTESTREAM)
self.connection.send(iq)
def _result_socks5_sid(self, sid, hash_id):
''' store the result of sha message from auth. '''
if not self.files_props.has_key(sid):
return
file_props = self.files_props[sid]
file_props['hash'] = hash_id
return
def _connect_error(self, to, _id, sid, code = 404):
''' cb, when there is an error establishing BS connection, or
when connection is rejected'''
msg_dict = {
404: 'Could not connect to given hosts',
405: 'Cancel',
406: 'Not acceptable',
}
msg = msg_dict[code]
iq = None
iq = common.xmpp.Protocol(name = 'iq', to = to,
typ = 'error')
iq.setAttr('id', _id)
err = iq.setTag('error')
err.setAttr('code', unicode(code))
err.setData(msg)
self.connection.send(iq)
if code == 404:
file_props = gajim.socks5queue.get_file_props(self.name, sid)
if file_props is not None:
self.disconnect_transfer(file_props)
file_props['error'] = -3
self.dispatch('FILE_REQUEST_ERROR', (to, file_props, msg))
def _proxy_auth_ok(self, proxy):
'''cb, called after authentication to proxy server '''
file_props = self.files_props[proxy['sid']]
iq = common.xmpp.Protocol(name = 'iq', to = proxy['initiator'],
typ = 'set')
auth_id = "au_" + proxy['sid']
iq.setID(auth_id)
query = iq.setTag('query')
query.setNamespace(common.xmpp.NS_BYTESTREAM)
query.setAttr('sid', proxy['sid'])
activate = query.setTag('activate')
activate.setData(file_props['proxy_receiver'])
iq.setID(auth_id)
self.connection.send(iq)
# register xmpppy handlers for bytestream and FT stanzas
def _bytestreamErrorCB(self, con, iq_obj):
gajim.log.debug('_bytestreamErrorCB')
id = unicode(iq_obj.getAttr('id'))
frm = unicode(iq_obj.getFrom())
query = iq_obj.getTag('query')
gajim.proxy65_manager.error_cb(frm, query)
jid = unicode(iq_obj.getFrom())
id = id[3:]
if not self.files_props.has_key(id):
return
file_props = self.files_props[id]
file_props['error'] = -4
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
raise common.xmpp.NodeProcessed
def _bytestreamSetCB(self, con, iq_obj):
gajim.log.debug('_bytestreamSetCB')
target = unicode(iq_obj.getAttr('to'))
@ -432,7 +183,7 @@ class ConnectionBytestream:
if self.files_props.has_key(sid):
file_props = self.files_props[sid]
file_props['fast'] = streamhosts
if file_props['type'] == 's':
if file_props['type'] == 's':
if file_props.has_key('streamhosts'):
file_props['streamhosts'].extend(streamhosts)
else:
@ -465,14 +216,14 @@ class ConnectionBytestream:
if host['initiator'] == frm and host.has_key('idx'):
gajim.socks5queue.activate_proxy(host['idx'])
raise common.xmpp.NodeProcessed
def _bytestreamResultCB(self, con, iq_obj):
gajim.log.debug('_bytestreamResultCB')
frm = unicode(iq_obj.getFrom())
real_id = unicode(iq_obj.getAttr('id'))
query = iq_obj.getTag('query')
gajim.proxy65_manager.resolve_result(frm, query)
try:
streamhost = query.getTag('streamhost-used')
except: # this bytestream result is not what we need
@ -531,9 +282,9 @@ class ConnectionBytestream:
if len(fasts) > 0:
self._connect_error(frm, fasts[0]['id'], file_props['sid'],
code = 406)
raise common.xmpp.NodeProcessed
def _siResultCB(self, con, iq_obj):
gajim.log.debug('_siResultCB')
self.peerhost = con._owner.Connection._sock.getsockname()
@ -571,7 +322,7 @@ class ConnectionBytestream:
return
self.send_socks5_info(file_props, fast = True)
raise common.xmpp.NodeProcessed
def _siSetCB(self, con, iq_obj):
gajim.log.debug('_siSetCB')
jid = unicode(iq_obj.getFrom())
@ -595,7 +346,7 @@ class ConnectionBytestream:
if mime_type is not None:
file_props['mime-type'] = mime_type
our_jid = gajim.get_jid_from_account(self.name)
file_props['receiver'] = our_jid
file_props['receiver'] = our_jid
file_props['sender'] = unicode(iq_obj.getFrom())
file_props['request-id'] = unicode(iq_obj.getAttr('id'))
file_props['sid'] = unicode(si.getAttr('id'))
@ -623,320 +374,100 @@ class ConnectionBytestream:
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
raise common.xmpp.NodeProcessed
class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream):
class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream, connection_handlers.ConnectionHandlersBase):
def __init__(self):
ConnectionVcard.__init__(self)
ConnectionBytestream.__init__(self)
# List of IDs we are waiting answers for {id: (type_of_request, data), }
self.awaiting_answers = {}
# List of IDs that will produce a timeout is answer doesn't arrive
# {time_of_the_timeout: (id, message to send to gui), }
self.awaiting_timeouts = {}
# keep the jids we auto added (transports contacts) to not send the
# SUBSCRIBED event to gui
self.automatically_added = []
# keep track of sessions this connection has with other JIDs
self.sessions = {}
connection_handlers.ConnectionHandlersBase.__init__(self)
try:
idle.init()
except:
HAS_IDLE = False
def _messageCB(self, ip, con, msg):
'''Called when we receive a message'''
gajim.log.debug('Zeroconf MessageCB')
frm = msg.getFrom()
mtype = msg.getType()
thread_id = msg.getThread()
tim = msg.getTimestamp()
tim = helpers.datetime_tuple(tim)
tim = time.localtime(timegm(tim))
frm = msg.getFrom()
if not mtype:
mtype = 'normal'
if frm == None:
for key in self.connection.zeroconf.contacts:
if ip == self.connection.zeroconf.contacts[key][zeroconf.C_ADDRESS]:
frm = key
frm = unicode(frm)
jid = frm
session = self.get_or_create_session(frm, thread_id, mtype)
frm = unicode(frm)
session = self.get_or_create_session(frm, thread_id)
if thread_id and not session.received_thread_id:
session.received_thread_id = True
if msg.getTag('feature') and msg.getTag('feature').namespace == \
common.xmpp.NS_FEATURE:
if gajim.HAVE_PYCRYPTO:
self._FeatureNegCB(con, msg, session)
return
if msg.getTag('init') and msg.getTag('init').namespace == \
common.xmpp.NS_ESESSION_INIT:
self._InitE2ECB(con, msg, session)
no_log_for = gajim.config.get_per('accounts', self.name,
'no_log_for').split()
encrypted = False
chatstate = None
tim = msg.getTimestamp()
tim = helpers.datetime_tuple(tim)
tim = time.localtime(timegm(tim))
e2e_tag = msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO)
if e2e_tag:
if msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO):
encrypted = True
try:
msg = session.decrypt_stanza(msg)
except:
self.dispatch('FAILED_DECRYPT', (frm, tim))
msgtxt = msg.getBody()
msghtml = msg.getXHTML()
subject = msg.getSubject() # if not there, it's None
encTag = msg.getTag('x', namespace = common.xmpp.NS_ENCRYPTED)
decmsg = ''
form_node = msg.getTag('x', namespace = common.xmpp.NS_DATA)
# invitations
invite = None
encTag = msg.getTag('x', namespace = common.xmpp.NS_ENCRYPTED)
if not encTag:
invite = msg.getTag('x', namespace = common.xmpp.NS_MUC_USER)
if invite and not invite.getTag('invite'):
invite = None
delayed = msg.getTag('x', namespace = common.xmpp.NS_DELAY) != None
msg_id = None
composing_xep = None
xtags = msg.getTags('x')
# chatstates - look for chatstate tags in a message if not delayed
if not delayed:
composing_xep = False
children = msg.getChildren()
for child in children:
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
chatstate = child.getName()
composing_xep = 'XEP-0085'
break
# No JEP-0085 support, fallback to JEP-0022
if not chatstate:
chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT)
if chatstate_child:
chatstate = 'active'
composing_xep = 'XEP-0022'
if not msgtxt and chatstate_child.getTag('composing'):
chatstate = 'composing'
# JEP-0172 User Nickname
user_nick = msg.getTagData('nick')
if not user_nick:
user_nick = ''
if encTag and self.USE_GPG:
#decrypt
encmsg = encTag.getData()
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if keyID:
decmsg = self.gpg.decrypt(encmsg, keyID)
# \x00 chars are not allowed in C (so in GTK)
decmsg = decmsg.replace('\x00', '')
if decmsg:
msgtxt = decmsg
encrypted = True
msgtxt = decmsg.replace('\x00', '')
encrypted = True
if mtype == 'error':
error_msg = msg.getError()
if not error_msg:
error_msg = msgtxt
msgtxt = None
if self.name not in no_log_for:
gajim.logger.write('error', frm, error_msg, tim = tim,
subject = subject)
self.dispatch('MSGERROR', (frm, msg.getErrorCode(), error_msg, msgtxt,
tim))
elif mtype == 'chat': # it's type 'chat'
if not msg.getTag('body') and chatstate is None: #no <body>
return
if msg.getTag('body') and self.name not in no_log_for and jid not in\
no_log_for and msgtxt:
msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt, tim = tim,
subject = subject)
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype, subject,
chatstate, msg_id, composing_xep, user_nick, msghtml, session,
form_node))
elif mtype == 'normal': # it's single message
if self.name not in no_log_for and jid not in no_log_for and msgtxt:
gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim,
subject = subject)
if invite:
self.dispatch('MSG', (frm, msgtxt, tim, encrypted, 'normal',
subject, chatstate, msg_id, composing_xep, user_nick, msghtml,
session, form_node))
# END messageCB
def _FeatureNegCB(self, con, stanza, session):
gajim.log.debug('FeatureNegCB')
feature = stanza.getTag(name='feature', namespace=common.xmpp.NS_FEATURE)
form = common.xmpp.DataForm(node=feature.getTag('x'))
if form['FORM_TYPE'] == 'urn:xmpp:ssn':
self.dispatch('SESSION_NEG', (stanza.getFrom(), session, form))
self.dispatch_error_msg(msg, msgtxt, session, frm, tim, subject)
else:
reply = stanza.buildReply()
reply.setType('error')
reply.addChild(feature)
reply.addChild(node=xmpp.ErrorNode('service-unavailable', typ='cancel'))
con.send(reply)
raise common.xmpp.NodeProcessed
def _InitE2ECB(self, con, stanza, session):
gajim.log.debug('InitE2ECB')
init = stanza.getTag(name='init', namespace=common.xmpp.NS_ESESSION_INIT)
form = common.xmpp.DataForm(node=init.getTag('x'))
self.dispatch('SESSION_NEG', (stanza.getFrom(), session, form))
raise common.xmpp.NodeProcessed
def terminate_sessions(self):
'''send termination messages and delete all active sessions'''
# XXX
pass
def get_or_create_session(self, jid, thread_id, type):
'''returns an existing session between this connection and 'jid', returns a new one if none exist.'''
session = self.find_session(jid, thread_id, type)
if session:
return session
else:
# it's possible we initiated a session with a bare JID and this is the
# first time we've seen a resource
bare_jid = gajim.get_jid_without_resource(jid)
if bare_jid != jid:
session = self.find_session(bare_jid, thread_id, type)
if session:
if not session.received_thread_id:
thread_id = session.thread_id
self.move_session(bare_jid, thread_id, jid.split("/")[1])
return session
return self.make_new_session(jid, thread_id, type)
def find_session(self, jid, thread_id, type):
try:
if type == 'chat' and not thread_id:
return self.find_null_session(jid)
# XXX this shouldn't be hardcoded
if isinstance(session, ChatControlSession):
session.received(frm, msgtxt, tim, encrypted, subject, msg)
else:
return self.sessions[jid][thread_id]
except KeyError:
return None
session.received(msg)
# END messageCB
def delete_session(self, jid, thread_id):
try:
del self.sessions[jid][thread_id]
if not self.sessions[jid]:
del self.sessions[jid]
except KeyError:
print "jid %s should have been in %s, but it wasn't. missing session?" % (repr(jid), repr(self.sessions.keys()))
def move_session(self, original_jid, thread_id, to_resource):
'''moves a session to another resource.'''
session = self.sessions[original_jid][thread_id]
del self.sessions[original_jid][thread_id]
new_jid = gajim.get_jid_without_resource(original_jid) + '/' + to_resource
session.jid = common.xmpp.JID(new_jid)
if not new_jid in self.sessions:
self.sessions[new_jid] = {}
self.sessions[new_jid][thread_id] = session
def find_null_session(self, jid):
'''finds all of the sessions between us and jid that jid hasn't sent a thread_id in yet.
returns the session that we last sent a message to.'''
sessions_with_jid = self.sessions[jid].values()
no_threadid_sessions = filter(lambda s: not s.received_thread_id, sessions_with_jid)
if no_threadid_sessions:
no_threadid_sessions.sort(key=lambda s: s.last_send)
return no_threadid_sessions[-1]
else:
return None
def make_new_session(self, jid, thread_id = None, type = 'chat'):
sess = EncryptedStanzaSession(self, common.xmpp.JID(jid), thread_id, type)
if not jid in self.sessions:
self.sessions[jid] = {}
self.sessions[jid][sess.thread_id] = sess
return sess
def parse_data_form(self, node):
dic = {}
tag = node.getTag('title')
if tag:
dic['title'] = tag.getData()
tag = node.getTag('instructions')
if tag:
dic['instructions'] = tag.getData()
i = 0
for child in node.getChildren():
if child.getName() != 'field':
continue
var = child.getAttr('var')
ctype = child.getAttr('type')
label = child.getAttr('label')
if not var and ctype != 'fixed': # We must have var if type != fixed
continue
dic[i] = {}
if var:
dic[i]['var'] = var
if ctype:
dic[i]['type'] = ctype
if label:
dic[i]['label'] = label
tags = child.getTags('value')
if len(tags):
dic[i]['values'] = []
for tag in tags:
data = tag.getData()
if ctype == 'boolean':
if data in ('yes', 'true', 'assent', '1'):
data = True
else:
data = False
dic[i]['values'].append(data)
tag = child.getTag('desc')
if tag:
dic[i]['desc'] = tag.getData()
option_tags = child.getTags('option')
if len(option_tags):
dic[i]['options'] = {}
j = 0
for option_tag in option_tags:
dic[i]['options'][j] = {}
label = option_tag.getAttr('label')
tags = option_tag.getTags('value')
dic[i]['options'][j]['values'] = []
for tag in tags:
dic[i]['options'][j]['values'].append(tag.getData())
if not label:
label = dic[i]['options'][j]['values'][0]
dic[i]['options'][j]['label'] = label
j += 1
if not dic[i].has_key('values'):
dic[i]['values'] = [dic[i]['options'][0]['values'][0]]
i += 1
return dic
def store_metacontacts(self, tags):
''' fake empty method '''
# serverside metacontacts are not supported with zeroconf
# serverside metacontacts are not supported with zeroconf
# (there is no server)
pass

View File

@ -196,7 +196,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
return self.call_resolve_timeout
# callbacks called from zeroconf
def _on_new_service(self,jid):
def _on_new_service(self, jid):
self.roster.setItem(jid)
self.dispatch('ROSTER_INFO', (jid, self.roster.getName(jid), 'both', 'no', self.roster.getGroups(jid)))
self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), self.roster.getMessage(jid), 'local', 0, None, 0, None))
@ -362,7 +362,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
def send_message(self, jid, msg, keyID, type = 'chat', subject='',
chatstate = None, msg_id = None, composing_xep = None, resource = None,
user_nick = None, session=None):
user_nick = None, session=None, forward_from=None, form_node=None, original_message=None):
fjid = jid
if not self.connection:
@ -454,13 +454,13 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
def on_send_not_ok(reason):
reason += ' ' + _('Your message could not be sent.')
self.dispatch('MSGERROR', [jid, '-1', reason, None, None])
self.dispatch('MSGERROR', [jid, '-1', reason, None, None, session])
ret = self.connection.send(msg_iq, msg != None, on_ok=on_send_ok,
on_not_ok=on_send_not_ok)
if ret == -1:
# Contact Offline
self.dispatch('MSGERROR', [jid, '-1', _('Contact is offline. Your message could not be sent.'), None, None])
self.dispatch('MSGERROR', [jid, '-1', _('Contact is offline. Your message could not be sent.'), None, None, session])
def send_stanza(self, stanza):
# send a stanza untouched