403Webshell
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/seaside.pacim.de/web/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/seaside.pacim.de/web/booking-mail.php
<?php
/**
 * booking-mail.php – „E-Mail erstellen" in der Buchungsansicht (Admin).
 * Liefert auswählbare Templates als Karten (mit Vorschau, Platzhalter bereits durch
 * die echten Daten der Buchung ersetzt) und versendet die im Modal bearbeitete Mail.
 * Anhänge werden automatisch aufgelöst; beim Versand greifen Konto & BCC der zum
 * Template angelegten Regel.
 *
 * Aktionen:
 *   templates  GET invoice_id            -> { to, templates:[{id,name,type,subject,html,attachments[]}] }
 *   send       POST invoice_id, template_id, to, subject, html -> versendet + protokolliert
 */
require_once( __DIR__ . '/includes/config.inc.php' );
@ini_set( 'display_errors', '0' );
error_reporting( 0 );
ob_start();

function bm_json( $a ) { while ( ob_get_level() > 0 ) ob_end_clean(); header( 'Content-Type: application/json; charset=utf-8' ); echo json_encode( $a ); exit(); }
function bm_fail( $m ) { bm_json( array( 'ok' => false, 'error' => $m ) ); }

if ( ! function_exists( 'is_admin' ) || ! is_admin() ) { http_response_code( 403 ); bm_json( array( 'ok' => false, 'error' => 'forbidden' ) ); }

$action  = $_GET['action'] ?? ( $_POST['action'] ?? '' );
$invoice = (int) ( $_GET['invoice_id'] ?? ( $_POST['invoice_id'] ?? 0 ) );
$db      = new MysqliDb( MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB );
if ( $invoice <= 0 ) bm_fail( 'Ungültige Rechnung.' );

