Package common :: Package zeroconf :: Module connection_zeroconf
[hide private]
[frames] | no frames]

Source Code for Module common.zeroconf.connection_zeroconf

  1  ##      common/zeroconf/connection_zeroconf.py 
  2  ## 
  3  ## Contributors for this file: 
  4  ##      - Yann Leboulanger <asterix@lagaule.org> 
  5  ##      - Nikos Kouremenos <nkour@jabber.org> 
  6  ##      - Dimitur Kirov <dkirov@gmail.com> 
  7  ##      - Travis Shirk <travis@pobox.com> 
  8  ## - Stefan Bethge <stefan@lanpartei.de> 
  9  ## 
 10  ## Copyright (C) 2003-2010 Yann Leboulanger <asterix@lagaule.org> 
 11  ## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org> 
 12  ## Copyright (C) 2006 Nikos Kouremenos <nkour@jabber.org> 
 13  ##                    Dimitur Kirov <dkirov@gmail.com> 
 14  ##                    Travis Shirk <travis@pobox.com> 
 15  ##                    Norman Rasmussen <norman@rasmussen.co.za> 
 16  ##                    Stefan Bethge <stefan@lanpartei.de> 
 17  ## 
 18  ## This file is part of Gajim. 
 19  ## 
 20  ## Gajim is free software; you can redistribute it and/or modify 
 21  ## it under the terms of the GNU General Public License as published 
 22  ## by the Free Software Foundation; version 3 only. 
 23  ## 
 24  ## Gajim is distributed in the hope that it will be useful, 
 25  ## but WITHOUT ANY WARRANTY; without even the implied warranty of 
 26  ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 27  ## GNU General Public License for more details. 
 28  ## 
 29  ## You should have received a copy of the GNU General Public License 
 30  ## along with Gajim.  If not, see <http://www.gnu.org/licenses/>. 
 31  ## 
 32   
 33   
 34  import os 
 35  import random 
 36  random.seed() 
 37   
 38  import signal 
 39  if os.name != 'nt': 
 40      signal.signal(signal.SIGPIPE, signal.SIG_DFL) 
 41  import getpass 
 42  import gobject 
 43   
 44  from common.connection import CommonConnection 
 45  from common import gajim 
 46  from common import GnuPG 
 47  from common.zeroconf import client_zeroconf 
 48  from common.zeroconf import zeroconf 
 49  from connection_handlers_zeroconf import * 
 50   
