gui_interface.py 125 KB
Newer Older
steve-e's avatar
steve-e committed
1 2 3
# -*- coding:utf-8 -*-
## src/gajim.py
##
4
## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
steve-e's avatar
steve-e committed
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 42
## 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
43
from subprocess import Popen
steve-e's avatar
steve-e committed
44 45 46 47 48 49 50 51 52

import gtk
import gobject

from common import i18n
from common import gajim

from common import dbus_support
if dbus_support.supported:
53 54 55
    from music_track_listener import MusicTrackListener
    from common import location_listener
    import dbus
steve-e's avatar
steve-e committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

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 common.xmpp import idlequeue
from common.zeroconf import connection_zeroconf
from common import resolver
76
from common import caps_cache
steve-e's avatar
steve-e committed
77 78 79 80 81 82
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
Dicson's avatar
Dicson committed
83 84
from common.connection_handlers_events import OurShowEvent, \
    FileRequestErrorEvent
85
from common.connection import Connection
steve-e's avatar
steve-e committed
86 87 88 89 90

import roster_window
import profile_window
import config
from threading import Thread
91
from common import ged
steve-e's avatar
steve-e committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

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
################################################################################

108 109 110 111
    def handle_event_error(self, unused, data):
        #('ERROR', account, (title_text, section_text))
        dialogs.ErrorDialog(data[0], data[1])

112 113 114 115 116 117 118 119 120
    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)

121 122 123 124
    def handle_event_information(self, unused, data):
        #('INFORMATION', account, (title_text, section_text))
        dialogs.InformationDialog(data[0], data[1])

125
    def handle_ask_new_nick(self, account, room_jid):
126 127
        title = _('Unable to join group chat')
        prompt = _('Your desired nickname in group chat %s is in use or '
128 129
            'registered by another occupant.\nPlease specify another nickname '
            'below:') % room_jid
130 131 132
        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,
133
                prompt)
134 135
        else:
            self.instances['change_nick_dialog'] = dialogs.ChangeNickDialog(
136
                account, room_jid, title, prompt)
137

138
    def handle_event_http_auth(self, obj):
139
        #('HTTP_AUTH', account, (method, url, transaction_id, iq_obj, msg))
140
        def response(account, answer):
141
            obj.conn.build_http_auth_answer(obj.stanza, answer)
142

143 144
        def on_yes(is_checked, obj):
            response(obj, 'yes')
145

146
        account = obj.conn.name
147 148 149
        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
150 151
        if obj.msg:
            sec_msg = obj.msg + '\n' + sec_msg
152
        dialog = dialogs.YesNoDialog(_('HTTP (%(method)s) Authorization for '
153 154 155
            '%(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'))
156

157
    def handle_event_iq_error(self, obj):
158 159
        #('ERROR_ANSWER', account, (id_, fjid, errmsg, errcode))
        if unicode(obj.errcode) in ('400', '403', '406') and obj.id_:
160 161
            # show the error dialog
            ft = self.instances['file_transfers']
162 163 164
            sid = obj.id_
            if len(obj.id_) > 3 and obj.id_[2] == '_':
                sid = obj.id_[3:]
165 166
            if sid in ft.files_props['s']:
                file_props = ft.files_props['s'][sid]
167
                if unicode(obj.errcode) == '400':
168 169 170
                    file_props['error'] = -3
                else:
                    file_props['error'] = -4
171
                gajim.nec.push_incoming_event(FileRequestErrorEvent(None,
Yann Leboulanger's avatar
Yann Leboulanger committed
172
                    conn=obj.conn, jid=obj.jid, file_props=file_props,
173
                    error_msg=obj.errmsg))
174
                obj.conn.disconnect_transfer(file_props)
175
                return
176 177 178 179 180 181 182 183 184
        elif unicode(obj.errcode) == '404':
            sid = obj.id_
            if len(obj.id_) > 3 and obj.id_[2] == '_':
                sid = obj.id_[3:]
            if sid in obj.conn.files_props:
                file_props = obj.conn.files_props[sid]
                self.handle_event_file_send_error(obj.conn.name, (obj.fjid,
                    file_props))
                obj.conn.disconnect_transfer(file_props)
185 186
                return

187
        ctrl = self.msg_win_mgr.get_control(obj.fjid, obj.conn.name)
188
        if ctrl and ctrl.type_id == message_control.TYPE_GC:
189
            ctrl.print_conversation('Error %s: %s' % (obj.errcode, obj.errmsg))
190

191
    def handle_event_connection_lost(self, obj):
192 193
        # ('CONNECTION_LOST', account, [title, text])
        path = gtkgui_helpers.get_icon_path('gajim-connection_lost', 48)
194
        account = obj.conn.name
195
        notify.popup(_('Connection Failed'), account, account,
196
            'connection_failed', path, obj.title, obj.msg)
197 198 199 200

    def unblock_signed_in_notifications(self, account):
        gajim.block_signed_in_notifications[account] = False

201
    def handle_event_status(self, obj): # OUR status
202
        #('STATUS', account, show)
203 204
        account = obj.conn.name
        if obj.show in ('offline', 'error'):
205
            for name in self.instances[account]['online_dialog'].keys():
206 207
                # .keys() is needed to not have a dictionary length changed
                # during iteration error
