Perl - Parse URL to get a GET Parameter Value - perl

How to get the value of a parameter code using URI::URL Perl module?
From this link:
http://www.someaddress.com/index.html?test=value&code=INT_12345
It can be done using URI::URL or URI (I know the first one is kind of obsolete). Thanks in advance.

Create a URI object and use the query_form method to get the key/value pairs for the query. If you know that the code parameter is only specified once, you can do it like this:
my $uri = URI->new("http://www.someaddress.com/index.html?test=value&code=INT_12345");
my %query = $uri->query_form;
print $query{code};
Alternatively you can use URI::QueryParam whichs adds soem aditional methods to the URI object:
my $uri = URI->new("http://www.someaddress.com/index.html?test=value&code=INT_12345");
print $uri->query_param("code");

use URI;
my $uri = URI->new("http://someaddr.com/index.html?test=FIRST&test=SECOND&code=INT_12345");
my %query = $uri->query_form;
use Data::Dumper;
print Dumper \%query;
We can see:
$VAR1 = {
'test' => 'SECOND',
'code' => 'INT_12345'
};
Unfortunately, this result is wrong.
There is possible solution:
use URI::Escape;
sub parse_query {
my ( $query, $params ) = #_;
$params ||= {};
foreach $var ( split( /&/, $query ) ){
my ( $k, $v ) = split( /=/, $var );
$k = uri_unescape $k;
$v = uri_unescape $v;
if( exists $params->{$k} ) {
if( 'ARRAY' eq ref $params->{$k} ) {
push #{ $params->{$k} }, $v;
} else {
$params->{$k} = [ $params->{$k}, $v ];
}
} else {
$params->{$k} = $v;
}
}
return $params;
}

Related

SOAP::Lite log transport request/response with custom identifier

