| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/clients/client1/seaside.pacim.de/web/ |
Upload File : |
<?php
/**
* chat-api.php – JSON-Endpunkt des internen Team-Chats (ein globaler Raum).
* Erreichbar nur für eingeloggte Clients (CRM-Session / gültiger Mobile-Token / Terminal-Sitzung).
*
* action= init | set-name | send | poll | heartbeat | mark-read | open-ref
* Gemeinsame Felder: client_id (localStorage-UUID), je nach Kontext t=<mobile-token> oder th=<terminal-hash>.
*/
require_once( __DIR__ . '/includes/config.inc.php' );
require_once( __DIR__ . '/includes/mobileaccess.php' ); // Token-Identität/Scope (nicht global geladen)
@ini_set( 'display_errors', '0' );
error_reporting( 0 );
function chat_json( $a ) { while ( ob_get_level() > 0 ) ob_end_clean(); header( 'Content-Type: application/json; charset=utf-8' ); header( 'Cache-Control: no-store' ); echo json_encode( $a ); exit(); }
function chat_fail( $m, $code = 200 ) { http_response_code( $code ); chat_json( array( 'ok' => false, 'error' => $m ) ); }
// Eigene Verbindung mit utf8mb4 (Emoji-Unterstützung; Standard-Handles laufen auf utf8mb3).
$db = new MysqliDb( MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB, null, 'utf8mb4' );
$ident = pacim_chat_identity( $db );
if ( empty( $ident['ok'] ) ) chat_fail( 'Nicht angemeldet.', 403 );
$action = (string) ( $_POST['action'] ?? ( $_GET['action'] ?? '' ) );
$clientId = (string) ( $_POST['client_id'] ?? ( $_GET['client_id'] ?? '' ) );
$profile = pacim_chat_profile( $db, $clientId, $ident );
if ( ! $profile ) chat_fail( 'Ungültige Client-ID.' );
$cpId = (int) $profile['cp_id'];
/** Standard-Status-Block (online, unread, last_id) für init/poll/send. */
function chat_state( $db, $cpId, $lastReadId ) {
$max = $db->rawQueryOne( "SELECT MAX(cm_id) AS m FROM pacim_chat_message WHERE cm_deleted_at IS NULL" );
return array(
'online' => pacim_chat_online_names( $db ),
'unread' => pacim_chat_unread( $db, $cpId, $lastReadId ),
'last_id' => $max ? (int) $max['m'] : 0,
);
}
/* --------------------------------------------------------------- init */
if ( $action === 'init' ) {
pacim_chat_touch( $db, $cpId );
$name = (string) $profile['cp_display_name'];
$msgs = array();
foreach ( pacim_chat_messages( $db, 0, 60 ) as $r ) $msgs[] = pacim_chat_msg_out( $r, $cpId );
$st = chat_state( $db, $cpId, (int) $profile['cp_last_read_id'] );
chat_json( array_merge( array(
'ok' => true,
'name' => $name,
'needs_name'=> ( trim( $name ) === '' ),
'suggested' => pacim_chat_clean_name( $ident['name'] ),
'messages' => $msgs,
), $st ) );
}
/* --------------------------------------------------------------- set-name */
if ( $action === 'set-name' ) {
$name = pacim_chat_clean_name( $_POST['name'] ?? '' );
if ( $name === '' ) chat_fail( 'Bitte einen Namen eingeben.' );
$db->where( 'cp_id', $cpId );
$db->update( 'pacim_chat_profile', array( 'cp_display_name' => $name, 'cp_updated_at' => date( 'Y-m-d H:i:s' ) ) );
chat_json( array( 'ok' => true, 'name' => $name ) );
}
/* --------------------------------------------------------------- send */
if ( $action === 'send' ) {
if ( $_SERVER['REQUEST_METHOD'] !== 'POST' ) chat_fail( 'Methode nicht erlaubt.' );
$name = trim( (string) $profile['cp_display_name'] );
if ( $name === '' ) chat_fail( 'Bitte zuerst einen Namen festlegen.' );
if ( pacim_chat_rate_exceeded( $db, $cpId ) ) chat_fail( 'Zu viele Nachrichten – bitte kurz warten.' );
$type = (string) ( $_POST['type'] ?? 'text' );
$now = date( 'Y-m-d H:i:s' );
$ins = array( 'cm_sender_id' => $cpId, 'cm_sender_name' => mb_substr( $name, 0, PACIM_CHAT_NAME_MAX ), 'cm_created_at' => $now );
if ( $type === 'booking_reference' ) {
$bid = (int) ( $_POST['booking_id'] ?? 0 );
if ( $bid <= 0 ) chat_fail( 'Keine Buchung angegeben.' );
// Sender muss die Buchung selbst sehen dürfen (keine fremden Daten posten).
list( $okRef ) = pacim_chat_resolve_booking( $db, $ident, $bid );
if ( ! $okRef ) chat_fail( 'Keine Berechtigung für diese Buchung.' );
$meta = pacim_chat_booking_meta( $db, $bid );
if ( ! $meta ) chat_fail( 'Buchung nicht gefunden.' );
$ins['cm_type'] = 'booking_reference';
$ins['cm_entity_type'] = 'booking';
$ins['cm_entity_id'] = $bid;
$ins['cm_meta'] = json_encode( $meta, JSON_UNESCAPED_UNICODE );
$ins['cm_text'] = ''; // unkritisch – Anzeige rein über meta-Badge
} else {
$text = (string) ( $_POST['text'] ?? '' );
$text = preg_replace( '/\r\n?/', "\n", $text ); // Zeilenumbrüche normalisieren
$text = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $text ); // Steuerzeichen raus (\n bleibt)
$text = trim( $text );
if ( $text === '' ) chat_fail( 'Leere Nachricht.' );
if ( mb_strlen( $text ) > PACIM_CHAT_MSG_MAX ) $text = mb_substr( $text, 0, PACIM_CHAT_MSG_MAX );
$ins['cm_type'] = 'text';
$ins['cm_text'] = $text;
}
$id = (int) $db->insert( 'pacim_chat_message', $ins );
if ( ! $id ) chat_fail( 'Senden fehlgeschlagen.' );
pacim_chat_touch( $db, $cpId );
// Eigene Nachricht gilt sofort als gelesen.
$db->where( 'cp_id', $cpId );
$db->update( 'pacim_chat_profile', array( 'cp_last_read_id' => $id ) );
$row = $db->rawQueryOne( "SELECT * FROM pacim_chat_message WHERE cm_id = ?", array( $id ) );
chat_json( array( 'ok' => true, 'message' => pacim_chat_msg_out( $row, $cpId ) ) );
}
/* --------------------------------------------------------------- poll */
if ( $action === 'poll' ) {
pacim_chat_touch( $db, $cpId );
$since = (int) ( $_POST['since'] ?? ( $_GET['since'] ?? 0 ) );
$msgs = array();
foreach ( pacim_chat_messages( $db, $since, 100 ) as $r ) $msgs[] = pacim_chat_msg_out( $r, $cpId );
$st = chat_state( $db, $cpId, (int) $profile['cp_last_read_id'] );
chat_json( array_merge( array( 'ok' => true, 'messages' => $msgs ), $st ) );
}
/* --------------------------------------------------------------- heartbeat */
if ( $action === 'heartbeat' ) {
pacim_chat_touch( $db, $cpId );
chat_json( array( 'ok' => true ) );
}
/* --------------------------------------------------------------- mark-read */
if ( $action === 'mark-read' ) {
$upTo = (int) ( $_POST['up_to'] ?? 0 );
if ( $upTo <= 0 ) { $m = $db->rawQueryOne( "SELECT MAX(cm_id) AS m FROM pacim_chat_message WHERE cm_deleted_at IS NULL" ); $upTo = $m ? (int) $m['m'] : 0; }
if ( $upTo > (int) $profile['cp_last_read_id'] ) {
$db->where( 'cp_id', $cpId );
$db->update( 'pacim_chat_profile', array( 'cp_last_read_id' => $upTo ) );
}
chat_json( array( 'ok' => true, 'unread' => 0 ) );
}
/* --------------------------------------------------------------- open-ref (Rechteprüfung) */
if ( $action === 'open-ref' ) {
$bid = (int) ( $_POST['booking_id'] ?? ( $_GET['booking_id'] ?? 0 ) );
list( $ok, $url ) = pacim_chat_resolve_booking( $db, $ident, $bid );
if ( ! $ok ) chat_fail( 'Du hast keine Berechtigung, diese Buchung zu öffnen.' );
chat_json( array( 'ok' => true, 'url' => $url ) );
}
chat_fail( 'Unbekannte Aktion.' );