* * This source file is subject to the GPL-3.0 license that is bundled * with this source code in the file LICENSE. */ namespace controllers\internals; use DateInterval; use Faker\Factory; /** * Class to call the console scripts. */ class Console extends \descartes\InternalController { /** * Start launcher daemon. */ public function launcher() { new \daemons\Launcher(); } /** * Start sender daemon. */ public function sender() { new \daemons\Sender(); } /** * Start webhook daemon. */ public function webhook() { new \daemons\Webhook(); } /** * Start mailer daemon. */ public function mailer() { new \daemons\Mailer(); } /** * Start a phone daemon. * * @param $id_phone : Phone id */ public function phone($id_phone) { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_phone = new \controllers\internals\Phone($bdd); $phone = $internal_phone->get($id_phone); if (!$phone) { exit(1); } new \daemons\Phone($phone); } /** * Check if a user exists based on email. * * @param string $email : User email */ public function user_exists(string $email) { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_user = new \controllers\internals\User($bdd); $user = $internal_user->get_by_email($email); exit($user ? 0 : 1); } /** * Check if a user exists based on id. * * @param string $id : User id */ public function user_id_exists(string $id) { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_user = new \controllers\internals\User($bdd); $user = $internal_user->get($id); exit($user ? 0 : 1); } /** * Create a user or update an existing user. * * @param $email : User email * @param $password : User password * @param $admin : Is user admin * @param $api_key : User API key, if null random api key is generated * @param $status : User status, default \models\User::STATUS_ACTIVE * @param bool $encrypt_password : Should the password be encrypted, by default true * * exit code 0 on success | 1 on error */ public function create_update_user(string $email, string $password, bool $admin, ?string $api_key = null, string $status = \models\User::STATUS_ACTIVE, bool $encrypt_password = true) { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_user = new \controllers\internals\User($bdd); $user = $internal_user->get_by_email($email); if ($user) { $api_key = $api_key ?? $internal_user->generate_random_api_key(); $update_datas = [ 'email' => $email, 'password' => $encrypt_password ? password_hash($password, PASSWORD_DEFAULT) : $password, 'admin' => $admin, 'api_key' => $api_key, 'status' => $status, ]; $success = $internal_user->update($user['id'], $update_datas); echo json_encode(['id' => $user['id']]); exit($success ? 0 : 1); } $new_user_id = $internal_user->create($email, $password, $admin, $api_key, $status, $encrypt_password); echo json_encode(['id' => $new_user_id]); exit($new_user_id ? 0 : 1); } /** * Update a user status. * * @param string $id : User id * @param string $status : User status, default \models\User::STATUS_ACTIVE */ public function update_user_status(string $id, string $status) { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_user = new \controllers\internals\User($bdd); $user = $internal_user->get($id); if (!$user) { exit(1); } $success = $internal_user->update_status($user['id'], $status); exit($success ? 0 : 1); } /** * Delete a user. * * @param string $id : User id */ public function delete_user(string $id) { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_user = new \controllers\internals\User($bdd); $success = $internal_user->delete($id); exit($success ? 0 : 1); } /** * Delete medias that are no longer usefull. */ public function clean_unused_medias() { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_media = new \controllers\internals\Media($bdd); $medias = $internal_media->gets_unused(); foreach ($medias as $media) { $success = $internal_media->delete_for_user($media['id_user'], $media['id']); echo (false === $success ? '[KO]' : '[OK]') . ' - ' . $media['path'] . "\n"; } } /** * Do alerting for quota limits. */ public function quota_limit_alerting() { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_quota = new \controllers\internals\Quota($bdd); $internal_quota->alerting_for_limit_close_and_reached(); } /** * Do quota renewal. */ public function renew_quotas() { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_quota = new \controllers\internals\Quota($bdd); $internal_quota->renew_quotas(); } /** * Do phone reliability verifications */ public function phone_reliability() { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_user = new \controllers\internals\User($bdd); $internal_settings = new \controllers\internals\Setting($bdd); $internal_sended = new \controllers\internals\Sended($bdd); $internal_phone_reliability = new \controllers\internals\PhoneReliability($bdd); $internal_phone = new \controllers\internals\Phone($bdd); $internal_webhook = new \controllers\internals\Webhook($bdd); $internal_mailer = new \controllers\internals\Mailer(); $users = $internal_user->get_all_active(); foreach ($users as $user) { $settings = $internal_settings->gets_for_user($user['id']); echo "\nCheck phone reliability for user " . $user['id'] . ":\n"; if ($settings['phone_reliability_failed']) { $rate_limit = intval($settings['phone_reliability_failed_rate_limit']) / 100; $min_volume = intval($settings['phone_reliability_failed_volume']); $period = intval($settings['phone_reliability_failed_period']); $grace_period = intval($settings['phone_reliability_failed_grace_period']); echo " Check for failed SMS with rate > " . $rate_limit . " and volume > " . $min_volume . " on period " . $period . "s with grace period of " . $grace_period . "s.\n"; $unreliable_phones = $internal_phone_reliability->find_unreliable_phones($user['id'], \models\Sended::STATUS_FAILED, $rate_limit, $min_volume, $period, $grace_period); foreach ($unreliable_phones as $unreliable_phone) { $phone = $internal_phone->get($unreliable_phone['id_phone']); if (!$phone) { echo ' Cannot find phone: ' . $unreliable_phone['id_phone'] . "\n"; continue; } echo "\n Phone " . $phone['id'] . ' - ' . $phone['name'] . " failed rate = " . $unreliable_phone['rate'] . " > " . $rate_limit . " and volume " . $unreliable_phone['total'] . " > " . $min_volume . "\n"; $internal_phone_reliability->create($user['id'], $phone['id'], \models\Sended::STATUS_FAILED); if ($settings['phone_reliability_failed_email']) { $success = $internal_mailer->enqueue($user['email'], EMAIL_PHONE_RELIABILITY_FAILED, [ 'phone' => $phone, 'period' => $period, 'total' => $unreliable_phone['total'], 'unreliable' => $unreliable_phone['unreliable'], 'rate' => $unreliable_phone['rate'], ]); if (!$success) { echo ' Cannot enqueue alert for unreliable failed phone: ' . $unreliable_phone['id_phone'] . "\n"; continue; } echo " Alert mail for unreliable failed phone " . $phone['id'] . ' - ' . $phone['name'] . " added\n"; } if ($settings['phone_reliability_failed_webhook']) { $webhook = [ 'reliability_type' => \models\Sended::STATUS_FAILED, 'id_phone' => $unreliable_phone['id_phone'], 'period' => $period, 'total' => $unreliable_phone['total'], 'unreliable' => $unreliable_phone['unreliable'], 'rate' => $unreliable_phone['rate'], ]; $internal_webhook->trigger($user['id'], \models\Webhook::TYPE_PHONE_RELIABILITY, $webhook); echo " Webhook for unreliable failed phone " . $phone['id'] . ' - ' . $phone['name'] . " triggered\n"; } if ($settings['phone_reliability_failed_auto_disable']) { $internal_phone->update_status($unreliable_phone['id_phone'], \models\Phone::STATUS_DISABLED); } } } if ($settings['phone_reliability_unknown']) { $rate_limit = intval($settings['phone_reliability_unknown_rate_limit']) / 100; $min_volume = intval($settings['phone_reliability_unknown_volume']); $period = intval($settings['phone_reliability_unknown_period']); $grace_period = intval($settings['phone_reliability_unknown_grace_period']); echo "\n Check for unknown SMS with rate > " . $rate_limit . " and volume > " . $min_volume . " on period " . $period . "s with grace period of " . $grace_period . "s.\n"; $unreliable_phones = $internal_phone_reliability->find_unreliable_phones($user['id'], \models\Sended::STATUS_UNKNOWN, $rate_limit, $min_volume, $period, $grace_period); foreach ($unreliable_phones as $unreliable_phone) { $phone = $internal_phone->get($unreliable_phone['id_phone']); if (!$phone) { echo ' Cannot find phone: ' . $unreliable_phone['id_phone'] . "\n"; continue; } echo "\n Phone " . $phone['id'] . ' - ' . $phone['name'] . " unknown rate = " . $unreliable_phone['rate'] . " > " . $rate_limit . "\n"; $internal_phone_reliability->create($user['id'], $phone['id'], \models\Sended::STATUS_UNKNOWN); if ($settings['phone_reliability_unknown_email']) { $success = $internal_mailer->enqueue($user['email'], EMAIL_PHONE_RELIABILITY_UNKNOWN, [ 'phone' => $phone, 'period' => $period, 'total' => $unreliable_phone['total'], 'unreliable' => $unreliable_phone['unreliable'], 'rate' => $unreliable_phone['rate'], ]); if (!$success) { echo ' Cannot enqueue alert for unreliable unknown phone: ' . $unreliable_phone['id_phone'] . "\n"; continue; } echo " Alert mail for unreliable unknown phone " . $phone['id'] . ' - ' . $phone['name'] . " added\n"; } if ($settings['phone_reliability_unknown_webhook']) { $webhook = [ 'reliability_type' => \models\Sended::STATUS_UNKNOWN, 'id_phone' => $unreliable_phone['id_phone'], 'period' => $period, 'total' => $unreliable_phone['total'], 'unreliable' => $unreliable_phone['unreliable'], 'rate' => $unreliable_phone['rate'], ]; $internal_webhook->trigger($user['id'], \models\Webhook::TYPE_PHONE_RELIABILITY, $webhook); echo " Webhook for unreliable unknown phone " . $phone['id'] . ' - ' . $phone['name'] . " triggered\n"; } if ($settings['phone_reliability_unknown_auto_disable']) { $internal_phone->update_status($unreliable_phone['id_phone'], \models\Phone::STATUS_DISABLED); } } } } } /** * Function to easily populate the database with fake data for testing. * * @param int $id_user : User ID for whom data is to be generated * @param int $received_entries : Number of entries to add to the received table * @param int $sended_entries : Number of entries to add to the sended table * @param int $contact_entries : Number of entries to add to the contact table */ public function seed_database(int $id_user, int $received_entries, int $sended_entries, int $contact_entries) { $this->seed_received($id_user, $received_entries); $this->seed_sended($id_user, $sended_entries); $this->seed_contact($id_user, $contact_entries); } /** * Fill table received with fake data * * @param int $id_user : User to insert received for * @param int $entries : How many received to insert */ public function seed_received(int $id_user, int $entries) { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_received = new \controllers\internals\Received($bdd); $internal_phone = new \controllers\internals\Phone($bdd); $faker = Factory::create(); $phones = $internal_phone->gets_for_user($id_user); for ($i = 0; $i < $entries; $i++) { $id_phone = $faker->randomElement($phones)['id']; $at = $faker->dateTimeBetween('-1 year', 'now')->format('Y-m-d H:i:s'); $text = $faker->sentence(rand(5,10), true); $origin = $faker->e164PhoneNumber; $status = $faker->randomElement(['read', 'unread']); $command = false; $mms = false; $media_ids = []; $internal_received->create($id_user, $id_phone, $at, $text, $origin, $status, $command, $mms, $media_ids); } } /** * Fill table sended with fake data * * @param int $id_user : User to insert sended entries for * @param int $entries : Number of entries to insert */ public function seed_sended(int $id_user, int $entries) { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_sended = new \controllers\internals\Sended($bdd); $internal_phone = new \controllers\internals\Phone($bdd); $faker = Factory::create(); $phones = $internal_phone->gets_for_user($id_user); for ($i = 0; $i < $entries; $i++) { echo $i."\n"; $phone = $faker->randomElement($phones); $id_phone = $phone['id']; $at = $faker->dateTimeBetween('-1 year', 'now')->format('Y-m-d H:i:s'); $text = $faker->sentence(rand(5, 10), true); $destination = $faker->e164PhoneNumber; $uid = $faker->uuid; $adapter = $phone['adapter']; $flash = $faker->boolean; $mms = $faker->boolean; $tag = $faker->optional()->word; $medias = []; // Add logic for media IDs if needed $originating_scheduled = $faker->numberBetween(1, 100); $status = $faker->randomElement([\models\Sended::STATUS_UNKNOWN, \models\Sended::STATUS_DELIVERED, \models\Sended::STATUS_FAILED]); $internal_sended->create($id_user, $id_phone, $at, $text, $destination, $uid, $adapter, $flash, $mms, $tag, $medias, $originating_scheduled, $status); } } /** * Fill table contact with fake data * * @param int $id_user : User to insert contacts for * @param int $entries : Number of contacts to insert */ public function seed_contact(int $id_user, int $entries) { $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD); $internal_contact = new \controllers\internals\Contact($bdd); $faker = Factory::create(); for ($i = 0; $i < $entries; $i++) { $name = $faker->name; $number = $faker->e164PhoneNumber; $data = '[]'; $internal_contact->create($id_user, $number, $name, $data); } } }