add ad hoc commands for zeroconf. Fixes #2819

This commit is contained in:
Yann Leboulanger 2009-08-30 00:57:49 +02:00
parent 1e76981322
commit da799bcb59
4 changed files with 132 additions and 40 deletions

View File

@ -1,32 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface> <glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkMenu" id="zeroconf_contact_context_menu"> <widget class="GtkMenu" id="zeroconf_contact_context_menu">
<child> <child>
<widget class="GtkImageMenuItem" id="start_chat_menuitem"> <widget class="GtkImageMenuItem" id="start_chat_menuitem">
<property name="label">Start _Chat</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="label" translatable="yes">Start _Chat</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">False</property>
<child internal-child="image"> <child internal-child="image">
<widget class="GtkImage" id="image1701"> <widget class="GtkImage" id="image9">
<property name="visible">True</property> <property name="visible">True</property>
<property name="stock">gtk-jump-to</property> <property name="stock">gtk-jump-to</property>
<property name="icon_size">1</property> <property name="icon-size">1</property>
</widget> </widget>
</child> </child>
</widget> </widget>
</child> </child>
<child> <child>
<widget class="GtkImageMenuItem" id="send_file_menuitem"> <widget class="GtkImageMenuItem" id="send_file_menuitem">
<property name="label">Send _File</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="label" translatable="yes">Send _File</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">False</property>
<child internal-child="image"> <child internal-child="image">
<widget class="GtkImage" id="image1706"> <widget class="GtkImage" id="image10">
<property name="visible">True</property> <property name="visible">True</property>
<property name="stock">gtk-save</property> <property name="stock">gtk-save</property>
<property name="icon_size">1</property> <property name="icon-size">1</property>
</widget> </widget>
</child> </child>
</widget> </widget>
@ -36,67 +38,85 @@
<property name="visible">True</property> <property name="visible">True</property>
</widget> </widget>
</child> </child>
<child>
<widget class="GtkImageMenuItem" id="execute_command_menuitem">
<property name="label" translatable="yes">Execute Command...</property>
<property name="visible">True</property>
<property name="use_stock">False</property>
<child internal-child="image">
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-execute</property>
<property name="icon-size">1</property>
</widget>
</child>
</widget>
</child>
<child> <child>
<widget class="GtkImageMenuItem" id="manage_contact"> <widget class="GtkImageMenuItem" id="manage_contact">
<property name="label">_Manage Contact</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_Manage Contact</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">False</property>
<child> <child>
<widget class="GtkMenu" id="menu2"> <widget class="GtkMenu" id="menu2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<child> <child>
<widget class="GtkImageMenuItem" id="rename_menuitem"> <widget class="GtkImageMenuItem" id="rename_menuitem">
<property name="label" translatable="yes">_Rename</property> <property name="label">_Rename</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">False</property>
<child internal-child="image"> <child internal-child="image">
<widget class="GtkImage" id="image1705"> <widget class="GtkImage" id="image7">
<property name="visible">True</property> <property name="visible">True</property>
<property name="stock">gtk-refresh</property> <property name="stock">gtk-refresh</property>
<property name="icon_size">1</property> <property name="icon-size">1</property>
</widget> </widget>
</child> </child>
</widget> </widget>
</child> </child>
<child> <child>
<widget class="GtkImageMenuItem" id="edit_groups_menuitem"> <widget class="GtkImageMenuItem" id="edit_groups_menuitem">
<property name="label" translatable="yes">Edit _Groups</property> <property name="label">Edit _Groups</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">False</property>
<child internal-child="image"> <child internal-child="image">
<widget class="GtkImage" id="menu-item-image21"> <widget class="GtkImage" id="image6">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-edit</property> <property name="stock">gtk-edit</property>
<property name="icon_size">1</property> <property name="icon-size">1</property>
</widget> </widget>
</child> </child>
</widget> </widget>
</child> </child>
<child> <child>
<widget class="GtkImageMenuItem" id="assign_openpgp_key_menuitem"> <widget class="GtkImageMenuItem" id="assign_openpgp_key_menuitem">
<property name="label" translatable="yes">Assign Open_PGP Key</property> <property name="label">Assign Open_PGP Key</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">False</property>
<signal name="activate" handler="on_assign_openpgp_key_menuitem_activate"/> <signal name="activate" handler="on_assign_openpgp_key_menuitem_activate"/>
<child internal-child="image"> <child internal-child="image">
<widget class="GtkImage" id="image1707"> <widget class="GtkImage" id="image5">
<property name="visible">True</property> <property name="visible">True</property>
<property name="stock">gtk-dialog-authentication</property> <property name="stock">gtk-dialog-authentication</property>
<property name="icon_size">1</property> <property name="icon-size">1</property>
</widget> </widget>
</child> </child>
</widget> </widget>
</child> </child>
<child> <child>
<widget class="GtkImageMenuItem" id="add_special_notification_menuitem"> <widget class="GtkImageMenuItem" id="add_special_notification_menuitem">
<property name="label">Add Special _Notification</property>
<property name="no_show_all">True</property> <property name="no_show_all">True</property>
<property name="label" translatable="yes">Add Special _Notification</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">False</property>
<child internal-child="image"> <child internal-child="image">
<widget class="GtkImage" id="image1708"> <widget class="GtkImage" id="image3">
<property name="visible">True</property> <property name="visible">True</property>
<property name="stock">gtk-info</property> <property name="stock">gtk-info</property>
<property name="icon_size">1</property> <property name="icon-size">1</property>
</widget> </widget>
</child> </child>
</widget> </widget>
@ -104,11 +124,10 @@
</widget> </widget>
</child> </child>
<child internal-child="image"> <child internal-child="image">
<widget class="GtkImage" id="menu-item-image19"> <widget class="GtkImage" id="image8">
<property name="visible">True</property> <property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="stock">gtk-properties</property> <property name="stock">gtk-properties</property>
<property name="icon_size">1</property> <property name="icon-size">1</property>
</widget> </widget>
</child> </child>
</widget> </widget>
@ -120,20 +139,28 @@
</child> </child>
<child> <child>
<widget class="GtkImageMenuItem" id="information_menuitem"> <widget class="GtkImageMenuItem" id="information_menuitem">
<property name="label">gtk-info</property> <property name="label">_Information</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">True</property> <property name="use_stock">False</property>
<child internal-child="image">
<widget class="GtkImage" id="image4">
<property name="visible">True</property>
<property name="stock">gtk-info</property>
<property name="icon-size">1</property>
</widget>
</child>
</widget> </widget>
</child> </child>
<child> <child>
<widget class="GtkImageMenuItem" id="history_menuitem"> <widget class="GtkImageMenuItem" id="history_menuitem">
<property name="label" translatable="yes">_History</property> <property name="label">_History</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<property name="use_stock">False</property>
<child internal-child="image"> <child internal-child="image">
<widget class="GtkImage" id="image1718"> <widget class="GtkImage" id="image2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="stock">gtk-justify-fill</property> <property name="stock">gtk-justify-fill</property>
<property name="icon_size">1</property> <property name="icon-size">1</property>
</widget> </widget>
</child> </child>
</widget> </widget>

