Package common :: Module connection_handlers
[hide private]
[frames] | no frames]

Source Code for Module common.connection_handlers

   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> 
  15  ## 
  16  ## This file is part of Gajim. 
  17  ## 
  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. 
  21  ## 
  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. 
  26  ## 
  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/>. 
  29  ## 
  30   
  31  import os 
  32  import base64 
  33  import sys 
  34  import operator 
  35  import hashlib 
  36  import hmac 
  37   
  38  from time import (altzone, daylight, gmtime, localtime, mktime, strftime, 
  39          time as time_time, timezone, tzname) 
  40  from calendar import timegm 
  41  import datetime 
  42   
  43  import common.xmpp 
  44  import common.caps_cache as capscache 
  45   
  46  from common import helpers 
  47  from common import gajim 
  48  from common import exceptions 
  49  from common.commands import ConnectionCommands 
  50  from common.pubsub import ConnectionPubSub 
  51  from common.pep import ConnectionPEP 
  52  from common.protocol.caps import ConnectionCaps 
  53  from common.protocol.bytestream import ConnectionSocks5Bytestream 
  54  from common import ged 
  55  from common import nec 
  56  from common.nec import NetworkEvent 
  57  from plugins import GajimPlugin 
  58  if gajim.HAVE_FARSIGHT: 
  59      from common.jingle import ConnectionJingle 
  60  else: 
