aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--.python-version2
-rw-r--r--.travis.yml19
-rw-r--r--README.rst60
-rwxr-xr-xdev.sh22
-rw-r--r--examples/change_upstream_proxy.py2
-rw-r--r--examples/custom_contentviews.py8
-rw-r--r--examples/dns_spoofing.py3
-rwxr-xr-x[-rw-r--r--]examples/flowbasic19
-rw-r--r--examples/har_extractor.py7
-rw-r--r--examples/mitmproxywrapper.py7
-rw-r--r--examples/pathod/test_setup.py1
-rw-r--r--examples/pathod/test_setupall.py2
-rw-r--r--examples/redirect_requests.py1
-rw-r--r--examples/sslstrip.py22
-rwxr-xr-x[-rw-r--r--]examples/stickycookies8
-rw-r--r--examples/tcp_message.py5
-rw-r--r--examples/tls_passthrough.py2
-rw-r--r--issue_template.md3
-rw-r--r--mitmproxy/cmdline.py31
-rw-r--r--mitmproxy/console/__init__.py70
-rw-r--r--mitmproxy/console/common.py40
-rw-r--r--mitmproxy/console/flowdetailview.py8
-rw-r--r--mitmproxy/console/flowlist.py11
-rw-r--r--mitmproxy/console/flowview.py36
-rw-r--r--mitmproxy/console/grideditor.py17
-rw-r--r--mitmproxy/console/help.py8
-rw-r--r--mitmproxy/console/options.py11
-rw-r--r--mitmproxy/console/palettepicker.py7
-rw-r--r--mitmproxy/console/palettes.py1
-rw-r--r--mitmproxy/console/pathedit.py2
-rw-r--r--mitmproxy/console/searchable.py4
-rw-r--r--mitmproxy/console/select.py7
-rw-r--r--mitmproxy/console/signals.py2
-rw-r--r--mitmproxy/console/statusbar.py11
-rw-r--r--mitmproxy/console/tabs.py2
-rw-r--r--mitmproxy/console/window.py5
-rw-r--r--mitmproxy/contentviews.py69
-rw-r--r--mitmproxy/controller.py164
-rw-r--r--mitmproxy/dump.py63
-rw-r--r--mitmproxy/exceptions.py13
-rw-r--r--mitmproxy/filt.py5
-rw-r--r--mitmproxy/flow.py1242
-rw-r--r--mitmproxy/flow/__init__.py21
-rw-r--r--mitmproxy/flow/export.py (renamed from mitmproxy/flow_export.py)37
-rw-r--r--mitmproxy/flow/io.py86
-rw-r--r--mitmproxy/flow/io_compat.py (renamed from mitmproxy/flow_format_compat.py)7
-rw-r--r--mitmproxy/flow/master.py544
-rw-r--r--mitmproxy/flow/modules.py358
-rw-r--r--mitmproxy/flow/state.py269
-rw-r--r--mitmproxy/main.py33
-rw-r--r--mitmproxy/models/__init__.py8
-rw-r--r--mitmproxy/models/connections.py14
-rw-r--r--mitmproxy/models/flow.py16
-rw-r--r--mitmproxy/models/http.py21
-rw-r--r--mitmproxy/models/tcp.py11
-rw-r--r--mitmproxy/onboarding/app.py11
-rw-r--r--mitmproxy/platform/osx.py1
-rw-r--r--mitmproxy/platform/windows.py61
-rw-r--r--mitmproxy/protocol/__init__.py9
-rw-r--r--mitmproxy/protocol/base.py23
-rw-r--r--mitmproxy/protocol/http.py77
-rw-r--r--mitmproxy/protocol/http1.py16
-rw-r--r--mitmproxy/protocol/http2.py101
-rw-r--r--mitmproxy/protocol/http_replay.py39
-rw-r--r--mitmproxy/protocol/rawtcp.py30
-rw-r--r--mitmproxy/protocol/tls.py86
-rw-r--r--mitmproxy/proxy/__init__.py4
-rw-r--r--mitmproxy/proxy/config.py37
-rw-r--r--mitmproxy/proxy/modes/__init__.py3
-rw-r--r--mitmproxy/proxy/modes/http_proxy.py8
-rw-r--r--mitmproxy/proxy/modes/reverse_proxy.py6
-rw-r--r--mitmproxy/proxy/modes/socks_proxy.py18
-rw-r--r--mitmproxy/proxy/modes/transparent_proxy.py12
-rw-r--r--mitmproxy/proxy/root_context.py59
-rw-r--r--mitmproxy/proxy/server.py57
-rw-r--r--mitmproxy/script/__init__.py4
-rw-r--r--mitmproxy/script/concurrent.py1
-rw-r--r--mitmproxy/script/reloader.py4
-rw-r--r--mitmproxy/script/script.py25
-rw-r--r--mitmproxy/script/script_context.py3
-rw-r--r--mitmproxy/stateobject.py9
-rw-r--r--mitmproxy/tnetstring.py5
-rw-r--r--mitmproxy/utils.py86
-rw-r--r--mitmproxy/version.py9
-rw-r--r--mitmproxy/web/__init__.py36
-rw-r--r--mitmproxy/web/app.py54
-rw-r--r--mitmproxy/web/static/app.css45
-rw-r--r--mitmproxy/web/static/app.js491
-rw-r--r--mitmproxy/web/static/vendor.js878
-rw-r--r--mitmproxy/webfonts/fontawesome-webfont.eotbin56006 -> 0 bytes
-rw-r--r--mitmproxy/webfonts/fontawesome-webfont.svg520
-rw-r--r--mitmproxy/webfonts/fontawesome-webfont.ttfbin112160 -> 0 bytes
-rw-r--r--mitmproxy/webfonts/fontawesome-webfont.woffbin65452 -> 0 bytes
-rw-r--r--netlib/basetypes.py34
-rw-r--r--netlib/certutils.py4
-rw-r--r--netlib/http/__init__.py14
-rw-r--r--netlib/http/authentication.py6
-rw-r--r--netlib/http/cookies.py14
-rw-r--r--netlib/http/headers.py56
-rw-r--r--netlib/http/http1/assemble.py10
-rw-r--r--netlib/http/http1/read.py91
-rw-r--r--netlib/http/http2/__init__.py4
-rw-r--r--netlib/http/http2/connections.py100
-rw-r--r--netlib/http/http2/framereader.py21
-rw-r--r--netlib/http/message.py33
-rw-r--r--netlib/http/multipart.py32
-rw-r--r--netlib/http/request.py80
-rw-r--r--netlib/http/response.py32
-rw-r--r--netlib/http/url.py96
-rw-r--r--netlib/human.py50
-rw-r--r--netlib/multidict.py48
-rw-r--r--netlib/odict.py8
-rw-r--r--netlib/socks.py5
-rw-r--r--netlib/strutils.py154
-rw-r--r--netlib/tcp.py103
-rw-r--r--netlib/tutils.py14
-rw-r--r--netlib/utils.py341
-rw-r--r--netlib/version_check.py1
-rw-r--r--netlib/websockets/__init__.py13
-rw-r--r--netlib/websockets/frame.py14
-rw-r--r--netlib/websockets/protocol.py35
-rw-r--r--netlib/wsgi.py31
-rw-r--r--pathod/app.py9
-rw-r--r--pathod/language/__init__.py15
-rw-r--r--pathod/language/base.py25
-rw-r--r--pathod/language/exceptions.py1
-rw-r--r--pathod/language/generators.py57
-rw-r--r--pathod/language/http.py1
-rw-r--r--pathod/language/http2.py7
-rw-r--r--pathod/language/websockets.py1
-rw-r--r--pathod/language/writer.py1
-rw-r--r--pathod/log.py12
-rw-r--r--pathod/pathoc.py19
-rw-r--r--pathod/pathod.py25
-rw-r--r--pathod/pathod_cmdline.py15
-rw-r--r--pathod/protocols/__init__.py6
-rw-r--r--pathod/protocols/http.py6
-rw-r--r--pathod/protocols/http2.py3
-rw-r--r--pathod/protocols/websockets.py2
-rw-r--r--pathod/test.py49
-rw-r--r--pathod/utils.py50
-rw-r--r--pathod/version.py7
-rw-r--r--setup.cfg15
-rw-r--r--setup.py9
-rw-r--r--test/mitmproxy/console/__init__.py0
-rw-r--r--test/mitmproxy/console/test_common.py (renamed from test/mitmproxy/test_console_common.py)9
-rw-r--r--test/mitmproxy/console/test_console.py (renamed from test/mitmproxy/test_console.py)2
-rw-r--r--test/mitmproxy/console/test_help.py (renamed from test/mitmproxy/test_console_help.py)7
-rw-r--r--test/mitmproxy/console/test_palettes.py (renamed from test/mitmproxy/test_console_palettes.py)6
-rw-r--r--test/mitmproxy/console/test_pathedit.py (renamed from test/mitmproxy/test_console_pathedit.py)2
-rw-r--r--test/mitmproxy/data/har_extractor.har10
-rw-r--r--test/mitmproxy/data/scripts/a.py (renamed from test/mitmproxy/scripts/a.py)0
-rw-r--r--test/mitmproxy/data/scripts/a_helper.py (renamed from test/mitmproxy/scripts/a_helper.py)0
-rw-r--r--test/mitmproxy/data/scripts/all.py (renamed from test/mitmproxy/scripts/all.py)0
-rw-r--r--test/mitmproxy/data/scripts/concurrent_decorator.py (renamed from test/mitmproxy/scripts/concurrent_decorator.py)1
-rw-r--r--test/mitmproxy/data/scripts/concurrent_decorator_err.py (renamed from test/mitmproxy/scripts/concurrent_decorator_err.py)0
-rw-r--r--test/mitmproxy/data/scripts/duplicate_flow.py (renamed from test/mitmproxy/scripts/duplicate_flow.py)0
-rw-r--r--test/mitmproxy/data/scripts/loaderr.py (renamed from test/mitmproxy/scripts/loaderr.py)0
-rw-r--r--test/mitmproxy/data/scripts/reqerr.py (renamed from test/mitmproxy/scripts/reqerr.py)0
-rw-r--r--test/mitmproxy/data/scripts/starterr.py (renamed from test/mitmproxy/scripts/starterr.py)0
-rw-r--r--test/mitmproxy/data/scripts/stream_modify.py (renamed from test/mitmproxy/scripts/stream_modify.py)0
-rw-r--r--test/mitmproxy/data/scripts/syntaxerr.py (renamed from test/mitmproxy/scripts/syntaxerr.py)0
-rw-r--r--test/mitmproxy/data/scripts/tcp_stream_modify.py (renamed from test/mitmproxy/scripts/tcp_stream_modify.py)0
-rw-r--r--test/mitmproxy/data/scripts/unloaderr.py (renamed from test/mitmproxy/scripts/unloaderr.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/locust_get.py (renamed from test/mitmproxy/test_flow_export/locust_get.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/locust_patch.py (renamed from test/mitmproxy/test_flow_export/locust_patch.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/locust_post.py (renamed from test/mitmproxy/test_flow_export/locust_post.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/locust_task_get.py (renamed from test/mitmproxy/test_flow_export/locust_task_get.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/locust_task_patch.py (renamed from test/mitmproxy/test_flow_export/locust_task_patch.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/locust_task_post.py (renamed from test/mitmproxy/test_flow_export/locust_task_post.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/python_get.py (renamed from test/mitmproxy/test_flow_export/python_get.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/python_patch.py (renamed from test/mitmproxy/test_flow_export/python_patch.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/python_post.py (renamed from test/mitmproxy/test_flow_export/python_post.py)0
-rw-r--r--test/mitmproxy/data/test_flow_export/python_post_json.py (renamed from test/mitmproxy/test_flow_export/python_post_json.py)6
-rw-r--r--test/mitmproxy/script/test_concurrent.py4
-rw-r--r--test/mitmproxy/script/test_script.py14
-rw-r--r--test/mitmproxy/test_app.py11
-rw-r--r--test/mitmproxy/test_contentview.py6
-rw-r--r--test/mitmproxy/test_controller.py38
-rw-r--r--test/mitmproxy/test_dump.py24
-rw-r--r--test/mitmproxy/test_examples.py2
-rw-r--r--test/mitmproxy/test_flow.py115
-rw-r--r--test/mitmproxy/test_flow_export.py56
-rw-r--r--test/mitmproxy/test_flow_format_compat.py3
-rw-r--r--test/mitmproxy/test_protocol_http2.py61
-rw-r--r--test/mitmproxy/test_proxy.py9
-rw-r--r--test/mitmproxy/test_script.py4
-rw-r--r--test/mitmproxy/test_server.py70
-rw-r--r--test/mitmproxy/test_utils.py44
-rw-r--r--test/mitmproxy/tools/memoryleak.py4
-rw-r--r--test/mitmproxy/tservers.py16
-rw-r--r--test/mitmproxy/tutils.py3
-rw-r--r--test/netlib/data/verificationcerts/generate.py2
-rw-r--r--test/netlib/http/http1/test_assemble.py10
-rw-r--r--test/netlib/http/http1/test_read.py18
-rw-r--r--test/netlib/http/http2/test_connections.py37
-rw-r--r--test/netlib/http/test_authentication.py2
-rw-r--r--test/netlib/http/test_cookies.py2
-rw-r--r--test/netlib/http/test_headers.py11
-rw-r--r--test/netlib/http/test_message.py4
-rw-r--r--test/netlib/http/test_multipart.py24
-rw-r--r--test/netlib/http/test_request.py2
-rw-r--r--test/netlib/http/test_response.py9
-rw-r--r--test/netlib/http/test_url.py66
-rw-r--r--test/netlib/test_basetypes.py28
-rw-r--r--test/netlib/test_human.py36
-rw-r--r--test/netlib/test_multidict.py2
-rw-r--r--test/netlib/test_socks.py1
-rw-r--r--test/netlib/test_strutils.py63
-rw-r--r--test/netlib/test_tcp.py2
-rw-r--r--test/netlib/test_utils.py179
-rw-r--r--test/netlib/test_version_check.py2
-rw-r--r--test/netlib/test_wsgi.py2
-rw-r--r--test/netlib/websockets/test_websockets.py13
-rw-r--r--test/pathod/test_app.py6
-rw-r--r--test/pathod/test_language_actions.py4
-rw-r--r--test/pathod/test_language_base.py28
-rw-r--r--test/pathod/test_language_generators.py13
-rw-r--r--test/pathod/test_language_http2.py3
-rw-r--r--test/pathod/test_log.py6
-rw-r--r--test/pathod/test_pathoc.py10
-rw-r--r--test/pathod/test_pathoc_cmdline.py10
-rw-r--r--test/pathod/test_pathod.py34
-rw-r--r--test/pathod/test_test.py4
-rw-r--r--test/pathod/test_utils.py19
-rw-r--r--test/pathod/tutils.py66
-rw-r--r--tox.ini15
-rw-r--r--web/.babelrc2
-rw-r--r--web/README2
-rw-r--r--web/package.json1
-rw-r--r--web/src/css/header.less18
-rw-r--r--web/src/js/components/common.js84
-rw-r--r--web/src/js/components/eventlog.js4
-rw-r--r--web/src/js/components/flowview/contentview.js64
-rw-r--r--web/src/js/components/flowview/index.js6
-rw-r--r--web/src/js/components/footer.js32
-rw-r--r--web/src/js/components/header.js133
-rw-r--r--web/src/js/components/mainview.js13
-rw-r--r--web/src/js/components/proxyapp.js57
240 files changed, 4965 insertions, 4770 deletions
diff --git a/.gitignore b/.gitignore
index 79555e82..01095908 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
.DS_Store
MANIFEST
*/tmp
-/venv
+/venv*
*.py[cdo]
*.swp
*.swo
@@ -9,6 +9,7 @@ MANIFEST
.coverage
.idea
.cache/
+.tox/
build/
# UI
@@ -16,3 +17,5 @@ build/
node_modules
bower_components
*.map
+sslkeylogfile.log
+.tox/
diff --git a/.python-version b/.python-version
new file mode 100644
index 00000000..2339c8bf
--- /dev/null
+++ b/.python-version
@@ -0,0 +1,2 @@
+2.7.11
+3.5.1
diff --git a/.travis.yml b/.travis.yml
index 7d3fbee8..5ec8b3bd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,9 +22,9 @@ matrix:
git:
depth: 9999999
- python: 3.5
- env: SCOPE="netlib ./test/mitmproxy/script"
+ env: SCOPE="netlib ./test/mitmproxy/script ./test/pathod/test_utils.py ./test/pathod/test_log.py ./test/pathod/test_language_generators.py"
- python: 3.5
- env: SCOPE="netlib ./test/mitmproxy/script" NO_ALPN=1
+ env: SCOPE="netlib ./test/mitmproxy/script ./test/pathod/test_utils.py ./test/pathod/test_log.py ./test/pathod/test_language_generators.py" NO_ALPN=1
- python: 2.7
env: DOCS=1
script: 'cd docs && make html'
@@ -46,6 +46,7 @@ install:
before_script:
- "openssl version -a"
- "python -c \"from OpenSSL import SSL; print(SSL.SSLeay_version(SSL.SSLEAY_VERSION))\""
+ - "flake8 --count mitmproxy netlib pathod examples test"
script:
- "py.test --timeout 60 --cov netlib --cov mitmproxy --cov pathod ./test/$SCOPE"
@@ -61,16 +62,12 @@ after_success:
fi
notifications:
- irc:
- channels:
- - "irc.oftc.net#mitmproxy"
- on_success: change
- on_failure: always
slack:
- rooms:
- - mitmproxy:YaDGC9Gt9TEM7o8zkC2OLNsu#ci
- on_success: always
- on_failure: always
+ -rooms:
+ mitmproxy:YaDGC9Gt9TEM7o8zkC2OLNsu
+ on_success: change
+ on_failure: change
+ on_start: never
cache:
directories:
diff --git a/README.rst b/README.rst
index 1228f318..4f84effd 100644
--- a/README.rst
+++ b/README.rst
@@ -1,21 +1,26 @@
mitmproxy
^^^^^^^^^
-|travis| |coveralls| |downloads| |latest_release| |python_versions|
+|travis| |coveralls| |latest_release| |python_versions|
-This repository contains the **mitmproxy** and **pathod** projects, as well as their shared networking library, **netlib**.
+This repository contains the **mitmproxy** and **pathod** projects, as well as
+their shared networking library, **netlib**.
-``mitmproxy`` is an interactive, SSL-capable intercepting proxy with a console interface.
+``mitmproxy`` is an interactive, SSL-capable intercepting proxy with a console
+interface.
``mitmdump`` is the command-line version of mitmproxy. Think tcpdump for HTTP.
-``pathoc`` and ``pathod`` are perverse HTTP client and server applications designed to let you craft almost any conceivable HTTP request, including ones that creatively violate the standards.
+``pathoc`` and ``pathod`` are perverse HTTP client and server applications
+designed to let you craft almost any conceivable HTTP request, including ones
+that creatively violate the standards.
Documentation & Help
--------------------
-Documentation, tutorials and precompiled binaries can be found on the mitmproxy and pathod websites.
+Documentation, tutorials and precompiled binaries can be found on the mitmproxy
+and pathod websites.
|mitmproxy_site| |pathod_site|
@@ -28,12 +33,19 @@ You can join our developer chat on Slack.
|slack|
+Installation
+------------
+
+The installation instructions are `here <http://docs.mitmproxy.org/en/stable/install.html>`_.
+If you want to contribute changes, keep on reading.
+
+
Hacking
-------
To get started hacking on mitmproxy, make sure you have Python_ 2.7.x. with
-virtualenv_ installed (you can find installation instructions for virtualenv here_).
-Then do the following:
+virtualenv_ installed (you can find installation instructions for virtualenv
+here_). Then do the following:
.. code-block:: text
@@ -42,10 +54,11 @@ Then do the following:
./dev.sh
-The *dev* script will create a virtualenv environment in a directory called "venv",
-and install all mandatory and optional dependencies into it.
-The primary mitmproxy components - mitmproxy, netlib and pathod - are installed as "editable",
-so any changes to the source in the repository will be reflected live in the virtualenv.
+The *dev* script will create a virtualenv environment in a directory called
+"venv", and install all mandatory and optional dependencies into it. The
+primary mitmproxy components - mitmproxy, netlib and pathod - are installed as
+"editable", so any changes to the source in the repository will be reflected
+live in the virtualenv.
To confirm that you're up and running, activate the virtualenv, and run the
mitmproxy test suite:
@@ -56,9 +69,9 @@ mitmproxy test suite:
py.test
Note that the main executables for the project - ``mitmdump``, ``mitmproxy``,
-``mitmweb``, ``pathod``, and ``pathoc`` - are all created within the virtualenv. After activating the
-virtualenv, they will be on your $PATH, and you can run them like any other
-command:
+``mitmweb``, ``pathod``, and ``pathoc`` - are all created within the
+virtualenv. After activating the virtualenv, they will be on your $PATH, and
+you can run them like any other command:
.. code-block:: text
@@ -85,9 +98,9 @@ suite. The project tries to maintain 100% test coverage.
Documentation
-------------
-The mitmproxy documentation is build using Sphinx_, which is installed automatically if you set up a development
-environment as described above.
-After installation, you can render the documentation like this:
+The mitmproxy documentation is build using Sphinx_, which is installed
+automatically if you set up a development environment as described above. After
+installation, you can render the documentation like this:
.. code-block:: text
@@ -99,6 +112,13 @@ After installation, you can render the documentation like this:
The last command invokes `sphinx-autobuild`_, which watches the Sphinx directory and rebuilds
the documentation when a change is detected.
+Style
+-----
+
+Keeping to a consistent code style throughout the project makes it easier to
+contribute and collaborate. Please stick to the guidelines in
+`PEP8`_ and the `Google Style Guide`_ unless there's a very
+good reason not to.
.. |mitmproxy_site| image:: https://shields.mitmproxy.org/api/https%3A%2F%2F-mitmproxy.org-blue.svg
@@ -125,10 +145,6 @@ the documentation when a change is detected.
:target: https://coveralls.io/r/mitmproxy/mitmproxy
:alt: Coverage Status
-.. |downloads| image:: https://shields.mitmproxy.org/pypi/dm/mitmproxy.svg?color=orange
- :target: https://pypi.python.org/pypi/mitmproxy
- :alt: Downloads
-
.. |latest_release| image:: https://shields.mitmproxy.org/pypi/v/mitmproxy.svg
:target: https://pypi.python.org/pypi/mitmproxy
:alt: Latest Version
@@ -145,3 +161,5 @@ the documentation when a change is detected.
.. _Sphinx: http://sphinx-doc.org/
.. _sphinx-autobuild: https://pypi.python.org/pypi/sphinx-autobuild
.. _issue_tracker: https://github.com/mitmproxy/mitmproxy/issues
+.. _PEP8: https://www.python.org/dev/peps/pep-0008
+.. _Google Style Guide: https://google.github.io/styleguide/pyguide.html
diff --git a/dev.sh b/dev.sh
index a9c27248..889ffecb 100755
--- a/dev.sh
+++ b/dev.sh
@@ -1,13 +1,17 @@
-#!/bin/bash
+#!/bin/sh
set -e
-VENV=./venv
+set -x
-python -m virtualenv $VENV --always-copy
-. $VENV/bin/activate
-pip install -U pip setuptools
-pip install -r requirements.txt
+PYVERSION=$1
+VENV="venv$1"
+
+echo "Creating dev environment in $VENV using Python $PYVERSION"
+
+python$PYVERSION -m virtualenv "$VENV" --always-copy
+. "$VENV/bin/activate"
+pip$PYVERSION install -U pip setuptools
+pip$PYVERSION install -r requirements.txt
echo ""
-echo "* Created virtualenv environment in $VENV."
-echo "* Installed all dependencies into the virtualenv."
-echo "* You can now activate the virtualenv: \`. $VENV/bin/activate\`"
+echo "* Virtualenv created in $VENV and all dependencies installed."
+echo "* You can now activate the $(python --version) virtualenv with this command: \`. $VENV/bin/activate\`"
diff --git a/examples/change_upstream_proxy.py b/examples/change_upstream_proxy.py
index 9c454897..34a6eece 100644
--- a/examples/change_upstream_proxy.py
+++ b/examples/change_upstream_proxy.py
@@ -21,4 +21,4 @@ def request(context, flow):
return
address = proxy_address(flow)
if flow.live:
- flow.live.change_upstream_proxy_server(address) \ No newline at end of file
+ flow.live.change_upstream_proxy_server(address)
diff --git a/examples/custom_contentviews.py b/examples/custom_contentviews.py
index f3b7317f..034f356c 100644
--- a/examples/custom_contentviews.py
+++ b/examples/custom_contentviews.py
@@ -1,7 +1,8 @@
import string
import lxml.html
import lxml.etree
-from mitmproxy import utils, contentviews
+from mitmproxy import contentviews
+from netlib import strutils
class ViewPigLatin(contentviews.View):
@@ -10,7 +11,7 @@ class ViewPigLatin(contentviews.View):
content_types = ["text/html"]
def __call__(self, data, **metadata):
- if utils.isXML(data):
+ if strutils.isXML(data):
parser = lxml.etree.HTMLParser(
strip_cdata=True,
remove_blank_text=True
@@ -23,7 +24,8 @@ class ViewPigLatin(contentviews.View):
ret = ''
for word in words:
idx = -1
- while word[idx] in string.punctuation and (idx * -1) != len(word): idx -= 1
+ while word[idx] in string.punctuation and (idx * -1) != len(word):
+ idx -= 1
if word[0].lower() in 'aeiou':
if idx == -1:
ret += word[0:] + "hay"
diff --git a/examples/dns_spoofing.py b/examples/dns_spoofing.py
index 7eb79695..8d715f33 100644
--- a/examples/dns_spoofing.py
+++ b/examples/dns_spoofing.py
@@ -22,7 +22,6 @@ Usage:
"""
import re
-
# This regex extracts splits the host header into host and port.
# Handles the edge case of IPv6 addresses containing colons.
# https://bugzilla.mozilla.org/show_bug.cgi?id=45891
@@ -47,4 +46,4 @@ def request(context, flow):
port = int(m.group("port"))
flow.request.host = sni or host_header
- flow.request.port = port \ No newline at end of file
+ flow.request.port = port
diff --git a/examples/flowbasic b/examples/flowbasic
index 4a87b86a..74af4e08 100644..100755
--- a/examples/flowbasic
+++ b/examples/flowbasic
@@ -8,7 +8,7 @@
Note that request and response messages are not automatically replied to,
so we need to implement handlers to do this.
"""
-from mitmproxy import flow
+from mitmproxy import flow, controller
from mitmproxy.proxy import ProxyServer, ProxyConfig
@@ -19,18 +19,15 @@ class MyMaster(flow.FlowMaster):
except KeyboardInterrupt:
self.shutdown()
- def handle_request(self, f):
- f = flow.FlowMaster.handle_request(self, f)
- if f:
- f.reply()
- return f
+ @controller.handler
+ def request(self, f):
+ f = flow.FlowMaster.request(self, f)
+ print(f)
- def handle_response(self, f):
- f = flow.FlowMaster.handle_response(self, f)
- if f:
- f.reply()
+ @controller.handler
+ def response(self, f):
+ f = flow.FlowMaster.response(self, f)
print(f)
- return f
config = ProxyConfig(
diff --git a/examples/har_extractor.py b/examples/har_extractor.py
index 371e2282..6806989d 100644
--- a/examples/har_extractor.py
+++ b/examples/har_extractor.py
@@ -162,8 +162,11 @@ def response(context, flow):
# If the current url is in the page list of context.HARLog or
# does not have a referrer, we add it as a new pages object.
- if (flow.request.url in context.HARLog.get_page_list() or
- flow.request.headers.get('Referer') is None):
+ is_new_page = (
+ flow.request.url in context.HARLog.get_page_list() or
+ flow.request.headers.get('Referer') is None
+ )
+ if is_new_page:
page_id = context.HARLog.create_page_id()
context.HARLog.add(
HAR.pages({
diff --git a/examples/mitmproxywrapper.py b/examples/mitmproxywrapper.py
index 7ea10715..6841d05f 100644
--- a/examples/mitmproxywrapper.py
+++ b/examples/mitmproxywrapper.py
@@ -16,7 +16,6 @@ import sys
class Wrapper(object):
-
def __init__(self, port, extra_arguments=None):
self.port = port
self.extra_arguments = extra_arguments
@@ -142,7 +141,7 @@ class Wrapper(object):
'--toggle',
action='store_true',
help='just toggle the proxy configuration')
-# parser.add_argument('--honeyproxy', action='store_true', help='run honeyproxy instead of mitmproxy')
+ # parser.add_argument('--honeyproxy', action='store_true', help='run honeyproxy instead of mitmproxy')
parser.add_argument(
'-p',
'--port',
@@ -155,8 +154,8 @@ class Wrapper(object):
if args.toggle:
wrapper.toggle_proxy()
-# elif args.honeyproxy:
-# wrapper.wrap_honeyproxy()
+ # elif args.honeyproxy:
+ # wrapper.wrap_honeyproxy()
else:
wrapper.wrap_mitmproxy()
diff --git a/examples/pathod/test_setup.py b/examples/pathod/test_setup.py
index 5dbc456d..32fcb214 100644
--- a/examples/pathod/test_setup.py
+++ b/examples/pathod/test_setup.py
@@ -3,7 +3,6 @@ from pathod import test
class Test:
-
"""
Testing the requests module with
a pathod instance started for
diff --git a/examples/pathod/test_setupall.py b/examples/pathod/test_setupall.py
index cb84b7b2..cc0ec2e4 100644
--- a/examples/pathod/test_setupall.py
+++ b/examples/pathod/test_setupall.py
@@ -3,12 +3,12 @@ from pathod import test
class Test:
-
"""
Testing the requests module with
a single pathod instance started
for the test suite.
"""
+
@classmethod
def setup_class(cls):
cls.d = test.Daemon()
diff --git a/examples/redirect_requests.py b/examples/redirect_requests.py
index c0a0ccba..3ff8f9e4 100644
--- a/examples/redirect_requests.py
+++ b/examples/redirect_requests.py
@@ -4,6 +4,7 @@ This example shows two ways to redirect flows to other destinations.
from mitmproxy.models import HTTPResponse
from netlib.http import Headers
+
def request(context, flow):
# pretty_host takes the "Host" header of the request into account,
# which is useful in transparent mode where we usually only have the IP
diff --git a/examples/sslstrip.py b/examples/sslstrip.py
index 369427a2..1bc89946 100644
--- a/examples/sslstrip.py
+++ b/examples/sslstrip.py
@@ -2,39 +2,39 @@ from netlib.http import decoded
import re
from six.moves import urllib
-def start(context, argv) :
- #set of SSL/TLS capable hosts
+def start(context, argv):
+ # set of SSL/TLS capable hosts
context.secure_hosts = set()
-def request(context, flow) :
+def request(context, flow):
flow.request.headers.pop('If-Modified-Since', None)
flow.request.headers.pop('Cache-Control', None)
- #proxy connections to SSL-enabled hosts
- if flow.request.pretty_host in context.secure_hosts :
+ # proxy connections to SSL-enabled hosts
+ if flow.request.pretty_host in context.secure_hosts:
flow.request.scheme = 'https'
flow.request.port = 443
-def response(context, flow) :
- with decoded(flow.response) :
+def response(context, flow):
+ with decoded(flow.response):
flow.request.headers.pop('Strict-Transport-Security', None)
flow.request.headers.pop('Public-Key-Pins', None)
- #strip links in response body
+ # strip links in response body
flow.response.content = flow.response.content.replace('https://', 'http://')
- #strip links in 'Location' header
- if flow.response.headers.get('Location','').startswith('https://'):
+ # strip links in 'Location' header
+ if flow.response.headers.get('Location', '').startswith('https://'):
location = flow.response.headers['Location']
hostname = urllib.parse.urlparse(location).hostname
if hostname:
context.secure_hosts.add(hostname)
flow.response.headers['Location'] = location.replace('https://', 'http://', 1)
- #strip secure flag from 'Set-Cookie' headers
+ # strip secure flag from 'Set-Cookie' headers
cookies = flow.response.headers.get_all('Set-Cookie')
cookies = [re.sub(r';\s*secure\s*', '', s) for s in cookies]
flow.response.headers.set_all('Set-Cookie', cookies)
diff --git a/examples/stickycookies b/examples/stickycookies
index 8f11de8d..43e5371d 100644..100755
--- a/examples/stickycookies
+++ b/examples/stickycookies
@@ -21,19 +21,19 @@ class StickyMaster(controller.Master):
except KeyboardInterrupt:
self.shutdown()
- def handle_request(self, flow):
+ @controller.handler
+ def request(self, flow):
hid = (flow.request.host, flow.request.port)
if "cookie" in flow.request.headers:
self.stickyhosts[hid] = flow.request.headers.get_all("cookie")
elif hid in self.stickyhosts:
flow.request.headers.set_all("cookie", self.stickyhosts[hid])
- flow.reply()
- def handle_response(self, flow):
+ @controller.handler
+ def response(self, flow):
hid = (flow.request.host, flow.request.port)
if "set-cookie" in flow.response.headers:
self.stickyhosts[hid] = flow.response.headers.get_all("set-cookie")
- flow.reply()
config = proxy.ProxyConfig(port=8080)
diff --git a/examples/tcp_message.py b/examples/tcp_message.py
index c63368e4..2c210618 100644
--- a/examples/tcp_message.py
+++ b/examples/tcp_message.py
@@ -8,7 +8,8 @@ tcp_message Inline Script Hook API Demonstration
example cmdline invocation:
mitmdump -T --host --tcp ".*" -q -s examples/tcp_message.py
'''
-from netlib.utils import clean_bin
+from netlib import strutils
+
def tcp_message(ctx, tcp_msg):
modified_msg = tcp_msg.message.replace("foo", "bar")
@@ -21,4 +22,4 @@ def tcp_message(ctx, tcp_msg):
"client" if tcp_msg.sender == tcp_msg.client_conn else "server",
tcp_msg.sender.address,
"server" if tcp_msg.receiver == tcp_msg.server_conn else "client",
- tcp_msg.receiver.address, clean_bin(tcp_msg.message)))
+ tcp_msg.receiver.address, strutils.clean_bin(tcp_msg.message)))
diff --git a/examples/tls_passthrough.py b/examples/tls_passthrough.py
index 8c8fa4eb..23afe3ff 100644
--- a/examples/tls_passthrough.py
+++ b/examples/tls_passthrough.py
@@ -40,6 +40,7 @@ class _TlsStrategy(object):
"""
Abstract base class for interception strategies.
"""
+
def __init__(self):
# A server_address -> interception results mapping
self.history = collections.defaultdict(lambda: collections.deque(maxlen=200))
@@ -78,6 +79,7 @@ class ProbabilisticStrategy(_TlsStrategy):
"""
Fixed probability that we intercept a given connection.
"""
+
def __init__(self, p):
self.p = p
super(ProbabilisticStrategy, self).__init__()
diff --git a/issue_template.md b/issue_template.md
index 08d390e4..79389380 100644
--- a/issue_template.md
+++ b/issue_template.md
@@ -17,3 +17,6 @@
Mitmproxy Version:
Operating System:
+
+
+<!-- Please use the mitmproxy forums (https://discourse.mitmproxy.org/) for support/how-to questions. Thanks! :) -->
diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py
index 8476f6f3..a873143d 100644
--- a/mitmproxy/cmdline.py
+++ b/mitmproxy/cmdline.py
@@ -1,14 +1,17 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function, division
+
+import base64
import os
import re
-import base64
import configargparse
-from netlib.tcp import Address, sslversion_choices
-import netlib.utils
-from . import filt, utils, version
-from .proxy import config
+from mitmproxy import filt
+from mitmproxy import version
+from mitmproxy.proxy import config
+from netlib import human
+from netlib import tcp
+from netlib.http import url
APP_HOST = "mitm.it"
APP_PORT = 80
@@ -103,17 +106,17 @@ def parse_setheader(s):
return _parse_hook(s)
-def parse_server_spec(url):
+def parse_server_spec(spec):
try:
- p = netlib.utils.parse_url(url)
+ p = url.parse(spec)
if p[0] not in ("http", "https"):
raise ValueError()
except ValueError:
raise configargparse.ArgumentTypeError(
- "Invalid server specification: %s" % url
+ "Invalid server specification: %s" % spec
)
- address = Address(p[1:3])
+ address = tcp.Address(p[1:3])
scheme = p[0].lower()
return config.ServerSpec(scheme, address)
@@ -135,7 +138,9 @@ def get_common_options(options):
if options.stickyauth_filt:
stickyauth = options.stickyauth_filt
- stream_large_bodies = utils.parse_size(options.stream_large_bodies)
+ stream_large_bodies = options.stream_large_bodies
+ if stream_large_bodies:
+ stream_large_bodies = human.parse_size(stream_large_bodies)
reps = []
for i in options.replace:
@@ -474,14 +479,14 @@ def proxy_ssl_options(parser):
group.add_argument(
"--ssl-version-client", dest="ssl_version_client",
default="secure", action="store",
- choices=sslversion_choices.keys(),
+ choices=tcp.sslversion_choices.keys(),
help="Set supported SSL/TLS versions for client connections. "
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+."
)
group.add_argument(
"--ssl-version-server", dest="ssl_version_server",
default="secure", action="store",
- choices=sslversion_choices.keys(),
+ choices=tcp.sslversion_choices.keys(),
help="Set supported SSL/TLS versions for server connections. "
"SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+."
)
diff --git a/mitmproxy/console/__init__.py b/mitmproxy/console/__init__.py
index 4541a6af..63692ec0 100644
--- a/mitmproxy/console/__init__.py
+++ b/mitmproxy/console/__init__.py
@@ -1,8 +1,7 @@
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, division
import mailcap
import mimetypes
-import tempfile
import os
import os.path
import shlex
@@ -10,16 +9,28 @@ import signal
import stat
import subprocess
import sys
+import tempfile
import traceback
-import urwid
import weakref
-from netlib import tcp
+import urwid
-from .. import flow, script, contentviews
-from . import flowlist, flowview, help, window, signals, options
-from . import grideditor, palettes, statusbar, palettepicker
-from ..exceptions import FlowReadException, ScriptException
+from mitmproxy import contentviews
+from mitmproxy import controller
+from mitmproxy import exceptions
+from mitmproxy import flow
+from mitmproxy import script
+from mitmproxy.console import flowlist
+from mitmproxy.console import flowview
+from mitmproxy.console import grideditor
+from mitmproxy.console import help
+from mitmproxy.console import options
+from mitmproxy.console import palettepicker
+from mitmproxy.console import palettes
+from mitmproxy.console import signals
+from mitmproxy.console import statusbar
+from mitmproxy.console import window
+from netlib import tcp
EVENTLOG_SIZE = 500
@@ -264,7 +275,7 @@ class ConsoleMaster(flow.FlowMaster):
for i in options.scripts:
try:
self.load_script(i)
- except ScriptException as e:
+ except exceptions.ScriptException as e:
print("Script load error: {}".format(e), file=sys.stderr)
sys.exit(1)
@@ -385,7 +396,7 @@ class ConsoleMaster(flow.FlowMaster):
"""
try:
return flow.read_flows_from_paths(path)
- except FlowReadException as e:
+ except exceptions.FlowReadException as e:
signals.status_message.send(message=e.strerror)
def client_playback_path(self, path):
@@ -669,7 +680,7 @@ class ConsoleMaster(flow.FlowMaster):
reterr = None
try:
flow.FlowMaster.load_flows_file(self, path)
- except FlowReadException as e:
+ except exceptions.FlowReadException as e:
reterr = str(e)
signals.flowlist_change.send(self)
return reterr
@@ -699,7 +710,7 @@ class ConsoleMaster(flow.FlowMaster):
for command in commands:
try:
self.load_script(command)
- except ScriptException as e:
+ except exceptions.ScriptException as e:
signals.status_message.send(
message='Error loading "{}".'.format(command)
)
@@ -746,14 +757,15 @@ class ConsoleMaster(flow.FlowMaster):
)
def process_flow(self, f):
- if self.state.intercept and f.match(self.state.intercept) and not f.request.is_replay:
+ should_intercept = any(
+ [
+ self.state.intercept and f.match(self.state.intercept) and not f.request.is_replay,
+ f.intercepted,
+ ]
+ )
+ if should_intercept:
f.intercept(self)
- else:
- # check if flow was intercepted within an inline script by flow.intercept()
- if f.intercepted:
- f.intercept(self)
- else:
- f.reply()
+ f.reply.take()
signals.flowlist_change.send(self)
signals.flow_change.send(self, flow = f)
@@ -761,26 +773,30 @@ class ConsoleMaster(flow.FlowMaster):
self.eventlist[:] = []
# Handlers
- def handle_error(self, f):
- f = flow.FlowMaster.handle_error(self, f)
+ @controller.handler
+ def error(self, f):
+ f = flow.FlowMaster.error(self, f)
if f:
self.process_flow(f)
return f
- def handle_request(self, f):
- f = flow.FlowMaster.handle_request(self, f)
+ @controller.handler
+ def request(self, f):
+ f = flow.FlowMaster.request(self, f)
if f:
self.process_flow(f)
return f
- def handle_response(self, f):
- f = flow.FlowMaster.handle_response(self, f)
+ @controller.handler
+ def response(self, f):
+ f = flow.FlowMaster.response(self, f)
if f:
self.process_flow(f)
return f
- def handle_script_change(self, script):
- if super(ConsoleMaster, self).handle_script_change(script):
+ @controller.handler
+ def script_change(self, script):
+ if super(ConsoleMaster, self).script_change(script):
signals.status_message.send(message='"{}" reloaded.'.format(script.filename))
else:
signals.status_message.send(message='Error reloading "{}".'.format(script.filename))
diff --git a/mitmproxy/console/common.py b/mitmproxy/console/common.py
index 25658dfa..acb7fc35 100644
--- a/mitmproxy/console/common.py
+++ b/mitmproxy/console/common.py
@@ -1,16 +1,16 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function, division
-import urwid
-import urwid.util
import os
-import netlib.utils
-
-from .. import utils
-from .. import flow_export
-from ..models import decoded
-from . import signals
+import urwid
+import urwid.util
+import netlib
+from mitmproxy import flow
+from mitmproxy import models
+from mitmproxy import utils
+from mitmproxy.console import signals
+from netlib import human
try:
import pyperclip
@@ -259,7 +259,7 @@ def copy_flow_format_data(part, scope, flow):
if scope in ("q", "a"):
if flow.request.content is None:
return None, "Request content is missing"
- with decoded(flow.request):
+ with models.decoded(flow.request):
if part == "h":
data += netlib.http.http1.assemble_request(flow.request)
elif part == "c":
@@ -272,7 +272,7 @@ def copy_flow_format_data(part, scope, flow):
if scope in ("s", "a") and flow.response:
if flow.response.content is None:
return None, "Response content is missing"
- with decoded(flow.response):
+ with models.decoded(flow.response):
if part == "h":
data += netlib.http.http1.assemble_response(flow.response)
elif part == "c":
@@ -282,16 +282,16 @@ def copy_flow_format_data(part, scope, flow):
return data, False
-def export_prompt(k, flow):
+def export_prompt(k, f):
exporters = {
- "c": flow_export.curl_command,
- "p": flow_export.python_code,
- "r": flow_export.raw_request,
- "l": flow_export.locust_code,
- "t": flow_export.locust_task,
+ "c": flow.export.curl_command,
+ "p": flow.export.python_code,
+ "r": flow.export.raw_request,
+ "l": flow.export.locust_code,
+ "t": flow.export.locust_task,
}
if k in exporters:
- copy_to_clipboard_or_prompt(exporters[k](flow))
+ copy_to_clipboard_or_prompt(exporters[k](f))
def copy_to_clipboard_or_prompt(data):
@@ -419,7 +419,7 @@ def format_flow(f, focus, extended=False, hostheader=False, marked=False):
)
if f.response:
if f.response.content:
- contentdesc = netlib.utils.pretty_size(len(f.response.content))
+ contentdesc = human.pretty_size(len(f.response.content))
elif f.response.content is None:
contentdesc = "[content missing]"
else:
@@ -427,7 +427,7 @@ def format_flow(f, focus, extended=False, hostheader=False, marked=False):
duration = 0
if f.response.timestamp_end and f.request.timestamp_start:
duration = f.response.timestamp_end - f.request.timestamp_start
- roundtrip = utils.pretty_duration(duration)
+ roundtrip = human.pretty_duration(duration)
d.update(dict(
resp_code = f.response.status_code,
diff --git a/mitmproxy/console/flowdetailview.py b/mitmproxy/console/flowdetailview.py
index ca083b10..e2c28e71 100644
--- a/mitmproxy/console/flowdetailview.py
+++ b/mitmproxy/console/flowdetailview.py
@@ -1,7 +1,9 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function, division
+
import urwid
-from . import common, searchable
-from .. import utils
+
+from mitmproxy import utils
+from mitmproxy.console import common, searchable
def maybe_timestamp(base, attr):
diff --git a/mitmproxy/console/flowlist.py b/mitmproxy/console/flowlist.py
index 5672c0d9..8c20c4b6 100644
--- a/mitmproxy/console/flowlist.py
+++ b/mitmproxy/console/flowlist.py
@@ -1,9 +1,10 @@
-from __future__ import absolute_import
-import urwid
+from __future__ import absolute_import, print_function, division
-import netlib.utils
+import urwid
-from . import common, signals
+import netlib.http.url
+from mitmproxy.console import common
+from mitmproxy.console import signals
def _mkhelp():
@@ -350,7 +351,7 @@ class FlowListBox(urwid.ListBox):
)
def new_request(self, url, method):
- parts = netlib.utils.parse_url(str(url))
+ parts = netlib.http.url.parse(str(url))
if not parts:
signals.status_message.send(message="Invalid Url")
return
diff --git a/mitmproxy/console/flowview.py b/mitmproxy/console/flowview.py
index 2010cecd..e9b23176 100644
--- a/mitmproxy/console/flowview.py
+++ b/mitmproxy/console/flowview.py
@@ -1,17 +1,25 @@
-from __future__ import absolute_import, division
+from __future__ import absolute_import, print_function, division
+
+import math
import os
-import traceback
import sys
+import traceback
-import math
import urwid
-from netlib.http import Headers, status_codes
-from . import common, grideditor, signals, searchable, tabs
-from . import flowdetailview
-from .. import utils, controller, contentviews
-from ..models import HTTPRequest, HTTPResponse, decoded
-from ..exceptions import ContentViewException
+from mitmproxy import contentviews
+from mitmproxy import controller
+from mitmproxy import exceptions
+from mitmproxy import models
+from mitmproxy import utils
+from mitmproxy.console import common
+from mitmproxy.console import flowdetailview
+from mitmproxy.console import grideditor
+from mitmproxy.console import searchable
+from mitmproxy.console import signals
+from mitmproxy.console import tabs
+from netlib.http import Headers
+from netlib.http import status_codes
class SearchError(Exception):
@@ -193,12 +201,12 @@ class FlowView(tabs.Tabs):
try:
query = None
- if isinstance(message, HTTPRequest):
+ if isinstance(message, models.HTTPRequest):
query = message.query
description, lines = contentviews.get_content_view(
viewmode, message.content, headers=message.headers, query=query
)
- except ContentViewException:
+ except exceptions.ContentViewException:
s = "Content viewer failed: \n" + traceback.format_exc()
signals.add_event(s, "error")
description, lines = contentviews.get_content_view(
@@ -207,7 +215,7 @@ class FlowView(tabs.Tabs):
description = description.replace("Raw", "Couldn't parse: falling back to Raw")
# Give hint that you have to tab for the response.
- if description == "No content" and isinstance(message, HTTPRequest):
+ if description == "No content" and isinstance(message, models.HTTPRequest):
description = "No request content (press tab to view response)"
# If the users has a wide terminal, he gets fewer lines; this should not be an issue.
@@ -372,7 +380,7 @@ class FlowView(tabs.Tabs):
message = self.flow.request
else:
if not self.flow.response:
- self.flow.response = HTTPResponse(
+ self.flow.response = models.HTTPResponse(
self.flow.request.http_version,
200, "OK", Headers(), ""
)
@@ -399,7 +407,7 @@ class FlowView(tabs.Tabs):
)
)
if part == "r":
- with decoded(message):
+ with models.decoded(message):
# Fix an issue caused by some editors when editing a
# request/response body. Many editors make it hard to save a
# file without a terminating newline on the last line. When
diff --git a/mitmproxy/console/grideditor.py b/mitmproxy/console/grideditor.py
index ea26d966..9fa51ccb 100644
--- a/mitmproxy/console/grideditor.py
+++ b/mitmproxy/console/grideditor.py
@@ -1,15 +1,18 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function, division
import copy
-import re
import os
-import urwid
-
-from netlib.http import user_agents, cookies
+import re
-from . import common, signals
-from .. import utils, filt, script
+import urwid
+from mitmproxy import filt
+from mitmproxy import script
+from mitmproxy import utils
+from mitmproxy.console import common
+from mitmproxy.console import signals
+from netlib.http import cookies
+from netlib.http import user_agents
FOOTER = [
('heading_key', "enter"), ":edit ",
diff --git a/mitmproxy/console/help.py b/mitmproxy/console/help.py
index 0c264ebf..26cb4ed3 100644
--- a/mitmproxy/console/help.py
+++ b/mitmproxy/console/help.py
@@ -1,9 +1,11 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function, division
import urwid
-from . import common, signals
-from .. import filt, version
+from mitmproxy import filt
+from mitmproxy import version
+from mitmproxy.console import common
+from mitmproxy.console import signals
footer = [
("heading", 'mitmproxy v%s ' % version.VERSION),
diff --git a/mitmproxy/console/options.py b/mitmproxy/console/options.py
index 5c9e0cc9..5a01c9d5 100644
--- a/mitmproxy/console/options.py
+++ b/mitmproxy/console/options.py
@@ -1,8 +1,13 @@
+from __future__ import absolute_import, print_function, division
+
import urwid
-from .. import contentviews
-from . import common, signals, grideditor
-from . import select, palettes
+from mitmproxy import contentviews
+from mitmproxy.console import common
+from mitmproxy.console import grideditor
+from mitmproxy.console import palettes
+from mitmproxy.console import select
+from mitmproxy.console import signals
footer = [
('heading_key', "enter/space"), ":toggle ",
diff --git a/mitmproxy/console/palettepicker.py b/mitmproxy/console/palettepicker.py
index 51ad0606..f2acba0a 100644
--- a/mitmproxy/console/palettepicker.py
+++ b/mitmproxy/console/palettepicker.py
@@ -1,6 +1,11 @@
+from __future__ import absolute_import, print_function, division
+
import urwid
-from . import select, common, palettes, signals
+from mitmproxy.console import common
+from mitmproxy.console import palettes
+from mitmproxy.console import select
+from mitmproxy.console import signals
footer = [
('heading_key', "enter/space"), ":select",
diff --git a/mitmproxy/console/palettes.py b/mitmproxy/console/palettes.py
index bd370181..36cc3ac0 100644
--- a/mitmproxy/console/palettes.py
+++ b/mitmproxy/console/palettes.py
@@ -3,6 +3,7 @@
#
# http://urwid.org/manual/displayattributes.html
#
+from __future__ import absolute_import, print_function, division
class Palette:
diff --git a/mitmproxy/console/pathedit.py b/mitmproxy/console/pathedit.py
index 4447070b..0eae9123 100644
--- a/mitmproxy/console/pathedit.py
+++ b/mitmproxy/console/pathedit.py
@@ -1,3 +1,5 @@
+from __future__ import absolute_import, print_function, division
+
import glob
import os.path
diff --git a/mitmproxy/console/searchable.py b/mitmproxy/console/searchable.py
index cff1f0a1..c60d1cd9 100644
--- a/mitmproxy/console/searchable.py
+++ b/mitmproxy/console/searchable.py
@@ -1,6 +1,8 @@
+from __future__ import absolute_import, print_function, division
+
import urwid
-from . import signals
+from mitmproxy.console import signals
class Highlight(urwid.AttrMap):
diff --git a/mitmproxy/console/select.py b/mitmproxy/console/select.py
index 928a7ca5..091f07a2 100644
--- a/mitmproxy/console/select.py
+++ b/mitmproxy/console/select.py
@@ -1,6 +1,8 @@
+from __future__ import absolute_import, print_function, division
+
import urwid
-from . import common
+from mitmproxy.console import common
class _OptionWidget(urwid.WidgetWrap):
@@ -72,7 +74,8 @@ class Heading:
return opt
-_neg = lambda: False
+def _neg(*args):
+ return False
class Option:
diff --git a/mitmproxy/console/signals.py b/mitmproxy/console/signals.py
index 6a439bf3..b57ebf0c 100644
--- a/mitmproxy/console/signals.py
+++ b/mitmproxy/console/signals.py
@@ -1,3 +1,5 @@
+from __future__ import absolute_import, print_function, division
+
import blinker
# Show a status message in the action bar
diff --git a/mitmproxy/console/statusbar.py b/mitmproxy/console/statusbar.py
index 96840a20..af8089b6 100644
--- a/mitmproxy/console/statusbar.py
+++ b/mitmproxy/console/statusbar.py
@@ -1,9 +1,14 @@
+from __future__ import absolute_import, print_function, division
+
import os.path
import urwid
import netlib.utils
-from . import pathedit, signals, common
+from mitmproxy.console import common
+from mitmproxy.console import pathedit
+from mitmproxy.console import signals
+from netlib import human
class ActionBar(urwid.WidgetWrap):
@@ -197,7 +202,7 @@ class StatusBar(urwid.WidgetWrap):
opts.append("following")
if self.master.stream_large_bodies:
opts.append(
- "stream:%s" % netlib.utils.pretty_size(
+ "stream:%s" % human.pretty_size(
self.master.stream_large_bodies.max_size
)
)
@@ -207,7 +212,7 @@ class StatusBar(urwid.WidgetWrap):
if self.master.server.config.mode in ["reverse", "upstream"]:
dst = self.master.server.config.upstream_server
- r.append("[dest:%s]" % netlib.utils.unparse_url(
+ r.append("[dest:%s]" % netlib.utils.unparse(
dst.scheme,
dst.address.host,
dst.address.port
diff --git a/mitmproxy/console/tabs.py b/mitmproxy/console/tabs.py
index b5423038..bfcdeba3 100644
--- a/mitmproxy/console/tabs.py
+++ b/mitmproxy/console/tabs.py
@@ -1,3 +1,5 @@
+from __future__ import absolute_import, print_function, division
+
import urwid
diff --git a/mitmproxy/console/window.py b/mitmproxy/console/window.py
index 47c284e4..25780daf 100644
--- a/mitmproxy/console/window.py
+++ b/mitmproxy/console/window.py
@@ -1,5 +1,8 @@
+from __future__ import absolute_import, print_function, division
+
import urwid
-from . import signals
+
+from mitmproxy.console import signals
class Window(urwid.Frame):
diff --git a/mitmproxy/contentviews.py b/mitmproxy/contentviews.py
index 1b0f389f..42061a8c 100644
--- a/mitmproxy/contentviews.py
+++ b/mitmproxy/contentviews.py
@@ -12,26 +12,31 @@ use. For HTTP, the message headers are passed as the ``headers`` keyword argumen
requests, the query parameters are passed as the ``query`` keyword argument.
"""
-from __future__ import (absolute_import, print_function, division)
-from six.moves import cStringIO as StringIO
+from __future__ import absolute_import, print_function, division
+
+import datetime
import json
import logging
import subprocess
import sys
-import lxml.html
-import lxml.etree
-import datetime
-from PIL import Image
-from PIL.ExifTags import TAGS
+
import html2text
+import lxml.etree
+import lxml.html
import six
-from netlib.odict import ODict
+from PIL import ExifTags
+from PIL import Image
+from six.moves import cStringIO as StringIO
+
+import mitmproxy.utils
+from mitmproxy import exceptions
+from mitmproxy.contrib import jsbeautifier
+from mitmproxy.contrib.wbxml import ASCommandResponse
from netlib import encoding
-from netlib.utils import clean_bin, hexdump, urldecode, multipartdecode, parse_content_type
-from . import utils
-from .exceptions import ContentViewException
-from .contrib import jsbeautifier
-from .contrib.wbxml.ASCommandResponse import ASCommandResponse
+from netlib import http
+from netlib import odict
+from netlib.http import url
+from netlib import strutils
try:
import pyamf
@@ -120,15 +125,15 @@ class ViewAuto(View):
headers = metadata.get("headers", {})
ctype = headers.get("content-type")
if data and ctype:
- ct = parse_content_type(ctype) if ctype else None
+ ct = http.parse_content_type(ctype) if ctype else None
ct = "%s/%s" % (ct[0], ct[1])
if ct in content_types_map:
return content_types_map[ct][0](data, **metadata)
- elif utils.isXML(data):
+ elif strutils.isXML(data):
return get("XML")(data, **metadata)
if metadata.get("query"):
return get("Query")(data, **metadata)
- if data and utils.isMostlyBin(data):
+ if data and strutils.isMostlyBin(data):
return get("Hex")(data)
if not data:
return "No content", []
@@ -151,7 +156,7 @@ class ViewHex(View):
@staticmethod
def _format(data):
- for offset, hexa, s in hexdump(data):
+ for offset, hexa, s in strutils.hexdump(data):
yield [
("offset", offset + " "),
("text", hexa + " "),
@@ -210,7 +215,7 @@ class ViewJSON(View):
content_types = ["application/json"]
def __call__(self, data, **metadata):
- pretty_json = utils.pretty_json(data)
+ pretty_json = mitmproxy.utils.pretty_json(data)
if pretty_json:
return "JSON", format_text(pretty_json)
@@ -221,7 +226,7 @@ class ViewHTML(View):
content_types = ["text/html"]
def __call__(self, data, **metadata):
- if utils.isXML(data):
+ if strutils.isXML(data):
parser = lxml.etree.HTMLParser(
strip_cdata=True,
remove_blank_text=True
@@ -257,8 +262,8 @@ class ViewURLEncoded(View):
content_types = ["application/x-www-form-urlencoded"]
def __call__(self, data, **metadata):
- d = urldecode(data)
- return "URLEncoded form", format_dict(ODict(d))
+ d = url.decode(data)
+ return "URLEncoded form", format_dict(odict.ODict(d))
class ViewMultipart(View):
@@ -269,12 +274,12 @@ class ViewMultipart(View):
@staticmethod
def _format(v):
yield [("highlight", "Form data:\n")]
- for message in format_dict(ODict(v)):
+ for message in format_dict(odict.ODict(v)):
yield message
def __call__(self, data, **metadata):
headers = metadata.get("headers", {})
- v = multipartdecode(headers, data)
+ v = http.multipart.decode(headers, data)
if v:
return "Multipart form", self._format(v)
@@ -414,11 +419,11 @@ class ViewImage(View):
ex = img._getexif()
if ex:
for i in sorted(ex.keys()):
- tag = TAGS.get(i, i)
+ tag = ExifTags.TAGS.get(i, i)
parts.append(
(str(tag), str(ex[i]))
)
- fmt = format_dict(ODict(parts))
+ fmt = format_dict(odict.ODict(parts))
return "%s image" % img.format, fmt
@@ -489,7 +494,7 @@ class ViewWBXML(View):
def __call__(self, data, **metadata):
try:
- parser = ASCommandResponse(data)
+ parser = ASCommandResponse.ASCommandResponse(data)
parsedContent = parser.xmlString
if parsedContent:
return "WBXML", format_text(parsedContent)
@@ -518,12 +523,12 @@ def add(view):
# TODO: auto-select a different name (append an integer?)
for i in views:
if i.name == view.name:
- raise ContentViewException("Duplicate view: " + view.name)
+ raise exceptions.ContentViewException("Duplicate view: " + view.name)
# TODO: the UI should auto-prompt for a replacement shortcut
for prompt in view_prompts:
if prompt[1] == view.prompt[1]:
- raise ContentViewException("Duplicate view shortcut: " + view.prompt[1])
+ raise exceptions.ContentViewException("Duplicate view shortcut: " + view.prompt[1])
views.append(view)
@@ -576,9 +581,9 @@ def safe_to_print(lines, encoding="utf8"):
clean_line = []
for (style, text) in line:
try:
- text = clean_bin(text.decode(encoding, "strict"))
+ text = strutils.clean_bin(text.decode(encoding, "strict"))
except UnicodeDecodeError:
- text = clean_bin(text).decode(encoding, "strict")
+ text = strutils.clean_bin(text).decode(encoding, "strict")
clean_line.append((style, text))
yield clean_line
@@ -610,8 +615,8 @@ def get_content_view(viewmode, data, **metadata):
# Third-party viewers can fail in unexpected ways...
except Exception as e:
six.reraise(
- ContentViewException,
- ContentViewException(str(e)),
+ exceptions.ContentViewException,
+ exceptions.ContentViewException(str(e)),
sys.exc_info()[2]
)
if not ret:
diff --git a/mitmproxy/controller.py b/mitmproxy/controller.py
index 81978a09..1498c3ad 100644
--- a/mitmproxy/controller.py
+++ b/mitmproxy/controller.py
@@ -1,21 +1,57 @@
-from __future__ import absolute_import
-from six.moves import queue
+from __future__ import absolute_import, print_function, division
+
+import functools
import threading
-from .exceptions import Kill
+from six.moves import queue
+
+from mitmproxy import exceptions
+
+Events = frozenset([
+ "clientconnect",
+ "clientdisconnect",
+ "serverconnect",
+ "serverdisconnect",
+
+ "tcp_open",
+ "tcp_message",
+ "tcp_error",
+ "tcp_close",
+
+ "request",
+ "response",
+ "responseheaders",
+
+ "next_layer",
+
+ "error",
+ "log",
+
+ "script_change",
+])
class Master(object):
"""
- The master handles mitmproxy's main event loop.
+ The master handles mitmproxy's main event loop.
"""
-
- def __init__(self):
+ def __init__(self, *servers):
self.event_queue = queue.Queue()
self.should_exit = threading.Event()
+ self.servers = []
+ for i in servers:
+ self.add_server(i)
+
+ def add_server(self, server):
+ # We give a Channel to the server which can be used to communicate with the master
+ channel = Channel(self.event_queue, self.should_exit)
+ server.set_channel(channel)
+ self.servers.append(server)
def start(self):
self.should_exit.clear()
+ for server in self.servers:
+ ServerThread(server).start()
def run(self):
self.start()
@@ -35,7 +71,17 @@ class Master(object):
# exception is thrown.
while True:
mtype, obj = self.event_queue.get(timeout=timeout)
- handle_func = getattr(self, "handle_" + mtype)
+ if mtype not in Events:
+ raise exceptions.ControlException("Unknown event %s" % repr(mtype))
+ handle_func = getattr(self, mtype)
+ if not hasattr(handle_func, "func_dict"):
+ raise exceptions.ControlException("Handler %s not a function" % mtype)
+ if not handle_func.func_dict.get("__handler"):
+ raise exceptions.ControlException(
+ "Handler function %s is not decorated with controller.handler" % (
+ handle_func
+ )
+ )
handle_func(obj)
self.event_queue.task_done()
changed = True
@@ -44,33 +90,9 @@ class Master(object):
return changed
def shutdown(self):
- self.should_exit.set()
-
-
-class ServerMaster(Master):
- """
- The ServerMaster adds server thread support to the master.
- """
-
- def __init__(self):
- super(ServerMaster, self).__init__()
- self.servers = []
-
- def add_server(self, server):
- # We give a Channel to the server which can be used to communicate with the master
- channel = Channel(self.event_queue, self.should_exit)
- server.set_channel(channel)
- self.servers.append(server)
-
- def start(self):
- super(ServerMaster, self).start()
- for server in self.servers:
- ServerThread(server).start()
-
- def shutdown(self):
for server in self.servers:
server.shutdown()
- super(ServerMaster, self).shutdown()
+ self.should_exit.set()
class ServerThread(threading.Thread):
@@ -86,10 +108,9 @@ class ServerThread(threading.Thread):
class Channel(object):
"""
- The only way for the proxy server to communicate with the master
- is to use the channel it has been given.
+ The only way for the proxy server to communicate with the master
+ is to use the channel it has been given.
"""
-
def __init__(self, q, should_exit):
self.q = q
self.should_exit = should_exit
@@ -100,7 +121,7 @@ class Channel(object):
master. Then wait for a response.
Raises:
- Kill: All connections should be closed immediately.
+ exceptions.Kill: All connections should be closed immediately.
"""
m.reply = Reply(m)
self.q.put((mtype, m))
@@ -110,11 +131,10 @@ class Channel(object):
g = m.reply.q.get(timeout=0.5)
except queue.Empty: # pragma: no cover
continue
- if g == Kill:
- raise Kill()
+ if g == exceptions.Kill:
+ raise exceptions.Kill()
return g
-
- raise Kill()
+ raise exceptions.Kill()
def tell(self, mtype, m):
"""
@@ -130,9 +150,13 @@ class DummyReply(object):
A reply object that does nothing. Useful when we need an object to seem
like it has a channel, and during testing.
"""
-
def __init__(self):
self.acked = False
+ self.taken = False
+ self.handled = False
+
+ def take(self):
+ self.taken = True
def __call__(self, msg=False):
self.acked = True
@@ -142,22 +166,68 @@ class DummyReply(object):
NO_REPLY = object()
+def handler(f):
+ @functools.wraps(f)
+ def wrapper(*args, **kwargs):
+ # We can either be called as a method, or as a wrapped solo function
+ if len(args) == 1:
+ message = args[0]
+ elif len(args) == 2:
+ message = args[1]
+ else:
+ raise exceptions.ControlException("Handler takes one argument: a message")
+
+ if not hasattr(message, "reply"):
+ raise exceptions.ControlException("Message %s has no reply attribute" % message)
+
+ # The following ensures that inheritance with wrapped handlers in the
+ # base class works. If we're the first handler, then responsibility for
+ # acking is ours. If not, it's someone else's and we ignore it.
+ handling = False
+ # We're the first handler - ack responsibility is ours
+ if not message.reply.handled:
+ handling = True
+ message.reply.handled = True
+
+ ret = f(*args, **kwargs)
+
+ if handling and not message.reply.acked and not message.reply.taken:
+ message.reply()
+ return ret
+ # Mark this function as a handler wrapper
+ wrapper.func_dict["__handler"] = True
+ return wrapper
+
+
class Reply(object):
"""
Messages sent through a channel are decorated with a "reply" attribute.
This object is used to respond to the message through the return
channel.
"""
-
def __init__(self, obj):
self.obj = obj
self.q = queue.Queue()
+ # Has this message been acked?
self.acked = False
+ # Has the user taken responsibility for ack-ing?
+ self.taken = False
+ # Has a handler taken responsibility for ack-ing?
+ self.handled = False
+
+ def take(self):
+ self.taken = True
def __call__(self, msg=NO_REPLY):
+ if self.acked:
+ raise exceptions.ControlException("Message already acked.")
+ self.acked = True
+ if msg is NO_REPLY:
+ self.q.put(self.obj)
+ else:
+ self.q.put(msg)
+
+ def __del__(self):
if not self.acked:
- self.acked = True
- if msg is NO_REPLY:
- self.q.put(self.obj)
- else:
- self.q.put(msg)
+ # This will be ignored by the interpreter, but emit a warning
+ raise exceptions.ControlException("Un-acked message")
diff --git a/mitmproxy/dump.py b/mitmproxy/dump.py
index f1eabdb8..cc6896ed 100644
--- a/mitmproxy/dump.py
+++ b/mitmproxy/dump.py
@@ -1,13 +1,19 @@
-from __future__ import absolute_import, print_function
-import traceback
+from __future__ import absolute_import, print_function, division
+
+import itertools
import sys
+import traceback
+
import click
-import itertools
+from mitmproxy import contentviews
+from mitmproxy import controller
+from mitmproxy import exceptions
+from mitmproxy import filt
+from mitmproxy import flow
+from netlib import human
from netlib import tcp
-from netlib.utils import bytes_to_escaped_str, pretty_size
-from . import flow, filt, contentviews
-from .exceptions import ContentViewException, FlowReadException, ScriptException
+from netlib import strutils
class DumpError(Exception):
@@ -74,8 +80,8 @@ class DumpMaster(flow.FlowMaster):
if self.server and self.server.config.http2 and not tcp.HAS_ALPN: # pragma: no cover
print("ALPN support missing (OpenSSL 1.0.2+ required)!\n"
- "HTTP/2 is disabled. Use --no-http2 to silence this warning.",
- file=sys.stderr)
+ "HTTP/2 is disabled. Use --no-http2 to silence this warning.",
+ file=sys.stderr)
if options.filtstr:
self.filt = filt.parse(options.filtstr)
@@ -127,13 +133,13 @@ class DumpMaster(flow.FlowMaster):
for command in scripts:
try:
self.load_script(command, use_reloader=True)
- except ScriptException as e:
+ except exceptions.ScriptException as e:
raise DumpError(str(e))
if options.rfile:
try:
self.load_flows_file(options.rfile)
- except FlowReadException as v:
+ except exceptions.FlowReadException as v:
self.add_event("Flow file corrupted.", "error")
raise DumpError(v)
@@ -147,7 +153,7 @@ class DumpMaster(flow.FlowMaster):
"""
try:
return flow.read_flows_from_paths(paths)
- except FlowReadException as e:
+ except exceptions.FlowReadException as e:
raise DumpError(str(e))
def add_event(self, e, level="info"):
@@ -175,8 +181,8 @@ class DumpMaster(flow.FlowMaster):
if self.o.flow_detail >= 2:
headers = "\r\n".join(
"{}: {}".format(
- click.style(bytes_to_escaped_str(k), fg="blue", bold=True),
- click.style(bytes_to_escaped_str(v), fg="blue"))
+ click.style(strutils.bytes_to_escaped_str(k), fg="blue", bold=True),
+ click.style(strutils.bytes_to_escaped_str(v), fg="blue"))
for k, v in message.headers.fields
)
self.echo(headers, indent=4)
@@ -192,7 +198,7 @@ class DumpMaster(flow.FlowMaster):
message.content,
headers=message.headers
)
- except ContentViewException:
+ except exceptions.ContentViewException:
s = "Content viewer failed: \n" + traceback.format_exc()
self.add_event(s, "debug")
type, lines = contentviews.get_content_view(
@@ -238,7 +244,7 @@ class DumpMaster(flow.FlowMaster):
stickycookie = ""
if flow.client_conn:
- client = click.style(bytes_to_escaped_str(flow.client_conn.address.host), bold=True)
+ client = click.style(strutils.bytes_to_escaped_str(flow.client_conn.address.host), bold=True)
else:
client = click.style("[replay]", fg="yellow", bold=True)
@@ -247,12 +253,12 @@ class DumpMaster(flow.FlowMaster):
GET="green",
DELETE="red"
).get(method.upper(), "magenta")
- method = click.style(bytes_to_escaped_str(method), fg=method_color, bold=True)
+ method = click.style(strutils.bytes_to_escaped_str(method), fg=method_color, bold=True)
if self.showhost:
url = flow.request.pretty_url
else:
url = flow.request.url
- url = click.style(bytes_to_escaped_str(url), bold=True)
+ url = click.style(strutils.bytes_to_escaped_str(url), bold=True)
httpversion = ""
if flow.request.http_version not in ("HTTP/1.1", "HTTP/1.0"):
@@ -282,12 +288,12 @@ class DumpMaster(flow.FlowMaster):
elif 400 <= code < 600:
code_color = "red"
code = click.style(str(code), fg=code_color, bold=True, blink=(code == 418))
- reason = click.style(bytes_to_escaped_str(flow.response.reason), fg=code_color, bold=True)
+ reason = click.style(strutils.bytes_to_escaped_str(flow.response.reason), fg=code_color, bold=True)
if flow.response.content is None:
size = "(content missing)"
else:
- size = pretty_size(len(flow.response.content))
+ size = human.pretty_size(len(flow.response.content))
size = click.style(size, bold=True)
arrows = click.style("<<", bold=True)
@@ -325,22 +331,23 @@ class DumpMaster(flow.FlowMaster):
self.echo_flow(f)
- def handle_request(self, f):
- flow.FlowMaster.handle_request(self, f)
- self.state.delete_flow(f)
+ @controller.handler
+ def request(self, f):
+ f = flow.FlowMaster.request(self, f)
if f:
- f.reply()
+ self.state.delete_flow(f)
return f
- def handle_response(self, f):
- flow.FlowMaster.handle_response(self, f)
+ @controller.handler
+ def response(self, f):
+ f = flow.FlowMaster.response(self, f)
if f:
- f.reply()
self._process_flow(f)
return f
- def handle_error(self, f):
- flow.FlowMaster.handle_error(self, f)
+ @controller.handler
+ def error(self, f):
+ flow.FlowMaster.error(self, f)
if f:
self._process_flow(f)
return f
diff --git a/mitmproxy/exceptions.py b/mitmproxy/exceptions.py
index 8f989063..d97b9498 100644
--- a/mitmproxy/exceptions.py
+++ b/mitmproxy/exceptions.py
@@ -5,14 +5,14 @@ Every Exception mitmproxy raises shall be a subclass of ProxyException.
See also: http://lucumr.pocoo.org/2014/10/16/on-error-handling/
"""
-from __future__ import (absolute_import, print_function, division)
-
-import traceback
+from __future__ import absolute_import, print_function, division
import sys
+import traceback
class ProxyException(Exception):
+
"""
Base class for all exceptions thrown by mitmproxy.
"""
@@ -22,6 +22,7 @@ class ProxyException(Exception):
class Kill(ProxyException):
+
"""
Signal that both client and server connection(s) should be killed immediately.
"""
@@ -37,6 +38,7 @@ class TlsProtocolException(ProtocolException):
class ClientHandshakeException(TlsProtocolException):
+
def __init__(self, message, server):
super(ClientHandshakeException, self).__init__(message)
self.server = server
@@ -67,6 +69,7 @@ class ReplayException(ProxyException):
class ScriptException(ProxyException):
+
@classmethod
def from_exception_context(cls, cut_tb=1):
"""
@@ -88,3 +91,7 @@ class ScriptException(ProxyException):
class FlowReadException(ProxyException):
pass
+
+
+class ControlException(Exception):
+ pass
diff --git a/mitmproxy/filt.py b/mitmproxy/filt.py
index f34969dd..d98e3749 100644
--- a/mitmproxy/filt.py
+++ b/mitmproxy/filt.py
@@ -31,13 +31,16 @@
~c CODE Response code.
rex Equivalent to ~u rex
"""
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, division
+
import re
import sys
+
import pyparsing as pp
class _Token(object):
+
def dump(self, indent=0, fp=sys.stdout):
print("{spacing}{name}{expr}".format(
spacing="\t" * indent,
diff --git a/mitmproxy/flow.py b/mitmproxy/flow.py
deleted file mode 100644
index a09a81a7..00000000
--- a/mitmproxy/flow.py
+++ /dev/null
@@ -1,1242 +0,0 @@
-"""
- This module provides more sophisticated flow tracking and provides filtering and interception facilities.
-"""
-from __future__ import absolute_import
-
-from abc import abstractmethod, ABCMeta
-import hashlib
-import sys
-
-import six
-from six.moves import http_cookies, http_cookiejar, urllib
-import os
-import re
-
-from typing import List, Optional, Set
-
-from netlib import wsgi, odict
-from netlib.exceptions import HttpException
-from netlib.http import Headers, http1, cookies
-from netlib.utils import clean_bin
-from . import controller, tnetstring, filt, script, version, flow_format_compat
-from .onboarding import app
-from .proxy.config import HostMatcher
-from .protocol.http_replay import RequestReplayThread
-from .exceptions import Kill, FlowReadException
-from .models import ClientConnection, ServerConnection, HTTPFlow, HTTPRequest, FLOW_TYPES, TCPFlow
-from collections import defaultdict
-
-
-class AppRegistry:
-
- def __init__(self):
- self.apps = {}
-
- def add(self, app, domain, port):
- """
- Add a WSGI app to the registry, to be served for requests to the
- specified domain, on the specified port.
- """
- self.apps[(domain, port)] = wsgi.WSGIAdaptor(
- app,
- domain,
- port,
- version.NAMEVERSION
- )
-
- def get(self, request):
- """
- Returns an WSGIAdaptor instance if request matches an app, or None.
- """
- if (request.host, request.port) in self.apps:
- return self.apps[(request.host, request.port)]
- if "host" in request.headers:
- host = request.headers["host"]
- return self.apps.get((host, request.port), None)
-
-
-class ReplaceHooks:
-
- def __init__(self):
- self.lst = []
-
- def set(self, r):
- self.clear()
- for i in r:
- self.add(*i)
-
- def add(self, fpatt, rex, s):
- """
- add a replacement hook.
-
- fpatt: a string specifying a filter pattern.
- rex: a regular expression.
- s: the replacement string
-
- returns true if hook was added, false if the pattern could not be
- parsed.
- """
- cpatt = filt.parse(fpatt)
- if not cpatt:
- return False
- try:
- re.compile(rex)
- except re.error:
- return False
- self.lst.append((fpatt, rex, s, cpatt))
- return True
-
- def get_specs(self):
- """
- Retrieve the hook specifcations. Returns a list of (fpatt, rex, s)
- tuples.
- """
- return [i[:3] for i in self.lst]
-
- def count(self):
- return len(self.lst)
-
- def run(self, f):
- for _, rex, s, cpatt in self.lst:
- if cpatt(f):
- if f.response:
- f.response.replace(rex, s)
- else:
- f.request.replace(rex, s)
-
- def clear(self):
- self.lst = []
-
-
-class SetHeaders:
-
- def __init__(self):
- self.lst = []
-
- def set(self, r):
- self.clear()
- for i in r:
- self.add(*i)
-
- def add(self, fpatt, header, value):
- """
- Add a set header hook.
-
- fpatt: String specifying a filter pattern.
- header: Header name.
- value: Header value string
-
- Returns True if hook was added, False if the pattern could not be
- parsed.
- """
- cpatt = filt.parse(fpatt)
- if not cpatt:
- return False
- self.lst.append((fpatt, header, value, cpatt))
- return True
-
- def get_specs(self):
- """
- Retrieve the hook specifcations. Returns a list of (fpatt, rex, s)
- tuples.
- """
- return [i[:3] for i in self.lst]
-
- def count(self):
- return len(self.lst)
-
- def clear(self):
- self.lst = []
-
- def run(self, f):
- for _, header, value, cpatt in self.lst:
- if cpatt(f):
- if f.response:
- f.response.headers.pop(header, None)
- else:
- f.request.headers.pop(header, None)
- for _, header, value, cpatt in self.lst:
- if cpatt(f):
- if f.response:
- f.response.headers.add(header, value)
- else:
- f.request.headers.add(header, value)
-
-
-class StreamLargeBodies(object):
-
- def __init__(self, max_size):
- self.max_size = max_size
-
- def run(self, flow, is_request):
- r = flow.request if is_request else flow.response
- expected_size = http1.expected_http_body_size(
- flow.request, flow.response if not is_request else None
- )
- if not r.content and not (0 <= expected_size <= self.max_size):
- # r.stream may already be a callable, which we want to preserve.
- r.stream = r.stream or True
-
-
-class ClientPlaybackState:
-
- def __init__(self, flows, exit):
- self.flows, self.exit = flows, exit
- self.current = None
- self.testing = False # Disables actual replay for testing.
-
- def count(self):
- return len(self.flows)
-
- def done(self):
- if len(self.flows) == 0 and not self.current:
- return True
- return False
-
- def clear(self, flow):
- """
- A request has returned in some way - if this is the one we're
- servicing, go to the next flow.
- """
- if flow is self.current:
- self.current = None
-
- def tick(self, master):
- if self.flows and not self.current:
- self.current = self.flows.pop(0).copy()
- if not self.testing:
- master.replay_request(self.current)
- else:
- self.current.reply = controller.DummyReply()
- master.handle_request(self.current)
- if self.current.response:
- master.handle_response(self.current)
-
-
-class ServerPlaybackState:
-
- def __init__(
- self,
- headers,
- flows,
- exit,
- nopop,
- ignore_params,
- ignore_content,
- ignore_payload_params,
- ignore_host):
- """
- headers: Case-insensitive list of request headers that should be
- included in request-response matching.
- """
- self.headers = headers
- self.exit = exit
- self.nopop = nopop
- self.ignore_params = ignore_params
- self.ignore_content = ignore_content
- self.ignore_payload_params = ignore_payload_params
- self.ignore_host = ignore_host
- self.fmap = {}
- for i in flows:
- if i.response:
- l = self.fmap.setdefault(self._hash(i), [])
- l.append(i)
-
- def count(self):
- return sum(len(i) for i in self.fmap.values())
-
- def _hash(self, flow):
- """
- Calculates a loose hash of the flow request.
- """
- r = flow.request
-
- _, _, path, _, query, _ = urllib.parse.urlparse(r.url)
- queriesArray = urllib.parse.parse_qsl(query, keep_blank_values=True)
-
- key = [
- str(r.port),
- str(r.scheme),
- str(r.method),
- str(path),
- ]
-
- if not self.ignore_content:
- form_contents = r.urlencoded_form or r.multipart_form
- if self.ignore_payload_params and form_contents:
- key.extend(
- p for p in form_contents.items(multi=True)
- if p[0] not in self.ignore_payload_params
- )
- else:
- key.append(str(r.content))
-
- if not self.ignore_host:
- key.append(r.host)
-
- filtered = []
- ignore_params = self.ignore_params or []
- for p in queriesArray:
- if p[0] not in ignore_params:
- filtered.append(p)
- for p in filtered:
- key.append(p[0])
- key.append(p[1])
-
- if self.headers:
- headers = []
- for i in self.headers:
- v = r.headers.get(i)
- headers.append((i, v))
- key.append(headers)
- return hashlib.sha256(repr(key)).digest()
-
- def next_flow(self, request):
- """
- Returns the next flow object, or None if no matching flow was
- found.
- """
- l = self.fmap.get(self._hash(request))
- if not l:
- return None
-
- if self.nopop:
- return l[0]
- else:
- return l.pop(0)
-
-
-class StickyCookieState:
-
- def __init__(self, flt):
- """
- flt: Compiled filter.
- """
- self.jar = defaultdict(dict)
- self.flt = flt
-
- def ckey(self, attrs, f):
- """
- Returns a (domain, port, path) tuple.
- """
- domain = f.request.host
- path = "/"
- if "domain" in attrs:
- domain = attrs["domain"]
- if "path" in attrs:
- path = attrs["path"]
- return (domain, f.request.port, path)
-
- def domain_match(self, a, b):
- if http_cookiejar.domain_match(a, b):
- return True
- elif http_cookiejar.domain_match(a, b.strip(".")):
- return True
- return False
-
- def handle_response(self, f):
- for name, (value, attrs) in f.response.cookies.items(multi=True):
- # FIXME: We now know that Cookie.py screws up some cookies with
- # valid RFC 822/1123 datetime specifications for expiry. Sigh.
- a = self.ckey(attrs, f)
- if self.domain_match(f.request.host, a[0]):
- b = attrs.with_insert(0, name, value)
- self.jar[a][name] = b
-
- def handle_request(self, f):
- l = []
- if f.match(self.flt):
- for domain, port, path in self.jar.keys():
- match = [
- self.domain_match(f.request.host, domain),
- f.request.port == port,
- f.request.path.startswith(path)
- ]
- if all(match):
- c = self.jar[(domain, port, path)]
- l.extend([cookies.format_cookie_header(c[name].items(multi=True)) for name in c.keys()])
- if l:
- f.request.stickycookie = True
- f.request.headers["cookie"] = "; ".join(l)
-
-
-class StickyAuthState:
-
- def __init__(self, flt):
- """
- flt: Compiled filter.
- """
- self.flt = flt
- self.hosts = {}
-
- def handle_request(self, f):
- host = f.request.host
- if "authorization" in f.request.headers:
- self.hosts[host] = f.request.headers["authorization"]
- elif f.match(self.flt):
- if host in self.hosts:
- f.request.headers["authorization"] = self.hosts[host]
-
-
-@six.add_metaclass(ABCMeta)
-class FlowList(object):
-
- def __init__(self):
- self._list = [] # type: List[Flow]
-
- def __iter__(self):
- return iter(self._list)
-
- def __contains__(self, item):
- return item in self._list
-
- def __getitem__(self, item):
- return self._list[item]
-
- def __bool__(self):
- return bool(self._list)
-
- if six.PY2:
- __nonzero__ = __bool__
-
- def __len__(self):
- return len(self._list)
-
- def index(self, f):
- return self._list.index(f)
-
- @abstractmethod
- def _add(self, f):
- return
-
- @abstractmethod
- def _update(self, f):
- return
-
- @abstractmethod
- def _remove(self, f):
- return
-
-
-class FlowView(FlowList):
-
- def __init__(self, store, filt=None):
- super(FlowView, self).__init__()
- if not filt:
- filt = lambda flow: True
- self._build(store, filt)
-
- self.store = store
- self.store.views.append(self)
-
- def _close(self):
- self.store.views.remove(self)
-
- def _build(self, flows, filt=None):
- if filt:
- self.filt = filt
- self._list = list(filter(self.filt, flows))
-
- def _add(self, f):
- if self.filt(f):
- self._list.append(f)
-
- def _update(self, f):
- if f not in self._list:
- self._add(f)
- elif not self.filt(f):
- self._remove(f)
-
- def _remove(self, f):
- if f in self._list:
- self._list.remove(f)
-
- def _recalculate(self, flows):
- self._build(flows)
-
-
-class FlowStore(FlowList):
-
- """
- Responsible for handling flows in the state:
- Keeps a list of all flows and provides views on them.
- """
-
- def __init__(self):
- super(FlowStore, self).__init__()
- self._set = set() # Used for O(1) lookups
- self.views = []
- self._recalculate_views()
-
- def get(self, flow_id):
- for f in self._list:
- if f.id == flow_id:
- return f
-
- def __contains__(self, f):
- return f in self._set
-
- def _add(self, f):
- """
- Adds a flow to the state.
- The flow to add must not be present in the state.
- """
- self._list.append(f)
- self._set.add(f)
- for view in self.views:
- view._add(f)
-
- def _update(self, f):
- """
- Notifies the state that a flow has been updated.
- The flow must be present in the state.
- """
- if f in self:
- for view in self.views:
- view._update(f)
-
- def _remove(self, f):
- """
- Deletes a flow from the state.
- The flow must be present in the state.
- """
- self._list.remove(f)
- self._set.remove(f)
- for view in self.views:
- view._remove(f)
-
- # Expensive bulk operations
-
- def _extend(self, flows):
- """
- Adds a list of flows to the state.
- The list of flows to add must not contain flows that are already in the state.
- """
- self._list.extend(flows)
- self._set.update(flows)
- self._recalculate_views()
-
- def _clear(self):
- self._list = []
- self._set = set()
- self._recalculate_views()
-
- def _recalculate_views(self):
- """
- Expensive operation: Recalculate all the views after a bulk change.
- """
- for view in self.views:
- view._recalculate(self)
-
- # Utility functions.
- # There are some common cases where we need to argue about all flows
- # irrespective of filters on the view etc (i.e. on shutdown).
-
- def active_count(self):
- c = 0
- for i in self._list:
- if not i.response and not i.error:
- c += 1
- return c
-
- # TODO: Should accept_all operate on views or on all flows?
- def accept_all(self, master):
- for f in self._list:
- f.accept_intercept(master)
-
- def kill_all(self, master):
- for f in self._list:
- f.kill(master)
-
-
-class State(object):
-
- def __init__(self):
- self.flows = FlowStore()
- self.view = FlowView(self.flows, None)
-
- # These are compiled filt expressions:
- self.intercept = None
-
- @property
- def limit_txt(self):
- return getattr(self.view.filt, "pattern", None)
-
- def flow_count(self):
- return len(self.flows)
-
- # TODO: All functions regarding flows that don't cause side-effects should
- # be moved into FlowStore.
- def index(self, f):
- return self.flows.index(f)
-
- def active_flow_count(self):
- return self.flows.active_count()
-
- def add_flow(self, f):
- """
- Add a request to the state.
- """
- self.flows._add(f)
- return f
-
- def update_flow(self, f):
- """
- Add a response to the state.
- """
- self.flows._update(f)
- return f
-
- def delete_flow(self, f):
- self.flows._remove(f)
-
- def load_flows(self, flows):
- self.flows._extend(flows)
-
- def set_limit(self, txt):
- if txt == self.limit_txt:
- return
- if txt:
- f = filt.parse(txt)
- if not f:
- return "Invalid filter expression."
- self.view._close()
- self.view = FlowView(self.flows, f)
- else:
- self.view._close()
- self.view = FlowView(self.flows, None)
-
- def set_intercept(self, txt):
- if txt:
- f = filt.parse(txt)
- if not f:
- return "Invalid filter expression."
- self.intercept = f
- else:
- self.intercept = None
-
- @property
- def intercept_txt(self):
- return getattr(self.intercept, "pattern", None)
-
- def clear(self):
- self.flows._clear()
-
- def accept_all(self, master):
- self.flows.accept_all(master)
-
- def backup(self, f):
- f.backup()
- self.update_flow(f)
-
- def revert(self, f):
- f.revert()
- self.update_flow(f)
-
- def killall(self, master):
- self.flows.kill_all(master)
-
-
-class FlowMaster(controller.ServerMaster):
-
- @property
- def server(self):
- # At some point, we may want to have support for multiple servers.
- # For now, this suffices.
- if len(self.servers) > 0:
- return self.servers[0]
-
- def __init__(self, server, state):
- super(FlowMaster, self).__init__()
- if server:
- self.add_server(server)
- self.state = state
- self.active_flows = set() # type: Set[Flow]
- self.server_playback = None # type: Optional[ServerPlaybackState]
- self.client_playback = None # type: Optional[ClientPlaybackState]
- self.kill_nonreplay = False
- self.scripts = [] # type: List[script.Script]
- self.pause_scripts = False
-
- self.stickycookie_state = None # type: Optional[StickyCookieState]
- self.stickycookie_txt = None
-
- self.stickyauth_state = False # type: Optional[StickyAuthState]
- self.stickyauth_txt = None
-
- self.anticache = False
- self.anticomp = False
- self.stream_large_bodies = None # type: Optional[StreamLargeBodies]
- self.refresh_server_playback = False
- self.replacehooks = ReplaceHooks()
- self.setheaders = SetHeaders()
- self.replay_ignore_params = False
- self.replay_ignore_content = None
- self.replay_ignore_host = False
-
- self.stream = None
- self.apps = AppRegistry()
-
- def start_app(self, host, port):
- self.apps.add(
- app.mapp,
- host,
- port
- )
-
- def add_event(self, e, level="info"):
- """
- level: debug, info, error
- """
-
- def unload_scripts(self):
- for s in self.scripts[:]:
- self.unload_script(s)
-
- def unload_script(self, script_obj):
- try:
- script_obj.unload()
- except script.ScriptException as e:
- self.add_event("Script error:\n" + str(e), "error")
- script.reloader.unwatch(script_obj)
- self.scripts.remove(script_obj)
-
- def load_script(self, command, use_reloader=False):
- """
- Loads a script.
-
- Raises:
- ScriptException
- """
- s = script.Script(command, script.ScriptContext(self))
- s.load()
- if use_reloader:
- script.reloader.watch(s, lambda: self.event_queue.put(("script_change", s)))
- self.scripts.append(s)
-
- def _run_single_script_hook(self, script_obj, name, *args, **kwargs):
- if script_obj and not self.pause_scripts:
- try:
- script_obj.run(name, *args, **kwargs)
- except script.ScriptException as e:
- self.add_event("Script error:\n{}".format(e), "error")
-
- def run_script_hook(self, name, *args, **kwargs):
- for script_obj in self.scripts:
- self._run_single_script_hook(script_obj, name, *args, **kwargs)
-
- def get_ignore_filter(self):
- return self.server.config.check_ignore.patterns
-
- def set_ignore_filter(self, host_patterns):
- self.server.config.check_ignore = HostMatcher(host_patterns)
-
- def get_tcp_filter(self):
- return self.server.config.check_tcp.patterns
-
- def set_tcp_filter(self, host_patterns):
- self.server.config.check_tcp = HostMatcher(host_patterns)
-
- def set_stickycookie(self, txt):
- if txt:
- flt = filt.parse(txt)
- if not flt:
- return "Invalid filter expression."
- self.stickycookie_state = StickyCookieState(flt)
- self.stickycookie_txt = txt
- else:
- self.stickycookie_state = None
- self.stickycookie_txt = None
-
- def set_stream_large_bodies(self, max_size):
- if max_size is not None:
- self.stream_large_bodies = StreamLargeBodies(max_size)
- else:
- self.stream_large_bodies = False
-
- def set_stickyauth(self, txt):
- if txt:
- flt = filt.parse(txt)
- if not flt:
- return "Invalid filter expression."
- self.stickyauth_state = StickyAuthState(flt)
- self.stickyauth_txt = txt
- else:
- self.stickyauth_state = None
- self.stickyauth_txt = None
-
- def start_client_playback(self, flows, exit):
- """
- flows: List of flows.
- """
- self.client_playback = ClientPlaybackState(flows, exit)
-
- def stop_client_playback(self):
- self.client_playback = None
-
- def start_server_playback(
- self,
- flows,
- kill,
- headers,
- exit,
- nopop,
- ignore_params,
- ignore_content,
- ignore_payload_params,
- ignore_host):
- """
- flows: List of flows.
- kill: Boolean, should we kill requests not part of the replay?
- ignore_params: list of parameters to ignore in server replay
- ignore_content: true if request content should be ignored in server replay
- ignore_payload_params: list of content params to ignore in server replay
- ignore_host: true if request host should be ignored in server replay
- """
- self.server_playback = ServerPlaybackState(
- headers,
- flows,
- exit,
- nopop,
- ignore_params,
- ignore_content,
- ignore_payload_params,
- ignore_host)
- self.kill_nonreplay = kill
-
- def stop_server_playback(self):
- self.server_playback = None
-
- def do_server_playback(self, flow):
- """
- This method should be called by child classes in the handle_request
- handler. Returns True if playback has taken place, None if not.
- """
- if self.server_playback:
- rflow = self.server_playback.next_flow(flow)
- if not rflow:
- return None
- response = rflow.response.copy()
- response.is_replay = True
- if self.refresh_server_playback:
- response.refresh()
- flow.response = response
- return True
- return None
-
- def tick(self, timeout):
- if self.client_playback:
- stop = (
- self.client_playback.done() and
- self.state.active_flow_count() == 0
- )
- exit = self.client_playback.exit
- if stop:
- self.stop_client_playback()
- if exit:
- self.shutdown()
- else:
- self.client_playback.tick(self)
-
- if self.server_playback:
- stop = (
- self.server_playback.count() == 0 and
- self.state.active_flow_count() == 0 and
- not self.kill_nonreplay
- )
- exit = self.server_playback.exit
- if stop:
- self.stop_server_playback()
- if exit:
- self.shutdown()
- return super(FlowMaster, self).tick(timeout)
-
- def duplicate_flow(self, f):
- f2 = f.copy()
- self.load_flow(f2)
- return f2
-
- def create_request(self, method, scheme, host, port, path):
- """
- this method creates a new artificial and minimalist request also adds it to flowlist
- """
- c = ClientConnection.make_dummy(("", 0))
- s = ServerConnection.make_dummy((host, port))
-
- f = HTTPFlow(c, s)
- headers = Headers()
-
- req = HTTPRequest(
- "absolute",
- method,
- scheme,
- host,
- port,
- path,
- b"HTTP/1.1",
- headers,
- b""
- )
- f.request = req
- self.load_flow(f)
- return f
-
- def load_flow(self, f):
- """
- Loads a flow
- """
- if isinstance(f, HTTPFlow):
- if self.server and self.server.config.mode == "reverse":
- f.request.host = self.server.config.upstream_server.address.host
- f.request.port = self.server.config.upstream_server.address.port
- f.request.scheme = self.server.config.upstream_server.scheme
-
- f.reply = controller.DummyReply()
- if f.request:
- self.handle_request(f)
- if f.response:
- self.handle_responseheaders(f)
- self.handle_response(f)
- if f.error:
- self.handle_error(f)
- elif isinstance(f, TCPFlow):
- messages = f.messages
- f.messages = []
- f.reply = controller.DummyReply()
- self.handle_tcp_open(f)
- while messages:
- f.messages.append(messages.pop(0))
- self.handle_tcp_message(f)
- if f.error:
- self.handle_tcp_error(f)
- self.handle_tcp_close(f)
- else:
- raise NotImplementedError()
-
- def load_flows(self, fr):
- """
- Load flows from a FlowReader object.
- """
- cnt = 0
- for i in fr.stream():
- cnt += 1
- self.load_flow(i)
- return cnt
-
- def load_flows_file(self, path):
- path = os.path.expanduser(path)
- try:
- if path == "-":
- # This is incompatible with Python 3 - maybe we can use click?
- freader = FlowReader(sys.stdin)
- return self.load_flows(freader)
- else:
- with open(path, "rb") as f:
- freader = FlowReader(f)
- return self.load_flows(freader)
- except IOError as v:
- raise FlowReadException(v.strerror)
-
- def process_new_request(self, f):
- if self.stickycookie_state:
- self.stickycookie_state.handle_request(f)
- if self.stickyauth_state:
- self.stickyauth_state.handle_request(f)
-
- if self.anticache:
- f.request.anticache()
- if self.anticomp:
- f.request.anticomp()
-
- if self.server_playback:
- pb = self.do_server_playback(f)
- if not pb and self.kill_nonreplay:
- f.kill(self)
-
- def process_new_response(self, f):
- if self.stickycookie_state:
- self.stickycookie_state.handle_response(f)
-
- def replay_request(self, f, block=False, run_scripthooks=True):
- """
- Returns None if successful, or error message if not.
- """
- if f.live and run_scripthooks:
- return "Can't replay live request."
- if f.intercepted:
- return "Can't replay while intercepting..."
- if f.request.content is None:
- return "Can't replay request with missing content..."
- if f.request:
- f.backup()
- f.request.is_replay = True
- if "Content-Length" in f.request.headers:
- f.request.headers["Content-Length"] = str(len(f.request.content))
- f.response = None
- f.error = None
- self.process_new_request(f)
- rt = RequestReplayThread(
- self.server.config,
- f,
- self.event_queue if run_scripthooks else False,
- self.should_exit
- )
- rt.start() # pragma: no cover
- if block:
- rt.join()
-
- def handle_log(self, l):
- self.add_event(l.msg, l.level)
- l.reply()
-
- def handle_clientconnect(self, root_layer):
- self.run_script_hook("clientconnect", root_layer)
- root_layer.reply()
-
- def handle_clientdisconnect(self, root_layer):
- self.run_script_hook("clientdisconnect", root_layer)
- root_layer.reply()
-
- def handle_serverconnect(self, server_conn):
- self.run_script_hook("serverconnect", server_conn)
- server_conn.reply()
-
- def handle_serverdisconnect(self, server_conn):
- self.run_script_hook("serverdisconnect", server_conn)
- server_conn.reply()
-
- def handle_next_layer(self, top_layer):
- self.run_script_hook("next_layer", top_layer)
- top_layer.reply()
-
- def handle_error(self, f):
- self.state.update_flow(f)
- self.run_script_hook("error", f)
- if self.client_playback:
- self.client_playback.clear(f)
- f.reply()
- return f
-
- def handle_request(self, f):
- if f.live:
- app = self.apps.get(f.request)
- if app:
- err = app.serve(
- f,
- f.client_conn.wfile,
- **{"mitmproxy.master": self}
- )
- if err:
- self.add_event("Error in wsgi app. %s" % err, "error")
- f.reply(Kill)
- return
- if f not in self.state.flows: # don't add again on replay
- self.state.add_flow(f)
- self.active_flows.add(f)
- self.replacehooks.run(f)
- self.setheaders.run(f)
- self.process_new_request(f)
- self.run_script_hook("request", f)
- return f
-
- def handle_responseheaders(self, f):
- try:
- if self.stream_large_bodies:
- self.stream_large_bodies.run(f, False)
- except HttpException:
- f.reply(Kill)
- return
-
- self.run_script_hook("responseheaders", f)
-
- f.reply()
- return f
-
- def handle_response(self, f):
- self.active_flows.discard(f)
- self.state.update_flow(f)
- self.replacehooks.run(f)
- self.setheaders.run(f)
- self.run_script_hook("response", f)
- if self.client_playback:
- self.client_playback.clear(f)
- self.process_new_response(f)
- if self.stream:
- self.stream.add(f)
- return f
-
- def handle_intercept(self, f):
- self.state.update_flow(f)
-
- def handle_accept_intercept(self, f):
- self.state.update_flow(f)
-
- def handle_script_change(self, s):
- """
- Handle a script whose contents have been changed on the file system.
-
- Args:
- s (script.Script): the changed script
-
- Returns:
- True, if reloading was successful.
- False, otherwise.
- """
- ok = True
- # We deliberately do not want to fail here.
- # In the worst case, we have an "empty" script object.
- try:
- s.unload()
- except script.ScriptException as e:
- ok = False
- self.add_event('Error reloading "{}":\n{}'.format(s.filename, e), 'error')
- try:
- s.load()
- except script.ScriptException as e:
- ok = False
- self.add_event('Error reloading "{}":\n{}'.format(s.filename, e), 'error')
- else:
- self.add_event('"{}" reloaded.'.format(s.filename), 'info')
- return ok
-
- def handle_tcp_open(self, flow):
- # TODO: This would break mitmproxy currently.
- # self.state.add_flow(flow)
- self.active_flows.add(flow)
- self.run_script_hook("tcp_open", flow)
- flow.reply()
-
- def handle_tcp_message(self, flow):
- self.run_script_hook("tcp_message", flow)
- message = flow.messages[-1]
- direction = "->" if message.from_client else "<-"
- self.add_event("{client} {direction} tcp {direction} {server}".format(
- client=repr(flow.client_conn.address),
- server=repr(flow.server_conn.address),
- direction=direction,
- ), "info")
- self.add_event(clean_bin(message.content), "debug")
- flow.reply()
-
- def handle_tcp_error(self, flow):
- self.add_event("Error in TCP connection to {}: {}".format(
- repr(flow.server_conn.address),
- flow.error
- ), "info")
- self.run_script_hook("tcp_error", flow)
- flow.reply()
-
- def handle_tcp_close(self, flow):
- self.active_flows.discard(flow)
- if self.stream:
- self.stream.add(flow)
- self.run_script_hook("tcp_close", flow)
- flow.reply()
-
- def shutdown(self):
- super(FlowMaster, self).shutdown()
-
- # Add all flows that are still active
- if self.stream:
- for flow in self.active_flows:
- self.stream.add(flow)
- self.stop_stream()
-
- self.unload_scripts()
-
- def start_stream(self, fp, filt):
- self.stream = FilteredFlowWriter(fp, filt)
-
- def stop_stream(self):
- self.stream.fo.close()
- self.stream = None
-
- def start_stream_to_path(self, path, mode="wb", filt=None):
- path = os.path.expanduser(path)
- try:
- f = open(path, mode)
- self.start_stream(f, filt)
- except IOError as v:
- return str(v)
- self.stream_path = path
-
-
-def read_flows_from_paths(paths):
- """
- Given a list of filepaths, read all flows and return a list of them.
- From a performance perspective, streaming would be advisable -
- however, if there's an error with one of the files, we want it to be raised immediately.
-
- Raises:
- FlowReadException, if any error occurs.
- """
- try:
- flows = []
- for path in paths:
- path = os.path.expanduser(path)
- with open(path, "rb") as f:
- flows.extend(FlowReader(f).stream())
- except IOError as e:
- raise FlowReadException(e.strerror)
- return flows
-
-
-class FlowWriter:
-
- def __init__(self, fo):
- self.fo = fo
-
- def add(self, flow):
- d = flow.get_state()
- tnetstring.dump(d, self.fo)
-
-
-class FlowReader:
-
- def __init__(self, fo):
- self.fo = fo
-
- def stream(self):
- """
- Yields Flow objects from the dump.
- """
-
- # There is a weird mingw bug that breaks .tell() when reading from stdin.
- try:
- self.fo.tell()
- except IOError: # pragma: no cover
- can_tell = False
- else:
- can_tell = True
-
- off = 0
- try:
- while True:
- data = tnetstring.load(self.fo)
- try:
- data = flow_format_compat.migrate_flow(data)
- except ValueError as e:
- raise FlowReadException(str(e))
- if can_tell:
- off = self.fo.tell()
- if data["type"] not in FLOW_TYPES:
- raise FlowReadException("Unknown flow type: {}".format(data["type"]))
- yield FLOW_TYPES[data["type"]].from_state(data)
- except ValueError:
- # Error is due to EOF
- if can_tell and self.fo.tell() == off and self.fo.read() == '':
- return
- raise FlowReadException("Invalid data format.")
-
-
-class FilteredFlowWriter:
-
- def __init__(self, fo, filt):
- self.fo = fo
- self.filt = filt
-
- def add(self, f):
- if self.filt and not f.match(self.filt):
- return
- d = f.get_state()
- tnetstring.dump(d, self.fo)
diff --git a/mitmproxy/flow/__init__.py b/mitmproxy/flow/__init__.py
new file mode 100644
index 00000000..c14a0fec
--- /dev/null
+++ b/mitmproxy/flow/__init__.py
@@ -0,0 +1,21 @@
+from __future__ import absolute_import, print_function, division
+
+from mitmproxy.flow import export, modules
+from mitmproxy.flow.io import FlowWriter, FilteredFlowWriter, FlowReader, read_flows_from_paths
+from mitmproxy.flow.master import FlowMaster
+from mitmproxy.flow.modules import (
+ AppRegistry, ReplaceHooks, SetHeaders, StreamLargeBodies, ClientPlaybackState,
+ ServerPlaybackState, StickyCookieState, StickyAuthState
+)
+from mitmproxy.flow.state import State, FlowView
+
+# TODO: We may want to remove the imports from .modules and just expose "modules"
+
+__all__ = [
+ "export", "modules",
+ "FlowWriter", "FilteredFlowWriter", "FlowReader", "read_flows_from_paths",
+ "FlowMaster",
+ "AppRegistry", "ReplaceHooks", "SetHeaders", "StreamLargeBodies", "ClientPlaybackState",
+ "ServerPlaybackState", "StickyCookieState", "StickyAuthState",
+ "State", "FlowView",
+]
diff --git a/mitmproxy/flow_export.py b/mitmproxy/flow/export.py
index ae282fce..f0ac02ab 100644
--- a/mitmproxy/flow_export.py
+++ b/mitmproxy/flow/export.py
@@ -1,11 +1,20 @@
+from __future__ import absolute_import, print_function, division
+
import json
+import re
from textwrap import dedent
+from six.moves.urllib.parse import quote, quote_plus
+
import netlib.http
-from netlib.utils import parse_content_type
-import re
-from six.moves.urllib.parse import urlparse, quote, quote_plus
+
+def dictstr(items, indent):
+ lines = []
+ for k, v in items:
+ lines.append(indent + "%s: %s,\n" % (repr(k), repr(v)))
+ return "{\n%s}\n" % "".join(lines)
+
def curl_command(flow):
data = "curl "
@@ -45,24 +54,19 @@ def python_code(flow):
args = ""
headers = ""
if flow.request.headers:
- lines = [" '%s': '%s',\n" % (k, v) for k, v in flow.request.headers.fields]
- headers += "\nheaders = {\n%s}\n" % "".join(lines)
+ headers += "\nheaders = %s\n" % dictstr(flow.request.headers.fields, " ")
args += "\n headers=headers,"
params = ""
if flow.request.query:
- lines = [" %s: %s,\n" % (repr(k), repr(v)) for k, v in flow.request.query.to_dict().items()]
- params = "\nparams = {\n%s}\n" % "".join(lines)
+ params = "\nparams = %s\n" % dictstr(flow.request.query.collect(), " ")
args += "\n params=params,"
data = ""
if flow.request.body:
json_obj = is_json(flow.request.headers, flow.request.body)
if json_obj:
- # Without the separators field json.dumps() produces
- # trailing white spaces: https://bugs.python.org/issue16333
- data = json.dumps(json_obj, indent=4, separators=(',', ': '))
- data = "\njson = %s\n" % data
+ data = "\njson = %s\n" % dictstr(sorted(json_obj.items()), " ")
args += "\n json=json,"
else:
data = "\ndata = '''%s'''\n" % flow.request.body
@@ -76,7 +80,6 @@ def python_code(flow):
method=flow.request.method,
args=args,
)
-
return code
@@ -87,7 +90,7 @@ def raw_request(flow):
def is_json(headers, content):
if headers:
- ct = parse_content_type(headers.get("content-type", ""))
+ ct = netlib.http.parse_content_type(headers.get("content-type", ""))
if ct and "%s/%s" % (ct[0], ct[1]) == "application/json":
try:
return json.loads(content)
@@ -123,24 +126,24 @@ def locust_code(flow):
max_wait = 3000
""").strip()
-
components = map(lambda x: quote(x, safe=""), flow.request.path_components)
file_name = "_".join(components)
name = re.sub('\W|^(?=\d)', '_', file_name)
url = flow.request.scheme + "://" + flow.request.host + "/" + "/".join(components)
if name == "" or name is None:
- new_name = "_".join([str(flow.request.host) , str(flow.request.timestamp_start)])
+ new_name = "_".join([str(flow.request.host), str(flow.request.timestamp_start)])
name = re.sub('\W|^(?=\d)', '_', new_name)
args = ""
headers = ""
if flow.request.headers:
- lines = [" '%s': '%s',\n" % (k, v) for k, v in flow.request.headers.fields if k.lower() not in ["host", "cookie"]]
+ lines = [(k, v) for k, v in flow.request.headers.fields if k.lower() not in ["host", "cookie"]]
+ lines = [" '%s': '%s',\n" % (k, v) for k, v in lines]
headers += "\n headers = {\n%s }\n" % "".join(lines)
args += "\n headers=headers,"
params = ""
if flow.request.query:
- lines = [" %s: %s,\n" % (repr(k), repr(v)) for k, v in flow.request.query.to_dict().items()]
+ lines = [" %s: %s,\n" % (repr(k), repr(v)) for k, v in flow.request.query.collect()]
params = "\n params = {\n%s }\n" % "".join(lines)
args += "\n params=params,"
diff --git a/mitmproxy/flow/io.py b/mitmproxy/flow/io.py
new file mode 100644
index 00000000..cd3d9986
--- /dev/null
+++ b/mitmproxy/flow/io.py
@@ -0,0 +1,86 @@
+from __future__ import absolute_import, print_function, division
+
+import os
+
+from mitmproxy import exceptions
+from mitmproxy import models
+from mitmproxy import tnetstring
+from mitmproxy.flow import io_compat
+
+
+class FlowWriter:
+ def __init__(self, fo):
+ self.fo = fo
+
+ def add(self, flow):
+ d = flow.get_state()
+ tnetstring.dump(d, self.fo)
+
+
+class FlowReader:
+ def __init__(self, fo):
+ self.fo = fo
+
+ def stream(self):
+ """
+ Yields Flow objects from the dump.
+ """
+
+ # There is a weird mingw bug that breaks .tell() when reading from stdin.
+ try:
+ self.fo.tell()
+ except IOError: # pragma: no cover
+ can_tell = False
+ else:
+ can_tell = True
+
+ off = 0
+ try:
+ while True:
+ data = tnetstring.load(self.fo)
+ try:
+ data = io_compat.migrate_flow(data)
+ except ValueError as e:
+ raise exceptions.FlowReadException(str(e))
+ if can_tell:
+ off = self.fo.tell()
+ if data["type"] not in models.FLOW_TYPES:
+ raise exceptions.FlowReadException("Unknown flow type: {}".format(data["type"]))
+ yield models.FLOW_TYPES[data["type"]].from_state(data)
+ except ValueError:
+ # Error is due to EOF
+ if can_tell and self.fo.tell() == off and self.fo.read() == '':
+ return
+ raise exceptions.FlowReadException("Invalid data format.")
+
+
+class FilteredFlowWriter:
+ def __init__(self, fo, filt):
+ self.fo = fo
+ self.filt = filt
+
+ def add(self, f):
+ if self.filt and not f.match(self.filt):
+ return
+ d = f.get_state()
+ tnetstring.dump(d, self.fo)
+
+
+def read_flows_from_paths(paths):
+ """
+ Given a list of filepaths, read all flows and return a list of them.
+ From a performance perspective, streaming would be advisable -
+ however, if there's an error with one of the files, we want it to be raised immediately.
+
+ Raises:
+ FlowReadException, if any error occurs.
+ """
+ try:
+ flows = []
+ for path in paths:
+ path = os.path.expanduser(path)
+ with open(path, "rb") as f:
+ flows.extend(FlowReader(f).stream())
+ except IOError as e:
+ raise exceptions.FlowReadException(e.strerror)
+ return flows
diff --git a/mitmproxy/flow_format_compat.py b/mitmproxy/flow/io_compat.py
index 8478367c..7522163f 100644
--- a/mitmproxy/flow_format_compat.py
+++ b/mitmproxy/flow/io_compat.py
@@ -2,7 +2,8 @@
This module handles the import of mitmproxy flows generated by old versions.
"""
from __future__ import absolute_import, print_function, division
-from . import version
+
+from mitmproxy import version
def convert_013_014(data):
@@ -64,5 +65,7 @@ def migrate_flow(flow_data):
flow_data = converters[flow_version](flow_data)
else:
v = ".".join(str(i) for i in flow_data["version"])
- raise ValueError("Incompatible serialized data version: {}".format(v))
+ raise ValueError(
+ "{} cannot read files serialized with version {}.".format(version.NAMEVERSION, v)
+ )
return flow_data
diff --git a/mitmproxy/flow/master.py b/mitmproxy/flow/master.py
new file mode 100644
index 00000000..ec0bf36d
--- /dev/null
+++ b/mitmproxy/flow/master.py
@@ -0,0 +1,544 @@
+from __future__ import absolute_import, print_function, division
+
+import os
+import sys
+
+from typing import List, Optional, Set # noqa
+
+import netlib.exceptions
+from mitmproxy import controller
+from mitmproxy import exceptions
+from mitmproxy import filt
+from mitmproxy import models
+from mitmproxy import script
+from mitmproxy.flow import io
+from mitmproxy.flow import modules
+from mitmproxy.onboarding import app
+from mitmproxy.protocol import http_replay
+from mitmproxy.proxy.config import HostMatcher
+from netlib import strutils
+
+
+class FlowMaster(controller.Master):
+
+ @property
+ def server(self):
+ # At some point, we may want to have support for multiple servers.
+ # For now, this suffices.
+ if len(self.servers) > 0:
+ return self.servers[0]
+
+ def __init__(self, server, state):
+ super(FlowMaster, self).__init__()
+ if server:
+ self.add_server(server)
+ self.state = state
+ self.active_flows = set() # type: Set[models.Flow]
+ self.server_playback = None # type: Optional[modules.ServerPlaybackState]
+ self.client_playback = None # type: Optional[modules.ClientPlaybackState]
+ self.kill_nonreplay = False
+ self.scripts = [] # type: List[script.Script]
+ self.pause_scripts = False
+
+ self.stickycookie_state = None # type: Optional[modules.StickyCookieState]
+ self.stickycookie_txt = None
+
+ self.stickyauth_state = False # type: Optional[modules.StickyAuthState]
+ self.stickyauth_txt = None
+
+ self.anticache = False
+ self.anticomp = False
+ self.stream_large_bodies = None # type: Optional[modules.StreamLargeBodies]
+ self.refresh_server_playback = False
+ self.replacehooks = modules.ReplaceHooks()
+ self.setheaders = modules.SetHeaders()
+ self.replay_ignore_params = False
+ self.replay_ignore_content = None
+ self.replay_ignore_host = False
+
+ self.stream = None
+ self.apps = modules.AppRegistry()
+
+ def start_app(self, host, port):
+ self.apps.add(
+ app.mapp,
+ host,
+ port
+ )
+
+ def add_event(self, e, level="info"):
+ """
+ level: debug, info, error
+ """
+
+ def unload_scripts(self):
+ for s in self.scripts[:]:
+ self.unload_script(s)
+
+ def unload_script(self, script_obj):
+ try:
+ script_obj.unload()
+ except script.ScriptException as e:
+ self.add_event("Script error:\n" + str(e), "error")
+ script.reloader.unwatch(script_obj)
+ self.scripts.remove(script_obj)
+
+ def load_script(self, command, use_reloader=False):
+ """
+ Loads a script.
+
+ Raises:
+ ScriptException
+ """
+ s = script.Script(command, script.ScriptContext(self))
+ s.load()
+ if use_reloader:
+ script.reloader.watch(s, lambda: self.event_queue.put(("script_change", s)))
+ self.scripts.append(s)
+
+ def _run_single_script_hook(self, script_obj, name, *args, **kwargs):
+ if script_obj and not self.pause_scripts:
+ try:
+ script_obj.run(name, *args, **kwargs)
+ except script.ScriptException as e:
+ self.add_event("Script error:\n{}".format(e), "error")
+
+ def run_script_hook(self, name, *args, **kwargs):
+ for script_obj in self.scripts:
+ self._run_single_script_hook(script_obj, name, *args, **kwargs)
+
+ def get_ignore_filter(self):
+ return self.server.config.check_ignore.patterns
+
+ def set_ignore_filter(self, host_patterns):
+ self.server.config.check_ignore = HostMatcher(host_patterns)
+
+ def get_tcp_filter(self):
+ return self.server.config.check_tcp.patterns
+
+ def set_tcp_filter(self, host_patterns):
+ self.server.config.check_tcp = HostMatcher(host_patterns)
+
+ def set_stickycookie(self, txt):
+ if txt:
+ flt = filt.parse(txt)
+ if not flt:
+ return "Invalid filter expression."
+ self.stickycookie_state = modules.StickyCookieState(flt)
+ self.stickycookie_txt = txt
+ else:
+ self.stickycookie_state = None
+ self.stickycookie_txt = None
+
+ def set_stream_large_bodies(self, max_size):
+ if max_size is not None:
+ self.stream_large_bodies = modules.StreamLargeBodies(max_size)
+ else:
+ self.stream_large_bodies = False
+
+ def set_stickyauth(self, txt):
+ if txt:
+ flt = filt.parse(txt)
+ if not flt:
+ return "Invalid filter expression."
+ self.stickyauth_state = modules.StickyAuthState(flt)
+ self.stickyauth_txt = txt
+ else:
+ self.stickyauth_state = None
+ self.stickyauth_txt = None
+
+ def start_client_playback(self, flows, exit):
+ """
+ flows: List of flows.
+ """
+ self.client_playback = modules.ClientPlaybackState(flows, exit)
+
+ def stop_client_playback(self):
+ self.client_playback = None
+
+ def start_server_playback(
+ self,
+ flows,
+ kill,
+ headers,
+ exit,
+ nopop,
+ ignore_params,
+ ignore_content,
+ ignore_payload_params,
+ ignore_host):
+ """
+ flows: List of flows.
+ kill: Boolean, should we kill requests not part of the replay?
+ ignore_params: list of parameters to ignore in server replay
+ ignore_content: true if request content should be ignored in server replay
+ ignore_payload_params: list of content params to ignore in server replay
+ ignore_host: true if request host should be ignored in server replay
+ """
+ self.server_playback = modules.ServerPlaybackState(
+ headers,
+ flows,
+ exit,
+ nopop,
+ ignore_params,
+ ignore_content,
+ ignore_payload_params,
+ ignore_host)
+ self.kill_nonreplay = kill
+
+ def stop_server_playback(self):
+ self.server_playback = None
+
+ def do_server_playback(self, flow):
+ """
+ This method should be called by child classes in the request
+ handler. Returns True if playback has taken place, None if not.
+ """
+ if self.server_playback:
+ rflow = self.server_playback.next_flow(flow)
+ if not rflow:
+ return None
+ response = rflow.response.copy()
+ response.is_replay = True
+ if self.refresh_server_playback:
+ response.refresh()
+ flow.response = response
+ return True
+ return None
+
+ def tick(self, timeout):
+ if self.client_playback:
+ stop = (
+ self.client_playback.done() and
+ self.state.active_flow_count() == 0
+ )
+ exit = self.client_playback.exit
+ if stop:
+ self.stop_client_playback()
+ if exit:
+ self.shutdown()
+ else:
+ self.client_playback.tick(self)
+
+ if self.server_playback:
+ stop = (
+ self.server_playback.count() == 0 and
+ self.state.active_flow_count() == 0 and
+ not self.kill_nonreplay
+ )
+ exit = self.server_playback.exit
+ if stop:
+ self.stop_server_playback()
+ if exit:
+ self.shutdown()
+ return super(FlowMaster, self).tick(timeout)
+
+ def duplicate_flow(self, f):
+ f2 = f.copy()
+ self.load_flow(f2)
+ return f2
+
+ def create_request(self, method, scheme, host, port, path):
+ """
+ this method creates a new artificial and minimalist request also adds it to flowlist
+ """
+ c = models.ClientConnection.make_dummy(("", 0))
+ s = models.ServerConnection.make_dummy((host, port))
+
+ f = models.HTTPFlow(c, s)
+ headers = models.Headers()
+
+ req = models.HTTPRequest(
+ "absolute",
+ method,
+ scheme,
+ host,
+ port,
+ path,
+ b"HTTP/1.1",
+ headers,
+ b""
+ )
+ f.request = req
+ self.load_flow(f)
+ return f
+
+ def load_flow(self, f):
+ """
+ Loads a flow
+ """
+ if isinstance(f, models.HTTPFlow):
+ if self.server and self.server.config.mode == "reverse":
+ f.request.host = self.server.config.upstream_server.address.host
+ f.request.port = self.server.config.upstream_server.address.port
+ f.request.scheme = self.server.config.upstream_server.scheme
+
+ f.reply = controller.DummyReply()
+ if f.request:
+ self.request(f)
+ if f.response:
+ self.responseheaders(f)
+ self.response(f)
+ if f.error:
+ self.error(f)
+ elif isinstance(f, models.TCPFlow):
+ messages = f.messages
+ f.messages = []
+ f.reply = controller.DummyReply()
+ self.tcp_open(f)
+ while messages:
+ f.messages.append(messages.pop(0))
+ self.tcp_message(f)
+ if f.error:
+ self.tcp_error(f)
+ self.tcp_close(f)
+ else:
+ raise NotImplementedError()
+
+ def load_flows(self, fr):
+ """
+ Load flows from a FlowReader object.
+ """
+ cnt = 0
+ for i in fr.stream():
+ cnt += 1
+ self.load_flow(i)
+ return cnt
+
+ def load_flows_file(self, path):
+ path = os.path.expanduser(path)
+ try:
+ if path == "-":
+ # This is incompatible with Python 3 - maybe we can use click?
+ freader = io.FlowReader(sys.stdin)
+ return self.load_flows(freader)
+ else:
+ with open(path, "rb") as f:
+ freader = io.FlowReader(f)
+ return self.load_flows(freader)
+ except IOError as v:
+ raise exceptions.FlowReadException(v.strerror)
+
+ def process_new_request(self, f):
+ if self.stickycookie_state:
+ self.stickycookie_state.handle_request(f)
+ if self.stickyauth_state:
+ self.stickyauth_state.handle_request(f)
+
+ if self.anticache:
+ f.request.anticache()
+ if self.anticomp:
+ f.request.anticomp()
+
+ if self.server_playback:
+ pb = self.do_server_playback(f)
+ if not pb and self.kill_nonreplay:
+ f.kill(self)
+
+ def process_new_response(self, f):
+ if self.stickycookie_state:
+ self.stickycookie_state.handle_response(f)
+
+ def replay_request(self, f, block=False, run_scripthooks=True):
+ """
+ Returns None if successful, or error message if not.
+ """
+ if f.live and run_scripthooks:
+ return "Can't replay live request."
+ if f.intercepted:
+ return "Can't replay while intercepting..."
+ if f.request.content is None:
+ return "Can't replay request with missing content..."
+ if f.request:
+ f.backup()
+ f.request.is_replay = True
+ if "Content-Length" in f.request.headers:
+ f.request.headers["Content-Length"] = str(len(f.request.content))
+ f.response = None
+ f.error = None
+ self.process_new_request(f)
+ rt = http_replay.RequestReplayThread(
+ self.server.config,
+ f,
+ self.event_queue if run_scripthooks else False,
+ self.should_exit
+ )
+ rt.start() # pragma: no cover
+ if block:
+ rt.join()
+
+ @controller.handler
+ def log(self, l):
+ self.add_event(l.msg, l.level)
+
+ @controller.handler
+ def clientconnect(self, root_layer):
+ self.run_script_hook("clientconnect", root_layer)
+
+ @controller.handler
+ def clientdisconnect(self, root_layer):
+ self.run_script_hook("clientdisconnect", root_layer)
+
+ @controller.handler
+ def serverconnect(self, server_conn):
+ self.run_script_hook("serverconnect", server_conn)
+
+ @controller.handler
+ def serverdisconnect(self, server_conn):
+ self.run_script_hook("serverdisconnect", server_conn)
+
+ @controller.handler
+ def next_layer(self, top_layer):
+ self.run_script_hook("next_layer", top_layer)
+
+ @controller.handler
+ def error(self, f):
+ self.state.update_flow(f)
+ self.run_script_hook("error", f)
+ if self.client_playback:
+ self.client_playback.clear(f)
+ return f
+
+ @controller.handler
+ def request(self, f):
+ if f.live:
+ app = self.apps.get(f.request)
+ if app:
+ err = app.serve(
+ f,
+ f.client_conn.wfile,
+ **{"mitmproxy.master": self}
+ )
+ if err:
+ self.add_event("Error in wsgi app. %s" % err, "error")
+ f.reply(exceptions.Kill)
+ return
+ if f not in self.state.flows: # don't add again on replay
+ self.state.add_flow(f)
+ self.active_flows.add(f)
+ self.replacehooks.run(f)
+ self.setheaders.run(f)
+ self.process_new_request(f)
+ self.run_script_hook("request", f)
+ return f
+
+ @controller.handler
+ def responseheaders(self, f):
+ try:
+ if self.stream_large_bodies:
+ self.stream_large_bodies.run(f, False)
+ except netlib.exceptions.HttpException:
+ f.reply(exceptions.Kill)
+ return
+ self.run_script_hook("responseheaders", f)
+ return f
+
+ @controller.handler
+ def response(self, f):
+ self.active_flows.discard(f)
+ self.state.update_flow(f)
+ self.replacehooks.run(f)
+ self.setheaders.run(f)
+ self.run_script_hook("response", f)
+ if self.client_playback:
+ self.client_playback.clear(f)
+ self.process_new_response(f)
+ if self.stream:
+ self.stream.add(f)
+ return f
+
+ def handle_intercept(self, f):
+ self.state.update_flow(f)
+
+ def handle_accept_intercept(self, f):
+ self.state.update_flow(f)
+
+ @controller.handler
+ def script_change(self, s):
+ """
+ Handle a script whose contents have been changed on the file system.
+
+ Args:
+ s (script.Script): the changed script
+
+ Returns:
+ True, if reloading was successful.
+ False, otherwise.
+ """
+ ok = True
+ # We deliberately do not want to fail here.
+ # In the worst case, we have an "empty" script object.
+ try:
+ s.unload()
+ except script.ScriptException as e:
+ ok = False
+ self.add_event('Error reloading "{}":\n{}'.format(s.filename, e), 'error')
+ try:
+ s.load()
+ except script.ScriptException as e:
+ ok = False
+ self.add_event('Error reloading "{}":\n{}'.format(s.filename, e), 'error')
+ else:
+ self.add_event('"{}" reloaded.'.format(s.filename), 'info')
+ return ok
+
+ @controller.handler
+ def tcp_open(self, flow):
+ # TODO: This would break mitmproxy currently.
+ # self.state.add_flow(flow)
+ self.active_flows.add(flow)
+ self.run_script_hook("tcp_open", flow)
+
+ @controller.handler
+ def tcp_message(self, flow):
+ self.run_script_hook("tcp_message", flow)
+ message = flow.messages[-1]
+ direction = "->" if message.from_client else "<-"
+ self.add_event("{client} {direction} tcp {direction} {server}".format(
+ client=repr(flow.client_conn.address),
+ server=repr(flow.server_conn.address),
+ direction=direction,
+ ), "info")
+ self.add_event(strutils.clean_bin(message.content), "debug")
+
+ @controller.handler
+ def tcp_error(self, flow):
+ self.add_event("Error in TCP connection to {}: {}".format(
+ repr(flow.server_conn.address),
+ flow.error
+ ), "info")
+ self.run_script_hook("tcp_error", flow)
+
+ @controller.handler
+ def tcp_close(self, flow):
+ self.active_flows.discard(flow)
+ if self.stream:
+ self.stream.add(flow)
+ self.run_script_hook("tcp_close", flow)
+
+ def shutdown(self):
+ super(FlowMaster, self).shutdown()
+
+ # Add all flows that are still active
+ if self.stream:
+ for flow in self.active_flows:
+ self.stream.add(flow)
+ self.stop_stream()
+
+ self.unload_scripts()
+
+ def start_stream(self, fp, filt):
+ self.stream = io.FilteredFlowWriter(fp, filt)
+
+ def stop_stream(self):
+ self.stream.fo.close()
+ self.stream = None
+
+ def start_stream_to_path(self, path, mode="wb", filt=None):
+ path = os.path.expanduser(path)
+ try:
+ f = open(path, mode)
+ self.start_stream(f, filt)
+ except IOError as v:
+ return str(v)
+ self.stream_path = path
diff --git a/mitmproxy/flow/modules.py b/mitmproxy/flow/modules.py
new file mode 100644
index 00000000..601ebfce
--- /dev/null
+++ b/mitmproxy/flow/modules.py
@@ -0,0 +1,358 @@
+from __future__ import absolute_import, print_function, division
+
+import collections
+import hashlib
+import re
+
+from six.moves import http_cookiejar
+from six.moves import urllib
+
+from mitmproxy import controller
+from mitmproxy import filt
+from mitmproxy import version
+from netlib import wsgi
+from netlib.http import cookies
+from netlib.http import http1
+
+
+class AppRegistry:
+ def __init__(self):
+ self.apps = {}
+
+ def add(self, app, domain, port):
+ """
+ Add a WSGI app to the registry, to be served for requests to the
+ specified domain, on the specified port.
+ """
+ self.apps[(domain, port)] = wsgi.WSGIAdaptor(
+ app,
+ domain,
+ port,
+ version.NAMEVERSION
+ )
+
+ def get(self, request):
+ """
+ Returns an WSGIAdaptor instance if request matches an app, or None.
+ """
+ if (request.host, request.port) in self.apps:
+ return self.apps[(request.host, request.port)]
+ if "host" in request.headers:
+ host = request.headers["host"]
+ return self.apps.get((host, request.port), None)
+
+
+class ReplaceHooks:
+ def __init__(self):
+ self.lst = []
+
+ def set(self, r):
+ self.clear()
+ for i in r:
+ self.add(*i)
+
+ def add(self, fpatt, rex, s):
+ """
+ add a replacement hook.
+
+ fpatt: a string specifying a filter pattern.
+ rex: a regular expression.
+ s: the replacement string
+
+ returns true if hook was added, false if the pattern could not be
+ parsed.
+ """
+ cpatt = filt.parse(fpatt)
+ if not cpatt:
+ return False
+ try:
+ re.compile(rex)
+ except re.error:
+ return False
+ self.lst.append((fpatt, rex, s, cpatt))
+ return True
+
+ def get_specs(self):
+ """
+ Retrieve the hook specifcations. Returns a list of (fpatt, rex, s)
+ tuples.
+ """
+ return [i[:3] for i in self.lst]
+
+ def count(self):
+ return len(self.lst)
+
+ def run(self, f):
+ for _, rex, s, cpatt in self.lst:
+ if cpatt(f):
+ if f.response:
+ f.response.replace(rex, s)
+ else:
+ f.request.replace(rex, s)
+
+ def clear(self):
+ self.lst = []
+
+
+class SetHeaders:
+ def __init__(self):
+ self.lst = []
+
+ def set(self, r):
+ self.clear()
+ for i in r:
+ self.add(*i)
+
+ def add(self, fpatt, header, value):
+ """
+ Add a set header hook.
+
+ fpatt: String specifying a filter pattern.
+ header: Header name.
+ value: Header value string
+
+ Returns True if hook was added, False if the pattern could not be
+ parsed.
+ """
+ cpatt = filt.parse(fpatt)
+ if not cpatt:
+ return False
+ self.lst.append((fpatt, header, value, cpatt))
+ return True
+
+ def get_specs(self):
+ """
+ Retrieve the hook specifcations. Returns a list of (fpatt, rex, s)
+ tuples.
+ """
+ return [i[:3] for i in self.lst]
+
+ def count(self):
+ return len(self.lst)
+
+ def clear(self):
+ self.lst = []
+
+ def run(self, f):
+ for _, header, value, cpatt in self.lst:
+ if cpatt(f):
+ if f.response:
+ f.response.headers.pop(header, None)
+ else:
+ f.request.headers.pop(header, None)
+ for _, header, value, cpatt in self.lst:
+ if cpatt(f):
+ if f.response:
+ f.response.headers.add(header, value)
+ else:
+ f.request.headers.add(header, value)
+
+
+class StreamLargeBodies(object):
+ def __init__(self, max_size):
+ self.max_size = max_size
+
+ def run(self, flow, is_request):
+ r = flow.request if is_request else flow.response
+ expected_size = http1.expected_http_body_size(
+ flow.request, flow.response if not is_request else None
+ )
+ if not r.content and not (0 <= expected_size <= self.max_size):
+ # r.stream may already be a callable, which we want to preserve.
+ r.stream = r.stream or True
+
+
+class ClientPlaybackState:
+ def __init__(self, flows, exit):
+ self.flows, self.exit = flows, exit
+ self.current = None
+ self.testing = False # Disables actual replay for testing.
+
+ def count(self):
+ return len(self.flows)
+
+ def done(self):
+ if len(self.flows) == 0 and not self.current:
+ return True
+ return False
+
+ def clear(self, flow):
+ """
+ A request has returned in some way - if this is the one we're
+ servicing, go to the next flow.
+ """
+ if flow is self.current:
+ self.current = None
+
+ def tick(self, master):
+ if self.flows and not self.current:
+ self.current = self.flows.pop(0).copy()
+ if not self.testing:
+ master.replay_request(self.current)
+ else:
+ self.current.reply = controller.DummyReply()
+ master.request(self.current)
+ if self.current.response:
+ master.response(self.current)
+
+
+class ServerPlaybackState:
+ def __init__(
+ self,
+ headers,
+ flows,
+ exit,
+ nopop,
+ ignore_params,
+ ignore_content,
+ ignore_payload_params,
+ ignore_host):
+ """
+ headers: Case-insensitive list of request headers that should be
+ included in request-response matching.
+ """
+ self.headers = headers
+ self.exit = exit
+ self.nopop = nopop
+ self.ignore_params = ignore_params
+ self.ignore_content = ignore_content
+ self.ignore_payload_params = ignore_payload_params
+ self.ignore_host = ignore_host
+ self.fmap = {}
+ for i in flows:
+ if i.response:
+ l = self.fmap.setdefault(self._hash(i), [])
+ l.append(i)
+
+ def count(self):
+ return sum(len(i) for i in self.fmap.values())
+
+ def _hash(self, flow):
+ """
+ Calculates a loose hash of the flow request.
+ """
+ r = flow.request
+
+ _, _, path, _, query, _ = urllib.parse.urlparse(r.url)
+ queriesArray = urllib.parse.parse_qsl(query, keep_blank_values=True)
+
+ key = [
+ str(r.port),
+ str(r.scheme),
+ str(r.method),
+ str(path),
+ ]
+
+ if not self.ignore_content:
+ form_contents = r.urlencoded_form or r.multipart_form
+ if self.ignore_payload_params and form_contents:
+ key.extend(
+ p for p in form_contents.items(multi=True)
+ if p[0] not in self.ignore_payload_params
+ )
+ else:
+ key.append(str(r.content))
+
+ if not self.ignore_host:
+ key.append(r.host)
+
+ filtered = []
+ ignore_params = self.ignore_params or []
+ for p in queriesArray:
+ if p[0] not in ignore_params:
+ filtered.append(p)
+ for p in filtered:
+ key.append(p[0])
+ key.append(p[1])
+
+ if self.headers:
+ headers = []
+ for i in self.headers:
+ v = r.headers.get(i)
+ headers.append((i, v))
+ key.append(headers)
+ return hashlib.sha256(repr(key)).digest()
+
+ def next_flow(self, request):
+ """
+ Returns the next flow object, or None if no matching flow was
+ found.
+ """
+ l = self.fmap.get(self._hash(request))
+ if not l:
+ return None
+
+ if self.nopop:
+ return l[0]
+ else:
+ return l.pop(0)
+
+
+class StickyCookieState:
+ def __init__(self, flt):
+ """
+ flt: Compiled filter.
+ """
+ self.jar = collections.defaultdict(dict)
+ self.flt = flt
+
+ def ckey(self, attrs, f):
+ """
+ Returns a (domain, port, path) tuple.
+ """
+ domain = f.request.host
+ path = "/"
+ if "domain" in attrs:
+ domain = attrs["domain"]
+ if "path" in attrs:
+ path = attrs["path"]
+ return (domain, f.request.port, path)
+
+ def domain_match(self, a, b):
+ if http_cookiejar.domain_match(a, b):
+ return True
+ elif http_cookiejar.domain_match(a, b.strip(".")):
+ return True
+ return False
+
+ def handle_response(self, f):
+ for name, (value, attrs) in f.response.cookies.items(multi=True):
+ # FIXME: We now know that Cookie.py screws up some cookies with
+ # valid RFC 822/1123 datetime specifications for expiry. Sigh.
+ a = self.ckey(attrs, f)
+ if self.domain_match(f.request.host, a[0]):
+ b = attrs.with_insert(0, name, value)
+ self.jar[a][name] = b
+
+ def handle_request(self, f):
+ l = []
+ if f.match(self.flt):
+ for domain, port, path in self.jar.keys():
+ match = [
+ self.domain_match(f.request.host, domain),
+ f.request.port == port,
+ f.request.path.startswith(path)
+ ]
+ if all(match):
+ c = self.jar[(domain, port, path)]
+ l.extend([cookies.format_cookie_header(c[name].items(multi=True)) for name in c.keys()])
+ if l:
+ f.request.stickycookie = True
+ f.request.headers["cookie"] = "; ".join(l)
+
+
+class StickyAuthState:
+ def __init__(self, flt):
+ """
+ flt: Compiled filter.
+ """
+ self.flt = flt
+ self.hosts = {}
+
+ def handle_request(self, f):
+ host = f.request.host
+ if "authorization" in f.request.headers:
+ self.hosts[host] = f.request.headers["authorization"]
+ elif f.match(self.flt):
+ if host in self.hosts:
+ f.request.headers["authorization"] = self.hosts[host]
diff --git a/mitmproxy/flow/state.py b/mitmproxy/flow/state.py
new file mode 100644
index 00000000..4287d77b
--- /dev/null
+++ b/mitmproxy/flow/state.py
@@ -0,0 +1,269 @@
+from __future__ import absolute_import, print_function, division
+
+from abc import abstractmethod, ABCMeta
+
+import six
+from typing import List # noqa
+
+from mitmproxy import filt
+from mitmproxy import models # noqa
+
+
+@six.add_metaclass(ABCMeta)
+class FlowList(object):
+ def __init__(self):
+ self._list = [] # type: List[models.Flow]
+
+ def __iter__(self):
+ return iter(self._list)
+
+ def __contains__(self, item):
+ return item in self._list
+
+ def __getitem__(self, item):
+ return self._list[item]
+
+ def __bool__(self):
+ return bool(self._list)
+
+ if six.PY2:
+ __nonzero__ = __bool__
+
+ def __len__(self):
+ return len(self._list)
+
+ def index(self, f):
+ return self._list.index(f)
+
+ @abstractmethod
+ def _add(self, f):
+ return
+
+ @abstractmethod
+ def _update(self, f):
+ return
+
+ @abstractmethod
+ def _remove(self, f):
+ return
+
+
+def _pos(*args):
+ return True
+
+
+class FlowView(FlowList):
+ def __init__(self, store, filt=None):
+ super(FlowView, self).__init__()
+ if not filt:
+ filt = _pos
+ self._build(store, filt)
+
+ self.store = store
+ self.store.views.append(self)
+
+ def _close(self):
+ self.store.views.remove(self)
+
+ def _build(self, flows, filt=None):
+ if filt:
+ self.filt = filt
+ self._list = list(filter(self.filt, flows))
+
+ def _add(self, f):
+ if self.filt(f):
+ self._list.append(f)
+
+ def _update(self, f):
+ if f not in self._list:
+ self._add(f)
+ elif not self.filt(f):
+ self._remove(f)
+
+ def _remove(self, f):
+ if f in self._list:
+ self._list.remove(f)
+
+ def _recalculate(self, flows):
+ self._build(flows)
+
+
+class FlowStore(FlowList):
+ """
+ Responsible for handling flows in the state:
+ Keeps a list of all flows and provides views on them.
+ """
+
+ def __init__(self):
+ super(FlowStore, self).__init__()
+ self._set = set() # Used for O(1) lookups
+ self.views = []
+ self._recalculate_views()
+
+ def get(self, flow_id):
+ for f in self._list:
+ if f.id == flow_id:
+ return f
+
+ def __contains__(self, f):
+ return f in self._set
+
+ def _add(self, f):
+ """
+ Adds a flow to the state.
+ The flow to add must not be present in the state.
+ """
+ self._list.append(f)
+ self._set.add(f)
+ for view in self.views:
+ view._add(f)
+
+ def _update(self, f):
+ """
+ Notifies the state that a flow has been updated.
+ The flow must be present in the state.
+ """
+ if f in self:
+ for view in self.views:
+ view._update(f)
+
+ def _remove(self, f):
+ """
+ Deletes a flow from the state.
+ The flow must be present in the state.
+ """
+ self._list.remove(f)
+ self._set.remove(f)
+ for view in self.views:
+ view._remove(f)
+
+ # Expensive bulk operations
+
+ def _extend(self, flows):
+ """
+ Adds a list of flows to the state.
+ The list of flows to add must not contain flows that are already in the state.
+ """
+ self._list.extend(flows)
+ self._set.update(flows)
+ self._recalculate_views()
+
+ def _clear(self):
+ self._list = []
+ self._set = set()
+ self._recalculate_views()
+
+ def _recalculate_views(self):
+ """
+ Expensive operation: Recalculate all the views after a bulk change.
+ """
+ for view in self.views:
+ view._recalculate(self)
+
+ # Utility functions.
+ # There are some common cases where we need to argue about all flows
+ # irrespective of filters on the view etc (i.e. on shutdown).
+
+ def active_count(self):
+ c = 0
+ for i in self._list:
+ if not i.response and not i.error:
+ c += 1
+ return c
+
+ # TODO: Should accept_all operate on views or on all flows?
+ def accept_all(self, master):
+ for f in self._list:
+ f.accept_intercept(master)
+
+ def kill_all(self, master):
+ for f in self._list:
+ if not f.reply.acked:
+ f.kill(master)
+
+
+class State(object):
+ def __init__(self):
+ self.flows = FlowStore()
+ self.view = FlowView(self.flows, None)
+
+ # These are compiled filt expressions:
+ self.intercept = None
+
+ @property
+ def limit_txt(self):
+ return getattr(self.view.filt, "pattern", None)
+
+ def flow_count(self):
+ return len(self.flows)
+
+ # TODO: All functions regarding flows that don't cause side-effects should
+ # be moved into FlowStore.
+ def index(self, f):
+ return self.flows.index(f)
+
+ def active_flow_count(self):
+ return self.flows.active_count()
+
+ def add_flow(self, f):
+ """
+ Add a request to the state.
+ """
+ self.flows._add(f)
+ return f
+
+ def update_flow(self, f):
+ """
+ Add a response to the state.
+ """
+ self.flows._update(f)
+ return f
+
+ def delete_flow(self, f):
+ self.flows._remove(f)
+
+ def load_flows(self, flows):
+ self.flows._extend(flows)
+
+ def set_limit(self, txt):
+ if txt == self.limit_txt:
+ return
+ if txt:
+ f = filt.parse(txt)
+ if not f:
+ return "Invalid filter expression."
+ self.view._close()
+ self.view = FlowView(self.flows, f)
+ else:
+ self.view._close()
+ self.view = FlowView(self.flows, None)
+
+ def set_intercept(self, txt):
+ if txt:
+ f = filt.parse(txt)
+ if not f:
+ return "Invalid filter expression."
+ self.intercept = f
+ else:
+ self.intercept = None
+
+ @property
+ def intercept_txt(self):
+ return getattr(self.intercept, "pattern", None)
+
+ def clear(self):
+ self.flows._clear()
+
+ def accept_all(self, master):
+ self.flows.accept_all(master)
+
+ def backup(self, f):
+ f.backup()
+ self.update_flow(f)
+
+ def revert(self, f):
+ f.revert()
+ self.update_flow(f)
+
+ def killall(self, master):
+ self.flows.kill_all(master)
diff --git a/mitmproxy/main.py b/mitmproxy/main.py
index d44c257e..bf0b7e4d 100644
--- a/mitmproxy/main.py
+++ b/mitmproxy/main.py
@@ -1,13 +1,16 @@
-from __future__ import print_function, absolute_import
+from __future__ import absolute_import, print_function, division
+
import os
import signal
import sys
+
from six.moves import _thread # PY3: We only need _thread.error, which is an alias of RuntimeError in 3.3+
-from netlib.version_check import check_pyopenssl_version
-from . import cmdline
-from .exceptions import ServerException
-from .proxy.server import DummyServer, ProxyServer
-from .proxy.config import process_proxy_options
+
+from mitmproxy import cmdline
+from mitmproxy import exceptions
+from mitmproxy.proxy import config
+from mitmproxy.proxy import server
+from netlib import version_check
def assert_utf8_env():
@@ -28,11 +31,11 @@ def assert_utf8_env():
def get_server(dummy_server, options):
if dummy_server:
- return DummyServer(options)
+ return server.DummyServer(options)
else:
try:
- return ProxyServer(options)
- except ServerException as v:
+ return server.ProxyServer(options)
+ except exceptions.ServerException as v:
print(str(v), file=sys.stderr)
sys.exit(1)
@@ -44,7 +47,7 @@ def mitmproxy(args=None): # pragma: no cover
sys.exit(1)
from . import console
- check_pyopenssl_version()
+ version_check.check_pyopenssl_version()
assert_utf8_env()
parser = cmdline.mitmproxy()
@@ -52,7 +55,7 @@ def mitmproxy(args=None): # pragma: no cover
if options.quiet:
options.verbose = 0
- proxy_config = process_proxy_options(parser, options)
+ proxy_config = config.process_proxy_options(parser, options)
console_options = console.Options(**cmdline.get_common_options(options))
console_options.palette = options.palette
console_options.palette_transparent = options.palette_transparent
@@ -74,7 +77,7 @@ def mitmproxy(args=None): # pragma: no cover
def mitmdump(args=None): # pragma: no cover
from . import dump
- check_pyopenssl_version()
+ version_check.check_pyopenssl_version()
parser = cmdline.mitmdump()
options = parser.parse_args(args)
@@ -82,7 +85,7 @@ def mitmdump(args=None): # pragma: no cover
options.verbose = 0
options.flow_detail = 0
- proxy_config = process_proxy_options(parser, options)
+ proxy_config = config.process_proxy_options(parser, options)
dump_options = dump.Options(**cmdline.get_common_options(options))
dump_options.flow_detail = options.flow_detail
dump_options.keepserving = options.keepserving
@@ -108,7 +111,7 @@ def mitmdump(args=None): # pragma: no cover
def mitmweb(args=None): # pragma: no cover
from . import web
- check_pyopenssl_version()
+ version_check.check_pyopenssl_version()
parser = cmdline.mitmweb()
@@ -116,7 +119,7 @@ def mitmweb(args=None): # pragma: no cover
if options.quiet:
options.verbose = 0
- proxy_config = process_proxy_options(parser, options)
+ proxy_config = config.process_proxy_options(parser, options)
web_options = web.Options(**cmdline.get_common_options(options))
web_options.intercept = options.intercept
web_options.wdebug = options.wdebug
diff --git a/mitmproxy/models/__init__.py b/mitmproxy/models/__init__.py
index 3d9d9dae..ca813567 100644
--- a/mitmproxy/models/__init__.py
+++ b/mitmproxy/models/__init__.py
@@ -1,12 +1,12 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+from netlib.http import decoded
+from .connections import ClientConnection, ServerConnection
+from .flow import Flow, Error
from .http import (
HTTPFlow, HTTPRequest, HTTPResponse, Headers,
make_error_response, make_connect_request, make_connect_response, expect_continue_response
)
-from netlib.http import decoded
-from .connections import ClientConnection, ServerConnection
-from .flow import Flow, Error
from .tcp import TCPFlow
FLOW_TYPES = dict(
diff --git a/mitmproxy/models/connections.py b/mitmproxy/models/connections.py
index 91590bca..6347f488 100644
--- a/mitmproxy/models/connections.py
+++ b/mitmproxy/models/connections.py
@@ -1,15 +1,18 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
import copy
import os
import six
-from netlib import tcp, certutils
-from .. import stateobject, utils
+from mitmproxy import stateobject
+from mitmproxy import utils
+from netlib import certutils
+from netlib import tcp
class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
+
"""
A client connection
@@ -21,6 +24,7 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
timestamp_ssl_setup: TLS established timestamp
timestamp_end: Connection end timestamp
"""
+
def __init__(self, client_connection, address, server):
# Eventually, this object is restored from state. We don't have a
# connection then.
@@ -101,6 +105,7 @@ class ClientConnection(tcp.BaseHandler, stateobject.StateObject):
class ServerConnection(tcp.TCPClient, stateobject.StateObject):
+
"""
A server connection
@@ -117,6 +122,7 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
timestamp_ssl_setup: TLS established timestamp
timestamp_end: Connection end timestamp
"""
+
def __init__(self, address, source_address=None):
tcp.TCPClient.__init__(self, address, source_address)
@@ -182,7 +188,7 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
timestamp_ssl_setup=None,
timestamp_end=None,
via=None
- ))
+ ))
def copy(self):
return copy.copy(self)
diff --git a/mitmproxy/models/flow.py b/mitmproxy/models/flow.py
index 1019c9fb..7b9ec030 100644
--- a/mitmproxy/models/flow.py
+++ b/mitmproxy/models/flow.py
@@ -1,10 +1,14 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
import copy
import uuid
-from .. import stateobject, utils, version
-from .connections import ClientConnection, ServerConnection
-from ..exceptions import Kill
+from mitmproxy import exceptions
+from mitmproxy import stateobject
+from mitmproxy import utils
+from mitmproxy import version
+from mitmproxy.models.connections import ClientConnection
+from mitmproxy.models.connections import ServerConnection
class Error(stateobject.StateObject):
@@ -151,8 +155,8 @@ class Flow(stateobject.StateObject):
"""
self.error = Error("Connection killed")
self.intercepted = False
- self.reply(Kill)
- master.handle_error(self)
+ self.reply(exceptions.Kill)
+ master.error(self)
def intercept(self, master):
"""
diff --git a/mitmproxy/models/http.py b/mitmproxy/models/http.py
index 75ffbfd0..a32124ac 100644
--- a/mitmproxy/models/http.py
+++ b/mitmproxy/models/http.py
@@ -1,11 +1,15 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
import cgi
+from mitmproxy import version
+from mitmproxy.models.flow import Flow
from netlib import encoding
-from netlib.http import status_codes, Headers, Request, Response
+from netlib.http import Headers
+from netlib.http import Request
+from netlib.http import Response
+from netlib.http import status_codes
from netlib.tcp import Address
-from .. import version
-from .flow import Flow
class MessageMixin(object):
@@ -72,9 +76,9 @@ class HTTPRequest(MessageMixin, Request):
def get_state(self):
state = super(HTTPRequest, self).get_state()
state.update(
- stickycookie = self.stickycookie,
- stickyauth = self.stickyauth,
- is_replay = self.is_replay,
+ stickycookie=self.stickycookie,
+ stickyauth=self.stickyauth,
+ is_replay=self.is_replay,
)
return state
@@ -109,6 +113,7 @@ class HTTPRequest(MessageMixin, Request):
class HTTPResponse(MessageMixin, Response):
+
"""
A mitmproxy HTTP response.
This is a very thin wrapper on top of :py:class:`netlib.http.Response` and
@@ -124,7 +129,7 @@ class HTTPResponse(MessageMixin, Response):
content,
timestamp_start=None,
timestamp_end=None,
- is_replay = False
+ is_replay=False
):
Response.__init__(
self,
diff --git a/mitmproxy/models/tcp.py b/mitmproxy/models/tcp.py
index 7e966b95..e33475c2 100644
--- a/mitmproxy/models/tcp.py
+++ b/mitmproxy/models/tcp.py
@@ -1,11 +1,15 @@
+from __future__ import absolute_import, print_function, division
+
import time
+
from typing import List
-from netlib.utils import Serializable
-from .flow import Flow
+import netlib.basetypes
+from mitmproxy.models.flow import Flow
-class TCPMessage(Serializable):
+class TCPMessage(netlib.basetypes.Serializable):
+
def __init__(self, from_client, content, timestamp=None):
self.content = content
self.from_client = from_client
@@ -33,6 +37,7 @@ class TCPMessage(Serializable):
class TCPFlow(Flow):
+
"""
A TCPFlow is a simplified representation of a TCP session.
"""
diff --git a/mitmproxy/onboarding/app.py b/mitmproxy/onboarding/app.py
index ff5ed63c..f93b9982 100644
--- a/mitmproxy/onboarding/app.py
+++ b/mitmproxy/onboarding/app.py
@@ -1,12 +1,13 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function, division
+
import os
+
+import tornado.template
import tornado.web
import tornado.wsgi
-import tornado.template
-
-from .. import utils
-from ..proxy import config
+from mitmproxy import utils
+from mitmproxy.proxy import config
loader = tornado.template.Loader(utils.pkg_data.path("onboarding/templates"))
diff --git a/mitmproxy/platform/osx.py b/mitmproxy/platform/osx.py
index 3cd4bc66..b16c1861 100644
--- a/mitmproxy/platform/osx.py
+++ b/mitmproxy/platform/osx.py
@@ -1,4 +1,5 @@
import subprocess
+
import pf
"""
diff --git a/mitmproxy/platform/windows.py b/mitmproxy/platform/windows.py
index 0a810908..576516e2 100644
--- a/mitmproxy/platform/windows.py
+++ b/mitmproxy/platform/windows.py
@@ -1,18 +1,17 @@
-import configargparse
-from six.moves import cPickle as pickle
-from ctypes import byref, windll, Structure
-from ctypes.wintypes import DWORD
+import collections
+import ctypes
+import ctypes.wintypes
import os
import socket
-from six.moves import socketserver
import struct
import threading
import time
-from collections import OrderedDict
-
-from pydivert.windivert import WinDivert
-from pydivert.enum import Direction, Layer, Flag
+import configargparse
+from pydivert import enum
+from pydivert import windivert
+from six.moves import cPickle as pickle
+from six.moves import socketserver
PROXY_API_PORT = 8085
@@ -91,22 +90,22 @@ ERROR_INSUFFICIENT_BUFFER = 0x7A
# http://msdn.microsoft.com/en-us/library/windows/desktop/bb485761(v=vs.85).aspx
-class MIB_TCPROW2(Structure):
+class MIB_TCPROW2(ctypes.Structure):
_fields_ = [
- ('dwState', DWORD),
- ('dwLocalAddr', DWORD),
- ('dwLocalPort', DWORD),
- ('dwRemoteAddr', DWORD),
- ('dwRemotePort', DWORD),
- ('dwOwningPid', DWORD),
- ('dwOffloadState', DWORD)
+ ('dwState', ctypes.wintypes.DWORD),
+ ('dwLocalAddr', ctypes.wintypes.DWORD),
+ ('dwLocalPort', ctypes.wintypes.DWORD),
+ ('dwRemoteAddr', ctypes.wintypes.DWORD),
+ ('dwRemotePort', ctypes.wintypes.DWORD),
+ ('dwOwningPid', ctypes.wintypes.DWORD),
+ ('dwOffloadState', ctypes.wintypes.DWORD)
]
# http://msdn.microsoft.com/en-us/library/windows/desktop/bb485772(v=vs.85).aspx
def MIB_TCPTABLE2(size):
- class _MIB_TCPTABLE2(Structure):
- _fields_ = [('dwNumEntries', DWORD),
+ class _MIB_TCPTABLE2(ctypes.Structure):
+ _fields_ = [('dwNumEntries', ctypes.wintypes.DWORD),
('table', MIB_TCPROW2 * size)]
return _MIB_TCPTABLE2()
@@ -192,13 +191,13 @@ class TransparentProxy(object):
self.proxy_addr, self.proxy_port = proxy_addr, proxy_port
self.connection_cache_size = cache_size
- self.client_server_map = OrderedDict()
+ self.client_server_map = collections.OrderedDict()
self.api = APIServer(self, (api_host, api_port), APIRequestHandler)
self.api_thread = threading.Thread(target=self.api.serve_forever)
self.api_thread.daemon = True
- self.driver = WinDivert()
+ self.driver = windivert.WinDivert()
self.driver.register()
self.request_filter = custom_filter or " or ".join(
@@ -212,7 +211,7 @@ class TransparentProxy(object):
self.addr_pid_map = dict()
self.trusted_pids = set()
self.tcptable2 = MIB_TCPTABLE2(0)
- self.tcptable2_size = DWORD(0)
+ self.tcptable2_size = ctypes.wintypes.DWORD(0)
self.request_local_handle = None
self.request_local_thread = threading.Thread(target=self.request_local)
self.request_local_thread.daemon = True
@@ -244,23 +243,23 @@ class TransparentProxy(object):
# real gateway if they are on the same network.
self.icmp_handle = self.driver.open_handle(
filter="icmp",
- layer=Layer.NETWORK,
- flags=Flag.DROP)
+ layer=enum.Layer.NETWORK,
+ flags=enum.Flag.DROP)
self.response_handle = self.driver.open_handle(
filter=self.response_filter,
- layer=Layer.NETWORK)
+ layer=enum.Layer.NETWORK)
self.response_thread.start()
if self.mode == "forward" or self.mode == "both":
self.request_forward_handle = self.driver.open_handle(
filter=self.request_filter,
- layer=Layer.NETWORK_FORWARD)
+ layer=enum.Layer.NETWORK_FORWARD)
self.request_forward_thread.start()
if self.mode == "local" or self.mode == "both":
self.request_local_handle = self.driver.open_handle(
filter=self.request_filter,
- layer=Layer.NETWORK)
+ layer=enum.Layer.NETWORK)
self.request_local_thread.start()
def shutdown(self):
@@ -288,9 +287,9 @@ class TransparentProxy(object):
raise
def fetch_pids(self):
- ret = windll.iphlpapi.GetTcpTable2(
- byref(
- self.tcptable2), byref(
+ ret = ctypes.windll.iphlpapi.GetTcpTable2(
+ ctypes.byref(
+ self.tcptable2), ctypes.byref(
self.tcptable2_size), 0)
if ret == ERROR_INSUFFICIENT_BUFFER:
self.tcptable2 = MIB_TCPTABLE2(self.tcptable2_size.value)
@@ -352,7 +351,7 @@ class TransparentProxy(object):
self.client_server_map[client] = server
packet.dst_addr, packet.dst_port = self.proxy_addr, self.proxy_port
- metadata.direction = Direction.INBOUND
+ metadata.direction = enum.Direction.INBOUND
packet = self.driver.update_packet_checksums(packet)
# Use any handle thats on the NETWORK layer - request_local may be
diff --git a/mitmproxy/protocol/__init__.py b/mitmproxy/protocol/__init__.py
index 3d9fa7d4..510cd195 100644
--- a/mitmproxy/protocol/__init__.py
+++ b/mitmproxy/protocol/__init__.py
@@ -25,15 +25,16 @@ Another subtle design goal of this architecture is that upstream connections sho
as late as possible; this makes server replay without any outgoing connections possible.
"""
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
from .base import Layer, ServerConnectionMixin
-from .tls import TlsLayer
-from .tls import is_tls_record_magic
-from .tls import TlsClientHello
from .http import UpstreamConnectLayer
from .http1 import Http1Layer
from .http2 import Http2Layer
from .rawtcp import RawTCPLayer
+from .tls import TlsClientHello
+from .tls import TlsLayer
+from .tls import is_tls_record_magic
__all__ = [
"Layer", "ServerConnectionMixin",
diff --git a/mitmproxy/protocol/base.py b/mitmproxy/protocol/base.py
index c8e58d1b..11773385 100644
--- a/mitmproxy/protocol/base.py
+++ b/mitmproxy/protocol/base.py
@@ -1,11 +1,12 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
import sys
import six
-from ..models import ServerConnection
-from ..exceptions import ProtocolException
-from netlib.exceptions import TcpException
+import netlib.exceptions
+from mitmproxy import exceptions
+from mitmproxy import models
class _LayerCodeCompletion(object):
@@ -113,7 +114,7 @@ class ServerConnectionMixin(object):
def __init__(self, server_address=None):
super(ServerConnectionMixin, self).__init__()
- self.server_conn = ServerConnection(server_address, (self.config.host, 0))
+ self.server_conn = models.ServerConnection(server_address, (self.config.host, 0))
self.__check_self_connect()
def __check_self_connect(self):
@@ -128,7 +129,7 @@ class ServerConnectionMixin(object):
address.host in ("localhost", "127.0.0.1", "::1")
)
if self_connect:
- raise ProtocolException(
+ raise exceptions.ProtocolException(
"Invalid server address: {}\r\n"
"The proxy shall not connect to itself.".format(repr(address))
)
@@ -154,7 +155,7 @@ class ServerConnectionMixin(object):
self.server_conn.finish()
self.server_conn.close()
self.channel.tell("serverdisconnect", self.server_conn)
- self.server_conn = ServerConnection(address, (source_address.host, 0))
+ self.server_conn = models.ServerConnection(address, (source_address.host, 0))
def connect(self):
"""
@@ -165,15 +166,15 @@ class ServerConnectionMixin(object):
~mitmproxy.exceptions.ProtocolException: if the connection could not be established.
"""
if not self.server_conn.address:
- raise ProtocolException("Cannot connect to server, no server address given.")
+ raise exceptions.ProtocolException("Cannot connect to server, no server address given.")
self.log("serverconnect", "debug", [repr(self.server_conn.address)])
self.channel.ask("serverconnect", self.server_conn)
try:
self.server_conn.connect()
- except TcpException as e:
+ except netlib.exceptions.TcpException as e:
six.reraise(
- ProtocolException,
- ProtocolException(
+ exceptions.ProtocolException,
+ exceptions.ProtocolException(
"Server connection to {} failed: {}".format(
repr(self.server_conn.address), str(e)
)
diff --git a/mitmproxy/protocol/http.py b/mitmproxy/protocol/http.py
index d9111303..ae03ab7f 100644
--- a/mitmproxy/protocol/http.py
+++ b/mitmproxy/protocol/http.py
@@ -1,30 +1,21 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
import sys
import traceback
+
+import h2.exceptions
import six
+import netlib.exceptions
+from mitmproxy import exceptions
+from mitmproxy import models
+from mitmproxy import utils
+from mitmproxy.protocol import base
+from netlib import http
from netlib import tcp
-from netlib.exceptions import HttpException, HttpReadDisconnect, NetlibException
-from netlib.http import Headers
-
-from h2.exceptions import H2Error
-
-from .. import utils
-from ..exceptions import HttpProtocolException, Http2ProtocolException, ProtocolException
-from ..models import (
- HTTPFlow,
- HTTPResponse,
- make_error_response,
- make_connect_response,
- Error,
- expect_continue_response
-)
-
-from .base import Layer
-class _HttpTransmissionLayer(Layer):
+class _HttpTransmissionLayer(base.Layer):
def read_request(self):
raise NotImplementedError()
@@ -51,7 +42,7 @@ class _HttpTransmissionLayer(Layer):
def send_response(self, response):
if response.content is None:
- raise HttpException("Cannot assemble flow with missing content")
+ raise netlib.exceptions.HttpException("Cannot assemble flow with missing content")
self.send_response_headers(response)
self.send_response_body(response, [response.content])
@@ -89,7 +80,7 @@ class ConnectServerConnection(object):
__nonzero__ = __bool__
-class UpstreamConnectLayer(Layer):
+class UpstreamConnectLayer(base.Layer):
def __init__(self, ctx, connect_request):
super(UpstreamConnectLayer, self).__init__(ctx)
@@ -107,7 +98,7 @@ class UpstreamConnectLayer(Layer):
self.send_request(self.connect_request)
resp = self.read_response(self.connect_request)
if resp.status_code != 200:
- raise ProtocolException("Reconnect: Upstream server refuses CONNECT request")
+ raise exceptions.ProtocolException("Reconnect: Upstream server refuses CONNECT request")
def connect(self):
if not self.server_conn:
@@ -129,7 +120,7 @@ class UpstreamConnectLayer(Layer):
self.server_conn.address = address
-class HttpLayer(Layer):
+class HttpLayer(base.Layer):
def __init__(self, ctx, mode):
super(HttpLayer, self).__init__(ctx)
@@ -166,16 +157,16 @@ class HttpLayer(Layer):
self.handle_regular_mode_connect(request)
return
- except HttpReadDisconnect:
+ except netlib.exceptions.HttpReadDisconnect:
# don't throw an error for disconnects that happen before/between requests.
return
- except NetlibException as e:
+ except netlib.exceptions.NetlibException as e:
self.send_error_response(400, repr(e))
- six.reraise(ProtocolException, ProtocolException(
+ six.reraise(exceptions.ProtocolException, exceptions.ProtocolException(
"Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
try:
- flow = HTTPFlow(self.client_conn, self.server_conn, live=self)
+ flow = models.HTTPFlow(self.client_conn, self.server_conn, live=self)
flow.request = request
# set upstream auth
if self.mode == "upstream" and self.config.upstream_auth is not None:
@@ -210,16 +201,16 @@ class HttpLayer(Layer):
self.handle_upstream_mode_connect(flow.request.copy())
return
- except (ProtocolException, NetlibException) as e:
+ except (exceptions.ProtocolException, netlib.exceptions.NetlibException) as e:
self.send_error_response(502, repr(e))
if not flow.response:
- flow.error = Error(str(e))
+ flow.error = models.Error(str(e))
self.channel.ask("error", flow)
self.log(traceback.format_exc(), "debug")
return
else:
- six.reraise(ProtocolException, ProtocolException(
+ six.reraise(exceptions.ProtocolException, exceptions.ProtocolException(
"Error in HTTP connection: %s" % repr(e)), sys.exc_info()[2])
finally:
if flow:
@@ -229,16 +220,16 @@ class HttpLayer(Layer):
request = self.read_request()
if request.headers.get("expect", "").lower() == "100-continue":
# TODO: We may have to use send_response_headers for HTTP2 here.
- self.send_response(expect_continue_response)
+ self.send_response(models.expect_continue_response)
request.headers.pop("expect")
request.body = b"".join(self.read_request_body(request))
return request
def send_error_response(self, code, message):
try:
- response = make_error_response(code, message)
+ response = models.make_error_response(code, message)
self.send_response(response)
- except (NetlibException, H2Error, Http2ProtocolException):
+ except (netlib.exceptions.NetlibException, h2.exceptions.H2Error, exceptions.Http2ProtocolException):
self.log(traceback.format_exc(), "debug")
def change_upstream_proxy_server(self, address):
@@ -249,7 +240,7 @@ class HttpLayer(Layer):
def handle_regular_mode_connect(self, request):
self.set_server((request.host, request.port))
- self.send_response(make_connect_response(request.data.http_version))
+ self.send_response(models.make_connect_response(request.data.http_version))
layer = self.ctx.next_layer(self)
layer()
@@ -283,7 +274,7 @@ class HttpLayer(Layer):
try:
get_response()
- except NetlibException as e:
+ except netlib.exceptions.NetlibException as e:
self.log(
"server communication error: %s" % repr(e),
level="debug"
@@ -300,9 +291,9 @@ class HttpLayer(Layer):
# > read (100-n)% of large request
# > send large request upstream
- if isinstance(e, Http2ProtocolException):
+ if isinstance(e, exceptions.Http2ProtocolException):
# do not try to reconnect for HTTP2
- raise ProtocolException("First and only attempt to get response via HTTP2 failed.")
+ raise exceptions.ProtocolException("First and only attempt to get response via HTTP2 failed.")
self.disconnect()
self.connect()
@@ -345,7 +336,7 @@ class HttpLayer(Layer):
flow.request.scheme = "https" if self.__initial_server_tls else "http"
request_reply = self.channel.ask("request", flow)
- if isinstance(request_reply, HTTPResponse):
+ if isinstance(request_reply, models.HTTPResponse):
flow.response = request_reply
return
@@ -365,7 +356,7 @@ class HttpLayer(Layer):
if not self.server_conn:
self.connect()
if tls:
- raise HttpProtocolException("Cannot change scheme in upstream proxy mode.")
+ raise exceptions.HttpProtocolException("Cannot change scheme in upstream proxy mode.")
"""
# This is a very ugly (untested) workaround to solve a very ugly problem.
if self.server_conn and self.server_conn.tls_established and not ssl:
@@ -383,7 +374,7 @@ class HttpLayer(Layer):
def validate_request(self, request):
if request.first_line_format == "absolute" and request.scheme != "http":
- raise HttpException("Invalid request scheme: %s" % request.scheme)
+ raise netlib.exceptions.HttpException("Invalid request scheme: %s" % request.scheme)
expected_request_forms = {
"regular": ("authority", "absolute",),
@@ -396,7 +387,7 @@ class HttpLayer(Layer):
err_message = "Invalid HTTP request form (expected: %s, got: %s)" % (
" or ".join(allowed_request_forms), request.first_line_format
)
- raise HttpException(err_message)
+ raise netlib.exceptions.HttpException(err_message)
if self.mode == "regular" and request.first_line_format == "absolute":
request.first_line_format = "relative"
@@ -406,10 +397,10 @@ class HttpLayer(Layer):
if self.config.authenticator.authenticate(request.headers):
self.config.authenticator.clean(request.headers)
else:
- self.send_response(make_error_response(
+ self.send_response(models.make_error_response(
407,
"Proxy Authentication Required",
- Headers(**self.config.authenticator.auth_challenge_headers())
+ http.Headers(**self.config.authenticator.auth_challenge_headers())
))
return False
return True
diff --git a/mitmproxy/protocol/http1.py b/mitmproxy/protocol/http1.py
index 940a4c98..7055a7fd 100644
--- a/mitmproxy/protocol/http1.py
+++ b/mitmproxy/protocol/http1.py
@@ -1,13 +1,11 @@
-from __future__ import (absolute_import, print_function, division)
-
+from __future__ import absolute_import, print_function, division
+from mitmproxy import models
+from mitmproxy.protocol import http
from netlib.http import http1
-from .http import _HttpTransmissionLayer, HttpLayer
-from ..models import HTTPRequest, HTTPResponse
-
-class Http1Layer(_HttpTransmissionLayer):
+class Http1Layer(http._HttpTransmissionLayer):
def __init__(self, ctx, mode):
super(Http1Layer, self).__init__(ctx)
@@ -15,7 +13,7 @@ class Http1Layer(_HttpTransmissionLayer):
def read_request(self):
req = http1.read_request(self.client_conn.rfile, body_size_limit=self.config.body_size_limit)
- return HTTPRequest.wrap(req)
+ return models.HTTPRequest.wrap(req)
def read_request_body(self, request):
expected_size = http1.expected_http_body_size(request)
@@ -27,7 +25,7 @@ class Http1Layer(_HttpTransmissionLayer):
def read_response_headers(self):
resp = http1.read_response_head(self.server_conn.rfile)
- return HTTPResponse.wrap(resp)
+ return models.HTTPResponse.wrap(resp)
def read_response_body(self, request, response):
expected_size = http1.expected_http_body_size(request, response)
@@ -63,5 +61,5 @@ class Http1Layer(_HttpTransmissionLayer):
return close_connection
def __call__(self):
- layer = HttpLayer(self, self.mode)
+ layer = http.HttpLayer(self, self.mode)
layer()
diff --git a/mitmproxy/protocol/http2.py b/mitmproxy/protocol/http2.py
index 30763c66..39512c8f 100644
--- a/mitmproxy/protocol/http2.py
+++ b/mitmproxy/protocol/http2.py
@@ -1,28 +1,27 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
import threading
import time
-from six.moves import queue
-
import traceback
+
+import h2.exceptions
+import hyperframe
import six
-from h2.connection import H2Connection
-from h2.exceptions import StreamClosedError
+from h2 import connection
from h2 import events
-from hyperframe.frame import PriorityFrame
-
-from netlib.tcp import ssl_read_select
-from netlib.exceptions import HttpException
-from netlib.http import Headers
-from netlib.utils import http2_read_raw_frame, parse_url
+from six.moves import queue
-from .base import Layer
-from .http import _HttpTransmissionLayer, HttpLayer
-from ..exceptions import ProtocolException, Http2ProtocolException
-from ..models import HTTPRequest, HTTPResponse
+import netlib.exceptions
+from mitmproxy import exceptions
+from mitmproxy import models
+from mitmproxy.protocol import base
+from mitmproxy.protocol import http
+import netlib.http
+from netlib import tcp
+from netlib.http import http2
-class SafeH2Connection(H2Connection):
+class SafeH2Connection(connection.H2Connection):
def __init__(self, conn, *args, **kwargs):
super(SafeH2Connection, self).__init__(*args, **kwargs)
@@ -45,7 +44,7 @@ class SafeH2Connection(H2Connection):
with self.lock:
try:
self.reset_stream(stream_id, error_code)
- except StreamClosedError: # pragma: no cover
+ except h2.exceptions.StreamClosedError: # pragma: no cover
# stream is already closed - good
pass
self.conn.send(self.data_to_send())
@@ -58,7 +57,7 @@ class SafeH2Connection(H2Connection):
def safe_send_headers(self, is_zombie, stream_id, headers):
with self.lock:
if is_zombie(): # pragma: no cover
- raise Http2ProtocolException("Zombie Stream")
+ raise exceptions.Http2ProtocolException("Zombie Stream")
self.send_headers(stream_id, headers.fields)
self.conn.send(self.data_to_send())
@@ -69,7 +68,7 @@ class SafeH2Connection(H2Connection):
self.lock.acquire()
if is_zombie(): # pragma: no cover
self.lock.release()
- raise Http2ProtocolException("Zombie Stream")
+ raise exceptions.Http2ProtocolException("Zombie Stream")
max_outbound_frame_size = self.max_outbound_frame_size
frame_chunk = chunk[position:position + max_outbound_frame_size]
if self.local_flow_control_window(stream_id) < len(frame_chunk):
@@ -82,12 +81,12 @@ class SafeH2Connection(H2Connection):
position += max_outbound_frame_size
with self.lock:
if is_zombie(): # pragma: no cover
- raise Http2ProtocolException("Zombie Stream")
+ raise exceptions.Http2ProtocolException("Zombie Stream")
self.end_stream(stream_id)
self.conn.send(self.data_to_send())
-class Http2Layer(Layer):
+class Http2Layer(base.Layer):
def __init__(self, ctx, mode):
super(Http2Layer, self).__init__(ctx)
@@ -107,13 +106,13 @@ class Http2Layer(Layer):
self.active_conns.append(self.server_conn.connection)
def connect(self): # pragma: no cover
- raise Http2ProtocolException("HTTP2 layer should already have a connection.")
+ raise exceptions.Http2ProtocolException("HTTP2 layer should already have a connection.")
def set_server(self): # pragma: no cover
- raise Http2ProtocolException("Cannot change server for HTTP2 connections.")
+ raise exceptions.Http2ProtocolException("Cannot change server for HTTP2 connections.")
def disconnect(self): # pragma: no cover
- raise Http2ProtocolException("Cannot dis- or reconnect in HTTP2 connections.")
+ raise exceptions.Http2ProtocolException("Cannot dis- or reconnect in HTTP2 connections.")
def next_layer(self): # pragma: no cover
# WebSockets over HTTP/2?
@@ -134,19 +133,19 @@ class Http2Layer(Layer):
eid = event.stream_id
if isinstance(event, events.RequestReceived):
- headers = Headers([[k, v] for k, v in event.headers])
+ headers = netlib.http.Headers([[k, v] for k, v in event.headers])
self.streams[eid] = Http2SingleStreamLayer(self, eid, headers)
self.streams[eid].timestamp_start = time.time()
self.streams[eid].start()
elif isinstance(event, events.ResponseReceived):
- headers = Headers([[k, v] for k, v in event.headers])
+ headers = netlib.http.Headers([[k, v] for k, v in event.headers])
self.streams[eid].queued_data_length = 0
self.streams[eid].timestamp_start = time.time()
self.streams[eid].response_headers = headers
self.streams[eid].response_arrived.set()
elif isinstance(event, events.DataReceived):
if self.config.body_size_limit and self.streams[eid].queued_data_length > self.config.body_size_limit:
- raise HttpException("HTTP body too large. Limit is {}.".format(self.config.body_size_limit))
+ raise netlib.exceptions.HttpException("HTTP body too large. Limit is {}.".format(self.config.body_size_limit))
self.streams[eid].data_queue.put(event.data)
self.streams[eid].queued_data_length += len(event.data)
source_conn.h2.safe_increment_flow_control(event.stream_id, event.flow_controlled_length)
@@ -177,7 +176,7 @@ class Http2Layer(Layer):
self.client_conn.h2.push_stream(parent_eid, event.pushed_stream_id, event.headers)
self.client_conn.send(self.client_conn.h2.data_to_send())
- headers = Headers([[str(k), str(v)] for k, v in event.headers])
+ headers = netlib.http.Headers([[str(k), str(v)] for k, v in event.headers])
headers['x-mitmproxy-pushed'] = 'true'
self.streams[event.pushed_stream_id] = Http2SingleStreamLayer(self, event.pushed_stream_id, headers)
self.streams[event.pushed_stream_id].timestamp_start = time.time()
@@ -196,7 +195,7 @@ class Http2Layer(Layer):
depends_on = self.streams[depends_on].server_stream_id
# weight is between 1 and 256 (inclusive), but represented as uint8 (0 to 255)
- frame = PriorityFrame(stream_id, depends_on, event.weight - 1, event.exclusive)
+ frame = hyperframe.frame.PriorityFrame(stream_id, depends_on, event.weight - 1, event.exclusive)
self.server_conn.send(frame.serialize())
elif isinstance(event, events.TrailersReceived):
raise NotImplementedError()
@@ -225,7 +224,7 @@ class Http2Layer(Layer):
self.client_conn.send(self.client_conn.h2.data_to_send())
while True:
- r = ssl_read_select(self.active_conns, 1)
+ r = tcp.ssl_read_select(self.active_conns, 1)
for conn in r:
source_conn = self.client_conn if conn == self.client_conn.connection else self.server_conn
other_conn = self.server_conn if conn == self.client_conn.connection else self.client_conn
@@ -233,7 +232,7 @@ class Http2Layer(Layer):
with source_conn.h2.lock:
try:
- raw_frame = b''.join(http2_read_raw_frame(source_conn.rfile))
+ raw_frame = b''.join(http2.framereader.http2_read_raw_frame(source_conn.rfile))
except:
# read frame failed: connection closed
self._kill_all_streams()
@@ -251,7 +250,7 @@ class Http2Layer(Layer):
self._cleanup_streams()
-class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
+class Http2SingleStreamLayer(http._HttpTransmissionLayer, threading.Thread):
def __init__(self, ctx, stream_id, request_headers):
super(Http2SingleStreamLayer, self).__init__(ctx, name="Thread-Http2SingleStreamLayer-{}".format(stream_id))
@@ -306,6 +305,9 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
method = self.request_headers.get(':method', 'GET')
scheme = self.request_headers.get(':scheme', 'https')
path = self.request_headers.get(':path', '/')
+ self.request_headers.clear(":method")
+ self.request_headers.clear(":scheme")
+ self.request_headers.clear(":path")
host = None
port = None
@@ -316,7 +318,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
else: # pragma: no cover
first_line_format = "absolute"
# FIXME: verify if path or :host contains what we need
- scheme, host, port, _ = parse_url(path)
+ scheme, host, port, _ = netlib.http.url.parse(path)
if authority:
host, _, port = authority.partition(':')
@@ -332,7 +334,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
data.append(self.request_data_queue.get())
data = b"".join(data)
- return HTTPRequest(
+ return models.HTTPRequest(
first_line_format,
method,
scheme,
@@ -357,15 +359,20 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
with self.server_conn.h2.lock:
# We must not assign a stream id if we are already a zombie.
if self.zombie: # pragma: no cover
- raise Http2ProtocolException("Zombie Stream")
+ raise exceptions.Http2ProtocolException("Zombie Stream")
self.server_stream_id = self.server_conn.h2.get_next_available_stream_id()
self.server_to_client_stream_ids[self.server_stream_id] = self.client_stream_id
+ headers = message.headers.copy()
+ headers.insert(0, ":path", message.path)
+ headers.insert(0, ":method", message.method)
+ headers.insert(0, ":scheme", message.scheme)
+
self.server_conn.h2.safe_send_headers(
self.is_zombie,
self.server_stream_id,
- message.headers
+ headers
)
self.server_conn.h2.safe_send_body(
self.is_zombie,
@@ -373,18 +380,20 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
message.body
)
if self.zombie: # pragma: no cover
- raise Http2ProtocolException("Zombie Stream")
+ raise exceptions.Http2ProtocolException("Zombie Stream")
def read_response_headers(self):
self.response_arrived.wait()
status_code = int(self.response_headers.get(':status', 502))
+ headers = self.response_headers.copy()
+ headers.clear(":status")
- return HTTPResponse(
+ return models.HTTPResponse(
http_version=b"HTTP/2.0",
status_code=status_code,
reason='',
- headers=self.response_headers,
+ headers=headers,
content=None,
timestamp_start=self.timestamp_start,
timestamp_end=self.timestamp_end,
@@ -401,16 +410,18 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
yield self.response_data_queue.get()
return
if self.zombie: # pragma: no cover
- raise Http2ProtocolException("Zombie Stream")
+ raise exceptions.Http2ProtocolException("Zombie Stream")
def send_response_headers(self, response):
+ headers = response.headers.copy()
+ headers.insert(0, ":status", str(response.status_code))
self.client_conn.h2.safe_send_headers(
self.is_zombie,
self.client_stream_id,
- response.headers
+ headers
)
if self.zombie: # pragma: no cover
- raise Http2ProtocolException("Zombie Stream")
+ raise exceptions.Http2ProtocolException("Zombie Stream")
def send_response_body(self, _response, chunks):
self.client_conn.h2.safe_send_body(
@@ -419,7 +430,7 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
chunks
)
if self.zombie: # pragma: no cover
- raise Http2ProtocolException("Zombie Stream")
+ raise exceptions.Http2ProtocolException("Zombie Stream")
def check_close_connection(self, flow):
# This layer only handles a single stream.
@@ -434,11 +445,11 @@ class Http2SingleStreamLayer(_HttpTransmissionLayer, threading.Thread):
self()
def __call__(self):
- layer = HttpLayer(self, self.mode)
+ layer = http.HttpLayer(self, self.mode)
try:
layer()
- except ProtocolException as e:
+ except exceptions.ProtocolException as e:
self.log(repr(e), "info")
self.log(traceback.format_exc(), "debug")
diff --git a/mitmproxy/protocol/http_replay.py b/mitmproxy/protocol/http_replay.py
index e78af074..5928c0af 100644
--- a/mitmproxy/protocol/http_replay.py
+++ b/mitmproxy/protocol/http_replay.py
@@ -1,13 +1,14 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
import threading
import traceback
-from mitmproxy.exceptions import ReplayException
-from netlib.exceptions import HttpException, TcpException
+
+import netlib.exceptions
+from mitmproxy import controller
+from mitmproxy import exceptions
+from mitmproxy import models
from netlib.http import http1
-from ..controller import Channel
-from ..models import Error, HTTPResponse, ServerConnection, make_connect_request
-from ..exceptions import Kill
# TODO: Doesn't really belong into mitmproxy.protocol...
@@ -22,7 +23,7 @@ class RequestReplayThread(threading.Thread):
"""
self.config, self.flow = config, flow
if event_queue:
- self.channel = Channel(event_queue, should_exit)
+ self.channel = controller.Channel(event_queue, should_exit)
else:
self.channel = None
super(RequestReplayThread, self).__init__()
@@ -36,17 +37,17 @@ class RequestReplayThread(threading.Thread):
# If we have a channel, run script hooks.
if self.channel:
request_reply = self.channel.ask("request", self.flow)
- if isinstance(request_reply, HTTPResponse):
+ if isinstance(request_reply, models.HTTPResponse):
self.flow.response = request_reply
if not self.flow.response:
# In all modes, we directly connect to the server displayed
if self.config.mode == "upstream":
server_address = self.config.upstream_server.address
- server = ServerConnection(server_address, (self.config.host, 0))
+ server = models.ServerConnection(server_address, (self.config.host, 0))
server.connect()
if r.scheme == "https":
- connect_request = make_connect_request((r.host, r.port))
+ connect_request = models.make_connect_request((r.host, r.port))
server.wfile.write(http1.assemble_request(connect_request))
server.wfile.flush()
resp = http1.read_response(
@@ -55,17 +56,17 @@ class RequestReplayThread(threading.Thread):
body_size_limit=self.config.body_size_limit
)
if resp.status_code != 200:
- raise ReplayException("Upstream server refuses CONNECT request")
+ raise exceptions.ReplayException("Upstream server refuses CONNECT request")
server.establish_ssl(
self.config.clientcerts,
sni=self.flow.server_conn.sni
)
r.first_line_format = "relative"
else:
- r.first_line_format= "absolute"
+ r.first_line_format = "absolute"
else:
server_address = (r.host, r.port)
- server = ServerConnection(server_address, (self.config.host, 0))
+ server = models.ServerConnection(server_address, (self.config.host, 0))
server.connect()
if r.scheme == "https":
server.establish_ssl(
@@ -77,20 +78,20 @@ class RequestReplayThread(threading.Thread):
server.wfile.write(http1.assemble_request(r))
server.wfile.flush()
self.flow.server_conn = server
- self.flow.response = HTTPResponse.wrap(http1.read_response(
+ self.flow.response = models.HTTPResponse.wrap(http1.read_response(
server.rfile,
r,
body_size_limit=self.config.body_size_limit
))
if self.channel:
response_reply = self.channel.ask("response", self.flow)
- if response_reply == Kill:
- raise Kill()
- except (ReplayException, HttpException, TcpException) as e:
- self.flow.error = Error(str(e))
+ if response_reply == exceptions.Kill:
+ raise exceptions.Kill()
+ except (exceptions.ReplayException, netlib.exceptions.NetlibException) as e:
+ self.flow.error = models.Error(str(e))
if self.channel:
self.channel.ask("error", self.flow)
- except Kill:
+ except exceptions.Kill:
# Kill should only be raised if there's a channel in the
# first place.
from ..proxy.root_context import Log
diff --git a/mitmproxy/protocol/rawtcp.py b/mitmproxy/protocol/rawtcp.py
index 1b546c40..70486cc4 100644
--- a/mitmproxy/protocol/rawtcp.py
+++ b/mitmproxy/protocol/rawtcp.py
@@ -1,21 +1,17 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
import socket
-import six
-import sys
from OpenSSL import SSL
-from netlib.exceptions import TcpException
-
-from netlib.tcp import ssl_read_select
-from netlib.utils import clean_bin
-from ..exceptions import ProtocolException
-from ..models import Error
-from ..models.tcp import TCPFlow, TCPMessage
-from .base import Layer
+import netlib.exceptions
+import netlib.tcp
+from mitmproxy import models
+from mitmproxy.models import tcp
+from mitmproxy.protocol import base
-class RawTCPLayer(Layer):
+class RawTCPLayer(base.Layer):
chunk_size = 4096
def __init__(self, ctx, ignore=False):
@@ -26,7 +22,7 @@ class RawTCPLayer(Layer):
self.connect()
if not self.ignore:
- flow = TCPFlow(self.client_conn, self.server_conn, self)
+ flow = models.TCPFlow(self.client_conn, self.server_conn, self)
self.channel.ask("tcp_open", flow)
buf = memoryview(bytearray(self.chunk_size))
@@ -37,7 +33,7 @@ class RawTCPLayer(Layer):
try:
while not self.channel.should_exit.is_set():
- r = ssl_read_select(conns, 10)
+ r = netlib.tcp.ssl_read_select(conns, 10)
for conn in r:
dst = server if conn == client else client
@@ -56,15 +52,15 @@ class RawTCPLayer(Layer):
return
continue
- tcp_message = TCPMessage(dst == server, buf[:size].tobytes())
+ tcp_message = tcp.TCPMessage(dst == server, buf[:size].tobytes())
if not self.ignore:
flow.messages.append(tcp_message)
self.channel.ask("tcp_message", flow)
dst.sendall(tcp_message.content)
- except (socket.error, TcpException, SSL.Error) as e:
+ except (socket.error, netlib.exceptions.TcpException, SSL.Error) as e:
if not self.ignore:
- flow.error = Error("TCP connection closed unexpectedly: {}".format(repr(e)))
+ flow.error = models.Error("TCP connection closed unexpectedly: {}".format(repr(e)))
self.channel.tell("tcp_error", flow)
finally:
if not self.ignore:
diff --git a/mitmproxy/protocol/tls.py b/mitmproxy/protocol/tls.py
index 74c55ab4..9f883b2b 100644
--- a/mitmproxy/protocol/tls.py
+++ b/mitmproxy/protocol/tls.py
@@ -1,16 +1,15 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
import struct
import sys
-from construct import ConstructError
+import construct
import six
-from netlib.exceptions import InvalidCertificateException
-from netlib.exceptions import TlsException
-from ..contrib.tls._constructs import ClientHello
-from ..exceptions import ProtocolException, TlsProtocolException, ClientHandshakeException
-from .base import Layer
+import netlib.exceptions
+from mitmproxy import exceptions
+from mitmproxy.contrib.tls import _constructs
+from mitmproxy.protocol import base
# taken from https://testssl.sh/openssl-rfc.mappping.html
@@ -246,11 +245,11 @@ def get_client_hello(client_conn):
while len(client_hello) < client_hello_size:
record_header = client_conn.rfile.peek(offset + 5)[offset:]
if not is_tls_record_magic(record_header) or len(record_header) != 5:
- raise TlsProtocolException('Expected TLS record, got "%s" instead.' % record_header)
+ raise exceptions.TlsProtocolException('Expected TLS record, got "%s" instead.' % record_header)
record_size = struct.unpack("!H", record_header[3:])[0] + 5
record_body = client_conn.rfile.peek(offset + record_size)[offset + 5:]
if len(record_body) != record_size - 5:
- raise TlsProtocolException("Unexpected EOF in TLS handshake: %s" % record_body)
+ raise exceptions.TlsProtocolException("Unexpected EOF in TLS handshake: %s" % record_body)
client_hello += record_body
offset += record_size
client_hello_size = struct.unpack("!I", b'\x00' + client_hello[1:4])[0] + 4
@@ -260,7 +259,7 @@ def get_client_hello(client_conn):
class TlsClientHello(object):
def __init__(self, raw_client_hello):
- self._client_hello = ClientHello.parse(raw_client_hello)
+ self._client_hello = _constructs.ClientHello.parse(raw_client_hello)
def raw(self):
return self._client_hello
@@ -273,9 +272,9 @@ class TlsClientHello(object):
def sni(self):
for extension in self._client_hello.extensions:
is_valid_sni_extension = (
- extension.type == 0x00
- and len(extension.server_names) == 1
- and extension.server_names[0].type == 0
+ extension.type == 0x00 and
+ len(extension.server_names) == 1 and
+ extension.server_names[0].type == 0
)
if is_valid_sni_extension:
return extension.server_names[0].name
@@ -297,21 +296,24 @@ class TlsClientHello(object):
"""
try:
raw_client_hello = get_client_hello(client_conn)[4:] # exclude handshake header.
- except ProtocolException as e:
- raise TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e))
+ except exceptions.ProtocolException as e:
+ raise exceptions.TlsProtocolException('Cannot read raw Client Hello: %s' % repr(e))
try:
return cls(raw_client_hello)
- except ConstructError as e:
- raise TlsProtocolException('Cannot parse Client Hello: %s, Raw Client Hello: %s' %
- (repr(e), raw_client_hello.encode("hex")))
+ except construct.ConstructError as e:
+ raise exceptions.TlsProtocolException(
+ 'Cannot parse Client Hello: %s, Raw Client Hello: %s' %
+ (repr(e), raw_client_hello.encode("hex"))
+ )
def __repr__(self):
return "TlsClientHello( sni: %s alpn_protocols: %s, cipher_suites: %s)" % \
(self.sni, self.alpn_protocols, self.cipher_suites)
-class TlsLayer(Layer):
+class TlsLayer(base.Layer):
+
"""
The TLS layer implements transparent TLS connections.
@@ -344,7 +346,7 @@ class TlsLayer(Layer):
# Peek into the connection, read the initial client hello and parse it to obtain SNI and ALPN values.
try:
self._client_hello = TlsClientHello.from_client_conn(self.client_conn)
- except TlsProtocolException as e:
+ except exceptions.TlsProtocolException as e:
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
# Do we need to do a server handshake now?
@@ -361,17 +363,17 @@ class TlsLayer(Layer):
# what is supported by the server
# 2.5 The client did not sent a SNI value, we don't know the certificate subject.
client_tls_requires_server_connection = (
- self._server_tls
- and not self.config.no_upstream_cert
- and (
- self.config.add_upstream_certs_to_client_chain
- or self._client_hello.alpn_protocols
- or not self._client_hello.sni
+ self._server_tls and
+ not self.config.no_upstream_cert and
+ (
+ self.config.add_upstream_certs_to_client_chain or
+ self._client_hello.alpn_protocols or
+ not self._client_hello.sni
)
)
establish_server_tls_now = (
- (self.server_conn and self._server_tls)
- or client_tls_requires_server_connection
+ (self.server_conn and self._server_tls) or
+ client_tls_requires_server_connection
)
if self._client_tls and establish_server_tls_now:
@@ -469,9 +471,9 @@ class TlsLayer(Layer):
cert, key, chain_file = self._find_cert()
if self.config.add_upstream_certs_to_client_chain:
- extra_certs = self.server_conn.server_certs
+ extra_certs = self.server_conn.server_certs
else:
- extra_certs = None
+ extra_certs = None
try:
self.client_conn.convert_to_ssl(
@@ -482,17 +484,17 @@ class TlsLayer(Layer):
dhparams=self.config.certstore.dhparams,
chain_file=chain_file,
alpn_select_callback=self.__alpn_select_callback,
- extra_chain_certs = extra_certs,
+ extra_chain_certs=extra_certs,
)
# Some TLS clients will not fail the handshake,
# but will immediately throw an "unexpected eof" error on the first read.
# The reason for this might be difficult to find, so we try to peek here to see if it
# raises ann error.
self.client_conn.rfile.peek(1)
- except TlsException as e:
+ except netlib.exceptions.TlsException as e:
six.reraise(
- ClientHandshakeException,
- ClientHandshakeException(
+ exceptions.ClientHandshakeException,
+ exceptions.ClientHandshakeException(
"Cannot establish TLS with client (sni: {sni}): {e}".format(
sni=self._client_hello.sni, e=repr(e)
),
@@ -507,7 +509,9 @@ class TlsLayer(Layer):
# We only support http/1.1 and h2.
# If the server only supports spdy (next to http/1.1), it may select that
# and mitmproxy would enter TCP passthrough mode, which we want to avoid.
- deprecated_http2_variant = lambda x: x.startswith(b"h2-") or x.startswith(b"spdy")
+ def deprecated_http2_variant(x):
+ return x.startswith(b"h2-") or x.startswith(b"spdy")
+
if self._client_hello.alpn_protocols:
alpn = [x for x in self._client_hello.alpn_protocols if not deprecated_http2_variant(x)]
else:
@@ -541,7 +545,7 @@ class TlsLayer(Layer):
(tls_cert_err['depth'], tls_cert_err['errno']),
"error")
self.log("Ignoring server verification error, continuing with connection", "error")
- except InvalidCertificateException as e:
+ except netlib.exceptions.InvalidCertificateException as e:
tls_cert_err = self.server_conn.ssl_verification_error
self.log(
"TLS verification failed for upstream server at depth %s with error: %s" %
@@ -549,18 +553,18 @@ class TlsLayer(Layer):
"error")
self.log("Aborting connection attempt", "error")
six.reraise(
- TlsProtocolException,
- TlsProtocolException("Cannot establish TLS with {address} (sni: {sni}): {e}".format(
+ exceptions.TlsProtocolException,
+ exceptions.TlsProtocolException("Cannot establish TLS with {address} (sni: {sni}): {e}".format(
address=repr(self.server_conn.address),
sni=self.server_sni,
e=repr(e),
)),
sys.exc_info()[2]
)
- except TlsException as e:
+ except netlib.exceptions.TlsException as e:
six.reraise(
- TlsProtocolException,
- TlsProtocolException("Cannot establish TLS with {address} (sni: {sni}): {e}".format(
+ exceptions.TlsProtocolException,
+ exceptions.TlsProtocolException("Cannot establish TLS with {address} (sni: {sni}): {e}".format(
address=repr(self.server_conn.address),
sni=self.server_sni,
e=repr(e),
diff --git a/mitmproxy/proxy/__init__.py b/mitmproxy/proxy/__init__.py
index be7f5207..ada9fa12 100644
--- a/mitmproxy/proxy/__init__.py
+++ b/mitmproxy/proxy/__init__.py
@@ -1,8 +1,8 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
-from .server import ProxyServer, DummyServer
from .config import ProxyConfig
from .root_context import RootContext, Log
+from .server import ProxyServer, DummyServer
__all__ = [
"ProxyServer", "DummyServer",
diff --git a/mitmproxy/proxy/config.py b/mitmproxy/proxy/config.py
index 58b568ea..9246fe04 100644
--- a/mitmproxy/proxy/config.py
+++ b/mitmproxy/proxy/config.py
@@ -1,4 +1,5 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
import collections
import os
import re
@@ -6,18 +7,26 @@ import re
import six
from OpenSSL import SSL
-from netlib import certutils, tcp
+from mitmproxy import platform
+from netlib import certutils
+from netlib import human
+from netlib import tcp
from netlib.http import authentication
-from netlib.tcp import Address, sslversion_choices
-
-from .. import utils, platform
CONF_BASENAME = "mitmproxy"
CA_DIR = "~/.mitmproxy"
# We manually need to specify this, otherwise OpenSSL may select a non-HTTP2 cipher by default.
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=apache-2.2.15&openssl=1.0.2&hsts=yes&profile=old
-DEFAULT_CLIENT_CIPHERS = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
+DEFAULT_CLIENT_CIPHERS = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:" \
+ "ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" \
+ "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:" \
+ "ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:" \
+ "DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:" \
+ "DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" \
+ "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:" \
+ "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" \
+ "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
class HostMatcher(object):
@@ -58,7 +67,7 @@ class ProxyConfig:
body_size_limit=None,
mode="regular",
upstream_server=None,
- upstream_auth = None,
+ upstream_auth=None,
authenticator=None,
ignore_hosts=tuple(),
tcp_hosts=tuple(),
@@ -83,7 +92,7 @@ class ProxyConfig:
self.body_size_limit = body_size_limit
self.mode = mode
if upstream_server:
- self.upstream_server = ServerSpec(upstream_server[0], Address.wrap(upstream_server[1]))
+ self.upstream_server = ServerSpec(upstream_server[0], tcp.Address.wrap(upstream_server[1]))
self.upstream_auth = upstream_auth
else:
self.upstream_server = None
@@ -103,9 +112,9 @@ class ProxyConfig:
self.certstore.add_cert_file(spec, cert)
self.openssl_method_client, self.openssl_options_client = \
- sslversion_choices[ssl_version_client]
+ tcp.sslversion_choices[ssl_version_client]
self.openssl_method_server, self.openssl_options_server = \
- sslversion_choices[ssl_version_server]
+ tcp.sslversion_choices[ssl_version_server]
if ssl_verify_upstream_cert:
self.openssl_verification_mode_server = SSL.VERIFY_PEER
@@ -117,10 +126,12 @@ class ProxyConfig:
def process_proxy_options(parser, options):
- body_size_limit = utils.parse_size(options.body_size_limit)
+ body_size_limit = options.body_size_limit
+ if body_size_limit:
+ body_size_limit = human.parse_size(body_size_limit)
c = 0
- mode, upstream_server, upstream_auth = "regular", None, None
+ mode, upstream_server, upstream_auth = "regular", None, None
if options.transparent_proxy:
c += 1
if not platform.resolver:
@@ -161,7 +172,7 @@ def process_proxy_options(parser, options):
options.clientcerts = os.path.expanduser(options.clientcerts)
if not os.path.exists(options.clientcerts):
return parser.error(
- "Client certificate path does not exist: %s" % options.clientcerts
+ "Client certificate path does not exist: %s" % options.clientcerts
)
if options.auth_nonanonymous or options.auth_singleuser or options.auth_htpasswd:
diff --git a/mitmproxy/proxy/modes/__init__.py b/mitmproxy/proxy/modes/__init__.py
index f014ed98..fa62570c 100644
--- a/mitmproxy/proxy/modes/__init__.py
+++ b/mitmproxy/proxy/modes/__init__.py
@@ -1,4 +1,5 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
from .http_proxy import HttpProxy, HttpUpstreamProxy
from .reverse_proxy import ReverseProxy
from .socks_proxy import Socks5Proxy
diff --git a/mitmproxy/proxy/modes/http_proxy.py b/mitmproxy/proxy/modes/http_proxy.py
index e19062b9..bc64ccd2 100644
--- a/mitmproxy/proxy/modes/http_proxy.py
+++ b/mitmproxy/proxy/modes/http_proxy.py
@@ -1,9 +1,9 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
-from ...protocol import Layer, ServerConnectionMixin
+from mitmproxy import protocol
-class HttpProxy(Layer, ServerConnectionMixin):
+class HttpProxy(protocol.Layer, protocol.ServerConnectionMixin):
def __call__(self):
layer = self.ctx.next_layer(self)
@@ -14,7 +14,7 @@ class HttpProxy(Layer, ServerConnectionMixin):
self.disconnect()
-class HttpUpstreamProxy(Layer, ServerConnectionMixin):
+class HttpUpstreamProxy(protocol.Layer, protocol.ServerConnectionMixin):
def __init__(self, ctx, server_address):
super(HttpUpstreamProxy, self).__init__(ctx, server_address=server_address)
diff --git a/mitmproxy/proxy/modes/reverse_proxy.py b/mitmproxy/proxy/modes/reverse_proxy.py
index c8e80a10..3739ac0e 100644
--- a/mitmproxy/proxy/modes/reverse_proxy.py
+++ b/mitmproxy/proxy/modes/reverse_proxy.py
@@ -1,9 +1,9 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
-from ...protocol import Layer, ServerConnectionMixin
+from mitmproxy import protocol
-class ReverseProxy(Layer, ServerConnectionMixin):
+class ReverseProxy(protocol.Layer, protocol.ServerConnectionMixin):
def __init__(self, ctx, server_address, server_tls):
super(ReverseProxy, self).__init__(ctx, server_address=server_address)
diff --git a/mitmproxy/proxy/modes/socks_proxy.py b/mitmproxy/proxy/modes/socks_proxy.py
index e2ce44ae..19437835 100644
--- a/mitmproxy/proxy/modes/socks_proxy.py
+++ b/mitmproxy/proxy/modes/socks_proxy.py
@@ -1,13 +1,13 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
-from netlib import socks, tcp
-from netlib.exceptions import TcpException
+import netlib.exceptions
+from mitmproxy import exceptions
+from mitmproxy import protocol
+from netlib import socks
+from netlib import tcp
-from ...exceptions import Socks5ProtocolException
-from ...protocol import Layer, ServerConnectionMixin
-
-class Socks5Proxy(Layer, ServerConnectionMixin):
+class Socks5Proxy(protocol.Layer, protocol.ServerConnectionMixin):
def __init__(self, ctx):
super(Socks5Proxy, self).__init__(ctx)
@@ -51,8 +51,8 @@ class Socks5Proxy(Layer, ServerConnectionMixin):
connect_reply.to_file(self.client_conn.wfile)
self.client_conn.wfile.flush()
- except (socks.SocksError, TcpException) as e:
- raise Socks5ProtocolException("SOCKS5 mode failure: %s" % repr(e))
+ except (socks.SocksError, netlib.exceptions.TcpException) as e:
+ raise exceptions.Socks5ProtocolException("SOCKS5 mode failure: %s" % repr(e))
# https://github.com/mitmproxy/mitmproxy/issues/839
address_bytes = (connect_request.addr.host.encode("idna"), connect_request.addr.port)
diff --git a/mitmproxy/proxy/modes/transparent_proxy.py b/mitmproxy/proxy/modes/transparent_proxy.py
index 3fdda656..c7df7900 100644
--- a/mitmproxy/proxy/modes/transparent_proxy.py
+++ b/mitmproxy/proxy/modes/transparent_proxy.py
@@ -1,11 +1,11 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
-from ... import platform
-from ...exceptions import ProtocolException
-from ...protocol import Layer, ServerConnectionMixin
+from mitmproxy import exceptions
+from mitmproxy import platform
+from mitmproxy import protocol
-class TransparentProxy(Layer, ServerConnectionMixin):
+class TransparentProxy(protocol.Layer, protocol.ServerConnectionMixin):
def __init__(self, ctx):
super(TransparentProxy, self).__init__(ctx)
@@ -15,7 +15,7 @@ class TransparentProxy(Layer, ServerConnectionMixin):
try:
self.server_conn.address = self.resolver.original_addr(self.client_conn.connection)
except Exception as e:
- raise ProtocolException("Transparent mode failure: %s" % repr(e))
+ raise exceptions.ProtocolException("Transparent mode failure: %s" % repr(e))
layer = self.ctx.next_layer(self)
try:
diff --git a/mitmproxy/proxy/root_context.py b/mitmproxy/proxy/root_context.py
index 96e7aab6..57183c7e 100644
--- a/mitmproxy/proxy/root_context.py
+++ b/mitmproxy/proxy/root_context.py
@@ -1,15 +1,13 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
import sys
import six
-from mitmproxy.exceptions import ProtocolException, TlsProtocolException
-from netlib.exceptions import TcpException
-from ..protocol import (
- RawTCPLayer, TlsLayer, Http1Layer, Http2Layer, is_tls_record_magic, ServerConnectionMixin,
- UpstreamConnectLayer, TlsClientHello
-)
-from .modes import HttpProxy, HttpUpstreamProxy, ReverseProxy
+import netlib.exceptions
+from mitmproxy import exceptions
+from mitmproxy import protocol
+from mitmproxy.proxy import modes
class RootContext(object):
@@ -50,53 +48,53 @@ class RootContext(object):
def _next_layer(self, top_layer):
try:
d = top_layer.client_conn.rfile.peek(3)
- except TcpException as e:
- six.reraise(ProtocolException, ProtocolException(str(e)), sys.exc_info()[2])
- client_tls = is_tls_record_magic(d)
+ except netlib.exceptions.TcpException as e:
+ six.reraise(exceptions.ProtocolException, exceptions.ProtocolException(str(e)), sys.exc_info()[2])
+ client_tls = protocol.is_tls_record_magic(d)
# 1. check for --ignore
if self.config.check_ignore:
ignore = self.config.check_ignore(top_layer.server_conn.address)
if not ignore and client_tls:
try:
- client_hello = TlsClientHello.from_client_conn(self.client_conn)
- except TlsProtocolException as e:
+ client_hello = protocol.TlsClientHello.from_client_conn(self.client_conn)
+ except exceptions.TlsProtocolException as e:
self.log("Cannot parse Client Hello: %s" % repr(e), "error")
else:
ignore = self.config.check_ignore((client_hello.sni, 443))
if ignore:
- return RawTCPLayer(top_layer, ignore=True)
+ return protocol.RawTCPLayer(top_layer, ignore=True)
# 2. Always insert a TLS layer, even if there's neither client nor server tls.
# An inline script may upgrade from http to https,
# in which case we need some form of TLS layer.
- if isinstance(top_layer, ReverseProxy):
- return TlsLayer(top_layer, client_tls, top_layer.server_tls)
- if isinstance(top_layer, ServerConnectionMixin) or isinstance(top_layer, UpstreamConnectLayer):
- return TlsLayer(top_layer, client_tls, client_tls)
+ if isinstance(top_layer, modes.ReverseProxy):
+ return protocol.TlsLayer(top_layer, client_tls, top_layer.server_tls)
+ if isinstance(top_layer, protocol.ServerConnectionMixin) or isinstance(top_layer, protocol.UpstreamConnectLayer):
+ return protocol.TlsLayer(top_layer, client_tls, client_tls)
# 3. In Http Proxy mode and Upstream Proxy mode, the next layer is fixed.
- if isinstance(top_layer, TlsLayer):
- if isinstance(top_layer.ctx, HttpProxy):
- return Http1Layer(top_layer, "regular")
- if isinstance(top_layer.ctx, HttpUpstreamProxy):
- return Http1Layer(top_layer, "upstream")
+ if isinstance(top_layer, protocol.TlsLayer):
+ if isinstance(top_layer.ctx, modes.HttpProxy):
+ return protocol.Http1Layer(top_layer, "regular")
+ if isinstance(top_layer.ctx, modes.HttpUpstreamProxy):
+ return protocol.Http1Layer(top_layer, "upstream")
# 4. Check for other TLS cases (e.g. after CONNECT).
if client_tls:
- return TlsLayer(top_layer, True, True)
+ return protocol.TlsLayer(top_layer, True, True)
# 4. Check for --tcp
if self.config.check_tcp(top_layer.server_conn.address):
- return RawTCPLayer(top_layer)
+ return protocol.RawTCPLayer(top_layer)
# 5. Check for TLS ALPN (HTTP1/HTTP2)
- if isinstance(top_layer, TlsLayer):
+ if isinstance(top_layer, protocol.TlsLayer):
alpn = top_layer.client_conn.get_alpn_proto_negotiated()
if alpn == b'h2':
- return Http2Layer(top_layer, 'transparent')
+ return protocol.Http2Layer(top_layer, 'transparent')
if alpn == b'http/1.1':
- return Http1Layer(top_layer, 'transparent')
+ return protocol.Http1Layer(top_layer, 'transparent')
# 6. Check for raw tcp mode
is_ascii = (
@@ -105,10 +103,10 @@ class RootContext(object):
all(65 <= x <= 90 and 97 <= x <= 122 for x in six.iterbytes(d))
)
if self.config.rawtcp and not is_ascii:
- return RawTCPLayer(top_layer)
+ return protocol.RawTCPLayer(top_layer)
# 7. Assume HTTP1 by default
- return Http1Layer(top_layer, 'transparent')
+ return protocol.Http1Layer(top_layer, 'transparent')
def log(self, msg, level, subs=()):
"""
@@ -132,7 +130,6 @@ class RootContext(object):
class Log(object):
-
def __init__(self, msg, level="info"):
self.msg = msg
self.level = level
diff --git a/mitmproxy/proxy/server.py b/mitmproxy/proxy/server.py
index 8483d3df..7e96911a 100644
--- a/mitmproxy/proxy/server.py
+++ b/mitmproxy/proxy/server.py
@@ -1,17 +1,18 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
-import traceback
-import sys
import socket
+import sys
+import traceback
+
import six
+import netlib.exceptions
+from mitmproxy import exceptions
+from mitmproxy import models
+from mitmproxy.proxy import modes
+from mitmproxy.proxy import root_context
from netlib import tcp
-from netlib.exceptions import TcpException
-from netlib.http.http1 import assemble_response
-from ..exceptions import ProtocolException, ServerException, ClientHandshakeException, Kill
-from ..models import ClientConnection, make_error_response
-from .modes import HttpUpstreamProxy, HttpProxy, ReverseProxy, TransparentProxy, Socks5Proxy
-from .root_context import RootContext, Log
+from netlib.http import http1
class DummyServer:
@@ -43,8 +44,8 @@ class ProxyServer(tcp.TCPServer):
super(ProxyServer, self).__init__((config.host, config.port))
except socket.error as e:
six.reraise(
- ServerException,
- ServerException('Error starting proxy server: ' + repr(e)),
+ exceptions.ServerException,
+ exceptions.ServerException('Error starting proxy server: ' + repr(e)),
sys.exc_info()[2]
)
self.channel = None
@@ -67,7 +68,7 @@ class ConnectionHandler(object):
def __init__(self, client_conn, client_address, config, channel):
self.config = config
"""@type: mitmproxy.proxy.config.ProxyConfig"""
- self.client_conn = ClientConnection(
+ self.client_conn = models.ClientConnection(
client_conn,
client_address,
None)
@@ -76,7 +77,7 @@ class ConnectionHandler(object):
"""@type: mitmproxy.controller.Channel"""
def _create_root_layer(self):
- root_context = RootContext(
+ root_ctx = root_context.RootContext(
self.client_conn,
self.config,
self.channel
@@ -84,25 +85,25 @@ class ConnectionHandler(object):
mode = self.config.mode
if mode == "upstream":
- return HttpUpstreamProxy(
- root_context,
+ return modes.HttpUpstreamProxy(
+ root_ctx,
self.config.upstream_server.address
)
elif mode == "transparent":
- return TransparentProxy(root_context)
+ return modes.TransparentProxy(root_ctx)
elif mode == "reverse":
server_tls = self.config.upstream_server.scheme == "https"
- return ReverseProxy(
- root_context,
+ return modes.ReverseProxy(
+ root_ctx,
self.config.upstream_server.address,
server_tls
)
elif mode == "socks5":
- return Socks5Proxy(root_context)
+ return modes.Socks5Proxy(root_ctx)
elif mode == "regular":
- return HttpProxy(root_context)
+ return modes.HttpProxy(root_ctx)
elif callable(mode): # pragma: no cover
- return mode(root_context)
+ return mode(root_ctx)
else: # pragma: no cover
raise ValueError("Unknown proxy mode: %s" % mode)
@@ -114,11 +115,11 @@ class ConnectionHandler(object):
try:
root_layer = self.channel.ask("clientconnect", root_layer)
root_layer()
- except Kill:
+ except exceptions.Kill:
self.log("Connection killed", "info")
- except ProtocolException as e:
+ except exceptions.ProtocolException as e:
- if isinstance(e, ClientHandshakeException):
+ if isinstance(e, exceptions.ClientHandshakeException):
self.log(
"Client Handshake failed. "
"The client may not trust the proxy's certificate for {}.".format(e.server),
@@ -133,9 +134,9 @@ class ConnectionHandler(object):
# we send an HTTP error response, which is both
# understandable by HTTP clients and humans.
try:
- error_response = make_error_response(502, repr(e))
- self.client_conn.send(assemble_response(error_response))
- except TcpException:
+ error_response = models.make_error_response(502, repr(e))
+ self.client_conn.send(http1.assemble_response(error_response))
+ except netlib.exceptions.TcpException:
pass
except Exception:
self.log(traceback.format_exc(), "error")
@@ -149,4 +150,4 @@ class ConnectionHandler(object):
def log(self, msg, level):
msg = "{}: {}".format(repr(self.client_conn.address), msg)
- self.channel.tell("log", Log(msg, level))
+ self.channel.tell("log", root_context.Log(msg, level))
diff --git a/mitmproxy/script/__init__.py b/mitmproxy/script/__init__.py
index 3ee19b04..d6bff4c7 100644
--- a/mitmproxy/script/__init__.py
+++ b/mitmproxy/script/__init__.py
@@ -1,8 +1,8 @@
+from . import reloader
+from .concurrent import concurrent
from .script import Script
from .script_context import ScriptContext
-from .concurrent import concurrent
from ..exceptions import ScriptException
-from . import reloader
__all__ = [
"Script",
diff --git a/mitmproxy/script/concurrent.py b/mitmproxy/script/concurrent.py
index 2f25e78c..43d0d328 100644
--- a/mitmproxy/script/concurrent.py
+++ b/mitmproxy/script/concurrent.py
@@ -3,6 +3,7 @@ This module provides a @concurrent decorator primitive to
offload computations from mitmproxy's main master thread.
"""
from __future__ import absolute_import, print_function, division
+
import threading
diff --git a/mitmproxy/script/reloader.py b/mitmproxy/script/reloader.py
index 99ce7f60..f5470bbf 100644
--- a/mitmproxy/script/reloader.py
+++ b/mitmproxy/script/reloader.py
@@ -1,6 +1,10 @@
+from __future__ import absolute_import, print_function, division
+
import os
import sys
+
from watchdog.events import RegexMatchingEventHandler
+
if sys.platform == 'darwin': # pragma: no cover
from watchdog.observers.polling import PollingObserver as Observer
else:
diff --git a/mitmproxy/script/script.py b/mitmproxy/script/script.py
index 484025b4..70f74817 100644
--- a/mitmproxy/script/script.py
+++ b/mitmproxy/script/script.py
@@ -4,14 +4,15 @@ Script objects know nothing about mitmproxy or mitmproxy's API - this knowledge
by the mitmproxy-specific ScriptContext.
"""
# Do not import __future__ here, this would apply transitively to the inline scripts.
+from __future__ import absolute_import, print_function, division
+
import os
import shlex
-import traceback
import sys
import six
-from ..exceptions import ScriptException
+from mitmproxy import exceptions
class Script(object):
@@ -42,7 +43,7 @@ class Script(object):
@staticmethod
def parse_command(command):
if not command or not command.strip():
- raise ScriptException("Empty script command.")
+ raise exceptions.ScriptException("Empty script command.")
# Windows: escape all backslashes in the path.
if os.name == "nt": # pragma: no cover
backslashes = shlex.split(command, posix=False)[0].count("\\")
@@ -50,13 +51,13 @@ class Script(object):
args = shlex.split(command) # pragma: no cover
args[0] = os.path.expanduser(args[0])
if not os.path.exists(args[0]):
- raise ScriptException(
+ raise exceptions.ScriptException(
("Script file not found: %s.\r\n"
"If your script path contains spaces, "
"make sure to wrap it in additional quotes, e.g. -s \"'./foo bar/baz.py' --args\".") %
args[0])
elif os.path.isdir(args[0]):
- raise ScriptException("Not a file: %s" % args[0])
+ raise exceptions.ScriptException("Not a file: %s" % args[0])
return args
def load(self):
@@ -70,7 +71,7 @@ class Script(object):
ScriptException on failure
"""
if self.ns is not None:
- raise ScriptException("Script is already loaded")
+ raise exceptions.ScriptException("Script is already loaded")
script_dir = os.path.dirname(os.path.abspath(self.args[0]))
self.ns = {'__file__': os.path.abspath(self.args[0])}
sys.path.append(script_dir)
@@ -78,11 +79,11 @@ class Script(object):
try:
with open(self.filename) as f:
code = compile(f.read(), self.filename, 'exec')
- exec (code, self.ns, self.ns)
+ exec(code, self.ns, self.ns)
except Exception:
six.reraise(
- ScriptException,
- ScriptException.from_exception_context(),
+ exceptions.ScriptException,
+ exceptions.ScriptException.from_exception_context(),
sys.exc_info()[2]
)
finally:
@@ -108,15 +109,15 @@ class Script(object):
ScriptException if there was an exception.
"""
if self.ns is None:
- raise ScriptException("Script not loaded.")
+ raise exceptions.ScriptException("Script not loaded.")
f = self.ns.get(name)
if f:
try:
return f(self.ctx, *args, **kwargs)
except Exception:
six.reraise(
- ScriptException,
- ScriptException.from_exception_context(),
+ exceptions.ScriptException,
+ exceptions.ScriptException.from_exception_context(),
sys.exc_info()[2]
)
else:
diff --git a/mitmproxy/script/script_context.py b/mitmproxy/script/script_context.py
index cd5d4b61..44e2736b 100644
--- a/mitmproxy/script/script_context.py
+++ b/mitmproxy/script/script_context.py
@@ -2,7 +2,8 @@
The mitmproxy script context provides an API to inline scripts.
"""
from __future__ import absolute_import, print_function, division
-from .. import contentviews
+
+from mitmproxy import contentviews
class ScriptContext(object):
diff --git a/mitmproxy/stateobject.py b/mitmproxy/stateobject.py
index 765c35d6..6283d845 100644
--- a/mitmproxy/stateobject.py
+++ b/mitmproxy/stateobject.py
@@ -1,9 +1,10 @@
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function, division
import six
-from typing import List, Any
+from typing import Any
+from typing import List
-from netlib.utils import Serializable
+import netlib.basetypes
def _is_list(cls):
@@ -13,7 +14,7 @@ def _is_list(cls):
return issubclass(cls, List) or is_list_bugfix
-class StateObject(Serializable):
+class StateObject(netlib.basetypes.Serializable):
"""
An object with serializable state.
diff --git a/mitmproxy/tnetstring.py b/mitmproxy/tnetstring.py
index d9d61258..6b1c117a 100644
--- a/mitmproxy/tnetstring.py
+++ b/mitmproxy/tnetstring.py
@@ -67,6 +67,8 @@ like so::
u'\u03b1'
"""
+from collections import deque
+
import six
__ver_major__ = 0
@@ -77,9 +79,6 @@ __version__ = "%d.%d.%d%s" % (
__ver_major__, __ver_minor__, __ver_patch__, __ver_sub__)
-from collections import deque
-
-
def dumps(value, encoding=None):
"""dumps(object,encoding=None) -> string
diff --git a/mitmproxy/utils.py b/mitmproxy/utils.py
index e56ac473..680bc495 100644
--- a/mitmproxy/utils.py
+++ b/mitmproxy/utils.py
@@ -1,7 +1,8 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
+
import datetime
-import time
import json
+import time
import netlib.utils
@@ -24,32 +25,6 @@ def format_timestamp_with_milli(s):
return d.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
-def isBin(s):
- """
- Does this string have any non-ASCII characters?
- """
- for i in s:
- i = ord(i)
- if i < 9 or 13 < i < 32 or 126 < i:
- return True
- return False
-
-
-def isMostlyBin(s):
- s = s[:100]
- return sum(isBin(ch) for ch in s) / len(s) > 0.3
-
-
-def isXML(s):
- for i in s:
- if i in "\n \t":
- continue
- elif i == "<":
- return True
- else:
- return False
-
-
def pretty_json(s):
try:
p = json.loads(s)
@@ -58,20 +33,6 @@ def pretty_json(s):
return json.dumps(p, sort_keys=True, indent=4)
-def pretty_duration(secs):
- formatters = [
- (100, "{:.0f}s"),
- (10, "{:2.1f}s"),
- (1, "{:1.2f}s"),
- ]
-
- for limit, formatter in formatters:
- if secs >= limit:
- return formatter.format(secs)
- # less than 1 sec
- return "{:.0f}ms".format(secs * 1000)
-
-
pkg_data = netlib.utils.Data(__name__)
@@ -105,44 +66,3 @@ class LRUCache:
d = self.cacheList.pop()
self.cache.pop(d)
return ret
-
-
-def clean_hanging_newline(t):
- """
- Many editors will silently add a newline to the final line of a
- document (I'm looking at you, Vim). This function fixes this common
- problem at the risk of removing a hanging newline in the rare cases
- where the user actually intends it.
- """
- if t and t[-1] == "\n":
- return t[:-1]
- return t
-
-
-def parse_size(s):
- """
- Parses a size specification. Valid specifications are:
-
- 123: bytes
- 123k: kilobytes
- 123m: megabytes
- 123g: gigabytes
- """
- if not s:
- return None
- mult = None
- if s[-1].lower() == "k":
- mult = 1024**1
- elif s[-1].lower() == "m":
- mult = 1024**2
- elif s[-1].lower() == "g":
- mult = 1024**3
-
- if mult:
- s = s[:-1]
- else:
- mult = 1
- try:
- return int(s) * mult
- except ValueError:
- raise ValueError("Invalid size specification: %s" % s)
diff --git a/mitmproxy/version.py b/mitmproxy/version.py
index da1e7229..0ebb0829 100644
--- a/mitmproxy/version.py
+++ b/mitmproxy/version.py
@@ -1,6 +1,13 @@
-from __future__ import (absolute_import, print_function, division)
+from __future__ import absolute_import, print_function, division
from netlib.version import VERSION, IVERSION
NAME = "mitmproxy"
NAMEVERSION = NAME + " " + VERSION
+
+__all__ = [
+ "NAME",
+ "NAMEVERSION",
+ "VERSION",
+ "IVERSION",
+]
diff --git a/mitmproxy/web/__init__.py b/mitmproxy/web/__init__.py
index 956d221d..80a65886 100644
--- a/mitmproxy/web/__init__.py
+++ b/mitmproxy/web/__init__.py
@@ -1,14 +1,16 @@
-from __future__ import absolute_import, print_function
+from __future__ import absolute_import, print_function, division
+
import collections
-import tornado.ioloop
-import tornado.httpserver
import sys
-from netlib.http import authentication
+import tornado.httpserver
+import tornado.ioloop
-from .. import flow
-from ..exceptions import FlowReadException
-from . import app
+from mitmproxy import controller
+from mitmproxy import exceptions
+from mitmproxy import flow
+from mitmproxy.web import app
+from netlib.http import authentication
class Stop(Exception):
@@ -156,7 +158,7 @@ class WebMaster(flow.FlowMaster):
if options.rfile:
try:
self.load_flows_file(options.rfile)
- except FlowReadException as v:
+ except exceptions.FlowReadException as v:
self.add_event(
"Could not read flow file: %s" % v,
"error"
@@ -194,19 +196,21 @@ class WebMaster(flow.FlowMaster):
if self.state.intercept and self.state.intercept(
f) and not f.request.is_replay:
f.intercept(self)
- else:
- f.reply()
+ f.reply.take()
- def handle_request(self, f):
- super(WebMaster, self).handle_request(f)
+ @controller.handler
+ def request(self, f):
+ super(WebMaster, self).request(f)
self._process_flow(f)
- def handle_response(self, f):
- super(WebMaster, self).handle_response(f)
+ @controller.handler
+ def response(self, f):
+ super(WebMaster, self).response(f)
self._process_flow(f)
- def handle_error(self, f):
- super(WebMaster, self).handle_error(f)
+ @controller.handler
+ def error(self, f):
+ super(WebMaster, self).error(f)
self._process_flow(f)
def add_event(self, e, level="info"):
diff --git a/mitmproxy/web/app.py b/mitmproxy/web/app.py
index 23a77cb0..43b2bad1 100644
--- a/mitmproxy/web/app.py
+++ b/mitmproxy/web/app.py
@@ -1,14 +1,16 @@
+from __future__ import absolute_import, print_function, division
+
+import base64
+import json
+import logging
import os.path
import re
import six
-import tornado.web
import tornado.websocket
-import logging
-import json
-import base64
-from .. import version, filt
+from mitmproxy import filt
+from mitmproxy import version
def _strip_content(flow_state):
@@ -112,7 +114,8 @@ class RequestHandler(BasicAuth, tornado.web.RequestHandler):
class IndexHandler(RequestHandler):
def get(self):
- _ = self.xsrf_token # https://github.com/tornadoweb/tornado/issues/645
+ token = self.xsrf_token # https://github.com/tornadoweb/tornado/issues/645
+ assert token
self.render("index.html")
@@ -282,11 +285,21 @@ class Events(RequestHandler):
class Settings(RequestHandler):
def get(self):
+
self.write(dict(
data=dict(
version=version.VERSION,
mode=str(self.master.server.config.mode),
- intercept=self.state.intercept_txt
+ intercept=self.state.intercept_txt,
+ showhost=self.master.options.showhost,
+ no_upstream_cert=self.master.server.config.no_upstream_cert,
+ rawtcp=self.master.server.config.rawtcp,
+ http2=self.master.server.config.http2,
+ anticache=self.master.options.anticache,
+ anticomp=self.master.options.anticomp,
+ stickyauth=self.master.stickyauth_txt,
+ stickycookie=self.master.stickycookie_txt,
+ stream= self.master.stream_large_bodies.max_size if self.master.stream_large_bodies else False
)
))
@@ -296,6 +309,33 @@ class Settings(RequestHandler):
if k == "intercept":
self.state.set_intercept(v)
update[k] = v
+ elif k == "showhost":
+ self.master.options.showhost = v
+ update[k] = v
+ elif k == "no_upstream_cert":
+ self.master.server.config.no_upstream_cert = v
+ update[k] = v
+ elif k == "rawtcp":
+ self.master.server.config.rawtcp = v
+ update[k] = v
+ elif k == "http2":
+ self.master.server.config.http2 = v
+ update[k] = v
+ elif k == "anticache":
+ self.master.options.anticache = v
+ update[k] = v
+ elif k == "anticomp":
+ self.master.options.anticomp = v
+ update[k] = v
+ elif k == "stickycookie":
+ self.master.set_stickycookie(v)
+ update[k] = v
+ elif k == "stickyauth":
+ self.master.set_stickyauth(v)
+ update[k] = v
+ elif k == "stream":
+ self.master.set_stream_large_bodies(v)
+ update[k] = v
else:
print("Warning: Unknown setting {}: {}".format(k, v))
diff --git a/mitmproxy/web/static/app.css b/mitmproxy/web/static/app.css
index 824dd827..ae2e963f 100644
--- a/mitmproxy/web/static/app.css
+++ b/mitmproxy/web/static/app.css
@@ -146,6 +146,7 @@ header .menu {
min-height: 1px;
padding-left: 2.5px;
padding-right: 2.5px;
+ margin-bottom: 5px;
}
@media (min-width: 768px) {
.filter-input {
@@ -163,8 +164,48 @@ header .menu {
max-height: 500px;
overflow-y: auto;
}
-.menu .btn {
- margin: 2px 2px 2px 2px;
+.menu .toggle-btn {
+ float: left;
+ width: 33.33333333%;
+ position: relative;
+ min-height: 1px;
+ padding-left: 2.5px;
+ padding-right: 2.5px;
+ margin-bottom: 5px;
+}
+@media (min-width: 768px) {
+ .menu .toggle-btn {
+ float: left;
+ width: 25%;
+ }
+}
+@media (min-width: 1200px) {
+ .menu .toggle-btn {
+ float: left;
+ width: 16.66666667%;
+ }
+}
+.menu .toggle-btn .btn {
+ width: 100%;
+}
+.menu .toggle-input-btn {
+ position: relative;
+ min-height: 1px;
+ padding-left: 2.5px;
+ padding-right: 2.5px;
+ margin-bottom: 5px;
+}
+@media (min-width: 768px) {
+ .menu .toggle-input-btn {
+ float: left;
+ width: 50%;
+ }
+}
+@media (min-width: 1200px) {
+ .menu .toggle-input-btn {
+ float: left;
+ width: 33.33333333%;
+ }
}
.flow-table {
width: 100%;
diff --git a/mitmproxy/web/static/app.js b/mitmproxy/web/static/app.js
index 1e8e313d..e02df55a 100644
--- a/mitmproxy/web/static/app.js
+++ b/mitmproxy/web/static/app.js
@@ -481,7 +481,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
Object.defineProperty(exports, "__esModule", {
value: true
});
-exports.ToggleComponent = exports.Splitter = exports.Router = undefined;
+exports.ToggleInputButton = exports.ToggleButton = exports.Splitter = undefined;
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _react = require("react");
@@ -491,37 +493,19 @@ var _reactDom = require("react-dom");
var _reactDom2 = _interopRequireDefault(_reactDom);
+var _utils = require("../utils.js");
+
var _lodash = require("lodash");
var _lodash2 = _interopRequireDefault(_lodash);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-var Router = exports.Router = {
- contextTypes: {
- location: _react2.default.PropTypes.object,
- router: _react2.default.PropTypes.object.isRequired
- },
- updateLocation: function updateLocation(pathname, queryUpdate) {
- if (pathname === undefined) {
- pathname = this.context.location.pathname;
- }
- var query = this.context.location.query;
- if (queryUpdate !== undefined) {
- for (var i in queryUpdate) {
- if (queryUpdate.hasOwnProperty(i)) {
- query[i] = queryUpdate[i] || undefined; //falsey values shall be removed.
- }
- }
- }
- this.context.router.replace({ pathname: pathname, query: query });
- },
- getQuery: function getQuery() {
- // For whatever reason, react-router always returns the same object, which makes comparing
- // the current props with nextProps impossible. As a workaround, we just clone the query object.
- return _lodash2.default.clone(this.context.location.query);
- }
-};
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+
+function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var Splitter = exports.Splitter = _react2.default.createClass({
displayName: "Splitter",
@@ -631,28 +615,91 @@ var Splitter = exports.Splitter = _react2.default.createClass({
}
});
-var ToggleComponent = exports.ToggleComponent = function ToggleComponent(props) {
+var ToggleButton = exports.ToggleButton = function ToggleButton(props) {
return _react2.default.createElement(
"div",
- {
- className: "btn " + (props.checked ? "btn-primary" : "btn-default"),
- onClick: props.onToggleChanged },
+ { className: "input-group toggle-btn" },
_react2.default.createElement(
- "span",
- null,
- _react2.default.createElement("i", { className: "fa " + (props.checked ? "fa-check-square-o" : "fa-square-o") }),
- " ",
- props.name
+ "div",
+ {
+ className: "btn " + (props.checked ? "btn-primary" : "btn-default"),
+ onClick: props.onToggleChanged },
+ _react2.default.createElement(
+ "span",
+ { className: "fa " + (props.checked ? "fa-check-square-o" : "fa-square-o") },
+ " ",
+ props.name
+ )
)
);
};
-ToggleComponent.propTypes = {
+ToggleButton.propTypes = {
+ name: _react2.default.PropTypes.string.isRequired,
+ onToggleChanged: _react2.default.PropTypes.func.isRequired
+};
+
+var ToggleInputButton = exports.ToggleInputButton = function (_React$Component) {
+ _inherits(ToggleInputButton, _React$Component);
+
+ function ToggleInputButton(props) {
+ _classCallCheck(this, ToggleInputButton);
+
+ var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ToggleInputButton).call(this, props));
+
+ _this.state = { txt: props.txt };
+ return _this;
+ }
+
+ _createClass(ToggleInputButton, [{
+ key: "render",
+ value: function render() {
+ var _this2 = this;
+
+ return _react2.default.createElement(
+ "div",
+ { className: "input-group toggle-input-btn" },
+ _react2.default.createElement(
+ "span",
+ {
+ className: "input-group-btn",
+ onClick: function onClick() {
+ return _this2.props.onToggleChanged(_this2.state.txt);
+ } },
+ _react2.default.createElement(
+ "div",
+ { className: "btn " + (this.props.checked ? "btn-primary" : "btn-default") },
+ _react2.default.createElement("span", { className: "fa " + (this.props.checked ? "fa-check-square-o" : "fa-square-o") }),
+ " ",
+ this.props.name
+ )
+ ),
+ _react2.default.createElement("input", {
+ className: "form-control",
+ placeholder: this.props.placeholder,
+ disabled: this.props.checked,
+ value: this.state.txt,
+ type: this.props.inputType,
+ onChange: function onChange(e) {
+ return _this2.setState({ txt: e.target.value });
+ },
+ onKeyDown: function onKeyDown(e) {
+ if (e.keyCode === _utils.Key.ENTER) _this2.props.onToggleChanged(_this2.state.txt);e.stopPropagation();
+ } })
+ );
+ }
+ }]);
+
+ return ToggleInputButton;
+}(_react2.default.Component);
+
+ToggleInputButton.propTypes = {
name: _react2.default.PropTypes.string.isRequired,
+ txt: _react2.default.PropTypes.string.isRequired,
onToggleChanged: _react2.default.PropTypes.func.isRequired
};
-},{"lodash":"lodash","react":"react","react-dom":"react-dom"}],5:[function(require,module,exports){
+},{"../utils.js":27,"lodash":"lodash","react":"react","react-dom":"react-dom"}],5:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -931,8 +978,6 @@ var _shallowequal = require("shallowequal");
var _shallowequal2 = _interopRequireDefault(_shallowequal);
-var _common = require("./common.js");
-
var _actions = require("../actions.js");
var _AutoScroll = require("./helpers/AutoScroll");
@@ -1120,8 +1165,6 @@ var AutoScrollEventLog = (0, _AutoScroll2.default)(EventLogContents);
var EventLog = _react2.default.createClass({
displayName: "EventLog",
-
- mixins: [_common.Router],
getInitialState: function getInitialState() {
return {
filter: {
@@ -1134,7 +1177,7 @@ var EventLog = _react2.default.createClass({
close: function close() {
var d = {};
d[_actions.Query.SHOW_EVENTLOG] = undefined;
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
},
toggleLevel: function toggleLevel(level) {
var filter = _lodash2.default.extend({}, this.state.filter);
@@ -1165,7 +1208,7 @@ var EventLog = _react2.default.createClass({
exports.default = EventLog;
-},{"../actions.js":2,"../store/view.js":26,"./common.js":4,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"lodash":"lodash","react":"react","react-dom":"react-dom","shallowequal":"shallowequal"}],7:[function(require,module,exports){
+},{"../actions.js":2,"../store/view.js":26,"./helpers/AutoScroll":16,"./helpers/VirtualScroll":17,"lodash":"lodash","react":"react","react-dom":"react-dom","shallowequal":"shallowequal"}],7:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -1749,13 +1792,17 @@ var _utils2 = require("../../utils.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-var image_regex = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i;
var ViewImage = _react2.default.createClass({
displayName: "ViewImage",
+ propTypes: {
+ flow: _react2.default.PropTypes.object.isRequired,
+ message: _react2.default.PropTypes.object.isRequired
+ },
statics: {
+ regex: /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i,
matches: function matches(message) {
- return image_regex.test(_utils.MessageUtils.getContentType(message));
+ return ViewImage.regex.test(_utils.MessageUtils.getContentType(message));
}
},
render: function render() {
@@ -1768,7 +1815,13 @@ var ViewImage = _react2.default.createClass({
}
});
-var RawMixin = {
+var ContentLoader = _react2.default.createClass({
+ displayName: "ContentLoader",
+
+ propTypes: {
+ flow: _react2.default.PropTypes.object.isRequired,
+ message: _react2.default.PropTypes.object.isRequired
+ },
getInitialState: function getInitialState() {
return {
content: undefined,
@@ -1816,43 +1869,53 @@ var RawMixin = {
_react2.default.createElement("i", { className: "fa fa-spinner fa-spin" })
);
}
- return this.renderContent();
+ return _react2.default.cloneElement(this.props.children, {
+ content: this.state.content
+ });
}
-};
+});
var ViewRaw = _react2.default.createClass({
displayName: "ViewRaw",
- mixins: [RawMixin],
+ propTypes: {
+ content: _react2.default.PropTypes.string.isRequired
+ },
statics: {
+ textView: true,
matches: function matches(message) {
return true;
}
},
- renderContent: function renderContent() {
+ render: function render() {
return _react2.default.createElement(
"pre",
null,
- this.state.content
+ this.props.content
);
}
});
-var json_regex = /^application\/json$/i;
var ViewJSON = _react2.default.createClass({
displayName: "ViewJSON",
- mixins: [RawMixin],
+ propTypes: {
+ content: _react2.default.PropTypes.string.isRequired
+ },
statics: {
+ textView: true,
+ regex: /^application\/json$/i,
matches: function matches(message) {
- return json_regex.test(_utils.MessageUtils.getContentType(message));
+ return ViewJSON.regex.test(_utils.MessageUtils.getContentType(message));
}
},
- renderContent: function renderContent() {
- var json = this.state.content;
+ render: function render() {
+ var json = this.props.content;
try {
json = JSON.stringify(JSON.parse(json), null, 2);
- } catch (e) {}
+ } catch (e) {
+ // @noop
+ }
return _react2.default.createElement(
"pre",
null,
@@ -1864,6 +1927,10 @@ var ViewJSON = _react2.default.createClass({
var ViewAuto = _react2.default.createClass({
displayName: "ViewAuto",
+ propTypes: {
+ message: _react2.default.PropTypes.object.isRequired,
+ flow: _react2.default.PropTypes.object.isRequired
+ },
statics: {
matches: function matches() {
return false; // don't match itself
@@ -1878,8 +1945,20 @@ var ViewAuto = _react2.default.createClass({
}
},
render: function render() {
+ var _props = this.props;
+ var message = _props.message;
+ var flow = _props.flow;
+
var View = ViewAuto.findView(this.props.message);
- return _react2.default.createElement(View, this.props);
+ if (View.textView) {
+ return _react2.default.createElement(
+ ContentLoader,
+ { message: message, flow: flow },
+ _react2.default.createElement(View, { content: "" })
+ );
+ } else {
+ return _react2.default.createElement(View, { message: message, flow: flow });
+ }
}
});
@@ -2004,6 +2083,10 @@ var ContentView = _react2.default.createClass({
}
},
render: function render() {
+ var _props2 = this.props;
+ var flow = _props2.flow;
+ var message = _props2.message;
+
var message = this.props.message;
if (message.contentLength === 0) {
return _react2.default.createElement(ContentEmpty, this.props);
@@ -2018,7 +2101,11 @@ var ContentView = _react2.default.createClass({
return _react2.default.createElement(
"div",
null,
- _react2.default.createElement(this.state.View, this.props),
+ this.state.View.textView ? _react2.default.createElement(
+ ContentLoader,
+ { flow: flow, message: message },
+ _react2.default.createElement(this.state.View, { content: "" })
+ ) : _react2.default.createElement(this.state.View, { flow: flow, message: message }),
_react2.default.createElement(
"div",
{ className: "view-options text-center" },
@@ -2315,8 +2402,6 @@ var _react = require("react");
var _react2 = _interopRequireDefault(_react);
-var _common = require("../common.js");
-
var _nav = require("./nav.js");
var _nav2 = _interopRequireDefault(_nav);
@@ -2343,7 +2428,6 @@ var allTabs = {
var FlowView = _react2.default.createClass({
displayName: "FlowView",
- mixins: [_common.StickyHeadMixin, _common.Router],
getInitialState: function getInitialState() {
return {
prompt: false
@@ -2367,7 +2451,7 @@ var FlowView = _react2.default.createClass({
this.selectTab(tabs[nextIndex]);
},
selectTab: function selectTab(panel) {
- this.updateLocation("/flows/" + this.props.flow.id + "/" + panel);
+ this.props.updateLocation("/flows/" + this.props.flow.id + "/" + panel);
},
promptEdit: function promptEdit() {
var options;
@@ -2436,7 +2520,7 @@ var FlowView = _react2.default.createClass({
exports.default = FlowView;
-},{"../common.js":4,"../prompt.js":19,"./details.js":10,"./messages.js":12,"./nav.js":13,"react":"react"}],12:[function(require,module,exports){
+},{"../prompt.js":19,"./details.js":10,"./messages.js":12,"./nav.js":13,"react":"react"}],12:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -2894,6 +2978,8 @@ var _react = require("react");
var _react2 = _interopRequireDefault(_react);
+var _utils = require("../utils.js");
+
var _common = require("./common.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -2906,6 +2992,15 @@ function Footer(_ref) {
var settings = _ref.settings;
var mode = settings.mode;
var intercept = settings.intercept;
+ var showhost = settings.showhost;
+ var no_upstream_cert = settings.no_upstream_cert;
+ var rawtcp = settings.rawtcp;
+ var http2 = settings.http2;
+ var anticache = settings.anticache;
+ var anticomp = settings.anticomp;
+ var stickyauth = settings.stickyauth;
+ var stickycookie = settings.stickycookie;
+ var stream = settings.stream;
return _react2.default.createElement(
"footer",
@@ -2921,19 +3016,65 @@ function Footer(_ref) {
{ className: "label label-success" },
"Intercept: ",
intercept
+ ),
+ showhost && _react2.default.createElement(
+ "span",
+ { className: "label label-success" },
+ "showhost"
+ ),
+ no_upstream_cert && _react2.default.createElement(
+ "span",
+ { className: "label label-success" },
+ "no-upstream-cert"
+ ),
+ rawtcp && _react2.default.createElement(
+ "span",
+ { className: "label label-success" },
+ "raw-tcp"
+ ),
+ !http2 && _react2.default.createElement(
+ "span",
+ { className: "label label-success" },
+ "no-http2"
+ ),
+ anticache && _react2.default.createElement(
+ "span",
+ { className: "label label-success" },
+ "anticache"
+ ),
+ anticomp && _react2.default.createElement(
+ "span",
+ { className: "label label-success" },
+ "anticomp"
+ ),
+ stickyauth && _react2.default.createElement(
+ "span",
+ { className: "label label-success" },
+ "stickyauth: ",
+ stickyauth
+ ),
+ stickycookie && _react2.default.createElement(
+ "span",
+ { className: "label label-success" },
+ "stickycookie: ",
+ stickycookie
+ ),
+ stream && _react2.default.createElement(
+ "span",
+ { className: "label label-success" },
+ "stream: ",
+ (0, _utils.formatSize)(stream)
)
);
}
-},{"./common.js":4,"react":"react"}],15:[function(require,module,exports){
+},{"../utils.js":27,"./common.js":4,"react":"react"}],15:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
-exports.Header = exports.MainMenu = undefined;
-
-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+exports.Header = exports.OptionMenu = exports.MainMenu = undefined;
var _react = require("react");
@@ -2959,12 +3100,6 @@ var _actions = require("../actions.js");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
-
-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
-
-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
-
var FilterDocs = _react2.default.createClass({
displayName: "FilterDocs",
@@ -3151,7 +3286,6 @@ var FilterInput = _react2.default.createClass({
var MainMenu = exports.MainMenu = _react2.default.createClass({
displayName: "MainMenu",
- mixins: [_common.Router],
propTypes: {
settings: _react2.default.PropTypes.object.isRequired
},
@@ -3162,19 +3296,19 @@ var MainMenu = exports.MainMenu = _react2.default.createClass({
onSearchChange: function onSearchChange(val) {
var d = {};
d[_actions.Query.SEARCH] = val;
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
},
onHighlightChange: function onHighlightChange(val) {
var d = {};
d[_actions.Query.HIGHLIGHT] = val;
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
},
onInterceptChange: function onInterceptChange(val) {
_actions.SettingsActions.update({ intercept: val });
},
render: function render() {
- var search = this.getQuery()[_actions.Query.SEARCH] || "";
- var highlight = this.getQuery()[_actions.Query.HIGHLIGHT] || "";
+ var search = this.props.query[_actions.Query.SEARCH] || "";
+ var highlight = this.props.query[_actions.Query.HIGHLIGHT] || "";
var intercept = this.props.settings.intercept || "";
return _react2.default.createElement(
@@ -3217,78 +3351,122 @@ var ViewMenu = _react2.default.createClass({
title: "View",
route: "flows"
},
- mixins: [_common.Router],
toggleEventLog: function toggleEventLog() {
var d = {};
- if (this.getQuery()[_actions.Query.SHOW_EVENTLOG]) {
+ if (this.props.query[_actions.Query.SHOW_EVENTLOG]) {
d[_actions.Query.SHOW_EVENTLOG] = undefined;
} else {
d[_actions.Query.SHOW_EVENTLOG] = "t"; // any non-false value will do it, keep it short
}
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
console.log('toggleevent');
},
render: function render() {
- var showEventLog = this.getQuery()[_actions.Query.SHOW_EVENTLOG];
+ var showEventLog = this.props.query[_actions.Query.SHOW_EVENTLOG];
return _react2.default.createElement(
"div",
null,
- _react2.default.createElement(_common.ToggleComponent, {
- checked: showEventLog,
- name: "Show Eventlog",
- onToggleChanged: this.toggleEventLog })
+ _react2.default.createElement(
+ "div",
+ { className: "menu-row" },
+ _react2.default.createElement(_common.ToggleButton, {
+ checked: showEventLog,
+ name: "Show Eventlog",
+ onToggleChanged: this.toggleEventLog })
+ ),
+ _react2.default.createElement("div", { className: "clearfix" })
);
}
});
-var OptionMenu = function (_React$Component) {
- _inherits(OptionMenu, _React$Component);
-
- function OptionMenu(props) {
- _classCallCheck(this, OptionMenu);
-
- var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(OptionMenu).call(this, props));
-
- _this.state = {
- options: [{ name: "--host", checked: true }, { name: "--no-upstream-cert", checked: false }, { name: "--http2", checked: false }, { name: "--anticache", checked: false }, { name: "--anticomp", checked: false }, { name: "--stickycookie", checked: true }, { name: "--stickyauth", checked: false }, { name: "--stream", checked: false }]
- };
- return _this;
- }
-
- _createClass(OptionMenu, [{
- key: "setOption",
- value: function setOption(entry) {
- console.log(entry.name); //TODO: get options from outside and remove state
- entry.checked = !entry.checked;
- this.setState({ options: this.state.options });
- }
- }, {
- key: "render",
- value: function render() {
- var _this2 = this;
-
- return _react2.default.createElement(
- "div",
- null,
- this.state.options.map(function (entry, i) {
- return _react2.default.createElement(_common.ToggleComponent, {
- key: i,
- checked: entry.checked,
- name: entry.name,
- onToggleChanged: function onToggleChanged() {
- return _this2.setOption(entry);
- } });
- })
- );
- }
- }]);
-
- return OptionMenu;
-}(_react2.default.Component);
+var OptionMenu = exports.OptionMenu = function OptionMenu(props) {
+ var _props$settings = props.settings;
+ var mode = _props$settings.mode;
+ var intercept = _props$settings.intercept;
+ var showhost = _props$settings.showhost;
+ var no_upstream_cert = _props$settings.no_upstream_cert;
+ var rawtcp = _props$settings.rawtcp;
+ var http2 = _props$settings.http2;
+ var anticache = _props$settings.anticache;
+ var anticomp = _props$settings.anticomp;
+ var stickycookie = _props$settings.stickycookie;
+ var stickyauth = _props$settings.stickyauth;
+ var stream = _props$settings.stream;
+ return _react2.default.createElement(
+ "div",
+ null,
+ _react2.default.createElement(
+ "div",
+ { className: "menu-row" },
+ _react2.default.createElement(_common.ToggleButton, { name: "showhost",
+ checked: showhost,
+ onToggleChanged: function onToggleChanged() {
+ return _actions.SettingsActions.update({ showhost: !showhost });
+ }
+ }),
+ _react2.default.createElement(_common.ToggleButton, { name: "no_upstream_cert",
+ checked: no_upstream_cert,
+ onToggleChanged: function onToggleChanged() {
+ return _actions.SettingsActions.update({ no_upstream_cert: !no_upstream_cert });
+ }
+ }),
+ _react2.default.createElement(_common.ToggleButton, { name: "rawtcp",
+ checked: rawtcp,
+ onToggleChanged: function onToggleChanged() {
+ return _actions.SettingsActions.update({ rawtcp: !rawtcp });
+ }
+ }),
+ _react2.default.createElement(_common.ToggleButton, { name: "http2",
+ checked: http2,
+ onToggleChanged: function onToggleChanged() {
+ return _actions.SettingsActions.update({ http2: !http2 });
+ }
+ }),
+ _react2.default.createElement(_common.ToggleButton, { name: "anticache",
+ checked: anticache,
+ onToggleChanged: function onToggleChanged() {
+ return _actions.SettingsActions.update({ anticache: !anticache });
+ }
+ }),
+ _react2.default.createElement(_common.ToggleButton, { name: "anticomp",
+ checked: anticomp,
+ onToggleChanged: function onToggleChanged() {
+ return _actions.SettingsActions.update({ anticomp: !anticomp });
+ }
+ }),
+ _react2.default.createElement(_common.ToggleInputButton, { name: "stickyauth", placeholder: "Sticky auth filter",
+ checked: Boolean(stickyauth),
+ txt: stickyauth || "",
+ onToggleChanged: function onToggleChanged(txt) {
+ return _actions.SettingsActions.update({ stickyauth: !stickyauth ? txt : null });
+ }
+ }),
+ _react2.default.createElement(_common.ToggleInputButton, { name: "stickycookie", placeholder: "Sticky cookie filter",
+ checked: Boolean(stickycookie),
+ txt: stickycookie || "",
+ onToggleChanged: function onToggleChanged(txt) {
+ return _actions.SettingsActions.update({ stickycookie: !stickycookie ? txt : null });
+ }
+ }),
+ _react2.default.createElement(_common.ToggleInputButton, { name: "stream", placeholder: "stream...",
+ checked: Boolean(stream),
+ txt: stream || "",
+ inputType: "number",
+ onToggleChanged: function onToggleChanged(txt) {
+ return _actions.SettingsActions.update({ stream: !stream ? txt : null });
+ }
+ })
+ ),
+ _react2.default.createElement("div", { className: "clearfix" })
+ );
+};
OptionMenu.title = "Options";
+OptionMenu.propTypes = {
+ settings: _react2.default.PropTypes.object.isRequired
+};
var ReportsMenu = _react2.default.createClass({
displayName: "ReportsMenu",
@@ -3391,7 +3569,6 @@ var header_entries = [MainMenu, ViewMenu, OptionMenu /*, ReportsMenu */];
var Header = exports.Header = _react2.default.createClass({
displayName: "Header",
- mixins: [_common.Router],
propTypes: {
settings: _react2.default.PropTypes.object.isRequired
},
@@ -3402,7 +3579,7 @@ var Header = exports.Header = _react2.default.createClass({
},
handleClick: function handleClick(active, e) {
e.preventDefault();
- this.updateLocation(active.route);
+ this.props.updateLocation(active.route);
this.setState({ active: active });
},
render: function render() {
@@ -3435,7 +3612,11 @@ var Header = exports.Header = _react2.default.createClass({
_react2.default.createElement(
"div",
{ className: "menu" },
- _react2.default.createElement(this.state.active, { ref: "active", settings: this.props.settings })
+ _react2.default.createElement(this.state.active, {
+ settings: this.props.settings,
+ updateLocation: this.props.updateLocation,
+ query: this.props.query
+ })
)
);
}
@@ -3622,7 +3803,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
var MainView = _react2.default.createClass({
displayName: "MainView",
- mixins: [_common.Router],
contextTypes: {
flowStore: _react2.default.PropTypes.object.isRequired
},
@@ -3653,11 +3833,11 @@ var MainView = _react2.default.createClass({
},
getViewFilt: function getViewFilt() {
try {
- var filtStr = this.getQuery()[_actions.Query.SEARCH];
+ var filtStr = this.props.query[_actions.Query.SEARCH];
var filt = filtStr ? _filt2.default.parse(filtStr) : function () {
return true;
};
- var highlightStr = this.getQuery()[_actions.Query.HIGHLIGHT];
+ var highlightStr = this.props.query[_actions.Query.HIGHLIGHT];
var highlight = highlightStr ? _filt2.default.parse(highlightStr) : function () {
return false;
};
@@ -3710,10 +3890,10 @@ var MainView = _react2.default.createClass({
selectFlow: function selectFlow(flow) {
if (flow) {
var tab = this.props.routeParams.detailTab || "request";
- this.updateLocation("/flows/" + flow.id + "/" + tab);
+ this.props.updateLocation("/flows/" + flow.id + "/" + tab);
this.refs.flowTable.scrollIntoView(flow);
} else {
- this.updateLocation("/flows");
+ this.props.updateLocation("/flows");
}
},
selectFlowRelative: function selectFlowRelative(shift) {
@@ -3837,6 +4017,8 @@ var MainView = _react2.default.createClass({
key: "flowDetails",
ref: "flowDetails",
tab: this.props.routeParams.detailTab,
+ query: this.props.query,
+ updateLocation: this.props.updateLocation,
flow: selected })];
} else {
details = null;
@@ -4054,13 +4236,34 @@ var Reports = _react2.default.createClass({
var ProxyAppMain = _react2.default.createClass({
displayName: "ProxyAppMain",
- mixins: [_common.Router],
childContextTypes: {
flowStore: _react2.default.PropTypes.object.isRequired,
eventStore: _react2.default.PropTypes.object.isRequired,
returnFocus: _react2.default.PropTypes.func.isRequired,
location: _react2.default.PropTypes.object.isRequired
},
+ contextTypes: {
+ router: _react2.default.PropTypes.object.isRequired
+ },
+ updateLocation: function updateLocation(pathname, queryUpdate) {
+ if (pathname === undefined) {
+ pathname = this.props.location.pathname;
+ }
+ var query = this.props.location.query;
+ if (queryUpdate !== undefined) {
+ for (var i in queryUpdate) {
+ if (queryUpdate.hasOwnProperty(i)) {
+ query[i] = queryUpdate[i] || undefined; //falsey values shall be removed.
+ }
+ }
+ }
+ this.context.router.replace({ pathname: pathname, query: query });
+ },
+ getQuery: function getQuery() {
+ // For whatever reason, react-router always returns the same object, which makes comparing
+ // the current props with nextProps impossible. As a workaround, we just clone the query object.
+ return _lodash2.default.clone(this.props.location.query);
+ },
componentDidMount: function componentDidMount() {
this.focus();
this.settingsStore.addListener("recalculate", this.onSettingsChange);
@@ -4130,18 +4333,18 @@ var ProxyAppMain = _react2.default.createClass({
e.preventDefault();
},
render: function render() {
+ var query = this.getQuery();
var eventlog;
if (this.props.location.query[_actions.Query.SHOW_EVENTLOG]) {
- eventlog = [_react2.default.createElement(_common.Splitter, { key: "splitter", axis: "y" }), _react2.default.createElement(_eventlog2.default, { key: "eventlog" })];
+ eventlog = [_react2.default.createElement(_common.Splitter, { key: "splitter", axis: "y" }), _react2.default.createElement(_eventlog2.default, { key: "eventlog", updateLocation: this.updateLocation })];
} else {
eventlog = null;
}
- var children = _react2.default.cloneElement(this.props.children, { ref: "view", location: this.props.location });
return _react2.default.createElement(
"div",
{ id: "container", tabIndex: "0", onKeyDown: this.onKeydown },
- _react2.default.createElement(_header.Header, { ref: "header", settings: this.state.settings }),
- children,
+ _react2.default.createElement(_header.Header, { ref: "header", settings: this.state.settings, updateLocation: this.updateLocation, query: query }),
+ _react2.default.cloneElement(this.props.children, { ref: "view", location: this.props.location, updateLocation: this.updateLocation, query: query }),
eventlog,
_react2.default.createElement(_footer2.default, { settings: this.state.settings })
);
diff --git a/mitmproxy/web/static/vendor.js b/mitmproxy/web/static/vendor.js
index 44bc86d8..efe6f2b4 100644
--- a/mitmproxy/web/static/vendor.js
+++ b/mitmproxy/web/static/vendor.js
@@ -2116,15 +2116,13 @@ var KNOWN_STATICS = {
};
module.exports = function hoistNonReactStatics(targetComponent, sourceComponent) {
- if (typeof sourceComponent !== 'string') { // don't hoist over string (html) components
- var keys = Object.getOwnPropertyNames(sourceComponent);
- for (var i=0; i<keys.length; ++i) {
- if (!REACT_STATICS[keys[i]] && !KNOWN_STATICS[keys[i]]) {
- try {
- targetComponent[keys[i]] = sourceComponent[keys[i]];
- } catch (error) {
-
- }
+ var keys = Object.getOwnPropertyNames(sourceComponent);
+ for (var i=0; i<keys.length; ++i) {
+ if (!REACT_STATICS[keys[i]] && !KNOWN_STATICS[keys[i]]) {
+ try {
+ targetComponent[keys[i]] = sourceComponent[keys[i]];
+ } catch (error) {
+
}
}
}
@@ -3087,9 +3085,6 @@ var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
- if (!draining || !currentQueue) {
- return;
- }
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
@@ -35722,7 +35717,8 @@ return jQuery;
(function (global){
/**
* @license
- * lodash <https://lodash.com/>
+ * lodash 4.11.2 (Custom Build) <https://lodash.com/>
+ * Build: `lodash -d -o ./foo/lodash.js`
* Copyright jQuery Foundation and other contributors <https://jquery.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
@@ -35734,7 +35730,7 @@ return jQuery;
var undefined;
/** Used as the semantic version number. */
- var VERSION = '4.12.0';
+ var VERSION = '4.11.2';
/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;
@@ -36179,6 +36175,30 @@ return jQuery;
}
/**
+ * Creates a new array concatenating `array` with `other`.
+ *
+ * @private
+ * @param {Array} array The first array to concatenate.
+ * @param {Array} other The second array to concatenate.
+ * @returns {Array} Returns the new concatenated array.
+ */
+ function arrayConcat(array, other) {
+ var index = -1,
+ length = array.length,
+ othIndex = -1,
+ othLength = other.length,
+ result = Array(length + othLength);
+
+ while (++index < length) {
+ result[index] = array[index];
+ }
+ while (++othIndex < othLength) {
+ result[index++] = other[othIndex];
+ }
+ return result;
+ }
+
+ /**
* A specialized version of `_.forEach` for arrays without support for
* iteratee shorthands.
*
@@ -36605,7 +36625,7 @@ return jQuery;
* @private
* @param {Object} object The object to query.
* @param {Array} props The property names to get values for.
- * @returns {Object} Returns the key-value pairs.
+ * @returns {Object} Returns the new array of key-value pairs.
*/
function baseToPairs(object, props) {
return arrayMap(props, function(key) {
@@ -36618,7 +36638,7 @@ return jQuery;
*
* @private
* @param {Function} func The function to cap arguments for.
- * @returns {Function} Returns the new capped function.
+ * @returns {Function} Returns the new function.
*/
function baseUnary(func) {
return function(value) {
@@ -36643,18 +36663,6 @@ return jQuery;
}
/**
- * Checks if a cache value for `key` exists.
- *
- * @private
- * @param {Object} cache The cache to query.
- * @param {string} key The key of the entry to check.
- * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
- */
- function cacheHas(cache, key) {
- return cache.has(key);
- }
-
- /**
* Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
* that is not found in the character symbols.
*
@@ -36810,11 +36818,11 @@ return jQuery;
}
/**
- * Converts `map` to its key-value pairs.
+ * Converts `map` to an array.
*
* @private
* @param {Object} map The map to convert.
- * @returns {Array} Returns the key-value pairs.
+ * @returns {Array} Returns the converted array.
*/
function mapToArray(map) {
var index = -1,
@@ -36852,11 +36860,11 @@ return jQuery;
}
/**
- * Converts `set` to an array of its values.
+ * Converts `set` to an array.
*
* @private
* @param {Object} set The set to convert.
- * @returns {Array} Returns the values.
+ * @returns {Array} Returns the converted array.
*/
function setToArray(set) {
var index = -1,
@@ -36869,23 +36877,6 @@ return jQuery;
}
/**
- * Converts `set` to its value-value pairs.
- *
- * @private
- * @param {Object} set The set to convert.
- * @returns {Array} Returns the value-value pairs.
- */
- function setToPairs(set) {
- var index = -1,
- result = Array(set.size);
-
- set.forEach(function(value) {
- result[++index] = [value, value];
- });
- return result;
- }
-
- /**
* Gets the number of symbols in `string`.
*
* @private
@@ -37138,10 +37129,10 @@ return jQuery;
* `floor`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`,
* `forOwnRight`, `get`, `gt`, `gte`, `has`, `hasIn`, `head`, `identity`,
* `includes`, `indexOf`, `inRange`, `invoke`, `isArguments`, `isArray`,
- * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`,
- * `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`,
- * `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`,
- * `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`,
+ * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`, `isBuffer`,
+ * `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`, `isError`,
+ * `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`, `isMatch`,
+ * `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`,
* `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, `isSafeInteger`,
* `isSet`, `isString`, `isUndefined`, `isTypedArray`, `isWeakMap`, `isWeakSet`,
* `join`, `kebabCase`, `last`, `lastIndexOf`, `lowerCase`, `lowerFirst`,
@@ -37150,9 +37141,9 @@ return jQuery;
* `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, `round`,
* `runInContext`, `sample`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,
* `sortedIndexBy`, `sortedLastIndex`, `sortedLastIndexBy`, `startCase`,
- * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toFinite`,
- * `toInteger`, `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`,
- * `toString`, `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`,
+ * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toInteger`,
+ * `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`, `toString`,
+ * `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`,
* `uniqueId`, `upperCase`, `upperFirst`, `value`, and `words`
*
* @name _
@@ -37412,212 +37403,64 @@ return jQuery;
*
* @private
* @constructor
- * @param {Array} [entries] The key-value pairs to cache.
+ * @returns {Object} Returns the new hash object.
*/
- function Hash(entries) {
- var index = -1,
- length = entries ? entries.length : 0;
-
- this.clear();
- while (++index < length) {
- var entry = entries[index];
- this.set(entry[0], entry[1]);
- }
- }
-
- /**
- * Removes all key-value entries from the hash.
- *
- * @private
- * @name clear
- * @memberOf Hash
- */
- function hashClear() {
- this.__data__ = nativeCreate ? nativeCreate(null) : {};
- }
+ function Hash() {}
/**
* Removes `key` and its value from the hash.
*
* @private
- * @name delete
- * @memberOf Hash
* @param {Object} hash The hash to modify.
* @param {string} key The key of the value to remove.
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
*/
- function hashDelete(key) {
- return this.has(key) && delete this.__data__[key];
+ function hashDelete(hash, key) {
+ return hashHas(hash, key) && delete hash[key];
}
/**
* Gets the hash value for `key`.
*
* @private
- * @name get
- * @memberOf Hash
+ * @param {Object} hash The hash to query.
* @param {string} key The key of the value to get.
* @returns {*} Returns the entry value.
*/
- function hashGet(key) {
- var data = this.__data__;
+ function hashGet(hash, key) {
if (nativeCreate) {
- var result = data[key];
+ var result = hash[key];
return result === HASH_UNDEFINED ? undefined : result;
}
- return hasOwnProperty.call(data, key) ? data[key] : undefined;
+ return hasOwnProperty.call(hash, key) ? hash[key] : undefined;
}
/**
* Checks if a hash value for `key` exists.
*
* @private
- * @name has
- * @memberOf Hash
+ * @param {Object} hash The hash to query.
* @param {string} key The key of the entry to check.
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
*/
- function hashHas(key) {
- var data = this.__data__;
- return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
+ function hashHas(hash, key) {
+ return nativeCreate ? hash[key] !== undefined : hasOwnProperty.call(hash, key);
}
/**
* Sets the hash `key` to `value`.
*
* @private
- * @name set
- * @memberOf Hash
- * @param {string} key The key of the value to set.
- * @param {*} value The value to set.
- * @returns {Object} Returns the hash instance.
- */
- function hashSet(key, value) {
- var data = this.__data__;
- data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
- return this;
- }
-
- // Add methods to `Hash`.
- Hash.prototype.clear = hashClear;
- Hash.prototype['delete'] = hashDelete;
- Hash.prototype.get = hashGet;
- Hash.prototype.has = hashHas;
- Hash.prototype.set = hashSet;
-
- /*------------------------------------------------------------------------*/
-
- /**
- * Creates an list cache object.
- *
- * @private
- * @constructor
- * @param {Array} [entries] The key-value pairs to cache.
- */
- function ListCache(entries) {
- var index = -1,
- length = entries ? entries.length : 0;
-
- this.clear();
- while (++index < length) {
- var entry = entries[index];
- this.set(entry[0], entry[1]);
- }
- }
-
- /**
- * Removes all key-value entries from the list cache.
- *
- * @private
- * @name clear
- * @memberOf ListCache
- */
- function listCacheClear() {
- this.__data__ = [];
- }
-
- /**
- * Removes `key` and its value from the list cache.
- *
- * @private
- * @name delete
- * @memberOf ListCache
- * @param {string} key The key of the value to remove.
- * @returns {boolean} Returns `true` if the entry was removed, else `false`.
- */
- function listCacheDelete(key) {
- var data = this.__data__,
- index = assocIndexOf(data, key);
-
- if (index < 0) {
- return false;
- }
- var lastIndex = data.length - 1;
- if (index == lastIndex) {
- data.pop();
- } else {
- splice.call(data, index, 1);
- }
- return true;
- }
-
- /**
- * Gets the list cache value for `key`.
- *
- * @private
- * @name get
- * @memberOf ListCache
- * @param {string} key The key of the value to get.
- * @returns {*} Returns the entry value.
- */
- function listCacheGet(key) {
- var data = this.__data__,
- index = assocIndexOf(data, key);
-
- return index < 0 ? undefined : data[index][1];
- }
-
- /**
- * Checks if a list cache value for `key` exists.
- *
- * @private
- * @name has
- * @memberOf ListCache
- * @param {string} key The key of the entry to check.
- * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
- */
- function listCacheHas(key) {
- return assocIndexOf(this.__data__, key) > -1;
- }
-
- /**
- * Sets the list cache `key` to `value`.
- *
- * @private
- * @name set
- * @memberOf ListCache
+ * @param {Object} hash The hash to modify.
* @param {string} key The key of the value to set.
* @param {*} value The value to set.
- * @returns {Object} Returns the list cache instance.
*/
- function listCacheSet(key, value) {
- var data = this.__data__,
- index = assocIndexOf(data, key);
-
- if (index < 0) {
- data.push([key, value]);
- } else {
- data[index][1] = value;
- }
- return this;
+ function hashSet(hash, key, value) {
+ hash[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
}
- // Add methods to `ListCache`.
- ListCache.prototype.clear = listCacheClear;
- ListCache.prototype['delete'] = listCacheDelete;
- ListCache.prototype.get = listCacheGet;
- ListCache.prototype.has = listCacheHas;
- ListCache.prototype.set = listCacheSet;
+ // Avoid inheriting from `Object.prototype` when possible.
+ Hash.prototype = nativeCreate ? nativeCreate(null) : objectProto;
/*------------------------------------------------------------------------*/
@@ -37626,15 +37469,15 @@ return jQuery;
*
* @private
* @constructor
- * @param {Array} [entries] The key-value pairs to cache.
+ * @param {Array} [values] The values to cache.
*/
- function MapCache(entries) {
+ function MapCache(values) {
var index = -1,
- length = entries ? entries.length : 0;
+ length = values ? values.length : 0;
this.clear();
while (++index < length) {
- var entry = entries[index];
+ var entry = values[index];
this.set(entry[0], entry[1]);
}
}
@@ -37646,10 +37489,10 @@ return jQuery;
* @name clear
* @memberOf MapCache
*/
- function mapCacheClear() {
+ function mapClear() {
this.__data__ = {
'hash': new Hash,
- 'map': new (Map || ListCache),
+ 'map': Map ? new Map : [],
'string': new Hash
};
}
@@ -37663,8 +37506,12 @@ return jQuery;
* @param {string} key The key of the value to remove.
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
*/
- function mapCacheDelete(key) {
- return getMapData(this, key)['delete'](key);
+ function mapDelete(key) {
+ var data = this.__data__;
+ if (isKeyable(key)) {
+ return hashDelete(typeof key == 'string' ? data.string : data.hash, key);
+ }
+ return Map ? data.map['delete'](key) : assocDelete(data.map, key);
}
/**
@@ -37676,8 +37523,12 @@ return jQuery;
* @param {string} key The key of the value to get.
* @returns {*} Returns the entry value.
*/
- function mapCacheGet(key) {
- return getMapData(this, key).get(key);
+ function mapGet(key) {
+ var data = this.__data__;
+ if (isKeyable(key)) {
+ return hashGet(typeof key == 'string' ? data.string : data.hash, key);
+ }
+ return Map ? data.map.get(key) : assocGet(data.map, key);
}
/**
@@ -37689,8 +37540,12 @@ return jQuery;
* @param {string} key The key of the entry to check.
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
*/
- function mapCacheHas(key) {
- return getMapData(this, key).has(key);
+ function mapHas(key) {
+ var data = this.__data__;
+ if (isKeyable(key)) {
+ return hashHas(typeof key == 'string' ? data.string : data.hash, key);
+ }
+ return Map ? data.map.has(key) : assocHas(data.map, key);
}
/**
@@ -37703,23 +37558,30 @@ return jQuery;
* @param {*} value The value to set.
* @returns {Object} Returns the map cache instance.
*/
- function mapCacheSet(key, value) {
- getMapData(this, key).set(key, value);
+ function mapSet(key, value) {
+ var data = this.__data__;
+ if (isKeyable(key)) {
+ hashSet(typeof key == 'string' ? data.string : data.hash, key, value);
+ } else if (Map) {
+ data.map.set(key, value);
+ } else {
+ assocSet(data.map, key, value);
+ }
return this;
}
// Add methods to `MapCache`.
- MapCache.prototype.clear = mapCacheClear;
- MapCache.prototype['delete'] = mapCacheDelete;
- MapCache.prototype.get = mapCacheGet;
- MapCache.prototype.has = mapCacheHas;
- MapCache.prototype.set = mapCacheSet;
+ MapCache.prototype.clear = mapClear;
+ MapCache.prototype['delete'] = mapDelete;
+ MapCache.prototype.get = mapGet;
+ MapCache.prototype.has = mapHas;
+ MapCache.prototype.set = mapSet;
/*------------------------------------------------------------------------*/
/**
*
- * Creates an array cache object to store unique values.
+ * Creates a set cache object to store unique values.
*
* @private
* @constructor
@@ -37731,41 +37593,52 @@ return jQuery;
this.__data__ = new MapCache;
while (++index < length) {
- this.add(values[index]);
+ this.push(values[index]);
}
}
/**
- * Adds `value` to the array cache.
+ * Checks if `value` is in `cache`.
*
* @private
- * @name add
- * @memberOf SetCache
- * @alias push
- * @param {*} value The value to cache.
- * @returns {Object} Returns the cache instance.
+ * @param {Object} cache The set cache to search.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `true` if `value` is found, else `false`.
*/
- function setCacheAdd(value) {
- this.__data__.set(value, HASH_UNDEFINED);
- return this;
+ function cacheHas(cache, value) {
+ var map = cache.__data__;
+ if (isKeyable(value)) {
+ var data = map.__data__,
+ hash = typeof value == 'string' ? data.string : data.hash;
+
+ return hash[value] === HASH_UNDEFINED;
+ }
+ return map.has(value);
}
/**
- * Checks if `value` is in the array cache.
+ * Adds `value` to the set cache.
*
* @private
- * @name has
+ * @name push
* @memberOf SetCache
- * @param {*} value The value to search for.
- * @returns {number} Returns `true` if `value` is found, else `false`.
+ * @param {*} value The value to cache.
*/
- function setCacheHas(value) {
- return this.__data__.has(value);
+ function cachePush(value) {
+ var map = this.__data__;
+ if (isKeyable(value)) {
+ var data = map.__data__,
+ hash = typeof value == 'string' ? data.string : data.hash;
+
+ hash[value] = HASH_UNDEFINED;
+ }
+ else {
+ map.set(value, HASH_UNDEFINED);
+ }
}
// Add methods to `SetCache`.
- SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
- SetCache.prototype.has = setCacheHas;
+ SetCache.prototype.push = cachePush;
/*------------------------------------------------------------------------*/
@@ -37774,10 +37647,17 @@ return jQuery;
*
* @private
* @constructor
- * @param {Array} [entries] The key-value pairs to cache.
+ * @param {Array} [values] The values to cache.
*/
- function Stack(entries) {
- this.__data__ = new ListCache(entries);
+ function Stack(values) {
+ var index = -1,
+ length = values ? values.length : 0;
+
+ this.clear();
+ while (++index < length) {
+ var entry = values[index];
+ this.set(entry[0], entry[1]);
+ }
}
/**
@@ -37788,7 +37668,7 @@ return jQuery;
* @memberOf Stack
*/
function stackClear() {
- this.__data__ = new ListCache;
+ this.__data__ = { 'array': [], 'map': null };
}
/**
@@ -37801,7 +37681,10 @@ return jQuery;
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
*/
function stackDelete(key) {
- return this.__data__['delete'](key);
+ var data = this.__data__,
+ array = data.array;
+
+ return array ? assocDelete(array, key) : data.map['delete'](key);
}
/**
@@ -37814,7 +37697,10 @@ return jQuery;
* @returns {*} Returns the entry value.
*/
function stackGet(key) {
- return this.__data__.get(key);
+ var data = this.__data__,
+ array = data.array;
+
+ return array ? assocGet(array, key) : data.map.get(key);
}
/**
@@ -37827,7 +37713,10 @@ return jQuery;
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
*/
function stackHas(key) {
- return this.__data__.has(key);
+ var data = this.__data__,
+ array = data.array;
+
+ return array ? assocHas(array, key) : data.map.has(key);
}
/**
@@ -37841,11 +37730,21 @@ return jQuery;
* @returns {Object} Returns the stack cache instance.
*/
function stackSet(key, value) {
- var cache = this.__data__;
- if (cache instanceof ListCache && cache.__data__.length == LARGE_ARRAY_SIZE) {
- cache = this.__data__ = new MapCache(cache.__data__);
+ var data = this.__data__,
+ array = data.array;
+
+ if (array) {
+ if (array.length < (LARGE_ARRAY_SIZE - 1)) {
+ assocSet(array, key, value);
+ } else {
+ data.array = null;
+ data.map = new MapCache(array);
+ }
+ }
+ var map = data.map;
+ if (map) {
+ map.set(key, value);
}
- cache.set(key, value);
return this;
}
@@ -37859,6 +37758,90 @@ return jQuery;
/*------------------------------------------------------------------------*/
/**
+ * Removes `key` and its value from the associative array.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function assocDelete(array, key) {
+ var index = assocIndexOf(array, key);
+ if (index < 0) {
+ return false;
+ }
+ var lastIndex = array.length - 1;
+ if (index == lastIndex) {
+ array.pop();
+ } else {
+ splice.call(array, index, 1);
+ }
+ return true;
+ }
+
+ /**
+ * Gets the associative array value for `key`.
+ *
+ * @private
+ * @param {Array} array The array to query.
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function assocGet(array, key) {
+ var index = assocIndexOf(array, key);
+ return index < 0 ? undefined : array[index][1];
+ }
+
+ /**
+ * Checks if an associative array value for `key` exists.
+ *
+ * @private
+ * @param {Array} array The array to query.
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function assocHas(array, key) {
+ return assocIndexOf(array, key) > -1;
+ }
+
+ /**
+ * Gets the index at which the `key` is found in `array` of key-value pairs.
+ *
+ * @private
+ * @param {Array} array The array to search.
+ * @param {*} key The key to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function assocIndexOf(array, key) {
+ var length = array.length;
+ while (length--) {
+ if (eq(array[length][0], key)) {
+ return length;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Sets the associative array `key` to `value`.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ */
+ function assocSet(array, key, value) {
+ var index = assocIndexOf(array, key);
+ if (index < 0) {
+ array.push([key, value]);
+ } else {
+ array[index][1] = value;
+ }
+ }
+
+ /*------------------------------------------------------------------------*/
+
+ /**
* Used by `_.defaults` to customize its `_.assignIn` use.
*
* @private
@@ -37911,24 +37894,6 @@ return jQuery;
}
/**
- * Gets the index at which the `key` is found in `array` of key-value pairs.
- *
- * @private
- * @param {Array} array The array to search.
- * @param {*} key The key to search for.
- * @returns {number} Returns the index of the matched value, else `-1`.
- */
- function assocIndexOf(array, key) {
- var length = array.length;
- while (length--) {
- if (eq(array[length][0], key)) {
- return length;
- }
- }
- return -1;
- }
-
- /**
* Aggregates elements of `collection` on `accumulator` with keys transformed
* by `iteratee` and values set by `setter`.
*
@@ -37965,7 +37930,7 @@ return jQuery;
* @private
* @param {Object} object The object to iterate over.
* @param {string[]} paths The property paths of elements to pick.
- * @returns {Array} Returns the picked elements.
+ * @returns {Array} Returns the new array of picked elements.
*/
function baseAt(object, paths) {
var index = -1,
@@ -38080,7 +38045,7 @@ return jQuery;
*
* @private
* @param {Object} source The object of property predicates to conform to.
- * @returns {Function} Returns the new spec function.
+ * @returns {Function} Returns the new function.
*/
function baseConforms(source) {
var props = keys(source),
@@ -38393,7 +38358,7 @@ return jQuery;
* @private
* @param {Object} object The object to inspect.
* @param {Array} props The property names to filter.
- * @returns {Array} Returns the function names.
+ * @returns {Array} Returns the new array of filtered property names.
*/
function baseFunctions(object, props) {
return arrayFilter(props, function(key) {
@@ -38434,7 +38399,9 @@ return jQuery;
*/
function baseGetAllKeys(object, keysFunc, symbolsFunc) {
var result = keysFunc(object);
- return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
+ return isArray(object)
+ ? result
+ : arrayPush(result, symbolsFunc(object));
}
/**
@@ -38826,7 +38793,7 @@ return jQuery;
*
* @private
* @param {Object} source The object of property values to match.
- * @returns {Function} Returns the new spec function.
+ * @returns {Function} Returns the new function.
*/
function baseMatches(source) {
var matchData = getMatchData(source);
@@ -38844,7 +38811,7 @@ return jQuery;
* @private
* @param {string} path The path of the property to get.
* @param {*} srcValue The value to match.
- * @returns {Function} Returns the new spec function.
+ * @returns {Function} Returns the new function.
*/
function baseMatchesProperty(path, srcValue) {
if (isKey(path) && isStrictComparable(srcValue)) {
@@ -39059,7 +39026,7 @@ return jQuery;
*
* @private
* @param {string} key The key of the property to get.
- * @returns {Function} Returns the new accessor function.
+ * @returns {Function} Returns the new function.
*/
function baseProperty(key) {
return function(object) {
@@ -39072,7 +39039,7 @@ return jQuery;
*
* @private
* @param {Array|string} path The path of the property to get.
- * @returns {Function} Returns the new accessor function.
+ * @returns {Function} Returns the new function.
*/
function basePropertyDeep(path) {
return function(object) {
@@ -39173,7 +39140,7 @@ return jQuery;
* @param {number} end The end of the range.
* @param {number} step The value to increment or decrement by.
* @param {boolean} [fromRight] Specify iterating from right to left.
- * @returns {Array} Returns the range of numbers.
+ * @returns {Array} Returns the new array of numbers.
*/
function baseRange(start, end, step, fromRight) {
var index = -1,
@@ -39887,7 +39854,7 @@ return jQuery;
* placeholders, and provided arguments into a single array of arguments.
*
* @private
- * @param {Array} args The provided arguments.
+ * @param {Array|Object} args The provided arguments.
* @param {Array} partials The arguments to prepend to those provided.
* @param {Array} holders The `partials` placeholder indexes.
* @params {boolean} [isCurried] Specify composing for a curried function.
@@ -39922,7 +39889,7 @@ return jQuery;
* is tailored for `_.partialRight`.
*
* @private
- * @param {Array} args The provided arguments.
+ * @param {Array|Object} args The provided arguments.
* @param {Array} partials The arguments to append to those provided.
* @param {Array} holders The `partials` placeholder indexes.
* @params {boolean} [isCurried] Specify composing for a curried function.
@@ -40044,7 +40011,7 @@ return jQuery;
customizer = length > 1 ? sources[length - 1] : undefined,
guard = length > 2 ? sources[2] : undefined;
- customizer = (assigner.length > 3 && typeof customizer == 'function')
+ customizer = typeof customizer == 'function'
? (length--, customizer)
: undefined;
@@ -40143,7 +40110,7 @@ return jQuery;
*
* @private
* @param {string} methodName The name of the `String` case method to use.
- * @returns {Function} Returns the new case function.
+ * @returns {Function} Returns the new function.
*/
function createCaseFirst(methodName) {
return function(string) {
@@ -40228,7 +40195,7 @@ return jQuery;
var length = arguments.length,
args = Array(length),
index = length,
- placeholder = getHolder(wrapper);
+ placeholder = getPlaceholder(wrapper);
while (index--) {
args[index] = arguments[index];
@@ -40343,14 +40310,14 @@ return jQuery;
function wrapper() {
var length = arguments.length,
- args = Array(length),
- index = length;
+ index = length,
+ args = Array(length);
while (index--) {
args[index] = arguments[index];
}
if (isCurried) {
- var placeholder = getHolder(wrapper),
+ var placeholder = getPlaceholder(wrapper),
holdersCount = countHolders(args, placeholder);
}
if (partials) {
@@ -40439,7 +40406,7 @@ return jQuery;
*
* @private
* @param {Function} arrayFunc The function to iterate over iteratees.
- * @returns {Function} Returns the new over function.
+ * @returns {Function} Returns the new invoker function.
*/
function createOver(arrayFunc) {
return rest(function(iteratees) {
@@ -40638,26 +40605,6 @@ return jQuery;
};
/**
- * Creates a `_.toPairs` or `_.toPairsIn` function.
- *
- * @private
- * @param {Function} keysFunc The function to get the keys of a given object.
- * @returns {Function} Returns the new pairs function.
- */
- function createToPairs(keysFunc) {
- return function(object) {
- var tag = getTag(object);
- if (tag == mapTag) {
- return mapToArray(object);
- }
- if (tag == setTag) {
- return setToPairs(object);
- }
- return baseToPairs(object, keysFunc(object));
- };
- }
-
- /**
* Creates a function that either curries or invokes `func` with optional
* `this` binding and partially applied arguments.
*
@@ -40674,7 +40621,6 @@ return jQuery;
* 64 - `_.partialRight`
* 128 - `_.rearg`
* 256 - `_.ary`
- * 512 - `_.flip`
* @param {*} [thisArg] The `this` binding of `func`.
* @param {Array} [partials] The arguments to be partially applied.
* @param {Array} [holders] The `partials` placeholder indexes.
@@ -40753,7 +40699,9 @@ return jQuery;
* @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
*/
function equalArrays(array, other, equalFunc, customizer, bitmask, stack) {
- var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
+ var index = -1,
+ isPartial = bitmask & PARTIAL_COMPARE_FLAG,
+ isUnordered = bitmask & UNORDERED_COMPARE_FLAG,
arrLength = array.length,
othLength = other.length;
@@ -40765,10 +40713,7 @@ return jQuery;
if (stacked) {
return stacked == other;
}
- var index = -1,
- result = true,
- seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined;
-
+ var result = true;
stack.set(array, other);
// Ignore non-index properties.
@@ -40789,12 +40734,10 @@ return jQuery;
break;
}
// Recursively compare arrays (susceptible to call stack limits).
- if (seen) {
- if (!arraySome(other, function(othValue, othIndex) {
- if (!seen.has(othIndex) &&
- (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) {
- return seen.add(othIndex);
- }
+ if (isUnordered) {
+ if (!arraySome(other, function(othValue) {
+ return arrValue === othValue ||
+ equalFunc(arrValue, othValue, customizer, bitmask, stack);
})) {
result = false;
break;
@@ -41029,18 +40972,6 @@ return jQuery;
}
/**
- * Gets the argument placeholder value for `func`.
- *
- * @private
- * @param {Function} func The function to inspect.
- * @returns {*} Returns the placeholder value.
- */
- function getHolder(func) {
- var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
- return object.placeholder;
- }
-
- /**
* Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
* this function returns the custom method, otherwise it returns `baseIteratee`.
* If arguments are provided, the chosen function is invoked with them and
@@ -41071,21 +41002,6 @@ return jQuery;
var getLength = baseProperty('length');
/**
- * Gets the data for `map`.
- *
- * @private
- * @param {Object} map The map to query.
- * @param {string} key The reference key.
- * @returns {*} Returns the map data.
- */
- function getMapData(map, key) {
- var data = map.__data__;
- return isKeyable(key)
- ? data[typeof key == 'string' ? 'string' : 'hash']
- : data.map;
- }
-
- /**
* Gets the property names, values, and compare flags of `object`.
*
* @private
@@ -41116,6 +41032,18 @@ return jQuery;
}
/**
+ * Gets the argument placeholder value for `func`.
+ *
+ * @private
+ * @param {Function} func The function to inspect.
+ * @returns {*} Returns the placeholder value.
+ */
+ function getPlaceholder(func) {
+ var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
+ return object.placeholder;
+ }
+
+ /**
* Gets the `[[Prototype]]` of `value`.
*
* @private
@@ -41364,7 +41292,7 @@ return jQuery;
* @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
*/
function isFlattenable(value) {
- return isArray(value) || isArguments(value);
+ return isArrayLikeObject(value) && (isArray(value) || isArguments(value));
}
/**
@@ -41508,7 +41436,7 @@ return jQuery;
* @private
* @param {string} key The key of the property to get.
* @param {*} srcValue The value to match.
- * @returns {Function} Returns the new spec function.
+ * @returns {Function} Returns the new function.
*/
function matchesStrictComparable(key, srcValue) {
return function(object) {
@@ -41760,7 +41688,7 @@ return jQuery;
* @param {Array} array The array to process.
* @param {number} [size=1] The length of each chunk
* @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Array} Returns the new array of chunks.
+ * @returns {Array} Returns the new array containing chunks.
* @example
*
* _.chunk(['a', 'b', 'c', 'd'], 2);
@@ -41843,16 +41771,16 @@ return jQuery;
*/
function concat() {
var length = arguments.length,
- args = Array(length ? length - 1 : 0),
- array = arguments[0],
- index = length;
+ array = castArray(arguments[0]);
- while (index--) {
- args[index - 1] = arguments[index];
+ if (length < 2) {
+ return length ? copyArray(array) : [];
}
- return length
- ? arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1))
- : [];
+ var args = Array(length - 1);
+ while (length--) {
+ args[length - 1] = arguments[length];
+ }
+ return arrayConcat(array, baseFlatten(args, 1));
}
/**
@@ -42571,8 +42499,8 @@ return jQuery;
}
/**
- * Gets the element at `n` index of `array`. If `n` is negative, the nth
- * element from the end is returned.
+ * Gets the nth element of `array`. If `n` is negative, the nth element
+ * from the end is returned.
*
* @static
* @memberOf _
@@ -43452,7 +43380,7 @@ return jQuery;
* @memberOf _
* @since 0.1.0
* @category Array
- * @param {Array} array The array to inspect.
+ * @param {Array} array The array to filter.
* @param {...*} [values] The values to exclude.
* @returns {Array} Returns the new array of filtered values.
* @see _.difference, _.xor
@@ -43478,7 +43406,7 @@ return jQuery;
* @since 2.4.0
* @category Array
* @param {...Array} [arrays] The arrays to inspect.
- * @returns {Array} Returns the new array of filtered values.
+ * @returns {Array} Returns the new array of values.
* @see _.difference, _.without
* @example
*
@@ -43502,7 +43430,7 @@ return jQuery;
* @param {...Array} [arrays] The arrays to inspect.
* @param {Array|Function|Object|string} [iteratee=_.identity]
* The iteratee invoked per element.
- * @returns {Array} Returns the new array of filtered values.
+ * @returns {Array} Returns the new array of values.
* @example
*
* _.xorBy([2.1, 1.2], [4.3, 2.4], Math.floor);
@@ -43531,7 +43459,7 @@ return jQuery;
* @category Array
* @param {...Array} [arrays] The arrays to inspect.
* @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new array of filtered values.
+ * @returns {Array} Returns the new array of values.
* @example
*
* var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
@@ -44279,8 +44207,9 @@ return jQuery;
* // => Logs 'a' then 'b' (iteration order is not guaranteed).
*/
function forEach(collection, iteratee) {
- var func = isArray(collection) ? arrayEach : baseEach;
- return func(collection, getIteratee(iteratee, 3));
+ return (typeof iteratee == 'function' && isArray(collection))
+ ? arrayEach(collection, iteratee)
+ : baseEach(collection, getIteratee(iteratee));
}
/**
@@ -44304,8 +44233,9 @@ return jQuery;
* // => Logs `2` then `1`.
*/
function forEachRight(collection, iteratee) {
- var func = isArray(collection) ? arrayEachRight : baseEachRight;
- return func(collection, getIteratee(iteratee, 3));
+ return (typeof iteratee == 'function' && isArray(collection))
+ ? arrayEachRight(collection, iteratee)
+ : baseEachRight(collection, getIteratee(iteratee));
}
/**
@@ -44986,7 +44916,7 @@ return jQuery;
* @param {Function} func The function to cap arguments for.
* @param {number} [n=func.length] The arity cap.
* @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Function} Returns the new capped function.
+ * @returns {Function} Returns the new function.
* @example
*
* _.map(['6', '8', '10'], _.ary(parseInt, 1));
@@ -45070,7 +45000,7 @@ return jQuery;
var bind = rest(function(func, thisArg, partials) {
var bitmask = BIND_FLAG;
if (partials.length) {
- var holders = replaceHolders(partials, getHolder(bind));
+ var holders = replaceHolders(partials, getPlaceholder(bind));
bitmask |= PARTIAL_FLAG;
}
return createWrapper(func, bitmask, thisArg, partials, holders);
@@ -45124,7 +45054,7 @@ return jQuery;
var bindKey = rest(function(object, key, partials) {
var bitmask = BIND_FLAG | BIND_KEY_FLAG;
if (partials.length) {
- var holders = replaceHolders(partials, getHolder(bindKey));
+ var holders = replaceHolders(partials, getPlaceholder(bindKey));
bitmask |= PARTIAL_FLAG;
}
return createWrapper(key, bitmask, object, partials, holders);
@@ -45450,7 +45380,7 @@ return jQuery;
* @since 4.0.0
* @category Function
* @param {Function} func The function to flip arguments for.
- * @returns {Function} Returns the new flipped function.
+ * @returns {Function} Returns the new function.
* @example
*
* var flipped = _.flip(function() {
@@ -45483,7 +45413,7 @@ return jQuery;
* @category Function
* @param {Function} func The function to have its output memoized.
* @param {Function} [resolver] The function to resolve the cache key.
- * @returns {Function} Returns the new memoized function.
+ * @returns {Function} Returns the new memoizing function.
* @example
*
* var object = { 'a': 1, 'b': 2 };
@@ -45541,7 +45471,7 @@ return jQuery;
* @since 3.0.0
* @category Function
* @param {Function} predicate The predicate to negate.
- * @returns {Function} Returns the new negated function.
+ * @returns {Function} Returns the new function.
* @example
*
* function isEven(n) {
@@ -45665,7 +45595,7 @@ return jQuery;
* // => 'hi fred'
*/
var partial = rest(function(func, partials) {
- var holders = replaceHolders(partials, getHolder(partial));
+ var holders = replaceHolders(partials, getPlaceholder(partial));
return createWrapper(func, PARTIAL_FLAG, undefined, partials, holders);
});
@@ -45702,7 +45632,7 @@ return jQuery;
* // => 'hello fred'
*/
var partialRight = rest(function(func, partials) {
- var holders = replaceHolders(partials, getHolder(partialRight));
+ var holders = replaceHolders(partials, getPlaceholder(partialRight));
return createWrapper(func, PARTIAL_RIGHT_FLAG, undefined, partials, holders);
});
@@ -45904,7 +45834,7 @@ return jQuery;
* @since 4.0.0
* @category Function
* @param {Function} func The function to cap arguments for.
- * @returns {Function} Returns the new capped function.
+ * @returns {Function} Returns the new function.
* @example
*
* _.map(['6', '8', '10'], _.unary(parseInt));
@@ -46580,13 +46510,13 @@ return jQuery;
* _.isFinite(3);
* // => true
*
- * _.isFinite(Number.MIN_VALUE);
+ * _.isFinite(Number.MAX_VALUE);
* // => true
*
- * _.isFinite(Infinity);
- * // => false
+ * _.isFinite(3.14);
+ * // => true
*
- * _.isFinite('3');
+ * _.isFinite(Infinity);
* // => false
*/
function isFinite(value) {
@@ -47309,41 +47239,6 @@ return jQuery;
}
/**
- * Converts `value` to a finite number.
- *
- * @static
- * @memberOf _
- * @since 4.12.0
- * @category Lang
- * @param {*} value The value to convert.
- * @returns {number} Returns the converted number.
- * @example
- *
- * _.toFinite(3.2);
- * // => 3.2
- *
- * _.toFinite(Number.MIN_VALUE);
- * // => 5e-324
- *
- * _.toFinite(Infinity);
- * // => 1.7976931348623157e+308
- *
- * _.toFinite('3.2');
- * // => 3.2
- */
- function toFinite(value) {
- if (!value) {
- return value === 0 ? value : 0;
- }
- value = toNumber(value);
- if (value === INFINITY || value === -INFINITY) {
- var sign = (value < 0 ? -1 : 1);
- return sign * MAX_INTEGER;
- }
- return value === value ? value : 0;
- }
-
- /**
* Converts `value` to an integer.
*
* **Note:** This function is loosely based on
@@ -47357,7 +47252,7 @@ return jQuery;
* @returns {number} Returns the converted integer.
* @example
*
- * _.toInteger(3.2);
+ * _.toInteger(3);
* // => 3
*
* _.toInteger(Number.MIN_VALUE);
@@ -47366,14 +47261,20 @@ return jQuery;
* _.toInteger(Infinity);
* // => 1.7976931348623157e+308
*
- * _.toInteger('3.2');
+ * _.toInteger('3');
* // => 3
*/
function toInteger(value) {
- var result = toFinite(value),
- remainder = result % 1;
-
- return result === result ? (remainder ? result - remainder : result) : 0;
+ if (!value) {
+ return value === 0 ? value : 0;
+ }
+ value = toNumber(value);
+ if (value === INFINITY || value === -INFINITY) {
+ var sign = (value < 0 ? -1 : 1);
+ return sign * MAX_INTEGER;
+ }
+ var remainder = value % 1;
+ return value === value ? (remainder ? value - remainder : value) : 0;
}
/**
@@ -47391,7 +47292,7 @@ return jQuery;
* @returns {number} Returns the converted integer.
* @example
*
- * _.toLength(3.2);
+ * _.toLength(3);
* // => 3
*
* _.toLength(Number.MIN_VALUE);
@@ -47400,7 +47301,7 @@ return jQuery;
* _.toLength(Infinity);
* // => 4294967295
*
- * _.toLength('3.2');
+ * _.toLength('3');
* // => 3
*/
function toLength(value) {
@@ -47418,8 +47319,8 @@ return jQuery;
* @returns {number} Returns the number.
* @example
*
- * _.toNumber(3.2);
- * // => 3.2
+ * _.toNumber(3);
+ * // => 3
*
* _.toNumber(Number.MIN_VALUE);
* // => 5e-324
@@ -47427,8 +47328,8 @@ return jQuery;
* _.toNumber(Infinity);
* // => Infinity
*
- * _.toNumber('3.2');
- * // => 3.2
+ * _.toNumber('3');
+ * // => 3
*/
function toNumber(value) {
if (typeof value == 'number') {
@@ -47491,7 +47392,7 @@ return jQuery;
* @returns {number} Returns the converted integer.
* @example
*
- * _.toSafeInteger(3.2);
+ * _.toSafeInteger(3);
* // => 3
*
* _.toSafeInteger(Number.MIN_VALUE);
@@ -47500,7 +47401,7 @@ return jQuery;
* _.toSafeInteger(Infinity);
* // => 9007199254740991
*
- * _.toSafeInteger('3.2');
+ * _.toSafeInteger('3');
* // => 3
*/
function toSafeInteger(value) {
@@ -47693,7 +47594,7 @@ return jQuery;
* @category Object
* @param {Object} object The object to iterate over.
* @param {...(string|string[])} [paths] The property paths of elements to pick.
- * @returns {Array} Returns the picked values.
+ * @returns {Array} Returns the new array of picked elements.
* @example
*
* var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
@@ -47909,7 +47810,7 @@ return jQuery;
function forIn(object, iteratee) {
return object == null
? object
- : baseFor(object, getIteratee(iteratee, 3), keysIn);
+ : baseFor(object, getIteratee(iteratee), keysIn);
}
/**
@@ -47941,7 +47842,7 @@ return jQuery;
function forInRight(object, iteratee) {
return object == null
? object
- : baseForRight(object, getIteratee(iteratee, 3), keysIn);
+ : baseForRight(object, getIteratee(iteratee), keysIn);
}
/**
@@ -47973,7 +47874,7 @@ return jQuery;
* // => Logs 'a' then 'b' (iteration order is not guaranteed).
*/
function forOwn(object, iteratee) {
- return object && baseForOwn(object, getIteratee(iteratee, 3));
+ return object && baseForOwn(object, getIteratee(iteratee));
}
/**
@@ -48003,7 +47904,7 @@ return jQuery;
* // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'.
*/
function forOwnRight(object, iteratee) {
- return object && baseForOwnRight(object, getIteratee(iteratee, 3));
+ return object && baseForOwnRight(object, getIteratee(iteratee));
}
/**
@@ -48015,7 +47916,7 @@ return jQuery;
* @memberOf _
* @category Object
* @param {Object} object The object to inspect.
- * @returns {Array} Returns the function names.
+ * @returns {Array} Returns the new array of property names.
* @see _.functionsIn
* @example
*
@@ -48042,7 +47943,7 @@ return jQuery;
* @since 4.0.0
* @category Object
* @param {Object} object The object to inspect.
- * @returns {Array} Returns the function names.
+ * @returns {Array} Returns the new array of property names.
* @see _.functions
* @example
*
@@ -48395,7 +48296,7 @@ return jQuery;
* inherited enumerable string keyed properties of source objects into the
* destination object. Source properties that resolve to `undefined` are
* skipped if a destination value exists. Array and plain object properties
- * are merged recursively. Other objects and value types are overridden by
+ * are merged recursively.Other objects and value types are overridden by
* assignment. Source objects are applied from left to right. Subsequent
* sources overwrite property assignments of previous sources.
*
@@ -48680,8 +48581,7 @@ return jQuery;
/**
* Creates an array of own enumerable string keyed-value pairs for `object`
- * which can be consumed by `_.fromPairs`. If `object` is a map or set, its
- * entries are returned.
+ * which can be consumed by `_.fromPairs`.
*
* @static
* @memberOf _
@@ -48689,7 +48589,7 @@ return jQuery;
* @alias entries
* @category Object
* @param {Object} object The object to query.
- * @returns {Array} Returns the key-value pairs.
+ * @returns {Array} Returns the new array of key-value pairs.
* @example
*
* function Foo() {
@@ -48702,12 +48602,13 @@ return jQuery;
* _.toPairs(new Foo);
* // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
*/
- var toPairs = createToPairs(keys);
+ function toPairs(object) {
+ return baseToPairs(object, keys(object));
+ }
/**
* Creates an array of own and inherited enumerable string keyed-value pairs
- * for `object` which can be consumed by `_.fromPairs`. If `object` is a map
- * or set, its entries are returned.
+ * for `object` which can be consumed by `_.fromPairs`.
*
* @static
* @memberOf _
@@ -48715,7 +48616,7 @@ return jQuery;
* @alias entriesIn
* @category Object
* @param {Object} object The object to query.
- * @returns {Array} Returns the key-value pairs.
+ * @returns {Array} Returns the new array of key-value pairs.
* @example
*
* function Foo() {
@@ -48726,9 +48627,11 @@ return jQuery;
* Foo.prototype.c = 3;
*
* _.toPairsIn(new Foo);
- * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed)
+ * // => [['a', 1], ['b', 2], ['c', 1]] (iteration order is not guaranteed)
*/
- var toPairsIn = createToPairs(keysIn);
+ function toPairsIn(object) {
+ return baseToPairs(object, keysIn(object));
+ }
/**
* An alternative to `_.reduce`; this method transforms `object` to a new
@@ -49558,7 +49461,7 @@ return jQuery;
* @param {string} [string=''] The string to split.
* @param {RegExp|string} separator The separator pattern to split by.
* @param {number} [limit] The length to truncate results to.
- * @returns {Array} Returns the string segments.
+ * @returns {Array} Returns the new array of string segments.
* @example
*
* _.split('a-b-c', '-', 2);
@@ -49703,6 +49606,12 @@ return jQuery;
* compiled({ 'user': 'pebbles' });
* // => 'hello pebbles!'
*
+ * // Use custom template delimiters.
+ * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
+ * var compiled = _.template('hello {{ user }}!');
+ * compiled({ 'user': 'mustache' });
+ * // => 'hello mustache!'
+ *
* // Use backslashes to treat delimiters as plain text.
* var compiled = _.template('<%= "\\<%- value %\\>" %>');
* compiled({ 'value': 'ignored' });
@@ -49728,15 +49637,9 @@ return jQuery;
* // return __p;
* // }
*
- * // Use custom template delimiters.
- * _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
- * var compiled = _.template('hello {{ user }}!');
- * compiled({ 'user': 'mustache' });
- * // => 'hello mustache!'
- *
* // Use the `source` property to inline compiled templates for meaningful
* // line numbers in error messages and stack traces.
- * fs.writeFileSync(path.join(process.cwd(), 'jst.js'), '\
+ * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
* var JST = {\
* "main": ' + _.template(mainText).source + '\
* };\
@@ -50272,7 +50175,7 @@ return jQuery;
* @since 4.0.0
* @category Util
* @param {Array} pairs The predicate-function pairs.
- * @returns {Function} Returns the new composite function.
+ * @returns {Function} Returns the new function.
* @example
*
* var func = _.cond([
@@ -50322,7 +50225,7 @@ return jQuery;
* @since 4.0.0
* @category Util
* @param {Object} source The object of property predicates to conform to.
- * @returns {Function} Returns the new spec function.
+ * @returns {Function} Returns the new function.
* @example
*
* var users = [
@@ -50345,7 +50248,7 @@ return jQuery;
* @since 2.4.0
* @category Util
* @param {*} value The value to return from the new function.
- * @returns {Function} Returns the new constant function.
+ * @returns {Function} Returns the new function.
* @example
*
* var object = { 'user': 'fred' };
@@ -50370,7 +50273,7 @@ return jQuery;
* @since 3.0.0
* @category Util
* @param {...(Function|Function[])} [funcs] Functions to invoke.
- * @returns {Function} Returns the new composite function.
+ * @returns {Function} Returns the new function.
* @see _.flowRight
* @example
*
@@ -50393,7 +50296,7 @@ return jQuery;
* @memberOf _
* @category Util
* @param {...(Function|Function[])} [funcs] Functions to invoke.
- * @returns {Function} Returns the new composite function.
+ * @returns {Function} Returns the new function.
* @see _.flow
* @example
*
@@ -50486,7 +50389,7 @@ return jQuery;
* @since 3.0.0
* @category Util
* @param {Object} source The object of property values to match.
- * @returns {Function} Returns the new spec function.
+ * @returns {Function} Returns the new function.
* @example
*
* var users = [
@@ -50514,7 +50417,7 @@ return jQuery;
* @category Util
* @param {Array|string} path The path of the property to get.
* @param {*} srcValue The value to match.
- * @returns {Function} Returns the new spec function.
+ * @returns {Function} Returns the new function.
* @example
*
* var users = [
@@ -50539,7 +50442,7 @@ return jQuery;
* @category Util
* @param {Array|string} path The path of the method to invoke.
* @param {...*} [args] The arguments to invoke the method with.
- * @returns {Function} Returns the new invoker function.
+ * @returns {Function} Returns the new function.
* @example
*
* var objects = [
@@ -50570,7 +50473,7 @@ return jQuery;
* @category Util
* @param {Object} object The object to query.
* @param {...*} [args] The arguments to invoke the method with.
- * @returns {Function} Returns the new invoker function.
+ * @returns {Function} Returns the new function.
* @example
*
* var array = _.times(3, _.constant),
@@ -50700,7 +50603,7 @@ return jQuery;
}
/**
- * Creates a function that gets the argument at `n` index. If `n` is negative,
+ * Creates a function that returns its nth argument. If `n` is negative,
* the nth argument from the end is returned.
*
* @static
@@ -50708,7 +50611,7 @@ return jQuery;
* @since 4.0.0
* @category Util
* @param {number} [n=0] The index of the argument to return.
- * @returns {Function} Returns the new pass-thru function.
+ * @returns {Function} Returns the new function.
* @example
*
* var func = _.nthArg(1);
@@ -50806,7 +50709,7 @@ return jQuery;
* @since 2.4.0
* @category Util
* @param {Array|string} path The path of the property to get.
- * @returns {Function} Returns the new accessor function.
+ * @returns {Function} Returns the new function.
* @example
*
* var objects = [
@@ -50833,7 +50736,7 @@ return jQuery;
* @since 3.0.0
* @category Util
* @param {Object} object The object to query.
- * @returns {Function} Returns the new accessor function.
+ * @returns {Function} Returns the new function.
* @example
*
* var array = [0, 1, 2],
@@ -50867,7 +50770,7 @@ return jQuery;
* @param {number} [start=0] The start of the range.
* @param {number} end The end of the range.
* @param {number} [step=1] The value to increment or decrement by.
- * @returns {Array} Returns the range of numbers.
+ * @returns {Array} Returns the new array of numbers.
* @see _.inRange, _.rangeRight
* @example
*
@@ -50905,7 +50808,7 @@ return jQuery;
* @param {number} [start=0] The start of the range.
* @param {number} end The end of the range.
* @param {number} [step=1] The value to increment or decrement by.
- * @returns {Array} Returns the range of numbers.
+ * @returns {Array} Returns the new array of numbers.
* @see _.inRange, _.range
* @example
*
@@ -51666,7 +51569,6 @@ return jQuery;
lodash.sumBy = sumBy;
lodash.template = template;
lodash.times = times;
- lodash.toFinite = toFinite;
lodash.toInteger = toInteger;
lodash.toLength = toLength;
lodash.toLower = toLower;
diff --git a/mitmproxy/webfonts/fontawesome-webfont.eot b/mitmproxy/webfonts/fontawesome-webfont.eot
deleted file mode 100644
index 84677bc0..00000000
--- a/mitmproxy/webfonts/fontawesome-webfont.eot
+++ /dev/null
Binary files differ
diff --git a/mitmproxy/webfonts/fontawesome-webfont.svg b/mitmproxy/webfonts/fontawesome-webfont.svg
deleted file mode 100644
index d907b25a..00000000
--- a/mitmproxy/webfonts/fontawesome-webfont.svg
+++ /dev/null
@@ -1,520 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
-<svg xmlns="http://www.w3.org/2000/svg">
-<metadata></metadata>
-<defs>
-<font id="fontawesomeregular" horiz-adv-x="1536" >
-<font-face units-per-em="1792" ascent="1536" descent="-256" />
-<missing-glyph horiz-adv-x="448" />
-<glyph unicode=" " horiz-adv-x="448" />
-<glyph unicode="&#x09;" horiz-adv-x="448" />
-<glyph unicode="&#xa0;" horiz-adv-x="448" />
-<glyph unicode="&#xa8;" horiz-adv-x="1792" />
-<glyph unicode="&#xa9;" horiz-adv-x="1792" />
-<glyph unicode="&#xae;" horiz-adv-x="1792" />
-<glyph unicode="&#xb4;" horiz-adv-x="1792" />
-<glyph unicode="&#xc6;" horiz-adv-x="1792" />
-<glyph unicode="&#xd8;" horiz-adv-x="1792" />
-<glyph unicode="&#x2000;" horiz-adv-x="768" />
-<glyph unicode="&#x2001;" horiz-adv-x="1537" />
-<glyph unicode="&#x2002;" horiz-adv-x="768" />
-<glyph unicode="&#x2003;" horiz-adv-x="1537" />
-<glyph unicode="&#x2004;" horiz-adv-x="512" />
-<glyph unicode="&#x2005;" horiz-adv-x="384" />
-<glyph unicode="&#x2006;" horiz-adv-x="256" />
-<glyph unicode="&#x2007;" horiz-adv-x="256" />
-<glyph unicode="&#x2008;" horiz-adv-x="192" />
-<glyph unicode="&#x2009;" horiz-adv-x="307" />
-<glyph unicode="&#x200a;" horiz-adv-x="85" />
-<glyph unicode="&#x202f;" horiz-adv-x="307" />
-<glyph unicode="&#x205f;" horiz-adv-x="384" />
-<glyph unicode="&#x2122;" horiz-adv-x="1792" />
-<glyph unicode="&#x221e;" horiz-adv-x="1792" />
-<glyph unicode="&#x2260;" horiz-adv-x="1792" />
-<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
-<glyph unicode="&#xf000;" horiz-adv-x="1792" d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" />
-<glyph unicode="&#xf001;" d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89 t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf002;" horiz-adv-x="1664" d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5 t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
-<glyph unicode="&#xf003;" horiz-adv-x="1792" d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13 t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf004;" horiz-adv-x="1792" d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600 q-18 -18 -44 -18z" />
-<glyph unicode="&#xf005;" horiz-adv-x="1664" d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455 l502 -73q56 -9 56 -46z" />
-<glyph unicode="&#xf006;" horiz-adv-x="1664" d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500 l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" />
-<glyph unicode="&#xf007;" horiz-adv-x="1408" d="M1408 131q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q9 0 42 -21.5t74.5 -48t108 -48t133.5 -21.5t133.5 21.5t108 48t74.5 48t42 21.5q61 0 111.5 -20t85.5 -53.5t62 -81 t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
-<glyph unicode="&#xf008;" horiz-adv-x="1920" d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128 q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45 t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128 q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19 t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf009;" horiz-adv-x="1664" d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38 h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf00a;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf00b;" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf00c;" horiz-adv-x="1792" d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" />
-<glyph unicode="&#xf00d;" horiz-adv-x="1408" d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68 t-28 -68l-294 -294l294 -294q28 -28 28 -68z" />
-<glyph unicode="&#xf00e;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224 q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5 t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
-<glyph unicode="&#xf010;" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z " />
-<glyph unicode="&#xf011;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5 t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" />
-<glyph unicode="&#xf012;" horiz-adv-x="1792" d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf013;" d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38 q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13 l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22 q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" />
-<glyph unicode="&#xf014;" horiz-adv-x="1408" d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832 q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf015;" horiz-adv-x="1664" d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5 l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" />
-<glyph unicode="&#xf016;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z " />
-<glyph unicode="&#xf017;" d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf018;" horiz-adv-x="1920" d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256 q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" />
-<glyph unicode="&#xf019;" horiz-adv-x="1664" d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136 q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" />
-<glyph unicode="&#xf01a;" d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273 t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf01b;" d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198 t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf01c;" d="M1023 576h316q-1 3 -2.5 8t-2.5 8l-212 496h-708l-212 -496q-1 -2 -2.5 -8t-2.5 -8h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552 q25 -61 25 -123z" />
-<glyph unicode="&#xf01d;" d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf01e;" d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q14 0 25 -9 l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" />
-<glyph unicode="&#xf021;" d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117 q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5 q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" />
-<glyph unicode="&#xf022;" horiz-adv-x="1792" d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5 t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47 t47 -113z" />
-<glyph unicode="&#xf023;" horiz-adv-x="1152" d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf024;" horiz-adv-x="1792" d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf025;" horiz-adv-x="1664" d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78 t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5 t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" />
-<glyph unicode="&#xf026;" horiz-adv-x="768" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" />
-<glyph unicode="&#xf027;" horiz-adv-x="1152" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" />
-<glyph unicode="&#xf028;" horiz-adv-x="1664" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5 t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289 t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" />
-<glyph unicode="&#xf029;" horiz-adv-x="1408" d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" />
-<glyph unicode="&#xf02a;" horiz-adv-x="1792" d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" />
-<glyph unicode="&#xf02b;" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91z" />
-<glyph unicode="&#xf02c;" horiz-adv-x="1920" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" />
-<glyph unicode="&#xf02d;" horiz-adv-x="1664" d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23 q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906 q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5 t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" />
-<glyph unicode="&#xf02e;" horiz-adv-x="1280" d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
-<glyph unicode="&#xf02f;" horiz-adv-x="1664" d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68 v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" />
-<glyph unicode="&#xf030;" horiz-adv-x="1920" d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136 q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
-<glyph unicode="&#xf031;" horiz-adv-x="1664" d="M725 977l-170 -450q33 0 136.5 -2t160.5 -2q19 0 57 2q-87 253 -184 452zM0 -128l2 79q23 7 56 12.5t57 10.5t49.5 14.5t44.5 29t31 50.5l237 616l280 724h75h53q8 -14 11 -21l205 -480q33 -78 106 -257.5t114 -274.5q15 -34 58 -144.5t72 -168.5q20 -45 35 -57 q19 -15 88 -29.5t84 -20.5q6 -38 6 -57q0 -4 -0.5 -13t-0.5 -13q-63 0 -190 8t-191 8q-76 0 -215 -7t-178 -8q0 43 4 78l131 28q1 0 12.5 2.5t15.5 3.5t14.5 4.5t15 6.5t11 8t9 11t2.5 14q0 16 -31 96.5t-72 177.5t-42 100l-450 2q-26 -58 -76.5 -195.5t-50.5 -162.5 q0 -22 14 -37.5t43.5 -24.5t48.5 -13.5t57 -8.5t41 -4q1 -19 1 -58q0 -9 -2 -27q-58 0 -174.5 10t-174.5 10q-8 0 -26.5 -4t-21.5 -4q-80 -14 -188 -14z" />
-<glyph unicode="&#xf032;" horiz-adv-x="1408" d="M555 15q74 -32 140 -32q376 0 376 335q0 114 -41 180q-27 44 -61.5 74t-67.5 46.5t-80.5 25t-84 10.5t-94.5 2q-73 0 -101 -10q0 -53 -0.5 -159t-0.5 -158q0 -8 -1 -67.5t-0.5 -96.5t4.5 -83.5t12 -66.5zM541 761q42 -7 109 -7q82 0 143 13t110 44.5t74.5 89.5t25.5 142 q0 70 -29 122.5t-79 82t-108 43.5t-124 14q-50 0 -130 -13q0 -50 4 -151t4 -152q0 -27 -0.5 -80t-0.5 -79q0 -46 1 -69zM0 -128l2 94q15 4 85 16t106 27q7 12 12.5 27t8.5 33.5t5.5 32.5t3 37.5t0.5 34v35.5v30q0 982 -22 1025q-4 8 -22 14.5t-44.5 11t-49.5 7t-48.5 4.5 t-30.5 3l-4 83q98 2 340 11.5t373 9.5q23 0 68.5 -0.5t67.5 -0.5q70 0 136.5 -13t128.5 -42t108 -71t74 -104.5t28 -137.5q0 -52 -16.5 -95.5t-39 -72t-64.5 -57.5t-73 -45t-84 -40q154 -35 256.5 -134t102.5 -248q0 -100 -35 -179.5t-93.5 -130.5t-138 -85.5t-163.5 -48.5 t-176 -14q-44 0 -132 3t-132 3q-106 0 -307 -11t-231 -12z" />
-<glyph unicode="&#xf033;" horiz-adv-x="1024" d="M0 -126l17 85q6 2 81.5 21.5t111.5 37.5q28 35 41 101q1 7 62 289t114 543.5t52 296.5v25q-24 13 -54.5 18.5t-69.5 8t-58 5.5l19 103q33 -2 120 -6.5t149.5 -7t120.5 -2.5q48 0 98.5 2.5t121 7t98.5 6.5q-5 -39 -19 -89q-30 -10 -101.5 -28.5t-108.5 -33.5 q-8 -19 -14 -42.5t-9 -40t-7.5 -45.5t-6.5 -42q-27 -148 -87.5 -419.5t-77.5 -355.5q-2 -9 -13 -58t-20 -90t-16 -83.5t-6 -57.5l1 -18q17 -4 185 -31q-3 -44 -16 -99q-11 0 -32.5 -1.5t-32.5 -1.5q-29 0 -87 10t-86 10q-138 2 -206 2q-51 0 -143 -9t-121 -11z" />
-<glyph unicode="&#xf034;" horiz-adv-x="1792" d="M1744 128q33 0 42 -18.5t-11 -44.5l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80zM81 1407l54 -27q12 -5 211 -5q44 0 132 2 t132 2q36 0 107.5 -0.5t107.5 -0.5h293q6 0 21 -0.5t20.5 0t16 3t17.5 9t15 17.5l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 48t-14.5 73.5t-7.5 35.5q-6 8 -12 12.5t-15.5 6t-13 2.5t-18 0.5t-16.5 -0.5 q-17 0 -66.5 0.5t-74.5 0.5t-64 -2t-71 -6q-9 -81 -8 -136q0 -94 2 -388t2 -455q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 q19 42 19 383q0 101 -3 303t-3 303v117q0 2 0.5 15.5t0.5 25t-1 25.5t-3 24t-5 14q-11 12 -162 12q-33 0 -93 -12t-80 -26q-19 -13 -34 -72.5t-31.5 -111t-42.5 -53.5q-42 26 -56 44v383z" />
-<glyph unicode="&#xf035;" d="M81 1407l54 -27q12 -5 211 -5q44 0 132 2t132 2q70 0 246.5 1t304.5 0.5t247 -4.5q33 -1 56 31l42 1q4 0 14 -0.5t14 -0.5q2 -112 2 -336q0 -80 -5 -109q-39 -14 -68 -18q-25 44 -54 128q-3 9 -11 47.5t-15 73.5t-7 36q-10 13 -27 19q-5 2 -66 2q-30 0 -93 1t-103 1 t-94 -2t-96 -7q-9 -81 -8 -136l1 -152v52q0 -55 1 -154t1.5 -180t0.5 -153q0 -16 -2.5 -71.5t0 -91.5t12.5 -69q40 -21 124 -42.5t120 -37.5q5 -40 5 -50q0 -14 -3 -29l-34 -1q-76 -2 -218 8t-207 10q-50 0 -151 -9t-152 -9q-3 51 -3 52v9q17 27 61.5 43t98.5 29t78 27 q7 16 11.5 74t6 145.5t1.5 155t-0.5 153.5t-0.5 89q0 7 -2.5 21.5t-2.5 22.5q0 7 0.5 44t1 73t0 76.5t-3 67.5t-6.5 32q-11 12 -162 12q-41 0 -163 -13.5t-138 -24.5q-19 -12 -34 -71.5t-31.5 -111.5t-42.5 -54q-42 26 -56 44v383zM1310 125q12 0 42 -19.5t57.5 -41.5 t59.5 -49t36 -30q26 -21 26 -49t-26 -49q-4 -3 -36 -30t-59.5 -49t-57.5 -41.5t-42 -19.5q-13 0 -20.5 10.5t-10 28.5t-2.5 33.5t1.5 33t1.5 19.5h-1024q0 -2 1.5 -19.5t1.5 -33t-2.5 -33.5t-10 -28.5t-20.5 -10.5q-12 0 -42 19.5t-57.5 41.5t-59.5 49t-36 30q-26 21 -26 49 t26 49q4 3 36 30t59.5 49t57.5 41.5t42 19.5q13 0 20.5 -10.5t10 -28.5t2.5 -33.5t-1.5 -33t-1.5 -19.5h1024q0 2 -1.5 19.5t-1.5 33t2.5 33.5t10 28.5t20.5 10.5z" />
-<glyph unicode="&#xf036;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf037;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19 h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf038;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf039;" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf03a;" horiz-adv-x="1792" d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5 t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344 q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192 q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf03b;" horiz-adv-x="1792" d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf03c;" horiz-adv-x="1792" d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf03d;" horiz-adv-x="1792" d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5 q39 -17 39 -59z" />
-<glyph unicode="&#xf03e;" horiz-adv-x="1920" d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216 q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf040;" d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38 q53 0 91 -38l235 -234q37 -39 37 -91z" />
-<glyph unicode="&#xf041;" horiz-adv-x="1024" d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" />
-<glyph unicode="&#xf042;" d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf043;" horiz-adv-x="1024" d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362 q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" />
-<glyph unicode="&#xf044;" horiz-adv-x="1792" d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92 l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" />
-<glyph unicode="&#xf045;" horiz-adv-x="1664" d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832 q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5 t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" />
-<glyph unicode="&#xf046;" horiz-adv-x="1664" d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832 q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110 q24 -24 24 -57t-24 -57z" />
-<glyph unicode="&#xf047;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45 t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
-<glyph unicode="&#xf048;" horiz-adv-x="1024" d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19z" />
-<glyph unicode="&#xf049;" horiz-adv-x="1792" d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19l710 710 q19 19 32 13t13 -32v-710q4 11 13 19z" />
-<glyph unicode="&#xf04a;" horiz-adv-x="1664" d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-8 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q5 11 13 19z" />
-<glyph unicode="&#xf04b;" horiz-adv-x="1408" d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" />
-<glyph unicode="&#xf04c;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf04d;" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf04e;" horiz-adv-x="1664" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
-<glyph unicode="&#xf050;" horiz-adv-x="1792" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19l-710 -710 q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
-<glyph unicode="&#xf051;" horiz-adv-x="1024" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19z" />
-<glyph unicode="&#xf052;" horiz-adv-x="1538" d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" />
-<glyph unicode="&#xf053;" horiz-adv-x="1280" d="M1171 1235l-531 -531l531 -531q19 -19 19 -45t-19 -45l-166 -166q-19 -19 -45 -19t-45 19l-742 742q-19 19 -19 45t19 45l742 742q19 19 45 19t45 -19l166 -166q19 -19 19 -45t-19 -45z" />
-<glyph unicode="&#xf054;" horiz-adv-x="1280" d="M1107 659l-742 -742q-19 -19 -45 -19t-45 19l-166 166q-19 19 -19 45t19 45l531 531l-531 531q-19 19 -19 45t19 45l166 166q19 19 45 19t45 -19l742 -742q19 -19 19 -45t-19 -45z" />
-<glyph unicode="&#xf055;" d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5 t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf056;" d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
-<glyph unicode="&#xf057;" d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19 q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf058;" d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf059;" d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59 q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf05a;" d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23 t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf05b;" d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109 q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143 q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf05c;" d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5 t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf05d;" d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198 t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf05e;" d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61 t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" />
-<glyph unicode="&#xf060;" d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5 t32.5 -90.5z" />
-<glyph unicode="&#xf061;" d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" />
-<glyph unicode="&#xf062;" horiz-adv-x="1664" d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651 q37 -39 37 -91z" />
-<glyph unicode="&#xf063;" horiz-adv-x="1664" d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
-<glyph unicode="&#xf064;" horiz-adv-x="1792" d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22 t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" />
-<glyph unicode="&#xf065;" d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332 q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf066;" d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45 t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" />
-<glyph unicode="&#xf067;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf068;" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf069;" horiz-adv-x="1664" d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154 q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" />
-<glyph unicode="&#xf06a;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192 q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" />
-<glyph unicode="&#xf06b;" d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320 q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5 t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf06c;" horiz-adv-x="1792" d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268 q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-30 0 -51 11t-31 24t-27 42q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5 t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" />
-<glyph unicode="&#xf06d;" horiz-adv-x="1408" d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1 q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" />
-<glyph unicode="&#xf06e;" horiz-adv-x="1792" d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5 t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" />
-<glyph unicode="&#xf070;" horiz-adv-x="1792" d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9 q-105 -188 -315 -566t-316 -567l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5 q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z " />
-<glyph unicode="&#xf071;" horiz-adv-x="1792" d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185 q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" />
-<glyph unicode="&#xf072;" horiz-adv-x="1408" d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9 q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" />
-<glyph unicode="&#xf073;" horiz-adv-x="1664" d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64 q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47 h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf074;" horiz-adv-x="1792" d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1 t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5 v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111 t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
-<glyph unicode="&#xf075;" horiz-adv-x="1792" d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281 q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" />
-<glyph unicode="&#xf076;" d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384 q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf077;" horiz-adv-x="1792" d="M1683 205l-166 -165q-19 -19 -45 -19t-45 19l-531 531l-531 -531q-19 -19 -45 -19t-45 19l-166 165q-19 19 -19 45.5t19 45.5l742 741q19 19 45 19t45 -19l742 -741q19 -19 19 -45.5t-19 -45.5z" />
-<glyph unicode="&#xf078;" horiz-adv-x="1792" d="M1683 728l-742 -741q-19 -19 -45 -19t-45 19l-742 741q-19 19 -19 45.5t19 45.5l166 165q19 19 45 19t45 -19l531 -531l531 531q19 19 45 19t45 -19l166 -165q19 -19 19 -45.5t-19 -45.5z" />
-<glyph unicode="&#xf079;" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " />
-<glyph unicode="&#xf07a;" horiz-adv-x="1664" d="M640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5 l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5 t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf07b;" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
-<glyph unicode="&#xf07c;" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
-<glyph unicode="&#xf07d;" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
-<glyph unicode="&#xf07e;" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
-<glyph unicode="&#xf080;" horiz-adv-x="2048" d="M640 640v-512h-256v512h256zM1024 1152v-1024h-256v1024h256zM2048 0v-128h-2048v1536h128v-1408h1920zM1408 896v-768h-256v768h256zM1792 1280v-1152h-256v1152h256z" />
-<glyph unicode="&#xf081;" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf082;" d="M1536 160q0 -119 -84.5 -203.5t-203.5 -84.5h-192v608h203l30 224h-233v143q0 54 28 83t96 29l132 1v207q-96 9 -180 9q-136 0 -218 -80.5t-82 -225.5v-166h-224v-224h224v-608h-544q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5v-960z" />
-<glyph unicode="&#xf083;" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
-<glyph unicode="&#xf084;" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
-<glyph unicode="&#xf085;" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
-<glyph unicode="&#xf086;" horiz-adv-x="1792" d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224 q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7 q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" />
-<glyph unicode="&#xf087;" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5 t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769 q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128 q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" />
-<glyph unicode="&#xf088;" d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 32 18 69t-17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5 t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5 h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -74 49 -163z" />
-<glyph unicode="&#xf089;" horiz-adv-x="896" d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" />
-<glyph unicode="&#xf08a;" horiz-adv-x="1792" d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559 q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5 q224 0 351 -124t127 -344z" />
-<glyph unicode="&#xf08b;" horiz-adv-x="1664" d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704 q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" />
-<glyph unicode="&#xf08c;" d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5 q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf08d;" horiz-adv-x="1152" d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38 t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" />
-<glyph unicode="&#xf08e;" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf090;" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf091;" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf092;" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf093;" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
-<glyph unicode="&#xf094;" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" />
-<glyph unicode="&#xf095;" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
-<glyph unicode="&#xf096;" horiz-adv-x="1408" d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf097;" horiz-adv-x="1280" d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289 q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
-<glyph unicode="&#xf098;" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf099;" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
-<glyph unicode="&#xf09a;" horiz-adv-x="1024" d="M959 1524v-264h-157q-86 0 -116 -36t-30 -108v-189h293l-39 -296h-254v-759h-306v759h-255v296h255v218q0 186 104 288.5t277 102.5q147 0 228 -12z" />
-<glyph unicode="&#xf09b;" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf09c;" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" />
-<glyph unicode="&#xf09d;" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
-<glyph unicode="&#xf09e;" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" />
-<glyph unicode="&#xf0a0;" d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5 h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75 l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" />
-<glyph unicode="&#xf0a1;" horiz-adv-x="1792" d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5 t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" />
-<glyph unicode="&#xf0a2;" horiz-adv-x="1792" d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM246 128h1300q-266 300 -266 832q0 51 -24 105t-69 103t-121.5 80.5t-169.5 31.5t-169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -532 -266 -832z M1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5 t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
-<glyph unicode="&#xf0a3;" d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70 l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70 l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" />
-<glyph unicode="&#xf0a4;" horiz-adv-x="1792" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106 q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43 q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5 t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" />
-<glyph unicode="&#xf0a5;" horiz-adv-x="1792" d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-2 3 -3.5 4.5t-4 4.5t-4.5 5q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576 q-50 0 -89 -38.5t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45 t45 -19t45 19t19 45zM1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128 q0 122 81.5 189t206.5 67q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" />
-<glyph unicode="&#xf0a6;" d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576 q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5 t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76 q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" />
-<glyph unicode="&#xf0a7;" d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33 t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580 q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100 q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" />
-<glyph unicode="&#xf0a8;" d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0a9;" d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0aa;" d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0ab;" d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0ac;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11 q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 10.5t-9.5 10.5q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5 q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5 q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5 t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-5 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3 q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25 q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5 t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5 t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10t17 -20q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21 q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5 q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3 q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5 t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q7 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5 q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7 q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" />
-<glyph unicode="&#xf0ad;" horiz-adv-x="1664" d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" />
-<glyph unicode="&#xf0ae;" horiz-adv-x="1792" d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19 t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0b0;" horiz-adv-x="1408" d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" />
-<glyph unicode="&#xf0b1;" horiz-adv-x="1792" d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68 t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf0b2;" d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144 l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z " />
-<glyph unicode="&#xf0c0;" horiz-adv-x="1920" d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75 t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5 t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" />
-<glyph unicode="&#xf0c1;" horiz-adv-x="1664" d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26 l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15 t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207 q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" />
-<glyph unicode="&#xf0c2;" horiz-adv-x="1920" d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z " />
-<glyph unicode="&#xf0c3;" horiz-adv-x="1664" d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" />
-<glyph unicode="&#xf0c4;" horiz-adv-x="1792" d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84 q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148 q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108 q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6 q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" />
-<glyph unicode="&#xf0c5;" horiz-adv-x="1792" d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299 h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" />
-<glyph unicode="&#xf0c6;" horiz-adv-x="1408" d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181 l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235 z" />
-<glyph unicode="&#xf0c7;" d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5 h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" />
-<glyph unicode="&#xf0c8;" d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf0c9;" d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45 t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0ca;" horiz-adv-x="1792" d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf0cb;" horiz-adv-x="1792" d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362 q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5 t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 122t0.5 121v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5 t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
-<glyph unicode="&#xf0cc;" horiz-adv-x="1792" d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 97 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6 l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -55 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23 l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" />
-<glyph unicode="&#xf0cd;" d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47 q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41 q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472 q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" />
-<glyph unicode="&#xf0ce;" horiz-adv-x="1664" d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23 v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192 q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192 q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113 z" />
-<glyph unicode="&#xf0d0;" horiz-adv-x="1664" d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276 l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" />
-<glyph unicode="&#xf0d1;" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0d2;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf0d3;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
-<glyph unicode="&#xf0d4;" d="M829 318q0 -76 -58.5 -112.5t-139.5 -36.5q-41 0 -80.5 9.5t-75.5 28.5t-58 53t-22 78q0 46 25 80t65.5 51.5t82 25t84.5 7.5q20 0 31 -2q2 -1 23 -16.5t26 -19t23 -18t24.5 -22t19 -22.5t17 -26t9 -26.5t4.5 -31.5zM755 863q0 -60 -33 -99.5t-92 -39.5q-53 0 -93 42.5 t-57.5 96.5t-17.5 106q0 61 32 104t92 43q53 0 93.5 -45t58 -101t17.5 -107zM861 1120l88 64h-265q-85 0 -161 -32t-127.5 -98t-51.5 -153q0 -93 64.5 -154.5t158.5 -61.5q22 0 43 3q-13 -29 -13 -54q0 -44 40 -94q-175 -12 -257 -63q-47 -29 -75.5 -73t-28.5 -95 q0 -43 18.5 -77.5t48.5 -56.5t69 -37t77.5 -21t76.5 -6q60 0 120.5 15.5t113.5 46t86 82.5t33 117q0 49 -20 89.5t-49 66.5t-58 47.5t-49 44t-20 44.5t15.5 42.5t37.5 39.5t44 42t37.5 59.5t15.5 82.5q0 60 -22.5 99.5t-72.5 90.5h83zM1152 672h128v64h-128v128h-64v-128 h-128v-64h128v-160h64v160zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf0d5;" horiz-adv-x="1664" d="M735 740q0 -36 32 -70.5t77.5 -68t90.5 -73.5t77 -104t32 -142q0 -90 -48 -173q-72 -122 -211 -179.5t-298 -57.5q-132 0 -246.5 41.5t-171.5 137.5q-37 60 -37 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 42 -47.5 74t-15.5 73q0 36 21 85q-46 -4 -68 -4 q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q77 66 182.5 98t217.5 32h418l-138 -88h-131q74 -63 112 -133t38 -160q0 -72 -24.5 -129.5t-59 -93t-69.5 -65t-59.5 -61.5t-24.5 -66zM589 836q38 0 78 16.5t66 43.5q53 57 53 159q0 58 -17 125t-48.5 129.5 t-84.5 103.5t-117 41q-42 0 -82.5 -19.5t-65.5 -52.5q-47 -59 -47 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26zM591 -37q58 0 111.5 13t99 39t73 73t27.5 109q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -48 2 q-53 0 -105 -7t-107.5 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -70 35 -123.5t91.5 -83t119 -44t127.5 -14.5zM1401 839h213v-108h-213v-219h-105v219h-212v108h212v217h105v-217z" />
-<glyph unicode="&#xf0d6;" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0d7;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0d8;" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
-<glyph unicode="&#xf0d9;" horiz-adv-x="640" d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" />
-<glyph unicode="&#xf0da;" horiz-adv-x="640" d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" />
-<glyph unicode="&#xf0db;" horiz-adv-x="1664" d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf0dc;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
-<glyph unicode="&#xf0dd;" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0de;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
-<glyph unicode="&#xf0e0;" horiz-adv-x="1792" d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123 q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" />
-<glyph unicode="&#xf0e1;" d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329 q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" />
-<glyph unicode="&#xf0e2;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" />
-<glyph unicode="&#xf0e3;" horiz-adv-x="1792" d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5 t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14 q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28 q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" />
-<glyph unicode="&#xf0e4;" horiz-adv-x="1792" d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5 t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5 t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29 q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
-<glyph unicode="&#xf0e5;" horiz-adv-x="1792" d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640 q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5 t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
-<glyph unicode="&#xf0e6;" horiz-adv-x="1792" d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257 t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5 t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129 q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" />
-<glyph unicode="&#xf0e7;" horiz-adv-x="896" d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" />
-<glyph unicode="&#xf0e8;" horiz-adv-x="1792" d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68 z" />
-<glyph unicode="&#xf0e9;" horiz-adv-x="1664" d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97 q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69 q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" />
-<glyph unicode="&#xf0ea;" horiz-adv-x="1792" d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28 h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" />
-<glyph unicode="&#xf0eb;" horiz-adv-x="1024" d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134 q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47 q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5 t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" />
-<glyph unicode="&#xf0ec;" horiz-adv-x="1792" d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9 q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
-<glyph unicode="&#xf0ed;" horiz-adv-x="1920" d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
-<glyph unicode="&#xf0ee;" horiz-adv-x="1920" d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
-<glyph unicode="&#xf0f0;" horiz-adv-x="1408" d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56 t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68 t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
-<glyph unicode="&#xf0f1;" horiz-adv-x="1408" d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48 t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252 t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" />
-<glyph unicode="&#xf0f2;" horiz-adv-x="1792" d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66 t66 -158z" />
-<glyph unicode="&#xf0f3;" horiz-adv-x="1792" d="M912 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1728 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q50 42 91 88t85 119.5t74.5 158.5 t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q190 -28 307 -158.5t117 -282.5q0 -139 19.5 -260t50 -206t74.5 -158.5t85 -119.5t91 -88z" />
-<glyph unicode="&#xf0f4;" horiz-adv-x="1920" d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45 t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" />
-<glyph unicode="&#xf0f5;" horiz-adv-x="1408" d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45 t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0f6;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M384 736q0 14 9 23t23 9h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64zM1120 512q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704zM1120 256q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704 q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704z" />
-<glyph unicode="&#xf0f7;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0f8;" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5 t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320 v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0f9;" horiz-adv-x="1920" d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152 q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf0fa;" horiz-adv-x="1792" d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32 q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" />
-<glyph unicode="&#xf0fb;" horiz-adv-x="1920" d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96 q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q261 -58 287 -93z" />
-<glyph unicode="&#xf0fc;" horiz-adv-x="1664" d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" />
-<glyph unicode="&#xf0fd;" d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf0fe;" d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf100;" horiz-adv-x="1024" d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" />
-<glyph unicode="&#xf101;" horiz-adv-x="1024" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23 l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
-<glyph unicode="&#xf102;" horiz-adv-x="1152" d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393 q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
-<glyph unicode="&#xf103;" horiz-adv-x="1152" d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
-<glyph unicode="&#xf104;" horiz-adv-x="640" d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
-<glyph unicode="&#xf105;" horiz-adv-x="640" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
-<glyph unicode="&#xf106;" horiz-adv-x="1152" d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
-<glyph unicode="&#xf107;" horiz-adv-x="1152" d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
-<glyph unicode="&#xf108;" horiz-adv-x="1920" d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19 t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf109;" horiz-adv-x="1920" d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" />
-<glyph unicode="&#xf10a;" horiz-adv-x="1152" d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832 q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" />
-<glyph unicode="&#xf10b;" horiz-adv-x="768" d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136 q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf10c;" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf10d;" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
-<glyph unicode="&#xf10e;" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
-<glyph unicode="&#xf110;" horiz-adv-x="1568" d="M496 192q0 -60 -42.5 -102t-101.5 -42q-60 0 -102 42t-42 102t42 102t102 42q59 0 101.5 -42t42.5 -102zM928 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -66 -47 -113t-113 -47t-113 47t-47 113 t47 113t113 47t113 -47t47 -113zM1360 192q0 -46 -33 -79t-79 -33t-79 33t-33 79t33 79t79 33t79 -33t33 -79zM528 1088q0 -73 -51.5 -124.5t-124.5 -51.5t-124.5 51.5t-51.5 124.5t51.5 124.5t124.5 51.5t124.5 -51.5t51.5 -124.5zM992 1280q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1536 640q0 -40 -28 -68t-68 -28t-68 28t-28 68t28 68t68 28t68 -28t28 -68zM1328 1088q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5z" />
-<glyph unicode="&#xf111;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf112;" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
-<glyph unicode="&#xf113;" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" />
-<glyph unicode="&#xf114;" horiz-adv-x="1664" d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320 q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
-<glyph unicode="&#xf115;" horiz-adv-x="1920" d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68 v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z " />
-<glyph unicode="&#xf116;" horiz-adv-x="1792" />
-<glyph unicode="&#xf117;" horiz-adv-x="1792" />
-<glyph unicode="&#xf118;" d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5 t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf119;" d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204 t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf11a;" d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf11b;" horiz-adv-x="1920" d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150 t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" />
-<glyph unicode="&#xf11c;" horiz-adv-x="1920" d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16 h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16 h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96 q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896 h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" />
-<glyph unicode="&#xf11d;" horiz-adv-x="1792" d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9 h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102 q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
-<glyph unicode="&#xf11e;" horiz-adv-x="1792" d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2 q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266 q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8 q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
-<glyph unicode="&#xf120;" horiz-adv-x="1664" d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9 t9 -23z" />
-<glyph unicode="&#xf121;" horiz-adv-x="1920" d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5 l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" />
-<glyph unicode="&#xf122;" horiz-adv-x="1792" d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1 q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" />
-<glyph unicode="&#xf123;" horiz-adv-x="1664" d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5 l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" />
-<glyph unicode="&#xf124;" horiz-adv-x="1408" d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" />
-<glyph unicode="&#xf125;" horiz-adv-x="1664" d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23 v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf126;" horiz-adv-x="1024" d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5 q-2 -287 -226 -414q-68 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497 q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" />
-<glyph unicode="&#xf127;" horiz-adv-x="1664" d="M439 265l-256 -256q-10 -9 -23 -9q-12 0 -23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320 q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18 l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9 t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
-<glyph unicode="&#xf128;" horiz-adv-x="1024" d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5 t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" />
-<glyph unicode="&#xf129;" horiz-adv-x="640" d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192 q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf12a;" horiz-adv-x="640" d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" />
-<glyph unicode="&#xf12b;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1534 846v-206h-514l-3 27 q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5t-65.5 -51.5t-30.5 -63h232v80 h126z" />
-<glyph unicode="&#xf12c;" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1536 -50v-206h-514l-4 27 q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73h232v80h126z" />
-<glyph unicode="&#xf12d;" horiz-adv-x="1920" d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" />
-<glyph unicode="&#xf12e;" horiz-adv-x="1664" d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5 t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89 q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117 q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" />
-<glyph unicode="&#xf130;" horiz-adv-x="1152" d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5 t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" />
-<glyph unicode="&#xf131;" horiz-adv-x="1408" d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128 q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23 t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" />
-<glyph unicode="&#xf132;" horiz-adv-x="1280" d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150 t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf133;" horiz-adv-x="1664" d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf134;" horiz-adv-x="1408" d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800 q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113 q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" />
-<glyph unicode="&#xf135;" horiz-adv-x="1664" d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1 q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" />
-<glyph unicode="&#xf136;" horiz-adv-x="1792" d="M1745 763l-164 -763h-334l178 832q13 56 -15 88q-27 33 -83 33h-169l-204 -953h-334l204 953h-286l-204 -953h-334l204 953l-153 327h1276q101 0 189.5 -40.5t147.5 -113.5q60 -73 81 -168.5t0 -194.5z" />
-<glyph unicode="&#xf137;" d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf138;" d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf139;" d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf13a;" d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf13b;" horiz-adv-x="1408" d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" />
-<glyph unicode="&#xf13c;" horiz-adv-x="1792" d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" />
-<glyph unicode="&#xf13d;" horiz-adv-x="1792" d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-13 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352 q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19 t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf13e;" horiz-adv-x="1152" d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181 v-320h736z" />
-<glyph unicode="&#xf140;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150 t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf141;" horiz-adv-x="1408" d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf142;" horiz-adv-x="384" d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
-<glyph unicode="&#xf143;" d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 232 -177 396t-396 177q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128q13 0 23 10 t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf144;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56 q16 -8 32 -8q17 0 32 9z" />
-<glyph unicode="&#xf145;" horiz-adv-x="1792" d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136 t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" />
-<glyph unicode="&#xf146;" d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
-<glyph unicode="&#xf147;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5 t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf148;" horiz-adv-x="1024" d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" />
-<glyph unicode="&#xf149;" horiz-adv-x="1024" d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" />
-<glyph unicode="&#xf14a;" d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5 t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf14b;" d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf14c;" d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf14d;" d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q10 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5 t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf14e;" d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf150;" d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf151;" d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf152;" d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf153;" horiz-adv-x="1024" d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9 t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26 l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" />
-<glyph unicode="&#xf154;" horiz-adv-x="1024" d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7 q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" />
-<glyph unicode="&#xf155;" horiz-adv-x="1024" d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43 t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5 t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50 t53 -63.5t31.5 -76.5t13 -94z" />
-<glyph unicode="&#xf156;" horiz-adv-x="898" d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102 q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf157;" horiz-adv-x="1027" d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61 l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" />
-<glyph unicode="&#xf158;" horiz-adv-x="1280" d="M1043 971q0 100 -65 162t-171 62h-320v-448h320q106 0 171 62t65 162zM1280 971q0 -193 -126.5 -315t-326.5 -122h-340v-118h505q14 0 23 -9t9 -23v-128q0 -14 -9 -23t-23 -9h-505v-192q0 -14 -9.5 -23t-22.5 -9h-167q-14 0 -23 9t-9 23v192h-224q-14 0 -23 9t-9 23v128 q0 14 9 23t23 9h224v118h-224q-14 0 -23 9t-9 23v149q0 13 9 22.5t23 9.5h224v629q0 14 9 23t23 9h539q200 0 326.5 -122t126.5 -315z" />
-<glyph unicode="&#xf159;" horiz-adv-x="1792" d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23 t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28 q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf15a;" horiz-adv-x="1280" d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164 l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30 t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" />
-<glyph unicode="&#xf15b;" d="M1024 1024v472q22 -14 36 -28l408 -408q14 -14 28 -36h-472zM896 992q0 -40 28 -68t68 -28h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544z" />
-<glyph unicode="&#xf15c;" d="M1468 1060q14 -14 28 -36h-472v472q22 -14 36 -28zM992 896h544v-1056q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h800v-544q0 -40 28 -68t68 -28zM1152 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704 q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23z" />
-<glyph unicode="&#xf15d;" horiz-adv-x="1664" d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23 v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162 l230 -662h70z" />
-<glyph unicode="&#xf15e;" horiz-adv-x="1664" d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150 v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248 v119h121z" />
-<glyph unicode="&#xf160;" horiz-adv-x="1792" d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832 q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf161;" horiz-adv-x="1792" d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192 q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf162;" d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23 zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5 t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" />
-<glyph unicode="&#xf163;" d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9 t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13 q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" />
-<glyph unicode="&#xf164;" horiz-adv-x="1664" d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76 q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5 t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" />
-<glyph unicode="&#xf165;" horiz-adv-x="1664" d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135 t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121 t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" />
-<glyph unicode="&#xf166;" d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 16 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15 q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38 q21 -28 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5 q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78l24 -69t23 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38q-51 0 -78 -38 q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf167;" d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73 q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51 q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99 q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-37 -51 -106 -51q-67 0 -105 51 q-28 38 -28 118v175q0 80 28 117q38 51 105 51q69 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" />
-<glyph unicode="&#xf168;" horiz-adv-x="1408" d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942 q25 45 64 45h241q22 0 31 -15z" />
-<glyph unicode="&#xf169;" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf16a;" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
-<glyph unicode="&#xf16b;" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
-<glyph unicode="&#xf16c;" horiz-adv-x="1408" d="M928 135v-151l-707 -1v151zM1169 481v-701l-1 -35v-1h-1132l-35 1h-1v736h121v-618h928v618h120zM241 393l704 -65l-13 -150l-705 65zM309 709l683 -183l-39 -146l-683 183zM472 1058l609 -360l-77 -130l-609 360zM832 1389l398 -585l-124 -85l-399 584zM1285 1536 l121 -697l-149 -26l-121 697z" />
-<glyph unicode="&#xf16d;" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
-<glyph unicode="&#xf16e;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
-<glyph unicode="&#xf170;" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf171;" horiz-adv-x="1408" d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" />
-<glyph unicode="&#xf172;" d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5 t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf173;" horiz-adv-x="1024" d="M944 207l80 -237q-23 -35 -111 -66t-177 -32q-104 -2 -190.5 26t-142.5 74t-95 106t-55.5 120t-16.5 118v544h-168v215q72 26 129 69.5t91 90t58 102t34 99t15 88.5q1 5 4.5 8.5t7.5 3.5h244v-424h333v-252h-334v-518q0 -30 6.5 -56t22.5 -52.5t49.5 -41.5t81.5 -14 q78 2 134 29z" />
-<glyph unicode="&#xf174;" d="M1136 75l-62 183q-44 -22 -103 -22q-36 -1 -62 10.5t-38.5 31.5t-17.5 40.5t-5 43.5v398h257v194h-256v326h-188q-8 0 -9 -10q-5 -44 -17.5 -87t-39 -95t-77 -95t-118.5 -68v-165h130v-418q0 -57 21.5 -115t65 -111t121 -85.5t176.5 -30.5q69 1 136.5 25t85.5 50z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf175;" horiz-adv-x="768" d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" />
-<glyph unicode="&#xf176;" horiz-adv-x="768" d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" />
-<glyph unicode="&#xf177;" horiz-adv-x="1792" d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf178;" horiz-adv-x="1792" d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" />
-<glyph unicode="&#xf179;" horiz-adv-x="1408" d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q112 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65 q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" />
-<glyph unicode="&#xf17a;" horiz-adv-x="1664" d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" />
-<glyph unicode="&#xf17b;" horiz-adv-x="1408" d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30 t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5 h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" />
-<glyph unicode="&#xf17c;" d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-7 -10 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7 q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15 q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5 t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19 q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63 q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18l-4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92 q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152 q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-14 -1 -7 -7l4 -2 q14 -4 18 -31q0 -3 8 2zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5t-30 -18.5 t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43q-19 4 -51 9.5 t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49t-14 -48 q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54q110 143 124 195 q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5t-40.5 -33.5t-61 -14 q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5t15.5 47.5q1 -31 8 -56.5 t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" />
-<glyph unicode="&#xf17d;" d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81 t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19 q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -6 6.5 -17.5t7.5 -16.5q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6 t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf17e;" d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5 t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5 q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80 q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" />
-<glyph unicode="&#xf180;" horiz-adv-x="1280" d="M1000 1102l37 194q5 23 -9 40t-35 17h-712q-23 0 -38.5 -17t-15.5 -37v-1101q0 -7 6 -1l291 352q23 26 38 33.5t48 7.5h239q22 0 37 14.5t18 29.5q24 130 37 191q4 21 -11.5 40t-36.5 19h-294q-29 0 -48 19t-19 48v42q0 29 19 47.5t48 18.5h346q18 0 35 13.5t20 29.5z M1227 1324q-15 -73 -53.5 -266.5t-69.5 -350t-35 -173.5q-6 -22 -9 -32.5t-14 -32.5t-24.5 -33t-38.5 -21t-58 -10h-271q-13 0 -22 -10q-8 -9 -426 -494q-22 -25 -58.5 -28.5t-48.5 5.5q-55 22 -55 98v1410q0 55 38 102.5t120 47.5h888q95 0 127 -53t10 -159zM1227 1324 l-158 -790q4 17 35 173.5t69.5 350t53.5 266.5z" />
-<glyph unicode="&#xf181;" d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408 q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf182;" horiz-adv-x="1280" d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43 q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
-<glyph unicode="&#xf183;" horiz-adv-x="1024" d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
-<glyph unicode="&#xf184;" d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf185;" horiz-adv-x="1792" d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4 l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94 q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" />
-<glyph unicode="&#xf186;" d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61 t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" />
-<glyph unicode="&#xf187;" horiz-adv-x="1792" d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536 q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" />
-<glyph unicode="&#xf188;" horiz-adv-x="1664" d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207 q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19 t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" />
-<glyph unicode="&#xf189;" horiz-adv-x="1920" d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-78 -100 -90 -131q-17 -41 14 -81q17 -21 81 -82h1l1 -1l1 -1l2 -2q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58 t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6 q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q17 19 38 30q53 26 239 24 q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2 q39 5 64 -2.5t31 -16.5z" />
-<glyph unicode="&#xf18a;" horiz-adv-x="1792" d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12 q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422 q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178 q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" />
-<glyph unicode="&#xf18b;" d="M1133 -34q-171 -94 -368 -94q-196 0 -367 94q138 87 235.5 211t131.5 268q35 -144 132.5 -268t235.5 -211zM638 1394v-485q0 -252 -126.5 -459.5t-330.5 -306.5q-181 215 -181 495q0 187 83.5 349.5t229.5 269.5t325 137zM1536 638q0 -280 -181 -495 q-204 99 -330.5 306.5t-126.5 459.5v485q179 -30 325 -137t229.5 -269.5t83.5 -349.5z" />
-<glyph unicode="&#xf18c;" horiz-adv-x="1408" d="M1402 433q-32 -80 -76 -138t-91 -88.5t-99 -46.5t-101.5 -14.5t-96.5 8.5t-86.5 22t-69.5 27.5t-46 22.5l-17 10q-113 -228 -289.5 -359.5t-384.5 -132.5q-19 0 -32 13t-13 32t13 31.5t32 12.5q173 1 322.5 107.5t251.5 294.5q-36 -14 -72 -23t-83 -13t-91 2.5t-93 28.5 t-92 59t-84.5 100t-74.5 146q114 47 214 57t167.5 -7.5t124.5 -56.5t88.5 -77t56.5 -82q53 131 79 291q-7 -1 -18 -2.5t-46.5 -2.5t-69.5 0.5t-81.5 10t-88.5 23t-84 42.5t-75 65t-54.5 94.5t-28.5 127.5q70 28 133.5 36.5t112.5 -1t92 -30t73.5 -50t56 -61t42 -63t27.5 -56 t16 -39.5l4 -16q12 122 12 195q-8 6 -21.5 16t-49 44.5t-63.5 71.5t-54 93t-33 112.5t12 127t70 138.5q73 -25 127.5 -61.5t84.5 -76.5t48 -85t20.5 -89t-0.5 -85.5t-13 -76.5t-19 -62t-17 -42l-7 -15q1 -5 1 -50.5t-1 -71.5q3 7 10 18.5t30.5 43t50.5 58t71 55.5t91.5 44.5 t112 14.5t132.5 -24q-2 -78 -21.5 -141.5t-50 -104.5t-69.5 -71.5t-81.5 -45.5t-84.5 -24t-80 -9.5t-67.5 1t-46.5 4.5l-17 3q-23 -147 -73 -283q6 7 18 18.5t49.5 41t77.5 52.5t99.5 42t117.5 20t129 -23.5t137 -77.5z" />
-<glyph unicode="&#xf18d;" horiz-adv-x="1280" d="M1259 283v-66q0 -85 -57.5 -144.5t-138.5 -59.5h-57l-260 -269v269h-529q-81 0 -138.5 59.5t-57.5 144.5v66h1238zM1259 609v-255h-1238v255h1238zM1259 937v-255h-1238v255h1238zM1259 1077v-67h-1238v67q0 84 57.5 143.5t138.5 59.5h846q81 0 138.5 -59.5t57.5 -143.5z " />
-<glyph unicode="&#xf18e;" d="M1152 640q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192h-352q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h352v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf190;" d="M1152 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-352v-192q0 -14 -9 -23t-23 -9q-12 0 -24 10l-319 319q-9 9 -9 23t9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h352q13 0 22.5 -9.5t9.5 -22.5zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198 t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf191;" d="M1024 960v-640q0 -26 -19 -45t-45 -19q-20 0 -37 12l-448 320q-27 19 -27 52t27 52l448 320q17 12 37 12q26 0 45 -19t19 -45zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5z M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf192;" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5 t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf193;" horiz-adv-x="1664" d="M1023 349l102 -204q-58 -179 -210 -290t-339 -111q-156 0 -288.5 77.5t-210 210t-77.5 288.5q0 181 104.5 330t274.5 211l17 -131q-122 -54 -195 -165.5t-73 -244.5q0 -185 131.5 -316.5t316.5 -131.5q126 0 232.5 65t165 175.5t49.5 236.5zM1571 249l58 -114l-256 -128 q-13 -7 -29 -7q-40 0 -57 35l-239 477h-472q-24 0 -42.5 16.5t-21.5 40.5l-96 779q-2 16 6 42q14 51 57 82.5t97 31.5q66 0 113 -47t47 -113q0 -69 -52 -117.5t-120 -41.5l37 -289h423v-128h-407l16 -128h455q40 0 57 -35l228 -455z" />
-<glyph unicode="&#xf194;" d="M1254 899q16 85 -21 132q-52 65 -187 45q-17 -3 -41 -12.5t-57.5 -30.5t-64.5 -48.5t-59.5 -70t-44.5 -91.5q80 7 113.5 -16t26.5 -99q-5 -52 -52 -143q-43 -78 -71 -99q-44 -32 -87 14q-23 24 -37.5 64.5t-19 73t-10 84t-8.5 71.5q-23 129 -34 164q-12 37 -35.5 69 t-50.5 40q-57 16 -127 -25q-54 -32 -136.5 -106t-122.5 -102v-7q16 -8 25.5 -26t21.5 -20q21 -3 54.5 8.5t58 10.5t41.5 -30q11 -18 18.5 -38.5t15 -48t12.5 -40.5q17 -46 53 -187q36 -146 57 -197q42 -99 103 -125q43 -12 85 -1.5t76 31.5q131 77 250 237 q104 139 172.5 292.5t82.5 226.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf195;" horiz-adv-x="1152" d="M1152 704q0 -191 -94.5 -353t-256.5 -256.5t-353 -94.5h-160q-14 0 -23 9t-9 23v611l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v93l-215 -66q-3 -1 -9 -1q-10 0 -19 6q-13 10 -13 26v128q0 23 23 31l233 71v250q0 14 9 23t23 9h160 q14 0 23 -9t9 -23v-181l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-93l375 116q15 5 28 -5t13 -26v-128q0 -23 -23 -31l-393 -121v-487q188 13 318 151t130 328q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf196;" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-352v-352q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v352h-352q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h352v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-352h352q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832 q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf197;" horiz-adv-x="2176" d="M620 416q-110 -64 -268 -64h-128v64h-64q-13 0 -22.5 23.5t-9.5 56.5q0 24 7 49q-58 2 -96.5 10.5t-38.5 20.5t38.5 20.5t96.5 10.5q-7 25 -7 49q0 33 9.5 56.5t22.5 23.5h64v64h128q158 0 268 -64h1113q42 -7 106.5 -18t80.5 -14q89 -15 150 -40.5t83.5 -47.5t22.5 -40 t-22.5 -40t-83.5 -47.5t-150 -40.5q-16 -3 -80.5 -14t-106.5 -18h-1113zM1739 668q53 -36 53 -92t-53 -92l81 -30q68 48 68 122t-68 122zM625 400h1015q-217 -38 -456 -80q-57 0 -113 -24t-83 -48l-28 -24l-288 -288q-26 -26 -70.5 -45t-89.5 -19h-96l-93 464h29 q157 0 273 64zM352 816h-29l93 464h96q46 0 90 -19t70 -45l288 -288q4 -4 11 -10.5t30.5 -23t48.5 -29t61.5 -23t72.5 -10.5l456 -80h-1015q-116 64 -273 64z" />
-<glyph unicode="&#xf198;" horiz-adv-x="1664" d="M1519 760q62 0 103.5 -40.5t41.5 -101.5q0 -97 -93 -130l-172 -59l56 -167q7 -21 7 -47q0 -59 -42 -102t-101 -43q-47 0 -85.5 27t-53.5 72l-55 165l-310 -106l55 -164q8 -24 8 -47q0 -59 -42 -102t-102 -43q-47 0 -85 27t-53 72l-55 163l-153 -53q-29 -9 -50 -9 q-61 0 -101.5 40t-40.5 101q0 47 27.5 85t71.5 53l156 53l-105 313l-156 -54q-26 -8 -48 -8q-60 0 -101 40.5t-41 100.5q0 47 27.5 85t71.5 53l157 53l-53 159q-8 24 -8 47q0 60 42 102.5t102 42.5q47 0 85 -27t53 -72l54 -160l310 105l-54 160q-8 24 -8 47q0 59 42.5 102 t101.5 43q47 0 85.5 -27.5t53.5 -71.5l53 -161l162 55q21 6 43 6q60 0 102.5 -39.5t42.5 -98.5q0 -45 -30 -81.5t-74 -51.5l-157 -54l105 -316l164 56q24 8 46 8zM725 498l310 105l-105 315l-310 -107z" />
-<glyph unicode="&#xf199;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM1280 352v436q-31 -35 -64 -55q-34 -22 -132.5 -85t-151.5 -99q-98 -69 -164 -69v0v0q-66 0 -164 69 q-46 32 -141.5 92.5t-142.5 92.5q-12 8 -33 27t-31 27v-436q0 -40 28 -68t68 -28h832q40 0 68 28t28 68zM1280 925q0 41 -27.5 70t-68.5 29h-832q-40 0 -68 -28t-28 -68q0 -37 30.5 -76.5t67.5 -64.5q47 -32 137.5 -89t129.5 -83q3 -2 17 -11.5t21 -14t21 -13t23.5 -13 t21.5 -9.5t22.5 -7.5t20.5 -2.5t20.5 2.5t22.5 7.5t21.5 9.5t23.5 13t21 13t21 14t17 11.5l267 174q35 23 66.5 62.5t31.5 73.5z" />
-<glyph unicode="&#xf19a;" horiz-adv-x="1792" d="M127 640q0 163 67 313l367 -1005q-196 95 -315 281t-119 411zM1415 679q0 -19 -2.5 -38.5t-10 -49.5t-11.5 -44t-17.5 -59t-17.5 -58l-76 -256l-278 826q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-75 1 -202 10q-12 1 -20.5 -5t-11.5 -15t-1.5 -18.5t9 -16.5 t19.5 -8l80 -8l120 -328l-168 -504l-280 832q46 3 88 8q19 2 26 18.5t-2.5 31t-28.5 13.5l-205 -10q-7 0 -23 0.5t-26 0.5q105 160 274.5 253.5t367.5 93.5q147 0 280.5 -53t238.5 -149h-10q-55 0 -92 -40.5t-37 -95.5q0 -12 2 -24t4 -21.5t8 -23t9 -21t12 -22.5t12.5 -21 t14.5 -24t14 -23q63 -107 63 -212zM909 573l237 -647q1 -6 5 -11q-126 -44 -255 -44q-112 0 -217 32zM1570 1009q95 -174 95 -369q0 -209 -104 -385.5t-279 -278.5l235 678q59 169 59 276q0 42 -6 79zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286 t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 -215q173 0 331.5 68t273 182.5t182.5 273t68 331.5t-68 331.5t-182.5 273t-273 182.5t-331.5 68t-331.5 -68t-273 -182.5t-182.5 -273t-68 -331.5t68 -331.5t182.5 -273 t273 -182.5t331.5 -68z" />
-<glyph unicode="&#xf19b;" horiz-adv-x="1792" d="M1086 1536v-1536l-272 -128q-228 20 -414 102t-293 208.5t-107 272.5q0 140 100.5 263.5t275 205.5t391.5 108v-172q-217 -38 -356.5 -150t-139.5 -255q0 -152 154.5 -267t388.5 -145v1360zM1755 954l37 -390l-525 114l147 83q-119 70 -280 99v172q277 -33 481 -157z" />
-<glyph unicode="&#xf19c;" horiz-adv-x="2048" d="M960 1536l960 -384v-128h-128q0 -26 -20.5 -45t-48.5 -19h-1526q-28 0 -48.5 19t-20.5 45h-128v128zM256 896h256v-768h128v768h256v-768h128v768h256v-768h128v768h256v-768h59q28 0 48.5 -19t20.5 -45v-64h-1664v64q0 26 20.5 45t48.5 19h59v768zM1851 -64 q28 0 48.5 -19t20.5 -45v-128h-1920v128q0 26 20.5 45t48.5 19h1782z" />
-<glyph unicode="&#xf19d;" horiz-adv-x="2304" d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433 q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" />
-<glyph unicode="&#xf19e;" d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q43 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0 q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" />
-<glyph unicode="&#xf1a0;" horiz-adv-x="1280" d="M981 197q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -49 2q-53 0 -104.5 -7t-107 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -56 23.5 -102t61 -75.5t87 -50t100 -29t101.5 -8.5q58 0 111.5 13t99 39t73 73t27.5 109zM864 1055 q0 59 -17 125.5t-48 129t-84 103.5t-117 41q-42 0 -82.5 -19.5t-66.5 -52.5q-46 -59 -46 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26q37 0 77.5 16.5t65.5 43.5q53 56 53 159zM752 1536h417l-137 -88h-132q75 -63 113 -133t38 -160q0 -72 -24.5 -129.5 t-59.5 -93t-69.5 -65t-59 -61.5t-24.5 -66q0 -36 32 -70.5t77 -68t90.5 -73.5t77.5 -104t32 -142q0 -91 -49 -173q-71 -122 -209.5 -179.5t-298.5 -57.5q-132 0 -246.5 41.5t-172.5 137.5q-36 59 -36 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 41 -47.5 73.5 t-15.5 73.5q0 40 21 85q-46 -4 -68 -4q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q76 66 182 98t218 32z" />
-<glyph unicode="&#xf1a1;" horiz-adv-x="1984" d="M831 572q0 -56 -40.5 -96t-96.5 -40q-57 0 -98 40t-41 96q0 57 41.5 98t97.5 41t96.5 -41t40.5 -98zM1292 711q56 0 96.5 -41t40.5 -98q0 -56 -40.5 -96t-96.5 -40q-57 0 -98 40t-41 96q0 57 41.5 98t97.5 41zM1984 722q0 -62 -31 -114t-83 -82q5 -33 5 -61 q0 -121 -68.5 -230.5t-197.5 -193.5q-125 -82 -285.5 -125.5t-335.5 -43.5q-176 0 -336.5 43.5t-284.5 125.5q-129 84 -197.5 193t-68.5 231q0 29 5 66q-48 31 -77 81.5t-29 109.5q0 94 66 160t160 66q83 0 148 -55q248 158 592 164l134 423q4 14 17.5 21.5t28.5 4.5 l347 -82q22 50 68.5 81t102.5 31q77 0 131.5 -54.5t54.5 -131.5t-54.5 -132t-131.5 -55q-76 0 -130.5 54t-55.5 131l-315 74l-116 -366q327 -14 560 -166q64 58 151 58q94 0 160 -66t66 -160zM1664 1459q-45 0 -77 -32t-32 -77t32 -77t77 -32t77 32t32 77t-32 77t-77 32z M77 722q0 -67 51 -111q49 131 180 235q-36 25 -82 25q-62 0 -105.5 -43.5t-43.5 -105.5zM1567 105q112 73 171.5 166t59.5 194t-59.5 193.5t-171.5 165.5q-116 75 -265.5 115.5t-313.5 40.5t-313.5 -40.5t-265.5 -115.5q-112 -73 -171.5 -165.5t-59.5 -193.5t59.5 -194 t171.5 -166q116 -75 265.5 -115.5t313.5 -40.5t313.5 40.5t265.5 115.5zM1850 605q57 46 57 117q0 62 -43.5 105.5t-105.5 43.5q-49 0 -86 -28q131 -105 178 -238zM1258 237q11 11 27 11t27 -11t11 -27.5t-11 -27.5q-99 -99 -319 -99h-2q-220 0 -319 99q-11 11 -11 27.5 t11 27.5t27 11t27 -11q77 -77 265 -77h2q188 0 265 77z" />
-<glyph unicode="&#xf1a2;" d="M950 393q7 7 17.5 7t17.5 -7t7 -18t-7 -18q-65 -64 -208 -64h-1h-1q-143 0 -207 64q-8 7 -8 18t8 18q7 7 17.5 7t17.5 -7q49 -51 172 -51h1h1q122 0 173 51zM671 613q0 -37 -26 -64t-63 -27t-63 27t-26 64t26 63t63 26t63 -26t26 -63zM1214 1049q-29 0 -50 21t-21 50 q0 30 21 51t50 21q30 0 51 -21t21 -51q0 -29 -21 -50t-51 -21zM1216 1408q132 0 226 -94t94 -227v-894q0 -133 -94 -227t-226 -94h-896q-132 0 -226 94t-94 227v894q0 133 94 227t226 94h896zM1321 596q35 14 57 45.5t22 70.5q0 51 -36 87.5t-87 36.5q-60 0 -98 -48 q-151 107 -375 115l83 265l206 -49q1 -50 36.5 -85t84.5 -35q50 0 86 35.5t36 85.5t-36 86t-86 36q-36 0 -66 -20.5t-45 -53.5l-227 54q-9 2 -17.5 -2.5t-11.5 -14.5l-95 -302q-224 -4 -381 -113q-36 43 -93 43q-51 0 -87 -36.5t-36 -87.5q0 -37 19.5 -67.5t52.5 -45.5 q-7 -25 -7 -54q0 -98 74 -181.5t201.5 -132t278.5 -48.5q150 0 277.5 48.5t201.5 132t74 181.5q0 27 -6 54zM971 702q37 0 63 -26t26 -63t-26 -64t-63 -27t-63 27t-26 64t26 63t63 26z" />
-<glyph unicode="&#xf1a3;" d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150 v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103 t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf1a4;" horiz-adv-x="1920" d="M1062 824v118q0 42 -30 72t-72 30t-72 -30t-30 -72v-612q0 -175 -126 -299t-303 -124q-178 0 -303.5 125.5t-125.5 303.5v266h328v-262q0 -43 30 -72.5t72 -29.5t72 29.5t30 72.5v620q0 171 126.5 292t301.5 121q176 0 302 -122t126 -294v-136l-195 -58zM1592 602h328 v-266q0 -178 -125.5 -303.5t-303.5 -125.5q-177 0 -303 124.5t-126 300.5v268l131 -61l195 58v-270q0 -42 30 -71.5t72 -29.5t72 29.5t30 71.5v275z" />
-<glyph unicode="&#xf1a5;" d="M1472 160v480h-704v704h-480q-93 0 -158.5 -65.5t-65.5 -158.5v-480h704v-704h480q93 0 158.5 65.5t65.5 158.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
-<glyph unicode="&#xf1a6;" horiz-adv-x="2048" d="M328 1254h204v-983h-532v697h328v286zM328 435v369h-123v-369h123zM614 968v-697h205v697h-205zM614 1254v-204h205v204h-205zM901 968h533v-942h-533v163h328v82h-328v697zM1229 435v369h-123v-369h123zM1516 968h532v-942h-532v163h327v82h-327v697zM1843 435v369h-123 v-369h123z" />
-<glyph unicode="&#xf1a7;" d="M1046 516q0 -64 -38 -109t-91 -45q-43 0 -70 15v277q28 17 70 17q53 0 91 -45.5t38 -109.5zM703 944q0 -64 -38 -109.5t-91 -45.5q-43 0 -70 15v277q28 17 70 17q53 0 91 -45t38 -109zM1265 513q0 134 -88 229t-213 95q-20 0 -39 -3q-23 -78 -78 -136q-87 -95 -211 -101 v-636l211 41v206q51 -19 117 -19q125 0 213 95t88 229zM922 940q0 134 -88.5 229t-213.5 95q-74 0 -141 -36h-186v-840l211 41v206q55 -19 116 -19q125 0 213.5 95t88.5 229zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf1a8;" horiz-adv-x="2038" d="M1222 607q75 3 143.5 -20.5t118 -58.5t101 -94.5t84 -108t75.5 -120.5q33 -56 78.5 -109t75.5 -80.5t99 -88.5q-48 -30 -108.5 -57.5t-138.5 -59t-114 -47.5q-44 37 -74 115t-43.5 164.5t-33 180.5t-42.5 168.5t-72.5 123t-122.5 48.5l-10 -2l-6 -4q4 -5 13 -14 q6 -5 28 -23.5t25.5 -22t19 -18t18 -20.5t11.5 -21t10.5 -27.5t4.5 -31t4 -40.5l1 -33q1 -26 -2.5 -57.5t-7.5 -52t-12.5 -58.5t-11.5 -53q-35 1 -101 -9.5t-98 -10.5q-39 0 -72 10q-2 16 -2 47q0 74 3 96q2 13 31.5 41.5t57 59t26.5 51.5q-24 2 -43 -24 q-36 -53 -111.5 -99.5t-136.5 -46.5q-25 0 -75.5 63t-106.5 139.5t-84 96.5q-6 4 -27 30q-482 -112 -513 -112q-16 0 -28 11t-12 27q0 15 8.5 26.5t22.5 14.5l486 106q-8 14 -8 25t5.5 17.5t16 11.5t20 7t23 4.5t18.5 4.5q4 1 15.5 7.5t17.5 6.5q15 0 28 -16t20 -33 q163 37 172 37q17 0 29.5 -11t12.5 -28q0 -15 -8.5 -26t-23.5 -14l-182 -40l-1 -16q-1 -26 81.5 -117.5t104.5 -91.5q47 0 119 80t72 129q0 36 -23.5 53t-51 18.5t-51 11.5t-23.5 34q0 16 10 34l-68 19q43 44 43 117q0 26 -5 58q82 16 144 16q44 0 71.5 -1.5t48.5 -8.5 t31 -13.5t20.5 -24.5t15.5 -33.5t17 -47.5t24 -60l50 25q-3 -40 -23 -60t-42.5 -21t-40 -6.5t-16.5 -20.5zM1282 842q-5 5 -13.5 15.5t-12 14.5t-10.5 11.5t-10 10.5l-8 8t-8.5 7.5t-8 5t-8.5 4.5q-7 3 -14.5 5t-20.5 2.5t-22 0.5h-32.5h-37.5q-126 0 -217 -43 q16 30 36 46.5t54 29.5t65.5 36t46 36.5t50 55t43.5 50.5q12 -9 28 -31.5t32 -36.5t38 -13l12 1v-76l22 -1q247 95 371 190q28 21 50 39t42.5 37.5t33 31t29.5 34t24 31t24.5 37t23 38t27 47.5t29.5 53l7 9q-2 -53 -43 -139q-79 -165 -205 -264t-306 -142q-14 -3 -42 -7.5 t-50 -9.5t-39 -14q3 -19 24.5 -46t21.5 -34q0 -11 -26 -30zM1061 -79q39 26 131.5 47.5t146.5 21.5q9 0 22.5 -15.5t28 -42.5t26 -50t24 -51t14.5 -33q-121 -45 -244 -45q-61 0 -125 11zM822 568l48 12l109 -177l-73 -48zM1323 51q3 -15 3 -16q0 -7 -17.5 -14.5t-46 -13 t-54 -9.5t-53.5 -7.5t-32 -4.5l-7 43q21 2 60.5 8.5t72 10t60.5 3.5h14zM866 679l-96 -20l-6 17q10 1 32.5 7t34.5 6q19 0 35 -10zM1061 45h31l10 -83l-41 -12v95zM1950 1535v1v-1zM1950 1535l-1 -5l-2 -2l1 3zM1950 1535l1 1z" />
-<glyph unicode="&#xf1a9;" d="M1167 -50q-5 19 -24 5q-30 -22 -87 -39t-131 -17q-129 0 -193 49q-5 4 -13 4q-11 0 -26 -12q-7 -6 -7.5 -16t7.5 -20q34 -32 87.5 -46t102.5 -12.5t99 4.5q41 4 84.5 20.5t65 30t28.5 20.5q12 12 7 29zM1128 65q-19 47 -39 61q-23 15 -76 15q-47 0 -71 -10 q-29 -12 -78 -56q-26 -24 -12 -44q9 -8 17.5 -4.5t31.5 23.5q3 2 10.5 8.5t10.5 8.5t10 7t11.5 7t12.5 5t15 4.5t16.5 2.5t20.5 1q27 0 44.5 -7.5t23 -14.5t13.5 -22q10 -17 12.5 -20t12.5 1q23 12 14 34zM1483 346q0 22 -5 44.5t-16.5 45t-34 36.5t-52.5 14 q-33 0 -97 -41.5t-129 -83.5t-101 -42q-27 -1 -63.5 19t-76 49t-83.5 58t-100 49t-111 19q-115 -1 -197 -78.5t-84 -178.5q-2 -112 74 -164q29 -20 62.5 -28.5t103.5 -8.5q57 0 132 32.5t134 71t120 70.5t93 31q26 -1 65 -31.5t71.5 -67t68 -67.5t55.5 -32q35 -3 58.5 14 t55.5 63q28 41 42.5 101t14.5 106zM1536 506q0 -164 -62 -304.5t-166 -236t-242.5 -149.5t-290.5 -54t-293 57.5t-247.5 157t-170.5 241.5t-64 302q0 89 19.5 172.5t49 145.5t70.5 118.5t78.5 94t78.5 69.5t64.5 46.5t42.5 24.5q14 8 51 26.5t54.5 28.5t48 30t60.5 44 q36 28 58 72.5t30 125.5q129 -155 186 -193q44 -29 130 -68t129 -66q21 -13 39 -25t60.5 -46.5t76 -70.5t75 -95t69 -122t47 -148.5t19.5 -177.5z" />
-<glyph unicode="&#xf1aa;" d="M1070 463l-160 -160l-151 -152l-30 -30q-65 -64 -151.5 -87t-171.5 -2q-16 -70 -72 -115t-129 -45q-85 0 -145 60.5t-60 145.5q0 72 44.5 128t113.5 72q-22 86 1 173t88 152l12 12l151 -152l-11 -11q-37 -37 -37 -89t37 -90q37 -37 89 -37t89 37l30 30l151 152l161 160z M729 1145l12 -12l-152 -152l-12 12q-37 37 -89 37t-89 -37t-37 -89.5t37 -89.5l29 -29l152 -152l160 -160l-151 -152l-161 160l-151 152l-30 30q-68 67 -90 159.5t5 179.5q-70 15 -115 71t-45 129q0 85 60 145.5t145 60.5q76 0 133.5 -49t69.5 -123q84 20 169.5 -3.5 t149.5 -87.5zM1536 78q0 -85 -60 -145.5t-145 -60.5q-74 0 -131 47t-71 118q-86 -28 -179.5 -6t-161.5 90l-11 12l151 152l12 -12q37 -37 89 -37t89 37t37 89t-37 89l-30 30l-152 152l-160 160l152 152l160 -160l152 -152l29 -30q64 -64 87.5 -150.5t2.5 -171.5 q76 -11 126.5 -68.5t50.5 -134.5zM1534 1202q0 -77 -51 -135t-127 -69q26 -85 3 -176.5t-90 -158.5l-12 -12l-151 152l12 12q37 37 37 89t-37 89t-89 37t-89 -37l-30 -30l-152 -152l-160 -160l-152 152l161 160l152 152l29 30q67 67 159 89.5t178 -3.5q11 75 68.5 126 t135.5 51q85 0 145 -60.5t60 -145.5z" />
-<glyph unicode="&#xf1ab;" d="M654 458q-1 -3 -12.5 0.5t-31.5 11.5l-20 9q-44 20 -87 49q-7 5 -41 31.5t-38 28.5q-67 -103 -134 -181q-81 -95 -105 -110q-4 -2 -19.5 -4t-18.5 0q6 4 82 92q21 24 85.5 115t78.5 118q17 30 51 98.5t36 77.5q-8 1 -110 -33q-8 -2 -27.5 -7.5t-34.5 -9.5t-17 -5 q-2 -2 -2 -10.5t-1 -9.5q-5 -10 -31 -15q-23 -7 -47 0q-18 4 -28 21q-4 6 -5 23q6 2 24.5 5t29.5 6q58 16 105 32q100 35 102 35q10 2 43 19.5t44 21.5q9 3 21.5 8t14.5 5.5t6 -0.5q2 -12 -1 -33q0 -2 -12.5 -27t-26.5 -53.5t-17 -33.5q-25 -50 -77 -131l64 -28 q12 -6 74.5 -32t67.5 -28q4 -1 10.5 -25.5t4.5 -30.5zM449 944q3 -15 -4 -28q-12 -23 -50 -38q-30 -12 -60 -12q-26 3 -49 26q-14 15 -18 41l1 3q3 -3 19.5 -5t26.5 0t58 16q36 12 55 14q17 0 21 -17zM1147 815l63 -227l-139 42zM39 15l694 232v1032l-694 -233v-1031z M1280 332l102 -31l-181 657l-100 31l-216 -536l102 -31l45 110l211 -65zM777 1294l573 -184v380zM1088 -29l158 -13l-54 -160l-40 66q-130 -83 -276 -108q-58 -12 -91 -12h-84q-79 0 -199.5 39t-183.5 85q-8 7 -8 16q0 8 5 13.5t13 5.5q4 0 18 -7.5t30.5 -16.5t20.5 -11 q73 -37 159.5 -61.5t157.5 -24.5q95 0 167 14.5t157 50.5q15 7 30.5 15.5t34 19t28.5 16.5zM1536 1050v-1079l-774 246q-14 -6 -375 -127.5t-368 -121.5q-13 0 -18 13q0 1 -1 3v1078q3 9 4 10q5 6 20 11q106 35 149 50v384l558 -198q2 0 160.5 55t316 108.5t161.5 53.5 q20 0 20 -21v-418z" />
-<glyph unicode="&#xf1ac;" horiz-adv-x="1792" d="M288 1152q66 0 113 -47t47 -113v-1088q0 -66 -47 -113t-113 -47h-128q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h128zM1664 989q58 -34 93 -93t35 -128v-768q0 -106 -75 -181t-181 -75h-864q-66 0 -113 47t-47 113v1536q0 40 28 68t68 28h672q40 0 88 -20t76 -48 l152 -152q28 -28 48 -76t20 -88v-163zM928 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM928 512v128q0 14 -9 23 t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1184 256v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128 q14 0 23 9t9 23zM1184 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 0v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 256v128q0 14 -9 23t-23 9h-128 q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1440 512v128q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h128q14 0 23 9t9 23zM1536 896v256h-160q-40 0 -68 28t-28 68v160h-640v-512h896z" />
-<glyph unicode="&#xf1ad;" d="M1344 1536q26 0 45 -19t19 -45v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280zM512 1248v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 992v-64q0 -14 9 -23t23 -9h64q14 0 23 9 t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 736v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM512 480v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 160v64 q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 q14 0 23 9t9 23zM384 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM384 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 -96v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9 t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM896 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 928v64 q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM896 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 160v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64 q14 0 23 9t9 23zM1152 416v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 672v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 928v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9 t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1152 1184v64q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h64q14 0 23 9t9 23z" />
-<glyph unicode="&#xf1ae;" horiz-adv-x="1280" d="M1188 988l-292 -292v-824q0 -46 -33 -79t-79 -33t-79 33t-33 79v384h-64v-384q0 -46 -33 -79t-79 -33t-79 33t-33 79v824l-292 292q-28 28 -28 68t28 68t68 28t68 -28l228 -228h368l228 228q28 28 68 28t68 -28t28 -68t-28 -68zM864 1152q0 -93 -65.5 -158.5 t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
-<glyph unicode="&#xf1b0;" horiz-adv-x="1664" d="M780 1064q0 -60 -19 -113.5t-63 -92.5t-105 -39q-76 0 -138 57.5t-92 135.5t-30 151q0 60 19 113.5t63 92.5t105 39q77 0 138.5 -57.5t91.5 -135t30 -151.5zM438 581q0 -80 -42 -139t-119 -59q-76 0 -141.5 55.5t-100.5 133.5t-35 152q0 80 42 139.5t119 59.5 q76 0 141.5 -55.5t100.5 -134t35 -152.5zM832 608q118 0 255 -97.5t229 -237t92 -254.5q0 -46 -17 -76.5t-48.5 -45t-64.5 -20t-76 -5.5q-68 0 -187.5 45t-182.5 45q-66 0 -192.5 -44.5t-200.5 -44.5q-183 0 -183 146q0 86 56 191.5t139.5 192.5t187.5 146t193 59zM1071 819 q-61 0 -105 39t-63 92.5t-19 113.5q0 74 30 151.5t91.5 135t138.5 57.5q61 0 105 -39t63 -92.5t19 -113.5q0 -73 -30 -151t-92 -135.5t-138 -57.5zM1503 923q77 0 119 -59.5t42 -139.5q0 -74 -35 -152t-100.5 -133.5t-141.5 -55.5q-77 0 -119 59t-42 139q0 74 35 152.5 t100.5 134t141.5 55.5z" />
-<glyph unicode="&#xf1b1;" horiz-adv-x="768" d="M704 1008q0 -145 -57 -243.5t-152 -135.5l45 -821q2 -26 -16 -45t-44 -19h-192q-26 0 -44 19t-16 45l45 821q-95 37 -152 135.5t-57 243.5q0 128 42.5 249.5t117.5 200t160 78.5t160 -78.5t117.5 -200t42.5 -249.5z" />
-<glyph unicode="&#xf1b2;" horiz-adv-x="1792" d="M896 -93l640 349v636l-640 -233v-752zM832 772l698 254l-698 254l-698 -254zM1664 1024v-768q0 -35 -18 -65t-49 -47l-704 -384q-28 -16 -61 -16t-61 16l-704 384q-31 17 -49 47t-18 65v768q0 40 23 73t61 47l704 256q22 8 44 8t44 -8l704 -256q38 -14 61 -47t23 -73z " />
-<glyph unicode="&#xf1b3;" horiz-adv-x="2304" d="M640 -96l384 192v314l-384 -164v-342zM576 358l404 173l-404 173l-404 -173zM1664 -96l384 192v314l-384 -164v-342zM1600 358l404 173l-404 173l-404 -173zM1152 651l384 165v266l-384 -164v-267zM1088 1030l441 189l-441 189l-441 -189zM2176 512v-416q0 -36 -19 -67 t-52 -47l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-5 2 -7 4q-2 -2 -7 -4l-448 -224q-25 -14 -57 -14t-57 14l-448 224q-33 16 -52 47t-19 67v416q0 38 21.5 70t56.5 48l434 186v400q0 38 21.5 70t56.5 48l448 192q23 10 50 10t50 -10l448 -192q35 -16 56.5 -48t21.5 -70 v-400l434 -186q36 -16 57 -48t21 -70z" />
-<glyph unicode="&#xf1b4;" horiz-adv-x="2048" d="M1848 1197h-511v-124h511v124zM1596 771q-90 0 -146 -52.5t-62 -142.5h408q-18 195 -200 195zM1612 186q63 0 122 32t76 87h221q-100 -307 -427 -307q-214 0 -340.5 132t-126.5 347q0 208 130.5 345.5t336.5 137.5q138 0 240.5 -68t153 -179t50.5 -248q0 -17 -2 -47h-658 q0 -111 57.5 -171.5t166.5 -60.5zM277 236h296q205 0 205 167q0 180 -199 180h-302v-347zM277 773h281q78 0 123.5 36.5t45.5 113.5q0 144 -190 144h-260v-294zM0 1282h594q87 0 155 -14t126.5 -47.5t90 -96.5t31.5 -154q0 -181 -172 -263q114 -32 172 -115t58 -204 q0 -75 -24.5 -136.5t-66 -103.5t-98.5 -71t-121 -42t-134 -13h-611v1260z" />
-<glyph unicode="&#xf1b5;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM499 1041h-371v-787h382q117 0 197 57.5t80 170.5q0 158 -143 200q107 52 107 164q0 57 -19.5 96.5 t-56.5 60.5t-79 29.5t-97 8.5zM477 723h-176v184h163q119 0 119 -90q0 -94 -106 -94zM486 388h-185v217h189q124 0 124 -113q0 -104 -128 -104zM1136 356q-68 0 -104 38t-36 107h411q1 10 1 30q0 132 -74.5 220.5t-203.5 88.5q-128 0 -210 -86t-82 -216q0 -135 79 -217 t213 -82q205 0 267 191h-138q-11 -34 -47.5 -54t-75.5 -20zM1126 722q113 0 124 -122h-254q4 56 39 89t91 33zM964 988h319v-77h-319v77z" />
-<glyph unicode="&#xf1b6;" horiz-adv-x="1792" d="M1582 954q0 -101 -71.5 -172.5t-172.5 -71.5t-172.5 71.5t-71.5 172.5t71.5 172.5t172.5 71.5t172.5 -71.5t71.5 -172.5zM812 212q0 104 -73 177t-177 73q-27 0 -54 -6l104 -42q77 -31 109.5 -106.5t1.5 -151.5q-31 -77 -107 -109t-152 -1q-21 8 -62 24.5t-61 24.5 q32 -60 91 -96.5t130 -36.5q104 0 177 73t73 177zM1642 953q0 126 -89.5 215.5t-215.5 89.5q-127 0 -216.5 -89.5t-89.5 -215.5q0 -127 89.5 -216t216.5 -89q126 0 215.5 89t89.5 216zM1792 953q0 -189 -133.5 -322t-321.5 -133l-437 -319q-12 -129 -109 -218t-229 -89 q-121 0 -214 76t-118 192l-230 92v429l389 -157q79 48 173 48q13 0 35 -2l284 407q2 187 135.5 319t320.5 132q188 0 321.5 -133.5t133.5 -321.5z" />
-<glyph unicode="&#xf1b7;" d="M1242 889q0 80 -57 136.5t-137 56.5t-136.5 -57t-56.5 -136q0 -80 56.5 -136.5t136.5 -56.5t137 56.5t57 136.5zM632 301q0 -83 -58 -140.5t-140 -57.5q-56 0 -103 29t-72 77q52 -20 98 -40q60 -24 120 1.5t85 86.5q24 60 -1.5 120t-86.5 84l-82 33q22 5 42 5 q82 0 140 -57.5t58 -140.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v153l172 -69q20 -92 93.5 -152t168.5 -60q104 0 181 70t87 173l345 252q150 0 255.5 105.5t105.5 254.5q0 150 -105.5 255.5t-255.5 105.5 q-148 0 -253 -104.5t-107 -252.5l-225 -322q-9 1 -28 1q-75 0 -137 -37l-297 119v468q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5zM1289 887q0 -100 -71 -170.5t-171 -70.5t-170.5 70.5t-70.5 170.5t70.5 171t170.5 71q101 0 171.5 -70.5t70.5 -171.5z " />
-<glyph unicode="&#xf1b8;" horiz-adv-x="1792" d="M836 367l-15 -368l-2 -22l-420 29q-36 3 -67 31.5t-47 65.5q-11 27 -14.5 55t4 65t12 55t21.5 64t19 53q78 -12 509 -28zM449 953l180 -379l-147 92q-63 -72 -111.5 -144.5t-72.5 -125t-39.5 -94.5t-18.5 -63l-4 -21l-190 357q-17 26 -18 56t6 47l8 18q35 63 114 188 l-140 86zM1680 436l-188 -359q-12 -29 -36.5 -46.5t-43.5 -20.5l-18 -4q-71 -7 -219 -12l8 -164l-230 367l211 362l7 -173q170 -16 283 -5t170 33zM895 1360q-47 -63 -265 -435l-317 187l-19 12l225 356q20 31 60 45t80 10q24 -2 48.5 -12t42 -21t41.5 -33t36 -34.5 t36 -39.5t32 -35zM1550 1053l212 -363q18 -37 12.5 -76t-27.5 -74q-13 -20 -33 -37t-38 -28t-48.5 -22t-47 -16t-51.5 -14t-46 -12q-34 72 -265 436l313 195zM1407 1279l142 83l-220 -373l-419 20l151 86q-34 89 -75 166t-75.5 123.5t-64.5 80t-47 46.5l-17 13l405 -1 q31 3 58 -10.5t39 -28.5l11 -15q39 -61 112 -190z" />
-<glyph unicode="&#xf1b9;" horiz-adv-x="2048" d="M480 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM516 768h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5zM1888 448q0 66 -47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47t113 47t47 113zM2048 544v-384 q0 -14 -9 -23t-23 -9h-96v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-1024v-128q0 -80 -56 -136t-136 -56t-136 56t-56 136v128h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5t179 63.5h768q98 0 179 -63.5t104 -157.5 l105 -419h28q93 0 158.5 -65.5t65.5 -158.5z" />
-<glyph unicode="&#xf1ba;" horiz-adv-x="2048" d="M1824 640q93 0 158.5 -65.5t65.5 -158.5v-384q0 -14 -9 -23t-23 -9h-96v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-1024v-64q0 -80 -56 -136t-136 -56t-136 56t-56 136v64h-96q-14 0 -23 9t-9 23v384q0 93 65.5 158.5t158.5 65.5h28l105 419q23 94 104 157.5 t179 63.5h128v224q0 14 9 23t23 9h448q14 0 23 -9t9 -23v-224h128q98 0 179 -63.5t104 -157.5l105 -419h28zM320 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM516 640h1016l-89 357q-2 8 -14 17.5t-21 9.5h-768q-9 0 -21 -9.5t-14 -17.5z M1728 160q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47z" />
-<glyph unicode="&#xf1bb;" d="M1504 64q0 -26 -19 -45t-45 -19h-462q1 -17 6 -87.5t5 -108.5q0 -25 -18 -42.5t-43 -17.5h-320q-25 0 -43 17.5t-18 42.5q0 38 5 108.5t6 87.5h-462q-26 0 -45 19t-19 45t19 45l402 403h-229q-26 0 -45 19t-19 45t19 45l402 403h-197q-26 0 -45 19t-19 45t19 45l384 384 q19 19 45 19t45 -19l384 -384q19 -19 19 -45t-19 -45t-45 -19h-197l402 -403q19 -19 19 -45t-19 -45t-45 -19h-229l402 -403q19 -19 19 -45z" />
-<glyph unicode="&#xf1bc;" d="M1127 326q0 32 -30 51q-193 115 -447 115q-133 0 -287 -34q-42 -9 -42 -52q0 -20 13.5 -34.5t35.5 -14.5q5 0 37 8q132 27 243 27q226 0 397 -103q19 -11 33 -11q19 0 33 13.5t14 34.5zM1223 541q0 40 -35 61q-237 141 -548 141q-153 0 -303 -42q-48 -13 -48 -64 q0 -25 17.5 -42.5t42.5 -17.5q7 0 37 8q122 33 251 33q279 0 488 -124q24 -13 38 -13q25 0 42.5 17.5t17.5 42.5zM1331 789q0 47 -40 70q-126 73 -293 110.5t-343 37.5q-204 0 -364 -47q-23 -7 -38.5 -25.5t-15.5 -48.5q0 -31 20.5 -52t51.5 -21q11 0 40 8q133 37 307 37 q159 0 309.5 -34t253.5 -95q21 -12 40 -12q29 0 50.5 20.5t21.5 51.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf1bd;" d="M1397 1408q58 0 98.5 -40.5t40.5 -98.5v-1258q0 -58 -40.5 -98.5t-98.5 -40.5h-1258q-58 0 -98.5 40.5t-40.5 98.5v1258q0 58 40.5 98.5t98.5 40.5h1258zM1465 11v1258q0 28 -20 48t-48 20h-1258q-28 0 -48 -20t-20 -48v-1258q0 -28 20 -48t48 -20h1258q28 0 48 20t20 48 zM694 749l188 -387l533 145v-496q0 -7 -5.5 -12.5t-12.5 -5.5h-1258q-7 0 -12.5 5.5t-5.5 12.5v141l711 195l-212 439q4 1 12 2.5t12 1.5q170 32 303.5 21.5t221 -46t143.5 -94.5q27 -28 -25 -42q-64 -16 -256 -62l-97 198q-111 7 -240 -16zM1397 1287q7 0 12.5 -5.5 t5.5 -12.5v-428q-85 30 -188 52q-294 64 -645 12l-18 -3l-65 134h-233l85 -190q-132 -51 -230 -137v560q0 7 5.5 12.5t12.5 5.5h1258zM286 387q-14 -3 -26 4.5t-14 21.5q-24 203 166 305l129 -270z" />
-<glyph unicode="&#xf1be;" horiz-adv-x="2304" d="M784 164l16 241l-16 523q-1 10 -7.5 17t-16.5 7q-9 0 -16 -7t-7 -17l-14 -523l14 -241q1 -10 7.5 -16.5t15.5 -6.5q22 0 24 23zM1080 193l11 211l-12 586q0 16 -13 24q-8 5 -16 5t-16 -5q-13 -8 -13 -24l-1 -6l-10 -579q0 -1 11 -236v-1q0 -10 6 -17q9 -11 23 -11 q11 0 20 9q9 7 9 20zM35 533l20 -128l-20 -126q-2 -9 -9 -9t-9 9l-17 126l17 128q2 9 9 9t9 -9zM121 612l26 -207l-26 -203q-2 -9 -10 -9q-9 0 -9 10l-23 202l23 207q0 9 9 9q8 0 10 -9zM401 159zM213 650l25 -245l-25 -237q0 -11 -11 -11q-10 0 -12 11l-21 237l21 245 q2 12 12 12q11 0 11 -12zM307 657l23 -252l-23 -244q-2 -13 -14 -13q-13 0 -13 13l-21 244l21 252q0 13 13 13q12 0 14 -13zM401 639l21 -234l-21 -246q-2 -16 -16 -16q-6 0 -10.5 4.5t-4.5 11.5l-20 246l20 234q0 6 4.5 10.5t10.5 4.5q14 0 16 -15zM784 164zM495 785 l21 -380l-21 -246q0 -7 -5 -12.5t-12 -5.5q-16 0 -18 18l-18 246l18 380q2 18 18 18q7 0 12 -5.5t5 -12.5zM589 871l19 -468l-19 -244q0 -8 -5.5 -13.5t-13.5 -5.5q-18 0 -20 19l-16 244l16 468q2 19 20 19q8 0 13.5 -5.5t5.5 -13.5zM687 911l18 -506l-18 -242 q-2 -21 -22 -21q-19 0 -21 21l-16 242l16 506q0 9 6.5 15.5t14.5 6.5q9 0 15 -6.5t7 -15.5zM1079 169v0v0zM881 915l15 -510l-15 -239q0 -10 -7.5 -17.5t-17.5 -7.5t-17 7t-8 18l-14 239l14 510q0 11 7.5 18t17.5 7t17.5 -7t7.5 -18zM980 896l14 -492l-14 -236q0 -11 -8 -19 t-19 -8t-19 8t-9 19l-12 236l12 492q1 12 9 20t19 8t18.5 -8t8.5 -20zM1192 404l-14 -231v0q0 -13 -9 -22t-22 -9t-22 9t-10 22l-6 114l-6 117l12 636v3q2 15 12 24q9 7 20 7q8 0 15 -5q14 -8 16 -26zM2304 423q0 -117 -83 -199.5t-200 -82.5h-786q-13 2 -22 11t-9 22v899 q0 23 28 33q85 34 181 34q195 0 338 -131.5t160 -323.5q53 22 110 22q117 0 200 -83t83 -201z" />
-<glyph unicode="&#xf1c0;" d="M768 768q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 0q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127 t443 -43zM768 384q237 0 443 43t325 127v-170q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5t-103 128v170q119 -84 325 -127t443 -43zM768 1536q208 0 385 -34.5t280 -93.5t103 -128v-128q0 -69 -103 -128t-280 -93.5t-385 -34.5t-385 34.5t-280 93.5 t-103 128v128q0 69 103 128t280 93.5t385 34.5z" />
-<glyph unicode="&#xf1c1;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M894 465q33 -26 84 -56q59 7 117 7q147 0 177 -49q16 -22 2 -52q0 -1 -1 -2l-2 -2v-1q-6 -38 -71 -38q-48 0 -115 20t-130 53q-221 -24 -392 -83q-153 -262 -242 -262q-15 0 -28 7l-24 12q-1 1 -6 5q-10 10 -6 36q9 40 56 91.5t132 96.5q14 9 23 -6q2 -2 2 -4q52 85 107 197 q68 136 104 262q-24 82 -30.5 159.5t6.5 127.5q11 40 42 40h21h1q23 0 35 -15q18 -21 9 -68q-2 -6 -4 -8q1 -3 1 -8v-30q-2 -123 -14 -192q55 -164 146 -238zM318 54q52 24 137 158q-51 -40 -87.5 -84t-49.5 -74zM716 974q-15 -42 -2 -132q1 7 7 44q0 3 7 43q1 4 4 8 q-1 1 -1 2t-0.5 1.5t-0.5 1.5q-1 22 -13 36q0 -1 -1 -2v-2zM592 313q135 54 284 81q-2 1 -13 9.5t-16 13.5q-76 67 -127 176q-27 -86 -83 -197q-30 -56 -45 -83zM1238 329q-24 24 -140 24q76 -28 124 -28q14 0 18 1q0 1 -2 3z" />
-<glyph unicode="&#xf1c2;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M233 768v-107h70l164 -661h159l128 485q7 20 10 46q2 16 2 24h4l3 -24q1 -3 3.5 -20t5.5 -26l128 -485h159l164 661h70v107h-300v-107h90l-99 -438q-5 -20 -7 -46l-2 -21h-4l-3 21q-1 5 -4 21t-5 25l-144 545h-114l-144 -545q-2 -9 -4.5 -24.5t-3.5 -21.5l-4 -21h-4l-2 21 q-2 26 -7 46l-99 438h90v107h-300z" />
-<glyph unicode="&#xf1c3;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M429 106v-106h281v106h-75l103 161q5 7 10 16.5t7.5 13.5t3.5 4h2q1 -4 5 -10q2 -4 4.5 -7.5t6 -8t6.5 -8.5l107 -161h-76v-106h291v106h-68l-192 273l195 282h67v107h-279v-107h74l-103 -159q-4 -7 -10 -16.5t-9 -13.5l-2 -3h-2q-1 4 -5 10q-6 11 -17 23l-106 159h76v107 h-290v-107h68l189 -272l-194 -283h-68z" />
-<glyph unicode="&#xf1c4;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M416 106v-106h327v106h-93v167h137q76 0 118 15q67 23 106.5 87t39.5 146q0 81 -37 141t-100 87q-48 19 -130 19h-368v-107h92v-555h-92zM769 386h-119v268h120q52 0 83 -18q56 -33 56 -115q0 -89 -62 -120q-31 -15 -78 -15z" />
-<glyph unicode="&#xf1c5;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M1280 320v-320h-1024v192l192 192l128 -128l384 384zM448 512q-80 0 -136 56t-56 136t56 136t136 56t136 -56t56 -136t-56 -136t-136 -56z" />
-<glyph unicode="&#xf1c6;" d="M640 1152v128h-128v-128h128zM768 1024v128h-128v-128h128zM640 896v128h-128v-128h128zM768 768v128h-128v-128h128zM1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400 v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-128v-128h-128v128h-512v-1536h1280zM781 593l107 -349q8 -27 8 -52q0 -83 -72.5 -137.5t-183.5 -54.5t-183.5 54.5t-72.5 137.5q0 25 8 52q21 63 120 396v128h128v-128h79 q22 0 39 -13t23 -34zM640 128q53 0 90.5 19t37.5 45t-37.5 45t-90.5 19t-90.5 -19t-37.5 -45t37.5 -45t90.5 -19z" />
-<glyph unicode="&#xf1c7;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M620 686q20 -8 20 -30v-544q0 -22 -20 -30q-8 -2 -12 -2q-12 0 -23 9l-166 167h-131q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h131l166 167q16 15 35 7zM1037 -3q31 0 50 24q129 159 129 363t-129 363q-16 21 -43 24t-47 -14q-21 -17 -23.5 -43.5t14.5 -47.5 q100 -123 100 -282t-100 -282q-17 -21 -14.5 -47.5t23.5 -42.5q18 -15 40 -15zM826 145q27 0 47 20q87 93 87 219t-87 219q-18 19 -45 20t-46 -17t-20 -44.5t18 -46.5q52 -57 52 -131t-52 -131q-19 -20 -18 -46.5t20 -44.5q20 -17 44 -17z" />
-<glyph unicode="&#xf1c8;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M768 768q52 0 90 -38t38 -90v-384q0 -52 -38 -90t-90 -38h-384q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h384zM1260 766q20 -8 20 -30v-576q0 -22 -20 -30q-8 -2 -12 -2q-14 0 -23 9l-265 266v90l265 266q9 9 23 9q4 0 12 -2z" />
-<glyph unicode="&#xf1c9;" d="M1468 1156q28 -28 48 -76t20 -88v-1152q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1600q0 40 28 68t68 28h896q40 0 88 -20t76 -48zM1024 1400v-376h376q-10 29 -22 41l-313 313q-12 12 -41 22zM1408 -128v1024h-416q-40 0 -68 28t-28 68v416h-768v-1536h1280z M480 768q8 11 21 12.5t24 -6.5l51 -38q11 -8 12.5 -21t-6.5 -24l-182 -243l182 -243q8 -11 6.5 -24t-12.5 -21l-51 -38q-11 -8 -24 -6.5t-21 12.5l-226 301q-14 19 0 38zM1282 467q14 -19 0 -38l-226 -301q-8 -11 -21 -12.5t-24 6.5l-51 38q-11 8 -12.5 21t6.5 24l182 243 l-182 243q-8 11 -6.5 24t12.5 21l51 38q11 8 24 6.5t21 -12.5zM662 6q-13 2 -20.5 13t-5.5 24l138 831q2 13 13 20.5t24 5.5l63 -10q13 -2 20.5 -13t5.5 -24l-138 -831q-2 -13 -13 -20.5t-24 -5.5z" />
-<glyph unicode="&#xf1ca;" d="M1497 709v-198q-101 -23 -198 -23q-65 -136 -165.5 -271t-181.5 -215.5t-128 -106.5q-80 -45 -162 3q-28 17 -60.5 43.5t-85 83.5t-102.5 128.5t-107.5 184t-105.5 244t-91.5 314.5t-70.5 390h283q26 -218 70 -398.5t104.5 -317t121.5 -235.5t140 -195q169 169 287 406 q-142 72 -223 220t-81 333q0 192 104 314.5t284 122.5q178 0 273 -105.5t95 -297.5q0 -159 -58 -286q-7 -1 -19.5 -3t-46 -2t-63 6t-62 25.5t-50.5 51.5q31 103 31 184q0 87 -29 132t-79 45q-53 0 -85 -49.5t-32 -140.5q0 -186 105 -293.5t267 -107.5q62 0 121 14z" />
-<glyph unicode="&#xf1cb;" horiz-adv-x="1792" d="M216 367l603 -402v359l-334 223zM154 511l193 129l-193 129v-258zM973 -35l603 402l-269 180l-334 -223v-359zM896 458l272 182l-272 182l-272 -182zM485 733l334 223v359l-603 -402zM1445 640l193 -129v258zM1307 733l269 180l-603 402v-359zM1792 913v-546 q0 -41 -34 -64l-819 -546q-21 -13 -43 -13t-43 13l-819 546q-34 23 -34 64v546q0 41 34 64l819 546q21 13 43 13t43 -13l819 -546q34 -23 34 -64z" />
-<glyph unicode="&#xf1cc;" horiz-adv-x="2048" d="M1800 764q111 -46 179.5 -145.5t68.5 -221.5q0 -164 -118 -280.5t-285 -116.5q-4 0 -11.5 0.5t-10.5 0.5h-1209h-1h-2h-5q-170 10 -288 125.5t-118 280.5q0 110 55 203t147 147q-12 39 -12 82q0 115 82 196t199 81q95 0 172 -58q75 154 222.5 248t326.5 94 q166 0 306 -80.5t221.5 -218.5t81.5 -301q0 -6 -0.5 -18t-0.5 -18zM468 498q0 -122 84 -193t208 -71q137 0 240 99q-16 20 -47.5 56.5t-43.5 50.5q-67 -65 -144 -65q-55 0 -93.5 33.5t-38.5 87.5q0 53 38.5 87t91.5 34q44 0 84.5 -21t73 -55t65 -75t69 -82t77 -75t97 -55 t121.5 -21q121 0 204.5 71.5t83.5 190.5q0 121 -84 192t-207 71q-143 0 -241 -97q14 -16 29.5 -34t34.5 -40t29 -34q66 64 142 64q52 0 92 -33t40 -84q0 -57 -37 -91.5t-94 -34.5q-43 0 -82.5 21t-72 55t-65.5 75t-69.5 82t-77.5 75t-96.5 55t-118.5 21q-122 0 -207 -70.5 t-85 -189.5z" />
-<glyph unicode="&#xf1cd;" horiz-adv-x="1792" d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 1408q-190 0 -361 -90l194 -194q82 28 167 28t167 -28l194 194q-171 90 -361 90zM218 279l194 194 q-28 82 -28 167t28 167l-194 194q-90 -171 -90 -361t90 -361zM896 -128q190 0 361 90l-194 194q-82 -28 -167 -28t-167 28l-194 -194q171 -90 361 -90zM896 256q159 0 271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5 t271.5 -112.5zM1380 473l194 -194q90 171 90 361t-90 361l-194 -194q28 -82 28 -167t-28 -167z" />
-<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348q0 222 101 414.5t276.5 317t390.5 155.5v-260q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 q0 230 -145.5 406t-366.5 221v260q215 -31 390.5 -155.5t276.5 -317t101 -414.5z" />
-<glyph unicode="&#xf1d0;" horiz-adv-x="1792" d="M19 662q8 217 116 406t305 318h5q0 -1 -1 -3q-8 -8 -28 -33.5t-52 -76.5t-60 -110.5t-44.5 -135.5t-14 -150.5t39 -157.5t108.5 -154q50 -50 102 -69.5t90.5 -11.5t69.5 23.5t47 32.5l16 16q39 51 53 116.5t6.5 122.5t-21 107t-26.5 80l-14 29q-10 25 -30.5 49.5t-43 41 t-43.5 29.5t-35 19l-13 6l104 115q39 -17 78 -52t59 -61l19 -27q1 48 -18.5 103.5t-40.5 87.5l-20 31l161 183l160 -181q-33 -46 -52.5 -102.5t-22.5 -90.5l-4 -33q22 37 61.5 72.5t67.5 52.5l28 17l103 -115q-44 -14 -85 -50t-60 -65l-19 -29q-31 -56 -48 -133.5t-7 -170 t57 -156.5q33 -45 77.5 -60.5t85 -5.5t76 26.5t57.5 33.5l21 16q60 53 96.5 115t48.5 121.5t10 121.5t-18 118t-37 107.5t-45.5 93t-45 72t-34.5 47.5l-13 17q-14 13 -7 13l10 -3q40 -29 62.5 -46t62 -50t64 -58t58.5 -65t55.5 -77t45.5 -88t38 -103t23.5 -117t10.5 -136 q3 -259 -108 -465t-312 -321t-456 -115q-185 0 -351 74t-283.5 198t-184 293t-60.5 353z" />
-<glyph unicode="&#xf1d1;" horiz-adv-x="1792" d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251 l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162 q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33 q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5 t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
-<glyph unicode="&#xf1d2;" d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85 q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392 q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072 q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf1d3;" horiz-adv-x="1792" d="M595 22q0 100 -165 100q-158 0 -158 -104q0 -101 172 -101q151 0 151 105zM536 777q0 61 -30 102t-89 41q-124 0 -124 -145q0 -135 124 -135q119 0 119 137zM805 1101v-202q-36 -12 -79 -22q16 -43 16 -84q0 -127 -73 -216.5t-197 -112.5q-40 -8 -59.5 -27t-19.5 -58 q0 -31 22.5 -51.5t58 -32t78.5 -22t86 -25.5t78.5 -37.5t58 -64t22.5 -98.5q0 -304 -363 -304q-69 0 -130 12.5t-116 41t-87.5 82t-32.5 127.5q0 165 182 225v4q-67 41 -67 126q0 109 63 137v4q-72 24 -119.5 108.5t-47.5 165.5q0 139 95 231.5t235 92.5q96 0 178 -47 q98 0 218 47zM1123 220h-222q4 45 4 134v609q0 94 -4 128h222q-4 -33 -4 -124v-613q0 -89 4 -134zM1724 442v-196q-71 -39 -174 -39q-62 0 -107 20t-70 50t-39.5 78t-18.5 92t-4 103v351h2v4q-7 0 -19 1t-18 1q-21 0 -59 -6v190h96v76q0 54 -6 89h227q-6 -41 -6 -165h171 v-190q-15 0 -43.5 2t-42.5 2h-85v-365q0 -131 87 -131q61 0 109 33zM1148 1389q0 -58 -39 -101.5t-96 -43.5q-58 0 -98 43.5t-40 101.5q0 59 39.5 103t98.5 44q58 0 96.5 -44.5t38.5 -102.5z" />
-<glyph unicode="&#xf1d4;" d="M825 547l343 588h-150q-21 -39 -63.5 -118.5t-68 -128.5t-59.5 -118.5t-60 -128.5h-3q-21 48 -44.5 97t-52 105.5t-46.5 92t-54 104.5t-49 95h-150l323 -589v-435h134v436zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf1d5;" horiz-adv-x="1280" d="M842 964q0 -80 -57 -136.5t-136 -56.5q-60 0 -111 35q-62 -67 -115 -146q-247 -371 -202 -859q1 -22 -12.5 -38.5t-34.5 -18.5h-5q-20 0 -35 13.5t-17 33.5q-14 126 -3.5 247.5t29.5 217t54 186t69 155.5t74 125q61 90 132 165q-16 35 -16 77q0 80 56.5 136.5t136.5 56.5 t136.5 -56.5t56.5 -136.5zM1223 953q0 -158 -78 -292t-212.5 -212t-292.5 -78q-64 0 -131 14q-21 5 -32.5 23.5t-6.5 39.5q5 20 23 31.5t39 7.5q51 -13 108 -13q97 0 186 38t153 102t102 153t38 186t-38 186t-102 153t-153 102t-186 38t-186 -38t-153 -102t-102 -153 t-38 -186q0 -114 52 -218q10 -20 3.5 -40t-25.5 -30t-39.5 -3t-30.5 26q-64 123 -64 265q0 119 46.5 227t124.5 186t186 124t226 46q158 0 292.5 -78t212.5 -212.5t78 -292.5z" />
-<glyph unicode="&#xf1d6;" horiz-adv-x="1792" d="M270 730q-8 19 -8 52q0 20 11 49t24 45q-1 22 7.5 53t22.5 43q0 139 92.5 288.5t217.5 209.5q139 66 324 66q133 0 266 -55q49 -21 90 -48t71 -56t55 -68t42 -74t32.5 -84.5t25.5 -89.5t22 -98l1 -5q55 -83 55 -150q0 -14 -9 -40t-9 -38q0 -1 1.5 -3.5t3.5 -5t2 -3.5 q77 -114 120.5 -214.5t43.5 -208.5q0 -43 -19.5 -100t-55.5 -57q-9 0 -19.5 7.5t-19 17.5t-19 26t-16 26.5t-13.5 26t-9 17.5q-1 1 -3 1l-5 -4q-59 -154 -132 -223q20 -20 61.5 -38.5t69 -41.5t35.5 -65q-2 -4 -4 -16t-7 -18q-64 -97 -302 -97q-53 0 -110.5 9t-98 20 t-104.5 30q-15 5 -23 7q-14 4 -46 4.5t-40 1.5q-41 -45 -127.5 -65t-168.5 -20q-35 0 -69 1.5t-93 9t-101 20.5t-74.5 40t-32.5 64q0 40 10 59.5t41 48.5q11 2 40.5 13t49.5 12q4 0 14 2q2 2 2 4l-2 3q-48 11 -108 105.5t-73 156.5l-5 3q-4 0 -12 -20q-18 -41 -54.5 -74.5 t-77.5 -37.5h-1q-4 0 -6 4.5t-5 5.5q-23 54 -23 100q0 275 252 466z" />
-<glyph unicode="&#xf1d7;" horiz-adv-x="2048" d="M580 1075q0 41 -25 66t-66 25q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 66 24.5t25 65.5zM1323 568q0 28 -25.5 50t-65.5 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q40 0 65.5 22t25.5 51zM1087 1075q0 41 -24.5 66t-65.5 25 q-43 0 -76 -25.5t-33 -65.5q0 -39 33 -64.5t76 -25.5q41 0 65.5 24.5t24.5 65.5zM1722 568q0 28 -26 50t-65 22q-27 0 -49.5 -22.5t-22.5 -49.5q0 -28 22.5 -50.5t49.5 -22.5q39 0 65 22t26 51zM1456 965q-31 4 -70 4q-169 0 -311 -77t-223.5 -208.5t-81.5 -287.5 q0 -78 23 -152q-35 -3 -68 -3q-26 0 -50 1.5t-55 6.5t-44.5 7t-54.5 10.5t-50 10.5l-253 -127l72 218q-290 203 -290 490q0 169 97.5 311t264 223.5t363.5 81.5q176 0 332.5 -66t262 -182.5t136.5 -260.5zM2048 404q0 -117 -68.5 -223.5t-185.5 -193.5l55 -181l-199 109 q-150 -37 -218 -37q-169 0 -311 70.5t-223.5 191.5t-81.5 264t81.5 264t223.5 191.5t311 70.5q161 0 303 -70.5t227.5 -192t85.5 -263.5z" />
-<glyph unicode="&#xf1d8;" horiz-adv-x="1792" d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-453 185l-242 -295q-18 -23 -49 -23q-13 0 -22 4q-19 7 -30.5 23.5t-11.5 36.5v349l864 1059l-1069 -925l-395 162q-37 14 -40 55q-2 40 32 59l1664 960q15 9 32 9q20 0 36 -11z" />
-<glyph unicode="&#xf1d9;" horiz-adv-x="1792" d="M1764 1525q33 -24 27 -64l-256 -1536q-5 -29 -32 -45q-14 -8 -31 -8q-11 0 -24 5l-527 215l-298 -327q-18 -21 -47 -21q-14 0 -23 4q-19 7 -30 23.5t-11 36.5v452l-472 193q-37 14 -40 55q-3 39 32 59l1664 960q35 21 68 -2zM1422 26l221 1323l-1434 -827l336 -137 l863 639l-478 -797z" />
-<glyph unicode="&#xf1da;" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298zM896 928v-448q0 -14 -9 -23 t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf1db;" d="M768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf1dc;" horiz-adv-x="1792" d="M1682 -128q-44 0 -132.5 3.5t-133.5 3.5q-44 0 -132 -3.5t-132 -3.5q-24 0 -37 20.5t-13 45.5q0 31 17 46t39 17t51 7t45 15q33 21 33 140l-1 391q0 21 -1 31q-13 4 -50 4h-675q-38 0 -51 -4q-1 -10 -1 -31l-1 -371q0 -142 37 -164q16 -10 48 -13t57 -3.5t45 -15 t20 -45.5q0 -26 -12.5 -48t-36.5 -22q-47 0 -139.5 3.5t-138.5 3.5q-43 0 -128 -3.5t-127 -3.5q-23 0 -35.5 21t-12.5 45q0 30 15.5 45t36 17.5t47.5 7.5t42 15q33 23 33 143l-1 57v813q0 3 0.5 26t0 36.5t-1.5 38.5t-3.5 42t-6.5 36.5t-11 31.5t-16 18q-15 10 -45 12t-53 2 t-41 14t-18 45q0 26 12 48t36 22q46 0 138.5 -3.5t138.5 -3.5q42 0 126.5 3.5t126.5 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17 -43.5t-38.5 -14.5t-49.5 -4t-43 -13q-35 -21 -35 -160l1 -320q0 -21 1 -32q13 -3 39 -3h699q25 0 38 3q1 11 1 32l1 320q0 139 -35 160 q-18 11 -58.5 12.5t-66 13t-25.5 49.5q0 26 12.5 48t37.5 22q44 0 132 -3.5t132 -3.5q43 0 129 3.5t129 3.5q25 0 37.5 -22t12.5 -48q0 -30 -17.5 -44t-40 -14.5t-51.5 -3t-44 -12.5q-35 -23 -35 -161l1 -943q0 -119 34 -140q16 -10 46 -13.5t53.5 -4.5t41.5 -15.5t18 -44.5 q0 -26 -12 -48t-36 -22z" />
-<glyph unicode="&#xf1dd;" horiz-adv-x="1280" d="M1278 1347v-73q0 -29 -18.5 -61t-42.5 -32q-50 0 -54 -1q-26 -6 -32 -31q-3 -11 -3 -64v-1152q0 -25 -18 -43t-43 -18h-108q-25 0 -43 18t-18 43v1218h-143v-1218q0 -25 -17.5 -43t-43.5 -18h-108q-26 0 -43.5 18t-17.5 43v496q-147 12 -245 59q-126 58 -192 179 q-64 117 -64 259q0 166 88 286q88 118 209 159q111 37 417 37h479q25 0 43 -18t18 -43z" />
-<glyph unicode="&#xf1de;" d="M352 128v-128h-352v128h352zM704 256q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM864 640v-128h-864v128h864zM224 1152v-128h-224v128h224zM1536 128v-128h-736v128h736zM576 1280q26 0 45 -19t19 -45v-256 q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1216 768q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h256zM1536 640v-128h-224v128h224zM1536 1152v-128h-864v128h864z" />
-<glyph unicode="&#xf1e0;" d="M1216 512q133 0 226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5t-226.5 93.5t-93.5 226.5q0 12 2 34l-360 180q-92 -86 -218 -86q-133 0 -226.5 93.5t-93.5 226.5t93.5 226.5t226.5 93.5q126 0 218 -86l360 180q-2 22 -2 34q0 133 93.5 226.5t226.5 93.5 t226.5 -93.5t93.5 -226.5t-93.5 -226.5t-226.5 -93.5q-126 0 -218 86l-360 -180q2 -22 2 -34t-2 -34l360 -180q92 86 218 86z" />
-<glyph unicode="&#xf1e1;" d="M1280 341q0 88 -62.5 151t-150.5 63q-84 0 -145 -58l-241 120q2 16 2 23t-2 23l241 120q61 -58 145 -58q88 0 150.5 63t62.5 151t-62.5 150.5t-150.5 62.5t-151 -62.5t-63 -150.5q0 -7 2 -23l-241 -120q-62 57 -145 57q-88 0 -150.5 -62.5t-62.5 -150.5t62.5 -150.5 t150.5 -62.5q83 0 145 57l241 -120q-2 -16 -2 -23q0 -88 63 -150.5t151 -62.5t150.5 62.5t62.5 150.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf1e2;" horiz-adv-x="1792" d="M571 947q-10 25 -34 35t-49 0q-108 -44 -191 -127t-127 -191q-10 -25 0 -49t35 -34q13 -5 24 -5q42 0 60 40q34 84 98.5 148.5t148.5 98.5q25 11 35 35t0 49zM1513 1303l46 -46l-244 -243l68 -68q19 -19 19 -45.5t-19 -45.5l-64 -64q89 -161 89 -343q0 -143 -55.5 -273.5 t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5q182 0 343 -89l64 64q19 19 45.5 19t45.5 -19l68 -68zM1521 1359q-10 -10 -22 -10q-13 0 -23 10l-91 90q-9 10 -9 23t9 23q10 9 23 9t23 -9l90 -91 q10 -9 10 -22.5t-10 -22.5zM1751 1129q-11 -9 -23 -9t-23 9l-90 91q-10 9 -10 22.5t10 22.5q9 10 22.5 10t22.5 -10l91 -90q9 -10 9 -23t-9 -23zM1792 1312q0 -14 -9 -23t-23 -9h-96q-14 0 -23 9t-9 23t9 23t23 9h96q14 0 23 -9t9 -23zM1600 1504v-96q0 -14 -9 -23t-23 -9 t-23 9t-9 23v96q0 14 9 23t23 9t23 -9t9 -23zM1751 1449l-91 -90q-10 -10 -22 -10q-13 0 -23 10q-10 9 -10 22.5t10 22.5l90 91q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
-<glyph unicode="&#xf1e3;" horiz-adv-x="1792" d="M609 720l287 208l287 -208l-109 -336h-355zM896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM1515 186q149 203 149 454v3l-102 -89l-240 224l63 323 l134 -12q-150 206 -389 282l53 -124l-287 -159l-287 159l53 124q-239 -76 -389 -282l135 12l62 -323l-240 -224l-102 89v-3q0 -251 149 -454l30 132l326 -40l139 -298l-116 -69q117 -39 240 -39t240 39l-116 69l139 298l326 40z" />
-<glyph unicode="&#xf1e4;" horiz-adv-x="1792" d="M448 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM256 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM832 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM66 768q-28 0 -47 19t-19 46v129h514v-129q0 -27 -19 -46t-46 -19h-383zM1216 224v-192q0 -14 -9 -23t-23 -9h-192 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1600 224v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23 zM1408 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1016v-13h-514v10q0 104 -382 102q-382 -1 -382 -102v-10h-514v13q0 17 8.5 43t34 64t65.5 75.5t110.5 76t160 67.5t224 47.5t293.5 18.5t293 -18.5t224 -47.5 t160.5 -67.5t110.5 -76t65.5 -75.5t34 -64t8.5 -43zM1792 608v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 962v-129q0 -27 -19 -46t-46 -19h-384q-27 0 -46 19t-19 46v129h514z" />
-<glyph unicode="&#xf1e5;" horiz-adv-x="1792" d="M704 1216v-768q0 -26 -19 -45t-45 -19v-576q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v512l249 873q7 23 31 23h424zM1024 1216v-704h-256v704h256zM1792 320v-512q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v576q-26 0 -45 19t-19 45v768h424q24 0 31 -23z M736 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23zM1408 1504v-224h-352v224q0 14 9 23t23 9h288q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf1e6;" horiz-adv-x="1792" d="M1755 1083q37 -37 37 -90t-37 -91l-401 -400l150 -150l-160 -160q-163 -163 -389.5 -186.5t-411.5 100.5l-362 -362h-181v181l362 362q-124 185 -100.5 411.5t186.5 389.5l160 160l150 -150l400 401q38 37 91 37t90 -37t37 -90.5t-37 -90.5l-400 -401l234 -234l401 400 q38 37 91 37t90 -37z" />
-<glyph unicode="&#xf1e7;" horiz-adv-x="1792" d="M873 796q0 -83 -63.5 -142.5t-152.5 -59.5t-152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59t152.5 -59t63.5 -143zM1375 796q0 -83 -63 -142.5t-153 -59.5q-89 0 -152.5 59.5t-63.5 142.5q0 84 63.5 143t152.5 59q90 0 153 -59t63 -143zM1600 616v667q0 87 -32 123.5 t-111 36.5h-1112q-83 0 -112.5 -34t-29.5 -126v-673q43 -23 88.5 -40t81 -28t81 -18.5t71 -11t70 -4t58.5 -0.5t56.5 2t44.5 2q68 1 95 -27q6 -6 10 -9q26 -25 61 -51q7 91 118 87q5 0 36.5 -1.5t43 -2t45.5 -1t53 1t54.5 4.5t61 8.5t62 13.5t67 19.5t67.5 27t72 34.5z M1763 621q-121 -149 -372 -252q84 -285 -23 -465q-66 -113 -183 -148q-104 -32 -182 15q-86 51 -82 164l-1 326v1q-8 2 -24.5 6t-23.5 5l-1 -338q4 -114 -83 -164q-79 -47 -183 -15q-117 36 -182 150q-105 180 -22 463q-251 103 -372 252q-25 37 -4 63t60 -1q3 -2 11 -7 t11 -8v694q0 72 47 123t114 51h1257q67 0 114 -51t47 -123v-694l21 15q39 27 60 1t-4 -63z" />
-<glyph unicode="&#xf1e8;" horiz-adv-x="1792" d="M896 1102v-434h-145v434h145zM1294 1102v-434h-145v434h145zM1294 342l253 254v795h-1194v-1049h326v-217l217 217h398zM1692 1536v-1013l-434 -434h-326l-217 -217h-217v217h-398v1158l109 289h1483z" />
-<glyph unicode="&#xf1e9;" d="M773 217v-127q-1 -292 -6 -305q-12 -32 -51 -40q-54 -9 -181.5 38t-162.5 89q-13 15 -17 36q-1 12 4 26q4 10 34 47t181 216q1 0 60 70q15 19 39.5 24.5t49.5 -3.5q24 -10 37.5 -29t12.5 -42zM624 468q-3 -55 -52 -70l-120 -39q-275 -88 -292 -88q-35 2 -54 36 q-12 25 -17 75q-8 76 1 166.5t30 124.5t56 32q13 0 202 -77q70 -29 115 -47l84 -34q23 -9 35.5 -30.5t11.5 -48.5zM1450 171q-7 -54 -91.5 -161t-135.5 -127q-37 -14 -63 7q-14 10 -184 287l-47 77q-14 21 -11.5 46t19.5 46q35 43 83 26q1 -1 119 -40q203 -66 242 -79.5 t47 -20.5q28 -22 22 -61zM778 803q5 -102 -54 -122q-58 -17 -114 71l-378 598q-8 35 19 62q41 43 207.5 89.5t224.5 31.5q40 -10 49 -45q3 -18 22 -305.5t24 -379.5zM1440 695q3 -39 -26 -59q-15 -10 -329 -86q-67 -15 -91 -23l1 2q-23 -6 -46 4t-37 32q-30 47 0 87 q1 1 75 102q125 171 150 204t34 39q28 19 65 2q48 -23 123 -133.5t81 -167.5v-3z" />
-<glyph unicode="&#xf1ea;" horiz-adv-x="2048" d="M1024 1024h-384v-384h384v384zM1152 384v-128h-640v128h640zM1152 1152v-640h-640v640h640zM1792 384v-128h-512v128h512zM1792 640v-128h-512v128h512zM1792 896v-128h-512v128h512zM1792 1152v-128h-512v128h512zM256 192v960h-128v-960q0 -26 19 -45t45 -19t45 19 t19 45zM1920 192v1088h-1536v-1088q0 -33 -11 -64h1483q26 0 45 19t19 45zM2048 1408v-1216q0 -80 -56 -136t-136 -56h-1664q-80 0 -136 56t-56 136v1088h256v128h1792z" />
-<glyph unicode="&#xf1eb;" horiz-adv-x="2048" d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
-<glyph unicode="&#xf1ec;" horiz-adv-x="1792" d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf1ed;" horiz-adv-x="1792" d="M1112 1090q0 159 -237 159h-70q-32 0 -59.5 -21.5t-34.5 -52.5l-63 -276q-2 -5 -2 -16q0 -24 17 -39.5t41 -15.5h53q69 0 128.5 13t112.5 41t83.5 81.5t30.5 126.5zM1716 938q0 -265 -220 -428q-219 -161 -612 -161h-61q-32 0 -59 -21.5t-34 -52.5l-73 -316 q-8 -36 -40.5 -61.5t-69.5 -25.5h-213q-31 0 -53 20t-22 51q0 10 13 65h151q34 0 64 23.5t38 56.5l73 316q8 33 37.5 57t63.5 24h61q390 0 607 160t217 421q0 129 -51 207q183 -92 183 -335zM1533 1123q0 -264 -221 -428q-218 -161 -612 -161h-60q-32 0 -59.5 -22t-34.5 -53 l-73 -315q-8 -36 -40 -61.5t-69 -25.5h-214q-31 0 -52.5 19.5t-21.5 51.5q0 8 2 20l300 1301q8 36 40.5 61.5t69.5 25.5h444q68 0 125 -4t120.5 -15t113.5 -30t96.5 -50.5t77.5 -74t49.5 -103.5t18.5 -136z" />
-<glyph unicode="&#xf1ee;" horiz-adv-x="1792" d="M602 949q19 -61 31 -123.5t17 -141.5t-14 -159t-62 -145q-21 81 -67 157t-95.5 127t-99 90.5t-78.5 57.5t-33 19q-62 34 -81.5 100t14.5 128t101 81.5t129 -14.5q138 -83 238 -177zM927 1236q11 -25 20.5 -46t36.5 -100.5t42.5 -150.5t25.5 -179.5t0 -205.5t-47.5 -209.5 t-105.5 -208.5q-51 -72 -138 -72q-54 0 -98 31q-57 40 -69 109t28 127q60 85 81 195t13 199.5t-32 180.5t-39 128t-22 52q-31 63 -8.5 129.5t85.5 97.5q34 17 75 17q47 0 88.5 -25t63.5 -69zM1248 567q-17 -160 -72 -311q-17 131 -63 246q25 174 -5 361q-27 178 -94 342 q114 -90 212 -211q9 -37 15 -80q26 -179 7 -347zM1520 1440q9 -17 23.5 -49.5t43.5 -117.5t50.5 -178t34 -227.5t5 -269t-47 -300t-112.5 -323.5q-22 -48 -66 -75.5t-95 -27.5q-39 0 -74 16q-67 31 -92.5 100t4.5 136q58 126 90 257.5t37.5 239.5t-3.5 213.5t-26.5 180.5 t-38.5 138.5t-32.5 90t-15.5 32.5q-34 65 -11.5 135.5t87.5 104.5q37 20 81 20q49 0 91.5 -25.5t66.5 -70.5z" />
-<glyph unicode="&#xf1f0;" horiz-adv-x="2304" d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf1f1;" horiz-adv-x="2304" d="M671 603h-13q-47 0 -47 -32q0 -22 20 -22q17 0 28 15t12 39zM1066 639h62v3q1 4 0.5 6.5t-1 7t-2 8t-4.5 6.5t-7.5 5t-11.5 2q-28 0 -36 -38zM1606 603h-12q-48 0 -48 -32q0 -22 20 -22q17 0 28 15t12 39zM1925 629q0 41 -30 41q-19 0 -31 -20t-12 -51q0 -42 28 -42 q20 0 32.5 20t12.5 52zM480 770h87l-44 -262h-56l32 201l-71 -201h-39l-4 200l-34 -200h-53l44 262h81l2 -163zM733 663q0 -6 -4 -42q-16 -101 -17 -113h-47l1 22q-20 -26 -58 -26q-23 0 -37.5 16t-14.5 42q0 39 26 60.5t73 21.5q14 0 23 -1q0 3 0.5 5.5t1 4.5t0.5 3 q0 20 -36 20q-29 0 -59 -10q0 4 7 48q38 11 67 11q74 0 74 -62zM889 721l-8 -49q-22 3 -41 3q-27 0 -27 -17q0 -8 4.5 -12t21.5 -11q40 -19 40 -60q0 -72 -87 -71q-34 0 -58 6q0 2 7 49q29 -8 51 -8q32 0 32 19q0 7 -4.5 11.5t-21.5 12.5q-43 20 -43 59q0 72 84 72 q30 0 50 -4zM977 721h28l-7 -52h-29q-2 -17 -6.5 -40.5t-7 -38.5t-2.5 -18q0 -16 19 -16q8 0 16 2l-8 -47q-21 -7 -40 -7q-43 0 -45 47q0 12 8 56q3 20 25 146h55zM1180 648q0 -23 -7 -52h-111q-3 -22 10 -33t38 -11q30 0 58 14l-9 -54q-30 -8 -57 -8q-95 0 -95 95 q0 55 27.5 90.5t69.5 35.5q35 0 55.5 -21t20.5 -56zM1319 722q-13 -23 -22 -62q-22 2 -31 -24t-25 -128h-56l3 14q22 130 29 199h51l-3 -33q14 21 25.5 29.5t28.5 4.5zM1506 763l-9 -57q-28 14 -50 14q-31 0 -51 -27.5t-20 -70.5q0 -30 13.5 -47t38.5 -17q21 0 48 13 l-10 -59q-28 -8 -50 -8q-45 0 -71.5 30.5t-26.5 82.5q0 70 35.5 114.5t91.5 44.5q26 0 61 -13zM1668 663q0 -18 -4 -42q-13 -79 -17 -113h-46l1 22q-20 -26 -59 -26q-23 0 -37 16t-14 42q0 39 25.5 60.5t72.5 21.5q15 0 23 -1q2 7 2 13q0 20 -36 20q-29 0 -59 -10q0 4 8 48 q38 11 67 11q73 0 73 -62zM1809 722q-14 -24 -21 -62q-23 2 -31.5 -23t-25.5 -129h-56l3 14q19 104 29 199h52q0 -11 -4 -33q15 21 26.5 29.5t27.5 4.5zM1950 770h56l-43 -262h-53l3 19q-23 -23 -52 -23q-31 0 -49.5 24t-18.5 64q0 53 27.5 92t64.5 39q31 0 53 -29z M2061 640q0 148 -72.5 273t-198 198t-273.5 73q-181 0 -328 -110q127 -116 171 -284h-50q-44 150 -158 253q-114 -103 -158 -253h-50q44 168 171 284q-147 110 -328 110q-148 0 -273.5 -73t-198 -198t-72.5 -273t72.5 -273t198 -198t273.5 -73q181 0 328 110 q-120 111 -165 264h50q46 -138 152 -233q106 95 152 233h50q-45 -153 -165 -264q147 -110 328 -110q148 0 273.5 73t198 198t72.5 273zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf1f2;" horiz-adv-x="2304" d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
-<glyph unicode="&#xf1f3;" horiz-adv-x="2304" d="M119 854h89l-45 108zM740 328l74 79l-70 79h-163v-49h142v-55h-142v-54h159zM898 406l99 -110v217zM1186 453q0 33 -40 33h-84v-69h83q41 0 41 36zM1475 457q0 29 -42 29h-82v-61h81q43 0 43 32zM1197 923q0 29 -42 29h-82v-60h81q43 0 43 31zM1656 854h89l-44 108z M699 1009v-271h-66v212l-94 -212h-57l-94 212v-212h-132l-25 60h-135l-25 -60h-70l116 271h96l110 -257v257h106l85 -184l77 184h108zM1255 453q0 -20 -5.5 -35t-14 -25t-22.5 -16.5t-26 -10t-31.5 -4.5t-31.5 -1t-32.5 0.5t-29.5 0.5v-91h-126l-80 90l-83 -90h-256v271h260 l80 -89l82 89h207q109 0 109 -89zM964 794v-56h-217v271h217v-57h-152v-49h148v-55h-148v-54h152zM2304 235v-229q0 -55 -38.5 -94.5t-93.5 -39.5h-2040q-55 0 -93.5 39.5t-38.5 94.5v678h111l25 61h55l25 -61h218v46l19 -46h113l20 47v-47h541v99l10 1q10 0 10 -14v-86h279 v23q23 -12 55 -18t52.5 -6.5t63 0.5t51.5 1l25 61h56l25 -61h227v58l34 -58h182v378h-180v-44l-25 44h-185v-44l-23 44h-249q-69 0 -109 -22v22h-172v-22q-24 22 -73 22h-628l-43 -97l-43 97h-198v-44l-22 44h-169l-78 -179v391q0 55 38.5 94.5t93.5 39.5h2040 q55 0 93.5 -39.5t38.5 -94.5v-678h-120q-51 0 -81 -22v22h-177q-55 0 -78 -22v22h-316v-22q-31 22 -87 22h-209v-22q-23 22 -91 22h-234l-54 -58l-50 58h-349v-378h343l55 59l52 -59h211v89h21q59 0 90 13v-102h174v99h8q8 0 10 -2t2 -10v-87h529q57 0 88 24v-24h168 q60 0 95 17zM1546 469q0 -23 -12 -43t-34 -29q25 -9 34 -26t9 -46v-54h-65v45q0 33 -12 43.5t-46 10.5h-69v-99h-65v271h154q48 0 77 -15t29 -58zM1269 936q0 -24 -12.5 -44t-33.5 -29q26 -9 34.5 -25.5t8.5 -46.5v-53h-65q0 9 0.5 26.5t0 25t-3 18.5t-8.5 16t-17.5 8.5 t-29.5 3.5h-70v-98h-64v271l153 -1q49 0 78 -14.5t29 -57.5zM1798 327v-56h-216v271h216v-56h-151v-49h148v-55h-148v-54zM1372 1009v-271h-66v271h66zM2065 357q0 -86 -102 -86h-126v58h126q34 0 34 25q0 16 -17 21t-41.5 5t-49.5 3.5t-42 22.5t-17 55q0 39 26 60t66 21 h130v-57h-119q-36 0 -36 -25q0 -16 17.5 -20.5t42 -4t49 -2.5t42 -21.5t17.5 -54.5zM2304 407v-101q-24 -35 -88 -35h-125v58h125q33 0 33 25q0 13 -12.5 19t-31 5.5t-40 2t-40 8t-31 24t-12.5 48.5q0 39 26.5 60t66.5 21h129v-57h-118q-36 0 -36 -25q0 -20 29 -22t68.5 -5 t56.5 -26zM2139 1008v-270h-92l-122 203v-203h-132l-26 60h-134l-25 -60h-75q-129 0 -129 133q0 138 133 138h63v-59q-7 0 -28 1t-28.5 0.5t-23 -2t-21.5 -6.5t-14.5 -13.5t-11.5 -23t-3 -33.5q0 -38 13.5 -58t49.5 -20h29l92 213h97l109 -256v256h99l114 -188v188h66z" />
-<glyph unicode="&#xf1f4;" horiz-adv-x="2304" d="M322 689h-15q-19 0 -19 18q0 28 19 85q5 15 15 19.5t28 4.5q77 0 77 -49q0 -41 -30.5 -59.5t-74.5 -18.5zM664 528q-47 0 -47 29q0 62 123 62l3 -3q-5 -88 -79 -88zM1438 687h-15q-19 0 -19 19q0 28 19 85q5 15 14.5 19t28.5 4q77 0 77 -49q0 -41 -30.5 -59.5 t-74.5 -18.5zM1780 527q-47 0 -47 30q0 62 123 62l3 -3q-5 -89 -79 -89zM373 894h-128q-8 0 -14.5 -4t-8.5 -7.5t-7 -12.5q-3 -7 -45 -190t-42 -192q0 -7 5.5 -12.5t13.5 -5.5h62q25 0 32.5 34.5l15 69t32.5 34.5q47 0 87.5 7.5t80.5 24.5t63.5 52.5t23.5 84.5 q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM719 798q-38 0 -74 -6q-2 0 -8.5 -1t-9 -1.5l-7.5 -1.5t-7.5 -2t-6.5 -3t-6.5 -4t-5 -5t-4.5 -7t-4 -9q-9 -29 -9 -39t9 -10q5 0 21.5 5t19.5 6q30 8 58 8q74 0 74 -36q0 -11 -10 -14q-8 -2 -18 -3t-21.5 -1.5t-17.5 -1.5 q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5q0 -38 26 -59.5t64 -21.5q24 0 45.5 6.5t33 13t38.5 23.5q-3 -7 -3 -15t5.5 -13.5t12.5 -5.5h56q1 1 7 3.5t7.5 3.5t5 3.5t5 5.5t2.5 8l45 194q4 13 4 30q0 81 -145 81zM1247 793h-74q-22 0 -39 -23q-5 -7 -29.5 -51 t-46.5 -81.5t-26 -38.5l-5 4q0 77 -27 166q-1 5 -3.5 8.5t-6 6.5t-6.5 5t-8.5 3t-8.5 1.5t-9.5 1t-9 0.5h-10h-8.5q-38 0 -38 -21l1 -5q5 -53 25 -151t25 -143q2 -16 2 -24q0 -19 -30.5 -61.5t-30.5 -58.5q0 -13 40 -13q61 0 76 25l245 415q10 20 10 26q0 9 -8 9zM1489 892 h-129q-18 0 -29 -23q-6 -13 -46.5 -191.5t-40.5 -190.5q0 -20 43 -20h7.5h9h9t9.5 1t8.5 2t8.5 3t6.5 4.5t5.5 6t3 8.5l21 91q2 10 10.5 17t19.5 7q47 0 87.5 7t80.5 24.5t63.5 52.5t23.5 84q0 36 -14.5 61t-41 36.5t-53.5 15.5t-62 4zM1835 798q-26 0 -74 -6 q-38 -6 -48 -16q-7 -8 -11 -19q-8 -24 -8 -39q0 -10 8 -10q1 0 41 12q30 8 58 8q74 0 74 -36q0 -12 -10 -14q-4 -1 -57 -7q-38 -4 -64.5 -10t-56.5 -19.5t-45.5 -39t-15.5 -62.5t26 -58.5t64 -21.5q24 0 45 6t34 13t38 24q-3 -15 -3 -16q0 -5 2 -8.5t6.5 -5.5t8 -3.5 t10.5 -2t9.5 -0.5h9.5h8q42 0 48 25l45 194q3 15 3 31q0 81 -145 81zM2157 889h-55q-25 0 -33 -40q-10 -44 -36.5 -167t-42.5 -190v-5q0 -16 16 -18h1h57q10 0 18.5 6.5t10.5 16.5l83 374h-1l1 5q0 7 -5.5 12.5t-13.5 5.5zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048 q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf1f5;" horiz-adv-x="2304" d="M1597 633q0 -69 -21 -106q-19 -35 -52 -35q-23 0 -41 9v224q29 30 57 30q57 0 57 -122zM2035 669h-110q6 98 56 98q51 0 54 -98zM476 534q0 59 -33 91.5t-101 57.5q-36 13 -52 24t-16 25q0 26 38 26q58 0 124 -33l18 112q-67 32 -149 32q-77 0 -123 -38q-48 -39 -48 -109 q0 -58 32.5 -90.5t99.5 -56.5q39 -14 54.5 -25.5t15.5 -27.5q0 -31 -48 -31q-29 0 -70 12.5t-72 30.5l-18 -113q72 -41 168 -41q81 0 129 37q51 41 51 117zM771 749l19 111h-96v135l-129 -21l-18 -114l-46 -8l-17 -103h62v-219q0 -84 44 -120q38 -30 111 -30q32 0 79 11v118 q-32 -7 -44 -7q-42 0 -42 50v197h77zM1087 724v139q-15 3 -28 3q-32 0 -55.5 -16t-33.5 -46l-10 56h-131v-471h150v306q26 31 82 31q16 0 26 -2zM1124 389h150v471h-150v-471zM1746 638q0 122 -45 179q-40 52 -111 52q-64 0 -117 -56l-8 47h-132v-645l150 25v151 q36 -11 68 -11q83 0 134 56q61 65 61 202zM1278 986q0 33 -23 56t-56 23t-56 -23t-23 -56t23 -56.5t56 -23.5t56 23.5t23 56.5zM2176 629q0 113 -48 176q-50 64 -144 64q-96 0 -151.5 -66t-55.5 -180q0 -128 63 -188q55 -55 161 -55q101 0 160 40l-16 103q-57 -31 -128 -31 q-43 0 -63 19q-23 19 -28 66h248q2 14 2 52zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
-<glyph unicode="&#xf1f6;" horiz-adv-x="2048" d="M1558 684q61 -356 298 -556q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5zM1024 -176q16 0 16 16t-16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5zM2026 1424q8 -10 7.5 -23.5t-10.5 -22.5 l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5 l418 363q10 8 23.5 7t21.5 -11z" />
-<glyph unicode="&#xf1f7;" horiz-adv-x="2048" d="M1040 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM503 315l877 760q-42 88 -132.5 146.5t-223.5 58.5q-93 0 -169.5 -31.5t-121.5 -80.5t-69 -103t-24 -105q0 -384 -137 -645zM1856 128 q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-180.5 74.5t-75.5 180.5l149 129h757q-166 187 -227 459l111 97q61 -356 298 -556zM1942 1520l84 -96q8 -10 7.5 -23.5t-10.5 -22.5l-1872 -1622q-10 -8 -23.5 -7t-21.5 11l-84 96q-8 10 -7.5 23.5t10.5 21.5l186 161 q-19 32 -19 66q50 42 91 88t85 119.5t74.5 158.5t50 206t19.5 260q0 152 117 282.5t307 158.5q-8 19 -8 39q0 40 28 68t68 28t68 -28t28 -68q0 -20 -8 -39q124 -18 219 -82.5t148 -157.5l418 363q10 8 23.5 7t21.5 -11z" />
-<glyph unicode="&#xf1f8;" horiz-adv-x="1408" d="M512 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM768 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1024 160v704q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-704 q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167 q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf1f9;" d="M1150 462v-109q0 -50 -36.5 -89t-94 -60.5t-118 -32.5t-117.5 -11q-205 0 -342.5 139t-137.5 346q0 203 136 339t339 136q34 0 75.5 -4.5t93 -18t92.5 -34t69 -56.5t28 -81v-109q0 -16 -16 -16h-118q-16 0 -16 16v70q0 43 -65.5 67.5t-137.5 24.5q-140 0 -228.5 -91.5 t-88.5 -237.5q0 -151 91.5 -249.5t233.5 -98.5q68 0 138 24t70 66v70q0 7 4.5 11.5t10.5 4.5h119q6 0 11 -4.5t5 -11.5zM768 1280q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf1fa;" d="M972 761q0 108 -53.5 169t-147.5 61q-63 0 -124 -30.5t-110 -84.5t-79.5 -137t-30.5 -180q0 -112 53.5 -173t150.5 -61q96 0 176 66.5t122.5 166t42.5 203.5zM1536 640q0 -111 -37 -197t-98.5 -135t-131.5 -74.5t-145 -27.5q-6 0 -15.5 -0.5t-16.5 -0.5q-95 0 -142 53 q-28 33 -33 83q-52 -66 -131.5 -110t-173.5 -44q-161 0 -249.5 95.5t-88.5 269.5q0 157 66 290t179 210.5t246 77.5q87 0 155 -35.5t106 -99.5l2 19l11 56q1 6 5.5 12t9.5 6h118q5 0 13 -11q5 -5 3 -16l-120 -614q-5 -24 -5 -48q0 -39 12.5 -52t44.5 -13q28 1 57 5.5t73 24 t77 50t57 89.5t24 137q0 292 -174 466t-466 174q-130 0 -248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51q228 0 405 144q11 9 24 8t21 -12l41 -49q8 -12 7 -24q-2 -13 -12 -22q-102 -83 -227.5 -128t-258.5 -45q-156 0 -298 61 t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q344 0 556 -212t212 -556z" />
-<glyph unicode="&#xf1fb;" horiz-adv-x="1792" d="M1698 1442q94 -94 94 -226.5t-94 -225.5l-225 -223l104 -104q10 -10 10 -23t-10 -23l-210 -210q-10 -10 -23 -10t-23 10l-105 105l-603 -603q-37 -37 -90 -37h-203l-256 -128l-64 64l128 256v203q0 53 37 90l603 603l-105 105q-10 10 -10 23t10 23l210 210q10 10 23 10 t23 -10l104 -104l223 225q93 94 225.5 94t226.5 -94zM512 64l576 576l-192 192l-576 -576v-192h192z" />
-<glyph unicode="&#xf1fc;" horiz-adv-x="1792" d="M1615 1536q70 0 122.5 -46.5t52.5 -116.5q0 -63 -45 -151q-332 -629 -465 -752q-97 -91 -218 -91q-126 0 -216.5 92.5t-90.5 219.5q0 128 92 212l638 579q59 54 130 54zM706 502q39 -76 106.5 -130t150.5 -76l1 -71q4 -213 -129.5 -347t-348.5 -134q-123 0 -218 46.5 t-152.5 127.5t-86.5 183t-29 220q7 -5 41 -30t62 -44.5t59 -36.5t46 -17q41 0 55 37q25 66 57.5 112.5t69.5 76t88 47.5t103 25.5t125 10.5z" />
-<glyph unicode="&#xf1fd;" horiz-adv-x="1792" d="M1792 128v-384h-1792v384q45 0 85 14t59 27.5t47 37.5q30 27 51.5 38t56.5 11t55.5 -11t52.5 -38q29 -25 47 -38t58 -27t86 -14q45 0 85 14.5t58 27t48 37.5q21 19 32.5 27t31 15t43.5 7q35 0 56.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14t85 14t59 27.5t47 37.5 q30 27 51.5 38t56.5 11q34 0 55.5 -11t51.5 -38q28 -24 47 -37.5t59 -27.5t85 -14zM1792 448v-192q-35 0 -55.5 11t-52.5 38q-29 25 -47 38t-58 27t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-22 -19 -33 -27t-31 -15t-44 -7q-35 0 -56.5 11t-51.5 38q-29 25 -47 38t-58 27 t-86 14q-45 0 -85 -14.5t-58 -27t-48 -37.5q-21 -19 -32.5 -27t-31 -15t-43.5 -7q-35 0 -56.5 11t-51.5 38q-28 24 -47 37.5t-59 27.5t-85 14q-46 0 -86 -14t-58 -27t-47 -38q-30 -27 -51.5 -38t-56.5 -11v192q0 80 56 136t136 56h64v448h256v-448h256v448h256v-448h256v448 h256v-448h64q80 0 136 -56t56 -136zM512 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1024 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51 t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150zM1536 1312q0 -77 -36 -118.5t-92 -41.5q-53 0 -90.5 37.5t-37.5 90.5q0 29 9.5 51t23.5 34t31 28t31 31.5t23.5 44.5t9.5 67q38 0 83 -74t45 -150z" />
-<glyph unicode="&#xf1fe;" horiz-adv-x="2048" d="M2048 0v-128h-2048v1536h128v-1408h1920zM1664 1024l256 -896h-1664v576l448 576l576 -576z" />
-<glyph unicode="&#xf200;" horiz-adv-x="1792" d="M768 646l546 -546q-106 -108 -247.5 -168t-298.5 -60q-209 0 -385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103v-762zM955 640h773q0 -157 -60 -298.5t-168 -247.5zM1664 768h-768v768q209 0 385.5 -103t279.5 -279.5t103 -385.5z" />
-<glyph unicode="&#xf201;" horiz-adv-x="2048" d="M2048 0v-128h-2048v1536h128v-1408h1920zM1920 1248v-435q0 -21 -19.5 -29.5t-35.5 7.5l-121 121l-633 -633q-10 -10 -23 -10t-23 10l-233 233l-416 -416l-192 192l585 585q10 10 23 10t23 -10l233 -233l464 464l-121 121q-16 16 -7.5 35.5t29.5 19.5h435q14 0 23 -9 t9 -23z" />
-<glyph unicode="&#xf202;" horiz-adv-x="1792" d="M1292 832q0 -6 10 -41q10 -29 25 -49.5t41 -34t44 -20t55 -16.5q325 -91 325 -332q0 -146 -105.5 -242.5t-254.5 -96.5q-59 0 -111.5 18.5t-91.5 45.5t-77 74.5t-63 87.5t-53.5 103.5t-43.5 103t-39.5 106.5t-35.5 95q-32 81 -61.5 133.5t-73.5 96.5t-104 64t-142 20 q-96 0 -183 -55.5t-138 -144.5t-51 -185q0 -160 106.5 -279.5t263.5 -119.5q177 0 258 95q56 63 83 116l84 -152q-15 -34 -44 -70l1 -1q-131 -152 -388 -152q-147 0 -269.5 79t-190.5 207.5t-68 274.5q0 105 43.5 206t116 176.5t172 121.5t204.5 46q87 0 159 -19t123.5 -50 t95 -80t72.5 -99t58.5 -117t50.5 -124.5t50 -130.5t55 -127q96 -200 233 -200q81 0 138.5 48.5t57.5 128.5q0 42 -19 72t-50.5 46t-72.5 31.5t-84.5 27t-87.5 34t-81 52t-65 82t-39 122.5q-3 16 -3 33q0 110 87.5 192t198.5 78q78 -3 120.5 -14.5t90.5 -53.5h-1 q12 -11 23 -24.5t26 -36t19 -27.5l-129 -99q-26 49 -54 70v1q-23 21 -97 21q-49 0 -84 -33t-35 -83z" />
-<glyph unicode="&#xf203;" d="M1432 484q0 173 -234 239q-35 10 -53 16.5t-38 25t-29 46.5q0 2 -2 8.5t-3 12t-1 7.5q0 36 24.5 59.5t60.5 23.5q54 0 71 -15h-1q20 -15 39 -51l93 71q-39 54 -49 64q-33 29 -67.5 39t-85.5 10q-80 0 -142 -57.5t-62 -137.5q0 -7 2 -23q16 -96 64.5 -140t148.5 -73 q29 -8 49 -15.5t45 -21.5t38.5 -34.5t13.5 -46.5v-5q1 -58 -40.5 -93t-100.5 -35q-97 0 -167 144q-23 47 -51.5 121.5t-48 125.5t-54 110.5t-74 95.5t-103.5 60.5t-147 24.5q-101 0 -192 -56t-144 -148t-50 -192v-1q4 -108 50.5 -199t133.5 -147.5t196 -56.5q186 0 279 110 q20 27 31 51l-60 109q-42 -80 -99 -116t-146 -36q-115 0 -191 87t-76 204q0 105 82 189t186 84q112 0 170 -53.5t104 -172.5q8 -21 25.5 -68.5t28.5 -76.5t31.5 -74.5t38.5 -74t45.5 -62.5t55.5 -53.5t66 -33t80 -13.5q107 0 183 69.5t76 174.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
-<glyph unicode="&#xf204;" horiz-adv-x="2048" d="M1152 640q0 104 -40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM1920 640q0 104 -40.5 198.5 t-109.5 163.5t-163.5 109.5t-198.5 40.5h-386q119 -90 188.5 -224t69.5 -288t-69.5 -288t-188.5 -224h386q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5zM2048 640q0 -130 -51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5 t-136.5 204t-51 248.5t51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5z" />
-<glyph unicode="&#xf205;" horiz-adv-x="2048" d="M0 640q0 130 51 248.5t136.5 204t204 136.5t248.5 51h768q130 0 248.5 -51t204 -136.5t136.5 -204t51 -248.5t-51 -248.5t-136.5 -204t-204 -136.5t-248.5 -51h-768q-130 0 -248.5 51t-204 136.5t-136.5 204t-51 248.5zM1408 128q104 0 198.5 40.5t163.5 109.5 t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5t-163.5 109.5t-198.5 40.5t-198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5z" />
-<glyph unicode="&#xf206;" horiz-adv-x="2304" d="M762 384h-314q-40 0 -57.5 35t6.5 67l188 251q-65 31 -137 31q-132 0 -226 -94t-94 -226t94 -226t226 -94q115 0 203 72.5t111 183.5zM576 512h186q-18 85 -75 148zM1056 512l288 384h-480l-99 -132q105 -103 126 -252h165zM2176 448q0 132 -94 226t-226 94 q-60 0 -121 -24l174 -260q15 -23 10 -49t-27 -40q-15 -11 -36 -11q-35 0 -53 29l-174 260q-93 -95 -93 -225q0 -132 94 -226t226 -94t226 94t94 226zM2304 448q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 97 39.5 183.5t109.5 149.5l-65 98l-353 -469 q-18 -26 -51 -26h-197q-23 -164 -149 -274t-294 -110q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q114 0 215 -55l137 183h-224q-26 0 -45 19t-19 45t19 45t45 19h384v-128h435l-85 128h-222q-26 0 -45 19t-19 45t19 45t45 19h256q33 0 53 -28l267 -400 q91 44 192 44q185 0 316.5 -131.5t131.5 -316.5z" />
-<glyph unicode="&#xf207;" d="M384 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1408 320q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1362 716l-72 384q-5 23 -22.5 37.5t-40.5 14.5 h-918q-23 0 -40.5 -14.5t-22.5 -37.5l-72 -384q-5 -30 14 -53t49 -23h1062q30 0 49 23t14 53zM1136 1328q0 20 -14 34t-34 14h-640q-20 0 -34 -14t-14 -34t14 -34t34 -14h640q20 0 34 14t14 34zM1536 603v-603h-128v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5v128h-768v-128q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5v128h-128v603q0 112 25 223l103 454q9 78 97.5 137t230 89t312.5 30t312.5 -30t230 -89t97.5 -137l105 -454q23 -102 23 -223z" />
-<glyph unicode="&#xf208;" horiz-adv-x="2048" d="M1463 704q0 -35 -25 -60.5t-61 -25.5h-702q-36 0 -61 25.5t-25 60.5t25 60.5t61 25.5h702q36 0 61 -25.5t25 -60.5zM1677 704q0 86 -23 170h-982q-36 0 -61 25t-25 60q0 36 25 61t61 25h908q-88 143 -235 227t-320 84q-177 0 -327.5 -87.5t-238 -237.5t-87.5 -327 q0 -86 23 -170h982q36 0 61 -25t25 -60q0 -36 -25 -61t-61 -25h-908q88 -143 235.5 -227t320.5 -84q132 0 253 51.5t208 139t139 208t52 253.5zM2048 959q0 -35 -25 -60t-61 -25h-131q17 -85 17 -170q0 -167 -65.5 -319.5t-175.5 -263t-262.5 -176t-319.5 -65.5 q-246 0 -448.5 133t-301.5 350h-189q-36 0 -61 25t-25 61q0 35 25 60t61 25h132q-17 85 -17 170q0 167 65.5 319.5t175.5 263t262.5 176t320.5 65.5q245 0 447.5 -133t301.5 -350h188q36 0 61 -25t25 -61z" />
-<glyph unicode="&#xf209;" horiz-adv-x="1280" d="M953 1158l-114 -328l117 -21q165 451 165 518q0 56 -38 56q-57 0 -130 -225zM654 471l33 -88q37 42 71 67l-33 5.5t-38.5 7t-32.5 8.5zM362 1367q0 -98 159 -521q18 10 49 10q15 0 75 -5l-121 351q-75 220 -123 220q-19 0 -29 -17.5t-10 -37.5zM283 608q0 -36 51.5 -119 t117.5 -153t100 -70q14 0 25.5 13t11.5 27q0 24 -32 102q-13 32 -32 72t-47.5 89t-61.5 81t-62 32q-20 0 -45.5 -27t-25.5 -47zM125 273q0 -41 25 -104q59 -145 183.5 -227t281.5 -82q227 0 382 170q152 169 152 427q0 43 -1 67t-11.5 62t-30.5 56q-56 49 -211.5 75.5 t-270.5 26.5q-37 0 -49 -11q-12 -5 -12 -35q0 -34 21.5 -60t55.5 -40t77.5 -23.5t87.5 -11.5t85 -4t70 0h23q24 0 40 -19q15 -19 19 -55q-28 -28 -96 -54q-61 -22 -93 -46q-64 -46 -108.5 -114t-44.5 -137q0 -31 18.5 -88.5t18.5 -87.5l-3 -12q-4 -12 -4 -14 q-137 10 -146 216q-8 -2 -41 -2q2 -7 2 -21q0 -53 -40.5 -89.5t-94.5 -36.5q-82 0 -166.5 78t-84.5 159q0 34 33 67q52 -64 60 -76q77 -104 133 -104q12 0 26.5 8.5t14.5 20.5q0 34 -87.5 145t-116.5 111q-43 0 -70 -44.5t-27 -90.5zM11 264q0 101 42.5 163t136.5 88 q-28 74 -28 104q0 62 61 123t122 61q29 0 70 -15q-163 462 -163 567q0 80 41 130.5t119 50.5q131 0 325 -581q6 -17 8 -23q6 16 29 79.5t43.5 118.5t54 127.5t64.5 123t70.5 86.5t76.5 36q71 0 112 -49t41 -122q0 -108 -159 -550q61 -15 100.5 -46t58.5 -78t26 -93.5 t7 -110.5q0 -150 -47 -280t-132 -225t-211 -150t-278 -55q-111 0 -223 42q-149 57 -258 191.5t-109 286.5z" />
-<glyph unicode="&#xf20a;" horiz-adv-x="2048" d="M785 528h207q-14 -158 -98.5 -248.5t-214.5 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-203q-5 64 -35.5 99t-81.5 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t40 -51.5t66 -18q95 0 109 139zM1497 528h206 q-14 -158 -98 -248.5t-214 -90.5q-162 0 -254.5 116t-92.5 316q0 194 93 311.5t233 117.5q148 0 232 -87t97 -247h-204q-4 64 -35 99t-81 35q-57 0 -88.5 -60.5t-31.5 -177.5q0 -48 5 -84t18 -69.5t39.5 -51.5t65.5 -18q49 0 76.5 38t33.5 101zM1856 647q0 207 -15.5 307 t-60.5 161q-6 8 -13.5 14t-21.5 15t-16 11q-86 63 -697 63q-625 0 -710 -63q-5 -4 -17.5 -11.5t-21 -14t-14.5 -14.5q-45 -60 -60 -159.5t-15 -308.5q0 -208 15 -307.5t60 -160.5q6 -8 15 -15t20.5 -14t17.5 -12q44 -33 239.5 -49t470.5 -16q610 0 697 65q5 4 17 11t20.5 14 t13.5 16q46 60 61 159t15 309zM2048 1408v-1536h-2048v1536h2048z" />
-<glyph unicode="&#xf20b;" d="M992 912v-496q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v496q0 112 -80 192t-192 80h-272v-1152q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v1344q0 14 9 23t23 9h464q135 0 249 -66.5t180.5 -180.5t66.5 -249zM1376 1376v-880q0 -135 -66.5 -249t-180.5 -180.5 t-249 -66.5h-464q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h160q14 0 23 -9t9 -23v-768h272q112 0 192 80t80 192v880q0 14 9 23t23 9h160q14 0 23 -9t9 -23z" />
-<glyph unicode="&#xf20c;" d="M1311 694v-114q0 -24 -13.5 -38t-37.5 -14h-202q-24 0 -38 14t-14 38v114q0 24 14 38t38 14h202q24 0 37.5 -14t13.5 -38zM821 464v250q0 53 -32.5 85.5t-85.5 32.5h-133q-68 0 -96 -52q-28 52 -96 52h-130q-53 0 -85.5 -32.5t-32.5 -85.5v-250q0 -22 21 -22h55 q22 0 22 22v230q0 24 13.5 38t38.5 14h94q24 0 38 -14t14 -38v-230q0 -22 21 -22h54q22 0 22 22v230q0 24 14 38t38 14h97q24 0 37.5 -14t13.5 -38v-230q0 -22 22 -22h55q21 0 21 22zM1410 560v154q0 53 -33 85.5t-86 32.5h-264q-53 0 -86 -32.5t-33 -85.5v-410 q0 -21 22 -21h55q21 0 21 21v180q31 -42 94 -42h191q53 0 86 32.5t33 85.5zM1536 1176v-1072q0 -96 -68 -164t-164 -68h-1072q-96 0 -164 68t-68 164v1072q0 96 68 164t164 68h1072q96 0 164 -68t68 -164z" />
-<glyph unicode="&#xf20d;" horiz-adv-x="1792" />
-<glyph unicode="&#xf20e;" horiz-adv-x="1792" />
-<glyph unicode="&#xf500;" horiz-adv-x="1792" />
-</font>
-</defs></svg> \ No newline at end of file
diff --git a/mitmproxy/webfonts/fontawesome-webfont.ttf b/mitmproxy/webfonts/fontawesome-webfont.ttf
deleted file mode 100644
index 96a3639c..00000000
--- a/mitmproxy/webfonts/fontawesome-webfont.ttf
+++ /dev/null
Binary files differ
diff --git a/mitmproxy/webfonts/fontawesome-webfont.woff b/mitmproxy/webfonts/fontawesome-webfont.woff
deleted file mode 100644
index 628b6a52..00000000
--- a/mitmproxy/webfonts/fontawesome-webfont.woff
+++ /dev/null
Binary files differ
diff --git a/netlib/basetypes.py b/netlib/basetypes.py
new file mode 100644
index 00000000..9d6c60ba
--- /dev/null
+++ b/netlib/basetypes.py
@@ -0,0 +1,34 @@
+import six
+import abc
+
+
+@six.add_metaclass(abc.ABCMeta)
+class Serializable(object):
+ """
+ Abstract Base Class that defines an API to save an object's state and restore it later on.
+ """
+
+ @classmethod
+ @abc.abstractmethod
+ def from_state(cls, state):
+ """
+ Create a new object from the given state.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def get_state(self):
+ """
+ Retrieve object state.
+ """
+ raise NotImplementedError()
+
+ @abc.abstractmethod
+ def set_state(self, state):
+ """
+ Set object state to the given state.
+ """
+ raise NotImplementedError()
+
+ def copy(self):
+ return self.from_state(self.get_state())
diff --git a/netlib/certutils.py b/netlib/certutils.py
index 34e01ed3..9eb41d03 100644
--- a/netlib/certutils.py
+++ b/netlib/certutils.py
@@ -12,7 +12,7 @@ from pyasn1.codec.der.decoder import decode
from pyasn1.error import PyAsn1Error
import OpenSSL
-from .utils import Serializable
+from netlib import basetypes
# Default expiry must not be too long: https://github.com/mitmproxy/mitmproxy/issues/815
@@ -364,7 +364,7 @@ class _GeneralNames(univ.SequenceOf):
constraint.ValueSizeConstraint(1, 1024)
-class SSLCert(Serializable):
+class SSLCert(basetypes.Serializable):
def __init__(self, cert):
"""
diff --git a/netlib/http/__init__.py b/netlib/http/__init__.py
index c4eb1d58..af95f4d0 100644
--- a/netlib/http/__init__.py
+++ b/netlib/http/__init__.py
@@ -1,14 +1,14 @@
from __future__ import absolute_import, print_function, division
-from .request import Request
-from .response import Response
-from .headers import Headers
-from .message import decoded
-from . import http1, http2, status_codes
+from netlib.http.request import Request
+from netlib.http.response import Response
+from netlib.http.headers import Headers, parse_content_type
+from netlib.http.message import decoded
+from netlib.http import http1, http2, status_codes, multipart
__all__ = [
"Request",
"Response",
- "Headers",
+ "Headers", "parse_content_type",
"decoded",
- "http1", "http2", "status_codes",
+ "http1", "http2", "status_codes", "multipart",
]
diff --git a/netlib/http/authentication.py b/netlib/http/authentication.py
index 6db70fdd..38ea46d6 100644
--- a/netlib/http/authentication.py
+++ b/netlib/http/authentication.py
@@ -1,5 +1,5 @@
from __future__ import (absolute_import, print_function, division)
-from argparse import Action, ArgumentTypeError
+import argparse
import binascii
@@ -124,7 +124,7 @@ class PassManSingleUser(PassMan):
return self.username == username and self.password == password_token
-class AuthAction(Action):
+class AuthAction(argparse.Action):
"""
Helper class to allow seamless integration int argparse. Example usage:
@@ -148,7 +148,7 @@ class SingleuserAuthAction(AuthAction):
def getPasswordManager(self, s):
if len(s.split(':')) != 2:
- raise ArgumentTypeError(
+ raise argparse.ArgumentTypeError(
"Invalid single-user specification. Please use the format username:password"
)
username, password = s.split(':')
diff --git a/netlib/http/cookies.py b/netlib/http/cookies.py
index 88c76870..768a85df 100644
--- a/netlib/http/cookies.py
+++ b/netlib/http/cookies.py
@@ -1,9 +1,8 @@
import collections
import re
-from email.utils import parsedate_tz, formatdate, mktime_tz
-from netlib.multidict import ImmutableMultiDict
-from .. import odict
+import email.utils
+from netlib import multidict
"""
A flexible module for cookie parsing and manipulation.
@@ -28,6 +27,7 @@ variants. Serialization follows RFC6265.
# TODO: Disallow LHS-only Cookie values
+
def _read_until(s, start, term):
"""
Read until one of the characters in term is reached.
@@ -167,7 +167,7 @@ def parse_set_cookie_headers(headers):
return ret
-class CookieAttrs(ImmutableMultiDict):
+class CookieAttrs(multidict.ImmutableMultiDict):
@staticmethod
def _kconv(key):
return key.lower()
@@ -243,10 +243,10 @@ def refresh_set_cookie_header(c, delta):
raise ValueError("Invalid Cookie")
if "expires" in attrs:
- e = parsedate_tz(attrs["expires"])
+ e = email.utils.parsedate_tz(attrs["expires"])
if e:
- f = mktime_tz(e) + delta
- attrs = attrs.with_set_all("expires", [formatdate(f)])
+ f = email.utils.mktime_tz(e) + delta
+ attrs = attrs.with_set_all("expires", [email.utils.formatdate(f)])
else:
# This can happen when the expires tag is invalid.
# reddit.com sends a an expires tag like this: "Thu, 31 Dec
diff --git a/netlib/http/headers.py b/netlib/http/headers.py
index 60d3f429..14888ea9 100644
--- a/netlib/http/headers.py
+++ b/netlib/http/headers.py
@@ -2,27 +2,28 @@ from __future__ import absolute_import, print_function, division
import re
-try:
- from collections.abc import MutableMapping
-except ImportError: # pragma: no cover
- from collections import MutableMapping # Workaround for Python < 3.3
-
import six
-from ..multidict import MultiDict
-from ..utils import always_bytes
+from netlib import multidict
+from netlib import strutils
# See also: http://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/
if six.PY2: # pragma: no cover
- _native = lambda x: x
- _always_bytes = lambda x: x
+ def _native(x):
+ return x
+
+ def _always_bytes(x):
+ return x
else:
# While headers _should_ be ASCII, it's not uncommon for certain headers to be utf-8 encoded.
- _native = lambda x: x.decode("utf-8", "surrogateescape")
- _always_bytes = lambda x: always_bytes(x, "utf-8", "surrogateescape")
+ def _native(x):
+ return x.decode("utf-8", "surrogateescape")
+
+ def _always_bytes(x):
+ return strutils.always_bytes(x, "utf-8", "surrogateescape")
-class Headers(MultiDict):
+class Headers(multidict.MultiDict):
"""
Header class which allows both convenient access to individual headers as well as
direct access to the underlying raw data. Provides a full dictionary interface.
@@ -70,7 +71,7 @@ class Headers(MultiDict):
For use with the "Set-Cookie" header, see :py:meth:`get_all`.
"""
- def __init__(self, fields=None, **headers):
+ def __init__(self, fields=(), **headers):
"""
Args:
fields: (optional) list of ``(name, value)`` header byte tuples,
@@ -91,7 +92,7 @@ class Headers(MultiDict):
headers = {
_always_bytes(name).replace(b"_", b"-"): _always_bytes(value)
for name, value in six.iteritems(headers)
- }
+ }
self.update(headers)
@staticmethod
@@ -174,3 +175,30 @@ class Headers(MultiDict):
fields.append([name, value])
self.fields = fields
return replacements
+
+
+def parse_content_type(c):
+ """
+ A simple parser for content-type values. Returns a (type, subtype,
+ parameters) tuple, where type and subtype are strings, and parameters
+ is a dict. If the string could not be parsed, return None.
+
+ E.g. the following string:
+
+ text/html; charset=UTF-8
+
+ Returns:
+
+ ("text", "html", {"charset": "UTF-8"})
+ """
+ parts = c.split(";", 1)
+ ts = parts[0].split("/", 1)
+ if len(ts) != 2:
+ return None
+ d = {}
+ if len(parts) == 2:
+ for i in parts[1].split(";"):
+ clause = i.split("=", 1)
+ if len(clause) == 2:
+ d[clause[0].strip()] = clause[1].strip()
+ return ts[0].lower(), ts[1].lower(), d
diff --git a/netlib/http/http1/assemble.py b/netlib/http/http1/assemble.py
index f06ad5a1..00d1563b 100644
--- a/netlib/http/http1/assemble.py
+++ b/netlib/http/http1/assemble.py
@@ -1,12 +1,12 @@
from __future__ import absolute_import, print_function, division
-from ... import utils
-import itertools
-from ...exceptions import HttpException
+from netlib import utils
+from netlib import exceptions
+
def assemble_request(request):
if request.content is None:
- raise HttpException("Cannot assemble flow with missing content")
+ raise exceptions.HttpException("Cannot assemble flow with missing content")
head = assemble_request_head(request)
body = b"".join(assemble_body(request.data.headers, [request.data.content]))
return head + body
@@ -20,7 +20,7 @@ def assemble_request_head(request):
def assemble_response(response):
if response.content is None:
- raise HttpException("Cannot assemble flow with missing content")
+ raise exceptions.HttpException("Cannot assemble flow with missing content")
head = assemble_response_head(response)
body = b"".join(assemble_body(response.data.headers, [response.data.content]))
return head + body
diff --git a/netlib/http/http1/read.py b/netlib/http/http1/read.py
index d30976bd..bf4c2f0c 100644
--- a/netlib/http/http1/read.py
+++ b/netlib/http/http1/read.py
@@ -3,9 +3,24 @@ import time
import sys
import re
-from ... import utils
-from ...exceptions import HttpReadDisconnect, HttpSyntaxException, HttpException, TcpDisconnect
-from .. import Request, Response, Headers
+from netlib.http import request
+from netlib.http import response
+from netlib.http import headers
+from netlib.http import url
+from netlib import utils
+from netlib import exceptions
+
+
+def get_header_tokens(headers, key):
+ """
+ Retrieve all tokens for a header key. A number of different headers
+ follow a pattern where each header line can containe comma-separated
+ tokens, and headers can be set multiple times.
+ """
+ if key not in headers:
+ return []
+ tokens = headers[key].split(",")
+ return [token.strip() for token in tokens]
def read_request(rfile, body_size_limit=None):
@@ -27,9 +42,9 @@ def read_request_head(rfile):
The HTTP request object (without body)
Raises:
- HttpReadDisconnect: No bytes can be read from rfile.
- HttpSyntaxException: The input is malformed HTTP.
- HttpException: Any other error occured.
+ exceptions.HttpReadDisconnect: No bytes can be read from rfile.
+ exceptions.HttpSyntaxException: The input is malformed HTTP.
+ exceptions.HttpException: Any other error occured.
"""
timestamp_start = time.time()
if hasattr(rfile, "reset_timestamps"):
@@ -42,7 +57,7 @@ def read_request_head(rfile):
# more accurate timestamp_start
timestamp_start = rfile.first_byte_timestamp
- return Request(
+ return request.Request(
form, method, scheme, host, port, path, http_version, headers, None, timestamp_start
)
@@ -66,9 +81,9 @@ def read_response_head(rfile):
The HTTP request object (without body)
Raises:
- HttpReadDisconnect: No bytes can be read from rfile.
- HttpSyntaxException: The input is malformed HTTP.
- HttpException: Any other error occured.
+ exceptions.HttpReadDisconnect: No bytes can be read from rfile.
+ exceptions.HttpSyntaxException: The input is malformed HTTP.
+ exceptions.HttpException: Any other error occured.
"""
timestamp_start = time.time()
@@ -82,7 +97,7 @@ def read_response_head(rfile):
# more accurate timestamp_start
timestamp_start = rfile.first_byte_timestamp
- return Response(http_version, status_code, message, headers, None, timestamp_start)
+ return response.Response(http_version, status_code, message, headers, None, timestamp_start)
def read_body(rfile, expected_size, limit=None, max_chunk_size=4096):
@@ -99,7 +114,7 @@ def read_body(rfile, expected_size, limit=None, max_chunk_size=4096):
A generator that yields byte chunks of the content.
Raises:
- HttpException, if an error occurs
+ exceptions.HttpException, if an error occurs
Caveats:
max_chunk_size is not considered if the transfer encoding is chunked.
@@ -114,7 +129,7 @@ def read_body(rfile, expected_size, limit=None, max_chunk_size=4096):
yield x
elif expected_size >= 0:
if limit is not None and expected_size > limit:
- raise HttpException(
+ raise exceptions.HttpException(
"HTTP Body too large. "
"Limit is {}, content length was advertised as {}".format(limit, expected_size)
)
@@ -123,7 +138,7 @@ def read_body(rfile, expected_size, limit=None, max_chunk_size=4096):
chunk_size = min(bytes_left, max_chunk_size)
content = rfile.read(chunk_size)
if len(content) < chunk_size:
- raise HttpException("Unexpected EOF")
+ raise exceptions.HttpException("Unexpected EOF")
yield content
bytes_left -= chunk_size
else:
@@ -137,7 +152,7 @@ def read_body(rfile, expected_size, limit=None, max_chunk_size=4096):
bytes_left -= chunk_size
not_done = rfile.read(1)
if not_done:
- raise HttpException("HTTP body too large. Limit is {}.".format(limit))
+ raise exceptions.HttpException("HTTP body too large. Limit is {}.".format(limit))
def connection_close(http_version, headers):
@@ -147,7 +162,7 @@ def connection_close(http_version, headers):
"""
# At first, check if we have an explicit Connection header.
if "connection" in headers:
- tokens = utils.get_header_tokens(headers, "connection")
+ tokens = get_header_tokens(headers, "connection")
if "close" in tokens:
return True
elif "keep-alive" in tokens:
@@ -167,7 +182,7 @@ def expected_http_body_size(request, response=None):
- -1, if all data should be read until end of stream.
Raises:
- HttpSyntaxException, if the content length header is invalid
+ exceptions.HttpSyntaxException, if the content length header is invalid
"""
# Determine response size according to
# http://tools.ietf.org/html/rfc7230#section-3.3
@@ -202,7 +217,7 @@ def expected_http_body_size(request, response=None):
raise ValueError()
return size
except ValueError:
- raise HttpSyntaxException("Unparseable Content Length")
+ raise exceptions.HttpSyntaxException("Unparseable Content Length")
if is_request:
return 0
return -1
@@ -214,19 +229,19 @@ def _get_first_line(rfile):
if line == b"\r\n" or line == b"\n":
# Possible leftover from previous message
line = rfile.readline()
- except TcpDisconnect:
- raise HttpReadDisconnect("Remote disconnected")
+ except exceptions.TcpDisconnect:
+ raise exceptions.HttpReadDisconnect("Remote disconnected")
if not line:
- raise HttpReadDisconnect("Remote disconnected")
+ raise exceptions.HttpReadDisconnect("Remote disconnected")
return line.strip()
def _read_request_line(rfile):
try:
line = _get_first_line(rfile)
- except HttpReadDisconnect:
+ except exceptions.HttpReadDisconnect:
# We want to provide a better error message.
- raise HttpReadDisconnect("Client disconnected")
+ raise exceptions.HttpReadDisconnect("Client disconnected")
try:
method, path, http_version = line.split(b" ")
@@ -240,11 +255,11 @@ def _read_request_line(rfile):
scheme, path = None, None
else:
form = "absolute"
- scheme, host, port, path = utils.parse_url(path)
+ scheme, host, port, path = url.parse(path)
_check_http_version(http_version)
except ValueError:
- raise HttpSyntaxException("Bad HTTP request line: {}".format(line))
+ raise exceptions.HttpSyntaxException("Bad HTTP request line: {}".format(line))
return form, method, scheme, host, port, path, http_version
@@ -263,7 +278,7 @@ def _parse_authority_form(hostport):
if not utils.is_valid_host(host) or not utils.is_valid_port(port):
raise ValueError()
except ValueError:
- raise HttpSyntaxException("Invalid host specification: {}".format(hostport))
+ raise exceptions.HttpSyntaxException("Invalid host specification: {}".format(hostport))
return host, port
@@ -271,9 +286,9 @@ def _parse_authority_form(hostport):
def _read_response_line(rfile):
try:
line = _get_first_line(rfile)
- except HttpReadDisconnect:
+ except exceptions.HttpReadDisconnect:
# We want to provide a better error message.
- raise HttpReadDisconnect("Server disconnected")
+ raise exceptions.HttpReadDisconnect("Server disconnected")
try:
@@ -286,14 +301,14 @@ def _read_response_line(rfile):
_check_http_version(http_version)
except ValueError:
- raise HttpSyntaxException("Bad HTTP response line: {}".format(line))
+ raise exceptions.HttpSyntaxException("Bad HTTP response line: {}".format(line))
return http_version, status_code, message
def _check_http_version(http_version):
if not re.match(br"^HTTP/\d\.\d$", http_version):
- raise HttpSyntaxException("Unknown HTTP version: {}".format(http_version))
+ raise exceptions.HttpSyntaxException("Unknown HTTP version: {}".format(http_version))
def _read_headers(rfile):
@@ -305,7 +320,7 @@ def _read_headers(rfile):
A headers object
Raises:
- HttpSyntaxException
+ exceptions.HttpSyntaxException
"""
ret = []
while True:
@@ -314,7 +329,7 @@ def _read_headers(rfile):
break
if line[0] in b" \t":
if not ret:
- raise HttpSyntaxException("Invalid headers")
+ raise exceptions.HttpSyntaxException("Invalid headers")
# continued header
ret[-1] = (ret[-1][0], ret[-1][1] + b'\r\n ' + line.strip())
else:
@@ -325,8 +340,8 @@ def _read_headers(rfile):
raise ValueError()
ret.append((name, value))
except ValueError:
- raise HttpSyntaxException("Invalid headers")
- return Headers(ret)
+ raise exceptions.HttpSyntaxException("Invalid headers")
+ return headers.Headers(ret)
def _read_chunked(rfile, limit=sys.maxsize):
@@ -341,22 +356,22 @@ def _read_chunked(rfile, limit=sys.maxsize):
while True:
line = rfile.readline(128)
if line == b"":
- raise HttpException("Connection closed prematurely")
+ raise exceptions.HttpException("Connection closed prematurely")
if line != b"\r\n" and line != b"\n":
try:
length = int(line, 16)
except ValueError:
- raise HttpSyntaxException("Invalid chunked encoding length: {}".format(line))
+ raise exceptions.HttpSyntaxException("Invalid chunked encoding length: {}".format(line))
total += length
if total > limit:
- raise HttpException(
+ raise exceptions.HttpException(
"HTTP Body too large. Limit is {}, "
"chunked content longer than {}".format(limit, total)
)
chunk = rfile.read(length)
suffix = rfile.readline(5)
if suffix != b"\r\n":
- raise HttpSyntaxException("Malformed chunked body")
+ raise exceptions.HttpSyntaxException("Malformed chunked body")
if length == 0:
return
yield chunk
diff --git a/netlib/http/http2/__init__.py b/netlib/http/http2/__init__.py
index 7043d36f..633e6a20 100644
--- a/netlib/http/http2/__init__.py
+++ b/netlib/http/http2/__init__.py
@@ -1,6 +1,8 @@
from __future__ import absolute_import, print_function, division
from .connections import HTTP2Protocol
+from netlib.http.http2 import framereader
__all__ = [
- "HTTP2Protocol"
+ "HTTP2Protocol",
+ "framereader",
]
diff --git a/netlib/http/http2/connections.py b/netlib/http/http2/connections.py
index 6643b6b9..8667d370 100644
--- a/netlib/http/http2/connections.py
+++ b/netlib/http/http2/connections.py
@@ -2,11 +2,15 @@ from __future__ import (absolute_import, print_function, division)
import itertools
import time
-from hpack.hpack import Encoder, Decoder
-from ... import utils
-from .. import Headers, Response, Request
+import hyperframe.frame
-from hyperframe import frame
+from hpack.hpack import Encoder, Decoder
+from netlib import utils
+from netlib.http import url
+import netlib.http.headers
+import netlib.http.response
+import netlib.http.request
+from netlib.http.http2 import framereader
class TCPHandler(object):
@@ -38,12 +42,12 @@ class HTTP2Protocol(object):
CLIENT_CONNECTION_PREFACE = b'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'
HTTP2_DEFAULT_SETTINGS = {
- frame.SettingsFrame.HEADER_TABLE_SIZE: 4096,
- frame.SettingsFrame.ENABLE_PUSH: 1,
- frame.SettingsFrame.MAX_CONCURRENT_STREAMS: None,
- frame.SettingsFrame.INITIAL_WINDOW_SIZE: 2 ** 16 - 1,
- frame.SettingsFrame.MAX_FRAME_SIZE: 2 ** 14,
- frame.SettingsFrame.MAX_HEADER_LIST_SIZE: None,
+ hyperframe.frame.SettingsFrame.HEADER_TABLE_SIZE: 4096,
+ hyperframe.frame.SettingsFrame.ENABLE_PUSH: 1,
+ hyperframe.frame.SettingsFrame.MAX_CONCURRENT_STREAMS: None,
+ hyperframe.frame.SettingsFrame.INITIAL_WINDOW_SIZE: 2 ** 16 - 1,
+ hyperframe.frame.SettingsFrame.MAX_FRAME_SIZE: 2 ** 14,
+ hyperframe.frame.SettingsFrame.MAX_HEADER_LIST_SIZE: None,
}
def __init__(
@@ -98,6 +102,11 @@ class HTTP2Protocol(object):
method = headers.get(':method', 'GET')
scheme = headers.get(':scheme', 'https')
path = headers.get(':path', '/')
+
+ headers.clear(":method")
+ headers.clear(":scheme")
+ headers.clear(":path")
+
host = None
port = None
@@ -112,7 +121,7 @@ class HTTP2Protocol(object):
else:
first_line_format = "absolute"
# FIXME: verify if path or :host contains what we need
- scheme, host, port, _ = utils.parse_url(path)
+ scheme, host, port, _ = url.parse(path)
scheme = scheme.decode('ascii')
host = host.decode('ascii')
@@ -122,7 +131,7 @@ class HTTP2Protocol(object):
port = 80 if scheme == 'http' else 443
port = int(port)
- request = Request(
+ request = netlib.http.request.Request(
first_line_format,
method.encode('ascii'),
scheme.encode('ascii'),
@@ -170,7 +179,7 @@ class HTTP2Protocol(object):
else:
timestamp_end = None
- response = Response(
+ response = netlib.http.response.Response(
b"HTTP/2.0",
int(headers.get(':status', 502)),
b'',
@@ -184,15 +193,15 @@ class HTTP2Protocol(object):
return response
def assemble(self, message):
- if isinstance(message, Request):
+ if isinstance(message, netlib.http.request.Request):
return self.assemble_request(message)
- elif isinstance(message, Response):
+ elif isinstance(message, netlib.http.response.Response):
return self.assemble_response(message)
else:
raise ValueError("HTTP message not supported.")
def assemble_request(self, request):
- assert isinstance(request, Request)
+ assert isinstance(request, netlib.http.request.Request)
authority = self.tcp_handler.sni if self.tcp_handler.sni else self.tcp_handler.address.host
if self.tcp_handler.address.port != 443:
@@ -202,12 +211,9 @@ class HTTP2Protocol(object):
if ':authority' not in headers:
headers.insert(0, b':authority', authority.encode('ascii'))
- if ':scheme' not in headers:
- headers.insert(0, b':scheme', request.scheme.encode('ascii'))
- if ':path' not in headers:
- headers.insert(0, b':path', request.path.encode('ascii'))
- if ':method' not in headers:
- headers.insert(0, b':method', request.method.encode('ascii'))
+ headers.insert(0, b':scheme', request.scheme.encode('ascii'))
+ headers.insert(0, b':path', request.path.encode('ascii'))
+ headers.insert(0, b':method', request.method.encode('ascii'))
if hasattr(request, 'stream_id'):
stream_id = request.stream_id
@@ -219,7 +225,7 @@ class HTTP2Protocol(object):
self._create_body(request.body, stream_id)))
def assemble_response(self, response):
- assert isinstance(response, Response)
+ assert isinstance(response, netlib.http.response.Response)
headers = response.headers.copy()
@@ -251,9 +257,9 @@ class HTTP2Protocol(object):
magic = self.tcp_handler.rfile.safe_read(magic_length)
assert magic == self.CLIENT_CONNECTION_PREFACE
- frm = frame.SettingsFrame(settings={
- frame.SettingsFrame.ENABLE_PUSH: 0,
- frame.SettingsFrame.MAX_CONCURRENT_STREAMS: 1,
+ frm = hyperframe.frame.SettingsFrame(settings={
+ hyperframe.frame.SettingsFrame.ENABLE_PUSH: 0,
+ hyperframe.frame.SettingsFrame.MAX_CONCURRENT_STREAMS: 1,
})
self.send_frame(frm, hide=True)
self._receive_settings(hide=True)
@@ -264,7 +270,7 @@ class HTTP2Protocol(object):
self.tcp_handler.wfile.write(self.CLIENT_CONNECTION_PREFACE)
- self.send_frame(frame.SettingsFrame(), hide=True)
+ self.send_frame(hyperframe.frame.SettingsFrame(), hide=True)
self._receive_settings(hide=True) # server announces own settings
self._receive_settings(hide=True) # server acks my settings
@@ -277,18 +283,18 @@ class HTTP2Protocol(object):
def read_frame(self, hide=False):
while True:
- frm = utils.http2_read_frame(self.tcp_handler.rfile)
+ frm = framereader.http2_read_frame(self.tcp_handler.rfile)
if not hide and self.dump_frames: # pragma no cover
print(frm.human_readable("<<"))
- if isinstance(frm, frame.PingFrame):
- raw_bytes = frame.PingFrame(flags=['ACK'], payload=frm.payload).serialize()
+ if isinstance(frm, hyperframe.frame.PingFrame):
+ raw_bytes = hyperframe.frame.PingFrame(flags=['ACK'], payload=frm.payload).serialize()
self.tcp_handler.wfile.write(raw_bytes)
self.tcp_handler.wfile.flush()
continue
- if isinstance(frm, frame.SettingsFrame) and 'ACK' not in frm.flags:
+ if isinstance(frm, hyperframe.frame.SettingsFrame) and 'ACK' not in frm.flags:
self._apply_settings(frm.settings, hide)
- if isinstance(frm, frame.DataFrame) and frm.flow_controlled_length > 0:
+ if isinstance(frm, hyperframe.frame.DataFrame) and frm.flow_controlled_length > 0:
self._update_flow_control_window(frm.stream_id, frm.flow_controlled_length)
return frm
@@ -300,7 +306,7 @@ class HTTP2Protocol(object):
return True
def _handle_unexpected_frame(self, frm):
- if isinstance(frm, frame.SettingsFrame):
+ if isinstance(frm, hyperframe.frame.SettingsFrame):
return
if self.unhandled_frame_cb:
self.unhandled_frame_cb(frm)
@@ -308,7 +314,7 @@ class HTTP2Protocol(object):
def _receive_settings(self, hide=False):
while True:
frm = self.read_frame(hide)
- if isinstance(frm, frame.SettingsFrame):
+ if isinstance(frm, hyperframe.frame.SettingsFrame):
break
else:
self._handle_unexpected_frame(frm)
@@ -332,31 +338,31 @@ class HTTP2Protocol(object):
old_value = '-'
self.http2_settings[setting] = value
- frm = frame.SettingsFrame(flags=['ACK'])
+ frm = hyperframe.frame.SettingsFrame(flags=['ACK'])
self.send_frame(frm, hide)
def _update_flow_control_window(self, stream_id, increment):
- frm = frame.WindowUpdateFrame(stream_id=0, window_increment=increment)
+ frm = hyperframe.frame.WindowUpdateFrame(stream_id=0, window_increment=increment)
self.send_frame(frm)
- frm = frame.WindowUpdateFrame(stream_id=stream_id, window_increment=increment)
+ frm = hyperframe.frame.WindowUpdateFrame(stream_id=stream_id, window_increment=increment)
self.send_frame(frm)
def _create_headers(self, headers, stream_id, end_stream=True):
def frame_cls(chunks):
for i in chunks:
if i == 0:
- yield frame.HeadersFrame, i
+ yield hyperframe.frame.HeadersFrame, i
else:
- yield frame.ContinuationFrame, i
+ yield hyperframe.frame.ContinuationFrame, i
header_block_fragment = self.encoder.encode(headers.fields)
- chunk_size = self.http2_settings[frame.SettingsFrame.MAX_FRAME_SIZE]
+ chunk_size = self.http2_settings[hyperframe.frame.SettingsFrame.MAX_FRAME_SIZE]
chunks = range(0, len(header_block_fragment), chunk_size)
frms = [frm_cls(
flags=[],
stream_id=stream_id,
- data=header_block_fragment[i:i+chunk_size]) for frm_cls, i in frame_cls(chunks)]
+ data=header_block_fragment[i:i + chunk_size]) for frm_cls, i in frame_cls(chunks)]
frms[-1].flags.add('END_HEADERS')
if end_stream:
@@ -372,12 +378,12 @@ class HTTP2Protocol(object):
if body is None or len(body) == 0:
return b''
- chunk_size = self.http2_settings[frame.SettingsFrame.MAX_FRAME_SIZE]
+ chunk_size = self.http2_settings[hyperframe.frame.SettingsFrame.MAX_FRAME_SIZE]
chunks = range(0, len(body), chunk_size)
- frms = [frame.DataFrame(
+ frms = [hyperframe.frame.DataFrame(
flags=[],
stream_id=stream_id,
- data=body[i:i+chunk_size]) for i in chunks]
+ data=body[i:i + chunk_size]) for i in chunks]
frms[-1].flags.add('END_STREAM')
if self.dump_frames: # pragma no cover
@@ -398,7 +404,7 @@ class HTTP2Protocol(object):
while True:
frm = self.read_frame()
if (
- (isinstance(frm, frame.HeadersFrame) or isinstance(frm, frame.ContinuationFrame)) and
+ (isinstance(frm, hyperframe.frame.HeadersFrame) or isinstance(frm, hyperframe.frame.ContinuationFrame)) and
(stream_id is None or frm.stream_id == stream_id)
):
stream_id = frm.stream_id
@@ -412,14 +418,14 @@ class HTTP2Protocol(object):
while body_expected:
frm = self.read_frame()
- if isinstance(frm, frame.DataFrame) and frm.stream_id == stream_id:
+ if isinstance(frm, hyperframe.frame.DataFrame) and frm.stream_id == stream_id:
body += frm.data
if 'END_STREAM' in frm.flags:
break
else:
self._handle_unexpected_frame(frm)
- headers = Headers(
+ headers = netlib.http.headers.Headers(
(k.encode('ascii'), v.encode('ascii')) for k, v in self.decoder.decode(header_blocks)
)
diff --git a/netlib/http/http2/framereader.py b/netlib/http/http2/framereader.py
new file mode 100644
index 00000000..d45be646
--- /dev/null
+++ b/netlib/http/http2/framereader.py
@@ -0,0 +1,21 @@
+import codecs
+
+import hyperframe
+
+
+def http2_read_raw_frame(rfile):
+ header = rfile.safe_read(9)
+ length = int(codecs.encode(header[:3], 'hex_codec'), 16)
+
+ if length == 4740180:
+ raise ValueError("Length field looks more like HTTP/1.1: %s" % rfile.peek(20))
+
+ body = rfile.safe_read(length)
+ return [header, body]
+
+
+def http2_read_frame(rfile):
+ header, body = http2_read_raw_frame(rfile)
+ frame, length = hyperframe.frame.Frame.parse_frame_header(header)
+ frame.parse_body(memoryview(body))
+ return frame
diff --git a/netlib/http/message.py b/netlib/http/message.py
index 028f43a1..b633b671 100644
--- a/netlib/http/message.py
+++ b/netlib/http/message.py
@@ -4,20 +4,25 @@ import warnings
import six
-from ..multidict import MultiDict
-from .headers import Headers
-from .. import encoding, utils
+from netlib import encoding, strutils, basetypes
+from netlib.http import headers
if six.PY2: # pragma: no cover
- _native = lambda x: x
- _always_bytes = lambda x: x
+ def _native(x):
+ return x
+
+ def _always_bytes(x):
+ return x
else:
- # While the HTTP head _should_ be ASCII, it's not uncommon for certain headers to be utf-8 encoded.
- _native = lambda x: x.decode("utf-8", "surrogateescape")
- _always_bytes = lambda x: utils.always_bytes(x, "utf-8", "surrogateescape")
+ # While headers _should_ be ASCII, it's not uncommon for certain headers to be utf-8 encoded.
+ def _native(x):
+ return x.decode("utf-8", "surrogateescape")
+
+ def _always_bytes(x):
+ return strutils.always_bytes(x, "utf-8", "surrogateescape")
-class MessageData(utils.Serializable):
+class MessageData(basetypes.Serializable):
def __eq__(self, other):
if isinstance(other, MessageData):
return self.__dict__ == other.__dict__
@@ -32,7 +37,7 @@ class MessageData(utils.Serializable):
def set_state(self, state):
for k, v in state.items():
if k == "headers":
- v = Headers.from_state(v)
+ v = headers.Headers.from_state(v)
setattr(self, k, v)
def get_state(self):
@@ -42,11 +47,11 @@ class MessageData(utils.Serializable):
@classmethod
def from_state(cls, state):
- state["headers"] = Headers.from_state(state["headers"])
+ state["headers"] = headers.Headers.from_state(state["headers"])
return cls(**state)
-class Message(utils.Serializable):
+class Message(basetypes.Serializable):
def __eq__(self, other):
if isinstance(other, Message):
return self.data == other.data
@@ -66,7 +71,7 @@ class Message(utils.Serializable):
@classmethod
def from_state(cls, state):
- state["headers"] = Headers.from_state(state["headers"])
+ state["headers"] = headers.Headers.from_state(state["headers"])
return cls(**state)
@property
@@ -195,7 +200,7 @@ class Message(utils.Serializable):
replacements = 0
if self.content:
with decoded(self):
- self.content, replacements = utils.safe_subn(
+ self.content, replacements = strutils.safe_subn(
pattern, repl, self.content, flags=flags
)
replacements += self.headers.replace(pattern, repl, flags)
diff --git a/netlib/http/multipart.py b/netlib/http/multipart.py
new file mode 100644
index 00000000..536b2809
--- /dev/null
+++ b/netlib/http/multipart.py
@@ -0,0 +1,32 @@
+import re
+
+from netlib.http import headers
+
+
+def decode(hdrs, content):
+ """
+ Takes a multipart boundary encoded string and returns list of (key, value) tuples.
+ """
+ v = hdrs.get("content-type")
+ if v:
+ v = headers.parse_content_type(v)
+ if not v:
+ return []
+ try:
+ boundary = v[2]["boundary"].encode("ascii")
+ except (KeyError, UnicodeError):
+ return []
+
+ rx = re.compile(br'\bname="([^"]+)"')
+ r = []
+
+ for i in content.split(b"--" + boundary):
+ parts = i.splitlines()
+ if len(parts) > 1 and parts[0][0:2] != b"--":
+ match = rx.search(parts[1])
+ if match:
+ key = match.group(1)
+ value = b"".join(parts[3 + parts[2:].index(b""):])
+ r.append((key, value))
+ return r
+ return []
diff --git a/netlib/http/request.py b/netlib/http/request.py
index 056a2d93..91d5f020 100644
--- a/netlib/http/request.py
+++ b/netlib/http/request.py
@@ -1,18 +1,18 @@
from __future__ import absolute_import, print_function, division
import re
-import warnings
import six
from six.moves import urllib
-from netlib import utils
+from netlib import encoding
+from netlib import multidict
+from netlib import strutils
+from netlib.http import multipart
from netlib.http import cookies
-from netlib.odict import ODict
-from .. import encoding
-from ..multidict import MultiDictView
-from .headers import Headers
-from .message import Message, _native, _always_bytes, MessageData
+from netlib.http import headers as nheaders
+from netlib.http import message
+import netlib.http.url
# This regex extracts & splits the host header into host and port.
# Handles the edge case of IPv6 addresses containing colons.
@@ -20,11 +20,11 @@ from .message import Message, _native, _always_bytes, MessageData
host_header_re = re.compile(r"^(?P<host>[^:]+|\[.+\])(?::(?P<port>\d+))?$")
-class RequestData(MessageData):
- def __init__(self, first_line_format, method, scheme, host, port, path, http_version, headers=None, content=None,
+class RequestData(message.MessageData):
+ def __init__(self, first_line_format, method, scheme, host, port, path, http_version, headers=(), content=None,
timestamp_start=None, timestamp_end=None):
- if not isinstance(headers, Headers):
- headers = Headers(headers)
+ if not isinstance(headers, nheaders.Headers):
+ headers = nheaders.Headers(headers)
self.first_line_format = first_line_format
self.method = method
@@ -39,7 +39,7 @@ class RequestData(MessageData):
self.timestamp_end = timestamp_end
-class Request(Message):
+class Request(message.Message):
"""
An HTTP request.
"""
@@ -67,7 +67,7 @@ class Request(Message):
"""
# TODO: Proper distinction between text and bytes.
c = super(Request, self).replace(pattern, repl, flags)
- self.path, pc = utils.safe_subn(
+ self.path, pc = strutils.safe_subn(
pattern, repl, self.path, flags=flags
)
c += pc
@@ -91,22 +91,22 @@ class Request(Message):
"""
HTTP request method, e.g. "GET".
"""
- return _native(self.data.method).upper()
+ return message._native(self.data.method).upper()
@method.setter
def method(self, method):
- self.data.method = _always_bytes(method)
+ self.data.method = message._always_bytes(method)
@property
def scheme(self):
"""
HTTP request scheme, which should be "http" or "https".
"""
- return _native(self.data.scheme)
+ return message._native(self.data.scheme)
@scheme.setter
def scheme(self, scheme):
- self.data.scheme = _always_bytes(scheme)
+ self.data.scheme = message._always_bytes(scheme)
@property
def host(self):
@@ -168,11 +168,11 @@ class Request(Message):
if self.data.path is None:
return None
else:
- return _native(self.data.path)
+ return message._native(self.data.path)
@path.setter
def path(self, path):
- self.data.path = _always_bytes(path)
+ self.data.path = message._always_bytes(path)
@property
def url(self):
@@ -181,11 +181,11 @@ class Request(Message):
"""
if self.first_line_format == "authority":
return "%s:%d" % (self.host, self.port)
- return utils.unparse_url(self.scheme, self.host, self.port, self.path)
+ return netlib.http.url.unparse(self.scheme, self.host, self.port, self.path)
@url.setter
def url(self, url):
- self.scheme, self.host, self.port, self.path = utils.parse_url(url)
+ self.scheme, self.host, self.port, self.path = netlib.http.url.parse(url)
def _parse_host_header(self):
"""Extract the host and port from Host header"""
@@ -221,28 +221,28 @@ class Request(Message):
"""
if self.first_line_format == "authority":
return "%s:%d" % (self.pretty_host, self.port)
- return utils.unparse_url(self.scheme, self.pretty_host, self.port, self.path)
+ return netlib.http.url.unparse(self.scheme, self.pretty_host, self.port, self.path)
@property
def query(self):
- # type: () -> MultiDictView
+ # type: () -> multidict.MultiDictView
"""
The request query string as an :py:class:`MultiDictView` object.
"""
- return MultiDictView(
+ return multidict.MultiDictView(
self._get_query,
self._set_query
)
def _get_query(self):
_, _, _, _, query, _ = urllib.parse.urlparse(self.url)
- return tuple(utils.urldecode(query))
+ return tuple(netlib.http.url.decode(query))
def _set_query(self, value):
- query = utils.urlencode(value)
+ query = netlib.http.url.encode(value)
scheme, netloc, path, params, _, fragment = urllib.parse.urlparse(self.url)
- _, _, _, self.path = utils.parse_url(
- urllib.parse.urlunparse([scheme, netloc, path, params, query, fragment]))
+ _, _, _, self.path = netlib.http.url.parse(
+ urllib.parse.urlunparse([scheme, netloc, path, params, query, fragment]))
@query.setter
def query(self, value):
@@ -250,13 +250,13 @@ class Request(Message):
@property
def cookies(self):
- # type: () -> MultiDictView
+ # type: () -> multidict.MultiDictView
"""
The request cookies.
- An empty :py:class:`MultiDictView` object if the cookie monster ate them all.
+ An empty :py:class:`multidict.MultiDictView` object if the cookie monster ate them all.
"""
- return MultiDictView(
+ return multidict.MultiDictView(
self._get_cookies,
self._set_cookies
)
@@ -289,8 +289,8 @@ class Request(Message):
components = map(lambda x: urllib.parse.quote(x, safe=""), components)
path = "/" + "/".join(components)
scheme, netloc, _, params, query, fragment = urllib.parse.urlparse(self.url)
- _, _, _, self.path = utils.parse_url(
- urllib.parse.urlunparse([scheme, netloc, path, params, query, fragment]))
+ _, _, _, self.path = netlib.http.url.parse(
+ urllib.parse.urlunparse([scheme, netloc, path, params, query, fragment]))
def anticache(self):
"""
@@ -329,11 +329,11 @@ class Request(Message):
@property
def urlencoded_form(self):
"""
- The URL-encoded form data as an :py:class:`MultiDictView` object.
- An empty MultiDictView if the content-type indicates non-form data
+ The URL-encoded form data as an :py:class:`multidict.MultiDictView` object.
+ An empty multidict.MultiDictView if the content-type indicates non-form data
or the content could not be parsed.
"""
- return MultiDictView(
+ return multidict.MultiDictView(
self._get_urlencoded_form,
self._set_urlencoded_form
)
@@ -341,7 +341,7 @@ class Request(Message):
def _get_urlencoded_form(self):
is_valid_content_type = "application/x-www-form-urlencoded" in self.headers.get("content-type", "").lower()
if is_valid_content_type:
- return tuple(utils.urldecode(self.content))
+ return tuple(netlib.http.url.decode(self.content))
return ()
def _set_urlencoded_form(self, value):
@@ -350,7 +350,7 @@ class Request(Message):
This will overwrite the existing content if there is one.
"""
self.headers["content-type"] = "application/x-www-form-urlencoded"
- self.content = utils.urlencode(value)
+ self.content = netlib.http.url.encode(value)
@urlencoded_form.setter
def urlencoded_form(self, value):
@@ -362,7 +362,7 @@ class Request(Message):
The multipart form data as an :py:class:`MultipartFormDict` object.
None if the content-type indicates non-form data.
"""
- return MultiDictView(
+ return multidict.MultiDictView(
self._get_multipart_form,
self._set_multipart_form
)
@@ -370,7 +370,7 @@ class Request(Message):
def _get_multipart_form(self):
is_valid_content_type = "multipart/form-data" in self.headers.get("content-type", "").lower()
if is_valid_content_type:
- return utils.multipartdecode(self.headers, self.content)
+ return multipart.decode(self.headers, self.content)
return ()
def _set_multipart_form(self, value):
diff --git a/netlib/http/response.py b/netlib/http/response.py
index 7d272e10..44b58be6 100644
--- a/netlib/http/response.py
+++ b/netlib/http/response.py
@@ -3,18 +3,18 @@ from __future__ import absolute_import, print_function, division
from email.utils import parsedate_tz, formatdate, mktime_tz
import time
-from . import cookies
-from .headers import Headers
-from .message import Message, _native, _always_bytes, MessageData
-from ..multidict import MultiDictView
-from .. import utils
+from netlib.http import cookies
+from netlib.http import headers as nheaders
+from netlib.http import message
+from netlib import multidict
+from netlib import human
-class ResponseData(MessageData):
- def __init__(self, http_version, status_code, reason=None, headers=None, content=None,
+class ResponseData(message.MessageData):
+ def __init__(self, http_version, status_code, reason=None, headers=(), content=None,
timestamp_start=None, timestamp_end=None):
- if not isinstance(headers, Headers):
- headers = Headers(headers)
+ if not isinstance(headers, nheaders.Headers):
+ headers = nheaders.Headers(headers)
self.http_version = http_version
self.status_code = status_code
@@ -25,7 +25,7 @@ class ResponseData(MessageData):
self.timestamp_end = timestamp_end
-class Response(Message):
+class Response(message.Message):
"""
An HTTP response.
"""
@@ -36,7 +36,7 @@ class Response(Message):
if self.content:
details = "{}, {}".format(
self.headers.get("content-type", "unknown content type"),
- utils.pretty_size(len(self.content))
+ human.pretty_size(len(self.content))
)
else:
details = "no content"
@@ -63,17 +63,17 @@ class Response(Message):
HTTP Reason Phrase, e.g. "Not Found".
This is always :py:obj:`None` for HTTP2 requests, because HTTP2 responses do not contain a reason phrase.
"""
- return _native(self.data.reason)
+ return message._native(self.data.reason)
@reason.setter
def reason(self, reason):
- self.data.reason = _always_bytes(reason)
+ self.data.reason = message._always_bytes(reason)
@property
def cookies(self):
- # type: () -> MultiDictView
+ # type: () -> multidict.MultiDictView
"""
- The response cookies. A possibly empty :py:class:`MultiDictView`, where the keys are
+ The response cookies. A possibly empty :py:class:`multidict.MultiDictView`, where the keys are
cookie name strings, and values are (value, attr) tuples. Value is a string, and attr is
an ODictCaseless containing cookie attributes. Within attrs, unary attributes (e.g. HTTPOnly)
are indicated by a Null value.
@@ -81,7 +81,7 @@ class Response(Message):
Caveats:
Updating the attr
"""
- return MultiDictView(
+ return multidict.MultiDictView(
self._get_cookies,
self._set_cookies
)
diff --git a/netlib/http/url.py b/netlib/http/url.py
new file mode 100644
index 00000000..5d461387
--- /dev/null
+++ b/netlib/http/url.py
@@ -0,0 +1,96 @@
+import six
+from six.moves import urllib
+
+from netlib import utils
+
+
+# PY2 workaround
+def decode_parse_result(result, enc):
+ if hasattr(result, "decode"):
+ return result.decode(enc)
+ else:
+ return urllib.parse.ParseResult(*[x.decode(enc) for x in result])
+
+
+# PY2 workaround
+def encode_parse_result(result, enc):
+ if hasattr(result, "encode"):
+ return result.encode(enc)
+ else:
+ return urllib.parse.ParseResult(*[x.encode(enc) for x in result])
+
+
+def parse(url):
+ """
+ URL-parsing function that checks that
+ - port is an integer 0-65535
+ - host is a valid IDNA-encoded hostname with no null-bytes
+ - path is valid ASCII
+
+ Args:
+ A URL (as bytes or as unicode)
+
+ Returns:
+ A (scheme, host, port, path) tuple
+
+ Raises:
+ ValueError, if the URL is not properly formatted.
+ """
+ parsed = urllib.parse.urlparse(url)
+
+ if not parsed.hostname:
+ raise ValueError("No hostname given")
+
+ if isinstance(url, six.binary_type):
+ host = parsed.hostname
+
+ # this should not raise a ValueError,
+ # but we try to be very forgiving here and accept just everything.
+ # decode_parse_result(parsed, "ascii")
+ else:
+ host = parsed.hostname.encode("idna")
+ parsed = encode_parse_result(parsed, "ascii")
+
+ port = parsed.port
+ if not port:
+ port = 443 if parsed.scheme == b"https" else 80
+
+ full_path = urllib.parse.urlunparse(
+ (b"", b"", parsed.path, parsed.params, parsed.query, parsed.fragment)
+ )
+ if not full_path.startswith(b"/"):
+ full_path = b"/" + full_path
+
+ if not utils.is_valid_host(host):
+ raise ValueError("Invalid Host")
+ if not utils.is_valid_port(port):
+ raise ValueError("Invalid Port")
+
+ return parsed.scheme, host, port, full_path
+
+
+def unparse(scheme, host, port, path=""):
+ """
+ Returns a URL string, constructed from the specified components.
+
+ Args:
+ All args must be str.
+ """
+ if path == "*":
+ path = ""
+ return "%s://%s%s" % (scheme, utils.hostport(scheme, host, port), path)
+
+
+def encode(s):
+ """
+ Takes a list of (key, value) tuples and returns a urlencoded string.
+ """
+ s = [tuple(i) for i in s]
+ return urllib.parse.urlencode(s, False)
+
+
+def decode(s):
+ """
+ Takes a urlencoded string and returns a list of (key, value) tuples.
+ """
+ return urllib.parse.parse_qsl(s, keep_blank_values=True)
diff --git a/netlib/human.py b/netlib/human.py
new file mode 100644
index 00000000..a007adc7
--- /dev/null
+++ b/netlib/human.py
@@ -0,0 +1,50 @@
+
+SIZE_TABLE = [
+ ("b", 1024 ** 0),
+ ("k", 1024 ** 1),
+ ("m", 1024 ** 2),
+ ("g", 1024 ** 3),
+ ("t", 1024 ** 4),
+]
+
+SIZE_UNITS = dict(SIZE_TABLE)
+
+
+def pretty_size(size):
+ for bottom, top in zip(SIZE_TABLE, SIZE_TABLE[1:]):
+ if bottom[1] <= size < top[1]:
+ suf = bottom[0]
+ lim = bottom[1]
+ x = round(size / lim, 2)
+ if x == int(x):
+ x = int(x)
+ return str(x) + suf
+ return "%s%s" % (size, SIZE_TABLE[0][0])
+
+
+def parse_size(s):
+ try:
+ return int(s)
+ except ValueError:
+ pass
+ for i in SIZE_UNITS.keys():
+ if s.endswith(i):
+ try:
+ return int(s[:-1]) * SIZE_UNITS[i]
+ except ValueError:
+ break
+ raise ValueError("Invalid size specification.")
+
+
+def pretty_duration(secs):
+ formatters = [
+ (100, "{:.0f}s"),
+ (10, "{:2.1f}s"),
+ (1, "{:1.2f}s"),
+ ]
+
+ for limit, formatter in formatters:
+ if secs >= limit:
+ return formatter.format(secs)
+ # less than 1 sec
+ return "{:.0f}ms".format(secs * 1000)
diff --git a/netlib/multidict.py b/netlib/multidict.py
index 248acdec..dc0f3466 100644
--- a/netlib/multidict.py
+++ b/netlib/multidict.py
@@ -2,7 +2,6 @@ from __future__ import absolute_import, print_function, division
from abc import ABCMeta, abstractmethod
-from typing import Tuple, TypeVar
try:
from collections.abc import MutableMapping
@@ -10,14 +9,13 @@ except ImportError: # pragma: no cover
from collections import MutableMapping # Workaround for Python < 3.3
import six
-
-from .utils import Serializable
+from netlib import basetypes
@six.add_metaclass(ABCMeta)
-class _MultiDict(MutableMapping, Serializable):
+class _MultiDict(MutableMapping, basetypes.Serializable):
def __repr__(self):
- fields = tuple(
+ fields = (
repr(field)
for field in self.fields
)
@@ -172,6 +170,30 @@ class _MultiDict(MutableMapping, Serializable):
else:
return super(_MultiDict, self).items()
+ def clear(self, key):
+ """
+ Removes all items with the specified key, and does not raise an
+ exception if the key does not exist.
+ """
+ if key in self:
+ del self[key]
+
+ def collect(self):
+ """
+ Returns a list of (key, value) tuples, where values are either
+ singular if threre is only one matching item for a key, or a list
+ if there are more than one. The order of the keys matches the order
+ in the underlying fields list.
+ """
+ coll = []
+ for key in self:
+ values = self.get_all(key)
+ if len(values) == 1:
+ coll.append([key, values[0]])
+ else:
+ coll.append([key, values])
+ return coll
+
def to_dict(self):
"""
Get the MultiDict as a plain Python dict.
@@ -191,12 +213,8 @@ class _MultiDict(MutableMapping, Serializable):
}
"""
d = {}
- for key in self:
- values = self.get_all(key)
- if len(values) == 1:
- d[key] = values[0]
- else:
- d[key] = values
+ for k, v in self.collect():
+ d[k] = v
return d
def get_state(self):
@@ -207,13 +225,15 @@ class _MultiDict(MutableMapping, Serializable):
@classmethod
def from_state(cls, state):
- return cls(tuple(x) for x in state)
+ return cls(state)
class MultiDict(_MultiDict):
- def __init__(self, fields=None):
+ def __init__(self, fields=()):
super(MultiDict, self).__init__()
- self.fields = tuple(fields) if fields else tuple() # type: Tuple[Tuple[bytes, bytes], ...]
+ self.fields = tuple(
+ tuple(i) for i in fields
+ )
@six.add_metaclass(ABCMeta)
diff --git a/netlib/odict.py b/netlib/odict.py
index 8a638dab..f9f55991 100644
--- a/netlib/odict.py
+++ b/netlib/odict.py
@@ -3,10 +3,10 @@ import copy
import six
-from .utils import Serializable, safe_subn
+from netlib import basetypes, strutils
-class ODict(Serializable):
+class ODict(basetypes.Serializable):
"""
A dictionary-like object for managing ordered (key, value) data. Think
@@ -139,9 +139,9 @@ class ODict(Serializable):
"""
new, count = [], 0
for k, v in self.lst:
- k, c = safe_subn(pattern, repl, k, *args, **kwargs)
+ k, c = strutils.safe_subn(pattern, repl, k, *args, **kwargs)
count += c
- v, c = safe_subn(pattern, repl, v, *args, **kwargs)
+ v, c = strutils.safe_subn(pattern, repl, v, *args, **kwargs)
count += c
new.append([k, v])
self.lst = new
diff --git a/netlib/socks.py b/netlib/socks.py
index 57ccd1be..8d7c5279 100644
--- a/netlib/socks.py
+++ b/netlib/socks.py
@@ -2,7 +2,8 @@ from __future__ import (absolute_import, print_function, division)
import struct
import array
import ipaddress
-from . import tcp, utils
+
+from netlib import tcp, utils
class SocksError(Exception):
@@ -147,7 +148,7 @@ class UsernamePasswordAuth(object):
class UsernamePasswordAuthResponse(object):
- __slots__ = ("ver", "status")
+ __slots__ = ("ver", "status")
def __init__(self, ver, status):
self.ver = ver
diff --git a/netlib/strutils.py b/netlib/strutils.py
new file mode 100644
index 00000000..03b371f5
--- /dev/null
+++ b/netlib/strutils.py
@@ -0,0 +1,154 @@
+import re
+import unicodedata
+import codecs
+
+import six
+
+
+def always_bytes(unicode_or_bytes, *encode_args):
+ if isinstance(unicode_or_bytes, six.text_type):
+ return unicode_or_bytes.encode(*encode_args)
+ return unicode_or_bytes
+
+
+def native(s, *encoding_opts):
+ """
+ Convert :py:class:`bytes` or :py:class:`unicode` to the native
+ :py:class:`str` type, using latin1 encoding if conversion is necessary.
+
+ https://www.python.org/dev/peps/pep-3333/#a-note-on-string-types
+ """
+ if not isinstance(s, (six.binary_type, six.text_type)):
+ raise TypeError("%r is neither bytes nor unicode" % s)
+ if six.PY3:
+ if isinstance(s, six.binary_type):
+ return s.decode(*encoding_opts)
+ else:
+ if isinstance(s, six.text_type):
+ return s.encode(*encoding_opts)
+ return s
+
+
+def clean_bin(s, keep_spacing=True):
+ """
+ Cleans binary data to make it safe to display.
+
+ Args:
+ keep_spacing: If False, tabs and newlines will also be replaced.
+ """
+ if isinstance(s, six.text_type):
+ if keep_spacing:
+ keep = u" \n\r\t"
+ else:
+ keep = u" "
+ return u"".join(
+ ch if (unicodedata.category(ch)[0] not in "CZ" or ch in keep) else u"."
+ for ch in s
+ )
+ else:
+ if keep_spacing:
+ keep = (9, 10, 13) # \t, \n, \r,
+ else:
+ keep = ()
+ return b"".join(
+ six.int2byte(ch) if (31 < ch < 127 or ch in keep) else b"."
+ for ch in six.iterbytes(s)
+ )
+
+
+def safe_subn(pattern, repl, target, *args, **kwargs):
+ """
+ There are Unicode conversion problems with re.subn. We try to smooth
+ that over by casting the pattern and replacement to strings. We really
+ need a better solution that is aware of the actual content ecoding.
+ """
+ return re.subn(str(pattern), str(repl), target, *args, **kwargs)
+
+
+def bytes_to_escaped_str(data):
+ """
+ Take bytes and return a safe string that can be displayed to the user.
+
+ Single quotes are always escaped, double quotes are never escaped:
+ "'" + bytes_to_escaped_str(...) + "'"
+ gives a valid Python string.
+ """
+ # TODO: We may want to support multi-byte characters without escaping them.
+ # One way to do would be calling .decode("utf8", "backslashreplace") first
+ # and then escaping UTF8 control chars (see clean_bin).
+
+ if not isinstance(data, bytes):
+ raise ValueError("data must be bytes, but is {}".format(data.__class__.__name__))
+ # We always insert a double-quote here so that we get a single-quoted string back
+ # https://stackoverflow.com/questions/29019340/why-does-python-use-different-quotes-for-representing-strings-depending-on-their
+ return repr(b'"' + data).lstrip("b")[2:-1]
+
+
+def escaped_str_to_bytes(data):
+ """
+ Take an escaped string and return the unescaped bytes equivalent.
+ """
+ if not isinstance(data, six.string_types):
+ if six.PY2:
+ raise ValueError("data must be str or unicode, but is {}".format(data.__class__.__name__))
+ raise ValueError("data must be str, but is {}".format(data.__class__.__name__))
+
+ if six.PY2:
+ if isinstance(data, unicode):
+ data = data.encode("utf8")
+ return data.decode("string-escape")
+
+ # This one is difficult - we use an undocumented Python API here
+ # as per http://stackoverflow.com/a/23151714/934719
+ return codecs.escape_decode(data)[0]
+
+
+def isBin(s):
+ """
+ Does this string have any non-ASCII characters?
+ """
+ for i in s:
+ i = ord(i)
+ if i < 9 or 13 < i < 32 or 126 < i:
+ return True
+ return False
+
+
+def isMostlyBin(s):
+ s = s[:100]
+ return sum(isBin(ch) for ch in s) / len(s) > 0.3
+
+
+def isXML(s):
+ for i in s:
+ if i in "\n \t":
+ continue
+ elif i == "<":
+ return True
+ else:
+ return False
+
+
+def clean_hanging_newline(t):
+ """
+ Many editors will silently add a newline to the final line of a
+ document (I'm looking at you, Vim). This function fixes this common
+ problem at the risk of removing a hanging newline in the rare cases
+ where the user actually intends it.
+ """
+ if t and t[-1] == "\n":
+ return t[:-1]
+ return t
+
+
+def hexdump(s):
+ """
+ Returns:
+ A generator of (offset, hex, str) tuples
+ """
+ for i in range(0, len(s), 16):
+ offset = "{:0=10x}".format(i).encode()
+ part = s[i:i + 16]
+ x = b" ".join("{:0=2x}".format(i).encode() for i in six.iterbytes(part))
+ x = x.ljust(47) # 16*2 + 15
+ yield (offset, x, clean_bin(part, False))
diff --git a/netlib/tcp.py b/netlib/tcp.py
index d26bb5f7..de12102e 100644
--- a/netlib/tcp.py
+++ b/netlib/tcp.py
@@ -6,6 +6,7 @@ import sys
import threading
import time
import traceback
+import contextlib
import binascii
from six.moves import range
@@ -16,13 +17,10 @@ import six
import OpenSSL
from OpenSSL import SSL
-from . import certutils, version_check, utils
+from netlib import certutils, version_check, basetypes, exceptions
# This is a rather hackish way to make sure that
# the latest version of pyOpenSSL is actually installed.
-from netlib.exceptions import InvalidCertificateException, TcpReadIncomplete, TlsException, \
- TcpTimeout, TcpDisconnect, TcpException
-
version_check.check_pyopenssl_version()
if six.PY2:
@@ -71,6 +69,7 @@ sslversion_choices = {
"TLSv1_2": (SSL.TLSv1_2_METHOD, SSL_BASIC_OPTIONS),
}
+
class SSLKeyLogger(object):
def __init__(self, filename):
@@ -161,17 +160,17 @@ class Writer(_FileLike):
def flush(self):
"""
- May raise TcpDisconnect
+ May raise exceptions.TcpDisconnect
"""
if hasattr(self.o, "flush"):
try:
self.o.flush()
except (socket.error, IOError) as v:
- raise TcpDisconnect(str(v))
+ raise exceptions.TcpDisconnect(str(v))
def write(self, v):
"""
- May raise TcpDisconnect
+ May raise exceptions.TcpDisconnect
"""
if v:
self.first_byte_timestamp = self.first_byte_timestamp or time.time()
@@ -184,7 +183,7 @@ class Writer(_FileLike):
self.add_log(v[:r])
return r
except (SSL.Error, socket.error) as e:
- raise TcpDisconnect(str(e))
+ raise exceptions.TcpDisconnect(str(e))
class Reader(_FileLike):
@@ -215,17 +214,17 @@ class Reader(_FileLike):
time.sleep(0.1)
continue
else:
- raise TcpTimeout()
+ raise exceptions.TcpTimeout()
except socket.timeout:
- raise TcpTimeout()
+ raise exceptions.TcpTimeout()
except socket.error as e:
- raise TcpDisconnect(str(e))
+ raise exceptions.TcpDisconnect(str(e))
except SSL.SysCallError as e:
if e.args == (-1, 'Unexpected EOF'):
break
- raise TlsException(str(e))
+ raise exceptions.TlsException(str(e))
except SSL.Error as e:
- raise TlsException(str(e))
+ raise exceptions.TlsException(str(e))
self.first_byte_timestamp = self.first_byte_timestamp or time.time()
if not data:
break
@@ -259,9 +258,9 @@ class Reader(_FileLike):
result = self.read(length)
if length != -1 and len(result) != length:
if not result:
- raise TcpDisconnect()
+ raise exceptions.TcpDisconnect()
else:
- raise TcpReadIncomplete(
+ raise exceptions.TcpReadIncomplete(
"Expected %s bytes, got %s" % (length, len(result))
)
return result
@@ -274,7 +273,7 @@ class Reader(_FileLike):
Up to the next N bytes if peeking is successful.
Raises:
- TcpException if there was an error with the socket
+ exceptions.TcpException if there was an error with the socket
TlsException if there was an error with pyOpenSSL.
NotImplementedError if the underlying file object is not a [pyOpenSSL] socket
"""
@@ -282,7 +281,7 @@ class Reader(_FileLike):
try:
return self.o._sock.recv(length, socket.MSG_PEEK)
except socket.error as e:
- raise TcpException(repr(e))
+ raise exceptions.TcpException(repr(e))
elif isinstance(self.o, SSL.Connection):
try:
if tuple(int(x) for x in OpenSSL.__version__.split(".")[:2]) > (0, 15):
@@ -296,12 +295,12 @@ class Reader(_FileLike):
self.o._raise_ssl_error(self.o._ssl, result)
return SSL._ffi.buffer(buf, result)[:]
except SSL.Error as e:
- six.reraise(TlsException, TlsException(str(e)), sys.exc_info()[2])
+ six.reraise(exceptions.TlsException, exceptions.TlsException(str(e)), sys.exc_info()[2])
else:
raise NotImplementedError("Can only peek into (pyOpenSSL) sockets")
-class Address(utils.Serializable):
+class Address(basetypes.Serializable):
"""
This class wraps an IPv4/IPv6 tuple to provide named attributes and
@@ -489,7 +488,7 @@ class _Connection(object):
try:
self.wfile.flush()
self.wfile.close()
- except TcpDisconnect:
+ except exceptions.TcpDisconnect:
pass
self.rfile.close()
@@ -553,7 +552,7 @@ class _Connection(object):
# TODO: maybe change this to with newer pyOpenSSL APIs
context.set_tmp_ecdh(OpenSSL.crypto.get_elliptic_curve('prime256v1'))
except SSL.Error as v:
- raise TlsException("SSL cipher specification error: %s" % str(v))
+ raise exceptions.TlsException("SSL cipher specification error: %s" % str(v))
# SSLKEYLOGFILE
if log_ssl_key:
@@ -574,11 +573,17 @@ class _Connection(object):
elif alpn_select_callback is not None and alpn_select is None:
context.set_alpn_select_callback(alpn_select_callback)
elif alpn_select_callback is not None and alpn_select is not None:
- raise TlsException("ALPN error: only define alpn_select (string) OR alpn_select_callback (method).")
+ raise exceptions.TlsException("ALPN error: only define alpn_select (string) OR alpn_select_callback (method).")
return context
+@contextlib.contextmanager
+def _closer(client):
+ yield
+ client.close()
+
+
class TCPClient(_Connection):
def __init__(self, address, source_address=None):
@@ -631,7 +636,7 @@ class TCPClient(_Connection):
context.use_privatekey_file(cert)
context.use_certificate_file(cert)
except SSL.Error as v:
- raise TlsException("SSL client certificate error: %s" % str(v))
+ raise exceptions.TlsException("SSL client certificate error: %s" % str(v))
return context
def convert_to_ssl(self, sni=None, alpn_protos=None, **sslctx_kwargs):
@@ -645,7 +650,7 @@ class TCPClient(_Connection):
"""
verification_mode = sslctx_kwargs.get('verify_options', None)
if verification_mode == SSL.VERIFY_PEER and not sni:
- raise TlsException("Cannot validate certificate hostname without SNI")
+ raise exceptions.TlsException("Cannot validate certificate hostname without SNI")
context = self.create_ssl_context(
alpn_protos=alpn_protos,
@@ -660,14 +665,14 @@ class TCPClient(_Connection):
self.connection.do_handshake()
except SSL.Error as v:
if self.ssl_verification_error:
- raise InvalidCertificateException("SSL handshake error: %s" % repr(v))
+ raise exceptions.InvalidCertificateException("SSL handshake error: %s" % repr(v))
else:
- raise TlsException("SSL handshake error: %s" % repr(v))
+ raise exceptions.TlsException("SSL handshake error: %s" % repr(v))
else:
# Fix for pre v1.0 OpenSSL, which doesn't throw an exception on
# certificate validation failure
if verification_mode == SSL.VERIFY_PEER and self.ssl_verification_error is not None:
- raise InvalidCertificateException("SSL handshake error: certificate verify failed")
+ raise exceptions.InvalidCertificateException("SSL handshake error: certificate verify failed")
self.cert = certutils.SSLCert(self.connection.get_peer_certificate())
@@ -690,7 +695,7 @@ class TCPClient(_Connection):
except (ValueError, ssl_match_hostname.CertificateError) as e:
self.ssl_verification_error = dict(depth=0, errno="Invalid Hostname")
if verification_mode == SSL.VERIFY_PEER:
- raise InvalidCertificateException("Presented certificate for {} is not valid: {}".format(sni, str(e)))
+ raise exceptions.InvalidCertificateException("Presented certificate for {} is not valid: {}".format(sni, str(e)))
self.ssl_established = True
self.rfile.set_descriptor(self.connection)
@@ -704,12 +709,13 @@ class TCPClient(_Connection):
connection.connect(self.address())
self.source_address = Address(connection.getsockname())
except (socket.error, IOError) as err:
- raise TcpException(
+ raise exceptions.TcpException(
'Error connecting to "%s": %s' %
(self.address.host, err))
self.connection = connection
self.ip_address = Address(connection.getpeername())
self._makefile()
+ return _closer(self)
def settimeout(self, n):
self.connection.settimeout(n)
@@ -817,7 +823,7 @@ class BaseHandler(_Connection):
try:
self.connection.do_handshake()
except SSL.Error as v:
- raise TlsException("SSL handshake error: %s" % repr(v))
+ raise exceptions.TlsException("SSL handshake error: %s" % repr(v))
self.ssl_established = True
self.rfile.set_descriptor(self.connection)
self.wfile.set_descriptor(self.connection)
@@ -835,6 +841,25 @@ class BaseHandler(_Connection):
return b""
+class Counter:
+ def __init__(self):
+ self._count = 0
+ self._lock = threading.Lock()
+
+ @property
+ def count(self):
+ with self._lock:
+ return self._count
+
+ def __enter__(self):
+ with self._lock:
+ self._count += 1
+
+ def __exit__(self, *args):
+ with self._lock:
+ self._count -= 1
+
+
class TCPServer(object):
request_queue_size = 20
@@ -847,15 +872,17 @@ class TCPServer(object):
self.socket.bind(self.address())
self.address = Address.wrap(self.socket.getsockname())
self.socket.listen(self.request_queue_size)
+ self.handler_counter = Counter()
def connection_thread(self, connection, client_address):
- client_address = Address(client_address)
- try:
- self.handle_client_connection(connection, client_address)
- except:
- self.handle_error(connection, client_address)
- finally:
- close_socket(connection)
+ with self.handler_counter:
+ client_address = Address(client_address)
+ try:
+ self.handle_client_connection(connection, client_address)
+ except:
+ self.handle_error(connection, client_address)
+ finally:
+ close_socket(connection)
def serve_forever(self, poll_interval=0.1):
self.__is_shut_down.clear()
@@ -900,7 +927,7 @@ class TCPServer(object):
"""
# If a thread has persisted after interpreter exit, the module might be
# none.
- if traceback:
+ if traceback and six:
exc = six.text_type(traceback.format_exc())
print(u'-' * 40, file=fp)
print(
diff --git a/netlib/tutils.py b/netlib/tutils.py
index 18d632f0..452766d6 100644
--- a/netlib/tutils.py
+++ b/netlib/tutils.py
@@ -7,8 +7,7 @@ from contextlib import contextmanager
import six
import sys
-from . import utils, tcp
-from .http import Request, Response, Headers
+from netlib import utils, tcp, http
def treader(bytes):
@@ -91,8 +90,7 @@ class RaisesContext(object):
test_data = utils.Data(__name__)
# FIXME: Temporary workaround during repo merge.
-import os
-test_data.dirname = os.path.join(test_data.dirname,"..","test","netlib")
+test_data.dirname = os.path.join(test_data.dirname, "..", "test", "netlib")
def treq(**kwargs):
@@ -108,11 +106,11 @@ def treq(**kwargs):
port=22,
path=b"/path",
http_version=b"HTTP/1.1",
- headers=Headers(header="qvalue", content_length="7"),
+ headers=http.Headers(((b"header", b"qvalue"), (b"content-length", b"7"))),
content=b"content"
)
default.update(kwargs)
- return Request(**default)
+ return http.Request(**default)
def tresp(**kwargs):
@@ -124,10 +122,10 @@ def tresp(**kwargs):
http_version=b"HTTP/1.1",
status_code=200,
reason=b"OK",
- headers=Headers(header_response="svalue", content_length="7"),
+ headers=http.Headers(((b"header-response", b"svalue"), (b"content-length", b"7"))),
content=b"message",
timestamp_start=time.time(),
timestamp_end=time.time(),
)
default.update(kwargs)
- return Response(**default)
+ return http.Response(**default)
diff --git a/netlib/utils.py b/netlib/utils.py
index 7499f71f..b4b99679 100644
--- a/netlib/utils.py
+++ b/netlib/utils.py
@@ -1,121 +1,11 @@
from __future__ import absolute_import, print_function, division
import os.path
import re
-import codecs
-import unicodedata
-from abc import ABCMeta, abstractmethod
import importlib
import inspect
import six
-from six.moves import urllib
-import hyperframe
-
-
-@six.add_metaclass(ABCMeta)
-class Serializable(object):
- """
- Abstract Base Class that defines an API to save an object's state and restore it later on.
- """
-
- @classmethod
- @abstractmethod
- def from_state(cls, state):
- """
- Create a new object from the given state.
- """
- raise NotImplementedError()
-
- @abstractmethod
- def get_state(self):
- """
- Retrieve object state.
- """
- raise NotImplementedError()
-
- @abstractmethod
- def set_state(self, state):
- """
- Set object state to the given state.
- """
- raise NotImplementedError()
-
- def copy(self):
- return self.from_state(self.get_state())
-
-
-def always_bytes(unicode_or_bytes, *encode_args):
- if isinstance(unicode_or_bytes, six.text_type):
- return unicode_or_bytes.encode(*encode_args)
- return unicode_or_bytes
-
-
-def native(s, *encoding_opts):
- """
- Convert :py:class:`bytes` or :py:class:`unicode` to the native
- :py:class:`str` type, using latin1 encoding if conversion is necessary.
-
- https://www.python.org/dev/peps/pep-3333/#a-note-on-string-types
- """
- if not isinstance(s, (six.binary_type, six.text_type)):
- raise TypeError("%r is neither bytes nor unicode" % s)
- if six.PY3:
- if isinstance(s, six.binary_type):
- return s.decode(*encoding_opts)
- else:
- if isinstance(s, six.text_type):
- return s.encode(*encoding_opts)
- return s
-
-
-def isascii(bytes):
- try:
- bytes.decode("ascii")
- except ValueError:
- return False
- return True
-
-
-def clean_bin(s, keep_spacing=True):
- """
- Cleans binary data to make it safe to display.
-
- Args:
- keep_spacing: If False, tabs and newlines will also be replaced.
- """
- if isinstance(s, six.text_type):
- if keep_spacing:
- keep = u" \n\r\t"
- else:
- keep = u" "
- return u"".join(
- ch if (unicodedata.category(ch)[0] not in "CZ" or ch in keep) else u"."
- for ch in s
- )
- else:
- if keep_spacing:
- keep = (9, 10, 13) # \t, \n, \r,
- else:
- keep = ()
- return b"".join(
- six.int2byte(ch) if (31 < ch < 127 or ch in keep) else b"."
- for ch in six.iterbytes(s)
- )
-
-
-def hexdump(s):
- """
- Returns:
- A generator of (offset, hex, str) tuples
- """
- for i in range(0, len(s), 16):
- offset = "{:0=10x}".format(i).encode()
- part = s[i:i + 16]
- x = b" ".join("{:0=2x}".format(i).encode() for i in six.iterbytes(part))
- x = x.ljust(47) # 16*2 + 15
- yield (offset, x, clean_bin(part, False))
-
def setbit(byte, offset, value):
"""
@@ -161,22 +51,6 @@ class BiDi(object):
return self.values.get(n, default)
-def pretty_size(size):
- suffixes = [
- ("B", 2 ** 10),
- ("kB", 2 ** 20),
- ("MB", 2 ** 30),
- ]
- for suf, lim in suffixes:
- if size >= lim:
- continue
- else:
- x = round(size / float(lim / 2 ** 10), 2)
- if x == int(x):
- x = int(x)
- return str(x) + suf
-
-
class Data(object):
def __init__(self, name):
@@ -222,83 +96,6 @@ def is_valid_port(port):
return 0 <= port <= 65535
-# PY2 workaround
-def decode_parse_result(result, enc):
- if hasattr(result, "decode"):
- return result.decode(enc)
- else:
- return urllib.parse.ParseResult(*[x.decode(enc) for x in result])
-
-
-# PY2 workaround
-def encode_parse_result(result, enc):
- if hasattr(result, "encode"):
- return result.encode(enc)
- else:
- return urllib.parse.ParseResult(*[x.encode(enc) for x in result])
-
-
-def parse_url(url):
- """
- URL-parsing function that checks that
- - port is an integer 0-65535
- - host is a valid IDNA-encoded hostname with no null-bytes
- - path is valid ASCII
-
- Args:
- A URL (as bytes or as unicode)
-
- Returns:
- A (scheme, host, port, path) tuple
-
- Raises:
- ValueError, if the URL is not properly formatted.
- """
- parsed = urllib.parse.urlparse(url)
-
- if not parsed.hostname:
- raise ValueError("No hostname given")
-
- if isinstance(url, six.binary_type):
- host = parsed.hostname
-
- # this should not raise a ValueError,
- # but we try to be very forgiving here and accept just everything.
- # decode_parse_result(parsed, "ascii")
- else:
- host = parsed.hostname.encode("idna")
- parsed = encode_parse_result(parsed, "ascii")
-
- port = parsed.port
- if not port:
- port = 443 if parsed.scheme == b"https" else 80
-
- full_path = urllib.parse.urlunparse(
- (b"", b"", parsed.path, parsed.params, parsed.query, parsed.fragment)
- )
- if not full_path.startswith(b"/"):
- full_path = b"/" + full_path
-
- if not is_valid_host(host):
- raise ValueError("Invalid Host")
- if not is_valid_port(port):
- raise ValueError("Invalid Port")
-
- return parsed.scheme, host, port, full_path
-
-
-def get_header_tokens(headers, key):
- """
- Retrieve all tokens for a header key. A number of different headers
- follow a pattern where each header line can containe comma-separated
- tokens, and headers can be set multiple times.
- """
- if key not in headers:
- return []
- tokens = headers[key].split(",")
- return [token.strip() for token in tokens]
-
-
def hostport(scheme, host, port):
"""
Returns the host component, with a port specifcation if needed.
@@ -310,141 +107,3 @@ def hostport(scheme, host, port):
return b"%s:%d" % (host, port)
else:
return "%s:%d" % (host, port)
-
-
-def unparse_url(scheme, host, port, path=""):
- """
- Returns a URL string, constructed from the specified components.
-
- Args:
- All args must be str.
- """
- if path == "*":
- path = ""
- return "%s://%s%s" % (scheme, hostport(scheme, host, port), path)
-
-
-def urlencode(s):
- """
- Takes a list of (key, value) tuples and returns a urlencoded string.
- """
- s = [tuple(i) for i in s]
- return urllib.parse.urlencode(s, False)
-
-
-def urldecode(s):
- """
- Takes a urlencoded string and returns a list of (key, value) tuples.
- """
- return urllib.parse.parse_qsl(s, keep_blank_values=True)
-
-
-def parse_content_type(c):
- """
- A simple parser for content-type values. Returns a (type, subtype,
- parameters) tuple, where type and subtype are strings, and parameters
- is a dict. If the string could not be parsed, return None.
-
- E.g. the following string:
-
- text/html; charset=UTF-8
-
- Returns:
-
- ("text", "html", {"charset": "UTF-8"})
- """
- parts = c.split(";", 1)
- ts = parts[0].split("/", 1)
- if len(ts) != 2:
- return None
- d = {}
- if len(parts) == 2:
- for i in parts[1].split(";"):
- clause = i.split("=", 1)
- if len(clause) == 2:
- d[clause[0].strip()] = clause[1].strip()
- return ts[0].lower(), ts[1].lower(), d
-
-
-def multipartdecode(headers, content):
- """
- Takes a multipart boundary encoded string and returns list of (key, value) tuples.
- """
- v = headers.get("content-type")
- if v:
- v = parse_content_type(v)
- if not v:
- return []
- try:
- boundary = v[2]["boundary"].encode("ascii")
- except (KeyError, UnicodeError):
- return []
-
- rx = re.compile(br'\bname="([^"]+)"')
- r = []
-
- for i in content.split(b"--" + boundary):
- parts = i.splitlines()
- if len(parts) > 1 and parts[0][0:2] != b"--":
- match = rx.search(parts[1])
- if match:
- key = match.group(1)
- value = b"".join(parts[3 + parts[2:].index(b""):])
- r.append((key, value))
- return r
- return []
-
-
-def http2_read_raw_frame(rfile):
- header = rfile.safe_read(9)
- length = int(codecs.encode(header[:3], 'hex_codec'), 16)
-
- if length == 4740180:
- raise ValueError("Length field looks more like HTTP/1.1: %s" % rfile.peek(20))
-
- body = rfile.safe_read(length)
- return [header, body]
-
-
-def http2_read_frame(rfile):
- header, body = http2_read_raw_frame(rfile)
- frame, length = hyperframe.frame.Frame.parse_frame_header(header)
- frame.parse_body(memoryview(body))
- return frame
-
-
-def safe_subn(pattern, repl, target, *args, **kwargs):
- """
- There are Unicode conversion problems with re.subn. We try to smooth
- that over by casting the pattern and replacement to strings. We really
- need a better solution that is aware of the actual content ecoding.
- """
- return re.subn(str(pattern), str(repl), target, *args, **kwargs)
-
-
-def bytes_to_escaped_str(data):
- """
- Take bytes and return a safe string that can be displayed to the user.
- """
- # TODO: We may want to support multi-byte characters without escaping them.
- # One way to do would be calling .decode("utf8", "backslashreplace") first
- # and then escaping UTF8 control chars (see clean_bin).
-
- if not isinstance(data, bytes):
- raise ValueError("data must be bytes")
- return repr(data).lstrip("b")[1:-1]
-
-
-def escaped_str_to_bytes(data):
- """
- Take an escaped string and return the unescaped bytes equivalent.
- """
- if not isinstance(data, str):
- raise ValueError("data must be str")
-
- if six.PY2:
- return data.decode("string-escape")
-
- # This one is difficult - we use an undocumented Python API here
- # as per http://stackoverflow.com/a/23151714/934719
- return codecs.escape_decode(data)[0]
diff --git a/netlib/version_check.py b/netlib/version_check.py
index 8e05b458..63f3e876 100644
--- a/netlib/version_check.py
+++ b/netlib/version_check.py
@@ -10,7 +10,6 @@ import os.path
import six
import OpenSSL
-from . import version
PYOPENSSL_MIN_VERSION = (0, 15)
diff --git a/netlib/websockets/__init__.py b/netlib/websockets/__init__.py
index 1c143919..fea696d9 100644
--- a/netlib/websockets/__init__.py
+++ b/netlib/websockets/__init__.py
@@ -1,2 +1,11 @@
-from .frame import *
-from .protocol import *
+from __future__ import absolute_import, print_function, division
+from .frame import FrameHeader, Frame, OPCODE
+from .protocol import Masker, WebsocketsProtocol
+
+__all__ = [
+ "FrameHeader",
+ "Frame",
+ "Masker",
+ "WebsocketsProtocol",
+ "OPCODE",
+]
diff --git a/netlib/websockets/frame.py b/netlib/websockets/frame.py
index fce2c9d3..42196ffb 100644
--- a/netlib/websockets/frame.py
+++ b/netlib/websockets/frame.py
@@ -6,15 +6,17 @@ import warnings
import six
-from .protocol import Masker
from netlib import tcp
+from netlib import strutils
from netlib import utils
+from netlib import human
+from netlib.websockets import protocol
MAX_16_BIT_INT = (1 << 16)
MAX_64_BIT_INT = (1 << 64)
-DEFAULT=object()
+DEFAULT = object()
OPCODE = utils.BiDi(
CONTINUE=0x00,
@@ -98,7 +100,7 @@ class FrameHeader(object):
if self.masking_key:
vals.append(":key=%s" % repr(self.masking_key))
if self.payload_length:
- vals.append(" %s" % utils.pretty_size(self.payload_length))
+ vals.append(" %s" % human.pretty_size(self.payload_length))
return "".join(vals)
def human_readable(self):
@@ -253,7 +255,7 @@ class Frame(object):
def __repr__(self):
ret = repr(self.header)
if self.payload:
- ret = ret + "\nPayload:\n" + utils.clean_bin(self.payload).decode("ascii")
+ ret = ret + "\nPayload:\n" + strutils.clean_bin(self.payload).decode("ascii")
return ret
def human_readable(self):
@@ -266,7 +268,7 @@ class Frame(object):
"""
b = bytes(self.header)
if self.header.masking_key:
- b += Masker(self.header.masking_key)(self.payload)
+ b += protocol.Masker(self.header.masking_key)(self.payload)
else:
b += self.payload
return b
@@ -295,7 +297,7 @@ class Frame(object):
payload = fp.safe_read(header.payload_length)
if header.mask == 1 and header.masking_key:
- payload = Masker(header.masking_key)(payload)
+ payload = protocol.Masker(header.masking_key)(payload)
return cls(
payload,
diff --git a/netlib/websockets/protocol.py b/netlib/websockets/protocol.py
index 1e95fa1c..c1b7be2c 100644
--- a/netlib/websockets/protocol.py
+++ b/netlib/websockets/protocol.py
@@ -1,26 +1,26 @@
+"""
+Colleciton of utility functions that implement small portions of the RFC6455
+WebSockets Protocol Useful for building WebSocket clients and servers.
+Emphassis is on readabilty, simplicity and modularity, not performance or
+completeness
+This is a work in progress and does not yet contain all the utilites need to
+create fully complient client/servers #
+Spec: https://tools.ietf.org/html/rfc6455
-# Colleciton of utility functions that implement small portions of the RFC6455
-# WebSockets Protocol Useful for building WebSocket clients and servers.
-#
-# Emphassis is on readabilty, simplicity and modularity, not performance or
-# completeness
-#
-# This is a work in progress and does not yet contain all the utilites need to
-# create fully complient client/servers #
-# Spec: https://tools.ietf.org/html/rfc6455
+The magic sha that websocket servers must know to prove they understand
+RFC6455
+"""
-# The magic sha that websocket servers must know to prove they understand
-# RFC6455
from __future__ import absolute_import
import base64
import hashlib
import os
-import binascii
import six
-from ..http import Headers
+
+from netlib import http
websockets_magic = b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
VERSION = "13"
@@ -73,11 +73,11 @@ class WebsocketsProtocol(object):
specified, it is generated, and can be found in sec-websocket-key in
the returned header set.
- Returns an instance of Headers
+ Returns an instance of http.Headers
"""
if not key:
key = base64.b64encode(os.urandom(16)).decode('ascii')
- return Headers(
+ return http.Headers(
sec_websocket_key=key,
sec_websocket_version=version,
connection="Upgrade",
@@ -89,27 +89,24 @@ class WebsocketsProtocol(object):
"""
The server response is a valid HTTP 101 response.
"""
- return Headers(
+ return http.Headers(
sec_websocket_accept=self.create_server_nonce(key),
connection="Upgrade",
upgrade="websocket"
)
-
@classmethod
def check_client_handshake(self, headers):
if headers.get("upgrade") != "websocket":
return
return headers.get("sec-websocket-key")
-
@classmethod
def check_server_handshake(self, headers):
if headers.get("upgrade") != "websocket":
return
return headers.get("sec-websocket-accept")
-
@classmethod
def create_server_nonce(self, client_nonce):
return base64.b64encode(hashlib.sha1(client_nonce + websockets_magic).digest())
diff --git a/netlib/wsgi.py b/netlib/wsgi.py
index d6dfae5d..c66fddc2 100644
--- a/netlib/wsgi.py
+++ b/netlib/wsgi.py
@@ -1,14 +1,13 @@
from __future__ import (absolute_import, print_function, division)
-from io import BytesIO, StringIO
-import urllib
+
import time
import traceback
-
import six
+from io import BytesIO
from six.moves import urllib
-from netlib.utils import always_bytes, native
-from . import http, tcp
+from netlib import http, tcp, strutils
+
class ClientConn(object):
@@ -55,38 +54,38 @@ class WSGIAdaptor(object):
self.app, self.domain, self.port, self.sversion = app, domain, port, sversion
def make_environ(self, flow, errsoc, **extra):
- path = native(flow.request.path, "latin-1")
+ path = strutils.native(flow.request.path, "latin-1")
if '?' in path:
- path_info, query = native(path, "latin-1").split('?', 1)
+ path_info, query = strutils.native(path, "latin-1").split('?', 1)
else:
path_info = path
query = ''
environ = {
'wsgi.version': (1, 0),
- 'wsgi.url_scheme': native(flow.request.scheme, "latin-1"),
+ 'wsgi.url_scheme': strutils.native(flow.request.scheme, "latin-1"),
'wsgi.input': BytesIO(flow.request.content or b""),
'wsgi.errors': errsoc,
'wsgi.multithread': True,
'wsgi.multiprocess': False,
'wsgi.run_once': False,
'SERVER_SOFTWARE': self.sversion,
- 'REQUEST_METHOD': native(flow.request.method, "latin-1"),
+ 'REQUEST_METHOD': strutils.native(flow.request.method, "latin-1"),
'SCRIPT_NAME': '',
'PATH_INFO': urllib.parse.unquote(path_info),
'QUERY_STRING': query,
- 'CONTENT_TYPE': native(flow.request.headers.get('Content-Type', ''), "latin-1"),
- 'CONTENT_LENGTH': native(flow.request.headers.get('Content-Length', ''), "latin-1"),
+ 'CONTENT_TYPE': strutils.native(flow.request.headers.get('Content-Type', ''), "latin-1"),
+ 'CONTENT_LENGTH': strutils.native(flow.request.headers.get('Content-Length', ''), "latin-1"),
'SERVER_NAME': self.domain,
'SERVER_PORT': str(self.port),
- 'SERVER_PROTOCOL': native(flow.request.http_version, "latin-1"),
+ 'SERVER_PROTOCOL': strutils.native(flow.request.http_version, "latin-1"),
}
environ.update(extra)
if flow.client_conn.address:
- environ["REMOTE_ADDR"] = native(flow.client_conn.address.host, "latin-1")
+ environ["REMOTE_ADDR"] = strutils.native(flow.client_conn.address.host, "latin-1")
environ["REMOTE_PORT"] = flow.client_conn.address.port
for key, value in flow.request.headers.items():
- key = 'HTTP_' + native(key, "latin-1").upper().replace('-', '_')
+ key = 'HTTP_' + strutils.native(key, "latin-1").upper().replace('-', '_')
if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
environ[key] = value
return environ
@@ -140,7 +139,7 @@ class WSGIAdaptor(object):
elif state["status"]:
raise AssertionError('Response already started')
state["status"] = status
- state["headers"] = http.Headers([[always_bytes(k), always_bytes(v)] for k,v in headers])
+ state["headers"] = http.Headers([[strutils.always_bytes(k), strutils.always_bytes(v)] for k, v in headers])
if exc_info:
self.error_page(soc, state["headers_sent"], traceback.format_tb(exc_info[2]))
state["headers_sent"] = True
@@ -154,7 +153,7 @@ class WSGIAdaptor(object):
write(i)
if not state["headers_sent"]:
write(b"")
- except Exception as e:
+ except Exception:
try:
s = traceback.format_exc()
errs.write(s.encode("utf-8", "replace"))
diff --git a/pathod/app.py b/pathod/app.py
index aa00ed69..e3216c58 100644
--- a/pathod/app.py
+++ b/pathod/app.py
@@ -1,10 +1,11 @@
import logging
import pprint
-from six.moves import cStringIO as StringIO
+import io
import copy
from flask import Flask, jsonify, render_template, request, abort, make_response
-from . import version, language, utils
+from . import version, language
from netlib.http import user_agents
+from netlib import strutils
logging.basicConfig(level="DEBUG")
EXAMPLE_HOST = "example.com"
@@ -145,7 +146,7 @@ def make_app(noapi, debug):
args["marked"] = v.marked()
return render(template, False, **args)
- s = StringIO()
+ s = io.BytesIO()
settings = copy.copy(app.config["pathod"].settings)
settings.request_host = EXAMPLE_HOST
@@ -166,7 +167,7 @@ def make_app(noapi, debug):
settings.websocket_key = EXAMPLE_WEBSOCKET_KEY
language.serve(safe, s, settings)
- args["output"] = utils.escape_unprintables(s.getvalue())
+ args["output"] = strutils.bytes_to_escaped_str(s.getvalue())
return render(template, False, **args)
@app.route('/response_preview')
diff --git a/pathod/language/__init__.py b/pathod/language/__init__.py
index 32199e08..0841196e 100644
--- a/pathod/language/__init__.py
+++ b/pathod/language/__init__.py
@@ -1,19 +1,26 @@
+from __future__ import absolute_import
+
import itertools
import time
+from six.moves import range
import pyparsing as pp
from . import http, http2, websockets, writer, exceptions
-from exceptions import *
-from base import Settings
-assert Settings # prevent pyflakes from messing with this
+from .exceptions import RenderError, FileAccessDenied, ParseException
+from .base import Settings
+
+__all__ = [
+ "RenderError", "FileAccessDenied", "ParseException",
+ "Settings",
+]
def expand(msg):
times = getattr(msg, "times", None)
if times:
- for j_ in xrange(int(times.value)):
+ for j_ in range(int(times.value)):
yield msg.strike_token("times")
else:
yield msg
diff --git a/pathod/language/base.py b/pathod/language/base.py
index a4302998..11ee0623 100644
--- a/pathod/language/base.py
+++ b/pathod/language/base.py
@@ -3,9 +3,14 @@ import os
import abc
import pyparsing as pp
-from .. import utils
+import six
+from six.moves import reduce
+from netlib import strutils
+from netlib import human
+
from . import generators, exceptions
+
class Settings(object):
def __init__(
@@ -105,7 +110,7 @@ class Token(object):
class _TokValueLiteral(Token):
def __init__(self, val):
- self.val = val.decode("string_escape")
+ self.val = strutils.escaped_str_to_bytes(val)
def get_generator(self, settings_):
return self.val
@@ -130,7 +135,7 @@ class TokValueLiteral(_TokValueLiteral):
return v
def spec(self):
- inner = self.val.encode("string_escape")
+ inner = strutils.bytes_to_escaped_str(self.val)
inner = inner.replace(r"\'", r"\x27")
return "'" + inner + "'"
@@ -143,7 +148,7 @@ class TokValueNakedLiteral(_TokValueLiteral):
return e.setParseAction(lambda x: cls(*x))
def spec(self):
- return self.val.encode("string_escape")
+ return strutils.bytes_to_escaped_str(self.val)
class TokValueGenerate(Token):
@@ -154,14 +159,14 @@ class TokValueGenerate(Token):
self.usize, self.unit, self.datatype = usize, unit, datatype
def bytes(self):
- return self.usize * utils.SIZE_UNITS[self.unit]
+ return self.usize * human.SIZE_UNITS[self.unit]
def get_generator(self, settings_):
return generators.RandomGenerator(self.datatype, self.bytes())
def freeze(self, settings):
g = self.get_generator(settings)
- return TokValueLiteral(g[:].encode("string_escape"))
+ return TokValueLiteral(strutils.bytes_to_escaped_str(g[:]))
@classmethod
def expr(cls):
@@ -169,7 +174,7 @@ class TokValueGenerate(Token):
u = reduce(
operator.or_,
- [pp.Literal(i) for i in utils.SIZE_UNITS.keys()]
+ [pp.Literal(i) for i in human.SIZE_UNITS.keys()]
).leaveWhitespace()
e = e + pp.Optional(u, default=None)
@@ -221,7 +226,7 @@ class TokValueFile(Token):
return generators.FileGenerator(s)
def spec(self):
- return "<'%s'" % self.path.encode("string_escape")
+ return "<'%s'" % strutils.bytes_to_escaped_str(self.path)
TokValue = pp.MatchFirst(
@@ -337,7 +342,7 @@ class OptionsOrValue(_Component):
# it to be canonical. The user can specify a different case by using a
# string value literal.
self.option_used = False
- if isinstance(value, basestring):
+ if isinstance(value, six.string_types):
for i in self.options:
# Find the exact option value in a case-insensitive way
if i.lower() == value.lower():
@@ -573,4 +578,4 @@ class NestedMessage(Token):
def freeze(self, settings):
f = self.parsed.freeze(settings).spec()
- return self.__class__(TokValueLiteral(f.encode("string_escape")))
+ return self.__class__(TokValueLiteral(strutils.bytes_to_escaped_str(f)))
diff --git a/pathod/language/exceptions.py b/pathod/language/exceptions.py
index 84ad3c02..eb22bed2 100644
--- a/pathod/language/exceptions.py
+++ b/pathod/language/exceptions.py
@@ -1,4 +1,3 @@
-
class RenderError(Exception):
pass
diff --git a/pathod/language/generators.py b/pathod/language/generators.py
index a17e7052..9fff3082 100644
--- a/pathod/language/generators.py
+++ b/pathod/language/generators.py
@@ -2,17 +2,19 @@ import string
import random
import mmap
+import six
+
DATATYPES = dict(
- ascii_letters=string.ascii_letters,
- ascii_lowercase=string.ascii_lowercase,
- ascii_uppercase=string.ascii_uppercase,
- digits=string.digits,
- hexdigits=string.hexdigits,
- octdigits=string.octdigits,
- punctuation=string.punctuation,
- whitespace=string.whitespace,
- ascii=string.printable,
- bytes="".join(chr(i) for i in range(256))
+ ascii_letters=string.ascii_letters.encode(),
+ ascii_lowercase=string.ascii_lowercase.encode(),
+ ascii_uppercase=string.ascii_uppercase.encode(),
+ digits=string.digits.encode(),
+ hexdigits=string.hexdigits.encode(),
+ octdigits=string.octdigits.encode(),
+ punctuation=string.punctuation.encode(),
+ whitespace=string.whitespace.encode(),
+ ascii=string.printable.encode(),
+ bytes=bytes(bytearray(range(256)))
)
@@ -35,16 +37,25 @@ class TransformGenerator(object):
def __getitem__(self, x):
d = self.gen.__getitem__(x)
+ if isinstance(x, slice):
+ return self.transform(x.start, d)
return self.transform(x, d)
- def __getslice__(self, a, b):
- d = self.gen.__getslice__(a, b)
- return self.transform(a, d)
-
def __repr__(self):
return "'transform(%s)'" % self.gen
+def rand_byte(chars):
+ """
+ Return a random character as byte from a charset.
+ """
+ # bytearray has consistent behaviour on both Python 2 and 3
+ # while bytes does not
+ if six.PY2:
+ return random.choice(chars)
+ return bytes([random.choice(chars)])
+
+
class RandomGenerator(object):
def __init__(self, dtype, length):
@@ -55,12 +66,10 @@ class RandomGenerator(object):
return self.length
def __getitem__(self, x):
- return random.choice(DATATYPES[self.dtype])
-
- def __getslice__(self, a, b):
- b = min(b, self.length)
chars = DATATYPES[self.dtype]
- return "".join(random.choice(chars) for x in range(a, b))
+ if isinstance(x, slice):
+ return b"".join(rand_byte(chars) for _ in range(*x.indices(self.length)))
+ return rand_byte(chars)
def __repr__(self):
return "%s random from %s" % (self.length, self.dtype)
@@ -70,17 +79,17 @@ class FileGenerator(object):
def __init__(self, path):
self.path = path
- self.fp = file(path, "rb")
+ self.fp = open(path, "rb")
self.map = mmap.mmap(self.fp.fileno(), 0, access=mmap.ACCESS_READ)
def __len__(self):
return len(self.map)
def __getitem__(self, x):
- return self.map.__getitem__(x)
-
- def __getslice__(self, a, b):
- return self.map.__getslice__(a, b)
+ if isinstance(x, slice):
+ return self.map.__getitem__(x)
+ # A slice of length 1 returns a byte object (not an integer)
+ return self.map.__getitem__(slice(x, x + 1 or self.map.size()))
def __repr__(self):
return "<%s" % self.path
diff --git a/pathod/language/http.py b/pathod/language/http.py
index a82f12fe..b2308d5e 100644
--- a/pathod/language/http.py
+++ b/pathod/language/http.py
@@ -11,6 +11,7 @@ from . import base, exceptions, actions, message
# instead of duplicating the HTTP on-the-wire representation here.
# see http2 language for an example
+
class WS(base.CaselessLiteral):
TOK = "ws"
diff --git a/pathod/language/http2.py b/pathod/language/http2.py
index d5e3ca31..85d9047f 100644
--- a/pathod/language/http2.py
+++ b/pathod/language/http2.py
@@ -27,6 +27,7 @@ from . import base, message
h2f:42:DATA:END_STREAM,PADDED:0x1234567:'content body payload'
"""
+
def get_header(val, headers):
"""
Header keys may be Values, so we have to "generate" them as we try the
@@ -48,6 +49,7 @@ class _HeaderMixin(object):
self.value.get_generator(settings),
)
+
class _HTTP2Message(message.Message):
@property
def actions(self):
@@ -287,13 +289,10 @@ class Request(_HTTP2Message):
def spec(self):
return ":".join([i.spec() for i in self.tokens])
+
def make_error_response(reason, body=None):
tokens = [
StatusCode("800"),
Body(base.TokValueLiteral("pathod error: " + (body or reason))),
]
return Response(tokens)
-
-
-# class Frame(message.Message):
-# pass
diff --git a/pathod/language/websockets.py b/pathod/language/websockets.py
index 09443a95..9b752b7e 100644
--- a/pathod/language/websockets.py
+++ b/pathod/language/websockets.py
@@ -1,4 +1,3 @@
-import os
import random
import string
import netlib.websockets
diff --git a/pathod/language/writer.py b/pathod/language/writer.py
index 1a27e1ef..22e32ce2 100644
--- a/pathod/language/writer.py
+++ b/pathod/language/writer.py
@@ -1,6 +1,5 @@
import time
from netlib.exceptions import TcpDisconnect
-import netlib.tcp
BLOCKSIZE = 1024
# It's not clear what the upper limit for time.sleep is. It's lower than the
diff --git a/pathod/log.py b/pathod/log.py
index f203542f..5bf55de4 100644
--- a/pathod/log.py
+++ b/pathod/log.py
@@ -1,8 +1,8 @@
import datetime
-import netlib.utils
-import netlib.tcp
-import netlib.http
+import six
+
+from netlib import strutils
TIMEFMT = '%d-%m-%y %H:%M:%S'
@@ -53,17 +53,17 @@ class LogCtx(object):
]
)
if exc_value:
- raise exc_type, exc_value, traceback
+ six.reraise(exc_type, exc_value, traceback)
def suppress(self):
self.suppressed = True
def dump(self, data, hexdump):
if hexdump:
- for line in netlib.utils.hexdump(data):
+ for line in strutils.hexdump(data):
self("\t%s %s %s" % line)
else:
- for i in netlib.utils.clean_bin(data).split("\n"):
+ for i in strutils.clean_bin(data).split("\n"):
self("\t%s" % i)
def __call__(self, line):
diff --git a/pathod/pathoc.py b/pathod/pathoc.py
index a49ed351..5cfb4591 100644
--- a/pathod/pathoc.py
+++ b/pathod/pathoc.py
@@ -13,21 +13,24 @@ import threading
import OpenSSL.crypto
import six
-from netlib import tcp, http, certutils, websockets, socks
+from netlib import tcp, certutils, websockets, socks
from netlib.exceptions import HttpException, TcpDisconnect, TcpTimeout, TlsException, TcpException, \
NetlibException
from netlib.http import http1, http2
-import language.http
-import language.websockets
-from . import utils, log
+from . import log, language
import logging
from netlib.tutils import treq
+from netlib import strutils
logging.getLogger("hpack").setLevel(logging.WARNING)
+def xrepr(s):
+ return repr(s)[1:-1]
+
+
class PathocError(Exception):
pass
@@ -43,7 +46,7 @@ class SSLInfo(object):
"Cipher: %s, %s bit, %s" % self.cipher,
"SSL certificate chain:"
]
- for n,i in enumerate(self.certchain):
+ for n, i in enumerate(self.certchain):
parts.append(" Certificate [%s]" % n)
parts.append("\tSubject: ")
for cn in i.get_subject().get_components():
@@ -74,7 +77,6 @@ class SSLInfo(object):
return "\n".join(parts)
-
class WebsocketFrameReader(threading.Thread):
def __init__(
@@ -284,7 +286,7 @@ class Pathoc(tcp.TCPClient):
if self.use_http2 and not self.ssl:
raise NotImplementedError("HTTP2 without SSL is not supported.")
- tcp.TCPClient.connect(self)
+ ret = tcp.TCPClient.connect(self)
if connect_to:
self.http_connect(connect_to)
@@ -322,6 +324,7 @@ class Pathoc(tcp.TCPClient):
if self.timeout:
self.settimeout(self.timeout)
+ return ret
def stop(self):
if self.ws_framereader:
@@ -426,7 +429,7 @@ class Pathoc(tcp.TCPClient):
finally:
if resp:
lg("<< %s %s: %s bytes" % (
- resp.status_code, utils.xrepr(resp.reason), len(resp.content)
+ resp.status_code, strutils.bytes_to_escaped_str(resp.reason), len(resp.content)
))
if resp.status_code in self.ignorecodes:
lg.suppress()
diff --git a/pathod/pathod.py b/pathod/pathod.py
index 017ce072..0449c0c1 100644
--- a/pathod/pathod.py
+++ b/pathod/pathod.py
@@ -6,15 +6,11 @@ import sys
import threading
import urllib
-from netlib import tcp, http, certutils, websockets
+from netlib import tcp, certutils, websockets
from netlib.exceptions import HttpException, HttpReadDisconnect, TcpTimeout, TcpDisconnect, \
TlsException
from . import version, app, language, utils, log, protocols
-import language.http
-import language.actions
-import language.exceptions
-import language.websockets
DEFAULT_CERT_DOMAIN = "pathod.net"
@@ -116,7 +112,6 @@ class PathodHandler(tcp.BaseHandler):
return None, response_log
return self.handle_http_request, response_log
-
def handle_http_request(self, logger):
"""
Returns a (handler, log) tuple.
@@ -358,6 +353,8 @@ class Pathod(tcp.TCPServer):
staticdir=self.staticdir
)
+ self.loglock = threading.Lock()
+
def check_policy(self, req, settings):
"""
A policy check that verifies the request size is within limits.
@@ -408,8 +405,7 @@ class Pathod(tcp.TCPServer):
def add_log(self, d):
if not self.noapi:
- lock = threading.Lock()
- with lock:
+ with self.loglock:
d["id"] = self.logid
self.log.insert(0, d)
if len(self.log) > self.LOGBUF:
@@ -418,17 +414,18 @@ class Pathod(tcp.TCPServer):
return d["id"]
def clear_log(self):
- lock = threading.Lock()
- with lock:
+ with self.loglock:
self.log = []
def log_by_id(self, identifier):
- for i in self.log:
- if i["id"] == identifier:
- return i
+ with self.loglock:
+ for i in self.log:
+ if i["id"] == identifier:
+ return i
def get_log(self):
- return self.log
+ with self.loglock:
+ return self.log
def main(args): # pragma: no cover
diff --git a/pathod/pathod_cmdline.py b/pathod/pathod_cmdline.py
index 1f972a49..a4f05faf 100644
--- a/pathod/pathod_cmdline.py
+++ b/pathod/pathod_cmdline.py
@@ -4,7 +4,7 @@ import os
import os.path
import re
-from netlib import tcp
+from netlib import tcp, human
from . import pathod, version, utils
@@ -49,12 +49,12 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
help="""
URL path specifying prefix for URL crafting
commands. (%s)
- """%pathod.DEFAULT_CRAFT_ANCHOR
+ """ % pathod.DEFAULT_CRAFT_ANCHOR
)
parser.add_argument(
"--confdir",
- action="store", type = str, dest="confdir", default='~/.mitmproxy',
- help = "Configuration directory. (~/.mitmproxy)"
+ action="store", type=str, dest="confdir", default='~/.mitmproxy',
+ help="Configuration directory. (~/.mitmproxy)"
)
parser.add_argument(
"-d", dest='staticdir', default=None, type=str,
@@ -117,8 +117,8 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
)
group.add_argument(
"--cert", dest='ssl_certs', default=[], type=str,
- metavar = "SPEC", action="append",
- help = """
+ metavar="SPEC", action="append",
+ help="""
Add an SSL certificate. SPEC is of the form "[domain=]path". The domain
may include a wildcard, and is equal to "*" if not specified. The file
at path is a certificate in PEM format. If a private key is included in
@@ -177,7 +177,6 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
help="Output all received & sent HTTP/2 frames"
)
-
args = parser.parse_args(argv[1:])
args.ssl_version, args.ssl_options = tcp.sslversion_choices[args.ssl_version]
@@ -206,7 +205,7 @@ def args_pathod(argv, stdout_=sys.stdout, stderr_=sys.stderr):
sizelimit = None
if args.sizelimit:
try:
- sizelimit = utils.parse_size(args.sizelimit)
+ sizelimit = human.parse_size(args.sizelimit)
except ValueError as v:
return parser.error(v)
args.sizelimit = sizelimit
diff --git a/pathod/protocols/__init__.py b/pathod/protocols/__init__.py
index 1a8c7dab..f8f3008f 100644
--- a/pathod/protocols/__init__.py
+++ b/pathod/protocols/__init__.py
@@ -1 +1,7 @@
from . import http, http2, websockets
+
+__all__ = [
+ "http",
+ "http2",
+ "websockets",
+]
diff --git a/pathod/protocols/http.py b/pathod/protocols/http.py
index 1f1765cb..d09b5bf2 100644
--- a/pathod/protocols/http.py
+++ b/pathod/protocols/http.py
@@ -1,6 +1,6 @@
-from netlib import tcp, wsgi
-from netlib.exceptions import HttpReadDisconnect, TlsException
-from netlib.http import http1, Request
+from netlib import wsgi
+from netlib.exceptions import TlsException
+from netlib.http import http1
from .. import version, language
diff --git a/pathod/protocols/http2.py b/pathod/protocols/http2.py
index a098a14e..3f45ec80 100644
--- a/pathod/protocols/http2.py
+++ b/pathod/protocols/http2.py
@@ -1,5 +1,6 @@
from netlib.http import http2
-from .. import version, app, language, utils, log
+from .. import language
+
class HTTP2Protocol:
diff --git a/pathod/protocols/websockets.py b/pathod/protocols/websockets.py
index 134d27bc..2b60e618 100644
--- a/pathod/protocols/websockets.py
+++ b/pathod/protocols/websockets.py
@@ -18,7 +18,7 @@ class WebsocketsProtocol:
frm = websockets.Frame.from_file(self.pathod_handler.rfile)
except NetlibException as e:
lg("Error reading websocket frame: %s" % e)
- break
+ return None, None
ended = time.time()
lg(frm.human_readable())
retlog = dict(
diff --git a/pathod/test.py b/pathod/test.py
index 23b7a5b6..11462729 100644
--- a/pathod/test.py
+++ b/pathod/test.py
@@ -1,12 +1,14 @@
from six.moves import cStringIO as StringIO
import threading
+import time
+
from six.moves import queue
-import requests
-import requests.packages.urllib3
from . import pathod
-requests.packages.urllib3.disable_warnings()
+
+class TimeoutError(Exception):
+ pass
class Daemon:
@@ -39,39 +41,51 @@ class Daemon:
"""
return "%s/p/%s" % (self.urlbase, spec)
- def info(self):
- """
- Return some basic info about the remote daemon.
- """
- resp = requests.get("%s/api/info" % self.urlbase, verify=False)
- return resp.json()
-
def text_log(self):
return self.logfp.getvalue()
+ def wait_for_silence(self, timeout=5):
+ start = time.time()
+ while 1:
+ if time.time() - start >= timeout:
+ raise TimeoutError(
+ "%s service threads still alive" %
+ self.thread.server.handler_counter.count
+ )
+ if self.thread.server.handler_counter.count == 0:
+ return
+
+ def expect_log(self, n, timeout=5):
+ l = []
+ start = time.time()
+ while True:
+ l = self.log()
+ if time.time() - start >= timeout:
+ return None
+ if len(l) >= n:
+ break
+ return l
+
def last_log(self):
"""
Returns the last logged request, or None.
"""
- l = self.log()
+ l = self.expect_log(1)
if not l:
return None
- return l[0]
+ return l[-1]
def log(self):
"""
Return the log buffer as a list of dictionaries.
"""
- resp = requests.get("%s/api/log" % self.urlbase, verify=False)
- return resp.json()["log"]
+ return self.thread.server.get_log()
def clear_log(self):
"""
Clear the log.
"""
- self.logfp.truncate(0)
- resp = requests.get("%s/api/clear_log" % self.urlbase, verify=False)
- return resp.ok
+ return self.thread.server.clear_log()
def shutdown(self):
"""
@@ -88,6 +102,7 @@ class _PaThread(threading.Thread):
self.name = "PathodThread"
self.iface, self.q, self.ssl = iface, q, ssl
self.daemonargs = daemonargs
+ self.server = None
def run(self):
self.server = pathod.Pathod(
diff --git a/pathod/utils.py b/pathod/utils.py
index d1e2dd00..3276198a 100644
--- a/pathod/utils.py
+++ b/pathod/utils.py
@@ -3,15 +3,6 @@ import sys
import netlib.utils
-SIZE_UNITS = dict(
- b=1024 ** 0,
- k=1024 ** 1,
- m=1024 ** 2,
- g=1024 ** 3,
- t=1024 ** 4,
-)
-
-
class MemBool(object):
"""
@@ -26,20 +17,6 @@ class MemBool(object):
return bool(v)
-def parse_size(s):
- try:
- return int(s)
- except ValueError:
- pass
- for i in SIZE_UNITS.keys():
- if s.endswith(i):
- try:
- return int(s[:-1]) * SIZE_UNITS[i]
- except ValueError:
- break
- raise ValueError("Invalid size specification.")
-
-
def parse_anchor_spec(s):
"""
Return a tuple, or None on error.
@@ -49,33 +26,6 @@ def parse_anchor_spec(s):
return tuple(s.split("=", 1))
-def xrepr(s):
- return repr(s)[1:-1]
-
-
-def inner_repr(s):
- """
- Returns the inner portion of a string or unicode repr (i.e. without the
- quotes)
- """
- if isinstance(s, unicode):
- return repr(s)[2:-1]
- else:
- return repr(s)[1:-1]
-
-
-def escape_unprintables(s):
- """
- Like inner_repr, but preserves line breaks.
- """
- s = s.replace("\r\n", "PATHOD_MARKER_RN")
- s = s.replace("\n", "PATHOD_MARKER_N")
- s = inner_repr(s)
- s = s.replace("PATHOD_MARKER_RN", "\n")
- s = s.replace("PATHOD_MARKER_N", "\n")
- return s
-
-
data = netlib.utils.Data(__name__)
diff --git a/pathod/version.py b/pathod/version.py
index 2da7637d..3441be92 100644
--- a/pathod/version.py
+++ b/pathod/version.py
@@ -4,3 +4,10 @@ from netlib.version import VERSION, IVERSION
NAME = "pathod"
NAMEVERSION = NAME + " " + VERSION
+
+__all__ = [
+ "NAME",
+ "NAMEVERSION",
+ "VERSION",
+ "IVERSION",
+]
diff --git a/setup.cfg b/setup.cfg
index d51ddc14..eeaac0c8 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,11 +1,9 @@
[flake8]
-max-line-length = 120
-max-complexity = 20
-
-[pep8]
-max-line-length = 120
-exclude = */contrib/*
-ignore = E251
+max-line-length = 140
+max-complexity = 25
+ignore = E251,C901
+exclude = mitmproxy/contrib/*,test/mitmproxy/data/*
+builtins = file,open,basestring,xrange,unicode,long,cmp
[pytest]
testpaths = test
@@ -13,8 +11,7 @@ addopts = --capture=no
[coverage:run]
branch = True
-include = *mitmproxy*, *netlib*, *pathod*
-omit = *contrib*, *tnetstring*, *platform*, *console*, *main.py
+omit = *contrib*, *tnetstring*, *platform*, *main.py
[coverage:report]
show_missing = True
diff --git a/setup.py b/setup.py
index 2ef799a5..e68be13a 100644
--- a/setup.py
+++ b/setup.py
@@ -65,7 +65,7 @@ setup(
"certifi>=2015.11.20.1", # no semver here - this should always be on the last release!
"configargparse>=0.10, <0.11",
"construct>=2.5.2, <2.6",
- "cryptography>=1.3,<1.4",
+ "cryptography>=1.3, <1.4",
"Flask>=0.10.1, <0.11",
"h2>=2.3.1, <3",
"html2text>=2016.1.8, <=2016.4.2",
@@ -75,7 +75,7 @@ setup(
"passlib>=1.6.5, <1.7",
"pyasn1>=0.1.9, <0.2",
"pyOpenSSL>=16.0, <17.0",
- "pyparsing>=2.0,<2.1", # 2.1.1 breaks our binaries, see https://sourceforge.net/p/pyparsing/bugs/93/
+ "pyparsing>=2.0, <2.1", # 2.1.1 breaks our binaries, see https://sourceforge.net/p/pyparsing/bugs/93/
"pyperclip>=1.5.22, <1.6",
"requests>=2.9.1, <2.10",
"six>=1.10, <1.11",
@@ -98,8 +98,9 @@ setup(
],
'dev': [
"coveralls>=1.1, <1.2",
- "mock>=2.0,<2.1",
- "pytest>=2.8.7,<2.10",
+ "mock>=2.0, <2.1",
+ "flake8>=2.5.4, <3",
+ "pytest>=2.8.7, <2.10",
"pytest-cov>=2.2.1, <2.3",
"pytest-timeout>=1.0.0, <1.1",
"pytest-xdist>=1.14, <1.15",
diff --git a/test/mitmproxy/console/__init__.py b/test/mitmproxy/console/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/mitmproxy/console/__init__.py
diff --git a/test/mitmproxy/test_console_common.py b/test/mitmproxy/console/test_common.py
index 219200e0..ac4e0209 100644
--- a/test/mitmproxy/test_console_common.py
+++ b/test/mitmproxy/console/test_common.py
@@ -1,13 +1,8 @@
-import os
-from unittest.case import SkipTest
-if os.name == "nt":
- raise SkipTest("Skipped on Windows.")
-
-
import mitmproxy.console.common as common
-from . import tutils
+from .. import tutils
+@tutils.skip_appveyor
def test_format_flow():
f = tutils.tflow(resp=True)
assert common.format_flow(f, True)
diff --git a/test/mitmproxy/test_console.py b/test/mitmproxy/console/test_console.py
index 58a812a6..b68dd811 100644
--- a/test/mitmproxy/test_console.py
+++ b/test/mitmproxy/console/test_console.py
@@ -4,7 +4,7 @@ import netlib.tutils
from mitmproxy import console
from mitmproxy.console import common
-from . import tutils
+from .. import tutils
class TestConsoleState:
diff --git a/test/mitmproxy/test_console_help.py b/test/mitmproxy/console/test_help.py
index 0589bd68..3c1cc57c 100644
--- a/test/mitmproxy/test_console_help.py
+++ b/test/mitmproxy/console/test_help.py
@@ -1,11 +1,8 @@
-import os
-from unittest.case import SkipTest
-if os.name == "nt":
- raise SkipTest("Skipped on Windows.")
-
import mitmproxy.console.help as help
+from .. import tutils
+@tutils.skip_appveyor
class TestHelp:
def test_helptext(self):
diff --git a/test/mitmproxy/test_console_palettes.py b/test/mitmproxy/console/test_palettes.py
index b5d84ddd..a99730fa 100644
--- a/test/mitmproxy/test_console_palettes.py
+++ b/test/mitmproxy/console/test_palettes.py
@@ -1,10 +1,8 @@
-import os
-from unittest.case import SkipTest
-if os.name == "nt":
- raise SkipTest("Skipped on Windows.")
import mitmproxy.console.palettes as palettes
+from .. import tutils
+@tutils.skip_appveyor
class TestPalette:
def test_helptext(self):
diff --git a/test/mitmproxy/test_console_pathedit.py b/test/mitmproxy/console/test_pathedit.py
index e2c27b7c..107a48ac 100644
--- a/test/mitmproxy/test_console_pathedit.py
+++ b/test/mitmproxy/console/test_pathedit.py
@@ -2,7 +2,7 @@ import os
from os.path import normpath
from mitmproxy.console import pathedit
-from . import tutils
+from .. import tutils
class TestPathCompleter:
diff --git a/test/mitmproxy/data/har_extractor.har b/test/mitmproxy/data/har_extractor.har
index 2f5099b3..6b5e2994 100644
--- a/test/mitmproxy/data/har_extractor.har
+++ b/test/mitmproxy/data/har_extractor.har
@@ -58,12 +58,12 @@
},
"headers": [
{
- "name": "content-length",
- "value": "7"
- },
- {
"name": "header-response",
"value": "svalue"
+ },
+ {
+ "name": "content-length",
+ "value": "7"
}
],
"headersSize": 44,
@@ -75,4 +75,4 @@
]
}
}
-} \ No newline at end of file
+}
diff --git a/test/mitmproxy/scripts/a.py b/test/mitmproxy/data/scripts/a.py
index d4272ac8..d4272ac8 100644
--- a/test/mitmproxy/scripts/a.py
+++ b/test/mitmproxy/data/scripts/a.py
diff --git a/test/mitmproxy/scripts/a_helper.py b/test/mitmproxy/data/scripts/a_helper.py
index e1f1c649..e1f1c649 100644
--- a/test/mitmproxy/scripts/a_helper.py
+++ b/test/mitmproxy/data/scripts/a_helper.py
diff --git a/test/mitmproxy/scripts/all.py b/test/mitmproxy/data/scripts/all.py
index dad2aade..dad2aade 100644
--- a/test/mitmproxy/scripts/all.py
+++ b/test/mitmproxy/data/scripts/all.py
diff --git a/test/mitmproxy/scripts/concurrent_decorator.py b/test/mitmproxy/data/scripts/concurrent_decorator.py
index cf3ab512..e017f605 100644
--- a/test/mitmproxy/scripts/concurrent_decorator.py
+++ b/test/mitmproxy/data/scripts/concurrent_decorator.py
@@ -1,6 +1,7 @@
import time
from mitmproxy.script import concurrent
+
@concurrent
def request(context, flow):
time.sleep(0.1)
diff --git a/test/mitmproxy/scripts/concurrent_decorator_err.py b/test/mitmproxy/data/scripts/concurrent_decorator_err.py
index 071b8889..071b8889 100644
--- a/test/mitmproxy/scripts/concurrent_decorator_err.py
+++ b/test/mitmproxy/data/scripts/concurrent_decorator_err.py
diff --git a/test/mitmproxy/scripts/duplicate_flow.py b/test/mitmproxy/data/scripts/duplicate_flow.py
index e13af786..e13af786 100644
--- a/test/mitmproxy/scripts/duplicate_flow.py
+++ b/test/mitmproxy/data/scripts/duplicate_flow.py
diff --git a/test/mitmproxy/scripts/loaderr.py b/test/mitmproxy/data/scripts/loaderr.py
index 8dc4d56d..8dc4d56d 100644
--- a/test/mitmproxy/scripts/loaderr.py
+++ b/test/mitmproxy/data/scripts/loaderr.py
diff --git a/test/mitmproxy/scripts/reqerr.py b/test/mitmproxy/data/scripts/reqerr.py
index e7c503a8..e7c503a8 100644
--- a/test/mitmproxy/scripts/reqerr.py
+++ b/test/mitmproxy/data/scripts/reqerr.py
diff --git a/test/mitmproxy/scripts/starterr.py b/test/mitmproxy/data/scripts/starterr.py
index b217bdfe..b217bdfe 100644
--- a/test/mitmproxy/scripts/starterr.py
+++ b/test/mitmproxy/data/scripts/starterr.py
diff --git a/test/mitmproxy/scripts/stream_modify.py b/test/mitmproxy/data/scripts/stream_modify.py
index e26d83f1..e26d83f1 100644
--- a/test/mitmproxy/scripts/stream_modify.py
+++ b/test/mitmproxy/data/scripts/stream_modify.py
diff --git a/test/mitmproxy/scripts/syntaxerr.py b/test/mitmproxy/data/scripts/syntaxerr.py
index 219d6b84..219d6b84 100644
--- a/test/mitmproxy/scripts/syntaxerr.py
+++ b/test/mitmproxy/data/scripts/syntaxerr.py
diff --git a/test/mitmproxy/scripts/tcp_stream_modify.py b/test/mitmproxy/data/scripts/tcp_stream_modify.py
index d7953ef9..d7953ef9 100644
--- a/test/mitmproxy/scripts/tcp_stream_modify.py
+++ b/test/mitmproxy/data/scripts/tcp_stream_modify.py
diff --git a/test/mitmproxy/scripts/unloaderr.py b/test/mitmproxy/data/scripts/unloaderr.py
index fba02734..fba02734 100644
--- a/test/mitmproxy/scripts/unloaderr.py
+++ b/test/mitmproxy/data/scripts/unloaderr.py
diff --git a/test/mitmproxy/test_flow_export/locust_get.py b/test/mitmproxy/data/test_flow_export/locust_get.py
index 632d5d53..632d5d53 100644
--- a/test/mitmproxy/test_flow_export/locust_get.py
+++ b/test/mitmproxy/data/test_flow_export/locust_get.py
diff --git a/test/mitmproxy/test_flow_export/locust_patch.py b/test/mitmproxy/data/test_flow_export/locust_patch.py
index f64e0857..f64e0857 100644
--- a/test/mitmproxy/test_flow_export/locust_patch.py
+++ b/test/mitmproxy/data/test_flow_export/locust_patch.py
diff --git a/test/mitmproxy/test_flow_export/locust_post.py b/test/mitmproxy/data/test_flow_export/locust_post.py
index df23476a..df23476a 100644
--- a/test/mitmproxy/test_flow_export/locust_post.py
+++ b/test/mitmproxy/data/test_flow_export/locust_post.py
diff --git a/test/mitmproxy/test_flow_export/locust_task_get.py b/test/mitmproxy/data/test_flow_export/locust_task_get.py
index 03821cd8..03821cd8 100644
--- a/test/mitmproxy/test_flow_export/locust_task_get.py
+++ b/test/mitmproxy/data/test_flow_export/locust_task_get.py
diff --git a/test/mitmproxy/test_flow_export/locust_task_patch.py b/test/mitmproxy/data/test_flow_export/locust_task_patch.py
index d425209c..d425209c 100644
--- a/test/mitmproxy/test_flow_export/locust_task_patch.py
+++ b/test/mitmproxy/data/test_flow_export/locust_task_patch.py
diff --git a/test/mitmproxy/test_flow_export/locust_task_post.py b/test/mitmproxy/data/test_flow_export/locust_task_post.py
index 989df455..989df455 100644
--- a/test/mitmproxy/test_flow_export/locust_task_post.py
+++ b/test/mitmproxy/data/test_flow_export/locust_task_post.py
diff --git a/test/mitmproxy/test_flow_export/python_get.py b/test/mitmproxy/data/test_flow_export/python_get.py
index af8f7c81..af8f7c81 100644
--- a/test/mitmproxy/test_flow_export/python_get.py
+++ b/test/mitmproxy/data/test_flow_export/python_get.py
diff --git a/test/mitmproxy/test_flow_export/python_patch.py b/test/mitmproxy/data/test_flow_export/python_patch.py
index 159e802f..159e802f 100644
--- a/test/mitmproxy/test_flow_export/python_patch.py
+++ b/test/mitmproxy/data/test_flow_export/python_patch.py
diff --git a/test/mitmproxy/test_flow_export/python_post.py b/test/mitmproxy/data/test_flow_export/python_post.py
index b13f6441..b13f6441 100644
--- a/test/mitmproxy/test_flow_export/python_post.py
+++ b/test/mitmproxy/data/test_flow_export/python_post.py
diff --git a/test/mitmproxy/test_flow_export/python_post_json.py b/test/mitmproxy/data/test_flow_export/python_post_json.py
index 7e105bf6..6c1b9740 100644
--- a/test/mitmproxy/test_flow_export/python_post_json.py
+++ b/test/mitmproxy/data/test_flow_export/python_post_json.py
@@ -6,11 +6,13 @@ headers = {
'content-type': 'application/json',
}
+
json = {
- "name": "example",
- "email": "example@example.com"
+ u'email': u'example@example.com',
+ u'name': u'example',
}
+
response = requests.request(
method='POST',
url=url,
diff --git a/test/mitmproxy/script/test_concurrent.py b/test/mitmproxy/script/test_concurrent.py
index a9c9e153..c2f169ad 100644
--- a/test/mitmproxy/script/test_concurrent.py
+++ b/test/mitmproxy/script/test_concurrent.py
@@ -11,7 +11,7 @@ class Dummy:
@tutils.skip_appveyor
def test_concurrent():
- with Script(tutils.test_data.path("scripts/concurrent_decorator.py"), None) as s:
+ with Script(tutils.test_data.path("data/scripts/concurrent_decorator.py"), None) as s:
def reply():
reply.acked.set()
reply.acked = Event()
@@ -27,6 +27,6 @@ def test_concurrent():
def test_concurrent_err():
- s = Script(tutils.test_data.path("scripts/concurrent_decorator_err.py"), None)
+ s = Script(tutils.test_data.path("data/scripts/concurrent_decorator_err.py"), None)
with tutils.raises("Concurrent decorator not supported for 'start' method"):
s.load()
diff --git a/test/mitmproxy/script/test_script.py b/test/mitmproxy/script/test_script.py
index a9b55977..fe98fab5 100644
--- a/test/mitmproxy/script/test_script.py
+++ b/test/mitmproxy/script/test_script.py
@@ -21,19 +21,19 @@ class TestParseCommand:
def test_parse_args(self):
with tutils.chdir(tutils.test_data.dirname):
- assert Script.parse_command("scripts/a.py") == ["scripts/a.py"]
- assert Script.parse_command("scripts/a.py foo bar") == ["scripts/a.py", "foo", "bar"]
- assert Script.parse_command("scripts/a.py 'foo bar'") == ["scripts/a.py", "foo bar"]
+ assert Script.parse_command("data/scripts/a.py") == ["data/scripts/a.py"]
+ assert Script.parse_command("data/scripts/a.py foo bar") == ["data/scripts/a.py", "foo", "bar"]
+ assert Script.parse_command("data/scripts/a.py 'foo bar'") == ["data/scripts/a.py", "foo bar"]
@tutils.skip_not_windows
def test_parse_windows(self):
with tutils.chdir(tutils.test_data.dirname):
- assert Script.parse_command("scripts\\a.py") == ["scripts\\a.py"]
- assert Script.parse_command("scripts\\a.py 'foo \\ bar'") == ["scripts\\a.py", 'foo \\ bar']
+ assert Script.parse_command("data\\scripts\\a.py") == ["data\\scripts\\a.py"]
+ assert Script.parse_command("data\\scripts\\a.py 'foo \\ bar'") == ["data\\scripts\\a.py", 'foo \\ bar']
def test_simple():
- with tutils.chdir(tutils.test_data.path("scripts")):
+ with tutils.chdir(tutils.test_data.path("data/scripts")):
s = Script("a.py --var 42", None)
assert s.filename == "a.py"
assert s.ns is None
@@ -55,7 +55,7 @@ def test_simple():
def test_script_exception():
- with tutils.chdir(tutils.test_data.path("scripts")):
+ with tutils.chdir(tutils.test_data.path("data/scripts")):
s = Script("syntaxerr.py", None)
with tutils.raises(ScriptException):
s.load()
diff --git a/test/mitmproxy/test_app.py b/test/mitmproxy/test_app.py
index 8d8ce271..4c9eff08 100644
--- a/test/mitmproxy/test_app.py
+++ b/test/mitmproxy/test_app.py
@@ -1,4 +1,4 @@
-from . import tutils, tservers
+from . import tservers
class TestApp(tservers.HTTPProxyTest):
@@ -7,8 +7,7 @@ class TestApp(tservers.HTTPProxyTest):
assert self.app("/").status_code == 200
def test_cert(self):
- with tutils.tmpdir() as d:
- for ext in ["pem", "p12"]:
- resp = self.app("/cert/%s" % ext)
- assert resp.status_code == 200
- assert resp.content
+ for ext in ["pem", "p12"]:
+ resp = self.app("/cert/%s" % ext)
+ assert resp.status_code == 200
+ assert resp.content
diff --git a/test/mitmproxy/test_contentview.py b/test/mitmproxy/test_contentview.py
index c00afa5f..9142bdad 100644
--- a/test/mitmproxy/test_contentview.py
+++ b/test/mitmproxy/test_contentview.py
@@ -1,8 +1,8 @@
from mitmproxy.exceptions import ContentViewException
from netlib.http import Headers
from netlib.odict import ODict
-import netlib.utils
from netlib import encoding
+from netlib.http import url
import mitmproxy.contentviews as cv
from . import tutils
@@ -60,10 +60,10 @@ class TestContentView:
assert f[0] == "Query"
def test_view_urlencoded(self):
- d = netlib.utils.urlencode([("one", "two"), ("three", "four")])
+ d = url.encode([("one", "two"), ("three", "four")])
v = cv.ViewURLEncoded()
assert v(d)
- d = netlib.utils.urlencode([("adsfa", "")])
+ d = url.encode([("adsfa", "")])
v = cv.ViewURLEncoded()
assert v(d)
diff --git a/test/mitmproxy/test_controller.py b/test/mitmproxy/test_controller.py
index f7bf615a..83ad428e 100644
--- a/test/mitmproxy/test_controller.py
+++ b/test/mitmproxy/test_controller.py
@@ -2,7 +2,7 @@ from threading import Thread, Event
from mock import Mock
-from mitmproxy.controller import Reply, DummyReply, Channel, ServerThread, ServerMaster, Master
+from mitmproxy import controller
from six.moves import queue
from mitmproxy.exceptions import Kill
@@ -10,11 +10,15 @@ from mitmproxy.proxy import DummyServer
from netlib.tutils import raises
+class TMsg:
+ pass
+
+
class TestMaster(object):
def test_simple(self):
-
- class DummyMaster(Master):
- def handle_panic(self, _):
+ class DummyMaster(controller.Master):
+ @controller.handler
+ def log(self, _):
m.should_exit.set()
def tick(self, timeout):
@@ -23,14 +27,14 @@ class TestMaster(object):
m = DummyMaster()
assert not m.should_exit.is_set()
- m.event_queue.put(("panic", 42))
+ msg = TMsg()
+ msg.reply = controller.DummyReply()
+ m.event_queue.put(("log", msg))
m.run()
assert m.should_exit.is_set()
-
-class TestServerMaster(object):
- def test_simple(self):
- m = ServerMaster()
+ def test_server_simple(self):
+ m = controller.Master()
s = DummyServer(None)
m.add_server(s)
m.start()
@@ -42,7 +46,7 @@ class TestServerMaster(object):
class TestServerThread(object):
def test_simple(self):
m = Mock()
- t = ServerThread(m)
+ t = controller.ServerThread(m)
t.run()
assert m.serve_forever.called
@@ -50,7 +54,7 @@ class TestServerThread(object):
class TestChannel(object):
def test_tell(self):
q = queue.Queue()
- channel = Channel(q, Event())
+ channel = controller.Channel(q, Event())
m = Mock()
channel.tell("test", m)
assert q.get() == ("test", m)
@@ -66,21 +70,21 @@ class TestChannel(object):
Thread(target=reply).start()
- channel = Channel(q, Event())
+ channel = controller.Channel(q, Event())
assert channel.ask("test", Mock()) == 42
def test_ask_shutdown(self):
q = queue.Queue()
done = Event()
done.set()
- channel = Channel(q, done)
+ channel = controller.Channel(q, done)
with raises(Kill):
channel.ask("test", Mock())
class TestDummyReply(object):
def test_simple(self):
- reply = DummyReply()
+ reply = controller.DummyReply()
assert not reply.acked
reply()
assert reply.acked
@@ -88,18 +92,18 @@ class TestDummyReply(object):
class TestReply(object):
def test_simple(self):
- reply = Reply(42)
+ reply = controller.Reply(42)
assert not reply.acked
reply("foo")
assert reply.acked
assert reply.q.get() == "foo"
def test_default(self):
- reply = Reply(42)
+ reply = controller.Reply(42)
reply()
assert reply.q.get() == 42
def test_reply_none(self):
- reply = Reply(42)
+ reply = controller.Reply(42)
reply(None)
assert reply.q.get() is None
diff --git a/test/mitmproxy/test_dump.py b/test/mitmproxy/test_dump.py
index ad4cee53..36b78168 100644
--- a/test/mitmproxy/test_dump.py
+++ b/test/mitmproxy/test_dump.py
@@ -64,14 +64,14 @@ class TestDumpMaster:
f = tutils.tflow(req=netlib.tutils.treq(content=content))
l = Log("connect")
l.reply = mock.MagicMock()
- m.handle_log(l)
- m.handle_clientconnect(f.client_conn)
- m.handle_serverconnect(f.server_conn)
- m.handle_request(f)
+ m.log(l)
+ m.clientconnect(f.client_conn)
+ m.serverconnect(f.server_conn)
+ m.request(f)
if not f.error:
f.response = HTTPResponse.wrap(netlib.tutils.tresp(content=content))
- f = m.handle_response(f)
- m.handle_clientdisconnect(f.client_conn)
+ f = m.response(f)
+ m.clientdisconnect(f.client_conn)
return f
def _dummy_cycle(self, n, filt, content, **options):
@@ -95,8 +95,8 @@ class TestDumpMaster:
o = dump.Options(flow_detail=1)
m = dump.DumpMaster(None, o, outfile=cs)
f = tutils.tflow(err=True)
- m.handle_request(f)
- assert m.handle_error(f)
+ m.request(f)
+ assert m.error(f)
assert "error" in cs.getvalue()
def test_missing_content(self):
@@ -105,10 +105,10 @@ class TestDumpMaster:
m = dump.DumpMaster(None, o, outfile=cs)
f = tutils.tflow()
f.request.content = None
- m.handle_request(f)
+ m.request(f)
f.response = HTTPResponse.wrap(netlib.tutils.tresp())
f.response.content = None
- m.handle_response(f)
+ m.response(f)
assert "content missing" in cs.getvalue()
def test_replay(self):
@@ -160,7 +160,7 @@ class TestDumpMaster:
assert o.verbosity == 2
def test_filter(self):
- assert not "GET" in self._dummy_cycle(1, "~u foo", "", verbosity=1)
+ assert "GET" not in self._dummy_cycle(1, "~u foo", "", verbosity=1)
def test_app(self):
o = dump.Options(app=True)
@@ -218,7 +218,7 @@ class TestDumpMaster:
def test_script(self):
ret = self._dummy_cycle(
1, None, "",
- scripts=[tutils.test_data.path("scripts/all.py")], verbosity=1
+ scripts=[tutils.test_data.path("data/scripts/all.py")], verbosity=1
)
assert "XCLIENTCONNECT" in ret
assert "XSERVERCONNECT" in ret
diff --git a/test/mitmproxy/test_examples.py b/test/mitmproxy/test_examples.py
index c4b06f4b..607d6faf 100644
--- a/test/mitmproxy/test_examples.py
+++ b/test/mitmproxy/test_examples.py
@@ -3,7 +3,7 @@ import json
import os
from contextlib import contextmanager
-from mitmproxy import utils, script
+from mitmproxy import script
from mitmproxy.proxy import config
import netlib.utils
from netlib import tutils as netutils
diff --git a/test/mitmproxy/test_flow.py b/test/mitmproxy/test_flow.py
index 3e78a5c4..1b1f03f9 100644
--- a/test/mitmproxy/test_flow.py
+++ b/test/mitmproxy/test_flow.py
@@ -4,7 +4,6 @@ from six.moves import cStringIO as StringIO
import mock
import netlib.utils
-from netlib import odict
from netlib.http import Headers
from mitmproxy import filt, controller, tnetstring, flow
from mitmproxy.exceptions import FlowReadException, ScriptException
@@ -54,8 +53,8 @@ class TestStickyCookieState:
assert s.domain_match("www.google.com", ".google.com")
assert s.domain_match("google.com", ".google.com")
- def test_handle_response(self):
- c = "SSID=mooo; domain=.google.com, FOO=bar; Domain=.google.com; Path=/; "\
+ def test_response(self):
+ c = "SSID=mooo; domain=.google.com, FOO=bar; Domain=.google.com; Path=/; " \
"Expires=Wed, 13-Jan-2021 22:23:01 GMT; Secure; "
s, f = self._response(c, "host")
@@ -101,7 +100,7 @@ class TestStickyCookieState:
assert len(s.jar[googlekey].keys()) == 1
assert s.jar[googlekey]["somecookie"].items()[0][1] == "newvalue"
- def test_handle_request(self):
+ def test_request(self):
s, f = self._response("SSID=mooo", "www.google.com")
assert "cookie" not in f.request.headers
s.handle_request(f)
@@ -110,7 +109,7 @@ class TestStickyCookieState:
class TestStickyAuthState:
- def test_handle_response(self):
+ def test_response(self):
s = flow.StickyAuthState(filt.parse(".*"))
f = tutils.tflow(resp=True)
f.request.headers["authorization"] = "foo"
@@ -389,22 +388,22 @@ class TestFlow(object):
del b["id"]
assert a == b
assert not f == f2
- assert not f is f2
+ assert f is not f2
assert f.request.get_state() == f2.request.get_state()
- assert not f.request is f2.request
+ assert f.request is not f2.request
assert f.request.headers == f2.request.headers
- assert not f.request.headers is f2.request.headers
+ assert f.request.headers is not f2.request.headers
assert f.response.get_state() == f2.response.get_state()
- assert not f.response is f2.response
+ assert f.response is not f2.response
f = tutils.tflow(err=True)
f2 = f.copy()
- assert not f is f2
- assert not f.request is f2.request
+ assert f is not f2
+ assert f.request is not f2.request
assert f.request.headers == f2.request.headers
- assert not f.request.headers is f2.request.headers
+ assert f.request.headers is not f2.request.headers
assert f.error.get_state() == f2.error.get_state()
- assert not f.error is f2.error
+ assert f.error is not f2.error
def test_match(self):
f = tutils.tflow(resp=True)
@@ -461,25 +460,20 @@ class TestFlow(object):
fm = flow.FlowMaster(None, s)
f = tutils.tflow()
f.intercept(mock.Mock())
- assert not f.reply.acked
f.kill(fm)
- assert f.reply.acked
+ for i in s.view:
+ assert "killed" in str(i.error)
def test_killall(self):
s = flow.State()
fm = flow.FlowMaster(None, s)
f = tutils.tflow()
- fm.handle_request(f)
-
- f = tutils.tflow()
- fm.handle_request(f)
+ f.intercept(fm)
- for i in s.view:
- assert not i.reply.acked
s.killall(fm)
for i in s.view:
- assert i.reply.acked
+ assert "killed" in str(i.error)
def test_accept_intercept(self):
f = tutils.tflow()
@@ -767,13 +761,13 @@ class TestFlowMaster:
s = flow.State()
fm = flow.FlowMaster(None, s)
- fm.load_script(tutils.test_data.path("scripts/a.py"))
- fm.load_script(tutils.test_data.path("scripts/a.py"))
+ fm.load_script(tutils.test_data.path("data/scripts/a.py"))
+ fm.load_script(tutils.test_data.path("data/scripts/a.py"))
fm.unload_scripts()
with tutils.raises(ScriptException):
fm.load_script("nonexistent")
try:
- fm.load_script(tutils.test_data.path("scripts/starterr.py"))
+ fm.load_script(tutils.test_data.path("data/scripts/starterr.py"))
except ScriptException as e:
assert "ValueError" in str(e)
assert len(fm.scripts) == 0
@@ -802,39 +796,39 @@ class TestFlowMaster:
def test_script_reqerr(self):
s = flow.State()
fm = flow.FlowMaster(None, s)
- fm.load_script(tutils.test_data.path("scripts/reqerr.py"))
+ fm.load_script(tutils.test_data.path("data/scripts/reqerr.py"))
f = tutils.tflow()
- fm.handle_clientconnect(f.client_conn)
- assert fm.handle_request(f)
+ fm.clientconnect(f.client_conn)
+ assert fm.request(f)
def test_script(self):
s = flow.State()
fm = flow.FlowMaster(None, s)
- fm.load_script(tutils.test_data.path("scripts/all.py"))
+ fm.load_script(tutils.test_data.path("data/scripts/all.py"))
f = tutils.tflow(resp=True)
- fm.handle_clientconnect(f.client_conn)
+ fm.clientconnect(f.client_conn)
assert fm.scripts[0].ns["log"][-1] == "clientconnect"
- fm.handle_serverconnect(f.server_conn)
+ fm.serverconnect(f.server_conn)
assert fm.scripts[0].ns["log"][-1] == "serverconnect"
- fm.handle_request(f)
+ fm.request(f)
assert fm.scripts[0].ns["log"][-1] == "request"
- fm.handle_response(f)
+ fm.response(f)
assert fm.scripts[0].ns["log"][-1] == "response"
# load second script
- fm.load_script(tutils.test_data.path("scripts/all.py"))
+ fm.load_script(tutils.test_data.path("data/scripts/all.py"))
assert len(fm.scripts) == 2
- fm.handle_clientdisconnect(f.server_conn)
+ fm.clientdisconnect(f.server_conn)
assert fm.scripts[0].ns["log"][-1] == "clientdisconnect"
assert fm.scripts[1].ns["log"][-1] == "clientdisconnect"
# unload first script
fm.unload_scripts()
assert len(fm.scripts) == 0
- fm.load_script(tutils.test_data.path("scripts/all.py"))
+ fm.load_script(tutils.test_data.path("data/scripts/all.py"))
f.error = tutils.terr()
- fm.handle_error(f)
+ fm.error(f)
assert fm.scripts[0].ns["log"][-1] == "error"
def test_duplicate_flow(self):
@@ -859,23 +853,22 @@ class TestFlowMaster:
fm.anticache = True
fm.anticomp = True
f = tutils.tflow(req=None)
- fm.handle_clientconnect(f.client_conn)
+ fm.clientconnect(f.client_conn)
f.request = HTTPRequest.wrap(netlib.tutils.treq())
- fm.handle_request(f)
+ fm.request(f)
assert s.flow_count() == 1
f.response = HTTPResponse.wrap(netlib.tutils.tresp())
- fm.handle_response(f)
- assert not fm.handle_response(None)
+ fm.response(f)
assert s.flow_count() == 1
- fm.handle_clientdisconnect(f.client_conn)
+ fm.clientdisconnect(f.client_conn)
f.error = Error("msg")
f.error.reply = controller.DummyReply()
- fm.handle_error(f)
+ fm.error(f)
- fm.load_script(tutils.test_data.path("scripts/a.py"))
+ fm.load_script(tutils.test_data.path("data/scripts/a.py"))
fm.shutdown()
def test_client_playback(self):
@@ -902,7 +895,7 @@ class TestFlowMaster:
assert fm.state.flow_count()
f.error = Error("error")
- fm.handle_error(f)
+ fm.error(f)
def test_server_playback(self):
s = flow.State()
@@ -983,12 +976,12 @@ class TestFlowMaster:
fm.set_stickycookie(".*")
f = tutils.tflow(resp=True)
f.response.headers["set-cookie"] = "foo=bar"
- fm.handle_request(f)
- fm.handle_response(f)
+ fm.request(f)
+ fm.response(f)
assert fm.stickycookie_state.jar
- assert not "cookie" in f.request.headers
+ assert "cookie" not in f.request.headers
f = f.copy()
- fm.handle_request(f)
+ fm.request(f)
assert f.request.headers["cookie"] == "foo=bar"
def test_stickyauth(self):
@@ -1003,12 +996,12 @@ class TestFlowMaster:
fm.set_stickyauth(".*")
f = tutils.tflow(resp=True)
f.request.headers["authorization"] = "foo"
- fm.handle_request(f)
+ fm.request(f)
f = tutils.tflow(resp=True)
assert fm.stickyauth_state.hosts
- assert not "authorization" in f.request.headers
- fm.handle_request(f)
+ assert "authorization" not in f.request.headers
+ fm.request(f)
assert f.request.headers["authorization"] == "foo"
def test_stream(self):
@@ -1024,15 +1017,15 @@ class TestFlowMaster:
f = tutils.tflow(resp=True)
fm.start_stream(file(p, "ab"), None)
- fm.handle_request(f)
- fm.handle_response(f)
+ fm.request(f)
+ fm.response(f)
fm.stop_stream()
assert r()[0].response
f = tutils.tflow()
fm.start_stream(file(p, "ab"), None)
- fm.handle_request(f)
+ fm.request(f)
fm.shutdown()
assert not r()[1].response
@@ -1077,8 +1070,8 @@ class TestRequest:
r.headers["if-modified-since"] = "test"
r.headers["if-none-match"] = "test"
r.anticache()
- assert not "if-modified-since" in r.headers
- assert not "if-none-match" in r.headers
+ assert "if-modified-since" not in r.headers
+ assert "if-none-match" not in r.headers
def test_replace(self):
r = HTTPRequest.wrap(netlib.tutils.treq())
@@ -1087,7 +1080,7 @@ class TestRequest:
r.content = "afoob"
assert r.replace("foo(?i)", "boo") == 4
assert r.path == "path/boo"
- assert not "foo" in r.content
+ assert "foo" not in r.content
assert r.headers["boo"] == "boo"
def test_constrain_encoding(self):
@@ -1129,7 +1122,7 @@ class TestResponse:
r.headers["Foo"] = "fOo"
r.content = "afoob"
assert r.replace("foo(?i)", "boo") == 3
- assert not "foo" in r.content
+ assert "foo" not in r.content
assert r.headers["boo"] == "boo"
def test_get_content_type(self):
@@ -1161,11 +1154,9 @@ class TestError:
class TestClientConnection:
-
def test_state(self):
-
c = tutils.tclient_conn()
- assert ClientConnection.from_state(c.get_state()).get_state() ==\
+ assert ClientConnection.from_state(c.get_state()).get_state() == \
c.get_state()
c2 = tutils.tclient_conn()
diff --git a/test/mitmproxy/test_flow_export.py b/test/mitmproxy/test_flow_export.py
index c252c5bd..9a263b1b 100644
--- a/test/mitmproxy/test_flow_export.py
+++ b/test/mitmproxy/test_flow_export.py
@@ -1,10 +1,9 @@
-import json
from textwrap import dedent
import re
import netlib.tutils
from netlib.http import Headers
-from mitmproxy import flow_export
+from mitmproxy.flow import export # heh
from . import tutils
@@ -21,49 +20,54 @@ def python_equals(testdata, text):
assert clean_blanks(text).rstrip() == clean_blanks(d).rstrip()
-req_get = lambda: netlib.tutils.treq(method='GET', content='', path=b"/path?a=foo&a=bar&b=baz")
+def req_get():
+ return netlib.tutils.treq(method='GET', content='', path=b"/path?a=foo&a=bar&b=baz")
-req_post = lambda: netlib.tutils.treq(method='POST', headers=None)
-req_patch = lambda: netlib.tutils.treq(method='PATCH', path=b"/path?query=param")
+def req_post():
+ return netlib.tutils.treq(method='POST', headers=())
+
+
+def req_patch():
+ return netlib.tutils.treq(method='PATCH', path=b"/path?query=param")
class TestExportCurlCommand():
def test_get(self):
flow = tutils.tflow(req=req_get())
result = """curl -H 'header:qvalue' -H 'content-length:7' 'http://address/path?a=foo&a=bar&b=baz'"""
- assert flow_export.curl_command(flow) == result
+ assert export.curl_command(flow) == result
def test_post(self):
flow = tutils.tflow(req=req_post())
result = """curl -X POST 'http://address/path' --data-binary 'content'"""
- assert flow_export.curl_command(flow) == result
+ assert export.curl_command(flow) == result
def test_patch(self):
flow = tutils.tflow(req=req_patch())
result = """curl -H 'header:qvalue' -H 'content-length:7' -X PATCH 'http://address/path?query=param' --data-binary 'content'"""
- assert flow_export.curl_command(flow) == result
+ assert export.curl_command(flow) == result
class TestExportPythonCode():
def test_get(self):
flow = tutils.tflow(req=req_get())
- python_equals("test_flow_export/python_get.py", flow_export.python_code(flow))
+ python_equals("data/test_flow_export/python_get.py", export.python_code(flow))
def test_post(self):
flow = tutils.tflow(req=req_post())
- python_equals("test_flow_export/python_post.py", flow_export.python_code(flow))
+ python_equals("data/test_flow_export/python_post.py", export.python_code(flow))
def test_post_json(self):
p = req_post()
p.content = '{"name": "example", "email": "example@example.com"}'
p.headers = Headers(content_type="application/json")
flow = tutils.tflow(req=p)
- python_equals("test_flow_export/python_post_json.py", flow_export.python_code(flow))
+ python_equals("data/test_flow_export/python_post_json.py", export.python_code(flow))
def test_patch(self):
flow = tutils.tflow(req=req_patch())
- python_equals("test_flow_export/python_patch.py", flow_export.python_code(flow))
+ python_equals("data/test_flow_export/python_patch.py", export.python_code(flow))
class TestRawRequest():
@@ -76,7 +80,7 @@ class TestRawRequest():
host: address:22\r
\r
""").strip(" ").lstrip()
- assert flow_export.raw_request(flow) == result
+ assert export.raw_request(flow) == result
def test_post(self):
flow = tutils.tflow(req=req_post())
@@ -86,7 +90,7 @@ class TestRawRequest():
\r
content
""").strip()
- assert flow_export.raw_request(flow) == result
+ assert export.raw_request(flow) == result
def test_patch(self):
flow = tutils.tflow(req=req_patch())
@@ -98,54 +102,54 @@ class TestRawRequest():
\r
content
""").strip()
- assert flow_export.raw_request(flow) == result
+ assert export.raw_request(flow) == result
class TestExportLocustCode():
def test_get(self):
flow = tutils.tflow(req=req_get())
- python_equals("test_flow_export/locust_get.py", flow_export.locust_code(flow))
+ python_equals("data/test_flow_export/locust_get.py", export.locust_code(flow))
def test_post(self):
p = req_post()
p.content = '''content'''
p.headers = ''
flow = tutils.tflow(req=p)
- python_equals("test_flow_export/locust_post.py", flow_export.locust_code(flow))
+ python_equals("data/test_flow_export/locust_post.py", export.locust_code(flow))
def test_patch(self):
flow = tutils.tflow(req=req_patch())
- python_equals("test_flow_export/locust_patch.py", flow_export.locust_code(flow))
+ python_equals("data/test_flow_export/locust_patch.py", export.locust_code(flow))
class TestExportLocustTask():
def test_get(self):
flow = tutils.tflow(req=req_get())
- python_equals("test_flow_export/locust_task_get.py", flow_export.locust_task(flow))
+ python_equals("data/test_flow_export/locust_task_get.py", export.locust_task(flow))
def test_post(self):
flow = tutils.tflow(req=req_post())
- python_equals("test_flow_export/locust_task_post.py", flow_export.locust_task(flow))
+ python_equals("data/test_flow_export/locust_task_post.py", export.locust_task(flow))
def test_patch(self):
flow = tutils.tflow(req=req_patch())
- python_equals("test_flow_export/locust_task_patch.py", flow_export.locust_task(flow))
+ python_equals("data/test_flow_export/locust_task_patch.py", export.locust_task(flow))
class TestIsJson():
def test_empty(self):
- assert flow_export.is_json(None, None) is False
+ assert export.is_json(None, None) is False
def test_json_type(self):
headers = Headers(content_type="application/json")
- assert flow_export.is_json(headers, "foobar") is False
+ assert export.is_json(headers, "foobar") is False
def test_valid(self):
headers = Headers(content_type="application/foobar")
- j = flow_export.is_json(headers, '{"name": "example", "email": "example@example.com"}')
+ j = export.is_json(headers, '{"name": "example", "email": "example@example.com"}')
assert j is False
- def test_valid(self):
+ def test_valid2(self):
headers = Headers(content_type="application/json")
- j = flow_export.is_json(headers, '{"name": "example", "email": "example@example.com"}')
+ j = export.is_json(headers, '{"name": "example", "email": "example@example.com"}')
assert isinstance(j, dict)
diff --git a/test/mitmproxy/test_flow_format_compat.py b/test/mitmproxy/test_flow_format_compat.py
index 2c477cc2..b2cef88d 100644
--- a/test/mitmproxy/test_flow_format_compat.py
+++ b/test/mitmproxy/test_flow_format_compat.py
@@ -1,4 +1,5 @@
-from mitmproxy.flow import FlowReader, FlowReadException
+from mitmproxy.flow import FlowReader
+from mitmproxy.exceptions import FlowReadException
from . import tutils
diff --git a/test/mitmproxy/test_protocol_http2.py b/test/mitmproxy/test_protocol_http2.py
index c3950975..23072260 100644
--- a/test/mitmproxy/test_protocol_http2.py
+++ b/test/mitmproxy/test_protocol_http2.py
@@ -2,15 +2,21 @@
from __future__ import (absolute_import, print_function, division)
-import OpenSSL
import pytest
import traceback
import os
import tempfile
+import h2
from mitmproxy.proxy.config import ProxyConfig
from mitmproxy.cmdline import APP_HOST, APP_PORT
+import netlib
+from ..netlib import tservers as netlib_tservers
+from netlib.http.http2 import framereader
+
+from . import tservers
+
import logging
logging.getLogger("hyper.packages.hpack.hpack").setLevel(logging.WARNING)
logging.getLogger("requests.packages.urllib3.connectionpool").setLevel(logging.WARNING)
@@ -19,13 +25,6 @@ logging.getLogger("passlib.registry").setLevel(logging.WARNING)
logging.getLogger("PIL.Image").setLevel(logging.WARNING)
logging.getLogger("PIL.PngImagePlugin").setLevel(logging.WARNING)
-import netlib
-from ..netlib import tservers as netlib_tservers
-from netlib.utils import http2_read_raw_frame
-
-import h2
-
-from . import tservers
requires_alpn = pytest.mark.skipif(
not netlib.tcp.HAS_ALPN,
@@ -49,7 +48,7 @@ class _Http2ServerBase(netlib_tservers.ServerTestBase):
done = False
while not done:
try:
- raw = b''.join(http2_read_raw_frame(self.rfile))
+ raw = b''.join(framereader.http2_read_raw_frame(self.rfile))
events = h2_conn.receive_data(raw)
except:
break
@@ -165,12 +164,21 @@ class TestSimple(_Http2TestBase, _Http2ServerBase):
assert ('client-foo', 'client-bar-1') in event.headers
assert ('client-foo', 'client-bar-2') in event.headers
- h2_conn.send_headers(event.stream_id, [
- (':status', '200'),
- ('server-foo', 'server-bar'),
- ('föo', 'bär'),
- ('X-Stream-ID', str(event.stream_id)),
- ])
+ import warnings
+ with warnings.catch_warnings():
+ # Ignore UnicodeWarning:
+ # h2/utilities.py:64: UnicodeWarning: Unicode equal comparison
+ # failed to convert both arguments to Unicode - interpreting
+ # them as being unequal.
+ # elif header[0] in (b'cookie', u'cookie') and len(header[1]) < 20:
+
+ warnings.simplefilter("ignore")
+ h2_conn.send_headers(event.stream_id, [
+ (':status', '200'),
+ ('server-foo', 'server-bar'),
+ ('föo', 'bär'),
+ ('X-Stream-ID', str(event.stream_id)),
+ ])
h2_conn.send_data(event.stream_id, b'foobar')
h2_conn.end_stream(event.stream_id)
wfile.write(h2_conn.data_to_send())
@@ -192,7 +200,7 @@ class TestSimple(_Http2TestBase, _Http2ServerBase):
done = False
while not done:
try:
- events = h2_conn.receive_data(b''.join(http2_read_raw_frame(client.rfile)))
+ events = h2_conn.receive_data(b''.join(framereader.http2_read_raw_frame(client.rfile)))
except:
break
client.wfile.write(h2_conn.data_to_send())
@@ -262,7 +270,7 @@ class TestWithBodies(_Http2TestBase, _Http2ServerBase):
done = False
while not done:
try:
- events = h2_conn.receive_data(b''.join(http2_read_raw_frame(client.rfile)))
+ events = h2_conn.receive_data(b''.join(framereader.http2_read_raw_frame(client.rfile)))
except:
break
client.wfile.write(h2_conn.data_to_send())
@@ -354,7 +362,7 @@ class TestPushPromise(_Http2TestBase, _Http2ServerBase):
responses = 0
while not done:
try:
- raw = b''.join(http2_read_raw_frame(client.rfile))
+ raw = b''.join(framereader.http2_read_raw_frame(client.rfile))
events = h2_conn.receive_data(raw)
except:
break
@@ -404,7 +412,7 @@ class TestPushPromise(_Http2TestBase, _Http2ServerBase):
responses = 0
while not done:
try:
- events = h2_conn.receive_data(b''.join(http2_read_raw_frame(client.rfile)))
+ events = h2_conn.receive_data(b''.join(framereader.http2_read_raw_frame(client.rfile)))
except:
break
client.wfile.write(h2_conn.data_to_send())
@@ -435,6 +443,7 @@ class TestPushPromise(_Http2TestBase, _Http2ServerBase):
assert b'regular_stream' in bodies
# the other two bodies might not be transmitted before the reset
+
@requires_alpn
class TestConnectionLost(_Http2TestBase, _Http2ServerBase):
@@ -468,17 +477,17 @@ class TestConnectionLost(_Http2TestBase, _Http2ServerBase):
])
done = False
- ended_streams = 0
- pushed_streams = 0
- responses = 0
while not done:
try:
- raw = b''.join(http2_read_raw_frame(client.rfile))
- events = h2_conn.receive_data(raw)
+ raw = b''.join(framereader.http2_read_raw_frame(client.rfile))
+ h2_conn.receive_data(raw)
+ except:
+ break
+ try:
+ client.wfile.write(h2_conn.data_to_send())
+ client.wfile.flush()
except:
break
- client.wfile.write(h2_conn.data_to_send())
- client.wfile.flush()
if len(self.master.state.flows) == 1:
assert self.master.state.flows[0].response is None
diff --git a/test/mitmproxy/test_proxy.py b/test/mitmproxy/test_proxy.py
index e0897135..49c9c909 100644
--- a/test/mitmproxy/test_proxy.py
+++ b/test/mitmproxy/test_proxy.py
@@ -113,11 +113,10 @@ class TestProcessProxyOptions:
"nonexistent")
def test_certs(self):
- with tutils.tmpdir() as cadir:
- self.assert_noerr(
- "--cert",
- tutils.test_data.path("data/testkey.pem"))
- self.assert_err("does not exist", "--cert", "nonexistent")
+ self.assert_noerr(
+ "--cert",
+ tutils.test_data.path("data/testkey.pem"))
+ self.assert_err("does not exist", "--cert", "nonexistent")
def test_auth(self):
p = self.assert_noerr("--nonanonymous")
diff --git a/test/mitmproxy/test_script.py b/test/mitmproxy/test_script.py
index f321d15c..81994780 100644
--- a/test/mitmproxy/test_script.py
+++ b/test/mitmproxy/test_script.py
@@ -5,9 +5,9 @@ from . import tutils
def test_duplicate_flow():
s = flow.State()
fm = flow.FlowMaster(None, s)
- fm.load_script(tutils.test_data.path("scripts/duplicate_flow.py"))
+ fm.load_script(tutils.test_data.path("data/scripts/duplicate_flow.py"))
f = tutils.tflow()
- fm.handle_request(f)
+ fm.request(f)
assert fm.state.flow_count() == 2
assert not fm.state.view[0].request.is_replay
assert fm.state.view[1].request.is_replay
diff --git a/test/mitmproxy/test_server.py b/test/mitmproxy/test_server.py
index 0701d52b..b58c4f44 100644
--- a/test/mitmproxy/test_server.py
+++ b/test/mitmproxy/test_server.py
@@ -12,6 +12,7 @@ from netlib.http import authentication, http1
from netlib.tutils import raises
from pathod import pathoc, pathod
+from mitmproxy import controller
from mitmproxy.proxy.config import HostMatcher
from mitmproxy.exceptions import Kill
from mitmproxy.models import Error, HTTPResponse, HTTPFlow
@@ -190,8 +191,8 @@ class TcpMixin:
assert i_cert == i2_cert == n_cert
# Make sure that TCP messages are in the event log.
- assert any("305" in m for m in self.master.log)
- assert any("306" in m for m in self.master.log)
+ assert any("305" in m for m in self.master.tlog)
+ assert any("306" in m for m in self.master.tlog)
class AppMixin:
@@ -260,7 +261,7 @@ class TestHTTP(tservers.HTTPProxyTest, CommonMixin, AppMixin):
p = self.pathoc()
assert p.request(req % self.server.urlbase)
assert p.request(req % self.server2.urlbase)
- assert switched(self.proxy.log)
+ assert switched(self.proxy.tlog)
def test_blank_leading_line(self):
p = self.pathoc()
@@ -285,7 +286,7 @@ class TestHTTP(tservers.HTTPProxyTest, CommonMixin, AppMixin):
self.master.set_stream_large_bodies(None)
def test_stream_modify(self):
- self.master.load_script(tutils.test_data.path("scripts/stream_modify.py"))
+ self.master.load_script(tutils.test_data.path("data/scripts/stream_modify.py"))
d = self.pathod('200:b"foo"')
assert d.content == "bar"
self.master.unload_scripts()
@@ -499,7 +500,7 @@ class TestHttps2Http(tservers.ReverseProxyTest):
def test_sni(self):
p = self.pathoc(ssl=True, sni="example.com")
assert p.request("get:'/p/200'").status_code == 200
- assert all("Error in handle_sni" not in msg for msg in self.proxy.log)
+ assert all("Error in handle_sni" not in msg for msg in self.proxy.tlog)
def test_http(self):
p = self.pathoc(ssl=False)
@@ -510,7 +511,7 @@ class TestTransparent(tservers.TransparentProxyTest, CommonMixin, TcpMixin):
ssl = False
def test_tcp_stream_modify(self):
- self.master.load_script(tutils.test_data.path("scripts/tcp_stream_modify.py"))
+ self.master.load_script(tutils.test_data.path("data/scripts/tcp_stream_modify.py"))
self._tcpproxy_on()
d = self.pathod('200:b"foo"')
@@ -623,7 +624,8 @@ class TestProxySSL(tservers.HTTPProxyTest):
class MasterRedirectRequest(tservers.TestMaster):
redirect_port = None # Set by TestRedirectRequest
- def handle_request(self, f):
+ @controller.handler
+ def request(self, f):
if f.request.path == "/p/201":
# This part should have no impact, but it should also not cause any exceptions.
@@ -634,12 +636,13 @@ class MasterRedirectRequest(tservers.TestMaster):
# This is the actual redirection.
f.request.port = self.redirect_port
- super(MasterRedirectRequest, self).handle_request(f)
+ super(MasterRedirectRequest, self).request(f)
- def handle_response(self, f):
+ @controller.handler
+ def response(self, f):
f.response.content = str(f.client_conn.address.port)
f.response.headers["server-conn-id"] = str(f.server_conn.source_address.port)
- super(MasterRedirectRequest, self).handle_response(f)
+ super(MasterRedirectRequest, self).response(f)
class TestRedirectRequest(tservers.HTTPProxyTest):
@@ -689,10 +692,9 @@ class MasterStreamRequest(tservers.TestMaster):
"""
Enables the stream flag on the flow for all requests
"""
-
- def handle_responseheaders(self, f):
+ @controller.handler
+ def responseheaders(self, f):
f.response.stream = True
- f.reply()
class TestStreamRequest(tservers.HTTPProxyTest):
@@ -739,8 +741,8 @@ class TestStreamRequest(tservers.HTTPProxyTest):
class MasterFakeResponse(tservers.TestMaster):
-
- def handle_request(self, f):
+ @controller.handler
+ def request(self, f):
resp = HTTPResponse.wrap(netlib.tutils.tresp())
f.reply(resp)
@@ -761,13 +763,14 @@ class TestServerConnect(tservers.HTTPProxyTest):
def test_unnecessary_serverconnect(self):
"""A replayed/fake response with no_upstream_cert should not connect to an upstream server"""
assert self.pathod("200").status_code == 200
- for msg in self.proxy.tmaster.log:
+ for msg in self.proxy.tmaster.tlog:
assert "serverconnect" not in msg
class MasterKillRequest(tservers.TestMaster):
- def handle_request(self, f):
+ @controller.handler
+ def request(self, f):
f.reply(Kill)
@@ -783,7 +786,8 @@ class TestKillRequest(tservers.HTTPProxyTest):
class MasterKillResponse(tservers.TestMaster):
- def handle_response(self, f):
+ @controller.handler
+ def response(self, f):
f.reply(Kill)
@@ -812,7 +816,8 @@ class TestTransparentResolveError(tservers.TransparentProxyTest):
class MasterIncomplete(tservers.TestMaster):
- def handle_request(self, f):
+ @controller.handler
+ def request(self, f):
resp = HTTPResponse.wrap(netlib.tutils.tresp())
resp.content = None
f.reply(resp)
@@ -930,7 +935,9 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxyTest):
k = [0] # variable scope workaround: put into array
_func = getattr(master, attr)
- def handler(f):
+ @controller.handler
+ def handler(*args):
+ f = args[-1]
k[0] += 1
if not (k[0] in exclude):
f.client_conn.finish()
@@ -940,13 +947,16 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxyTest):
setattr(master, attr, handler)
- kill_requests(self.chain[1].tmaster, "handle_request",
- exclude=[
- # fail first request
- 2, # allow second request
- ])
+ kill_requests(
+ self.chain[1].tmaster,
+ "request",
+ exclude = [
+ # fail first request
+ 2, # allow second request
+ ]
+ )
- kill_requests(self.chain[0].tmaster, "handle_request",
+ kill_requests(self.chain[0].tmaster, "request",
exclude=[
1, # CONNECT
# fail first request
@@ -1004,10 +1014,10 @@ class AddUpstreamCertsToClientChainMixin:
ssl = True
servercert = tutils.test_data.path("data/trusted-server.crt")
ssloptions = pathod.SSLOptions(
- cn="trusted-cert",
- certs=[
- ("trusted-cert", servercert)
- ]
+ cn="trusted-cert",
+ certs=[
+ ("trusted-cert", servercert)
+ ]
)
def test_add_upstream_certs_to_client_chain(self):
diff --git a/test/mitmproxy/test_utils.py b/test/mitmproxy/test_utils.py
index db7dec4a..c01b5f2a 100644
--- a/test/mitmproxy/test_utils.py
+++ b/test/mitmproxy/test_utils.py
@@ -13,25 +13,6 @@ def test_format_timestamp_with_milli():
assert utils.format_timestamp_with_milli(utils.timestamp())
-def test_isBin():
- assert not utils.isBin("testing\n\r")
- assert utils.isBin("testing\x01")
- assert utils.isBin("testing\x0e")
- assert utils.isBin("testing\x7f")
-
-
-def test_isXml():
- assert not utils.isXML("foo")
- assert utils.isXML("<foo")
- assert utils.isXML(" \n<foo")
-
-
-def test_clean_hanging_newline():
- s = "foo\n"
- assert utils.clean_hanging_newline(s) == "foo"
- assert utils.clean_hanging_newline("foo") == "foo"
-
-
def test_pkg_data():
assert utils.pkg_data.path("console")
tutils.raises("does not exist", utils.pkg_data.path, "nonexistent")
@@ -43,21 +24,6 @@ def test_pretty_json():
assert not utils.pretty_json("moo")
-def test_pretty_duration():
- assert utils.pretty_duration(0.00001) == "0ms"
- assert utils.pretty_duration(0.0001) == "0ms"
- assert utils.pretty_duration(0.001) == "1ms"
- assert utils.pretty_duration(0.01) == "10ms"
- assert utils.pretty_duration(0.1) == "100ms"
- assert utils.pretty_duration(1) == "1.00s"
- assert utils.pretty_duration(10) == "10.0s"
- assert utils.pretty_duration(100) == "100s"
- assert utils.pretty_duration(1000) == "1000s"
- assert utils.pretty_duration(10000) == "10000s"
- assert utils.pretty_duration(1.123) == "1.12s"
- assert utils.pretty_duration(0.123) == "123ms"
-
-
def test_LRUCache():
cache = utils.LRUCache(2)
@@ -89,13 +55,3 @@ def test_LRUCache():
assert len(cache.cacheList) == 2
assert len(cache.cache) == 2
-
-
-def test_parse_size():
- assert not utils.parse_size("")
- assert utils.parse_size("1") == 1
- assert utils.parse_size("1k") == 1024
- assert utils.parse_size("1m") == 1024**2
- assert utils.parse_size("1g") == 1024**3
- tutils.raises(ValueError, utils.parse_size, "1f")
- tutils.raises(ValueError, utils.parse_size, "ak")
diff --git a/test/mitmproxy/tools/memoryleak.py b/test/mitmproxy/tools/memoryleak.py
index 259309a6..c03230da 100644
--- a/test/mitmproxy/tools/memoryleak.py
+++ b/test/mitmproxy/tools/memoryleak.py
@@ -3,8 +3,8 @@ import threading
from pympler import muppy, refbrowser
from OpenSSL import SSL
# import os
-#os.environ["TK_LIBRARY"] = r"C:\Python27\tcl\tcl8.5"
-#os.environ["TCL_LIBRARY"] = r"C:\Python27\tcl\tcl8.5"
+# os.environ["TK_LIBRARY"] = r"C:\Python27\tcl\tcl8.5"
+# os.environ["TCL_LIBRARY"] = r"C:\Python27\tcl\tcl8.5"
# Also noteworthy: guppy, objgraph
diff --git a/test/mitmproxy/tservers.py b/test/mitmproxy/tservers.py
index c9d68cfd..24ebb476 100644
--- a/test/mitmproxy/tservers.py
+++ b/test/mitmproxy/tservers.py
@@ -39,19 +39,11 @@ class TestMaster(flow.FlowMaster):
self.apps.add(errapp, "errapp", 80)
self.clear_log()
- def handle_request(self, f):
- flow.FlowMaster.handle_request(self, f)
- f.reply()
-
- def handle_response(self, f):
- flow.FlowMaster.handle_response(self, f)
- f.reply()
-
def clear_log(self):
- self.log = []
+ self.tlog = []
def add_event(self, message, level=None):
- self.log.append(message)
+ self.tlog.append(message)
class ProxyThread(threading.Thread):
@@ -68,8 +60,8 @@ class ProxyThread(threading.Thread):
return self.tmaster.server.address.port
@property
- def log(self):
- return self.tmaster.log
+ def tlog(self):
+ return self.tmaster.tlog
def run(self):
self.tmaster.run()
diff --git a/test/mitmproxy/tutils.py b/test/mitmproxy/tutils.py
index 118f849c..d0a09035 100644
--- a/test/mitmproxy/tutils.py
+++ b/test/mitmproxy/tutils.py
@@ -12,7 +12,7 @@ from unittest.case import SkipTest
import netlib.utils
import netlib.tutils
-from mitmproxy import utils, controller
+from mitmproxy import controller
from mitmproxy.models import (
ClientConnection, ServerConnection, Error, HTTPRequest, HTTPResponse, HTTPFlow, TCPFlow
)
@@ -156,6 +156,7 @@ def chdir(dir):
yield
os.chdir(orig_dir)
+
@contextmanager
def tmpdir(*args, **kwargs):
temp_workdir = tempfile.mkdtemp(*args, **kwargs)
diff --git a/test/netlib/data/verificationcerts/generate.py b/test/netlib/data/verificationcerts/generate.py
index 9203abbb..6d4d8550 100644
--- a/test/netlib/data/verificationcerts/generate.py
+++ b/test/netlib/data/verificationcerts/generate.py
@@ -64,5 +64,3 @@ do("openssl req -x509 -new -nodes -batch "
"-days 1024 "
"-out self-signed.crt".format(SUBJECT)
)
-
-
diff --git a/test/netlib/http/http1/test_assemble.py b/test/netlib/http/http1/test_assemble.py
index 8dcbae8e..50d29384 100644
--- a/test/netlib/http/http1/test_assemble.py
+++ b/test/netlib/http/http1/test_assemble.py
@@ -10,11 +10,11 @@ from netlib.tutils import treq, raises, tresp
def test_assemble_request():
- c = assemble_request(treq()) == (
+ assert assemble_request(treq()) == (
b"GET /path HTTP/1.1\r\n"
b"header: qvalue\r\n"
- b"Host: address:22\r\n"
- b"Content-Length: 7\r\n"
+ b"content-length: 7\r\n"
+ b"host: address:22\r\n"
b"\r\n"
b"content"
)
@@ -32,10 +32,10 @@ def test_assemble_request_head():
def test_assemble_response():
- c = assemble_response(tresp()) == (
+ assert assemble_response(tresp()) == (
b"HTTP/1.1 200 OK\r\n"
b"header-response: svalue\r\n"
- b"Content-Length: 7\r\n"
+ b"content-length: 7\r\n"
b"\r\n"
b"message"
)
diff --git a/test/netlib/http/http1/test_read.py b/test/netlib/http/http1/test_read.py
index d8106904..5285ac1d 100644
--- a/test/netlib/http/http1/test_read.py
+++ b/test/netlib/http/http1/test_read.py
@@ -1,6 +1,5 @@
from __future__ import absolute_import, print_function, division
from io import BytesIO
-import textwrap
from mock import Mock
from netlib.exceptions import HttpException, HttpSyntaxException, HttpReadDisconnect, TcpDisconnect
from netlib.http import Headers
@@ -8,11 +7,22 @@ from netlib.http.http1.read import (
read_request, read_response, read_request_head,
read_response_head, read_body, connection_close, expected_http_body_size, _get_first_line,
_read_request_line, _parse_authority_form, _read_response_line, _check_http_version,
- _read_headers, _read_chunked
+ _read_headers, _read_chunked, get_header_tokens
)
from netlib.tutils import treq, tresp, raises
+def test_get_header_tokens():
+ headers = Headers()
+ assert get_header_tokens(headers, "foo") == []
+ headers["foo"] = "bar"
+ assert get_header_tokens(headers, "foo") == ["bar"]
+ headers["foo"] = "bar, voing"
+ assert get_header_tokens(headers, "foo") == ["bar", "voing"]
+ headers.set_all("foo", ["bar, voing", "oink"])
+ assert get_header_tokens(headers, "foo") == ["bar", "voing", "oink"]
+
+
def test_read_request():
rfile = BytesIO(b"GET / HTTP/1.1\r\n\r\nskip")
r = read_request(rfile)
@@ -106,6 +116,7 @@ class TestReadBody(object):
rfile = BytesIO(b"123456")
assert list(read_body(rfile, -1, max_chunk_size=1)) == [b"1", b"2", b"3", b"4", b"5", b"6"]
+
def test_connection_close():
headers = Headers()
assert connection_close(b"HTTP/1.0", headers)
@@ -121,6 +132,7 @@ def test_connection_close():
assert connection_close(b"HTTP/1.0", headers)
assert not connection_close(b"HTTP/1.1", headers)
+
def test_expected_http_body_size():
# Expect: 100-continue
assert expected_http_body_size(
@@ -203,6 +215,7 @@ def test_read_request_line():
with raises(HttpReadDisconnect):
t(b"")
+
def test_parse_authority_form():
assert _parse_authority_form(b"foo:42") == (b"foo", 42)
with raises(HttpSyntaxException):
@@ -302,6 +315,7 @@ class TestReadHeaders(object):
headers = self._read(data)
assert headers.fields == ((b"bar", b""),)
+
def test_read_chunked():
req = treq(content=None)
req.headers["Transfer-Encoding"] = "chunked"
diff --git a/test/netlib/http/http2/test_connections.py b/test/netlib/http/http2/test_connections.py
index 7d240c0e..27cc30ba 100644
--- a/test/netlib/http/http2/test_connections.py
+++ b/test/netlib/http/http2/test_connections.py
@@ -1,16 +1,16 @@
-import OpenSSL
import mock
import codecs
-from hyperframe.frame import *
-
-from netlib import tcp, http, utils
+import hyperframe
+from netlib import tcp, http
from netlib.tutils import raises
from netlib.exceptions import TcpDisconnect
from netlib.http.http2.connections import HTTP2Protocol, TCPHandler
+from netlib.http.http2 import framereader
from ... import tservers
+
class TestTCPHandlerWrapper:
def test_wrapped(self):
h = TCPHandler(rfile='foo', wfile='bar')
@@ -111,11 +111,11 @@ class TestPerformServerConnectionPreface(tservers.ServerTestBase):
self.wfile.flush()
# check empty settings frame
- raw = utils.http2_read_raw_frame(self.rfile)
+ raw = framereader.http2_read_raw_frame(self.rfile)
assert raw == codecs.decode('00000c040000000000000200000000000300000001', 'hex_codec')
# check settings acknowledgement
- raw = utils.http2_read_raw_frame(self.rfile)
+ raw = framereader.http2_read_raw_frame(self.rfile)
assert raw == codecs.decode('000000040100000000', 'hex_codec')
# send settings acknowledgement
@@ -214,19 +214,19 @@ class TestApplySettings(tservers.ServerTestBase):
protocol = HTTP2Protocol(c)
protocol._apply_settings({
- SettingsFrame.ENABLE_PUSH: 'foo',
- SettingsFrame.MAX_CONCURRENT_STREAMS: 'bar',
- SettingsFrame.INITIAL_WINDOW_SIZE: 'deadbeef',
+ hyperframe.frame.SettingsFrame.ENABLE_PUSH: 'foo',
+ hyperframe.frame.SettingsFrame.MAX_CONCURRENT_STREAMS: 'bar',
+ hyperframe.frame.SettingsFrame.INITIAL_WINDOW_SIZE: 'deadbeef',
})
assert c.rfile.safe_read(2) == b"OK"
assert protocol.http2_settings[
- SettingsFrame.ENABLE_PUSH] == 'foo'
+ hyperframe.frame.SettingsFrame.ENABLE_PUSH] == 'foo'
assert protocol.http2_settings[
- SettingsFrame.MAX_CONCURRENT_STREAMS] == 'bar'
+ hyperframe.frame.SettingsFrame.MAX_CONCURRENT_STREAMS] == 'bar'
assert protocol.http2_settings[
- SettingsFrame.INITIAL_WINDOW_SIZE] == 'deadbeef'
+ hyperframe.frame.SettingsFrame.INITIAL_WINDOW_SIZE] == 'deadbeef'
class TestCreateHeaders(object):
@@ -258,7 +258,7 @@ class TestCreateHeaders(object):
(b'server', b'version')])
protocol = HTTP2Protocol(self.c)
- protocol.http2_settings[SettingsFrame.MAX_FRAME_SIZE] = 8
+ protocol.http2_settings[hyperframe.frame.SettingsFrame.MAX_FRAME_SIZE] = 8
bytes = protocol._create_headers(headers, 1, end_stream=True)
assert len(bytes) == 3
assert bytes[0] == codecs.decode('000008010100000001828487408294e783', 'hex_codec')
@@ -281,7 +281,7 @@ class TestCreateBody(object):
def test_create_body_multiple_frames(self):
protocol = HTTP2Protocol(self.c)
- protocol.http2_settings[SettingsFrame.MAX_FRAME_SIZE] = 5
+ protocol.http2_settings[hyperframe.frame.SettingsFrame.MAX_FRAME_SIZE] = 5
bytes = protocol._create_body(b'foobarmehm42', 1)
assert len(bytes) == 3
assert bytes[0] == codecs.decode('000005000000000001666f6f6261', 'hex_codec')
@@ -312,7 +312,10 @@ class TestReadRequest(tservers.ServerTestBase):
req = protocol.read_request(NotImplemented)
assert req.stream_id
- assert req.headers.fields == ((b':method', b'GET'), (b':path', b'/'), (b':scheme', b'https'))
+ assert req.headers.fields == ()
+ assert req.method == "GET"
+ assert req.path == "/"
+ assert req.scheme == "https"
assert req.content == b'foobar'
@@ -461,7 +464,7 @@ class TestAssembleRequest(object):
b'',
b'/',
b"HTTP/2.0",
- None,
+ (),
None,
))
assert len(bytes) == 1
@@ -476,7 +479,7 @@ class TestAssembleRequest(object):
b'',
b'/',
b"HTTP/2.0",
- None,
+ (),
None,
)
req.stream_id = 0x42
diff --git a/test/netlib/http/test_authentication.py b/test/netlib/http/test_authentication.py
index 1df7cd9c..95d72447 100644
--- a/test/netlib/http/test_authentication.py
+++ b/test/netlib/http/test_authentication.py
@@ -78,7 +78,7 @@ class TestBasicProxyAuth:
assert ba.authenticate(headers)
ba.clean(headers)
- assert not ba.AUTH_HEADER in headers
+ assert ba.AUTH_HEADER not in headers
headers[ba.AUTH_HEADER] = ""
assert not ba.authenticate(headers)
diff --git a/test/netlib/http/test_cookies.py b/test/netlib/http/test_cookies.py
index 6f84c4ce..83b85656 100644
--- a/test/netlib/http/test_cookies.py
+++ b/test/netlib/http/test_cookies.py
@@ -184,7 +184,7 @@ def test_parse_set_cookie_pairs():
assert ret == lst
s2 = cookies._format_set_cookie_pairs(ret)
ret2 = cookies._parse_set_cookie_pairs(s2)
- assert ret2 == lst
+ assert ret2 == lst
def test_parse_set_cookie_header():
diff --git a/test/netlib/http/test_headers.py b/test/netlib/http/test_headers.py
index cd2ca9d1..51819b86 100644
--- a/test/netlib/http/test_headers.py
+++ b/test/netlib/http/test_headers.py
@@ -1,4 +1,4 @@
-from netlib.http import Headers
+from netlib.http import Headers, parse_content_type
from netlib.tutils import raises
@@ -72,3 +72,12 @@ class TestHeaders(object):
replacements = headers.replace(r"Host: ", "X-Host ")
assert replacements == 0
assert headers["Host"] == "example.com"
+
+
+def test_parse_content_type():
+ p = parse_content_type
+ assert p("text/html") == ("text", "html", {})
+ assert p("text") is None
+
+ v = p("text/html; charset=UTF-8")
+ assert v == ('text', 'html', {'charset': 'UTF-8'})
diff --git a/test/netlib/http/test_message.py b/test/netlib/http/test_message.py
index 64592921..f5bf7f0c 100644
--- a/test/netlib/http/test_message.py
+++ b/test/netlib/http/test_message.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, division
-from netlib.http import decoded, Headers
-from netlib.tutils import tresp, raises
+from netlib.http import decoded
+from netlib.tutils import tresp
def _test_passthrough_attr(message, attr):
diff --git a/test/netlib/http/test_multipart.py b/test/netlib/http/test_multipart.py
new file mode 100644
index 00000000..1d7e0062
--- /dev/null
+++ b/test/netlib/http/test_multipart.py
@@ -0,0 +1,24 @@
+from netlib.http import Headers
+from netlib.http import multipart
+
+
+def test_decode():
+ boundary = 'somefancyboundary'
+ headers = Headers(
+ content_type='multipart/form-data; boundary=' + boundary
+ )
+ content = (
+ "--{0}\n"
+ "Content-Disposition: form-data; name=\"field1\"\n\n"
+ "value1\n"
+ "--{0}\n"
+ "Content-Disposition: form-data; name=\"field2\"\n\n"
+ "value2\n"
+ "--{0}--".format(boundary).encode()
+ )
+
+ form = multipart.decode(headers, content)
+
+ assert len(form) == 2
+ assert form[0] == (b"field1", b"value1")
+ assert form[1] == (b"field2", b"value2")
diff --git a/test/netlib/http/test_request.py b/test/netlib/http/test_request.py
index fae7aefe..c03db339 100644
--- a/test/netlib/http/test_request.py
+++ b/test/netlib/http/test_request.py
@@ -13,7 +13,7 @@ class TestRequestData(object):
with raises(ValueError):
treq(headers="foobar")
- assert isinstance(treq(headers=None).headers, Headers)
+ assert isinstance(treq(headers=()).headers, Headers)
class TestRequestCore(object):
diff --git a/test/netlib/http/test_response.py b/test/netlib/http/test_response.py
index cfd093d4..b3c2f736 100644
--- a/test/netlib/http/test_response.py
+++ b/test/netlib/http/test_response.py
@@ -2,12 +2,10 @@ from __future__ import absolute_import, print_function, division
import email
-import six
import time
from netlib.http import Headers
from netlib.http.cookies import CookieAttrs
-from netlib.odict import ODict, ODictCaseless
from netlib.tutils import raises, tresp
from .test_message import _test_passthrough_attr, _test_decoded_attr
@@ -17,7 +15,7 @@ class TestResponseData(object):
with raises(ValueError):
tresp(headers="foobar")
- assert isinstance(tresp(headers=None).headers, Headers)
+ assert isinstance(tresp(headers=()).headers, Headers)
class TestResponseCore(object):
@@ -26,7 +24,7 @@ class TestResponseCore(object):
"""
def test_repr(self):
response = tresp()
- assert repr(response) == "Response(200 OK, unknown content type, 7B)"
+ assert repr(response) == "Response(200 OK, unknown content type, 7b)"
response.content = None
assert repr(response) == "Response(200 OK, no content)"
@@ -61,7 +59,8 @@ class TestResponseUtils(object):
def test_get_cookies_with_parameters(self):
resp = tresp()
- resp.headers = Headers(set_cookie="cookiename=cookievalue;domain=example.com;expires=Wed Oct 21 16:29:41 2015;path=/; HttpOnly")
+ cookie = "cookiename=cookievalue;domain=example.com;expires=Wed Oct 21 16:29:41 2015;path=/; HttpOnly"
+ resp.headers = Headers(set_cookie=cookie)
result = resp.cookies
assert len(result) == 1
assert "cookiename" in result
diff --git a/test/netlib/http/test_url.py b/test/netlib/http/test_url.py
new file mode 100644
index 00000000..26b37230
--- /dev/null
+++ b/test/netlib/http/test_url.py
@@ -0,0 +1,66 @@
+from netlib import tutils
+from netlib.http import url
+
+
+def test_parse():
+ with tutils.raises(ValueError):
+ url.parse("")
+
+ s, h, po, pa = url.parse(b"http://foo.com:8888/test")
+ assert s == b"http"
+ assert h == b"foo.com"
+ assert po == 8888
+ assert pa == b"/test"
+
+ s, h, po, pa = url.parse("http://foo/bar")
+ assert s == b"http"
+ assert h == b"foo"
+ assert po == 80
+ assert pa == b"/bar"
+
+ s, h, po, pa = url.parse(b"http://user:pass@foo/bar")
+ assert s == b"http"
+ assert h == b"foo"
+ assert po == 80
+ assert pa == b"/bar"
+
+ s, h, po, pa = url.parse(b"http://foo")
+ assert pa == b"/"
+
+ s, h, po, pa = url.parse(b"https://foo")
+ assert po == 443
+
+ with tutils.raises(ValueError):
+ url.parse(b"https://foo:bar")
+
+ # Invalid IDNA
+ with tutils.raises(ValueError):
+ url.parse("http://\xfafoo")
+ # Invalid PATH
+ with tutils.raises(ValueError):
+ url.parse("http:/\xc6/localhost:56121")
+ # Null byte in host
+ with tutils.raises(ValueError):
+ url.parse("http://foo\0")
+ # Port out of range
+ _, _, port, _ = url.parse("http://foo:999999")
+ assert port == 80
+ # Invalid IPv6 URL - see http://www.ietf.org/rfc/rfc2732.txt
+ with tutils.raises(ValueError):
+ url.parse('http://lo[calhost')
+
+
+def test_unparse():
+ assert url.unparse("http", "foo.com", 99, "") == "http://foo.com:99"
+ assert url.unparse("http", "foo.com", 80, "/bar") == "http://foo.com/bar"
+ assert url.unparse("https", "foo.com", 80, "") == "https://foo.com:80"
+ assert url.unparse("https", "foo.com", 443, "") == "https://foo.com"
+
+
+def test_urlencode():
+ assert url.encode([('foo', 'bar')])
+
+
+def test_urldecode():
+ s = "one=two&three=four"
+ assert len(url.decode(s)) == 2
diff --git a/test/netlib/test_basetypes.py b/test/netlib/test_basetypes.py
new file mode 100644
index 00000000..aa415784
--- /dev/null
+++ b/test/netlib/test_basetypes.py
@@ -0,0 +1,28 @@
+from netlib import basetypes
+
+
+class SerializableDummy(basetypes.Serializable):
+ def __init__(self, i):
+ self.i = i
+
+ def get_state(self):
+ return self.i
+
+ def set_state(self, i):
+ self.i = i
+
+ def from_state(self, state):
+ return type(self)(state)
+
+
+class TestSerializable:
+
+ def test_copy(self):
+ a = SerializableDummy(42)
+ assert a.i == 42
+ b = a.copy()
+ assert b.i == 42
+
+ a.set_state(1)
+ assert a.i == 1
+ assert b.i == 42
diff --git a/test/netlib/test_human.py b/test/netlib/test_human.py
new file mode 100644
index 00000000..2a5c2a85
--- /dev/null
+++ b/test/netlib/test_human.py
@@ -0,0 +1,36 @@
+from netlib import human, tutils
+
+
+def test_parse_size():
+ assert human.parse_size("0") == 0
+ assert human.parse_size("0b") == 0
+ assert human.parse_size("1") == 1
+ assert human.parse_size("1k") == 1024
+ assert human.parse_size("1m") == 1024**2
+ assert human.parse_size("1g") == 1024**3
+ tutils.raises(ValueError, human.parse_size, "1f")
+ tutils.raises(ValueError, human.parse_size, "ak")
+
+
+def test_pretty_size():
+ assert human.pretty_size(0) == "0b"
+ assert human.pretty_size(100) == "100b"
+ assert human.pretty_size(1024) == "1k"
+ assert human.pretty_size(1024 + (1024 / 2.0)) == "1.5k"
+ assert human.pretty_size(1024 * 1024) == "1m"
+ assert human.pretty_size(10 * 1024 * 1024) == "10m"
+
+
+def test_pretty_duration():
+ assert human.pretty_duration(0.00001) == "0ms"
+ assert human.pretty_duration(0.0001) == "0ms"
+ assert human.pretty_duration(0.001) == "1ms"
+ assert human.pretty_duration(0.01) == "10ms"
+ assert human.pretty_duration(0.1) == "100ms"
+ assert human.pretty_duration(1) == "1.00s"
+ assert human.pretty_duration(10) == "10.0s"
+ assert human.pretty_duration(100) == "100s"
+ assert human.pretty_duration(1000) == "1000s"
+ assert human.pretty_duration(10000) == "10000s"
+ assert human.pretty_duration(1.123) == "1.12s"
+ assert human.pretty_duration(0.123) == "123ms"
diff --git a/test/netlib/test_multidict.py b/test/netlib/test_multidict.py
index 5bb65e3f..7319f1c5 100644
--- a/test/netlib/test_multidict.py
+++ b/test/netlib/test_multidict.py
@@ -49,7 +49,7 @@ class TestMultiDict(object):
assert md["foo"] == "bar"
with tutils.raises(KeyError):
- _ = md["bar"]
+ md["bar"]
md_multi = TMultiDict(
[("foo", "a"), ("foo", "b")]
diff --git a/test/netlib/test_socks.py b/test/netlib/test_socks.py
index 486b975b..17e08054 100644
--- a/test/netlib/test_socks.py
+++ b/test/netlib/test_socks.py
@@ -1,6 +1,5 @@
import ipaddress
from io import BytesIO
-import socket
from netlib import socks, tcp, tutils
diff --git a/test/netlib/test_strutils.py b/test/netlib/test_strutils.py
new file mode 100644
index 00000000..734265c4
--- /dev/null
+++ b/test/netlib/test_strutils.py
@@ -0,0 +1,63 @@
+# coding=utf-8
+
+from netlib import strutils
+
+
+def test_clean_bin():
+ assert strutils.clean_bin(b"one") == b"one"
+ assert strutils.clean_bin(b"\00ne") == b".ne"
+ assert strutils.clean_bin(b"\nne") == b"\nne"
+ assert strutils.clean_bin(b"\nne", False) == b".ne"
+ assert strutils.clean_bin(u"\u2605".encode("utf8")) == b"..."
+
+ assert strutils.clean_bin(u"one") == u"one"
+ assert strutils.clean_bin(u"\00ne") == u".ne"
+ assert strutils.clean_bin(u"\nne") == u"\nne"
+ assert strutils.clean_bin(u"\nne", False) == u".ne"
+ assert strutils.clean_bin(u"\u2605") == u"\u2605"
+
+
+def test_safe_subn():
+ assert strutils.safe_subn("foo", u"bar", "\xc2foo")
+
+
+def test_bytes_to_escaped_str():
+ assert strutils.bytes_to_escaped_str(b"foo") == "foo"
+ assert strutils.bytes_to_escaped_str(b"\b") == r"\x08"
+ assert strutils.bytes_to_escaped_str(br"&!?=\)") == r"&!?=\\)"
+ assert strutils.bytes_to_escaped_str(b'\xc3\xbc') == r"\xc3\xbc"
+ assert strutils.bytes_to_escaped_str(b"'") == r"\'"
+ assert strutils.bytes_to_escaped_str(b'"') == r'"'
+
+
+def test_escaped_str_to_bytes():
+ assert strutils.escaped_str_to_bytes("foo") == b"foo"
+ assert strutils.escaped_str_to_bytes("\x08") == b"\b"
+ assert strutils.escaped_str_to_bytes("&!?=\\\\)") == br"&!?=\)"
+ assert strutils.escaped_str_to_bytes("ü") == b'\xc3\xbc'
+ assert strutils.escaped_str_to_bytes(u"\\x08") == b"\b"
+ assert strutils.escaped_str_to_bytes(u"&!?=\\\\)") == br"&!?=\)"
+ assert strutils.escaped_str_to_bytes(u"ü") == b'\xc3\xbc'
+
+
+def test_isBin():
+ assert not strutils.isBin("testing\n\r")
+ assert strutils.isBin("testing\x01")
+ assert strutils.isBin("testing\x0e")
+ assert strutils.isBin("testing\x7f")
+
+
+def test_isXml():
+ assert not strutils.isXML("foo")
+ assert strutils.isXML("<foo")
+ assert strutils.isXML(" \n<foo")
+
+
+def test_clean_hanging_newline():
+ s = "foo\n"
+ assert strutils.clean_hanging_newline(s) == "foo"
+ assert strutils.clean_hanging_newline("foo") == "foo"
+
+
+def test_hexdump():
+ assert list(strutils.hexdump(b"one\0" * 10))
diff --git a/test/netlib/test_tcp.py b/test/netlib/test_tcp.py
index 4b4bbb92..083360b4 100644
--- a/test/netlib/test_tcp.py
+++ b/test/netlib/test_tcp.py
@@ -8,7 +8,6 @@ import threading
import mock
from OpenSSL import SSL
-import OpenSSL
from netlib import tcp, certutils, tutils
from netlib.exceptions import InvalidCertificateException, TcpReadIncomplete, TlsException, \
@@ -16,6 +15,7 @@ from netlib.exceptions import InvalidCertificateException, TcpReadIncomplete, Tl
from . import tservers
+
class EchoHandler(tcp.BaseHandler):
sni = None
diff --git a/test/netlib/test_utils.py b/test/netlib/test_utils.py
index 1d8f7b0f..eaa66f13 100644
--- a/test/netlib/test_utils.py
+++ b/test/netlib/test_utils.py
@@ -1,6 +1,7 @@
# coding=utf-8
+
from netlib import utils, tutils
-from netlib.http import Headers
+
def test_bidi():
b = utils.BiDi(a=1, b=2)
@@ -9,179 +10,3 @@ def test_bidi():
assert b.get_name(5) is None
tutils.raises(AttributeError, getattr, b, "c")
tutils.raises(ValueError, utils.BiDi, one=1, two=1)
-
-
-def test_hexdump():
- assert list(utils.hexdump(b"one\0" * 10))
-
-
-def test_clean_bin():
- assert utils.clean_bin(b"one") == b"one"
- assert utils.clean_bin(b"\00ne") == b".ne"
- assert utils.clean_bin(b"\nne") == b"\nne"
- assert utils.clean_bin(b"\nne", False) == b".ne"
- assert utils.clean_bin(u"\u2605".encode("utf8")) == b"..."
-
- assert utils.clean_bin(u"one") == u"one"
- assert utils.clean_bin(u"\00ne") == u".ne"
- assert utils.clean_bin(u"\nne") == u"\nne"
- assert utils.clean_bin(u"\nne", False) == u".ne"
- assert utils.clean_bin(u"\u2605") == u"\u2605"
-
-
-def test_pretty_size():
- assert utils.pretty_size(100) == "100B"
- assert utils.pretty_size(1024) == "1kB"
- assert utils.pretty_size(1024 + (1024 / 2.0)) == "1.5kB"
- assert utils.pretty_size(1024 * 1024) == "1MB"
-
-
-def test_parse_url():
- with tutils.raises(ValueError):
- utils.parse_url("")
-
- s, h, po, pa = utils.parse_url(b"http://foo.com:8888/test")
- assert s == b"http"
- assert h == b"foo.com"
- assert po == 8888
- assert pa == b"/test"
-
- s, h, po, pa = utils.parse_url("http://foo/bar")
- assert s == b"http"
- assert h == b"foo"
- assert po == 80
- assert pa == b"/bar"
-
- s, h, po, pa = utils.parse_url(b"http://user:pass@foo/bar")
- assert s == b"http"
- assert h == b"foo"
- assert po == 80
- assert pa == b"/bar"
-
- s, h, po, pa = utils.parse_url(b"http://foo")
- assert pa == b"/"
-
- s, h, po, pa = utils.parse_url(b"https://foo")
- assert po == 443
-
- with tutils.raises(ValueError):
- utils.parse_url(b"https://foo:bar")
-
- # Invalid IDNA
- with tutils.raises(ValueError):
- utils.parse_url("http://\xfafoo")
- # Invalid PATH
- with tutils.raises(ValueError):
- utils.parse_url("http:/\xc6/localhost:56121")
- # Null byte in host
- with tutils.raises(ValueError):
- utils.parse_url("http://foo\0")
- # Port out of range
- _, _, port, _ = utils.parse_url("http://foo:999999")
- assert port == 80
- # Invalid IPv6 URL - see http://www.ietf.org/rfc/rfc2732.txt
- with tutils.raises(ValueError):
- utils.parse_url('http://lo[calhost')
-
-
-def test_unparse_url():
- assert utils.unparse_url("http", "foo.com", 99, "") == "http://foo.com:99"
- assert utils.unparse_url("http", "foo.com", 80, "/bar") == "http://foo.com/bar"
- assert utils.unparse_url("https", "foo.com", 80, "") == "https://foo.com:80"
- assert utils.unparse_url("https", "foo.com", 443, "") == "https://foo.com"
-
-
-def test_urlencode():
- assert utils.urlencode([('foo', 'bar')])
-
-
-def test_urldecode():
- s = "one=two&three=four"
- assert len(utils.urldecode(s)) == 2
-
-
-def test_get_header_tokens():
- headers = Headers()
- assert utils.get_header_tokens(headers, "foo") == []
- headers["foo"] = "bar"
- assert utils.get_header_tokens(headers, "foo") == ["bar"]
- headers["foo"] = "bar, voing"
- assert utils.get_header_tokens(headers, "foo") == ["bar", "voing"]
- headers.set_all("foo", ["bar, voing", "oink"])
- assert utils.get_header_tokens(headers, "foo") == ["bar", "voing", "oink"]
-
-
-def test_multipartdecode():
- boundary = 'somefancyboundary'
- headers = Headers(
- content_type='multipart/form-data; boundary=' + boundary
- )
- content = (
- "--{0}\n"
- "Content-Disposition: form-data; name=\"field1\"\n\n"
- "value1\n"
- "--{0}\n"
- "Content-Disposition: form-data; name=\"field2\"\n\n"
- "value2\n"
- "--{0}--".format(boundary).encode()
- )
-
- form = utils.multipartdecode(headers, content)
-
- assert len(form) == 2
- assert form[0] == (b"field1", b"value1")
- assert form[1] == (b"field2", b"value2")
-
-
-def test_parse_content_type():
- p = utils.parse_content_type
- assert p("text/html") == ("text", "html", {})
- assert p("text") is None
-
- v = p("text/html; charset=UTF-8")
- assert v == ('text', 'html', {'charset': 'UTF-8'})
-
-
-class SerializableDummy(utils.Serializable):
- def __init__(self, i):
- self.i = i
-
- def get_state(self):
- return self.i
-
- def set_state(self, i):
- self.i = i
-
- def from_state(self, state):
- return type(self)(state)
-
-
-class TestSerializable:
-
- def test_copy(self):
- a = SerializableDummy(42)
- assert a.i == 42
- b = a.copy()
- assert b.i == 42
-
- a.set_state(1)
- assert a.i == 1
- assert b.i == 42
-
-
-def test_safe_subn():
- assert utils.safe_subn("foo", u"bar", "\xc2foo")
-
-
-def test_bytes_to_escaped_str():
- assert utils.bytes_to_escaped_str(b"foo") == "foo"
- assert utils.bytes_to_escaped_str(b"\b") == r"\x08"
- assert utils.bytes_to_escaped_str(br"&!?=\)") == r"&!?=\\)"
- assert utils.bytes_to_escaped_str(b'\xc3\xbc') == r"\xc3\xbc"
-
-
-def test_escaped_str_to_bytes():
- assert utils.escaped_str_to_bytes("foo") == b"foo"
- assert utils.escaped_str_to_bytes(r"\x08") == b"\b"
- assert utils.escaped_str_to_bytes(r"&!?=\\)") == br"&!?=\)"
- assert utils.escaped_str_to_bytes(r"ü") == b'\xc3\xbc'
diff --git a/test/netlib/test_version_check.py b/test/netlib/test_version_check.py
index 680f80e0..fa6b19e5 100644
--- a/test/netlib/test_version_check.py
+++ b/test/netlib/test_version_check.py
@@ -1,6 +1,6 @@
from io import StringIO
import mock
-from netlib import version_check, version
+from netlib import version_check
@mock.patch("sys.exit")
diff --git a/test/netlib/test_wsgi.py b/test/netlib/test_wsgi.py
index 8c782b27..5c61f81c 100644
--- a/test/netlib/test_wsgi.py
+++ b/test/netlib/test_wsgi.py
@@ -11,7 +11,7 @@ def tflow():
class ExampleApp:
-
+
def __init__(self):
self.called = False
diff --git a/test/netlib/websockets/test_websockets.py b/test/netlib/websockets/test_websockets.py
index a7d782a4..50fa26e6 100644
--- a/test/netlib/websockets/test_websockets.py
+++ b/test/netlib/websockets/test_websockets.py
@@ -2,13 +2,16 @@ import os
from netlib.http.http1 import read_response, read_request
-from netlib import tcp, websockets, http, tutils
+from netlib import tcp
+from netlib import tutils
+from netlib import websockets
from netlib.http import status_codes
from netlib.tutils import treq
-from netlib.exceptions import *
+from netlib import exceptions
from .. import tservers
+
class WebSocketsEchoHandler(tcp.BaseHandler):
def __init__(self, connection, address, server):
@@ -117,8 +120,8 @@ class TestWebSockets(tservers.ServerTestBase):
default builder should always generate valid frames
"""
msg = self.random_bytes()
- client_frame = websockets.Frame.default(msg, from_client=True)
- server_frame = websockets.Frame.default(msg, from_client=False)
+ assert websockets.Frame.default(msg, from_client=True)
+ assert websockets.Frame.default(msg, from_client=False)
def test_serialization_bijection(self):
"""
@@ -174,7 +177,7 @@ class TestBadHandshake(tservers.ServerTestBase):
handler = BadHandshakeHandler
def test(self):
- with tutils.raises(TcpDisconnect):
+ with tutils.raises(exceptions.TcpDisconnect):
client = WebSocketsClient(("127.0.0.1", self.port))
client.connect()
client.send_message(b"hello")
diff --git a/test/pathod/test_app.py b/test/pathod/test_app.py
index ac89c44c..fbaa773c 100644
--- a/test/pathod/test_app.py
+++ b/test/pathod/test_app.py
@@ -11,11 +11,11 @@ class TestApp(tutils.DaemonTests):
def test_about(self):
r = self.getpath("/about")
- assert r.ok
+ assert r.status_code == 200
def test_download(self):
r = self.getpath("/download")
- assert r.ok
+ assert r.status_code == 200
def test_docs(self):
assert self.getpath("/docs/pathod").status_code == 200
@@ -27,7 +27,7 @@ class TestApp(tutils.DaemonTests):
def test_log(self):
assert self.getpath("/log").status_code == 200
assert self.get("200:da").status_code == 200
- id = self.d.log()[0]["id"]
+ id = self.d.expect_log(1)[0]["id"]
assert self.getpath("/log").status_code == 200
assert self.getpath("/log/%s" % id).status_code == 200
assert self.getpath("/log/9999999").status_code == 404
diff --git a/test/pathod/test_language_actions.py b/test/pathod/test_language_actions.py
index c2e15189..81d2155d 100644
--- a/test/pathod/test_language_actions.py
+++ b/test/pathod/test_language_actions.py
@@ -68,9 +68,9 @@ class TestInject:
def test_spec(self):
e = actions.InjectAt.expr()
v = e.parseString("i0,'foo'")[0]
- assert v.spec() == 'i0,"foo"'
+ assert v.spec() == "i0,'foo'"
- def test_spec(self):
+ def test_spec2(self):
e = actions.InjectAt.expr()
v = e.parseString("i0,@100")[0]
v2 = v.freeze({})
diff --git a/test/pathod/test_language_base.py b/test/pathod/test_language_base.py
index 64d4af1f..47e51bb0 100644
--- a/test/pathod/test_language_base.py
+++ b/test/pathod/test_language_base.py
@@ -41,11 +41,11 @@ class TestTokValueLiteral:
def test_espr(self):
v = base.TokValueLiteral("foo")
assert v.expr()
- assert v.val == "foo"
+ assert v.val == b"foo"
v = base.TokValueLiteral("foo\n")
assert v.expr()
- assert v.val == "foo\n"
+ assert v.val == b"foo\n"
assert repr(v)
def test_spec(self):
@@ -67,7 +67,7 @@ class TestTokValueLiteral:
def test_roundtrip(self):
self.roundtrip("'")
- self.roundtrip('\'')
+ self.roundtrip(r"\'")
self.roundtrip("a")
self.roundtrip("\"")
# self.roundtrip("\\")
@@ -171,19 +171,19 @@ class TestMisc:
def test_generators(self):
v = base.TokValue.parseString("'val'")[0]
g = v.get_generator({})
- assert g[:] == "val"
+ assert g[:] == b"val"
def test_value(self):
- assert base.TokValue.parseString("'val'")[0].val == "val"
- assert base.TokValue.parseString('"val"')[0].val == "val"
- assert base.TokValue.parseString('"\'val\'"')[0].val == "'val'"
+ assert base.TokValue.parseString("'val'")[0].val == b"val"
+ assert base.TokValue.parseString('"val"')[0].val == b"val"
+ assert base.TokValue.parseString('"\'val\'"')[0].val == b"'val'"
- def test_value(self):
+ def test_value2(self):
class TT(base.Value):
preamble = "m"
e = TT.expr()
v = e.parseString("m'msg'")[0]
- assert v.value.val == "msg"
+ assert v.value.val == b"msg"
s = v.spec()
assert s == e.parseString(s)[0].spec()
@@ -235,8 +235,8 @@ class TestKeyValue:
def test_simple(self):
e = TKeyValue.expr()
v = e.parseString("h'foo'='bar'")[0]
- assert v.key.val == "foo"
- assert v.value.val == "bar"
+ assert v.key.val == b"foo"
+ assert v.value.val == b"bar"
v2 = e.parseString(v.spec())[0]
assert v2.key.val == v.key.val
@@ -289,9 +289,9 @@ def test_options_or_value():
"three"
]
e = TT.expr()
- assert e.parseString("one")[0].value.val == "one"
- assert e.parseString("'foo'")[0].value.val == "foo"
- assert e.parseString("'get'")[0].value.val == "get"
+ assert e.parseString("one")[0].value.val == b"one"
+ assert e.parseString("'foo'")[0].value.val == b"foo"
+ assert e.parseString("'get'")[0].value.val == b"get"
assert e.parseString("one")[0].spec() == "one"
assert e.parseString("'foo'")[0].spec() == "'foo'"
diff --git a/test/pathod/test_language_generators.py b/test/pathod/test_language_generators.py
index 0fceae85..51f55991 100644
--- a/test/pathod/test_language_generators.py
+++ b/test/pathod/test_language_generators.py
@@ -7,24 +7,27 @@ import tutils
def test_randomgenerator():
g = generators.RandomGenerator("bytes", 100)
assert repr(g)
+ assert g[0]
+ assert len(g[0]) == 1
assert len(g[:10]) == 10
assert len(g[1:10]) == 9
assert len(g[:1000]) == 100
assert len(g[1000:1001]) == 0
- assert g[0]
def test_filegenerator():
with tutils.tmpdir() as t:
path = os.path.join(t, "foo")
f = open(path, "wb")
- f.write("x" * 10000)
+ f.write(b"x" * 10000)
f.close()
g = generators.FileGenerator(path)
assert len(g) == 10000
- assert g[0] == "x"
- assert g[-1] == "x"
- assert g[0:5] == "xxxxx"
+ assert g[0] == b"x"
+ assert g[-1] == b"x"
+ assert g[0:5] == b"xxxxx"
+ assert len(g[1:10]) == 9
+ assert len(g[10000:10001]) == 0
assert repr(g)
# remove all references to FileGenerator instance to close the file
# handle.
diff --git a/test/pathod/test_language_http2.py b/test/pathod/test_language_http2.py
index abfe4606..de256626 100644
--- a/test/pathod/test_language_http2.py
+++ b/test/pathod/test_language_http2.py
@@ -5,7 +5,7 @@ from netlib import tcp
from netlib.http import user_agents
from pathod import language
-from pathod.language import http2, base
+from pathod.language import http2
import tutils
@@ -141,7 +141,6 @@ class TestRequest:
assert isinstance(r.tokens[2], http2.NestedResponse)
assert r.values(default_settings())
-
def test_render_with_body(self):
s = StringIO()
r = parse_request("GET:'/foo':bfoobar")
diff --git a/test/pathod/test_log.py b/test/pathod/test_log.py
index d91b8bb1..29801eb3 100644
--- a/test/pathod/test_log.py
+++ b/test/pathod/test_log.py
@@ -1,10 +1,10 @@
-import StringIO
from pathod import log
from netlib.exceptions import TcpDisconnect
-import netlib.tcp
+import six
-class DummyIO(StringIO.StringIO):
+
+class DummyIO(six.StringIO):
def start_log(self, *args, **kwargs):
pass
diff --git a/test/pathod/test_pathoc.py b/test/pathod/test_pathoc.py
index 4e8c89c5..6e36c4bf 100644
--- a/test/pathod/test_pathoc.py
+++ b/test/pathod/test_pathoc.py
@@ -1,12 +1,12 @@
import json
from six.moves import cStringIO as StringIO
import re
-import OpenSSL
import pytest
from mock import Mock
-from netlib import tcp, http, socks
-from netlib.exceptions import HttpException, TcpException, NetlibException
+from netlib import http
+from netlib import tcp
+from netlib.exceptions import NetlibException
from netlib.http import http1, http2
from pathod import pathoc, test, version, pathod, language
@@ -147,7 +147,7 @@ class TestDaemon(_TestDaemon):
tutils.raises("ssl handshake", c.connect)
def test_showssl(self):
- assert not "certificate chain" in self.tval(
+ assert "certificate chain" not in self.tval(
["get:/p/200"],
showssl=True)
@@ -170,7 +170,7 @@ class TestDaemon(_TestDaemon):
showresp=True,
timeout=1
)
- assert not "HTTP" in self.tval(
+ assert "HTTP" not in self.tval(
["get:'/p/200:p3,100'"],
showresp=True,
timeout=1,
diff --git a/test/pathod/test_pathoc_cmdline.py b/test/pathod/test_pathoc_cmdline.py
index f527e861..35909325 100644
--- a/test/pathod/test_pathoc_cmdline.py
+++ b/test/pathod/test_pathoc_cmdline.py
@@ -29,13 +29,13 @@ def test_pathoc(perror):
assert a.connect_to == ["foo", 10]
a = cmdline.args_pathoc(["pathoc", "foo.com", "get:/", "--http2"])
- assert a.use_http2 == True
- assert a.ssl == True
+ assert a.use_http2 is True
+ assert a.ssl is True
a = cmdline.args_pathoc(["pathoc", "foo.com", "get:/", "--http2-skip-connection-preface"])
- assert a.use_http2 == True
- assert a.ssl == True
- assert a.http2_skip_connection_preface == True
+ assert a.use_http2 is True
+ assert a.ssl is True
+ assert a.http2_skip_connection_preface is True
a = cmdline.args_pathoc(["pathoc", "-c", "foo", "foo.com:8888", "get:/"])
assert perror.called
diff --git a/test/pathod/test_pathod.py b/test/pathod/test_pathod.py
index 05a3962e..ec9c169f 100644
--- a/test/pathod/test_pathod.py
+++ b/test/pathod/test_pathod.py
@@ -1,8 +1,7 @@
from six.moves import cStringIO as StringIO
-import pytest
-from pathod import pathod, version
-from netlib import tcp, http
+from pathod import pathod
+from netlib import tcp
from netlib.exceptions import HttpException, TlsException
import tutils
@@ -51,7 +50,7 @@ class TestNoApi(tutils.DaemonTests):
assert self.getpath("/log").status_code == 404
r = self.getpath("/")
assert r.status_code == 200
- assert not "Log" in r.content
+ assert "Log" not in r.content
class TestNotAfterConnect(tutils.DaemonTests):
@@ -110,7 +109,7 @@ class TestHexdump(tutils.DaemonTests):
hexdump = True
def test_hexdump(self):
- r = self.get(r"200:b'\xf0'")
+ assert self.get(r"200:b'\xf0'")
class TestNocraft(tutils.DaemonTests):
@@ -125,11 +124,10 @@ class TestNocraft(tutils.DaemonTests):
class CommonTests(tutils.DaemonTests):
def test_binarydata(self):
- r = self.get(r"200:b'\xf0'")
- l = self.d.last_log()
+ assert self.get(r"200:b'\xf0'")
+ assert self.d.last_log()
# FIXME: Other binary data elements
- @pytest.mark.skip(reason="race condition")
def test_sizelimit(self):
r = self.get("200:b@1g")
assert r.status_code == 800
@@ -140,21 +138,15 @@ class CommonTests(tutils.DaemonTests):
r, _ = self.pathoc([r"get:'/p/200':i0,'\r\n'"])
assert r[0].status_code == 200
- def test_info(self):
- assert tuple(self.d.info()["version"]) == version.IVERSION
-
- @pytest.mark.skip(reason="race condition")
def test_logs(self):
- assert self.d.clear_log()
- assert not self.d.last_log()
- rsp = self.get("202:da")
- assert len(self.d.log()) == 1
- assert self.d.clear_log()
+ self.d.clear_log()
+ assert self.get("202:da")
+ assert self.d.expect_log(1)
+ self.d.clear_log()
assert len(self.d.log()) == 0
def test_disconnect(self):
- rsp = self.get("202:b@100k:d200")
- assert len(rsp.content) < 200
+ tutils.raises("unexpected eof", self.get, "202:b@100k:d200")
def test_parserr(self):
rsp = self.get("400:msg,b:")
@@ -166,7 +158,7 @@ class CommonTests(tutils.DaemonTests):
assert rsp.content.strip() == "testfile"
def test_anchor(self):
- rsp = self.getpath("anchor/foo")
+ rsp = self.getpath("/anchor/foo")
assert rsp.status_code == 202
def test_invalid_first_line(self):
@@ -223,7 +215,6 @@ class CommonTests(tutils.DaemonTests):
)
assert r[1].payload == "test"
- @pytest.mark.skip(reason="race condition")
def test_websocket_frame_reflect_error(self):
r, _ = self.pathoc(
["ws:/p/", "wf:-mask:knone:f'wf:b@10':i13,'a'"],
@@ -233,7 +224,6 @@ class CommonTests(tutils.DaemonTests):
# FIXME: Race Condition?
assert "Parse error" in self.d.text_log()
- @pytest.mark.skip(reason="race condition")
def test_websocket_frame_disconnect_error(self):
self.pathoc(["ws:/p/", "wf:b@10:d3"], ws_read_limit=0)
assert self.d.last_log()
diff --git a/test/pathod/test_test.py b/test/pathod/test_test.py
index cee286a4..6399894e 100644
--- a/test/pathod/test_test.py
+++ b/test/pathod/test_test.py
@@ -2,6 +2,10 @@ import logging
import requests
from pathod import test
import tutils
+
+import requests.packages.urllib3
+
+requests.packages.urllib3.disable_warnings()
logging.disable(logging.CRITICAL)
diff --git a/test/pathod/test_utils.py b/test/pathod/test_utils.py
index 4dcedf6e..a46a523a 100644
--- a/test/pathod/test_utils.py
+++ b/test/pathod/test_utils.py
@@ -11,13 +11,6 @@ def test_membool():
assert m.v == 2
-def test_parse_size():
- assert utils.parse_size("100") == 100
- assert utils.parse_size("100k") == 100 * 1024
- tutils.raises("invalid size spec", utils.parse_size, "foo")
- tutils.raises("invalid size spec", utils.parse_size, "100kk")
-
-
def test_parse_anchor_spec():
assert utils.parse_anchor_spec("foo=200") == ("foo", "200")
assert utils.parse_anchor_spec("foo") is None
@@ -25,15 +18,3 @@ def test_parse_anchor_spec():
def test_data_path():
tutils.raises(ValueError, utils.data.path, "nonexistent")
-
-
-def test_inner_repr():
- assert utils.inner_repr("\x66") == "\x66"
- assert utils.inner_repr(u"foo") == "foo"
-
-
-def test_escape_unprintables():
- s = "".join([chr(i) for i in range(255)])
- e = utils.escape_unprintables(s)
- assert e.encode('ascii')
- assert not "PATHOD_MARKER" in e
diff --git a/test/pathod/tutils.py b/test/pathod/tutils.py
index f6ed3efb..b9f38d86 100644
--- a/test/pathod/tutils.py
+++ b/test/pathod/tutils.py
@@ -1,12 +1,19 @@
import tempfile
import re
import shutil
+import requests
from six.moves import cStringIO as StringIO
+import urllib
-import netlib
-from pathod import utils, test, pathoc, pathod, language
from netlib import tcp
-import requests
+from netlib import utils
+from netlib import tutils
+
+from pathod import language
+from pathod import pathoc
+from pathod import pathod
+from pathod import test
+
def treader(bytes):
"""
@@ -57,10 +64,11 @@ class DaemonTests(object):
shutil.rmtree(cls.confdir)
def teardown(self):
+ self.d.wait_for_silence()
if not (self.noweb or self.noapi):
self.d.clear_log()
- def getpath(self, path, params=None):
+ def _getpath(self, path, params=None):
scheme = "https" if self.ssl else "http"
resp = requests.get(
"%s://localhost:%s/%s" % (
@@ -73,9 +81,29 @@ class DaemonTests(object):
)
return resp
+ def getpath(self, path, params=None):
+ logfp = StringIO()
+ c = pathoc.Pathoc(
+ ("localhost", self.d.port),
+ ssl=self.ssl,
+ fp=logfp,
+ )
+ with c.connect():
+ if params:
+ path = path + "?" + urllib.urlencode(params)
+ resp = c.request("get:%s" % path)
+ return resp
+
def get(self, spec):
- resp = requests.get(self.d.p(spec), verify=False)
- return resp
+ logfp = StringIO()
+ c = pathoc.Pathoc(
+ ("localhost", self.d.port),
+ ssl=self.ssl,
+ fp=logfp,
+ )
+ with c.connect():
+ resp = c.request("get:/p/%s" % urllib.quote(spec).encode("string_escape"))
+ return resp
def pathoc(
self,
@@ -100,23 +128,23 @@ class DaemonTests(object):
fp=logfp,
use_http2=use_http2,
)
- c.connect(connect_to)
- ret = []
- for i in specs:
- resp = c.request(i)
- if resp:
- ret.append(resp)
- for frm in c.wait():
- ret.append(frm)
- c.stop()
- return ret, logfp.getvalue()
+ with c.connect(connect_to):
+ ret = []
+ for i in specs:
+ resp = c.request(i)
+ if resp:
+ ret.append(resp)
+ for frm in c.wait():
+ ret.append(frm)
+ c.stop()
+ return ret, logfp.getvalue()
-tmpdir = netlib.tutils.tmpdir
+tmpdir = tutils.tmpdir
-raises = netlib.tutils.raises
+raises = tutils.raises
-test_data = netlib.utils.Data(__name__)
+test_data = utils.Data(__name__)
def render(r, settings=language.Settings()):
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 00000000..08542426
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,15 @@
+[tox]
+envlist = py27, py35, lint
+
+[testenv]
+deps = -rrequirements.txt
+
+[testenv:py27]
+commands = py.test -n 8 --timeout 60 ./test
+
+[testenv:py35]
+commands = py.test -n 8 --timeout 60 ./test/netlib ./test/mitmproxy/script ./test/pathod/test_utils.py ./test/pathod/test_log.py
+
+[testenv:lint]
+deps = flake8
+commands = flake8 --count mitmproxy netlib pathod examples test
diff --git a/web/.babelrc b/web/.babelrc
index 5dd7708c..e2d67e33 100644
--- a/web/.babelrc
+++ b/web/.babelrc
@@ -1,4 +1,4 @@
{
"presets": ["es2015", "react"],
- "plugins": ["transform-class-properties"]
+ "plugins": ["transform-class-properties", "transform-object-rest-spread"]
} \ No newline at end of file
diff --git a/web/README b/web/README
index 63c3e6e0..c8e60379 100644
--- a/web/README
+++ b/web/README
@@ -3,4 +3,4 @@ Starting up
- npm install
- gulp
-- run mitmweb and open http://localhost:8081/ \ No newline at end of file
+- run mitmweb and open http://localhost:8081/
diff --git a/web/package.json b/web/package.json
index 2eaac445..88e976e1 100644
--- a/web/package.json
+++ b/web/package.json
@@ -30,6 +30,7 @@
"babel-core": "^6.7.7",
"babel-jest": "^12.0.2",
"babel-plugin-transform-class-properties": "^6.6.0",
+ "babel-plugin-transform-object-rest-spread": "^6.8.0",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"babelify": "^7.3.0",
diff --git a/web/src/css/header.less b/web/src/css/header.less
index 065471d1..b1bd9c04 100644
--- a/web/src/css/header.less
+++ b/web/src/css/header.less
@@ -18,6 +18,7 @@ header {
.filter-input {
.make-sm-column(3, @menu-row-gutter-width);
+ margin-bottom:5px;
}
.filter-input .popover {
@@ -32,6 +33,19 @@ header {
}
}
-.menu .btn {
- margin: 2px 2px 2px 2px;
+.menu .toggle-btn {
+ .make-xs-column(4, @menu-row-gutter-width);
+ .make-sm-column(3, @menu-row-gutter-width);
+ .make-lg-column(2, @menu-row-gutter-width);
+ margin-bottom:5px;
+}
+
+.menu .toggle-btn .btn {
+ width: 100%;
+}
+
+.menu .toggle-input-btn {
+ .make-sm-column(6, @menu-row-gutter-width);
+ .make-lg-column(4, @menu-row-gutter-width);
+ margin-bottom:5px;
} \ No newline at end of file
diff --git a/web/src/js/components/common.js b/web/src/js/components/common.js
index 21ca454f..87c34ffc 100644
--- a/web/src/js/components/common.js
+++ b/web/src/js/components/common.js
@@ -1,33 +1,8 @@
import React from "react"
import ReactDOM from "react-dom"
+import {Key} from "../utils.js";
import _ from "lodash"
-export var Router = {
- contextTypes: {
- location: React.PropTypes.object,
- router: React.PropTypes.object.isRequired
- },
- updateLocation: function (pathname, queryUpdate) {
- if (pathname === undefined) {
- pathname = this.context.location.pathname;
- }
- var query = this.context.location.query;
- if (queryUpdate !== undefined) {
- for (var i in queryUpdate) {
- if (queryUpdate.hasOwnProperty(i)) {
- query[i] = queryUpdate[i] || undefined; //falsey values shall be removed.
- }
- }
- }
- this.context.router.replace({pathname, query});
- },
- getQuery: function () {
- // For whatever reason, react-router always returns the same object, which makes comparing
- // the current props with nextProps impossible. As a workaround, we just clone the query object.
- return _.clone(this.context.location.query);
- }
-};
-
export var Splitter = React.createClass({
getDefaultProps: function () {
return {
@@ -133,14 +108,55 @@ export var Splitter = React.createClass({
}
});
-export const ToggleComponent = (props) =>
- <div
- className={"btn " + (props.checked ? "btn-primary" : "btn-default")}
- onClick={props.onToggleChanged}>
- <span><i className={"fa " + (props.checked ? "fa-check-square-o" : "fa-square-o")}></i> {props.name}</span>
- </div>
+export const ToggleButton = (props) =>
+ <div className="input-group toggle-btn">
+ <div
+ className={"btn " + (props.checked ? "btn-primary" : "btn-default")}
+ onClick={props.onToggleChanged}>
+ <span className={"fa " + (props.checked ? "fa-check-square-o" : "fa-square-o")}>&nbsp;{props.name}</span>
+ </div>
+ </div>;
+
+ToggleButton.propTypes = {
+ name: React.PropTypes.string.isRequired,
+ onToggleChanged: React.PropTypes.func.isRequired
+};
+
+export class ToggleInputButton extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {txt: props.txt};
+ }
-ToggleComponent.propTypes = {
+ render() {
+ return (
+ <div className="input-group toggle-input-btn">
+ <span
+ className="input-group-btn"
+ onClick={() => this.props.onToggleChanged(this.state.txt)}>
+ <div className={"btn " + (this.props.checked ? "btn-primary" : "btn-default")}>
+ <span className={"fa " + (this.props.checked ? "fa-check-square-o" : "fa-square-o")}/>
+ &nbsp;{this.props.name}
+ </div>
+ </span>
+ <input
+ className="form-control"
+ placeholder={this.props.placeholder}
+ disabled={this.props.checked}
+ value={this.state.txt}
+ type={this.props.inputType}
+ onChange={e => this.setState({txt: e.target.value})}
+ onKeyDown={e => {if (e.keyCode === Key.ENTER) this.props.onToggleChanged(this.state.txt); e.stopPropagation()}}/>
+ </div>
+ );
+ }
+}
+
+ToggleInputButton.propTypes = {
name: React.PropTypes.string.isRequired,
+ txt: React.PropTypes.string.isRequired,
onToggleChanged: React.PropTypes.func.isRequired
-} \ No newline at end of file
+};
+
+
+
diff --git a/web/src/js/components/eventlog.js b/web/src/js/components/eventlog.js
index d1b23ace..6e4f9096 100644
--- a/web/src/js/components/eventlog.js
+++ b/web/src/js/components/eventlog.js
@@ -1,7 +1,6 @@
import React from "react"
import ReactDOM from "react-dom"
import shallowEqual from "shallowequal"
-import {Router} from "./common.js"
import {Query} from "../actions.js"
import AutoScroll from "./helpers/AutoScroll";
import {calcVScroll} from "./helpers/VirtualScroll"
@@ -144,7 +143,6 @@ function ToggleFilter ({ name, active, toggleLevel }) {
const AutoScrollEventLog = AutoScroll(EventLogContents);
var EventLog = React.createClass({
- mixins: [Router],
getInitialState() {
return {
filter: {
@@ -157,7 +155,7 @@ var EventLog = React.createClass({
close() {
var d = {};
d[Query.SHOW_EVENTLOG] = undefined;
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
},
toggleLevel(level) {
var filter = _.extend({}, this.state.filter);
diff --git a/web/src/js/components/flowview/contentview.js b/web/src/js/components/flowview/contentview.js
index 2743eec3..cbac9a75 100644
--- a/web/src/js/components/flowview/contentview.js
+++ b/web/src/js/components/flowview/contentview.js
@@ -4,11 +4,15 @@ import _ from "lodash";
import {MessageUtils} from "../../flow/utils.js";
import {formatSize} from "../../utils.js";
-var image_regex = /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i;
var ViewImage = React.createClass({
+ propTypes: {
+ flow: React.PropTypes.object.isRequired,
+ message: React.PropTypes.object.isRequired,
+ },
statics: {
+ regex: /^image\/(png|jpe?g|gif|vnc.microsoft.icon|x-icon)$/i,
matches: function (message) {
- return image_regex.test(MessageUtils.getContentType(message));
+ return ViewImage.regex.test(MessageUtils.getContentType(message));
}
},
render: function () {
@@ -19,7 +23,11 @@ var ViewImage = React.createClass({
}
});
-var RawMixin = {
+var ContentLoader = React.createClass({
+ propTypes: {
+ flow: React.PropTypes.object.isRequired,
+ message: React.PropTypes.object.isRequired,
+ },
getInitialState: function () {
return {
content: undefined,
@@ -66,41 +74,54 @@ var RawMixin = {
<i className="fa fa-spinner fa-spin"></i>
</div>;
}
- return this.renderContent();
+ return React.cloneElement(this.props.children, {
+ content: this.state.content
+ })
}
-};
+});
var ViewRaw = React.createClass({
- mixins: [RawMixin],
+ propTypes: {
+ content: React.PropTypes.string.isRequired,
+ },
statics: {
+ textView: true,
matches: function (message) {
return true;
}
},
- renderContent: function () {
- return <pre>{this.state.content}</pre>;
+ render: function () {
+ return <pre>{this.props.content}</pre>;
}
});
-var json_regex = /^application\/json$/i;
var ViewJSON = React.createClass({
- mixins: [RawMixin],
+ propTypes: {
+ content: React.PropTypes.string.isRequired,
+ },
statics: {
+ textView: true,
+ regex: /^application\/json$/i,
matches: function (message) {
- return json_regex.test(MessageUtils.getContentType(message));
+ return ViewJSON.regex.test(MessageUtils.getContentType(message));
}
},
- renderContent: function () {
- var json = this.state.content;
+ render: function () {
+ var json = this.props.content;
try {
json = JSON.stringify(JSON.parse(json), null, 2);
} catch (e) {
+ // @noop
}
return <pre>{json}</pre>;
}
});
var ViewAuto = React.createClass({
+ propTypes: {
+ message: React.PropTypes.object.isRequired,
+ flow: React.PropTypes.object.isRequired,
+ },
statics: {
matches: function () {
return false; // don't match itself
@@ -115,14 +136,18 @@ var ViewAuto = React.createClass({
}
},
render: function () {
+ var { message, flow } = this.props
var View = ViewAuto.findView(this.props.message);
- return <View {...this.props}/>;
+ if (View.textView) {
+ return <ContentLoader message={message} flow={flow}><View content="" /></ContentLoader>
+ } else {
+ return <View message={message} flow={flow} />
+ }
}
});
var all = [ViewAuto, ViewImage, ViewJSON, ViewRaw];
-
var ContentEmpty = React.createClass({
render: function () {
var message_name = this.props.flow.request === this.props.message ? "request" : "response";
@@ -210,6 +235,7 @@ var ContentView = React.createClass({
}
},
render: function () {
+ var { flow, message } = this.props
var message = this.props.message;
if (message.contentLength === 0) {
return <ContentEmpty {...this.props}/>;
@@ -222,7 +248,11 @@ var ContentView = React.createClass({
var downloadUrl = MessageUtils.getContentURL(this.props.flow, message);
return <div>
- <this.state.View {...this.props} />
+ {this.state.View.textView ? (
+ <ContentLoader flow={flow} message={message}><this.state.View content="" /></ContentLoader>
+ ) : (
+ <this.state.View flow={flow} message={message} />
+ )}
<div className="view-options text-center">
<ViewSelector selectView={this.selectView} active={this.state.View} message={message}/>
&nbsp;
@@ -234,4 +264,4 @@ var ContentView = React.createClass({
}
});
-export default ContentView; \ No newline at end of file
+export default ContentView;
diff --git a/web/src/js/components/flowview/index.js b/web/src/js/components/flowview/index.js
index 47531f58..6f4f7395 100644
--- a/web/src/js/components/flowview/index.js
+++ b/web/src/js/components/flowview/index.js
@@ -1,6 +1,5 @@
import React from "react";
-import {Router, StickyHeadMixin} from "../common.js"
import Nav from "./nav.js";
import {Request, Response, Error} from "./messages.js";
import Details from "./details.js";
@@ -15,7 +14,6 @@ var allTabs = {
};
var FlowView = React.createClass({
- mixins: [StickyHeadMixin, Router],
getInitialState: function () {
return {
prompt: false
@@ -39,7 +37,7 @@ var FlowView = React.createClass({
this.selectTab(tabs[nextIndex]);
},
selectTab: function (panel) {
- this.updateLocation(`/flows/${this.props.flow.id}/${panel}`);
+ this.props.updateLocation(`/flows/${this.props.flow.id}/${panel}`);
},
promptEdit: function () {
var options;
@@ -114,4 +112,4 @@ var FlowView = React.createClass({
}
});
-export default FlowView; \ No newline at end of file
+export default FlowView;
diff --git a/web/src/js/components/footer.js b/web/src/js/components/footer.js
index e2d96288..8fe1081b 100644
--- a/web/src/js/components/footer.js
+++ b/web/src/js/components/footer.js
@@ -1,4 +1,5 @@
import React from "react";
+import {formatSize} from "../utils.js"
import {SettingsState} from "./common.js";
Footer.propTypes = {
@@ -6,7 +7,7 @@ Footer.propTypes = {
};
export default function Footer({ settings }) {
- const {mode, intercept} = settings;
+ const {mode, intercept, showhost, no_upstream_cert, rawtcp, http2, anticache, anticomp, stickyauth, stickycookie, stream} = settings;
return (
<footer>
{mode && mode != "regular" && (
@@ -15,6 +16,35 @@ export default function Footer({ settings }) {
{intercept && (
<span className="label label-success">Intercept: {intercept}</span>
)}
+ {showhost && (
+ <span className="label label-success">showhost</span>
+ )}
+ {no_upstream_cert && (
+ <span className="label label-success">no-upstream-cert</span>
+ )}
+ {rawtcp && (
+ <span className="label label-success">raw-tcp</span>
+ )}
+ {!http2 && (
+ <span className="label label-success">no-http2</span>
+ )}
+ {anticache && (
+ <span className="label label-success">anticache</span>
+ )}
+ {anticomp && (
+ <span className="label label-success">anticomp</span>
+ )}
+ {stickyauth && (
+ <span className="label label-success">stickyauth: {stickyauth}</span>
+ )}
+ {stickycookie && (
+ <span className="label label-success">stickycookie: {stickycookie}</span>
+ )}
+ {stream && (
+ <span className="label label-success">stream: {formatSize(stream)}</span>
+ )}
+
+
</footer>
);
}
diff --git a/web/src/js/components/header.js b/web/src/js/components/header.js
index 226cb61f..643659c3 100644
--- a/web/src/js/components/header.js
+++ b/web/src/js/components/header.js
@@ -4,9 +4,10 @@ import $ from "jquery";
import Filt from "../filt/filt.js";
import {Key} from "../utils.js";
-import {Router, ToggleComponent} from "./common.js";
+import {ToggleInputButton, ToggleButton} from "./common.js";
import {SettingsActions, FlowActions} from "../actions.js";
import {Query} from "../actions.js";
+import {SettingsState} from "./common.js";
var FilterDocs = React.createClass({
statics: {
@@ -161,7 +162,6 @@ var FilterInput = React.createClass({
});
export var MainMenu = React.createClass({
- mixins: [Router],
propTypes: {
settings: React.PropTypes.object.isRequired,
},
@@ -172,19 +172,19 @@ export var MainMenu = React.createClass({
onSearchChange: function (val) {
var d = {};
d[Query.SEARCH] = val;
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
},
onHighlightChange: function (val) {
var d = {};
d[Query.HIGHLIGHT] = val;
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
},
onInterceptChange: function (val) {
SettingsActions.update({intercept: val});
},
render: function () {
- var search = this.getQuery()[Query.SEARCH] || "";
- var highlight = this.getQuery()[Query.HIGHLIGHT] || "";
+ var search = this.props.query[Query.SEARCH] || "";
+ var highlight = this.props.query[Query.HIGHLIGHT] || "";
var intercept = this.props.settings.intercept || "";
return (
@@ -224,72 +224,88 @@ var ViewMenu = React.createClass({
title: "View",
route: "flows"
},
- mixins: [Router],
toggleEventLog: function () {
var d = {};
- if (this.getQuery()[Query.SHOW_EVENTLOG]) {
+ if (this.props.query[Query.SHOW_EVENTLOG]) {
d[Query.SHOW_EVENTLOG] = undefined;
} else {
d[Query.SHOW_EVENTLOG] = "t"; // any non-false value will do it, keep it short
}
- this.updateLocation(undefined, d);
+ this.props.updateLocation(undefined, d);
console.log('toggleevent');
},
render: function () {
- var showEventLog = this.getQuery()[Query.SHOW_EVENTLOG];
+ var showEventLog = this.props.query[Query.SHOW_EVENTLOG];
return (
- <div>
- <ToggleComponent
- checked={showEventLog}
- name = "Show Eventlog"
- onToggleChanged={this.toggleEventLog}/>
- </div>
+ <div>
+ <div className="menu-row">
+ <ToggleButton
+ checked={showEventLog}
+ name = "Show Eventlog"
+ onToggleChanged={this.toggleEventLog}/>
+ </div>
+ <div className="clearfix"></div>
+ </div>
);
}
});
-
-class OptionMenu extends React.Component{
- static title = "Options";
- constructor(props){
- super(props);
- this.state = {
- options :
- [
- {name: "--host", checked: true},
- {name: "--no-upstream-cert", checked: false},
- {name: "--http2", checked: false},
- {name: "--anticache", checked: false},
- {name: "--anticomp", checked: false},
- {name: "--stickycookie", checked: true},
- {name: "--stickyauth", checked: false},
- {name: "--stream", checked: false}
- ]
- }
- }
- setOption(entry){
- console.log(entry.name);//TODO: get options from outside and remove state
- entry.checked = !entry.checked;
- this.setState({options: this.state.options});
- }
- render() {
- return (
+export const OptionMenu = (props) => {
+ const {mode, intercept, showhost, no_upstream_cert, rawtcp, http2, anticache, anticomp, stickycookie, stickyauth, stream} = props.settings;
+ return (
<div>
- {this.state.options.map((entry, i) => {
- return (
- <ToggleComponent
- key={i}
- checked={entry.checked}
- name = {entry.name}
- onToggleChanged={() => this.setOption(entry)}/>
- );
- })}
+ <div className="menu-row">
+ <ToggleButton name="showhost"
+ checked={showhost}
+ onToggleChanged={() => SettingsActions.update({showhost: !showhost})}
+ />
+ <ToggleButton name="no_upstream_cert"
+ checked={no_upstream_cert}
+ onToggleChanged={() => SettingsActions.update({no_upstream_cert: !no_upstream_cert})}
+ />
+ <ToggleButton name="rawtcp"
+ checked={rawtcp}
+ onToggleChanged={() => SettingsActions.update({rawtcp: !rawtcp})}
+ />
+ <ToggleButton name="http2"
+ checked={http2}
+ onToggleChanged={() => SettingsActions.update({http2: !http2})}
+ />
+ <ToggleButton name="anticache"
+ checked={anticache}
+ onToggleChanged={() => SettingsActions.update({anticache: !anticache})}
+ />
+ <ToggleButton name="anticomp"
+ checked={anticomp}
+ onToggleChanged={() => SettingsActions.update({anticomp: !anticomp})}
+ />
+ <ToggleInputButton name="stickyauth" placeholder="Sticky auth filter"
+ checked={Boolean(stickyauth)}
+ txt={stickyauth || ""}
+ onToggleChanged={txt => SettingsActions.update({stickyauth: (!stickyauth ? txt : null)})}
+ />
+ <ToggleInputButton name="stickycookie" placeholder="Sticky cookie filter"
+ checked={Boolean(stickycookie)}
+ txt={stickycookie || ""}
+ onToggleChanged={txt => SettingsActions.update({stickycookie: (!stickycookie ? txt : null)})}
+ />
+ <ToggleInputButton name="stream" placeholder="stream..."
+ checked={Boolean(stream)}
+ txt={stream || ""}
+ inputType = "number"
+ onToggleChanged={txt => SettingsActions.update({stream: (!stream ? txt : null)})}
+ />
+ </div>
+ <div className="clearfix"/>
</div>
- );
- }
-}
+ );
+};
+OptionMenu.title = "Options";
+OptionMenu.propTypes = {
+ settings: React.PropTypes.object.isRequired
+};
var ReportsMenu = React.createClass({
statics: {
@@ -391,7 +407,6 @@ var header_entries = [MainMenu, ViewMenu, OptionMenu /*, ReportsMenu */];
export var Header = React.createClass({
- mixins: [Router],
propTypes: {
settings: React.PropTypes.object.isRequired,
},
@@ -402,7 +417,7 @@ export var Header = React.createClass({
},
handleClick: function (active, e) {
e.preventDefault();
- this.updateLocation(active.route);
+ this.props.updateLocation(active.route);
this.setState({active: active});
},
render: function () {
@@ -430,7 +445,11 @@ export var Header = React.createClass({
{header}
</nav>
<div className="menu">
- <this.state.active ref="active" settings={this.props.settings}/>
+ <this.state.active
+ settings={this.props.settings}
+ updateLocation={this.props.updateLocation}
+ query={this.props.query}
+ />
</div>
</header>
);
diff --git a/web/src/js/components/mainview.js b/web/src/js/components/mainview.js
index 87c0c4bd..964e82db 100644
--- a/web/src/js/components/mainview.js
+++ b/web/src/js/components/mainview.js
@@ -5,12 +5,11 @@ import {Query} from "../actions.js";
import {Key} from "../utils.js";
import {StoreView} from "../store/view.js";
import Filt from "../filt/filt.js";
-import { Router, Splitter} from "./common.js"
+import {Splitter} from "./common.js"
import FlowTable from "./flowtable.js";
import FlowView from "./flowview/index.js";
var MainView = React.createClass({
- mixins: [Router],
contextTypes: {
flowStore: React.PropTypes.object.isRequired,
},
@@ -41,9 +40,9 @@ var MainView = React.createClass({
},
getViewFilt: function () {
try {
- var filtStr = this.getQuery()[Query.SEARCH];
+ var filtStr = this.props.query[Query.SEARCH];
var filt = filtStr ? Filt.parse(filtStr) : () => true;
- var highlightStr = this.getQuery()[Query.HIGHLIGHT];
+ var highlightStr = this.props.query[Query.HIGHLIGHT];
var highlight = highlightStr ? Filt.parse(highlightStr) : () => false;
} catch (e) {
console.error("Error when processing filter: " + e);
@@ -94,10 +93,10 @@ var MainView = React.createClass({
selectFlow: function (flow) {
if (flow) {
var tab = this.props.routeParams.detailTab || "request";
- this.updateLocation(`/flows/${flow.id}/${tab}`);
+ this.props.updateLocation(`/flows/${flow.id}/${tab}`);
this.refs.flowTable.scrollIntoView(flow);
} else {
- this.updateLocation("/flows");
+ this.props.updateLocation("/flows");
}
},
selectFlowRelative: function (shift) {
@@ -225,6 +224,8 @@ var MainView = React.createClass({
key="flowDetails"
ref="flowDetails"
tab={this.props.routeParams.detailTab}
+ query={this.props.query}
+ updateLocation={this.props.updateLocation}
flow={selected}/>
];
} else {
diff --git a/web/src/js/components/proxyapp.js b/web/src/js/components/proxyapp.js
index d17a1522..f47c5bb4 100644
--- a/web/src/js/components/proxyapp.js
+++ b/web/src/js/components/proxyapp.js
@@ -2,7 +2,7 @@ import React from "react";
import ReactDOM from "react-dom";
import _ from "lodash";
-import {Router, Splitter} from "./common.js"
+import {Splitter} from "./common.js"
import MainView from "./mainview.js";
import Footer from "./footer.js";
import {Header, MainMenu} from "./header.js";
@@ -21,13 +21,34 @@ var Reports = React.createClass({
var ProxyAppMain = React.createClass({
- mixins: [Router],
childContextTypes: {
flowStore: React.PropTypes.object.isRequired,
eventStore: React.PropTypes.object.isRequired,
returnFocus: React.PropTypes.func.isRequired,
location: React.PropTypes.object.isRequired,
},
+ contextTypes: {
+ router: React.PropTypes.object.isRequired
+ },
+ updateLocation: function (pathname, queryUpdate) {
+ if (pathname === undefined) {
+ pathname = this.props.location.pathname;
+ }
+ var query = this.props.location.query;
+ if (queryUpdate !== undefined) {
+ for (var i in queryUpdate) {
+ if (queryUpdate.hasOwnProperty(i)) {
+ query[i] = queryUpdate[i] || undefined; //falsey values shall be removed.
+ }
+ }
+ }
+ this.context.router.replace({pathname, query});
+ },
+ getQuery: function () {
+ // For whatever reason, react-router always returns the same object, which makes comparing
+ // the current props with nextProps impossible. As a workaround, we just clone the query object.
+ return _.clone(this.props.location.query);
+ },
componentDidMount: function () {
this.focus();
this.settingsStore.addListener("recalculate", this.onSettingsChange);
@@ -97,23 +118,23 @@ var ProxyAppMain = React.createClass({
e.preventDefault();
},
render: function () {
+ var query = this.getQuery();
var eventlog;
if (this.props.location.query[Query.SHOW_EVENTLOG]) {
eventlog = [
<Splitter key="splitter" axis="y"/>,
- <EventLog key="eventlog"/>
+ <EventLog key="eventlog" updateLocation={this.updateLocation}/>
];
} else {
eventlog = null;
}
- var children = React.cloneElement(
- this.props.children,
- { ref: "view", location: this.props.location }
- );
return (
<div id="container" tabIndex="0" onKeyDown={this.onKeydown}>
- <Header ref="header" settings={this.state.settings}/>
- {children}
+ <Header ref="header" settings={this.state.settings} updateLocation={this.updateLocation} query={query} />
+ {React.cloneElement(
+ this.props.children,
+ { ref: "view", location: this.props.location , updateLocation: this.updateLocation, query }
+ )}
{eventlog}
<Footer settings={this.state.settings}/>
</div>
@@ -125,12 +146,12 @@ var ProxyAppMain = React.createClass({
import { Route, Router as ReactRouter, hashHistory, Redirect} from "react-router";
export var app = (
-<ReactRouter history={hashHistory}>
- <Redirect from="/" to="/flows" />
- <Route path="/" component={ProxyAppMain}>
- <Route path="flows" component={MainView}/>
- <Route path="flows/:flowId/:detailTab" component={MainView}/>
- <Route path="reports" component={Reports}/>
- </Route>
-</ReactRouter>
-); \ No newline at end of file
+ <ReactRouter history={hashHistory}>
+ <Redirect from="/" to="/flows" />
+ <Route path="/" component={ProxyAppMain}>
+ <Route path="flows" component={MainView}/>
+ <Route path="flows/:flowId/:detailTab" component={MainView}/>
+ <Route path="reports" component={Reports}/>
+ </Route>
+ </ReactRouter>
+);