2 ## src/common/connection.py
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>
20 ## This file is part of Gajim.
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.
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.
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/>.
45 randomsource
= random
.SystemRandom()
47 randomsource
= random
.Random()
52 signal
.signal(signal
.SIGPIPE
, signal
.SIG_DFL
)
55 from common
import helpers
56 from common
import gajim
57 from common
import gpg
58 from common
import passwords
59 from common
import exceptions
61 from connection_handlers
import *
63 from string
import Template
65 log
= logging
.getLogger('gajim.c.connection')
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")
102 class CommonConnection
:
104 Common connection class, can be derivated for normal connection or zeroconf
108 def __init__(self
, name
):
112 # 1=>connection in progress,
117 self
.connection
= None # xmpppy ClientCommon instance
118 self
.on_purpose
= False
119 self
.is_zeroconf
= False
121 self
.server_resource
= self
._compute
_resource
()
126 self
.gpg
= gpg
.GnuPG(gajim
.config
.get('use_gpg_agent'))
129 self
.priority
= gajim
.get_priority(name
, 'offline')
130 self
.time_to_reconnect
= None
133 self
.blocked_list
= []
134 self
.blocked_contacts
= []
135 self
.blocked_groups
= []
136 self
.blocked_all
= False
138 self
.seclabel_supported
= False
139 self
.seclabel_catalogues
= {}
141 self
.pep_supported
= False
143 # Do we continue connection when we get roster (send presence,get vcard..)
144 self
.continue_connect_info
= None
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
151 self
.groupchat_jids
= {} # {ID : groupchat_jid}
153 self
.privacy_rules_supported
= False
154 self
.vcard_supported
= False
155 self
.private_storage_supported
= False
156 self
.archiving_supported
= False
157 self
.archive_pref_supported
= False
159 self
.muc_jid
= {} # jid of muc server for each transport type
160 self
._stun
_servers
= [] # STUN servers of our jabber server
162 self
.awaiting_cids
= {} # Used for XEP-0231
164 self
.nested_group_delimiter
= '::'
166 self
.get_config_values_or_default()
168 def _compute_resource(self
):
169 resource
= gajim
.config
.get_per('accounts', self
.name
, 'resource')
170 # All valid resource substitution strings should be added to this hash.
172 resource
= Template(resource
).safe_substitute({
173 'hostname': socket
.gethostname()
177 def dispatch(self
, event
, data
):
179 Always passes account name as first param
181 gajim
.ged
.raise_event(event
, self
.name
, data
)
183 def _reconnect(self
):
185 To be implemented by derivated classes
187 raise NotImplementedError
189 def quit(self
, kill_core
):
190 if kill_core
and gajim
.account_is_connected(self
.name
):
191 self
.disconnect(on_purpose
=True)
193 def test_gpg_passphrase(self
, password
):
195 Returns 'ok', 'bad_pass' or 'expired'
199 self
.gpg
.passphrase
= password
200 keyID
= gajim
.config
.get_per('accounts', self
.name
, 'keyid')
201 signed
= self
.gpg
.sign('test', keyID
)
202 self
.gpg
.password
= None
203 if signed
== 'KEYEXPIRED':
205 elif signed
== 'BAD_PASSPHRASE':
209 def get_signed_msg(self
, msg
, callback
= None):
211 Returns the signed message if possible or an empty string if gpg is not
212 used or None if waiting for passphrase
214 callback is the function to call when user give the passphrase
217 keyID
= gajim
.config
.get_per('accounts', self
.name
, 'keyid')
218 if keyID
and self
.USE_GPG
:
219 use_gpg_agent
= gajim
.config
.get('use_gpg_agent')
220 if self
.gpg
.passphrase
is None and not use_gpg_agent
:
221 # We didn't set a passphrase
223 signed
= self
.gpg
.sign(msg
, keyID
)
224 if signed
== 'BAD_PASSPHRASE':
227 gajim
.nec
.push_incoming_event(BadGPGPassphraseEvent(None,
231 def _on_disconnected(self
):
233 Called when a disconnect request has completed successfully
235 self
.disconnect(on_purpose
=True)
236 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
239 def get_status(self
):
240 return gajim
.SHOW_LIST
[self
.connected
]
242 def check_jid(self
, jid
):
244 This function must be implemented by derivated classes. It has to return
245 the valid jid, or raise a helpers.InvalidFormat exception
247 raise NotImplementedError
249 def _prepare_message(self
, jid
, msg
, keyID
, type_
='chat', subject
='',
250 chatstate
=None, msg_id
=None, composing_xep
=None, resource
=None,
251 user_nick
=None, xhtml
=None, session
=None, forward_from
=None, form_node
=None,
252 label
=None, original_message
=None, delayed
=None, callback
=None):
253 if not self
.connection
or self
.connected
< 2:
256 jid
= self
.check_jid(jid
)
257 except helpers
.InvalidFormat
:
258 self
.dispatch('ERROR', (_('Invalid Jabber ID'),
259 _('It is not possible to send a message to %s, this JID is not '
263 if msg
and not xhtml
and gajim
.config
.get(
264 'rst_formatting_outgoing_messages'):
265 from common
.rst_xhtml_generator
import create_xhtml
266 xhtml
= create_xhtml(msg
)
267 if not msg
and chatstate
is None and form_node
is None:
271 fjid
+= '/' + resource
276 fjid
= session
.get_to()
278 if keyID
and self
.USE_GPG
:
280 if keyID
== 'UNKNOWN':
281 error
= _('Neither the remote presence is signed, nor a key was '
283 elif keyID
.endswith('MISMATCH'):
284 error
= _('The contact\'s key (%s) does not match the key assigned '
285 'in Gajim.' % keyID
[:8])
287 def encrypt_thread(msg
, keyID
, always_trust
=False):
288 # encrypt message. This function returns (msgenc, error)
289 return self
.gpg
.encrypt(msg
, [keyID
], always_trust
)
290 def _on_encrypted(output
):
291 msgenc
, error
= output
292 if error
== 'NOT_TRUSTED':
293 def _on_always_trust(answer
):
295 gajim
.thread_interface(encrypt_thread
, [msg
, keyID
,
296 True], _on_encrypted
, [])
298 self
._message
_encrypted
_cb
(output
, type_
, msg
,
299 msgtxt
, original_message
, fjid
, resource
,
300 jid
, xhtml
, subject
, chatstate
, msg_id
,
301 composing_xep
, label
, forward_from
, delayed
,
302 session
, form_node
, user_nick
, keyID
,
304 gajim
.nec
.push_incoming_event(GPGTrustKeyEvent(None,
305 conn
=self
, callback
=_on_always_trust
))
307 self
._message
_encrypted
_cb
(output
, type_
, msg
, msgtxt
,
308 original_message
, fjid
, resource
, jid
, xhtml
,
309 subject
, chatstate
, msg_id
, composing_xep
, label
,
310 forward_from
, delayed
, session
, form_node
,
311 user_nick
, keyID
, callback
)
312 gajim
.thread_interface(encrypt_thread
, [msg
, keyID
, False],
316 self
._message
_encrypted
_cb
(('', error
), type_
, msg
, msgtxt
,
317 original_message
, fjid
, resource
, jid
, xhtml
, subject
,
318 chatstate
, msg_id
, composing_xep
, label
, forward_from
, delayed
,
319 session
, form_node
, user_nick
, keyID
, callback
)
321 self
._on
_continue
_message
(type_
, msg
, msgtxt
, original_message
, fjid
,
322 resource
, jid
, xhtml
, subject
, msgenc
, keyID
, chatstate
, msg_id
,
323 composing_xep
, label
, forward_from
, delayed
, session
, form_node
,
326 def _message_encrypted_cb(self
, output
, type_
, msg
, msgtxt
,
327 original_message
, fjid
, resource
, jid
, xhtml
, subject
, chatstate
, msg_id
,
328 composing_xep
, label
, forward_from
, delayed
, session
, form_node
, user_nick
,
330 msgenc
, error
= output
332 if msgenc
and not error
:
333 msgtxt
= '[This message is *encrypted* (See :XEP:`27`]'
334 lang
= os
.getenv('LANG')
335 if lang
is not None and lang
!= 'en': # we're not english
336 # one in locale and one en
337 msgtxt
= _('[This message is *encrypted* (See :XEP:`27`]') + \
339 self
._on
_continue
_message
(type_
, msg
, msgtxt
, original_message
,
340 fjid
, resource
, jid
, xhtml
, subject
, msgenc
, keyID
,
341 chatstate
, msg_id
, composing_xep
, label
, forward_from
, delayed
,
342 session
, form_node
, user_nick
, callback
)
344 # Encryption failed, do not send message
346 gajim
.nec
.push_incoming_event(MessageNotSentEvent(None, conn
=self
,
347 jid
=jid
, message
=msgtxt
, error
=error
, time_
=tim
, session
=session
))
349 def _on_continue_message(self
, type_
, msg
, msgtxt
, original_message
, fjid
,
350 resource
, jid
, xhtml
, subject
, msgenc
, keyID
, chatstate
, msg_id
,
351 composing_xep
, label
, forward_from
, delayed
, session
, form_node
, user_nick
,
354 msg_iq
= common
.xmpp
.Message(to
=fjid
, body
=msgtxt
, typ
=type_
,
358 msg_iq
= common
.xmpp
.Message(to
=fjid
, body
=msgtxt
, typ
='normal',
359 subject
=subject
, xhtml
=xhtml
)
361 msg_iq
= common
.xmpp
.Message(to
=fjid
, body
=msgtxt
, typ
='normal',
368 msg_iq
.setTag(common
.xmpp
.NS_ENCRYPTED
+ ' x').setData(msgenc
)
371 msg_iq
.addChild(node
=form_node
)
373 msg_iq
.addChild(node
=label
)
375 # XEP-0172: user_nickname
377 msg_iq
.setTag('nick', namespace
= common
.xmpp
.NS_NICK
).setData(
380 # TODO: We might want to write a function so we don't need to
381 # reproduce that ugly if somewhere else.
383 contact
= gajim
.contacts
.get_contact(self
.name
, jid
, resource
)
385 contact
= gajim
.contacts
.get_contact_with_highest_priority(self
.name
,
388 # chatstates - if peer supports xep85 or xep22, send chatstates
389 # please note that the only valid tag inside a message containing a <body>
390 # tag is the active event
391 if chatstate
is not None and contact
:
392 if ((composing_xep
== 'XEP-0085' or not composing_xep
) \
393 and composing_xep
!= 'asked_once') or \
394 contact
.supports(common
.xmpp
.NS_CHATSTATES
):
396 msg_iq
.setTag(chatstate
, namespace
=common
.xmpp
.NS_CHATSTATES
)
397 if composing_xep
in ('XEP-0022', 'asked_once') or \
400 chatstate_node
= msg_iq
.setTag('x', namespace
=common
.xmpp
.NS_EVENT
)
401 if chatstate
is 'composing' or msgtxt
:
402 chatstate_node
.addChild(name
='composing')
405 addresses
= msg_iq
.addChild('addresses',
406 namespace
=common
.xmpp
.NS_ADDRESS
)
407 addresses
.addChild('address', attrs
= {'type': 'ofrom',
408 'jid': forward_from
})
412 our_jid
= gajim
.get_jid_from_account(self
.name
) + '/' + \
414 timestamp
= time
.strftime('%Y-%m-%dT%H:%M:%SZ', time
.gmtime(delayed
))
415 msg_iq
.addChild('delay', namespace
=common
.xmpp
.NS_DELAY2
,
416 attrs
={'from': our_jid
, 'stamp': timestamp
})
419 if msgtxt
and gajim
.config
.get_per('accounts', self
.name
,
420 'request_receipt') and contact
and contact
.supports(
421 common
.xmpp
.NS_RECEIPTS
):
422 msg_iq
.setTag('request', namespace
=common
.xmpp
.NS_RECEIPTS
)
426 session
.last_send
= time
.time()
427 msg_iq
.setThread(session
.thread_id
)
430 if session
.enable_encryption
:
431 msg_iq
= session
.encrypt_stanza(msg_iq
)
434 callback(jid
, msg
, keyID
, forward_from
, session
, original_message
,
435 subject
, type_
, msg_iq
)
437 def log_message(self
, jid
, msg
, forward_from
, session
, original_message
,
439 if not forward_from
and session
and session
.is_loggable():
440 ji
= gajim
.get_jid_without_resource(jid
)
441 if gajim
.config
.should_log(self
.name
, ji
):
443 if original_message
is not None:
444 log_msg
= original_message
446 log_msg
= _('Subject: %(subject)s\n%(message)s') % \
447 {'subject': subject
, 'message': log_msg
}
450 kind
= 'chat_msg_sent'
452 kind
= 'single_msg_sent'
454 gajim
.logger
.write(kind
, jid
, log_msg
)
455 except exceptions
.PysqliteOperationalError
, e
:
456 self
.dispatch('DB_ERROR', (_('Disk Write Error'),
458 except exceptions
.DatabaseMalformed
:
459 pritext
= _('Database Error')
460 sectext
= _('The database file (%s) cannot be read. Try to '
461 'repair it (see http://trac.gajim.org/wiki/DatabaseBackup)'
462 ' or remove it (all history will be lost).') % \
463 common
.logger
.LOG_DB_PATH
464 self
.dispatch('DB_ERROR', (pritext
, sectext
))
466 def ack_subscribed(self
, jid
):
468 To be implemented by derivated classes
470 raise NotImplementedError
472 def ack_unsubscribed(self
, jid
):
474 To be implemented by derivated classes
476 raise NotImplementedError
478 def request_subscription(self
, jid
, msg
='', name
='', groups
=[],
481 To be implemented by derivated classes
483 raise NotImplementedError
485 def send_authorization(self
, jid
):
487 To be implemented by derivated classes
489 raise NotImplementedError
491 def refuse_authorization(self
, jid
):
493 To be implemented by derivated classes
495 raise NotImplementedError
497 def unsubscribe(self
, jid
, remove_auth
= True):
499 To be implemented by derivated classes
501 raise NotImplementedError
503 def unsubscribe_agent(self
, agent
):
505 To be implemented by derivated classes
507 raise NotImplementedError
509 def update_contact(self
, jid
, name
, groups
):
511 self
.connection
.getRoster().setItem(jid
=jid
, name
=name
, groups
=groups
)
513 def update_contacts(self
, contacts
):
515 Update multiple roster items
518 self
.connection
.getRoster().setItemMulti(contacts
)
520 def new_account(self
, name
, config
, sync
=False):
522 To be implemented by derivated classes
524 raise NotImplementedError
526 def _on_new_account(self
, con
=None, con_type
=None):
528 To be implemented by derivated classes
530 raise NotImplementedError
532 def account_changed(self
, new_name
):
535 def request_last_status_time(self
, jid
, resource
, groupchat_jid
=None):
537 groupchat_jid is used when we want to send a request to a real jid and
538 act as if the answer comes from the groupchat_jid
540 if not gajim
.account_is_connected(self
.name
):
544 to_whom_jid
+= '/' + resource
545 iq
= common
.xmpp
.Iq(to
=to_whom_jid
, typ
='get', queryNS
=\
547 id_
= self
.connection
.getAnID()
550 self
.groupchat_jids
[id_
] = groupchat_jid
551 self
.last_ids
.append(id_
)
552 self
.connection
.send(iq
)
554 def request_os_info(self
, jid
, resource
):
556 To be implemented by derivated classes
558 raise NotImplementedError
560 def get_settings(self
):
562 To be implemented by derivated classes
564 raise NotImplementedError
566 def get_bookmarks(self
):
568 To be implemented by derivated classes
570 raise NotImplementedError
572 def store_bookmarks(self
):
574 To be implemented by derivated classes
576 raise NotImplementedError
578 def get_metacontacts(self
):
580 To be implemented by derivated classes
582 raise NotImplementedError
584 def send_agent_status(self
, agent
, ptype
):
586 To be implemented by derivated classes
588 raise NotImplementedError
590 def gpg_passphrase(self
, passphrase
):
592 use_gpg_agent
= gajim
.config
.get('use_gpg_agent')
594 self
.gpg
.passphrase
= None
596 self
.gpg
.passphrase
= passphrase
598 def ask_gpg_keys(self
):
600 return self
.gpg
.get_keys()
603 def ask_gpg_secrete_keys(self
):
605 return self
.gpg
.get_secret_keys()
608 def load_roster_from_db(self
):
609 # Do nothing by default
612 def _event_dispatcher(self
, realm
, event
, data
):
614 if event
== common
.xmpp
.transports_nb
.DATA_RECEIVED
:
615 gajim
.nec
.push_incoming_event(StanzaReceivedEvent(None,
616 conn
=self
, stanza_str
=unicode(data
, errors
='ignore')))
617 elif event
== common
.xmpp
.transports_nb
.DATA_SENT
:
618 gajim
.nec
.push_incoming_event(StanzaSentEvent(None, conn
=self
,
619 stanza_str
=unicode(data
)))
621 def change_status(self
, show
, msg
, auto
=False):
625 if not auto
and not show
== 'offline':
627 if show
!= 'invisible':
628 # We save it only when privacy list is accepted
630 if show
!= 'offline' and self
.connected
< 1:
631 # set old_show to requested 'show' in case we need to
632 # recconect before we auth to server
634 self
.on_purpose
= False
635 self
.server_resource
= self
._compute
_resource
()
638 self
.gpg
= gpg
.GnuPG(gajim
.config
.get('use_gpg_agent'))
639 self
.connect_and_init(show
, msg
, sign_msg
)
642 if show
== 'offline':
645 p
= common
.xmpp
.Presence(typ
= 'unavailable')
646 p
= self
.add_sha(p
, False)
650 self
.connection
.RegisterDisconnectHandler(self
._on
_disconnected
)
651 self
.connection
.send(p
, now
=True)
652 self
.connection
.start_disconnect()
654 self
._on
_disconnected
()
657 if show
!= 'offline' and self
.connected
> 0:
658 # dont'try to connect, when we are in state 'connecting'
659 if self
.connected
== 1:
661 if show
== 'invisible':
662 self
._change
_to
_invisible
(msg
)
664 if show
not in ['offline', 'online', 'chat', 'away', 'xa', 'dnd']:
666 was_invisible
= self
.connected
== gajim
.SHOW_LIST
.index('invisible')
667 self
.connected
= gajim
.SHOW_LIST
.index(show
)
669 self
._change
_from
_invisible
()
670 self
._update
_status
(show
, msg
)
672 class Connection(CommonConnection
, ConnectionHandlers
):
673 def __init__(self
, name
):
674 CommonConnection
.__init
__(self
, name
)
675 ConnectionHandlers
.__init
__(self
)
676 # this property is used to prevent double connections
677 self
.last_connection
= None # last ClientCommon instance
678 # If we succeed to connect, remember it so next time we try (after a
679 # disconnection) we try only this type.
680 self
.last_connection_type
= None
682 if locale
.getdefaultlocale()[0]:
683 self
.lang
= locale
.getdefaultlocale()[0].split('_')[0]
684 # increase/decrease default timeout for server responses
685 self
.try_connecting_for_foo_secs
= 45
686 # holds the actual hostname to which we are connected
687 self
.connected_hostname
= None
688 self
.last_time_to_reconnect
= None
689 self
.new_account_info
= None
690 self
.new_account_form
= None
691 self
.annotations
= {}
692 self
.last_io
= gajim
.idlequeue
.current_time()
694 self
.last_history_time
= {}
695 self
.password
= passwords
.get_password(name
)
697 self
.music_track_info
= 0
698 self
.location_info
= {}
699 self
.pubsub_supported
= False
700 self
.pubsub_publish_options_supported
= False
701 # Do we auto accept insecure connection
702 self
.connection_auto_accepted
= False
703 self
.pasword_callback
= None
705 self
.on_connect_success
= None
706 self
.on_connect_failure
= None
708 self
.jids_for_auto_auth
= [] # list of jid to auto-authorize
709 self
.available_transports
= {} # list of available transports on this
710 # server {'icq': ['icq.server.com', 'icq2.server.com'], }
711 self
.private_storage_supported
= True
712 self
.streamError
= ''
713 self
.secret_hmac
= str(random
.random())[2:]
714 gajim
.ged
.register_event_handler('privacy-list-received', ged
.CORE
,
715 self
._nec
_privacy
_list
_received
)
716 gajim
.ged
.register_event_handler('agent-info-error-received', ged
.CORE
,
717 self
._nec
_agent
_info
_error
_received
)
718 gajim
.ged
.register_event_handler('agent-info-received', ged
.CORE
,
719 self
._nec
_agent
_info
_received
)
723 ConnectionHandlers
.cleanup(self
)
724 gajim
.ged
.remove_event_handler('privacy-list-received', ged
.CORE
,
725 self
._nec
_privacy
_list
_received
)
726 gajim
.ged
.remove_event_handler('agent-info-error-received', ged
.CORE
,
727 self
._nec
_agent
_info
_error
_received
)
728 gajim
.ged
.remove_event_handler('agent-info-received', ged
.CORE
,
729 self
._nec
_agent
_info
_received
)
731 def get_config_values_or_default(self
):
732 if gajim
.config
.get_per('accounts', self
.name
, 'keep_alives_enabled'):
733 self
.keepalives
= gajim
.config
.get_per('accounts', self
.name
,
734 'keep_alive_every_foo_secs')
737 if gajim
.config
.get_per('accounts', self
.name
, 'ping_alives_enabled'):
738 self
.pingalives
= gajim
.config
.get_per('accounts', self
.name
,
739 'ping_alive_every_foo_secs')
742 self
.client_cert
= gajim
.config
.get_per('accounts', self
.name
,
745 def check_jid(self
, jid
):
746 return helpers
.parse_jid(jid
)
748 def _reconnect(self
):
749 # Do not try to reco while we are already trying
750 self
.time_to_reconnect
= None
751 if self
.connected
< 2: # connection failed
752 log
.debug('reconnect')
754 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
757 self
.on_connect_auth
= self
._discover
_server
_at
_connection
758 self
.connect_and_init(self
.old_show
, self
.status
, self
.USE_GPG
)
760 # reconnect succeeded
761 self
.time_to_reconnect
= None
764 # We are doing disconnect at so many places, better use one function in all
765 def disconnect(self
, on_purpose
=False):
766 gajim
.interface
.music_track_changed(None, None, self
.name
)
767 self
.reset_awaiting_pep()
768 self
.on_purpose
= on_purpose
770 self
.time_to_reconnect
= None
771 self
.privacy_rules_supported
= False
773 # make sure previous connection is completely closed
774 gajim
.proxy65_manager
.disconnect(self
.connection
)
775 self
.terminate_sessions()
776 self
.remove_all_transfers()
777 self
.connection
.disconnect()
778 self
.last_connection
= None
779 self
.connection
= None
781 def _disconnectedReconnCB(self
):
783 Called when we are disconnected
785 log
.info('disconnectedReconnCB called')
786 if gajim
.account_is_connected(self
.name
):
787 # we cannot change our status to offline or connecting
788 # after we auth to server
789 self
.old_show
= gajim
.SHOW_LIST
[self
.connected
]
791 if not self
.on_purpose
:
792 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
795 if gajim
.config
.get_per('accounts', self
.name
, 'autoreconnect'):
797 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
799 if gajim
.status_before_autoaway
[self
.name
]:
800 # We were auto away. So go back online
801 self
.status
= gajim
.status_before_autoaway
[self
.name
]
802 gajim
.status_before_autoaway
[self
.name
] = ''
803 self
.old_show
= 'online'
804 # this check has moved from _reconnect method
805 # do exponential backoff until 15 minutes,
806 # then small linear increase
807 if self
.retrycount
< 2 or self
.last_time_to_reconnect
is None:
808 self
.last_time_to_reconnect
= 5
809 if self
.last_time_to_reconnect
< 800:
810 self
.last_time_to_reconnect
*= 1.5
811 self
.last_time_to_reconnect
+= randomsource
.randint(0, 5)
812 self
.time_to_reconnect
= int(self
.last_time_to_reconnect
)
813 log
.info("Reconnect to %s in %ss", self
.name
, self
.time_to_reconnect
)
814 gajim
.idlequeue
.set_alarm(self
._reconnect
_alarm
,
815 self
.time_to_reconnect
)
816 elif self
.on_connect_failure
:
817 self
.on_connect_failure()
818 self
.on_connect_failure
= None
821 self
._connection
_lost
()
824 self
.on_purpose
= False
825 # END disconnectedReconnCB
827 def _connection_lost(self
):
828 log
.debug('_connection_lost')
829 self
.disconnect(on_purpose
= False)
830 gajim
.nec
.push_incoming_event(ConnectionLostEvent(None, conn
=self
,
831 title
=_('Connection with account "%s" has been lost') % self
.name
,
832 msg
=_('Reconnect manually.')))
834 def _event_dispatcher(self
, realm
, event
, data
):
835 CommonConnection
._event
_dispatcher
(self
, realm
, event
, data
)
836 if realm
== common
.xmpp
.NS_REGISTER
:
837 if event
== common
.xmpp
.features_nb
.REGISTER_DATA_RECEIVED
:
838 # data is (agent, DataFrom, is_form, error_msg)
839 if self
.new_account_info
and \
840 self
.new_account_info
['hostname'] == data
[0]:
842 if not data
[1]: # wrong answer
843 reason
= _('Server %(name)s answered wrongly to '
844 'register request: %(error)s') % {'name': data
[0],
846 gajim
.nec
.push_incoming_event(AccountNotCreatedEvent(
847 None, conn
=self
, reason
=reason
))
851 if data
[4] is not '':
852 helpers
.replace_dataform_media(conf
, data
[4])
853 if self
.new_account_form
:
854 def _on_register_result(result
):
855 if not common
.xmpp
.isResultNode(result
):
856 gajim
.nec
.push_incoming_event(AccountNotCreatedEvent(
857 None, conn
=self
, reason
=result
.getError()))
861 self
.gpg
= gpg
.GnuPG(gajim
.config
.get(
863 gajim
.nec
.push_incoming_event(
864 AccountCreatedEvent(None, conn
=self
,
865 account_info
= self
.new_account_info
))
866 self
.new_account_info
= None
867 self
.new_account_form
= None
869 self
.connection
.UnregisterDisconnectHandler(
870 self
._on
_new
_account
)
871 self
.disconnect(on_purpose
=True)
872 # it's the second time we get the form, we have info user
873 # typed, so send them
875 #TODO: Check if form has changed
876 iq
= common
.xmpp
.Iq('set', common
.xmpp
.NS_REGISTER
,
878 iq
.setTag('query').addChild(node
=self
.new_account_form
)
879 self
.connection
.SendAndCallForResponse(iq
,
882 if self
.new_account_form
.keys().sort() != \
884 # requested config has changed since first connection
885 reason
= _('Server %s provided a different '
886 'registration form') % data
[0]
887 gajim
.nec
.push_incoming_event(AccountNotCreatedEvent(
888 None, conn
=self
, reason
=reason
))
890 common
.xmpp
.features_nb
.register(self
.connection
,
891 self
._hostname
, self
.new_account_form
,
894 gajim
.nec
.push_incoming_event(NewAccountConnectedEvent(None,
895 conn
=self
, config
=conf
, is_form
=is_form
))
896 self
.connection
.UnregisterDisconnectHandler(
897 self
._on
_new
_account
)
898 self
.disconnect(on_purpose
=True)
900 if not data
[1]: # wrong answer
901 self
.dispatch('ERROR', (_('Invalid answer'),
902 _('Transport %(name)s answered wrongly to register '
903 'request: %(error)s') % {'name': data
[0],
908 gajim
.nec
.push_incoming_event(RegisterAgentInfoReceivedEvent(
909 None, conn
=self
, agent
=data
[0], config
=conf
,
911 elif realm
== common
.xmpp
.NS_PRIVACY
:
912 if event
== common
.xmpp
.features_nb
.PRIVACY_LISTS_RECEIVED
:
914 gajim
.nec
.push_incoming_event(PrivacyListsReceivedEvent(None,
915 conn
=self
, lists_list
=data
))
916 elif event
== common
.xmpp
.features_nb
.PRIVACY_LIST_RECEIVED
:
921 name
= data
.getTag('query').getTag('list').getAttr('name')
922 for child
in data
.getTag('query').getTag('list').getChildren():
923 dict_item
= child
.getAttrs()
925 if 'type' in dict_item
:
926 for scnd_child
in child
.getChildren():
927 childs
+= [scnd_child
.getName()]
928 rules
.append({'action':dict_item
['action'],
929 'type':dict_item
['type'], 'order':dict_item
['order'],
930 'value':dict_item
['value'], 'child':childs
})
932 for scnd_child
in child
.getChildren():
933 childs
.append(scnd_child
.getName())
934 rules
.append({'action':dict_item
['action'],
935 'order':dict_item
['order'], 'child':childs
})
936 gajim
.nec
.push_incoming_event(PrivacyListReceivedEvent(None,
937 conn
=self
, list_name
=name
, rules
=rules
))
938 elif event
== common
.xmpp
.features_nb
.PRIVACY_LISTS_ACTIVE_DEFAULT
:
940 gajim
.nec
.push_incoming_event(PrivacyListActiveDefaultEvent(
941 None, conn
=self
, active_list
=data
['active'],
942 default_list
=data
['default']))
944 def _select_next_host(self
, hosts
):
946 Selects the next host according to RFC2782 p.3 based on it's priority.
947 Chooses between hosts with the same priority randomly, where the
948 probability of being selected is proportional to the weight of the host
950 hosts_by_prio
= sorted(hosts
, key
=operator
.itemgetter('prio'))
953 lowest_prio
= hosts_by_prio
[0]['prio']
955 raise ValueError("No hosts to choose from!")
957 hosts_lowest_prio
= [h
for h
in hosts_by_prio
if h
['prio'] == lowest_prio
]
959 if len(hosts_lowest_prio
) == 1:
960 return hosts_lowest_prio
[0]
962 rndint
= random
.randint(0, sum(h
['weight'] for h
in hosts_lowest_prio
))
964 for host
in sorted(hosts_lowest_prio
, key
=operator
.itemgetter(
966 weightsum
+= host
['weight']
967 if weightsum
>= rndint
:
970 def connect(self
, data
= None):
972 Start a connection to the Jabber server
974 Returns connection, and connection type ('tls', 'ssl', 'plain', '') data
975 MUST contain hostname, usessl, proxy, use_custom_host, custom_host (if
976 use_custom_host), custom_port (if use_custom_host)
979 return self
.connection
, ''
982 hostname
= data
['hostname']
983 self
.try_connecting_for_foo_secs
= 45
986 use_custom
= data
['use_custom_host']
988 custom_h
= data
['custom_host']
989 custom_p
= data
['custom_port']
991 hostname
= gajim
.config
.get_per('accounts', self
.name
, 'hostname')
992 usessl
= gajim
.config
.get_per('accounts', self
.name
, 'usessl')
993 self
.try_connecting_for_foo_secs
= gajim
.config
.get_per('accounts',
994 self
.name
, 'try_connecting_for_foo_secs')
995 p
= gajim
.config
.get_per('accounts', self
.name
, 'proxy')
996 use_srv
= gajim
.config
.get_per('accounts', self
.name
, 'use_srv')
997 use_custom
= gajim
.config
.get_per('accounts', self
.name
,
999 custom_h
= gajim
.config
.get_per('accounts', self
.name
, 'custom_host')
1000 custom_p
= gajim
.config
.get_per('accounts', self
.name
, 'custom_port')
1002 # create connection if it doesn't already exist
1004 if p
and p
in gajim
.config
.get_per('proxies'):
1006 proxyptr
= gajim
.config
.get_per('proxies', p
)
1007 for key
in proxyptr
.keys():
1008 proxy
[key
] = proxyptr
[key
][1]
1010 elif gajim
.config
.get_per('accounts', self
.name
, 'use_env_http_proxy'):
1013 env_http_proxy
= os
.environ
['HTTP_PROXY']
1015 env_http_proxy
= os
.environ
['http_proxy']
1016 env_http_proxy
= env_http_proxy
.strip('"')
1017 # Dispose of the http:// prefix
1018 env_http_proxy
= env_http_proxy
.split('://')
1019 env_http_proxy
= env_http_proxy
[len(env_http_proxy
)-1]
1020 env_http_proxy
= env_http_proxy
.split('@')
1022 if len(env_http_proxy
) == 2:
1023 login
= env_http_proxy
[0].split(':')
1024 addr
= env_http_proxy
[1].split(':')
1027 addr
= env_http_proxy
[0].split(':')
1029 proxy
= {'host': addr
[0], 'type' : u
'http', 'user':login
[0]}
1032 proxy
['port'] = addr
[1]
1034 proxy
['port'] = 3128
1037 proxy
['pass'] = login
[1]
1038 proxy
['useauth'] = True
1049 # use_srv = False # wants ssl? disable srv lookup
1058 self
._hosts
= [ {'host': h
, 'port': p
, 'ssl_port': ssl_p
, 'prio': 10,
1060 self
._hostname
= hostname
1062 # add request for srv query to the resolve, on result '_on_resolve'
1064 gajim
.resolver
.resolve('_xmpp-client._tcp.' + helpers
.idn_to_ascii(h
),
1067 self
._on
_resolve
('', [])
1069 def _on_resolve(self
, host
, result_array
):
1070 # SRV query returned at least one valid result, we put it in hosts dict
1071 if len(result_array
) != 0:
1072 self
._hosts
= [i
for i
in result_array
]
1075 if gajim
.config
.get_per('accounts', self
.name
, 'use_custom_host'):
1076 ssl_p
= gajim
.config
.get_per('accounts', self
.name
, 'custom_port')
1077 for i
in self
._hosts
:
1078 i
['ssl_port'] = ssl_p
1079 self
._connect
_to
_next
_host
()
1082 def _connect_to_next_host(self
, retry
=False):
1083 log
.debug('Connection to next host')
1084 if len(self
._hosts
):
1085 # No config option exist when creating a new account
1086 if self
.last_connection_type
:
1087 self
._connection
_types
= [self
.last_connection_type
]
1088 elif self
.name
in gajim
.config
.get_per('accounts'):
1089 self
._connection
_types
= gajim
.config
.get_per('accounts', self
.name
,
1090 'connection_types').split()
1092 self
._connection
_types
= ['tls', 'ssl', 'plain']
1094 if self
._proxy
and self
._proxy
['type']=='bosh':
1095 # with BOSH, we can't do TLS negotiation with <starttls>, we do only "plain"
1096 # connection and TLS with handshake right after TCP connecting ("ssl")
1097 scheme
= common
.xmpp
.transports_nb
.urisplit(self
._proxy
['bosh_uri'])[0]
1099 self
._connection
_types
= ['ssl']
1101 self
._connection
_types
= ['plain']
1103 host
= self
._select
_next
_host
(self
._hosts
)
1104 self
._current
_host
= host
1105 self
._hosts
.remove(host
)
1106 self
.connect_to_next_type()
1109 if not retry
and self
.retrycount
== 0:
1110 log
.debug("Out of hosts, giving up connecting to %s", self
.name
)
1111 self
.time_to_reconnect
= None
1112 if self
.on_connect_failure
:
1113 self
.on_connect_failure()
1114 self
.on_connect_failure
= None
1116 # shown error dialog
1117 self
._connection
_lost
()
1119 # try reconnect if connection has failed before auth to server
1120 self
._disconnectedReconnCB
()
1122 def connect_to_next_type(self
, retry
=False):
1123 if len(self
._connection
_types
):
1124 self
._current
_type
= self
._connection
_types
.pop(0)
1125 if self
.last_connection
:
1126 self
.last_connection
.socket
.disconnect()
1127 self
.last_connection
= None
1128 self
.connection
= None
1130 if self
._current
_type
== 'ssl':
1131 # SSL (force TLS on different port than plain)
1132 # If we do TLS over BOSH, port of XMPP server should be the standard one
1133 # and TLS should be negotiated because TLS on 5223 is deprecated
1134 if self
._proxy
and self
._proxy
['type']=='bosh':
1135 port
= self
._current
_host
['port']
1137 port
= self
._current
_host
['ssl_port']
1138 elif self
._current
_type
== 'tls':
1139 # TLS - negotiate tls after XMPP stream is estabilished
1140 port
= self
._current
_host
['port']
1141 elif self
._current
_type
== 'plain':
1142 # plain connection on defined port
1143 port
= self
._current
_host
['port']
1145 cacerts
= os
.path
.join(common
.gajim
.DATA_DIR
, 'other', 'cacerts.pem')
1146 mycerts
= common
.gajim
.MY_CACERTS
1147 secure_tuple
= (self
._current
_type
, cacerts
, mycerts
)
1149 con
= common
.xmpp
.NonBlockingClient(
1150 domain
=self
._hostname
,
1152 idlequeue
=gajim
.idlequeue
)
1154 self
.last_connection
= con
1155 # increase default timeout for server responses
1156 common
.xmpp
.dispatcher_nb
.DEFAULT_TIMEOUT_SECONDS
= self
.try_connecting_for_foo_secs
1157 # FIXME: this is a hack; need a better way
1158 if self
.on_connect_success
== self
._on
_new
_account
:
1159 con
.RegisterDisconnectHandler(self
._on
_new
_account
)
1161 self
.log_hosttype_info(port
)
1163 hostname
=self
._current
_host
['host'],
1165 on_connect
=self
.on_connect_success
,
1166 on_proxy_failure
=self
.on_proxy_failure
,
1167 on_connect_failure
=self
.connect_to_next_type
,
1169 secure_tuple
= secure_tuple
)
1171 self
._connect
_to
_next
_host
(retry
)
1173 def log_hosttype_info(self
, port
):
1174 msg
= '>>>>>> Connecting to %s [%s:%d], type = %s' % (self
.name
,
1175 self
._current
_host
['host'], port
, self
._current
_type
)
1179 if self
._proxy
['type']=='bosh':
1180 msg
= '%s over BOSH %s' % (msg
, self
._proxy
['bosh_uri'])
1181 if self
._proxy
['type'] in ['http', 'socks5'] or self
._proxy
['bosh_useproxy']:
1182 msg
= '%s over proxy %s:%s' % (msg
, self
._proxy
['host'], self
._proxy
['port'])
1185 def _connect_failure(self
, con_type
=None):
1187 # we are not retrying, and not conecting
1188 if not self
.retrycount
and self
.connected
!= 0:
1189 self
.disconnect(on_purpose
= True)
1190 pritxt
= _('Could not connect to "%s"') % self
._hostname
1191 sectxt
= _('Check your connection or try again later.')
1192 if self
.streamError
:
1194 key
= common
.xmpp
.NS_XMPP_STREAMS
+ ' ' + self
.streamError
1195 if key
in common
.xmpp
.ERRORS
:
1196 sectxt2
= _('Server replied: %s') % common
.xmpp
.ERRORS
[key
][2]
1197 self
.dispatch('ERROR', (pritxt
, '%s\n%s' % (sectxt2
, sectxt
)))
1200 gajim
.nec
.push_incoming_event(ConnectionLostEvent(None,
1201 conn
=self
, title
=pritxt
, msg
=sectxt
))
1203 def on_proxy_failure(self
, reason
):
1204 log
.error('Connection to proxy failed: %s' % reason
)
1205 self
.time_to_reconnect
= None
1206 self
.on_connect_failure
= None
1207 self
.disconnect(on_purpose
= True)
1208 gajim
.nec
.push_incoming_event(ConnectionLostEvent(None, conn
=self
,
1209 title
=_('Connection to proxy failed'), msg
=reason
))
1211 def _connect_success(self
, con
, con_type
):
1212 if not self
.connected
: # We went offline during connecting process
1213 # FIXME - not possible, maybe it was when we used threads
1215 _con_type
= con_type
1216 if _con_type
!= self
._current
_type
:
1217 log
.info('Connecting to next type beacuse desired is %s and returned is %s'
1218 % (self
._current
_type
, _con_type
))
1219 self
.connect_to_next_type()
1221 con
.RegisterDisconnectHandler(self
._on
_disconnected
)
1222 if _con_type
== 'plain' and gajim
.config
.get_per('accounts', self
.name
,
1223 'warn_when_plaintext_connection'):
1224 gajim
.nec
.push_incoming_event(PlainConnectionEvent(None, conn
=self
,
1227 if _con_type
in ('tls', 'ssl') and con
.Connection
.ssl_lib
!= 'PYOPENSSL' \
1228 and gajim
.config
.get_per('accounts', self
.name
,
1229 'warn_when_insecure_ssl_connection') and \
1230 not self
.connection_auto_accepted
:
1231 # Pyopenssl is not used
1232 gajim
.nec
.push_incoming_event(InsecureSSLConnectionEvent(None,
1233 conn
=self
, xmpp_client
=con
, conn_type
=_con_type
))
1235 return self
.connection_accepted(con
, con_type
)
1237 def connection_accepted(self
, con
, con_type
):
1238 if not con
or not con
.Connection
:
1239 self
.disconnect(on_purpose
=True)
1240 gajim
.nec
.push_incoming_event(ConnectionLostEvent(None, conn
=self
,
1241 title
=_('Could not connect to account %s') % self
.name
,
1242 msg
=_('Connection with account %s has been lost. Retry '
1243 'connecting.') % self
.name
))
1246 self
.connection_auto_accepted
= False
1247 self
.connected_hostname
= self
._current
_host
['host']
1248 self
.on_connect_failure
= None
1249 con
.UnregisterDisconnectHandler(self
._on
_disconnected
)
1250 con
.RegisterDisconnectHandler(self
._disconnectedReconnCB
)
1251 log
.debug('Connected to server %s:%s with %s' % (
1252 self
._current
_host
['host'], self
._current
_host
['port'], con_type
))
1254 self
.last_connection_type
= con_type
1255 if gajim
.config
.get_per('accounts', self
.name
, 'anonymous_auth'):
1258 name
= gajim
.config
.get_per('accounts', self
.name
, 'name')
1259 hostname
= gajim
.config
.get_per('accounts', self
.name
, 'hostname')
1260 self
.connection
= con
1262 errnum
= con
.Connection
.ssl_errnum
1263 except AttributeError:
1264 errnum
= -1 # we don't have an errnum
1265 if errnum
> 0 and str(errnum
) not in gajim
.config
.get_per('accounts',
1266 self
.name
, 'ignore_ssl_errors'):
1267 text
= _('The authenticity of the %s certificate could be invalid.') %\
1269 if errnum
in ssl_error
:
1270 text
+= _('\nSSL Error: <b>%s</b>') % ssl_error
[errnum
]
1272 text
+= _('\nUnknown SSL error: %d') % errnum
1273 gajim
.nec
.push_incoming_event(SSLErrorEvent(None, conn
=self
,
1274 error_text
=text
, error_num
=errnum
,
1275 cert
=con
.Connection
.ssl_cert_pem
,
1276 fingerprint
=con
.Connection
.ssl_fingerprint_sha1
,
1277 certificate
=con
.Connection
.ssl_certificate
))
1279 if hasattr(con
.Connection
, 'ssl_fingerprint_sha1'):
1280 saved_fingerprint
= gajim
.config
.get_per('accounts', self
.name
, 'ssl_fingerprint_sha1')
1281 if saved_fingerprint
:
1282 # Check sha1 fingerprint
1283 if con
.Connection
.ssl_fingerprint_sha1
!= saved_fingerprint
:
1284 gajim
.nec
.push_incoming_event(FingerprintErrorEvent(None,
1285 conn
=self
, certificate
=con
.Connection
.ssl_certificate
,
1286 new_fingerprint
=con
.Connection
.ssl_fingerprint_sha1
))
1289 gajim
.config
.set_per('accounts', self
.name
, 'ssl_fingerprint_sha1',
1290 con
.Connection
.ssl_fingerprint_sha1
)
1291 self
._register
_handlers
(con
, con_type
)
1294 password
=self
.password
,
1295 resource
=self
.server_resource
,
1297 on_auth
=self
.__on
_auth
)
1299 def ssl_certificate_accepted(self
):
1300 if not self
.connection
:
1301 self
.disconnect(on_purpose
=True)
1302 gajim
.nec
.push_incoming_event(ConnectionLostEvent(None, conn
=self
,
1303 title
=_('Could not connect to account %s') % self
.name
,
1304 msg
=_('Connection with account %s has been lost. Retry '
1305 'connecting.') % self
.name
))
1307 name
= gajim
.config
.get_per('accounts', self
.name
, 'name')
1308 self
._register
_handlers
(self
.connection
, 'ssl')
1309 self
.connection
.auth(name
, self
.password
, self
.server_resource
, 1,
1312 def _register_handlers(self
, con
, con_type
):
1313 self
.peerhost
= con
.get_peerhost()
1314 gajim
.con_types
[self
.name
] = con_type
1315 # notify the gui about con_type
1316 gajim
.nec
.push_incoming_event(ConnectionTypeEvent(None,
1317 conn
=self
, connection_type
=con_type
))
1318 ConnectionHandlers
._register
_handlers
(self
, con
, con_type
)
1320 def __on_auth(self
, con
, auth
):
1322 self
.disconnect(on_purpose
=True)
1323 gajim
.nec
.push_incoming_event(ConnectionLostEvent(None, conn
=self
,
1324 title
=_('Could not connect to "%s"') % self
._hostname
,
1325 msg
=_('Check your connection or try again later')))
1326 if self
.on_connect_auth
:
1327 self
.on_connect_auth(None)
1328 self
.on_connect_auth
= None
1330 if not self
.connected
: # We went offline during connecting process
1331 if self
.on_connect_auth
:
1332 self
.on_connect_auth(None)
1333 self
.on_connect_auth
= None
1335 if hasattr(con
, 'Resource'):
1336 self
.server_resource
= con
.Resource
1337 if gajim
.config
.get_per('accounts', self
.name
, 'anonymous_auth'):
1338 # Get jid given by server
1339 old_jid
= gajim
.get_jid_from_account(self
.name
)
1340 gajim
.config
.set_per('accounts', self
.name
, 'name', con
.User
)
1341 new_jid
= gajim
.get_jid_from_account(self
.name
)
1342 gajim
.nec
.push_incoming_event(AnonymousAuthEvent(None,
1343 conn
=self
, old_jid
=old_jid
, new_jid
=new_jid
))
1345 self
.last_io
= gajim
.idlequeue
.current_time()
1348 if self
.on_connect_auth
:
1349 self
.on_connect_auth(con
)
1350 self
.on_connect_auth
= None
1352 if not gajim
.config
.get_per('accounts', self
.name
, 'savepass'):
1353 # Forget password, it's wrong
1354 self
.password
= None
1355 gajim
.log
.debug("Couldn't authenticate to %s" % self
._hostname
)
1356 self
.disconnect(on_purpose
= True)
1357 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
1359 self
.dispatch('ERROR', (_('Authentication failed with "%s"') % \
1361 _('Please check your login and password for correctness.')))
1362 if self
.on_connect_auth
:
1363 self
.on_connect_auth(None)
1364 self
.on_connect_auth
= None
1367 def add_lang(self
, stanza
):
1369 stanza
.setAttr('xml:lang', self
.lang
)
1371 def get_privacy_lists(self
):
1372 if not gajim
.account_is_connected(self
.name
):
1374 common
.xmpp
.features_nb
.getPrivacyLists(self
.connection
)
1376 def send_keepalive(self
):
1377 # nothing received for the last foo seconds
1379 self
.connection
.send(' ')
1381 def _on_xmpp_ping_answer(self
, iq_obj
):
1382 id_
= unicode(iq_obj
.getAttr('id'))
1383 assert id_
== self
.awaiting_xmpp_ping_id
1384 self
.awaiting_xmpp_ping_id
= None
1386 def sendPing(self
, pingTo
=None):
1388 Send XMPP Ping (XEP-0199) request. If pingTo is not set, ping is sent to
1389 server to detect connection failure at application level
1391 if not gajim
.account_is_connected(self
.name
):
1393 id_
= self
.connection
.getAnID()
1395 to
= pingTo
.get_full_jid()
1396 gajim
.nec
.push_incoming_event(PingSentEvent(None, conn
=self
,
1399 to
= gajim
.config
.get_per('accounts', self
.name
, 'hostname')
1400 self
.awaiting_xmpp_ping_id
= id_
1401 iq
= common
.xmpp
.Iq('get', to
=to
)
1402 iq
.addChild(name
= 'ping', namespace
= common
.xmpp
.NS_PING
)
1404 def _on_response(resp
):
1405 timePong
= time_time()
1406 if not common
.xmpp
.isResultNode(resp
):
1407 gajim
.nec
.push_incoming_event(PingErrorEvent(None, conn
=self
,
1410 timeDiff
= round(timePong
- timePing
, 2)
1411 gajim
.nec
.push_incoming_event(PingReplyEvent(None, conn
=self
,
1412 contact
=pingTo
, seconds
=timeDiff
))
1414 timePing
= time_time()
1415 self
.connection
.SendAndCallForResponse(iq
, _on_response
)
1417 self
.connection
.SendAndCallForResponse(iq
, self
._on
_xmpp
_ping
_answer
)
1418 gajim
.idlequeue
.set_alarm(self
.check_pingalive
, gajim
.config
.get_per(
1419 'accounts', self
.name
, 'time_for_ping_alive_answer'))
1421 def get_active_default_lists(self
):
1422 if not gajim
.account_is_connected(self
.name
):
1424 common
.xmpp
.features_nb
.getActiveAndDefaultPrivacyLists(self
.connection
)
1426 def del_privacy_list(self
, privacy_list
):
1427 if not gajim
.account_is_connected(self
.name
):
1429 def _on_del_privacy_list_result(result
):
1431 gajim
.nec
.push_incoming_event(PrivacyListRemovedEvent(None,
1432 conn
=self
, list_name
=privacy_list
))
1434 self
.dispatch('ERROR', (_('Error while removing privacy list'),
1435 _('Privacy list %s has not been removed. It is maybe active in '
1436 'one of your connected resources. Deactivate it and try '
1437 'again.') % privacy_list
))
1438 common
.xmpp
.features_nb
.delPrivacyList(self
.connection
, privacy_list
,
1439 _on_del_privacy_list_result
)
1441 def get_privacy_list(self
, title
):
1442 if not gajim
.account_is_connected(self
.name
):
1444 common
.xmpp
.features_nb
.getPrivacyList(self
.connection
, title
)
1446 def set_privacy_list(self
, listname
, tags
):
1447 if not gajim
.account_is_connected(self
.name
):
1449 common
.xmpp
.features_nb
.setPrivacyList(self
.connection
, listname
, tags
)
1451 def set_active_list(self
, listname
):
1452 if not gajim
.account_is_connected(self
.name
):
1454 common
.xmpp
.features_nb
.setActivePrivacyList(self
.connection
, listname
, 'active')
1456 def set_default_list(self
, listname
):
1457 if not gajim
.account_is_connected(self
.name
):
1459 common
.xmpp
.features_nb
.setDefaultPrivacyList(self
.connection
, listname
)
1461 def build_privacy_rule(self
, name
, action
, order
=1):
1463 Build a Privacy rule stanza for invisibility
1465 iq
= common
.xmpp
.Iq('set', common
.xmpp
.NS_PRIVACY
, xmlns
= '')
1466 l
= iq
.getTag('query').setTag('list', {'name': name
})
1467 i
= l
.setTag('item', {'action': action
, 'order': str(order
)})
1468 i
.setTag('presence-out')
1471 def build_invisible_rule(self
):
1472 iq
= common
.xmpp
.Iq('set', common
.xmpp
.NS_PRIVACY
, xmlns
= '')
1473 l
= iq
.getTag('query').setTag('list', {'name': 'invisible'})
1474 if self
.name
in gajim
.interface
.status_sent_to_groups
and \
1475 len(gajim
.interface
.status_sent_to_groups
[self
.name
]) > 0:
1476 for group
in gajim
.interface
.status_sent_to_groups
[self
.name
]:
1477 i
= l
.setTag('item', {'type': 'group', 'value': group
,
1478 'action': 'allow', 'order': '1'})
1479 i
.setTag('presence-out')
1480 if self
.name
in gajim
.interface
.status_sent_to_users
and \
1481 len(gajim
.interface
.status_sent_to_users
[self
.name
]) > 0:
1482 for jid
in gajim
.interface
.status_sent_to_users
[self
.name
]:
1483 i
= l
.setTag('item', {'type': 'jid', 'value': jid
,
1484 'action': 'allow', 'order': '2'})
1485 i
.setTag('presence-out')
1486 i
= l
.setTag('item', {'action': 'deny', 'order': '3'})
1487 i
.setTag('presence-out')
1490 def set_invisible_rule(self
):
1491 if not gajim
.account_is_connected(self
.name
):
1493 iq
= self
.build_invisible_rule()
1494 self
.connection
.send(iq
)
1496 def activate_privacy_rule(self
, name
):
1498 Activate a privacy rule
1500 if not gajim
.account_is_connected(self
.name
):
1502 iq
= common
.xmpp
.Iq('set', common
.xmpp
.NS_PRIVACY
, xmlns
= '')
1503 iq
.getTag('query').setTag('active', {'name': name
})
1504 self
.connection
.send(iq
)
1506 def send_invisible_presence(self
, msg
, signed
, initial
= False):
1507 if not gajim
.account_is_connected(self
.name
):
1509 if not self
.privacy_rules_supported
:
1510 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
1511 show
=gajim
.SHOW_LIST
[self
.connected
]))
1512 self
.dispatch('ERROR', (_('Invisibility not supported'),
1513 _('Account %s doesn\'t support invisibility.') % self
.name
))
1515 # If we are already connected, and privacy rules are supported, send
1516 # offline presence first as it's required by XEP-0126
1517 if self
.connected
> 1 and self
.privacy_rules_supported
:
1518 self
.on_purpose
= True
1519 p
= common
.xmpp
.Presence(typ
= 'unavailable')
1520 p
= self
.add_sha(p
, False)
1523 self
.remove_all_transfers()
1524 self
.connection
.send(p
)
1526 # try to set the privacy rule
1527 iq
= self
.build_invisible_rule()
1528 self
.connection
.SendAndCallForResponse(iq
, self
._continue
_invisible
,
1529 {'msg': msg
, 'signed': signed
, 'initial': initial
})
1531 def _continue_invisible(self
, con
, iq_obj
, msg
, signed
, initial
):
1532 if iq_obj
.getType() == 'error': # server doesn't support privacy lists
1534 # active the privacy rule
1535 self
.privacy_rules_supported
= True
1536 self
.activate_privacy_rule('invisible')
1537 self
.connected
= gajim
.SHOW_LIST
.index('invisible')
1539 priority
= unicode(gajim
.get_priority(self
.name
, 'invisible'))
1540 p
= common
.xmpp
.Presence(priority
= priority
)
1541 p
= self
.add_sha(p
, True)
1545 p
.setTag(common
.xmpp
.NS_SIGNED
+ ' x').setData(signed
)
1546 self
.connection
.send(p
)
1547 self
.priority
= priority
1548 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
1552 self
.request_vcard(None)
1554 # Get bookmarks from private namespace
1555 self
.get_bookmarks()
1558 self
.get_annotations()
1560 # Inform GUI we just signed in
1561 gajim
.nec
.push_incoming_event(SignedInEvent(None, conn
=self
))
1563 def get_signed_presence(self
, msg
, callback
= None):
1564 if gajim
.config
.get_per('accounts', self
.name
, 'gpg_sign_presence'):
1565 return self
.get_signed_msg(msg
, callback
)
1568 def connect_and_auth(self
):
1569 self
.on_connect_success
= self
._connect
_success
1570 self
.on_connect_failure
= self
._connect
_failure
1573 def connect_and_init(self
, show
, msg
, sign_msg
):
1574 self
.continue_connect_info
= [show
, msg
, sign_msg
]
1575 self
.on_connect_auth
= self
._discover
_server
_at
_connection
1576 self
.connect_and_auth()
1578 def _discover_server_at_connection(self
, con
):
1579 self
.connection
= con
1580 if not gajim
.account_is_connected(self
.name
):
1582 self
.connection
.set_send_timeout(self
.keepalives
, self
.send_keepalive
)
1583 self
.connection
.set_send_timeout2(self
.pingalives
, self
.sendPing
)
1584 self
.connection
.onreceive(None)
1586 self
.request_message_archiving_preferences()
1588 self
.privacy_rules_requested
= False
1589 self
.discoverInfo(gajim
.config
.get_per('accounts', self
.name
, 'hostname'),
1592 # Discover Stun server(s)
1593 gajim
.resolver
.resolve('_stun._udp.' + helpers
.idn_to_ascii(
1594 self
.connected_hostname
), self
._on
_stun
_resolved
)
1596 def _on_stun_resolved(self
, host
, result_array
):
1597 if len(result_array
) != 0:
1598 self
._stun
_servers
= self
._hosts
= [i
for i
in result_array
]
1600 def _request_privacy(self
):
1601 iq
= common
.xmpp
.Iq('get', common
.xmpp
.NS_PRIVACY
, xmlns
= '')
1602 id_
= self
.connection
.getAnID()
1604 self
.awaiting_answers
[id_
] = (PRIVACY_ARRIVED
, )
1605 self
.connection
.send(iq
)
1607 def _nec_agent_info_error_received(self
, obj
):
1608 if obj
.conn
.name
!= self
.name
:
1610 if obj
.id_
[:6] == 'Gajim_':
1611 if not self
.privacy_rules_requested
:
1612 self
.privacy_rules_requested
= True
1613 self
._request
_privacy
()
1615 def _nec_agent_info_received(self
, obj
):
1616 if obj
.conn
.name
!= self
.name
:
1620 for identity
in obj
.identities
:
1621 if 'category' in identity
and identity
['category'] in ('gateway',
1622 'headline') and 'type' in identity
:
1623 transport_type
= identity
['type']
1624 if 'category' in identity
and identity
['category'] == 'conference' \
1625 and 'type' in identity
and identity
['type'] == 'text':
1628 if transport_type
and obj
.fjid
not in gajim
.transport_type
:
1629 gajim
.transport_type
[obj
.fjid
] = transport_type
1630 gajim
.logger
.save_transport_type(obj
.fjid
, transport_type
)
1632 if obj
.id_
[:6] == 'Gajim_':
1633 hostname
= gajim
.config
.get_per('accounts', self
.name
, 'hostname')
1634 our_jid
= gajim
.get_jid_from_account(self
.name
)
1635 if obj
.fjid
== hostname
:
1636 if common
.xmpp
.NS_GMAILNOTIFY
in obj
.features
:
1637 gajim
.gmail_domains
.append(obj
.fjid
)
1638 self
.request_gmail_notifications()
1639 if common
.xmpp
.NS_SECLABEL
in obj
.features
:
1640 self
.seclabel_supported
= True
1641 for identity
in obj
.identities
:
1642 if identity
['category'] == 'pubsub' and identity
.get(
1644 self
.pep_supported
= True
1646 if common
.xmpp
.NS_VCARD
in obj
.features
:
1647 self
.vcard_supported
= True
1648 if common
.xmpp
.NS_PUBSUB
in obj
.features
:
1649 self
.pubsub_supported
= True
1650 if common
.xmpp
.NS_PUBSUB_PUBLISH_OPTIONS
in obj
.features
:
1651 self
.pubsub_publish_options_supported
= True
1653 # Remove stored bookmarks accessible to everyone.
1654 self
.send_pb_purge(our_jid
, 'storage:bookmarks')
1655 self
.send_pb_delete(our_jid
, 'storage:bookmarks')
1656 if common
.xmpp
.NS_ARCHIVE
in obj
.features
:
1657 self
.archiving_supported
= True
1658 if common
.xmpp
.NS_ARCHIVE_AUTO
in obj
.features
:
1659 self
.archive_auto_supported
= True
1660 if common
.xmpp
.NS_ARCHIVE_MANAGE
in obj
.features
:
1661 self
.archive_manage_supported
= True
1662 if common
.xmpp
.NS_ARCHIVE_MANUAL
in obj
.features
:
1663 self
.archive_manual_supported
= True
1664 if common
.xmpp
.NS_ARCHIVE_PREF
in obj
.features
:
1665 self
.archive_pref_supported
= True
1666 if common
.xmpp
.NS_BYTESTREAM
in obj
.features
and \
1667 gajim
.config
.get_per('accounts', self
.name
, 'use_ft_proxies'):
1668 our_fjid
= helpers
.parse_jid(our_jid
+ '/' + \
1669 self
.server_resource
)
1670 gajim
.proxy65_manager
.resolve(obj
.fjid
, self
.connection
,
1671 our_fjid
, self
.name
)
1672 if common
.xmpp
.NS_MUC
in obj
.features
and is_muc
:
1673 type_
= transport_type
or 'jabber'
1674 self
.muc_jid
[type_
] = obj
.fjid
1676 if transport_type
in self
.available_transports
:
1677 self
.available_transports
[transport_type
].append(obj
.fjid
)
1679 self
.available_transports
[transport_type
] = [obj
.fjid
]
1680 if not self
.privacy_rules_requested
:
1681 self
.privacy_rules_requested
= True
1682 self
._request
_privacy
()
1684 def send_custom_status(self
, show
, msg
, jid
):
1685 if not show
in gajim
.SHOW_LIST
:
1687 if not gajim
.account_is_connected(self
.name
):
1689 sshow
= helpers
.get_xmpp_show(show
)
1692 if show
== 'offline':
1693 p
= common
.xmpp
.Presence(typ
= 'unavailable', to
= jid
)
1694 p
= self
.add_sha(p
, False)
1698 signed
= self
.get_signed_presence(msg
)
1699 priority
= unicode(gajim
.get_priority(self
.name
, sshow
))
1700 p
= common
.xmpp
.Presence(typ
= None, priority
= priority
, show
= sshow
,
1706 p
.setTag(common
.xmpp
.NS_SIGNED
+ ' x').setData(signed
)
1707 self
.connection
.send(p
)
1709 def _change_to_invisible(self
, msg
):
1710 signed
= self
.get_signed_presence(msg
)
1711 self
.send_invisible_presence(msg
, signed
)
1713 def _change_from_invisible(self
):
1714 if self
.privacy_rules_supported
:
1715 iq
= self
.build_privacy_rule('visible', 'allow')
1716 self
.connection
.send(iq
)
1717 self
.activate_privacy_rule('visible')
1719 def _update_status(self
, show
, msg
):
1720 xmpp_show
= helpers
.get_xmpp_show(show
)
1721 priority
= unicode(gajim
.get_priority(self
.name
, xmpp_show
))
1722 p
= common
.xmpp
.Presence(typ
=None, priority
=priority
, show
=xmpp_show
)
1726 signed
= self
.get_signed_presence(msg
)
1728 p
.setTag(common
.xmpp
.NS_SIGNED
+ ' x').setData(signed
)
1730 self
.connection
.send(p
)
1731 self
.priority
= priority
1732 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
,
1735 def send_motd(self
, jid
, subject
= '', msg
= '', xhtml
= None):
1736 if not gajim
.account_is_connected(self
.name
):
1738 msg_iq
= common
.xmpp
.Message(to
= jid
, body
= msg
, subject
= subject
,
1741 self
.connection
.send(msg_iq
)
1743 def send_message(self
, jid
, msg
, keyID
=None, type_
='chat', subject
='',
1744 chatstate
=None, msg_id
=None, composing_xep
=None, resource
=None,
1745 user_nick
=None, xhtml
=None, label
=None, session
=None, forward_from
=None,
1746 form_node
=None, original_message
=None, delayed
=None, callback
=None,
1747 callback_args
=[], now
=False):
1749 def cb(jid
, msg
, keyID
, forward_from
, session
, original_message
,
1750 subject
, type_
, msg_iq
):
1751 msg_id
= self
.connection
.send(msg_iq
, now
=now
)
1752 jid
= helpers
.parse_jid(jid
)
1753 gajim
.nec
.push_incoming_event(MessageSentEvent(None, conn
=self
,
1754 jid
=jid
, message
=msg
, keyID
=keyID
, chatstate
=chatstate
))
1756 callback(msg_id
, *callback_args
)
1758 self
.log_message(jid
, msg
, forward_from
, session
, original_message
,
1761 self
._prepare
_message
(jid
, msg
, keyID
, type_
=type_
, subject
=subject
,
1762 chatstate
=chatstate
, msg_id
=msg_id
, composing_xep
=composing_xep
,
1763 resource
=resource
, user_nick
=user_nick
, xhtml
=xhtml
, label
=label
,
1764 session
=session
, forward_from
=forward_from
, form_node
=form_node
,
1765 original_message
=original_message
, delayed
=delayed
, callback
=cb
)
1767 def send_contacts(self
, contacts
, jid
):
1769 Send contacts with RosterX (Xep-0144)
1771 if not gajim
.account_is_connected(self
.name
):
1773 if len(contacts
) == 1:
1774 msg
= _('Sent contact: "%s" (%s)') % (contacts
[0].get_full_jid(),
1775 contacts
[0].get_shown_name())
1777 msg
= _('Sent contacts:')
1778 for contact
in contacts
:
1779 msg
+= '\n "%s" (%s)' % (contact
.get_full_jid(),
1780 contact
.get_shown_name())
1781 msg_iq
= common
.xmpp
.Message(to
=jid
, body
=msg
)
1782 x
= msg_iq
.addChild(name
='x', namespace
=common
.xmpp
.NS_ROSTERX
)
1783 for contact
in contacts
:
1784 x
.addChild(name
='item', attrs
={'action': 'add', 'jid': contact
.jid
,
1785 'name': contact
.get_shown_name()})
1786 self
.connection
.send(msg_iq
)
1788 def send_stanza(self
, stanza
):
1790 Send a stanza untouched
1792 if not self
.connection
:
1794 self
.connection
.send(stanza
)
1796 def ack_subscribed(self
, jid
):
1797 if not gajim
.account_is_connected(self
.name
):
1799 log
.debug('ack\'ing subscription complete for %s' % jid
)
1800 p
= common
.xmpp
.Presence(jid
, 'subscribe')
1801 self
.connection
.send(p
)
1803 def ack_unsubscribed(self
, jid
):
1804 if not gajim
.account_is_connected(self
.name
):
1806 log
.debug('ack\'ing unsubscription complete for %s' % jid
)
1807 p
= common
.xmpp
.Presence(jid
, 'unsubscribe')
1808 self
.connection
.send(p
)
1810 def request_subscription(self
, jid
, msg
='', name
='', groups
=[],
1811 auto_auth
=False, user_nick
=''):
1812 if not gajim
.account_is_connected(self
.name
):
1814 log
.debug('subscription request for %s' % jid
)
1816 self
.jids_for_auto_auth
.append(jid
)
1817 # RFC 3921 section 8.2
1818 infos
= {'jid': jid
}
1820 infos
['name'] = name
1821 iq
= common
.xmpp
.Iq('set', common
.xmpp
.NS_ROSTER
)
1822 q
= iq
.getTag('query')
1823 item
= q
.addChild('item', attrs
=infos
)
1825 item
.addChild('group').setData(g
)
1826 self
.connection
.send(iq
)
1828 p
= common
.xmpp
.Presence(jid
, 'subscribe')
1830 p
.setTag('nick', namespace
= common
.xmpp
.NS_NICK
).setData(user_nick
)
1834 self
.connection
.send(p
)
1836 def send_authorization(self
, jid
):
1837 if not gajim
.account_is_connected(self
.name
):
1839 p
= common
.xmpp
.Presence(jid
, 'subscribed')
1841 self
.connection
.send(p
)
1843 def refuse_authorization(self
, jid
):
1844 if not gajim
.account_is_connected(self
.name
):
1846 p
= common
.xmpp
.Presence(jid
, 'unsubscribed')
1848 self
.connection
.send(p
)
1850 def unsubscribe(self
, jid
, remove_auth
= True):
1851 if not gajim
.account_is_connected(self
.name
):
1854 self
.connection
.getRoster().delItem(jid
)
1855 jid_list
= gajim
.config
.get_per('contacts')
1857 if j
.startswith(jid
):
1858 gajim
.config
.del_per('contacts', j
)
1860 self
.connection
.getRoster().Unsubscribe(jid
)
1861 self
.update_contact(jid
, '', [])
1863 def unsubscribe_agent(self
, agent
):
1864 if not gajim
.account_is_connected(self
.name
):
1866 iq
= common
.xmpp
.Iq('set', common
.xmpp
.NS_REGISTER
, to
= agent
)
1867 iq
.getTag('query').setTag('remove')
1868 id_
= self
.connection
.getAnID()
1870 self
.awaiting_answers
[id_
] = (AGENT_REMOVED
, agent
)
1871 self
.connection
.send(iq
)
1872 self
.connection
.getRoster().delItem(agent
)
1874 def send_new_account_infos(self
, form
, is_form
):
1876 # Get username and password and put them in new_account_info
1877 for field
in form
.iter_fields():
1878 if field
.var
== 'username':
1879 self
.new_account_info
['name'] = field
.value
1880 if field
.var
== 'password':
1881 self
.new_account_info
['password'] = field
.value
1883 # Get username and password and put them in new_account_info
1884 if 'username' in form
:
1885 self
.new_account_info
['name'] = form
['username']
1886 if 'password' in form
:
1887 self
.new_account_info
['password'] = form
['password']
1888 self
.new_account_form
= form
1889 self
.new_account(self
.name
, self
.new_account_info
)
1891 def new_account(self
, name
, config
, sync
=False):
1892 # If a connection already exist we cannot create a new account
1895 self
._hostname
= config
['hostname']
1896 self
.new_account_info
= config
1898 self
.on_connect_success
= self
._on
_new
_account
1899 self
.on_connect_failure
= self
._on
_new
_account
1900 self
.connect(config
)
1902 def _on_new_account(self
, con
=None, con_type
=None):
1904 if len(self
._connection
_types
) or len(self
._hosts
):
1905 # There are still other way to try to connect
1907 reason
= _('Could not connect to "%s"') % self
._hostname
1908 gajim
.nec
.push_incoming_event(NewAccountNotConnectedEvent(None,
1909 conn
=self
, reason
=reason
))
1911 self
.on_connect_failure
= None
1912 self
.connection
= con
1913 common
.xmpp
.features_nb
.getRegInfo(con
, self
._hostname
)
1915 def request_os_info(self
, jid
, resource
, groupchat_jid
=None):
1917 groupchat_jid is used when we want to send a request to a real jid and
1918 act as if the answer comes from the groupchat_jid
1920 if not gajim
.account_is_connected(self
.name
):
1922 # If we are invisible, do not request
1923 if self
.connected
== gajim
.SHOW_LIST
.index('invisible'):
1924 self
.dispatch('OS_INFO', (jid
, resource
, _('Not fetched because of invisible status'), _('Not fetched because of invisible status')))
1928 to_whom_jid
+= '/' + resource
1929 iq
= common
.xmpp
.Iq(to
=to_whom_jid
, typ
='get', queryNS
=\
1930 common
.xmpp
.NS_VERSION
)
1931 id_
= self
.connection
.getAnID()
1934 self
.groupchat_jids
[id_
] = groupchat_jid
1935 self
.version_ids
.append(id_
)
1936 self
.connection
.send(iq
)
1938 def request_entity_time(self
, jid
, resource
, groupchat_jid
=None):
1940 groupchat_jid is used when we want to send a request to a real jid and
1941 act as if the answer comes from the groupchat_jid
1943 if not gajim
.account_is_connected(self
.name
):
1945 # If we are invisible, do not request
1946 if self
.connected
== gajim
.SHOW_LIST
.index('invisible'):
1947 self
.dispatch('ENTITY_TIME', (jid
, resource
, _('Not fetched because of invisible status')))
1951 to_whom_jid
+= '/' + resource
1952 iq
= common
.xmpp
.Iq(to
=to_whom_jid
, typ
='get')
1953 iq
.addChild('time', namespace
=common
.xmpp
.NS_TIME_REVISED
)
1954 id_
= self
.connection
.getAnID()
1957 self
.groupchat_jids
[id_
] = groupchat_jid
1958 self
.entity_time_ids
.append(id_
)
1959 self
.connection
.send(iq
)
1961 def request_gateway_prompt(self
, jid
, prompt
=None):
1962 def _on_prompt_result(resp
):
1963 gajim
.nec
.push_incoming_event(GatewayPromptReceivedEvent(None,
1964 conn
=self
, stanza
=resp
))
1969 iq
= common
.xmpp
.Iq(typ
=typ_
, to
=jid
)
1970 query
= iq
.addChild(name
='query', namespace
=common
.xmpp
.NS_GATEWAY
)
1972 query
.setTagData('prompt', prompt
)
1973 self
.connection
.SendAndCallForResponse(iq
, _on_prompt_result
)
1975 def get_settings(self
):
1977 Get Gajim settings as described in XEP 0049
1979 if not gajim
.account_is_connected(self
.name
):
1981 iq
= common
.xmpp
.Iq(typ
='get')
1982 iq2
= iq
.addChild(name
='query', namespace
=common
.xmpp
.NS_PRIVATE
)
1983 iq2
.addChild(name
='gajim', namespace
='gajim:prefs')
1984 self
.connection
.send(iq
)
1986 def seclabel_catalogue(self
, to
, callback
):
1987 if not gajim
.account_is_connected(self
.name
):
1989 self
.seclabel_catalogue_request(to
, callback
)
1990 iq
= common
.xmpp
.Iq(typ
='get')
1991 iq2
= iq
.addChild(name
='catalog', namespace
=common
.xmpp
.NS_SECLABEL_CATALOG
)
1992 iq2
.setAttr('to', to
)
1993 self
.connection
.send(iq
)
1995 def _nec_privacy_list_received(self
, obj
):
1996 if obj
.conn
.name
!= self
.name
:
1998 if obj
.list_name
!= 'block':
2000 self
.blocked_contacts
= []
2001 self
.blocked_groups
= []
2002 self
.blocked_list
= []
2003 self
.blocked_all
= False
2004 for rule
in obj
.rules
:
2005 if rule
['action'] == 'allow':
2006 if not 'type' in rule
:
2007 self
.blocked_all
= False
2008 elif rule
['type'] == 'jid' and rule
['value'] in \
2009 self
.blocked_contacts
:
2010 self
.blocked_contacts
.remove(rule
['value'])
2011 elif rule
['type'] == 'group' and rule
['value'] in \
2012 self
.blocked_groups
:
2013 self
.blocked_groups
.remove(rule
['value'])
2014 elif rule
['action'] == 'deny':
2015 if not 'type' in rule
:
2016 self
.blocked_all
= True
2017 elif rule
['type'] == 'jid' and rule
['value'] not in \
2018 self
.blocked_contacts
:
2019 self
.blocked_contacts
.append(rule
['value'])
2020 elif rule
['type'] == 'group' and rule
['value'] not in \
2021 self
.blocked_groups
:
2022 self
.blocked_groups
.append(rule
['value'])
2023 self
.blocked_list
.append(rule
)
2025 def _request_bookmarks_xml(self
):
2026 if not gajim
.account_is_connected(self
.name
):
2028 iq
= common
.xmpp
.Iq(typ
='get')
2029 iq2
= iq
.addChild(name
='query', namespace
=common
.xmpp
.NS_PRIVATE
)
2030 iq2
.addChild(name
='storage', namespace
='storage:bookmarks')
2031 self
.connection
.send(iq
)
2033 def _check_bookmarks_received(self
):
2034 if not self
.bookmarks
:
2035 self
._request
_bookmarks
_xml
()
2037 def get_bookmarks(self
, storage_type
=None):
2039 Get Bookmarks from storage or PubSub if supported as described in XEP
2042 storage_type can be set to xml to force request to xml storage
2044 if not gajim
.account_is_connected(self
.name
):
2046 if self
.pubsub_supported
and storage_type
!= 'xml':
2047 self
.send_pb_retrieve('', 'storage:bookmarks')
2048 # some server (ejabberd) are so slow to answer that we request via XML
2049 # if we don't get answer in the next 30 seconds
2050 gajim
.idlequeue
.set_alarm(self
._check
_bookmarks
_received
, 30)
2052 self
._request
_bookmarks
_xml
()
2054 def store_bookmarks(self
, storage_type
=None):
2056 Send bookmarks to the storage namespace or PubSub if supported
2058 storage_type can be set to 'pubsub' or 'xml' so store in only one method
2059 else it will be stored on both
2061 if not gajim
.account_is_connected(self
.name
):
2063 iq
= common
.xmpp
.Node(tag
='storage', attrs
={'xmlns': 'storage:bookmarks'})
2064 for bm
in self
.bookmarks
:
2065 iq2
= iq
.addChild(name
= "conference")
2066 iq2
.setAttr('jid', bm
['jid'])
2067 iq2
.setAttr('autojoin', bm
['autojoin'])
2068 iq2
.setAttr('minimize', bm
['minimize'])
2069 iq2
.setAttr('name', bm
['name'])
2070 # Only add optional elements if not empty
2071 # Note: need to handle both None and '' as empty
2072 # thus shouldn't use "is not None"
2073 if bm
.get('nick', None):
2074 iq2
.setTagData('nick', bm
['nick'])
2075 if bm
.get('password', None):
2076 iq2
.setTagData('password', bm
['password'])
2077 if bm
.get('print_status', None):
2078 iq2
.setTagData('print_status', bm
['print_status'])
2080 if self
.pubsub_supported
and self
.pubsub_publish_options_supported
and \
2081 storage_type
!= 'xml':
2082 options
= common
.xmpp
.Node(common
.xmpp
.NS_DATA
+ ' x',
2083 attrs
={'type': 'submit'})
2084 f
= options
.addChild('field', attrs
={'var': 'FORM_TYPE',
2086 f
.setTagData('value', common
.xmpp
.NS_PUBSUB_PUBLISH_OPTIONS
)
2087 f
= options
.addChild('field', attrs
={'var': 'pubsub#persist_items'})
2088 f
.setTagData('value', 'true')
2089 f
= options
.addChild('field', attrs
={'var': 'pubsub#access_model'})
2090 f
.setTagData('value', 'whitelist')
2091 self
.send_pb_publish('', 'storage:bookmarks', iq
, 'current',
2093 if storage_type
!= 'pubsub':
2094 iqA
= common
.xmpp
.Iq(typ
='set')
2095 iqB
= iqA
.addChild(name
='query', namespace
=common
.xmpp
.NS_PRIVATE
)
2096 iqB
.addChild(node
=iq
)
2097 self
.connection
.send(iqA
)
2099 def get_annotations(self
):
2101 Get Annonations from storage as described in XEP 0048, and XEP 0145
2103 self
.annotations
= {}
2104 if not gajim
.account_is_connected(self
.name
):
2106 iq
= common
.xmpp
.Iq(typ
='get')
2107 iq2
= iq
.addChild(name
='query', namespace
=common
.xmpp
.NS_PRIVATE
)
2108 iq2
.addChild(name
='storage', namespace
='storage:rosternotes')
2109 self
.connection
.send(iq
)
2111 def store_annotations(self
):
2113 Set Annonations in private storage as described in XEP 0048, and XEP 0145
2115 if not gajim
.account_is_connected(self
.name
):
2117 iq
= common
.xmpp
.Iq(typ
='set')
2118 iq2
= iq
.addChild(name
='query', namespace
=common
.xmpp
.NS_PRIVATE
)
2119 iq3
= iq2
.addChild(name
='storage', namespace
='storage:rosternotes')
2120 for jid
in self
.annotations
.keys():
2121 if self
.annotations
[jid
]:
2122 iq4
= iq3
.addChild(name
= "note")
2123 iq4
.setAttr('jid', jid
)
2124 iq4
.setData(self
.annotations
[jid
])
2125 self
.connection
.send(iq
)
2127 def get_roster_delimiter(self
):
2129 Get roster group delimiter from storage as described in XEP 0083
2131 if not gajim
.account_is_connected(self
.name
):
2133 iq
= common
.xmpp
.Iq(typ
='get')
2134 iq2
= iq
.addChild(name
='query', namespace
=common
.xmpp
.NS_PRIVATE
)
2135 iq2
.addChild(name
='roster', namespace
='roster:delimiter')
2136 id_
= self
.connection
.getAnID()
2138 self
.awaiting_answers
[id_
] = (DELIMITER_ARRIVED
, )
2139 self
.connection
.send(iq
)
2141 def set_roster_delimiter(self
, delimiter
='::'):
2143 Set roster group delimiter to the storage namespace
2145 if not gajim
.account_is_connected(self
.name
):
2147 iq
= common
.xmpp
.Iq(typ
='set')
2148 iq2
= iq
.addChild(name
='query', namespace
=common
.xmpp
.NS_PRIVATE
)
2149 iq3
= iq2
.addChild(name
='roster', namespace
='roster:delimiter')
2150 iq3
.setData(delimiter
)
2152 self
.connection
.send(iq
)
2154 def get_metacontacts(self
):
2156 Get metacontacts list from storage as described in XEP 0049
2158 if not gajim
.account_is_connected(self
.name
):
2160 iq
= common
.xmpp
.Iq(typ
='get')
2161 iq2
= iq
.addChild(name
='query', namespace
=common
.xmpp
.NS_PRIVATE
)
2162 iq2
.addChild(name
='storage', namespace
='storage:metacontacts')
2163 id_
= self
.connection
.getAnID()
2165 self
.awaiting_answers
[id_
] = (METACONTACTS_ARRIVED
, )
2166 self
.connection
.send(iq
)
2168 def store_metacontacts(self
, tags_list
):
2170 Send meta contacts to the storage namespace
2172 if not gajim
.account_is_connected(self
.name
):
2174 iq
= common
.xmpp
.Iq(typ
='set')
2175 iq2
= iq
.addChild(name
='query', namespace
=common
.xmpp
.NS_PRIVATE
)
2176 iq3
= iq2
.addChild(name
='storage', namespace
='storage:metacontacts')
2177 for tag
in tags_list
:
2178 for data
in tags_list
[tag
]:
2180 dict_
= {'jid': jid
, 'tag': tag
}
2182 dict_
['order'] = data
['order']
2183 iq3
.addChild(name
= 'meta', attrs
= dict_
)
2184 self
.connection
.send(iq
)
2186 def request_roster(self
):
2188 features
= self
.connection
.Dispatcher
.Stream
.features
2189 if features
and features
.getTag('ver',
2190 namespace
=common
.xmpp
.NS_ROSTER_VER
):
2191 version
= gajim
.config
.get_per('accounts', self
.name
,
2193 if version
and not gajim
.contacts
.get_contacts_jid_list(
2195 gajim
.config
.set_per('accounts', self
.name
, 'roster_version',
2199 iq_id
= self
.connection
.initRoster(version
=version
)
2200 self
.awaiting_answers
[iq_id
] = (ROSTER_ARRIVED
, )
2202 def send_agent_status(self
, agent
, ptype
):
2203 if not gajim
.account_is_connected(self
.name
):
2205 show
= helpers
.get_xmpp_show(gajim
.SHOW_LIST
[self
.connected
])
2206 p
= common
.xmpp
.Presence(to
= agent
, typ
= ptype
, show
= show
)
2207 p
= self
.add_sha(p
, ptype
!= 'unavailable')
2208 self
.connection
.send(p
)
2210 def send_captcha(self
, jid
, form_node
):
2211 if not gajim
.account_is_connected(self
.name
):
2213 iq
= common
.xmpp
.Iq(typ
='set', to
=jid
)
2214 captcha
= iq
.addChild(name
='captcha', namespace
=common
.xmpp
.NS_CAPTCHA
)
2215 captcha
.addChild(node
=form_node
)
2216 self
.connection
.send(iq
)
2218 def check_unique_room_id_support(self
, server
, instance
):
2219 if not gajim
.account_is_connected(self
.name
):
2221 iq
= common
.xmpp
.Iq(typ
= 'get', to
= server
)
2222 iq
.setAttr('id', 'unique1')
2223 iq
.addChild('unique', namespace
=common
.xmpp
.NS_MUC_UNIQUE
)
2224 def _on_response(resp
):
2225 if not common
.xmpp
.isResultNode(resp
):
2226 gajim
.nec
.push_incoming_event(UniqueRoomIdNotSupportedEvent(
2227 None, conn
=self
, instance
=instance
, server
=server
))
2229 gajim
.nec
.push_incoming_event(UniqueRoomIdSupportedEvent(None,
2230 conn
=self
, instance
=instance
, server
=server
,
2231 room_id
=resp
.getTag('unique').getData()))
2232 self
.connection
.SendAndCallForResponse(iq
, _on_response
)
2234 def join_gc(self
, nick
, room_jid
, password
, change_nick
=False):
2235 # FIXME: This room JID needs to be normalized; see #1364
2236 if not gajim
.account_is_connected(self
.name
):
2238 show
= helpers
.get_xmpp_show(gajim
.SHOW_LIST
[self
.connected
])
2239 if show
== 'invisible':
2240 # Never join a room when invisible
2243 # last date/time in history to avoid duplicate
2244 if room_jid
not in self
.last_history_time
:
2245 # Not in memory, get it from DB
2247 # Do not check if we are not logging for this room
2248 if gajim
.config
.should_log(self
.name
, room_jid
):
2249 # Check time first in the FAST table
2250 last_log
= gajim
.logger
.get_room_last_message_time(room_jid
)
2251 if last_log
is None:
2252 # Not in special table, get it from messages DB
2253 last_log
= gajim
.logger
.get_last_date_that_has_logs(room_jid
,
2255 # Create self.last_history_time[room_jid] even if not logging,
2256 # could be used in connection_handlers
2257 if last_log
is None:
2259 self
.last_history_time
[room_jid
] = last_log
2261 p
= common
.xmpp
.Presence(to
='%s/%s' % (room_jid
, nick
),
2262 show
=show
, status
=self
.status
)
2263 h
= hmac
.new(self
.secret_hmac
, room_jid
).hexdigest()[:6]
2264 id_
= self
.connection
.getAnID()
2265 id_
= 'gajim_muc_' + id_
+ '_' + h
2267 if gajim
.config
.get('send_sha_in_gc_presence'):
2271 t
= p
.setTag(common
.xmpp
.NS_MUC
+ ' x')
2272 last_date
= self
.last_history_time
[room_jid
]
2274 last_date
= time
.time() - gajim
.config
.get(
2275 'muc_restore_timeout') * 60
2277 last_date
= min(last_date
, time
.time() - gajim
.config
.get(
2278 'muc_restore_timeout') * 60)
2279 last_date
= time
.strftime('%Y-%m-%dT%H:%M:%SZ', time
.gmtime(last_date
))
2280 t
.setTag('history', {'maxstanzas': gajim
.config
.get(
2281 'muc_restore_lines'), 'since': last_date
})
2283 t
.setTagData('password', password
)
2284 self
.connection
.send(p
)
2286 def send_gc_message(self
, jid
, msg
, xhtml
= None, label
= None):
2287 if not gajim
.account_is_connected(self
.name
):
2289 if not xhtml
and gajim
.config
.get('rst_formatting_outgoing_messages'):
2290 from common
.rst_xhtml_generator
import create_xhtml
2291 xhtml
= create_xhtml(msg
)
2292 msg_iq
= common
.xmpp
.Message(jid
, msg
, typ
= 'groupchat', xhtml
= xhtml
)
2293 if label
is not None:
2294 msg_iq
.addChild(node
= label
)
2295 self
.connection
.send(msg_iq
)
2296 gajim
.nec
.push_incoming_event(MessageSentEvent(None, conn
=self
,
2297 jid
=jid
, message
=msg
, keyID
=None))
2299 def send_gc_subject(self
, jid
, subject
):
2300 if not gajim
.account_is_connected(self
.name
):
2302 msg_iq
= common
.xmpp
.Message(jid
, typ
= 'groupchat', subject
= subject
)
2303 self
.connection
.send(msg_iq
)
2305 def request_gc_config(self
, room_jid
):
2306 if not gajim
.account_is_connected(self
.name
):
2308 iq
= common
.xmpp
.Iq(typ
= 'get', queryNS
= common
.xmpp
.NS_MUC_OWNER
,
2311 self
.connection
.send(iq
)
2313 def destroy_gc_room(self
, room_jid
, reason
= '', jid
= ''):
2314 if not gajim
.account_is_connected(self
.name
):
2316 iq
= common
.xmpp
.Iq(typ
= 'set', queryNS
= common
.xmpp
.NS_MUC_OWNER
,
2318 destroy
= iq
.getTag('query').setTag('destroy')
2320 destroy
.setTagData('reason', reason
)
2322 destroy
.setAttr('jid', jid
)
2323 self
.connection
.send(iq
)
2325 def send_gc_status(self
, nick
, jid
, show
, status
):
2326 if not gajim
.account_is_connected(self
.name
):
2328 if show
== 'invisible':
2331 if show
== 'offline':
2332 ptype
= 'unavailable'
2333 xmpp_show
= helpers
.get_xmpp_show(show
)
2334 p
= common
.xmpp
.Presence(to
= '%s/%s' % (jid
, nick
), typ
= ptype
,
2335 show
= xmpp_show
, status
= status
)
2336 h
= hmac
.new(self
.secret_hmac
, jid
).hexdigest()[:6]
2337 id_
= self
.connection
.getAnID()
2338 id_
= 'gajim_muc_' + id_
+ '_' + h
2340 if gajim
.config
.get('send_sha_in_gc_presence') and show
!= 'offline':
2341 p
= self
.add_sha(p
, ptype
!= 'unavailable')
2343 # send instantly so when we go offline, status is sent to gc before we
2344 # disconnect from jabber server
2345 self
.connection
.send(p
)
2347 def gc_got_disconnected(self
, room_jid
):
2349 A groupchat got disconnected. This can be or purpose or not
2351 Save the time we had last message to avoid duplicate logs AND be faster
2352 than get that date from DB. Save time that we have in mem in a small
2353 table (with fast access)
2355 gajim
.logger
.set_room_last_message_time(room_jid
, self
.last_history_time
[room_jid
])
2357 def gc_set_role(self
, room_jid
, nick
, role
, reason
= ''):
2359 Role is for all the life of the room so it's based on nick
2361 if not gajim
.account_is_connected(self
.name
):
2363 iq
= common
.xmpp
.Iq(typ
= 'set', to
= room_jid
, queryNS
=\
2364 common
.xmpp
.NS_MUC_ADMIN
)
2365 item
= iq
.getTag('query').setTag('item')
2366 item
.setAttr('nick', nick
)
2367 item
.setAttr('role', role
)
2369 item
.addChild(name
= 'reason', payload
= reason
)
2370 self
.connection
.send(iq
)
2372 def gc_set_affiliation(self
, room_jid
, jid
, affiliation
, reason
= ''):
2374 Affiliation is for all the life of the room so it's based on jid
2376 if not gajim
.account_is_connected(self
.name
):
2378 iq
= common
.xmpp
.Iq(typ
= 'set', to
= room_jid
, queryNS
=\
2379 common
.xmpp
.NS_MUC_ADMIN
)
2380 item
= iq
.getTag('query').setTag('item')
2381 item
.setAttr('jid', jid
)
2382 item
.setAttr('affiliation', affiliation
)
2384 item
.addChild(name
= 'reason', payload
= reason
)
2385 self
.connection
.send(iq
)
2387 def send_gc_affiliation_list(self
, room_jid
, users_dict
):
2388 if not gajim
.account_is_connected(self
.name
):
2390 iq
= common
.xmpp
.Iq(typ
= 'set', to
= room_jid
, queryNS
= \
2391 common
.xmpp
.NS_MUC_ADMIN
)
2392 item
= iq
.getTag('query')
2393 for jid
in users_dict
:
2394 item_tag
= item
.addChild('item', {'jid': jid
,
2395 'affiliation': users_dict
[jid
]['affiliation']})
2396 if 'reason' in users_dict
[jid
] and users_dict
[jid
]['reason']:
2397 item_tag
.setTagData('reason', users_dict
[jid
]['reason'])
2398 self
.connection
.send(iq
)
2400 def get_affiliation_list(self
, room_jid
, affiliation
):
2401 if not gajim
.account_is_connected(self
.name
):
2403 iq
= common
.xmpp
.Iq(typ
= 'get', to
= room_jid
, queryNS
= \
2404 common
.xmpp
.NS_MUC_ADMIN
)
2405 item
= iq
.getTag('query').setTag('item')
2406 item
.setAttr('affiliation', affiliation
)
2407 self
.connection
.send(iq
)
2409 def send_gc_config(self
, room_jid
, form
):
2410 if not gajim
.account_is_connected(self
.name
):
2412 iq
= common
.xmpp
.Iq(typ
= 'set', to
= room_jid
, queryNS
=\
2413 common
.xmpp
.NS_MUC_OWNER
)
2414 query
= iq
.getTag('query')
2415 form
.setAttr('type', 'submit')
2416 query
.addChild(node
= form
)
2417 self
.connection
.send(iq
)
2419 def change_password(self
, password
):
2420 if not gajim
.account_is_connected(self
.name
):
2422 hostname
= gajim
.config
.get_per('accounts', self
.name
, 'hostname')
2423 username
= gajim
.config
.get_per('accounts', self
.name
, 'name')
2424 iq
= common
.xmpp
.Iq(typ
= 'set', to
= hostname
)
2425 q
= iq
.setTag(common
.xmpp
.NS_REGISTER
+ ' query')
2426 q
.setTagData('username', username
)
2427 q
.setTagData('password', password
)
2428 self
.connection
.send(iq
)
2430 def get_password(self
, callback
, type_
):
2431 self
.pasword_callback
= (callback
, type_
)
2433 self
.set_password(self
.password
)
2435 gajim
.nec
.push_incoming_event(PasswordRequiredEvent(None, conn
=self
))
2437 def set_password(self
, password
):
2438 self
.password
= password
2439 if self
.pasword_callback
:
2440 callback
, type_
= self
.pasword_callback
2441 if self
._current
_type
== 'plain' and type_
== 'PLAIN' and \
2442 gajim
.config
.get_per('accounts', self
.name
,
2443 'warn_when_insecure_password'):
2444 gajim
.nec
.push_incoming_event(InsecurePasswordEvent(None,
2448 self
.pasword_callback
= None
2450 def accept_insecure_password(self
):
2451 if self
.pasword_callback
:
2452 callback
, type_
= self
.pasword_callback
2453 callback(self
.password
)
2454 self
.pasword_callback
= None
2456 def unregister_account(self
, on_remove_success
):
2457 # no need to write this as a class method and keep the value of
2458 # on_remove_success as a class property as pass it as an argument
2459 def _on_unregister_account_connect(con
):
2460 self
.on_connect_auth
= None
2461 if gajim
.account_is_connected(self
.name
):
2462 hostname
= gajim
.config
.get_per('accounts', self
.name
, 'hostname')
2463 iq
= common
.xmpp
.Iq(typ
= 'set', to
= hostname
)
2464 id_
= self
.connection
.getAnID()
2466 iq
.setTag(common
.xmpp
.NS_REGISTER
+ ' query').setTag('remove')
2467 def _on_answer(con
, result
):
2468 if result
.getID() == id_
:
2469 on_remove_success(True)
2471 self
.dispatch('ERROR', (_('Unregister failed'),
2472 _('Unregistration with server %(server)s failed: '
2473 '%(error)s') % {'server': hostname
,
2474 'error': result
.getErrorMsg()}))
2475 on_remove_success(False)
2476 con
.RegisterHandler('iq', _on_answer
, 'result', system
=True)
2477 con
.SendAndWaitForResponse(iq
)
2479 on_remove_success(False)
2480 if self
.connected
== 0:
2481 self
.on_connect_auth
= _on_unregister_account_connect
2482 self
.connect_and_auth()
2484 _on_unregister_account_connect(self
.connection
)
2486 def send_invite(self
, room
, to
, reason
='', continue_tag
=False):
2490 message
=common
.xmpp
.Message(to
= room
)
2491 c
= message
.addChild(name
= 'x', namespace
= common
.xmpp
.NS_MUC_USER
)
2492 c
= c
.addChild(name
= 'invite', attrs
={'to' : to
})
2494 c
.addChild(name
= 'continue')
2496 c
.setTagData('reason', reason
)
2497 self
.connection
.send(message
)
2499 def request_voice(self
, room
, nick
):
2501 Request voice in a moderated room
2503 message
= common
.xmpp
.Message(to
=room
)
2505 x
= xmpp
.DataForm(typ
='submit')
2506 x
.addChild(node
=xmpp
.DataField(name
='FORM_TYPE',
2507 value
=common
.xmpp
.NS_MUC
+ '#request'))
2508 x
.addChild(node
=xmpp
.DataField(name
='muc#role', value
='participant',
2511 message
.addChild(node
=x
)
2513 self
.connection
.send(message
)
2515 def check_pingalive(self
):
2516 if self
.awaiting_xmpp_ping_id
:
2517 # We haven't got the pong in time, disco and reconnect
2518 log
.warn("No reply received for keepalive ping. Reconnecting.")
2519 self
._disconnectedReconnCB
()
2521 def _reconnect_alarm(self
):
2522 if not gajim
.config
.get_per('accounts', self
.name
, 'active'):
2523 # Account may have been disabled
2525 if self
.time_to_reconnect
:
2526 if self
.connected
< 2:
2529 self
.time_to_reconnect
= None
2531 def request_search_fields(self
, jid
):
2532 iq
= common
.xmpp
.Iq(typ
= 'get', to
= jid
, queryNS
= \
2533 common
.xmpp
.NS_SEARCH
)
2534 self
.connection
.send(iq
)
2536 def send_search_form(self
, jid
, form
, is_form
):
2537 iq
= common
.xmpp
.Iq(typ
= 'set', to
= jid
, queryNS
= \
2538 common
.xmpp
.NS_SEARCH
)
2539 item
= iq
.getTag('query')
2541 item
.addChild(node
=form
)
2543 for i
in form
.keys():
2544 item
.setTagData(i
, form
[i
])
2545 def _on_response(resp
):
2546 gajim
.nec
.push_incoming_event(SearchResultReceivedEvent(None,
2547 conn
=self
, stanza
=resp
))
2549 self
.connection
.SendAndCallForResponse(iq
, _on_response
)
2551 def load_roster_from_db(self
):
2552 gajim
.nec
.push_incoming_event(RosterReceivedEvent(None, conn
=self
))