don't show error messages without a <body>. Fixes #6756
[gajim.git] / src / common / connection_handlers.py
blob3fb4263d4fa6a529e0416062065e80090d18eb57
1 # -*- coding:utf-8 -*-
2 ## src/common/connection_handlers.py
3 ##
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/>.
31 import os
32 import base64
33 import sys
34 import operator
35 import hashlib
37 from time import (altzone, daylight, gmtime, localtime, mktime, strftime,
38 time as time_time, timezone, tzname)
39 from calendar import timegm
41 import common.xmpp
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
66 else:
67 class ConnectionJingle():
68 def __init__(self):
69 pass
70 def _JingleCB(self, con, stanza):
71 pass
73 from common import dbus_support
74 if dbus_support.supported:
75 import dbus
76 from music_track_listener import MusicTrackListener
78 import logging
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'
90 HAS_IDLE = True
91 try:
92 # import idle
93 import common.sleepy
94 except Exception:
95 log.debug(_('Unable to load idle module'))
96 HAS_IDLE = False
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:
107 jid is mandatory;
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:
122 return None
123 iq = common.xmpp.Iq('get', common.xmpp.NS_REGISTER, to=agent)
124 id_ = self.connection.getAnID()
125 iq.setID(id_)
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 '
129 'time') % agent)
130 self.connection.SendAndCallForResponse(iq, self._ReceivedRegInfo,
131 {'agent': agent})
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')
141 p = self.add_sha(p)
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:
151 return
152 if is_form:
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})
159 else:
160 # fixed: blocking
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:
168 return
169 iq = common.xmpp.Iq(typ='get', to=jid, queryNS=ns)
170 if id_prefix:
171 id_ = self.connection.getAnID()
172 iq.setID('%s%s' % (id_prefix, id_))
173 if node:
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):
183 Get disco info
185 if not self.connection or self.connected < 2:
186 return
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,
191 frm=to)
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,
213 stanza=iq_obj))
215 def _DiscoverItemsGetCB(self, con, iq_obj):
216 log.debug('DiscoverItemsGetCB')
218 if not self.connection or self.connected < 2:
219 return
221 if self.commandItemsQuery(con, iq_obj):
222 raise common.xmpp.NodeProcessed
223 node = iq_obj.getTagAttr('query', 'node')
224 if node is None:
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:
235 return
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')
249 if node:
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})
260 if q.getChildren():
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:
272 return
273 gajim.nec.push_incoming_event(AgentInfoReceivedEvent(None, conn=self,
274 stanza=iq_obj))
276 class ConnectionVcard:
277 def __init__(self):
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
281 self.room_jids = []
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)
287 if send_caps:
288 return self._add_caps(p)
289 return 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])
297 return p
299 def _node_to_dict(self, node):
300 dict_ = {}
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, [])
305 entry = {}
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()
311 else:
312 dict_[name] = {}
313 for c in info.getChildren():
314 dict_[name][c.getName()] = c.getData()
315 return dict_
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):
322 if not nick:
323 return
324 # remove room_jid file if needed
325 if os.path.isfile(path):
326 os.remove(path)
327 # create folder if needed
328 if not os.path.isdir(path):
329 os.mkdir(path, 0700)
330 puny_nick = helpers.sanitize_filename(nick)
331 path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
332 else:
333 path_to_file = path
334 try:
335 fil = open(path_to_file, 'w')
336 fil.write(str(card))
337 fil.close()
338 except IOError, e:
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)
349 if is_fake_jid:
350 puny_nick = helpers.sanitize_filename(nick)
351 path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
352 else:
353 path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid)
354 if not os.path.isfile(path_to_file):
355 return None
356 # We have the vcard cached
357 f = open(path_to_file)
358 c = f.read()
359 f.close()
360 try:
361 card = common.xmpp.Node(node=c)
362 except Exception:
363 # We are unable to parse it. Remove it
364 os.remove(path_to_file)
365 return None
366 vcard = self._node_to_dict(card)
367 if 'PHOTO' in vcard:
368 if not isinstance(vcard['PHOTO'], dict):
369 del vcard['PHOTO']
370 elif 'SHA' in vcard['PHOTO']:
371 cached_sha = vcard['PHOTO']['SHA']
372 if jid in self.vcard_shas and self.vcard_shas[jid] != \
373 cached_sha:
374 # user change his vcard so don't use the cached one
375 return {}
376 vcard['jid'] = jid
377 vcard['resource'] = gajim.get_resource_from_jid(fjid)
378 return vcard
380 def request_vcard(self, jid=None, groupchat_jid=None):
382 Request the VCARD
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:
389 return
390 iq = common.xmpp.Iq(typ='get')
391 if jid:
392 iq.setTo(jid)
393 iq.setTag(common.xmpp.NS_VCARD + ' vCard')
395 id_ = self.connection.getAnID()
396 iq.setID(id_)
397 j = jid
398 if not j:
399 j = gajim.get_jid_from_account(self.name)
400 self.awaiting_answers[id_] = (VCARD_ARRIVED, j, groupchat_jid)
401 if 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:
410 return
411 iq = common.xmpp.Iq(typ='set')
412 iq2 = iq.setTag(common.xmpp.NS_VCARD + ' vCard')
413 for i in vcard:
414 if i == 'jid':
415 continue
416 if isinstance(vcard[i], dict):
417 iq3 = iq2.addChild(i)
418 for j in vcard[i]:
419 iq3.addChild(j).setData(vcard[i][j])
420 elif isinstance(vcard[i], list):
421 for j in vcard[i]:
422 iq3 = iq2.addChild(i)
423 for k in j:
424 iq3.addChild(k).setData(j[k])
425 else:
426 iq2.addChild(i).setData(vcard[i])
428 id_ = self.connection.getAnID()
429 iq.setID(id_)
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)
441 else:
442 gajim.interface.remove_avatar_files(our_jid)
444 self.awaiting_answers[id_] = (VCARD_PUBLISHED, iq2)
446 def _IqCB(self, con, iq_obj):
447 id_ = iq_obj.getID()
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
453 found_tim = None
454 for tim in self.awaiting_timeouts:
455 if id_ == self.awaiting_timeouts[tim][0]:
456 found_tim = tim
457 break
458 if found_tim:
459 del self.awaiting_timeouts[found_tim]
461 if id_ not in self.awaiting_answers:
462 return
463 if self.awaiting_answers[id_][0] == VCARD_PUBLISHED:
464 if iq_obj.getType() == 'result':
465 vcard_iq = self.awaiting_answers[id_][1]
466 # Save vcard to HD
467 if vcard_iq.getTag('PHOTO') and vcard_iq.getTag('PHOTO').getTag(
468 'SHA'):
469 new_sha = vcard_iq.getTag('PHOTO').getTagData('SHA')
470 else:
471 new_sha = ''
473 # Save it to file
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:
481 return
482 self.vcard_sha = new_sha
483 sshow = helpers.get_xmpp_show(gajim.SHOW_LIST[
484 self.connected])
485 p = common.xmpp.Presence(typ=None, priority=self.priority,
486 show=sshow, status=self.status)
487 p = self.add_sha(p)
488 self.connection.send(p)
489 gajim.nec.push_incoming_event(VcardPublishedEvent(None,
490 conn=self))
491 elif iq_obj.getType() == 'error':
492 gajim.nec.push_incoming_event(VcardNotPublishedEvent(None,
493 conn=self))
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]
499 frm = jid
500 if groupchat_jid:
501 # We do as if it comes from the fake_jid
502 frm = groupchat_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,
519 agent=jid))
520 elif self.awaiting_answers[id_][0] == METACONTACTS_ARRIVED:
521 if not self.connection:
522 return
523 if iq_obj.getType() == 'result':
524 gajim.nec.push_incoming_event(MetacontactsReceivedEvent(None,
525 conn=self, stanza=iq_obj))
526 else:
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:
532 return
533 if iq_obj.getType() == 'result':
534 query = iq_obj.getTag('query')
535 delimiter = query.getTagData('roster')
536 if delimiter:
537 self.nested_group_delimiter = delimiter
538 else:
539 self.set_roster_delimiter('::')
540 else:
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)
552 self._getRoster()
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
560 # supported
561 self.disconnect(on_purpose=True)
562 gajim.nec.push_incoming_event(OurShowEvent(None, conn=self,
563 show='offline'))
564 self.dispatch('ERROR', (_('Invisibility not supported'),
565 _('Account %s doesn\'t support invisibility.') % \
566 self.name))
567 return
568 # Ask metacontacts before roster
569 self.get_metacontacts()
570 elif self.awaiting_answers[id_][0] == PEP_CONFIG:
571 if iq_obj.getType() == 'error':
572 return
573 if not iq_obj.getTag('pubsub'):
574 return
575 conf = iq_obj.getTag('pubsub').getTag('configure')
576 if not conf:
577 return
578 node = conf.getAttr('node')
579 form_tag = conf.getTag('x', namespace=common.xmpp.NS_DATA)
580 if form_tag:
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:
586 # TODO
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')
599 if chat:
600 with_ = chat.getAttr('with')
601 start_ = chat.getAttr('start')
602 tim = helpers.datetime_tuple(start_)
603 tim = timegm(tim)
604 nb = 0
605 for element in chat.getChildren():
606 try:
607 secs = int(element.getAttr('secs'))
608 except TypeError:
609 secs = 0
610 if secs:
611 tim += secs
612 nick = element.getAttr('name')
613 if element.getName() == 'from':
614 save_if_not_exists(with_, nick, 'from', localtime(tim),
615 element.getPayload())
616 nb += 1
617 if element.getName() == 'to':
618 save_if_not_exists(with_, nick, 'to', localtime(tim),
619 element.getPayload())
620 nb += 1
621 set_ = chat.getTag('set')
622 first = set_.getTag('first')
623 if first:
624 try:
625 index = int(first.getAttr('index'))
626 except TypeError:
627 index = 0
628 try:
629 count = int(set_.getTagData('count'))
630 except TypeError:
631 count = 0
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')
639 if 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':
646 # do nothing
647 pass
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'):
656 return
657 if not vc.getTag('vCard').getNamespace() == common.xmpp.NS_VCARD:
658 return
659 id_ = vc.getID()
660 frm_iq = vc.getFrom()
661 our_jid = gajim.get_jid_from_account(self.name)
662 resource = ''
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_]
667 elif frm_iq:
668 who = helpers.get_full_jid_from_iq(vc)
669 frm, resource = gajim.get_room_and_nick_from_fjid(who)
670 else:
671 who = frm = our_jid
672 card = vc.getChildren()[0]
673 vcard = self._node_to_dict(card)
674 photo_decoded = None
675 if 'PHOTO' in vcard and isinstance(vcard['PHOTO'], dict) and \
676 'BINVAL' in vcard['PHOTO']:
677 photo = vcard['PHOTO']['BINVAL']
678 try:
679 photo_decoded = base64.decodestring(photo)
680 avatar_sha = hashlib.sha1(photo_decoded).hexdigest()
681 except Exception:
682 avatar_sha = ''
683 else:
684 avatar_sha = ''
686 if avatar_sha:
687 card.getTag('PHOTO').setTagData('SHA', avatar_sha)
689 # Save it to file
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)
694 puny_nick = None
695 begin_path = os.path.join(gajim.AVATAR_PATH, puny_jid)
696 frm_jid = frm
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
704 if photo_decoded:
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)
712 if avatar_sha:
713 self.vcard_shas[frm_jid] = avatar_sha
714 elif frm in self.vcard_shas:
715 del self.vcard_shas[frm]
716 else:
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):
721 os.remove(path)
723 vcard['jid'] = frm
724 vcard['resource'] = resource
725 gajim.nec.push_incoming_event(VcardReceivedEvent(None, conn=self,
726 vcard_dict=vcard))
727 if frm_jid == our_jid:
728 # we re-send our presence with sha if has changed and if we are
729 # not invisible
730 if self.vcard_sha == avatar_sha:
731 return
732 self.vcard_sha = avatar_sha
733 if gajim.SHOW_LIST[self.connected] == 'invisible':
734 return
735 if not self.connection:
736 return
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)
740 p = self.add_sha(p)
741 self.connection.send(p)
743 # basic connection handlers used here and in zeroconf
744 class ConnectionHandlersBase:
745 def __init__(self):
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
755 self.last_ids = []
757 # keep track of sessions this connection has with other JIDs
758 self.sessions = {}
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)
769 def cleanup(self):
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:
781 return
782 if obj.id_ in self.last_ids:
783 gajim.nec.push_incoming_event(LastResultReceivedEvent(None,
784 conn=self, stanza=obj.stanza))
785 return True
787 def _nec_presence_received(self, obj):
788 account = obj.conn.name
789 if account != self.name:
790 return
791 jid = obj.jid
792 resource = obj.resource or ''
794 statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
795 'invisible']
796 obj.old_show = 0
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)
804 # Update contact
805 obj.contact_list = gajim.contacts.get_contacts(account, jid)
806 obj.contact = None
807 resources = []
808 for c in obj.contact_list:
809 resources.append(c.resource)
810 if c.resource == resource:
811 obj.contact = c
812 break
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']
820 else:
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)
826 if obj.contact:
827 if obj.contact.show in statuss:
828 obj.old_show = statuss.index(obj.contact.show)
829 # nick changed
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
837 return
838 else:
839 obj.contact = gajim.contacts.get_first_contact_from_jid(account,
840 jid)
841 if not obj.contact:
842 # Presence of another resource of our jid
843 # Create self contact and add to roster
844 if resource == obj.conn.server_resource:
845 return
846 # Ignore offline presence of unknown self resource
847 if obj.new_show < 2:
848 return
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
861 obj.old_show = 0
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:
869 # It's not an agent
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
887 if obj.timestamp:
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):
895 return
897 # It isn't an agent
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):
917 continue
918 if sess.control:
919 sess.control.no_autonegotiation = False
920 if sess.enable_encryption:
921 sess.terminate_e2e()
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:
927 continue
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)):
935 sess.terminate()
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):
940 try:
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:
954 return
955 if obj.encrypted == 'xep200':
956 try:
957 obj.stanza = obj.session.decrypt_stanza(obj.stanza)
958 obj.msgtxt = obj.stanza.getBody()
959 except Exception:
960 gajim.nec.push_incoming_event(FailedDecryptEvent(None,
961 conn=self, msg_obj=obj))
962 return
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')
968 if 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',
973 ''))
974 obj.encrypted = 'xep27'
975 gajim.thread_interface(decrypt_thread, [encmsg, keyID, obj],
976 self._on_message_decrypted, [obj])
977 return
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:
986 return
988 # Receipt requested
989 # TODO: We shouldn't answer if we're invisible!
990 contact = gajim.contacts.get_contact(self.name, obj.jid)
991 nick = obj.resource
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_})
1001 if obj.thread_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':
1011 if not obj.msgtxt:
1012 return True
1013 self.dispatch_error_message(obj.stanza, obj.msgtxt,
1014 obj.session, obj.fjid, obj.timestamp)
1015 return True
1016 elif obj.mtype == 'groupchat':
1017 gajim.nec.push_incoming_event(GcMessageReceivedEvent(None,
1018 conn=self, msg_obj=obj))
1019 return True
1020 elif obj.invite_tag is not None:
1021 gajim.nec.push_incoming_event(GcInvitationReceivedEvent(None,
1022 conn=self, msg_obj=obj))
1023 return True
1025 # process and dispatch an error message
1026 def dispatch_error_message(self, msg, msgtxt, session, frm, tim):
1027 error_msg = msg.getErrorMsg()
1029 if not error_msg:
1030 error_msg = msgtxt
1031 msgtxt = None
1033 subject = msg.getSubject()
1035 if session.is_loggable():
1036 try:
1037 gajim.logger.write('error', frm, error_msg, tim=tim,
1038 subject=subject)
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,
1055 stanza=iq_obj))
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)
1064 try:
1065 return self.sessions[jid].values()
1066 except KeyError:
1067 return []
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
1074 pm = True
1075 jid = fjid
1077 if not gajim.interface.is_pm_contact(fjid, self.name):
1078 pm = False
1079 jid = gajim.get_jid_without_resource(fjid)
1081 session = self.find_session(jid, thread_id)
1083 if session:
1084 return session
1086 if pm:
1087 return self.make_new_session(fjid, thread_id, type_='pm')
1088 else:
1089 return self.make_new_session(fjid, thread_id)
1091 def find_session(self, jid, thread_id):
1092 try:
1093 if not thread_id:
1094 return self.find_null_session(jid)
1095 else:
1096 return self.sessions[jid][thread_id]
1097 except KeyError:
1098 return None
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)
1108 self.sessions = {}
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:
1114 return
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
1125 message to
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)]
1136 if chat_sessions:
1137 # return the session that we last sent a message in
1138 return sorted(chat_sessions, key=operator.attrgetter('last_send'))[
1140 else:
1141 return None
1143 def find_controlless_session(self, jid, resource=None):
1145 Find an active session that doesn't have a control attached
1147 try:
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]
1156 if resource:
1157 orphaned = [s for s in orphaned if s.resource == resource]
1159 return orphaned[0]
1160 except (KeyError, IndexError):
1161 return None
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'.
1170 if not cls:
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
1185 return sess
1187 class ConnectionHandlers(ConnectionArchive, ConnectionVcard,
1188 ConnectionSocks5Bytestream, ConnectionDisco, ConnectionCommands,
1189 ConnectionPubSub, ConnectionPEP, ConnectionCaps, ConnectionHandlersBase,
1190 ConnectionJingle, ConnectionIBBytestream):
1191 def __init__(self):
1192 global HAS_IDLE
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
1223 try:
1224 self.sleeper = common.sleepy.Sleepy()
1225 HAS_IDLE = True
1226 except Exception:
1227 HAS_IDLE = False
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)
1280 def cleanup(self):
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:
1324 return
1325 if answer == 'yes':
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:
1338 return
1339 if obj.opt in ('yes', 'no'):
1340 obj.conn.build_http_auth_answer(obj.stanza, obj.opt)
1341 return True
1343 def _HttpAuthCB(self, con, iq_obj):
1344 log.debug('HttpAuthCB')
1345 gajim.nec.push_incoming_event(HttpAuthReceivedEvent(None, conn=self,
1346 stanza=iq_obj))
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,
1352 stanza=iq_obj))
1354 def _nec_iq_error_received(self, obj):
1355 if obj.conn.name != self.name:
1356 return
1357 if obj.id_ in self.version_ids:
1358 gajim.nec.push_incoming_event(VersionResultReceivedEvent(None,
1359 conn=self, stanza=obj.stanza))
1360 return True
1361 if obj.id_ in self.entity_time_ids:
1362 gajim.nec.push_incoming_event(TimeResultReceivedEvent(None,
1363 conn=self, stanza=obj.stanza))
1364 return True
1366 def _nec_private_storate_bookmarks_received(self, obj):
1367 if obj.conn.name != self.name:
1368 return
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:
1381 return
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')
1401 labels = {}
1402 ll = []
1403 for item in items:
1404 label = item.getTag('displaymarking').getData()
1405 labels[label] = item
1406 ll.append(label)
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]:
1412 callback()
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,
1423 stanza=iq_obj))
1424 raise common.xmpp.NodeProcessed
1426 def _nec_roster_set_received(self, obj):
1427 if obj.conn.name != self.name:
1428 return
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'])
1437 if obj.version:
1438 gajim.config.set_per('accounts', self.name, 'roster_version',
1439 obj.version)
1441 def _VersionCB(self, con, iq_obj):
1442 log.debug('VersionCB')
1443 if not self.connection or self.connected < 2:
1444 return
1445 gajim.nec.push_incoming_event(VersionRequestEvent(None, conn=self,
1446 stanza=iq_obj))
1447 raise common.xmpp.NodeProcessed
1449 def _nec_version_request_received(self, obj):
1450 if obj.conn.name != self.name:
1451 return
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')
1457 if send_os:
1458 qp.setTagData('os', helpers.get_os_info())
1459 self.connection.send(iq_obj)
1461 def _LastCB(self, con, iq_obj):
1462 log.debug('LastCB')
1463 if not self.connection or self.connected < 2:
1464 return
1465 gajim.nec.push_incoming_event(LastRequestEvent(None, conn=self,
1466 stanza=iq_obj))
1467 raise common.xmpp.NodeProcessed
1469 def _nec_last_request_received(self, obj):
1470 global HAS_IDLE
1471 if obj.conn.name != self.name:
1472 return
1473 if HAS_IDLE and gajim.config.get_per('accounts', self.name,
1474 'send_idle_time'):
1475 iq_obj = obj.stanza.buildReply('result')
1476 qp = iq_obj.getTag('query')
1477 qp.attrs['seconds'] = int(self.sleeper.getIdleSec())
1478 else:
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):
1491 log.debug('TimeCB')
1492 if not self.connection or self.connected < 2:
1493 return
1494 gajim.nec.push_incoming_event(TimeRequestEvent(None, conn=self,
1495 stanza=iq_obj))
1496 raise common.xmpp.NodeProcessed
1498 def _nec_time_request_received(self, obj):
1499 if obj.conn.name != self.name:
1500 return
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',
1507 localtime())))
1508 else:
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:
1518 return
1519 gajim.nec.push_incoming_event(TimeRevisedRequestEvent(None, conn=self,
1520 stanza=iq_obj))
1521 raise common.xmpp.NodeProcessed
1523 def _nec_time_revised_request_received(self, obj):
1524 if obj.conn.name != self.name:
1525 return
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))
1534 else:
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,
1544 stanza=iq_obj))
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,
1552 stanza=iq_obj))
1553 raise common.xmpp.NodeProcessed
1555 def _nec_gmail_new_mail_received(self, obj):
1556 if obj.conn.name != self.name:
1557 return
1558 if not self.connection or self.connected < 2:
1559 return
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
1577 in gmail account
1579 log.debug('gMailQueryCB')
1580 gajim.nec.push_incoming_event(GMailQueryReceivedEvent(None, conn=self,
1581 stanza=iq_obj))
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,
1590 stanza=msg))
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:
1612 return
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]:
1618 cb = func[0]
1619 args = func[1]
1620 pos = func[2]
1621 bob_data = data.getData()
1622 def recurs(node, cid, data):
1623 if node.getData() == 'cid:' + cid:
1624 node.setData(data)
1625 else:
1626 for child in node.getChildren():
1627 recurs(child, cid, data)
1628 recurs(args[pos], cid, bob_data)
1629 cb(*args)
1630 del self.awaiting_cids[cid]
1631 return
1633 # An error occured, call callback without modifying data.
1634 for func in self.awaiting_cids[cid]:
1635 cb = func[0]
1636 args = func[1]
1637 cb(*args)
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
1644 args[position]
1646 if cid in self.awaiting_cids:
1647 self.awaiting_cids[cid].appends((callback, args, position))
1648 else:
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,
1654 {'cid': cid})
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:
1669 return
1671 captcha = msg.getTag('captcha', namespace=common.xmpp.NS_CAPTCHA)
1672 if captcha:
1673 captcha = captcha.getTag('x', namespace=common.xmpp.NS_DATA)
1674 found = helpers.replace_dataform_media(captcha, msg)
1675 if not found:
1676 self.get_bob_data(uri_data, frm, self.dispatch_gc_message,
1677 [msg, frm, msgtxt, jid, tim], 0)
1678 return
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
1688 try:
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:
1716 return
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
1722 return True
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:
1726 if self.connection:
1727 p = xmpp.Presence(obj.fjid, 'subscribed')
1728 p = self.add_sha(p)
1729 self.connection.send(p)
1730 if gajim.jid_is_transport(obj.fjid) or obj.transport_auto_auth:
1731 #TODO!?!?
1732 #self.show = 'offline'
1733 #self.status = 'offline'
1734 #emit NOTIFY
1735 pass
1736 if obj.transport_auto_auth:
1737 self.automatically_added.append(obj.jid)
1738 self.request_subscription(obj.jid, name=obj.user_nick)
1739 return True
1740 if not obj.status:
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:
1746 return
1747 # BE CAREFUL: no con.updateRosterItem() in a callback
1748 if obj.jid in self.automatically_added:
1749 self.automatically_added.remove(obj.jid)
1750 return True
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())
1755 block = False
1756 if len(self.subscribed_events[obj.jid]) > 5:
1757 if time_time() - self.subscribed_events[obj.jid][0] < 5:
1758 block = True
1759 self.subscribed_events[obj.jid] = \
1760 self.subscribed_events[obj.jid][1:]
1761 if block:
1762 gajim.config.set_per('account', self.name, 'dont_ack_subscription',
1763 True)
1764 return True
1766 def _nec_subscribed_presence_received_end(self, obj):
1767 account = obj.conn.name
1768 if account != self.name:
1769 return
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:
1777 return
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())
1782 block = False
1783 if len(self.subscribed_events[obj.jid]) > 5:
1784 if time_time() - self.subscribed_events[obj.jid][0] < 5:
1785 block = True
1786 self.subscribed_events[obj.jid] = \
1787 self.subscribed_events[obj.jid][1:]
1788 if block:
1789 gajim.config.set_per('account', self.name, 'dont_ack_subscription',
1790 True)
1791 return True
1793 def _nec_unsubscribed_presence_received_end(self, obj):
1794 account = obj.conn.name
1795 if account != self.name:
1796 return
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:
1803 return
1804 for jid in obj.jid_list:
1805 log.debug('Removing contact %s due to unregistered transport %s' % \
1806 (jid, obj.agent))
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,
1819 stanza=iq_obj))
1821 def _MucAdminCB(self, con, iq_obj):
1822 log.debug('MucAdminCB')
1823 gajim.nec.push_incoming_event(MucAdminReceivedEvent(None, conn=self,
1824 stanza=iq_obj))
1826 def _IqPingCB(self, con, iq_obj):
1827 log.debug('IqPingCB')
1828 gajim.nec.push_incoming_event(PingReceivedEvent(None, conn=self,
1829 stanza=iq_obj))
1830 raise common.xmpp.NodeProcessed
1832 def _nec_ping_received(self, obj):
1833 if obj.conn.name != self.name:
1834 return
1835 if not self.connection or self.connected < 2:
1836 return
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:
1848 return
1849 result = iq_obj.buildReply('result')
1850 q = result.getTag('query')
1851 if q:
1852 result.delChild(q)
1853 self.connection.send(result)
1854 raise common.xmpp.NodeProcessed
1856 def _getRoster(self):
1857 log.debug('getRosterCB')
1858 if not self.connection:
1859 return
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)
1871 if cfg_proxies:
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:
1882 return
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]
1887 signed = ''
1888 send_first_presence = True
1889 if sign_msg:
1890 signed = self.get_signed_presence(msg,
1891 self._send_first_presence)
1892 if signed is None:
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
1896 # passphrase
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)
1928 if signed is None:
1929 gajim.nec.push_incoming_event(BadGPGPassphraseEvent(None,
1930 conn=self))
1931 self.USE_GPG = False
1932 signed = ''
1933 self.connected = gajim.SHOW_LIST.index(show)
1934 sshow = helpers.get_xmpp_show(show)
1935 # send our presence
1936 if show == 'invisible':
1937 self.send_invisible_presence(msg, signed, True)
1938 return
1939 if show not in ['offline', 'online', 'chat', 'away', 'xa', 'dnd']:
1940 return
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)
1947 p = self.add_sha(p)
1948 if msg:
1949 p.setStatus(msg)
1950 if signed:
1951 p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
1953 if self.connection:
1954 self.connection.send(p)
1955 self.priority = priority
1956 gajim.nec.push_incoming_event(OurShowEvent(None, conn=self,
1957 show=show))
1958 if self.vcard_supported:
1959 # ask our VCard
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:
1975 return
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
2013 # be called
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)