1357 lines
54 KiB
PHP
1357 lines
54 KiB
PHP
<?php
|
|
/**
|
|
* Helper functions for the Domain post type.
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
use GuzzleHttp\Client;
|
|
|
|
class RL_MailWarmer_Domain_Helper {
|
|
|
|
/**
|
|
* Get the domain post object by ID or name.
|
|
*
|
|
* @param mixed $domain The domain ID or name.
|
|
* @return WP_Post|null The domain post object or null if not found.
|
|
*/
|
|
public static function get_domain_post($domain) {
|
|
if ( is_a( $domain, 'WP_Post' ) ) {
|
|
// log_to_file("get_domain_post - Already a WP Post");
|
|
return $domain;
|
|
} else if (is_numeric($domain)) {
|
|
// log_to_file("get_domain_post - Fetching WP Post by ID");
|
|
return get_post($domain);
|
|
}
|
|
// log_to_file("get_domain_post - Fetching WP Post by Title");
|
|
return get_page_by_title($domain, OBJECT, 'domain');
|
|
}
|
|
|
|
/**
|
|
* Get CloudFlare credentials from the domain post.
|
|
*
|
|
* @param WP_Post $domain_post The domain post object.
|
|
* @return array Associative array containing `api_email`, `api_key`, and `zone_id`.
|
|
* @throws Exception If credentials are missing.
|
|
*/
|
|
public static function get_cloudflare_credentials($domain) {
|
|
$domain_post = self::get_domain_post($domain);
|
|
$domain_name = $domain_post->post_title;
|
|
$api_email = get_post_meta($domain_post->ID, 'cloudflare_api_email', true);
|
|
$api_key = get_post_meta($domain_post->ID, 'cloudflare_api_key', true);
|
|
$zone_id = get_post_meta($domain_post->ID, 'cloudflare_zone_id') ? get_post_meta($domain_post->ID, 'cloudflare_zone_id', true) : false;
|
|
|
|
// If either credential is missing from domain, check user meta
|
|
if (!$api_email || !$api_key) {
|
|
$owner_id = get_post_meta($domain_post->ID, 'owner_id', true);
|
|
// log_to_file("get_cloudflare_credentials - API credentials not saved to domain. Trying to fetch them from the owner: $owner_id");
|
|
if ($owner_id) {
|
|
if (!$api_email) {
|
|
$api_email = get_user_meta($owner_id, 'cloudflare_api_email', true);
|
|
}
|
|
if (!$api_key) {
|
|
$api_key = get_user_meta($owner_id, 'cloudflare_api_key', true);
|
|
}
|
|
} else {
|
|
throw new Exception(__("get_cloudflare_credentials - CloudFlare credentials are incomplete for domain: {$domain_post->ID}.", "rl-mailwarmer"));
|
|
}
|
|
}
|
|
|
|
// Fetch & save the Zone ID if we don't already have it
|
|
if (!$zone_id) {
|
|
$client = new \GuzzleHttp\Client([
|
|
'base_uri' => 'https://api.cloudflare.com/client/v4/',
|
|
'headers' => [
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
]);
|
|
|
|
// log_to_file(" - Client API Key: ", $client->getConfig('api_key'));
|
|
|
|
try {
|
|
$response = $client->get('zones', [
|
|
'headers' => [
|
|
'Authorization' => "Bearer {$api_key}",
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
'query' => ['name' => $domain_name],
|
|
]);
|
|
|
|
$data = json_decode($response->getBody()->getContents(), true);
|
|
|
|
if (isset($data['result'][0]['id'])) {
|
|
$zone_id = $data['result'][0]['id'];
|
|
update_post_meta($domain_post->ID,'cloudflare_zone_id',$zone_id);
|
|
log_to_file("get_cloudflare_credentials - Saved Zone ID to $domain_name: $zone_id");
|
|
|
|
}
|
|
|
|
// throw new Exception(__('get_cloudflare_credentials - Zone ID not found for the domain.', 'rl-mailwarmer'));
|
|
} catch (Exception $e) {
|
|
throw new Exception(__('get_cloudflare_credentials - Failed to fetch CloudFlare zone ID: ', 'rl-mailwarmer') . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
if (!$api_email || !$api_key || !$zone_id) {
|
|
return false;
|
|
} else {
|
|
return compact('api_email', 'api_key', 'zone_id');
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Establish a connection to the CloudFlare API.
|
|
*
|
|
* @param array $credentials The CloudFlare credentials.
|
|
* @return GuzzleHttp\Client The Guzzle client for API requests.
|
|
*/
|
|
private static function get_cloudflare_client($domain) {
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
throw new Exception(__('get_cloudflare_client - Invalid domain specified.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
$domain_name = $domain_post->post_title;
|
|
$credentials = self::get_cloudflare_credentials($domain_post);
|
|
|
|
|
|
$client = new \GuzzleHttp\Client([
|
|
'base_uri' => 'https://api.cloudflare.com/client/v4/',
|
|
'domain' => $domain_name,
|
|
'api_email' => $credentials['api_email'],
|
|
'api_key' => $credentials['api_key'],
|
|
'zone_id' => $credentials['zone_id'],
|
|
'headers' => [
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
]);
|
|
return $client;
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* Fetch DNS records using the CloudFlare API.
|
|
*
|
|
* @param string $domain The domain name.
|
|
* @param string $type The record type (e.g., TXT, A).
|
|
* @return array The DNS records.
|
|
* @throws Exception If fetching records fails.
|
|
*/
|
|
private static function fetch_dns_records($client, $type = null) {
|
|
|
|
try {
|
|
$response = $client->get("zones/{$client->getConfig('zone_id')}/dns_records", [
|
|
'headers' => [
|
|
'Authorization' => "Bearer {$client->getConfig('api_key')}",
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
'query' => ['type' => $type],
|
|
]);
|
|
|
|
// log_to_file("fetch_dns_records - response: ", $response);
|
|
$data = json_decode($response->getBody()->getContents(), true);
|
|
|
|
if (isset($data['result']) && is_array($data['result'])) {
|
|
return $data['result'];
|
|
// log_to_file("fetch_dns_records - Data: ", $data['result']);
|
|
}
|
|
|
|
throw new Exception(__('fetch_dns_records - No DNS records found.', 'rl-mailwarmer'));
|
|
} catch (Exception $e) {
|
|
throw new Exception(__('fetch_dns_records - Failed to fetch DNS records: ', 'rl-mailwarmer') . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Update or create a DNS record using the Cloudflare API.
|
|
*
|
|
* @param int $domainID The ID of the parent domain post.
|
|
* @param string $domain The domain name.
|
|
* @param string $type The record type (e.g., TXT).
|
|
* @param string $name The record name.
|
|
* @param string $content The record content.
|
|
* @param int $ttl The TTL value.
|
|
* @return bool True on success.
|
|
*/
|
|
private static function update_dns_record($domain, $domain_name, $type, $name, $content, $ttl, $priority = 0)
|
|
{
|
|
|
|
// log_to_file("update_dns_record - Updating $name record on $domain_name with: " . $content);
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
// log_to_file("Invalid domain input");
|
|
throw new InvalidArgumentException('Invalid domain input.');
|
|
}
|
|
$client = self::get_cloudflare_client($domain);
|
|
$dns_records = self::fetch_dns_records($client, $type);
|
|
|
|
// Search existing records first, update it if found
|
|
foreach ($dns_records as $record) {
|
|
if (($record['name'] === $name) && ($record['type'] === $type)) {
|
|
if ($content === $record['content']) {
|
|
log_to_file("update_dns_record - Old and new values are the same. Not updating DNS $name record on $domain with: " . $content);
|
|
return true;
|
|
}
|
|
log_to_file("update_dns_record - Match found! Creating backup before updating");
|
|
// Backup the existing record before updating
|
|
$backup_response = RL_MailWarmer_DB_Helper::insert_dns_backup($domain, $record);
|
|
// return RL_MailWarmer_DB_Helper::insert_dns_backup($domain_post->post_title, $data['result']);
|
|
// log_to_file("Backup response: $backup_response");
|
|
if ( $backup_response ){
|
|
log_to_file("update_dns_record - Backup successful! Updating $name record on $domain_name with: " . $content);
|
|
// log_to_file("Current value: " . $record['content']);
|
|
|
|
// Update existing record
|
|
$client->put("zones/{$client->getConfig('zone_id')}/dns_records/{$record['id']}", [
|
|
'headers' => [
|
|
'Authorization' => "Bearer {$client->getConfig('api_key')}",
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
'json' => [
|
|
'type' => $type,
|
|
'name' => $name,
|
|
'content' => $content,
|
|
'ttl' => $ttl,
|
|
'priority'=> (int) $priority,
|
|
],
|
|
]);
|
|
return true;
|
|
} else {
|
|
log_to_file("update_dns_record - Error creating Backup DNS Record $name for $domain_post->title. NOT updating DNS record.");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create new record if not found
|
|
log_to_file("update_dns_record - Creating new record");
|
|
$response = $client->post("zones/{$client->getConfig('zone_id')}/dns_records", [
|
|
'headers' => [
|
|
'Authorization' => "Bearer {$client->getConfig('api_key')}",
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
'json' => [
|
|
'type' => $type,
|
|
'name' => $name,
|
|
'content' => $content,
|
|
'ttl' => (int) $ttl,
|
|
'priority'=> (int) $priority,
|
|
],
|
|
]);
|
|
$result = json_decode($response->getBody(), true);
|
|
log_to_file("update_dns_record - Result: ", $result);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Perform a full BIND export for a domain and save a backup
|
|
*
|
|
* @param mixed $domain The domain ID or name.
|
|
* @param array|null $record The specific DNS record to export, or null for all records.
|
|
* @return bool True on success, false on failure.
|
|
*/
|
|
public static function export_dns_zone($domain) {
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
throw new Exception(__('export_dns_zone - Invalid domain specified.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
$domain_name = $domain_post->post_title;
|
|
// log_to_file("export_dns_zone - Attempting to export records for $domain_name");
|
|
|
|
// $credentials = self::get_cloudflare_credentials($domain_post);
|
|
$client = self::get_cloudflare_client($domain_post);
|
|
|
|
$endpoint = "zones/{$client->getConfig('zone_id')}/dns_records/export";
|
|
// if ($record) {
|
|
// $endpoint .= "?name=" . urlencode($record['name']) . "&type=" . urlencode($record['type']);
|
|
// } else {
|
|
// $endpoint .= "/export";
|
|
// }
|
|
|
|
try {
|
|
$response = $client->get($endpoint, [
|
|
'headers' => [
|
|
'Authorization' => "Bearer {$client->getConfig('api_key')}",
|
|
'Content-Type' => 'application/json',
|
|
],
|
|
]);
|
|
|
|
if ($response->getStatusCode() !== 200) {
|
|
$data = json_decode($response->getBody(), true);
|
|
throw new \RuntimeException('Failed to export DNS records: ' . json_encode($data['errors'] ?? 'Unknown error'));
|
|
}
|
|
|
|
$data = (string) $response->getBody();
|
|
// log_to_file("export_dns_zone - Exported data: ", $data);
|
|
|
|
if ($data != '') {
|
|
$record['name'] = $domain_name;
|
|
$record['type'] = "FULL";
|
|
$record['content'] = $data;
|
|
return RL_MailWarmer_DB_Helper::insert_dns_backup($domain, $record);
|
|
// return true;
|
|
}
|
|
// return 69;
|
|
} catch (Exception $e) {
|
|
error_log(__('Failed to export DNS records: ', 'rl-mailwarmer') . $e->getMessage());
|
|
}
|
|
log_to_file("export_dns_zone - Unable to export DNS record for $domain_name");
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Generate a comprehensive email deliverability report for the domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @return array The generated report.
|
|
*/
|
|
public static function generate_domain_report($domain)
|
|
{
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
throw new InvalidArgumentException('generate_domain_report - Invalid domain input.');
|
|
}
|
|
$domain_name = $domain_post->post_title;
|
|
|
|
// log_to_file("generate_domain_report - Running generate_domain_report for " . $domain_name);
|
|
|
|
$client = self::get_cloudflare_client($domain);
|
|
// $zone_id = self::get_cloudflare_zone_id($client, $credentials, $domain_post->post_title);
|
|
$dns_records = self::fetch_dns_records($client);
|
|
// log_to_file("generate_domain_report - All Records: ", $dns_records);
|
|
|
|
$report = [
|
|
'domain_health' => self::check_domain_registration($domain_name),
|
|
'a_record' => self::check_a_record($dns_records),
|
|
'mx_record' => self::check_mx_record($dns_records),
|
|
'spf_record' => self::check_spf_record($dns_records),
|
|
'dkim_records' => self::check_dkim_record($dns_records),
|
|
'dmarc_record' => self::check_dmarc_record($dns_records),
|
|
'blacklists' => self::check_blacklists($domain_name),
|
|
];
|
|
|
|
// log_to_file("generate_domain_report - Health Report for $domain_name: ", $report);
|
|
|
|
// return false;
|
|
return $report;
|
|
}
|
|
|
|
/**
|
|
* Save a domain health report as a custom post.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @return int|WP_Error The post ID of the saved report or WP_Error on failure.
|
|
*/
|
|
public static function save_domain_health_report($domain)
|
|
{
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
throw new InvalidArgumentException('Invalid domain input.');
|
|
}
|
|
// log_to_file("save_domain_health_report - Trying to generate report for {$domain_post->post_title}");
|
|
|
|
// log_to_file("save_domain_health_report - Before Report Generation");
|
|
$report = self::generate_domain_report($domain);
|
|
// log_to_file("save_domain_health_report - After Report Generation");
|
|
|
|
// Prepare the post title and content
|
|
// $title = "{$domain_post->post_title}-" . current_time('timestamp');
|
|
$content = json_encode($report);
|
|
// log_to_file("save_domain_health_report - Report JSON Content: ", $content);
|
|
$report_id = RL_MailWarmer_DB_Helper::insert_health_report_backup($domain, $content);
|
|
|
|
if (is_wp_error($report_id)) {
|
|
wp_send_json_error($report_id->get_error_message());
|
|
// return false;
|
|
} else {
|
|
update_post_meta($domain_post->ID,'domain_health_report',$content);
|
|
// wp_send_json_success($report_id);
|
|
return $report_id;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check domain health by verifying WHOIS information and basic attributes.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @return array Domain health details.
|
|
*/
|
|
private static function check_domain_registration($domain_name)
|
|
{
|
|
|
|
// log_to_file("check_domain_registration - Running check_domain_registration for $domain_name");
|
|
|
|
// Fetch WHOIS data
|
|
$whois_data = shell_exec("whois {$domain_name}");
|
|
|
|
if (!$whois_data) {
|
|
return [
|
|
'registration_valid' => false,
|
|
'domain_age' => null,
|
|
'days_to_expiration' => null,
|
|
];
|
|
}
|
|
|
|
// Parse WHOIS data
|
|
$registration_valid = true;
|
|
$creation_date = null;
|
|
$expiration_date = null;
|
|
|
|
if (preg_match('/Creation Date:\s*(.+)/i', $whois_data, $matches)) {
|
|
$creation_date = new DateTime(trim($matches[1]));
|
|
}
|
|
|
|
if (preg_match('/Expiration Date:\s*(.+)/i', $whois_data, $matches)) {
|
|
$expiration_date = new DateTime(trim($matches[1]));
|
|
} else if (preg_match('/Expiry Date:\s*(.+)/i', $whois_data, $matches)) {
|
|
$expiration_date = new DateTime(trim($matches[1]));
|
|
}
|
|
|
|
if (!$creation_date || !$expiration_date || $expiration_date < new DateTime()) {
|
|
$registration_valid = false;
|
|
}
|
|
|
|
$domain_age = $creation_date ? (new DateTime())->diff($creation_date)->format('%y years, %m months') : null;
|
|
$days_to_expiration = $expiration_date ? (new DateTime())->diff($expiration_date)->days : null;
|
|
|
|
return [
|
|
'registration_valid' => $registration_valid,
|
|
'domain_age' => $domain_age,
|
|
'days_to_expiration' => $days_to_expiration,
|
|
];
|
|
}
|
|
|
|
|
|
/**
|
|
* Check A record for the domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @return array A record details.
|
|
*/
|
|
private static function check_a_record($dns_records)
|
|
{
|
|
$domain_name = $dns_records[0]['zone_name'];
|
|
// log_to_file("check_a_record - Running check_mx_record for $domain_name");
|
|
|
|
foreach ($dns_records as $record) {
|
|
// Check if the record matches the criteria
|
|
// log_to_file("check_a_record - DNS Record: ", $record);
|
|
if ( ($record['zone_name'] === $record['name']) && ($record['type'] === 'A') ) {
|
|
$ip = $record['content'];
|
|
$http_status = self::get_http_status($domain_name);
|
|
|
|
return [
|
|
'ip' => $ip,
|
|
'http_status' => $http_status,
|
|
'https_enabled'=> $http_status === 200 && self::is_https_enabled($domain_name),
|
|
];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'ip' => false,
|
|
'http_status' => false,
|
|
'https_enabled'=> false,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get HTTP status code for the domain.
|
|
*/
|
|
private static function get_http_status($domain)
|
|
{
|
|
$client = new Client(['timeout' => 10]);
|
|
try {
|
|
$response = $client->get("http://{$domain}");
|
|
return $response->getStatusCode();
|
|
} catch (Exception $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if HTTPS is enabled for the domain.
|
|
*/
|
|
private static function is_https_enabled($domain)
|
|
{
|
|
$client = new Client(['timeout' => 10]);
|
|
try {
|
|
$response = $client->get("https://{$domain}");
|
|
return $response->getStatusCode() === 200;
|
|
} catch (Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check MX record for the domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @return array MX record details.
|
|
*/
|
|
private static function check_mx_record($dns_records)
|
|
{
|
|
$domain_name = $dns_records[0]['zone_name'];
|
|
// log_to_file("check_mx_record - Running check_mx_record for $domain_name");
|
|
|
|
foreach ($dns_records as $record) {
|
|
// Check if the record matches the criteria
|
|
if ( ($record['zone_name'] === $domain_name) && ($record['type'] === 'MX') ) {
|
|
$host = $record['content'];
|
|
$ptr_record = gethostbyaddr(gethostbyname($host));
|
|
|
|
return [
|
|
'host' => $host,
|
|
'ptr_valid' => $ptr_record !== false,
|
|
'ptr_matches' => $ptr_record === $host,
|
|
];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'host' => false,
|
|
'ptr_valid' => false,
|
|
'ptr_matches' => false,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Update or create an MX record for the specified domain in CloudFlare.
|
|
*
|
|
* @param mixed $domain The domain (can be ID, post object, or name).
|
|
* @param string $host The mail server host.
|
|
* @param int $priority The MX record priority.
|
|
* @return array|string Response from CloudFlare or error message.
|
|
*/
|
|
public static function update_mx_record($domain, $content, $priority, $ttl = 3600, $action = NULL) {
|
|
try {
|
|
// Normalize the domain and get CloudFlare credentials
|
|
$domain_post = self::get_domain_post($domain);
|
|
$client = self::get_cloudflare_client($domain);
|
|
$dns_records = self::fetch_dns_records($client);
|
|
$existing_record_id = null;
|
|
log_to_file("update_mx_record - DNS Records: ", $dns_records);
|
|
|
|
if ( !empty($dns_records) && ($dns_records != [])) {
|
|
log_to_file("update_mx_record - Searching for existing record");
|
|
// throw new Exception('Failed to fetch existing DNS records from CloudFlare.');
|
|
|
|
foreach ($dns_records['result'] as $record) {
|
|
if ($record['content'] === $content && $record['priority'] === $priority) {
|
|
$existing_record_id = $record['id'];
|
|
log_to_file("update_mx_record - Matching record found");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prepare the payload for the MX record
|
|
// $payload = [
|
|
// 'type' => 'MX',
|
|
// 'name' => $domain_post->post_title,
|
|
// 'content' => $content,
|
|
// 'priority' => $priority,
|
|
// 'ttl' => $ttl, // Default TTL: 1 hour
|
|
// ];
|
|
|
|
|
|
// if ($existing_record_id) {
|
|
// // Update the existing MX record
|
|
// $response = $client->request('PUT', "zones/$zone_id/dns_records/$existing_record_id", [
|
|
// 'json' => $payload,
|
|
// ]);
|
|
// } else {
|
|
// // Create a new MX record
|
|
// $response = $client->request('POST', "zones/$zone_id/dns_records", [
|
|
// 'json' => $payload,
|
|
// ]);
|
|
// }
|
|
|
|
log_to_file("update_mx_record - Attempting to update record");
|
|
$response = self::update_dns_record($domain_post->ID, $domain_post->post_title, 'MX', $domain_post->post_title, $content, $ttl, $priority);
|
|
$result = json_decode($response->getBody(), true);
|
|
log_to_file("update_mx_record - Result: ", $result);
|
|
|
|
if (!$result['success']) {
|
|
throw new Exception('Failed to update or create the MX record in CloudFlare.');
|
|
}
|
|
|
|
return $result; // Return the CloudFlare response
|
|
} catch (Exception $e) {
|
|
log_to_file('update_mx_record - Error in update_mx_record: ' . $e->getMessage());
|
|
return 'Error: ' . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check SPF record for the domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @return array SPF record details.
|
|
*/
|
|
private static function check_spf_record($dns_records)
|
|
{
|
|
$domain_name = $dns_records[0]['zone_name'];
|
|
// log_to_file("check_spf_record - Running check_spf_record for $domain_name");
|
|
|
|
foreach ($dns_records as $record) {
|
|
// log_to_file("Found " . $record['name'] . " - ". $record['content']);
|
|
if ( ($record['name'] == $domain_name) && ( strpos($record['content'], 'v=spf1') !== false ) ) {
|
|
// log_to_file("Match Found: " . $record['name'] . ": " . $record['content']);
|
|
return [
|
|
'content' => addslashes($record['content']),
|
|
'ttl' => $record['ttl'],
|
|
'all_mechanism' => strpos($record['content'], '-all') !== false ? '-all' : (strpos($record['content'], '~all') !== false ? '~all' : 'none'),
|
|
];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'content' => false,
|
|
'ttl' => false,
|
|
'all_mechanism' => false,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Update SPF record for the domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @param string $host The hostname or IP to add or remove.
|
|
* @param string $action The action to perform: 'add' or 'delete'.
|
|
* @param string $all_policy The `all` mechanism to set (default: '-all').
|
|
* @param int $ttl The TTL value (default: 3600).
|
|
* @return bool True on success.
|
|
*/
|
|
public static function update_spf_record($domain, $host, $action, $all_policy = '-all', $ttl = 3600)
|
|
{
|
|
// log_to_file("update_spf_record - Post ID: $domain $host $action $all_policy $ttl");
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
throw new InvalidArgumentException('update_spf_record - Invalid domain input.');
|
|
}
|
|
$domain_name = $domain_post->post_title;
|
|
// log_to_file("update_spf_record - Post ID: $domain_name $host $action $all_policy $ttl");
|
|
|
|
// log_to_file("update_spf_record - Updating SPF for $domain_name");
|
|
|
|
$client = self::get_cloudflare_client($domain);
|
|
// log_to_file("update_spf_record - Client created");
|
|
$dns_records = self::fetch_dns_records($client, "TXT");
|
|
$spf_record = null;
|
|
|
|
foreach ($dns_records as $record) {
|
|
// log_to_file("update_spf_record - Found record: ", $record);
|
|
if (($record['name'] === $domain_name) && strpos($record['content'], 'v=spf1') !== false) {
|
|
$spf_record = $record;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Determine the correct format for the $host
|
|
$host_format = filter_var($host, FILTER_VALIDATE_IP) ? "ip4:$host" : "include:$host";
|
|
|
|
if (!$spf_record) {
|
|
// Create a new SPF record
|
|
$new_content = "v=spf1 +a +mx $host_format $all_policy";
|
|
|
|
// Ensure TXT content is wrapped in double quotes
|
|
$wrapped_content = '"' . trim($new_content, '"') . '"';
|
|
|
|
// log_to_file("No record found. Creating new record: $domain_name: $wrapped_content");
|
|
return self::update_dns_record($domain_post, $domain_name, 'TXT', $domain_name, $wrapped_content, $ttl);
|
|
}
|
|
|
|
// Update the existing SPF record
|
|
$content_parts = explode(' ', $spf_record['content']);
|
|
|
|
if ($action === 'add') {
|
|
if (!in_array($host_format, $content_parts, true)) {
|
|
array_splice($content_parts, -1, 0, $host_format); // Insert before `all` mechanism
|
|
}
|
|
} elseif ($action === 'delete') {
|
|
$content_parts = array_filter($content_parts, fn($part) => $part !== $host_format);
|
|
}
|
|
|
|
// Ensure the `all` mechanism is set correctly
|
|
$content_parts = array_filter($content_parts, fn($part) => !preg_match('/~?all/', $part));
|
|
$content_parts[] = $all_policy .'"';
|
|
|
|
$updated_content = implode(' ', $content_parts);
|
|
// log_to_file("Existing record found. Updating");
|
|
// log_to_file("Old record: $domain_post->post_title: " .$record['content']);
|
|
// log_to_file("New record: $domain_post->post_title: $updated_content");
|
|
return self::update_dns_record($domain_post->ID, $domain_post->post_title, 'TXT', $domain_post->post_title, $updated_content, $ttl);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Check DMARC record for the domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @return array DMARC record details.
|
|
*/
|
|
private static function check_dmarc_record($dns_records)
|
|
{
|
|
$domain_name = $dns_records[0]['zone_name'];
|
|
// log_to_file("check_dmarc_record - Running check_dmarc_record for $domain_name");
|
|
|
|
foreach ($dns_records as $record) {
|
|
if ($record['name'] === "_dmarc.{$domain_name}") {
|
|
return [
|
|
'exists' => true,
|
|
'content' => addslashes($record['content']),
|
|
'ttl' => $record['ttl'],
|
|
'policy' => preg_match('/p=(\w+)/', $record['content'], $matches) ? $matches[1] : null,
|
|
'sp_policy' => preg_match('/sp=(\w+)/', $record['content'], $matches) ? $matches[1] : null,
|
|
'percentage' => preg_match('/pct=(\d+)/', $record['content'], $matches) ? $matches[1] : null,
|
|
'aggregate_rpt' => preg_match('/rua=mailto:([^;]+)/', $record['content'], $matches) ? $matches[1] : null,
|
|
'forensic_rpt' => preg_match('/ruf=mailto:([^;]+)/', $record['content'], $matches) ? $matches[1] : null,
|
|
'report_interval' => preg_match('/ri=(\d+)/', $record['content'], $matches) ? $matches[1] : null,
|
|
'report_format' => preg_match('/rf=(\w+)/', $record['content'], $matches) ? $matches[1] : null,
|
|
'aspf' => preg_match('/aspf=(\w+)/', $record['content'], $matches) ? $matches[1] : null,
|
|
'adkim' => preg_match('/adkim=(\w+)/', $record['content'], $matches) ? $matches[1] : null,
|
|
];
|
|
}
|
|
}
|
|
|
|
return [
|
|
'exists' => false,
|
|
'content' => false,
|
|
'ttl' => false,
|
|
'policy' => false,
|
|
'sp_policy' => false,
|
|
'percentage' => false,
|
|
'aggregate_rpt' => false,
|
|
'forensic_rpt' => false,
|
|
'report_interval' => false,
|
|
'report_format' => false,
|
|
'aspf' => false,
|
|
'adkim' => false,
|
|
];
|
|
}
|
|
|
|
|
|
/**
|
|
* Update DMARC record for the domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @param array $params Array of DMARC record parameters to update.
|
|
* @return bool True on success.
|
|
*/
|
|
public static function update_dmarc_record($domain, $params = [])
|
|
{
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
throw new InvalidArgumentException('Invalid domain input.');
|
|
}
|
|
|
|
$domain_name = $domain_post->post_title;
|
|
|
|
$client = self::get_cloudflare_client($domain);
|
|
|
|
// Fetch existing DMARC record
|
|
$dns_records = self::fetch_dns_records($client, "TXT");
|
|
|
|
$name = "_dmarc.{$domain_name}";
|
|
|
|
// log_to_file("update_dmarc_record - Updating DMARC record for $name");
|
|
|
|
$existing_record = null;
|
|
|
|
foreach ($dns_records as $record) {
|
|
if ($record['name'] === $name) {
|
|
log_to_file("update_dmarc_record - Match found: " . $record['name']);
|
|
$existing_record = $record;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Default DMARC record if $params is empty
|
|
if (empty($params)) {
|
|
$params = [
|
|
'v' => 'DMARC1',
|
|
'p' => 'quarantine',
|
|
'sp' => 'quarantine',
|
|
'pct' => 100,
|
|
'aspf' => 'r',
|
|
'adkim' => 'r',
|
|
];
|
|
}
|
|
|
|
// Parse existing record if available
|
|
$content_parts = [];
|
|
if ($existing_record) {
|
|
parse_str(str_replace('; ', '&', $existing_record['content']), $content_parts);
|
|
}
|
|
|
|
// Update or merge parameters
|
|
$updated_params = array_merge($content_parts, $params);
|
|
|
|
// Build the DMARC record string
|
|
$dmarc_content = '';
|
|
foreach ($updated_params as $key => $value) {
|
|
$dmarc_content .= "{$key}={$value}; ";
|
|
}
|
|
$dmarc_content = rtrim($dmarc_content, '; '); // Remove trailing semicolon and space
|
|
|
|
// Ensure TXT content is wrapped in double quotes
|
|
$wrapped_content = '"' . trim($dmarc_content, '"') . '"';
|
|
|
|
// log_to_file("update_dmarc_record - New DMARC value: $wrapped_content");
|
|
// $dmarc_content .= '"';
|
|
|
|
// Update or create the DNS record
|
|
if ($existing_record) {
|
|
return self::update_dns_record(
|
|
$domain_post->ID,
|
|
$domain_post->post_title,
|
|
'TXT',
|
|
$name,
|
|
$wrapped_content,
|
|
$existing_record['ttl'],
|
|
$credentials
|
|
);
|
|
} else {
|
|
return self::update_dns_record(
|
|
$domain_post->ID,
|
|
$domain_post->post_title,
|
|
'TXT',
|
|
$name,
|
|
$wrapped_content,
|
|
3600, // Default TTL
|
|
$credentials
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Check DKIM records for the domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @param array $selectors Array of DKIM selectors to check.
|
|
* @return array DKIM record details for each selector.
|
|
*/
|
|
private static function check_dkim_record($dns_records, $selectors = [])
|
|
{
|
|
$domain_name = $dns_records[0]['zone_name'];
|
|
// log_to_file("check_dkim_record - Running check_dkim_record for $domain_name");
|
|
|
|
$dkim_records = [];
|
|
$records = [];
|
|
$select_suffix = "._domainkey.{$domain_name}";
|
|
$default_selectors = ['google', 'selector1', 'selector2', 'k1', 'k2', 'ctct1', 'ctct2', 'sm', 's1',
|
|
's2', 'sig1', 'litesrv', 'zendesk1', 'zendesk2', 'mail', 'email', 'dkim', 'default', '202410', '202406'];
|
|
$selectors = array_unique(array_merge($default_selectors, $selectors));
|
|
$selectors = array_map(function ($item) use ($select_suffix) {
|
|
return $item . $select_suffix;
|
|
}, $selectors);
|
|
|
|
foreach ($dns_records as $record) {
|
|
// log_to_file("check_dkim_record - Found DKIM Record: {$record['name']}");
|
|
if ( in_array($record['name'], $selectors) ) {
|
|
// log_to_file("check_dkim_record - Found {$record['name']}._domainkey");
|
|
$records[$record['name']] = [
|
|
'ttl' => $record['ttl'],
|
|
// 'content'=> $record['content'],
|
|
];
|
|
// break;
|
|
}
|
|
}
|
|
|
|
// $dkim_records = [ "dkim_records" => $records ];
|
|
|
|
return $records;
|
|
}
|
|
|
|
|
|
/**
|
|
* Update or create a DKIM record for the domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @param string $selector The DKIM selector to update.
|
|
* @param string $action The action to perform: 'add' or 'delete'.
|
|
* @param string $value The DKIM public key (required for 'add').
|
|
* @param int $ttl The TTL value (default: 3600).
|
|
* @return bool True on success.
|
|
*/
|
|
public static function update_dkim_record($domain, $selector, $action, $value = '', $ttl = 3600)
|
|
{
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
throw new InvalidArgumentException('Invalid domain input.');
|
|
}
|
|
|
|
$domain_name = $domain_post->post_title;
|
|
|
|
$client = self::get_cloudflare_client($domain);
|
|
|
|
// Fetch existing DMARC record
|
|
$dns_records = self::fetch_dns_records($client, "TXT");
|
|
|
|
$name = "{$selector}._domainkey.{$domain_name}";
|
|
|
|
// Ensure TXT content is wrapped in double quotes
|
|
$wrapped_content = '"' . trim($value, '"') . '"';
|
|
|
|
foreach ($dns_records as $record) {
|
|
if ($record['name'] === $name) {
|
|
if ($action === 'delete') {
|
|
// Delete existing DKIM record
|
|
log_to_file("update_dkim_record - delete_dns_record() is not implemented!");
|
|
throw new RuntimeException('delete_dns_record() is not implemented!');
|
|
// return self::delete_dns_record($domain_post->post_title, $record['id'], $credentials);
|
|
}
|
|
|
|
if ($action === 'add') {
|
|
// Update existing DKIM record
|
|
return self::update_dns_record($domain_post->ID, $domain_post->post_title, 'TXT', $name, $wrapped_content, $ttl);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($action === 'add') {
|
|
// Create new DKIM record if not found
|
|
return self::update_dns_record($domain_post->ID, $domain_post->post_title, 'TXT', $name, $wrapped_content, $ttl);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check if the domain or its associated IP is on any blacklist.
|
|
*
|
|
* @param mixed $host The domain or IP address to check.
|
|
* @return array Blacklist check results.
|
|
*/
|
|
private static function check_blacklists($host)
|
|
{
|
|
// log_to_file("check_blacklists - Running check_blacklists for $host");
|
|
|
|
$blacklist_servers = [
|
|
'zen.spamhaus.org',
|
|
'bl.spamcop.net',
|
|
'b.barracudacentral.org',
|
|
];
|
|
|
|
$results = [];
|
|
$ip = filter_var($host, FILTER_VALIDATE_IP) ? $host : gethostbyname($host);
|
|
|
|
foreach ($blacklist_servers as $server) {
|
|
$query = implode('.', array_reverse(explode('.', $ip))) . '.' . $server;
|
|
$listed = gethostbyname($query) !== $query;
|
|
$results[$server] = $listed ? 'Listed' : 'Not Listed';
|
|
}
|
|
|
|
return [
|
|
'host' => $host,
|
|
'blacklists' => $results,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Fix deliverability-related DNS issues for the given domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @return array Results of the actions performed.
|
|
*/
|
|
public static function fix_deliverability_dns_issues($domain)
|
|
{
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
throw new InvalidArgumentException('Invalid domain input.');
|
|
}
|
|
$domain_name = $domain_post->post_title;
|
|
|
|
// Create a full backup of the DNS zone before starting
|
|
// log_to_file("fix_deliverability_dns_issues - Exporting DNS zone for $domain_name");
|
|
|
|
$backup_id = self::export_dns_zone($domain_post);
|
|
if (is_wp_error($backup_id)) {
|
|
log_to_file("fix_deliverability_dns_issues - Error while exporting DNS zone. Quitting");
|
|
wp_send_json_error($backup_id->get_error_message());
|
|
}
|
|
// log_to_file("fix_deliverability_dns_issues - Done exporting DNS zone for $domain_name");
|
|
|
|
$results = [];
|
|
|
|
$client = self::get_cloudflare_client($domain);
|
|
|
|
// Fetch existing DMARC record
|
|
// log_to_file("fix_deliverability_dns_issues - Fetching TXT records for $domain_name");
|
|
$dns_records = self::fetch_dns_records($client, "TXT");
|
|
|
|
// Helper to find specific records
|
|
$findRecord = function ($records, $contains, $search_name = false) {
|
|
foreach ($records as $record) {
|
|
if ($search_name) {
|
|
if (strpos($record['name'], $contains) !== false) {
|
|
return $record;
|
|
}
|
|
} else {
|
|
if (strpos($record['content'], $contains) !== false) {
|
|
return $record;
|
|
}
|
|
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// SPF
|
|
// log_to_file("fix_deliverability_dns_issues - Fixing SPF record for $domain_name");
|
|
$default_spf_include = get_field('default_spf_include', 'option');
|
|
if ($default_spf_include) {
|
|
$spf_record = $findRecord($dns_records, 'v=spf1');
|
|
if ($spf_record) {
|
|
// log_to_file("fix_deliverability_dns_issues - Current SPF record: ",$spf_record);
|
|
if (strpos($spf_record['content'], $default_spf_include) === false) {
|
|
// Add default_spf_include to the SPF record
|
|
// $updated_spf_content = rtrim(str_replace('-all', '', $spf_record['content'])) . " include:{$default_spf_include} -all";
|
|
try {
|
|
self::update_spf_record($domain_post, $default_spf_include, 'add');
|
|
$results['spf'] = "Updated SPF to include $default_spf_include";
|
|
} catch (Exception $e) {
|
|
$results['spf'] = 'Error: ' . $e->getMessage();
|
|
}
|
|
} else {
|
|
$results['spf'] = 'SPF record already includes default include.';
|
|
}
|
|
} else {
|
|
try {
|
|
$results['spf'] = self::update_spf_record($domain_post, $default_spf_include, 'add');
|
|
} catch (Exception $e) {
|
|
$results['spf'] = 'Error: ' . $e->getMessage();
|
|
}
|
|
// $results['spf'] = 'No SPF record found for the domain.';
|
|
}
|
|
} else {
|
|
$results['spf'] = 'No default SPF include found in settings.';
|
|
}
|
|
|
|
// DKIM
|
|
// log_to_file("fix_deliverability_dns_issues - Fixing DKIM record for $domain_name");
|
|
$servers = get_field('servers', $domain_post->ID);
|
|
if ($servers && is_array($servers)) {
|
|
$results['dkim'] = [];
|
|
foreach ($servers as $server) {
|
|
$selector = get_field('dkim_selector', $server);
|
|
$value = get_field('dkim_value', $server);
|
|
// log_to_file("fix_deliverability_dns_issues - DKIM $selector: $value");
|
|
if ($selector && $value) {
|
|
$dkim_record = $findRecord($dns_records, "{$selector}._domainkey", true);
|
|
if (!$dkim_record) {
|
|
try {
|
|
$dkim_result = self::update_dkim_record($domain_post, $selector, 'add', $value);
|
|
$results['dkim'][$selector] = $dkim_result
|
|
? "DKIM record for selector '{$selector}' added successfully."
|
|
: "Failed to add DKIM record for selector '{$selector}'.";
|
|
} catch (Exception $e) {
|
|
$results['dkim'][$selector] = 'Error: ' . $e->getMessage();
|
|
}
|
|
} else {
|
|
$results['dkim'][$selector] = "DKIM record for selector '{$selector}' already exists.";
|
|
}
|
|
} else {
|
|
$results['dkim'][$server] = 'Missing DKIM selector or value for server.';
|
|
}
|
|
}
|
|
} else {
|
|
$results['dkim'] = 'No servers selected for the domain.';
|
|
}
|
|
|
|
// DMARC
|
|
// log_to_file("fix_deliverability_dns_issues - Fixing DMARC record for $domain_name");
|
|
$dmarc_record = $findRecord($dns_records, '_dmarc', true);
|
|
if (!$dmarc_record) {
|
|
// log_to_file("fix_deliverability_dns_issues - No existing DMARC record found. Creating one.");
|
|
$dmarc_params = [
|
|
'v' => 'DMARC1',
|
|
'p' => 'quarantine',
|
|
'pct' => 100,
|
|
'aspf' => 'relaxed',
|
|
'adkim' => 'relaxed',
|
|
];
|
|
try {
|
|
$dmarc_result = self::update_dmarc_record($domain_post, $dmarc_params);
|
|
$results['dmarc'] = $dmarc_result
|
|
? 'Default DMARC record created successfully.'
|
|
: 'Failed to create DMARC record.';
|
|
} catch (Exception $e) {
|
|
$results['dmarc'] = 'Error: ' . $e->getMessage();
|
|
}
|
|
} else {
|
|
// log_to_file("fix_deliverability_dns_issues - Existing DMARC record found. Doing nothing.");
|
|
$results['dmarc'] = "Existing DMARC record found: {$dmarc_record['content']}";
|
|
}
|
|
// $results['health_report'] = '';
|
|
|
|
// Save a health report after fixing DNS issues
|
|
try {
|
|
// log_to_file("fix_deliverability_dns_issues - Running save_domain_health_report() for {$domain_name}");
|
|
// $report_id = self::save_domain_health_report($domain_post);
|
|
$report = self::generate_domain_report($domain);
|
|
$content = json_encode($report);
|
|
$report_id = RL_MailWarmer_DB_Helper::insert_health_report_backup($domain, $content);
|
|
|
|
if (is_wp_error($report_id)) {
|
|
wp_send_json_error($report_id->get_error_message());
|
|
// return false;
|
|
} else {
|
|
update_post_meta($domain_post->ID,'domain_health_report',$content);
|
|
// wp_send_json_success($report_id);
|
|
}
|
|
$results['health_report'] = "Health report saved successfully with ID {$report_id}.";
|
|
// log_to_file("fix_deliverability_dns_issues - Health report saved successfully with ID {$report_id}");
|
|
} catch (Exception $e) {
|
|
$results['health_report'] = 'Error saving health report: ' . $e->getMessage();
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Enable Resend for the given domain.
|
|
*
|
|
* @param mixed $domain The domain input (post object, array, or ID).
|
|
* @return array Results of the actions performed.
|
|
*/
|
|
public static function enabled_resend_for_domain($domain)
|
|
{
|
|
$domain_post = self::get_domain_post($domain);
|
|
if (!$domain_post) {
|
|
throw new InvalidArgumentException('Invalid domain input.');
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* TO DO!
|
|
*
|
|
*/
|
|
// modify_domain_on_server - Creates/Updates/Deletes the specified domain on the specified server if it doesn't already exist (VirtualMin). Checks/Updates DNS config to include server IP in SPF, DKIM key, etc.
|
|
|
|
/**
|
|
* Modify a domain account on a VirtualMin server.
|
|
*
|
|
* Creates, updates, or deletes a virtual server for the specified domain.
|
|
*
|
|
* @param int $domain_id The domain post ID.
|
|
* @param string $action The action to perform: 'create', 'update', or 'delete'.
|
|
* @return bool|WP_Error True on success, WP_Error on failure.
|
|
*/
|
|
private static function modify_domain_account_on_server($domain_id, $action)
|
|
{
|
|
// Validate domain post
|
|
$domain = get_post($domain_id);
|
|
if (!$domain || $domain->post_type !== 'domain') {
|
|
return new WP_Error('invalid_domain', __('Invalid domain post.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Fetch associated server posts
|
|
$server_ids = get_post_meta($domain_id, 'associated_servers', true); // Assume this holds server IDs
|
|
if (empty($server_ids) || !is_array($server_ids)) {
|
|
return new WP_Error('missing_servers', __('No associated servers found for the domain.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
$domain_name = $domain->post_title;
|
|
|
|
// 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_user = get_post_meta($server_id, 'username', true);
|
|
$server_port = get_post_meta($server_id, 'ssh_port', true) ?: 22;
|
|
$server_password = get_post_meta($server_id, 'ssh_private_key', true);
|
|
|
|
if (!$server_ip || !$server_user || !$server_password) {
|
|
return new WP_Error('missing_server_credentials', __('Missing server credentials.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Build VirtualMin command
|
|
$command = "virtualmin";
|
|
if ($action === 'create') {
|
|
$command .= " create-domain --domain $domain_name --unix --dir --web --email";
|
|
} elseif ($action === 'update') {
|
|
$command .= " modify-domain --domain $domain_name";
|
|
} elseif ($action === 'delete') {
|
|
$command .= " delete-domain --domain $domain_name";
|
|
} else {
|
|
return new WP_Error('invalid_action', __('Invalid action specified.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Execute the command via SSH
|
|
// $ssh = new phpseclib3\Net\SSH2($server_ip, $ssh_port);
|
|
// $key = phpseclib3\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_domain_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
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
add_action('restrict_manage_posts', function ($post_type) {
|
|
if ($post_type === 'domain') {
|
|
?>
|
|
<button id="check-domain-health-button" class="button button-primary" style="margin-left: 10px;">
|
|
<?php esc_html_e('Check Domain Health', 'rl-mailwarmer'); ?>
|
|
</button>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
document.getElementById('check-domain-health-button').addEventListener('click', function (event) {
|
|
event.preventDefault(); // Prevent form submission
|
|
|
|
const selectedDomains = Array.from(document.querySelectorAll('.check-column input[type="checkbox"]:checked'))
|
|
.map(checkbox => checkbox.value)
|
|
.filter(value => value !== 'on'); // Exclude the "select all" checkbox
|
|
|
|
if (selectedDomains.length === 0) {
|
|
alert('<?php esc_html_e('Please select at least one domain.', 'rl-mailwarmer'); ?>');
|
|
return;
|
|
}
|
|
|
|
if (confirm('<?php esc_html_e('Are you sure you want to check the health of the selected domains?', 'rl-mailwarmer'); ?>')) {
|
|
jQuery.post(ajaxurl, {
|
|
action: 'rl_check_domain_registration',
|
|
domain_ids: selectedDomains
|
|
}, function (response) {
|
|
alert(response.data.message);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
<?php
|
|
}
|
|
});
|
|
|
|
add_action('wp_ajax_rl_check_domain_registration', function () {
|
|
$domain_ids = isset($_POST['domain_ids']) ? array_map('intval', $_POST['domain_ids']) : [];
|
|
|
|
if (empty($domain_ids)) {
|
|
wp_send_json_error(['message' => __('No domains selected.', 'rl-mailwarmer')]);
|
|
}
|
|
|
|
foreach ($domain_ids as $domain_id) {
|
|
// Run the save_domain_health_report function
|
|
$result = RL_MailWarmer_Domain_Helper::save_domain_health_report($domain_id);
|
|
|
|
if (is_wp_error($result)) {
|
|
wp_send_json_error(['message' => $result->get_error_message()]);
|
|
}
|
|
}
|
|
|
|
wp_send_json_success(['message' => __('Domain health check completed for selected domains.', 'rl-mailwarmer')]);
|
|
});
|
|
|
|
|
|
|
|
add_action('add_meta_boxes', 'rl_mailwarmer_add_generate_accounts_metabox');
|
|
function rl_mailwarmer_add_generate_accounts_metabox() {
|
|
add_meta_box(
|
|
'rl-generate-accounts',
|
|
__('Generate Accounts', 'rl-mailwarmer'),
|
|
'rl_mailwarmer_generate_accounts_metabox_callback',
|
|
'domain',
|
|
'side',
|
|
'default'
|
|
);
|
|
}
|
|
|
|
function rl_mailwarmer_generate_accounts_metabox_callback($post) {
|
|
// Nonce field for security
|
|
wp_nonce_field('rl_generate_accounts_nonce', 'rl_generate_accounts_nonce_field');
|
|
|
|
?>
|
|
<p>
|
|
<label for="rl_generate_account_qty"><?php _e('Number of Accounts', 'rl-mailwarmer'); ?></label>
|
|
<input type="number" id="rl_generate_account_qty" name="rl_generate_account_qty" value="1" min="1" max="50" />
|
|
</p>
|
|
<button type="button" id="rl_generate_accounts_button" class="button button-primary">
|
|
<?php _e('Generate Accounts', 'rl-mailwarmer'); ?>
|
|
</button>
|
|
<div id="rl_generate_accounts_result"></div>
|
|
<?php
|
|
}
|
|
|
|
|
|
add_action('admin_enqueue_scripts', 'rl_mailwarmer_enqueue_generate_accounts_script');
|
|
function rl_mailwarmer_enqueue_generate_accounts_script($hook) {
|
|
if ($hook === 'post.php' || $hook === 'post-new.php') {
|
|
wp_enqueue_script(
|
|
'rl-generate-accounts',
|
|
RL_MAILWARMER_URL . 'js/rl-generate-accounts.js',
|
|
['jquery'],
|
|
'1.0',
|
|
true
|
|
);
|
|
|
|
wp_localize_script('rl-generate-accounts', 'rlGenerateAccounts', [
|
|
'ajax_url' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('rl_generate_accounts_nonce'),
|
|
]);
|
|
}
|
|
}
|
|
|
|
add_action('wp_ajax_rl_generate_random_accounts', 'rl_mailwarmer_generate_random_accounts_handler');
|
|
function rl_mailwarmer_generate_random_accounts_handler() {
|
|
check_ajax_referer('rl_generate_accounts_nonce', 'security');
|
|
|
|
$post_id = intval($_POST['post_id']);
|
|
$qty = intval($_POST['qty']);
|
|
|
|
if (!$post_id || !$qty || $qty < 1 || $qty > 50) {
|
|
wp_send_json_error(__('Invalid input.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
$accounts = RL_MailWarmer_Email_Helper::generate_random_accounts($post_id, $qty);
|
|
|
|
if (is_array($accounts)) {
|
|
wp_send_json_success($accounts);
|
|
} else {
|
|
wp_send_json_error($accounts);
|
|
}
|
|
}
|