| 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 : |
<?php
/**
* partner-reset.php – ÖFFENTLICHE Passwort-vergessen-Funktion für das Partnerportal.
* Kein Login nötig. Versand des Zurücksetzen-Links über den hinterlegten SMTP-Server
* (mailservice::send → PHPMailer). Tokens liegen gehasht in pacim_password_resets.
*
* action=request POST email -> { ok } (immer generisch, kein Account-Leak)
* action=verify GET token -> { ok, valid } (Link noch gültig?)
* action=reset POST token,password,password2 -> { ok } | { ok:false, error, field? }
*
* Nur Partner-/Vermittler-Konten (user_level = BROKER_LEVEL) können zurückgesetzt werden.
*/
require_once( __DIR__ . '/includes/config.inc.php' );
@ini_set( 'display_errors', '0' );
error_reporting( 0 );
ob_start();
function prr_json( $a ) { while ( ob_get_level() > 0 ) ob_end_clean(); header( 'Content-Type: application/json; charset=utf-8' ); echo json_encode( $a ); exit(); }
function prr_fail( $msg, $field = '' ) { prr_json( array( 'ok' => false, 'error' => $msg, 'field' => $field ) ); }
$RESET_TTL_MIN = 60; // Gültigkeit des Links in Minuten
$db = new MysqliDb( MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB );
$LVL = (int) BROKER_LEVEL;
$action = isset( $_GET['action'] ) ? $_GET['action'] : ( $_POST['action'] ?? '' );
/* Hilfsfunktion: gültigen, unbenutzten Token-Datensatz zu einem Klartext-Token holen. */
function prr_find_token( $db, $token ) {
$token = (string) $token;
if ( $token === '' || strlen( $token ) < 16 ) return null;
$hash = hash( 'sha256', $token );
return $db->rawQueryOne(
"SELECT * FROM pacim_password_resets WHERE reset_token_hash = ? AND reset_used = 0 AND reset_expires > NOW() LIMIT 1",
array( $hash )
);
}
/* ---------------------------------------------------------------- anfordern */
if ( $action === 'request' ) {
if ( $_SERVER['REQUEST_METHOD'] !== 'POST' ) { http_response_code( 405 ); prr_fail( 'Methode nicht erlaubt.' ); }
// Spam-Schutz: verstecktes Honeypot-Feld muss leer sein
if ( trim( (string) ( $_POST['website'] ?? '' ) ) !== '' ) prr_json( array( 'ok' => true ) );
$email = trim( (string) ( $_POST['email'] ?? '' ) );
if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) prr_fail( 'Bitte eine gültige E-Mail-Adresse angeben.', 'email' );
// Nur Partner-/Vermittler-Konten
$u = $db->rawQueryOne(
'SELECT user_id, user_firstname, user_lastname, user_email FROM pacim_users WHERE user_email = ? AND user_level = ? LIMIT 1',
array( $email, $LVL )
);
if ( $u ) {
$now = date( 'Y-m-d H:i:s' );
// Alte, noch offene Tokens dieses Nutzers entwerten (nur der jüngste Link gilt).
$db->rawQuery( 'UPDATE pacim_password_resets SET reset_used = 1 WHERE reset_user = ? AND reset_used = 0', array( (int) $u['user_id'] ) );
$token = bin2hex( random_bytes( 32 ) ); // 64 Hex-Zeichen
$db->insert( 'pacim_password_resets', array(
'reset_user' => (int) $u['user_id'],
'reset_token_hash' => hash( 'sha256', $token ),
'reset_expires' => date( 'Y-m-d H:i:s', time() + $GLOBALS['RESET_TTL_MIN'] * 60 ),
'reset_used' => 0,
'reset_ip' => substr( (string) ( $_SERVER['REMOTE_ADDR'] ?? '' ), 0, 45 ),
'reset_created' => $now,
) );
$link = SITE_URL . '/partner/passwort-zuruecksetzen/?token=' . $token;
$name = trim( (string) $u['user_firstname'] . ' ' . (string) $u['user_lastname'] );
$html = prr_mail_html( $name, $link, $GLOBALS['RESET_TTL_MIN'] );
// Versand über den hinterlegten SMTP-Server. Fehler bleiben für den Nutzer unsichtbar (kein Account-Leak).
if ( class_exists( 'mailservice' ) ) {
mailservice::send( $email, $name, 'Passwort zurücksetzen · Parken-am-Schiff.de Partnerprogramm', $html );
}
}
// Immer generische Antwort – verrät nicht, ob die Adresse existiert.
prr_json( array( 'ok' => true ) );
}
/* ------------------------------------------------------------- link prüfen */
if ( $action === 'verify' ) {
$row = prr_find_token( $db, $_GET['token'] ?? '' );
prr_json( array( 'ok' => true, 'valid' => (bool) $row ) );
}
/* --------------------------------------------------------------- neu setzen */
if ( $action === 'reset' ) {
if ( $_SERVER['REQUEST_METHOD'] !== 'POST' ) { http_response_code( 405 ); prr_fail( 'Methode nicht erlaubt.' ); }
$token = (string) ( $_POST['token'] ?? '' );
$password = (string) ( $_POST['password'] ?? '' );
$password2 = (string) ( $_POST['password2'] ?? '' );
if ( strlen( $password ) < 8 ) prr_fail( 'Das Passwort muss mindestens 8 Zeichen lang sein.', 'password' );
if ( $password !== $password2 ) prr_fail( 'Die beiden Passwörter stimmen nicht überein.', 'password2' );
$row = prr_find_token( $db, $token );
if ( ! $row ) prr_fail( 'Der Link ist ungültig oder abgelaufen. Bitte fordern Sie einen neuen an.', 'token' );
// Sicherheitshalber: Konto muss noch ein Partner-Konto sein.
$u = $db->rawQueryOne( 'SELECT user_id FROM pacim_users WHERE user_id = ? AND user_level = ? LIMIT 1', array( (int) $row['reset_user'], $LVL ) );
if ( ! $u ) prr_fail( 'Der Link ist ungültig oder abgelaufen. Bitte fordern Sie einen neuen an.', 'token' );
try {
$db->startTransaction();
$db->where( 'user_id', (int) $row['reset_user'] );
$ok = $db->update( 'pacim_users', array(
'user_password' => password_hash( $password, PASSWORD_DEFAULT ),
'user_updated' => date( 'Y-m-d H:i:s' ),
) );
if ( ! $ok ) throw new Exception( 'update failed' );
// Diesen Token + alle übrigen offenen Tokens des Nutzers entwerten.
$db->rawQuery( 'UPDATE pacim_password_resets SET reset_used = 1 WHERE reset_user = ?', array( (int) $row['reset_user'] ) );
$db->commit();
} catch ( Exception $e ) {
$db->rollback();
prr_fail( 'Das Passwort konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.' );
}
prr_json( array( 'ok' => true ) );
}
http_response_code( 400 );
prr_fail( 'Unbekannte Aktion.' );
/* ---------------------------------------------------------------- Mail-HTML */
function prr_mail_html( $name, $link, $ttlMin ) {
$co = function_exists( 'pacim_company' ) ? pacim_company() : array();
$brand = '#0b4677';
$firma = trim( ( $co['name'] ?? 'SeaSide Travel GmbH' ) . ' ' . ( $co['suffix'] ?? '' ) );
$greet = $name !== '' ? ( 'Hallo ' . htmlspecialchars( $name ) . ',' ) : 'Hallo,';
$linkE = htmlspecialchars( $link );
$foot = htmlspecialchars( $firma );
return '<!doctype html><html><body style="margin:0;padding:0;background:#f4f6f9;font-family:Arial,Helvetica,sans-serif;color:#1f2933;">'
. '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f4f6f9;padding:24px 0;"><tr><td align="center">'
. '<table role="presentation" width="560" cellpadding="0" cellspacing="0" style="max-width:560px;width:100%;background:#ffffff;border-radius:10px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,.06);">'
. '<tr><td style="background:' . $brand . ';padding:20px 28px;color:#fff;font-size:18px;font-weight:700;">Parken-am-Schiff.de · Partnerprogramm</td></tr>'
. '<tr><td style="padding:28px;">'
. '<h1 style="margin:0 0 14px;font-size:20px;color:#102a43;">Passwort zurücksetzen</h1>'
. '<p style="margin:0 0 12px;line-height:1.55;">' . $greet . '</p>'
. '<p style="margin:0 0 18px;line-height:1.55;">Sie haben angefordert, Ihr Passwort für das Partnerportal zurückzusetzen. Klicken Sie auf den folgenden Button, um ein neues Passwort zu vergeben:</p>'
. '<p style="margin:0 0 22px;text-align:center;"><a href="' . $linkE . '" style="display:inline-block;background:' . $brand . ';color:#fff;text-decoration:none;font-weight:600;padding:12px 26px;border-radius:8px;">Neues Passwort festlegen</a></p>'
. '<p style="margin:0 0 8px;line-height:1.55;font-size:13px;color:#52606d;">Der Link ist <strong>' . (int) $ttlMin . ' Minuten</strong> gültig. Falls der Button nicht funktioniert, kopieren Sie diese Adresse in Ihren Browser:</p>'
. '<p style="margin:0 0 18px;word-break:break-all;font-size:12px;color:#52606d;"><a href="' . $linkE . '" style="color:' . $brand . ';">' . $linkE . '</a></p>'
. '<p style="margin:0;line-height:1.55;font-size:13px;color:#52606d;">Wenn Sie diese Anfrage nicht gestellt haben, können Sie diese E-Mail ignorieren – Ihr Passwort bleibt unverändert.</p>'
. '</td></tr>'
. '<tr><td style="padding:16px 28px;background:#f0f2f5;color:#7b8794;font-size:12px;">' . $foot . '</td></tr>'
. '</table></td></tr></table></body></html>';
}