mirror of
				https://github.com/sqlmapproject/sqlmap.git
				synced 2025-11-04 01:47:37 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			287 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
######################## BEGIN LICENSE BLOCK ########################
 | 
						|
# The Original Code is Mozilla Universal charset detector code.
 | 
						|
#
 | 
						|
# The Initial Developer of the Original Code is
 | 
						|
# Netscape Communications Corporation.
 | 
						|
# Portions created by the Initial Developer are Copyright (C) 2001
 | 
						|
# the Initial Developer. All Rights Reserved.
 | 
						|
#
 | 
						|
# Contributor(s):
 | 
						|
#   Mark Pilgrim - port to Python
 | 
						|
#   Shy Shalom - original C code
 | 
						|
#
 | 
						|
# This library is free software; you can redistribute it and/or
 | 
						|
# modify it under the terms of the GNU Lesser General Public
 | 
						|
# License as published by the Free Software Foundation; either
 | 
						|
# version 2.1 of the License, or (at your option) any later version.
 | 
						|
#
 | 
						|
# This library 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
 | 
						|
# Lesser General Public License for more details.
 | 
						|
#
 | 
						|
# You should have received a copy of the GNU Lesser General Public
 | 
						|
# License along with this library; if not, write to the Free Software
 | 
						|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 | 
						|
# 02110-1301  USA
 | 
						|
######################### END LICENSE BLOCK #########################
 | 
						|
"""
 | 
						|
Module containing the UniversalDetector detector class, which is the primary
 | 
						|
class a user of ``chardet`` should use.
 | 
						|
 | 
						|
:author: Mark Pilgrim (initial port to Python)
 | 
						|
:author: Shy Shalom (original C code)
 | 
						|
:author: Dan Blanchard (major refactoring for 3.0)
 | 
						|
:author: Ian Cordasco
 | 
						|
"""
 | 
						|
 | 
						|
 | 
						|
import codecs
 | 
						|
import logging
 | 
						|
import re
 | 
						|
 | 
						|
from .charsetgroupprober import CharSetGroupProber
 | 
						|
from .enums import InputState, LanguageFilter, ProbingState
 | 
						|
from .escprober import EscCharSetProber
 | 
						|
from .latin1prober import Latin1Prober
 | 
						|
from .mbcsgroupprober import MBCSGroupProber
 | 
						|
from .sbcsgroupprober import SBCSGroupProber
 | 
						|
 | 
						|
 | 
						|