61 - class ConnectionJingle():
62 - def __init__(self):
63 pass
64 - def _JingleCB(self, con, stanza):
65 pass
66 67 from common import dbus_support 68 if dbus_support.supported: 69 import dbus 70 from music_track_listener import MusicTrackListener 71 72 import logging 73 log = logging.getLogger('gajim.c.connection_handlers') 74 75 # kind of events we can wait for an answer 76 VCARD_PUBLISHED = 'vcard_published' 77 VCARD_ARRIVED = 'vcard_arrived' 78 AGENT_REMOVED = 'agent_removed' 79 METACONTACTS_ARRIVED = 'metacontacts_arrived' 80 ROSTER_ARRIVED = 'roster_arrived' 81 PRIVACY_ARRIVED = 'privacy_arrived' 82 PEP_CONFIG = 'pep_config' 83 HAS_IDLE = True 84 try: 85 # import idle 86 import common.sleepy 87 except Exception: 88 log.debug(_('Unable to load idle module')) 89 HAS_IDLE = False 90 91
92 -class ConnectionDisco:
93 """ 94 Holds xmpppy handlers and public methods for discover services 95 """ 96
97 - def discoverItems(self, jid, node = None, id_prefix = None):
98 """ 99 According to XEP-0030: 100 jid is mandatory; 101 name, node, action is optional. 102 """ 103 self._discover(common.xmpp.NS_DISCO_ITEMS, jid, node, id_prefix)
104
105 - def discoverInfo(self, jid, node = None, id_prefix = None):
106 """ 107 According to XEP-0030: 108 For identity: category, type is mandatory, name is optional. 109 For feature: var is mandatory. 110 """ 111 self._discover(common.xmpp.NS_DISCO_INFO, jid, node, id_prefix)
112
113 - def request_register_agent_info(self, agent):
114 if not self.connection or self.connected < 2: 115 return None 116 iq = common.xmpp.Iq('get', common.xmpp.NS_REGISTER, to=agent) 117 id_ = self.connection.getAnID() 118 iq.setID(id_) 119 # Wait the answer during 30 secondes 120 self.awaiting_timeouts[gajim.idlequeue.current_time() + 30] = (id_, 121 _('Registration information for transport %s has not arrived in time')\ 122 % agent) 123 self.connection.SendAndCallForResponse(iq, self._ReceivedRegInfo, 124 {'agent': agent})
125
126 - def _agent_registered_cb(self, con, resp, agent):
127 if resp.getType() == 'result': 128 self.dispatch('INFORMATION', (_('Registration succeeded'), 129 _('Registration with agent %s succeeded') % agent)) 130 self.request_subscription(agent, auto_auth=True) 131 self.agent_registrations[agent]['roster_push'] = True 132 if self.agent_registrations[agent]['sub_received']: 133 p = common.xmpp.Presence(agent, 'subscribed') 134 p = self.add_sha(p) 135 self.connection.send(p) 136 if resp.getType() == 'error': 137 self.dispatch('ERROR', (_('Registration failed'), _('Registration with' 138 ' agent %(agent)s failed with error %(error)s: %(error_msg)s') % { 139 'agent': agent, 'error': resp.getError(), 140 'error_msg': resp.getErrorMsg()}))
141
142 - def register_agent(self, agent, info, is_form = False):
143 if not self.connection or self.connected < 2: 144 return 145 if is_form: 146 iq = common.xmpp.Iq('set', common.xmpp.NS_REGISTER, to=agent) 147 query = iq.getTag('query') 148 info.setAttr('type', 'submit') 149 query.addChild(node=info) 150 self.connection.SendAndCallForResponse(iq, self._agent_registered_cb, 151 {'agent': agent}) 152 else: 153 # fixed: blocking 154 common.xmpp.features_nb.register(self.connection, agent, info, 155 self._agent_registered_cb, {'agent': agent}) 156 self.agent_registrations[agent] = {'roster_push': False, 157 'sub_received': False}
158
159 - def _discover(self, ns, jid, node=None, id_prefix=None):
160 if not self.connection or self.connected < 2: 161 return 162 iq = common.xmpp.Iq(typ='get', to=jid, queryNS=ns) 163 if id_prefix: 164 id_ = self.connection.getAnID() 165 iq.setID('%s%s' % (id_prefix, id_)) 166 if node: 167 iq.setQuerynode(node) 168 self.connection.send(iq)
169
170 - def _ReceivedRegInfo(self, con, resp, agent):
171 common.xmpp.features_nb._ReceivedRegInfo(con, resp, agent) 172 self._IqCB(con, resp)
173
174 - def _discoGetCB(self, con, iq_obj):
175 """ 176 Get disco info 177 """ 178 if not self.connection or self.connected < 2: 179 return 180 frm = helpers.get_full_jid_from_iq(iq_obj) 181 to = unicode(iq_obj.getAttr('to')) 182 id_ = unicode(iq_obj.getAttr('id')) 183 iq = common.xmpp.Iq(to=frm, typ='result', queryNS=common.xmpp.NS_DISCO, 184 frm=to) 185 iq.setAttr('id', id_) 186 query = iq.setTag('query') 187 query.setAttr('node', 'http://gajim.org#' + gajim.version.split('-', 1)[0]) 188 for f in (common.xmpp.NS_BYTESTREAM, common.xmpp.NS_SI, 189 common.xmpp.NS_FILE, common.xmpp.NS_COMMANDS): 190 feature = common.xmpp.Node('feature') 191 feature.setAttr('var', f) 192 query.addChild(node=feature) 193 194 self.connection.send(iq) 195 raise common.xmpp.NodeProcessed
196
197 - def _DiscoverItemsErrorCB(self, con, iq_obj):
198 log.debug('DiscoverItemsErrorCB') 199 jid = helpers.get_full_jid_from_iq(iq_obj) 200 self.dispatch('AGENT_ERROR_ITEMS', (jid))
201
202 - def _DiscoverItemsCB(self, con, iq_obj):
203 log.debug('DiscoverItemsCB') 204 q = iq_obj.getTag('query') 205 node = q.getAttr('node') 206 if not node: 207 node = '' 208 qp = iq_obj.getQueryPayload() 209 items = [] 210 if not qp: 211 qp = [] 212 for i in qp: 213 # CDATA payload is not processed, only nodes 214 if not isinstance(i, common.xmpp.simplexml.Node): 215 continue 216 attr = {} 217 for key in i.getAttrs(): 218 attr[key] = i.getAttrs()[key] 219 if 'jid' not in attr: 220 continue 221 try: 222 attr['jid'] = helpers.parse_jid(attr['jid']) 223 except common.helpers.InvalidFormat: 224 # jid is not conform 225 continue 226 items.append(attr) 227 jid = helpers.get_full_jid_from_iq(iq_obj) 228 hostname = gajim.config.get_per('accounts', self.name, 'hostname') 229 id_ = iq_obj.getID() 230 if jid == hostname and id_[:6] == 'Gajim_': 231 for item in items: 232 self.discoverInfo(item['jid'], id_prefix='Gajim_') 233 else: 234 self.dispatch('AGENT_INFO_ITEMS', (jid, node, items))
235
236 - def _DiscoverItemsGetCB(self, con, iq_obj):
237 log.debug('DiscoverItemsGetCB') 238 239 if not self.connection or self.connected < 2: 240 return 241 242 if self.commandItemsQuery(con, iq_obj): 243 raise common.xmpp.NodeProcessed 244 node = iq_obj.getTagAttr('query', 'node') 245 if node is None: 246 result = iq_obj.buildReply('result') 247 self.connection.send(result) 248 raise common.xmpp.NodeProcessed 249 if node==common.xmpp.NS_COMMANDS: 250 self.commandListQuery(con, iq_obj) 251 raise common.xmpp.NodeProcessed
252
253 - def _DiscoverInfoGetCB(self, con, iq_obj):
254 log.debug('DiscoverInfoGetCB') 255 if not self.connection or self.connected < 2: 256 return 257 q = iq_obj.getTag('query') 258 node = q.getAttr('node') 259 260 if self.commandInfoQuery(con, iq_obj): 261 raise common.xmpp.NodeProcessed 262 263 id_ = unicode(iq_obj.getAttr('id')) 264 if id_[:6] == 'Gajim_': 265 # We get this request from echo.server 266 raise common.xmpp.NodeProcessed 267 268 iq = iq_obj.buildReply('result') 269 q = iq.getTag('query') 270 if node: 271 q.setAttr('node', node) 272 q.addChild('identity', attrs = gajim.gajim_identity) 273 client_version = 'http://gajim.org#' + gajim.caps_hash[self.name] 274 275 if node in (None, client_version): 276 for f in gajim.gajim_common_features: 277 q.addChild('feature', attrs = {'var': f}) 278 for f in gajim.gajim_optional_features[self.name]: 279 q.addChild('feature', attrs = {'var': f}) 280 281 if q.getChildren(): 282 self.connection.send(iq) 283 raise common.xmpp.NodeProcessed
284
285 - def _DiscoverInfoErrorCB(self, con, iq_obj):
286 log.debug('DiscoverInfoErrorCB') 287 jid = helpers.get_full_jid_from_iq(iq_obj) 288 id_ = iq_obj.getID() 289 if id_[:6] == 'Gajim_': 290 if not self.privacy_rules_requested: 291 self.privacy_rules_requested = True 292 self._request_privacy() 293 self.dispatch('AGENT_ERROR_INFO', (jid))
294
295 - def _DiscoverInfoCB(self, con, iq_obj):
296 log.debug('DiscoverInfoCB') 297 if not self.connection or self.connected < 2: 298 return 299 # According to XEP-0030: 300 # For identity: category, type is mandatory, name is optional. 301 # For feature: var is mandatory 302 identities, features, data = [], [], [] 303 q = iq_obj.getTag('query') 304 node = q.getAttr('node') 305 if not node: 306 node = '' 307 qc = iq_obj.getQueryChildren() 308 if not qc: 309 qc = [] 310 is_muc = False 311 transport_type = '' 312 for i in qc: 313 if i.getName() == 'identity': 314 attr = {} 315 for key in i.getAttrs().keys(): 316 attr[key] = i.getAttr(key) 317 if 'category' in attr and \ 318 attr['category'] in ('gateway', 'headline') and \ 319 'type' in attr: 320 transport_type = attr['type'] 321 if 'category' in attr and \ 322 attr['category'] == 'conference' and \ 323 'type' in attr and attr['type'] == 'text': 324 is_muc = True 325 identities.append(attr) 326 elif i.getName() == 'feature': 327 var = i.getAttr('var') 328 if var: 329 features.append(var) 330 elif i.getName() == 'x' and i.getNamespace() == common.xmpp.NS_DATA: 331 data.append(common.xmpp.DataForm(node=i)) 332 jid = helpers.get_full_jid_from_iq(iq_obj) 333 if transport_type and jid not in gajim.transport_type: 334 gajim.transport_type[jid] = transport_type 335 gajim.logger.save_transport_type(jid, transport_type) 336 id_ = iq_obj.getID() 337 if id_ is None: 338 log.warn('Invalid IQ received without an ID. Ignoring it: %s' % iq_obj) 339 return 340 if not identities: # ejabberd doesn't send identities when we browse online users 341 #FIXME: see http://www.jabber.ru/bugzilla/show_bug.cgi?id=225 342 identities = [{'category': 'server', 'type': 'im', 'name': node}] 343 if id_[:6] == 'Gajim_': 344 if jid == gajim.config.get_per('accounts', self.name, 'hostname'): 345 if features.__contains__(common.xmpp.NS_GMAILNOTIFY): 346 gajim.gmail_domains.append(jid) 347 self.request_gmail_notifications() 348 if features.__contains__(common.xmpp.NS_SECLABEL): 349 self.seclabel_supported = True 350 for identity in identities: 351 if identity['category'] == 'pubsub' and identity.get('type') == \ 352 'pep': 353 self.pep_supported = True 354 break 355 if features.__contains__(common.xmpp.NS_VCARD): 356 self.vcard_supported = True 357 if features.__contains__(common.xmpp.NS_PUBSUB): 358 self.pubsub_supported = True 359 if features.__contains__(common.xmpp.NS_PUBSUB_PUBLISH_OPTIONS): 360 self.pubsub_publish_options_supported = True 361 else: 362 # Remove stored bookmarks accessible to everyone. 363 our_jid = gajim.get_jid_from_account(self.name) 364 self.send_pb_purge(our_jid, 'storage:bookmarks') 365 self.send_pb_delete(our_jid, 'storage:bookmarks') 366 if features.__contains__(common.xmpp.NS_BYTESTREAM): 367 our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name) +\ 368 '/' + self.server_resource) 369 gajim.proxy65_manager.resolve(jid, self.connection, our_jid, 370 self.name) 371 if features.__contains__(common.xmpp.NS_MUC) and is_muc: 372 type_ = transport_type or 'jabber' 373 self.muc_jid[type_] = jid 374 if transport_type: 375 if transport_type in self.available_transports: 376 self.available_transports[transport_type].append(jid) 377 else: 378 self.available_transports[transport_type] = [jid] 379 if not self.privacy_rules_requested: 380 self.privacy_rules_requested = True 381 self._request_privacy() 382 383 self.dispatch('AGENT_INFO_INFO', (jid, node, identities, 384 features, data)) 385 self._capsDiscoCB(jid, node, identities, features, data)
386
387 -class ConnectionVcard:
388 - def __init__(self):
389 self.vcard_sha = None 390 self.vcard_shas = {} # sha of contacts 391 self.room_jids = [] # list of gc jids so that vcard are saved in a folder
392
393 - def add_sha(self, p, send_caps = True):
394 c = p.setTag('x', namespace = common.xmpp.NS_VCARD_UPDATE) 395 if self.vcard_sha is not None: 396 c.setTagData('photo', self.vcard_sha) 397 if send_caps: 398 return self._add_caps(p) 399 return p
400
401 - def _add_caps(self, p):
402 ''' advertise our capabilities in presence stanza (xep-0115)''' 403 c = p.setTag('c', namespace = common.xmpp.NS_CAPS) 404 c.setAttr('hash', 'sha-1') 405 c.setAttr('node', 'http://gajim.org') 406 c.setAttr('ver', gajim.caps_hash[self.name]) 407 return p
408
409 - def _node_to_dict(self, node):
410 dict_ = {} 411 for info in node.getChildren(): 412 name = info.getName() 413 if name in ('ADR', 'TEL', 'EMAIL'): # we can have several 414 dict_.setdefault(name, []) 415 entry = {} 416 for c in info.getChildren(): 417 entry[c.getName()] = c.getData() 418 dict_[name].append(entry) 419 elif info.getChildren() == []: 420 dict_[name] = info.getData() 421 else: 422 dict_[name] = {} 423 for c in info.getChildren(): 424 dict_[name][c.getName()] = c.getData() 425 return dict_
426
427 - def _save_vcard_to_hd(self, full_jid, card):
428 jid, nick = gajim.get_room_and_nick_from_fjid(full_jid) 429 puny_jid = helpers.sanitize_filename(jid) 430 path = os.path.join(gajim.VCARD_PATH, puny_jid) 431 if jid in self.room_jids or os.path.isdir(path): 432 if not nick: 433 return 434 # remove room_jid file if needed 435 if os.path.isfile(path): 436 os.remove(path) 437 # create folder if needed 438 if not os.path.isdir(path): 439 os.mkdir(path, 0700) 440 puny_nick = helpers.sanitize_filename(nick) 441 path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick) 442 else: 443 path_to_file = path 444 try: 445 fil = open(path_to_file, 'w') 446 fil.write(str(card)) 447 fil.close() 448 except IOError, e: 449 self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
450
451 - def get_cached_vcard(self, fjid, is_fake_jid=False):
452 """ 453 Return the vcard as a dict. 454 Return {} if vcard was too old. 455 Return None if we don't have cached vcard. 456 """ 457 jid, nick = gajim.get_room_and_nick_from_fjid(fjid) 458 puny_jid = helpers.sanitize_filename(jid) 459 if is_fake_jid: 460 puny_nick = helpers.sanitize_filename(nick) 461 path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick) 462 else: 463 path_to_file = os.path.join(gajim.VCARD_PATH, puny_jid) 464 if not os.path.isfile(path_to_file): 465 return None 466 # We have the vcard cached 467 f = open(path_to_file) 468 c = f.read() 469 f.close() 470 try: 471 card = common.xmpp.Node(node=c) 472 except Exception: 473 # We are unable to parse it. Remove it 474 os.remove(path_to_file) 475 return None 476 vcard = self._node_to_dict(card) 477 if 'PHOTO' in vcard: 478 if not isinstance(vcard['PHOTO'], dict): 479 del vcard['PHOTO'] 480 elif 'SHA' in vcard['PHOTO']: 481 cached_sha = vcard['PHOTO']['SHA'] 482 if jid in self.vcard_shas and self.vcard_shas[jid] != \ 483 cached_sha: 484 # user change his vcard so don't use the cached one 485 return {} 486 vcard['jid'] = jid 487 vcard['resource'] = gajim.get_resource_from_jid(fjid) 488 return vcard
489
490 - def request_vcard(self, jid=None, groupchat_jid=None):
491 """ 492 Request the VCARD 493 494 If groupchat_jid is not null, it means we request a vcard to a fake jid, 495 like in private messages in groupchat. jid can be the real jid of the 496 contact, but we want to consider it comes from a fake jid 497 """ 498 if not self.connection or self.connected < 2: 499 return 500 iq = common.xmpp.Iq(typ = 'get') 501 if jid: 502 iq.setTo(jid) 503 iq.setTag(common.xmpp.NS_VCARD + ' vCard') 504 505 id_ = self.connection.getAnID() 506 iq.setID(id_) 507 j = jid 508 if not j: 509 j = gajim.get_jid_from_account(self.name) 510 self.awaiting_answers[id_] = (VCARD_ARRIVED, j, groupchat_jid) 511 if groupchat_jid: 512 room_jid = gajim.get_room_and_nick_from_fjid(groupchat_jid)[0] 513 if not room_jid in self.room_jids: 514 self.room_jids.append(room_jid) 515 self.groupchat_jids[id_] = groupchat_jid 516 self.connection.send(iq)
517
518 - def send_vcard(self, vcard):
519 if not self.connection or self.connected < 2: 520 return 521 iq = common.xmpp.Iq(typ = 'set') 522 iq2 = iq.setTag(common.xmpp.NS_VCARD + ' vCard') 523 for i in vcard: 524 if i == 'jid': 525 continue 526 if isinstance(vcard[i], dict): 527 iq3 = iq2.addChild(i) 528 for j in vcard[i]: 529 iq3.addChild(j).setData(vcard[i][j]) 530 elif isinstance(vcard[i], list): 531 for j in vcard[i]: 532 iq3 = iq2.addChild(i) 533 for k in j: 534 iq3.addChild(k).setData(j[k]) 535 else: 536 iq2.addChild(i).setData(vcard[i]) 537 538 id_ = self.connection.getAnID() 539 iq.setID(id_) 540 self.connection.send(iq) 541 542 our_jid = gajim.get_jid_from_account(self.name) 543 # Add the sha of the avatar 544 if 'PHOTO' in vcard and isinstance(vcard['PHOTO'], dict) and \ 545 'BINVAL' in vcard['PHOTO']: 546 photo = vcard['PHOTO']['BINVAL'] 547 photo_decoded = base64.decodestring(photo) 548 gajim.interface.save_avatar_files(our_jid, photo_decoded) 549 avatar_sha = hashlib.sha1(photo_decoded).hexdigest() 550 iq2.getTag('PHOTO').setTagData('SHA', avatar_sha) 551 else: 552 gajim.interface.remove_avatar_files(our_jid) 553 554 self.awaiting_answers[id_] = (VCARD_PUBLISHED, iq2)
555
556 - def _IqCB(self, con, iq_obj):
557 id_ = iq_obj.getID() 558 559 gajim.nec.push_incoming_event(NetworkEvent('raw-iq-received', 560 conn=con, xmpp_iq=iq_obj)) 561 562 # Check if we were waiting a timeout for this id 563 found_tim = None 564 for tim in self.awaiting_timeouts: 565 if id_ == self.awaiting_timeouts[tim][0]: 566 found_tim = tim 567 break 568 if found_tim: 569 del self.awaiting_timeouts[found_tim] 570 571 if id_ not in self.awaiting_answers: 572 return 573 if self.awaiting_answers[id_][0] == VCARD_PUBLISHED: 574 if iq_obj.getType() == 'result': 575 vcard_iq = self.awaiting_answers[id_][1] 576 # Save vcard to HD 577 if vcard_iq.getTag('PHOTO') and vcard_iq.getTag('PHOTO').getTag('SHA'): 578 new_sha = vcard_iq.getTag('PHOTO').getTagData('SHA') 579 else: 580 new_sha = '' 581 582 # Save it to file 583 our_jid = gajim.get_jid_from_account(self.name) 584 self._save_vcard_to_hd(our_jid, vcard_iq) 585 586 # Send new presence if sha changed and we are not invisible 587 if self.vcard_sha != new_sha and gajim.SHOW_LIST[self.connected] !=\ 588 'invisible': 589 if not self.connection or self.connected < 2: 590 return 591 self.vcard_sha = new_sha 592 sshow = helpers.get_xmpp_show(gajim.SHOW_LIST[self.connected]) 593 p = common.xmpp.Presence(typ = None, priority = self.priority, 594 show = sshow, status = self.status) 595 p = self.add_sha(p) 596 self.connection.send(p) 597 self.dispatch('VCARD_PUBLISHED', ()) 598 elif iq_obj.getType() == 'error': 599 self.dispatch('VCARD_NOT_PUBLISHED', ()) 600 elif self.awaiting_answers[id_][0] == VCARD_ARRIVED: 601 # If vcard is empty, we send to the interface an empty vcard so that 602 # it knows it arrived 603 jid = self.awaiting_answers[id_][1] 604 groupchat_jid = self.awaiting_answers[id_][2] 605 frm = jid 606 if groupchat_jid: 607 # We do as if it comes from the fake_jid 608 frm = groupchat_jid 609 our_jid = gajim.get_jid_from_account(self.name) 610 if not iq_obj.getTag('vCard') or iq_obj.getType() == 'error': 611 if frm and frm != our_jid: 612 # Write an empty file 613 self._save_vcard_to_hd(frm, '') 614 jid, resource = gajim.get_room_and_nick_from_fjid(frm) 615 self.dispatch('VCARD', {'jid': jid, 'resource': resource}) 616 elif frm == our_jid: 617 self.dispatch('MYVCARD', {'jid': frm}) 618 elif self.awaiting_answers[id_][0] == AGENT_REMOVED: 619 jid = self.awaiting_answers[id_][1] 620 self.dispatch('AGENT_REMOVED', jid) 621 elif self.awaiting_answers[id_][0] == METACONTACTS_ARRIVED: 622 if not self.connection: 623 return 624 if iq_obj.getType() == 'result': 625 # Metacontact tags 626 # http://www.xmpp.org/extensions/xep-0209.html 627 meta_list = {} 628 query = iq_obj.getTag('query') 629 storage = query.getTag('storage') 630 metas = storage.getTags('meta') 631 for meta in metas: 632 try: 633 jid = helpers.parse_jid(meta.getAttr('jid')) 634 except common.helpers.InvalidFormat: 635 continue 636 tag = meta.getAttr('tag') 637 data = {'jid': jid} 638 order = meta.getAttr('order') 639 try: 640 order = int(order) 641 except Exception: 642 order = 0 643 if order is not None: 644 data['order'] = order 645 if tag in meta_list: 646 meta_list[tag].append(data) 647 else: 648 meta_list[tag] = [data] 649 self.dispatch('METACONTACTS', meta_list) 650 else: 651 if iq_obj.getErrorCode() not in ('403', '406', '404'): 652 self.private_storage_supported = False 653 654 # We can now continue connection by requesting the roster 655 version = None 656 if con.Stream.features and con.Stream.features.getTag('ver', 657 namespace=common.xmpp.NS_ROSTER_VER): 658 version = gajim.config.get_per('accounts', self.name, 659 'roster_version') 660 if version and not gajim.contacts.get_contacts_jid_list( 661 self.name): 662 gajim.config.set_per('accounts', self.name, 663 'roster_version', '') 664 version = None 665 666 iq_id = self.connection.initRoster(version=version) 667 self.awaiting_answers[iq_id] = (ROSTER_ARRIVED, ) 668 elif self.awaiting_answers[id_][0] == ROSTER_ARRIVED: 669 if iq_obj.getType() == 'result': 670 if not iq_obj.getTag('query'): 671 account_jid = gajim.get_jid_from_account(self.name) 672 roster_data = gajim.logger.get_roster(account_jid) 673 roster = self.connection.getRoster(force=True) 674 roster.setRaw(roster_data) 675 self._getRoster() 676 elif self.awaiting_answers[id_][0] == PRIVACY_ARRIVED: 677 if iq_obj.getType() != 'error': 678 self.privacy_rules_supported = True 679 self.get_privacy_list('block') 680 elif self.continue_connect_info: 681 if self.continue_connect_info[0] == 'invisible': 682 # Trying to login as invisible but privacy list not supported 683 self.disconnect(on_purpose=True) 684 self.dispatch('STATUS', 'offline') 685 self.dispatch('ERROR', (_('Invisibility not supported'), 686 _('Account %s doesn\'t support invisibility.') % self.name)) 687 return 688 # Ask metacontacts before roster 689 self.get_metacontacts() 690 elif self.awaiting_answers[id_][0] == PEP_CONFIG: 691 if iq_obj.getType() == 'error': 692 return 693 if not iq_obj.getTag('pubsub'): 694 return 695 conf = iq_obj.getTag('pubsub').getTag('configure') 696 if not conf: 697 return 698 node = conf.getAttr('node') 699 form_tag = conf.getTag('x', namespace=common.xmpp.NS_DATA) 700 if form_tag: 701 form = common.dataforms.ExtendForm(node=form_tag) 702 self.dispatch('PEP_CONFIG', (node, form)) 703 704 del self.awaiting_answers[id_]
705
706 - def _vCardCB(self, con, vc):
707 """ 708 Called when we receive a vCard Parse the vCard and send it to plugins 709 """ 710 if not vc.getTag('vCard'): 711 return 712 if not vc.getTag('vCard').getNamespace() == common.xmpp.NS_VCARD: 713 return 714 id_ = vc.getID() 715 frm_iq = vc.getFrom() 716 our_jid = gajim.get_jid_from_account(self.name) 717 resource = '' 718 if id_ in self.groupchat_jids: 719 who = self.groupchat_jids[id_] 720 frm, resource = gajim.get_room_and_nick_from_fjid(who) 721 del self.groupchat_jids[id_] 722 elif frm_iq: 723 who = helpers.get_full_jid_from_iq(vc) 724 frm, resource = gajim.get_room_and_nick_from_fjid(who) 725 else: 726 who = frm = our_jid 727 card = vc.getChildren()[0] 728 vcard = self._node_to_dict(card) 729 photo_decoded = None 730 if 'PHOTO' in vcard and isinstance(vcard['PHOTO'], dict) and \ 731 'BINVAL' in vcard['PHOTO']: 732 photo = vcard['PHOTO']['BINVAL'] 733 try: 734 photo_decoded = base64.decodestring(photo) 735 avatar_sha = hashlib.sha1(photo_decoded).hexdigest() 736 except Exception: 737 avatar_sha = '' 738 else: 739 avatar_sha = '' 740 741 if avatar_sha: 742 card.getTag('PHOTO').setTagData('SHA', avatar_sha) 743 744 # Save it to file 745 self._save_vcard_to_hd(who, card) 746 # Save the decoded avatar to a separate file too, and generate files for dbus notifications 747 puny_jid = helpers.sanitize_filename(frm) 748 puny_nick = None 749 begin_path = os.path.join(gajim.AVATAR_PATH, puny_jid) 750 frm_jid = frm 751 if frm in self.room_jids: 752 puny_nick = helpers.sanitize_filename(resource) 753 # create folder if needed 754 if not os.path.isdir(begin_path): 755 os.mkdir(begin_path, 0700) 756 begin_path = os.path.join(begin_path, puny_nick) 757 frm_jid += '/' + resource 758 if photo_decoded: 759 avatar_file = begin_path + '_notif_size_colored.png' 760 if frm_jid == our_jid and avatar_sha != self.vcard_sha: 761 gajim.interface.save_avatar_files(frm, photo_decoded, puny_nick) 762 elif frm_jid != our_jid and (not os.path.exists(avatar_file) or \ 763 frm_jid not in self.vcard_shas or \ 764 avatar_sha != self.vcard_shas[frm_jid]): 765 gajim.interface.save_avatar_files(frm, photo_decoded, puny_nick) 766 if avatar_sha: 767 self.vcard_shas[frm_jid] = avatar_sha 768 elif frm in self.vcard_shas: 769 del self.vcard_shas[frm] 770 else: 771 for ext in ('.jpeg', '.png', '_notif_size_bw.png', 772 '_notif_size_colored.png'): 773 path = begin_path + ext 774 if os.path.isfile(path): 775 os.remove(path) 776 777 vcard['jid'] = frm 778 vcard['resource'] = resource 779 if frm_jid == our_jid: 780 self.dispatch('MYVCARD', vcard) 781 # we re-send our presence with sha if has changed and if we are 782 # not invisible 783 if self.vcard_sha == avatar_sha: 784 return 785 self.vcard_sha = avatar_sha 786 if gajim.SHOW_LIST[self.connected] == 'invisible': 787 return 788 if not self.connection: 789 return 790 sshow = helpers.get_xmpp_show(gajim.SHOW_LIST[self.connected]) 791 p = common.xmpp.Presence(typ = None, priority = self.priority, 792 show = sshow, status = self.status) 793 p = self.add_sha(p) 794 self.connection.send(p) 795 else: 796 #('VCARD', {entry1: data, entry2: {entry21: data, ...}, ...}) 797 self.dispatch('VCARD', vcard)
798 799 # basic connection handlers used here and in zeroconf
800 -class ConnectionHandlersBase:
801 - def __init__(self):
802 # List of IDs we are waiting answers for {id: (type_of_request, data), } 803 self.awaiting_answers = {} 804 # List of IDs that will produce a timeout is answer doesn't arrive 805 # {time_of_the_timeout: (id, message to send to gui), } 806 self.awaiting_timeouts = {} 807 # keep the jids we auto added (transports contacts) to not send the 808 # SUBSCRIBED event to gui 809 self.automatically_added = [] 810 # IDs of jabber:iq:last requests 811 self.last_ids = [] 812 813 # keep track of sessions this connection has with other JIDs 814 self.sessions = {}
815
816 - def _ErrorCB(self, con, iq_obj):
817 log.debug('ErrorCB') 818 id_ = unicode(iq_obj.getID()) 819 if id_ in self.last_ids: 820 gajim.nec.push_incoming_event(LastResultReceivedEvent(None, 821 conn=self, iq_obj=iq_obj)) 822 return True
823
824 - def _LastResultCB(self, con, iq_obj):
825 log.debug('LastResultCB') 826 gajim.nec.push_incoming_event(LastResultReceivedEvent(None, conn=self, 827 iq_obj=iq_obj))
828
829 - def get_sessions(self, jid):
830 """ 831 Get all sessions for the given full jid 832 """ 833 if not gajim.interface.is_pm_contact(jid, self.name): 834 jid = gajim.get_jid_without_resource(jid) 835 836 try: 837 return self.sessions[jid].values() 838 except KeyError: 839 return []
840
841 - def get_or_create_session(self, fjid, thread_id):
842 """ 843 Return an existing session between this connection and 'jid', returns a 844 new one if none exist 845 """ 846 pm = True 847 jid = fjid 848 849 if not gajim.interface.is_pm_contact(fjid, self.name): 850 pm = False 851 jid = gajim.get_jid_without_resource(fjid) 852 853 session = self.find_session(jid, thread_id) 854 855 if session: 856 return session 857 858 if pm: 859 return self.make_new_session(fjid, thread_id, type_='pm') 860 else: 861 return self.make_new_session(fjid, thread_id)
862
863 - def find_session(self, jid, thread_id):
864 try: 865 if not thread_id: 866 return self.find_null_session(jid) 867 else: 868 return self.sessions[jid][thread_id] 869 except KeyError: 870 return None
871
872 - def terminate_sessions(self, send_termination=False):
873 """ 874 Send termination messages and delete all active sessions 875 """ 876 for jid in self.sessions: 877 for thread_id in self.sessions[jid]: 878 self.sessions[jid][thread_id].terminate(send_termination) 879 880 self.sessions = {}
881
882 - def delete_session(self, jid, thread_id):
883 if not jid in self.sessions: 884 jid = gajim.get_jid_without_resource(jid) 885 if not jid in self.sessions: 886 return 887 888 del self.sessions[jid][thread_id] 889 890 if not self.sessions[jid]: 891 del self.sessions[jid]
892
893 - def find_null_session(self, jid):
894 """ 895 Find all of the sessions between us and a remote jid in which we haven't 896 received a thread_id yet and returns the session that we last sent a 897 message to 898 """ 899 sessions = self.sessions[jid].values() 900 901 # sessions that we haven't received a thread ID in 902 idless = [s for s in sessions if not s.received_thread_id] 903 904 # filter out everything except the default session type 905 chat_sessions = [s for s in idless if isinstance(s, 906 gajim.default_session_type)] 907 908 if chat_sessions: 909 # return the session that we last sent a message in 910 return sorted(chat_sessions, key=operator.attrgetter("last_send"))[-1] 911 else: 912 return None
913
914 - def find_controlless_session(self, jid, resource=None):
915 """ 916 Find an active session that doesn't have a control attached 917 """ 918 try: 919 sessions = self.sessions[jid].values() 920 921 # filter out everything except the default session type 922 chat_sessions = [s for s in sessions if isinstance(s, 923 gajim.default_session_type)] 924 925 orphaned = [s for s in chat_sessions if not s.control] 926 927 if resource: 928 orphaned = [s for s in orphaned if s.resource == resource] 929 930 return orphaned[0] 931 except (KeyError, IndexError): 932 return None
933
934 - def make_new_session(self, jid, thread_id=None, type_='chat', cls=None):
935 """ 936 Create and register a new session 937 938 thread_id=None to generate one. 939 type_ should be 'chat' or 'pm'. 940 """ 941 if not cls: 942 cls = gajim.default_session_type 943 944 sess = cls(self, common.xmpp.JID(jid), thread_id, type_) 945 946 # determine if this session is a pm session 947 # if not, discard the resource so that all sessions are stored bare 948 if not type_ == 'pm': 949 jid = gajim.get_jid_without_resource(jid) 950 951 if not jid in self.sessions: 952 self.sessions[jid] = {} 953 954 self.sessions[jid][sess.thread_id] = sess 955 956 return sess
957
958 -class ConnectionHandlers(ConnectionVcard, ConnectionSocks5Bytestream, 959 ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionPEP, 960 ConnectionCaps, ConnectionHandlersBase, ConnectionJingle):
961 - def __init__(self):
962 global HAS_IDLE 963 ConnectionVcard.__init__(self) 964 ConnectionSocks5Bytestream.__init__(self) 965 ConnectionCommands.__init__(self) 966 ConnectionPubSub.__init__(self) 967 ConnectionPEP.__init__(self, account=self.name, dispatcher=self, 968 pubsub_connection=self) 969 ConnectionCaps.__init__(self, account=self.name, 970 dispatch_event=self.dispatch, capscache=capscache.capscache, 971 client_caps_factory=capscache.create_suitable_client_caps) 972 ConnectionJingle.__init__(self) 973 ConnectionHandlersBase.__init__(self) 974 self.gmail_url = None 975 976 # keep the latest subscribed event for each jid to prevent loop when we 977 # acknowledge presences 978 self.subscribed_events = {} 979 # IDs of jabber:iq:version requests 980 self.version_ids = [] 981 # IDs of urn:xmpp:time requests 982 self.entity_time_ids = [] 983 # ID of urn:xmpp:ping requests 984 self.awaiting_xmpp_ping_id = None 985 self.continue_connect_info = None 986 987 try: 988 self.sleeper = common.sleepy.Sleepy() 989 # idle.init() 990 HAS_IDLE = True 991 except Exception: 992 HAS_IDLE = False 993 994 self.gmail_last_tid = None 995 self.gmail_last_time = None 996 997 gajim.ged.register_event_handler('http-auth-received', ged.CORE, 998 self._nec_http_auth_received)
999
1000 - def build_http_auth_answer(self, iq_obj, answer):
1001 if not self.connection or self.connected < 2: 1002 return 1003 if answer == 'yes': 1004 confirm = iq_obj.getTag('confirm') 1005 reply = iq_obj.buildReply('result') 1006 if iq_obj.getName() == 'message': 1007 reply.addChild(node=confirm) 1008 self.connection.send(reply) 1009 elif answer == 'no': 1010 err = common.xmpp.Error(iq_obj, 1011 common.xmpp.protocol.ERR_NOT_AUTHORIZED) 1012 self.connection.send(err)
1013
1014 - def _nec_http_auth_received(self, obj):
1015 if obj.conn.name != self.name: 1016 return 1017 if obj.opt in ('yes', 'no'): 1018 obj.conn.build_http_auth_answer(obj.iq_obj, obj.opt) 1019 return True
1020
1021 - def _HttpAuthCB(self, con, iq_obj):
1022 log.debug('HttpAuthCB') 1023 gajim.nec.push_incoming_event(HttpAuthReceivedEvent(None, conn=self, 1024 iq_obj=iq_obj)) 1025 raise common.xmpp.NodeProcessed
1026
1027 - def _ErrorCB(self, con, iq_obj):
1028 log.debug('ErrorCB') 1029 if ConnectionHandlersBase._ErrorCB(self, con, iq_obj): 1030 return 1031 id_ = unicode(iq_obj.getID()) 1032 if id_ in self.version_ids: 1033 gajim.nec.push_incoming_event(VersionResultReceivedEvent(None, 1034 conn=self, iq_obj=iq_obj)) 1035 return 1036 if id_ in self.entity_time_ids: 1037 gajim.nec.push_incoming_event(LastResultReceivedEvent(None, 1038 conn=self, iq_obj=iq_obj)) 1039 return 1040 jid_from = helpers.get_full_jid_from_iq(iq_obj) 1041 errmsg = iq_obj.getErrorMsg() 1042 errcode = iq_obj.getErrorCode() 1043 self.dispatch('ERROR_ANSWER', (id_, jid_from, errmsg, errcode))
1044
1045 - def _PrivateCB(self, con, iq_obj):
1046 """ 1047 Private Data (XEP 048 and 049) 1048 """ 1049 log.debug('PrivateCB') 1050 query = iq_obj.getTag('query') 1051 storage = query.getTag('storage') 1052 if storage: 1053 ns = storage.getNamespace() 1054 if ns == 'storage:bookmarks': 1055 self._parse_bookmarks(storage, 'xml') 1056 elif ns == 'gajim:prefs': 1057 # Preferences data 1058 # http://www.xmpp.org/extensions/xep-0049.html 1059 #TODO: implement this 1060 pass 1061 elif ns == 'storage:rosternotes': 1062 # Annotations 1063 # http://www.xmpp.org/extensions/xep-0145.html 1064 notes = storage.getTags('note') 1065 for note in notes: 1066 try: 1067 jid = helpers.parse_jid(note.getAttr('jid')) 1068 except common.helpers.InvalidFormat: 1069 log.warn('Invalid JID: %s, ignoring it' % note.getAttr('jid')) 1070 continue 1071 annotation = note.getData() 1072 self.annotations[jid] = annotation
1073
1074 - def _SecLabelCB(self, con, iq_obj):
1075 """ 1076 Security Label callback, used for catalogues. 1077 """ 1078 log.debug('SecLabelCB') 1079 query = iq_obj.getTag('catalog') 1080 to = query.getAttr('to') 1081 items = query.getTags('securitylabel') 1082 labels = {} 1083 ll = [] 1084 for item in items: 1085 label = item.getTag('displaymarking').getData() 1086 labels[label] = item 1087 ll.append(label) 1088 if to not in self.seclabel_catalogues: 1089 self.seclabel_catalogues[to] = [[], None, None] 1090 self.seclabel_catalogues[to][1] = labels 1091 self.seclabel_catalogues[to][2] = ll 1092 for callback in self.seclabel_catalogues[to][0]: 1093 callback() 1094 self.seclabel_catalogues[to][0] = []
1095
1096 - def seclabel_catalogue_request(self, to, callback):
1097 if to not in self.seclabel_catalogues: 1098 self.seclabel_catalogues[to] = [[], None, None] 1099 self.seclabel_catalogues[to][0].append(callback)
1100
1101 - def _parse_bookmarks(self, storage, storage_type):
1102 """ 1103 storage_type can be 'pubsub' or 'xml' to tell from where we got bookmarks 1104 """ 1105 # Bookmarked URLs and Conferences 1106 # http://www.xmpp.org/extensions/xep-0048.html 1107 resend_to_pubsub = False 1108 confs = storage.getTags('conference') 1109 for conf in confs: 1110 autojoin_val = conf.getAttr('autojoin') 1111 if autojoin_val is None: # not there (it's optional) 1112 autojoin_val = False 1113 minimize_val = conf.getAttr('minimize') 1114 if minimize_val is None: # not there (it's optional) 1115 minimize_val = False 1116 print_status = conf.getTagData('print_status') 1117 if not print_status: 1118 print_status = conf.getTagData('show_status') 1119 try: 1120 bm = {'name': conf.getAttr('name'), 1121 'jid': helpers.parse_jid(conf.getAttr('jid')), 1122 'autojoin': autojoin_val, 1123 'minimize': minimize_val, 1124 'password': conf.getTagData('password'), 1125 'nick': conf.getTagData('nick'), 1126 'print_status': print_status} 1127 except common.helpers.InvalidFormat: 1128 log.warn('Invalid JID: %s, ignoring it' % conf.getAttr('jid')) 1129 continue 1130 1131 bm_jids = [b['jid'] for b in self.bookmarks] 1132 if bm['jid'] not in bm_jids: 1133 self.bookmarks.append(bm) 1134 if storage_type == 'xml': 1135 # We got a bookmark that was not in pubsub 1136 resend_to_pubsub = True 1137 self.dispatch('BOOKMARKS', self.bookmarks) 1138 if storage_type == 'pubsub': 1139 # We gor bookmarks from pubsub, now get those from xml to merge them 1140 self.get_bookmarks(storage_type='xml') 1141 if self.pubsub_supported and resend_to_pubsub: 1142 self.store_bookmarks('pubsub')
1143
1144 - def _rosterSetCB(self, con, iq_obj):
1145 log.debug('rosterSetCB') 1146 version = iq_obj.getTagAttr('query', 'ver') 1147 for item in iq_obj.getTag('query').getChildren(): 1148 try: 1149 jid = helpers.parse_jid(item.getAttr('jid')) 1150 except common.helpers.InvalidFormat: 1151 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('jid')) 1152 continue 1153 name = item.getAttr('name') 1154 sub = item.getAttr('subscription') 1155 ask = item.getAttr('ask') 1156 groups = [] 1157 for group in item.getTags('group'): 1158 groups.append(group.getData()) 1159 self.dispatch('ROSTER_INFO', (jid, name, sub, ask, groups)) 1160 account_jid = gajim.get_jid_from_account(self.name) 1161 gajim.logger.add_or_update_contact(account_jid, jid, name, sub, ask, 1162 groups) 1163 if version: 1164 gajim.config.set_per('accounts', self.name, 'roster_version', 1165 version) 1166 if not self.connection or self.connected < 2: 1167 raise common.xmpp.NodeProcessed 1168 reply = common.xmpp.Iq(typ='result', attrs={'id': iq_obj.getID()}, 1169 to=iq_obj.getFrom(), frm=iq_obj.getTo(), xmlns=None) 1170 self.connection.send(reply) 1171 raise common.xmpp.NodeProcessed
1172
1173 - def _VersionCB(self, con, iq_obj):
1174 log.debug('VersionCB') 1175 if not self.connection or self.connected < 2: 1176 return 1177 iq_obj = iq_obj.buildReply('result') 1178 qp = iq_obj.getTag('query') 1179 qp.setTagData('name', 'Gajim') 1180 qp.setTagData('version', gajim.version) 1181 send_os = gajim.config.get_per('accounts', self.name, 'send_os_info') 1182 if send_os: 1183 qp.setTagData('os', helpers.get_os_info()) 1184 self.connection.send(iq_obj) 1185 raise common.xmpp.NodeProcessed
1186
1187 - def _LastCB(self, con, iq_obj):
1188 global HAS_IDLE 1189 log.debug('LastCB') 1190 if not self.connection or self.connected < 2: 1191 return 1192 if HAS_IDLE and gajim.config.get_per('accounts', self.name, 1193 'send_idle_time'): 1194 iq_obj = iq_obj.buildReply('result') 1195 qp = iq_obj.getTag('query') 1196 qp.attrs['seconds'] = int(self.sleeper.getIdleSec()) 1197 else: 1198 iq_obj = iq_obj.buildReply('error') 1199 err = common.xmpp.ErrorNode(name=common.xmpp.NS_STANZAS+' service-unavailable') 1200 iq_obj.addChild(node=err) 1201 1202 self.connection.send(iq_obj) 1203 raise common.xmpp.NodeProcessed
1204
1205 - def _VersionResultCB(self, con, iq_obj):
1206 log.debug('VersionResultCB') 1207 gajim.nec.push_incoming_event(VersionResultReceivedEvent(None, 1208 conn=self, iq_obj=iq_obj))
1209
1210 - def _TimeCB(self, con, iq_obj):
1211 log.debug('TimeCB') 1212 if not self.connection or self.connected < 2: 1213 return 1214 iq_obj = iq_obj.buildReply('result') 1215 qp = iq_obj.getTag('query') 1216 qp.setTagData('utc', strftime('%Y%m%dT%H:%M:%S', gmtime())) 1217 qp.setTagData('tz', helpers.decode_string(tzname[daylight])) 1218 qp.setTagData('display', helpers.decode_string(strftime('%c', 1219 localtime()))) 1220 self.connection.send(iq_obj) 1221 raise common.xmpp.NodeProcessed
1222
1223 - def _TimeRevisedCB(self, con, iq_obj):
1224 log.debug('TimeRevisedCB') 1225 if not self.connection or self.connected < 2: 1226 return 1227 iq_obj = iq_obj.buildReply('result') 1228 qp = iq_obj.setTag('time', 1229 namespace=common.xmpp.NS_TIME_REVISED) 1230 qp.setTagData('utc', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())) 1231 isdst = localtime().tm_isdst 1232 zone = -(timezone, altzone)[isdst] / 60 1233 tzo = (zone / 60, abs(zone % 60)) 1234 qp.setTagData('tzo', '%+03d:%02d' % (tzo)) 1235 self.connection.send(iq_obj) 1236 raise common.xmpp.NodeProcessed
1237
1238 - def _TimeRevisedResultCB(self, con, iq_obj):
1239 log.debug('TimeRevisedResultCB') 1240 gajim.nec.push_incoming_event(TimeResultReceivedEvent(None, 1241 conn=self, iq_obj=iq_obj))
1242
1243 - def _gMailNewMailCB(self, con, gm):
1244 """ 1245 Called when we get notified of new mail messages in gmail account 1246 """ 1247 if not self.connection or self.connected < 2: 1248 return 1249 if not gm.getTag('new-mail'): 1250 return 1251 if gm.getTag('new-mail').getNamespace() == common.xmpp.NS_GMAILNOTIFY: 1252 # we'll now ask the server for the exact number of new messages 1253 jid = gajim.get_jid_from_account(self.name) 1254 log.debug('Got notification of new gmail e-mail on %s. Asking the server for more info.' % jid) 1255 iq = common.xmpp.Iq(typ = 'get') 1256 iq.setID(self.connection.getAnID()) 1257 query = iq.setTag('query') 1258 query.setNamespace(common.xmpp.NS_GMAILNOTIFY) 1259 # we want only be notified about newer mails 1260 if self.gmail_last_tid: 1261 query.setAttr('newer-than-tid', self.gmail_last_tid) 1262 if self.gmail_last_time: 1263 query.setAttr('newer-than-time', self.gmail_last_time) 1264 self.connection.send(iq) 1265 raise common.xmpp.NodeProcessed
1266
1267 - def _gMailQueryCB(self, con, iq_obj):
1268 """ 1269 Called when we receive results from Querying the server for mail messages 1270 in gmail account 1271 """ 1272 log.debug('gMailQueryCB') 1273 gajim.nec.push_incoming_event(GMailQueryReceivedEvent(None, 1274 conn=self, iq_obj=iq_obj)) 1275 raise common.xmpp.NodeProcessed
1276
1277 - def _rosterItemExchangeCB(self, con, msg):
1278 """ 1279 XEP-0144 Roster Item Echange 1280 """ 1281 log.debug('rosterItemExchangeCB') 1282 gajim.nec.push_incoming_event(RosterItemExchangeEvent(None, 1283 conn=self, iq_obj=msg)) 1284 raise common.xmpp.NodeProcessed
1285
1286 - def _messageCB(self, con, msg):
1287 """ 1288 Called when we receive a message 1289 """ 1290 log.debug('MessageCB') 1291 1292 gajim.nec.push_incoming_event(NetworkEvent('raw-message-received', 1293 conn=con, xmpp_msg=msg, account=self.name)) 1294 1295 mtype = msg.getType() 1296 1297 # check if the message is a roster item exchange (XEP-0144) 1298 if msg.getTag('x', namespace=common.xmpp.NS_ROSTERX): 1299 self._rosterItemExchangeCB(con, msg) 1300 return 1301 1302 # check if the message is a XEP-0070 confirmation request 1303 if msg.getTag('confirm', namespace=common.xmpp.NS_HTTP_AUTH): 1304 self._HttpAuthCB(con, msg) 1305 return 1306 1307 try: 1308 frm = helpers.get_full_jid_from_iq(msg) 1309 jid = helpers.get_jid_from_iq(msg) 1310 except helpers.InvalidFormat: 1311 self.dispatch('ERROR', (_('Invalid Jabber ID'), 1312 _('A message from a non-valid JID arrived, it has been ignored.'))) 1313 return 1314 1315 addressTag = msg.getTag('addresses', namespace = common.xmpp.NS_ADDRESS) 1316 1317 # Be sure it comes from one of our resource, else ignore address element 1318 if addressTag and jid == gajim.get_jid_from_account(self.name): 1319 address = addressTag.getTag('address', attrs={'type': 'ofrom'}) 1320 if address: 1321 try: 1322 frm = helpers.parse_jid(address.getAttr('jid')) 1323 except common.helpers.InvalidFormat: 1324 log.warn('Invalid JID: %s, ignoring it' % address.getAttr('jid')) 1325 return 1326 jid = gajim.get_jid_without_resource(frm) 1327 1328 # invitations 1329 invite = None 1330 encTag = msg.getTag('x', namespace=common.xmpp.NS_ENCRYPTED) 1331 1332 if not encTag: 1333 invite = msg.getTag('x', namespace = common.xmpp.NS_MUC_USER) 1334 if invite and not invite.getTag('invite'): 1335 invite = None 1336 1337 # FIXME: Msn transport (CMSN1.2.1 and PyMSN0.10) do NOT RECOMMENDED 1338 # invitation 1339 # stanza (MUC XEP) remove in 2007, as we do not do NOT RECOMMENDED 1340 xtags = msg.getTags('x') 1341 for xtag in xtags: 1342 if xtag.getNamespace() == common.xmpp.NS_CONFERENCE and not invite: 1343 try: 1344 room_jid = helpers.parse_jid(xtag.getAttr('jid')) 1345 except common.helpers.InvalidFormat: 1346 log.warn('Invalid JID: %s, ignoring it' % xtag.getAttr('jid')) 1347 continue 1348 is_continued = False 1349 if xtag.getTag('continue'): 1350 is_continued = True 1351 self.dispatch('GC_INVITATION', (room_jid, frm, '', None, 1352 is_continued)) 1353 return 1354 1355 thread_id = msg.getThread() 1356 1357 if not mtype or mtype not in ('chat', 'groupchat', 'error'): 1358 mtype = 'normal' 1359 1360 msgtxt = msg.getBody() 1361 1362 encrypted = False 1363 xep_200_encrypted = msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO) 1364 1365 session = None 1366 gc_control = gajim.interface.msg_win_mgr.get_gc_control(jid, self.name) 1367 if not gc_control and \ 1368 jid in gajim.interface.minimized_controls[self.name]: 1369 gc_control = gajim.interface.minimized_controls[self.name][jid] 1370 1371 if gc_control and jid == frm: # message from a gc without a resource 1372 mtype = 'groupchat' 1373 1374 if mtype != 'groupchat': 1375 session = self.get_or_create_session(frm, thread_id) 1376 1377 if thread_id and not session.received_thread_id: 1378 session.received_thread_id = True 1379 1380 session.last_receive = time_time() 1381 1382 # check if the message is a XEP-0020 feature negotiation request 1383 if msg.getTag('feature', namespace=common.xmpp.NS_FEATURE): 1384 if gajim.HAVE_PYCRYPTO: 1385 feature = msg.getTag(name='feature', namespace=common.xmpp.NS_FEATURE) 1386 form = common.xmpp.DataForm(node=feature.getTag('x')) 1387 1388 if form['FORM_TYPE'] == 'urn:xmpp:ssn': 1389 session.handle_negotiation(form) 1390 else: 1391 reply = msg.buildReply() 1392 reply.setType('error') 1393 1394 reply.addChild(feature) 1395 err = common.xmpp.ErrorNode('service-unavailable', typ='cancel') 1396 reply.addChild(node=err) 1397 1398 con.send(reply) 1399 1400 raise common.xmpp.NodeProcessed 1401 1402 return 1403 1404 if msg.getTag('init', namespace=common.xmpp.NS_ESESSION_INIT): 1405 init = msg.getTag(name='init', namespace=common.xmpp.NS_ESESSION_INIT) 1406 form = common.xmpp.DataForm(node=init.getTag('x')) 1407 1408 session.handle_negotiation(form) 1409 1410 raise common.xmpp.NodeProcessed 1411 1412 tim = msg.getTimestamp() 1413 tim = helpers.datetime_tuple(tim) 1414 tim = localtime(timegm(tim)) 1415 1416 if xep_200_encrypted: 1417 encrypted = 'xep200' 1418 1419 try: 1420 msg = session.decrypt_stanza(msg) 1421 msgtxt = msg.getBody() 1422 except Exception: 1423 self.dispatch('FAILED_DECRYPT', (frm, tim, session)) 1424 1425 # Receipt requested 1426 # TODO: We shouldn't answer if we're invisible! 1427 contact = gajim.contacts.get_contact(self.name, jid) 1428 nick = gajim.get_room_and_nick_from_fjid(frm)[1] 1429 gc_contact = gajim.contacts.get_gc_contact(self.name, jid, nick) 1430 if msg.getTag('request', namespace=common.xmpp.NS_RECEIPTS) \ 1431 and gajim.config.get_per('accounts', self.name, 1432 'answer_receipts') and ((contact and contact.sub \ 1433 not in (u'to', u'none')) or gc_contact) and mtype != 'error': 1434 receipt = common.xmpp.Message(to=frm, typ='chat') 1435 receipt.setID(msg.getID()) 1436 receipt.setTag('received', 1437 namespace='urn:xmpp:receipts') 1438 1439 if thread_id: 1440 receipt.setThread(thread_id) 1441 con.send(receipt) 1442 1443 # We got our message's receipt 1444 if msg.getTag('received', namespace=common.xmpp.NS_RECEIPTS) and \ 1445 session.control and gajim.config.get_per('accounts', self.name, 1446 'request_receipt'): 1447 session.control.conv_textview.hide_xep0184_warning(msg.getID()) 1448 1449 if encTag and self.USE_GPG: 1450 encmsg = encTag.getData() 1451 1452 keyID = gajim.config.get_per('accounts', self.name, 'keyid') 1453 if keyID: 1454 def decrypt_thread(encmsg, keyID): 1455 decmsg = self.gpg.decrypt(encmsg, keyID) 1456 # \x00 chars are not allowed in C (so in GTK) 1457 msgtxt = helpers.decode_string(decmsg.replace('\x00', '')) 1458 encrypted = 'xep27' 1459 return (msgtxt, encrypted)
1460 gajim.thread_interface(decrypt_thread, [encmsg, keyID], 1461 self._on_message_decrypted, [mtype, msg, session, frm, jid, 1462 invite, tim]) 1463 return 1464 self._on_message_decrypted((msgtxt, encrypted), mtype, msg, session, frm, 1465 jid, invite, tim)
1466
1467 - def _on_message_decrypted(self, output, mtype, msg, session, frm, jid, 1468 invite, tim):
1469 msgtxt, encrypted = output 1470 if mtype == 'error': 1471 self.dispatch_error_message(msg, msgtxt, session, frm, tim) 1472 elif mtype == 'groupchat': 1473 self.dispatch_gc_message(msg, frm, msgtxt, jid, tim) 1474 elif invite is not None: 1475 self.dispatch_invite_message(invite, frm) 1476 else: 1477 if isinstance(session, gajim.default_session_type): 1478 session.received(frm, msgtxt, tim, encrypted, msg) 1479 else: 1480 session.received(msg)
1481 # END messageCB 1482 1483 # process and dispatch an error message
1484 - def dispatch_error_message(self, msg, msgtxt, session, frm, tim):
1485 error_msg = msg.getErrorMsg() 1486 1487 if not error_msg: 1488 error_msg = msgtxt 1489 msgtxt = None 1490 1491 subject = msg.getSubject() 1492 1493 if session.is_loggable(): 1494 try: 1495 gajim.logger.write('error', frm, error_msg, tim=tim, 1496 subject=subject) 1497 except exceptions.PysqliteOperationalError, e: 1498 self.dispatch('DB_ERROR', (_('Disk Write Error'), str(e))) 1499 except exceptions.DatabaseMalformed: 1500 pritext = _('Database Error') 1501 sectext = _('The database file (%s) cannot be read. Try to repair ' 1502 'it (see http://trac.gajim.org/wiki/DatabaseBackup) or remove ' 1503 'it (all history will be lost).') % common.logger.LOG_DB_PATH 1504 self.dispatch('DB_ERROR', (pritext, sectext)) 1505 self.dispatch('MSGERROR', (frm, msg.getErrorCode(), error_msg, msgtxt, 1506 tim, session))
1507
1508 - def _on_bob_received(self, conn, result, cid):
1509 """ 1510 Called when we receive BoB data 1511 """ 1512 if cid not in self.awaiting_cids: 1513 return 1514 1515 if result.getType() == 'result': 1516 data = msg.getTags('data', namespace=common.xmpp.NS_BOB) 1517 if data.getAttr('cid') == cid: 1518 for func in self.awaiting_cids[cid]: 1519 cb = func[0] 1520 args = func[1] 1521 pos = func[2] 1522 bob_data = data.getData() 1523 def recurs(node, cid, data): 1524 if node.getData() == 'cid:' + cid: 1525 node.setData(data) 1526 else: 1527 for child in node.getChildren(): 1528 recurs(child, cid, data)
1529 recurs(args[pos], cid, bob_data) 1530 cb(*args) 1531 del self.awaiting_cids[cid] 1532 return 1533 1534 # An error occured, call callback without modifying data. 1535 for func in self.awaiting_cids[cid]: 1536 cb = func[0] 1537 args = func[1] 1538 cb(*args) 1539 del self.awaiting_cids[cid] 1540
1541 - def get_bob_data(self, cid, to, callback, args, position):
1542 """ 1543 Request for BoB (XEP-0231) and when data will arrive, call callback 1544 with given args, after having replaced cid by it's data in 1545 args[position] 1546 """ 1547 if cid in self.awaiting_cids: 1548 self.awaiting_cids[cid].appends((callback, args, position)) 1549 else: 1550 self.awaiting_cids[cid] = [(callback, args, position)] 1551 iq = common.xmpp.Iq(to=to, typ='get') 1552 data = iq.addChild(name='data', attrs={'cid': cid}, 1553 namespace=common.xmpp.NS_BOB) 1554 self.connection.SendAndCallForResponse(iq, self._on_bob_received, 1555 {'cid': cid})
1556 1557 # process and dispatch a groupchat message
1558 - def dispatch_gc_message(self, msg, frm, msgtxt, jid, tim):
1559 has_timestamp = bool(msg.timestamp) 1560 1561 subject = msg.getSubject() 1562 1563 if subject is not None: 1564 self.dispatch('GC_SUBJECT', (frm, subject, msgtxt, has_timestamp)) 1565 return 1566 1567 statusCode = msg.getStatusCode() 1568 1569 if not msg.getTag('body'): # no <body> 1570 # It could be a config change. See 1571 # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify 1572 if msg.getTag('x'): 1573 if statusCode != []: 1574 self.dispatch('GC_CONFIG_CHANGE', (jid, statusCode)) 1575 return 1576 1577 displaymarking = None 1578 seclabel = msg.getTag('securitylabel') 1579 if seclabel and seclabel.getNamespace() == common.xmpp.NS_SECLABEL: 1580 displaymarking = seclabel.getTag('displaymarking') # Ignore message from room in which we are not 1581 if jid not in self.last_history_time: 1582 return 1583 1584 captcha = msg.getTag('captcha', namespace=common.xmpp.NS_CAPTCHA) 1585 if captcha: 1586 captcha = captcha.getTag('x', namespace=common.xmpp.NS_DATA) 1587 for field in captcha.getTags('field'): 1588 for media in field.getTags('media'): 1589 for uri in media.getTags('uri'): 1590 uri_data = uri.getData() 1591 if uri_data.startswith('cid:'): 1592 uri_data = uri_data[4:] 1593 found = False 1594 for data in msg.getTags('data', 1595 namespace=common.xmpp.NS_BOB): 1596 if data.getAttr('cid') == uri_data: 1597 uri.setData(data.getData()) 1598 found = True 1599 if not found: 1600 self.get_bob_data(uri_data, frm, 1601 self.dispatch_gc_message, [msg, frm, msgtxt, 1602 jid, tim], 0) 1603 return 1604 self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, 1605 msg.getXHTML(), statusCode, displaymarking, captcha)) 1606 1607 tim_int = int(float(mktime(tim))) 1608 if gajim.config.should_log(self.name, jid) and not \ 1609 tim_int <= self.last_history_time[jid] and msgtxt and frm.find('/') >= 0: 1610 # if frm.find('/') < 0, it means message comes from room itself 1611 # usually it hold description and can be send at each connection 1612 # so don't store it in logs 1613 try: 1614 gajim.logger.write('gc_msg', frm, msgtxt, tim=tim) 1615 # store in memory time of last message logged. 1616 # this will also be saved in rooms_last_message_time table 1617 # when we quit this muc 1618 self.last_history_time[jid] = mktime(tim) 1619 1620 except exceptions.PysqliteOperationalError, e: 1621 self.dispatch('DB_ERROR', (_('Disk Write Error'), str(e))) 1622 except exceptions.DatabaseMalformed: 1623 pritext = _('Database Error') 1624 sectext = _('The database file (%s) cannot be read. Try to repair ' 1625 'it (see http://trac.gajim.org/wiki/DatabaseBackup) or remove ' 1626 'it (all history will be lost).') % common.logger.LOG_DB_PATH 1627 self.dispatch('DB_ERROR', (pritext, sectext))
1628
1629 - def dispatch_invite_message(self, invite, frm):
1630 item = invite.getTag('invite') 1631 try: 1632 jid_from = helpers.parse_jid(item.getAttr('from')) 1633 except common.helpers.InvalidFormat: 1634 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('from')) 1635 return 1636 reason = item.getTagData('reason') 1637 item = invite.getTag('password') 1638 password = invite.getTagData('password') 1639 1640 is_continued = False 1641 if invite.getTag('invite').getTag('continue'): 1642 is_continued = True 1643 self.dispatch('GC_INVITATION', (frm, jid_from, reason, password, 1644 is_continued))
1645
1646 - def _presenceCB(self, con, prs):
1647 """ 1648 Called when we receive a presence 1649 """ 1650 gajim.nec.push_incoming_event(NetworkEvent('raw-pres-received', 1651 conn=con, xmpp_pres=prs)) 1652 ptype = prs.getType() 1653 if ptype == 'available': 1654 ptype = None 1655 rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed', 1656 'unsubscribe', 'unsubscribed') 1657 if ptype and not ptype in rfc_types: 1658 ptype = None 1659 log.debug('PresenceCB: %s' % ptype) 1660 if not self.connection or self.connected < 2: 1661 log.debug('account is no more connected') 1662 return 1663 try: 1664 who = helpers.get_full_jid_from_iq(prs) 1665 except Exception: 1666 if prs.getTag('error') and prs.getTag('error').getTag('jid-malformed'): 1667 # wrong jid, we probably tried to change our nick in a room to a non 1668 # valid one 1669 who = str(prs.getFrom()) 1670 jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who) 1671 self.dispatch('GC_MSG', (jid_stripped, 1672 _('Nickname not allowed: %s') % resource, None, False, None, [])) 1673 return 1674 jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who) 1675 timestamp = None 1676 id_ = prs.getID() 1677 is_gc = False # is it a GC presence ? 1678 sigTag = None 1679 ns_muc_user_x = None 1680 avatar_sha = None 1681 # XEP-0172 User Nickname 1682 user_nick = prs.getTagData('nick') 1683 if not user_nick: 1684 user_nick = '' 1685 contact_nickname = None 1686 transport_auto_auth = False 1687 # XEP-0203 1688 delay_tag = prs.getTag('delay', namespace=common.xmpp.NS_DELAY2) 1689 if delay_tag: 1690 tim = prs.getTimestamp2() 1691 tim = helpers.datetime_tuple(tim) 1692 timestamp = localtime(timegm(tim)) 1693 xtags = prs.getTags('x') 1694 for x in xtags: 1695 namespace = x.getNamespace() 1696 if namespace.startswith(common.xmpp.NS_MUC): 1697 is_gc = True 1698 if namespace == common.xmpp.NS_MUC_USER and x.getTag('destroy'): 1699 ns_muc_user_x = x 1700 elif namespace == common.xmpp.NS_SIGNED: 1701 sigTag = x 1702 elif namespace == common.xmpp.NS_VCARD_UPDATE: 1703 avatar_sha = x.getTagData('photo') 1704 contact_nickname = x.getTagData('nickname') 1705 elif namespace == common.xmpp.NS_DELAY and not timestamp: 1706 # XEP-0091 1707 tim = prs.getTimestamp() 1708 tim = helpers.datetime_tuple(tim) 1709 timestamp = localtime(timegm(tim)) 1710 elif namespace == 'http://delx.cjb.net/protocol/roster-subsync': 1711 # see http://trac.gajim.org/ticket/326 1712 agent = gajim.get_server_from_jid(jid_stripped) 1713 if self.connection.getRoster().getItem(agent): # to be sure it's a transport contact 1714 transport_auto_auth = True 1715 1716 if not is_gc and id_ and id_.startswith('gajim_muc_') and \ 1717 ptype == 'error': 1718 # Error presences may not include sent stanza, so we don't detect it's 1719 # a muc preence. So detect it by ID 1720 h = hmac.new(self.secret_hmac, jid_stripped).hexdigest()[:6] 1721 if id_.split('_')[-1] == h: 1722 is_gc = True 1723 status = prs.getStatus() or '' 1724 show = prs.getShow() 1725 if show not in ('chat', 'away', 'xa', 'dnd'): 1726 show = '' # We ignore unknown show 1727 if not ptype and not show: 1728 show = 'online' 1729 elif ptype == 'unavailable': 1730 show = 'offline' 1731 1732 prio = prs.getPriority() 1733 try: 1734 prio = int(prio) 1735 except Exception: 1736 prio = 0 1737 keyID = '' 1738 if sigTag and self.USE_GPG and ptype != 'error': 1739 # error presences contain our own signature 1740 # verify 1741 sigmsg = sigTag.getData() 1742 keyID = self.gpg.verify(status, sigmsg) 1743 1744 if is_gc: 1745 if ptype == 'error': 1746 errcon = prs.getError() 1747 errmsg = prs.getErrorMsg() 1748 errcode = prs.getErrorCode() 1749 room_jid, nick = gajim.get_room_and_nick_from_fjid(who) 1750 1751 gc_control = gajim.interface.msg_win_mgr.get_gc_control(room_jid, 1752 self.name) 1753 1754 # If gc_control is missing - it may be minimized. Try to get it from 1755 # there. If it's not there - then it's missing anyway and will 1756 # remain set to None. 1757 if gc_control is None: 1758 minimized = gajim.interface.minimized_controls[self.name] 1759 gc_control = minimized.get(room_jid) 1760 1761 if errcode == '502': 1762 # Internal Timeout: 1763 self.dispatch('NOTIFY', (jid_stripped, 'error', errmsg, resource, 1764 prio, keyID, timestamp, None)) 1765 elif (errcode == '503'): 1766 if gc_control is None or gc_control.autorejoin is None: 1767 # maximum user number reached 1768 self.dispatch('GC_ERROR', (gc_control, 1769 _('Unable to join group chat'), 1770 _('Maximum number of users for %s has been ' 1771 'reached') % room_jid)) 1772 elif (errcode == '401') or (errcon == 'not-authorized'): 1773 # password required to join 1774 self.dispatch('GC_PASSWORD_REQUIRED', (room_jid, nick)) 1775 elif (errcode == '403') or (errcon == 'forbidden'): 1776 # we are banned 1777 self.dispatch('GC_ERROR', (gc_control, 1778 _('Unable to join group chat'), 1779 _('You are banned from group chat %s.') % room_jid)) 1780 elif (errcode == '404') or (errcon in ('item-not-found', 1781 'remote-server-not-found')): 1782 if gc_control is None or gc_control.autorejoin is None: 1783 # group chat does not exist 1784 self.dispatch('GC_ERROR', (gc_control, 1785 _('Unable to join group chat'), 1786 _('Group chat %s does not exist.') % room_jid)) 1787 elif (errcode == '405') or (errcon == 'not-allowed'): 1788 self.dispatch('GC_ERROR', (gc_control, 1789 _('Unable to join group chat'), 1790 _('Group chat creation is restricted.'))) 1791 elif (errcode == '406') or (errcon == 'not-acceptable'): 1792 self.dispatch('GC_ERROR', (gc_control, 1793 _('Unable to join group chat'), 1794 _('Your registered nickname must be used in group chat ' 1795 '%s.') % room_jid)) 1796 elif (errcode == '407') or (errcon == 'registration-required'): 1797 self.dispatch('GC_ERROR', (gc_control, 1798 _('Unable to join group chat'), 1799 _('You are not in the members list in groupchat %s.') %\ 1800 room_jid)) 1801 elif (errcode == '409') or (errcon == 'conflict'): 1802 # nick conflict 1803 room_jid = gajim.get_room_from_fjid(who) 1804 self.dispatch('ASK_NEW_NICK', (room_jid,)) 1805 else: # print in the window the error 1806 self.dispatch('ERROR_ANSWER', ('', jid_stripped, 1807 errmsg, errcode)) 1808 if not ptype or ptype == 'unavailable': 1809 if gajim.config.get('log_contact_status_changes') and \ 1810 gajim.config.should_log(self.name, jid_stripped): 1811 gc_c = gajim.contacts.get_gc_contact(self.name, jid_stripped, 1812 resource) 1813 st = status or '' 1814 if gc_c: 1815 jid = gc_c.jid 1816 else: 1817 jid = prs.getJid() 1818 if jid: 1819 # we know real jid, save it in db 1820 st += ' (%s)' % jid 1821 try: 1822 gajim.logger.write('gcstatus', who, st, show) 1823 except exceptions.PysqliteOperationalError, e: 1824 self.dispatch('DB_ERROR', (_('Disk Write Error'), 1825 str(e))) 1826 except exceptions.DatabaseMalformed: 1827 pritext = _('Database Error') 1828 sectext = _('The database file (%s) cannot be read. Try to ' 1829 'repair it (see http://trac.gajim.org/wiki/DatabaseBackup)' 1830 ' or remove it (all history will be lost).') % \ 1831 common.logger.LOG_DB_PATH 1832 self.dispatch('DB_ERROR', (pritext, sectext)) 1833 if avatar_sha or avatar_sha == '': 1834 if avatar_sha == '': 1835 # contact has no avatar 1836 puny_nick = helpers.sanitize_filename(resource) 1837 gajim.interface.remove_avatar_files(jid_stripped, puny_nick) 1838 # if it's a gc presence, don't ask vcard here. We may ask it to 1839 # real jid in gui part. 1840 if ns_muc_user_x: 1841 # Room has been destroyed. see 1842 # http://www.xmpp.org/extensions/xep-0045.html#destroyroom 1843 reason = _('Room has been destroyed') 1844 destroy = ns_muc_user_x.getTag('destroy') 1845 r = destroy.getTagData('reason') 1846 if r: 1847 reason += ' (%s)' % r 1848 if destroy.getAttr('jid'): 1849 try: 1850 jid = helpers.parse_jid(destroy.getAttr('jid')) 1851 reason += '\n' + _('You can join this room instead: %s') \ 1852 % jid 1853 except common.helpers.InvalidFormat: 1854 pass 1855 statusCode = ['destroyed'] 1856 else: 1857 reason = prs.getReason() 1858 statusCode = prs.getStatusCode() 1859 self.dispatch('GC_NOTIFY', (jid_stripped, show, status, resource, 1860 prs.getRole(), prs.getAffiliation(), prs.getJid(), 1861 reason, prs.getActor(), statusCode, prs.getNewNick(), 1862 avatar_sha)) 1863 return 1864 1865 if ptype == 'subscribe': 1866 log.debug('subscribe request from %s' % who) 1867 if who.find('@') <= 0 and who in self.agent_registrations: 1868 self.agent_registrations[who]['sub_received'] = True 1869 if not self.agent_registrations[who]['roster_push']: 1870 # We'll reply after roster push result 1871 return 1872 if gajim.config.get_per('accounts', self.name, 'autoauth') or \ 1873 who.find('@') <= 0 or jid_stripped in self.jids_for_auto_auth or \ 1874 transport_auto_auth: 1875 if self.connection: 1876 p = common.xmpp.Presence(who, 'subscribed') 1877 p = self.add_sha(p) 1878 self.connection.send(p) 1879 if who.find('@') <= 0 or transport_auto_auth: 1880 self.dispatch('NOTIFY', (jid_stripped, 'offline', 'offline', 1881 resource, prio, keyID, timestamp, None)) 1882 if transport_auto_auth: 1883 self.automatically_added.append(jid_stripped) 1884 self.request_subscription(jid_stripped, name = user_nick) 1885 else: 1886 if not status: 1887 status = _('I would like to add you to my roster.') 1888 self.dispatch('SUBSCRIBE', (jid_stripped, status, user_nick)) 1889 elif ptype == 'subscribed': 1890 if jid_stripped in self.automatically_added: 1891 self.automatically_added.remove(jid_stripped) 1892 else: 1893 # detect a subscription loop 1894 if jid_stripped not in self.subscribed_events: 1895 self.subscribed_events[jid_stripped] = [] 1896 self.subscribed_events[jid_stripped].append(time_time()) 1897 block = False 1898 if len(self.subscribed_events[jid_stripped]) > 5: 1899 if time_time() - self.subscribed_events[jid_stripped][0] < 5: 1900 block = True 1901 self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:] 1902 if block: 1903 gajim.config.set_per('account', self.name, 1904 'dont_ack_subscription', True) 1905 else: 1906 self.dispatch('SUBSCRIBED', (jid_stripped, resource)) 1907 # BE CAREFUL: no con.updateRosterItem() in a callback 1908 log.debug(_('we are now subscribed to %s') % who) 1909 elif ptype == 'unsubscribe': 1910 log.debug(_('unsubscribe request from %s') % who) 1911 elif ptype == 'unsubscribed': 1912 log.debug(_('we are now unsubscribed from %s') % who) 1913 # detect a unsubscription loop 1914 if jid_stripped not in self.subscribed_events: 1915 self.subscribed_events[jid_stripped] = [] 1916 self.subscribed_events[jid_stripped].append(time_time()) 1917 block = False 1918 if len(self.subscribed_events[jid_stripped]) > 5: 1919 if time_time() - self.subscribed_events[jid_stripped][0] < 5: 1920 block = True 1921 self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:] 1922 if block: 1923 gajim.config.set_per('account', self.name, 'dont_ack_subscription', 1924 True) 1925 else: 1926 self.dispatch('UNSUBSCRIBED', jid_stripped) 1927 elif ptype == 'error': 1928 errmsg = prs.getError() 1929 errcode = prs.getErrorCode() 1930 if errcode != '502': # Internal Timeout: 1931 # print in the window the error 1932 self.dispatch('ERROR_ANSWER', ('', jid_stripped, 1933 errmsg, errcode)) 1934 if errcode != '409': # conflict # See #5120 1935 self.dispatch('NOTIFY', (jid_stripped, 'error', errmsg, resource, 1936 prio, keyID, timestamp, None)) 1937 1938 if ptype == 'unavailable': 1939 for jid in [jid_stripped, who]: 1940 if jid not in self.sessions: 1941 continue 1942 # automatically terminate sessions that they haven't sent a thread 1943 # ID in, only if other part support thread ID 1944 for sess in self.sessions[jid].values(): 1945 if not sess.received_thread_id: 1946 contact = gajim.contacts.get_contact(self.name, jid) 1947 # FIXME: I don't know if this is the correct behavior here. 1948 # Anyway, it is the old behavior when we assumed that 1949 # not-existing contacts don't support anything 1950 contact_exists = bool(contact) 1951 session_supported = contact_exists and ( 1952 contact.supports(common.xmpp.NS_SSN) or 1953 contact.supports(common.xmpp.NS_ESESSION)) 1954 if session_supported: 1955 sess.terminate() 1956 del self.sessions[jid][sess.thread_id] 1957 1958 if avatar_sha is not None and ptype != 'error': 1959 if jid_stripped not in self.vcard_shas: 1960 cached_vcard = self.get_cached_vcard(jid_stripped) 1961 if cached_vcard and 'PHOTO' in cached_vcard and \ 1962 'SHA' in cached_vcard['PHOTO']: 1963 self.vcard_shas[jid_stripped] = cached_vcard['PHOTO']['SHA'] 1964 else: 1965 self.vcard_shas[jid_stripped] = '' 1966 if avatar_sha != self.vcard_shas[jid_stripped]: 1967 # avatar has been updated 1968 self.request_vcard(jid_stripped) 1969 if not ptype or ptype == 'unavailable': 1970 if gajim.config.get('log_contact_status_changes') and \ 1971 gajim.config.should_log(self.name, jid_stripped): 1972 try: 1973 gajim.logger.write('status', jid_stripped, status, show) 1974 except exceptions.PysqliteOperationalError, e: 1975 self.dispatch('DB_ERROR', (_('Disk Write Error'), str(e))) 1976 except exceptions.DatabaseMalformed: 1977 pritext = _('Database Error') 1978 sectext = _('The database file (%s) cannot be read. Try to ' 1979 'repair it (see http://trac.gajim.org/wiki/DatabaseBackup) ' 1980 'or remove it (all history will be lost).') % \ 1981 common.logger.LOG_DB_PATH 1982 self.dispatch('DB_ERROR', (pritext, sectext)) 1983 our_jid = gajim.get_jid_from_account(self.name) 1984 if jid_stripped == our_jid and resource == self.server_resource: 1985 # We got our own presence 1986 self.dispatch('STATUS', show) 1987 else: 1988 self.dispatch('NOTIFY', (jid_stripped, show, status, resource, prio, 1989 keyID, timestamp, contact_nickname))
1990 # END presenceCB 1991
1992 - def _StanzaArrivedCB(self, con, obj):
1993 self.last_io = gajim.idlequeue.current_time()
1994
1995 - def _MucOwnerCB(self, con, iq_obj):
1996 log.debug('MucOwnerCB') 1997 qp = iq_obj.getQueryPayload() 1998 node = None 1999 for q in qp: 2000 if q.getNamespace() == common.xmpp.NS_DATA: 2001 node = q 2002 if not node: 2003 return 2004 self.dispatch('GC_CONFIG', (helpers.get_full_jid_from_iq(iq_obj), node))
2005
2006 - def _MucAdminCB(self, con, iq_obj):
2007 log.debug('MucAdminCB') 2008 items = iq_obj.getTag('query', namespace=common.xmpp.NS_MUC_ADMIN).\ 2009 getTags('item') 2010 users_dict = {} 2011 for item in items: 2012 if item.has_attr('jid') and item.has_attr('affiliation'): 2013 try: 2014 jid = helpers.parse_jid(item.getAttr('jid')) 2015 except common.helpers.InvalidFormat: 2016 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('jid')) 2017 continue 2018 affiliation = item.getAttr('affiliation') 2019 users_dict[jid] = {'affiliation': affiliation} 2020 if item.has_attr('nick'): 2021 users_dict[jid]['nick'] = item.getAttr('nick') 2022 if item.has_attr('role'): 2023 users_dict[jid]['role'] = item.getAttr('role') 2024 reason = item.getTagData('reason') 2025 if reason: 2026 users_dict[jid]['reason'] = reason 2027 2028 self.dispatch('GC_AFFILIATION', (helpers.get_full_jid_from_iq(iq_obj), 2029 users_dict))
2030
2031 - def _MucErrorCB(self, con, iq_obj):
2032 log.debug('MucErrorCB') 2033 jid = helpers.get_full_jid_from_iq(iq_obj) 2034 errmsg = iq_obj.getError() 2035 errcode = iq_obj.getErrorCode() 2036 self.dispatch('MSGERROR', (jid, errcode, errmsg))
2037
2038 - def _IqPingCB(self, con, iq_obj):
2039 log.debug('IqPingCB') 2040 if not self.connection or self.connected < 2: 2041 return 2042 iq_obj = iq_obj.buildReply('result') 2043 self.connection.send(iq_obj) 2044 raise common.xmpp.NodeProcessed
2045
2046 - def _PrivacySetCB(self, con, iq_obj):
2047 """ 2048 Privacy lists (XEP 016) 2049 2050 A list has been set. 2051 """ 2052 log.debug('PrivacySetCB') 2053 if not self.connection or self.connected < 2: 2054 return 2055 result = iq_obj.buildReply('result') 2056 q = result.getTag('query') 2057 if q: 2058 result.delChild(q) 2059 self.connection.send(result) 2060 raise common.xmpp.NodeProcessed
2061
2062 - def _getRoster(self):
2063 log.debug('getRosterCB') 2064 if not self.connection: 2065 return 2066 self.connection.getRoster(self._on_roster_set) 2067 self.discoverItems(gajim.config.get_per('accounts', self.name, 2068 'hostname'), id_prefix='Gajim_') 2069 if gajim.config.get_per('accounts', self.name, 'use_ft_proxies'): 2070 self.discover_ft_proxies()
2071
2072 - def discover_ft_proxies(self):
2073 cfg_proxies = gajim.config.get_per('accounts', self.name, 2074 'file_transfer_proxies') 2075 our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name) + '/' +\ 2076 self.server_resource) 2077 if cfg_proxies: 2078 proxies = [e.strip() for e in cfg_proxies.split(',')] 2079 for proxy in proxies: 2080 gajim.proxy65_manager.resolve(proxy, self.connection, our_jid)
2081
2082 - def _on_roster_set(self, roster):
2083 roster_version = roster.version 2084 received_from_server = roster.received_from_server 2085 raw_roster = roster.getRaw() 2086 roster = {} 2087 our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name)) 2088 if self.connected > 1 and self.continue_connect_info: 2089 msg = self.continue_connect_info[1] 2090 sign_msg = self.continue_connect_info[2] 2091 signed = '' 2092 send_first_presence = True 2093 if sign_msg: 2094 signed = self.get_signed_presence(msg, self._send_first_presence) 2095 if signed is None: 2096 self.dispatch('GPG_PASSWORD_REQUIRED', 2097 (self._send_first_presence,)) 2098 # _send_first_presence will be called when user enter passphrase 2099 send_first_presence = False 2100 if send_first_presence: 2101 self._send_first_presence(signed) 2102 2103 for jid in raw_roster: 2104 try: 2105 j = helpers.parse_jid(jid) 2106 except Exception: 2107 print >> sys.stderr, _('JID %s is not RFC compliant. It will not be added to your roster. Use roster management tools such as http://jru.jabberstudio.org/ to remove it') % jid 2108 else: 2109 infos = raw_roster[jid] 2110 if jid != our_jid and (not infos['subscription'] or \ 2111 infos['subscription'] == 'none') and (not infos['ask'] or \ 2112 infos['ask'] == 'none') and not infos['name'] and \ 2113 not infos['groups']: 2114 # remove this useless item, it won't be shown in roster anyway 2115 self.connection.getRoster().delItem(jid) 2116 elif jid != our_jid: # don't add our jid 2117 roster[j] = raw_roster[jid] 2118 if gajim.jid_is_transport(jid) and \ 2119 not gajim.get_transport_name_from_jid(jid): 2120 # we can't determine which iconset to use 2121 self.discoverInfo(jid) 2122 2123 gajim.logger.replace_roster(self.name, roster_version, roster) 2124 if received_from_server: 2125 for contact in gajim.contacts.iter_contacts(self.name): 2126 if not contact.is_groupchat() and contact.jid not in roster and \ 2127 contact.jid != gajim.get_jid_from_account(self.name): 2128 self.dispatch('ROSTER_INFO', (contact.jid, None, None, None, 2129 ())) 2130 for jid in roster: 2131 self.dispatch('ROSTER_INFO', (jid, roster[jid]['name'], 2132 roster[jid]['subscription'], roster[jid]['ask'], 2133 roster[jid]['groups']))
2134
2135 - def _send_first_presence(self, signed = ''):
2136 show = self.continue_connect_info[0] 2137 msg = self.continue_connect_info[1] 2138 sign_msg = self.continue_connect_info[2] 2139 if sign_msg and not signed: 2140 signed = self.get_signed_presence(msg) 2141 if signed is None: 2142 self.dispatch('BAD_PASSPHRASE', ()) 2143 self.USE_GPG = False 2144 signed = '' 2145 self.connected = gajim.SHOW_LIST.index(show) 2146 sshow = helpers.get_xmpp_show(show) 2147 # send our presence 2148 if show == 'invisible': 2149 self.send_invisible_presence(msg, signed, True) 2150 return 2151 if show not in ['offline', 'online', 'chat', 'away', 'xa', 'dnd']: 2152 return 2153 priority = gajim.get_priority(self.name, sshow) 2154 our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name)) 2155 vcard = self.get_cached_vcard(our_jid) 2156 if vcard and 'PHOTO' in vcard and 'SHA' in vcard['PHOTO']: 2157 self.vcard_sha = vcard['PHOTO']['SHA'] 2158 p = common.xmpp.Presence(typ = None, priority = priority, show = sshow) 2159 p = self.add_sha(p) 2160 if msg: 2161 p.setStatus(msg) 2162 if signed: 2163 p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed) 2164 2165 if self.connection: 2166 self.connection.send(p) 2167 self.priority = priority 2168 self.dispatch('STATUS', show) 2169 if self.vcard_supported: 2170 # ask our VCard 2171 self.request_vcard(None) 2172 2173 # Get bookmarks from private namespace 2174 self.get_bookmarks() 2175 2176 # Get annotations from private namespace 2177 self.get_annotations() 2178 2179 # Inform GUI we just signed in 2180 self.dispatch('SIGNED_IN', ()) 2181 self.send_awaiting_pep() 2182 self.continue_connect_info = None
2183
2184 - def request_gmail_notifications(self):
2185 if not self.connection or self.connected < 2: 2186 return 2187 # It's a gmail account, 2188 # inform the server that we want e-mail notifications 2189 our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name)) 2190 log.debug(('%s is a gmail account. Setting option ' 2191 'to get e-mail notifications on the server.') % (our_jid)) 2192 iq = common.xmpp.Iq(typ = 'set', to = our_jid) 2193 iq.setAttr('id', 'MailNotify') 2194 query = iq.setTag('usersetting') 2195 query.setNamespace(common.xmpp.NS_GTALKSETTING) 2196 query = query.setTag('mailnotifications') 2197 query.setAttr('value', 'true') 2198 self.connection.send(iq) 2199 # Ask how many messages there are now 2200 iq = common.xmpp.Iq(typ = 'get') 2201 iq.setID(self.connection.getAnID()) 2202 query = iq.setTag('query') 2203 query.setNamespace(common.xmpp.NS_GMAILNOTIFY) 2204 self.connection.send(iq)
2205 2206
2207 - def _search_fields_received(self, con, iq_obj):
2208 jid = jid = helpers.get_jid_from_iq(iq_obj) 2209 tag = iq_obj.getTag('query', namespace = common.xmpp.NS_SEARCH) 2210 if not tag: 2211 self.dispatch('SEARCH_FORM', (jid, None, False)) 2212 return 2213 df = tag.getTag('x', namespace = common.xmpp.NS_DATA) 2214 if df: 2215 self.dispatch('SEARCH_FORM', (jid, df, True)) 2216 return 2217 df = {} 2218 for i in iq_obj.getQueryPayload(): 2219 df[i.getName()] = i.getData() 2220 self.dispatch('SEARCH_FORM', (jid, df, False))
2221
2222 - def _StreamCB(self, con, obj):
2223 if obj.getTag('conflict'): 2224 # disconnected because of a resource conflict 2225 self.dispatch('RESOURCE_CONFLICT', ())
2226
2227 - def _register_handlers(self, con, con_type):
2228 # try to find another way to register handlers in each class 2229 # that defines handlers 2230 con.RegisterHandler('message', self._messageCB) 2231 con.RegisterHandler('presence', self._presenceCB) 2232 con.RegisterHandler('presence', self._capsPresenceCB) 2233 # We use makefirst so that this handler is called before _messageCB, and 2234 # can prevent calling it when it's not needed. 2235 # We also don't check for namespace, else it cannot stop _messageCB to be 2236 # called 2237 con.RegisterHandler('message', self._pubsubEventCB, makefirst=True) 2238 con.RegisterHandler('iq', self._vCardCB, 'result', 2239 common.xmpp.NS_VCARD) 2240 con.RegisterHandler('iq', self._rosterSetCB, 'set', 2241 common.xmpp.NS_ROSTER) 2242 con.RegisterHandler('iq', self._siSetCB, 'set', 2243 common.xmpp.NS_SI) 2244 con.RegisterHandler('iq', self._rosterItemExchangeCB, 'set', 2245 common.xmpp.NS_ROSTERX) 2246 con.RegisterHandler('iq', self._siErrorCB, 'error', 2247 common.xmpp.NS_SI) 2248 con.RegisterHandler('iq', self._siResultCB, 'result', 2249 common.xmpp.NS_SI) 2250 con.RegisterHandler('iq', self._discoGetCB, 'get', 2251 common.xmpp.NS_DISCO) 2252 con.RegisterHandler('iq', self._bytestreamSetCB, 'set', 2253 common.xmpp.NS_BYTESTREAM) 2254 con.RegisterHandler('iq', self._bytestreamResultCB, 'result', 2255 common.xmpp.NS_BYTESTREAM) 2256 con.RegisterHandler('iq', self._bytestreamErrorCB, 'error', 2257 common.xmpp.NS_BYTESTREAM) 2258 con.RegisterHandler('iq', self._DiscoverItemsCB, 'result', 2259 common.xmpp.NS_DISCO_ITEMS) 2260 con.RegisterHandler('iq', self._DiscoverItemsErrorCB, 'error', 2261 common.xmpp.NS_DISCO_ITEMS) 2262 con.RegisterHandler('iq', self._DiscoverInfoCB, 'result', 2263 common.xmpp.NS_DISCO_INFO) 2264 con.RegisterHandler('iq', self._DiscoverInfoErrorCB, 'error', 2265 common.xmpp.NS_DISCO_INFO) 2266 con.RegisterHandler('iq', self._VersionCB, 'get', 2267 common.xmpp.NS_VERSION) 2268 con.RegisterHandler('iq', self._TimeCB, 'get', 2269 common.xmpp.NS_TIME) 2270 con.RegisterHandler('iq', self._TimeRevisedCB, 'get', 2271 common.xmpp.NS_TIME_REVISED) 2272 con.RegisterHandler('iq', self._LastCB, 'get', 2273 common.xmpp.NS_LAST) 2274 con.RegisterHandler('iq', self._LastResultCB, 'result', 2275 common.xmpp.NS_LAST) 2276 con.RegisterHandler('iq', self._VersionResultCB, 'result', 2277 common.xmpp.NS_VERSION) 2278 con.RegisterHandler('iq', self._TimeRevisedResultCB, 'result', 2279 common.xmpp.NS_TIME_REVISED) 2280 con.RegisterHandler('iq', self._MucOwnerCB, 'result', 2281 common.xmpp.NS_MUC_OWNER) 2282 con.RegisterHandler('iq', self._MucAdminCB, 'result', 2283 common.xmpp.NS_MUC_ADMIN) 2284 con.RegisterHandler('iq', self._PrivateCB, 'result', 2285 common.xmpp.NS_PRIVATE) 2286 con.RegisterHandler('iq', self._SecLabelCB, 'result', 2287 common.xmpp.NS_SECLABEL_CATALOG) 2288 con.RegisterHandler('iq', self._HttpAuthCB, 'get', 2289 common.xmpp.NS_HTTP_AUTH) 2290 con.RegisterHandler('iq', self._CommandExecuteCB, 'set', 2291 common.xmpp.NS_COMMANDS) 2292 con.RegisterHandler('iq', self._gMailNewMailCB, 'set', 2293 common.xmpp.NS_GMAILNOTIFY) 2294 con.RegisterHandler('iq', self._gMailQueryCB, 'result', 2295 common.xmpp.NS_GMAILNOTIFY) 2296 con.RegisterHandler('iq', self._DiscoverInfoGetCB, 'get', 2297 common.xmpp.NS_DISCO_INFO) 2298 con.RegisterHandler('iq', self._DiscoverItemsGetCB, 'get', 2299 common.xmpp.NS_DISCO_ITEMS) 2300 con.RegisterHandler('iq', self._IqPingCB, 'get', 2301 common.xmpp.NS_PING) 2302 con.RegisterHandler('iq', self._search_fields_received, 'result', 2303 common.xmpp.NS_SEARCH) 2304 con.RegisterHandler('iq', self._PrivacySetCB, 'set', 2305 common.xmpp.NS_PRIVACY) 2306 con.RegisterHandler('iq', self._PubSubCB, 'result') 2307 con.RegisterHandler('iq', self._PubSubErrorCB, 'error') 2308 con.RegisterHandler('iq', self._JingleCB, 'result') 2309 con.RegisterHandler('iq', self._JingleCB, 'error') 2310 con.RegisterHandler('iq', self._JingleCB, 'set', 2311 common.xmpp.NS_JINGLE) 2312 con.RegisterHandler('iq', self._ErrorCB, 'error') 2313 con.RegisterHandler('iq', self._IqCB) 2314 con.RegisterHandler('iq', self._StanzaArrivedCB) 2315 con.RegisterHandler('iq', self._ResultCB, 'result') 2316 con.RegisterHandler('presence', self._StanzaArrivedCB) 2317 con.RegisterHandler('message', self._StanzaArrivedCB) 2318 con.RegisterHandler('unknown', self._StreamCB, 'urn:ietf:params:xml:ns:xmpp-streams', xmlns='http://etherx.jabber.org/streams')
2319
2320 -class HelperEvent:
2321 - def get_jid_resource(self):
2322 if self.id_ in self.conn.groupchat_jids: 2323 self.fjid = self.conn.groupchat_jids[self.id_] 2324 del self.conn.groupchat_jids[self.id_] 2325 else: 2326 self.fjid = helpers.get_full_jid_from_iq(self.iq_obj) 2327 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
2328
2329 - def get_id(self):
2330 self.id_ = self.iq_obj.getID()
2331
2332 -class HttpAuthReceivedEvent(nec.NetworkIncomingEvent):
2333 name = 'http-auth-received' 2334 base_network_events = [] 2335
2336 - def generate(self):
2337 if not self.conn: 2338 self.conn = self.base_event.conn 2339 if not self.iq_obj: 2340 self.iq_obj = self.base_event.xmpp_iq 2341 2342 self.opt = gajim.config.get_per('accounts', self.conn.name, 'http_auth') 2343 self.iq_id = self.iq_obj.getTagAttr('confirm', 'id') 2344 self.method = self.iq_obj.getTagAttr('confirm', 'method') 2345 self.url = self.iq_obj.getTagAttr('confirm', 'url') 2346 # In case it's a message with a body 2347 self.msg = self.iq_obj.getTagData('body') 2348 return True
2349
2350 -class LastResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
2351 name = 'last-result-received' 2352 base_network_events = [] 2353
2354 - def generate(self):
2355 if not self.conn: 2356 self.conn = self.base_event.conn 2357 if not self.iq_obj: 2358 self.iq_obj = self.base_event.xmpp_iq 2359 2360 self.get_id() 2361 self.get_jid_resource() 2362 if self.id_ in self.conn.last_ids: 2363 self.conn.last_ids.remove(self.id_) 2364 2365 self.status = '' 2366 self.seconds = -1 2367 2368 if self.iq_obj.getType() == 'error': 2369 return True 2370 2371 qp = self.iq_obj.getTag('query') 2372 sec = qp.getAttr('seconds') 2373 self.status = qp.getData() 2374 try: 2375 self.seconds = int(sec) 2376 except Exception: 2377 return 2378 2379 return True
2380
2381 -class VersionResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
2382 name = 'version-result-received' 2383 base_network_events = [] 2384
2385 - def generate(self):
2386 if not self.conn: 2387 self.conn = self.base_event.conn 2388 if not self.iq_obj: 2389 self.iq_obj = self.base_event.xmpp_iq 2390 2391 self.get_id() 2392 self.get_jid_resource() 2393 if self.id_ in self.conn.version_ids: 2394 self.conn.version_ids.remove(self.id_) 2395 2396 self.client_info = '' 2397 self.os_info = '' 2398 2399 if self.iq_obj.getType() == 'error': 2400 return True 2401 2402 qp = self.iq_obj.getTag('query') 2403 if qp.getTag('name'): 2404 self.client_info += qp.getTag('name').getData() 2405 if qp.getTag('version'): 2406 self.client_info += ' ' + qp.getTag('version').getData() 2407 if qp.getTag('os'): 2408 self.os_info += qp.getTag('os').getData() 2409 2410 return True
2411
2412 -class TimeResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
2413 name = 'time-result-received' 2414 base_network_events = [] 2415
2416 - def generate(self):
2417 if not self.conn: 2418 self.conn = self.base_event.conn 2419 if not self.iq_obj: 2420 self.iq_obj = self.base_event.xmpp_iq 2421 2422 self.get_id() 2423 self.get_jid_resource() 2424 if self.id_ in self.conn.entity_time_ids: 2425 self.conn.entity_time_ids.remove(self.id_) 2426 2427 self.time_info = '' 2428 2429 if self.iq_obj.getType() == 'error': 2430 return True 2431 2432 qp = self.iq_obj.getTag('time') 2433 if not qp: 2434 # wrong answer 2435 return 2436 tzo = qp.getTag('tzo').getData() 2437 if tzo.lower() == 'z': 2438 tzo = '0:0' 2439 tzoh, tzom = tzo.split(':') 2440 utc_time = qp.getTag('utc').getData() 2441 ZERO = datetime.timedelta(0) 2442 class UTC(datetime.tzinfo): 2443 def utcoffset(self, dt): 2444 return ZERO
2445 def tzname(self, dt): 2446 return "UTC"
2447 def dst(self, dt): 2448 return ZERO 2449 2450 class contact_tz(datetime.tzinfo): 2451 def utcoffset(self, dt): 2452 return datetime.timedelta(hours=int(tzoh), minutes=int(tzom)) 2453 def tzname(self, dt): 2454 return "remote timezone" 2455 def dst(self, dt): 2456 return ZERO 2457 2458 try: 2459 t = datetime.datetime.strptime(utc_time, '%Y-%m-%dT%H:%M:%SZ') 2460 t = t.replace(tzinfo=UTC()) 2461 self.time_info = t.astimezone(contact_tz()).strftime('%c') 2462 except ValueError, e: 2463 log.info('Wrong time format: %s' % str(e)) 2464 return 2465 2466 return True 2467
2468 -class GMailQueryReceivedEvent(nec.NetworkIncomingEvent):
2469 name = 'gmail-notify' 2470 base_network_events = [] 2471
2472 - def generate(self):
2473 if not self.conn: 2474 self.conn = self.base_event.conn 2475 if not self.iq_obj: 2476 self.iq_obj = self.base_event.xmpp_iq 2477 2478 if not self.iq_obj.getTag('mailbox'): 2479 return 2480 mb = self.iq_obj.getTag('mailbox') 2481 if not mb.getAttr('url'): 2482 return 2483 self.conn.gmail_url = mb.getAttr('url') 2484 if mb.getNamespace() != common.xmpp.NS_GMAILNOTIFY: 2485 return 2486 self.newmsgs = mb.getAttr('total-matched') 2487 if not self.newmsgs: 2488 return 2489 if self.newmsgs == '0': 2490 return 2491 # there are new messages 2492 self.gmail_messages_list = [] 2493 if mb.getTag('mail-thread-info'): 2494 gmail_messages = mb.getTags('mail-thread-info') 2495 for gmessage in gmail_messages: 2496 unread_senders = [] 2497 for sender in gmessage.getTag('senders').getTags( 2498 'sender'): 2499 if sender.getAttr('unread') != '1': 2500 continue 2501 if sender.getAttr('name'): 2502 unread_senders.append(sender.getAttr('name') + \ 2503 '< ' + sender.getAttr('address') + '>') 2504 else: 2505 unread_senders.append(sender.getAttr('address')) 2506 2507 if not unread_senders: 2508 continue 2509 gmail_subject = gmessage.getTag('subject').getData() 2510 gmail_snippet = gmessage.getTag('snippet').getData() 2511 tid = int(gmessage.getAttr('tid')) 2512 if not self.conn.gmail_last_tid or \ 2513 tid > self.conn.gmail_last_tid: 2514 self.conn.gmail_last_tid = tid 2515 self.gmail_messages_list.append({ 2516 'From': unread_senders, 2517 'Subject': gmail_subject, 2518 'Snippet': gmail_snippet, 2519 'url': gmessage.getAttr('url'), 2520 'participation': gmessage.getAttr('participation'), 2521 'messages': gmessage.getAttr('messages'), 2522 'date': gmessage.getAttr('date')}) 2523 self.conn.gmail_last_time = int(mb.getAttr('result-time')) 2524 2525 self.jid = gajim.get_jid_from_account(self.name) 2526 log.debug(('You have %s new gmail e-mails on %s.') % (self.newmsgs, 2527 self.jid)) 2528 return True
2529
2530 -class RosterItemExchangeEvent(nec.NetworkIncomingEvent, HelperEvent):
2531 name = 'roster-item-exchange-received' 2532 base_network_events = [] 2533
2534 - def generate(self):
2535 if not self.conn: 2536 self.conn = self.base_event.conn 2537 if not self.iq_obj: 2538 self.iq_obj = self.base_event.xmpp_iq 2539 2540 self.get_id() 2541 self.get_jid_resource() 2542 self.exchange_items_list = {} 2543 items_list = self.iq_obj.getTag('x').getChildren() 2544 if not items_list: 2545 return 2546 self.action = items_list[0].getAttr('action') 2547 if self.action is None: 2548 self.action = 'add' 2549 for item in self.iq_obj.getTag('x', namespace=common.xmpp.NS_ROSTERX).\ 2550 getChildren(): 2551 try: 2552 jid = helpers.parse_jid(item.getAttr('jid')) 2553 except common.helpers.InvalidFormat: 2554 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('jid')) 2555 continue 2556 name = item.getAttr('name') 2557 contact = gajim.contacts.get_contact(self.conn.name, jid) 2558 groups = [] 2559 same_groups = True 2560 for group in item.getTags('group'): 2561 groups.append(group.getData()) 2562 # check that all suggested groups are in the groups we have for this 2563 # contact 2564 if not contact or group not in contact.groups: 2565 same_groups = False 2566 if contact: 2567 # check that all groups we have for this contact are in the 2568 # suggested groups 2569 for group in contact.groups: 2570 if group not in groups: 2571 same_groups = False 2572 if contact.sub in ('both', 'to') and same_groups: 2573 continue 2574 self.exchange_items_list[jid] = [] 2575 self.exchange_items_list[jid].append(name) 2576 self.exchange_items_list[jid].append(groups) 2577 if exchange_items_list: 2578 return True
2579