parent
f68ffc3816
commit
f7874d29c7
|
@ -1206,6 +1206,9 @@ class ChatControl(ChatControlBase):
|
|||
ChatControlBase.update_ui(self)
|
||||
|
||||
def get_otr_status(self):
|
||||
if not self.session:
|
||||
return 0
|
||||
|
||||
ctx = gajim.otr_module.otrl_context_find(
|
||||
self.session.conn.otr_userstates,
|
||||
self.contact.get_full_jid().encode(),
|
||||
|
@ -1613,7 +1616,7 @@ class ChatControl(ChatControlBase):
|
|||
|
||||
def print_esession_details(self):
|
||||
'''print esession settings to textview'''
|
||||
e2e_is_active = self.session and self.session.enable_encryption
|
||||
e2e_is_active = bool(self.session) and self.session.enable_encryption
|
||||
if e2e_is_active:
|
||||
msg = _('E2E encryption enabled')
|
||||
ChatControlBase.print_conversation_line(self, msg, 'status', '', None)
|
||||
|
@ -2022,6 +2025,7 @@ class ChatControl(ChatControlBase):
|
|||
self.contact.our_chatstate = None
|
||||
|
||||
# disconnect self from session
|
||||
if self.session:
|
||||
self.session.control = None
|
||||
|
||||
# Disconnect timer callbacks
|
||||
|
|
|
@ -980,6 +980,8 @@ class Connection(ConnectionHandlers):
|
|||
elif show == 'offline':
|
||||
self.connected = 0
|
||||
if self.connection:
|
||||
self.terminate_sessions()
|
||||
|
||||
self.on_purpose = True
|
||||
p = common.xmpp.Presence(typ = 'unavailable')
|
||||
p = self.add_sha(p, False)
|
||||
|
|
|
@ -1282,13 +1282,13 @@ class ConnectionHandlersBase:
|
|||
self.sessions = {}
|
||||
|
||||
def delete_session(self, jid, thread_id):
|
||||
try:
|
||||
if not jid in self.sessions:
|
||||
jid = gajim.get_jid_without_resource(jid)
|
||||
|
||||
del self.sessions[jid][thread_id]
|
||||
|
||||
if not self.sessions[jid]:
|
||||
del self.sessions[jid]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def find_null_session(self, jid):
|
||||
'''finds all of the sessions between us and a remote jid in which we
|
||||
|
@ -1644,17 +1644,17 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
mtype = 'normal'
|
||||
|
||||
msgtxt = msg.getBody()
|
||||
subject = msg.getSubject() # if not there, it's None
|
||||
|
||||
jid = helpers.get_jid_from_iq(msg)
|
||||
|
||||
encrypted = False
|
||||
xep_200_encrypted = msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO)
|
||||
|
||||
# I don't trust libotr, that's why I only pass the
|
||||
# message to it if it either contains the magic
|
||||
# ?OTR string or a plaintext tagged message.
|
||||
if gajim.otr_module and \
|
||||
isinstance(msgtxt, unicode) and \
|
||||
if gajim.otr_module and not xep_200_encrypted \
|
||||
and isinstance(msgtxt, unicode) and \
|
||||
('\x20\x09\x20\x20\x09\x09\x09\x09\x20\x09\x20\x09\x20\x09\x20\x20' \
|
||||
in msgtxt or '?OTR' in msgtxt):
|
||||
# If it doesn't include ?OTR, it wasn't an
|
||||
|
@ -1764,11 +1764,12 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
tim = helpers.datetime_tuple(tim)
|
||||
tim = localtime(timegm(tim))
|
||||
|
||||
if msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO):
|
||||
if xep_200_encrypted:
|
||||
encrypted = True
|
||||
|
||||
try:
|
||||
msg = session.decrypt_stanza(msg)
|
||||
msgtxt = msg.getBody()
|
||||
except:
|
||||
self.dispatch('FAILED_DECRYPT', (frm, tim, session))
|
||||
|
||||
|
@ -1791,26 +1792,28 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
msgtxt = decmsg.replace('\x00', '')
|
||||
encrypted = True
|
||||
if mtype == 'error':
|
||||
self.dispatch_error_message(msg, msgtxt, session, frm, tim, subject)
|
||||
self.dispatch_error_message(msg, msgtxt, session, frm, tim)
|
||||
elif mtype == 'groupchat':
|
||||
self.dispatch_gc_message(msg, subject, frm, msgtxt, jid, tim)
|
||||
self.dispatch_gc_message(msg, frm, msgtxt, jid, tim)
|
||||
elif invite is not None:
|
||||
self.dispatch_invite_message(invite, frm)
|
||||
else:
|
||||
if isinstance(session, ChatControlSession):
|
||||
session.received(frm, msgtxt, tim, encrypted, subject, msg)
|
||||
session.received(frm, msgtxt, tim, encrypted, msg)
|
||||
else:
|
||||
session.received(msg)
|
||||
# END messageCB
|
||||
|
||||
# process and dispatch an error message
|
||||
def dispatch_error_message(self, msg, msgtxt, session, frm, tim, subject):
|
||||
def dispatch_error_message(self, msg, msgtxt, session, frm, tim):
|
||||
error_msg = msg.getErrorMsg()
|
||||
|
||||
if not error_msg:
|
||||
error_msg = msgtxt
|
||||
msgtxt = None
|
||||
|
||||
subject = msg.getSubject()
|
||||
|
||||
if session.is_loggable():
|
||||
try:
|
||||
gajim.logger.write('error', frm, error_msg, tim=tim,
|
||||
|
@ -1821,9 +1824,11 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
|
|||
tim, session))
|
||||
|
||||
# process and dispatch a groupchat message
|
||||
def dispatch_gc_message(self, msg, subject, frm, msgtxt, jid, tim):
|
||||
def dispatch_gc_message(self, msg, frm, msgtxt, jid, tim):
|
||||
has_timestamp = bool(msg.timestamp)
|
||||
|
||||
subject = msg.getSubject()
|
||||
|
||||
if subject is not None:
|
||||
self.dispatch('GC_SUBJECT', (frm, subject, msgtxt, has_timestamp))
|
||||
return
|
||||
|
|
|
@ -243,6 +243,9 @@ class EncryptedStanzaSession(StanzaSession):
|
|||
|
||||
return stanza
|
||||
|
||||
def is_xep_200_encrypted(self, msg):
|
||||
msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO)
|
||||
|
||||
def hmac(self, key, content):
|
||||
return HMAC.new(key, content, self.hash_alg).digest()
|
||||
|
||||
|
|
22
src/gajim.py
22
src/gajim.py
|
@ -2038,30 +2038,12 @@ class Interface:
|
|||
|
||||
if form.getField('terminate') and\
|
||||
form.getField('terminate').getValue() in ('1', 'true'):
|
||||
was_encrypted = session.enable_encryption
|
||||
ctrl = session.control
|
||||
jid = str(jid)
|
||||
|
||||
session.acknowledge_termination()
|
||||
gajim.connections[account].delete_session(jid, session.thread_id)
|
||||
|
||||
if ctrl:
|
||||
# replace the old session in this control with a new one
|
||||
new_sess = gajim.connections[account].make_new_session(jid)
|
||||
win = ctrl.parent_win
|
||||
|
||||
ctrl.set_session(new_sess)
|
||||
gajim.connections[account].delete_session(jid,
|
||||
session.thread_id)
|
||||
|
||||
if not jid in win._controls[account]:
|
||||
jid = gajim.get_jid_without_resource(jid)
|
||||
|
||||
del win._controls[account][jid][session.thread_id]
|
||||
win._controls[account][jid][new_sess.thread_id] = ctrl
|
||||
|
||||
if was_encrypted:
|
||||
ctrl.print_esession_details()
|
||||
conn = gajim.connections[account]
|
||||
conn.delete_session(jid, session.thread_id)
|
||||
|
||||
return
|
||||
|
||||
|
|
|
@ -128,13 +128,15 @@ class MessageControl:
|
|||
|
||||
self.session = session
|
||||
|
||||
new_key = None
|
||||
if session:
|
||||
session.control = self
|
||||
new_key = session.thread_id
|
||||
|
||||
if oldsession:
|
||||
self.parent_win.change_thread_key(
|
||||
self.contact.jid, self.account,
|
||||
oldsession.thread_id, session.thread_id)
|
||||
oldsession.thread_id, new_key)
|
||||
|
||||
if oldsession.enable_encryption:
|
||||
self.print_esession_details()
|
||||
|
@ -147,7 +149,14 @@ class MessageControl:
|
|||
jid = self.contact.jid
|
||||
original_message = message
|
||||
|
||||
if gajim.otr_module and jid not in gajim.otr_dont_append_tag:
|
||||
if not self.session:
|
||||
sess = gajim.connections[self.account].make_new_session(jid)
|
||||
self.set_session(sess)
|
||||
self.parent_win.move_from_sessionless(self)
|
||||
|
||||
xep_200 = bool(self.session) and self.session.enable_encryption
|
||||
|
||||
if gajim.otr_module and not xep_200 and (jid not in gajim.otr_dont_append_tag):
|
||||
if type == 'chat' and isinstance(message, unicode):
|
||||
d = {'kwargs': {'keyID': keyID, 'type': type,
|
||||
'chatstate': chatstate,
|
||||
|
|
|
@ -54,8 +54,15 @@ class MessageWindow(object):
|
|||
) = range(5)
|
||||
|
||||
def __init__(self, acct, type, parent_window=None, parent_paned=None):
|
||||
# A dictionary of dictionaries where _contacts[account][jid] == A MessageControl
|
||||
# A dictionary of dictionaries of dictionaries
|
||||
# where _contacts[account][jid][thread_id] == A MessageControl
|
||||
self._controls = {}
|
||||
|
||||
# a dictionary of dictionaries where
|
||||
# sessionless_ctrls[account][jid] = a list of MessageControls that don't have
|
||||
# sessions attached
|
||||
self.sessionless_ctrls = {}
|
||||
|
||||
# If None, the window is not tied to any specific account
|
||||
self.account = acct
|
||||
# If None, the window is not tied to any specific type
|
||||
|
@ -146,6 +153,11 @@ class MessageWindow(object):
|
|||
if self._controls.has_key(old_name):
|
||||
self._controls[new_name] = self._controls[old_name]
|
||||
del self._controls[old_name]
|
||||
|
||||
if self.sessionless_ctrls.has_key(old_name):
|
||||
self.sessionless_ctrls[new_name] = self.sessionless_ctrls[old_name]
|
||||
del self.sessionless_ctrls[old_name]
|
||||
|
||||
for ctrl in self.controls():
|
||||
if ctrl.account == old_name:
|
||||
ctrl.account = new_name
|
||||
|
@ -157,6 +169,11 @@ class MessageWindow(object):
|
|||
for jid_dict in self._controls.values():
|
||||
for dict in jid_dict.values():
|
||||
n += len(dict)
|
||||
|
||||
for jid_dict in self.sessionless_ctrls.values():
|
||||
for ctrls in jid_dict.values():
|
||||
n += len(ctrls)
|
||||
|
||||
return n
|
||||
|
||||
def resize(self, width, height):
|
||||
|
@ -197,6 +214,7 @@ class MessageWindow(object):
|
|||
for ctrl in self.controls():
|
||||
ctrl.shutdown()
|
||||
self._controls.clear()
|
||||
self.sessionless_ctrls.clear()
|
||||
# Clean up handlers connected to the parent window, this is important since
|
||||
# self.window may be the RosterWindow
|
||||
for i in self.handlers.keys():
|
||||
|
@ -206,14 +224,24 @@ class MessageWindow(object):
|
|||
del self.handlers
|
||||
|
||||
def new_tab(self, control):
|
||||
fjid = control.get_full_jid()
|
||||
|
||||
if control.session:
|
||||
if not self._controls.has_key(control.account):
|
||||
self._controls[control.account] = {}
|
||||
fjid = control.get_full_jid()
|
||||
|
||||
if not self._controls[control.account].has_key(fjid):
|
||||
self._controls[control.account][fjid] = {}
|
||||
|
||||
self._controls[control.account][fjid][control.session.thread_id] = control
|
||||
else:
|
||||
if not self.sessionless_ctrls.has_key(control.account):
|
||||
self.sessionless_ctrls[control.account] = {}
|
||||
|
||||
if not self.sessionless_ctrls[control.account].has_key(fjid):
|
||||
self.sessionless_ctrls[control.account][fjid] = []
|
||||
|
||||
self.sessionless_ctrls[control.account][fjid].append(control)
|
||||
|
||||
if self.get_num_controls() == 2:
|
||||
# is first conversation_textview scrolled down ?
|
||||
|
@ -443,7 +471,6 @@ class MessageWindow(object):
|
|||
|
||||
fjid = ctrl.get_full_jid()
|
||||
jid = gajim.get_jid_without_resource(fjid)
|
||||
thread_id = ctrl.session.thread_id
|
||||
|
||||
fctrls = self.get_controls(fjid, ctrl.account)
|
||||
bctrls = self.get_controls(jid, ctrl.account)
|
||||
|
@ -458,13 +485,20 @@ class MessageWindow(object):
|
|||
|
||||
self.notebook.remove_page(self.notebook.page_num(ctrl.widget))
|
||||
|
||||
del self._controls[ctrl.account][fjid][thread_id]
|
||||
if ctrl.session:
|
||||
dict = self._controls
|
||||
idx = ctrl.session.thread_id
|
||||
else:
|
||||
dict = self.sessionless_ctrls
|
||||
idx = dict[ctrl.account][fjid].index(ctrl)
|
||||
|
||||
if len(self._controls[ctrl.account][fjid]) == 0:
|
||||
del self._controls[ctrl.account][fjid]
|
||||
del dict[ctrl.account][fjid][idx]
|
||||
|
||||
if len(self._controls[ctrl.account]) == 0:
|
||||
del self._controls[ctrl.account]
|
||||
if len(dict[ctrl.account][fjid]) == 0:
|
||||
del dict[ctrl.account][fjid]
|
||||
|
||||
if len(dict[ctrl.account]) == 0:
|
||||
del dict[ctrl.account]
|
||||
|
||||
self.check_tabs()
|
||||
self.show_title()
|
||||
|
@ -591,7 +625,16 @@ class MessageWindow(object):
|
|||
|
||||
def get_controls(self, jid, acct):
|
||||
try:
|
||||
return self._controls[acct][jid].values()
|
||||
sessioned = self._controls[acct][jid].values()
|
||||
except KeyError:
|
||||
sessioned = []
|
||||
|
||||
sessionless = self.sessionless_controls(acct, jid)
|
||||
return sessioned + sessionless
|
||||
|
||||
def sessionless_controls(self, acct, jid):
|
||||
try:
|
||||
return self.sessionless_ctrls[acct][jid]
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
|
@ -604,6 +647,15 @@ class MessageWindow(object):
|
|||
return
|
||||
self._controls[acct][new_jid] = ctrls
|
||||
del self._controls[acct][old_jid]
|
||||
|
||||
try:
|
||||
ctrls = self.sessionless_ctrls[acct][old_jid]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
self.sessionless_ctrls[acct][new_jid] = ctrls
|
||||
del self.sessionless_ctrls[acct][new_jid]
|
||||
|
||||
if old_jid in gajim.last_message_time[acct]:
|
||||
gajim.last_message_time[acct][new_jid] = \
|
||||
gajim.last_message_time[acct][old_jid]
|
||||
|
@ -611,20 +663,54 @@ class MessageWindow(object):
|
|||
|
||||
def change_thread_key(self, jid, acct, old_thread_id, new_thread_id):
|
||||
'''Change the thread_id key of a control'''
|
||||
try:
|
||||
# Check if control exists
|
||||
ctrl = self._controls[acct][jid][old_thread_id]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
self._controls[acct][jid][new_thread_id] = ctrl
|
||||
if jid in self._controls[acct]:
|
||||
ctrl = self._controls[acct][jid][old_thread_id]
|
||||
else:
|
||||
jid = gajim.get_jid_without_resource(jid)
|
||||
ctrl = self._controls[acct][jid][old_thread_id]
|
||||
|
||||
del self._controls[acct][jid][old_thread_id]
|
||||
|
||||
if new_thread_id:
|
||||
self._controls[acct][jid][new_thread_id] = ctrl
|
||||
else:
|
||||
if acct not in self.sessionless_ctrls:
|
||||
self.sessionless_ctrls[acct] = {}
|
||||
|
||||
if jid not in self.sessionless_ctrls[acct]:
|
||||
self.sessionless_ctrls[acct][jid] = []
|
||||
|
||||
self.sessionless_ctrls[acct][jid].append(ctrl)
|
||||
|
||||
def move_from_sessionless(self, ctrl):
|
||||
'''a control just got a session, move it to the proper holding cell'''
|
||||
acct = ctrl.account
|
||||
jid = ctrl.get_full_jid()
|
||||
|
||||
idx = self.sessionless_ctrls[acct][jid].index(ctrl)
|
||||
|
||||
del self.sessionless_ctrls[acct][jid][idx]
|
||||
|
||||
if not self._controls.has_key(acct):
|
||||
self._controls[acct] = {}
|
||||
|
||||
if not self.sessionless_ctrls[acct].has_key(jid):
|
||||
self._controls[acct][jid] = {}
|
||||
|
||||
thread_id = ctrl.session.thread_id
|
||||
|
||||
self._controls[acct][jid][thread_id] = ctrl
|
||||
|
||||
def controls(self):
|
||||
for jid_dict in self._controls.values():
|
||||
for ctrl_dict in jid_dict.values():
|
||||
for ctrl in ctrl_dict.values():
|
||||
yield ctrl
|
||||
for jid_dict in self.sessionless_ctrls.values():
|
||||
for ctrl_dict in jid_dict.values():
|
||||
for ctrl in ctrl_dict:
|
||||
yield ctrl
|
||||
|
||||
def move_to_next_unread_tab(self, forward):
|
||||
ind = self.notebook.get_current_page()
|
||||
|
@ -834,11 +920,10 @@ class MessageWindowMgr(gobject.GObject):
|
|||
|
||||
def get_window(self, jid, acct):
|
||||
for win in self.windows():
|
||||
try:
|
||||
if win._controls[acct][jid]:
|
||||
if (acct in win._controls and jid in win._controls[acct]) or \
|
||||
(acct in win.sessionless_ctrls and jid in win.sessionless_ctrls[acct]):
|
||||
return win
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
def get_gc_control(self, jid, acct):
|
||||
|
|
|
@ -2100,8 +2100,6 @@ class RosterWindow:
|
|||
|
||||
self.quit_on_next_offline = 0
|
||||
for acct in accounts:
|
||||
gajim.connections[acct].terminate_sessions()
|
||||
|
||||
if gajim.connections[acct].connected:
|
||||
self.quit_on_next_offline += 1
|
||||
self.send_status(acct, 'offline', message)
|
||||
|
@ -3214,7 +3212,7 @@ class RosterWindow:
|
|||
import tictactoe
|
||||
|
||||
sess = gajim.connections[account].make_new_session(jid,
|
||||
klass=tictactoe.TicTacToeSession)
|
||||
cls=tictactoe.TicTacToeSession)
|
||||
sess.begin()
|
||||
|
||||
def on_execute_command(self, widget, contact, account, resource=None):
|
||||
|
|
|
@ -20,11 +20,19 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
|
|||
self.control = None
|
||||
|
||||
def acknowledge_termination(self):
|
||||
# the other party terminated the session. we'll keep the control around, though.
|
||||
stanza_session.EncryptedStanzaSession.acknowledge_termination(self)
|
||||
|
||||
if self.control:
|
||||
self.control.session = None
|
||||
if self.enable_encryption:
|
||||
self.control.print_esession_details()
|
||||
|
||||
self.control.set_session(None)
|
||||
|
||||
def terminate(self):
|
||||
stanza_session.EncryptedStanzaSession.terminate(self)
|
||||
|
||||
if self.control:
|
||||
self.control.set_session(None)
|
||||
|
||||
# extracts chatstate from a <message/> stanza
|
||||
def get_chatstate(self, msg, msgtxt):
|
||||
|
@ -53,8 +61,9 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
|
|||
return (composing_xep, chatstate)
|
||||
|
||||
# dispatch a received <message> stanza
|
||||
def received(self, full_jid_with_resource, msgtxt, tim, encrypted, subject, msg):
|
||||
def received(self, full_jid_with_resource, msgtxt, tim, encrypted, msg):
|
||||
msg_type = msg.getType()
|
||||
subject = msg.getSubject()
|
||||
|
||||
if not msg_type:
|
||||
msg_type = 'normal'
|
||||
|
@ -246,6 +255,19 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
|
|||
contact = gajim.interface.roster.add_to_not_in_the_roster(
|
||||
self.conn.name, jid, user_nick)
|
||||
|
||||
if not self.control:
|
||||
# look for an existing chat control without a session
|
||||
mw = gajim.interface.msg_win_mgr.get_window(jid, self.conn.name)
|
||||
|
||||
if mw:
|
||||
ctrls = mw.sessionless_controls(self.conn.name, jid)
|
||||
|
||||
if len(ctrls):
|
||||
ctrl = ctrls[0]
|
||||
self.control = ctrl
|
||||
ctrl.set_session(self)
|
||||
ctrl.parent_win.move_from_sessionless(ctrl)
|
||||
|
||||
if not self.control:
|
||||
# if no control exists and message comes from highest prio, the new
|
||||
# control shouldn't have a resource
|
||||
|
|
Loading…
Reference in New Issue