| Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding:utf-8 -*-
2 ## src/common/connection.py
3 ##
4 ## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
5 ## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
6 ## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
7 ## Stéphan Kochen <stephan AT kochen.nl>
8 ## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
9 ## Travis Shirk <travis AT pobox.com>
10 ## Nikos Kouremenos <kourem AT gmail.com>
11 ## Copyright (C) 2006 Junglecow J <junglecow AT gmail.com>
12 ## Stefan Bethge <stefan AT lanpartei.de>
13 ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
14 ## Copyright (C) 2007 Tomasz Melcer <liori AT exroot.org>
15 ## Julien Pivotto <roidelapluie AT gmail.com>
16 ## Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de>
17 ## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
18 ## Jonathan Schleifer <js-gajim AT webkeks.org>
19 ##
20 ## This file is part of Gajim.
21 ##
22 ## Gajim is free software; you can redistribute it and/or modify
23 ## it under the terms of the GNU General Public License as published
24 ## by the Free Software Foundation; version 3 only.
25 ##
26 ## Gajim is distributed in the hope that it will be useful,
27 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
28 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 ## GNU General Public License for more details.
30 ##
31 ## You should have received a copy of the GNU General Public License
32 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
33 ##
34
35 import os
36 import random
37 import socket
38 import operator
39
40 import time
41 import locale
42 import hmac
43
44 try:
45 randomsource = random.SystemRandom()
46 except Exception:
47 randomsource = random.Random()
48 randomsource.seed()
49
50 import signal
51 if os.name != 'nt':
52 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
53
54 import common.xmpp
55 from common import helpers
56 from common import gajim
57 from common import GnuPG
58 from common import passwords
59 from common import exceptions
60
61 from connection_handlers import *
62
63 from string import Template
64 import logging
65 log = logging.getLogger('gajim.c.connection')
66
67 ssl_error = {
68 2: _("Unable to get issuer certificate"),
69 3: _("Unable to get certificate CRL"),
70 4: _("Unable to decrypt certificate's signature"),
71 5: _("Unable to decrypt CRL's signature"),
72 6: _("Unable to decode issuer public key"),
73 7: _("Certificate signature failure"),
74 8: _("CRL signature failure"),
75 9: _("Certificate is not yet valid"),
76 10: _("Certificate has expired"),
77 11: _("CRL is not yet valid"),
78 12: _("CRL has expired"),
79 13: _("Format error in certificate's notBefore field"),
80 14: _("Format error in certificate's notAfter field"),
81 15: _("Format error in CRL's lastUpdate field"),
82 16: _("Format error in CRL's nextUpdate field"),
83 17: _("Out of memory"),
84 18: _("Self signed certificate"),
85 19: _("Self signed certificate in certificate chain"),
86 20: _("Unable to get local issuer certificate"),
87 21: _("Unable to verify the first certificate"),
88 22: _("Certificate chain too long"),
89 23: _("Certificate revoked"),
90 24: _("Invalid CA certificate"),
91 25: _("Path length constraint exceeded"),
92 26: _("Unsupported certificate purpose"),
93 27: _("Certificate not trusted"),
94 28: _("Certificate rejected"),
95 29: _("Subject issuer mismatch"),
96 30: _("Authority and subject key identifier mismatch"),
97 31: _("Authority and issuer serial number mismatch"),
98 32: _("Key usage does not include certificate signing"),
99 50: _("Application verification failure")
100 }
101
103 """
104 Common connection class, can be derivated for normal connection or zeroconf
105 connection
106 """
107
109 self.name = name
110 # self.connected:
111 # 0=>offline,
112 # 1=>connection in progress,
113 # 2=>online
114 # 3=>free for chat
115 # ...
116 self.connected = 0
117 self.connection = None # xmpppy ClientCommon instance
118 self.on_purpose = False
119 self.is_zeroconf = False
120 self.password = ''
121 self.server_resource = self._compute_resource()
122 self.gpg = None
123 self.USE_GPG = False
124 if gajim.HAVE_GPG:
125 self.USE_GPG = True
126 self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))
127 self.status = ''
128 self.old_show = ''
129 self.priority = gajim.get_priority(name, 'offline')
130 self.time_to_reconnect = None
131 self.bookmarks = []
132
133 self.blocked_list = []
134 self.blocked_contacts = []
135 self.blocked_groups = []
136 self.blocked_all = False
137
138 self.seclabel_supported = False
139 self.seclabel_catalogues = {}
140
141 self.pep_supported = False
142 self.pep = {}
143 # Do we continue connection when we get roster (send presence,get vcard..)
144 self.continue_connect_info = None
145
146 # Remember where we are in the register agent process
147 self.agent_registrations = {}
148 # To know the groupchat jid associated with a sranza ID. Useful to
149 # request vcard or os info... to a real JID but act as if it comes from
150 # the fake jid
151 self.groupchat_jids = {} # {ID : groupchat_jid}
152
153 self.privacy_rules_supported = False
154 self.vcard_supported = False
155 self.private_storage_supported = False
156
157 self.muc_jid = {} # jid of muc server for each transport type
158 self._stun_servers = [] # STUN servers of our jabber server
159
160 self.awaiting_cids = {} # Used for XEP-0231
161
162 self.get_config_values_or_default()
163
165 resource = gajim.config.get_per('accounts', self.name, 'resource')
166 # All valid resource substitution strings should be added to this hash.
167 if resource:
168 resource = Template(resource).safe_substitute({
169 'hostname': socket.gethostname()
170 })
171 return resource
172
174 """
175 Always passes account name as first param
176 """
177 gajim.ged.raise_event(event, self.name, data)
178
184
188
190 """
191 Returns 'ok', 'bad_pass' or 'expired'
192 """
193 if not self.gpg:
194 return False
195 self.gpg.passphrase = password
196 keyID = gajim.config.get_per('accounts', self.name, 'keyid')
197 signed = self.gpg.sign('test', keyID)
198 self.gpg.password = None
199 if signed == 'KEYEXPIRED':
200 return 'expired'
201 elif signed == 'BAD_PASSPHRASE':
202 return 'bad_pass'
203 return 'ok'
204
206 """
207 Returns the signed message if possible or an empty string if gpg is not
208 used or None if waiting for passphrase
209
210 callback is the function to call when user give the passphrase
211 """
212 signed = ''
213 keyID = gajim.config.get_per('accounts', self.name, 'keyid')
214 if keyID and self.USE_GPG:
215 use_gpg_agent = gajim.config.get('use_gpg_agent')
216 if self.gpg.passphrase is None and not use_gpg_agent:
217 # We didn't set a passphrase
218 return None
219 if self.gpg.passphrase is not None or use_gpg_agent:
220 signed = self.gpg.sign(msg, keyID)
221 if signed == 'BAD_PASSPHRASE':
222 self.USE_GPG = False
223 signed = ''
224 self.dispatch('BAD_PASSPHRASE', ())
225 return signed
226
228 """
229 Called when a disconnect request has completed successfully
230 """
231 self.disconnect(on_purpose=True)
232 self.dispatch('STATUS', 'offline')
233
236
238 """
239 This function must be implemented by derivated classes. It has to return
240 the valid jid, or raise a helpers.InvalidFormat exception
241 """
242 raise NotImplementedError
243
244 - def _prepare_message(self, jid, msg, keyID, type_='chat', subject='',
245 chatstate=None, msg_id=None, composing_xep=None, resource=None,
246 user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None,
247 label=None, original_message=None, delayed=None, callback=None):
248 if not self.connection or self.connected < 2:
249 return 1
250 try:
251 jid = self.check_jid(jid)
252 except helpers.InvalidFormat:
253 self.dispatch('ERROR', (_('Invalid Jabber ID'),
254 _('It is not possible to send a message to %s, this JID is not '
255 'valid.') % jid))
256 return
257
258 if msg and not xhtml and gajim.config.get(
259 'rst_formatting_outgoing_messages'):
260 from common.rst_xhtml_generator import create_xhtml
261 xhtml = create_xhtml(msg)
262 if not msg and chatstate is None and form_node is None:
263 return
264 fjid = jid
265 if resource:
266 fjid += '/' + resource
267 msgtxt = msg
268 msgenc = ''
269
270 if session:
271 fjid = session.get_to()
272
273 if keyID and self.USE_GPG:
274 xhtml = None
275 if keyID == 'UNKNOWN':
276 error = _('Neither the remote presence is signed, nor a key was '
277 'assigned.')
278 elif keyID.endswith('MISMATCH'):
279 error = _('The contact\'s key (%s) does not match the key assigned '
280 'in Gajim.' % keyID[:8])
281 else:
282 def encrypt_thread(msg, keyID, always_trust=False):
283 # encrypt message. This function returns (msgenc, error)
284 return self.gpg.encrypt(msg, [keyID], always_trust)
285 def _on_encrypted(output):
286 msgenc, error = output
287 if error == 'NOT_TRUSTED':
288 def _on_always_trust(answer):
289 if answer:
290 gajim.thread_interface(encrypt_thread, [msg, keyID,
291 True], _on_encrypted, [])
292 else:
293 self._message_encrypted_cb(output, type_, msg,
294 msgtxt, original_message, fjid, resource,
295 jid, xhtml, subject, chatstate, msg_id,
296 composing_xep, label, forward_from, delayed,
297 session, form_node, user_nick, keyID,
298 callback)
299 self.dispatch('GPG_ALWAYS_TRUST', _on_always_trust)
300 else:
301 self._message_encrypted_cb(output, type_, msg, msgtxt,
302 original_message, fjid, resource, jid, xhtml,
303 subject, chatstate, msg_id, composing_xep, label,
304 forward_from, delayed, session, form_node,
305 user_nick, keyID, callback)
306 gajim.thread_interface(encrypt_thread, [msg, keyID, False],
307 _on_encrypted, [])
308 return
309
310 self._message_encrypted_cb(('', error), type_, msg, msgtxt,
311 original_message, fjid, resource, jid, xhtml, subject,
312 chatstate, msg_id, composing_xep, label, forward_from, delayed,
313 session, form_node, user_nick, keyID, callback)
314
315 self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
316 resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
317 composing_xep, label, forward_from, delayed, session, form_node,
318 user_nick, callback)
319
320 - def _message_encrypted_cb(self, output, type_, msg, msgtxt,
321 original_message, fjid, resource, jid, xhtml, subject, chatstate, msg_id,
322 composing_xep, label, forward_from, delayed, session, form_node, user_nick,
323 keyID, callback):
324 msgenc, error = output
325
326 if msgenc and not error:
327 msgtxt = '[This message is *encrypted* (See :XEP:`27`]'
328 lang = os.getenv('LANG')
329 if lang is not None and lang != 'en': # we're not english
330 # one in locale and one en
331 msgtxt = _('[This message is *encrypted* (See :XEP:`27`]') + \
332 ' (' + msgtxt + ')'
333 self._on_continue_message(type_, msg, msgtxt, original_message,
334 fjid, resource, jid, xhtml, subject, msgenc, keyID,
335 chatstate, msg_id, composing_xep, label, forward_from, delayed,
336 session, form_node, user_nick, callback)
337 return
338 # Encryption failed, do not send message
339 tim = localtime()
340 self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim, session))
341
342 - def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid,
343 resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
344 composing_xep, label, forward_from, delayed, session, form_node, user_nick,
345 callback):
346 if type_ == 'chat':
347 msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_,
348 xhtml=xhtml)
349 else:
350 if subject:
351 msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ='normal',
352 subject=subject, xhtml=xhtml)
353 else:
354 msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ='normal',
355 xhtml=xhtml)
356
357 if msg_id:
358 msg_iq.setID(msg_id)
359
360 if msgenc:
361 msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
362
363 if form_node:
364 msg_iq.addChild(node=form_node)
365 if label:
366 msg_iq.addChild(node=label)
367
368 # XEP-0172: user_nickname
369 if user_nick:
370 msg_iq.setTag('nick', namespace = common.xmpp.NS_NICK).setData(
371 user_nick)
372
373 # TODO: We might want to write a function so we don't need to
374 # reproduce that ugly if somewhere else.
375 if resource:
376 contact = gajim.contacts.get_contact(self.name, jid, resource)
377 else:
378 contact = gajim.contacts.get_contact_with_highest_priority(self.name,
379 jid)
380
381 # chatstates - if peer supports xep85 or xep22, send chatstates
382 # please note that the only valid tag inside a message containing a <body>
383 # tag is the active event
384 if chatstate is not None and contact:
385 if ((composing_xep == 'XEP-0085' or not composing_xep) \
386 and composing_xep != 'asked_once') or \
387 contact.supports(common.xmpp.NS_CHATSTATES):
388 # XEP-0085
389 msg_iq.setTag(chatstate, namespace=common.xmpp.NS_CHATSTATES)
390 if composing_xep in ('XEP-0022', 'asked_once') or \
391 not composing_xep:
392 # XEP-0022
393 chatstate_node = msg_iq.setTag('x', namespace=common.xmpp.NS_EVENT)
394 if chatstate is 'composing' or msgtxt:
395 chatstate_node.addChild(name='composing')
396
397 if forward_from:
398 addresses = msg_iq.addChild('addresses',
399 namespace=common.xmpp.NS_ADDRESS)
400 addresses.addChild('address', attrs = {'type': 'ofrom',
401 'jid': forward_from})
402
403 # XEP-0203
404 if delayed:
405 our_jid = gajim.get_jid_from_account(self.name) + '/' + \
406 self.server_resource
407 timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(delayed))
408 msg_iq.addChild('delay', namespace=common.xmpp.NS_DELAY2,
409 attrs={'from': our_jid, 'stamp': timestamp})
410
411 # XEP-0184
412 if msgtxt and gajim.config.get_per('accounts', self.name,
413 'request_receipt') and contact and contact.supports(
414 common.xmpp.NS_RECEIPTS):
415 msg_iq.setTag('request', namespace=common.xmpp.NS_RECEIPTS)
416
417 if session:
418 # XEP-0201
419 session.last_send = time.time()
420 msg_iq.setThread(session.thread_id)
421
422 # XEP-0200
423 if session.enable_encryption:
424 msg_iq = session.encrypt_stanza(msg_iq)
425
426 if callback:
427 callback(jid, msg, keyID, forward_from, session, original_message,
428 subject, type_, msg_iq)
429
432 if not forward_from and session and session.is_loggable():
433 ji = gajim.get_jid_without_resource(jid)
434 if gajim.config.should_log(self.name, ji):
435 log_msg = msg
436 if original_message is not None:
437 log_msg = original_message
438 if subject:
439 log_msg = _('Subject: %(subject)s\n%(message)s') % \
440 {'subject': subject, 'message': log_msg}
441 if log_msg:
442 if type_ == 'chat':
443 kind = 'chat_msg_sent'
444 else:
445 kind = 'single_msg_sent'
446 try:
447 gajim.logger.write(kind, jid, log_msg)
448 except exceptions.PysqliteOperationalError, e:
449 self.dispatch('DB_ERROR', (_('Disk Write Error'),
450 str(e)))
451 except exceptions.DatabaseMalformed:
452 pritext = _('Database Error')
453 sectext = _('The database file (%s) cannot be read. Try to '
454 'repair it (see http://trac.gajim.org/wiki/DatabaseBackup)'
455 ' or remove it (all history will be lost).') % \
456 common.logger.LOG_DB_PATH
457 self.dispatch('DB_ERROR', (pritext, sectext))
458
464
470
477
483
489
495
501
505
507 """
508 Update multiple roster items
509 """
510 if self.connection:
511 self.connection.getRoster().setItemMulti(contacts)
512
518
524
526 self.name = new_name
527
529 """
530 groupchat_jid is used when we want to send a request to a real jid and
531 act as if the answer comes from the groupchat_jid
532 """
533 if not gajim.account_is_connected(self.name):
534 return
535 to_whom_jid = jid
536 if resource:
537 to_whom_jid += '/' + resource
538 iq = common.xmpp.Iq(to=to_whom_jid, typ='get', queryNS=\
539 common.xmpp.NS_LAST)
540 id_ = self.connection.getAnID()
541 iq.setID(id_)
542 if groupchat_jid:
543 self.groupchat_jids[id_] = groupchat_jid
544 self.last_ids.append(id_)
545 self.connection.send(iq)
546
552
558
564
570
576
582
584 if self.gpg:
585 use_gpg_agent = gajim.config.get('use_gpg_agent')
586 if use_gpg_agent:
587 self.gpg.passphrase = None
588 else:
589 self.gpg.passphrase = passphrase
590
596
602
606
608 if realm == '':
609 if event == common.xmpp.transports_nb.DATA_RECEIVED:
610 self.dispatch('STANZA_ARRIVED', unicode(data, errors='ignore'))
611 elif event == common.xmpp.transports_nb.DATA_SENT:
612 self.dispatch('STANZA_SENT', unicode(data))
613
615 if not msg:
616 msg = ''
617 sign_msg = False
618 if not auto and not show == 'offline':
619 sign_msg = True
620 if show != 'invisible':
621 # We save it only when privacy list is accepted
622 self.status = msg
623 if show != 'offline' and self.connected < 1:
624 # set old_show to requested 'show' in case we need to
625 # recconect before we auth to server
626 self.old_show = show
627 self.on_purpose = False
628 self.server_resource = self._compute_resource()
629 if gajim.HAVE_GPG:
630 self.USE_GPG = True
631 self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))
632 self.connect_and_init(show, msg, sign_msg)
633 return
634
635 if show == 'offline':
636 self.connected = 0
637 if self.connection:
638 p = common.xmpp.Presence(typ = 'unavailable')
639 p = self.add_sha(p, False)
640 if msg:
641 p.setStatus(msg)
642
643 self.connection.RegisterDisconnectHandler(self._on_disconnected)
644 self.connection.send(p, now=True)
645 self.connection.start_disconnect()
646 else:
647 self._on_disconnected()
648 return
649
650 if show != 'offline' and self.connected > 0:
651 # dont'try to connect, when we are in state 'connecting'
652 if self.connected == 1:
653 return
654 if show == 'invisible':
655 self._change_to_invisible(msg)
656 return
657 if show not in ['offline', 'online', 'chat', 'away', 'xa', 'dnd']:
658 return -1
659 was_invisible = self.connected == gajim.SHOW_LIST.index('invisible')
660 self.connected = gajim.SHOW_LIST.index(show)
661 if was_invisible:
662 self._change_from_invisible()
663 self._update_status(show, msg)
664
667 CommonConnection.__init__(self, name)
668 ConnectionHandlers.__init__(self)
669 # this property is used to prevent double connections
670 self.last_connection = None # last ClientCommon instance
671 # If we succeed to connect, remember it so next time we try (after a
672 # disconnection) we try only this type.
673 self.last_connection_type = None
674 self.lang = None
675 if locale.getdefaultlocale()[0]:
676 self.lang = locale.getdefaultlocale()[0].split('_')[0]
677 # increase/decrease default timeout for server responses
678 self.try_connecting_for_foo_secs = 45
679 # holds the actual hostname to which we are connected
680 self.connected_hostname = None
681 self.last_time_to_reconnect = None
682 self.new_account_info = None
683 self.new_account_form = None
684 self.annotations = {}
685 self.last_io = gajim.idlequeue.current_time()
686 self.last_sent = []
687 self.last_history_time = {}
688 self.password = passwords.get_password(name)
689
690 self.music_track_info = 0
691 self.location_info = {}
692 self.pubsub_supported = False
693 self.pubsub_publish_options_supported = False
694 # Do we auto accept insecure connection
695 self.connection_auto_accepted = False
696 self.pasword_callback = None
697
698 self.on_connect_success = None
699 self.on_connect_failure = None
700 self.retrycount = 0
701 self.jids_for_auto_auth = [] # list of jid to auto-authorize
702 self.available_transports = {} # list of available transports on this
703 # server {'icq': ['icq.server.com', 'icq2.server.com'], }
704 self.private_storage_supported = True
705 self.streamError = ''
706 self.secret_hmac = str(random.random())[2:]
707 # END __init__
708
710 if gajim.config.get_per('accounts', self.name, 'keep_alives_enabled'):
711 self.keepalives = gajim.config.get_per('accounts', self.name,
712 'keep_alive_every_foo_secs')
713 else:
714 self.keepalives = 0
715 if gajim.config.get_per('accounts', self.name, 'ping_alives_enabled'):
716 self.pingalives = gajim.config.get_per('accounts', self.name,
717 'ping_alive_every_foo_secs')
718 else:
719 self.pingalives = 0
720 self.client_cert = gajim.config.get_per('accounts', self.name,
721 'client_cert')
722
725
727 # Do not try to reco while we are already trying
728 self.time_to_reconnect = None
729 if self.connected < 2: # connection failed
730 log.debug('reconnect')
731 self.connected = 1
732 self.dispatch('STATUS', 'connecting')
733 self.retrycount += 1
734 self.on_connect_auth = self._discover_server_at_connection
735 self.connect_and_init(self.old_show, self.status, self.USE_GPG)
736 else:
737 # reconnect succeeded
738 self.time_to_reconnect = None
739 self.retrycount = 0
740
741 # We are doing disconnect at so many places, better use one function in all
743 gajim.interface.music_track_changed(None, None, self.name)
744 self.reset_awaiting_pep()
745 self.on_purpose = on_purpose
746 self.connected = 0
747 self.time_to_reconnect = None
748 self.privacy_rules_supported = False
749 if self.connection:
750 # make sure previous connection is completely closed
751 gajim.proxy65_manager.disconnect(self.connection)
752 self.terminate_sessions()
753 self.remove_all_transfers()
754 self.connection.disconnect()
755 self.last_connection = None
756 self.connection = None
757
759 """
760 Called when we are disconnected
761 """
762 log.info('disconnectedReconnCB called')
763 if gajim.account_is_connected(self.name):
764 # we cannot change our status to offline or connecting
765 # after we auth to server
766 self.old_show = gajim.SHOW_LIST[self.connected]
767 self.connected = 0
768 if not self.on_purpose:
769 self.dispatch('STATUS', 'offline')
770 self.disconnect()
771 if gajim.config.get_per('accounts', self.name, 'autoreconnect'):
772 self.connected = -1
773 self.dispatch('STATUS', 'error')
774 if gajim.status_before_autoaway[self.name]:
775 # We were auto away. So go back online
776 self.status = gajim.status_before_autoaway[self.name]
777 gajim.status_before_autoaway[self.name] = ''
778 self.old_show = 'online'
779 # this check has moved from _reconnect method
780 # do exponential backoff until 15 minutes,
781 # then small linear increase
782 if self.retrycount < 2 or self.last_time_to_reconnect is None:
783 self.last_time_to_reconnect = 5
784 if self.last_time_to_reconnect < 800:
785 self.last_time_to_reconnect *= 1.5
786 self.last_time_to_reconnect += randomsource.randint(0, 5)
787 self.time_to_reconnect = int(self.last_time_to_reconnect)
788 log.info("Reconnect to %s in %ss", self.name, self.time_to_reconnect)
789 gajim.idlequeue.set_alarm(self._reconnect_alarm,
790 self.time_to_reconnect)
791 elif self.on_connect_failure:
792 self.on_connect_failure()
793 self.on_connect_failure = None
794 else:
795 # show error dialog
796 self._connection_lost()
797 else:
798 self.disconnect()
799 self.on_purpose = False
800 # END disconnectedReconnCB
801
803 log.debug('_connection_lost')
804 self.disconnect(on_purpose = False)
805 self.dispatch('STATUS', 'offline')
806 self.dispatch('CONNECTION_LOST',
807 (_('Connection with account "%s" has been lost') % self.name,
808 _('Reconnect manually.')))
809
811 CommonConnection._event_dispatcher(self, realm, event, data)
812 if realm == common.xmpp.NS_REGISTER:
813 if event == common.xmpp.features_nb.REGISTER_DATA_RECEIVED:
814 # data is (agent, DataFrom, is_form, error_msg)
815 if self.new_account_info and \
816 self.new_account_info['hostname'] == data[0]:
817 # it's a new account
818 if not data[1]: # wrong answer
819 self.dispatch('ACC_NOT_OK', (
820 _('Server %(name)s answered wrongly to register request: '
821 '%(error)s') % {'name': data[0], 'error': data[3]}))
822 return
823 is_form = data[2]
824 conf = data[1]
825 if self.new_account_form:
826 def _on_register_result(result):
827 if not common.xmpp.isResultNode(result):
828 self.dispatch('ACC_NOT_OK', (result.getError()))
829 return
830 if gajim.HAVE_GPG:
831 self.USE_GPG = True
832 self.gpg = GnuPG.GnuPG(gajim.config.get(
833 'use_gpg_agent'))
834 self.dispatch('ACC_OK', (self.new_account_info))
835 self.new_account_info = None
836 self.new_account_form = None
837 if self.connection:
838 self.connection.UnregisterDisconnectHandler(
839 self._on_new_account)
840 self.disconnect(on_purpose=True)
841 # it's the second time we get the form, we have info user
842 # typed, so send them
843 if is_form:
844 #TODO: Check if form has changed
845 iq = common.xmpp.Iq('set', common.xmpp.NS_REGISTER, to=self._hostname)
846 iq.setTag('query').addChild(node=self.new_account_form)
847 self.connection.SendAndCallForResponse(iq,
848 _on_register_result)
849 else:
850 if self.new_account_form.keys().sort() != \
851 conf.keys().sort():
852 # requested config has changed since first connection
853 self.dispatch('ACC_NOT_OK', (_(
854 'Server %s provided a different registration form')\
855 % data[0]))
856 return
857 common.xmpp.features_nb.register(self.connection,
858 self._hostname, self.new_account_form,
859 _on_register_result)
860 return
861 try:
862 errnum = self.connection.Connection.ssl_errnum
863 except AttributeError:
864 errnum = -1 # we don't have an errnum
865 ssl_msg = ''
866 if errnum > 0:
867 ssl_msg = ssl_error.get(errnum, _('Unknown SSL error: %d') % errnum)
868 ssl_cert = ''
869 if hasattr(self.connection.Connection, 'ssl_cert_pem'):
870 ssl_cert = self.connection.Connection.ssl_cert_pem
871 ssl_fingerprint = ''
872 if hasattr(self.connection.Connection, 'ssl_fingerprint_sha1'):
873 ssl_fingerprint = \
874 self.connection.Connection.ssl_fingerprint_sha1
875 self.dispatch('NEW_ACC_CONNECTED', (conf, is_form, ssl_msg,
876 errnum, ssl_cert, ssl_fingerprint))
877 self.connection.UnregisterDisconnectHandler(
878 self._on_new_account)
879 self.disconnect(on_purpose=True)
880 return
881 if not data[1]: # wrong answer
882 self.dispatch('ERROR', (_('Invalid answer'),
883 _('Transport %(name)s answered wrongly to register request: '
884 '%(error)s') % {'name': data[0], 'error': data[3]}))
885 return
886 is_form = data[2]
887 conf = data[1]
888 self.dispatch('REGISTER_AGENT_INFO', (data[0], conf, is_form))
889 elif realm == common.xmpp.NS_PRIVACY:
890 if event == common.xmpp.features_nb.PRIVACY_LISTS_RECEIVED:
891 # data is (list)
892 self.dispatch('PRIVACY_LISTS_RECEIVED', (data))
893 elif event == common.xmpp.features_nb.PRIVACY_LIST_RECEIVED:
894 # data is (resp)
895 if not data:
896 return
897 rules = []
898 name = data.getTag('query').getTag('list').getAttr('name')
899 for child in data.getTag('query').getTag('list').getChildren():
900 dict_item = child.getAttrs()
901 childs = []
902 if 'type' in dict_item:
903 for scnd_child in child.getChildren():
904 childs += [scnd_child.getName()]
905 rules.append({'action':dict_item['action'],
906 'type':dict_item['type'], 'order':dict_item['order'],
907 'value':dict_item['value'], 'child':childs})
908 else:
909 for scnd_child in child.getChildren():
910 childs.append(scnd_child.getName())
911 rules.append({'action':dict_item['action'],
912 'order':dict_item['order'], 'child':childs})
913 self.dispatch('PRIVACY_LIST_RECEIVED', (name, rules))
914 elif event == common.xmpp.features_nb.PRIVACY_LISTS_ACTIVE_DEFAULT:
915 # data is (dict)
916 self.dispatch('PRIVACY_LISTS_ACTIVE_DEFAULT', (data))
917
919 """
920 Selects the next host according to RFC2782 p.3 based on it's priority.
921 Chooses between hosts with the same priority randomly, where the
922 probability of being selected is proportional to the weight of the host
923 """
924 hosts_by_prio = sorted(hosts, key=operator.itemgetter('prio'))
925
926 try:
927 lowest_prio = hosts_by_prio[0]['prio']
928 except IndexError:
929 raise ValueError("No hosts to choose from!")
930
931 hosts_lowest_prio = [h for h in hosts_by_prio if h['prio'] == lowest_prio]
932
933 if len(hosts_lowest_prio) == 1:
934 return hosts_lowest_prio[0]
935 else:
936 rndint = random.randint(0, sum(h['weight'] for h in hosts_lowest_prio))
937 weightsum = 0
938 for host in sorted(hosts_lowest_prio, key=operator.itemgetter(
939 'weight')):
940 weightsum += host['weight']
941 if weightsum >= rndint:
942 return host
943
945 """
946 Start a connection to the Jabber server
947
948 Returns connection, and connection type ('tls', 'ssl', 'plain', '') data
949 MUST contain hostname, usessl, proxy, use_custom_host, custom_host (if
950 use_custom_host), custom_port (if use_custom_host)
951 """
952 if self.connection:
953 return self.connection, ''
954
955 if data:
956 hostname = data['hostname']
957 self.try_connecting_for_foo_secs = 45
958 p = data['proxy']
959 use_srv = True
960 use_custom = data['use_custom_host']
961 if use_custom:
962 custom_h = data['custom_host']
963 custom_p = data['custom_port']
964 else:
965 hostname = gajim.config.get_per('accounts', self.name, 'hostname')
966 usessl = gajim.config.get_per('accounts', self.name, 'usessl')
967 self.try_connecting_for_foo_secs = gajim.config.get_per('accounts',
968 self.name, 'try_connecting_for_foo_secs')
969 p = gajim.config.get_per('accounts', self.name, 'proxy')
970 use_srv = gajim.config.get_per('accounts', self.name, 'use_srv')
971 use_custom = gajim.config.get_per('accounts', self.name,
972 'use_custom_host')
973 custom_h = gajim.config.get_per('accounts', self.name, 'custom_host')
974 custom_p = gajim.config.get_per('accounts', self.name, 'custom_port')
975
976 # create connection if it doesn't already exist
977 self.connected = 1
978 if p and p in gajim.config.get_per('proxies'):
979 proxy = {}
980 proxyptr = gajim.config.get_per('proxies', p)
981 for key in proxyptr.keys():
982 proxy[key] = proxyptr[key][1]
983
984 elif gajim.config.get_per('accounts', self.name, 'use_env_http_proxy'):
985 try:
986 try:
987 env_http_proxy = os.environ['HTTP_PROXY']
988 except Exception:
989 env_http_proxy = os.environ['http_proxy']
990 env_http_proxy = env_http_proxy.strip('"')
991 # Dispose of the http:// prefix
992 env_http_proxy = env_http_proxy.split('://')
993 env_http_proxy = env_http_proxy[len(env_http_proxy)-1]
994 env_http_proxy = env_http_proxy.split('@')
995
996 if len(env_http_proxy) == 2:
997 login = env_http_proxy[0].split(':')
998 addr = env_http_proxy[1].split(':')
999 else:
1000 login = ['', '']
1001 addr = env_http_proxy[0].split(':')
1002
1003 proxy = {'host': addr[0], 'type' : u'http', 'user':login[0]}
1004
1005 if len(addr) == 2:
1006 proxy['port'] = addr[1]
1007 else:
1008 proxy['port'] = 3128
1009
1010 if len(login) == 2:
1011 proxy['pass'] = login[1]
1012 proxy['useauth'] = True
1013 else:
1014 proxy['pass'] = u''
1015
1016 except Exception:
1017 proxy = None
1018 else:
1019 proxy = None
1020 h = hostname
1021 p = 5222
1022 ssl_p = 5223
1023 # use_srv = False # wants ssl? disable srv lookup
1024 if use_custom:
1025 h = custom_h
1026 p = custom_p
1027 ssl_p = custom_p
1028 use_srv = False
1029
1030 # SRV resolver
1031 self._proxy = proxy
1032 self._hosts = [ {'host': h, 'port': p, 'ssl_port': ssl_p, 'prio': 10,
1033 'weight': 10} ]
1034 self._hostname = hostname
1035 if use_srv:
1036 # add request for srv query to the resolve, on result '_on_resolve'
1037 # will be called
1038 gajim.resolver.resolve('_xmpp-client._tcp.' + helpers.idn_to_ascii(h),
1039 self._on_resolve)
1040 else:
1041 self._on_resolve('', [])
1042
1044 # SRV query returned at least one valid result, we put it in hosts dict
1045 if len(result_array) != 0:
1046 self._hosts = [i for i in result_array]
1047 # Add ssl port
1048 ssl_p = 5223
1049 if gajim.config.get_per('accounts', self.name, 'use_custom_host'):
1050 ssl_p = gajim.config.get_per('accounts', self.name, 'custom_port')
1051 for i in self._hosts:
1052 i['ssl_port'] = ssl_p
1053 self._connect_to_next_host()
1054
1055
1057 log.debug('Connection to next host')
1058 if len(self._hosts):
1059 # No config option exist when creating a new account
1060 if self.last_connection_type:
1061 self._connection_types = [self.last_connection_type]
1062 elif self.name in gajim.config.get_per('accounts'):
1063 self._connection_types = gajim.config.get_per('accounts', self.name,
1064 'connection_types').split()
1065 else:
1066 self._connection_types = ['tls', 'ssl', 'plain']
1067
1068 if self._proxy and self._proxy['type']=='bosh':
1069 # with BOSH, we can't do TLS negotiation with <starttls>, we do only "plain"
1070 # connection and TLS with handshake right after TCP connecting ("ssl")
1071 scheme = common.xmpp.transports_nb.urisplit(self._proxy['bosh_uri'])[0]
1072 if scheme=='https':
1073 self._connection_types = ['ssl']
1074 else:
1075 self._connection_types = ['plain']
1076
1077 host = self._select_next_host(self._hosts)
1078 self._current_host = host
1079 self._hosts.remove(host)
1080 self.connect_to_next_type()
1081
1082 else:
1083 if not retry and self.retrycount == 0:
1084 log.debug("Out of hosts, giving up connecting to %s", self.name)
1085 self.time_to_reconnect = None
1086 if self.on_connect_failure:
1087 self.on_connect_failure()
1088 self.on_connect_failure = None
1089 else:
1090 # shown error dialog
1091 self._connection_lost()
1092 else:
1093 # try reconnect if connection has failed before auth to server
1094 self._disconnectedReconnCB()
1095
1097 if len(self._connection_types):
1098 self._current_type = self._connection_types.pop(0)
1099 if self.last_connection:
1100 self.last_connection.socket.disconnect()
1101 self.last_connection = None
1102 self.connection = None
1103
1104 if self._current_type == 'ssl':
1105 # SSL (force TLS on different port than plain)
1106 # If we do TLS over BOSH, port of XMPP server should be the standard one
1107 # and TLS should be negotiated because TLS on 5223 is deprecated
1108 if self._proxy and self._proxy['type']=='bosh':
1109 port = self._current_host['port']
1110 else:
1111 port = self._current_host['ssl_port']
1112 elif self._current_type == 'tls':
1113 # TLS - negotiate tls after XMPP stream is estabilished
1114 port = self._current_host['port']
1115 elif self._current_type == 'plain':
1116 # plain connection on defined port
1117 port = self._current_host['port']
1118
1119 cacerts = os.path.join(common.gajim.DATA_DIR, 'other', 'cacerts.pem')
1120 mycerts = common.gajim.MY_CACERTS
1121 secure_tuple = (self._current_type, cacerts, mycerts)
1122
1123 con = common.xmpp.NonBlockingClient(
1124 domain=self._hostname,
1125 caller=self,
1126 idlequeue=gajim.idlequeue)
1127
1128 self.last_connection = con
1129 # increase default timeout for server responses
1130 common.xmpp.dispatcher_nb.DEFAULT_TIMEOUT_SECONDS = self.try_connecting_for_foo_secs
1131 # FIXME: this is a hack; need a better way
1132 if self.on_connect_success == self._on_new_account:
1133 con.RegisterDisconnectHandler(self._on_new_account)
1134
1135 self.log_hosttype_info(port)
1136 con.connect(
1137 hostname=self._current_host['host'],
1138 port=port,
1139 on_connect=self.on_connect_success,
1140 on_proxy_failure=self.on_proxy_failure,
1141 on_connect_failure=self.connect_to_next_type,
1142 proxy=self._proxy,
1143 secure_tuple = secure_tuple)
1144 else:
1145 self._connect_to_next_host(retry)
1146
1148 msg = '>>>>>> Connecting to %s [%s:%d], type = %s' % (self.name,
1149 self._current_host['host'], port, self._current_type)
1150 log.info(msg)
1151 if self._proxy:
1152 msg = '>>>>>> '
1153 if self._proxy['type']=='bosh':
1154 msg = '%s over BOSH %s' % (msg, self._proxy['bosh_uri'])
1155 if self._proxy['type'] in ['http', 'socks5'] or self._proxy['bosh_useproxy']:
1156 msg = '%s over proxy %s:%s' % (msg, self._proxy['host'], self._proxy['port'])
1157 log.info(msg)
1158
1160 if not con_type:
1161 # we are not retrying, and not conecting
1162 if not self.retrycount and self.connected != 0:
1163 self.disconnect(on_purpose = True)
1164 self.dispatch('STATUS', 'offline')
1165 pritxt = _('Could not connect to "%s"') % self._hostname
1166 sectxt = _('Check your connection or try again later.')
1167 if self.streamError:
1168 # show error dialog
1169 key = common.xmpp.NS_XMPP_STREAMS + ' ' + self.streamError
1170 if key in common.xmpp.ERRORS:
1171 sectxt2 = _('Server replied: %s') % common.xmpp.ERRORS[key][2]
1172 self.dispatch('ERROR', (pritxt, '%s\n%s' % (sectxt2, sectxt)))
1173 return
1174 # show popup
1175 self.dispatch('CONNECTION_LOST', (pritxt, sectxt))
1176
1178 log.error('Connection to proxy failed: %s' % reason)
1179 self.time_to_reconnect = None
1180 self.on_connect_failure = None
1181 self.disconnect(on_purpose = True)
1182 self.dispatch('STATUS', 'offline')
1183 self.dispatch('CONNECTION_LOST',
1184 (_('Connection to proxy failed'), reason))
1185
1187 if not self.connected: # We went offline during connecting process
1188 # FIXME - not possible, maybe it was when we used threads
1189 return
1190 _con_type = con_type
1191 if _con_type != self._current_type:
1192 log.info('Connecting to next type beacuse desired is %s and returned is %s'
1193 % (self._current_type, _con_type))
1194 self.connect_to_next_type()
1195 return
1196 con.RegisterDisconnectHandler(self._on_disconnected)
1197 if _con_type == 'plain' and gajim.config.get_per('accounts', self.name,
1198 'warn_when_plaintext_connection'):
1199 self.dispatch('PLAIN_CONNECTION', (con,))
1200 return True
1201 if _con_type in ('tls', 'ssl') and con.Connection.ssl_lib != 'PYOPENSSL' \
1202 and gajim.config.get_per('accounts', self.name,
1203 'warn_when_insecure_ssl_connection') and \
1204 not self.connection_auto_accepted:
1205 # Pyopenssl is not used
1206 self.dispatch('INSECURE_SSL_CONNECTION', (con, _con_type))
1207 return True
1208 return self.connection_accepted(con, con_type)
1209
1211 if not con or not con.Connection:
1212 self.disconnect(on_purpose=True)
1213 self.dispatch('STATUS', 'offline')
1214 self.dispatch('CONNECTION_LOST',
1215 (_('Could not connect to account %s') % self.name,
1216 _('Connection with account %s has been lost. Retry connecting.') % \
1217 self.name))
1218 return
1219 self.hosts = []
1220 self.connection_auto_accepted = False
1221 self.connected_hostname = self._current_host['host']
1222 self.on_connect_failure = None
1223 con.UnregisterDisconnectHandler(self._on_disconnected)
1224 con.RegisterDisconnectHandler(self._disconnectedReconnCB)
1225 log.debug('Connected to server %s:%s with %s' % (
1226 self._current_host['host'], self._current_host['port'], con_type))
1227
1228 self.last_connection_type = con_type
1229 if gajim.config.get_per('accounts', self.name, 'anonymous_auth'):
1230 name = None
1231 else:
1232 name = gajim.config.get_per('accounts', self.name, 'name')
1233 hostname = gajim.config.get_per('accounts', self.name, 'hostname')
1234 self.connection = con
1235 try:
1236 errnum = con.Connection.ssl_errnum
1237 except AttributeError:
1238 errnum = -1 # we don't have an errnum
1239 if errnum > 0 and str(errnum) not in gajim.config.get_per('accounts',
1240 self.name, 'ignore_ssl_errors'):
1241 text = _('The authenticity of the %s certificate could be invalid.') %\
1242 hostname
1243 if errnum in ssl_error:
1244 text += _('\nSSL Error: <b>%s</b>') % ssl_error[errnum]
1245 else:
1246 text += _('\nUnknown SSL error: %d') % errnum
1247 self.dispatch('SSL_ERROR', (text, errnum, con.Connection.ssl_cert_pem,
1248 con.Connection.ssl_fingerprint_sha1))
1249 return True
1250 if hasattr(con.Connection, 'ssl_fingerprint_sha1'):
1251 saved_fingerprint = gajim.config.get_per('accounts', self.name, 'ssl_fingerprint_sha1')
1252 if saved_fingerprint:
1253 # Check sha1 fingerprint
1254 if con.Connection.ssl_fingerprint_sha1 != saved_fingerprint:
1255 self.dispatch('FINGERPRINT_ERROR',
1256 (con.Connection.ssl_fingerprint_sha1,))
1257 return True
1258 else:
1259 gajim.config.set_per('accounts', self.name, 'ssl_fingerprint_sha1',
1260 con.Connection.ssl_fingerprint_sha1)
1261 self._register_handlers(con, con_type)
1262 con.auth(
1263 user=name,
1264 password=self.password,
1265 resource=self.server_resource,
1266 sasl=1,
1267 on_auth=self.__on_auth)
1268
1270 if not self.connection:
1271 self.disconnect(on_purpose=True)
1272 self.dispatch('STATUS', 'offline')
1273 self.dispatch('CONNECTION_LOST',
1274 (_('Could not connect to account %s') % self.name,
1275 _('Connection with account %s has been lost. Retry connecting.') % \
1276 self.name))
1277 return
1278 name = gajim.config.get_per('accounts', self.name, 'name')
1279 self._register_handlers(self.connection, 'ssl')
1280 self.connection.auth(name, self.password, self.server_resource, 1,
1281 self.__on_auth)
1282
1284 self.peerhost = con.get_peerhost()
1285 # notify the gui about con_type
1286 self.dispatch('CON_TYPE', con_type)
1287 ConnectionHandlers._register_handlers(self, con, con_type)
1288
1290 if not con:
1291 self.disconnect(on_purpose=True)
1292 self.dispatch('STATUS', 'offline')
1293 self.dispatch('CONNECTION_LOST',
1294 (_('Could not connect to "%s"') % self._hostname,
1295 _('Check your connection or try again later')))
1296 if self.on_connect_auth:
1297 self.on_connect_auth(None)
1298 self.on_connect_auth = None
1299 return
1300 if not self.connected: # We went offline during connecting process
1301 if self.on_connect_auth:
1302 self.on_connect_auth(None)
1303 self.on_connect_auth = None
1304 return
1305 if hasattr(con, 'Resource'):
1306 self.server_resource = con.Resource
1307 if gajim.config.get_per('accounts', self.name, 'anonymous_auth'):
1308 # Get jid given by server
1309 old_jid = gajim.get_jid_from_account(self.name)
1310 gajim.config.set_per('accounts', self.name, 'name', con.User)
1311 new_jid = gajim.get_jid_from_account(self.name)
1312 self.dispatch('NEW_JID', (old_jid, new_jid))
1313 if auth:
1314 self.last_io = gajim.idlequeue.current_time()
1315 self.connected = 2
1316 self.retrycount = 0
1317 if self.on_connect_auth:
1318 self.on_connect_auth(con)
1319 self.on_connect_auth = None
1320 else:
1321 gajim.log.debug("Couldn't authenticate to %s" % self._hostname)
1322 self.disconnect(on_purpose = True)
1323 self.dispatch('STATUS', 'offline')
1324 self.dispatch('ERROR', (_('Authentication failed with "%s"') % \
1325 self._hostname,
1326 _('Please check your login and password for correctness.')))
1327 if self.on_connect_auth:
1328 self.on_connect_auth(None)
1329 self.on_connect_auth = None
1330 # END connect
1331
1335
1337 if not gajim.account_is_connected(self.name):
1338 return
1339 common.xmpp.features_nb.getPrivacyLists(self.connection)
1340
1342 # nothing received for the last foo seconds
1343 if self.connection:
1344 self.connection.send(' ')
1345
1347 id_ = unicode(iq_obj.getAttr('id'))
1348 assert id_ == self.awaiting_xmpp_ping_id
1349 self.awaiting_xmpp_ping_id = None
1350
1352 """
1353 Send XMPP Ping (XEP-0199) request. If pingTo is not set, ping is sent to
1354 server to detect connection failure at application level
1355 """
1356 if not gajim.account_is_connected(self.name):
1357 return
1358 id_ = self.connection.getAnID()
1359 if pingTo:
1360 to = pingTo.get_full_jid()
1361 self.dispatch('PING_SENT', (pingTo))
1362 else:
1363 to = gajim.config.get_per('accounts', self.name, 'hostname')
1364 self.awaiting_xmpp_ping_id = id_
1365 iq = common.xmpp.Iq('get', to=to)
1366 iq.addChild(name = 'ping', namespace = common.xmpp.NS_PING)
1367 iq.setID(id_)
1368 def _on_response(resp):
1369 timePong = time_time()
1370 if not common.xmpp.isResultNode(resp):
1371 self.dispatch('PING_ERROR', (pingTo))
1372 return
1373 timeDiff = round(timePong - timePing, 2)
1374 self.dispatch('PING_REPLY', (pingTo, timeDiff))
1375 if pingTo:
1376 timePing = time_time()
1377 self.connection.SendAndCallForResponse(iq, _on_response)
1378 else:
1379 self.connection.SendAndCallForResponse(iq, self._on_xmpp_ping_answer)
1380 gajim.idlequeue.set_alarm(self.check_pingalive, gajim.config.get_per(
1381 'accounts', self.name, 'time_for_ping_alive_answer'))
1382
1384 if not gajim.account_is_connected(self.name):
1385 return
1386 common.xmpp.features_nb.getActiveAndDefaultPrivacyLists(self.connection)
1387
1389 if not gajim.account_is_connected(self.name):
1390 return
1391 def _on_del_privacy_list_result(result):
1392 if result:
1393 self.dispatch('PRIVACY_LIST_REMOVED', privacy_list)
1394 else:
1395 self.dispatch('ERROR', (_('Error while removing privacy list'),
1396 _('Privacy list %s has not been removed. It is maybe active in '
1397 'one of your connected resources. Deactivate it and try '
1398 'again.') % privacy_list))
1399 common.xmpp.features_nb.delPrivacyList(self.connection, privacy_list,
1400 _on_del_privacy_list_result)
1401
1403 if not gajim.account_is_connected(self.name):
1404 return
1405 common.xmpp.features_nb.getPrivacyList(self.connection, title)
1406
1408 if not gajim.account_is_connected(self.name):
1409 return
1410 common.xmpp.features_nb.setPrivacyList(self.connection, listname, tags)
1411
1413 if not gajim.account_is_connected(self.name):
1414 return
1415 common.xmpp.features_nb.setActivePrivacyList(self.connection, listname, 'active')
1416
1418 if not gajim.account_is_connected(self.name):
1419 return
1420 common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname)
1421
1423 """
1424 Build a Privacy rule stanza for invisibility
1425 """
1426 iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
1427 l = iq.getTag('query').setTag('list', {'name': name})
1428 i = l.setTag('item', {'action': action, 'order': str(order)})
1429 i.setTag('presence-out')
1430 return iq
1431
1433 iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
1434 l = iq.getTag('query').setTag('list', {'name': 'invisible'})
1435 if self.name in gajim.interface.status_sent_to_groups and \
1436 len(gajim.interface.status_sent_to_groups[self.name]) > 0:
1437 for group in gajim.interface.status_sent_to_groups[self.name]:
1438 i = l.setTag('item', {'type': 'group', 'value': group,
1439 'action': 'allow', 'order': '1'})
1440 i.setTag('presence-out')
1441 if self.name in gajim.interface.status_sent_to_users and \
1442 len(gajim.interface.status_sent_to_users[self.name]) > 0:
1443 for jid in gajim.interface.status_sent_to_users[self.name]:
1444 i = l.setTag('item', {'type': 'jid', 'value': jid,
1445 'action': 'allow', 'order': '2'})
1446 i.setTag('presence-out')
1447 i = l.setTag('item', {'action': 'deny', 'order': '3'})
1448 i.setTag('presence-out')
1449 return iq
1450
1452 if not gajim.account_is_connected(self.name):
1453 return
1454 iq = self.build_invisible_rule()
1455 self.connection.send(iq)
1456
1458 """
1459 Activate a privacy rule
1460 """
1461 if not gajim.account_is_connected(self.name):
1462 return
1463 iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
1464 iq.getTag('query').setTag('active', {'name': name})
1465 self.connection.send(iq)
1466
1468 if not gajim.account_is_connected(self.name):
1469 return
1470 if not self.privacy_rules_supported:
1471 self.dispatch('STATUS', gajim.SHOW_LIST[self.connected])
1472 self.dispatch('ERROR', (_('Invisibility not supported'),
1473 _('Account %s doesn\'t support invisibility.') % self.name))
1474 return
1475 # If we are already connected, and privacy rules are supported, send
1476 # offline presence first as it's required by XEP-0126
1477 if self.connected > 1 and self.privacy_rules_supported:
1478 self.on_purpose = True
1479 p = common.xmpp.Presence(typ = 'unavailable')
1480 p = self.add_sha(p, False)
1481 if msg:
1482 p.setStatus(msg)
1483 self.remove_all_transfers()
1484 self.connection.send(p)
1485
1486 # try to set the privacy rule
1487 iq = self.build_invisible_rule()
1488 self.connection.SendAndCallForResponse(iq, self._continue_invisible,
1489 {'msg': msg, 'signed': signed, 'initial': initial})
1490
1492 if iq_obj.getType() == 'error': # server doesn't support privacy lists
1493 return
1494 # active the privacy rule
1495 self.privacy_rules_supported = True
1496 self.activate_privacy_rule('invisible')
1497 self.connected = gajim.SHOW_LIST.index('invisible')
1498 self.status = msg
1499 priority = unicode(gajim.get_priority(self.name, 'invisible'))
1500 p = common.xmpp.Presence(priority = priority)
1501 p = self.add_sha(p, True)
1502 if msg:
1503 p.setStatus(msg)
1504 if signed:
1505 p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
1506 self.connection.send(p)
1507 self.priority = priority
1508 self.dispatch('STATUS', 'invisible')
1509 if initial:
1510 # ask our VCard
1511 self.request_vcard(None)
1512
1513 # Get bookmarks from private namespace
1514 self.get_bookmarks()
1515
1516 # Get annotations
1517 self.get_annotations()
1518
1519 # Inform GUI we just signed in
1520 self.dispatch('SIGNED_IN', ())
1521
1523 if gajim.config.get_per('accounts', self.name, 'gpg_sign_presence'):
1524 return self.get_signed_msg(msg, callback)
1525 return ''
1526
1528 self.on_connect_success = self._connect_success
1529 self.on_connect_failure = self._connect_failure
1530 self.connect()
1531
1533 self.continue_connect_info = [show, msg, sign_msg]
1534 self.on_connect_auth = self._discover_server_at_connection
1535 self.connect_and_auth()
1536
1538 self.connection = con
1539 if not gajim.account_is_connected(self.name):
1540 return
1541 self.connection.set_send_timeout(self.keepalives, self.send_keepalive)
1542 self.connection.set_send_timeout2(self.pingalives, self.sendPing)
1543 self.connection.onreceive(None)
1544 self.discoverInfo(gajim.config.get_per('accounts', self.name, 'hostname'),
1545 id_prefix='Gajim_')
1546 self.privacy_rules_requested = False
1547 # Discover Stun server(s)
1548 gajim.resolver.resolve('_stun._udp.' + helpers.idn_to_ascii(
1549 self.connected_hostname), self._on_stun_resolved)
1550
1554
1556 iq = common.xmpp.Iq('get', common.xmpp.NS_PRIVACY, xmlns = '')
1557 id_ = self.connection.getAnID()
1558 iq.setID(id_)
1559 self.awaiting_answers[id_] = (PRIVACY_ARRIVED, )
1560 self.connection.send(iq)
1561
1563 if not show in gajim.SHOW_LIST:
1564 return -1
1565 if not gajim.account_is_connected(self.name):
1566 return
1567 sshow = helpers.get_xmpp_show(show)
1568 if not msg:
1569 msg = ''
1570 if show == 'offline':
1571 p = common.xmpp.Presence(typ = 'unavailable', to = jid)
1572 p = self.add_sha(p, False)
1573 if msg:
1574 p.setStatus(msg)
1575 else:
1576 signed = self.get_signed_presence(msg)
1577 priority = unicode(gajim.get_priority(self.name, sshow))
1578 p = common.xmpp.Presence(typ = None, priority = priority, show = sshow,
1579 to = jid)
1580 p = self.add_sha(p)
1581 if msg:
1582 p.setStatus(msg)
1583 if signed:
1584 p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
1585 self.connection.send(p)
1586
1590
1592 if self.privacy_rules_supported:
1593 iq = self.build_privacy_rule('visible', 'allow')
1594 self.connection.send(iq)
1595 self.activate_privacy_rule('visible')
1596
1598 xmpp_show = helpers.get_xmpp_show(show)
1599 priority = unicode(gajim.get_priority(self.name, xmpp_show))
1600 p = common.xmpp.Presence(typ=None, priority=priority, show=xmpp_show)
1601 p = self.add_sha(p)
1602 if msg:
1603 p.setStatus(msg)
1604 signed = self.get_signed_presence(msg)
1605 if signed:
1606 p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
1607 if self.connection:
1608 self.connection.send(p)
1609 self.priority = priority
1610 self.dispatch('STATUS', show)
1611
1613 if not gajim.account_is_connected(self.name):
1614 return
1615 msg_iq = common.xmpp.Message(to = jid, body = msg, subject = subject,
1616 xhtml = xhtml)
1617
1618 self.connection.send(msg_iq)
1619
1620 - def send_message(self, jid, msg, keyID=None, type_='chat', subject='',
1621 chatstate=None, msg_id=None, composing_xep=None, resource=None,
1622 user_nick=None, xhtml=None, label=None, session=None, forward_from=None,
1623 form_node=None, original_message=None, delayed=None, callback=None,
1624 callback_args=[], now=False):
1625
1626 def cb(jid, msg, keyID, forward_from, session, original_message,
1627 subject, type_, msg_iq):
1628 msg_id = self.connection.send(msg_iq, now=now)
1629 jid = helpers.parse_jid(jid)
1630 self.dispatch('MSGSENT', (jid, msg, keyID))
1631 if callback:
1632 callback(msg_id, *callback_args)
1633
1634 self.log_message(jid, msg, forward_from, session, original_message,
1635 subject, type_)
1636
1637 self._prepare_message(jid, msg, keyID, type_=type_, subject=subject,
1638 chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
1639 resource=resource, user_nick=user_nick, xhtml=xhtml, label=label,
1640 session=session, forward_from=forward_from, form_node=form_node,
1641 original_message=original_message, delayed=delayed, callback=cb)
1642
1644 """
1645 Send contacts with RosterX (Xep-0144)
1646 """
1647 if not gajim.account_is_connected(self.name):
1648 return
1649 if len(contacts) == 1:
1650 msg = _('Sent contact: "%s" (%s)') % (contacts[0].get_full_jid(),
1651 contacts[0].get_shown_name())
1652 else:
1653 msg = _('Sent contacts:')
1654 for contact in contacts:
1655 msg += '\n "%s" (%s)' % (contact.get_full_jid(),
1656 contact.get_shown_name())
1657 msg_iq = common.xmpp.Message(to=jid, body=msg)
1658 x = msg_iq.addChild(name='x', namespace=common.xmpp.NS_ROSTERX)
1659 for contact in contacts:
1660 x.addChild(name='item', attrs={'action': 'add', 'jid': contact.jid,
1661 'name': contact.get_shown_name()})
1662 self.connection.send(msg_iq)
1663
1665 """
1666 Send a stanza untouched
1667 """
1668 if not self.connection:
1669 return
1670 self.connection.send(stanza)
1671
1673 if not gajim.account_is_connected(self.name):
1674 return
1675 log.debug('ack\'ing subscription complete for %s' % jid)
1676 p = common.xmpp.Presence(jid, 'subscribe')
1677 self.connection.send(p)
1678
1680 if not gajim.account_is_connected(self.name):
1681 return
1682 log.debug('ack\'ing unsubscription complete for %s' % jid)
1683 p = common.xmpp.Presence(jid, 'unsubscribe')
1684 self.connection.send(p)
1685
1686 - def request_subscription(self, jid, msg='', name='', groups=[],
1687 auto_auth=False, user_nick=''):
1688 if not gajim.account_is_connected(self.name):
1689 return
1690 log.debug('subscription request for %s' % jid)
1691 if auto_auth:
1692 self.jids_for_auto_auth.append(jid)
1693 # RFC 3921 section 8.2
1694 infos = {'jid': jid}
1695 if name:
1696 infos['name'] = name
1697 iq = common.xmpp.Iq('set', common.xmpp.NS_ROSTER)
1698 q = iq.getTag('query')
1699 item = q.addChild('item', attrs=infos)
1700 for g in groups:
1701 item.addChild('group').setData(g)
1702 self.connection.send(iq)
1703
1704 p = common.xmpp.Presence(jid, 'subscribe')
1705 if user_nick:
1706 p.setTag('nick', namespace = common.xmpp.NS_NICK).setData(user_nick)
1707 p = self.add_sha(p)
1708 if msg:
1709 p.setStatus(msg)
1710 self.connection.send(p)
1711
1718
1725
1727 if not gajim.account_is_connected(self.name):
1728 return
1729 if remove_auth:
1730 self.connection.getRoster().delItem(jid)
1731 jid_list = gajim.config.get_per('contacts')
1732 for j in jid_list:
1733 if j.startswith(jid):
1734 gajim.config.del_per('contacts', j)
1735 else:
1736 self.connection.getRoster().Unsubscribe(jid)
1737 self.update_contact(jid, '', [])
1738
1740 if not gajim.account_is_connected(self.name):
1741 return
1742 iq = common.xmpp.Iq('set', common.xmpp.NS_REGISTER, to = agent)
1743 iq.getTag('query').setTag('remove')
1744 id_ = self.connection.getAnID()
1745 iq.setID(id_)
1746 self.awaiting_answers[id_] = (AGENT_REMOVED, agent)
1747 self.connection.send(iq)
1748 self.connection.getRoster().delItem(agent)
1749
1751 if is_form:
1752 # Get username and password and put them in new_account_info
1753 for field in form.iter_fields():
1754 if field.var == 'username':
1755 self.new_account_info['name'] = field.value
1756 if field.var == 'password':
1757 self.new_account_info['password'] = field.value
1758 else:
1759 # Get username and password and put them in new_account_info
1760 if 'username' in form:
1761 self.new_account_info['name'] = form['username']
1762 if 'password' in form:
1763 self.new_account_info['password'] = form['password']
1764 self.new_account_form = form
1765 self.new_account(self.name, self.new_account_info)
1766
1768 # If a connection already exist we cannot create a new account
1769 if self.connection:
1770 return
1771 self._hostname = config['hostname']
1772 self.new_account_info = config
1773 self.name = name
1774 self.on_connect_success = self._on_new_account
1775 self.on_connect_failure = self._on_new_account
1776 self.connect(config)
1777
1779 if not con_type:
1780 if len(self._connection_types) or len(self._hosts):
1781 # There are still other way to try to connect
1782 return
1783 self.dispatch('NEW_ACC_NOT_CONNECTED',
1784 (_('Could not connect to "%s"') % self._hostname))
1785 return
1786 self.on_connect_failure = None
1787 self.connection = con
1788 common.xmpp.features_nb.getRegInfo(con, self._hostname)
1789
1791 """
1792 groupchat_jid is used when we want to send a request to a real jid and
1793 act as if the answer comes from the groupchat_jid
1794 """
1795 if not gajim.account_is_connected(self.name):
1796 return
1797 # If we are invisible, do not request
1798 if self.connected == gajim.SHOW_LIST.index('invisible'):
1799 self.dispatch('OS_INFO', (jid, resource, _('Not fetched because of invisible status'), _('Not fetched because of invisible status')))
1800 return
1801 to_whom_jid = jid
1802 if resource:
1803 to_whom_jid += '/' + resource
1804 iq = common.xmpp.Iq(to=to_whom_jid, typ='get', queryNS=\
1805 common.xmpp.NS_VERSION)
1806 id_ = self.connection.getAnID()
1807 iq.setID(id_)
1808 if groupchat_jid:
1809 self.groupchat_jids[id_] = groupchat_jid
1810 self.version_ids.append(id_)
1811 self.connection.send(iq)
1812
1814 """
1815 groupchat_jid is used when we want to send a request to a real jid and
1816 act as if the answer comes from the groupchat_jid
1817 """
1818 if not gajim.account_is_connected(self.name):
1819 return
1820 # If we are invisible, do not request
1821 if self.connected == gajim.SHOW_LIST.index('invisible'):
1822 self.dispatch('ENTITY_TIME', (jid, resource, _('Not fetched because of invisible status')))
1823 return
1824 to_whom_jid = jid
1825 if resource:
1826 to_whom_jid += '/' + resource
1827 iq = common.xmpp.Iq(to=to_whom_jid, typ='get')
1828 iq.addChild('time', namespace=common.xmpp.NS_TIME_REVISED)
1829 id_ = self.connection.getAnID()
1830 iq.setID(id_)
1831 if groupchat_jid:
1832 self.groupchat_jids[id_] = groupchat_jid
1833 self.entity_time_ids.append(id_)
1834 self.connection.send(iq)
1835
1837 """
1838 Get Gajim settings as described in XEP 0049
1839 """
1840 if not gajim.account_is_connected(self.name):
1841 return
1842 iq = common.xmpp.Iq(typ='get')
1843 iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
1844 iq2.addChild(name='gajim', namespace='gajim:prefs')
1845 self.connection.send(iq)
1846
1848 if not gajim.account_is_connected(self.name):
1849 return
1850 self.seclabel_catalogue_request(to, callback)
1851 iq = common.xmpp.Iq(typ='get')
1852 iq2 = iq.addChild(name='catalog', namespace=common.xmpp.NS_SECLABEL_CATALOG)
1853 iq2.setAttr('to', to)
1854 self.connection.send(iq)
1855
1857 if not gajim.account_is_connected(self.name):
1858 return
1859 iq = common.xmpp.Iq(typ='get')
1860 iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
1861 iq2.addChild(name='storage', namespace='storage:bookmarks')
1862 self.connection.send(iq)
1863
1867
1869 """
1870 Get Bookmarks from storage or PubSub if supported as described in XEP
1871 0048
1872
1873 storage_type can be set to xml to force request to xml storage
1874 """
1875 if not gajim.account_is_connected(self.name):
1876 return
1877 if self.pubsub_supported and storage_type != 'xml':
1878 self.send_pb_retrieve('', 'storage:bookmarks')
1879 # some server (ejabberd) are so slow to answer that we request via XML
1880 # if we don't get answer in the next 30 seconds
1881 gajim.idlequeue.set_alarm(self._check_bookmarks_received, 30)
1882 else:
1883 self._request_bookmarks_xml()
1884
1886 """
1887 Send bookmarks to the storage namespace or PubSub if supported
1888
1889 storage_type can be set to 'pubsub' or 'xml' so store in only one method
1890 else it will be stored on both
1891 """
1892 if not gajim.account_is_connected(self.name):
1893 return
1894 iq = common.xmpp.Node(tag='storage', attrs={'xmlns': 'storage:bookmarks'})
1895 for bm in self.bookmarks:
1896 iq2 = iq.addChild(name = "conference")
1897 iq2.setAttr('jid', bm['jid'])
1898 iq2.setAttr('autojoin', bm['autojoin'])
1899 iq2.setAttr('minimize', bm['minimize'])
1900 iq2.setAttr('name', bm['name'])
1901 # Only add optional elements if not empty
1902 # Note: need to handle both None and '' as empty
1903 # thus shouldn't use "is not None"
1904 if bm.get('nick', None):
1905 iq2.setTagData('nick', bm['nick'])
1906 if bm.get('password', None):
1907 iq2.setTagData('password', bm['password'])
1908 if bm.get('print_status', None):
1909 iq2.setTagData('print_status', bm['print_status'])
1910
1911 if self.pubsub_supported and self.pubsub_publish_options_supported and \
1912 storage_type != 'xml':
1913 options = common.xmpp.Node(common.xmpp.NS_DATA + ' x',
1914 attrs={'type': 'submit'})
1915 f = options.addChild('field', attrs={'var': 'FORM_TYPE',
1916 'type': 'hidden'})
1917 f.setTagData('value', common.xmpp.NS_PUBSUB_PUBLISH_OPTIONS)
1918 f = options.addChild('field', attrs={'var': 'pubsub#persist_items'})
1919 f.setTagData('value', 'true')
1920 f = options.addChild('field', attrs={'var': 'pubsub#access_model'})
1921 f.setTagData('value', 'whitelist')
1922 self.send_pb_publish('', 'storage:bookmarks', iq, 'current',
1923 options=options)
1924 if storage_type != 'pubsub':
1925 iqA = common.xmpp.Iq(typ='set')
1926 iqB = iqA.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
1927 iqB.addChild(node=iq)
1928 self.connection.send(iqA)
1929
1931 """
1932 Get Annonations from storage as described in XEP 0048, and XEP 0145
1933 """
1934 self.annotations = {}
1935 if not gajim.account_is_connected(self.name):
1936 return
1937 iq = common.xmpp.Iq(typ='get')
1938 iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
1939 iq2.addChild(name='storage', namespace='storage:rosternotes')
1940 self.connection.send(iq)
1941
1943 """
1944 Set Annonations in private storage as described in XEP 0048, and XEP 0145
1945 """
1946 if not gajim.account_is_connected(self.name):
1947 return
1948 iq = common.xmpp.Iq(typ='set')
1949 iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
1950 iq3 = iq2.addChild(name='storage', namespace='storage:rosternotes')
1951 for jid in self.annotations.keys():
1952 if self.annotations[jid]:
1953 iq4 = iq3.addChild(name = "note")
1954 iq4.setAttr('jid', jid)
1955 iq4.setData(self.annotations[jid])
1956 self.connection.send(iq)
1957
1958
1960 """
1961 Get metacontacts list from storage as described in XEP 0049
1962 """
1963 if not gajim.account_is_connected(self.name):
1964 return
1965 iq = common.xmpp.Iq(typ='get')
1966 iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
1967 iq2.addChild(name='storage', namespace='storage:metacontacts')
1968 id_ = self.connection.getAnID()
1969 iq.setID(id_)
1970 self.awaiting_answers[id_] = (METACONTACTS_ARRIVED, )
1971 self.connection.send(iq)
1972
1974 """
1975 Send meta contacts to the storage namespace
1976 """
1977 if not gajim.account_is_connected(self.name):
1978 return
1979 iq = common.xmpp.Iq(typ='set')
1980 iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
1981 iq3 = iq2.addChild(name='storage', namespace='storage:metacontacts')
1982 for tag in tags_list:
1983 for data in tags_list[tag]:
1984 jid = data['jid']
1985 dict_ = {'jid': jid, 'tag': tag}
1986 if 'order' in data:
1987 dict_['order'] = data['order']
1988 iq3.addChild(name = 'meta', attrs = dict_)
1989 self.connection.send(iq)
1990
1992 if not gajim.account_is_connected(self.name):
1993 return
1994 show = helpers.get_xmpp_show(gajim.SHOW_LIST[self.connected])
1995 p = common.xmpp.Presence(to = agent, typ = ptype, show = show)
1996 p = self.add_sha(p, ptype != 'unavailable')
1997 self.connection.send(p)
1998
2000 if not gajim.account_is_connected(self.name):
2001 return
2002 iq = common.xmpp.Iq(typ='set', to=jid)
2003 captcha = iq.addChild(name='captcha', namespace=common.xmpp.NS_CAPTCHA)
2004 captcha.addChild(node=form_node)
2005 self.connection.send(iq)
2006
2008 if not gajim.account_is_connected(self.name):
2009 return
2010 iq = common.xmpp.Iq(typ = 'get', to = server)
2011 iq.setAttr('id', 'unique1')
2012 iq.addChild('unique', namespace=common.xmpp.NS_MUC_UNIQUE)
2013 def _on_response(resp):
2014 if not common.xmpp.isResultNode(resp):
2015 self.dispatch('UNIQUE_ROOM_ID_UNSUPPORTED', (server, instance))
2016 return
2017 self.dispatch('UNIQUE_ROOM_ID_SUPPORTED', (server, instance,
2018 resp.getTag('unique').getData()))
2019 self.connection.SendAndCallForResponse(iq, _on_response)
2020
2022 # FIXME: This room JID needs to be normalized; see #1364
2023 if not gajim.account_is_connected(self.name):
2024 return
2025 show = helpers.get_xmpp_show(gajim.SHOW_LIST[self.connected])
2026 if show == 'invisible':
2027 # Never join a room when invisible
2028 return
2029
2030 # last date/time in history to avoid duplicate
2031 if room_jid not in self.last_history_time:
2032 # Not in memory, get it from DB
2033 last_log = None
2034 # Do not check if we are not logging for this room
2035 if gajim.config.should_log(self.name, room_jid):
2036 # Check time first in the FAST table
2037 last_log = gajim.logger.get_room_last_message_time(room_jid)
2038 if last_log is None:
2039 # Not in special table, get it from messages DB
2040 last_log = gajim.logger.get_last_date_that_has_logs(room_jid,
2041 is_room=True)
2042 # Create self.last_history_time[room_jid] even if not logging,
2043 # could be used in connection_handlers
2044 if last_log is None:
2045 last_log = 0
2046 self.last_history_time[room_jid] = last_log
2047
2048 p = common.xmpp.Presence(to='%s/%s' % (room_jid, nick),
2049 show=show, status=self.status)
2050 h = hmac.new(self.secret_hmac, room_jid).hexdigest()[:6]
2051 id_ = self.connection.getAnID()
2052 id_ = 'gajim_muc_' + id_ + '_' + h
2053 p.setID(id_)
2054 if gajim.config.get('send_sha_in_gc_presence'):
2055 p = self.add_sha(p)
2056 self.add_lang(p)
2057 if not change_nick:
2058 t = p.setTag(common.xmpp.NS_MUC + ' x')
2059 last_date = self.last_history_time[room_jid]
2060 if last_date == 0:
2061 last_date = time.time() - gajim.config.get(
2062 'muc_restore_timeout') * 60
2063 else:
2064 last_date = min(last_date, time.time() - gajim.config.get(
2065 'muc_restore_timeout') * 60)
2066 last_date = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(last_date))
2067 t.setTag('history', {'maxstanzas': gajim.config.get(
2068 'muc_restore_lines'), 'since': last_date})
2069 if password:
2070 t.setTagData('password', password)
2071 self.connection.send(p)
2072
2074 if not gajim.account_is_connected(self.name):
2075 return
2076 if not xhtml and gajim.config.get('rst_formatting_outgoing_messages'):
2077 from common.rst_xhtml_generator import create_xhtml
2078 xhtml = create_xhtml(msg)
2079 msg_iq = common.xmpp.Message(jid, msg, typ = 'groupchat', xhtml = xhtml)
2080 if label is not None:
2081 msg_iq.addChild(node = label)
2082 self.connection.send(msg_iq)
2083 self.dispatch('MSGSENT', (jid, msg))
2084
2086 if not gajim.account_is_connected(self.name):
2087 return
2088 msg_iq = common.xmpp.Message(jid, typ = 'groupchat', subject = subject)
2089 self.connection.send(msg_iq)
2090
2092 if not gajim.account_is_connected(self.name):
2093 return
2094 iq = common.xmpp.Iq(typ = 'get', queryNS = common.xmpp.NS_MUC_OWNER,
2095 to = room_jid)
2096 self.add_lang(iq)
2097 self.connection.send(iq)
2098
2100 if not gajim.account_is_connected(self.name):
2101 return
2102 iq = common.xmpp.Iq(typ = 'set', queryNS = common.xmpp.NS_MUC_OWNER,
2103 to = room_jid)
2104 destroy = iq.getTag('query').setTag('destroy')
2105 if reason:
2106 destroy.setTagData('reason', reason)
2107 if jid:
2108 destroy.setAttr('jid', jid)
2109 self.connection.send(iq)
2110
2112 if not gajim.account_is_connected(self.name):
2113 return
2114 if show == 'invisible':
2115 show = 'offline'
2116 ptype = None
2117 if show == 'offline':
2118 ptype = 'unavailable'
2119 xmpp_show = helpers.get_xmpp_show(show)
2120 p = common.xmpp.Presence(to = '%s/%s' % (jid, nick), typ = ptype,
2121 show = xmpp_show, status = status)
2122 h = hmac.new(self.secret_hmac, jid).hexdigest()[:6]
2123 id_ = self.connection.getAnID()
2124 id_ = 'gajim_muc_' + id_ + '_' + h
2125 p.setID(id_)
2126 if gajim.config.get('send_sha_in_gc_presence') and show != 'offline':
2127 p = self.add_sha(p, ptype != 'unavailable')
2128 self.add_lang(p)
2129 # send instantly so when we go offline, status is sent to gc before we
2130 # disconnect from jabber server
2131 self.connection.send(p)
2132
2134 """
2135 A groupchat got disconnected. This can be or purpose or not
2136
2137 Save the time we had last message to avoid duplicate logs AND be faster
2138 than get that date from DB. Save time that we have in mem in a small
2139 table (with fast access)
2140 """
2141 gajim.logger.set_room_last_message_time(room_jid, self.last_history_time[room_jid])
2142
2144 """
2145 Role is for all the life of the room so it's based on nick
2146 """
2147 if not gajim.account_is_connected(self.name):
2148 return
2149 iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
2150 common.xmpp.NS_MUC_ADMIN)
2151 item = iq.getTag('query').setTag('item')
2152 item.setAttr('nick', nick)
2153 item.setAttr('role', role)
2154 if reason:
2155 item.addChild(name = 'reason', payload = reason)
2156 self.connection.send(iq)
2157
2159 """
2160 Affiliation is for all the life of the room so it's based on jid
2161 """
2162 if not gajim.account_is_connected(self.name):
2163 return
2164 iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
2165 common.xmpp.NS_MUC_ADMIN)
2166 item = iq.getTag('query').setTag('item')
2167 item.setAttr('jid', jid)
2168 item.setAttr('affiliation', affiliation)
2169 if reason:
2170 item.addChild(name = 'reason', payload = reason)
2171 self.connection.send(iq)
2172
2174 if not gajim.account_is_connected(self.name):
2175 return
2176 iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS = \
2177 common.xmpp.NS_MUC_ADMIN)
2178 item = iq.getTag('query')
2179 for jid in users_dict:
2180 item_tag = item.addChild('item', {'jid': jid,
2181 'affiliation': users_dict[jid]['affiliation']})
2182 if 'reason' in users_dict[jid] and users_dict[jid]['reason']:
2183 item_tag.setTagData('reason', users_dict[jid]['reason'])
2184 self.connection.send(iq)
2185
2187 if not gajim.account_is_connected(self.name):
2188 return
2189 iq = common.xmpp.Iq(typ = 'get', to = room_jid, queryNS = \
2190 common.xmpp.NS_MUC_ADMIN)
2191 item = iq.getTag('query').setTag('item')
2192 item.setAttr('affiliation', affiliation)
2193 self.connection.send(iq)
2194
2196 if not gajim.account_is_connected(self.name):
2197 return
2198 iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
2199 common.xmpp.NS_MUC_OWNER)
2200 query = iq.getTag('query')
2201 form.setAttr('type', 'submit')
2202 query.addChild(node = form)
2203 self.connection.send(iq)
2204
2206 if not gajim.account_is_connected(self.name):
2207 return
2208 hostname = gajim.config.get_per('accounts', self.name, 'hostname')
2209 username = gajim.config.get_per('accounts', self.name, 'name')
2210 iq = common.xmpp.Iq(typ = 'set', to = hostname)
2211 q = iq.setTag(common.xmpp.NS_REGISTER + ' query')
2212 q.setTagData('username', username)
2213 q.setTagData('password', password)
2214 self.connection.send(iq)
2215
2217 self.pasword_callback = (callback, type_)
2218 if self.password:
2219 self.set_password(self.password)
2220 return
2221 self.dispatch('PASSWORD_REQUIRED', None)
2222
2224 self.password = password
2225 if self.pasword_callback:
2226 callback, type_ = self.pasword_callback
2227 if self._current_type == 'plain' and type_ == 'PLAIN' and \
2228 gajim.config.get_per('accounts', self.name,
2229 'warn_when_insecure_password'):
2230 self.dispatch('INSECURE_PASSWORD', None)
2231 return
2232 callback(password)
2233 self.pasword_callback = None
2234
2236 if self.pasword_callback:
2237 callback, type_ = self.pasword_callback
2238 callback(self.password)
2239 self.pasword_callback = None
2240
2242 # no need to write this as a class method and keep the value of
2243 # on_remove_success as a class property as pass it as an argument
2244 def _on_unregister_account_connect(con):
2245 self.on_connect_auth = None
2246 if gajim.account_is_connected(self.name):
2247 hostname = gajim.config.get_per('accounts', self.name, 'hostname')
2248 iq = common.xmpp.Iq(typ = 'set', to = hostname)
2249 iq.setTag(common.xmpp.NS_REGISTER + ' query').setTag('remove')
2250 def _on_answer(result):
2251 if result.getType() == 'result':
2252 on_remove_success(True)
2253 return
2254 self.dispatch('ERROR', (_('Unregister failed'),
2255 _('Unregistration with server %(server)s failed: %(error)s') \
2256 % {'server': hostname, 'error': result.getErrorMsg()}))
2257 on_remove_success(False)
2258 con.SendAndCallForResponse(iq, _on_answer)
2259 return
2260 on_remove_success(False)
2261 if self.connected == 0:
2262 self.on_connect_auth = _on_unregister_account_connect
2263 self.connect_and_auth()
2264 else:
2265 _on_unregister_account_connect(self.connection)
2266
2268 """
2269 Send invitation
2270 """
2271 message=common.xmpp.Message(to = room)
2272 c = message.addChild(name = 'x', namespace = common.xmpp.NS_MUC_USER)
2273 c = c.addChild(name = 'invite', attrs={'to' : to})
2274 if continue_tag:
2275 c.addChild(name = 'continue')
2276 if reason != '':
2277 c.setTagData('reason', reason)
2278 self.connection.send(message)
2279
2281 if self.awaiting_xmpp_ping_id:
2282 # We haven't got the pong in time, disco and reconnect
2283 log.warn("No reply received for keepalive ping. Reconnecting.")
2284 self._disconnectedReconnCB()
2285
2287 if self.time_to_reconnect:
2288 if self.connected < 2:
2289 self._reconnect()
2290 else:
2291 self.time_to_reconnect = None
2292
2294 iq = common.xmpp.Iq(typ = 'get', to = jid, queryNS = \
2295 common.xmpp.NS_SEARCH)
2296 self.connection.send(iq)
2297
2299 iq = common.xmpp.Iq(typ = 'set', to = jid, queryNS = \
2300 common.xmpp.NS_SEARCH)
2301 item = iq.getTag('query')
2302 if is_form:
2303 item.addChild(node = form)
2304 else:
2305 for i in form.keys():
2306 item.setTagData(i, form[i])
2307 def _on_response(resp):
2308 jid = jid = helpers.get_jid_from_iq(resp)
2309 tag = resp.getTag('query', namespace = common.xmpp.NS_SEARCH)
2310 if not tag:
2311 self.dispatch('SEARCH_RESULT', (jid, None, False))
2312 return
2313 df = tag.getTag('x', namespace = common.xmpp.NS_DATA)
2314 if df:
2315 self.dispatch('SEARCH_RESULT', (jid, df, True))
2316 return
2317 df = []
2318 for item in tag.getTags('item'):
2319 # We also show attributes. jid is there
2320 f = item.attrs
2321 for i in item.getPayload():
2322 f[i.getName()] = i.getData()
2323 df.append(f)
2324 self.dispatch('SEARCH_RESULT', (jid, df, False))
2325
2326 self.connection.SendAndCallForResponse(iq, _on_response)
2327
2329 roster = gajim.logger.get_roster(gajim.get_jid_from_account(self.name))
2330 self.dispatch('ROSTER', roster)
2331
2332
2333 # END Connection
2334
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Aug 12 02:07:22 2010 | http://epydoc.sourceforge.net |