Newer
Older
exec python -OOt "$0" ${1+"$@"}
## Contributors for this file:

nicfit
committed
## - Travis Shirk <travis@pobox.com>
## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
## Vincent Hanquez <tab@snarc.org>
## Nikos Kouremenos <nkour@jabber.org>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
from common import exceptions
from common import i18n
i18n.init()
_ = i18n._
try:
import gtk
except RuntimeError, msg:
if str(msg) == 'could not open display':
print >> sys.stderr, _('Gajim needs Xserver to run. Quiting...')
pritext = ''
pritext = _('Gajim needs PyGTK 2.6 or above')
sectext = _('Gajim needs PyGTK 2.6 or above to run. Quiting...')
try:
import gtk.glade # check if user has libglade (in pygtk and in gtk)
except ImportError:
pritext = _('GTK+ runtime is missing libglade support')
if os.name == 'nt':
sectext = _('Please remove your current GTK+ runtime and install the latest stable version from %s') % 'http://gladewin32.sourceforge.net'
sectext = _('Please make sure that GTK+ and PyGTK have libglade support in your system.')
try:
from common import check_paths
except exceptions.PysqliteNotAvailable, e:
sectext = str(e)
if pritext:
dlg = gtk.MessageDialog(None,
gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message_format = pritext)
dlg.format_secondary_text(sectext)
dlg.run()
dlg.destroy()
sys.exit()
path = os.getcwd()
if '.svn' in os.listdir(path) or '_svn' in os.listdir(path):
# import gtkexcepthook only for those that run svn
# those than run with --verbose run from terminal so no need to care
# about those
import gtkexcepthook
del path
import signal
import getopt
import time
import threading
import gtkgui_helpers
import notify
from common.xmpp import idlequeue
from common import nslookup
from common import proxy65_manager
from common import socks5
from common import helpers
from common import optparser
profile = ''
try:
opts, args = getopt.getopt(sys.argv[1:], 'hvp:', ['help', 'verbose',
'profile=', 'sm-config-prefix=', 'sm-client-id='])
except getopt.error, msg:
print msg
print 'for help use --help'
sys.exit(2)
for o, a in opts:
elif o in ('-v', '--verbose'):
gajim.verbose = True
elif o in ('-p', '--profile'): # gajim --profile name
profile = a

nkour
committed
config_filename = os.path.expanduser('~/.gajim/config')
if os.name == 'nt':
try:
# Documents and Settings\[User Name]\Application Data\Gajim\logs
config_filename = os.environ['appdata'] + '/Gajim/config'
except KeyError:

nkour
committed
if profile:

nkour
committed
parser = optparser.OptionsParser(config_filename)
import roster_window
import systray
import dialogs
GTKGUI_GLADE = 'gtkgui.glade'
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
class GlibIdleQueue(idlequeue.IdleQueue):
'''
Extends IdleQueue to use glib io_add_wath, instead of select/poll
In another, `non gui' implementation of Gajim IdleQueue can be used safetly.
'''
def init_idle(self):
''' this method is called at the end of class constructor.
Creates a dict, which maps file/pipe/sock descriptor to glib event id'''
self.events = {}
if gtk.pygtk_version >= (2, 8, 0):
# time() is already called in glib, we just get the last value
# overrides IdleQueue.current_time()
self.current_time = lambda: gobject.get_current_time()
def add_idle(self, fd, flags):
''' this method is called when we plug a new idle object.
Start listening for events from fd
'''
res = gobject.io_add_watch(fd, flags, self.process_events,
priority=gobject.PRIORITY_LOW)
# store the id of the watch, so that we can remove it on unplug
self.events[fd] = res
def remove_idle(self, fd):
''' this method is called when we unplug a new idle object.
Stop listening for events from fd
'''
gobject.source_remove(self.events[fd])
del(self.events[fd])
def process(self):
self.check_time_events()
class Interface:

Yann Leboulanger
committed
def handle_event_roster(self, account, data):
self.roster.fill_contacts_and_groups_dicts(data, account)
self.roster.add_account_contacts(account)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('Roster', (account, data))

Yann Leboulanger
committed
def handle_event_warning(self, unused, data):
#('WARNING', account, (title_text, section_text))

nkour
committed
dialogs.WarningDialog(data[0], data[1]).get_response()

Yann Leboulanger
committed
def handle_event_error(self, unused, data):
#('ERROR', account, (title_text, section_text))

