rl-warmup-plugin/includes/class-rl-mailwarmer-email-helper.php

471 lines
18 KiB
PHP

<?php
/**
* Helper functions for the email-account post type.
*/
require 'vendor/autoload.php';
use phpseclib3\Net\SSH2;
use phpseclib3\Crypt\PublicKeyLoader;
if (!defined('ABSPATH')) {
exit;
}
/**
* RL_MailWarmer_Email_Helper Class
*
* Handles email account management and mail operations.
*/
class RL_MailWarmer_Email_Helper
{
/**
* Modify an email account post.
*
* Creates, updates, or deletes an email account post and its associated metadata.
*
* @param array $args {
* Arguments for modifying the email account.
*
* @type int $post_id Optional. Post ID to update or delete. Required for updates or deletions.
* @type string $action Required. Action to perform: 'create', 'update', or 'delete'.
* @type string $email Optional. Email address for the account (used as post title).
* @type array $metadata Optional. Additional metadata to save with the post.
* }
* @return int|bool Post ID on success, false on failure.
*/
// Create an Email Account
// $post_id = RL_MailWarmer_Email_Helper::modify_email_account([
// 'action' => 'create',
// 'email' => 'johndoe@example.com',
// 'metadata' => [
// 'full_name' => 'John Doe',
// 'mail_password' => 'securepassword123',
// 'email_provider' => 123, // Post ID of the email provider
// 'smtp_server' => 'smtp.example.com',
// 'smtp_port' => 587,
// 'imap_server' => 'imap.example.com',
// 'imap_port' => 993,
// ],
// ]);
// // Update an Email Account
// $result = RL_MailWarmer_Email_Helper::modify_email_account([
// 'action' => 'update',
// 'post_id' => $post_id,
// 'metadata' => [
// 'full_name' => 'Jane Doe',
// 'mail_password' => 'newsecurepassword123',
// 'smtp_status' => 'connected',
// ],
// ]);
// // Delete an Email Account
// $result = RL_MailWarmer_Email_Helper::modify_email_account([
// 'action' => 'delete',
// 'post_id' => $post_id,
// ]);
public static function modify_email_account(array $args)
{
// Validate required arguments
if (empty($args['action']) || !in_array($args['action'], ['create', 'update', 'delete'], true)) {
throw new InvalidArgumentException('Invalid or missing action.');
}
/*
* Add validation to only delete email-account posts
*
*/
$action = $args['action'];
$post_id = $args['post_id'] ?? null;
// Handle delete action
if ($action === 'delete') {
if (!$post_id) {
throw new InvalidArgumentException('Post ID is required for deletion.');
}
return wp_delete_post($post_id, true);
}
// Validate fields for create/update
$post_data = [
'post_type' => 'email-account',
'post_status' => 'publish',
];
// For "create", ensure no existing post with the same title
if ($action === 'create') {
if (empty($args['email'])) {
throw new InvalidArgumentException('Email is required for creating a new account.');
}
$existing_post = get_page_by_title($args['email'], OBJECT, 'email-account');
if ($existing_post) {
throw new RuntimeException('An email account with this title already exists.');
}
$post_data['post_title'] = $args['email'];
} elseif ($action === 'update') {
if (!$post_id) {
throw new InvalidArgumentException('Post ID is required for updates.');
}
$post_data['ID'] = $post_id;
}
// Assemble metadata
$meta_args = $args['metadata'] ?? [];
// Generate a random password if mail_password is not provided
if (empty($meta_args['mail_password'])) {
$meta_args['mail_password'] = bin2hex(random_bytes(8)); // 16-character password
}
$post_data['meta_input'] = array_map('sanitize_text_field', $meta_args);
// Save or update the post
$post_id = wp_insert_post($post_data);
if (is_wp_error($post_id)) {
throw new RuntimeException('Failed to save email account post: ' . $post_id->get_error_message());
}
return $post_id;
}
/**
* Check mail login credentials for an email account.
*
* Validates IMAP/SMTP connection settings for the given email account.
*
* @param mixed $email_account The email-account post object or ID.
* @param string|null $protocol Optional. The protocol to validate ('IMAP' or 'SMTP'). Defaults to both.
* @return array|WP_Error Validation results for IMAP and/or SMTP or WP_Error on failure.
*/
public static function check_mail_login($email_account, $protocol = null)
{
// Get the post object
$post = is_numeric($email_account) ? get_post($email_account) : $email_account;
if (!$post || $post->post_type !== 'email-account') {
return new WP_Error('invalid_post', __('Invalid email account post.', 'rl-mailwarmer'));
}
// Fetch email provider and override defaults with saved values
$email_provider_id = get_post_meta($post->ID, 'email_provider', true);
// log_to_file("check_mail_login - ");
log_to_file("check_mail_login - Email Provider ID $email_provider_id");
$defaults = $email_provider_id ? self::get_provider_defaults($email_provider_id) : [];
log_to_file("check_mail_login - Email Provider Defaults: ", $defaults);
// Fetch saved settings
$saved_settings = [
'mail_password' => get_post_meta($post->ID, 'mail_password', true),
'imap_password' => get_post_meta($post->ID, 'imap_password', true),
'imap_server' => get_post_meta($post->ID, 'imap_server', true),
'imap_port' => get_post_meta($post->ID, 'imap_port', true),
'smtp_password' => get_post_meta($post->ID, 'smtp_password', true),
'smtp_server' => get_post_meta($post->ID, 'smtp_server', true),
'smtp_port' => get_post_meta($post->ID, 'smtp_port', true),
];
// Merge saved settings with defaults
$settings = array_merge($defaults, array_filter($saved_settings));
log_to_file("check_mail_login - Using settings: ", $settings);
$results = [];
// Validate IMAP connection if required
if ($protocol === null || strtoupper($protocol) === 'IMAP') {
$imap_result = self::validate_imap_connection($post->post_title, $settings);
log_to_file("check_mail_login - IMAP Result for " . $post->post_title . ": ", $imap_result);
$results['IMAP'] = $imap_result ? __('IMAP connection successful.', 'rl-mailwarmer') : __('IMAP connection failed.', 'rl-mailwarmer');
}
// Validate SMTP connection if required
if ($protocol === null || strtoupper($protocol) === 'SMTP') {
$smtp_result = self::validate_smtp_connection($post->post_title, $settings);
log_to_file("check_mail_login - SMTP Result for " . $post->post_title . ": ", $imap_result);
$results['SMTP'] = $smtp_result ? __('SMTP connection successful.', 'rl-mailwarmer') : __('SMTP connection failed.', 'rl-mailwarmer');
}
log_to_file("check_mail_login - Full Results for " . $post->post_title . ": ", $results);
return $results;
}
/**
* Fetch default settings for an email provider.
*
* @param int $email_provider_id The post ID of the email provider.
* @return array The default server settings.
*/
private static function get_provider_defaults($email_provider_id)
{
return [
'imap_server' => get_post_meta($email_provider_id, 'default_imap_server', true),
'imap_port' => get_post_meta($email_provider_id, 'default_imap_port', true),
'smtp_server' => get_post_meta($email_provider_id, 'default_smtp_server', true),
'smtp_port' => get_post_meta($email_provider_id, 'default_smtp_port', true),
];
}
/**
* Validate an IMAP connection for an email account.
*
* @param string $email The email address.
* @param array $settings The server settings (imap_server, imap_port, imap_password).
* @return bool True if the connection is successful, false otherwise.
*/
private static function validate_imap_connection($email, $settings)
{
if ( empty($settings['imap_server']) || empty($settings['imap_port']) ) {
return false; // Missing required settings
}
if (!empty($settings['imap_password'])) {
$password = $settings['imap_password'];
} else {
$password = $settings['mail_password'];
}
// Try connecting to the IMAP server
$imap_stream = @imap_open(
'{' . $settings['imap_server'] . ':' . $settings['imap_port'] . '/imap/ssl}',
$email,
$password
);
if ($imap_stream) {
imap_close($imap_stream); // Close connection if successful
return true;
}
return false; // Connection failed
}
/**
* Validate an SMTP connection for an email account using Symfony Mailer.
*
* @param string $email The email address.
* @param array $settings The server settings (smtp_server, smtp_port, smtp_password).
* @return bool True if the connection is successful, false otherwise.
*/
private static function validate_smtp_connection($email, $settings)
{
if (empty($settings['smtp_server']) || empty($settings['smtp_port']) ) {
return false; // Missing required settings
}
if (!empty($settings['smtp_password'])) {
$password = $settings['smtp_password'];
} else {
$password = $settings['mail_password'];
}
$test_to_email = "ruben@redlotusaustin.com";
try {
// Create the SMTP transport
$transport = new Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
$settings['smtp_server'],
$settings['smtp_port']
);
// Set authentication details
$transport->setUsername($email);
$transport->setPassword($password);
// Create the mailer
$mailer = new Symfony\Component\Mailer\Mailer($transport);
// Send a test email
$test_email = (new Symfony\Component\Mime\Email())
->from($email)
->to($test_to_email)
->subject('SMTP Connection for ' . $email)
->text('This is a test email to verify SMTP connection for ' . $email);
$mailer->send($test_email);
return true;
} catch (Exception $e) {
error_log('SMTP validation failed: ' . $e->getMessage());
return false;
}
}
/**
* Generate random email accounts for the specified domain.
*
* @param string $domain The domain name to use for the email accounts.
* @param int $qty The number of email accounts to generate. Defaults to 1.
* @return array List of generated names and email addresses.
* @throws Exception If name pools are empty or post creation fails.
*/
public static function generate_random_accounts($domain, $qty = 1)
{
// Fetch name pools from ACF options
$first_name_pool = get_field('valid_first_name_pool', 'option');
$last_name_pool = get_field('valid_last_name_pool', 'option');
if (empty($first_name_pool) || empty($last_name_pool)) {
throw new Exception(__('Name pools are empty. Please configure them in the ACF options.', 'rl-mailwarmer'));
}
$first_names = explode(',', $first_name_pool); // Assume comma-separated list
$last_names = explode(',', $last_name_pool);
$generated_accounts = [];
for ($i = 0; $i < $qty; $i++) {
// Generate a random name
$first_name = trim($first_names[array_rand($first_names)]);
$last_name = trim($last_names[array_rand($last_names)]);
$full_name = "{$first_name} {$last_name}";
// Generate a semi-random email address
$email_formats = [
"{$first_name}{$last_name}",
"{$first_name}.{$last_name}",
substr($first_name, 0, 1) . ".{$last_name}",
"{$first_name}.l",
substr($first_name, 0, 1) . $last_name,
];
$email_local_part = strtolower($email_formats[array_rand($email_formats)]);
$email_address = "{$email_local_part}@{$domain}";
// Create the email-account post
// $post_id = RL_MailWarmer_Email_Helper::modify_email_account([
// 'action' => 'create',
// 'email' => $email_address,
// 'metadata' => [
// 'full_name' => $full_name,
// 'mail_password' => bin2hex(random_bytes(8)), // Generate a secure random password
// ],
// ]);
// if (!$post_id) {
// throw new Exception(__('Failed to create email account post.', 'rl-mailwarmer'));
// }
// Add to results
$generated_accounts[] = [
'full_name' => $full_name,
'email_address' => $email_address,
];
}
return $generated_accounts;
}
/**
* Modify an email account on a VirtualMin server.
*
* @param int $account_id The email-account post ID.
* @param string $action The action to perform: 'create', 'update', or 'delete'.
* @return bool|WP_Error True on success, WP_Error on failure.
*/
public static function modify_email_account_on_server($account_id, $action)
{
// Validate email-account post
$email_account = get_post($account_id);
if (!$email_account || $email_account->post_type !== 'email-account') {
return new WP_Error('invalid_account', __('Invalid email account.', 'rl-mailwarmer'));
}
// Fetch associated server posts
$domain_id = get_post_meta($account_id, 'domain_id', true);
if (!$domain_id) {
return new WP_Error('missing_domain', __('No associated domain found.', 'rl-mailwarmer'));
}
$server_ids = get_post_meta($domain_id, 'associated_servers', true); // Assuming this field holds server post IDs
if (empty($server_ids) || !is_array($server_ids)) {
return new WP_Error('missing_servers', __('No associated servers found.', 'rl-mailwarmer'));
}
// Fetch email account details
$email_address = $email_account->post_title;
$password = get_post_meta($account_id, 'mail_password', true);
[$username, $domain] = explode('@', $email_address);
// Iterate over servers and perform the action
foreach ($server_ids as $server_id) {
$server = get_post($server_id);
if (!$server || $server->post_type !== 'server') {
continue; // Skip invalid server posts
}
$server_ip = get_post_meta($server_id, 'ip_address', true);
$server_port = get_post_meta($server_id, 'ssh_port', true);
$server_user = get_post_meta($server_id, 'username', true);
$server_password = get_post_meta($server_id, 'ssh_private_key', true);
// $server_password = get_post_meta($server_id, 'password', true);
if (!$server_ip || !$server_user || !$server_password) {
return new WP_Error('missing_server_details', __('Missing server credentials.', 'rl-mailwarmer'));
}
// Build VirtualMin command
$command = "virtualmin";
if ($action === 'create') {
$command .= " create-user --domain $domain --user $username --pass $password";
} elseif ($action === 'update') {
$command .= " modify-user --domain $domain --user $username --pass $password";
} elseif ($action === 'delete') {
$command .= " delete-user --domain $domain --user $username";
} else {
return new WP_Error('invalid_action', __('Invalid action specified.', 'rl-mailwarmer'));
}
// Execute the command via SSH
// $ssh = new phpseclib\Net\SSH2($server_ip);
// $key = new phpseclib\Crypt\PublicKeyLoader::loadPrivateKey($server_password); // Adjust for SSH key or plain password
$ssh = new SSH2($server_ip, $server_port);
if (!empty($server_password)) {
// Load the private key from the postmeta field
$key = PublicKeyLoader::loadPrivateKey($server_password);
} else {
// Fallback to password-based authentication
// $key = $server_password;
log_to_file("modify_email_account_on_server - Server $$server_id ssh_private_key empty");
return new WP_Error('ssh_login_failed', __('No private key found!', 'rl-mailwarmer'));
}
if (!$ssh->login($server_user, $key)) {
return new WP_Error('ssh_login_failed', __('Failed to log into the server.', 'rl-mailwarmer'));
}
$output = $ssh->exec($command);
if (strpos($output, 'failed') !== false) {
return new WP_Error('command_failed', __('Failed to execute VirtualMin command.', 'rl-mailwarmer'));
}
}
return true; // Success
}
/**
* Send or reply to a conversation email.
*
* @param int $conversation_id The conversation post ID.
* @param int $account_id The email account post ID.
* @return bool True on success, false on failure.
*/
public static function send_conversation_mail(int $conversation_id, int $account_id)
{
// Implementation goes here
}
}