mirror of
https://github.com/jayofelony/pwnagotchi.git
synced 2025-07-01 18:37:27 -04:00
Merge remote-tracking branch 'upstream/master' into patch-1
This commit is contained in:
@ -154,7 +154,7 @@ class AutoUpdate(plugins.Plugin):
|
||||
self.lock = Lock()
|
||||
|
||||
def on_loaded(self):
|
||||
if 'interval' not in self.options or ('interval' in self.options and self.options['interval'] is None):
|
||||
if 'interval' not in self.options or ('interval' in self.options and not self.options['interval']):
|
||||
logging.error("[update] main.plugins.auto-update.interval is not set")
|
||||
return
|
||||
self.ready = True
|
||||
|
@ -437,7 +437,7 @@ class BTTether(plugins.Plugin):
|
||||
for device_opt in ['enabled', 'priority', 'scantime', 'search_order',
|
||||
'max_tries', 'share_internet', 'mac', 'ip',
|
||||
'netmask', 'interval']:
|
||||
if device_opt not in options or (device_opt in options and options[device_opt] is None):
|
||||
if device_opt not in options or (device_opt in options and not options[device_opt]):
|
||||
logging.error("BT-TETHER: Please specify the %s for device %s.",
|
||||
device_opt, device)
|
||||
break
|
||||
@ -448,7 +448,7 @@ class BTTether(plugins.Plugin):
|
||||
# legacy
|
||||
if 'mac' in self.options:
|
||||
for opt in ['share_internet', 'mac', 'ip', 'netmask', 'interval']:
|
||||
if opt not in self.options or (opt in self.options and self.options[opt] is None):
|
||||
if opt not in self.options or (opt in self.options and not self.options[opt]):
|
||||
logging.error("BT-TETHER: Please specify the %s in your config.yml.", opt)
|
||||
return
|
||||
|
||||
|
@ -26,7 +26,7 @@ class NetPos(plugins.Plugin):
|
||||
self.lock = threading.Lock()
|
||||
|
||||
def on_loaded(self):
|
||||
if 'api_key' not in self.options or ('api_key' in self.options and self.options['api_key'] is None):
|
||||
if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']):
|
||||
logging.error("NET-POS: api_key isn't set. Can't use mozilla's api.")
|
||||
return
|
||||
|
||||
|
@ -28,7 +28,7 @@ class OnlineHashCrack(plugins.Plugin):
|
||||
"""
|
||||
Gets called when the plugin gets loaded
|
||||
"""
|
||||
if 'email' not in self.options or ('email' in self.options and self.options['email'] is None):
|
||||
if 'email' not in self.options or ('email' in self.options and not self.options['email']):
|
||||
logging.error("OHC: Email isn't set. Can't upload to onlinehashcrack.com")
|
||||
return
|
||||
|
||||
|
147
pwnagotchi/plugins/default/switcher.py
Normal file
147
pwnagotchi/plugins/default/switcher.py
Normal file
@ -0,0 +1,147 @@
|
||||
import os
|
||||
import logging
|
||||
from threading import Lock
|
||||
from functools import partial
|
||||
from pwnagotchi import plugins
|
||||
from pwnagotchi import reboot
|
||||
|
||||
|
||||
def systemd_dropin(name, content):
|
||||
if not name.endswith('.service'):
|
||||
name = '%s.service' % name
|
||||
|
||||
dropin_dir = "/etc/systemd/system/%s.d/" % name
|
||||
os.makedirs(dropin_dir, exist_ok=True)
|
||||
|
||||
with open(os.path.join(dropin_dir, "switcher.conf"), "wt") as dropin:
|
||||
dropin.write(content)
|
||||
|
||||
systemctl("daemon-reload")
|
||||
|
||||
def systemctl(command, unit=None):
|
||||
if unit:
|
||||
os.system("/bin/systemctl %s %s" % (command, unit))
|
||||
else:
|
||||
os.system("/bin/systemctl %s" % command)
|
||||
|
||||
def run_task(name, options):
|
||||
task_service_name = "switcher-%s-task.service" % name
|
||||
# save all the commands to a shell script
|
||||
script_dir = '/usr/local/bin/'
|
||||
script_path = os.path.join(script_dir, 'switcher-%s.sh' % name)
|
||||
os.makedirs(script_dir, exist_ok=True)
|
||||
|
||||
with open(script_path, 'wt') as script_file:
|
||||
script_file.write('#!/bin/bash\n')
|
||||
for cmd in options['commands']:
|
||||
script_file.write('%s\n' % cmd)
|
||||
|
||||
os.system("chmod a+x %s" % script_path)
|
||||
|
||||
# here we create the service which runs the tasks
|
||||
with open('/etc/systemd/system/%s' % task_service_name, 'wt') as task_service:
|
||||
task_service.write("""
|
||||
[Unit]
|
||||
Description=Executes the tasks of the pwnagotchi switcher plugin
|
||||
After=pwnagotchi.service bettercap.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=-/usr/local/bin/switcher-%s.sh
|
||||
ExecStart=-/bin/rm /etc/systemd/system/%s
|
||||
ExecStart=-/bin/rm /usr/local/bin/switcher-%s.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
""" % (name, task_service_name, name))
|
||||
|
||||
if 'reboot' in options and options['reboot']:
|
||||
# create a indication file!
|
||||
# if this file is set, we want the switcher-tasks to run
|
||||
open('/root/.switcher', 'a').close()
|
||||
|
||||
# add condition
|
||||
systemd_dropin("pwnagotchi.service", """
|
||||
[Unit]
|
||||
ConditionPathExists=!/root/.switcher""")
|
||||
|
||||
systemd_dropin("bettercap.service", """
|
||||
[Unit]
|
||||
ConditionPathExists=!/root/.switcher""")
|
||||
|
||||
systemd_dropin(task_service_name, """
|
||||
[Service]
|
||||
ExecStart=-/bin/rm /root/.switcher
|
||||
ExecStart=-/bin/rm /etc/systemd/system/switcher-reboot.timer""")
|
||||
|
||||
with open('/etc/systemd/system/switcher-reboot.timer', 'wt') as reboot_timer:
|
||||
reboot_timer.write("""
|
||||
[Unit]
|
||||
Description=Reboot when time is up
|
||||
ConditionPathExists=/root/.switcher
|
||||
|
||||
[Timer]
|
||||
OnBootSec=%sm
|
||||
Unit=reboot.target
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
""" % options['stopwatch'])
|
||||
|
||||
systemctl("daemon-reload")
|
||||
systemctl("enable", "switcher-reboot.timer")
|
||||
systemctl("enable", task_service_name)
|
||||
reboot()
|
||||
return
|
||||
|
||||
systemctl("daemon-reload")
|
||||
systemctl("start", task_service_name)
|
||||
|
||||
class Switcher(plugins.Plugin):
|
||||
__author__ = '33197631+dadav@users.noreply.github.com'
|
||||
__version__ = '0.0.1'
|
||||
__name__ = 'switcher'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'This plugin is a generic task scheduler.'
|
||||
|
||||
def __init__(self):
|
||||
self.ready = False
|
||||
self.lock = Lock()
|
||||
|
||||
def trigger(self, name, *args, **kwargs):
|
||||
with self.lock:
|
||||
function_name = name.lstrip('on_')
|
||||
if function_name in self.tasks:
|
||||
task = self.tasks[function_name]
|
||||
|
||||
# is this task enabled?
|
||||
if 'enabled' not in task or ('enabled' in task and not task['enabled']):
|
||||
return
|
||||
|
||||
run_task(function_name, task)
|
||||
|
||||
def on_loaded(self):
|
||||
if 'tasks' in self.options and self.options['tasks']:
|
||||
self.tasks = self.options['tasks']
|
||||
else:
|
||||
logging.debug('[switcher] No tasks found...')
|
||||
return
|
||||
|
||||
logging.info("[switcher] is loaded.")
|
||||
|
||||
# create hooks
|
||||
logging.debug("[switcher] creating hooks...")
|
||||
methods = ['webhook', 'internet_available', 'ui_setup', 'ui_update',
|
||||
'unload', 'display_setup', 'ready', 'ai_ready', 'ai_policy',
|
||||
'ai_training_start', 'ai_training_step', 'ai_training_end',
|
||||
'ai_best_reward', 'ai_worst_reward', 'free_channel',
|
||||
'bored', 'sad', 'excited', 'lonely', 'rebooting', 'wait',
|
||||
'sleep', 'wifi_update', 'unfiltered_ap_list', 'association',
|
||||
'deauthentication', 'channel_hop', 'handshake', 'epoch',
|
||||
'peer_detected', 'peer_lost']
|
||||
|
||||
for m in methods:
|
||||
setattr(Switcher, 'on_%s' % m, partial(self.trigger, m))
|
||||
|
||||
logging.debug("[switcher] triggers are ready to fire...")
|
@ -7,12 +7,14 @@
|
||||
# For Raspberry Pi Zero Ups Power Expansion Board with Integrated Serial Port S3U4
|
||||
# https://www.ebay.de/itm/For-Raspberry-Pi-Zero-Ups-Power-Expansion-Board-with-Integrated-Serial-Port-S3U4/323873804310
|
||||
# https://www.aliexpress.com/item/32888533624.html
|
||||
import logging
|
||||
import struct
|
||||
|
||||
from pwnagotchi.ui.components import LabeledValue
|
||||
from pwnagotchi.ui.view import BLACK
|
||||
import pwnagotchi.ui.fonts as fonts
|
||||
import pwnagotchi.plugins as plugins
|
||||
import pwnagotchi
|
||||
|
||||
|
||||
# TODO: add enable switch in config.yml an cleanup all to the best place
|
||||
@ -63,4 +65,9 @@ class UPSLite(plugins.Plugin):
|
||||
ui.remove_element('ups')
|
||||
|
||||
def on_ui_update(self, ui):
|
||||
ui.set('ups', "%2i%%" % self.ups.capacity())
|
||||
capacity = self.ups.capacity()
|
||||
ui.set('ups', "%2i%%" % capacity)
|
||||
if capacity <= self.options['shutdown']:
|
||||
logging.info('[ups_lite] Empty battery (<= %s%%): shuting down' % self.options['shutdown'])
|
||||
ui.update(force=True, new_data={'status': 'Battery exhausted, bye ...'})
|
||||
pwnagotchi.shutdown()
|
||||
|
@ -1,13 +1,12 @@
|
||||
import logging
|
||||
import json
|
||||
import yaml
|
||||
import toml
|
||||
import _thread
|
||||
import pwnagotchi.plugins as plugins
|
||||
from pwnagotchi import restart
|
||||
from flask import abort
|
||||
from flask import render_template_string
|
||||
|
||||
|
||||
INDEX = """
|
||||
<html>
|
||||
<head>
|
||||
@ -500,13 +499,13 @@ class WebConfig(plugins.Plugin):
|
||||
elif request.method == "POST":
|
||||
if path == "save-config":
|
||||
try:
|
||||
parsed_yaml = yaml.safe_load(str(request.get_json()))
|
||||
with open('/etc/pwnagotchi/config.yml', 'w') as config_file:
|
||||
yaml.safe_dump(parsed_yaml, config_file, encoding='utf-8',
|
||||
allow_unicode=True, default_flow_style=False)
|
||||
parsed_toml = toml.loads(request.get_json())
|
||||
with open('/etc/pwnagotchi/config.toml') as config_file:
|
||||
toml.dump(parsed_toml, config_file)
|
||||
|
||||
_thread.start_new_thread(restart, (self.mode,))
|
||||
return "success"
|
||||
except yaml.YAMLError as yaml_ex:
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
return "config error"
|
||||
abort(404)
|
||||
|
@ -270,5 +270,16 @@
|
||||
positionsLoaded = true;
|
||||
drawPositions();
|
||||
});
|
||||
// get current position and set marker in interval
|
||||
var myLocationMarker = {};
|
||||
function onLocationFound(e) {
|
||||
if (myLocationMarker != undefined) {
|
||||
mymap.removeLayer(myLocationMarker);
|
||||
};
|
||||
myLocationMarker = L.marker(e.latlng).addTo(mymap);
|
||||
setTimeout(function(){ mymap.locate(); }, 30000);
|
||||
}
|
||||
mymap.on('locationfound', onLocationFound);
|
||||
mymap.locate({setView: true});
|
||||
</script>
|
||||
</body></html>
|
||||
|
@ -23,7 +23,7 @@ from dateutil.parser import parse
|
||||
|
||||
class Webgpsmap(plugins.Plugin):
|
||||
__author__ = 'https://github.com/xenDE and https://github.com/dadav'
|
||||
__version__ = '1.3.0'
|
||||
__version__ = '1.3.1'
|
||||
__name__ = 'webgpsmap'
|
||||
__license__ = 'GPL3'
|
||||
__description__ = 'a plugin for pwnagotchi that shows a openstreetmap with positions of ap-handshakes in your webbrowser'
|
||||
@ -50,6 +50,7 @@ class Webgpsmap(plugins.Plugin):
|
||||
"""
|
||||
# defaults:
|
||||
response_header_contenttype = None
|
||||
response_header_contentdisposition = None
|
||||
response_mimetype = "application/xhtml+xml"
|
||||
if not self.ready:
|
||||
try:
|
||||
@ -90,6 +91,21 @@ class Webgpsmap(plugins.Plugin):
|
||||
except Exception as error:
|
||||
logging.error(f"[webgpsmap] error: {error}")
|
||||
return
|
||||
elif path.startswith('offlinemap'):
|
||||
# for download an all-in-one html file with positions.json inside
|
||||
try:
|
||||
self.ALREADY_SENT = list()
|
||||
json_data = json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']))
|
||||
html_data = self.get_html()
|
||||
html_data = html_data.replace('var positions = [];', 'var positions = ' + json_data + ';positionsLoaded=true;drawPositions();')
|
||||
response_data = bytes(html_data, "utf-8")
|
||||
response_status = 200
|
||||
response_mimetype = "application/xhtml+xml"
|
||||
response_header_contenttype = 'text/html'
|
||||
response_header_contentdisposition = 'attachment; filename=webgpsmap.html';
|
||||
except Exception as error:
|
||||
logging.error(f"[webgpsmap] offlinemap: error: {error}")
|
||||
return
|
||||
# elif path.startswith('/newest'):
|
||||
# # returns all positions newer then timestamp
|
||||
# response_data = bytes(json.dumps(self.load_gps_from_dir(self.config['bettercap']['handshakes']), newest_only=True), "utf-8")
|
||||
@ -120,6 +136,8 @@ class Webgpsmap(plugins.Plugin):
|
||||
r = Response(response=response_data, status=response_status, mimetype=response_mimetype)
|
||||
if response_header_contenttype is not None:
|
||||
r.headers["Content-Type"] = response_header_contenttype
|
||||
if response_header_contentdisposition is not None:
|
||||
r.headers["Content-Disposition"] = response_header_contentdisposition
|
||||
return r
|
||||
except Exception as error:
|
||||
logging.error(f"[webgpsmap] error: {error}")
|
||||
|
@ -70,11 +70,11 @@ class WpaSec(plugins.Plugin):
|
||||
"""
|
||||
Gets called when the plugin gets loaded
|
||||
"""
|
||||
if 'api_key' not in self.options or ('api_key' in self.options and self.options['api_key'] is None):
|
||||
if 'api_key' not in self.options or ('api_key' in self.options and not self.options['api_key']):
|
||||
logging.error("WPA_SEC: API-KEY isn't set. Can't upload to wpa-sec.stanev.org")
|
||||
return
|
||||
|
||||
if 'api_url' not in self.options or ('api_url' in self.options and self.options['api_url'] is None):
|
||||
if 'api_url' not in self.options or ('api_url' in self.options and not self.options['api_url']):
|
||||
logging.error("WPA_SEC: API-URL isn't set. Can't upload, no endpoint configured.")
|
||||
return
|
||||
|
||||
|
Reference in New Issue
Block a user