nkour
committed
dialogs.ErrorDialog(data[0], data[1]).get_response()
def handle_event_information(self, unused, data):
#('INFORMATION', account, (title_text, section_text))
dialogs.InformationDialog(data[0], data[1])
def handle_event_ask_new_nick(self, account, data):
#('ASK_NEW_NICK', account, (room_jid, title_text, prompt_text, proposed_nick))

nkour
committed
room_jid = data[0]
title = data[1]
prompt = data[2]
proposed_nick = data[3]

Yann Leboulanger
committed
gc_control = self.msg_win_mgr.get_control(room_jid, account)
if gc_control: # user may close the window before we are here
gc_control.show_change_nick_input_dialog(title, prompt, proposed_nick)
def handle_event_http_auth(self, account, data):

nicfit
committed
#('HTTP_AUTH', account, (method, url, transaction_id, iq_obj))
dialog = dialogs.ConfirmationDialog(_('HTTP (%s) Authorization for %s (id: %s)') \
% (data[0], data[1], data[2]), _('Do you accept this request?'))
if dialog.get_response() == gtk.RESPONSE_OK:
answer = 'yes'
else:
answer = 'no'

nicfit
committed
gajim.connections[account].build_http_auth_answer(data[3], answer)
def handle_event_error_answer(self, account, array):
#('ERROR_ANSWER', account, (id, jid_from. errmsg, errcode))
id, jid_from, errmsg, errcode = array
if unicode(errcode) in ('403', '406') and id:
ft = self.instances['file_transfers']
sid = id
if len(id) > 3 and id[2] == '_':
sid = id[3:]
if ft.files_props['s'].has_key(sid):
file_props = ft.files_props['s'][sid]
file_props['error'] = -4
self.handle_event_file_request_error(account,
(jid_from, file_props))
conn = gajim.connections[account]
conn.disconnect_transfer(file_props)
return
elif unicode(errcode) == '404':
sid = id
if len(id) > 3 and id[2] == '_':
sid = id[3:]
if conn.files_props.has_key(sid):
file_props = conn.files_props[sid]
self.handle_event_file_send_error(account,
(jid_from, file_props))
conn.disconnect_transfer(file_props)
return

Yann Leboulanger
committed
ctrl = self.msg_win_mgr.get_control(jid_from, account)
if ctrl and ctrl.type_id == message_control.TYPE_GC:
ctrl.print_conversation('Error %s: %s' % (array[2], array[1]))
def handle_event_con_type(self, account, con_type):
# ('CON_TYPE', account, con_type) which can be 'ssl', 'tls', 'tcp'

Yann Leboulanger
committed
gajim.con_types[account] = con_type
self.roster.draw_account(account)
def unblock_signed_in_notifications(self, account):
gajim.block_signed_in_notifications[account] = False
def handle_event_status(self, account, status): # OUR status
model = self.roster.status_combobox.get_model()
if status == 'offline':
# sensitivity for this menuitem
model[self.roster.status_message_menuitem_iter][3] = False
gajim.block_signed_in_notifications[account] = True
# 30 seconds after we change our status to sth else than offline
# we stop blocking notifications of any kind
# this prevents from getting the roster items as 'just signed in'
# contacts. 30 seconds should be enough time
gobject.timeout_add(30000, self.unblock_signed_in_notifications, account)
# sensitivity for this menuitem
model[self.roster.status_message_menuitem_iter][3] = True
# Inform all controls for this account of the connection state change

Yann Leboulanger
committed
for ctrl in self.msg_win_mgr.get_controls(
type = message_control.TYPE_GC):
if ctrl.account == account:
if status == 'offline':
ctrl.got_disconnected()
else:
# Other code rejoins all GCs, so we don't do it here
if not ctrl.type_id == message_control.TYPE_GC:
ctrl.got_connected()
ctrl.parent_win.redraw_tab(ctrl)
self.roster.on_status_changed(account, status)

Yann Leboulanger
committed
if account in self.show_vcard_when_connect:
jid = gajim.get_jid_from_account(account)

Yann Leboulanger
committed
if not self.instances[account]['infos'].has_key(jid):
self.instances[account]['infos'][jid] = \

