root/trunk/src/gajim-remote.py

Revision 10630, 15.9 kB (checked in by jim++, 3 weeks ago)

Removed some unused imports

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