encrypt and decrypt GPG messages in a thread, and call a callback when it's finished (sending a message is now asyncronous). Fixes #4445

This commit is contained in:
Yann Leboulanger 2009-02-06 19:01:36 +00:00
parent 36f8280620
commit e0123f0c24
9 changed files with 157 additions and 88 deletions

View File

@ -312,6 +312,7 @@ class ChatControlBase(MessageControl):
spell.set_language(lang) spell.set_language(lang)
except (gobject.GError, RuntimeError): except (gobject.GError, RuntimeError):
dialogs.AspellDictError(lang) dialogs.AspellDictError(lang)
def on_msg_textview_populate_popup(self, textview, menu): def on_msg_textview_populate_popup(self, textview, menu):
'''we override the default context menu and we prepend an option to switch languages''' '''we override the default context menu and we prepend an option to switch languages'''
def _on_select_dictionary(widget, lang): def _on_select_dictionary(widget, lang):
@ -594,21 +595,19 @@ class ChatControlBase(MessageControl):
return True return True
return False return False
def send_message(self, message, keyID = '', type_ = 'chat', chatstate = None, def send_message(self, message, keyID='', type_='chat', chatstate=None,
msg_id = None, composing_xep = None, resource = None, msg_id=None, composing_xep=None, resource=None, process_command=True,
process_command = True, xhtml = None): xhtml=None, callback=None, callback_args=[]):
'''Send the given message to the active tab. Doesn't return None if error '''Send the given message to the active tab. Doesn't return None if error
''' '''
if not message or message == '\n': if not message or message == '\n':
return None return None
ret = None
if not process_command or not self._process_command(message): if not process_command or not self._process_command(message):
ret = MessageControl.send_message(self, message, keyID, type_ = type_, MessageControl.send_message(self, message, keyID, type_=type_,
chatstate = chatstate, msg_id = msg_id, chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
composing_xep = composing_xep, resource = resource, resource=resource, user_nick=self.user_nick, xhtml=xhtml,
user_nick = self.user_nick, xhtml = xhtml) callback=callback, callback_args=callback_args)
# Record message history # Record message history
self.save_sent_message(message) self.save_sent_message(message)
@ -620,8 +619,6 @@ class ChatControlBase(MessageControl):
message_buffer = self.msg_textview.get_buffer() message_buffer = self.msg_textview.get_buffer()
message_buffer.set_text('') # clear message buffer (and tv of course) message_buffer.set_text('') # clear message buffer (and tv of course)
return ret
def save_sent_message(self, message): def save_sent_message(self, message):
# save the message, so user can scroll though the list with key up/down # save the message, so user can scroll though the list with key up/down
size = len(self.sent_history) size = len(self.sent_history)
@ -1802,11 +1799,7 @@ class ChatControl(ChatControlBase):
gobject.source_remove(self.possible_inactive_timeout_id) gobject.source_remove(self.possible_inactive_timeout_id)
self._schedule_activity_timers() self._schedule_activity_timers()
id_ = ChatControlBase.send_message(self, message, keyID, def _on_sent(id_, contact, message, encrypted, xhtml):
type_ = 'chat', chatstate = chatstate_to_send,
composing_xep = composing_xep,
process_command = process_command, xhtml = xhtml)
if id_:
# XXX: Once we have fallback to disco, remove notexistant check # XXX: Once we have fallback to disco, remove notexistant check
if gajim.capscache.is_supported(contact, NS_RECEIPTS) \ if gajim.capscache.is_supported(contact, NS_RECEIPTS) \
and not gajim.capscache.is_supported(contact, and not gajim.capscache.is_supported(contact,
@ -1820,6 +1813,11 @@ class ChatControl(ChatControlBase):
encrypted = encrypted, xep0184_id = xep0184_id, encrypted = encrypted, xep0184_id = xep0184_id,
xhtml = xhtml) xhtml = xhtml)
ChatControlBase.send_message(self, message, keyID, type_='chat',
chatstate=chatstate_to_send, composing_xep=composing_xep,
process_command=process_command, xhtml=xhtml, callback=_on_sent,
callback_args=[contact, message, encrypted, xhtml])
def check_for_possible_paused_chatstate(self, arg): def check_for_possible_paused_chatstate(self, arg):
''' did we move mouse of that window or write something in message ''' did we move mouse of that window or write something in message
textview in the last 5 seconds? textview in the last 5 seconds?

