Beispiel #1
0
    def command_say(self, line, attention=False, correct=False):
        msg = self.core.xmpp.make_message(self.get_dest_jid())
        msg['type'] = 'chat'
        msg['body'] = line
        if not self.nick_sent:
            msg['nick'] = self.core.own_nick
            self.nick_sent = True
        # trigger the event BEFORE looking for colors.
        # and before displaying the message in the window
        # This lets a plugin insert \x19xxx} colors, that will
        # be converted in xhtml.
        self.core.events.trigger('conversation_say', msg, self)
        if not msg['body']:
            self.cancel_paused_delay()
            self.text_win.refresh()
            self.input.refresh()
            return
        replaced = False
        if correct or msg['replace']['id']:
            msg['replace']['id'] = self.last_sent_message['id']
            if config.get_by_tabname('group_corrections', self.name):
                try:
                    self.modify_message(msg['body'], self.last_sent_message['id'], msg['id'], jid=self.core.xmpp.boundjid,
                            nickname=self.core.own_nick)
                    replaced = True
                except CorrectionError:
                    log.error('Unable to correct a message', exc_info=True)
        else:
            del msg['replace']
        if msg['body'].find('\x19') != -1:
            msg.enable('html')
            msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
            msg['body'] = xhtml.clean_text(msg['body'])
        if (config.get_by_tabname('send_chat_states', self.general_jid) and
                self.remote_wants_chatstates is not False):
            needed = 'inactive' if self.inactive else 'active'
            msg['chat_state'] = needed
        if attention and self.remote_supports_attention:
            msg['attention'] = True
        self.core.events.trigger('conversation_say_after', msg, self)
        if not msg['body']:
            self.cancel_paused_delay()
            self.text_win.refresh()
            self.input.refresh()
            return
        if not replaced:
            self.add_message(msg['body'],
                    nickname=self.core.own_nick,
                    nick_color=get_theme().COLOR_OWN_NICK,
                    identifier=msg['id'],
                    jid=self.core.xmpp.boundjid,
                    typ=1)

        self.last_sent_message = msg
        if self.remote_supports_receipts:
            msg._add_receipt = True
        msg.send()
        self.cancel_paused_delay()
        self.text_win.refresh()
        self.input.refresh()
    def command_say(self, line, attention=False, correct=False):
        msg = self.core.xmpp.make_message(self.get_dest_jid())
        msg['type'] = 'chat'
        msg['body'] = line
        if not self.nick_sent:
            msg['nick'] = self.core.own_nick
            self.nick_sent = True
        # trigger the event BEFORE looking for colors.
        # and before displaying the message in the window
        # This lets a plugin insert \x19xxx} colors, that will
        # be converted in xhtml.
        self.core.events.trigger('conversation_say', msg, self)
        if not msg['body']:
            self.cancel_paused_delay()
            self.text_win.refresh()
            self.input.refresh()
            return
        replaced = False
        if correct or msg['replace']['id']:
            msg['replace']['id'] = self.last_sent_message['id']
            if config.get_by_tabname('group_corrections', self.name):
                try:
                    self.modify_message(msg['body'], self.last_sent_message['id'], msg['id'], jid=self.core.xmpp.boundjid,
                            nickname=self.core.own_nick)
                    replaced = True
                except:
                    log.error('Unable to correct a message', exc_info=True)
        else:
            del msg['replace']
        if msg['body'].find('\x19') != -1:
            msg.enable('html')
            msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
            msg['body'] = xhtml.clean_text(msg['body'])
        if (config.get_by_tabname('send_chat_states', self.general_jid) and
                self.remote_wants_chatstates is not False):
            needed = 'inactive' if self.inactive else 'active'
            msg['chat_state'] = needed
        if attention and self.remote_supports_attention:
            msg['attention'] = True
        self.core.events.trigger('conversation_say_after', msg, self)
        if not msg['body']:
            self.cancel_paused_delay()
            self.text_win.refresh()
            self.input.refresh()
            return
        if not replaced:
            self.add_message(msg['body'],
                    nickname=self.core.own_nick,
                    nick_color=get_theme().COLOR_OWN_NICK,
                    identifier=msg['id'],
                    jid=self.core.xmpp.boundjid,
                    typ=1)

        self.last_sent_message = msg
        if self.remote_supports_receipts:
            msg._add_receipt = True
        msg.send()
        self.cancel_paused_delay()
        self.text_win.refresh()
        self.input.refresh()
Beispiel #3
0
 def get_local(self):
     """Add the locally stored bookmarks to the list."""
     rooms = config.get('rooms')
     if not rooms:
         return
     rooms = rooms.split(':')
     for room in rooms:
         try:
             jid = JID(room)
         except InvalidJID:
             continue
         if jid.bare == '':
             continue
         if jid.resource != '':
             nick = jid.resource
         else:
             nick = None
         passwd = config.get_by_tabname(
             'password', jid.bare, fallback=False) or None
         b = Bookmark(jid.bare,
                      jid.user,
                      autojoin=True,
                      nick=nick,
                      password=passwd,
                      method='local')
         self.append(b)
Beispiel #4
0
 def _check_and_create_log_dir(self, room, open_fd=True):
     """
     Check that the directory where we want to log the messages
     exists. if not, create it
     """
     if not config.get_by_tabname('use_log', room):
         return
     try:
         makedirs(log_dir)
     except OSError as e:
         if e.errno != 17: # file exists
             log.error('Unable to create the log dir', exc_info=True)
     except:
         log.error('Unable to create the log dir', exc_info=True)
         return
     if not open_fd:
         return
     try:
         fd = open(os.path.join(log_dir, room), 'a')
         self._fds[room] = fd
         return fd
     except IOError:
         log.error('Unable to open the log file (%s)',
                 os.path.join(log_dir, room),
                 exc_info=True)
Beispiel #5
0
 def _check_and_create_log_dir(self, room, open_fd=True):
     """
     Check that the directory where we want to log the messages
     exists. if not, create it
     """
     if not config.get_by_tabname('use_log', room):
         return
     try:
         makedirs(log_dir)
     except OSError as e:
         if e.errno != 17:  # file exists
             log.error('Unable to create the log dir', exc_info=True)
     except:
         log.error('Unable to create the log dir', exc_info=True)
         return
     if not open_fd:
         return
     try:
         fd = open(os.path.join(log_dir, room), 'a', encoding='utf-8')
         self._fds[room] = fd
         return fd
     except IOError:
         log.error('Unable to open the log file (%s)',
                   os.path.join(log_dir, room),
                   exc_info=True)
