Newer
Older
## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com>
##
## 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 os

Yann Leboulanger
committed
import random
import socket
try:
randomsource = random.SystemRandom()
except:
randomsource = random.Random()
randomsource.seed()

Yann Leboulanger
committed
import signal
if os.name != 'nt':
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
from common import gajim
from common import GnuPG
from common import passwords
from connection_handlers import *
USE_GPG = GnuPG.USE_GPG
from common.rst_xhtml_generator import create_xhtml
from string import Template
log = logging.getLogger('gajim.c.connection')
import gtkgui_helpers

Yann Leboulanger
committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
ssl_error = {
2: "Unable to get issuer certificate",
3: "Unable to get certificate CRL",
4: "Unable to decrypt certificate's signature",
5: "Unable to decrypt CRL's signature",
6: "Unable to decode issuer public key",
7: "Certificate signature failure",
8: "CRL signature failure",
9: "Certificate is not yet valid",
10: "Certificate has expired",
11: "CRL is not yet valid",
12: "CRL has expired",
13: "Format error in certificate's notBefore field",
14: "Format error in certificate's notAfter field",
15: "Format error in CRL's lastUpdate field",
16: "Format error in CRL's nextUpdate field",
17: "Out of memory",
18: "Self signed certificate in certificate chain",
19: "Unable to get local issuer certificate",
20: "Unable to verify the first certificate",
21: "Unable to verify the first certificate",
22: "Certificate chain too long",
23: "Certificate revoked",
24: "Invalid CA certificate",
25: "Path length constraint exceeded",
26: "Unsupported certificate purpose",
27: "Certificate not trusted",
28: "Certificate rejected",
29: "Subject issuer mismatch",
30: "Authority and subject key identifier mismatch",
31: "Authority and issuer serial number mismatch",
32: "Key usage does not include certificate signing",
50: "Application verification failure"
}
class Connection(ConnectionHandlers):

Yann Leboulanger
committed
def __init__(self, name):
# self.connected:
# 0=>offline,
# 1=>connection in progress,
# 2=>authorised
self.connected = 0
self.connection = None # xmpppy ClientCommon instance
# this property is used to prevent double connections
self.last_connection = None # last ClientCommon instance
self.priority = gajim.get_priority(name, 'offline')
# 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
self.last_time_to_reconnect = None

nkour
committed
self.bookmarks = []
self.annotations = {}
self.on_purpose = False
self.last_io = gajim.idlequeue.current_time()
self.last_sent = []
self.last_history_line = {}
self.password = passwords.get_password(name)
self.server_resource = gajim.config.get_per('accounts', name, 'resource')
# All valid resource substitution strings should be added to this hash.
if self.server_resource:
self.server_resource = Template(self.server_resource).safe_substitute({
'hostname': socket.gethostname()
})
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

Yann Leboulanger
committed
self.blocked_list = []
self.blocked_contacts = []
self.blocked_groups = []
self.pep_supported = False

Yann Leboulanger
committed
# Do we continue connection when we get roster (send presence,get vcard...)

Yann Leboulanger
committed
if USE_GPG:
self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent'))

Yann Leboulanger
committed
gajim.config.set('usegpg', True)
else:
gajim.config.set('usegpg', False)
self.on_connect_failure = None
self.jids_for_auto_auth = [] # list of jid to auto-authorize

Yann Leboulanger
committed
self.muc_jid = {} # jid of muc server for each transport type

Yann Leboulanger
committed
self.available_transports = {} # list of available transports on this
# server {'icq': ['icq.server.com', 'icq2.server.com'], }

Yann Leboulanger
committed
self.vcard_supported = True

Yann Leboulanger
committed
self.metacontacts_supported = True
if gajim.handlers.has_key(ev[0]):
gajim.handlers[ev[0]](self.name, ev[1])

Yann Leboulanger
committed
def dispatch(self, event, data):
'''always passes account name as first param'''
# Do not try to reco while we are already trying
self.time_to_reconnect = None