208
                self.instances[account]['online_dialog'][name].destroy()
209 210 211
                if name in self.instances[account]['online_dialog']:
                    # destroy handler may have already removed it
                    del self.instances[account]['online_dialog'][name]
212 213
            for request in self.gpg_passphrase.values():
                if request:
214
                    request.interrupt(account=account)
215 216
            if account in self.pass_dialog:
                self.pass_dialog[account].window.destroy()
217
        if obj.show == 'offline':
218 219 220 221 222 223
            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
224 225
            gobject.timeout_add_seconds(30,
                self.unblock_signed_in_notifications, account)
226 227 228

        if account in self.show_vcard_when_connect and obj.show not in (
        'offline', 'error'):
229 230 231 232 233 234 235 236 237
            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.connections[account].request_vcard(jid)

238
    def handle_gc_error(self, gc_control, pritext, sectext):
Yann Leboulanger's avatar
Yann Leboulanger committed
239
        if gc_control and gc_control.autorejoin is not None:
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
            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)
        else:
            dialogs.ErrorDialog(pritext, sectext)

    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

        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)

        dlg = 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)
        dlg.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 %s 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 %s.') % 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 %s 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 '
                    '%s.') % 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))
309 310
            if gc_control and gc_control.autorejoin:
                gc_control.autorejoin = False
311

312
    def handle_event_presence(self, obj):
313 314 315 316 317
        # 'NOTIFY' (account, (jid, status, status message, resource,
        # priority, # keyID, timestamp, contact_nickname))
        #
        # Contact changed show

318 319 320 321 322
        account = obj.conn.name
        jid = obj.jid
        show = obj.show
        status = obj.status
        resource = obj.resource or ''
323 324 325

        jid_list = gajim.contacts.get_jid_list(account)

326 327 328 329 330 331
        # 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]
332 333 334 335

        if gajim.jid_is_transport(jid):
            # It must be an agent

336 337 338 339 340 341 342 343 344
            # 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)

        else:
            # It isn't an agent
345
            # Notifications
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
            obj.show_notif = True
            for c in obj.contact_list:
                if c.resource == resource:
                    # we look for other connected resources
                    continue
                if c.show not in ('offline', 'error'):
                    obj.show_notif = False
                    break
            if obj.show_notif:
                # no other resource is connected, let's look in metacontacts
                family = gajim.contacts.get_metacontacts_family(account,
                    jid)
                for info in family:
                    acct_ = info['account']
                    jid_ = info['jid']
                    c_ = gajim.contacts.get_contact_with_highest_priority(
                        acct_, jid_)
                    if not c_:
364
                        continue
365 366
                    if c_.show not in ('offline', 'error'):
                        obj.show_notif = False
367
                        break
368
            if obj.show_notif:
369 370 371 372
                if obj.old_show < 2 and obj.new_show > 1:
                    notify.notify('contact_connected', jid, account, status)

                elif obj.old_show > 1 and obj.new_show < 2:
373
                    notify.notify('contact_disconnected', jid, account, status)
374 375 376 377 378
                # Status change (not connected/disconnected or
                # error (<1))
                elif obj.new_show > 1:
                    notify.notify('status_change', jid, account, [obj.new_show,
                        status])
379 380 381 382

        highest = gajim.contacts.get_contact_with_highest_priority(account, jid)
        is_highest = (highest and highest.resource == resource)

383 384
        # disconnect the session from the ctrl if the highest resource has
        # changed
385 386
        if (obj.was_highest and not is_highest) or \
        (not obj.was_highest and is_highest):
387 388 389 390 391 392
            ctrl = self.msg_win_mgr.get_control(jid, account)
            if ctrl:
                ctrl.no_autonegotiation = False
                ctrl.set_session(None)
                ctrl.contact = highest

393 394 395 396
    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)
397 398
        jid = jids[0]

399
        if obj.error_code == '503':
400 401 402 403
            # If we get server-not-found error, stop sending chatstates
            for contact in gajim.contacts.get_contacts(account, jid):
                contact.composing_xep = False

404
        session = obj.session
405 406 407 408 409 410 411 412 413 414 415 416 417 418

        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:
419
                    ctrl = self.msg_win_mgr.get_control(obj.fjid, account)
420 421 422 423 424 425 426 427 428

                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'
429 430
                    gc_c = gajim.contacts.create_gc_contact(room_jid=jid,
                        account=account, name=nick, show=show)
431 432 433
                    ctrl = self.new_private_chat(gc_c, account, session)

                ctrl.print_conversation(_('Error %(code)s: %(msg)s') % {
434
                    'code': obj.error_code, 'msg': obj.error_msg}, 'status')
435 436 437
                return

            gc_control.print_conversation(_('Error %(code)s: %(msg)s') % {
438
                'code': obj.error_code, 'msg': obj.error_msg}, 'status')
439 440
            if gc_control.parent_win and \
            gc_control.parent_win.get_active_jid() == jid:
441 442 443 444 445
                gc_control.set_subject(gc_control.subject)
            return

        if gajim.jid_is_transport(jid):
            jid = jid.replace('@', '')
446 447
        msg = obj.error_msg
        if obj.msg:
448
            msg = _('error while sending %(message)s ( %(error)s )') % {
449
                    'message': obj.msg, 'error': msg}
450
        if session:
