Esempio n. 1
0
class SendStream:
    def __init__(self, data : bytes = b''):
        self.byteArray = QByteArray(data)
        self.buffer = QBuffer(self.byteArray)
        self.buffer.open(QBuffer.ReadOnly if data else QBuffer.WriteOnly)
        self.stream = QDataStream(self.buffer)

    def atEnd(self):
        return self.stream.atEnd()

    def data(self):
        return self.byteArray.data()

    def clear(self):
        self.buffer.reset()
        self.byteArray.resize(0)

    def send(self, tcpSocket : QTcpSocket):
        writeInt(tcpSocket, len(self.byteArray))
        tcpSocket.write(self.byteArray)

    def writeBytes(self, data: bytes):
        self.buffer.write(b'\xFE')
        self.stream.writeBytes(data)

    def readBytes(self):
        controlByte = self.buffer.read(1)
        if controlByte != b'\xFE':
            raise Exception('Failed to read bytes from stream')
        return self.stream.readBytes()

    def writeString(self, s: str):
        self.buffer.write(b'\xFD')
        self.stream.writeBytes(s.encode())

    def readString(self):
        controlByte = self.buffer.read(1)
        if controlByte != b'\xFD':
            raise Exception('Failed to read string from stream.')
        return self.stream.readBytes().decode()

    def writeInt(self, i: int):
        self.buffer.write(b'\xFC')
        self.stream.writeInt(i)

    def readInt(self):
        controlByte = self.buffer.read(1)
        if controlByte != b'\xFC':
            raise Exception('Failed to read int from stream.')
        return self.stream.readInt()

    def writeBool(self, b: bool):
        self.buffer.write(b'\xFB' if b else b'\xFA')

    def readBool(self):
        b = self.buffer.read(1)
        if b == b'\xFB':
            return True
        elif b == b'\xFA':
            return False
        else:
            raise Exception('Failed to read bool from stream')
Esempio n. 2
0
class HelpSchemeReply(QIODevice):
    """
    Class implementing a reply for a requested qthelp: page.

    @signal closed emitted to signal that the web engine has read
        the data

    see: https://fossies.org/linux/eric6/eric/WebBrowser/Network/QtHelpSchemeHandler.py
    All credits for this class go to:
    Detlev Offenbach, the developer of The Eric Python IDE(https://eric-ide.python-projects.org)

    """
    closed = pyqtSignal()

    def __init__(self, job, engine, parent=None):
        """
        Constructor

        @param job reference to the URL request
        @type QWebEngineUrlRequestJob
        @param engine reference to the help engine
        @type QHelpEngine
        @param parent reference to the parent object
        @type QObject
        """
        super(HelpSchemeReply, self).__init__(parent)

        url = job.requestUrl()
        strUrl = url.toString()

        self.__buffer = QBuffer()

        # For some reason the url to load maybe wrong (passed from web engine)
        # though the css file and the references inside should work that way.
        # One possible problem might be that the css is loaded at the same
        # level as the html, thus a path inside the css like
        # (../images/foo.png) might cd out of the virtual folder
        if not engine.findFile(url).isValid():
            if strUrl.startswith(QtHelp_DOCROOT):
                newUrl = job.requestUrl()
                if not newUrl.path().startswith("/qdoc/"):
                    newUrl.setPath("/qdoc" + newUrl.path())
                    url = newUrl
                    strUrl = url.toString()

        self.__mimeType = mimetypes.guess_type(strUrl)[0]
        if self.__mimeType is None:
            # do our own (limited) guessing
            self.__mimeType = self.__mimeFromUrl(url)

        if engine.findFile(url).isValid():
            data = engine.fileData(url)
        else:
            data = QByteArray(self.tr(
                """<html>"""
                """<head><title>Error 404...</title></head>"""
                """<body><div align="center"><br><br>"""
                """<h1>The page could not be found</h1><br>"""
                """<h3>'{0}'</h3></div></body>"""
                """</html>""").format(strUrl)
                              .encode("utf-8"))

        self.__buffer.setData(data)
        self.__buffer.open(QIODevice.ReadOnly)
        self.open(QIODevice.ReadOnly)

    def bytesAvailable(self):
        """
        Public method to get the number of available bytes.

        @return number of available bytes
        @rtype int
        """
        return self.__buffer.bytesAvailable()

    def readData(self, maxlen):
        """
        Public method to retrieve data from the reply object.

        @param maxlen maximum number of bytes to read (integer)
        @return string containing the data (bytes)
        """
        return self.__buffer.read(maxlen)

    def close(self):
        """
        Public method used to cloase the reply.
        """
        super(HelpSchemeReply, self).close()
        self.closed.emit()

    def __mimeFromUrl(self, url):
        """
        Private method to guess the mime type given an URL.

        @param url URL to guess the mime type from (QUrl)
        @return mime type for the given URL (string)
        """
        path = url.path()
        ext = os.path.splitext(path)[1].lower()
        if ext in ExtensionMap:
            return ExtensionMap[ext]
        else:
            return "application/octet-stream"

    def mimeType(self):
        """
        Public method to get the reply mime type.

        @return mime type of the reply
        @rtype bytes
        """
        return self.__mimeType.encode("utf-8")
