Compare commits

..
1 Commits
Author SHA1 Message Date
adminandClaude Opus 5 c59e7fb393 Sicherheit: AJAX-Endpunkte und Statistik nur mit Anmeldung
Keiner der AJAX-Endpunkte hat einen Login geprueft. Ohne Anmeldung liessen
sich Kunden, Adressen und Rechnungen durchsuchen, und ajax/autosave.php
setzt $_POST ungeprueft in SQL (SQL-Injection ohne Login).

- incs/connectmysql.php und ajax/auto/connectmysql.php, die alle
  AJAX-Endpunkte laden, antworten ohne gueltige Session mit 403
- stats.php verlangt eine Anmeldung
- db-backup.php und clean_db.php laufen nur noch per Kommandozeile;
  ueber den Webserver konnte jeder einen Datenbank-Dump anstossen

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 10:30:30 +02:00
22 changed files with 208 additions and 1298 deletions
-2
View File
@@ -11,8 +11,6 @@
# --- Datenbank-Dumps / Backups --------------------------------- # --- Datenbank-Dumps / Backups ---------------------------------
*.sql *.sql
*.sql.gz *.sql.gz
# Ausnahme: das Schema selbst enthaelt keine Daten
!update/schema.sql
*.dump *.dump
*.bak *.bak
-2
View File
@@ -84,8 +84,6 @@ Eine Schemaänderung besteht also aus: neuer Eintrag **vorn** in `update/version
## Datenbankzugriff ## Datenbankzugriff
**Umbau läuft (Branch `db-umbau`):** Verbindungen entstehen nur noch über `db_verbinden()` aus `incs/db.php`. Die Funktion setzt `utf8mb4`, den strikten `sql_mode` und schaltet die mysqli-Exceptions ab. Neue und umgebaute Abfragen nutzen `db_abfrage()`, `db_zeilen()`, `db_zeile()` und `db_wert()` mit `?`-Platzhaltern statt String-Verkettung. Ziel-Schema ist das der Instanz `kunden` in `utf8mb4`; die Altdaten sind teils doppelt kodiert und werden per Migration repariert. Dieser Stand darf nur zusammen mit der migrierten Datenbank live gehen.
Prozedurales `mysqli`. `$dbconn` wird zusätzlich in `$_SESSION["dbconn"]` abgelegt, damit `incs/mysql_ecape_wrapper.php` eine globale Funktion `mysql_escape_string()` als Shim über diese Verbindung bereitstellen kann — ein Überbleibsel der `mysql_*`-Ära, das in altem Code noch aufgerufen wird. Prozedurales `mysqli`. `$dbconn` wird zusätzlich in `$_SESSION["dbconn"]` abgelegt, damit `incs/mysql_ecape_wrapper.php` eine globale Funktion `mysql_escape_string()` als Shim über diese Verbindung bereitstellen kann — ein Überbleibsel der `mysql_*`-Ära, das in altem Code noch aufgerufen wird.
SQL wird durchgehend per String-Verkettung gebaut. Escaping ist Handarbeit über `mysqli_real_escape_string($dbconn, …)` und muss bei jeder Änderung mitgedacht werden. SQL wird durchgehend per String-Verkettung gebaut. Escaping ist Handarbeit über `mysqli_real_escape_string($dbconn, …)` und muss bei jeder Änderung mitgedacht werden.
+10 -4
View File
@@ -11,15 +11,21 @@ if($settings != "valid")
if($settings == "valid") if($settings == "valid")
{ {
if(!$dbconn = db_verbinden($mysql_server, $mysql_user, $mysql_pw, $mysql_db)) if(!$dbconn = @mysqli_connect($mysql_server, $mysql_user, $mysql_pw))
$err = error("Verbindung zur Datenbank fehlgeschlagen: ".htmlspecialchars(mysqli_connect_error())); $err = error("Verbindung zur Datenbank fehlgeschlagen!!");
if(!@mysqli_select_db($dbconn,$mysql_db))
$err = error("Datenbank nicht gefunden!! ($mysql_db)");
} }
// Wie sites/userkeys.php: nur Superadmins dürfen Passkeys vergeben // Wie sites/userkeys.php: nur Superadmins dürfen Passkeys vergeben
if($_SESSION["login"] == "valid" && isset($_GET["id"]) && ((int)substr($_SESSION["rights"], RIGHTS_SUPERADMIN, 1) & RIGHTS_CHANGE)){ if($_SESSION["login"] == "valid" && isset($_GET["id"]) && ((int)substr($_SESSION["rights"], RIGHTS_SUPERADMIN, 1) & RIGHTS_CHANGE)){
db_abfrage($dbconn, "DELETE FROM addKey WHERE datetime < DATE_SUB(NOW(), INTERVAL 1 MINUTE)") or print(mysqli_error($dbconn)); if(!mysqli_query($dbconn,"DELETE FROM addKey WHERE datetime < DATE_SUB(NOW(), INTERVAL 1 MINUTE);")){
echo mysqli_error($dbconn);
}
if(isLocal()){ if(isLocal()){
$accessKey = strval(random_int(0,99999999)); $accessKey = strval(random_int(0,99999999));
db_abfrage($dbconn, "INSERT INTO addKey SET accesskey = ?, datetime = NOW()", array($accessKey)) or print(mysqli_error($dbconn)); if(!mysqli_query($dbconn,"INSERT INTO addKey SET accesskey=".$accessKey.", datetime=NOW();")){
echo mysqli_error($dbconn);
}
header('Location: https://wm.el-wa.org/kunden?action=userkeys&id='.(int)$_GET["id"].'&addKey='.$accessKey); header('Location: https://wm.el-wa.org/kunden?action=userkeys&id='.(int)$_GET["id"].'&addKey='.$accessKey);
echo 'https://wm.el-wa.org/kunden?action=userkeys&id='.(int)$_GET["id"].'&addKey='.$accessKey; echo 'https://wm.el-wa.org/kunden?action=userkeys&id='.(int)$_GET["id"].'&addKey='.$accessKey;
}else{ }else{
+8 -3
View File
@@ -2,14 +2,19 @@
require_once("../../incs/constants.php"); require_once("../../incs/constants.php");
session_name(SESSION_NAME); session_name(SESSION_NAME);
session_start(); session_start();
// Alle AJAX-Endpunkte laden diese Datei: ohne Anmeldung gibt es keine Daten.
if(($_SESSION["login"] ?? "") !== "valid")
{
http_response_code(403);
exit("Nicht angemeldet.");
}
require_once("../../incs/settings.php"); require_once("../../incs/settings.php");
$settings=readsettings(); $settings=readsettings();
if($settings == "valid") if($settings == "valid")
{ {
$dbconn = db_verbinden($mysql_server, $mysql_user, $mysql_pw, $mysql_db); $dbconn = mysqli_connect($mysql_server, $mysql_user, $mysql_pw);
if(!$dbconn) mysqli_select_db($dbconn,$mysql_db);
$err = 1;
} }
else else
$err = 1; $err = 1;
+1 -1
View File
@@ -123,7 +123,7 @@ if(!isset($err))
//OLD SKS RECHNUNGEN!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //OLD SKS RECHNUNGEN!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if(getUserSetting($dbconn,"show_sks", $_SESSION["user"], 1)) //show sks rechnungen???? if(getUserSetting($dbconn,"show_sks", mysql_escape_string($_SESSION["user"]), 1)) //show sks rechnungen????
{ {
echo "<h3>SKS Dokumente:</h3><div class='line'></div>"; echo "<h3>SKS Dokumente:</h3><div class='line'></div>";
$queries=""; $queries="";
+1 -1
View File
@@ -118,7 +118,7 @@ if(!isset($err))
//OLD SKS RECHNUNGEN!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //OLD SKS RECHNUNGEN!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if(getUserSetting($dbconn,"show_sks", $_SESSION["user"], 1)) //show sks rechnungen???? if(getUserSetting($dbconn,"show_sks", mysql_escape_string($_SESSION["user"]), 1)) //show sks rechnungen????
{ {
echo "<h3>SKS Dokumente:</h3><div class='line'></div>"; echo "<h3>SKS Dokumente:</h3><div class='line'></div>";
$queries = createRechQry($skspage,$pagesize,$src,$combiner,1); $queries = createRechQry($skspage,$pagesize,$src,$combiner,1);
+27 -22
View File
@@ -50,8 +50,10 @@ try {
if($settings == "valid") if($settings == "valid")
{ {
if(!$dbconn = db_verbinden($mysql_server, $mysql_user, $mysql_pw, $mysql_db)) if(!$dbconn = @mysqli_connect($mysql_server, $mysql_user, $mysql_pw))
$err = error("Verbindung zur Datenbank fehlgeschlagen: ".htmlspecialchars(mysqli_connect_error())); $err = error("Verbindung zur Datenbank fehlgeschlagen!!");
if(!@mysqli_select_db($dbconn,$mysql_db))
$err = error("Datenbank nicht gefunden!! ($mysql_db)");
} }
// read get argument and post body // read get argument and post body
$fn = filter_input(INPUT_GET, 'fn'); $fn = filter_input(INPUT_GET, 'fn');
@@ -139,20 +141,22 @@ try {
// ------------------------------------ // ------------------------------------
} else if ($fn === 'getGetArgs') { } else if ($fn === 'getGetArgs') {
$userId = db_wert($dbconn, "SELECT id FROM user WHERE nick = ?", array((string)filter_input(INPUT_GET, 'user'))); $result = mysqli_query($dbconn,"SELECT id FROM user WHERE nick = '".mysqli_real_escape_string($dbconn,$_GET["user"])."';");
if ($userId === false) { if ($result->num_rows > 0) {
throw new Exception(mysqli_error($dbconn)); $row = $result->fetch_assoc();
} $userId = $row["id"];
if ($userId === null) { }else{
throw new Exception('Username not found'); throw new Exception('Username not found');
} }
$ids = []; $ids = [];
$zeilen = db_zeilen($dbconn, "SELECT credentialId FROM passkeys WHERE userId = ?", array($userId)); $result = mysqli_query($dbconn,"SELECT credentialId FROM passkeys WHERE userId = '".$userId."';");
if ($zeilen === false) { if(!$result){
throw new Exception(mysqli_error($dbconn)); $msg = "Error:<br>".mysqli_error($dbconn)."<br />";
} }
foreach ($zeilen as $row) { if ($result->num_rows > 0) {
$ids[] = base64_decode($row["credentialId"]); while($row = $result->fetch_assoc()) {
$ids[] = base64_decode($row["credentialId"]);
}
} }
if (count($ids) === 0) { if (count($ids) === 0) {
@@ -195,8 +199,7 @@ try {
if (!isset($_SESSION['registrations']) || !array_key_exists('registrations', $_SESSION) || !is_array($_SESSION['registrations'])) { if (!isset($_SESSION['registrations']) || !array_key_exists('registrations', $_SESSION) || !is_array($_SESSION['registrations'])) {
$_SESSION['registrations'] = []; $_SESSION['registrations'] = [];
}*/ }*/
if(!db_abfrage($dbconn, "INSERT INTO passkeys SET credentialId = ?, credentialPublicKey = ?, signatureCounter = ?, name = ?, userId = ?, lastAuth = NOW(), authKey = 0", if(!mysqli_query($dbconn,"INSERT INTO passkeys SET credentialId = '".base64_encode($data->credentialId)."', credentialPublicKey = '".base64_encode($data->credentialPublicKey)."', signatureCounter = '".base64_encode($data->signatureCounter)."', name = '".mysqli_real_escape_string($mysql,filter_input(INPUT_GET, 'name'))."', userId = '".(int)$passkeyFreigabe["user"]."';")){
array(base64_encode($data->credentialId), base64_encode($data->credentialPublicKey), base64_encode($data->signatureCounter), (string)filter_input(INPUT_GET, 'name'), $passkeyFreigabe["user"]))){
$msg = "Error:<br>".mysqli_error($dbconn)."<br />"; $msg = "Error:<br>".mysqli_error($dbconn)."<br />";
} }
else{ else{
@@ -235,11 +238,12 @@ try {
// looking up correspondending public key of the credential id // looking up correspondending public key of the credential id
// you should also validate that only ids of the given user name // you should also validate that only ids of the given user name
// are taken for the login. // are taken for the login.
$row = db_zeile($dbconn, "SELECT credentialPublicKey, userId, name FROM passkeys WHERE credentialId = ?", array(base64_encode((string)$id))); $result = mysqli_query($dbconn,"SELECT credentialPublicKey, userId, name FROM passkeys WHERE credentialId = '".base64_encode($id)."';");
if ($row === false) { if(!$result){
throw new Exception(mysqli_error($dbconn)); $msg = "Error:<br>".mysqli_error($dbconn)."<br />";
} }
if ($row) { if ($result->num_rows > 0) {
$row = $result->fetch_assoc();
$userId = $row["userId"]; $userId = $row["userId"];
$credentialPublicKey = base64_decode($row["credentialPublicKey"]); $credentialPublicKey = base64_decode($row["credentialPublicKey"]);
$reg = (object) ['userId' => base64_decode($row["userId"])]; $reg = (object) ['userId' => base64_decode($row["userId"])];
@@ -268,15 +272,16 @@ try {
$authKey = strval(random_int(0,99999999)); $authKey = strval(random_int(0,99999999));
db_abfrage($dbconn, "UPDATE passkeys SET authKey = ?, lastAuth = NOW() WHERE credentialId = ?", array($authKey, base64_encode($id))); $result = mysqli_query($dbconn,"UPDATE passkeys SET authKey=".$authKey.", lastAuth=NOW() WHERE credentialId = '".base64_encode($id)."';");
// Gesperrte Benutzer bleiben gesperrt, auch mit gültigem Passkey // Gesperrte Benutzer bleiben gesperrt, auch mit gültigem Passkey
$udata = db_zeile($dbconn, "SELECT * FROM user WHERE id = ? AND blocked = 0", array($userId)); $qry= mysqli_query($dbconn, "SELECT * FROM user WHERE id = '".mysqli_real_escape_string($dbconn,$userId)."' AND blocked = 0");
if(!$udata) if(!$qry || mysqli_num_rows($qry) != 1)
{ {
throw new Exception('Benutzer nicht gefunden oder gesperrt.'); throw new Exception('Benutzer nicht gefunden oder gesperrt.');
} }
else else
{ {
$udata = mysqli_fetch_assoc($qry);
session_regenerate_id(true); session_regenerate_id(true);
$_SESSION["login"] = "valid"; $_SESSION["login"] = "valid";
$_SESSION["logedout"] = $udata["logedout"]; $_SESSION["logedout"] = $udata["logedout"];
@@ -296,7 +301,7 @@ try {
$_SESSION["zoom"] = getUserSetting($dbconn,"zoom", $_SESSION["user"], 1); $_SESSION["zoom"] = getUserSetting($dbconn,"zoom", $_SESSION["user"], 1);
$_SESSION["privateNotes"] = getUserSetting($dbconn,"privnotes", $_SESSION["user"], ""); $_SESSION["privateNotes"] = getUserSetting($dbconn,"privnotes", $_SESSION["user"], "");
db_abfrage($dbconn, "UPDATE user SET wrongpw = 0, lastlogin = NOW(), numlogins = numlogins + 1, logedout = 0 WHERE id = ?", array($userId)); mysqli_query($dbconn, "UPDATE user SET wrongpw = 0, lastlogin = NOW(), numlogins = numlogins + 1, logedout = 0 WHERE id = '".mysqli_real_escape_string($dbconn,$userId)."'");
} }
$return = new stdClass(); $return = new stdClass();
$return->success = true; $return->success = true;
+5 -3
View File
@@ -1,4 +1,7 @@
<?php <?php
// Wartungsskript: nur per Kommandozeile, nie über den Webserver
if(PHP_SAPI != "cli")
exit;
require_once("./incs/constants.php"); require_once("./incs/constants.php");
session_name(SESSION_NAME); session_name(SESSION_NAME);
session_start(); session_start();
@@ -7,9 +10,8 @@ $settings=readsettings();
if($settings == "valid") if($settings == "valid")
{ {
$dbconn = db_verbinden($mysql_server, $mysql_user, $mysql_pw, $mysql_db); $dbconn = mysqli_connect($mysql_server, $mysql_user, $mysql_pw);
if(!$dbconn) mysqli_select_db($dbconn,$mysql_db);
$err = 1;
} }
else else
$err = 1; $err = 1;
+3
View File
@@ -1,4 +1,7 @@
<?php <?php
// Nur per Kommandozeile (Cronjob), nie über den Webserver
if(PHP_SAPI != "cli")
exit;
/* /*
* Datenbank-Backup (Cronjob). * Datenbank-Backup (Cronjob).
* *
+36 -10
View File
@@ -8,8 +8,10 @@ if($settings != "valid")
if($settings == "valid") if($settings == "valid")
{ {
if(!$mysql = db_verbinden($mysql_server, $mysql_user, $mysql_pw, $mysql_db)) if(!$mysql = @mysqli_connect($mysql_server, $mysql_user, $mysql_pw))
$err = error("Verbindung zur Datenbank fehlgeschlagen: ".htmlspecialchars(mysqli_connect_error())); $err = error("Verbindung zur Datenbank fehlgeschlagen!!");
if(!@mysqli_select_db($mysql,$mysql_db))
$err = error("Datenbank nicht gefunden!! ($mysql_db)");
} }
$_SESSION["local"] =false; $_SESSION["local"] =false;
@@ -84,17 +86,41 @@ function isLocal(){
return $lokal; return $lokal;
} }
/* Ist $_GET["addKey"] eine gültige, höchstens eine Minute alte Freigabe? */ function checkLogin(){
global $mysql;
//$mysql = new mysqli($GLOBALS["mysql_server"],$GLOBALS["mysql_user"],$GLOBALS["mysql_pass"],$GLOBALS["mysql_db"]);
if(isLocal()){
$_SESSION["local"] =true;
return true;
}
if(isset($_SESSION["authKey"])){
$res = mysqli_query($mysql,"SELECT id FROM users WHERE lastAuth > DATE_SUB(NOW(), INTERVAL 2 DAY) AND authKey = '".mysqli_real_escape_string($mysql,$_SESSION["authKey"])."' AND name = '".mysqli_real_escape_string($mysql,$_SESSION["user"])."';");
if(!$res){
echo mysqli_error($mysql);
return false;
}
if(mysqli_num_rows($res) == 1){
return isset($_SESSION["Logged"]);
}
}else{
return false;
}
}
function checkAdduser(){ function checkAdduser(){
global $mysql; global $mysql;
db_abfrage($mysql, "DELETE FROM addKey WHERE datetime < DATE_SUB(NOW(), INTERVAL 1 MINUTE)") or print(mysqli_error($mysql)); //$mysql = new mysqli($GLOBALS["mysql_server"],$GLOBALS["mysql_user"],$GLOBALS["mysql_pass"],$GLOBALS["mysql_db"]);
// Nur reine Ziffern: accesskey ist eine Zahlenspalte, MariaDB würde if(!mysqli_query($mysql,"DELETE FROM addKey WHERE datetime < DATE_SUB(NOW(), INTERVAL 1 MINUTE);")){
// "12345678xyz" beim Vergleich stillschweigend zu 12345678 machen. echo mysqli_error($mysql);
$key = $_GET["addKey"] ?? ""; }
if(!is_string($key) || !ctype_digit($key)) $result = mysqli_query($mysql,"SELECT * FROM addKey WHERE accesskey='".mysqli_real_escape_string($mysql,$_GET["addKey"])."';");
if(!$result){
return false; return false;
$anzahl = db_wert($mysql, "SELECT COUNT(*) FROM addKey WHERE accesskey = ?", array((int)$key)); }
return $anzahl > 0; if ($result->num_rows > 0) {
return true;
}
return false;
} }
?> ?>
+8 -3
View File
@@ -2,14 +2,19 @@
require_once("../incs/constants.php"); require_once("../incs/constants.php");
session_name(SESSION_NAME); session_name(SESSION_NAME);
session_start(); session_start();
// Alle AJAX-Endpunkte laden diese Datei: ohne Anmeldung gibt es keine Daten.
if(($_SESSION["login"] ?? "") !== "valid")
{
http_response_code(403);
exit("Nicht angemeldet.");
}
require_once("../incs/settings.php"); require_once("../incs/settings.php");
$settings=readsettings(); $settings=readsettings();
if($settings == "valid") if($settings == "valid")
{ {
$dbconn = db_verbinden($mysql_server, $mysql_user, $mysql_pw, $mysql_db); $dbconn = mysqli_connect($mysql_server, $mysql_user, $mysql_pw);
if(!$dbconn) mysqli_select_db($dbconn,$mysql_db);
$err = 1;
} }
else else
$err = 1; $err = 1;
-79
View File
@@ -1,79 +0,0 @@
<?php
/*
* Datenbankzugriff: Verbindungsaufbau und Prepared Statements.
*
* Neue und umgebaute Abfragen laufen über db_abfrage() und Co. Werte stehen
* im SQL nur als ? und kommen getrennt als Array - sie werden nie in den
* SQL-String eingesetzt, Escaping entfällt. Tabellen- und Spaltennamen
* (z.B. für ORDER BY) lassen sich nicht binden und müssen gegen eine feste
* Liste erlaubter Werte geprüft werden.
*
* Fehler verhalten sich wie bei mysqli_query(): Rückgabe false, Details über
* mysqli_error($dbconn). Das bestehende Muster
* db_abfrage(...) or $err = error(...mysqli_error($dbconn));
* funktioniert also unverändert.
*/
/*
* Baut die Verbindung auf und stellt sie einheitlich ein. Gibt die
* Verbindung zurück oder false; der Grund steht dann in
* mysqli_connect_error().
*/
function db_verbinden($server, $user, $pw, $db)
{
// Seit PHP 8.1 wirft mysqli standardmäßig Exceptions. Der Code prüft
// Rückgabewerte, also das alte Verhalten wiederherstellen.
mysqli_report(MYSQLI_REPORT_OFF);
$dbconn = @mysqli_connect($server, $user, $pw, $db);
if(!$dbconn)
return false;
// Ohne das behandelt der Server die Verbindung je nach Einstellung als
// latin1, und UTF-8 aus dem Browser wird doppelt kodiert gespeichert.
mysqli_set_charset($dbconn, "utf8mb4");
// Unabhängig von der Server-Konfiguration immer strikt: ungültige Werte
// und fehlende Pflichtfelder sind Fehler, statt still zu '' oder 0 zu
// werden.
mysqli_query($dbconn, "SET SESSION sql_mode = 'STRICT_ALL_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'");
return $dbconn;
}
/*
* Führt eine Abfrage mit ?-Platzhaltern aus. Gibt bei SELECT ein
* mysqli_result zurück, sonst true, bei Fehlern false.
*/
function db_abfrage($dbconn, $sql, $params = array())
{
return mysqli_execute_query($dbconn, $sql, array_values($params));
}
/* Alle Zeilen als Liste assoziativer Arrays, bei Fehlern false. */
function db_zeilen($dbconn, $sql, $params = array())
{
$res = db_abfrage($dbconn, $sql, $params);
if(!$res)
return false;
return mysqli_fetch_all($res, MYSQLI_ASSOC);
}
/* Erste Zeile als assoziatives Array, null ohne Treffer, bei Fehlern false. */
function db_zeile($dbconn, $sql, $params = array())
{
$res = db_abfrage($dbconn, $sql, $params);
if(!$res)
return false;
return mysqli_fetch_assoc($res);
}
/* Erste Spalte der ersten Zeile, null ohne Treffer, bei Fehlern false. */
function db_wert($dbconn, $sql, $params = array())
{
$res = db_abfrage($dbconn, $sql, $params);
if(!$res)
return false;
$zeile = mysqli_fetch_row($res);
return $zeile === null ? null : $zeile[0];
}
+17 -15
View File
@@ -1,6 +1,5 @@
<?php <?php
include_once("constants.php"); include_once("constants.php");
require_once(__DIR__."/db.php");
function readsettings() function readsettings()
{ {
if(is_file(CONFIG_PATH.CONFIG_FILE)) if(is_file(CONFIG_PATH.CONFIG_FILE))
@@ -18,27 +17,30 @@ function readsettings()
} }
/*
* Benutzereinstellungen. Werte und ids roh übergeben - nicht escapen, das
* erledigen die Platzhalter.
*/
function getUserSetting($dbconn,$setting, $user, $default = 0) function getUserSetting($dbconn,$setting, $user, $default = 0)
{ {
$wert = db_wert($dbconn, "SELECT val FROM setting WHERE opt = ? AND user_id = ?", array($setting, $user)); $qry2 = mysqli_query($dbconn, "SELECT val FROM setting WHERE opt = '".mysqli_real_escape_string($dbconn, $setting)."' AND user_id = '".mysqli_real_escape_string($dbconn, $user)."'") or die(mysqli_error($dbconn));
if($wert === false) if($res = mysqli_fetch_array($qry2))
die(mysqli_error($dbconn)); $ret = $res[0];
return $wert === null ? $default : $wert; else
$ret = $default;
return $ret;
} }
function updateUserSetting($dbconn,$setting, $value, $user) function updateUserSetting($dbconn,$setting, $value, $user)
{ {
$id = db_wert($dbconn, "SELECT id FROM setting WHERE opt = ? AND user_id = ?", array($setting, $user)); $qry = mysqli_query($dbconn,"SELECT id FROM setting WHERE opt = '".mysqli_real_escape_string($dbconn, $setting)."' AND user_id = '".mysqli_real_escape_string($dbconn, $user)."'") or die(mysqli_error($dbconn));
if($id === false) if(mysqli_num_rows($qry))
die(mysqli_error($dbconn)); {
if($id !== null) $res = mysqli_fetch_array($qry);
db_abfrage($dbconn, "UPDATE setting SET val = ? WHERE id = ?", array($value, $id)) or die(mysqli_error($dbconn)); mysqli_query($dbconn, "UPDATE setting SET val = '".mysqli_real_escape_string($dbconn, $value)."' WHERE id = ".$res[0]) or die($mysqli_error($dbconn));
echo "update";
}
else else
db_abfrage($dbconn, "INSERT INTO setting SET val = ?, user_id = ?, opt = ?", array($value, $user, $setting)) or die(mysqli_error($dbconn)); {
mysqli_query($dbconn, "INSERT INTO setting SET val = '".mysqli_real_escape_string($dbconn, $value)."', user_id = '".mysqli_real_escape_string($dbconn, $user)."', opt = '".mysqli_real_escape_string($setting)."'") or die(mysqli_error($dbconn));
}
} }
+4 -2
View File
@@ -56,8 +56,10 @@ else if($action == "setup")
if($settings == "valid" && $action != "setup") if($settings == "valid" && $action != "setup")
{ {
if(!$dbconn = db_verbinden($mysql_server, $mysql_user, $mysql_pw, $mysql_db)) if(!$dbconn = @mysqli_connect($mysql_server, $mysql_user, $mysql_pw))
$err = error("Verbindung zur Datenbank fehlgeschlagen: ".htmlspecialchars(mysqli_connect_error())); $err = error("Verbindung zur Datenbank fehlgeschlagen!!");
if(!@mysqli_select_db($dbconn,$mysql_db))
$err = error("Datenbank nicht gefunden!! ($mysql_db)");
$newversion = checknewversion($version); $newversion = checknewversion($version);
$status = manageupdate($version, $newversion); $status = manageupdate($version, $newversion);
$version = $status[1]; $version = $status[1];
+4 -5
View File
@@ -76,13 +76,12 @@ else
{ {
if($_GET["formsent"] == 1) if($_GET["formsent"] == 1)
{ {
// nur Dateien direkt in styles/, kein ../ if(is_file("./styles/".$_POST["style"]))
if(basename($_POST["style"]) === $_POST["style"] && is_file("./styles/".$_POST["style"]))
{ {
updateUserSetting($dbconn,"style", $_POST["style"], $_SESSION["user"]); updateUserSetting($dbconn,"style", mysqli_real_escape_string($dbconn,$_POST["style"]), mysqli_real_escape_string($dbconn,$_SESSION["user"]));
$_SESSION["style"] = $_POST["style"]; $_SESSION["style"] = $_POST["style"];
} }
updateUserSetting($dbconn,"show_sks", (int)$_POST["show_sks"], $_SESSION["user"]); updateUserSetting($dbconn,"show_sks", mysqi_real_escape_string($dbconn,$_POST["show_sks"]), mysqli_real_escape_string($dbconn,$_SESSION["user"]));
} }
if(substr($_SESSION["rights"],RIGHTS_SUPERADMIN,1) & RIGHTS_CHANGE || substr($_SESSION["rights"],RIGHTS_SUPERADMIN,1) & RIGHTS_ADD) if(substr($_SESSION["rights"],RIGHTS_SUPERADMIN,1) & RIGHTS_CHANGE || substr($_SESSION["rights"],RIGHTS_SUPERADMIN,1) & RIGHTS_ADD)
$smarty->assign("superadmin","add"); $smarty->assign("superadmin","add");
@@ -130,7 +129,7 @@ else
} }
$show_sks = getUserSetting($dbconn,"show_sks", $_SESSION["user"], 1); $show_sks = getUserSetting($dbconn,"show_sks", mysqli_real_escape_string($dbconn,$_SESSION["user"]), 1);
$smarty->assign("show_sks",$show_sks); $smarty->assign("show_sks",$show_sks);
$smarty->assign("styles",$styles); $smarty->assign("styles",$styles);
+58 -42
View File
@@ -1,57 +1,73 @@
<?php <?php
if(!isset($_POST["user"]))
$_POST["user"] = "";
else
$_POST["user"] = htmlspecialchars($_POST["user"]);
if(!isset($_POST["pass"]))
$_POST["pass"] = "";
else
$_POST["pass"] = htmlspecialchars($_POST["pass"]);
$smarty->display("heading.tpl"); $smarty->display("heading.tpl");
$smarty->assign("local",isLocal()); $smarty->assign("local",isLocal());
if(isset($_GET["formsent"]) && $_SESSION["login"] != "valid" && isLocal()) if(isset($_GET["formsent"]) && $_SESSION["login"] != "valid" && isLocal())
{ {
// Eingaben unverändert verwenden: Passwörter werden beim Anlegen roh $qry= mysqli_query($dbconn, "SELECT salt FROM user WHERE LOWER(nick) = '".mysql_escape_string(strtolower($_POST["user"]))."' AND blocked = 0") or error("Es ist ein Fehler in der abfrage der Benutzerdaten: <br />".formaterror(mysqli_error($dbconn)));
// gehasht, ein htmlspecialchars() hier ließ Passwörter mit & < > " ' if(mysqli_num_rows($qry) == 1)
// nie passen. Ausgabe-Escaping übernimmt das Template.
$nick = isset($_POST["user"]) && is_string($_POST["user"]) ? $_POST["user"] : "";
$pass = isset($_POST["pass"]) && is_string($_POST["pass"]) ? $_POST["pass"] : "";
$udata = db_zeile($dbconn, "SELECT * FROM user WHERE nick = ? AND blocked = 0", array($nick));
if($udata === false)
error("Es ist ein Fehler in der abfrage der Benutzerdaten: <br />".formaterror(mysqli_error($dbconn)));
elseif($udata === null)
error("Dieser Benutzername existiert nicht.");
elseif(hash_equals($udata["pass"], crypt($pass, $udata["salt"])))
{ {
// neue Session-ID nach dem Login, sonst ist Session-Fixation möglich $res = mysqli_fetch_array($qry);
session_regenerate_id(true); $qry= mysqli_query($dbconn, "SELECT * FROM user WHERE nick = '".mysql_escape_string($_POST["user"])."' AND pass = '".crypt($_POST["pass"],$res[0])."'") or error("Es ist ein Fehler in der abfrage der Benutzerdaten: <br />".formaterror(mysqli_error($dbconn)));
$_SESSION["login"] = "valid"; if(mysqli_num_rows($qry) == 1)
$_SESSION["logedout"] = $udata["logedout"];
$_SESSION["user"] = $udata["id"];
$_SESSION["nick"] = $udata["nick"];
$_SESSION["vname"] = $udata["vname"];
$_SESSION["nname"] = $udata["nname"];
$_SESSION["rights"] = $udata["rights"];
$_SESSION["lastlogin"] = $udata["lastlogin"];
$_SESSION["numlogins"] = $udata["numlogins"];
$_SESSION["style"] = getUserSetting($dbconn,"style", $udata["id"]);
if(!is_file("styles/".$_SESSION["style"]))
{ {
updateUserSetting($dbconn,"style", "default.css", $udata["id"]); $udata = mysqli_fetch_assoc($qry);
// neue Session-ID nach dem Login, sonst ist Session-Fixation möglich
session_regenerate_id(true);
$_SESSION["login"] = "valid";
$_SESSION["logedout"] = $udata["logedout"];
$_SESSION["user"] = $udata["id"];
$_SESSION["nick"] = $udata["nick"];
$_SESSION["vname"] = $udata["vname"];
$_SESSION["nname"] = $udata["nname"];
$_SESSION["rights"] = $udata["rights"];
$_SESSION["lastlogin"] = $udata["lastlogin"];
$_SESSION["numlogins"] = $udata["numlogins"];
$_SESSION["style"] = getUserSetting($dbconn,"style", $udata["id"]); $_SESSION["style"] = getUserSetting($dbconn,"style", $udata["id"]);
} if(!is_file("styles/".$_SESSION["style"]))
$_SESSION["zoom"] = getUserSetting($dbconn,"zoom", $_SESSION["user"], 1); {
$_SESSION["privateNotes"] = getUserSetting($dbconn,"privnotes", $_SESSION["user"], ""); updateUserSetting($dbconn,"style", "default.css", $udata["id"]);
$_SESSION["style"] = getUserSetting($dbconn,"style", $udata["id"]);
}
$_SESSION["zoom"] = getUserSetting($dbconn,"zoom", $_SESSION["user"], 1);
$_SESSION["privateNotes"] = getUserSetting($dbconn,"privnotes", $_SESSION["user"], "");
db_abfrage($dbconn, "UPDATE user SET wrongpw = 0, lastlogin = NOW(), numlogins = numlogins + 1, logedout = 0 WHERE id = ?", array($udata["id"])); mysqli_query($dbconn, "UPDATE user SET wrongpw = 0, lastlogin = NOW(), numlogins = numlogins + 1, logedout = 0 WHERE nick = '".mysql_escape_string($_POST["user"])."'");
} }
elseif($udata["wrongpw"] < 10)
{
db_abfrage($dbconn, "UPDATE user SET wrongpw = wrongpw + 1 WHERE id = ?", array($udata["id"]));
$triesleft = 10 - $udata["wrongpw"];
if($triesleft > 1)
error("Der Benuzername oder das Passwort sind falsch, bitte versuchen Sie es erneut (Nach weiteren $triesleft Versuchen wird der Benutzer deaktiviert und muss vom Hauptbenutzer wieder aktiviert werden, damit er sich anmelden kann.)");
else else
error("Der Benuzername oder das Passwort sind falsch, bitte versuchen Sie es erneut (Wenn der nächste Versuch falsch ist, wird der Benutzer für deaktiviert und muss vom Hauptbenutzer wieder aktiviert werden, damit er sich anmelden kann.)"); {
$qry= mysqli_query($dbconn, "SELECT wrongpw FROM user WHERE nick = '".mysql_escape_string($_POST["user"])."'") or error("Es ist ein Fehler in der abfrage der Benutzerdaten: <br />".formaterror(mysqli_error($dbconn)));;
if(mysqli_num_rows($qry))
{
$wrongpw = mysqli_fetch_array($qry);
if($wrongpw[0] < 10)
{
mysqli_query($dbconn, "UPDATE user SET wrongpw = wrongpw + 1 WHERE nick = '".mysql_escape_string($_POST["user"])."'");
$triesleft = 10 - $wrongpw[0];
if($triesleft > 1)
error("Der Benuzername oder das Passwort sind falsch, bitte versuchen Sie es erneut (Nach weiteren $triesleft Versuchen wird der Benutzer deaktiviert und muss vom Hauptbenutzer wieder aktiviert werden, damit er sich anmelden kann.)");
else
error("Der Benuzername oder das Passwort sind falsch, bitte versuchen Sie es erneut (Wenn der nächste Versuch falsch ist, wird der Benutzer für deaktiviert und muss vom Hauptbenutzer wieder aktiviert werden, damit er sich anmelden kann.)");
}
else
{
mysqli_query($dbconn, "UPDATE user SET blocked = 1 WHERE nick = '".mysql_escape_string($_POST["user"])."'");
error("Der Benuzer wurde deaktiviert, er muss vom Hauptbenutzer wieder aktiviert werden, damit er sich anmelden kann.");
}
}
}
} }
else else
{ error("Dieser Benutzername existiert nicht.");
db_abfrage($dbconn, "UPDATE user SET blocked = 1 WHERE id = ?", array($udata["id"]));
error("Der Benuzer wurde deaktiviert, er muss vom Hauptbenutzer wieder aktiviert werden, damit er sich anmelden kann.");
}
} }
if($_SESSION["login"]=="valid"){ if($_SESSION["login"]=="valid"){
header('Location:?action=kunden'); header('Location:?action=kunden');
+8 -7
View File
@@ -7,7 +7,7 @@ if(isset($_GET["id"]) && isset($_GET["addKey"])){
// in dieser Session (geprüft in authServer.php). Der addKey ist damit // in dieser Session (geprüft in authServer.php). Der addKey ist damit
// verbraucht. // verbraucht.
$_SESSION["passkey_freigabe"] = array("user" => (int)$_GET["id"], "bis" => time() + 300); $_SESSION["passkey_freigabe"] = array("user" => (int)$_GET["id"], "bis" => time() + 300);
db_abfrage($mysql, "DELETE FROM addKey WHERE accesskey = ?", array((int)$_GET["addKey"])); mysqli_query($mysql, "DELETE FROM addKey WHERE accesskey = '".mysqli_real_escape_string($mysql, $_GET["addKey"])."'");
echo <<<ENDE echo <<<ENDE
<html> <html>
<head></head> <head></head>
@@ -78,14 +78,15 @@ if(isset($_GET["id"]) && isset($_GET["addKey"])){
$smarty->assign("heading","Passkeys für externen Zugang verwalten"); $smarty->assign("heading","Passkeys für externen Zugang verwalten");
$smarty->display("heading.tpl"); $smarty->display("heading.tpl");
$row = db_zeile($dbconn, "SELECT id, vname, nname FROM user WHERE blocked = 0 AND user.id = ?", array($_GET["id"])); $qry = mysqli_query($_SESSION["dbconn"],"SELECT id, vname, nname FROM user WHERE blocked = 0 AND user.id = ".(int)$_GET["id"]) or die("Datenbank fehler: ".mysqli_error($_SESSION["dbconn"]));
if($row === false) $row=mysqli_fetch_array($qry);
die("Datenbank fehler: ".mysqli_error($dbconn));
$smarty->assign("user",$row); $smarty->assign("user",$row);
$keys = db_zeilen($dbconn, "SELECT passkeys.id, passkeys.name FROM user JOIN passkeys ON user.id = passkeys.userId WHERE blocked = 0 AND user.id = ?", array($_GET["id"])); $qry = mysqli_query($_SESSION["dbconn"],"SELECT passkeys.id , passkeys.name FROM user JOIN passkeys ON user.id = passkeys.userId WHERE blocked = 0 AND user.id = ".(int)$_GET["id"]) or die("Datenbank fehler: ".mysqli_error($_SESSION["dbconn"]));
if($keys === false) $keys = array();
die("Datenbank fehler: ".mysqli_error($dbconn)); while($row=mysqli_fetch_array($qry)){
$keys[] = $row;
}
$smarty->assign("keys",$keys); $smarty->assign("keys",$keys);
$smarty->assign("local",isLocal()); $smarty->assign("local",isLocal());
$smarty->display("userkeys.tpl"); $smarty->display("userkeys.tpl");
+10 -1
View File
@@ -1,4 +1,12 @@
<?php <?php
require_once("incs/constants.php");
session_name(SESSION_NAME);
session_start();
if((($_SESSION["login"] ?? "") !== "valid"))
{
http_response_code(403);
exit("Nicht angemeldet.");
}
error_reporting(E_ERROR | E_WARNING | E_PARSE); error_reporting(E_ERROR | E_WARNING | E_PARSE);
// Standard inclusions // Standard inclusions
@@ -9,7 +17,8 @@
// Zugang zur Statistik-Datenbank steht in der externen Config // Zugang zur Statistik-Datenbank steht in der externen Config
require_once("incs/settings.php"); require_once("incs/settings.php");
readsettings(); readsettings();
$dbconn = db_verbinden($stats_mysql_server, $stats_mysql_user, $stats_mysql_pw, $stats_mysql_db) or die("Keine Verbindung zur Statistik-Datenbank."); $dbconn = mysqli_connect($stats_mysql_server, $stats_mysql_user, $stats_mysql_pw) or die("Keine Verbindung zur Statistik-Datenbank.");
mysqli_select_db($dbconn, $stats_mysql_db);
$qry = mysqli_query($dbconn, "SELECT DATE_FORMAT(datetime, '%d.%m.%y') AS datetime, MAX(percent) AS value FROM hdd WHERE datetime > DATE_SUB(NOW(), INTERVAL 31 DAY) GROUP BY DATE_FORMAT(datetime, '%d.%m.%y') ORDER BY DATE_FORMAT(datetime, '%y%m%d') ASC") or die(mysqli_error($dbconn)); $qry = mysqli_query($dbconn, "SELECT DATE_FORMAT(datetime, '%d.%m.%y') AS datetime, MAX(percent) AS value FROM hdd WHERE datetime > DATE_SUB(NOW(), INTERVAL 31 DAY) GROUP BY DATE_FORMAT(datetime, '%d.%m.%y') ORDER BY DATE_FORMAT(datetime, '%y%m%d') ASC") or die(mysqli_error($dbconn));
// Dataset definition // Dataset definition
+1 -1
View File
@@ -10,7 +10,7 @@
<table><tr><th>Name</th><th>Aktion</th></tr> <table><tr><th>Name</th><th>Aktion</th></tr>
{foreach $keys as $key} {foreach $keys as $key}
<tr> <tr>
<td>{$key.name|escape}</td> <td>{$key.name}</td>
<td><a href="?action=userkeys&amp;id={$user.id}&amp;del={$key.id}"><span class="del" title="Passkey löschen"></span></a></td> <td><a href="?action=userkeys&amp;id={$user.id}&amp;del={$key.id}"><span class="del" title="Passkey löschen"></span></a></td>
</tr> </tr>
{/foreach} {/foreach}
-418
View File
@@ -1,418 +0,0 @@
<?php
/*
* Migration einer Instanz-Datenbank auf das Schema update/schema.sql
* (utf8mb4) mit Reparatur doppelt kodierter Texte.
*
* Hintergrund: Die Anwendung hat jahrelang UTF-8 aus dem Browser über eine
* Verbindung geschrieben, die der Server als latin1 behandelt hat. Aus "ä"
* wurde dabei "ä". Datensätze, die auf anderem Weg entstanden sind
* (phpMyAdmin, neuere Server-Defaults), können dagegen korrekt sein. Deshalb
* wird jeder Wert einzeln geprüft und nur repariert, wenn er eindeutig
* doppelt kodiert ist.
*
* Aufruf im Installationsverzeichnis der Instanz (die Zugangsdaten kommen
* aus deren Config):
*
* php update/migration-utf8mb4.php pruefen
* Liest nur und gibt einen Bericht aus. Ändert nichts.
*
* php update/migration-utf8mb4.php migrieren <zieldatenbank>
* Legt die Zieldatenbank mit update/schema.sql an und kopiert alle
* Daten hinein. Die Quelldatenbank bleibt unverändert. Die Ziel-
* datenbank darf noch nicht existieren oder muss leer sein.
*
* Für lokale Tests lässt sich die Config übergehen:
* --server=127.0.0.1:3307 --user=root --db=wagner
* Das Passwort kommt dann aus der Umgebungsvariable MIGRATION_PW, damit es
* nicht in der Prozessliste steht.
*/
if(PHP_SAPI != "cli")
exit("Nur per Kommandozeile aufrufbar.\n");
require_once(__DIR__."/../incs/db.php");
function fehler($text)
{
fwrite(STDERR, "FEHLER: ".$text."\n");
exit(1);
}
/* ---------------------------------------------------------------------
* Kodierung
* ------------------------------------------------------------------ */
/*
* Umkehrung dessen, was MariaDB beim Speichern gemacht hat: Unicode-Text
* zurück in die Bytes von MySQL-"latin1" (das ist cp1252; die fünf dort
* undefinierten Bytes bildet MySQL auf U+0081, U+008D, U+008F, U+0090 und
* U+009D ab). Gibt null zurück, wenn ein Zeichen in latin1 nicht vorkommt -
* dann kann der Wert nicht über eine latin1-Verbindung entstanden sein.
*/
function latin1Bytes($text)
{
static $cp1252 = array(
0x20AC => 0x80, 0x201A => 0x82, 0x0192 => 0x83, 0x201E => 0x84,
0x2026 => 0x85, 0x2020 => 0x86, 0x2021 => 0x87, 0x02C6 => 0x88,
0x2030 => 0x89, 0x0160 => 0x8A, 0x2039 => 0x8B, 0x0152 => 0x8C,
0x017D => 0x8E, 0x2018 => 0x91, 0x2019 => 0x92, 0x201C => 0x93,
0x201D => 0x94, 0x2022 => 0x95, 0x2013 => 0x96, 0x2014 => 0x97,
0x02DC => 0x98, 0x2122 => 0x99, 0x0161 => 0x9A, 0x203A => 0x9B,
0x0153 => 0x9C, 0x017E => 0x9E, 0x0178 => 0x9F,
0x0081 => 0x81, 0x008D => 0x8D, 0x008F => 0x8F, 0x0090 => 0x90,
0x009D => 0x9D,
);
$bytes = "";
foreach(preg_split('//u', $text, -1, PREG_SPLIT_NO_EMPTY) as $zeichen)
{
$cp = codepoint($zeichen);
if($cp < 0x80 || ($cp >= 0xA0 && $cp <= 0xFF))
$bytes .= chr($cp);
elseif(isset($cp1252[$cp]))
$bytes .= chr($cp1252[$cp]);
else
return null;
}
return $bytes;
}
function codepoint($zeichen)
{
$b = array_values(unpack("C*", $zeichen));
switch(count($b))
{
case 1: return $b[0];
case 2: return (($b[0] & 0x1F) << 6) | ($b[1] & 0x3F);
case 3: return (($b[0] & 0x0F) << 12) | (($b[1] & 0x3F) << 6) | ($b[2] & 0x3F);
default: return (($b[0] & 0x07) << 18) | (($b[1] & 0x3F) << 12) | (($b[2] & 0x3F) << 6) | ($b[3] & 0x3F);
}
}
/*
* Ordnet einen Textwert ein und liefert array(kategorie, neuer_wert).
*
* ascii nur ASCII, unverändert
* korrekt enthält Nicht-ASCII, ist aber kein doppelt kodierter Text
* repariert war doppelt kodiert
* mehrfach war mehr als doppelt kodiert
* unklar sieht teilweise doppelt kodiert aus, lässt sich aber nicht
* eindeutig zurückführen; bleibt unverändert
*/
function pruefeText($wert)
{
if(!preg_match('/[^\x00-\x7F]/', $wert))
return array("ascii", $wert);
$runden = 0;
$aktuell = $wert;
while($runden < 5)
{
$bytes = latin1Bytes($aktuell);
// Echte Umlaute in latin1-Bytes ("M\xFCller") sind kein gültiges
// UTF-8. Nur wenn die Bytes gültiges UTF-8 mit Nicht-ASCII ergeben,
// war der Text doppelt kodiert.
if($bytes === null || !preg_match('//u', $bytes) || !preg_match('/[^\x00-\x7F]/', $bytes))
break;
$aktuell = $bytes;
$runden++;
}
if($runden == 1)
return array("repariert", $aktuell);
if($runden > 1)
return array("mehrfach", $aktuell);
// Typische Reste doppelter Kodierung neben Zeichen, die nicht aus
// latin1 stammen können: gemischter Wert, nicht automatisch lösbar.
if(preg_match('/[\x{00C2}\x{00C3}][\x{0080}-\x{00BF}\x{0152}\x{0153}\x{0160}\x{0161}\x{0178}\x{017D}\x{017E}\x{0192}\x{02C6}\x{02DC}\x{2013}\x{2014}\x{2018}-\x{201E}\x{2020}-\x{2022}\x{2026}\x{2030}\x{2039}\x{203A}\x{20AC}\x{2122}]/u', $wert))
return array("unklar", $wert);
return array("korrekt", $wert);
}
/* ---------------------------------------------------------------------
* Aufruf und Verbindung
* ------------------------------------------------------------------ */
$modus = null;
$zielDb = null;
$optionen = array();
foreach(array_slice($argv, 1) as $arg)
{
if(preg_match('/^--(server|user|db)=(.*)$/', $arg, $m))
$optionen[$m[1]] = $m[2];
elseif($modus === null)
$modus = $arg;
elseif($zielDb === null)
$zielDb = $arg;
else
fehler("Unbekanntes Argument: ".$arg);
}
if($modus != "pruefen" && $modus != "migrieren")
fehler("Aufruf: php update/migration-utf8mb4.php pruefen | migrieren <zieldatenbank>");
if($modus == "migrieren" && !preg_match('/^[A-Za-z0-9_]+$/', (string)$zielDb))
fehler("Zieldatenbank fehlt oder enthält unzulässige Zeichen.");
if(isset($optionen["db"]))
{
$server = isset($optionen["server"]) ? $optionen["server"] : "localhost";
$user = isset($optionen["user"]) ? $optionen["user"] : "";
$pw = (string)getenv("MIGRATION_PW");
$quellDb = $optionen["db"];
}
else
{
require_once(__DIR__."/../incs/constants.php");
require_once(__DIR__."/../incs/settings.php");
if(readsettings() != "valid")
fehler("Konfiguration ".CONFIG_PATH.CONFIG_FILE." nicht lesbar. Im Installationsverzeichnis der Instanz aufrufen.");
$server = $mysql_server;
$user = $mysql_user;
$pw = $mysql_pw;
$quellDb = $mysql_db;
}
if($modus == "migrieren" && strcasecmp($zielDb, $quellDb) == 0)
fehler("Quelle und Ziel sind dieselbe Datenbank.");
// Lesen immer als utf8mb4: Der Server wandelt dabei nur um, die Reparatur
// passiert ausschließlich hier im Skript.
$quelle = db_verbinden($server, $user, $pw, $quellDb) or fehler("Keine Verbindung zu ".$quellDb.": ".mysqli_connect_error());
/* Spalten einer Datenbank: tabelle(klein) => array(name, spalten) */
function tabellen($dbconn, $db)
{
$tabellen = array();
$zeilen = db_zeilen($dbconn, "SELECT table_name AS t, column_name AS c, data_type AS typ, is_nullable AS nullbar, column_default AS standard, character_set_name AS zeichensatz, extra
FROM information_schema.columns WHERE table_schema = ? ORDER BY table_name, ordinal_position", array($db));
foreach($zeilen as $z)
{
$schluessel = strtolower($z["t"]);
if(!isset($tabellen[$schluessel]))
$tabellen[$schluessel] = array("name" => $z["t"], "spalten" => array());
$tabellen[$schluessel]["spalten"][$z["c"]] = $z;
}
return $tabellen;
}
$quellTabellen = tabellen($quelle, $quellDb);
if(!$quellTabellen)
fehler("Datenbank ".$quellDb." enthält keine Tabellen.");
/* ---------------------------------------------------------------------
* Zielschema anlegen
* ------------------------------------------------------------------ */
$ziel = null;
if($modus == "migrieren")
{
$vorhanden = db_wert($quelle, "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = ?", array($zielDb));
if($vorhanden > 0)
fehler("Zieldatenbank ".$zielDb." existiert und ist nicht leer.");
mysqli_query($quelle, "CREATE DATABASE IF NOT EXISTS `".$zielDb."` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")
or fehler("Zieldatenbank nicht anlegbar: ".mysqli_error($quelle));
$ziel = db_verbinden($server, $user, $pw, $zielDb) or fehler("Keine Verbindung zu ".$zielDb.": ".mysqli_connect_error());
$schema = file_get_contents(__DIR__."/schema.sql") or fehler("update/schema.sql nicht lesbar.");
if(!mysqli_multi_query($ziel, $schema))
fehler("Schema: ".mysqli_error($ziel));
do {
if($r = mysqli_store_result($ziel))
mysqli_free_result($r);
} while(mysqli_more_results($ziel) && mysqli_next_result($ziel));
if(mysqli_errno($ziel))
fehler("Schema: ".mysqli_error($ziel));
// Beim Kopieren: ids 0 behalten, Fremdschlüssel erst hinterher prüfen.
mysqli_query($ziel, "SET SESSION sql_mode = 'STRICT_ALL_TABLES,NO_AUTO_VALUE_ON_ZERO,NO_ENGINE_SUBSTITUTION'");
mysqli_query($ziel, "SET FOREIGN_KEY_CHECKS = 0");
$zielTabellen = tabellen($ziel, $zielDb);
}
else
{
// Im Prüfmodus zählt das Schema aus der Datei nicht - geprüft wird,
// was in der Quelle steht.
$zielTabellen = $quellTabellen;
}
/* ---------------------------------------------------------------------
* Tabellen durchgehen
* ------------------------------------------------------------------ */
/* Wert für eine Spalte, die es in der Quelle nicht gibt */
function ersatzwert($spalte)
{
if(stripos($spalte["extra"], "auto_increment") !== false)
return null; // Spalte weglassen, die Datenbank vergibt die Werte
if($spalte["standard"] !== null && strtoupper($spalte["standard"]) !== "NULL")
return null; // Spalte weglassen, DEFAULT greift
if($spalte["nullbar"] == "YES")
return array(null);
return array(leerwert($spalte));
}
/* Was der alte, nicht strikte Modus für einen fehlenden Wert eingesetzt hätte */
function leerwert($spalte)
{
switch($spalte["typ"])
{
case "date": return "0000-00-00";
case "datetime": case "timestamp": return "0000-00-00 00:00:00";
case "time": return "00:00:00";
case "char": case "varchar": case "text": case "tinytext": case "mediumtext": case "longtext": case "enum": case "set":
return "";
default: return 0;
}
}
$bericht = array();
$hinweise = array();
foreach($quellTabellen as $schluessel => $q)
if(!isset($zielTabellen[$schluessel]))
$hinweise[] = "Tabelle ".$q["name"]." gibt es im neuen Schema nicht, sie wird nicht übernommen.";
foreach($zielTabellen as $schluessel => $z)
{
if(!isset($quellTabellen[$schluessel]))
{
$hinweise[] = "Tabelle ".$z["name"]." fehlt in der Quelle und bleibt leer.";
continue;
}
$q = $quellTabellen[$schluessel];
$gemeinsam = array();
$ergaenzt = array();
foreach($z["spalten"] as $name => $spalte)
{
if(isset($q["spalten"][$name]))
$gemeinsam[] = $name;
elseif(($ersatz = ersatzwert($spalte)) !== null)
$ergaenzt[$name] = $ersatz[0];
}
foreach(array_diff(array_keys($q["spalten"]), array_keys($z["spalten"])) as $name)
$hinweise[] = "Spalte ".$q["name"].".".$name." gibt es im neuen Schema nicht, sie wird nicht übernommen.";
foreach($ergaenzt as $name => $wert)
$hinweise[] = "Spalte ".$z["name"].".".$name." fehlt in der Quelle und wird mit ".var_export($wert, true)." gefüllt.";
// Im Ziel Pflicht, in der Quelle darf NULL stehen: dort den Leerwert
// einsetzen, sonst lehnt der strikte Modus die Zeile ab.
$nullErsatz = array();
foreach($gemeinsam as $name)
if($z["spalten"][$name]["nullbar"] == "NO" && $q["spalten"][$name]["nullbar"] == "YES")
$nullErsatz[$name] = 0;
$textSpalten = array();
foreach($gemeinsam as $name)
if($q["spalten"][$name]["zeichensatz"] !== null)
$textSpalten[] = $name;
$liste = implode(", ", array_map(function($n) { return "`".$n."`"; }, $gemeinsam));
$res = mysqli_query($quelle, "SELECT ".$liste." FROM `".$q["name"]."`", MYSQLI_USE_RESULT)
or fehler($q["name"].": ".mysqli_error($quelle));
$stmt = null;
if($ziel)
{
$spaltenZiel = array_merge($gemeinsam, array_keys($ergaenzt));
$sql = "INSERT INTO `".$z["name"]."` (".implode(", ", array_map(function($n) { return "`".$n."`"; }, $spaltenZiel)).") VALUES (".implode(", ", array_fill(0, count($spaltenZiel), "?")).")";
$stmt = mysqli_prepare($ziel, $sql) or fehler($z["name"].": ".mysqli_error($ziel));
mysqli_begin_transaction($ziel);
}
$zeilen = 0;
while($zeile = mysqli_fetch_assoc($res))
{
$zeilen++;
foreach($textSpalten as $name)
{
if($zeile[$name] === null)
continue;
list($kategorie, $neu) = pruefeText($zeile[$name]);
if(!isset($bericht[$q["name"]][$name]))
$bericht[$q["name"]][$name] = array("ascii" => 0, "korrekt" => 0, "repariert" => 0, "mehrfach" => 0, "unklar" => 0, "unklar_ids" => array());
$bericht[$q["name"]][$name][$kategorie]++;
if($kategorie == "unklar" && isset($zeile["id"]) && count($bericht[$q["name"]][$name]["unklar_ids"]) < 20)
$bericht[$q["name"]][$name]["unklar_ids"][] = $zeile["id"];
$zeile[$name] = $neu;
}
foreach($nullErsatz as $name => $anzahl)
{
if($zeile[$name] === null)
{
$zeile[$name] = leerwert($z["spalten"][$name]);
$nullErsatz[$name]++;
}
}
if($stmt)
{
$werte = array_merge(array_values($zeile), array_values($ergaenzt));
mysqli_stmt_execute($stmt, $werte)
or fehler($z["name"].", Zeile ".$zeilen.(isset($zeile["id"]) ? " (id ".$zeile["id"].")" : "").": ".mysqli_stmt_error($stmt));
}
}
mysqli_free_result($res);
foreach($nullErsatz as $name => $anzahl)
if($anzahl > 0)
$hinweise[] = "Spalte ".$z["name"].".".$name.": ".$anzahl." Zeilen mit NULL, im neuen Schema Pflichtfeld, gefüllt mit ".var_export(leerwert($z["spalten"][$name]), true).".";
if($stmt)
{
mysqli_commit($ziel);
mysqli_stmt_close($stmt);
$anzahl = db_wert($ziel, "SELECT COUNT(*) FROM `".$z["name"]."`");
if($anzahl != $zeilen)
fehler($z["name"].": ".$zeilen." Zeilen gelesen, aber ".$anzahl." im Ziel.");
}
$bericht[$q["name"]]["_zeilen"] = $zeilen;
}
/* ---------------------------------------------------------------------
* Fremdschlüssel prüfen (nur Ziel)
* ------------------------------------------------------------------ */
$verwaist = array();
if($ziel)
{
mysqli_query($ziel, "SET FOREIGN_KEY_CHECKS = 1");
$fks = db_zeilen($ziel, "SELECT table_name AS t, column_name AS c, referenced_table_name AS rt, referenced_column_name AS rc
FROM information_schema.key_column_usage WHERE table_schema = ? AND referenced_table_name IS NOT NULL", array($zielDb));
foreach($fks as $fk)
{
$anzahl = db_wert($ziel, "SELECT COUNT(*) FROM `".$fk["t"]."` k LEFT JOIN `".$fk["rt"]."` e ON e.`".$fk["rc"]."` = k.`".$fk["c"]."` WHERE k.`".$fk["c"]."` IS NOT NULL AND e.`".$fk["rc"]."` IS NULL");
if($anzahl > 0)
$verwaist[] = sprintf("%s.%s -> %s.%s: %d Zeilen ohne Gegenstück", $fk["t"], $fk["c"], $fk["rt"], $fk["rc"], $anzahl);
}
}
/* ---------------------------------------------------------------------
* Bericht
* ------------------------------------------------------------------ */
echo "Migration utf8mb4 - ".($modus == "pruefen" ? "Prüfmodus (nichts geändert)" : "Migration nach ".$zielDb)."\n";
echo "Quelle: ".$quellDb."\n\n";
$summe = array("repariert" => 0, "mehrfach" => 0, "unklar" => 0, "korrekt" => 0);
printf("%-40s %8s %8s %9s %8s %7s\n", "Spalte", "Zeilen", "korrekt", "repariert", "mehrfach", "unklar");
foreach($bericht as $tabelle => $spalten)
{
$zeilen = $spalten["_zeilen"];
unset($spalten["_zeilen"]);
foreach($spalten as $spalte => $w)
{
foreach($summe as $k => $v)
$summe[$k] += $w[$k];
if($w["korrekt"] + $w["repariert"] + $w["mehrfach"] + $w["unklar"] == 0)
continue; // reine ASCII-Spalte
printf("%-40s %8d %8d %9d %8d %7d\n", $tabelle.".".$spalte, $zeilen, $w["korrekt"], $w["repariert"], $w["mehrfach"], $w["unklar"]);
if($w["unklar_ids"])
echo " unklar bei id: ".implode(", ", $w["unklar_ids"]).(count($w["unklar_ids"]) == 20 ? ", ..." : "")."\n";
}
}
printf("\nSumme: %d korrekt, %d repariert, %d mehrfach kodiert, %d unklar\n", $summe["korrekt"], $summe["repariert"], $summe["mehrfach"], $summe["unklar"]);
if($hinweise)
echo "\nHinweise:\n - ".implode("\n - ", $hinweise)."\n";
if($ziel)
echo "\nFremdschlüssel: ".($verwaist ? "\n - ".implode("\n - ", $verwaist) : "alle Verweise gültig")."\n";
+4 -2
View File
@@ -72,8 +72,10 @@ else
$settings=readsettings(); $settings=readsettings();
if($settings == "valid") if($settings == "valid")
{ {
if(!$dbconn = db_verbinden($mysql_server, $mysql_user, $mysql_pw, $mysql_db)) if(!$dbconn = @mysqli_connect($mysql_server, $mysql_user, $mysql_pw))
$err = error("Verbindung zur Datenbank fehlgeschlagen: ".htmlspecialchars(mysqli_connect_error())); $err = error("Verbindung zur Datenbank fehlgeschlagen!!");
if(!@mysqli_select_db($dbconn,$mysql_db))
$err = error("Datenbank nicht gefunden!!");
if(!$err) if(!$err)
-672
View File
@@ -1,672 +0,0 @@
-- Schema der Kundenverwaltung
--
-- Maßgeblich ist die Datenbank der Instanz kunden; project und work
-- stammen aus ingwa, weil sie dort fehlen, der Code sie aber braucht.
-- Einheitlich utf8mb4. Die Anwendung verbindet sich im strikten sql_mode
-- (siehe db_verbinden() in incs/db.php).
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
CREATE TABLE `accesstype` (
`id` tinyint(3) NOT NULL AUTO_INCREMENT,
`typ` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `addKey` (
`accesskey` int(11) NOT NULL,
`datetime` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `adresse` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`partner_id` int(11) NOT NULL,
`anrede_id` int(5) NOT NULL,
`anrede2_id` int(5) NOT NULL,
`titel_id` int(5) NOT NULL,
`titel2_id` int(5) NOT NULL,
`vname` varchar(50) NOT NULL,
`vname2` varchar(50) NOT NULL,
`nname` varchar(50) NOT NULL,
`nname2` varchar(50) NOT NULL,
`firma` varchar(50) NOT NULL,
`str_id` int(11) NOT NULL,
`typ_id` int(2) NOT NULL,
`hnr` varchar(10) NOT NULL,
`zusatz` varchar(100) NOT NULL,
`gebdat` date NOT NULL,
`gebdat2` date NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_partner_titel_id` (`titel_id`),
KEY `fk_partner_anrede_id` (`anrede_id`),
KEY `fk_adr_str_id` (`str_id`),
KEY `fk_adr_typ_id` (`typ_id`),
KEY `onlyoneadresstype` (`partner_id`,`typ_id`),
CONSTRAINT `fk_adr_partner_id` FOREIGN KEY (`partner_id`) REFERENCES `partner` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_adr_str_id` FOREIGN KEY (`str_id`) REFERENCES `strasse` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_adr_typ_id` FOREIGN KEY (`typ_id`) REFERENCES `adresstyp` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_partner_anrede_id` FOREIGN KEY (`anrede_id`) REFERENCES `anrede` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_partner_titel_id` FOREIGN KEY (`titel_id`) REFERENCES `titel` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `adresstyp` (
`id` int(2) NOT NULL AUTO_INCREMENT,
`typ` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `altlasten` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`vacationdays` int(11) NOT NULL,
`hours` decimal(8,4) NOT NULL,
`ma_id` int(11) NOT NULL,
`last_generated` date NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_altlasten_user_id` (`ma_id`),
CONSTRAINT `fk_altlasten_user_id` FOREIGN KEY (`ma_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `anrede` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`anrede` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `artikel` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`bezeichn` varchar(150) NOT NULL,
`typ_id` int(5) NOT NULL,
`datetime` datetime NOT NULL,
`user_id` int(11) NOT NULL,
`einheit_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_artikel_typ_id` (`typ_id`),
KEY `fk_art_rech_einheit_id` (`einheit_id`),
KEY `fk_artikel_user_id` (`user_id`),
CONSTRAINT `fk_art_rech_einheit_id` FOREIGN KEY (`einheit_id`) REFERENCES `einheit` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_artikel_typ_id` FOREIGN KEY (`typ_id`) REFERENCES `artikeltyp` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_artikel_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `artikel_eigensch` (
`art_id` int(11) NOT NULL,
`eig_id` int(11) NOT NULL,
`rech_id` int(11) NOT NULL,
`posnr` int(5) NOT NULL,
`artposnr` int(11) NOT NULL,
KEY `fk_art_eig_art_id` (`art_id`),
KEY `fk_art_eig_rech_id` (`rech_id`),
KEY `fk_art_eig_eig_id` (`eig_id`),
CONSTRAINT `fk_art_eig_art_id` FOREIGN KEY (`art_id`) REFERENCES `artikel` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_art_eig_eig_id` FOREIGN KEY (`eig_id`) REFERENCES `eigensch` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_art_eig_rech_id` FOREIGN KEY (`rech_id`) REFERENCES `rechnung` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `artikel_rechn` (
`art_id` int(11) NOT NULL,
`rech_id` int(11) NOT NULL,
`posnr` int(5) NOT NULL,
`rabatt` int(3) NOT NULL,
`rabatttyp` int(2) NOT NULL,
`menge` decimal(6,3) NOT NULL,
`preis` decimal(10,2) DEFAULT NULL,
`angtyp` int(1) NOT NULL,
`artmwst` int(3) NOT NULL,
`wanr` varchar(100) NOT NULL,
`pagebreak` int(1) NOT NULL,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
KEY `fk_art_rech_art_id` (`art_id`),
KEY `fk_art_rech_rech_id` (`rech_id`),
CONSTRAINT `fk_art_rech_art_id` FOREIGN KEY (`art_id`) REFERENCES `artikel` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_art_rech_rech_id` FOREIGN KEY (`rech_id`) REFERENCES `rechnung` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `artikel_termin` (
`art_id` int(11) NOT NULL,
`termin_id` int(11) NOT NULL,
`menge` decimal(6,3) NOT NULL,
`rabatt` int(3) NOT NULL,
`wanr` varchar(100) NOT NULL,
KEY `fk_art_term_art_id` (`art_id`),
KEY `fk_art_term_ang_id` (`termin_id`),
CONSTRAINT `fk_art_term_ang_id` FOREIGN KEY (`termin_id`) REFERENCES `termin` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_art_term_art_id` FOREIGN KEY (`art_id`) REFERENCES `artikel` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `artikelsks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`rechnr` int(11) NOT NULL,
`bezeichn1` varchar(60) NOT NULL,
`bezeichn2` varchar(60) NOT NULL,
`menge` decimal(6,3) NOT NULL,
`beschr` text DEFAULT NULL,
`pos` tinyint(3) NOT NULL,
`rabatt` tinyint(3) NOT NULL,
`preis` decimal(10,2) DEFAULT NULL,
`datetime` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_artikelsks_rech_id` (`rechnr`),
CONSTRAINT `fk_artikelsks_rech_id` FOREIGN KEY (`rechnr`) REFERENCES `rechnung` (`rechnr`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `artikeltyp` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`bezeichn` varchar(50) NOT NULL,
`dropdown` tinyint(1) NOT NULL,
`list` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `ausgabe` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`partner_id` int(11) NOT NULL,
`rec_kto_id` int(11) NOT NULL,
`send_kto_id` int(11) NOT NULL,
`betr` decimal(12,3) NOT NULL,
`rabatt` decimal(4,1) NOT NULL,
KEY `fk_ausg_user_id` (`user_id`),
KEY `fk_ausg_reckto_id` (`rec_kto_id`),
KEY `fk_ausg_sendkto_id` (`send_kto_id`),
KEY `fk_ausg_partner_id` (`partner_id`),
CONSTRAINT `fk_ausg_partner_id` FOREIGN KEY (`partner_id`) REFERENCES `partner` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_ausg_reckto_id` FOREIGN KEY (`rec_kto_id`) REFERENCES `konto` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_ausg_sendkto_id` FOREIGN KEY (`send_kto_id`) REFERENCES `konto` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_ausg_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `bank` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`blz` varchar(30) NOT NULL,
`bank` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `buchungsKontos` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`kto` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
`descr` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `eigensch` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`bezeichn` varchar(100) NOT NULL,
`wert` varchar(50) NOT NULL,
`einheit_id` int(11) NOT NULL,
`datetime` datetime NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_eigensch_user_id` (`user_id`),
KEY `fk_eigensch_einheit_id` (`einheit_id`),
CONSTRAINT `fk_eigensch_einheit_id` FOREIGN KEY (`einheit_id`) REFERENCES `einheit` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_eigensch_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `einheit` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`einheit` varchar(10) NOT NULL,
`si` tinyint(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `kontakt` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`partner_id` int(11) NOT NULL,
`typ_id` int(3) NOT NULL,
`kontakt` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_kontakt_typ_id` (`typ_id`),
KEY `fk_kontakt_partner_id` (`partner_id`),
CONSTRAINT `fk_kontakt_partner_id` FOREIGN KEY (`partner_id`) REFERENCES `partner` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_kontakt_typ_id` FOREIGN KEY (`typ_id`) REFERENCES `kontakttyp` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `kontakttyp` (
`id` int(3) NOT NULL AUTO_INCREMENT,
`typ` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `konto` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`bank_id` int(5) NOT NULL,
`partner_id` int(11) NOT NULL,
`inhaber` varchar(50) NOT NULL,
`iban` varchar(30) NOT NULL,
`swift` varchar(15) NOT NULL,
`ktonr` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_kto_partner_id` (`partner_id`),
KEY `fk_kto_bank_id` (`bank_id`),
CONSTRAINT `fk_kto_bank_id` FOREIGN KEY (`bank_id`) REFERENCES `bank` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_kto_partner_id` FOREIGN KEY (`partner_id`) REFERENCES `partner` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `land` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`land_short` varchar(5) NOT NULL,
`land` varchar(30) NOT NULL,
`datetime` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`typ_id` tinyint(3) NOT NULL,
`user_id` int(11) NOT NULL,
`datetime` datetime NOT NULL,
`qry` text NOT NULL,
`undo_qry` text DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_log_accesstype_id` (`typ_id`),
KEY `fk_log_user_id` (`user_id`),
CONSTRAINT `fk_log_accesstype_id` FOREIGN KEY (`typ_id`) REFERENCES `accesstype` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_log_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`send_id` int(11) NOT NULL,
`rec_id` int(11) NOT NULL,
`head` varchar(50) DEFAULT NULL,
`msg` text NOT NULL,
`rd` tinyint(1) NOT NULL,
`datetime` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_msg_senduser_id` (`send_id`),
KEY `fk_msg_recuser_id` (`rec_id`),
CONSTRAINT `fk_msg_recuser_id` FOREIGN KEY (`rec_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_msg_senduser_id` FOREIGN KEY (`send_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `mwst` (
`id` int(3) NOT NULL AUTO_INCREMENT,
`mwst` decimal(4,1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `mykonto` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`bank_id` int(5) NOT NULL,
`inhaber` varchar(50) NOT NULL,
`iban` varchar(30) NOT NULL,
`swift` varchar(15) NOT NULL,
`ktonr` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_mykto_bank_id` (`bank_id`),
CONSTRAINT `fk_mykto_bank_id` FOREIGN KEY (`bank_id`) REFERENCES `bank` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `notiz` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`partner_id` int(11) NOT NULL,
`notiz` text NOT NULL,
`datetime` datetime NOT NULL,
`color` varchar(6) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_notizen_partner_id` (`partner_id`),
CONSTRAINT `fk_notizen_partner_id` FOREIGN KEY (`partner_id`) REFERENCES `partner` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `ort` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ort` varchar(30) NOT NULL,
`ortsteil` varchar(30) NOT NULL,
`plz` varchar(15) NOT NULL,
`vwahl` varchar(15) NOT NULL,
`land_id` int(5) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_ort_land_id` (`land_id`),
CONSTRAINT `fk_ort_land_id` FOREIGN KEY (`land_id`) REFERENCES `land` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `partner` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datetime_new` datetime NOT NULL,
`datetime_lastchange` datetime NOT NULL,
`user_id` int(11) NOT NULL,
`typ_id` int(2) NOT NULL,
`zahlziel_id` int(3) NOT NULL,
`rabatt` int(3) NOT NULL,
`sks_not_checked` int(1) NOT NULL,
`sks_kdnotiz` text NOT NULL,
`updated_by` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_partner_typ_id` (`typ_id`),
KEY `fk_partner_zahlziel_id` (`zahlziel_id`),
KEY `fk_partner_user_id` (`user_id`),
CONSTRAINT `fk_partner_typ_id` FOREIGN KEY (`typ_id`) REFERENCES `partnertyp` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_partner_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_partner_zahlziel_id` FOREIGN KEY (`zahlziel_id`) REFERENCES `zahlziel` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `partnertyp` (
`id` int(2) NOT NULL AUTO_INCREMENT,
`typ` varchar(15) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `passkeys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`lastAuth` datetime NOT NULL,
`name` varchar(40) NOT NULL,
`userId` int(11) NOT NULL,
`credentialId` varchar(1023) NOT NULL,
`credentialPublicKey` text NOT NULL,
`signatureCounter` text NOT NULL,
`authKey` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `project` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(150) NOT NULL,
`name2` varchar(150) NOT NULL,
`partner_id` int(11) NOT NULL,
`changetime` datetime NOT NULL,
`changeuser` int(11) NOT NULL,
`datetime` datetime NOT NULL,
`intro` text NOT NULL,
`outro` text NOT NULL,
PRIMARY KEY (`id`),
KEY `fek_proj_partner_id` (`partner_id`),
CONSTRAINT `fek_proj_partner_id` FOREIGN KEY (`partner_id`) REFERENCES `partner` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `rechnr` (
`rechnr` int(11) NOT NULL,
`typ_id` int(5) NOT NULL,
PRIMARY KEY (`rechnr`,`typ_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `rechnung` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`adressfields` varchar(12) NOT NULL,
`anrede1` varchar(150) NOT NULL,
`anrede2` varchar(150) NOT NULL,
`partner_id` int(11) NOT NULL,
`mwst_id` int(3) NOT NULL,
`user_id` int(11) NOT NULL,
`typ_id` int(2) NOT NULL,
`zahlziel_id` int(3) NOT NULL,
`adresse_id` int(11) NOT NULL,
`wahrung_id` tinyint(3) NOT NULL,
`changetime` datetime NOT NULL,
`changeuser` int(11) NOT NULL,
`datetime` datetime NOT NULL,
`rechnr` int(11) NOT NULL,
`rabatt` int(3) NOT NULL,
`rabattdesc` varchar(100) NOT NULL,
`intro` text NOT NULL,
`outro` text NOT NULL,
`sks` tinyint(1) NOT NULL,
`sub_mwst` int(1) NOT NULL,
`kommission` int(11) NOT NULL,
`lieferdatum` varchar(100) NOT NULL,
`adresserweiterung` varchar(100) NOT NULL,
`storno` int(1) NOT NULL DEFAULT 0,
`stornodate` datetime NOT NULL,
`buchungsKto_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fek_rech_partner_id` (`partner_id`),
KEY `fek_rech_adresse_id` (`adresse_id`),
KEY `fek_rech_mwst_id` (`mwst_id`),
KEY `fek_rech_user_id` (`user_id`),
KEY `fek_rech_zahlziehl_id` (`zahlziel_id`),
KEY `fek_rech_typ_id` (`typ_id`),
KEY `fek_rech_wahrung_id` (`wahrung_id`),
KEY `rechnr` (`rechnr`),
KEY `fk_rech_buchungsKto_id` (`buchungsKto_id`),
CONSTRAINT `fek_rech_adresse_id` FOREIGN KEY (`adresse_id`) REFERENCES `adresse` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fek_rech_mwst_id` FOREIGN KEY (`mwst_id`) REFERENCES `mwst` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fek_rech_partner_id` FOREIGN KEY (`partner_id`) REFERENCES `partner` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fek_rech_typ_id` FOREIGN KEY (`typ_id`) REFERENCES `rechtyp` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fek_rech_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fek_rech_wahrung_id` FOREIGN KEY (`wahrung_id`) REFERENCES `wahrung` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fek_rech_zahlziehl_id` FOREIGN KEY (`zahlziel_id`) REFERENCES `zahlziel` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_rech_buchungsKto_id` FOREIGN KEY (`buchungsKto_id`) REFERENCES `buchungsKontos` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `rechtyp` (
`id` int(3) NOT NULL AUTO_INCREMENT,
`typ` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `setting` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`opt` varchar(10) NOT NULL,
`val` text NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_settings_user_id` (`user_id`),
CONSTRAINT `fk_settings_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `strasse` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`str` varchar(50) NOT NULL,
`ort_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_str_ort_id` (`ort_id`),
CONSTRAINT `fk_str_ort_id` FOREIGN KEY (`ort_id`) REFERENCES `ort` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `tel` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ort_id` int(11) NOT NULL,
`partner_id` int(11) NOT NULL,
`typ_id` int(3) NOT NULL,
`nr` varchar(20) NOT NULL,
`name` varchar(30) NOT NULL,
`adresse_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_tel_ort_id` (`ort_id`),
KEY `fk_tel_typ_id` (`typ_id`),
KEY `fk_tel_partner_id` (`partner_id`),
CONSTRAINT `fk_tel_ort_id` FOREIGN KEY (`ort_id`) REFERENCES `ort` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tel_partner_id` FOREIGN KEY (`partner_id`) REFERENCES `partner` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tel_typ_id` FOREIGN KEY (`typ_id`) REFERENCES `teltyp` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `teltyp` (
`id` int(3) NOT NULL AUTO_INCREMENT,
`typ` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `termin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`typ` int(2) NOT NULL,
`adresse_id` int(11) NOT NULL,
`text` text NOT NULL,
`changetime` datetime NOT NULL,
`done` tinyint(1) NOT NULL,
`user` int(11) NOT NULL,
`changeuser` int(11) NOT NULL,
`datetime` datetime NOT NULL,
`termtime` datetime NOT NULL,
`termdauer` time NOT NULL,
`followed_by` int(11) NOT NULL,
`act_date` datetime NOT NULL,
`head` varchar(100) NOT NULL,
`main_id` int(11) NOT NULL,
`user2` int(11) NOT NULL,
`user3` int(11) NOT NULL,
`user4` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_termin_typ_termtyp_id` (`typ`),
CONSTRAINT `fk_termin_typ_termtyp_id` FOREIGN KEY (`typ`) REFERENCES `termtyp` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `termtyp` (
`id` int(2) NOT NULL AUTO_INCREMENT,
`typ` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `titel` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`titel` varchar(50) NOT NULL,
`datetime` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `tmp_artikel` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`bezeichn` varchar(150) NOT NULL,
`typ_id` int(5) NOT NULL,
`einheit_id` int(11) NOT NULL,
`preis` decimal(10,2) DEFAULT NULL,
`datetime` datetime NOT NULL,
`posnr` int(5) NOT NULL,
`rabatt` int(3) NOT NULL,
`rabatttyp` int(2) NOT NULL,
`menge` decimal(6,3) NOT NULL,
`angtyp` int(1) NOT NULL,
`mwst_id` int(3) NOT NULL,
`rech_id` int(11) NOT NULL,
`wanr` varchar(100) NOT NULL,
`pagebreak` int(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_tmp_artikel_typ_id` (`typ_id`),
KEY `fk_tmp_art_mwst_id` (`mwst_id`),
KEY `fk_tmp_art_rech_id` (`rech_id`),
KEY `fk_tmp_artikel_einheit_id` (`einheit_id`),
CONSTRAINT `fk_tmp_art_mwst_id` FOREIGN KEY (`mwst_id`) REFERENCES `mwst` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_art_rech_id` FOREIGN KEY (`rech_id`) REFERENCES `tmp_rechnung` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_artikel_einheit_id` FOREIGN KEY (`einheit_id`) REFERENCES `einheit` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_artikel_typ_id` FOREIGN KEY (`typ_id`) REFERENCES `artikeltyp` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `tmp_eigensch` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`bezeichn` varchar(100) NOT NULL,
`wert` varchar(50) NOT NULL,
`einheit_id` int(11) NOT NULL,
`art_id` int(11) NOT NULL,
`datetime` datetime NOT NULL,
`posnr` int(5) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_tmp_eigensch_art_id` (`art_id`),
KEY `fk_tmp_eigensch_einheit_id` (`einheit_id`),
CONSTRAINT `fk_tmp_eigensch_art_id` FOREIGN KEY (`art_id`) REFERENCES `tmp_artikel` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_eigensch_einheit_id` FOREIGN KEY (`einheit_id`) REFERENCES `einheit` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `tmp_rechnung` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`adressfields` varchar(12) NOT NULL,
`partner_id` int(11) NOT NULL,
`anrede1` varchar(150) NOT NULL,
`anrede2` varchar(150) NOT NULL,
`mwst_id` int(3) NOT NULL,
`user_id` int(11) NOT NULL,
`rechnr` int(11) NOT NULL,
`typ_id` int(2) NOT NULL,
`zahlziel_id` int(3) NOT NULL,
`adresse_id` int(11) NOT NULL,
`wahrung_id` tinyint(3) NOT NULL,
`datetime` datetime NOT NULL,
`rabatt` int(3) NOT NULL,
`rabattdesc` varchar(100) NOT NULL,
`intro` text NOT NULL,
`outro` text NOT NULL,
`sub_mwst` int(1) NOT NULL,
`kommission` int(11) NOT NULL,
`lieferdatum` varchar(100) NOT NULL,
`adresserweiterung` varchar(100) NOT NULL,
`storno` int(1) NOT NULL DEFAULT 0,
`buchungsKto_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_tmp_rech_partner_id` (`partner_id`),
KEY `fk_tmp_rech_adresse_id` (`adresse_id`),
KEY `fk_tmp_rech_mwst_id` (`mwst_id`),
KEY `fk_tmp_rech_user_id` (`user_id`),
KEY `fk_tmp_rech_zahlziehl_id` (`zahlziel_id`),
KEY `fk_tmp_rech_typ_id` (`typ_id`),
KEY `fk_tmp_rech_wahrung_id` (`wahrung_id`),
KEY `fek_rech_buchungsKto_id` (`buchungsKto_id`),
CONSTRAINT `fek_rech_buchungsKto_id` FOREIGN KEY (`buchungsKto_id`) REFERENCES `buchungsKontos` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_rech_adresse_id` FOREIGN KEY (`adresse_id`) REFERENCES `adresse` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_rech_mwst_id` FOREIGN KEY (`mwst_id`) REFERENCES `mwst` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_rech_partner_id` FOREIGN KEY (`partner_id`) REFERENCES `partner` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_rech_typ_id` FOREIGN KEY (`typ_id`) REFERENCES `rechtyp` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_rech_user_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_rech_wahrung_id` FOREIGN KEY (`wahrung_id`) REFERENCES `wahrung` (`id`) ON UPDATE CASCADE,
CONSTRAINT `fk_tmp_rech_zahlziehl_id` FOREIGN KEY (`zahlziel_id`) REFERENCES `zahlziel` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`vname` varchar(50) NOT NULL,
`nname` varchar(50) NOT NULL,
`nick` varchar(50) NOT NULL,
`pass` varchar(150) NOT NULL,
`mail` varchar(50) NOT NULL,
`salt` varchar(50) NOT NULL,
`tel_priv` varchar(30) NOT NULL,
`tel_gesch` varchar(30) NOT NULL,
`tel_mobil` varchar(30) NOT NULL,
`lastlogin` datetime NOT NULL,
`numlogins` int(11) NOT NULL,
`logedout` tinyint(2) NOT NULL,
`wrongpw` tinyint(3) NOT NULL,
`notizen` text NOT NULL,
`blocked` tinyint(1) NOT NULL,
`rights` varchar(6) NOT NULL,
`weekly_hours` int(11) NOT NULL,
`rfid` varchar(10) NOT NULL,
`color` varchar(8) NOT NULL,
`weekhours` decimal(6,3) NOT NULL,
`vacationdays` int(4) NOT NULL,
`sahours` decimal(3,1) NOT NULL,
`timelog_start` date NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `nick` (`nick`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `wahrung` (
`id` tinyint(3) NOT NULL AUTO_INCREMENT,
`wahrung` varchar(20) NOT NULL,
`kurs` decimal(8,4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `work` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`bezeichn` varchar(150) NOT NULL,
`bezeichn2` varchar(150) NOT NULL,
`project_id` int(11) NOT NULL,
`changetime` datetime NOT NULL,
`changeuser` int(11) NOT NULL,
`datetime` datetime NOT NULL,
`std` decimal(6,3) NOT NULL,
`rechn` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `fek_work_partner_id` (`project_id`),
CONSTRAINT `fek_work_partner_id` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `zahlziel` (
`id` int(3) NOT NULL AUTO_INCREMENT,
`ziel` varchar(150) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `zeiten` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ma_id` int(11) NOT NULL,
`kommen` datetime NOT NULL,
`gehen` datetime NOT NULL,
`pause_mins` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_zeiten_user_id` (`ma_id`),
CONSTRAINT `fk_zeiten_user_id` FOREIGN KEY (`ma_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
SET FOREIGN_KEY_CHECKS = 1;