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

Revision 4814, 15.5 kB (checked in by asterix, 3 years ago)

fix TB (missing self.)

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