Esempio n. 3
0
class EricSchemeReply(QIODevice):
    """
    Class implementing a reply for a requested eric: page.
    
    @signal closed emitted to signal that the web engine has read
        the data
    """
    closed = pyqtSignal()

    _speedDialPage = ""

    def __init__(self, job, parent=None):
        """
        Constructor
        
        @param job reference to the URL request
        @type QWebEngineUrlRequestJob
        @param parent reference to the parent object
        @type QObject
        """
        super(EricSchemeReply, self).__init__(parent)

        self.__loaded = False
        self.__job = job
        self.__mutex = QMutex()

        self.__pageName = self.__job.requestUrl().path()
        self.__buffer = QBuffer()

        self.__loadPage()

    def __loadPage(self):
        """
        Private method to load the requested page.
        """
        if self.__loaded:
            return

        lock = QMutexLocker(self.__mutex)

        if self.__pageName == "adblock":
            contents = self.__adBlockPage()
        elif self.__pageName in ["home", "start", "startpage"]:
            contents = self.__startPage()
        elif self.__pageName == "speeddial":
            contents = self.__speedDialPage()
        else:
            contents = ""

        self.__buffer.setData(contents.encode("utf-8"))
        self.__buffer.open(QIODevice.ReadOnly)
        self.open(QIODevice.ReadOnly)
        lock.unlock()

        self.readyRead.emit()

        self.__loaded = True

    def bytesAvailable(self):
        """
        Public method to get the number of available bytes.
        
        @return number of available bytes
        @rtype int
        """
        lock = QMutexLocker(self.__mutex)  # __IGNORE_WARNING__
        return self.__buffer.bytesAvailable()

    def readData(self, maxlen):
        """
        Public method to retrieve data from the reply object.
        
        @param maxlen maximum number of bytes to read (integer)
        @return string containing the data (bytes)
        """
        lock = QMutexLocker(self.__mutex)  # __IGNORE_WARNING__
        return self.__buffer.read(maxlen)

    def close(self):
        """
        Public method used to cloase the reply.
        """
        super(EricSchemeReply, self).close()
        self.closed.emit()

    def __adBlockPage(self):
        """
        Private method to build the AdBlock page.
        
        @return built AdBlock page
        @rtype str
        """
        query = QUrlQuery(self.__job.requestUrl())
        rule = query.queryItemValue("rule")
        subscription = query.queryItemValue("subscription")
        title = self.tr("Content blocked by AdBlock Plus")
        message = self.tr("Blocked by rule: <i>{0} ({1})</i>").format(
            rule, subscription)

        page = readAllFileContents(":/html/adblockPage.html")
        page = page.replace("@FAVICON@", "qrc:icons/adBlockPlus16.png")
        page = page.replace("@IMAGE@", "qrc:icons/adBlockPlus64.png")
        page = page.replace("@TITLE@", title)
        page = page.replace("@MESSAGE@", message)

        return page

    def __startPage(self):
        """
        Private method to build the Start page.
        
        @return built Start page
        @rtype str
        """
        page = readAllFileContents(":/html/startPage.html")
        page = page.replace("@FAVICON@", "qrc:icons/ericWeb16.png")
        page = page.replace("@IMAGE@", "qrc:icons/ericWeb32.png")
        page = page.replace("@TITLE@",
                            self.tr("Welcome to eric6 Web Browser!"))
        page = page.replace("@ERIC_LINK@", self.tr("About eric6"))
        page = page.replace("@HEADER_TITLE@", self.tr("eric6 Web Browser"))
        page = page.replace("@SUBMIT@", self.tr("Search!"))
        if qApp.isLeftToRight():
            ltr = "LTR"
        else:
            ltr = "RTL"
        page = page.replace("@QT_LAYOUT_DIRECTION@", ltr)

        return page

    def __speedDialPage(self):
        """
        Private method to create the Speeddial page.
        
        @return prepared speeddial page (QByteArray)
        """
        if not self._speedDialPage:
            page = readAllFileContents(":/html/speeddialPage.html")
            page = page.replace("@FAVICON@", "qrc:icons/ericWeb16.png")
            page = page.replace("@IMG_PLUS@", "qrc:icons/plus.png")
            page = page.replace("@IMG_CLOSE@", "qrc:icons/close.png")
            page = page.replace("@IMG_EDIT@", "qrc:icons/edit.png")
            page = page.replace("@IMG_RELOAD@", "qrc:icons/reload.png")
            page = page.replace("@IMG_SETTINGS@", "qrc:icons/setting.png")
            page = page.replace("@LOADING-IMG@", "qrc:icons/loading.gif")
            page = page.replace("@BOX-BORDER@",
                                "qrc:icons/box-border-small.png")

            page = page.replace("@JQUERY@", "qrc:javascript/jquery.js")
            page = page.replace("@JQUERY-UI@", "qrc:javascript/jquery-ui.js")

            page = page.replace("@SITE-TITLE@", self.tr("Speed Dial"))
            page = page.replace("@URL@", self.tr("URL"))
            page = page.replace("@TITLE@", self.tr("Title"))
            page = page.replace("@APPLY@", self.tr("Apply"))
            page = page.replace("@CLOSE@", self.tr("Close"))
            page = page.replace("@NEW-PAGE@", self.tr("New Page"))
            page = page.replace("@TITLE-EDIT@", self.tr("Edit"))
            page = page.replace("@TITLE-REMOVE@", self.tr("Remove"))
            page = page.replace("@TITLE-RELOAD@", self.tr("Reload"))
            page = page.replace(
                "@TITLE-WARN@",
                self.tr("Are you sure to remove this"
                        " speed dial?"))
            page = page.replace(
                "@TITLE-WARN-REL@",
                self.tr("Are you sure you want to reload"
                        " all speed dials?"))
            page = page.replace("@TITLE-FETCHTITLE@",
                                self.tr("Load title from page"))
            page = page.replace("@SETTINGS-TITLE@",
                                self.tr("Speed Dial Settings"))
            page = page.replace("@ADD-TITLE@", self.tr("Add New Page"))
            page = page.replace("@TXT_NRROWS@",
                                self.tr("Maximum pages in a row:"))
            page = page.replace("@TXT_SDSIZE@",
                                self.tr("Change size of pages:"))
            page = page.replace(
                "@JAVASCRIPT_DISABLED@",
                self.tr("SpeedDial requires enabled"
                        " JavaScript."))

            self._speedDialPage = page

        from WebBrowser.WebBrowserWindow import WebBrowserWindow
        dial = WebBrowserWindow.speedDial()
        page = (self._speedDialPage.replace(
            "@INITIAL-SCRIPT@", dial.initialScript()).replace(
                "@ROW-PAGES@",
                str(dial.pagesInRow())).replace("@SD-SIZE@",
                                                str(dial.sdSize())))

        return page
