diff --git a/bin/pwnagotchi b/bin/pwnagotchi index 24868fc..0ce53f3 100755 --- a/bin/pwnagotchi +++ b/bin/pwnagotchi @@ -32,7 +32,10 @@ if __name__ == '__main__': help="Enable debug logs.") parser.add_argument('--version', dest="version", action="store_true", default=False, - help="Prints the version.") + help="Print the version.") + + parser.add_argument('--print-config', dest="print_config", action="store_true", default=False, + help="Print the configuration.") args = parser.parse_args() @@ -41,6 +44,10 @@ if __name__ == '__main__': exit(0) config = utils.load_config(args) + if args.print_config: + print(yaml.dump(config, default_flow_style=False)) + exit(0) + utils.setup_logging(args, config) pwnagotchi.set_name(config['main']['name']) @@ -53,8 +60,6 @@ if __name__ == '__main__': logging.info("%s@%s (v%s)" % (pwnagotchi.name(), agent.fingerprint(), pwnagotchi.version)) - logging.debug("effective configuration:\n\n%s\n\n" % yaml.dump(config, default_flow_style=False)) - for _, plugin in plugins.loaded.items(): logging.debug("plugin '%s' v%s" % (plugin.__class__.__name__, plugin.__version__)) diff --git a/pwnagotchi/grid.py b/pwnagotchi/grid.py index daef21a..0cac9de 100644 --- a/pwnagotchi/grid.py +++ b/pwnagotchi/grid.py @@ -12,8 +12,12 @@ API_ADDRESS = "http://127.0.0.1:8666/api/v1" def is_connected(): try: - socket.create_connection(("api.pwnagotchi.ai", 443), timeout=30) - return True + # check DNS + host = socket.gethostbyname('api.pwnagotchi.ai') + if host: + # check connectivity itself + socket.create_connection((host, 443), timeout=30) + return True except: pass return False diff --git a/pwnagotchi/ui/view.py b/pwnagotchi/ui/view.py index 36d280c..e2207af 100644 --- a/pwnagotchi/ui/view.py +++ b/pwnagotchi/ui/view.py @@ -119,12 +119,14 @@ class View(object): def _refresh_handler(self): delay = 1.0 / self._config['ui']['fps'] - # logging.info("view refresh handler started with period of %.2fs" % delay) - while True: - name = self._state.get('name') - self.set('name', name.rstrip('█').strip() if '█' in name else (name + ' █')) - self.update() + try: + name = self._state.get('name') + self.set('name', name.rstrip('█').strip() if '█' in name else (name + ' █')) + self.update() + except Exception as e: + logging.warning("non fatal error while updating view: %s" % e) + time.sleep(delay) def set(self, key, value): @@ -360,14 +362,15 @@ class View(object): if self._frozen: return - changes = self._state.changes(ignore=self._ignore_changes) + state = self._state + changes = state.changes(ignore=self._ignore_changes) if force or len(changes): self._canvas = Image.new('1', (self._width, self._height), WHITE) drawer = ImageDraw.Draw(self._canvas) plugins.on('ui_update', self) - for key, lv in self._state.items(): + for key, lv in state.items(): lv.draw(self._canvas, drawer) web.update_frame(self._canvas) diff --git a/pwnagotchi/ui/web/handler.py b/pwnagotchi/ui/web/handler.py index e7a4dc1..c11b173 100644 --- a/pwnagotchi/ui/web/handler.py +++ b/pwnagotchi/ui/web/handler.py @@ -3,6 +3,7 @@ import os import base64 import _thread import secrets +import json from functools import wraps # https://stackoverflow.com/questions/14888799/disable-console-messages-in-flask-server @@ -61,9 +62,11 @@ class Handler: @wraps(f) def wrapper(*args, **kwargs): auth = request.authorization - if not auth or not auth.username or not auth.password or not self._check_creds(auth.username, auth.password): + if not auth or not auth.username or not auth.password or not self._check_creds(auth.username, + auth.password): return Response('Unauthorized', 401, {'WWW-Authenticate': 'Basic realm="Unauthorized"'}) return f(*args, **kwargs) + return wrapper def index(self): @@ -82,6 +85,9 @@ class Handler: error = None try: + if not grid.is_connected(): + raise Exception('not connected') + inbox = grid.inbox(page, with_pager=True) except Exception as e: logging.exception('error while reading pwnmail inbox') @@ -106,7 +112,7 @@ class Handler: return render_template('profile.html', name=pwnagotchi.name(), fingerprint=self._agent.fingerprint(), - data=data, + data=json.dumps(data, indent=2), error=error) def inbox_peers(self): @@ -129,6 +135,9 @@ class Handler: error = None try: + if not grid.is_connected(): + raise Exception('not connected') + message = grid.inbox_message(id) if message['data']: message['data'] = base64.b64decode(message['data']).decode("utf-8") @@ -151,6 +160,9 @@ class Handler: error = None try: + if not grid.is_connected(): + raise Exception('not connected') + grid.send_message(to, message) except Exception as e: error = str(e) @@ -158,6 +170,9 @@ class Handler: return jsonify({"error": error}) def mark_message(self, id, mark): + if not grid.is_connected(): + abort(200) + logging.info("marking message %d as %s" % (int(id), mark)) grid.mark_message(id, mark) return redirect("/inbox") diff --git a/pwnagotchi/ui/web/server.py b/pwnagotchi/ui/web/server.py index 4ab77dc..1b10903 100644 --- a/pwnagotchi/ui/web/server.py +++ b/pwnagotchi/ui/web/server.py @@ -35,6 +35,7 @@ class Server: static_url_path='', static_folder=os.path.join(web_path, 'static'), template_folder=os.path.join(web_path, 'templates')) + app.secret_key = secrets.token_urlsafe(256) if self._origin: diff --git a/pwnagotchi/ui/web/static/css/style.css b/pwnagotchi/ui/web/static/css/style.css index 6c66bee..8acc267 100644 --- a/pwnagotchi/ui/web/static/css/style.css +++ b/pwnagotchi/ui/web/static/css/style.css @@ -1,22 +1,7 @@ -form { - margin-block-end: 0; +.ui-image { + width: 100%; } - -.content { - position: absolute; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - height: calc(var(--vh, 1vh) * 100); - display: flex; - flex-direction: column; -} - -/** - * make sure image is displayed without any blur - */ .pixelated { image-rendering: optimizeSpeed; /* Legal fallback */ image-rendering: -moz-crisp-edges; /* Firefox */ @@ -33,33 +18,6 @@ form { position: relative; } -.ui-image { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - max-width: 100vw; - height: 100%; - object-fit: contain; - object-position: 50% 0; -} - -.buttons-wrapper { - flex-shrink: 0; - display: flex; - flex-wrap: wrap; - padding: 0 16px; -} - -.button { - border: 1px solid black; - border-radius: 4px; - font-size: 2em; - background: #f8b506; - margin: 16px; -} - div.status { position: absolute; top: 0; diff --git a/pwnagotchi/ui/web/static/js/refresh.js b/pwnagotchi/ui/web/static/js/refresh.js deleted file mode 100644 index 24e7789..0000000 --- a/pwnagotchi/ui/web/static/js/refresh.js +++ /dev/null @@ -1,7 +0,0 @@ -window.onload = function() { - var image = document.getElementById("ui"); - function updateImage() { - image.src = image.src.split("?")[0] + "?" + new Date().getTime(); - } - setInterval(updateImage, 1000); -} \ No newline at end of file diff --git a/pwnagotchi/ui/web/templates/base.html b/pwnagotchi/ui/web/templates/base.html new file mode 100644 index 0000000..c39e080 --- /dev/null +++ b/pwnagotchi/ui/web/templates/base.html @@ -0,0 +1,71 @@ + + + + + + + + {% block title %} + {% endblock %} + + + + + + + + + + + + +
+ + {% if error %} +
+