class UniversalDetector(object):
 | 
						|
    """
 | 
						|
    The ``UniversalDetector`` class underlies the ``chardet.detect`` function
 | 
						|
    and coordinates all of the different charset probers.
 | 
						|
 | 
						|
    To get a ``dict`` containing an encoding and its confidence, you can simply
 | 
						|
    run:
 | 
						|
 | 
						|
    .. code::
 | 
						|
 | 
						|
            u = UniversalDetector()
 | 
						|
            u.feed(some_bytes)
 | 
						|
            u.close()
 | 
						|
            detected = u.result
 | 
						|
 | 
						|
    """
 | 
						|
 | 
						|
    MINIMUM_THRESHOLD = 0.20
 | 
						|
    HIGH_BYTE_DETECTOR = re.compile(b'[\x80-\xFF]')
 | 
						|
    ESC_DETECTOR = re.compile(b'(\033|~{)')
 | 
						|
    WIN_BYTE_DETECTOR = re.compile(b'[\x80-\x9F]')
 | 
						|
    ISO_WIN_MAP = {'iso-8859-1': 'Windows-1252',
 | 
						|
                   'iso-8859-2': 'Windows-1250',
 | 
						|
                   'iso-8859-5': 'Windows-1251',
 | 
						|
                   'iso-8859-6': 'Windows-1256',
 | 
						|
                   'iso-8859-7': 'Windows-1253',
 | 
						|
                   'iso-8859-8': 'Windows-1255',
 | 
						|
                   'iso-8859-9': 'Windows-1254',
 | 
						|
                   'iso-8859-13': 'Windows-1257'}
 | 
						|
 | 
						|
    def __init__(self, lang_filter=LanguageFilter.ALL):
 | 
						|
        self._esc_charset_prober = None
 | 
						|
        self._charset_probers = []
 | 
						|
        self.result = None
 | 
						|
        self.done = None
 | 
						|
        self._got_data = None
 | 
						|
        self._input_state = None
 | 
						|
        self._last_char = None
 | 
						|
        self.lang_filter = lang_filter
 | 
						|
        self.logger = logging.getLogger(__name__)
 | 
						|
        self._has_win_bytes = None
 | 
						|
        self.reset()
 | 
						|
 | 
						|
    def reset(self):
 | 
						|
        """
 | 
						|
        Reset the UniversalDetector and all of its probers back to their
 | 
						|
        initial states.  This is called by ``__init__``, so you only need to
 | 
						|
        call this directly in between analyses of different documents.
 | 
						|
        """
 | 
						|
        self.result = {'encoding': None, 'confidence': 0.0, 'language': None}
 | 
						|
        self.done = False
 | 
						|
        self._got_data = False
 | 
						|
        self._has_win_bytes = False
 | 
						|
        self._input_state = InputState.PURE_ASCII
 | 
						|
        self._last_char = b''
 | 
						|
        if self._esc_charset_prober:
 | 
						|
            self._esc_charset_prober.reset()
 | 
						|
        for prober in self._charset_probers:
 | 
						|
            prober.reset()
 | 
						|
 | 
						|
    def feed(self, byte_str):
 | 
						|
        """
 | 
						|
        Takes a chunk of a document and feeds it through all of the relevant
 | 
						|
        charset probers.
 | 
						|
 | 
						|
        After calling ``feed``, you can check the value of the ``done``
 | 
						|
        attribute to see if you need to continue feeding the
 | 
						|
        ``UniversalDetector`` more data, or if it has made a prediction
 | 
						|
        (in the ``result`` attribute).
 | 
						|
 | 
						|
        .. note::
 | 
						|
           You should always call ``close`` when you're done feeding in your
 | 
						|
           document if ``done`` is not already ``True``.
 | 
						|
        """
 | 
						|
        if self.done:
 | 
						|
            return
 | 
						|
 | 
						|
        if not len(byte_str):
 | 
						|
            return
 | 
						|
 | 
						|
        if not isinstance(byte_str, bytearray):
 | 
						|
            byte_str = bytearray(byte_str)
 | 
						|
 | 
						|
        # First check for known BOMs, since these are guaranteed to be correct
 | 
						|
        if not self._got_data:
 | 
						|
            # If the data starts with BOM, we know it is UTF
 | 
						|
            if byte_str.startswith(codecs.BOM_UTF8):
 | 
						|
                # EF BB BF  UTF-8 with BOM
 | 
						|
                self.result = {'encoding': "UTF-8-SIG",
 | 
						|
                               'confidence': 1.0,
 | 
						|
                               'language': ''}
 | 
						|
            elif byte_str.startswith((codecs.BOM_UTF32_LE,
 | 
						|
                                      codecs.BOM_UTF32_BE)):
 | 
						|
                # FF FE 00 00  UTF-32, little-endian BOM
 | 
						|
                # 00 00 FE FF  UTF-32, big-endian BOM
 | 
						|
                self.result = {'encoding': "UTF-32",
 | 
						|
                               'confidence': 1.0,
 | 
						|
                               'language': ''}
 | 
						|
            elif byte_str.startswith(b'\xFE\xFF\x00\x00'):
 | 
						|
                # FE FF 00 00  UCS-4, unusual octet order BOM (3412)
 | 
						|
                self.result = {'encoding': "X-ISO-10646-UCS-4-3412",
 | 
						|
                               'confidence': 1.0,
 | 
						|
                               'language': ''}
 | 
						|
            elif byte_str.startswith(b'\x00\x00\xFF\xFE'):
 | 
						|
                # 00 00 FF FE  UCS-4, unusual octet order BOM (2143)
 | 
						|
                self.result = {'encoding': "X-ISO-10646-UCS-4-2143",
 | 
						|
                               'confidence': 1.0,
 | 
						|
                               'language': ''}
 | 
						|
            elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)):
 | 
						|
                # FF FE  UTF-16, little endian BOM
 | 
						|
                # FE FF  UTF-16, big endian BOM
 | 
						|
                self.result = {'encoding': "UTF-16",
 | 
						|
                               'confidence': 1.0,
 | 
						|
                               'language': ''}
 | 
						|
 | 
						|
            self._got_data = True
 | 
						|
            if self.result['encoding'] is not None:
 | 
						|
                self.done = True
 | 
						|
                return
 | 
						|
 | 
						|
        # If none of those matched and we've only see ASCII so far, check
 | 
						|
        # for high bytes and escape sequences
 | 
						|
        if self._input_state == InputState.PURE_ASCII:
 | 
						|
            if self.HIGH_BYTE_DETECTOR.search(byte_str):
 | 
						|
                self._input_state = InputState.HIGH_BYTE
 | 
						|
            elif self._input_state == InputState.PURE_ASCII and \
 | 
						|
                    self.ESC_DETECTOR.search(self._last_char + byte_str):
 | 
						|
                self._input_state = InputState.ESC_ASCII
 | 
						|
 | 
						|
        self._last_char = byte_str[-1:]
 | 
						|
 | 
						|
        # If we've seen escape sequences, use the EscCharSetProber, which
 | 
						|
        # uses a simple state machine to check for known escape sequences in
 | 
						|
        # HZ and ISO-2022 encodings, since those are the only encodings that
 | 
						|
        # use such sequences.
 | 
						|
        if self._input_state == InputState.ESC_ASCII:
 | 
						|
            if not self._esc_charset_prober:
 | 
						|
                self._esc_charset_prober = EscCharSetProber(self.lang_filter)
 | 
						|
            if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT:
 | 
						|
                self.result = {'encoding':
 | 
						|
                               self._esc_charset_prober.charset_name,
 | 
						|
                               'confidence':
 | 
						|
                               self._esc_charset_prober.get_confidence(),
 | 
						|
                               'language':
 | 
						|
                               self._esc_charset_prober.language}
 | 
						|
                self.done = True
 | 
						|
        # If we've seen high bytes (i.e., those with values greater than 127),
 | 
						|
        # we need to do more complicated checks using all our multi-byte and
 | 
						|
        # single-byte probers that are left.  The single-byte probers
 | 
						|
        # use character bigram distributions to determine the encoding, whereas
 | 
						|
        # the multi-byte probers use a combination of character unigram and
 | 
						|
        # bigram distributions.
 | 
						|
        elif self._input_state == InputState.HIGH_BYTE:
 | 
						|
            if not self._charset_probers:
 | 
						|
                self._charset_probers = [MBCSGroupProber(self.lang_filter)]
 | 
						|
                # If we're checking non-CJK encodings, use single-byte prober
 | 
						|
                if self.lang_filter & LanguageFilter.NON_CJK:
 | 
						|
                    self._charset_probers.append(SBCSGroupProber())
 | 
						|
                self._charset_probers.append(Latin1Prober())
 | 
						|
            for prober in self._charset_probers:
 | 
						|
                if prober.feed(byte_str) == ProbingState.FOUND_IT:
 | 
						|
                    self.result = {'encoding': prober.charset_name,
 | 
						|
                                   'confidence': prober.get_confidence(),
 | 
						|
                                   'language': prober.language}
 | 
						|
                    self.done = True
 | 
						|
                    break
 | 
						|
            if self.WIN_BYTE_DETECTOR.search(byte_str):
 | 
						|
                self._has_win_bytes = True
 | 
						|
 | 
						|
    def close(self):
 | 
						|
        """
 | 
						|
        Stop analyzing the current document and come up with a final
 | 
						|
        prediction.
 | 
						|
 | 
						|
        :returns:  The ``result`` attribute, a ``dict`` with the keys
 | 
						|
                   `encoding`, `confidence`, and `language`.
 | 
						|
        """
 | 
						|
        # Don't bother with checks if we're already done
 | 
						|
        if self.done:
 | 
						|
            return self.result
 | 
						|
        self.done = True
 | 
						|
 | 
						|
        if not self._got_data:
 | 
						|
            self.logger.debug('no data received!')
 | 
						|
 | 
						|
        # Default to ASCII if it is all we've seen so far
 | 
						|
        elif self._input_state == InputState.PURE_ASCII:
 | 
						|
            self.result = {'encoding': 'ascii',
 | 
						|
                           'confidence': 1.0,
 | 
						|
                           'language': ''}
 | 
						|
 | 
						|
        # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD
 | 
						|
        elif self._input_state == InputState.HIGH_BYTE:
 | 
						|
            prober_confidence = None
 | 
						|
            max_prober_confidence = 0.0
 | 
						|
            max_prober = None
 | 
						|
            for prober in self._charset_probers:
 | 
						|
                if not prober:
 | 
						|
                    continue
 | 
						|
                prober_confidence = prober.get_confidence()
 | 
						|
                if prober_confidence > max_prober_confidence:
 | 
						|
                    max_prober_confidence = prober_confidence
 | 
						|
                    max_prober = prober
 | 
						|
            if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD):
 | 
						|
                charset_name = max_prober.charset_name
 | 
						|
                lower_charset_name = max_prober.charset_name.lower()
 | 
						|
                confidence = max_prober.get_confidence()
 | 
						|
                # Use Windows encoding name instead of ISO-8859 if we saw any
 | 
						|
                # extra Windows-specific bytes
 | 
						|
                if lower_charset_name.startswith('iso-8859'):
 | 
						|
                    if self._has_win_bytes:
 | 
						|
                        charset_name = self.ISO_WIN_MAP.get(lower_charset_name,
 | 
						|
                                                            charset_name)
 | 
						|
                self.result = {'encoding': charset_name,
 | 
						|
                               'confidence': confidence,
 | 
						|
                               'language': max_prober.language}
 | 
						|
 | 
						|
        # Log all prober confidences if none met MINIMUM_THRESHOLD
 | 
						|
        if self.logger.getEffectiveLevel() == logging.DEBUG:
 | 
						|
            if self.result['encoding'] is None:
 | 
						|
                self.logger.debug('no probers hit minimum threshold')
 | 
						|
                for group_prober in self._charset_probers:
 | 
						|
                    if not group_prober:
 | 
						|
                        continue
 | 
						|
                    if isinstance(group_prober, CharSetGroupProber):
 | 
						|
                        for prober in group_prober.probers:
 | 
						|
                            self.logger.debug('%s %s confidence = %s',
 | 
						|
                                              prober.charset_name,
 | 
						|
                                              prober.language,
 | 
						|
                                              prober.get_confidence())
 | 
						|
                    else:
 | 
						|
                        self.logger.debug('%s %s confidence = %s',
 | 
						|
                                          prober.charset_name,
 | 
						|
                                          prober.language,
 | 
						|
                                          prober.get_confidence())
 | 
						|
        return self.result
 |