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; } ?>
domain ); ?> server_name ); ?> Open Webmail
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, ] ); } } } }