Beispiel #6
0
 def log_roster_change(self, jid, message):
     """
     Log a roster change
     """
     if not config.get_by_tabname('use_log', jid):
         return True
     self._check_and_create_log_dir('', open_fd=False)
     if not self._roster_logfile:
         try:
             self._roster_logfile = open(os.path.join(
                 log_dir, 'roster.log'),
                                         'a',
                                         encoding='utf-8')
         except IOError:
             log.error('Unable to create the log file (%s)',
                       os.path.join(log_dir, 'roster.log'),
                       exc_info=True)
             return False
     try:
         str_time = common.get_utc_time().strftime('%Y%m%dT%H:%M:%SZ')
         message = clean_text(message)
         lines = message.split('\n')
         first_line = lines.pop(0)
         nb_lines = str(len(lines)).zfill(3)
         self._roster_logfile.write('MI %s %s %s %s\n' %
                                    (str_time, nb_lines, jid, first_line))
         for line in lines:
             self._roster_logfile.write(' %s\n' % line)
         self._roster_logfile.flush()
     except:
         log.error('Unable to write in the log file (%s)',
                   os.path.join(log_dir, 'roster.log'),
                   exc_info=True)
         return False
     return True
Beispiel #7
0
 def log_message(self, jid, nick, msg, date=None, typ=1):
     """
     log the message in the appropriate jid's file
     type:
           0 = Don’t log
           1 = Message
           2 = Status/whatever
     """
     if not config.get_by_tabname('use_log', jid):
         return True
     logged_msg = build_log_message(nick, msg, date=date, typ=typ)
     if not logged_msg:
         return True
     if jid in self._fds.keys():
         fd = self._fds[jid]
     else:
         fd = self._check_and_create_log_dir(jid)
     if not fd:
         return True
     try:
         fd.write(logged_msg)
     except OSError:
         log.error('Unable to write in the log file (%s)',
                   os.path.join(log_dir, jid),
                   exc_info=True)
         return False
     else:
         try:
             fd.flush()  # TODO do something better here?
         except OSError:
             log.error('Unable to flush the log file (%s)',
                       os.path.join(log_dir, jid),
                       exc_info=True)
             return False
     return True
Beispiel #8
0
 def on_gain_focus(self):
     self.state = 'current'
     curses.curs_set(1)
     tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab)
     if tab and tab.joined and config.get_by_tabname('send_chat_states',
             self.general_jid,) and not self.input.get_text() and self.on:
         self.send_chat_state('active')
Beispiel #9
0
 def log_roster_change(self, jid: str, message: str) -> bool:
     """
     Log a roster change
     """
     if not config.get_by_tabname('use_log', jid):
         return True
     self._check_and_create_log_dir('', open_fd=False)
     filename = log_dir / 'roster.log'
     if not self._roster_logfile:
         try:
             self._roster_logfile = filename.open('a', encoding='utf-8')
         except IOError:
             log.error(
                 'Unable to create the log file (%s)',
                 filename,
                 exc_info=True)
             return False
     try:
         str_time = common.get_utc_time().strftime('%Y%m%dT%H:%M:%SZ')
         message = clean_text(message)
         lines = message.split('\n')
         first_line = lines.pop(0)
         nb_lines = str(len(lines)).zfill(3)
         self._roster_logfile.write(
             'MI %s %s %s %s\n' % (str_time, nb_lines, jid, first_line))
         for line in lines:
             self._roster_logfile.write(' %s\n' % line)
         self._roster_logfile.flush()
     except:
         log.error(
             'Unable to write in the log file (%s)',
             filename,
             exc_info=True)
         return False
     return True
Beispiel #10
0
 def _check_and_create_log_dir(self, room: str,
                               open_fd: bool = True) -> Optional[IO[Any]]:
     """
     Check that the directory where we want to log the messages
     exists. if not, create it
     """
     if not config.get_by_tabname('use_log', room):
         return None
     try:
         log_dir.mkdir(parents=True, exist_ok=True)
     except OSError as e:
         log.error('Unable to create the log dir', exc_info=True)
     except:
         log.error('Unable to create the log dir', exc_info=True)
         return None
     if not open_fd:
         return None
     filename = log_dir / room
     try:
         fd = filename.open('a', encoding='utf-8')
         self._fds[room] = fd
         return fd
     except IOError:
         log.error(
             'Unable to open the log file (%s)', filename, exc_info=True)
     return None
Beispiel #11
0
    def update_status(self, status):
        old_status = self.__status
        if not (old_status.show != status.show
                or old_status.message != status.message):
            return
        self.__status = status
        hide_status_change = config.get_by_tabname('hide_status_change',
                                                   self.jid.bare)
        now = datetime.now()
        dff = now - self.last_remote_message
        if hide_status_change > -1 and dff.total_seconds() > hide_status_change:
            return

        info_c = dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
        nick = self.get_nick()
        remote = self.remote_user_color()
        msg = '\x19%(color)s}%(nick)s\x19%(info)s} changed: '
        msg %= {'color': remote, 'nick': nick, 'info': info_c}
        if status.message != old_status.message and status.message:
            msg += 'status: %s, ' % status.message
        if status.show in SHOW_NAME:
            msg += 'show: %s, ' % SHOW_NAME[status.show]
        self.add_message(
            InfoMessage(txt=msg[:-2]),
            typ=2,
        )
Beispiel #12
0
 def _check_and_create_log_dir(self, room: str,
                               open_fd: bool = True) -> Optional[IO[Any]]:
     """
     Check that the directory where we want to log the messages
     exists. if not, create it
     """
     if not config.get_by_tabname('use_log', room):
         return None
     try:
         log_dir.mkdir(parents=True, exist_ok=True)
     except OSError as e:
         log.error('Unable to create the log dir', exc_info=True)
     except:
         log.error('Unable to create the log dir', exc_info=True)
         return None
     if not open_fd:
         return None
     filename = log_dir / room
     try:
         fd = filename.open('a', encoding='utf-8')
         self._fds[room] = fd
         return fd
     except IOError:
         log.error(
             'Unable to open the log file (%s)', filename, exc_info=True)
     return None
