Schlagwort-Archive: PHP

adlerweb // BitBastelei 2023-01-12 22:25:16

Me: Fragt was man heute so für ne holt, wenn am Ende etwas mit und rausfallen soll
Person: *nennt Java-Framework*

Ich sollte wohl noch spezifizieren, dass ich zwar durchaus "dreckige Dinge" wie oder akzeptiere, aber sicher nicht in die -Jauchegrube absteigen werde. Da setz ich ja lieber nen mit -Backend hin als sowas. XD

[Gentoo] Apache 2.4.26 + WordPress: Invalid post type

Hmk? After the latest system updates on Gentoo I noticed WordPress was no longer able to open any kind of post list („invalid post type“), edit posts (you do not have access to this kind of post) or create posts (form shown, but right sidebar missing). Not quite uncommon, but this time it was no broken plugin – I could reproduce the same behavior with several other WordPress instances with different versions – even a fresh install. That’s a new one.

After some probing around I’m fairly certain Apache 2.4.26 is the culprit, after downgrading to 2.4.25 the problem disappeared. All other downgrades like PHP 7.0.15 instead of PHP 7.0.19 didn’t show any effect. I yet have to confirm which combination ultimately triggers the error (FPM? Event-MPM?), but if you have similar errors: Here is your starting point…

FRITZ!Box per Konsole auslesen (PHP/TR64)

Statistiken sind toll. Wäre fein, wenn man auch der FRITZ!Box einiges entlocken könnte. Das Zauberwort lautet TR64 und ist über HTTP/SOAP im LAN erreichbar. Hierzu müssen in den Netzwerkeinstellungen die Anwendungszugriffe und Statusinformationen aktiv sein.

Bild: https://www.adlerweb.info/blog/wp-content/uploads/2017/01/fbox-300×137.png

Allgemeine Infos wie die aktuell verwendete Bandbreite lassen sich von jedem Abrufen, andere Bereiche konnte ich bisher nur über /control abrufen – hier werden die Zugangsdaten eines FB-Nutzers benötigt.

Über das Protokoll lassen sich neben IP, Verbindungsstatus und Bandbreiten auch erweiterte Infos wie Dämpfungen & Co aufzeichnen. Technisch kann man sogar Aktionen wie einen Reconnect oder komplette Konfigurationsänderungen durchführen, das würde hier jedoch den Rahmen sprengen. Einige Infos gibt es in der Wiki von WeHaveMoreFun oder das ausgiebigere Perl-Modul von FHem.

Hier mal mein Notizzettel, welcher eine Abfrage per PHP erlaubt:

<?php



function FbSOAP($url, $urn, $method='GetInfo', $user='', $pass='') {
    $parameter = array(
        'location'   => $url,
        'uri'        => $urn,
        'noroot'     => True
    );

    if($user != '') $parameter['login'] = $user;
    if($pass != '') $parameter['password'] = $pass;

    $client = new SoapClient(
        null,
        $parameter
    );
    $status = $client->$method();
    return $status;
}

$host = 'http://fritz.box:49000';
$user = 'nutzer';
$pass = 'geheim';

