Commit 0a8af736 authored by Yann Leboulanger's avatar Yann Leboulanger
Browse files

add XEP-313 (MAM) support

parent e8277c05
...@@ -3006,7 +3006,7 @@ def _on_message_tv_buffer_changed(self, textbuffer): ...@@ -3006,7 +3006,7 @@ def _on_message_tv_buffer_changed(self, textbuffer):
and gajim.HAVE_PYCRYPTO and self.contact.supports(NS_ESESSION): and gajim.HAVE_PYCRYPTO and self.contact.supports(NS_ESESSION):
self.begin_e2e_negotiation() self.begin_e2e_negotiation()
elif (not self.session or not self.session.status) and \ elif (not self.session or not self.session.status) and \
gajim.connections[self.account].archiving_supported: gajim.connections[self.account].archiving_136_supported:
self.begin_archiving_negotiation() self.begin_archiving_negotiation()
else: else:
self.send_chatstate('active', self.contact) self.send_chatstate('active', self.contact)
......
...@@ -316,6 +316,7 @@ class Config: ...@@ -316,6 +316,7 @@ class Config:
'ignore_incoming_attention': [opt_bool, False, _('If True, Gajim will ignore incoming attention requestd ("wizz").')], 'ignore_incoming_attention': [opt_bool, False, _('If True, Gajim will ignore incoming attention requestd ("wizz").')],
'remember_opened_chat_controls': [ opt_bool, True, _('If enabled, Gajim will reopen chat windows that were opened last time Gajim was closed.')], 'remember_opened_chat_controls': [ opt_bool, True, _('If enabled, Gajim will reopen chat windows that were opened last time Gajim was closed.')],
'positive_184_ack': [ opt_bool, False, _('If enabled, Gajim will show an icon to show that sent message has been received by your contact')], 'positive_184_ack': [ opt_bool, False, _('If enabled, Gajim will show an icon to show that sent message has been received by your contact')],
'last_mam_id': [opt_str, '', _('Last MAM id we are syncronized with')],
}, {}) }, {})
__options_per_key = { __options_per_key = {
......
...@@ -423,6 +423,8 @@ def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid, ...@@ -423,6 +423,8 @@ def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid,
msg_iq.setTag(nbxmpp.NS_ENCRYPTED + ' x').setData(msgenc) msg_iq.setTag(nbxmpp.NS_ENCRYPTED + ' x').setData(msgenc)
if self.carbons_enabled: if self.carbons_enabled:
msg_iq.addChild(name='private', namespace=nbxmpp.NS_CARBONS) msg_iq.addChild(name='private', namespace=nbxmpp.NS_CARBONS)
msg_iq.addChild(name='no-permanent-storage',
namespace=nbxmpp.NS_MSG_HINTS)
if form_node: if form_node:
msg_iq.addChild(node=form_node) msg_iq.addChild(node=form_node)
...@@ -501,6 +503,10 @@ def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid, ...@@ -501,6 +503,10 @@ def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid,
if self.carbons_enabled: if self.carbons_enabled:
msg_iq.addChild(name='private', msg_iq.addChild(name='private',
namespace=nbxmpp.NS_CARBONS) namespace=nbxmpp.NS_CARBONS)
msg_iq.addChild(name='no-permanent-storage',
namespace=nbxmpp.NS_MSG_HINTS)
msg_iq.addChild(name='no-copy',
namespace=nbxmpp.NS_MSG_HINTS)
if callback: if callback:
callback(jid, msg, keyID, forward_from, session, original_message, callback(jid, msg, keyID, forward_from, session, original_message,
...@@ -1977,8 +1983,12 @@ def _nec_agent_info_received(self, obj): ...@@ -1977,8 +1983,12 @@ def _nec_agent_info_received(self, obj):
# Remove stored bookmarks accessible to everyone. # Remove stored bookmarks accessible to everyone.
self.send_pb_purge(our_jid, 'storage:bookmarks') self.send_pb_purge(our_jid, 'storage:bookmarks')
self.send_pb_delete(our_jid, 'storage:bookmarks') self.send_pb_delete(our_jid, 'storage:bookmarks')
if nbxmpp.NS_MAM in obj.features:
self.archiving_supported = True
self.archiving_313_supported = True
if nbxmpp.NS_ARCHIVE in obj.features: if nbxmpp.NS_ARCHIVE in obj.features:
self.archiving_supported = True self.archiving_supported = True
self.archiving_136_supported = True
if nbxmpp.NS_ARCHIVE_AUTO in obj.features: if nbxmpp.NS_ARCHIVE_AUTO in obj.features:
self.archive_auto_supported = True self.archive_auto_supported = True
if nbxmpp.NS_ARCHIVE_MANAGE in obj.features: if nbxmpp.NS_ARCHIVE_MANAGE in obj.features:
......
...@@ -53,10 +53,8 @@ ...@@ -53,10 +53,8 @@
from common.protocol.caps import ConnectionCaps from common.protocol.caps import ConnectionCaps
from common.protocol.bytestream import ConnectionSocks5Bytestream from common.protocol.bytestream import ConnectionSocks5Bytestream
from common.protocol.bytestream import ConnectionIBBytestream from common.protocol.bytestream import ConnectionIBBytestream
from common.message_archiving import ConnectionArchive from common.message_archiving import ConnectionArchive136
from common.message_archiving import ARCHIVING_COLLECTIONS_ARRIVED from common.message_archiving import ConnectionArchive313
from common.message_archiving import ARCHIVING_COLLECTION_ARRIVED
from common.message_archiving import ARCHIVING_MODIFICATIONS_ARRIVED
from common.connection_handlers_events import * from common.connection_handlers_events import *
from common import ged from common import ged
...@@ -463,6 +461,7 @@ def _IqCB(self, con, iq_obj): ...@@ -463,6 +461,7 @@ def _IqCB(self, con, iq_obj):
if id_ not in self.awaiting_answers: if id_ not in self.awaiting_answers:
return return
if self.awaiting_answers[id_][0] == VCARD_PUBLISHED: if self.awaiting_answers[id_][0] == VCARD_PUBLISHED:
if iq_obj.getType() == 'result': if iq_obj.getType() == 'result':
vcard_iq = self.awaiting_answers[id_][1] vcard_iq = self.awaiting_answers[id_][1]
...@@ -481,6 +480,7 @@ def _IqCB(self, con, iq_obj): ...@@ -481,6 +480,7 @@ def _IqCB(self, con, iq_obj):
if self.vcard_sha != new_sha and gajim.SHOW_LIST[ if self.vcard_sha != new_sha and gajim.SHOW_LIST[
self.connected] != 'invisible': self.connected] != 'invisible':
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
del self.awaiting_answers[id_]
return return
self.vcard_sha = new_sha self.vcard_sha = new_sha
sshow = helpers.get_xmpp_show(gajim.SHOW_LIST[ sshow = helpers.get_xmpp_show(gajim.SHOW_LIST[
...@@ -494,6 +494,7 @@ def _IqCB(self, con, iq_obj): ...@@ -494,6 +494,7 @@ def _IqCB(self, con, iq_obj):
elif iq_obj.getType() == 'error': elif iq_obj.getType() == 'error':
gajim.nec.push_incoming_event(VcardNotPublishedEvent(None, gajim.nec.push_incoming_event(VcardNotPublishedEvent(None,
conn=self)) conn=self))
del self.awaiting_answers[id_]
elif self.awaiting_answers[id_][0] == VCARD_ARRIVED: elif self.awaiting_answers[id_][0] == VCARD_ARRIVED:
# If vcard is empty, we send to the interface an empty vcard so that # If vcard is empty, we send to the interface an empty vcard so that
# it knows it arrived # it knows it arrived
...@@ -516,10 +517,12 @@ def _IqCB(self, con, iq_obj): ...@@ -516,10 +517,12 @@ def _IqCB(self, con, iq_obj):
vcard = {'jid': jid, 'resource': resource} vcard = {'jid': jid, 'resource': resource}
gajim.nec.push_incoming_event(VcardReceivedEvent(None, gajim.nec.push_incoming_event(VcardReceivedEvent(None,
conn=self, vcard_dict=vcard)) conn=self, vcard_dict=vcard))
del self.awaiting_answers[id_]
elif self.awaiting_answers[id_][0] == AGENT_REMOVED: elif self.awaiting_answers[id_][0] == AGENT_REMOVED:
jid = self.awaiting_answers[id_][1] jid = self.awaiting_answers[id_][1]
gajim.nec.push_incoming_event(AgentRemovedEvent(None, conn=self, gajim.nec.push_incoming_event(AgentRemovedEvent(None, conn=self,
agent=jid)) agent=jid))
del self.awaiting_answers[id_]
elif self.awaiting_answers[id_][0] == METACONTACTS_ARRIVED: elif self.awaiting_answers[id_][0] == METACONTACTS_ARRIVED:
if not self.connection: if not self.connection:
return return
...@@ -530,7 +533,9 @@ def _IqCB(self, con, iq_obj): ...@@ -530,7 +533,9 @@ def _IqCB(self, con, iq_obj):
if iq_obj.getErrorCode() not in ('403', '406', '404'): if iq_obj.getErrorCode() not in ('403', '406', '404'):
self.private_storage_supported = False self.private_storage_supported = False
self.get_roster_delimiter() self.get_roster_delimiter()
del self.awaiting_answers[id_]
elif self.awaiting_answers[id_][0] == DELIMITER_ARRIVED: elif self.awaiting_answers[id_][0] == DELIMITER_ARRIVED:
del self.awaiting_answers[id_]
if not self.connection: if not self.connection:
return return
if iq_obj.getType() == 'result': if iq_obj.getType() == 'result':
...@@ -565,7 +570,9 @@ def _IqCB(self, con, iq_obj): ...@@ -565,7 +570,9 @@ def _IqCB(self, con, iq_obj):
gajim.nec.push_incoming_event(RosterReceivedEvent(None, gajim.nec.push_incoming_event(RosterReceivedEvent(None,
conn=self)) conn=self))
GLib.timeout_add_seconds(10, self.discover_servers) GLib.timeout_add_seconds(10, self.discover_servers)
del self.awaiting_answers[id_]
elif self.awaiting_answers[id_][0] == PRIVACY_ARRIVED: elif self.awaiting_answers[id_][0] == PRIVACY_ARRIVED:
del self.awaiting_answers[id_]
if iq_obj.getType() != 'error': if iq_obj.getType() != 'error':
self.privacy_rules_supported = True self.privacy_rules_supported = True
self.get_privacy_list('block') self.get_privacy_list('block')
...@@ -594,6 +601,7 @@ def _IqCB(self, con, iq_obj): ...@@ -594,6 +601,7 @@ def _IqCB(self, con, iq_obj):
# Ask metacontacts before roster # Ask metacontacts before roster
self.get_metacontacts() self.get_metacontacts()
elif self.awaiting_answers[id_][0] == BLOCKING_ARRIVED: elif self.awaiting_answers[id_][0] == BLOCKING_ARRIVED:
del self.awaiting_answers[id_]
if iq_obj.getType() == 'result': if iq_obj.getType() == 'result':
list_node = iq_obj.getTag('blocklist') list_node = iq_obj.getTag('blocklist')
if not list_node: if not list_node:
...@@ -602,6 +610,7 @@ def _IqCB(self, con, iq_obj): ...@@ -602,6 +610,7 @@ def _IqCB(self, con, iq_obj):
for i in list_node.iterTags('item'): for i in list_node.iterTags('item'):
self.blocked_contacts.append(i.getAttr('jid')) self.blocked_contacts.append(i.getAttr('jid'))
elif self.awaiting_answers[id_][0] == PEP_CONFIG: elif self.awaiting_answers[id_][0] == PEP_CONFIG:
del self.awaiting_answers[id_]
if iq_obj.getType() == 'error': if iq_obj.getType() == 'error':
return return
if not iq_obj.getTag('pubsub'): if not iq_obj.getTag('pubsub'):
...@@ -616,71 +625,6 @@ def _IqCB(self, con, iq_obj): ...@@ -616,71 +625,6 @@ def _IqCB(self, con, iq_obj):
gajim.nec.push_incoming_event(PEPConfigReceivedEvent(None, gajim.nec.push_incoming_event(PEPConfigReceivedEvent(None,
conn=self, node=node, form=form)) conn=self, node=node, form=form))
elif self.awaiting_answers[id_][0] == ARCHIVING_COLLECTIONS_ARRIVED:
# TODO
print('ARCHIVING_COLLECTIONS_ARRIVED')
elif self.awaiting_answers[id_][0] == ARCHIVING_COLLECTION_ARRIVED:
def save_if_not_exists(with_, nick, direction, tim, payload):
assert len(payload) == 1, 'got several archiving messages in' +\
' the same time %s' % ''.join(payload)
if payload[0].getName() == 'body':
gajim.logger.save_if_not_exists(with_, direction, tim,
msg=payload[0].getData(), nick=nick)
elif payload[0].getName() == 'message':
print('Not implemented')
chat = iq_obj.getTag('chat')
if chat:
with_ = chat.getAttr('with')
start_ = chat.getAttr('start')
tim = helpers.datetime_tuple(start_)
tim = timegm(tim)
nb = 0
for element in chat.getChildren():
try:
secs = int(element.getAttr('secs'))
except TypeError:
secs = 0
if secs:
tim += secs
nick = element.getAttr('name')
if element.getName() == 'from':
save_if_not_exists(with_, nick, 'from', localtime(tim),
element.getPayload())
nb += 1
if element.getName() == 'to':
save_if_not_exists(with_, nick, 'to', localtime(tim),
element.getPayload())
nb += 1
set_ = chat.getTag('set')
first = set_.getTag('first')
if first:
try:
index = int(first.getAttr('index'))
except TypeError:
index = 0
try:
count = int(set_.getTagData('count'))
except TypeError:
count = 0
if count > index + nb:
# Request the next page
after = element.getTagData('last')
self.request_collection_page(with_, start_, after=after)
elif self.awaiting_answers[id_][0] == ARCHIVING_MODIFICATIONS_ARRIVED:
modified = iq_obj.getTag('modified')
if modified:
for element in modified.getChildren():
if element.getName() == 'changed':
with_ = element.getAttr('with')
start_ = element.getAttr('start')
self.request_collection_page(with_, start_)
#elif element.getName() == 'removed':
# do nothing
del self.awaiting_answers[id_]
def _vCardCB(self, con, vc): def _vCardCB(self, con, vc):
""" """
Called when we receive a vCard Parse the vCard and send it to plugins Called when we receive a vCard Parse the vCard and send it to plugins
...@@ -1421,13 +1365,14 @@ def make_new_session(self, jid, thread_id=None, type_='chat', cls=None): ...@@ -1421,13 +1365,14 @@ def make_new_session(self, jid, thread_id=None, type_='chat', cls=None):
return sess return sess
class ConnectionHandlers(ConnectionArchive, ConnectionVcard, class ConnectionHandlers(ConnectionArchive136, ConnectionArchive313,
ConnectionSocks5Bytestream, ConnectionDisco, ConnectionCommands, ConnectionVcard, ConnectionSocks5Bytestream, ConnectionDisco,
ConnectionPubSub, ConnectionPEP, ConnectionCaps, ConnectionHandlersBase, ConnectionCommands, ConnectionPubSub, ConnectionPEP, ConnectionCaps,
ConnectionJingle, ConnectionIBBytestream): ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
def __init__(self): def __init__(self):
global HAS_IDLE global HAS_IDLE
ConnectionArchive.__init__(self) ConnectionArchive136.__init__(self)
ConnectionArchive313.__init__(self)
ConnectionVcard.__init__(self) ConnectionVcard.__init__(self)
ConnectionSocks5Bytestream.__init__(self) ConnectionSocks5Bytestream.__init__(self)
ConnectionIBBytestream.__init__(self) ConnectionIBBytestream.__init__(self)
......
...@@ -1088,6 +1088,60 @@ def generate(self): ...@@ -1088,6 +1088,60 @@ def generate(self):
return return
self.forwarded = True self.forwarded = True
result = self.stanza.getTag('result', namespace=nbxmpp.NS_MAM)
if result:
forwarded = result.getTag('forwarded', namespace=nbxmpp.NS_FORWARD)
if not forwarded:
return
delay = forwarded.getTag('delay', namespace=nbxmpp.NS_DELAY2)
if not delay:
return
tim = delay.getAttr('stamp')
tim = helpers.datetime_tuple(tim)
tim = localtime(timegm(tim))
msg_ = forwarded.getTag('message')
to_ = msg_.getAttr('to')
if to_:
gajim.get_jid_without_resource(to_)
else:
to_ = gajim.get_jid_from_account(account)
frm_ = gajim.get_jid_without_resource(msg_.getAttr('from'))
nick = None
msg_txt = msg_.getTagData('body')
if to_ == gajim.get_jid_from_account(account):
with_ = frm_
direction = 'from'
res = gajim.get_resource_from_jid(msg_.getAttr('from'))
else:
with_ = to_
direction = 'to'
res = gajim.get_resource_from_jid(msg_.getAttr('to'))
is_pm = gajim.logger.jid_is_room_jid(with_)
if msg_.getAttr('type') == 'groupchat':
if is_pm == False:
log.warn('JID %s is marked as normal contact in database '
'but we got a groupchat message from it.')
return
if is_pm == None:
gajim.logger.get_jid_id(with_, 'ROOM')
nick = res
else:
if is_pm == None:
# we don't know this JID, we need to disco it.
server = gajim.get_server_from_jid(with_)
if server not in self.conn.mam_awaiting_disco_result:
self.conn.mam_awaiting_disco_result[server] = [
[with_, direction, tim, msg_txt, res]]
self.conn.discoverInfo(server)
else:
self.conn.mam_awaiting_disco_result[server].append(
[with_, direction, tim, msg_txt, res])
return
gajim.logger.save_if_not_exists(with_, direction, tim,
msg=msg_txt, nick=nick)
return
self.enc_tag = self.stanza.getTag('x', namespace=nbxmpp.NS_ENCRYPTED) self.enc_tag = self.stanza.getTag('x', namespace=nbxmpp.NS_ENCRYPTED)
self.invite_tag = None self.invite_tag = None
......
...@@ -223,13 +223,17 @@ def jid_is_from_pm(self, jid): ...@@ -223,13 +223,17 @@ def jid_is_from_pm(self, jid):
return False return False
def jid_is_room_jid(self, jid): def jid_is_room_jid(self, jid):
self.cur.execute('SELECT jid_id FROM jids WHERE jid=? AND type=?', """
(jid, constants.JID_ROOM_TYPE)) Return True if it's a room jid, False if it's not, None if we don't know
"""
self.cur.execute('SELECT type FROM jids WHERE jid=?', (jid,))
row = self.cur.fetchone() row = self.cur.fetchone()
if row is None: if row is None:
return False return None
else: else:
return True if row[0] == constants.JID_ROOM_TYPE:
return True
return False
def get_jid_id(self, jid, typestr=None): def get_jid_id(self, jid, typestr=None):
""" """
......
...@@ -29,9 +29,97 @@ ...@@ -29,9 +29,97 @@
ARCHIVING_COLLECTIONS_ARRIVED = 'archiving_collections_arrived' ARCHIVING_COLLECTIONS_ARRIVED = 'archiving_collections_arrived'
ARCHIVING_COLLECTION_ARRIVED = 'archiving_collection_arrived' ARCHIVING_COLLECTION_ARRIVED = 'archiving_collection_arrived'
ARCHIVING_MODIFICATIONS_ARRIVED = 'archiving_modifications_arrived' ARCHIVING_MODIFICATIONS_ARRIVED = 'archiving_modifications_arrived'
MAM_RESULTS_ARRIVED = 'mam_results_arrived'
class ConnectionArchive: class ConnectionArchive:
def __init__(self): def __init__(self):
pass
class ConnectionArchive313(ConnectionArchive):
def __init__(self):
ConnectionArchive.__init__(self)
self.archiving_313_supported = False
self.mam_awaiting_disco_result = {}
gajim.ged.register_event_handler('raw-iq-received', ged.CORE,
self._nec_raw_iq_313_received)
gajim.ged.register_event_handler('agent-info-error-received', ged.CORE,
self._nec_agent_info_error)
gajim.ged.register_event_handler('agent-info-received', ged.CORE,
self._nec_agent_info)
def cleanup(self):
gajim.ged.remove_event_handler('raw-iq-received', ged.CORE,
self._nec_raw_iq_313_received)
def _nec_agent_info_error(self, obj):
if obj.jid in self.mam_awaiting_disco_result:
log.warn('Unable to discover %s, ignoring those logs', obj.jid)
del self.mam_awaiting_disco_result[obj.jid]
def _nec_agent_info(self, obj):
if obj.jid in self.mam_awaiting_disco_result:
for identity in obj.identities:
if identity['category'] == 'conference':
# it's a groupchat
for with_, direction, tim, msg_txt, res in \
self.mam_awaiting_disco_result[obj.jid]:
gajim.logger.get_jid_id(with_, 'ROOM')
gajim.logger.save_if_not_exists(with_, direction, tim,
msg=msg_txt, nick=res)
del self.mam_awaiting_disco_result[obj.jid]
return
# it's not a groupchat
for with_, direction, tim, msg_txt, res in \
self.mam_awaiting_disco_result[obj.jid]:
gajim.logger.get_jid_id(with_)
gajim.logger.save_if_not_exists(with_, direction, tim,
msg=msg_txt)
del self.mam_awaiting_disco_result[obj.jid]
def _nec_raw_iq_313_received(self, obj):
if obj.conn.name != self.name:
return
id_ = obj.stanza.getID()
if id_ not in self.awaiting_answers:
return
if self.awaiting_answers[id_][0] == MAM_RESULTS_ARRIVED:
query = obj.stanza.getTag('query', namespace=nbxmpp.NS_MAM)
if query:
set_ = query.getTag('set', namespace=nbxmpp.NS_RSM)
if set_:
last = set_.getTagData('last')
if last:
gajim.config.set('last_mam_id', last)
self.request_archive(after=last)
del self.awaiting_answers[id_]
def request_archive(self, start=None, end=None, with_=None, after=None,
max=30):
iq_ = nbxmpp.Iq('get')
query = iq_.setTag('query', namespace=nbxmpp.NS_MAM)
if start:
query.addChild('start', payload=start)
if end:
query.addChild('end', payload=end)
if with_:
query.addChild('with', payload=with_)
set_ = query.setTag('set', namespace=nbxmpp.NS_RSM)
set_.setTagData('max', max)
if after:
set_.setTagData('after', after)
id_ = self.connection.getAnID()
iq_.setID(id_)
self.awaiting_answers[id_] = (MAM_RESULTS_ARRIVED, )
self.connection.send(iq_)
class ConnectionArchive136(ConnectionArchive):
def __init__(self):
ConnectionArchive.__init__(self)
self.archiving_136_supported = False
self.archive_auto_supported = False self.archive_auto_supported = False
self.archive_manage_supported = False self.archive_manage_supported = False
self.archive_manual_supported = False self.archive_manual_supported = False
...@@ -45,11 +133,89 @@ def __init__(self): ...@@ -45,11 +133,89 @@ def __init__(self):
gajim.ged.register_event_handler( gajim.ged.register_event_handler(
'archiving-preferences-changed-received', ged.CORE, 'archiving-preferences-changed-received', ged.CORE,
self._nec_archiving_changed_received) self._nec_archiving_changed_received)
gajim.ged.register_event_handler('raw-iq-received', ged.CORE,
self._nec_raw_iq_136_received)
def cleanup(self): def cleanup(self):
gajim.ged.remove_event_handler( gajim.ged.remove_event_handler(
'archiving-preferences-changed-received', ged.CORE, 'archiving-preferences-changed-received', ged.CORE,
self._nec_archiving_changed_received) self._nec_archiving_changed_received)
gajim.ged.remove_event_handler('raw-iq-received', ged.CORE,
self._nec_raw_iq_136_received)
def _nec_raw_iq_136_received(self, obj):
if obj.conn.name != self.name:
return
id_ = obj.stanza.getID()
if id_ not in self.awaiting_answers:
return
if self.awaiting_answers[id_][0] == ARCHIVING_COLLECTIONS_ARRIVED:
del self.awaiting_answers[id_]
# TODO
print('ARCHIVING_COLLECTIONS_ARRIVED')
elif self.awaiting_answers[id_][0] == ARCHIVING_COLLECTION_ARRIVED:
def save_if_not_exists(with_, nick, direction, tim, payload):
assert len(payload) == 1, 'got several archiving messages in' +\
' the same time %s' % ''.join(payload)
if payload[0].getName() == 'body':
gajim.logger.save_if_not_exists(with_, direction, tim,
msg=payload[0].getData(), nick=nick)
elif payload[0].getName() == 'message':
print('Not implemented')
chat = iq_obj.getTag('chat')
if chat:
with_ = chat.getAttr('with')
start_ = chat.getAttr('start')
tim = helpers.datetime_tuple(start_)
tim = timegm(tim)
nb = 0
for element in chat.getChildren():
try:
secs = int(element.getAttr('secs'))
except TypeError:
secs = 0
if secs:
tim += secs
nick = element.getAttr('name')
if element.getName() == 'from':
save_if_not_exists(with_, nick, 'from', localtime(tim),
element.getPayload())
nb += 1
if element.getName() == 'to':
save_if_not_exists(with_, nick, 'to', localtime(tim),
element.getPayload())
nb += 1
set_ = chat.getTag('set')
first = set_.getTag('first')
if first:
try:
index = int(first.getAttr('index'))
except TypeError:
index = 0
try:
count = int(set_.getTagData('count'))
except TypeError:
count = 0
if count > index + nb:
# Request the next page