Add png parser

This commit is contained in:
Sachin Kelkar 2017-01-30 22:50:35 +05:30
parent 79aa994275
commit 6202958048
7 changed files with 320 additions and 6 deletions

View File

@ -23,8 +23,9 @@ from mitmproxy.net import http
from mitmproxy.utils import strutils from mitmproxy.utils import strutils
from . import ( from . import (
auto, raw, hex, json, xml_html, html_outline, wbxml, javascript, css, auto, raw, hex, json, xml_html, html_outline, wbxml, javascript, css,
urlencoded, multipart, image, query, protobuf urlencoded, multipart, query, protobuf
) )
from .image import pillow
from .base import View, VIEW_CUTOFF, KEY_MAX, format_text, format_dict from .base import View, VIEW_CUTOFF, KEY_MAX, format_text, format_dict
views = [] # type: List[View] views = [] # type: List[View]
@ -170,7 +171,7 @@ add(javascript.ViewJavaScript())
add(css.ViewCSS()) add(css.ViewCSS())
add(urlencoded.ViewURLEncoded()) add(urlencoded.ViewURLEncoded())
add(multipart.ViewMultipart()) add(multipart.ViewMultipart())
add(image.ViewImage()) add(pillow.ViewImage())
add(query.ViewQuery()) add(query.ViewQuery())
if protobuf.ViewProtobuf.is_available(): if protobuf.ViewProtobuf.is_available():

View File

View File

@ -0,0 +1,34 @@
from kaitaistruct import KaitaiStream
from . import png
def get_png(data):
img = png.Png(KaitaiStream(data))
parts = {'format': 'Portable network graphics'}
f = 'PNG'
width = img.ihdr.width
height = img.ihdr.height
parts["width"] = width
parts["height"] = height
for i in range(0, len(img.chunks)):
chunk = img.chunks[i]
if chunk.type == 'gAMA':
gamma = chunk.gamma_int / 100000
parts['gamma'] = gamma
elif chunk.type == 'pHYs':
aspectx = chunk.pixels_per_unit_x
aspecty = chunk.pixels_per_unit_y
parts["aspectx"] = aspectx
parts["aspecty"] = aspecty
return f, parts
def format_contentviews(parts):
ret = []
ret.append(tuple(['Format', parts["format"]]))
if "width" in parts:
ret.append(tuple(['Size', str(parts["width"]) + " x " + str(parts["height"]) + " px"]))
if "aspectx" in parts:
ret.append(tuple(['aspect', '(' + str(parts["aspectx"]) + ', ' + str(parts["aspecty"]) + ')']))
if "gamma" in parts:
ret.append(tuple(['gamma', str(parts["gamma"])]))
return ret

View File

@ -1,10 +1,13 @@
import io import io, imghdr
from PIL import ExifTags from PIL import ExifTags
from PIL import Image from PIL import Image
from mitmproxy.types import multidict from mitmproxy.types import multidict
from . import base from . import image_parser
from mitmproxy.contentviews import base
from kaitaistruct import KaitaiStream
class ViewImage(base.View): class ViewImage(base.View):
@ -19,6 +22,11 @@ class ViewImage(base.View):
] ]
def __call__(self, data, **metadata): def __call__(self, data, **metadata):
if imghdr.what('', h=data) == 'png':
f, parts = image_parser.get_png(io.BytesIO(data))
parts = image_parser.format_contentviews(parts)
fmt = base.format_dict(multidict.MultiDict(parts))
return "%s image" % f, fmt
try: try:
img = Image.open(io.BytesIO(data)) img = Image.open(io.BytesIO(data))
except IOError: except IOError:

View File

