2026-02-27 08:06:22 +01:00
|
|
|
|
<?php
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Mailcow API client.
|
|
|
|
|
|
* Wraps all HTTP calls to a single Mailcow server instance.
|
|
|
|
|
|
*/
|
|
|
|
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
|
|
|
|
|
|
|
|
class WooCow_API {
|
|
|
|
|
|
|
|
|
|
|
|
private string $base_url;
|
|
|
|
|
|
private string $api_key;
|
|
|
|
|
|
private int $timeout = 20;
|
|
|
|
|
|
|
|
|
|
|
|
public function __construct( string $url, string $api_key ) {
|
|
|
|
|
|
$this->base_url = rtrim( $url, '/' );
|
|
|
|
|
|
$this->api_key = $api_key;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Core HTTP ────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
feat: domains, transports, logs, quarantine, spam filter, i18n + UX fixes
Features added:
- Admin > Domains: add domains to Mailcow servers, auto-generate DKIM,
display full DNS record set (MX, SPF, DMARC, DKIM, autoconfig CNAMEs)
with one-click copy per record
- Admin > Transports: manage sender-dependent relay hosts (add/delete)
- Admin > Logs: view Postfix, Dovecot, Rspamd, Ratelimit, API and other
server logs in a dark scrollable panel
- My Account: per-domain Quarantine panel — view score, sender, subject,
date; permanently delete quarantined messages
- My Account: per-mailbox Spam Filter slider (1–15 threshold) saved via API
- My Account: Aliases & Forwarders (alias creation doubles as forwarder
to any external address)
UX fixes:
- Quota 0 now displays ∞ (unlimited) in both admin and account views
- Admin mailbox action buttons replaced with Dashicon icon buttons
(lock, chart-bar, trash) with title tooltips
i18n:
- load_plugin_textdomain registered on init hook
- All user-facing PHP strings wrapped in __() / esc_html__()
- Translated strings array passed to account JS via wp_localize_script
- woocow-es_ES.po/.mo — Spanish translation
- woocow-ro_RO.po/.mo — Romanian translation (with correct plural forms)
- English remains the fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:38:52 +01:00
|
|
|
|
public function request( string $method, string $endpoint, array $body = [] ): array {
|
2026-02-27 08:06:22 +01:00
|
|
|
|
$args = [
|
|
|
|
|
|
'method' => strtoupper( $method ),
|
|
|
|
|
|
'timeout' => $this->timeout,
|
|
|
|
|
|
'sslverify' => apply_filters( 'woocow_sslverify', true ),
|
|
|
|
|
|
'headers' => [
|
|
|
|
|
|
'X-API-Key' => $this->api_key,
|
|
|
|
|
|
'Content-Type' => 'application/json',
|
|
|
|
|
|
'Accept' => 'application/json',
|
|
|
|
|
|
],
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
if ( ! empty( $body ) ) {
|
|
|
|
|
|
$args['body'] = wp_json_encode( $body );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$response = wp_remote_request( $this->base_url . $endpoint, $args );
|
|
|
|
|
|
|
|
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
|
|
|
|
return [ 'success' => false, 'error' => $response->get_error_message() ];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$code = wp_remote_retrieve_response_code( $response );
|
|
|
|
|
|
$raw = wp_remote_retrieve_body( $response );
|
|
|
|
|
|
$data = json_decode( $raw, true );
|
|
|
|
|
|
|
|
|
|
|
|
if ( $code === 401 ) {
|
|
|
|
|
|
return [ 'success' => false, 'error' => 'Authentication failed – check your API key.' ];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'success' => ( $code >= 200 && $code < 300 ),
|
|
|
|
|
|
'data' => $data,
|
|
|
|
|
|
'code' => $code,
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Health / Version ─────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public function test_connection(): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/status/version' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Domains ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public function get_domains(): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/domain/all' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function get_domain( string $domain ): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/domain/' . rawurlencode( $domain ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function create_domain( array $data ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/add/domain', $data );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function edit_domain( array $items, array $attr ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/edit/domain', [ 'items' => $items, 'attr' => $attr ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function delete_domain( string $domain ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/delete/domain', [ 'items' => [ $domain ] ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Mailboxes ────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public function get_all_mailboxes(): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/mailbox/all' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function get_mailbox( string $email ): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/mailbox/' . rawurlencode( $email ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function get_domain_mailboxes( string $domain ): array {
|
2026-02-27 08:18:08 +01:00
|
|
|
|
return $this->request( 'GET', '/api/v1/get/mailbox/all/' . rawurlencode( $domain ) );
|
2026-02-27 08:06:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function create_mailbox( array $data ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/add/mailbox', $data );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function edit_mailbox( array $items, array $attr ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/edit/mailbox', [ 'items' => $items, 'attr' => $attr ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function delete_mailbox( string $email ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/delete/mailbox', [ 'items' => [ $email ] ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Aliases ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public function get_all_aliases(): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/alias/all' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function get_alias( int $id ): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/alias/' . $id );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function create_alias( array $data ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/add/alias', $data );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function edit_alias( array $items, array $attr ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/edit/alias', [ 'items' => $items, 'attr' => $attr ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function delete_alias( int $id ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/delete/alias', [ 'items' => [ $id ] ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Domain Admins ────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public function get_domain_admins(): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/domain-admin/all' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function create_domain_admin( array $data ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/add/domain-admin', $data );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function delete_domain_admin( string $username ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/delete/domain-admin', [ 'items' => [ $username ] ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
feat: domains, transports, logs, quarantine, spam filter, i18n + UX fixes
Features added:
- Admin > Domains: add domains to Mailcow servers, auto-generate DKIM,
display full DNS record set (MX, SPF, DMARC, DKIM, autoconfig CNAMEs)
with one-click copy per record
- Admin > Transports: manage sender-dependent relay hosts (add/delete)
- Admin > Logs: view Postfix, Dovecot, Rspamd, Ratelimit, API and other
server logs in a dark scrollable panel
- My Account: per-domain Quarantine panel — view score, sender, subject,
date; permanently delete quarantined messages
- My Account: per-mailbox Spam Filter slider (1–15 threshold) saved via API
- My Account: Aliases & Forwarders (alias creation doubles as forwarder
to any external address)
UX fixes:
- Quota 0 now displays ∞ (unlimited) in both admin and account views
- Admin mailbox action buttons replaced with Dashicon icon buttons
(lock, chart-bar, trash) with title tooltips
i18n:
- load_plugin_textdomain registered on init hook
- All user-facing PHP strings wrapped in __() / esc_html__()
- Translated strings array passed to account JS via wp_localize_script
- woocow-es_ES.po/.mo — Spanish translation
- woocow-ro_RO.po/.mo — Romanian translation (with correct plural forms)
- English remains the fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:38:52 +01:00
|
|
|
|
// ── DKIM ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public function get_dkim( string $domain ): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/dkim/' . rawurlencode( $domain ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function generate_dkim( string $domain, string $selector = 'dkim', int $key_size = 2048 ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/add/dkim', [
|
|
|
|
|
|
'domains' => $domain,
|
|
|
|
|
|
'dkim_selector' => $selector,
|
|
|
|
|
|
'key_size' => $key_size,
|
|
|
|
|
|
] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Relayhosts ───────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public function get_relayhosts(): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/relayhost/all' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function create_relayhost( array $data ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/add/relayhost', $data );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function delete_relayhost( int $id ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/delete/relayhost', [ 'items' => [ $id ] ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Quarantine ───────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public function get_quarantine(): array {
|
|
|
|
|
|
return $this->request( 'GET', '/api/v1/get/quarantine/all' );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function delete_quarantine( int $id ): array {
|
|
|
|
|
|
return $this->request( 'POST', '/api/v1/delete/quarantine', [ 'items' => [ $id ] ] );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-27 08:06:22 +01:00
|
|
|
|
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
public function get_webmail_url(): string {
|
|
|
|
|
|
return $this->base_url . '/SOGo';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public function get_base_url(): string {
|
|
|
|
|
|
return $this->base_url;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ── Static factory ───────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
/** Build API instance from a server DB row. */
|
|
|
|
|
|
public static function from_server( object $server ): self {
|
|
|
|
|
|
return new self( $server->url, $server->api_key );
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|