Beispiel #13
0
async def retrieve_messages(tab: tabs.ChatTab,
                            results: AsyncIterable[SMessage],
                            amount: int = 100) -> List[BaseMessage]:
    """Run the MAM query and put messages in order"""
    text_buffer = tab._text_buffer
    msg_count = 0
    msgs = []
    to_add = []
    deterministic = config.get_by_tabname(
        'deterministic_nick_colors',
        tab.jid.bare
    )
    try:
        async for rsm in results:
            for msg in rsm['mam']['results']:
                if msg['mam_result']['forwarded']['stanza'] \
                        .xml.find('{%s}%s' % ('jabber:client', 'body')) is not None:
                    args = _parse_message(msg)
                    msgs.append(make_line(tab, deterministic=deterministic, **args))
            for msg in reversed(msgs):
                to_add.append(msg)
                msg_count += 1
                if msg_count == amount:
                    to_add.reverse()
                    return to_add
            msgs = []
        to_add.reverse()
        return to_add
    except (IqError, IqTimeout) as exc:
        log.debug('Unable to complete MAM query: %s', exc, exc_info=True)
        raise MAMQueryException('Query interrupted')
Beispiel #14
0
 def user_rejoined(self, nick):
     """
     The user (or at least someone with the same nick) came back in the MUC
     """
     self.activate()
     self.check_features()
     tab = self.parent_muc
     theme = get_theme()
     color = dump_tuple(theme.COLOR_REMOTE_USER)
     if tab and config.get_by_tabname('display_user_color_in_join_part',
                                      self.general_jid):
         user = tab.get_user_by_name(nick)
         if user:
             color = dump_tuple(user.color)
     self.add_message(
         '\x19%(join_col)s}%(spec)s \x19%(color)s}%(nick)s\x19'
         '%(info_col)s} joined the room' % {
             'nick': nick,
             'color': color,
             'spec': theme.CHAR_JOIN,
             'join_col': dump_tuple(theme.COLOR_JOIN_CHAR),
             'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT)
         },
         typ=2)
     return self.core.tabs.current_tab is self
