HEX
Server: LiteSpeed
System: Linux melbournecleaninggroup 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
User: www-data (33)
PHP: 7.3.33-1+focal
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
Upload Files
File: /var/www/html/wp-content/plugins/dologin/src/sms.cls.php
<?php
/**
 * SMS class
 *
 * @since 1.3
 */
namespace dologin;
defined( 'WPINC' ) || exit;

class SMS extends Instance {
	private $_dry_run = false;

	/**
	 * Return current usre's phone number
	 *
	 * @since 1.3
	 */
	public function current_user_phone() {
		$uid = get_current_user_id();
		$phone = get_user_meta( $uid, 'phone_number', true );
		return $phone;
	}

	/**
	 * Check if is dry run (dry run = before sending sms) or not
	 *
	 * @since  1.6
	 */
	public static function is_dry_run() {
		return self::cls()->_dry_run;
	}

	/**
	 * Verify SMS after u+p authenticated
	 *
	 * @since  1.3
	 */
	public function authenticate( $user, $username, $password ) {
		global $wpdb;

		defined( 'debug' ) && debug( 'auth' );

		if ( $this->_dry_run ) {
			defined( 'debug' ) && debug( 'bypassed due to dryrun' );
			return $user;
		}

		if ( empty( $username ) || empty( $password ) ) {
			defined( 'debug' ) && debug( 'bypassed due to lack of u/p' );
			return $user;
		}

		if ( is_wp_error( $user ) ) {
			defined( 'debug' ) && debug( 'bypassed due to is_wp_error already' );
			return $user;
		}

		// If sms is optional and the user doesn't have phone set, bypass
		$phone = get_user_meta( $user->ID, 'phone_number', true );
		if ( ! $phone ) {
			defined( 'debug' ) && debug( 'no phone number set' );
			if ( ! Conf::val( 'sms_force' ) ) {
				defined( 'debug' ) && debug( 'bypassed due to no force_sms check' );
				return $user;
			}
		}

		$error = new \WP_Error();

		// Validate dynamic code
		if ( empty( $_POST[ 'dologin-two_factor_code' ] ) ) {
			$error->add( 'sms_missing', Lang::msg( 'sms_missing' ) );
			define( 'DOLOGIN_ERR', true );
			defined( 'debug' ) && debug( '❌ sms missing' );
			return $error;
		}

		$tb_sms = $this->cls( 'Data' )->tb( 'sms' );

		$q = "SELECT id, code FROM $tb_sms WHERE user_id = %d AND used = 0";
		$row = $wpdb->get_row( $wpdb->prepare( $q, array( $user->ID ) ) );

		if ( $row->id ) {
			$wpdb->query( $wpdb->prepare( "UPDATE $tb_sms SET used = 1 WHERE id = %d", array( $row->id ) ) );
		}

		if ( ! $row->code || $row->code != $_POST[ 'dologin-two_factor_code' ] ) {
			$error->add( 'sms_wrong', Lang::msg( 'sms_wrong' ) );
			define( 'DOLOGIN_ERR', true );
			defined( 'debug' ) && debug( '❌ sms wrong' );
			return $error;
		}

		defined( 'debug' ) && debug( '✅ auth successfully' );

		return $user;
	}

	/**
	 * Send test SMS
	 *
	 * @since  1.3
	 */
	public function test_send() {
		global $wpdb;
		if ( empty( $_POST[ 'phone' ] ) ) {
			return REST::err( Lang::msg( 'not_phone_set_curr' ) );
		}

		// Check interval
		if ( time() - get_option( 'dologin_test' ) < 60 ) {
			return REST::err( Lang::msg( 'try_after', 60 ) );
		}

		update_option( 'dologin_test', time() );

		$phone = $_POST[ 'phone' ];

		// Send
		try {
			$res = $this->_api( $phone, array( 'type' => 'test' ) );
		} catch ( \Exception $ex ) {
			return REST::err( $ex->getMessage() );
		}

		return REST::ok( array( 'info' => 'Sent to ***' . substr( $phone, -4 ) . ' at ' . date( 'm/d/Y H:i:s', time() + get_option( 'gmt_offset' ) * 60 * 60 ) ) );
	}

