# 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 import notify 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.gtk.single_message import SingleMessageWindow class ChatControlSession: def __init__(self, conn, jid, thread_id, type_='chat'): self.conn = conn self.jid = jid self.type_ = type_ self.resource = jid.getResource() 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 app.config.should_log(self.conn.name, self.jid.getStripped()) def get_to(self): to = str(self.jid) return app.get_jid_without_resource(to) + '/' + self.resource def _nec_decrypted_message_received(self, obj): """ Dispatch a received stanza """ if obj.session != self: return if obj.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 obj.mtype == 'chat': if not obj.msgtxt: return log_type = KindConstant.CHAT_MSG_RECV if obj.forwarded and obj.sent: log_type = KindConstant.CHAT_MSG_SENT else: log_type = KindConstant.SINGLE_MSG_RECV if obj.forwarded and obj.sent: log_type = KindConstant.SINGLE_MSG_SENT treat_as = app.config.get('treat_incoming_messages') if treat_as: obj.mtype = treat_as pm = False if obj.muc_pm or (obj.gc_control and obj.resource): # It's a Private message pm = True obj.mtype = 'pm' if self.is_loggable() and obj.msgtxt: if obj.xhtml and app.config.get('log_xhtml_messages'): msg_to_log = obj.xhtml else: msg_to_log = obj.msgtxt jid = obj.fjid if not pm: jid = obj.jid obj.msg_log_id = app.logger.insert_into_logs( self.conn.name, jid, obj.timestamp, log_type, message=msg_to_log, subject=obj.subject, additional_data=obj.additional_data, stanza_id=obj.unique_id) self.conn.get_module('MAM').save_archive_id( None, obj.stanza_id, obj.timestamp) if obj.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 app.config.get_per('accounts', self.conn.name, 'ignore_unknown_contacts') and not app.contacts.get_contacts( self.conn.name, obj.jid) and not pm: 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 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.user_nick) 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 obj.mtype == 'normal': event_t = events.NormalEvent event_type = 'single_message_received' if self.control and obj.mtype != 'normal': # We have a ChatControl open obj.show_in_roster = False obj.show_in_systray = False do_event = False elif obj.forwarded and obj.sent: # 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.logger.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 = notify.get_show_in_roster(event_type, self.conn.name, contact.jid, self) obj.show_in_systray = notify.get_show_in_systray(event_type, self.conn.name, contact.jid) if obj.mtype == 'normal' and obj.popup: do_event = False else: do_event = True if do_event: event = event_t(obj.msgtxt, obj.subject, obj.mtype, obj.timestamp, obj.encrypted, obj.resource, obj.msg_log_id, correct_id=(obj.id_, obj.correct_id), xhtml=obj.xhtml, session=self, form_node=obj.form_node, displaymarking=obj.displaymarking, sent_forwarded=obj.forwarded and obj.sent, 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, encrypted=False, msg_type='', subject=None, resource='', msg_log_id=None, user_nick='', xhtml=None, form_node=None, 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) if msg_type == 'normal' and popup: # it's single message to be autopopuped SingleMessageWindow(self.conn.name, contact.jid, action='receive', from_whom=jid, subject=subject, message=msg, resource=resource, session=self, form_node=form_node) return # We print if window is opened and it's not a single message if self.control and msg_type != 'normal': typ = '' if msg_type == 'error': typ = 'error' self.control.print_conversation(msg, typ, tim=tim, encrypted=encrypted, subject=subject, xhtml=xhtml, displaymarking=displaymarking, additional_data=additional_data) if msg_log_id: app.logger.set_read_messages([msg_log_id]) return # We save it in a queue event_t = events.ChatEvent event_type = 'message_received' if msg_type == 'normal': event_t = events.NormalEvent event_type = 'single_message_received' show_in_roster = notify.get_show_in_roster(event_type, self.conn.name, contact.jid, self) show_in_systray = notify.get_show_in_systray(event_type, self.conn.name, contact.jid) event = event_t(msg, subject, msg_type, tim, encrypted, resource, msg_log_id, xhtml=xhtml, session=self, form_node=form_node, 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)