more usage of NEC to handle messages
[gajim.git] / src / remote_control.py
blob1ada41a14c28e95725b1a20e1c14c86e0336a6f5
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)
135 def on_last_status_time(self, obj):
136 self.raise_signal('LastStatusTime', (obj.conn.name, [
137 obj.jid, obj.resource, obj.seconds, obj.status]))
139 def on_os_info(self, obj):
140 self.raise_signal('OsInfo', (obj.conn.name, [obj.jid, obj.resource,
141 obj.client_info, obj.os_info]))
143 def on_time(self, obj):
144 self.raise_signal('EntityTime', (obj.conn.name, [obj.jid, obj.resource,
145 obj.time_info]))
147 def on_gmail_notify(self, obj):
148 self.raise_signal('NewGmail', (obj.conn.name, [obj.jid, obj.newmsgs,
149 obj.gmail_messages_list]))
151 def on_roster_info(self, obj):
152 self.raise_signal('RosterInfo', (obj.conn.name, [obj.jid, obj.nickname,
153 obj.sub, obj.ask, obj.groups]))
155 def on_presence_received(self, obj):
156 event = None
157 if obj.old_show < 2 and obj.new_show > 1:
158 event = 'ContactPresence'
159 elif obj.old_show > 1 and obj.new_show < 2:
160 event = 'ContactAbsence'
161 elif obj.new_show > 1:
162 event = 'ContactStatus'
163 if event:
164 self.raise_signal(event, (obj.conn.name, [obj.jid, obj.show,
165 obj.status, obj.resource, obj.prio, obj.keyID, obj.timestamp,
166 obj.contact_nickname]))
168 def on_subscribe_presence_received(self, obj):
169 self.raise_signal('Subscribe', (obj.conn.name, [obj.jid, obj.status,
170 obj.user_nick]))
172 def on_subscribed_presence_received(self, obj):
173 self.raise_signal('Subscribed', (obj.conn.name, [obj.jid,
174 obj.resource]))
176 def on_unsubscribed_presence_received(self, obj):
177 self.raise_signal('Unsubscribed', (obj.conn.name, obj.jid))
179 def on_gc_message_received(self, obj):
180 if not hasattr(obj, 'needs_highlight'):
181 # event has not been handled at GUI level
182 return
183 self.raise_signal('GCMessage', (obj.conn.name, [obj.fjid, obj.msgtxt,
184 obj.timestamp, obj.has_timestamp, obj.xhtml_msgtxt, obj.status_code,
185 obj.displaymarking, obj.captcha_form, obj.needs_highlight]))
187 def on_our_status(self, obj):
188 self.raise_signal('AccountPresence', (obj.show, obj.conn.name))
190 def on_account_created(self, obj):
191 self.raise_signal('NewAccount', (obj.conn.name, obj.account_info))
193 def on_vcard_received(self, obj):
194 self.raise_signal('VcardInfo', (obj.conn.name, obj.vcard_dict))
196 def raise_signal(self, signal, arg):
197 if self.signal_object:
198 try:
199 getattr(self.signal_object, signal)(get_dbus_struct(arg))
200 except UnicodeDecodeError:
201 pass # ignore error when we fail to announce on dbus
204 class SignalObject(dbus.service.Object):
206 Local object definition for /org/gajim/dbus/RemoteObject
208 This docstring is not be visible, because the clients can access only the
209 remote object.
212 def __init__(self, bus_name):
213 self.first_show = True
214 self.vcard_account = None
216 # register our dbus API
217 dbus.service.Object.__init__(self, bus_name, OBJ_PATH)
219 @dbus.service.signal(INTERFACE, signature='av')
220 def Roster(self, account_and_data):
221 pass
223 @dbus.service.signal(INTERFACE, signature='av')
224 def AccountPresence(self, status_and_account):
225 pass
227 @dbus.service.signal(INTERFACE, signature='av')
228 def ContactPresence(self, account_and_array):
229 pass
231 @dbus.service.signal(INTERFACE, signature='av')
232 def ContactAbsence(self, account_and_array):
233 pass
235 @dbus.service.signal(INTERFACE, signature='av')
236 def ContactStatus(self, account_and_array):
237 pass
239 @dbus.service.signal(INTERFACE, signature='av')
240 def NewMessage(self, account_and_array):
241 pass
243 @dbus.service.signal(INTERFACE, signature='av')
244 def Subscribe(self, account_and_array):
245 pass
247 @dbus.service.signal(INTERFACE, signature='av')
248 def Subscribed(self, account_and_array):
249 pass
251 @dbus.service.signal(INTERFACE, signature='av')
252 def Unsubscribed(self, account_and_jid):
253 pass
255 @dbus.service.signal(INTERFACE, signature='av')
256 def NewAccount(self, account_and_array):
257 pass
259 @dbus.service.signal(INTERFACE, signature='av')
260 def VcardInfo(self, account_and_vcard):
261 pass
263 @dbus.service.signal(INTERFACE, signature='av')
264 def LastStatusTime(self, account_and_array):
265 pass
267 @dbus.service.signal(INTERFACE, signature='av')
268 def OsInfo(self, account_and_array):
269 pass
271 @dbus.service.signal(INTERFACE, signature='av')
272 def EntityTime(self, account_and_array):
273 pass
275 @dbus.service.signal(INTERFACE, signature='av')
276 def GCPresence(self, account_and_array):
277 pass
279 @dbus.service.signal(INTERFACE, signature='av')
280 def GCMessage(self, account_and_array):
281 pass
283 @dbus.service.signal(INTERFACE, signature='av')
284 def RosterInfo(self, account_and_array):
285 pass
287 @dbus.service.signal(INTERFACE, signature='av')
288 def NewGmail(self, account_and_array):
289 pass
291 def raise_signal(self, signal, arg):
293 Raise a signal, with a single argument of unspecified type Instead of
294 obj.raise_signal("Foo", bar), use obj.Foo(bar)
296 getattr(self, signal)(arg)
298 @dbus.service.method(INTERFACE, in_signature='s', out_signature='s')
299 def get_status(self, account):
301 Return status (show to be exact) which is the global one unless account is
302 given
304 if not account:
305 # If user did not ask for account, returns the global status
306 return DBUS_STRING(helpers.get_global_show())
307 # return show for the given account
308 index = gajim.connections[account].connected
309 return DBUS_STRING(gajim.SHOW_LIST[index])
311 @dbus.service.method(INTERFACE, in_signature='s', out_signature='s')
312 def get_status_message(self, account):
314 Return status which is the global one unless account is given
316 if not account:
317 # If user did not ask for account, returns the global status
318 return DBUS_STRING(str(helpers.get_global_status()))
319 # return show for the given account
320 status = gajim.connections[account].status
321 return DBUS_STRING(status)
323 def _get_account_and_contact(self, account, jid):
325 Get the account (if not given) and contact instance from jid
327 connected_account = None
328 contact = None
329 accounts = gajim.contacts.get_accounts()
330 # if there is only one account in roster, take it as default
331 # if user did not ask for account
332 if not account and len(accounts) == 1:
333 account = accounts[0]
334 if account:
335 if gajim.connections[account].connected > 1: # account is connected
336 connected_account = account
337 contact = gajim.contacts.get_contact_with_highest_priority(account,
338 jid)
339 else:
340 for account in accounts:
341 contact = gajim.contacts.get_contact_with_highest_priority(account,
342 jid)
343 if contact and gajim.connections[account].connected > 1:
344 # account is connected
345 connected_account = account
346 break
347 if not contact:
348 contact = jid
350 return connected_account, contact
352 def _get_account_for_groupchat(self, account, room_jid):
354 Get the account which is connected to groupchat (if not given)
355 or check if the given account is connected to the groupchat
357 connected_account = None
358 accounts = gajim.contacts.get_accounts()
359 # if there is only one account in roster, take it as default
360 # if user did not ask for account
361 if not account and len(accounts) == 1:
362 account = accounts[0]
363 if account:
364 if gajim.connections[account].connected > 1 and \
365 room_jid in gajim.gc_connected[account] and \
366 gajim.gc_connected[account][room_jid]:
367 # account and groupchat are connected
368 connected_account = account
369 else:
370 for account in accounts:
371 if gajim.connections[account].connected > 1 and \
372 room_jid in gajim.gc_connected[account] and \
373 gajim.gc_connected[account][room_jid]:
374 # account and groupchat are connected
375 connected_account = account
376 break
377 return connected_account
379 @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
380 def send_file(self, file_path, jid, account):
382 Send file, located at 'file_path' to 'jid', using account (optional)
383 'account'
385 jid = self._get_real_jid(jid, account)
386 connected_account, contact = self._get_account_and_contact(account, jid)
388 if connected_account:
389 if file_path.startswith('file://'):
390 file_path=file_path[7:]
391 if os.path.isfile(file_path): # is it file?
392 gajim.interface.instances['file_transfers'].send_file(
393 connected_account, contact, file_path)
394 return DBUS_BOOLEAN(True)
395 return DBUS_BOOLEAN(False)
397 def _send_message(self, jid, message, keyID, account, type_ = 'chat',
398 subject = None):
400 Can be called from send_chat_message (default when send_message) or
401 send_single_message
403 if not jid or not message:
404 return DBUS_BOOLEAN(False)
405 if not keyID:
406 keyID = ''
408 connected_account, contact = self._get_account_and_contact(account, jid)
409 if connected_account:
410 connection = gajim.connections[connected_account]
411 connection.send_message(jid, message, keyID, type_, subject)
412 return DBUS_BOOLEAN(True)
413 return DBUS_BOOLEAN(False)
415 @dbus.service.method(INTERFACE, in_signature='ssss', out_signature='b')
416 def send_chat_message(self, jid, message, keyID, account):
418 Send chat 'message' to 'jid', using account (optional) 'account'. If keyID
419 is specified, encrypt the message with the pgp key
421 jid = self._get_real_jid(jid, account)
422 return self._send_message(jid, message, keyID, account)
424 @dbus.service.method(INTERFACE, in_signature='sssss', out_signature='b')
425 def send_single_message(self, jid, subject, message, keyID, account):
427 Send single 'message' to 'jid', using account (optional) 'account'. If
428 keyID is specified, encrypt the message with the pgp key
430 jid = self._get_real_jid(jid, account)
431 return self._send_message(jid, message, keyID, account, type, subject)
433 @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
434 def send_groupchat_message(self, room_jid, message, account):
436 Send 'message' to groupchat 'room_jid', using account (optional) 'account'
438 if not room_jid or not message:
439 return DBUS_BOOLEAN(False)
440 connected_account = self._get_account_for_groupchat(account, room_jid)
441 if connected_account:
442 connection = gajim.connections[connected_account]
443 connection.send_gc_message(room_jid, message)
444 return DBUS_BOOLEAN(True)
445 return DBUS_BOOLEAN(False)
447 @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
448 def open_chat(self, jid, account, message):
450 Shows the tabbed window for new message to 'jid', using account (optional)
451 'account'
453 if not jid:
454 raise dbus_support.MissingArgument()
455 jid = self._get_real_jid(jid, account)
456 try:
457 jid = helpers.parse_jid(jid)
458 except Exception:
459 # Jid is not conform, ignore it
460 return DBUS_BOOLEAN(False)
462 if account:
463 accounts = [account]
464 else:
465 accounts = gajim.connections.keys()
466 if len(accounts) == 1:
467 account = accounts[0]
468 connected_account = None
469 first_connected_acct = None
470 for acct in accounts:
471 if gajim.connections[acct].connected > 1: # account is online
472 contact = gajim.contacts.get_first_contact_from_jid(acct, jid)
473 if gajim.interface.msg_win_mgr.has_window(jid, acct):
474 connected_account = acct
475 break
476 # jid is in roster
477 elif contact:
478 connected_account = acct
479 break
480 # we send the message to jid not in roster, because account is
481 # specified, or there is only one account
482 elif account:
483 connected_account = acct
484 elif first_connected_acct is None:
485 first_connected_acct = acct
487 # if jid is not a conntact, open-chat with first connected account
488 if connected_account is None and first_connected_acct:
489 connected_account = first_connected_acct
491 if connected_account:
492 gajim.interface.new_chat_from_jid(connected_account, jid, message)
493 # preserve the 'steal focus preservation'
494 win = gajim.interface.msg_win_mgr.get_window(jid,
495 connected_account).window
496 if win.get_property('visible'):
497 win.window.focus(gtk.get_current_event_time())
498 return DBUS_BOOLEAN(True)
499 return DBUS_BOOLEAN(False)
501 @dbus.service.method(INTERFACE, in_signature='sss', out_signature='b')
502 def change_status(self, status, message, account):
504 change_status(status, message, account). Account is optional - if not
505 specified status is changed for all accounts
507 if status not in ('offline', 'online', 'chat',
508 'away', 'xa', 'dnd', 'invisible'):
509 status = ''
510 if account:
511 if not status:
512 if account not in gajim.connections:
513 return DBUS_BOOLEAN(False)
514 status = gajim.SHOW_LIST[gajim.connections[account].connected]
515 gobject.idle_add(gajim.interface.roster.send_status, account,
516 status, message)
517 else:
518 # account not specified, so change the status of all accounts
519 for acc in gajim.contacts.get_accounts():
520 if not gajim.config.get_per('accounts', acc,
521 'sync_with_global_status'):
522 continue
523 if status:
524 status_ = status
525 else:
526 if acc not in gajim.connections:
527 continue
528 status_ = gajim.SHOW_LIST[gajim.connections[acc].connected]
529 gobject.idle_add(gajim.interface.roster.send_status, acc,
530 status_, message)
531 return DBUS_BOOLEAN(False)
533 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='')
534 def set_priority(self, prio, account):
536 set_priority(prio, account). Account is optional - if not specified
537 priority is changed for all accounts. That are synced with global status
539 if account:
540 gajim.config.set_per('accounts', account, 'priority', prio)
541 show = gajim.SHOW_LIST[gajim.connections[account].connected]
542 status = gajim.connections[account].status
543 gobject.idle_add(gajim.connections[account].change_status, show,
544 status)
545 else:
546 # account not specified, so change prio of all accounts
547 for acc in gajim.contacts.get_accounts():
548 if not gajim.account_is_connected(acc):
549 continue
550 if not gajim.config.get_per('accounts', acc,
551 'sync_with_global_status'):
552 continue
553 gajim.config.set_per('accounts', acc, 'priority', prio)
554 show = gajim.SHOW_LIST[gajim.connections[acc].connected]
555 status = gajim.connections[acc].status
556 gobject.idle_add(gajim.connections[acc].change_status, show,
557 status)
559 @dbus.service.method(INTERFACE, in_signature='', out_signature='')
560 def show_next_pending_event(self):
562 Show the window(s) with next pending event in tabbed/group chats
564 if gajim.events.get_nb_events():
565 gajim.interface.systray.handle_first_event()
567 @dbus.service.method(INTERFACE, in_signature='s', out_signature='a{sv}')
568 def contact_info(self, jid):
570 Get vcard info for a contact. Return cached value of the vcard
572 if not isinstance(jid, unicode):
573 jid = unicode(jid)
574 if not jid:
575 raise dbus_support.MissingArgument()
576 jid = self._get_real_jid(jid)
578 cached_vcard = gajim.connections.values()[0].get_cached_vcard(jid)
579 if cached_vcard:
580 return get_dbus_struct(cached_vcard)
582 # return empty dict
583 return DBUS_DICT_SV()
585 @dbus.service.method(INTERFACE, in_signature='', out_signature='as')
586 def list_accounts(self):
588 List register accounts
590 result = gajim.contacts.get_accounts()
591 result_array = dbus.Array([], signature='s')
592 if result and len(result) > 0:
593 for account in result:
594 result_array.append(DBUS_STRING(account))
595 return result_array
597 @dbus.service.method(INTERFACE, in_signature='s', out_signature='a{ss}')
598 def account_info(self, account):
600 Show info on account: resource, jid, nick, prio, message
602 result = DBUS_DICT_SS()
603 if account in gajim.connections:
604 # account is valid
605 con = gajim.connections[account]
606 index = con.connected
607 result['status'] = DBUS_STRING(gajim.SHOW_LIST[index])
608 result['name'] = DBUS_STRING(con.name)
609 result['jid'] = DBUS_STRING(gajim.get_jid_from_account(con.name))
610 result['message'] = DBUS_STRING(con.status)
611 result['priority'] = DBUS_STRING(unicode(con.priority))
612 result['resource'] = DBUS_STRING(unicode(gajim.config.get_per(
613 'accounts', con.name, 'resource')))
614 return result
616 @dbus.service.method(INTERFACE, in_signature='s', out_signature='aa{sv}')
617 def list_contacts(self, account):
619 List all contacts in the roster. If the first argument is specified, then
620 return the contacts for the specified account
622 result = dbus.Array([], signature='aa{sv}')
623 accounts = gajim.contacts.get_accounts()
624 if len(accounts) == 0:
625 return result
626 if account:
627 accounts_to_search = [account]
628 else:
629 accounts_to_search = accounts
630 for acct in accounts_to_search:
631 if acct in accounts:
632 for jid in gajim.contacts.get_jid_list(acct):
633 item = self._contacts_as_dbus_structure(
634 gajim.contacts.get_contacts(acct, jid))
635 if item:
636 result.append(item)
637 return result
639 @dbus.service.method(INTERFACE, in_signature='', out_signature='')
640 def toggle_roster_appearance(self):
642 Show/hide the roster window
644 win = gajim.interface.roster.window
645 if win.get_property('visible'):
646 gobject.idle_add(win.hide)
647 else:
648 win.present()
649 # preserve the 'steal focus preservation'
650 if self._is_first():
651 win.window.focus(gtk.get_current_event_time())
652 else:
653 win.window.focus(long(time()))
655 @dbus.service.method(INTERFACE, in_signature='', out_signature='')
656 def toggle_ipython(self):
658 Show/hide the ipython window
660 win = gajim.ipython_window
661 if win:
662 if win.window.is_visible():
663 gobject.idle_add(win.hide)
664 else:
665 win.show_all()
666 win.present()
667 else:
668 gajim.interface.create_ipython_window()
670 @dbus.service.method(INTERFACE, in_signature='', out_signature='a{ss}')
671 def prefs_list(self):
672 prefs_dict = DBUS_DICT_SS()
673 def get_prefs(data, name, path, value):
674 if value is None:
675 return
676 key = ''
677 if path is not None:
678 for node in path:
679 key += node + '#'
680 key += name
681 prefs_dict[DBUS_STRING(key)] = DBUS_STRING(value[1])
682 gajim.config.foreach(get_prefs)
683 return prefs_dict
685 @dbus.service.method(INTERFACE, in_signature='', out_signature='b')
686 def prefs_store(self):
687 try:
688 gajim.interface.save_config()
689 except Exception, e:
690 return DBUS_BOOLEAN(False)
691 return DBUS_BOOLEAN(True)
693 @dbus.service.method(INTERFACE, in_signature='s', out_signature='b')
694 def prefs_del(self, key):
695 if not key:
696 return DBUS_BOOLEAN(False)
697 key_path = key.split('#', 2)
698 if len(key_path) != 3:
699 return DBUS_BOOLEAN(False)
700 if key_path[2] == '*':
701 gajim.config.del_per(key_path[0], key_path[1])
702 else:
703 gajim.config.del_per(key_path[0], key_path[1], key_path[2])
704 return DBUS_BOOLEAN(True)
706 @dbus.service.method(INTERFACE, in_signature='s', out_signature='b')
707 def prefs_put(self, key):
708 if not key:
709 return DBUS_BOOLEAN(False)
710 key_path = key.split('#', 2)
711 if len(key_path) < 3:
712 subname, value = key.split('=', 1)
713 gajim.config.set(subname, value)
714 return DBUS_BOOLEAN(True)
715 subname, value = key_path[2].split('=', 1)
716 gajim.config.set_per(key_path[0], key_path[1], subname, value)
717 return DBUS_BOOLEAN(True)
719 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='b')
720 def add_contact(self, jid, account):
721 if account:
722 if account in gajim.connections and \
723 gajim.connections[account].connected > 1:
724 # if given account is active, use it
725 AddNewContactWindow(account = account, jid = jid)
726 else:
727 # wrong account
728 return DBUS_BOOLEAN(False)
729 else:
730 # if account is not given, show account combobox
731 AddNewContactWindow(account = None, jid = jid)
732 return DBUS_BOOLEAN(True)
734 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='b')
735 def remove_contact(self, jid, account):
736 jid = self._get_real_jid(jid, account)
737 accounts = gajim.contacts.get_accounts()
739 # if there is only one account in roster, take it as default
740 if account:
741 accounts = [account]
742 contact_exists = False
743 for account in accounts:
744 contacts = gajim.contacts.get_contacts(account, jid)
745 if contacts:
746 gajim.connections[account].unsubscribe(jid)
747 for contact in contacts:
748 gajim.interface.roster.remove_contact(contact, account)
749 gajim.contacts.remove_jid(account, jid)
750 contact_exists = True
751 return DBUS_BOOLEAN(contact_exists)
753 def _is_first(self):
754 if self.first_show:
755 self.first_show = False
756 return True
757 return False
759 def _get_real_jid(self, jid, account = None):
761 Get the real jid from the given one: removes xmpp: or get jid from nick if
762 account is specified, search only in this account
764 if account:
765 accounts = [account]
766 else:
767 accounts = gajim.connections.keys()
768 if jid.startswith('xmpp:'):
769 return jid[5:] # len('xmpp:') = 5
770 nick_in_roster = None # Is jid a nick ?
771 for account in accounts:
772 # Does jid exists in roster of one account ?
773 if gajim.contacts.get_contacts(account, jid):
774 return jid
775 if not nick_in_roster:
776 # look in all contact if one has jid as nick
777 for jid_ in gajim.contacts.get_jid_list(account):
778 c = gajim.contacts.get_contacts(account, jid_)
779 if c[0].name == jid:
780 nick_in_roster = jid_
781 break
782 if nick_in_roster:
783 # We have not found jid in roster, but we found is as a nick
784 return nick_in_roster
785 # We have not found it as jid nor as nick, probably a not in roster jid
786 return jid
788 def _contacts_as_dbus_structure(self, contacts):
790 Get info from list of Contact objects and create dbus dict
792 if not contacts:
793 return None
794 prim_contact = None # primary contact
795 for contact in contacts:
796 if prim_contact is None or contact.priority > prim_contact.priority:
797 prim_contact = contact
798 contact_dict = DBUS_DICT_SV()
799 contact_dict['name'] = DBUS_STRING(prim_contact.name)
800 contact_dict['show'] = DBUS_STRING(prim_contact.show)
801 contact_dict['jid'] = DBUS_STRING(prim_contact.jid)
802 if prim_contact.keyID:
803 keyID = None
804 if len(prim_contact.keyID) == 8:
805 keyID = prim_contact.keyID
806 elif len(prim_contact.keyID) == 16:
807 keyID = prim_contact.keyID[8:]
808 if keyID:
809 contact_dict['openpgp'] = keyID
810 contact_dict['resources'] = dbus.Array([], signature='(sis)')
811 for contact in contacts:
812 resource_props = dbus.Struct((DBUS_STRING(contact.resource),
813 dbus.Int32(contact.priority), DBUS_STRING(contact.status)))
814 contact_dict['resources'].append(resource_props)
815 contact_dict['groups'] = dbus.Array([], signature='(s)')
816 for group in prim_contact.groups:
817 contact_dict['groups'].append((DBUS_STRING(group),))
818 return contact_dict
820 @dbus.service.method(INTERFACE, in_signature='', out_signature='s')
821 def get_unread_msgs_number(self):
822 return DBUS_STRING(str(gajim.events.get_nb_events()))
824 @dbus.service.method(INTERFACE, in_signature='s', out_signature='b')
825 def start_chat(self, account):
826 if not account:
827 # error is shown in gajim-remote check_arguments(..)
828 return DBUS_BOOLEAN(False)
829 NewChatDialog(account)
830 return DBUS_BOOLEAN(True)
832 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='')
833 def send_xml(self, xml, account):
834 if account:
835 gajim.connections[account].send_stanza(str(xml))
836 else:
837 for acc in gajim.contacts.get_accounts():
838 gajim.connections[acc].send_stanza(str(xml))
840 @dbus.service.method(INTERFACE, in_signature='ss', out_signature='')
841 def change_avatar(self, picture, account):
842 filesize = os.path.getsize(picture)
843 invalid_file = False
844 if os.path.isfile(picture):
845 stat = os.stat(picture)
846 if stat[6] == 0:
847 invalid_file = True
848 else:
849 invalid_file = True
850 if not invalid_file and filesize < 16384:
851 fd = open(picture, 'rb')
852 data = fd.read()
853 avatar = base64.encodestring(data)
854 avatar_mime_type = mimetypes.guess_type(picture)[0]
855 vcard={}
856 vcard['PHOTO'] = {'BINVAL': avatar}
857 if avatar_mime_type:
858 vcard['PHOTO']['TYPE'] = avatar_mime_type
859 if account:
860 gajim.connections[account].send_vcard(vcard)
861 else:
862 for acc in gajim.connections:
863 gajim.connections[acc].send_vcard(vcard)
865 @dbus.service.method(INTERFACE, in_signature='ssss', out_signature='')
866 def join_room(self, room_jid, nick, password, account):
867 if not account:
868 # get the first connected account
869 accounts = gajim.connections.keys()
870 for acct in accounts:
871 if gajim.account_is_connected(acct):
872 account = acct
873 break
874 if not account:
875 return
876 if not nick:
877 nick = ''
878 gajim.interface.instances[account]['join_gc'] = \
879 JoinGroupchatWindow(account, room_jid, nick)
880 else:
881 gajim.interface.join_gc_room(account, room_jid, nick, password)