[kepi] ability to use subkeys. Fixes #6051
[gajim.git] / src / remote_control.py
blob4dfe18f60477d3545a31d7d53adac1a2bbbdfa71
1 # -*- coding:utf-8 -*-
2 ## src/remote_control.py
3 ##
4 ## Copyright (C) 2005-2006 Andrew Sayman <lorien420 AT myrealbox.com>
5 ## Dimitur Kirov <dkirov AT gmail.com>
6 ## Nikos Kouremenos <kourem AT gmail.com>
7 ## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
8 ## Copyright (C) 2006-2007 Travis Shirk <travis AT pobox.com>
9 ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
10 ## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
11 ## Julien Pivotto <roidelapluie AT gmail.com>
12 ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
14 ## This file is part of Gajim.
16 ## Gajim is free software; you can redistribute it and/or modify
17 ## it under the terms of the GNU General Public License as published
18 ## by the Free Software Foundation; version 3 only.
20 ## Gajim is distributed in the hope that it will be useful,
21 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 ## GNU General Public License for more details.
25 ## You should have received a copy of the GNU General Public License
26 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
29 import gobject
30 import gtk
31 import os
32 import base64
33 import mimetypes
35 from common import gajim
36 from common import helpers
37 from time import time
38 from dialogs import AddNewContactWindow, NewChatDialog, JoinGroupchatWindow
39 from common import ged
41 from common import dbus_support
42 if dbus_support.supported:
43 import dbus
44 if dbus_support:
45 import dbus.service
46 import dbus.glib
48 INTERFACE = 'org.gajim.dbus.RemoteInterface'
49 OBJ_PATH = '/org/gajim/dbus/RemoteObject'
50 SERVICE = 'org.gajim.dbus'
52 # type mapping
54 # in most cases it is a utf-8 string
55 DBUS_STRING = dbus.String
57 # general type (for use in dicts, where all values should have the same type)
58 DBUS_BOOLEAN = dbus.Boolean
59 DBUS_DOUBLE = dbus.Double
60 DBUS_INT32 = dbus.Int32
61 # dictionary with string key and binary value
62 DBUS_DICT_SV = lambda : dbus.Dictionary({}, signature="sv")
63 # dictionary with string key and value
64 DBUS_DICT_SS = lambda : dbus.Dictionary({}, signature="ss")
65 # empty type (there is no equivalent of None on D-Bus, but historically gajim
66 # used 0 instead)
67 DBUS_NONE = lambda : dbus.Int32(0)
69 def get_dbus_struct(obj):
70 """
71 Recursively go through all the items and replace them with their casted dbus
72 equivalents
73 """
74 if obj is None:
75 return DBUS_NONE()
76 if isinstance(obj, (unicode, str)):
77 return DBUS_STRING(obj)
78 if isinstance(obj, int):
79 return DBUS_INT32(obj)
80 if isinstance(obj, float):
81 return DBUS_DOUBLE(obj)
82 if isinstance(obj, bool):
83 return DBUS_BOOLEAN(obj)
84 if isinstance(obj, (list, tuple)):
85 result = dbus.Array([get_dbus_struct(i) for i in obj],
86 signature='v')
87 if result == []:
88 return DBUS_NONE()
89 return result
90 if isinstance(obj, dict):
91 result = DBUS_DICT_SV()
92 for key, value in obj.items():
93 result[DBUS_STRING(key)] = get_dbus_struct(value)
94 if result == {}:
95 return DBUS_NONE()
96 return result
97 # unknown type
98 return DBUS_NONE()
100 class Remote:
101 def __init__(self):
102 self.signal_object = None
103 session_bus = dbus_support.session_bus.SessionBus()
105 bus_name = dbus.service.BusName(SERVICE, bus=session_bus)
106 self.signal_object = SignalObject(bus_name)
108 gajim.ged.register_event_handler('last-result-received', ged.POSTGUI,
109 self.on_last_status_time)
110 gajim.ged.register_event_handler('version-result-received', ged.POSTGUI,
111 self.on_os_info)
112 gajim.ged.register_event_handler('time-result-received', ged.POSTGUI,
113 self.on_time)
114 gajim.ged.register_event_handler('gmail-nofify', ged.POSTGUI,
115 self.on_gmail_notify)
116 gajim.ged.register_event_handler('roster-info', ged.POSTGUI,
117 self.on_roster_info)
118 gajim.ged.register_event_handler('presence-received', ged.POSTGUI,
119 self.on_presence_received)
120 gajim.ged.register_event_handler('subscribe-presence-received',
121 ged.POSTGUI, self.on_subscribe_presence_received)
122 gajim.ged.register_event_handler('subscribed-presence-received',
123 ged.POSTGUI, self.on_subscribed_presence_received)
124 gajim.ged.register_event_handler('unsubscribed-presence-received',
125 ged.POSTGUI, self.on_unsubscribed_presence_received)
126 gajim.ged.register_event_handler('gc-message-received',
127 ged.POSTGUI, self.on_gc_message_received)
128 gajim.ged.register_event_handler('our-show', ged.POSTGUI,
129 self.on_our_status)
130 gajim.ged.register_event_handler('account-created', ged.POSTGUI,
131 self.on_account_created)
132 gajim.ged.register_event_handler('vcard-received', ged.POSTGUI,
133 self.on_vcard_received)
134 gajim.ged.register_event_handler('chatstate-received', ged.POSTGUI,
135 self.on_chatstate_received)
136 gajim.ged.register_event_handler('message-sent', ged.POSTGUI,
137 self.on_message_sent)
139 def on_chatstate_received(self, obj):
140 self.raise_signal('ChatState', (obj.conn.name, [
141 obj.jid, obj.fjid, obj.stanza, obj.resource, obj.composing_xep,
142 obj.chatstate]))
144 def on_message_sent(self, obj):
145 try:
146 chatstate = obj.chatstate
147 except AttributeError:
148 chatstate = ""
149 self.raise_signal('MessageSent', (obj.conn.name, [
150 obj.jid, obj.message, obj.keyID, chatstate]))
152 def on_last_status_time(self, obj):
153 self.raise_signal('LastStatusTime', (obj.conn.name, [
154 obj.jid, obj.resource, obj.seconds, obj.status]))
156 def on_os_info(self, obj):
157 self.raise_signal('OsInfo', (obj.conn.name, [obj.jid, obj.resource,
158 obj.client_info, obj.os_info]))
160 def on_time(self, obj):
161 self.raise_signal('EntityTime', (obj.conn.name, [obj.jid, obj.resource,
162 obj.time_info]))
164 def on_gmail_notify(self, obj):
165 self.raise_signal('NewGmail', (obj.conn.name, [obj.jid, obj.newmsgs,
166 obj.gmail_messages_list]))
168 def on_roster_info(self, obj):
169 self.raise_signal('RosterInfo', (obj.conn.name, [obj.jid, obj.nickname,
170 obj.sub, obj.ask, obj.groups]))
172 def on_presence_received(self, obj):
173 event = None
174 if obj.old_show < 2 and obj.new_show > 1:
175 event = 'ContactPresence'
176 elif obj.old_show > 1 and obj.new_show < 2:
177 event = 'ContactAbsence'
178 elif obj.new_show > 1:
179 event = 'ContactStatus'
180 if event:
181 self.raise_signal(event, (obj.conn.name, [obj.jid, obj.show,
182 obj.status, obj.resource, obj.prio, obj.keyID, obj.timestamp,
183 obj.contact_nickname]))
185 def on_subscribe_presence_received(self, obj):
186 self.raise_signal('Subscribe', (obj.conn.name, [obj.jid, obj.status,
187 obj.user_nick]))
189 def on_subscribed_presence_received(self, obj):
190 self.raise_signal('Subscribed', (obj.conn.name, [obj.jid,
191 obj.resource]))
193 def on_unsubscribed_presence_received(self, obj):
194 self.raise_signal('Unsubscribed', (obj.conn.name, obj.jid))
196 def on_gc_message_received(self, obj):
197 if not hasattr(obj, 'needs_highlight'):
198 # event has not been handled at GUI level
199 return
200 self.raise_signal('GCMessage', (obj.conn.name, [obj.fjid, obj.msgtxt,
201 obj.timestamp, obj.has_timestamp, obj.xhtml_msgtxt, obj.status_code,
202 obj.displaymarking, obj.captcha_form, obj.needs_highlight]))
204 def on_our_status(self, obj):
205 self.raise_signal('AccountPresence', (obj.show, obj.conn.name))
207 def on_account_created(self, obj):
208 self.raise_signal('NewAccount', (obj.conn.name, obj.account_info))
210 def on_vcard_received(self, obj):
211 self.raise_signal('VcardInfo', (obj.conn.name, obj.vcard_dict))
213 def raise_signal(self, signal, arg):
214 if self.signal_object:
215 try:
216 getattr(self.signal_object, signal)(get_dbus_struct(arg))
217 except UnicodeDecodeError:
218 pass # ignore error when we fail to announce on dbus
221 class SignalObject(dbus.service.Object):
223 Local object definition for /org/gajim/dbus/RemoteObject
225 This docstring is not be visible, because the clients can access only the
226 remote object.
229 def __init__(self, bus_name):
230 self.first_show = True
231 self.vcard_account = None
233 # register our dbus API
234 dbus.service.Object.__init__(self, bus_name, OBJ_PATH)
236 @dbus.service.signal(INTERFACE, signature='av')
237 def Roster(self, account_and_data):
238 pass
240 @dbus.service.signal(INTERFACE, signature='av')
241 def AccountPresence(self, status_and_account):
242 pass
244 @dbus.service.signal(INTERFACE, signature='av')
245 def ContactPresence(self, account_and_array):
246 pass
248 @dbus.service.signal(INTERFACE, signature='av')
249 def ContactAbsence(self, account_and_array):
250 pass
252 @dbus.service.signal(INTERFACE, signature='av')
253 def ContactStatus(self, account_and_array):
254 pass
256 @dbus.service.signal(INTERFACE, signature='av')
257 def NewMessage(self, account_and_array):
258 pass
260 @dbus.service.signal(INTERFACE, signature='av')
261 def Subscribe(self, account_and_array):
262 pass
264 @dbus.service.signal(INTERFACE, signature='av')
265 def Subscribed(self, account_and_array):
266 pass
268 @dbus.service.signal(INTERFACE, signature='av')
269 def Unsubscribed(self, account_and_jid):
270 pass
272 @dbus.service.signal(INTERFACE, signature='av')
273 def NewAccount(self, account_and_array):
274 pass
276 @dbus.service.signal(INTERFACE, signature='av')
277 def VcardInfo(self, account_and_vcard):
278 pass
280 @dbus.service.signal(INTERFACE, signature='av')
281 def LastStatusTime(self, account_and_array):
282 pass
284 @dbus.service.signal(INTERFACE, signature='av')
285 def OsInfo(self, account_and_array):
286 pass
288 @dbus.service.signal(INTERFACE, signature='av')
289 def EntityTime(self, account_and_array):
290 pass
292 @dbus.service.signal(INTERFACE, signature='av')
293 def GCPresence(self, account_and_array):
294 pass
296 @dbus.service.signal(INTERFACE, signature='av')
297 def GCMessage(self, account_and_array):
298 pass
300 @dbus.service.signal(INTERFACE, signature='av')
301 def RosterInfo(self, account_and_array):
302 pass
304 @dbus.service.signal(INTERFACE, signature='av')
305 def NewGmail(self, account_and_array):
306 pass
308 @dbus.service.signal(INTERFACE, signature='av')
309 def ChatState(self, account_and_array):
310 pass
312 @dbus.service.signal(INTERFACE, signature='av')
313 def MessageSent(self, account_and_array):
314 pass
316 def raise_signal(self, signal, arg):
318 Raise a signal, with a single argument of unspecified type Instead of
319 obj.raise_signal("Foo", bar), use obj.Foo(bar)
321 getattr(self, signal)(arg)
323 @dbus.service.method(INTERFACE, in_signature='s', out_signature='s')
324 def get_status(self, account):
326 Return status (show to be exact) which is the global one unless account is
327 given
329 if not account:
330 # If user did not ask for account, returns the global status
331 return DBUS_STRING(helpers.get_global_show())
332 # return show for the given account
333 index = gajim.connections[account].connected
334 return DBUS_STRING(gajim.SHOW_LIST[index])
336 @dbus.service.method(INTERFACE, in_signature='s', out_signature='s')
337 def get_status_message(self, account):
339 Return status which is the global one unless account is given
341 if not account:
342 # If user did not ask for account, returns the global status
343 return DBUS_STRING(str(helpers.get_global_status()))
344 # return show for the given account
345 status = gajim.connections[account].status
346 return DBUS_STRING(status)
348 def _get_account_and_contact(self, account, jid):
350 Get the account (if not given) and contact instance from jid
352 connected_account = None
353 contact = None
354 accounts = gajim.contacts.get_accounts()
355 # if there is only one account in roster, take it as default
356 # if user did not ask for account
357 if not account and len(accounts) == 1:
358 account = accounts[0]
359 if account:
360 if gajim.connections[account].connected > 1: # account is connected
361 connected_account = account
362 contact = gajim.contacts.get_contact_with_highest_priority(account,
363 jid)
364 else:
365 for account in accounts:
366 contact = gajim.contacts.get_contact_with_highest_priority(account,
367 jid)
368 if contact and gajim.connections[account].connected > 1:
369 # account is connected
370 connected_account = account
371 break
372 if not contact:
373 contact = jid
375 return connected_account, contact
377 def _get_account_for_groupchat(self, account, room_jid):
379 Get the account which is connected to groupchat (if not given)
380 or check if the given account is connected to the groupchat
382 connected_account = None
383 accounts = gajim.contacts.get_accounts()
384 # if there is only one account in roster, take it as default
385 # if user did not ask for account
386 if not account and len(accounts) == 1:
387 account = accounts[0]
388 if account:
389 if gajim.connections[account].connected > 1 and \
390 room_jid in gajim.gc_connected[account] and \
391 gajim.gc_connected[account][room_jid]:
392 # account and groupchat are connected
393 connected_account = account
394 else:
395 for account in accounts:
396 if gajim.connections[account].connected > 1 and \
397 room_jid in gajim.gc_connected[account] and \
398 gajim.gc_connected[account][room_jid]:
399 # account and groupchat are connected
400 connected_account = account
401 break
402 return connected_account
404 @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
405 def send_file(self, file_path, jid, account):
407 Send file, located at 'file_path' to 'jid', using account (optional)
408 'account'
410 jid = self._get_real_jid(jid, account)
411 connected_account, contact = self._get_account_and_contact(account, jid)
413 if connected_account:
414 if file_path.startswith('file://'):
415 file_path=file_path[7:]
416 if os.path.isfile(file_path): # is it file?
417 gajim.interface.instances['file_transfers'].send_file(
418 connected_account, contact, file_path)
419 return DBUS_BOOLEAN(True)
420 return DBUS_BOOLEAN(False)
422 def _send_message(self, jid, message, keyID, account, type_ = 'chat',
423 subject = None):
425 Can be called from send_chat_message (default when send_message) or
426 send_single_message
428 if not jid or not message:
429 return DBUS_BOOLEAN(False)
430 if not keyID:
431 keyID = ''
433 connected_account, contact = self._get_account_and_contact(account, jid)
434 if connected_account:
435 connection = gajim.connections[connected_account]
436 connection.send_message(jid, message, keyID, type_, subject)
437 return DBUS_BOOLEAN(True)
438 return DBUS_BOOLEAN(False)
440 @dbus.service.method(INTERFACE, in_signature='ssss', out_signature='b')
441 def send_chat_message(self, jid, message, keyID, account):
443 Send chat 'message' to 'jid', using account (optional) 'account'. If keyID
444 is specified, encrypt the message with the pgp key
446 jid = self._get_real_jid(jid, account)
447 return self._send_message(jid, message, keyID, account)
449 @dbus.service.method(INTERFACE, in_signature='sssss', out_signature='b')
450 def send_single_message(self, jid, subject, message, keyID, account):
452 Send single 'message' to 'jid', using account (optional) 'account'. If
453 keyID is specified, encrypt the message with the pgp key
455 jid = self._get_real_jid(jid, account)
456 return self._send_message(jid, message, keyID, account, type, subject)
458 @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
459 def send_groupchat_message(self, room_jid, message, account):
461 Send 'message' to groupchat 'room_jid', using account (optional) 'account'
463 if not room_jid or not message:
464 return DBUS_BOOLEAN(False)
465 connected_account = self._get_account_for_groupchat(account, room_jid)
466 if connected_account:
467 connection = gajim.connections[connected_account]
468 connection.send_gc_message(room_jid, message)
469 return DBUS_BOOLEAN(True)
470 return DBUS_BOOLEAN(False)
472 @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
473 def open_chat(self, jid, account, message):
475 Shows the tabbed window for new message to 'jid', using account (optional)
476 'account'
478 if not jid:
479 raise dbus_support.MissingArgument()
480 jid = self._get_real_jid(jid, account)
481 try:
482 jid = helpers.parse_jid(jid)
483 except Exception:
484 # Jid is not conform, ignore it
485 return DBUS_BOOLEAN(False)
487 minimized_control = None
488 if account:
489 accounts = [account]
490 else:
491 accounts = gajim.connections.keys()
492 if len(accounts) == 1:
493 account = accounts[0]
494 connected_account = None
495 first_connected_acct = None
496 for acct in accounts:
497 if gajim.connections[acct].connected > 1: # account is online
498 contact = gajim.contacts.get_first_contact_from_jid(acct, jid)
499 if gajim.interface.msg_win_mgr.has_window(jid, acct):
500 connected_account = acct
501 break
502 # jid is in roster
503 elif contact:
504 minimized_control = \
505 jid in gajim.interface.minimized_controls[acct]
506 connected_account = acct
507 break
508 # we send the message to jid not in roster, because account is
509 # specified, or there is only one account
510 elif account:
511 connected_account = acct
512 elif first_connected_acct is None:
513 first_connected_acct = acct
515 # if jid is not a conntact, open-chat with first connected account
516 if connected_account is None and first_connected_acct:
517 connected_account = first_connected_acct
519 if minimized_control:
520 gajim.interface.roster.on_groupchat_maximized(None, jid,
521 connected_account)
523 if connected_account:
524 gajim.interface.new_chat_from_jid(connected_account, jid, message)
525 # preserve the 'steal focus preservation'
526 win = gajim.interface.msg_win_mgr.get_window(jid,
527 connected_account).window
528 if win.get_property('visible'):
529 win.window.focus(gtk.get_current_event_time())
530 return DBUS_BOOLEAN(True)
531 return DBUS_BOOLEAN(False)
533 @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
534 def change_status(self, status, message, account):
536 change_status(status, message, account). Account is optional - if not
537 specified status is changed for all accounts
539 if status not in ('offline', 'online', 'chat',
540 'away', 'xa', 'dnd', 'invisible'):
541 status = ''
542 if account:
543 if not status:
544 if account not in gajim.connections:
545 return DBUS_BOOLEAN(False)
546 status = gajim.SHOW_LIST[gajim.connections[account].connected]
547 gobject.idle_add(gajim.interface.roster.send_status, account,
548 status, message)
549 else:
550 # account not specified, so change the status of all accounts
551 for acc in gajim.contacts.get_accounts():
552 if not gajim.config.get_per('accounts', acc,
553 'sync_with_global_status'):
554 continue
555 if status:
556 status_ = status
557 else:
558 if acc not in gajim.connections:
559 continue
560 status_ = gajim.SHOW_LIST[gajim.connections[acc].connected]
561 gobject.idle_add(gajim.interface.roster.send_status, acc,
562 status_, message)
563 return DBUS_BOOLEAN(False)
565 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='')
566 def set_priority(self, prio, account):
568 set_priority(prio, account). Account is optional - if not specified
569 priority is changed for all accounts. That are synced with global status
571 if account:
572 gajim.config.set_per('accounts', account, 'priority', prio)
573 show = gajim.SHOW_LIST[gajim.connections[account].connected]
574 status = gajim.connections[account].status
575 gobject.idle_add(gajim.connections[account].change_status, show,
576 status)
577 else:
578 # account not specified, so change prio of all accounts
579 for acc in gajim.contacts.get_accounts():
580 if not gajim.account_is_connected(acc):
581 continue
582 if not gajim.config.get_per('accounts', acc,
583 'sync_with_global_status'):
584 continue
585 gajim.config.set_per('accounts', acc, 'priority', prio)
586 show = gajim.SHOW_LIST[gajim.connections[acc].connected]
587 status = gajim.connections[acc].status
588 gobject.idle_add(gajim.connections[acc].change_status, show,
589 status)
591 @dbus.service.method(INTERFACE, in_signature='', out_signature='')
592 def show_next_pending_event(self):
594 Show the window(s) with next pending event in tabbed/group chats
596 if gajim.events.get_nb_events():
597 gajim.interface.systray.handle_first_event()
599 @dbus.service.method(INTERFACE, in_signature='s', out_signature='a{sv}')
600 def contact_info(self, jid):
602 Get vcard info for a contact. Return cached value of the vcard
604 if not isinstance(jid, unicode):
605 jid = unicode(jid)
606 if not jid:
607 raise dbus_support.MissingArgument()
608 jid = self._get_real_jid(jid)
610 cached_vcard = gajim.connections.values()[0].get_cached_vcard(jid)
611 if cached_vcard:
612 return get_dbus_struct(cached_vcard)
614 # return empty dict
615 return DBUS_DICT_SV()
617 @dbus.service.method(INTERFACE, in_signature='', out_signature='as')
618 def list_accounts(self):
620 List register accounts
622 result = gajim.contacts.get_accounts()
623 result_array = dbus.Array([], signature='s')
624 if result and len(result) > 0:
625 for account in result:
626 result_array.append(DBUS_STRING(account))
627 return result_array
629 @dbus.service.method(INTERFACE, in_signature='s', out_signature='a{ss}')
630 def account_info(self, account):
632 Show info on account: resource, jid, nick, prio, message
634 result = DBUS_DICT_SS()
635 if account in gajim.connections:
636 # account is valid
637 con = gajim.connections[account]
638 index = con.connected
639 result['status'] = DBUS_STRING(gajim.SHOW_LIST[index])
640 result['name'] = DBUS_STRING(con.name)
641 result['jid'] = DBUS_STRING(gajim.get_jid_from_account(con.name))
642 result['message'] = DBUS_STRING(con.status)
643 result['priority'] = DBUS_STRING(unicode(con.priority))
644 result['resource'] = DBUS_STRING(unicode(gajim.config.get_per(
645 'accounts', con.name, 'resource')))
646 return result
648 @dbus.service.method(INTERFACE, in_signature='s', out_signature='aa{sv}')
649 def list_contacts(self, account):
651 List all contacts in the roster. If the first argument is specified, then
652 return the contacts for the specified account
654 result = dbus.Array([], signature='aa{sv}')
655 accounts = gajim.contacts.get_accounts()
656 if len(accounts) == 0:
657 return result
658 if account:
659 accounts_to_search = [account]
660 else:
661 accounts_to_search = accounts
662 for acct in accounts_to_search:
663 if acct in accounts:
664 for jid in gajim.contacts.get_jid_list(acct):
665 item = self._contacts_as_dbus_structure(
666 gajim.contacts.get_contacts(acct, jid))
667 if item:
668 result.append(item)
669 return result
671 @dbus.service.method(INTERFACE, in_signature='', out_signature='')
672 def toggle_roster_appearance(self):
674 Show/hide the roster window
676 win = gajim.interface.roster.window
677 if win.get_property('visible'):
678 gobject.idle_add(win.hide)
679 else:
680 win.present()
681 # preserve the 'steal focus preservation'
682 if self._is_first():
683 win.window.focus(gtk.get_current_event_time())
684 else:
685 win.window.focus(long(time()))
687 @dbus.service.method(INTERFACE, in_signature='', out_signature='')
688 def toggle_ipython(self):
690 Show/hide the ipython window
692 win = gajim.ipython_window
693 if win:
694 if win.window.is_visible():
695 gobject.idle_add(win.hide)
696 else:
697 win.show_all()
698 win.present()
699 else:
700 gajim.interface.create_ipython_window()
702 @dbus.service.method(INTERFACE, in_signature='', out_signature='a{ss}')
703 def prefs_list(self):
704 prefs_dict = DBUS_DICT_SS()
705 def get_prefs(data, name, path, value):
706 if value is None:
707 return
708 key = ''
709 if path is not None:
710 for node in path:
711 key += node + '#'
712 key += name
713 prefs_dict[DBUS_STRING(key)] = DBUS_STRING(value[1])
714 gajim.config.foreach(get_prefs)
715 return prefs_dict
717 @dbus.service.method(INTERFACE, in_signature='', out_signature='b')
718 def prefs_store(self):
719 try:
720 gajim.interface.save_config()
721 except Exception, e:
722 return DBUS_BOOLEAN(False)
723 return DBUS_BOOLEAN(True)
725 @dbus.service.method(INTERFACE, in_signature='s', out_signature='b')
726 def prefs_del(self, key):
727 if not key:
728 return DBUS_BOOLEAN(False)
729 key_path = key.split('#', 2)
730 if len(key_path) != 3:
731 return DBUS_BOOLEAN(False)
732 if key_path[2] == '*':
733 gajim.config.del_per(key_path[0], key_path[1])
734 else:
735 gajim.config.del_per(key_path[0], key_path[1], key_path[2])
736 return DBUS_BOOLEAN(True)
738 @dbus.service.method(INTERFACE, in_signature='s', out_signature='b')
739 def prefs_put(self, key):
740 if not key:
741 return DBUS_BOOLEAN(False)
742 key_path = key.split('#', 2)
743 if len(key_path) < 3:
744 subname, value = key.split('=', 1)
745 gajim.config.set(subname, value)
746 return DBUS_BOOLEAN(True)
747 subname, value = key_path[2].split('=', 1)
748 gajim.config.set_per(key_path[0], key_path[1], subname, value)
749 return DBUS_BOOLEAN(True)
751 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='b')
752 def add_contact(self, jid, account):
753 if account:
754 if account in gajim.connections and \
755 gajim.connections[account].connected > 1:
756 # if given account is active, use it
757 AddNewContactWindow(account = account, jid = jid)
758 else:
759 # wrong account
760 return DBUS_BOOLEAN(False)
761 else:
762 # if account is not given, show account combobox
763 AddNewContactWindow(account = None, jid = jid)
764 return DBUS_BOOLEAN(True)
766 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='b')
767 def remove_contact(self, jid, account):
768 jid = self._get_real_jid(jid, account)
769 accounts = gajim.contacts.get_accounts()
771 # if there is only one account in roster, take it as default
772 if account:
773 accounts = [account]
774 contact_exists = False
775 for account in accounts:
776 contacts = gajim.contacts.get_contacts(account, jid)
777 if contacts:
778 gajim.connections[account].unsubscribe(jid)
779 for contact in contacts:
780 gajim.interface.roster.remove_contact(contact, account)
781 gajim.contacts.remove_jid(account, jid)
782 contact_exists = True
783 return DBUS_BOOLEAN(contact_exists)
785 def _is_first(self):
786 if self.first_show:
787 self.first_show = False
788 return True
789 return False
791 def _get_real_jid(self, jid, account = None):
793 Get the real jid from the given one: removes xmpp: or get jid from nick if
794 account is specified, search only in this account
796 if account:
797 accounts = [account]
798 else:
799 accounts = gajim.connections.keys()
800 if jid.startswith('xmpp:'):
801 return jid[5:] # len('xmpp:') = 5
802 nick_in_roster = None # Is jid a nick ?
803 for account in accounts:
804 # Does jid exists in roster of one account ?
805 if gajim.contacts.get_contacts(account, jid):
806 return jid
807 if not nick_in_roster:
808 # look in all contact if one has jid as nick
809 for jid_ in gajim.contacts.get_jid_list(account):
810 c = gajim.contacts.get_contacts(account, jid_)
811 if c[0].name == jid:
812 nick_in_roster = jid_
813 break
814 if nick_in_roster:
815 # We have not found jid in roster, but we found is as a nick
816 return nick_in_roster
817 # We have not found it as jid nor as nick, probably a not in roster jid
818 return jid
820 def _contacts_as_dbus_structure(self, contacts):
822 Get info from list of Contact objects and create dbus dict
824 if not contacts:
825 return None
826 prim_contact = None # primary contact
827 for contact in contacts:
828 if prim_contact is None or contact.priority > prim_contact.priority:
829 prim_contact = contact
830 contact_dict = DBUS_DICT_SV()
831 contact_dict['name'] = DBUS_STRING(prim_contact.name)
832 contact_dict['show'] = DBUS_STRING(prim_contact.show)
833 contact_dict['jid'] = DBUS_STRING(prim_contact.jid)
834 if prim_contact.keyID:
835 keyID = None
836 if len(prim_contact.keyID) == 8:
837 keyID = prim_contact.keyID
838 elif len(prim_contact.keyID) == 16:
839 keyID = prim_contact.keyID[8:]
840 if keyID:
841 contact_dict['openpgp'] = keyID
842 contact_dict['resources'] = dbus.Array([], signature='(sis)')
843 for contact in contacts:
844 resource_props = dbus.Struct((DBUS_STRING(contact.resource),
845 dbus.Int32(contact.priority), DBUS_STRING(contact.status)))
846 contact_dict['resources'].append(resource_props)
847 contact_dict['groups'] = dbus.Array([], signature='(s)')
848 for group in prim_contact.groups:
849 contact_dict['groups'].append((DBUS_STRING(group),))
850 return contact_dict
852 @dbus.service.method(INTERFACE, in_signature='', out_signature='s')
853 def get_unread_msgs_number(self):
854 return DBUS_STRING(str(gajim.events.get_nb_events()))
856 @dbus.service.method(INTERFACE, in_signature='s', out_signature='b')
857 def start_chat(self, account):
858 if not account:
859 # error is shown in gajim-remote check_arguments(..)
860 return DBUS_BOOLEAN(False)
861 NewChatDialog(account)
862 return DBUS_BOOLEAN(True)
864 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='')
865 def send_xml(self, xml, account):
866 if account:
867 gajim.connections[account].send_stanza(str(xml))
868 else:
869 for acc in gajim.contacts.get_accounts():
870 gajim.connections[acc].send_stanza(str(xml))
872 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='')
873 def change_avatar(self, picture, account):
874 filesize = os.path.getsize(picture)
875 invalid_file = False
876 if os.path.isfile(picture):
877 stat = os.stat(picture)
878 if stat[6] == 0:
879 invalid_file = True
880 else:
881 invalid_file = True
882 if not invalid_file and filesize < 16384:
883 fd = open(picture, 'rb')
884 data = fd.read()
885 avatar = base64.encodestring(data)
886 avatar_mime_type = mimetypes.guess_type(picture)[0]
887 vcard={}
888 vcard['PHOTO'] = {'BINVAL': avatar}
889 if avatar_mime_type:
890 vcard['PHOTO']['TYPE'] = avatar_mime_type
891 if account:
892 gajim.connections[account].send_vcard(vcard)
893 else:
894 for acc in gajim.connections:
895 gajim.connections[acc].send_vcard(vcard)
897 @dbus.service.method(INTERFACE, in_signature='ssss', out_signature='')
898 def join_room(self, room_jid, nick, password, account):
899 if not account:
900 # get the first connected account
901 accounts = gajim.connections.keys()
902 for acct in accounts:
903 if gajim.account_is_connected(acct):
904 account = acct
905 break
906 if not account:
907 return
908 if not nick:
909 nick = ''
910 gajim.interface.instances[account]['join_gc'] = \
911 JoinGroupchatWindow(account, room_jid, nick)
912 else:
913 gajim.interface.join_gc_room(account, room_jid, nick, password)