package APIjsonclient; # # SPDX-License-Identifier: AGPL-3.0-or-later # Copyright (c) Rainer Gümpelein, TeilRad GmbH # #Client for shareejson api # use strict; use warnings; use POSIX; use CGI; # only for debugging use JSON; use LWP::UserAgent; use URI::Encode; use Config::General; use Mod::Basework; use Mod::DBtank; use Data::Dumper; sub new { my $class = shift; my $self = {}; bless($self,$class); return $self; } my $q = new CGI; my $netloc = $q->url(-base=>1); my $ua = LWP::UserAgent->new; my $uri_encode = URI::Encode->new( { encode_reserved => 1 } ); $ua->agent("APIclient $netloc"); my $bw = new Basework; my $dbt = new DBtank; my $json = JSON->new->allow_nonref; sub loop_sharees { my $self = shift; my $q = shift || ""; my $auth = shift; my $authraw = shift; my $return_merchant = shift || ""; my $last_used_operator = shift || ""; my @keywords = $q->param; #only request keys which initated by sharee primary requests to operator my $rest_hash = {}; my $rest = ""; foreach (@keywords){ if($_ =~ /request|authcookie|system|bike|station/g){ my $val = $q->param($_); my $encoded_val = $uri_encode->encode($val); $rest .= "$_=$encoded_val&"; $rest_hash->{$_} = "$encoded_val"; } } $rest =~ s/\&$//; my $response_in = {}; my $globalconf_file = "/var/www/copri4/shareeconf/global.cfg"; my $conf = Config::General->new($globalconf_file); my %globalconf = $conf->getall; my @uri_operator_array = (); my @user_group = (); my @user_tour = (); my $fetch_hash = {}; my $o=0; while (my ($key, $op_name) = each %{ $globalconf{operator} }) { my $ret_json = ""; my %ret_json_all = (); my %ret_json_private = (); my %ret_json_public = (); my %withpub = (); #shareetool, to loop only operators who is the shareetool by usersconf enabled my $users_serviceapp = { u_id => 0 }; if($return_merchant->{aowner} && $return_merchant->{aowner} == 187 && $op_name->{database}->{dbname} && $authraw->{txt17} =~ /$op_name->{database}->{dbname}/){ my $dbh_operator = $dbt->dbconnect_extern($op_name->{database}->{dbname}); $users_serviceapp = $dbt->select_users($dbh_operator,$authraw->{c_id},"and int09=1"); $bw->log("shareetool select_users $return_merchant->{aowner} on $op_name->{database}->{dbname} $authraw->{c_id} $authraw->{txt17}",$users_serviceapp->{u_id},""); } #every sharee client must have a merchant_id which associate a project if($op_name->{operatorApp} && ($users_serviceapp->{u_id} || $return_merchant->{merchant_id} eq $globalconf{website}->{'shareeweb-sharee'}->{merchant_id} || ($return_merchant->{project_id} eq $op_name->{project}))){ $o++; #stations_available with optional caching if($rest_hash->{request} eq "stations_available"){ push(@uri_operator_array, $op_name->{operatorApp}); #if disabled, reads DB if($op_name->{cache_station} == 1){ #primary authraw if($authraw->{txt30}){ my %prim_tarif_hash = (); if($authraw->{txt30} =~ /\w\s\w/){ %prim_tarif_hash = map { $_ => 1 } split(/\s+/,$authraw->{txt30}); }elsif($authraw->{txt30} && $authraw->{txt30} =~ /(\w+)/){ $prim_tarif_hash{$1} = 1; } foreach my $optarif (keys(%prim_tarif_hash)){ my $op_key = ""; my $tarif_key = ""; if($optarif =~ /([A-Z]+)(\d+)/){ $op_key = $1; $tarif_key = $2; } if($op_key && $op_key eq $op_name->{oprefix}){ #print $optarif . "---" . $op_name->{oprefix} . "\n"; $ret_json_private{$optarif} = $self->fetch_operator_json("$op_name->{operatorApp}/json/stations-$optarif.json",""); eval { $bw->log("$o $key) Test Decode json-caching, using 1: $op_name->{operatorApp}/json/stations-$optarif.json","",""); my $response_in = ""; $response_in = decode_json($ret_json_private{$optarif}); }; if ($@){ $bw->log("$o $key) Failure Test reading json-caching, using fallback 1 and generate cache just-in-time: $op_name->{operatorApp}/APIjsonserver?$rest&cachme=$tarif_key",$ret_json,""); #$ret_json_private{$optarif} = $self->fetch_operator_json("$op_name->{operatorApp}/APIjsonserver",$rest); $self->fetch_operator_json("$op_name->{operatorApp}/APIjsonserver","$rest&cachme=$tarif_key"); $ret_json_private{$optarif} = $self->fetch_operator_json("$op_name->{operatorApp}/json/stations-$optarif.json",""); } } %ret_json_all = %ret_json_private; } }#primary private Tarifs $withpub{$op_name->{oprefix}} = 0; foreach my $optarif (keys(%ret_json_all)){ my $opx = $1 if($optarif =~ /([A-Z]+)(\d+)/); if($op_name->{oprefix} eq $opx){ eval { $bw->log("$o $key) Test Decode json-caching, using 2: ret_json_private hash","",""); my $response_in = ""; $response_in = decode_json($ret_json_all{$optarif}); foreach my $station (keys (%{ $response_in })) { $withpub{$op_name->{oprefix}} = $response_in->{$station}->{withpub}; } }; if ($@){ $bw->log("$o $key) Failure Test reading json-caching, No fallback for ret_json_private hash","",""); } } } #print "$op_name->{oprefix}: $withpub{$op_name->{oprefix}}\n"; if($withpub{$op_name->{oprefix}} || $op_name->{withpub}){ $ret_json_public{$op_name->{oprefix}} = $self->fetch_operator_json("$op_name->{operatorApp}/json/stations-$op_name->{oprefix}.json",""); eval { $bw->log("$o $key) Test Decode json-caching, using 3: $op_name->{operatorApp}/json/stations-$op_name->{oprefix}.json","",""); my $response_in = ""; $response_in = decode_json($ret_json_public{$op_name->{oprefix}}); }; if ($@){ $bw->log("$o $key) Failure Test reading json-caching, using fallback 3 and generate cache just-in-time: $op_name->{operatorApp}/APIjsonserver?$rest&cachme=public",$ret_json,""); #$ret_json_public{$op_name->{oprefix}} = $self->fetch_operator_json("$op_name->{operatorApp}/APIjsonserver",$rest); $self->fetch_operator_json("$op_name->{operatorApp}/APIjsonserver","$rest&cachme=public"); $ret_json_public{$op_name->{oprefix}} = $self->fetch_operator_json("$op_name->{operatorApp}/json/stations-$op_name->{oprefix}.json",""); } %ret_json_all = ( %ret_json_all, %ret_json_public ); } foreach my $optarif (keys(%ret_json_all)){ if($ret_json_all{$optarif}){ eval { $bw->log("$o $key) Test Decode json-caching, using 3: ret_json_all hash","",""); my $response_in = ""; $response_in = decode_json($ret_json_all{$optarif}); foreach my $result (keys (%{ $response_in })) { $fetch_hash->{$result} = $response_in->{$result}; } }; if ($@){ $bw->log("$o $key) Failure Test reading json-caching, No fallback for ret_json_all hash","",""); } } } }#end cache_station else{ $ret_json = $self->fetch_operator_json("$op_name->{operatorApp}/APIjsonserver",$rest); $bw->log("$o $key) Caching disabled, No reading json-caching, taking: $op_name->{operatorApp}/APIjsonserver?$rest",$ret_json,""); } }#end stations_available #user_bike_occupied by $last_used_operator elsif($last_used_operator && $rest_hash->{request} eq "user_bikes_occupied"){ if($last_used_operator eq $op_name->{database}->{dbname}){ #$bw->log("fetch_operator_json >> user_bikes_occupied by last_used_operator:$last_used_operator ","",""); $ret_json = $self->fetch_operator_json("$op_name->{operatorApp}/APIjsonserver","$rest&withoutkey=1"); } } #all other operator requests else{ $ret_json = $self->fetch_operator_json("$op_name->{operatorApp}/APIjsonserver",$rest); } if($ret_json){ push(@uri_operator_array, $op_name->{operatorApp}); eval { my $response_in = ""; $response_in = decode_json($ret_json); #print Dumper($response_in); #collect OP user_group if($response_in->{shareejson}->{user_group}){ push (@user_group, @{$response_in->{shareejson}->{user_group}}); } #collect OP user_tour if($response_in->{shareejson}->{user_tour}){ push (@user_tour, @{$response_in->{shareejson}->{user_tour}}); } if($q->param('request') && $q->param('request') =~ /stations_all|stations_available/){ foreach my $result (keys (%{ $response_in->{shareejson}->{stations} })) { $fetch_hash->{$result} = $response_in->{shareejson}->{stations}->{$result}; #workaround, delete contributor test stations on global sharee project Freiburg if($return_merchant->{project_id} eq "Freiburg" && $return_merchant->{project_id} ne $op_name->{project} && $fetch_hash->{$result}->{description} =~ /Contributor/i){ #$bw->log("delete contributor-station $result: $fetch_hash->{$result}->{description}","",""); delete $fetch_hash->{$result}; } } } if($q->param('request') && $q->param('request') =~ /bikes_all|bikes_available/){ foreach my $result (keys (%{ $response_in->{shareejson}->{bikes} })) { $fetch_hash->{$result} = $response_in->{shareejson}->{bikes}->{$result}; } } if($q->param('request') && $q->param('request') =~ /user_bikes_occupied/){ foreach my $result (keys (%{ $response_in->{shareejson}->{bikes_occupied} })) { $fetch_hash->{$result} = $response_in->{shareejson}->{bikes_occupied}->{$result}; } } }; if ($@){ $bw->log("$o $key) Failure, eval json from jsonclient",$ret_json,""); warn $@; } }else{ $bw->log("$o $key) NO json from Operator:",$op_name->{operatorApp},""); } #$bw->log("--> LOOP-end jsonclient loop_sharees user_group:\n",\@user_group,""); } } #print "ALL:" . Dumper($fetch_hash); # return ($fetch_hash,\@uri_operator_array,\@user_group,\@user_tour); } sub fetch_operator_json { my $self = shift; my $operator_server = shift || ""; my $rest = shift || ""; my $operator_request = "$operator_server?$rest"; #$bw->log("fetch_operator_json >> $operator_request","$operator_request",""); my $req = HTTP::Request->new(GET => "$operator_request"); $req->content_type('application/x-www-form-urlencoded'); $req->content($rest); my $res = $ua->request($req); if ($res->is_success) { #print $res->content; #return $res->content; my $encoded = Encode::encode('utf-8', Encode::decode('iso-8859-1',$res->content)); return $encoded; }else { return ""; } } 1;