gajim-plural/src/common/connection.py

1049 lines
34 KiB
Python
Raw Normal View History

## common/connection.py
##
## Contributors for this file:
## - Yann Le Boulanger <asterix@lagaule.org>
## - Nikos Kouremenos <nkour@jabber.org>
## - Dimitur Kirov <dkirov@gmail.com>
## - Travis Shirk <travis@pobox.com>
##
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <nkour@jabber.org>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
import sys
import os
import time
import traceback
import random
random.seed()
import signal
2005-10-05 13:05:51 +02:00
if os.name != 'nt':
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
2005-07-30 12:59:15 +02:00
2005-04-26 20:45:54 +02:00
import common.xmpp
2005-05-27 07:56:17 +02:00
from common import helpers
2005-04-14 09:42:26 +02:00
from common import gajim
from common import GnuPG
from connection_handlers import *
USE_GPG = GnuPG.USE_GPG
from common import i18n
_ = i18n._
class Connection(ConnectionHandlers):
2006-01-19 00:04:31 +01:00
'''Connection class'''
def __init__(self, name):
ConnectionHandlers.__init__(self)
self.name = name
self.connected = 0 # offline
self.connection = None # xmpppy ClientCommon instance
# this property is used to prevent double connections
self.last_connection = None # last ClientCommon instance
self.gpg = None
self.status = ''
2005-09-05 15:28:09 +02:00
self.old_show = ''
# increase/decrease default timeout for server responses
self.try_connecting_for_foo_secs = 45
# holds the actual hostname to which we are connected
self.connected_hostname = None
2005-09-05 15:28:09 +02:00
self.time_to_reconnect = None
self.new_account_info = None
self.bookmarks = []
self.on_purpose = False
self.last_io = gajim.idlequeue.current_time()
self.last_sent = []
self.last_history_line = {}
self.password = gajim.config.get_per('accounts', name, 'password')
2005-09-07 17:32:31 +02:00
self.server_resource = gajim.config.get_per('accounts', name, 'resource')
if gajim.config.get_per('accounts', self.name, 'keep_alives_enabled'):
self.keepalives = gajim.config.get_per('accounts', self.name,'keep_alive_every_foo_secs')
else:
self.keepalives = 0
2005-09-08 10:33:35 +02:00
self.privacy_rules_supported = False
# Do we continue connection when we get roster (send presence,get vcard...)
2005-09-08 10:33:35 +02:00
self.continue_connect_info = None
if USE_GPG:
self.gpg = GnuPG.GnuPG()
gajim.config.set('usegpg', True)
else:
gajim.config.set('usegpg', False)
self.on_connect_success = None
self.on_connect_failure = None
2005-09-05 15:28:09 +02:00
self.retrycount = 0
self.jids_for_auto_auth = [] # list of jid to auto-authorize
# END __init__
def put_event(self, ev):
if gajim.handlers.has_key(ev[0]):
gajim.handlers[ev[0]](self.name, ev[1])
def dispatch(self, event, data):
'''always passes account name as first param'''
self.put_event((event, data))
2005-09-05 15:28:09 +02:00
def _reconnect(self):
2005-09-10 14:05:48 +02:00
# Do not try to reco while we are already trying
self.time_to_reconnect = None
2005-09-05 15:28:09 +02:00
gajim.log.debug('reconnect')
2005-09-10 14:05:48 +02:00
self.retrycount += 1
2005-09-05 15:28:09 +02:00
signed = self.get_signed_msg(self.status)
self.on_connect_auth = self._init_roster
2005-09-05 15:28:09 +02:00
self.connect_and_init(self.old_show, self.status, signed)
2005-09-05 15:28:09 +02:00
if self.connected < 2: #connection failed
if self.retrycount > 10:
self.connected = 0
self.dispatch('STATUS', 'offline')
self.dispatch('ERROR',
2005-09-05 15:28:09 +02:00
(_('Connection with account "%s" has been lost') % self.name,
_('To continue sending and receiving messages, you will need to reconnect.')))
self.retrycount = 0
return
if self.retrycount > 5:
self.time_to_reconnect = 20
2005-09-05 15:28:09 +02:00
else:
self.time_to_reconnect = 10
gajim.idlequeue.set_alarm(self._reconnect_alarm, self.time_to_reconnect)
2005-09-05 15:28:09 +02:00
else:
# reconnect succeeded
2005-09-05 15:28:09 +02:00
self.time_to_reconnect = None
self.retrycount = 0
2005-09-05 15:28:09 +02:00
def _disconnectedReconnCB(self):
2006-01-19 00:04:31 +01:00
'''Called when we are disconnected'''
2005-09-05 15:28:09 +02:00
gajim.log.debug('disconnectedReconnCB')
if not self.connection:
return
self.old_show = STATUS_LIST[self.connected]
self.connected = 0
self.dispatch('STATUS', 'offline')
gajim.proxy65_manager.disconnect(self.connection)
2005-09-05 15:28:09 +02:00
self.connection = None
if not self.on_purpose:
if gajim.config.get_per('accounts', self.name, 'autoreconnect'):
self.connected = 1
self.dispatch('STATUS', 'connecting')
self.time_to_reconnect = 10
gajim.idlequeue.set_alarm(self._reconnect_alarm, 10)
2005-09-05 15:28:09 +02:00
else:
self.dispatch('ERROR',
2005-09-05 15:28:09 +02:00
(_('Connection with account "%s" has been lost') % self.name,
_('To continue sending and receiving messages, you will need to reconnect.')))
self.on_purpose = False
# END disconenctedReconnCB
def _event_dispatcher(self, realm, event, data):
if realm == common.xmpp.NS_REGISTER:
if event == common.xmpp.features_nb.REGISTER_DATA_RECEIVED:
# data is (agent, DataFrom, is_form)
if self.new_account_info and\
self.new_account_info['hostname'] == data[0]:
#it's a new account
if not data[1]: # wrong answer
print self.connection.lastErr
self.dispatch('ACC_NOT_OK', (
_('Transport %s answered wrongly to register request.') % \
data[0]))
return
req = data[1].asDict()
req['username'] = self.new_account_info['name']
req['password'] = self.new_account_info['password']
def _on_register_result(result):
if not common.xmpp.isResultNode(result):
self.dispatch('ACC_NOT_OK', (result.getError()))
return
self.connected = 0
self.password = self.new_account_info['password']
if USE_GPG:
self.gpg = GnuPG.GnuPG()
gajim.config.set('usegpg', True)
else:
gajim.config.set('usegpg', False)
gajim.connections[self.name] = self
self.dispatch('ACC_OK', (self.new_account_info))
self.new_account_info = None
self.connection = None
common.xmpp.features_nb.register(self.connection, data[0],
req, _on_register_result)
return
if not data[1]: # wrong answer
self.dispatch('ERROR', (_('Invalid answer'),
_('Transport %s answered wrongly to register request.') % \
data[0]))
return
is_form = data[2]
if is_form:
conf = self.parse_data_form(data[1])
else:
conf = data[1].asDict()
self.dispatch('REGISTER_AGENT_INFO', (data[0], conf, is_form))
elif realm == '':
if event == common.xmpp.transports.DATA_RECEIVED:
self.dispatch('STANZA_ARRIVED', unicode(data, errors = 'ignore'))
elif event == common.xmpp.transports.DATA_SENT:
self.dispatch('STANZA_SENT', unicode(data))
def select_next_host(self, hosts):
hosts_best_prio = []
best_prio = 65535
sum_weight = 0
for h in hosts:
if h['prio'] < best_prio:
hosts_best_prio = [h]
best_prio = h['prio']
sum_weight = h['weight']
elif h['prio'] == best_prio:
hosts_best_prio.append(h)
sum_weight += h['weight']
if len(hosts_best_prio) == 1:
return hosts_best_prio[0]
r = random.randint(0, sum_weight)
min_w = sum_weight
# We return the one for which has the minimum weight and weight >= r
for h in hosts_best_prio:
if h['weight'] >= r:
if h['weight'] <= min_w:
min_w = h['weight']
return h
def connect(self, data = None):
''' Start a connection to the Jabber server.
Returns connection, and connection type ('tls', 'ssl', 'tcp', '')
data MUST contain name, hostname, resource, usessl, proxy,
use_custom_host, custom_host (if use_custom_host), custom_port (if
2006-01-19 00:04:31 +01:00
use_custom_host), '''
2005-10-04 12:49:58 +02:00
if self.connection:
2005-10-17 13:18:42 +02:00
return self.connection, ''
2005-10-04 12:49:58 +02:00
if data:
name = data['name']
hostname = data['hostname']
resource = data['resource']
usessl = data['usessl']
self.try_connecting_for_foo_secs = 45
p = data['proxy']
use_srv = True
use_custom = data['use_custom_host']
if use_custom:
custom_h = data['custom_host']
custom_p = data['custom_port']
else:
name = gajim.config.get_per('accounts', self.name, 'name')
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
resource = gajim.config.get_per('accounts', self.name, 'resource')
usessl = gajim.config.get_per('accounts', self.name, 'usessl')
self.try_connecting_for_foo_secs = gajim.config.get_per('accounts',
self.name, 'try_connecting_for_foo_secs')
p = gajim.config.get_per('accounts', self.name, 'proxy')
use_srv = gajim.config.get_per('accounts', self.name, 'use_srv')
use_custom = gajim.config.get_per('accounts', self.name,
'use_custom_host')
custom_h = gajim.config.get_per('accounts', self.name, 'custom_host')
custom_p = gajim.config.get_per('accounts', self.name, 'custom_port')
#create connection if it doesn't already exist
self.connected = 1
2005-07-04 09:28:42 +02:00
if p and p in gajim.config.get_per('proxies'):
2005-06-08 17:48:53 +02:00
proxy = {'host': gajim.config.get_per('proxies', p, 'host')}
proxy['port'] = gajim.config.get_per('proxies', p, 'port')
proxy['user'] = gajim.config.get_per('proxies', p, 'user')
proxy['password'] = gajim.config.get_per('proxies', p, 'pass')
else:
proxy = None
2005-06-14 12:26:01 +02:00
h = hostname
p = 5222
2005-08-05 01:29:41 +02:00
# autodetect [for SSL in 5223/443 and for TLS if broadcasted]
secur = None
2005-06-14 12:26:01 +02:00
if usessl:
p = 5223
secur = 1 # 1 means force SSL no matter what the port will be
use_srv = False # wants ssl? disable srv lookup
if use_custom:
h = custom_h
p = custom_p
use_srv = False
hosts = []
# SRV resolver
self._proxy = proxy
self._secure = secur
self._hosts = [ {'host': h, 'port': p, 'prio': 10, 'weight': 10} ]
self._hostname = hostname
if use_srv:
# add request for srv query to the resolve, on result '_on_resolve' will be called
gajim.resolver.resolve('_xmpp-client._tcp.' + h.encode('utf-8'), self._on_resolve)
else:
self._on_resolve('', [])
def _on_resolve(self, host, result_array):
# SRV query returned at least one valid result, we put it in hosts dict
if len(result_array) != 0:
self._hosts = [i for i in result_array]
self.connect_to_next_host()
def connect_to_next_host(self):
if len(self._hosts):
if self.last_connection:
self.last_connection.socket.disconnect()
self.last_connection = None
self.connection = None
if gajim.verbose:
con = common.xmpp.NonBlockingClient(self._hostname, caller = self,
on_connect = self.on_connect_success,
on_connect_failure = self.connect_to_next_host)
else:
con = common.xmpp.NonBlockingClient(self._hostname, debug = [], caller = self,
on_connect = self.on_connect_success,
on_connect_failure = self.connect_to_next_host)
self.last_connection = con
# increase default timeout for server responses
common.xmpp.dispatcher_nb.DEFAULT_TIMEOUT_SECONDS = self.try_connecting_for_foo_secs
con.set_idlequeue(gajim.idlequeue)
host = self.select_next_host(self._hosts)
self._current_host = host
self._hosts.remove(host)
con.connect((host['host'], host['port']), proxy = self._proxy,
secure = self._secure)
return
else:
self.on_connect_failure()
def _connect_failure(self, con_type = None):
if not con_type:
# we are not retrying, and not conecting
if not self.retrycount and self.connected != 0:
2005-09-05 15:28:09 +02:00
self.connected = 0
self.dispatch('STATUS', 'offline')
self.dispatch('ERROR', (_('Could not connect to "%s"') % self._hostname,
_('Check your connection or try again later.')))
def _connect_success(self, con, con_type):
if not self.connected: # We went offline during connecting process
2005-10-17 13:18:42 +02:00
return None, ''
self.hosts = []
if not con_type:
gajim.log.debug('Could not connect to %s:%s' % (self._current_host['host'],
self._current_host['port']))
self.connected_hostname = self._current_host['host']
con.RegisterDisconnectHandler(self._disconnectedReconnCB)
gajim.log.debug(_('Connected to server %s:%s with %s') % (self._current_host['host'],
self._current_host['port'], con_type))
# Ask metacontacts before roster
self.get_metacontacts()
self._register_handlers(con, con_type)
return True
def _register_handlers(self, con, con_type):
self.peerhost = con.get_peerhost()
# notify the gui about con_type
self.dispatch('CON_TYPE', con_type)
ConnectionHandlers._register_handlers(self, con, con_type)
name = gajim.config.get_per('accounts', self.name, 'name')
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
resource = gajim.config.get_per('accounts', self.name, 'resource')
self.connection = con
con.auth(name, self.password, resource, 1, self.__on_auth)
def __on_auth(self, con, auth):
if not con:
self.connected = 0
self.dispatch('STATUS', 'offline')
self.dispatch('ERROR', (_('Could not connect to "%s"') % self._hostname,
_('Check your connection or try again later')))
if self.on_connect_auth:
self.on_connect_auth(None)
self.on_connect_auth = None
return
if not self.connected: # We went offline during connecting process
if self.on_connect_auth:
self.on_connect_auth(None)
self.on_connect_auth = None
return
if hasattr(con, 'Resource'):
self.server_resource = con.Resource
if auth:
self.last_io = gajim.idlequeue.current_time()
self.connected = 2
if self.on_connect_auth:
self.on_connect_auth(con)
self.on_connect_auth = None
else:
# Forget password if needed
if not gajim.config.get_per('accounts', self.name, 'savepass'):
self.password = None
gajim.log.debug("Couldn't authenticate to %s" % self._hostname)
self.connected = 0
self.dispatch('STATUS', 'offline')
self.dispatch('ERROR', (_('Authentication failed with "%s"') % self._hostname,
2005-06-10 01:08:28 +02:00
_('Please check your login and password for correctness.')))
if self.on_connect_auth:
self.on_connect_auth(None)
self.on_connect_auth = None
# END connect
def quit(self, kill_core):
if kill_core:
if self.connected > 1:
self.connected = 0
2005-07-03 14:36:13 +02:00
self.connection.disconnect()
self.time_to_reconnect = None
return
def build_privacy_rule(self, name, action):
'''Build a Privacy rule stanza for invisibility'''
iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
l = iq.getTag('query').setTag('list', {'name': name})
i = l.setTag('item', {'action': action, 'order': '1'})
i.setTag('presence-out')
return iq
def activate_privacy_rule(self, name):
'''activate a privacy rule'''
iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
iq.getTag('query').setTag('active', {'name': name})
self.connection.send(iq)
def send_invisible_presence(self, msg, signed, initial = False):
# try to set the privacy rule
iq = self.build_privacy_rule('invisible', 'deny')
self.connection.SendAndCallForResponse(iq, self._continue_invisible,
{'msg': msg, 'signed': signed, 'initial': initial})
def _continue_invisible(self, con, iq_obj, msg, signed, initial):
ptype = ''
show = ''
# FIXME: JEP 126 need some modifications (see http://lists.jabber.ru/pipermail/ejabberd/2005-July/001252.html). So I disable it for the moment
if 1 or iq_obj.getType() == 'error': #server doesn't support privacy lists
# We use the old way which is not xmpp complient
ptype = 'invisible'
show = 'invisible'
else:
# active the privacy rule
self.privacy_rules_supported = True
self.activate_privacy_rule('invisible')
prio = unicode(gajim.config.get_per('accounts', self.name, 'priority'))
p = common.xmpp.Presence(typ = ptype, priority = prio, show = show)
p = self.add_sha(p)
if msg:
p.setStatus(msg)
if signed:
p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
self.connection.send(p)
self.dispatch('STATUS', 'invisible')
if initial:
#ask our VCard
self.request_vcard(None)
#Get bookmarks from private namespace
self.get_bookmarks()
#Inform GUI we just signed in
2005-11-27 13:42:42 +01:00
self.dispatch('SIGNED_IN', ())
2005-09-05 15:28:09 +02:00
def test_gpg_passphrase(self, password):
self.gpg.passphrase = password
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
signed = self.gpg.sign('test', keyID)
self.gpg.password = None
return signed != 'BAD_PASSPHRASE'
2005-09-05 15:28:09 +02:00
def get_signed_msg(self, msg):
signed = ''
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if keyID and USE_GPG:
use_gpg_agent = gajim.config.get('use_gpg_agent')
if self.connected < 2 and self.gpg.passphrase is None and \
not use_gpg_agent:
2005-09-05 15:28:09 +02:00
# We didn't set a passphrase
self.dispatch('ERROR', (_('OpenPGP passphrase was not given'),
#%s is the account name here
_('You will be connected to %s without OpenPGP.') % self.name))
elif self.gpg.passphrase is not None or use_gpg_agent:
signed = self.gpg.sign(msg, keyID)
if signed == 'BAD_PASSPHRASE':
signed = ''
if self.connected < 2:
self.dispatch('BAD_PASSPHRASE', ())
return signed
def connect_and_auth(self):
self.on_connect_success = self._connect_success
self.on_connect_failure = self._connect_failure
self.connect()
2005-09-05 15:28:09 +02:00
def connect_and_init(self, show, msg, signed):
self.continue_connect_info = [show, msg, signed]
self.on_connect_auth = self._init_roster
self.connect_and_auth()
def _init_roster(self, con):
self.connection = con
if self.connection:
con.set_send_timeout(self.keepalives, self.send_keepalive)
self.connection.onreceive(None)
# Ask metacontacts before roster
self.get_metacontacts()
def change_status(self, show, msg, sync = False, auto = False):
if not show in STATUS_LIST:
return -1
2005-09-05 15:28:09 +02:00
sshow = helpers.get_xmpp_show(show)
if not msg:
msg = ''
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if keyID and USE_GPG and not msg:
lowered_uf_status_msg = helpers.get_uf_show(show).lower()
# do not show I'm invisible!
if lowered_uf_status_msg == _('invisible'):
2005-08-14 16:32:10 +02:00
lowered_uf_status_msg = _('offline')
msg = _("I'm %s") % lowered_uf_status_msg
signed = ''
2005-09-05 15:28:09 +02:00
if not auto and not show == 'offline':
signed = self.get_signed_msg(msg)
self.status = msg
if show != 'offline' and not self.connected:
2005-09-05 15:28:09 +02:00
self.connect_and_init(show, msg, signed)
2005-04-26 20:45:54 +02:00
elif show == 'offline' and self.connected:
self.connected = 0
2005-05-04 21:20:02 +02:00
if self.connection:
self.on_purpose = True
p = common.xmpp.Presence(typ = 'unavailable')
p = self.add_sha(p)
if msg:
p.setStatus(msg)
2005-08-10 17:59:55 +02:00
self.remove_all_transfers()
self.time_to_reconnect = None
self.connection.start_disconnect(p, self._on_disconnected)
else:
self.time_to_reconnect = None
self._on_disconnected()
elif show != 'offline' and self.connected:
# dont'try to connect, when we are in state 'connecting'
if self.connected == 1:
return
was_invisible = self.connected == STATUS_LIST.index('invisible')
self.connected = STATUS_LIST.index(show)
if show == 'invisible':
self.send_invisible_presence(msg, signed)
return
if was_invisible and self.privacy_rules_supported:
iq = self.build_privacy_rule('visible', 'allow')
self.connection.send(iq)
self.activate_privacy_rule('visible')
prio = unicode(gajim.config.get_per('accounts', self.name,
'priority'))
p = common.xmpp.Presence(typ = None, priority = prio, show = sshow)
p = self.add_sha(p)
if msg:
p.setStatus(msg)
if signed:
p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
2005-07-16 19:12:11 +02:00
if self.connection:
2005-07-15 22:04:02 +02:00
self.connection.send(p)
self.dispatch('STATUS', show)
def _on_disconnected(self):
''' called when a disconnect request has completed successfully'''
self.dispatch('STATUS', 'offline')
gajim.proxy65_manager.disconnect(self.connection)
self.connection = None
2005-09-13 20:46:21 +02:00
def get_status(self):
return STATUS_LIST[self.connected]
def send_motd(self, jid, subject = '', msg = ''):
if not self.connection:
return
msg_iq = common.xmpp.Message(to = jid, body = msg, subject = subject)
self.connection.send(msg_iq)
def send_message(self, jid, msg, keyID, type = 'chat', subject='',
2006-03-14 14:10:09 +01:00
chatstate = None, msg_id = None, composing_jep = None, resource = None):
2005-04-14 09:42:26 +02:00
if not self.connection:
return
if not msg and chatstate is None:
2005-06-21 22:04:23 +02:00
return
2006-03-14 14:10:09 +01:00
fjid = jid
if resource:
fjid += '/' + resource
msgtxt = msg
msgenc = ''
if keyID and USE_GPG:
#encrypt
2005-05-27 16:16:34 +02:00
msgenc = self.gpg.encrypt(msg, [keyID])
if msgenc:
msgtxt = '[This message is encrypted]'
lang = os.getenv('LANG')
if lang is not None or lang != 'en': # we're not english
msgtxt = _('[This message is encrypted]') +\
' ([This message is encrypted])' # one in locale and one en
if type == 'chat':
2006-03-14 14:10:09 +01:00
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt, typ = type)
else:
if subject:
2006-03-14 14:10:09 +01:00
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt,
typ = 'normal', subject = subject)
else:
2006-03-14 14:10:09 +01:00
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt,
typ = 'normal')
if msgenc:
2005-04-26 20:45:54 +02:00
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
# chatstates - if peer supports jep85 or jep22, send chatstates
# please note that the only valid tag inside a message containing a <body>
# tag is the active event
if chatstate is not None:
if composing_jep == 'JEP-0085' or not composing_jep:
# JEP-0085
msg_iq.setTag(chatstate, namespace = common.xmpp.NS_CHATSTATES)
if composing_jep == 'JEP-0022' or not composing_jep:
# JEP-0022
chatstate_node = msg_iq.setTag('x', namespace = common.xmpp.NS_EVENT)
if not msgtxt: # when no <body>, add <id>
if not msg_id: # avoid putting 'None' in <id> tag
msg_id = ''
chatstate_node.setTagData('id', msg_id)
# when msgtxt, requests JEP-0022 composing notification
if chatstate is 'composing' or msgtxt:
chatstate_node.addChild(name = 'composing')
self.connection.send(msg_iq)
2005-12-04 23:58:38 +01:00
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')
ji = gajim.get_jid_without_resource(jid)
if self.name not in no_log_for and ji not in no_log_for:
log_msg = msg
if subject:
log_msg = _('Subject: %s\n%s') % (subject, msg)
if log_msg:
if type == 'chat':
kind = 'chat_msg_sent'
else:
kind = 'single_msg_sent'
gajim.logger.write(kind, jid, log_msg)
self.dispatch('MSGSENT', (jid, msg, keyID))
def send_stanza(self, stanza):
''' send a stanza untouched '''
if not self.connection:
return
self.connection.send(stanza)
def ack_subscribed(self, jid):
if not self.connection:
return
gajim.log.debug('ack\'ing subscription complete for %s' % jid)
p = common.xmpp.Presence(jid, 'subscribe')
self.connection.send(p)
def ack_unsubscribed(self, jid):
if not self.connection:
return
gajim.log.debug('ack\'ing unsubscription complete for %s' % jid)
p = common.xmpp.Presence(jid, 'unsubscribe')
self.connection.send(p)
def request_subscription(self, jid, msg, name = '', groups = [],
auto_auth = False):
if not self.connection:
return
gajim.log.debug('subscription request for %s' % jid)
if auto_auth:
self.jids_for_auto_auth.append(jid)
# RFC 3921 section 8.2
infos = {'jid': jid}
if name:
infos['name'] = name
iq = common.xmpp.Iq('set', common.xmpp.NS_ROSTER)
q = iq.getTag('query')
item = q.addChild('item', attrs = infos)
for g in groups:
item.addChild('group').setData(g)
self.connection.send(iq)
p = common.xmpp.Presence(jid, 'subscribe')
p = self.add_sha(p)
if not msg:
msg = _('I would like to add you to my roster.')
p.setStatus(msg)
self.connection.send(p)
2005-04-14 09:42:26 +02:00
def send_authorization(self, jid):
if not self.connection:
return
p = common.xmpp.Presence(jid, 'subscribed')
p = self.add_sha(p)
self.connection.send(p)
2005-04-14 09:42:26 +02:00
def refuse_authorization(self, jid):
if not self.connection:
return
p = common.xmpp.Presence(jid, 'unsubscribed')
p = self.add_sha(p)
self.connection.send(p)
def unsubscribe(self, jid, remove_auth = True):
if not self.connection:
return
if remove_auth:
2005-04-26 20:45:54 +02:00
self.connection.getRoster().delItem(jid)
2005-09-07 17:57:01 +02:00
else:
self.connection.getRoster().Unsubscribe(jid)
self.update_contact(jid, '', [])
def _continue_unsubscribe(self, con, iq_obj, agent):
if iq_obj.getTag('error'):
# error, probably not a real agent
return
self.connection.getRoster().delItem(agent)
2005-05-08 19:33:08 +02:00
def unsubscribe_agent(self, agent):
if not self.connection:
return
iq = common.xmpp.Iq('set', common.xmpp.NS_REGISTER, to = agent)
iq.getTag('query').setTag('remove')
self.connection.SendAndCallForResponse(iq, self._continue_unsubscribe,
2005-05-08 19:33:08 +02:00
{'agent': agent})
return
def update_contact(self, jid, name, groups):
'''update roster item on jabber server'''
if self.connection:
2005-04-30 10:48:50 +02:00
self.connection.getRoster().setItem(jid = jid, name = name,
2005-04-26 20:45:54 +02:00
groups = groups)
def new_account(self, name, config, sync = False):
# If a connection already exist we cannot create a new account
if self.connection:
return
self._hostname = config['hostname']
self.new_account_info = config
self.name = name
self.on_connect_success = self._on_new_account
self.on_connect_failure = self._on_new_account
self.connect(config)
def _on_new_account(self, con = None, con_type = None):
if not con_type:
self.dispatch('ACC_NOT_OK',
(_('Could not connect to "%s"') % self._hostname))
return
self.connection = con
common.xmpp.features_nb.getRegInfo(con, self._hostname)
def account_changed(self, new_name):
self.name = new_name
def request_last_status_time(self, jid, resource):
if not self.connection:
return
to_whom_jid = jid
if resource:
to_whom_jid += '/' + resource
iq = common.xmpp.Iq(to = to_whom_jid, typ = 'get', queryNS =\
common.xmpp.NS_LAST)
self.connection.send(iq)
def request_os_info(self, jid, resource):
if not self.connection:
return
2005-07-03 01:07:26 +02:00
to_whom_jid = jid
if resource:
2005-07-03 01:07:26 +02:00
to_whom_jid += '/' + resource
iq = common.xmpp.Iq(to = to_whom_jid, typ = 'get', queryNS =\
2005-04-26 20:45:54 +02:00
common.xmpp.NS_VERSION)
self.connection.send(iq)
def get_settings(self):
''' Get Gajim settings as described in JEP 0049 '''
if not self.connection:
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq3 = iq2.addChild(name='gajim', namespace='gajim:prefs')
self.connection.send(iq)
def get_bookmarks(self):
'''Get Bookmarks from storage as described in JEP 0048'''
self.bookmarks = [] #avoid multiple bookmarks when re-connecting
if not self.connection:
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2.addChild(name='storage', namespace='storage:bookmarks')
self.connection.send(iq)
def store_bookmarks(self):
''' Send bookmarks to the storage namespace '''
if not self.connection:
return
iq = common.xmpp.Iq(typ='set')
2005-07-04 23:30:00 +02:00
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq3 = iq2.addChild(name='storage', namespace='storage:bookmarks')
for bm in self.bookmarks:
2005-07-04 23:30:00 +02:00
iq4 = iq3.addChild(name = "conference")
iq4.setAttr('jid', bm['jid'])
iq4.setAttr('autojoin', bm['autojoin'])
iq4.setAttr('name', bm['name'])
# Only add optional elements if not empty
# Note: need to handle both None and '' as empty
# thus shouldn't use "is not None"
if bm['nick']:
iq5 = iq4.setTagData('nick', bm['nick'])
if bm['password']:
iq5 = iq4.setTagData('password', bm['password'])
self.connection.send(iq)
def get_metacontacts(self):
'''Get metacontacts list from storage as described in JEP 0049'''
if not self.connection:
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2.addChild(name='storage', namespace='storage:metacontacts')
self.connection.send(iq)
#FIXME: remove the old infos, remove that before 0.10
iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2.addChild(name='gajim', namespace='gajim:metacontacts')
self.connection.send(iq)
def store_metacontacts(self, tags_list):
''' Send meta contacts to the storage namespace '''
if not self.connection:
return
iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq3 = iq2.addChild(name='storage', namespace='storage:metacontacts')
for tag in tags_list:
for data in tags_list[tag]:
jid = data['jid']
dict_ = {'jid': jid, 'tag': tag}
if data.has_key('priority'):
dict_['priority'] = data['priority']
iq3.addChild(name = 'meta', attrs = dict_)
self.connection.send(iq)
2005-04-12 14:23:08 +02:00
def send_agent_status(self, agent, ptype):
if not self.connection:
return
2005-04-26 20:45:54 +02:00
p = common.xmpp.Presence(to = agent, typ = ptype)
p = self.add_sha(p)
self.connection.send(p)
2005-04-18 14:17:43 +02:00
def join_gc(self, nick, room, server, password):
if not self.connection:
return
2005-09-05 15:28:09 +02:00
show = helpers.get_xmpp_show(STATUS_LIST[self.connected])
2006-03-29 16:32:45 +02:00
if show == 'invisible':
# Never join a room when invisible
return
p = common.xmpp.Presence(to = '%s@%s/%s' % (room, server, nick),
show = show, status = self.status)
if gajim.config.get('send_sha_in_gc_presence'):
p = self.add_sha(p)
t = p.setTag(common.xmpp.NS_MUC + ' x')
if password:
t.setTagData('password', password)
self.connection.send(p)
#last date/time in history to avoid duplicate
# FIXME: This JID needs to be normalized; see #1364
jid='%s@%s' % (room, server)
last_log = gajim.logger.get_last_date_that_has_logs(jid, is_room = True)
if last_log is None:
2005-12-24 23:00:28 +01:00
last_log = 0
self.last_history_line[jid]= last_log
def send_gc_message(self, jid, msg):
if not self.connection:
return
2005-04-26 20:45:54 +02:00
msg_iq = common.xmpp.Message(jid, msg, typ = 'groupchat')
self.connection.send(msg_iq)
self.dispatch('MSGSENT', (jid, msg))
def send_gc_subject(self, jid, subject):
if not self.connection:
return
2005-04-26 20:45:54 +02:00
msg_iq = common.xmpp.Message(jid,typ = 'groupchat', subject = subject)
self.connection.send(msg_iq)
def request_gc_config(self, room_jid):
iq = common.xmpp.Iq(typ = 'get', queryNS = common.xmpp.NS_MUC_OWNER,
2005-04-26 20:45:54 +02:00
to = room_jid)
self.connection.send(iq)
def change_gc_nick(self, room_jid, nick):
2005-06-19 00:09:31 +02:00
if not self.connection:
return
p = common.xmpp.Presence(to = '%s/%s' % (room_jid, nick))
p = self.add_sha(p)
self.connection.send(p)
def send_gc_status(self, nick, jid, show, status):
if not self.connection:
return
if show == 'invisible':
show = 'offline'
ptype = None
if show == 'offline':
2005-04-12 14:23:08 +02:00
ptype = 'unavailable'
2005-09-05 15:28:09 +02:00
show = helpers.get_xmpp_show(show)
p = common.xmpp.Presence(to = '%s/%s' % (jid, nick), typ = ptype,
show = show, status = status)
if gajim.config.get('send_sha_in_gc_presence'):
p = self.add_sha(p)
# send instantly so when we go offline, status is sent to gc before we
# disconnect from jabber server
self.connection.send(p)
def gc_set_role(self, room_jid, nick, role, reason = ''):
'''role is for all the life of the room so it's based on nick'''
if not self.connection:
return
2005-04-26 20:45:54 +02:00
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
common.xmpp.NS_MUC_ADMIN)
item = iq.getTag('query').setTag('item')
item.setAttr('nick', nick)
item.setAttr('role', role)
if reason:
item.addChild(name = 'reason', payload = reason)
self.connection.send(iq)
def gc_set_affiliation(self, room_jid, jid, affiliation, reason = ''):
'''affiliation is for all the life of the room so it's based on jid'''
if not self.connection:
return
2005-04-26 20:45:54 +02:00
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
common.xmpp.NS_MUC_ADMIN)
item = iq.getTag('query').setTag('item')
item.setAttr('jid', jid)
item.setAttr('affiliation', affiliation)
if reason:
item.addChild(name = 'reason', payload = reason)
self.connection.send(iq)
def send_gc_affiliation_list(self, room_jid, list):
if not self.connection:
return
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS = \
common.xmpp.NS_MUC_ADMIN)
item = iq.getTag('query')
for jid in list:
item_tag = item.addChild('item', {'jid': jid,
'affiliation': list[jid]['affiliation']})
if list[jid].has_key('reason') and list[jid]['reason']:
item_tag.setTagData('reason', list[jid]['reason'])
self.connection.send(iq)
def get_affiliation_list(self, room_jid, affiliation):
if not self.connection:
return
iq = common.xmpp.Iq(typ = 'get', to = room_jid, queryNS = \
common.xmpp.NS_MUC_ADMIN)
item = iq.getTag('query').setTag('item')
item.setAttr('affiliation', affiliation)
self.connection.send(iq)
def send_gc_config(self, room_jid, config):
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
common.xmpp.NS_MUC_OWNER)
query = iq.getTag('query')
self.build_data_from_dict(query, config)
self.connection.send(iq)
def gpg_passphrase(self, passphrase):
if USE_GPG:
use_gpg_agent = gajim.config.get('use_gpg_agent')
if use_gpg_agent:
self.gpg.passphrase = None
else:
self.gpg.passphrase = passphrase
def ask_gpg_keys(self):
if USE_GPG:
keys = self.gpg.get_keys()
return keys
return None
def ask_gpg_secrete_keys(self):
if USE_GPG:
keys = self.gpg.get_secret_keys()
return keys
return None
def change_password(self, password):
if not self.connection:
return
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
username = gajim.config.get_per('accounts', self.name, 'name')
2005-04-26 20:45:54 +02:00
iq = common.xmpp.Iq(typ = 'set', to = hostname)
q = iq.setTag(common.xmpp.NS_REGISTER + ' query')
q.setTagData('username',username)
q.setTagData('password',password)
self.connection.send(iq)
def unregister_account(self, on_remove_success):
# no need to write this as a class method and keep the value of on_remove_success
# as a class property as pass it as an argument
def _on_unregister_account_connect(con):
self.on_connect_auth = None
if self.connected > 1:
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
iq = common.xmpp.Iq(typ = 'set', to = hostname)
q = iq.setTag(common.xmpp.NS_REGISTER + ' query').setTag('remove')
con.send(iq)
on_remove_success(True)
return
on_remove_success(False)
2005-04-20 14:23:41 +02:00
if self.connected == 0:
self.on_connect_auth = _on_unregister_account_connect
self.connect_and_auth()
else:
_on_unregister_account_connect(self.connection)
def send_invite(self, room, to, reason=''):
'''sends invitation'''
message=common.xmpp.Message(to = room)
c = message.addChild(name = 'x', namespace = common.xmpp.NS_MUC_USER)
c = c.addChild(name = 'invite', attrs={'to' : to})
if reason != '':
c.setTagData('reason', reason)
self.connection.send(message)
def send_keepalive(self):
# nothing received for the last foo seconds (60 secs by default)
if self.connection:
self.connection.send(' ')
def _reconnect_alarm(self):
2005-09-05 15:28:09 +02:00
if self.time_to_reconnect:
if self.connected < 2:
self._reconnect()
2005-09-05 15:28:09 +02:00
else:
self.time_to_reconnect = None
# END Connection