451
            session.roster_message(jid, msg, obj.time_, msg_type='error')
452

453
    def handle_event_msgsent(self, obj):
454 455
        #('MSGSENT', account, (jid, msg, keyID))
        # do not play sound when standalone chatstate message (eg no msg)
456
        if obj.message and gajim.config.get_per('soundevents', 'message_sent',
457
        'enabled'):
458 459
            helpers.play_sound('message_sent')

460
    def handle_event_msgnotsent(self, obj):
461 462
        #('MSGNOTSENT', account, (jid, ierror_msg, msg, time, session))
        msg = _('error while sending %(message)s ( %(error)s )') % {
463 464
                'message': obj.message, 'error': obj.error}
        if not obj.session:
465 466
            # No session. This can happen when sending a message from
            # gajim-remote
467 468
            log.warn(msg)
            return
469 470
        obj.session.roster_message(obj.jid, msg, obj.time_, obj.conn.name,
            msg_type='error')
471

472
    def handle_event_subscribe_presence(self, obj):
473
        #('SUBSCRIBE', account, (jid, text, user_nick)) user_nick is JEP-0172
474
        account = obj.conn.name
475
        if helpers.allow_popup_window(account) or not self.systray_enabled:
476 477
            dialogs.SubscriptionRequestWindow(obj.jid, obj.status, account,
                obj.user_nick)
478 479
            return

480 481
        self.add_event(account, obj.jid, 'subscription_request', (obj.status,
            obj.user_nick))
482 483

        if helpers.allow_showing_notification(account):
484 485
            path = gtkgui_helpers.get_icon_path('gajim-subscription_request',
                48)
486
            event_type = _('Subscription request')
487 488
            notify.popup(event_type, obj.jid, account, 'subscription_request',
                path, event_type, obj.jid)
489

490
    def handle_event_subscribed_presence(self, obj):
491
        #('SUBSCRIBED', account, (jid, resource))
492 493 494 495
        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
496
            self.roster.remove_contact_from_groups(c.jid, account,
497
                [_('Not in Roster'), _('Observers')], update=False)
498 499 500
        else:
            keyID = ''
            attached_keys = gajim.config.get_per('accounts', account,
501
                'attached_gpg_keys').split()
502 503 504
            if obj.jid in attached_keys:
                keyID = attached_keys[attached_keys.index(obj.jid) + 1]
            name = obj.jid.split('@', 1)[0]
505
            name = name.split('%', 1)[0]
506 507 508
            contact1 = gajim.contacts.create_contact(jid=obj.jid,
                account=account, name=name, groups=[], show='online',
                status='online', ask='to', resource=obj.resource, keyID=keyID)
509
            gajim.contacts.add_contact(account, contact1)
510
            self.roster.add_contact(obj.jid, account)
511
        dialogs.InformationDialog(_('Authorization accepted'),
512
            _('The contact "%s" has authorized you to see his or her status.')
513
            % obj.jid)
514 515 516 517 518 519 520 521 522 523 524 525 526

    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

527
    def handle_event_unsubscribed_presence(self, obj):
528
        #('UNSUBSCRIBED', account, jid)
529 530
        account = obj.conn.name
        contact = gajim.contacts.get_first_contact_from_jid(account, obj.jid)
531 532 533 534 535 536 537
        if not contact:
            return

        if helpers.allow_popup_window(account) or not self.systray_enabled:
            self.show_unsubscribed_dialog(account, contact)
            return

538
        self.add_event(account, obj.jid, 'unsubscribed', contact)
539 540 541 542

        if helpers.allow_showing_notification(account):
            path = gtkgui_helpers.get_icon_path('gajim-unsubscribed', 48)
            event_type = _('Unsubscribed')
543 544
            notify.popup(event_type, obj.jid, account, 'unsubscribed', path,
                event_type, obj.jid)
545

546
    def handle_event_register_agent_info(self, obj):
547 548
        # ('REGISTER_AGENT_INFO', account, (agent, infos, is_form))
        # info in a dataform if is_form is True
549 550 551
        if obj.is_form or 'instructions' in obj.config:
            config.ServiceRegistrationWindow(obj.agent, obj.config,
                obj.conn.name, obj.is_form)
552
        else:
553 554
            dialogs.ErrorDialog(_('Contact with "%s" cannot be established') % \
                obj.agent, _('Check your connection or try again later.'))
555

556
    def handle_event_vcard(self, obj):
557 558
        # ('VCARD', account, data)
        '''vcard holds the vcard data'''
559 560 561 562 563 564
        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)
565

566
    def handle_event_last_status_time(self, obj):
567
        # ('LAST_STATUS_TIME', account, (jid, resource, seconds, status))
568
        if obj.seconds < 0:
569 570
            # Ann error occured
            return
571 572
        account = obj.conn.name
        c = gajim.contacts.get_contact(account, obj.jid, obj.resource)
573
        if c: # c can be none if it's a gc contact
574 575
            if obj.status:
                c.status = obj.status
576
                self.roster.draw_contact(c.jid, account) # draw offline status
577
            last_time = time.localtime(time.time() - obj.seconds)
578 579 580 581
            if c.show == 'offline':
                c.last_status_time = last_time
            else:
                c.last_activity_time = last_time