Beispiel #15
0
    def user_left(self, status_message, user):
        """
        The user left the associated MUC
        """
        self.deactivate()
        if config.get_by_tabname('display_user_color_in_join_part', self.general_jid):
            color = dump_tuple(user.color)
        else:
            color = dump_tuple(get_theme().COLOR_REMOTE_USER)

        if not status_message:
            self.add_message('\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}'
                             '%(nick)s\x19%(info_col)s} has left the room' % {
                                 'nick': user.nick, 'spec': get_theme().CHAR_QUIT,
                                 'nick_col': color,
                                 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR),
                                 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
                             typ=2)
        else:
            self.add_message('\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}'
                             '%(nick)s\x19%(info_col)s} has left the room'
                             ' (%(status)s)' % { 'status': status_message,
                                 'nick': user.nick, 'spec': get_theme().CHAR_QUIT,
                                 'nick_col': color,
                                 'quit_col': dump_tuple(get_theme().COLOR_QUIT_CHAR),
                                 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
                             typ=2)
        return self.core.current_tab() is self
Beispiel #16
0
 def user_rejoined(self, nick):
     """
     The user (or at least someone with the same nick) came back in the MUC
     """
     self.activate()
     self.check_features()
     tab = self.parent_muc
     theme = get_theme()
     color = dump_tuple(theme.COLOR_REMOTE_USER)
     if tab and config.get_by_tabname('display_user_color_in_join_part',
                                      self.general_jid):
         user = tab.get_user_by_name(nick)
         if user:
             color = dump_tuple(user.color)
     self.add_message(
         '\x19%(join_col)s}%(spec)s \x19%(color)s}%(nick)s\x19'
         '%(info_col)s} joined the room' % {
             'nick': nick,
             'color': color,
             'spec': theme.CHAR_JOIN,
             'join_col': dump_tuple(theme.COLOR_JOIN_CHAR),
             'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT)
         },
         typ=2)
     return self.core.tabs.current_tab is self
Beispiel #17
0
    def log_message(self, jid, nick, msg, date=None, typ=1):
        """
        log the message in the appropriate jid's file
        type:
              0 = Don’t log
              1 = Message
              2 = Status/whatever
        """
        if not typ:
            return True

        jid = str(jid).replace('/', '\\')
        if not config.get_by_tabname('use_log', jid):
            return True
        if jid in self._fds.keys():
            fd = self._fds[jid]
        else:
            fd = self._check_and_create_log_dir(jid)
        if not fd:
            return True
        try:
            msg = clean_text(msg)
            if date is None:
                str_time = common.get_utc_time().strftime('%Y%m%dT%H:%M:%SZ')
            else:
                str_time = common.get_utc_time(date).strftime(
                    '%Y%m%dT%H:%M:%SZ')
            if typ == 1:
                prefix = 'MR'
            else:
                prefix = 'MI'
            lines = msg.split('\n')
            first_line = lines.pop(0)
            nb_lines = str(len(lines)).zfill(3)

            if nick:
                nick = '<' + nick + '>'
                fd.write(' '.join((prefix, str_time, nb_lines, nick,
                                   ' ' + first_line, '\n')))
            else:
                fd.write(' '.join(
                    (prefix, str_time, nb_lines, first_line, '\n')))
            for line in lines:
                fd.write(' %s\n' % line)
        except:
            log.error('Unable to write in the log file (%s)',
                      os.path.join(log_dir, jid),
                      exc_info=True)
            return False
        else:
            try:
                fd.flush()  # TODO do something better here?
            except:
                log.error('Unable to flush the log file (%s)',
                          os.path.join(log_dir, jid),
                          exc_info=True)
                return False
        return True
Beispiel #18
0
    def get_logs(self, jid, nb=10):
        """
        Get the nb last messages from the log history for the given jid.
        Note that a message may be more than one line in these files, so
        this function is a little bit more complicated than “read the last
        nb lines”.
        """
        if config.get_by_tabname('load_log', jid) <= 0:
            return

        if not config.get_by_tabname('use_log', jid):
            return

        if nb <= 0:
            return

        self._check_and_create_log_dir(jid, open_fd=False)

        try:
            fd = open(os.path.join(log_dir, jid), 'rb')
        except FileNotFoundError:
            log.info('Non-existing log file (%s)',
                     os.path.join(log_dir, jid),
                     exc_info=True)
            return
        except OSError:
            log.error('Unable to open the log file (%s)',
                      os.path.join(log_dir, jid),
                      exc_info=True)
            return
        if not fd:
            return

        # read the needed data from the file, we just search nb messages by
        # searching "\nM" nb times from the end of the file.  We use mmap to
        # do that efficiently, instead of seek()s and read()s which are costly.
        with fd:
            try:
                lines = get_lines_from_fd(fd, nb=nb)
            except Exception:  # file probably empty
                log.error('Unable to mmap the log file for (%s)',
                          os.path.join(log_dir, jid),
                          exc_info=True)
                return
        return parse_log_lines(lines)
Beispiel #19
0
    def get_logs(self, jid: str,
                 nb: int = 10) -> Optional[List[Dict[str, Any]]]:
        """
        Get the nb last messages from the log history for the given jid.
        Note that a message may be more than one line in these files, so
        this function is a little bit more complicated than “read the last
        nb lines”.
        """
        if config.get_by_tabname('load_log', jid) <= 0:
            return None

        if not config.get_by_tabname('use_log', jid):
            return None

        if nb <= 0:
            return None

        self._check_and_create_log_dir(jid, open_fd=False)

        filename = log_dir / jid
        try:
            fd = filename.open('rb')
        except FileNotFoundError:
            log.info('Non-existing log file (%s)', filename, exc_info=True)
            return None
        except OSError:
            log.error(
                'Unable to open the log file (%s)', filename, exc_info=True)
            return None
        if not fd:
            return None

        # read the needed data from the file, we just search nb messages by
        # searching "\nM" nb times from the end of the file.  We use mmap to
        # do that efficiently, instead of seek()s and read()s which are costly.
        with fd:
            try:
                lines = get_lines_from_fd(fd, nb=nb)
            except Exception:  # file probably empty
                log.error(
                    'Unable to mmap the log file for (%s)',
                    filename,
                    exc_info=True)
                return None
        return parse_log_lines(lines)
Beispiel #20
0
 def on_gain_focus(self):
     self.state = 'current'
     curses.curs_set(1)
     tab = self.core.tabs.by_name_and_class(safeJID(self.name).bare, MucTab)
     if tab and tab.joined and config.get_by_tabname(
             'send_chat_states',
             self.general_jid,
     ) and not self.input.get_text() and self.on:
         self.send_chat_state('active')
Beispiel #21
0
    def log_message(self, jid, nick, msg, date=None, typ=1):
        """
        log the message in the appropriate jid's file
        type:
              0 = Don’t log
              1 = Message
              2 = Status/whatever
        """
        if not typ:
            return True

        jid = str(jid).replace('/', '\\')
        if not config.get_by_tabname('use_log', jid):
            return True
        if jid in self._fds.keys():
            fd = self._fds[jid]
        else:
            fd = self._check_and_create_log_dir(jid)
        if not fd:
            return True
        try:
            msg = clean_text(msg)
            if date is None:
                str_time = common.get_utc_time().strftime('%Y%m%dT%H:%M:%SZ')
            else:
                str_time = common.get_utc_time(date).strftime('%Y%m%dT%H:%M:%SZ')
            if typ == 1:
                prefix = 'MR'
            else:
                prefix = 'MI'
            lines = msg.split('\n')
            first_line = lines.pop(0)
            nb_lines = str(len(lines)).zfill(3)

            if nick:
                nick = '<' + nick + '>'
                fd.write(' '.join((prefix, str_time, nb_lines, nick, ' '+first_line, '\n')))
            else:
                fd.write(' '.join((prefix, str_time, nb_lines, first_line, '\n')))
            for line in lines:
                fd.write(' %s\n' % line)
        except:
            log.error('Unable to write in the log file (%s)',
                    os.path.join(log_dir, jid),
                    exc_info=True)
            return False
        else:
            try:
                fd.flush()          # TODO do something better here?
            except:
                log.error('Unable to flush the log file (%s)',
                        os.path.join(log_dir, jid),
                        exc_info=True)
                return False
        return True
Beispiel #22
0
def hl(tab):
    """
    Make a tab beep and change its status.
    """
    if tab.state != 'current':
        tab.state = 'private'

    conv_jid = safeJID(tab.name)
    if 'private' in config.get('beep_on', 'highlight private').split():
        if not config.get_by_tabname('disable_beep', conv_jid.bare, default=False):
            curses.beep()
Beispiel #23
0
def hl(tab):
    """
    Make a tab beep and change its status.
    """
    if tab.state != 'current':
        tab.state = 'private'

    conv_jid = safeJID(tab.name)
    if 'private' in config.get('beep_on', 'highlight private').split():
        if not config.get_by_tabname('disable_beep', conv_jid.bare, default=False):
            curses.beep()
Beispiel #24
0
def add_line(
    tab,
    text_buffer: TextBuffer,
    text: str,
    time: datetime,
    nick: str,
    top: bool,
) -> None:
    """Adds a textual entry in the TextBuffer"""

    # Convert to local timezone
    time = time.replace(tzinfo=timezone.utc).astimezone(tz=None)
    time = time.replace(tzinfo=None)

    deterministic = config.get_by_tabname('deterministic_nick_colors',
                                          tab.jid.bare)
    if isinstance(tab, tabs.MucTab):
        nick = nick.split('/')[1]
        user = tab.get_user_by_name(nick)
        if deterministic:
            if user:
                color = user.color
            else:
                theme = get_theme()
                if theme.ccg_palette:
                    fg_color = colors.ccg_text_to_color(
                        theme.ccg_palette, nick)
                    color = fg_color, -1
                else:
                    mod = len(theme.LIST_COLOR_NICKNAMES)
                    nick_pos = int(md5(nick.encode('utf-8')).hexdigest(),
                                   16) % mod
                    color = theme.LIST_COLOR_NICKNAMES[nick_pos]
        else:
            color = random.choice(list(xhtml.colors))
            color = xhtml.colors.get(color)
            color = (color, -1)
    else:
        nick = nick.split('/')[0]
        color = get_theme().COLOR_OWN_NICK
    text_buffer.add_message(
        txt=text,
        time=time,
        nickname=nick,
        nick_color=color,
        history=True,
        user=None,
        highlight=False,
        top=top,
        identifier=None,
        str_time=None,
        jid=None,
    )
Beispiel #25
0
 def set_paused_delay(self, composing):
     """
     we create a timed event that will put us to paused
     in a few seconds
     """
     if not config.get_by_tabname('send_chat_states', self.general_jid):
         return
     # First, cancel the delay if it already exists, before rescheduling
     # it at a new date
     self.cancel_paused_delay()
     new_event = timed_events.DelayedEvent(4, self.send_chat_state, 'paused')
     self.core.add_timed_event(new_event)
     self.timed_event_paused = new_event
Beispiel #26
0
    def on_lose_focus(self):
        if self.input.text:
            self.state = 'nonempty'
        else:
            self.state = 'normal'

        self.text_win.remove_line_separator()
        self.text_win.add_line_separator(self._text_buffer)
        tab = self.core.tabs.by_name_and_class(safeJID(self.name).bare, MucTab)
        if tab and tab.joined and config.get_by_tabname(
                'send_chat_states', self.general_jid) and self.on:
            self.send_chat_state('inactive')
        self.check_scrolled()
Beispiel #27
0
    def on_lose_focus(self):
        if self.input.text:
            self.state = 'nonempty'
        else:
            self.state = 'normal'

        self.text_win.remove_line_separator()
        self.text_win.add_line_separator(self._text_buffer)
        tab = self.core.tabs.by_name_and_class(safeJID(self.name).bare, MucTab)
        if tab and tab.joined and config.get_by_tabname(
                'send_chat_states', self.general_jid) and self.on:
            self.send_chat_state('inactive')
        self.check_scrolled()
Beispiel #28
0
 def set_paused_delay(self, composing):
     """
     we create a timed event that will put us to paused
     in a few seconds
     """
     if not config.get_by_tabname('send_chat_states', self.general_jid):
         return
     # First, cancel the delay if it already exists, before rescheduling
     # it at a new date
     self.cancel_paused_delay()
     new_event = timed_events.DelayedEvent(4, self.send_chat_state,
                                           'paused')
     self.core.add_timed_event(new_event)
     self.timed_event_paused = new_event
Beispiel #29
0
 def send_chat_state(self, state, always_send=False):
     """
     Send an empty chatstate message
     """
     if self.check_send_chat_state():
         if state in ('active', 'inactive',
                      'gone') and self.inactive and not always_send:
             return
         if config.get_by_tabname('send_chat_states', self.general_jid):
             msg = self.core.xmpp.make_message(self.get_dest_jid())
             msg['type'] = self.message_type
             msg['chat_state'] = state
             self.chat_state = state
             msg.send()
             return True
Beispiel #30
0
 def send_composing_chat_state(self, empty_after):
     """
     Send the "active" or "composing" chatstate, depending
     on the the current status of the input
     """
     name = self.general_jid
     if config.get_by_tabname('send_chat_states', name):
         needed = 'inactive' if self.inactive else 'active'
         self.cancel_paused_delay()
         if not empty_after:
             if self.chat_state != "composing":
                 self.send_chat_state("composing")
             self.set_paused_delay(True)
         elif empty_after and self.chat_state != needed:
             self.send_chat_state(needed, True)
Beispiel #31
0
 def send_chat_state(self, state, always_send=False):
     """
     Send an empty chatstate message
     """
     if self.check_send_chat_state():
         if state in ('active', 'inactive',
                      'gone') and self.inactive and not always_send:
             return
         if config.get_by_tabname('send_chat_states', self.general_jid):
             msg = self.core.xmpp.make_message(self.get_dest_jid())
             msg['type'] = self.message_type
             msg['chat_state'] = state
             self.chat_state = state
             msg.send()
             return True
Beispiel #32
0
 def send_composing_chat_state(self, empty_after):
     """
     Send the "active" or "composing" chatstate, depending
     on the the current status of the input
     """
     name = self.general_jid
     if config.get_by_tabname('send_chat_states', name):
         needed = 'inactive' if self.inactive else 'active'
         self.cancel_paused_delay()
         if not empty_after:
             if self.chat_state != "composing":
                 self.send_chat_state("composing")
             self.set_paused_delay(True)
         elif empty_after and self.chat_state != needed:
             self.send_chat_state(needed, True)
Beispiel #33
0
    def join(self, args):
        """
        /join [room][/nick] [password]
        """
        if len(args) == 0:
            room, nick = self._empty_join()
        else:
            room, nick = self._parse_join_jid(args[0])
        if not room and not nick:
            return  # nothing was parsed

        room = room.lower()
        if nick == '':
            nick = self.core.own_nick

        # a password is provided
        if len(args) == 2:
            password = args[1]
        else:
            password = config.get_by_tabname('password', room, fallback=False)

        if room in self.core.pending_invites:
            del self.core.pending_invites[room]

        tab = self.core.get_tab_by_name(room, tabs.MucTab)
        # New tab
        if tab is None:
            tab = self.core.open_new_room(room, nick, password=password)
            tab.join()
        else:
            self.core.focus_tab_named(tab.name)
            if tab.own_nick == nick and tab.joined:
                self.core.information('/join: Nothing to do.', 'Info')
            else:
                tab.command_part('')
                tab.own_nick = nick
                tab.password = password
                tab.join()

        if config.get('bookmark_on_join'):
            method = 'remote' if config.get(
                'use_remote_bookmarks') else 'local'
            self._add_bookmark('%s/%s' % (room, nick), True, password, method)

        if tab == self.core.current_tab():
            tab.refresh()
            self.core.doupdate()
Beispiel #34
0
    def join(self, args):
        """
        /join [room][/nick] [password]
        """
        if len(args) == 0:
            room, nick = self._empty_join()
        else:
            room, nick = self._parse_join_jid(args[0])
        if not room and not nick:
            return  # nothing was parsed

        room = room.lower()
        if nick == '':
            nick = self.core.own_nick

        # a password is provided
        if len(args) == 2:
            password = args[1]
        else:
            password = config.get_by_tabname('password', room, fallback=False)

        if room in self.core.pending_invites:
            del self.core.pending_invites[room]

        tab = self.core.tabs.by_name_and_class(room, tabs.MucTab)
        # New tab
        if tab is None:
            tab = self.core.open_new_room(room, nick, password=password)
            tab.join()
        else:
            self.core.focus_tab(tab)
            if tab.own_nick == nick and tab.joined:
                self.core.information('/join: Nothing to do.', 'Info')
            else:
                tab.command_part('')
                tab.own_nick = nick
                tab.password = password
                tab.join()

        if config.get('bookmark_on_join'):
            method = 'remote' if config.get(
                'use_remote_bookmarks') else 'local'
            self._add_bookmark('%s/%s' % (room, nick), True, password, method)

        if tab == self.core.tabs.current_tab:
            tab.refresh()
            self.core.doupdate()
Beispiel #35
0
 def log_message(self,
                 jid: str,
                 nick: str,
                 msg: str,
                 date: Optional[datetime] = None,
                 typ: int = 1) -> bool:
     """
     log the message in the appropriate jid's file
     type:
           0 = Don’t log
           1 = Message
           2 = Status/whatever
     """
     if not config.get_by_tabname('use_log', jid):
         return True
     logged_msg = build_log_message(nick, msg, date=date, typ=typ)
     if not logged_msg:
         return True
     jid = str(jid).replace('/', '\\')
     if jid in self._fds.keys():
         fd = self._fds[jid]
     else:
         option_fd = self._check_and_create_log_dir(jid)
         if option_fd is None:
             return True
         fd = option_fd
     filename = log_dir / jid
     try:
         fd.write(logged_msg)
     except OSError:
         log.error(
             'Unable to write in the log file (%s)',
             filename,
             exc_info=True)
         return False
     else:
         try:
             fd.flush()  # TODO do something better here?
         except OSError:
             log.error(
                 'Unable to flush the log file (%s)',
                 filename,
                 exc_info=True)
             return False
     return True
Beispiel #36
0
 def log_message(self,
                 jid: str,
                 nick: str,
                 msg: str,
                 date: Optional[datetime] = None,
                 typ: int = 1) -> bool:
     """
     log the message in the appropriate jid's file
     type:
           0 = Don’t log
           1 = Message
           2 = Status/whatever
     """
     if not config.get_by_tabname('use_log', jid):
         return True
     logged_msg = build_log_message(nick, msg, date=date, typ=typ)
     if not logged_msg:
         return True
     jid = str(jid).replace('/', '\\')
     if jid in self._fds.keys():
         fd = self._fds[jid]
     else:
         option_fd = self._check_and_create_log_dir(jid)
         if option_fd is None:
             return True
         fd = option_fd
     filename = log_dir / jid
     try:
         fd.write(logged_msg)
     except OSError:
         log.error(
             'Unable to write in the log file (%s)',
             filename,
             exc_info=True)
         return False
     else:
         try:
             fd.flush()  # TODO do something better here?
         except OSError:
             log.error(
                 'Unable to flush the log file (%s)',
                 filename,
                 exc_info=True)
             return False
     return True
Beispiel #37
0
    def on_gain_focus(self):
        contact = roster[self.get_dest_jid()]
        jid = safeJID(self.get_dest_jid())
        if contact:
            if jid.resource:
                resource = contact[jid.full]
            else:
                resource = contact.get_highest_priority_resource()
        else:
            resource = None

        self.state = 'current'
        curses.curs_set(1)
        if (config.get_by_tabname('send_chat_states', self.general_jid)
                and (not self.input.get_text()
                    or not self.input.get_text().startswith('//'))):
            if resource:
                self.send_chat_state('active')
Beispiel #38
0
    def on_gain_focus(self):
        contact = roster[self.get_dest_jid()]
        jid = safeJID(self.get_dest_jid())
        if contact:
            if jid.resource:
                resource = contact[jid.full]
            else:
                resource = contact.get_highest_priority_resource()
        else:
            resource = None

        self.state = 'current'
        curses.curs_set(1)
        if (config.get_by_tabname('send_chat_states', self.general_jid)
                and (not self.input.get_text()
                     or not self.input.get_text().startswith('//'))):
            if resource:
                self.send_chat_state('active')
Beispiel #39
0
    def command_say(self,
                    line: str,
                    attention: bool = False,
                    correct: bool = False) -> None:
        if not self.on:
            return
        our_jid = JID(self.jid.bare)
        our_jid.resource = self.own_nick
        msg = self.core.xmpp.make_message(
            mto=self.jid.full,
            mfrom=our_jid,
        )
        msg['type'] = 'chat'
        msg['body'] = line
        x = ET.Element('{%s}x' % NS_MUC_USER)
        msg.append(x)
        # trigger the event BEFORE looking for colors.
        # This lets a plugin insert \x19xxx} colors, that will
        # be converted in xhtml.
        self.core.events.trigger('private_say', msg, self)
        if not msg['body']:
            return
        if correct or msg['replace']['id']:
            msg['replace']['id'] = self.last_sent_message['id']
        else:
            del msg['replace']

        if msg['body'].find('\x19') != -1:
            msg.enable('html')
            msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
            msg['body'] = xhtml.clean_text(msg['body'])
        if config.get_by_tabname('send_chat_states', self.general_jid):
            needed = 'inactive' if self.inactive else 'active'
            msg['chat_state'] = needed
        if attention:
            msg['attention'] = True
        self.core.events.trigger('private_say_after', msg, self)
        if not msg['body']:
            return
        self.set_last_sent_message(msg, correct=correct)
        self.core.handler.on_groupchat_private_message(msg, sent=True)
        msg._add_receipt = True
        msg.send()
        self.cancel_paused_delay()
Beispiel #40
0
    def join(self, args):
        """
        /join [room][/nick] [password]
        """
        if len(args) == 0:
            room, nick = self._empty_join()
        else:
            room, nick = self._parse_join_jid(args[0])
        if not room and not nick:
            return # nothing was parsed

        room = room.lower()
        if nick == '':
            nick = self.core.own_nick

        # a password is provided
        if len(args) == 2:
            password = args[1]
        else:
            password = config.get_by_tabname('password', room, fallback=False)

        if room in self.core.pending_invites:
            del self.core.pending_invites[room]

        tab = self.core.get_tab_by_name(room, tabs.MucTab)
        # New tab
        if tab is None:
            tab = self.core.open_new_room(room, nick, password=password)
            tab.join()
        else:
            self.core.focus_tab_named(tab.name)
            if tab.own_nick == nick and tab.joined:
                self.core.information('/join: Nothing to do.', 'Info')
            else:
                tab.command_part('')
                tab.own_nick = nick
                tab.password = password
                tab.join()

        if tab == self.core.current_tab():
            tab.refresh()
            self.core.doupdate()
Beispiel #41
0
 def on_lose_focus(self):
     contact = roster[self.get_dest_jid()]
     jid = safeJID(self.get_dest_jid())
     if contact:
         if jid.resource:
             resource = contact[jid.full]
         else:
             resource = contact.get_highest_priority_resource()
     else:
         resource = None
     if self.input.text:
         self.state = 'nonempty'
     else:
         self.state = 'normal'
     self.text_win.remove_line_separator()
     self.text_win.add_line_separator(self._text_buffer)
     if config.get_by_tabname('send_chat_states', self.general_jid):
         if resource:
             self.send_chat_state('inactive')
     self.check_scrolled()
Beispiel #42
0
 def on_lose_focus(self):
     contact = roster[self.get_dest_jid()]
     jid = safeJID(self.get_dest_jid())
     if contact:
         if jid.resource:
             resource = contact[jid.full]
         else:
             resource = contact.get_highest_priority_resource()
     else:
         resource = None
     if self.input.text:
         self.state = 'nonempty'
     else:
         self.state = 'normal'
     self.text_win.remove_line_separator()
     self.text_win.add_line_separator(self._text_buffer)
     if config.get_by_tabname('send_chat_states', self.general_jid):
         if resource:
             self.send_chat_state('inactive')
     self.check_scrolled()
Beispiel #43
0
    def send_chat_state(self, state: str, always_send: bool = False) -> None:
        """
        Send an empty chatstate message
        """
        from poezio.tabs import PrivateTab

        if self.check_send_chat_state():
            if state in ('active', 'inactive',
                         'gone') and self.inactive and not always_send:
                return
            if config.get_by_tabname('send_chat_states', self.general_jid):
                msg = self.core.xmpp.make_message(self.get_dest_jid())
                msg['type'] = self.message_type
                msg['chat_state'] = state
                self.chat_state = state
                msg['no-store'] = True
                if isinstance(self, PrivateTab):
                    x = ET.Element('{%s}x' % NS_MUC_USER)
                    msg.append(x)
                msg.send()
Beispiel #44
0
    def update_status(self, status):
        old_status = self.__status
        if not (old_status.show != status.show
                or old_status.message != status.message):
            return
        self.__status = status
        hide_status_change = config.get_by_tabname('hide_status_change',
                                                   safeJID(self.name).bare)
        now = datetime.now()
        dff = now - self.last_remote_message
        if hide_status_change > -1 and dff.total_seconds() > hide_status_change:
            return

        info_c = dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
        nick = self.get_nick()
        remote = self.remote_user_color()
        msg = '\x19%(color)s}%(nick)s\x19%(info)s} changed: '
        msg %= {'color': remote, 'nick': nick, 'info': info_c}
        if status.message != old_status.message and status.message:
            msg += 'status: %s, ' % status.message
        if status.show in SHOW_NAME:
            msg += 'show: %s, ' % SHOW_NAME[status.show]
        self.add_message(msg[:-2], typ=2)
Beispiel #45
0
    def user_left(self, status_message, user):
        """
        The user left the associated MUC
        """
        self.deactivate()
        theme = get_theme()
        if config.get_by_tabname('display_user_color_in_join_part',
                                 self.general_jid):
            color = dump_tuple(user.color)
        else:
            color = dump_tuple(theme.COLOR_REMOTE_USER)

        if not status_message:
            self.add_message(
                '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}'
                '%(nick)s\x19%(info_col)s} has left the room' % {
                    'nick': user.nick,
                    'spec': theme.CHAR_QUIT,
                    'nick_col': color,
                    'quit_col': dump_tuple(theme.COLOR_QUIT_CHAR),
                    'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT)
                },
                typ=2)
        else:
            self.add_message(
                '\x19%(quit_col)s}%(spec)s \x19%(nick_col)s}'
                '%(nick)s\x19%(info_col)s} has left the room'
                ' (%(status)s)' % {
                    'status': status_message,
                    'nick': user.nick,
                    'spec': theme.CHAR_QUIT,
                    'nick_col': color,
                    'quit_col': dump_tuple(theme.COLOR_QUIT_CHAR),
                    'info_col': dump_tuple(theme.COLOR_INFORMATION_TEXT)
                },
                typ=2)
        return self.core.tabs.current_tab is self
Beispiel #46
0
 def command_say(self, line, attention=False, correct=False):
     msg = self.core.xmpp.make_message(mto=self.get_dest_jid(),
                                       mfrom=self.core.xmpp.boundjid)
     msg['type'] = 'chat'
     msg['body'] = line
     if not self.nick_sent:
         msg['nick'] = self.core.own_nick
         self.nick_sent = True
     # trigger the event BEFORE looking for colors.
     # and before displaying the message in the window
     # This lets a plugin insert \x19xxx} colors, that will
     # be converted in xhtml.
     self.core.events.trigger('conversation_say', msg, self)
     if not msg['body']:
         return
     replaced = False
     if correct or msg['replace']['id']:
         msg['replace']['id'] = self.last_sent_message['id']
     else:
         del msg['replace']
     if msg['body'].find('\x19') != -1:
         msg.enable('html')
         msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
         msg['body'] = xhtml.clean_text(msg['body'])
     if config.get_by_tabname('send_chat_states', self.general_jid):
         needed = 'inactive' if self.inactive else 'active'
         msg['chat_state'] = needed
     if attention:
         msg['attention'] = True
     self.core.events.trigger('conversation_say_after', msg, self)
     if not msg['body']:
         return
     self.last_sent_message = msg
     self.core.handler.on_normal_message(msg)
     msg._add_receipt = True
     msg.send()
     self.cancel_paused_delay()
Beispiel #47
0
 def get_local(self):
     """Add the locally stored bookmarks to the list."""
     rooms = config.get('rooms')
     if not rooms:
         return
     rooms = rooms.split(':')
     for room in rooms:
         jid = safeJID(room)
         if jid.bare == '':
             continue
         if jid.resource != '':
             nick = jid.resource
         else:
             nick = None
         passwd = config.get_by_tabname(
             'password', jid.bare, fallback=False) or None
         b = Bookmark(
             jid.bare,
             jid.user,
             autojoin=True,
             nick=nick,
             password=passwd,
             method='local')
         self.append(b)
Beispiel #48
0
    def get_logs(self, jid, nb=10):
        """
        Get the nb last messages from the log history for the given jid.
        Note that a message may be more than one line in these files, so
        this function is a little bit more complicated than “read the last
        nb lines”.
        """
        if config.get_by_tabname('load_log', jid) <= 0:
            return

        if not config.get_by_tabname('use_log', jid):
            return

        if nb <= 0:
            return

        self._check_and_create_log_dir(jid, open_fd=False)

        try:
            fd = open(os.path.join(log_dir, jid), 'rb')
        except FileNotFoundError:
            log.info('Non-existing log file (%s)',
                     os.path.join(log_dir, jid),
                     exc_info=True)
            return
        except OSError:
            log.error('Unable to open the log file (%s)',
                      os.path.join(log_dir, jid),
                      exc_info=True)
            return
        if not fd:
            return

        # read the needed data from the file, we just search nb messages by
        # searching "\nM" nb times from the end of the file.  We use mmap to
        # do that efficiently, instead of seek()s and read()s which are costly.
        with fd:
            try:
                m = mmap.mmap(fd.fileno(), 0, prot=mmap.PROT_READ)
            except Exception:  # file probably empty
                log.error('Unable to mmap the log file for (%s)',
                          os.path.join(log_dir, jid),
                          exc_info=True)
                return
            pos = m.rfind(b"\nM")  # start of messages begin with MI or MR,
            # after a \n
            # number of message found so far
            count = 0
            while pos != -1 and count < nb - 1:
                count += 1
                pos = m.rfind(b"\nM", 0, pos)
            if pos == -1:  # If we don't have enough lines in the file
                pos = 1  # 1, because we do -1 just on the next line
                # to get 0 (start of the file)
            lines = m[pos - 1:].decode(errors='replace').splitlines()

        messages = []
        color = '\x19%s}' % dump_tuple(get_theme().COLOR_LOG_MSG)

        # now convert that data into actual Message objects
        idx = 0
        while idx < len(lines):
            if lines[idx].startswith(' '):  # should not happen ; skip
                idx += 1
                log.debug('fail?')
                continue
            log_item = parse_message_line(lines[idx])
            idx += 1
            if not isinstance(log_item, LogItem):
                log.debug('wrong log format? %s', log_item)
                continue
            message = {
                'lines': [],
                'history': True,
                'time': common.get_local_time(log_item.time)
            }
            size = log_item.nb_lines
            if isinstance(log_item, LogInfo):
                message['lines'].append(color + log_item.text)
            elif isinstance(log_item, LogMessage):
                message['nickname'] = log_item.nick
                message['lines'].append(color + log_item.text)
            while size != 0 and idx < len(lines):
                message['lines'].append(lines[idx][1:])
                size -= 1
                idx += 1
            message['txt'] = '\n'.join(message['lines'])
            del message['lines']
            messages.append(message)

        return messages
Beispiel #49
0
    def command_say(self, line, attention=False, correct=False):
        if not self.on:
            return
        echo_message = JID(self.name).resource != self.own_nick
        msg = self.core.xmpp.make_message(self.name)
        msg['type'] = 'chat'
        msg['body'] = line
        # trigger the event BEFORE looking for colors.
        # This lets a plugin insert \x19xxx} colors, that will
        # be converted in xhtml.
        self.core.events.trigger('private_say', msg, self)
        if not msg['body']:
            self.cancel_paused_delay()
            self.text_win.refresh()
            self.input.refresh()
            return
        user = self.parent_muc.get_user_by_name(self.own_nick)
        replaced = False
        if correct or msg['replace']['id']:
            msg['replace']['id'] = self.last_sent_message['id']
            if (config.get_by_tabname('group_corrections', self.name)
                    and echo_message):
                try:
                    self.modify_message(
                        msg['body'],
                        self.last_sent_message['id'],
                        msg['id'],
                        user=user,
                        jid=self.core.xmpp.boundjid,
                        nickname=self.own_nick)
                    replaced = True
                except:
                    log.error('Unable to correct a message', exc_info=True)
        else:
            del msg['replace']

        if msg['body'].find('\x19') != -1:
            msg.enable('html')
            msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
            msg['body'] = xhtml.clean_text(msg['body'])
        if config.get_by_tabname('send_chat_states', self.general_jid):
            needed = 'inactive' if self.inactive else 'active'
            msg['chat_state'] = needed
        if attention:
            msg['attention'] = True
        self.core.events.trigger('private_say_after', msg, self)
        if not msg['body']:
            self.cancel_paused_delay()
            self.text_win.refresh()
            self.input.refresh()
            return
        if not replaced and echo_message:
            self.add_message(
                msg['body'],
                nickname=self.own_nick or self.core.own_nick,
                forced_user=user,
                nick_color=get_theme().COLOR_OWN_NICK,
                identifier=msg['id'],
                jid=self.core.xmpp.boundjid,
                typ=1)

        self.last_sent_message = msg
        msg._add_receipt = True
        msg.send()
        self.cancel_paused_delay()
        self.text_win.refresh()
        self.input.refresh()
Beispiel #50
0
 def on_close(self):
     Tab.on_close(self)
     if config.get_by_tabname('send_chat_states', self.general_jid):
         self.send_chat_state('gone')
Beispiel #51
0
    def get_logs(self, jid, nb=10):
        """
        Get the nb last messages from the log history for the given jid.
        Note that a message may be more than one line in these files, so
        this function is a little bit more complicated than “read the last
        nb lines”.
        """
        if config.get_by_tabname('load_log', jid) <= 0:
            return

        if not config.get_by_tabname('use_log', jid):
            return

        if nb <= 0:
            return

        self._check_and_create_log_dir(jid, open_fd=False)

        try:
            fd = open(os.path.join(log_dir, jid), 'rb')
        except FileNotFoundError:
            log.info('Non-existing log file (%s)',
                     os.path.join(log_dir, jid),
                     exc_info=True)
            return
        except OSError:
            log.error('Unable to open the log file (%s)',
                      os.path.join(log_dir, jid),
                      exc_info=True)
            return
        if not fd:
            return

        # read the needed data from the file, we just search nb messages by
        # searching "\nM" nb times from the end of the file.  We use mmap to
        # do that efficiently, instead of seek()s and read()s which are costly.
        with fd:
            try:
                m = mmap.mmap(fd.fileno(), 0, prot=mmap.PROT_READ)
            except Exception: # file probably empty
                log.error('Unable to mmap the log file for (%s)',
                        os.path.join(log_dir, jid),
                        exc_info=True)
                return
            pos = m.rfind(b"\nM") # start of messages begin with MI or MR,
                                  # after a \n
            # number of message found so far
            count = 0
            while pos != -1 and count < nb-1:
                count += 1
                pos = m.rfind(b"\nM", 0, pos)
            if pos == -1:       # If we don't have enough lines in the file
                pos = 1         # 1, because we do -1 just on the next line
                                # to get 0 (start of the file)
            lines = m[pos-1:].decode(errors='replace').splitlines()

        messages = []
        color = '\x19%s}' % dump_tuple(get_theme().COLOR_LOG_MSG)

        # now convert that data into actual Message objects
        idx = 0
        while idx < len(lines):
            if lines[idx].startswith(' '): # should not happen ; skip
                idx += 1
                log.debug('fail?')
                continue
            tup = _parse_message_line(lines[idx])
            idx += 1
            if not tup or len(tup) not in (8, 9): # skip
                log.debug('format? %s', tup)
                continue
            time = [int(i) for i in tup[:6]]
            message = {'lines': [],
                       'history': True,
                       'time': common.get_local_time(datetime(*time))}
            size = int(tup[6])
            if len(tup) == 8: #info line
                message['lines'].append(color + tup[7])
            else: # message line
                message['nickname'] = tup[7]
                message['lines'].append(color + tup[8])
            while size != 0 and idx < len(lines):
                message['lines'].append(lines[idx][1:])
                size -= 1
                idx += 1
            message['txt'] = '\n'.join(message['lines'])
            del message['lines']
            messages.append(message)

        return messages
Beispiel #52
0
 def on_close(self):
     Tab.on_close(self)
     if config.get_by_tabname('send_chat_states', self.general_jid):
         self.send_chat_state('gone')