connection_handlers_events.py 45.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# Copyright (C) 2010-2014 Yann Leboulanger <asterix AT lagaule.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/>.
Tanguy Ortolo's avatar
Tanguy Ortolo committed
16

17 18 19 20 21
# pylint: disable=no-init
# pylint: disable=attribute-defined-outside-init

import logging
from time import time as time_time
Tanguy Ortolo's avatar
Tanguy Ortolo committed
22

23
import OpenSSL.crypto
24
import nbxmpp
25

26 27 28 29
from gajim.common import nec
from gajim.common import helpers
from gajim.common import app
from gajim.common import i18n
30
from gajim.common.i18n import _
31
from gajim.common.modules import dataforms
Martin's avatar
Martin committed
32 33
from gajim.common.modules.misc import parse_idle
from gajim.common.modules.misc import parse_delay
34
from gajim.common.const import KindConstant, SSLError
35 36 37 38
from gajim.common.pep import SUPPORTED_PERSONAL_USER_EVENTS
from gajim.common.jingle_transport import JingleTransportSocks5
from gajim.common.file_props import FilesProp

39

Tanguy Ortolo's avatar
Tanguy Ortolo committed
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
log = logging.getLogger('gajim.c.connection_handlers_events')

CONDITION_TO_CODE = {
    'realjid-public': 100,
    'affiliation-changed': 101,
    'unavailable-shown': 102,
    'unavailable-not-shown': 103,
    'configuration-changed': 104,
    'self-presence': 110,
    'logging-enabled': 170,
    'logging-disabled': 171,
    'non-anonymous': 172,
    'semi-anonymous': 173,
    'fully-anonymous': 174,
    'room-created': 201,
    'nick-assigned': 210,
    'banned': 301,
    'new-nick': 303,
    'kicked': 307,
    'removed-affiliation': 321,
    'removed-membership': 322,
    'removed-shutdown': 332,
}

class HelperEvent:
    def get_jid_resource(self, check_fake_jid=False):
        if check_fake_jid and hasattr(self, 'id_') and \
67
                self.id_ in self.conn.groupchat_jids:
Tanguy Ortolo's avatar
Tanguy Ortolo committed
68 69 70 71
            self.fjid = self.conn.groupchat_jids[self.id_]
            del self.conn.groupchat_jids[self.id_]
        else:
            self.fjid = helpers.get_full_jid_from_iq(self.stanza)
72 73 74 75
        if self.fjid is None:
            self.jid = None
        else:
            self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
76 77 78 79 80

    def get_id(self):
        self.id_ = self.stanza.getID()

    def get_gc_control(self):
81 82
        self.gc_control = app.interface.msg_win_mgr.get_gc_control(self.jid,
                                                                     self.conn.name)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
83 84 85 86 87

        # If gc_control is missing - it may be minimized. Try to get it
        # from there. If it's not there - then it's missing anyway and
        # will remain set to None.
        if not self.gc_control:
88
            minimized = app.interface.minimized_controls[self.conn.name]
Tanguy Ortolo's avatar
Tanguy Ortolo committed
89 90
            self.gc_control = minimized.get(self.jid)

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
    def get_oob_data(self, stanza):
        oob_node = stanza.getTag('x', namespace=nbxmpp.NS_X_OOB)
        if oob_node is not None:
            if 'gajim' not in self.additional_data:
                self.additional_data['gajim'] = {}
            oob_url = oob_node.getTagData('url')
            if oob_url is not None:
                self.additional_data['gajim']['oob_url'] = oob_url
            oob_desc = oob_node.getTagData('desc')
            if oob_desc is not None:
                self.additional_data['gajim']['oob_desc'] = oob_desc

    def get_stanza_id(self, stanza, query=False):
        if query:
            # On a MAM query the stanza-id is maybe not set, so
            # get the id of the stanza
            return stanza.getAttr('id')
        stanza_id, by = stanza.getStanzaIDAttrs()
        if by is None:
            # We can not verify who set this stanza-id, ignore it.
            return
        if stanza.getType() == 'groupchat':
            if stanza.getFrom().bareMatch(by):
                # by attribute must match the server
                return stanza_id
        elif self.conn.get_own_jid().bareMatch(by):
            # by attribute must match the server
            return stanza_id
        return

    @staticmethod
    def get_forwarded_message(stanza):
        forwarded = stanza.getTag('forwarded',
                                  namespace=nbxmpp.NS_FORWARD,
                                  protocol=True)
        if forwarded is not None:
            return forwarded.getTag('message', protocol=True)

Martin's avatar
Martin committed
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
    def _is_self_message(self, message):
        if self.self_message is not None:
            return self.self_message
        own_jid = self.conn.get_own_jid()
        frm = message.getFrom()
        to = message.getTo()
        # If 'to' is not set we assume own jid
        self.self_message = frm.bareMatch(to or own_jid)
        return self.self_message

    def _is_muc_pm(self, message):
        if self.muc_pm is not None:
            return self.muc_pm
        self.muc_pm = False
        muc_user = message.getTag('x', namespace=nbxmpp.NS_MUC_USER)
        if muc_user is not None:
            self.muc_pm = muc_user.getChildren() == []
        return self.muc_pm

Tanguy Ortolo's avatar
Tanguy Ortolo committed
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    name = 'iq-error-received'

    def generate(self):
        self.get_id()
        self.get_jid_resource(check_fake_jid=True)
        self.errmsg = self.stanza.getErrorMsg()
        self.errcode = self.stanza.getErrorCode()
        return True

class StreamReceivedEvent(nec.NetworkIncomingEvent):
    name = 'stream-received'

class StreamConflictReceivedEvent(nec.NetworkIncomingEvent):
    name = 'stream-conflict-received'
    base_network_events = ['stream-received']

    def generate(self):
        if self.base_event.stanza.getTag('conflict'):
            self.conn = self.base_event.conn
            return True
169

Tanguy Ortolo's avatar
Tanguy Ortolo committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
class PresenceHelperEvent:
    def _generate_show(self):
        self.show = self.stanza.getShow()
        if self.show not in ('chat', 'away', 'xa', 'dnd'):
            self.show = '' # We ignore unknown show
        if not self.ptype and not self.show:
            self.show = 'online'
        elif self.ptype == 'unavailable':
            self.show = 'offline'

    def _generate_ptype(self):
        self.ptype = self.stanza.getType()
        if self.ptype == 'available':
            self.ptype = None
        rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed',
            'unsubscribe', 'unsubscribed')
        if self.ptype and not self.ptype in rfc_types:
            self.ptype = None