//Aktuell verwendete Bandbreite, Traffic seit Boot, DNS-Konfiguration (kein Passwort nötig)
var_dump(FbSOAP($host.'/igdupnp/control/WANCommonIFC1', 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1', 'GetAddonInfos'));

/*
  ["NewByteSendRate"]=>
  string(5) "20709"
  ["NewByteReceiveRate"]=>
  string(5) "21372"
  ["NewPacketSendRate"]=>
  string(1) "0"
  ["NewPacketReceiveRate"]=>
  string(1) "0"
  ["NewTotalBytesSent"]=>
  string(9) "986828869"
  ["NewTotalBytesReceived"]=>
  string(10) "1140825575"
  ["NewAutoDisconnectTime"]=>
  string(1) "0"
  ["NewIdleDisconnectTime"]=>
  string(2) "30"
  ["NewDNSServer1"]=>
  string(14) "217.237.15.1"
  ["NewDNSServer2"]=>
  string(14) "217.237.14.2"
  ["NewVoipDNSServer1"]=>
  string(14) "217.237.15.1"
  ["NewVoipDNSServer2"]=>
  string(14) "217.237.14.2"
  ["NewUpnpControlEnabled"]=>
  string(1) "0"
  ["NewRoutedBridgedModeBoth"]=>
  string(1) "1"
*/

//Verbindungsstatus und Typ (kein Passwort nötig)
var_dump(FbSOAP($host.'/igdupnp/control/WANCommonIFC1', 'urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1', 'GetCommonLinkProperties'));

/*
Achtung - MaxBitRate ist nicht konsistent

array(4) {
  ["NewWANAccessType"]=>
  string(3) "DSL"
  ["NewLayer1UpstreamMaxBitRate"]=>
  string(7) "1073000"
  ["NewLayer1DownstreamMaxBitRate"]=>
  string(7) "2304000"
  ["NewPhysicalLinkStatus"]=>
  string(2) "Up"
}
*/

//DSL-Sync-Status, DSL-Datenraten und Dämpfungen
var_dump(FbSOAP($host.'/upnp/control/wandslifconfig1', 'urn:dslforum-org:service:WANDSLInterfaceConfig:1', 'GetInfo', $user, $pass));

/*
array(15) {
  ["NewEnable"]=>
  string(1) "1"
  ["NewStatus"]=>
  string(2) "Up"
  ["NewDataPath"]=>
  string(11) "Interleaved"
  ["NewUpstreamCurrRate"]=>
  string(3) "224"
  ["NewDownstreamCurrRate"]=>
  string(4) "2304"
  ["NewUpstreamMaxRate"]=>
  string(4) "1196"
  ["NewDownstreamMaxRate"]=>
  string(4) "4736"
  ["NewUpstreamNoiseMargin"]=>
  string(3) "270"
  ["NewDownstreamNoiseMargin"]=>
  string(3) "130"
  ["NewUpstreamAttenuation"]=>
  string(3) "290"
  ["NewDownstreamAttenuation"]=>
  string(3) "490"
  ["NewATURVendor"]=>
  string(8) "41564d00"
  ["NewATURCountry"]=>
  string(4) "0400"
  ["NewUpstreamPower"]=>
  string(3) "502"
  ["NewDownstreamPower"]=>
  string(3) "500"
}
*/

//DSL-Fehlerstatistiken
var_dump(FbSOAP($host.'/upnp/control/wandslifconfig1', 'urn:dslforum-org:service:WANDSLInterfaceConfig:1', 'GetStatisticsTotal', $user, $pass));

/*
array(15) {
  ["NewReceiveBlocks"]=>
  string(1) "0"
  ["NewTransmitBlocks"]=>
  string(1) "0"
  ["NewCellDelin"]=>
  string(1) "0"
  ["NewLinkRetrain"]=>
  string(1) "9"
  ["NewInitErrors"]=>
  string(1) "0"
  ["NewInitTimeouts"]=>
  string(1) "0"
  ["NewLossOfFraming"]=>
  string(1) "0"
  ["NewErroredSecs"]=>
  string(3) "637"
  ["NewSeverelyErroredSecs"]=>
  string(2) "54"
  ["NewFECErrors"]=>
  string(7) "3932348"
  ["NewATUCFECErrors"]=>
  string(1) "9"
  ["NewHECErrors"]=>
  string(4) "7289"
  ["NewATUCHECErrors"]=>
  string(2) "10"
  ["NewCRCErrors"]=>
  string(4) "1635"
  ["NewATUCCRCErrors"]=>
  string(2) "13"
}
*/

//Gerätemodell, Softwareversion, Seriennummer, Logfile
var_dump(FbSOAP($host.'/upnp/control/deviceinfo', 'urn:dslforum-org:service:DeviceInfo:1', 'GetInfo', $user, $pass));

/*
array(12) {
  ["NewManufacturerName"]=>
  string(3) "AVM"
  ["NewManufacturerOUI"]=>
  string(6) "00040E"
  ["NewModelName"]=>
  string(28) "FRITZ!Box Fon WLAN 7390 (UI)"
  ["NewDescription"]=>
  string(37) "FRITZ!Box Fon WLAN 7390 (UI) 84.06.51"
  ["NewProductClass"]=>
  string(9) "FRITZ!Box"
  ["NewSerialNumber"]=>
  string(12) "C02506210000"
  ["NewSoftwareVersion"]=>
  string(8) "84.06.51"
  ["NewHardwareVersion"]=>
  string(28) "FRITZ!Box Fon WLAN 7390 (UI)"
  ["NewSpecVersion"]=>
  string(3) "1.0"
  ["NewProvisioningCode"]=>
  string(0) ""
  ["NewUpTime"]=>
  string(7) "2375523"
  ["NewDeviceLog"]=>
  string(15974) "03.01.17 02:32:46 Internetverbindung wurde erfolgreich hergestellt. IP-Adresse: xxxx, DNS-Server: 217.237.150.xx und 217.237.148.xx, Gateway: 87.186.225.xx, Breitband-PoP: xxx05-asr
03.01.17 02:32:46 Internetverbindung wurde getrennt.
03.01.17 02:32:43 Die Internetverbindung wird kurz unterbrochen, um der Zwangstrennung durch den Anbieter zuvorzukommen.
…
*/

//Software-Update verfügbar?
var_dump(FbSOAP($host.'/upnp/control/userif', 'urn:dslforum-org:service:UserInterface:1', 'GetInfo', $user, $pass));
/*
array(9) {
  ["NewUpgradeAvailable"]=>
  string(1) "0"
  ["NewPasswordRequired"]=>
  string(1) "0"
  ["NewPasswordUserSelectable"]=>
  string(1) "1"
  ["NewWarrantyDate"]=>
  string(19) "0001-01-01T00:00:00"
  ["NewX_AVM-DE_Version"]=>
  string(0) ""
  ["NewX_AVM-DE_DownloadURL"]=>
  string(0) ""
  ["NewX_AVM-DE_InfoURL"]=>
  string(0) ""
  ["NewX_AVM-DE_UpdateState"]=>
  string(8) "NoUpdate"
  ["NewX_AVM-DE_LaborVersion"]=>
  string(0) ""
}
*/

//WLAN-Konfiguration und Status
var_dump(FbSOAP($host.'/upnp/control/wlanconfig1', 'urn:dslforum-org:service:WLANConfiguration:1', 'GetInfo', $user, $pass));

/*
array(17) {
  ["NewEnable"]=>
  string(1) "0"
  ["NewStatus"]=>
  string(8) "Disabled"
  ["NewMaxBitRate"]=>
  string(4) "Auto"
  ["NewChannel"]=>
  string(2) "13"
  ["NewSSID"]=>
  string(17) "ADLERWEB-TEST"
  ["NewBeaconType"]=>
  string(3) "11i"
  ["NewMACAddressControlEnabled"]=>
  string(1) "0"
  ["NewStandard"]=>
  string(1) "n"
  ["NewBSSID"]=>
  string(17) "C0:25:06:00:00:00"
  ["NewBasicEncryptionModes"]=>
  string(4) "None"
  ["NewBasicAuthenticationMode"]=>
  string(4) "None"
  ["NewMaxCharsSSID"]=>
  string(2) "32"
  ["NewMinCharsSSID"]=>
  string(1) "1"
  ["NewAllowedCharsSSID"]=>
  string(95) "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"
  ["NewMinCharsPSK"]=>
  string(2) "64"
  ["NewMaxCharsPSK"]=>
  string(2) "64"
  ["NewAllowedCharsPSK"]=>
  string(22) "0123456789ABCDEFabcdef"
}
*/

//DSL-Status und Konfiguration
var_dump(FbSOAP($host.'/upnp/control/wandsllinkconfig1', 'urn:dslforum-org:service:WANDSLLinkConfig:1', 'GetInfo', $user, $pass));

/*
array(9) {
  ["NewEnable"]=>
  string(1) "1"
  ["NewLinkStatus"]=>
  string(2) "Up"
  ["NewLinkType"]=>
  string(5) "PPPoE"
  ["NewDestinationAddress"]=>
  string(9) "PVC: 1/32"
  ["NewATMEncapsulation"]=>
  string(3) "LLC"
  ["NewAutoConfig"]=>
  string(1) "0"
  ["NewATMQoS"]=>
  string(3) "UBR"
  ["NewATMPeakCellRate"]=>
  string(1) "0"
  ["NewATMSustainableCellRate"]=>
  string(1) "0"
}
*/

//DSL-Statistiken
var_dump(FbSOAP($host.'/upnp/control/wandsllinkconfig1', 'urn:dslforum-org:service:WANDSLLinkConfig:1', 'GetStatistics', $user, $pass));

/*
array(4) {
  ["NewATMTransmittedBlocks"]=>
  string(1) "0"
  ["NewATMReceivedBlocks"]=>
  string(1) "0"
  ["NewAAL5CRCErrors"]=>
  string(1) "0"
  ["NewATMCRCErrors"]=>
  string(1) "0"
}
*/

//PPP-Status (incl. externer IP!)
var_dump(FbSOAP($host.'/upnp/control/wanpppconn1', 'urn:dslforum-org:service:WANPPPConnection:1', 'GetInfo', $user, $pass));

/*
BitRate auch hier nicht nachvollziehbar

array(31) {
  ["NewEnable"]=>
  string(1) "1"
  ["NewConnectionStatus"]=>
  string(9) "Connected"
  ["NewPossibleConnectionTypes"]=>
  string(21) "IP_Routed, IP_Bridged"
  ["NewConnectionType"]=>
  string(9) "IP_Routed"
  ["NewName"]=>
  string(8) "internet"
 ["NewUptime"]=>
  string(5) "57428"
  ["NewUpstreamMaxBitRate"]=>
  string(7) "1083169"
  ["NewDownstreamMaxBitRate"]=>
  string(7) "4289207"
  ["NewLastConnectionError"]=>
  string(10) "ERROR_NONE"
  ["NewIdleDisconnectTime"]=>
  string(1) "0"
  ["NewRSIPAvailable"]=>
  string(1) "0"
  ["NewUserName"]=>
  string(40) "deineid@t-online.de"
  ["NewNATEnabled"]=>
  string(1) "1"
  ["NewExternalIPAddress"]=>
  string(13) "91.35.130.0"
  ["NewDNSServers"]=>
  string(30) "217.237.150.0, 217.237.148.0"
  ["NewMACAddress"]=>
  string(17) "C0:25:06:00:00:00"
  ["NewConnectionTrigger"]=>
  string(8) "AlwaysOn"
  ["NewLastAuthErrorInfo"]=>
  string(0) ""
  ["NewMaxCharsUsername"]=>
  string(3) "128"
  ["NewMinCharsUsername"]=>
  string(1) "3"
  ["NewAllowedCharsUsername"]=>
  string(87) "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._@()#/%[]{}*+§$&=?!:;,"
  ["NewMaxCharsPassword"]=>
  string(2) "64"
  ["NewMinCharsPassword"]=>
  string(1) "3"
  ["NewAllowedCharsPassword"]=>
  string(87) "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._@()#/%[]{}*+§$&=?!:;,"
  ["NewTransportType"]=>
  string(5) "PPPoE"
  ["NewRouteProtocolRx"]=>
  string(3) "Off"
  ["NewPPPoEServiceName"]=>
  string(0) ""
  ["NewRemoteIPAddress"]=>
  string(0) ""
  ["NewPPPoEACName"]=>
  string(10) "xxxx05-asr"
  ["NewDNSEnabled"]=>
  string(1) "1"
  ["NewDNSOverrideAllowed"]=>
  string(1) "1"
}

*/


?>

 

BitNotice #96 – Live-Scripting: Kdenlive-XML-Reparatur

BitNotice #96 - Live-Scripting: Kdenlive-XML-Reparatur

(42 MB) 00:26:19

2016-03-09 11:00 🛈

Vorwarnung: Wenig Inhalt, viel Blabla und QnD-Code

Narf – irgendwo hat mein üblicher Videoeditor „Kdenlive“ seine Metadaten zerstört: Statt den Originalvideos meinte der Editor plötzlich nur noch die herunterskalierten Zwischenvideos verwenden zu können. Versuchen wir mit etwas Scripting die Schnittdatei zu reparieren.

PHP: Binärdaten als Float interpretieren

Aktuell arbeite ich daran Binärdaten einer Steuerung für ein Logging-System zu übersetzen. Die Daten werden – mit verschiedenen Formaten – per TCP angeliefert und kommen in etwas ungewöhnlichen Formaten (MSB/LSB, Füllbits, etc) herein. Die erste Aufgabe meines Scriptes besteht entsprechend darin die Daten in Binärform aufzubereiten. Das meiste sind ASCII-Werte, welche per chr() schnell übersetzt sind. Auch das Zusammensetzen von vorzeichenlosen Integern ist mit etwas Shiften kein Problem.

Gegen die Wand lief ich dann allerdings bei Kommawerten. Zwar nutzt sowohl die Steuerung als auch PHP Gleitkommazahlen nach IEEE754, allerdings lässt sich PHP mit dem üblichen Type-juggling/casting nicht dazu überreden die Binärdaten als Float zu interpretieren. Die Lösung fand sich (wie üblich) auf Stackoverflow: pack/unpack.

$input = 0xbf9e0419;
var_dump(unpack('f', pack('i', $input)));

Und schon weiß auch PHP, dass 0xbf9e0419 eine -1.2345 ist. Commence logging.

PHP mail() – Header-Fehler in aktuellen Versionen

„Das hat aber früher funktioniert“! Exakt das dachte ich mir, als nach einem PHP-Update ein kleines „Aushilfsscript“ seinen Dienst verweigerte. Das Script nimmt eine fertige E-Mail, entfernt einige Header, welche das Quellsystem nicht Standardkonform einbringt, und sendet die gesäuberte Mail weiter. Um unnötiges Parsing zu vermeiden ging die komplette Mail in den Header.

$headers[] = 'From: Test <test@adlerweb.info>';
$headers[] = 'Cc: Test2 <test2@adlerweb.info>';
$headers[] = 'X-cleaned: cleaned';
//[…]
$headers[] = '  boundary="'.$boundary.'"';

//Neue Header und Content zusammenführen
$content = implode("\r\n", $headers)."\r\n\r\n".'-----'.implode('-----',$content);

//Mailversand
$check = mail($receiver, NULL, NULL, $content);

Nicht schön, da aber nur ersetzt/ergänzt wird muss nicht bei jeder Änderung der Quelle eine Anpassung erfolgen um Content und Header sauber zu trennen. Leider ist seit den letzten Versionen (vermutlich 5.4.43/5.5.26) diese Anwendung nicht mehr erlaubt. Hat man zwei Newlines (\r\n\r\n – genutzt zur Content-Trennung) im Header bricht PHP mit einer Fehlermeldung ab. Das Ganze soll dazu dienen Personen zu schützen, welche Fremddaten aus unsicheren Quellen ohne Eingabevalidierung nutzen und so ihre Anwendung verwundbar machen. Leider trifft es aber eben auch solche Anwendungen wie meine, was gerade bei solchen minor-Versions etwas unschön ist. Nunja, mein Script spricht nun direkt SMTP mit dem zuständigen Mailserver und umgeht so die PHP-mail()-Funktion. Problem solved.

Related:

Q’n’D Windows-Shutdown-Daemon in PHP

Alles in Deckung, ich mache wieder unschönes Zeugs. Ich hatte ja bereits erklärt wie man über RPC/Samba einen Windows-PC von Linux herunterfahren kann. Leider ist die Installation von Samba aus Embedded-Systemen eine eher ungünstige Sache und ist in meinem Fall wegen Speichermangel nicht drin. Allerdings ist sowohl auf dem Embedded-System als auch dem betroffenen Rechner PHP verfügbar. I aim to misbehave.

Auf dem herunterzufahrenden Windows-Rechner wird per PHP ein TCP-Socket geöffnet. Dieser Code basiert auf einem Beispiel von Michael Kliewe. Um Scriptkiddies nicht direkt Zugang zu geben ist etwas (sehr schlechte) Challenge-Response-Authentifizierung drin – nunja, ist kein kritisches System. Wird der Client bestätigt folgt ein simples Shutdown per exec. Nicht viel, aber schnell Fertig und funktioniert:

<?php
class SocketChatServer {
    private $address = '0.0.0.0';   // 0.0.0.0 means all available interfaces
    private $port = 12345;          // the TCP port that should be used
    private $maxClients = 10;
 
    private $clients;
    private $socket;
    
    private $salt = 'YourSalt';
    private $pass = 'YourSecret';
 
    public function __construct() {
        // Set time limit to indefinite execution
        set_time_limit(0);
        error_reporting(E_ALL ^ E_NOTICE);
    }
 
    public function start() {
        // Create a TCP Stream socket
        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        // Bind the socket to an address/port
        socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 1);
        socket_bind($this->socket, $this->address, $this->port);
        // Start listening for connections
        socket_listen($this->socket, $this->maxClients);
 
 
        $this->clients = array('0' => array('socket' => $this->socket));
 
        while (true) {
            // Setup clients listen socket for reading
            $read[0] = $this->socket;
            for($i=1; $i<count ($this->clients)+1; ++$i) {
                if($this->clients[$i] != NULL) {
                    $read[$i+1] = $this->clients[$i]['socket'];
                }
            }
 
            // Set up a blocking call to socket_select()
            $ready = socket_select($read, $write = NULL, $except = NULL, $tv_sec = NULL);
 
            /* if a new connection is being made add it to the client array */
            if(in_array($this->socket, $read)) {
                for($i=1; $i < $this->maxClients+1; ++$i) {
                    if(!isset($this->clients[$i])) {
                        $this->clients[$i]['socket'] = socket_accept($this->socket);
                        socket_getpeername($this->clients[$i]['socket'], $ip);
                        $this->clients[$i]['ipaddy'] = $ip;
 
                        socket_write($this->clients[$i]['socket'], 'Welcome to the Shutdown Control System'."\r\n");
                        socket_write($this->clients[$i]['socket'], 'Your personal code is '.md5($this->salt.strftime('%Y-%m-%d').$ip)."\r\n");
 
                        $this->log("New client #$i connected: " . $this->clients[$i]['ipaddy']);
                        break;
                    } elseif($i == $this->maxClients - 1) {
                        $this->log('Too many Clients connected!');
                    }
 
                    if($ready < 1) {
                        continue;
                    }
                }
            }
 
            // If a client is trying to write - handle it now
            for($i=1; $i<$this->maxClients+1; ++$i) {
                if(in_array($this->clients[$i]['socket'], $read)) {
                    $data = @socket_read($this->clients[$i]['socket'], 1024, PHP_NORMAL_READ);
 
                    if($data === FALSE) {
                        unset($this->clients[$i]);
                        $this->log('Client disconnected!');
                        continue;
                    }
 
                    $data = trim($data);
 
                    if(!empty($data)) {
                        switch ($data) {
                            case 'exit':
                            case 'quit':
                                socket_write($this->clients[$i]['socket'], "Disconnecting without any action, Goodbye.\r\n");
                                $this->log("Client #$i is exiting");
                                unset($this->clients[$i]);
                                continue;
                            case 'shutdown-'.md5($this->pass.md5($this->salt.strftime('%Y-%m-%d').$ip)):
                                // first write a message to all connected clients
                                for($j=1; $j < $this->maxClients+1; ++$j) {
                                    if(isset($this->clients[$j]['socket'])) {
                                        if($this->clients[$j]['socket'] != $this->socket) {
                                            socket_write($this->clients[$j]['socket'], "System will be shut down now...\r\n");
                                        }
                                    }
                                }
                                // Close the master sockets, server termination requested
                                exec('shutdown /s /t 15 /c "System wurde per Fernbedienung ausgeschaltet"');
                                $this->log("Terminated server (requested by client #$i)");
                                exit;
                            case 'whoami':
                                // first write a message to all connected clients
                                socket_write($this->clients[$i]['socket'], "You are registred with the source IP ".$this->clients[$i]['ipaddy']." - your Code was ".md5($this->salt.strftime('%Y-%m-%d').$ip)." \r\n");
                                
                                break(2);
                            default:
                                for($j=1; $j < $this->maxClients+1; ++$j) {
                                    if(isset($this->clients[$j]['socket'])) {
                                        if(($this->clients[$j]['socket'] != $this->socket)) {
                                            $this->log($this->clients[$i]['ipaddy'] . ' is sending a message to ' . $this->clients[$j]['ipaddy'] . '!');
                                            socket_write($this->clients[$j]['socket'], '[' . $this->clients[$i]['ipaddy'] . '] says: ' . $data . "\r\n");
                                        }
                                    }
                                }
                                break(2);
                        }
                    }
                }
            }
        } // end while
    }
 
    private function log($msg) {
        // instead of echoing to console we could write this to a database or a textfile
        echo "[".date('Y-m-d H:i:s')."] " . $msg . "\r\n";
    }
}

$srv = new SocketChatServer;
$srv->start();
?>

Der Client bastelt sich eine passende Authentifizierung und spielt Zündknopf:

function pc_shutdown() {                                                                                                                                                                                                              
                $pass = 'YoutSecret';                                                                                                                                                                                                         
                                                                                                                                                                                                                                              
                $fp = fsockopen('pc.name', 12345, $errno, $errstr, 2);                                                                                                                                                                              
                if(!$fp) return false;                                                                                                                                                                                                        
                                                                                                                                                                                                                                              
                $line = fgets($fp); //Greeting                                                                                                                                                                                                
                $line = fgets($fp); //Hash                                                                                                                                                                                                    
                                                                                                                                                                                                                                              
                preg_match('/code is ([\w\d]{32})/', $line, $match);                                                                                                                                                                          
                $seed = $match[1];                                                                                                                                                                                                            
                                                                                                                                                                                                                                              
                $cmd = 'shutdown-'.md5($pass.$seed)."\n";                                                                                                                                                                                     
                                                                                                                                                                                                                                              
                fwrite($fp, $cmd);                                                                                                                                                                                                            
                                                                                                                                                                                                                                              
                var_dump(fgets($fp));                                                                                                                                                                                                         
                                                                                                                                                                                                                                              
                fclose($fp);                                                                                                                                                                                                                  
        }

Hoffen wir, dass es läuft und ich nie wieder drauf schauen muss, denn irgendwo tut sowas selbst mir weh 😉

PEBCAK: Negative Temperaturen gibt’s ja auch…

In der BitNotice #44 hatte ich „mal schnell“ ein Script gebaut um die Werte eines DS18B20 Temperatursensors über einen Raspberry Pi in Volkszähler zu schaufeln. Was irgendwie nicht so ganz aufgefallen ist: Das Script konnte nur Plusgrade. Jetzt steht auf Github eine überarbeitete Version mit „-“ und etwas mehr Fehlerbehandlung. Danke an Peter, dessen Kühltruhe den Fehler aufgedeckt hat.

BitBastelei #111 – OwnCloud – Installation für Linux & Android

BitBastelei #111 - OwnCloud - Installation für Linux & Android

(24 MB) 00:26:06

2014-08-24 10:00 🛈

Die Cloud ist praktisch – alle Daten auf allen Geräten immer greifbar – doch möchte man „sein Leben“ wirklich Firmen anvertrauen, die ihr Geld mit dem Verkauf von Daten machen? Mit Owncloud könnt ihr das Ruder selbst in die Hand nehmen: Dateien, Kontakte, Kalender u.v.m über den eigenen Server überall synchron.

Gezeigt wird:
00:12 Einführung
01:25 Installation auf einem Server
05:28 Die Owncloud WebApp
10:19 [Linux] Dateizugriff Part 1: WebDAV (online-Zugriff)
11:08 [Linux] Termine mit Thunderbird/Lightning
12:48 [Linux] Kontakte mit Thunderbird/Sogo Connector (http://www.sogo.nu/downloads/frontends.html)
15:41 [Linux] Dateizugriff Part 2: Miral/OwnCloud-Client (syncronisation)
18:48 [Android] Dateien mit OwnCloud (https://play.google.com/store/apps/details?id=com.owncloud.android – 0,79€ + https://play.google.com/store/apps/details?id=com.owncloud.android.workaround.accounts)
20:18 [Android] Kalender mit CalDAV-Sync (https://play.google.com/store/apps/details?id=org.dmfs.caldav.lib – 2,59€ + https://play.google.com/store/apps/details?id=org.dmfs.jb.workaround.caldavsync)
22:27 [Android] Kontakte mit CardDAV-Sync (https://play.google.com/store/apps/details?id=org.dmfs.carddav.sync )
24:06 Audblick & Fazit

Weitere Links:
Owncloud: http://owncloud.org/
Aufgabenverwaltung: https://play.google.com/store/apps/details?id=org.dmfs.tasks

Poor mans PC-Interface – Multimeter per Webcam auslesen

Improvisieren? Kann ich. Derzeit teste ich mit einem Gerät herum, welches sich in regelmäßigen Abständen einschaltet – interessant für mich wäre es nun diese Zyklen genauer zu kennen. Zwar habe ich einige Energiekostenmessgeräte, damit lässt sich jedoch nur Min/Max bzw. der Durchschnitt errechnen, nicht jedoch wie oft das Gerät nun tatsächlich läuft. Hier würde ein Multimeter mit PC-Anbindung helfen, leider käme hierzu nur mein UniT in Frage, welches im Dauerbetrieb jedoch recht schnell mit leerer Batterie seinen Dienst einstellen würde. Meine „großen“ HPs besitzen zwar eine GPIB-Schnittstelle, den passenden PC-Adapter habe ich allerdings noch nicht fertig.

Bleibt nur ein anderer Ansatz: Optische Erkennung. Als Multimeter hält mein Rohde&Schwarz/Keithley her – durch die LEDs ist dessen Anzeige am einfachsten lesbar. Direkt davor sitzt nun eine Webcam, welche die Anzeige im Auge behält. Über die Software „motion“ stellt mein Linux-Server einen MJPEG-Stream bereit – ich bin diesen Weg gegangen, da ein ständiges Reinitialisieren der Kamera für einzelne Screenshots gerne zu Hängern führt.

Bild: https://adlerweb.info/blog/wp-content/uploads/2014/08/cam1.jpg

Ein kleiner PHP-Daemon fragt nun regelmäßig das letzte Bild ab, hierzu wird ein (modifizierter) Code des motion-Projektes verwendet:

$camurl='http://127.0.0.1:8081/';
$boundary="\n--";

$f = fopen($camurl,"r") ;

   if(!$f)
   {
        //**** cannot open
        echo "error";
   }
    else
   {
        //**** URL OK
         $r='';
         unset($p);
         while (substr_count($r,"Content-Length") != 2) $r.=fread($f,512);

         $start = strpos($r,chr(0xFF));
         $end   = strpos($r,$boundary,$start)-1;
         $frame = substr("$r",$start,$end - $start);

         //In $frame sind nun die rohen JPEG-Daten - diese können ausgegeben werden, gespeichert werden oder per imagecreatefromstring (gd) in PHP weiter verarbeitet werden
   }

Weiterhin wird das Bild passend beschnitten, gedreht und in ein invertiertes Schwarz-Weiß-Bild umgewandelt:

$src = imagecreatefromstring($frame);
$dst = imagecreatetruecolor(173,75);

imagecopy($dst, $src, 0, 0, 225, 66, 173, 75);
$dst = imagerotate($dst, 10, 0);
imagefilter($dst, IMG_FILTER_NEGATE);
imagefilter($dst, IMG_FILTER_GRAYSCALE);
imagefilter($dst, IMG_FILTER_CONTRAST, -100);

Im Anschluss wird über eine Reihe von „imagecolorat“-Abfragen für jede der 7-Segment-Stellen ein Array generiert – jedes Segment ist entweder an (1) oder aus (0). Zur besseren Erkennung wird jedes Segment an mehreren Stellen abgefragt und der Durchschnitt mit einem Schwellwert bewertet, so ist das System unempfindlicher gegen Rauschen.

Bild: https://adlerweb.info/blog/wp-content/uploads/2014/08/7SEG-208×300.png
(Im Array ist der Index entsprechend 0-6, nicht 1-7)

Am Ende steht ein Mapping, welches die erkannte Anzeige in einen Integer umwandelt

function seven2int($p) {
    $map[1][1][1][0][1][1][1] = 0;
    $map[0][1][0][0][1][0][0] = 1;
    $map[0][0][1][0][0][1][0] = 1;
    $map[1][0][1][1][1][0][1] = 2;
    $map[1][0][1][1][0][1][1] = 3;
    $map[0][1][1][1][0][1][0] = 4;
    $map[1][1][0][1][0][1][1] = 5;
    $map[1][1][0][1][1][1][1] = 6;
    $map[0][1][0][1][1][1][1] = 6;
    $map[1][0][1][0][0][1][0] = 7;
    $map[1][1][1][1][1][1][1] = 8;
    $map[1][1][1][1][0][1][0] = 9;
    $map[1][1][1][1][0][1][1] = 9;

    if(isset($map[$p[0]][$p[1]][$p[2]][$p[3]][$p[4]][$p[5]][$p[6]])) {
        return $map[$p[0]][$p[1]][$p[2]][$p[3]][$p[4]][$p[5]][$p[6]];
    }else{
        return false;
    }
}

Die (passend multiplizierten) Werte ergeben den Messwert. Die Erkennung ist zwar nicht 100%ig, jedoch bei der verfügbaren Auflösung wesentlich zuverlässiger als ein klassischer OCR und ausreichend um ein paar Kurven zu zeichnen. Derzeit werden etwa 80-90% der Captures erkannt. Die meisten Fehler kommen durch ungünstiges Timing – wenn die Kamera eine Aufnahme macht während die LED-Anzeige gerade aktualisiert kommt es zu halben Zahlen.

Bild: https://adlerweb.info/blog/wp-content/uploads/2014/08/Unbenannt.png