@ -0,0 +1,257 @@
# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
# The source was png.ksy from here - https://github.com/kaitai-io/kaitai_struct_formats/tree/master/image
import array
import struct
import zlib
from enum import Enum
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
class Png(KaitaiStruct):
class ColorType(Enum):
greyscale = 0
truecolor = 2
indexed = 3
greyscale_alpha = 4
truecolor_alpha = 6
class PhysUnit(Enum):
unknown = 0
meter = 1
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.magic = self._io.ensure_fixed_contents(8, struct.pack('8b', -119, 80, 78, 71, 13, 10, 26, 10))
self.ihdr_len = self._io.ensure_fixed_contents(4, struct.pack('4b', 0, 0, 0, 13))
self.ihdr_type = self._io.ensure_fixed_contents(4, struct.pack('4b', 73, 72, 68, 82))
self.ihdr = self._root.IhdrChunk(self._io, self, self._root)
self.ihdr_crc = self._io.read_bytes(4)
self.chunks = []
while not self._io.is_eof():
self.chunks.append(self._root.Chunk(self._io, self, self._root))
class Rgb(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.r = self._io.read_u1()
self.g = self._io.read_u1()
self.b = self._io.read_u1()
class Chunk(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.len = self._io.read_u4be()
self.type = self._io.read_str_byte_limit(4, "UTF-8")
_on = self.type
if _on == u"gAMA":
self._raw_body = self._io.read_bytes(self.len)
io = KaitaiStream(BytesIO(self._raw_body))
self.body = self._root.GamaChunk(io, self, self._root)
elif _on == u"tIME":
self._raw_body = self._io.read_bytes(self.len)
io = KaitaiStream(BytesIO(self._raw_body))
self.body = self._root.TimeChunk(io, self, self._root)
elif _on == u"PLTE":
self._raw_body = self._io.read_bytes(self.len)
io = KaitaiStream(BytesIO(self._raw_body))
self.body = self._root.PlteChunk(io, self, self._root)
elif _on == u"bKGD":
self._raw_body = self._io.read_bytes(self.len)
io = KaitaiStream(BytesIO(self._raw_body))
self.body = self._root.BkgdChunk(io, self, self._root)
elif _on == u"pHYs":
self._raw_body = self._io.read_bytes(self.len)
io = KaitaiStream(BytesIO(self._raw_body))
self.body = self._root.PhysChunk(io, self, self._root)
elif _on == u"tEXt":
self._raw_body = self._io.read_bytes(self.len)
io = KaitaiStream(BytesIO(self._raw_body))
self.body = self._root.TextChunk(io, self, self._root)
elif _on == u"cHRM":
self._raw_body = self._io.read_bytes(self.len)
io = KaitaiStream(BytesIO(self._raw_body))
self.body = self._root.ChrmChunk(io, self, self._root)
elif _on == u"sRGB":
self._raw_body = self._io.read_bytes(self.len)
io = KaitaiStream(BytesIO(self._raw_body))
self.body = self._root.SrgbChunk(io, self, self._root)
else:
self.body = self._io.read_bytes(self.len)
self.crc = self._io.read_bytes(4)
class BkgdIndexed(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.palette_index = self._io.read_u1()
class Point(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.x_int = self._io.read_u4be()
self.y_int = self._io.read_u4be()
@property
def x(self):
if hasattr(self, '_m_x'):
return self._m_x
self._m_x = (self.x_int / 100000.0)
return self._m_x
@property
def y(self):
if hasattr(self, '_m_y'):
return self._m_y
self._m_y = (self.y_int / 100000.0)
return self._m_y
class BkgdGreyscale(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.value = self._io.read_u2be()
class ChrmChunk(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.white_point = self._root.Point(self._io, self, self._root)
self.red = self._root.Point(self._io, self, self._root)
self.green = self._root.Point(self._io, self, self._root)
self.blue = self._root.Point(self._io, self, self._root)
class IhdrChunk(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.width = self._io.read_u4be()
self.height = self._io.read_u4be()
self.bit_depth = self._io.read_u1()
self.color_type = self._root.ColorType(self._io.read_u1())
self.compression_method = self._io.read_u1()
self.filter_method = self._io.read_u1()
self.interlace_method = self._io.read_u1()
class PlteChunk(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.entries = []
while not self._io.is_eof():
self.entries.append(self._root.Rgb(self._io, self, self._root))
class SrgbChunk(KaitaiStruct):
class Intent(Enum):
perceptual = 0
relative_colorimetric = 1
saturation = 2
absolute_colorimetric = 3
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.render_intent = self._root.Intent(self._io.read_u1())
class BkgdTruecolor(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.red = self._io.read_u2be()
self.green = self._io.read_u2be()
self.blue = self._io.read_u2be()
class GamaChunk(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.gamma_int = self._io.read_u4be()
@property
def gamma_ratio(self):
if hasattr(self, '_m_gamma_ratio'):
return self._m_gamma_ratio
self._m_gamma_ratio = (100000.0 / self.gamma_int)
return self._m_gamma_ratio
class BkgdChunk(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
_on = self._root.ihdr.color_type
if _on == self._root.ColorType.greyscale_alpha:
self.bkgd = self._root.BkgdGreyscale(self._io, self, self._root)
elif _on == self._root.ColorType.indexed:
self.bkgd = self._root.BkgdIndexed(self._io, self, self._root)
elif _on == self._root.ColorType.greyscale:
self.bkgd = self._root.BkgdGreyscale(self._io, self, self._root)
elif _on == self._root.ColorType.truecolor_alpha:
self.bkgd = self._root.BkgdTruecolor(self._io, self, self._root)
elif _on == self._root.ColorType.truecolor:
self.bkgd = self._root.BkgdTruecolor(self._io, self, self._root)
class PhysChunk(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.pixels_per_unit_x = self._io.read_u4be()
self.pixels_per_unit_y = self._io.read_u4be()
self.unit = self._root.PhysUnit(self._io.read_u1())
class TextChunk(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.keyword = self._io.read_strz("iso8859-1", 0, False, True, True)
self.text = self._io.read_str_eos("iso8859-1")
class TimeChunk(KaitaiStruct):
def __init__(self, _io, _parent=None, _root=None):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
self.year = self._io.read_u2be()
self.month = self._io.read_u1()
self.day = self._io.read_u1()
self.hour = self._io.read_u1()
self.minute = self._io.read_u1()
self.second = self._io.read_u1()

View File

@ -1,10 +1,10 @@
from mitmproxy.contentviews import image from mitmproxy.contentviews.image import pillow
from mitmproxy.test import tutils from mitmproxy.test import tutils
from . import full_eval from . import full_eval
def test_view_image(): def test_view_image():
v = full_eval(image.ViewImage()) v = full_eval(pillow.ViewImage())
for img in [ for img in [
"mitmproxy/data/image.png", "mitmproxy/data/image.png",
"mitmproxy/data/image.gif", "mitmproxy/data/image.gif",

View File

@ -0,0 +1,14 @@
import io
from mitmproxy.contentviews.image import image_parser
from mitmproxy.test import tutils
def test_png_parser():
img = "mitmproxy/data/image.png"
with open(tutils.test_data.path(img), "rb") as f:
fmt, parts = image_parser.get_png(io.BytesIO(f.read()))
assert fmt == "PNG"
assert parts
assert parts["width"] == 174
assert parts["height"] == 174
assert parts["format"] == "Portable network graphics"