PHP-Script zum Auslesen des Telefonbuches?

qualidat

Neuer User
Mitglied seit
25 Apr 2011
Beiträge
84
Punkte für Reaktionen
3
Punkte
8
Kennt jemand ein funktionierendes PHP-Script (oder einen Link dazu), mit dem es möglich ist, unter den aktuellen Fritz-OS-Versionen, das interne Telefonbuch auszulesen? Meine bisherige Version hat ca. 2017 die Funktion eingestellt (wohl nach einem Update) und es nun mal endlich Zeit, das Problem anzufassen.
Bis Dato hatte ich ein sehr schönes PHP-Schript, welches von meinen Grandstream-Tischtelefonen (über einen Trick mit .httaccess) als .xml aufgerufen wurde und on-the-fly (also ohne Datei dazwischen) die Einträge aus dem TB der FB auslesen und in die Telfone einspielen konnte. Den Zustand möchte ich endlich wieder herstellen ...

Danke für Tips.
 
Zeig mal deiner alten Version, dann könnte man die ja anpassen.
 
Ok, danke mache ich hiermit. Es erscheint diese Fehlermeldung:

Fatal error: Uncaught SoapFault exception: [s:Client] UPnPError in /volume1/web/ptx/book.php:97 Stack trace: #0 /volume1/web/ptx/book.php(97): SoapClient->__call('GetPhonebook', Array) #1 {main} thrown in /volume1/web/ptx/book.php on line 97

In der Fritzbox sind die Einstellungen "Zugriff für Anwendungen zulassen" und "Statusinformationen über UPnP übertragen" gesetzt. Der Code hängt als ZIP an.
 

Anhänge

  • gs_phonebook.php.zip
    1.3 KB · Aufrufe: 17
Auf welcher Box/Version ist das Script denn gelaufen?
Wahrscheinlich fehlt der Authentication Header in der Soapmessage. Dein Script schickt folgendes:
Code:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:dslforum-org:service:X_AVM-DE_OnTel:1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
    <ns1:GetPhonebook>
      <NewPhonebookID xsi:nil="true"/>
    </ns1:GetPhonebook>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Die neueren Boxen verlangen eine Authentifzierung, vor dem "body" gehört sowas rein:
Code:
<SOAP_ENV:Header>
    <h:ClientAuth xmlns:h="http://soap-authentication.org/digest/2001/10/" s:mustUnderstand="1">
      <Nonce>$nonce</Nonce>
      <Auth>$auth</Auth>
      <UserID>$user</UserID>
      <Realm>$realm</Realm>
    </h:ClientAuth>
  </SOAP_ENV:Header>
$nonce, $auth und $realm müssen vorher durch eine Authentifizierungsanfrage bei der Box ermittelt werden.
 
@qualidat

Du kannst auch das ausprobieren.

Wenn Du noch Anregungen suchst, wie mit den XML-Files jongliert werden kann* (z.B. xPath), dann findest du hier oder hier bestimmt was Passendes.

Mich würde die Konvertierung in das Grandstream-Format** & Uploading interessieren. Kannst Du das mal veröffentlichen?

MfG

Black Senator



* z.B, statt parsen von XML-Zeilen solltest Du lieber mit SimpleXML durch das File browsen - oder besser "brausen"
** ist das hier die passende XML-Struktur für das Grandstream? Wovon hängt <accountindex> und <groupid> ab bzw. wird durch was bestimmt? Und Umlaute veträgt es nicht, wenn ich dein Snippet richtig interpretiere - oder?
 
Zuletzt bearbeitet:
  • Like
Reaktionen: Chatty
@Black Senator

Vielen Dank, ich bin gerade dabei, mir deinen Code anzusehen und damit zu experimentieren ...

Zu deiner Frage: Also das, was mein Script da produziert, wird vom Grandsteam als XML-Importformat akzeptiert. Nachdem das so war, habe ich es nicht weiter vertieft. Als Editor verwende ich die Fritzbox-GUI. Umlaute müssen auf dem Weg aus der Fritzbox zum Grandstream umcodiert werden, wie im Script zu sehen ist. In der FB können sie ganz normal verwendet weden. Der notwendige Aufbau des Grandsream-XML ist im Anhang zu lesen, speziell auf Seite 14. Account-ID bezieht sich wohl auf die verschiedenen SIP-Connections (bis zu 6 beim 2020), ich kann aber nur aus einem globalen Verzeichnis am Telefon wählen, da ich immer die Nummer heraussuche, bevor ich einen SIP-Channel aktiviere. Gruppen unterstützt das 2020 m.W. ebenfalls nicht. Ich habe nicht ausprobiert, ob man diese XML-Tags auch weglassen kann.