Esempio n. 4
0
class HexEditChunks(object):
    """
    Class implementing the storage backend for the hex editor.
    
    When HexEditWidget loads data, HexEditChunks access them using a QIODevice
    interface. When the app uses a QByteArray or Python bytearray interface,
    QBuffer is used to provide again a QIODevice like interface. No data will
    be changed, therefore HexEditChunks opens the QIODevice in
    QIODevice.ReadOnly mode. After every access HexEditChunks closes the
    QIODevice. That's why external applications can overwrite files while
    HexEditWidget shows them.

    When the the user starts to edit the data, HexEditChunks creates a local
    copy of a chunk of data (4 kilobytes) and notes all changes there. Parallel
    to that chunk, there is a second chunk, which keeps track of which bytes
    are changed and which are not.
    """
    BUFFER_SIZE = 0x10000
    CHUNK_SIZE = 0x1000
    READ_CHUNK_MASK = 0xfffffffffffff000

    def __init__(self, ioDevice=None):
        """
        Constructor
        
        @param ioDevice io device to get the data from
        @type QIODevice
        """
        self.__ioDevice = None
        self.__pos = 0
        self.__size = 0
        self.__chunks = []

        if ioDevice is None:
            buf = QBuffer()
            self.setIODevice(buf)
        else:
            self.setIODevice(ioDevice)

    def setIODevice(self, ioDevice):
        """
        Public method to set an io device to read the binary data from.
        
        @param ioDevice io device to get the data from
        @type QIODevice
        @return flag indicating successful operation
        @rtype bool
        """
        self.__ioDevice = ioDevice
        ok = self.__ioDevice.open(QIODevice.ReadOnly)
        if ok:
            # open successfully
            self.__size = self.__ioDevice.size()
            self.__ioDevice.close()
        else:
            # fallback is an empty buffer
            self.__ioDevice = QBuffer()
            self.__size = 0

        self.__chunks = []
        self.__pos = 0

        return ok

    def data(self, pos=0, maxSize=-1, highlighted=None):
        """
        Public method to get data out of the chunks.
        
        @param pos position to get bytes from
        @type int
        @param maxSize maximum amount of bytes to get
        @type int
        @param highlighted reference to a byte array storing highlighting info
        @type bytearray
        @return retrieved data
        @rtype bytearray
        """
        ioDelta = 0
        chunkIdx = 0

        chunk = HexEditChunk()
        buffer = bytearray()

        if highlighted is not None:
            del highlighted[:]

        if pos >= self.__size:
            return buffer

        if maxSize < 0:
            maxSize = self.__size
        elif (pos + maxSize) > self.__size:
            maxSize = self.__size - pos

        self.__ioDevice.open(QIODevice.ReadOnly)

        while maxSize > 0:
            chunk.absPos = sys.maxsize
            chunksLoopOngoing = True
            while chunkIdx < len(self.__chunks) and chunksLoopOngoing:
                # In this section, we track changes before our required data
                # and we take the edited data, if availible. ioDelta is a
                # difference counter to justify the read pointer to the
                # original data, if data in between was deleted or inserted.

                chunk = self.__chunks[chunkIdx]
                if chunk.absPos > pos:
                    chunksLoopOngoing = False
                else:
                    chunkIdx += 1
                    chunkOfs = pos - chunk.absPos
                    if maxSize > (len(chunk.data) - chunkOfs):
                        count = len(chunk.data) - chunkOfs
                        ioDelta += self.CHUNK_SIZE - len(chunk.data)
                    else:
                        count = maxSize
                    if count > 0:
                        buffer += chunk.data[chunkOfs:chunkOfs + count]
                        maxSize -= count
                        pos += count
                        if highlighted is not None:
                            highlighted += chunk.dataChanged[
                                chunkOfs:chunkOfs + count]

            if maxSize > 0 and pos < chunk.absPos:
                # In this section, we read data from the original source. This
                # will only happen, when no copied data is available.
                if chunk.absPos - pos > maxSize:
                    byteCount = maxSize
                else:
                    byteCount = chunk.absPos - pos

                maxSize -= byteCount
                self.__ioDevice.seek(pos + ioDelta)
                readBuffer = bytearray(self.__ioDevice.read(byteCount))
                buffer += readBuffer
                if highlighted is not None:
                    highlighted += bytearray(len(readBuffer))
                pos += len(readBuffer)

        self.__ioDevice.close()
        return buffer

    def write(self, ioDevice, pos=0, count=-1):
        """
        Public method to write data to an io device.
        
        @param ioDevice io device to write the data to
        @type QIODevice
        @param pos position to write bytes from
        @type int
        @param count amount of bytes to write
        @type int
        @return flag indicating success
        @rtype bool
        """
        if count == -1:
            # write all data
            count = self.__size

        ok = ioDevice.open(QIODevice.WriteOnly)
        if ok:
            idx = pos
            while idx < count:
                data = self.data(idx, self.BUFFER_SIZE)
                ioDevice.write(QByteArray(data))

                # increment loop variable
                idx += self.BUFFER_SIZE

            ioDevice.close()

        return ok

    def setDataChanged(self, pos, dataChanged):
        """
        Public method to set highlighting info.
        
        @param pos position to set highlighting info for
        @type int
        @param dataChanged flag indicating changed data
        @type bool
        """
        if pos < 0 or pos >= self.__size:
            # position is out of range, do nothing
            return
        chunkIdx = self.__getChunkIndex(pos)
        posInChunk = pos - self.__chunks[chunkIdx].absPos
        self.__chunks[chunkIdx].dataChanged[posInChunk] = int(dataChanged)

    def dataChanged(self, pos):
        """
        Public method to test, if some data was changed.
        
        @param pos byte position to check
        @type int
        @return flag indicating the changed state
        @rtype bool
        """
        highlighted = bytearray()
        self.data(pos, 1, highlighted)
        return highlighted and bool(highlighted[0])

    def indexOf(self, byteArray, start):
        """
        Public method to search the first occurrence of some data.
        
        @param byteArray data to search for
        @type bytearray
        @param start position to start the search at
        @type int
        @return position the data was found at or -1 if nothing could be found
        @rtype int
        """
        ba = bytearray(byteArray)

        result = -1
        pos = start
        while pos < self.__size:
            buffer = self.data(pos, self.BUFFER_SIZE + len(ba) - 1)
            findPos = buffer.find(ba)
            if findPos >= 0:
                result = pos + findPos
                break

            # increment loop variable
            pos += self.BUFFER_SIZE

        return result

    def lastIndexOf(self, byteArray, start):
        """
        Public method to search the last occurrence of some data.
        
        @param byteArray data to search for
        @type bytearray
        @param start position to start the search at
        @type int
        @return position the data was found at or -1 if nothing could be found
        @rtype int
        """
        ba = bytearray(byteArray)

        result = -1
        pos = start
        while pos > 0 and result < 0:
            sPos = pos - self.BUFFER_SIZE - len(ba) + 1
            if sPos < 0:
                sPos = 0

            buffer = self.data(sPos, pos - sPos)
            findPos = buffer.rfind(ba)
            if findPos >= 0:
                result = sPos + findPos
                break

            # increment loop variable
            pos -= self.BUFFER_SIZE

        return result

    def insert(self, pos, data):
        """
        Public method to insert a byte.
        
        @param pos position to insert at
        @type int
        @param data byte to insert
        @type int (range 0 to 255)
        @return flag indicating success
        @rtype bool
        """
        if pos < 0 or pos > self.__size:
            # position is out of range, do nothing
            return False

        if pos == self.__size:
            chunkIdx = self.__getChunkIndex(pos - 1)
        else:
            chunkIdx = self.__getChunkIndex(pos)
        chunk = self.__chunks[chunkIdx]
        posInChunk = pos - chunk.absPos
        chunk.data.insert(posInChunk, data)
        chunk.dataChanged.insert(posInChunk, 1)
        for idx in range(chunkIdx + 1, len(self.__chunks)):
            self.__chunks[idx].absPos += 1
        self.__size += 1
        self.__pos = pos
        return True

    def overwrite(self, pos, data):
        """
        Public method to overwrite a byte.
        
        @param pos position to overwrite
        @type int
        @param data byte to overwrite with
        @type int (range 0 to 255)
        @return flag indicating success
        @rtype bool
        """
        if pos < 0 or pos >= self.__size:
            # position is out of range, do nothing
            return False

        chunkIdx = self.__getChunkIndex(pos)
        chunk = self.__chunks[chunkIdx]
        posInChunk = pos - chunk.absPos
        chunk.data[posInChunk] = data
        chunk.dataChanged[posInChunk] = 1
        self.__pos = pos
        return True

    def removeAt(self, pos):
        """
        Public method to remove a byte.
        
        @param pos position to remove
        @type int
        @return flag indicating success
        @rtype bool
        """
        if pos < 0 or pos >= self.__size:
            # position is out of range, do nothing
            return False

        chunkIdx = self.__getChunkIndex(pos)
        chunk = self.__chunks[chunkIdx]
        posInChunk = pos - chunk.absPos
        chunk.data.pop(posInChunk)
        chunk.dataChanged.pop(posInChunk)
        for idx in range(chunkIdx + 1, len(self.__chunks)):
            self.__chunks[idx].absPos -= 1
        self.__size -= 1
        self.__pos = pos
        return True

    def __getitem__(self, pos):
        """
        Special method to get a byte at a position.
        
        Note: This realizes the [] get operator.
        
        @param pos position of byte to get
        @type int
        @return requested byte
        @rtype int (range 0 to 255)
        """
        if pos >= self.__size:
            return 0
