예제 #1
0
 public GzipUncompressionFilter()
     : base()
 {
     _crc        = new PhpHash.HashPhpResource.CRC32B();
     _state      = UncompressionState.Header;
     _chunkQueue = new BinaryChunkQueue();
 }
예제 #2
0
 /// <summary>
 /// Changes state based on header flags. Is called by header-handling states only.
 /// </summary>
 private void UpdateHeaderState()
 {
     switch (_state)
     {
         case UncompressionState.Header:
             if (HeaderFlag(Zlib.GZIP_HEADER_EXTRAFIELD))
                 _state = UncompressionState.HeaderExtraField;
             else if (HeaderFlag(Zlib.GZIP_HEADER_FILENAME))
                 _state = UncompressionState.HeaderFilename;
             else if (HeaderFlag(Zlib.GZIP_HEADER_COMMENT))
                 _state = UncompressionState.HeaderComment;
             else if (HeaderFlag(Zlib.GZIP_HEADER_CRC))
                 _state = UncompressionState.HeaderCRC;
             else
                 _state = UncompressionState.Data;
             break;
         case UncompressionState.HeaderExtraField:
             if (HeaderFlag(Zlib.GZIP_HEADER_FILENAME))
                 _state = UncompressionState.HeaderFilename;
             else if (HeaderFlag(Zlib.GZIP_HEADER_COMMENT))
                 _state = UncompressionState.HeaderComment;
             else if (HeaderFlag(Zlib.GZIP_HEADER_CRC))
                 _state = UncompressionState.HeaderCRC;
             else
                 _state = UncompressionState.Data;
             break;
         case UncompressionState.HeaderFilename:
             if (HeaderFlag(Zlib.GZIP_HEADER_COMMENT))
                 _state = UncompressionState.HeaderComment;
             else if (HeaderFlag(Zlib.GZIP_HEADER_CRC))
                 _state = UncompressionState.HeaderCRC;
             else
                 _state = UncompressionState.Data;
             break;
         case UncompressionState.HeaderComment:
             if (HeaderFlag(Zlib.GZIP_HEADER_CRC))
                 _state = UncompressionState.HeaderCRC;
             else
                 _state = UncompressionState.Data;
             break;
         case UncompressionState.HeaderCRC:
             _state = UncompressionState.Data;
             break;
     }
 }
