# Copyright (C) 2008-2014 Yann Leboulanger # Copyright (C) 2008 Brendan Taylor # Jonathan Schleifer # Stephan Erb # # This file is part of Gajim. # # Gajim is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 3 only. # # Gajim is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Gajim. If not, see . import string import random import itertools from gajim.common import helpers from gajim.common import events from gajim.common import app from gajim.common import contacts from gajim.common import ged from gajim.common.helpers import AdditionalDataDict from gajim.common.const import KindConstant from gajim.gui.util import get_show_in_roster from gajim.gui.util import get_show_in_systray class ChatControlSession: def __init__(self, conn, jid, thread_id, type_='chat'): self.conn = conn self.jid = jid self.type_ = type_ self.resource = jid.resource self.control = None if thread_id: self.received_thread_id = True self.thread_id = thread_id else: self.received_thread_id = False if type_ == 'normal': self.thread_id = None else: self.thread_id = self.generate_thread_id() self.loggable = True self.last_send = 0 self.last_receive = 0 app.ged.register_event_handler('decrypted-message-received', ged.PREGUI, self._nec_decrypted_message_received) def generate_thread_id(self): return ''.join( [f(string.ascii_letters) for f in itertools.repeat( random.choice, 32)] ) def is_loggable(self): return helpers.should_log(self.conn.name, self.jid.bare) def get_to(self): bare_jid = self.jid.bare if not self.resource: return bare_jid return bare_jid + '/' + self.resource def _nec_decrypted_message_received(self, obj): """ Dispatch a received stanza """ if obj.session != self: return if obj.properties.is_muc_pm: contact = app.contacts.get_gc_contact( self.conn.name, obj.jid, obj.resource) else: contact = app.contacts.get_contact( self.conn.name, obj.jid, obj.resource) if self.resource != obj.resource: self.resource = obj.resource if self.control: if isinstance(contact, contacts.GC_Contact): self.control.gc_contact = contact self.control.contact = contact.as_contact() else: self.control.contact = contact if self.control.resource: self.control.change_resource(self.resource) if not obj.msgtxt: return log_type = KindConstant.CHAT_MSG_RECV if obj.properties.is_sent_carbon: log_type = KindConstant.CHAT_MSG_SENT if self.is_loggable() and obj.msgtxt: jid = obj.fjid if not obj.properties.is_muc_pm: jid = obj.jid obj.msg_log_id = app.storage.archive.insert_into_logs( self.conn.name, jid, obj.properties.timestamp, log_type, message=obj.msgtxt, subject=obj.properties.subject, additional_data=obj.additional_data, stanza_id=obj.unique_id, message_id=obj.properties.id) if obj.properties.is_muc_pm and not obj.gc_control: # This is a carbon of a PM from a MUC we are not currently # joined. We log it silently without notification. return True if not obj.msgtxt: # empty message text return True if not self.control: ctrl = app.interface.msg_win_mgr.search_control(obj.jid, obj.conn.name, obj.resource) if ctrl: self.control = ctrl self.control.set_session(self) if isinstance(contact, contacts.GC_Contact): self.control.gc_contact = contact self.control.contact = contact.as_contact() else: self.control.contact = contact if not obj.properties.is_muc_pm: self.roster_message2(obj) def roster_message2(self, obj): """ Display the message or show notification in the roster """ contact = None jid = obj.jid resource = obj.resource fjid = jid # Try to catch the contact with correct resource if resource: fjid = jid + '/' + resource contact = app.contacts.get_contact(obj.conn.name, jid, resource) highest_contact = app.contacts.get_contact_with_highest_priority( obj.conn.name, jid) if not contact: # If there is another resource, it may be a message from an # invisible resource lcontact = app.contacts.get_contacts(obj.conn.name, jid) if (len(lcontact) > 1 or (lcontact and lcontact[0].resource and \ lcontact[0].show != 'offline')) and jid.find('@') > 0: contact = app.contacts.copy_contact(highest_contact) contact.resource = resource contact.priority = 0 contact.show = 'offline' contact.status = '' app.contacts.add_contact(obj.conn.name, contact) else: # Default to highest prio fjid = jid contact = highest_contact if not contact: # contact is not in roster contact = app.interface.roster.add_to_not_in_the_roster( obj.conn.name, jid, obj.properties.nickname) if not self.control: ctrl = app.interface.msg_win_mgr.search_control(obj.jid, obj.conn.name, obj.resource) if ctrl: self.control = ctrl self.control.set_session(self) else: fjid = jid obj.popup = helpers.allow_popup_window(self.conn.name) event_t = events.ChatEvent event_type = 'message_received' if self.control: # We have a ChatControl open obj.show_in_roster = False obj.show_in_systray = False do_event = False elif obj.properties.is_sent_carbon: # Its a Carbon Copied Message we sent obj.show_in_roster = False obj.show_in_systray = False unread_events = app.events.get_events( self.conn.name, fjid, types=['chat']) read_ids = [] for msg in unread_events: read_ids.append(msg.msg_log_id) app.storage.archive.set_read_messages(read_ids) app.events.remove_events(self.conn.name, fjid, types=['chat']) do_event = False else: # Everything else obj.show_in_roster = get_show_in_roster(event_type, self) obj.show_in_systray = get_show_in_systray(event_type, obj.conn.name, contact.jid) do_event = True if do_event: kind = obj.properties.type.value event = event_t( obj.msgtxt, obj.properties.subject, kind, obj.properties.timestamp, obj.resource, obj.msg_log_id, correct_id=obj.correct_id, message_id=obj.properties.id, session=self, displaymarking=obj.displaymarking, sent_forwarded=obj.properties.is_sent_carbon, show_in_roster=obj.show_in_roster, show_in_systray=obj.show_in_systray, additional_data=obj.additional_data) app.events.add_event(self.conn.name, fjid, event) def roster_message(self, jid, msg, tim, msg_type='', subject=None, resource='', msg_log_id=None, user_nick='', displaymarking=None, additional_data=None): """ Display the message or show notification in the roster """ contact = None fjid = jid if additional_data is None: additional_data = AdditionalDataDict() # Try to catch the contact with correct resource if resource: fjid = jid + '/' + resource contact = app.contacts.get_contact(self.conn.name, jid, resource) highest_contact = app.contacts.get_contact_with_highest_priority( self.conn.name, jid) if not contact: # If there is another resource, it may be a message from an invisible # resource lcontact = app.contacts.get_contacts(self.conn.name, jid) if (len(lcontact) > 1 or (lcontact and lcontact[0].resource and \ lcontact[0].show != 'offline')) and jid.find('@') > 0: contact = app.contacts.copy_contact(highest_contact) contact.resource = resource if resource: fjid = jid + '/' + resource contact.priority = 0 contact.show = 'offline' contact.status = '' app.contacts.add_contact(self.conn.name, contact) else: # Default to highest prio fjid = jid contact = highest_contact if not contact: # contact is not in roster contact = app.interface.roster.add_to_not_in_the_roster( self.conn.name, jid, user_nick) if not self.control: ctrl = app.interface.msg_win_mgr.get_control(fjid, self.conn.name) if ctrl: self.control = ctrl self.control.set_session(self) else: fjid = jid # Do we have a queue? no_queue = len(app.events.get_events(self.conn.name, fjid)) == 0 popup = helpers.allow_popup_window(self.conn.name) # We print if window is opened and it's not a single message if self.control: typ = '' if msg_type == 'error': typ = 'error' self.control.add_message(msg, typ, tim=tim, subject=subject, displaymarking=displaymarking, additional_data=additional_data) if msg_log_id: app.storage.archive.set_read_messages([msg_log_id]) return # We save it in a queue event_t = events.ChatEvent event_type = 'message_received' show_in_roster = get_show_in_roster(event_type, self) show_in_systray = get_show_in_systray(event_type, self.conn.name, contact.jid) event = event_t(msg, subject, msg_type, tim, resource, msg_log_id, session=self, displaymarking=displaymarking, sent_forwarded=False, show_in_roster=show_in_roster, show_in_systray=show_in_systray, additional_data=additional_data) app.events.add_event(self.conn.name, fjid, event) if popup: if not self.control: self.control = app.interface.new_chat(contact, self.conn.name, session=self) if app.events.get_events(self.conn.name, fjid): self.control.read_queue() else: if no_queue: # We didn't have a queue: we change icons app.interface.roster.draw_contact(jid, self.conn.name) app.interface.roster.show_title() # we show the * or [n] # Select the big brother contact in roster, it's visible because it has # events. family = app.contacts.get_metacontacts_family(self.conn.name, jid) if family: _nearby_family, bb_jid, bb_account = \ app.contacts.get_nearby_family_and_big_brother(family, self.conn.name) else: bb_jid, bb_account = jid, self.conn.name app.interface.roster.select_contact(bb_jid, bb_account)