Yann Leboulanger
committed
if self.connected < 2: # connection failed
log.debug('reconnect')
self.retrycount += 1
signed = self.get_signed_msg(self.status)
self.on_connect_auth = self._init_roster
self.connect_and_init(self.old_show, self.status, signed)
self.time_to_reconnect = None
self.retrycount = 0
# We are doing disconnect at so many places, better use one function in all
def disconnect(self, on_purpose = False):
self.on_purpose = on_purpose
self.connected = 0
self.time_to_reconnect = None
self.privacy_rules_supported = False
if self.connection:
# make sure previous connection is completely closed
gajim.proxy65_manager.disconnect(self.connection)
self.connection.disconnect()
log.debug('disconnectedReconnCB')
# we cannot change our status to offline or connecting
# after we auth to server
self.old_show = STATUS_LIST[self.connected]
self.connected = 0
self.dispatch('STATUS', 'offline')
if not self.on_purpose:
if gajim.config.get_per('accounts', self.name, 'autoreconnect'):

Yann Leboulanger
committed
if gajim.status_before_autoaway[self.name]:
# We were auto away. So go back online
self.status = gajim.status_before_autoaway[self.name]
gajim.status_before_autoaway[self.name] = ''
self.old_show = 'online'
# this check has moved from _reconnect method
# do exponential backoff until 15 minutes,
# then small linear increase
if self.retrycount < 2 or self.last_time_to_reconnect is None:
self.last_time_to_reconnect = 5
if self.last_time_to_reconnect < 800:
self.last_time_to_reconnect *= 1.5
self.last_time_to_reconnect += randomsource.randint(0, 5)
self.time_to_reconnect = int(self.last_time_to_reconnect)
log.info("Reconnect to %s in %ss", self.name, self.time_to_reconnect)

Yann Leboulanger
committed
gajim.idlequeue.set_alarm(self._reconnect_alarm,
self.time_to_reconnect)
elif self.on_connect_failure:
self.on_connect_failure()

Yann Leboulanger
committed
# show error dialog
self.on_purpose = False
# END disconenctedReconnCB
self.disconnect(on_purpose = False)
self.dispatch('STATUS', 'offline')

Yann Leboulanger
committed
self.dispatch('CONNECTION_LOST',
(_('Connection with account "%s" has been lost') % self.name,
_('Reconnect manually.')))
if realm == common.xmpp.NS_REGISTER:
if event == common.xmpp.features_nb.REGISTER_DATA_RECEIVED:

Yann Leboulanger
committed
# data is (agent, DataFrom, is_form, error_msg)
self.new_account_info['hostname'] == data[0]:

Yann Leboulanger
committed
if not data[1]: # wrong answer
self.dispatch('ACC_NOT_OK', (

Yann Leboulanger
committed
_('Server %s answered wrongly to register request: %s')\

Yann Leboulanger
committed
% (data[0], data[3])))

Yann Leboulanger
committed
return

Yann Leboulanger
committed
is_form = data[2]

Yann Leboulanger
committed
self.dispatch('NEW_ACC_CONNECTED', (conf, is_form))

Yann Leboulanger
committed
return

Yann Leboulanger
committed
if not data[1]: # wrong answer
self.dispatch('ERROR', (_('Invalid answer'),

Yann Leboulanger
committed
_('Transport %s answered wrongly to register request: %s') % \
(data[0], data[3])))

