admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'woocow_account' ),
'i18n' => [
'load_mailboxes' => __( 'Load Mailboxes', 'woocow' ),
'refresh' => __( 'Refresh', 'woocow' ),
'loading' => __( 'Loading…', 'woocow' ),
'no_mailboxes' => __( 'No mailboxes yet. Create one below.', 'woocow' ),
'change_password' => __( 'Change Password', 'woocow' ),
'aliases_forwarders' => __( 'Aliases & Forwarders', 'woocow' ),
'spam_filter' => __( 'Spam Filter', 'woocow' ),
'webmail' => __( 'Webmail', 'woocow' ),
'add_alias' => __( 'Add Alias / Forwarder', 'woocow' ),
'no_aliases' => __( 'No aliases for this domain yet.', 'woocow' ),
'add_mailbox' => __( 'Add Mailbox', 'woocow' ),
'create_mailbox' => __( 'Create Mailbox', 'woocow' ),
'cancel' => __( 'Cancel', 'woocow' ),
'save' => __( 'Save', 'woocow' ),
'delete' => __( 'Delete', 'woocow' ),
'quarantine' => __( 'Quarantine', 'woocow' ),
'no_quarantine' => __( 'No quarantined messages for this domain.', 'woocow' ),
'q_release_note' => __( 'To release a message to your inbox, use the link in your quarantine notification email or via Webmail.', 'woocow' ),
'q_delete_confirm' => __( 'Permanently delete this quarantined message?', 'woocow' ),
'spam_threshold' => __( 'Spam Filter Threshold', 'woocow' ),
'spam_help' => __( 'Lower = stricter. Default is 5. Emails above this score go to spam/quarantine.', 'woocow' ),
'update_password' => __( 'Update Password', 'woocow' ),
'passwords_mismatch' => __( 'Passwords do not match or are empty.', 'woocow' ),
'unlimited' => __( 'Unlimited', 'woocow' ),
],
] );
}
// ── My Account page render ────────────────────────────────────────────────
public function render_page(): void {
if ( ! is_user_logged_in() ) {
echo '
' . esc_html__( 'Please log in to manage your email hosting.', 'woocow' ) . '
';
return;
}
$customer_id = get_current_user_id();
$assignments = $this->get_customer_assignments( $customer_id );
if ( empty( $assignments ) ) {
echo '' . esc_html__( 'You have no email domains assigned yet. Please contact support.', 'woocow' ) . '
';
return;
}
?>
get_results( $wpdb->prepare( "
SELECT a.id, a.server_id, a.domain, s.name AS server_name, s.url AS server_url
FROM {$wpdb->prefix}woocow_assignments a
JOIN {$wpdb->prefix}woocow_servers s ON s.id = a.server_id
WHERE a.customer_id = %d AND s.active = 1
ORDER BY a.domain
", $customer_id ) );
foreach ( $rows as $row ) {
$row->webmail_url = rtrim( $row->server_url, '/' ) . '/SOGo';
}
return $rows;
}
private function get_server( int $id ): ?object {
global $wpdb;
return $wpdb->get_row( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocow_servers WHERE id = %d AND active = 1",
$id
) );
}
/** Verify that the current user owns the given assignment. */
private function verify_ownership( int $server_id, string $domain ): bool {
global $wpdb;
$found = $wpdb->get_var( $wpdb->prepare( "
SELECT id FROM {$wpdb->prefix}woocow_assignments
WHERE customer_id = %d AND server_id = %d AND domain = %s
", get_current_user_id(), $server_id, $domain ) );
return (bool) $found;
}
private function account_verify(): void {
check_ajax_referer( 'woocow_account', 'nonce' );
if ( ! is_user_logged_in() ) {
wp_send_json_error( 'Not logged in.', 401 );
}
}
// ── AJAX: Account ─────────────────────────────────────────────────────────
public function ajax_woocow_acct_domains(): void {
$this->account_verify();
$assignments = $this->get_customer_assignments( get_current_user_id() );
wp_send_json_success( $assignments );
}
public function ajax_woocow_acct_mailboxes(): void {
$this->account_verify();
$server_id = absint( $_POST['server_id'] ?? 0 );
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
if ( ! $this->verify_ownership( $server_id, $domain ) ) {
wp_send_json_error( 'Access denied.', 403 );
}
$server = $this->get_server( $server_id );
if ( ! $server ) {
wp_send_json_error( 'Server unavailable.' );
}
$api = WooCow_API::from_server( $server );
$result = $api->get_domain_mailboxes( $domain );
if ( ! $result['success'] ) {
wp_send_json_error( $result['error'] ?? 'Could not load mailboxes.' );
}
wp_send_json_success( [
'mailboxes' => $result['data'] ?? [],
'webmail_url' => $api->get_webmail_url(),
] );
}
public function ajax_woocow_acct_mailbox_create(): void {
$this->account_verify();
$server_id = absint( $_POST['server_id'] ?? 0 );
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
$local_part = sanitize_text_field( $_POST['local_part'] ?? '' );
$name = sanitize_text_field( $_POST['name'] ?? '' );
$password = $_POST['password'] ?? '';
$password2 = $_POST['password2'] ?? '';
$quota = absint( $_POST['quota'] ?? 1024 );
if ( ! $this->verify_ownership( $server_id, $domain ) ) {
wp_send_json_error( 'Access denied.', 403 );
}
if ( ! $local_part || ! $password ) {
wp_send_json_error( 'Username and password are required.' );
}
if ( $password !== $password2 ) {
wp_send_json_error( 'Passwords do not match.' );
}
$server = $this->get_server( $server_id );
if ( ! $server ) {
wp_send_json_error( 'Server unavailable.' );
}
$api = WooCow_API::from_server( $server );
$result = $api->create_mailbox( [
'local_part' => $local_part,
'domain' => $domain,
'name' => $name ?: $local_part,
'password' => $password,
'password2' => $password2,
'quota' => $quota,
'active' => 1,
'force_pw_update' => 0,
'tls_enforce_in' => 0,
'tls_enforce_out' => 0,
] );
if ( ! $result['success'] ) {
wp_send_json_error( $result['error'] ?? 'Failed to create mailbox.' );
}
wp_send_json_success( [ 'email' => $local_part . '@' . $domain ] );
}
public function ajax_woocow_acct_mailbox_password(): void {
$this->account_verify();
$server_id = absint( $_POST['server_id'] ?? 0 );
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
$email = sanitize_email( $_POST['email'] ?? '' );
$password = $_POST['password'] ?? '';
$password2 = $_POST['password2'] ?? '';
if ( ! $this->verify_ownership( $server_id, $domain ) ) {
wp_send_json_error( 'Access denied.', 403 );
}
if ( ! $password || $password !== $password2 ) {
wp_send_json_error( 'Passwords do not match or are empty.' );
}
// Validate that email belongs to the customer's domain.
if ( ! str_ends_with( $email, '@' . $domain ) ) {
wp_send_json_error( 'Email does not belong to your domain.' );
}
$server = $this->get_server( $server_id );
if ( ! $server ) {
wp_send_json_error( 'Server unavailable.' );
}
$api = WooCow_API::from_server( $server );
$result = $api->edit_mailbox( [ $email ], [
'password' => $password,
'password2' => $password2,
] );
if ( ! $result['success'] ) {
wp_send_json_error( $result['error'] ?? 'Failed to update password.' );
}
wp_send_json_success();
}
public function ajax_woocow_acct_aliases(): void {
$this->account_verify();
$server_id = absint( $_POST['server_id'] ?? 0 );
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
if ( ! $this->verify_ownership( $server_id, $domain ) ) {
wp_send_json_error( 'Access denied.', 403 );
}
$server = $this->get_server( $server_id );
if ( ! $server ) {
wp_send_json_error( 'Server unavailable.' );
}
$api = WooCow_API::from_server( $server );
$result = $api->get_all_aliases();
if ( ! $result['success'] ) {
wp_send_json_error( $result['error'] ?? 'Could not load aliases.' );
}
// Filter to this domain only.
$aliases = array_filter( (array) $result['data'], function ( $a ) use ( $domain ) {
return isset( $a['address'] ) && str_ends_with( $a['address'], '@' . $domain );
} );
wp_send_json_success( array_values( $aliases ) );
}
public function ajax_woocow_acct_alias_create(): void {
$this->account_verify();
$server_id = absint( $_POST['server_id'] ?? 0 );
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
$address = sanitize_email( $_POST['address'] ?? '' );
$goto = sanitize_email( $_POST['goto'] ?? '' );
if ( ! $this->verify_ownership( $server_id, $domain ) ) {
wp_send_json_error( 'Access denied.', 403 );
}
if ( ! $address || ! $goto ) {
wp_send_json_error( 'Alias address and destination are required.' );
}
if ( ! str_ends_with( $address, '@' . $domain ) ) {
wp_send_json_error( 'Alias address must belong to your domain.' );
}
$server = $this->get_server( $server_id );
if ( ! $server ) {
wp_send_json_error( 'Server unavailable.' );
}
$api = WooCow_API::from_server( $server );
$result = $api->create_alias( [
'address' => $address,
'goto' => $goto,
'active' => 1,
] );
if ( ! $result['success'] ) {
wp_send_json_error( $result['error'] ?? 'Failed to create alias.' );
}
wp_send_json_success();
}
public function ajax_woocow_acct_alias_delete(): void {
$this->account_verify();
$server_id = absint( $_POST['server_id'] ?? 0 );
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
$alias_id = absint( $_POST['alias_id'] ?? 0 );
if ( ! $this->verify_ownership( $server_id, $domain ) ) {
wp_send_json_error( 'Access denied.', 403 );
}
$server = $this->get_server( $server_id );
if ( ! $server ) {
wp_send_json_error( 'Server unavailable.' );
}
$api = WooCow_API::from_server( $server );
$result = $api->delete_alias( $alias_id );
if ( ! $result['success'] ) {
wp_send_json_error( $result['error'] ?? 'Failed to delete alias.' );
}
wp_send_json_success();
}
// ── AJAX: Quarantine ──────────────────────────────────────────────────────
public function ajax_woocow_acct_quarantine(): void {
$this->account_verify();
$server_id = absint( $_POST['server_id'] ?? 0 );
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
if ( ! $this->verify_ownership( $server_id, $domain ) ) {
wp_send_json_error( 'Access denied.', 403 );
}
$server = $this->get_server( $server_id );
if ( ! $server ) {
wp_send_json_error( 'Server unavailable.' );
}
$api = WooCow_API::from_server( $server );
$result = $api->get_quarantine();
if ( ! $result['success'] ) {
wp_send_json_error( $result['error'] ?? 'Could not load quarantine.' );
}
// Filter to messages where the recipient belongs to this domain.
$messages = array_values( array_filter( (array) $result['data'], function ( $m ) use ( $domain ) {
return isset( $m['rcpt'] ) && str_ends_with( $m['rcpt'], '@' . $domain );
} ) );
wp_send_json_success( $messages );
}
public function ajax_woocow_acct_quarantine_delete(): void {
$this->account_verify();
$server_id = absint( $_POST['server_id'] ?? 0 );
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
$qid = absint( $_POST['qid'] ?? 0 );
if ( ! $this->verify_ownership( $server_id, $domain ) ) {
wp_send_json_error( 'Access denied.', 403 );
}
$server = $this->get_server( $server_id );
if ( ! $server ) {
wp_send_json_error( 'Server unavailable.' );
}
$api = WooCow_API::from_server( $server );
$result = $api->delete_quarantine( $qid );
if ( ! $result['success'] ) {
wp_send_json_error( $result['error'] ?? 'Failed to delete quarantine message.' );
}
wp_send_json_success();
}
// ── AJAX: Spam score ──────────────────────────────────────────────────────
public function ajax_woocow_acct_spam_score(): void {
$this->account_verify();
$server_id = absint( $_POST['server_id'] ?? 0 );
$domain = sanitize_text_field( $_POST['domain'] ?? '' );
$email = sanitize_email( $_POST['email'] ?? '' );
$spam_score = floatval( $_POST['spam_score'] ?? 5 );
if ( ! $this->verify_ownership( $server_id, $domain ) ) {
wp_send_json_error( 'Access denied.', 403 );
}
if ( ! str_ends_with( $email, '@' . $domain ) ) {
wp_send_json_error( 'Email does not belong to your domain.' );
}
$server = $this->get_server( $server_id );
if ( ! $server ) {
wp_send_json_error( 'Server unavailable.' );
}
$api = WooCow_API::from_server( $server );
$result = $api->request( 'POST', '/api/v1/edit/spam-score/' . rawurlencode( $email ), [
'spam_score' => $spam_score,
] );
if ( ! $result['success'] ) {
wp_send_json_error( $result['error'] ?? 'Failed to update spam score.' );
}
wp_send_json_success();
}
// ── WP password change sync ───────────────────────────────────────────────
/**
* When a customer saves their WooCommerce account details with a new password,
* offer them the option to sync it to all their mailboxes.
*
* NOTE: This hook fires after the WP password has been updated.
* We only sync if the customer explicitly checked the option.
*/
public function maybe_sync_password( int $user_id ): void {
// Only proceed if the "sync to mailcow" checkbox was checked and a new password given.
if ( empty( $_POST['woocow_sync_pw'] ) || empty( $_POST['password_1'] ) ) {
return;
}
$password = $_POST['password_1'];
global $wpdb;
$assignments = $wpdb->get_results( $wpdb->prepare( "
SELECT a.server_id, a.domain, s.url, s.api_key
FROM {$wpdb->prefix}woocow_assignments a
JOIN {$wpdb->prefix}woocow_servers s ON s.id = a.server_id
WHERE a.customer_id = %d AND s.active = 1
", $user_id ) );
foreach ( $assignments as $assignment ) {
$api = new WooCow_API( $assignment->url, $assignment->api_key );
$mboxes = $api->get_domain_mailboxes( $assignment->domain );
if ( ! $mboxes['success'] ) {
continue;
}
foreach ( (array) ( $mboxes['data'] ?? [] ) as $mbox ) {
$api->edit_mailbox( [ $mbox['username'] ], [
'password' => $password,
'password2' => $password,
] );
}
}
}
}