#!/usr/bin/perl
#?
#? NAME
#?      $0 - Protokollhandler für Firefox; startet ein Terminal
#?
#? AUFRUF
#?      $0 [<argumente>]
#?
#? OPTIONEN
#?      -h --h  das hier
#?
#? BESCHREIBUNG
#?      Unterstützt folgende Protokolle und ruft das angegebene Programm auf:
#?      ssh     ssh
#?      shell   (Alias für term, siehe unten)
#?      term    gnome-terminal oder Terminal.app auf Mac OS X
#?              verwendet xfce4-terminal statt gnome-terminal, wenn installiert
# TODO: weitere Protokolle: xterm, aterm, lxterm, kitty, usw. die dann dann
#       genau dieses Terminal aufrufen; default xterm statt gnome-terminal
# TODO: überlegen, wie Terminal aus $TERM verwendet werden könnte
# TODO: Option --hold (o.ä.) des Terminal in Beispiel zeigen
#?
#?      $0 muss als Protokollhandler in Firefox für diese Protokolle
#?      konfiguriert werden, siehe  KONFIGURATION  unten.
#?
#? BEISPIELE (CLI)
#?      $0 ssh://user@host
#?              ssh user@host
#?
#?      $0 term://Titel
#?              gnome-terminal --titel=Titel
#?
#?      $0 term://Titel?--working-directory=/opt/
#?              gnome-terminal --titel=Titel --working-directory=/opt/
#?
#?      $0 term://Titel?--working-directory=/opt/&--hide-menubar
#?              gnome-terminal --titel=Titel --working-directory=/opt/ --hide-menubar
#?
#?      $0 term://man ls?--command="man ls"
#?              gnome-terminal --command="man ls"
#?
#? BEISPIELE (HTML)
#?      <a href="ssh://user@remote">ssh user@remote</a>
#?              (Verwendung in Webseite)
#?
#?      <a href="term://ein_Titel?--working-directory=/tmp&amp;--profile=gross">term://Demo in /tmp</a>
#?              (Verwendung in Webseite)
#?
#? KONFIGURATION im BROWSER
#?   Firefox Linux (vermutlich auch Windows)
#?      In  about:config  eintragen:
#?          network.protocol-handler.expose.ssh     Boolean  false
#?          network.protocol-handler.external.ssh   Boolean  true
#?          network.protocol-handler.expose.term    Boolean  false
#?          network.protocol-handler.external.term  Boolean  true
#?      Dannach Seite aufrufen, die einen entsprechenden Link enthält. Link
#?      klicken und den Handler über den Filebrowser auswählen.
#?      Oder direkt in  handlers.json  eintragen (Abschnitt "schemes"), z.B.:
#?          "ssh": {
#?              "action": 2,
#?              "handlers": [
#?                  {"name":"ssh", "path" "/usr/bin/ssh"}
#?              ]
#?          }
#?          "term": {
#?              "action": 2,
#?              "handlers": [
#?                  {"name":"ff-handler.pl", "path" "/opt/bin/ff-handler.pl"}
#?              ]
#?          }
#?
#?   Firefox Mac OS X
#?      Wie zuvor, aber Mac OS X erlaubt nur  *.app  als Handler einzugtragen.
#?      Man kann danach  .app  in  .pl  ändern (beides funktioniert nicht):
#?       /Volumes/User/ah/Library/Application Support/Firefox/Profiles/ff10/mimeTypes.rdf
#?       nach Eintrag in  about:preferences#applications  steht da:
#           <RDF:Description
#                RDF:about="urn:handler:local:/opt/bin/ff-handler.app"
#                NC:prettyName="ff-handler.app"
#                NC:path="/Volumes/User/opt/bin/ff-handler.app" />
#?
#?   Safari Mac OS X
#       Vermutlich ändern (nicht getestet):
#           plutil -convert xml1 ~/Library/Preferences/com.apple.LaunchServices.plist
#           ~/Library/Preferences/com.apple.LaunchServices.plist
#            <dict>  
#                <key>LSHandlerRoleAll</key>
#                <string>/opt/bin/ff-handler.pl</string>
#                <key>LSHandlerURLScheme</key>
#                <string>term</string>
#            </dict>
#?
#? WARNUNG
#?      Dieser Handler macht keinerlei Prüfungen der übergebenen Argumente!
#?      Weiter setzt der Handler voraus, dass das Programm  gnome-terminal bzw.
#?      Terminal.app  des Betriebssystems vertrauenswürdig ist.
#?
#? ###########################################################################
#? ##   **ACHTUNG**                                                         ##
#? ##   Durch unsachgemäße Nutzung ist Command-Injection möglich!!          ##
#? ##   Das ist ein beabsichtigtes Verhalten.                               ##
#? ###########################################################################
#?
# Hacker's INFO
#       Debugging im Browser ist schwierig, bzw. nicht möglich.  Insbesondere,
#       wenn dieses Skript mit einem Status <> 0 endet,  ist im Browser nichts
#       erkennbar.  URLs (Wert des href= oder src= Attributes, siehe Beispiele
#       oben) ggf. auf der Command-Line testen.
#
#? AUTHOR
#?      24-Mar-14 Achim Hoffmann
#?
# -----------------------------------------------------------------------------

