Newer
Older
## Copyright (C) 2003-2013 Yann Leboulanger <asterix AT lagaule.org>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
## Copyright (C) 2005 Alex Podaras <bigpod AT gmail.com>
## Norman Rasmussen <norman AT rasmussen.co.za>
## Stéphan Kochen <stephan AT kochen.nl>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Alex Mauer <hawke AT hawkesnest.net>
## Copyright (C) 2005-2007 Travis Shirk <travis AT pobox.com>
## Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Junglecow J <junglecow AT gmail.com>
## Stefan Bethge <stefan AT lanpartei.de>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
## James Newton <redshodan AT gmail.com>
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
## Julien Pivotto <roidelapluie AT gmail.com>
## Stephan Erb <steve-e AT h3c.de>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
##
## This file is part of Gajim.
##
## Gajim 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 3 only.
##
## Gajim 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.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import os
import sys
import re
import time
import math

Alexander Cherniuk
committed
from subprocess import Popen
import gtk
import gobject
from common import i18n
from common import gajim
from common import dbus_support
if dbus_support.supported:
from music_track_listener import MusicTrackListener
from common import location_listener
import dbus
import gtkgui_helpers
import dialogs
import notify
import message_control
from chat_control import ChatControlBase
from chat_control import ChatControl
from groupchat_control import GroupchatControl
from groupchat_control import PrivateChatControl
from atom_window import AtomWindow
from session import ChatControlSession
import common.sleepy
from nbxmpp import idlequeue
from nbxmpp import Hashes
from common.zeroconf import connection_zeroconf
from common import resolver

steve-e
committed
from common import caps_cache
from common import proxy65_manager
from common import socks5
from common import helpers
from common import dataforms
from common import passwords
from common import logging_helpers
from common.connection_handlers_events import OurShowEvent, \
from common.connection import Connection
from common import jingle
import roster_window
import profile_window
import config
from threading import Thread
gajimpaths = common.configpaths.gajimpaths
config_filename = gajimpaths['CONFIG_FILE']
from common import optparser
parser = optparser.OptionsParser(config_filename)
import logging
log = logging.getLogger('gajim.interface')
class Interface:
################################################################################
### Methods handling events from connection
################################################################################