I would like to log SOAP::Lite transport request/response contents using a custom identifier (e.g. a transaction-id or txn_id in my example below):
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use SOAP::Lite +trace => [ transport => \&log_transport, ];
sub log_transport {
my ($in) = #_;
if (ref($in) eq "HTTP::Request") {
# INSERT INTO logs ( txn_id, request ) VALUES ( $tnx_id, $in->content )
say STDERR Dumper(ref($in), $in->content);
}
elsif (ref($in) eq "HTTP::Response") {
# UPDATE logs SET response = '$in->content' WHERE txn_id = $tnx_id
say STDERR Dumper(ref($in), $in->content);
}
}
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;
my $soap = SOAP::Lite->proxy('https://www.dataaccess.com/webservicesserver/NumberConversion.wso?op=NumberToWords');
$soap->serializer()->register_ns('http://www.dataaccess.com/webservicesserver/' ,"ns");
sub getWords
{
my ($number, $txn_id) = #_ ;
my $method = SOAP::Data->name("ns:NumberToWords");
my #params = ( SOAP::Data->name("ubiNum" => $number) );
my $response = $soap->call($method => #params);
if (!$response->fault) {
say STDOUT "NumberToWords = " . $response->result;
}
else {
say STDERR "error: " . (defined $response->faultstring? $response->faultstring : $soap->transport->status);
}
}
getWords(444, '123abc');
In my example above, how can I pass the transaction-id 123abc to my logger?
P.S. I do not wish to use:
$soap->outputxml(1)->call($method => #params)
It does not seem like the SOAP::Trace transport callback supports extra argument passing. As a workaround you could use a lexical variable declared in the outer scope like this:
use strict;
use warnings;
use Data::Dumper;
my $TXN_ID;
use SOAP::Lite +trace => [ transport => \&log_transport, ];
sub log_transport {
my ($in) = #_;
say STDERR "Logging transaction id: $TXN_ID:";
if (ref($in) eq "HTTP::Request") {
# INSERT INTO logs ( txn_id, request ) VALUES ( $tnx_id, $in->content )
say STDERR Dumper(ref($in), $in->content);
}
elsif (ref($in) eq "HTTP::Response") {
# UPDATE logs SET response = '$in->content' WHERE txn_id = $tnx_id
say STDERR Dumper(ref($in), $in->content);
}
}
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME}=0;
my $soap = SOAP::Lite->proxy('https://www.dataaccess.com/webservicesserver/NumberConversion.wso?op=NumberToWords');
$soap->serializer()->register_ns('http://www.dataaccess.com/webservicesserver/' ,"ns");
sub getWords
{
my ($number, $txn_id) = #_ ;
$TXN_ID = $txn_id;
my $method = SOAP::Data->name("ns:NumberToWords");
my #params = ( SOAP::Data->name("ubiNum" => $number) );
my $response = $soap->call($method => #params);
if (!$response->fault) {
say STDOUT "NumberToWords = " . $response->result;
}
else {
say STDERR "error: " . (defined $response->faultstring? $response->faultstring : $soap->transport->status);
}
}
getWords(444, '123abc');

Config::Simple, how to change default output header?

Testing the generation on the fly of config files in ini format with the package Config::Simple generates the desired file but always with the same header section, which includes the name of the Perl package at the beginning of the file. Does Config::Simple have a method to modify this default printing? I would like to replace this name with a new one. Is it possible with some feature in the package?
Here is a toy code:
#!/usr/bin/perl
use strict;
use warnings;
use Config::Simple;
my $cfg = new Config::Simple(
syntax => 'ini'
) or die Config::Simple->error();
$cfg->param("Program.mode", "ALL");
$cfg->param("Program.strategies", "1,2,3,4,5,6,7,8,9,10");
$cfg->param("Data.name_specie", "Homo sapiens");
$cfg->write("test.cfg") or die $cfg->error();
The output:
; Config::Simple 4.58
; Mon Mar 16 12:33:55 2020
[Program]
strategies=1,2,3,4,5,6,7,8,9,10
mode=ALL
[Data]
name_specie=Homo sapiens
Just wanted to replace the ; Config::Simple 4.58 line.
Thanks in advance for your suggestions.
"Simple" modules often aren't simple because they make so many decisions for you. If you don't like those decisions, you are stuck. This particular module hasn't been updated in over a decade and has several architectural issues. If you want INI files, use a different module, such as Config:: IniFiles, Config::Tiny, or Config::INI which are maintained and flexible.
My first thought was to subclass and override the method that adds the header since it was hard-baked into one of the methods. This is onerous because the module uses a combination of "private" subroutines and package variables. I tend to try this first because I don't disturb the original package:
use strict;
use warnings;
use v5.12;
use Config::Simple;
package Local::Config::Simple {
use parent qw(Config::Simple);
# generates a writable string
sub as_string {
my $self = shift;
my $syntax = $self->{_SYNTAX} or die "'_SYNTAX' is not defined";
my $sub_syntax = $self->{_SUB_SYNTAX} || '';
my $currtime = localtime;
my $STRING = undef;
if ( $syntax eq 'ini' ) {
while ( my ($block_name, $key_values) = each %{$self->{_DATA}} ) {
unless ( $sub_syntax eq 'simple-ini' ) {
$STRING .= sprintf("[%s]\n", $block_name);
}
while ( my ($key, $value) = each %{$key_values} ) {
my $values = join (Config::Simple::WRITE_DELIM(), map { Config::Simple::quote_values($_) } #$value);
$STRING .= sprintf("%s=%s\n", $key, $values );
}
$STRING .= "\n";
}
} elsif ( $syntax eq 'http' ) {
while ( my ($key, $value) = each %{$self->{_DATA}} ) {
my $values = join (Config::Simple::WRITE_DELIM(), map { Config::Simple::quote_values($_) } #$value);
$STRING .= sprintf("%s: %s\n", $key, $values);
}
} elsif ( $syntax eq 'simple' ) {
while ( my ($key, $value) = each %{$self->{_DATA}} ) {
my $values = join (Config::Simple::WRITE_DELIM(), map { Config::Simple::quote_values($_) } #$value);
$STRING .= sprintf("%s %s\n", $key, $values);
}
}
$STRING .= "\n";
return $STRING;
}
}
my $cfg = Local::Config::Simple->new(
syntax => 'ini'
) or die Config::Simple->error();
$cfg->param("Program.mode", "ALL");
$cfg->param("Program.strategies", "1,2,3,4,5,6,7,8,9,10");
$cfg->param("Data.name_specie", "Homo sapiens");
$cfg->write("file.ini") or die $cfg->error();
That works and gives the output:
[Data]
name_specie=Homo sapiens
[Program]
mode=ALL
strategies=1,2,3,4,5,6,7,8,9,10
However, it broke several OO ideas, so I find this approach unpleasant. I can do a little bit less work by fixing the original package by redefining the original subroutine. Then the package variables and subroutines still work. Load the original module first then add your redefinitions:
use strict;
use warnings;
use v5.12;
use Config::Simple;
no warnings 'redefine';
package Config::Simple {
# generates a writable string
sub as_string {
my $self = shift;
my $syntax = $self->{_SYNTAX} or die "'_SYNTAX' is not defined";
my $sub_syntax = $self->{_SUB_SYNTAX} || '';
my $currtime = localtime;
my $STRING = undef;
if ( $syntax eq 'ini' ) {
while ( my ($block_name, $key_values) = each %{$self->{_DATA}} ) {
unless ( $sub_syntax eq 'simple-ini' ) {
$STRING .= sprintf("[%s]\n", $block_name);
}
while ( my ($key, $value) = each %{$key_values} ) {
my $values = join (WRITE_DELIM, map { quote_values($_) } #$value);
$STRING .= sprintf("%s=%s\n", $key, $values );
}
$STRING .= "\n";
}
} elsif ( $syntax eq 'http' ) {
while ( my ($key, $value) = each %{$self->{_DATA}} ) {
my $values = join (WRITE_DELIM, map { quote_values($_) } #$value);
$STRING .= sprintf("%s: %s\n", $key, $values);
}
} elsif ( $syntax eq 'simple' ) {
while ( my ($key, $value) = each %{$self->{_DATA}} ) {
my $values = join (WRITE_DELIM, map { quote_values($_) } #$value);
$STRING .= sprintf("%s %s\n", $key, $values);
}
}
$STRING .= "\n";
return $STRING;
}
}
my $cfg = Config::Simple->new(
syntax => 'ini'
) or die Config::Simple->error();
$cfg->param("Program.mode", "ALL");
$cfg->param("Program.strategies", "1,2,3,4,5,6,7,8,9,10");
$cfg->param("Data.name_specie", "Homo sapiens");
$cfg->write("file.ini") or die $cfg->error();
I write quite a bit about this in Effective Perl Programming as a way to deal with legacy code.
As a side note, you asked if there was some method in the module. You could have simply looked at the source to see what was happening and what was available. You would have seen that the header was hard-coded into as_string.

Perl OOP method returns array I cannot loop

Here is what my module looks like:
#!/usr/bin/perl
package Page;
use strict;
use warnings;
use base qw/DBObj/;
our %fields = (
id => undef,
title => '$',
content => '$'
);
sub get_field_names {
my #names = sort keys \%fields;
return \#names;
}
for my $field ( keys %fields ) {
my $slot = __PACKAGE__ ."::$field";
no strict "refs";
*$field = sub {
my $self = shift;
$self->{$slot} = shift if #_;
return $self->{$slot};
}
}
1;
Here the parent module where the strange behaviour occurs
#!/usr/bin/perl
package DBObj;
use strict;
use warnings;
use Data::Dumper;
use DBConn;
sub new {
my $me = shift;
my $class = ref $me || $me;
my $self = {
dbh => new DBConn->new(
dns => '/db.sql',
user => '',
pass => '',
)
};
return bless $self, $class;
}
sub save {
my $self = shift;
my #field_names = #{$self->get_field_names};
print Dumper #field_names;
foreach my $item ( reverse #field_names ) {
print $item;
}
}
sub fill {
my ( $self, $args ) = #_;
foreach my $key ( keys $args ) {
$self->$key( $args->{$key} );
}
}
1;
here is what I am experiencing. This snippet
my #field_names = $self->get_field_names;
print Dumper #field_names;
foreach my $item ( reverse #field_names ) {
print $item;
}
Data::Dumper shows
$VAR1 = [
'content',
'id',
'title'
];
But the foreach loop returns
ARRAY(0x7fc750a26470)
I have a Test::Simple test case where I perform the following test
ok( shift $page->get_field_names eq 'content', 'Page has field content');
so I can shift off an item from the array, but I cannot loop through it which is a puzzle to me.
And please; before you tell me that I shouldn't be doing this and that there is a ton of modules out there I should pick instead, I want to point out; I am doing this our of pure fun, I have been away from Perl for ~10 years and thought it would be fun to play around with it again.
You have made get_field_names return a reference to an array, but you are then putting that reference into an array variable.
Try:
my $field_names = $self->get_field_names;
print Dumper $field_names;
foreach my $item ( reverse #$field_names ) {
print $item;
}
get_field_names returns an arrayref, not an array. Either change its return type by removing the backslash from return \#names; or "cast" its return type to an array by writing:
my #field_names = #{$self->get_field_names};

How to print the profile details individual lines

#!/usr/bin/perl -w
use WWW::LinkedIn;
use CGI; # load CGI routines
use CGI::Session;
$q = CGI->new; # create new CGI object
print $q->header, # create the HTTP header
$q->start_html('hello world'), # start the HTML
$q->h1('hello world'), # level 1 header
$q->end_html; # end the HTML
my $consumer_key = 'xxxxxxx';
my $consumer_secret = 'xxxxxxxxx';
my $li = WWW::LinkedIn->new(
consumer_key => $consumer_key,
consumer_secret => $consumer_secret,
);
if ( length( $ENV{'QUERY_STRING'} ) > 0 ) {
$buffer = $ENV{'QUERY_STRING'};
#pairs = split( /&/, $buffer );
foreach $pair (#pairs) {
( $name, $value ) = split( /=/, $pair );
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$in{$name} = $value;
}
$sid = $q->cookie('CGISESSID') || $q->param('CGISESSID') || undef;
$session = new CGI::Session( undef, $sid, { Directory => '/tmp' } );
my $access_token = $li->get_access_token(
verifier => $in{'oauth_verifier'},
request_token => $session->param("request_token"),
request_token_secret => $session->param("request_token_secret"),
);
undef($session);
my $profile_xml = $li->request(
request_url =>
'http://api.linkedin.com/v1/people/~:(id,first-name,last-name,positions,industry,distance)',
access_token => $access_token->{token},
access_token_secret => $access_token->{secret},
);
print $profile_xml;
}
The output is printing in single line. I want to print that is separate line.
OUTPUT
aAVGFD34 jj DD 456456 2003 6 true ara systems Technology and Services Technology and Services 0
How can i get the each column value from the profile_xml variable?
id avsdff
first name jj
lastname dd
Simply use Data::Dumper and XML::Simple.
use Data::Dumper;
use XML::Simple; #you may want to install a specific package from your distribution
{...}
my $hash_ref = SimpeXML::XMLin($profile_xml);
print Dumper($hash_ref);
I do not know if you would like more beautifully output.
try just to make simple print out from your hash reference
foreach $key (keys %{$profile_xml}) {
print "$key $profile_xml->{$key}\n";
}
Here i am going the show the way to parse the data and print in the individual lines.
my $parser = XML::Parser->new( Style => 'Tree' );
my $tree = $parser->parse( $profile_xml );
#print Dumper( $tree ); you can use this see the data displayed in the tree formatted
my $UID = $tree->[1]->[4]->[2],"\n";
print "User ID:$UID";
print"</br>";
my $FirstName = $tree->[1]->[8]->[2],"\n";
print "First Name:$FirstName";
print"</br>";
For sample i have showed for UID and FirstName. And this is working fine.

Perl & Facebook json signed_request parsing

I'm trying to parse the json signed_request you receive from Facebook in perl but need a little help!
($encoded_sig, $payload) = split('\.', $formdata{'signed_request'});
$sig = decode_base64url($encoded_sig);
$data = decode_json(decode_base64url($payload));
%decoded = %{ decode_json(decode_base64url($payload)) };
$expected_sig = hmac_sha256($payload, $app_secret);
if ($expected_sig eq $sig) {
while (my ($key, $value) = each %decoded) {
print "$key = $value\n<br>";
}
}
Each $value can either contain a string or a reference to another HASH (and some of those HASHs may contain another HASH), is there a better way to process this?
The script above returns:
algorithm = HMAC-SHA256
page = HASH(0x632b100)
issued_at = 1323081670
user = HASH(0x632b150)
and the json received after decoding is:
{"algorithm":"HMAC-SHA256","issued_at":1323081670,"page":{"id":"192130540873448","liked":true,"admin":true},"user":{"country":"gb","locale":"en_GB","age":{"min":21}}}
Ideally would like to see a result of:
algorithm = HMAC-SHA256
page-id = 192130540873448
page-liked = true
page-admin = true
issued_at = 1323081670
user-country = gb
user-locale = en_GB
user-age-min = 21
Data structure will increase as upon an authorised user response from Facebook more data is provided.
Many thanks
Initially I posted an URL with a working example. Thanks to Brad for pointing out that it's better to provide a code here, so I removed an URL and included the code below:
#!/usr/bin/perl
use strict;
use CGI;
use MIME::Base64;
use JSON;
use Digest::SHA qw(hmac_sha256);
my $secret = '<secret>';
my $q = new CGI;
my $sreq = $q->param('signed_request');
eval {
die 'not a signed_request' unless (defined $sreq);
my ($esig, $pload) = split('\.', $sreq);
$esig = decode_base64url($esig);
my $vsig = hmac_sha256($pload, $secret);
die 'invalid signature' unless ($esig eq $vsig);
my $data = decode_json(decode_base64url($pload));
print "Content-Type: text/html\n\n";
if ($data->{'page'}->{'liked'} eq 'true') {
print "Oh yeah, you liked me :-)";
} else {
print "No? why not? Please press the Like button";
}
};
if ($#) {
print "Content-Type: text/html\n\ndied: $#";
}
sub decode_base64url {
my $s = shift;
$s =~ tr[-_][+/];
$s .= '=' while length($s) % 4;
return decode_base64($s);
}
sub parse_hash {
my $hash = shift;
my $array = [];
foreach my $key (keys %$hash) {
if (ref($hash->{$key}) eq 'HASH') {
push #$array, $key . ' => {' . join(', ', #{ parse_hash($hash->{$key}) }) . '}';
} else {
push #$array, $key . ' => ' . $hash->{$key};
}
}
return $array;
}
To answer my own question :)
if ($expected_sig eq $sig) {
while (my ($key, $value) = each %decoded) {
if (ref($value) eq "HASH") {
while ( my ($key2, $value2) = each(%{$value}) ) {
$mydata{"$key-$key2"}=$value2;
if (ref($value2) eq "HASH") {
while ( my ($key3, $value3) = each(%{$value2}) ) {
$mydata{"$key-$key2-$key3"}=$value3;
print "$key-$key2-$key3 = $value3\n<br>";
}}
else
{
print "$key-$key2 = $value2\n<br>";
}}}
else
{
$mydata{"$key"}=$value;
print "$key = $value\n<br>";
}}}
outputs:
algorithm = HMAC-SHA256
page-admin = 1
page-liked = 1
page-id = 192130540873448
issued_at = 1323081670
user-country = gb
user-locale = en_GB
user-age-min = 21
Only way is:
$my_algorithm = $algorithm
$page-id = $page->{'id'};
$page-liked = $page->{'linked'};
$page-admin = $page->{'admin'};
$my_issued_at = $issued_at
$user-country = $user->{'country'};
$user-locale = $user->{'locale'};
$user-age-min = $user->{'age'}->{'min'};