*/ class ParTCP_Issue_Services { public $fileSystem; public $dataDir; public $lastResponse; public $partcp; // ParTCP object for sending messages public $poolMax = 100; // maximum number of lotcodes in the pool public $poolMin = 50; // minimum number of lotcodes in the pool public function __construct( $fileSystem, $dataDir, &$partcp ){ $this->fileSystem = $fileSystem; $this->dataDir = $dataDir; $this->partcp = $partcp; } public function get_id( $server, $eventId ){ $eventName = basename( $eventId ); return substr( $eventName, 0, 8 ) . '-' . md5( "{$server}:{$eventId}" ); } public function get_dir( $id ){ $subdir = substr( $id, 0, 6 ); return "issue_services/{$subdir}/{$id}"; } public function get_list(){ if ( ! $this->fileSystem->exists('issue_services') ){ return []; } $folderList = $this->fileSystem->get_listing('issue_services'); $services = []; foreach ( $folderList as $dir ){ $list = $this->fileSystem->get_listing( "issue_services/{$dir}" ); foreach ( $list as $serviceId ){ $services[] = $this->get_data( $serviceId ); } } return $services; } public function get_data( $id ){ $dir = $this->get_dir( $id ); $msg = $this->fileSystem->get_recent_contents( $dir, '[0-9]*-{update,definition}*' ); if ( empty( $msg ) ){ return FALSE; } $receipt = yaml_parse( $msg ); if ( empty( $receipt['Issue-Service-Data'] ) ){ return FALSE; } return $receipt['Issue-Service-Data']; } public function purge_data( $data ){ $validKeys = [ 'id' => 0, 'name' => 0, 'voting_server' => 0, 'event_id' => 0, ]; return array_intersect_key( $data, $validKeys ); } public function fetch_event_data( $serviceData ){ if ( empty( $serviceData['id'] ) || empty( $serviceData['voting_server'] ) || empty( $serviceData['event_id'] ) ){ return -1; } $this->partcp->set_remote_id( $serviceData['voting_server'] ); $this->lastResponse = $this->partcp->send_message([ 'Message-Type' => 'event-details-request', 'Event-Id' => $serviceData['event_id'] ]); if ( ! $this->lastResponse ){ return -2; } return $this->lastResponse->get('Event-Data'); } public function initialize_service( $serviceId ){ $dir = $this->get_dir( $serviceId ); if ( ! mkdir( "{$this->dataDir}/{$dir}/lotcodes", 0755, TRUE ) ){ return FALSE; } if ( ! $this->fill_up_lot_codes( $serviceId ) ){ return FALSE; } return TRUE; } public function fill_up_lot_codes( $serviceId ){ $dir = $this->get_dir( $serviceId ); $path = "{$this->dataDir}/{$dir}/lotcodes"; $existing = count( glob( "{$path}/*" ) ); if ( $existing > $this->poolMin ){ return TRUE; } $data = $this->get_data( $serviceId ); $this->partcp->set_remote_id( $data['voting_server'] ); $msg = [ 'Message-Type' => 'multi-registration', 'Event-Id' => $data['event_id'], 'Count' => $this->poolMax - $existing, ]; $this->lastResponse = $this->partcp->send_message( $msg ); if ( ! $this->lastResponse ){ return FALSE; } $codes = $this->lastResponse->get('Lot-Codes'); foreach ( $codes as $code ){ file_put_contents( "{$path}/" . uniqid(), ptcp_local_encrypt( $code ) ); } return TRUE; } public function check_if_terms_are_matched( $sender, $terms, $attr = [] ){ if ( ! $attr ){ list ( $ptcpId, $server ) = explode( '@', $sender ) + ['','']; if ( ! $server ){ return FALSE; } $this->partcp->set_remote_id( $server ); $msg = [ 'Message-Type' => 'participant-details-request', 'Participant-Id' => $ptcpId, 'Attributes' => array_keys( $terms ), 'Encryption' => false, ]; $this->lastResponse = $this->partcp->send_message( $msg ); if ( ! $this->lastResponse ){ return FALSE; } $ptcpData = $this->lastResponse->get('Participant-Data'); $attr = $ptcpData['attributes'] ?? NULL; if ( ! $attr ){ return FALSE; } } foreach ( $terms as $name => $pattern ){ if ( empty( $attr[ $name ] ) || ! preg_match( $pattern, $attr[ $name ] ) ){ return FALSE; } } return TRUE; } public function get_lot_code( $serviceId ){ $dir = $this->get_dir( $serviceId ); $lotcodeDir = "{$this->dataDir}/{$dir}/lotcodes"; if ( ! is_dir( $lotcodeDir ) ){ if ( ! $this->initialize_service( $serviceId ) ){ return FALSE; } } require_once 'lib/locker/locker.class.php'; $locker = new Locker( "{$this->dataDir}/{$dir}" ); if ( ! $locker->get_lock( 'lock', 180 ) ){ return FALSE; } $codes = glob( "{$lotcodeDir}/*" ); if ( ! $codes ){ if ( ! $this->fill_up_lot_codes( $serviceId ) ){ return FALSE; } $codes = glob( "{$lotcodeDir}/*" ); } $code = ptcp_local_decrypt( file_get_contents( $codes[0] ) ); unlink( $codes[0] ); $locker->release_lock('lock'); // TODO: update counter // TODO: if counter < poolMin, set marker to trigger fill-up on next cron execution return $code; } } // end of file models/issue_services.class.php