Yann Leboulanger
committed
gajim.connections[account].request_vcard(jid)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('AccountPresence', (status, account))
def handle_event_notify(self, account, array):
#('NOTIFY', account, (jid, status, message, resource, priority, keyID))
# if we're here it means contact changed show
statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible']
jid = array[0].split('/')[0]
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if jid in attached_keys:
keyID = attached_keys[attached_keys.index(jid) + 1]
resource = array[3]
if not resource:
resource = ''
priority = array[4]
if gajim.jid_is_transport(jid):
# It must be an agent
ji = jid.replace('@', '')
jid_list = gajim.contacts.get_jid_list(account)
if ji in jid_list:
lcontact = gajim.contacts.get_contacts_from_jid(account, ji)
contact1 = None
for c in lcontact:
if c.resource == resource:
contact1 = c
if contact1:
if contact1.show in statuss:
old_show = statuss.index(contact1.show)
if old_show == new_show and contact1.status == array[2] and \
contact1.priority == priority: # no change

Yann Leboulanger
committed
return
contact1 = gajim.contacts.get_first_contact_from_jid(account, ji)
if contact1.show in statuss:
old_show = statuss.index(contact1.show)
if (resources != [''] and (len(lcontact) != 1 or
lcontact[0].show != 'offline')) and jid.find('@') > 0:
contact1 = gajim.contacts.copy_contact(contact1)
lcontact.append(contact1)
contact1.resource = resource
if contact1.jid.find('@') > 0 and len(lcontact) == 1: # It's not an agent
if not contact1.jid in gajim.newly_added[account]:
gajim.newly_added[account].append(contact1.jid)
if contact1.jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].remove(contact1.jid)
gobject.timeout_add(5000, self.roster.remove_newly_added,
contact1.jid, account)
elif old_show > 1 and new_show == 0 and gajim.connections[account].\
connected > 1:
if not contact1.jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].append(contact1.jid)
if contact1.jid in gajim.newly_added[account]:
gajim.newly_added[account].remove(contact1.jid)
self.roster.draw_contact(contact1.jid, account)
if not gajim.awaiting_events[account].has_key(jid):
gobject.timeout_add(5000, self.roster.really_remove_contact,
contact1, account)
contact1.show = array[1]
contact1.status = array[2]
contact1.priority = priority
contact1.keyID = keyID

Yann Leboulanger
committed
if contact1.jid not in gajim.newly_added[account]:
contact1.last_status_time = time.localtime()
if gajim.jid_is_transport(jid):
# It must be an agent
# Update existing iter
self.roster.draw_contact(ji, account)
elif jid == gajim.get_jid_from_account(account):
# It's another of our resources. We don't need to see that!
# reset chatstate if needed:
# (when contact signs out or has errors)
if array[1] in ('offline', 'error'):

Yann Leboulanger
committed
contact1.our_chatstate = contact1.chatstate = contact1.composing_jep = None
gajim.connections[account].remove_transfers_for_contact(contact1)
self.roster.chg_contact_status(contact1, array[1], array[2], account)
if old_show < 2 and new_show > 1:
if gajim.config.get_per('soundevents', 'contact_connected',
'enabled') and not gajim.block_signed_in_notifications[account]:
helpers.play_sound('contact_connected')

Yann Leboulanger
committed
if not gajim.awaiting_events[account].has_key(jid) and \
gajim.config.get('notify_on_signin') and \
not gajim.block_signed_in_notifications[account]:

Yann Leboulanger
committed
transport_name = gajim.get_transport_name_from_jid(jid)
img = None
if transport_name:
img = os.path.join(gajim.DATA_DIR, 'iconsets',
'transports', transport_name, '48x48',
'online.png')
if not img or not os.path.isfile(img):

Yann Leboulanger
committed
iconset = gajim.config.get('iconset')
img = os.path.join(gajim.DATA_DIR, 'iconsets',
iconset, '48x48', 'online.png')

