PVA: Pluginsystem für Carola

Goldene Zeiten für alle Voice Assistenten Fans unter Euch. Sofern Ihr Java könnt, seid Ihr nun in der Lage Carola um neue Funktionen zu erweitern.

PVA: Pluginsystem für Carola

Im GitHub Pluginsverzeichnis ist ein Beispiel, daß Euch die Möglichkeiten aufzeigt. Ihr könnt reine Kommandoausführung machen, also auf Sprachbefehle lauschen und ausführen, oder Ihr erstellt einen periodischen Dienst, der die ganze Zeit im Hintergrund arbeitet, oder beides 😉

Vor ein paar Tagen habe ich ja auf die neue Prozessüberwachung hingewiesen, die wurde jetzt umgeschrieben, so daß Sie als Plugin arbeitet. Das führte dazu, daß wir die „nervigen“ Audiowarnungen abschalten können, wenn die Ursache bekannt ist.

Was müssen wir machen um ein Plugin zu schrieben?

Nicht so viel wie man denken würde. Erstmal brauchen wir die nötigen Kommandos und Texte:

command:“schalte|warnung|aus“,“SILENCELOADWARNING“,““
command:“schalte|warnung|ein“,“UNSILENCELOADWARNING“,““

text:“de“,“HEALTHRESPONSETURNEDOFF“,“es erfolgen keine weiteren audiowarnungen mehr“
text:“de“,“HEALTHRESPONSETURNEDON“,“Sprachwarnungen eingeschaltet“

Und dann natürlich den Plugincode:

 



package plugins.files;

import plugins.Plugin;
import io.Dos;
import server.PVA;
import hash.StringHash;
import hash.TwoKeyHash;

public class LoadTask extends Plugin {

	private String getFilter = ":silence:";
	private String setFilter = ":silence:";
	private TwoKeyHash validValues = new TwoKeyHash(); // this way we get multiply options for an argument 
	
	public LoadTask() {
		System.out.println("Class LoadTask Constructor called");
	}
	
	public void init(PVA pva) {
		this.pva = pva;
		info.put("hasThread","yes"); // Das sagt dem Pluginloader, das wir einen Prozess laufen lassen wollen
		info.put("hasCodes","yes");  // Das sagt dem PVA Task, daß wir Kommandos verarbeiten
		info.put("name","LoadTask"); // Ein eindeutiger Name, könnte auch de.linux-am-dienstag.LoadTask sein.
		vars.put("silence","no");    // hier merken wir uns, ob wir Sprachwarnungen ausgeben wollen: JA!
		validValues.put("silence","yes","ok"); // Dieses Plugin prüft, ob die Variable "Silence" geändert werden darf und wenn ja wie
		validValues.put("silence","no","ok");
	}
	
	public StringHash getPluginInfo() {
		return this.info;		// wir geben unsere Infos an den PluginManager damit der etwas über uns weiß.
	}

	public String  getVar(String name) {
		if ( getFilter.contains(":"+name+":") )   // Wir prüfen, ob die Variable "name" ausgelesen werden darf.
			return vars.get(name);            // Wenn JA, ann gibt Inhalt zurück
		return ""; // war wohl nichts.
	} 

        // Hier prüfen wir, ob eine bestimmte Variable von außen geändert werden darf. Das KANN man so machen, muß man aber nicht.

	public boolean setVar(String name,String value) {
		if ( getFilter.contains(":"+name+":") && validValues.get(name,value).equals("ok")  )  {
			vars.put(name,value);
			return true;
		}
		return false;
	}
	
	// getActionCodes() gibt alle Kommandos aus, die in der Config für dieses Plugin eingetragen sein sollten, ergo auf was es so hört:

	public String[] getActionCodes() {  return "SILENCELOADWARNING:UNSILENCELOADWARNING".split(":"); };

        // hier führen wir Kommandos aus. Dazu prüfen wir, ob wir das Kommando kennen. 
        // Optional gibt es noch den rohen text der Eingabe, so daß dort Infos ausgelesen werden können.
	public boolean execute(String actioncode, String rawtext) { 
		try {
			if ( actioncode.equals("SILENCELOADWARNING") ) {
				log("schalte warnung aus");
				setVar("silence","yes");
				pva.say( pva.texte.get( pva.config.get("conf","lang_short"), "HEALTHRESPONSETURNEDOFF") );
	
			} else if ( actioncode.equals("UNSILENCELOADWARNING") ) {
	
				log("schalte warnung ein");
				setVar("silence","no");
				pva.say( pva.texte.get( pva.config.get("conf","lang_short"), "HEALTHRESPONSETURNEDON") );
	
			} else return false;
	
			return true;
		}  catch (Exception localException) {
			localException.printStackTrace();
			return true;
		}
	};