$::VERSION = '@(#) ff-handler.pl 1.6 24/05/15 14:07:23';

BEGIN{ unshift(@INC, q(.)); }

use strict;

sub HELP_MESSAGE() {
    my $me  =  $0;
    $me     =~ s#.*\/##;
    open(FID, $0) || die "$0: WARNING: cannot read myself.\n";
    while(<FID>) {
        s/\$0/$me/g;
        /AUTHOR$/       && print " VERSION\n      $::VERSION\n\n";
        /^#\?(.*)$/     && print "$1\n";
    }
    close(FID);
    exit(0);
} # HELP_MESSAGE

sub EXEC_TERM($$$$) {
    my ($mode, $title, $geo, $cmd) = @_;
    my $plat = qx(uname -s); chomp $plat;
    my $vers = qx(uname -r); chomp $vers;
    my $term = "gnome-terminal";
       $term = "xfce4-terminal" if (-x "/usr/bin/xfce4-terminal");
       $term = "Terminal"       if ($plat =~ m/^Darwin$/ && $vers =~ /^10\./);
    my $win  = "";
       $win  = "--window"       if ($term eq "gnome-terminal");
    if ($term eq "Terminal") {  # MacOSX
        my ($col, $row) = split("x", $geo);
        $cmd =~ s#--working-directory=([^\s]*)##;
        $cmd =~ s#[?&].*$##;    # alles rechts des Usernamens löschen
        my $dir  = $1;
        if ($dir !~ m/^\s*$/) {
            $cmd .= " cd $dir";
        }
        #dbx# print "TERMINAL $row $col : $cmd\n";
        # hmm, AppleScript braucht double quotes; aufpassen bei Leerzeichen!
        exec qq(osascript << "End-of-osascript"
                tell app "$term"
                  do script "$cmd"
                  tell window (index of first window)
                    set custom title      to "$title"
                    set number of rows    to $row
                    set number of columns to $col
                  end tell
                end tell
End-of-osascript
        );
    } else {
        # for gnome-terminal ssh is different
        if ($mode eq "ssh") {
            $cmd =~ s#[?&].*$##;    # alles rechts des Usernamens löschen
            print "$term $win --title='$title' --hide-menubar --geometry=$geo -e '$cmd'\n";
            exec "$term $win --title='$title' --hide-menubar --geometry=$geo -e '$cmd'";
        }
        if ($mode eq "term") {
            #dbx# print "$term $win --title='$title' --hide-menubar --geometry=$geo $cmd\n";
            exec "$term $win --title='$title' --hide-menubar --geometry=$geo $cmd";
        }
    }
} # EXEC_TERM

# scan options, do work
# -------------------------------------
my ($prot, $user, $host, $args);
while (0 <= $#ARGV) {
    my  $arg = shift;
    if ($arg =~ m/^--?h(?:elp)?$/) {
        # -h oder --h mus das erste Argument sein, --help darf überall stehen
        HELP_MESSAGE();
        exit;
    }
    if ($arg =~ m/^--?v(?:ersion)?$/i) {
        print "$::VERSION\n";
        exit;
    }
    #dbx STDERR "#dbx# ARG=$arg\n";
    $args=$arg;
    $args=~ s#^[^?]*\?##;
    $arg =~ m#^([^:]*)://([^@]*)(?:@(.*))?#;
    $prot=$1;
    $user=$2;
    $host=$3;
    $host=$user if ($host =~ m/^\s*$/); # kein User angegeben, dann ist es der Host
    $user=~ s#\?.*##;
    #dbx STDERR "#dbx# prot=$prot host=$host user=$user args=$args\n";

    if ($prot eq "ssh") {
        $user =  "$user@"."$host";
        EXEC_TERM("ssh",  "SSH $user", "90x45", "ssh $user");
    }
    if ($prot =~ m/shell|term/) {
        $user =~ s#([^?]*)#$1#;
        $args =~ s#&# #g;
        $args =~ s/(?:%([2-9a-f][0-9a-f]))/pack("H2",$1)/egi; # nur ASCII umwandeln
        EXEC_TERM("term", $user, "90x28", $args);
    }
}

exit(0);

