use NEC to handle metacontacts events
[gajim.git] / src / common / connection_handlers_events.py
blob86a20c3120038ce02ebbe7007f34d68cc70384ae
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.logger import LOG_DB_PATH
35 from common.pep import SUPPORTED_PERSONAL_USER_EVENTS
37 import logging
38 log = logging.getLogger('gajim.c.connection_handlers_events')
40 class HelperEvent:
41 def get_jid_resource(self, check_fake_jid=False):
42 if check_fake_jid and hasattr(self, 'id_') and \
43 self.id_ in self.conn.groupchat_jids:
44 self.fjid = self.conn.groupchat_jids[self.id_]
45 del self.conn.groupchat_jids[self.id_]
46 else:
47 self.fjid = helpers.get_full_jid_from_iq(self.stanza)
48 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
50 def get_id(self):
51 self.id_ = self.stanza.getID()
53 def get_gc_control(self):
54 self.gc_control = gajim.interface.msg_win_mgr.get_gc_control(self.jid,
55 self.conn.name)
57 # If gc_control is missing - it may be minimized. Try to get it
58 # from there. If it's not there - then it's missing anyway and
59 # will remain set to None.
60 if not self.gc_control:
61 minimized = gajim.interface.minimized_controls[self.conn.name]
62 self.gc_control = minimized.get(self.jid)
64 def _generate_timestamp(self, tag):
65 tim = helpers.datetime_tuple(tag)
66 self.timestamp = localtime(timegm(tim))
68 class HttpAuthReceivedEvent(nec.NetworkIncomingEvent):
69 name = 'http-auth-received'
70 base_network_events = []
72 def generate(self):
73 self.opt = gajim.config.get_per('accounts', self.conn.name, 'http_auth')
74 self.iq_id = self.stanza.getTagAttr('confirm', 'id')
75 self.method = self.stanza.getTagAttr('confirm', 'method')
76 self.url = self.stanza.getTagAttr('confirm', 'url')
77 # In case it's a message with a body
78 self.msg = self.stanza.getTagData('body')
79 return True
81 class LastResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
82 name = 'last-result-received'
83 base_network_events = []
85 def generate(self):
86 self.get_id()
87 self.get_jid_resource(check_fake_jid=True)
88 if self.id_ in self.conn.last_ids:
89 self.conn.last_ids.remove(self.id_)
91 self.status = ''
92 self.seconds = -1
94 if self.stanza.getType() == 'error':
95 return True
97 qp = self.stanza.getTag('query')
98 if not qp:
99 return
100 sec = qp.getAttr('seconds')
101 self.status = qp.getData()
102 try:
103 self.seconds = int(sec)
104 except Exception:
105 return
107 return True
109 class VersionResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
110 name = 'version-result-received'
111 base_network_events = []
113 def generate(self):
114 self.get_id()
115 self.get_jid_resource(check_fake_jid=True)
116 if self.id_ in self.conn.version_ids:
117 self.conn.version_ids.remove(self.id_)
119 self.client_info = ''
120 self.os_info = ''
122 if self.stanza.getType() == 'error':
123 return True
125 qp = self.stanza.getTag('query')
126 if qp.getTag('name'):
127 self.client_info += qp.getTag('name').getData()
128 if qp.getTag('version'):
129 self.client_info += ' ' + qp.getTag('version').getData()
130 if qp.getTag('os'):
131 self.os_info += qp.getTag('os').getData()
133 return True
135 class TimeResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
136 name = 'time-result-received'
137 base_network_events = []
139 def generate(self):
140 self.get_id()
141 self.get_jid_resource(check_fake_jid=True)
142 if self.id_ in self.conn.entity_time_ids:
143 self.conn.entity_time_ids.remove(self.id_)
145 self.time_info = ''
147 if self.stanza.getType() == 'error':
148 return True
150 qp = self.stanza.getTag('time')
151 if not qp:
152 # wrong answer
153 return
154 tzo = qp.getTag('tzo').getData()
155 if tzo.lower() == 'z':
156 tzo = '0:0'
157 tzoh, tzom = tzo.split(':')
158 utc_time = qp.getTag('utc').getData()
159 ZERO = datetime.timedelta(0)
160 class UTC(datetime.tzinfo):
161 def utcoffset(self, dt):
162 return ZERO
163 def tzname(self, dt):
164 return "UTC"
165 def dst(self, dt):
166 return ZERO
168 class contact_tz(datetime.tzinfo):
169 def utcoffset(self, dt):
170 return datetime.timedelta(hours=int(tzoh), minutes=int(tzom))
171 def tzname(self, dt):
172 return "remote timezone"
173 def dst(self, dt):
174 return ZERO
176 try:
177 t = datetime.datetime.strptime(utc_time, '%Y-%m-%dT%H:%M:%SZ')
178 except ValueError, e:
179 try:
180 t = datetime.datetime.strptime(utc_time,
181 '%Y-%m-%dT%H:%M:%S.%fZ')
182 except ValueError, e:
183 log.info('Wrong time format: %s' % str(e))
184 return
186 t = t.replace(tzinfo=UTC())
187 self.time_info = t.astimezone(contact_tz()).strftime('%c')
188 return True
190 class GMailQueryReceivedEvent(nec.NetworkIncomingEvent):
191 name = 'gmail-notify'
192 base_network_events = []
194 def generate(self):
195 if not self.stanza.getTag('mailbox'):
196 return
197 mb = self.stanza.getTag('mailbox')
198 if not mb.getAttr('url'):
199 return
200 self.conn.gmail_url = mb.getAttr('url')
201 if mb.getNamespace() != xmpp.NS_GMAILNOTIFY:
202 return
203 self.newmsgs = mb.getAttr('total-matched')
204 if not self.newmsgs:
205 return
206 if self.newmsgs == '0':
207 return
208 # there are new messages
209 self.gmail_messages_list = []
210 if mb.getTag('mail-thread-info'):
211 gmail_messages = mb.getTags('mail-thread-info')
212 for gmessage in gmail_messages:
213 unread_senders = []
214 for sender in gmessage.getTag('senders').getTags(
215 'sender'):
216 if sender.getAttr('unread') != '1':
217 continue
218 if sender.getAttr('name'):
219 unread_senders.append(sender.getAttr('name') + \
220 '< ' + sender.getAttr('address') + '>')
221 else:
222 unread_senders.append(sender.getAttr('address'))
224 if not unread_senders:
225 continue
226 gmail_subject = gmessage.getTag('subject').getData()
227 gmail_snippet = gmessage.getTag('snippet').getData()
228 tid = int(gmessage.getAttr('tid'))
229 if not self.conn.gmail_last_tid or \
230 tid > self.conn.gmail_last_tid:
231 self.conn.gmail_last_tid = tid
232 self.gmail_messages_list.append({
233 'From': unread_senders,
234 'Subject': gmail_subject,
235 'Snippet': gmail_snippet,
236 'url': gmessage.getAttr('url'),
237 'participation': gmessage.getAttr('participation'),
238 'messages': gmessage.getAttr('messages'),
239 'date': gmessage.getAttr('date')})
240 self.conn.gmail_last_time = int(mb.getAttr('result-time'))
242 self.jid = gajim.get_jid_from_account(self.name)
243 log.debug(('You have %s new gmail e-mails on %s.') % (self.newmsgs,
244 self.jid))
245 return True
247 class RosterItemExchangeEvent(nec.NetworkIncomingEvent, HelperEvent):
248 name = 'roster-item-exchange-received'
249 base_network_events = []
251 def generate(self):
252 self.get_id()
253 self.get_jid_resource()
254 self.exchange_items_list = {}
255 items_list = self.stanza.getTag('x').getChildren()
256 if not items_list:
257 return
258 self.action = items_list[0].getAttr('action')
259 if self.action is None:
260 self.action = 'add'
261 for item in self.stanza.getTag('x', namespace=xmpp.NS_ROSTERX).\
262 getChildren():
263 try:
264 jid = helpers.parse_jid(item.getAttr('jid'))
265 except helpers.InvalidFormat:
266 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('jid'))
267 continue
268 name = item.getAttr('name')
269 contact = gajim.contacts.get_contact(self.conn.name, jid)
270 groups = []
271 same_groups = True
272 for group in item.getTags('group'):
273 groups.append(group.getData())
274 # check that all suggested groups are in the groups we have for
275 # this contact
276 if not contact or group not in contact.groups:
277 same_groups = False
278 if contact:
279 # check that all groups we have for this contact are in the
280 # suggested groups
281 for group in contact.groups:
282 if group not in groups:
283 same_groups = False
284 if contact.sub in ('both', 'to') and same_groups:
285 continue
286 self.exchange_items_list[jid] = []
287 self.exchange_items_list[jid].append(name)
288 self.exchange_items_list[jid].append(groups)
289 if self.exchange_items_list:
290 return True
292 class VersionRequestEvent(nec.NetworkIncomingEvent):
293 name = 'version-request-received'
294 base_network_events = []
296 class LastRequestEvent(nec.NetworkIncomingEvent):
297 name = 'last-request-received'
298 base_network_events = []
300 class TimeRequestEvent(nec.NetworkIncomingEvent):
301 name = 'time-request-received'
302 base_network_events = []
304 class TimeRevisedRequestEvent(nec.NetworkIncomingEvent):
305 name = 'time-revised-request-received'
306 base_network_events = []
308 class RosterReceivedEvent(nec.NetworkIncomingEvent):
309 name = 'roster-received'
310 base_network_events = []
312 def generate(self):
313 if hasattr(self, 'xmpp_roster'):
314 self.version = self.xmpp_roster.version
315 self.received_from_server = self.xmpp_roster.received_from_server
316 self.roster = {}
317 raw_roster = self.xmpp_roster.getRaw()
318 our_jid = gajim.get_jid_from_account(self.conn.name)
320 for jid in raw_roster:
321 try:
322 j = helpers.parse_jid(jid)
323 except Exception:
324 print >> sys.stderr, _('JID %s is not RFC compliant. It '
325 'will not be added to your roster. Use roster '
326 'management tools such as '
327 'http://jru.jabberstudio.org/ to remove it') % jid
328 else:
329 infos = raw_roster[jid]
330 if jid != our_jid and (not infos['subscription'] or \
331 infos['subscription'] == 'none') and (not infos['ask'] or \
332 infos['ask'] == 'none') and not infos['name'] and \
333 not infos['groups']:
334 # remove this useless item, it won't be shown in roster
335 # anyway
336 self.conn.connection.getRoster().delItem(jid)
337 elif jid != our_jid: # don't add our jid
338 self.roster[j] = raw_roster[jid]
339 else:
340 # Roster comes from DB
341 self.received_from_server = False
342 self.version = gajim.config.get_per('accounts', self.conn.name,
343 'roster_version')
344 self.roster = gajim.logger.get_roster(gajim.get_jid_from_account(
345 self.conn.name))
346 return True
348 class RosterSetReceivedEvent(nec.NetworkIncomingEvent):
349 name = 'roster-set-received'
350 base_network_events = []
352 def generate(self):
353 self.version = self.stanza.getTagAttr('query', 'ver')
354 self.items = {}
355 for item in self.stanza.getTag('query').getChildren():
356 try:
357 jid = helpers.parse_jid(item.getAttr('jid'))
358 except helpers.InvalidFormat:
359 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('jid'))
360 continue
361 name = item.getAttr('name')
362 sub = item.getAttr('subscription')
363 ask = item.getAttr('ask')
364 groups = []
365 for group in item.getTags('group'):
366 groups.append(group.getData())
367 self.items[jid] = {'name': name, 'sub': sub, 'ask': ask,
368 'groups': groups}
369 if self.conn.connection and self.conn.connected > 1:
370 reply = xmpp.Iq(typ='result', attrs={'id': self.stanza.getID()},
371 to=self.stanza.getFrom(), frm=self.stanza.getTo(), xmlns=None)
372 self.conn.connection.send(reply)
373 return True
375 class RosterInfoEvent(nec.NetworkIncomingEvent):
376 name = 'roster-info'
377 base_network_events = []
379 class MucOwnerReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
380 name = 'muc-owner-received'
381 base_network_events = []
383 def generate(self):
384 self.get_jid_resource()
385 qp = self.stanza.getQueryPayload()
386 self.form_node = None
387 for q in qp:
388 if q.getNamespace() == xmpp.NS_DATA:
389 self.form_node = q
390 self.dataform = dataforms.ExtendForm(node=self.form_node)
391 return True
393 class MucAdminReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
394 name = 'muc-admin-received'
395 base_network_events = []
397 def generate(self):
398 self.get_jid_resource()
399 items = self.stanza.getTag('query',
400 namespace=xmpp.NS_MUC_ADMIN).getTags('item')
401 self.users_dict = {}
402 for item in items:
403 if item.has_attr('jid') and item.has_attr('affiliation'):
404 try:
405 jid = helpers.parse_jid(item.getAttr('jid'))
406 except helpers.InvalidFormat:
407 log.warn('Invalid JID: %s, ignoring it' % \
408 item.getAttr('jid'))
409 continue
410 affiliation = item.getAttr('affiliation')
411 self.users_dict[jid] = {'affiliation': affiliation}
412 if item.has_attr('nick'):
413 self.users_dict[jid]['nick'] = item.getAttr('nick')
414 if item.has_attr('role'):
415 self.users_dict[jid]['role'] = item.getAttr('role')
416 reason = item.getTagData('reason')
417 if reason:
418 self.users_dict[jid]['reason'] = reason
419 return True
421 class PrivateStorageReceivedEvent(nec.NetworkIncomingEvent):
422 name = 'private-storage-received'
423 base_network_events = []
425 def generate(self):
426 query = self.stanza.getTag('query')
427 self.storage_node = query.getTag('storage')
428 if self.storage_node:
429 self.namespace = self.storage_node.getNamespace()
430 return True
432 class BookmarksHelper:
433 def parse_bookmarks(self):
434 self.bookmarks = []
435 confs = self.base_event.storage_node.getTags('conference')
436 for conf in confs:
437 autojoin_val = conf.getAttr('autojoin')
438 if autojoin_val is None: # not there (it's optional)
439 autojoin_val = False
440 minimize_val = conf.getAttr('minimize')
441 if minimize_val is None: # not there (it's optional)
442 minimize_val = False
443 print_status = conf.getTagData('print_status')
444 if not print_status:
445 print_status = conf.getTagData('show_status')
446 try:
447 jid = helpers.parse_jid(conf.getAttr('jid'))
448 except helpers.InvalidFormat:
449 log.warn('Invalid JID: %s, ignoring it' % conf.getAttr('jid'))
450 continue
451 bm = {'name': conf.getAttr('name'),
452 'jid': jid,
453 'autojoin': autojoin_val,
454 'minimize': minimize_val,
455 'password': conf.getTagData('password'),
456 'nick': conf.getTagData('nick'),
457 'print_status': print_status}
460 bm_jids = [b['jid'] for b in self.bookmarks]
461 if bm['jid'] not in bm_jids:
462 self.bookmarks.append(bm)
464 class PrivateStorageBookmarksReceivedEvent(nec.NetworkIncomingEvent,
465 BookmarksHelper):
466 name = 'private-storage-bookmarks-received'
467 base_network_events = ['private-storage-received']
469 def generate(self):
470 self.conn = self.base_event.conn
471 if self.base_event.namespace != 'storage:bookmarks':
472 return
473 self.parse_bookmarks()
474 return True
476 class BookmarksReceivedEvent(nec.NetworkIncomingEvent):
477 name = 'bookmarks-received'
478 base_network_events = ['private-storage-bookmarks-received',
479 'pubsub-bookmarks-received']
481 def generate(self):
482 self.conn = self.base_event.conn
483 self.bookmarks = self.base_event.bookmarks
484 return True
486 class PrivateStorageRosternotesReceivedEvent(nec.NetworkIncomingEvent):
487 name = 'private-storage-rosternotes-received'
488 base_network_events = ['private-storage-received']
490 def generate(self):
491 self.conn = self.base_event.conn
492 if self.base_event.namespace != 'storage:rosternotes':
493 return
494 notes = self.base_event.storage_node.getTags('note')
495 self.annotations = {}
496 for note in notes:
497 try:
498 jid = helpers.parse_jid(note.getAttr('jid'))
499 except helpers.InvalidFormat:
500 log.warn('Invalid JID: %s, ignoring it' % note.getAttr('jid'))
501 continue
502 annotation = note.getData()
503 self.annotations[jid] = annotation
504 if self.annotations:
505 return True
507 class RosternotesReceivedEvent(nec.NetworkIncomingEvent):
508 name = 'rosternotes-received'
509 base_network_events = ['private-storage-rosternotes-received']
511 def generate(self):
512 self.conn = self.base_event.conn
513 self.annotations = self.base_event.annotations
514 return True
516 class PubsubReceivedEvent(nec.NetworkIncomingEvent):
517 name = 'pubsub-received'
518 base_network_events = []
520 def generate(self):
521 self.pubsub_node = self.stanza.getTag('pubsub')
522 if not self.pubsub_node:
523 return
524 self.items_node = self.pubsub_node.getTag('items')
525 if not self.items_node:
526 return
527 self.item_node = self.items_node.getTag('item')
528 if not self.item_node:
529 return
530 return True
532 class PubsubBookmarksReceivedEvent(nec.NetworkIncomingEvent, BookmarksHelper):
533 name = 'pubsub-bookmarks-received'
534 base_network_events = ['pubsub-received']
536 def generate(self):
537 self.conn = self.base_event.conn
538 storage = self.base_event.item_node.getTag('storage')
539 if not storage:
540 return
541 ns = storage.getNamespace()
542 if ns != 'storage:bookmarks':
543 return
544 self.parse_bookmarks()
545 return True
547 class SearchFormReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
548 name = 'search-form-received'
549 base_network_events = []
551 def generate(self):
552 self.get_jid_resource()
553 self.data = None
554 self.is_dataform = False
555 tag = self.stanza.getTag('query', namespace=xmpp.NS_SEARCH)
556 if not tag:
557 return True
558 self.data = tag.getTag('x', namespace=xmpp.NS_DATA)
559 if self.data:
560 self.is_dataform = True
561 return True
562 self.data = {}
563 for i in self.stanza.getQueryPayload():
564 self.data[i.getName()] = i.getData()
565 return True
568 class SearchResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
569 name = 'search-result-received'
570 base_network_events = []
572 def generate(self):
573 self.get_jid_resource()
574 self.data = None
575 self.is_dataform = False
576 tag = self.stanza.getTag('query', namespace=xmpp.NS_SEARCH)
577 if not tag:
578 return True
579 self.data = tag.getTag('x', namespace=xmpp.NS_DATA)
580 if self.data:
581 self.is_dataform = True
582 return True
583 self.data = []
584 for item in tag.getTags('item'):
585 # We also show attributes. jid is there
586 f = item.attrs
587 for i in item.getPayload():
588 f[i.getName()] = i.getData()
589 self.data.append(f)
590 return True
592 class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
593 name = 'iq-error-received'
594 base_network_events = []
596 def generate(self):
597 self.get_id()
598 self.get_jid_resource(check_fake_jid=True)
599 self.errmsg = self.stanza.getErrorMsg()
600 self.errcode = self.stanza.getErrorCode()
601 return True
603 class GmailNewMailReceivedEvent(nec.NetworkIncomingEvent):
604 name = 'gmail-new-mail-received'
605 base_network_events = []
607 def generate(self):
608 if not self.stanza.getTag('new-mail'):
609 return
610 if self.stanza.getTag('new-mail').getNamespace() != xmpp.NS_GMAILNOTIFY:
611 return
612 return True
614 class PingReceivedEvent(nec.NetworkIncomingEvent):
615 name = 'ping-received'
616 base_network_events = []
618 class StreamReceivedEvent(nec.NetworkIncomingEvent):
619 name = 'stream-received'
620 base_network_events = []
622 class StreamConflictReceivedEvent(nec.NetworkIncomingEvent):
623 name = 'stream-conflict-received'
624 base_network_events = ['stream-received']
626 def generate(self):
627 if self.base_event.stanza.getTag('conflict'):
628 self.conn = self.base_event.conn
629 return True
631 class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
632 name = 'presence-received'
633 base_network_events = ['raw-pres-received']
635 def _generate_keyID(self, sig_tag):
636 self.keyID = ''
637 if sig_tag and self.conn.USE_GPG and self.ptype != 'error':
638 # error presences contain our own signature
639 # verify
640 sig_msg = sig_tag.getData()
641 self.keyID = self.conn.gpg.verify(self.status, sig_msg)
642 self.keyID = helpers.prepare_and_validate_gpg_keyID(self.conn.name,
643 self.jid, self.keyID)
645 def _generate_show(self):
646 self.show = self.stanza.getShow()
647 if self.show not in ('chat', 'away', 'xa', 'dnd'):
648 self.show = '' # We ignore unknown show
649 if not self.ptype and not self.show:
650 self.show = 'online'
651 elif self.ptype == 'unavailable':
652 self.show = 'offline'
654 def _generate_prio(self):
655 self.prio = self.stanza.getPriority()
656 try:
657 self.prio = int(self.prio)
658 except Exception:
659 self.prio = 0
661 def _generate_ptype(self):
662 self.ptype = self.stanza.getType()
663 if self.ptype == 'available':
664 self.ptype = None
665 rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed',
666 'unsubscribe', 'unsubscribed')
667 if self.ptype and not self.ptype in rfc_types:
668 self.ptype = None
670 def generate(self):
671 self.conn = self.base_event.conn
672 self.stanza = self.base_event.stanza
674 self.need_add_in_roster = False
675 self.need_redraw = False
677 if not self.conn or self.conn.connected < 2:
678 log.debug('account is no more connected')
679 return
681 self._generate_ptype()
682 try:
683 self.get_jid_resource()
684 except Exception:
685 return
686 jid_list = gajim.contacts.get_jid_list(self.conn.name)
687 self.timestamp = None
688 self.get_id()
689 self.is_gc = False # is it a GC presence ?
690 sig_tag = None
691 self.avatar_sha = None
692 # XEP-0172 User Nickname
693 self.user_nick = self.stanza.getTagData('nick') or ''
694 self.contact_nickname = None
695 self.transport_auto_auth = False
696 # XEP-0203
697 delay_tag = self.stanza.getTag('delay', namespace=xmpp.NS_DELAY2)
698 if delay_tag:
699 self._generate_timestamp(self.stanza.getTimestamp2())
700 xtags = self.stanza.getTags('x')
701 for x in xtags:
702 namespace = x.getNamespace()
703 if namespace.startswith(xmpp.NS_MUC):
704 self.is_gc = True
705 elif namespace == xmpp.NS_SIGNED:
706 sig_tag = x
707 elif namespace == xmpp.NS_VCARD_UPDATE:
708 self.avatar_sha = x.getTagData('photo')
709 self.contact_nickname = x.getTagData('nickname')
710 elif namespace == xmpp.NS_DELAY and not self.timestamp:
711 # XEP-0091
712 self._generate_timestamp(self.stanza.getTimestamp())
713 elif namespace == 'http://delx.cjb.net/protocol/roster-subsync':
714 # see http://trac.gajim.org/ticket/326
715 agent = gajim.get_server_from_jid(self.jid)
716 if self.conn.connection.getRoster().getItem(agent):
717 # to be sure it's a transport contact
718 self.transport_auto_auth = True
720 if not self.is_gc and self.id_ and self.id_.startswith('gajim_muc_') \
721 and self.ptype == 'error':
722 # Error presences may not include sent stanza, so we don't detect
723 # it's a muc presence. So detect it by ID
724 h = hmac.new(self.conn.secret_hmac, self.jid).hexdigest()[:6]
725 if self.id_.split('_')[-1] == h:
726 self.is_gc = True
727 self.status = self.stanza.getStatus() or ''
728 self._generate_show()
729 self._generate_prio()
730 self._generate_keyID(sig_tag)
732 self.errcode = self.stanza.getErrorCode()
733 self.errmsg = self.stanza.getErrorMsg()
735 if self.is_gc:
736 gajim.nec.push_incoming_event(GcPresenceReceivedEvent(None,
737 conn=self.conn, stanza=self.stanza, presence_obj=self))
738 return
740 if self.ptype == 'subscribe':
741 gajim.nec.push_incoming_event(SubscribePresenceReceivedEvent(None,
742 conn=self.conn, stanza=self.stanza, presence_obj=self))
743 elif self.ptype == 'subscribed':
744 # BE CAREFUL: no con.updateRosterItem() in a callback
745 gajim.nec.push_incoming_event(SubscribedPresenceReceivedEvent(None,
746 conn=self.conn, stanza=self.stanza, presence_obj=self))
747 elif self.ptype == 'unsubscribe':
748 log.debug(_('unsubscribe request from %s') % self.jid)
749 elif self.ptype == 'unsubscribed':
750 gajim.nec.push_incoming_event(UnsubscribedPresenceReceivedEvent(
751 None, conn=self.conn, stanza=self.stanza, presence_obj=self))
752 elif self.ptype == 'error':
753 if self.errcode != '409': # conflict # See #5120
754 self.show = 'error'
755 self.status = self.errmsg
756 return True
758 if not self.ptype or self.ptype == 'unavailable':
759 our_jid = gajim.get_jid_from_account(self.conn.name)
760 if self.jid == our_jid and self.resource == \
761 self.conn.server_resource:
762 # We got our own presence
763 gajim.nec.push_incoming_event(OurShowEvent(None, conn=self.conn,
764 show=self.show))
765 elif self.jid in jid_list or self.jid == our_jid:
766 return True
768 class GcPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
769 name = 'gc-presence-received'
770 base_network_events = []
772 def generate(self):
773 self.ptype = self.presence_obj.ptype
774 self.fjid = self.presence_obj.fjid
775 self.jid = self.presence_obj.jid
776 self.room_jid = self.presence_obj.jid
777 self.nick = self.presence_obj.resource
778 self.show = self.presence_obj.show
779 self.status = self.presence_obj.status
780 self.avatar_sha = self.presence_obj.avatar_sha
781 self.errcode = self.presence_obj.errcode
782 self.errmsg = self.presence_obj.errmsg
783 self.errcon = self.stanza.getError()
784 self.get_gc_control()
785 self.gc_contact = gajim.contacts.get_gc_contact(self.conn.name,
786 self.room_jid, self.nick)
788 if self.ptype == 'error':
789 return True
791 if self.ptype and self.ptype != 'unavailable':
792 return
793 if gajim.config.get('log_contact_status_changes') and \
794 gajim.config.should_log(self.conn.name, self.room_jid):
795 if self.gc_contact:
796 jid = self.gc_contact.jid
797 else:
798 jid = self.stanza.getJid()
799 st = self.status
800 if jid:
801 # we know real jid, save it in db
802 st += ' (%s)' % jid
803 try:
804 gajim.logger.write('gcstatus', self.fjid, st,
805 self.show)
806 except exceptions.PysqliteOperationalError, e:
807 self.conn.dispatch('DB_ERROR', (_('Disk Write Error'),
808 str(e)))
809 except exceptions.DatabaseMalformed:
810 pritext = _('Database Error')
811 sectext = _('The database file (%s) cannot be read. '
812 'Try to repair it (see '
813 'http://trac.gajim.org/wiki/DatabaseBackup) or '
814 'remove it (all history will be lost).') % \
815 LOG_DB_PATH
816 self.conn.dispatch('DB_ERROR', (pritext, sectext))
817 if self.avatar_sha == '':
818 # contact has no avatar
819 puny_nick = helpers.sanitize_filename(self.nick)
820 gajim.interface.remove_avatar_files(self.room_jid, puny_nick)
821 # NOTE: if it's a gc presence, don't ask vcard here.
822 # We may ask it to real jid in gui part.
823 self.status_code = []
824 ns_muc_user_x = self.stanza.getTag('x', namespace=xmpp.NS_MUC_USER)
825 destroy = ns_muc_user_x.getTag('destroy')
826 if ns_muc_user_x and destroy:
827 # Room has been destroyed. see
828 # http://www.xmpp.org/extensions/xep-0045.html#destroyroom
829 self.reason = _('Room has been destroyed')
830 r = destroy.getTagData('reason')
831 if r:
832 self.reason += ' (%s)' % r
833 if destroy.getAttr('jid'):
834 try:
835 jid = helpers.parse_jid(destroy.getAttr('jid'))
836 self.reason += '\n' + \
837 _('You can join this room instead: %s') % jid
838 except common.helpers.InvalidFormat:
839 pass
840 self.status_code = ['destroyed']
841 else:
842 self.reason = self.stanza.getReason()
843 self.status_code = self.stanza.getStatusCode()
844 self.role = self.stanza.getRole()
845 self.affiliation = self.stanza.getAffiliation()
846 self.real_jid = self.stanza.getJid()
847 self.actor = self.stanza.getActor()
848 self.new_nick = self.stanza.getNewNick()
849 return True
851 class SubscribePresenceReceivedEvent(nec.NetworkIncomingEvent):
852 name = 'subscribe-presence-received'
853 base_network_events = []
855 def generate(self):
856 self.jid = self.presence_obj.jid
857 self.fjid = self.presence_obj.fjid
858 self.status = self.presence_obj.status
859 self.transport_auto_auth = self.presence_obj.transport_auto_auth
860 self.user_nick = self.presence_obj.user_nick
861 return True
863 class SubscribedPresenceReceivedEvent(nec.NetworkIncomingEvent):
864 name = 'subscribed-presence-received'
865 base_network_events = []
867 def generate(self):
868 self.jid = self.presence_obj.jid
869 self.resource = self.presence_obj.resource
870 return True
872 class UnsubscribedPresenceReceivedEvent(nec.NetworkIncomingEvent):
873 name = 'unsubscribed-presence-received'
874 base_network_events = []
876 def generate(self):
877 self.jid = self.presence_obj.jid
878 return True
880 class OurShowEvent(nec.NetworkIncomingEvent):
881 name = 'our-show'
882 base_network_events = []
884 class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
885 name = 'message-received'
886 base_network_events = ['raw-message-received']
888 def generate(self):
889 self.conn = self.base_event.conn
890 self.stanza = self.base_event.stanza
891 self.get_id()
893 account = self.conn.name
895 # check if the message is a roster item exchange (XEP-0144)
896 if self.stanza.getTag('x', namespace=xmpp.NS_ROSTERX):
897 gajim.nec.push_incoming_event(RosterItemExchangeEvent(None,
898 conn=self.conn, stanza=self.stanza))
899 return
901 # check if the message is a XEP-0070 confirmation request
902 if self.stanza.getTag('confirm', namespace=xmpp.NS_HTTP_AUTH):
903 gajim.nec.push_incoming_event(HttpAuthReceivedEvent(None,
904 conn=self.conn, stanza=self.stanza))
905 return
907 try:
908 self.get_jid_resource()
909 except helpers.InvalidFormat:
910 self.conn.dispatch('ERROR', (_('Invalid Jabber ID'),
911 _('A message from a non-valid JID arrived, it has been '
912 'ignored.')))
913 return
915 address_tag = self.stanza.getTag('addresses', namespace=xmpp.NS_ADDRESS)
916 # Be sure it comes from one of our resource, else ignore address element
917 if address_tag and self.jid == gajim.get_jid_from_account(account):
918 address = address_tag.getTag('address', attrs={'type': 'ofrom'})
919 if address:
920 try:
921 self.fjid = helpers.parse_jid(address.getAttr('jid'))
922 except helpers.InvalidFormat:
923 log.warn('Invalid JID: %s, ignoring it' % address.getAttr(
924 'jid'))
925 return
926 self.jid = gajim.get_jid_without_resource(self.fjid)
928 self.enc_tag = self.stanza.getTag('x', namespace=xmpp.NS_ENCRYPTED)
930 self.invite_tag = None
931 if not self.enc_tag:
932 self.invite_tag = self.stanza.getTag('x',
933 namespace=xmpp.NS_MUC_USER)
934 if self.invite_tag and not self.invite_tag.getTag('invite'):
935 self.invite_tag = None
937 self.thread_id = self.stanza.getThread()
938 self.mtype = self.stanza.getType()
939 if not self.mtype or self.mtype not in ('chat', 'groupchat', 'error'):
940 self.mtype = 'normal'
942 self.msgtxt = self.stanza.getBody()
944 self.get_gc_control()
946 if self.gc_control and self.jid == self.fjid:
947 # message from a gc without a resource
948 self.mtype = 'groupchat'
950 self.session = None
951 if self.mtype != 'groupchat':
952 self.session = self.conn.get_or_create_session(self.fjid,
953 self.thread_id)
955 if self.thread_id and not self.session.received_thread_id:
956 self.session.received_thread_id = True
958 self.session.last_receive = time_time()
960 # check if the message is a XEP-0020 feature negotiation request
961 if self.stanza.getTag('feature', namespace=xmpp.NS_FEATURE):
962 if gajim.HAVE_PYCRYPTO:
963 feature = self.stanza.getTag(name='feature',
964 namespace=xmpp.NS_FEATURE)
965 form = xmpp.DataForm(node=feature.getTag('x'))
967 if form['FORM_TYPE'] == 'urn:xmpp:ssn':
968 self.session.handle_negotiation(form)
969 else:
970 reply = self.stanza.buildReply()
971 reply.setType('error')
972 reply.addChild(feature)
973 err = xmpp.ErrorNode('service-unavailable', typ='cancel')
974 reply.addChild(node=err)
975 self.conn.connection.send(reply)
976 return
978 if self.stanza.getTag('init', namespace=xmpp.NS_ESESSION_INIT):
979 init = self.stanza.getTag(name='init',
980 namespace=xmpp.NS_ESESSION_INIT)
981 form = xmpp.DataForm(node=init.getTag('x'))
983 self.session.handle_negotiation(form)
985 return
987 self._generate_timestamp(self.stanza.getTimestamp())
989 self.encrypted = False
990 xep_200_encrypted = self.stanza.getTag('c',
991 namespace=xmpp.NS_STANZA_CRYPTO)
992 if xep_200_encrypted:
993 self.encrypted = 'xep200'
995 return True
997 class GcInvitationReceivedEvent(nec.NetworkIncomingEvent):
998 name = 'gc-invitation-received'
999 base_network_events = []
1001 def generate(self):
1002 self.room_jid = self.msg_obj.fjid
1004 item = self.msg_obj.invite_tag.getTag('invite')
1005 try:
1006 self.jid_from = helpers.parse_jid(item.getAttr('from'))
1007 except helpers.InvalidFormat:
1008 log.warn('Invalid JID: %s, ignoring it' % item.getAttr('from'))
1009 return
1010 self.reason = item.getTagData('reason')
1011 self.password = self.msg_obj.invite_tag.getTagData('password')
1013 self.is_continued = False
1014 if item.getTag('continue'):
1015 self.is_continued = True
1017 return True
1019 class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent):
1020 name = 'decrypted-message-received'
1021 base_network_events = []
1023 def generate(self):
1024 self.stanza = self.msg_obj.stanza
1025 self.id_ = self.msg_obj.id_
1026 self.jid = self.msg_obj.jid
1027 self.fjid = self.msg_obj.fjid
1028 self.resource = self.msg_obj.resource
1029 self.mtype = self.msg_obj.mtype
1030 self.invite_tag = self.msg_obj.invite_tag
1031 self.thread_id = self.msg_obj.thread_id
1032 self.msgtxt = self.msg_obj.msgtxt
1033 self.gc_control = self.msg_obj.gc_control
1034 self.session = self.msg_obj.session
1035 self.timestamp = self.msg_obj.timestamp
1036 self.encrypted = self.msg_obj.encrypted
1038 self.receipt_request_tag = self.stanza.getTag('request',
1039 namespace=xmpp.NS_RECEIPTS)
1040 self.receipt_received_tag = self.stanza.getTag('received',
1041 namespace=xmpp.NS_RECEIPTS)
1042 return True
1044 class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
1045 name = 'gc-message-received'
1046 base_network_events = []
1048 def generate(self):
1049 self.stanza = self.msg_obj.stanza
1050 self.fjid = self.msg_obj.fjid
1051 self.msgtxt = self.msg_obj.msgtxt
1052 self.jid = self.msg_obj.jid
1053 self.room_jid = self.msg_obj.jid
1054 self.nickname = self.msg_obj.resource
1055 self.timestamp = self.msg_obj.timestamp
1056 self.xhtml_msgtxt = self.stanza.getXHTML()
1058 if gajim.config.get('ignore_incoming_xhtml'):
1059 self.xhtml_msgtxt = None
1061 if self.msg_obj.resource:
1062 # message from someone
1063 self.nick = self.msg_obj.resource
1064 else:
1065 # message from server
1066 self.nick = ''
1068 self.has_timestamp = bool(self.stanza.timestamp)
1070 self.subject = self.stanza.getSubject()
1072 if self.subject is not None:
1073 gajim.nec.push_incoming_event(GcSubjectReceivedEvent(None,
1074 conn=self.conn, msg_event=self))
1075 return
1077 self.status_code = self.stanza.getStatusCode()
1079 if not self.stanza.getTag('body'): # no <body>
1080 # It could be a config change. See
1081 # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
1082 if self.stanza.getTag('x'):
1083 if self.status_code != []:
1084 gajim.nec.push_incoming_event(GcConfigChangedReceivedEvent(
1085 None, conn=self.conn, msg_event=self))
1086 return
1088 self.displaymarking = None
1089 seclabel = self.stanza.getTag('securitylabel')
1090 if seclabel and seclabel.getNamespace() == xmpp.NS_SECLABEL:
1091 # Ignore message from room in which we are not
1092 self.displaymarking = seclabel.getTag('displaymarking')
1094 if self.jid not in self.conn.last_history_time:
1095 return
1097 self.captcha_form = None
1098 captcha_tag = self.stanza.getTag('captcha', namespace=xmpp.NS_CAPTCHA)
1099 if captcha_tag:
1100 self.captcha_form = captcha_tag.getTag('x', namespace=xmpp.NS_DATA)
1101 for field in self.captcha_form.getTags('field'):
1102 for media in field.getTags('media'):
1103 for uri in media.getTags('uri'):
1104 uri_data = uri.getData()
1105 if uri_data.startswith('cid:'):
1106 uri_data = uri_data[4:]
1107 found = False
1108 for data in self.stanza.getTags('data',
1109 namespace=xmpp.NS_BOB):
1110 if data.getAttr('cid') == uri_data:
1111 uri.setData(data.getData())
1112 found = True
1113 if not found:
1114 self.conn.get_bob_data(uri_data, frm,
1115 self.conn._dispatch_gc_msg_with_captcha,
1116 [self.stanza, self.msg_obj], 0)
1117 return
1119 return True
1121 class GcSubjectReceivedEvent(nec.NetworkIncomingEvent):
1122 name = 'gc-subject-received'
1123 base_network_events = []
1125 def generate(self):
1126 self.conn = self.msg_event.conn
1127 self.stanza = self.msg_event.stanza
1128 self.room_jid = self.msg_event.room_jid
1129 self.nickname = self.msg_event.nickname
1130 self.fjid = self.msg_event.fjid
1131 self.subject = self.msg_event.subject
1132 self.msgtxt = self.msg_event.msgtxt
1133 self.has_timestamp = self.msg_event.has_timestamp
1134 return True
1136 class GcConfigChangedReceivedEvent(nec.NetworkIncomingEvent):
1137 name = 'gc-config-changed-received'
1138 base_network_events = []
1140 def generate(self):
1141 self.conn = self.msg_event.conn
1142 self.stanza = self.msg_event.stanza
1143 self.room_jid = self.msg_event.room_jid
1144 self.status_code = self.msg_event.status_code
1145 return True
1147 class MessageSentEvent(nec.NetworkIncomingEvent):
1148 name = 'message-sent'
1149 base_network_events = []
1151 class AnonymousAuthEvent(nec.NetworkIncomingEvent):
1152 name = 'anonymous-auth'
1153 base_network_events = []
1155 class JingleRequestReceivedEvent(nec.NetworkIncomingEvent):
1156 name = 'jingle-request-received'
1157 base_network_events = []
1159 def generate(self):
1160 self.fjid = self.jingle_session.peerjid
1161 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
1162 self.sid = self.jingle_session.sid
1163 return True
1165 class JingleConnectedReceivedEvent(nec.NetworkIncomingEvent):
1166 name = 'jingle-connected-received'
1167 base_network_events = []
1169 def generate(self):
1170 self.fjid = self.jingle_session.peerjid
1171 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
1172 self.sid = self.jingle_session.sid
1173 return True
1175 class JingleDisconnectedReceivedEvent(nec.NetworkIncomingEvent):
1176 name = 'jingle-disconnected-received'
1177 base_network_events = []
1179 def generate(self):
1180 self.fjid = self.jingle_session.peerjid
1181 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
1182 self.sid = self.jingle_session.sid
1183 return True
1185 class JingleErrorReceivedEvent(nec.NetworkIncomingEvent):
1186 name = 'jingle-error-received'
1187 base_network_events = []
1189 def generate(self):
1190 self.fjid = self.jingle_session.peerjid
1191 self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid)
1192 self.sid = self.jingle_session.sid
1193 return True
1195 class ArchivingReceivedEvent(nec.NetworkIncomingEvent):
1196 name = 'archiving-received'
1197 base_network_events = []
1199 def generate(self):
1200 self.type_ = self.stanza.getType()
1201 if self.type_ not in ('result', 'set', 'error'):
1202 return
1203 return True
1205 class ArchivingErrorReceivedEvent(nec.NetworkIncomingEvent):
1206 name = 'archiving-error-received'
1207 base_network_events = ['archiving-received']
1209 def generate(self):
1210 self.conn = self.base_event.conn
1211 self.stanza = self.base_event.stanza
1212 self.type_ = self.base_event.type_
1214 if self.type_ == 'error':
1215 self.error_msg = self.stanza.getErrorMsg()
1216 return True
1218 class ArchivingPreferencesChangedReceivedEvent(nec.NetworkIncomingEvent):
1219 name = 'archiving-preferences-changed-received'
1220 base_network_events = ['archiving-received']
1222 def generate(self):
1223 self.conn = self.base_event.conn
1224 self.stanza = self.base_event.stanza
1225 self.type_ = self.base_event.type_
1227 if self.type_ not in ('result', 'set'):
1228 return
1230 self.conf = {}
1231 self.new_items = {}
1232 self.removed_items = []
1233 if self.stanza.getTag('pref'):
1234 pref = self.stanza.getTag('pref')
1236 if pref.getTag('auto'):
1237 self.conf['auto'] = pref.getTagAttr('auto', 'save')
1239 method_auto = pref.getTag('method', attrs={'type': 'auto'})
1240 if method_auto:
1241 self.conf['method_auto'] = method_auto.getAttr('use')
1243 method_local = pref.getTag('method', attrs={'type': 'local'})
1244 if method_local:
1245 self.conf['method_local'] = method_local.getAttr('use')
1247 method_manual = pref.getTag('method', attrs={'type': 'manual'})
1248 if method_manual:
1249 self.conf['method_manual'] = method_manual.getAttr('use')
1251 default = pref.getTag('default')
1252 if default:
1253 self.conf['default'] = {
1254 'expire': default.getAttr('expire'),
1255 'otr': default.getAttr('otr'),
1256 'save': default.getAttr('save'),
1257 'unset': default.getAttr('unset')}
1259 for item in pref.getTags('item'):
1260 self.new_items[item.getAttr('jid')] = {
1261 'expire': item.getAttr('expire'),
1262 'otr': item.getAttr('otr'),
1263 'save': item.getAttr('save')}
1265 elif self.stanza.getTag('itemremove'):
1266 for item in pref.getTags('item'):
1267 self.removed_items.append(item.getAttr('jid'))
1268 return True
1270 class AccountCreatedEvent(nec.NetworkIncomingEvent):
1271 name = 'account-created'
1272 base_network_events = []
1274 class AccountNotCreatedEvent(nec.NetworkIncomingEvent):
1275 name = 'account-not-created'
1276 base_network_events = []
1278 class NewAccountConnectedEvent(nec.NetworkIncomingEvent):
1279 name = 'new-account-connected'
1280 base_network_events = []
1282 def generate(self):
1283 try:
1284 self.errnum = self.conn.connection.Connection.ssl_errnum
1285 except AttributeError:
1286 self.errnum = -1 # we don't have an errnum
1287 self.ssl_msg = ''
1288 if self.errnum > 0:
1289 from common.connection import ssl_error
1290 self.ssl_msg = ssl_error.get(errnum, _('Unknown SSL error: %d') % \
1291 errnum)
1292 self.ssl_cert = ''
1293 if hasattr(self.conn.connection.Connection, 'ssl_cert_pem'):
1294 self.ssl_cert = self.conn.connection.Connection.ssl_cert_pem
1295 self.ssl_fingerprint = ''
1296 if hasattr(self.conn.connection.Connection, 'ssl_fingerprint_sha1'):
1297 self.ssl_fingerprint = \
1298 self.conn.connection.Connection.ssl_fingerprint_sha1
1299 return True
1301 class NewAccountNotConnectedEvent(nec.NetworkIncomingEvent):
1302 name = 'new-account-not-connected'
1303 base_network_events = []
1305 class ConnectionTypeEvent(nec.NetworkIncomingEvent):
1306 name = 'connection-type'
1307 base_network_events = []
1309 class VcardPublishedEvent(nec.NetworkIncomingEvent):
1310 name = 'vcard-published'
1311 base_network_events = []
1313 class VcardNotPublishedEvent(nec.NetworkIncomingEvent):
1314 name = 'vcard-not-published'
1315 base_network_events = []
1317 class StanzaReceivedEvent(nec.NetworkIncomingEvent):
1318 name = 'stanza-received'
1319 base_network_events = []
1321 class StanzaSentEvent(nec.NetworkIncomingEvent):
1322 name = 'stanza-sent'
1323 base_network_events = []
1325 class AgentRemovedEvent(nec.NetworkIncomingEvent):
1326 name = 'agent-removed'
1327 base_network_events = []
1329 def generate(self):
1330 self.jid_list = []
1331 for jid in gajim.contacts.get_jid_list(self.conn.name):
1332 if jid.endswith('@' + self.agent):
1333 self.jid_list.append(jid)
1334 return True
1336 class BadGPGPassphraseEvent(nec.NetworkIncomingEvent):
1337 name = 'bad-gpg-passphrase'
1338 base_network_events = []
1340 def generate(self):
1341 self.account = self.conn.name
1342 self.use_gpg_agent = gajim.config.get('use_gpg_agent')
1343 self.keyID = gajim.config.get_per('accounts', self.conn.name, 'keyid')
1344 return True
1346 class ConnectionLostEvent(nec.NetworkIncomingEvent):
1347 name = 'connection-lost'
1348 base_network_events = []
1350 def generate(self):
1351 gajim.nec.push_incoming_event(OurShowEvent(None, conn=self.conn,
1352 show='offline'))
1353 return True
1355 class PingSentEvent(nec.NetworkIncomingEvent):
1356 name = 'ping-sent'
1357 base_network_events = []
1359 class PingReplyEvent(nec.NetworkIncomingEvent):
1360 name = 'ping-reply'
1361 base_network_events = []
1363 class PingErrorEvent(nec.NetworkIncomingEvent):
1364 name = 'ping-error'
1365 base_network_events = []
1367 class CapsPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1368 name = 'caps-presence-received'
1369 base_network_events = ['raw-pres-received']
1371 def _extract_caps_from_presence(self):
1372 caps_tag = self.stanza.getTag('c', namespace=xmpp.NS_CAPS)
1373 if caps_tag:
1374 self.hash_method = caps_tag['hash']
1375 self.node = caps_tag['node']
1376 self.caps_hash = caps_tag['ver']
1377 else:
1378 self.hash_method = self.node = self.caps_hash = None
1380 def generate(self):
1381 self.conn = self.base_event.conn
1382 self.stanza = self.base_event.stanza
1383 try:
1384 self.get_jid_resource()
1385 except Exception:
1386 return
1387 self._extract_caps_from_presence()
1388 return True
1390 class CapsDiscoReceivedEvent(nec.NetworkIncomingEvent):
1391 name = 'caps-disco-received'
1392 base_network_events = []
1394 class CapsReceivedEvent(nec.NetworkIncomingEvent):
1395 name = 'caps-received'
1396 base_network_events = ['caps-presence-received', 'caps-disco-received']
1398 def generate(self):
1399 self.conn = self.base_event.conn
1400 self.fjid = self.base_event.fjid
1401 self.jid = self.base_event.jid
1402 self.resource = self.base_event.resource
1403 self.client_caps = self.base_event.client_caps
1404 return True
1406 class GPGTrustKeyEvent(nec.NetworkIncomingEvent):
1407 name = 'gpg-trust-key'
1408 base_network_events = []
1410 class GPGPasswordRequiredEvent(nec.NetworkIncomingEvent):
1411 name = 'gpg-password-required'
1412 base_network_events = []
1414 def generate(self):
1415 self.keyid = gajim.config.get_per('accounts', self.conn.name, 'keyid')
1416 return True
1418 class PEPReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
1419 name = 'pep-received'
1420 base_network_events = []
1422 def generate(self):
1423 if not self.stanza.getTag('event'):
1424 return
1425 if self.stanza.getTag('error'):
1426 log.debug('PEPReceivedEvent received error stanza. Ignoring')
1427 return
1429 try:
1430 self.get_jid_resource()
1431 except Exception:
1432 return
1434 self.event_tag = self.stanza.getTag('event')
1436 for pep_class in SUPPORTED_PERSONAL_USER_EVENTS:
1437 pep = pep_class.get_tag_as_PEP(self.fjid, self.conn.name,
1438 self.event_tag)
1439 if pep:
1440 self.pep_type = pep.type
1441 return True
1443 items = self.event_tag.getTag('items')
1444 if items:
1445 # for each entry in feed (there shouldn't be more than one, but to
1446 # be sure...
1447 for item in items.getTags('item'):
1448 entry = item.getTag('entry', namespace=xmpp.NS_ATOM)
1449 if entry:
1450 gajim.nec.push_incoming_event(AtomEntryReceived(None,
1451 conn=self.conn, node=entry))
1452 raise xmpp.NodeProcessed
1454 class AtomEntryReceived(nec.NetworkIncomingEvent):
1455 name = 'atom-entry-received'
1456 base_network_events = []
1458 def generate(self):
1459 self.atom_entry = atom.OldEntry(node=self.node)
1460 return True
1462 class PlainConnectionEvent(nec.NetworkIncomingEvent):
1463 name = 'plain-connection'
1464 base_network_events = []
1466 class InsecurePasswordEvent(nec.NetworkIncomingEvent):
1467 name = 'insecure-password'
1468 base_network_events = []
1470 class InsecureSSLConnectionEvent(nec.NetworkIncomingEvent):
1471 name = 'insecure-ssl-connection'
1472 base_network_events = []
1474 class SSLErrorEvent(nec.NetworkIncomingEvent):
1475 name = 'ssl-error'
1476 base_network_events = []
1478 class FingerprintErrorEvent(nec.NetworkIncomingEvent):
1479 name = 'fingerprint-error'
1480 base_network_events = []
1482 class UniqueRoomIdSupportedEvent(nec.NetworkIncomingEvent):
1483 name = 'unique-room-id-supported'
1484 base_network_events = []
1486 class UniqueRoomIdNotSupportedEvent(nec.NetworkIncomingEvent):
1487 name = 'unique-room-id-not-supported'
1488 base_network_events = []
1490 class PrivacyListsReceivedEvent(nec.NetworkIncomingEvent):
1491 name = 'privacy-lists-received'
1492 base_network_events = []
1494 class PrivacyListReceivedEvent(nec.NetworkIncomingEvent):
1495 name = 'privacy-list-received'
1496 base_network_events = []
1498 class PrivacyListRemovedEvent(nec.NetworkIncomingEvent):
1499 name = 'privacy-list-removed'
1500 base_network_events = []
1502 class PrivacyListActiveDefaultEvent(nec.NetworkIncomingEvent):
1503 name = 'privacy-list-active-default'
1504 base_network_events = []
1506 class VcardReceivedEvent(nec.NetworkIncomingEvent):
1507 name = 'vcard-received'
1508 base_network_events = []
1510 def generate(self):
1511 self.nickname = None
1512 if 'NICKNAME' in self.vcard_dict:
1513 self.nickname = self.vcard_dict['NICKNAME']
1514 elif 'FN' in self.vcard_dict:
1515 self.nickname = self.vcard_dict['FN']
1516 self.jid = self.vcard_dict['jid']
1517 self.resource = self.vcard_dict['resource']
1518 self.fjid = self.jid
1519 if self.resource:
1520 self.fjid += '/' + self.resource
1521 return True
1523 class PEPConfigReceivedEvent(nec.NetworkIncomingEvent):
1524 name = 'pep-config-received'
1525 base_network_events = []
1527 class MetacontactsReceivedEvent(nec.NetworkIncomingEvent):
1528 name = 'metacontacts-received'
1529 base_network_events = []
1531 def generate(self):
1532 # Metacontact tags
1533 # http://www.xmpp.org/extensions/xep-0209.html
1534 self.meta_list = {}
1535 query = self.stanza.getTag('query')
1536 storage = self.stanza.getTag('storage')
1537 metas = self.stanza.getTags('meta')
1538 for meta in metas:
1539 try:
1540 jid = helpers.parse_jid(meta.getAttr('jid'))
1541 except common.helpers.InvalidFormat:
1542 continue
1543 tag = meta.getAttr('tag')
1544 data = {'jid': jid}
1545 order = meta.getAttr('order')
1546 try:
1547 order = int(order)
1548 except Exception:
1549 order = 0
1550 if order is not None:
1551 data['order'] = order
1552 if tag in self.meta_list:
1553 self.meta_list[tag].append(data)
1554 else:
1555 self.meta_list[tag] = [data]
1556 return True