View File

@ -167,6 +167,8 @@ class P2PClient(IdleObject):
self.conn_holder.ids_of_awaiting_messages[self.fd] = [(id_, self.conn_holder.ids_of_awaiting_messages[self.fd] = [(id_,
thread_id)] thread_id)]
self.on_responses = {}
def add_stanza(self, stanza, is_message=False): def add_stanza(self, stanza, is_message=False):
if self.Connection: if self.Connection:
if self.Connection.state == -1: if self.Connection.state == -1:
@ -282,6 +284,9 @@ class P2PClient(IdleObject):
self.onreceive(None) self.onreceive(None)
return True return True
def remove_timeout(self):
pass
def _register_handlers(self): def _register_handlers(self):
self.RegisterHandler('message', lambda conn, data:self._caller._messageCB( self.RegisterHandler('message', lambda conn, data:self._caller._messageCB(
self.Server, conn, data)) self.Server, conn, data))
@ -297,6 +302,8 @@ class P2PClient(IdleObject):
common.xmpp.NS_BYTESTREAM) common.xmpp.NS_BYTESTREAM)
self.RegisterHandler('iq', self._caller._bytestreamErrorCB, 'error', self.RegisterHandler('iq', self._caller._bytestreamErrorCB, 'error',
common.xmpp.NS_BYTESTREAM) common.xmpp.NS_BYTESTREAM)
self.RegisterHandler('iq', self._caller._DiscoverItemsGetCB, 'get',
common.xmpp.NS_DISCO_ITEMS)
class P2PConnection(IdleObject, PlugIn): class P2PConnection(IdleObject, PlugIn):
def __init__(self, sock_hash, _sock, host=None, port=None, caller=None, def __init__(self, sock_hash, _sock, host=None, port=None, caller=None,
@ -717,4 +724,35 @@ class ClientZeroconf:
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)
def SendAndWaitForResponse(self, stanza, timeout=None, func=None, args=None):
'''
Send stanza and wait for recipient's response to it. Will call transports
on_timeout callback if response is not retrieved in time.
Be aware: Only timeout of latest call of SendAndWait is active.
'''
# if timeout is None:
# timeout = DEFAULT_TIMEOUT_SECONDS
def on_ok(_waitid):
# if timeout:
# self._owner.set_timeout(timeout)
to = stanza.getTo()
conn = None
if to in self.recipient_to_hash:
conn = self.connections[self.recipient_to_hash[to]]
elif item['address'] in self.ip_to_hash:
hash_ = self.ip_to_hash[item['address']]
if self.hash_to_port[hash_] == item['port']:
conn = self.connections[hash_]
if func:
conn.Dispatcher.on_responses[_waitid] = (func, args)
conn.onreceive(conn.Dispatcher._WaitForData)
conn.Dispatcher._expected[_waitid] = None
self.send(stanza, on_ok=on_ok)
def SendAndCallForResponse(self, stanza, func=None, args=None):
''' Put stanza on the wire and call back when recipient replies.
Additional callback arguments can be specified in args. '''
self.SendAndWaitForResponse(stanza, 0, func, args)
# vim: se ts=3: # vim: se ts=3:

View File

@ -34,6 +34,10 @@ import common.xmpp
from common import helpers from common import helpers
from common import gajim from common import gajim
from common.zeroconf import zeroconf from common.zeroconf import zeroconf
from common.commands import ConnectionCommands
import logging
log = logging.getLogger('gajim.c.z.connection_handlers_zeroconf')
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd', STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible'] 'invisible']
@ -45,7 +49,7 @@ HAS_IDLE = True
try: try:
import idle import idle
except Exception: except Exception:
gajim.log.debug(_('Unable to load idle module')) log.debug(_('Unable to load idle module'))
HAS_IDLE = False HAS_IDLE = False
from common import connection_handlers from common import connection_handlers
@ -156,7 +160,7 @@ class ConnectionBytestream(connection_handlers.ConnectionBytestream):
self.connection.send(iq) self.connection.send(iq)
def _bytestreamSetCB(self, con, iq_obj): def _bytestreamSetCB(self, con, iq_obj):
gajim.log.debug('_bytestreamSetCB') log.debug('_bytestreamSetCB')
target = unicode(iq_obj.getAttr('to')) target = unicode(iq_obj.getAttr('to'))
id_ = unicode(iq_obj.getAttr('id')) id_ = unicode(iq_obj.getAttr('id'))
query = iq_obj.getTag('query') query = iq_obj.getTag('query')
@ -198,7 +202,7 @@ class ConnectionBytestream(connection_handlers.ConnectionBytestream):
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
def _ResultCB(self, con, iq_obj): def _ResultCB(self, con, iq_obj):
gajim.log.debug('_ResultCB') log.debug('_ResultCB')
# if we want to respect jep-0065 we have to check for proxy # if we want to respect jep-0065 we have to check for proxy
# activation result in any result iq # activation result in any result iq
real_id = unicode(iq_obj.getAttr('id')) real_id = unicode(iq_obj.getAttr('id'))
@ -215,7 +219,7 @@ class ConnectionBytestream(connection_handlers.ConnectionBytestream):
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
def _bytestreamResultCB(self, con, iq_obj): def _bytestreamResultCB(self, con, iq_obj):
gajim.log.debug('_bytestreamResultCB') log.debug('_bytestreamResultCB')
frm = unicode(iq_obj.getFrom()) frm = unicode(iq_obj.getFrom())
real_id = unicode(iq_obj.getAttr('id')) real_id = unicode(iq_obj.getAttr('id'))
query = iq_obj.getTag('query') query = iq_obj.getTag('query')
@ -283,7 +287,7 @@ class ConnectionBytestream(connection_handlers.ConnectionBytestream):
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
def _siResultCB(self, con, iq_obj): def _siResultCB(self, con, iq_obj):
gajim.log.debug('_siResultCB') log.debug('_siResultCB')
self.peerhost = con._owner.Connection._sock.getsockname() self.peerhost = con._owner.Connection._sock.getsockname()
id_ = iq_obj.getAttr('id') id_ = iq_obj.getAttr('id')
if id_ not in self.files_props: if id_ not in self.files_props:
@ -321,7 +325,7 @@ class ConnectionBytestream(connection_handlers.ConnectionBytestream):
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
def _siSetCB(self, con, iq_obj): def _siSetCB(self, con, iq_obj):
gajim.log.debug('_siSetCB') log.debug('_siSetCB')
jid = unicode(iq_obj.getFrom()) jid = unicode(iq_obj.getFrom())
si = iq_obj.getTag('si') si = iq_obj.getTag('si')
profile = si.getAttr('profile') profile = si.getAttr('profile')
@ -353,7 +357,7 @@ class ConnectionBytestream(connection_handlers.ConnectionBytestream):
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
def _siErrorCB(self, con, iq_obj): def _siErrorCB(self, con, iq_obj):
gajim.log.debug('_siErrorCB') log.debug('_siErrorCB')
si = iq_obj.getTag('si') si = iq_obj.getTag('si')
profile = si.getAttr('profile') profile = si.getAttr('profile')
if profile != common.xmpp.NS_FILE: if profile != common.xmpp.NS_FILE:
@ -371,10 +375,12 @@ class ConnectionBytestream(connection_handlers.ConnectionBytestream):
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, '')) self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
raise common.xmpp.NodeProcessed raise common.xmpp.NodeProcessed
class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream, connection_handlers.ConnectionHandlersBase): class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream,
ConnectionCommands, connection_handlers.ConnectionHandlersBase):
def __init__(self): def __init__(self):
ConnectionVcard.__init__(self) ConnectionVcard.__init__(self)
ConnectionBytestream.__init__(self) ConnectionBytestream.__init__(self)
ConnectionCommands.__init__(self)
connection_handlers.ConnectionHandlersBase.__init__(self) connection_handlers.ConnectionHandlersBase.__init__(self)
try: try:
@ -386,7 +392,7 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream, connecti
def _messageCB(self, ip, con, msg): def _messageCB(self, ip, con, msg):
'''Called when we receive a message''' '''Called when we receive a message'''
gajim.log.debug('Zeroconf MessageCB') log.debug('Zeroconf MessageCB')
frm = msg.getFrom() frm = msg.getFrom()
mtype = msg.getType() mtype = msg.getType()
@ -480,4 +486,21 @@ class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestream, connecti
def remove_transfer(self, file_props, remove_from_list = True): def remove_transfer(self, file_props, remove_from_list = True):
pass pass
def _DiscoverItemsGetCB(self, con, iq_obj):
log.debug('DiscoverItemsGetCB')
if not self.connection or self.connected < 2:
return
if self.commandItemsQuery(con, iq_obj):
raise common.xmpp.NodeProcessed
node = iq_obj.getTagAttr('query', 'node')
if node is None:
result = iq_obj.buildReply('result')
self.connection.send(result)
raise common.xmpp.NodeProcessed
if node==common.xmpp.NS_COMMANDS:
self.commandListQuery(con, iq_obj)
raise common.xmpp.NodeProcessed
# vim: se ts=3: # vim: se ts=3:

View File

@ -5305,6 +5305,7 @@ class RosterWindow:
'zeroconf_contact_context_menu') 'zeroconf_contact_context_menu')
start_chat_menuitem = xml.get_widget('start_chat_menuitem') start_chat_menuitem = xml.get_widget('start_chat_menuitem')
execute_command_menuitem = xml.get_widget('execute_command_menuitem')
rename_menuitem = xml.get_widget('rename_menuitem') rename_menuitem = xml.get_widget('rename_menuitem')
edit_groups_menuitem = xml.get_widget('edit_groups_menuitem') edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
send_file_menuitem = xml.get_widget('send_file_menuitem') send_file_menuitem = xml.get_widget('send_file_menuitem')
@ -5364,6 +5365,9 @@ class RosterWindow:
else: else:
send_file_menuitem.set_sensitive(False) send_file_menuitem.set_sensitive(False)
execute_command_menuitem.connect('activate',
self.on_execute_command, contact, account)
rename_menuitem.connect('activate', self.on_rename, 'contact', jid, rename_menuitem.connect('activate', self.on_rename, 'contact', jid,
account) account)
if contact.show in ('offline', 'error'): if contact.show in ('offline', 'error'):