{{ error }}

+
+ + {% endif %} + + {% set navigation = [ + ( '/', 'home', 'eye', 'Home' ), + ( '/inbox', 'inbox', 'bars', 'Inbox' ), + ( '/inbox/new', 'new', 'mail', 'New' ), + ( '/inbox/profile', 'profile', 'info', 'Profile' ), + ( '/inbox/peers', 'peers', 'user', 'Peers' ), + ] %} + {% set active_page = active_page|default('inbox') %} + +
+
+
    + {% for href, id, icon, caption in navigation %} +
  • + {{ caption }} +
  • + {% endfor %} +
+
+
+ + {% block content %} + {% endblock %} + +
+ + + diff --git a/pwnagotchi/ui/web/templates/inbox.html b/pwnagotchi/ui/web/templates/inbox.html index 2132d6d..a63a717 100644 --- a/pwnagotchi/ui/web/templates/inbox.html +++ b/pwnagotchi/ui/web/templates/inbox.html @@ -1,81 +1,38 @@ - - - - - +{% extends "base.html" %} +{% set active_page = "inbox" %} - {{ name }} Inbox +{% block title %} +{{ name }} Inbox +{% endblock %} - - - - +{% block content %} + - - - - - -
- - {% if error %} -
{{ error }}
- {% else %} - {% if inbox.records == 0 %} - Inbox is empty. - {% else %} - -
-
- -
-
- -