View File

@ -1135,7 +1135,7 @@ class Connection(ConnectionHandlers):
def send_message(self, jid, msg, keyID, type_='chat', subject='', def send_message(self, jid, msg, keyID, type_='chat', subject='',
chatstate=None, msg_id=None, composing_xep=None, resource=None, chatstate=None, msg_id=None, composing_xep=None, resource=None,
user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None, user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None,
original_message=None, delayed=None): original_message=None, delayed=None, callback=None, callback_args=[]):
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return 1 return 1
try: try:
@ -1151,7 +1151,7 @@ class Connection(ConnectionHandlers):
from common.rst_xhtml_generator import create_xhtml from common.rst_xhtml_generator import create_xhtml
xhtml = create_xhtml(msg) xhtml = create_xhtml(msg)
if not msg and chatstate is None and form_node is None: if not msg and chatstate is None and form_node is None:
return 2 return
fjid = jid fjid = jid
if resource: if resource:
fjid += '/' + resource fjid += '/' + resource
@ -1168,30 +1168,61 @@ class Connection(ConnectionHandlers):
elif keyID.endswith('MISMATCH'): elif keyID.endswith('MISMATCH'):
error = _('The contact\'s key (%s) does not match the key assigned in Gajim.' % keyID[:8]) error = _('The contact\'s key (%s) does not match the key assigned in Gajim.' % keyID[:8])
else: else:
#encrypt def encrypt_thread(msg, keyID):
msgenc, error = self.gpg.encrypt(msg, [keyID]) # encrypt message. This function returns (msgenc, error)
if msgenc and not error: return self.gpg.encrypt(msg, [keyID])
msgtxt = '[This message is *encrypted* (See :XEP:`27`]' gajim.thread_interface(encrypt_thread, [msg, keyID],
lang = os.getenv('LANG') self._on_message_encrypted, [type_, msg, msgtxt,
if lang is not None and lang != 'en': # we're not english original_message, fjid, resource, jid, xhtml, subject,
# one in locale and one en chatstate, composing_xep, forward_from, delayed, session,
msgtxt = _('[This message is *encrypted* (See :XEP:`27`]') + \ form_node, user_nick, keyID, callback, callback_args])
' (' + msgtxt + ')' return
else:
# Encryption failed, do not send message self._on_message_encrypted(self, ('', error), type_, msg, msgtxt,
tim = localtime() original_message, fjid, resource, jid, xhtml, subject, chatstate,
self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim, session)) composing_xep, forward_from, delayed, session, form_node, user_nick,
return 3 keyID, callback, callback_args)
self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
resource, jid, xhtml, subject, msgenc, keyID, chatstate, composing_xep,
forward_from, delayed, session, form_node, user_nick, callback,
callback_args)
def _on_message_encrypted(self, output, type_, msg, msgtxt, original_message,
fjid, resource, jid, xhtml, subject, chatstate, composing_xep, forward_from,
delayed, session, form_node, user_nick, keyID, callback, callback_args):
msgenc, error = output
if msgenc and not error:
msgtxt = '[This message is *encrypted* (See :XEP:`27`]'
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 :XEP:`27`]') + \
' (' + msgtxt + ')'
self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
resource, jid, xhtml, subject, msgenc, keyID, chatstate,
composing_xep, forward_from, delayed, session, form_node, user_nick,
callback, callback_args)
return
# Encryption failed, do not send message
tim = localtime()
self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim, session))
def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid,
resource, jid, xhtml, subject, msgenc, keyID, chatstate, composing_xep,
forward_from, delayed, session, form_node, user_nick, callback,
callback_args):
if type_ == 'chat': if type_ == 'chat':
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt, typ = type_, msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_,
xhtml = xhtml) xhtml=xhtml)
else: else:
if subject: if subject:
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt, msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ='normal',
typ = 'normal', subject = subject, xhtml = xhtml) subject=subject, xhtml=xhtml)
else: else:
msg_iq = common.xmpp.Message(to = fjid, body = msgtxt, msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ='normal',
typ = 'normal', xhtml = xhtml) xhtml=xhtml)
if msgenc: if msgenc:
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc) msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
@ -1206,11 +1237,9 @@ class Connection(ConnectionHandlers):
# TODO: We might want to write a function so we don't need to # TODO: We might want to write a function so we don't need to
# reproduce that ugly if somewhere else. # reproduce that ugly if somewhere else.
if resource: if resource:
contact = gajim.contacts.get_contact(self.name, jid, contact = gajim.contacts.get_contact(self.name, jid, resource)
resource)
else: else:
contact = gajim.contacts. \ contact = gajim.contacts.get_contact_with_highest_priority(self.name,
get_contact_with_highest_priority(self.name,
jid) jid)
# chatstates - if peer supports xep85 or xep22, send chatstates # chatstates - if peer supports xep85 or xep22, send chatstates
@ -1226,16 +1255,13 @@ class Connection(ConnectionHandlers):
not gajim.capscache.is_supported(contact, not gajim.capscache.is_supported(contact,
'notexistant')): 'notexistant')):
# XEP-0085 # XEP-0085
msg_iq.setTag(chatstate, msg_iq.setTag(chatstate, namespace=common.xmpp.NS_CHATSTATES)
namespace = common.xmpp.NS_CHATSTATES)
if composing_xep in ('XEP-0022', 'asked_once') or \ if composing_xep in ('XEP-0022', 'asked_once') or \
not composing_xep: not composing_xep:
# XEP-0022 # XEP-0022
chatstate_node = msg_iq.setTag('x', chatstate_node = msg_iq.setTag('x', namespace=common.xmpp.NS_EVENT)
namespace = common.xmpp.NS_EVENT)
if chatstate is 'composing' or msgtxt: if chatstate is 'composing' or msgtxt:
chatstate_node.addChild( chatstate_node.addChild(name='composing')
name = 'composing')
if forward_from: if forward_from:
addresses = msg_iq.addChild('addresses', addresses = msg_iq.addChild('addresses',
@ -1255,8 +1281,7 @@ class Connection(ConnectionHandlers):
if msgtxt and gajim.config.get_per('accounts', self.name, if msgtxt and gajim.config.get_per('accounts', self.name,
'request_receipt') and gajim.capscache.is_supported(contact, 'request_receipt') and gajim.capscache.is_supported(contact,
common.xmpp.NS_RECEIPTS): common.xmpp.NS_RECEIPTS):
msg_iq.setTag('request', msg_iq.setTag('request', namespace=common.xmpp.NS_RECEIPTS)
namespace=common.xmpp.NS_RECEIPTS)
if session: if session:
# XEP-0201 # XEP-0201
@ -1294,7 +1319,8 @@ class Connection(ConnectionHandlers):
common.logger.LOG_DB_PATH common.logger.LOG_DB_PATH
self.dispatch('MSGSENT', (jid, msg, keyID)) self.dispatch('MSGSENT', (jid, msg, keyID))
return msg_id if callback:
callback(msg_id, *callback_args)
def send_stanza(self, stanza): def send_stanza(self, stanza):
''' send a stanza untouched ''' ''' send a stanza untouched '''

View File

@ -1772,6 +1772,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
encrypted = False encrypted = False
xep_200_encrypted = msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO) xep_200_encrypted = msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO)
session = None
if mtype != 'groupchat': if mtype != 'groupchat':
session = self.get_or_create_session(frm, thread_id) session = self.get_or_create_session(frm, thread_id)
@ -1853,10 +1854,22 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
keyID = gajim.config.get_per('accounts', self.name, 'keyid') keyID = gajim.config.get_per('accounts', self.name, 'keyid')
if keyID: if keyID:
decmsg = self.gpg.decrypt(encmsg, keyID) def decrypt_thread(encmsg, keyID):
# \x00 chars are not allowed in C (so in GTK) decmsg = self.gpg.decrypt(encmsg, keyID)
msgtxt = decmsg.replace('\x00', '') # \x00 chars are not allowed in C (so in GTK)
encrypted = 'xep27' msgtxt = decmsg.replace('\x00', '')
encrypted = 'xep27'
return (msgtxt, encrypted)
gajim.thread_interface(decrypt_thread, [encmsg, keyID],
self._on_message_decrypted, [mtype, msg, session, frm, jid,
invite, tim])
return
self._on_message_decrypted((msgtxt, encrypted), mtype, msg, session, frm,
jid, invite, tim)
def _on_message_decrypted(self, output, mtype, msg, session, frm, jid,
invite, tim):
msgtxt, encrypted = output
if mtype == 'error': if mtype == 'error':
self.dispatch_error_message(msg, msgtxt, session, frm, tim) self.dispatch_error_message(msg, msgtxt, session, frm, tim)
elif mtype == 'groupchat': elif mtype == 'groupchat':