Yann Leboulanger
committed
path = gtkgui_helpers.get_path_to_generic_or_avatar(img,
jid = jid, suffix = '_notif_size_colored.png')
notify.notify(_('Contact Signed In'), jid, account,
path_to_image = path)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('ContactPresence',

Yann Leboulanger
committed
elif old_show > 1 and new_show < 2:
if gajim.config.get_per('soundevents', 'contact_disconnected',
helpers.play_sound('contact_disconnected')

Yann Leboulanger
committed
if not gajim.awaiting_events[account].has_key(jid) and \
gajim.config.get('notify_on_signout'):
if helpers.allow_showing_notification(account):

Yann Leboulanger
committed
transport_name = gajim.get_transport_name_from_jid(jid)
img = None
if transport_name:
img = os.path.join(gajim.DATA_DIR, 'iconsets',
'transports', transport_name, '48x48',
'offline.png')
if not img or not os.path.isfile(img):

Yann Leboulanger
committed
iconset = gajim.config.get('iconset')
img = os.path.join(gajim.DATA_DIR, 'iconsets',
iconset, '48x48', 'offline.png')

Yann Leboulanger
committed
path = gtkgui_helpers.get_path_to_generic_or_avatar(img,
jid = jid, suffix = '_notif_size_bw.png')
notify.notify(_('Contact Signed Out'), jid, account,
path_to_image = path)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('ContactAbsence', (account, array))
# FIXME: stop non active file transfers
# FIXME: Msn transport (CMSN1.2.1 and PyMSN0.10) doesn't follow the JEP
# remove in 2007
# It's maybe a GC_NOTIFY (specialy for MSN gc)
self.handle_event_gc_notify(account, (jid, array[1], array[2],
array[3], None, None, None, None, None, None, None))
def handle_event_msg(self, account, array):
# ('MSG', account, (jid, msg, time, encrypted, msg_type, subject,
# chatstate))
resource = gajim.get_resource_from_jid(array[0])

nkour
committed
fjid = array[0]
msg_type = array[4]

Yann Leboulanger
committed
msg_id = array[7]

Yann Leboulanger
committed
composing_jep = array[8]
if gajim.jid_is_transport(jid):
jid = jid.replace('@', '')
chat_control = self.msg_win_mgr.get_control(jid, account)
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# Handle chat states
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
if contact:
contact.composing_jep = composing_jep
if chat_control and chat_control.type_id == message_control.TYPE_CHAT:
if chatstate is not None:
# other peer sent us reply, so he supports jep85 or jep22
contact.chatstate = chatstate
if contact.our_chatstate == 'ask': # we were jep85 disco?
contact.our_chatstate = 'active' # no more
chat_control.handle_incoming_chatstate()
elif contact.chatstate != 'active':
# got no valid jep85 answer, peer does not support it
contact.chatstate = False
elif contact and chatstate == 'active':
# Brand new message, incoming.
contact.our_chatstate = chatstate
contact.chatstate = chatstate
if msg_id: # Do not overwrite an existing msg_id with None
contact.msg_id = msg_id
# THIS MUST BE AFTER chatstates handling
# AND BEFORE playsound (else we here sounding on chatstates!)
if not array[1]: # empty message text
return
first = False

nkour
committed
pm = False
if not chat_control and not gajim.awaiting_events[account].has_key(jid):

nkour
committed
# It's a first message and not a Private Message
first = True

nkour
committed
elif chat_control and chat_control.type_id == message_control.TYPE_GC:
# It's a Private message
pm = True
if not self.msg_win_mgr.has_window(fjid, account) and \
not gajim.awaiting_events[account].has_key(fjid):
first =True
if gajim.config.get_per('soundevents', 'first_message_received',
'enabled') and first:
helpers.play_sound('first_message_received')
elif gajim.config.get_per('soundevents', 'next_message_received',
'enabled'):
helpers.play_sound('next_message_received')

Yann Leboulanger
committed
jid_of_control = jid

nkour
committed
if pm:

nkour
committed
if first:
if helpers.allow_showing_notification(account):
room_name,t = gajim.get_room_name_and_server_from_room_jid(
room_jid)
txt = _('%(nickname)s in room %(room_name)s has sent you a new '
'message.') % {'nickname': nick, 'room_name': room_name}

Yann Leboulanger
committed
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'priv_msg_recv.png')
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
notify.notify(_('New Private Message'), fjid, account, 'pm',
path_to_image = path, text = txt)

Yann Leboulanger
committed
chat_control.on_private_message(nick, array[1], array[2])

Yann Leboulanger
committed
return
# THIS HAS TO BE AFTER PM handling so we can get PMs
if gajim.config.get('ignore_unknown_contacts') and \
not gajim.contacts.get_contact(account, jid):
return
highest_contact = gajim.contacts.get_contact_with_highest_priority(
account, jid)

Yann Leboulanger
committed
# Look for a chat control that has the given resource, or default to one
# without resource
ctrl = self.msg_win_mgr.get_control(fjid, account)
if ctrl:
chat_control = ctrl
elif not highest_contact or not highest_contact.resource:
# unknow contact or offline message
chat_control = None
jid_of_control = jid
elif resource != highest_contact.resource:
chat_control = None
jid_of_control = fjid

nkour
committed
if first:

nkour
committed
if gajim.config.get('notify_on_new_message'):
if helpers.allow_showing_notification(account):
txt = _('%s has sent you a new message.') % gajim.get_name_from_jid(account, jid)
if msg_type == 'normal': # single message

Yann Leboulanger
committed
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'single_msg_recv.png')
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
notify.notify(_('New Single Message'), jid_of_control,
account, msg_type, path_to_image = path, text = txt)
else: # chat message

