print2forms im Intranet steuern

Anforderung

In grossen, verteilten print2forms-Installationen kann es vorkommen, dass man einzelnen Mitarbeitern die Kontrolle über eine kleine Anzahl Drucker übertragen möchte, ohne diesen gleich den vollen Zugriff auf das Kontrollfeld des Client/Gateway-Services zu erlauben. Möglicherweise stehen dem auch Rechteprobleme mit dem Zugriff auf den Rechner mit dem Client/Gateway-Service entgegen.

Bei solchen Konstellationen kann die Integration des Programms p2fRemote.exe (siehe auch Tip p2fRemote.exe) in eine Intranet-Umgebung die Anforderung nach dezentraler Druckerverwaltung auf elegante Weise umsetzen. Durch die Steuerung der Drucker über eine Browser-Oberfläche entfällt jeglicher print2forms-Installationsaufwand auf den eingesetzten Rechnern, und die Installations- und Konfigurationsarbeiten beschränken sich auf den Web-Server im Intranet.

Die hier vorgestellte Lösung soll nur als Leitfaden für eigene Installationen dienen und stellt keineswegs die einzige und bestimmt auch nicht für alle Fälle optimale Lösung dar. Beispielsweise muss die Definition der zu kontrollierenden Drucker bei mehrfachem Einsatz der Lösung innerhalb eines Unternehmens anders realisiert werden. Auch wird eine Integration in eventuell bereits vorhandene Management-Oberflächen andere Vorgehensweisen erfordern. Zu guter Letzt ist die graphische Präsentation ausbaufähig - bis hin zu Gebäudeplänen mit eingezeichneten Druckerstandorten.

Realisierung

Um einen ersten Eindruck von der hier diskutierten Intranet-Integration zu zeigen, nachfolgend eine Abbildung der von der Lösung erzeugten Startseite und dann ein Abbildung von der eigentlichen Statusanzeige.

Mit der Startseite wird ein gemeinsames Portal für alle Zugriffe auf print2forms realisiert. Hinter den drei oder gegebenenfalls auch mehr Schaltflächen befinden sich Verweise auf Textdateien, in denen die Liste der jeweils zu überwachenden Drucker abgelegt sind. Durch Anklicken der Schaltfläche wird dann auf die Statusanzeige weitergeleitet.

In der Statusanzeige werden in einer Tabelle die Namen und der Status zweier (oder mehrerer) Drucker (oder Gateways) angezeigt. Der eine Drucker Import ist durch einen Klick in die am Anfang jeder Tabellenzeile positionierte CheckBox für eine weitere Aktion ausgewählt. Der Drucker Systemdrucker ist im Augenblick nicht betriebsbereit, und erfordert ein manuelles Eingreifen, weil die Druckerabdeckung offen ist.

Über die vier Schaltflächen unterhalb der Tabelle werden Aktionen ausgeführt, wobei Aktualisieren die Anzeige auf den neuesten Stand bringt (unabhängig von den getroffenen Auswahlen) und die drei anderen Schaltflächen die jeweilige Aktion auf den ausgewählten Druckern (oder Gateways) ausführen. Dabei haben die Aktionen und auch die Flaggen die aus dem Kontrollfeld des Client/Gateway-Services bekannten Bedeutungen.

Die im nachfolgenden vorgestellten technischen Informationen basieren auf einer Beispielinstallation mit einem Apache-Web-Server mit Perl-Unterstützung auf einem Windows-Rechner. Eine solche Umgebung ist mit der Standardinstallation eines XXAMP-Paketes leicht testweise herzustellen. An notwendigen Basis-Techniken werden lediglich html, CSS und zwei kleine Perl-Skripte eingesetzt. Muss die Lösung in ein anderes Umfeld portiert werden, müssen gegebenenfalls die ersten Zeile der Skripte und alle in den Skripten und HTML-Seiten vorkommenden Pfade angepasst werden.

Die Lösung geht auch der Einfachheit halber zunächst davon aus, dass der Apache-Server auf dem gleichen Rechner läuft, wie der print2forms-Client/Gateway-Service, der unter dem Pfad C:\Programme\print2forms installiert ist. Läuft print2forms auf einem anderen Rechner, muss der Aufruf des Programms p2fRemote über einen Aufruf des Microsoft-Tools PsExec erfolgen, was die Ausführung von Programmen auf anderen Rechnern ermöglicht.

