Hallo, Habr!
Für mich ist dieser Satz so etwas wie ein 'Hello World', da ich endlich meine erste Veröffentlichung erreicht habe. Ich habe diesen wunderbaren Moment lange hinausgezögert, da ich nichts zu schreiben hatte und es mir nicht gefiel, bereits oft besprochene Themen abzukratzen. Für meine erste Veröffentlichung wollte ich etwas Originelles, Nützliches für andere, das eine Herausforderung beinhaltet und Probleme löst. Und jetzt kann ich das teilen. Nun, alles der Reihe nach.
Einleitung
Alles begann damit, dass ich mir vor einiger Zeit auf meinem Arbeitscomputer Linux Mint installiert habe. Viele wissen wahrscheinlich, dass Pidgin mit dem Sipe-Plugin eine durchaus brauchbare Alternative zu Microsoft Lync (jetzt Skype for Business) für Linux-Systeme ist. Aufgrund der Art meiner Arbeit nehme ich häufig an SIP-Konferenzen teil, und in meiner Zeit als Windows-Nutzer war der Zugang zu den Konferenzen ganz einfach: Wir erhielten eine Einladung per E-Mail, klickten auf den Zuganglink und waren drin.
Als ich auf die dunkle Seite von Linux wechselte, wurde alles etwas komplizierter: Der Zugang zu Konferenzen in Pidgin ist zwar vorhanden, aber dafür muss man in den Eigenschaften des Sip-Accounts die Option zur Teilnahme an Konferenzen im Menü auswählen und in dem sich öffnenden Fenster den Link zur Konferenz einfügen oder den Namen des Veranstalters und die Konferenz-ID eingeben. Irgendwann begann ich zu überlegen: "Kann man das nicht vereinfachen?" Aha, sagt ihr, wozu brauchst du das, hätte einfach bei Windows bleiben können und wäre dafür nicht zuständig gewesen.
Schritt 1. Forschung
„Was man sich ins Ohr setzt, bekommt man nicht mehr heraus“ — so sagte Nekrasov in seinem Werk „Kому на Руси жить хорошо“.
So, da der Gedanke im Kopf war, entstand bald die erste Umsetzungsidee. Es schien ganz einfach — man muss die Aufrufe der Links abfangen — Lokal einen Webanwendungsprozess auf 127.0.0.1 einrichten und im /etc/hosts einen statischen Eintrag für die Domain des Unternehmens hinzufügen, über die der Zugang zu den Konferenzen erfolgt und auf localhost verweist. Dieser Webserver sollte dann den eingehenden Link verarbeiten und irgendwie an Pidgin weitergeben (ich muss zugeben, dass ich zu diesem Zeitpunkt noch nicht wusste, wie ich es ihm überhaupt übergeben sollte). Die Lösung hat natürlich etwas von einem Workaround, aber wir sind schließlich Programmierer, Workarounds machen uns keine Angst (Spaß).
Dann habe ich zufällig einen Link zur Einladung in Google Chrome geöffnet (normalerweise benutze ich immer Mozilla Firefox). Und zu meiner Überraschung sah die Webseite ganz anders aus – es gab kein Eingabeformular für Benutzerdaten und direkt nach dem Betreten der Seite kam eine Aufforderung, etwas über xdg-open. Aus Neugier klicke ich auf „ja“ und erhalte eine Fehlermeldung – der Link lync15:confjoin?url=https://meet.company.com/user/confid kann nicht geöffnet werden. Hm. Was ist das für ein xdg-open und was benötigt er, um solche Links zu öffnen? Eine eingehende Analyse der Dokumentation hat gezeigt, dass dies ein Handler für die grafische Oberfläche ist, der hilft, assoziierte Anwendungen mit URI-Schemas oder bestimmten Dateitypen zu starten. Die Assoziationen werden über das Mapping von MIME-Typen konfiguriert. Also sehen wir, dass eine Suche nach der assoziierten Anwendung für das URI-Schema mit dem Namen lync15 gestartet wird und der Link an xdg-open übergeben wird, der theoretisch weiter an eine Anwendung übergeben werden sollte, die für diesen Linktyp zuständig ist. Die haben wir natürlich nicht im System. Und was macht man in der Welt von Open Source, wenn man so etwas nicht hat? Richtig, wir schreiben es selbst.
Ein tieferer Einblick in die Welt von Linux und insbesondere ins Studium, wie die grafische Benutzeroberfläche (Desktop-Umgebung, DE) funktioniert, zeigt, dass Anwendungen und der zugehörige Mime-Typ in der Regel direkt in Dateien mit der Erweiterung .desktop festgelegt werden. Warum also nicht? Ich erstelle eine einfache Anwendungsstartverknüpfung, die einfach ein Bash-Skript ausführen und das übergebene Argument in die Konsole ausgeben soll. Hier ist nur die Verknüpfungsdatei:
[Desktop Entry]
Name=Lync
Exec=/usr/local/bin/lync.sh %u
Type=Application
Terminal=false
Categories=Network;InstantMessaging;
MimeType=x-scheme-handler/lync15;
Ich starte xdg-open aus der Konsole mit der gleichen URL, die aus dem Browser kommt, und... es gibt einen Rückschlag. Es sagt erneut, dass es den Link nicht verarbeiten kann.
Wie sich herausstellt, habe ich das Verzeichnis der zugehörigen Mime-Typen für meine Anwendung nicht aktualisiert. Dies geschieht mit einem einfachen Befehl:
xdg-mime default lync.desktop x-scheme-handler/lync15
der einfach die Datei bearbeitet ~/ .config/mimeapps.list.
Versuch Nummer 2 mit dem Aufruf von xdg-open – und wieder ein Fehlschlag. Kein Problem, Schwierigkeiten schrecken uns nicht ab, sie steigern nur unser Interesse. Und mit der vollen Kraft von Bash (d.h. mit dem Tracing) tauchen wir tief in das Debugging ein. Hier ist wichtig zu erwähnen, dass xdg-open einfach ein Shell-Skript ist.
bash -x xdg-open $url
Bei der Analyse der Ausgaben nach der Traceroute wird langsam klar, dass die Kontrolle übergeben wird an exo-open. Dies ist bereits eine Binärdatei, und es wird komplizierter zu verstehen, warum sie einen Fehlercode zurückgibt, wenn ihr ein Link als Argument übergeben wird.
Durch das Durchforschen von xdg-open habe ich herausgefunden, dass es verschiedene Umgebungsparameter analysiert und die Kontrolle entweder an Werkzeuge zur Öffnung spezifischer Dateilinks für ein bestimmtes DE übergibt, oder es hat ein Fallback in Form der Funktion open_generic
open_xfce()
{
if exo-open --help 2>/dev/null 1>&2; then
exo-open "$1"
elif gio help open 2>/dev/null 1>&2; then
gio open "$1"
elif gvfs-open --help 2>/dev/null 1>&2; then
gvfs-open "$1"
else
open_generic "$1"
fi
if [ $? -eq 0 ]; then
exit_success
else
exit_failure_operation_failed
fi
}
Ich fügen hier schnell einen kleinen Hack ein, um das übergebene Argument zu analysieren, und wenn sich dort unser spezifischer Substring befindet lync15:, übergeben wir sofort die Kontrolle an die Funktion open_generic.
Versuch Nummer 3 und denken Sie, es hat funktioniert? Ja, sicher, als ob. Aber die Fehlermeldung hat sich bereits geändert, das ist ein Fortschritt – jetzt sagt es mir, dass die Datei nicht gefunden wurde und hat mir die übergebenen Argumente als Datei ausgegeben.
Diesmal lag es an der Funktion is_file_url_or_path, die die gegebene Datei-URL file:// oder den Pfad zu einer Datei oder etwas anderem analysiert. Und die Überprüfung funktionierte nicht richtig, weil unser Präfix (URL-Schema) Ziffern enthält, während der reguläre Ausdruck nur eine Menge von Zeichen überprüft, die aus :alpha:, Punkten und Bindestrichen bestehen. Nach Rücksprache mit dem Standard rfc3986 für uniform resource identifier wurde klar, dass Microsoft diesmal nichts falsch macht (obwohl ich das vermutet hatte). Die Zeichenklasse :alpha: enthält nur Buchstaben des lateinischen Alphabets. Ich ändere schnell die Überprüfung des regulären Ausdrucks in alphanumeric. Fertig, Sie sind großartig, alles läuft endlich, die Kontrolle nach allen Überprüfungen wird an unsere Skriptanwendung übergeben, unsere URL wird auf die Konsole ausgegeben, alles wie es sein soll. Danach beginne ich zu vermuten, dass alle Probleme bei exo-open auch aufgrund der Formatvalidierung der URL wegen der Ziffern im Schema liegen. Um die Hypothese zu überprüfen, ändere ich den MIME-Typ der Anwendung auf einfach das Schema lync und voilà – alles funktioniert auch ohne die Überschreibung der Funktion open_xfce. Aber das hilft uns nicht, denn die Webseite für den Zugriff auf die Konferenz erstellt genau den Link mit lync15.
So, der erste Teil des Weges ist geschafft. Wir können den Linkaufruf abfangen, und jetzt müssen wir ihn irgendwie verarbeiten und in Pidgin weiterleiten. Um zu verstehen, wie die Eingabe von Daten über den Link im Menü "Konferenzen beitreten" funktioniert, habe ich das Git-Repository des Sipe-Projekts geklont und mich darauf vorbereitet, wieder in den Code einzutauchen. Doch glücklicherweise wurden meine Aufmerksamkeit von den Skripten im Verzeichnis contrib/dbus/:
- sipe-join-conference-with-uri.pl
- sipe-join-conference-with-organizer-and-id.pl
- sipe-call-phone-number.pl
- SipeHelper.pm
Es stellt sich heraus, dass das Sipe-Plugin über dbus (Desktop Bus) interagieren kann, und in den Skripten gibt es direkt Beispiele, um über den Link an einer Konferenz teilzunehmen, entweder über den Namen des Veranstalters und die conf-id oder indem man einen Anruf über SIP initiiert. Genau das, was uns gefehlt hat.
Schritt 2. Implementierung des Auto-Join-Handlers
Da es bereits fertige Beispiele in Perl gibt, habe ich beschlossen, diese einfach zu nutzen sipe-join-conference-with-uri.pl und ein wenig an meine Bedürfnisse anzupassen. Perl zu schreiben fällt mir leicht, daher hat es keine großen Schwierigkeiten verursacht.
Nach dem Testen des Skripts separat habe ich seinen Aufruf in die Datei lync.desktop. Und das war ein Sieg! Beim Zugriff auf die Seite für den Beitritt zur Konferenz und der Erlaubnis zur Ausführung von xdg-open öffnete sich das Popup-Fenster der Konferenz aus Pidgin automatisch. Wie habe ich mich gefreut.
Begeistert von diesem Erfolg entschied ich mich, dasselbe auch für meinen Hauptbrowser Mozilla Firefox zu tun. Beim Zugriff über den Fuchs öffnete sich die Anmeldeseite, und ganz unten gab es einen Button join using office communicator. Der zog meine Aufmerksamkeit auf sich. Wenn ich darauf klicke, wird im Browser folgende Adresse aufgerufen:
conf:sip:{user};gruu;opaque=app:conf:focus:id:{conf-id}required-media=audio
, woraufhin er mir freundlich mitteilt, dass er nicht weiß, wie man es öffnet, und dass ich möglicherweise keine zugeordnete Anwendung für dieses Protokoll habe. Das haben wir ja schon durchgemacht.
Ich registriere schnell meine Skriptanwendung auch für die URI-Schemata conf und … es passiert nichts. Der Browser beschwert sich weiterhin, dass keine Anwendung vorhanden ist, die meine Links verarbeitet. Dabei funktioniert der Aufruf aus der Konsole xdg-open mit den Parametern hervorragend.
„Einen benutzerdefinierten Protokollhandler in Firefox einrichten“ — mit dieser Frage habe ich das Internet durchforstet. Nach einigen Diskussionen auf Stackoverflow (wohin auch sonst?) schien ich eine Antwort gefunden zu haben. Man muss eine spezielle Einstellung erstellen in about:config (natürlich foo durch conf ersetzen):
network.protocol-handler.expose.conf = false
Ich erstelle das, öffne den Link und… nicht so schnell. Der Browser sagt ganz unverfroren, dass er unsere Anwendung nicht kennt.
Ich lese die offizielle Dokumentation zur Registrierung des Protokolls bei Mozilla, es gibt die Möglichkeit, die Assoziationen direkt im Gnome-Desktop zu hinterlegen (natürlich foo durch conf ersetzen):
gconftool-2 -s /desktop/gnome/url-handlers/conf/command '/path/to/app %s' --type String
gconftool-2 -s /desktop/gnome/url-handlers/conf/enabled --type Boolean true
Ich registriere, öffne den Browser… und wieder kein Erfolg.
Dabei fällt mir eine Zeile aus der Dokumentation ins Auge:
Beim nächsten Klick auf einen Link des Protokolls vom Typ foo werden Sie gefragt, mit welcher Anwendung Sie ihn öffnen möchten.
— Semen Semenovich
— Aaa
Wir klicken nicht auf den Link, sondern die Webseite ändert einfach die window.location über JavaScript. Ich schreibe eine einfache HTML-Datei mit einem Link zum conf-Protokoll, öffne sie im Browser, klicke auf den Link – Yos! Ein Fenster öffnet sich mit der Frage, in welcher Anwendung wir unseren Link öffnen möchten und dort steht bereits unsere Anwendung Lync auf der Liste – wir haben sie auf alle möglichen Arten ehrlich registriert. In dem Fenster gibt es auch ein Kontrollkästchen „Auswahl merken und Links immer in unserer Anwendung öffnen“, das aktivieren wir und drücken auf OK. Und das ist der zweite Erfolg – das Konferenzfenster öffnet sich. Dabei funktioniert das Öffnen von Konferenzen bereits und zwar nicht nur beim Klicken auf den Link, sondern auch beim Wechsel von der entsprechenden Seite, um an der Konferenz teilzunehmen.
Dann habe ich überprüft, dass das Entfernen der Parameter network.protocol-handler.expose.conf keinen Einfluss auf die Funktionsweise des Protokolls in Firefox hatte. Die Links funktionierten weiterhin.
Fazit
Ich habe all meine Entwicklungen in ein Repository auf GitHub hochgeladen, die Links zu allen Ressourcen werden am Ende des Artikels stehen.
Ich würde gerne Feedback von denen erhalten, die meine Entwicklungen nutzen möchten. Ich möchte klarstellen, dass ich alles nur für mein Linux Mint System entwickelt habe, daher könnten andere Distributionen oder Desktops nicht in dieser Form funktionieren. Ich bin mir sogar fast sicher, dass das so ist, da ich in xdg-open nur eine Funktion gepatcht habe, die sich ausschließlich auf meine Desktop-Umgebung bezieht. Wenn Sie Unterstützung für andere Systeme oder Desktops hinzufügen möchten, senden Sie mir Pull-Requests auf GitHub.
Die gesamte Projektumsetzung hat einen Abend gedauert.
Links:
Quelle: habr.com