	/**
	 * Send SMS
	 *
	 * @since  1.3
	 */
	public function send() {
		global $wpdb;

		if ( ! Conf::val( 'sms' ) ) {
			return REST::ok( array( 'bypassed' => 1 ) );
		}

		$field_u = 'log';
		$field_p = 'pwd';
		if ( isset( $_POST[ 'woocommerce-login-nonce' ] ) ) {
			$field_u = 'username';
			$field_p = 'password';
		}

		if ( empty( $_POST[ $field_u ] ) || empty( $_POST[ $field_p ] ) ) {
			return REST::err( Lang::msg( 'empty_u_p' ) );
		}

		// Verify u & p first
		$this->_dry_run = true;
		$user = wp_authenticate( $_POST[ $field_u ], $_POST[ $field_p ] );
		$this->_dry_run = false;
		if ( is_wp_error( $user ) ) {
			return REST::err( $user->get_error_message() );
		}

		// Search if the user has number set in phone
		$phone = get_user_meta( $user->ID, 'phone_number', true );

		if ( ! $phone ) {
			if ( ! Conf::val( 'sms_force' ) ) {
				defined( 'debug' ) && debug( 'bypassed due to no phone set' );
				return REST::ok( array( 'bypassed' => 1 ) );
			}
			return REST::err( Lang::msg( 'not_phone_set_user' ) );
		}

		// Generate dynamic code
		$code = s::rrand( 4, 1 );
		$rid = s::rrand( 2, 1 );
		$ip_info = ip::geo();
		$info = sprintf( __( 'Dynamic Code:%1$s.(Tag:%2$s) From: ', 'dologin' ), $code, $rid ) . $ip_info[ 'country' ] . '-' . $ip_info[ 'city' ] . '.';
		$data = array(
			'type'	=> 'login',
			'lang'	=> get_locale(),
			'code'	=> $code,
			'tag'	=> $rid,
		);

		$tb_sms = $this->cls( 'Data' )->tb( 'sms' );

		// Expire old ones
		$wpdb->query( $wpdb->prepare( "UPDATE $tb_sms SET used = -1 WHERE user_id = %d AND used = 0", array( $user->ID ) ) );

		// Save to db
		$s = array(
			'user_id'	=> $user->ID,
			'sms'		=> $info,
			'code'		=> $code,
			'used'		=> 0,
			'dateline'	=> time(),
		);
		$q = 'INSERT INTO ' . $tb_sms . ' ( ' . implode( ',', array_keys( $s ) ) . ' ) VALUES ( ' . implode( ',', array_fill( 0, count( $s ), '%s' ) ) . ' )' ;
		$wpdb->query( $wpdb->prepare( $q, $s ) );
		$id = $wpdb->insert_id;

		// Send
		try {
			$res = $this->_api( $phone, $data );
		} catch ( \Exception $ex ) {
			return REST::err( $ex->getMessage() );
		}

		// Update log
		$wpdb->query( $wpdb->prepare( "UPDATE $tb_sms SET res = %s WHERE id = %d", array( $res, $id ) ) );

		$res_json = json_decode( $res, true );

		// Expected response
		if ( ! empty( $res_json[ '_res' ] ) && $res_json[ '_res' ] == 'ok' ) {
			return REST::ok( array( 'info' => "Tag:$rid. Sent to ***" . substr( $phone, -4 ) . '.' ) );
		}

		if ( ! empty( $res_json[ '_msg' ] ) ) {
			return REST::err( $res_json[ '_msg' ] );
		}

		return REST::err( 'Unknown error' );
	}

	/**
	 * Call API to send msg
	 *
	 * @since  1.5
	 */
	private function _api( $phone, $data ) {
		// Send
		$url = 'https://doapi.us/text?format=json';
		$data = array(
			'app' 	=> 'dologin',
			'domain'=> home_url(),
			'ip'	=> ip::me(),
			'phone' => $phone,
			'data' 	=> json_encode( $data ),
		);

		$res = wp_remote_post( $url, array( 'body' => $data, 'timeout' => 15, 'sslverify' => false ) ); // id=>xx

		if ( is_wp_error( $res ) ) {
			$error_message = $res->get_error_message();
			throw new \Exception( $error_message );
		}

		return $res[ 'body' ];
	}

}