582 583
            if self.roster.tooltip.id and self.roster.tooltip.win:
                self.roster.tooltip.update_last_time(last_time)
584

585 586 587 588 589
    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]:
590
                # We're converting chat to muc. allow participants to invite
591
                for f in obj.dataform.iter_fields():
592 593 594 595 596 597 598 599
                    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
600
                obj.conn.send_gc_config(obj.jid, obj.dataform.get_purged())
601 602
            else:
                # use default configuration
603
                obj.conn.send_gc_config(obj.jid, obj.form_node)
604 605 606
            # invite contacts
            # check if it is necessary to add <continue />
            continue_tag = False
607
            if 'continue_tag' in gajim.automatic_rooms[account][obj.jid]:
608
                continue_tag = True
609 610
            if 'invities' in gajim.automatic_rooms[account][obj.jid]:
                for jid in gajim.automatic_rooms[account][obj.jid]['invities']:
611
                    obj.conn.send_invite(obj.jid, jid,
612 613 614 615 616
                        continue_tag=continue_tag)
            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)
617

618
    def handle_event_gc_affiliation(self, obj):
619
        #('GC_AFFILIATION', account, (room_jid, users_dict))
620 621 622 623
        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)
624

625
    def handle_event_gc_invitation(self, obj):
626
        #('GC_INVITATION', (room_jid, jid_from, reason, password, is_continued))
627 628
        jid = gajim.get_jid_without_resource(obj.jid_from)
        account = obj.conn.name
629
        if helpers.allow_popup_window(account) or not self.systray_enabled:
630 631
            dialogs.InvitationReceivedDialog(account, obj.room_jid, jid,
                obj.password, obj.reason, is_continued=obj.is_continued)
632 633
            return

634 635
        self.add_event(account, jid, 'gc-invitation', (obj.room_jid,
            obj.reason, obj.password, obj.is_continued))
636 637 638 639 640

        if helpers.allow_showing_notification(account):
            path = gtkgui_helpers.get_icon_path('gajim-gc_invitation', 48)
            event_type = _('Groupchat Invitation')
            notify.popup(event_type, jid, account, 'gc-invitation', path,
641
                event_type, obj.room_jid)
642 643 644 645 646 647

    def forget_gpg_passphrase(self, keyid):
        if keyid in self.gpg_passphrase:
            del self.gpg_passphrase[keyid]
        return False

648
    def handle_event_bad_gpg_passphrase(self, obj):
649
        #('BAD_PASSPHRASE', account, ())
650
        if obj.use_gpg_agent:
651
            sectext = _('You configured Gajim to use GPG agent, but there is no'
652
                ' GPG agent running or it returned a wrong passphrase.\n')
653 654
            sectext += _('You are currently connected without your OpenPGP '
                'key.')
655 656 657
            dialogs.WarningDialog(_('Your passphrase is incorrect'), sectext)
        else:
            path = gtkgui_helpers.get_icon_path('gajim-warning', 48)
658
            account = obj.conn.name
659
            notify.popup('warning', account, account, 'warning', path,
660 661 662
                _('OpenGPG Passphrase Incorrect'),
                _('You are currently connected without your OpenPGP key.'))
        self.forget_gpg_passphrase(obj.keyID)
663

664
    def handle_event_gpg_password_required(self, obj):
665
        #('GPG_PASSWORD_REQUIRED', account, (callback,))
666 667
        if obj.keyid in self.gpg_passphrase:
            request = self.gpg_passphrase[obj.keyid]
668
        else:
669 670 671
            request = PassphraseRequest(obj.keyid)
            self.gpg_passphrase[obj.keyid] = request
        request.add_callback(obj.conn.name, obj.callback)
672

673
    def handle_event_gpg_trust_key(self, obj):
674 675 676
        #('GPG_ALWAYS_TRUST', account, callback)
        def on_yes(checked):
            if checked:
677 678
                obj.conn.gpg.always_trust = True
            obj.callback(True)
679 680

        def on_no():
681
            obj.callback(False)
682 683

        dialogs.YesNoDialog(_('GPG key not trusted'), _('The GPG key used to '
684
            'encrypt this chat is not trusted. Do you really want to encrypt '
685
            'this message?'), checktext=_('_Do not ask me again'),
686
            on_response_yes=on_yes, on_response_no=on_no)
687

688
    def handle_event_password_required(self, obj):
689
        #('PASSWORD_REQUIRED', account, None)
690
        account = obj.conn.name
691 692 693 694 695 696
        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 \
697 698
                correctly started (environment variable probably not \
                correctly set)')
699 700 701 702 703

        def on_ok(passphrase, save):
            if save:
                gajim.config.set_per('accounts', account, 'savepass', True)
                passwords.save_password(account, passphrase)
704
            obj.conn.set_password(passphrase)
705 706 707 708 709 710 711 712
            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(
713 714
            _('Password Required'), text, _('Save password'), ok_handler=on_ok,
            cancel_handler=on_cancel)
715

716
    def handle_event_roster_info(self, obj):
717
        #('ROSTER_INFO', account, (jid, name, sub, ask, groups))
718 719 720 721 722
        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:
723 724
            # contact removed us.
            if contacts:
725
                self.roster.remove_contact(obj.jid, account, backend=True)
726 727
                return
        elif not contacts:
728
            if obj.sub == 'remove':