예제 #3
0
        public override object Filter(object input, bool closing)
        {
            // TODO: not the most efficient method - after the filters are upgraded to bucket lists, update this

            PhpBytes bInput = Core.Convert.ObjectToPhpBytes(input);

            if (bInput != null)
            {
                if (_state == UncompressionState.Failed)
                {
                    // failed filter should not get any more calls
                    PhpException.Throw(PhpError.Warning, "using filter in failed state");
                    return null;
                }

                if (_state == UncompressionState.PostTrailer)
                {
                    // post trailer - ignore everything
                    if (closing)
                    {
                        _state = UncompressionState.Finished;
                    }

                    return new PhpBytes();
                }

                if (_state == UncompressionState.Finished)
                {
                    // finished filter should not get any more data
                    PhpException.Throw(PhpError.Warning, "using filter in finished state");
                    return null;
                }

                if (_state == UncompressionState.Passthrough)
                {
                    // this is not gzip data format - pass the data through
                    return new PhpBytes(bInput);
                }

                // enqueue the block
                _chunkQueue.EnqueueByteBlock(bInput.ReadonlyData, 0, bInput.Length);

                if (_state == UncompressionState.Header)
                {
                    #region Header handling
                    //beginning of the stream
                    byte[] beginning = _chunkQueue.DequeueByteBlock(Zlib.GZIP_HEADER_LENGTH);

                    if (beginning == null && !closing)
                    {
                        // we do not have enough data, but we know there would be more data ahead
                        return new PhpBytes();
                    }
                    else
                    {
                        //check the header format
                        if (beginning.Length >= 2 && beginning[0] == Zlib.GZIP_HEADER[0] && beginning[1] == Zlib.GZIP_HEADER[1])
                        {
                            //header magic bytes are OK
                            if (beginning.Length < Zlib.GZIP_HEADER_LENGTH)
                            {
                                // header is too short -> this is an error
                                PhpException.Throw(PhpError.Warning, "unexpected end of file");
                                return null;
                            }
                            else
                            {
                                // check the rest of the header
                                if (beginning[2] != Zlib.Z_DEFLATED)
                                {
                                    PhpException.Throw(PhpError.Warning, "unknown compression method");
                                    return null;
                                }

                                if ((beginning[3] & Zlib.GZIP_HEADER_RESERVED_FLAGS) != 0)
                                {
                                    PhpException.Throw(PhpError.Warning, "unknown header flags set");
                                    return null;
                                }

                                _headerFlags = beginning[3];

                                //change the header state based on the header flags
                                UpdateHeaderState();
                            }
                        }
                        else
                        {
                            // this is not a gzip format -> passthrough the data
                            _state = UncompressionState.Passthrough;
                            return new PhpBytes(beginning);
                        }
                    }
                    #endregion
                }

                if (_state == UncompressionState.HeaderExtraField)
                {
                    #region Header Extra Field Handling
                    if (_extraHeaderLength == null)
                    {
                        //length was not yet detected
                        if (_chunkQueue.AvailableBytes < 2)
                        {
                            //wait for more input
                            return new PhpBytes();
                        }
                        else
                        {
                            //assemble length
                            _extraHeaderLength = _chunkQueue.DequeueByte();
                            _extraHeaderLength &= (_chunkQueue.DequeueByte() << 8);
                        }
                    }

                    if (_extraHeaderLength != null)
                    {
                        //length was already read
                        if (_chunkQueue.AvailableBytes < _extraHeaderLength)
                        {
                            //wait for more input
                            return new PhpBytes();
                        }
                        else
                        {
                            Debug.Assert(_extraHeaderLength.HasValue);

                            //skip the extra header
                            _chunkQueue.SkipByteBlock(_extraHeaderLength.Value);

                            UpdateHeaderState();
                        }
                    }
                    #endregion
                }

                if (_state == UncompressionState.HeaderFilename || _state == UncompressionState.HeaderComment)
                {
                    #region Header Filename and Comment Handling
                    // filename or comment

                    // cycle until input ends or zero character is encountered
                    while (true)
                    {
                        byte? nextByte = _chunkQueue.DequeueByte();

                        if (nextByte == null)
                        {
                            //wait for more input
                            return new PhpBytes();
                        }

                        if (nextByte == 0)
                        {
                            // end the cycle
                            break;
                        }
                    }

                    // go to the next state
                    UpdateHeaderState();
                    #endregion
                }

                if (_state == UncompressionState.HeaderCRC)
                {
                    #region CRC Handling
                    // header CRC

                    if (_chunkQueue.AvailableBytes < 2)
                    {
                        //wait for more input
                        return new PhpBytes();
                    }
                    else
                    {
                        //skip the CRC
                        _chunkQueue.DequeueByte();
                        _chunkQueue.DequeueByte();

                        UpdateHeaderState();
                    }
                    #endregion
                }

                //filled by data handling and sometimes returned by trailer handling
                byte[] output = null;

                if (_state == UncompressionState.Data)
                {
                    #region Deflated Data Handling

                    //get all available bytes
                    byte[] inputBytes = _chunkQueue.DequeueByteBlock(_chunkQueue.AvailableBytes);
                    int inputOffset = 0;

                    // perform the inner operation
                    try
                    {
                        output = FilterInner(inputBytes, ref inputOffset, closing);
                    }
                    catch
                    {
                        // exception was thrown
                        _state = UncompressionState.Failed;
                        throw;
                    }

                    if (output == null)
                    {
                        // error happened and exception was not thrown
                        _state = UncompressionState.Failed;
                        return null;
                    }

                    // update the hash algorithm
                    _crc.Update(output);

                    if (inputOffset != inputBytes.Length)
                    {
                        // push the rest of the data into the chunk queue
                        _chunkQueue.PushByteBlock(inputBytes, inputOffset, inputBytes.Length - inputOffset);

                        // end of deflated block reached
                        _state = UncompressionState.Trailer;

                        // pass through to Trailer handling
                    }
                    else
                    {
                        //normal decompressed block - return it
                        return new PhpBytes(output);
                    }

                    #endregion
                }

                if (_state == UncompressionState.Trailer)
                {
                    #region Trailer Handling
                    // the deflate block has already ended, we are processing trailer
                    if (closing || _chunkQueue.AvailableBytes >= Zlib.GZIP_FOOTER_LENGTH)
                    {
                        byte[] trailer;

                        trailer = _chunkQueue.DequeueByteBlock(_chunkQueue.AvailableBytes);

                        if (trailer.Length >= Zlib.GZIP_FOOTER_LENGTH)
                        {
                            byte[] crc = _crc.Final();

                            if (crc[3] != trailer[0] || crc[2] != trailer[1] || crc[1] != trailer[2] || crc[0] != trailer[3])
                            {
                                _state = UncompressionState.Failed;
                                PhpException.Throw(PhpError.Warning, "incorrect data check");
                                return null;
                            }

                            if (BitConverter.ToInt32(trailer, 4) != _stream.total_out)
                            {
                                _state = UncompressionState.Failed;
                                PhpException.Throw(PhpError.Warning, "incorrect length check");
                                return null;
                            }

                            _state = closing ? UncompressionState.Finished : UncompressionState.PostTrailer;

                            // everything is fine, return the output if available
                            return output != null ? new PhpBytes(output) : new PhpBytes();
                        }
                        else
                        {
                            _state = UncompressionState.Failed;
                            PhpException.Throw(PhpError.Warning, "unexpected end of file");
                            return null;
                        }
                    }
                    else
                    {
                        //stream is not closing yet - return the remaining output, otherwise empty
                        return output != null ? new PhpBytes(output) : new PhpBytes();
                    }
                    #endregion
                }

                //this should not happen
                Debug.Fail();
                return null;
            }
            else
            {
                Debug.Fail("GzipUncompressionFilter expects chunks to be convertible to PhpBytes.");
                return null;
            }
        }
