require_once __DIR__ . '/db.php'; require_once __DIR__ . '/MailService.php'; class EmailVerification { private PDO $pdo; private string $baseUrl; private MailService $mailService; public function __construct(PDO $pdo, array $options = []) { $this->pdo = $pdo; $this->baseUrl = rtrim((string)($options['base_url'] ?? getenv('APP_BASE_URL') ?? 'http://localhost'), '/'); $this->mailService = new MailService([ 'from_email' => (string)($options['from_email'] ?? getenv('MAIL_FROM_ADDRESS') ?? 'no-reply@localhost'), 'from_name' => (string)($options['from_name'] ?? getenv('MAIL_FROM_NAME') ?? 'The Med World'), 'reply_to' => (string)($options['reply_to'] ?? getenv('MAIL_REPLY_TO') ?? getenv('MAIL_FROM_ADDRESS') ?? 'no-reply@localhost'), 'smtp_provider' => (string)($options['smtp_provider'] ?? getenv('MAIL_SMTP_PROVIDER') ?? 'gmail'), 'smtp_host' => (string)($options['smtp_host'] ?? getenv('MAIL_SMTP_HOST') ?? 'smtp.gmail.com'), 'smtp_port' => (int)($options['smtp_port'] ?? getenv('MAIL_SMTP_PORT') ?? 587), 'smtp_username' => (string)($options['smtp_username'] ?? getenv('MAIL_SMTP_USERNAME') ?? ''), 'smtp_password' => (string)($options['smtp_password'] ?? getenv('MAIL_SMTP_PASSWORD') ?? ''), 'smtp_secure' => (string)($options['smtp_secure'] ?? getenv('MAIL_SMTP_SECURE') ?? getenv('MAIL_SMTP_ENCRYPTION') ?? 'tls'), 'smtp_auth_enabled' => filter_var($options['smtp_auth_enabled'] ?? getenv('MAIL_SMTP_AUTH_ENABLED') ?? true, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? true, 'smtp_verify_peer' => filter_var($options['smtp_verify_peer'] ?? getenv('MAIL_SMTP_VERIFY_PEER') ?? true, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? true, 'smtp_verify_peer_name' => filter_var($options['smtp_verify_peer_name'] ?? getenv('MAIL_SMTP_VERIFY_PEER_NAME') ?? true, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? true, 'smtp_allow_self_signed' => filter_var($options['smtp_allow_self_signed'] ?? getenv('MAIL_SMTP_ALLOW_SELF_SIGNED') ?? false, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ?? false, 'smtp_timeout' => (int)($options['smtp_timeout'] ?? getenv('MAIL_SMTP_TIMEOUT') ?? 10), 'google_client_id' => getenv('GOOGLE_CLIENT_ID') ?: '', 'google_client_secret' => getenv('GOOGLE_CLIENT_SECRET') ?: '', 'google_refresh_token' => getenv('GOOGLE_REFRESH_TOKEN') ?: '', 'google_email_address' => getenv('GOOGLE_GMAIL_ADDRESS') ?: '', ]); } public function sendVerificationEmail(string $accountId, ?string $recipientEmail = null, ?string $recipientName = null): bool { if (!$this->isVerificationEnabled()) { return $this->markUserVerified($accountId); } $this->ensureVerificationColumns(); $user = $this->getUserByAccount($accountId); if ($user === null) { throw new RuntimeException('Kullanıcı hesabı bulunamadı.'); } if ((int)($user['is_verified'] ?? 0) === 1) { return true; } $recipientEmail = $recipientEmail ?: (string)($user['Email'] ?? ''); $recipientName = $recipientName ?: ((string)($user['strAccountID'] ?? $recipientEmail)); if (!filter_var($recipientEmail, FILTER_VALIDATE_EMAIL)) { throw new RuntimeException('Geçerli bir e-posta adresi bulunamadı.'); } $tokenPlain = $this->generateSecureToken(); $tokenHash = $this->hashToken($tokenPlain); $now = new DateTimeImmutable('now', new DateTimeZone('UTC')); $expiresAt = $now->add(new DateInterval('PT30M')); $stmt = $this->pdo->prepare( 'UPDATE dbo.TB_USER SET verification_token = :token, token_expires_at = :expiresAt, last_requested_at = :lastRequestedAt WHERE strAccountID = :accountId' ); $stmt->bindValue(':token', $tokenHash, PDO::PARAM_STR); $stmt->bindValue(':expiresAt', $expiresAt->format('Y-m-d H:i:s'), PDO::PARAM_STR); $stmt->bindValue(':lastRequestedAt', $now->format('Y-m-d H:i:s'), PDO::PARAM_STR); $stmt->bindValue(':accountId', $accountId, PDO::PARAM_STR); if (!$stmt->execute()) { throw new RuntimeException('Doğrulama bilgileri hazırlanamadı.'); } $verificationLink = $this->buildVerificationLink($tokenPlain); $subject = 'E-posta Doğrulama'; $htmlBody = $this->buildEmailBody($recipientName, $verificationLink); $plainBody = $this->buildPlainTextBody($recipientName, $verificationLink); return $this->mailService->send($recipientEmail, $recipientName, $subject, $htmlBody, $plainBody); } public function resendVerificationEmail(string $accountId): bool { return $this->sendVerificationEmail($accountId); } public function verifyToken(string $tokenPlain): bool { $this->ensureVerificationColumns(); $tokenHash = $this->hashToken($tokenPlain); $stmt = $this->pdo->prepare( 'SELECT TOP 1 ID, token_expires_at FROM dbo.TB_USER WHERE verification_token = :token' ); $stmt->bindValue(':token', $tokenHash, PDO::PARAM_STR); $stmt->execute(); $user = $stmt->fetch(PDO::FETCH_ASSOC); if ($user === false || empty($user)) { return false; } $expiresAt = new DateTimeImmutable((string)$user['token_expires_at'], new DateTimeZone('UTC')); if ($expiresAt < new DateTimeImmutable('now', new DateTimeZone('UTC'))) { return false; } $update = $this->pdo->prepare( 'UPDATE dbo.TB_USER SET is_verified = 1, verification_token = NULL, token_expires_at = NULL, last_requested_at = NULL WHERE ID = :userId' ); $update->bindValue(':userId', (int)$user['ID'], PDO::PARAM_INT); return $update->execute(); } public function markUserVerified(string $accountId): bool { $this->ensureVerificationColumns(); $stmt = $this->pdo->prepare( 'UPDATE dbo.TB_USER SET is_verified = 1, verification_token = NULL, token_expires_at = NULL, last_requested_at = NULL WHERE strAccountID = :accountId' ); $stmt->bindValue(':accountId', $accountId, PDO::PARAM_STR); return $stmt->execute(); } private function ensureVerificationColumns(): void { if (!$this->pdo instanceof PDO) { return; } $columns = [ 'is_verified' => "ALTER TABLE dbo.TB_USER ADD is_verified TINYINT NOT NULL DEFAULT 0", 'verification_token' => "ALTER TABLE dbo.TB_USER ADD verification_token VARCHAR(64) NULL", 'token_expires_at' => "ALTER TABLE dbo.TB_USER ADD token_expires_at DATETIME NULL", 'last_requested_at' => "ALTER TABLE dbo.TB_USER ADD last_requested_at DATETIME NULL", ]; foreach ($columns as $column => $alterSql) { $sql = "IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'TB_USER' AND COLUMN_NAME = :column) " . $alterSql; $stmt = $this->pdo->prepare($sql); $stmt->bindValue(':column', $column, PDO::PARAM_STR); $stmt->execute(); } } private function getUserByAccount(string $accountId): ?array { $stmt = $this->pdo->prepare( 'SELECT TOP 1 ID, strAccountID, Email, is_verified FROM dbo.TB_USER WHERE strAccountID = :accountId' ); $stmt->bindValue(':accountId', $accountId, PDO::PARAM_STR); $stmt->execute(); $user = $stmt->fetch(PDO::FETCH_ASSOC); return $user === false ? null : $user; } private function buildVerificationLink(string $tokenPlain): string { return $this->baseUrl . '/public/verify.php?token=' . rawurlencode($tokenPlain); } private function generateSecureToken(): string { return bin2hex(random_bytes(32)); } private function hashToken(string $tokenPlain): string { return hash('sha256', $tokenPlain); } private function buildEmailBody(string $recipientName, string $verificationLink): string { return sprintf( '

Merhaba %s,

Hesabınızı doğrulamak için aşağıdaki bağlantıya tıklayın:

Doğrulama bağlantısı

Bu bağlantı 30 dakika içinde geçerlidir.

', htmlspecialchars($recipientName, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), htmlspecialchars($verificationLink, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ); } private function buildPlainTextBody(string $recipientName, string $verificationLink): string { return sprintf( "Merhaba %s,\n\nHesabınızı doğrulamak için aşağıdaki bağlantıya tıklayın:\n%s\n\nBu bağlantı 30 dakika içinde geçerlidir.", $recipientName, $verificationLink ); } private function isVerificationEnabled(): bool { return is_email_verification_enabled(); } } The Med World | Login

Login

Manage your security settings and character details here.

Secure Access
9 - 2 = ? Refresh