729 730
                return
            # Add new contact to roster
731 732 733
            contact = gajim.contacts.create_contact(jid=obj.jid,
                account=account, name=obj.nickname, groups=obj.groups,
                show='offline', sub=obj.sub, ask=obj.ask)
734
            gajim.contacts.add_contact(account, contact)
735
            self.roster.add_contact(obj.jid, account)
736 737 738
        else:
            # If contact has changed (sub, ask or group) update roster
            # Mind about observer status changes:
739 740
            #   According to xep 0162, a contact is not an observer anymore when
            #   we asked for auth, so also remove him if ask changed
741
            old_groups = contacts[0].groups
742 743 744 745
            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
746 747
            if contacts[0].sub != obj.sub or contacts[0].ask != obj.ask\
            or old_groups != obj.groups:
748 749
                # c.get_shown_groups() has changed. Reflect that in
                # roster_winodow
750
                self.roster.remove_contact(obj.jid, account, force=True)
751
            for contact in contacts:
752 753 754 755 756
                contact.name = obj.nickname or ''
                contact.sub = obj.sub
                contact.ask = obj.ask
                contact.groups = obj.groups or []
            self.roster.add_contact(obj.jid, account)
757 758 759
            # Refilter and update old groups
            for group in old_groups:
                self.roster.draw_group(group, account)
760
            self.roster.draw_contact(obj.jid, account)
761

762
    def handle_event_bookmarks(self, obj):
763 764 765 766 767 768 769
        # ('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
770
        if obj.conn.connected == invisible_show:
771 772
            return

773
        self.auto_join_bookmarks(obj.conn.name)
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790

    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):
            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,
791
                event_type, file_props['name'])
792

793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
    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

821 822 823 824
        command = gajim.config.get('notify_on_new_gmail_email_command')
        if command:
            Popen(command, shell=True)

825 826 827 828
        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)
829

830
    def handle_event_file_request_error(self, obj):
831 832
        # ('FILE_REQUEST_ERROR', account, (jid, file_props, error_msg))
        ft = self.instances['file_transfers']
833 834
        ft.set_status(obj.file_props['type'], obj.file_props['sid'], 'stop')
        errno = obj.file_props['error']
835

836
        if helpers.allow_popup_window(obj.conn.name):
837
            if errno in (-4, -5):
838
                ft.show_stopped(obj.jid, obj.file_props, obj.error_msg)
839
            else:
840
                ft.show_request_error(obj.file_props)
841 842 843 844 845 846 847
            return

        if errno in (-4, -5):
            msg_type = 'file-error'
        else:
            msg_type = 'file-request-error'

848
        self.add_event(obj.conn.name, obj.jid, msg_type, obj.file_props)
849

850
        if helpers.allow_showing_notification(obj.conn.name):
851 852 853
            # check if we should be notified
            path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48)
            event_type = _('File Transfer Error')
854 855
            notify.popup(event_type, obj.jid, obj.conn.name, msg_type, path,
                title=event_type, text=obj.file_props['name'])
856

857 858 859
    def handle_event_file_request(self, obj):
        account = obj.conn.name
        if obj.jid not in gajim.contacts.get_jid_list(account):
