prefix . 'aieo_push_dispatch'; if ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t ) ) !== $t ) { return; } if ( ! AIEO_Schema_Lock::acquire( self::SCHEMA_OPT ) ) { return; } $has = (bool) $wpdb->get_var( $wpdb->prepare( "SHOW COLUMNS FROM `{$t}` LIKE %s", 'opened_at' ) ); if ( ! $has ) { $wpdb->query( "ALTER TABLE `{$t}` ADD COLUMN `opened_at` datetime DEFAULT NULL" ); } $has_idx = (bool) $wpdb->get_var( "SHOW INDEX FROM `{$t}` WHERE Key_name = 'idx_recipient_email'" ); if ( ! $has_idx ) { $wpdb->query( "ALTER TABLE `{$t}` ADD KEY `idx_recipient_email` (`recipient_email`)" ); } update_option( self::SCHEMA_OPT, '2', false ); } private static function sign( $type, $did, $url = '' ) { return substr( hash_hmac( 'sha256', $type . '|' . (int) $did . '|' . $url, wp_salt( 'auth' ) ), 0, 20 ); } public static function open_url( $did ) { return add_query_arg( array( self::QP => 'o', 'd' => (int) $did, 's' => self::sign( 'o', $did ) ), home_url( '/' ) ); } public static function click_url( $did, $url ) { return add_query_arg( array( self::QP => 'c', 'd' => (int) $did, 'u' => rawurlencode( (string) $url ), 's' => self::sign( 'c', $did, (string) $url ) ), home_url( '/' ) ); } public static function instrument( $html, $did ) { $did = (int) $did; $html = (string) $html; if ( $did <= 0 || '' === trim( $html ) ) { return $html; } $html = preg_replace_callback( '/]*?)href=(["\'])(.*?)\2/i', function ( $m ) use ( $did ) { $url = html_entity_decode( $m[3], ENT_QUOTES ); $low = strtolower( trim( $url ) ); if ( '' === $low || '#' === $url[0] || 0 === strpos( $low, 'mailto:' ) || 0 === strpos( $low, 'tel:' ) || false !== strpos( $url, '{{' ) || false !== strpos( $low, self::QP . '=' ) || 0 !== strpos( $low, 'http' ) ) { return $m[0]; } return ''; if ( false !== stripos( $html, '' ) ) { $html = preg_replace( '/<\/body>/i', $pixel . '', $html, 1 ); } else { $html .= $pixel; } return $html; } public static function maybe_handle() { if ( ! isset( $_GET[ self::QP ] ) ) { return; } $type = sanitize_key( wp_unslash( $_GET[ self::QP ] ) ); $did = isset( $_GET['d'] ) ? absint( $_GET['d'] ) : 0; $sig = isset( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : ''; if ( 'o' === $type ) { if ( $did > 0 && hash_equals( self::sign( 'o', $did ), $sig ) ) { self::record_open( $did ); } self::out_pixel(); } elseif ( 'c' === $type ) { $url = isset( $_GET['u'] ) ? rawurldecode( wp_unslash( $_GET['u'] ) ) : ''; if ( $did > 0 && '' !== $url && hash_equals( self::sign( 'c', $did, $url ), $sig ) ) { self::record_click( $did, $url ); wp_redirect( esc_url_raw( $url ), 302 ); exit; } wp_safe_redirect( home_url( '/' ) ); exit; } } private static function out_pixel() { nocache_headers(); if ( ! headers_sent() ) { header( 'Content-Type: image/gif' ); header( 'Content-Length: 43' ); } echo base64_decode( 'R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' ); exit; } private static function record_open( $did ) { global $wpdb; $t = $wpdb->prefix . 'aieo_push_dispatch'; $wpdb->query( $wpdb->prepare( "UPDATE `{$t}` SET opened_at = COALESCE(opened_at, %s) WHERE dispatch_id = %d AND channel = 'email'", gmdate( 'Y-m-d H:i:s' ), (int) $did ) ); } private static function record_click( $did, $url ) { global $wpdb; $t = $wpdb->prefix . 'aieo_push_dispatch'; $now = gmdate( 'Y-m-d H:i:s' ); $wpdb->query( $wpdb->prepare( "UPDATE `{$t}` SET clicked_at = COALESCE(clicked_at, %s), opened_at = COALESCE(opened_at, %s), state = CASE WHEN state = 'converted' THEN state ELSE 'clicked' END WHERE dispatch_id = %d AND channel = 'email'", $now, $now, (int) $did ) ); } }