##            raise IndexError

        return self.data(pos, 1)[0]

    def pos(self):
        """
        Public method to get the current position.
        
        @return current position
        @rtype int
        """
        return self.__pos

    def size(self):
        """
        Public method to get the current data size.
        
        @return current data size
        @rtype int
        """
        return self.__size

    def __getChunkIndex(self, absPos):
        """
        Private method to get the chunk index for a position.
        
        This method checks, if there is already a copied chunk available. If
        there is one, it returns its index. If there is no copied chunk
        available, original data will be copied into a new chunk.
        
        @param absPos absolute position of the data.
        @type int
        @return index of the chunk containing the position
        @rtype int
        """
        foundIdx = -1
        insertIdx = 0
        ioDelta = 0

        for idx in range(len(self.__chunks)):
            chunk = self.__chunks[idx]
            if (absPos >= chunk.absPos and absPos <
                (chunk.absPos + len(chunk.data))):
                foundIdx = idx
                break

            if absPos < chunk.absPos:
                insertIdx = idx
                break

            ioDelta += len(chunk.data) - self.CHUNK_SIZE
            insertIdx = idx + 1

        if foundIdx == -1:
            newChunk = HexEditChunk()
            readAbsPos = absPos - ioDelta
            readPos = readAbsPos & self.READ_CHUNK_MASK
            self.__ioDevice.open(QIODevice.ReadOnly)
            self.__ioDevice.seek(readPos)
            newChunk.data = bytearray(self.__ioDevice.read(self.CHUNK_SIZE))
            self.__ioDevice.close()
            newChunk.absPos = absPos - (readAbsPos - readPos)
            newChunk.dataChanged = bytearray(len(newChunk.data))
            self.__chunks.insert(insertIdx, newChunk)
            foundIdx = insertIdx

        return foundIdx