Yann Leboulanger
committed
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'chat_msg_recv.png')
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
notify.notify(_('New Message'), jid_of_control, account,
msg_type, path_to_image = path, text = txt)
# array: (contact, msg, time, encrypted, msg_type, subject)
self.roster.on_message(jid, array[1], array[2], account, array[3],
msg_type, array[5], resource)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('NewMessage', (account, array))
def handle_event_msgerror(self, account, array):

Yann Leboulanger
committed
#('MSGERROR', account, (jid, error_code, error_msg, msg, time))

Yann Leboulanger
committed
gcs = self.msg_win_mgr.get_controls(message_control.TYPE_GC)
for gc_control in gcs:
if jid == gc_control.contact.jid:
if len(jids) > 1: # it's a pm
nick = jids[1]

Yann Leboulanger
committed
if not self.msg_win_mgr.get_control(fjid, account):
tv = gc_control.list_treeview
model = tv.get_model()
i = gc_control.get_contact_iter(nick)
if i:
show = model[i][3]
else:
show = 'offline'
gc_c = gajim.contacts.create_gc_contact(room_jid = jid,
name = nick, show = show)
c = gajim.contacts.contact_from_gc_contact(gc_c)

Yann Leboulanger
committed
ctrl = self.msg_win_mgr.get_control(fjid, account)
'status')
return
gc_control.print_conversation('Error %s: %s' % (array[1], array[2]))
if gc_control.parent_win.get_active_jid() == jid:
gc_control.set_subject(gc_control.subject)
if gajim.jid_is_transport(jid):
jid = jid.replace('@', '')

Yann Leboulanger
committed
self.roster.on_message(jid, _('error while sending') + \
' \"%s\" ( %s )' % (array[3], array[2]), array[4], account, \
msg_type='error')
def handle_event_msgsent(self, account, array):
msg = array[1]
# do not play sound when standalone chatstate message (eg no msg)
if msg and gajim.config.get_per('soundevents', 'message_sent', 'enabled'):
helpers.play_sound('message_sent')
def handle_event_subscribe(self, account, array):
#('SUBSCRIBE', account, (jid, text))

Yann Leboulanger
committed
dialogs.SubscriptionRequestWindow(array[0], array[1], account)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('Subscribe', (account, array))
def handle_event_subscribed(self, account, array):
#('SUBSCRIBED', account, (jid, resource))
if jid in gajim.contacts.get_jid_list(account):
c = gajim.contacts.get_first_contact_from_jid(account, jid)
c.resource = array[1]
self.roster.remove_contact(c, account)
if _('Not in Roster') in c.groups:
c.groups.remove(_('Not in Roster'))
self.roster.add_contact_to_roster(c.jid, account)
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if jid in attached_keys:
keyID = attached_keys[attached_keys.index(jid) + 1]
name = jid.split('@', 1)[0]
name = name.split('%', 1)[0]
contact1 = gajim.contacts.create_contact(jid = jid, name = name,
groups = [], show = 'online', status = 'online',
ask = 'to', resource = array[1], keyID = keyID)
gajim.contacts.add_contact(account, contact1)
self.roster.add_contact_to_roster(jid, account)

