#! /bin/sh
# vim: set tabstop=4 syntax=sh :
# SPDX-License-Identifier: GPL-2.0-or-later
#######################################################################################################
# #
# check firmware versions from AVM's JUIS #
# #
###################################################################################################VER#
# #
# juis_check, version 0.5 #
# #
# This script is a part of the YourFritz project from https://github.com/PeterPawn/YourFritz. #
# #
###################################################################################################CPY#
# #
# Copyright (C) 2010-2021 P.Haemmerlein (peterpawn@yourfritz.de) #
# #
###################################################################################################LIC#
# #
# This project is free software, you can redistribute it and/or modify it under the terms of the GNU #
# General Public License as published by the Free Software Foundation; either version 2 of the #
# License, or (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without #
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
# General Public License under http://www.gnu.org/licenses/gpl-2.0.html for more details. #
# #
#######################################################################################################
# #
# This is another rewritten version ... the original script gots more attention than ever expected #
# and is used in different environments. To make it more flexible, it's now able to run under a bunch #
# of shell hosts, from 'bash' over BusyBox' 'ash' to the very limited 'dash' from Debian based #
# distributions. #
# #
# As long as a 'bash' host is used, the network communication is done via the integrated TCP/UDP #
# support. In all other cases, a 'netcat' version is needed (reachable via the command name 'nc') to #
# handle network I/O. #
# #
# Each other command used within this script, should be available on each *nix-based system, from a #
# native Linux installation (Desktop, Embedded, One-Chip like RasPi) over a BSD-like system (even on #
# MacOS X) up to Canonical's 'bash' on Win10 installations. #
# #
# If the script was launched from a 'non-bash' shell (due to its SheBang of '/bin/sh') and a Bash #
# shell seems to be available (looked-up with 'command'), the script tries to respawn itself with a #
# 'bash' instance, due to the expected network support (using /dev/tcp) from this shell. Respawning #
# may be blocked by an option. #
# #
#######################################################################################################
usage_text()
{
if [ $__language__ = de ]; then
__purpose_hdr
__nl "Mit diesem Shell-Skript kann man über den AVM-Update-Service (JUIS) nach neuer Firmware"
__nl "suchen lassen.\n"
__nl "Dank der Möglichkeit, fast jeden Aspekt bzw. Parameter einer solchen Abfrage nach eigenem"
__nl "Bedarf anzupassen, kann man damit für praktisch jedes beliebige FRITZ!Box-Modell nach"
__nl "neuer Firmware suchen lassen.\n"
__nl "Trotzdem kann sich der Aufruf des Skripts im einfachsten Fall auf die Angabe des DNS-Namens"
__nl "oder der IP-Adresse einer existierenden (und von der LAN-Seite erreichbaren) FRITZ!Box im"
__nl "ersten Parameter beschränken und alle weiteren Angaben für die Abfrage beim Hersteller"
__nl "werden dann von dieser FRITZ!Box gelesen.\n"
__nl "Damit ist es nach wie vor ganz einfach, regelmäßig bei AVM (dem Hersteller der FRITZ!Box)"
__nl "nach neuer Firmware zu suchen - auch von 'außerhalb' der FRITZ!Box und für den Fall, dass"
__nl "man aus (nachvollziehbaren) Sicherheitsbedenken lieber auf die automatische Suche und die"
__nl "automatische Installation neuer Firmware verzichten möchte."
__usage_hdr
__usage_opt "Optionen"; __usage_opt_end; __usage_opt "optionale Parameter"
__usage_end
__options_hdr
__option_debug 30
__option_help 30
__option_version 30
__nl; __option_show_opt 30 "-n" "--no-respawn"; __option_show_desc "nicht neu starten mit der 'bash' als Shell"
__nl; __option_show_opt 30 "-s" "--save-response" "filename"; __option_show_desc "die Antwort vom AVM-Server wird in $(__undl "filename") gespeichert"
__nl; __option_show_opt 30 "-a" "--show-response"; __option_show_desc "die Antwort vom AVM-Server wird nach STDERR ausgegeben"
__nl; __option_show_opt 30 "-i" "--ignore-cfgfile"; __option_show_desc "keine Konfigurationsdatei verwenden"
__nl; __option_show_opt 30 "-c" "--current"; __option_show_desc "aktuelle Version ermitteln ('Patch' - s.u. - wird dekrementiert)"
__nl; __option_show_opt 30 "-l" "--local"; __option_show_desc "die Daten der Box verwenden, auf der das Skript läuft"
__nl; __option_show_opt 30 "-p" "--print-version"; __option_show_desc "die Versionsnummer der gefundenen Firmware zusätzlich ausgeben auf STDOUT"
__nl; __option_show_opt 30 "-r" "--use-real-serial"; __option_show_desc "die echte Seriennummer (maca) der Box senden"
__options_end
__nl "Das Skript versucht, eine Konfigurationsdatei zur Anpassung an die lokalen Gegebenheiten"
__nl "zu verwenden, dazu wird nach einer Datei mit dem (Aufruf-)Namen des Skripts und der Erweiterung"
__nl "'cfg' in dem Verzeichnis gesucht, in welchem das Skript selbst enthalten ist. Diese Datei wird"
__nl "dann 'eingefügt' und kann beliebigen Shell-Code enthalten - man sollte also sehr genau die"
__nl "hiermit zum Ausdruck gebrachte Warnung beachten, dass es sich zu einem schweren Sicherheits-"
__nl "problem auswachsen kann, wenn irgendjemand diese Datei ohne Kenntnis des Benutzers ändern"
__nl "kann.\n"
__nl "Will man eine Konfigurationsdatei mit einem anderen Namen verwenden, kann deren Name über die"
__nl "Umgebungsvariable 'JUIS_CHECK_CFG' festgelegt werden. Soll gar keine Konfigurationsdatei"
__nl "benutzt werden, kann man die Option '--ignore-cfgfile' (oder '-i') angeben - dann muß man"
__nl "aber auf irgendeinem anderen Weg dafür sorgen, daß die Variable 'Box' einen passenden Wert"
__nl "hat, wenn nicht alle notwendigen Parameter explizit gesetzt wurden und ein Auslesen weiterer"
__nl "Werte aus einer FRITZ!Box erforderlich ist.\n"
__nl "Wird keine Konfigurationsdatei (mit dem automatisch gebildeten Namen) gefunden und wurde ihre"
__nl "Verarbeitung nicht über die o.a. Option unterdrückt, wird folgener Inhalt angenommen:\n"
__nl "Box=\$1"
__nl "shift\n"
__nl "- damit wird also als erster und einziger Parameter die Angabe des DNS-Namens oder der IP-"
__nl "Adresse der FRITZ!Box erwartet, von der weitere Einstellungen zu lesen wären. Gleichzeitig"
__nl "dürfen aber auf der Kommandozeile noch weitere Angaben folgen, die dann ihrerseits einen"
__nl "der weiter unten beschriebenen Parameter festlegen (solange bei der Beschreibung der Variablen"
__nl "nichts anderes steht).\n"
__nl "Bei Angabe der Option '--show-response', wird die SOAP-Response aus der AVM-Antwort extrahiert"
__nl "(also die HTTP-Header aus der Antwort entfernt) und das Ergebnis nach STDERR ausgegeben. Um"
__nl "diese Anzeige übersichtlicher zu gestalten, wird dabei ein Programm zur Formatierung der (im"
__nl "Original einzeiligen) Ausgabe verwendet, sofern ein solches gefunden wird. Standardmäßig wird"
__nl "dabei nach 'xmllint' aus dem 'libxml2'-Projekt (http://xmlsoft.org/index.html) gesucht, dieser"
__nl "Name kann mit der Environment-Variablen 'XML_LINTER' überschrieben werden. Wird kein passendes"
__nl "Programm gefunden, erfolgt die Ausgabe der Daten 1:1 so, wie sie von AVM gesendet wurden.\n"
__nl "-----------------------------------------------------------------------------------------------\n"
__nl "Egal, auf welchem Weg man jetzt die notwendigen Parameter bereitstellt (ob bereits beim Aufruf"
__nl "der Skript-Datei über das Shell-Environment oder über entsprechende Paare aus Namen und Werten"
__nl "(verbunden durch ein Gleichheitszeichen) auf der Kommandozeile oder über die Anweisungen in"
__nl "einer Konfigurationsdatei), am Ende braucht es für die Abfrage die folgenden Werte:\n"
__nl "$(__bold "Name Bedeutung/Inhalt\n")"
__nl "Version die Version der Firmware, die als Basis für die Suche nach einer $(__undl "neuen") Version"
__nl " benutzt werden soll - das ist die kombinierte Versionsnummer aus den folgenden"
__nl " Variablen und ihr Wert 'überstimmt' mit jedem dort enthaltenen 'Einzelwert' alle"
__nl " anderen Angaben, unabhängig von ihrer Quelle:"
__nl "-----------------------------------------------------------------------------------------------"
__nl "Major die modellspezifische Firmware-Version ... üblicherweise ist das der Wert von 'HW'"
__nl " (also der 'HWRevision' der Box) minus 72, wie man mir mal irgendwo geschrieben hat"
__nl "Minor die 'Hauptversion' des FRITZ!OS"
__nl "Patch die 'Unterversion' des FRITZ!OS"
__nl "Buildnumber eine immer weiter ansteigende Zahl, die vermutlich eine fortlaufende Nummerierung"
__nl " für die komplettierten Durchläufe zum Erstellen einer Firmware bei AVM darstellt"
__nl " und über alle Modelle 'hochgezählt' wird; in älteren Firmware-Versionen (und in der"
__nl " 'jason_boxinfo.xml') lief das noch unter dem Namen 'Revision'"
__nl "-----------------------------------------------------------------------------------------------"
__nl "Serial die 'Seriennummer' der FRITZ!Box, üblicherweise ist das aber in Wirklichkeit der"
__nl " Wert von 'maca' (also der MAC-Adresse auf dem LAN-Interface) und nicht der Wert von"
__nl " 'SerialNumber' aus dem FRITZ!Box-Environment im TFFS"
__nl "$__yllw__ Anstatt des von der FRITZ!Box gelesenen Wertes wird hier (bei fehlender Angabe) nur"
__nl "$__yllw__ die Kombination aus den ersten drei Bytes (der OUI), gefolgt von einem zufälligen"
__nl "$__yllw__ Wert, der definitiv nicht der tatsächlichen MAC-Adresse entspricht, verwendet."
__nl "$__yllw__ Wer den echten Wert verwenden möchte, kann das durch Angabe der Option '-r'"
__nl "$__yllw__ erreichen - die Daten werden dann aber ohne weitere Nachfrage an den Hersteller"
__nl "$__yllw__ gesendet.$__rset__"
__nl "Name der Produktname der FRITZ!Box (CONFIG_PRODUKT), kann auch Leerzeichen enthalten"
__nl "HW der Wert von 'HWRevision' aus dem FRITZ!Box-Environment im TFFS"
__nl "OEM der Wert für das 'Branding', hiermit wird bei Boxen, die speziell für bestimmte"
__nl " ISPs produziert wurden, eine providerspezifische Konfiguration eingestellt; bei"
__nl " den Geräten, die sich direkt als 'AVM FRITZ!Box' zu erkennen geben, steht dort"
__nl " 'avm' für Geräte mit deutscher Firmware und 'avme' für solche, die eine Version"
__nl " der Firmware für internationale Verwendung installiert haben (oder hatten)"
__nl "Lang die in der Firmware eingestellte Sprache, wenn die Firmware mehrere Sprachversionen"
__nl " unterstützt oder einfach 'de' für die deutsche Version"
__nl "Annex das vom zuletzt verwendeten DSL-Anschluss genutzte Schema für die Belegung der"
__nl " Frequenzen (bei DSL-Boxen) oder 'Kabel' bei den DOCSIS-Boxen"
__nl "Country der in der Box eingestellte Ländercode (nach ITU-Empfehlung - E.164)"
__nl "Flag eine durch Kommata getrennte Liste von 'Flags', die bei der Abfrage des AVM-Servers"
__nl " zu verwenden sind - eine erfolgreiche Abfrage für die FRITZ!Box 6590 braucht hier"
__nl " z.B. (derzeitiger Stand, kann sich bei AVM jederzeit ändern) die Angabe von"
__nl " 'cable_retail', damit man eine sinnvolle Antwort erhält"
__nl "-----------------------------------------------------------------------------------------------"
__nl "$buildtype_option_name dieser Parameter wird von AVM benutzt, um die verschiedenen Entwicklungsreihen der"
__nl " Firmware (Release, Labor, Inhouse, spez. Beta-Versionen für einzelne Änderungen,"
__nl " etc.) voneinander zu unterscheiden. Eigentlich besteht sein Wert aus einer ein-"
__nl " bis fünfstelligen Dezimalzahl und so kann dieser Wert hier (sofern man ihn kennt)"
__nl " auch direkt angegeben werden. Fehlt er, wird er üblicherweise von einer vorhandenen"
__nl " FRITZ!Box gelesen (s.u. für die Beschreibung von 'Box'). Wird er explizit nicht"
__nl " angegeben (Wert 'empty'), wird '$buildtype_option_default' als Standard verwendet."
__nl " Um diesen Parameter auch ohne Kenntnis der passenden numerischen Angaben benutzbar"
__nl " zu machen, gibt es ein paar vordefinierte Namen für diese Firmware-Serien. Ein"
__nl " angegebener Wert wird zuerst in Großschreibweise überführt (es spielt also keine"
__nl " Rolle, wie der Wert geschrieben wurde, solange die Abfolge der Buchstaben paßt)"
__nl " und das Ergebnis mit den Bezeichnungen aus der unten stehenden Liste verglichen."
__nl " Handelt es sich beim angegebene Wert um einen dieser Bezeichner, wird der ihm"
__nl " zugeordnete numerische Wert für die Anfrage bei AVM verwendet. Da es für einige"
__nl " Werte mehrere (gebräuchliche) Namen gibt, sind einige Werte auch mehrfach in der"
__nl " Liste vorhanden."
get_known_buildtype_values | \
while read name value; do
__nl " $name=$value"
done
__nl "-----------------------------------------------------------------------------------------------"
__nl "$inhouse_option_name '1', um nur nach offziellen Versionen zu suchen oder '0', um auch die sogenannten"
__nl " 'Inhouse'-Versionen zu finden"
__nl "$__red__ Das Verwenden dieses Parameters wird ab Version 0.5 nicht mehr empfohlen,"
__nl "$__red__ stattdessen sollte der Parameter '$buildtype_option_name' (s.o.) genutzt werden.$__rset__"
__nl " Wird er weiterhin genutzt, wird für '${inhouse_option_name}=0' der Wert '1000' und für '${inhouse_option_name}=1'"
__nl " der Wert '1001' als '$buildtype_option_name' verwendet. Eine gleichzeitige Benutzung von"
__nl " '$inhouse_option_name' und '$buildtype_option_name' ist nicht erlaubt."
__nl "-----------------------------------------------------------------------------------------------"
__nl "Nonce dieser Parameter ist komplett optional, sein Fehlen führt auch nicht zur Abfrage"
__nl " bei der FRITZ!Box und der Wert (es muss sich um die Base64-Darstellung einer Folge"
__nl " von 16 Bytes mit (möglichst) zufälligen Daten handeln) wird vor seiner Verwendung"
__nl " nicht auf seine Gültigkeit geprüft - er muss nur dann angegeben werden, wenn der"
__nl " Aufrufer die Antwort des AVM-Servers speichern lassen will (mit der Option '-s')"
__nl " und dann seinerseits die Gültigkeit der Signatur in der SOAP-Response von AVM"
__nl " prüfen möchte"
__nl "-----------------------------------------------------------------------------------------------"
__nl "Box dieser Wert kann nicht durch die Angabe als Name/Wert-Paar auf der Kommandozeile"
__nl " festgelegt werden, aber es ist möglich, ihn bereits vor dem Start über das Shell-"
__nl " Environment zu setzen oder eben über eine Zuweisung in einer Konfigurationsdatei;"
__nl " aber sollten nach der Verarbeitung der Name/Wert-Paare von der Kommandozeile und"
__nl " nach dem Ausführen der Anweisungen in einer Konfigurationsdatei noch Einstellungen"
__nl " fehlen, die für den SOAP-Request benötigt werden, so muss diese Variable den DNS-"
__nl " Namen oder die IP-Adresse der FRITZ!Box enthalten, von der die fehlenden Angaben"
__nl " gelesen werden sollen (aus der Datei 'juis_boxinfo.xml' oder, wenn diese Datei"
__nl " in älterer Firmware nicht existieren sollte, aus der Datei 'jason_boxinfo.xml')\n"
__nl "Die Werte für 'Major', 'Minor', 'Patch' und 'Buildnumber' können auch nicht über Name/Wert-"
__nl "Paare auf der Kommandozeile gesetzt werden, nur vor dem Aufruf über das Environment oder über"
__nl "Zuweisungen in einer Konfigurationsdatei. Will man tatsächlich die Versionsnummer beim Aufruf"
__nl "direkt von der Kommandozeile aus angeben, muss man dafür die 'kombinierte' Versionsnummer als"
__nl "'Version' verwenden, die sich aus 'Major', 'Minor' und 'Patch' - jeweils mit einem Punkt"
__nl "getrennt - zusammensetzt, denen dann - durch einen Bindestrich (oder auch ein Minuszeichen,"
__nl "ganz wie man will) getrennt - noch die 'Buildnumber' folgt.\n"
__nl "Sollten jedenfalls am Ende noch irgendwelche Angaben fehlen (die Option '--debug' kann auch"
__nl "benutzt werden, um die Variablenzuweisungen zu protokollieren), muss die Variable 'Box' einen"
__nl "Wert enthalten, der die Abfrage einer vorhandenen FRITZ!Box ermöglicht.\n"
__nl "Für jede benötigte Einstellung kann man auch den Wert 'detect' angeben, das hat denselben"
__nl "Effekt wie das Fehlen dieser Einstellung und führt zum Versuch, den Wert aus der Box zu lesen."
__nl "Will man einen Wert nicht angeben und gleichzeitig verhindern, dass dieser aus dem Gerät"
__nl "gelesen wird, kann man 'empty' angeben - das Ergebnis ist dann ein leerer Wert. Als drittes"
__nl "'Schlüsselwort' im Wert einer Variablen kann 'fixed' angegeben werden, dem dann - durch einen"
__nl "Doppelpunkt getrennt - der eigentliche Wert folgt. Das ist zwar dasselbe wie die direkte"
__nl "Angabe des Wertes, aber wenn dieser Wert selbst eines der Schlüsselworte wäre (also 'detect'"
__nl "oder 'empty'), dann braucht man auch mal das 'fixed:' als Präfix.\n"
__nl "Wenn man einen Wert angeben will (oder muss), der seinerseits ein Leerzeichen (oder irgendein"
__nl "anderes Zeichen aus der IFS-Variablen) enthält, muss man diesen Wert beim Aufruf passend in"
__nl "Anführungszeichen setzen. Alternativ kann anstelle eines normalen Leerzeichens auch das"
__nl "Unicode-Zeichen für 'ZERO WIDTH SPACE' (U+200B) benutzt werden, diese Zeichen werden vor der"
__nl "Verwendung im SOAP-Request durch normale 'SPACE'-Kodierungen (U+0020) ersetzt."
__nl "Verwendet man Anführungszeichen, kann man diese nach eigenem Ermessen entweder um die gesamte"
__nl "Angabe von 'Parameter=Wert' setzen oder auch nur um 'Wert'; die Angaben:\n"
__nl "'Name=FRITZ!Box 7490 (UI)' und"
__nl "Name='FRITZ!Box 7490 (UI)'\n"
__nl "sind also vom Ergebnis her identisch. Doppelte Anführungszeichen sollte man hier nur nutzen,"
__nl "wenn ihre Verwendung einen speziellen Grund hat, z.B. Shell-Variablen in der Angabe, die beim"
__nl "Aufruf substituiert werden sollen.\n"
__nl "-----------------------------------------------------------------------------------------------\n"
__nl "Der Rückgabewert des Skripts (der 'exit code') kann verwendet werden, um Informationen über"
__nl "das Ergebnis zu erhalten - dabei werden die folgenden Werte verwendet:\n"
__nl "$(__bold "Wert Bedeutung\n")"
__nl "0 neue Firmware gefunden, die URL zum Download wurde nach STDOUT geschrieben"
__nl "1 Fehler beim Aufruf des Skripts, z.B. fehlender Wert für die 'Box'-Variable, ungültige"
__nl " Parameter beim Aufruf, fehlende Programme, usw."
__nl "2 keine neue Firmware gefunden, aber die Abfrage bei AVM war erfolgreich"
__nl "3 unvollständige Parameter, i.d.R. auch das Ergebnis einer nicht erreichbaren FRITZ!Box"
__nl " beim Versuch, fehlende Werte von dort zu lesen"
__nl "4 die Abfrage bei AVM war falsch, das kann an fehlenden oder falschen Parametern liegen"
__nl " und ist am Ende nur eine Schlussfolgerung aus der Tatsache, dass es gar keine Antwort"
__nl " vom AVM-Server innerhalb der Timeout-Zeitspanne gab (der könnte aber auch ganz simpel"
__nl " mal ausgefallen sein), die Antwort nicht von '200 OK' als Status-Code begleitet ist"
__nl " oder in der Antwort nicht die erwarteten Felder - das wären 'Found' und 'DownloadURL'"
__nl " im XML-Namespace 'ns3' (http://juis.avm.de/response) - vorhanden sind"
__nl "5 Fehler bei der Netzwerk-Kommunikation (Host nicht gefunden, Timeout, etc.)"
else
__purpose_hdr
__nl "This script may be used to query AVM's update information service (JUIS) for new versions.\n"
__nl "Due to the ability to configure many (or most) parameters of such a query manually, it is"
__nl "possible to look for fresh firmware for nearly all AVM devices.\n"
__nl "Nevertheless it's still possible to call the script only with the IP address of an existing"
__nl "(and locally reachable) FRITZ!OS-based router - then it simply will try to get all needed"
__nl "parameters for the query from this device.\n"
__nl "So it's still as easy as possible to look regularly, if a new firmware for your \"beloved"
__nl "router\" was published meanwhile - even from outside of this device and if you want to avoid"
__nl "any automatic discovery and installation of newer firmware versions, e.g. due to (reasonable)"
__nl "security concerns."
__usage_hdr
__usage_opt "options"; __usage_opt_end; __usage_opt "optional parameters"
__usage_end
__options_hdr
__option_debug 30
__option_help 30
__option_version 30
__nl; __option_show_opt 30 "-n" "--no-respawn"; __option_show_desc "do not respawn with 'bash', even if it's possible"
__nl; __option_show_opt 30 "-s" "--save-response" "filename"; __option_show_desc "save SOAP response to $(__undl "filename") for further processing"
__nl; __option_show_opt 30 "-a" "--show-response"; __option_show_desc "write SOAP response to STDERR"
__nl; __option_show_opt 30 "-i" "--ignore-cfgfile"; __option_show_desc "skip configuration file processing"
__nl; __option_show_opt 30 "-c" "--current"; __option_show_desc "try to get current version URL ('Patch' - see below - gets decremented)"
__nl; __option_show_opt 30 "-l" "--local"; __option_show_desc "use data from local device, do not try to read via network"
__nl; __option_show_opt 30 "-p" "--print-version"; __option_show_desc "show version number of found firmware on STDOUT"
__nl; __option_show_opt 30 "-r" "--use-real-serial"; __option_show_desc "send real Serial value read from device"
__options_end
__nl "The script attempts to read a configuration file with its own name (or better expressed:"
__nl "with the name, that was used to call it, because this may be a symbolic link, too) and an"
__nl "extension of 'cfg' from the same location, where the script itself was found. This file will"
__nl "be 'included' with a '.' (dot) command and may contain any shell statement - please be aware,"
__nl "that this may lead to a severe security threat, if anyone else may modify this file without your"
__nl "knowledge.\n"
__nl "To use another file instead, add a value named 'JUIS_CHECK_CFG', containing the name of a"
__nl "configuration file, to the environment of this script. To skip all configuration file"
__nl "processing, use the option '--ignore-cfgfile' (or '-i') from above.\n"
__nl "This configuration file may be used to set one or more of the needed settings (see below)"
__nl "from positional parameters on the command line and has to use the \"shift\" statement to"
__nl "remove any processed entries from the parameters collection.\n"
__nl "If the option '--show-response' is specified, the SOAP response (without HTTP headers) will be"
__nl "written to STDERR. This will be done using a XML formatter/linter, if present, and the default"
__nl "command looked up is 'xmllint' from the 'libxml2' project (http://xmlsoft.org/index.html). This"
__nl "default value may be replaced by an environment variable named 'XML_LINTER'."
__nl "-----------------------------------------------------------------------------------------------\n"
__nl "Any remaining values from command line are expected to be a name/value pair for one of the"
__nl "following known settings:\n"
__nl "$(__bold "name meaning\n")"
__nl "Version the firmware version to be assumed as the currently running one - this is the"
__nl " combined version string, which overrules any of the following partial values:"
__nl "-----------------------------------------------------------------------------------------------"
__nl "Major model-specific firmware version, usually it's 'HW' minus 72, someone has told me"
__nl "Minor main version number of FRITZ!OS"
__nl "Patch second part of version number"
__nl "Buildnumber an incrementing value, presumably the consecutive number for a whole build process,"
__nl " it's called 'Revision' in older firmware (and in the 'jason_boxinfo.xml' file)"
__nl "-----------------------------------------------------------------------------------------------"
__nl "Serial the serial number of the FRITZ!Box device (usually the same as 'maca')"
__nl "$__yllw__ If the value is read from device, only the first three bytes (the OUI) of it will"
__nl "$__yllw__ be used, together with three random bytes, which will never be the same as in the"
__nl "$__yllw__ real value. If you need/want to submit the original value of your device, you may"
__nl "$__yllw__ call the script with '-r' option - data will be sent to vendor without further"
__nl "$__yllw__ notification or information in this case.$__rset__"
__nl "Name the product name"
__nl "HW the hardware revision"
__nl "OEM the OEM value used (also known as the 'branding')"
__nl "Lang the current language set"
__nl "Annex the used annex for the DSL modem or 'Kabel' for DOCSIS devices"
__nl "Country the ITU recommended country code (E.164)"
__nl "Flag a comma-delimited list of flags to be included into the request"
__nl "-----------------------------------------------------------------------------------------------"
__nl "$buildtype_option_name this parameter is used by AVM to distinguish between various lines/series of"
__nl " firmware development."
__nl " The value is a number (with 1 to 5 digits) and may be specified this way, if the"
__nl " needed/wanted value is known. A missing value will be read from device usually (see"
__nl " 'Box' below), but if it's left off intentionally (specified as 'empty'), a default"
__nl " value of '$buildtype_option_default' is used."
__nl " To make calls with a specific value more handy, some known values (formerly seen by"
__nl " AVM or others) are available to be used in place of a numeric value. A specified"
__nl " value is converted to upper case and compared to the names below. If a match was"
__nl " found, the corresponding numeric value will be used for the JUIS request. Due to"
__nl " different (well-known) names for the same value, some duplicates exist."
get_known_buildtype_values | \
while read name value; do
__nl " $name=$value"
done
__nl "-----------------------------------------------------------------------------------------------"
__nl "$inhouse_option_name '1' to check only for public versions, '0' to accept 'inhouse builds' instead"
__nl "$__red__ This parameter is deprecated now (since version 0.5), use '$buildtype_option_name' (see above)"
__nl "$__red__ instead.$__rset__"
__nl " If it's still used (it's mutually exclusive with '$buildtype_option_name'), a value of '1001'"
__nl " for '${inhouse_option_name}=1' and '1000' for '${inhouse_option_name}=0' will be used as '$buildtype_option_name'."
__nl "-----------------------------------------------------------------------------------------------"
__nl "Nonce this parameter is strictly optional, its absence will not trigger any attempt to"
__nl " read from the 'Box' device and its value isn't checked further (has to be a Base64"
__nl " representation of 16 bytes with (preferably) random content; it may be used to set"
__nl " a caller specified 'nonce' for the SOAP response signature - this is required, if"
__nl " the caller wants to save the whole SOAP response (with '-s' option) and check its"
__nl " signature externally"
__nl "-----------------------------------------------------------------------------------------------"
__nl "Box this variable can't be set from a name/value pair on the command line, but it's"
__nl " possible to define it in the (shell) environment for the script or it has to be set"
__nl " from a configuration file; if any parameter for the SOAP request is missing, after"
__nl " all name/value pairs from command line were set and the configuration file was"
__nl " processed, this variable has to contain the address of a FRITZ!Box device, which"
__nl " must provide further settings/parameters for the request\n"
__nl "The values for 'Major', 'Minor', 'Patch' and 'Buildnumber' may not be set from the command"
__nl "line, only from a configuration file. If you want to set a different version number while"
__nl "calling the script, use positional parameters with an appropriate configuration file or use"
__nl "the compound value 'Version' to specify all parts at once.\n"
__nl "If any of the above values isn't set after the configuration file was processed, the 'Box'"
__nl "value has to be present - either containing an IP address or a DNS name of a FRITZ!Box"
__nl "device, which will be accessed to retrieve missing values. Only if all of the values above"
__nl "are already present, this read attempt will be skipped and the 'Box' value will be ignored.\n"
__nl "If no configuration file was found, a file containing the lines:\n"
__nl "Box=\$1"
__nl "shift\n"
__nl "is assumed instead - this means, the script expects the name or address of a FRITZ!OS device"
__nl "as first (and only) parameter and tries to read all other settings from the specified device.\n"
__nl "Each setting may be specified with the keyword \"detect\" (it has the same meaning as a missing"
__nl "entry), a setting with this state will be read from the FRITZ!OS device. If the keyword \"empty\""
__nl "is used, the parameter will be set to an empty string, but is seen as \"present\" and not read"
__nl "from the device. If the keyword \"fixed:\" is used at the beginning of the value (or if any"
__nl "other keyword is absent), the string after the colon is used as value. If you want to use a"
__nl "parameter starting with the string \"detect\", you have to use \"fixed:detect\" to specify it"
__nl "in the right manner.\n"
__nl "If you need to specify a setting with a value, which contains characters from the IFS value"
__nl "(it's used for field splitting from POSIX-compatible shells), it has to be enclosed properly"
__nl "in quotes. Or you may use the Unicode character 'ZERO WIDTH SPACE' (U+200B) instead, each"
__nl "occurrence of this character will be replace by a normal 'SPACE' (U+0020), before the value"
__nl "gets used in the SOAP request."
__nl "If you're using quotes, it doesn't matter, whether you're enclosing the parameter name and"
__nl "its value within, or if only the value itself is quoted. That means:\n"
__nl "'Name=FRITZ!Box 7490 (UI)' and"
__nl "Name='FRITZ!Box 7490 (UI)'\n"
__nl "have the same outcome. You should avoid using double quotes (\"), if there's not a special"
__nl "reason to use them - e.g. the substitution of variables within a value."
__nl "-----------------------------------------------------------------------------------------------\n"
__nl "The exit code of this script may be used to distinguish between the results, the following"
__nl "values are used:\n"
__nl "$(__bold "value meaning\n")"
__nl "0 new firmware found in vendor's answer, URL written to STDOUT"
__nl "1 error during call (missing 'Box' value, invalid parameter, missing programs, etc.)"
__nl "2 no newer firmware found, but processing finished without errors"
__nl "3 incomplete parameters (usually an unreachable device with address from 'Box')"
__nl "4 wrong SOAP call built with specified and/or read parameters - it's the inference based"
__nl " on a missing answer, a status code other than '200 OK' from AVM or a malformed answer,"
__nl " which has to be a valid SOAP response in case of success"
__nl "5 network I/O error (host not found, no answer, etc.)"
fi
}
#######################################################################################################
# #
# L10N strings #
# #
#######################################################################################################
languages="en de"
L10N_DBG_001_en="Reading and interpreting configuration file '%%s' with content:\\\\n"
L10N_DBG_001_de="Verarbeiten der Konfigurationsdatei '%%s' mit folgendem Inhalt:\\\\n"
L10N_DBG_002_en="Configuration file processing finished.\\\\n"
L10N_DBG_002_de="Ende der Verarbeitung der Konfigurationsdatei\\\\n"
L10N_DBG_003_en="Variables set:\\\\n"
L10N_DBG_003_de="Werte der Variablen:\\\\n"
L10N_DBG_004_en="Sent request:\\\\n"
L10N_DBG_004_de="Gesendete Abfrage:\\\\n"
L10N_DBG_005_en="Received response:\\\\n"
L10N_DBG_005_de="Empfangene Antwort:\\\\n"
L10N_DBG_006_en="Reading values from '%%s:%%s/%%s': "
L10N_DBG_006_de="Lesen der Parameter von '%%s:%%s/%%s': "
L10N_DBG_007_en="Read response from device:\\\\n"
L10N_DBG_007_de="Antwort vom Gerät:\\\\n"
L10N_DBG_008_en="Reading response from '%%s:%%s': "
L10N_DBG_008_de="Lesen der Antwort von '%%s:%%s': "
L10N_DBG_009_en="Setting '%%s' to '%%s' from command line parameter\\\\n"
L10N_DBG_009_de="Setzen des Parameters '%%s' auf '%%s' entsprechend der Angabe auf der Kommandozeile\\\\n"
L10N_DBG_010_en="Splitting compound version number '%%s' to:\\\\n"
L10N_DBG_010_de="Zerlegen der zusammengesetzten Versionsnummer '%%s' in die Teile:\\\\n"
L10N_DBG_011_en="Compound version number used for request: '%%s'\\\\n"
L10N_DBG_011_de="Zusammengesetzte Versionsnummer für die Prüfung: '%%s'\\\\n"
L10N_DBG_012_en="Respawning script with Bash as shell, calling: %%s\\\\n"
L10N_DBG_012_de="Neustart des Skripts mit einer Bash-Shell mit dem Kommando: %%s\\\\n"
L10N_DBG_013_en="SOAP response will be saved to: %%s\\\\n"
L10N_DBG_013_de="Die Antwort vom AVM-Server wird in folgender Datei gespeichert: %%s\\\\n"
L10N_DBG_014_en="SOAP request nonce set to '%%s' from command line parameter\\\\n"
L10N_DBG_014_de="Der Zufallswert für die Signatur der Serverantwort wurde auf '%%s' gesetzt, entsprechend der Angabe auf der Kommandozeile.\\\\n"
L10N_DBG_015_en="Skipping configuration file processing ...\\\\n"
L10N_DBG_015_de="Die Verarbeitung der Konfigurationsdatei wird übergangen ...\\\\n"
L10N_DBG_016_en="SOAP response saved to: %%s\\\\n"
L10N_DBG_016_de="Die SOAP-Antwort wurde in folgender Datei gespeichert: %%s\\\\n"
L10N_DBG_017_en="Reading values from local file '%%s'.\\\\n"
L10N_DBG_017_de="Lesen der Parameter aus der lokalen Datei '%%s'.\\\\n"
L10N_DBG_018_en="Just decremented the version number to get an answer with URL for current version.\\\\n"
L10N_DBG_018_de="Die Versionsnummer wurde dekrementiert, um eine Antwort mit der URL für die aktuelle Version zu erhalten.\\\\n"
L10N_DBG_019_en="TCP request using internal network feature (from Bash) failed, 'netcat' (nc) will be tried next: "
L10N_DBG_019_de="Die Abfrage mit den Netzwerk-Funktionen der 'Bash'-Shell war nicht erfolgreich, jetzt wird es mit 'netcat' (nc) erneut versucht: "
L10N_ERR_OPT_en="Unknown option '%%s' specified."
L10N_ERR_OPT_de="Unbekannte Option '%%s' beim Aufruf angegeben."
L10N_ERR_SCR_en="Missing another needed executable: %%s."
L10N_ERR_SCR_de="Ein anderes benötigtes Skript ist nicht vorhanden: %%s."
L10N_ERR_EXC_en="Missing a required command: %%s."
L10N_ERR_EXC_de="Eine benötigte ausführbare Datei fehlt: %%s."
L10N_ERR_TRM_en="%%s is a terminal device. %%s"
L10N_ERR_TRM_de="%%s ist ein Terminal. %%s"
L10N_ERR_001_en="Missing configuration file and first parameter (the IP address of a reachable FRITZ!Box) at the same time."
L10N_ERR_001_de="Die Konfigurationsdatei wurde nicht gefunden und gleichzeitig fehlt der erste Parameter (die IP-Adresse der FRITZ!Box) beim Aufruf."
L10N_ERR_002_en="Missing FRITZ!Box IP address or DNS name."
L10N_ERR_002_de="Die IP-Adresse oder der DNS-Name der FRITZ!Box fehlt in den Parametern."
L10N_ERR_003_en="Error reading '%%s' from FRITZ!Box device with address '%%s:%%s'."
L10N_ERR_003_de="Fehler beim Lesen der Geräteparameter (%%s) von der FRITZ!Box mit der Adresse '%%s:%%s'."
L10N_ERR_004_en="'%%s' item may only be empty or set to a fixed boolean value."
L10N_ERR_004_de="Der Wert für '%%s' muß leer sein oder fest auf '0' oder '1' (bzw. 'false' oder 'true') gesetzt werden."
L10N_ERR_005_en="Unknown setting '%%s' found at command line."
L10N_ERR_005_de="Der Parameter '%%s' ist unbekannt."
L10N_ERR_006_en="The '%%s' item has to be set to '0' for 'inhouse versions' or '1' for others."
L10N_ERR_006_de="Der Wert für '%%s' kann nur '0' für die Suche nach internen Versionen sein, ansonsten ist '1' die einzige gültige Alternative und auch gleichzeitig der Standardwert."
L10N_ERR_007_en="Unexpected data received from network."
L10N_ERR_007_de="Es wurden Daten mit einem unerwarteten Format empfangen vom Server."
L10N_ERR_008_en="Unexpected error code %s returned from %s."
L10N_ERR_008_de="Es wurde ein unerwarteter Statuscode (%%s) von %%s gesendet."
L10N_ERR_009_en="No newer version found, check was made with source version '%%s'."
L10N_ERR_009_de="Es wurde keine neue Version gefunden, die Prüfung erfolgte ausgehend von der Version '%%s'."
L10N_ERR_010_en="No data file '%%s' found - make sure it's a FRITZ!OS device, if you use '--local' option."
L10N_ERR_010_de="Die Datei '%%s' existiert nicht - die Option '--local' ist nur sinnvoll, wenn das Skript auf einer FRITZ!Box aufgerufen wird."
L10N_ERR_011_en="(unused)"
L10N_ERR_011_de="(unused)"
L10N_ERR_012_en="Error saving SOAP response to '%%s'."
L10N_ERR_012_de="Fehler beim Speichern der Antwort in der Datei '%%s'."
L10N_ERR_013_en="Missing file name after option '%%s'."
L10N_ERR_013_de="Der Option '%%s' muss ein Dateiname folgen."
L10N_ERR_014_en="Missing 'HW' value after processing all settings, it's part of AVM' server name and MUST be present."
L10N_ERR_014_de="Der Wert für 'HW' fehlt für die Abfrage bei AVM - er ist Bestandteil des Servernamens und MUSS vorhanden sein."
L10N_ERR_015_en="Values requiring detection: %%s"
L10N_ERR_015_de="Werte, die von der FRITZ!Box gelesen werden müssen: %%s"
L10N_ERR_016_en="Missing value for '%%s' variable."
L10N_ERR_016_de="Der Wert für den '%%s'-Parameter fehlt."
L10N_ERR_017_en="The value for '%%s' variable (%%s) is neither a known name nor a valid number."
L10N_ERR_017_de="Der Wert für den '%%s'-Parameter (%%s) ist weder einer der bekannten Namen, noch eine gültige Nummer."
L10N_ERR_018_en="The parameters '%%s' and '%%s' are mutually exclusive, please use only '%%s' because '%%s' is deprecated now."
L10N_ERR_018_de="Die Angabe von '%%s' und '%%s' bei demselben Aufruf ist nicht erlaubt, bitte nur '%%s' verwenden, weil '%%s' nicht mehr genutzt werden sollte."
L10N_ERR_019_en="TCP connection using Bash' internal network feature failed and 'netcat' (nc) was not found."
L10N_ERR_019_de="Die TCP-Funktion der 'Bash'-Shell hat ein Problem und 'netcat' (nc) konnte nicht gefunden werden."
L10N_ERR_020_en="Network communication failed."
L10N_ERR_020_de="Bei der Netzwerk-Kommunikation ist ein Fehler aufgetreten."
L10N_INF_001_en="Found newer version: %%s"
L10N_INF_001_de="Neue Version gefunden: %%s"
L10N_INF_002_en="JUIS response was:"
L10N_INF_002_de="JUIS-Antwort:"
L10N_HLP_PUR_en="Purpose"
L10N_HLP_PUR_de="Zweck"
L10N_HLP_USG_en="Usage"
L10N_HLP_USG_de="Aufruf"
L10N_HLP_SUP_en="Supported "
L10N_HLP_SUP_de="Verfügbare "
L10N_HLP_OPT_en="options"
L10N_HLP_OPT_de="Optionen"
L10N_HLP_ARE_en="are"
L10N_HLP_ARE_de="sind"
L10N_HLP_DBG_en="display debug info on STDERR; must prefix all other options"
L10N_HLP_DBG_de="Debug-Ausgaben auf STDERR; muss die erste Option sein"
L10N_HLP_HLP_en="show this information (must be the first option)"
L10N_HLP_HLP_de="Anzeige dieser Hilfe-Information (muss die erste Option sein)"
L10N_HLP_VER_en="show version and exit (must be the first option)"
L10N_HLP_VER_de="(ausschließliche) Anzeige der Versionsinformation"
#######################################################################################################
# #
# usage and display helpers from YourFritz framework (included to get a single file to copy/install) #
# #
#######################################################################################################
__bold__="$(printf "\033[1m")"
__undl__="$(printf "\033[4m")"
__red__="$(printf "\033[1m\033[31m")"
__gren__="$(printf "\033[1m\033[32m")"
__yllw__="$(printf "\033[1m\033[33m")"
__blue__="$(printf "\033[1m\033[34m")"
__rset__="$(printf "\033[0m")"
__bold() { printf "$__bold__"; printf -- "$@"; printf "$__rset__"; }
__undl() { printf "$__undl__"; printf -- "$@"; printf "$__rset__"; }
__show_script_name()
{
printf "%s${0#*/}\033[0m: " "$1"
}
__get_script_lines()
{
sed -n -e "/^#*${1}#\$/,/^#\{20\}.*#\$/p" "$0" | \
sed -e '1d;$d' | \
sed -e 's|# \(.*\) *#$|\1|' | \
sed -e 's|^#*#$|--|p' | \
sed -e '$d'
}
__license()
{
__get_script_lines "LIC"
}
__version()
{
__get_script_lines "VER" | sed -e "1,2s|^\([^,]*\),\(.*\)\$|$__bold__\1$__rset__,\2|"
}
__copyright()
{
__get_script_lines "CPY"
}
__get_language()
(
__get_language_code()
{
printf "%s\n" "$1" | sed -n -e "1s|^\([A-Za-z]*\).*|\1|p" | sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/"
}
lang="$1"
shift
if ! [ -z "$LC_ALL" ]; then
check="$(__get_language_code "$LC_ALL")"
else
[ -z "$LANG" ] || check="$(__get_language_code "$LANG")"
fi
if ! [ -z "$check" ]; then
[ "$lang" = "$check" ] || \
for l in $*; do
[ "$l" = "$check" ] && lang="$l" && break
done
fi
printf "%s" "$lang"
)
__language__="$(eval __get_language $languages)"
__get_localized()
{
eval printf "\"\$L10N_${1}_${__language__}\""
}
__emsg()
{
__show_script_name "$__red__" 1>&2
mask="$1"
shift
printf "${__bold__}${mask}${__rset__}\a\n" "$@" 1>&2
}
__info()
{
__show_script_name "$__gren__" 1>&2
mask="$1"
shift
printf "${__bold__}${mask}${__rset__}\n" "$@" 1>&2
}
__check_option()
{
o="$1"
shift
for v in $*; do
[ "$o" = "$v" ] && printf 1 && return 0
done
printf 0
return 1
}
__is_option()
{
[ "$(expr \( "$1" : "\(.\).*" \) )" = "-" ] && return 0 || return 1
}
__is_last_option()
{
[ "$1" = "--" ] && return 0 || return 1
}
__options_end__="eval while __is_option \"\$1\"; do __is_last_option \"\$1\" && shift && break;\
__emsg \"\$(__get_localized ERR_OPT)\" \"\$1\"; exit 1; done;"
__version_option()
{
if __check_option "$1" "-V" "--version" >/dev/null; then
__version
__copyright
__license
printf "\n"
exit 1
fi
return 1
}
__version_option__="eval __version_option \"\$@\" && exit 0"
__help_option()
{
if __check_option "$1" "-h" "--help" >/dev/null; then
if [ -t 1 ]; then
pager="${PAGER:-less}"
#[ "$pager" = "less" ] && pager_options="-R" || unset pager_options
if command -v "$pager" >/dev/null; then
__usage | "$pager" $pager_options
exit 1
fi
fi
__usage
exit 1
fi
}
__help_option__="eval __help_option \"\$@\""
__debug_option()
{
__check_option "$1" "-d" "--debug" && return 0
return 1
}
__debug_option__="eval __debug_set__=\$(__debug_option \$1) && __debug_text__=\"\$1\" && shift"
__debug_on__="eval __debug_set__=1; __debug_text__=\"-d\";"
__is_debug() { [ $__debug_set__ -eq 1 ] && return 0 || return 1; }
__debug()
{
[ $__debug_set__ -eq 1 ] || return;
mask="$1"
shift
printf "%sdebug%s: " "$__yllw__" "$__rset__" 1>&2
printf -- "$mask" "$@" 1>&2
}
__usage()
(
indent=0
__indent_on() { indent=$(( indent + 4 )); }
__indent_off() { indent=$(( indent - 4 )); }
__indent() { [ $indent -gt 0 ] && printf "%0${indent}s" " "; };
__nl() { printf "\n%s" "$(__indent)"; printf -- "$1"; }
__purpose_hdr() { __nl; __bold "$(__get_localized HLP_PUR):"; printf "\n"; }
__usage_name() { __bold "${0#*/}"; }
__usage_hdr() { printf "\n"; __nl; __bold "$(__get_localized HLP_USG):\n"; __indent_on; __nl "$(__usage_name)"; }
__usage_end() { __indent_off; printf "\n"; }
__usage_opt_int() { v="$1"; shift; [ $# ] && m="$@"; printf -- "[ %s%s ]" "$(__undl "$v")" "$m"; unset m v; };
__usage_opt_end() { printf -- " [ -- ]"; }
__usage_opt() { printf -- " %s" "$(__usage_opt_int "$@")"; }
__usage_arg() { printf -- " %s" "$(__undl "$1")"; }
__options_hdr() { __nl "$(__get_localized HLP_SUP)"; __undl "$(__get_localized HLP_OPT)"; printf " $(__get_localized HLP_ARE):\n"; }
__options_end() { printf "\n"; }
__option_show_opt() {
printf -- "%s, %s" "$2" "$3"
__l4__=${#4}
[ $__l4__ -gt 0 ] && printf " %s%s%s" "$__undl__" "$4" "$__rset__" && __l4__=$(( __l4__ + 1 ))
printf "%0$(( $1 - ${#2} - ${#3} - __l4__ - 3 ))s" " "
unset __l4__
}
__option_show_desc() { printf -- "- %s" "$@"; }
__option_debug() { __nl; __option_show_opt ${1:-15} "-d" "--debug"; __option_show_desc "$(__get_localized HLP_DBG)"; }
__option_help() { __nl; __option_show_opt ${1:-15} "-h" "--help"; __option_show_desc "$(__get_localized HLP_HLP)"; }
__option_version() { __nl; __option_show_opt ${1:-15} "-V" "--version"; __option_show_desc "$(__get_localized HLP_VER)"; }
__end() { printf "\n%s\n" "$__rset__"; }
__version
__copyright
__license
usage_text
__end
)
__check_required_command()
(
IFS=:
set -- $1
for n in $@; do
command -v $n 2>/dev/null 1>&2 && exit 0
done
exit 1
)
__check_required_commands()
{
for n in $@; do
__check_required_command $n && continue
__emsg "$(__get_localized ERR_EXC)" "$(printf "%s\n" "$n" | sed -e "s|:| or |g")"
return 1
done
return 0
}
__check_required_commands__="eval __check_required_commands \"\$__required_commands\" || exit 1"
__required_commands="sed mkfifo:mknod cat rm mkdir touch wc cmp date"
[ -z "${BASH_VERSION}" ] && __required_commands="$__required_commands nc"
#######################################################################################################
# #
# constants #
# #
#######################################################################################################
# common property names
common_names="Name HW OEM Lang Annex Country"
# properties with different names
version_name="Version"
buildnumber_name="Buildnumber"
serial_name="Serial"
version_parts="%Major%.%Minor%.%Patch%[-%Buildnumber%]"
version_compound="\$Version-\$Buildnumber"
version_string="\$(printf "%d.%02d.%02d-%s" \$Major \$Minor \$Patch \$$buildnumber_name)"
version_decrement="Patch Minor"
version_decrement_clear="$buildnumber_name=00000"
boxinfo1_replace_names=""
boxinfo2_replace_names="Revision:Buildnumber"
numeric_values="Major Minor Patch"
alt_names="$version_name:Major $version_name:Minor $version_name:Patch $version_name:$buildnumber_name"
# flag option name, may contain more than one value (delimited by comma), will be splitted to multiple XML entities
flags_name="Flag"
# special 'boolean' option, usable to retrieve internal updates from AVM' servers, if they're published
# via JUIS - finally it modifies the 'buildtype' parameter of our SOAP request
# the (variable's) name and meaning are a little bit confusing, but really 'true' or 'false' in shell
# syntax ... a value of "1" means "no inhouse version" (or 'inhouse=false') and "0" searches for non-public
# versions
# Public=0 uses BuildType=1000 and Public=1 uses Buildtype=1001.
# Public variable has been deprecated now, use $buildtype_option_name from below instead.
inhouse_option_name="Public"
# firmware build type - found in 'juis_boxinfo.xml' with XML tag 'Buildtype'
# This variable is mutually exclusive with $inhouse_option_name, which is maintained for backward compatibility only.
# The specified value is converted to upper case letters and compared to the list of known, pre-defined values below.
# If a match is found, the associated value will be used for our JUIS request, otherwise the value will be checked,
# whether it contains only one to five digits (according to AVM's error message, if an invalid value was used) and
# in this case the raw value is used for the JUIS request.
buildtype_option_name="Buildtype"
known_buildtype_values="RELEASE:1 LABOR:1001 BETA:1001 LABBETA:1001 PLUS:1007 LABPLUS:1007 INHOUSE:1000 INHAUS:1000 PHONE:1004 LABPHONE:1004"
buildtype_option_default="1"
get_known_buildtype_values()
(
for item in $known_buildtype_values; do
name="$(expr "$item" : "\([^:]*\):.*")"
value="$(expr "$item" : "[^:]*:\(.*\)")"
printf "%s %s\n" "$name" "$value"
done
)
# nonce variable name for response signature
nonce_option_name="Nonce"
# local file retrieval constants
boxport=80
boxinfo1="juis_boxinfo.xml"
boxinfo2="jason_boxinfo.xml"
local_timeout=10
# SOAP request constants
hostbase="jws.avm.de"
#hostbase="fritz.box"
juisport=80
juisurl="/Jason/UpdateInfoService"
remote_timeout=20
# path of local data file on a FRITZ!OS device
local_dir="/var"
# HTTP header related constants
headerline_0="POST %s HTTP/1.1\r\n"
headerline_1="Host: %s:%u\r\n"
headerline_2="Content-Length: %u\r\n"
headerline_3="Content-Type: %s\r\n"
headerline_4="Connection: close\r\n"
contenttype="text/xml; charset=\"utf-8\""
# SOAP request body, should better be constructed on-the-fly in a future version
body_tmpl="\
\n\
\n\
\n\
\n\
\n\
%s\n\
Box\n\
true\n\
\n\
\n\
%s\n\
%s\n\
%s\n\
%s\n\
%s\n\
%s\n\
%s\n\
%s\n\
%s\n\
%s\n\
%s\n\
%s\n\
%s\n\
1\n\
oma_lan\n\
\n\
\n\
\n\
\n\
"
# debug log delimiter line for easier reading
delimiter_line="-------------------------------------------------------\n"
#######################################################################################################
# #
# check parameters #
# #
#######################################################################################################
options=""
parameters=""
$__help_option__
$__version_option__
$__debug_option__
__no_cfgfile=0
__no_respawn=0
__use_real_serial=0
__get_current=0
__use_local=0
__show_new_version=0
__show_response=0
__save_output=""
escape_value() { printf "%s\n" "$@" | sed -e "s| |$(printf "\342\200\212")|g"; }
unescape_value() { printf "%s\n" "$@" | sed -e "s|$(printf "\342\200\212")| |g"; }
while [ $# -gt 0 ]; do
! __is_option "$1" && parameters="${parameters}${parameters:+ }$(escape_value "$1")" && shift && continue
__is_last_option "$1" && shift && options="${options:+${options} }$(escape_value "$1")" && continue
if __check_option "$1" "-i" "--ignore-cfgfile" >/dev/null; then
__no_cfgfile=1
options="${options:+${options} }--ignore-cfgfile"
shift
continue
elif __check_option "$1" "-n" "--no-respawn" >/dev/null; then
__no_respawn=1
shift
continue
elif __check_option "$1" "-r" "--use-real-serial" >/dev/null; then
__use_real_serial=1
options="${options:+${options} }--use-real-serial"
shift
continue
elif __check_option "$1" "-c" "--current" >/dev/null; then
__get_current=1
options="${options:+${options} }--current"
shift
continue
elif __check_option "$1" "-l" "--local" >/dev/null; then
__use_local=1
options="${options:+${options} }--local"
shift
continue
elif __check_option "$1" "-p" "--print-version" >/dev/null; then
__show_new_version=1
options="${options:+${options} }--print-version"
shift
continue
elif __check_option "$1" "-a" "--show-response" >/dev/null; then
__show_response=1
options="${options:+${options} }--show-response"
shift
continue
elif __check_option "$1" "-s" "--save-response" >/dev/null; then
if [ ${#2} -eq 0 ] || __is_option "$2"; then
__emsg "$(__get_localized ERR_013)" "$1" && exit 1
else
__save_output="$(unescape_value "$2")"
options="${options:+${options} }--save-response $(escape_value "$2")"
shift 2
continue
fi
else
$__options_end__
fi
done
__is_debug && options="--debug${options:+ $options}"
if [ $__no_respawn -eq 0 ] && [ -z "${BASH_VERSION}" ]; then
bash="$(command -v bash 2>/dev/null)"
if ! [ -z "$bash" ]; then
if command bash --help 2>/dev/null | grep -q "GNU bash" 2>/dev/null; then
[ "$(set -o 2>/dev/null | sed -n -e 's|^xtrace[ \t]*\(.*\)$|\1|p')" = "on" ] && xtrace="-x " || xtrace=""
options="${options:+${options} }--no-respawn "
__debug "$(__get_localized DBG_012)" "command bash $xtrace$0 $options$parameters"
command bash $xtrace$0 $options$parameters
exit $?
fi
fi
fi
set -- $parameters
#######################################################################################################
# #
# check environment #
# #
#######################################################################################################
$__check_required_commands__
#######################################################################################################
# #
# subfunctions #
# #
#######################################################################################################
# #
# read data from a TCP connection - use native /dev/tcp, if we're running from a 'bash' instance or #
# use a 'netcat' version (called with 'nc') for any other host shell #
# #
#######################################################################################################
tcp_read()
(
rc=0
if [ -n "${BASH_VERSION}" ]; then
use_devtcp $1 $2 "$3" $4
rc=$?
if [ "$rc" -ne 0 ]; then
__debug "$(__get_localized DBG_019)"
if __check_required_command "nc"; then
run_nc $1 $2 "$3" $4
rc=$?
else
__emsg "$(__get_localized ERR_019)"
rc=1
fi
fi
else
run_nc $1 $2 "$3" $4
rc=$?
fi
exit $rc
)
#######################################################################################################
# #
# use /dev/tcp to access the server #
# #
#######################################################################################################
use_devtcp()
(
host="$1"
port="$2"
out="$3"
timeout="$4"
i=0
exec 7>&2
__is_debug && trap='printf\ "\\\\n"\ 1\>\&7\;exec\ 2\>\&7' || trap='exec\ 2\>\&7'
eval trap "$trap" EXIT
exec 2>/dev/null
set -e
exec 5<>/dev/tcp/$host/$port
set +e
cat <&5 >"$out" &
catpid=$!
cat >&5
while ! [ -s "$out" ] && kill -s 0 $catpid; do
i=$(( i + 1 ))
__is_debug && printf "." 1>&7
[ $i -gt $timeout ] && break
sleep 1
done
sleep 1
exec 5>&-
sleep 1
kill -s 0 $catpid && kill $catpid
__is_debug && printf "\n" 1>&7
trap '' EXIT
exec 2>&7
)
#######################################################################################################
# #
# run 'nc' applet/command #
# #
#######################################################################################################
run_nc()
(
hostname="$1"
port="$2"
out="$3"
timeout="$4"
pin="$(mktmp -p "$td")"
i=0
exec 7>&2
exec 2>/dev/null
rm "$pin"
command -v mkfifo 2>/dev/null 1>&2 && mkfifo "$pin" || mknod "$pin" p
exec 6<>"$pin"
nc "$hostname" "$port" >"$out" <"$pin" &
ncpid=$!
cat - 1>&6
while ! [ -s "$out" ] && kill -s 0 $ncpid; do
i=$(( i + 1 ))
__is_debug && printf "." 1>&7
[ $i -gt $timeout ] && break
sleep 1
done
__is_debug && printf "\n" 1>&7
sleep 1
exec 6>&-
sleep 1
kill -s 0 $ncpid && kill $ncpid
rm "$pin"
exec 2>&7
)
#######################################################################################################
# #
# get the numbers from 1 to 8 in any random order #
# #
#######################################################################################################
getrandomorder()
(
v=$(date +%s)
i=0;
n="8 5 2 1 4 7 6 3"
while [ $i -lt 8 ]; do
x=$(( ( ( v >> ( y * ( 3 + ( y & 1 ) ) ) ) & 7 ) + 1 ))
set -- $n
[ $x -gt $# ] && x=$#
eval y=\$$x
n=""
while [ $1 ]; do
[ $y = $1 ] || n="$n $1"
shift
done
printf "\\\\%d" $y
i=$(( i + 1 ))
done
)
#######################################################################################################
# #
# extract result variables from XML answer #
# #
#######################################################################################################
extract_xml()
(
input="$1"
ns="$2"
tag="$3"
if [ "$3" = "$serial_name" ] && [ $__use_real_serial -eq 0 ]; then
# replace read serial number with a random value after OUI
serial="$(sed -n -e "s|.*<$ns:$tag>\(.*\)$ns:$tag>.*|\1|p" $input)"
oui="$(expr "$serial" : "\([0-9a-fA-F]\{6\}\).*")"
rn="$(expr "$serial" : "[0-9a-fA-F]\{6\}\(.*\)")"
rand="$rn"
mask="%06X"
[ -z "$(expr "$serial" : ".*\([a-f]\).*")" ] || mask="%06x"
order="$(getrandomorder)"
while [ "$rand" = "$rn" ]; do
rand="$(printf "$mask" "$(date +%s)" | sed -n -e "s|\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)|$order|p" | sed -n -e "s|.*\(.\{6\}\)\$|\1|p")"
done
printf "%s%s" "$oui" "$rand"
else
sed -n -e "s|.*<$ns:$tag>\(.*\)$ns:$tag>.*|\1|p" $input
fi
)
#######################################################################################################
# #
# read device parameters from a FRITZ!Box #
# #
#######################################################################################################
device_read()
(
ip="$1"
port="$2"
file="$3"
out="$(mktmp -p "$td")"
__debug "$delimiter_line"
__debug "$(__get_localized DBG_006)" "$ip" "$port" "$file"
printf "GET /%s HTTP/1.0\r\n\r\n" "$file" | tcp_read "$ip" "$port" "$out" $local_timeout
if [ -s "$out" ]; then
if __is_debug; then
__debug "$(__get_localized DBG_007)"
__debug "$delimiter_line"
sed -e "s|^| |" "$out" 1>&2
printf "\n" 1>&2
fi
answer="$(sed -n -e "1p" "$out")"
if [ "$(expr \( "$answer" : ".*\(200 OK\).*" \) )" = "200 OK" ]; then
cat "$out"
rc=0
else
rc=1
fi
else
rc=1
fi
rm "$out" 2>/dev/null
exit $rc
)
#######################################################################################################
# #
# check and convert Buildtype value specified #
# #
#######################################################################################################
check_buildtype_value()
(
value="$(printf "%s\n" "$1" | sed -e "y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/")"
for k in $known_buildtype_values; do
n="$(expr "$k" : "\([^:]*\):.*")"
v="$(expr "$k" : "[^:]*:\(.*\)")"
if [ "$n" = "$value" ]; then
printf "%s\n" "$v"
exit 0
fi
done
if [ "$(expr "$value" : "\([0-9]\{1,5\}\)")" = "$value" ]; then
printf "%s\n" "$value"
exit 0
fi
exit 1
)
#######################################################################################################
# #
# wrap 'realpath' call with never-failing code, if the native binary is missing (on MacOS X) #
# #
#######################################################################################################
realpath()
(
command realpath "$*" 2>/dev/null || printf "%s" "$*"
)
#######################################################################################################
# #
# create a nonce value with our inline Base64 encoder, if no real 'base64' command exists #
# #
#######################################################################################################
get_nonce()
(
__base64_charset="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
__base64_append()
{
if ! [ $1 = eof ]; then
l=$(( l + 1 ))
v=$(( ( v * 256 ) + $1 ))
else
[ $l -eq 0 ] && return
v=$(( v << ( 8 * ( 3 - l ) ) ))
fi
if [ $l -eq 3 ] || [ $1 = eof ]; then
printf "%c" $(expr "${__base64_charset}" : ".\{$(( v >> 18 ))\}\(.\).*")
printf "%c" $(expr "${__base64_charset}" : ".\{$(( ( v >> 12 ) % 64 ))\}\(.\).*")
[ $l -eq 1 ] && printf "==" && return
printf "%c" $(expr "${__base64_charset}" : ".\{$(( ( v >> 6 ) % 64 ))\}\(.\).*")
[ $l -eq 2 ] && printf "=" && return
printf "%c" $(expr "${__base64_charset}" : ".\{$(( v % 64 ))\}\(.\).*")
l=0
v=0
k=$(( k + 1 ))
[ $k -eq 19 ] && printf "\n" && k=0
fi
}
__base64_read_octal()
{
i=1
l=0
v=0
k=0
ff=0
while read pos left right; do
while [ $i -lt $pos ]; do # zeros are equal for 'cmp'
if [ $ff -eq 1 ]; then
__base64_append 377
ff=0
i=$(( i + 1 ))
fi
__base64_append 0
i=$(( i + 1 ))
done
if [ $right = 377 ] && [ $ff -eq 0 ]; then
ff=1
continue
fi
__base64_append 0$right
i=$(( pos + 1 ))
done
__base64_append eof
}
zeros_1="$(mktmp -p "$td")"
zeros_2="$(mktmp -p "$td")"
printf "\000" >"$zeros_1"
cat "$zeros_1" "$zeros_1" >"$zeros_2"
cat "$zeros_2" "$zeros_2" >"$zeros_1"
cat "$zeros_1" "$zeros_1" >"$zeros_2"
cat "$zeros_2" "$zeros_2" >"$zeros_1"
cat "$zeros_1" "$zeros_1" >"$zeros_2"
printf "%s\377" "$(date +%T%D)" | command cmp -l -- "$zeros_2" - 2>/dev/null | __base64_read_octal
rm "$zeros_1" "$zeros_2" 2>/dev/null
exit $rc
)
#######################################################################################################
# #
# make temporary directory or file, emulate 'mktemp' if it's missing #
# #
#######################################################################################################
mktmp()
(
name="$(mktemp $* 2>/dev/null)"
if [ $? -eq 127 ] || [ -z "$name" ]; then
# mktemp is missing, emulation needed
tmp="$TMPDIR"
[ "$1" = "-d" ] && dir=1 || dir=0
[ "$1" = "-p" ] && tmp="$2"
[ -z "$tmp" ] && tmp="/tmp"
name="$tmp/$(date +%s)_$$"
if [ $dir -eq 1 ]; then
[ -d "$name" ] && name="${name}_$(sleep 1; date +%s)"
mkdir -p "$name" 2>/dev/null
else
while [ -e "$name" ]; do
name="${name}_$$"
done
touch "$name" 2>/dev/null
fi
fi
printf "$name"
)
#######################################################################################################
# #
# build a string with each valid variable name #
# #
#######################################################################################################
get_names()
(
l_names="$1"
i=$3
for n in $2; do
p=$(IFS=:; set -- $n; eval printf "%s" "\$${i}")
l_names="$l_names $p"
done
l_names="$l_names"
for n in $l_names; do
[ "$n" = "$last" ] && continue
printf " %s" "$n"
last="$n"
done
)
#######################################################################################################
# #
# replace occurrences of a template with another string in a file #
# #
#######################################################################################################
replace_in_file()
(
file="$1"
t="$2"
r="$3"
tmp="$(mktmp -p "$td")"
cat "$file" >"$tmp"
sed -e "s|$t|$r|g" "$tmp" >"$file"
rm "$tmp" 2>/dev/null
)
#######################################################################################################
# #
# convert values with preceding zeros to their decimal value, any leading zero would switch the #
# meaning of following number to 'octal digit' #
# #
#######################################################################################################
get_decimal_value()
(
expr "$1" : "0*\([1-9]*[0-9]\+\)"
)
#######################################################################################################
# #
# split old style version number into parts with the new names #
# #
#######################################################################################################
split_version_number()
(
__first_char()
{
expr \( "$1" : "\(.\).*" \)
}
__remove_first_char()
{
expr \( "$1" : ".\(.*\)" \)
}
v="$1"
m="$version_parts"
s=0
while [ ${#v} -gt 0 ]; do
c="$(__first_char "$m")"
m="$(__remove_first_char "$m")"
if [ "$c" = "%" ]; then
if [ $s -eq 0 ]; then
s=1
n=""
else
s=0
fi
elif [ "$c" = "-" ] || [ -z "$(expr \( "$c" : "\([A-Za-z]\)" \) )" ]; then
if [ "$c" = "[" ]; then
o=1
elif [ "$c" = "]" ]; then
o=0
else
printf "%s=" "$n"
until [ ${#v} -eq 0 ]; do
e="$(__first_char "$v")"
v="$(__remove_first_char "$v")"
[ "$e" = "$c" ] && printf "\n" && break
printf "%s" "$e"
done
[ ${#v} -eq 0 ] && printf "\n"
fi
else
n="$n$c"
fi
done
)
#######################################################################################################
# #
# now it's time to do something useful #
# #
#######################################################################################################
# #
# prepare temporary directory and cleanup on exit #
# #
#######################################################################################################
td="$(mktmp -d)"
trap 'rm -rf "$td" 2>/dev/null' EXIT INT HUP
#######################################################################################################
# #
# let's determine, which configuration file has to be used and include it, if necessary #
# #
#######################################################################################################
if [ $__no_cfgfile -eq 0 ]; then
[ -z "$JUIS_CHECK_CFG" ] && cfg="${0}.cfg" || cfg="$JUIS_CHECK_CFG"
if [ -f "$cfg" ]; then
if __is_debug; then
__debug "$(__get_localized DBG_001)" "$(realpath "$cfg")"
__debug "$delimiter_line"
sed -e "\${/^\$/d}" "$cfg" | sed -e "s|^| |" 1>&2
__debug "$delimiter_line"
fi
[ ${#Version} -gt 0 ] && save_version="$Version" || unset save_version
unset Version
. "$cfg"
if [ ${#Version} -eq 0 ]; then
if [ ${#save_version} -gt 0 ]; then
Version="${save_version}"
unset save_version
fi
fi
if [ ${#Version} -gt 0 ]; then
spl="$(mktmp -p "$td")"
split_version_number "$Version" >"$spl"
. "$spl"
rm "$spl" 2>/dev/null
fi
__debug "$(__get_localized DBG_002)"
else
if [ -z "$1" ] && [ $__use_local -eq 0 ]; then
__emsg "$(__get_localized ERR_001)"
exit 1
fi
if [ $__use_local -eq 0 ]; then
Box="$1"
[ -z "$1" ] || shift
fi
fi
else
__debug "$(__get_localized DBG_015)"
fi
# we use the older variant with a 'Version' value here to retain backward compatibility
names="$(get_names "$common_names $serial_name" "$alt_names" 1) $flags_name $inhouse_option_name $buildtype_option_name"
inhouse_option_set=0
buildtype_option_set=0
eval $buildtype_option_name="$buildtype_option_default"
#######################################################################################################
# #
# first attempt to process name/value pairs from command line, to set variables without an existing #
# device #
# #
#######################################################################################################
for nv in $@; do
name="${nv%%=*}"
value="${nv#*=}"
valid=0
l_names="$(get_names "$common_names $serial_name" "$alt_names" 1) $flags_name $inhouse_option_name $buildtype_option_name"
for n in $l_names; do
if [ "$n" = "$name" ]; then
__debug "$(__get_localized DBG_009)" "$n" "$value"
if [ "$value" = "empty" ]; then
eval $n='$$_empty'
else
[ "$n" = "$flags_name" ] && value="$(printf "%s\n" "$value" | sed -e "s|,| |g")"
eval $n=\'$(unescape_value "$value")\'
[ "$n" = "$inhouse_option_name" ] && inhouse_option_set=1
if [ "$n" = "$buildtype_option_name" ] && ! [ "$value" = "detect" ]; then
if ! [ -z "$value" ]; then
if [ -z "$(check_buildtype_value "$value")" ]; then
__emsg "$(__get_localized ERR_017)" "$buildtype_option_name" "$value"
exit 1
else
eval $buildtype_option_name="$(check_buildtype_value "$value")"
fi
else
__emsg "$(__get_localized ERR_016)" "$buildtype_option_name"
exit 1
fi
buildtype_option_set=1
fi
fi
valid=1
break
fi
done
if [ $valid -eq 1 ] && [ "$name" = "$version_name" ]; then
spl="$(mktmp -p "$td")"
split_version_number "$value" >"$spl"
. "$spl"
rm "$spl" 2>/dev/null
fi
if [ $valid -ne 1 ] && ! [ "$name" = "$nonce_option_name" ]; then
__emsg "$(__get_localized ERR_005)" "$name"
exit 1
fi
done
#######################################################################################################
# #
# time to check mutual exclusions for variables/parameters #
# #
#######################################################################################################
if [ "$inhouse_option_set" = "1" ] && [ "$buildtype_option_set" = "1" ]; then
__emsg "$(__get_localized ERR_018)" "$buildtype_option_name" "$inhouse_option_name" "$buildtype_option_name" "$inhouse_option_name"
exit 1
fi
#######################################################################################################
# #
# check still missing variables, after the configuration file and command line were processed #
# #
#######################################################################################################
detect=0
needed_variables=""
for n in $names; do
[ "$n" = "$inhouse_option_name" ] && continue
[ "$n" = "$buildtype_option_name" ] && continue
eval var="\$$n"
if [ -z "$var" ] || [ "$var" = "detect" ]; then
detect=1
needed_variables="$needed_variables${needed_variables:+ }$n"
fi
done
#######################################################################################################
# #
# read variables from device, if needed #
# #
#######################################################################################################
if [ $detect -eq 1 ]; then
boxinfo="$(mktmp -p "$td")"
if [ $__use_local -eq 1 ]; then
if ! [ -f "$local_dir/$boxinfo1" ]; then
if ! [ -f "$local_dir/$boxinfo2" ]; then
__emsg "$(__get_localized ERR_010)" "$local_dir/$boxinfo1"
exit 3
else
__debug "$(__get_localized DBG_017)" "$local_dir/$boxinfo2"
cat "$local_dir/$boxinfo2" >"$boxinfo"
names="$(get_names "$common_names $serial_name" "$alt_names" 1) $flags_name $inhouse_option_name $buildtype_option_name"
ns="j"
replace="$boxinfo2_replace_names"
fi
else
__debug "$(__get_localized DBG_017)" "$local_dir/$boxinfo1"
cat "$local_dir/$boxinfo1" >"$boxinfo"
names="$(get_names "$common_names $serial_name" "$alt_names" 2) $flags_name $inhouse_option_name $buildtype_option_name"
ns="q"
replace="$boxinfo1_replace_names"
fi
else
if [ -z "$Box" ]; then
__emsg "$(__get_localized ERR_002)"
__emsg "$(__get_localized ERR_015)" "$needed_variables"
exit 1
fi
device_read "$Box" "$boxport" "$boxinfo1" >"$boxinfo"
if [ $? -ne 0 ]; then
__debug "$(__get_localized ERR_003)\n" "$boxinfo1" "$Box" "$boxport"
rm "$boxinfo" 2>/dev/null
device_read "$Box" "$boxport" "$boxinfo2" >"$boxinfo"
if [ $? -ne 0 ]; then
__emsg "$(__get_localized ERR_003)" "$boxinfo2" "$Box" "$boxport"
exit 3
else
names="$(get_names "$common_names $serial_name" "$alt_names" 1) $flags_name $inhouse_option_name $buildtype_option_name"
ns="j"
replace="$boxinfo2_replace_names"
fi
else
names="$(get_names "$common_names $serial_name" "$alt_names" 2) $flags_name $inhouse_option_name $buildtype_option_name"
ns="q"
replace="$boxinfo1_replace_names"
fi
fi
if ! [ -z "$replace" ]; then
for v in $replace; do
l=$(expr \( "$v" : "\([^:]*\):.*" \) )
r=$(expr \( "$v" : "[^:]*:\(.*\)" \) )
replace_in_file "$boxinfo" "$ns:$l" "$ns:$r"
done
fi
fi
#######################################################################################################
# #
# set missing variables from the read data #
# #
#######################################################################################################
for n in $names; do
eval var="\$$n"
if [ "$var" = "$$_empty" ]; then
if [ "$n" = "$inhouse_option_name" ]; then
eval ${inhouse_option_name}=1
elif [ "$n" = "$buildtype_option_name" ] && [ "$inhouse_option_set" = "0" ]; then
eval ${buildtype_option_name}="$buildtype_option_default"
else
eval $n=""
fi
elif ! [ "${var#fixed:}" = "$var" ]; then
eval $n="${var#fixed:}"
elif [ "$n" = "$inhouse_option_name" ]; then
if ! [ -z "$var" ]; then
d="$(printf -- "%s\n" "$var" | sed -e "y/AEFLRSTU/aeflrstu/")"
[ "$(printf -- "%s\n" "$d" | sed -n -e "s|^false\$|0|p")" = "0" ] && var=0
[ "$(printf -- "%s\n" "$d" | sed -n -e "s|^true\$|1|p")" = "1" ] && var=1
if ! [ "$var" = "0" ] && ! [ "$var" = "1" ]; then
__emsg "$(__get_localized ERR_004)" "$inhouse_option_name"
exit 1
else
eval $inhouse_option_name=$var
fi
else
eval $inhouse_option_name=1
fi
elif [ -z "$var" ] || [ "$var" = "detect" ]; then
val="$(extract_xml "$boxinfo" "$ns" $n)"
eval $n=\'$val\'
if [ "$n" = "$version_name" ]; then
eval $buildnumber_name="\$(extract_xml "$boxinfo" "$ns" "$buildnumber_name")"
eval $n="$version_compound"
value="$(eval printf "%s" "\$$n")"
__debug "$(__get_localized DBG_010)" "$value"
spl="$(mktmp -p "$td")"
split_version_number "$value" >"$spl"
cat "$spl" | \
while read line; do
__debug "%s\n" "$line"
done
. "$spl"
rm "$spl" 2>/dev/null
elif [ "$n" = "$buildtype_option_name" ]; then
buildtype_option_set=1
fi
else
eval $n=\'$var\'
fi
done
#######################################################################################################
# #
# process additional command line settings again, they overrule all other values #
# #
#######################################################################################################
for nv in $@; do
name="${nv%%=*}"
value="${nv#*=}"
valid=0
[ "$name" = "$inhouse_option_name" ] && continue
l_names="$(get_names "$common_names $serial_name" "$alt_names" 1) $flags_name $buildtype_option_name"
for n in $l_names; do
if [ "$n" = "$name" ]; then
if [ "$value" = "empty" ]; then
[ "$n" = "$buildtype_option_name" ] && eval ${buildtype_option_name}="$buildtype_option_default" || eval $n=''
elif ! [ "$value" = "detect" ]; then
if [ "$n" = "$buildtype_option_name" ]; then
# invalid values are detected earlier already
eval $buildtype_option_name="$(check_buildtype_value "$value")"
else
[ "$n" = "$flags_name" ] && value="$(printf "%s\n" "$value" | sed -e "s|,| |g")"
eval $n=\'$(unescape_value "$value")\'
fi
fi
valid=1
if [ "$n" = "$version_name" ]; then
# old style version number found, we have to split it
__debug "$(__get_localized DBG_010)" "$value"
spl="$(mktmp -p "$td")"
split_version_number "$value" >"$spl"
cat "$spl" | \
while read line; do
__debug "%s\n" "$line"
done
. "$spl"
rm "$spl" 2>/dev/null
fi
break
fi
done
if [ $valid -eq 0 ]; then
if [ "$name" = "$nonce_option_name" ]; then
__debug "$(__get_localized DBG_014)" "$value"
nonce="$value"
continue
fi
__emsg "$(__get_localized ERR_005)" "$name"
exit 1
fi
done
#######################################################################################################
# #
# check mutual exclusions of variables/parameters again and process '$inhouse_option_name', if needed #
# #
#######################################################################################################
if [ "$inhouse_option_set" = "1" ] && [ "$buildtype_option_set" = "1" ]; then
__emsg "$(__get_localized ERR_018)" "$buildtype_option_name" "$inhouse_option_name" "$buildtype_option_name" "$inhouse_option_name"
exit 1
elif [ "$inhouse_option_set" = "1" ]; then
if ! [ "$(eval printf "%s" "\$$inhouse_option_name")" = "0" ] && ! [ "$(eval printf "%s" "\$$inhouse_option_name")" = "1" ]; then
__emsg "$(__get_localized ERR_006)" "$inhouse_option_name"
exit 1
fi
eval $buildtype_option_name="100\$$inhouse_option_name"
fi
if [ -z "$HW" ]; then
__emsg "$(__get_localized ERR_014)"
exit 4
fi
hostname="$HW.$hostbase"
[ -z "$nonce" ] && nonce=$(get_nonce)
Flag_XML=""
for f in $Flag; do
[ ${#Flag_XML} -gt 0 ] && Flag_XML="$(printf "%s" "$Flag_XML")"
Flag_XML="$Flag_XML$f"
done
#######################################################################################################
# #
# convert values to surely decimal content #
# #
#######################################################################################################
for n in $numeric_values; do
eval v=\$$n
eval $n=$(get_decimal_value $v)
done
#######################################################################################################
# #
# decrement version number to get an answer with an URL in every case #
# #
#######################################################################################################
if [ $__get_current -eq 1 ]; then
for dec_value in $version_decrement; do
eval [ \$$dec_value -eq 0 ] && eval $dec_value=99 && continue
eval $dec_value=$(( $dec_value - 1 ))
eval $version_decrement_clear
break
done
__debug "$(__get_localized DBG_018)"
fi
eval $version_name="$version_string"
__debug "$(__get_localized DBG_011)" "$(eval printf "%s" "\$$version_name")"
#######################################################################################################
# #
# show debug info regarding final variable content #
# #
#######################################################################################################
if __is_debug; then
names="$(get_names "$common_names $serial_name" "$alt_names" 2) $flags_name $buildtype_option_name"
__debug "$delimiter_line"
__debug "$(__get_localized DBG_003)"
__debug "$delimiter_line"
for n in $names hostname nonce; do
__debug "%s=\"%s\"\n" "$n" "$(eval printf "%s" \"\$$n\")"
done
__debug "$delimiter_line"
fi
#######################################################################################################
# #
# prepare temporary files for the check request #
# #
#######################################################################################################
body="$(mktmp -p "$td")"
header="$(mktmp -p "$td")"
response="$(mktmp -p "$td")"
printf "$body_tmpl" "$nonce" "$Name" "$HW" "$Major" "$Minor" "$Patch" "$Buildnumber" "$(eval printf "%s" "\$$buildtype_option_name")" "$Serial" "$OEM" "$Lang" "$Country" "$Annex" "$Flag_XML" >>"$body"
len=$(wc -c <"$body")
printf "$headerline_0" "$juisurl" >>"$header"
printf "$headerline_1" "$hostname" "$juisport" >>"$header"
printf "$headerline_2" $len >>"$header"
printf "$headerline_3" "$contenttype" >>"$header"
printf "$headerline_4" >>"$header"
if __is_debug; then
__debug "$(__get_localized DBG_004)"
__debug "$delimiter_line"
sed -e "s|^| |" "$header" 1>&2
printf "\r\n" 1>&2
sed -e "s|^| |" "$body" 1>&2
__debug "$delimiter_line"
fi
#######################################################################################################
# #
# execute SOAP request #
# #
#######################################################################################################
__debug "$(__get_localized DBG_008)" "$hostname" "$juisport"
( cat "$header"; printf "\r\n"; cat "$body" ) | tcp_read "$hostname" "$juisport" "$response" "$remote_timeout"
if [ "$?" -ne 0 ] || ! [ -s "$response" ]; then
__emsg "$(__get_localized ERR_020)"
exit 5
else
if __is_debug; then
__debug "$(__get_localized DBG_005)"
__debug "$delimiter_line"
sed -e "s|^| |" "$response" 1>&2
printf "\n" 1>&2
__debug "$delimiter_line"
fi
fi
#######################################################################################################
# #
# examine the status code #
# #
#######################################################################################################
status="$(sed -n -e "1p" "$response" | sed -n -e "s|HTTP/1.1 \([0-9]*\) OK.*|\1|p")"
if [ -z "$status" ]; then
__emsg "$(__get_localized ERR_007)"
exit 4
elif ! [ "$status" = "200" ]; then
__emsg "$(__get_localized ERR_008)" "$status" "$hostname"
exit 4
fi
#######################################################################################################
# #
# examine the response and show the result #
# #
#######################################################################################################
xmlresp="$(mktmp -p "$td")"
# AVM's response is one single line, extraction may need a more sophisticated command, if this changes
# and a newline character is appended to make further appends easier and safer.
sed -n -e "\$p" "$response" >"$xmlresp"
printf "\n" >>"$xmlresp"
if ! [ -z "$__save_output" ]; then
exec 7>&2
exec 2>/dev/null
cat "$xmlresp" >"$__save_output"
err=$?
exec 2>&7
if [ $err -ne 0 ]; then
file="$(realpath "$__save_output" 2>/dev/null)"
[ -z "$file" ] && file="$__save_output"
__emsg "$(__get_localized ERR_012)" "$file"
exit 1
else
__debug "$(__get_localized DBG_016)" "$(realpath "$__save_output")"
fi
fi
if [ "$__show_response" = "1" ]; then
__info "$(__get_localized INF_002)"
[ -z "$XML_LINTER" ] && XML_LINTER="xmllint --format \"%s\""
linter_cmd="$XML_LINTER"
[ -z "$(expr "$linter_cmd" : ".*\(%s\).*")" ] && linter_cmd="$linter_cmd \"%s\""
linter="$(set -- $XML_LINTER && printf "%s" "$1")"
if __check_required_command "$linter"; then
eval "$(printf "$linter_cmd" "$xmlresp")" 1>&2
else
cat "$xmlresp" 1>&2
fi
fi
found="$(extract_xml "$xmlresp" "ns3" "Found")"
if ! [ "$found" = "true" ]; then
__emsg "$(__get_localized ERR_009)" "$Version"
exit 2
fi
URL="$(extract_xml "$xmlresp" "ns3" "DownloadURL")"
delay="$(sed -n -e "s|^Download-Delay: \([0-9]*\)|\1|p" "$response")"
Version="$(extract_xml "$xmlresp" "ns3" "Version")"
__info "$(__get_localized INF_001)" "$Version"
printf "URL=%s\n" "$URL"
[ -z "$delay" ] || printf "DelayDownload=%s\n" "$delay"
[ $__show_new_version -eq 1 ] && printf "NewVersion=\"%s\"\n" "$Version"
#######################################################################################################
# #
# end of script #
# #
#######################################################################################################
exit 0