Esempio n. 5
0
class HexEditChunks(object):
    """
    Class implementing the storage backend for the hex editor.
    
    When HexEditWidget loads data, HexEditChunks access them using a QIODevice
    interface. When the app uses a QByteArray or Python bytearray interface,
    QBuffer is used to provide again a QIODevice like interface. No data will
    be changed, therefore HexEditChunks opens the QIODevice in
    QIODevice.ReadOnly mode. After every access HexEditChunks closes the
    QIODevice. That's why external applications can overwrite files while
    HexEditWidget shows them.

    When the the user starts to edit the data, HexEditChunks creates a local
    copy of a chunk of data (4 kilobytes) and notes all changes there. Parallel
    to that chunk, there is a second chunk, which keeps track of which bytes
    are changed and which are not.
    """
    BUFFER_SIZE = 0x10000
    CHUNK_SIZE = 0x1000
    READ_CHUNK_MASK = 0xfffffffffffff000
    
    def __init__(self, ioDevice=None):
        """
        Constructor
        
        @param ioDevice io device to get the data from
        @type QIODevice
        """
        self.__ioDevice = None
        self.__pos = 0
        self.__size = 0
        self.__chunks = []
        
        if ioDevice is None:
            buf = QBuffer()
            self.setIODevice(buf)
        else:
            self.setIODevice(ioDevice)
    
    def setIODevice(self, ioDevice):
        """
        Public method to set an io device to read the binary data from.
        
        @param ioDevice io device to get the data from
        @type QIODevice
        @return flag indicating successful operation
        @rtype bool
        """
        self.__ioDevice = ioDevice
        ok = self.__ioDevice.open(QIODevice.ReadOnly)
        if ok:
            # open successfully
            self.__size = self.__ioDevice.size()
            self.__ioDevice.close()
        else:
            # fallback is an empty buffer
            self.__ioDevice = QBuffer()
            self.__size = 0
        
        self.__chunks = []
        self.__pos = 0
        
        return ok
    
    def data(self, pos=0, maxSize=-1, highlighted=None):
        """
        Public method to get data out of the chunks.
        
        @param pos position to get bytes from
        @type int
        @param maxSize maximum amount of bytes to get
        @type int
        @param highlighted reference to a byte array storing highlighting info
        @type bytearray
        @return retrieved data
        @rtype bytearray
        """
        ioDelta = 0
        chunkIdx = 0
        
        chunk = HexEditChunk()
        buffer = bytearray()
        
        if highlighted is not None:
            del highlighted[:]
        
        if pos >= self.__size:
            return buffer
        
        if maxSize < 0:
            maxSize = self.__size
        elif (pos + maxSize) > self.__size:
            maxSize = self.__size - pos
        
        self.__ioDevice.open(QIODevice.ReadOnly)
        
        while maxSize > 0:
            chunk.absPos = sys.maxsize
            chunksLoopOngoing = True
            while chunkIdx < len(self.__chunks) and chunksLoopOngoing:
                # In this section, we track changes before our required data
                # and we take the edited data, if availible. ioDelta is a
                # difference counter to justify the read pointer to the
                # original data, if data in between was deleted or inserted.
                
                chunk = self.__chunks[chunkIdx]
                if chunk.absPos > pos:
                    chunksLoopOngoing = False
                else:
                    chunkIdx += 1
                    chunkOfs = pos - chunk.absPos
                    if maxSize > (len(chunk.data) - chunkOfs):
                        count = len(chunk.data) - chunkOfs
                        ioDelta += self.CHUNK_SIZE - len(chunk.data)
                    else:
                        count = maxSize
                    if count > 0:
                        buffer += chunk.data[chunkOfs:chunkOfs + count]
                        maxSize -= count
                        pos += count
                        if highlighted is not None:
                            highlighted += \
                                chunk.dataChanged[chunkOfs:chunkOfs + count]
            
            if maxSize > 0 and pos < chunk.absPos:
                # In this section, we read data from the original source. This
                # will only happen, when no copied data is available.
                if chunk.absPos - pos > maxSize:
                    byteCount = maxSize
                else:
                    byteCount = chunk.absPos - pos
                
                maxSize -= byteCount
                self.__ioDevice.seek(pos + ioDelta)
                readBuffer = bytearray(self.__ioDevice.read(byteCount))
                buffer += readBuffer
                if highlighted is not None:
                    highlighted += bytearray(len(readBuffer))
                pos += len(readBuffer)
        
        self.__ioDevice.close()
        return buffer
    
    def write(self, ioDevice, pos=0, count=-1):
        """
        Public method to write data to an io device.
        
        @param ioDevice io device to write the data to
        @type QIODevice
        @param pos position to write bytes from
        @type int
        @param count amount of bytes to write
        @type int
        @return flag indicating success
        @rtype bool
        """
        if count == -1:
            # write all data
            count = self.__size
        
        ok = ioDevice.open(QIODevice.WriteOnly)
        if ok:
            idx = pos
            while idx < count:
                data = self.data(idx, self.BUFFER_SIZE)
                ioDevice.write(QByteArray(data))
                
                # increment loop variable
                idx += self.BUFFER_SIZE
            
            ioDevice.close()
        
        return ok
    
    def setDataChanged(self, pos, dataChanged):
        """
        Public method to set highlighting info.
        
        @param pos position to set highlighting info for
        @type int
        @param dataChanged flag indicating changed data
        @type bool
        """
        if pos < 0 or pos >= self.__size:
            # position is out of range, do nothing
            return
        chunkIdx = self.__getChunkIndex(pos)
        posInChunk = pos - self.__chunks[chunkIdx].absPos
        self.__chunks[chunkIdx].dataChanged[posInChunk] = int(dataChanged)
    
    def dataChanged(self, pos):
        """
        Public method to test, if some data was changed.
        
        @param pos byte position to check
        @type int
        @return flag indicating the changed state
        @rtype bool
        """
        highlighted = bytearray()
        self.data(pos, 1, highlighted)
        return highlighted and bool(highlighted[0])
    
    def indexOf(self, byteArray, start):
        """
        Public method to search the first occurrence of some data.
        
        @param byteArray data to search for
        @type bytearray
        @param start position to start the search at
        @type int
        @return position the data was found at or -1 if nothing could be found
        @rtype int
        """
        ba = bytearray(byteArray)
        
        result = -1
        pos = start
        while pos < self.__size:
            buffer = self.data(pos, self.BUFFER_SIZE + len(ba) - 1)
            findPos = buffer.find(ba)
            if findPos >= 0:
                result = pos + findPos
                break
            
            # increment loop variable
            pos += self.BUFFER_SIZE
        
        return result
    
    def lastIndexOf(self, byteArray, start):
        """
        Public method to search the last occurrence of some data.
        
        @param byteArray data to search for
        @type bytearray
        @param start position to start the search at
        @type int
        @return position the data was found at or -1 if nothing could be found
        @rtype int
        """
        ba = bytearray(byteArray)
        
        result = -1
        pos = start
        while pos > 0 and result < 0:
            sPos = pos - self.BUFFER_SIZE - len(ba) + 1
            if sPos < 0:
                sPos = 0
            
            buffer = self.data(sPos, pos - sPos)
            findPos = buffer.rfind(ba)
            if findPos >= 0:
                result = sPos + findPos
                break
            
            # increment loop variable
            pos -= self.BUFFER_SIZE
        
        return result
    
    def insert(self, pos, data):
        """
        Public method to insert a byte.
        
        @param pos position to insert at
        @type int
        @param data byte to insert
        @type int (range 0 to 255)
        @return flag indicating success
        @rtype bool
        """
        if pos < 0 or pos > self.__size:
            # position is out of range, do nothing
            return False
        
        if pos == self.__size:
            chunkIdx = self.__getChunkIndex(pos - 1)
        else:
            chunkIdx = self.__getChunkIndex(pos)
        chunk = self.__chunks[chunkIdx]
        posInChunk = pos - chunk.absPos
        chunk.data.insert(posInChunk, data)
        chunk.dataChanged.insert(posInChunk, 1)
        for idx in range(chunkIdx + 1, len(self.__chunks)):
            self.__chunks[idx].absPos += 1
        self.__size += 1
        self.__pos = pos
        return True
    
    def overwrite(self, pos, data):
        """
        Public method to overwrite a byte.
        
        @param pos position to overwrite
        @type int
        @param data byte to overwrite with
        @type int (range 0 to 255)
        @return flag indicating success
        @rtype bool
        """
        if pos < 0 or pos >= self.__size:
            # position is out of range, do nothing
            return False
        
        chunkIdx = self.__getChunkIndex(pos)
        chunk = self.__chunks[chunkIdx]
        posInChunk = pos - chunk.absPos
        chunk.data[posInChunk] = data
        chunk.dataChanged[posInChunk] = 1
        self.__pos = pos
        return True
    
    def removeAt(self, pos):
        """
        Public method to remove a byte.
        
        @param pos position to remove
        @type int
        @return flag indicating success
        @rtype bool
        """
        if pos < 0 or pos >= self.__size:
            # position is out of range, do nothing
            return
        
        chunkIdx = self.__getChunkIndex(pos)
        chunk = self.__chunks[chunkIdx]
        posInChunk = pos - chunk.absPos
        chunk.data.pop(posInChunk)
        chunk.dataChanged.pop(posInChunk)
        for idx in range(chunkIdx + 1, len(self.__chunks)):
            self.__chunks[idx].absPos -= 1
        self.__size -= 1
        self.__pos = pos
        return True
    
    def __getitem__(self, pos):
        """
        Special method to get a byte at a position.
        
        Note: This realizes the [] get operator.
        
        @param pos position of byte to get
        @type int
        @return requested byte
        @rtype int (range 0 to 255)
        """
        if pos >= self.__size:
            return 0
