2 ## src/common/connection_handlers_events.py
4 ## Copyright (C) 2010 Yann Leboulanger <asterix AT lagaule.org>
6 ## This file is part of Gajim.
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/>.
23 from time
import (localtime
, time
as time_time
)
24 from calendar
import timegm
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
41 log
= logging
.getLogger('gajim.c.connection_handlers_events')
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_
]
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
)
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
,
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
):
73 Extract chatstate from a <message/> stanza
74 Requires self.stanza and self.msgtxt
76 self
.composing_xep
= 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
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'
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
)
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
= []
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')
112 class LastResultReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
113 name
= 'last-result-received'
114 base_network_events
= []
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_
)
125 if self
.stanza
.getType() == 'error':
128 qp
= self
.stanza
.getTag('query')
131 sec
= qp
.getAttr('seconds')
132 self
.status
= qp
.getData()
134 self
.seconds
= int(sec
)
140 class VersionResultReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
141 name
= 'version-result-received'
142 base_network_events
= []
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
= ''
153 if self
.stanza
.getType() == 'error':
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()
162 self
.os_info
+= qp
.getTag('os').getData()
166 class TimeResultReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
167 name
= 'time-result-received'
168 base_network_events
= []
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_
)
178 if self
.stanza
.getType() == 'error':
181 qp
= self
.stanza
.getTag('time')
185 tzo
= qp
.getTag('tzo').getData()
186 if tzo
.lower() == 'z':
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
):
194 def tzname(self
, dt
):
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"
208 t
= datetime
.datetime
.strptime(utc_time
, '%Y-%m-%dT%H:%M:%SZ')
209 except ValueError, e
:
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
))
217 t
= t
.replace(tzinfo
=UTC())
218 self
.time_info
= t
.astimezone(contact_tz()).strftime('%c')
221 class GMailQueryReceivedEvent(nec
.NetworkIncomingEvent
):
222 name
= 'gmail-notify'
223 base_network_events
= []
226 if not self
.stanza
.getTag('mailbox'):
228 mb
= self
.stanza
.getTag('mailbox')
229 if not mb
.getAttr('url'):
231 self
.conn
.gmail_url
= mb
.getAttr('url')
232 if mb
.getNamespace() != xmpp
.NS_GMAILNOTIFY
:
234 self
.newmsgs
= mb
.getAttr('total-matched')
237 if self
.newmsgs
== '0':
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
:
245 for sender
in gmessage
.getTag('senders').getTags(
247 if sender
.getAttr('unread') != '1':
249 if sender
.getAttr('name'):
250 unread_senders
.append(sender
.getAttr('name') + \
251 '< ' + sender
.getAttr('address') + '>')
253 unread_senders
.append(sender
.getAttr('address'))
255 if not unread_senders
:
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
,
278 class RosterItemExchangeEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
279 name
= 'roster-item-exchange-received'
280 base_network_events
= []
284 self
.get_jid_resource()
285 self
.exchange_items_list
= {}
286 items_list
= self
.stanza
.getTag('x').getChildren()
289 self
.action
= items_list
[0].getAttr('action')
290 if self
.action
is None:
292 for item
in self
.stanza
.getTag('x', namespace
=xmpp
.NS_ROSTERX
).\
295 jid
= helpers
.parse_jid(item
.getAttr('jid'))
296 except helpers
.InvalidFormat
:
297 log
.warn('Invalid JID: %s, ignoring it' % item
.getAttr('jid'))
299 name
= item
.getAttr('name')
300 contact
= gajim
.contacts
.get_contact(self
.conn
.name
, jid
)
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
307 if not contact
or group
not in contact
.groups
:
310 # check that all groups we have for this contact are in the
312 for group
in contact
.groups
:
313 if group
not in groups
:
315 if contact
.sub
in ('both', 'to') and same_groups
:
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
:
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
= []
344 if hasattr(self
, 'xmpp_roster'):
345 self
.version
= self
.xmpp_roster
.version
346 self
.received_from_server
= self
.xmpp_roster
.received_from_server
348 raw_roster
= self
.xmpp_roster
.getRaw()
349 our_jid
= gajim
.get_jid_from_account(self
.conn
.name
)
351 for jid
in raw_roster
:
353 j
= helpers
.parse_jid(jid
)
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
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 \
365 # remove this useless item, it won't be shown in roster
367 self
.conn
.connection
.getRoster().delItem(jid
)
368 elif jid
!= our_jid
: # don't add our jid
369 self
.roster
[j
] = raw_roster
[jid
]
371 # Roster comes from DB
372 self
.received_from_server
= False
373 self
.version
= gajim
.config
.get_per('accounts', self
.conn
.name
,
375 self
.roster
= gajim
.logger
.get_roster(gajim
.get_jid_from_account(
379 class RosterSetReceivedEvent(nec
.NetworkIncomingEvent
):
380 name
= 'roster-set-received'
381 base_network_events
= []
384 self
.version
= self
.stanza
.getTagAttr('query', 'ver')
386 for item
in self
.stanza
.getTag('query').getChildren():
388 jid
= helpers
.parse_jid(item
.getAttr('jid'))
389 except helpers
.InvalidFormat
:
390 log
.warn('Invalid JID: %s, ignoring it' % item
.getAttr('jid'))
392 name
= item
.getAttr('name')
393 sub
= item
.getAttr('subscription')
394 ask
= item
.getAttr('ask')
396 for group
in item
.getTags('group'):
397 groups
.append(group
.getData())
398 self
.items
[jid
] = {'name': name
, 'sub': sub
, 'ask': ask
,
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
)
406 class RosterInfoEvent(nec
.NetworkIncomingEvent
):
408 base_network_events
= []
410 class MucOwnerReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
411 name
= 'muc-owner-received'
412 base_network_events
= []
415 self
.get_jid_resource()
416 qp
= self
.stanza
.getQueryPayload()
417 self
.form_node
= None
419 if q
.getNamespace() == xmpp
.NS_DATA
:
421 self
.dataform
= dataforms
.ExtendForm(node
=self
.form_node
)
424 class MucAdminReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
425 name
= 'muc-admin-received'
426 base_network_events
= []
429 self
.get_jid_resource()
430 items
= self
.stanza
.getTag('query',
431 namespace
=xmpp
.NS_MUC_ADMIN
).getTags('item')
434 if item
.has_attr('jid') and item
.has_attr('affiliation'):
436 jid
= helpers
.parse_jid(item
.getAttr('jid'))
437 except helpers
.InvalidFormat
:
438 log
.warn('Invalid JID: %s, ignoring it' % \
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')
449 self
.users_dict
[jid
]['reason'] = reason
452 class PrivateStorageReceivedEvent(nec
.NetworkIncomingEvent
):
453 name
= 'private-storage-received'
454 base_network_events
= []
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()
463 class BookmarksHelper
:
464 def parse_bookmarks(self
):
466 confs
= self
.storage_node
.getTags('conference')
468 autojoin_val
= conf
.getAttr('autojoin')
469 if autojoin_val
is None: # not there (it's optional)
471 minimize_val
= conf
.getAttr('minimize')
472 if minimize_val
is None: # not there (it's optional)
474 print_status
= conf
.getTagData('print_status')
476 print_status
= conf
.getTagData('show_status')
478 jid
= helpers
.parse_jid(conf
.getAttr('jid'))
479 except helpers
.InvalidFormat
:
480 log
.warn('Invalid JID: %s, ignoring it' % conf
.getAttr('jid'))
482 bm
= {'name': conf
.getAttr('name'),
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
,
497 name
= 'private-storage-bookmarks-received'
498 base_network_events
= ['private-storage-received']
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
:
505 self
.parse_bookmarks()
508 class BookmarksReceivedEvent(nec
.NetworkIncomingEvent
):
509 name
= 'bookmarks-received'
510 base_network_events
= ['private-storage-bookmarks-received',
511 'pubsub-bookmarks-received']
514 self
.conn
= self
.base_event
.conn
515 self
.bookmarks
= self
.base_event
.bookmarks
518 class PrivateStorageRosternotesReceivedEvent(nec
.NetworkIncomingEvent
):
519 name
= 'private-storage-rosternotes-received'
520 base_network_events
= ['private-storage-received']
523 self
.conn
= self
.base_event
.conn
524 if self
.base_event
.namespace
!= xmpp
.NS_ROSTERNOTES
:
526 notes
= self
.base_event
.storage_node
.getTags('note')
527 self
.annotations
= {}
530 jid
= helpers
.parse_jid(note
.getAttr('jid'))
531 except helpers
.InvalidFormat
:
532 log
.warn('Invalid JID: %s, ignoring it' % note
.getAttr('jid'))
534 annotation
= note
.getData()
535 self
.annotations
[jid
] = annotation
539 class RosternotesReceivedEvent(nec
.NetworkIncomingEvent
):
540 name
= 'rosternotes-received'
541 base_network_events
= ['private-storage-rosternotes-received']
544 self
.conn
= self
.base_event
.conn
545 self
.annotations
= self
.base_event
.annotations
548 class PubsubReceivedEvent(nec
.NetworkIncomingEvent
):
549 name
= 'pubsub-received'
550 base_network_events
= []
553 self
.pubsub_node
= self
.stanza
.getTag('pubsub')
554 if not self
.pubsub_node
:
556 self
.items_node
= self
.pubsub_node
.getTag('items')
557 if not self
.items_node
:
559 self
.item_node
= self
.items_node
.getTag('item')
560 if not self
.item_node
:
562 children
= self
.item_node
.getChildren()
565 self
.node
= children
[0]
568 class PubsubBookmarksReceivedEvent(nec
.NetworkIncomingEvent
, BookmarksHelper
):
569 name
= 'pubsub-bookmarks-received'
570 base_network_events
= ['pubsub-received']
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
:
578 self
.parse_bookmarks()
581 class SearchFormReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
582 name
= 'search-form-received'
583 base_network_events
= []
586 self
.get_jid_resource()
588 self
.is_dataform
= False
589 tag
= self
.stanza
.getTag('query', namespace
=xmpp
.NS_SEARCH
)
592 self
.data
= tag
.getTag('x', namespace
=xmpp
.NS_DATA
)
594 self
.is_dataform
= True
597 for i
in self
.stanza
.getQueryPayload():
598 self
.data
[i
.getName()] = i
.getData()
602 class SearchResultReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
603 name
= 'search-result-received'
604 base_network_events
= []
607 self
.get_jid_resource()
609 self
.is_dataform
= False
610 tag
= self
.stanza
.getTag('query', namespace
=xmpp
.NS_SEARCH
)
613 self
.data
= tag
.getTag('x', namespace
=xmpp
.NS_DATA
)
615 self
.is_dataform
= True
618 for item
in tag
.getTags('item'):
619 # We also show attributes. jid is there
621 for i
in item
.getPayload():
622 f
[i
.getName()] = i
.getData()
626 class IqErrorReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
627 name
= 'iq-error-received'
628 base_network_events
= []
632 self
.get_jid_resource(check_fake_jid
=True)
633 self
.errmsg
= self
.stanza
.getErrorMsg()
634 self
.errcode
= self
.stanza
.getErrorCode()
637 class GmailNewMailReceivedEvent(nec
.NetworkIncomingEvent
):
638 name
= 'gmail-new-mail-received'
639 base_network_events
= []
642 if not self
.stanza
.getTag('new-mail'):
644 if self
.stanza
.getTag('new-mail').getNamespace() != xmpp
.NS_GMAILNOTIFY
:
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']
661 if self
.base_event
.stanza
.getTag('conflict'):
662 self
.conn
= self
.base_event
.conn
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
:
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':
679 rfc_types
= ('unavailable', 'error', 'subscribe', 'subscribed',
680 'unsubscribe', 'unsubscribed')
681 if self
.ptype
and not self
.ptype
in rfc_types
:
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
):
691 if sig_tag
and self
.conn
.USE_GPG
and self
.ptype
!= 'error':
692 # error presences contain our own signature
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()
702 self
.prio
= int(self
.prio
)
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')
717 self
._generate
_ptype
()
719 self
.get_jid_resource()
722 jid_list
= gajim
.contacts
.get_jid_list(self
.conn
.name
)
723 self
.timestamp
= None
725 self
.is_gc
= False # is it a GC presence ?
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
733 delay_tag
= self
.stanza
.getTag('delay', namespace
=xmpp
.NS_DELAY2
)
735 self
._generate
_timestamp
(self
.stanza
.getTimestamp2())
736 xtags
= self
.stanza
.getTags('x')
738 namespace
= x
.getNamespace()
739 if namespace
.startswith(xmpp
.NS_MUC
):
741 elif namespace
== xmpp
.NS_SIGNED
:
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
:
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
:
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()
772 gajim
.nec
.push_incoming_event(GcPresenceReceivedEvent(None,
773 conn
=self
.conn
, stanza
=self
.stanza
, presence_obj
=self
))
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
791 self
.status
= self
.errmsg
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
,
801 elif self
.jid
in jid_list
or self
.jid
== our_jid
:
804 class ZeroconfPresenceReceivedEvent(nec
.NetworkIncomingEvent
):
805 name
= 'presence-received'
806 base_network_events
= []
809 self
.jid
, self
.resource
= gajim
.get_room_and_nick_from_fjid(self
.fjid
)
810 self
.resource
= 'local'
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'
824 self
.transport_auto_auth
= False
829 class GcPresenceReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
830 name
= 'gc-presence-received'
831 base_network_events
= []
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':
852 if self
.ptype
and self
.ptype
!= 'unavailable':
854 if gajim
.config
.get('log_contact_status_changes') and \
855 gajim
.config
.should_log(self
.conn
.name
, self
.room_jid
):
857 jid
= self
.gc_contact
.jid
859 jid
= self
.stanza
.getJid()
862 # we know real jid, save it in db
865 gajim
.logger
.write('gcstatus', self
.fjid
, st
,
867 except exceptions
.PysqliteOperationalError
, e
:
868 self
.conn
.dispatch('DB_ERROR', (_('Disk Write Error'),
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).') % \
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
)
887 destroy
= ns_muc_user_x
.getTag('destroy')
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')
896 self
.reason
+= ' (%s)' % r
897 if destroy
.getAttr('jid'):
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
:
904 self
.status_code
= ['destroyed']
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()
915 class SubscribePresenceReceivedEvent(nec
.NetworkIncomingEvent
):
916 name
= 'subscribe-presence-received'
917 base_network_events
= []
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
927 class SubscribedPresenceReceivedEvent(nec
.NetworkIncomingEvent
):
928 name
= 'subscribed-presence-received'
929 base_network_events
= []
932 self
.jid
= self
.presence_obj
.jid
933 self
.resource
= self
.presence_obj
.resource
936 class UnsubscribedPresenceReceivedEvent(nec
.NetworkIncomingEvent
):
937 name
= 'unsubscribed-presence-received'
938 base_network_events
= []
941 self
.jid
= self
.presence_obj
.jid
944 class OurShowEvent(nec
.NetworkIncomingEvent
):
946 base_network_events
= []
948 class MessageReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
949 name
= 'message-received'
950 base_network_events
= ['raw-message-received']
953 self
.conn
= self
.base_event
.conn
954 self
.stanza
= self
.base_event
.stanza
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
))
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
))
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 '
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'})
985 self
.fjid
= helpers
.parse_jid(address
.getAttr('jid'))
986 except helpers
.InvalidFormat
:
987 log
.warn('Invalid JID: %s, ignoring it' % address
.getAttr(
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
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'
1015 if self
.mtype
!= 'groupchat':
1016 self
.session
= self
.conn
.get_or_create_session(self
.fjid
,
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
)
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
)
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
)
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'
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
]:
1075 self
.fjid
= unicode(self
.fjid
)
1076 self
.jid
, self
.resource
= gajim
.get_room_and_nick_from_fjid(self
.fjid
)
1079 self
.base_event
= nec
.NetworkIncomingEvent(None, conn
=self
.conn
,
1081 return super(ZeroconfMessageReceivedEvent
, self
).generate()
1083 class GcInvitationReceivedEvent(nec
.NetworkIncomingEvent
):
1084 name
= 'gc-invitation-received'
1085 base_network_events
= []
1088 self
.room_jid
= self
.msg_obj
.fjid
1090 item
= self
.msg_obj
.invite_tag
.getTag('invite')
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'))
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
):
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
1110 class DecryptedMessageReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
1111 name
= 'decrypted-message-received'
1112 base_network_events
= []
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
)
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'):
1147 self
.xhtml
= self
.stanza
.getXHTML()
1149 # XEP-0172 User Nickname
1150 self
.user_nick
= self
.stanza
.getTagData('nick') or ''
1152 self
.get_chatstate()
1155 class ChatstateReceivedEvent(nec
.NetworkIncomingEvent
):
1156 name
= 'chatstate-received'
1157 base_network_events
= []
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
1168 class GcMessageReceivedEvent(nec
.NetworkIncomingEvent
):
1169 name
= 'gc-message-received'
1170 base_network_events
= []
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
1189 # message from server
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
))
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
))
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
:
1221 self
.captcha_form
= None
1222 captcha_tag
= self
.stanza
.getTag('captcha', namespace
=xmpp
.NS_CAPTCHA
)
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:]
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())
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)
1245 class GcSubjectReceivedEvent(nec
.NetworkIncomingEvent
):
1246 name
= 'gc-subject-received'
1247 base_network_events
= []
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
1260 class GcConfigChangedReceivedEvent(nec
.NetworkIncomingEvent
):
1261 name
= 'gc-config-changed-received'
1262 base_network_events
= []
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
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
= []
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
1297 class JingleConnectedReceivedEvent(nec
.NetworkIncomingEvent
):
1298 name
= 'jingle-connected-received'
1299 base_network_events
= []
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
1307 class JingleDisconnectedReceivedEvent(nec
.NetworkIncomingEvent
):
1308 name
= 'jingle-disconnected-received'
1309 base_network_events
= []
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
1317 class JingleErrorReceivedEvent(nec
.NetworkIncomingEvent
):
1318 name
= 'jingle-error-received'
1319 base_network_events
= []
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
1327 class ArchivingReceivedEvent(nec
.NetworkIncomingEvent
):
1328 name
= 'archiving-received'
1329 base_network_events
= []
1332 self
.type_
= self
.stanza
.getType()
1333 if self
.type_
not in ('result', 'set', 'error'):
1337 class ArchivingErrorReceivedEvent(nec
.NetworkIncomingEvent
):
1338 name
= 'archiving-error-received'
1339 base_network_events
= ['archiving-received']
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()
1350 class ArchivingPreferencesChangedReceivedEvent(nec
.NetworkIncomingEvent
):
1351 name
= 'archiving-preferences-changed-received'
1352 base_network_events
= ['archiving-received']
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'):
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'})
1373 self
.conf
['method_auto'] = method_auto
.getAttr('use')
1375 method_local
= pref
.getTag('method', attrs
={'type': 'local'})
1377 self
.conf
['method_local'] = method_local
.getAttr('use')
1379 method_manual
= pref
.getTag('method', attrs
={'type': 'manual'})
1381 self
.conf
['method_manual'] = method_manual
.getAttr('use')
1383 default
= pref
.getTag('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'))
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
= []
1416 self
.errnum
= self
.conn
.connection
.Connection
.ssl_errnum
1417 except AttributeError:
1418 self
.errnum
= -1 # we don't have an errnum
1421 from common
.connection
import ssl_error
1422 self
.ssl_msg
= ssl_error
.get(self
.errnum
, _(
1423 'Unknown SSL error: %d') % self
.errnum
)
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
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
= []
1463 for jid
in gajim
.contacts
.get_jid_list(self
.conn
.name
):
1464 if jid
.endswith('@' + self
.agent
):
1465 self
.jid_list
.append(jid
)
1468 class BadGPGPassphraseEvent(nec
.NetworkIncomingEvent
):
1469 name
= 'bad-gpg-passphrase'
1470 base_network_events
= []
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')
1478 class ConnectionLostEvent(nec
.NetworkIncomingEvent
):
1479 name
= 'connection-lost'
1480 base_network_events
= []
1483 gajim
.nec
.push_incoming_event(OurShowEvent(None, conn
=self
.conn
,
1487 class PingSentEvent(nec
.NetworkIncomingEvent
):
1489 base_network_events
= []
1491 class PingReplyEvent(nec
.NetworkIncomingEvent
):
1493 base_network_events
= []
1495 class PingErrorEvent(nec
.NetworkIncomingEvent
):
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
)
1507 self
.hash_method
= caps_tag
['hash']
1508 self
.node
= caps_tag
['node']
1509 self
.caps_hash
= caps_tag
['ver']
1511 self
.hash_method
= self
.node
= self
.caps_hash
= None
1514 self
.conn
= self
.base_event
.conn
1515 self
.stanza
= self
.base_event
.stanza
1517 self
.get_jid_resource()
1520 self
._generate
_ptype
()
1521 self
._generate
_show
()
1522 self
._extract
_caps
_from
_presence
()
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']
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
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
= []
1550 self
.keyid
= gajim
.config
.get_per('accounts', self
.conn
.name
, 'keyid')
1553 class PEPReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
1554 name
= 'pep-received'
1555 base_network_events
= []
1558 if not self
.stanza
.getTag('event'):
1560 if self
.stanza
.getTag('error'):
1561 log
.debug('PEPReceivedEvent received error stanza. Ignoring')
1565 self
.get_jid_resource()
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
,
1575 self
.pep_type
= pep
.type
1578 items
= self
.event_tag
.getTag('items')
1580 # for each entry in feed (there shouldn't be more than one, but to
1582 for item
in items
.getTags('item'):
1583 entry
= item
.getTag('entry', namespace
=xmpp
.NS_ATOM
)
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
= []
1594 self
.atom_entry
= atom
.OldEntry(node
=self
.node
)
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
):
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
= []
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
1655 self
.fjid
+= '/' + self
.resource
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
= []
1668 # http://www.xmpp.org/extensions/xep-0209.html
1670 query
= self
.stanza
.getTag('query')
1671 storage
= query
.getTag('storage')
1672 metas
= storage
.getTags('meta')
1675 jid
= helpers
.parse_jid(meta
.getAttr('jid'))
1676 except helpers
.InvalidFormat
:
1678 tag
= meta
.getAttr('tag')
1680 order
= meta
.getAttr('order')
1685 if order
is not None:
1686 data
['order'] = order
1687 if tag
in self
.meta_list
:
1688 self
.meta_list
[tag
].append(data
)
1690 self
.meta_list
[tag
] = [data
]
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
= []
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
1712 class SignedInEvent(nec
.NetworkIncomingEvent
):
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
= []
1725 q
= self
.stanza
.getTag('query')
1726 self
.node
= q
.getAttr('node')
1729 qp
= self
.stanza
.getQueryPayload()
1734 # CDATA payload is not processed, only nodes
1735 if not isinstance(i
, xmpp
.simplexml
.Node
):
1738 for key
in i
.getAttrs():
1739 attr
[key
] = i
.getAttrs()[key
]
1740 if 'jid' not in attr
:
1743 attr
['jid'] = helpers
.parse_jid(attr
['jid'])
1744 except helpers
.InvalidFormat
:
1745 # jid is not conform
1747 self
.items
.append(attr
)
1748 self
.get_jid_resource()
1749 hostname
= gajim
.config
.get_per('accounts', self
.conn
.name
, 'hostname')
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_')
1757 class AgentItemsErrorReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
1758 name
= 'agent-items-error-received'
1759 base_network_events
= []
1762 self
.get_jid_resource()
1765 class AgentInfoReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
1766 name
= 'agent-info-received'
1767 base_network_events
= []
1771 if self
.id_
is None:
1772 log
.warn('Invalid IQ received without an ID. Ignoring it: %s' % \
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')
1783 qc
= self
.stanza
.getQueryChildren()
1788 if i
.getName() == 'identity':
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')
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',
1805 self
.get_jid_resource()
1808 class AgentInfoErrorReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
1809 name
= 'agent-info-error-received'
1810 base_network_events
= []
1813 self
.get_jid_resource()
1817 class FileRequestReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
1818 name
= 'file-request-received'
1819 base_network_events
= []
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
)
1836 form_tag
= feature_tag
.getTag('x', namespace
=xmpp
.NS_DATA
)
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
:
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
)
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'] = []
1869 class FileRequestErrorEvent(nec
.NetworkIncomingEvent
):
1870 name
= 'file-request-error'
1871 base_network_events
= []
1874 self
.jid
= gajim
.get_jid_without_resource(self
.jid
)
1877 class GatewayPromptReceivedEvent(nec
.NetworkIncomingEvent
, HelperEvent
):
1878 name
= 'gateway-prompt-received'
1879 base_network_events
= []
1882 self
.get_jid_resource()
1883 query
= self
.stanza
.getTag('query')
1885 self
.desc
= query
.getTagData('desc')
1886 self
.prompt
= query
.getTagData('prompt')
1887 self
.prompt_jid
= query
.getTagData('jid')
1891 self
.prompt_jid
= None
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
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
:
1915 self
.jid
= msg_obj
.jid
1917 self
.control
= msg_obj
.session
.control
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
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'
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:]
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') % \
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') % \
1957 self
.popup_text
= _('%(nickname)s: %(message)s') % \
1958 {'nickname': nick
, 'message': self
.popup_text
}
1960 self
.popup_text
= _('Messaged by %(nickname)s') % \
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') % \
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'
2001 self
.do_sound
= False
2003 self
.do_popup
= False
2005 def handle_incoming_pres_event(self
, msg_obj
):
2009 # what's needed to compute output
2010 self
.conn
= self
.base_event
.conn
2012 self
.control_focused
= False
2013 self
.first_unread
= False
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
2031 self
.show_in_notification_area
= False
2032 self
.show_in_roster
= False
2036 if self
.notif_type
== 'msg':
2037 self
.handle_incoming_msg_event(self
.base_event
)
2038 elif self
.notif_type
== 'gc-msg':
2039 self
.handle_incoming_gc_msg_event(self
.base_event
)
2040 elif self
.notif_type
== 'pres':
2041 self
.handle_incoming_pres_event(self
.base_event
)
2044 class MessageOutgoingEvent(nec
.NetworkIncomingEvent
):
2045 name
= 'message-outgoing'
2046 base_network_events
= []
2053 self
.chatstate
= None
2055 self
.composing_xep
= None
2056 self
.resource
= None
2057 self
.user_nick
= None
2061 self
.forward_from
= None
2062 self
.form_node
= None
2063 self
.original_message
= ''
2065 self
.callback
= None
2066 self
.callback_args
= []
2068 self
.is_loggable
= True
2073 class ClientCertPassphraseEvent(nec
.NetworkIncomingEvent
):
2074 name
= 'client-cert-passphrase'
2075 base_network_events
= []