diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml index 4c5568a3..2cdea820 100644 --- a/pwnagotchi/defaults.yml +++ b/pwnagotchi/defaults.yml @@ -39,6 +39,7 @@ main: enabled: false api_key: ~ api_url: "https://wpa-sec.stanev.org" + download_results: false wigle: enabled: false api_key: ~ diff --git a/pwnagotchi/locale/de/LC_MESSAGES/voice.mo b/pwnagotchi/locale/de/LC_MESSAGES/voice.mo index 683dca25..f0885f34 100644 Binary files a/pwnagotchi/locale/de/LC_MESSAGES/voice.mo and b/pwnagotchi/locale/de/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/de/LC_MESSAGES/voice.po b/pwnagotchi/locale/de/LC_MESSAGES/voice.po index bd7d8b36..a7ae8719 100644 --- a/pwnagotchi/locale/de/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/de/LC_MESSAGES/voice.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-10-23 20:56+0200\n" +"POT-Creation-Date: 2019-11-14 21:15+0100\n" "PO-Revision-Date: 2019-09-29 14:00+0200\n" "Last-Translator: dadav <33197631+dadav@users.noreply.github.com>\n" "Language-Team: DE <33197631+dadav@users.noreply.github.com>\n" @@ -20,7 +20,7 @@ msgid "ZzzzZZzzzzZzzz" msgstr "" msgid "Hi, I'm Pwnagotchi! Starting ..." -msgstr "Hi, ich bin ein Pwnagotchi! Starte ..." +msgstr "Hi, ich bin ein Pwnagotchi! Starte..." msgid "New day, new hunt, new pwns!" msgstr "Neuer Tag, neue Jagd, neue Pwns!" @@ -35,23 +35,30 @@ msgid "The neural network is ready." msgstr "Das neurale Netz ist bereit." msgid "Generating keys, do not turn off ..." -msgstr "Generiere Keys, nicht ausschalten ..." +msgstr "Generiere Keys, nicht ausschalten..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." -msgstr "Hey, Channel {channel} ist frei! Dein AP wir des dir danken." +msgstr "Hey, Channel {channel} ist frei! Dein AP wird es Dir danken." + +msgid "Reading last session logs ..." +msgstr "Lese die Logs der letzten Session..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Bisher {lines_so_far} Zeilen im Log gelesen..." msgid "I'm bored ..." msgstr "Mir ist langweilig..." msgid "Let's go for a walk!" -msgstr "Lass uns laufen gehen!" +msgstr "Lass uns spazieren gehen!" msgid "This is the best day of my life!" -msgstr "Das ist der beste Tag meines Lebens." +msgstr "Das ist der beste Tag meines Lebens!" msgid "Shitty day :/" -msgstr "Scheis Tag :/" +msgstr "Scheißtag :/" msgid "I'm extremely bored ..." msgstr "Mir ist sau langweilig..." @@ -62,6 +69,13 @@ msgstr "Ich bin sehr traurig..." msgid "I'm sad" msgstr "Ich bin traurig" +#, fuzzy +msgid "Leave me alone ..." +msgstr "Lass mich in Ruhe..." + +msgid "I'm mad at you!" +msgstr "Ich bin sauer auf Dich!" + msgid "I'm living the life!" msgstr "Ich lebe das Leben!" @@ -75,27 +89,35 @@ msgid "I'm having so much fun!" msgstr "Ich habe sooo viel Spaß!" msgid "My crime is that of curiosity ..." -msgstr "Mein Verbrechen ist das der Neugier ..." +msgstr "Mein Verbrechen ist das der Neugier..." #, python-brace-format msgid "Hello {name}! Nice to meet you." msgstr "Hallo {name}, nett Dich kennenzulernen." +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Jo {name}! Was geht!?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hey {name}, wie geht's?" + #, python-brace-format msgid "Unit {name} is nearby!" -msgstr "Gerät {name} ist in der nähe!!" +msgstr "Gerät {name} ist in der Nähe!" #, python-brace-format msgid "Uhm ... goodbye {name}" -msgstr "Uhm ...tschüß {name}" +msgstr "Uhm... tschüß {name}" #, python-brace-format msgid "{name} is gone ..." -msgstr "{name} ist weg ..." +msgstr "{name} ist weg..." #, python-brace-format msgid "Whoops ... {name} is gone." -msgstr "Whoops ...{name} ist weg." +msgstr "Whoops... {name} ist weg." #, python-brace-format msgid "{name} missed!" @@ -111,17 +133,17 @@ msgid "I love my friends!" msgstr "Ich liebe meine Freunde!" msgid "Nobody wants to play with me ..." -msgstr "Niemand will mit mir spielen ..." +msgstr "Niemand will mit mir spielen..." msgid "I feel so alone ..." -msgstr "Ich fühl michso alleine ..." +msgstr "Ich fühl' mich so allein..." msgid "Where's everybody?!" -msgstr "Wo sind denn alle?" +msgstr "Wo sind denn alle?!" #, python-brace-format msgid "Napping for {secs}s ..." -msgstr "Schlafe für {secs}s" +msgstr "Schlafe für {secs}s..." msgid "Zzzzz" msgstr "" @@ -138,7 +160,7 @@ msgstr "" #, python-brace-format msgid "Waiting for {secs}s ..." -msgstr "Warte für {secs}s ..." +msgstr "Warte für {secs}s..." #, python-brace-format msgid "Looking around ({secs}s)" @@ -158,7 +180,7 @@ msgstr "Jo {what}!" #, python-brace-format msgid "Just decided that {mac} needs no WiFi!" -msgstr "Ich denke, dass {mac} kein WiFi brauch!" +msgstr "Ich denke, dass {mac} kein WiFi braucht!" #, python-brace-format msgid "Deauthenticating {mac}" @@ -177,7 +199,7 @@ msgid "You have {count} new message{plural}!" msgstr "Cool, wir haben {num} neue Handshake{plural}!" msgid "Ops, something went wrong ... Rebooting ..." -msgstr "Ops, da ist etwas schief gelaufen ...Starte neu ..." +msgstr "Ops, da ist was schief gelaufen... Starte neu..." #, python-brace-format msgid "Kicked {num} stations\n" diff --git a/pwnagotchi/locale/ro/LC_MESSAGES/voice.mo b/pwnagotchi/locale/ro/LC_MESSAGES/voice.mo new file mode 100644 index 00000000..09bb9c44 Binary files /dev/null and b/pwnagotchi/locale/ro/LC_MESSAGES/voice.mo differ diff --git a/pwnagotchi/locale/ro/LC_MESSAGES/voice.po b/pwnagotchi/locale/ro/LC_MESSAGES/voice.po new file mode 100644 index 00000000..8f770d4d --- /dev/null +++ b/pwnagotchi/locale/ro/LC_MESSAGES/voice.po @@ -0,0 +1,249 @@ +# Pwnagotchi translation. +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. +# FIRST AUTHOR , 2019. +# +#, +msgid "" +msgstr "" +"Project-Id-Version: 0.0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-11-04 12:57+0100\n" +"PO-Revision-Date: 2019-11-20 00:18+594\n" +"Last-Translator: Ungureanu Radu-Andrei \n" +"Language-Team: pwnagotchi <33197631+dadav@users.noreply.github." +"com>\n" +"Language: ro\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "ZzzzZZzzzzZzzz" +msgstr "" + +msgid "Hi, I'm Pwnagotchi! Starting ..." +msgstr "Buna, sunt Pwnagotchi! Pornesc..." + +msgid "New day, new hunt, new pwns!" +msgstr "O noua zi, o noua vanatoare, noi pwn-uri!" + +msgid "Hack the Planet!" +msgstr "Pirateaza planeta!" + +msgid "AI ready." +msgstr "AI-ul e gata." + +msgid "The neural network is ready." +msgstr "Rețeaua neuronală este gata." + +msgid "Generating keys, do not turn off ..." +msgstr "Se generează chei, nu închide..." + +#, python-brace-format +msgid "Hey, channel {channel} is free! Your AP will say thanks." +msgstr "Hey, canalul {channel} este liber! AP-ul tău îti va mulțumi." + +msgid "Reading last session logs ..." +msgstr "Se citesc log-urile din sesiunea anterioara..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Am citit {lines_so_far} linii din log pana acum..." + +msgid "I'm bored ..." +msgstr "Sunt plictisit..." + +msgid "Let's go for a walk!" +msgstr "Hai să ne plimbăm!" + +msgid "This is the best day of my life!" +msgstr "Asta este cea mai buna zi din viața mea!" + +msgid "Shitty day :/" +msgstr "O zi proasta :/" + +msgid "I'm extremely bored ..." +msgstr "Sunt extrem de plictisit..." + +msgid "I'm very sad ..." +msgstr "Sunt foarte trist..." + +msgid "I'm sad" +msgstr "Sunt trist" + +msgid "Leave me alone ..." +msgstr "Lasă-mă in pace..." + +msgid "I'm mad at you!" +msgstr "Sunt supărat pe tine!" + +msgid "I'm living the life!" +msgstr "Trăiesc viața!" + +msgid "I pwn therefore I am." +msgstr "Eu pwn-ez, deci aici sunt." + +msgid "So many networks!!!" +msgstr "Atât de multe rețele!" + +msgid "I'm having so much fun!" +msgstr "Mă distrez așa de mult!" + +msgid "My crime is that of curiosity ..." +msgstr "Crima mea este una de curiozitate..." + +#, python-brace-format +msgid "Hello {name}! Nice to meet you." +msgstr "Bună {name}! Mă bucur să te cunosc." + +#, python-brace-format +msgid "Yo {name}! Sup?" +msgstr "Yo {name}! Cmf?" + +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Hey {nume} ce mai faci?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Unitatea {name} este aproape!" + +#, python-brace-format +msgid "Uhm ... goodbye {name}" +msgstr "Uhm... Pa {name}" + +#, python-brace-format +msgid "{name} is gone ..." +msgstr "{name} a dispărut." + +#, python-brace-format +msgid "Whoops ... {name} is gone." +msgstr "Oops... {name} a dispărut." + +#, python-brace-format +msgid "{name} missed!" +msgstr "{name} ratat!" + +msgid "Missed!" +msgstr "Ratat!" + +msgid "Good friends are a blessing!" +msgstr "Prietenii buni sunt o binecuvântare!" + +msgid "I love my friends!" +msgstr "Îmi iubesc prietenii!" + +msgid "Nobody wants to play with me ..." +msgstr "Nimeni nu vrea sa se joace cu mine..." + +msgid "I feel so alone ..." +msgstr "Mă simt așa de singuratic..." + +msgid "Where's everybody?!" +msgstr "Unde-i toată lumea?!" + +#, python-brace-format +msgid "Napping for {secs}s ..." +msgstr "Dorm pentru {secs}s..." + +msgid "Zzzzz" +msgstr "Zzzzz" + +#, python-brace-format +msgid "ZzzZzzz ({secs}s)" +msgstr "ZzzZzzz ({secs}s)" + +msgid "Good night." +msgstr "Noapte bună." + +msgid "Zzz" +msgstr "Zzz" + +#, python-brace-format +msgid "Waiting for {secs}s ..." +msgstr "Aștept pentru {secs}s..." + +#, python-brace-format +msgid "Looking around ({secs}s)" +msgstr "Mă uit împrejur ({secs}s)" + +#, python-brace-format +msgid "Hey {what} let's be friends!" +msgstr "Hey {what} hai să fim prieteni!" + +#, python-brace-format +msgid "Associating to {what}" +msgstr "Mă asociez cu {what}" + +#, python-brace-format +msgid "Yo {what}!" +msgstr "Yo {what}" + +#, python-brace-format +msgid "Just decided that {mac} needs no WiFi!" +msgstr "Am decis că lui {mac} nu-i trebuie WiFi!" + +#, python-brace-format +msgid "Deauthenticating {mac}" +msgstr "Îl deautentific pe {mac}" + +#, python-brace-format +msgid "Kickbanning {mac}!" +msgstr "Îi dau kickban lui {mac}" + +#, python-brace-format +msgid "Cool, we got {num} new handshake{plural}!" +msgstr "Șmecher, avem {num} de handshake-uri noi!" + +#, python-brace-format +msgid "You have {count} new message{plural}!" +msgstr "Ai {count} mesaj(e) nou/noi!" + +msgid "Ops, something went wrong ... Rebooting ..." +msgstr "OOps, ceva s-a întamplat... Îmi dau reboot...+" + +#, python-brace-format +msgid "Kicked {num} stations\n" +msgstr "Am dat afară {num} de stații\n" + +#, python-brace-format +msgid "Made {num} new friends\n" +msgstr "Am făcut {num} prieteni noi \n" + +#, python-brace-format +msgid "Got {num} handshakes\n" +msgstr "Am primit {num} de handshake-uri\n" + +msgid "Met 1 peer" +msgstr "Am întalnit un peer" + +#, python-brace-format +msgid "Met {num} peers" +msgstr "Am întalnit {num} de peer-uri" + +#, python-brace-format +msgid "" +"I've been pwning for {duration} and kicked {deauthed} clients! I've also met " +"{associated} new friends and ate {handshakes} handshakes! #pwnagotchi " +"#pwnlog #pwnlife #hacktheplanet #skynet" +msgstr "Eu am făcut pwning pentru {duration} și am dat afara {deauthed} clienți! " +"De asemenea, am întalnit {associated} prieteni noi și am mancat {handshakes} de " +"handshake-uri! #pwnagotchi #pwnlog #pwnlife #hacktheplanet #skynet" + +msgid "hours" +msgstr "ore" + +msgid "minutes" +msgstr "minute" + +msgid "seconds" +msgstr "secunde" + +msgid "hour" +msgstr "oră" + +msgid "minute" +msgstr "minut" + +msgid "second" +msgstr "secundă" diff --git a/pwnagotchi/locale/ru/LC_MESSAGES/voice.po b/pwnagotchi/locale/ru/LC_MESSAGES/voice.po index 146527f0..4103dae2 100644 --- a/pwnagotchi/locale/ru/LC_MESSAGES/voice.po +++ b/pwnagotchi/locale/ru/LC_MESSAGES/voice.po @@ -1,45 +1,51 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. +# Pwnagotchi Russian translation. +# Copyright (C) 2019 +# This file is distributed under the same license as the pwnagotchi package. # FIRST AUTHOR <25989971+adolfaka@users.noreply.github.com>, 2019. -# +# Second author , 2019 msgid "" msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-10-03 16:47+0200\n" -"PO-Revision-Date: 2019-10-05 18:50+0300\n" -"Language-Team: \n" +"Project-Id-Version: 0.0.2" +"Report-Msgid-Bugs-To: m-b-g@yandex.ru" +"POT-Creation-Date: 2019-11-27 16:47+0200" +"PO-Revision-Date: 2019-11-27 18:50+0300" +"Last-Translator: Evgeny Zelenin " +"Language-Team: ru" +"Language: ru" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.2.4\n" -"Last-Translator: Elliot Manson\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" -"Language: ru_RU\n" + msgid "ZzzzZZzzzzZzzz" -msgstr "ZzzzZZzzzzZzzz" +msgstr "Хрррр..." msgid "Hi, I'm Pwnagotchi! Starting ..." -msgstr "Привет, я Pwnagotchi! Поехали …" +msgstr "Привет, я Pwnagotchi! Стартуем!" msgid "New day, new hunt, new pwns!" msgstr "Новый день, новая охота, новые взломы!" msgid "Hack the Planet!" -msgstr "Хак зе планет!" +msgstr "Взломай эту Планету!" msgid "AI ready." -msgstr "AI готов." +msgstr "A.I. готов." msgid "The neural network is ready." msgstr "Нейронная сеть готова." +msgid "Generating keys, do not turn off ..." +msgstr "Генерация ключей, не выключайте..." #, python-brace-format msgid "Hey, channel {channel} is free! Your AP will say thanks." msgstr "Эй, канал {channel} свободен! Ваша точка доступа скажет спасибо." +msgid "Reading last session logs ..." +msgstr "Чтение логов последнего сеанса..." + +#, python-brace-format +msgid "Read {lines_so_far} log lines so far ..." +msgstr "Чтение {lines_so_far} строк журнала..." msgid "I'm bored ..." msgstr "Мне скучно …" @@ -61,9 +67,14 @@ msgstr "Мне очень грустно …" msgid "I'm sad" msgstr "Мне грустно" +msgid "Leave me alone ..." +msgstr "Оставь меня в покое..." + +msgid "I'm mad at you!" +msgstr "Я зол на тебя!" msgid "I'm living the life!" -msgstr "Угараю по полной!" +msgstr "Живу полной жизнью!" msgid "I pwn therefore I am." msgstr "Я взламываю, поэтому я существую." @@ -75,15 +86,22 @@ msgid "I'm having so much fun!" msgstr "Мне так весело!" msgid "My crime is that of curiosity ..." -msgstr "Моe преступление - это любопытство …" +msgstr "Моё преступление - это любопытство…" #, python-brace-format msgid "Hello {name}! Nice to meet you. {name}" -msgstr "Привет, {name}! Приятно познакомиться. {name}" +msgstr "Привет, {name}! Рад встрече с тобой!" #, python-brace-format msgid "Unit {name} is nearby! {name}" -msgstr "Цель {name} близко! {name}" +msgstr "Цель {name} близко!" +#, python-brace-format +msgid "Hey {name} how are you doing?" +msgstr "Хэй {nume}! Как дела?" + +#, python-brace-format +msgid "Unit {name} is nearby!" +msgstr "Цель {name} рядом!" #, python-brace-format msgid "Uhm ... goodbye {name}" @@ -91,11 +109,11 @@ msgstr "Хм … до свидания {name}" #, python-brace-format msgid "{name} is gone ..." -msgstr "{name} исчезла …" +msgstr "{name} ушла…" #, python-brace-format msgid "Whoops ... {name} is gone." -msgstr "Упс … {name} исчезла." +msgstr "Упс… {name} исчезла." #, python-brace-format msgid "{name} missed!" @@ -103,12 +121,17 @@ msgstr "{name} упустил!" msgid "Missed!" msgstr "Промахнулся!" +msgid "Good friends are a blessing!" +msgstr "Хорошие друзья - это благословение!" + +msgid "I love my friends!" +msgstr "Я люблю своих друзей!" msgid "Nobody wants to play with me ..." msgstr "Никто не хочет со мной играть ..." msgid "I feel so alone ..." -msgstr "Мне так одиноко …" +msgstr "Я так одинок…" msgid "Where's everybody?!" msgstr "Где все?!" @@ -118,11 +141,16 @@ msgid "Napping for {secs}s ..." msgstr "Дремлет {secs}с …" msgid "Zzzzz" -msgstr "Zzzzz" +msgstr "Хррр..." #, python-brace-format msgid "ZzzZzzz ({secs}s)" -msgstr "ZzzZzzz ({secs}c)" +msgstr "Хррррр.. ({secs}c)" +msgid "Good night." +msgstr "Доброй ночи." + +msgid "Zzz" +msgstr "Хрррр" #, python-brace-format msgid "Waiting for {secs}s ..." @@ -130,7 +158,7 @@ msgstr "Ждем {secs}c …" #, python-brace-format msgid "Looking around ({secs}s)" -msgstr "Оглядываюсь вокруг ({secs}с)" +msgstr "Осматриваюсь вокруг ({secs}с)" #, python-brace-format msgid "Hey {what} let's be friends!" @@ -173,7 +201,7 @@ msgstr "Заимел {num} новых друзей\n" #, python-brace-format msgid "Got {num} handshakes\n" -msgstr "Получил {num} рукопожатие\n" +msgstr "Получил {num} рукопожатий\n" msgid "Met 1 peer" msgstr "Встретился один знакомый" diff --git a/pwnagotchi/locale/voice.pot b/pwnagotchi/locale/voice.pot index c72d1316..93f75b9b 100644 --- a/pwnagotchi/locale/voice.pot +++ b/pwnagotchi/locale/voice.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-04 12:57+0100\n" +"POT-Creation-Date: 2019-11-18 18:29+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/pwnagotchi/plugins/__init__.py b/pwnagotchi/plugins/__init__.py index 44c27879..c5efaf7d 100644 --- a/pwnagotchi/plugins/__init__.py +++ b/pwnagotchi/plugins/__init__.py @@ -6,7 +6,7 @@ import logging default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default") loaded = {} - +database = {} class Plugin: @classmethod @@ -18,6 +18,26 @@ class Plugin: logging.debug("loaded plugin %s as %s" % (plugin_name, plugin_instance)) loaded[plugin_name] = plugin_instance +def toggle_plugin(name, enable=True): + """ + Load or unload a plugin + + returns True if changed, otherwise False + """ + global loaded, database + if not enable and name in loaded: + if getattr(loaded[name], 'on_unload', None): + loaded[name].on_unload() + del loaded[name] + return True + + if enable and name in database and name not in loaded: + load_from_file(database[name]) + one(name, 'loaded') + return True + + return False + def on(event_name, *args, **kwargs): for plugin_name, plugin in loaded.items(): @@ -48,10 +68,11 @@ def load_from_file(filename): def load_from_path(path, enabled=()): - global loaded + global loaded, database logging.debug("loading plugins from %s - enabled: %s" % (path, enabled)) for filename in glob.glob(os.path.join(path, "*.py")): plugin_name = os.path.basename(filename.replace(".py", "")) + database[plugin_name] = filename if plugin_name in enabled: try: load_from_file(filename) diff --git a/pwnagotchi/plugins/default/bt-tether.py b/pwnagotchi/plugins/default/bt-tether.py index 6acaf270..5f986f84 100644 --- a/pwnagotchi/plugins/default/bt-tether.py +++ b/pwnagotchi/plugins/default/bt-tether.py @@ -466,7 +466,11 @@ class BTTether(plugins.Plugin): logging.info("BT-TETHER: Successfully loaded ...") self.ready = True + def on_unload(self): + self.ui.remove_element('bluetooth') + def on_ui_setup(self, ui): + self.ui = ui ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0), label_font=fonts.Bold, text_font=fonts.Medium)) diff --git a/pwnagotchi/plugins/default/example.py b/pwnagotchi/plugins/default/example.py index 035a30d5..58fe4488 100644 --- a/pwnagotchi/plugins/default/example.py +++ b/pwnagotchi/plugins/default/example.py @@ -25,6 +25,10 @@ class Example(plugins.Plugin): def on_loaded(self): logging.warning("WARNING: this plugin should be disabled! options = " % self.options) + # called before the plugin is unloaded + def on_unload(self): + pass + # called hen there's internet connectivity def on_internet_available(self, agent): pass @@ -118,6 +122,11 @@ class Example(plugins.Plugin): def on_wifi_update(self, agent, access_points): pass + # called when the agent refreshed an unfiltered access point list + # this list contains all access points that were detected BEFORE filtering + def on_unfiltered_ap_list(self, agent, access_points): + pass + # called when the agent is sending an association frame def on_association(self, agent, access_point): pass diff --git a/pwnagotchi/plugins/default/gpio_buttons.py b/pwnagotchi/plugins/default/gpio_buttons.py index 10075efe..cec7cf79 100644 --- a/pwnagotchi/plugins/default/gpio_buttons.py +++ b/pwnagotchi/plugins/default/gpio_buttons.py @@ -32,6 +32,7 @@ class GPIOButtons(plugins.Plugin): GPIO.setmode(GPIO.BCM) for gpio, command in gpios.items(): + gpio = int(gpio) self.ports[gpio] = command GPIO.setup(gpio, GPIO.IN, GPIO.PUD_UP) GPIO.add_event_detect(gpio, GPIO.FALLING, callback=self.runCommand, bouncetime=600) diff --git a/pwnagotchi/plugins/default/grid.py b/pwnagotchi/plugins/default/grid.py index a727e249..23181aee 100644 --- a/pwnagotchi/plugins/default/grid.py +++ b/pwnagotchi/plugins/default/grid.py @@ -67,7 +67,8 @@ class Grid(plugins.Plugin): logging.info("grid plugin loaded.") def set_reported(self, reported, net_id): - reported.append(net_id) + if net_id not in reported: + reported.append(net_id) self.report.update(data={'reported': reported}) def check_inbox(self, agent): diff --git a/pwnagotchi/plugins/default/paw-gps.py b/pwnagotchi/plugins/default/paw-gps.py index 0b477d7d..e92805b2 100644 --- a/pwnagotchi/plugins/default/paw-gps.py +++ b/pwnagotchi/plugins/default/paw-gps.py @@ -4,10 +4,7 @@ import pwnagotchi.plugins as plugins ''' You need an bluetooth connection to your android phone which is running PAW server with the GPS "hack" from Systemik and edited by shaynemk -NEW BETTER GUIDE HERE: https://community.pwnagotchi.ai/t/setting-up-paw-gps-on-android - -Old guide here, (not recommended if you plan on using it with the webgpsmap plugin) -https://raw.githubusercontent.com/systemik/pwnagotchi-bt-tether/master/GPS-via-PAW +GUIDE HERE: https://community.pwnagotchi.ai/t/setting-up-paw-gps-on-android ''' @@ -30,7 +27,7 @@ class PawGPS(plugins.Plugin): ip = self.options['ip'] gps = requests.get('http://' + ip + '/gps.xhtml') - gps_filename = filename.replace('.pcap', '.gps.json') + gps_filename = filename.replace('.pcap', '.paw-gps.json') logging.info("saving GPS to %s (%s)" % (gps_filename, gps)) with open(gps_filename, 'w+t') as f: diff --git a/pwnagotchi/plugins/default/webcfg.py b/pwnagotchi/plugins/default/webcfg.py index 52532f2c..40f74772 100644 --- a/pwnagotchi/plugins/default/webcfg.py +++ b/pwnagotchi/plugins/default/webcfg.py @@ -500,8 +500,9 @@ 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(request.get_json(), config_file, encoding='utf-8', + yaml.safe_dump(parsed_yaml, config_file, encoding='utf-8', allow_unicode=True, default_flow_style=False) _thread.start_new_thread(restart, (self.mode,)) diff --git a/pwnagotchi/plugins/default/webgpsmap.html b/pwnagotchi/plugins/default/webgpsmap.html index 13bfacbb..6c401ec1 100644 --- a/pwnagotchi/plugins/default/webgpsmap.html +++ b/pwnagotchi/plugins/default/webgpsmap.html @@ -9,7 +9,7 @@
+
+ +
0 APs
+
(⌐■  ■)
loading positions...
- \ No newline at end of file + diff --git a/pwnagotchi/plugins/default/webgpsmap.py b/pwnagotchi/plugins/default/webgpsmap.py index fc8cedc9..85479f9b 100644 --- a/pwnagotchi/plugins/default/webgpsmap.py +++ b/pwnagotchi/plugins/default/webgpsmap.py @@ -9,7 +9,7 @@ from functools import lru_cache ''' 2do: - - make the cache handling multiple clients + - make+test the cache handling multiple clients - cleanup the javascript in a class and handle "/newest" additions - create map filters (only cracked APs, only last xx days, between 2 days with slider) http://www.gistechsolutions.com/leaflet/DEMO/filter/filter.html @@ -22,16 +22,10 @@ from functools import lru_cache class Webgpsmap(plugins.Plugin): __author__ = 'https://github.com/xenDE and https://github.com/dadav' - __version__ = '1.2.2' + __version__ = '1.3.0' __name__ = 'webgpsmap' __license__ = 'GPL3' __description__ = 'a plugin for pwnagotchi that shows a openstreetmap with positions of ap-handshakes in your webbrowser' - __help__ = """ -- install: copy "webgpsmap.py" and "webgpsmap.html" to your configured "custom_plugins" directory -- add webgpsmap.yml to your config -- connect your PC/Smartphone/* with USB, BT or other to your pwnagotchi and browse to http://pwnagotchi.local:8080/plugins/webgpsmap/ - (change pwnagotchi.local to your pwnagotchis IP, if needed) -""" ALREADY_SENT = list() SKIP = list() @@ -47,7 +41,7 @@ class Webgpsmap(plugins.Plugin): """ Plugin got loaded """ - logging.info("webgpsmap plugin loaded") + logging.info("[webgpsmap]: plugin loaded") def on_webhook(self, path, request): """ @@ -68,8 +62,8 @@ class Webgpsmap(plugins.Plugin): response_status = 500 response_mimetype = "application/xhtml+xml" response_header_contenttype = 'text/html' - except Exception as ex: - logging.error(ex) + except Exception as error: + logging.error(f"[webgpsmap] error: {error}") return else: if request.method == "GET": @@ -78,8 +72,8 @@ class Webgpsmap(plugins.Plugin): self.ALREADY_SENT = list() try: response_data = bytes(self.get_html(), "utf-8") - except Exception as ex: - logging.error(ex) + except Exception as error: + logging.error(f"[webgpsmap] error: {error}") return response_status = 200 response_mimetype = "application/xhtml+xml" @@ -92,8 +86,8 @@ class Webgpsmap(plugins.Plugin): response_status = 200 response_mimetype = "application/json" response_header_contenttype = 'application/json' - except Exception as ex: - logging.error(ex) + except Exception as error: + logging.error(f"[webgpsmap] error: {error}") return # elif path.startswith('/newest'): # # returns all positions newer then timestamp @@ -118,7 +112,7 @@ class Webgpsmap(plugins.Plugin): - 4😋4 + 4😋4 for bad boys ''', "utf-8") response_status = 404 try: @@ -126,12 +120,12 @@ class Webgpsmap(plugins.Plugin): if response_header_contenttype is not None: r.headers["Content-Type"] = response_header_contenttype return r - except Exception as ex: - logging.error(ex) + except Exception as error: + logging.error(f"[webgpsmap] error: {error}") return - # cache 1024 items - @lru_cache(maxsize=1024, typed=False) + # cache 2048 items + @lru_cache(maxsize=2048, typed=False) def _get_pos_from_file(self, path): return PositionFile(path) @@ -144,7 +138,7 @@ class Webgpsmap(plugins.Plugin): handshake_dir = gpsdir gps_data = dict() - logging.info("webgpsmap: scanning %s", handshake_dir) + logging.info(f"[webgpsmap] scanning {handshake_dir}") all_files = os.listdir(handshake_dir) @@ -156,33 +150,40 @@ class Webgpsmap(plugins.Plugin): all_geo_or_gps_files = [] for filename_pcap in all_pcap_files: filename_base = filename_pcap[:-5] # remove ".pcap" - logging.debug("webgpsmap: found: " + filename_base) + logging.debug(f"[webgpsmap] found: {filename_base}") filename_position = None + logging.debug("[webgpsmap] search for .gps.json") check_for = os.path.basename(filename_base) + ".gps.json" if check_for in all_files: filename_position = str(os.path.join(handshake_dir, check_for)) + logging.debug("[webgpsmap] search for .geo.json") check_for = os.path.basename(filename_base) + ".geo.json" if check_for in all_files: filename_position = str(os.path.join(handshake_dir, check_for)) + logging.debug("[webgpsmap] search for .paw-gps.json") + check_for = os.path.basename(filename_base) + ".paw-gps.json" + if check_for in all_files: + filename_position = str(os.path.join(handshake_dir, check_for)) + + logging.debug(f"[webgpsmap] end search for position data files and use {filename_position}") + if filename_position is not None: - # logging.debug("webgpsmap: -- found: %s %d" % (check_for, len(all_geo_or_gps_files)) ) all_geo_or_gps_files.append(filename_position) - # all_geo_or_gps_files = set(all_geo_or_gps_files) - set(SKIP) # remove skiped networks? No! + # all_geo_or_gps_files = set(all_geo_or_gps_files) - set(SKIP) # remove skipped networks? No! if newest_only: all_geo_or_gps_files = set(all_geo_or_gps_files) - set(self.ALREADY_SENT) - logging.info("webgpsmap: Found %d .(geo|gps).json files from %d handshakes. Fetching positions ...", - len(all_geo_or_gps_files), len(all_pcap_files)) + logging.info(f"[webgpsmap] Found {len(all_geo_or_gps_files)} position-data files from {len(all_pcap_files)} handshakes. Fetching positions ...") for pos_file in all_geo_or_gps_files: try: pos = self._get_pos_from_file(pos_file) - if not pos.type() == PositionFile.GPS and not pos.type() == PositionFile.GEO: + if not pos.type() == PositionFile.GPS and not pos.type() == PositionFile.GEO and not pos.type() == PositionFile.PAWGPS: continue ssid, mac = pos.ssid(), pos.mac() @@ -190,10 +191,17 @@ class Webgpsmap(plugins.Plugin): # invalid mac is strange and should abort; ssid is ok if not mac: raise ValueError("Mac can't be parsed from filename") + pos_type = 'unknown' + if pos.type() == PositionFile.GPS: + pos_type = 'gps' + elif pos.type() == PositionFile.GEO: + pos_type = 'geo' + elif pos.type() == PositionFile.PAWGPS: + pos_type = 'paw' gps_data[ssid+"_"+mac] = { 'ssid': ssid, 'mac': mac, - 'type': 'gps' if pos.type() == PositionFile.GPS else 'geo', + 'type': pos_type, 'lng': pos.lng(), 'lat': pos.lat(), 'acc': pos.accuracy(), @@ -201,24 +209,25 @@ class Webgpsmap(plugins.Plugin): 'ts_last': pos.timestamp_last(), } + # get ap password if exist check_for = os.path.basename(pos_file[:-9]) + ".pcap.cracked" if check_for in all_files: gps_data[ssid + "_" + mac]["pass"] = pos.password() self.ALREADY_SENT += pos_file - except json.JSONDecodeError as js_e: + except json.JSONDecodeError as error: self.SKIP += pos_file - logging.error(js_e) + logging.error(f"[webgpsmap] JSONDecodeError in: {error}") continue - except ValueError as v_e: + except ValueError as error: self.SKIP += pos_file - logging.error(v_e) + logging.error(f"[webgpsmap] ValueError: {error}") continue - except OSError as os_e: + except OSError as error: self.SKIP += pos_file - logging.error(os_e) + logging.error(f"[webgpsmap] OSError: {error}") continue - logging.info("webgpsmap loaded %d positions", len(gps_data)) + logging.info(f"[webgpsmap] loaded {len(gps_data)} positions") return gps_data def get_html(self): @@ -226,11 +235,10 @@ class Webgpsmap(plugins.Plugin): Returns the html page """ try: - template_file = os.path.dirname(os.path.realpath(__file__))+"/"+"webgpsmap.html" + template_file = os.path.dirname(os.path.realpath(__file__)) + "/" + "webgpsmap.html" html_data = open(template_file, "r").read() - except Exception as ex: - logging.error("error loading template file: %s", template_file) - logging.error(ex) + except Exception as error: + logging.error(f"[webgpsmap] error loading template file {template_file} - error: {error}") return html_data @@ -238,15 +246,18 @@ class PositionFile: """ Wraps gps / net-pos files """ - GPS = 0 - GEO = 1 + GPS = 1 + GEO = 2 + PAWGPS = 3 def __init__(self, path): self._file = path self._filename = os.path.basename(path) try: + logging.debug(f"[webgpsmap] loading {path}") with open(path, 'r') as json_file: self._json = json.load(json_file) + logging.debug(f"[webgpsmap] loaded {path}") except json.JSONDecodeError as js_e: raise js_e @@ -254,7 +265,7 @@ class PositionFile: """ Returns the mac from filename """ - parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo)\.json', self._filename) + parsed_mac = re.search(r'.*_?([a-zA-Z0-9]{12})\.(?:gps|geo|paw-gps)\.json', self._filename) if parsed_mac: mac = parsed_mac.groups()[0] return mac @@ -264,7 +275,7 @@ class PositionFile: """ Returns the ssid from filename """ - parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo)\.json', self._filename) + parsed_ssid = re.search(r'(.+)_[a-zA-Z0-9]{12}\.(?:gps|geo|paw-gps)\.json', self._filename) if parsed_ssid: return parsed_ssid.groups()[0] return None @@ -293,32 +304,37 @@ class PositionFile: elif 'Updated' in self._json: # convert gps datetime to unix timestamp: "2019-10-05T23:12:40.422996+01:00" date_iso_formated = self._json['Updated'] - # fill milliseconds to 6 numbers + # bad microseconds fix: fill/cut microseconds to 6 numbers part1, part2, part3 = re.split('\.|\+', date_iso_formated) - part2 = part2.ljust(6, '0') + part2 = part2.ljust(6, '0')[:6] + # timezone fix: 0200 >>> 02:00 + if len(part3) == 4: + part3 = part3[1:2].rjust(2, '0') + ':' + part3[3:4].rjust(2, '0') date_iso_formated = part1 + "." + part2 + "+" + part3 dateObj = datetime.datetime.fromisoformat(date_iso_formated) return_ts = int("%.0f" % dateObj.timestamp()) else: - # use file timestamp last modification of the pcap file + # use file timestamp last modification of the json file return_ts = int("%.0f" % os.path.getmtime(self._file)) return return_ts def password(self): """ - returns the password from file.pcap.cracked od None + returns the password from file.pcap.cracked or None """ return_pass = None - password_file_path = self._file[:-9] + ".pcap.cracked" + # 2do: make better filename split/remove extension because this one has problems with "." in path + base_filename, ext1, ext2 = re.split('\.', self._file) + password_file_path = base_filename + ".pcap.cracked" if os.path.isfile(password_file_path): try: password_file = open(password_file_path, 'r') return_pass = password_file.read() password_file.close() - except OSError as err: - print("OS error: {0}".format(err)) + except OSError as error: + logging.error(f"[webgpsmap] OS error: {format(error)}") except: - print("Unexpected error:", sys.exc_info()[0]) + logging.error(f"[webgpsmap] Unexpected error: {sys.exc_info()[0]}") raise return return_pass @@ -330,37 +346,57 @@ class PositionFile: return PositionFile.GPS if self._file.endswith('.geo.json'): return PositionFile.GEO + if self._file.endswith('.paw-gps.json'): + return PositionFile.PAWGPS return None def lat(self): try: - if self.type() == PositionFile.GPS: + lat = None + # try to get value from known formats + if 'Latitude' in self._json: lat = self._json['Latitude'] - if self.type() == PositionFile.GEO: - lat = self._json['location']['lat'] - if lat != 0: - return lat - raise ValueError("Lat is 0") + if 'lat' in self._json: + lat = self._json['lat'] # an old paw-gps format: {"long": 14.693561, "lat": 40.806375} + if 'location' in self._json: + if 'lat' in self._json['location']: + lat = self._json['location']['lat'] + # check value + if lat is None: + raise ValueError(f"Lat is None in {self._filename}") + if lat == 0: + raise ValueError(f"Lat is 0 in {self._filename}") + return lat except KeyError: pass return None def lng(self): try: - if self.type() == PositionFile.GPS: + lng = None + # try to get value from known formats + if 'Longitude' in self._json: lng = self._json['Longitude'] - if self.type() == PositionFile.GEO: - lng = self._json['location']['lng'] - if lng != 0: - return lng - raise ValueError("Lng is 0") + if 'long' in self._json: + lng = self._json['long'] # an old paw-gps format: {"long": 14.693561, "lat": 40.806375} + if 'location' in self._json: + if 'lng' in self._json['location']: + lng = self._json['location']['lng'] + # check value + if lng is None: + raise ValueError(f"Lng is None in {self._filename}") + if lng == 0: + raise ValueError(f"Lng is 0 in {self._filename}") + return lng except KeyError: pass return None def accuracy(self): if self.type() == PositionFile.GPS: - return 50.0 + return 50.0 # a default + if self.type() == PositionFile.PAWGPS: + return 50.0 # a default if self.type() == PositionFile.GEO: try: return self._json['accuracy'] diff --git a/pwnagotchi/plugins/default/wpa-sec.py b/pwnagotchi/plugins/default/wpa-sec.py index ee99f105..7dd8a844 100644 --- a/pwnagotchi/plugins/default/wpa-sec.py +++ b/pwnagotchi/plugins/default/wpa-sec.py @@ -1,19 +1,27 @@ import os import logging +import threading import requests +from datetime import datetime from pwnagotchi.utils import StatusFile -import pwnagotchi.plugins as plugins +from pwnagotchi import plugins +from json.decoder import JSONDecodeError class WpaSec(plugins.Plugin): __author__ = '33197631+dadav@users.noreply.github.com' - __version__ = '2.0.1' + __version__ = '2.1.0' __license__ = 'GPL3' __description__ = 'This plugin automatically uploads handshakes to https://wpa-sec.stanev.org' def __init__(self): self.ready = False - self.report = StatusFile('/root/.wpa_sec_uploads', data_format='json') + self.lock = threading.Lock() + try: + self.report = StatusFile('/root/.wpa_sec_uploads', data_format='json') + except JSONDecodeError as json_err: + os.remove("/root/.wpa_sec_uploads") + self.report = StatusFile('/root/.wpa_sec_uploads', data_format='json') self.options = dict() self.skip = list() @@ -35,6 +43,29 @@ class WpaSec(plugins.Plugin): except requests.exceptions.RequestException as req_e: raise req_e + + def _download_from_wpasec(self, output, timeout=30): + """ + Downloads the results from wpasec and safes them to output + + Output-Format: bssid, station_mac, ssid, password + """ + api_url = self.options['api_url'] + if not api_url.endswith('/'): + api_url = f"{api_url}/" + api_url = f"{api_url}?api&dl=1" + + cookie = {'key': self.options['api_key']} + try: + result = requests.get(api_url, cookies=cookie, timeout=timeout) + with open(output, 'wb') as output_file: + output_file.write(result.content) + except requests.exceptions.RequestException as req_e: + raise req_e + except OSError as os_e: + raise os_e + + def on_loaded(self): """ Gets called when the plugin gets loaded @@ -53,32 +84,48 @@ class WpaSec(plugins.Plugin): """ Called in manual mode when there's internet connectivity """ - if self.ready: - config = agent.config() - display = agent.view() - reported = self.report.data_field_or('reported', default=list()) + with self.lock: + if self.ready: + config = agent.config() + display = agent.view() + reported = self.report.data_field_or('reported', default=list()) - handshake_dir = config['bettercap']['handshakes'] - handshake_filenames = os.listdir(handshake_dir) - handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if - filename.endswith('.pcap')] - handshake_new = set(handshake_paths) - set(reported) - set(self.skip) + handshake_dir = config['bettercap']['handshakes'] + handshake_filenames = os.listdir(handshake_dir) + handshake_paths = [os.path.join(handshake_dir, filename) for filename in handshake_filenames if + filename.endswith('.pcap')] + handshake_new = set(handshake_paths) - set(reported) - set(self.skip) - if handshake_new: - logging.info("WPA_SEC: Internet connectivity detected. Uploading new handshakes to wpa-sec.stanev.org") + if handshake_new: + logging.info("WPA_SEC: Internet connectivity detected. Uploading new handshakes to wpa-sec.stanev.org") + + for idx, handshake in enumerate(handshake_new): + display.set('status', f"Uploading handshake to wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})") + display.update(force=True) + try: + self._upload_to_wpasec(handshake) + reported.append(handshake) + self.report.update(data={'reported': reported}) + logging.info("WPA_SEC: Successfully uploaded %s", handshake) + except requests.exceptions.RequestException as req_e: + self.skip.append(handshake) + logging.error("WPA_SEC: %s", req_e) + continue + except OSError as os_e: + logging.error("WPA_SEC: %s", os_e) + continue + + if 'download_results' in self.options and self.options['download_results']: + cracked_file = os.path.join(handshake_dir, 'wpa-sec.cracked.potfile') + if os.path.exists(cracked_file): + last_check = datetime.fromtimestamp(os.path.getmtime(cracked_file)) + if last_check is not None and ((datetime.now() - last_check).seconds / (60 * 60)) < 1: + return - for idx, handshake in enumerate(handshake_new): - display.set('status', f"Uploading handshake to wpa-sec.stanev.org ({idx + 1}/{len(handshake_new)})") - display.update(force=True) try: - self._upload_to_wpasec(handshake) - reported.append(handshake) - self.report.update(data={'reported': reported}) - logging.info("WPA_SEC: Successfully uploaded %s", handshake) + self._download_from_wpasec(os.path.join(handshake_dir, 'wpa-sec.cracked.potfile')) + logging.info("WPA_SEC: Downloaded cracked passwords.") except requests.exceptions.RequestException as req_e: - self.skip.append(handshake) - logging.error("WPA_SEC: %s", req_e) - continue + logging.debug("WPA_SEC: %s", req_e) except OSError as os_e: - logging.error("WPA_SEC: %s", os_e) - continue + logging.debug("WPA_SEC: %s", os_e) diff --git a/pwnagotchi/ui/web/handler.py b/pwnagotchi/ui/web/handler.py index c11b1731..f8552532 100644 --- a/pwnagotchi/ui/web/handler.py +++ b/pwnagotchi/ui/web/handler.py @@ -34,6 +34,7 @@ class Handler: self._app.add_url_rule('/ui', 'ui', self.with_auth(self.ui)) self._app.add_url_rule('/shutdown', 'shutdown', self.with_auth(self.shutdown), methods=['POST']) + self._app.add_url_rule('/reboot', 'reboot', self.with_auth(self.reboot), methods=['POST']) self._app.add_url_rule('/restart', 'restart', self.with_auth(self.restart), methods=['POST']) # inbox @@ -179,16 +180,19 @@ class Handler: def plugins(self, name, subpath): if name is None: - # show plugins overview - abort(404) + return render_template('plugins.html', loaded=plugins.loaded, database=plugins.database) + + if name == 'toggle' and request.method == 'POST': + checked = True if 'enabled' in request.form else False + return 'success' if plugins.toggle_plugin(request.form['plugin'], checked) else 'failed' + + if name in plugins.loaded and plugins.loaded[name] is not None and hasattr(plugins.loaded[name], 'on_webhook'): + try: + return plugins.loaded[name].on_webhook(subpath, request) + except Exception: + abort(500) else: - if name in plugins.loaded and hasattr(plugins.loaded[name], 'on_webhook'): - try: - return plugins.loaded[name].on_webhook(subpath, request) - except Exception: - abort(500) - else: - abort(404) + abort(404) # serve a message and shuts down the unit def shutdown(self): @@ -198,6 +202,14 @@ class Handler: finally: _thread.start_new_thread(pwnagotchi.shutdown, ()) + # serve a message and reboot the unit + def reboot(self): + try: + return render_template('status.html', title=pwnagotchi.name(), go_back_after=60, + message='Rebooting ...') + finally: + _thread.start_new_thread(pwnagotchi.reboot, ()) + # serve a message and restart the unit in the other mode def restart(self): mode = request.form['mode'] diff --git a/pwnagotchi/ui/web/static/css/style.css b/pwnagotchi/ui/web/static/css/style.css index 8acc2677..a005a1d6 100644 --- a/pwnagotchi/ui/web/static/css/style.css +++ b/pwnagotchi/ui/web/static/css/style.css @@ -31,4 +31,37 @@ a.read { p.messagebody { padding: 1em; -} \ No newline at end of file +} + +li.navitem { + width: 16.66% !important; + clear: none !important; +} + +/* Custom indentations are needed because the length of custom labels differs from + the length of the standard labels */ +.custom-size-flipswitch.ui-flipswitch .ui-btn.ui-flipswitch-on { + text-indent: -5.9em; +} + +.custom-size-flipswitch.ui-flipswitch .ui-flipswitch-off { + text-indent: 0.5em; +} + +/* Custom widths are needed because the length of custom labels differs from + the length of the standard labels */ +.custom-size-flipswitch.ui-flipswitch { + width: 8.875em; +} + +.custom-size-flipswitch.ui-flipswitch.ui-flipswitch-active { + padding-left: 7em; + width: 1.875em; +} + +@media (min-width: 28em) { + /*Repeated from rule .ui-flipswitch above*/ + .ui-field-contain > label + .custom-size-flipswitch.ui-flipswitch { + width: 1.875em; + } +} diff --git a/pwnagotchi/ui/web/templates/base.html b/pwnagotchi/ui/web/templates/base.html index c39e080f..be2d618f 100644 --- a/pwnagotchi/ui/web/templates/base.html +++ b/pwnagotchi/ui/web/templates/base.html @@ -47,6 +47,7 @@ ( '/inbox/new', 'new', 'mail', 'New' ), ( '/inbox/profile', 'profile', 'info', 'Profile' ), ( '/inbox/peers', 'peers', 'user', 'Peers' ), + ( '/plugins', 'plugins', 'grid', 'Plugins' ), ] %} {% set active_page = active_page|default('inbox') %} @@ -54,7 +55,7 @@
    {% for href, id, icon, caption in navigation %} -
  • +
  • {% endfor %} diff --git a/pwnagotchi/ui/web/templates/index.html b/pwnagotchi/ui/web/templates/index.html index 8463cd26..475a97a4 100644 --- a/pwnagotchi/ui/web/templates/index.html +++ b/pwnagotchi/ui/web/templates/index.html @@ -26,6 +26,13 @@ window.onload = function() { +
  • +
    + + +
    +
  • diff --git a/pwnagotchi/ui/web/templates/plugins.html b/pwnagotchi/ui/web/templates/plugins.html new file mode 100644 index 00000000..82714d58 --- /dev/null +++ b/pwnagotchi/ui/web/templates/plugins.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% set active_page = "plugins" %} + +{% block title %} +Plugins +{% endblock %} + +{% block script %} +$(function(){ + $('input[type=checkbox]').change(function(e) { + var checkbox = $(this); + var form = checkbox.closest('form'); + var url = form.attr('action'); + + $.ajax({ + type: 'POST', + url: url, + data: form.serialize(), + success: function(data) { + if( data.indexOf('failed') != -1 ) { + alert('Could not be toggled.'); + } + } + }); + }); +}); +{% endblock %} +{% block content %} +
    + {% for name in database.keys() %} +

    + {{name}} +

    + + + + + + {% endfor %} +
    +{% endblock %} diff --git a/scripts/backup.sh b/scripts/backup.sh index 526d5bbd..6b91917f 100755 --- a/scripts/backup.sh +++ b/scripts/backup.sh @@ -54,4 +54,4 @@ ping -c 1 "${UNIT_HOSTNAME}" > /dev/null 2>&1 || { } echo "@ backing up $UNIT_HOSTNAME to $OUTPUT ..." -ssh "${UNIT_USERNAME}@${UNIT_HOSTNAME}" "sudo find ${FILES_TO_BACKUP} -print0 | xargs -0 sudo tar cv" | gzip -9 > "$OUTPUT" +ssh "${UNIT_USERNAME}@${UNIT_HOSTNAME}" "sudo find ${FILES_TO_BACKUP} -type f -print0 | xargs -0 sudo tar cv" | gzip -9 > "$OUTPUT"