View File

@ -63,6 +63,7 @@ If you start gajim from svn:
sys.exit(1) sys.exit(1)
interface = None # The actual interface (the gtk one for the moment) interface = None # The actual interface (the gtk one for the moment)
thread_interface = None # Interface to run a thread and then a callback
config = config.Config() config = config.Config()
version = config.get('version') version = config.get('version')
connections = {} # 'account name': 'account (connection.Connection) instance' connections = {} # 'account name': 'account (connection.Connection) instance'

View File

@ -134,7 +134,10 @@ class XMPPDispatcher(PlugIn):
self._owner.lastErrNode = None self._owner.lastErrNode = None
self._owner.lastErr = None self._owner.lastErr = None
self._owner.lastErrCode = None self._owner.lastErrCode = None
self.StreamInit() if hasattr(self._owner, 'StreamInit'):
self._owner.StreamInit()
else:
self.StreamInit()
def plugout(self): def plugout(self):
''' Prepares instance to be destructed. ''' ''' Prepares instance to be destructed. '''

View File

@ -155,11 +155,16 @@ class P2PClient(IdleObject):
if on_not_ok: if on_not_ok:
on_not_ok('Connection to host could not be established.') on_not_ok('Connection to host could not be established.')
return return
id = stanza.getThread() thread_id = stanza.getThread()
id_ = stanza.getID()
if not id_:
id_ = self.Dispatcher.getAnID()
if self.conn_holder.ids_of_awaiting_messages.has_key(self.fd): if self.conn_holder.ids_of_awaiting_messages.has_key(self.fd):
self.conn_holder.ids_of_awaiting_messages[self.fd].append(id) self.conn_holder.ids_of_awaiting_messages[self.fd].append((id_,
thread_id))
else: else:
self.conn_holder.ids_of_awaiting_messages[self.fd] = [id] self.conn_holder.ids_of_awaiting_messages[self.fd] = [(id_,
thread_id)]
def add_stanza(self, stanza, is_message=False): def add_stanza(self, stanza, is_message=False):
if self.Connection: if self.Connection:
@ -170,24 +175,32 @@ class P2PClient(IdleObject):
self.stanzaqueue.append((stanza, is_message)) self.stanzaqueue.append((stanza, is_message))
if is_message: if is_message:
id = stanza.getThread() id_ = stanza.getID()
if not id_:
id_ = self.Dispatcher.getAnID()
if self.conn_holder.ids_of_awaiting_messages.has_key(self.fd): if self.conn_holder.ids_of_awaiting_messages.has_key(self.fd):
self.conn_holder.ids_of_awaiting_messages[self.fd].append(id) self.conn_holder.ids_of_awaiting_messages[self.fd].append((id_,
thread_id))
else: else:
self.conn_holder.ids_of_awaiting_messages[self.fd] = [id] self.conn_holder.ids_of_awaiting_messages[self.fd] = [(id_,
thread_id)]
return True return True
def on_message_sent(self, connection_id): def on_message_sent(self, connection_id):
self.conn_holder.ids_of_awaiting_messages[connection_id].pop(0) id_, thread_id = \
self.conn_holder.ids_of_awaiting_messages[connection_id].pop(0)
if self.on_ok:
self.on_ok(id_)
# use on_ok only on first message. For others it's called in
# ClientZeroconf
self.on_ok = None
def on_connect(self, conn): def on_connect(self, conn):
self.Connection = conn self.Connection = conn
self.Connection.PlugIn(self) self.Connection.PlugIn(self)
dispatcher_nb.Dispatcher().PlugIn(self) dispatcher_nb.Dispatcher().PlugIn(self)
self._register_handlers() self._register_handlers()
if self.on_ok:
self.on_ok()
def StreamInit(self): def StreamInit(self):
''' Send an initial stream header. ''' ''' Send an initial stream header. '''
@ -393,8 +406,9 @@ class P2PConnection(IdleObject, PlugIn):
def read_timeout(self): def read_timeout(self):
ids = self.client.conn_holder.ids_of_awaiting_messages ids = self.client.conn_holder.ids_of_awaiting_messages
if self.fd in ids and len(ids[self.fd]) > 0: if self.fd in ids and len(ids[self.fd]) > 0:
for id in ids[self.fd]: for (id_, thread_id) in ids[self.fd]:
self._owner.Dispatcher.Event('', DATA_ERROR, (self.client.to, id)) self._owner.Dispatcher.Event('', DATA_ERROR, (self.client.to,
thread_id))
ids[self.fd] = [] ids[self.fd] = []
self.pollend() self.pollend()
@ -679,8 +693,7 @@ class ClientZeroconf:
stanza.setID(id_) stanza.setID(id_)
if conn.add_stanza(stanza, is_message): if conn.add_stanza(stanza, is_message):
if on_ok: if on_ok:
on_ok() on_ok(id_)
return id_
if item['address'] in self.ip_to_hash: if item['address'] in self.ip_to_hash:
hash_ = self.ip_to_hash[item['address']] hash_ = self.ip_to_hash[item['address']]
@ -690,14 +703,11 @@ class ClientZeroconf:
stanza.setID(id_) stanza.setID(id_)
if conn.add_stanza(stanza, is_message): if conn.add_stanza(stanza, is_message):
if on_ok: if on_ok:
on_ok() on_ok(id_)
return id_
# otherwise open new connection # otherwise open new connection
stanza.setID('zero') stanza.setID('zero')
P2PClient(None, item['address'], item['port'], self, P2PClient(None, item['address'], item['port'], self,
[(stanza, is_message)], to, on_ok=on_ok, on_not_ok=on_not_ok) [(stanza, is_message)], to, on_ok=on_ok, on_not_ok=on_not_ok)
return 'zero'
# vim: se ts=3: # vim: se ts=3:

View File

@ -364,7 +364,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
def send_message(self, jid, msg, keyID, type_='chat', subject='', def send_message(self, jid, msg, keyID, type_='chat', subject='',
chatstate=None, msg_id=None, composing_xep=None, resource=None, chatstate=None, msg_id=None, composing_xep=None, resource=None,
user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None, user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None,
original_message=None, delayed=None): original_message=None, delayed=None, callback=None, callback_args=[]):
fjid = jid fjid = jid
if msg and not xhtml and gajim.config.get( if msg and not xhtml and gajim.config.get(
@ -402,7 +402,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
# Encryption failed, do not send message # Encryption failed, do not send message
tim = time.localtime() tim = time.localtime()
self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim, session)) self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim, session))
return 3 return
if type_ == 'chat': if type_ == 'chat':
msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_, msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_,
@ -458,7 +458,7 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
if session.enable_encryption: if session.enable_encryption:
msg_iq = session.encrypt_stanza(msg_iq) msg_iq = session.encrypt_stanza(msg_iq)
def on_send_ok(): def on_send_ok(id):
no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for') no_log_for = gajim.config.get_per('accounts', self.name, 'no_log_for')
ji = gajim.get_jid_without_resource(jid) ji = gajim.get_jid_without_resource(jid)
if session.is_loggable() and self.name not in no_log_for and\ if session.is_loggable() and self.name not in no_log_for and\
@ -473,9 +473,12 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
else: else:
kind = 'single_msg_sent' kind = 'single_msg_sent'
gajim.logger.write(kind, jid, log_msg) gajim.logger.write(kind, jid, log_msg)
self.dispatch('MSGSENT', (jid, msg, keyID)) self.dispatch('MSGSENT', (jid, msg, keyID))
if callback:
callback(id, *callback_args)
def on_send_not_ok(reason): def on_send_not_ok(reason):
reason += ' ' + _('Your message could not be sent.') reason += ' ' + _('Your message could not be sent.')
self.dispatch('MSGERROR', [jid, -1, reason, None, None, session]) self.dispatch('MSGERROR', [jid, -1, reason, None, None, session])
@ -484,8 +487,6 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
if ret == -1: if ret == -1:
# Contact Offline # Contact Offline
self.dispatch('MSGERROR', [jid, -1, _('Contact is offline. Your message could not be sent.'), None, None, session]) self.dispatch('MSGERROR', [jid, -1, _('Contact is offline. Your message could not be sent.'), None, None, session])
return ret
return ret
def send_stanza(self, stanza): def send_stanza(self, stanza):