860 861
            keyID = ''
            attached_keys = gajim.config.get_per('accounts', account,
862 863 864 865 866
                '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)
867
            gajim.contacts.add_contact(account, contact)
868 869
            self.roster.add_contact(obj.jid, account)
        contact = gajim.contacts.get_first_contact_from_jid(account, obj.jid)
870 871 872

        if helpers.allow_popup_window(account):
            self.instances['file_transfers'].show_file_request(account, contact,
873
                obj.file_props)
874 875
            return

876
        self.add_event(account, obj.jid, 'file-request', obj.file_props)
877 878 879 880

        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(
881
                account, obj.jid)
882
            event_type = _('File Transfer Request')
883 884
            notify.popup(event_type, obj.jid, account, 'file-request',
                path_to_image=path, title=event_type, text=txt)
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918

    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 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'])
        else:
            ft.set_status(file_props['type'], file_props['sid'], 'stop')
        if 'stalled' in file_props and file_props['stalled'] or \
                'paused' in file_props and file_props['paused']:
            return
        if file_props['type'] == 'r': # we receive a file
            jid = unicode(file_props['sender'])
        else: # we send a file
            jid = unicode(file_props['receiver'])

        if helpers.allow_popup_window(account):
            if file_props['error'] == 0:
                if gajim.config.get('notify_on_file_complete'):
                    ft.show_completed(jid, file_props)
            elif file_props['error'] == -1:
                ft.show_stopped(jid, file_props,
                        error_msg=_('Remote contact stopped transfer'))
            elif file_props['error'] == -6:
919 920
                ft.show_stopped(jid, file_props,
                    error_msg=_('Error opening file'))
921 922 923 924 925 926 927 928 929 930 931 932 933 934
            return

        msg_type = ''
        event_type = ''
        if file_props['error'] == 0 and gajim.config.get(
        'notify_on_file_complete'):
            msg_type = 'file-completed'
            event_type = _('File Transfer Completed')
        elif file_props['error'] in (-1, -6):
            msg_type = 'file-stopped'
            event_type = _('File Transfer Stopped')

        if event_type == '':
            # FIXME: ugly workaround (this can happen Gajim sent, Gaim recvs)
935 936 937 938
            # this should never happen but it does. see process_result() in
            # socks5.py
            # who calls this func (sth is really wrong unless this func is also
            # registered as progress_cb
939 940 941 942 943 944 945 946 947 948 949 950 951
            return

        if msg_type:
            self.add_event(account, jid, msg_type, file_props)

        if file_props is not None:
            if file_props['type'] == 'r':
                # get the name of the sender, as it is in the roster
                sender = unicode(file_props['sender']).split('/')[0]
                name = gajim.contacts.get_first_contact_from_jid(account,
                        sender).get_shown_name()
                filename = os.path.basename(file_props['file-name'])
                if event_type == _('File Transfer Completed'):
952 953
                    txt = _('You successfully received %(filename)s from '
                        '%(name)s.') % {'filename': filename, 'name': name}
954 955
                    img_name = 'gajim-ft_done'
                else: # ft stopped
956 957
                    txt = _('File transfer of %(filename)s from %(name)s '
                        'stopped.') % {'filename': filename, 'name': name}
958 959 960 961 962 963 964 965 966 967 968 969 970 971 972
                    img_name = 'gajim-ft_stopped'
            else:
                receiver = file_props['receiver']
                if hasattr(receiver, 'jid'):
                    receiver = receiver.jid
                receiver = receiver.split('/')[0]
                # get the name of the contact, as it is in the roster
                name = gajim.contacts.get_first_contact_from_jid(account,
                        receiver).get_shown_name()
                filename = os.path.basename(file_props['file-name'])
                if event_type == _('File Transfer Completed'):
                    txt = _('You successfully sent %(filename)s to %(name)s.')\
                            % {'filename': filename, 'name': name}
                    img_name = 'gajim-ft_done'
                else: # ft stopped
973 974
                    txt = _('File transfer of %(filename)s to %(name)s '
                        'stopped.') % {'filename': filename, 'name': name}
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
                    img_name = 'gajim-ft_stopped'
            path = gtkgui_helpers.get_icon_path(img_name, 48)
        else:
            txt = ''
            path = ''

        if gajim.config.get('notify_on_file_complete') and \
                (gajim.config.get('autopopupaway') or \
                gajim.connections[account].connected in (2, 3)):
            # we want to be notified and we are online/chat or we don't mind
            # bugged when away/na/busy
            notify.popup(event_type, jid, account, msg_type, path_to_image=path,
                    title=event_type, text=txt)

    def ask_offline_status(self, account):
        for contact in gajim.contacts.iter_contacts(account):
            gajim.connections[account].request_last_status_time(contact.jid,
992
                contact.resource)
993

994
    def handle_event_signed_in(self, obj):
995 996 997 998 999
        """
        SIGNED_IN event is emitted when we sign in, so handle it
        """
        # ('SIGNED_IN', account, ())
        # block signed in notifications for 30 seconds
1000
        account = obj.conn.name
1001 1002
        gajim.block_signed_in_notifications[account] = True
        state = self.sleeper.getState()
1003
        connected = obj.conn.connected
1004 1005 1006 1007 1008 1009 1010 1011
        if gajim.config.get('ask_offline_status_on_connection'):
            # Ask offline status in 1 minute so w'are sure we got all online
            # presences
            gobject.timeout_add_seconds(60, self.ask_offline_status, account)
        if state != common.sleepy.STATE_UNKNOWN and connected in (2, 3):
            # we go online or free for chat, so we activate auto status
            gajim.sleeper_state[account] = 'online'
        elif not ((state == common.sleepy.STATE_AWAY and connected == 4) or \
1012
        (state == common.sleepy.STATE_XA and connected == 5)):
1013 1014
            # If we are autoaway/xa and come back after a disconnection, do
            # nothing
1015 1016
            # Else disable autoaway
            gajim.sleeper_state[account] = 'off'
1017 1018 1019 1020 1021 1022 1023 1024

        if obj.conn.archiving_supported:
            # Start merging logs from server
            obj.conn.request_modifications_page(gajim.config.get_per('accounts',
                account, 'last_archiving_time'))
            gajim.config.set_per('accounts', account, 'last_archiving_time',
                time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()))

1025 1026
        invisible_show = gajim.SHOW_LIST.index('invisible')
        # We cannot join rooms if we are invisible
1027
        if connected == invisible_show:
1028 1029
            return
        # send currently played music
1030 1031
        if obj.conn.pep_supported and dbus_support.supported and \
        gajim.config.get_per('accounts', account, 'publish_tune'):
1032 1033
            self.enable_music_listener()
        # enable location listener
1034 1035
        if obj.conn.pep_supported and dbus_support.supported and \
        gajim.config.get_per('accounts', account, 'publish_location'):
1036
            location_listener.enable()
1037

1038

1039 1040
    def handle_event_metacontacts(self, obj):
        gajim.contacts.define_metacontacts(obj.conn.name, obj.meta_list)
1041

1042 1043
    def handle_atom_entry(self, obj):
        AtomWindow.newAtomEntry(obj.atom_entry)
1044

1045 1046 1047 1048
    def handle_event_failed_decrypt(self, obj):
        details = _('Unable to decrypt message from %s\nIt may have been '
            'tampered with.') % obj.fjid
        dialogs.WarningDialog(_('Unable to decrypt message'), details)
