aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2017-02-11 23:00:14 +0100
committerGitHub <noreply@github.com>2017-02-11 23:00:14 +0100
commit3067a971f9aef02e1e77c35f7ccc733f37da0c65 (patch)
tree598f9930c7b223dd3d475899adcbfd7e2d323266
parent51f6d279a7a2d93410c5df4c46c5fffeabfff6a1 (diff)
parent245e24dcf3327e46b7ce2ea4a4b667c0d90bd1c0 (diff)
downloadmitmproxy-3067a971f9aef02e1e77c35f7ccc733f37da0c65.tar.gz
mitmproxy-3067a971f9aef02e1e77c35f7ccc733f37da0c65.tar.bz2
mitmproxy-3067a971f9aef02e1e77c35f7ccc733f37da0c65.zip
Merge pull request #2000 from s4chin/add-jpeg-parser
Add jpeg parser
-rw-r--r--mitmproxy/contentviews/image/image_parser.py23
-rw-r--r--mitmproxy/contentviews/image/view.py14
-rw-r--r--mitmproxy/contrib/kaitaistruct/exif.py24
-rw-r--r--mitmproxy/contrib/kaitaistruct/exif_be.py571
-rw-r--r--mitmproxy/contrib/kaitaistruct/exif_le.py571
-rw-r--r--mitmproxy/contrib/kaitaistruct/gif.py38
-rw-r--r--mitmproxy/contrib/kaitaistruct/jpeg.py206
-rw-r--r--mitmproxy/contrib/kaitaistruct/png.py20
-rw-r--r--setup.py2
-rw-r--r--test/mitmproxy/contentviews/test_image.py2
-rw-r--r--test/mitmproxy/contentviews/test_image_parser.py63
-rw-r--r--test/mitmproxy/data/all.jpegbin0 -> 230334 bytes
-rw-r--r--test/mitmproxy/data/image-err1.jpgbin82674 -> 0 bytes
-rw-r--r--test/mitmproxy/data/image_parser/README.md17
-rw-r--r--test/mitmproxy/data/image_parser/all.jpegbin0 -> 230334 bytes
-rw-r--r--test/mitmproxy/data/image_parser/app1.jpegbin0 -> 82670 bytes
-rw-r--r--test/mitmproxy/data/image_parser/comment.jpgbin0 -> 1779 bytes
-rw-r--r--test/mitmproxy/data/image_parser/example.jpgbin0 -> 1755 bytes
18 files changed, 1511 insertions, 40 deletions
diff --git a/mitmproxy/contentviews/image/image_parser.py b/mitmproxy/contentviews/image/image_parser.py
index b104d105..1ff3cff7 100644
--- a/mitmproxy/contentviews/image/image_parser.py
+++ b/mitmproxy/contentviews/image/image_parser.py
@@ -5,6 +5,7 @@ from kaitaistruct import KaitaiStream
from mitmproxy.contrib.kaitaistruct import png
from mitmproxy.contrib.kaitaistruct import gif
+from mitmproxy.contrib.kaitaistruct import jpeg
Metadata = typing.List[typing.Tuple[str, str]]
@@ -55,3 +56,25 @@ def parse_gif(data: bytes) -> Metadata:
if comment is not b'':
parts.append(('comment', str(comment)))
return parts
+
+
+def parse_jpeg(data: bytes) -> Metadata:
+ img = jpeg.Jpeg(KaitaiStream(io.BytesIO(data)))
+ parts = [
+ ('Format', 'JPEG (ISO 10918)')
+ ]
+ for segment in img.segments:
+ if segment.marker._name_ == 'sof0':
+ parts.append(('Size', "{0} x {1} px".format(segment.data.image_width, segment.data.image_height)))
+ if segment.marker._name_ == 'app0':
+ parts.append(('jfif_version', "({0}, {1})".format(segment.data.version_major, segment.data.version_minor)))
+ parts.append(('jfif_density', "({0}, {1})".format(segment.data.density_x, segment.data.density_y)))
+ parts.append(('jfif_unit', str(segment.data.density_units._value_)))
+ if segment.marker._name_ == 'com':
+ parts.append(('comment', str(segment.data)))
+ if segment.marker._name_ == 'app1':
+ if hasattr(segment.data, 'body'):
+ for field in segment.data.body.data.body.ifd0.fields:
+ if field.data is not None:
+ parts.append((field.tag._name_, field.data.decode('UTF-8').strip('\x00')))
+ return parts
diff --git a/mitmproxy/contentviews/image/view.py b/mitmproxy/contentviews/image/view.py
index 9caf9a6c..8fdb26e9 100644
--- a/mitmproxy/contentviews/image/view.py
+++ b/mitmproxy/contentviews/image/view.py
@@ -1,7 +1,6 @@
import io
import imghdr
-from PIL import ExifTags
from PIL import Image
from mitmproxy.types import multidict
@@ -33,6 +32,11 @@ class ViewImage(base.View):
parts = image_parser.parse_gif(data)
fmt = base.format_dict(multidict.MultiDict(parts))
return "%s image" % f, fmt
+ elif image_type == 'jpeg':
+ f = "JPEG"
+ parts = image_parser.parse_jpeg(data)
+ fmt = base.format_dict(multidict.MultiDict(parts))
+ return "%s image" % f, fmt
try:
img = Image.open(io.BytesIO(data))
except IOError:
@@ -47,13 +51,5 @@ class ViewImage(base.View):
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/contrib/kaitaistruct/exif.py b/mitmproxy/contrib/kaitaistruct/exif.py
new file mode 100644
index 00000000..6236a708
--- /dev/null
+++ b/mitmproxy/contrib/kaitaistruct/exif.py
@@ -0,0 +1,24 @@
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+# The source was exif.ksy from here - https://github.com/kaitai-io/kaitai_struct_formats/blob/24e2d00048b8084ceec30a187a79cb87a79a48ba/image/exif.ksy
+
+import array
+import struct
+import zlib
+from enum import Enum
+
+from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
+
+
+from .exif_le import ExifLe
+from .exif_be import ExifBe
+class Exif(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.endianness = self._io.read_u2le()
+ _on = self.endianness
+ if _on == 18761:
+ self.body = ExifLe(self._io)
+ elif _on == 19789:
+ self.body = ExifBe(self._io)
diff --git a/mitmproxy/contrib/kaitaistruct/exif_be.py b/mitmproxy/contrib/kaitaistruct/exif_be.py
new file mode 100644
index 00000000..7980a9e8
--- /dev/null
+++ b/mitmproxy/contrib/kaitaistruct/exif_be.py
@@ -0,0 +1,571 @@
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+# The source was exif_be.ksy from here - https://github.com/kaitai-io/kaitai_struct_formats/blob/24e2d00048b8084ceec30a187a79cb87a79a48ba/image/exif_be.ksy
+
+import array
+import struct
+import zlib
+from enum import Enum
+
+from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
+
+
+class ExifBe(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.version = self._io.read_u2be()
+ self.ifd0_ofs = self._io.read_u4be()
+
+ class Ifd(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.num_fields = self._io.read_u2be()
+ self.fields = [None] * (self.num_fields)
+ for i in range(self.num_fields):
+ self.fields[i] = self._root.IfdField(self._io, self, self._root)
+
+ self.next_ifd_ofs = self._io.read_u4be()
+
+ @property
+ def next_ifd(self):
+ if hasattr(self, '_m_next_ifd'):
+ return self._m_next_ifd if hasattr(self, '_m_next_ifd') else None
+
+ if self.next_ifd_ofs != 0:
+ _pos = self._io.pos()
+ self._io.seek(self.next_ifd_ofs)
+ self._m_next_ifd = self._root.Ifd(self._io, self, self._root)
+ self._io.seek(_pos)
+
+ return self._m_next_ifd if hasattr(self, '_m_next_ifd') else None
+
+
+ class IfdField(KaitaiStruct):
+
+ class FieldTypeEnum(Enum):
+ byte = 1
+ ascii_string = 2
+ word = 3
+ dword = 4
+ rational = 5
+
+ class TagEnum(Enum):
+ image_width = 256
+ image_height = 257
+ bits_per_sample = 258
+ compression = 259
+ photometric_interpretation = 262
+ thresholding = 263
+ cell_width = 264
+ cell_length = 265
+ fill_order = 266
+ document_name = 269
+ image_description = 270
+ make = 271
+ model = 272
+ strip_offsets = 273
+ orientation = 274
+ samples_per_pixel = 277
+ rows_per_strip = 278
+ strip_byte_counts = 279
+ min_sample_value = 280
+ max_sample_value = 281
+ x_resolution = 282
+ y_resolution = 283
+ planar_configuration = 284
+ page_name = 285
+ x_position = 286
+ y_position = 287
+ free_offsets = 288
+ free_byte_counts = 289
+ gray_response_unit = 290
+ gray_response_curve = 291
+ t4_options = 292
+ t6_options = 293
+ resolution_unit = 296
+ page_number = 297
+ color_response_unit = 300
+ transfer_function = 301
+ software = 305
+ modify_date = 306
+ artist = 315
+ host_computer = 316
+ predictor = 317
+ white_point = 318
+ primary_chromaticities = 319
+ color_map = 320
+ halftone_hints = 321
+ tile_width = 322
+ tile_length = 323
+ tile_offsets = 324
+ tile_byte_counts = 325
+ bad_fax_lines = 326
+ clean_fax_data = 327
+ consecutive_bad_fax_lines = 328
+ sub_ifd = 330
+ ink_set = 332
+ ink_names = 333
+ numberof_inks = 334
+ dot_range = 336
+ target_printer = 337
+ extra_samples = 338
+ sample_format = 339
+ s_min_sample_value = 340
+ s_max_sample_value = 341
+ transfer_range = 342
+ clip_path = 343
+ x_clip_path_units = 344
+ y_clip_path_units = 345
+ indexed = 346
+ jpeg_tables = 347
+ opi_proxy = 351
+ global_parameters_ifd = 400
+ profile_type = 401
+ fax_profile = 402
+ coding_methods = 403
+ version_year = 404
+ mode_number = 405
+ decode = 433
+ default_image_color = 434
+ t82_options = 435
+ jpeg_tables2 = 437
+ jpeg_proc = 512
+ thumbnail_offset = 513
+ thumbnail_length = 514
+ jpeg_restart_interval = 515
+ jpeg_lossless_predictors = 517
+ jpeg_point_transforms = 518
+ jpegq_tables = 519
+ jpegdc_tables = 520
+ jpegac_tables = 521
+ y_cb_cr_coefficients = 529
+ y_cb_cr_sub_sampling = 530
+ y_cb_cr_positioning = 531
+ reference_black_white = 532
+ strip_row_counts = 559
+ application_notes = 700
+ uspto_miscellaneous = 999
+ related_image_file_format = 4096
+ related_image_width = 4097
+ related_image_height = 4098
+ rating = 18246
+ xp_dip_xml = 18247
+ stitch_info = 18248
+ rating_percent = 18249
+ sony_raw_file_type = 28672
+ light_falloff_params = 28722
+ chromatic_aberration_corr_params = 28725
+ distortion_corr_params = 28727
+ image_id = 32781
+ wang_tag1 = 32931
+ wang_annotation = 32932
+ wang_tag3 = 32933
+ wang_tag4 = 32934
+ image_reference_points = 32953
+ region_xform_tack_point = 32954
+ warp_quadrilateral = 32955
+ affine_transform_mat = 32956
+ matteing = 32995
+ data_type = 32996
+ image_depth = 32997
+ tile_depth = 32998
+ image_full_width = 33300
+ image_full_height = 33301
+ texture_format = 33302
+ wrap_modes = 33303
+ fov_cot = 33304
+ matrix_world_to_screen = 33305
+ matrix_world_to_camera = 33306
+ model2 = 33405
+ cfa_repeat_pattern_dim = 33421
+ cfa_pattern2 = 33422
+ battery_level = 33423
+ kodak_ifd = 33424
+ copyright = 33432
+ exposure_time = 33434
+ f_number = 33437
+ md_file_tag = 33445
+ md_scale_pixel = 33446
+ md_color_table = 33447
+ md_lab_name = 33448
+ md_sample_info = 33449
+ md_prep_date = 33450
+ md_prep_time = 33451
+ md_file_units = 33452
+ pixel_scale = 33550
+ advent_scale = 33589
+ advent_revision = 33590
+ uic1_tag = 33628
+ uic2_tag = 33629
+ uic3_tag = 33630
+ uic4_tag = 33631
+ iptc_naa = 33723
+ intergraph_packet_data = 33918
+ intergraph_flag_registers = 33919
+ intergraph_matrix = 33920
+ ingr_reserved = 33921
+ model_tie_point = 33922
+ site = 34016
+ color_sequence = 34017
+ it8_header = 34018
+ raster_padding = 34019
+ bits_per_run_length = 34020
+ bits_per_extended_run_length = 34021
+ color_table = 34022
+ image_color_indicator = 34023
+ background_color_indicator = 34024
+ image_color_value = 34025
+ background_color_value = 34026
+ pixel_intensity_range = 34027
+ transparency_indicator = 34028
+ color_characterization = 34029
+ hc_usage = 34030
+ trap_indicator = 34031
+ cmyk_equivalent = 34032
+ sem_info = 34118
+ afcp_iptc = 34152
+ pixel_magic_jbig_options = 34232
+ jpl_carto_ifd = 34263
+ model_transform = 34264
+ wb_grgb_levels = 34306
+ leaf_data = 34310
+ photoshop_settings = 34377
+ exif_offset = 34665
+ icc_profile = 34675
+ tiff_fx_extensions = 34687
+ multi_profiles = 34688
+ shared_data = 34689
+ t88_options = 34690
+ image_layer = 34732
+ geo_tiff_directory = 34735
+ geo_tiff_double_params = 34736
+ geo_tiff_ascii_params = 34737
+ jbig_options = 34750
+ exposure_program = 34850
+ spectral_sensitivity = 34852
+ gps_info = 34853
+ iso = 34855
+ opto_electric_conv_factor = 34856
+ interlace = 34857
+ time_zone_offset = 34858
+ self_timer_mode = 34859
+ sensitivity_type = 34864
+ standard_output_sensitivity = 34865
+ recommended_exposure_index = 34866
+ iso_speed = 34867
+ iso_speed_latitudeyyy = 34868
+ iso_speed_latitudezzz = 34869
+ fax_recv_params = 34908
+ fax_sub_address = 34909
+ fax_recv_time = 34910
+ fedex_edr = 34929
+ leaf_sub_ifd = 34954
+ exif_version = 36864
+ date_time_original = 36867
+ create_date = 36868
+ google_plus_upload_code = 36873
+ offset_time = 36880
+ offset_time_original = 36881
+ offset_time_digitized = 36882
+ components_configuration = 37121
+ compressed_bits_per_pixel = 37122
+ shutter_speed_value = 37377
+ aperture_value = 37378
+ brightness_value = 37379
+ exposure_compensation = 37380
+ max_aperture_value = 37381
+ subject_distance = 37382
+ metering_mode = 37383
+ light_source = 37384
+ flash = 37385
+ focal_length = 37386
+ flash_energy = 37387
+ spatial_frequency_response = 37388
+ noise = 37389
+ focal_plane_x_resolution = 37390
+ focal_plane_y_resolution = 37391
+ focal_plane_resolution_unit = 37392
+ image_number = 37393
+ security_classification = 37394
+ image_history = 37395
+ subject_area = 37396
+ exposure_index = 37397
+ tiff_ep_standard_id = 37398
+ sensing_method = 37399
+ cip3_data_file = 37434
+ cip3_sheet = 37435
+ cip3_side = 37436
+ sto_nits = 37439
+ maker_note = 37500
+ user_comment = 37510
+ sub_sec_time = 37520
+ sub_sec_time_original = 37521
+ sub_sec_time_digitized = 37522
+ ms_document_text = 37679
+ ms_property_set_storage = 37680
+ ms_document_text_position = 37681
+ image_source_data = 37724
+ ambient_temperature = 37888
+ humidity = 37889
+ pressure = 37890
+ water_depth = 37891
+ acceleration = 37892
+ camera_elevation_angle = 37893
+ xp_title = 40091
+ xp_comment = 40092
+ xp_author = 40093
+ xp_keywords = 40094
+ xp_subject = 40095
+ flashpix_version = 40960
+ color_space = 40961
+ exif_image_width = 40962
+ exif_image_height = 40963
+ related_sound_file = 40964
+ interop_offset = 40965
+ samsung_raw_pointers_offset = 40976
+ samsung_raw_pointers_length = 40977
+ samsung_raw_byte_order = 41217
+ samsung_raw_unknown = 41218
+ flash_energy2 = 41483
+ spatial_frequency_response2 = 41484
+ noise2 = 41485
+ focal_plane_x_resolution2 = 41486
+ focal_plane_y_resolution2 = 41487
+ focal_plane_resolution_unit2 = 41488
+ image_number2 = 41489
+ security_classification2 = 41490
+ image_history2 = 41491
+ subject_location = 41492
+ exposure_index2 = 41493
+ tiff_ep_standard_id2 = 41494
+ sensing_method2 = 41495
+ file_source = 41728
+ scene_type = 41729
+ cfa_pattern = 41730
+ custom_rendered = 41985
+ exposure_mode = 41986
+ white_balance = 41987
+ digital_zoom_ratio = 41988
+ focal_length_in35mm_format = 41989
+ scene_capture_type = 41990
+ gain_control = 41991
+ contrast = 41992
+ saturation = 41993
+ sharpness = 41994
+ device_setting_description = 41995
+ subject_distance_range = 41996
+ image_unique_id = 42016
+ owner_name = 42032
+ serial_number = 42033
+ lens_info = 42034
+ lens_make = 42035
+ lens_model = 42036
+ lens_serial_number = 42037
+ gdal_metadata = 42112
+ gdal_no_data = 42113
+ gamma = 42240
+ expand_software = 44992
+ expand_lens = 44993
+ expand_film = 44994
+ expand_filter_lens = 44995
+ expand_scanner = 44996
+ expand_flash_lamp = 44997
+ pixel_format = 48129
+ transformation = 48130
+ uncompressed = 48131
+ image_type = 48132
+ image_width2 = 48256
+ image_height2 = 48257
+ width_resolution = 48258
+ height_resolution = 48259
+ image_offset = 48320
+ image_byte_count = 48321
+ alpha_offset = 48322
+ alpha_byte_count = 48323
+ image_data_discard = 48324
+ alpha_data_discard = 48325
+ oce_scanjob_desc = 50215
+ oce_application_selector = 50216
+ oce_id_number = 50217
+ oce_image_logic = 50218
+ annotations = 50255
+ print_im = 50341
+ original_file_name = 50547
+ uspto_original_content_type = 50560
+ dng_version = 50706
+ dng_backward_version = 50707
+ unique_camera_model = 50708
+ localized_camera_model = 50709
+ cfa_plane_color = 50710
+ cfa_layout = 50711
+ linearization_table = 50712
+ black_level_repeat_dim = 50713
+ black_level = 50714
+ black_level_delta_h = 50715
+ black_level_delta_v = 50716
+ white_level = 50717
+ default_scale = 50718
+ default_crop_origin = 50719
+ default_crop_size = 50720
+ color_matrix1 = 50721
+ color_matrix2 = 50722
+ camera_calibration1 = 50723
+ camera_calibration2 = 50724
+ reduction_matrix1 = 50725
+ reduction_matrix2 = 50726
+ analog_balance = 50727
+ as_shot_neutral = 50728
+ as_shot_white_xy = 50729
+ baseline_exposure = 50730
+ baseline_noise = 50731
+ baseline_sharpness = 50732
+ bayer_green_split = 50733
+ linear_response_limit = 50734
+ camera_serial_number = 50735
+ dng_lens_info = 50736
+ chroma_blur_radius = 50737
+ anti_alias_strength = 50738
+ shadow_scale = 50739
+ sr2_private = 50740
+ maker_note_safety = 50741
+ raw_image_segmentation = 50752
+ calibration_illuminant1 = 50778
+ calibration_illuminant2 = 50779
+ best_quality_scale = 50780
+ raw_data_unique_id = 50781
+ alias_layer_metadata = 50784
+ original_raw_file_name = 50827
+ original_raw_file_data = 50828
+ active_area = 50829
+ masked_areas = 50830
+ as_shot_icc_profile = 50831
+ as_shot_pre_profile_matrix = 50832
+ current_icc_profile = 50833
+ current_pre_profile_matrix = 50834
+ colorimetric_reference = 50879
+ s_raw_type = 50885
+ panasonic_title = 50898
+ panasonic_title2 = 50899
+ camera_calibration_sig = 50931
+ profile_calibration_sig = 50932
+ profile_ifd = 50933
+ as_shot_profile_name = 50934
+ noise_reduction_applied = 50935
+ profile_name = 50936
+ profile_hue_sat_map_dims = 50937
+ profile_hue_sat_map_data1 = 50938
+ profile_hue_sat_map_data2 = 50939
+ profile_tone_curve = 50940
+ profile_embed_policy = 50941
+ profile_copyright = 50942
+ forward_matrix1 = 50964
+ forward_matrix2 = 50965
+ preview_application_name = 50966
+ preview_application_version = 50967
+ preview_settings_name = 50968
+ preview_settings_digest = 50969
+ preview_color_space = 50970
+ preview_date_time = 50971
+ raw_image_digest = 50972
+ original_raw_file_digest = 50973
+ sub_tile_block_size = 50974
+ row_interleave_factor = 50975
+ profile_look_table_dims = 50981
+ profile_look_table_data = 50982
+ opcode_list1 = 51008
+ opcode_list2 = 51009
+ opcode_list3 = 51022
+ noise_profile = 51041
+ time_codes = 51043
+ frame_rate = 51044
+ t_stop = 51058
+ reel_name = 51081
+ original_default_final_size = 51089
+ original_best_quality_size = 51090
+ original_default_crop_size = 51091
+ camera_label = 51105
+ profile_hue_sat_map_encoding = 51107
+ profile_look_table_encoding = 51108
+ baseline_exposure_offset = 51109
+ default_black_render = 51110
+ new_raw_image_digest = 51111
+ raw_to_preview_gain = 51112
+ default_user_crop = 51125
+ padding = 59932
+ offset_schema = 59933
+ owner_name2 = 65000
+ serial_number2 = 65001
+ lens = 65002
+ kdc_ifd = 65024
+ raw_file = 65100
+ converter = 65101
+ white_balance2 = 65102
+ exposure = 65105
+ shadows = 65106
+ brightness = 65107
+ contrast2 = 65108
+ saturation2 = 65109
+ sharpness2 = 65110
+ smoothness = 65111
+ moire_filter = 65112
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.tag = self._root.IfdField.TagEnum(self._io.read_u2be())
+ self.field_type = self._root.IfdField.FieldTypeEnum(self._io.read_u2be())
+ self.length = self._io.read_u4be()
+ self.ofs_or_data = self._io.read_u4be()
+
+ @property
+ def type_byte_length(self):
+ if hasattr(self, '_m_type_byte_length'):
+ return self._m_type_byte_length if hasattr(self, '_m_type_byte_length') else None
+
+ self._m_type_byte_length = (2 if self.field_type == self._root.IfdField.FieldTypeEnum.word else (4 if self.field_type == self._root.IfdField.FieldTypeEnum.dword else 1))
+ return self._m_type_byte_length if hasattr(self, '_m_type_byte_length') else None
+
+ @property
+ def byte_length(self):
+ if hasattr(self, '_m_byte_length'):
+ return self._m_byte_length if hasattr(self, '_m_byte_length') else None
+
+ self._m_byte_length = (self.length * self.type_byte_length)
+ return self._m_byte_length if hasattr(self, '_m_byte_length') else None
+
+ @property
+ def is_immediate_data(self):
+ if hasattr(self, '_m_is_immediate_data'):
+ return self._m_is_immediate_data if hasattr(self, '_m_is_immediate_data') else None
+
+ self._m_is_immediate_data = self.byte_length <= 4
+ return self._m_is_immediate_data if hasattr(self, '_m_is_immediate_data') else None
+
+ @property
+ def data(self):
+ if hasattr(self, '_m_data'):
+ return self._m_data if hasattr(self, '_m_data') else None
+
+ if not self.is_immediate_data:
+ io = self._root._io
+ _pos = io.pos()
+ io.seek(self.ofs_or_data)
+ self._m_data = io.read_bytes(self.byte_length)
+ io.seek(_pos)
+
+ return self._m_data if hasattr(self, '_m_data') else None
+
+
+ @property
+ def ifd0(self):
+ if hasattr(self, '_m_ifd0'):
+ return self._m_ifd0 if hasattr(self, '_m_ifd0') else None
+
+ _pos = self._io.pos()
+ self._io.seek(self.ifd0_ofs)
+ self._m_ifd0 = self._root.Ifd(self._io, self, self._root)
+ self._io.seek(_pos)
+ return self._m_ifd0 if hasattr(self, '_m_ifd0') else None
diff --git a/mitmproxy/contrib/kaitaistruct/exif_le.py b/mitmproxy/contrib/kaitaistruct/exif_le.py
new file mode 100644
index 00000000..207b3beb
--- /dev/null
+++ b/mitmproxy/contrib/kaitaistruct/exif_le.py
@@ -0,0 +1,571 @@
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+# The source was exif_le.ksy from here - https://github.com/kaitai-io/kaitai_struct_formats/blob/24e2d00048b8084ceec30a187a79cb87a79a48ba/image/exif_le.ksy
+
+import array
+import struct
+import zlib
+from enum import Enum
+
+from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
+
+
+class ExifLe(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.version = self._io.read_u2le()
+ self.ifd0_ofs = self._io.read_u4le()
+
+ class Ifd(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.num_fields = self._io.read_u2le()
+ self.fields = [None] * (self.num_fields)
+ for i in range(self.num_fields):
+ self.fields[i] = self._root.IfdField(self._io, self, self._root)
+
+ self.next_ifd_ofs = self._io.read_u4le()
+
+ @property
+ def next_ifd(self):
+ if hasattr(self, '_m_next_ifd'):
+ return self._m_next_ifd if hasattr(self, '_m_next_ifd') else None
+
+ if self.next_ifd_ofs != 0:
+ _pos = self._io.pos()
+ self._io.seek(self.next_ifd_ofs)
+ self._m_next_ifd = self._root.Ifd(self._io, self, self._root)
+ self._io.seek(_pos)
+
+ return self._m_next_ifd if hasattr(self, '_m_next_ifd') else None
+
+
+ class IfdField(KaitaiStruct):
+
+ class FieldTypeEnum(Enum):
+ byte = 1
+ ascii_string = 2
+ word = 3
+ dword = 4
+ rational = 5
+
+ class TagEnum(Enum):
+ image_width = 256
+ image_height = 257
+ bits_per_sample = 258
+ compression = 259
+ photometric_interpretation = 262
+ thresholding = 263
+ cell_width = 264
+ cell_length = 265
+ fill_order = 266
+ document_name = 269
+ image_description = 270
+ make = 271
+ model = 272
+ strip_offsets = 273
+ orientation = 274
+ samples_per_pixel = 277
+ rows_per_strip = 278
+ strip_byte_counts = 279
+ min_sample_value = 280
+ max_sample_value = 281
+ x_resolution = 282
+ y_resolution = 283
+ planar_configuration = 284
+ page_name = 285
+ x_position = 286
+ y_position = 287
+ free_offsets = 288
+ free_byte_counts = 289
+ gray_response_unit = 290
+ gray_response_curve = 291
+ t4_options = 292
+ t6_options = 293
+ resolution_unit = 296
+ page_number = 297
+ color_response_unit = 300
+ transfer_function = 301
+ software = 305
+ modify_date = 306
+ artist = 315
+ host_computer = 316
+ predictor = 317
+ white_point = 318
+ primary_chromaticities = 319
+ color_map = 320
+ halftone_hints = 321
+ tile_width = 322
+ tile_length = 323
+ tile_offsets = 324
+ tile_byte_counts = 325
+ bad_fax_lines = 326
+ clean_fax_data = 327
+ consecutive_bad_fax_lines = 328
+ sub_ifd = 330
+ ink_set = 332
+ ink_names = 333
+ numberof_inks = 334
+ dot_range = 336
+ target_printer = 337
+ extra_samples = 338
+ sample_format = 339
+ s_min_sample_value = 340
+ s_max_sample_value = 341
+ transfer_range = 342
+ clip_path = 343
+ x_clip_path_units = 344
+ y_clip_path_units = 345
+ indexed = 346
+ jpeg_tables = 347
+ opi_proxy = 351
+ global_parameters_ifd = 400
+ profile_type = 401
+ fax_profile = 402
+ coding_methods = 403
+ version_year = 404
+ mode_number = 405
+ decode = 433
+ default_image_color = 434
+ t82_options = 435
+ jpeg_tables2 = 437
+ jpeg_proc = 512
+ thumbnail_offset = 513
+ thumbnail_length = 514
+ jpeg_restart_interval = 515
+ jpeg_lossless_predictors = 517
+ jpeg_point_transforms = 518
+ jpegq_tables = 519
+ jpegdc_tables = 520
+ jpegac_tables = 521
+ y_cb_cr_coefficients = 529
+ y_cb_cr_sub_sampling = 530
+ y_cb_cr_positioning = 531
+ reference_black_white = 532
+ strip_row_counts = 559
+ application_notes = 700
+ uspto_miscellaneous = 999
+ related_image_file_format = 4096
+ related_image_width = 4097
+ related_image_height = 4098
+ rating = 18246
+ xp_dip_xml = 18247
+ stitch_info = 18248
+ rating_percent = 18249
+ sony_raw_file_type = 28672
+ light_falloff_params = 28722
+ chromatic_aberration_corr_params = 28725
+ distortion_corr_params = 28727
+ image_id = 32781
+ wang_tag1 = 32931
+ wang_annotation = 32932
+ wang_tag3 = 32933
+ wang_tag4 = 32934
+ image_reference_points = 32953
+ region_xform_tack_point = 32954
+ warp_quadrilateral = 32955
+ affine_transform_mat = 32956
+ matteing = 32995
+ data_type = 32996
+ image_depth = 32997
+ tile_depth = 32998
+ image_full_width = 33300
+ image_full_height = 33301
+ texture_format = 33302
+ wrap_modes = 33303
+ fov_cot = 33304
+ matrix_world_to_screen = 33305
+ matrix_world_to_camera = 33306
+ model2 = 33405
+ cfa_repeat_pattern_dim = 33421
+ cfa_pattern2 = 33422
+ battery_level = 33423
+ kodak_ifd = 33424
+ copyright = 33432
+ exposure_time = 33434
+ f_number = 33437
+ md_file_tag = 33445
+ md_scale_pixel = 33446
+ md_color_table = 33447
+ md_lab_name = 33448
+ md_sample_info = 33449
+ md_prep_date = 33450
+ md_prep_time = 33451
+ md_file_units = 33452
+ pixel_scale = 33550
+ advent_scale = 33589
+ advent_revision = 33590
+ uic1_tag = 33628
+ uic2_tag = 33629
+ uic3_tag = 33630
+ uic4_tag = 33631
+ iptc_naa = 33723
+ intergraph_packet_data = 33918
+ intergraph_flag_registers = 33919
+ intergraph_matrix = 33920
+ ingr_reserved = 33921
+ model_tie_point = 33922
+ site = 34016
+ color_sequence = 34017
+ it8_header = 34018
+ raster_padding = 34019
+ bits_per_run_length = 34020
+ bits_per_extended_run_length = 34021
+ color_table = 34022
+ image_color_indicator = 34023
+ background_color_indicator = 34024
+ image_color_value = 34025
+ background_color_value = 34026
+ pixel_intensity_range = 34027
+ transparency_indicator = 34028
+ color_characterization = 34029
+ hc_usage = 34030
+ trap_indicator = 34031
+ cmyk_equivalent = 34032
+ sem_info = 34118
+ afcp_iptc = 34152
+ pixel_magic_jbig_options = 34232
+ jpl_carto_ifd = 34263
+ model_transform = 34264
+ wb_grgb_levels = 34306
+ leaf_data = 34310
+ photoshop_settings = 34377
+ exif_offset = 34665
+ icc_profile = 34675
+ tiff_fx_extensions = 34687
+ multi_profiles = 34688
+ shared_data = 34689
+ t88_options = 34690
+ image_layer = 34732
+ geo_tiff_directory = 34735
+ geo_tiff_double_params = 34736
+ geo_tiff_ascii_params = 34737
+ jbig_options = 34750
+ exposure_program = 34850
+ spectral_sensitivity = 34852
+ gps_info = 34853
+ iso = 34855
+ opto_electric_conv_factor = 34856
+ interlace = 34857
+ time_zone_offset = 34858
+ self_timer_mode = 34859
+ sensitivity_type = 34864
+ standard_output_sensitivity = 34865
+ recommended_exposure_index = 34866
+ iso_speed = 34867
+ iso_speed_latitudeyyy = 34868
+ iso_speed_latitudezzz = 34869
+ fax_recv_params = 34908
+ fax_sub_address = 34909
+ fax_recv_time = 34910
+ fedex_edr = 34929
+ leaf_sub_ifd = 34954
+ exif_version = 36864
+ date_time_original = 36867
+ create_date = 36868
+ google_plus_upload_code = 36873
+ offset_time = 36880
+ offset_time_original = 36881
+ offset_time_digitized = 36882
+ components_configuration = 37121
+ compressed_bits_per_pixel = 37122
+ shutter_speed_value = 37377
+ aperture_value = 37378
+ brightness_value = 37379
+ exposure_compensation = 37380
+ max_aperture_value = 37381
+ subject_distance = 37382
+ metering_mode = 37383
+ light_source = 37384
+ flash = 37385
+ focal_length = 37386
+ flash_energy = 37387
+ spatial_frequency_response = 37388
+ noise = 37389
+ focal_plane_x_resolution = 37390
+ focal_plane_y_resolution = 37391
+ focal_plane_resolution_unit = 37392
+ image_number = 37393
+ security_classification = 37394
+ image_history = 37395
+ subject_area = 37396
+ exposure_index = 37397
+ tiff_ep_standard_id = 37398
+ sensing_method = 37399
+ cip3_data_file = 37434
+ cip3_sheet = 37435
+ cip3_side = 37436
+ sto_nits = 37439
+ maker_note = 37500
+ user_comment = 37510
+ sub_sec_time = 37520
+ sub_sec_time_original = 37521
+ sub_sec_time_digitized = 37522
+ ms_document_text = 37679
+ ms_property_set_storage = 37680
+ ms_document_text_position = 37681
+ image_source_data = 37724
+ ambient_temperature = 37888
+ humidity = 37889
+ pressure = 37890
+ water_depth = 37891
+ acceleration = 37892
+ camera_elevation_angle = 37893
+ xp_title = 40091
+ xp_comment = 40092
+ xp_author = 40093
+ xp_keywords = 40094
+ xp_subject = 40095
+ flashpix_version = 40960
+ color_space = 40961
+ exif_image_width = 40962
+ exif_image_height = 40963
+ related_sound_file = 40964
+ interop_offset = 40965
+ samsung_raw_pointers_offset = 40976
+ samsung_raw_pointers_length = 40977
+ samsung_raw_byte_order = 41217
+ samsung_raw_unknown = 41218
+ flash_energy2 = 41483
+ spatial_frequency_response2 = 41484
+ noise2 = 41485
+ focal_plane_x_resolution2 = 41486
+ focal_plane_y_resolution2 = 41487
+ focal_plane_resolution_unit2 = 41488
+ image_number2 = 41489
+ security_classification2 = 41490
+ image_history2 = 41491
+ subject_location = 41492
+ exposure_index2 = 41493
+ tiff_ep_standard_id2 = 41494
+ sensing_method2 = 41495
+ file_source = 41728
+ scene_type = 41729
+ cfa_pattern = 41730
+ custom_rendered = 41985
+ exposure_mode = 41986
+ white_balance = 41987
+ digital_zoom_ratio = 41988
+ focal_length_in35mm_format = 41989
+ scene_capture_type = 41990
+ gain_control = 41991
+ contrast = 41992
+ saturation = 41993
+ sharpness = 41994
+ device_setting_description = 41995
+ subject_distance_range = 41996
+ image_unique_id = 42016
+ owner_name = 42032
+ serial_number = 42033
+ lens_info = 42034
+ lens_make = 42035
+ lens_model = 42036
+ lens_serial_number = 42037
+ gdal_metadata = 42112
+ gdal_no_data = 42113
+ gamma = 42240
+ expand_software = 44992
+ expand_lens = 44993
+ expand_film = 44994
+ expand_filter_lens = 44995
+ expand_scanner = 44996
+ expand_flash_lamp = 44997
+ pixel_format = 48129
+ transformation = 48130
+ uncompressed = 48131
+ image_type = 48132
+ image_width2 = 48256
+ image_height2 = 48257
+ width_resolution = 48258
+ height_resolution = 48259
+ image_offset = 48320
+ image_byte_count = 48321
+ alpha_offset = 48322
+ alpha_byte_count = 48323
+ image_data_discard = 48324
+ alpha_data_discard = 48325
+ oce_scanjob_desc = 50215
+ oce_application_selector = 50216
+ oce_id_number = 50217
+ oce_image_logic = 50218
+ annotations = 50255
+ print_im = 50341
+ original_file_name = 50547
+ uspto_original_content_type = 50560
+ dng_version = 50706
+ dng_backward_version = 50707
+ unique_camera_model = 50708
+ localized_camera_model = 50709
+ cfa_plane_color = 50710
+ cfa_layout = 50711
+ linearization_table = 50712
+ black_level_repeat_dim = 50713
+ black_level = 50714
+ black_level_delta_h = 50715
+ black_level_delta_v = 50716
+ white_level = 50717
+ default_scale = 50718
+ default_crop_origin = 50719
+ default_crop_size = 50720
+ color_matrix1 = 50721
+ color_matrix2 = 50722
+ camera_calibration1 = 50723
+ camera_calibration2 = 50724
+ reduction_matrix1 = 50725
+ reduction_matrix2 = 50726
+ analog_balance = 50727
+ as_shot_neutral = 50728
+ as_shot_white_xy = 50729
+ baseline_exposure = 50730
+ baseline_noise = 50731
+ baseline_sharpness = 50732
+ bayer_green_split = 50733
+ linear_response_limit = 50734
+ camera_serial_number = 50735
+ dng_lens_info = 50736
+ chroma_blur_radius = 50737
+ anti_alias_strength = 50738
+ shadow_scale = 50739
+ sr2_private = 50740
+ maker_note_safety = 50741
+ raw_image_segmentation = 50752
+ calibration_illuminant1 = 50778
+ calibration_illuminant2 = 50779
+ best_quality_scale = 50780
+ raw_data_unique_id = 50781
+ alias_layer_metadata = 50784
+ original_raw_file_name = 50827
+ original_raw_file_data = 50828
+ active_area = 50829
+ masked_areas = 50830
+ as_shot_icc_profile = 50831
+ as_shot_pre_profile_matrix = 50832
+ current_icc_profile = 50833
+ current_pre_profile_matrix = 50834
+ colorimetric_reference = 50879
+ s_raw_type = 50885
+ panasonic_title = 50898
+ panasonic_title2 = 50899
+ camera_calibration_sig = 50931
+ profile_calibration_sig = 50932
+ profile_ifd = 50933
+ as_shot_profile_name = 50934
+ noise_reduction_applied = 50935
+ profile_name = 50936
+ profile_hue_sat_map_dims = 50937
+ profile_hue_sat_map_data1 = 50938
+ profile_hue_sat_map_data2 = 50939
+ profile_tone_curve = 50940
+ profile_embed_policy = 50941
+ profile_copyright = 50942
+ forward_matrix1 = 50964
+ forward_matrix2 = 50965
+ preview_application_name = 50966
+ preview_application_version = 50967
+ preview_settings_name = 50968
+ preview_settings_digest = 50969
+ preview_color_space = 50970
+ preview_date_time = 50971
+ raw_image_digest = 50972
+ original_raw_file_digest = 50973
+ sub_tile_block_size = 50974
+ row_interleave_factor = 50975
+ profile_look_table_dims = 50981
+ profile_look_table_data = 50982
+ opcode_list1 = 51008
+ opcode_list2 = 51009
+ opcode_list3 = 51022
+ noise_profile = 51041
+ time_codes = 51043
+ frame_rate = 51044
+ t_stop = 51058
+ reel_name = 51081
+ original_default_final_size = 51089
+ original_best_quality_size = 51090
+ original_default_crop_size = 51091
+ camera_label = 51105
+ profile_hue_sat_map_encoding = 51107
+ profile_look_table_encoding = 51108
+ baseline_exposure_offset = 51109
+ default_black_render = 51110
+ new_raw_image_digest = 51111
+ raw_to_preview_gain = 51112
+ default_user_crop = 51125
+ padding = 59932
+ offset_schema = 59933
+ owner_name2 = 65000
+ serial_number2 = 65001
+ lens = 65002
+ kdc_ifd = 65024
+ raw_file = 65100
+ converter = 65101
+ white_balance2 = 65102
+ exposure = 65105
+ shadows = 65106
+ brightness = 65107
+ contrast2 = 65108
+ saturation2 = 65109
+ sharpness2 = 65110
+ smoothness = 65111
+ moire_filter = 65112
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.tag = self._root.IfdField.TagEnum(self._io.read_u2le())
+ self.field_type = self._root.IfdField.FieldTypeEnum(self._io.read_u2le())
+ self.length = self._io.read_u4le()
+ self.ofs_or_data = self._io.read_u4le()
+
+ @property
+ def type_byte_length(self):
+ if hasattr(self, '_m_type_byte_length'):
+ return self._m_type_byte_length if hasattr(self, '_m_type_byte_length') else None
+
+ self._m_type_byte_length = (2 if self.field_type == self._root.IfdField.FieldTypeEnum.word else (4 if self.field_type == self._root.IfdField.FieldTypeEnum.dword else 1))
+ return self._m_type_byte_length if hasattr(self, '_m_type_byte_length') else None
+
+ @property
+ def byte_length(self):
+ if hasattr(self, '_m_byte_length'):
+ return self._m_byte_length if hasattr(self, '_m_byte_length') else None
+
+ self._m_byte_length = (self.length * self.type_byte_length)
+ return self._m_byte_length if hasattr(self, '_m_byte_length') else None
+
+ @property
+ def is_immediate_data(self):
+ if hasattr(self, '_m_is_immediate_data'):
+ return self._m_is_immediate_data if hasattr(self, '_m_is_immediate_data') else None
+
+ self._m_is_immediate_data = self.byte_length <= 4
+ return self._m_is_immediate_data if hasattr(self, '_m_is_immediate_data') else None
+
+ @property
+ def data(self):
+ if hasattr(self, '_m_data'):
+ return self._m_data if hasattr(self, '_m_data') else None
+
+ if not self.is_immediate_data:
+ io = self._root._io
+ _pos = io.pos()
+ io.seek(self.ofs_or_data)
+ self._m_data = io.read_bytes(self.byte_length)
+ io.seek(_pos)
+
+ return self._m_data if hasattr(self, '_m_data') else None
+
+
+ @property
+ def ifd0(self):
+ if hasattr(self, '_m_ifd0'):
+ return self._m_ifd0 if hasattr(self, '_m_ifd0') else None
+
+ _pos = self._io.pos()
+ self._io.seek(self.ifd0_ofs)
+ self._m_ifd0 = self._root.Ifd(self._io, self, self._root)
+ self._io.seek(_pos)
+ return self._m_ifd0 if hasattr(self, '_m_ifd0') else None
diff --git a/mitmproxy/contrib/kaitaistruct/gif.py b/mitmproxy/contrib/kaitaistruct/gif.py
index 3a847d54..61499cc7 100644
--- a/mitmproxy/contrib/kaitaistruct/gif.py
+++ b/mitmproxy/contrib/kaitaistruct/gif.py
@@ -69,18 +69,18 @@ class Gif(KaitaiStruct):
@property
def has_color_table(self):
if hasattr(self, '_m_has_color_table'):
- return self._m_has_color_table
+ return self._m_has_color_table if hasattr(self, '_m_has_color_table') else None
self._m_has_color_table = (self.flags & 128) != 0
- return self._m_has_color_table
+ return self._m_has_color_table if hasattr(self, '_m_has_color_table') else None
@property
def color_table_size(self):
if hasattr(self, '_m_color_table_size'):
- return self._m_color_table_size
+ return self._m_color_table_size if hasattr(self, '_m_color_table_size') else None
self._m_color_table_size = (2 << (self.flags & 7))
- return self._m_color_table_size
+ return self._m_color_table_size if hasattr(self, '_m_color_table_size') else None
class LocalImageDescriptor(KaitaiStruct):
@@ -103,34 +103,34 @@ class Gif(KaitaiStruct):
@property
def has_color_table(self):
if hasattr(self, '_m_has_color_table'):
- return self._m_has_color_table
+ return self._m_has_color_table if hasattr(self, '_m_has_color_table') else None
self._m_has_color_table = (self.flags & 128) != 0
- return self._m_has_color_table
+ return self._m_has_color_table if hasattr(self, '_m_has_color_table') else None
@property
def has_interlace(self):
if hasattr(self, '_m_has_interlace'):
- return self._m_has_interlace
+ return self._m_has_interlace if hasattr(self, '_m_has_interlace') else None
self._m_has_interlace = (self.flags & 64) != 0
- return self._m_has_interlace
+ return self._m_has_interlace if hasattr(self, '_m_has_interlace') else None
@property
def has_sorted_color_table(self):
if hasattr(self, '_m_has_sorted_color_table'):
- return self._m_has_sorted_color_table
+ return self._m_has_sorted_color_table if hasattr(self, '_m_has_sorted_color_table') else None
self._m_has_sorted_color_table = (self.flags & 32) != 0
- return self._m_has_sorted_color_table
+ return self._m_has_sorted_color_table if hasattr(self, '_m_has_sorted_color_table') else None
@property
def color_table_size(self):
if hasattr(self, '_m_color_table_size'):
- return self._m_color_table_size
+ return self._m_color_table_size if hasattr(self, '_m_color_table_size') else None
self._m_color_table_size = (2 << (self.flags & 7))
- return self._m_color_table_size
+ return self._m_color_table_size if hasattr(self, '_m_color_table_size') else None
class Block(KaitaiStruct):
@@ -162,7 +162,7 @@ class Gif(KaitaiStruct):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
- self.magic = self._io.ensure_fixed_contents(3, struct.pack('3b', 71, 73, 70))
+ self.magic = self._io.ensure_fixed_contents(struct.pack('3b', 71, 73, 70))
self.version = self._io.read_bytes(3)
@@ -171,27 +171,27 @@ class Gif(KaitaiStruct):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
- self.block_size = self._io.ensure_fixed_contents(1, struct.pack('1b', 4))
+ self.block_size = self._io.ensure_fixed_contents(struct.pack('1b', 4))
self.flags = self._io.read_u1()
self.delay_time = self._io.read_u2le()
self.transparent_idx = self._io.read_u1()
- self.terminator = self._io.ensure_fixed_contents(1, struct.pack('1b', 0))
+ self.terminator = self._io.ensure_fixed_contents(struct.pack('1b', 0))
@property
def transparent_color_flag(self):
if hasattr(self, '_m_transparent_color_flag'):
- return self._m_transparent_color_flag
+ return self._m_transparent_color_flag if hasattr(self, '_m_transparent_color_flag') else None
self._m_transparent_color_flag = (self.flags & 1) != 0
- return self._m_transparent_color_flag
+ return self._m_transparent_color_flag if hasattr(self, '_m_transparent_color_flag') else None
@property
def user_input_flag(self):
if hasattr(self, '_m_user_input_flag'):
- return self._m_user_input_flag
+ return self._m_user_input_flag if hasattr(self, '_m_user_input_flag') else None
self._m_user_input_flag = (self.flags & 2) != 0
- return self._m_user_input_flag
+ return self._m_user_input_flag if hasattr(self, '_m_user_input_flag') else None
class Subblock(KaitaiStruct):
diff --git a/mitmproxy/contrib/kaitaistruct/jpeg.py b/mitmproxy/contrib/kaitaistruct/jpeg.py
new file mode 100644
index 00000000..08e382a9
--- /dev/null
+++ b/mitmproxy/contrib/kaitaistruct/jpeg.py
@@ -0,0 +1,206 @@
+# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
+# The source was jpeg.ksy from here - https://github.com/kaitai-io/kaitai_struct_formats/blob/24e2d00048b8084ceec30a187a79cb87a79a48ba/image/jpeg.ksy
+
+import array
+import struct
+import zlib
+from enum import Enum
+
+from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
+
+
+from .exif import Exif
+class Jpeg(KaitaiStruct):
+
+ class ComponentId(Enum):
+ y = 1
+ cb = 2
+ cr = 3
+ i = 4
+ q = 5
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.segments = []
+ while not self._io.is_eof():
+ self.segments.append(self._root.Segment(self._io, self, self._root))
+
+
+ class Segment(KaitaiStruct):
+
+ class MarkerEnum(Enum):
+ tem = 1
+ sof0 = 192
+ sof1 = 193
+ sof2 = 194
+ sof3 = 195
+ dht = 196
+ sof5 = 197
+ sof6 = 198
+ sof7 = 199
+ soi = 216
+ eoi = 217
+ sos = 218
+ dqt = 219
+ dnl = 220
+ dri = 221
+ dhp = 222
+ app0 = 224
+ app1 = 225
+ app2 = 226
+ app3 = 227
+ app4 = 228
+ app5 = 229
+ app6 = 230
+ app7 = 231
+ app8 = 232
+ app9 = 233
+ app10 = 234
+ app11 = 235
+ app12 = 236
+ app13 = 237
+ app14 = 238
+ app15 = 239
+ com = 254
+ 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(struct.pack('1b', -1))
+ self.marker = self._root.Segment.MarkerEnum(self._io.read_u1())
+ if ((self.marker != self._root.Segment.MarkerEnum.soi) and (self.marker != self._root.Segment.MarkerEnum.eoi)) :
+ self.length = self._io.read_u2be()
+
+ if ((self.marker != self._root.Segment.MarkerEnum.soi) and (self.marker != self._root.Segment.MarkerEnum.eoi)) :
+ _on = self.marker
+ if _on == self._root.Segment.MarkerEnum.sos:
+ self._raw_data = self._io.read_bytes((self.length - 2))
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.SegmentSos(io, self, self._root)
+ elif _on == self._root.Segment.MarkerEnum.app1:
+ self._raw_data = self._io.read_bytes((self.length - 2))
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.SegmentApp1(io, self, self._root)
+ elif _on == self._root.Segment.MarkerEnum.sof0:
+ self._raw_data = self._io.read_bytes((self.length - 2))
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.SegmentSof0(io, self, self._root)
+ elif _on == self._root.Segment.MarkerEnum.app0:
+ self._raw_data = self._io.read_bytes((self.length - 2))
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = self._root.SegmentApp0(io, self, self._root)
+ else:
+ self.data = self._io.read_bytes((self.length - 2))
+
+ if self.marker == self._root.Segment.MarkerEnum.sos:
+ self.image_data = self._io.read_bytes_full()
+
+
+
+ class SegmentSos(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.num_components = self._io.read_u1()
+ self.components = [None] * (self.num_components)
+ for i in range(self.num_components):
+ self.components[i] = self._root.SegmentSos.Component(self._io, self, self._root)
+
+ self.start_spectral_selection = self._io.read_u1()
+ self.end_spectral = self._io.read_u1()
+ self.appr_bit_pos = self._io.read_u1()
+
+ class Component(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.id = self._root.ComponentId(self._io.read_u1())
+ self.huffman_table = self._io.read_u1()
+
+
+
+ class SegmentApp1(KaitaiStruct):
+ 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.read_strz("ASCII", 0, False, True, True)
+ _on = self.magic
+ if _on == u"Exif":
+ self.body = self._root.ExifInJpeg(self._io, self, self._root)
+
+
+ class SegmentSof0(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.bits_per_sample = self._io.read_u1()
+ self.image_height = self._io.read_u2be()
+ self.image_width = self._io.read_u2be()
+ self.num_components = self._io.read_u1()
+ self.components = [None] * (self.num_components)
+ for i in range(self.num_components):
+ self.components[i] = self._root.SegmentSof0.Component(self._io, self, self._root)
+
+
+ class Component(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.id = self._root.ComponentId(self._io.read_u1())
+ self.sampling_factors = self._io.read_u1()
+ self.quantization_table_id = self._io.read_u1()
+
+ @property
+ def sampling_x(self):
+ if hasattr(self, '_m_sampling_x'):
+ return self._m_sampling_x if hasattr(self, '_m_sampling_x') else None
+
+ self._m_sampling_x = ((self.sampling_factors & 240) >> 4)
+ return self._m_sampling_x if hasattr(self, '_m_sampling_x') else None
+
+ @property
+ def sampling_y(self):
+ if hasattr(self, '_m_sampling_y'):
+ return self._m_sampling_y if hasattr(self, '_m_sampling_y') else None
+
+ self._m_sampling_y = (self.sampling_factors & 15)
+ return self._m_sampling_y if hasattr(self, '_m_sampling_y') else None
+
+
+
+ class ExifInJpeg(KaitaiStruct):
+ def __init__(self, _io, _parent=None, _root=None):
+ self._io = _io
+ self._parent = _parent
+ self._root = _root if _root else self
+ self.extra_zero = self._io.ensure_fixed_contents(struct.pack('1b', 0))
+ self._raw_data = self._io.read_bytes_full()
+ io = KaitaiStream(BytesIO(self._raw_data))
+ self.data = Exif(io)
+
+
+ class SegmentApp0(KaitaiStruct):
+
+ class DensityUnit(Enum):
+ no_units = 0
+ pixels_per_inch = 1
+ pixels_per_cm = 2
+ 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.read_str_byte_limit(5, "ASCII")
+ self.version_major = self._io.read_u1()
+ self.version_minor = self._io.read_u1()
+ self.density_units = self._root.SegmentApp0.DensityUnit(self._io.read_u1())
+ self.density_x = self._io.read_u2be()
+ self.density_y = self._io.read_u2be()
+ self.thumbnail_x = self._io.read_u1()
+ self.thumbnail_y = self._io.read_u1()
+ self.thumbnail = self._io.read_bytes(((self.thumbnail_x * self.thumbnail_y) * 3))
diff --git a/mitmproxy/contrib/kaitaistruct/png.py b/mitmproxy/contrib/kaitaistruct/png.py
index 5b0e3ca3..2f3c1a5c 100644
--- a/mitmproxy/contrib/kaitaistruct/png.py
+++ b/mitmproxy/contrib/kaitaistruct/png.py
@@ -25,9 +25,9 @@ class Png(KaitaiStruct):
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.magic = self._io.ensure_fixed_contents(struct.pack('8b', -119, 80, 78, 71, 13, 10, 26, 10))
+ self.ihdr_len = self._io.ensure_fixed_contents(struct.pack('4b', 0, 0, 0, 13))
+ self.ihdr_type = self._io.ensure_fixed_contents(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 = []
@@ -117,18 +117,18 @@ class Png(KaitaiStruct):
@property
def x(self):
if hasattr(self, '_m_x'):
- return self._m_x
+ return self._m_x if hasattr(self, '_m_x') else None
self._m_x = (self.x_int / 100000.0)
- return self._m_x
+ return self._m_x if hasattr(self, '_m_x') else None
@property
def y(self):
if hasattr(self, '_m_y'):
- return self._m_y
+ return self._m_y if hasattr(self, '_m_y') else None
self._m_y = (self.y_int / 100000.0)
- return self._m_y
+ return self._m_y if hasattr(self, '_m_y') else None
class BkgdGreyscale(KaitaiStruct):
@@ -186,7 +186,7 @@ class Png(KaitaiStruct):
self._io = _io
self._parent = _parent
self._root = _root if _root else self
- self.render_intent = self._root.Intent(self._io.read_u1())
+ self.render_intent = self._root.SrgbChunk.Intent(self._io.read_u1())
class CompressedTextChunk(KaitaiStruct):
@@ -220,10 +220,10 @@ class Png(KaitaiStruct):
@property
def gamma_ratio(self):
if hasattr(self, '_m_gamma_ratio'):
- return self._m_gamma_ratio
+ return self._m_gamma_ratio if hasattr(self, '_m_gamma_ratio') else None
self._m_gamma_ratio = (100000.0 / self.gamma_int)
- return self._m_gamma_ratio
+ return self._m_gamma_ratio if hasattr(self, '_m_gamma_ratio') else None
class BkgdChunk(KaitaiStruct):
diff --git a/setup.py b/setup.py
index 7f3e7186..0e41539a 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, <0.6",
+ "kaitaistruct>=0.6, <0.7",
"Pillow>=3.2, <4.1",
"passlib>=1.6.5, <1.8",
"pyasn1>=0.1.9, <0.3",
diff --git a/test/mitmproxy/contentviews/test_image.py b/test/mitmproxy/contentviews/test_image.py
index 9e7e28f5..e3dfb714 100644
--- a/test/mitmproxy/contentviews/test_image.py
+++ b/test/mitmproxy/contentviews/test_image.py
@@ -8,7 +8,7 @@ def test_view_image():
for img in [
"mitmproxy/data/image.png",
"mitmproxy/data/image.gif",
- "mitmproxy/data/image-err1.jpg",
+ "mitmproxy/data/all.jpeg",
"mitmproxy/data/image.ico"
]:
with open(tutils.test_data.path(img), "rb") as f:
diff --git a/test/mitmproxy/contentviews/test_image_parser.py b/test/mitmproxy/contentviews/test_image_parser.py
index 4241a1bb..3c8bfdf7 100644
--- a/test/mitmproxy/contentviews/test_image_parser.py
+++ b/test/mitmproxy/contentviews/test_image_parser.py
@@ -104,3 +104,66 @@ def test_parse_png(filename, metadata):
def test_parse_gif(filename, metadata):
with open(tutils.test_data.path(filename), 'rb') as f:
assert metadata == image_parser.parse_gif(f.read())
+
+
+@pytest.mark.parametrize("filename, metadata", {
+ # check app0
+ "mitmproxy/data/image_parser/example.jpg": [
+ ('Format', 'JPEG (ISO 10918)'),
+ ('jfif_version', '(1, 1)'),
+ ('jfif_density', '(96, 96)'),
+ ('jfif_unit', '1'),
+ ('Size', '256 x 256 px')
+ ],
+ # check com
+ "mitmproxy/data/image_parser/comment.jpg": [
+ ('Format', 'JPEG (ISO 10918)'),
+ ('jfif_version', '(1, 1)'),
+ ('jfif_density', '(96, 96)'),
+ ('jfif_unit', '1'),
+ ('comment', "b'mitmproxy test image'"),
+ ('Size', '256 x 256 px')
+ ],
+ # check app1
+ "mitmproxy/data/image_parser/app1.jpeg": [
+ ('Format', 'JPEG (ISO 10918)'),
+ ('jfif_version', '(1, 1)'),
+ ('jfif_density', '(72, 72)'),
+ ('jfif_unit', '1'),
+ ('make', 'Canon'),
+ ('model', 'Canon PowerShot A60'),
+ ('modify_date', '2004:07:16 18:46:04'),
+ ('Size', '717 x 558 px')
+ ],
+ # check multiple segments
+ "mitmproxy/data/image_parser/all.jpeg": [
+ ('Format', 'JPEG (ISO 10918)'),
+ ('jfif_version', '(1, 1)'),
+ ('jfif_density', '(300, 300)'),
+ ('jfif_unit', '1'),
+ ('comment', 'b\'BARTOLOMEO DI FRUOSINO\\r\\n(b. ca. 1366, Firenze, d. 1441, '
+ 'Firenze)\\r\\n\\r\\nInferno, from the Divine Comedy by Dante (Folio 1v)'
+ '\\r\\n1430-35\\r\\nTempera, gold, and silver on parchment, 365 x 265 mm'
+ '\\r\\nBiblioth\\xe8que Nationale, Paris\\r\\n\\r\\nThe codex in Paris '
+ 'contains the text of the Inferno, the first of three books of the Divine '
+ 'Comedy, the masterpiece of the Florentine poet Dante Alighieri (1265-1321).'
+ ' The codex begins with two full-page illuminations. On folio 1v Dante and '
+ 'Virgil stand within the doorway of Hell at the upper left and observe its '
+ 'nine different zones. Dante and Virgil are to wade through successive '
+ 'circles teeming with images of the damned. The gates of Hell appear in '
+ 'the middle, a scarlet row of open sarcophagi before them. Devils orchestrate'
+ ' the movements of the wretched souls.\\r\\n\\r\\nThe vision of the fiery '
+ 'inferno follows a convention established by <A onclick="return OpenOther'
+ '(\\\'/html/n/nardo/strozzi3.html\\\')" HREF="/html/n/nardo/strozzi3.html">'
+ 'Nardo di Cione\\\'s fresco</A> in the church of Santa Maria Novella, Florence.'
+ ' Of remarkable vivacity and intensity of expression, the illumination is '
+ 'executed in Bartolomeo\\\'s late style.\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n'
+ '--- Keywords: --------------\\r\\n\\r\\nAuthor: BARTOLOMEO DI FRUOSINO'
+ '\\r\\nTitle: Inferno, from the Divine Comedy by Dante (Folio 1v)\\r\\nTime-line:'
+ ' 1401-1450\\r\\nSchool: Italian\\r\\nForm: illumination\\r\\nType: other\\r\\n\''),
+ ('Size', '750 x 1055 px')
+ ],
+}.items())
+def test_parse_jpeg(filename, metadata):
+ with open(tutils.test_data.path(filename), 'rb') as f:
+ assert metadata == image_parser.parse_jpeg(f.read())
diff --git a/test/mitmproxy/data/all.jpeg b/test/mitmproxy/data/all.jpeg
new file mode 100644
index 00000000..ea5d8d0f
--- /dev/null
+++ b/test/mitmproxy/data/all.jpeg
Binary files differ
diff --git a/test/mitmproxy/data/image-err1.jpg b/test/mitmproxy/data/image-err1.jpg
deleted file mode 100644
index 1b251e6e..00000000
--- a/test/mitmproxy/data/image-err1.jpg
+++ /dev/null
Binary files differ
diff --git a/test/mitmproxy/data/image_parser/README.md b/test/mitmproxy/data/image_parser/README.md
new file mode 100644
index 00000000..c23409bf
--- /dev/null
+++ b/test/mitmproxy/data/image_parser/README.md
@@ -0,0 +1,17 @@
+# Sources of the images used
+
+## PNG
+
+`aspect.png` - http://pngimg.com/upload/water_PNG3290.png
+All other PNGs are from the [PNGTestSuite](http://www.schaik.com/pngsuite/)
+
+# GIF
+
+All the GIFs are from the Pillow repository [here](https://github.com/python-pillow/Pillow/tree/master/Tests/images)
+
+# JPEG
+
+`app1.jpeg` - https://commons.wikimedia.org/wiki/File:PT05_ubt.jpeg
+`all.jpeg` - https://commons.wikimedia.org/wiki/Category:Dante's_Inferno#/media/File:Bartolomeo_Di_Fruosino_-_Inferno,_from_the_Divine_Comedy_by_Dante_(Folio_1v)_-_WGA01339.jpg
+`comment.jpg` has been taken from [here](https://commons.wikimedia.org/wiki/File:JPEG_example_image.jpg) and has a comment added locally
+All other JPEGs are from the Pillow repository [here](https://github.com/python-pillow/Pillow/tree/master/Tests/images)
diff --git a/test/mitmproxy/data/image_parser/all.jpeg b/test/mitmproxy/data/image_parser/all.jpeg
new file mode 100644
index 00000000..ea5d8d0f
--- /dev/null
+++ b/test/mitmproxy/data/image_parser/all.jpeg
Binary files differ
diff --git a/test/mitmproxy/data/image_parser/app1.jpeg b/test/mitmproxy/data/image_parser/app1.jpeg
new file mode 100644
index 00000000..baa77dfe
--- /dev/null
+++ b/test/mitmproxy/data/image_parser/app1.jpeg
Binary files differ
diff --git a/test/mitmproxy/data/image_parser/comment.jpg b/test/mitmproxy/data/image_parser/comment.jpg
new file mode 100644
index 00000000..74d443dd
--- /dev/null
+++ b/test/mitmproxy/data/image_parser/comment.jpg
Binary files differ
diff --git a/test/mitmproxy/data/image_parser/example.jpg b/test/mitmproxy/data/image_parser/example.jpg
new file mode 100644
index 00000000..415ee4b2
--- /dev/null
+++ b/test/mitmproxy/data/image_parser/example.jpg
Binary files differ