Add connect_maschine()
Add method that sequentially works the steps we have to do before sending first presence - Move Delimiter into own module - Move Metacontacts into own module
This commit is contained in:
parent
db77fa1ace
commit
5a6f03dea4
|
@ -415,7 +415,9 @@ def account_is_zeroconf(account):
|
||||||
return connections[account].is_zeroconf
|
return connections[account].is_zeroconf
|
||||||
|
|
||||||
def account_supports_private_storage(account):
|
def account_supports_private_storage(account):
|
||||||
return connections[account].private_storage_supported
|
# If Delimiter module is not available we can assume
|
||||||
|
# Private Storage is not available
|
||||||
|
return connections[account].get_module('Delimiter').available
|
||||||
|
|
||||||
def account_is_connected(account):
|
def account_is_connected(account):
|
||||||
if account not in connections:
|
if account not in connections:
|
||||||
|
|
|
@ -115,7 +115,6 @@ class CommonConnection:
|
||||||
# the fake jid
|
# the fake jid
|
||||||
self.groupchat_jids = {} # {ID : groupchat_jid}
|
self.groupchat_jids = {} # {ID : groupchat_jid}
|
||||||
|
|
||||||
self.private_storage_supported = False
|
|
||||||
self.roster_supported = True
|
self.roster_supported = True
|
||||||
self.addressing_supported = False
|
self.addressing_supported = False
|
||||||
|
|
||||||
|
@ -124,7 +123,8 @@ class CommonConnection:
|
||||||
|
|
||||||
self.awaiting_cids = {} # Used for XEP-0231
|
self.awaiting_cids = {} # Used for XEP-0231
|
||||||
|
|
||||||
self.nested_group_delimiter = '::'
|
# Tracks the calls of the connect_maschine() method
|
||||||
|
self._connect_maschine_calls = 0
|
||||||
|
|
||||||
self.get_config_values_or_default()
|
self.get_config_values_or_default()
|
||||||
|
|
||||||
|
@ -412,12 +412,6 @@ class CommonConnection:
|
||||||
def account_changed(self, new_name):
|
def account_changed(self, new_name):
|
||||||
self.name = new_name
|
self.name = new_name
|
||||||
|
|
||||||
def get_metacontacts(self):
|
|
||||||
"""
|
|
||||||
To be implemented by derived classes
|
|
||||||
"""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def send_agent_status(self, agent, ptype):
|
def send_agent_status(self, agent, ptype):
|
||||||
"""
|
"""
|
||||||
To be implemented by derived classes
|
To be implemented by derived classes
|
||||||
|
@ -546,8 +540,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
self.retrycount = 0
|
self.retrycount = 0
|
||||||
self.available_transports = {} # list of available transports on this
|
self.available_transports = {} # list of available transports on this
|
||||||
# server {'icq': ['icq.server.com', 'icq2.server.com'], }
|
# server {'icq': ['icq.server.com', 'icq2.server.com'], }
|
||||||
self.private_storage_supported = True
|
|
||||||
self.privacy_rules_requested = False
|
|
||||||
self.streamError = ''
|
self.streamError = ''
|
||||||
self.secret_hmac = str(random.random())[2:].encode('utf-8')
|
self.secret_hmac = str(random.random())[2:].encode('utf-8')
|
||||||
self.removing_account = False
|
self.removing_account = False
|
||||||
|
@ -644,7 +637,6 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
self.on_purpose = on_purpose
|
self.on_purpose = on_purpose
|
||||||
self.connected = 0
|
self.connected = 0
|
||||||
self.time_to_reconnect = None
|
self.time_to_reconnect = None
|
||||||
self.get_module('PrivacyLists').supported = False
|
|
||||||
self.get_module('VCardAvatars').avatar_advertised = False
|
self.get_module('VCardAvatars').avatar_advertised = False
|
||||||
if on_purpose:
|
if on_purpose:
|
||||||
self.sm = Smacks(self)
|
self.sm = Smacks(self)
|
||||||
|
@ -1427,6 +1419,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
# Get annotations
|
# Get annotations
|
||||||
self.get_module('Annotations').get_annotations()
|
self.get_module('Annotations').get_annotations()
|
||||||
|
|
||||||
|
# Blocking
|
||||||
|
self.get_module('Blocking').get_blocking_list()
|
||||||
|
|
||||||
# Inform GUI we just signed in
|
# Inform GUI we just signed in
|
||||||
app.nec.push_incoming_event(SignedInEvent(None, conn=self))
|
app.nec.push_incoming_event(SignedInEvent(None, conn=self))
|
||||||
|
|
||||||
|
@ -1455,60 +1450,39 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
self.pingalives, self.get_module('Ping').send_keepalive_ping)
|
self.pingalives, self.get_module('Ping').send_keepalive_ping)
|
||||||
self.connection.onreceive(None)
|
self.connection.onreceive(None)
|
||||||
|
|
||||||
self.privacy_rules_requested = False
|
|
||||||
|
|
||||||
# If we are not resuming, we ask for discovery info
|
# If we are not resuming, we ask for discovery info
|
||||||
# and archiving preferences
|
# and archiving preferences
|
||||||
if not self.sm.supports_sm or (not self.sm.resuming and self.sm.enabled):
|
if not self.sm.supports_sm or (not self.sm.resuming and self.sm.enabled):
|
||||||
our_server = app.config.get_per('accounts', self.name, 'hostname')
|
# This starts the connect_maschine
|
||||||
self.get_module('Discovery').discover_account_info()
|
|
||||||
self.get_module('Discovery').discover_server_info()
|
self.get_module('Discovery').discover_server_info()
|
||||||
else:
|
self.get_module('Discovery').discover_account_info()
|
||||||
self.request_roster(resume=True)
|
|
||||||
|
|
||||||
self.sm.resuming = False # back to previous state
|
self.sm.resuming = False # back to previous state
|
||||||
# Discover Stun server(s)
|
# Discover Stun server(s)
|
||||||
if self._proxy is None:
|
if self._proxy is None:
|
||||||
hostname = app.config.get_per('accounts', self.name, 'hostname')
|
hostname = app.config.get_per('accounts', self.name, 'hostname')
|
||||||
app.resolver.resolve('_stun._udp.' + helpers.idn_to_ascii(hostname),
|
app.resolver.resolve(
|
||||||
self._on_stun_resolved)
|
'_stun._udp.' + helpers.idn_to_ascii(hostname),
|
||||||
|
self._on_stun_resolved)
|
||||||
|
|
||||||
def _on_stun_resolved(self, host, result_array):
|
def _on_stun_resolved(self, host, result_array):
|
||||||
if len(result_array) != 0:
|
if len(result_array) != 0:
|
||||||
self._stun_servers = self._hosts = [i for i in result_array]
|
self._stun_servers = self._hosts = [i for i in result_array]
|
||||||
|
|
||||||
def _continue_connection_request_privacy(self):
|
@helpers.call_counter
|
||||||
if self.get_module('PrivacyLists').supported:
|
def connect_maschine(self, restart=False):
|
||||||
if not self.privacy_rules_requested:
|
log.info('Connect maschine state: %s', self._connect_maschine_calls)
|
||||||
self.privacy_rules_requested = True
|
if self._connect_maschine_calls == 1:
|
||||||
self.get_module('PrivacyLists').get_privacy_lists(
|
self.get_module('MetaContacts').get_metacontacts()
|
||||||
self._received_privacy)
|
elif self._connect_maschine_calls == 2:
|
||||||
else:
|
self.get_module('Delimiter').get_roster_delimiter()
|
||||||
# Privacy lists not supported
|
elif self._connect_maschine_calls == 3:
|
||||||
log.info('Privacy Lists not supported')
|
self.get_module('Roster').request_roster()
|
||||||
self._received_privacy(False)
|
elif self._connect_maschine_calls == 4:
|
||||||
|
self.send_first_presence()
|
||||||
def _received_privacy(self, result):
|
|
||||||
if not result:
|
|
||||||
if (self.continue_connect_info and
|
|
||||||
self.continue_connect_info[0] == 'invisible'):
|
|
||||||
# Trying to login as invisible but privacy list not
|
|
||||||
# supported
|
|
||||||
self.disconnect(on_purpose=True)
|
|
||||||
app.nec.push_incoming_event(OurShowEvent(
|
|
||||||
None, conn=self, show='offline'))
|
|
||||||
app.nec.push_incoming_event(InformationEvent(
|
|
||||||
None, dialog_name='invisibility-not-supported',
|
|
||||||
args=self.name))
|
|
||||||
return
|
|
||||||
|
|
||||||
self.get_module('Blocking').get_blocking_list()
|
|
||||||
|
|
||||||
# Ask metacontacts before roster
|
|
||||||
self.get_metacontacts()
|
|
||||||
|
|
||||||
def send_custom_status(self, show, msg, jid):
|
def send_custom_status(self, show, msg, jid):
|
||||||
if not show in app.SHOW_LIST:
|
if show not in app.SHOW_LIST:
|
||||||
return -1
|
return -1
|
||||||
if not app.account_is_connected(self.name):
|
if not app.account_is_connected(self.name):
|
||||||
return
|
return
|
||||||
|
@ -1677,85 +1651,9 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
query.setTagData('prompt', prompt)
|
query.setTagData('prompt', prompt)
|
||||||
self.connection.SendAndCallForResponse(iq, _on_prompt_result)
|
self.connection.SendAndCallForResponse(iq, _on_prompt_result)
|
||||||
|
|
||||||
def bookmarks_available(self):
|
|
||||||
if self.private_storage_supported:
|
|
||||||
return True
|
|
||||||
if self.get_module('PubSub').publish_options:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_roster_delimiter(self):
|
|
||||||
"""
|
|
||||||
Get roster group delimiter from storage as described in XEP 0083
|
|
||||||
"""
|
|
||||||
if not app.account_is_connected(self.name):
|
|
||||||
return
|
|
||||||
iq = nbxmpp.Iq(typ='get')
|
|
||||||
iq2 = iq.addChild(name='query', namespace=nbxmpp.NS_PRIVATE)
|
|
||||||
iq2.addChild(name='roster', namespace='roster:delimiter')
|
|
||||||
id_ = self.connection.getAnID()
|
|
||||||
iq.setID(id_)
|
|
||||||
self.awaiting_answers[id_] = (DELIMITER_ARRIVED, )
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
def set_roster_delimiter(self, delimiter='::'):
|
|
||||||
"""
|
|
||||||
Set roster group delimiter to the storage namespace
|
|
||||||
"""
|
|
||||||
if not app.account_is_connected(self.name):
|
|
||||||
return
|
|
||||||
iq = nbxmpp.Iq(typ='set')
|
|
||||||
iq2 = iq.addChild(name='query', namespace=nbxmpp.NS_PRIVATE)
|
|
||||||
iq3 = iq2.addChild(name='roster', namespace='roster:delimiter')
|
|
||||||
iq3.setData(delimiter)
|
|
||||||
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
def get_metacontacts(self):
|
|
||||||
"""
|
|
||||||
Get metacontacts list from storage as described in XEP 0049
|
|
||||||
"""
|
|
||||||
if not app.account_is_connected(self.name):
|
|
||||||
return
|
|
||||||
iq = nbxmpp.Iq(typ='get')
|
|
||||||
iq2 = iq.addChild(name='query', namespace=nbxmpp.NS_PRIVATE)
|
|
||||||
iq2.addChild(name='storage', namespace='storage:metacontacts')
|
|
||||||
id_ = self.connection.getAnID()
|
|
||||||
iq.setID(id_)
|
|
||||||
self.awaiting_answers[id_] = (METACONTACTS_ARRIVED, )
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
def store_metacontacts(self, tags_list):
|
|
||||||
"""
|
|
||||||
Send meta contacts to the storage namespace
|
|
||||||
"""
|
|
||||||
if not app.account_is_connected(self.name):
|
|
||||||
return
|
|
||||||
iq = nbxmpp.Iq(typ='set')
|
|
||||||
iq2 = iq.addChild(name='query', namespace=nbxmpp.NS_PRIVATE)
|
|
||||||
iq3 = iq2.addChild(name='storage', namespace='storage:metacontacts')
|
|
||||||
for tag in tags_list:
|
|
||||||
for data in tags_list[tag]:
|
|
||||||
jid = data['jid']
|
|
||||||
dict_ = {'jid': jid, 'tag': tag}
|
|
||||||
if 'order' in data:
|
|
||||||
dict_['order'] = data['order']
|
|
||||||
iq3.addChild(name='meta', attrs=dict_)
|
|
||||||
self.connection.send(iq)
|
|
||||||
|
|
||||||
def getRoster(self):
|
def getRoster(self):
|
||||||
return self.get_module('Roster')
|
return self.get_module('Roster')
|
||||||
|
|
||||||
def request_roster(self, resume=False):
|
|
||||||
version = None
|
|
||||||
features = self.connection.Dispatcher.Stream.features
|
|
||||||
if features and features.getTag('ver', namespace=nbxmpp.NS_ROSTER_VER):
|
|
||||||
version = app.config.get_per(
|
|
||||||
'accounts', self.name, 'roster_version')
|
|
||||||
|
|
||||||
if not resume:
|
|
||||||
self.get_module('Roster').request_roster(version)
|
|
||||||
|
|
||||||
def send_agent_status(self, agent, ptype):
|
def send_agent_status(self, agent, ptype):
|
||||||
if not app.account_is_connected(self.name):
|
if not app.account_is_connected(self.name):
|
||||||
return
|
return
|
||||||
|
|
|
@ -53,10 +53,6 @@ log = logging.getLogger('gajim.c.connection_handlers')
|
||||||
|
|
||||||
# kind of events we can wait for an answer
|
# kind of events we can wait for an answer
|
||||||
AGENT_REMOVED = 'agent_removed'
|
AGENT_REMOVED = 'agent_removed'
|
||||||
METACONTACTS_ARRIVED = 'metacontacts_arrived'
|
|
||||||
ROSTER_ARRIVED = 'roster_arrived'
|
|
||||||
DELIMITER_ARRIVED = 'delimiter_arrived'
|
|
||||||
PRIVACY_ARRIVED = 'privacy_arrived'
|
|
||||||
|
|
||||||
|
|
||||||
class ConnectionDisco:
|
class ConnectionDisco:
|
||||||
|
@ -518,35 +514,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco,
|
||||||
app.nec.push_incoming_event(AgentRemovedEvent(None, conn=self,
|
app.nec.push_incoming_event(AgentRemovedEvent(None, conn=self,
|
||||||
agent=jid))
|
agent=jid))
|
||||||
del self.awaiting_answers[id_]
|
del self.awaiting_answers[id_]
|
||||||
elif self.awaiting_answers[id_][0] == METACONTACTS_ARRIVED:
|
|
||||||
if not self.connection:
|
|
||||||
return
|
|
||||||
if iq_obj.getType() == 'result':
|
|
||||||
app.nec.push_incoming_event(MetacontactsReceivedEvent(None,
|
|
||||||
conn=self, stanza=iq_obj))
|
|
||||||
else:
|
|
||||||
if iq_obj.getErrorCode() not in ('403', '406', '404'):
|
|
||||||
self.private_storage_supported = False
|
|
||||||
self.get_roster_delimiter()
|
|
||||||
del self.awaiting_answers[id_]
|
|
||||||
elif self.awaiting_answers[id_][0] == DELIMITER_ARRIVED:
|
|
||||||
del self.awaiting_answers[id_]
|
|
||||||
if not self.connection:
|
|
||||||
return
|
|
||||||
if iq_obj.getType() == 'result':
|
|
||||||
query = iq_obj.getTag('query')
|
|
||||||
if not query:
|
|
||||||
return
|
|
||||||
delimiter = query.getTagData('roster')
|
|
||||||
if delimiter:
|
|
||||||
self.nested_group_delimiter = delimiter
|
|
||||||
else:
|
|
||||||
self.set_roster_delimiter('::')
|
|
||||||
else:
|
|
||||||
self.private_storage_supported = False
|
|
||||||
|
|
||||||
# We can now continue connection by requesting the roster
|
|
||||||
self.request_roster()
|
|
||||||
|
|
||||||
def _dispatch_gc_msg_with_captcha(self, stanza, msg_obj):
|
def _dispatch_gc_msg_with_captcha(self, stanza, msg_obj):
|
||||||
msg_obj.stanza = stanza
|
msg_obj.stanza = stanza
|
||||||
|
|
|
@ -852,37 +852,6 @@ class UpdateRoomAvatarEvent(nec.NetworkIncomingEvent):
|
||||||
def generate(self):
|
def generate(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class MetacontactsReceivedEvent(nec.NetworkIncomingEvent):
|
|
||||||
name = 'metacontacts-received'
|
|
||||||
base_network_events = []
|
|
||||||
|
|
||||||
def generate(self):
|
|
||||||
# Metacontact tags
|
|
||||||
# http://www.xmpp.org/extensions/xep-0209.html
|
|
||||||
self.meta_list = {}
|
|
||||||
query = self.stanza.getTag('query')
|
|
||||||
storage = query.getTag('storage')
|
|
||||||
metas = storage.getTags('meta')
|
|
||||||
for meta in metas:
|
|
||||||
try:
|
|
||||||
jid = helpers.parse_jid(meta.getAttr('jid'))
|
|
||||||
except helpers.InvalidFormat:
|
|
||||||
continue
|
|
||||||
tag = meta.getAttr('tag')
|
|
||||||
data = {'jid': jid}
|
|
||||||
order = meta.getAttr('order')
|
|
||||||
try:
|
|
||||||
order = int(order)
|
|
||||||
except Exception:
|
|
||||||
order = 0
|
|
||||||
if order is not None:
|
|
||||||
data['order'] = order
|
|
||||||
if tag in self.meta_list:
|
|
||||||
self.meta_list[tag].append(data)
|
|
||||||
else:
|
|
||||||
self.meta_list[tag] = [data]
|
|
||||||
return True
|
|
||||||
|
|
||||||
class ZeroconfNameConflictEvent(nec.NetworkIncomingEvent):
|
class ZeroconfNameConflictEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'zeroconf-name-conflict'
|
name = 'zeroconf-name-conflict'
|
||||||
base_network_events = []
|
base_network_events = []
|
||||||
|
|
|
@ -732,8 +732,9 @@ class MetacontactManager():
|
||||||
self._metacontacts_tags[brother_account][tag] = [{'jid': brother_jid,
|
self._metacontacts_tags[brother_account][tag] = [{'jid': brother_jid,
|
||||||
'tag': tag}]
|
'tag': tag}]
|
||||||
if brother_account != account:
|
if brother_account != account:
|
||||||
common.app.connections[brother_account].store_metacontacts(
|
con = common.app.connections[brother_account]
|
||||||
self._metacontacts_tags[brother_account])
|
con.get_module('MetaContacts').store_metacontacts(
|
||||||
|
self._metacontacts_tags[brother_account])
|
||||||
# be sure jid has no other tag
|
# be sure jid has no other tag
|
||||||
old_tag = self._get_metacontacts_tag(account, jid)
|
old_tag = self._get_metacontacts_tag(account, jid)
|
||||||
while old_tag:
|
while old_tag:
|
||||||
|
@ -748,11 +749,12 @@ class MetacontactManager():
|
||||||
else:
|
else:
|
||||||
self._metacontacts_tags[account][tag].append({'jid': jid,
|
self._metacontacts_tags[account][tag].append({'jid': jid,
|
||||||
'tag': tag})
|
'tag': tag})
|
||||||
common.app.connections[account].store_metacontacts(
|
con = common.app.connections[account]
|
||||||
self._metacontacts_tags[account])
|
con.get_module('MetaContacts').store_metacontacts(
|
||||||
|
self._metacontacts_tags[account])
|
||||||
|
|
||||||
def remove_metacontact(self, account, jid):
|
def remove_metacontact(self, account, jid):
|
||||||
if not account in self._metacontacts_tags:
|
if account not in self._metacontacts_tags:
|
||||||
return
|
return
|
||||||
|
|
||||||
found = None
|
found = None
|
||||||
|
@ -763,8 +765,9 @@ class MetacontactManager():
|
||||||
break
|
break
|
||||||
if found:
|
if found:
|
||||||
self._metacontacts_tags[account][tag].remove(found)
|
self._metacontacts_tags[account][tag].remove(found)
|
||||||
common.app.connections[account].store_metacontacts(
|
con = common.app.connections[account]
|
||||||
self._metacontacts_tags[account])
|
con.get_module('MetaContacts').store_metacontacts(
|
||||||
|
self._metacontacts_tags[account])
|
||||||
break
|
break
|
||||||
|
|
||||||
def has_brother(self, account, jid, accounts):
|
def has_brother(self, account, jid, accounts):
|
||||||
|
|
|
@ -1508,3 +1508,11 @@ def get_emoticon_theme_path(theme):
|
||||||
emoticons_user_path = os.path.join(configpaths.get('MY_EMOTS'), theme)
|
emoticons_user_path = os.path.join(configpaths.get('MY_EMOTS'), theme)
|
||||||
if os.path.exists(emoticons_user_path):
|
if os.path.exists(emoticons_user_path):
|
||||||
return emoticons_user_path
|
return emoticons_user_path
|
||||||
|
|
||||||
|
def call_counter(func):
|
||||||
|
def helper(self, restart=False):
|
||||||
|
if restart:
|
||||||
|
self._connect_maschine_calls = 0
|
||||||
|
self._connect_maschine_calls += 1
|
||||||
|
return func(self, restart=False)
|
||||||
|
return helper
|
||||||
|
|
|
@ -43,7 +43,7 @@ class ModuleMock:
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
# HTTPUpload
|
# HTTPUpload, ..
|
||||||
self.available = False
|
self.available = False
|
||||||
|
|
||||||
# Blocking
|
# Blocking
|
||||||
|
@ -54,6 +54,9 @@ class ModuleMock:
|
||||||
self.blocked_groups = []
|
self.blocked_groups = []
|
||||||
self.blocked_all = False
|
self.blocked_all = False
|
||||||
|
|
||||||
|
# Delimiter
|
||||||
|
self.delimiter = '::'
|
||||||
|
|
||||||
# Bookmarks
|
# Bookmarks
|
||||||
self.bookmarks = {}
|
self.bookmarks = {}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ class Bookmarks:
|
||||||
self._con = con
|
self._con = con
|
||||||
self._account = con.name
|
self._account = con.name
|
||||||
self.bookmarks = {}
|
self.bookmarks = {}
|
||||||
|
self.available = False
|
||||||
|
|
||||||
self.handlers = []
|
self.handlers = []
|
||||||
|
|
||||||
|
@ -65,6 +66,7 @@ class Bookmarks:
|
||||||
self._request_private_bookmarks()
|
self._request_private_bookmarks()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.available = True
|
||||||
log.info('Received Bookmarks (PubSub)')
|
log.info('Received Bookmarks (PubSub)')
|
||||||
self._parse_bookmarks(stanza)
|
self._parse_bookmarks(stanza)
|
||||||
self._request_private_bookmarks()
|
self._request_private_bookmarks()
|
||||||
|
@ -84,6 +86,7 @@ class Bookmarks:
|
||||||
if not nbxmpp.isResultNode(stanza):
|
if not nbxmpp.isResultNode(stanza):
|
||||||
log.info('No private bookmarks: %s', stanza.getError())
|
log.info('No private bookmarks: %s', stanza.getError())
|
||||||
else:
|
else:
|
||||||
|
self.available = True
|
||||||
log.info('Received Bookmarks (PrivateStorage)')
|
log.info('Received Bookmarks (PrivateStorage)')
|
||||||
merged = self._parse_bookmarks(stanza, check_merge=True)
|
merged = self._parse_bookmarks(stanza, check_merge=True)
|
||||||
if merged and self._pubsub_support():
|
if merged and self._pubsub_support():
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
# This file is part of Gajim.
|
||||||
|
#
|
||||||
|
# Gajim is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation; version 3 only.
|
||||||
|
#
|
||||||
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# XEP-0083: Nested Roster Groups
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
|
|
||||||
|
log = logging.getLogger('gajim.c.m.delimiter')
|
||||||
|
|
||||||
|
|
||||||
|
class Delimiter:
|
||||||
|
def __init__(self, con):
|
||||||
|
self._con = con
|
||||||
|
self._account = con.name
|
||||||
|
self.available = False
|
||||||
|
|
||||||
|
self.delimiter = '::'
|
||||||
|
|
||||||
|
self.handlers = []
|
||||||
|
|
||||||
|
def get_roster_delimiter(self):
|
||||||
|
log.info('Request')
|
||||||
|
node = nbxmpp.Node('storage', attrs={'xmlns': 'roster:delimiter'})
|
||||||
|
iq = nbxmpp.Iq('get', nbxmpp.NS_PRIVATE, payload=node)
|
||||||
|
|
||||||
|
self._con.connection.SendAndCallForResponse(
|
||||||
|
iq, self._delimiter_received)
|
||||||
|
|
||||||
|
def _delimiter_received(self, stanza):
|
||||||
|
if not nbxmpp.isResultNode(stanza):
|
||||||
|
log.info('Request error: %s', stanza.getError())
|
||||||
|
else:
|
||||||
|
delimiter = stanza.getQuery().getTagData('roster')
|
||||||
|
self.available = True
|
||||||
|
log.info('Delimiter received: %s', delimiter)
|
||||||
|
if delimiter:
|
||||||
|
self.delimiter = delimiter
|
||||||
|
else:
|
||||||
|
self.set_roster_delimiter()
|
||||||
|
|
||||||
|
self._con.connect_maschine()
|
||||||
|
|
||||||
|
def set_roster_delimiter(self):
|
||||||
|
log.info('Set delimiter')
|
||||||
|
iq = nbxmpp.Iq('set', nbxmpp.NS_PRIVATE)
|
||||||
|
roster = iq.getQuery().addChild('roster', namespace='roster:delimiter')
|
||||||
|
roster.setData('::')
|
||||||
|
|
||||||
|
self._con.connection.SendAndCallForResponse(
|
||||||
|
iq, self._set_delimiter_response)
|
||||||
|
|
||||||
|
def _set_delimiter_response(self, stanza):
|
||||||
|
if not nbxmpp.isResultNode(stanza):
|
||||||
|
log.info('Store error: %s', stanza.getError())
|
||||||
|
|
||||||
|
|
||||||
|
def get_instance(*args, **kwargs):
|
||||||
|
return Delimiter(*args, **kwargs), 'Delimiter'
|
|
@ -199,7 +199,7 @@ class Discovery:
|
||||||
if nbxmpp.NS_ADDRESS in features:
|
if nbxmpp.NS_ADDRESS in features:
|
||||||
self._con.addressing_supported = True
|
self._con.addressing_supported = True
|
||||||
|
|
||||||
self._con._continue_connection_request_privacy()
|
self._con.connect_maschine()
|
||||||
|
|
||||||
def _parse_transports(self, from_, identities, features, data, node):
|
def _parse_transports(self, from_, identities, features, data, node):
|
||||||
for identity in identities:
|
for identity in identities:
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
# This file is part of Gajim.
|
||||||
|
#
|
||||||
|
# Gajim is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation; version 3 only.
|
||||||
|
#
|
||||||
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# XEP-0209: Metacontacts
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
|
|
||||||
|
from gajim.common import app
|
||||||
|
from gajim.common import helpers
|
||||||
|
from gajim.common.nec import NetworkEvent
|
||||||
|
|
||||||
|
log = logging.getLogger('gajim.c.m.metacontacts')
|
||||||
|
|
||||||
|
|
||||||
|
class MetaContacts:
|
||||||
|
def __init__(self, con):
|
||||||
|
self._con = con
|
||||||
|
self._account = con.name
|
||||||
|
self.available = False
|
||||||
|
|
||||||
|
self.handlers = []
|
||||||
|
|
||||||
|
def get_metacontacts(self):
|
||||||
|
log.info('Request')
|
||||||
|
node = nbxmpp.Node('storage', attrs={'xmlns': 'storage:metacontacts'})
|
||||||
|
iq = nbxmpp.Iq('get', nbxmpp.NS_PRIVATE, payload=node)
|
||||||
|
|
||||||
|
self._con.connection.SendAndCallForResponse(
|
||||||
|
iq, self._metacontacts_received)
|
||||||
|
|
||||||
|
def _metacontacts_received(self, stanza):
|
||||||
|
if not nbxmpp.isResultNode(stanza):
|
||||||
|
log.info('Request error: %s', stanza.getError())
|
||||||
|
else:
|
||||||
|
self.available = True
|
||||||
|
meta_list = self._parse_metacontacts(stanza)
|
||||||
|
|
||||||
|
log.info('Received: %s', meta_list)
|
||||||
|
|
||||||
|
app.nec.push_incoming_event(NetworkEvent(
|
||||||
|
'metacontacts-received', conn=self._con, meta_list=meta_list))
|
||||||
|
|
||||||
|
self._con.connect_maschine()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parse_metacontacts(stanza):
|
||||||
|
meta_list = {}
|
||||||
|
query = stanza.getQuery()
|
||||||
|
storage = query.getTag('storage')
|
||||||
|
metas = storage.getTags('meta')
|
||||||
|
for meta in metas:
|
||||||
|
try:
|
||||||
|
jid = helpers.parse_jid(meta.getAttr('jid'))
|
||||||
|
except helpers.InvalidFormat:
|
||||||
|
continue
|
||||||
|
tag = meta.getAttr('tag')
|
||||||
|
data = {'jid': jid}
|
||||||
|
order = meta.getAttr('order')
|
||||||
|
try:
|
||||||
|
order = int(order)
|
||||||
|
except Exception:
|
||||||
|
order = 0
|
||||||
|
if order is not None:
|
||||||
|
data['order'] = order
|
||||||
|
if tag in meta_list:
|
||||||
|
meta_list[tag].append(data)
|
||||||
|
else:
|
||||||
|
meta_list[tag] = [data]
|
||||||
|
return meta_list
|
||||||
|
|
||||||
|
def store_metacontacts(self, tags_list):
|
||||||
|
if not app.account_is_connected(self._account):
|
||||||
|
return
|
||||||
|
iq = nbxmpp.Iq('set', nbxmpp.NS_PRIVATE)
|
||||||
|
meta = iq.getQuery().addChild('storage',
|
||||||
|
namespace='storage:metacontacts')
|
||||||
|
for tag in tags_list:
|
||||||
|
for data in tags_list[tag]:
|
||||||
|
jid = data['jid']
|
||||||
|
dict_ = {'jid': jid, 'tag': tag}
|
||||||
|
if 'order' in data:
|
||||||
|
dict_['order'] = data['order']
|
||||||
|
meta.addChild(name='meta', attrs=dict_)
|
||||||
|
log.info('Store: %s', tags_list)
|
||||||
|
self._con.connection.SendAndCallForResponse(
|
||||||
|
iq, self._store_response_received)
|
||||||
|
|
||||||
|
def _store_response_received(self, stanza):
|
||||||
|
if not nbxmpp.isResultNode(stanza):
|
||||||
|
log.info('Store error: %s', stanza.getError())
|
||||||
|
|
||||||
|
|
||||||
|
def get_instance(*args, **kwargs):
|
||||||
|
return MetaContacts(*args, **kwargs), 'MetaContacts'
|
|
@ -24,10 +24,6 @@ from gajim.common.nec import NetworkEvent
|
||||||
|
|
||||||
log = logging.getLogger('gajim.c.m.roster')
|
log = logging.getLogger('gajim.c.m.roster')
|
||||||
|
|
||||||
|
|
||||||
# TODO: Error IQs
|
|
||||||
# What if roster not supported on server -> error
|
|
||||||
|
|
||||||
RosterItem = namedtuple('RosterItem', 'jid data')
|
RosterItem = namedtuple('RosterItem', 'jid data')
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,7 +61,13 @@ class Roster:
|
||||||
app.config.set_per(
|
app.config.set_per(
|
||||||
'accounts', self._account, 'roster_version', '')
|
'accounts', self._account, 'roster_version', '')
|
||||||
|
|
||||||
def request_roster(self, version):
|
def request_roster(self):
|
||||||
|
version = None
|
||||||
|
features = self._con.connection.Dispatcher.Stream.features
|
||||||
|
if features and features.getTag('ver', namespace=nbxmpp.NS_ROSTER_VER):
|
||||||
|
version = app.config.get_per(
|
||||||
|
'accounts', self._account, 'roster_version')
|
||||||
|
|
||||||
log.info('Requested from server')
|
log.info('Requested from server')
|
||||||
iq = nbxmpp.Iq('get', nbxmpp.NS_ROSTER)
|
iq = nbxmpp.Iq('get', nbxmpp.NS_ROSTER)
|
||||||
if version is not None:
|
if version is not None:
|
||||||
|
@ -77,27 +79,26 @@ class Roster:
|
||||||
def _roster_received(self, stanza):
|
def _roster_received(self, stanza):
|
||||||
if not nbxmpp.isResultNode(stanza):
|
if not nbxmpp.isResultNode(stanza):
|
||||||
log.warning('Unable to retrive roster: %s', stanza.getError())
|
log.warning('Unable to retrive roster: %s', stanza.getError())
|
||||||
return
|
else:
|
||||||
|
log.info('Received Roster')
|
||||||
|
received_from_server = False
|
||||||
|
if stanza.getTag('query') is not None:
|
||||||
|
# clear Roster
|
||||||
|
self._data = {}
|
||||||
|
version = self._parse_roster(stanza)
|
||||||
|
|
||||||
log.info('Received Roster')
|
log.info('New version: %s', version)
|
||||||
received_from_server = False
|
app.logger.replace_roster(self._account, version, self._data)
|
||||||
if stanza.getTag('query') is not None:
|
|
||||||
# clear Roster
|
|
||||||
self._data = {}
|
|
||||||
version = self._parse_roster(stanza)
|
|
||||||
|
|
||||||
log.info('New version: %s', version)
|
received_from_server = True
|
||||||
app.logger.replace_roster(self._account, version, self._data)
|
|
||||||
|
|
||||||
received_from_server = True
|
app.nec.push_incoming_event(NetworkEvent(
|
||||||
|
'roster-received',
|
||||||
|
conn=self._con,
|
||||||
|
roster=self._data.copy(),
|
||||||
|
received_from_server=received_from_server))
|
||||||
|
|
||||||
app.nec.push_incoming_event(NetworkEvent(
|
self._con.connect_maschine()
|
||||||
'roster-received',
|
|
||||||
conn=self._con,
|
|
||||||
roster=self._data.copy(),
|
|
||||||
received_from_server=received_from_server))
|
|
||||||
|
|
||||||
self._con.send_first_presence()
|
|
||||||
|
|
||||||
def _roster_push_received(self, con, stanza):
|
def _roster_push_received(self, con, stanza):
|
||||||
log.info('Push received')
|
log.info('Push received')
|
||||||
|
|
|
@ -183,11 +183,3 @@ connection_handlers.ConnectionJingle):
|
||||||
|
|
||||||
app.nec.push_incoming_event(
|
app.nec.push_incoming_event(
|
||||||
DecryptedMessageReceivedEvent(None, **vars(event)))
|
DecryptedMessageReceivedEvent(None, **vars(event)))
|
||||||
|
|
||||||
def store_metacontacts(self, tags):
|
|
||||||
"""
|
|
||||||
Fake empty method
|
|
||||||
"""
|
|
||||||
# serverside metacontacts are not supported with zeroconf
|
|
||||||
# (there is no server)
|
|
||||||
pass
|
|
||||||
|
|
|
@ -592,7 +592,7 @@ class GroupchatControl(ChatControlBase):
|
||||||
|
|
||||||
# Bookmarks
|
# Bookmarks
|
||||||
con = app.connections[self.account]
|
con = app.connections[self.account]
|
||||||
bookmark_support = con.bookmarks_available()
|
bookmark_support = con.get_module('Bookmarks').available
|
||||||
bookmarked = self.room_jid in con.get_module('Bookmarks').bookmarks
|
bookmarked = self.room_jid in con.get_module('Bookmarks').bookmarks
|
||||||
win.lookup_action('bookmark-' + self.control_id).set_enabled(
|
win.lookup_action('bookmark-' + self.control_id).set_enabled(
|
||||||
online and bookmark_support and not bookmarked)
|
online and bookmark_support and not bookmarked)
|
||||||
|
|
|
@ -111,7 +111,8 @@ class JoinGroupchatWindow(Gtk.ApplicationWindow):
|
||||||
|
|
||||||
# Set bookmark switch sensitive if server supports bookmarks
|
# Set bookmark switch sensitive if server supports bookmarks
|
||||||
acc = self.account_combo.get_active_id()
|
acc = self.account_combo.get_active_id()
|
||||||
if not app.connections[acc].private_storage_supported:
|
con = app.connections[acc]
|
||||||
|
if not con.get_module('Bookmarks').available:
|
||||||
self.bookmark_switch.set_sensitive(False)
|
self.bookmark_switch.set_sensitive(False)
|
||||||
self.autojoin_switch.set_sensitive(False)
|
self.autojoin_switch.set_sensitive(False)
|
||||||
|
|
||||||
|
@ -256,8 +257,6 @@ class JoinGroupchatWindow(Gtk.ApplicationWindow):
|
||||||
|
|
||||||
def _add_bookmark(self, account, nickname, password):
|
def _add_bookmark(self, account, nickname, password):
|
||||||
con = app.connections[account]
|
con = app.connections[account]
|
||||||
if not con.private_storage_supported:
|
|
||||||
return
|
|
||||||
|
|
||||||
add_bookmark = self.bookmark_switch.get_active()
|
add_bookmark = self.bookmark_switch.get_active()
|
||||||
if not add_bookmark:
|
if not add_bookmark:
|
||||||
|
|
|
@ -343,7 +343,7 @@ class RosterWindow:
|
||||||
account_group = 'MERGED'
|
account_group = 'MERGED'
|
||||||
else:
|
else:
|
||||||
account_group = account
|
account_group = account
|
||||||
delimiter = app.connections[account].nested_group_delimiter
|
delimiter = app.connections[account].get_module('Delimiter').delimiter
|
||||||
group_splited = group.split(delimiter)
|
group_splited = group.split(delimiter)
|
||||||
parent_group = delimiter.join(group_splited[:-1])
|
parent_group = delimiter.join(group_splited[:-1])
|
||||||
if len(group_splited) > 1 and parent_group in self._iters[account_group]['groups']:
|
if len(group_splited) > 1 and parent_group in self._iters[account_group]['groups']:
|
||||||
|
@ -1347,7 +1347,7 @@ class RosterWindow:
|
||||||
self.draw_parent_contact(jid, account)
|
self.draw_parent_contact(jid, account)
|
||||||
|
|
||||||
if visible:
|
if visible:
|
||||||
delimiter = app.connections[account].nested_group_delimiter
|
delimiter = app.connections[account].get_module('Delimiter').delimiter
|
||||||
for group in contact.get_shown_groups():
|
for group in contact.get_shown_groups():
|
||||||
group_splited = group.split(delimiter)
|
group_splited = group.split(delimiter)
|
||||||
i = 1
|
i = 1
|
||||||
|
@ -1600,7 +1600,7 @@ class RosterWindow:
|
||||||
return
|
return
|
||||||
if account not in app.connections:
|
if account not in app.connections:
|
||||||
return
|
return
|
||||||
delimiter = app.connections[account].nested_group_delimiter
|
delimiter = app.connections[account].get_module('Delimiter').delimiter
|
||||||
group_splited = group.split(delimiter)
|
group_splited = group.split(delimiter)
|
||||||
i = 1
|
i = 1
|
||||||
while i < len(group_splited) + 1:
|
while i < len(group_splited) + 1:
|
||||||
|
@ -4159,9 +4159,10 @@ class RosterWindow:
|
||||||
|
|
||||||
def on_drop_in_contact(self, widget, account_source, c_source, account_dest,
|
def on_drop_in_contact(self, widget, account_source, c_source, account_dest,
|
||||||
c_dest, was_big_brother, context, etime):
|
c_dest, was_big_brother, context, etime):
|
||||||
|
con_source = app.connections[account_source]
|
||||||
if not app.connections[account_source].private_storage_supported or \
|
con_dest = app.connections[account_dest]
|
||||||
not app.connections[account_dest].private_storage_supported:
|
if (not con_source.get_module('MetaContacts').available or
|
||||||
|
not con_dest.get_module('MetaContacts').available):
|
||||||
WarningDialog(_('Metacontacts storage not supported by '
|
WarningDialog(_('Metacontacts storage not supported by '
|
||||||
'your server'),
|
'your server'),
|
||||||
_('Your server does not support storing metacontacts '
|
_('Your server does not support storing metacontacts '
|
||||||
|
@ -4433,7 +4434,7 @@ class RosterWindow:
|
||||||
# drop on another account
|
# drop on another account
|
||||||
return
|
return
|
||||||
grp_source = model[iter_source][Column.JID]
|
grp_source = model[iter_source][Column.JID]
|
||||||
delimiter = app.connections[account_source].nested_group_delimiter
|
delimiter = app.connections[account_source].get_module('Delimiter').delimiter
|
||||||
grp_source_list = grp_source.split(delimiter)
|
grp_source_list = grp_source.split(delimiter)
|
||||||
new_grp = None
|
new_grp = None
|
||||||
if type_dest == 'account':
|
if type_dest == 'account':
|
||||||
|
|
|
@ -221,8 +221,6 @@ class StatusIcon:
|
||||||
if connected_accounts < 1:
|
if connected_accounts < 1:
|
||||||
item.set_sensitive(False)
|
item.set_sensitive(False)
|
||||||
|
|
||||||
connected_accounts_with_private_storage = 0
|
|
||||||
|
|
||||||
item = Gtk.SeparatorMenuItem.new()
|
item = Gtk.SeparatorMenuItem.new()
|
||||||
sub_menu.append(item)
|
sub_menu.append(item)
|
||||||
|
|
||||||
|
@ -271,8 +269,6 @@ class StatusIcon:
|
||||||
for account in app.connections:
|
for account in app.connections:
|
||||||
if app.account_is_connected(account) and \
|
if app.account_is_connected(account) and \
|
||||||
not app.config.get_per('accounts', account, 'is_zeroconf'):
|
not app.config.get_per('accounts', account, 'is_zeroconf'):
|
||||||
if app.connections[account].private_storage_supported:
|
|
||||||
connected_accounts_with_private_storage += 1
|
|
||||||
|
|
||||||
# for single message
|
# for single message
|
||||||
single_message_menuitem.set_submenu(None)
|
single_message_menuitem.set_submenu(None)
|
||||||
|
@ -296,8 +292,6 @@ class StatusIcon:
|
||||||
if app.connections[account].is_zeroconf or \
|
if app.connections[account].is_zeroconf or \
|
||||||
not app.account_is_connected(account):
|
not app.account_is_connected(account):
|
||||||
continue
|
continue
|
||||||
if app.connections[account].private_storage_supported:
|
|
||||||
connected_accounts_with_private_storage += 1
|
|
||||||
# for single message
|
# for single message
|
||||||
item = Gtk.MenuItem.new_with_label(
|
item = Gtk.MenuItem.new_with_label(
|
||||||
_('using account %s') % account_label)
|
_('using account %s') % account_label)
|
||||||
|
@ -320,9 +314,12 @@ class StatusIcon:
|
||||||
newitem = Gtk.MenuItem.new_with_mnemonic(_('_Manage Bookmarks…'))
|
newitem = Gtk.MenuItem.new_with_mnemonic(_('_Manage Bookmarks…'))
|
||||||
newitem.connect('activate',
|
newitem.connect('activate',
|
||||||
app.interface.roster.on_manage_bookmarks_menuitem_activate)
|
app.interface.roster.on_manage_bookmarks_menuitem_activate)
|
||||||
|
newitem.set_sensitive(False)
|
||||||
gc_sub_menu.append(newitem)
|
gc_sub_menu.append(newitem)
|
||||||
if connected_accounts_with_private_storage == 0:
|
for account in accounts_list:
|
||||||
newitem.set_sensitive(False)
|
if app.account_supports_private_storage(account):
|
||||||
|
newitem.set_sensitive(True)
|
||||||
|
break
|
||||||
|
|
||||||
sounds_mute_menuitem.set_active(not app.config.get('sounds_on'))
|
sounds_mute_menuitem.set_active(not app.config.get('sounds_on'))
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ class MockConnection(Mock, ConnectionHandlers):
|
||||||
self.connected = 2
|
self.connected = 2
|
||||||
self.pep = {}
|
self.pep = {}
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
self.nested_group_delimiter = '::'
|
|
||||||
self.server_resource = 'Gajim'
|
self.server_resource = 'Gajim'
|
||||||
|
|
||||||
app.interface.instances[account] = {'infos': {}, 'disco': {},
|
app.interface.instances[account] = {'infos': {}, 'disco': {},
|
||||||
|
|
Loading…
Reference in New Issue