In der GUI des Telefones ist eingerichtet, dass es ein XML-File in regelmäßigen Abständen importiert. Also das Phone ist der aktive Part, es muss nichts hochgeladen werden. Allerdings erwartet es ein .xml. Das Script wird also mit dem Namen und der Endung "gs_phonebook.xml" gespeichert, obwohl es eigentlich ein .php ist. Durch einen Eintrag in eine im gleichen Ordner enthaltene .htaccess-Datei bringe ich den Apache (auf einer Synology) dazu, das File trotzdem als PHP zu interpretieren. So "liest" das Grandstream die während der Ausführung des PHP per echo ausgegebenen XML-Tags scheinbar wie aus einem realen File ...

Zum Code-Prinzip: Um das Script mögl. kurz zu halten und einfach weitergeben zu können und die 99% Nicht-Programmierer nicht mit zus. Ordnern und Pfaden zu überfordern, habe ich ganz bewusst auf Strukturen wie Klassen, zus. Ordner und externe Dateien verzichtet und ein gaaanz simples plain file gemacht, in voller Absicht.
 

Anhänge

  • gxp_wp_xml_phonebook.pdf
    253.4 KB · Aufrufe: 4
Zuletzt bearbeitet:
Hallo qualidat,

das mit PHP als XML und .htaccess habe ich nicht verstanden - klingt nach einem Weg, aber eher nach einem komplizierten Umweg.

Da ich an Konvertierungen Spaß habe (habe viele System-Migratonen gemacht) hat es mich in den Fingern gejuckt und ich habe mal auf die Schnelle ein Ad-On für meinen Fork von carddav2fb geschrieben. Ich dachte, dass könnte ein zusätzliches Feature werden.

Wenn Du magst, dann gebe ich Dir mehr Details.

Grüße

Black Senator
 
Ok, ich erkläre das mit XML, PHP und .htaccess:

Wenn von einem Webserver (bei dem PHP aktiviert ist), ein File mit der Endung .php angefordert wird, wird das ja (im Gegensatz zu einem .html-File) nicht direkt ausgeliefert, sondern durch den PHP-Interpreter gezerrt und dabei nur das, was z.B. per "echo()" ausgegeben wird, gelangt zum Browser bzw. dem, der den Request gemacht hat. Nun will aber ein Grandstream ausschließlich .xml-Files ziehen, ein .php wird ignoriert bzw. kann auch nirgendwo eingetragen werden. Der Dateiname ist intern fest verdrahtet, es kann nur die URL in der GUI konfiguriert werden.

Also am Grandstream kann ich nix drehen, aber am Webserver. Durch einen einfachen 2-zeiligen Eintrag in eine Text-Datei mit dem Namen .htaccess (ohne Endung), die im selben Verzeichnis wie das PHP-Script liegt, kann ich dafür sorgen, dass der Apache jetzt auch eine Datei mit der Endung .xml nicht mehr direkt ausliefert, sondern wie ein .php-File durch den PHP-Interpreter leitet. Dazu muss das PHP-Script die Dateiendung .xml bekommen. Das Grandstream "denkt" nun, es würde direkt die xml-Datei laden, statt dessen erhält es nur die Ausgaben aus dem PHP-Script und "merkt" es nicht.

Dadurch erspare ich mir, das XML-File als reale Datei zu erzeugen - wer sollte das auch wann tun? Das müsste manuell oder per Cronjob getan werden. So genügt allein der automatische Abruf des XML-Files (was in Wirklichkeit ein PHP-Script ist) durch das Telefon.

hier der Inhalt der .haccess Datei:

RewriteEngine On
RewriteRule ^gs_phonebook.xml$ gs_phonebook.php [L]

Wenn das umbenannte PHP-Script und die .htaccess zusammen in einem eigenen Ordner liegen, wirkt sich das auch nicht auf andere "echte" XML-Dateien aus.

Ich hätte eine Bitte: Könntest du mein Script so modifizieren, dass es wieder funktioniert? Ich erhalte bei den Versuchen mit deiner Klasse immer "SoapFault exception: [HTTP] Could not connect to host in /volume1/ ..." wie bisher auch. Ausserdem wird mir dann das Projekt zu groß, ich habe auch nicht soo viel Zeit ... ich fände es in dem Falle gut, wenn es nur ein simples plain file bliebe.
 
Zuletzt bearbeitet:
Hallo,