##            raise IndexError
        
        return self.data(pos, 1)[0]
    
    def pos(self):
        """
        Public method to get the current position.
        
        @return current position
        @rtype int
        """
        return self.__pos
    
    def size(self):
        """
        Public method to get the current data size.
        
        @return current data size
        @rtype int
        """
        return self.__size
    
    def __getChunkIndex(self, absPos):
        """
        Private method to get the chunk index for a position.
        
        This method checks, if there is already a copied chunk available. If
        there is one, it returns its index. If there is no copied chunk
        available, original data will be copied into a new chunk.
        
        @param absPos absolute position of the data.
        @type int
        @return index of the chunk containing the position
        @rtype int
        """
        foundIdx = -1
        insertIdx = 0
        ioDelta = 0
        
        for idx in range(len(self.__chunks)):
            chunk = self.__chunks[idx]
            if absPos >= chunk.absPos and \
                    absPos < (chunk.absPos + len(chunk.data)):
                foundIdx = idx
                break
            
            if absPos < chunk.absPos:
                insertIdx = idx
                break
            
            ioDelta += len(chunk.data) - self.CHUNK_SIZE
            insertIdx = idx + 1
        
        if foundIdx == -1:
            newChunk = HexEditChunk()
            readAbsPos = absPos - ioDelta
            readPos = readAbsPos & self.READ_CHUNK_MASK
            self.__ioDevice.open(QIODevice.ReadOnly)
            self.__ioDevice.seek(readPos)
            newChunk.data = bytearray(self.__ioDevice.read(self.CHUNK_SIZE))
            self.__ioDevice.close()
            newChunk.absPos = absPos - (readAbsPos - readPos)
            newChunk.dataChanged = bytearray(len(newChunk.data))
            self.__chunks.insert(insertIdx, newChunk)
            foundIdx = insertIdx
        
        return foundIdx