class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent,
PresenceHelperEvent):
    name = 'presence-received'
    base_network_events = ['raw-pres-received']

    def _generate_keyID(self, sig_tag):
        self.keyID = ''
        if sig_tag and self.conn.USE_GPG and self.ptype != 'error':
            # error presences contain our own signature
            # verify
            sig_msg = sig_tag.getData()
            self.keyID = self.conn.gpg.verify(self.status, sig_msg)
            self.keyID = helpers.prepare_and_validate_gpg_keyID(self.conn.name,
202 203
                                                                self.jid,
                                                                self.keyID)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228

    def _generate_prio(self):
        self.prio = self.stanza.getPriority()
        try:
            self.prio = int(self.prio)
        except Exception:
            self.prio = 0

    def generate(self):
        self.conn = self.base_event.conn
        self.stanza = self.base_event.stanza

        self.need_add_in_roster = False
        self.need_redraw = False

        self.popup = False # Do we want to open chat window ?

        if not self.conn or self.conn.connected < 2:
            log.debug('account is no more connected')
            return

        self._generate_ptype()
        try:
            self.get_jid_resource()
        except Exception:
229
            log.warning('Invalid JID: %s, ignoring it', self.stanza.getFrom())
Tanguy Ortolo's avatar
Tanguy Ortolo committed
230
            return
231
        jid_list = app.contacts.get_jid_list(self.conn.name)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
232 233 234 235 236 237 238 239 240
        self.timestamp = None
        self.get_id()
        self.is_gc = False # is it a GC presence ?
        sig_tag = None
        self.avatar_sha = None
        # XEP-0172 User Nickname
        self.user_nick = self.stanza.getTagData('nick') or ''
        self.contact_nickname = None
        self.transport_auto_auth = False
Martin's avatar
Martin committed
241

Tanguy Ortolo's avatar
Tanguy Ortolo committed
242
        # XEP-0203
Martin's avatar
Martin committed
243 244 245 246
        self.timestamp = parse_delay(self.stanza)
        if self.timestamp is None:
            self.timestamp = time_time()

247
        # XEP-0319
Martin's avatar
Martin committed
248
        self.idle_time = parse_idle(self.stanza)
249

Tanguy Ortolo's avatar
Tanguy Ortolo committed
250 251 252
        xtags = self.stanza.getTags('x')
        for x in xtags:
            namespace = x.getNamespace()
253
            if namespace in (nbxmpp.NS_MUC_USER, nbxmpp.NS_MUC):
Tanguy Ortolo's avatar
Tanguy Ortolo committed
254
                self.is_gc = True
255
            elif namespace == nbxmpp.NS_SIGNED:
Tanguy Ortolo's avatar
Tanguy Ortolo committed
256 257 258 259 260 261 262 263 264 265 266
                sig_tag = x

        self.status = self.stanza.getStatus() or ''
        self._generate_show()
        self._generate_prio()
        self._generate_keyID(sig_tag)

        self.errcode = self.stanza.getErrorCode()
        self.errmsg = self.stanza.getErrorMsg()

        if self.is_gc:
267 268 269 270
            app.nec.push_incoming_event(
                GcPresenceReceivedEvent(
                    None, conn=self.conn, stanza=self.stanza,
                    presence_obj=self))
Tanguy Ortolo's avatar
Tanguy Ortolo committed
271 272
            return

273
        if self.ptype == 'error':
274
            return
Tanguy Ortolo's avatar
Tanguy Ortolo committed
275 276

        if not self.ptype or self.ptype == 'unavailable':
277 278
            our_jid = app.get_jid_from_account(self.conn.name)
            if self.jid == our_jid and self.resource == self.conn.server_resource:
Tanguy Ortolo's avatar
Tanguy Ortolo committed
279
                # We got our own presence
280 281
                app.nec.push_incoming_event(OurShowEvent(None, conn=self.conn,
                                                           show=self.show))
Tanguy Ortolo's avatar
Tanguy Ortolo committed
282 283 284 285 286 287 288
            elif self.jid in jid_list or self.jid == our_jid:
                return True

class ZeroconfPresenceReceivedEvent(nec.NetworkIncomingEvent):
    name = 'presence-received'

    def generate(self):
289
        self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
290 291 292
        self.resource = 'local'
        self.prio = 0
        self.keyID = None
293
        self.idle_time = None
Tanguy Ortolo's avatar
Tanguy Ortolo committed
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
        self.timestamp = 0
        self.contact_nickname = None
        self.avatar_sha = None
        self.need_add_in_roster = False
        self.need_redraw = False
        if self.show == 'offline':
            self.ptype = 'unavailable'
        else:
            self.ptype = None
        self.is_gc = False
        self.user_nick = ''
        self.transport_auto_auth = False
        self.errcode = None
        self.errmsg = ''
        self.popup = False # Do we want to open chat window ?
        return True

class GcPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    name = 'gc-presence-received'

    def generate(self):
        self.ptype = self.presence_obj.ptype
        self.fjid = self.presence_obj.fjid
        self.jid = self.presence_obj.jid
        self.room_jid = self.presence_obj.jid
        self.nick = self.presence_obj.resource
        self.show = self.presence_obj.show
        self.status = self.presence_obj.status
        self.avatar_sha = self.presence_obj.avatar_sha
        self.errcode = self.presence_obj.errcode
        self.errmsg = self.presence_obj.errmsg
        self.errcon = self.stanza.getError()
        self.get_gc_control()
327
        self.gc_contact = app.contacts.get_gc_contact(self.conn.name,
Tanguy Ortolo's avatar
Tanguy Ortolo committed
328 329 330 331 332 333 334
            self.room_jid, self.nick)

        if self.ptype == 'error':
            return True

        if self.ptype and self.ptype != 'unavailable':
            return
335 336
        if app.config.get('log_contact_status_changes') and \
        app.config.should_log(self.conn.name, self.room_jid):
Tanguy Ortolo's avatar
Tanguy Ortolo committed
337 338 339 340 341 342 343 344
            if self.gc_contact:
                jid = self.gc_contact.jid
            else:
                jid = self.stanza.getJid()
            st = self.status
            if jid:
                # we know real jid, save it in db
                st += ' (%s)' % jid
345 346 347 348 349 350 351 352 353 354 355 356
            show = app.logger.convert_show_values_to_db_api_values(self.show)
            if show is not None:
                fjid = nbxmpp.JID(self.fjid)
                app.logger.insert_into_logs(self.conn.name,
                                            fjid.getStripped(),
                                            time_time(),
                                            KindConstant.GCSTATUS,
                                            contact_name=fjid.getResource(),
                                            message=st,
                                            show=show)


Tanguy Ortolo's avatar
Tanguy Ortolo committed
357 358 359
        # NOTE: if it's a gc presence, don't ask vcard here.
        # We may ask it to real jid in gui part.
        self.status_code = []
360
        ns_muc_user_x = self.stanza.getTag('x', namespace=nbxmpp.NS_MUC_USER)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
        if ns_muc_user_x:
            destroy = ns_muc_user_x.getTag('destroy')
        else:
            destroy = None
        if ns_muc_user_x and destroy:
            # Room has been destroyed. see
            # http://www.xmpp.org/extensions/xep-0045.html#destroyroom
            self.reason = _('Room has been destroyed')
            r = destroy.getTagData('reason')
            if r:
                self.reason += ' (%s)' % r
            if destroy.getAttr('jid'):
                try:
                    jid = helpers.parse_jid(destroy.getAttr('jid'))
                    self.reason += '\n' + \
                        _('You can join this room instead: %s') % jid
                except helpers.InvalidFormat:
                    pass
            self.status_code = ['destroyed']
        else:
            self.reason = self.stanza.getReason()
            conditions = self.stanza.getStatusConditions()
            if conditions:
                self.status_code = []
                for condition in conditions:
                    if condition in CONDITION_TO_CODE:
                        self.status_code.append(CONDITION_TO_CODE[condition])
            else:
                self.status_code = self.stanza.getStatusCode()

391 392 393 394 395 396
        self.role = self.stanza.getRole()
        self.affiliation = self.stanza.getAffiliation()
        self.real_jid = self.stanza.getJid()
        self.actor = self.stanza.getActor()
        self.new_nick = self.stanza.getNewNick()
        return True
397

398 399
class OurShowEvent(nec.NetworkIncomingEvent):
    name = 'our-show'
400

401 402
class BeforeChangeShowEvent(nec.NetworkIncomingEvent):
    name = 'before-change-show'
Tanguy Ortolo's avatar
Tanguy Ortolo committed
403 404 405 406 407 408

class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
    name = 'gc-message-received'

    def generate(self):
        self.stanza = self.msg_obj.stanza
409 410 411 412 413 414
        if not hasattr(self.msg_obj, 'additional_data'):
            self.additional_data = {}
        else:
            self.additional_data = self.msg_obj.additional_data
        self.id_ = self.msg_obj.stanza.getID()
        self.unique_id = self.msg_obj.unique_id
Tanguy Ortolo's avatar
Tanguy Ortolo committed
415 416 417 418 419 420 421
        self.fjid = self.msg_obj.fjid
        self.msgtxt = self.msg_obj.msgtxt
        self.jid = self.msg_obj.jid
        self.room_jid = self.msg_obj.jid
        self.nickname = self.msg_obj.resource
        self.timestamp = self.msg_obj.timestamp
        self.xhtml_msgtxt = self.stanza.getXHTML()
422
        self.encrypted = self.msg_obj.encrypted
423
        self.correct_id = None # XEP-0308
424
        self.delayed = self.msg_obj.delayed
Tanguy Ortolo's avatar
Tanguy Ortolo committed
425

426
        if app.config.get('ignore_incoming_xhtml'):
Tanguy Ortolo's avatar
Tanguy Ortolo committed
427 428 429 430 431 432 433 434 435 436 437 438
            self.xhtml_msgtxt = None

        if self.msg_obj.resource:
            # message from someone
            self.nick = self.msg_obj.resource
        else:
            # message from server
            self.nick = ''

        self.subject = self.stanza.getSubject()

        if self.subject is not None:
439 440 441 442
            app.nec.push_incoming_event(
                nec.NetworkEvent('gc-subject-received',
                                 nickname=self.msg_obj.resource,
                                 **vars(self.msg_obj)))
Tanguy Ortolo's avatar
Tanguy Ortolo committed
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
            return

        conditions = self.stanza.getStatusConditions()
        if conditions:
            self.status_code = []
            for condition in conditions:
                if condition in CONDITION_TO_CODE:
                    self.status_code.append(CONDITION_TO_CODE[condition])
        else:
            self.status_code = self.stanza.getStatusCode()

        if not self.stanza.getTag('body'): # no <body>
            # It could be a config change. See
            # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
            if self.stanza.getTag('x'):
                if self.status_code != []:
459
                    app.nec.push_incoming_event(GcConfigChangedReceivedEvent(
Tanguy Ortolo's avatar
Tanguy Ortolo committed
460 461
                        None, conn=self.conn, msg_event=self))
            if self.msg_obj.form_node:
462 463
                # It could be a voice request. See
                # http://www.xmpp.org/extensions/xep-0045.html#voiceapprove
464
                from gajim.gtk.single_message import SingleMessageWindow
465 466 467 468 469
                SingleMessageWindow(
                    self.conn.name, self.fjid,
                    action='receive', from_whom=self.fjid,
                    subject='', message='', resource='', session=None,
                    form_node=self.msg_obj.form_node)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
470 471
            return

472 473
        from gajim.common.modules.security_labels import parse_securitylabel
        self.displaymarking = parse_securitylabel(self.stanza)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
474 475

        self.captcha_form = None
476
        captcha_tag = self.stanza.getTag('captcha', namespace=nbxmpp.NS_CAPTCHA)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
477
        if captcha_tag:
478 479
            self.captcha_form = captcha_tag.getTag('x',
                namespace=nbxmpp.NS_DATA)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
480 481 482 483 484 485 486 487
            for field in self.captcha_form.getTags('field'):
                for media in field.getTags('media'):
                    for uri in media.getTags('uri'):
                        uri_data = uri.getData()
                        if uri_data.startswith('cid:'):
                            uri_data = uri_data[4:]
                            found = False
                            for data in self.stanza.getTags('data',
488
                            namespace=nbxmpp.NS_BOB):
Tanguy Ortolo's avatar
Tanguy Ortolo committed
489 490 491 492 493 494 495 496 497
                                if data.getAttr('cid') == uri_data:
                                    uri.setData(data.getData())
                                    found = True
                            if not found:
                                self.conn.get_bob_data(uri_data, self.fjid,
                                    self.conn._dispatch_gc_msg_with_captcha,
                                    [self.stanza, self.msg_obj], 0)
                                return

498 499
        from gajim.common.modules.misc import parse_correction
        self.correct_id = parse_correction(self.stanza)
500

Tanguy Ortolo's avatar
Tanguy Ortolo committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
        return True

class GcConfigChangedReceivedEvent(nec.NetworkIncomingEvent):
    name = 'gc-config-changed-received'

    def generate(self):
        self.conn = self.msg_event.conn
        self.stanza = self.msg_event.stanza
        self.room_jid = self.msg_event.room_jid
        self.status_code = self.msg_event.status_code
        return True

class MessageSentEvent(nec.NetworkIncomingEvent):
    name = 'message-sent'

516 517
    def generate(self):
        if not self.automatic_message:
518
            self.conn.sent_message_ids.append(self.stanza_id)
519 520 521 522 523
            # only record the last 20000 message ids (should be about 1MB [36 byte per uuid]
            # and about 24 hours if you send out a message every 5 seconds)
            self.conn.sent_message_ids = self.conn.sent_message_ids[-20000:]
        return True

Tanguy Ortolo's avatar
Tanguy Ortolo committed
524 525 526
class MessageNotSentEvent(nec.NetworkIncomingEvent):
    name = 'message-not-sent'

527
class MessageErrorEvent(nec.NetworkIncomingEvent, HelperEvent):
Tanguy Ortolo's avatar
Tanguy Ortolo committed
528 529
    name = 'message-error'

530 531 532
    def init(self):
        self.zeroconf = False

533
    def generate(self):
534 535
        if self.zeroconf:
            return True
536 537 538 539 540 541 542
        self.get_id()
        #only alert for errors of explicitly sent messages (see https://trac.gajim.org/ticket/8222)
        if self.id_ in self.conn.sent_message_ids:
            self.conn.sent_message_ids.remove(self.id_)
            return True
        return False

Tanguy Ortolo's avatar
Tanguy Ortolo committed
543 544 545 546 547 548 549 550
class AnonymousAuthEvent(nec.NetworkIncomingEvent):
    name = 'anonymous-auth'

class JingleRequestReceivedEvent(nec.NetworkIncomingEvent):
    name = 'jingle-request-received'

    def generate(self):
        self.fjid = self.jingle_session.peerjid
551
        self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
552 553 554 555 556 557 558 559
        self.sid = self.jingle_session.sid
        return True

class JingleConnectedReceivedEvent(nec.NetworkIncomingEvent):
    name = 'jingle-connected-received'

    def generate(self):
        self.fjid = self.jingle_session.peerjid
560
        self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
561 562 563 564 565 566 567 568
        self.sid = self.jingle_session.sid
        return True

class JingleDisconnectedReceivedEvent(nec.NetworkIncomingEvent):
    name = 'jingle-disconnected-received'

    def generate(self):
        self.fjid = self.jingle_session.peerjid
569
        self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
570 571 572
        self.sid = self.jingle_session.sid
        return True

573 574 575 576 577
class JingleTransferCancelledEvent(nec.NetworkIncomingEvent):
    name = 'jingleFT-cancelled-received'

    def generate(self):
        self.fjid = self.jingle_session.peerjid
578
        self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
579 580 581
        self.sid = self.jingle_session.sid
        return True

Tanguy Ortolo's avatar
Tanguy Ortolo committed
582 583 584 585 586
class JingleErrorReceivedEvent(nec.NetworkIncomingEvent):
    name = 'jingle-error-received'

    def generate(self):
        self.fjid = self.jingle_session.peerjid
587
        self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
        self.sid = self.jingle_session.sid
        return True

class AccountCreatedEvent(nec.NetworkIncomingEvent):
    name = 'account-created'

class AccountNotCreatedEvent(nec.NetworkIncomingEvent):
    name = 'account-not-created'

class NewAccountConnectedEvent(nec.NetworkIncomingEvent):
    name = 'new-account-connected'

    def generate(self):
        try:
            self.errnum = self.conn.connection.Connection.ssl_errnum
        except AttributeError:
604
            self.errnum = 0 # we don't have an errnum
Tanguy Ortolo's avatar
Tanguy Ortolo committed
605
        self.ssl_msg = ''
606
        if self.errnum > 0:
607
            self.ssl_msg = SSLError.get(self.errnum,
608
                _('Unknown SSL error: %d') % self.errnum)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
609
        self.ssl_cert = ''
610 611
        self.ssl_fingerprint_sha1 = ''
        self.ssl_fingerprint_sha256 = ''
612 613 614
        if self.conn.connection.Connection.ssl_certificate:
            cert = self.conn.connection.Connection.ssl_certificate
            self.ssl_cert = OpenSSL.crypto.dump_certificate(
615 616 617
                OpenSSL.crypto.FILETYPE_PEM, cert).decode('utf-8')
            self.ssl_fingerprint_sha1 = cert.digest('sha1').decode('utf-8')
            self.ssl_fingerprint_sha256 = cert.digest('sha256').decode('utf-8')
Tanguy Ortolo's avatar
Tanguy Ortolo committed
618 619 620 621 622 623 624 625 626 627
        return True

class NewAccountNotConnectedEvent(nec.NetworkIncomingEvent):
    name = 'new-account-not-connected'

class ConnectionTypeEvent(nec.NetworkIncomingEvent):
    name = 'connection-type'

class StanzaReceivedEvent(nec.NetworkIncomingEvent):
    name = 'stanza-received'
628

629 630
    def init(self):
        self.additional_data = {}
631

632 633
    def generate(self):
        return True
Tanguy Ortolo's avatar
Tanguy Ortolo committed
634 635 636

class StanzaSentEvent(nec.NetworkIncomingEvent):
    name = 'stanza-sent'
637

638 639
    def init(self):
        self.additional_data = {}
Tanguy Ortolo's avatar
Tanguy Ortolo committed
640 641 642 643 644 645

class AgentRemovedEvent(nec.NetworkIncomingEvent):
    name = 'agent-removed'

    def generate(self):
        self.jid_list = []
646
        for jid in app.contacts.get_jid_list(self.conn.name):
Tanguy Ortolo's avatar
Tanguy Ortolo committed
647 648 649 650 651 652 653 654 655
            if jid.endswith('@' + self.agent):
                self.jid_list.append(jid)
        return True

class BadGPGPassphraseEvent(nec.NetworkIncomingEvent):
    name = 'bad-gpg-passphrase'

    def generate(self):
        self.account = self.conn.name
656 657
        self.use_gpg_agent = app.config.get('use_gpg_agent')
        self.keyID = app.config.get_per('accounts', self.conn.name, 'keyid')
Tanguy Ortolo's avatar
Tanguy Ortolo committed
658 659 660 661 662 663
        return True

class ConnectionLostEvent(nec.NetworkIncomingEvent):
    name = 'connection-lost'

    def generate(self):
664
        app.nec.push_incoming_event(OurShowEvent(None, conn=self.conn,
Tanguy Ortolo's avatar
Tanguy Ortolo committed
665 666 667 668 669 670 671 672 673 674
            show='offline'))
        return True

class GPGTrustKeyEvent(nec.NetworkIncomingEvent):
    name = 'gpg-trust-key'

class GPGPasswordRequiredEvent(nec.NetworkIncomingEvent):
    name = 'gpg-password-required'

    def generate(self):
675
        self.keyid = app.config.get_per('accounts', self.conn.name, 'keyid')
Tanguy Ortolo's avatar
Tanguy Ortolo committed
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
        return True

class PEPReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    name = 'pep-received'

    def generate(self):
        if not self.stanza.getTag('event'):
            return
        if self.stanza.getTag('error'):
            log.debug('PEPReceivedEvent received error stanza. Ignoring')
            return

        try:
            self.get_jid_resource()
        except Exception:
            return

        self.event_tag = self.stanza.getTag('event')

        for pep_class in SUPPORTED_PERSONAL_USER_EVENTS:
            pep = pep_class.get_tag_as_PEP(self.fjid, self.conn.name,
                self.event_tag)
            if pep:
699
                self.pep_type = pep.type_
Tanguy Ortolo's avatar
Tanguy Ortolo committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
                return True

class PlainConnectionEvent(nec.NetworkIncomingEvent):
    name = 'plain-connection'

class InsecurePasswordEvent(nec.NetworkIncomingEvent):
    name = 'insecure-password'

class InsecureSSLConnectionEvent(nec.NetworkIncomingEvent):
    name = 'insecure-ssl-connection'

class SSLErrorEvent(nec.NetworkIncomingEvent):
    name = 'ssl-error'

class UniqueRoomIdSupportedEvent(nec.NetworkIncomingEvent):
    name = 'unique-room-id-supported'

class UniqueRoomIdNotSupportedEvent(nec.NetworkIncomingEvent):
    name = 'unique-room-id-not-supported'

720 721 722
class NonAnonymousServerErrorEvent(nec.NetworkIncomingEvent):
    name = 'non-anonymous-server-error'

723 724 725 726 727 728 729 730 731 732
class UpdateGCAvatarEvent(nec.NetworkIncomingEvent):
    name = 'update-gc-avatar'

    def generate(self):
        return True

class UpdateRosterAvatarEvent(nec.NetworkIncomingEvent):
    name = 'update-roster-avatar'

    def generate(self):
Tanguy Ortolo's avatar
Tanguy Ortolo committed
733 734
        return True

735 736
class UpdateRoomAvatarEvent(nec.NetworkIncomingEvent):
    name = 'update-room-avatar'
Tanguy Ortolo's avatar
Tanguy Ortolo committed
737 738 739 740 741 742 743 744 745 746

    def generate(self):
        return True

class ZeroconfNameConflictEvent(nec.NetworkIncomingEvent):
    name = 'zeroconf-name-conflict'

class PasswordRequiredEvent(nec.NetworkIncomingEvent):
    name = 'password-required'

747 748 749
class Oauth2CredentialsRequiredEvent(nec.NetworkIncomingEvent):
    name = 'oauth2-credentials-required'

Tanguy Ortolo's avatar
Tanguy Ortolo committed
750 751 752 753 754 755
class SignedInEvent(nec.NetworkIncomingEvent):
    name = 'signed-in'

class FileRequestReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    name = 'file-request-received'

756 757 758 759
    def init(self):
        self.jingle_content = None
        self.FT_content = None

Tanguy Ortolo's avatar
Tanguy Ortolo committed
760 761 762
    def generate(self):
        self.get_id()
        self.fjid = self.conn._ft_get_from(self.stanza)
763
        self.jid = app.get_jid_without_resource(self.fjid)
764 765 766 767 768 769 770 771 772 773 774 775 776
        if self.jingle_content:
            secu = self.jingle_content.getTag('security')
            self.FT_content.use_security = bool(secu)
            if secu:
                fingerprint = secu.getTag('fingerprint')
                if fingerprint:
                    self.FT_content.x509_fingerprint = fingerprint.getData()
            if not self.FT_content.transport:
                self.FT_content.transport = JingleTransportSocks5()
                self.FT_content.transport.set_our_jid(
                    self.FT_content.session.ourjid)
                self.FT_content.transport.set_connection(
                    self.FT_content.session.connection)
777
            sid = self.stanza.getTag('jingle').getAttr('sid')
778 779 780 781 782 783 784 785 786 787 788 789
            self.file_props = FilesProp.getNewFileProp(self.conn.name, sid)
            self.file_props.transport_sid = self.FT_content.transport.sid
            self.FT_content.file_props = self.file_props
            self.FT_content.transport.set_file_props(self.file_props)
            self.file_props.streamhosts.extend(
                    self.FT_content.transport.remote_candidates)
            for host in self.file_props.streamhosts:
                host['initiator'] = self.FT_content.session.initiator
                host['target'] = self.FT_content.session.responder
            self.file_props.session_type = 'jingle'
            self.file_props.stream_methods = nbxmpp.NS_BYTESTREAM
            desc = self.jingle_content.getTag('description')
790 791
            if self.jingle_content.getAttr('creator') == 'initiator':
                file_tag = desc.getTag('file')
792 793 794
                self.file_props.sender = self.fjid
                self.file_props.receiver = self.conn._ft_get_our_jid()
            else:
795
                file_tag = desc.getTag('file')
796 797 798 799
                h = file_tag.getTag('hash')
                h = h.getData() if h else None
                n = file_tag.getTag('name')
                n = n.getData() if n else None
800
                pjid = app.get_jid_without_resource(self.fjid)
801 802
                file_info = self.conn.get_file_info(
                    pjid, hash_=h, name=n, account=self.conn.name)
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
                self.file_props.file_name = file_info['file-name']
                self.file_props.sender = self.conn._ft_get_our_jid()
                self.file_props.receiver = self.fjid
                self.file_props.type_ = 's'
            for child in file_tag.getChildren():
                name = child.getName()
                val = child.getData()
                if val is None:
                    continue
                if name == 'name':
                    self.file_props.name = val
                if name == 'size':
                    self.file_props.size = int(val)
                if name == 'hash':
                    self.file_props.algo = child.getAttr('algo')
                    self.file_props.hash_ = val
                if name == 'date':
                    self.file_props.date = val
Tanguy Ortolo's avatar
Tanguy Ortolo committed
821
        else:
822 823
            si = self.stanza.getTag('si')
            self.file_props = FilesProp.getNewFileProp(self.conn.name,
824 825
                si.getAttr('id'))
            self.file_props.transport_sid = self.file_props.sid
826 827 828 829 830 831 832 833 834 835 836
            profile = si.getAttr('profile')
            if profile != nbxmpp.NS_FILE:
                self.conn.send_file_rejection(self.file_props, code='400',
                    typ='profile')
                raise nbxmpp.NodeProcessed
            feature_tag = si.getTag('feature', namespace=nbxmpp.NS_FEATURE)
            if not feature_tag:
                return
            form_tag = feature_tag.getTag('x', namespace=nbxmpp.NS_DATA)
            if not form_tag:
                return
837
            self.dataform = dataforms.extend_form(node=form_tag)
838 839 840 841 842 843 844 845 846 847 848 849 850
            for f in self.dataform.iter_fields():
                if f.var == 'stream-method' and f.type_ == 'list-single':
                    values = [o[1] for o in f.options]
                    self.file_props.stream_methods = ' '.join(values)
                    if nbxmpp.NS_BYTESTREAM in values or \
                    nbxmpp.NS_IBB in values:
                        break
            else:
                self.conn.send_file_rejection(self.file_props, code='400',
                    typ='stream')
                raise nbxmpp.NodeProcessed
            file_tag = si.getTag('file')
            for name, val in file_tag.getAttrs().items():
Tanguy Ortolo's avatar
Tanguy Ortolo committed
851 852
                if val is None:
                    continue
853 854 855 856 857 858 859 860 861 862
                if name == 'name':
                    self.file_props.name = val
                if name == 'size':
                    self.file_props.size = int(val)
            mime_type = si.getAttr('mime-type')
            if mime_type is not None:
                self.file_props.mime_type = mime_type
            self.file_props.sender = self.fjid
            self.file_props.receiver = self.conn._ft_get_our_jid()
        self.file_props.request_id = self.id_
Tanguy Ortolo's avatar
Tanguy Ortolo committed
863 864
        file_desc_tag = file_tag.getTag('desc')
        if file_desc_tag is not None:
865 866
            self.file_props.desc = file_desc_tag.getData()
        self.file_props.transfered_size = []
Tanguy Ortolo's avatar
Tanguy Ortolo committed
867 868 869 870 871 872
        return True

class FileRequestErrorEvent(nec.NetworkIncomingEvent):
    name = 'file-request-error'

    def generate(self):
873
        self.jid = app.get_jid_without_resource(self.jid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
874 875
        return True

876 877 878 879
class FileTransferCompletedEvent(nec.NetworkIncomingEvent):
    name = 'file-transfer-completed'

    def generate(self):
880 881
        jid = str(self.file_props.receiver)
        self.jid = app.get_jid_without_resource(jid)
882 883
        return True

Tanguy Ortolo's avatar
Tanguy Ortolo committed
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
class GatewayPromptReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
    name = 'gateway-prompt-received'

    def generate(self):
        self.get_jid_resource()
        query = self.stanza.getTag('query')
        if query:
            self.desc = query.getTagData('desc')
            self.prompt = query.getTagData('prompt')
            self.prompt_jid = query.getTagData('jid')
        else:
            self.desc = None
            self.prompt = None
            self.prompt_jid = None
        return True

class NotificationEvent(nec.NetworkIncomingEvent):
    name = 'notification'
902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
    base_network_events = ['decrypted-message-received',
                           'gc-message-received',
                           'presence-received']

    def generate(self):
        # what's needed to compute output
        self.account = self.base_event.conn.name
        self.conn = self.base_event.conn
        self.jid = ''
        self.control = None
        self.control_focused = False
        self.first_unread = False

        # For output
        self.do_sound = False
        self.sound_file = ''
        self.sound_event = '' # gajim sound played if not sound_file is set
        self.show_popup = False

        self.do_popup = False
        self.popup_title = ''
        self.popup_text = ''
        self.popup_event_type = ''
        self.popup_msg_type = ''
        self.icon_name = None
        self.transport_name = None
        self.show = None
        self.popup_timeout = -1

        self.do_command = False
        self.command = ''

        self.show_in_notification_area = False
        self.show_in_roster = False

        self.detect_type()

        if self.notif_type == 'msg':
            self.handle_incoming_msg_event(self.base_event)
        elif self.notif_type == 'gc-msg':
            self.handle_incoming_gc_msg_event(self.base_event)
        elif self.notif_type == 'pres':
            self.handle_incoming_pres_event(self.base_event)
        return True
Tanguy Ortolo's avatar
Tanguy Ortolo committed
946 947 948 949 950 951 952 953 954 955

    def detect_type(self):
        if self.base_event.name == 'decrypted-message-received':
            self.notif_type = 'msg'
        if self.base_event.name == 'gc-message-received':
            self.notif_type = 'gc-msg'
        if self.base_event.name == 'presence-received':
            self.notif_type = 'pres'

    def handle_incoming_msg_event(self, msg_obj):
956 957 958
        # don't alert for carbon copied messages from ourselves
        if msg_obj.sent:
            return
Tanguy Ortolo's avatar
Tanguy Ortolo committed
959 960 961
        if not msg_obj.msgtxt:
            return
        self.jid = msg_obj.jid
962 963 964 965 966 967 968 969 970 971
        if msg_obj.mtype == 'pm':
            self.jid = msg_obj.fjid

        self.control = app.interface.msg_win_mgr.search_control(
            msg_obj.jid, self.account, msg_obj.resource)

        if self.control is None:
            if len(app.events.get_events(
                    self.account, msg_obj.jid, [msg_obj.mtype])) <= 1:
                self.first_unread = True
Tanguy Ortolo's avatar
Tanguy Ortolo committed
972
        else:
973
            self.control_focused = self.control.has_focus()
Tanguy Ortolo's avatar
Tanguy Ortolo committed
974 975 976 977

        if msg_obj.mtype == 'pm':
            nick = msg_obj.resource
        else:
978
            nick = app.get_name_from_jid(self.conn.name, self.jid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
979 980 981 982 983 984 985 986

        if self.first_unread:
            self.sound_event = 'first_message_received'
        elif self.control_focused:
            self.sound_event = 'next_message_received_focused'
        else:
            self.sound_event = 'next_message_received_unfocused'

987
        if app.config.get('notification_preview_message'):
Tanguy Ortolo's avatar
Tanguy Ortolo committed
988 989 990 991 992 993 994
            self.popup_text = msg_obj.msgtxt
            if self.popup_text and (self.popup_text.startswith('/me ') or \
            self.popup_text.startswith('/me\n')):
                self.popup_text = '* ' + nick + self.popup_text[3:]
        else:
            # We don't want message preview, do_preview = False
            self.popup_text = ''
995

Tanguy Ortolo's avatar
Tanguy Ortolo committed
996 997 998 999 1000 1001 1002 1003 1004 1005
        if msg_obj.mtype == 'normal': # single message
            self.popup_msg_type = 'normal'
            self.popup_event_type = _('New Single Message')
        elif msg_obj.mtype == 'pm':
            self.popup_msg_type = 'pm'
            self.popup_event_type = _('New Private Message')
        else: # chat message
            self.popup_msg_type = 'chat'
            self.popup_event_type = _('New Message')

1006 1007 1008 1009 1010 1011
        num_unread = len(app.events.get_events(self.conn.name, self.jid,
            ['printed_' + self.popup_msg_type, self.popup_msg_type]))
        self.popup_title = i18n.ngettext(
            'New message from %(nickname)s',
            '%(n_msgs)i unread messages from %(nickname)s',
            num_unread) % {'nickname': nick, 'n_msgs': num_unread}
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1012

1013 1014 1015 1016 1017 1018 1019 1020 1021
        if app.config.get('notify_on_new_message'):
            if self.first_unread or (app.config.get('autopopup_chat_opened') \
            and not self.control_focused):
                if app.config.get('autopopupaway'):
                    # always show notification
                    self.do_popup = True
                if app.connections[self.conn.name].connected in (2, 3):
                    # we're online or chat
                    self.do_popup = True
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1022

1023
        if msg_obj.attention and not app.config.get(
1024 1025 1026 1027
        'ignore_incoming_attention'):
            self.popup_timeout = 0
            self.do_popup = True
        else:
1028
            self.popup_timeout = app.config.get('notification_timeout')
1029

1030 1031
        if msg_obj.attention and not app.config.get(
        'ignore_incoming_attention') and app.config.get_per('soundevents',
1032 1033 1034 1035
        'attention_received', 'enabled'):
            self.sound_event = 'attention_received'
            self.do_sound = True
        elif self.first_unread and helpers.allow_sound_notification(
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
        self.conn.name, 'first_message_received'):
            self.do_sound = True
        elif not self.first_unread and self.control_focused and \
        helpers.allow_sound_notification(self.conn.name,
        'next_message_received_focused'):
            self.do_sound = True
        elif not self.first_unread and not self.control_focused and \
        helpers.allow_sound_notification(self.conn.name,
        'next_message_received_unfocused'):
            self.do_sound = True

    def handle_incoming_gc_msg_event(self, msg_obj):
        if not msg_obj.msg_obj.gc_control:
            # we got a message from a room we're not in? ignore it
            return
1051
        self.jid = msg_obj.jid
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
        sound = msg_obj.msg_obj.gc_control.highlighting_for_message(
            msg_obj.msgtxt, msg_obj.timestamp)[1]

        if msg_obj.nickname != msg_obj.msg_obj.gc_control.nick:
            self.do_sound = True
            if sound == 'received':
                self.sound_event = 'muc_message_received'
            elif sound == 'highlight':
                self.sound_event = 'muc_message_highlight'
            else:
                self.do_sound = False
        else:
            self.do_sound = False

        self.do_popup = False

    def handle_incoming_pres_event(self, pres_obj):
1069
        if app.jid_is_transport(pres_obj.jid):
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
            return True
        account = pres_obj.conn.name
        self.jid = pres_obj.jid
        resource = pres_obj.resource or ''
        # It isn't an agent
        for c in pres_obj.contact_list:
            if c.resource == resource:
                # we look for other connected resources
                continue
            if c.show not in ('offline', 'error'):
                return True

        # no other resource is connected, let's look in metacontacts
1083
        family = app.contacts.get_metacontacts_family(account, self.jid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1084 1085 1086
        for info in family:
            acct_ = info['account']
            jid_ = info['jid']
1087
            c_ = app.contacts.get_contact_with_highest_priority(acct_, jid_)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1088 1089 1090 1091 1092 1093 1094 1095 1096
            if not c_:
                continue
            if c_.jid == self.jid:
                continue
            if c_.show not in ('offline', 'error'):
                return True

        if pres_obj.old_show < 2 and pres_obj.new_show > 1:
            event = 'contact_connected'
1097
            server = app.get_server_from_jid(self.jid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1098 1099
            account_server = account + '/' + server
            block_transport = False
1100 1101
            if account_server in app.block_signed_in_notifications and \
            app.block_signed_in_notifications[account_server]:
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1102 1103
                block_transport = True
            if helpers.allow_showing_notification(account, 'notify_on_signin') \
1104
            and not app.block_signed_in_notifications[account] and \
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1105 1106
            not block_transport:
                self.do_popup = True
1107 1108
            if app.config.get_per('soundevents', 'contact_connected',
            'enabled') and not app.block_signed_in_notifications[account] and\
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1109 1110 1111 1112 1113 1114 1115 1116 1117
            not block_transport and helpers.allow_sound_notification(account,
            'contact_connected'):
                self.sound_event = event
                self.do_sound = True

        elif pres_obj.old_show > 1 and pres_obj.new_show < 2:
            event = 'contact_disconnected'
            if helpers.allow_showing_notification(account, 'notify_on_signout'):
                self.do_popup = True
1118
            if app.config.get_per('soundevents', 'contact_disconnected',
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1119 1120 1121 1122 1123 1124 1125 1126 1127
            'enabled') and helpers.allow_sound_notification(account, event):
                self.sound_event = event
                self.do_sound = True
        # Status change (not connected/disconnected or error (<1))
        elif pres_obj.new_show > 1:
            event = 'status_change'
        else:
            return True

1128 1129 1130 1131
        if app.jid_is_transport(self.jid):
            self.transport_name = app.get_transport_name_from_jid(self.jid)

        self.show = pres_obj.show
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1132

1133
        self.popup_timeout = app.config.get('notification_timeout')
1134

1135
        nick = i18n.direction_mark + app.get_name_from_jid(account, self.jid)
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1136 1137
        if event == 'status_change':
            self.popup_title = _('%(nick)s Changed Status') % \
1138
                {'nick': nick}
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1139
            self.popup_text = _('%(nick)s is now %(status)s') % \
1140
                {'nick': nick, 'status': helpers.get_uf_show(pres_obj.show)}
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1141 1142 1143 1144
            if pres_obj.status:
                self.popup_text = self.popup_text + " : " + pres_obj.status
            self.popup_event_type = _('Contact Changed Status')
        elif event == 'contact_connected':
1145
            self.popup_title = _('%(nickname)s Signed In') % {'nickname': nick}
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1146 1147 1148 1149 1150
            self.popup_text = ''
            if pres_obj.status:
                self.popup_text = pres_obj.status
            self.popup_event_type = _('Contact Signed In')
        elif event == 'contact_disconnected':
1151
            self.popup_title = _('%(nickname)s Signed Out') % {'nickname': nick}
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1152 1153 1154 1155 1156 1157 1158 1159 1160
            self.popup_text = ''
            if pres_obj.status:
                self.popup_text = pres_obj.status
            self.popup_event_type = _('Contact Signed Out')

class MessageOutgoingEvent(nec.NetworkOutgoingEvent):
    name = 'message-outgoing'

    def init(self):
1161 1162
        self.additional_data = {}
        self.message = None
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1163 1164
        self.keyID = None
        self.type_ = 'chat'
1165 1166
        self.kind = None
        self.timestamp = None
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1167 1168
        self.subject = ''
        self.chatstate = None
1169
        self.stanza_id = None
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
        self.resource = None
        self.user_nick = None
        self.xhtml = None
        self.label = None
        self.session = None
        self.form_node = None
        self.delayed = None
        self.callback = None
        self.callback_args = []
        self.now = False
        self.is_loggable = True
        self.control = None
1182
        self.attention = False
1183
        self.correct_id = None
1184
        self.automatic_message = True
1185 1186 1187 1188 1189 1190 1191 1192 1193
        self.encryption = ''
        self.encrypted = False

    def get_full_jid(self):
        if self.resource:
            return self.jid + '/' + self.resource
        if self.session:
            return self.session.get_to()
        return self.jid
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1194 1195

    def generate(self):
1196 1197 1198 1199
        if self.type_ == 'chat':
            self.kind = KindConstant.CHAT_MSG_SENT
        else:
            self.kind = KindConstant.SINGLE_MSG_SENT
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1200 1201
        return True

1202
class StanzaMessageOutgoingEvent(nec.NetworkOutgoingEvent):
1203
    name = 'stanza-message-outgoing'
1204

1205

1206
class GcStanzaMessageOutgoingEvent(nec.NetworkOutgoingEvent):
1207
    name = 'gc-stanza-message-outgoing'
1208 1209


1210 1211 1212 1213
class GcMessageOutgoingEvent(nec.NetworkOutgoingEvent):
    name = 'gc-message-outgoing'

    def init(self):
1214
        self.additional_data = {}
1215
        self.message = ''
1216
        self.chatstate = None
1217
        self.xhtml = None
1218
        self.stanza_id = None
1219 1220 1221 1222 1223
        self.label = None
        self.callback = None
        self.callback_args = []
        self.is_loggable = True
        self.control = None
1224
        self.correct_id = None
1225
        self.automatic_message = True
1226 1227 1228 1229 1230

    def generate(self):
        return True


Tanguy Ortolo's avatar
Tanguy Ortolo committed
1231 1232 1233 1234 1235 1236 1237
class ClientCertPassphraseEvent(nec.NetworkIncomingEvent):
    name = 'client-cert-passphrase'

class InformationEvent(nec.NetworkIncomingEvent):
    name = 'information'

    def init(self):
1238 1239 1240
        self.args = None
        self.kwargs = {}
        self.dialog_name = None
Tanguy Ortolo's avatar
Tanguy Ortolo committed
1241
        self.popup = True
1242

1243 1244 1245 1246 1247 1248 1249
    def generate(self):
        if self.args is None:
            self.args = ()
        else:
            self.args = (self.args,)
        return True

1250

1251 1252
class StyleChanged(nec.NetworkIncomingEvent):
    name = 'style-changed'
1253 1254 1255

    def generate(self):
        return True