예제 #4
0
 public GzipUncompressionFilter()
     : base()
 {
     _crc = new PhpHash.HashPhpResource.CRC32B();
     _state = UncompressionState.Header;
     _chunkQueue = new BinaryChunkQueue();
 }
예제 #5
0
        /// <summary>
        /// Changes state based on header flags. Is called by header-handling states only.
        /// </summary>
        private void UpdateHeaderState()
        {
            switch (_state)
            {
            case UncompressionState.Header:
                if (HeaderFlag(Zlib.GZIP_HEADER_EXTRAFIELD))
                {
                    _state = UncompressionState.HeaderExtraField;
                }
                else if (HeaderFlag(Zlib.GZIP_HEADER_FILENAME))
                {
                    _state = UncompressionState.HeaderFilename;
                }
                else if (HeaderFlag(Zlib.GZIP_HEADER_COMMENT))
                {
                    _state = UncompressionState.HeaderComment;
                }
                else if (HeaderFlag(Zlib.GZIP_HEADER_CRC))
                {
                    _state = UncompressionState.HeaderCRC;
                }
                else
                {
                    _state = UncompressionState.Data;
                }
                break;

            case UncompressionState.HeaderExtraField:
                if (HeaderFlag(Zlib.GZIP_HEADER_FILENAME))
                {
                    _state = UncompressionState.HeaderFilename;
                }
                else if (HeaderFlag(Zlib.GZIP_HEADER_COMMENT))
                {
                    _state = UncompressionState.HeaderComment;
                }
                else if (HeaderFlag(Zlib.GZIP_HEADER_CRC))
                {
                    _state = UncompressionState.HeaderCRC;
                }
                else
                {
                    _state = UncompressionState.Data;
                }
                break;

            case UncompressionState.HeaderFilename:
                if (HeaderFlag(Zlib.GZIP_HEADER_COMMENT))
                {
                    _state = UncompressionState.HeaderComment;
                }
                else if (HeaderFlag(Zlib.GZIP_HEADER_CRC))
                {
                    _state = UncompressionState.HeaderCRC;
                }
                else
                {
                    _state = UncompressionState.Data;
                }
                break;

            case UncompressionState.HeaderComment:
                if (HeaderFlag(Zlib.GZIP_HEADER_CRC))
                {
                    _state = UncompressionState.HeaderCRC;
                }
                else
                {
                    _state = UncompressionState.Data;
                }
                break;

            case UncompressionState.HeaderCRC:
                _state = UncompressionState.Data;
                break;
            }
        }