Esempio n. 6
0
class HelpSchemeReply(QIODevice):
    """
    Class implementing a reply for a requested qthelp: page.

    The Qt signal *closed* is emitted to signal that the web engine has read the data.

    see: https://fossies.org/linux/eric6/eric/WebBrowser/Network/QtHelpSchemeHandler.py
    All credits for this class go to:
    Detlev Offenbach, the developer of The Eric Python IDE(https://eric-ide.python-projects.org)

    """
    closed = pyqtSignal()

    def __init__(self, job, engine, parent=None):
        """
        Constructor

        :param job: reference to the URL request
        :type job: QWebEngineUrlRequestJob
        :param engine: reference to the help engine
        :type engine: QHelpEngine
        :param parent: reference to the parent object
        :type parent: QObject
        """
        super(HelpSchemeReply, self).__init__(parent)

        url = job.requestUrl()
        strUrl = url.toString()

        self.__buffer = QBuffer()

        # For some reason the url to load maybe wrong (passed from web engine)
        # though the css file and the references inside should work that way.
        # One possible problem might be that the css is loaded at the same
        # level as the html, thus a path inside the css like
        # (../images/foo.png) might cd out of the virtual folder
        if not engine.findFile(url).isValid():
            if strUrl.startswith(QtHelp_DOCROOT):
                newUrl = job.requestUrl()
                if not newUrl.path().startswith("/qdoc/"):
                    newUrl.setPath("/qdoc" + newUrl.path())
                    url = newUrl
                    strUrl = url.toString()

        self.__mimeType = mimetypes.guess_type(strUrl)[0]
        if self.__mimeType is None:
            # do our own (limited) guessing
            self.__mimeType = self.__mimeFromUrl(url)

        if engine.findFile(url).isValid():
            data = engine.fileData(url)
        else:
            data = QByteArray(self.tr(
                """<html>"""
                """<head><title>Error 404...</title></head>"""
                """<body><div align="center"><br><br>"""
                """<h1>The page could not be found</h1><br>"""
                """<h3>'{0}'</h3></div></body>"""
                """</html>""").format(strUrl)
                              .encode("utf-8"))

        self.__buffer.setData(data)
        self.__buffer.open(QIODevice.ReadOnly)
        self.open(QIODevice.ReadOnly)

    def bytesAvailable(self):
        """
        Public method to get the number of available bytes.

        :returns: number of available bytes
        :rtype: int
        """
        return self.__buffer.bytesAvailable()

    def readData(self, maxlen):
        """
        Public method to retrieve data from the reply object.

        :param maxlen: maximum number of bytes to read (integer)
        :returns: string containing the data (bytes)
        """
        return self.__buffer.read(maxlen)

    def close(self):
        """
        Public method used to close the reply.
        """
        super(HelpSchemeReply, self).close()
        self.closed.emit()

    def __mimeFromUrl(self, url):
        """
        Private method to guess the mime type given an URL.

        :param url: URL to guess the mime type from (QUrl)
        :returns: mime type for the given URL (string)
        """
        path = url.path()
        ext = os.path.splitext(path)[1].lower()
        if ext in ExtensionMap:
            return ExtensionMap[ext]
        else:
            return "application/octet-stream"

    def mimeType(self):
        """
        Public method to get the reply mime type.

        :returns: mime type of the reply
        :rtype: bytes
        """
        return self.__mimeType.encode("utf-8")