1049

1050
    def handle_event_zc_name_conflict(self, obj):
1051
        def on_ok(new_name):
Dicson's avatar
Dicson committed
1052
            gajim.config.set_per('accounts', obj.conn.name, 'name', new_name)
1053 1054 1055 1056
            show = obj.conn.old_show
            status = obj.conn.status
            obj.conn.username = new_name
            obj.conn.change_status(show, status)
1057
        def on_cancel():
1058
            obj.conn.change_status('offline', '')
1059 1060

        dlg = dialogs.InputDialog(_('Username Conflict'),
1061
            _('Please type a new username for your local account'),
1062
            input_str=obj.alt_name, is_modal=True, ok_handler=on_ok,
1063
            cancel_handler=on_cancel)
1064

1065
    def handle_event_resource_conflict(self, obj):
1066 1067
        # ('RESOURCE_CONFLICT', account, ())
        # First we go offline, but we don't overwrite status message
1068 1069 1070
        account = obj.conn.name
        conn = obj.conn
        self.roster.send_status(account, 'offline', conn.status)
1071 1072
        def on_ok(new_resource):
            gajim.config.set_per('accounts', account, 'resource', new_resource)
1073 1074
            self.roster.send_status(account, conn.old_show, conn.status)
        proposed_resource = conn.server_resource
1075 1076
        proposed_resource += gajim.config.get('gc_proposed_nick_char')
        dlg = dialogs.ResourceConflictDialog(_('Resource Conflict'),
1077 1078 1079
            _('You are already connected to this account with the same '
            'resource. Please type a new one'), resource=proposed_resource,
            ok_handler=on_ok)
1080

1081
    def handle_event_jingle_incoming(self, obj):
1082 1083 1084 1085
        # ('JINGLE_INCOMING', account, peer jid, sid, tuple-of-contents==(type,
        # data...))
        # TODO: conditional blocking if peer is not in roster

1086 1087
        account = obj.conn.name
        content_types = set(c[0] for c in obj.contents)
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098

        # check type of jingle session
        if 'audio' in content_types or 'video' in content_types:
            # a voip session...
            # we now handle only voip, so the only thing we will do here is
            # not to return from function
            pass
        else:
            # unknown session type... it should be declined in common/jingle.py
            return

1099 1100
        ctrl = (self.msg_win_mgr.get_control(obj.fjid, account)
            or self.msg_win_mgr.get_control(obj.jid, account))
1101 1102
        if ctrl:
            if 'audio' in content_types:
1103
                ctrl.set_audio_state('connection_received', obj.sid)
1104
            if 'video' in content_types:
1105
                ctrl.set_video_state('connection_received', obj.sid)
1106

1107
        dlg = dialogs.VoIPCallReceivedDialog.get_dialog(obj.fjid, obj.sid)
1108 1109 1110 1111 1112
        if dlg:
            dlg.add_contents(content_types)
            return

        if helpers.allow_popup_window(account):
1113
            dialogs.VoIPCallReceivedDialog(account, obj.fjid, obj.sid,
1114
                content_types)
1115 1116
            return

1117
        self.add_event(account, obj.jid, 'jingle-incoming', (obj.fjid, obj.sid,
1118 1119 1120 1121
                content_types))

        if helpers.allow_showing_notification(account):
            # TODO: we should use another pixmap ;-)
1122
            txt = _('%s wants to start a voice chat.') % \
1123
                gajim.get_name_from_jid(account, obj.fjid)
1124 1125
            path = gtkgui_helpers.get_icon_path('gajim-mic_active', 48)
            event_type = _('Voice Chat Request')
1126
            notify.popup(event_type, obj.fjid, account, 'jingle-incoming',
1127
                    path_to_image=path, title=event_type, text=txt)
1128

1129
    def handle_event_jingle_connected(self, obj):
1130
        # ('JINGLE_CONNECTED', account, (peerjid, sid, media))
1131 1132 1133 1134
        if obj.media in ('audio', 'video'):
            account = obj.conn.name
            ctrl = (self.msg_win_mgr.get_control(obj.fjid, account)
                or self.msg_win_mgr.get_control(obj.jid, account))
1135
            if ctrl:
1136 1137
                if obj.media == 'audio':
                    ctrl.set_audio_state('connected', obj.sid)
1138
                else:
1139
                    ctrl.set_video_state('connected', obj.sid)
1140

1141
    def handle_event_jingle_disconnected(self, obj):
1142
        # ('JINGLE_DISCONNECTED', account, (peerjid, sid, reason))
1143 1144 1145
        account = obj.conn.name
        ctrl = (self.msg_win_mgr.get_control(obj.fjid, account)
            or self.msg_win_mgr.get_control(obj.jid, account))
1146
        if ctrl:
1147 1148 1149 1150 1151 1152 1153
            if obj.media is None:
                ctrl.stop_jingle(sid=obj.sid, reason=obj.reason)
            elif obj.media == 'audio':
                ctrl.set_audio_state('stop', sid=obj.sid, reason=obj.reason)
            elif obj.media == 'video':
                ctrl.set_video_state('stop', sid=obj.sid, reason=obj.reason)
        dialog = dialogs.VoIPCallReceivedDialog.get_dialog(obj.fjid, obj.sid)
1154
        if dialog:
