#!/usr/bin/env perl use strict; use warnings; use CGI ":standard"; use CGI::Carp qw(warningsToBrowser fatalsToBrowser); use Data::Dumper; use LWP::UserAgent (); use XML::LibXML; use Encode; use URI; use Benchmark; use JSON; use utf8; use feature qw/say/; use Encode::Deep; use DBD::mysql; use URI::Encode qw(uri_encode uri_decode); use URI::Split qw(uri_split uri_join); use Data::Dumper; use HTML::TreeBuilder::XPath; # LWP my $ua = LWP::UserAgent->new(keep_alive => 1); $ua->agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36'); $ua->timeout(10); $ua->env_proxy; # CGI my $cgi = CGI->new; # GLOBAL my %CONFIG = ( 'DEBUG' => 0, 'DEBUG_PRINT_DATA' => 0, 'ONLINE' => 1, 'USE_CORSANYWHERE' => 0, 'OFFLINE_XML' => '../seatmap-client/offline/MapTicketSales.xml', 'OFFLINE_XML_PARKETT' => '../seatmap-client/offline/MapTicketSales_Parkett.xml', 'OFFLINE_XML_RANG' => '../seatmap-client/offline/MapTicketSales_Rang.xml', 'MYSQL' => { 'database' => 'seatmap', 'hostname' => 'localhost', 'user' => 'seatmap', 'pw' => 'EGNSOp6DE1xd7wMN', 'port' => 3306, }, ); my %DATA; my $connection; # WORKFLOW my $t0 = Benchmark->new if $CONFIG{'DEBUG'}; &getParameter(); print Dumper $DATA{'PARAMETER'} if $CONFIG{'DEBUG'}; &checkHost(); if( $DATA{'PARAMETER'}{'request_switch'} eq 'seatmapWorkflow' ) { say 'Switch seatmapWorkflow' if ($CONFIG{'DEBUG'}); &generateVenueUrl(); my $xml; if ($CONFIG{'ONLINE'}) { print "SET AS ONLINE\n" if $CONFIG{'DEBUG'}; $xml = &get($DATA{'PARAMETER'}{'urlVenue'}); &loadXML($xml, \$DATA{'DOM'}); &parseXML(); &getSeatmaps(); } elsif (!$CONFIG{'ONLINE'}) { print "SET AS OFFLINE: USING LOCAL XML\n" if $CONFIG{'DEBUG'}; $xml = $CONFIG{'OFFLINE_XML'}; &loadXML($xml, \$DATA{'DOM'}); &parseXML(); &loadXML($CONFIG{'OFFLINE_XML_PARKETT'}, \$DATA{'DOM'}); parxeXML_section(); &loadXML($CONFIG{'OFFLINE_XML_RANG'}, \$DATA{'DOM'}); parxeXML_section(); } my $config_ref = &getVenueConfig(); &printOutput($config_ref); } elsif( $DATA{'PARAMETER'}{'request_switch'} eq 'checkoutCheckResponse' ) { say 'Switch checkoutCheckResponse' if ($CONFIG{'DEBUG'}); my $decoded_content = &get($DATA{'PARAMETER'}{'url'}); #print Dumper $decoded_content; my $tree = HTML::TreeBuilder::XPath->new_from_content($decoded_content); my $xpath_alert = '//input[@id="no_timeout_error_msg"]/@value'; if ($tree->exists($xpath_alert)) { #my $alert = decode('utf-8', trim($tree->findvalue($xpath_alert))); my %alert; $alert{'no_timeout_error_msg'}{'value'} = trim($tree->findvalue($xpath_alert)); $alert{'no_timeout_error_msg'}{'id'} = 'no_timeout_error_msg'; if (!$CONFIG{'DEBUG'}) { print header( -type => 'application/json', -access_control_allow_origin => '*', -charset => 'utf-8', ); # OUTPUT JSON print encode_json \%alert; } elsif ($CONFIG{'DEBUG'}) { print Dumper \%alert; } } } &benchmarkEnd() if ($CONFIG{'DEBUG'}); # SUBS sub trim { my $string = shift; $string =~ s/^\s+|\s+$//g; return $string; } sub db_getVenueConfig { &Delimiter((caller(0))[3]); my $query = "Select \ venues.AGENCY As VENUE_AGENCY, venues.NAME As VENUE_NAME, halls.NAME As HALL_NAME, halls.CODE As HALL_CODE, halls.`FRONT-INDICATOR_WIDTH` As HALL_FRONT_INDICATOR, halls.`PADDING-LEFT` As HALL_PADDING_LEFT, halls.`PADDING-LEFT-STAGE` As HALL_PADDING_LEFT_STAGE, halls.MAX_TICKETS_PER_USER As HALL_MAX_TICKETS_PER_USER, halls.BUEHNE As HALL_BUEHNE, halls.LEFT_NAMING As HALL_LEFT_NAMING, halls.TONPULT As HALL_TONPULT, halls.CSS As HALL_CSS, halls.DESCRIPTION As HALL_DESC, buyer_types.CODE As BUYER_TYPE_CODE, buyer_types.DESCRIPTION As BUYER_TYPE_DESC, halls.ID As HALL_ID From venues Inner Join halls On halls.VENUE_ID = venues.ID Inner Join buyer_types On buyer_types.VENUE_ID = halls.VENUE_ID Where venues.AGENCY = '$DATA{'PARAMETER'}{'supplier_code'}'"; print Dumper $query if $CONFIG{'DEBUG'}; my $response = &db_query($query, $connection, 'selectall_arrayref'); my $query_config = "SELECT * FROM config"; print Dumper $query_config if $CONFIG{'DEBUG'}; my $response_config = &db_query($query_config, $connection, 'selectall_hashref', 'NAME'); return ($response, $response_config); } sub accumulate_halls_data { &Delimiter((caller(0))[3]); my ($response) = @_; my %halls; for my $i (0 .. $#{$response}) { # print Dumper $$response[$i]; my $hall_code = $$response[$i][3]; $halls{$hall_code}{'VENUE_AGENCY'} = $$response[$i][0]; $halls{$hall_code}{'VENUE_NAME'} = $$response[$i][1]; $halls{$hall_code}{'HALL_NAME'} = $$response[$i][2]; $halls{$hall_code}{'HALL_CODE'} = $$response[$i][3]; $halls{$hall_code}{'HALL_FRONT_INDICATOR'} = $$response[$i][4]; $halls{$hall_code}{'HALL_PADDING_LEFT'} = $$response[$i][5]; $halls{$hall_code}{'HALL_PADDING_LEFT_STAGE'} = $$response[$i][6]; $halls{$hall_code}{'HALL_MAX_TICKETS_PER_USER'} = $$response[$i][7]; $halls{$hall_code}{'HALL_BUEHNE'} = $$response[$i][8]; $halls{$hall_code}{'HALL_LEFT_NAMING'} = $$response[$i][9]; $halls{$hall_code}{'HALL_TONPULT'} = $$response[$i][10]; $halls{$hall_code}{'HALL_CSS'} = $$response[$i][11]; $halls{$hall_code}{'HALL_DESC'} = $$response[$i][12]; $halls{$hall_code}{'BUYER_TYPES'}{$$response[$i][13]} = $$response[$i][14]; $halls{$hall_code}{'HALL_ID'} = $$response[$i][15]; my $hall_id = $$response[0][15]; my $query_color = "SELECT * FROM custom_pricescales_color WHERE HALL_ID = $halls{$hall_code}{'HALL_ID'}"; print Dumper $query_color if $CONFIG{'DEBUG'}; my $response_color = &db_query($query_color, $connection, 'selectall_hashref', 'ID'); if (%$response_color) { $halls{$hall_code}{'CUSTOM_PRICESCALES_COLOR'} = $response_color; } }; return \%halls; } sub convert_to_config_format { &Delimiter((caller(0))[3]); my ($halls_ref, $response_config) = @_; my %seatmap_config; for my $hall_code (keys %{$halls_ref}) { my $hall_name = $$halls_ref{$hall_code}{'HALL_NAME'}; $seatmap_config{$hall_code} = # HALL CONFIG { "FRONT-INDICATOR-WIDTH" => $$halls_ref{$hall_code}{'HALL_FRONT_INDICATOR'}, "PADDING-LEFT" => $$halls_ref{$hall_code}{'HALL_PADDING_LEFT'}, "PADDING-LEFT-STAGE" => $$halls_ref{$hall_code}{'HALL_PADDING_LEFT_STAGE'}, "MAX_TICKETS_PER_USER" => $$halls_ref{$hall_code}{'HALL_MAX_TICKETS_PER_USER'}, "BUEHNE" => $$halls_ref{$hall_code}{'HALL_BUEHNE'}, "LEFT_NAMING" => $$halls_ref{$hall_code}{'HALL_LEFT_NAMING'}, "CSS" => $$halls_ref{$hall_code}{'HALL_CSS'}, }; $seatmap_config{$hall_name} = # HALL BUYER TYPES { "AGENCY" => $$halls_ref{$hall_code}{'VENUE_AGENCY'}, "BUYER_TYPES" => $$halls_ref{$hall_code}{'BUYER_TYPES'}, }; if ($$halls_ref{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}) { for my $id (keys %{$$halls_ref{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}}) { my $code = $$halls_ref{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}{$id}{'CODE'}; my $color = $$halls_ref{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}{$id}{'COLOR'}; $seatmap_config{$hall_code}{'CUSTOM_PRICESCALES_COLOR'}{$code} = $color; }; } } for my $key (keys %$response_config) { if ($key eq 'CORS_ANYWHERE') { $seatmap_config{'CORS-ANYWHERE'}{'ROOT_URL'} = $$response_config{$key}{'VALUE'}; } else { $seatmap_config{$key} = $$response_config{$key}{'VALUE'}; } }; return \%seatmap_config; } sub getVenueConfig { &Delimiter((caller(0))[3]); # CONNECT TO DB $connection = connectToMySql(); # GET DB DATA FOR VENUE / HALLS / BUYER TYPES my ($response, $response_config) = db_getVenueConfig(); # CREATE ONE DATASET AND ACCUMULATE ALL BUYER TYPES FOR HALL my $halls_ref = &accumulate_halls_data($response); # print Dumper $halls_ref; # CONVERT DATA TO STEAMAP CONFIG FORMAT my $seatmap_config_ref = convert_to_config_format($halls_ref, $response_config); # DISCONNECT FROM DB $connection->disconnect(); return $seatmap_config_ref; } sub db_query { &Delimiter((caller(0))[3]); my ($query, $connection, $switch, @rest) = @_; my $response; my $statement; if ($switch eq 'fetchrow') { $statement = $connection->prepare($query); $statement->execute(); $response = $statement->fetchrow(); } elsif ($switch eq 'fetchall_hashref') { $statement = $connection->prepare($query); $statement->execute(); $response = $statement->fetchall_hashref($rest[0]); } elsif ($switch eq 'fetchall_arrayref') { $statement = $connection->prepare($query); $statement->execute(); $response = $statement->fetchall_arrayref(); } elsif ($switch eq 'do') { $response = $connection->do($query) or die $connection->errstr; } elsif ($switch eq 'selectall_hashref') { $response = $connection->selectall_hashref($query, $rest[0]) or die $connection->errstr; } elsif ($switch eq 'selectall_array') { my @response = $connection->selectall_array($query) or die $connection->errstr; return \@response; } elsif ($switch eq 'selectall_arrayref') { $response = $connection->selectall_arrayref($query) or die $connection->errstr; } elsif ($switch eq 'selectrow_hashref') { $response = $connection->selectrow_hashref($query) or die $connection->errstr; } $statement->finish if $statement; return $response; } sub connectToMySql { &Delimiter((caller(0))[3]); my $connectionInfo = "dbi:mysql:$CONFIG{'MYSQL'}{'database'};$CONFIG{'MYSQL'}{'hostname'}"; # make connection to database my $connection = DBI->connect($connectionInfo, $CONFIG{'MYSQL'}{'user'}, $CONFIG{'MYSQL'}{'pw'}, { RaiseError => 0, # SUGGESTED BY AnyEvent::DBI::MySQL AutoCommit => 1, mysql_auto_reconnect => 1, }); return $connection; } sub wipeDataOverhead { &Delimiter((caller(0))[3]); delete $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'}; for my $pricescaleID (keys(%{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}})) { delete $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'SEATS'}; } } sub printOutput { my $config_ref = shift; if (!$CONFIG{'DEBUG'}) { &wipeDataOverhead(); # print header('application/json; charset=utf-8;'), encode_json $DATA{'COMPILED'}; print header( -type => 'application/json', -access_control_allow_origin => '*', -charset => 'utf-8', ); # FIX ENCODING $DATA{'COMPILED'} = Encode::Deep::decode('utf-8', Encode::Deep::encode('cp850', $DATA{'COMPILED'})); # my $encoded = Encode::Deep::encode('cp850', $DATA{'COMPILED'}); # my $decoded = Encode::Deep::decode('utf-8', $encoded); # ADD SEATMAP CONFIG $DATA{'COMPILED'}{'CONFIG'} = $config_ref; # OUTPUT JSON print encode_json $DATA{'COMPILED'}; } elsif ($CONFIG{'DEBUG'} && $CONFIG{'DEBUG_PRINT_DATA'}) { &Delimiter((caller(0))[3]); print "\n\n" . encode_json $DATA{'COMPILED'}; # print "\n\n" . Dumper(\%DATA); } } sub checkHost() { &Delimiter((caller(0))[3]); return if !$CONFIG{'ONLINE'}; my $URI = URI->new($DATA{'PARAMETER'}{'url'}, 'http'); if (($URI->host() eq 'purchase.tickets.com') && $URI->scheme && $DATA{'PARAMETER'}{'url'}) { print "VALID HOST\n" if $CONFIG{'DEBUG'}; } else { die "INVALID OR MISSING HOST.\n"; } } sub benchmarkEnd() { &Delimiter((caller(0))[3]); my $t1 = Benchmark->new; my $td = timediff($t1, $t0); print "BENCHMARK: ",timestr($td),"\n" if $CONFIG{'DEBUG'}; } sub generateVenueUrl() { &Delimiter((caller(0))[3]); return if !$CONFIG{'ONLINE'}; $DATA{'PARAMETER'}{'urlVenue'} = $DATA{'PARAMETER'}{'url'} . "&supplier_code=$DATA{'PARAMETER'}{'supplier_code'}&bots_event_code=$DATA{'PARAMETER'}{'bots_event_code'}&event_sub_code=$DATA{'PARAMETER'}{'event_sub_code'}" . "&holdcode=0&event=$DATA{'PARAMETER'}{'event'}&nocache=0&inclpkg=Y&incloffer=Y&inclcartdetails=Y&inclCart=Y&inclvenue=Y"; } sub getSeatmaps() { &Delimiter((caller(0))[3]); for my $seatmapID (keys(%{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}})) { my $url = $DATA{'PARAMETER'}{'url'} . "&supplier_code=$DATA{'PARAMETER'}{'supplier_code'}&bots_event_code=$DATA{'PARAMETER'}{'bots_event_code'}&event_sub_code=$DATA{'PARAMETER'}{'event_sub_code'}" . "&event=$DATA{'PARAMETER'}{'event'}&nocache=1&inclsection=Y&incloffer=Y&offer=0&inclseatmap=Y&inclCart=Y&holdcode=0&seatmap=$seatmapID"; print "GET SEATMAP $seatmapID...\n" if $CONFIG{'DEBUG'}; &loadXML(get($url), \$DATA{'DOM'}); parxeXML_section(); } } sub parxeXML_section { &Delimiter((caller(0))[3]); my $id = $DATA{'DOM'}->findvalue('//seatmap/@id'); #print "ID: $id\n"; # CREATE PRICE ARRAYS foreach my $node ($DATA{'DOM'}->findnodes('//pricescale_config/pricescale')) { my $pricescaleID = $node->findvalue('@id'); @{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'SEATS'}} = split /,/, $node->findvalue('@mask'); } # CREATE AVAILABILITY ARRAYS @{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'}{'AVAILABLE'}} = split /,/, $DATA{'DOM'}->findvalue('//availability/@available_selectable_mask'); @{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'}{'UNAVAILABLE'}} = split /,/, $DATA{'DOM'}->findvalue('//availability/@unavailable_unselectable_mask'); # CREATE SEATMAP_CONFIG ROWS my $rowCalculated = 0; foreach my $node ($DATA{'DOM'}->findnodes('//rows/row')) { $rowCalculated++; my @seats = split /\|/, $node->findvalue('@seats'); my $yCoord = $node->findvalue('@y_cell_coord'); #print Dumper(@seats); my $seatNR = 0; foreach my $i (0 .. $#seats) { $seatNR++; my ($id1, $id2, $number, $sectionID, $row, $seatNR_parsed) = split /,/, $seats[$i]; #print "ID1: $id1 ID2: $id2 NUMBER: $number SECTIONID: $sectionID ROW: $rowCalculated SEATNR: $seatNR\n"; $row =~ s/\s+$//; ($DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ID1'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'X_COORD'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'SECTIONID'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ROW'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'SEATNR'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'Y_COORD'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ROW_PARSED'}, $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'SEATNR_PARSED'}) = ($id1, $number, $sectionID, $rowCalculated, $seatNR, $yCoord, $row, $seatNR_parsed); # SECTION DESC $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'SECTIONID_DESC'} = $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$sectionID}{'DESC'}; # CHECK PRICE AND ADD COLOR for my $pricescaleID (keys(%{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}})) { if ( grep( /^$id1$/, @{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'SEATS'}} ) ) { #print "found it for price $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'PRICES'}{'REF_PRICE'}\n"; $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'PRICESCALES_ID'} = $pricescaleID; #%{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'PRICES'}} = %{$DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'PRICES'}}; #$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'COLOR'} = $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$pricescaleID}{'COLOR'}; } } # CHECK AVAILABILITY if ( grep( /^$id1$/, @{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'}{'AVAILABLE'}} ) ) { $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'AVAILABLE'} = 1; $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ENABLED'} = 1; } elsif ( grep( /^$id1$/, @{$DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{'AVAILABILITY'}{'UNAVAILABLE'}} ) ) { $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'AVAILABLE'} = 0; $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ENABLED'} = 1; } else { $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'AVAILABLE'} = 0; $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ROWS'}{$rowCalculated}{$seatNR}{'ENABLED'} = 0; } } } } sub substituteUmlauts { &Delimiter((caller(0))[3]); my $string_ref = shift; $$string_ref =~ s/\x{251c}\x{255d}/ue/ if $$string_ref =~ /\x{251c}\x{255d}/; $$string_ref =~ s/\x{251c}\x{2562}/oe/ if $$string_ref =~ /\x{251c}\x{2562}/; $$string_ref =~ s/\x{251c}\x{f1}/ae/ if $$string_ref =~ /\x{251c}\x{f1}/; $$string_ref =~ s/\x{251c}\x{cd}/a/ if $$string_ref =~ /\x{251c}\x{cd}/; $$string_ref = uc($$string_ref); } sub fix_encoding() { my $string = shift; return decode('utf-8', encode('cp850', $string)); } sub parseXML { &Delimiter((caller(0))[3]); # EVENT $DATA{'COMPILED'}{'EVENT'}{'ID'} = $DATA{'DOM'}->findvalue('//event/@id'); $DATA{'COMPILED'}{'EVENT'}{'CODE'} = $DATA{'DOM'}->findvalue('//event/@code'); $DATA{'COMPILED'}{'EVENT'}{'DESC'} = $DATA{'DOM'}->findvalue('//event/@desc'); # $DATA{'COMPILED'}{'EVENT'}{'DESC'} = &fix_encoding($DATA{'COMPILED'}{'EVENT'}{'DESC'}); # &substituteUmlauts(\$DATA{'COMPILED'}{'EVENT'}{'DESC'}); $DATA{'COMPILED'}{'EVENT'}{'PUBLIC_DESC'} = $DATA{'DOM'}->findvalue('//event/@public_desc'); # &substituteUmlauts(\$DATA{'COMPILED'}{'EVENT'}{'PUBLIC_DESC'}); $DATA{'COMPILED'}{'EVENT'}{'YEAR'} = $DATA{'DOM'}->findvalue('//event/@year'); $DATA{'COMPILED'}{'EVENT'}{'MONTH'} = $DATA{'DOM'}->findvalue('//event/@month'); $DATA{'COMPILED'}{'EVENT'}{'DAY'} = $DATA{'DOM'}->findvalue('//event/@day'); $DATA{'COMPILED'}{'EVENT'}{'HOUR'} = $DATA{'DOM'}->findvalue('//event/@hour'); $DATA{'COMPILED'}{'EVENT'}{'MINUTE'} = $DATA{'DOM'}->findvalue('//event/@minute'); $DATA{'COMPILED'}{'EVENT'}{'DATETIME_DE'} = "$DATA{'COMPILED'}{'EVENT'}{'DAY'}.$DATA{'COMPILED'}{'EVENT'}{'MONTH'}.$DATA{'COMPILED'}{'EVENT'}{'YEAR'} $DATA{'COMPILED'}{'EVENT'}{'HOUR'}:$DATA{'COMPILED'}{'EVENT'}{'MINUTE'}"; # VENUE $DATA{'COMPILED'}{'VENUE'}{'CODE'} = $DATA{'DOM'}->findvalue('//venue/@code'); $DATA{'COMPILED'}{'VENUE'}{'DESC'} = $DATA{'DOM'}->findvalue('//venue/@desc'); $DATA{'COMPILED'}{'VENUE'}{'NAME'} = $DATA{'DOM'}->findvalue('//venue/@name'); $DATA{'COMPILED'}{'VENUE'}{'ID'} = $DATA{'DOM'}->findvalue('//venue/@id'); # VENUE PRICESCALES foreach my $node ($DATA{'DOM'}->findnodes('//pricescales/pricescale')) { my $id = $node->findvalue('@id'); $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'ID'} = $id; $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'CODE'} = $node->findvalue('@code'); $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'COLOR'} = $node->findvalue('@color'); $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'DESC'} = $node->findvalue('@desc'); } # VENUE BUYER TYPES foreach my $node ($DATA{'DOM'}->findnodes('//buyer_types/buyer_type')) { my $id = $node->findvalue('@id'); $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'ID'} = $id; $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'CODE'} = $node->findvalue('@code'); $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'DESC'} = uc($node->findvalue('@desc')); # &substituteUmlauts(\$DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'DESC'}); $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'DISPLAY_INDICATOR'} = uc($node->findvalue('@desc')); # &substituteUmlauts(\$DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id}{'DISPLAY_INDICATOR'}); } # MASTER CONFIG TO VENUE $DATA{'COMPILED'}{'VENUE'}{'CAPACITY'} = $DATA{'DOM'}->findvalue('//master_config/@capacity'); # MASTER SECTION CONFIG foreach my $node ($DATA{'DOM'}->findnodes('//master_config/section_config/section')) { my $id = $node->findvalue('@id'); $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'ID'} = $id; $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'CODE'} = $node->findvalue('@code'); $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'DESC'} = $node->findvalue('@desc'); } # PRICE_STRUCTURE PRICESCALE foreach my $node ($DATA{'DOM'}->findnodes('//price_structure/pricescale')) { my $id = $node->findvalue('@id'); $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'CURRENCY'} = $node->findvalue('@currency'); $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'PRICES'}{'REF_PRICE'} = $node->findvalue('@ref_price'); # BUYER TYPES foreach my $node_buyer ($node->findnodes('./buyer_type')) { my $id2 = $node_buyer->findvalue('@id'); my $desc = $DATA{'COMPILED'}{'VENUE'}{'BUYER_TYPES'}{$id2}{'DESC'}; my $price = $node_buyer->findvalue('@price'); $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id}{'PRICES'}{$desc} = $price; } } # SEATMAP_CONFIG foreach my $node ($DATA{'DOM'}->findnodes('//seatmap_config/seatmap')) { my $id = $node->findvalue('@id'); $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'ID'} = $id; $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'CODE'} = $node->findvalue('@code'); $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'DESC'} = $node->findvalue('@desc'); $DATA{'COMPILED'}{'VENUE'}{'SEATMAP_CONFIG'}{$id}{'HOTSPOT_COORDS'} = $node->findvalue('@hotspot_coords'); } # MASTER SECTION INVENTORY foreach my $node ($DATA{'DOM'}->findnodes('//master_config/section_inventory/section')) { my $id = $node->findvalue('@id'); $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'CAPACITY'} = $node->findvalue('@capacity'); # PRICESCALE my $still_available = 0; foreach my $node_pricescale ($node->findnodes('./pricescale')) { my $id2 = $node_pricescale->findvalue('@id'); my $available = $node_pricescale->findvalue('@available'); $still_available += $available; my $price = $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id2}{'PRICES'}{'REF_PRICE'}; my $code = $DATA{'COMPILED'}{'VENUE'}{'PRICESCALES'}{$id2}{'CODE'}; $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'AVAILABLILITY'}{$code}{'PRICE'} = $price; $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'AVAILABLILITY'}{$code}{'AVAILABLE'} = $available; } $DATA{'COMPILED'}{'VENUE'}{'SECTION_CONFIG'}{$id}{'STILL_AVAILABLE'} = $still_available; } } sub loadXML { &Delimiter((caller(0))[3]); my ($xml, $DATA_ref) = @_; # DELETE IF DOM ALREADY PRESENT if (ref($$DATA_ref) eq 'XML::LibXML::Document') { print 'DELETING EXISTING ' . ref($$DATA_ref) . "\n" if $CONFIG{'DEBUG'}; $$DATA_ref = (); } if (!$CONFIG{'ONLINE'}) { print "LOADING $xml...\n" if $CONFIG{'DEBUG'}; $$DATA_ref = eval { XML::LibXML->load_xml(location => $xml) } or die "DIE: XML LOADING ERROR\n" . $@; } elsif ($CONFIG{'ONLINE'}) { print "LOADING XML...\n" if $CONFIG{'DEBUG'}; $$DATA_ref = eval { XML::LibXML->load_xml(string => $xml) } or die "DIE: XML LOADING ERROR\n" . $@; # $$DATA_ref = eval { decode('utf-8', encode('cp850', XML::LibXML->load_xml(string => $xml) ) ) } or die "DIE: XML LOADING ERROR\n" . $@; } } sub getParameter { &Delimiter((caller(0))[3]); return if !$CONFIG{'ONLINE'}; my $url = $ENV{QUERY_STRING}; my $decoded = uri_decode($url); say "decoded: " . $decoded if ($CONFIG{'DEBUG'}); my $uri = URI->new($decoded); %{$DATA{'PARAMETER'}} = $uri->query_form; #$DATA{'PARAMETER'}{'url'} = url_param('url'); $DATA{'PARAMETER'}{'url'} = $decoded; $DATA{'PARAMETER'}{'url'} =~ s/^url=//; # Remove note, request_switch from decoded uri $DATA{'PARAMETER'}{'url'} =~ s/¬e(=[^&]*)?|^note(=[^&]*)?&?//; $DATA{'PARAMETER'}{'url'} =~ s/&request_switch(=[^&]*)?|^request_switch(=[^&]*)?&?//; } sub get { &Delimiter((caller(0))[3]); my $url = shift; if ($CONFIG{'USE_CORSANYWHERE'}) { say 'Generating url for cors-anywhere...'; my ($scheme, $auth, $path, $query, $frag) = uri_split($url); #say "scheme: $scheme\nauth: $auth\npath: $path\nquery: $query\nfrag: $frag"; $url = 'https://cors.zinomedia.de/purchase.tickets.com:443' . $path . '?' . $query; say "cors-anywhere URL: $url"; } if ($CONFIG{'ONLINE'}) { print "GET:\n$url\n" if $CONFIG{'DEBUG'}; #my $response = $ua->get($url); my $response = $ua->get($url, 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36', 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language' => 'de,en-US;q=0.7,en;q=0.3', 'Host' => 'purchase.tickets.com', ); print 'STATUS_LINE: ' . $response->status_line . "\n" if $CONFIG{'DEBUG'}; if ($response->is_success) { return $response->decoded_content; } else { $response->code(); $response->message(); $response->status_line(); say 'Get unsuccessful' if $CONFIG{'DEBUG'}; die; } } elsif (!$CONFIG{'ONLINE'}) { print "SET AS OFFLINE: USING LOCAL XML\n" if $CONFIG{'DEBUG'}; } } sub Delimiter { my $SubName = shift; print "\n" . "\n> SUB " . $SubName . "\n" . '-' x 80 . "\n" if $CONFIG{'DEBUG'}; #print "\n" . "-" x 80 . "\nSUB " . $SubName . "\n" . '-' x 80 . "\n"; } exit;