feat: add OpenSSL setup guide and enhance email logging functionality

This commit is contained in:
2025-11-24 14:55:57 +01:00
parent 3a2926675a
commit 22b3fe9d0b
5 changed files with 462 additions and 93 deletions

100
php/OPENSSL_SETUP.md Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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');

View File

@@ -302,8 +302,8 @@
'<td><input type="text" name="herausgeber_signature[]" data-section="' + sectionId + '" placeholder="Optional"></td>' +
'<td><input type="number" name="herausgeber_pages_from[]" data-section="' + sectionId + '" required min="1"></td>' +
'<td><input type="number" name="herausgeber_pages_to[]" data-section="' + sectionId + '" required min="1"></td>' +
attachSignatureListeners(row);
'<td><button type="button" class="btn-icon" onclick="removeRow(\'' + rowId + '\')" title="Zeile entfernen"><span class="mdi mdi-delete"></span></button></td>';
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();
}
}
}

View File

@@ -1,24 +1,48 @@
<?php
require_once 'config.php';
/**
* Write log message to file
*/
function writeLog($message, $level = 'INFO')
{
if (!LOG_ENABLED) {
return;
}
$timestamp = date('Y-m-d H:i:s');
$logMessage = "[{$timestamp}] [{$level}] {$message}" . PHP_EOL;
// Also log to error_log for immediate visibility
error_log("[MAIL] {$message}");
// Write to log file
if (defined('LOG_FILE')) {
file_put_contents(LOG_FILE, $logMessage, FILE_APPEND | LOCK_EX);
}
}
/**
* Validate email address
*/
function validateEmail($email) {
function validateEmail($email)
{
return preg_match(EMAIL_REGEX, $email);
}
/**
* Sanitize input string
*/
function sanitizeInput($input) {
function sanitizeInput($input)
{
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
}
/**
* Generate XML from form data
*/
function generateXML($data) {
function generateXML($data)
{
$xml = new DOMDocument('1.0', 'UTF-8');
$xml->formatOutput = true;
@@ -64,7 +88,8 @@ function generateXML($data) {
/**
* Generate XML for ELSA form
*/
function generateELSAXML($data) {
function generateELSAXML($data)
{
$xml = new DOMDocument('1.0', 'UTF-8');
$xml->formatOutput = true;
@@ -75,8 +100,17 @@ function generateELSAXML($data) {
$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])) {
@@ -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';
// 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;
// Recipients
$mail->setFrom($from);
$mail->addAddress($to);
writeLog("Connecting to {$host}:" . SMTP_PORT);
$smtp = @fsockopen($host, SMTP_PORT, $errno, $errstr, $timeout);
// Content
$mail->Subject = $subject;
$mail->Body = $xmlContent;
$mail->ContentType = 'application/xml';
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;
}
$mail->send();
return true;
} 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) {
function postArray($key)
{
return isset($_POST[$key]) && is_array($_POST[$key]) ?
array_map('sanitizeInput', $_POST[$key]) : [];
array_map('sanitizeInput', $_POST[$key]) : [];
}