From 6202958048dd73af55b55e879317d583851fc2e3 Mon Sep 17 00:00:00 2001 From: Sachin Kelkar Date: Mon, 30 Jan 2017 22:50:35 +0530 Subject: Add png parser --- mitmproxy/contentviews/__init__.py | 5 +- mitmproxy/contentviews/image.py | 45 ---- mitmproxy/contentviews/image/__init__.py | 0 mitmproxy/contentviews/image/image_parser.py | 34 +++ mitmproxy/contentviews/image/pillow.py | 53 +++++ mitmproxy/contentviews/image/png.py | 257 +++++++++++++++++++++++ test/mitmproxy/contentviews/test_image.py | 4 +- test/mitmproxy/contentviews/test_image_parser.py | 14 ++ 8 files changed, 363 insertions(+), 49 deletions(-) delete mode 100644 mitmproxy/contentviews/image.py create mode 100644 mitmproxy/contentviews/image/__init__.py create mode 100644 mitmproxy/contentviews/image/image_parser.py create mode 100644 mitmproxy/contentviews/image/pillow.py create mode 100644 mitmproxy/contentviews/image/png.py create mode 100644 test/mitmproxy/contentviews/test_image_parser.py diff --git a/mitmproxy/contentviews/__init__.py b/mitmproxy/contentviews/__init__.py index 357172e3..b28f9d8f 100644 --- a/mitmproxy/contentviews/__init__.py +++ b/mitmproxy/contentviews/__init__.py @@ -23,8 +23,9 @@ from mitmproxy.net import http from mitmproxy.utils import strutils from . import ( 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 views = [] # type: List[View] @@ -170,7 +171,7 @@ add(javascript.ViewJavaScript()) add(css.ViewCSS()) add(urlencoded.ViewURLEncoded()) add(multipart.ViewMultipart()) -add(image.ViewImage()) +add(pillow.ViewImage()) add(query.ViewQuery()) if protobuf.ViewProtobuf.is_available(): diff --git a/mitmproxy/contentviews/image.py b/mitmproxy/contentviews/image.py deleted file mode 100644 index 57b1fffb..00000000 --- a/mitmproxy/contentviews/image.py +++ /dev/null @@ -1,45 +0,0 @@ -import io - -from PIL import ExifTags -from PIL import Image - -from mitmproxy.types import multidict -from . import base - - -class ViewImage(base.View): - name = "Image" - prompt = ("image", "i") - content_types = [ - "image/png", - "image/jpeg", - "image/gif", - "image/vnd.microsoft.icon", - "image/x-icon", - ] - - def __call__(self, data, **metadata): - try: - img = Image.open(io.BytesIO(data)) - except IOError: - return None - parts = [ - ("Format", str(img.format_description)), - ("Size", "%s x %s px" % img.size), - ("Mode", str(img.mode)), - ] - for i in sorted(img.info.keys()): - if i != "exif": - parts.append( - (str(i), str(img.info[i])) - ) - if hasattr(img, "_getexif"): - ex = img._getexif() - if ex: - for i in sorted(ex.keys()): - tag = ExifTags.TAGS.get(i, i) - parts.append( - (str(tag), str(ex[i])) - ) - fmt = base.format_dict(multidict.MultiDict(parts)) - return "%s image" % img.format, fmt diff --git a/mitmproxy/contentviews/image/__init__.py b/mitmproxy/contentviews/image/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/mitmproxy/contentviews/image/image_parser.py b/mitmproxy/contentviews/image/image_parser.py new file mode 100644 index 00000000..8e5762c9 --- /dev/null +++ b/mitmproxy/contentviews/image/image_parser.py @@ -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 diff --git a/mitmproxy/contentviews/image/pillow.py b/mitmproxy/contentviews/image/pillow.py new file mode 100644 index 00000000..c48d665a --- /dev/null +++ b/mitmproxy/contentviews/image/pillow.py @@ -0,0 +1,53 @@ +import io, imghdr + +from PIL import ExifTags +from PIL import Image + +from mitmproxy.types import multidict +from . import image_parser + +from mitmproxy.contentviews import base +from kaitaistruct import KaitaiStream + + +class ViewImage(base.View): + name = "Image" + prompt = ("image", "i") + content_types = [ + "image/png", + "image/jpeg", + "image/gif", + "image/vnd.microsoft.icon", + "image/x-icon", + ] + + 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: + img = Image.open(io.BytesIO(data)) + except IOError: + return None + parts = [ + ("Format", str(img.format_description)), + ("Size", "%s x %s px" % img.size), + ("Mode", str(img.mode)), + ] + for i in sorted(img.info.keys()): + if i != "exif": + parts.append( + (str(i), str(img.info[i])) + ) + if hasattr(img, "_getexif"): + ex = img._getexif() + if ex: + for i in sorted(ex.keys()): + tag = ExifTags.TAGS.get(i, i) + parts.append( + (str(tag), str(ex[i])) + ) + fmt = base.format_dict(multidict.MultiDict(parts)) + return "%s image" % img.format, fmt diff --git a/mitmproxy/contentviews/image/png.py b/mitmproxy/contentviews/image/png.py new file mode 100644 index 00000000..5996e3ce --- /dev/null +++ b/mitmproxy/contentviews/image/png.py @@ -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() diff --git a/test/mitmproxy/contentviews/test_image.py b/test/mitmproxy/contentviews/test_image.py index 9e7e28f5..000b9da5 100644 --- a/test/mitmproxy/contentviews/test_image.py +++ b/test/mitmproxy/contentviews/test_image.py @@ -1,10 +1,10 @@ -from mitmproxy.contentviews import image +from mitmproxy.contentviews.image import pillow from mitmproxy.test import tutils from . import full_eval def test_view_image(): - v = full_eval(image.ViewImage()) + v = full_eval(pillow.ViewImage()) for img in [ "mitmproxy/data/image.png", "mitmproxy/data/image.gif", diff --git a/test/mitmproxy/contentviews/test_image_parser.py b/test/mitmproxy/contentviews/test_image_parser.py new file mode 100644 index 00000000..d4116392 --- /dev/null +++ b/test/mitmproxy/contentviews/test_image_parser.py @@ -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" -- cgit v1.2.3 From abef020e07bcb6b0fd6493f73fbb48c8275f502e Mon Sep 17 00:00:00 2001 From: Sachin Kelkar Date: Thu, 2 Feb 2017 16:30:01 +0530 Subject: Fix as per feedback and add more tests --- mitmproxy/contentviews/__init__.py | 5 +- mitmproxy/contentviews/image/__init__.py | 1 + mitmproxy/contentviews/image/image_parser.py | 42 ++++++---------- mitmproxy/contentviews/image/pillow.py | 53 -------------------- mitmproxy/contentviews/image/view.py | 53 ++++++++++++++++++++ setup.py | 1 + test/mitmproxy/contentviews/test_image.py | 4 +- test/mitmproxy/contentviews/test_image_parser.py | 60 +++++++++++++++++++---- test/mitmproxy/data/png_parser/aspect.png | Bin 0 -> 1230326 bytes test/mitmproxy/data/png_parser/ct0n0g04.png | Bin 0 -> 273 bytes test/mitmproxy/data/png_parser/ct1n0g04.png | Bin 0 -> 792 bytes test/mitmproxy/data/png_parser/cten0g04.png | Bin 0 -> 742 bytes test/mitmproxy/data/png_parser/g07n0g16.png | Bin 0 -> 321 bytes 13 files changed, 125 insertions(+), 94 deletions(-) delete mode 100644 mitmproxy/contentviews/image/pillow.py create mode 100644 mitmproxy/contentviews/image/view.py create mode 100644 test/mitmproxy/data/png_parser/aspect.png create mode 100644 test/mitmproxy/data/png_parser/ct0n0g04.png create mode 100644 test/mitmproxy/data/png_parser/ct1n0g04.png create mode 100644 test/mitmproxy/data/png_parser/cten0g04.png create mode 100644 test/mitmproxy/data/png_parser/g07n0g16.png diff --git a/mitmproxy/contentviews/__init__.py b/mitmproxy/contentviews/__init__.py index b28f9d8f..357172e3 100644 --- a/mitmproxy/contentviews/__init__.py +++ b/mitmproxy/contentviews/__init__.py @@ -23,9 +23,8 @@ from mitmproxy.net import http from mitmproxy.utils import strutils from . import ( auto, raw, hex, json, xml_html, html_outline, wbxml, javascript, css, - urlencoded, multipart, query, protobuf + urlencoded, multipart, image, query, protobuf ) -from .image import pillow from .base import View, VIEW_CUTOFF, KEY_MAX, format_text, format_dict views = [] # type: List[View] @@ -171,7 +170,7 @@ add(javascript.ViewJavaScript()) add(css.ViewCSS()) add(urlencoded.ViewURLEncoded()) add(multipart.ViewMultipart()) -add(pillow.ViewImage()) +add(image.ViewImage()) add(query.ViewQuery()) if protobuf.ViewProtobuf.is_available(): diff --git a/mitmproxy/contentviews/image/__init__.py b/mitmproxy/contentviews/image/__init__.py index e69de29b..d2de66d0 100644 --- a/mitmproxy/contentviews/image/__init__.py +++ b/mitmproxy/contentviews/image/__init__.py @@ -0,0 +1 @@ +from .view import ViewImage diff --git a/mitmproxy/contentviews/image/image_parser.py b/mitmproxy/contentviews/image/image_parser.py index 8e5762c9..e523def5 100644 --- a/mitmproxy/contentviews/image/image_parser.py +++ b/mitmproxy/contentviews/image/image_parser.py @@ -1,34 +1,22 @@ +import typing + from kaitaistruct import KaitaiStream from . import png -def get_png(data): +Metadata = typing.List[typing.Tuple[str, str]] + +def parse_png(data: bytes) -> Metadata: 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] + parts = [tuple(['Format', 'Portable network graphics'])] + parts.append(tuple(['Size', str(img.ihdr.width) + " x " + str(img.ihdr.height) + " px"])) + for chunk in img.chunks: if chunk.type == 'gAMA': - gamma = chunk.gamma_int / 100000 - parts['gamma'] = gamma + parts.append(tuple(['gamma', str(chunk.body.gamma_int / 100000)])) 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 + aspectx = chunk.body.pixels_per_unit_x + aspecty = chunk.body.pixels_per_unit_y + parts.append(tuple(['aspect', str(aspectx) + " x " + str(aspecty)])) + elif chunk.type == 'tEXt': + parts.append(tuple([chunk.body.keyword, chunk.body.text])) + return parts diff --git a/mitmproxy/contentviews/image/pillow.py b/mitmproxy/contentviews/image/pillow.py deleted file mode 100644 index c48d665a..00000000 --- a/mitmproxy/contentviews/image/pillow.py +++ /dev/null @@ -1,53 +0,0 @@ -import io, imghdr - -from PIL import ExifTags -from PIL import Image - -from mitmproxy.types import multidict -from . import image_parser - -from mitmproxy.contentviews import base -from kaitaistruct import KaitaiStream - - -class ViewImage(base.View): - name = "Image" - prompt = ("image", "i") - content_types = [ - "image/png", - "image/jpeg", - "image/gif", - "image/vnd.microsoft.icon", - "image/x-icon", - ] - - 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: - img = Image.open(io.BytesIO(data)) - except IOError: - return None - parts = [ - ("Format", str(img.format_description)), - ("Size", "%s x %s px" % img.size), - ("Mode", str(img.mode)), - ] - for i in sorted(img.info.keys()): - if i != "exif": - parts.append( - (str(i), str(img.info[i])) - ) - if hasattr(img, "_getexif"): - ex = img._getexif() - if ex: - for i in sorted(ex.keys()): - tag = ExifTags.TAGS.get(i, i) - parts.append( - (str(tag), str(ex[i])) - ) - fmt = base.format_dict(multidict.MultiDict(parts)) - return "%s image" % img.format, fmt diff --git a/mitmproxy/contentviews/image/view.py b/mitmproxy/contentviews/image/view.py new file mode 100644 index 00000000..4d13c917 --- /dev/null +++ b/mitmproxy/contentviews/image/view.py @@ -0,0 +1,53 @@ +import io, imghdr + +from PIL import ExifTags +from PIL import Image + +from mitmproxy.types import multidict +from . import image_parser + +from mitmproxy.contentviews import base +from kaitaistruct import KaitaiStream + + +class ViewImage(base.View): + name = "Image" + prompt = ("image", "i") + content_types = [ + "image/png", + "image/jpeg", + "image/gif", + "image/vnd.microsoft.icon", + "image/x-icon", + ] + + def __call__(self, data, **metadata): + if imghdr.what('', h=data) == 'png': + f = "PNG" + parts = image_parser.parse_png(io.BytesIO(data)) + fmt = base.format_dict(multidict.MultiDict(parts)) + return "%s image" % f, fmt + try: + img = Image.open(io.BytesIO(data)) + except IOError: + return None + parts = [ + ("Format", str(img.format_description)), + ("Size", "%s x %s px" % img.size), + ("Mode", str(img.mode)), + ] + for i in sorted(img.info.keys()): + if i != "exif": + parts.append( + (str(i), str(img.info[i])) + ) + if hasattr(img, "_getexif"): + ex = img._getexif() + if ex: + for i in sorted(ex.keys()): + tag = ExifTags.TAGS.get(i, i) + parts.append( + (str(tag), str(ex[i])) + ) + fmt = base.format_dict(multidict.MultiDict(parts)) + return "%s image" % img.format, fmt diff --git a/setup.py b/setup.py index 85eece95..f2b5db3e 100644 --- a/setup.py +++ b/setup.py @@ -71,6 +71,7 @@ setup( "html2text>=2016.1.8, <=2016.9.19", "hyperframe>=4.0.1, <5", "jsbeautifier>=1.6.3, <1.7", + "kaitaistruct>=0.5", "Pillow>=3.2, <4.1", "passlib>=1.6.5, <1.8", "pyasn1>=0.1.9, <0.2", diff --git a/test/mitmproxy/contentviews/test_image.py b/test/mitmproxy/contentviews/test_image.py index 000b9da5..9e7e28f5 100644 --- a/test/mitmproxy/contentviews/test_image.py +++ b/test/mitmproxy/contentviews/test_image.py @@ -1,10 +1,10 @@ -from mitmproxy.contentviews.image import pillow +from mitmproxy.contentviews import image from mitmproxy.test import tutils from . import full_eval def test_view_image(): - v = full_eval(pillow.ViewImage()) + v = full_eval(image.ViewImage()) for img in [ "mitmproxy/data/image.png", "mitmproxy/data/image.gif", diff --git a/test/mitmproxy/contentviews/test_image_parser.py b/test/mitmproxy/contentviews/test_image_parser.py index d4116392..2544e3f1 100644 --- a/test/mitmproxy/contentviews/test_image_parser.py +++ b/test/mitmproxy/contentviews/test_image_parser.py @@ -3,12 +3,54 @@ 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" +class TestPngParser: + def test_png_parser(self): + img = "mitmproxy/data/image.png" + with open(tutils.test_data.path(img), "rb") as f: + parts = image_parser.parse_png(io.BytesIO(f.read())) + assert parts + assert tuple(['Size', '174 x 174 px']) in parts + assert tuple(["Format", "Portable network graphics"]) in parts + + def test_textual_data(self): + img = "mitmproxy/data/png_parser/ct1n0g04.png" + with open(tutils.test_data.path(img), "rb") as f: + parts = image_parser.parse_png(io.BytesIO(f.read())) + assert parts + expected = [ + ('Title', 'PngSuite'), + ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), + ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), + ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), + ('Software', 'Created on a NeXTstation color using "pnmtopng".'), + ('Disclaimer', 'Freeware.') + ] + for data in expected: + assert data in parts + + def test_no_textual_data(self): + img = "mitmproxy/data/png_parser/ct0n0g04.png" + with open(tutils.test_data.path(img), "rb") as f: + parts = image_parser.parse_png(io.BytesIO(f.read())) + assert parts + metadata = [ + ('Format', 'Portable network graphics'), + ('Size', '32 x 32 px'), + ('gamma', '1.0') + ] + parts = [data for data in parts if data not in metadata] + assert not parts + + def test_gamma(self): + img = "mitmproxy/data/png_parser/g07n0g16.png" + with open(tutils.test_data.path(img), "rb") as f: + parts = image_parser.parse_png(io.BytesIO(f.read())) + assert parts + assert ('gamma', '0.7') in parts + + def test_gamma(self): + img = "mitmproxy/data/png_parser/aspect.png" + with open(tutils.test_data.path(img), "rb") as f: + parts = image_parser.parse_png(io.BytesIO(f.read())) + assert parts + assert ('aspect', '72 x 72') in parts diff --git a/test/mitmproxy/data/png_parser/aspect.png b/test/mitmproxy/data/png_parser/aspect.png new file mode 100644 index 00000000..17c01913 Binary files /dev/null and b/test/mitmproxy/data/png_parser/aspect.png differ diff --git a/test/mitmproxy/data/png_parser/ct0n0g04.png b/test/mitmproxy/data/png_parser/ct0n0g04.png new file mode 100644 index 00000000..40d1e062 Binary files /dev/null and b/test/mitmproxy/data/png_parser/ct0n0g04.png differ diff --git a/test/mitmproxy/data/png_parser/ct1n0g04.png b/test/mitmproxy/data/png_parser/ct1n0g04.png new file mode 100644 index 00000000..3ba110aa Binary files /dev/null and b/test/mitmproxy/data/png_parser/ct1n0g04.png differ diff --git a/test/mitmproxy/data/png_parser/cten0g04.png b/test/mitmproxy/data/png_parser/cten0g04.png new file mode 100644 index 00000000..a6a56faf Binary files /dev/null and b/test/mitmproxy/data/png_parser/cten0g04.png differ diff --git a/test/mitmproxy/data/png_parser/g07n0g16.png b/test/mitmproxy/data/png_parser/g07n0g16.png new file mode 100644 index 00000000..d6a47c2d Binary files /dev/null and b/test/mitmproxy/data/png_parser/g07n0g16.png differ -- cgit v1.2.3 From 88dbfd525719229154a7badcd7628857b4e16af4 Mon Sep 17 00:00:00 2001 From: Sachin Kelkar Date: Thu, 2 Feb 2017 18:01:48 +0530 Subject: Add iTXt and zTXt tests --- mitmproxy/contentviews/image/image_parser.py | 4 ++ mitmproxy/contentviews/image/png.py | 34 +++++++++++- test/mitmproxy/contentviews/test_image_parser.py | 68 +++++++++++++++++------ test/mitmproxy/data/png_parser/ctzn0g04.png | Bin 0 -> 753 bytes 4 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 test/mitmproxy/data/png_parser/ctzn0g04.png diff --git a/mitmproxy/contentviews/image/image_parser.py b/mitmproxy/contentviews/image/image_parser.py index e523def5..3a375006 100644 --- a/mitmproxy/contentviews/image/image_parser.py +++ b/mitmproxy/contentviews/image/image_parser.py @@ -19,4 +19,8 @@ def parse_png(data: bytes) -> Metadata: parts.append(tuple(['aspect', str(aspectx) + " x " + str(aspecty)])) elif chunk.type == 'tEXt': parts.append(tuple([chunk.body.keyword, chunk.body.text])) + elif chunk.type == 'iTXt': + parts.append(tuple([chunk.body.keyword, chunk.body.text])) + elif chunk.type == 'zTXt': + parts.append(tuple([chunk.body.keyword, chunk.body.text_datastream.decode('iso8859-1')])) return parts diff --git a/mitmproxy/contentviews/image/png.py b/mitmproxy/contentviews/image/png.py index 5996e3ce..53d39799 100644 --- a/mitmproxy/contentviews/image/png.py +++ b/mitmproxy/contentviews/image/png.py @@ -53,7 +53,11 @@ class Png(KaitaiStruct): self.len = self._io.read_u4be() self.type = self._io.read_str_byte_limit(4, "UTF-8") _on = self.type - if _on == u"gAMA": + if _on == u"iTXt": + self._raw_body = self._io.read_bytes(self.len) + io = KaitaiStream(BytesIO(self._raw_body)) + self.body = self._root.InternationalTextChunk(io, self, self._root) + elif _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) @@ -85,6 +89,10 @@ class Png(KaitaiStruct): self._raw_body = self._io.read_bytes(self.len) io = KaitaiStream(BytesIO(self._raw_body)) self.body = self._root.SrgbChunk(io, self, self._root) + elif _on == u"zTXt": + self._raw_body = self._io.read_bytes(self.len) + io = KaitaiStream(BytesIO(self._raw_body)) + self.body = self._root.CompressedTextChunk(io, self, self._root) else: self.body = self._io.read_bytes(self.len) self.crc = self._io.read_bytes(4) @@ -181,6 +189,17 @@ class Png(KaitaiStruct): self.render_intent = self._root.Intent(self._io.read_u1()) + class CompressedTextChunk(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("UTF-8", 0, False, True, True) + self.compression_method = self._io.read_u1() + self._raw_text_datastream = self._io.read_bytes_full() + self.text_datastream = zlib.decompress(self._raw_text_datastream) + + class BkgdTruecolor(KaitaiStruct): def __init__(self, _io, _parent=None, _root=None): self._io = _io @@ -235,6 +254,19 @@ class Png(KaitaiStruct): self.unit = self._root.PhysUnit(self._io.read_u1()) + class InternationalTextChunk(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("UTF-8", 0, False, True, True) + self.compression_flag = self._io.read_u1() + self.compression_method = self._io.read_u1() + self.language_tag = self._io.read_strz("iso8859-1", 0, False, True, True) + self.translated_keyword = self._io.read_strz("UTF-8", 0, False, True, True) + self.text = self._io.read_str_eos("UTF-8") + + class TextChunk(KaitaiStruct): def __init__(self, _io, _parent=None, _root=None): self._io = _io diff --git a/test/mitmproxy/contentviews/test_image_parser.py b/test/mitmproxy/contentviews/test_image_parser.py index 2544e3f1..3d987b9d 100644 --- a/test/mitmproxy/contentviews/test_image_parser.py +++ b/test/mitmproxy/contentviews/test_image_parser.py @@ -4,7 +4,7 @@ from mitmproxy.contentviews.image import image_parser from mitmproxy.test import tutils class TestPngParser: - def test_png_parser(self): + def test_png_size(self): img = "mitmproxy/data/image.png" with open(tutils.test_data.path(img), "rb") as f: parts = image_parser.parse_png(io.BytesIO(f.read())) @@ -12,22 +12,6 @@ class TestPngParser: assert tuple(['Size', '174 x 174 px']) in parts assert tuple(["Format", "Portable network graphics"]) in parts - def test_textual_data(self): - img = "mitmproxy/data/png_parser/ct1n0g04.png" - with open(tutils.test_data.path(img), "rb") as f: - parts = image_parser.parse_png(io.BytesIO(f.read())) - assert parts - expected = [ - ('Title', 'PngSuite'), - ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), - ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), - ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), - ('Software', 'Created on a NeXTstation color using "pnmtopng".'), - ('Disclaimer', 'Freeware.') - ] - for data in expected: - assert data in parts - def test_no_textual_data(self): img = "mitmproxy/data/png_parser/ct0n0g04.png" with open(tutils.test_data.path(img), "rb") as f: @@ -41,6 +25,54 @@ class TestPngParser: parts = [data for data in parts if data not in metadata] assert not parts + def test_textual_data(self): + img = "mitmproxy/data/png_parser/ct1n0g04.png" + expected = [ + ('Title', 'PngSuite'), + ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), + ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), + ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), + ('Software', 'Created on a NeXTstation color using "pnmtopng".'), + ('Disclaimer', 'Freeware.') + ] + with open(tutils.test_data.path(img), "rb") as f: + parts = image_parser.parse_png(io.BytesIO(f.read())) + assert parts + for data in expected: + assert data in parts + + def test_compressed_textual_data(self): + img = "mitmproxy/data/png_parser/ctzn0g04.png" + expected = [ + ('Title', 'PngSuite'), + ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), + ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), + ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), + ('Software', 'Created on a NeXTstation color using "pnmtopng".'), + ('Disclaimer', 'Freeware.') + ] + with open(tutils.test_data.path(img), "rb") as f: + parts = image_parser.parse_png(io.BytesIO(f.read())) + assert parts + for data in expected: + assert data in parts + + def test_international_textual_data(self): + img = "mitmproxy/data/png_parser/cten0g04.png" + expected = [ + ('Author', 'Willem van Schaik (willem@schaik.com)'), + ('Copyright', 'Copyright Willem van Schaik, Canada 2011'), + ('Description', 'A compilation of a set of images created to test the various color-types of the PNG format. Included are black&white, color, paletted, with alpha channel, with transparency formats. All bit-depths allowed according to the spec are present.'), + ('Software', 'Created on a NeXTstation color using "pnmtopng".'), + ('Disclaimer', 'Freeware.') + ] + with open(tutils.test_data.path(img), "rb") as f: + parts = image_parser.parse_png(io.BytesIO(f.read())) + print(parts) + assert parts + for data in expected: + assert data in parts + def test_gamma(self): img = "mitmproxy/data/png_parser/g07n0g16.png" with open(tutils.test_data.path(img), "rb") as f: @@ -48,7 +80,7 @@ class TestPngParser: assert parts assert ('gamma', '0.7') in parts - def test_gamma(self): + def test_aspect(self): img = "mitmproxy/data/png_parser/aspect.png" with open(tutils.test_data.path(img), "rb") as f: parts = image_parser.parse_png(io.BytesIO(f.read())) diff --git a/test/mitmproxy/data/png_parser/ctzn0g04.png b/test/mitmproxy/data/png_parser/ctzn0g04.png new file mode 100644 index 00000000..b4401c9c Binary files /dev/null and b/test/mitmproxy/data/png_parser/ctzn0g04.png differ -- cgit v1.2.3 From 0674485e76b14377922d6763e546fa1466d0452f Mon Sep 17 00:00:00 2001 From: Sachin Kelkar Date: Fri, 3 Feb 2017 15:02:35 +0530 Subject: Fix and cleanup --- mitmproxy/contentviews/image/image_parser.py | 18 +- mitmproxy/contentviews/image/png.py | 289 ----------------------- mitmproxy/contrib/kaitaistruct/png.py | 289 +++++++++++++++++++++++ test/mitmproxy/contentviews/test_image_parser.py | 140 +++++------ 4 files changed, 356 insertions(+), 380 deletions(-) delete mode 100644 mitmproxy/contentviews/image/png.py create mode 100644 mitmproxy/contrib/kaitaistruct/png.py diff --git a/mitmproxy/contentviews/image/image_parser.py b/mitmproxy/contentviews/image/image_parser.py index 3a375006..11d66c61 100644 --- a/mitmproxy/contentviews/image/image_parser.py +++ b/mitmproxy/contentviews/image/image_parser.py @@ -2,25 +2,27 @@ import typing from kaitaistruct import KaitaiStream -from . import png +from mitmproxy.contrib.kaitaistruct import png Metadata = typing.List[typing.Tuple[str, str]] def parse_png(data: bytes) -> Metadata: img = png.Png(KaitaiStream(data)) - parts = [tuple(['Format', 'Portable network graphics'])] - parts.append(tuple(['Size', str(img.ihdr.width) + " x " + str(img.ihdr.height) + " px"])) + parts = [ + ('Format', 'Portable network graphics') + ] + parts.append(('Size', "{0} x {1} px".format(img.ihdr.width, img.ihdr.height))) for chunk in img.chunks: if chunk.type == 'gAMA': - parts.append(tuple(['gamma', str(chunk.body.gamma_int / 100000)])) + parts.append(('gamma', str(chunk.body.gamma_int / 100000))) elif chunk.type == 'pHYs': aspectx = chunk.body.pixels_per_unit_x aspecty = chunk.body.pixels_per_unit_y - parts.append(tuple(['aspect', str(aspectx) + " x " + str(aspecty)])) + parts.append(('aspect', "{0} x {1}".format(aspectx, aspecty))) elif chunk.type == 'tEXt': - parts.append(tuple([chunk.body.keyword, chunk.body.text])) + parts.append((chunk.body.keyword, chunk.body.text)) elif chunk.type == 'iTXt': - parts.append(tuple([chunk.body.keyword, chunk.body.text])) + parts.append((chunk.body.keyword, chunk.body.text)) elif chunk.type == 'zTXt': - parts.append(tuple([chunk.body.keyword, chunk.body.text_datastream.decode('iso8859-1')])) + parts.append((chunk.body.keyword, chunk.body.text_datastream.decode('iso8859-1'))) return parts diff --git a/mitmproxy/contentviews/image/png.py b/mitmproxy/contentviews/image/png.py deleted file mode 100644 index 53d39799..00000000 --- a/mitmproxy/contentviews/image/png.py +++ /dev/null @@ -1,289 +0,0 @@ -# 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"iTXt": - self._raw_body = self._io.read_bytes(self.len) - io = KaitaiStream(BytesIO(self._raw_body)) - self.body = self._root.InternationalTextChunk(io, self, self._root) - elif _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) - elif _on == u"zTXt": - self._raw_body = self._io.read_bytes(self.len) - io = KaitaiStream(BytesIO(self._raw_body)) - self.body = self._root.CompressedTextChunk(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 CompressedTextChunk(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("UTF-8", 0, False, True, True) - self.compression_method = self._io.read_u1() - self._raw_text_datastream = self._io.read_bytes_full() - self.text_datastream = zlib.decompress(self._raw_text_datastream) - - - 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 InternationalTextChunk(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("UTF-8", 0, False, True, True) - self.compression_flag = self._io.read_u1() - self.compression_method = self._io.read_u1() - self.language_tag = self._io.read_strz("iso8859-1", 0, False, True, True) - self.translated_keyword = self._io.read_strz("UTF-8", 0, False, True, True) - self.text = self._io.read_str_eos("UTF-8") - - - 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() diff --git a/mitmproxy/contrib/kaitaistruct/png.py b/mitmproxy/contrib/kaitaistruct/png.py new file mode 100644 index 00000000..5b0e3ca3 --- /dev/null +++ b/mitmproxy/contrib/kaitaistruct/png.py @@ -0,0 +1,289 @@ +# 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/blob/9370c720b7d2ad329102d89bdc880ba6a706ef26/image/png.ksy + +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"iTXt": + self._raw_body = self._io.read_bytes(self.len) + io = KaitaiStream(BytesIO(self._raw_body)) + self.body = self._root.InternationalTextChunk(io, self, self._root) + elif _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) + elif _on == u"zTXt": + self._raw_body = self._io.read_bytes(self.len) + io = KaitaiStream(BytesIO(self._raw_body)) + self.body = self._root.CompressedTextChunk(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 CompressedTextChunk(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("UTF-8", 0, False, True, True) + self.compression_method = self._io.read_u1() + self._raw_text_datastream = self._io.read_bytes_full() + self.text_datastream = zlib.decompress(self._raw_text_datastream) + + + 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 InternationalTextChunk(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("UTF-8", 0, False, True, True) + self.compression_flag = self._io.read_u1() + self.compression_method = self._io.read_u1() + self.language_tag = self._io.read_strz("ASCII", 0, False, True, True) + self.translated_keyword = self._io.read_strz("UTF-8", 0, False, True, True) + self.text = self._io.read_str_eos("UTF-8") + + + 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() diff --git a/test/mitmproxy/contentviews/test_image_parser.py b/test/mitmproxy/contentviews/test_image_parser.py index 3d987b9d..ba215b93 100644 --- a/test/mitmproxy/contentviews/test_image_parser.py +++ b/test/mitmproxy/contentviews/test_image_parser.py @@ -1,88 +1,62 @@ import io +import pytest + from mitmproxy.contentviews.image import image_parser from mitmproxy.test import tutils -class TestPngParser: - def test_png_size(self): - img = "mitmproxy/data/image.png" - with open(tutils.test_data.path(img), "rb") as f: - parts = image_parser.parse_png(io.BytesIO(f.read())) - assert parts - assert tuple(['Size', '174 x 174 px']) in parts - assert tuple(["Format", "Portable network graphics"]) in parts - - def test_no_textual_data(self): - img = "mitmproxy/data/png_parser/ct0n0g04.png" - with open(tutils.test_data.path(img), "rb") as f: - parts = image_parser.parse_png(io.BytesIO(f.read())) - assert parts - metadata = [ - ('Format', 'Portable network graphics'), - ('Size', '32 x 32 px'), - ('gamma', '1.0') - ] - parts = [data for data in parts if data not in metadata] - assert not parts - - def test_textual_data(self): - img = "mitmproxy/data/png_parser/ct1n0g04.png" - expected = [ - ('Title', 'PngSuite'), - ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), - ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), - ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), - ('Software', 'Created on a NeXTstation color using "pnmtopng".'), - ('Disclaimer', 'Freeware.') - ] - with open(tutils.test_data.path(img), "rb") as f: - parts = image_parser.parse_png(io.BytesIO(f.read())) - assert parts - for data in expected: - assert data in parts - - def test_compressed_textual_data(self): - img = "mitmproxy/data/png_parser/ctzn0g04.png" - expected = [ - ('Title', 'PngSuite'), - ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), - ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), - ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), - ('Software', 'Created on a NeXTstation color using "pnmtopng".'), - ('Disclaimer', 'Freeware.') - ] - with open(tutils.test_data.path(img), "rb") as f: - parts = image_parser.parse_png(io.BytesIO(f.read())) - assert parts - for data in expected: - assert data in parts - - def test_international_textual_data(self): - img = "mitmproxy/data/png_parser/cten0g04.png" - expected = [ - ('Author', 'Willem van Schaik (willem@schaik.com)'), - ('Copyright', 'Copyright Willem van Schaik, Canada 2011'), - ('Description', 'A compilation of a set of images created to test the various color-types of the PNG format. Included are black&white, color, paletted, with alpha channel, with transparency formats. All bit-depths allowed according to the spec are present.'), - ('Software', 'Created on a NeXTstation color using "pnmtopng".'), - ('Disclaimer', 'Freeware.') - ] - with open(tutils.test_data.path(img), "rb") as f: - parts = image_parser.parse_png(io.BytesIO(f.read())) - print(parts) - assert parts - for data in expected: - assert data in parts - - def test_gamma(self): - img = "mitmproxy/data/png_parser/g07n0g16.png" - with open(tutils.test_data.path(img), "rb") as f: - parts = image_parser.parse_png(io.BytesIO(f.read())) - assert parts - assert ('gamma', '0.7') in parts - - def test_aspect(self): - img = "mitmproxy/data/png_parser/aspect.png" - with open(tutils.test_data.path(img), "rb") as f: - parts = image_parser.parse_png(io.BytesIO(f.read())) - assert parts - assert ('aspect', '72 x 72') in parts +@pytest.mark.parametrize("filename, metadata", { + # no textual data + "mitmproxy/data/png_parser/ct0n0g04.png": [ + ('Format', 'Portable network graphics'), + ('Size', '32 x 32 px'), + ('gamma', '1.0') + ], + # with textual data + "mitmproxy/data/png_parser/ct1n0g04.png": [ + ('Format', 'Portable network graphics'), + ('Size', '32 x 32 px'), + ('gamma', '1.0'), + ('Title', 'PngSuite'), + ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), + ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), + ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), ('Software', 'Created on a NeXTstation color using "pnmtopng".'), ('Disclaimer', 'Freeware.') + ], + # with compressed textual data + "mitmproxy/data/png_parser/ctzn0g04.png": [ + ('Format', 'Portable network graphics'), + ('Size', '32 x 32 px'), + ('gamma', '1.0'), + ('Title', 'PngSuite'), + ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), + ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), + ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), ('Software', 'Created on a NeXTstation color using "pnmtopng".'), ('Disclaimer', 'Freeware.') + ], + # UTF-8 international text - english + "mitmproxy/data/png_parser/cten0g04.png": [ + ('Format', 'Portable network graphics'), + ('Size', '32 x 32 px'), + ('gamma', '1.0'), + ('Title', 'PngSuite'), + ('Author', 'Willem van Schaik (willem@schaik.com)'), + ('Copyright', 'Copyright Willem van Schaik, Canada 2011'), + ('Description', 'A compilation of a set of images created to test the various color-types of the PNG format. Included are black&white, color, paletted, with alpha channel, with transparency formats. All bit-depths allowed according to the spec are present.'), ('Software', 'Created on a NeXTstation color using "pnmtopng".'), ('Disclaimer', 'Freeware.') + ], + # check gamma value + "mitmproxy/data/png_parser/g07n0g16.png": [ + ('Format', 'Portable network graphics'), + ('Size', '32 x 32 px'), + ('gamma', '0.7') + ], + # check aspect value + "mitmproxy/data/png_parser/aspect.png": [ + ('Format', 'Portable network graphics'), + ('Size', '1280 x 798 px'), + ('aspect', '72 x 72'), + ('date:create', '2012-07-11T14:04:52-07:00'), + ('date:modify', '2012-07-11T14:04:52-07:00') + ], +}.items()) +def test_parse_png(filename, metadata): + with open(tutils.test_data.path(filename), "rb") as f: + assert metadata == image_parser.parse_png(io.BytesIO(f.read())) -- cgit v1.2.3 From 15548ff433d4283d4e46906decde5baa406b6584 Mon Sep 17 00:00:00 2001 From: Sachin Kelkar Date: Fri, 3 Feb 2017 17:32:55 +0530 Subject: Feedback --- mitmproxy/contentviews/image/__init__.py | 2 +- mitmproxy/contentviews/image/image_parser.py | 4 +++- mitmproxy/contentviews/image/view.py | 6 +++--- setup.py | 2 +- test/mitmproxy/contentviews/test_image_parser.py | 26 ++++++++++++++++++------ 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/mitmproxy/contentviews/image/__init__.py b/mitmproxy/contentviews/image/__init__.py index d2de66d0..0d0f06e0 100644 --- a/mitmproxy/contentviews/image/__init__.py +++ b/mitmproxy/contentviews/image/__init__.py @@ -1 +1 @@ -from .view import ViewImage +from .view import ViewImage # noqa diff --git a/mitmproxy/contentviews/image/image_parser.py b/mitmproxy/contentviews/image/image_parser.py index 11d66c61..0af58a88 100644 --- a/mitmproxy/contentviews/image/image_parser.py +++ b/mitmproxy/contentviews/image/image_parser.py @@ -1,3 +1,4 @@ +import io import typing from kaitaistruct import KaitaiStream @@ -6,8 +7,9 @@ from mitmproxy.contrib.kaitaistruct import png Metadata = typing.List[typing.Tuple[str, str]] + def parse_png(data: bytes) -> Metadata: - img = png.Png(KaitaiStream(data)) + img = png.Png(KaitaiStream(io.BytesIO(data))) parts = [ ('Format', 'Portable network graphics') ] diff --git a/mitmproxy/contentviews/image/view.py b/mitmproxy/contentviews/image/view.py index 4d13c917..08a70795 100644 --- a/mitmproxy/contentviews/image/view.py +++ b/mitmproxy/contentviews/image/view.py @@ -1,4 +1,5 @@ -import io, imghdr +import io +import imghdr from PIL import ExifTags from PIL import Image @@ -7,7 +8,6 @@ from mitmproxy.types import multidict from . import image_parser from mitmproxy.contentviews import base -from kaitaistruct import KaitaiStream class ViewImage(base.View): @@ -24,7 +24,7 @@ class ViewImage(base.View): def __call__(self, data, **metadata): if imghdr.what('', h=data) == 'png': f = "PNG" - parts = image_parser.parse_png(io.BytesIO(data)) + parts = image_parser.parse_png(data) fmt = base.format_dict(multidict.MultiDict(parts)) return "%s image" % f, fmt try: diff --git a/setup.py b/setup.py index f2b5db3e..406f9e6a 100644 --- a/setup.py +++ b/setup.py @@ -71,7 +71,7 @@ setup( "html2text>=2016.1.8, <=2016.9.19", "hyperframe>=4.0.1, <5", "jsbeautifier>=1.6.3, <1.7", - "kaitaistruct>=0.5", + "kaitaistruct>=0.5, <0.6", "Pillow>=3.2, <4.1", "passlib>=1.6.5, <1.8", "pyasn1>=0.1.9, <0.2", diff --git a/test/mitmproxy/contentviews/test_image_parser.py b/test/mitmproxy/contentviews/test_image_parser.py index ba215b93..62a07f56 100644 --- a/test/mitmproxy/contentviews/test_image_parser.py +++ b/test/mitmproxy/contentviews/test_image_parser.py @@ -1,10 +1,9 @@ -import io - import pytest from mitmproxy.contentviews.image import image_parser from mitmproxy.test import tutils + @pytest.mark.parametrize("filename, metadata", { # no textual data "mitmproxy/data/png_parser/ct0n0g04.png": [ @@ -20,7 +19,12 @@ from mitmproxy.test import tutils ('Title', 'PngSuite'), ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), - ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), ('Software', 'Created on a NeXTstation color using "pnmtopng".'), ('Disclaimer', 'Freeware.') + ('Description', 'A compilation of a set of images created to test the\n' + 'various color-types of the PNG format. Included are\nblack&white, color,' + ' paletted, with alpha channel, with\ntransparency formats. All bit-depths' + ' allowed according\nto the spec are present.'), + ('Software', 'Created on a NeXTstation color using "pnmtopng".'), + ('Disclaimer', 'Freeware.') ], # with compressed textual data "mitmproxy/data/png_parser/ctzn0g04.png": [ @@ -30,7 +34,12 @@ from mitmproxy.test import tutils ('Title', 'PngSuite'), ('Author', 'Willem A.J. van Schaik\n(willem@schaik.com)'), ('Copyright', 'Copyright Willem van Schaik, Singapore 1995-96'), - ('Description', 'A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present.'), ('Software', 'Created on a NeXTstation color using "pnmtopng".'), ('Disclaimer', 'Freeware.') + ('Description', 'A compilation of a set of images created to test the\n' + 'various color-types of the PNG format. Included are\nblack&white, color,' + ' paletted, with alpha channel, with\ntransparency formats. All bit-depths' + ' allowed according\nto the spec are present.'), + ('Software', 'Created on a NeXTstation color using "pnmtopng".'), + ('Disclaimer', 'Freeware.') ], # UTF-8 international text - english "mitmproxy/data/png_parser/cten0g04.png": [ @@ -40,7 +49,12 @@ from mitmproxy.test import tutils ('Title', 'PngSuite'), ('Author', 'Willem van Schaik (willem@schaik.com)'), ('Copyright', 'Copyright Willem van Schaik, Canada 2011'), - ('Description', 'A compilation of a set of images created to test the various color-types of the PNG format. Included are black&white, color, paletted, with alpha channel, with transparency formats. All bit-depths allowed according to the spec are present.'), ('Software', 'Created on a NeXTstation color using "pnmtopng".'), ('Disclaimer', 'Freeware.') + ('Description', 'A compilation of a set of images created to test the ' + 'various color-types of the PNG format. Included are black&white, color,' + ' paletted, with alpha channel, with transparency formats. All bit-depths' + ' allowed according to the spec are present.'), + ('Software', 'Created on a NeXTstation color using "pnmtopng".'), + ('Disclaimer', 'Freeware.') ], # check gamma value "mitmproxy/data/png_parser/g07n0g16.png": [ @@ -59,4 +73,4 @@ from mitmproxy.test import tutils }.items()) def test_parse_png(filename, metadata): with open(tutils.test_data.path(filename), "rb") as f: - assert metadata == image_parser.parse_png(io.BytesIO(f.read())) + assert metadata == image_parser.parse_png(f.read()) -- cgit v1.2.3