root/branches/gajim_0.8.2/src/gajim-remote.py

Revision 3456, 13.4 kB (checked in by dkirov, 3 years ago)

fix: show vcard on contact_info

  • Property svn:executable set to *
Line 
1#!/bin/sh
2''':'
3exec python -OOt "$0" ${1+"$@"}
4' '''
5##      scripts/gajim-remote.py
6##
7## Gajim Team:
8##      - Yann Le Boulanger <asterix@lagaule.org>
9##      - Vincent Hanquez <tab@snarc.org>
10##      - Nikos Kouremenos <kourem@gmail.com>
11##      - Dimitur Kirov <dkirov@gmail.com>
12##
13##      Copyright (C) 2003-2005 Gajim Team
14##
15## This program is free software; you can redistribute it and/or modify
16## it under the terms of the GNU General Public License as published
17## by the Free Software Foundation; version 2 only.
18##
19## This program is distributed in the hope that it will be useful,
20## but WITHOUT ANY WARRANTY; without even the implied warranty of
21## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22## GNU General Public License for more details.
23##
24
25# gajim-remote help will show you the DBUS API of Gajim
26
27import sys
28import gtk
29import gobject
30
31import signal
32
33signal.signal(signal.SIGINT, signal.SIG_DFL) # ^C exits the application
34
35from common import i18n
36
37_ = i18n._
38i18n.init()
39
40def send_error(error_message):
41                '''  Writes error message to stderr and exits '''
42                sys.stderr.write(error_message + '\n')
43                sys.stderr.flush()
44                sys.exit(1)
45
46try:
47        import dbus
48except:
49        send_error('Dbus is not supported.\n')
50
51_version = getattr(dbus, 'version', (0, 20, 0))
52if _version[1] >= 41:
53        import dbus.service
54        import dbus.glib
55
56OBJ_PATH = '/org/gajim/dbus/RemoteObject'
57INTERFACE = 'org.gajim.dbus.RemoteInterface'
58SERVICE = 'org.gajim.dbus'
59BASENAME = 'gajim-remote'
60
61class GajimRemote:
62       
63        def __init__(self):
64                self.argv_len = len(sys.argv) 
65                # define commands dict. Prototype :
66                # {
67                #       'command': [comment, [list of arguments] ]
68                # }
69                #
70                # each argument is defined as a tuple:
71                #    (argument name, help on argument, is mandatory)
72                #
73                self.commands = {
74                        'help':[
75                                        _('show a help on specific command'),
76                                        [
77                                                #parameter, named "on_command". User gets help for the command, specified by this parameter
78                                                (_('on_command'), 
79                                                _('show help on command'), False)
80                                        ]
81                                ], 
82                        'toggle_roster_appearance' : [
83                                        _('Shows or hides the roster window'),
84                                        []
85                                ], 
86                        'show_next_unread': [
87                                        _('Popup a window with the next unread message'),
88                                        []
89                                ],
90                        'list_contacts': [
91                                        _('Print a list of all contacts in the roster. \
92Each contact appear on a separate line'),
93                                        [
94                                                (_('account'), _('show only contacts of the \
95given account'), False)
96                                        ]
97                                       
98                                ],     
99                        'list_accounts': [
100                                        _('Print a list of registered accounts'),
101                                        []
102                                ], 
103                        'change_status': [
104                                        _('Change the status of account or accounts'),
105                                        [
106                                                (_('status'), _('one of: offline, online, chat, away, \
107xa, dnd, invisible '), True), 
108                                                (_('message'), _('status message'), False), 
109                                                (_('account'), _('change status of account "account". \
110If not specified, try to change status of all accounts that \
111have "sync with global status" option set'), False)
112                                        ]
113                                ],
114                        'open_chat': [ 
115                                        _('Show the chat dialog so that you can send message to a \
116contact'), 
117                                        [
118                                                ('jid', _('JID of the contact that you want to chat \
119with'),
120                                                        True), 
121                                                (_('account'), _('if specified, contact is taken from \
122the contact list of this account'), False)
123                                        ]
124                                ],
125                        'send_message':[
126                                        _('Send new message to a contact in the roster. Both OpenPGP \
127key and account are optional. If you want to set only \'account\', without \
128\'OpenPGP key\', just set \'OpenPGP key\' to \'\'.'), 
129                                        [
130                                                ('jid', _('JID of the contact that will receive the \
131message'), True),
132                                                (_('message'), _('message contents'), True),
133                                                (_('pgp key'), _('if specified, the message will be \
134encrypted using this public key'), False),
135                                                (_('account'), _('if specified, the message will be \
136sent using this account'), False),
137                                        ]
138                                ], 
139                        'contact_info': [
140                                        _('Get detailed info on a contact'), 
141                                        [
142                                                ('jid', _('JID of the contact'), True)
143                                        ]
144                                ],
145                        'send_file': [
146                                        _('Send file to a contact'),
147                                        [
148                                                (_('file'), _('File path'), True),
149                                                ('jid', _('JID of the contact'), True),
150                                                (_('account'), _('if specified, file will be sent \
151using this account'), False)
152                                        ]
153                                ]
154                        }
155                if self.argv_len  < 2 or \
156                        sys.argv[1] not in self.commands.keys(): # no args or bad args
157                        send_error(self.compose_help())
158                self.command = sys.argv[1]
159                if self.command == 'help':
160                        if self.argv_len == 3:
161                                print self.help_on_command(sys.argv[2])
162                        else:
163                                print self.compose_help()
164                        sys.exit()
165               
166                self.init_connection()
167                self.check_arguments()
168               
169                if self.command == 'contact_info':
170                        if self.argv_len < 3:
171                                send_error(_('Missing argument "contact_jid"'))
172                        try:
173                                id = self.sbus.add_signal_receiver(self.show_vcard_info, 
174                                        'VcardInfo', INTERFACE, SERVICE, OBJ_PATH)
175                        except Exception, e:
176                                send_error(_('Service not available'))
177               
178                res = self.call_remote_method()
179                self.print_result(res)
180               
181                if self.command == 'contact_info':
182                        gobject.timeout_add(10000, self.gtk_quit) # wait 10 sec for response
183                        gtk.main()
184       
185        def print_result(self, res):
186                ''' Print retrieved result to the output '''
187                if res is not None:
188                        if self.command in ['open_chat', 'send_message']:
189                                if self.command == 'send_message':
190                                        self.argv_len -= 2
191                               
192                                if res == False:
193                                        if self.argv_len < 4:
194                                                send_error(_('\'%s\' is not in your roster.\n\
195Please specify account for sending the message.') % sys.argv[2])
196                                        else:
197                                                send_error(_('You have no active account'))
198                        elif self.command == 'list_accounts':
199                                if isinstance(res, list):
200                                        for account in res:
201                                                print account
202                        elif self.command == 'list_contacts':
203                                for single_res in res:
204                                        accounts = self.unrepr(single_res)
205                                        for account_dict in accounts:
206                                                print self.print_info(0, account_dict)
207                        elif res:
208                                print res
209       
210        def init_connection(self):
211                ''' create the onnection to the session dbus,
212                or exit if it is not possible '''
213                try:
214                        self.sbus = dbus.SessionBus()
215                except:
216                        send_error(_('Session bus is not available.'))
217               
218                if _version[1] >= 30:
219                        obj = self.sbus.get_object(SERVICE, OBJ_PATH)
220                        interface = dbus.Interface(obj, INTERFACE)
221                elif _version[1] < 30:
222                        self.service = self.sbus.get_service(SERVICE)
223                        interface = self.service.get_object(OBJ_PATH, INTERFACE)
224                else:
225                        send_error(_('Unknown D-Bus version: %s') % _version[1])
226                       
227                # get the function asked
228                self.method = interface.__getattr__(self.command)
229               
230        def make_arguments_row(self, args):
231                ''' return arguments list. Mandatory arguments are enclosed with:
232                '<', '>', optional arguments - with '[', ']' '''
233                str = ''
234                for argument in args:
235                        str += ' '
236                        if argument[2]:
237                                str += '<'
238                        else:
239                                str += '['
240                        str += argument[0]
241                        if argument[2]:
242                                str += '>'
243                        else:
244                                str += ']'
245                return str
246               
247        def help_on_command(self, command):
248                ''' return help message for a given command '''
249                if command in self.commands:
250                        command_props = self.commands[command]
251                        arguments_str = self.make_arguments_row(command_props[1])
252                        str = _('Usage: %s %s %s \n\t') % (BASENAME, command, 
253                                        arguments_str)
254                        str += command_props[0] + '\n\n' + _('Arguments:') + '\n'
255                        for argument in command_props[1]:
256                                str += ' ' +  argument[0] + ' - ' + argument[1] + '\n'
257                        return str
258                send_error(_('%s not found') % command)
259                       
260        def compose_help(self):
261                ''' print usage, and list available commands '''
262                str = _('Usage: %s command [arguments]\nCommand is one of:\n' ) % BASENAME
263                for command in self.commands.keys():
264                        str += '  ' + command
265                        for argument in self.commands[command][1]:
266                                str += ' '
267                                if argument[2]:
268                                        str += '<'
269                                else:
270                                        str += '['
271                                str += argument[0]
272                                if argument[2]:
273                                        str += '>'
274                                else:
275                                        str += ']'
276                        str += '\n'
277                return str
278               
279        def print_info(self, level, prop_dict):
280                ''' return formated string from serialized vcard data '''
281                if prop_dict is None or type(prop_dict) \
282                        not in [dict, list, tuple]:
283                        return ''
284                ret_str = ''
285                if type(prop_dict) in [list, tuple]:
286                        ret_str = ''
287                        spacing = ' ' * level * 4
288                        for val in prop_dict:
289                                if val is None:
290                                        ret_str +='\t'
291                                elif type(val) in (unicode, int, str):
292                                        ret_str +='\t' + str(val)
293                                elif type(val) in (list, tuple):
294                                        res = ''
295                                        for items in val:
296                                                res += self.print_info(level+1, items)
297                                        if res != '':
298                                                ret_str += '\t' + res
299                                elif type(val) == dict:
300                                        ret_str += self.print_info(level+1, val)
301                        ret_str = '%s(%s)\n' % (spacing, ret_str[1:])
302                elif isinstance(prop_dict, dict):
303                        for key in prop_dict.keys():
304                                val = prop_dict[key]
305                                spacing = ' ' * level * 4
306                                if type(val) in (unicode, int, str):
307                                        if val is not None:
308                                                val = val.strip()
309                                                ret_str += '%s%-10s: %s\n' % (spacing, key, val)
310                                elif type(val) in (list, tuple):
311                                        res = ''
312                                        for items in val:
313                                                res += self.print_info(level+1, items)
314                                        if res != '':
315                                                ret_str += '%s%s: \n%s' % (spacing, key, res)
316                                elif isinstance(val, dict):
317                                        res = self.print_info(level+1, val)
318                                        if res != '':
319                                                ret_str += '%s%s: \n%s' % (spacing, key, res)
320                return ret_str
321               
322        def unrepr(self, serialized_data):
323                ''' works the same as eval, but only for structural values,
324                not functions! e.g. dicts, lists, strings, tuples '''
325                if not serialized_data:
326                        return (None, '') 
327                value = serialized_data.strip()
328                first_char = value[0]
329                is_unicode  = False
330                is_int  = False
331               
332                if first_char == 'u':
333                        is_unicode = True
334                        value = value[1:]
335                        first_char = value[0]
336                elif '0123456789.'.find(first_char) != -1:
337                        is_int = True
338                        _str = first_char
339                        if first_char == '.':
340                                is_float = True
341                        else:
342                                is_float =  False
343                        for i in range(len(value) - 1):
344                                chr = value[i+1]
345                                if chr == '.':
346                                        is_float = True
347                                elif '0123456789'.find(chr) == -1:
348                                        break
349                                _str += chr
350                        if is_float:
351                                return (float(_str), value[len(_str):])
352                        else:
353                                return (int(_str), value[len(_str):])
354                elif first_char == 'N':
355                        if value[1:4] == 'one':
356                                return (None, value[4:])
357                        else:
358                                return (None, '')
359                if first_char == "'" or first_char == '"': # handle strings and unicode
360                        if len(value) < 2:
361                                return ('',value[1:])
362                        _str = ''
363                        previous_slash = False
364                        for i in range(len(value) - 1):
365                                chr = value[i+1]
366                                if previous_slash:
367                                        previous_slash = False
368                                        if chr == '\\':
369                                                _str += '\\'
370                                        elif chr == 'n':
371                                                _str += '\n'
372                                        elif chr == 't':
373                                                _str += '\t'
374                                        elif chr == 'r':
375                                                _str += '\r'
376                                        elif chr == 'b':
377                                                _str += '\b'
378                                        elif chr == '\'':
379                                                _str += '\''
380                                        elif chr == '\"':
381                                                _str += '\"'
382                                        elif chr == 'u' and is_unicode:
383                                                _str += '\\u'
384                                elif chr == first_char:
385                                        break
386                                elif chr == '\\':
387                                        previous_slash = True
388                                else:
389                                        _str += chr
390                        substr_len = len(_str)+2
391                        if is_unicode and _str:
392                                _str = _str.decode('unicode-escape').encode('utf-8')
393                        return (_str, value[substr_len :])
394                elif first_char == '{': # dict
395                        _dict = {}
396                        while True:
397                                if value[1] == '}':
398                                        break
399                                key, next = self.unrepr(value[1:])
400                                if type(key) not in (str, unicode):
401                                        send_error('Wrong string: %s' % value)
402                                next = next.strip()
403                                if not next or next[0] != ':':
404                                        send_error('Wrong string: %s' % (value))
405                                val, next = self.unrepr(next[1:])
406                                _dict[key] = val
407                                next = next.strip()
408                                if not next:
409                                        break
410                                if next[0] == ',':
411                                        value = next
412                                elif next[0] == '}':
413                                        break
414                                else:
415                                        break
416                        return (_dict, next[1:])
417                elif first_char in ['[', '(']: # return list
418                        _tuple = []
419                        while True:
420                                if value[1] == ']':
421                                        break
422                                val, next = self.unrepr(value[1:])
423                                next = next.strip()
424                                if not next:
425                                        send_error('Wrong string: %s' % val)
426                                _tuple.append(val)
427                                next = next.strip()
428                                if not next:
429                                        break
430                                if next[0] == ',':
431                                        value = next
432                                elif next[0] in [']', ')']:
433                                        break
434                        return (_tuple, next[1:])
435               
436        def show_vcard_info(self, *args, **keyword):
437                ''' write user vcart in a formated output '''
438                props_dict = None
439                if _version[1] >= 30:
440                        props_dict = self.unrepr(args[0])
441                else:
442                        if args and len(args) >= 5:
443                                props_dict = self.unrepr(args[4].get_args_list()[0])
444                if props_dict:
445                        print self.print_info(0,props_dict[0])
446                # remove_signal_receiver is broken in lower versions (< 0.35),
447                # so we leave the leak - nothing can be done
448                if _version[1] >= 41:
449                        self.sbus.remove_signal_receiver(self.show_vcard_info, 'VcardInfo', 
450                                INTERFACE, SERVICE, OBJ_PATH)
451       
452                gtk.main_quit()
453               
454        def check_arguments(self):
455                ''' Make check if all necessary arguments are given '''
456                argv_len = self.argv_len - 2
457                args = self.commands[self.command][1]
458                if len(args) > argv_len:
459                        if args[argv_len][2]:
460                                send_error(_('Argument "%s" is not specified. \n\
461Type "%s help %s" for more info') % (args[argv_len][0], BASENAME, self.command))
462
463        def gtk_quit(self):
464                if _version[1] >= 41:
465                        self.sbus.remove_signal_receiver(self.show_vcard_info, 'VcardInfo', 
466                                INTERFACE, SERVICE, OBJ_PATH)
467                gtk.main_quit()
468       
469        # FIXME - didn't find more clever way for the below method.
470        # method(sys.argv[2:]) doesn't work, cos sys.argv[2:] is a tuple
471        def call_remote_method(self):
472                ''' calls self.method with arguments from sys.argv[2:] '''
473                try:
474                        if self.argv_len == 2:
475                                res = self.method()
476                        elif self.argv_len == 3:
477                                res = self.method(sys.argv[2])
478                        elif self.argv_len == 4:
479                                res = self.method(sys.argv[2], sys.argv[3])
480                        elif self.argv_len == 5:
481                                res = self.method(sys.argv[2], sys.argv[3], sys.argv[4])
482                        elif argv_len == 6:
483                                res = self.method(sys.argv[2], sys.argv[3], sys.argv[4], 
484                                        sys.argv[5])
485                        return res
486                except:
487                        send_error(_('Service not available'))
488                return None
489
490
491if __name__ == '__main__':
492        GajimRemote()
Note: See TracBrowser for help on using the browser.