View File

@ -423,6 +423,7 @@ parser = optparser.OptionsParser(config_filename)
import roster_window import roster_window
import profile_window import profile_window
import config import config
from threading import Thread
class PassphraseRequest: class PassphraseRequest:
@ -482,6 +483,22 @@ class PassphraseRequest:
cancel_handler=_cancel) cancel_handler=_cancel)
self.dialog_created = True self.dialog_created = True
class ThreadInterface:
def __init__(self, func, func_args, callback, callback_args):
'''Call a function in a thread
:param func: the function to call in the thread
:param func_args: list or arguments for this function
:param callback: callback to call once function is finished
:param callback_args: list of arguments for this callback
'''
def thread_function(func, func_args, callback, callback_args):
output = func(*func_args)
gobject.idle_add(callback, output, *callback_args)
Thread(target=thread_function, args=(func, func_args, callback,
callback_args)).start()
class Interface: class Interface:
################################################################################ ################################################################################
@ -3071,6 +3088,7 @@ class Interface:
def __init__(self): def __init__(self):
gajim.interface = self gajim.interface = self
gajim.thread_interface = ThreadInterface
# This is the manager and factory of message windows set by the module # This is the manager and factory of message windows set by the module
self.msg_win_mgr = None self.msg_win_mgr = None
self.jabber_state_images = {'16': {}, '32': {}, 'opened': {}, self.jabber_state_images = {'16': {}, '32': {}, 'opened': {},

View File

@ -158,9 +158,9 @@ class MessageControl:
if crypto_changed: if crypto_changed:
self.print_esession_details() self.print_esession_details()
def send_message(self, message, keyID = '', type_ = 'chat', def send_message(self, message, keyID='', type_='chat', chatstate=None,
chatstate = None, msg_id = None, composing_xep = None, resource = None, msg_id=None, composing_xep=None, resource=None, user_nick=None, xhtml=None,
user_nick = None, xhtml = None): callback=None, callback_args=[]):
# Send the given message to the active tab. # Send the given message to the active tab.
# Doesn't return None if error # Doesn't return None if error
jid = self.contact.jid jid = self.contact.jid
@ -182,11 +182,10 @@ class MessageControl:
self.set_session(sess) self.set_session(sess)
# Send and update history # Send and update history
return conn.send_message(jid, message, keyID, type_ = type_, conn.send_message(jid, message, keyID, type_=type_, chatstate=chatstate,
chatstate = chatstate, msg_id = msg_id, msg_id=msg_id, composing_xep=composing_xep, resource=self.resource,
composing_xep = composing_xep, user_nick=user_nick, session=self.session,
resource = self.resource, user_nick = user_nick, original_message=original_message, xhtml=xhtml, callback=callback,
session = self.session, callback_args=callback_args)
original_message = original_message, xhtml = xhtml)
# vim: se ts=3: # vim: se ts=3: