new way to handle incominf messages, new notification event.
[gajim.git] / src / common / connection_handlers_events.py
blob976bc0f13270d3238f5fcfa39293b440e5dd4b43
1 # -*- coding:utf-8 -*-
2 ## src/common/connection_handlers_events.py
3 ##
4 ## Copyright (C) 2010 Yann Leboulanger <asterix AT lagaule.org>
5 ##
6 ## This file is part of Gajim.
7 ##
8 ## Gajim is free software; you can redistribute it and/or modify
9 ## it under the terms of the GNU General Public License as published
10 ## by the Free Software Foundation; version 3 only.
12 ## Gajim is distributed in the hope that it will be useful,
13 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ## GNU General Public License for more details.
17 ## You should have received a copy of the GNU General Public License
18 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
21 import datetime
22 import sys
23 from time import (localtime, time as time_time)
24 from calendar import timegm
25 import hmac
27 from common import atom
28 from common import nec
29 from common import helpers
30 from common import gajim
31 from common import xmpp
32 from common import dataforms
33 from common import exceptions
34 from common.zeroconf import zeroconf
35 from common.logger import LOG_DB_PATH
36 from common.pep import SUPPORTED_PERSONAL_USER_EVENTS
38 import gtkgui_helpers
40 import logging
41 log = logging.getLogger('gajim.c.connection_handlers_events')
43 class HelperEvent:
44 def get_jid_resource(self, check_fake_jid=False):
45 if check_fake_jid and hasattr(self, 'id_') and \
46 self.id_ in self.conn.groupchat_jids:
47 self.fjid = self.conn.groupchat_jids[self.id_]
48 del self.conn.groupchat_jids[self.id_]
49 else:
50 self.fjid = helpers.get_full_jid_from_iq(self.stanza)
51 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
53 def get_id(self):
54 self.id_ = self.stanza.getID()
56 def get_gc_control(self):
57 self.gc_control = gajim.interface.msg_win_mgr.get_gc_control(self.jid,
58 self.conn.name)
60 # If gc_control is missing - it may be minimized. Try to get it
61 # from there. If it's not there - then it's missing anyway and
62 # will remain set to None.
63 if not self.gc_control:
64 minimized = gajim.interface.minimized_controls[self.conn.name]
65 self.gc_control = minimized.get(self.jid)
67 def _generate_timestamp(self, tag):
68 tim = helpers.datetime_tuple(tag)
69 self.timestamp = localtime(timegm(tim))
71 def get_chatstate(self):
72 """
73 Extract chatstate from a <message/> stanza
74 Requires self.stanza and self.msgtxt
75 """
76 self.composing_xep = None
77 self.chatstate = None
79 # chatstates - look for chatstate tags in a message if not delayed
80 delayed = self.stanza.getTag('x', namespace=xmpp.NS_DELAY) is not None
81 if not delayed:
82 self.composing_xep = False
83 children = self.stanza.getChildren()
84 for child in children:
85 if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
86 self.chatstate = child.getName()
87 self.composing_xep = 'XEP-0085'
88 break
89 # No XEP-0085 support, fallback to XEP-0022
90 if not self.chatstate:
91 chatstate_child = self.stanza.getTag('x',
92 namespace=xmpp.NS_EVENT)
93 if chatstate_child:
94 self.chatstate = 'active'
95 self.composing_xep = 'XEP-0022'
96 if not self.msgtxt and chatstate_child.getTag('composing'):
97 self.chatstate = 'composing'
99 class HttpAuthReceivedEvent(nec.NetworkIncomingEvent):
100 name = 'http-auth-received'
101 base_network_events = []
103 def generate(self):
104 self.opt = gajim.config.get_per('accounts', self.conn.name, 'http_auth')
105 self.iq_id = self.stanza.getTagAttr('confirm', 'id')
106 self.method = self.stanza.getTagAttr('confirm', 'method')
107 self.url = self.stanza.getTagAttr('confirm', 'url')
108 # In case it's a message with a body
109 self.msg = self.stanza.getTagData('body')
110 return True
112 class LastResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
113 name = 'last-result-received'
114 base_network_events = []
116 def generate(self):
117 self.get_id()
118 self.get_jid_resource(check_fake_jid=True)
119 if self.id_ in self.conn.last_ids:
120 self.conn.last_ids.remove(self.id_)
122 self.status = ''
123 self.seconds = -1
125 if self.stanza.getType() == 'error':
126 return True
128 qp = self.stanza.getTag('query')
129 if not qp:
130 return
131 sec = qp.getAttr('seconds')
132 self.status = qp.getData()
133 try:
134 self.seconds = int(sec)
135 except Exception:
136 return
138 return True
140 class VersionResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
141 name = 'version-result-received'
142 base_network_events = []
144 def generate(self):
145 self.get_id()
146 self.get_jid_resource(check_fake_jid=True)
147 if self.id_ in self.conn.version_ids:
148 self.conn.version_ids.remove(self.id_)
150 self.client_info = ''
151 self.os_info = ''
153 if self.stanza.getType() == 'error':
154 return True
156 qp = self.stanza.getTag('query')
157 if qp.getTag('name'):
158 self.client_info += qp.getTag('name').getData()
159 if qp.getTag('version'):
160 self.client_info += ' ' + qp.getTag('version').getData()
161 if qp.getTag('os'):
162 self.os_info += qp.getTag('os').getData()
164 return True
166 class TimeResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
167 name = 'time-result-received'
168 base_network_events = []
170 def generate(self):
171 self.get_id()
172 self.get_jid_resource(check_fake_jid=True)
173 if self.id_ in self.conn.entity_time_ids:
174 self.conn.entity_time_ids.remove(self.id_)
176 self.time_info = ''
178 if self.stanza.getType() == 'error':
179 return True
181 qp = self.stanza.getTag('time')
182 if not qp:
183 # wrong answer
184 return
185 tzo = qp.getTag('tzo').getData()
186 if tzo.lower() == 'z':
187 tzo = '0:0'
188 tzoh, tzom = tzo.split(':')
189 utc_time = qp.getTag('utc').getData()
190 ZERO = datetime.timedelta(0)
191 class UTC(datetime.tzinfo):
192 def utcoffset(self, dt):
193 return ZERO
194 def tzname(self, dt):
195 return "UTC"
196 def dst(self, dt):
197 return ZERO
199 class contact_tz(datetime.tzinfo):
200 def utcoffset(self, dt):
201 return datetime.timedelta(hours=int(tzoh), minutes=int(tzom))
202 def tzname(self, dt):
203 return "remote timezone"
204 def dst(self, dt):
205 return ZERO
207 try:
208 t = datetime.datetime.strptime(utc_time, '%Y-%m-%dT%H:%M:%SZ')
209 except ValueError, e:
210 try:
211 t = datetime.datetime.strptime(utc_time,
212 '%Y-%m-%dT%H:%M:%S.%fZ')
213 except ValueError, e:
214 log.info('Wrong time format: %s' % str(e))
215 return
217 t = t.replace(tzinfo=UTC())
218 self.time_info = t.astimezone(contact_tz()).strftime('%c')
219 return True
221 class GMailQueryReceivedEvent(nec.NetworkIncomingEvent):
222 name = 'gmail-notify'
223 base_network_events = []
225 def generate(self):
226 if not self.stanza.getTag('mailbox'):
227 return
228 mb = self.stanza.getTag('mailbox')
229 if not mb.getAttr('url'):
230 return
231 self.conn.gmail_url = mb.getAttr('url')
232 if mb.getNamespace() != xmpp.NS_GMAILNOTIFY:
233 return
234 self.newmsgs = mb.getAttr('total-matched')
235 if not self.newmsgs:
236 return
237 if self.newmsgs == '0':
238 return
239 # there are new messages
240 self.gmail_messages_list = []
241 if mb.getTag('mail-thread-info'):
242 gmail_messages = mb.getTags('mail-thread-info')
243 for gmessage in gmail_messages:
244 unread_senders = []
245 for sender in gmessage.getTag('senders').getTags(
246 'sender'):
247 if sender.getAttr('unread') != '1':
248 continue
249 if sender.getAttr('name'):
250 unread_senders.append(sender.getAttr('name') + \
251 '< ' + sender.getAttr('address') + '>')
252 else:
253 unread_senders.append(sender.getAttr('address'))
255 if not unread_senders:
256 continue
257 gmail_subject = gmessage.getTag('subject').getData()
258 gmail_snippet = gmessage.getTag('snippet').getData()
259 tid = int(gmessage.getAttr('tid'))
260 if not self.conn.gmail_last_tid or \
261 tid > self.conn.gmail_last_tid:
262 self.conn.gmail_last_tid = tid
263 self.gmail_messages_list.append({
264 'From': unread_senders,
265 'Subject': gmail_subject,
266 'Snippet': gmail_snippet,
267 'url': gmessage.getAttr('url'),
268 'participation': gmessage.getAttr('participation'),
269 'messages': gmessage.getAttr('messages'),
270 'date': gmessage.getAttr('date')})
271 self.conn.gmail_last_time = int(mb.getAttr('result-time'))
273 self.jid = gajim.get_jid_from_account(self.name)
274 log.debug(('You have %s new gmail e-mails on %s.') % (self.newmsgs,
275 self.jid))
276 return True
278 class RosterItemExchangeEvent(nec.NetworkIncomingEvent, HelperEvent):
279 name = 'roster-item-exchange-received'
280 base_network_events = []
282 def generate(self):
283 self.get_id()
284 self.get_jid_resource()
285 self.exchange_items_list = {}
286 items_list = self.stanza.getTag('x').getChildren()
287 if not items_list:
288 return
289 self.action = items_list[0].getAttr('action')
290 if self.action is None:
291 self.action = 'add'
292 for item in self.stanza.getTag('x', namespace=xmpp.NS_ROSTERX).\
293 getChildren():
294 try:
295 jid = helpers.parse_jid(item.getAttr('jid'))
296 except helpers.InvalidFormat:
297 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('jid'))
298 continue
299 name = item.getAttr('name')
300 contact = gajim.contacts.get_contact(self.conn.name, jid)
301 groups = []
302 same_groups = True
303 for group in item.getTags('group'):
304 groups.append(group.getData())
305 # check that all suggested groups are in the groups we have for
306 # this contact
307 if not contact or group not in contact.groups:
308 same_groups = False
309 if contact:
310 # check that all groups we have for this contact are in the
311 # suggested groups
312 for group in contact.groups:
313 if group not in groups:
314 same_groups = False
315 if contact.sub in ('both', 'to') and same_groups:
316 continue
317 self.exchange_items_list[jid] = []
318 self.exchange_items_list[jid].append(name)
319 self.exchange_items_list[jid].append(groups)
320 if self.exchange_items_list:
321 return True
323 class VersionRequestEvent(nec.NetworkIncomingEvent):
324 name = 'version-request-received'
325 base_network_events = []
327 class LastRequestEvent(nec.NetworkIncomingEvent):
328 name = 'last-request-received'
329 base_network_events = []
331 class TimeRequestEvent(nec.NetworkIncomingEvent):
332 name = 'time-request-received'
333 base_network_events = []
335 class TimeRevisedRequestEvent(nec.NetworkIncomingEvent):
336 name = 'time-revised-request-received'
337 base_network_events = []
339 class RosterReceivedEvent(nec.NetworkIncomingEvent):
340 name = 'roster-received'
341 base_network_events = []
343 def generate(self):
344 if hasattr(self, 'xmpp_roster'):
345 self.version = self.xmpp_roster.version
346 self.received_from_server = self.xmpp_roster.received_from_server
347 self.roster = {}
348 raw_roster = self.xmpp_roster.getRaw()
349 our_jid = gajim.get_jid_from_account(self.conn.name)
351 for jid in raw_roster:
352 try:
353 j = helpers.parse_jid(jid)
354 except Exception:
355 print >> sys.stderr, _('JID %s is not RFC compliant. It '
356 'will not be added to your roster. Use roster '
357 'management tools such as '
358 'http://jru.jabberstudio.org/ to remove it') % jid
359 else:
360 infos = raw_roster[jid]
361 if jid != our_jid and (not infos['subscription'] or \
362 infos['subscription'] == 'none') and (not infos['ask'] or \
363 infos['ask'] == 'none') and not infos['name'] and \
364 not infos['groups']:
365 # remove this useless item, it won't be shown in roster
366 # anyway
367 self.conn.connection.getRoster().delItem(jid)
368 elif jid != our_jid: # don't add our jid
369 self.roster[j] = raw_roster[jid]
370 else:
371 # Roster comes from DB
372 self.received_from_server = False
373 self.version = gajim.config.get_per('accounts', self.conn.name,
374 'roster_version')
375 self.roster = gajim.logger.get_roster(gajim.get_jid_from_account(
376 self.conn.name))
377 return True
379 class RosterSetReceivedEvent(nec.NetworkIncomingEvent):
380 name = 'roster-set-received'
381 base_network_events = []
383 def generate(self):
384 self.version = self.stanza.getTagAttr('query', 'ver')
385 self.items = {}
386 for item in self.stanza.getTag('query').getChildren():
387 try:
388 jid = helpers.parse_jid(item.getAttr('jid'))
389 except helpers.InvalidFormat:
390 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('jid'))
391 continue
392 name = item.getAttr('name')
393 sub = item.getAttr('subscription')
394 ask = item.getAttr('ask')
395 groups = []
396 for group in item.getTags('group'):
397 groups.append(group.getData())
398 self.items[jid] = {'name': name, 'sub': sub, 'ask': ask,
399 'groups': groups}
400 if self.conn.connection and self.conn.connected > 1:
401 reply = xmpp.Iq(typ='result', attrs={'id': self.stanza.getID()},
402 to=self.stanza.getFrom(), frm=self.stanza.getTo(), xmlns=None)
403 self.conn.connection.send(reply)
404 return True
406 class RosterInfoEvent(nec.NetworkIncomingEvent):
407 name = 'roster-info'
408 base_network_events = []
410 class MucOwnerReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
411 name = 'muc-owner-received'
412 base_network_events = []
414 def generate(self):
415 self.get_jid_resource()
416 qp = self.stanza.getQueryPayload()
417 self.form_node = None
418 for q in qp:
419 if q.getNamespace() == xmpp.NS_DATA:
420 self.form_node = q
421 self.dataform = dataforms.ExtendForm(node=self.form_node)
422 return True
424 class MucAdminReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
425 name = 'muc-admin-received'
426 base_network_events = []
428 def generate(self):
429 self.get_jid_resource()
430 items = self.stanza.getTag('query',
431 namespace=xmpp.NS_MUC_ADMIN).getTags('item')
432 self.users_dict = {}
433 for item in items:
434 if item.has_attr('jid') and item.has_attr('affiliation'):
435 try:
436 jid = helpers.parse_jid(item.getAttr('jid'))
437 except helpers.InvalidFormat:
438 log.warn('Invalid JID: %s, ignoring it' % \
439 item.getAttr('jid'))
440 continue
441 affiliation = item.getAttr('affiliation')
442 self.users_dict[jid] = {'affiliation': affiliation}
443 if item.has_attr('nick'):
444 self.users_dict[jid]['nick'] = item.getAttr('nick')
445 if item.has_attr('role'):
446 self.users_dict[jid]['role'] = item.getAttr('role')
447 reason = item.getTagData('reason')
448 if reason:
449 self.users_dict[jid]['reason'] = reason
450 return True
452 class PrivateStorageReceivedEvent(nec.NetworkIncomingEvent):
453 name = 'private-storage-received'
454 base_network_events = []
456 def generate(self):
457 query = self.stanza.getTag('query')
458 self.storage_node = query.getTag('storage')
459 if self.storage_node:
460 self.namespace = self.storage_node.getNamespace()
461 return True
463 class BookmarksHelper:
464 def parse_bookmarks(self):
465 self.bookmarks = []
466 confs = self.storage_node.getTags('conference')
467 for conf in confs:
468 autojoin_val = conf.getAttr('autojoin')
469 if autojoin_val is None: # not there (it's optional)
470 autojoin_val = False
471 minimize_val = conf.getAttr('minimize')
472 if minimize_val is None: # not there (it's optional)
473 minimize_val = False
474 print_status = conf.getTagData('print_status')
475 if not print_status:
476 print_status = conf.getTagData('show_status')
477 try:
478 jid = helpers.parse_jid(conf.getAttr('jid'))
479 except helpers.InvalidFormat:
480 log.warn('Invalid JID: %s, ignoring it' % conf.getAttr('jid'))
481 continue
482 bm = {'name': conf.getAttr('name'),
483 'jid': jid,
484 'autojoin': autojoin_val,
485 'minimize': minimize_val,
486 'password': conf.getTagData('password'),
487 'nick': conf.getTagData('nick'),
488 'print_status': print_status}
491 bm_jids = [b['jid'] for b in self.bookmarks]
492 if bm['jid'] not in bm_jids:
493 self.bookmarks.append(bm)
495 class PrivateStorageBookmarksReceivedEvent(nec.NetworkIncomingEvent,
496 BookmarksHelper):
497 name = 'private-storage-bookmarks-received'
498 base_network_events = ['private-storage-received']
500 def generate(self):
501 self.conn = self.base_event.conn
502 self.storage_node = self.base_event.storage_node
503 if self.base_event.namespace != xmpp.NS_BOOKMARKS:
504 return
505 self.parse_bookmarks()
506 return True
508 class BookmarksReceivedEvent(nec.NetworkIncomingEvent):
509 name = 'bookmarks-received'
510 base_network_events = ['private-storage-bookmarks-received',
511 'pubsub-bookmarks-received']
513 def generate(self):
514 self.conn = self.base_event.conn
515 self.bookmarks = self.base_event.bookmarks
516 return True
518 class PrivateStorageRosternotesReceivedEvent(nec.NetworkIncomingEvent):
519 name = 'private-storage-rosternotes-received'
520 base_network_events = ['private-storage-received']
522 def generate(self):
523 self.conn = self.base_event.conn
524 if self.base_event.namespace != xmpp.NS_ROSTERNOTES:
525 return
526 notes = self.base_event.storage_node.getTags('note')
527 self.annotations = {}
528 for note in notes:
529 try:
530 jid = helpers.parse_jid(note.getAttr('jid'))
531 except helpers.InvalidFormat:
532 log.warn('Invalid JID: %s, ignoring it' % note.getAttr('jid'))
533 continue
534 annotation = note.getData()
535 self.annotations[jid] = annotation
536 if self.annotations:
537 return True
539 class RosternotesReceivedEvent(nec.NetworkIncomingEvent):
540 name = 'rosternotes-received'
541 base_network_events = ['private-storage-rosternotes-received']
543 def generate(self):
544 self.conn = self.base_event.conn
545 self.annotations = self.base_event.annotations
546 return True
548 class PubsubReceivedEvent(nec.NetworkIncomingEvent):
549 name = 'pubsub-received'
550 base_network_events = []
552 def generate(self):
553 self.pubsub_node = self.stanza.getTag('pubsub')
554 if not self.pubsub_node:
555 return
556 self.items_node = self.pubsub_node.getTag('items')
557 if not self.items_node:
558 return
559 self.item_node = self.items_node.getTag('item')
560 if not self.item_node:
561 return
562 children = self.item_node.getChildren()
563 if not children:
564 return
565 self.node = children[0]
566 return True
568 class PubsubBookmarksReceivedEvent(nec.NetworkIncomingEvent, BookmarksHelper):
569 name = 'pubsub-bookmarks-received'
570 base_network_events = ['pubsub-received']
572 def generate(self):
573 self.conn = self.base_event.conn
574 self.storage_node = self.base_event.node
575 ns = self.storage_node.getNamespace()
576 if ns != xmpp.NS_BOOKMARKS:
577 return
578 self.parse_bookmarks()
579 return True
581 class SearchFormReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
582 name = 'search-form-received'
583 base_network_events = []
585 def generate(self):
586 self.get_jid_resource()
587 self.data = None
588 self.is_dataform = False
589 tag = self.stanza.getTag('query', namespace=xmpp.NS_SEARCH)
590 if not tag:
591 return True
592 self.data = tag.getTag('x', namespace=xmpp.NS_DATA)
593 if self.data:
594 self.is_dataform = True
595 return True
596 self.data = {}
597 for i in self.stanza.getQueryPayload():
598 self.data[i.getName()] = i.getData()
599 return True
602 class SearchResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
603 name = 'search-result-received'
604 base_network_events = []
606 def generate(self):
607 self.get_jid_resource()
608 self.data = None
609 self.is_dataform = False
610 tag = self.stanza.getTag('query', namespace=xmpp.NS_SEARCH)
611 if not tag:
612 return True
613 self.data = tag.getTag('x', namespace=xmpp.NS_DATA)
614 if self.data:
615 self.is_dataform = True
616 return True
617 self.data = []
618 for item in tag.getTags('item'):
619 # We also show attributes. jid is there
620 f = item.attrs
621 for i in item.getPayload():
622 f[i.getName()] = i.getData()
623 self.data.append(f)
624 return True
626 class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
627 name = 'iq-error-received'
628 base_network_events = []
630 def generate(self):
631 self.get_id()
632 self.get_jid_resource(check_fake_jid=True)
633 self.errmsg = self.stanza.getErrorMsg()
634 self.errcode = self.stanza.getErrorCode()
635 return True
637 class GmailNewMailReceivedEvent(nec.NetworkIncomingEvent):
638 name = 'gmail-new-mail-received'
639 base_network_events = []
641 def generate(self):
642 if not self.stanza.getTag('new-mail'):
643 return
644 if self.stanza.getTag('new-mail').getNamespace() != xmpp.NS_GMAILNOTIFY:
645 return
646 return True
648 class PingReceivedEvent(nec.NetworkIncomingEvent):
649 name = 'ping-received'
650 base_network_events = []
652 class StreamReceivedEvent(nec.NetworkIncomingEvent):
653 name = 'stream-received'
654 base_network_events = []
656 class StreamConflictReceivedEvent(nec.NetworkIncomingEvent):
657 name = 'stream-conflict-received'
658 base_network_events = ['stream-received']
660 def generate(self):
661 if self.base_event.stanza.getTag('conflict'):
662 self.conn = self.base_event.conn
663 return True
665 class PresenceHelperEvent:
666 def _generate_show(self):
667 self.show = self.stanza.getShow()
668 if self.show not in ('chat', 'away', 'xa', 'dnd'):
669 self.show = '' # We ignore unknown show
670 if not self.ptype and not self.show:
671 self.show = 'online'
672 elif self.ptype == 'unavailable':
673 self.show = 'offline'
675 def _generate_ptype(self):
676 self.ptype = self.stanza.getType()
677 if self.ptype == 'available':
678 self.ptype = None
679 rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed',
680 'unsubscribe', 'unsubscribed')
681 if self.ptype and not self.ptype in rfc_types:
682 self.ptype = None
684 class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent,
685 PresenceHelperEvent):
686 name = 'presence-received'
687 base_network_events = ['raw-pres-received']
689 def _generate_keyID(self, sig_tag):
690 self.keyID = ''
691 if sig_tag and self.conn.USE_GPG and self.ptype != 'error':
692 # error presences contain our own signature
693 # verify
694 sig_msg = sig_tag.getData()
695 self.keyID = self.conn.gpg.verify(self.status, sig_msg)
696 self.keyID = helpers.prepare_and_validate_gpg_keyID(self.conn.name,
697 self.jid, self.keyID)
699 def _generate_prio(self):
700 self.prio = self.stanza.getPriority()
701 try:
702 self.prio = int(self.prio)
703 except Exception:
704 self.prio = 0
706 def generate(self):
707 self.conn = self.base_event.conn
708 self.stanza = self.base_event.stanza
710 self.need_add_in_roster = False
711 self.need_redraw = False
713 if not self.conn or self.conn.connected < 2:
714 log.debug('account is no more connected')
715 return
717 self._generate_ptype()
718 try:
719 self.get_jid_resource()
720 except Exception:
721 return
722 jid_list = gajim.contacts.get_jid_list(self.conn.name)
723 self.timestamp = None
724 self.get_id()
725 self.is_gc = False # is it a GC presence ?
726 sig_tag = None
727 self.avatar_sha = None
728 # XEP-0172 User Nickname
729 self.user_nick = self.stanza.getTagData('nick') or ''
730 self.contact_nickname = None
731 self.transport_auto_auth = False
732 # XEP-0203
733 delay_tag = self.stanza.getTag('delay', namespace=xmpp.NS_DELAY2)
734 if delay_tag:
735 self._generate_timestamp(self.stanza.getTimestamp2())
736 xtags = self.stanza.getTags('x')
737 for x in xtags:
738 namespace = x.getNamespace()
739 if namespace.startswith(xmpp.NS_MUC):
740 self.is_gc = True
741 elif namespace == xmpp.NS_SIGNED:
742 sig_tag = x
743 elif namespace == xmpp.NS_VCARD_UPDATE:
744 self.avatar_sha = x.getTagData('photo')
745 self.contact_nickname = x.getTagData('nickname')
746 elif namespace == xmpp.NS_DELAY and not self.timestamp:
747 # XEP-0091
748 self._generate_timestamp(self.stanza.getTimestamp())
749 elif namespace == 'http://delx.cjb.net/protocol/roster-subsync':
750 # see http://trac.gajim.org/ticket/326
751 agent = gajim.get_server_from_jid(self.jid)
752 if self.conn.connection.getRoster().getItem(agent):
753 # to be sure it's a transport contact
754 self.transport_auto_auth = True
756 if not self.is_gc and self.id_ and self.id_.startswith('gajim_muc_') \
757 and self.ptype == 'error':
758 # Error presences may not include sent stanza, so we don't detect
759 # it's a muc presence. So detect it by ID
760 h = hmac.new(self.conn.secret_hmac, self.jid).hexdigest()[:6]
761 if self.id_.split('_')[-1] == h:
762 self.is_gc = True
763 self.status = self.stanza.getStatus() or ''
764 self._generate_show()
765 self._generate_prio()
766 self._generate_keyID(sig_tag)
768 self.errcode = self.stanza.getErrorCode()
769 self.errmsg = self.stanza.getErrorMsg()
771 if self.is_gc:
772 gajim.nec.push_incoming_event(GcPresenceReceivedEvent(None,
773 conn=self.conn, stanza=self.stanza, presence_obj=self))
774 return
776 if self.ptype == 'subscribe':
777 gajim.nec.push_incoming_event(SubscribePresenceReceivedEvent(None,
778 conn=self.conn, stanza=self.stanza, presence_obj=self))
779 elif self.ptype == 'subscribed':
780 # BE CAREFUL: no con.updateRosterItem() in a callback
781 gajim.nec.push_incoming_event(SubscribedPresenceReceivedEvent(None,
782 conn=self.conn, stanza=self.stanza, presence_obj=self))
783 elif self.ptype == 'unsubscribe':
784 log.debug(_('unsubscribe request from %s') % self.jid)
785 elif self.ptype == 'unsubscribed':
786 gajim.nec.push_incoming_event(UnsubscribedPresenceReceivedEvent(
787 None, conn=self.conn, stanza=self.stanza, presence_obj=self))
788 elif self.ptype == 'error':
789 if self.errcode != '409': # conflict # See #5120
790 self.show = 'error'
791 self.status = self.errmsg
792 return True
794 if not self.ptype or self.ptype == 'unavailable':
795 our_jid = gajim.get_jid_from_account(self.conn.name)
796 if self.jid == our_jid and self.resource == \
797 self.conn.server_resource:
798 # We got our own presence
799 gajim.nec.push_incoming_event(OurShowEvent(None, conn=self.conn,
800 show=self.show))
801 elif self.jid in jid_list or self.jid == our_jid:
802 return True
804 class ZeroconfPresenceReceivedEvent(nec.NetworkIncomingEvent):
805 name = 'presence-received'
806 base_network_events = []
808 def generate(self):
809 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
810 self.resource = 'local'
811 self.prio = 0
812 self.keyID = None
813 self.timestamp = 0
814 self.contact_nickname = None
815 self.avatar_sha = None
816 self.need_add_in_roster = False
817 self.need_redraw = False
818 if self.show == 'offline':
819 self.ptype = 'unavailable'
820 else:
821 self.ptype = None
822 self.is_gc = False
823 self.user_nick = ''
824 self.transport_auto_auth = False
825 self.errcode = None
826 self.errmsg = ''
827 return True
829 class GcPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
830 name = 'gc-presence-received'
831 base_network_events = []
833 def generate(self):
834 self.ptype = self.presence_obj.ptype
835 self.fjid = self.presence_obj.fjid
836 self.jid = self.presence_obj.jid
837 self.room_jid = self.presence_obj.jid
838 self.nick = self.presence_obj.resource
839 self.show = self.presence_obj.show
840 self.status = self.presence_obj.status
841 self.avatar_sha = self.presence_obj.avatar_sha
842 self.errcode = self.presence_obj.errcode
843 self.errmsg = self.presence_obj.errmsg
844 self.errcon = self.stanza.getError()
845 self.get_gc_control()
846 self.gc_contact = gajim.contacts.get_gc_contact(self.conn.name,
847 self.room_jid, self.nick)
849 if self.ptype == 'error':
850 return True
852 if self.ptype and self.ptype != 'unavailable':
853 return
854 if gajim.config.get('log_contact_status_changes') and \
855 gajim.config.should_log(self.conn.name, self.room_jid):
856 if self.gc_contact:
857 jid = self.gc_contact.jid
858 else:
859 jid = self.stanza.getJid()
860 st = self.status
861 if jid:
862 # we know real jid, save it in db
863 st += ' (%s)' % jid
864 try:
865 gajim.logger.write('gcstatus', self.fjid, st,
866 self.show)
867 except exceptions.PysqliteOperationalError, e:
868 self.conn.dispatch('DB_ERROR', (_('Disk Write Error'),
869 str(e)))
870 except exceptions.DatabaseMalformed:
871 pritext = _('Database Error')
872 sectext = _('The database file (%s) cannot be read. '
873 'Try to repair it (see '
874 'http://trac.gajim.org/wiki/DatabaseBackup) or '
875 'remove it (all history will be lost).') % \
876 LOG_DB_PATH
877 self.conn.dispatch('DB_ERROR', (pritext, sectext))
878 if self.avatar_sha == '':
879 # contact has no avatar
880 puny_nick = helpers.sanitize_filename(self.nick)
881 gajim.interface.remove_avatar_files(self.room_jid, puny_nick)
882 # NOTE: if it's a gc presence, don't ask vcard here.
883 # We may ask it to real jid in gui part.
884 self.status_code = []
885 ns_muc_user_x = self.stanza.getTag('x', namespace=xmpp.NS_MUC_USER)
886 if ns_muc_user_x:
887 destroy = ns_muc_user_x.getTag('destroy')
888 else:
889 destroy = None
890 if ns_muc_user_x and destroy:
891 # Room has been destroyed. see
892 # http://www.xmpp.org/extensions/xep-0045.html#destroyroom
893 self.reason = _('Room has been destroyed')
894 r = destroy.getTagData('reason')
895 if r:
896 self.reason += ' (%s)' % r
897 if destroy.getAttr('jid'):
898 try:
899 jid = helpers.parse_jid(destroy.getAttr('jid'))
900 self.reason += '\n' + \
901 _('You can join this room instead: %s') % jid
902 except helpers.InvalidFormat:
903 pass
904 self.status_code = ['destroyed']
905 else:
906 self.reason = self.stanza.getReason()
907 self.status_code = self.stanza.getStatusCode()
908 self.role = self.stanza.getRole()
909 self.affiliation = self.stanza.getAffiliation()
910 self.real_jid = self.stanza.getJid()
911 self.actor = self.stanza.getActor()
912 self.new_nick = self.stanza.getNewNick()
913 return True
915 class SubscribePresenceReceivedEvent(nec.NetworkIncomingEvent):
916 name = 'subscribe-presence-received'
917 base_network_events = []
919 def generate(self):
920 self.jid = self.presence_obj.jid
921 self.fjid = self.presence_obj.fjid
922 self.status = self.presence_obj.status
923 self.transport_auto_auth = self.presence_obj.transport_auto_auth
924 self.user_nick = self.presence_obj.user_nick
925 return True
927 class SubscribedPresenceReceivedEvent(nec.NetworkIncomingEvent):
928 name = 'subscribed-presence-received'
929 base_network_events = []
931 def generate(self):
932 self.jid = self.presence_obj.jid
933 self.resource = self.presence_obj.resource
934 return True
936 class UnsubscribedPresenceReceivedEvent(nec.NetworkIncomingEvent):
937 name = 'unsubscribed-presence-received'
938 base_network_events = []
940 def generate(self):
941 self.jid = self.presence_obj.jid
942 return True
944 class OurShowEvent(nec.NetworkIncomingEvent):
945 name = 'our-show'
946 base_network_events = []
948 class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
949 name = 'message-received'
950 base_network_events = ['raw-message-received']
952 def generate(self):
953 self.conn = self.base_event.conn
954 self.stanza = self.base_event.stanza
955 self.get_id()
957 account = self.conn.name
959 # check if the message is a roster item exchange (XEP-0144)
960 if self.stanza.getTag('x', namespace=xmpp.NS_ROSTERX):
961 gajim.nec.push_incoming_event(RosterItemExchangeEvent(None,
962 conn=self.conn, stanza=self.stanza))
963 return
965 # check if the message is a XEP-0070 confirmation request
966 if self.stanza.getTag('confirm', namespace=xmpp.NS_HTTP_AUTH):
967 gajim.nec.push_incoming_event(HttpAuthReceivedEvent(None,
968 conn=self.conn, stanza=self.stanza))
969 return
971 try:
972 self.get_jid_resource()
973 except helpers.InvalidFormat:
974 self.conn.dispatch('ERROR', (_('Invalid Jabber ID'),
975 _('A message from a non-valid JID arrived, it has been '
976 'ignored.')))
977 return
979 address_tag = self.stanza.getTag('addresses', namespace=xmpp.NS_ADDRESS)
980 # Be sure it comes from one of our resource, else ignore address element
981 if address_tag and self.jid == gajim.get_jid_from_account(account):
982 address = address_tag.getTag('address', attrs={'type': 'ofrom'})
983 if address:
984 try:
985 self.fjid = helpers.parse_jid(address.getAttr('jid'))
986 except helpers.InvalidFormat:
987 log.warn('Invalid JID: %s, ignoring it' % address.getAttr(
988 'jid'))
989 return
990 self.jid = gajim.get_jid_without_resource(self.fjid)
992 self.enc_tag = self.stanza.getTag('x', namespace=xmpp.NS_ENCRYPTED)
994 self.invite_tag = None
995 if not self.enc_tag:
996 self.invite_tag = self.stanza.getTag('x',
997 namespace=xmpp.NS_MUC_USER)
998 if self.invite_tag and not self.invite_tag.getTag('invite'):
999 self.invite_tag = None
1001 self.thread_id = self.stanza.getThread()
1002 self.mtype = self.stanza.getType()
1003 if not self.mtype or self.mtype not in ('chat', 'groupchat', 'error'):
1004 self.mtype = 'normal'
1006 self.msgtxt = self.stanza.getBody()
1008 self.get_gc_control()
1010 if self.gc_control and self.jid == self.fjid:
1011 # message from a gc without a resource
1012 self.mtype = 'groupchat'
1014 self.session = None
1015 if self.mtype != 'groupchat':
1016 self.session = self.conn.get_or_create_session(self.fjid,
1017 self.thread_id)
1019 if self.thread_id and not self.session.received_thread_id:
1020 self.session.received_thread_id = True
1022 self.session.last_receive = time_time()
1024 # check if the message is a XEP-0020 feature negotiation request
1025 if self.stanza.getTag('feature', namespace=xmpp.NS_FEATURE):
1026 if gajim.HAVE_PYCRYPTO:
1027 feature = self.stanza.getTag(name='feature',
1028 namespace=xmpp.NS_FEATURE)
1029 form = xmpp.DataForm(node=feature.getTag('x'))
1031 if form['FORM_TYPE'] == 'urn:xmpp:ssn':
1032 self.session.handle_negotiation(form)
1033 else:
1034 reply = self.stanza.buildReply()
1035 reply.setType('error')
1036 reply.addChild(feature)
1037 err = xmpp.ErrorNode('service-unavailable', typ='cancel')
1038 reply.addChild(node=err)
1039 self.conn.connection.send(reply)
1040 return
1042 if self.stanza.getTag('init', namespace=xmpp.NS_ESESSION_INIT):
1043 init = self.stanza.getTag(name='init',
1044 namespace=xmpp.NS_ESESSION_INIT)
1045 form = xmpp.DataForm(node=init.getTag('x'))
1047 self.session.handle_negotiation(form)
1049 return
1051 self._generate_timestamp(self.stanza.getTimestamp())
1053 self.encrypted = False
1054 xep_200_encrypted = self.stanza.getTag('c',
1055 namespace=xmpp.NS_STANZA_CRYPTO)
1056 if xep_200_encrypted:
1057 self.encrypted = 'xep200'
1059 return True
1061 class ZeroconfMessageReceivedEvent(MessageReceivedEvent):
1062 name = 'message-received'
1063 base_network_events = []
1065 def get_jid_resource(self):
1066 self.fjid =self.stanza.getFrom()
1068 if self.fjid is None:
1069 for key in self.conn.connection.zeroconf.contacts:
1070 if self.ip == self.conn.connection.zeroconf.contacts[key][
1071 zeroconf.C_ADDRESS]:
1072 self.fjid = key
1073 break
1075 self.fjid = unicode(self.fjid)
1076 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
1078 def generate(self):
1079 self.base_event = nec.NetworkIncomingEvent(None, conn=self.conn,
1080 stanza=self.stanza)
1081 return super(ZeroconfMessageReceivedEvent, self).generate()
1083 class GcInvitationReceivedEvent(nec.NetworkIncomingEvent):
1084 name = 'gc-invitation-received'
1085 base_network_events = []
1087 def generate(self):
1088 self.room_jid = self.msg_obj.fjid
1090 item = self.msg_obj.invite_tag.getTag('invite')
1091 try:
1092 self.jid_from = helpers.parse_jid(item.getAttr('from'))
1093 except helpers.InvalidFormat:
1094 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('from'))
1095 return
1096 jid = gajim.get_jid_without_resource(self.jid_from)
1097 if gajim.config.get_per('accounts', self.conn.name,
1098 'ignore_unknown_contacts') and not gajim.contacts.get_contacts(
1099 self.conn.name, jid):
1100 return
1101 self.reason = item.getTagData('reason')
1102 self.password = self.msg_obj.invite_tag.getTagData('password')
1104 self.is_continued = False
1105 if item.getTag('continue'):
1106 self.is_continued = True
1108 return True
1110 class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1111 name = 'decrypted-message-received'
1112 base_network_events = []
1114 def generate(self):
1115 self.stanza = self.msg_obj.stanza
1116 self.id_ = self.msg_obj.id_
1117 self.jid = self.msg_obj.jid
1118 self.fjid = self.msg_obj.fjid
1119 self.resource = self.msg_obj.resource
1120 self.mtype = self.msg_obj.mtype
1121 self.invite_tag = self.msg_obj.invite_tag
1122 self.thread_id = self.msg_obj.thread_id
1123 self.msgtxt = self.msg_obj.msgtxt
1124 self.gc_control = self.msg_obj.gc_control
1125 self.session = self.msg_obj.session
1126 self.timestamp = self.msg_obj.timestamp
1127 self.encrypted = self.msg_obj.encrypted
1129 self.receipt_request_tag = self.stanza.getTag('request',
1130 namespace=xmpp.NS_RECEIPTS)
1131 self.receipt_received_tag = self.stanza.getTag('received',
1132 namespace=xmpp.NS_RECEIPTS)
1134 self.subject = self.stanza.getSubject()
1136 self.displaymarking = None
1137 self.seclabel = self.stanza.getTag('securitylabel',
1138 namespace=xmpp.NS_SECLABEL)
1139 if self.seclabel:
1140 self.displaymarking = self.seclabel.getTag('displaymarking')
1142 self.form_node = self.stanza.getTag('x', namespace=xmpp.NS_DATA)
1144 if gajim.config.get('ignore_incoming_xhtml'):
1145 self.xhtml = None
1146 else:
1147 self.xhtml = self.stanza.getXHTML()
1149 # XEP-0172 User Nickname
1150 self.user_nick = self.stanza.getTagData('nick') or ''
1152 self.get_chatstate()
1153 return True
1155 class ChatstateReceivedEvent(nec.NetworkIncomingEvent):
1156 name = 'chatstate-received'
1157 base_network_events = []
1159 def generate(self):
1160 self.stanza = self.msg_obj.stanza
1161 self.jid = self.msg_obj.jid
1162 self.fjid = self.msg_obj.fjid
1163 self.resource = self.msg_obj.resource
1164 self.composing_xep = self.msg_obj.composing_xep
1165 self.chatstate = self.msg_obj.chatstate
1166 return True
1168 class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
1169 name = 'gc-message-received'
1170 base_network_events = []
1172 def generate(self):
1173 self.stanza = self.msg_obj.stanza
1174 self.fjid = self.msg_obj.fjid
1175 self.msgtxt = self.msg_obj.msgtxt
1176 self.jid = self.msg_obj.jid
1177 self.room_jid = self.msg_obj.jid
1178 self.nickname = self.msg_obj.resource
1179 self.timestamp = self.msg_obj.timestamp
1180 self.xhtml_msgtxt = self.stanza.getXHTML()
1182 if gajim.config.get('ignore_incoming_xhtml'):
1183 self.xhtml_msgtxt = None
1185 if self.msg_obj.resource:
1186 # message from someone
1187 self.nick = self.msg_obj.resource
1188 else:
1189 # message from server
1190 self.nick = ''
1192 self.has_timestamp = bool(self.stanza.timestamp)
1194 self.subject = self.stanza.getSubject()
1196 if self.subject is not None:
1197 gajim.nec.push_incoming_event(GcSubjectReceivedEvent(None,
1198 conn=self.conn, msg_event=self))
1199 return
1201 self.status_code = self.stanza.getStatusCode()
1203 if not self.stanza.getTag('body'): # no <body>
1204 # It could be a config change. See
1205 # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
1206 if self.stanza.getTag('x'):
1207 if self.status_code != []:
1208 gajim.nec.push_incoming_event(GcConfigChangedReceivedEvent(
1209 None, conn=self.conn, msg_event=self))
1210 return
1212 self.displaymarking = None
1213 seclabel = self.stanza.getTag('securitylabel')
1214 if seclabel and seclabel.getNamespace() == xmpp.NS_SECLABEL:
1215 # Ignore message from room in which we are not
1216 self.displaymarking = seclabel.getTag('displaymarking')
1218 if self.jid not in self.conn.last_history_time:
1219 return
1221 self.captcha_form = None
1222 captcha_tag = self.stanza.getTag('captcha', namespace=xmpp.NS_CAPTCHA)
1223 if captcha_tag:
1224 self.captcha_form = captcha_tag.getTag('x', namespace=xmpp.NS_DATA)
1225 for field in self.captcha_form.getTags('field'):
1226 for media in field.getTags('media'):
1227 for uri in media.getTags('uri'):
1228 uri_data = uri.getData()
1229 if uri_data.startswith('cid:'):
1230 uri_data = uri_data[4:]
1231 found = False
1232 for data in self.stanza.getTags('data',
1233 namespace=xmpp.NS_BOB):
1234 if data.getAttr('cid') == uri_data:
1235 uri.setData(data.getData())
1236 found = True
1237 if not found:
1238 self.conn.get_bob_data(uri_data, self.fjid,
1239 self.conn._dispatch_gc_msg_with_captcha,
1240 [self.stanza, self.msg_obj], 0)
1241 return
1243 return True
1245 class GcSubjectReceivedEvent(nec.NetworkIncomingEvent):
1246 name = 'gc-subject-received'
1247 base_network_events = []
1249 def generate(self):
1250 self.conn = self.msg_event.conn
1251 self.stanza = self.msg_event.stanza
1252 self.room_jid = self.msg_event.room_jid
1253 self.nickname = self.msg_event.nickname
1254 self.fjid = self.msg_event.fjid
1255 self.subject = self.msg_event.subject
1256 self.msgtxt = self.msg_event.msgtxt
1257 self.has_timestamp = self.msg_event.has_timestamp
1258 return True
1260 class GcConfigChangedReceivedEvent(nec.NetworkIncomingEvent):
1261 name = 'gc-config-changed-received'
1262 base_network_events = []
1264 def generate(self):
1265 self.conn = self.msg_event.conn
1266 self.stanza = self.msg_event.stanza
1267 self.room_jid = self.msg_event.room_jid
1268 self.status_code = self.msg_event.status_code
1269 return True
1271 class MessageSentEvent(nec.NetworkIncomingEvent):
1272 name = 'message-sent'
1273 base_network_events = []
1275 class MessageNotSentEvent(nec.NetworkIncomingEvent):
1276 name = 'message-not-sent'
1277 base_network_events = []
1279 class MessageErrorEvent(nec.NetworkIncomingEvent):
1280 name = 'message-error'
1281 base_network_events = []
1283 class AnonymousAuthEvent(nec.NetworkIncomingEvent):
1284 name = 'anonymous-auth'
1285 base_network_events = []
1287 class JingleRequestReceivedEvent(nec.NetworkIncomingEvent):
1288 name = 'jingle-request-received'
1289 base_network_events = []
1291 def generate(self):
1292 self.fjid = self.jingle_session.peerjid
1293 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
1294 self.sid = self.jingle_session.sid
1295 return True
1297 class JingleConnectedReceivedEvent(nec.NetworkIncomingEvent):
1298 name = 'jingle-connected-received'
1299 base_network_events = []
1301 def generate(self):
1302 self.fjid = self.jingle_session.peerjid
1303 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
1304 self.sid = self.jingle_session.sid
1305 return True
1307 class JingleDisconnectedReceivedEvent(nec.NetworkIncomingEvent):
1308 name = 'jingle-disconnected-received'
1309 base_network_events = []
1311 def generate(self):
1312 self.fjid = self.jingle_session.peerjid
1313 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
1314 self.sid = self.jingle_session.sid
1315 return True
1317 class JingleErrorReceivedEvent(nec.NetworkIncomingEvent):
1318 name = 'jingle-error-received'
1319 base_network_events = []
1321 def generate(self):
1322 self.fjid = self.jingle_session.peerjid
1323 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
1324 self.sid = self.jingle_session.sid
1325 return True
1327 class ArchivingReceivedEvent(nec.NetworkIncomingEvent):
1328 name = 'archiving-received'
1329 base_network_events = []
1331 def generate(self):
1332 self.type_ = self.stanza.getType()
1333 if self.type_ not in ('result', 'set', 'error'):
1334 return
1335 return True
1337 class ArchivingErrorReceivedEvent(nec.NetworkIncomingEvent):
1338 name = 'archiving-error-received'
1339 base_network_events = ['archiving-received']
1341 def generate(self):
1342 self.conn = self.base_event.conn
1343 self.stanza = self.base_event.stanza
1344 self.type_ = self.base_event.type_
1346 if self.type_ == 'error':
1347 self.error_msg = self.stanza.getErrorMsg()
1348 return True
1350 class ArchivingPreferencesChangedReceivedEvent(nec.NetworkIncomingEvent):
1351 name = 'archiving-preferences-changed-received'
1352 base_network_events = ['archiving-received']
1354 def generate(self):
1355 self.conn = self.base_event.conn
1356 self.stanza = self.base_event.stanza
1357 self.type_ = self.base_event.type_
1359 if self.type_ not in ('result', 'set'):
1360 return
1362 self.conf = {}
1363 self.new_items = {}
1364 self.removed_items = []
1365 if self.stanza.getTag('pref'):
1366 pref = self.stanza.getTag('pref')
1368 if pref.getTag('auto'):
1369 self.conf['auto'] = pref.getTagAttr('auto', 'save')
1371 method_auto = pref.getTag('method', attrs={'type': 'auto'})
1372 if method_auto:
1373 self.conf['method_auto'] = method_auto.getAttr('use')
1375 method_local = pref.getTag('method', attrs={'type': 'local'})
1376 if method_local:
1377 self.conf['method_local'] = method_local.getAttr('use')
1379 method_manual = pref.getTag('method', attrs={'type': 'manual'})
1380 if method_manual:
1381 self.conf['method_manual'] = method_manual.getAttr('use')
1383 default = pref.getTag('default')
1384 if default:
1385 self.conf['default'] = {
1386 'expire': default.getAttr('expire'),
1387 'otr': default.getAttr('otr'),
1388 'save': default.getAttr('save'),
1389 'unset': default.getAttr('unset')}
1391 for item in pref.getTags('item'):
1392 self.new_items[item.getAttr('jid')] = {
1393 'expire': item.getAttr('expire'),
1394 'otr': item.getAttr('otr'),
1395 'save': item.getAttr('save')}
1397 elif self.stanza.getTag('itemremove'):
1398 for item in pref.getTags('item'):
1399 self.removed_items.append(item.getAttr('jid'))
1400 return True
1402 class AccountCreatedEvent(nec.NetworkIncomingEvent):
1403 name = 'account-created'
1404 base_network_events = []
1406 class AccountNotCreatedEvent(nec.NetworkIncomingEvent):
1407 name = 'account-not-created'
1408 base_network_events = []
1410 class NewAccountConnectedEvent(nec.NetworkIncomingEvent):
1411 name = 'new-account-connected'
1412 base_network_events = []
1414 def generate(self):
1415 try:
1416 self.errnum = self.conn.connection.Connection.ssl_errnum
1417 except AttributeError:
1418 self.errnum = -1 # we don't have an errnum
1419 self.ssl_msg = ''
1420 if self.errnum > 0:
1421 from common.connection import ssl_error
1422 self.ssl_msg = ssl_error.get(self.errnum, _(
1423 'Unknown SSL error: %d') % self.errnum)
1424 self.ssl_cert = ''
1425 if hasattr(self.conn.connection.Connection, 'ssl_cert_pem'):
1426 self.ssl_cert = self.conn.connection.Connection.ssl_cert_pem
1427 self.ssl_fingerprint = ''
1428 if hasattr(self.conn.connection.Connection, 'ssl_fingerprint_sha1'):
1429 self.ssl_fingerprint = \
1430 self.conn.connection.Connection.ssl_fingerprint_sha1
1431 return True
1433 class NewAccountNotConnectedEvent(nec.NetworkIncomingEvent):
1434 name = 'new-account-not-connected'
1435 base_network_events = []
1437 class ConnectionTypeEvent(nec.NetworkIncomingEvent):
1438 name = 'connection-type'
1439 base_network_events = []
1441 class VcardPublishedEvent(nec.NetworkIncomingEvent):
1442 name = 'vcard-published'
1443 base_network_events = []
1445 class VcardNotPublishedEvent(nec.NetworkIncomingEvent):
1446 name = 'vcard-not-published'
1447 base_network_events = []
1449 class StanzaReceivedEvent(nec.NetworkIncomingEvent):
1450 name = 'stanza-received'
1451 base_network_events = []
1453 class StanzaSentEvent(nec.NetworkIncomingEvent):
1454 name = 'stanza-sent'
1455 base_network_events = []
1457 class AgentRemovedEvent(nec.NetworkIncomingEvent):
1458 name = 'agent-removed'
1459 base_network_events = []
1461 def generate(self):
1462 self.jid_list = []
1463 for jid in gajim.contacts.get_jid_list(self.conn.name):
1464 if jid.endswith('@' + self.agent):
1465 self.jid_list.append(jid)
1466 return True
1468 class BadGPGPassphraseEvent(nec.NetworkIncomingEvent):
1469 name = 'bad-gpg-passphrase'
1470 base_network_events = []
1472 def generate(self):
1473 self.account = self.conn.name
1474 self.use_gpg_agent = gajim.config.get('use_gpg_agent')
1475 self.keyID = gajim.config.get_per('accounts', self.conn.name, 'keyid')
1476 return True
1478 class ConnectionLostEvent(nec.NetworkIncomingEvent):
1479 name = 'connection-lost'
1480 base_network_events = []
1482 def generate(self):
1483 gajim.nec.push_incoming_event(OurShowEvent(None, conn=self.conn,
1484 show='offline'))
1485 return True
1487 class PingSentEvent(nec.NetworkIncomingEvent):
1488 name = 'ping-sent'
1489 base_network_events = []
1491 class PingReplyEvent(nec.NetworkIncomingEvent):
1492 name = 'ping-reply'
1493 base_network_events = []
1495 class PingErrorEvent(nec.NetworkIncomingEvent):
1496 name = 'ping-error'
1497 base_network_events = []
1499 class CapsPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent,
1500 PresenceHelperEvent):
1501 name = 'caps-presence-received'
1502 base_network_events = ['raw-pres-received']
1504 def _extract_caps_from_presence(self):
1505 caps_tag = self.stanza.getTag('c', namespace=xmpp.NS_CAPS)
1506 if caps_tag:
1507 self.hash_method = caps_tag['hash']
1508 self.node = caps_tag['node']
1509 self.caps_hash = caps_tag['ver']
1510 else:
1511 self.hash_method = self.node = self.caps_hash = None
1513 def generate(self):
1514 self.conn = self.base_event.conn
1515 self.stanza = self.base_event.stanza
1516 try:
1517 self.get_jid_resource()
1518 except Exception:
1519 return
1520 self._generate_ptype()
1521 self._generate_show()
1522 self._extract_caps_from_presence()
1523 return True
1525 class CapsDiscoReceivedEvent(nec.NetworkIncomingEvent):
1526 name = 'caps-disco-received'
1527 base_network_events = []
1529 class CapsReceivedEvent(nec.NetworkIncomingEvent):
1530 name = 'caps-received'
1531 base_network_events = ['caps-presence-received', 'caps-disco-received']
1533 def generate(self):
1534 self.conn = self.base_event.conn
1535 self.fjid = self.base_event.fjid
1536 self.jid = self.base_event.jid
1537 self.resource = self.base_event.resource
1538 self.client_caps = self.base_event.client_caps
1539 return True
1541 class GPGTrustKeyEvent(nec.NetworkIncomingEvent):
1542 name = 'gpg-trust-key'
1543 base_network_events = []
1545 class GPGPasswordRequiredEvent(nec.NetworkIncomingEvent):
1546 name = 'gpg-password-required'
1547 base_network_events = []
1549 def generate(self):
1550 self.keyid = gajim.config.get_per('accounts', self.conn.name, 'keyid')
1551 return True
1553 class PEPReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1554 name = 'pep-received'
1555 base_network_events = []
1557 def generate(self):
1558 if not self.stanza.getTag('event'):
1559 return
1560 if self.stanza.getTag('error'):
1561 log.debug('PEPReceivedEvent received error stanza. Ignoring')
1562 return
1564 try:
1565 self.get_jid_resource()
1566 except Exception:
1567 return
1569 self.event_tag = self.stanza.getTag('event')
1571 for pep_class in SUPPORTED_PERSONAL_USER_EVENTS:
1572 pep = pep_class.get_tag_as_PEP(self.fjid, self.conn.name,
1573 self.event_tag)
1574 if pep:
1575 self.pep_type = pep.type
1576 return True
1578 items = self.event_tag.getTag('items')
1579 if items:
1580 # for each entry in feed (there shouldn't be more than one, but to
1581 # be sure...
1582 for item in items.getTags('item'):
1583 entry = item.getTag('entry', namespace=xmpp.NS_ATOM)
1584 if entry:
1585 gajim.nec.push_incoming_event(AtomEntryReceived(None,
1586 conn=self.conn, node=entry))
1587 raise xmpp.NodeProcessed
1589 class AtomEntryReceived(nec.NetworkIncomingEvent):
1590 name = 'atom-entry-received'
1591 base_network_events = []
1593 def generate(self):
1594 self.atom_entry = atom.OldEntry(node=self.node)
1595 return True
1597 class PlainConnectionEvent(nec.NetworkIncomingEvent):
1598 name = 'plain-connection'
1599 base_network_events = []
1601 class InsecurePasswordEvent(nec.NetworkIncomingEvent):
1602 name = 'insecure-password'
1603 base_network_events = []
1605 class InsecureSSLConnectionEvent(nec.NetworkIncomingEvent):
1606 name = 'insecure-ssl-connection'
1607 base_network_events = []
1609 class SSLErrorEvent(nec.NetworkIncomingEvent):
1610 name = 'ssl-error'
1611 base_network_events = []
1613 class FingerprintErrorEvent(nec.NetworkIncomingEvent):
1614 name = 'fingerprint-error'
1615 base_network_events = []
1617 class UniqueRoomIdSupportedEvent(nec.NetworkIncomingEvent):
1618 name = 'unique-room-id-supported'
1619 base_network_events = []
1621 class UniqueRoomIdNotSupportedEvent(nec.NetworkIncomingEvent):
1622 name = 'unique-room-id-not-supported'
1623 base_network_events = []
1625 class PrivacyListsReceivedEvent(nec.NetworkIncomingEvent):
1626 name = 'privacy-lists-received'
1627 base_network_events = []
1629 class PrivacyListReceivedEvent(nec.NetworkIncomingEvent):
1630 name = 'privacy-list-received'
1631 base_network_events = []
1633 class PrivacyListRemovedEvent(nec.NetworkIncomingEvent):
1634 name = 'privacy-list-removed'
1635 base_network_events = []
1637 class PrivacyListActiveDefaultEvent(nec.NetworkIncomingEvent):
1638 name = 'privacy-list-active-default'
1639 base_network_events = []
1641 class VcardReceivedEvent(nec.NetworkIncomingEvent):
1642 name = 'vcard-received'
1643 base_network_events = []
1645 def generate(self):
1646 self.nickname = None
1647 if 'NICKNAME' in self.vcard_dict:
1648 self.nickname = self.vcard_dict['NICKNAME']
1649 elif 'FN' in self.vcard_dict:
1650 self.nickname = self.vcard_dict['FN']
1651 self.jid = self.vcard_dict['jid']
1652 self.resource = self.vcard_dict['resource']
1653 self.fjid = self.jid
1654 if self.resource:
1655 self.fjid += '/' + self.resource
1656 return True
1658 class PEPConfigReceivedEvent(nec.NetworkIncomingEvent):
1659 name = 'pep-config-received'
1660 base_network_events = []
1662 class MetacontactsReceivedEvent(nec.NetworkIncomingEvent):
1663 name = 'metacontacts-received'
1664 base_network_events = []
1666 def generate(self):
1667 # Metacontact tags
1668 # http://www.xmpp.org/extensions/xep-0209.html
1669 self.meta_list = {}
1670 query = self.stanza.getTag('query')
1671 storage = query.getTag('storage')
1672 metas = storage.getTags('meta')
1673 for meta in metas:
1674 try:
1675 jid = helpers.parse_jid(meta.getAttr('jid'))
1676 except helpers.InvalidFormat:
1677 continue
1678 tag = meta.getAttr('tag')
1679 data = {'jid': jid}
1680 order = meta.getAttr('order')
1681 try:
1682 order = int(order)
1683 except Exception:
1684 order = 0
1685 if order is not None:
1686 data['order'] = order
1687 if tag in self.meta_list:
1688 self.meta_list[tag].append(data)
1689 else:
1690 self.meta_list[tag] = [data]
1691 return True
1693 class ZeroconfNameConflictEvent(nec.NetworkIncomingEvent):
1694 name = 'zeroconf-name-conflict'
1695 base_network_events = []
1697 class PasswordRequiredEvent(nec.NetworkIncomingEvent):
1698 name = 'password-required'
1699 base_network_events = []
1701 class FailedDecryptEvent(nec.NetworkIncomingEvent):
1702 name = 'failed-decrypt'
1703 base_network_events = []
1705 def generate(self):
1706 self.conn = self.msg_obj.conn
1707 self.fjid = self.msg_obj.fjid
1708 self.timestamp = self.msg_obj.timestamp
1709 self.session = self.msg_obj.session
1710 return True
1712 class SignedInEvent(nec.NetworkIncomingEvent):
1713 name = 'signed-in'
1714 base_network_events = []
1716 class RegisterAgentInfoReceivedEvent(nec.NetworkIncomingEvent):
1717 name = 'register-agent-info-received'
1718 base_network_events = []
1720 class AgentItemsReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1721 name = 'agent-items-received'
1722 base_network_events = []
1724 def generate(self):
1725 q = self.stanza.getTag('query')
1726 self.node = q.getAttr('node')
1727 if not self.node:
1728 self.node = ''
1729 qp = self.stanza.getQueryPayload()
1730 self.items = []
1731 if not qp:
1732 qp = []
1733 for i in qp:
1734 # CDATA payload is not processed, only nodes
1735 if not isinstance(i, xmpp.simplexml.Node):
1736 continue
1737 attr = {}
1738 for key in i.getAttrs():
1739 attr[key] = i.getAttrs()[key]
1740 if 'jid' not in attr:
1741 continue
1742 try:
1743 attr['jid'] = helpers.parse_jid(attr['jid'])
1744 except helpers.InvalidFormat:
1745 # jid is not conform
1746 continue
1747 self.items.append(attr)
1748 self.get_jid_resource()
1749 hostname = gajim.config.get_per('accounts', self.conn.name, 'hostname')
1750 self.get_id()
1751 if self.fjid == hostname and self.id_[:6] == 'Gajim_':
1752 for item in self.items:
1753 self.conn.discoverInfo(item['jid'], id_prefix='Gajim_')
1754 else:
1755 return True
1757 class AgentItemsErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1758 name = 'agent-items-error-received'
1759 base_network_events = []
1761 def generate(self):
1762 self.get_jid_resource()
1763 return True
1765 class AgentInfoReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1766 name = 'agent-info-received'
1767 base_network_events = []
1769 def generate(self):
1770 self.get_id()
1771 if self.id_ is None:
1772 log.warn('Invalid IQ received without an ID. Ignoring it: %s' % \
1773 self.stanza)
1774 return
1775 # According to XEP-0030:
1776 # For identity: category, type is mandatory, name is optional.
1777 # For feature: var is mandatory
1778 self.identities, self.features, self.data = [], [], []
1779 q = self.stanza.getTag('query')
1780 self.node = q.getAttr('node')
1781 if not self.node:
1782 self.node = ''
1783 qc = self.stanza.getQueryChildren()
1784 if not qc:
1785 qc = []
1787 for i in qc:
1788 if i.getName() == 'identity':
1789 attr = {}
1790 for key in i.getAttrs().keys():
1791 attr[key] = i.getAttr(key)
1792 self.identities.append(attr)
1793 elif i.getName() == 'feature':
1794 var = i.getAttr('var')
1795 if var:
1796 self.features.append(var)
1797 elif i.getName() == 'x' and i.getNamespace() == xmpp.NS_DATA:
1798 self.data.append(xmpp.DataForm(node=i))
1800 if not self.identities:
1801 # ejabberd doesn't send identities when we browse online users
1802 # see http://www.jabber.ru/bugzilla/show_bug.cgi?id=225
1803 self.identities = [{'category': 'server', 'type': 'im',
1804 'name': self.node}]
1805 self.get_jid_resource()
1806 return True
1808 class AgentInfoErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1809 name = 'agent-info-error-received'
1810 base_network_events = []
1812 def generate(self):
1813 self.get_jid_resource()
1814 self.get_id()
1815 return True
1817 class FileRequestReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1818 name = 'file-request-received'
1819 base_network_events = []
1821 def generate(self):
1822 self.get_id()
1823 self.fjid = self.conn._ft_get_from(self.stanza)
1824 self.jid = gajim.get_jid_without_resource(self.fjid)
1825 self.file_props = {'type': 'r'}
1826 self.file_props['sender'] = self.fjid
1827 self.file_props['request-id'] = self.id_
1828 si = self.stanza.getTag('si')
1829 profile = si.getAttr('profile')
1830 if profile != xmpp.NS_FILE:
1831 self.conn.send_file_rejection(self.file_props, code='400', typ='profile')
1832 raise xmpp.NodeProcessed
1833 feature_tag = si.getTag('feature', namespace=xmpp.NS_FEATURE)
1834 if not feature_tag:
1835 return
1836 form_tag = feature_tag.getTag('x', namespace=xmpp.NS_DATA)
1837 if not form_tag:
1838 return
1839 self.dataform = dataforms.ExtendForm(node=form_tag)
1840 for f in self.dataform.iter_fields():
1841 if f.var == 'stream-method' and f.type == 'list-single':
1842 values = [o[1] for o in f.options]
1843 self.file_props['stream-methods'] = ' '.join(values)
1844 if xmpp.NS_BYTESTREAM in values or xmpp.NS_IBB in values:
1845 break
1846 else:
1847 self.conn.send_file_rejection(self.file_props, code='400', typ='stream')
1848 raise xmpp.NodeProcessed
1849 file_tag = si.getTag('file')
1850 for attribute in file_tag.getAttrs():
1851 if attribute in ('name', 'size', 'hash', 'date'):
1852 val = file_tag.getAttr(attribute)
1853 if val is None:
1854 continue
1855 self.file_props[attribute] = val
1856 file_desc_tag = file_tag.getTag('desc')
1857 if file_desc_tag is not None:
1858 self.file_props['desc'] = file_desc_tag.getData()
1860 mime_type = si.getAttr('mime-type')
1861 if mime_type is not None:
1862 self.file_props['mime-type'] = mime_type
1864 self.file_props['receiver'] = self.conn._ft_get_our_jid()
1865 self.file_props['sid'] = unicode(si.getAttr('id'))
1866 self.file_props['transfered_size'] = []
1867 return True
1869 class FileRequestErrorEvent(nec.NetworkIncomingEvent):
1870 name = 'file-request-error'
1871 base_network_events = []
1873 def generate(self):
1874 self.jid = gajim.get_jid_without_resource(self.jid)
1875 return True
1877 class GatewayPromptReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1878 name = 'gateway-prompt-received'
1879 base_network_events = []
1881 def generate(self):
1882 self.get_jid_resource()
1883 query = self.stanza.getTag('query')
1884 if query:
1885 self.desc = query.getTagData('desc')
1886 self.prompt = query.getTagData('prompt')
1887 self.prompt_jid = query.getTagData('jid')
1888 else:
1889 self.desc = None
1890 self.prompt = None
1891 self.prompt_jid = None
1892 return True
1894 class NotificationEvent(nec.NetworkIncomingEvent):
1895 name = 'notification'
1896 base_network_events = ['decrypted-message-received', 'gc-message-received']
1898 def detect_type(self):
1899 if self.base_event.name == 'decrypted-message-received':
1900 self.notif_type = 'msg'
1901 if self.base_event.name == 'gc-message-received':
1902 self.notif_type = 'gc-msg'
1904 def get_focused(self):
1905 self.control_focused = False
1906 if self.control:
1907 parent_win = self.control.parent_win
1908 if parent_win and self.control == parent_win.get_active_control() \
1909 and parent_win.window.has_focus:
1910 self.control_focused = True
1912 def handle_incoming_msg_event(self, msg_obj):
1913 if not msg_obj.msgtxt:
1914 return
1915 self.jid = msg_obj.jid
1916 if msg_obj.session:
1917 self.control = msg_obj.session.control
1918 else:
1919 self.control = None
1920 self.get_focused()
1921 # This event has already been added to event list
1922 if not self.control and len(gajim.events.get_events(self.conn.name, \
1923 self.jid, [msg_obj.mtype])) <= 1:
1924 self.first_unread = True
1926 if msg_obj.mtype == 'pm':
1927 nick = msg_obj.resource
1928 else:
1929 nick = gajim.get_name_from_jid(self.conn.name, self.jid)
1931 if self.first_unread:
1932 self.sound_event = 'first_message_received'
1933 elif self.control_focused:
1934 self.sound_event = 'next_message_received_focused'
1935 else:
1936 self.sound_event = 'next_message_received_unfocused'
1938 if gajim.config.get('notification_preview_message'):
1939 self.popup_text = msg_obj.msgtxt
1940 if self.popup_text and (self.popup_text.startswith('/me ') or \
1941 self.popup_text.startswith('/me\n')):
1942 self.popup_text = '* ' + nick + self.popup_text[3:]
1943 else:
1944 # We don't want message preview, do_preview = False
1945 self.popup_text = ''
1946 if msg_obj.mtype == 'normal': # single message
1947 self.popup_event_type = _('New Single Message')
1948 self.popup_image = 'gajim-single_msg_recv'
1949 self.popup_title = _('New Single Message from %(nickname)s') % \
1950 {'nickname': nick}
1951 elif msg_obj.mtype == 'pm':
1952 self.popup_event_type = _('New Private Message')
1953 self.popup_image = 'gajim-priv_msg_recv'
1954 self.popup_title = _('New Private Message from group chat %s') % \
1955 msg_obj.jid
1956 if self.popup_text:
1957 self.popup_text = _('%(nickname)s: %(message)s') % \
1958 {'nickname': nick, 'message': self.popup_text}
1959 else:
1960 self.popup_text = _('Messaged by %(nickname)s') % \
1961 {'nickname': nick}
1962 else: # chat message
1963 self.popup_event_type = _('New Message')
1964 self.popup_image = 'gajim-chat_msg_recv'
1965 self.popup_title = _('New Message from %(nickname)s') % \
1966 {'nickname': nick}
1968 self.popup_image = gtkgui_helpers.get_icon_path(self.popup_image, 48)
1970 if not gajim.config.get('notify_on_new_message') or \
1971 not self.first_unread:
1972 self.do_popup = False
1973 elif gajim.config.get('autopopupaway'):
1974 # always show notification
1975 self.do_popup = True
1976 elif gajim.connections[self.conn.name].connected in (2, 3):
1977 # we're online or chat
1978 self.do_popup = True
1980 if self.first_unread and helpers.allow_sound_notification(
1981 self.conn.name, 'first_message_received'):
1982 self.do_sound = True
1983 elif not self.first_unread and self.control_focused and \
1984 helpers.allow_sound_notification(self.conn.name,
1985 'next_message_received_focused'):
1986 self.do_sound = True
1987 elif not self.first_unread and not self.control_focused and \
1988 helpers.allow_sound_notification(self.conn.name,
1989 'next_message_received_unfocused'):
1990 self.do_sound = True
1992 def handle_incoming_gc_msg_event(self, msg_obj):
1993 sound = msg_obj.msg_obj.gc_control.highlighting_for_message(
1994 msg_obj.msgtxt, msg_obj.timestamp)[1]
1995 self.do_sound = True
1996 if sound == 'received':
1997 self.sound_event = 'muc_message_received'
1998 elif sound == 'highlight':
1999 self.sound_event = 'muc_message_highlight'
2000 else:
2001 self.do_sound = False
2003 self.do_popup = False
2005 def handle_incoming_pres_event(self, msg_obj):
2006 pass
2008 def generate(self):
2009 # what's needed to compute output
2010 self.conn = self.base_event.conn
2011 self.control = None
2012 self.control_focused = False
2013 self.first_unread = False
2015 # For output
2016 self.do_sound = False
2017 self.sound_file = ''
2018 self.sound_event = '' # gajim sound played if not sound_file is set
2019 self.show_popup = False
2021 self.do_popup = False
2022 self.popup_title = ''
2023 self.popup_text = ''
2024 self.popup_event_type = ''
2025 self.popup_msg_type = ''
2026 self.popup_image = ''
2028 self.do_command = False
2029 self.command = ''
2031 self.open_chat = False
2032 self.activate_urgency_hint = False
2033 self.show_in_notification_area = False
2034 self.show_in_roster = False
2036 self.detect_type()
2038 if self.notif_type == 'msg':
2039 self.handle_incoming_msg_event(self.base_event)
2040 elif self.notif_type == 'gc-msg':
2041 self.handle_incoming_gc_msg_event(self.base_event)
2042 elif self.notif_type == 'pres':
2043 self.handle_incoming_pres_event(self.base_event)
2044 return True