@@ -458,7 +529,18 @@ class WooCow_Admin {
public function page_logs(): void {
global $wpdb;
$servers = $wpdb->get_results( "SELECT id, name FROM {$wpdb->prefix}woocow_servers WHERE active=1 ORDER BY name" );
- $log_types = [ 'postfix', 'dovecot', 'rspamd', 'ratelimit', 'api', 'acme', 'autodiscover', 'sogo', 'netfilter', 'watchdog' ];
+ $log_types = [
+ 'postfix' => 'Postfix',
+ 'dovecot' => 'Dovecot',
+ 'rspamd-history' => 'Rspamd',
+ 'ratelimited' => 'Rate Limit',
+ 'api' => 'API',
+ 'acme' => 'ACME',
+ 'autodiscover' => 'Autodiscover',
+ 'sogo' => 'SOGo',
+ 'netfilter' => 'Netfilter',
+ 'watchdog' => 'Watchdog',
+ ];
?>
WooCow – Server Logs
@@ -471,8 +553,8 @@ class WooCow_Admin {
-
-
+ $label ) : ?>
+
Load Logs
@@ -485,6 +567,34 @@ class WooCow_Admin {
get_results( "SELECT id, name FROM {$wpdb->prefix}woocow_servers WHERE active=1 ORDER BY name" );
+ ?>
+
+
WooCow – Quarantine
+
View and manage quarantined messages across all servers. You can permanently delete messages or blacklist senders by domain.
+
+
+
+ — Select server —
+
+ name ); ?>
+
+
+ Load Quarantine
+
+
+
+
+
Select a server above to view quarantined messages.
+
+
+ json_err( $result['error'] ?? 'Failed to fetch domains.' );
}
- $domains = array_map( fn( $d ) => [ 'domain' => $d['domain_name'], 'active' => $d['active'] ], (array) $result['data'] );
+ $domains = array_map( function ( $d ) {
+ $rl = is_array( $d['rl'] ?? false ) ? $d['rl'] : [];
+ return [
+ 'domain' => $d['domain_name'],
+ 'active' => $d['active'],
+ 'description' => $d['description'] ?? '',
+ 'mailboxes' => $d['max_num_mboxes_for_domain'] ?? 0,
+ 'mboxes_in' => $d['mboxes_in_domain'] ?? 0,
+ 'aliases' => $d['max_num_aliases_for_domain'] ?? 0,
+ 'aliases_in' => $d['aliases_in_domain'] ?? 0,
+ 'quota' => (int) round( ( $d['max_quota_for_domain'] ?? 0 ) / 1024 / 1024 ),
+ 'defquota' => (int) round( ( $d['def_new_mailbox_quota'] ?? 0 ) / 1024 / 1024 ),
+ 'quota_used' => (int) round( ( $d['quota_used_in_domain'] ?? 0 ) / 1024 / 1024 ),
+ 'relayhost' => $d['relayhost'] ?? '0',
+ 'rl_value' => $rl['value'] ?? 0,
+ 'rl_frame' => $rl['frame'] ?? 's',
+ 'gal' => $d['gal'] ?? '0',
+ 'backupmx' => $d['backupmx'] ?? '0',
+ ];
+ }, (array) $result['data'] );
$this->json_ok( $domains );
}
@@ -904,6 +1033,90 @@ class WooCow_Admin {
] );
}
+ public function ajax_woocow_admin_domain_edit(): void {
+ $this->verify();
+
+ $server_id = absint( $_POST['server_id'] ?? 0 );
+ $domain = sanitize_text_field( $_POST['domain'] ?? '' );
+
+ if ( ! $domain ) {
+ $this->json_err( 'Domain name is required.' );
+ }
+
+ $server = $this->get_server( $server_id );
+ if ( ! $server ) {
+ $this->json_err( 'Server not found.' );
+ }
+
+ $attr = [];
+
+ if ( isset( $_POST['description'] ) ) {
+ $attr['description'] = sanitize_text_field( $_POST['description'] );
+ }
+ if ( isset( $_POST['mailboxes'] ) ) {
+ $attr['mailboxes'] = absint( $_POST['mailboxes'] );
+ }
+ if ( isset( $_POST['aliases'] ) ) {
+ $attr['aliases'] = absint( $_POST['aliases'] );
+ }
+ if ( isset( $_POST['quota'] ) ) {
+ $attr['quota'] = absint( $_POST['quota'] );
+ $attr['maxquota'] = absint( $_POST['quota'] );
+ }
+ if ( isset( $_POST['defquota'] ) ) {
+ $attr['defquota'] = absint( $_POST['defquota'] );
+ }
+ if ( isset( $_POST['rl_value'] ) ) {
+ $attr['rl_value'] = absint( $_POST['rl_value'] );
+ }
+ if ( isset( $_POST['rl_frame'] ) ) {
+ $frame = sanitize_text_field( $_POST['rl_frame'] );
+ if ( in_array( $frame, [ 's', 'm', 'h', 'd' ], true ) ) {
+ $attr['rl_frame'] = $frame;
+ }
+ }
+ if ( isset( $_POST['relayhost'] ) ) {
+ $attr['relayhost'] = absint( $_POST['relayhost'] );
+ }
+ if ( isset( $_POST['active'] ) ) {
+ $attr['active'] = absint( $_POST['active'] );
+ }
+
+ $api = WooCow_API::from_server( $server );
+ $result = $api->edit_domain( [ $domain ], $attr );
+
+ if ( ! $result['success'] ) {
+ $this->json_err( $result['error'] ?? 'Failed to update domain.' );
+ }
+
+ $this->json_ok();
+ }
+
+ public function ajax_woocow_admin_domain_delete(): void {
+ $this->verify();
+
+ $server_id = absint( $_POST['server_id'] ?? 0 );
+ $domain = sanitize_text_field( $_POST['domain'] ?? '' );
+
+ if ( ! $domain ) {
+ $this->json_err( 'Domain name is required.' );
+ }
+
+ $server = $this->get_server( $server_id );
+ if ( ! $server ) {
+ $this->json_err( 'Server not found.' );
+ }
+
+ $api = WooCow_API::from_server( $server );
+ $result = $api->delete_domain( $domain );
+
+ if ( ! $result['success'] ) {
+ $this->json_err( $result['error'] ?? 'Failed to delete domain.' );
+ }
+
+ $this->json_ok();
+ }
+
// ── AJAX: Transports ──────────────────────────────────────────────────────
public function ajax_woocow_admin_relayhosts_list(): void {
@@ -982,7 +1195,7 @@ class WooCow_Admin {
$server_id = absint( $_POST['server_id'] ?? 0 );
$log_type = sanitize_key( $_POST['log_type'] ?? 'postfix' );
- $allowed = [ 'postfix', 'dovecot', 'rspamd', 'ratelimit', 'api', 'acme', 'autodiscover', 'sogo', 'netfilter', 'watchdog' ];
+ $allowed = [ 'postfix', 'dovecot', 'rspamd-history', 'ratelimited', 'api', 'acme', 'autodiscover', 'sogo', 'netfilter', 'watchdog' ];
if ( ! in_array( $log_type, $allowed, true ) ) {
$this->json_err( 'Invalid log type.' );
}
@@ -993,7 +1206,7 @@ class WooCow_Admin {
}
$api = WooCow_API::from_server( $server );
- $result = $api->request( 'GET', '/api/v1/get/logs/' . $log_type );
+ $result = $api->request( 'GET', '/api/v1/get/logs/' . $log_type . '/100' );
if ( ! $result['success'] ) {
$this->json_err( $result['error'] ?? 'Failed to fetch logs.' );
@@ -1001,4 +1214,76 @@ class WooCow_Admin {
$this->json_ok( $result['data'] ?? [] );
}
+
+ // ── AJAX: Admin Quarantine ─────────────────────────────────────────────────
+
+ public function ajax_woocow_admin_quarantine(): void {
+ $this->verify();
+
+ $server_id = absint( $_POST['server_id'] ?? 0 );
+ $server = $this->get_server( $server_id );
+ if ( ! $server ) {
+ $this->json_err( 'Server not found.' );
+ }
+
+ $api = WooCow_API::from_server( $server );
+ $result = $api->get_quarantine();
+
+ if ( ! $result['success'] ) {
+ $this->json_err( $result['error'] ?? 'Failed to fetch quarantine.' );
+ }
+
+ $this->json_ok( $result['data'] ?? [] );
+ }
+
+ public function ajax_woocow_admin_quarantine_delete(): void {
+ $this->verify();
+
+ $server_id = absint( $_POST['server_id'] ?? 0 );
+ $qid = absint( $_POST['qid'] ?? 0 );
+
+ if ( ! $qid ) {
+ $this->json_err( 'Message ID is required.' );
+ }
+
+ $server = $this->get_server( $server_id );
+ if ( ! $server ) {
+ $this->json_err( 'Server not found.' );
+ }
+
+ $api = WooCow_API::from_server( $server );
+ $result = $api->delete_quarantine( $qid );
+
+ if ( ! $result['success'] ) {
+ $this->json_err( $result['error'] ?? 'Failed to delete quarantine message.' );
+ }
+
+ $this->json_ok();
+ }
+
+ public function ajax_woocow_admin_quarantine_block(): void {
+ $this->verify();
+
+ $server_id = absint( $_POST['server_id'] ?? 0 );
+ $domain = sanitize_text_field( $_POST['domain'] ?? '' );
+ $object_from = sanitize_text_field( $_POST['object_from'] ?? '' );
+
+ if ( ! $domain || ! $object_from ) {
+ $this->json_err( 'Domain and sender address are required.' );
+ }
+
+ $server = $this->get_server( $server_id );
+ if ( ! $server ) {
+ $this->json_err( 'Server not found.' );
+ }
+
+ $api = WooCow_API::from_server( $server );
+ $result = $api->add_domain_policy( $domain, $object_from, 'bl' );
+
+ if ( ! $result['success'] ) {
+ $this->json_err( $result['error'] ?? 'Failed to blacklist sender.' );
+ }
+
+ $this->json_ok();
+ }
}
diff --git a/includes/class-woocow-api.php b/includes/class-woocow-api.php
index dc628ff..0b4c415 100644
--- a/includes/class-woocow-api.php
+++ b/includes/class-woocow-api.php
@@ -183,6 +183,24 @@ class WooCow_API {
return $this->request( 'POST', '/api/v1/delete/quarantine', [ 'items' => [ $id ] ] );
}
+ // ── Domain Policies ──────────────────────────────────────────────────────
+
+ public function get_domain_policy_bl( string $domain ): array {
+ return $this->request( 'GET', '/api/v1/get/policy_bl_domain/' . rawurlencode( $domain ) );
+ }
+
+ public function add_domain_policy( string $domain, string $object_from, string $list = 'bl' ): array {
+ return $this->request( 'POST', '/api/v1/add/domain-policy', [
+ 'domain' => $domain,
+ 'object_from' => $object_from,
+ 'object_list' => $list,
+ ] );
+ }
+
+ public function delete_domain_policy( array $prefids ): array {
+ return $this->request( 'POST', '/api/v1/delete/domain-policy', [ 'items' => $prefids ] );
+ }
+
// ── Helpers ──────────────────────────────────────────────────────────────
public function get_webmail_url(): string {