	Float lastState = Float.parseFloat("0");
	
       // hier starten wir den eigentlich Überwachungsprozess und lesen die Load aus und berichten über unsere Funde via Sprachausgabe:
	public void run() {
		try {
			String load = "";
			int time = 0;
			boolean inform = false;

			while ( true ) {
				if (isInterrupted()) {
					return;
				}

				Float f = Float.parseFloat( dos.readPipe("cat /proc/loadavg").split(" ")[0].trim() );
				long  c = Long.parseLong( dos.readPipe("grep -c processor /proc/cpuinfo").trim() );
				
				if ( f > c && ( lastState < c || time > 60 ) ) {
						if ( vars.get("silence").equals("no") ) pva.say( pva.texte.get( pva.config.get("conf","lang_short"), "HEALTHRESPONSEHELPHELP") );
						time = 0;
						inform = true;
					
				} else if ( f > ( c*80/100) && ( lastState < ( c*80/100) || time > 60 ) ) {
						if ( vars.get("silence").equals("no") ) pva.say( pva.texte.get( pva.config.get("conf","lang_short"), "HEALTHRESPONSE80P") );
						time = 0;
						inform = true;
				} else if ( f < ( c/2 ) && lastState > (c/2) && inform ) {
						if ( vars.get("silence").equals("no") ) pva.say( pva.texte.get( pva.config.get("conf","lang_short"), "HEALTHRESPONSEOK") );
						inform = false;
				}	

				lastState = f;
				time++;
				sleep(1000L);

			}
		} catch (Exception localException) {
			localException.printStackTrace();
		}
	}
}

Die Load auslesen ist jetzt nicht so dramatisch schwer, so das das Plugin recht kurz ist. Da kann man natürlich auch viel umfangreichere Anwendungen schreiben.

Falls Euch der Javacode nichts sagt, wird es langsam Zeit mal Java zu lernen, daß ist recht einfach 😉

Ein cooles Plugin wäre eine Anbindugn an ChatGPT 😉 Vielleicht will ja mal wer oder fragt doch ChatGPT mal, der braucht doch auch Code 😉

PVA: kleines Sicherheitsloch im IMAP Modul

Wie das so mit komplexer Software ist, CVE Meldungen sind quasi vorprogrammiert, aber einfache Programmierfehler tun es meistens auch 🙁

PVA: kleines Sicherheitsloch im IMAP Modul

Jetzt ist der Einschlagkrater durch die im PVA gefundene Sicherheitslücke nicht besonders groß, man müßte Ihn vermutlich mit der Lupe suchen, weil kaum wer von dem neuen Feature wußte und es ausprobiert hat, aber, zur Vermeidung gleichartiger zukünftiger Unfälle, soll die Lücke seziert und dokumentiert werden.

Was ist passiert?

In der MailConnection-Komponente, die macht die IMAP Verbindung auf, war ein Programmierfehler drin, der die verschlüsselte Verbindung zum IMAP Server unterband. Wer also die neue Funktion schon ausprobiert hat , muß mindestens auf diesen Commit updaten: 685ee5ecaa486006a0cee04cac763ee479160e6e

Hier der fehlerhafte Code:

Properties props = System.getProperties();
if ( m.secure == true ) {
       props.setProperty("mail.store.protocol", "imaps");
} else props.setProperty("mail.store.protocol", "imap");

Session session = Session.getInstance(props, null);
Store store = session.getStore("imap");
store.connect(m.servername, m.username, m.password);

Statt die laut Konfiguration (m.secure) gewünschte Verbindungsart „imaps“ zu wählen, zog es der Code vor, dann doch unabhängig davon „imap“ zu verwenden. Korrekt wäre diese Zeile gewesen:

Store store = session.getStore();

Was war die Ursache?

Als Ursache konnte „mangelndes Verständnis“ der Methode getStore(…) ausgemacht werden, um genau zu sein, wurde erwartet, daß es sich um den Unterschied zwischen „POP3“ oder „IMAP“ handelt, weil der Sicherheitslevel wurde ja vorher schon festgelegt, aber leider überschrieb der Aufruf diese Voreinstellung wieder.

Was müßt Ihr tun?

Im Repo ist das Update bereits enthalten, so daß alle die, die Automatische Updates einspielen, schon sicher sind.
Wer seinen PVA selbst baut, der muß einmal updaten und compilieren.

Wie wurde es gefunden?

Vor 11 Jahren, ja, einfach weiterlesen, entschied ich, daß meine POP3/IMAP Serverlogs von einer Software auf genau diese unverschlüsselten Verbindungen prüft und den Benutzer darüber informiert, daß er irgendwo eine unsichere Konfig hat. 11 Jahre lang habe ich keine Email erhalten, und jetzt rettet dieses kleine Script dem PVA den Arsch 😉

