flow.record 3.17.dev5__tar.gz → 3.17.dev7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flow_record-3.17.dev5/flow.record.egg-info → flow_record-3.17.dev7}/PKG-INFO +2 -3
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/fieldtypes/net/ip.py +37 -18
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/version.py +2 -2
- {flow_record-3.17.dev5 → flow_record-3.17.dev7/flow.record.egg-info}/PKG-INFO +2 -3
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow.record.egg-info/requires.txt +0 -3
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/pyproject.toml +1 -2
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_fieldtype_ip.py +15 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tox.ini +1 -1
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/COPYRIGHT +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/LICENSE +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/MANIFEST.in +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/README.md +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/examples/filesystem.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/examples/passivedns.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/examples/records.json +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/examples/tcpconn.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/__init__.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/__init__.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/archive.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/avro.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/broker.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/csvfile.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/duckdb.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/elastic.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/jsonfile.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/line.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/mongo.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/split.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/splunk.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/sqlite.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/stream.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/text.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/adapter/xlsx.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/base.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/exceptions.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/fieldtypes/__init__.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/fieldtypes/credential.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/fieldtypes/net/__init__.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/fieldtypes/net/ipv4.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/fieldtypes/net/tcp.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/fieldtypes/net/udp.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/jsonpacker.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/packer.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/selector.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/stream.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/tools/__init__.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/tools/geoip.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/tools/rdump.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/utils.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow/record/whitelist.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow.record.egg-info/SOURCES.txt +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow.record.egg-info/dependency_links.txt +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow.record.egg-info/entry_points.txt +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/flow.record.egg-info/top_level.txt +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/setup.cfg +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/__init__.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/_utils.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/docs/Makefile +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/docs/conf.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/docs/index.rst +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/selector_explain_example.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/standalone_test.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_adapter_line.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_adapter_text.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_avro.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_avro_adapter.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_compiled_selector.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_csv_adapter.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_deprecations.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_elastic_adapter.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_fieldtypes.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_json_packer.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_json_record_adapter.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_multi_timestamp.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_packer.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_rdump.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_record.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_record_adapter.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_record_descriptor.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_regression.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_selector.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_splunk_adapter.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_sqlite_duckdb_adapter.py +0 -0
- {flow_record-3.17.dev5 → flow_record-3.17.dev7}/tests/test_xlsx_adapter.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flow.record
|
|
3
|
-
Version: 3.17.
|
|
3
|
+
Version: 3.17.dev7
|
|
4
4
|
Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Affero General Public License v3
|
|
@@ -16,12 +16,11 @@ Classifier: Operating System :: OS Independent
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
17
|
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
18
18
|
Classifier: Topic :: Utilities
|
|
19
|
-
Requires-Python: ~=3.
|
|
19
|
+
Requires-Python: ~=3.9
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
License-File: COPYRIGHT
|
|
23
23
|
Requires-Dist: msgpack>=0.5.2
|
|
24
|
-
Requires-Dist: backports.zoneinfo[tzdata]; python_version < "3.9"
|
|
25
24
|
Requires-Dist: tzdata; platform_system == "Windows"
|
|
26
25
|
Provides-Extra: compression
|
|
27
26
|
Requires-Dist: lz4; extra == "compression"
|
|
@@ -1,38 +1,54 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ipaddress import (
|
|
4
|
+
IPv4Address,
|
|
5
|
+
IPv4Network,
|
|
6
|
+
IPv6Address,
|
|
7
|
+
IPv6Network,
|
|
8
|
+
ip_address,
|
|
9
|
+
ip_network,
|
|
10
|
+
)
|
|
11
|
+
from typing import Union
|
|
2
12
|
|
|
3
13
|
from flow.record.base import FieldType
|
|
4
14
|
from flow.record.fieldtypes import defang
|
|
5
15
|
|
|
16
|
+
_IPNetwork = Union[IPv4Network, IPv6Network]
|
|
17
|
+
_IPAddress = Union[IPv4Address, IPv6Address]
|
|
18
|
+
|
|
6
19
|
|
|
7
20
|
class ipaddress(FieldType):
|
|
8
21
|
val = None
|
|
9
22
|
_type = "net.ipaddress"
|
|
10
23
|
|
|
11
|
-
def __init__(self, addr):
|
|
24
|
+
def __init__(self, addr: str | int | bytes):
|
|
12
25
|
self.val = ip_address(addr)
|
|
13
26
|
|
|
14
|
-
def __eq__(self, b):
|
|
27
|
+
def __eq__(self, b: str | int | bytes | _IPAddress) -> bool:
|
|
15
28
|
try:
|
|
16
29
|
return self.val == ip_address(b)
|
|
17
30
|
except ValueError:
|
|
18
31
|
return False
|
|
19
32
|
|
|
20
|
-
def
|
|
33
|
+
def __hash__(self) -> int:
|
|
34
|
+
return hash(self.val)
|
|
35
|
+
|
|
36
|
+
def __str__(self) -> str:
|
|
21
37
|
return str(self.val)
|
|
22
38
|
|
|
23
|
-
def __repr__(self):
|
|
24
|
-
return "{}({!r})"
|
|
39
|
+
def __repr__(self) -> str:
|
|
40
|
+
return f"{self._type}({str(self)!r})"
|
|
25
41
|
|
|
26
|
-
def __format__(self, spec):
|
|
42
|
+
def __format__(self, spec: str) -> str:
|
|
27
43
|
if spec == "defang":
|
|
28
44
|
return defang(str(self))
|
|
29
45
|
return str.__format__(str(self), spec)
|
|
30
46
|
|
|
31
|
-
def _pack(self):
|
|
47
|
+
def _pack(self) -> int:
|
|
32
48
|
return int(self.val)
|
|
33
49
|
|
|
34
50
|
@staticmethod
|
|
35
|
-
def _unpack(data):
|
|
51
|
+
def _unpack(data: int) -> ipaddress:
|
|
36
52
|
return ipaddress(data)
|
|
37
53
|
|
|
38
54
|
|
|
@@ -40,17 +56,20 @@ class ipnetwork(FieldType):
|
|
|
40
56
|
val = None
|
|
41
57
|
_type = "net.ipnetwork"
|
|
42
58
|
|
|
43
|
-
def __init__(self, addr):
|
|
59
|
+
def __init__(self, addr: str | int | bytes):
|
|
44
60
|
self.val = ip_network(addr)
|
|
45
61
|
|
|
46
|
-
def __eq__(self, b):
|
|
62
|
+
def __eq__(self, b: str | int | bytes | _IPNetwork) -> bool:
|
|
47
63
|
try:
|
|
48
64
|
return self.val == ip_network(b)
|
|
49
65
|
except ValueError:
|
|
50
66
|
return False
|
|
51
67
|
|
|
68
|
+
def __hash__(self) -> int:
|
|
69
|
+
return hash(self.val)
|
|
70
|
+
|
|
52
71
|
@staticmethod
|
|
53
|
-
def _is_subnet_of(a, b):
|
|
72
|
+
def _is_subnet_of(a: _IPNetwork, b: _IPNetwork) -> bool:
|
|
54
73
|
try:
|
|
55
74
|
# Always false if one is v4 and the other is v6.
|
|
56
75
|
if a._version != b._version:
|
|
@@ -59,23 +78,23 @@ class ipnetwork(FieldType):
|
|
|
59
78
|
except AttributeError:
|
|
60
79
|
raise TypeError("Unable to test subnet containment " "between {} and {}".format(a, b))
|
|
61
80
|
|
|
62
|
-
def __contains__(self, b):
|
|
81
|
+
def __contains__(self, b: str | int | bytes | _IPAddress) -> bool:
|
|
63
82
|
try:
|
|
64
83
|
return self._is_subnet_of(ip_network(b), self.val)
|
|
65
84
|
except (ValueError, TypeError):
|
|
66
85
|
return False
|
|
67
86
|
|
|
68
|
-
def __str__(self):
|
|
87
|
+
def __str__(self) -> str:
|
|
69
88
|
return str(self.val)
|
|
70
89
|
|
|
71
|
-
def __repr__(self):
|
|
72
|
-
return "{}({!r})"
|
|
90
|
+
def __repr__(self) -> str:
|
|
91
|
+
return f"{self._type}({str(self)!r})"
|
|
73
92
|
|
|
74
|
-
def _pack(self):
|
|
93
|
+
def _pack(self) -> str:
|
|
75
94
|
return self.val.compressed
|
|
76
95
|
|
|
77
96
|
@staticmethod
|
|
78
|
-
def _unpack(data):
|
|
97
|
+
def _unpack(data: str) -> ipnetwork:
|
|
79
98
|
return ipnetwork(data)
|
|
80
99
|
|
|
81
100
|
|
|
@@ -12,5 +12,5 @@ __version__: str
|
|
|
12
12
|
__version_tuple__: VERSION_TUPLE
|
|
13
13
|
version_tuple: VERSION_TUPLE
|
|
14
14
|
|
|
15
|
-
__version__ = version = '3.17.
|
|
16
|
-
__version_tuple__ = version_tuple = (3, 17, '
|
|
15
|
+
__version__ = version = '3.17.dev7'
|
|
16
|
+
__version_tuple__ = version_tuple = (3, 17, 'dev7')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flow.record
|
|
3
|
-
Version: 3.17.
|
|
3
|
+
Version: 3.17.dev7
|
|
4
4
|
Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
|
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
|
6
6
|
License: Affero General Public License v3
|
|
@@ -16,12 +16,11 @@ Classifier: Operating System :: OS Independent
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
17
|
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
18
18
|
Classifier: Topic :: Utilities
|
|
19
|
-
Requires-Python: ~=3.
|
|
19
|
+
Requires-Python: ~=3.9
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
License-File: COPYRIGHT
|
|
23
23
|
Requires-Dist: msgpack>=0.5.2
|
|
24
|
-
Requires-Dist: backports.zoneinfo[tzdata]; python_version < "3.9"
|
|
25
24
|
Requires-Dist: tzdata; platform_system == "Windows"
|
|
26
25
|
Provides-Extra: compression
|
|
27
26
|
Requires-Dist: lz4; extra == "compression"
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
name = "flow.record"
|
|
7
7
|
description = "A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record"
|
|
8
8
|
readme = "README.md"
|
|
9
|
-
requires-python = "~=3.
|
|
9
|
+
requires-python = "~=3.9"
|
|
10
10
|
license.text = "Affero General Public License v3"
|
|
11
11
|
authors = [
|
|
12
12
|
{name = "Dissect Team", email = "dissect@fox-it.com"}
|
|
@@ -24,7 +24,6 @@ classifiers = [
|
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
26
|
"msgpack>=0.5.2",
|
|
27
|
-
"backports.zoneinfo[tzdata]; python_version<'3.9'",
|
|
28
27
|
"tzdata; platform_system=='Windows'",
|
|
29
28
|
]
|
|
30
29
|
dynamic = ["version"]
|
|
@@ -48,12 +48,19 @@ def test_record_ipaddress():
|
|
|
48
48
|
assert TestRecord("0.0.0.0").ip == "0.0.0.0"
|
|
49
49
|
assert TestRecord("192.168.0.1").ip == "192.168.0.1"
|
|
50
50
|
assert TestRecord("255.255.255.255").ip == "255.255.255.255"
|
|
51
|
+
assert hash(TestRecord("192.168.0.1").ip) == hash(net.ipaddress("192.168.0.1"))
|
|
51
52
|
|
|
52
53
|
# ipv6
|
|
53
54
|
assert TestRecord("::1").ip == "::1"
|
|
54
55
|
assert TestRecord("2001:4860:4860::8888").ip == "2001:4860:4860::8888"
|
|
55
56
|
assert TestRecord("2001:4860:4860::4444").ip == "2001:4860:4860::4444"
|
|
56
57
|
|
|
58
|
+
# Test whether it functions in a set
|
|
59
|
+
data = {TestRecord(ip).ip for ip in ["192.168.0.1", "192.168.0.1", "::1", "::1"]}
|
|
60
|
+
assert len(data) == 2
|
|
61
|
+
assert net.ipaddress("::1") in data
|
|
62
|
+
assert net.ipaddress("192.168.0.1") in data
|
|
63
|
+
|
|
57
64
|
# instantiate from different types
|
|
58
65
|
assert TestRecord(1).ip == "0.0.0.1"
|
|
59
66
|
assert TestRecord(0x7F0000FF).ip == "127.0.0.255"
|
|
@@ -90,6 +97,7 @@ def test_record_ipnetwork():
|
|
|
90
97
|
assert "192.168.1.1" not in r.subnet
|
|
91
98
|
assert isinstance(r.subnet, net.ipnetwork)
|
|
92
99
|
assert repr(r.subnet) == "net.ipnetwork('192.168.0.0/24')"
|
|
100
|
+
assert hash(r.subnet) == hash(net.ipnetwork("192.168.0.0/24"))
|
|
93
101
|
|
|
94
102
|
r = TestRecord("192.168.1.1/32")
|
|
95
103
|
assert r.subnet == "192.168.1.1"
|
|
@@ -111,6 +119,13 @@ def test_record_ipnetwork():
|
|
|
111
119
|
assert "64:ff9b::0.0.0.0" in r.subnet
|
|
112
120
|
assert "64:ff9b::255.255.255.255" in r.subnet
|
|
113
121
|
|
|
122
|
+
# Test whether it functions in a set
|
|
123
|
+
data = {TestRecord(x).subnet for x in ["192.168.0.0/24", "192.168.0.0/24", "::1", "::1"]}
|
|
124
|
+
assert len(data) == 2
|
|
125
|
+
assert net.ipnetwork("::1") in data
|
|
126
|
+
assert net.ipnetwork("192.168.0.0/24") in data
|
|
127
|
+
assert "::1" not in data
|
|
128
|
+
|
|
114
129
|
|
|
115
130
|
@pytest.mark.parametrize("PSelector", [Selector, CompiledSelector])
|
|
116
131
|
def test_selector_ipaddress(PSelector):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|