Yann Leboulanger
committed
def handle_event_db_error(self, unused, data):
#('DB_ERROR', account, (title_text, section_text))
if self.db_error_dialog:
return
self.db_error_dialog = dialogs.ErrorDialog(data[0], data[1])
def destroyed(win):
self.db_error_dialog = None
self.db_error_dialog.connect('destroy', destroyed)
def handle_event_information(self, obj):
if obj.popup:
if obj.level == 'error':
cls = dialogs.ErrorDialog
elif obj.level == 'warn':
cls = dialogs.WarningDialog
elif obj.level == 'info':
cls = dialogs.InformationDialog
else:
return
cls(obj.pri_txt, obj.sec_txt)
def handle_ask_new_nick(self, account, room_jid):
title = _('Unable to join group chat')
prompt = _('Your desired nickname in group chat %s is in use or '
'registered by another occupant.\nPlease specify another nickname '
'below:') % room_jid
check_text = _('Always use this nickname when there is a conflict')
if 'change_nick_dialog' in self.instances:
self.instances['change_nick_dialog'].add_room(account, room_jid,
else:
self.instances['change_nick_dialog'] = dialogs.ChangeNickDialog(
account, room_jid, title, prompt)

Yann Leboulanger
committed
def handle_event_http_auth(self, obj):
#('HTTP_AUTH', account, (method, url, transaction_id, iq_obj, msg))

Yann Leboulanger
committed
def response(account, answer):
obj.conn.build_http_auth_answer(obj.stanza, answer)

Yann Leboulanger
committed
def on_yes(is_checked, obj):
response(obj, 'yes')

Yann Leboulanger
committed
account = obj.conn.name
sec_msg = _('Do you accept this request?')
if gajim.get_number_of_connected_accounts() > 1:
sec_msg = _('Do you accept this request on account %s?') % account

Yann Leboulanger
committed
if obj.msg:
sec_msg = obj.msg + '\n' + sec_msg
dialog = dialogs.YesNoDialog(_('HTTP (%(method)s) Authorization for '

Yann Leboulanger
committed
'%(url)s (id: %(id)s)') % {'method': obj.method, 'url': obj.url,
'id': obj.iq_id}, sec_msg, on_response_yes=(on_yes, obj),
on_response_no=(response, obj, 'no'))
def handle_event_iq_error(self, obj):
#('ERROR_ANSWER', account, (id_, fjid, errmsg, errcode))
if unicode(obj.errcode) in ('400', '403', '406') and obj.id_:
# show the error dialog
ft = self.instances['file_transfers']
sid = obj.id_
if len(obj.id_) > 3 and obj.id_[2] == '_':
sid = obj.id_[3:]
file_props = FilesProp.getFileProp(obj.conn.name, sid)
if file_props :
gajim.nec.push_incoming_event(FileRequestErrorEvent(None,
obj.conn.disconnect_transfer(file_props)
elif unicode(obj.errcode) == '404':
sid = obj.id_
if len(obj.id_) > 3 and obj.id_[2] == '_':
sid = obj.id_[3:]
if file_props:
self.handle_event_file_send_error(obj.conn.name, (obj.fjid,
file_props))
obj.conn.disconnect_transfer(file_props)
return
ctrl = self.msg_win_mgr.get_control(obj.fjid, obj.conn.name)
if ctrl and ctrl.type_id == message_control.TYPE_GC:
ctrl.print_conversation('Error %s: %s' % (obj.errcode, obj.errmsg))
def handle_event_connection_lost(self, obj):
# ('CONNECTION_LOST', account, [title, text])
path = gtkgui_helpers.get_icon_path('gajim-connection_lost', 48)
notify.popup(_('Connection Failed'), account, account,
'connection_failed', path, obj.title, obj.msg)
def unblock_signed_in_notifications(self, account):
gajim.block_signed_in_notifications[account] = False
def handle_event_status(self, obj): # OUR status
#('STATUS', account, show)
account = obj.conn.name
if obj.show in ('offline', 'error'):
for name in self.instances[account]['online_dialog'].keys():
# .keys() is needed to not have a dictionary length changed
# during iteration error
self.instances[account]['online_dialog'][name].destroy()
if name in self.instances[account]['online_dialog']:
# destroy handler may have already removed it
del self.instances[account]['online_dialog'][name]
for request in self.gpg_passphrase.values():
if request:

Yann Leboulanger
committed
request.interrupt(account=account)
if account in self.pass_dialog:
self.pass_dialog[account].window.destroy()
gajim.block_signed_in_notifications[account] = True
else:
# 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_seconds(30,
self.unblock_signed_in_notifications, account)
if account in self.show_vcard_when_connect and obj.show not in (
'offline', 'error'):
self.edit_own_details(account)
def edit_own_details(self, account):
jid = gajim.get_jid_from_account(account)
if 'profile' not in self.instances[account]:
self.instances[account]['profile'] = \
profile_window.ProfileWindow(account, gajim.interface.roster.window)
gajim.connections[account].request_vcard(jid)
def handle_gc_error(self, gc_control, pritext, sectext):
if gc_control.error_dialog:
gc_control.error_dialog.destroy()
def on_close(dummy):
gc_control.error_dialog.destroy()
gc_control.error_dialog = None
gc_control.error_dialog = dialogs.ErrorDialog(pritext, sectext,
on_response_ok=on_close, on_response_cancel=on_close)
gc_control.error_dialog.set_modal(False)
if gc_control.parent_win:
gc_control.error_dialog.set_transient_for(
gc_control.parent_win.window)
d = dialogs.ErrorDialog(pritext, sectext)
if gc_control and gc_control.parent_win:
d.set_transient_for(gc_control.parent_win.window)
d.set_modal(False)
def handle_gc_password_required(self, account, room_jid, nick):
def on_ok(text):
gajim.connections[account].join_gc(nick, room_jid, text)
gajim.gc_passwords[room_jid] = text

Yann Leboulanger
committed
gc_control.error_dialog = None
def on_cancel():
# get and destroy window
if room_jid in gajim.interface.minimized_controls[account]:
self.roster.on_disconnect(None, room_jid, account)
else:
win = self.msg_win_mgr.get_window(room_jid, account)
ctrl = self.msg_win_mgr.get_gc_control(room_jid, account)
win.remove_tab(ctrl, 3)

Yann Leboulanger
committed
gc_control.error_dialog = None

Yann Leboulanger
committed
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
if gc_control:
if gc_control.error_dialog:
gc_control.error_dialog.destroy()
gc_control.error_dialog = dialogs.InputDialog(_('Password Required'),
_('A Password is required to join the room %s. Please type it.') % \
room_jid, is_modal=False, ok_handler=on_ok,
cancel_handler=on_cancel)
gc_control.error_dialog.input_entry.set_visibility(False)
def handle_event_gc_presence(self, obj):
gc_control = obj.gc_control
if obj.ptype == 'error':
if obj.errcode == '503':
# maximum user number reached
self.handle_gc_error(gc_control,
_('Unable to join group chat'),
_('Maximum number of users for <b>%s</b> has been reached')\
% obj.room_jid)
elif (obj.errcode == '401') or (obj.errcon == 'not-authorized'):
# password required to join
self.handle_gc_password_required(obj.conn.name, obj.room_jid,
obj.nick)
elif (obj.errcode == '403') or (obj.errcon == 'forbidden'):
# we are banned
self.handle_gc_error(gc_control, _('Unable to join group chat'),
_('You are banned from group chat <b>%s</b>.') % \
obj.room_jid)
elif (obj.errcode == '404') or (obj.errcon in ('item-not-found',
'remote-server-not-found')):
# group chat does not exist
self.handle_gc_error(gc_control, _('Unable to join group chat'),
_('Group chat <b>%s</b> does not exist.') % obj.room_jid)
elif (obj.errcode == '405') or (obj.errcon == 'not-allowed'):
self.handle_gc_error(gc_control, _('Unable to join group chat'),
_('Group chat creation is restricted.'))
elif (obj.errcode == '406') or (obj.errcon == 'not-acceptable'):
self.handle_gc_error(gc_control, _('Unable to join group chat'),
_('Your registered nickname must be used in group chat '
'<b>%s</b>.') % obj.room_jid)
elif (obj.errcode == '407') or (obj.errcon == \
'registration-required'):
self.handle_gc_error(gc_control, _('Unable to join group chat'),
_('You are not in the members list in groupchat %s.') % \
obj.room_jid)
elif (obj.errcode == '409') or (obj.errcon == 'conflict'):
self.handle_ask_new_nick(obj.conn.name, obj.room_jid)
elif gc_control:
gc_control.print_conversation('Error %s: %s' % (obj.errcode,
obj.errmsg))
if gc_control and gc_control.autorejoin:
gc_control.autorejoin = False

Yann Leboulanger
committed
def handle_event_gc_message(self, obj):
if not obj.stanza.getTag('body'): # no <body>
# It could be a voice request. See
# http://www.xmpp.org/extensions/xep-0045.html#voiceapprove
if obj.msg_obj.form_node:
dialogs.SingleMessageWindow(obj.conn.name, obj.fjid,
action='receive', from_whom=obj.fjid,
subject='', message='', resource='', session=None,
form_node=obj.msg_obj.form_node)
def handle_event_presence(self, obj):
# 'NOTIFY' (account, (jid, status, status message, resource,
# priority, # keyID, timestamp, contact_nickname))
#
# Contact changed show
account = obj.conn.name
jid = obj.jid
show = obj.show
status = obj.status
resource = obj.resource or ''
jid_list = gajim.contacts.get_jid_list(account)
# unset custom status
if (obj.old_show == 0 and obj.new_show > 1) or \
(obj.old_show > 1 and obj.new_show == 0 and obj.conn.connected > 1):
if account in self.status_sent_to_users and \
jid in self.status_sent_to_users[account]:
del self.status_sent_to_users[account][jid]
if gajim.jid_is_transport(jid):
# It must be an agent
# transport just signed in/out, don't show
# popup notifications for 30s
account_jid = account + '/' + jid
gajim.block_signed_in_notifications[account_jid] = True
gobject.timeout_add_seconds(30,
self.unblock_signed_in_notifications, account_jid)
highest = gajim.contacts.get_contact_with_highest_priority(account, jid)
is_highest = (highest and highest.resource == resource)

Yann Leboulanger
committed
ctrl = self.msg_win_mgr.get_control(jid, account)
if ctrl and ctrl.session and len(obj.contact_list) > 1:

Yann Leboulanger
committed
ctrl.remove_session(ctrl.session)
def handle_event_msgerror(self, obj):
#'MSGERROR' (account, (jid, error_code, error_msg, msg, time[session]))
account = obj.conn.name
jids = obj.fjid.split('/', 1)
jid = jids[0]
gc_control = self.msg_win_mgr.get_gc_control(jid, account)
if not gc_control and \
jid in self.minimized_controls[account]:
gc_control = self.minimized_controls[account][jid]
if gc_control and gc_control.type_id != message_control.TYPE_GC:
gc_control = None
if gc_control:
if len(jids) > 1: # it's a pm
nick = jids[1]
if session:
ctrl = session.control
else:
ctrl = self.msg_win_mgr.get_control(obj.fjid, account)
if not ctrl:
tv = gc_control.list_treeview
model = tv.get_model()
iter_ = gc_control.get_contact_iter(nick)
if iter_:
show = model[iter_][3]
else:
show = 'offline'
gc_c = gajim.contacts.create_gc_contact(room_jid=jid,
account=account, name=nick, show=show)
ctrl = self.new_private_chat(gc_c, account, session)
ctrl.print_conversation(_('Error %(code)s: %(msg)s') % {
'code': obj.error_code, 'msg': obj.error_msg}, 'status')
return
gc_control.print_conversation(_('Error %(code)s: %(msg)s') % {
'code': obj.error_code, 'msg': obj.error_msg}, 'status')
if gc_control.parent_win and \
gc_control.parent_win.get_active_jid() == jid:
gc_control.set_subject(gc_control.subject)
return
if gajim.jid_is_transport(jid):
jid = jid.replace('@', '')
msg = _('error while sending %(message)s ( %(error)s )') % {
session.roster_message(jid, msg, obj.time_, msg_type='error')
def handle_event_msgsent(self, obj):
#('MSGSENT', account, (jid, msg, keyID))
# do not play sound when standalone chatstate message (eg no msg)
if obj.message and gajim.config.get_per('soundevents', 'message_sent',
helpers.play_sound('message_sent')
def handle_event_msgnotsent(self, obj):
#('MSGNOTSENT', account, (jid, ierror_msg, msg, time, session))
msg = _('error while sending %(message)s ( %(error)s )') % {
'message': obj.message, 'error': obj.error}
if not obj.session:
# No session. This can happen when sending a message from
# gajim-remote
log.warn(msg)
return
obj.session.roster_message(obj.jid, msg, obj.time_, obj.conn.name,
msg_type='error')
def handle_event_subscribe_presence(self, obj):
#('SUBSCRIBE', account, (jid, text, user_nick)) user_nick is JEP-0172
if helpers.allow_popup_window(account) or not self.systray_enabled:
if obj.jid in self.instances[account]['sub_request']:
self.instances[account]['sub_request'][obj.jid].window.destroy()
self.instances[account]['sub_request'][obj.jid] = \
dialogs.SubscriptionRequestWindow(obj.jid, obj.status, account,
self.add_event(account, obj.jid, 'subscription_request', (obj.status,
obj.user_nick))
if helpers.allow_showing_notification(account):
path = gtkgui_helpers.get_icon_path('gajim-subscription_request',
48)
event_type = _('Subscription request')
notify.popup(event_type, obj.jid, account, 'subscription_request',
path, event_type, obj.jid)
def handle_event_subscribed_presence(self, obj):
#('SUBSCRIBED', account, (jid, resource))
account = obj.conn.name
if obj.jid in gajim.contacts.get_jid_list(account):
c = gajim.contacts.get_first_contact_from_jid(account, obj.jid)
c.resource = obj.resource
self.roster.remove_contact_from_groups(c.jid, account,
[_('Not in Roster'), _('Observers')], update=False)
else:
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if obj.jid in attached_keys:
keyID = attached_keys[attached_keys.index(obj.jid) + 1]
name = obj.jid.split('@', 1)[0]
name = name.split('%', 1)[0]
contact1 = gajim.contacts.create_contact(jid=obj.jid,
account=account, name=name, groups=[], show='online',
status='online', ask='to', resource=obj.resource, keyID=keyID)
gajim.contacts.add_contact(account, contact1)
self.roster.add_contact(obj.jid, account)
dialogs.InformationDialog(_('Authorization accepted'),
_('The contact "%s" has authorized you to see his or her status.')
def show_unsubscribed_dialog(self, account, contact):
def on_yes(is_checked, list_):
self.roster.on_req_usub(None, list_)
list_ = [(contact, account)]
dialogs.YesNoDialog(
_('Contact "%s" removed subscription from you') % contact.jid,
_('You will always see him or her as offline.\nDo you want to '
'remove him or her from your contact list?'),
on_response_yes=(on_yes, list_))
# FIXME: Per RFC 3921, we can "deny" ack as well, but the GUI does
# not show deny
def handle_event_unsubscribed_presence(self, obj):
#('UNSUBSCRIBED', account, jid)
account = obj.conn.name
contact = gajim.contacts.get_first_contact_from_jid(account, obj.jid)
if not contact:
return
if helpers.allow_popup_window(account) or not self.systray_enabled:
self.show_unsubscribed_dialog(account, contact)
return
self.add_event(account, obj.jid, 'unsubscribed', contact)
if helpers.allow_showing_notification(account):
path = gtkgui_helpers.get_icon_path('gajim-unsubscribed', 48)
event_type = _('Unsubscribed')
notify.popup(event_type, obj.jid, account, 'unsubscribed', path,
event_type, obj.jid)
def handle_event_register_agent_info(self, obj):
# ('REGISTER_AGENT_INFO', account, (agent, infos, is_form))
# info in a dataform if is_form is True
if obj.is_form or 'instructions' in obj.config:
config.ServiceRegistrationWindow(obj.agent, obj.config,
obj.conn.name, obj.is_form)
dialogs.ErrorDialog(_('Contact with "%s" cannot be established') % \
obj.agent, _('Check your connection or try again later.'))
# ('VCARD', account, data)
'''vcard holds the vcard data'''
our_jid = gajim.get_jid_from_account(obj.conn.name)
if obj.jid == our_jid:
if obj.nickname:
gajim.nicks[obj.conn.name] = obj.nickname
if obj.conn.name in self.show_vcard_when_connect:
self.show_vcard_when_connect.remove(obj.conn.name)
def handle_event_last_status_time(self, obj):
# ('LAST_STATUS_TIME', account, (jid, resource, seconds, status))
if obj.seconds < 0:
# Ann error occured
return
account = obj.conn.name
c = gajim.contacts.get_contact(account, obj.jid, obj.resource)
if c: # c can be none if it's a gc contact
if obj.status:
c.status = obj.status
self.roster.draw_contact(c.jid, account) # draw offline status
last_time = time.localtime(time.time() - obj.seconds)
if c.show == 'offline':
c.last_status_time = last_time
else:
c.last_activity_time = last_time
if self.roster.tooltip.id and self.roster.tooltip.win:
self.roster.tooltip.update_last_time(last_time)
def handle_event_gc_config(self, obj):
#('GC_CONFIG', account, (jid, form_node)) config is a dict
account = obj.conn.name
if obj.jid in gajim.automatic_rooms[account]:
if 'continue_tag' in gajim.automatic_rooms[account][obj.jid]:
# We're converting chat to muc. allow participants to invite
for f in obj.dataform.iter_fields():
if f.var == 'muc#roomconfig_allowinvites':
f.value = True
elif f.var == 'muc#roomconfig_publicroom':
f.value = False
elif f.var == 'muc#roomconfig_membersonly':
f.value = True
elif f.var == 'public_list':
f.value = False
obj.conn.send_gc_config(obj.jid, obj.dataform.get_purged())
else:
# use default configuration
obj.conn.send_gc_config(obj.jid, obj.form_node)
# invite contacts
# check if it is necessary to add <continue />
continue_tag = False
if 'continue_tag' in gajim.automatic_rooms[account][obj.jid]:
continue_tag = True
if 'invities' in gajim.automatic_rooms[account][obj.jid]:
for jid in gajim.automatic_rooms[account][obj.jid]['invities']:
obj.conn.send_invite(obj.jid, jid,

Yann Leboulanger
committed
gc_control = self.msg_win_mgr.get_gc_control(obj.jid,
account)
if gc_control:
gc_control.print_conversation(
_('%(jid)s has been invited in this room') % {
'jid': jid}, graphics=False)
del gajim.automatic_rooms[account][obj.jid]
elif obj.jid not in self.instances[account]['gc_config']:
self.instances[account]['gc_config'][obj.jid] = \
config.GroupchatConfigWindow(account, obj.jid, obj.dataform)
def handle_event_gc_affiliation(self, obj):
#('GC_AFFILIATION', account, (room_jid, users_dict))
account = obj.conn.name
if obj.jid in self.instances[account]['gc_config']:
self.instances[account]['gc_config'][obj.jid].\
affiliation_list_received(obj.users_dict)
def handle_event_gc_decline(self, obj):
account = obj.conn.name
gc_control = self.msg_win_mgr.get_gc_control(obj.room_jid, account)
if gc_control:
if obj.reason:
gc_control.print_conversation(
_('%(jid)s declined the invitation: %(reason)s') % {
'jid': obj.jid_from, 'reason': obj.reason}, graphics=False)
else:
gc_control.print_conversation(
_('%(jid)s declined the invitation') % {
'jid': obj.jid_from}, graphics=False)
def handle_event_gc_invitation(self, obj):
#('GC_INVITATION', (room_jid, jid_from, reason, password, is_continued))
if helpers.allow_popup_window(account) or not self.systray_enabled:

Yann Leboulanger
committed
dialogs.InvitationReceivedDialog(account, obj.room_jid,
obj.jid_from, obj.password, obj.reason,
is_continued=obj.is_continued)

Yann Leboulanger
committed
self.add_event(account, obj.jid_from, 'gc-invitation', (obj.room_jid,
obj.reason, obj.password, obj.is_continued, obj.jid_from))
if helpers.allow_showing_notification(account):
path = gtkgui_helpers.get_icon_path('gajim-gc_invitation', 48)
event_type = _('Groupchat Invitation')

Yann Leboulanger
committed
notify.popup(event_type, obj.jid_from, account, 'gc-invitation',
path, event_type, obj.room_jid)
def forget_gpg_passphrase(self, keyid):
if keyid in self.gpg_passphrase:
del self.gpg_passphrase[keyid]
return False
def handle_event_bad_gpg_passphrase(self, obj):
#('BAD_PASSPHRASE', account, ())
sectext = _('You configured Gajim to use OpenPGP agent, but there '
'is no OpenPGP agent running or it returned a wrong passphrase.'
'\n')
sectext += _('You are currently connected without your OpenPGP '
'key.')
dialogs.WarningDialog(_('Your passphrase is incorrect'), sectext)
else:
path = gtkgui_helpers.get_icon_path('gtk-dialog-warning', 48)
notify.popup('warning', account, account, 'warning', path,
_('You are currently connected without your OpenPGP key.'))
self.forget_gpg_passphrase(obj.keyID)

Yann Leboulanger
committed
def handle_event_client_cert_passphrase(self, obj):
def on_ok(passphrase, checked):
obj.conn.on_client_cert_passphrase(passphrase, obj.con, obj.port,
obj.secure_tuple)
def on_cancel():
obj.conn.on_client_cert_passphrase('', obj.con, obj.port,
obj.secure_tuple)
dialogs.PassphraseDialog(_('Certificate Passphrase Required'),
_('Enter the passphrase for the certificate for account %s') % \
obj.conn.name, ok_handler=on_ok, cancel_handler=on_cancel)
def handle_event_gpg_password_required(self, obj):
#('GPG_PASSWORD_REQUIRED', account, (callback,))
if obj.keyid in self.gpg_passphrase:
request = self.gpg_passphrase[obj.keyid]
request = PassphraseRequest(obj.keyid)
self.gpg_passphrase[obj.keyid] = request
request.add_callback(obj.conn.name, obj.callback)
def handle_event_gpg_trust_key(self, obj):
#('GPG_ALWAYS_TRUST', account, callback)
def on_yes(checked):
if checked:
obj.conn.gpg.always_trust = True
obj.callback(True)
def on_no():
dialogs.YesNoDialog(_('OpenPGP key not trusted'), _('The OpenPGP key '
'used to encrypt this chat is not trusted. Do you really want to '
'encrypt this message?'), checktext=_('_Do not ask me again'),
on_response_yes=on_yes, on_response_no=on_no)
def handle_event_password_required(self, obj):
#('PASSWORD_REQUIRED', account, None)
if account in self.pass_dialog:
return
text = _('Enter your password for account %s') % account
if passwords.USER_HAS_GNOMEKEYRING and \
not passwords.USER_USES_GNOMEKEYRING:
text += '\n' + _('Gnome Keyring is installed but not '
'correctly started (environment variable probably not '
'correctly set)')
def on_ok(passphrase, save):
if save:
gajim.config.set_per('accounts', account, 'savepass', True)
passwords.save_password(account, passphrase)
obj.conn.set_password(passphrase)
del self.pass_dialog[account]
def on_cancel():
self.roster.set_state(account, 'offline')
self.roster.update_status_combobox()
del self.pass_dialog[account]
self.pass_dialog[account] = dialogs.PassphraseDialog(
_('Password Required'), text, _('Save password'), ok_handler=on_ok,
cancel_handler=on_cancel)
def handle_oauth2_credentials(self, obj):
account = obj.conn.name
def on_ok(refresh):
gajim.config.set_per('accounts', account, 'oauth2_refresh_token',
refresh)
st = gajim.config.get_per('accounts', account, 'last_status')
msg = helpers.from_one_line(gajim.config.get_per('accounts',
account, 'last_status_msg'))
gajim.interface.roster.send_status(account, st, msg)
del self.pass_dialog[account]
def on_cancel():
gajim.config.set_per('accounts', account, 'oauth2_refresh_token',
'')
self.roster.set_state(account, 'offline')
self.roster.update_status_combobox()
del self.pass_dialog[account]
instruction = _('Please copy / paste the refresh token from the website'
' that has just been opened.')
self.pass_dialog[account] = dialogs.InputTextDialog(
_('Oauth2 Credentials'), instruction, is_modal=False,
ok_handler=on_ok, cancel_handler=on_cancel)
def handle_event_roster_info(self, obj):
#('ROSTER_INFO', account, (jid, name, sub, ask, groups))
account = obj.conn.name
contacts = gajim.contacts.get_contacts(account, obj.jid)
if (not obj.sub or obj.sub == 'none') and \
(not obj.ask or obj.ask == 'none') and not obj.nickname and \
not obj.groups:
# contact removed us.
if contacts:
self.roster.remove_contact(obj.jid, account, backend=True)
return
elif not contacts:
return
# Add new contact to roster
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if obj.jid in attached_keys:
keyID = attached_keys[attached_keys.index(obj.jid) + 1]
contact = gajim.contacts.create_contact(jid=obj.jid,
account=account, name=obj.nickname, groups=obj.groups,
show='offline', sub=obj.sub, ask=obj.ask, keyID=keyID)
gajim.contacts.add_contact(account, contact)
self.roster.add_contact(obj.jid, account)
else:
# If contact has changed (sub, ask or group) update roster
# Mind about observer status changes:
# According to xep 0162, a contact is not an observer anymore when
# we asked for auth, so also remove him if ask changed
old_groups = contacts[0].groups
if obj.sub == 'remove':
# another of our instance removed a contact. Remove it here too
self.roster.remove_contact(obj.jid, account, backend=True)
return
if contacts[0].sub != obj.sub or contacts[0].ask != obj.ask\
or old_groups != obj.groups:
# c.get_shown_groups() has changed. Reflect that in

Yann Leboulanger
committed
# roster_window
self.roster.remove_contact(obj.jid, account, force=True)
for contact in contacts:
contact.name = obj.nickname or ''
contact.sub = obj.sub
contact.ask = obj.ask
contact.groups = obj.groups or []
if update:
self.roster.add_contact(obj.jid, account)
# Refilter and update old groups
for group in old_groups:
self.roster.draw_group(group, account)
self.roster.draw_contact(obj.jid, account)

Yann Leboulanger
committed
if obj.jid in self.instances[account]['sub_request'] and obj.sub in (
'from', 'both'):
self.instances[account]['sub_request'][obj.jid].window.destroy()
def handle_event_bookmarks(self, obj):
# ('BOOKMARKS', account, [{name,jid,autojoin,password,nick}, {}])
# We received a bookmark item from the server (JEP48)
# Auto join GC windows if neccessary
self.roster.set_actions_menu_needs_rebuild()
invisible_show = gajim.SHOW_LIST.index('invisible')
# do not autojoin if we are invisible
if obj.conn.connected == invisible_show:
self.auto_join_bookmarks(obj.conn.name)
def handle_event_file_send_error(self, account, array):
jid = array[0]
file_props = array[1]
ft = self.instances['file_transfers']
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):
path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48)
event_type = _('File Transfer Error')
notify.popup(event_type, jid, account, 'file-send-error', path,
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
def handle_event_gmail_notify(self, obj):
jid = obj.jid
gmail_new_messages = int(obj.newmsgs)
gmail_messages_list = obj.gmail_messages_list
if not gajim.config.get('notify_on_new_gmail_email'):
return
path = gtkgui_helpers.get_icon_path('gajim-new_email_recv', 48)
title = _('New mail on %(gmail_mail_address)s') % \
{'gmail_mail_address': jid}
text = i18n.ngettext('You have %d new mail conversation',
'You have %d new mail conversations', gmail_new_messages,
gmail_new_messages, gmail_new_messages)
if gajim.config.get('notify_on_new_gmail_email_extra'):
cnt = 0
for gmessage in gmail_messages_list:
# FIXME: emulate Gtalk client popups. find out what they
# parse and how they decide what to show each message has a
# 'From', 'Subject' and 'Snippet' field
if cnt >= 5:
break
senders = ',\n '.join(reversed(gmessage['From']))
text += _('\n\nFrom: %(from_address)s\nSubject: '
'%(subject)s\n%(snippet)s') % {'from_address': senders,
'subject': gmessage['Subject'],
'snippet': gmessage['Snippet']}
cnt += 1
command = gajim.config.get('notify_on_new_gmail_email_command')
if command:
Popen(command, shell=True)
if gajim.config.get_per('soundevents', 'gmail_received', 'enabled'):
helpers.play_sound('gmail_received')
notify.popup(_('New E-mail'), jid, obj.conn.name, 'gmail',
path_to_image=path, title=title, text=text)
def handle_event_file_request_error(self, obj):
# ('FILE_REQUEST_ERROR', account, (jid, file_props, error_msg))
ft = self.instances['file_transfers']
ft.set_status(obj.file_props, 'stop')
errno = obj.file_props.error
if helpers.allow_popup_window(obj.conn.name):
if errno in (-4, -5):
ft.show_stopped(obj.jid, obj.file_props, obj.error_msg)
ft.show_request_error(obj.file_props)
return
if errno in (-4, -5):
msg_type = 'file-error'
else:
msg_type = 'file-request-error'
self.add_event(obj.conn.name, obj.jid, msg_type, obj.file_props)
if helpers.allow_showing_notification(obj.conn.name):
# check if we should be notified
path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48)
event_type = _('File Transfer Error')
notify.popup(event_type, obj.jid, obj.conn.name, msg_type, path,
def handle_event_file_request(self, obj):
account = obj.conn.name
if obj.jid not in gajim.contacts.get_jid_list(account):
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
'attached_gpg_keys').split()
if obj.jid in attached_keys:
keyID = attached_keys[attached_keys.index(obj.jid) + 1]
contact = gajim.contacts.create_not_in_roster_contact(jid=obj.jid,
account=account, keyID=keyID)
gajim.contacts.add_contact(account, contact)
self.roster.add_contact(obj.jid, account)
contact = gajim.contacts.get_first_contact_from_jid(account, obj.jid)
if obj.file_props.session_type == 'jingle':
request = obj.stanza.getTag('jingle').getTag('content')\
.getTag('description').getTag('request')
if request:
# If we get a request instead
ft_win = self.instances['file_transfers']
ft_win.add_transfer(account, contact, obj.file_props)
if helpers.allow_popup_window(account):
self.instances['file_transfers'].show_file_request(account, contact,
self.add_event(account, obj.jid, 'file-request', obj.file_props)
if helpers.allow_showing_notification(account):
path = gtkgui_helpers.get_icon_path('gajim-ft_request', 48)
txt = _('%s wants to send you a file.') % gajim.get_name_from_jid(
event_type = _('File Transfer Request')
notify.popup(event_type, obj.jid, account, 'file-request',
path_to_image=path, title=event_type, text=txt)
def handle_event_file_error(self, title, message):
dialogs.ErrorDialog(title, message)
def handle_event_file_progress(self, account, file_props):
if time.time() - self.last_ftwindow_update > 0.5:
# update ft window every 500ms
self.last_ftwindow_update = time.time()
self.instances['file_transfers'].set_progress(file_props.type_,
file_props.sid, file_props.received_len)
def __compare_hashes(self, account, file_props):
session = gajim.connections[account].get_jingle_session(jid=None,

Yann Leboulanger
committed
ft_win = self.instances['file_transfers']

Yann Leboulanger
committed
# We disn't get the hash, sender probably don't support that

Yann Leboulanger
committed
self.popup_ft_result(account, jid, file_props)
# If the hash we received and the hash of the file are the same,
# then the file is not corrupt
gobject.idle_add(self.popup_ft_result, account, jid, file_props)
gobject.idle_add(ft_win.set_status, file_props, 'ok')

Yann Leboulanger
committed
else:
# wrong hash, we need to get the file again!
gobject.idle_add(self.popup_ft_result, account, jid, file_props)
gobject.idle_add(ft_win.set_status, file_props, 'hash_error')
# End jingle session
if session:
session.end_session()
def handle_event_file_rcv_completed(self, account, file_props):
ft = self.instances['file_transfers']
if file_props.error == 0:
ft.set_progress(file_props.type_, file_props.sid,
file_props.received_len)
ft.set_status(file_props, 'stop')
if file_props.stalled or file_props.paused:
if file_props.type_ == 'r' and file_props.hash_: # we receive a file
gajim.socks5queue.remove_receiver(file_props.sid, True, True)