1155
            if obj.media is None:
1156 1157
                dialog.dialog.destroy()
            else:
1158
                dialog.remove_contents((obj.media, ))
1159

1160
    def handle_event_jingle_error(self, obj):
1161
        # ('JINGLE_ERROR', account, (peerjid, sid, reason))
1162 1163 1164
        account = obj.conn.name
        ctrl = (self.msg_win_mgr.get_control(obj.fjid, account)
            or self.msg_win_mgr.get_control(obj.jid, account))
1165
        if ctrl:
1166
            ctrl.set_audio_state('error', reason=obj.reason)
1167

1168
    def handle_event_roster_item_exchange(self, obj):
1169
        # data = (action in [add, delete, modify], exchange_list, jid_from)
1170 1171
        dialogs.RosterItemExchangeWindow(obj.conn.name, obj.action,
            obj.exchange_items_list, obj.fjid)
1172

1173
    def handle_event_ssl_error(self, obj):
1174
        # ('SSL_ERROR', account, (text, errnum, cert, sha1_fingerprint))
1175
        account = obj.conn.name
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
        server = gajim.config.get_per('accounts', account, 'hostname')

        def on_ok(is_checked):
            del self.instances[account]['online_dialog']['ssl_error']
            if is_checked[0]:
                # Check if cert is already in file
                certs = ''
                if os.path.isfile(gajim.MY_CACERTS):
                    f = open(gajim.MY_CACERTS)
                    certs = f.read()
                    f.close()
1187
                if obj.cert in certs:
1188
                    dialogs.ErrorDialog(_('Certificate Already in File'),
1189 1190
                        _('This certificate is already in file %s, so it\'s '
                        'not added again.') % gajim.MY_CACERTS)
1191 1192 1193
                else:
                    f = open(gajim.MY_CACERTS, 'a')
                    f.write(server + '\n')
1194
                    f.write(obj.cert + '\n\n')
1195
                    f.close()
1196
                gajim.config.set_per('accounts', account,
1197
                    'ssl_fingerprint_sha1', obj.fingerprint)
1198 1199
            if is_checked[1]:
                ignore_ssl_errors = gajim.config.get_per('accounts', account,
1200 1201
                    'ignore_ssl_errors').split()
                ignore_ssl_errors.append(str(obj.error_num))
1202
                gajim.config.set_per('accounts', account, 'ignore_ssl_errors',
1203 1204
                    ' '.join(ignore_ssl_errors))
            obj.conn.ssl_certificate_accepted()
1205 1206 1207

        def on_cancel():
            del self.instances[account]['online_dialog']['ssl_error']
1208 1209
            obj.conn.disconnect(on_purpose=True)
            gajim.nec.push_incoming_event(OurShowEvent(None, conn=obj.conn,
Yann Leboulanger's avatar
Yann Leboulanger committed
1210
                show='offline'))
1211 1212

        pritext = _('Error verifying SSL certificate')
1213 1214
        sectext = _('There was an error verifying the SSL certificate of your '
            'jabber server: %(error)s\nDo you still want to connect to this '
1215 1216
            'server?') % {'error': obj.error_text}
        if obj.error_num in (18, 27):
1217
            checktext1 = _('Add this certificate to the list of trusted '
1218 1219
            'certificates.\nSHA1 fingerprint of the certificate:\n%s') % \
            obj.fingerprint
1220 1221 1222 1223 1224 1225
        else:
            checktext1 = ''
        checktext2 = _('Ignore this error for this certificate.')
        if 'ssl_error' in self.instances[account]['online_dialog']:
            self.instances[account]['online_dialog']['ssl_error'].destroy()
        self.instances[account]['online_dialog']['ssl_error'] = \
1226 1227 1228
            dialogs.SSLErrorDialog(obj.conn.name, obj.certificate, pritext,
            sectext, checktext1, checktext2, on_response_ok=on_ok,
            on_response_cancel=on_cancel)
1229

1230
    def handle_event_fingerprint_error(self, obj):
1231
        # ('FINGERPRINT_ERROR', account, (new_fingerprint,))
1232
        account = obj.conn.name
1233 1234 1235
        def on_yes(is_checked):
            del self.instances[account]['online_dialog']['fingerprint_error']
            gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1',
1236
                obj.new_fingerprint)
1237 1238
            # Reset the ignored ssl errors
            gajim.config.set_per('accounts', account, 'ignore_ssl_errors', '')
1239 1240
            obj.conn.ssl_certificate_accepted()

1241 1242
        def on_no():
            del self.instances[account]['online_dialog']['fingerprint_error']
1243
            obj.conn.disconnect(on_purpose=True)
1244
            gajim.nec.push_incoming_event(OurShowEvent(None, conn=obj.conn,
Yann Leboulanger's avatar
Yann Leboulanger committed
1245
                show='offline'))
1246

1247 1248
        pritext = _('SSL certificate error')
        sectext = _('It seems the SSL certificate of account %(account)s has '
1249 1250 1251 1252
            'changed or your connection is being hacked.\nOld fingerprint: '
            '%(old)s\nNew fingerprint: %(new)s\n\nDo you still want to connect '
            'and update the fingerprint of the certificate?') % \
            {'account': account, 'old': gajim.config.get_per('accounts',
Yann Leboulanger's avatar