Henryk Plötz: Anmeldung mit PHP

Beitrag lesen

Moin

Klingt gut...leider hab ich keine Ahnung noch Fantasie wie ich das realisiern soll...gibts da irgendwo ein Beispiel?

Nicht dass ich wüsste. Ich kann jedoch die Idee mal _grob_ skizzieren.
Also du hast ein PHP-Skript, dass wir jetzt einfach mal bla.php nennen. Ausserdem hast du deinen Server so konfiguriert, dass http://deinserver/bla.php/lalala funktioniert, bla.php aufruft und /lalala in $PATH_INFO legt (das sollte eigentlich default sein, zumindest bei PHP als Apache-Modul).

Dann sieht dein Skript ungefähr so aus (+/- ein paar Features)

<?php

// *** Erstmal Benutzername und Passwort vom User erfragen.
 // Damit das ganze in diesem Beispiel einfach wird, nehm ich $PHP_AUTH_USER und $PHP_AUTH_PW, du kannst das sicherlich trivial auf ein Anmeldeformular ummünzen

$pw = $PHP_AUTH_PW;
 $user = $PHP_AUTH_USER;
 if($pw == "" || $user == "") {
  header('WWW-Authenticate: Basic realm="Mein lustiger Webmailverbieger"');
  header("HTTP/1.0 401 Unauthorized");
  die("Bitte Username/PW angeben");
 }

// *** Nun da wir die nötigen Daten haben, machen wir eine Verbindung auf
 $fh = fsockopen("www.domain.ch", 2095, $errno, $errstr, 10);
 if(!$fh) die("Verbindung konnte nicht aufgebaut werden. Fehlerursache ($errno) $errstr. (Wenn keine Fehlerursache angegeben wird, ist wahrscheinlich die Namensauflösung schuld)");

// *** OK, wir senden eine einfache HTTP-Anfrage
 // Ich hab mir grad überlegt, dass $PATH_INFO doch nicht das gelbe vom Ei ist
 $mypathinfo = substr($REQUEST_URI, strlen($SCRIPT_NAME));
 if($mypathinfo == "") {
  // Hmm, der Browser sollte wenigstens / an den Skriptnamen anhängen, sonst geht das mit den unten erwähnten relativen Referenzen schief
  header("Location: bla.php/");
  exit();
 }

// Abfrage zusammenbauen
 $abf = "GET ".$mypathinfo." HTTP/1.0\r\n";
 $abf .= "Authorization: Basic ".base64_encode($user.":".$pw)."\r\n";

// Jetzt die Header die der Browser gesendet hat noch dranhängen, ausgenommen den Authorization-Header, den der Browser extra für uns gesendet hat, sowie einen evt. Keep-Alive Header, da wir noch kein Keep-Alive können

$abf .= "Connection: Close\r\n"; // Kein Keep-Alive

$hdr = getallheaders();
 foreach($hdr as $hdrnam => $hdrval)
  if($hdrnam != "Authorization" && $hdrnam != "Connection")
   $abf .= $hdrnam . ": " .$hdrval . "\r\n";

// Anmerkung: Wenn dein Zielrechner und der Rechner auf dem dieses Skript läuft nicht identisch sind, musst du höchstwahrscheinlich auch noch den Host:-Header ändern

$abf .= "\r\n";

// Abfrage senden
 fwrite($fh, $abf);

$meinehdr = Array(); // Ordentlich Leermachen
 $status = ""; $ctype = "";

