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

Revision 7829, 13.8 kB (checked in by asterix, 20 months ago)

merge diff from trunc to 0.11 branch

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2##
3## Copyright (C) 2005-2006 Yann Le Boulanger <asterix@lagaule.org>
4## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com>
5## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
6##
7## This program is free software; you can redistribute it and/or modify
8## it under the terms of the GNU General Public License as published
9## by the Free Software Foundation; version 2 only.
10##
11## This program is distributed in the hope that it will be useful,
12## but WITHOUT ANY WARRANTY; without even the implied warranty of
13## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14## GNU General Public License for more details.
15##
16
17# gajim-remote help will show you the D-BUS API of Gajim
18
19import sys
20import locale
21import signal
22signal.signal(signal.SIGINT, signal.SIG_DFL) # ^C exits the application
23
24from common import exceptions
25from common import i18n
26
27try:
28        PREFERRED_ENCODING = locale.getpreferredencoding()
29except:
30        sys.exc_clear()
31        PREFERRED_ENCODING = 'UTF-8'
32
33def send_error(error_message):
34        '''Writes error message to stderr and exits'''
35        print >> sys.stderr, error_message.encode(PREFERRED_ENCODING)
36        sys.exit(1)
37
38try:
39        import dbus
40        import dbus.service
41        import dbus.glib
42except:
43        print str(exceptions.DbusNotSupported())
44        sys.exit(1)
45
46OBJ_PATH = '/org/gajim/dbus/RemoteObject'
47INTERFACE = 'org.gajim.dbus.RemoteInterface'
48SERVICE = 'org.gajim.dbus'
49BASENAME = 'gajim-remote'
50
51
52class GajimRemote:
53
54        def __init__(self):
55                self.argv_len = len(sys.argv)
56                # define commands dict. Prototype :
57                # {
58                #       'command': [comment, [list of arguments] ]
59                # }
60                #
61                # each argument is defined as a tuple:
62                #    (argument name, help on argument, is mandatory)
63                #
64                self.commands = {
65                        'help':[
66                                        _('Shows a help on specific command'),
67                                        [
68                                                #User gets help for the command, specified by this parameter
69                                                (_('command'),
70                                                _('show help on command'), False)
71                                        ]
72                                ],
73                        'toggle_roster_appearance' : [
74                                        _('Shows or hides the roster window'),
75                                        []
76                                ],
77                        'show_next_pending_event': [
78                                        _('Pops up a window with the next pending event'),
79                                        []
80                                ],
81                        'list_contacts': [
82                                        _('Prints a list of all contacts in the roster. Each contact '
83                                        'appears on a separate line'),
84                                        [
85                                                (_('account'), _('show only contacts of the given account'),
86                                                        False)
87                                        ]
88
89                                ],     
90                        'list_accounts': [
91                                        _('Prints a list of registered accounts'),
92                                        []
93                                ],
94                        'change_status': [
95                                        _('Changes the status of account or accounts'),
96                                        [
97#offline, online, chat, away, xa, dnd, invisible should not be translated
98                                                (_('status'), _('one of: offline, online, chat, away, xa, dnd, invisible '), True),
99                                                (_('message'), _('status message'), False),
100                                                (_('account'), _('change status of account "account". '
101                'If not specified, try to change status of all accounts that have '
102                '"sync with global status" option set'), False)
103                                        ]
104                                ],
105                        'open_chat': [
106                                        _('Shows the chat dialog so that you can send messages to a contact'),
107                                        [
108                                                ('jid', _('JID of the contact that you want to chat with'),
109                                                        True),
110                                                (_('account'), _('if specified, contact is taken from the '
111                                                'contact list of this account'), False)
112                                        ]
113                                ],
114                        'send_chat_message':[
115                                        _('Sends new chat message to a contact in the roster. Both OpenPGP key '
116                                        'and account are optional. If you want to set only \'account\', '
117                                        'without \'OpenPGP key\', just set \'OpenPGP key\' to \'\'.'),
118                                        [
119                                                ('jid', _('JID of the contact that will receive the message'), True),
120                                                (_('message'), _('message contents'), True),
121                                                (_('pgp key'), _('if specified, the message will be encrypted '
122                                                        'using this public key'), False),
123                                                (_('account'), _('if specified, the message will be sent '
124                                                        'using this account'), False),
125                                        ]
126                                ],
127                        'send_single_message':[
128                                        _('Sends new single 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                                                (_('subject'), _('message subject'), True),
134                                                (_('message'), _('message contents'), True),
135                                                (_('pgp key'), _('if specified, the message will be encrypted '
136                                                        'using this public key'), False),
137                                                (_('account'), _('if specified, the message will be sent '
138                                                        'using this account'), False),
139                                        ]
140                                ],
141                        'contact_info': [
142                                        _('Gets detailed info on a contact'),
143                                        [
144                                                ('jid', _('JID of the contact'), True)
145                                        ]
146                                ],
147                        'account_info': [
148                                        _('Gets detailed info on a account'),
149                                        [
150                                                ('account', _('Name of the account'), True)
151                                        ]
152                                ],
153                        'send_file': [
154                                        _('Sends file to a contact'),
155                                        [
156                                                (_('file'), _('File path'), True),
157                                                ('jid', _('JID of the contact'), True),
158                                                (_('account'), _('if specified, file will be sent using this '
159                                                        'account'), False)
160                                        ]
161                                ],
162                        'prefs_list': [
163                                        _('Lists all preferences and their values'),
164                                        [ ]
165                                ],
166                        'prefs_put': [
167                                        _('Sets value of \'key\' to \'value\'.'),
168                                        [
169                                                (_('key=value'), _('\'key\' is the name of the preference, '
170                                                        '\'value\' is the value to set it to'), True)
171                                        ]
172                                ],
173                        'prefs_del': [
174                                        _('Deletes a preference item'),
175                                        [
176                                                (_('key'), _('name of the preference to be deleted'), True)
177                                        ]
178                                ],
179                        'prefs_store': [
180                                        _('Writes the current state of Gajim preferences to the .config '
181                                                'file'),
182                                        [ ]
183                                ],
184                        'remove_contact': [
185                                        _('Removes contact from roster'),
186                                        [
187                                                ('jid', _('JID of the contact'), True),
188                                                (_('account'), _('if specified, contact is taken from the '
189                                                        'contact list of this account'), False)
190
191                                        ]
192                                ],
193                        'add_contact': [
194                                        _('Adds contact to roster'),
195                                        [
196                                                (_('jid'), _('JID of the contact'), True),
197                                                (_('account'), _('Adds new contact to this account'), False)
198                                        ]
199                                ],
200
201                        'get_status': [
202                                _('Returns current status (the global one unless account is specified)'),
203                                        [
204                                                (_('account'), _(''), False)
205                                        ]
206                                ],
207
208                        'get_status_message': [
209                                _('Returns current status message(the global one unless account is specified)'),
210                                        [
211                                                (_('account'), _(''), False)
212                                        ]
213                                ],                             
214
215                        'get_unread_msgs_number': [
216                                _('Returns number of unread messages'),
217                                        [ ]
218                                ],
219                        'start_chat': [
220                                _('Opens \'Start Chat\' dialog'),
221                                        [
222                                                (_('account'), _('Starts chat, using this account'), True)
223                                        ]
224                                ],
225                        'send_xml': [
226                                        _('Sends custom XML'),
227                                        [
228                                                ('xml', _('XML to send'), True),
229                                                ('account', _('Account in which the xml will be sent; '
230                                                'if not specified, xml will be sent to all accounts'),
231                                                        False)
232                                        ]
233                                ],
234                        'handle_uri': [
235                                        _('Handle a xmpp:/ uri'),
236                                        [
237                                                (_('uri'), _(''), True),
238                                                (_('account'), _(''), False)
239                                        ]
240                                ],
241                        'join_room': [
242                                        _('Join a MUC room'),
243                                        [
244                                                (_('room'), _(''), True),
245                                                (_('nick'), _(''), False),
246                                                (_('password'), _(''), False),
247                                                (_('account'), _(''), False)
248                                        ]
249                                ],
250
251                        }
252                if self.argv_len  < 2 or \
253                        sys.argv[1] not in self.commands.keys(): # no args or bad args
254                        send_error(self.compose_help())
255                self.command = sys.argv[1]
256                if self.command == 'help':
257                        if self.argv_len == 3:
258                                print self.help_on_command(sys.argv[2]).encode(PREFERRED_ENCODING)
259                        else:
260                                print self.compose_help().encode(PREFERRED_ENCODING)
261                        sys.exit(0)
262                if self.command == 'handle_uri':
263                        self.handle_uri()
264                self.init_connection()
265                self.check_arguments()
266
267                if self.command == 'contact_info':
268                        if self.argv_len < 3:
269                                send_error(_('Missing argument "contact_jid"'))
270
271                try:
272                        res = self.call_remote_method()
273                except exceptions.ServiceNotAvailable:
274                        # At this point an error message has already been displayed
275                        sys.exit(1)
276                else:
277                        self.print_result(res)
278
279        def print_result(self, res):
280                ''' Print retrieved result to the output '''
281                if res is not None:
282                        if self.command in ('open_chat', 'send_chat_message', 'send_single_message', 'start_chat'):
283                                if self.command in ('send_message', 'send_single_message'):
284                                        self.argv_len -= 2
285
286                                if res is False:
287                                        if self.argv_len < 4:
288                                                send_error(_('\'%s\' is not in your roster.\n'
289                                        'Please specify account for sending the message.') % sys.argv[2])
290                                        else:
291                                                send_error(_('You have no active account'))
292                        elif self.command == 'list_accounts':
293                                if isinstance(res, list):
294                                        for account in res:
295                                                if isinstance(account, unicode):
296                                                        print account.encode(PREFERRED_ENCODING)
297                                                else:
298                                                        print account
299                        elif self.command == 'account_info':
300                                if res:
301                                        print self.print_info(0, res, True)
302                        elif self.command == 'list_contacts':
303                                for account_dict in res:
304                                        print self.print_info(0, account_dict, True)
305                        elif self.command == 'prefs_list':
306                                pref_keys = res.keys()
307                                pref_keys.sort()
308                                for pref_key in pref_keys:
309                                        result = '%s = %s' % (pref_key, res[pref_key])
310                                        if isinstance(result, unicode):
311                                                print result.encode(PREFERRED_ENCODING)
312                                        else:
313                                                print result
314                        elif self.command == 'contact_info':
315                                print self.print_info(0, res, True)
316                        elif res:
317                                print unicode(res).encode(PREFERRED_ENCODING)
318
319        def init_connection(self):
320                ''' create the onnection to the session dbus,
321                or exit if it is not possible '''
322                try:
323                        self.sbus = dbus.SessionBus()
324                except:
325                        raise exceptions.SessionBusNotPresent
326
327                obj = self.sbus.get_object(SERVICE, OBJ_PATH)
328                interface = dbus.Interface(obj, INTERFACE)
329
330                # get the function asked
331                self.method = interface.__getattr__(self.command)
332
333        def make_arguments_row(self, args):
334                ''' return arguments list. Mandatory arguments are enclosed with:
335                '<', '>', optional arguments - with '[', ']' '''
336                str = ''
337                for argument in args:
338                        str += ' '
339                        if argument[2]:
340                                str += '<'
341                        else:
342                                str += '['
343                        str += argument[0]
344                        if argument[2]:
345                                str += '>'
346                        else:
347                                str += ']'
348                return str
349
350        def help_on_command(self, command):
351                ''' return help message for a given command '''
352                if command in self.commands:
353                        command_props = self.commands[command]
354                        arguments_str = self.make_arguments_row(command_props[1])
355                        str = _('Usage: %s %s %s \n\t %s') % (BASENAME, command,
356                                        arguments_str, command_props[0])
357                        if len(command_props[1]) > 0:
358                                str += '\n\n' + _('Arguments:') + '\n'
359                                for argument in command_props[1]:
360                                        str += ' ' +  argument[0] + ' - ' + argument[1] + '\n'
361                        return str
362                send_error(_('%s not found') % command)
363
364        def compose_help(self):
365                ''' print usage, and list available commands '''
366                str = _('Usage: %s command [arguments]\nCommand is one of:\n' ) % BASENAME
367                commands = self.commands.keys()
368                commands.sort()
369                for command in commands:
370                        str += '  ' + command
371                        for argument in self.commands[command][1]:
372                                str += ' '
373                                if argument[2]:
374                                        str += '<'
375                                else:
376                                        str += '['
377                                str += argument[0]
378                                if argument[2]:
379                                        str += '>'
380                                else:
381                                        str += ']'
382                        str += '\n'
383                return str
384
385        def print_info(self, level, prop_dict, encode_return = False):
386                ''' return formated string from data structure '''
387                if prop_dict is None or not isinstance(prop_dict, (dict, list, tuple)):
388                        return ''
389                ret_str = ''
390                if isinstance(prop_dict, (list, tuple)):
391                        ret_str = ''
392                        spacing = ' ' * level * 4
393                        for val in prop_dict:
394                                if val is None:
395                                        ret_str +='\t'
396                                elif isinstance(val, int):
397                                        ret_str +='\t' + str(val)
398                                elif isinstance(val, (str, unicode)):
399                                        ret_str +='\t' + val
400                                elif isinstance(val, (list, tuple)):
401                                        res = ''
402                                        for items in val:
403                                                res += self.print_info(level+1, items)
404                                        if res != '':
405                                                ret_str += '\t' + res
406                                elif isinstance(val, dict):
407                                        ret_str += self.print_info(level+1, val)
408                        ret_str = '%s(%s)\n' % (spacing, ret_str[1:])
409                elif isinstance(prop_dict, dict):
410                        for key in prop_dict.keys():
411                                val = prop_dict[key]
412                                spacing = ' ' * level * 4
413                                if isinstance(val, (unicode, int, str)):
414                                        if val is not None:
415                                                val = val.strip()
416                                                ret_str += '%s%-10s: %s\n' % (spacing, key, val)
417                                elif isinstance(val, (list, tuple)):
418                                        res = ''
419                                        for items in val:
420                                                res += self.print_info(level+1, items)
421                                        if res != '':
422                                                ret_str += '%s%s: \n%s' % (spacing, key, res)
423                                elif isinstance(val, dict):
424                                        res = self.print_info(level+1, val)
425                                        if res != '':
426                                                ret_str += '%s%s: \n%s' % (spacing, key, res)
427                if (encode_return):
428                        try:
429                                ret_str = ret_str.encode(PREFERRED_ENCODING)
430                        except:
431                                pass
432                return ret_str
433
434        def check_arguments(self):
435                ''' Make check if all necessary arguments are given '''
436                argv_len = self.argv_len - 2
437                args = self.commands[self.command][1]
438                if len(args) < argv_len:
439                        send_error(_('Too many arguments. \n'
440                                'Type "%s help %s" for more info') % (BASENAME, self.command))
441                if len(args) > argv_len:
442                        if args[argv_len][2]:
443                                send_error(_('Argument "%s" is not specified. \n'
444                                        'Type "%s help %s" for more info') %
445                                        (args[argv_len][0], BASENAME, self.command))
446                self.arguments = []
447                i = 0
448                for arg in sys.argv[2:]:
449                        i += 1
450                        if i < len(args):
451                                self.arguments.append(arg)
452                        else:
453                                # it's latest argument with spaces
454                                self.arguments.append(' '.join(sys.argv[i+1:]))
455                                break
456                # add empty string for missing args
457                self.arguments += ['']*(len(args)-i)
458
459        def handle_uri(self):
460                if not sys.argv[2:][0].startswith('xmpp:'):
461                        send_error(_('Wrong uri'))
462                sys.argv[2] = sys.argv[2][5:]
463                uri = sys.argv[2:][0]
464                if not '?' in uri:
465                        self.command = sys.argv[1] = 'open_chat'
466                        return
467                (jid, action) = uri.split('?', 1)
468                sys.argv[2] = jid
469                if action == 'join':
470                        self.command = sys.argv[1] = 'join_room'
471                        return
472                       
473                sys.exit(0)
474
475        def call_remote_method(self):
476                ''' calls self.method with arguments from sys.argv[2:] '''
477                args = [i.decode(PREFERRED_ENCODING) for i in self.arguments]
478                args = [dbus.String(i) for i in args]
479                try:
480                        res = self.method(*args)
481                        return res
482                except Exception:
483                        raise exceptions.ServiceNotAvailable
484                return None
485
486if __name__ == '__main__':
487        GajimRemote()
Note: See TracBrowser for help on using the browser.