Termine auf Prepared Statements umgebaut

Kalender, Terminliste, Terminformular und Terminzettel laufen ueber
db_abfrage() mit Platzhaltern; das Anlegen einer mehrtaegigen Terminreihe
liegt in einer Transaktion. Die Logik steckt jetzt in incs/termin.php,
sites/termin*.php rufen sie nur noch auf.

Dabei behoben:
- SQL-Injektion ueber die Suchbegriffe der Terminliste, ueber die
  Mehrfachauswahl (Ankreuzfelder del<N>/erl<N> gingen roh in DELETE bzw.
  UPDATE), ueber die Wiedervorlage-Tage (INTERVAL $folgedays DAY) und ueber
  ?action=terminshow&id=... in maketerminarray(), maketerminadresse(),
  maketelnrn() und makegooglestring()
- gespeichertes XSS: Beschreibung, Notiz, Termintyp, Ort, Kundenname und
  Mitarbeiterfarbe im Kalender, in der Terminliste und im Formular
- "or $err=error(...)" stand innerhalb des Abfrage-Strings, dadurch wurden
  die Nebenzeilen einer Terminreihe beim Loeschen nie entfernt
- die Abfrage zum Folgetermin las eine Spalte typ_id, die es in termin nicht
  gibt; Typ und Wiedervorlage fehlten deshalb beim Bearbeiten
- ein Folgetermin wurde mit termtime='' und termdauer='' angelegt, ein
  Termin ohne Folgetermin mit followed_by='' - im strikten Modus Fehler
- ein Termin ohne Datum landete auf dem 01.01.1970 und gab dabei
  "NICHTTERMINIERT!!" mitten in die Seite aus
- die Ueberschneidungspruefung verglich ab dem zweiten Tag gegen das Ende
  des ersten Tages
- eine unbekannte Registerkarte der Terminliste ergab ein leeres WHERE und
  damit einen SQL-Fehler; jetzt gilt die Standardansicht
