From 5f7ad6fea1b0f42f3c85840792c022843b326978 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Thu, 11 Jan 2007 17:58:44 +0000 Subject: [PATCH] always give the same number of arguments to dbus methods, use signatures. fixes #2784 simon, could you have a look to this patch ? does it seems correct ? --- src/gajim-remote.py | 18 ++- src/remote_control.py | 307 ++++++++++++++++++------------------------ 2 files changed, 145 insertions(+), 180 deletions(-) diff --git a/src/gajim-remote.py b/src/gajim-remote.py index 9e5d56dd5..e350409e5 100755 --- a/src/gajim-remote.py +++ b/src/gajim-remote.py @@ -434,11 +434,26 @@ class GajimRemote: ''' Make check if all necessary arguments are given ''' argv_len = self.argv_len - 2 args = self.commands[self.command][1] + if len(args) < argv_len: + send_error(_('Too many arguments. \n' + 'Type "%s help %s" for more info') % (BASENAME, self.command)) if len(args) > argv_len: if args[argv_len][2]: send_error(_('Argument "%s" is not specified. \n' 'Type "%s help %s" for more info') % (args[argv_len][0], BASENAME, self.command)) + self.arguments = [] + i = 0 + for arg in sys.argv[2:]: + i += 1 + if i < len(args): + self.arguments.append(arg) + else: + # it's latest argument with spaces + self.arguments.append(' '.join(sys.argv[i+1:])) + break + # add empty string for missing args + self.arguments += ['']*(len(args)-i) def handle_uri(self): if not sys.argv[2:][0].startswith('xmpp:'): @@ -458,8 +473,7 @@ class GajimRemote: def call_remote_method(self): ''' calls self.method with arguments from sys.argv[2:] ''' - args = sys.argv[2:] - args = [i.decode(PREFERRED_ENCODING) for i in sys.argv[2:]] + args = [i.decode(PREFERRED_ENCODING) for i in self.arguments] args = [dbus.String(i) for i in args] try: res = self.method(*args) diff --git a/src/remote_control.py b/src/remote_control.py index 639b8802c..ea159377e 100644 --- a/src/remote_control.py +++ b/src/remote_control.py @@ -105,111 +105,99 @@ class SignalObject(dbus.service.Object): # register our dbus API dbus.service.Object.__init__(self, bus_name, OBJ_PATH) - # FIXME: what are the signatures for these signals? - - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def Roster(self, account_and_data): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def AccountPresence(self, status_and_account): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def ContactPresence(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def ContactAbsence(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def NewMessage(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def Subscribe(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def Subscribed(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def Unsubscribed(self, account_and_jid): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def NewAccount(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def VcardInfo(self, account_and_vcard): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def LastStatusTime(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def OsInfo(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def GCPresence(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def GCMessage(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def RosterInfo(self, account_and_array): pass - @dbus.service.signal(INTERFACE) + @dbus.service.signal(INTERFACE, signature='av') def NewGmail(self, account_and_array): pass def raise_signal(self, signal, arg): '''raise a signal, with a single argument of unspecified type - Instead of obj.raise_signal("Foo", bar), use obj.Foo(bar).''' getattr(self, signal)(arg) - # FIXME: can't specify any introspect signature for any of these - # since they take a variable number of arguments (this is not - # recommended in a D-Bus interface) - - @dbus.service.method(INTERFACE) - def get_status(self, *args): - '''get_status(account = None) - returns status (show to be exact) which is the global one + @dbus.service.method(INTERFACE, in_signature='s', out_signature='s') + def get_status(self, account): + '''Returns status (show to be exact) which is the global one unless account is given''' - account = self._get_real_arguments(args, 1)[0] if not account: # If user did not ask for account, returns the global status - return helpers.get_global_show() + return DBUS_STRING(helpers.get_global_show()) # return show for the given account index = gajim.connections[account].connected return DBUS_STRING(gajim.SHOW_LIST[index]) - @dbus.service.method(INTERFACE) - def get_status_message(self, *args): - '''get_status(account = None) - returns status which is the global one + @dbus.service.method(INTERFACE, in_signature='s', out_signature='s') + def get_status_message(self, account): + '''Returns status which is the global one unless account is given''' - account = self._get_real_arguments(args, 1)[0] if not account: # If user did not ask for account, returns the global status - return str(helpers.get_global_status()) + return DBUS_STRING(str(helpers.get_global_status())) # return show for the given account status = gajim.connections[account].status return DBUS_STRING(status) - @dbus.service.method(INTERFACE) - def get_account_and_contact(self, account, jid): - ''' get the account (if not given) and contact instance from jid''' + def _get_account_and_contact(self, account, jid): + '''get the account (if not given) and contact instance from jid''' connected_account = None contact = None accounts = gajim.contacts.get_accounts() @@ -235,14 +223,12 @@ class SignalObject(dbus.service.Object): return connected_account, contact - @dbus.service.method(INTERFACE) - def send_file(self, *args): - '''send_file(file_path, jid, account=None) - send file, located at 'file_path' to 'jid', using account + @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b') + def send_file(self, file_path, jid, account): + '''send file, located at 'file_path' to 'jid', using account (optional) 'account' ''' - file_path, jid, account = self._get_real_arguments(args, 3) jid = self._get_real_jid(jid, account) - connected_account, contact = self.get_account_and_contact(account, jid) + connected_account, contact = self._get_account_and_contact(account, jid) if connected_account: if file_path[:7] == 'file://': @@ -250,57 +236,52 @@ class SignalObject(dbus.service.Object): if os.path.isfile(file_path): # is it file? gajim.interface.instances['file_transfers'].send_file( connected_account, contact, file_path) - return True - return False + return DBUS_BOOLEAN(True) + return DBUS_BOOLEAN(False) def _send_message(self, jid, message, keyID, account, type = 'chat', subject = None): '''can be called from send_chat_message (default when send_message) or send_single_message''' if not jid or not message: - return None # or raise error + return DBUS_BOOLEAN(False) if not keyID: keyID = '' - connected_account, contact = self.get_account_and_contact(account, jid) + connected_account, contact = self._get_account_and_contact(account, jid) if connected_account: connection = gajim.connections[connected_account] connection.send_message(jid, message, keyID, type, subject) - return True - return False + return DBUS_BOOLEAN(True) + return DBUS_BOOLEAN(False) - @dbus.service.method(INTERFACE) - def send_chat_message(self, *args): - '''send_message(jid, message, keyID=None, account=None) - send chat 'message' to 'jid', using account (optional) 'account'. + @dbus.service.method(INTERFACE, in_signature='ssss', out_signature='b') + def send_chat_message(self, jid, message, keyID, account): + '''Send chat 'message' to 'jid', using account (optional) 'account'. if keyID is specified, encrypt the message with the pgp key ''' - jid, message, keyID, account = self._get_real_arguments(args, 4) jid = self._get_real_jid(jid, account) return self._send_message(jid, message, keyID, account) - @dbus.service.method(INTERFACE) - def send_single_message(self, *args): - '''send_single_message(jid, subject, message, keyID=None, account=None) - send single 'message' to 'jid', using account (optional) 'account'. + @dbus.service.method(INTERFACE, in_signature='sssss', out_signature='b') + def send_single_message(self, jid, subject, message, keyID, account): + '''Send single 'message' to 'jid', using account (optional) 'account'. if keyID is specified, encrypt the message with the pgp key ''' - jid, subject, message, keyID, account = self._get_real_arguments(args, 5) jid = self._get_real_jid(jid, account) return self._send_message(jid, message, keyID, account, type, subject) - @dbus.service.method(INTERFACE) - def open_chat(self, *args): - ''' start_chat(jid, account=None) -> shows the tabbed window for new - message to 'jid', using account(optional) 'account' ''' - jid, account = self._get_real_arguments(args, 2) + @dbus.service.method(INTERFACE, in_signature='ss', out_signature='b') + def open_chat(self, jid, account): + '''Shows the tabbed window for new message to 'jid', using account + (optional) 'account' ''' if not jid: raise MissingArgument - return None + return DBUS_BOOLEAN(False) jid = self._get_real_jid(jid, account) try: jid = helpers.parse_jid(jid) except: # Jid is not conform, ignore it - return None + return DBUS_BOOLEAN(False) if account: accounts = [account] @@ -338,20 +319,19 @@ class SignalObject(dbus.service.Object): connected_account).window if win.get_property('visible'): win.window.focus() - return True - return False + return DBUS_BOOLEAN(True) + return DBUS_BOOLEAN(False) - @dbus.service.method(INTERFACE) - def change_status(self, *args, **keywords): + @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b') + def change_status(self, status, message, account): ''' change_status(status, message, account). account is optional - if not specified status is changed for all accounts. ''' - status, message, account = self._get_real_arguments(args, 3) if status not in ('offline', 'online', 'chat', 'away', 'xa', 'dnd', 'invisible'): raise InvalidArgument - return None + return DBUS_BOOLEAN(False) if account: - gobject.idle_add(gajim.interface.roster.send_status, account, + gobject.idle_add(gajim.interface.roster.send_status, account, status, message) else: # account not specified, so change the status of all accounts @@ -359,26 +339,25 @@ class SignalObject(dbus.service.Object): if not gajim.config.get_per('accounts', acc, 'sync_with_global_status'): continue - gobject.idle_add(gajim.interface.roster.send_status, acc, + gobject.idle_add(gajim.interface.roster.send_status, acc, status, message) - return None + return DBUS_BOOLEAN(False) - @dbus.service.method(INTERFACE) - def show_next_pending_event(self, *args): + @dbus.service.method(INTERFACE, in_signature='', out_signature='') + def show_next_pending_event(self): '''Show the window(s) with next pending event in tabbed/group chats.''' if gajim.events.get_nb_events(): gajim.interface.systray.handle_first_event() - @dbus.service.method(INTERFACE) - def contact_info(self, *args): + @dbus.service.method(INTERFACE, in_signature='s', out_signature='a{sv}') + def contact_info(self, jid): '''get vcard info for a contact. Return cached value of the vcard. ''' - [jid] = self._get_real_arguments(args, 1) if not isinstance(jid, unicode): jid = unicode(jid) if not jid: raise MissingArgument - return None + return DBUS_DICT_SV() jid = self._get_real_jid(jid) cached_vcard = gajim.connections.values()[0].get_cached_vcard(jid) @@ -388,63 +367,56 @@ class SignalObject(dbus.service.Object): # return empty dict return DBUS_DICT_SV() - @dbus.service.method(INTERFACE) - def list_accounts(self, *args): + @dbus.service.method(INTERFACE, in_signature='', out_signature='as') + def list_accounts(self): '''list register accounts''' result = gajim.contacts.get_accounts() + result_array = dbus.Array([], signature='s') if result and len(result) > 0: - result_array = [] for account in result: result_array.append(DBUS_STRING(account)) - return result_array - return None + return result_array - @dbus.service.method(INTERFACE) - def account_info(self, *args): + @dbus.service.method(INTERFACE, in_signature='s', out_signature='a{ss}') + def account_info(self, account): '''show info on account: resource, jid, nick, prio, message''' - [for_account] = self._get_real_arguments(args, 1) - if not gajim.connections.has_key(for_account): - # account is invalid - return None - account = gajim.connections[for_account] result = DBUS_DICT_SS() - index = account.connected - result['status'] = DBUS_STRING(gajim.SHOW_LIST[index]) - result['name'] = DBUS_STRING(account.name) - result['jid'] = DBUS_STRING(gajim.get_jid_from_account(account.name)) - result['message'] = DBUS_STRING(account.status) - result['priority'] = DBUS_STRING(unicode(account.priority)) - result['resource'] = DBUS_STRING(unicode(gajim.config.get_per('accounts', - account.name, 'resource'))) + if gajim.connections.has_key(account): + # account is valid + con = gajim.connections[account] + index = con.connected + result['status'] = DBUS_STRING(gajim.SHOW_LIST[index]) + result['name'] = DBUS_STRING(con.name) + result['jid'] = DBUS_STRING(gajim.get_jid_from_account(con.name)) + result['message'] = DBUS_STRING(con.status) + result['priority'] = DBUS_STRING(unicode(con.priority)) + result['resource'] = DBUS_STRING(unicode(gajim.config.get_per( + 'accounts', con.name, 'resource'))) return result - @dbus.service.method(INTERFACE) - def list_contacts(self, *args): + @dbus.service.method(INTERFACE, in_signature='s', out_signature='a{ss}') + def list_contacts(self, account): '''list all contacts in the roster. If the first argument is specified, then return the contacts for the specified account''' - [for_account] = self._get_real_arguments(args, 1) - result = [] + result = dbus.Array([], signature='s') accounts = gajim.contacts.get_accounts() if len(accounts) == 0: - return None - if for_account: - accounts_to_search = [for_account] + return result + if account: + accounts_to_search = [account] else: accounts_to_search = accounts - for account in accounts_to_search: - if account in accounts: - for jid in gajim.contacts.get_jid_list(account): + for acct in accounts_to_search: + if acct in accounts: + for jid in gajim.contacts.get_jid_list(acct): item = self._contacts_as_dbus_structure( - gajim.contacts.get_contact(account, jid)) + gajim.contacts.get_contact(acct, jid)) if item: result.append(item) - # dbus 0.40 does not support return result as empty list - if result == []: - return None return result - @dbus.service.method(INTERFACE) - def toggle_roster_appearance(self, *args): + @dbus.service.method(INTERFACE, in_signature='', out_signature='') + def toggle_roster_appearance(self): ''' shows/hides the roster window ''' win = gajim.interface.roster.window if win.get_property('visible'): @@ -457,60 +429,57 @@ class SignalObject(dbus.service.Object): else: win.window.focus(long(time())) - @dbus.service.method(INTERFACE) - def prefs_list(self, *args): + @dbus.service.method(INTERFACE, in_signature='', out_signature='a{ss}') + def prefs_list(self): prefs_dict = DBUS_DICT_SS() def get_prefs(data, name, path, value): if value is None: return - key = "" + key = '' if path is not None: for node in path: - key += node + "#" + key += node + '#' key += name prefs_dict[DBUS_STRING(key)] = DBUS_STRING(value[1]) gajim.config.foreach(get_prefs) return prefs_dict - @dbus.service.method(INTERFACE) - def prefs_store(self, *args): + @dbus.service.method(INTERFACE, in_signature='', out_signature='b') + def prefs_store(self): try: gajim.interface.save_config() except Exception, e: - return False - return True + return DBUS_BOOLEAN(False) + return DBUS_BOOLEAN(True) - @dbus.service.method(INTERFACE) - def prefs_del(self, *args): - [key] = self._get_real_arguments(args, 1) + @dbus.service.method(INTERFACE, in_signature='s', out_signature='b') + def prefs_del(self, key): if not key: - return False + return DBUS_BOOLEAN(False) key_path = key.split('#', 2) if len(key_path) != 3: - return False + return DBUS_BOOLEAN(False) if key_path[2] == '*': gajim.config.del_per(key_path[0], key_path[1]) else: gajim.config.del_per(key_path[0], key_path[1], key_path[2]) - return True + return DBUS_BOOLEAN(True) - @dbus.service.method(INTERFACE) - def prefs_put(self, *args): - [key] = self._get_real_arguments(args, 1) + @dbus.service.method(INTERFACE, in_signature='s', out_signature='b') + def prefs_put(self, key): if not key: - return False + return DBUS_BOOLEAN(False) key_path = key.split('#', 2) if len(key_path) < 3: subname, value = key.split('=', 1) gajim.config.set(subname, value) - return True + return DBUS_BOOLEAN(True) subname, value = key_path[2].split('=', 1) gajim.config.set_per(key_path[0], key_path[1], subname, value) - return True + return DBUS_BOOLEAN(True) - @dbus.service.method(INTERFACE) - def add_contact(self, *args): - [jid, account] = self._get_real_arguments(args, 2) + @dbus.service.method(INTERFACE, in_signature='ss', out_signature='b') + def add_contact(self, jid, account): if account: if account in gajim.connections and \ gajim.connections[account].connected > 1: @@ -518,15 +487,14 @@ class SignalObject(dbus.service.Object): AddNewContactWindow(account = account, jid = jid) else: # wrong account - return False + return DBUS_BOOLEAN(False) else: # if account is not given, show account combobox AddNewContactWindow(account = None, jid = jid) - return True + return DBUS_BOOLEAN(True) - @dbus.service.method(INTERFACE) - def remove_contact(self, *args): - [jid, account] = self._get_real_arguments(args, 2) + @dbus.service.method(INTERFACE, in_signature='ss', out_signature='b') + def remove_contact(self, jid, account): jid = self._get_real_jid(jid, account) accounts = gajim.contacts.get_accounts() @@ -542,7 +510,7 @@ class SignalObject(dbus.service.Object): gajim.interface.roster.remove_contact(contact, account) gajim.contacts.remove_jid(account, jid) contact_exists = True - return contact_exists + return DBUS_BOOLEAN(contact_exists) def _is_first(self): if self.first_show: @@ -550,20 +518,6 @@ class SignalObject(dbus.service.Object): return True return False - def _get_real_arguments(self, args, desired_length): - ''' extend, or descend the length of args to match desired_length - ''' - args = list(args) - for i in range(len(args)): - if args[i]: - args[i] = unicode(args[i]) - else: - args[i] = None - if desired_length > 0: - args.extend([None] * (desired_length - len(args))) - args = args[:desired_length] - return args - def _get_real_jid(self, jid, account = None): '''get the real jid from the given one: removes xmpp: or get jid from nick if account is specified, search only in this account @@ -619,31 +573,28 @@ class SignalObject(dbus.service.Object): contact_dict['resources'].append(resource_props) return contact_dict - @dbus.service.method(INTERFACE) - def get_unread_msgs_number(self, *args): - return str(gajim.events.get_nb_events()) + @dbus.service.method(INTERFACE, in_signature='', out_signature='s') + def get_unread_msgs_number(self): + return DBUS_STRING(str(gajim.events.get_nb_events())) - @dbus.service.method(INTERFACE) - def start_chat(self, *args): - [account] = self._get_real_arguments(args, 1) + @dbus.service.method(INTERFACE, in_signature='s', out_signature='b') + def start_chat(self, account): if not account: # error is shown in gajim-remote check_arguments(..) - return None + return DBUS_BOOLEAN(False) NewChatDialog(account) - return True + return DBUS_BOOLEAN(True) - @dbus.service.method(INTERFACE) - def send_xml(self, *args): - xml, account = self._get_real_arguments(args, 2) + @dbus.service.method(INTERFACE, in_signature='ss', out_signature='') + def send_xml(self, xml, account): if account: - gajim.connections[account[0]].send_stanza(xml) + gajim.connections[account].send_stanza(xml) else: for acc in gajim.contacts.get_accounts(): gajim.connections[acc].send_stanza(xml) - @dbus.service.method(INTERFACE) - def join_room(self, *args): - room_jid, nick, passwd, account = self._get_real_arguments(args, 4) + @dbus.service.method(INTERFACE, in_signature='ssss', out_signature='') + def join_room(self, room_jid, nick, passwd, account): if not account: # get the first connected account accounts = gajim.connections.keys() @@ -652,8 +603,8 @@ class SignalObject(dbus.service.Object): account = acct break if not account: - account = gajim.contacts.get_accounts()[0] - if nick is None: + return + if not nick: nick = '' gajim.interface.instances[account]['join_gc'] = \ JoinGroupchatWindow(account, room_jid, nick)