comparison src/plugins/plugin_sec_otr.py @ 2125:ca82c97db195

plugin sec OTR: fixed OTR: - fixed bridge calls - use deferred translation - unicode string fixes - various other improvments
author Goffi <goffi@goffi.org>
date Thu, 26 Jan 2017 20:24:58 +0100
parents 200cd707a46d
children aa94f33fd2ad
comparison
equal deleted inserted replaced
2124:cf63e4209643 2125:ca82c97db195
35 import time 35 import time
36 import uuid 36 import uuid
37 37
38 NS_OTR = "otr_plugin" 38 NS_OTR = "otr_plugin"
39 PRIVATE_KEY = "PRIVATE KEY" 39 PRIVATE_KEY = "PRIVATE KEY"
40 MAIN_MENU = D_('OTR') 40 OTR_MENU = D_(u'OTR')
41 AUTH_TXT = D_("To authenticate your correspondent, you need to give your below fingerprint *BY AN EXTERNAL CANAL* (i.e. not in this chat), and check that the one he gives you is the same as below. If there is a mismatch, there can be a spy between you!") 41 AUTH_TXT = D_(u"To authenticate your correspondent, you need to give your below fingerprint *BY AN EXTERNAL CANAL* (i.e. not in this chat), and check that the one he gives you is the same as below. If there is a mismatch, there can be a spy between you!")
42 DROP_TXT = D_("You private key is used to encrypt messages for your correspondent, nobody except you must know it, if you are in doubt, you should drop it!\n\nAre you sure you want to drop your private key?") 42 DROP_TXT = D_(u"You private key is used to encrypt messages for your correspondent, nobody except you must know it, if you are in doubt, you should drop it!\n\nAre you sure you want to drop your private key?")
43 NO_LOG = D_(u"/!\\Your history is not logged anymore, and most of advanced features are disabled !") # FIXME: not used at the moment
43 44
44 DEFAULT_POLICY_FLAGS = { 45 DEFAULT_POLICY_FLAGS = {
45 'ALLOW_V1':False, 46 'ALLOW_V1':False,
46 'ALLOW_V2':True, 47 'ALLOW_V2':True,
47 'REQUIRE_ENCRYPTION':True, 48 'REQUIRE_ENCRYPTION':True,
48 } 49 }
50
49 51
50 PLUGIN_INFO = { 52 PLUGIN_INFO = {
51 "name": "OTR", 53 "name": "OTR",
52 "import_name": "OTR", 54 "import_name": "OTR",
53 "type": "SEC", 55 "type": "SEC",
54 "protocols": [], 56 "protocols": [],
55 "dependencies": [], 57 "dependencies": [],
56 "main": "OTR", 58 "main": "OTR",
57 "handler": "no", 59 "handler": "no",
58 "description": _("""Implementation of OTR""") 60 "description": _(u"""Implementation of OTR""")
59 } 61 }
60 62
61 63
62 class Context(potr.context.Context): 64 class Context(potr.context.Context):
63 def __init__(self, host, account, other_jid): 65 def __init__(self, host, account, other_jid):
101 except TypeError: 103 except TypeError:
102 trusted = False 104 trusted = False
103 trusted_str = _(u"trusted") if trusted else _(u"untrusted") 105 trusted_str = _(u"trusted") if trusted else _(u"untrusted")
104 106
105 if old_state == potr.context.STATE_ENCRYPTED: 107 if old_state == potr.context.STATE_ENCRYPTED:
106 feedback = _(u"%(trusted)s OTR conversation with %(other_jid)s REFRESHED") % {'trusted': trusted_str, 'other_jid': self.peer.full()} 108 feedback = D_(u"{trusted} OTR conversation with {other_jid} REFRESHED").format(
109 trusted = trusted_str,
110 other_jid = self.peer.full())
107 else: 111 else:
108 feedback = _(u"%(trusted)s Encrypted OTR conversation started with %(other_jid)s\n/!\\ Your history is not logged anymore, and most of advanced features are disabled !") % {'trusted': trusted_str, 'other_jid': self.peer.full()} 112 feedback = D_(u"{trusted} Encrypted OTR conversation started with {other_jid}\n{no_log}").format(
113 trusted = trusted_str,
114 other_jid = self.peer.full(),
115 no_log = NO_LOG)
109 elif state == potr.context.STATE_FINISHED: 116 elif state == potr.context.STATE_FINISHED:
110 feedback = _(u"OTR conversation with %(other_jid)s is FINISHED") % {'other_jid': self.peer.full()} 117 feedback = D_(u"OTR conversation with {other_jid} is FINISHED").format(other_jid = self.peer.full())
111 else: 118 else:
112 log.error(_(u"Unknown OTR state")) 119 log.error(D_(u"Unknown OTR state"))
113 return 120 return
114 121
115 client = self.user.client 122 client = self.user.client
116 self.host.bridge.messageNew(client.jid.full(), 123 self.host.bridge.messageNew(uid=unicode(uuid.uuid4()),
117 feedback, 124 timestamp=time.time(),
125 from_jid=client.jid.full(),
126 to_jid=self.peer.full(),
127 message={u'': feedback},
128 subject={},
118 mess_type=C.MESS_TYPE_INFO, 129 mess_type=C.MESS_TYPE_INFO,
119 to_jid=self.peer.full(),
120 extra={}, 130 extra={},
121 profile=client.profile) 131 profile=client.profile)
122 # TODO: send signal to frontends 132 # TODO: send signal to frontends
123 133
124 def disconnect(self): 134 def disconnect(self):
148 return self.privkey 158 return self.privkey
149 159
150 def savePrivkey(self): 160 def savePrivkey(self):
151 log.debug(u"savePrivkey") 161 log.debug(u"savePrivkey")
152 if self.privkey is None: 162 if self.privkey is None:
153 raise exceptions.InternalError(_("Save is called but privkey is None !")) 163 raise exceptions.InternalError(_(u"Save is called but privkey is None !"))
154 priv_key = self.privkey.serializePrivateKey().encode('hex') 164 priv_key = self.privkey.serializePrivateKey().encode('hex')
155 d = self.host.memory.encryptValue(priv_key, self.client.profile) 165 d = self.host.memory.encryptValue(priv_key, self.client.profile)
156 def save_encrypted_key(encrypted_priv_key): 166 def save_encrypted_key(encrypted_priv_key):
157 self.client.otr_data[PRIVATE_KEY] = encrypted_priv_key 167 self.client.otr_data[PRIVATE_KEY] = encrypted_priv_key
158 d.addCallback(save_encrypted_key) 168 d.addCallback(save_encrypted_key)
208 self.context_managers = {} 218 self.context_managers = {}
209 self.skipped_profiles = set() 219 self.skipped_profiles = set()
210 host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=100000) 220 host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=100000)
211 host.trigger.add("messageSend", self.messageSendTrigger, priority=100000) 221 host.trigger.add("messageSend", self.messageSendTrigger, priority=100000)
212 host.bridge.addMethod("skipOTR", ".plugin", in_sign='s', out_sign='', method=self._skipOTR) # FIXME: must be removed, must be done on per-message basis 222 host.bridge.addMethod("skipOTR", ".plugin", in_sign='s', out_sign='', method=self._skipOTR) # FIXME: must be removed, must be done on per-message basis
213 host.importMenu((MAIN_MENU, D_("Start/Refresh")), self._startRefresh, security_limit=0, help_string=D_("Start or refresh an OTR session"), type_=C.MENU_SINGLE) 223 host.importMenu((OTR_MENU, D_(u"Start/Refresh")), self._otrStartRefresh, security_limit=0, help_string=D_(u"Start or refresh an OTR session"), type_=C.MENU_SINGLE)
214 host.importMenu((MAIN_MENU, D_("End session")), self._endSession, security_limit=0, help_string=D_("Finish an OTR session"), type_=C.MENU_SINGLE) 224 host.importMenu((OTR_MENU, D_(u"End session")), self._otrSessionEnd, security_limit=0, help_string=D_(u"Finish an OTR session"), type_=C.MENU_SINGLE)
215 host.importMenu((MAIN_MENU, D_("Authenticate")), self._authenticate, security_limit=0, help_string=D_("Authenticate user/see your fingerprint"), type_=C.MENU_SINGLE) 225 host.importMenu((OTR_MENU, D_(u"Authenticate")), self._otrAuthenticate, security_limit=0, help_string=D_(u"Authenticate user/see your fingerprint"), type_=C.MENU_SINGLE)
216 host.importMenu((MAIN_MENU, D_("Drop private key")), self._dropPrivKey, security_limit=0, type_=C.MENU_SINGLE) 226 host.importMenu((OTR_MENU, D_(u"Drop private key")), self._dropPrivKey, security_limit=0, type_=C.MENU_SINGLE)
217 host.trigger.add("presenceReceived", self._presenceReceivedTrigger) 227 host.trigger.add("presenceReceived", self._presenceReceivedTrigger)
218 228
219 def _fixPotr(self): 229 def _fixPotr(self):
220 # FIXME: potr fix for bad unicode handling 230 # FIXME: potr fix for bad unicode handling
221 # this method monkeypatch it, must be removed when potr 231 # this method monkeypatch it, must be removed when potr
268 try: 278 try:
269 self.skipped_profiles.remove(profile) 279 self.skipped_profiles.remove(profile)
270 except KeyError: 280 except KeyError:
271 pass 281 pass
272 282
273 def _startRefresh(self, menu_data, profile): 283 def _otrStartRefresh(self, menu_data, profile):
274 """Start or refresh an OTR session 284 """Start or refresh an OTR session
275 285
276 @param menu_data: %(menu_data)s 286 @param menu_data: %(menu_data)s
277 @param profile: %(doc_profile)s 287 @param profile: %(doc_profile)s
278 """ 288 """
279 client = self.host.getClient(profile) 289 client = self.host.getClient(profile)
280 try: 290 try:
281 to_jid = jid.JID(menu_data['jid']) 291 to_jid = jid.JID(menu_data['jid'])
282 if not to_jid.resource: 292 except KeyError:
283 to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored 293 log.error(_(u"jid key is not present !"))
284 except KeyError:
285 log.error(_("jid key is not present !"))
286 return defer.fail(exceptions.DataError) 294 return defer.fail(exceptions.DataError)
287 otrctx = self.context_managers[profile].getContextForUser(to_jid) 295 self.startRefresh(client, to_jid)
288 query = otrctx.messageSend(0, '?OTRv?') 296 return {}
297
298 def startRefresh(self, client, to_jid):
299 """Start or refresh an OTR session
300
301 @param to_jid(jid.JID): jid to start encrypted session with
302 """
303 if not to_jid.resource:
304 to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
305 otrctx = self.context_managers[client.profile].getContextForUser(to_jid)
306 query = otrctx.sendMessage(0, '?OTRv?')
289 otrctx.inject(query) 307 otrctx.inject(query)
290 return {} 308
291 309 def _otrSessionEnd(self, menu_data, profile):
292 def _endSession(self, menu_data, profile):
293 """End an OTR session 310 """End an OTR session
294 311
295 @param menu_data: %(menu_data)s 312 @param menu_data: %(menu_data)s
296 @param profile: %(doc_profile)s 313 @param profile: %(doc_profile)s
297 """ 314 """
298 client = self.host.getClient(profile) 315 client = self.host.getClient(profile)
299 try: 316 try:
300 to_jid = jid.JID(menu_data['jid']) 317 to_jid = jid.JID(menu_data['jid'])
301 if not to_jid.resource: 318 except KeyError:
302 to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored 319 log.error(_(u"jid key is not present !"))
303 except KeyError:
304 log.error(_("jid key is not present !"))
305 return defer.fail(exceptions.DataError) 320 return defer.fail(exceptions.DataError)
306 otrctx = self.context_managers[profile].getContextForUser(to_jid) 321 self.endSession(client, to_jid)
322 return {}
323
324 def endSession(self, client, to_jid):
325 """End an OTR session"""
326 if not to_jid.resource:
327 to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
328 otrctx = self.context_managers[client.profile].getContextForUser(to_jid)
307 otrctx.disconnect() 329 otrctx.disconnect()
308 return {} 330 return {}
309 331
310 def _authenticate(self, menu_data, profile): 332 def _otrAuthenticate(self, menu_data, profile):
311 """Authenticate other user and see our own fingerprint 333 """End an OTR session
312 334
313 @param menu_data: %(menu_data)s 335 @param menu_data: %(menu_data)s
314 @param profile: %(doc_profile)s 336 @param profile: %(doc_profile)s
315 """ 337 """
316 client = self.host.getClient(profile) 338 client = self.host.getClient(profile)
317 try: 339 try:
318 to_jid = jid.JID(menu_data['jid']) 340 to_jid = jid.JID(menu_data['jid'])
319 if not to_jid.resource: 341 except KeyError:
320 to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored 342 log.error(_(u"jid key is not present !"))
321 except KeyError:
322 log.error(_("jid key is not present !"))
323 return defer.fail(exceptions.DataError) 343 return defer.fail(exceptions.DataError)
324 ctxMng = self.context_managers[profile] 344 return self.authenticate(client, to_jid)
345
346 def authenticate(self, client, to_jid):
347 """Authenticate other user and see our own fingerprint"""
348 if not to_jid.resource:
349 to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
350 ctxMng = self.context_managers[client.profile]
325 otrctx = ctxMng.getContextForUser(to_jid) 351 otrctx = ctxMng.getContextForUser(to_jid)
326 priv_key = ctxMng.account.privkey 352 priv_key = ctxMng.account.privkey
327 353
328 if priv_key is None: 354 if priv_key is None:
329 # we have no private key yet 355 # we have no private key yet
330 dialog = xml_tools.XMLUI(C.XMLUI_DIALOG, 356 dialog = xml_tools.XMLUI(C.XMLUI_DIALOG,
331 dialog_opt = {C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE, 357 dialog_opt = {C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE,
332 C.XMLUI_DATA_MESS: _("You have no private key yet, start an OTR conversation to have one"), 358 C.XMLUI_DATA_MESS: _(u"You have no private key yet, start an OTR conversation to have one"),
333 C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_WARNING 359 C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_WARNING
334 }, 360 },
335 title = _("No private key"), 361 title = _(u"No private key"),
336 ) 362 )
337 return {'xmlui': dialog.toXml()} 363 return {'xmlui': dialog.toXml()}
338 364
339 other_fingerprint = otrctx.getCurrentKey() 365 other_fingerprint = otrctx.getCurrentKey()
340 366
341 if other_fingerprint is None: 367 if other_fingerprint is None:
342 # we have a private key, but not the fingerprint of our correspondent 368 # we have a private key, but not the fingerprint of our correspondent
343 dialog = xml_tools.XMLUI(C.XMLUI_DIALOG, 369 dialog = xml_tools.XMLUI(C.XMLUI_DIALOG,
344 dialog_opt = {C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE, 370 dialog_opt = {C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE,
345 C.XMLUI_DATA_MESS: _("Your fingerprint is:\n{fingerprint}\n\nStart an OTR conversation to have your correspondent one.").format(fingerprint=priv_key), 371 C.XMLUI_DATA_MESS: _(u"Your fingerprint is:\n{fingerprint}\n\nStart an OTR conversation to have your correspondent one.").format(fingerprint=priv_key),
346 C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_INFO 372 C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_INFO
347 }, 373 },
348 title = _("Fingerprint"), 374 title = _(u"Fingerprint"),
349 ) 375 )
350 return {'xmlui': dialog.toXml()} 376 return {'xmlui': dialog.toXml()}
351 377
352 def setTrust(raw_data, profile): 378 def setTrust(raw_data, profile):
353 # This method is called when authentication form is submited 379 # This method is called when authentication form is submited
354 data = xml_tools.XMLUIResult2DataFormResult(raw_data) 380 data = xml_tools.XMLUIResult2DataFormResult(raw_data)
355 if data['match'] == 'yes': 381 if data['match'] == 'yes':
356 otrctx.setCurrentTrust('verified') 382 otrctx.setCurrentTrust('verified')
357 note_msg = _("Your correspondent {correspondent} is now TRUSTED") 383 note_msg = _(u"Your correspondent {correspondent} is now TRUSTED")
358 else: 384 else:
359 otrctx.setCurrentTrust('') 385 otrctx.setCurrentTrust('')
360 note_msg = _("Your correspondent {correspondent} is now UNTRUSTED") 386 note_msg = _(u"Your correspondent {correspondent} is now UNTRUSTED")
361 note = xml_tools.XMLUI(C.XMLUI_DIALOG, dialog_opt = { 387 note = xml_tools.XMLUI(C.XMLUI_DIALOG, dialog_opt = {
362 C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE, 388 C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE,
363 C.XMLUI_DATA_MESS: note_msg.format(correspondent=otrctx.peer)} 389 C.XMLUI_DATA_MESS: note_msg.format(correspondent=otrctx.peer)}
364 ) 390 )
365 return {'xmlui': note.toXml()} 391 return {'xmlui': note.toXml()}
368 trusted = bool(otrctx.getCurrentTrust()) 394 trusted = bool(otrctx.getCurrentTrust())
369 395
370 xmlui = xml_tools.XMLUI(C.XMLUI_FORM, title=_('Authentication (%s)') % to_jid.full(), submit_id=submit_id) 396 xmlui = xml_tools.XMLUI(C.XMLUI_FORM, title=_('Authentication (%s)') % to_jid.full(), submit_id=submit_id)
371 xmlui.addText(_(AUTH_TXT)) 397 xmlui.addText(_(AUTH_TXT))
372 xmlui.addDivider() 398 xmlui.addDivider()
373 xmlui.addText(_("Your own fingerprint is:\n{fingerprint}").format(fingerprint=priv_key)) 399 xmlui.addText(D_(u"Your own fingerprint is:\n{fingerprint}").format(fingerprint=priv_key))
374 xmlui.addText(_("Your correspondent fingerprint should be:\n{fingerprint}").format(fingerprint=other_fingerprint)) 400 xmlui.addText(D_(u"Your correspondent fingerprint should be:\n{fingerprint}").format(fingerprint=other_fingerprint))
375 xmlui.addDivider('blank') 401 xmlui.addDivider('blank')
376 xmlui.changeContainer('pairs') 402 xmlui.changeContainer('pairs')
377 xmlui.addLabel(_('Is your correspondent fingerprint the same as here ?')) 403 xmlui.addLabel(D_(u'Is your correspondent fingerprint the same as here ?'))
378 xmlui.addList("match", [('yes', _('yes')),('no', _('no'))], ['yes' if trusted else 'no']) 404 xmlui.addList("match", [('yes', _('yes')),('no', _('no'))], ['yes' if trusted else 'no'])
379 return {'xmlui': xmlui.toXml()} 405 return {'xmlui': xmlui.toXml()}
380 406
381 def _dropPrivKey(self, menu_data, profile): 407 def _dropPrivKey(self, menu_data, profile):
382 """Drop our private Key 408 """Drop our private Key
388 try: 414 try:
389 to_jid = jid.JID(menu_data['jid']) 415 to_jid = jid.JID(menu_data['jid'])
390 if not to_jid.resource: 416 if not to_jid.resource:
391 to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored 417 to_jid.resource = self.host.memory.getMainResource(client, to_jid) # FIXME: temporary and unsecure, must be changed when frontends are refactored
392 except KeyError: 418 except KeyError:
393 log.error(_("jid key is not present !")) 419 log.error(_(u"jid key is not present !"))
394 return defer.fail(exceptions.DataError) 420 return defer.fail(exceptions.DataError)
395 421
396 ctxMng = self.context_managers[profile] 422 ctxMng = self.context_managers[profile]
397 if ctxMng.account.privkey is None: 423 if ctxMng.account.privkey is None:
398 return {'xmlui': xml_tools.note(_("You don't have a private key yet !")).toXml()} 424 return {'xmlui': xml_tools.note(_(u"You don't have a private key yet !")).toXml()}
399 425
400 def dropKey(data, profile): 426 def dropKey(data, profile):
401 if C.bool(data['answer']): 427 if C.bool(data['answer']):
402 # we end all sessions 428 # we end all sessions
403 for context in ctxMng.contexts.values(): 429 for context in ctxMng.contexts.values():
404 context.disconnect() 430 context.disconnect()
405 ctxMng.account.privkey = None 431 ctxMng.account.privkey = None
406 ctxMng.account.getPrivkey() # as account.privkey is None, getPrivkey will generate a new key, and save it 432 ctxMng.account.getPrivkey() # as account.privkey is None, getPrivkey will generate a new key, and save it
407 return {'xmlui': xml_tools.note(_("Your private key has been dropped")).toXml()} 433 return {'xmlui': xml_tools.note(_(u"Your private key has been dropped")).toXml()}
408 return {} 434 return {}
409 435
410 submit_id = self.host.registerCallback(dropKey, with_data=True, one_shot=True) 436 submit_id = self.host.registerCallback(dropKey, with_data=True, one_shot=True)
411 437
412 confirm = xml_tools.XMLUI(C.XMLUI_DIALOG, title=_('Confirm private key drop'), dialog_opt = {'type': C.XMLUI_DIALOG_CONFIRM, 'message': _(DROP_TXT)}, submit_id = submit_id) 438 confirm = xml_tools.XMLUI(C.XMLUI_DIALOG, title=_('Confirm private key drop'), dialog_opt = {'type': C.XMLUI_DIALOG_CONFIRM, 'message': _(DROP_TXT)}, submit_id = submit_id)
423 res = otrctx.receiveMessage(message.encode('utf-8')) 449 res = otrctx.receiveMessage(message.encode('utf-8'))
424 except potr.context.UnencryptedMessage: 450 except potr.context.UnencryptedMessage:
425 if otrctx.state == potr.context.STATE_ENCRYPTED: 451 if otrctx.state == potr.context.STATE_ENCRYPTED:
426 log.warning(u"Received unencrypted message in an encrypted context (from %(jid)s)" % {'jid': from_jid.full()}) 452 log.warning(u"Received unencrypted message in an encrypted context (from %(jid)s)" % {'jid': from_jid.full()})
427 client = self.host.getClient(profile) 453 client = self.host.getClient(profile)
428 self.host.bridge.messageNew(from_jid.full(), 454
429 _(u"WARNING: received unencrypted data in a supposedly encrypted context"), 455 feedback=D_(u"WARNING: received unencrypted data in a supposedly encrypted context"),
456 self.host.bridge.messageNew(uid=unicode(uuid.uuid4()),
457 timestamp=time.time(),
458 from_jid=from_jid.full(),
459 to_jid=client.jid.full(),
460 message={u'': feedback},
461 subject={},
430 mess_type=C.MESS_TYPE_INFO, 462 mess_type=C.MESS_TYPE_INFO,
431 to_jid=client.jid.full(),
432 extra={}, 463 extra={},
433 profile=client.profile) 464 profile=client.profile)
434 encrypted = False 465 encrypted = False
435 except StopIteration: 466 except StopIteration:
436 return data 467 return data
485 log.warning(u"No message found") 516 log.warning(u"No message found")
486 return False 517 return False
487 otrctx.sendMessage(0, msg.encode('utf-8')) 518 otrctx.sendMessage(0, msg.encode('utf-8'))
488 self.host.messageSendToBridge(mess_data, client) 519 self.host.messageSendToBridge(mess_data, client)
489 else: 520 else:
490 feedback = D_("Your message was not sent because your correspondent closed the encrypted conversation on his/her side. Either close your own side, or refresh the session.") 521 feedback = D_(u"Your message was not sent because your correspondent closed the encrypted conversation on his/her side. Either close your own side, or refresh the session.")
491 self.host.bridge.messageNew(to_jid.full(), 522 self.host.bridge.messageNew(uid=unicode(uuid.uuid4()),
492 feedback, 523 timestamp=time.time(),
524 from_jid=to_jid.full(),
525 to_jid=client.jid.full(),
526 message={u'': feedback},
527 subject={},
493 mess_type=C.MESS_TYPE_INFO, 528 mess_type=C.MESS_TYPE_INFO,
494 to_jid=client.jid.full(),
495 extra={}, 529 extra={},
496 profile=client.profile) 530 profile=client.profile)
497 return False 531 return False
498 else: 532 else:
499 log.debug(u"sending message unencrypted") 533 log.debug(u"sending message unencrypted")