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