PS: Falls wer dachte, daß man im Github einfach mal so ein Security Advisory schreiben könnte, der hat das noch nie versucht und mir ist jetzt klar, warum so viele Projekte mit Lücken, keine Securityeinträge haben 🙁

PVA: Emails abhören mit Carola

Wieso labbert Mich Deine Carola voll, ich soll in die Küche kommen?“ fragte meine Freundin und zog wieder ab, weil ich nur verschmitzt grinste. Ein erfolgreicher Testlauf, auch wenn der technisch gar nicht Ihr galt, sondern natürlich Carola selbst. Naja, irgendwie braucht man ja Feedback 😉

PVA: Emails abhören mit Carola

Da Carola jetzt meine Email mitlesen kann und davon auch heftigst Gebrauch macht, genau wie mein Emailprogramm, sollte ich Euch jetzt erklären Sie das geht. Vorher sollte ich aber noch erwähnen, daß mein Test darin bestand mir am Handy eine Email zu schreiben, in deren Betreff die Anweisung für meine Freundin enthalten war 😉 Das sie in die Küche kam, war der Beweis, daß Carola den Job erledigt, wenn ich nicht da bin. Man sollte aber daran denken, daß, wenn man so etwas vorhat, man vorher sicherstellen muß, daß die Zielperson für die Ansprache auch in Hörweite ist 🙂

Erstmal die Regeln:

1) Das ist kein Feature, daß man global aktiviert, ergo, es gehört in eine Konfig in Eurem $HOME.

2) Ihr könnt Euch aussuchen, ob nur der Betreff oder die ganze Email gelesen werden soll.

3) Wer GTTS benutzt, sollte sich ihm klaren sein, daß Google damit Infos bekommt, die es nicht haben sollte. Also im Zweifel MaryTTS nutzen, da sind die Emailinhalte sicherer.

4) Es wird nur die INBOX ausgelesen, nicht irgendwelche Unterverzeichnisse.

5) Es wäre eine Straftat nach §303b StGb, einen entsprechend konfigurierten Sprachassistenten von Extern mit Mails zu bombardieren, um Werbung oder sonstige Parolen in die Welt schreien zu lassen. Ich rate dringend von solchem Unsinn ab, das wird für Euch nicht gut ausgehen:

(1) Wer eine Datenverarbeitung, die für einen anderen von wesentlicher Bedeutung ist, dadurch erheblich stört, dass er

1. eine Tat nach § 303a Abs. 1 begeht,
2. Daten (§ 202a Abs. 2) in der Absicht, einem anderen Nachteil zuzufügen, eingibt oder übermittelt oder
3. eine Datenverarbeitungsanlage oder einen Datenträger zerstört, beschädigt, unbrauchbar macht, beseitigt oder verändert,

wird mit Freiheitsstrafe bis zu drei Jahren oder mit Geldstrafe bestraft.

So aktiviert Ihr das IMAP-Feature

Die Konfiguration im Format …

# mailbox:"servername","username","password","commonname","secure","port","readoutloud","pullinterval"

kommt in ein Konfigfile unter ~/.config/pva/conf.d/ z.B. 09-imap.conf .

Beispiel:

mailbox:"imap.domain.de","harry.mud@gmail.com","30243klSXLS3os3S!X#","Mailbox from Harry Mudd","true","143","false","60"

Der Flag „readoutloud“ (false/true) würde die gesamte Email vorlesen, wenn er aktiviert (true) ist.

Im Beispiel ist der Benutzername des Kontos als Email angegeben, so wie das bei einigen Diensten der Fall ist. Ihr braucht eigentlich nur Server- , Username und Passwort, der Rest ist eigentlich bis auf readoutloud egal. Gewöhnt Euch nicht dran, fliegt vermutlich raus.

Ihr könnt beliebig viele Mailboxen eintragen.

Das passiert dann

Die Mailbox wird 1x pro Minute geöffnet und ermittelt, ob eine neue Email da ist. Ist eine neue Email vorhanden UND diese noch ungelesen, dann wir per Sachausgabe der Absender und der Betreff vorgelesen.

Gewöhnt Euch aber daran, daß nach dem Starten des Desktops die nächtliche Flut von Emails abgearbeitet wird. Konten mit aktiven Cryptonews-Mailinglisten würde ich da nicht eintragen, daß wäre mir zu stressig 🙂

Wichtige Security Info dazu:

PVA: kleines Sicherheitsloch im IMAP Modul

… und weiter im Text:

Damit Ihr keine neue Features mehr verpasst, habe ich Euch ein Fedora Repository eingerichtet. Am besten lest Ihr Euch das hier mal nach:

PVA: Carola hat Ihr eigenes Repo bekommen