diff --git a/src/gajim-remote.py b/src/gajim-remote.py index 4d1f98b6b..3038cbb18 100755 --- a/src/gajim-remote.py +++ b/src/gajim-remote.py @@ -228,20 +228,10 @@ class GajimRemote: if self.command == 'contact_info': if self.argv_len < 3: send_error(_('Missing argument "contact_jid"')) - try: - id = self.sbus.add_signal_receiver(self.show_vcard_info, - 'VcardInfo', INTERFACE, SERVICE, OBJ_PATH) - except Exception, e: - raise exceptions.ServiceNotAvailable res = self.call_remote_method() self.print_result(res) - if self.command == 'contact_info': - gobject.timeout_add(10000, self.gobject_quit) # wait 10 sec for response - self.loop = gobject.MainLoop() - self.loop.run() - def print_result(self, res): ''' Print retrieved result to the output ''' if res is not None: @@ -261,18 +251,17 @@ class GajimRemote: print account elif self.command == 'account_info': if res: - print self.print_info(0, self.unrepr(res)[0]) + print self.print_info(0, res, True) elif self.command == 'list_contacts': - for single_res in res: - accounts = self.unrepr(single_res) - for account_dict in accounts: - print self.print_info(0, account_dict) + for account_dict in res: + print self.print_info(0, account_dict, True) elif self.command == 'prefs_list': - prefs_dict = self.unrepr(res) - pref_keys = prefs_dict[0].keys() + pref_keys = res.keys() pref_keys.sort() for pref_key in pref_keys: - print pref_key, '=', prefs_dict[0][pref_key] + print pref_key, '=', res[pref_key] + elif self.command == 'contact_info': + print self.print_info(0, res, True) elif res: print res @@ -348,8 +337,8 @@ class GajimRemote: str += '\n' return str - def print_info(self, level, prop_dict): - ''' return formated string from serialized vcard data ''' + def print_info(self, level, prop_dict, encode_return = False): + ''' return formated string from data structure ''' if prop_dict is None or not isinstance(prop_dict, (dict, list, tuple)): return '' ret_str = '' @@ -359,9 +348,9 @@ class GajimRemote: for val in prop_dict: if val is None: ret_str +='\t' - elif isinstance(val, (str, int)): + elif isinstance(val, int): ret_str +='\t' + str(val) - elif isinstance(val, unicode): + elif isinstance(val, (str, unicode)): ret_str +='\t' + val elif isinstance(val, (list, tuple)): res = '' @@ -385,167 +374,18 @@ class GajimRemote: for items in val: res += self.print_info(level+1, items) if res != '': - if isinstance(res, str): - res = res.decode(PREFERRED_ENCODING) ret_str += '%s%s: \n%s' % (spacing, key, res) elif isinstance(val, dict): res = self.print_info(level+1, val) if res != '': ret_str += '%s%s: \n%s' % (spacing, key, res) - - # utf-8 string come from gajim - # FIXME: why we have strings instead of unicode? - if isinstance(ret_str, str): - return ret_str - return ret_str.encode(PREFERRED_ENCODING) - - def unrepr(self, serialized_data): - ''' works the same as eval, but only for structural values, - not functions! e.g. dicts, lists, strings, tuples ''' - if not serialized_data: - return (None, '') - value = serialized_data.strip() - first_char = value[0] - is_unicode = False - is_int = False - - if first_char == 'u': - is_unicode = True - value = value[1:] - first_char = value[0] - elif '0123456789.'.find(first_char) != -1: - is_int = True - _str = first_char - if first_char == '.': - is_float = True - else: - is_float = False - for i in xrange(len(value) - 1): - chr = value[i+1] - if chr == '.': - is_float = True - elif '0123456789'.find(chr) == -1: - break - _str += chr - if is_float: - return (float(_str), value[len(_str):]) - else: - return (int(_str), value[len(_str):]) - elif first_char == 'N': - if value[1:4] == 'one': - return (None, value[4:]) - else: - return (None, '') - if first_char == "'" or first_char == '"': # handle strings and unicode - if len(value) < 2: - return ('',value[1:]) - _str = '' - previous_slash = False - slashes = 0 - for i in xrange(len(value) - 1): - chr = value[i+1] - if previous_slash: - previous_slash = False - if chr == '\\': - _str += '\\' - elif chr == 'n': - _str += '\n' - elif chr == 't': - _str += '\t' - elif chr == 'r': - _str += '\r' - elif chr == 'b': - _str += '\b' - elif chr == '\'': - _str += '\'' - elif chr == '\"': - _str += '\"' - elif chr in ('u', 'x') and is_unicode: - slashes -= 1 - _str += '\\' + chr - else: - _str += chr - elif chr == first_char: - break - elif chr == '\\': - previous_slash = True - slashes += 1 - else: - _str += chr - substr_len = len(_str) + 2 + slashes - if is_unicode and _str: - _str = _str.decode('unicode-escape').encode('utf-8') - return (_str, value[substr_len :]) - elif first_char == '{': # dict - _dict = {} - if value[1] == '}': - return ({}, value[2:]) - while True: - if value[1] == '}': - break - key, next = self.unrepr(value[1:]) - if not isinstance(key, (str, unicode)): - send_error('Wrong string: %s' % value) - next = next.strip() - if not next or next[0] != ':': - send_error('Wrong string: %s' % (value)) - val, next = self.unrepr(next[1:]) - if isinstance(key, str): - key = key.decode(PREFERRED_ENCODING) - if isinstance(val, str): - val = val.decode(PREFERRED_ENCODING) - _dict[key] = val - next = next.strip() - if not next: - break - if next[0] == ',': - value = next - elif next[0] == '}': - break - else: - break - return (_dict, next[1:]) - elif first_char in ('[', '('): # return list - _tuple = [] - if value[1] == ']': - return ([], value[2:]) - while True: - if value[1] == ']': - break - val, next = self.unrepr(value[1:]) - next = next.strip() - if not next: - send_error('Wrong string: %s' % val) - if isinstance(val, str): - val = val.decode('utf-8') - _tuple.append(val) - next = next.strip() - if not next: - break - if next[0] == ',': - value = next - elif next[0] in (']', ')'): - break - return (_tuple, next[1:]) - - def show_vcard_info(self, *args, **keyword): - ''' write user vcart in a formated output ''' - props_dict = None - if _version[1] >= 30: - props_dict = self.unrepr(args[0]) - else: - if args and len(args) >= 5: - props_dict = self.unrepr(args[4].get_args_list()[0]) - if props_dict: - print self.print_info(0,props_dict[0]) - # remove_signal_receiver is broken in lower versions (< 0.35), - # so we leave the leak - nothing can be done - if _version[1] >= 41: - self.sbus.remove_signal_receiver(self.show_vcard_info, 'VcardInfo', - INTERFACE, SERVICE, OBJ_PATH) + if (encode_return): + try: + ret_str = ret_str.encode(PREFERRED_ENCODING) + except: + pass + return ret_str - self.loop.quit() - def check_arguments(self): ''' Make check if all necessary arguments are given ''' argv_len = self.argv_len - 2 @@ -555,22 +395,15 @@ class GajimRemote: send_error(_('Argument "%s" is not specified. \n' 'Type "%s help %s" for more info') % (args[argv_len][0], BASENAME, self.command)) - - def gobject_quit(self): - if _version[1] >= 41: - self.sbus.remove_signal_receiver(self.show_vcard_info, 'VcardInfo', - INTERFACE, SERVICE, OBJ_PATH) - self.loop.quit() def call_remote_method(self): ''' calls self.method with arguments from sys.argv[2:] ''' try: - sys.argv.pop(0) - sys.argv.pop(0) - res = self.method(*sys.argv) + # make console arguments unicode + args = [dbus.String(i.decode(PREFERRED_ENCODING)) for i in sys.argv[2:]] + res = self.method(*args) return res - except Exception, e: - print str(e) + except Exception: raise exceptions.ServiceNotAvailable return None diff --git a/src/remote_control.py b/src/remote_control.py index b65fe490b..c51813941 100644 --- a/src/remote_control.py +++ b/src/remote_control.py @@ -47,7 +47,7 @@ if dbus_support.supported: import dbus.service import dbus.glib # cause dbus 0.35+ doesn't return signal replies without it DbusPrototype = dbus.service.Object - elif dbus_support.version >= (0, 20, 0): + elif dbus_support.version >= (0, 30, 0): DbusPrototype = dbus.Object else: #dbus is not defined DbusPrototype = str @@ -67,7 +67,7 @@ class Remote: if dbus_support.version[1] >= 41: service = dbus.service.BusName(SERVICE, bus=session_bus) self.signal_object = SignalObject(service) - elif dbus_support.version[1] <= 40 and dbus_support.version[1] >= 20: + else: service=dbus.Service(SERVICE, session_bus) self.signal_object = SignalObject(service) @@ -113,19 +113,12 @@ class SignalObject(DbusPrototype): def raise_signal(self, signal, arg): ''' raise a signal, with a single string message ''' - if dbus_support.version[1] >= 30: - from dbus import dbus_bindings - message = dbus_bindings.Signal(OBJ_PATH, INTERFACE, signal) - i = message.get_iter(True) - i.append(arg) - self._connection.send(message) - else: - self.emit_signal(INTERFACE, signal, arg) - - # signals - def VcardInfo(self, *vcard): - pass - + from dbus import dbus_bindings + message = dbus_bindings.Signal(OBJ_PATH, INTERFACE, signal) + i = message.get_iter(True) + i.append(arg) + self._connection.send(message) + def get_status(self, *args): '''get_status(account = None) returns status (show to be exact) which is the global one @@ -137,7 +130,7 @@ class SignalObject(DbusPrototype): return helpers.get_global_show() # return show for the given account index = gajim.connections[account].connected - return STATUS_LIST[index] + return dbus.String(STATUS_LIST[index]) def get_status_message(self, *args): '''get_status(account = None) @@ -150,7 +143,7 @@ class SignalObject(DbusPrototype): return str(helpers.get_global_status()) # return show for the given account status = gajim.connections[account].status - return str(status) + return dbus.String(status) def get_account_and_contact(self, account, jid): @@ -177,7 +170,7 @@ class SignalObject(DbusPrototype): break if not contact: contact = jid - + return connected_account, contact def send_file(self, *args): @@ -204,9 +197,9 @@ class SignalObject(DbusPrototype): return None # or raise error if not keyID: keyID = '' - + connected_account, contact = self.get_account_and_contact(account, jid) - + if connected_account: connection = gajim.connections[connected_account] res = connection.send_message(jid, message, keyID) @@ -222,7 +215,7 @@ class SignalObject(DbusPrototype): return None if jid.startswith('xmpp:'): jid = jid[5:] # len('xmpp:') = 5 - + if account: accounts = [account] else: @@ -247,11 +240,11 @@ class SignalObject(DbusPrototype): connected_account = acct elif first_connected_acct is None: first_connected_acct = acct - + # if jid is not a conntact, open-chat with first connected account if connected_account is None and first_connected_acct: connected_account = first_connected_acct - + if connected_account: gajim.interface.roster.new_chat_from_jid(connected_account, jid) # preserve the 'steal focus preservation' @@ -260,7 +253,7 @@ class SignalObject(DbusPrototype): win.window.focus() return True return False - + def change_status(self, *args, **keywords): ''' change_status(status, message, account). account is optional - if not specified status is changed for all accounts. ''' @@ -278,43 +271,40 @@ class SignalObject(DbusPrototype): gobject.idle_add(gajim.interface.roster.send_status, acc, status, message) return None - + def show_next_unread(self, *args): ''' Show the window(s) with next waiting messages in tabbed/group chats. ''' #FIXME: when systray is disabled this method does nothing. if len(gajim.interface.systray.jids) != 0: gajim.interface.systray.handle_first_event() - + def contact_info(self, *args): - ''' get vcard info for a contact. This method returns nothing. - You have to register the 'VcardInfo' signal to get the real vcard. ''' + ''' 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: # FIXME: raise exception for missing argument (0.3+) return None - - accounts = gajim.contacts.get_accounts() - - for account in accounts: - contact = gajim.contacts.get_first_contact_from_jid(account, jid) - if contact: - self.vcard_account = account - gajim.connections[account].request_vcard(jid) - break - return None - + + cached_vcard = gajim.connections.values()[0].get_cached_vcard(jid) + if cached_vcard: + return self._get_dbus_struct(cached_vcard) + + # return empty dict + return dbus.Dictionary({}, signature="sv") + def list_accounts(self, *args): ''' list register accounts ''' result = gajim.contacts.get_accounts() if result and len(result) > 0: result_array = [] for account in result: - result_array.append(account) + result_array.append(dbus.String(account)) return result_array return None - + def account_info(self, *args): ''' show info on account: resource, jid, nick, prio, message ''' [for_account] = self._get_real_arguments(args, 1) @@ -322,17 +312,17 @@ class SignalObject(DbusPrototype): # account is invalid return None account = gajim.connections[for_account] - result = {} + result = dbus.Dictionary({}, signature="ss") index = account.connected - result['status'] = STATUS_LIST[index] - result['name'] = account.name - result['jid'] = gajim.get_jid_from_account(account.name) - result['message'] = account.status - result['priority'] = unicode(gajim.config.get_per('accounts', - account.name, 'priority')) - result['resource'] = unicode(gajim.config.get_per('accounts', - account.name, 'resource')) - return repr(result) + result['status'] = dbus.String(STATUS_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(gajim.config.get_per('accounts', + account.name, 'priority'))) + result['resource'] = dbus.String(unicode(gajim.config.get_per('accounts', + account.name, 'resource'))) + return result def list_contacts(self, *args): ''' list all contacts in the roster. If the first argument is specified, @@ -349,8 +339,8 @@ class SignalObject(DbusPrototype): for account in accounts_to_search: if account in accounts: for jid in gajim.contacts.get_jid_list(account): - item = self._serialized_contacts(gajim.contacts.get_contact( - account, jid)) + item = self._contacts_as_dbus_structure( + gajim.contacts.get_contact(account, jid)) if item: result.append(item) else: @@ -359,7 +349,7 @@ class SignalObject(DbusPrototype): if result == []: return None return result - + def toggle_roster_appearance(self, *args): ''' shows/hides the roster window ''' win = gajim.interface.roster.window @@ -374,7 +364,7 @@ class SignalObject(DbusPrototype): win.window.focus(long(time())) def prefs_list(self, *args): - prefs_dict = {} + prefs_dict = dbus.Dictionary({}, signature="ss") def get_prefs(data, name, path, value): if value is None: return @@ -383,9 +373,9 @@ class SignalObject(DbusPrototype): for node in path: key += node + "#" key += name - prefs_dict[key] = unicode(value[1]) + prefs_dict[dbus.String(key)] = dbus.String(value[1]) gajim.config.foreach(get_prefs) - return repr(prefs_dict) + return prefs_dict def prefs_store(self, *args): try: @@ -406,7 +396,7 @@ class SignalObject(DbusPrototype): else: gajim.config.del_per(key_path[0], key_path[1], key_path[2]) return True - + def prefs_put(self, *args): [key] = self._get_real_arguments(args, 1) if not key: @@ -419,7 +409,7 @@ class SignalObject(DbusPrototype): subname, value = key_path[2].split('=', 1) gajim.config.set_per(key_path[0], key_path[1], subname, value) return True - + def add_contact(self, *args): [account] = self._get_real_arguments(args, 1) accounts = gajim.contacts.get_accounts() @@ -445,7 +435,7 @@ class SignalObject(DbusPrototype): gajim.contacts.remove_jid(account, jid) contact_exists = True return contact_exists - + def _is_first(self): if self.first_show: self.first_show = False @@ -453,28 +443,53 @@ class SignalObject(DbusPrototype): return False def _get_real_arguments(self, args, desired_length): - # supresses the first 'message' argument, which is set in dbus 0.23 - if dbus_support.version[1] == 20: - args=args[1:] + ''' 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 = list(args) args.extend([None] * (desired_length - len(args))) args = args[:desired_length] return args - - def _serialized_contacts(self, contacts): - ''' get info from list of Contact objects and create a serialized - dict for sending it over dbus ''' + + def _get_dbus_struct(self, obj): + ''' recursively go through all the items and replace + them with their casted dbus equivalents + ''' + if obj is None: + return dbus.String('') + if isinstance(obj, (unicode, str)): + return dbus.String(obj) + if isinstance(obj, int): + return dbus.UInt32(obj) + if isinstance(obj, float): + return dbus.Double(obj) + if isinstance(obj, (list, tuple)): + return [self._get_dbus_struct(i) for i in obj] + if isinstance(obj, dict): + result = dbus.Dictionary({}, signature="sv") + for key, value in obj.items(): + result[dbus.String(key)] = self._get_dbus_struct(value) + return result + # unknown type + return dbus.Variant(obj) + + def _contacts_as_dbus_structure(self, contacts): + ''' get info from list of Contact objects and create dbus dict ''' if not contacts: return None prim_contact = None # primary contact for contact in contacts: if prim_contact == None or contact.priority > prim_contact.priority: prim_contact = contact - contact_dict = {} - contact_dict['name'] = prim_contact.name - contact_dict['show'] = prim_contact.show - contact_dict['jid'] = prim_contact.jid + contact_dict = dbus.Dictionary({}, key_type=dbus.String, value_type=dbus.Variant) + contact_dict['name'] = dbus.Variant(dbus.String(prim_contact.name)) + contact_dict['show'] = dbus.Variant(dbus.String(prim_contact.show)) + contact_dict['jid'] = dbus.Variant(dbus.String(prim_contact.jid)) if prim_contact.keyID: keyID = None if len(prim_contact.keyID) == 8: @@ -485,10 +500,10 @@ class SignalObject(DbusPrototype): contact_dict['openpgp'] = keyID contact_dict['resources'] = [] for contact in contacts: - contact_dict['resources'].append(tuple([contact.resource, - contact.priority, contact.status])) - return repr(contact_dict) - + resource_props = [dbus.String(contact.resource), contact.priority, dbus.String(contact.status)] + contact_dict['resources'].append(tuple(resource_props)) + contact_dict['resources'] = dbus.Variant(contact_dict['resources']) + return contact_dict if dbus_support.version[1] >= 30 and dbus_support.version[1] <= 40: method = dbus.method @@ -496,27 +511,25 @@ class SignalObject(DbusPrototype): elif dbus_support.version[1] >= 41: method = dbus.service.method signal = dbus.service.signal - - if dbus_support.version[1] >= 30: - # prevent using decorators, because they are not supported - # on python < 2.4 - # FIXME: use decorators when python2.3 (and dbus 0.23) is OOOOOOLD - toggle_roster_appearance = method(INTERFACE)(toggle_roster_appearance) - list_contacts = method(INTERFACE)(list_contacts) - list_accounts = method(INTERFACE)(list_accounts) - show_next_unread = method(INTERFACE)(show_next_unread) - change_status = method(INTERFACE)(change_status) - open_chat = method(INTERFACE)(open_chat) - contact_info = method(INTERFACE)(contact_info) - send_message = method(INTERFACE)(send_message) - send_file = method(INTERFACE)(send_file) - VcardInfo = signal(INTERFACE)(VcardInfo) - prefs_list = method(INTERFACE)(prefs_list) - prefs_put = method(INTERFACE)(prefs_put) - prefs_del = method(INTERFACE)(prefs_del) - prefs_store = method(INTERFACE)(prefs_store) - remove_contact = method(INTERFACE)(remove_contact) - add_contact = method(INTERFACE)(add_contact) - get_status = method(INTERFACE)(get_status) - get_status_message = method(INTERFACE)(get_status_message) - account_info = method(INTERFACE)(account_info) + + # prevent using decorators, because they are not supported + # on python < 2.4 + # FIXME: use decorators when python2.3 (and dbus 0.23) is OOOOOOLD + toggle_roster_appearance = method(INTERFACE)(toggle_roster_appearance) + list_contacts = method(INTERFACE)(list_contacts) + list_accounts = method(INTERFACE)(list_accounts) + show_next_unread = method(INTERFACE)(show_next_unread) + change_status = method(INTERFACE)(change_status) + open_chat = method(INTERFACE)(open_chat) + contact_info = method(INTERFACE)(contact_info) + send_message = method(INTERFACE)(send_message) + send_file = method(INTERFACE)(send_file) + prefs_list = method(INTERFACE)(prefs_list) + prefs_put = method(INTERFACE)(prefs_put) + prefs_del = method(INTERFACE)(prefs_del) + prefs_store = method(INTERFACE)(prefs_store) + remove_contact = method(INTERFACE)(remove_contact) + add_contact = method(INTERFACE)(add_contact) + get_status = method(INTERFACE)(get_status) + get_status_message = method(INTERFACE)(get_status_message) + account_info = method(INTERFACE)(account_info)