// Ergebnis einlesen
 do { // Hauptschleife für die Header
  $line = "";
  while($tmp = fgets($fh, 1024)) { // Unterschleife zum Einlesen beliebig langer Zeilen
   $line .= $tmp;
   if(strlen($tmp) < 1024 || $tmp[1023] == "\n") break; // Eine Zeile eingelesen
  }

// Lasst uns herausfinden, was der Server da so sendet
  $line = trim($line); // Überflüssige Whitespace in die Tonne
  if(preg_match("!^HTTP/\d.\d\s+(\d{3})!", $line, $matches)) {
   // Das ist eine HTTP-Statuszeile
   if($matches[1] == "401") {
    // Tja, das Passwort/der Benutzername/die Kombination ist wohl falsch
    // Hier dein Anmeldeformular nochmal ausgeben (dafür willst du natürlich eine Funktion bauen)
    header('WWW-Authenticate: Basic realm="Mein lustiger Webmailverbieger"');
    header("HTTP/1.0 401 Unauthorized");
    die("Bitte Username/PW angeben");
   }
   // Der Rest ist wohl ok
   $status = $matches[1]; // Status merken
   $meinehdr[] = $line; // Header merken
  } else if(preg_match("!^([^:]*):\s+(.*)$!", $line, $matches)) {
   // Das ist ein sonstiger Header, da interessiert uns später nur der Content-Type
   if($matches[1] == "Content-Type") $ctype = $matches[2];
   $meinehdr[] = $line;
  } else if($line == "") break; // Eine Leerzeile bedeutet das Ende der Header, die Schleife kann beendet werden
 } while(!feof($fh));

// Die vorher gespeicherten Header ausgeben
 foreach($meinehdr as $meinhdr) header($meinhdr);

// Wenn der Content-Typ nicht text/html, sondern zum Beispiel ein Bild, ist, dann jetzt einfach den Rest der übertragenen Daten ausgeben und dieses Skript beenden
 if($ctype != "text/html") {
  fpassthru($fh);
  exit;
 }

// Jetzt wirds lustig. Wir lesen den Rest in einen String ein, um dann die Links und Bildreferenzen etc. zu verwurschteln

$inhalt = "";
 while(!feof($fh) && $tmp = fread($fh, 2048)) $inhalt.=$tmp;

// Das herausfinden von Verweisen etc. ist leider nicht ganz so einfach wie man meinen möchte. Wenn du es allgemein haben willst, findest du im Forumsarchiv einen RegExp der alles findet was wie ein gültiger URL aussieht (such mal nach "prospero", das Wort sollte nur in diesem Regexp - und einigen Verweisen darauf - vorkommen). Das gefundene kannst du dann umschreiben. Ich wähle hier mal den Weg, alles herauszusuchen was wie eine Referenz in HTML aussieht und das gegebebenfalls umzuschreiben. Wenn in deinem Zielskript noch andere Sachen (etwa in JavaScript) vorkommen, musst du diese RegExpe erweitern oder auf die allgemeine Variante umsteigen.
 // Wahrscheinlich reicht es aus, nur absolute Verweise umzubauen, die relativen müsste der Browser schon von selbst in den virtuellen URL-Baum deines Skriptes einbauen können

$quellregexps = Array('!<a(.*)href\s*=\s*"(?:http://www.mydomain.ch:2095)?/([^"]*)"(.*)>!Ui',
                       '!<img(.*)src\s*=\s*"(?:http://www.mydomain.ch:2095)?/([^"]*)"(.*)>!Ui'); // BTW: Wenn der Autor des anderen Skriptes unfähig war seine href-Parameter in " einzuschliessen, dann darfst du ihn gerne hauen und kannst diesen Ansatz vergessen
 $zielregexps = Array('<a\1href="http://deinserver/bla.php/\2"\3>',
                       '<img\1src="http://deinserver/bla.php/\2"\3>');

$inhalt = preg_replace($quellregexps, $zielregexps, inhalt);

echo $inhalt;

?>

Hmmpf, aus "grob" ist wohl nix geworden. Immerhin habe ich das Skript grade lokal getestet und mit normalen Webseiten funktioniert es (erstaunlicherweise :) völlig problemlos. Wie gesagt, bei Frames oder JavaScript-Spielereien musst du die Regulären Ausdrücke anpassen. Ausserdem müsste man sich auch noch mit Location:-Headern befassen, die das andere Skript zum Umleiten sendet, und deren Ziele ebenfalls auf den Aufruf deines Skriptes umbiegen.

--
Henryk Plötz
Grüße aus Berlin