예제 #6
0
        public override object Filter(object input, bool closing)
        {
            // TODO: not the most efficient method - after the filters are upgraded to bucket lists, update this

            PhpBytes bInput = Core.Convert.ObjectToPhpBytes(input);

            if (bInput != null)
            {
                if (_state == UncompressionState.Failed)
                {
                    // failed filter should not get any more calls
                    PhpException.Throw(PhpError.Warning, "using filter in failed state");
                    return(null);
                }

                if (_state == UncompressionState.PostTrailer)
                {
                    // post trailer - ignore everything
                    if (closing)
                    {
                        _state = UncompressionState.Finished;
                    }

                    return(new PhpBytes());
                }

                if (_state == UncompressionState.Finished)
                {
                    // finished filter should not get any more data
                    PhpException.Throw(PhpError.Warning, "using filter in finished state");
                    return(null);
                }

                if (_state == UncompressionState.Passthrough)
                {
                    // this is not gzip data format - pass the data through
                    return(new PhpBytes(bInput));
                }

                // enqueue the block
                _chunkQueue.EnqueueByteBlock(bInput.ReadonlyData, 0, bInput.Length);

                if (_state == UncompressionState.Header)
                {
                    #region Header handling
                    //beginning of the stream
                    byte[] beginning = _chunkQueue.DequeueByteBlock(Zlib.GZIP_HEADER_LENGTH);

                    if (beginning == null && !closing)
                    {
                        // we do not have enough data, but we know there would be more data ahead
                        return(new PhpBytes());
                    }
                    else
                    {
                        //check the header format
                        if (beginning.Length >= 2 && beginning[0] == Zlib.GZIP_HEADER[0] && beginning[1] == Zlib.GZIP_HEADER[1])
                        {
                            //header magic bytes are OK
                            if (beginning.Length < Zlib.GZIP_HEADER_LENGTH)
                            {
                                // header is too short -> this is an error
                                PhpException.Throw(PhpError.Warning, "unexpected end of file");
                                return(null);
                            }
                            else
                            {
                                // check the rest of the header
                                if (beginning[2] != Zlib.Z_DEFLATED)
                                {
                                    PhpException.Throw(PhpError.Warning, "unknown compression method");
                                    return(null);
                                }

                                if ((beginning[3] & Zlib.GZIP_HEADER_RESERVED_FLAGS) != 0)
                                {
                                    PhpException.Throw(PhpError.Warning, "unknown header flags set");
                                    return(null);
                                }

                                _headerFlags = beginning[3];

                                //change the header state based on the header flags
                                UpdateHeaderState();
                            }
                        }
                        else
                        {
                            // this is not a gzip format -> passthrough the data
                            _state = UncompressionState.Passthrough;
                            return(new PhpBytes(beginning));
                        }
                    }
                    #endregion
                }

                if (_state == UncompressionState.HeaderExtraField)
                {
                    #region Header Extra Field Handling
                    if (_extraHeaderLength == null)
                    {
                        //length was not yet detected
                        if (_chunkQueue.AvailableBytes < 2)
                        {
                            //wait for more input
                            return(new PhpBytes());
                        }
                        else
                        {
                            //assemble length
                            _extraHeaderLength  = _chunkQueue.DequeueByte();
                            _extraHeaderLength &= (_chunkQueue.DequeueByte() << 8);
                        }
                    }

                    if (_extraHeaderLength != null)
                    {
                        //length was already read
                        if (_chunkQueue.AvailableBytes < _extraHeaderLength)
                        {
                            //wait for more input
                            return(new PhpBytes());
                        }
                        else
                        {
                            Debug.Assert(_extraHeaderLength.HasValue);

                            //skip the extra header
                            _chunkQueue.SkipByteBlock(_extraHeaderLength.Value);

                            UpdateHeaderState();
                        }
                    }
                    #endregion
                }

                if (_state == UncompressionState.HeaderFilename || _state == UncompressionState.HeaderComment)
                {
                    #region Header Filename and Comment Handling
                    // filename or comment

                    // cycle until input ends or zero character is encountered
                    while (true)
                    {
                        byte?nextByte = _chunkQueue.DequeueByte();

                        if (nextByte == null)
                        {
                            //wait for more input
                            return(new PhpBytes());
                        }

                        if (nextByte == 0)
                        {
                            // end the cycle
                            break;
                        }
                    }

                    // go to the next state
                    UpdateHeaderState();
                    #endregion
                }

                if (_state == UncompressionState.HeaderCRC)
                {
                    #region CRC Handling
                    // header CRC

                    if (_chunkQueue.AvailableBytes < 2)
                    {
                        //wait for more input
                        return(new PhpBytes());
                    }
                    else
                    {
                        //skip the CRC
                        _chunkQueue.DequeueByte();
                        _chunkQueue.DequeueByte();

                        UpdateHeaderState();
                    }
                    #endregion
                }

                //filled by data handling and sometimes returned by trailer handling
                byte[] output = null;

                if (_state == UncompressionState.Data)
                {
                    #region Deflated Data Handling

                    //get all available bytes
                    byte[] inputBytes  = _chunkQueue.DequeueByteBlock(_chunkQueue.AvailableBytes);
                    int    inputOffset = 0;

                    // perform the inner operation
                    try
                    {
                        output = FilterInner(inputBytes, ref inputOffset, closing);
                    }
                    catch
                    {
                        // exception was thrown
                        _state = UncompressionState.Failed;
                        throw;
                    }

                    if (output == null)
                    {
                        // error happened and exception was not thrown
                        _state = UncompressionState.Failed;
                        return(null);
                    }

                    // update the hash algorithm
                    _crc.Update(output);

                    if (inputOffset != inputBytes.Length)
                    {
                        // push the rest of the data into the chunk queue
                        _chunkQueue.PushByteBlock(inputBytes, inputOffset, inputBytes.Length - inputOffset);

                        // end of deflated block reached
                        _state = UncompressionState.Trailer;

                        // pass through to Trailer handling
                    }
                    else
                    {
                        //normal decompressed block - return it
                        return(new PhpBytes(output));
                    }

                    #endregion
                }

                if (_state == UncompressionState.Trailer)
                {
                    #region Trailer Handling
                    // the deflate block has already ended, we are processing trailer
                    if (closing || _chunkQueue.AvailableBytes >= Zlib.GZIP_FOOTER_LENGTH)
                    {
                        byte[] trailer;

                        trailer = _chunkQueue.DequeueByteBlock(_chunkQueue.AvailableBytes);

                        if (trailer.Length >= Zlib.GZIP_FOOTER_LENGTH)
                        {
                            byte[] crc = _crc.Final();

                            if (crc[3] != trailer[0] || crc[2] != trailer[1] || crc[1] != trailer[2] || crc[0] != trailer[3])
                            {
                                _state = UncompressionState.Failed;
                                PhpException.Throw(PhpError.Warning, "incorrect data check");
                                return(null);
                            }

                            if (BitConverter.ToInt32(trailer, 4) != _stream.total_out)
                            {
                                _state = UncompressionState.Failed;
                                PhpException.Throw(PhpError.Warning, "incorrect length check");
                                return(null);
                            }

                            _state = closing ? UncompressionState.Finished : UncompressionState.PostTrailer;

                            // everything is fine, return the output if available
                            return(output != null ? new PhpBytes(output) : new PhpBytes());
                        }
                        else
                        {
                            _state = UncompressionState.Failed;
                            PhpException.Throw(PhpError.Warning, "unexpected end of file");
                            return(null);
                        }
                    }
                    else
                    {
                        //stream is not closing yet - return the remaining output, otherwise empty
                        return(output != null ? new PhpBytes(output) : new PhpBytes());
                    }
                    #endregion
                }

                //this should not happen
                Debug.Fail(null);
                return(null);
            }
            else
            {
                Debug.Fail("GzipUncompressionFilter expects chunks to be convertible to PhpBytes.");
                return(null);
            }
        }