2 ## src/common/connection_handlers.py
4 ## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
5 ## Junglecow J <junglecow AT gmail.com>
6 ## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
7 ## Travis Shirk <travis AT pobox.com>
8 ## Nikos Kouremenos <kourem AT gmail.com>
9 ## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
10 ## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
11 ## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
12 ## Jean-Marie Traissard <jim AT lapin.org>
13 ## Stephan Erb <steve-e AT h3c.de>
14 ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
16 ## This file is part of Gajim.
18 ## Gajim is free software; you can redistribute it and/or modify
19 ## it under the terms of the GNU General Public License as published
20 ## by the Free Software Foundation; version 3 only.
22 ## Gajim is distributed in the hope that it will be useful,
23 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
24 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 ## GNU General Public License for more details.
27 ## You should have received a copy of the GNU General Public License
28 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
37 from time
import (altzone
, daylight
, gmtime
, localtime
, mktime
, strftime
,
38 time
as time_time
, timezone
, tzname
)
39 from calendar
import timegm
42 import common
.caps_cache
as capscache
44 from common
import helpers
45 from common
import gajim
46 from common
import exceptions
47 from common
import dataforms
48 from common
.commands
import ConnectionCommands
49 from common
.pubsub
import ConnectionPubSub
50 from common
.pep
import ConnectionPEP
51 from common
.protocol
.caps
import ConnectionCaps
52 from common
.protocol
.bytestream
import ConnectionSocks5Bytestream
53 from common
.protocol
.bytestream
import ConnectionIBBytestream
54 from common
.message_archiving
import ConnectionArchive
55 from common
.message_archiving
import ARCHIVING_COLLECTIONS_ARRIVED
56 from common
.message_archiving
import ARCHIVING_COLLECTION_ARRIVED
57 from common
.message_archiving
import ARCHIVING_MODIFICATIONS_ARRIVED
58 from common
.connection_handlers_events
import *
60 from common
import ged
61 from common
import nec
62 from common
.nec
import NetworkEvent
64 if gajim
.HAVE_FARSIGHT
:
65 from common
.jingle
import ConnectionJingle
67 class ConnectionJingle():
70 def _JingleCB(self
, con
, stanza
):
73 from common
import dbus_support
74 if dbus_support
.supported
:
76 from music_track_listener
import MusicTrackListener
79 log
= logging
.getLogger('gajim.c.connection_handlers')
81 # kind of events we can wait for an answer
82 VCARD_PUBLISHED
= 'vcard_published'
83 VCARD_ARRIVED
= 'vcard_arrived'
84 AGENT_REMOVED
= 'agent_removed'
85 METACONTACTS_ARRIVED
= 'metacontacts_arrived'
86 ROSTER_ARRIVED
= 'roster_arrived'
87 DELIMITER_ARRIVED
= 'delimiter_arrived'
88 PRIVACY_ARRIVED
= 'privacy_arrived'
89 PEP_CONFIG
= 'pep_config'
95 log
.debug(_('Unable to load idle module'))
99 class ConnectionDisco
:
101 Holds xmpppy handlers and public methods for discover services
104 def discoverItems(self
, jid
, node
=None, id_prefix
=None):
106 According to XEP-0030:
108 name, node, action is optional.
110 self
._discover
(common
.xmpp
.NS_DISCO_ITEMS
, jid
, node
, id_prefix
)
112 def discoverInfo(self
, jid
, node
=None, id_prefix
=None):
114 According to XEP-0030:
115 For identity: category, type is mandatory, name is optional.
116 For feature: var is mandatory.
118 self
._discover
(common
.xmpp
.NS_DISCO_INFO
, jid
, node
, id_prefix
)
120 def request_register_agent_info(self
, agent
):
121 if not self
.connection
or self
.connected
< 2:
123 iq
= common
.xmpp
.Iq('get', common
.xmpp
.NS_REGISTER
, to
=agent
)
124 id_
= self
.connection
.getAnID()
126 # Wait the answer during 30 secondes
127 self
.awaiting_timeouts
[gajim
.idlequeue
.current_time() + 30] = (id_
,
128 _('Registration information for transport %s has not arrived in '
130 self
.connection
.SendAndCallForResponse(iq
, self
._ReceivedRegInfo
,
133 def _agent_registered_cb(self
, con
, resp
, agent
):
134 if resp
.getType() == 'result':
135 self
.dispatch('INFORMATION', (_('Registration succeeded'),
136 _('Registration with agent %s succeeded') % agent
))
137 self
.request_subscription(agent
, auto_auth
=True)
138 self
.agent_registrations
[agent
]['roster_push'] = True
139 if self
.agent_registrations
[agent
]['sub_received']:
140 p
= common
.xmpp
.Presence(agent
, 'subscribed')
142 self
.connection
.send(p
)
143 if resp
.getType() == 'error':
144 self
.dispatch('ERROR', (_('Registration failed'), _('Registration '
145 'with agent %(agent)s failed with error %(error)s: '
146 '%(error_msg)s') % {'agent': agent
, 'error': resp
.getError(),
147 'error_msg': resp
.getErrorMsg()}))
149 def register_agent(self
, agent
, info
, is_form
=False):
150 if not self
.connection
or self
.connected
< 2:
153 iq
= common
.xmpp
.Iq('set', common
.xmpp
.NS_REGISTER
, to
=agent
)
154 query
= iq
.getTag('query')
155 info
.setAttr('type', 'submit')
156 query
.addChild(node
=info
)
157 self
.connection
.SendAndCallForResponse(iq
,
158 self
._agent
_registered
_cb
, {'agent': agent
})
161 common
.xmpp
.features_nb
.register(self
.connection
, agent
, info
,
162 self
._agent
_registered
_cb
, {'agent': agent
})
163 self
.agent_registrations
[agent
] = {'roster_push': False,
164 'sub_received': False}
166 def _discover(self
, ns
, jid
, node
=None, id_prefix
=None):
167 if not self
.connection
or self
.connected
< 2:
169 iq
= common
.xmpp
.Iq(typ
='get', to
=jid
, queryNS
=ns
)
171 id_
= self
.connection
.getAnID()
172 iq
.setID('%s%s' % (id_prefix
, id_
))
174 iq
.setQuerynode(node
)
175 self
.connection
.send(iq
)
177 def _ReceivedRegInfo(self
, con
, resp
, agent
):
178 common
.xmpp
.features_nb
._ReceivedRegInfo
(con
, resp
, agent
)
179 self
._IqCB
(con
, resp
)
181 def _discoGetCB(self
, con
, iq_obj
):
185 if not self
.connection
or self
.connected
< 2:
187 frm
= helpers
.get_full_jid_from_iq(iq_obj
)
188 to
= unicode(iq_obj
.getAttr('to'))
189 id_
= unicode(iq_obj
.getAttr('id'))
190 iq
= common
.xmpp
.Iq(to
=frm
, typ
='result', queryNS
=common
.xmpp
.NS_DISCO
,
192 iq
.setAttr('id', id_
)
193 query
= iq
.setTag('query')
194 query
.setAttr('node', 'http://gajim.org#' + gajim
.version
.split('-', 1)[
196 for f
in (common
.xmpp
.NS_BYTESTREAM
, common
.xmpp
.NS_SI
,
197 common
.xmpp
.NS_FILE
, common
.xmpp
.NS_COMMANDS
):
198 feature
= common
.xmpp
.Node('feature')
199 feature
.setAttr('var', f
)
200 query
.addChild(node
=feature
)
202 self
.connection
.send(iq
)
203 raise common
.xmpp
.NodeProcessed
205 def _DiscoverItemsErrorCB(self
, con
, iq_obj
):
206 log
.debug('DiscoverItemsErrorCB')
207 gajim
.nec
.push_incoming_event(AgentItemsErrorReceivedEvent(None,
208 conn
=self
, stanza
=iq_obj
))
210 def _DiscoverItemsCB(self
, con
, iq_obj
):
211 log
.debug('DiscoverItemsCB')
212 gajim
.nec
.push_incoming_event(AgentItemsReceivedEvent(None, conn
=self
,
215 def _DiscoverItemsGetCB(self
, con
, iq_obj
):
216 log
.debug('DiscoverItemsGetCB')
218 if not self
.connection
or self
.connected
< 2:
221 if self
.commandItemsQuery(con
, iq_obj
):
222 raise common
.xmpp
.NodeProcessed
223 node
= iq_obj
.getTagAttr('query', 'node')
225 result
= iq_obj
.buildReply('result')
226 self
.connection
.send(result
)
227 raise common
.xmpp
.NodeProcessed
228 if node
== common
.xmpp
.NS_COMMANDS
:
229 self
.commandListQuery(con
, iq_obj
)
230 raise common
.xmpp
.NodeProcessed
232 def _DiscoverInfoGetCB(self
, con
, iq_obj
):
233 log
.debug('DiscoverInfoGetCB')
234 if not self
.connection
or self
.connected
< 2:
236 q
= iq_obj
.getTag('query')
237 node
= q
.getAttr('node')
239 if self
.commandInfoQuery(con
, iq_obj
):
240 raise common
.xmpp
.NodeProcessed
242 id_
= unicode(iq_obj
.getAttr('id'))
243 if id_
[:6] == 'Gajim_':
244 # We get this request from echo.server
245 raise common
.xmpp
.NodeProcessed
247 iq
= iq_obj
.buildReply('result')
248 q
= iq
.getTag('query')
250 q
.setAttr('node', node
)
251 q
.addChild('identity', attrs
=gajim
.gajim_identity
)
252 client_version
= 'http://gajim.org#' + gajim
.caps_hash
[self
.name
]
254 if node
in (None, client_version
):
255 for f
in gajim
.gajim_common_features
:
256 q
.addChild('feature', attrs
={'var': f
})
257 for f
in gajim
.gajim_optional_features
[self
.name
]:
258 q
.addChild('feature', attrs
={'var': f
})
261 self
.connection
.send(iq
)
262 raise common
.xmpp
.NodeProcessed
264 def _DiscoverInfoErrorCB(self
, con
, iq_obj
):
265 log
.debug('DiscoverInfoErrorCB')
266 gajim
.nec
.push_incoming_event(AgentInfoErrorReceivedEvent(None,
267 conn
=self
, stanza
=iq_obj
))
269 def _DiscoverInfoCB(self
, con
, iq_obj
):
270 log
.debug('DiscoverInfoCB')
271 if not self
.connection
or self
.connected
< 2:
273 gajim
.nec
.push_incoming_event(AgentInfoReceivedEvent(None, conn
=self
,
276 class ConnectionVcard
:
278 self
.vcard_sha
= None
279 self
.vcard_shas
= {} # sha of contacts
280 # list of gc jids so that vcard are saved in a folder
283 def add_sha(self
, p
, send_caps
=True):
284 c
= p
.setTag('x', namespace
=common
.xmpp
.NS_VCARD_UPDATE
)
285 if self
.vcard_sha
is not None:
286 c
.setTagData('photo', self
.vcard_sha
)
288 return self
._add
_caps
(p
)
291 def _add_caps(self
, p
):
292 ''' advertise our capabilities in presence stanza (xep-0115)'''
293 c
= p
.setTag('c', namespace
=common
.xmpp
.NS_CAPS
)
294 c
.setAttr('hash', 'sha-1')
295 c
.setAttr('node', 'http://gajim.org')
296 c
.setAttr('ver', gajim
.caps_hash
[self
.name
])
299 def _node_to_dict(self
, node
):
301 for info
in node
.getChildren():
302 name
= info
.getName()
303 if name
in ('ADR', 'TEL', 'EMAIL'): # we can have several
304 dict_
.setdefault(name
, [])
306 for c
in info
.getChildren():
307 entry
[c
.getName()] = c
.getData()
308 dict_
[name
].append(entry
)
309 elif info
.getChildren() == []:
310 dict_
[name
] = info
.getData()
313 for c
in info
.getChildren():
314 dict_
[name
][c
.getName()] = c
.getData()
317 def _save_vcard_to_hd(self
, full_jid
, card
):
318 jid
, nick
= gajim
.get_room_and_nick_from_fjid(full_jid
)
319 puny_jid
= helpers
.sanitize_filename(jid
)
320 path
= os
.path
.join(gajim
.VCARD_PATH
, puny_jid
)
321 if jid
in self
.room_jids
or os
.path
.isdir(path
):
324 # remove room_jid file if needed
325 if os
.path
.isfile(path
):
327 # create folder if needed
328 if not os
.path
.isdir(path
):
330 puny_nick
= helpers
.sanitize_filename(nick
)
331 path_to_file
= os
.path
.join(gajim
.VCARD_PATH
, puny_jid
, puny_nick
)
335 fil
= open(path_to_file
, 'w')
339 self
.dispatch('ERROR', (_('Disk Write Error'), str(e
)))
341 def get_cached_vcard(self
, fjid
, is_fake_jid
=False):
343 Return the vcard as a dict.
344 Return {} if vcard was too old.
345 Return None if we don't have cached vcard.
347 jid
, nick
= gajim
.get_room_and_nick_from_fjid(fjid
)
348 puny_jid
= helpers
.sanitize_filename(jid
)
350 puny_nick
= helpers
.sanitize_filename(nick
)
351 path_to_file
= os
.path
.join(gajim
.VCARD_PATH
, puny_jid
, puny_nick
)
353 path_to_file
= os
.path
.join(gajim
.VCARD_PATH
, puny_jid
)
354 if not os
.path
.isfile(path_to_file
):
356 # We have the vcard cached
357 f
= open(path_to_file
)
361 card
= common
.xmpp
.Node(node
=c
)
363 # We are unable to parse it. Remove it
364 os
.remove(path_to_file
)
366 vcard
= self
._node
_to
_dict
(card
)
368 if not isinstance(vcard
['PHOTO'], dict):
370 elif 'SHA' in vcard
['PHOTO']:
371 cached_sha
= vcard
['PHOTO']['SHA']
372 if jid
in self
.vcard_shas
and self
.vcard_shas
[jid
] != \
374 # user change his vcard so don't use the cached one
377 vcard
['resource'] = gajim
.get_resource_from_jid(fjid
)
380 def request_vcard(self
, jid
=None, groupchat_jid
=None):
384 If groupchat_jid is not null, it means we request a vcard to a fake jid,
385 like in private messages in groupchat. jid can be the real jid of the
386 contact, but we want to consider it comes from a fake jid
388 if not self
.connection
or self
.connected
< 2:
390 iq
= common
.xmpp
.Iq(typ
='get')
393 iq
.setTag(common
.xmpp
.NS_VCARD
+ ' vCard')
395 id_
= self
.connection
.getAnID()
399 j
= gajim
.get_jid_from_account(self
.name
)
400 self
.awaiting_answers
[id_
] = (VCARD_ARRIVED
, j
, groupchat_jid
)
402 room_jid
= gajim
.get_room_and_nick_from_fjid(groupchat_jid
)[0]
403 if not room_jid
in self
.room_jids
:
404 self
.room_jids
.append(room_jid
)
405 self
.groupchat_jids
[id_
] = groupchat_jid
406 self
.connection
.send(iq
)
408 def send_vcard(self
, vcard
):
409 if not self
.connection
or self
.connected
< 2:
411 iq
= common
.xmpp
.Iq(typ
='set')
412 iq2
= iq
.setTag(common
.xmpp
.NS_VCARD
+ ' vCard')
416 if isinstance(vcard
[i
], dict):
417 iq3
= iq2
.addChild(i
)
419 iq3
.addChild(j
).setData(vcard
[i
][j
])
420 elif isinstance(vcard
[i
], list):
422 iq3
= iq2
.addChild(i
)
424 iq3
.addChild(k
).setData(j
[k
])
426 iq2
.addChild(i
).setData(vcard
[i
])
428 id_
= self
.connection
.getAnID()
430 self
.connection
.send(iq
)
432 our_jid
= gajim
.get_jid_from_account(self
.name
)
433 # Add the sha of the avatar
434 if 'PHOTO' in vcard
and isinstance(vcard
['PHOTO'], dict) and \
435 'BINVAL' in vcard
['PHOTO']:
436 photo
= vcard
['PHOTO']['BINVAL']
437 photo_decoded
= base64
.decodestring(photo
)
438 gajim
.interface
.save_avatar_files(our_jid
, photo_decoded
)
439 avatar_sha
= hashlib
.sha1(photo_decoded
).hexdigest()
440 iq2
.getTag('PHOTO').setTagData('SHA', avatar_sha
)
442 gajim
.interface
.remove_avatar_files(our_jid
)
444 self
.awaiting_answers
[id_
] = (VCARD_PUBLISHED
, iq2
)
446 def _IqCB(self
, con
, iq_obj
):
449 gajim
.nec
.push_incoming_event(NetworkEvent('raw-iq-received',
450 conn
=self
, stanza
=iq_obj
))
452 # Check if we were waiting a timeout for this id
454 for tim
in self
.awaiting_timeouts
:
455 if id_
== self
.awaiting_timeouts
[tim
][0]:
459 del self
.awaiting_timeouts
[found_tim
]
461 if id_
not in self
.awaiting_answers
:
463 if self
.awaiting_answers
[id_
][0] == VCARD_PUBLISHED
:
464 if iq_obj
.getType() == 'result':
465 vcard_iq
= self
.awaiting_answers
[id_
][1]
467 if vcard_iq
.getTag('PHOTO') and vcard_iq
.getTag('PHOTO').getTag(
469 new_sha
= vcard_iq
.getTag('PHOTO').getTagData('SHA')
474 our_jid
= gajim
.get_jid_from_account(self
.name
)
475 self
._save
_vcard
_to
_hd
(our_jid
, vcard_iq
)
477 # Send new presence if sha changed and we are not invisible
478 if self
.vcard_sha
!= new_sha
and gajim
.SHOW_LIST
[
479 self
.connected
] != 'invisible':
480 if not self
.connection
or self
.connected
< 2:
482 self
.vcard_sha
= new_sha
483 sshow
= helpers
.get_xmpp_show(gajim
.SHOW_LIST
[
485 p
= common
.xmpp
.Presence(typ
=None, priority
=self
.priority
,
486 show
=sshow
, status
=self
.status
)
488 self
.connection
.send(p
)
489 gajim
.nec
.push_incoming_event(VcardPublishedEvent(None,
491 elif iq_obj
.getType() == 'error':
492 gajim
.nec
.push_incoming_event(VcardNotPublishedEvent(None,
494 elif self
.awaiting_answers
[id_
][0] == VCARD_ARRIVED
:
495 # If vcard is empty, we send to the interface an empty vcard so that
496 # it knows it arrived
497 jid
= self
.awaiting_answers
[id_
][1]
498 groupchat_jid
= self
.awaiting_answers
[id_
][2]
501 # We do as if it comes from the fake_jid
503 our_jid
= gajim
.get_jid_from_account(self
.name
)
504 if (not iq_obj
.getTag('vCard') and iq_obj
.getType() == 'result') or\
505 iq_obj
.getType() == 'error':
506 if id_
in self
.groupchat_jids
:
507 frm
= self
.groupchat_jids
[id_
]
508 del self
.groupchat_jids
[id_
]
509 if frm
and frm
!= our_jid
:
510 # Write an empty file
511 self
._save
_vcard
_to
_hd
(frm
, '')
512 jid
, resource
= gajim
.get_room_and_nick_from_fjid(frm
)
513 vcard
= {'jid': jid
, 'resource': resource
}
514 gajim
.nec
.push_incoming_event(VcardReceivedEvent(None,
515 conn
=self
, vcard_dict
=vcard
))
516 elif self
.awaiting_answers
[id_
][0] == AGENT_REMOVED
:
517 jid
= self
.awaiting_answers
[id_
][1]
518 gajim
.nec
.push_incoming_event(AgentRemovedEvent(None, conn
=self
,
520 elif self
.awaiting_answers
[id_
][0] == METACONTACTS_ARRIVED
:
521 if not self
.connection
:
523 if iq_obj
.getType() == 'result':
524 gajim
.nec
.push_incoming_event(MetacontactsReceivedEvent(None,
525 conn
=self
, stanza
=iq_obj
))
527 if iq_obj
.getErrorCode() not in ('403', '406', '404'):
528 self
.private_storage_supported
= False
529 self
.get_roster_delimiter()
530 elif self
.awaiting_answers
[id_
][0] == DELIMITER_ARRIVED
:
531 if not self
.connection
:
533 if iq_obj
.getType() == 'result':
534 query
= iq_obj
.getTag('query')
535 delimiter
= query
.getTagData('roster')
537 self
.nested_group_delimiter
= delimiter
539 self
.set_roster_delimiter('::')
541 self
.private_storage_supported
= False
543 # We can now continue connection by requesting the roster
544 self
.request_roster()
545 elif self
.awaiting_answers
[id_
][0] == ROSTER_ARRIVED
:
546 if iq_obj
.getType() == 'result':
547 if not iq_obj
.getTag('query'):
548 account_jid
= gajim
.get_jid_from_account(self
.name
)
549 roster_data
= gajim
.logger
.get_roster(account_jid
)
550 roster
= self
.connection
.getRoster(force
=True)
551 roster
.setRaw(roster_data
)
553 elif self
.awaiting_answers
[id_
][0] == PRIVACY_ARRIVED
:
554 if iq_obj
.getType() != 'error':
555 self
.privacy_rules_supported
= True
556 self
.get_privacy_list('block')
557 elif self
.continue_connect_info
:
558 if self
.continue_connect_info
[0] == 'invisible':
559 # Trying to login as invisible but privacy list not
561 self
.disconnect(on_purpose
=True)
562 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
564 self
.dispatch('ERROR', (_('Invisibility not supported'),
565 _('Account %s doesn\'t support invisibility.') % \
568 # Ask metacontacts before roster
569 self
.get_metacontacts()
570 elif self
.awaiting_answers
[id_
][0] == PEP_CONFIG
:
571 if iq_obj
.getType() == 'error':
573 if not iq_obj
.getTag('pubsub'):
575 conf
= iq_obj
.getTag('pubsub').getTag('configure')
578 node
= conf
.getAttr('node')
579 form_tag
= conf
.getTag('x', namespace
=common
.xmpp
.NS_DATA
)
581 form
= common
.dataforms
.ExtendForm(node
=form_tag
)
582 gajim
.nec
.push_incoming_event(PEPConfigReceivedEvent(None,
583 conn
=self
, node
=node
, form
=form
))
585 elif self
.awaiting_answers
[id_
][0] == ARCHIVING_COLLECTIONS_ARRIVED
:
587 print 'ARCHIVING_COLLECTIONS_ARRIVED'
589 elif self
.awaiting_answers
[id_
][0] == ARCHIVING_COLLECTION_ARRIVED
:
590 def save_if_not_exists(with_
, nick
, direction
, tim
, payload
):
591 assert len(payload
) == 1, 'got several archiving messages in' +\
592 ' the same time %s' % ''.join(payload
)
593 if payload
[0].getName() == 'body':
594 gajim
.logger
.save_if_not_exists(with_
, direction
, tim
,
595 msg
=payload
[0].getData(), nick
=nick
)
596 elif payload
[0].getName() == 'message':
597 print 'Not implemented'
598 chat
= iq_obj
.getTag('chat')
600 with_
= chat
.getAttr('with')
601 start_
= chat
.getAttr('start')
602 tim
= helpers
.datetime_tuple(start_
)
605 for element
in chat
.getChildren():
607 secs
= int(element
.getAttr('secs'))
612 nick
= element
.getAttr('name')
613 if element
.getName() == 'from':
614 save_if_not_exists(with_
, nick
, 'from', localtime(tim
),
615 element
.getPayload())
617 if element
.getName() == 'to':
618 save_if_not_exists(with_
, nick
, 'to', localtime(tim
),
619 element
.getPayload())
621 set_
= chat
.getTag('set')
622 first
= set_
.getTag('first')
625 index
= int(first
.getAttr('index'))
629 count
= int(set_
.getTagData('count'))
632 if count
> index
+ nb
:
633 # Request the next page
634 after
= element
.getTagData('last')
635 self
.request_collection_page(with_
, start_
, after
=after
)
637 elif self
.awaiting_answers
[id_
][0] == ARCHIVING_MODIFICATIONS_ARRIVED
:
638 modified
= iq_obj
.getTag('modified')
640 for element
in modified
.getChildren():
641 if element
.getName() == 'changed':
642 with_
= element
.getAttr('with')
643 start_
= element
.getAttr('start')
644 self
.request_collection_page(with_
, start_
)
645 elif element
.getName() == 'removed':
649 del self
.awaiting_answers
[id_
]
651 def _vCardCB(self
, con
, vc
):
653 Called when we receive a vCard Parse the vCard and send it to plugins
655 if not vc
.getTag('vCard'):
657 if not vc
.getTag('vCard').getNamespace() == common
.xmpp
.NS_VCARD
:
660 frm_iq
= vc
.getFrom()
661 our_jid
= gajim
.get_jid_from_account(self
.name
)
663 if id_
in self
.groupchat_jids
:
664 who
= self
.groupchat_jids
[id_
]
665 frm
, resource
= gajim
.get_room_and_nick_from_fjid(who
)
666 del self
.groupchat_jids
[id_
]
668 who
= helpers
.get_full_jid_from_iq(vc
)
669 frm
, resource
= gajim
.get_room_and_nick_from_fjid(who
)
672 card
= vc
.getChildren()[0]
673 vcard
= self
._node
_to
_dict
(card
)
675 if 'PHOTO' in vcard
and isinstance(vcard
['PHOTO'], dict) and \
676 'BINVAL' in vcard
['PHOTO']:
677 photo
= vcard
['PHOTO']['BINVAL']
679 photo_decoded
= base64
.decodestring(photo
)
680 avatar_sha
= hashlib
.sha1(photo_decoded
).hexdigest()
687 card
.getTag('PHOTO').setTagData('SHA', avatar_sha
)
690 self
._save
_vcard
_to
_hd
(who
, card
)
691 # Save the decoded avatar to a separate file too, and generate files
692 # for dbus notifications
693 puny_jid
= helpers
.sanitize_filename(frm
)
695 begin_path
= os
.path
.join(gajim
.AVATAR_PATH
, puny_jid
)
697 if frm
in self
.room_jids
:
698 puny_nick
= helpers
.sanitize_filename(resource
)
699 # create folder if needed
700 if not os
.path
.isdir(begin_path
):
701 os
.mkdir(begin_path
, 0700)
702 begin_path
= os
.path
.join(begin_path
, puny_nick
)
703 frm_jid
+= '/' + resource
705 avatar_file
= begin_path
+ '_notif_size_colored.png'
706 if frm_jid
== our_jid
and avatar_sha
!= self
.vcard_sha
:
707 gajim
.interface
.save_avatar_files(frm
, photo_decoded
, puny_nick
)
708 elif frm_jid
!= our_jid
and (not os
.path
.exists(avatar_file
) or \
709 frm_jid
not in self
.vcard_shas
or \
710 avatar_sha
!= self
.vcard_shas
[frm_jid
]):
711 gajim
.interface
.save_avatar_files(frm
, photo_decoded
, puny_nick
)
713 self
.vcard_shas
[frm_jid
] = avatar_sha
714 elif frm
in self
.vcard_shas
:
715 del self
.vcard_shas
[frm
]
717 for ext
in ('.jpeg', '.png', '_notif_size_bw.png',
718 '_notif_size_colored.png'):
719 path
= begin_path
+ ext
720 if os
.path
.isfile(path
):
724 vcard
['resource'] = resource
725 gajim
.nec
.push_incoming_event(VcardReceivedEvent(None, conn
=self
,
727 if frm_jid
== our_jid
:
728 # we re-send our presence with sha if has changed and if we are
730 if self
.vcard_sha
== avatar_sha
:
732 self
.vcard_sha
= avatar_sha
733 if gajim
.SHOW_LIST
[self
.connected
] == 'invisible':
735 if not self
.connection
:
737 sshow
= helpers
.get_xmpp_show(gajim
.SHOW_LIST
[self
.connected
])
738 p
= common
.xmpp
.Presence(typ
=None, priority
=self
.priority
,
739 show
=sshow
, status
=self
.status
)
741 self
.connection
.send(p
)
743 # basic connection handlers used here and in zeroconf
744 class ConnectionHandlersBase
:
746 # List of IDs we are waiting answers for {id: (type_of_request, data), }
747 self
.awaiting_answers
= {}
748 # List of IDs that will produce a timeout is answer doesn't arrive
749 # {time_of_the_timeout: (id, message to send to gui), }
750 self
.awaiting_timeouts
= {}
751 # keep the jids we auto added (transports contacts) to not send the
752 # SUBSCRIBED event to gui
753 self
.automatically_added
= []
754 # IDs of jabber:iq:last requests
757 # keep track of sessions this connection has with other JIDs
760 gajim
.ged
.register_event_handler('iq-error-received', ged
.CORE
,
761 self
._nec
_iq
_error
_received
)
762 gajim
.ged
.register_event_handler('presence-received', ged
.CORE
,
763 self
._nec
_presence
_received
)
764 gajim
.ged
.register_event_handler('message-received', ged
.CORE
,
765 self
._nec
_message
_received
)
766 gajim
.ged
.register_event_handler('decrypted-message-received', ged
.CORE
,
767 self
._nec
_decrypted
_message
_received
)
770 gajim
.ged
.remove_event_handler('iq-error-received', ged
.CORE
,
771 self
._nec
_iq
_error
_received
)
772 gajim
.ged
.remove_event_handler('presence-received', ged
.CORE
,
773 self
._nec
_presence
_received
)
774 gajim
.ged
.remove_event_handler('message-received', ged
.CORE
,
775 self
._nec
_message
_received
)
776 gajim
.ged
.remove_event_handler('decrypted-message-received', ged
.CORE
,
777 self
._nec
_decrypted
_message
_received
)
779 def _nec_iq_error_received(self
, obj
):
780 if obj
.conn
.name
!= self
.name
:
782 if obj
.id_
in self
.last_ids
:
783 gajim
.nec
.push_incoming_event(LastResultReceivedEvent(None,
784 conn
=self
, stanza
=obj
.stanza
))
787 def _nec_presence_received(self
, obj
):
788 account
= obj
.conn
.name
789 if account
!= self
.name
:
792 resource
= obj
.resource
or ''
794 statuss
= ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
797 obj
.new_show
= statuss
.index(obj
.show
)
799 obj
.contact_list
= []
801 highest
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
802 obj
.was_highest
= (highest
and highest
.resource
== resource
)
805 obj
.contact_list
= gajim
.contacts
.get_contacts(account
, jid
)
808 for c
in obj
.contact_list
:
809 resources
.append(c
.resource
)
810 if c
.resource
== resource
:
814 if obj
.avatar_sha
is not None and obj
.ptype
!= 'error':
815 if obj
.jid
not in self
.vcard_shas
:
816 cached_vcard
= self
.get_cached_vcard(obj
.jid
)
817 if cached_vcard
and 'PHOTO' in cached_vcard
and \
818 'SHA' in cached_vcard
['PHOTO']:
819 self
.vcard_shas
[obj
.jid
] = cached_vcard
['PHOTO']['SHA']
821 self
.vcard_shas
[obj
.jid
] = ''
822 if obj
.avatar_sha
!= self
.vcard_shas
[obj
.jid
]:
823 # avatar has been updated
824 self
.request_vcard(obj
.jid
)
827 if obj
.contact
.show
in statuss
:
828 obj
.old_show
= statuss
.index(obj
.contact
.show
)
830 if obj
.contact_nickname
is not None and \
831 obj
.contact
.contact_name
!= obj
.contact_nickname
:
832 obj
.contact
.contact_name
= obj
.contact_nickname
833 obj
.need_redraw
= True
835 if obj
.old_show
== obj
.new_show
and obj
.contact
.status
== \
836 obj
.status
and obj
.contact
.priority
== obj
.prio
: # no change
839 obj
.contact
= gajim
.contacts
.get_first_contact_from_jid(account
,
842 # Presence of another resource of our jid
843 # Create self contact and add to roster
844 if resource
== obj
.conn
.server_resource
:
846 # Ignore offline presence of unknown self resource
849 obj
.contact
= gajim
.contacts
.create_self_contact(jid
=jid
,
850 account
=account
, show
=obj
.show
, status
=obj
.status
,
851 priority
=obj
.prio
, keyID
=obj
.keyID
,
852 resource
=obj
.resource
)
853 gajim
.contacts
.add_contact(account
, obj
.contact
)
854 obj
.contact_list
.append(obj
.contact
)
855 elif obj
.contact
.show
in statuss
:
856 obj
.old_show
= statuss
.index(obj
.contact
.show
)
857 if (resources
!= [''] and (len(obj
.contact_list
) != 1 or \
858 obj
.contact_list
[0].show
!= 'offline')) and \
859 not gajim
.jid_is_transport(jid
):
860 # Another resource of an existing contact connected
862 obj
.contact
= gajim
.contacts
.copy_contact(obj
.contact
)
863 obj
.contact_list
.append(obj
.contact
)
864 obj
.contact
.resource
= resource
866 obj
.need_add_in_roster
= True
868 if not gajim
.jid_is_transport(jid
) and len(obj
.contact_list
) == 1:
870 if obj
.old_show
== 0 and obj
.new_show
> 1:
871 if not jid
in gajim
.newly_added
[account
]:
872 gajim
.newly_added
[account
].append(jid
)
873 if jid
in gajim
.to_be_removed
[account
]:
874 gajim
.to_be_removed
[account
].remove(jid
)
875 elif obj
.old_show
> 1 and obj
.new_show
== 0 and \
876 obj
.conn
.connected
> 1:
877 if not jid
in gajim
.to_be_removed
[account
]:
878 gajim
.to_be_removed
[account
].append(jid
)
879 if jid
in gajim
.newly_added
[account
]:
880 gajim
.newly_added
[account
].remove(jid
)
881 obj
.need_redraw
= True
883 obj
.contact
.show
= obj
.show
884 obj
.contact
.status
= obj
.status
885 obj
.contact
.priority
= obj
.prio
886 obj
.contact
.keyID
= obj
.keyID
888 obj
.contact
.last_status_time
= obj
.timestamp
889 elif not gajim
.block_signed_in_notifications
[account
]:
890 # We're connected since more that 30 seconds
891 obj
.contact
.last_status_time
= localtime()
892 obj
.contact
.contact_nickname
= obj
.contact_nickname
894 if gajim
.jid_is_transport(jid
):
898 # reset chatstate if needed:
899 # (when contact signs out or has errors)
900 if obj
.show
in ('offline', 'error'):
901 obj
.contact
.our_chatstate
= obj
.contact
.chatstate
= \
902 obj
.contact
.composing_xep
= None
904 # TODO: This causes problems when another
905 # resource signs off!
906 self
.stop_all_active_file_transfers(obj
.contact
)
908 # disable encryption, since if any messages are
909 # lost they'll be not decryptable (note that
910 # this contradicts XEP-0201 - trying to get that
911 # in the XEP, though)
913 # there won't be any sessions here if the contact terminated
914 # their sessions before going offline (which we do)
915 for sess
in self
.get_sessions(jid
):
916 if obj
.fjid
!= str(sess
.jid
):
919 sess
.control
.no_autonegotiation
= False
920 if sess
.enable_encryption
:
922 self
.delete_session(jid
, sess
.thread_id
)
924 if obj
.ptype
== 'unavailable':
925 for jid
in (obj
.jid
, obj
.fjid
):
926 if jid
not in self
.sessions
:
928 # automatically terminate sessions that they haven't sent a
929 # thread ID in, only if other part support thread ID
930 for sess
in self
.sessions
[jid
].values():
931 if not sess
.received_thread_id
:
932 contact
= gajim
.contacts
.get_contact(self
.name
, jid
)
933 if contact
and (contact
.supports(xmpp
.NS_SSN
) or \
934 contact
.supports(xmpp
.NS_ESESSION
)):
936 del self
.sessions
[jid
][sess
.thread_id
]
938 if gajim
.config
.get('log_contact_status_changes') and \
939 gajim
.config
.should_log(self
.name
, obj
.jid
):
941 gajim
.logger
.write('status', obj
.jid
, obj
.status
, obj
.show
)
942 except exceptions
.PysqliteOperationalError
, e
:
943 self
.dispatch('DB_ERROR', (_('Disk Write Error'), str(e
)))
944 except exceptions
.DatabaseMalformed
:
945 pritext
= _('Database Error')
946 sectext
= _('The database file (%s) cannot be read. Try to '
947 'repair it (see http://trac.gajim.org/wiki/DatabaseBackup) '
948 'or remove it (all history will be lost).') % LOG_DB_PATH
949 self
.dispatch('DB_ERROR', (pritext
, sectext
))
950 our_jid
= gajim
.get_jid_from_account(self
.name
)
952 def _nec_message_received(self
, obj
):
953 if obj
.conn
.name
!= self
.name
:
955 if obj
.encrypted
== 'xep200':
957 obj
.stanza
= obj
.session
.decrypt_stanza(obj
.stanza
)
958 obj
.msgtxt
= obj
.stanza
.getBody()
960 gajim
.nec
.push_incoming_event(FailedDecryptEvent(None,
961 conn
=self
, msg_obj
=obj
))
964 if obj
.enc_tag
and self
.USE_GPG
:
965 encmsg
= obj
.enc_tag
.getData()
967 keyID
= gajim
.config
.get_per('accounts', self
.name
, 'keyid')
969 def decrypt_thread(encmsg
, keyID
, obj
):
970 decmsg
= self
.gpg
.decrypt(encmsg
, keyID
)
971 # \x00 chars are not allowed in C (so in GTK)
972 obj
.msgtxt
= helpers
.decode_string(decmsg
.replace('\x00',
974 obj
.encrypted
= 'xep27'
975 gajim
.thread_interface(decrypt_thread
, [encmsg
, keyID
, obj
],
976 self
._on
_message
_decrypted
, [obj
])
978 self
._on
_message
_decrypted
(None, obj
)
980 def _on_message_decrypted(self
, output
, obj
):
981 gajim
.nec
.push_incoming_event(DecryptedMessageReceivedEvent(None,
982 conn
=self
, msg_obj
=obj
))
984 def _nec_decrypted_message_received(self
, obj
):
985 if obj
.conn
.name
!= self
.name
:
989 # TODO: We shouldn't answer if we're invisible!
990 contact
= gajim
.contacts
.get_contact(self
.name
, obj
.jid
)
992 gc_contact
= gajim
.contacts
.get_gc_contact(self
.name
, obj
.jid
, nick
)
993 if obj
.receipt_request_tag
and gajim
.config
.get_per('accounts',
994 self
.name
, 'answer_receipts') and ((contact
and contact
.sub \
995 not in (u
'to', u
'none')) or gc_contact
) and obj
.mtype
!= 'error':
996 receipt
= common
.xmpp
.Message(to
=obj
.fjid
, typ
='chat')
997 receipt
.setID(obj
.id_
)
998 receipt
.setTag('received', namespace
='urn:xmpp:receipts',
999 attrs
={'id': obj
.id_
})
1002 receipt
.setThread(obj
.thread_id
)
1003 self
.connection
.send(receipt
)
1005 # We got our message's receipt
1006 if obj
.receipt_received_tag
and obj
.session
.control
and \
1007 gajim
.config
.get_per('accounts', self
.name
, 'request_receipt'):
1008 obj
.session
.control
.conv_textview
.hide_xep0184_warning(obj
.id_
)
1010 if obj
.mtype
== 'error':
1013 self
.dispatch_error_message(obj
.stanza
, obj
.msgtxt
,
1014 obj
.session
, obj
.fjid
, obj
.timestamp
)
1016 elif obj
.mtype
== 'groupchat':
1017 gajim
.nec
.push_incoming_event(GcMessageReceivedEvent(None,
1018 conn
=self
, msg_obj
=obj
))
1020 elif obj
.invite_tag
is not None:
1021 gajim
.nec
.push_incoming_event(GcInvitationReceivedEvent(None,
1022 conn
=self
, msg_obj
=obj
))
1025 # process and dispatch an error message
1026 def dispatch_error_message(self
, msg
, msgtxt
, session
, frm
, tim
):
1027 error_msg
= msg
.getErrorMsg()
1033 subject
= msg
.getSubject()
1035 if session
.is_loggable():
1037 gajim
.logger
.write('error', frm
, error_msg
, tim
=tim
,
1039 except exceptions
.PysqliteOperationalError
, e
:
1040 self
.dispatch('DB_ERROR', (_('Disk Write Error'), str(e
)))
1041 except exceptions
.DatabaseMalformed
:
1042 pritext
= _('Database Error')
1043 sectext
= _('The database file (%s) cannot be read. Try to '
1044 'repair it (see http://trac.gajim.org/wiki/DatabaseBackup) '
1045 'or remove it (all history will be lost).') % \
1046 common
.logger
.LOG_DB_PATH
1047 self
.dispatch('DB_ERROR', (pritext
, sectext
))
1048 gajim
.nec
.push_incoming_event(MessageErrorEvent(None, conn
=self
,
1049 fjid
=frm
, error_code
=msg
.getErrorCode(), error_msg
=error_msg
,
1050 msg
=msgtxt
, time_
=tim
, session
=session
))
1052 def _LastResultCB(self
, con
, iq_obj
):
1053 log
.debug('LastResultCB')
1054 gajim
.nec
.push_incoming_event(LastResultReceivedEvent(None, conn
=self
,
1057 def get_sessions(self
, jid
):
1059 Get all sessions for the given full jid
1061 if not gajim
.interface
.is_pm_contact(jid
, self
.name
):
1062 jid
= gajim
.get_jid_without_resource(jid
)
1065 return self
.sessions
[jid
].values()
1069 def get_or_create_session(self
, fjid
, thread_id
):
1071 Return an existing session between this connection and 'jid', returns a
1072 new one if none exist
1077 if not gajim
.interface
.is_pm_contact(fjid
, self
.name
):
1079 jid
= gajim
.get_jid_without_resource(fjid
)
1081 session
= self
.find_session(jid
, thread_id
)
1087 return self
.make_new_session(fjid
, thread_id
, type_
='pm')
1089 return self
.make_new_session(fjid
, thread_id
)
1091 def find_session(self
, jid
, thread_id
):
1094 return self
.find_null_session(jid
)
1096 return self
.sessions
[jid
][thread_id
]
1100 def terminate_sessions(self
, send_termination
=False):
1102 Send termination messages and delete all active sessions
1104 for jid
in self
.sessions
:
1105 for thread_id
in self
.sessions
[jid
]:
1106 self
.sessions
[jid
][thread_id
].terminate(send_termination
)
1110 def delete_session(self
, jid
, thread_id
):
1111 if not jid
in self
.sessions
:
1112 jid
= gajim
.get_jid_without_resource(jid
)
1113 if not jid
in self
.sessions
:
1116 del self
.sessions
[jid
][thread_id
]
1118 if not self
.sessions
[jid
]:
1119 del self
.sessions
[jid
]
1121 def find_null_session(self
, jid
):
1123 Find all of the sessions between us and a remote jid in which we haven't
1124 received a thread_id yet and returns the session that we last sent a
1127 sessions
= self
.sessions
[jid
].values()
1129 # sessions that we haven't received a thread ID in
1130 idless
= [s
for s
in sessions
if not s
.received_thread_id
]
1132 # filter out everything except the default session type
1133 chat_sessions
= [s
for s
in idless
if isinstance(s
,
1134 gajim
.default_session_type
)]
1137 # return the session that we last sent a message in
1138 return sorted(chat_sessions
, key
=operator
.attrgetter('last_send'))[
1143 def find_controlless_session(self
, jid
, resource
=None):
1145 Find an active session that doesn't have a control attached
1148 sessions
= self
.sessions
[jid
].values()
1150 # filter out everything except the default session type
1151 chat_sessions
= [s
for s
in sessions
if isinstance(s
,
1152 gajim
.default_session_type
)]
1154 orphaned
= [s
for s
in chat_sessions
if not s
.control
]
1157 orphaned
= [s
for s
in orphaned
if s
.resource
== resource
]
1160 except (KeyError, IndexError):
1163 def make_new_session(self
, jid
, thread_id
=None, type_
='chat', cls
=None):
1165 Create and register a new session
1167 thread_id=None to generate one.
1168 type_ should be 'chat' or 'pm'.
1171 cls
= gajim
.default_session_type
1173 sess
= cls(self
, common
.xmpp
.JID(jid
), thread_id
, type_
)
1175 # determine if this session is a pm session
1176 # if not, discard the resource so that all sessions are stored bare
1177 if not type_
== 'pm':
1178 jid
= gajim
.get_jid_without_resource(jid
)
1180 if not jid
in self
.sessions
:
1181 self
.sessions
[jid
] = {}
1183 self
.sessions
[jid
][sess
.thread_id
] = sess
1187 class ConnectionHandlers(ConnectionArchive
, ConnectionVcard
,
1188 ConnectionSocks5Bytestream
, ConnectionDisco
, ConnectionCommands
,
1189 ConnectionPubSub
, ConnectionPEP
, ConnectionCaps
, ConnectionHandlersBase
,
1190 ConnectionJingle
, ConnectionIBBytestream
):
1193 ConnectionArchive
.__init
__(self
)
1194 ConnectionVcard
.__init
__(self
)
1195 ConnectionSocks5Bytestream
.__init
__(self
)
1196 ConnectionIBBytestream
.__init
__(self
)
1197 ConnectionCommands
.__init
__(self
)
1198 ConnectionPubSub
.__init
__(self
)
1199 ConnectionPEP
.__init
__(self
, account
=self
.name
, dispatcher
=self
,
1200 pubsub_connection
=self
)
1202 # Handle presences BEFORE caps
1203 gajim
.nec
.register_incoming_event(PresenceReceivedEvent
)
1205 ConnectionCaps
.__init
__(self
, account
=self
.name
,
1206 dispatch_event
=self
.dispatch
, capscache
=capscache
.capscache
,
1207 client_caps_factory
=capscache
.create_suitable_client_caps
)
1208 ConnectionJingle
.__init
__(self
)
1209 ConnectionHandlersBase
.__init
__(self
)
1210 self
.gmail_url
= None
1212 # keep the latest subscribed event for each jid to prevent loop when we
1213 # acknowledge presences
1214 self
.subscribed_events
= {}
1215 # IDs of jabber:iq:version requests
1216 self
.version_ids
= []
1217 # IDs of urn:xmpp:time requests
1218 self
.entity_time_ids
= []
1219 # ID of urn:xmpp:ping requests
1220 self
.awaiting_xmpp_ping_id
= None
1221 self
.continue_connect_info
= None
1224 self
.sleeper
= common
.sleepy
.Sleepy()
1229 self
.gmail_last_tid
= None
1230 self
.gmail_last_time
= None
1232 gajim
.nec
.register_incoming_event(PrivateStorageBookmarksReceivedEvent
)
1233 gajim
.nec
.register_incoming_event(BookmarksReceivedEvent
)
1234 gajim
.nec
.register_incoming_event(
1235 PrivateStorageRosternotesReceivedEvent
)
1236 gajim
.nec
.register_incoming_event(RosternotesReceivedEvent
)
1237 gajim
.nec
.register_incoming_event(StreamConflictReceivedEvent
)
1238 gajim
.nec
.register_incoming_event(MessageReceivedEvent
)
1239 gajim
.nec
.register_incoming_event(ArchivingErrorReceivedEvent
)
1240 gajim
.nec
.register_incoming_event(
1241 ArchivingPreferencesChangedReceivedEvent
)
1243 gajim
.ged
.register_event_handler('http-auth-received', ged
.CORE
,
1244 self
._nec
_http
_auth
_received
)
1245 gajim
.ged
.register_event_handler('version-request-received', ged
.CORE
,
1246 self
._nec
_version
_request
_received
)
1247 gajim
.ged
.register_event_handler('last-request-received', ged
.CORE
,
1248 self
._nec
_last
_request
_received
)
1249 gajim
.ged
.register_event_handler('time-request-received', ged
.CORE
,
1250 self
._nec
_time
_request
_received
)
1251 gajim
.ged
.register_event_handler('time-revised-request-received',
1252 ged
.CORE
, self
._nec
_time
_revised
_request
_received
)
1253 gajim
.ged
.register_event_handler('roster-set-received',
1254 ged
.CORE
, self
._nec
_roster
_set
_received
)
1255 gajim
.ged
.register_event_handler('private-storage-bookmarks-received',
1256 ged
.CORE
, self
._nec
_private
_storate
_bookmarks
_received
)
1257 gajim
.ged
.register_event_handler('private-storage-rosternotes-received',
1258 ged
.CORE
, self
._nec
_private
_storate
_rosternotes
_received
)
1259 gajim
.ged
.register_event_handler('roster-received', ged
.CORE
,
1260 self
._nec
_roster
_received
)
1261 gajim
.ged
.register_event_handler('iq-error-received', ged
.CORE
,
1262 self
._nec
_iq
_error
_received
)
1263 gajim
.ged
.register_event_handler('gmail-new-mail-received', ged
.CORE
,
1264 self
._nec
_gmail
_new
_mail
_received
)
1265 gajim
.ged
.register_event_handler('ping-received', ged
.CORE
,
1266 self
._nec
_ping
_received
)
1267 gajim
.ged
.register_event_handler('subscribe-presence-received',
1268 ged
.CORE
, self
._nec
_subscribe
_presence
_received
)
1269 gajim
.ged
.register_event_handler('subscribed-presence-received',
1270 ged
.CORE
, self
._nec
_subscribed
_presence
_received
)
1271 gajim
.ged
.register_event_handler('subscribed-presence-received',
1272 ged
.POSTGUI
, self
._nec
_subscribed
_presence
_received
_end
)
1273 gajim
.ged
.register_event_handler('unsubscribed-presence-received',
1274 ged
.CORE
, self
._nec
_unsubscribed
_presence
_received
)
1275 gajim
.ged
.register_event_handler('unsubscribed-presence-received',
1276 ged
.POSTGUI
, self
._nec
_unsubscribed
_presence
_received
_end
)
1277 gajim
.ged
.register_event_handler('agent-removed', ged
.CORE
,
1278 self
._nec
_agent
_removed
)
1281 ConnectionHandlersBase
.cleanup(self
)
1282 ConnectionCaps
.cleanup(self
)
1283 ConnectionArchive
.cleanup(self
)
1284 ConnectionPubSub
.cleanup(self
)
1285 gajim
.ged
.remove_event_handler('http-auth-received', ged
.CORE
,
1286 self
._nec
_http
_auth
_received
)
1287 gajim
.ged
.remove_event_handler('version-request-received', ged
.CORE
,
1288 self
._nec
_version
_request
_received
)
1289 gajim
.ged
.remove_event_handler('last-request-received', ged
.CORE
,
1290 self
._nec
_last
_request
_received
)
1291 gajim
.ged
.remove_event_handler('time-request-received', ged
.CORE
,
1292 self
._nec
_time
_request
_received
)
1293 gajim
.ged
.remove_event_handler('time-revised-request-received',
1294 ged
.CORE
, self
._nec
_time
_revised
_request
_received
)
1295 gajim
.ged
.remove_event_handler('roster-set-received',
1296 ged
.CORE
, self
._nec
_roster
_set
_received
)
1297 gajim
.ged
.remove_event_handler('private-storage-bookmarks-received',
1298 ged
.CORE
, self
._nec
_private
_storate
_bookmarks
_received
)
1299 gajim
.ged
.remove_event_handler('private-storage-rosternotes-received',
1300 ged
.CORE
, self
._nec
_private
_storate
_rosternotes
_received
)
1301 gajim
.ged
.remove_event_handler('roster-received', ged
.CORE
,
1302 self
._nec
_roster
_received
)
1303 gajim
.ged
.remove_event_handler('iq-error-received', ged
.CORE
,
1304 self
._nec
_iq
_error
_received
)
1305 gajim
.ged
.remove_event_handler('gmail-new-mail-received', ged
.CORE
,
1306 self
._nec
_gmail
_new
_mail
_received
)
1307 gajim
.ged
.remove_event_handler('ping-received', ged
.CORE
,
1308 self
._nec
_ping
_received
)
1309 gajim
.ged
.remove_event_handler('subscribe-presence-received',
1310 ged
.CORE
, self
._nec
_subscribe
_presence
_received
)
1311 gajim
.ged
.remove_event_handler('subscribed-presence-received',
1312 ged
.CORE
, self
._nec
_subscribed
_presence
_received
)
1313 gajim
.ged
.remove_event_handler('subscribed-presence-received',
1314 ged
.POSTGUI
, self
._nec
_subscribed
_presence
_received
_end
)
1315 gajim
.ged
.remove_event_handler('unsubscribed-presence-received',
1316 ged
.CORE
, self
._nec
_unsubscribed
_presence
_received
)
1317 gajim
.ged
.remove_event_handler('unsubscribed-presence-received',
1318 ged
.POSTGUI
, self
._nec
_unsubscribed
_presence
_received
_end
)
1319 gajim
.ged
.remove_event_handler('agent-removed', ged
.CORE
,
1320 self
._nec
_agent
_removed
)
1322 def build_http_auth_answer(self
, iq_obj
, answer
):
1323 if not self
.connection
or self
.connected
< 2:
1326 confirm
= iq_obj
.getTag('confirm')
1327 reply
= iq_obj
.buildReply('result')
1328 if iq_obj
.getName() == 'message':
1329 reply
.addChild(node
=confirm
)
1330 self
.connection
.send(reply
)
1331 elif answer
== 'no':
1332 err
= common
.xmpp
.Error(iq_obj
,
1333 common
.xmpp
.protocol
.ERR_NOT_AUTHORIZED
)
1334 self
.connection
.send(err
)
1336 def _nec_http_auth_received(self
, obj
):
1337 if obj
.conn
.name
!= self
.name
:
1339 if obj
.opt
in ('yes', 'no'):
1340 obj
.conn
.build_http_auth_answer(obj
.stanza
, obj
.opt
)
1343 def _HttpAuthCB(self
, con
, iq_obj
):
1344 log
.debug('HttpAuthCB')
1345 gajim
.nec
.push_incoming_event(HttpAuthReceivedEvent(None, conn
=self
,
1347 raise common
.xmpp
.NodeProcessed
1349 def _ErrorCB(self
, con
, iq_obj
):
1350 log
.debug('ErrorCB')
1351 gajim
.nec
.push_incoming_event(IqErrorReceivedEvent(None, conn
=self
,
1354 def _nec_iq_error_received(self
, obj
):
1355 if obj
.conn
.name
!= self
.name
:
1357 if obj
.id_
in self
.version_ids
:
1358 gajim
.nec
.push_incoming_event(VersionResultReceivedEvent(None,
1359 conn
=self
, stanza
=obj
.stanza
))
1361 if obj
.id_
in self
.entity_time_ids
:
1362 gajim
.nec
.push_incoming_event(TimeResultReceivedEvent(None,
1363 conn
=self
, stanza
=obj
.stanza
))
1366 def _nec_private_storate_bookmarks_received(self
, obj
):
1367 if obj
.conn
.name
!= self
.name
:
1369 resend_to_pubsub
= False
1370 bm_jids
= [b
['jid'] for b
in self
.bookmarks
]
1371 for bm
in obj
.bookmarks
:
1372 if bm
['jid'] not in bm_jids
:
1373 self
.bookmarks
.append(bm
)
1374 # We got a bookmark that was not in pubsub
1375 resend_to_pubsub
= True
1376 if self
.pubsub_supported
and resend_to_pubsub
:
1377 self
.store_bookmarks('pubsub')
1379 def _nec_private_storate_rosternotes_received(self
, obj
):
1380 if obj
.conn
.name
!= self
.name
:
1382 for jid
in obj
.annotations
:
1383 self
.annotations
[jid
] = obj
.annotations
[jid
]
1385 def _PrivateCB(self
, con
, iq_obj
):
1387 Private Data (XEP 048 and 049)
1389 log
.debug('PrivateCB')
1390 gajim
.nec
.push_incoming_event(PrivateStorageReceivedEvent(None,
1391 conn
=self
, stanza
=iq_obj
))
1393 def _SecLabelCB(self
, con
, iq_obj
):
1395 Security Label callback, used for catalogues.
1397 log
.debug('SecLabelCB')
1398 query
= iq_obj
.getTag('catalog')
1399 to
= query
.getAttr('to')
1400 items
= query
.getTags('securitylabel')
1404 label
= item
.getTag('displaymarking').getData()
1405 labels
[label
] = item
1407 if to
not in self
.seclabel_catalogues
:
1408 self
.seclabel_catalogues
[to
] = [[], None, None]
1409 self
.seclabel_catalogues
[to
][1] = labels
1410 self
.seclabel_catalogues
[to
][2] = ll
1411 for callback
in self
.seclabel_catalogues
[to
][0]:
1413 self
.seclabel_catalogues
[to
][0] = []
1415 def seclabel_catalogue_request(self
, to
, callback
):
1416 if to
not in self
.seclabel_catalogues
:
1417 self
.seclabel_catalogues
[to
] = [[], None, None]
1418 self
.seclabel_catalogues
[to
][0].append(callback
)
1420 def _rosterSetCB(self
, con
, iq_obj
):
1421 log
.debug('rosterSetCB')
1422 gajim
.nec
.push_incoming_event(RosterSetReceivedEvent(None, conn
=self
,
1424 raise common
.xmpp
.NodeProcessed
1426 def _nec_roster_set_received(self
, obj
):
1427 if obj
.conn
.name
!= self
.name
:
1429 for jid
in obj
.items
:
1430 item
= obj
.items
[jid
]
1431 gajim
.nec
.push_incoming_event(RosterInfoEvent(None, conn
=self
,
1432 jid
=jid
, nickname
=item
['name'], sub
=item
['sub'],
1433 ask
=item
['ask'], groups
=item
['groups']))
1434 account_jid
= gajim
.get_jid_from_account(self
.name
)
1435 gajim
.logger
.add_or_update_contact(account_jid
, jid
, item
['name'],
1436 item
['sub'], item
['ask'], item
['groups'])
1438 gajim
.config
.set_per('accounts', self
.name
, 'roster_version',
1441 def _VersionCB(self
, con
, iq_obj
):
1442 log
.debug('VersionCB')
1443 if not self
.connection
or self
.connected
< 2:
1445 gajim
.nec
.push_incoming_event(VersionRequestEvent(None, conn
=self
,
1447 raise common
.xmpp
.NodeProcessed
1449 def _nec_version_request_received(self
, obj
):
1450 if obj
.conn
.name
!= self
.name
:
1452 iq_obj
= obj
.stanza
.buildReply('result')
1453 qp
= iq_obj
.getTag('query')
1454 qp
.setTagData('name', 'Gajim')
1455 qp
.setTagData('version', gajim
.version
)
1456 send_os
= gajim
.config
.get_per('accounts', self
.name
, 'send_os_info')
1458 qp
.setTagData('os', helpers
.get_os_info())
1459 self
.connection
.send(iq_obj
)
1461 def _LastCB(self
, con
, iq_obj
):
1463 if not self
.connection
or self
.connected
< 2:
1465 gajim
.nec
.push_incoming_event(LastRequestEvent(None, conn
=self
,
1467 raise common
.xmpp
.NodeProcessed
1469 def _nec_last_request_received(self
, obj
):
1471 if obj
.conn
.name
!= self
.name
:
1473 if HAS_IDLE
and gajim
.config
.get_per('accounts', self
.name
,
1475 iq_obj
= obj
.stanza
.buildReply('result')
1476 qp
= iq_obj
.getTag('query')
1477 qp
.attrs
['seconds'] = int(self
.sleeper
.getIdleSec())
1479 iq_obj
= obj
.stanza
.buildReply('error')
1480 err
= common
.xmpp
.ErrorNode(name
=common
.xmpp
.NS_STANZAS
+ \
1481 ' service-unavailable')
1482 iq_obj
.addChild(node
=err
)
1483 self
.connection
.send(iq_obj
)
1485 def _VersionResultCB(self
, con
, iq_obj
):
1486 log
.debug('VersionResultCB')
1487 gajim
.nec
.push_incoming_event(VersionResultReceivedEvent(None,
1488 conn
=self
, stanza
=iq_obj
))
1490 def _TimeCB(self
, con
, iq_obj
):
1492 if not self
.connection
or self
.connected
< 2:
1494 gajim
.nec
.push_incoming_event(TimeRequestEvent(None, conn
=self
,
1496 raise common
.xmpp
.NodeProcessed
1498 def _nec_time_request_received(self
, obj
):
1499 if obj
.conn
.name
!= self
.name
:
1501 if gajim
.config
.get_per('accounts', self
.name
, 'send_time_info'):
1502 iq_obj
= obj
.stanza
.buildReply('result')
1503 qp
= iq_obj
.getTag('query')
1504 qp
.setTagData('utc', strftime('%Y%m%dT%H:%M:%S', gmtime()))
1505 qp
.setTagData('tz', helpers
.decode_string(tzname
[daylight
]))
1506 qp
.setTagData('display', helpers
.decode_string(strftime('%c',
1509 iq_obj
= obj
.stanza
.buildReply('error')
1510 err
= common
.xmpp
.ErrorNode(name
=common
.xmpp
.NS_STANZAS
+ \
1511 ' service-unavailable')
1512 iq_obj
.addChild(node
=err
)
1513 self
.connection
.send(iq_obj
)
1515 def _TimeRevisedCB(self
, con
, iq_obj
):
1516 log
.debug('TimeRevisedCB')
1517 if not self
.connection
or self
.connected
< 2:
1519 gajim
.nec
.push_incoming_event(TimeRevisedRequestEvent(None, conn
=self
,
1521 raise common
.xmpp
.NodeProcessed
1523 def _nec_time_revised_request_received(self
, obj
):
1524 if obj
.conn
.name
!= self
.name
:
1526 if gajim
.config
.get_per('accounts', self
.name
, 'send_time_info'):
1527 iq_obj
= obj
.stanza
.buildReply('result')
1528 qp
= iq_obj
.setTag('time', namespace
=common
.xmpp
.NS_TIME_REVISED
)
1529 qp
.setTagData('utc', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime()))
1530 isdst
= localtime().tm_isdst
1531 zone
= -(timezone
, altzone
)[isdst
] / 60.0
1532 tzo
= (zone
/ 60, abs(zone
% 60))
1533 qp
.setTagData('tzo', '%+03d:%02d' % (tzo
))
1535 iq_obj
= obj
.stanza
.buildReply('error')
1536 err
= common
.xmpp
.ErrorNode(name
=common
.xmpp
.NS_STANZAS
+ \
1537 ' service-unavailable')
1538 iq_obj
.addChild(node
=err
)
1539 self
.connection
.send(iq_obj
)
1541 def _TimeRevisedResultCB(self
, con
, iq_obj
):
1542 log
.debug('TimeRevisedResultCB')
1543 gajim
.nec
.push_incoming_event(TimeResultReceivedEvent(None, conn
=self
,
1546 def _gMailNewMailCB(self
, con
, iq_obj
):
1548 Called when we get notified of new mail messages in gmail account
1550 log
.debug('gMailNewMailCB')
1551 gajim
.nec
.push_incoming_event(GmailNewMailReceivedEvent(None, conn
=self
,
1553 raise common
.xmpp
.NodeProcessed
1555 def _nec_gmail_new_mail_received(self
, obj
):
1556 if obj
.conn
.name
!= self
.name
:
1558 if not self
.connection
or self
.connected
< 2:
1560 # we'll now ask the server for the exact number of new messages
1561 jid
= gajim
.get_jid_from_account(self
.name
)
1562 log
.debug('Got notification of new gmail e-mail on %s. Asking the '
1563 'server for more info.' % jid
)
1564 iq
= common
.xmpp
.Iq(typ
='get')
1565 query
= iq
.setTag('query')
1566 query
.setNamespace(common
.xmpp
.NS_GMAILNOTIFY
)
1567 # we want only be notified about newer mails
1568 if self
.gmail_last_tid
:
1569 query
.setAttr('newer-than-tid', self
.gmail_last_tid
)
1570 if self
.gmail_last_time
:
1571 query
.setAttr('newer-than-time', self
.gmail_last_time
)
1572 self
.connection
.send(iq
)
1574 def _gMailQueryCB(self
, con
, iq_obj
):
1576 Called when we receive results from Querying the server for mail messages
1579 log
.debug('gMailQueryCB')
1580 gajim
.nec
.push_incoming_event(GMailQueryReceivedEvent(None, conn
=self
,
1582 raise common
.xmpp
.NodeProcessed
1584 def _rosterItemExchangeCB(self
, con
, msg
):
1586 XEP-0144 Roster Item Echange
1588 log
.debug('rosterItemExchangeCB')
1589 gajim
.nec
.push_incoming_event(RosterItemExchangeEvent(None, conn
=self
,
1591 raise common
.xmpp
.NodeProcessed
1593 def _messageCB(self
, con
, msg
):
1595 Called when we receive a message
1597 log
.debug('MessageCB')
1599 gajim
.nec
.push_incoming_event(NetworkEvent('raw-message-received',
1600 conn
=self
, stanza
=msg
, account
=self
.name
))
1602 def _dispatch_gc_msg_with_captcha(self
, stanza
, msg_obj
):
1603 msg_obj
.stanza
= stanza
1604 gajim
.nec
.push_incoming_event(GcMessageReceivedEvent(None,
1605 conn
=self
, msg_obj
=msg_obj
))
1607 def _on_bob_received(self
, conn
, result
, cid
):
1609 Called when we receive BoB data
1611 if cid
not in self
.awaiting_cids
:
1614 if result
.getType() == 'result':
1615 data
= msg
.getTags('data', namespace
=common
.xmpp
.NS_BOB
)
1616 if data
.getAttr('cid') == cid
:
1617 for func
in self
.awaiting_cids
[cid
]:
1621 bob_data
= data
.getData()
1622 def recurs(node
, cid
, data
):
1623 if node
.getData() == 'cid:' + cid
:
1626 for child
in node
.getChildren():
1627 recurs(child
, cid
, data
)
1628 recurs(args
[pos
], cid
, bob_data
)
1630 del self
.awaiting_cids
[cid
]
1633 # An error occured, call callback without modifying data.
1634 for func
in self
.awaiting_cids
[cid
]:
1638 del self
.awaiting_cids
[cid
]
1640 def get_bob_data(self
, cid
, to
, callback
, args
, position
):
1642 Request for BoB (XEP-0231) and when data will arrive, call callback
1643 with given args, after having replaced cid by it's data in
1646 if cid
in self
.awaiting_cids
:
1647 self
.awaiting_cids
[cid
].appends((callback
, args
, position
))
1649 self
.awaiting_cids
[cid
] = [(callback
, args
, position
)]
1650 iq
= common
.xmpp
.Iq(to
=to
, typ
='get')
1651 data
= iq
.addChild(name
='data', attrs
={'cid': cid
},
1652 namespace
=common
.xmpp
.NS_BOB
)
1653 self
.connection
.SendAndCallForResponse(iq
, self
._on
_bob
_received
,
1656 # process and dispatch a groupchat message
1657 def dispatch_gc_message(self
, msg
, frm
, msgtxt
, jid
, tim
):
1658 has_timestamp
= bool(msg
.timestamp
)
1660 statusCode
= msg
.getStatusCode()
1662 displaymarking
= None
1663 seclabel
= msg
.getTag('securitylabel')
1664 if seclabel
and seclabel
.getNamespace() == common
.xmpp
.NS_SECLABEL
:
1665 # Ignore message from room in which we are not
1666 displaymarking
= seclabel
.getTag('displaymarking')
1668 if jid
not in self
.last_history_time
:
1671 captcha
= msg
.getTag('captcha', namespace
=common
.xmpp
.NS_CAPTCHA
)
1673 captcha
= captcha
.getTag('x', namespace
=common
.xmpp
.NS_DATA
)
1674 found
= helpers
.replace_dataform_media(captcha
, msg
)
1676 self
.get_bob_data(uri_data
, frm
, self
.dispatch_gc_message
,
1677 [msg
, frm
, msgtxt
, jid
, tim
], 0)
1679 self
.dispatch('GC_MSG', (frm
, msgtxt
, tim
, has_timestamp
,
1680 msg
.getXHTML(), statusCode
, displaymarking
, captcha
))
1682 tim_int
= int(float(mktime(tim
)))
1683 if gajim
.config
.should_log(self
.name
, jid
) and not \
1684 tim_int
<= self
.last_history_time
[jid
] and msgtxt
and frm
.find('/') >= 0:
1685 # if frm.find('/') < 0, it means message comes from room itself
1686 # usually it hold description and can be send at each connection
1687 # so don't store it in logs
1689 gajim
.logger
.write('gc_msg', frm
, msgtxt
, tim
=tim
)
1690 # store in memory time of last message logged.
1691 # this will also be saved in rooms_last_message_time table
1692 # when we quit this muc
1693 self
.last_history_time
[jid
] = mktime(tim
)
1695 except exceptions
.PysqliteOperationalError
, e
:
1696 self
.dispatch('DB_ERROR', (_('Disk Write Error'), str(e
)))
1697 except exceptions
.DatabaseMalformed
:
1698 pritext
= _('Database Error')
1699 sectext
= _('The database file (%s) cannot be read. Try to '
1700 'repair it (see http://trac.gajim.org/wiki/DatabaseBackup) '
1701 'or remove it (all history will be lost).') % \
1702 common
.logger
.LOG_DB_PATH
1703 self
.dispatch('DB_ERROR', (pritext
, sectext
))
1705 def _presenceCB(self
, con
, prs
):
1707 Called when we receive a presence
1709 log
.debug('PresenceCB')
1710 gajim
.nec
.push_incoming_event(NetworkEvent('raw-pres-received',
1711 conn
=self
, stanza
=prs
))
1713 def _nec_subscribe_presence_received(self
, obj
):
1714 account
= obj
.conn
.name
1715 if account
!= self
.name
:
1717 if gajim
.jid_is_transport(obj
.fjid
) and obj
.fjid
in \
1718 self
.agent_registrations
:
1719 self
.agent_registrations
[obj
.fjid
]['sub_received'] = True
1720 if not self
.agent_registrations
[obj
.fjid
]['roster_push']:
1721 # We'll reply after roster push result
1723 if gajim
.config
.get_per('accounts', self
.name
, 'autoauth') or \
1724 gajim
.jid_is_transport(obj
.fjid
) or obj
.jid
in self
.jids_for_auto_auth \
1725 or obj
.transport_auto_auth
:
1727 p
= xmpp
.Presence(obj
.fjid
, 'subscribed')
1729 self
.connection
.send(p
)
1730 if gajim
.jid_is_transport(obj
.fjid
) or obj
.transport_auto_auth
:
1732 #self.show = 'offline'
1733 #self.status = 'offline'
1736 if obj
.transport_auto_auth
:
1737 self
.automatically_added
.append(obj
.jid
)
1738 self
.request_subscription(obj
.jid
, name
=obj
.user_nick
)
1741 obj
.status
= _('I would like to add you to my roster.')
1743 def _nec_subscribed_presence_received(self
, obj
):
1744 account
= obj
.conn
.name
1745 if account
!= self
.name
:
1747 # BE CAREFUL: no con.updateRosterItem() in a callback
1748 if obj
.jid
in self
.automatically_added
:
1749 self
.automatically_added
.remove(obj
.jid
)
1751 # detect a subscription loop
1752 if obj
.jid
not in self
.subscribed_events
:
1753 self
.subscribed_events
[obj
.jid
] = []
1754 self
.subscribed_events
[obj
.jid
].append(time_time())
1756 if len(self
.subscribed_events
[obj
.jid
]) > 5:
1757 if time_time() - self
.subscribed_events
[obj
.jid
][0] < 5:
1759 self
.subscribed_events
[obj
.jid
] = \
1760 self
.subscribed_events
[obj
.jid
][1:]
1762 gajim
.config
.set_per('account', self
.name
, 'dont_ack_subscription',
1766 def _nec_subscribed_presence_received_end(self
, obj
):
1767 account
= obj
.conn
.name
1768 if account
!= self
.name
:
1770 if not gajim
.config
.get_per('accounts', account
,
1771 'dont_ack_subscription'):
1772 self
.ack_subscribed(obj
.jid
)
1774 def _nec_unsubscribed_presence_received(self
, obj
):
1775 account
= obj
.conn
.name
1776 if account
!= self
.name
:
1778 # detect a unsubscription loop
1779 if obj
.jid
not in self
.subscribed_events
:
1780 self
.subscribed_events
[obj
.jid
] = []
1781 self
.subscribed_events
[obj
.jid
].append(time_time())
1783 if len(self
.subscribed_events
[obj
.jid
]) > 5:
1784 if time_time() - self
.subscribed_events
[obj
.jid
][0] < 5:
1786 self
.subscribed_events
[obj
.jid
] = \
1787 self
.subscribed_events
[obj
.jid
][1:]
1789 gajim
.config
.set_per('account', self
.name
, 'dont_ack_subscription',
1793 def _nec_unsubscribed_presence_received_end(self
, obj
):
1794 account
= obj
.conn
.name
1795 if account
!= self
.name
:
1797 if not gajim
.config
.get_per('accounts', account
,
1798 'dont_ack_subscription'):
1799 self
.ack_unsubscribed(obj
.jid
)
1801 def _nec_agent_removed(self
, obj
):
1802 if obj
.conn
.name
!= self
.name
:
1804 for jid
in obj
.jid_list
:
1805 log
.debug('Removing contact %s due to unregistered transport %s' % \
1807 self
.unsubscribe(jid
)
1808 # Transport contacts can't have 2 resources
1809 if jid
in gajim
.to_be_removed
[self
.name
]:
1810 # This way we'll really remove it
1811 gajim
.to_be_removed
[self
.name
].remove(jid
)
1813 def _StanzaArrivedCB(self
, con
, obj
):
1814 self
.last_io
= gajim
.idlequeue
.current_time()
1816 def _MucOwnerCB(self
, con
, iq_obj
):
1817 log
.debug('MucOwnerCB')
1818 gajim
.nec
.push_incoming_event(MucOwnerReceivedEvent(None, conn
=self
,
1821 def _MucAdminCB(self
, con
, iq_obj
):
1822 log
.debug('MucAdminCB')
1823 gajim
.nec
.push_incoming_event(MucAdminReceivedEvent(None, conn
=self
,
1826 def _IqPingCB(self
, con
, iq_obj
):
1827 log
.debug('IqPingCB')
1828 gajim
.nec
.push_incoming_event(PingReceivedEvent(None, conn
=self
,
1830 raise common
.xmpp
.NodeProcessed
1832 def _nec_ping_received(self
, obj
):
1833 if obj
.conn
.name
!= self
.name
:
1835 if not self
.connection
or self
.connected
< 2:
1837 iq_obj
= obj
.stanza
.buildReply('result')
1838 self
.connection
.send(iq_obj
)
1840 def _PrivacySetCB(self
, con
, iq_obj
):
1842 Privacy lists (XEP 016)
1844 A list has been set.
1846 log
.debug('PrivacySetCB')
1847 if not self
.connection
or self
.connected
< 2:
1849 result
= iq_obj
.buildReply('result')
1850 q
= result
.getTag('query')
1853 self
.connection
.send(result
)
1854 raise common
.xmpp
.NodeProcessed
1856 def _getRoster(self
):
1857 log
.debug('getRosterCB')
1858 if not self
.connection
:
1860 self
.connection
.getRoster(self
._on
_roster
_set
)
1861 self
.discoverItems(gajim
.config
.get_per('accounts', self
.name
,
1862 'hostname'), id_prefix
='Gajim_')
1863 if gajim
.config
.get_per('accounts', self
.name
, 'use_ft_proxies'):
1864 self
.discover_ft_proxies()
1866 def discover_ft_proxies(self
):
1867 cfg_proxies
= gajim
.config
.get_per('accounts', self
.name
,
1868 'file_transfer_proxies')
1869 our_jid
= helpers
.parse_jid(gajim
.get_jid_from_account(self
.name
) + \
1870 '/' + self
.server_resource
)
1872 proxies
= [e
.strip() for e
in cfg_proxies
.split(',')]
1873 for proxy
in proxies
:
1874 gajim
.proxy65_manager
.resolve(proxy
, self
.connection
, our_jid
)
1876 def _on_roster_set(self
, roster
):
1877 gajim
.nec
.push_incoming_event(RosterReceivedEvent(None, conn
=self
,
1878 xmpp_roster
=roster
))
1880 def _nec_roster_received(self
, obj
):
1881 if obj
.conn
.name
!= self
.name
:
1883 our_jid
= gajim
.get_jid_from_account(self
.name
)
1884 if self
.connected
> 1 and self
.continue_connect_info
:
1885 msg
= self
.continue_connect_info
[1]
1886 sign_msg
= self
.continue_connect_info
[2]
1888 send_first_presence
= True
1890 signed
= self
.get_signed_presence(msg
,
1891 self
._send
_first
_presence
)
1893 gajim
.nec
.push_incoming_event(GPGPasswordRequiredEvent(None,
1894 conn
=self
, callback
=self
._send
_first
_presence
))
1895 # _send_first_presence will be called when user enter
1897 send_first_presence
= False
1898 if send_first_presence
:
1899 self
._send
_first
_presence
(signed
)
1901 if obj
.received_from_server
:
1902 for jid
in obj
.roster
:
1903 if jid
!= our_jid
and gajim
.jid_is_transport(jid
) and \
1904 not gajim
.get_transport_name_from_jid(jid
):
1905 # we can't determine which iconset to use
1906 self
.discoverInfo(jid
)
1908 gajim
.logger
.replace_roster(self
.name
, obj
.version
, obj
.roster
)
1910 for contact
in gajim
.contacts
.iter_contacts(self
.name
):
1911 if not contact
.is_groupchat() and contact
.jid
not in obj
.roster\
1912 and contact
.jid
!= our_jid
:
1913 gajim
.nec
.push_incoming_event(RosterInfoEvent(None,
1914 conn
=self
, jid
=contact
.jid
, nickname
=None, sub
=None,
1915 ask
=None, groups
=()))
1916 for jid
, info
in obj
.roster
.items():
1917 gajim
.nec
.push_incoming_event(RosterInfoEvent(None,
1918 conn
=self
, jid
=jid
, nickname
=info
['name'],
1919 sub
=info
['subscription'], ask
=info
['ask'],
1920 groups
=info
['groups']))
1922 def _send_first_presence(self
, signed
=''):
1923 show
= self
.continue_connect_info
[0]
1924 msg
= self
.continue_connect_info
[1]
1925 sign_msg
= self
.continue_connect_info
[2]
1926 if sign_msg
and not signed
:
1927 signed
= self
.get_signed_presence(msg
)
1929 gajim
.nec
.push_incoming_event(BadGPGPassphraseEvent(None,
1931 self
.USE_GPG
= False
1933 self
.connected
= gajim
.SHOW_LIST
.index(show
)
1934 sshow
= helpers
.get_xmpp_show(show
)
1936 if show
== 'invisible':
1937 self
.send_invisible_presence(msg
, signed
, True)
1939 if show
not in ['offline', 'online', 'chat', 'away', 'xa', 'dnd']:
1941 priority
= gajim
.get_priority(self
.name
, sshow
)
1942 our_jid
= helpers
.parse_jid(gajim
.get_jid_from_account(self
.name
))
1943 vcard
= self
.get_cached_vcard(our_jid
)
1944 if vcard
and 'PHOTO' in vcard
and 'SHA' in vcard
['PHOTO']:
1945 self
.vcard_sha
= vcard
['PHOTO']['SHA']
1946 p
= common
.xmpp
.Presence(typ
=None, priority
=priority
, show
=sshow
)
1951 p
.setTag(common
.xmpp
.NS_SIGNED
+ ' x').setData(signed
)
1954 self
.connection
.send(p
)
1955 self
.priority
= priority
1956 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
1958 if self
.vcard_supported
:
1960 self
.request_vcard(None)
1962 # Get bookmarks from private namespace
1963 self
.get_bookmarks()
1965 # Get annotations from private namespace
1966 self
.get_annotations()
1968 # Inform GUI we just signed in
1969 gajim
.nec
.push_incoming_event(SignedInEvent(None, conn
=self
))
1970 self
.send_awaiting_pep()
1971 self
.continue_connect_info
= None
1973 def request_gmail_notifications(self
):
1974 if not self
.connection
or self
.connected
< 2:
1976 # It's a gmail account,
1977 # inform the server that we want e-mail notifications
1978 our_jid
= helpers
.parse_jid(gajim
.get_jid_from_account(self
.name
))
1979 log
.debug(('%s is a gmail account. Setting option '
1980 'to get e-mail notifications on the server.') % (our_jid
))
1981 iq
= common
.xmpp
.Iq(typ
='set', to
=our_jid
)
1982 iq
.setAttr('id', 'MailNotify')
1983 query
= iq
.setTag('usersetting')
1984 query
.setNamespace(common
.xmpp
.NS_GTALKSETTING
)
1985 query
= query
.setTag('mailnotifications')
1986 query
.setAttr('value', 'true')
1987 self
.connection
.send(iq
)
1988 # Ask how many messages there are now
1989 iq
= common
.xmpp
.Iq(typ
='get')
1990 iq
.setID(self
.connection
.getAnID())
1991 query
= iq
.setTag('query')
1992 query
.setNamespace(common
.xmpp
.NS_GMAILNOTIFY
)
1993 self
.connection
.send(iq
)
1995 def _SearchCB(self
, con
, iq_obj
):
1996 log
.debug('SearchCB')
1997 gajim
.nec
.push_incoming_event(SearchFormReceivedEvent(None,
1998 conn
=self
, stanza
=iq_obj
))
2000 def _StreamCB(self
, con
, iq_obj
):
2001 log
.debug('StreamCB')
2002 gajim
.nec
.push_incoming_event(StreamReceivedEvent(None,
2003 conn
=self
, stanza
=iq_obj
))
2005 def _register_handlers(self
, con
, con_type
):
2006 # try to find another way to register handlers in each class
2007 # that defines handlers
2008 con
.RegisterHandler('message', self
._messageCB
)
2009 con
.RegisterHandler('presence', self
._presenceCB
)
2010 # We use makefirst so that this handler is called before _messageCB, and
2011 # can prevent calling it when it's not needed.
2012 # We also don't check for namespace, else it cannot stop _messageCB to
2014 con
.RegisterHandler('message', self
._pubsubEventCB
, makefirst
=True)
2015 con
.RegisterHandler('iq', self
._vCardCB
, 'result', common
.xmpp
.NS_VCARD
)
2016 con
.RegisterHandler('iq', self
._rosterSetCB
, 'set',
2017 common
.xmpp
.NS_ROSTER
)
2018 con
.RegisterHandler('iq', self
._siSetCB
, 'set', common
.xmpp
.NS_SI
)
2019 con
.RegisterHandler('iq', self
._rosterItemExchangeCB
, 'set',
2020 common
.xmpp
.NS_ROSTERX
)
2021 con
.RegisterHandler('iq', self
._siErrorCB
, 'error', common
.xmpp
.NS_SI
)
2022 con
.RegisterHandler('iq', self
._siResultCB
, 'result', common
.xmpp
.NS_SI
)
2023 con
.RegisterHandler('iq', self
._discoGetCB
, 'get', common
.xmpp
.NS_DISCO
)
2024 con
.RegisterHandler('iq', self
._bytestreamSetCB
, 'set',
2025 common
.xmpp
.NS_BYTESTREAM
)
2026 con
.RegisterHandler('iq', self
._bytestreamResultCB
, 'result',
2027 common
.xmpp
.NS_BYTESTREAM
)
2028 con
.RegisterHandler('iq', self
._bytestreamErrorCB
, 'error',
2029 common
.xmpp
.NS_BYTESTREAM
)
2030 con
.RegisterHandlerOnce('iq', self
.IBBAllIqHandler
)
2031 con
.RegisterHandler('iq', self
.IBBIqHandler
, ns
=common
.xmpp
.NS_IBB
)
2032 con
.RegisterHandler('message', self
.IBBMessageHandler
,
2033 ns
=common
.xmpp
.NS_IBB
)
2034 con
.RegisterHandler('iq', self
._DiscoverItemsCB
, 'result',
2035 common
.xmpp
.NS_DISCO_ITEMS
)
2036 con
.RegisterHandler('iq', self
._DiscoverItemsErrorCB
, 'error',
2037 common
.xmpp
.NS_DISCO_ITEMS
)
2038 con
.RegisterHandler('iq', self
._DiscoverInfoCB
, 'result',
2039 common
.xmpp
.NS_DISCO_INFO
)
2040 con
.RegisterHandler('iq', self
._DiscoverInfoErrorCB
, 'error',
2041 common
.xmpp
.NS_DISCO_INFO
)
2042 con
.RegisterHandler('iq', self
._VersionCB
, 'get',
2043 common
.xmpp
.NS_VERSION
)
2044 con
.RegisterHandler('iq', self
._TimeCB
, 'get', common
.xmpp
.NS_TIME
)
2045 con
.RegisterHandler('iq', self
._TimeRevisedCB
, 'get',
2046 common
.xmpp
.NS_TIME_REVISED
)
2047 con
.RegisterHandler('iq', self
._LastCB
, 'get', common
.xmpp
.NS_LAST
)
2048 con
.RegisterHandler('iq', self
._LastResultCB
, 'result',
2049 common
.xmpp
.NS_LAST
)
2050 con
.RegisterHandler('iq', self
._VersionResultCB
, 'result',
2051 common
.xmpp
.NS_VERSION
)
2052 con
.RegisterHandler('iq', self
._TimeRevisedResultCB
, 'result',
2053 common
.xmpp
.NS_TIME_REVISED
)
2054 con
.RegisterHandler('iq', self
._MucOwnerCB
, 'result',
2055 common
.xmpp
.NS_MUC_OWNER
)
2056 con
.RegisterHandler('iq', self
._MucAdminCB
, 'result',
2057 common
.xmpp
.NS_MUC_ADMIN
)
2058 con
.RegisterHandler('iq', self
._PrivateCB
, 'result',
2059 common
.xmpp
.NS_PRIVATE
)
2060 con
.RegisterHandler('iq', self
._SecLabelCB
, 'result',
2061 common
.xmpp
.NS_SECLABEL_CATALOG
)
2062 con
.RegisterHandler('iq', self
._HttpAuthCB
, 'get',
2063 common
.xmpp
.NS_HTTP_AUTH
)
2064 con
.RegisterHandler('iq', self
._CommandExecuteCB
, 'set',
2065 common
.xmpp
.NS_COMMANDS
)
2066 con
.RegisterHandler('iq', self
._gMailNewMailCB
, 'set',
2067 common
.xmpp
.NS_GMAILNOTIFY
)
2068 con
.RegisterHandler('iq', self
._gMailQueryCB
, 'result',
2069 common
.xmpp
.NS_GMAILNOTIFY
)
2070 con
.RegisterHandler('iq', self
._DiscoverInfoGetCB
, 'get',
2071 common
.xmpp
.NS_DISCO_INFO
)
2072 con
.RegisterHandler('iq', self
._DiscoverItemsGetCB
, 'get',
2073 common
.xmpp
.NS_DISCO_ITEMS
)
2074 con
.RegisterHandler('iq', self
._IqPingCB
, 'get', common
.xmpp
.NS_PING
)
2075 con
.RegisterHandler('iq', self
._SearchCB
, 'result',
2076 common
.xmpp
.NS_SEARCH
)
2077 con
.RegisterHandler('iq', self
._PrivacySetCB
, 'set',
2078 common
.xmpp
.NS_PRIVACY
)
2079 con
.RegisterHandler('iq', self
._ArchiveCB
, ns
=common
.xmpp
.NS_ARCHIVE
)
2080 con
.RegisterHandler('iq', self
._PubSubCB
, 'result')
2081 con
.RegisterHandler('iq', self
._PubSubErrorCB
, 'error')
2082 con
.RegisterHandler('iq', self
._JingleCB
, 'result')
2083 con
.RegisterHandler('iq', self
._JingleCB
, 'error')
2084 con
.RegisterHandler('iq', self
._JingleCB
, 'set', common
.xmpp
.NS_JINGLE
)
2085 con
.RegisterHandler('iq', self
._ErrorCB
, 'error')
2086 con
.RegisterHandler('iq', self
._IqCB
)
2087 con
.RegisterHandler('iq', self
._StanzaArrivedCB
)
2088 con
.RegisterHandler('iq', self
._ResultCB
, 'result')
2089 con
.RegisterHandler('presence', self
._StanzaArrivedCB
)
2090 con
.RegisterHandler('message', self
._StanzaArrivedCB
)
2091 con
.RegisterHandler('unknown', self
._StreamCB
,
2092 common
.xmpp
.NS_XMPP_STREAMS
, xmlns
=common
.xmpp
.NS_STREAMS
)