Yann Leboulanger
committed
return
self.dispatch('REGISTER_AGENT_INFO', (data[0], conf, is_form))
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
elif realm == common.xmpp.NS_PRIVACY:
if event == common.xmpp.features_nb.PRIVACY_LISTS_RECEIVED:
# data is (list)
self.dispatch('PRIVACY_LISTS_RECEIVED', (data))
elif event == common.xmpp.features_nb.PRIVACY_LIST_RECEIVED:
# data is (resp)
if not data:
return
rules = []
name = data.getTag('query').getTag('list').getAttr('name')
for child in data.getTag('query').getTag('list').getChildren():
dict_item = child.getAttrs()
childs = []
if dict_item.has_key('type'):
for scnd_child in child.getChildren():
childs += [scnd_child.getName()]
rules.append({'action':dict_item['action'],
'type':dict_item['type'], 'order':dict_item['order'],
'value':dict_item['value'], 'child':childs})
else:
for scnd_child in child.getChildren():
childs.append(scnd_child.getName())
rules.append({'action':dict_item['action'],
'order':dict_item['order'], 'child':childs})
self.dispatch('PRIVACY_LIST_RECEIVED', (name, rules))
elif event == common.xmpp.features_nb.PRIVACY_LISTS_ACTIVE_DEFAULT:
# data is (dict)
self.dispatch('PRIVACY_LISTS_ACTIVE_DEFAULT', (data))
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))

Yann Leboulanger
committed
def select_next_host(self, hosts):
'''Chooses best 'real' host basing on the SRV priority and weight data;
more info in RFC2782'''

Yann Leboulanger
committed
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

Yann Leboulanger
committed
def connect(self, data = None):
''' Start a connection to the Jabber server.

Yann Leboulanger
committed
Returns connection, and connection type ('tls', 'ssl', 'tcp', '')
data MUST contain hostname, usessl, proxy, use_custom_host,
custom_host (if use_custom_host), custom_port (if use_custom_host)'''

Yann Leboulanger
committed
if data:
hostname = data['hostname']
usessl = data['usessl']

Yann Leboulanger
committed
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:
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
usessl = gajim.config.get_per('accounts', self.name, 'usessl')
self.try_connecting_for_foo_secs = gajim.config.get_per('accounts',

Yann Leboulanger
committed
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
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')
proxy['type'] = gajim.config.get_per('proxies', p, 'type')
else:
proxy = None

nkour
committed
# autodetect [for SSL in 5223/443 and for TLS if broadcasted]
secur = None
secur = 1 # 1 means force SSL no matter what the port will be
use_srv = False # wants ssl? disable srv lookup

Yann Leboulanger
committed
if use_custom:
h = custom_h
p = custom_p
use_srv = False

Yann Leboulanger
committed
hosts = []
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.' + helpers.idn_to_ascii(h),
self._on_resolve)
def _on_resolve(self, host, result_array):
# SRV query returned at least one valid result, we put it in hosts dict
self._hosts = [i for i in result_array]
self.connect_to_next_host()

