diff --git a/content/plan.md b/content/plan.md index fb8bc9b..64ecf62 100755 --- a/content/plan.md +++ b/content/plan.md @@ -192,8 +192,8 @@ disableComments = true
- - + +
diff --git a/forms/forms_helper.php b/forms/forms_helper.php index e3ccc1a..54e86e3 100644 --- a/forms/forms_helper.php +++ b/forms/forms_helper.php @@ -59,22 +59,32 @@ function get_forms_settings() { /** * Простое шифрование данных для безопасного хранения + * Совместимо с Python Crypto.Cipher.AES + unpad */ function encrypt_data($data, $key) { $json = json_encode($data, JSON_UNESCAPED_UNICODE); $iv = random_bytes(16); - $encrypted = openssl_encrypt($json, 'AES-256-CBC', hash('sha256', $key), 0, $iv); - return base64_encode($iv . $encrypted); + + // openssl_encrypt с параметром 0 сам добавляет PKCS7 padding и возвращает base64 + $encrypted_b64 = openssl_encrypt($json, 'AES-256-CBC', hash('sha256', $key), 0, $iv); + // Декодируем base64 чтобы получить бинарные данные + $encrypted_raw = base64_decode($encrypted_b64); + // Добавляем IV в начало и кодируем все вместе + return base64_encode($iv . $encrypted_raw); } /** * Расшифровка данных + * Совместимо с Python Crypto.Cipher.AES + unpad */ function decrypt_data($encrypted_data, $key) { $data = base64_decode($encrypted_data); $iv = substr($data, 0, 16); - $encrypted = substr($data, 16); - $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', hash('sha256', $key), 0, $iv); + $encrypted_raw = substr($data, 16); + // Кодируем обратно в base64 для openssl_decrypt + $encrypted_b64 = base64_encode($encrypted_raw); + // openssl_decrypt с параметром 0 сам убирает PKCS7 padding + $decrypted = openssl_decrypt($encrypted_b64, 'AES-256-CBC', hash('sha256', $key), 0, $iv); return json_decode($decrypted, true); } @@ -208,6 +218,126 @@ function send_telegram_notification($form_data, $form_type) { } } +/** + * Отправка уведомления на Email через msmtp с поддержкой файлов + */ +function send_email_notification($form_data, $form_type) { + $settings = get_forms_settings(); + + if (!$settings['send_email']) { + return true; // Отправка email отключена + } + + // Получаем admin email из .env + $admin_email = getenv('ADMIN_EMAIL') ?: 'admin@example.com'; + $from_email = getenv('SMTP_FROM_EMAIL') ?: getenv('SMTP_USERNAME') ?: 'noreply@yourdomain.com'; + $from_name = getenv('SMTP_FROM_NAME') ?: 'PTP Robot'; + + // Формируем тему письма + $subject = ($form_type === 'plan') + ? '🎫 Новая заявка на поездку - ' . $form_data['name'] + : '❓ Новый вопрос - ' . ($form_data['subject'] ?? 'Без темы'); + + // Формируем тело письма + $message = "Новая заявка с сайта\n\n"; + $message .= "Тип: " . ($form_type === 'plan' ? 'Заявка на поездку' : 'Вопрос') . "\n"; + $message .= "Время: " . date('Y-m-d H:i:s') . "\n\n"; + + $message .= "=== ДАННЫЕ ЗАЯВИТЕЛЯ ===\n"; + $message .= "Имя: " . $form_data['name'] . "\n"; + $message .= "Email: " . $form_data['email'] . "\n"; + + if ($form_type === 'plan') { + $message .= "Телефон: " . ($form_data['phone'] ?: 'не указан') . "\n"; + $message .= "Telegram: " . ($form_data['telegram'] ?: 'не указан') . "\n"; + $message .= "Поездка: " . ($form_data['trip_period'] ?: 'не выбрана') . "\n"; + $message .= "БВС/Направление: " . ($form_data['bvs_number'] ?: 'не указано') . "\n"; + } else { + if (!empty($form_data['telegram'])) { + $message .= "Telegram: " . $form_data['telegram'] . "\n"; + } + if (!empty($form_data['phone'])) { + $message .= "Телефон: " . $form_data['phone'] . "\n"; + } + $message .= "Тема: " . $form_data['subject'] . "\n"; + $message .= "\n=== СООБЩЕНИЕ ===\n"; + $message .= $form_data['message'] . "\n"; + } + + // Проверяем наличие файла для отправки + $attachment_file = $form_data['bvs_file_path'] ?? null; + $has_attachment = $attachment_file && file_exists($attachment_file); + + // Создаем boundary для multipart письма если есть файл + $boundary = "----=_Part_" . md5(time() . rand()); + + if ($has_attachment) { + // Заголовки письма с файлом (multipart) + $headers = "From: $from_name <$from_email>\r\n"; + $headers .= "Reply-To: " . $form_data['email'] . "\r\n"; + $headers .= "MIME-Version: 1.0\r\n"; + $headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\"\r\n"; + $headers .= "X-Mailer: PHP/" . phpversion(); + + // Формируем multipart тело письма + $multipart_message = "--$boundary\r\n"; + $multipart_message .= "Content-Type: text/plain; charset=UTF-8\r\n"; + $multipart_message .= "Content-Transfer-Encoding: 8bit\r\n\r\n"; + $multipart_message .= $message . "\r\n"; + + // Добавляем файл + $file_content = file_get_contents($attachment_file); + $file_name = basename($attachment_file); + // Извлекаем оригинальное имя файла (после bvs_) + if (preg_match('/bvs_\w+_(.+)$/', $file_name, $matches)) { + $file_name = $matches[1]; + } + + $multipart_message .= "--$boundary\r\n"; + $multipart_message .= "Content-Type: application/pdf; name=\"$file_name\"\r\n"; + $multipart_message .= "Content-Transfer-Encoding: base64\r\n"; + $multipart_message .= "Content-Disposition: attachment; filename=\"$file_name\"\r\n\r\n"; + $multipart_message .= chunk_split(base64_encode($file_content)) . "\r\n"; + + $multipart_message .= "--$boundary--\r\n"; + + $email_message = $multipart_message; + } else { + // Заголовки письма без файла + $headers = "From: $from_name <$from_email>\r\n"; + $headers .= "Reply-To: " . $form_data['email'] . "\r\n"; + $headers .= "Content-Type: text/plain; charset=UTF-8\r\n"; + $headers .= "X-Mailer: PHP/" . phpversion(); + $email_message = $message; + } + + // Отправка письма + try { + error_log("=== Attempting mail() ==="); + error_log("To: $admin_email | From: $from_email | Subject: $subject | Has attachment: " . ($has_attachment ? 'YES' : 'NO')); + $result = mail($admin_email, $subject, $email_message, $headers); + error_log("mail() returned: " . ($result ? 'TRUE' : 'FALSE')); + + if ($result) { + error_log("Email отправлен успешно на: $admin_email"); + + // Удаляем временный файл после отправки + if ($has_attachment && file_exists($attachment_file)) { + unlink($attachment_file); + error_log("Временный файл удален: $attachment_file"); + } + + return true; + } else { + error_log("Ошибка отправки email на: $admin_email"); + return false; + } + } catch (Exception $e) { + error_log("Исключение при отправке email: " . $e->getMessage()); + return false; + } +} + /** * Получение списка всех заявок (для Telegram бота) */ diff --git a/forms/send_plan.php b/forms/send_plan.php index 88b967c..38b3837 100644 --- a/forms/send_plan.php +++ b/forms/send_plan.php @@ -16,8 +16,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $bvs_file = $_FILES['bvs_file'] ?? null; // Валидация - if (empty($name) || empty($consent)) { - $error = "Обязательные поля: Фамилия, имя, Согласие на обработку данных."; + if (empty($name) || empty($consent) || empty($bvs_number)) { + $error = "Обязательные поля: Фамилия, имя, Учётный номер дрона, Согласие на обработку данных."; } elseif (empty($phone)) { $error = "Обязательное поле: Телефон."; } elseif (empty($email) && empty($telegram)) { @@ -38,6 +38,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } if (!isset($error)) { + // Сохраняем загруженный файл во временную папку + $uploaded_file_path = null; + if ($bvs_file && $bvs_file['error'] === UPLOAD_ERR_OK) { + $upload_dir = sys_get_temp_dir() . '/ptp_forms_uploads/'; + if (!is_dir($upload_dir)) { + mkdir($upload_dir, 0700, true); + } + $uploaded_file_path = $upload_dir . uniqid('bvs_') . '_' . basename($bvs_file['name']); + move_uploaded_file($bvs_file['tmp_name'], $uploaded_file_path); + } + // Подготавливаем данные заявки $form_data = [ 'name' => $name, @@ -47,7 +58,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 'bvs_number' => $bvs_number, 'trip_period' => $trip_period, 'consent' => $consent, - 'bvs_file' => $bvs_file ? $bvs_file['name'] : '' + 'bvs_file' => $bvs_file ? $bvs_file['name'] : '', + 'bvs_file_path' => $uploaded_file_path ]; $success_messages = [];