okay, Weg und Lösung habe ich verstanden. Bischen crazy, aber wenn es so ohne Umwege funktioniert, dann ist es zwar quick `n dirty aber simple.

Also, den SOAP-Fehler zu bereinigen, kann ich nicht einfach das Script anpassen - dafür sind zu viele Variablen im Spiel (z.B. PHP Version, ...). Ich glaube, hier im Forum ist das auch zu speziell.
Meine klare Empfehlung für derartige Coding-Fragen ist stack overflow! Da wird einem schnell geholfen, weil eine riesige Community weltweit dahinter steht - die zudem - weil nicht typisch Deutsch - einen nicht als erstes zusammenscheißt (wie auch hier öfters).

Last but not least:
Ich würde das XML aber mit zeitgemäßen PHP-Werkzeugen (z.B. SimpleXMLElement) erstellen.
Ich gebe mal ein Beispiel, das ein bischen komplexer ist, aber dann ist es - glaube ich - besser nachzuvollziehen (man kann ja das wesentliche auf deinen OnePager herunterstrippen):
In carddav2fb gibt es bereits eine Routine, welche Telfonbucheinträge in 1:1-Beziehung als Array liefert, den habe ich minimal ergänzt und bekomme so folgende Telefonbuchdaten - im folgenden in der Variablen $contacts - (Ausschnitt):
Code:
    [54] => Array
        (
            [number] => 03302xxxxxx
            [id] => 0
            [type] => home
            [quickdial] => 8
            [vanity] =>
            [prio] => 1
            [name] => Bxxxxx, Dr. Claudia
        )
    [55] => Array
        (
            [number] => 0157xxxxxxx
            [id] => 1
            [type] => mobile
            [quickdial] =>
            [vanity] =>
            [prio] =>
            [name] => Bxxxxx, Dr. Claudia
        )
    [56] => Array
        (
            [number] => 0511344144
            [id] => 0
            [type] => work
            [quickdial] =>
            [vanity] =>
            [prio] => 1
            [name] => Bildungsverein Hannover
        )
Um daraus eine passende XML zu erzeugen habe ich folgendes (hier vereinfacht) codiert:
Code:
        $adressBook = new simpleXMLElement("<?xml version='1.0' encoding='utf-8'?><AddressBook />");
        foreach ($contacts as $contact) {
            $number = (string)$contact['number'];
            $realName = htmlspecialchars(str_replace('&', '&amp;', $contact['name']));
            $parts = explode(', ', $realName);
            if (count($parts) !== 2) {
                $lastName = $realName;
                $firstName = '';
            } else {
                $lastName = $parts[0];
                $firstName = $parts[1];
            }
            $contact = $adressBook->addChild('Contact');
            $contact->addChild('LastName', $lastName);
            $contact->addChild('FirstName', $firstName);
            $phone = $contact->addChild('Phone');
            $phone->addChild('phonenumber', $number);
            $phone->addChild('accountindex', '0');
            $groups = $contact->addChild('Groups');
            $groups->addChild('groupid', '0');
        }
Im Ergebnis wird dann die XML erzeugt, welche als lokales File $adressbook->asXML('gs_phonebook.xml') so aussieht:
Code:
<?xml version="1.0" encoding="utf-8"?>
<AddressBook>
    ...
    <Contact>
        <LastName>Bxxxxx</LastName>
        <FirstName>Dr. Claudia</FirstName>
        <Phone>
            <phonenumber>03302xxxxx</phonenumber>
            <accountindex>0</accountindex>
        </Phone>
        <Groups>
            <groupid>0</groupid>
        </Groups>
    </Contact>
    <Contact>
        <LastName>Bxxxxx</LastName>
        <FirstName>Dr. Claudia</FirstName>
        <Phone>
            <phonenumber>0157xxxxxx</phonenumber>
            <accountindex>0</accountindex>
        </Phone>
        <Groups>
            <groupid>0</groupid>
        </Groups>
    </Contact>
    <Contact>
        <LastName>Bildungsverein Hannover</LastName>
        <FirstName/>
        <Phone>
            <phonenumber>0511344144</phonenumber>
            <accountindex>0</accountindex>
        </Phone>
        <Groups>
            <groupid>0</groupid>
        </Groups>
    </Contact>
    ...
</AddressBook>

Dein Output wäre dann einfach echo $adressbook->asXML();
Das FRITZ!Box Telefonbuch würde ich auch nicht empfehlen mit explode() zu zerlegen, sondern das XML entweder mit foreach() durchlaufen oder mit xPath('//contact') aufdröseln.

Viel Erfolg

Black Senator
 
Zuletzt bearbeitet:
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.