- terminshow.php pruefte den Typ mit = statt ==
- der Kalender haengt nicht mehr an der Locale de_DE (strftime)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-22 00:04:38 +02:00
co-authored by Claude Opus 5
parent 3dfbf1c9c7
commit 490c4f12a4
12 changed files with 986 additions and 950 deletions
+65 -44
View File
@@ -285,60 +285,80 @@ function createRechQry($page,$pagesize,$src,$combiner,$sks=0)
/*
* Terminliste. $termtyp waehlt die Registerkarte:
* 1 offene mit Datum, 2 offene ohne Datum, 3 erledigte,
* 4 vorgemerkte (Wiedervorlage in der Zukunft), 5 ganztaegige.
* Rueckgabe wie createKdnRechQry().
*/
function createTermQry($page,$pagesize,$src,$combiner,$termtyp)
{
$query = "SELECT termin.id,termtyp.typ, termin.head, MIN(termtime) AS termtime, SEC_TO_TIME(SUM(TIME_TO_SEC(termdauer))) AS termdauer, user, user2, user3, user4, done, partner_id, adresse_id, termin.id, str, ort, ortsteil, plz, hnr, adresse.vname as vname, adresse.nname as nname,
adresse.firma as firma,
user.vname as uservname, user.nname as usernname, user.color as color,
user2.vname as uservname2, user2.nname as usernname2,
user3.vname as uservname3, user3.nname as usernname3,
user4.vname as uservname4, user4.nname as usernname4,
firma, vname2, nname2
FROM termin
LEFT JOIN termtyp ON termin.typ = termtyp.id
LEFT JOIN adresse ON termin.adresse_id = adresse.id
LEFT JOIN partner ON adresse.partner_id = partner.id
LEFT JOIN strasse ON adresse.str_id = strasse.id
LEFT JOIN ort ON strasse.ort_id = ort.id
LEFT JOIN user ON termin.user = user.id
LEFT JOIN user AS user2 ON termin.user2 = user2.id
LEFT JOIN user AS user3 ON termin.user3 = user3.id
LEFT JOIN user AS user4 ON termin.user4 = user4.id
WHERE ";
if($termtyp == 1)
$query .= "termdauer != '24:00:00' AND termtime != '0000-00-00 00:00:00' AND done = 0 AND act_date < NOW()";
elseif($termtyp == 2)
$query .= "termdauer != '24:00:00' AND termtime = '0000-00-00 00:00:00' AND done = 0 AND act_date < NOW()";
elseif($termtyp == 3)
$query .= "termdauer != '24:00:00' AND done = 1 AND act_date < NOW()";
elseif($termtyp == 4)
$query .= "termdauer != '24:00:00' AND act_date > NOW()";
elseif($termtyp == 5)
$query .= "termdauer = '24:00:00'";
$combiner = $combiner == "OR" ? "OR" : "AND";
$params = array();
$query = "SELECT termin.id, termtyp.typ, termin.head, MIN(termtime) AS termtime,
SEC_TO_TIME(SUM(TIME_TO_SEC(termdauer))) AS termdauer,
user, user2, user3, user4, done, partner_id, adresse_id, main_id,
str, ort, ortsteil, plz, hnr, adresse.vname AS vname, adresse.nname AS nname,
user.vname AS uservname, user.nname AS usernname, user.color AS color,
user2.vname AS uservname2, user2.nname AS usernname2,
user3.vname AS uservname3, user3.nname AS usernname3,
user4.vname AS uservname4, user4.nname AS usernname4,
firma, vname2, nname2
FROM termin
LEFT JOIN termtyp ON termin.typ = termtyp.id
LEFT JOIN adresse ON termin.adresse_id = adresse.id
LEFT JOIN partner ON adresse.partner_id = partner.id
LEFT JOIN strasse ON adresse.str_id = strasse.id
LEFT JOIN ort ON strasse.ort_id = ort.id
LEFT JOIN user ON termin.user = user.id
LEFT JOIN user AS user2 ON termin.user2 = user2.id
LEFT JOIN user AS user3 ON termin.user3 = user3.id
LEFT JOIN user AS user4 ON termin.user4 = user4.id
WHERE ";
switch((int)$termtyp)
{
case 2:
$query .= "termdauer != '24:00:00' AND termtime = '0000-00-00 00:00:00' AND done = 0 AND act_date < NOW()";
break;
case 3:
$query .= "termdauer != '24:00:00' AND done = 1 AND act_date < NOW()";
break;
case 4:
$query .= "termdauer != '24:00:00' AND act_date > NOW()";
break;
case 5:
$query .= "termdauer = '24:00:00'";
break;
// 1 ist die uebliche Ansicht und faengt zugleich unbekannte Werte ab
default:
$query .= "termdauer != '24:00:00' AND termtime != '0000-00-00 00:00:00' AND done = 0 AND act_date < NOW()";
break;
}
foreach($src AS $tag)
{
$tag = "%".$tag."%";
$query .= " $combiner (termin.head LIKE '$tag' OR
termin.text LIKE '$tag' OR
adresse.firma LIKE '$tag' OR
adresse.vname LIKE '$tag' OR
adresse.nname LIKE '$tag' OR
ort LIKE '$tag' OR
plz LIKE '$tag' OR
str LIKE '$tag' OR
DATE_FORMAT(termtime, '%d.%m.%Y %H:%i') LIKE '$tag')";
$query .= " $combiner (termin.head LIKE ? OR
termin.text LIKE ? OR
adresse.firma LIKE ? OR
adresse.vname LIKE ? OR
adresse.nname LIKE ? OR
ort LIKE ? OR
plz LIKE ? OR
str LIKE ? OR
DATE_FORMAT(termtime, '%d.%m.%Y %H:%i') LIKE ?)";
for($i = 0; $i < 9; $i++)
$params[] = $tag;
}
if($termtyp == 2)
if((int)$termtyp == 2)
$query .= " GROUP BY main_id ORDER BY ort ASC, termtime DESC";
elseif($termtyp != 5 && $termtyp != 3)
elseif((int)$termtyp != 5 && (int)$termtyp != 3)
$query .= " GROUP BY main_id ORDER BY termtime ASC";
else
$query .= " GROUP BY main_id ORDER BY termtime DESC";
$ret["all"] = $query;
$query .= " LIMIT ".$page*$pagesize.", $pagesize";
$ret["limited"] = $query;
$ret["limited"] = $query." LIMIT ".((int)$page*(int)$pagesize).", ".(int)$pagesize;
$ret["params"] = $params;
return $ret;
}
@@ -502,7 +522,8 @@ function displayList($header,$entries,$speciallines=array(),$colors=array())
echo "<tr style='background-color: #e2e2e2;'>";
if(isset($colors[$i]))
echo "<td bgcolor='".$colors[$i]."' style='box-shadow: inset 0 0 0.7em #000; border: 1px solid black; border-top-left-radius: 0.5em; border-bottom-left-radius: 0.5em;'></td>";
// die Farbe kommt aus der Benutzerverwaltung, nicht aus dem Code
echo "<td bgcolor='".htmlspecialchars((string)$colors[$i])."' style='box-shadow: inset 0 0 0.7em #000; border: 1px solid black; border-top-left-radius: 0.5em; border-bottom-left-radius: 0.5em;'></td>";
foreach($entry AS $element)
{