*/ class ParTCP_LC_Service_Messages { static function handle_lc_event_definition( $message, $receipt ){ global $Events, $FileSystem, $Groups, $Shortcoder, $Timestamp; $eventData = $message->get('Event-Data'); if ( empty( $eventData['name'] ) || empty( $eventData['estimated_turnout'] ) ){ $receipt->set_rejection( 21, _('Incomplete event data') ); return $receipt->dump( TRUE ); } if ( empty( $eventData['date'] ) ){ $eventData['date'] = date('Y-m-d'); $date = time(); } else { $date = is_int( $eventData['date'] ) ? $eventData['date'] : strtotime( $eventData['date'] ); if ( ! $date || date( 'Ymd', $date ) < date('Ymd') ){ $receipt->set_rejection( 22, _('Invalid event date') ); return $receipt->dump( TRUE ); } $eventData['date'] = date( 'Y-m-d', $date ); } $estTurnout = (int) $eventData['estimated_turnout']; if ( $estTurnout < 1 || $estTurnout > 1000000 ){ $receipt->set_rejection( 23, _('Invalid value for estimated turnout') ); return $receipt->dump( TRUE ); } $object = NULL; $groupId = $message->get('Group-Id'); if ( $groupId && is_object( $Groups ) ){ if ( ! $Groups->get_data( $groupId ) ){ $receipt->set_rejection( 41, _('Group does not exist') ); return $receipt->dump( TRUE ); } $object = [ 'type' => 'group', 'id' => $groupId, 'dir' => $Groups->get_dir( $groupId ) ]; } $sender = $message->get('From'); if ( ! ptcp_is_authorized( $sender, 'event-definition', $object ) ){ $receipt->set_rejection( 31, _('Sender is not authorized to define event here') ); return $receipt->dump( TRUE ); } $name = substr( $Events->string_to_name( $eventData['name'] ), 0, 32 ); $id = $origId = ( $groupId ? $groupId . '/' : '' ) . date( 'Ymd', $date ) . "-{$name}"; $dir = $Events->get_dir( $id ); while ( $FileSystem->exists( $dir ) ){ $count = $id == $origId ? 2 : (int) substr( $id, strrpos( $id, '-' ) + 1 ) + 1; $id = "{$origId}-{$count}"; $dir = $Events->get_dir( $id ); } $success = $FileSystem->make_dir( $dir ); if ( ! $success ){ $receipt->set_failure( _('Could not create event directory') . ': ' . $FileSystem->lastError ); return $receipt->dump( TRUE ); } $Events->generate_secret( $id ); $data = [ 'id' => $id, 'created_on' => date( 'c', $Timestamp ), 'created_by' => $sender, 'modified_on' => null, 'modified_by' => null, 'naming_rules' => [ 'prefix' => 'p', 'crc_length' => 1, 'group_length' => 3, 'group_separator' => '.' ], 'credential_rules' => [ 'char_list' => '1234567890', 'final_length' => 9, 'crc_length' => 1, 'group_length' => 3, 'group_separator' => '.' ], 'lot_code_rules' => [ 'char_list' => 'ABCDEFGHJKLMNPQRSTUVWXYZ123456789', 'final_length' => 32, 'crc_length' => 0, 'group_length' => 0, 'group_separator' => '.' ], ]; ptcp_update_object( $data, $Events->purge_data( $eventData ) ); $data['naming_rules']['counter_width'] = strlen( $data['estimated_turnout'] ); $data['shortcode'] = $Shortcoder->create( $id, 'events' ); $Events->update_calculated_fields( $data ); $fileName = date( 'Ymd-His', $Timestamp ) . '-event-definition'; $receipt->set( 'Message-Type', 'event-definition-confirmation' ); $receipt->set( 'Event-Data', $data ); if ( $votings = $message->get('Votings') ){ $results = self::process_votings_list( $id, $votings, $sender ); $receipt->set( 'Votings-Results', $results ); } $receiptString = $receipt->dump( TRUE ); $FileSystem->put_contents( "{$dir}/{$fileName}", $receiptString ); return $receiptString; } static function handle_lc_event_update_request( $message, $receipt ){ global $Events, $FileSystem, $Timestamp; $eventId = $message->get('Event-Id'); $eventData = $Events->get_data( $eventId ); if ( ! $eventData ){ $receipt->set_rejection( 41, sprintf( _('Event %s does not exist'), $eventId ) ); return $receipt->dump( TRUE ); } $eventDir = $Events->get_dir( $eventId ); $object = [ 'type' => 'event', 'id' => $eventId, 'dir' => $eventDir ]; $sender = $message->get('From'); if ( ! ptcp_is_authorized( $sender, 'event-update-request', $object ) ){ $receipt->set_rejection( 31, _('Sender is not authorized to update event') ); return $receipt->dump( TRUE ); } $stats = $Events->get_statistics( $eventId ); $newData = $message->get('Event-Data'); if ( ! empty( $stats['lots_created'] ) ){ // some data must not be changed after first lot generation unset( $newData['estimated_turnout'], $newData['naming_rules'], $newData['credential_rules'], $newData['lot_code_rules'] ); } unset( $newData['date'], $newData['shortcode'] ); ptcp_update_object( $eventData, $Events->purge_data( $newData ) ); $Events->update_calculated_fields( $eventData ); $eventData['modified_on'] = date( 'c', $Timestamp ); $eventData['modified_by'] = $sender; $receipt->set( 'Message-Type', 'event-update-confirmation' ); $receipt->set( 'Event-Data', $eventData ); if ( $votings = $message->get('Votings') ){ $results = self::process_votings_list( $eventId, $votings, $sender ); $receipt->set( 'Votings-Results', $results ); } $fileName = date( 'Ymd-His', $Timestamp ) . '-event-update-request'; $receiptString = $receipt->dump( TRUE ); $FileSystem->put_contents( "{$eventDir}/{$fileName}", $receiptString ); return $receiptString; } static function handle_lc_event_list_request( $message, $receipt ){ global $Events, $FileSystem, $Groups, $Votings; if ( is_object( $Groups ) && ( $groupId = $message->get('Group-Id') ) && ( ! $groupData = $Groups->get_data( $groupId ) ) ){ $receipt->set_rejection( 41, sprintf( _('Group %s does not exist'), $groupId ) ); return $receipt->dump( TRUE ); } $receipt->set( 'Message-Type', 'event-list' ); $eventList = $Events->get_list( $groupId ); if ( $message->get('Include-Subgroups') && is_object( $Groups ) ){ $groupList = $Groups->get_id_list_recursive( $groupId ); foreach ( $groupList as $groupId ){ $eventList = array_merge( $eventList, $Events->get_list( $groupId ) ); } } $sender = $message->get('From'); if ( $sender ){ list ( $ptcpId, $server ) = explode( '@', $sender ) + ['','']; } $includeVotingCount = $message->get('Include-Voting-Count'); $includeOpenVotings = $message->get('Include-Open-Votings'); $includeAdminInfo = $message->get('Include-Admin-Info'); $administrableOnly = $message->get('Administrable-Only'); $finalList = []; foreach ( $eventList as $index => $event ){ $eventDir = $Events->get_dir( $event['id'] ); if ( $includeAdminInfo && $sender ){ // TODO: Verify sender identity $event['administrable'] = ptcp_is_authorized( $sender, 'event-update-request', [ 'type' => 'event', 'id' => $event['id'], 'dir' => $eventDir ] ); } if ( $includeVotingCount ){ $Votings->set_base_dir( $eventDir ); $event['voting_count'] = $Votings->count(); } if ( $includeOpenVotings ){ $Votings->set_base_dir( $eventDir ); $event['open_votings'] = $Votings->get_list('open'); } if ( $sender ){ $event['lot_code_deposited'] = $FileSystem->exists( "{$eventDir}/lotcodes/{$ptcpId}" ); } if ( ! $sender || ! $administrableOnly || $event['administrable'] ){ $Events->update_calculated_fields( $event ); $finalList[] = $event; } } $receipt->set( 'Events', $finalList ); return $receipt->dump( TRUE ); } static function handle_lc_event_details_request( $message, $receipt ){ global $Events, $Shortcoder, $Votings; $eventId = $message->get('Event-Id'); if ( $eventId[0] == '#' ){ $eventId = $Shortcoder->resolve( $eventId, 'events' ); } $eventData = $Events->get_data( $eventId ); if ( ! $eventData ){ $receipt->set_rejection( 41, _('Event does not exist') ); return $receipt->dump( TRUE ); } $eventDir = $Events->get_dir( $eventId ); if ( $sender = $message->get('From') ){ $eventData['administrable'] = ptcp_is_authorized( $sender, 'event-update-request', [ 'type' => 'event', 'id' => $eventId, 'dir' => $eventDir ] ); } $Events->update_calculated_fields( $eventData ); $Votings->set_base_dir( $eventDir ); $receipt->set( 'Message-Type', 'event-details' ); $receipt->set( 'Event-Data', $eventData ); $receipt->set( 'Votings', $Votings->get_list( 'all', 'created_on' ) ); $receipt->set( 'Participants', $Events->get_statistics( $eventId ) ); return $receipt->dump( TRUE ); } static function handle_lc_lot_code_request( $message, $receipt ){ global $Events, $FileSystem; $eventId = $message->get('Event-Id'); $eventData = $Events->get_data( $eventId ); if ( ! $eventData ){ $receipt->set_rejection( 41, sprintf( _('Event %s does not exist'), $eventId ) ); return $receipt->dump( TRUE ); } $sender = $message->get('From'); list ( $ptcpId, $server ) = explode( '@', $sender ) + ['','']; $eventDir = $Events->get_dir( $eventId ); $counter = 0; $file = "{$eventDir}/lotcodes/{$ptcpId}"; while ( $FileSystem->exists( $file ) ) { $counter++; $nextFile = "{$eventDir}/lotcodes/{$ptcpId}-{$counter}"; if ( ! $FileSystem->exists( $nextFile ) ){ break; } $file = $nextFile; } if ( ! $counter ){ $receipt->set_rejection( 42, sprintf( _('No lot code deposited for %s'), $ptcpId ) ); return $receipt->dump( TRUE ); } if ( ! ( $msg = yaml_parse( $FileSystem->get_contents( $file ) ) ) || empty( $msg['Original-Message'] ) || ! ( $msg = yaml_parse( $msg['Original-Message'] ) ) || ( empty ( $msg['Lot-Code'] ) && empty( $msg['Lot-Code~'] ) ) ){ $receipt->set_failure( sprintf( _('Could not load lot code for %s'), $sender ) ); return $receipt->dump( TRUE ); } $receipt->set( 'Message-Type', 'lot-code' ); $receipt->set( 'Lot-Code~', $msg['Lot-Code'] ?? $msg['Lot-Code~'] ); $receipt->set( 'Original-Sender', $msg['From'] ); $receipt->set( 'Original-Public-Key', $msg['Public-Key'] ); return $receipt->dump( TRUE ); } } // end of file lc_service.messages.class.php