Die erste Aufgabe für die Realisation einer Intranet-Integration ist die Erstellung einer HTML-Seite, die als Startseite für unsere Lösung dient. Auf dieser Seite wird ausgewählt, welche Drucker gesteuert und überwacht werden sollen. Am sinnvollsten ist wohl eine Auswahl nach Abteilungen oder innerhalb der Firma existierenden organisatorischen Einheiten. Die Seite heist print2forms.html und ihr HTML-Code ist nachfolgend abgebildet.

print2forms.html
<!DOCTYPE html PUBLIC "-//W3C//DTD html 4.01 Transitional//EN">
<html>
  <head>
    <title>Willkommen</title>
    <style type="text/css">
 
      body  { font-size: 8pt; 
              font-family: Verdana, Arial, Helvetica, MS Sans Serif; }
      h1    { font-size: 24pt; 
              margin-left: 190px; margin-top: 80px; margin-bottom: 0px; } 
 
      .Button { background-color: #ccc; color: #000; width: 100px;
                border: 1px solid #888; margin-right: 10px; }
 
    </style>
  </head>
  <body style="background-image:url(imgs/p2f.gif); 
               background-repeat: no-repeat;
               background-position: 0px 20px;
               background-color: white;">
 
    <h1>Willkommen</h1>
 
    <br/>
    <br/>
 
    <form action="/cgi-bin/remote.pl" method="get">
 
      <input type="hidden" name="Drucker" value="">
 
      <input type="submit" class="button" value="Versand" 
             onclick="this.form.Drucker.value = 'Versand.txt'">
      <input type="submit" class="button" value="Lager"
             onclick="this.form.Drucker.value = 'Lager.txt'">
      <input type="submit" class="button" value="Büro"
             onclick="this.form.Drucker.value = 'Buero.txt'">
    </form>
 
  </body>
</html>

Diese recht überschaubare HTML-Datei stellt drei Schaltflächen mit der Beschriftung Versand, Lager und Büro zur Verfügung, die auf die Statusanzeige für die jeweilige Abteilung verweisen. Damit die Statusanzeige weiss, welche Drucker zu welcher Abteilung gehören, wird die Liste der Drucker als Textdatei übergeben. In einer solchen Datei stehen einfach die Namen der zu überwachenden Drucker oder Gateways. Jeweils ein Name je Zeile.

Die zweite HTML-Seite, die benötigt wird, ist die, die zur Statusanzeige verwendet werden soll. Der erste Teil der Datei (CSS) ist identisch mit der der Startseite, um ein einheitliches Bild zu gewährleisten. Diese Seite wird unter dem Namen Template.html abgelegt, weil sie nicht direkt angezeigt werden kann, sondern erst noch durch ein Skript bearbeitet wird. Der HTML-Code ist nachfolgend abgebildet.

template.html
<!DOCTYPE html PUBLIC "-//W3C//DTD html 4.01 Transitional//EN">
<html>
  <head>
    <title>Druckerstatus</title>
    <style type="text/css">
 
      body  { font-size: 8pt; 
              font-family: Verdana, Arial, Helvetica, MS Sans Serif; }
      table { font-size: 10pt; }
      h1    { font-size: 24pt; 
              margin-left: 190px; margin-top: 80px; margin-bottom: 0px; } 
 
      .Button { background-color: #aaa; color: #000; width: 100px;
                border: 1px solid #888; margin-right: 10px; }
 
    </style>
  </head>
  <body style="background-image:url(imgs/p2f.gif); 
               background-repeat: no-repeat;
               background-position: 0px 20px;
               background-color: white;">
 
    <h1>Druckerstatus</h1>
 
    <div style="margin-left: 192px;">
      Zuletzt aktualisiert: 
  <!--@@Datum-->
    </div>
 
    <br/>
 
    <form action="/cgi-bin/update.pl" method="get">
 
      <input type="hidden" name="Aktion" value="">
  <!--@@Liste-->
 
      <table cellpadding="3" cellspacing="0" border="1"
             style="border-collapse:collapse; border-style:solid;">
        <tr bgcolor="#EEEEEE">
          <td width="20"></td>
          <td width="20" height="20"></td>
          <td width="200"><b>Drucker</b></td>
          <td width="200"><b>Statusinformation</b></td>
        </tr>
 
  <!--@@Drucker-->
      </table>
 
      <br/>
      <br/>
 
      <input type="submit" class="button" value="Aktualisieren" 
                           onclick="this.form.Aktion.value = 'u'"
                           style="margin-right: 35px;">
      <input type="submit" class="button" value="Starten"
                           onclick="this.form.Aktion.value = 's'">
      <input type="submit" class="button" value="Anhalten"
                           onclick="this.form.Aktion.value = 't'">
      <input type="submit" class="button" value="Abbrechen"
                           onclick="this.form.Aktion.value = 'a'">
    </form>
 
  </body>
</html>

Das einzig Besondere an diesem HTML-Code sind die drei als Kommentare eingefügten Platzhalter, die bei der Bearbeitung durch ein Perl-Skript durch weiteren HTML-Code ersetzt werden. Soweit der HTML-Code.

Ein weiterer wichtiger Bestandteil der Lösung sind zwei kleine Perl-Skripte. Das erste Skript hat den Namen remote.pl und wird von der Startseite aus aufgerufen. Beim Aufruf wird der Name der Textdatei mit den Drucker- und Gateway-Namen als Parameter Drucker übergeben.

Am Anfang des Skriptes werden zwei Umsetzungstabellen (Hashes %Flaggen und %Druckerstatus) definiert. Diese dienen zum einen zur Auswahl einer den Status des Druckers anzeigenden farbigen Flagge und zum anderen zur Anzeige des Druckerstatus im Klartext.

Der erste Teil des Skripts dient der Aufbereitung der durch den HTTP-Server übergebenen Formularparameter. Die Komplexität dieser Anweisungen ist der aufwändigen Umkodierung von Steuerzeichen und in HTML selbst benutzten Zeichen innerhalb der Formularparameter geschuldet. Die Formularparameter werden als Paare aus Namen und Werten abwechselnd im Feld $Parameter untergebracht.

Anschliessend liest das Skript die HTML-Datei Template.html und sucht über einen regulären Ausdruck nach den Kommentarzeilen. Enthält der Kommentar den Text Datum, wird das aktuelle Datum und Uhrzeit ermittelt und statt des Kommentars in die Ausgabe eingesetzt. Enthält der Kommentar den Text Liste, wird ein Formularparameter statt des Kommentars erzeugt, der einfach den Namen der Textdatei weitergibt.

Das Entscheidende passiert, wenn der Kommentar den Text Drucker enthält. In diesem Fall wird die als Parameter übergebene Textdatei geöffnet, die Druckernamen werden gelesen und an eine Prozedur mit dem Namen DruckerStatus übergeben. Diese Prozedur ermittelt für jeden Drucker dessen aktuellen Status.

Wichtigster Bestandteil dieser Prozedur ist der Aufruf des mit der Installation von print2forms mitgelieferten Hilfsprogramms p2fRemote, Dieses Hilfsprogramm gestattet eine gegenüber dem Kontrollfeld etwas eingeschränkte Steuerung des Client/Gateway-Services via Kommandozeile - eine Beschreibung dieses Programms findet sich übrigens im vorangegangenen Kapitel.

Das Programm p2fRemote wird mit dem Parameter '-q' für eine Statusanfrage und dem als Prozedurparameter übergebenen Druckernamen aus der Textdatei aufgerufen. Aus dem Rückgabewert des Programms wird dann der HTML-Code für die Statusanzeige erzeugt.

remote.pl
 #!"C:\perl\bin\perl.exe"
 
 my %Flaggen = (
                 100 => "Grey.gif",
                 101 => "Yellow.gif",
                 102 => "Green.gif",
                 103 => "Cyan.gif",
                 104 => "Magenta.gif",
                 105 => "White.gif",
                 106 => "Red.gif",
                 110 => "Blue.gif",
                 111 => "Blue.gif",
                 112 => "Blue.gif",
                 113 => "Blue.gif",
                 114 => "Blue.gif",
                 115 => "Blue.gif",
                 116 => "Blue.gif",
                 117 => "Blue.gif"
               );     
 
 my %Druckerstatus = (
                       100 => "Angehalten",
                       101 => "Bereit",
                       102 => "Druckt",
                       103 => "Lizenzanfrage",
                       104 => "Druckeranfrage",
                       105 => "",
                       106 => "Fehler",
                       110 => "Eingriff erforderlich",                            
                       111 => "Papierstau",
                       112 => "Papierende",
                       113 => "Wenig Toner",
                       114 => "Abdeckung offen",
                       115 => "Offline",
                       116 => "Netzwerkverbindung verloren",
                       117 => "Ausgabefach voll"
                     );   
 
#------------------------------------------------------------------------------#
 
 if ($ENV{'REQUEST_METHOD'} eq 'GET')
   {
     my @ParameterListe = split(/&/, $ENV{'QUERY_STRING'});
     my $i = 0;
 
     foreach $Param (@ParameterListe)  
       {
         ($Parameter [$i], $Parameter [$i + 1]) = split(/=/, $Param);
         $i++;   
         $Parameter [$i] =~ tr/+/ /;
         $Parameter [$i] =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; 
         $Parameter [$i] =~ s/<!--(.|\n)*-->//g;      
         $i++;
       }
 
    open (TMPL, "<Template.html");
    print "Content-type: text/html\n\n";
 
    while (<TMPL>)
      {
        if (/^<!--\@@(.+)-->.*/)
          {
            if ($1 eq "Datum")
              {
                ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday)
                                                         = localtime (time);
                 $year += 1900;
                 $mon += 1;
 
                 printf "%02d.%02d.%04d %02d:%02d:%02d\n",
                        $mday, $mon, $year, $hour, $min, $sec;
              }
            elsif ($1 eq "Drucker")
                 {
                   open (PRT, "<$Parameter[1]");
 
                   while (<PRT>)
                     {
                       chomp ($_);
                       DruckerStatus ($_);
                     }
 
                   close (PRT);
                 }
               elsif ($1 eq "Liste")
                    {
                      print "<input type=\"hidden\" name=\"Drucker\" "
                          . "value=\"$Parameter[1]\">\n";
                    }
           }
         else { print $_; }
       }
 
     close (TMPL);
   }
 
 #------------------------------------------------------------------------------# 
 
 sub DruckerStatus 
   {
     $Status = system ("C:\\Programme\\(p2f)\\p2fRemote",
                        "-q",
                        "-n\"$_[0]\"") / 256;
 
     print "<tr>\n";
     print "  <td><input type=\"checkbox\" name=\"$_[0]\" /></td>\n"; 
     print "  <td align=\"center\">\n";
     print "    <img src=\"imgs/$Flaggen{$Status}\""
                 . " align=\"center\" alt=\"\" />\n"; 
     print "  </td>\n";
     print "  <td>$_[0]</td>\n";
     print "  <td>$Druckerstatus{$Status}</td>\n";
     print "</tr>\n";
   }
 
 #------------------------------------------------------------------------------#

Der bereits letzte Bestandteil der Lösung ist ein weiteres Skript update.pl, welches aufgerufen wird, wenn eine der vier Schaltflächen unterhalb der Tabelle angeklickt wird. Welche der Schaltflächen angeklickt wurde, entnimmt das Skript dem verdeckt übermittelten Formularparameter Aktion, der von jeder Schaltfläche via JavaScript entsprechend gesetzt wird.

Die erste Hälfte des Skripts dient wieder der Aufbereitung der durch den HTTP-Server übergebenen Formularparameter und ist ähnlich mit den Anweisungen aus dem ersten Skript. Allerdings bezieht sich hier die Aufbereitung auf die als Parametername übermittelten Druckernamen.

Der einfachste Fall für das Skript ist die Aktualisierung der Anzeige, weil dies lediglich den erneuten Aufruf des Skriptes remote.pl bedeutet. Dabei muss der als zweiter Parameter an das Skript übergebene Name der Textdatei erneut übergeben werden.

In allen anderen Fällen (Starten, Anhalten, Abbrechen) wird wieder das Programm p2fRemote aufgerufen, diesmal allerdings zusätzlich parametrisiert mit der durch die Schaltfläche vorgegebenen Aktion, also '-s' für Starten, '-t' für Anhalten und '-a' für Abbrechen. Der zweite Parameter ist wieder der Druckername.

update.pl
 #!"C:\perl\bin\perl.exe"
 
 if($ENV{'REQUEST_METHOD'} eq 'GET')
   {
     my @ParameterListe = split(/&/, $ENV{'QUERY_STRING'});
     my $i = 0;
 
     foreach $Param (@ParameterListe)  
       {
         ($Parameter [$i], $Parameter [$i + 1]) = split(/=/, $Param);
         $Parameter [$i] =~ tr/+/ /;
         $Parameter [$i] =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; 
         $Parameter [$i] =~ s/<!--(.|\n)*-->//g;
         $i++; $i++;
       }
 
     if ($Parameter [1] ne 'u')
       {
         while ($i > 2)
           {
             $i--; $i--;
 
             system ("C:\\Programme\\(p2f)\\p2fRemote",
                     "-$Parameter[1]",
                     "-n\"$Parameter[$i]\"");
           }
       }
 
    print "Location: http://$ENV{'HTTP_HOST'}/cgi-bin/remote.pl?"
        . "$Parameter[2]=$Parameter[3]\n\n";
   }
 
 exit 0;

Die Liste der ausgewählten Drucker (oder Gateways) wird über eine Schleife beginnend mit dem als letzten ausgewählten Drucker abgearbeitet. Sollte übrigens das Installationsverzeichnis von print2forms nicht C:\Programme\print2forms sein, ist der Programmaufruf in beiden Skripten entsprechend abzuändern.

Bisher nicht weiter erwähnt wurde, dass die Lösung noch eine Reihe von Graphikdateien benötigt. Zum einen die acht möglichen Flaggen zur Statusanzeige und natürlich das print2forms-Logo. Alle diese Dateien befinden sich in einem Ordner mit dem Namen imgs.

Die beiden Skripte remote.pl und update.pl gehören ins CGI-Verzeichnis des HTTP-Servers, genauso wie die Datei Template.html und alle Textdateien mit den Druckernamen - im Falle von Apache also nach C:\xxamp\cgi-bin. Die Seite print2forms.html wurde im Beispiel auf die Hauptebene des Servers kopiert, kann aber natürlich abhängig von den lokalen Gegebenheiten auch in jedem Unterverzeichnis liegen. Im Falle von Apache wird die Datei print2forms.html ins Verzeichnis C:\xampp\htdocs kopiert. Dorthin wird auch der Ordner imgs mit den Bildern kopiert.

Wenn die Datei print2forms.html für den HTTP-Server nicht prinzipiell als Startseite angegeben wird, erfolgt der Aufruf im Browser mit 'http://p2f.spe-systemhaus.de/print2forms.html', wobei der Servername natürlich an die lokalen Gegebenheiten angepasst werden muss.

Bereits mit so überschaubarem Aufwand ist also eine Lösung für die Druckerüberwachung und Steuerung im Intranet zu realisieren. Dadurch, dass nur der HTTP-Server Zugriff auf die Rechner mit der print2forms-Installation hat, ist keine aufwändige Konfiguration von Zugriffsrechten erforderlich. Für die HTTP-Zugriffe kann auf das zumeist bereits im Intranet vorhandene Zugriffskontrollsystem zurückgegriffen werden, und auf dem Rechner des jeweiligen Mitarbeiters ist nur ein Internet-Browser notwendig.

Zum Abschluss sei noch kurz erwähnt, was zu ändern ist, wenn print2forms nicht auf dem gleichen Rechner installiert ist, wie der Web-Server. In diesem Fall muss der Web-Server die Ausführung des Programms p2fRemote auf dem Rechner mit der print2forms-Installation veranlassen. Dazu kann von www.sysinternals.com das Paket PsTools geladen werden, das ein Programm mit dem Namen PsExec.exe enthält.

PsExec.exe führt ein als Parameter übergebenes Kommando auf einem anderen Rechner aus. Eine Beschreibung des Programms findet sich in der der Tool-Sammlung beiliegenden Dokumentation. Im Skript remote.pl muss demnach der Programmaufruf wie folgt geändert werden:

 $Status = system ("C:\\Programme\\Sysinternals\\psexec",
                    "\\\\10.1.17.7",
                    "-u",
                    "Benutzer",
                    "-p",
                    "Passwort",
                    "D:\\Programme\\(p2f)\\p2fRemote.exe", 
                    "-q",
                    "-n\"$_[0]\"") / 256;

Dem entsprechend sieht der Programmaufruf im Skript update.pl dann so aus:

 system ("C:\\Programme\\Sysinternals\\psexec",
         "\\\\10.1.17.7",
         "-u",
         "Benutzer",
         "-p",
         "Passwort",
         "D:\\Programme\\(p2f)\\p2fRemote",                
         "-$Parameter[1]",
         "-n\"$Parameter[$i]\"");

Dabei wird angenommen, dass das Tool PsExec im Verzeichnis C:\Programme\Sysinternals liegt, dass der andere Rechner die IP-Adresse '10.1.17.7' hat, und dass dort die print2forms-Installation auf D:\Programme\print2forms erfolgte. Ein weiterer wesentlicher Punkt ist, dass auch eventuell die Anmeldung als Benutzer auf dem anderen Rechner notwendig ist.

Zu dieser Lösung ist allerdings zu sagen, dass sie sehr teuer ist, im Sinne von Prozessor- und Netzwerklast (ca. 300 KByte / Zugriff). Auch aus Sicht der Systemsicherheit ist das Erlauben von Remote-Zugriffen durch andere Rechner nicht immer gern gesehen. Dieses Vorgehen sollte daher wirklich nur in Ausnahmefällen zum Einsatz kommen.

Bemerkungen