51 -class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
52 - def __init__(self, name):
53 ConnectionHandlersZeroconf.__init__(self) 54 # system username 55 self.username = None 56 self.server_resource = '' # zeroconf has no resource, fake an empty one 57 self.call_resolve_timeout = False 58 # we don't need a password, but must be non-empty 59 self.password = 'zeroconf' 60 self.autoconnect = False 61 62 CommonConnection.__init__(self, name) 63 self.is_zeroconf = True
64
66 """ 67 Get name, host, port from config, or create zeroconf account with default 68 values 69 """ 70 if not gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'name'): 71 gajim.log.debug('Creating zeroconf account') 72 gajim.config.add_per('accounts', gajim.ZEROCONF_ACC_NAME) 73 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 74 'autoconnect', True) 75 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'no_log_for', 76 '') 77 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'password', 78 'zeroconf') 79 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 80 'sync_with_global_status', True) 81 82 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 83 'custom_port', 5298) 84 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 85 'is_zeroconf', True) 86 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 87 'use_ft_proxies', False) 88 #XXX make sure host is US-ASCII 89 self.host = unicode(socket.gethostname()) 90 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'hostname', 91 self.host) 92 self.port = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 93 'custom_port') 94 self.autoconnect = gajim.config.get_per('accounts', 95 gajim.ZEROCONF_ACC_NAME, 'autoconnect') 96 self.sync_with_global_status = gajim.config.get_per('accounts', 97 gajim.ZEROCONF_ACC_NAME, 'sync_with_global_status') 98 self.first = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 99 'zeroconf_first_name') 100 self.last = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 101 'zeroconf_last_name') 102 self.jabber_id = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 103 'zeroconf_jabber_id') 104 self.email = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 105 'zeroconf_email') 106 107 if not self.username: 108 self.username = unicode(getpass.getuser()) 109 gajim.config.set_per('accounts', gajim.ZEROCONF_ACC_NAME, 'name', 110 self.username) 111 else: 112 self.username = gajim.config.get_per('accounts', 113 gajim.ZEROCONF_ACC_NAME, 'name')
114 # END __init__ 115
116 - def check_jid(self, jid):
117 return jid
118
119 - def _reconnect(self):
120 # Do not try to reco while we are already trying 121 self.time_to_reconnect = None 122 gajim.log.debug('reconnect') 123 124 self.disconnect() 125 self.change_status(self.old_show, self.status)
126
127 - def disable_account(self):
128 self.disconnect()
129
130 - def _on_resolve_timeout(self):
131 if self.connected: 132 self.connection.resolve_all() 133 diffs = self.roster.getDiffs() 134 for key in diffs: 135 self.roster.setItem(key) 136 self.dispatch('ROSTER_INFO', (key, self.roster.getName(key), 137 'both', 'no', self.roster.getGroups(key))) 138 self.dispatch('NOTIFY', (key, self.roster.getStatus(key), 139 self.roster.getMessage(key), 'local', 0, None, 0, None)) 140 #XXX open chat windows don't get refreshed (full name), add that 141 return self.call_resolve_timeout
142 143 # callbacks called from zeroconf
144 - def _on_new_service(self, jid):
145 self.roster.setItem(jid) 146 self.dispatch('ROSTER_INFO', (jid, self.roster.getName(jid), 'both', 'no', 147 self.roster.getGroups(jid))) 148 self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), 149 self.roster.getMessage(jid), 'local', 0, None, 0, None))
150
151 - def _on_remove_service(self, jid):
152 self.roster.delItem(jid) 153 # 'NOTIFY' (account, (jid, status, status message, resource, priority, 154 # keyID, timestamp, contact_nickname)) 155 self.dispatch('NOTIFY', (jid, 'offline', '', 'local', 0, None, 0, None))
156
157 - def _disconnectedReconnCB(self):
158 """ 159 Called when we are disconnected. Comes from network manager for example 160 we don't try to reconnect, network manager will tell us when we can 161 """ 162 if gajim.account_is_connected(self.name): 163 # we cannot change our status to offline or connecting 164 # after we auth to server 165 self.old_show = STATUS_LIST[self.connected] 166 self.connected = 0 167 self.dispatch('STATUS', 'offline') 168 # random number to show we wait network manager to send us a reconenct 169 self.time_to_reconnect = 5 170 self.on_purpose = False
171
172 - def _on_name_conflictCB(self, alt_name):
173 self.disconnect() 174 self.dispatch('STATUS', 'offline') 175 self.dispatch('ZC_NAME_CONFLICT', alt_name)
176
177 - def _on_error(self, message):
178 self.dispatch('ERROR', (_('Avahi error'), 179 _('%s\nLink-local messaging might not work properly.') % message))
180
181 - def connect(self, show='online', msg=''):
182 self.get_config_values_or_default() 183 if not self.connection: 184 self.connection = client_zeroconf.ClientZeroconf(self) 185 if not zeroconf.test_zeroconf(): 186 self.dispatch('STATUS', 'offline') 187 self.status = 'offline' 188 self.dispatch('CONNECTION_LOST', 189 (_('Could not connect to "%s"') % self.name, 190 _('Please check if Avahi or Bonjour is installed.'))) 191 self.disconnect() 192 return 193 result = self.connection.connect(show, msg) 194 if not result: 195 self.dispatch('STATUS', 'offline') 196 self.status = 'offline' 197 if result is False: 198 self.dispatch('CONNECTION_LOST', 199 (_('Could not start local service'), 200 _('Unable to bind to port %d.' % self.port))) 201 else: # result is None 202 self.dispatch('CONNECTION_LOST', 203 (_('Could not start local service'), 204 _('Please check if avahi-daemon is running.'))) 205 self.disconnect() 206 return 207 else: 208 self.connection.announce() 209 self.roster = self.connection.getRoster() 210 self.dispatch('ROSTER', self.roster) 211 212 # display contacts already detected and resolved 213 for jid in self.roster.keys(): 214 self.dispatch('ROSTER_INFO', (jid, self.roster.getName(jid), 'both', 215 'no', self.roster.getGroups(jid))) 216 self.dispatch('NOTIFY', (jid, self.roster.getStatus(jid), 217 self.roster.getMessage(jid), 'local', 0, None, 0, None)) 218 219 self.connected = STATUS_LIST.index(show) 220 221 # refresh all contacts data every five seconds 222 self.call_resolve_timeout = True 223 gobject.timeout_add_seconds(5, self._on_resolve_timeout) 224 return True
225
226 - def disconnect(self, on_purpose=False):
227 self.connected = 0 228 self.time_to_reconnect = None 229 if self.connection: 230 self.connection.disconnect() 231 self.connection = None 232 # stop calling the timeout 233 self.call_resolve_timeout = False
234
235 - def reannounce(self):
236 if self.connected: 237 txt = {} 238 txt['1st'] = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 239 'zeroconf_first_name') 240 txt['last'] = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 241 'zeroconf_last_name') 242 txt['jid'] = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 243 'zeroconf_jabber_id') 244 txt['email'] = gajim.config.get_per('accounts', 245 gajim.ZEROCONF_ACC_NAME, 'zeroconf_email') 246 self.connection.reannounce(txt)
247
248 - def update_details(self):
249 if self.connection: 250 port = gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 251 'custom_port') 252 if port != self.port: 253 self.port = port 254 last_msg = self.connection.last_msg 255 self.disconnect() 256 if not self.connect(self.status, last_msg): 257 return 258 if self.status != 'invisible': 259 self.connection.announce() 260 else: 261 self.reannounce()
262
263 - def connect_and_init(self, show, msg, sign_msg):
264 # to check for errors from zeroconf 265 check = True 266 if not self.connect(show, msg): 267 return 268 if show != 'invisible': 269 check = self.connection.announce() 270 else: 271 self.connected = STATUS_LIST.index(show) 272 self.dispatch('SIGNED_IN', ()) 273 274 # stay offline when zeroconf does something wrong 275 if check: 276 self.dispatch('STATUS', show) 277 else: 278 # show notification that avahi or system bus is down 279 self.dispatch('STATUS', 'offline') 280 self.status = 'offline' 281 self.dispatch('CONNECTION_LOST', 282 (_('Could not change status of account "%s"') % self.name, 283 _('Please check if avahi-daemon is running.')))
284
285 - def _change_to_invisible(self, msg):
286 if self.connection.remove_announce(): 287 self.dispatch('STATUS', 'invisible') 288 else: 289 # show notification that avahi or system bus is down 290 self.dispatch('STATUS', 'offline') 291 self.status = 'offline' 292 self.dispatch('CONNECTION_LOST', 293 (_('Could not change status of account "%s"') % self.name, 294 _('Please check if avahi-daemon is running.')))
295
296 - def _change_from_invisible(self):
297 self.connection.announce()
298
299 - def _update_status(self, show, msg):
300 if self.connection.set_show_msg(show, msg): 301 self.dispatch('STATUS', show) 302 else: 303 # show notification that avahi or system bus is down 304 self.dispatch('STATUS', 'offline') 305 self.status = 'offline' 306 self.dispatch('CONNECTION_LOST', 307 (_('Could not change status of account "%s"') % self.name, 308 _('Please check if avahi-daemon is running.')))
309
310 - def send_message(self, jid, msg, keyID, type_='chat', subject='', 311 chatstate=None, msg_id=None, composing_xep=None, resource=None, 312 user_nick=None, xhtml=None, label=None, session=None, forward_from=None, 313 form_node=None, original_message=None, delayed=None, callback=None, 314 callback_args=[], now=True):
315 316 def on_send_ok(msg_id): 317 self.dispatch('MSGSENT', (jid, msg, keyID)) 318 if callback: 319 callback(msg_id, *callback_args) 320 321 self.log_message(jid, msg, forward_from, session, original_message, 322 subject, type_)
323 324 def on_send_not_ok(reason): 325 reason += ' ' + _('Your message could not be sent.') 326 self.dispatch('MSGERROR', [jid, -1, reason, None, None, session])
327 328 def cb(jid, msg, keyID, forward_from, session, original_message, subject, 329 type_, msg_iq): 330 ret = self.connection.send(msg_iq, msg is not None, on_ok=on_send_ok, 331 on_not_ok=on_send_not_ok) 332 333 if ret == -1: 334 # Contact Offline 335 self.dispatch('MSGERROR', [jid, -1, _('Contact is offline. Your ' 336 'message could not be sent.'), None, None, session]) 337 338 self._prepare_message(jid, msg, keyID, type_=type_, subject=subject, 339 chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep, 340 resource=resource, user_nick=user_nick, xhtml=xhtml, session=session, 341 forward_from=forward_from, form_node=form_node, 342 original_message=original_message, delayed=delayed, callback=cb) 343
344 - def send_stanza(self, stanza):
345 # send a stanza untouched 346 if not self.connection: 347 return 348 if not isinstance(stanza, common.xmpp.Node): 349 stanza = common.xmpp.Protocol(node=stanza) 350 self.connection.send(stanza)
351
352 - def _event_dispatcher(self, realm, event, data):
353 CommonConnection._event_dispatcher(self, realm, event, data) 354 if realm == '': 355 if event == common.xmpp.transports_nb.DATA_ERROR: 356 thread_id = data[1] 357 frm = unicode(data[0]) 358 session = self.get_or_create_session(frm, thread_id) 359 self.dispatch('MSGERROR', [frm, -1, 360 _('Connection to host could not be established: Timeout while ' 361 'sending data.'), None, None, session])
362 363 # END ConnectionZeroconf 364