nkour
committed
dialogs.InformationDialog(_('Authorization accepted'),
_('The contact "%s" has authorized you to see his or her status.')
if not gajim.config.get_per('accounts', account, 'dont_ack_subscription'):
gajim.connections[account].ack_subscribed(jid)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('Subscribed', (account, array))
def handle_event_unsubscribed(self, account, jid):
dialogs.InformationDialog(_('Contact "%s" removed subscription from you') % jid,
# FIXME: Per RFC 3921, we can "deny" ack as well, but the GUI does not show deny
gajim.connections[account].ack_unsubscribed(jid)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('Unsubscribed', (account, jid))
def handle_event_agent_info_error(self, account, agent):
#('AGENT_ERROR_INFO', account, (agent))
try:
gajim.connections[account].services_cache.agent_info_error(agent)
except AttributeError:
return
def handle_event_agent_items_error(self, account, agent):
#('AGENT_ERROR_INFO', account, (agent))
try:
gajim.connections[account].services_cache.agent_items_error(agent)
except AttributeError:
return
def handle_event_register_agent_info(self, account, array):
#('REGISTER_AGENT_INFO', account, (agent, infos, is_form))
if array[1].has_key('instructions'):

Yann Leboulanger
committed
config.ServiceRegistrationWindow(array[0], array[1], account,

nkour
committed
dialogs.ErrorDialog(_('Contact with "%s" cannot be established'\
% array[0]), _('Check your connection or try again later.')).get_response()

Yann Leboulanger
committed
def handle_event_agent_info_items(self, account, array):
#('AGENT_INFO_ITEMS', account, (agent, node, items))
try:
gajim.connections[account].services_cache.agent_items(array[0],
array[1], array[2])
except AttributeError:
return

Yann Leboulanger
committed
def handle_event_agent_info_info(self, account, array):
#('AGENT_INFO_INFO', account, (agent, node, identities, features, data))
try:
gajim.connections[account].services_cache.agent_info(array[0],
array[1], array[2], array[3], array[4])
except AttributeError:
return

Yann Leboulanger
committed
def handle_event_acc_ok(self, account, array):
#('ACC_OK', account, (config))
if self.instances.has_key('account_creation_wizard'):
self.instances['account_creation_wizard'].acc_is_ok(array)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('NewAccount', (account, array))
def handle_event_acc_not_ok(self, account, array):
#('ACC_NOT_OK', account, (reason))
if self.instances.has_key('account_creation_wizard'):
self.instances['account_creation_wizard'].acc_is_not_ok(array)
def handle_event_quit(self, p1, p2):

Yann Leboulanger
committed
self.roster.quit_gtkgui_interface()
def handle_event_myvcard(self, account, array):
if array.has_key('NICKNAME'):
nick = array['NICKNAME']

Yann Leboulanger
committed
gajim.nicks[account] = nick
if self.instances[account]['infos'].has_key(array['jid']):
win = self.instances[account]['infos'][array['jid']]

Yann Leboulanger
committed
win.set_values(array)
if account in self.show_vcard_when_connect:
win.xml.get_widget('information_notebook').set_current_page(-1)
win.xml.get_widget('set_avatar_button').clicked()
self.show_vcard_when_connect.remove(account)
def handle_event_vcard(self, account, vcard):
'''vcard holds the vcard data'''
jid = vcard['jid']

Yann Leboulanger
committed
resource = ''
if vcard.has_key('resource'):
resource = vcard['resource']
# vcard window
if self.instances[account]['infos'].has_key(jid):
win = self.instances[account]['infos'][jid]

Yann Leboulanger
committed
elif resource and self.instances[account]['infos'].has_key(
jid + '/' + resource):
win = self.instances[account]['infos'][jid + '/' + resource]

Yann Leboulanger
committed
if win:
# show avatar in chat

Yann Leboulanger
committed
if resource and self.msg_win_mgr.has_window(
jid + '/' + resource, account):

Yann Leboulanger
committed
win = self.msg_win_mgr.get_window(jid + '/' + resource,
account)
ctrl = win.get_control(jid + '/' + resource, account)

Yann Leboulanger
committed
elif self.msg_win_mgr.has_window(jid, account):
win = self.msg_win_mgr.get_window(jid, account)
ctrl = win.get_control(jid, account)
if win and ctrl.type_id != message_control.TYPE_GC:
ctrl.show_avatar()

Yann Leboulanger
committed
# Show avatar in roster or gc_roster

Yann Leboulanger
committed
gc_ctrl = self.msg_win_mgr.get_control(jid, account)
if gc_ctrl and gc_ctrl.type_id == message_control.TYPE_GC:

Yann Leboulanger
committed
gc_ctrl.draw_avatar(resource)
else:
self.roster.draw_avatar(jid, account)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('VcardInfo', (account, vcard))

Yann Leboulanger
committed
def handle_event_last_status_time(self, account, array):
# ('LAST_STATUS_TIME', account, (jid, resource, seconds, status))
win = None
if self.instances[account]['infos'].has_key(array[0]):
win = self.instances[account]['infos'][array[0]]
elif self.instances[account]['infos'].has_key(array[0] + '/' + array[1]):
win = self.instances[account]['infos'][array[0] + '/' + array[1]]
if win:
c = gajim.contacts.get_contact(account, array[0], array[1])
# c is a list when no resource is given. it probably means that contact
# is offline, so only on Contact instance
if isinstance(c, list):
c = c[0]

Yann Leboulanger
committed
if c: # c can be none if it's a gc contact
c.last_status_time = time.localtime(time.time() - array[2])
if array[3]:
c.status = array[3]
win.set_last_status_time()

Yann Leboulanger
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('LastStatusTime', (account, array))
def handle_event_os_info(self, account, array):
if self.instances[account]['infos'].has_key(array[0]):
win = self.instances[account]['infos'][array[0]]
elif self.instances[account]['infos'].has_key(array[0] + '/' + array[1]):
win = self.instances[account]['infos'][array[0] + '/' + array[1]]
if win:
win.set_os_info(array[1], array[2], array[3])

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('OsInfo', (account, array))
def handle_event_gc_notify(self, account, array):
#('GC_NOTIFY', account, (room_jid, show, status, nick,
# role, affiliation, jid, reason, actor, statusCode, newNick))
nick = array[3]
if not nick:
return
room_jid = array[0]
fjid = room_jid + '/' + nick
show = array[1]
status = array[2]
# print status in chat window and update status/GPG image

Yann Leboulanger
committed
if self.msg_win_mgr.has_window(fjid, account):
ctrl = self.msg_win_mgr.get_control(fjid, account)
contact.show = show
contact.status = status
uf_show = helpers.get_uf_show(show)
ctrl.print_conversation(_('%s is now %s (%s)') % (nick, uf_show, status),
# Get the window and control for the updated status, this may be a PrivateChatControl

Yann Leboulanger
committed
control = self.msg_win_mgr.get_control(room_jid, account)
if control:
control.chg_contact_status(nick, show, status, array[4], array[5], array[6],
array[7], array[8], array[9], array[10])
# Find any PM chat through this room, and tell it to update.

Yann Leboulanger
committed
pm_control = self.msg_win_mgr.get_control(fjid, account)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('GCPresence', (account, array))
def handle_event_gc_msg(self, account, array):

Yann Leboulanger
committed
# ('GC_MSG', account, (jid, msg, time))

Yann Leboulanger
committed
room_jid = jids[0]

Yann Leboulanger
committed
gc_control = self.msg_win_mgr.get_control(room_jid, account)

Yann Leboulanger
committed
# message from server
nick = ''

Yann Leboulanger
committed
# message from someone
nick = jids[1]

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('GCMessage', (account, array))
def handle_event_gc_subject(self, account, array):
#('GC_SUBJECT', account, (jid, subject, body))

Yann Leboulanger
committed
gc_control = self.msg_win_mgr.get_control(jid, account)
# We can receive a subject with a body that contains "X has set the subject to Y" ...
if array[2]:
gc_control.print_conversation(array[2])
# ... Or the message comes from the occupant who set the subject
elif len(jids) > 1:
gc_control.print_conversation('%s has set the subject to %s' % (jids[1], array[1]))
def handle_event_gc_config(self, account, array):
#('GC_CONFIG', account, (jid, config)) config is a dict
jid = array[0].split('/')[0]
if not self.instances[account]['gc_config'].has_key(jid):
self.instances[account]['gc_config'][jid] = \

Yann Leboulanger
committed
config.GroupchatConfigWindow(account, jid, array[1])

Yann Leboulanger
committed
def handle_event_gc_affiliation(self, account, array):
#('GC_AFFILIATION', account, (room_jid, affiliation, list)) list is list
room_jid = array[0]
if self.instances[account]['gc_config'].has_key(room_jid):

Yann Leboulanger
committed
self.instances[account]['gc_config'][room_jid].\
affiliation_list_received(array[1], array[2])

Yann Leboulanger
committed
def handle_event_gc_invitation(self, account, array):
#('GC_INVITATION', (room_jid, jid_from, reason, password))

Yann Leboulanger
committed
jid = gajim.get_jid_without_resource(array[1])
room_jid = array[0]
if helpers.allow_popup_window(account) or not self.systray_enabled:

Yann Leboulanger
committed
dialogs.InvitationReceivedDialog(account, room_jid, jid, array[3],
array[2])
return
self.add_event(account, jid, 'gc-invitation', (room_jid, array[2],
array[3]))
if helpers.allow_showing_notification(account):

Yann Leboulanger
committed
path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'gc_invitation.png')
path = gtkgui_helpers.get_path_to_generic_or_avatar(path)

Yann Leboulanger
committed
notify.notify(_('Groupchat Invitation'),
jid, account, 'gc-invitation', path, room_jid)
def handle_event_bad_passphrase(self, account, array):
use_gpg_agent = gajim.config.get('use_gpg_agent')
if use_gpg_agent:
return
keyID = gajim.config.get_per('accounts', account, 'keyid')
self.roster.forget_gpg_passphrase(keyID)
dialogs.WarningDialog(_('Your passphrase is incorrect'),
_('You are currently connected without your OpenPGP key.')).get_response()

Yann Leboulanger
committed
def handle_event_roster_info(self, account, array):
#('ROSTER_INFO', account, (jid, name, sub, ask, groups))
jid = array[0]

Yann Leboulanger
committed
name = array[1]
sub = array[2]
ask = array[3]
groups = array[4]
contacts = gajim.contacts.get_contacts_from_jid(account, jid)
# contact removes us.

Yann Leboulanger
committed
if (not sub or sub == 'none') and (not ask or ask == 'none') and \
not name and not groups:
if contacts:
self.roster.remove_contact(contacts[0], account)
gajim.contacts.remove_jid(account, jid)
#FIXME if it was the only one in its group, remove the group
return
elif not contacts:
# Add it to roster
contact = gajim.contacts.create_contact(jid = jid, name = name,
groups = groups, show = 'offline', sub = sub, ask = ask)
gajim.contacts.add_contact(account, contact)
self.roster.add_contact_to_roster(jid, account)
else:
for contact in contacts:
if not name:
name = ''
contact.name = name
contact.sub = sub
contact.ask = ask
if groups:
contact.groups = groups
self.roster.draw_contact(jid, account)

nkour
committed
if self.remote_ctrl:
self.remote_ctrl.raise_signal('RosterInfo', (account, array))

Yann Leboulanger
committed
def handle_event_bookmarks(self, account, bms):
# ('BOOKMARKS', account, [{name,jid,autojoin,password,nick}, {}])
# We received a bookmark item from the server (JEP48)
# Auto join GC windows if neccessary
invisible_show = gajim.SHOW_LIST.index('invisible')
# do not autojoin if we are invisible
if gajim.connections[account].connected == invisible_show:
return
# join autojoinable rooms

Yann Leboulanger
committed
for bm in bms:
if bm['autojoin'] in ('1', 'true'):
self.roster.join_gc_room(account, bm['jid'], bm['nick'],
bm['password'])
def handle_event_file_send_error(self, account, array):
jid = array[0]
file_props = array[1]
ft = self.instances['file_transfers']
ft.set_status(file_props['type'], file_props['sid'], 'stop')
if helpers.allow_popup_window(account):
ft.show_send_error(file_props)
return
self.add_event(account, jid, 'file-send-error', file_props)
if helpers.allow_showing_notification(account):

Yann Leboulanger
committed
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events', 'ft_error.png')
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
notify.notify(_('File Transfer Error'),
jid, account, 'file-send-error', path, file_props['name'])
def handle_event_gmail_notify(self, account, array):
jid = array[0]
gmail_new_messages = int(array[1])
if gajim.config.get('notify_on_new_gmail_email'):

Yann Leboulanger
committed
img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'events',
'single_msg_recv.png') #FIXME: find a better image
txt = i18n.ngettext('You have %d new E-mail message', 'You have %d new E-mail messages', gmail_new_messages, gmail_new_messages, gmail_new_messages)
txt = _('%(new_mail_gajim_ui_msg)s on %(gmail_mail_address)s') % {'new_mail_gajim_ui_msg': txt, 'gmail_mail_address': jid}
path = gtkgui_helpers.get_path_to_generic_or_avatar(img)
notify.notify(_('New E-mail'), jid, account, 'gmail', path_to_image = path, text = txt)

Yann Leboulanger
committed
def save_avatar_files(self, jid, photo_decoded, puny_nick = None):
'''Save the decoded avatar to a separate file, and generate files for dbus notifications'''
puny_jid = helpers.sanitize_filename(jid)
path_to_file = os.path.join(gajim.AVATAR_PATH, puny_jid)