Yann Leboulanger
committed
def on_proxy_failure(self, reason):
log.debug('Connection to proxy failed')
self.time_to_reconnect = None
self.on_connect_failure = None
self.disconnect(on_purpose = True)
self.dispatch('STATUS', 'offline')
self.dispatch('CONNECTION_LOST',
(_('Connection to proxy failed'), reason))
def connect_to_next_host(self, retry = False):
if self.last_connection:
self.last_connection.socket.disconnect()
self.last_connection = None
con = common.xmpp.NonBlockingClient(self._hostname, caller = self,
on_connect = self.on_connect_success,

Yann Leboulanger
committed
on_proxy_failure = self.on_proxy_failure,
on_connect_failure = self.connect_to_next_host)
con = common.xmpp.NonBlockingClient(self._hostname, debug = [], caller = self,
on_connect = self.on_connect_success,

Yann Leboulanger
committed
on_proxy_failure = self.on_proxy_failure,
on_connect_failure = self.connect_to_next_host)
# 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)
# FIXME: this is a hack; need a better way
if self.on_connect_success == self._on_new_account:
con.RegisterDisconnectHandler(self._on_new_account)
log.info("Connecting to %s: [%s:%d]", self.name, host['host'], host['port'])
con.connect((host['host'], host['port']), proxy = self._proxy,
if not retry and self.retrycount == 0:
log.debug("Out of hosts, giving up connecting to %s", self.name)
if self.on_connect_failure:
self.on_connect_failure()
# shown error dialog
self._connection_lost()

nkour
committed
# try reconnect if connection has failed before auth to server
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:
self.disconnect(on_purpose = True)

Yann Leboulanger
committed
self.dispatch('CONNECTION_LOST',
(_('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
# FIXME - not possible, maybe it was when we used threads
return

Yann Leboulanger
committed
if not con_type:
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)
log.debug(_('Connected to server %s:%s with %s') % (self._current_host['host'],
self._current_host['port'], con_type))
self._register_handlers(con, con_type)

Yann Leboulanger
committed
name = gajim.config.get_per('accounts', self.name, 'name')
hostname = gajim.config.get_per('accounts', self.name, 'hostname')

Yann Leboulanger
committed
errnum = con.Connection.ssl_errnum
except AttributeError:

Yann Leboulanger
committed
if errnum > 0:
# FIXME: tell the user that the certificat is untrusted, and ask him what to do
try:
log.warning("The authenticity of the "+hostname+" certificate could be unvalid.\nSSL Error: "+ssl_error[errnum])
except KeyError:
log.warning("Unknown SSL error: %d" % errnum)
con.auth(name, self.password, self.server_resource, 1, self.__on_auth)

Yann Leboulanger
committed
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)
def __on_auth(self, con, auth):
if not con:
self.disconnect(on_purpose = True)

Yann Leboulanger
committed
self.dispatch('STATUS', 'offline')

Yann Leboulanger
committed
self.dispatch('CONNECTION_LOST',
(_('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

Yann Leboulanger
committed
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

Yann Leboulanger
committed
if auth:
self.last_io = gajim.idlequeue.current_time()
self.retrycount = 0
if self.on_connect_auth:
self.on_connect_auth(con)
self.on_connect_auth = None

Yann Leboulanger
committed
# 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.disconnect(on_purpose = True)
self.dispatch('ERROR', (_('Authentication failed with "%s"') % self._hostname,
_('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
if kill_core and gajim.account_is_connected(self.name):
self.disconnect(on_purpose = True)
def get_privacy_lists(self):
if not self.connection:
return
common.xmpp.features_nb.getPrivacyLists(self.connection)

Yann Leboulanger
committed
def sendPing(self, pingTo):
if not self.connection:
return
iq = common.xmpp.Iq('get', to = pingTo.get_full_jid())
iq.addChild(name = 'ping', namespace = common.xmpp.NS_PING)
def _on_response(resp):
timePong = time_time()

Yann Leboulanger
committed
if not common.xmpp.isResultNode(resp):
self.dispatch('PING_ERROR', (pingTo))
return
timeDiff = round(timePong - timePing,2)
self.dispatch('PING_REPLY', (pingTo, timeDiff))
self.dispatch('PING_SENT', (pingTo))
timePing = time_time()

Yann Leboulanger
committed
self.connection.SendAndCallForResponse(iq, _on_response)
def get_active_default_lists(self):
if not self.connection:
return
common.xmpp.features_nb.getActiveAndDefaultPrivacyLists(self.connection)
def del_privacy_list(self, privacy_list):
if not self.connection:
return

Yann Leboulanger
committed
def _on_del_privacy_list_result(result):
if result:
self.dispatch('PRIVACY_LIST_REMOVED', privacy_list)
else:
self.dispatch('ERROR', (_('Error while removing privacy list'),
_('Privacy list %s has not been removed. It is maybe active in '
'one of your connected resources. Deactivate it and try '

Yann Leboulanger
committed
'again.') % privacy_list))
common.xmpp.features_nb.delPrivacyList(self.connection, privacy_list,
_on_del_privacy_list_result)
def get_privacy_list(self, title):
if not self.connection:
return
common.xmpp.features_nb.getPrivacyList(self.connection, title)
def set_privacy_list(self, listname, tags):
if not self.connection:
return
common.xmpp.features_nb.setPrivacyList(self.connection, listname, tags)
def set_active_list(self, listname):
if not self.connection:
return
common.xmpp.features_nb.setActivePrivacyList(self.connection, listname, 'active')
def set_default_list(self, listname):
if not self.connection:
return
common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname)

Yann Leboulanger
committed
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):
if not self.connection:
return

Yann Leboulanger
committed
'''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):

Yann Leboulanger
committed
# 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})

Yann Leboulanger
committed
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')
priority = unicode(gajim.get_priority(self.name, show))
p = common.xmpp.Presence(typ = ptype, priority = priority, show = show)

Yann Leboulanger
committed
if msg:
p.setStatus(msg)
if signed:
p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
self.connection.send(p)
self.priority = priority

Yann Leboulanger
committed
self.dispatch('STATUS', 'invisible')

Yann Leboulanger
committed
if initial:
#ask our VCard
self.request_vcard(None)

Yann Leboulanger
committed
#Get bookmarks from private namespace
self.get_bookmarks()
#Get annotations
self.get_annotations()
#Inform GUI we just signed in

Yann Leboulanger
committed
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'
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:
# 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
def connect_and_init(self, show, msg, signed):

Yann Leboulanger
committed
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)

Yann Leboulanger
committed
iq = common.xmpp.Iq('get', common.xmpp.NS_PRIVACY, xmlns = '')
id = self.connection.getAnID()
iq.setID(id)
self.awaiting_answers[id] = (PRIVACY_ARRIVED, )
self.connection.send(iq)
def send_custom_status(self, show, msg, jid):
if not show in STATUS_LIST:
return -1
if not self.connection:
return
sshow = helpers.get_xmpp_show(show)
if not msg:
msg = ''
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if show == 'offline':

Yann Leboulanger
committed
p = common.xmpp.Presence(typ = 'unavailable', to = jid)
p = self.add_sha(p, False)
if msg:
p.setStatus(msg)
else:
signed = self.get_signed_msg(msg)
priority = unicode(gajim.get_priority(self.name, sshow))
p = common.xmpp.Presence(typ = None, priority = priority, show = sshow,
to = jid)
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)
def change_status(self, show, msg, auto = False):
sshow = helpers.get_xmpp_show(show)
msg = ''
keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if not auto and not show == 'offline':
signed = self.get_signed_msg(msg)
if show != 'offline' and not self.connected:
# set old_show to requested 'show' in case we need to
# recconect before we auth to server
self.old_show = show

Yann Leboulanger
committed
self.server_resource = gajim.config.get_per('accounts', self.name,
'resource')
# All valid resource substitution strings should be added to this hash.
if self.server_resource:
self.server_resource = Template(self.server_resource).\
safe_substitute({
'hostname': socket.gethostname()
})
self.connect_and_init(show, msg, signed)

Yann Leboulanger
committed
elif show == 'offline':
self.on_purpose = True
p = common.xmpp.Presence(typ = 'unavailable')
if msg:
p.setStatus(msg)
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

Yann Leboulanger
committed
was_invisible = self.connected == STATUS_LIST.index('invisible')
self.connected = STATUS_LIST.index(show)
if show == 'invisible':

Yann Leboulanger
committed
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')
priority = unicode(gajim.get_priority(self.name, sshow))
p = common.xmpp.Presence(typ = None, priority = priority, show = sshow)
p = self.add_sha(p)
if msg:
p.setStatus(msg)
if signed:
p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
self.priority = priority
self.dispatch('STATUS', show)
def _on_disconnected(self):
''' called when a disconnect request has completed successfully'''
self.dispatch('STATUS', 'offline')
def get_status(self):
return STATUS_LIST[self.connected]
def send_motd(self, jid, subject = '', msg = '', xhtml = None):
if not self.connection:
return
msg_iq = common.xmpp.Message(to = jid, body = msg, subject = subject,
xhtml = xhtml)

Yann Leboulanger
committed
def send_message(self, jid, msg, keyID, type = 'chat', subject='',
chatstate = None, msg_id = None, composing_xep = None, resource = None,
user_nick = None, xhtml = None, forward_from = None):

Yann Leboulanger
committed
return 1

Yann Leboulanger
committed
if msg and not xhtml and gajim.config.get('rst_formatting_outgoing_messages'):
xhtml = create_xhtml(msg)
if not msg and chatstate is None:

Yann Leboulanger
committed
return 2
fjid = jid
if resource:
fjid += '/' + resource
msgtxt = msg
msgenc = ''
if keyID and USE_GPG:
#encrypt

Yann Leboulanger
committed
msgenc, error = self.gpg.encrypt(msg, [keyID])
if msgenc and not error:
msgtxt = '[This message is encrypted]'
lang = os.getenv('LANG')
if lang is not None and lang != 'en': # we're not english
# one in locale and one en
msgtxt = _('[This message is *encrypted* (See :JEP:`27`]') +\
' ([This message is *encrypted* (See :JEP:`27`])'

Yann Leboulanger
committed
else:
# Encryption failed, do not send message

Yann Leboulanger
committed
self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim))
return 3
if msgtxt and not xhtml and gajim.config.get(
'rst_formatting_outgoing_messages'):
# Generate a XHTML part using reStructured text markup
xhtml = create_xhtml(msgtxt)
if type == 'chat':
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt, typ = type,
xhtml = xhtml)
else:
if subject:
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt,
typ = 'normal', subject = subject, xhtml = xhtml)
else:
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt,
typ = 'normal', xhtml = xhtml)
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
# JEP-0172: user_nickname
if user_nick:
msg_iq.setTag('nick', namespace = common.xmpp.NS_NICK).setData(
user_nick)

Yann Leboulanger
committed
# chatstates - if peer supports jep85 or jep22, send chatstates

nicfit
committed
# please note that the only valid tag inside a message containing a <body>
# tag is the active event
if (composing_xep == 'XEP-0085' or not composing_xep) and \
composing_xep != 'asked_once':
# XEP-0085

Yann Leboulanger
committed
msg_iq.setTag(chatstate, namespace = common.xmpp.NS_CHATSTATES)
if composing_xep in ('XEP-0022', 'asked_once') or not composing_xep:
# XEP-0022
chatstate_node = msg_iq.setTag('x',
namespace = common.xmpp.NS_EVENT)

Yann Leboulanger
committed
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')
if forward_from:
addresses = msg_iq.addChild('addresses',
namespace=common.xmpp.NS_ADDRESS)
addresses.addChild('address', attrs = {'type': 'ofrom',
'jid': forward_from})
if not forward_from:
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')\
.split()
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
log.debug('ack\'ing subscription complete for %s' % jid)
p = common.xmpp.Presence(jid, 'subscribe')
def ack_unsubscribed(self, jid):
if not self.connection:
return
log.debug('ack\'ing unsubscription complete for %s' % jid)
p = common.xmpp.Presence(jid, 'unsubscribe')
def request_subscription(self, jid, msg = '', name = '', groups = [],
auto_auth = False, user_nick = ''):
log.debug('subscription request for %s' % jid)

Yann Leboulanger
committed
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.setTag('nick', namespace = common.xmpp.NS_NICK).setData(user_nick)
p = self.add_sha(p)
if msg:
p.setStatus(msg)
p = common.xmpp.Presence(jid, 'subscribed')
p = self.add_sha(p)
p = common.xmpp.Presence(jid, 'unsubscribed')
p = self.add_sha(p)

Yann Leboulanger
committed
def unsubscribe(self, jid, remove_auth = True):

Yann Leboulanger
committed
if remove_auth:
self.connection.getRoster().delItem(jid)
for j in jid_list:
if j.startswith(jid):
gajim.config.del_per('contacts', j)
else:
self.connection.getRoster().Unsubscribe(jid)

Yann Leboulanger
committed
self.update_contact(jid, '', [])
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')

Yann Leboulanger
committed
id = self.connection.getAnID()
iq.setID(id)
self.awaiting_answers[id] = (AGENT_REMOVED, agent)
self.connection.send(iq)
self.connection.getRoster().delItem(agent)
def update_contact(self, jid, name, groups):
'''update roster item on jabber server'''
self.connection.getRoster().setItem(jid = jid, name = name,

Yann Leboulanger
committed
def send_new_account_infos(self, form, is_form):
def _on_register_result(result):
if not common.xmpp.isResultNode(result):