Esempio n. 7
0
class Chunks(QObject):
    def __init__(self, parent: QObject = None, device: QIODevice = None):
        super().__init__(parent)
        self.device = QBuffer(self) if device is None else device
        self.chunks: [Chunk] = []
        self.position = 0
        self.size = 0

        self.setIODevice(self.device)

    def setIODevice(self, device: QIODevice) -> bool:
        self.device = device
        status = self.device.open(QIODevice.ReadOnly)
        if status:
            self.size = self.device.size()
            self.device.close()
        else:
            # Fallback is an empty buffer
            self.size = 0
            self.device = QBuffer(self)
        self.chunks.clear()
        self.position = 0
        return status

    def data(self,
             position: int,
             maxSize: int = -1,
             highlighted: QByteArray = None) -> QByteArray:
        delta = 0
        chunkIdx = 0
        chunk = Chunk()
        buffer = QByteArray()
        if highlighted:
            highlighted.clear()
        if position >= self.size:
            return buffer
        if maxSize < 0:
            maxSize = self.size
        elif (position + maxSize) > self.size:
            maxSize = self.size - position

        self.device.open(QIODevice.ReadOnly)
        while maxSize > 0:
            chunk.absPos = sys.maxsize
            chunksLoopOngoing = True
            while chunkIdx < len(self.chunks) and chunksLoopOngoing:
                # In this section, we track changes before our required data and
                # we take the editdet data, if availible. ioDelta is a difference
                # counter to justify the read pointer to the original data, if
                # data in between was deleted or inserted.
                chunk = self.chunks[chunkIdx]
                if chunk.absPos > position:
                    chunksLoopOngoing = False
                else:
                    count = 0
                    chunkIdx += 1
                    chunkOfs = position - chunk.absPos
                    if maxSize > chunk.data.size() - chunkOfs:
                        count = chunk.data.size() - chunkOfs
                        delta += CHUNK_SIZE - chunk.data.size()
                    else:
                        count = maxSize

                    if count > 0:
                        buffer += chunk.data.mid(chunkOfs, count)
                        maxSize -= count
                        position += count
                        if highlighted:
                            highlighted += chunk.dataChanged.mid(
                                chunkOfs, count)
            if maxSize > 0 and position < chunk.absPos:
                byteCount = 0
                if (chunk.absPos - position) > maxSize:
                    byteCount = maxSize
                else:
                    byteCount = chunk.absPos - position
                maxSize -= byteCount
                self.device.seek(position + delta)
                readBuffer = self.device.read(byteCount)
                buffer += readBuffer
                if highlighted:
                    highlighted += QByteArray(readBuffer.size(), NORMAL)
                position += len(readBuffer)
        self.device.close()
        return buffer

    def write(self,
              device: QIODevice,
              position: int = 0,
              count: int = -1) -> bool:
        if count == -1:
            count = self.size
        status = device.open(QIODevice.WriteOnly)
        if status:
            for idx in range(position, count, BUFFER_SIZE):
                array = self.data(idx, BUFFER_SIZE)
                device.write(array)
            device.close()
        return status

    def setDataChanged(self, position: int, dataChanged: bool) -> None:
        pass

    def dataChanged(self, position: int) -> bool:
        pass

    def indexOf(self, array: QByteArray, _from: int) -> int:
        pass

    def lastIndexOf(self, array: QByteArray, _from: int) -> int:
        pass

    def insert(self, position: int, character: str) -> bool:
        pass

    def overwrite(self, position: int, character: str) -> bool:
        pass

    def removeAt(self, position: int) -> bool:
        pass

    def getPosition(self) -> int:
        pass

    def getSize(self) -> int:
        pass

    def __getitem__(self, item):
        pass