| 1 | ## common/connection.py |
|---|
| 2 | ## |
|---|
| 3 | ## |
|---|
| 4 | ## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org> |
|---|
| 5 | ## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org> |
|---|
| 6 | ## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com> |
|---|
| 7 | ## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com> |
|---|
| 8 | ## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com> |
|---|
| 9 | ## |
|---|
| 10 | ## This program is free software; you can redistribute it and/or modify |
|---|
| 11 | ## it under the terms of the GNU General Public License as published |
|---|
| 12 | ## by the Free Software Foundation; version 2 only. |
|---|
| 13 | ## |
|---|
| 14 | ## This program is distributed in the hope that it will be useful, |
|---|
| 15 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 16 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 17 | ## GNU General Public License for more details. |
|---|
| 18 | ## |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | import os |
|---|
| 22 | import random |
|---|
| 23 | |
|---|
| 24 | try: |
|---|
| 25 | randomsource = random.SystemRandom() |
|---|
| 26 | except: |
|---|
| 27 | randomsource = random.Random() |
|---|
| 28 | randomsource.seed() |
|---|
| 29 | |
|---|
| 30 | import signal |
|---|
| 31 | if os.name != 'nt': |
|---|
| 32 | signal.signal(signal.SIGPIPE, signal.SIG_DFL) |
|---|
| 33 | |
|---|
| 34 | import common.xmpp |
|---|
| 35 | from common import helpers |
|---|
| 36 | from common import gajim |
|---|
| 37 | from common import GnuPG |
|---|
| 38 | from common import passwords |
|---|
| 39 | from common import exceptions |
|---|
| 40 | |
|---|
| 41 | from connection_handlers import * |
|---|
| 42 | USE_GPG = GnuPG.USE_GPG |
|---|
| 43 | |
|---|
| 44 | from common.rst_xhtml_generator import create_xhtml |
|---|
| 45 | |
|---|
| 46 | class Connection(ConnectionHandlers): |
|---|
| 47 | '''Connection class''' |
|---|
| 48 | def __init__(self, name): |
|---|
| 49 | ConnectionHandlers.__init__(self) |
|---|
| 50 | self.name = name |
|---|
| 51 | self.connected = 0 # offline |
|---|
| 52 | self.connection = None # xmpppy ClientCommon instance |
|---|
| 53 | # this property is used to prevent double connections |
|---|
| 54 | self.last_connection = None # last ClientCommon instance |
|---|
| 55 | self.is_zeroconf = False |
|---|
| 56 | self.gpg = None |
|---|
| 57 | self.status = '' |
|---|
| 58 | self.priority = gajim.get_priority(name, 'offline') |
|---|
| 59 | self.old_show = '' |
|---|
| 60 | # increase/decrease default timeout for server responses |
|---|
| 61 | self.try_connecting_for_foo_secs = 45 |
|---|
| 62 | # holds the actual hostname to which we are connected |
|---|
| 63 | self.connected_hostname = None |
|---|
| 64 | self.time_to_reconnect = None |
|---|
| 65 | self.last_time_to_reconnect = None |
|---|
| 66 | self.new_account_info = None |
|---|
| 67 | self.bookmarks = [] |
|---|
| 68 | self.annotations = {} |
|---|
| 69 | self.on_purpose = False |
|---|
| 70 | self.last_io = gajim.idlequeue.current_time() |
|---|
| 71 | self.last_sent = [] |
|---|
| 72 | self.last_history_line = {} |
|---|
| 73 | self.password = passwords.get_password(name) |
|---|
| 74 | self.server_resource = gajim.config.get_per('accounts', name, 'resource') |
|---|
| 75 | if gajim.config.get_per('accounts', self.name, 'keep_alives_enabled'): |
|---|
| 76 | self.keepalives = gajim.config.get_per('accounts', self.name,'keep_alive_every_foo_secs') |
|---|
| 77 | else: |
|---|
| 78 | self.keepalives = 0 |
|---|
| 79 | self.privacy_rules_supported = False |
|---|
| 80 | # Do we continue connection when we get roster (send presence,get vcard...) |
|---|
| 81 | self.continue_connect_info = None |
|---|
| 82 | # To know the groupchat jid associated with a sranza ID. Useful to |
|---|
| 83 | # request vcard or os info... to a real JID but act as if it comes from |
|---|
| 84 | # the fake jid |
|---|
| 85 | self.groupchat_jids = {} # {ID : groupchat_jid} |
|---|
| 86 | if USE_GPG: |
|---|
| 87 | self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent')) |
|---|
| 88 | gajim.config.set('usegpg', True) |
|---|
| 89 | else: |
|---|
| 90 | gajim.config.set('usegpg', False) |
|---|
| 91 | |
|---|
| 92 | self.on_connect_success = None |
|---|
| 93 | self.on_connect_failure = None |
|---|
| 94 | self.retrycount = 0 |
|---|
| 95 | self.jids_for_auto_auth = [] # list of jid to auto-authorize |
|---|
| 96 | self.muc_jid = {} # jid of muc server for each transport type |
|---|
| 97 | self.available_transports = {} # list of available transports on this |
|---|
| 98 | # server {'icq': ['icq.server.com', 'icq2.server.com'], } |
|---|
| 99 | self.vcard_supported = True |
|---|
| 100 | self.private_storage_supported = True |
|---|
| 101 | # END __init__ |
|---|
| 102 | |
|---|
| 103 | def put_event(self, ev): |
|---|
| 104 | if gajim.handlers.has_key(ev[0]): |
|---|
| 105 | gajim.handlers[ev[0]](self.name, ev[1]) |
|---|
| 106 | |
|---|
| 107 | def dispatch(self, event, data): |
|---|
| 108 | '''always passes account name as first param''' |
|---|
| 109 | self.put_event((event, data)) |
|---|
| 110 | |
|---|
| 111 | |
|---|
| 112 | def _reconnect(self): |
|---|
| 113 | # Do not try to reco while we are already trying |
|---|
| 114 | self.time_to_reconnect = None |
|---|
| 115 | if self.connected < 2: # connection failed |
|---|
| 116 | gajim.log.debug('reconnect') |
|---|
| 117 | self.retrycount += 1 |
|---|
| 118 | signed = self.get_signed_msg(self.status) |
|---|
| 119 | self.on_connect_auth = self._init_roster |
|---|
| 120 | self.connect_and_init(self.old_show, self.status, signed) |
|---|
| 121 | else: |
|---|
| 122 | # reconnect succeeded |
|---|
| 123 | self.time_to_reconnect = None |
|---|
| 124 | self.retrycount = 0 |
|---|
| 125 | |
|---|
| 126 | # We are doing disconnect at so many places, better use one function in all |
|---|
| 127 | def disconnect(self, on_purpose = False): |
|---|
| 128 | self.on_purpose = on_purpose |
|---|
| 129 | self.connected = 0 |
|---|
| 130 | self.time_to_reconnect = None |
|---|
| 131 | self.privacy_rules_supported = False |
|---|
| 132 | if self.connection: |
|---|
| 133 | # make sure previous connection is completely closed |
|---|
| 134 | gajim.proxy65_manager.disconnect(self.connection) |
|---|
| 135 | self.connection.disconnect() |
|---|
| 136 | self.last_connection = None |
|---|
| 137 | self.connection = None |
|---|
| 138 | |
|---|
| 139 | def _disconnectedReconnCB(self): |
|---|
| 140 | '''Called when we are disconnected''' |
|---|
| 141 | gajim.log.debug('disconnectedReconnCB') |
|---|
| 142 | if gajim.account_is_connected(self.name): |
|---|
| 143 | # we cannot change our status to offline or connecting |
|---|
| 144 | # after we auth to server |
|---|
| 145 | self.old_show = STATUS_LIST[self.connected] |
|---|
| 146 | self.connected = 0 |
|---|
| 147 | self.dispatch('STATUS', 'offline') |
|---|
| 148 | if not self.on_purpose: |
|---|
| 149 | self.disconnect() |
|---|
| 150 | if gajim.config.get_per('accounts', self.name, 'autoreconnect'): |
|---|
| 151 | self.connected = 1 |
|---|
| 152 | self.dispatch('STATUS', 'connecting') |
|---|
| 153 | if gajim.status_before_autoaway[self.name]: |
|---|
| 154 | # We were auto away. So go back online |
|---|
| 155 | self.status = gajim.status_before_autoaway[self.name] |
|---|
| 156 | gajim.status_before_autoaway[self.name] = '' |
|---|
| 157 | self.old_show = 'online' |
|---|
| 158 | # this check has moved from _reconnect method |
|---|
| 159 | # do exponential backoff until 15 minutes, |
|---|
| 160 | # then small linear increase |
|---|
| 161 | if self.retrycount < 2 or self.last_time_to_reconnect is None: |
|---|
| 162 | self.last_time_to_reconnect = 5 |
|---|
| 163 | if self.last_time_to_reconnect < 800: |
|---|
| 164 | self.last_time_to_reconnect *= 1.5 |
|---|
| 165 | self.last_time_to_reconnect += randomsource.randint(0, 5) |
|---|
| 166 | self.time_to_reconnect = int(self.last_time_to_reconnect) |
|---|
| 167 | gajim.log.debug("Reconnect to %s in %ss", self.name, self.time_to_reconnect) |
|---|
| 168 | gajim.idlequeue.set_alarm(self._reconnect_alarm, |
|---|
| 169 | self.time_to_reconnect) |
|---|
| 170 | elif self.on_connect_failure: |
|---|
| 171 | self.on_connect_failure() |
|---|
| 172 | self.on_connect_failure = None |
|---|
| 173 | else: |
|---|
| 174 | # show error dialog |
|---|
| 175 | self._connection_lost() |
|---|
| 176 | else: |
|---|
| 177 | self.disconnect() |
|---|
| 178 | self.on_purpose = False |
|---|
| 179 | # END disconenctedReconnCB |
|---|
| 180 | |
|---|
| 181 | def _connection_lost(self): |
|---|
| 182 | self.disconnect(on_purpose = False) |
|---|
| 183 | self.dispatch('STATUS', 'offline') |
|---|
| 184 | self.dispatch('CONNECTION_LOST', |
|---|
| 185 | (_('Connection with account "%s" has been lost') % self.name, |
|---|
| 186 | _('Reconnect manually.'))) |
|---|
| 187 | |
|---|
| 188 | def _event_dispatcher(self, realm, event, data): |
|---|
| 189 | if realm == common.xmpp.NS_REGISTER: |
|---|
| 190 | if event == common.xmpp.features_nb.REGISTER_DATA_RECEIVED: |
|---|
| 191 | # data is (agent, DataFrom, is_form, error_msg) |
|---|
| 192 | if self.new_account_info and \ |
|---|
| 193 | self.new_account_info['hostname'] == data[0]: |
|---|
| 194 | # it's a new account |
|---|
| 195 | if not data[1]: # wrong answer |
|---|
| 196 | self.dispatch('ACC_NOT_OK', ( |
|---|
| 197 | _('Transport %s answered wrongly to register request: %s')\ |
|---|
| 198 | % (data[0], data[3]))) |
|---|
| 199 | return |
|---|
| 200 | req = data[1] |
|---|
| 201 | req['username'] = self.new_account_info['name'] |
|---|
| 202 | req['password'] = self.new_account_info['password'] |
|---|
| 203 | def _on_register_result(result): |
|---|
| 204 | if not common.xmpp.isResultNode(result): |
|---|
| 205 | self.dispatch('ACC_NOT_OK', (result.getError())) |
|---|
| 206 | return |
|---|
| 207 | self.password = self.new_account_info['password'] |
|---|
| 208 | if USE_GPG: |
|---|
| 209 | self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent')) |
|---|
| 210 | gajim.config.set('usegpg', True) |
|---|
| 211 | else: |
|---|
| 212 | gajim.config.set('usegpg', False) |
|---|
| 213 | gajim.connections[self.name] = self |
|---|
| 214 | self.dispatch('ACC_OK', (self.new_account_info)) |
|---|
| 215 | self.new_account_info = None |
|---|
| 216 | if self.connection: |
|---|
| 217 | self.connection.UnregisterDisconnectHandler(self._on_new_account) |
|---|
| 218 | self.disconnect(on_purpose=True) |
|---|
| 219 | common.xmpp.features_nb.register(self.connection, data[0], |
|---|
| 220 | req, _on_register_result) |
|---|
| 221 | return |
|---|
| 222 | if not data[1]: # wrong answer |
|---|
| 223 | self.dispatch('ERROR', (_('Invalid answer'), |
|---|
| 224 | _('Transport %s answered wrongly to register request: %s') % \ |
|---|
| 225 | (data[0], data[3]))) |
|---|
| 226 | return |
|---|
| 227 | is_form = data[2] |
|---|
| 228 | conf = data[1] |
|---|
| 229 | self.dispatch('REGISTER_AGENT_INFO', (data[0], conf, is_form)) |
|---|
| 230 | elif realm == common.xmpp.NS_PRIVACY: |
|---|
| 231 | if event == common.xmpp.features_nb.PRIVACY_LISTS_RECEIVED: |
|---|
| 232 | # data is (list) |
|---|
| 233 | self.dispatch('PRIVACY_LISTS_RECEIVED', (data)) |
|---|
| 234 | elif event == common.xmpp.features_nb.PRIVACY_LIST_RECEIVED: |
|---|
| 235 | # data is (resp) |
|---|
| 236 | if not data: |
|---|
| 237 | return |
|---|
| 238 | rules = [] |
|---|
| 239 | name = data.getTag('query').getTag('list').getAttr('name') |
|---|
| 240 | for child in data.getTag('query').getTag('list').getChildren(): |
|---|
| 241 | dict_item = child.getAttrs() |
|---|
| 242 | childs = [] |
|---|
| 243 | if dict_item.has_key('type'): |
|---|
| 244 | for scnd_child in child.getChildren(): |
|---|
| 245 | childs += [scnd_child.getName()] |
|---|
| 246 | rules.append({'action':dict_item['action'], |
|---|
| 247 | 'type':dict_item['type'], 'order':dict_item['order'], |
|---|
| 248 | 'value':dict_item['value'], 'child':childs}) |
|---|
| 249 | else: |
|---|
| 250 | for scnd_child in child.getChildren(): |
|---|
| 251 | childs.append(scnd_child.getName()) |
|---|
| 252 | rules.append({'action':dict_item['action'], |
|---|
| 253 | 'order':dict_item['order'], 'child':childs}) |
|---|
| 254 | self.dispatch('PRIVACY_LIST_RECEIVED', (name, rules)) |
|---|
| 255 | elif event == common.xmpp.features_nb.PRIVACY_LISTS_ACTIVE_DEFAULT: |
|---|
| 256 | # data is (dict) |
|---|
| 257 | self.dispatch('PRIVACY_LISTS_ACTIVE_DEFAULT', (data)) |
|---|
| 258 | elif realm == '': |
|---|
| 259 | if event == common.xmpp.transports.DATA_RECEIVED: |
|---|
| 260 | self.dispatch('STANZA_ARRIVED', unicode(data, errors = 'ignore')) |
|---|
| 261 | elif event == common.xmpp.transports.DATA_SENT: |
|---|
| 262 | self.dispatch('STANZA_SENT', unicode(data)) |
|---|
| 263 | |
|---|
| 264 | def select_next_host(self, hosts): |
|---|
| 265 | hosts_best_prio = [] |
|---|
| 266 | best_prio = 65535 |
|---|
| 267 | sum_weight = 0 |
|---|
| 268 | for h in hosts: |
|---|
| 269 | if h['prio'] < best_prio: |
|---|
| 270 | hosts_best_prio = [h] |
|---|
| 271 | best_prio = h['prio'] |
|---|
| 272 | sum_weight = h['weight'] |
|---|
| 273 | elif h['prio'] == best_prio: |
|---|
| 274 | hosts_best_prio.append(h) |
|---|
| 275 | sum_weight += h['weight'] |
|---|
| 276 | if len(hosts_best_prio) == 1: |
|---|
| 277 | return hosts_best_prio[0] |
|---|
| 278 | r = random.randint(0, sum_weight) |
|---|
| 279 | min_w = sum_weight |
|---|
| 280 | # We return the one for which has the minimum weight and weight >= r |
|---|
| 281 | for h in hosts_best_prio: |
|---|
| 282 | if h['weight'] >= r: |
|---|
| 283 | if h['weight'] <= min_w: |
|---|
| 284 | min_w = h['weight'] |
|---|
| 285 | return h |
|---|
| 286 | |
|---|
| 287 | def connect(self, data = None): |
|---|
| 288 | ''' Start a connection to the Jabber server. |
|---|
| 289 | Returns connection, and connection type ('tls', 'ssl', 'tcp', '') |
|---|
| 290 | data MUST contain hostname, usessl, proxy, use_custom_host, |
|---|
| 291 | custom_host (if use_custom_host), custom_port (if use_custom_host)''' |
|---|
| 292 | if self.connection: |
|---|
| 293 | return self.connection, '' |
|---|
| 294 | |
|---|
| 295 | if data: |
|---|
| 296 | hostname = data['hostname'] |
|---|
| 297 | usessl = data['usessl'] |
|---|
| 298 | self.try_connecting_for_foo_secs = 45 |
|---|
| 299 | p = data['proxy'] |
|---|
| 300 | use_srv = True |
|---|
| 301 | use_custom = data['use_custom_host'] |
|---|
| 302 | if use_custom: |
|---|
| 303 | custom_h = data['custom_host'] |
|---|
| 304 | custom_p = data['custom_port'] |
|---|
| 305 | else: |
|---|
| 306 | hostname = gajim.config.get_per('accounts', self.name, 'hostname') |
|---|
| 307 | usessl = gajim.config.get_per('accounts', self.name, 'usessl') |
|---|
| 308 | self.try_connecting_for_foo_secs = gajim.config.get_per('accounts', |
|---|
| 309 | self.name, 'try_connecting_for_foo_secs') |
|---|
| 310 | p = gajim.config.get_per('accounts', self.name, 'proxy') |
|---|
| 311 | use_srv = gajim.config.get_per('accounts', self.name, 'use_srv') |
|---|
| 312 | use_custom = gajim.config.get_per('accounts', self.name, |
|---|
| 313 | 'use_custom_host') |
|---|
| 314 | custom_h = gajim.config.get_per('accounts', self.name, 'custom_host') |
|---|
| 315 | custom_p = gajim.config.get_per('accounts', self.name, 'custom_port') |
|---|
| 316 | |
|---|
| 317 | # create connection if it doesn't already exist |
|---|
| 318 | self.connected = 1 |
|---|
| 319 | if p and p in gajim.config.get_per('proxies'): |
|---|
| 320 | proxy = {'host': gajim.config.get_per('proxies', p, 'host')} |
|---|
| 321 | proxy['port'] = gajim.config.get_per('proxies', p, 'port') |
|---|
| 322 | proxy['user'] = gajim.config.get_per('proxies', p, 'user') |
|---|
| 323 | proxy['password'] = gajim.config.get_per('proxies', p, 'pass') |
|---|
| 324 | else: |
|---|
| 325 | proxy = None |
|---|
| 326 | |
|---|
| 327 | h = hostname |
|---|
| 328 | p = 5222 |
|---|
| 329 | # autodetect [for SSL in 5223/443 and for TLS if broadcasted] |
|---|
| 330 | secur = None |
|---|
| 331 | if usessl: |
|---|
| 332 | p = 5223 |
|---|
| 333 | secur = 1 # 1 means force SSL no matter what the port will be |
|---|
| 334 | use_srv = False # wants ssl? disable srv lookup |
|---|
| 335 | if use_custom: |
|---|
| 336 | h = custom_h |
|---|
| 337 | p = custom_p |
|---|
| 338 | use_srv = False |
|---|
| 339 | |
|---|
| 340 | hosts = [] |
|---|
| 341 | # SRV resolver |
|---|
| 342 | self._proxy = proxy |
|---|
| 343 | self._secure = secur |
|---|
| 344 | self._hosts = [ {'host': h, 'port': p, 'prio': 10, 'weight': 10} ] |
|---|
| 345 | self._hostname = hostname |
|---|
| 346 | if use_srv: |
|---|
| 347 | # add request for srv query to the resolve, on result '_on_resolve' |
|---|
| 348 | # will be called |
|---|
| 349 | gajim.resolver.resolve('_xmpp-client._tcp.' + helpers.idn_to_ascii(h), |
|---|
| 350 | self._on_resolve) |
|---|
| 351 | else: |
|---|
| 352 | self._on_resolve('', []) |
|---|
| 353 | |
|---|
| 354 | def _on_resolve(self, host, result_array): |
|---|
| 355 | # SRV query returned at least one valid result, we put it in hosts dict |
|---|
| 356 | if len(result_array) != 0: |
|---|
| 357 | self._hosts = [i for i in result_array] |
|---|
| 358 | self.connect_to_next_host() |
|---|
| 359 | |
|---|
| 360 | def connect_to_next_host(self, retry = False): |
|---|
| 361 | if len(self._hosts): |
|---|
| 362 | if self.last_connection: |
|---|
| 363 | self.last_connection.socket.disconnect() |
|---|
| 364 | self.last_connection = None |
|---|
| 365 | self.connection = None |
|---|
| 366 | if gajim.verbose: |
|---|
| 367 | con = common.xmpp.NonBlockingClient(self._hostname, caller = self, |
|---|
| 368 | on_connect = self.on_connect_success, |
|---|
| 369 | on_connect_failure = self.connect_to_next_host) |
|---|
| 370 | else: |
|---|
| 371 | con = common.xmpp.NonBlockingClient(self._hostname, debug = [], caller = self, |
|---|
| 372 | on_connect = self.on_connect_success, |
|---|
| 373 | on_connect_failure = self.connect_to_next_host) |
|---|
| 374 | self.last_connection = con |
|---|
| 375 | # increase default timeout for server responses |
|---|
| 376 | common.xmpp.dispatcher_nb.DEFAULT_TIMEOUT_SECONDS = self.try_connecting_for_foo_secs |
|---|
| 377 | con.set_idlequeue(gajim.idlequeue) |
|---|
| 378 | host = self.select_next_host(self._hosts) |
|---|
| 379 | self._current_host = host |
|---|
| 380 | self._hosts.remove(host) |
|---|
| 381 | |
|---|
| 382 | # FIXME: this is a hack; need a better way |
|---|
| 383 | if self.on_connect_success == self._on_new_account: |
|---|
| 384 | con.RegisterDisconnectHandler(self._on_new_account) |
|---|
| 385 | |
|---|
| 386 | con.connect((host['host'], host['port']), proxy = self._proxy, |
|---|
| 387 | secure = self._secure) |
|---|
| 388 | return |
|---|
| 389 | else: |
|---|
| 390 | if not retry and self.retrycount == 0: |
|---|
| 391 | self.time_to_reconnect = None |
|---|
| 392 | if self.on_connect_failure: |
|---|
| 393 | self.on_connect_failure() |
|---|
| 394 | self.on_connect_failure = None |
|---|
| 395 | else: |
|---|
| 396 | # shown error dialog |
|---|
| 397 | self._connection_lost() |
|---|
| 398 | else: |
|---|
| 399 | # try reconnect if connection has failed before auth to server |
|---|
| 400 | self._disconnectedReconnCB() |
|---|
| 401 | |
|---|
| 402 | def _connect_failure(self, con_type = None): |
|---|
| 403 | if not con_type: |
|---|
| 404 | # we are not retrying, and not conecting |
|---|
| 405 | if not self.retrycount and self.connected != 0: |
|---|
| 406 | self.disconnect(on_purpose = True) |
|---|
| 407 | self.dispatch('STATUS', 'offline') |
|---|
| 408 | self.dispatch('CONNECTION_LOST', |
|---|
| 409 | (_('Could not connect to "%s"') % self._hostname, |
|---|
| 410 | _('Check your connection or try again later.'))) |
|---|
| 411 | |
|---|
| 412 | def _connect_success(self, con, con_type): |
|---|
| 413 | if not self.connected: # We went offline during connecting process |
|---|
| 414 | # FIXME - not possible, maybe it was when we used threads |
|---|
| 415 | return |
|---|
| 416 | self.hosts = [] |
|---|
| 417 | if not con_type: |
|---|
| 418 | gajim.log.debug('Could not connect to %s:%s' % (self._current_host['host'], |
|---|
| 419 | self._current_host['port'])) |
|---|
| 420 | self.connected_hostname = self._current_host['host'] |
|---|
| 421 | self.on_connect_failure = None |
|---|
| 422 | con.RegisterDisconnectHandler(self._disconnectedReconnCB) |
|---|
| 423 | gajim.log.debug(_('Connected to server %s:%s with %s') % (self._current_host['host'], |
|---|
| 424 | self._current_host['port'], con_type)) |
|---|
| 425 | self._register_handlers(con, con_type) |
|---|
| 426 | return True |
|---|
| 427 | |
|---|
| 428 | def _register_handlers(self, con, con_type): |
|---|
| 429 | self.peerhost = con.get_peerhost() |
|---|
| 430 | # notify the gui about con_type |
|---|
| 431 | self.dispatch('CON_TYPE', con_type) |
|---|
| 432 | ConnectionHandlers._register_handlers(self, con, con_type) |
|---|
| 433 | name = gajim.config.get_per('accounts', self.name, 'name') |
|---|
| 434 | hostname = gajim.config.get_per('accounts', self.name, 'hostname') |
|---|
| 435 | self.connection = con |
|---|
| 436 | con.auth(name, self.password, self.server_resource, 1, self.__on_auth) |
|---|
| 437 | |
|---|
| 438 | def __on_auth(self, con, auth): |
|---|
| 439 | if not con: |
|---|
| 440 | self.disconnect(on_purpose = True) |
|---|
| 441 | self.dispatch('STATUS', 'offline') |
|---|
| 442 | self.dispatch('CONNECTION_LOST', |
|---|
| 443 | (_('Could not connect to "%s"') % self._hostname, |
|---|
| 444 | _('Check your connection or try again later'))) |
|---|
| 445 | if self.on_connect_auth: |
|---|
| 446 | self.on_connect_auth(None) |
|---|
| 447 | self.on_connect_auth = None |
|---|
| 448 | return |
|---|
| 449 | if not self.connected: # We went offline during connecting process |
|---|
| 450 | if self.on_connect_auth: |
|---|
| 451 | self.on_connect_auth(None) |
|---|
| 452 | self.on_connect_auth = None |
|---|
| 453 | return |
|---|
| 454 | if hasattr(con, 'Resource'): |
|---|
| 455 | self.server_resource = con.Resource |
|---|
| 456 | if auth: |
|---|
| 457 | self.last_io = gajim.idlequeue.current_time() |
|---|
| 458 | self.connected = 2 |
|---|
| 459 | self.retrycount = 0 |
|---|
| 460 | if self.on_connect_auth: |
|---|
| 461 | self.on_connect_auth(con) |
|---|
| 462 | self.on_connect_auth = None |
|---|
| 463 | else: |
|---|
| 464 | # Forget password, it's wrong |
|---|
| 465 | self.password = None |
|---|
| 466 | gajim.log.debug("Couldn't authenticate to %s" % self._hostname) |
|---|
| 467 | self.disconnect(on_purpose = True) |
|---|
| 468 | self.dispatch('STATUS', 'offline') |
|---|
| 469 | self.dispatch('ERROR', (_('Authentication failed with "%s"') % self._hostname, |
|---|
| 470 | _('Please check your login and password for correctness.'))) |
|---|
| 471 | if self.on_connect_auth: |
|---|
| 472 | self.on_connect_auth(None) |
|---|
| 473 | self.on_connect_auth = None |
|---|
| 474 | # END connect |
|---|
| 475 | |
|---|
| 476 | def quit(self, kill_core): |
|---|
| 477 | if kill_core and gajim.account_is_connected(self.name): |
|---|
| 478 | self.disconnect(on_purpose = True) |
|---|
| 479 | |
|---|
| 480 | def get_privacy_lists(self): |
|---|
| 481 | if not self.connection: |
|---|
| 482 | return |
|---|
| 483 | common.xmpp.features_nb.getPrivacyLists(self.connection) |
|---|
| 484 | |
|---|
| 485 | def get_active_default_lists(self): |
|---|
| 486 | if not self.connection: |
|---|
| 487 | return |
|---|
| 488 | common.xmpp.features_nb.getActiveAndDefaultPrivacyLists(self.connection) |
|---|
| 489 | |
|---|
| 490 | def del_privacy_list(self, privacy_list): |
|---|
| 491 | if not self.connection: |
|---|
| 492 | return |
|---|
| 493 | def _on_del_privacy_list_result(result): |
|---|
| 494 | if result: |
|---|
| 495 | self.dispatch('PRIVACY_LIST_REMOVED', privacy_list) |
|---|
| 496 | else: |
|---|
| 497 | self.dispatch('ERROR', (_('Error while removing privacy list'), |
|---|
| 498 | _('Privacy list %s has not been removed. It is maybe active in ' |
|---|
| 499 | 'one of your connected resources. Deactivate it and try ' |
|---|
| 500 | 'again.') % privacy_list)) |
|---|
| 501 | common.xmpp.features_nb.delPrivacyList(self.connection, privacy_list, |
|---|
| 502 | _on_del_privacy_list_result) |
|---|
| 503 | |
|---|
| 504 | def get_privacy_list(self, title): |
|---|
| 505 | if not self.connection: |
|---|
| 506 | return |
|---|
| 507 | common.xmpp.features_nb.getPrivacyList(self.connection, title) |
|---|
| 508 | |
|---|
| 509 | def set_privacy_list(self, listname, tags): |
|---|
| 510 | if not self.connection: |
|---|
| 511 | return |
|---|
| 512 | common.xmpp.features_nb.setPrivacyList(self.connection, listname, tags) |
|---|
| 513 | |
|---|
| 514 | def set_active_list(self, listname): |
|---|
| 515 | if not self.connection: |
|---|
| 516 | return |
|---|
| 517 | common.xmpp.features_nb.setActivePrivacyList(self.connection, listname, 'active') |
|---|
| 518 | |
|---|
| 519 | def set_default_list(self, listname): |
|---|
| 520 | if not self.connection: |
|---|
| 521 | return |
|---|
| 522 | common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname) |
|---|
| 523 | |
|---|
| 524 | def build_privacy_rule(self, name, action): |
|---|
| 525 | '''Build a Privacy rule stanza for invisibility''' |
|---|
| 526 | iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '') |
|---|
| 527 | l = iq.getTag('query').setTag('list', {'name': name}) |
|---|
| 528 | i = l.setTag('item', {'action': action, 'order': '1'}) |
|---|
| 529 | i.setTag('presence-out') |
|---|
| 530 | return iq |
|---|
| 531 | |
|---|
| 532 | def activate_privacy_rule(self, name): |
|---|
| 533 | if not self.connection: |
|---|
| 534 | return |
|---|
| 535 | '''activate a privacy rule''' |
|---|
| 536 | iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '') |
|---|
| 537 | iq.getTag('query').setTag('active', {'name': name}) |
|---|
| 538 | self.connection.send(iq) |
|---|
| 539 | |
|---|
| 540 | def send_invisible_presence(self, msg, signed, initial = False): |
|---|
| 541 | # try to set the privacy rule |
|---|
| 542 | iq = self.build_privacy_rule('invisible', 'deny') |
|---|
| 543 | self.connection.SendAndCallForResponse(iq< |
|---|