From 54e6fc4005449c45be7099d73c53a6b5847d0536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Tr=C3=B6ger?= Date: Sun, 3 Dec 2023 15:36:06 +0100 Subject: [PATCH] no message --- .github/workflows/style.yml | 16 + .github/workflows/tests.yml | 14 + .gitmodules | 9 + .style | 1 + .styleci.yml | 14 - .vscode | 1 + ClientSplitter/README.md | 18 +- ClientSplitter/form.json | 15 +- ClientSplitter/module.json | 20 +- ClientSplitter/module.php | 2 + DHCPSniffer/README.md | 18 +- DHCPSniffer/form.json | 103 ++++--- DHCPSniffer/locale.json | 14 +- DHCPSniffer/module.json | 17 +- DHCPSniffer/module.php | 42 +-- HookReverseProxy/README.md | 22 +- HookReverseProxy/form.json | 122 ++++---- HookReverseProxy/module.json | 23 +- HookReverseProxy/module.php | 62 ++-- JSONFilter/form.json | 116 ------- JSONFilter/locale.json | 10 - JSONFilter/module.json | 11 - JSONFilter/module.php | 146 --------- JSONValues/form.json | 53 ---- JSONValues/locale.json | 39 --- JSONValues/module.json | 11 - JSONValues/module.php | 86 ------ README.md | 24 +- WebSocketClient/README.md | 54 ++-- WebSocketClient/form.json | 278 +++++++++-------- WebSocketClient/module.json | 22 +- WebSocketClient/module.php | 495 +++++++++++++++--------------- WebSocketServer/README.md | 102 +++--- WebSocketServer/form.json | 262 ++++++++-------- WebSocketServer/locale.json | 3 +- WebSocketServer/module.json | 23 +- WebSocketServer/module.php | 379 +++++++++++------------ WebSocketServerIfTest/README.md | 2 +- WebSocketServerIfTest/form.json | 4 +- WebSocketServerIfTest/module.json | 17 +- WebSocketServerIfTest/module.php | 2 + library.json | 13 +- libs/NetworkTraits.php | 174 +---------- libs/WebhookHelper.php | 2 + libs/WebsocketClass.php | 22 +- libs/loadTLS.php | 2 + tests/LibraryTest.php | 37 +++ tests/phpunit.xml | 11 + tests/stubs | 1 + 49 files changed, 1219 insertions(+), 1715 deletions(-) create mode 100644 .github/workflows/style.yml create mode 100644 .github/workflows/tests.yml create mode 100644 .gitmodules create mode 160000 .style delete mode 100644 .styleci.yml create mode 160000 .vscode delete mode 100644 JSONFilter/form.json delete mode 100644 JSONFilter/locale.json delete mode 100644 JSONFilter/module.json delete mode 100644 JSONFilter/module.php delete mode 100644 JSONValues/form.json delete mode 100644 JSONValues/locale.json delete mode 100644 JSONValues/module.json delete mode 100644 JSONValues/module.php create mode 100644 tests/LibraryTest.php create mode 100644 tests/phpunit.xml create mode 160000 tests/stubs diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 0000000..23ef350 --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,16 @@ +name: Check Style + +on: [push, pull_request] + +jobs: + style: + runs-on: ubuntu-latest + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + - name: Checkout module + uses: actions/checkout@master + - name: Check style + uses: Nall-chan/action-style@master diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..e6563d0 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,14 @@ +name: Run Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout module + uses: actions/checkout@master + with: + submodules: true + - name: Run tests + uses: symcon/action-tests@master diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..18a56a6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "tests/stubs"] + path = tests/stubs + url = https://github.com/Nall-chan/SymconStubs +[submodule ".vscode"] + path = .vscode + url = https://github.com/Nall-chan/SymconVSCTasks.git +[submodule ".style"] + path = .style + url = https://github.com/Nall-chan/StylePHP diff --git a/.style b/.style new file mode 160000 index 0000000..db94317 --- /dev/null +++ b/.style @@ -0,0 +1 @@ +Subproject commit db94317399d4ad0cac9220616961df392aad41c5 diff --git a/.styleci.yml b/.styleci.yml deleted file mode 100644 index 1f535fe..0000000 --- a/.styleci.yml +++ /dev/null @@ -1,14 +0,0 @@ -## YAML Template. ---- -preset: recommended - -enabled: - - concat_with_spaces - - -disabled: - - trailing_comma_in_multiline_array - - concat_without_spaces - - blank_line_before_return - - no_useless_return - - simplified_null_return \ No newline at end of file diff --git a/.vscode b/.vscode new file mode 160000 index 0000000..672a096 --- /dev/null +++ b/.vscode @@ -0,0 +1 @@ +Subproject commit 672a096abc8fcc6afa43f4aedd47821b5583b4c5 diff --git a/ClientSplitter/README.md b/ClientSplitter/README.md index 98429ba..ad6ae30 100644 --- a/ClientSplitter/README.md +++ b/ClientSplitter/README.md @@ -1,4 +1,4 @@ -# ClientSplitter (IPSNetwork) +# ClientSplitter (Network) Implementierung eines Splitters für ServerSocket und WebSocket-Server. @@ -31,7 +31,7 @@ Implementierung eines Splitters für ServerSocket und WebSocket-Server. ## 3. Installation Über das Modul-Control folgende URL hinzufügen. - `git://github.com/Nall-chan/IPSNetwork.git` + `git://github.com/Nall-chan/Network.git` **Bei kommerzieller Nutzung (z.B. als Errichter oder Integrator) wenden Sie sich bitte an den Autor.** @@ -59,15 +59,15 @@ Implementierung eines Splitters für ServerSocket und WebSocket-Server. GUID des Modules (z.B. wenn Instanz per PHP angelegt werden soll): -| Instanz | GUID | -| :--------------: | :------------------------------------: | -| Client Splitter | {7A107D38-75ED-47CB-83F9-F41228CAEEFA} | +| Instanz | GUID | +| :-------------: | :------------------------------------: | +| Client Splitter | {7A107D38-75ED-47CB-83F9-F41228CAEEFA} | Eigenschaften des 'Client Splitter' für Get/SetProperty-Befehle: -| Eigenschaft | Typ | Standardwert | Funktion | -| :-----------: | :-----: | :----------: | :-----------------------: | -| ClientIP | string | | Die IP-Adresse des Client | +| Eigenschaft | Typ | Standardwert | Funktion | +| :---------: | :----: | :----------: | :-----------------------: | +| ClientIP | string | | Die IP-Adresse des Client | ## 8. Datenaustausch @@ -79,7 +79,7 @@ Kompatibel zum Interface Virtual-IO. **Changlog:** Version 1.1: - - In IPSNetwork-Library integriert + - In Network-Library integriert Version 1.0: - Erstes offizielles Release diff --git a/ClientSplitter/form.json b/ClientSplitter/form.json index c727d7c..a9d9ec8 100644 --- a/ClientSplitter/form.json +++ b/ClientSplitter/form.json @@ -1,10 +1,9 @@ { - "elements": - [ - { - "name": "ClientIP", - "type": "ValidationTextBox", - "caption": "Client IP:" - } - ] + "elements": [ + { + "name": "ClientIP", + "type": "ValidationTextBox", + "caption": "Client IP:" + } + ] } \ No newline at end of file diff --git a/ClientSplitter/module.json b/ClientSplitter/module.json index 849f920..a94581e 100644 --- a/ClientSplitter/module.json +++ b/ClientSplitter/module.json @@ -3,9 +3,19 @@ "name": "ClientSplitter", "type": 2, "vendor": "", - "aliases": ["Client Splitter"], - "parentRequirements": ["{C8792760-65CF-4C53-B5C7-A30FCC84FEFE}"], - "childRequirements": ["{018EF6B5-AB94-40C6-AA53-46943E824ACF}"], - "implemented": ["{7A1272A4-CBDB-46EF-BFC6-DCF4A53D2FC7}", "{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}"], - "prefix": "WSC" + "aliases": [ + "Client Splitter" + ], + "parentRequirements": [ + "{C8792760-65CF-4C53-B5C7-A30FCC84FEFE}" + ], + "childRequirements": [ + "{018EF6B5-AB94-40C6-AA53-46943E824ACF}" + ], + "implemented": [ + "{7A1272A4-CBDB-46EF-BFC6-DCF4A53D2FC7}", + "{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}" + ], + "prefix": "WSC", + "url": "https://github.com/Nall-chan/Network" } \ No newline at end of file diff --git a/ClientSplitter/module.php b/ClientSplitter/module.php index 2b61436..4cb65bb 100644 --- a/ClientSplitter/module.php +++ b/ClientSplitter/module.php @@ -1,5 +1,7 @@ ApplyChanges(); - } - /** * Interne Funktion des SDK. * Wird von der Console aufgerufen, wenn 'unser' IO-Parent geöffnet wird. @@ -180,6 +174,7 @@ public function ReceiveData($JSONString) { $Data = utf8_decode(json_decode($JSONString)->Buffer); $this->SendDebug('Data', $Data, 1); + $this->LogMessage(bin2hex($Data), KL_MESSAGE); $isDHCP = (substr($Data, 236, 4) === chr(0x63) . chr(0x82) . chr(0x53) . chr(0x63)); $isDHCPRequest = (substr($Data, 236, 7) === chr(0x63) . chr(0x82) . chr(0x53) . chr(0x63) . chr(0x35) . chr(0x01) . chr(0x03)); $this->SendDebug('isDHCP', $isDHCP, 0); @@ -198,14 +193,21 @@ public function ReceiveData($JSONString) case 2: // both if ($isDHCPRequest) { $this->SendEvent(); - } - if (!$isDHCP) { + } elseif (!$isDHCP) { $this->SendEvent(); } break; } } + /** + * Wird ausgeführt wenn der Kernel hochgefahren wurde. + */ + protected function KernelReady() + { + $this->ApplyChanges(); + } + /** * Beschreibt die Statusvariable. */ @@ -214,25 +216,15 @@ protected function SendEvent() $this->SendDebug('FIRE', 'EVENT', 0); switch ($this->ReadPropertyInteger('Action')) { case 0: //EVENT - $vid = @$this->GetIDForIdent('EVENT'); - if ($vid > 0) { - SetValueBoolean($vid, true); - } + $this->SetValue('EVENT', true); break; case 1: // IMPULSE - $vid = @$this->GetIDForIdent('IMPULSE'); - if ($vid > 0) { - SetValueBoolean($vid, true); - IPS_Sleep(1); - SetValueBoolean($vid, false); - } - + $this->SetValue('EVENT', true); + IPS_Sleep(1); + $this->SetValue('EVENT', false); break; case 2: // Toggle - $vid = @$this->GetIDForIdent('TOGGLE'); - if ($vid > 0) { - SetValueBoolean($vid, !GetValueBoolean($vid)); - } + $this->SetValue('EVENT', !$this->GetValue('EVENT')); break; } } diff --git a/HookReverseProxy/README.md b/HookReverseProxy/README.md index 0e6359a..24aacf4 100644 --- a/HookReverseProxy/README.md +++ b/HookReverseProxy/README.md @@ -1,4 +1,4 @@ -[![Version](https://img.shields.io/badge/Symcon-PHPModul-red.svg)](https://www.symcon.de/service/dokumentation/entwicklerbereich/sdk-tools/sdk-php/) +[![SDK](https://img.shields.io/badge/Symcon-PHPModul-red.svg)](https://www.symcon.de/service/dokumentation/entwicklerbereich/sdk-tools/sdk-php/) [![Version](https://img.shields.io/badge/Modul%20Version-2.20-blue.svg)]() [![License](https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-green.svg)](https://creativecommons.org/licenses/by-nc-sa/4.0/) [![Version](https://img.shields.io/badge/Symcon%20Version-4.3%20%3E-green.svg)](https://www.symcon.de/forum/threads/30857-IP-Symcon-4-3-%28Stable%29-Changelog) @@ -36,11 +36,11 @@ Stellt interne Dateien und URLs als Webhook bereit ## 3. Software-Installation - Dieses Modul ist Bestandteil der [IPSNetwork-Library](../). + Dieses Modul ist Bestandteil der [Network-Library](../). **IPS 5.0:** Bei privater Nutzung: Über das 'Module-Control' in IPS folgende URL hinzufügen. - `git://github.com/Nall-chan/IPSNetwork.git` + `git://github.com/Nall-chan/Network.git` **Bei kommerzieller Nutzung (z.B. als Errichter oder Integrator) wenden Sie sich bitte an den Autor.** @@ -56,13 +56,13 @@ Folgende Parameter sind in der Instanz zu konfigurieren: Werte pro Spalte: -| Eigenschaft | Typ | Standardwert | Funktion | -| :-----------------: | :-----: | :----------: | :------------------------------------------------------------------------------------: | -| Hook | string | /hook/ | URI des Webhook. Muss mit /hook/ anfangen. | -| Url | string | http:// | URL zur Quelle. Kann auch https oder eine lokale Datei sein. | -| forceDL | bool | false | Zwingt Browser die gelieferten Daten als Datei zu speichern und nicht darzustellen. | -| allowGet | bool | true | Erlaubt das Erweitern und Überscheiben von Parametern durch den Hook. | -| weakSSL | bool | false | Deaktiviert die SSL Prüfung. Zum Beispiel für selbst-signierte Zertifikate. | +| Eigenschaft | Typ | Standardwert | Funktion | +| :---------: | :----: | :----------: | :---------------------------------------------------------------------------------: | +| Hook | string | /hook/ | URI des Webhook. Muss mit /hook/ anfangen. | +| Url | string | http:// | URL zur Quelle. Kann auch https oder eine lokale Datei sein. | +| forceDL | bool | false | Zwingt Browser die gelieferten Daten als Datei zu speichern und nicht darzustellen. | +| allowGet | bool | true | Erlaubt das Erweitern und Überscheiben von Parametern durch den Hook. | +| weakSSL | bool | false | Deaktiviert die SSL Prüfung. Zum Beispiel für selbst-signierte Zertifikate. | ## 5. Statusvariablen und Profile @@ -89,7 +89,7 @@ Version 2.01: - Doku ergänzt Version 2.0: - - WebSocket-Module in IPSNetwork überführt + - WebSocket-Module in Network überführt Version 1.0: - Erstes offizielles Release diff --git a/HookReverseProxy/form.json b/HookReverseProxy/form.json index 64daeeb..914ca4e 100644 --- a/HookReverseProxy/form.json +++ b/HookReverseProxy/form.json @@ -1,63 +1,67 @@ { - "elements": - [ + "elements": [ + { + "type": "List", + "name": "Hooks", + "add": true, + "delete": true, + "sort": { + "column": "Hook", + "direction": "ascending" + }, + "columns": [ { - "type": "List", - "name": "Hooks", + "caption": "IPS Hook", + "name": "Hook", + "width": "300px", + "add": "/hook/", + "edit": { + "caption": "URI", + "type": "ValidationTextBox" + } + }, + { + "caption": "Source", + "name": "Url", + "width": "auto", + "add": "http://", + "edit": { + "caption": "URL or File", + "type": "ValidationTextBox" + } + }, + { + "caption": "force download", + "name": "forceDL", + "width": "150px", + "add": false, + "edit": { + "caption": "force download", + "type": "CheckBox" + } + }, + { + "caption": "allow get query", + "name": "allowGet", + "width": "150px", "add": true, - "delete": true, - "sort": { - "column": "Hook", - "direction": "ascending" - }, - "columns": [{ - "caption": "IPS Hook", - "name": "Hook", - "width": "300px", - "add": "/hook/", - "edit": { - "caption": "URI", - "type": "ValidationTextBox" - } - }, { - "caption": "Source", - "name": "Url", - "width": "auto", - "add": "http://", - "edit": { - "caption": "URL or File", - "type": "ValidationTextBox" - } - }, { - "caption": "force download", - "name": "forceDL", - "width": "150px", - "add": false, - "edit": { - "caption": "force download", - "type": "CheckBox" - } - }, { - "caption": "allow get query", - "name": "allowGet", - "width": "150px", - "add": true, - "edit": { - "caption": "allow get", - "type": "CheckBox" - } - }, { - "caption": "no SSL checks", - "name": "weakSSL", - "width": "150px", - "add": false, - "edit": { - "caption": "no SSL checks", - "type": "CheckBox" - } - } - ], - "values": [] + "edit": { + "caption": "allow get", + "type": "CheckBox" + } + }, + { + "caption": "no SSL checks", + "name": "weakSSL", + "width": "150px", + "add": false, + "edit": { + "caption": "no SSL checks", + "type": "CheckBox" + } } - ] -} + ], + "values": [] + } + ] +} \ No newline at end of file diff --git a/HookReverseProxy/module.json b/HookReverseProxy/module.json index ca3c8de..4188118 100644 --- a/HookReverseProxy/module.json +++ b/HookReverseProxy/module.json @@ -1,11 +1,14 @@ { - "id": "{26E5D8BC-8117-4DFB-9E85-66BB30F1CF95}", - "name": "HookReverseProxy", - "type": 0, - "vendor": "", - "aliases": ["Webhook Reverse Proxy"], - "parentRequirements": [], - "childRequirements": [], - "implemented": [], - "prefix": "HOOKPROXY" -} + "id": "{26E5D8BC-8117-4DFB-9E85-66BB30F1CF95}", + "name": "HookReverseProxy", + "type": 0, + "vendor": "", + "aliases": [ + "Webhook Reverse Proxy" + ], + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "HOOKPROXY", + "url": "https://github.com/Nall-chan/Network" +} \ No newline at end of file diff --git a/HookReverseProxy/module.php b/HookReverseProxy/module.php index 872879f..3c3ebb9 100644 --- a/HookReverseProxy/module.php +++ b/HookReverseProxy/module.php @@ -1,5 +1,7 @@ GetURL($_SERVER['HOOK']); + if ($HookData) { + $URLScheme = parse_url($HookData['Url'], PHP_URL_SCHEME); + if (in_array($URLScheme, ['http', 'https', 'ftp'])) { + return $this->DeliveryRemoteFile($HookData, $_GET); + } + return $this->DeliveryLocalFile($HookData['Url'], $HookData['forceDL']); + } else { + http_response_code(404); + header('Content-Type: text/plain'); + header('Connection: close'); + header('Server: Symcon ' . IPS_GetKernelVersion()); + header('X-Powered-By: Hook Reverse Proxy'); + header('Expires: 0'); + header('Cache-Control: no-cache'); + header('Content-Type: text/plain'); + die('File not found!'); + } + } + private function GetURL($RequestURI) { $Hooks = $this->Hooks; @@ -223,33 +252,6 @@ private function DeliveryRemoteFile(array $HookData, array $Get) } } - /** - * Interne Funktion des SDK. - */ - protected function ProcessHookdata() - { - // SSL fehlt - // Authentifizierung lokal einbauen - $HookData = $this->GetURL($_SERVER['HOOK']); - if ($HookData) { - $URLScheme = parse_url($HookData['Url'], PHP_URL_SCHEME); - if (in_array($URLScheme, ['http', 'https', 'ftp'])) { - return $this->DeliveryRemoteFile($HookData, $_GET); - } - return $this->DeliveryLocalFile($HookData['Url'], $HookData['forceDL']); - } else { - http_response_code(404); - header('Content-Type: text/plain'); - header('Connection: close'); - header('Server: Symcon ' . IPS_GetKernelVersion()); - header('X-Powered-By: Hook Reverse Proxy'); - header('Expires: 0'); - header('Cache-Control: no-cache'); - header('Content-Type: text/plain'); - die('File not found!'); - } - } - private function unparse_url($parsed_url) { $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; diff --git a/JSONFilter/form.json b/JSONFilter/form.json deleted file mode 100644 index 03f71c7..0000000 --- a/JSONFilter/form.json +++ /dev/null @@ -1,116 +0,0 @@ -{ - "elements": - [ - { - "type": "Select", - "name": "Condition", - "caption": "Condition", - "options": [ - { - "caption": "and", - "value": 0 - }, - { - "caption": "or", - "value": 1 - } - ] - }, { - "type": "List", - "name": "FilterItems", - "rowCount": 20, - "add": true, - "delete": true, - "sort": { - "column": "Item", - "direction": "ascending" - }, - "columns": [{ - "caption": "Item", - "name": "Item", - "width": "auto", - "add": "", - "edit": { - "type": "ValidationTextBox" - } - }, { - "caption": "Value", - "name": "Value", - "width": "100px", - "add": "", - "edit": { - "type": "ValidationTextBox" - } - }, { - "caption": "Type", - "name": "Type", - "width": "50px", - "add": 3, - "edit": { - "type": "Select", - "caption": "Variabletyp", - "options": [ - { - "caption": "boolean", - "value": 0 - }, - { - "caption": "integer", - "value": 1 - }, - { - "caption": "float", - "value": 2 - }, - { - "caption": "string", - "value": 3 - } - ] - } - }, { - "caption": "Condition", - "name": "Condition", - "width": "50px", - "add": 0, - "edit": { - "type": "Select", - "caption": "Condition", - "options": [ - { - "caption": "void", - "value": 0 - }, - { - "caption": "match", - "value": 1 - }, - { - "caption": "include (only string)", - "value": 2 - } - ] - } - } - ], - "values": [] - }, { - "type": "Select", - "name": "Type", - "caption": "Filter of", - "options": [ - { - "caption": "Sub-Item", - "value": 0 - }, - { - "caption": "Self-Item", - "value": 1 - }, - { - "caption": "all", - "value": 2 - } - ] - }] -} diff --git a/JSONFilter/locale.json b/JSONFilter/locale.json deleted file mode 100644 index 0817d9b..0000000 --- a/JSONFilter/locale.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "translations": { - "de": { - "Host": "Host", - "New Hub": "Neuer Hub", - "Search Sensors": "Suche Sensoren", - "No hubs defined!":"Keine Hubs eingerichtet!" - } - } -} \ No newline at end of file diff --git a/JSONFilter/module.json b/JSONFilter/module.json deleted file mode 100644 index 02c46a4..0000000 --- a/JSONFilter/module.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "id": "{57230BB1-A650-4D6A-AE7E-07B672651284}", - "name": "JSON Filter", - "type": 2, - "vendor": "Nall-chan", - "aliases": [], - "parentRequirements": ["{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}", "{D4C1D08F-CD3B-494B-BE18-B36EF73B8F43}"], - "childRequirements": ["{018EF6B5-AB94-40C6-AA53-46943E824ACF}"], - "implemented": ["{018EF6B5-AB94-40C6-AA53-46943E824ACF}", "{4CB91589-CE01-4700-906F-26320EFCF6C4}", "{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}"], - "prefix": "JSON" -} diff --git a/JSONFilter/module.php b/JSONFilter/module.php deleted file mode 100644 index 31dab99..0000000 --- a/JSONFilter/module.php +++ /dev/null @@ -1,146 +0,0 @@ -RegisterPropertyString('FilterItems', json_encode([])); - $this->RegisterPropertyInteger('Condition', 0); - $this->RegisterPropertyInteger('Type', 0); - } - - public function ApplyChanges() - { - parent::ApplyChanges(); - $Items = json_decode($this->ReadPropertyString('FilterItems'), true); - - if (count($Items) > 0) { - foreach ($Items as $Item) { - $Value = ''; - switch ($Item['Type']) { - case 0: - if (is_numeric($Item['Value'])) { - $Value = (bool) $Item['Value'] ? 'true' : 'false'; - } elseif (is_string($Item['Value'])) { - $Value = strtolower($Item['Value']) == 'true' ? 'true' : 'false'; - } else { - $Value = 'false'; - } - break; - case 1: - if (is_numeric($Item['Value'])) { - $Value = (int) $Item['Value'] . '\D'; - } else { - $Value = '0\D'; - } - break; - case 2: - if (is_numeric($Item['Value'])) { - $Value = (float) $Item['Value'] . '\D'; - } else { - $Value = '0\D'; - } - break; - case 3: - switch ($Item['Condition']) { - case 0: - $Value = ''; - break; - case 1: - $Value = '\\\"' . (string) $Item['Value'] . '\\\"'; - - break; - case 2: - $Value = '\\\".*' . (string) $Item['Value'] . '.*\\\"'; - break; - } - break; - } - $Types[$Item['Item']][] = $Value; - } - foreach ($Types as $Key => $Typ) { - if (count($Typ) > 1) { - $ValueLine = '(' . implode('|', $Typ) . ')'; - } else { - $ValueLine = $Typ[0]; - } - - if ($ValueLine != '') { - $Lines[] = '.*\\\"' . $Key . '\\\":' . $ValueLine . '.*'; - } else { - $Lines[] = '.*\\\"' . $Key . '\\\":.*'; - } - } - switch ($this->ReadPropertyInteger('Condition')) { - case 0: // and - $Line = implode(')(?=', $Lines); - $Line = '.*(?=' . $Line . ').*'; - break; - case 1: // or - $Line = implode('|', $Lines); - break; - } - - $this->SetReceiveDataFilter($Line); - $this->SendDebug('FILTER', $Line, 0); - } else { - $this->SetReceiveDataFilter(''); - $this->SendDebug('FILTER', 'NOTHING', 0); - } - // Alles Items lesen und als Filter setzen - /* - $this->SetReceiveDataFilter('.*"UUID":"' . $UUID . '".*'); - else - $this->SetReceiveDataFilter(".*9999999999.*"); - */ - } - - public function ReceiveData($JSONString) - { - $this->SendDebug('Receive', $JSONString, 0); - $AllData = utf8_decode(json_decode($JSONString)->Buffer); - $this->SendDebug('Receive', $AllData, 0); - $FilterType = $this->ReadPropertyInteger('Type'); - - if ($FilterType == 2) { - $this->SendDebug('ForwardToChild', $JSONString, 0); - $this->SendDataToChildren($JSONString); - return; - } - - if ($FilterType == 0) { - $ReceiveItems = json_decode($AllData, true); - if ($ReceiveItems === null) { - trigger_error('Error receive Data', E_USER_NOTICE); - $this->SendDebug('Error', 'Error receive Data', 0); - return; - } - $ReceiveItems = $this->DecodeUTF8($ReceiveItems); - $ConfigItems = array_column(json_decode($this->ReadPropertyString('FilterItems'), true), 'Value', 'Item'); - $this->SendDebug('ConfigItems', $ConfigItems, 0); - $Items = array_intersect_key($ReceiveItems, $ConfigItems); - $this->SendDebug('Items', $Items, 0); - foreach (array_keys($Items) as $Item) { - $ReceiveItems[$Item] = $this->EncodeUTF8($ReceiveItems[$Item]); - - $SendData['DataID'] = '{018EF6B5-AB94-40C6-AA53-46943E824ACF}'; - $SendData['Buffer'] = json_encode($ReceiveItems[$Item]); - $this->SendDebug('Forward', $SendData['Buffer'], 0); - $this->SendDataToChildren(json_encode($SendData)); - } - return; - } - } - - public function ForwardData($JSONString) - { - $this->SendDataToChildren($JSONString); - } -} diff --git a/JSONValues/form.json b/JSONValues/form.json deleted file mode 100644 index 815bf7a..0000000 --- a/JSONValues/form.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "elements": - [ - { - "type": "List", - "name": "Items", - "rowCount": 20, - "add": true, - "delete": true, - "sort": { - "column": "Item", - "direction": "ascending" - }, - "columns": [{ - "caption": "Item", - "name": "Item", - "width": "auto", - "add": "", - "edit": { - "type": "ValidationTextBox" - } - }, { - "caption": "Type", - "name": "Type", - "width": "150px", - "add": 3, - "edit": { - "type": "Select", - "caption": "Variabletyp", - "options": [ - { - "caption": "boolean", - "value": 0 - }, - { - "caption": "integer", - "value": 1 - }, - { - "caption": "float", - "value": 2 - }, - { - "caption": "string", - "value": 3 - } - ] - } - } - ], - "values": [] - }] -} diff --git a/JSONValues/locale.json b/JSONValues/locale.json deleted file mode 100644 index 0615320..0000000 --- a/JSONValues/locale.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "translations": { - "de": { - "Instance has no active parent.": "Instanz hat keinen aktiven Parent.", - "Error on read MiFlora data": "Fehler beim lesen der MiFlora Daten", - "UUID": "UUID", - "Interval": "Intervall", - "Read data": "Lese Daten", - "Lux": "Helligkeit", - "BatteryLevel": "Batterie", - "Temperature": "Temperatur", - "Firmware": "Firmware", - "SoilMoisture": "Bodenfeuchtigkeit", - "SoilElectricalConductivity": "Bodenleitfähigkeit", - "Use of min/max enables Data logging !": "Verwendung von min/max aktiviert das Logging !", - "min Electrical Conductivity": "min Bodenleitfähigkeit", - "max Electrical Conductivity": "max Bodenleitfähigkeit", - "Electrical Conductivity hint": "Empfehlung Bodenleitfähigkeit", - "fertilize": "Düngen", - "OK": "OK", - "over fertilize": "Überdüngt", - "min Lux": "min Helligkeit", - "max Lux": "max Helligkeit", - "Lux hint": "Empfehlung Helligkeit", - "too dark": "zu dunkel", - "too bright": "zu hell", - "min Moisture": "min Bodenfeuchtigkeit", - "max Moisture": "max Bodenfeuchtigkeit", - "Moisture hint": "Empfehlung Bodenfeuchte", - "too dry": "zu trocken", - "too moist": "zu feucht", - "min Temperature": "min Temperatur", - "max Temperature": "max Temperatur", - "Temperature hint": "Empfehlung Temperatur", - "too cold": "zu kalt", - "too warm": "zu warm" - } - } -} \ No newline at end of file diff --git a/JSONValues/module.json b/JSONValues/module.json deleted file mode 100644 index 7d3fa87..0000000 --- a/JSONValues/module.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "id": "{2F72A5E7-B6CA-4AB4-B20E-D09EC35C7682}", - "name": "JSON Values", - "type": 3, - "vendor": "Nall-chan", - "aliases": [], - "parentRequirements": ["{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}", "{D4C1D08F-CD3B-494B-BE18-B36EF73B8F43}"], - "childRequirements": [], - "implemented": ["{018EF6B5-AB94-40C6-AA53-46943E824ACF}", "{4CB91589-CE01-4700-906F-26320EFCF6C4}"], - "prefix": "JSON" -} diff --git a/JSONValues/module.php b/JSONValues/module.php deleted file mode 100644 index 414bb75..0000000 --- a/JSONValues/module.php +++ /dev/null @@ -1,86 +0,0 @@ -RegisterPropertyString('Items', json_encode([])); - } - - public function ApplyChanges() - { - parent::ApplyChanges(); - - // Alles Items lesen und als Filter setzen - /* - $this->SetReceiveDataFilter('.*"UUID":"' . $UUID . '".*'); - else - $this->SetReceiveDataFilter(".*9999999999.*"); - */ - - // Prüfen Einstellungen und anlegen Variablen - $this->MakeStatusVariables(); - } - - private function MakeStatusVariables() - { - $Items = json_decode($this->ReadPropertyString('Items'), true); - $this->SendDebug('Config', $Items, 0); -// print_r($Items); - $ConfigItems = array_column($Items, 'Type', 'Item'); - - foreach ($ConfigItems as $Item => $Typ) { - if ($Item == '') { - continue; - } - $Ident = $this->generateIdent($Item); - $vid = @$this->GetIDForIdent($Ident); - if ($vid === false) { - $this->MaintainVariable($Ident, $Item, $Typ, '', 0, true); - $vid = $this->GetIDForIdent($Ident); - } - } - } - - public function ReceiveData($JSONString) - { - $Data = utf8_decode(json_decode($JSONString)->Buffer); - $this->SendDebug('Receive', $Data, 0); - $ReceiveItems = json_decode($Data, true); - - if ($ReceiveItems === null) { - trigger_error('Error receive Data', E_USER_NOTICE); - return; - } - $ReceiveItems = $this->DecodeUTF8($ReceiveItems); - $this->SendDebug('ReceiveItems', $ReceiveItems, 0); - $ConfigItems = array_column(json_decode($this->ReadPropertyString('Items'), true), 'Type', 'Item'); - - $Items = array_intersect_key($ReceiveItems, $ConfigItems); - $this->SendDebug('ProcessItems', $Items, 0); - - foreach ($Items as $Item => $Value) { - $Ident = $this->generateIdent($Item); - $vid = @$this->GetIDForIdent($Ident); - if ($vid === false) { - $this->MaintainVariable($Ident, $Item, $ConfigItems[$Item], '', 0, true); - $vid = $this->GetIDForIdent($Ident); - } - SetValue($vid, $Value); - } - } - - protected function generateIdent($Name) - { - if (preg_match('/^[a-zA-Z0-9]+$/', $Name)) { - return $Name; - } - return preg_replace('/[^a-z0-9]+/i', '', $Name); - } -} diff --git a/README.md b/README.md index 217b68c..813daa0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -[![Version](https://img.shields.io/badge/Symcon-PHPModul-red.svg)](https://www.symcon.de/service/dokumentation/entwicklerbereich/sdk-tools/sdk-php/) +[![SDK](https://img.shields.io/badge/Symcon-PHPModul-red.svg)](https://www.symcon.de/service/dokumentation/entwicklerbereich/sdk-tools/sdk-php/) [![Version](https://img.shields.io/badge/Modul%20Version-2.4-blue.svg)]() [![License](https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-green.svg)](https://creativecommons.org/licenses/by-nc-sa/4.0/) [![Version](https://img.shields.io/badge/Symcon%20Version-4.3%20%3E-green.svg)](https://www.symcon.de/forum/threads/30857-IP-Symcon-4-3-%28Stable%29-Changelog) [![StyleCI](https://styleci.io/repos/104255893/shield?style=flat)](https://styleci.io/repos/104255893) -# IPSNetwork +# Network Diese Library enthält verschiedene Module für Netzwerkanwendungen. ## Dokumentation @@ -49,7 +49,7 @@ Diese Library enthält verschiedene Module für Netzwerkanwendungen. **IPS 4.3:** Bei privater Nutzung: Über das 'Module-Control' in IPS folgende URL hinzufügen. - `git://github.com/Nall-chan/IPSNetwork.git` + `git://github.com/Nall-chan/Network.git` **Bei kommerzieller Nutzung (z.B. als Errichter oder Integrator) wenden Sie sich bitte an den Autor.** @@ -61,14 +61,14 @@ Details sind in der Dokumentation der jeweiligen Module beschrieben. ### 1. GUID der Module -| Modul | Typ | Prefix | GUID | +| Modul | Typ | Prefix | GUID | | :--------------------: | :------: | :-------: | :------------------------------------: | -| Client Splitter | Splitter | WSC | {7A107D38-75ED-47CB-83F9-F41228CAEEFA} | -| DHCP Sniffer | Device | DHCP | {E93BCE5E-BA95-424E-8C3A-BF6AEE6CB976} | -| Webhook Reverse Proxy | Core | HOOKPROXY | {26E5D8BC-8117-4DFB-9E85-66BB30F1CF95} | -| WebsocketClient | Splitter | WSC | {3AB77A94-3467-4E66-8A73-840B4AD89582} | -| WebsocketServer | Splitter | WSS | {7869923C-6E1D-4E66-A0BD-627FAD1679C2} | -| WebSocketInterfaceTest | Splitter | WSTest | {FC11DB7C-4999-4EA7-B57A-82A878ADD273} | +| Client Splitter | Splitter | WSC | {7A107D38-75ED-47CB-83F9-F41228CAEEFA} | +| DHCP Sniffer | Device | DHCP | {E93BCE5E-BA95-424E-8C3A-BF6AEE6CB976} | +| Webhook Reverse Proxy | Core | HOOKPROXY | {26E5D8BC-8117-4DFB-9E85-66BB30F1CF95} | +| WebsocketClient | Splitter | WSC | {3AB77A94-3467-4E66-8A73-840B4AD89582} | +| WebsocketServer | Splitter | WSS | {7869923C-6E1D-4E66-A0BD-627FAD1679C2} | +| WebSocketInterfaceTest | Splitter | WSTest | {FC11DB7C-4999-4EA7-B57A-82A878ADD273} | ### 2. Changlog @@ -88,7 +88,7 @@ Version 2.01: - Doku ergänzt Version 2.0: - - WebSocket-Module in IPSNetwork überführt + - WebSocket-Module in Network überführt Version 1.0: - Erstes offizielles Release @@ -97,7 +97,7 @@ Version 1.0: Die Library ist für die nicht kommzerielle Nutzung kostenlos, Schenkungen als Unterstützung für den Autor werden hier akzeptiert: - + ## 6. Lizenz diff --git a/WebSocketClient/README.md b/WebSocketClient/README.md index abfd1be..7a6a2c3 100644 --- a/WebSocketClient/README.md +++ b/WebSocketClient/README.md @@ -1,4 +1,4 @@ -# WebSocket-Client (IPSNetwork) +# WebSocket-Client (Network) Implementierung eines Clients mit Websocket Protokoll in IPS. @@ -30,7 +30,7 @@ Implementierung eines Clients mit Websocket Protokoll in IPS. ## 3. Installation Über das Modul-Control folgende URL hinzufügen. - `git://github.com/Nall-chan/IPSNetwork.git` + `git://github.com/Nall-chan/Network.git` **Bei kommerzieller Nutzung (z.B. als Errichter oder Integrator) wenden Sie sich bitte an den Autor.** @@ -112,24 +112,24 @@ bool WSC_SendPing(integer $InstanzeID, string $Text); GUID des Moduls (z.B. wenn Instanz per PHP angelegt werden soll): -| Instanz | GUID | +| Instanz | GUID | | :--------------: | :------------------------------------: | | Websocket Client | {3AB77A94-3467-4E66-8A73-840B4AD89582} | Eigenschaften des 'Websocket Client' für Get/SetProperty-Befehle: -| Eigenschaft | Typ | Standardwert | Funktion | -| :----------: | :-----: | :----------: | :-----------------------------------------------------------------------------------: | -| Open | boolean | false | false für inaktiv, true für aktiv | -| URL | string | | Die URL auf die sich verbunden wird | -| Version | integer | 13 | Die WebSocket-Version 13, 8 oder 6 | -| Origin | string | | Das Origin Feld im Protokoll | -| PingInterval | integer | 0 | In Sekunden, wann ein Ping an den Server gesendet wird | -| PingPayload | string | | Die im Ping zu versendenen Daten | -| Frame | integer | 1 | Format in welchen Daten versendet werden, wenn der Typ nicht bekannt ist (2 = binär) | -| BasisAuth | boolean | false | true = Basis-Authentifizierung verwenden | -| Username | string | | Benutzername für die Authentifizierung | -| Password | string | | Passwort für die Authentifizierung | +| Eigenschaft | Typ | Standardwert | Funktion | +| :----------: | :-----: | :----------: | :----------------------------------------------------------------------------------: | +| Open | boolean | false | false für inaktiv, true für aktiv | +| URL | string | | Die URL auf die sich verbunden wird | +| Version | integer | 13 | Die WebSocket-Version 13, 8 oder 6 | +| Origin | string | | Das Origin Feld im Protokoll | +| PingInterval | integer | 0 | In Sekunden, wann ein Ping an den Server gesendet wird | +| PingPayload | string | | Die im Ping zu versendenen Daten | +| Frame | integer | 1 | Format in welchen Daten versendet werden, wenn der Typ nicht bekannt ist (2 = binär) | +| BasisAuth | boolean | false | true = Basis-Authentifizierung verwenden | +| Username | string | | Benutzername für die Authentifizierung | +| Password | string | | Passwort für die Authentifizierung | ## 8. Datenaustausch @@ -139,11 +139,11 @@ Eigenschaften des 'Websocket Client' für Get/SetProperty-Befehle: Der WebSocket-Client buffert die Daten eigenständig bis zum nächten Paket mit gesetzten Fin-Flag. Ping/Pong Funktionalität sowie das manuelle schließen der Verbindung sind aktuell nicht vorgesehen. -| Parameter | Typ | Beschreibung | -| :----------: | :-----: | :-------------------------------------------------------: | -| DataID | string | {C51A4B94-8195-4673-B78D-04D91D52D2DD} | -| FrameTyp | integer | 1 = text, 2 = binär | -| Buffer | string | Payload | +| Parameter | Typ | Beschreibung | +| :-------: | :-----: | :------------------------------------: | +| DataID | string | {C51A4B94-8195-4673-B78D-04D91D52D2DD} | +| FrameTyp | integer | 1 = text, 2 = binär | +| Buffer | string | Payload | ![](imgs/IfWSC.png) @@ -152,12 +152,12 @@ Eigenschaften des 'Websocket Client' für Get/SetProperty-Befehle: Es ist empfohlen nur den FrameTyp 1 & 2 in Verbindung mit Fin = true zu nutzen! Die Instanz meldet True zurück, solange sie Verbunden ist. -| Parameter | Typ | Beschreibung | -| :----------: | :-----: | :-------------------------------------------------------: | -| DataID | string | {BC49DE11-24CA-484D-85AE-9B6F24D89321} | -| FrameTyp | integer | 0 = continuation, 1 = text, 2 = binär | -| Fin | bool | true wenn Paket komplett, false wenn weitere Daten folgen | -| Buffer | string | Payload | +| Parameter | Typ | Beschreibung | +| :-------: | :-----: | :-------------------------------------------------------: | +| DataID | string | {BC49DE11-24CA-484D-85AE-9B6F24D89321} | +| FrameTyp | integer | 0 = continuation, 1 = text, 2 = binär | +| Fin | bool | true wenn Paket komplett, false wenn weitere Daten folgen | +| Buffer | string | Payload | ![](imgs/IfWSC2.png) @@ -166,7 +166,7 @@ Eigenschaften des 'Websocket Client' für Get/SetProperty-Befehle: **Changlog:** Version 1.1: - - In IPSNetwork-Library integriert + - In Network-Library integriert Version 1.0: - Erstes offizielles Release diff --git a/WebSocketClient/form.json b/WebSocketClient/form.json index 30f0dac..ce4d00f 100644 --- a/WebSocketClient/form.json +++ b/WebSocketClient/form.json @@ -1,145 +1,143 @@ { - "elements": - [ - { - "name": "Open", - "type": "CheckBox", - "caption": "Open" - }, - { - "type": "Label", - "caption": "Use ws:// or wss:// and :port when using none HTTP-default Ports." - }, - { - "type": "Label", - "caption": "Example: wss://my.host.org:9091/websocket/" - }, - { - "name": "URL", - "type": "ValidationTextBox", - "caption": "URL" - }, - { - "type": "Label", - "caption": "--------------------------------------------------" - }, - { - "type": "Label", - "caption": "Advanced settings:" - }, - { - "name": "Protocol", - "type": "ValidationTextBox", - "caption": "Used protocol" - }, - { - "name": "Version", - "type": "Select", - "caption": "Version", - "options": [ - { - "caption": "6", - "value": 6 - }, - { - "caption": "8", - "value": 8 - }, - { - "caption": "13", - "value": 13 - } - ] - }, - { - "name": "Origin", - "type": "ValidationTextBox", - "caption": "Send origin" - }, - { - "type": "Label", - "caption": "Send Ping:" - }, - { - "name": "PingInterval", - "type": "IntervalBox", - "caption": "seconds" - }, - { - "name": "PingPayload", - "type": "ValidationTextBox", - "caption": "Ping payload" - }, - { - "name": "Frame", - "type": "Select", - "caption": "Default for SendText", - "options": [ - { - "caption": "text", - "value": 1 - }, - { - "caption": "binary", - "value": 2 - } - ] - }, - { - "type": "Label", - "caption": "--------------------------------------------------" - }, - { - "type": "Label", - "caption": "Optional HTTP Basic-Authentication:" - }, - { - "name": "BasisAuth", - "type": "CheckBox", - "caption": "Active" - }, - { - "name": "Username", - "type": "ValidationTextBox", - "caption": "Username" - }, - { - "name": "Password", - "type": "PasswordTextBox", - "caption": "Password" + "elements": [ + { + "name": "Open", + "type": "CheckBox", + "caption": "Open" + }, + { + "type": "Label", + "caption": "Use ws:// or wss:// and :port when using none HTTP-default Ports." + }, + { + "type": "Label", + "caption": "Example: wss://my.host.org:9091/websocket/" + }, + { + "name": "URL", + "type": "ValidationTextBox", + "caption": "URL" + }, + { + "type": "Label", + "caption": "--------------------------------------------------" + }, + { + "type": "Label", + "caption": "Advanced settings:" + }, + { + "name": "Protocol", + "type": "ValidationTextBox", + "caption": "Used protocol" + }, + { + "name": "Version", + "type": "Select", + "caption": "Version", + "options": [ + { + "caption": "6", + "value": 6 + }, + { + "caption": "8", + "value": 8 + }, + { + "caption": "13", + "value": 13 } - ], - "status": - [ - { - "code": 102, - "icon": "active", - "caption": "Connected" - }, - { - "code": 104, - "icon": "inactive", - "caption": "Interface closed" - }, - { - "code": 201, - "icon": "error", - "caption": "Connection lost" - }, - { - "code": 202, - "icon": "error", - "caption": "URL invalid" - }, - { - "code": 203, - "icon": "error", - "caption": "Handshake error" - }, - { - "code": 204, - "icon": "error", - "caption": "Ping interval to small" + ] + }, + { + "name": "Origin", + "type": "ValidationTextBox", + "caption": "Send origin" + }, + { + "type": "Label", + "caption": "Send Ping:" + }, + { + "name": "PingInterval", + "type": "IntervalBox", + "caption": "seconds" + }, + { + "name": "PingPayload", + "type": "ValidationTextBox", + "caption": "Ping payload" + }, + { + "name": "Frame", + "type": "Select", + "caption": "Default for SendText", + "options": [ + { + "caption": "text", + "value": 1 + }, + { + "caption": "binary", + "value": 2 } ] + }, + { + "type": "Label", + "caption": "--------------------------------------------------" + }, + { + "type": "Label", + "caption": "Optional HTTP Basic-Authentication:" + }, + { + "name": "BasisAuth", + "type": "CheckBox", + "caption": "Active" + }, + { + "name": "Username", + "type": "ValidationTextBox", + "caption": "Username" + }, + { + "name": "Password", + "type": "PasswordTextBox", + "caption": "Password" + } + ], + "status": [ + { + "code": 102, + "icon": "active", + "caption": "Connected" + }, + { + "code": 104, + "icon": "inactive", + "caption": "Interface closed" + }, + { + "code": 201, + "icon": "error", + "caption": "Connection lost" + }, + { + "code": 202, + "icon": "error", + "caption": "URL invalid" + }, + { + "code": 203, + "icon": "error", + "caption": "Handshake error" + }, + { + "code": 204, + "icon": "error", + "caption": "Ping interval to small" + } + ] } \ No newline at end of file diff --git a/WebSocketClient/module.json b/WebSocketClient/module.json index 99ac686..4dac8ed 100644 --- a/WebSocketClient/module.json +++ b/WebSocketClient/module.json @@ -3,9 +3,21 @@ "name": "WebsocketClient", "type": 2, "vendor": "", - "aliases": ["Websocket Client"], - "parentRequirements": ["{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}"], - "childRequirements": ["{018EF6B5-AB94-40C6-AA53-46943E824ACF}","{C51A4B94-8195-4673-B78D-04D91D52D2DD}"], - "implemented": ["{018EF6B5-AB94-40C6-AA53-46943E824ACF}", "{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}","{BC49DE11-24CA-484D-85AE-9B6F24D89321}"], - "prefix": "WSC" + "aliases": [ + "Websocket Client" + ], + "parentRequirements": [ + "{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}" + ], + "childRequirements": [ + "{018EF6B5-AB94-40C6-AA53-46943E824ACF}", + "{C51A4B94-8195-4673-B78D-04D91D52D2DD}" + ], + "implemented": [ + "{018EF6B5-AB94-40C6-AA53-46943E824ACF}", + "{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}", + "{BC49DE11-24CA-484D-85AE-9B6F24D89321}" + ], + "prefix": "WSC", + "url": "https://github.com/Nall-chan/Network" } \ No newline at end of file diff --git a/WebSocketClient/module.php b/WebSocketClient/module.php index 3c7dc84..0c0b170 100644 --- a/WebSocketClient/module.php +++ b/WebSocketClient/module.php @@ -1,5 +1,7 @@ KernelReady(); break; @@ -151,22 +155,6 @@ public function MessageSink($TimeStamp, $SenderID, $Message, $Data) } } - /** - * Wird ausgeführt wenn der Kernel hochgefahren wurde. - */ - protected function KernelReady() - { - @$this->ApplyChanges(); - } - - /** - * Wird ausgeführt wenn sich der Parent ändert. - */ - protected function ForceRefresh() - { - $this->ApplyChanges(); - } - /** * Interne Funktion des SDK. */ @@ -222,7 +210,7 @@ public function ApplyChanges() } $OldState = $this->State; $this->SendDebug(__FUNCTION__, 'OldState:' . $OldState, 0); - if ((($OldState != WebSocketState::unknow) and ( $OldState != WebSocketState::Connected)) or ( $OldState == WebSocketState::init)) { + if ((($OldState != WebSocketState::unknow) && ($OldState != WebSocketState::Connected)) || ($OldState == WebSocketState::init)) { return; } @@ -254,7 +242,7 @@ public function ApplyChanges() $Open = false; trigger_error('Invalid URL', E_USER_NOTICE); } else { - if (($this->ReadPropertyInteger('PingInterval') != 0) and ( $this->ReadPropertyInteger('PingInterval') < 5)) { + if (($this->ReadPropertyInteger('PingInterval') != 0) && ($this->ReadPropertyInteger('PingInterval') < 5)) { $NewState = IS_EBASE + 4; $Open = false; trigger_error('Ping interval to small', E_USER_NOTICE); @@ -328,6 +316,241 @@ public function ApplyChanges() $this->SetStatus($NewState); } + //################# DATAPOINTS CHILDS + /** + * Interne Funktion des SDK. Nimmt Daten von Childs entgegen und sendet Diese weiter. + * + * @param string $JSONString + * @result bool true wenn Daten gesendet werden konnten, sonst false. + */ + public function ForwardData($JSONString) + { + if ($this->State != WebSocketState::Connected) { + trigger_error('Not connected', E_USER_NOTICE); + return false; + } + $Data = json_decode($JSONString); + if ($Data->DataID == '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}') { //Raw weitersenden + $this->SendText(utf8_decode($Data->Buffer)); + } + if ($Data->DataID == '{BC49DE11-24CA-484D-85AE-9B6F24D89321}') { // WSC send + $this->Send(utf8_decode($Data->Buffer), $Data->FrameTyp, $Data->Fin); + } + return true; + } + + //################# DATAPOINTS PARENT + /** + * Empfängt Daten vom Parent. + * + * @param string $JSONString Das empfangene JSON-kodierte Objekt vom Parent. + * @result bool True wenn Daten verarbeitet wurden, sonst false. + */ + public function ReceiveData($JSONString) + { + $data = json_decode($JSONString); + if ($this->UseTLS) { // TLS aktiv + $Data = $this->TLSReceiveBuffer . utf8_decode($data->Buffer); + if ((ord($Data[0]) >= 0x14) && (ord($Data[0]) <= 0x18) && (substr($Data, 1, 2) == "\x03\x03")) { + $TLSData = $Data; + $Data = ''; + + while (strlen($TLSData) > 0) { + $len = unpack('n', substr($TLSData, 3, 2))[1] + 5; + if (strlen($TLSData) >= $len) { + $Part = substr($TLSData, 0, $len); + $TLSData = substr($TLSData, $len); + if ($this->TLSState == TLSState::init) { + if (!$this->WriteTLSReceiveData($Part)) { + break; + } + } elseif ($this->TLSState == TLSState::Connected) { + $this->SendDebug('Receive TLS Frame', $Part, 0); + try { + $TLS = $this->GetTLSContext(); + $TLS->encode($Part); + $Data .= $TLS->input(); + $this->SetTLSContext($TLS); + } catch (\PTLS\Exceptions\TLSAlertException $e) { + $this->SendDebug('Error', $e->getMessage(), 0); + $out = $e->decode(); + if (($out !== null) && (strlen($out) > 0)) { + $JSON['DataID'] = '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}'; + $JSON['Buffer'] = utf8_encode($out); + $JsonString = json_encode($JSON); + parent::SendDataToParent($JsonString); + } + trigger_error($e->getMessage(), E_USER_NOTICE); + $this->TLSState = TLSState::unknow; + $this->TLSReceiveBuffer = ''; + return; + } + } + } else { + break; + } + } + if (strlen($TLSData) == 0) { + $this->TLSReceiveBuffer = ''; + } else { + //$this->SendDebug('Receive TLS Part', $TLSData, 0); + $this->TLSReceiveBuffer = $TLSData; + } + } else { // Anfang (inkl. Buffer) paßt nicht + $this->TLSReceiveBuffer = ''; + return; + } + } else { // ende TLS + $Data = utf8_decode($data->Buffer); + } + + $Data = $this->Buffer . $Data; + if ($Data == '') { + return; + } + switch ($this->State) { + case WebSocketState::HandshakeSend: + if (strpos($Data, "\r\n\r\n") !== false) { + $this->Handshake = $Data; + $this->State = WebSocketState::HandshakeReceived; + $Data = ''; + } else { + $this->SendDebug('Receive inclomplete Handshake', $Data, 0); + } + $this->Buffer = $Data; + break; + case WebSocketState::Connected: + $this->SendDebug('ReceivePacket', $Data, 1); + while (true) { + if (strlen($Data) < 2) { + break; + } + $Frame = new WebSocketFrame($Data); + if ($Data == $Frame->Tail) { + break; + } + $Data = $Frame->Tail; + $Frame->Tail = null; + $this->DecodeFrame($Frame); + } + $this->Buffer = $Data; + break; + case WebSocketState::CloseSend: + $this->SendDebug('Receive', 'Server answer client stream close !', 0); + $this->State = WebSocketState::CloseReceived; + break; + } + } + + //################# PUBLIC + /** + * Versendet RawData mit OpCode an den IO. + * + * @param string $Text + */ + public function SendText(string $Text) + { + if ($this->State != WebSocketState::Connected) { + trigger_error('Not connected', E_USER_NOTICE); + return false; + } + $this->Send($Text, $this->ReadPropertyInteger('Frame')); + return true; + } + + /** + * Versendet ein String. + * + * @param bool $Fin + * @param int $OPCode + * @param string $Text + */ + public function SendPacket(bool $Fin, int $OPCode, string $Text) + { + if (($OPCode < 0) || ($OPCode > 2)) { + trigger_error('OpCode invalid', E_USER_NOTICE); + return false; + } + if ($this->State != WebSocketState::Connected) { + trigger_error('Not connected', E_USER_NOTICE); + return false; + } + $this->Send($Text, $OPCode, $Fin); + return true; + } + + /** + * Wird durch den Timer aufgerufen und senden einen Ping an den Server. + */ + public function Keepalive() + { + $result = @$this->SendPing($this->ReadPropertyString('PingPayload')); + if ($result !== true) { + $this->SetStatus(IS_EBASE + 1); + $this->SetTimerInterval('KeepAlive', 0); + trigger_error('Ping timeout', E_USER_NOTICE); + } + } + + /** + * Versendet einen Ping an den Server. + * + * @param string $Text Der zu versendene Payload im Ping. + * + * @return bool True wenn Ping bestätigt wurde, sonst false. + */ + public function SendPing(string $Text) + { + $this->Send($Text, WebSocketOPCode::ping); + $Result = $this->WaitForPong(); + if ($Result === false) { + trigger_error('Timeout', E_USER_NOTICE); + return false; + } + + if ($Result != $Text) { + trigger_error('Wrong pong received', E_USER_NOTICE); + return false; + } + return true; + } + + /** + * Wird ausgeführt wenn der Kernel hochgefahren wurde. + */ + protected function KernelReady() + { + @$this->ApplyChanges(); + } + + /** + * Wird ausgeführt wenn sich der Parent ändert. + */ + protected function ForceRefresh() + { + $this->ApplyChanges(); + } + + /** + * Sendet ein Paket an den Parent. + * + * @param string $Data + */ + protected function SendDataToParent($Data) + { + $JSON['DataID'] = '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}'; + if ($this->UseTLS) { + $TLS = $this->GetTLSContext(); + $this->SendDebug('Send TLS', $Data, 0); + $Data = $TLS->output($Data)->decode(); + $this->SetTLSContext($TLS); + } + $JSON['Buffer'] = utf8_encode($Data); + $JsonString = json_encode($JSON); + $this->SendDebug('Send Packet', $Data, 1); + parent::SendDataToParent($JsonString); + } + //################# PRIVATE /** * Baut eine TLS Verbindung auf. @@ -367,7 +590,7 @@ private function CreateTLSConnection() } catch (TLSAlertException $e) { $this->SendDebug('Error', $e->getMessage(), 1); $out = $e->decode(); - if (($out !== null) and ( strlen($out) > 0)) { + if (($out !== null) && (strlen($out) > 0)) { $JSON['DataID'] = '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}'; $JSON['Buffer'] = utf8_encode($out); $JsonString = json_encode($JSON); @@ -380,7 +603,7 @@ private function CreateTLSConnection() } $SendData = $TLS->decode(); - if (($SendData !== null) and ( strlen($SendData) > 0)) { + if (($SendData !== null) && (strlen($SendData) > 0)) { $this->SendDebug('TLS loop ' . $loop, $SendData, 0); $JSON['DataID'] = '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}'; $JSON['Buffer'] = utf8_encode($SendData); @@ -653,29 +876,6 @@ private function WaitForPong() return false; } - //################# DATAPOINTS CHILDS - /** - * Interne Funktion des SDK. Nimmt Daten von Childs entgegen und sendet Diese weiter. - * - * @param string $JSONString - * @result bool true wenn Daten gesendet werden konnten, sonst false. - */ - public function ForwardData($JSONString) - { - if ($this->State != WebSocketState::Connected) { - trigger_error('Not connected', E_USER_NOTICE); - return false; - } - $Data = json_decode($JSONString); - if ($Data->DataID == '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}') { //Raw weitersenden - $this->SendText(utf8_decode($Data->Buffer)); - } - if ($Data->DataID == '{BC49DE11-24CA-484D-85AE-9B6F24D89321}') { // WSC send - $this->Send(utf8_decode($Data->Buffer), $Data->FrameTyp, $Data->Fin); - } - return true; - } - /** * Sendet die Rohdaten an die Childs. * @@ -694,204 +894,8 @@ private function SendDataToChilds(string $RawData) $this->SendDataToChildren($Data); } - //################# DATAPOINTS PARENT - /** - * Empfängt Daten vom Parent. - * - * @param string $JSONString Das empfangene JSON-kodierte Objekt vom Parent. - * @result bool True wenn Daten verarbeitet wurden, sonst false. - */ - public function ReceiveData($JSONString) - { - $data = json_decode($JSONString); - if ($this->UseTLS) { // TLS aktiv - $Data = $this->TLSReceiveBuffer . utf8_decode($data->Buffer); - if ((ord($Data[0]) >= 0x14) && (ord($Data[0]) <= 0x18) && (substr($Data, 1, 2) == "\x03\x03")) { - $TLSData = $Data; - $Data = ''; - - while (strlen($TLSData) > 0) { - $len = unpack('n', substr($TLSData, 3, 2))[1] + 5; - if (strlen($TLSData) >= $len) { - $Part = substr($TLSData, 0, $len); - $TLSData = substr($TLSData, $len); - if ($this->TLSState == TLSState::init) { - if (!$this->WriteTLSReceiveData($Part)) { - break; - } - } elseif ($this->TLSState == TLSState::Connected) { - $this->SendDebug('Receive TLS Frame', $Part, 0); - try { - $TLS = $this->GetTLSContext(); - $TLS->encode($Part); - $Data .= $TLS->input(); - $this->SetTLSContext($TLS); - } catch (\PTLS\Exceptions\TLSAlertException $e) { - $this->SendDebug('Error', $e->getMessage(), 0); - $out = $e->decode(); - if (($out !== null) and ( strlen($out) > 0)) { - $JSON['DataID'] = '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}'; - $JSON['Buffer'] = utf8_encode($out); - $JsonString = json_encode($JSON); - parent::SendDataToParent($JsonString); - } - trigger_error($e->getMessage(), E_USER_NOTICE); - $this->TLSState = TLSState::unknow; - $this->TLSReceiveBuffer = ''; - return; - } - } - } else { - break; - } - } - if (strlen($TLSData) == 0) { - $this->TLSReceiveBuffer = ''; - } else { - //$this->SendDebug('Receive TLS Part', $TLSData, 0); - $this->TLSReceiveBuffer = $TLSData; - } - } else { // Anfang (inkl. Buffer) paßt nicht - $this->TLSReceiveBuffer = ''; - return; - } - } else { // ende TLS - $Data = utf8_decode($data->Buffer); - } - - $Data = $this->Buffer . $Data; - if ($Data == '') { - return; - } - switch ($this->State) { - case WebSocketState::HandshakeSend: - if (strpos($Data, "\r\n\r\n") !== false) { - $this->Handshake = $Data; - $this->State = WebSocketState::HandshakeReceived; - $Data = ''; - } else { - $this->SendDebug('Receive inclomplete Handshake', $Data, 0); - } - $this->Buffer = $Data; - break; - case WebSocketState::Connected: - $this->SendDebug('ReceivePacket', $Data, 1); - while (true) { - if (strlen($Data) < 2) { - break; - } - $Frame = new WebSocketFrame($Data); - if ($Data == $Frame->Tail) { - break; - } - $Data = $Frame->Tail; - $Frame->Tail = null; - $this->DecodeFrame($Frame); - } - $this->Buffer = $Data; - break; - case WebSocketState::CloseSend: - $this->SendDebug('Receive', 'Server answer client stream close !', 0); - $this->State = WebSocketState::CloseReceived; - break; - } - } - - /** - * Sendet ein Paket an den Parent. - * - * @param string $Data - */ - protected function SendDataToParent($Data) - { - $JSON['DataID'] = '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}'; - if ($this->UseTLS) { - $TLS = $this->GetTLSContext(); - $this->SendDebug('Send TLS', $Data, 0); - $Data = $TLS->output($Data)->decode(); - $this->SetTLSContext($TLS); - } - $JSON['Buffer'] = utf8_encode($Data); - $JsonString = json_encode($JSON); - $this->SendDebug('Send Packet', $Data, 1); - parent::SendDataToParent($JsonString); - } - - //################# PUBLIC - /** - * Versendet RawData mit OpCode an den IO. - * - * @param string $Text - */ - public function SendText(string $Text) - { - if ($this->State != WebSocketState::Connected) { - trigger_error('Not connected', E_USER_NOTICE); - return false; - } - $this->Send($Text, $this->ReadPropertyInteger('Frame')); - return true; - } - /** - * Versendet ein String. * - * @param bool $Fin - * @param int $OPCode - * @param string $Text - */ - public function SendPacket(bool $Fin, int $OPCode, string $Text) - { - if (($OPCode < 0) || ($OPCode > 2)) { - trigger_error('OpCode invalid', E_USER_NOTICE); - return false; - } - if ($this->State != WebSocketState::Connected) { - trigger_error('Not connected', E_USER_NOTICE); - return false; - } - $this->Send($Text, $OPCode, $Fin); - return true; - } - - /** - * Wird durch den Timer aufgerufen und senden einen Ping an den Server. - */ - public function Keepalive() - { - $result = @$this->SendPing($this->ReadPropertyString('PingPayload')); - if ($result !== true) { - $this->SetStatus(IS_EBASE + 1); - $this->SetTimerInterval('KeepAlive', 0); - trigger_error('Ping timeout', E_USER_NOTICE); - } - } - - /** - * Versendet einen Ping an den Server. - * - * @param string $Text Der zu versendene Payload im Ping. - * - * @return bool True wenn Ping bestätigt wurde, sonst false. - */ - public function SendPing(string $Text) - { - $this->Send($Text, WebSocketOPCode::ping); - $Result = $this->WaitForPong(); - if ($Result === false) { - trigger_error('Timeout', E_USER_NOTICE); - return false; - } - - if ($Result != $Text) { - trigger_error('Wrong pong received', E_USER_NOTICE); - return false; - } - return true; - } - - /** - * * @return \PTLS\TLSContext */ private function GetTLSContext() @@ -901,7 +905,7 @@ private function GetTLSContext() } /** - * + * * @param \PTLS\TLSContext $TLS */ private function SetTLSContext($TLS) @@ -909,7 +913,6 @@ private function SetTLSContext($TLS) $this->Multi_TLS = $TLS; $this->unlock('TLS'); } - } /* @} */ diff --git a/WebSocketServer/README.md b/WebSocketServer/README.md index 1535868..9c0ffb5 100644 --- a/WebSocketServer/README.md +++ b/WebSocketServer/README.md @@ -1,4 +1,4 @@ -# WebSocket-Server (IPSNetwork) +# WebSocket-Server (Network) Implementierung eines Server mit Websocket Protokoll in IPS. @@ -29,7 +29,7 @@ Implementierung eines Server mit Websocket Protokoll in IPS. ## 3. Installation Über das Modul-Control folgende URL hinzufügen. - `git://github.com/Nall-chan/IPSNetwork.git` + `git://github.com/Nall-chan/Network.git` **Bei kommerzieller Nutzung (z.B. als Errichter oder Integrator) wenden Sie sich bitte an den Autor.** @@ -97,26 +97,26 @@ bool WSS_SendPing(integer $InstanzeID, string $ClientIP, int $ClientPort, string GUID des Moduls (z.B. wenn Instanz per PHP angelegt werden soll): -| Instanz | GUID | +| Instanz | GUID | | :--------------: | :------------------------------------: | | Websocket Server | {7869923C-6E1D-4E66-A0BD-627FAD1679C2} | Eigenschaften des 'Websocket Server' für Get/SetProperty-Befehle: -| Eigenschaft | Typ | Standardwert | Funktion | -| :----------: | :-----: | :----------: | :---------------------------------------------------------------------------: | -| Open | boolean | false | false für inaktiv, true für aktiv | -| Port | integer | 8080 | Port auf welchen der Server Verbindungen annimmt | -| URI | string | / | URI auf welche Clients sich verbinden dürfen | -| Interval | integer | 0 | Timeout & Ping-Intervall wenn Clients keine Daten übertragen | -| TLS | boolean | false | True wenn Transport-Socket-Layer Verbindungen erlaubt sind | -| Plain | boolean | true | True wenn unverschlüsselte Verbindungen erlaubt sind | -| CertFile | string | siehe * | Pfad zum Zertifikat (IPS4.1) / Zertifikat base64 encodiert (IPS4.2>) | -| KeyFile | string | siehe * | Pfad zum privaten Schlüssel (IPS4.1) / Schlüssel base64 encodiert (IPS4.2>) | -| KeyPassword | string | siehe * | Passwort vom privaten Schlüssel | -| BasisAuth | boolean | false | true = Basis-Authentifizierung verwenden | -| Username | string | | Benutzername für die Authentifizierung | -| Password | string | | Passwort für die Authentifizierung | +| Eigenschaft | Typ | Standardwert | Funktion | +| :---------: | :-----: | :----------: | :-------------------------------------------------------------------------: | +| Open | boolean | false | false für inaktiv, true für aktiv | +| Port | integer | 8080 | Port auf welchen der Server Verbindungen annimmt | +| URI | string | / | URI auf welche Clients sich verbinden dürfen | +| Interval | integer | 0 | Timeout & Ping-Intervall wenn Clients keine Daten übertragen | +| TLS | boolean | false | True wenn Transport-Socket-Layer Verbindungen erlaubt sind | +| Plain | boolean | true | True wenn unverschlüsselte Verbindungen erlaubt sind | +| CertFile | string | siehe * | Pfad zum Zertifikat (IPS4.1) / Zertifikat base64 encodiert (IPS4.2>) | +| KeyFile | string | siehe * | Pfad zum privaten Schlüssel (IPS4.1) / Schlüssel base64 encodiert (IPS4.2>) | +| KeyPassword | string | siehe * | Passwort vom privaten Schlüssel | +| BasisAuth | boolean | false | true = Basis-Authentifizierung verwenden | +| Username | string | | Benutzername für die Authentifizierung | +| Password | string | | Passwort für die Authentifizierung | \* Sobald TLS auf True gesetzt wird und kein Zertifikat vorliegt, wird ein selbst-signiertes Zertifikat erzeugt und in der Instanz eingetragen. Es ist jederzeit möglich das Zertifikat durch ein eigenes zu erstzen. @@ -129,57 +129,57 @@ Eigenschaften des 'Websocket Server' für Get/SetProperty-Befehle: Die Datensätze werden erst nach dem Empfang eines Fin als ein Block weitergeleitet. Der WebSocket-Server buffert die Daten eigenständig bis zum nächten Paket mit gesetzten Fin-Flag. -| Parameter | Typ | Beschreibung | -| :----------: | :-----: | :-------------------------------------------------------------: | -| DataID | string | {8F1F6C32-B1AD-4B7F-8DFB-1244A96FCACF} | -| ClientIP | string | Die IP-Adresse des Client von welchem die Daten kommen | -| ClientPort | string | Die Port des Client von welchem die Daten kommen | -| FrameTyp | integer | 0 = neu verbunden, 1 = text, 2 = binär, 8 = getrennt, 10 = pong | -| Buffer | string | Payload, nur bei FrameTyp 1, 2 und 9 | +| Parameter | Typ | Beschreibung | +| :--------: | :-----: | :-------------------------------------------------------------: | +| DataID | string | {8F1F6C32-B1AD-4B7F-8DFB-1244A96FCACF} | +| ClientIP | string | Die IP-Adresse des Client von welchem die Daten kommen | +| ClientPort | string | Die Port des Client von welchem die Daten kommen | +| FrameTyp | integer | 0 = neu verbunden, 1 = text, 2 = binär, 8 = getrennt, 10 = pong | +| Buffer | string | Payload, nur bei FrameTyp 1, 2 und 9 | ![](imgs/IfWSS.png) Folgende GUID ist komplatible mit dem ServerSocket und kann ebenfalls zum empfang genutzt werden. -| Parameter | Typ | Beschreibung | -| :----------: | :-----: | :-------------------------------------------------------------: | -| DataID | string | {7A1272A4-CBDB-46EF-BFC6-DCF4A53D2FC7} | -| ClientIP | string | Die IP-Adresse des Client zu welchem die Daten versendet werden | -| ClientPort | string | Die Port des Client zu welchem die Daten versendet werden | -| Type | integer | 0 = Daten, 1 = neu verbunden, 2 = getrennt | -| Buffer | string | Payload, nur wenn Type = 0 ist | +| Parameter | Typ | Beschreibung | +| :--------: | :-----: | :-------------------------------------------------------------: | +| DataID | string | {7A1272A4-CBDB-46EF-BFC6-DCF4A53D2FC7} | +| ClientIP | string | Die IP-Adresse des Client zu welchem die Daten versendet werden | +| ClientPort | string | Die Port des Client zu welchem die Daten versendet werden | +| Type | integer | 0 = Daten, 1 = neu verbunden, 2 = getrennt | +| Buffer | string | Payload, nur wenn Type = 0 ist | **Datenversand:** Von der untergeordneten Instanz zum WebSocket-Server (SendDataToParent im fremden Modul). Es wird true zurückgeliefert wenn die Funktion gemäß FrameTyp erfolgreich ausgeführt wurde. -| Parameter | Typ | Beschreibung | -| :----------: | :-----: | :-------------------------------------------------------------: | -| DataID | string | {714B71FB-3D11-41D1-AFAC-E06F1E983E09} | -| ClientIP | string | Die IP-Adresse des Client zu welchem die Daten versendet werden | -| ClientPort | string | Die Port des Client von welchem die Daten kommen | -| FrameTyp | integer | 0 = continuation, 1 = text, 2 = binär, 8 = close, 9 = ping | -| Fin | bool | true wenn Paket komplett, false wenn weitere Daten folgen | -| Buffer | string | Payload, außer bei FrameTyp 8, dann leer | +| Parameter | Typ | Beschreibung | +| :--------: | :-----: | :-------------------------------------------------------------: | +| DataID | string | {714B71FB-3D11-41D1-AFAC-E06F1E983E09} | +| ClientIP | string | Die IP-Adresse des Client zu welchem die Daten versendet werden | +| ClientPort | string | Die Port des Client von welchem die Daten kommen | +| FrameTyp | integer | 0 = continuation, 1 = text, 2 = binär, 8 = close, 9 = ping | +| Fin | bool | true wenn Paket komplett, false wenn weitere Daten folgen | +| Buffer | string | Payload, außer bei FrameTyp 8, dann leer | ![](imgs/IfWSS2.png) Folgende GUID ist komplatible mit dem ServerSocket und kann ebenfalls zum senden genutzt werden. -| Parameter | Typ | Beschreibung | -| :----------: | :-----: | :-------------------------------------------------------------: | -| DataID | string | {C8792760-65CF-4C53-B5C7-A30FCC84FEFE} | -| ClientIP | string | Die IP-Adresse des Client zu welchem die Daten versendet werden | -| ClientPort | string | Die Port des Client zu welchem die Daten versendet werden | -| Type | integer | 0 = Daten, 2 = Verbindung soll getrennt werden | -| Buffer | string | Payload, außer bei Type 2, dann leer | +| Parameter | Typ | Beschreibung | +| :--------: | :-----: | :-------------------------------------------------------------: | +| DataID | string | {C8792760-65CF-4C53-B5C7-A30FCC84FEFE} | +| ClientIP | string | Die IP-Adresse des Client zu welchem die Daten versendet werden | +| ClientPort | string | Die Port des Client zu welchem die Daten versendet werden | +| Type | integer | 0 = Daten, 2 = Verbindung soll getrennt werden | +| Buffer | string | Payload, außer bei Type 2, dann leer | Mit folgender GUID (Virtual-IO) wird der Payload immer an alle verbundenen Clients versenden. -| Parameter | Typ | Beschreibung | -| :----------: | :-----: | :-------------------------------------------------------------: | -| DataID | string | {79827379-F36E-4ADA-8A95-5F8D1DC92FA9} | -| Buffer | string | Payload | +| Parameter | Typ | Beschreibung | +| :-------: | :----: | :------------------------------------: | +| DataID | string | {79827379-F36E-4ADA-8A95-5F8D1DC92FA9} | +| Buffer | string | Payload | ## 9. Anhang @@ -198,7 +198,7 @@ Version 1.2: - Mehrfache Verbindungen von einer IP sind nun möglich Version 1.1: - - In IPSNetwork-Library integriert + - In Network-Library integriert Version 1.0: - Erstes offizielles Release diff --git a/WebSocketServer/form.json b/WebSocketServer/form.json index 3b667a9..7a30999 100644 --- a/WebSocketServer/form.json +++ b/WebSocketServer/form.json @@ -1,141 +1,149 @@ { - "elements": - [{ - "type": "Label", - "caption": "Caution: Only use TLS with authentication if the port is accessible externally!" - }, { - "name": "Open", - "type": "CheckBox", - "caption": "Active" - }, - { - "name": "Port", - "type": "NumberSpinner", - "caption": "Port" - }, + "elements": [ + { + "type": "Label", + "caption": "Caution: Only use TLS with authentication if the port is accessible externally!" + }, + { + "name": "Open", + "type": "CheckBox", + "caption": "Active" + }, + { + "name": "Port", + "type": "NumberSpinner", + "caption": "Port" + }, + { + "name": "URI", + "type": "ValidationTextBox", + "caption": "URI" + }, + { + "name": "Interval", + "type": "NumberSpinner", + "caption": "Timeout" + }, + { + "type": "Select", + "name": "Mode", + "caption": "Mode", + "options": [ { - "name": "URI", - "type": "ValidationTextBox", - "caption": "URI" - }, { - "name": "Interval", - "type": "NumberSpinner", - "caption": "Timeout" - }, { - "type": "Select", - "name": "Mode", - "caption": "Mode", - "options": [ - { - "caption": "no TLS", - "value": [ - { - "name": "TLS", - "value": false - }, - { - "name": "Plain", - "value": true - } - ] - }, + "caption": "no TLS", + "value": [ { - "caption": "both", - "value": [ - { - "name": "TLS", - "value": true - }, - { - "name": "Plain", - "value": true - } - ] + "name": "TLS", + "value": false }, { - "caption": "only TLS", - "value": [ - { - "name": "TLS", - "value": true - }, - { - "name": "Plain", - "value": false - } - ] + "name": "Plain", + "value": true } ] - }, { - "type": "Label", - "caption": "--------------------------------------------------" - }, { - "type": "Label", - "caption": "Optional Certificate:" - }, { - "name": "CertFile", - "type": "SelectFile", - "extensions": ".pem,.crt,.cer", - "caption": "Certificate" - }, { - "name": "KeyFile", - "type": "SelectFile", - "extensions": ".pem,.key", - "caption": "Private key" - }, - { - "name": "KeyPassword", - "type": "PasswordTextBox", - "caption": "Passphrase(optional)" - }, { - "type": "Label", - "caption": "--------------------------------------------------" - }, { - "type": "Label", - "caption": "Optional HTTP Basic-Authentication:" - }, - { - "name": "BasisAuth", - "type": "CheckBox", - "caption": "Active" - }, - { - "name": "Username", - "type": "ValidationTextBox", - "caption": "Username" - }, - { - "name": "Password", - "type": "PasswordTextBox", - "caption": "Password" - } - ], - "status": - [ - { - "code": 102, - "icon": "active", - "caption": "Interface open" }, { - "code": 104, - "icon": "inactive", - "caption": "Interface closed" - }, - { - "code": 201, - "icon": "error", - "caption": "Certificate or key missing or not found" - }, - { - "code": 202, - "icon": "error", - "caption": "Port invalid" + "caption": "both", + "value": [ + { + "name": "TLS", + "value": true + }, + { + "name": "Plain", + "value": true + } + ] }, { - "code": 204, - "icon": "error", - "caption": "Ping interval to small" + "caption": "only TLS", + "value": [ + { + "name": "TLS", + "value": true + }, + { + "name": "Plain", + "value": false + } + ] } ] + }, + { + "type": "Label", + "caption": "--------------------------------------------------" + }, + { + "type": "Label", + "caption": "Optional Certificate:" + }, + { + "name": "CertFile", + "type": "SelectFile", + "extensions": ".pem,.crt,.cer", + "caption": "Certificate" + }, + { + "name": "KeyFile", + "type": "SelectFile", + "extensions": ".pem,.key", + "caption": "Private key" + }, + { + "name": "KeyPassword", + "type": "PasswordTextBox", + "caption": "Passphrase(optional)" + }, + { + "type": "Label", + "caption": "--------------------------------------------------" + }, + { + "type": "Label", + "caption": "Optional HTTP Basic-Authentication:" + }, + { + "name": "BasisAuth", + "type": "CheckBox", + "caption": "Active" + }, + { + "name": "Username", + "type": "ValidationTextBox", + "caption": "Username" + }, + { + "name": "Password", + "type": "PasswordTextBox", + "caption": "Password" + } + ], + "status": [ + { + "code": 102, + "icon": "active", + "caption": "Interface open" + }, + { + "code": 104, + "icon": "inactive", + "caption": "Interface closed" + }, + { + "code": 201, + "icon": "error", + "caption": "Certificate or key missing or not found" + }, + { + "code": 202, + "icon": "error", + "caption": "Port invalid" + }, + { + "code": 204, + "icon": "error", + "caption": "Ping interval to small" + } + ] } \ No newline at end of file diff --git a/WebSocketServer/locale.json b/WebSocketServer/locale.json index 6950872..b758be4 100644 --- a/WebSocketServer/locale.json +++ b/WebSocketServer/locale.json @@ -5,7 +5,7 @@ "Active": "Aktiv", "Port": "Port", "URI": "URI", - "Timeout": "Timeout", + "Timeout": "Zeitüberschreitung", "Mode": "Modus", "no TLS": "kein TLS", "both": "beides", @@ -27,7 +27,6 @@ "Unknow client": "Unbekannter Client", "Client not connected": "Client nicht verbunden", "FrameTyp invalid": "FrameTyp ungültig", - "Timeout": "Zeitüberschreitung", "Wrong pong received": "Falschen pong empfangen" } } diff --git a/WebSocketServer/module.json b/WebSocketServer/module.json index 7187b75..b458f54 100644 --- a/WebSocketServer/module.json +++ b/WebSocketServer/module.json @@ -3,9 +3,22 @@ "name": "WebsocketServer", "type": 2, "vendor": "", - "aliases": ["Websocket Server"], - "parentRequirements": ["{C8792760-65CF-4C53-B5C7-A30FCC84FEFE}"], - "childRequirements": ["{7A1272A4-CBDB-46EF-BFC6-DCF4A53D2FC7}","{8F1F6C32-B1AD-4B7F-8DFB-1244A96FCACF}"], - "implemented": ["{7A1272A4-CBDB-46EF-BFC6-DCF4A53D2FC7}", "{C8792760-65CF-4C53-B5C7-A30FCC84FEFE}","{714B71FB-3D11-41D1-AFAC-E06F1E983E09}","{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}"], - "prefix": "WSS" + "aliases": [ + "Websocket Server" + ], + "parentRequirements": [ + "{C8792760-65CF-4C53-B5C7-A30FCC84FEFE}" + ], + "childRequirements": [ + "{7A1272A4-CBDB-46EF-BFC6-DCF4A53D2FC7}", + "{8F1F6C32-B1AD-4B7F-8DFB-1244A96FCACF}" + ], + "implemented": [ + "{7A1272A4-CBDB-46EF-BFC6-DCF4A53D2FC7}", + "{C8792760-65CF-4C53-B5C7-A30FCC84FEFE}", + "{714B71FB-3D11-41D1-AFAC-E06F1E983E09}", + "{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}" + ], + "prefix": "WSS", + "url": "https://github.com/Nall-chan/Network" } \ No newline at end of file diff --git a/WebSocketServer/module.php b/WebSocketServer/module.php index b5a33d1..37d4553 100644 --- a/WebSocketServer/module.php +++ b/WebSocketServer/module.php @@ -1,10 +1,11 @@ ApplyChanges(); break; @@ -163,7 +167,7 @@ public function ApplyChanges() $this->SetTimerInterval('KeepAlivePing', 0); $OldParentID = $this->ParentID; - if ($this->HasActiveParent() and ( $OldParentID > 0)) { + if ($this->HasActiveParent() && ($OldParentID > 0)) { $this->DisconnectAllClients(); } @@ -179,7 +183,7 @@ public function ApplyChanges() if (!file_exists($basedir)) { mkdir($basedir); } - if (($this->ReadPropertyString('CertFile') == '') and ( $this->ReadPropertyString('KeyFile') == '')) { + if (($this->ReadPropertyString('CertFile') == '') && ($this->ReadPropertyString('KeyFile') == '')) { return $this->CreateNewCert($basedir); } @@ -236,12 +240,12 @@ public function ApplyChanges() if (!$Open) { $NewState = IS_INACTIVE; } else { - if (($Port < 1) or ( $Port > 65535)) { + if (($Port < 1) || ($Port > 65535)) { $NewState = IS_EBASE + 2; $Open = false; trigger_error($this->Translate('Port invalid'), E_USER_NOTICE); } else { - if (($this->PingInterval != 0) and ( $this->PingInterval < 5)) { + if (($this->PingInterval != 0) && ($this->PingInterval < 5)) { $this->PingInterval = 0; $NewState = IS_EBASE + 4; $Open = false; @@ -277,6 +281,177 @@ public function ApplyChanges() $this->NoNewClients = false; } + //################# DATAPOINTS CHILDS + /** + * Interne Funktion des SDK. Nimmt Daten von Childs entgegen und sendet Diese weiter. + * + * @param string $JSONString + * @result bool true wenn Daten gesendet werden konnten, sonst false. + */ + public function ForwardData($JSONString) + { + $Data = json_decode($JSONString); + if ($Data->DataID == '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}') { + $this->SendDebug('Forward Broadcast', utf8_decode($Data->Buffer), 0); + $Clients = $this->Multi_Clients; + foreach ($Clients as $Client) { + $Data->FrameTyp = $this->{'OpCode' . $Client->ClientIP . $Client->ClientPort}; + $Data->Fin = true; + $this->Send(utf8_decode($Data->Buffer), $Data->FrameTyp, $Client, $Data->Fin); + } + return true; + } + + $Client = $this->Multi_Clients->GetByIpPort(new Websocket_Client($Data->ClientIP, $Data->ClientPort)); + if ($Client === false) { + trigger_error($this->Translate('Unknow client') . ': ' . $Data->ClientIP . ':' . $Data->ClientPort, E_USER_NOTICE); + return false; + } + if ($Client->State != WebSocketState::Connected) { + trigger_error($this->Translate('Client not connected') . ': ' . $Data->ClientIP . ':' . $Data->ClientPort, E_USER_NOTICE); + return false; + } + $this->SendDebug('Forward', utf8_decode($Data->Buffer), 0); + + if ($Data->DataID == '{714B71FB-3D11-41D1-AFAC-E06F1E983E09}') { + if ($Data->FrameTyp == WebSocketOPCode::close) { + return $this->SendDisconnect($Client); + } + if ($Data->FrameTyp == WebSocketOPCode::ping) { + return $this->SendPing($Client->ClientIP, $Client->ClientPort, utf8_decode($Data->Buffer)); + } + if (($Data->FrameTyp < 0) || ($Data->FrameTyp > 2)) { + trigger_error($this->Translate('FrameTyp invalid') . ': ' . $Data->ClientIP . ':' . $Data->ClientPort, E_USER_NOTICE); + return false; + } + } else { //{C8792760-65CF-4C53-B5C7-A30FCC84FEFE} + if (property_exists($Data, 'Type')) { + if ($Data->Type == 2) { + return $this->SendDisconnect($Client); + } + } + //Type fehlt + $Data->FrameTyp = $this->{'OpCode' . $Client->ClientIP . $Client->ClientPort}; + $Data->Fin = true; + } + + $this->Send(utf8_decode($Data->Buffer), $Data->FrameTyp, $Client, $Data->Fin); + return true; + } + + //################# DATAPOINTS PARENT + /** + * Empfängt Daten vom Parent. + * + * @param string $JSONString Das empfangene JSON-kodierte Objekt vom Parent. + */ + public function ReceiveData($JSONString) + { + $data = json_decode($JSONString); + unset($data->DataID); + //$this->SendDebug('incoming', $data, 0); + $Payload = utf8_decode($data->Buffer); + $Clients = $this->Multi_Clients; + $IncomingClient = new Websocket_Client($data->ClientIP, $data->ClientPort, WebSocketState::init); + $Client = $Clients->GetByIpPort($IncomingClient); + $this->SendDebug(($Client ? 'OLD' : 'NEW') . ' CLIENT', SocketType::ToString($data->Type), 0); + switch ($data->Type) { + case 0: /* Data */ + if ($Client === false) { + $this->SendDebug('no Connection for Data found', $IncomingClient->ClientIP . ':' . $IncomingClient->ClientPort, 0); + $this->CloseConnection($IncomingClient); + } else { + $this->ProcessIncomingData($Client, $Payload); + $Clients->Update($Client); + } + break; + case 1: /* Connected */ + if (!$this->NoNewClients) { + $this->SendDebug('new Connection', $IncomingClient->ClientIP . ':' . $IncomingClient->ClientPort, 0); + $this->ClearClientBuffer($IncomingClient); + $Clients->Update($IncomingClient); + } + break; + case 2: /* Disconnected */ + if ($Client === false) { + $this->SendDebug('no Connection to disconnect found', $IncomingClient->ClientIP . ':' . $IncomingClient->ClientPort, 0); + } else { + $Clients->Remove($Client); + $this->ClearClientBuffer($Client); + } + break; + } + $this->Multi_Clients = $Clients; + $this->SetNextTimer(); + } + + //################# PUBLIC + /** + * Wird vom Timer aufgerufen. + * Sendet einen Ping an den Client welcher als nächstes das Timeout erreicht. + */ + public function KeepAlive() + { + $this->SendDebug('KeepAlive', 'start', 0); + $this->SetTimerInterval('KeepAlivePing', 0); + $Client = true; + + while ($Client) { + $Clients = $this->Multi_Clients; + $Client = $Clients->GetNextTimeout(1); + if ($Client === false) { + break; + } + if (@$this->SendPing($Client->ClientIP, $Client->ClientPort, '') === false) { + $this->SendDebug('TIMEOUT ' . $Client->ClientIP . ':' . $Client->ClientPort, 'Ping timeout', 0); + } + } + $this->SendDebug('KeepAlive', 'end', 0); + } + + /** + * Versendet einen Ping an einen Client. + * + * @param string $ClientIP Die IP-Adresse des Client. + * @param int $ClientPort Der Port des Client. + * @param string $Text Der Payload des Ping. + * + * @return bool True bei Erfolg, im Fehlerfall wird eine Warnung und false ausgegeben. + */ + public function SendPing(string $ClientIP, int $ClientPort, string $Text) + { + $Client = $this->Multi_Clients->GetByIpPort(new Websocket_Client($ClientIP, $ClientPort)); + if ($Client === false) { + $this->SendDebug('Unknow client', $ClientIP . ':' . $ClientPort, 0); + trigger_error($this->Translate('Unknow client') . ': ' . $ClientIP . ':' . $ClientPort, E_USER_NOTICE); + return false; + } + if ($Client->State != WebSocketState::Connected) { + $this->SendDebug('Client not connected', $ClientIP . ':' . $ClientPort, 0); + trigger_error($this->Translate('Client not connected') . ': ' . $ClientIP . ':' . $ClientPort, E_USER_NOTICE); + return false; + } + $this->SendDebug('Send Ping' . $Client->ClientIP . ':' . $Client->ClientPort, $Text, 0); + $this->Send($Text, WebSocketOPCode::ping, $Client); + $Result = $this->WaitForPong($Client); + $this->{'Pong' . $Client->ClientIP . $Client->ClientPort} = ''; + if ($Result === false) { + $this->SendDebug('Timeout ' . $Client->ClientIP . ':' . $Client->ClientPort, '', 0); + trigger_error($this->Translate('Timeout'), E_USER_NOTICE); + $this->RemoveOneClient($Client); + $this->CloseConnection($Client); + return false; + } + if ($Result !== $Text) { + $this->SendDebug('Error in Pong ' . $Client->ClientIP . ':' . $Client->ClientPort, $Result, 0); + trigger_error($this->Translate('Wrong pong received'), E_USER_NOTICE); + $this->RemoveOneClient($Client); + $this->SendDisconnect($Client); + return false; + } + return true; + } + //################# PRIVATE /** * Erzeugt ein selbst-signiertes Zertifikat. @@ -521,7 +696,7 @@ private function SendHandshake(int $Code, string $Data, Websocket_Client $Client private function MakeJSON(Websocket_Client $Client, string $Data, $UseTLS = true, int $Type = SocketType::Data) { if ($Type == SocketType::Data) { - if ($UseTLS and $Client->UseTLS) { + if ($UseTLS && $Client->UseTLS) { $this->SendDebug('Send TLS', $Data, 0); try { @@ -769,64 +944,6 @@ private function Send(string $RawData, int $OPCode, Websocket_Client $Client, $F } } - //################# DATAPOINTS CHILDS - /** - * Interne Funktion des SDK. Nimmt Daten von Childs entgegen und sendet Diese weiter. - * - * @param string $JSONString - * @result bool true wenn Daten gesendet werden konnten, sonst false. - */ - public function ForwardData($JSONString) - { - $Data = json_decode($JSONString); - if ($Data->DataID == '{79827379-F36E-4ADA-8A95-5F8D1DC92FA9}') { - $this->SendDebug('Forward Broadcast', utf8_decode($Data->Buffer), 0); - $Clients = $this->Multi_Clients; - foreach ($Clients as $Client) { - $Data->FrameTyp = $this->{'OpCode' . $Client->ClientIP . $Client->ClientPort}; - $Data->Fin = true; - $this->Send(utf8_decode($Data->Buffer), $Data->FrameTyp, $Client, $Data->Fin); - } - return true; - } - - $Client = $this->Multi_Clients->GetByIpPort(new Websocket_Client($Data->ClientIP, $Data->ClientPort)); - if ($Client === false) { - trigger_error($this->Translate('Unknow client') . ': ' . $Data->ClientIP . ':' . $Data->ClientPort, E_USER_NOTICE); - return false; - } - if ($Client->State != WebSocketState::Connected) { - trigger_error($this->Translate('Client not connected') . ': ' . $Data->ClientIP . ':' . $Data->ClientPort, E_USER_NOTICE); - return false; - } - $this->SendDebug('Forward', utf8_decode($Data->Buffer), 0); - - if ($Data->DataID == '{714B71FB-3D11-41D1-AFAC-E06F1E983E09}') { - if ($Data->FrameTyp == WebSocketOPCode::close) { - return $this->SendDisconnect($Client); - } - if ($Data->FrameTyp == WebSocketOPCode::ping) { - return $this->SendPing($Client->ClientIP, $Client->ClientPort, utf8_decode($Data->Buffer)); - } - if (($Data->FrameTyp < 0) || ($Data->FrameTyp > 2)) { - trigger_error($this->Translate('FrameTyp invalid') . ': ' . $Data->ClientIP . ':' . $Data->ClientPort, E_USER_NOTICE); - return false; - } - } else { //{C8792760-65CF-4C53-B5C7-A30FCC84FEFE} - if (property_exists($Data, 'Type')) { - if ($Data->Type == 2) { - return $this->SendDisconnect($Client); - } - } - //Type fehlt - $Data->FrameTyp = $this->{'OpCode' . $Client->ClientIP . $Client->ClientPort}; - $Data->Fin = true; - } - - $this->Send(utf8_decode($Data->Buffer), $Data->FrameTyp, $Client, $Data->Fin); - return true; - } - /** * Sendet die Rohdaten an die Childs. * @@ -880,24 +997,24 @@ private function ProcessIncomingData(Websocket_Client &$Client, string $Payload) { $this->SendDebug('Receive ' . $Client->ClientIP . ':' . $Client->ClientPort, $Payload, 0); if ($Client->State == WebSocketState::init) { //new - if ($this->UseTLS and ( (ord($Payload[0]) >= 0x14) && (ord($Payload[0]) <= 0x18) && (ord($Payload[1]) == 0x03))) { //valid header wenn TLS is active + if ($this->UseTLS && ((ord($Payload[0]) >= 0x14) && (ord($Payload[0]) <= 0x18) && (ord($Payload[1]) == 0x03))) { //valid header wenn TLS is active $Client->State = WebSocketState::TLSisReceived; $Client->UseTLS = true; $this->{'BufferTLS' . $Client->ClientIP . $Client->ClientPort} = ''; $this->{'Buffer' . $Client->ClientIP . $Client->ClientPort} = ''; // TLS Config $TLSconfig = \PTLS\TLSContext::getServerConfig([ - 'key_pair_files' => [ - 'cert' => [$this->CertData], - 'key' => [$this->KeyData, $this->KeyPassword] - ] + 'key_pair_files' => [ + 'cert' => [$this->CertData], + 'key' => [$this->KeyData, $this->KeyPassword] + ] ]); $this->SendDebug('NEW TLSContex', '', 0); //$this->lock($Client->ClientIP . $Client->ClientPort); $this->lock($Client->ClientIP . $Client->ClientPort); $TLS = \PTLS\TLSContext::createTLS($TLSconfig); } - if ($this->UsePlain and ( preg_match("/^GET ?([^?#]*) HTTP\/1.1\r\n/", $Payload, $match))) { //valid header wenn Plain is active + if ($this->UsePlain && (preg_match("/^GET ?([^?#]*) HTTP\/1.1\r\n/", $Payload, $match))) { //valid header wenn Plain is active $Client->State = WebSocketState::HandshakeReceived; $Client->UseTLS = false; $this->{'Buffer' . $Client->ClientIP . $Client->ClientPort} = ''; @@ -1034,120 +1151,6 @@ private function CloseConnection(Websocket_Client $Client) $SendSocketClose = $this->MakeJSON($Client, '', false, SocketType::Disconnected); $this->SendDataToParent($SendSocketClose); } - - //################# DATAPOINTS PARENT - /** - * Empfängt Daten vom Parent. - * - * @param string $JSONString Das empfangene JSON-kodierte Objekt vom Parent. - */ - public function ReceiveData($JSONString) - { - $data = json_decode($JSONString); - unset($data->DataID); - //$this->SendDebug('incoming', $data, 0); - $Payload = utf8_decode($data->Buffer); - $Clients = $this->Multi_Clients; - $IncomingClient = new Websocket_Client($data->ClientIP, $data->ClientPort, WebSocketState::init); - $Client = $Clients->GetByIpPort($IncomingClient); - $this->SendDebug(($Client ? 'OLD' : 'NEW') . ' CLIENT', SocketType::ToString($data->Type), 0); - switch ($data->Type) { - case 0: /* Data */ - if ($Client === false) { - $this->SendDebug('no Connection for Data found', $IncomingClient->ClientIP . ':' . $IncomingClient->ClientPort, 0); - $this->CloseConnection($IncomingClient); - } else { - $this->ProcessIncomingData($Client, $Payload); - $Clients->Update($Client); - } - break; - case 1: /* Connected */ - if (!$this->NoNewClients) { - $this->SendDebug('new Connection', $IncomingClient->ClientIP . ':' . $IncomingClient->ClientPort, 0); - $this->ClearClientBuffer($IncomingClient); - $Clients->Update($IncomingClient); - } - break; - case 2: /* Disconnected */ - if ($Client === false) { - $this->SendDebug('no Connection to disconnect found', $IncomingClient->ClientIP . ':' . $IncomingClient->ClientPort, 0); - } else { - $Clients->Remove($Client); - $this->ClearClientBuffer($Client); - } - break; - } - $this->Multi_Clients = $Clients; - $this->SetNextTimer(); - } - - //################# PUBLIC - /** - * Wird vom Timer aufgerufen. - * Sendet einen Ping an den Client welcher als nächstes das Timeout erreicht. - */ - public function KeepAlive() - { - $this->SendDebug('KeepAlive', 'start', 0); - $this->SetTimerInterval('KeepAlivePing', 0); - $Client = true; - - while ($Client) { - $Clients = $this->Multi_Clients; - $Client = $Clients->GetNextTimeout(1); - if ($Client === false) { - break; - } - if (@$this->SendPing($Client->ClientIP, $Client->ClientPort, '') === false) { - $this->SendDebug('TIMEOUT ' . $Client->ClientIP . ':' . $Client->ClientPort, 'Ping timeout', 0); - } - } - $this->SendDebug('KeepAlive', 'end', 0); - } - - /** - * Versendet einen Ping an einen Client. - * - * @param string $ClientIP Die IP-Adresse des Client. - * @param int $ClientPort Der Port des Client. - * @param string $Text Der Payload des Ping. - * - * @return bool True bei Erfolg, im Fehlerfall wird eine Warnung und false ausgegeben. - */ - public function SendPing(string $ClientIP, int $ClientPort, string $Text) - { - $Client = $this->Multi_Clients->GetByIpPort(new Websocket_Client($ClientIP, $ClientPort)); - if ($Client === false) { - $this->SendDebug('Unknow client', $ClientIP . ':' . $ClientPort, 0); - trigger_error($this->Translate('Unknow client') . ': ' . $ClientIP . ':' . $ClientPort, E_USER_NOTICE); - return false; - } - if ($Client->State != WebSocketState::Connected) { - $this->SendDebug('Client not connected', $ClientIP . ':' . $ClientPort, 0); - trigger_error($this->Translate('Client not connected') . ': ' . $ClientIP . ':' . $ClientPort, E_USER_NOTICE); - return false; - } - $this->SendDebug('Send Ping' . $Client->ClientIP . ':' . $Client->ClientPort, $Text, 0); - $this->Send($Text, WebSocketOPCode::ping, $Client); - $Result = $this->WaitForPong($Client); - $this->{'Pong' . $Client->ClientIP . $Client->ClientPort} = ''; - if ($Result === false) { - $this->SendDebug('Timeout ' . $Client->ClientIP . ':' . $Client->ClientPort, '', 0); - trigger_error($this->Translate('Timeout'), E_USER_NOTICE); - $this->RemoveOneClient($Client); - $this->CloseConnection($Client); - return false; - } - if ($Result !== $Text) { - $this->SendDebug('Error in Pong ' . $Client->ClientIP . ':' . $Client->ClientPort, $Result, 0); - trigger_error($this->Translate('Wrong pong received'), E_USER_NOTICE); - $this->RemoveOneClient($Client); - $this->SendDisconnect($Client); - return false; - } - return true; - } - } /* @} */ diff --git a/WebSocketServerIfTest/README.md b/WebSocketServerIfTest/README.md index 23003e2..18551b6 100644 --- a/WebSocketServerIfTest/README.md +++ b/WebSocketServerIfTest/README.md @@ -1,4 +1,4 @@ -# WebSocketInterfaceTest (IPSNetwork) +# WebSocketInterfaceTest (Network) Beispiel-Modul für den Datenaustausch mit den WebSocket Modulen. diff --git a/WebSocketServerIfTest/form.json b/WebSocketServerIfTest/form.json index 6e1a00f..92079a0 100644 --- a/WebSocketServerIfTest/form.json +++ b/WebSocketServerIfTest/form.json @@ -1,5 +1,3 @@ { - "elements": - [ - ] + "elements": [] } \ No newline at end of file diff --git a/WebSocketServerIfTest/module.json b/WebSocketServerIfTest/module.json index 3ca1d3d..9119dfe 100644 --- a/WebSocketServerIfTest/module.json +++ b/WebSocketServerIfTest/module.json @@ -3,9 +3,18 @@ "name": "WebSocketInterfaceTest", "type": 2, "vendor": "", - "aliases": ["WebSocket-InterfaceTest"], - "parentRequirements": ["{BC49DE11-24CA-484D-85AE-9B6F24D89321}","{714B71FB-3D11-41D1-AFAC-E06F1E983E09}"], + "aliases": [ + "WebSocket-InterfaceTest" + ], + "parentRequirements": [ + "{BC49DE11-24CA-484D-85AE-9B6F24D89321}", + "{714B71FB-3D11-41D1-AFAC-E06F1E983E09}" + ], "childRequirements": [], - "implemented": ["{C51A4B94-8195-4673-B78D-04D91D52D2DD}","{8F1F6C32-B1AD-4B7F-8DFB-1244A96FCACF}"], - "prefix": "WSTest" + "implemented": [ + "{C51A4B94-8195-4673-B78D-04D91D52D2DD}", + "{8F1F6C32-B1AD-4B7F-8DFB-1244A96FCACF}" + ], + "prefix": "WSTEST", + "url": "https://github.com/Nall-chan/Network" } \ No newline at end of file diff --git a/WebSocketServerIfTest/module.php b/WebSocketServerIfTest/module.php index 1041aac..806adac 100644 --- a/WebSocketServerIfTest/module.php +++ b/WebSocketServerIfTest/module.php @@ -1,5 +1,7 @@ Ohne */ - -if (!defined('IPS_BASE')) { - // --- BASE MESSAGE - define('IPS_BASE', 10000); //Base Message - define('IPS_KERNELSTARTED', IPS_BASE + 1); //Post Ready Message - define('IPS_KERNELSHUTDOWN', IPS_BASE + 2); //Pre Shutdown Message, Runlevel UNINIT Follows -} -if (!defined('IPS_KERNELMESSAGE')) { - // --- KERNEL - define('IPS_KERNELMESSAGE', IPS_BASE + 100); //Kernel Message - define('KR_CREATE', IPS_KERNELMESSAGE + 1); //Kernel is beeing created - define('KR_INIT', IPS_KERNELMESSAGE + 2); //Kernel Components are beeing initialised, Modules loaded, Settings read - define('KR_READY', IPS_KERNELMESSAGE + 3); //Kernel is ready and running - define('KR_UNINIT', IPS_KERNELMESSAGE + 4); //Got Shutdown Message, unloading all stuff - define('KR_SHUTDOWN', IPS_KERNELMESSAGE + 5); //Uninit Complete, Destroying Kernel Inteface -} -if (!defined('IPS_LOGMESSAGE')) { - // --- KERNEL LOGMESSAGE - define('IPS_LOGMESSAGE', IPS_BASE + 200); //Logmessage Message - define('KL_MESSAGE', IPS_LOGMESSAGE + 1); //Normal Message | FG: Black | BG: White | STLYE : NONE - define('KL_SUCCESS', IPS_LOGMESSAGE + 2); //Success Message | FG: Black | BG: Green | STYLE : NONE - define('KL_NOTIFY', IPS_LOGMESSAGE + 3); //Notiy about Changes | FG: Black | BG: Blue | STLYE : NONE - define('KL_WARNING', IPS_LOGMESSAGE + 4); //Warnings | FG: Black | BG: Yellow | STLYE : NONE - define('KL_ERROR', IPS_LOGMESSAGE + 5); //Error Message | FG: Black | BG: Red | STLYE : BOLD - define('KL_DEBUG', IPS_LOGMESSAGE + 6); //Debug Informations + Script Results | FG: Grey | BG: White | STLYE : NONE - define('KL_CUSTOM', IPS_LOGMESSAGE + 7); //User Message | FG: Black | BG: White | STLYE : NONE -} -if (!defined('IPS_MODULEMESSAGE')) { - // --- MODULE LOADER - define('IPS_MODULEMESSAGE', IPS_BASE + 300); //ModuleLoader Message - define('ML_LOAD', IPS_MODULEMESSAGE + 1); //Module loaded - define('ML_UNLOAD', IPS_MODULEMESSAGE + 2); //Module unloaded -} -if (!defined('IPS_OBJECTMESSAGE')) { - // --- OBJECT MANAGER - define('IPS_OBJECTMESSAGE', IPS_BASE + 400); - define('OM_REGISTER', IPS_OBJECTMESSAGE + 1); //Object was registered - define('OM_UNREGISTER', IPS_OBJECTMESSAGE + 2); //Object was unregistered - define('OM_CHANGEPARENT', IPS_OBJECTMESSAGE + 3); //Parent was Changed - define('OM_CHANGENAME', IPS_OBJECTMESSAGE + 4); //Name was Changed - define('OM_CHANGEINFO', IPS_OBJECTMESSAGE + 5); //Info was Changed - define('OM_CHANGETYPE', IPS_OBJECTMESSAGE + 6); //Type was Changed - define('OM_CHANGESUMMARY', IPS_OBJECTMESSAGE + 7); //Summary was Changed - define('OM_CHANGEPOSITION', IPS_OBJECTMESSAGE + 8); //Position was Changed - define('OM_CHANGEREADONLY', IPS_OBJECTMESSAGE + 9); //ReadOnly was Changed - define('OM_CHANGEHIDDEN', IPS_OBJECTMESSAGE + 10); //Hidden was Changed - define('OM_CHANGEICON', IPS_OBJECTMESSAGE + 11); //Icon was Changed - define('OM_CHILDADDED', IPS_OBJECTMESSAGE + 12); //Child for Object was added - define('OM_CHILDREMOVED', IPS_OBJECTMESSAGE + 13); //Child for Object was removed - define('OM_CHANGEIDENT', IPS_OBJECTMESSAGE + 14); //Ident was Changed -} -if (!defined('IPS_INSTANCEMESSAGE')) { - // --- INSTANCE MANAGER - define('IPS_INSTANCEMESSAGE', IPS_BASE + 500); //Instance Manager Message - define('IM_CREATE', IPS_INSTANCEMESSAGE + 1); //Instance created - define('IM_DELETE', IPS_INSTANCEMESSAGE + 2); //Instance deleted - define('IM_CONNECT', IPS_INSTANCEMESSAGE + 3); //Instance connectged - define('IM_DISCONNECT', IPS_INSTANCEMESSAGE + 4); //Instance disconncted - define('IM_CHANGESTATUS', IPS_INSTANCEMESSAGE + 5); //Status was Changed - define('IM_CHANGESETTINGS', IPS_INSTANCEMESSAGE + 6); //Settings were Changed - define('IM_CHANGESEARCH', IPS_INSTANCEMESSAGE + 7); //Searching was started/stopped - define('IM_SEARCHUPDATE', IPS_INSTANCEMESSAGE + 8); //Searching found new results - define('IM_SEARCHPROGRESS', IPS_INSTANCEMESSAGE + 9); //Searching progress in % - define('IM_SEARCHCOMPLETE', IPS_INSTANCEMESSAGE + 10); //Searching is complete -} -if (!defined('IPS_VARIABLEMESSAGE')) { - // --- VARIABLE MANAGER - define('IPS_VARIABLEMESSAGE', IPS_BASE + 600); //Variable Manager Message - define('VM_CREATE', IPS_VARIABLEMESSAGE + 1); //Variable Created - define('VM_DELETE', IPS_VARIABLEMESSAGE + 2); //Variable Deleted - define('VM_UPDATE', IPS_VARIABLEMESSAGE + 3); //On Variable Update - define('VM_CHANGEPROFILENAME', IPS_VARIABLEMESSAGE + 4); //On Profile Name Change - define('VM_CHANGEPROFILEACTION', IPS_VARIABLEMESSAGE + 5); //On Profile Action Change -} -if (!defined('IPS_SCRIPTMESSAGE')) { - // --- SCRIPT MANAGER - define('IPS_SCRIPTMESSAGE', IPS_BASE + 700); //Script Manager Message - define('SM_CREATE', IPS_SCRIPTMESSAGE + 1); //On Script Create - define('SM_DELETE', IPS_SCRIPTMESSAGE + 2); //On Script Delete - define('SM_CHANGEFILE', IPS_SCRIPTMESSAGE + 3); //On Script File changed - define('SM_BROKEN', IPS_SCRIPTMESSAGE + 4); //Script Broken Status changed -} -if (!defined('IPS_EVENTMESSAGE')) { - // --- EVENT MANAGER - define('IPS_EVENTMESSAGE', IPS_BASE + 800); //Event Scripter Message - define('EM_CREATE', IPS_EVENTMESSAGE + 1); //On Event Create - define('EM_DELETE', IPS_EVENTMESSAGE + 2); //On Event Delete - define('EM_UPDATE', IPS_EVENTMESSAGE + 3); - define('EM_CHANGEACTIVE', IPS_EVENTMESSAGE + 4); - define('EM_CHANGELIMIT', IPS_EVENTMESSAGE + 5); - define('EM_CHANGESCRIPT', IPS_EVENTMESSAGE + 6); - define('EM_CHANGETRIGGER', IPS_EVENTMESSAGE + 7); - define('EM_CHANGETRIGGERVALUE', IPS_EVENTMESSAGE + 8); - define('EM_CHANGETRIGGEREXECUTION', IPS_EVENTMESSAGE + 9); - define('EM_CHANGECYCLIC', IPS_EVENTMESSAGE + 10); - define('EM_CHANGECYCLICDATEFROM', IPS_EVENTMESSAGE + 11); - define('EM_CHANGECYCLICDATETO', IPS_EVENTMESSAGE + 12); - define('EM_CHANGECYCLICTIMEFROM', IPS_EVENTMESSAGE + 13); - define('EM_CHANGECYCLICTIMETO', IPS_EVENTMESSAGE + 14); -} -if (!defined('IPS_MEDIAMESSAGE')) { - // --- MEDIA MANAGER - define('IPS_MEDIAMESSAGE', IPS_BASE + 900); //Media Manager Message - define('MM_CREATE', IPS_MEDIAMESSAGE + 1); //On Media Create - define('MM_DELETE', IPS_MEDIAMESSAGE + 2); //On Media Delete - define('MM_CHANGEFILE', IPS_MEDIAMESSAGE + 3); //On Media File changed - define('MM_AVAILABLE', IPS_MEDIAMESSAGE + 4); //Media Available Status changed - define('MM_UPDATE', IPS_MEDIAMESSAGE + 5); -} -if (!defined('IPS_LINKMESSAGE')) { - // --- LINK MANAGER - define('IPS_LINKMESSAGE', IPS_BASE + 1000); //Link Manager Message - define('LM_CREATE', IPS_LINKMESSAGE + 1); //On Link Create - define('LM_DELETE', IPS_LINKMESSAGE + 2); //On Link Delete - define('LM_CHANGETARGET', IPS_LINKMESSAGE + 3); //On Link TargetID change -} -if (!defined('IPS_FLOWMESSAGE')) { - // --- DATA HANDLER - define('IPS_FLOWMESSAGE', IPS_BASE + 1100); //Data Handler Message - define('FM_CONNECT', IPS_FLOWMESSAGE + 1); //On Instance Connect - define('FM_DISCONNECT', IPS_FLOWMESSAGE + 2); //On Instance Disconnect -} -if (!defined('IPS_ENGINEMESSAGE')) { - // --- SCRIPT ENGINE - define('IPS_ENGINEMESSAGE', IPS_BASE + 1200); //Script Engine Message - define('SE_UPDATE', IPS_ENGINEMESSAGE + 1); //On Library Refresh - define('SE_EXECUTE', IPS_ENGINEMESSAGE + 2); //On Script Finished execution - define('SE_RUNNING', IPS_ENGINEMESSAGE + 3); //On Script Started execution -} -if (!defined('IPS_PROFILEMESSAGE')) { - // --- PROFILE POOL - define('IPS_PROFILEMESSAGE', IPS_BASE + 1300); - define('PM_CREATE', IPS_PROFILEMESSAGE + 1); - define('PM_DELETE', IPS_PROFILEMESSAGE + 2); - define('PM_CHANGETEXT', IPS_PROFILEMESSAGE + 3); - define('PM_CHANGEVALUES', IPS_PROFILEMESSAGE + 4); - define('PM_CHANGEDIGITS', IPS_PROFILEMESSAGE + 5); - define('PM_CHANGEICON', IPS_PROFILEMESSAGE + 6); - define('PM_ASSOCIATIONADDED', IPS_PROFILEMESSAGE + 7); - define('PM_ASSOCIATIONREMOVED', IPS_PROFILEMESSAGE + 8); - define('PM_ASSOCIATIONCHANGED', IPS_PROFILEMESSAGE + 9); -} -if (!defined('IPS_TIMERMESSAGE')) { - // --- TIMER POOL - define('IPS_TIMERMESSAGE', IPS_BASE + 1400); //Timer Pool Message - define('TM_REGISTER', IPS_TIMERMESSAGE + 1); - define('TM_UNREGISTER', IPS_TIMERMESSAGE + 2); - define('TM_SETINTERVAL', IPS_TIMERMESSAGE + 3); - define('TM_UPDATE', IPS_TIMERMESSAGE + 4); - define('TM_RUNNING', IPS_TIMERMESSAGE + 5); -} - -if (!defined('IS_ACTIVE')) { //Nur wenn Konstanten noch nicht bekannt sind. - // --- STATUS CODES - define('IS_SBASE', 100); - define('IS_CREATING', IS_SBASE + 1); //module is being created - define('IS_ACTIVE', IS_SBASE + 2); //module created and running - define('IS_DELETING', IS_SBASE + 3); //module us being deleted - define('IS_INACTIVE', IS_SBASE + 4); //module is not beeing used -// --- ERROR CODES - define('IS_EBASE', 200); //default errorcode - define('IS_NOTCREATED', IS_EBASE + 1); //instance could not be created -} - -if (!defined('vtBoolean')) { //Nur wenn Konstanten noch nicht bekannt sind. - define('vtBoolean', 0); - define('vtInteger', 1); - define('vtFloat', 2); - define('vtString', 3); -} /** * DebugHelper ergänzt SendDebug um die Möglichkeit Array und Objekte auszugeben. */ @@ -301,7 +133,7 @@ protected function HasActiveParent() } /** - * Biete Funktionen um Thread-Safe auf Objekte zuzugrifen. + * Biete Funktionen um auf Objekte Thread-Safe zuzugreifen. */ trait Semaphore { diff --git a/libs/WebhookHelper.php b/libs/WebhookHelper.php index 46d78ff..3a3fbc1 100644 --- a/libs/WebhookHelper.php +++ b/libs/WebhookHelper.php @@ -1,5 +1,7 @@ = $start + $len) { $this->Payload = substr($Frame, $start, $len); - if ($this->Mask and ($len > 0)) { + if ($this->Mask && ($len > 0)) { for ($i = 0; $i < strlen($this->Payload); $i++) { $this->Payload[$i] = $this->Payload[$i] ^ $this->MaskKey[$i % 4]; } @@ -263,7 +265,7 @@ public function ToFrame($Masked = false) $len = 126; } $this->Mask = $Masked; - if ($this->Mask and ($len > 0)) { + if ($this->Mask && ($len > 0)) { $this->PayloadRAW = $this->Payload; $len = $len | WebSocketMask::mask; $this->MaskKey = openssl_random_pseudo_bytes(4); @@ -319,14 +321,6 @@ class Websocket_Client */ public $UseTLS; - /** - * Liefert die Daten welche behalten werden müssen. - */ - public function __sleep() - { - return ['ClientIP', 'ClientPort', 'State', 'Timestamp', 'UseTLS']; - } - /** * Erzeugt ein Websocket_Client-Objekt aus den übergebenden Daten. * @@ -343,6 +337,14 @@ public function __construct(string $ClientIP, int $ClientPort, $State = WebSocke $this->Timestamp = 0; $this->UseTLS = $UseTLS; } + + /** + * Liefert die Daten welche behalten werden müssen. + */ + public function __sleep() + { + return ['ClientIP', 'ClientPort', 'State', 'Timestamp', 'UseTLS']; + } } /** diff --git a/libs/loadTLS.php b/libs/loadTLS.php index 77fc82d..961be93 100644 --- a/libs/loadTLS.php +++ b/libs/loadTLS.php @@ -1,5 +1,7 @@ validateLibrary(__DIR__ . '/..'); + } + public function testValidateClientSplitter(): void + { + $this->validateModule(__DIR__ . '/../ClientSplitter'); + } + public function testValidateDHCPSniffer(): void + { + $this->validateModule(__DIR__ . '/../DHCPSniffer'); + } + public function testValidateHookReverseProxy(): void + { + $this->validateModule(__DIR__ . '/../HookReverseProxy'); + } + public function testValidateWebSocketClient(): void + { + $this->validateModule(__DIR__ . '/../WebSocketClient'); + } + public function testValidateWebSocketServer(): void + { + $this->validateModule(__DIR__ . '/../WebSocketServer'); + } + public function testValidateWebSocketServerIfTest(): void + { + $this->validateModule(__DIR__ . '/../WebSocketServerIfTest'); + } +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml new file mode 100644 index 0000000..b1dadd3 --- /dev/null +++ b/tests/phpunit.xml @@ -0,0 +1,11 @@ + + + + + ../Network + + + + + + diff --git a/tests/stubs b/tests/stubs new file mode 160000 index 0000000..9ee089a --- /dev/null +++ b/tests/stubs @@ -0,0 +1 @@ +Subproject commit 9ee089af18a9f19b8046b2f0672939121f4f3d42