root/branches/gajim_0.8/src/remote_control.py

Revision 3034, 12.3 kB (checked in by nk, 3 years ago)

in windows do not say about missing dbus bidingins

Line 
1##      roster_window.py
2##
3## Gajim Team:
4##      - Yann Le Boulanger <asterix@lagaule.org>
5##      - Vincent Hanquez <tab@snarc.org>
6##      - Nikos Kouremenos <kourem@gmail.com>
7##
8##      Copyright (C) 2003-2005 Gajim Team
9##
10## This program is free software; you can redistribute it and/or modify
11## it under the terms of the GNU General Public License as published
12## by the Free Software Foundation; version 2 only.
13##
14## This program is distributed in the hope that it will be useful,
15## but WITHOUT ANY WARRANTY; without even the implied warranty of
16## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17## GNU General Public License for more details.
18##
19
20import gobject
21import gtk
22import os
23
24from common import gajim
25from time import time
26from common import i18n
27
28_ = i18n._
29
30try:
31        import dbus
32        _version = getattr(dbus, 'version', (0, 20, 0)) 
33except ImportError:
34        _version = (0, 0, 0)
35       
36if _version >= (0, 41, 0):
37        import dbus.service
38        import dbus.glib # cause dbus 0.35+ doesn't return signal replies without it
39        DbusPrototype = dbus.service.Object
40elif _version >= (0, 20, 0):
41        DbusPrototype = dbus.Object
42else: #dbus is not defined
43        DbusPrototype = str 
44
45INTERFACE = 'org.gajim.dbus.RemoteInterface'
46OBJ_PATH = '/org/gajim/dbus/RemoteObject'
47SERVICE = 'org.gajim.dbus'
48
49class Remote:
50        def __init__(self, plugin):
51                self.signal_object = None
52                if 'dbus' not in globals() and not os.name == 'nt':
53                        print _('D-Bus python bindings are missing in this computer')
54                        print _('D-Bus capabilities of Gajim cannot be used')
55                        raise DbusNotSupported()
56                try:
57                        session_bus = dbus.SessionBus()
58                except:
59                        raise SessionBusNotPresent()
60               
61                if _version[1] >= 41:
62                        service = dbus.service.BusName(SERVICE, bus=session_bus)
63                        self.signal_object = SignalObject(service, plugin)
64                elif _version[1] <= 40 and _version[1] >= 20:
65                        service=dbus.Service(SERVICE, session_bus)
66                        self.signal_object = SignalObject(service, plugin)
67       
68        def set_enabled(self, status):
69                self.signal_object.disabled = not status
70               
71        def is_enabled(self):
72                return not self.signal_object.disabled
73
74        def raise_signal(self, signal, arg):
75                if self.signal_object:
76                        self.signal_object.raise_signal(signal, repr(arg))
77               
78
79class SignalObject(DbusPrototype):
80        ''' Local object definition for /org/gajim/dbus/RemoteObject. This doc must
81        not be visible, because the clients can access only the remote object. '''
82       
83        def __init__(self, service, plugin):
84                self.plugin = plugin
85                self.first_show = True
86                self.vcard_account = None
87                self.disabled = False
88
89                # register our dbus API
90                if _version[1] >= 41:
91                        DbusPrototype.__init__(self, service, OBJ_PATH)
92                elif _version[1] >= 30:
93                        DbusPrototype.__init__(self, OBJ_PATH, service)
94                else:
95                        DbusPrototype.__init__(self, OBJ_PATH, service, 
96                        [       self.toggle_roster_appearance,
97                                self.show_next_unread,
98                                self.list_contacts,
99                                self.list_accounts,
100                                self.change_status,
101                                self.open_chat,
102                                self.send_message,
103                                self.contact_info
104                        ])
105
106        def raise_signal(self, signal, arg):
107                ''' raise a signal, with a single string message '''
108                if self.disabled :
109                        return
110                if _version[1] >= 30:
111                        from dbus import dbus_bindings
112                        message = dbus_bindings.Signal(OBJ_PATH, INTERFACE, signal)
113                        i = message.get_iter(True)
114                        i.append(arg)
115                        self._connection.send(message)
116                else:
117                        self.emit_signal(INTERFACE, signal, arg)
118
119       
120        # signals
121        def VcardInfo(self, *vcard):
122                pass
123
124        def send_message(self, *args):
125                ''' send_message(jid, message, keyID=None, account=None)
126                send 'message' to 'jid', using account (optional) 'account'.
127                if keyID is specified, encrypt the message with the pgp key '''
128                if self.disabled:
129                        return
130                jid, message, keyID, account = self._get_real_arguments(args, 4)
131                if not jid or not message:
132                        return None # or raise error
133                if not keyID:
134                        keyID = ''
135                connected_account = None
136                accounts = gajim.contacts.keys()
137               
138                # if there is only one account in roster, take it as default
139                if not account and len(accounts) == 1:
140                        account = accounts[0]
141                if account:
142                        if gajim.connections[account].connected > 1: # account is  online
143                                connected_account = gajim.connections[account]
144                else:
145                        for account in accounts:
146                                if gajim.contacts[account].has_key(jid) and \
147                                        gajim.connections[account].connected > 1: # account is  online
148                                        connected_account = gajim.connections[account]
149                                        break
150                if connected_account:
151                        res = connected_account.send_message(jid, message, keyID)
152                        return True
153                return False
154
155        def open_chat(self, *args):
156                ''' start_chat(jid, account=None) -> shows the tabbed window for new
157                message to 'jid', using account(optional) 'account ' '''
158                if self.disabled:
159                        return
160                jid, account = self._get_real_arguments(args, 2)
161                if not jid:
162                        # FIXME: raise exception for missing argument (dbus0.35+ - released last week)
163                        return None
164                if account:
165                        accounts = [account]
166                else:
167                        accounts = gajim.connections.keys()
168                        if len(accounts) == 1:
169                                account = accounts[0]
170                connected_account = None
171                for acct in accounts:
172                        if gajim.connections[acct].connected > 1: # account is  online
173                                if self.plugin.windows[acct]['chats'].has_key(jid):
174                                        connected_account = acct
175                                        break
176                                # jid is in roster
177                                elif gajim.contacts[acct].has_key(jid):
178                                        connected_account = acct
179                                        break
180                                # we send the message to jid not in roster, because account is specified,
181                                # or there is only one account
182                                elif account: 
183                                        connected_account = acct
184                if connected_account:
185                        self.plugin.roster.new_chat_from_jid(connected_account, jid)
186                        # preserve the 'steal focus preservation'
187                        win = self.plugin.windows[connected_account]['chats'][jid].window
188                        if win.get_property('visible'):
189                                win.window.focus()
190                        return True
191                return False
192       
193        def change_status(self, *args, **keywords):
194                ''' change_status(status, message, account). account is optional -
195                if not specified status is changed for all accounts. '''
196                if self.disabled:
197                        return
198                status, message, account = self._get_real_arguments(args, 3)
199                if status not in ('offline', 'online', 'chat', 
200                        'away', 'xa', 'dnd', 'invisible'):
201                        # FIXME: raise exception for bad status (dbus0.35)
202                        return None
203                if account:
204                        gobject.idle_add(self.plugin.roster.send_status, account, 
205                                status, message)
206                else:
207                        # account not specified, so change the status of all accounts
208                        for acc in gajim.contacts.keys():
209                                gobject.idle_add(self.plugin.roster.send_status, acc, 
210                                        status, message)
211                return None
212
213        def show_next_unread(self, *args):
214                ''' Show the window(s) with next waiting messages in tabbed/group chats. '''
215                if self.disabled:
216                        return
217                #FIXME: when systray is disabled this method does nothing.
218                #FIXME: show message from GC that refer to us (like systray does)
219                if len(self.plugin.systray.jids) != 0:
220                        account = self.plugin.systray.jids[0][0]
221                        jid = self.plugin.systray.jids[0][1]
222                        acc = self.plugin.windows[account]
223                        jid_tab = None
224                        if acc['gc'].has_key(jid):
225                                jid_tab = acc['gc'][jid]
226                        elif acc['chats'].has_key(jid):
227                                jid_tab = acc['chats'][jid]
228                        else:
229                                self.plugin.roster.new_chat(
230                                        gajim.contacts[account][jid][0], account)
231                                jid_tab = acc['chats'][jid]
232                        if jid_tab:
233                                jid_tab.set_active_tab(jid)
234                                jid_tab.window.present()
235                                # preserve the 'steal focus preservation'
236                                if self._is_first():
237                                        jid_tab.window.window.focus()
238                                else:
239                                        jid_tab.window.window.focus(long(time()))
240                               
241
242        def contact_info(self, *args):
243                ''' get vcard info for a contact. This method returns nothing.
244                You have to register the 'VcardInfo' signal to get the real vcard. '''
245                if self.disabled:
246                        return
247               
248                [jid] = self._get_real_arguments(args, 1)
249                if not jid:
250                        # FIXME: raise exception for missing argument (0.3+)
251                        return None
252
253                accounts = gajim.contacts.keys()
254               
255                for account in accounts:
256                        if gajim.contacts[account].has_key(jid):
257                                self.vcard_account =  account
258                                gajim.connections[account].register_handler('VCARD', 
259                                        self._receive_vcard)
260                                gajim.connections[account].request_vcard(jid)
261                                break
262                return None
263
264        def list_accounts(self, *args):
265                ''' list register accounts '''
266                if self.disabled:
267                        return
268                if gajim.contacts:
269                        result = gajim.contacts.keys()
270                        if result and len(result) > 0:
271                                return result
272                return None
273
274
275        def list_contacts(self, *args):
276                if self.disabled:
277                        return
278                ''' list all contacts in the roster. If the first argument is specified,
279                then return the contacts for the specified account '''
280                [for_account] = self._get_real_arguments(args, 1)
281                result = []
282                if not gajim.contacts or len(gajim.contacts) == 0:
283                        return None
284                if for_account:
285                        if gajim.contacts.has_key(for_account):
286                                for jid in gajim.contacts[for_account]:
287                                        item = self._serialized_contacts(
288                                                gajim.contacts[for_account][jid])
289                                        if item:
290                                                result.append(item)
291                        else:
292                                # 'for_account: is not recognised:',
293                                # FIXME: there can be a return status for this [0.3+]
294                                return None
295                else:
296                        for account in gajim.contacts:
297                                for jid in gajim.contacts[account]:
298                                        item = self._serialized_contacts(gajim.contacts[account][jid])
299                                        if item:
300                                                result.append(item)
301                # dbus 0.40 does not support return result as empty list
302                if result == []:
303                        return None
304                return result
305
306        def toggle_roster_appearance(self, *args):
307                ''' shows/hides the roster window '''
308                if self.disabled:
309                        return
310                win = self.plugin.roster.window
311                if win.get_property('visible'):
312                        gobject.idle_add(win.hide)
313                else:
314                        win.present()
315                        # preserve the 'steal focus preservation'
316                        if self._is_first():
317                                win.window.focus()
318                        else:
319                                win.window.focus(long(time()))
320
321        def _is_first(self):
322                if self.first_show:
323                        self.first_show = False
324                        return True
325                return False
326
327        def _receive_vcard(self,account, array):
328                if self.vcard_account:
329                        gajim.connections[self.vcard_account].unregister_handler('VCARD', 
330                                self._receive_vcard)
331                        self.unregistered_vcard = None
332                        if self.disabled:
333                                return
334                        if _version[1] >=30:
335                                self.VcardInfo(repr(array))
336                        else:
337                                self.emit_signal(INTERFACE, 'VcardInfo', 
338                                        repr(array))
339
340        def _get_real_arguments(self, args, desired_length):
341                # supresses the first 'message' argument, which is set in dbus 0.23
342                if _version[1] == 20:
343                        args=args[1:]
344                if desired_length > 0:
345                        args = list(args)
346                        args.extend([None] * (desired_length - len(args)))
347                        args = args[:desired_length]
348                return args
349
350        def _serialized_contacts(self, contacts):
351                ''' get info from list of Contact objects and create a serialized
352                dict for sending it over dbus '''
353                if not contacts:
354                        return None
355                prim_contact = None # primary contact
356                for contact in contacts:
357                        if prim_contact == None or contact.priority > prim_contact.priority:
358                                prim_contact = contact
359                contact_dict = {}
360                contact_dict['name'] = prim_contact.name
361                contact_dict['show'] = prim_contact.show
362                contact_dict['jid'] = prim_contact.jid
363                if prim_contact.keyID:
364                        keyID = None
365                        if len(prim_contact.keyID) == 8:
366                                keyID = prim_contact.keyID
367                        elif len(prim_contact.keyID) == 16:
368                                keyID = prim_contact.keyID[8:]
369                        if keyID:
370                                contact_dict['openpgp'] = keyID
371                contact_dict['resources'] = []
372                for contact in contacts:
373                        contact_dict['resources'].append(tuple([contact.resource, 
374                                contact.priority, contact.status]))
375                return repr(contact_dict)
376       
377       
378        if _version[1] >= 30 and _version[1] <= 40:
379                method = dbus.method
380                signal = dbus.signal
381        elif _version[1] >= 41:
382                method = dbus.service.method
383                signal = dbus.service.signal
384
385        if _version[1] >= 30:
386                # prevent using decorators, because they are not supported
387                # on python < 2.4
388                # FIXME: use decorators when python2.3 (and dbus 0.23) is OOOOOOLD
389                toggle_roster_appearance = method(INTERFACE)(toggle_roster_appearance)
390                list_contacts = method(INTERFACE)(list_contacts)
391                list_accounts = method(INTERFACE)(list_accounts)
392                show_next_unread = method(INTERFACE)(show_next_unread)
393                change_status = method(INTERFACE)(change_status)
394                open_chat = method(INTERFACE)(open_chat)
395                contact_info = method(INTERFACE)(contact_info)
396                send_message = method(INTERFACE)(send_message)
397                VcardInfo = signal(INTERFACE)(VcardInfo)
398
399class SessionBusNotPresent(Exception):
400        ''' This exception indicates that there is no session daemon '''
401        def __init__(self):
402                Exception.__init__(self)
403
404        def __str__(self):
405                return _('Session bus is not available')
406
407class DbusNotSupported(Exception):
408        ''' D-Bus is not installed or python bindings are missing '''
409        def __init__(self):
410                Exception.__init__(self)
411
412        def __str__(self):
413                return _('D-Bus is not present on this machine')
Note: See TracBrowser for help on using the browser.