/* ---- Auswählbare Templates inkl. gerenderter Vorschau (echte Daten) ---- */
if ( $action === 'templates' ) {
    $ctx  = mailrenderer::context( $invoice );
    $db->rawQuery( "ALTER TABLE pacim_templates ADD COLUMN IF NOT EXISTS template_booking TINYINT(1) NOT NULL DEFAULT 0" );
    $rows = $db->rawQuery( "SELECT template_id, template_key, template_name, template_type FROM pacim_templates
                            WHERE template_active=1 AND template_booking=1 ORDER BY template_name" );
    $out = array();
    foreach ( (array) $rows as $t ) {
        $tpl = (object) $db->rawQueryOne( "SELECT * FROM pacim_templates WHERE template_id=?", array( (int) $t['template_id'] ) );
        $r   = mailrenderer::render( $tpl, $ctx, false );
        $att = mailrenderer::resolveAttachments( (int) $t['template_id'], $ctx );
        $names = array();
        foreach ( $att['files'] as $f ) $names[] = $f['name'];
        $out[] = array(
            'id' => (int) $t['template_id'], 'name' => $t['template_name'], 'type' => $t['template_key'],
            'subject' => $r['subject'], 'html' => $r['html'], 'attachments' => $names,
        );
    }
    bm_json( array( 'ok' => true, 'to' => trim( (string) ( $ctx['customer_email'] ?? '' ) ), 'templates' => $out ) );
}

/* ---- Eigenes Dokument hochladen (PDF) für den Versand ---- *
 * Speichert die Datei in datas/incoming/mailupload unter einem zufälligen Token und gibt
 * Token + bereinigten Anzeigenamen zurück. Beim Senden wird das Token (s. u.) angehängt.
 * Nur Admin (oben geprüft), nur PDF, max. 15 MB; alte Uploads (>24 h) werden mitaufgeräumt. */
if ( $action === 'upload' ) {
    if ( $_SERVER['REQUEST_METHOD'] !== 'POST' ) bm_fail( 'method' );
    if ( ! isset( $_FILES['file'] ) || ! is_uploaded_file( (string) ( $_FILES['file']['tmp_name'] ?? '' ) ) ) bm_fail( 'Keine Datei empfangen.' );
    $f = $_FILES['file'];
    if ( (int) $f['error'] !== UPLOAD_ERR_OK ) bm_fail( 'Upload fehlgeschlagen (Code ' . (int) $f['error'] . ').' );
    if ( (int) $f['size'] <= 0 || (int) $f['size'] > 15 * 1024 * 1024 ) bm_fail( 'Datei ist leer oder zu groß (max. 15 MB).' );
    $ext  = strtolower( pathinfo( (string) $f['name'], PATHINFO_EXTENSION ) );
    $mime = function_exists( 'finfo_open' ) ? @finfo_file( finfo_open( FILEINFO_MIME_TYPE ), $f['tmp_name'] ) : 'application/pdf';
    if ( $ext !== 'pdf' || stripos( (string) $mime, 'pdf' ) === false ) bm_fail( 'Nur PDF-Dateien sind erlaubt.' );
    $dir = rtrim( PATH_DATAS, '/' ) . '/incoming/mailupload';
    if ( ! is_dir( $dir ) ) @mkdir( $dir, 0775, true );
    // Aufräumen: Uploads älter als 24 h entfernen
    foreach ( (array) @glob( $dir . '/*.pdf' ) as $old ) { if ( is_file( $old ) && filemtime( $old ) < time() - 86400 ) @unlink( $old ); }
    $token = bin2hex( random_bytes( 16 ) ) . '.pdf';
    if ( ! @move_uploaded_file( $f['tmp_name'], $dir . '/' . $token ) ) bm_fail( 'Datei konnte nicht gespeichert werden.' );
    $name = preg_replace( '/[^\w.\- ]+/u', '', (string) $f['name'] );
    if ( $name === '' || strtolower( pathinfo( $name, PATHINFO_EXTENSION ) ) !== 'pdf' ) $name = 'Dokument.pdf';
    bm_json( array( 'ok' => true, 'token' => $token, 'name' => $name, 'size' => (int) $f['size'] ) );
}

/* ---- Versenden ---- *
 * Konsistent zur gated Mail-Pipeline: Die (im Modal bearbeitete) Mail wird als Snapshot
 * in pacim_messages abgelegt. Ist der lokale SMTP-Versand aktiv, wird sofort versendet
 * (Status 1), sonst landet sie in der Queue (Status 5) und geht nach Aktivierung raus.
 * Konto & BCC stammen aus der zum Template angelegten aktiven Regel. */
if ( $action === 'send' ) {
    if ( $_SERVER['REQUEST_METHOD'] !== 'POST' ) bm_fail( 'method' );
    $tplId   = (int) ( $_POST['template_id'] ?? 0 );
    $to      = trim( (string) ( $_POST['to'] ?? '' ) );
    $subject = (string) ( $_POST['subject'] ?? '' );
    $html    = (string) ( $_POST['html'] ?? '' );
    if ( ! filter_var( $to, FILTER_VALIDATE_EMAIL ) ) bm_fail( 'Bitte eine gültige Empfängeradresse angeben.' );
    if ( trim( $html ) === '' ) bm_fail( 'Kein Inhalt zum Versenden.' );

    $ctx = mailrenderer::context( $invoice );
    $files = array();
    if ( $tplId > 0 ) { $att = mailrenderer::resolveAttachments( $tplId, $ctx ); $files = $att['files']; }

    // Eigene hochgeladene Anhänge (Tokens aus action=upload); optional automatische Anhänge ersetzen.
    if ( ! empty( $_POST['skip_auto'] ) ) $files = array();
    $uploads = json_decode( (string) ( $_POST['uploads'] ?? '' ), true );
    if ( is_array( $uploads ) ) {
        $udir = rtrim( PATH_DATAS, '/' ) . '/incoming/mailupload';
        foreach ( $uploads as $u ) {
            $tok = isset( $u['token'] ) ? (string) $u['token'] : '';
            if ( ! preg_match( '/^[a-f0-9]{32}\.pdf$/', $tok ) ) continue; // nur eigene Token-Dateien
            $path = $udir . '/' . $tok;
            if ( ! is_file( $path ) ) continue;
            $nm = isset( $u['name'] ) ? preg_replace( '/[^\w.\- ]+/u', '', (string) $u['name'] ) : '';
            if ( $nm === '' || strtolower( pathinfo( $nm, PATHINFO_EXTENSION ) ) !== 'pdf' ) $nm = 'Dokument.pdf';
            $files[] = array( 'path' => $path, 'name' => $nm );
        }
    }

    // Konto & BCC aus der zum Template angelegten (aktiven) Regel übernehmen
    $opts = array(); $ruleAccount = 0; $ruleBcc = null;
    if ( $tplId > 0 ) {
        $rule = $db->rawQueryOne( "SELECT rule_account, rule_bcc FROM pacim_mail_rules WHERE rule_template=? AND rule_active=1 ORDER BY rule_id ASC LIMIT 1", array( $tplId ) );
        if ( $rule ) {
            if ( (int) $rule['rule_account'] > 0 ) { $ruleAccount = (int) $rule['rule_account']; $opts['account'] = $ruleAccount; }
            if ( trim( (string) $rule['rule_bcc'] ) !== '' ) { $ruleBcc = (string) $rule['rule_bcc']; $opts['bcc'] = $ruleBcc; }
        }
    }

    // Snapshot anlegen (erscheint in der E-Mail-Liste der Buchung)
    $cfg  = mailservice::config();
    $cust = $db->rawQueryOne( "SELECT invoice_customer FROM pacim_invoice WHERE invoice_id=?", array( $invoice ) );
    $bk   = $db->rawQueryOne( "SELECT booking_id FROM pacim_booking WHERE booking_invoice=? ORDER BY booking_id ASC LIMIT 1", array( $invoice ) );
    $live = mailservice::smtpEnabled();
    $msgId = $db->insert( 'pacim_messages', array(
        'message_status'   => 5, 'message_type' => 'system', 'message_count' => 0,
        'message_template' => $tplId, 'message_customer' => (int) ( $cust['invoice_customer'] ?? 0 ),
        'message_invoice'  => $invoice, 'message_booking' => (int) ( $bk['booking_id'] ?? 0 ),
        'message_subject'  => $subject, 'message_sender' => $cfg['from_email'], 'message_receiver' => $to,
        'message_account'  => $ruleAccount, 'message_bcc' => $ruleBcc,
        'message_content'  => $html, 'message_token' => pacim_sign( 'msg:' . uniqid( '', true ) ),
        'message_datetime' => date( 'Y-m-d H:i:s' ),
    ) );

    if ( ! $live ) {
        bm_json( array( 'ok' => true, 'queued' => true, 'message' => 'E-Mail in die Warteschlange gelegt – sie wird versendet, sobald der SMTP-Versand aktiviert ist.' ) );
    }

    // SMTP aktiv -> sofort versenden
    $res = mailservice::send( $to, '', $subject, $html, $files, $opts );
    if ( ! empty( $res['ok'] ) ) {
        $db->where( 'message_id', (int) $msgId );
        $db->update( 'pacim_messages', array( 'message_status' => 1, 'message_count' => 1, 'message_error' => null ) );
        bm_json( array( 'ok' => true, 'message' => 'E-Mail versendet.' ) );
    }
    // Fehler: in Queue belassen (Cron versucht erneut)
    $db->where( 'message_id', (int) $msgId );
    $db->update( 'pacim_messages', array( 'message_count' => 1, 'message_error' => mb_substr( (string) ( $res['error'] ?? 'unbekannt' ), 0, 500 ) ) );
    bm_fail( 'Versand fehlgeschlagen: ' . ( $res['error'] ?? 'unbekannt' ) . '. Die E-Mail bleibt in der Warteschlange und wird automatisch erneut versucht.' );
}

bm_fail( 'unknown_action' );

Youez - 2016 - github.com/yon3zu
LinuXploit