diff --git a/php/OPENSSL_SETUP.md b/php/OPENSSL_SETUP.md new file mode 100644 index 0000000..df6e21c --- /dev/null +++ b/php/OPENSSL_SETUP.md @@ -0,0 +1,100 @@ +# Enabling OpenSSL Extension in PHP (Windows) + +## Problem +Your PHP installation doesn't have the OpenSSL extension enabled, which is required for secure SMTP connections (TLS/SSL). + +Current error: `Unable to find the socket transport "ssl"` or `TLS/SSL transports not available` + +## Solution + +### Option 1: Enable OpenSSL in php.ini (Recommended) + +1. **Find your php.ini file:** + ```powershell + php --ini + ``` + + If it shows "Loaded Configuration File: (none)", you need to create one: + ```powershell + # Find PHP installation directory + where.exe php + + # Copy the example php.ini + cd C:\path\to\php + copy php.ini-development php.ini + ``` + +2. **Edit php.ini** and find this line: + ```ini + ;extension=openssl + ``` + + Remove the semicolon to uncomment it: + ```ini + extension=openssl + ``` + +3. **Restart your PHP server** (or command line if using built-in server) + +4. **Verify OpenSSL is enabled:** + ```powershell + php -m | Select-String -Pattern "openssl" + ``` + + Should output: `openssl` + +### Option 2: Enable OpenSSL temporarily via command line + +You can enable OpenSSL for a single command: + +```powershell +php -d extension=openssl test_email.php +``` + +Or for the built-in server: + +```powershell +php -d extension=openssl -S localhost:8000 +``` + +### Option 3: Install Composer and PHPMailer + +If you can't modify php.ini, install PHPMailer which may handle SMTP better: + +```powershell +# Install Composer first (https://getcomposer.org/) +# Then in the php/ directory: +composer require phpmailer/phpmailer +``` + +The application will automatically detect and use PHPMailer if available. + +## Verifying the Fix + +After enabling OpenSSL, run the test: + +```powershell +cd C:\Users\aky547\GitHub\SemapForm\php +php test_email.php +``` + +You should see: +- `Available transports: tcp, udp, ssl, tls` (or similar) +- `TLS encryption enabled successfully` +- `Email sent successfully via SMTP` + +## Alternative: Use a Different SMTP Port + +If you absolutely cannot enable OpenSSL, you could try: + +1. Contact your email administrator to see if there's an unencrypted SMTP port (not recommended for security) +2. Use a different email solution (e.g., send via a web service API instead of SMTP) + +## Production Deployment + +On a production server (Linux/shared hosting): + +- OpenSSL is usually enabled by default +- Check with `php -m | grep openssl` +- If not available, contact your hosting provider +- Most shared hosting environments have it pre-configured diff --git a/php/README.md b/php/README.md index 54126cb..84a0bb2 100644 --- a/php/README.md +++ b/php/README.md @@ -95,13 +95,61 @@ - Set environment variables through hosting control panel - Test mail() function first before adding SMTP +## Email Logging + +The application now includes comprehensive email logging: + +- **Log Location**: `php/mail.log` +- **Log Format**: `[YYYY-MM-DD HH:MM:SS] [LEVEL] Message` +- **Configuration**: + - Set `LOG_ENABLED = true` in `config.php` to enable logging + - Logs are also written to PHP error_log with `[MAIL]` prefix + +### What Gets Logged + +- Email send attempts with from/to/subject +- Email content length +- SMTP connection details (host, port, encryption) +- PHPMailer initialization and configuration steps +- Success/failure status for each send attempt +- Detailed error messages for failed sends +- When mail is disabled (includes full XML content) + +### Viewing Logs + +```bash +# View recent log entries +tail -f php/mail.log + +# View last 50 lines +tail -n 50 php/mail.log + +# Search for errors +grep ERROR php/mail.log + +# Search for successful sends +grep SUCCESS php/mail.log +``` + +### Log File Management + +The log file is automatically created when needed. To rotate logs: + +```bash +# Manual rotation +mv php/mail.log php/mail.log.$(date +%Y%m%d) +touch php/mail.log +chmod 666 php/mail.log +``` + ## Troubleshooting ### Emails not sending 1. Check `MAIL_ENABLED` is set to `true` -2. Verify SMTP credentials are correct -3. Check server error logs: `tail -f /var/log/apache2/error.log` -4. Test with `MAIL_ENABLED=false` to see XML output in logs +2. **Check `php/mail.log`** for detailed error messages +3. Verify SMTP credentials are correct +4. Check server error logs: `tail -f /var/log/apache2/error.log` +5. Test with `MAIL_ENABLED=false` to see XML output in logs ### Form validation errors - Ensure all required fields have values diff --git a/php/config.php b/php/config.php index 4db6933..a975136 100644 --- a/php/config.php +++ b/php/config.php @@ -11,19 +11,23 @@ error_reporting(E_ALL); ini_set('display_errors', 1); // Email configuration -define('MAIL_ENABLED', getenv('MAIL_ENABLED') !== false ? filter_var(getenv('MAIL_ENABLED'), FILTER_VALIDATE_BOOLEAN) : true); -define('SMTP_HOST', getenv('SMTP_HOST') ?: 'smtp.ph-freiburg.de'); -define('SMTP_PORT', getenv('SMTP_PORT') ?: 465); -define('SMTP_ENCRYPTION', 'ssl'); // 'ssl' or 'tls' or '' for none -define('SMTP_USERNAME', getenv('MAIL_USERNAME') ?: ''); -define('SMTP_PASSWORD', getenv('MAIL_PASSWORD') ?: ''); -define('MAIL_FROM', getenv('MAIL_FROM') ?: 'alexander.kirchner@ph-freiburg.de'); -define('MAIL_TO', getenv('MAIL_TO') ?: 'semesterapparate@ph-freiburg.de'); +define('MAIL_ENABLED', true);//getenv('MAIL_ENABLED') !== false ? filter_var(getenv('MAIL_ENABLED'), FILTER_VALIDATE_BOOLEAN) : true); +define('SMTP_HOST', 'smtp.ph-freiburg.de');//getenv('SMTP_HOST') ?: 'smtp.ph-freiburg.de'); +define('SMTP_PORT', 587);//getenv('SMTP_PORT') ?: 587); // Use 587 for TLS, 465 for SSL +define('SMTP_ENCRYPTION', 'tls'); // 'ssl' or 'tls' or '' for none +define('SMTP_USERNAME', 'aky547');//getenv('MAIL_USERNAME') ?: 'aky547'); +define('SMTP_PASSWORD', 'CMcDna3qPh2*n');//getenv('MAIL_PASSWORD') ?: 'CMcDna3qPh2*n'); +define('MAIL_FROM', 'alexander.kirchner@ph-freiburg.de');//getenv('MAIL_FROM') ?: 'alexander.kirchner@ph-freiburg.de'); +define('MAIL_TO', 'kirchneralexander020@gmail.com');//getenv('MAIL_TO') ?: 'kirchneralexander@proton.me'); // Application settings define('BASE_PATH', __DIR__); define('STATIC_PATH', '/static'); +// Logging configuration +define('LOG_FILE', BASE_PATH . '/mail.log'); +define('LOG_ENABLED', true); + // Signature validation API (optional Python service) define('SIGNATURE_API_URL', getenv('SIGNATURE_API_URL') ?: 'http://localhost:8001'); diff --git a/php/elsa.php b/php/elsa.php index b70b41b..0601f87 100644 --- a/php/elsa.php +++ b/php/elsa.php @@ -302,8 +302,8 @@ '' + '' + '' + - attachSignatureListeners(row); ''; + attachSignatureListeners(row); } } @@ -491,7 +491,9 @@ function removeMediaSection(sectionId) { const section = document.getElementById(sectionId); if (section) { - if (const rows = section.querySelectorAll('tr[id]'); + if (confirm('Möchten Sie diese Sektion wirklich entfernen?')) { + const type = section.getAttribute('data-type'); + const rows = section.querySelectorAll('tr[id]'); rows.forEach(row => { const signatureInput = row.querySelector('input[name*="_signature"]'); if (signatureInput) { @@ -504,9 +506,7 @@ btn.disabled = false; btn.title = 'Sektion hinzufügen'; } - updateSubmitButton(); btn.disabled = false; - btn.title = 'Sektion hinzufügen'; - } + updateSubmitButton(); } } } diff --git a/php/functions.php b/php/functions.php index aad384d..cd1cc4c 100644 --- a/php/functions.php +++ b/php/functions.php @@ -1,55 +1,79 @@ formatOutput = true; - + $root = $xml->createElement('form_submission'); $xml->appendChild($root); - + // Static data $static = $xml->createElement('static'); $root->appendChild($static); - + foreach (['name', 'lastname', 'title', 'telno', 'mail', 'apparatsname', 'subject', 'semester', 'dauerapparat'] as $field) { if (isset($data[$field])) { $element = $xml->createElement($field, htmlspecialchars($data[$field])); $static->appendChild($element); } } - + if (!empty($data['message'])) { $messageEl = $xml->createElement('message', htmlspecialchars($data['message'])); $static->appendChild($messageEl); } - + // Books if (isset($data['books']) && is_array($data['books'])) { $booksNode = $xml->createElement('books'); $root->appendChild($booksNode); - + foreach ($data['books'] as $book) { $bookNode = $xml->createElement('book'); $booksNode->appendChild($bookNode); - + foreach (['authorname', 'year', 'title', 'signature'] as $field) { $value = isset($book[$field]) ? htmlspecialchars($book[$field]) : ''; $fieldNode = $xml->createElement($field, $value); @@ -57,59 +81,69 @@ function generateXML($data) { } } } - + return $xml->saveXML(); } /** * Generate XML for ELSA form */ -function generateELSAXML($data) { +function generateELSAXML($data) +{ $xml = new DOMDocument('1.0', 'UTF-8'); $xml->formatOutput = true; - + $root = $xml->createElement('elsa_submission'); $xml->appendChild($root); - + // General info $generalInfo = $xml->createElement('general_info'); $root->appendChild($generalInfo); - - $generalFields = ['name', 'lastname', 'title', 'mail', 'subject', 'classname', - 'usage_date_from', 'usage_date_to', 'availability_date']; - + + $generalFields = [ + 'name', + 'lastname', + 'title', + 'mail', + 'subject', + 'classname', + 'usage_date_from', + 'usage_date_to', + 'availability_date' + ]; + foreach ($generalFields as $field) { if (isset($data[$field])) { $element = $xml->createElement($field, htmlspecialchars($data[$field])); $generalInfo->appendChild($element); } } - + if (!empty($data['message'])) { $messageEl = $xml->createElement('message', htmlspecialchars($data['message'])); $generalInfo->appendChild($messageEl); } - + // Media sections $mediaRoot = $xml->createElement('media'); $root->appendChild($mediaRoot); - + // Add different media types (monografie, zeitschrift, herausgeber) $mediaTypes = [ 'monografien' => $data['monografien'] ?? [], 'zeitschriftenartikel' => $data['zeitschriftenartikel'] ?? [], 'herausgeberwerke' => $data['herausgeberwerke'] ?? [] ]; - + foreach ($mediaTypes as $type => $entries) { if (!empty($entries)) { $section = $xml->createElement($type); $mediaRoot->appendChild($section); - + foreach ($entries as $entry) { $entryNode = $xml->createElement('entry'); $section->appendChild($entryNode); - + foreach ($entry as $key => $value) { $fieldNode = $xml->createElement($key, htmlspecialchars($value)); $entryNode->appendChild($fieldNode); @@ -117,7 +151,7 @@ function generateELSAXML($data) { } } } - + return $xml->saveXML(); } @@ -125,71 +159,251 @@ function generateELSAXML($data) { * Send email with XML attachment * Uses PHP's mail() function or SMTP if configured */ -function sendEmail($subject, $xmlContent, $toEmail = null) { +function sendEmail($subject, $xmlContent, $toEmail = null) +{ $to = $toEmail ?? MAIL_TO; $from = MAIL_FROM; - + + writeLog("=========================================================="); + writeLog("Email Send Attempt"); + writeLog("From: {$from}"); + writeLog("To: {$to}"); + writeLog("Subject: {$subject}"); + writeLog("Content Length: " . strlen($xmlContent) . " bytes"); + if (!MAIL_ENABLED) { - // Log instead of sending - error_log("=========================================================="); - error_log("MAIL SENDING DISABLED - Would have sent:"); - error_log("From: " . $from); - error_log("To: " . $to); - error_log("Subject: " . $subject); - error_log("----------------------------------------------------------"); - error_log($xmlContent); - error_log("=========================================================="); + writeLog("MAIL SENDING DISABLED - Email not sent", 'WARNING'); + writeLog("XML Content:\n" . $xmlContent); + writeLog("=========================================================="); return true; } - + // Try using SMTP if credentials are configured - if (SMTP_USERNAME && SMTP_PASSWORD && class_exists('PHPMailer\PHPMailer\PHPMailer')) { - return sendEmailSMTP($subject, $xmlContent, $to, $from); + if (SMTP_USERNAME && SMTP_PASSWORD) { + writeLog("SMTP credentials configured, attempting SMTP send"); + $result = sendEmailSMTP($subject, $xmlContent, $to, $from); + + if ($result) { + writeLog("Email sent successfully via SMTP", 'SUCCESS'); + } else { + writeLog("Email sending via SMTP failed", 'ERROR'); + } + + writeLog("=========================================================="); + return $result; } - - // Fallback to PHP mail() + + // Fallback to PHP mail() only if no SMTP credentials + writeLog("No SMTP credentials configured, using PHP mail() function"); $headers = "From: " . $from . "\r\n"; $headers .= "Content-Type: application/xml; charset=UTF-8\r\n"; $headers .= "X-Mailer: PHP/" . phpversion(); - - return mail($to, $subject, $xmlContent, $headers); + + $result = mail($to, $subject, $xmlContent, $headers); + + if ($result) { + writeLog("Email sent successfully via mail()", 'SUCCESS'); + } else { + writeLog("Email sending via mail() failed", 'ERROR'); + } + + writeLog("=========================================================="); + return $result; } /** - * Send email via SMTP using PHPMailer (if available) + * Send email via SMTP using PHPMailer (if available) or native PHP sockets */ -function sendEmailSMTP($subject, $xmlContent, $to, $from) { - if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { - error_log("PHPMailer not available, falling back to mail()"); +function sendEmailSMTP($subject, $xmlContent, $to, $from) +{ + // Try PHPMailer first if available + if (class_exists('PHPMailer\PHPMailer\PHPMailer')) { + writeLog("Initializing PHPMailer for SMTP send"); + writeLog("SMTP Host: " . SMTP_HOST . ":" . SMTP_PORT); + writeLog("SMTP Encryption: " . SMTP_ENCRYPTION); + writeLog("SMTP Username: " . SMTP_USERNAME); + + $mail = new PHPMailer\PHPMailer\PHPMailer(true); + + try { + // Server settings + $mail->isSMTP(); + $mail->Host = SMTP_HOST; + $mail->SMTPAuth = true; + $mail->Username = SMTP_USERNAME; + $mail->Password = SMTP_PASSWORD; + $mail->SMTPSecure = SMTP_ENCRYPTION; + $mail->Port = SMTP_PORT; + $mail->CharSet = 'UTF-8'; + + writeLog("SMTP connection configured"); + + // Recipients + $mail->setFrom($from); + $mail->addAddress($to); + + writeLog("Recipients configured"); + + // Content - XML as body + $mail->isHTML(false); + $mail->Subject = $subject; + $mail->Body = $xmlContent; + $mail->ContentType = 'text/plain'; + + writeLog("Sending email via SMTP..."); + $mail->send(); + + writeLog("SMTP send() completed successfully", 'SUCCESS'); + return true; + } catch (Exception $e) { + writeLog("SMTP Exception: " . $e->getMessage(), 'ERROR'); + writeLog("PHPMailer ErrorInfo: " . $mail->ErrorInfo, 'ERROR'); + return false; + } + } + + // Helper function to read SMTP multi-line response + $readResponse = function ($socket) { + $response = ''; + while ($line = fgets($socket, 515)) { + $response .= $line; + // Check if this is the last line (code followed by space, not hyphen) + if (preg_match('/^\d{3} /', $line)) { + break; + } + } + return $response; + }; + + // Fallback to native PHP SMTP + writeLog("PHPMailer not available, using native PHP SMTP implementation"); + writeLog("SMTP Host: " . SMTP_HOST . ":" . SMTP_PORT); + writeLog("SMTP Encryption: " . SMTP_ENCRYPTION); + + // Check if required transports are available + $transports = stream_get_transports(); + writeLog("Available transports: " . implode(', ', $transports)); + + if (SMTP_ENCRYPTION === 'ssl' && !in_array('ssl', $transports)) { + writeLog("SSL transport not available. Enable OpenSSL extension in PHP.", 'ERROR'); + writeLog("Try changing SMTP_ENCRYPTION to 'tls' and SMTP_PORT to 587 in config.php", 'ERROR'); return false; } - - $mail = new PHPMailer\PHPMailer\PHPMailer(true); - + try { - // Server settings - $mail->isSMTP(); - $mail->Host = SMTP_HOST; - $mail->SMTPAuth = true; - $mail->Username = SMTP_USERNAME; - $mail->Password = SMTP_PASSWORD; - $mail->SMTPSecure = SMTP_ENCRYPTION; - $mail->Port = SMTP_PORT; - $mail->CharSet = 'UTF-8'; - - // Recipients - $mail->setFrom($from); - $mail->addAddress($to); - - // Content - $mail->Subject = $subject; - $mail->Body = $xmlContent; - $mail->ContentType = 'application/xml'; - - $mail->send(); - return true; + // Build the connection string - always start with plain TCP for TLS + if (SMTP_ENCRYPTION === 'ssl') { + $host = 'ssl://' . SMTP_HOST; + } else { + $host = SMTP_HOST; + } + $timeout = 30; + + writeLog("Connecting to {$host}:" . SMTP_PORT); + $smtp = @fsockopen($host, SMTP_PORT, $errno, $errstr, $timeout); + + if (!$smtp) { + writeLog("Failed to connect: ({$errno}) {$errstr}", 'ERROR'); + return false; + } + + writeLog("Connected successfully"); + + // Read server response + $response = $readResponse($smtp); + writeLog("Server greeting: " . trim($response)); + + // Send EHLO + fputs($smtp, "EHLO " . SMTP_HOST . "\r\n"); + $response = $readResponse($smtp); + writeLog("EHLO response: " . trim(str_replace("\r\n", " | ", $response))); + + // Start TLS if needed and not using SSL + if (SMTP_ENCRYPTION === 'tls') { + fputs($smtp, "STARTTLS\r\n"); + $response = $readResponse($smtp); + writeLog("STARTTLS response: " . trim($response)); + + if (strpos($response, '220') === 0) { + if (in_array('tls', $transports) || in_array('ssl', $transports)) { + $crypto = @stream_socket_enable_crypto($smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); + if ($crypto) { + writeLog("TLS encryption enabled successfully"); + fputs($smtp, "EHLO " . SMTP_HOST . "\r\n"); + $response = $readResponse($smtp); + writeLog("EHLO after TLS: " . trim(str_replace("\r\n", " | ", $response))); + } else { + writeLog("Failed to enable TLS encryption", 'WARNING'); + writeLog("Continuing without encryption (not recommended)", 'WARNING'); + } + } else { + writeLog("TLS/SSL transports not available. Enable OpenSSL extension.", 'WARNING'); + writeLog("Continuing without encryption (not recommended)", 'WARNING'); + } + } + } + + // Authenticate + fputs($smtp, "AUTH LOGIN\r\n"); + $response = $readResponse($smtp); + writeLog("AUTH response: " . trim($response)); + + fputs($smtp, base64_encode(SMTP_USERNAME) . "\r\n"); + $response = $readResponse($smtp); + + fputs($smtp, base64_encode(SMTP_PASSWORD) . "\r\n"); + $response = $readResponse($smtp); + + if (strpos($response, '235') !== 0) { + writeLog("Authentication failed: " . trim($response), 'ERROR'); + fclose($smtp); + return false; + } + + writeLog("Authentication successful"); + + // Send MAIL FROM + fputs($smtp, "MAIL FROM: <{$from}>\r\n"); + $response = $readResponse($smtp); + writeLog("MAIL FROM response: " . trim($response)); + + // Send RCPT TO + fputs($smtp, "RCPT TO: <{$to}>\r\n"); + $response = $readResponse($smtp); + writeLog("RCPT TO response: " . trim($response)); + + // Send DATA + fputs($smtp, "DATA\r\n"); + $response = $readResponse($smtp); + writeLog("DATA response: " . trim($response)); + + // Send email headers and body + $headers = "From: {$from}\r\n"; + $headers .= "To: {$to}\r\n"; + $headers .= "Subject: {$subject}\r\n"; + $headers .= "Content-Type: text/plain; charset=UTF-8\r\n"; + $headers .= "X-Mailer: PHP/" . phpversion() . "\r\n"; + $headers .= "\r\n"; + + fputs($smtp, $headers . $xmlContent . "\r\n.\r\n"); + $response = $readResponse($smtp); + writeLog("Message response: " . trim($response)); + + // Quit + fputs($smtp, "QUIT\r\n"); + $readResponse($smtp); // Read QUIT response + fclose($smtp); + + if (strpos($response, '250') === 0) { + writeLog("Email sent successfully via native SMTP", 'SUCCESS'); + return true; + } else { + writeLog("Email sending failed: " . trim($response), 'ERROR'); + return false; + } + } catch (Exception $e) { - error_log("Email sending failed: " . $mail->ErrorInfo); + writeLog("Native SMTP Exception: " . $e->getMessage(), 'ERROR'); return false; } } @@ -197,7 +411,8 @@ function sendEmailSMTP($subject, $xmlContent, $to, $from) { /** * Redirect to URL */ -function redirect($url) { +function redirect($url) +{ header("Location: " . $url); exit; } @@ -205,14 +420,16 @@ function redirect($url) { /** * Get POST value with default */ -function post($key, $default = '') { +function post($key, $default = '') +{ return isset($_POST[$key]) ? sanitizeInput($_POST[$key]) : $default; } /** * Get all POST values matching a pattern (for arrays) */ -function postArray($key) { - return isset($_POST[$key]) && is_array($_POST[$key]) ? - array_map('sanitizeInput', $_POST[$key]) : []; +function postArray($key) +{ + return isset($_POST[$key]) && is_array($_POST[$key]) ? + array_map('sanitizeInput', $_POST[$key]) : []; }