mapping hardcoded config files - perl

I have searched for modules to read config files such as Config, Config::Tiny, Config::Simple. I am not too vague about using those, are there any modules for storing/reading dbi config and usernames/passwords? I have attempted to do this myself, I am wanting to have the config file in a hash data structure for easy importing into my module. Is their an easier way to do what I am attempting or a preferred module that could be suggested?
Example config file:
[database]
db=newsdb
host=example.com
user=test
pass=test
[login]
user=john
pass=doe
Coding:
use strict;
use warnings;
use File::Slurp;
use Data::Dumper;
# get database info
my %conf =
map { /^\[database/ ? () : $_ }
grep { /^\w+.*$/ }
map { s/\s?\n?\r?//g; (split /=/)[0,1] } read_file('database.conf');
print Dumper \%conf;
$VAR1 = {
'pass' => 'test',
'db' => 'newsdb',
'user' => 'test',
'host' => 'example.com'
};

The Config module is not used to read configuration files, it gives detailed information on the configuration of your perl instead.
An easy route here would be to use Config::Simple, and then
Config::Simple->import_from("database.conf" => \my %config);
print Dumper \%config;
Output:
$VAR1 = {
'database.host' => 'example.com',
'login.pass' => 'doe',
'login.user' => 'john',
'database.user' => 'test',
'database.db' => 'newsdb',
'database.pass' => 'test'
};
Alternatively, to access just one block, we could do
my $config = Config::Simple->new("database.conf")->get_block("database");
print Dumper $config;
which would give
$VAR1 = {
'pass' => 'test',
'db' => 'newsdb',
'user' => 'test',
'host' => 'example.com'
};
as output. Read the documentation for more information.
It gets even simpler with Config::Tiny:
my $config = Config::Tiny->read("database.conf");
print Dumper $config;
would give
$VAR1 = bless( {
'database' => {
'pass' => 'test',
'db' => 'newsdb',
'user' => 'test',
'host' => 'example.com'
},
'login' => {
'pass' => 'doe',
'user' => 'john'
}
}, 'Config::Tiny' );
so the database portion could be selected with
print Dumper $config->{database}
which would output
$VAR1 = {
'pass' => 'test',
'db' => 'newsdb',
'user' => 'test',
'host' => 'example.com'
};
You can learn more in the documentation.

Related

Perl DBM::Deep - add/delete in an arrayref of hashrefs

I've been working with DBM::Deep and so far, it's been easy to Read and Update the keys in the DB but when it comes to adding or deleting entities it gets a little complicated and I can't see how it could be done.
I've imported an XML file with XML::Hash and then copied on a DBM::Deep object. So the result is somehow complicated ... The objective of course is to be able to recreate the XML file easily.
So this code:
use DBM::Deep;
use List::Util qw(first);
use Data::Dumper;
my $db = DBM::Deep->new('foo.db');
my $devices = $db->{foo}->{devices}->{device};
(my $match) = grep { $_->{hostname} eq 'myfoo' } #$devices;
print Dumper ($match);
print Dumper($devices);
Gives the following output for the first print:
$VAR1 = bless( {
'enable' => '0',
'hostname' => 'myfoo',
'auth' => 'myauth',
'ip' => 'myip',
'protocol' => 'ssh'
}, 'DBM::Deep::Hash' );
The second print shows:
$VAR1 = bless( [
bless( {
'enable' => '0',
'hostname' => 'myfoo',
'auth' => 'myauth',
'ip' => 'myip',
'protocol' => 'ssh'
}, 'DBM::Deep::Hash' ),
bless( {
'ip' => 'myotherip',
'hostname' => 'myotherfoo',
'auth' => 'myauth',
'protocol' => 'telnet'
}, 'DBM::Deep::Hash' ),
and so on.
Can someone please help me to understand how to Create and Delete in this data structure?

Perl : parse a file and grab blocks

impossible to parse the file below and grab the blocks in an hash table or simple tab.
I would like to have an hash table with for example
[serv-test] => parent=PRODUCTION.Windows,host=1.1.1.1
Problem is I can delimit the start of a block (with /\[.*\]/) but impossible to delimit the end. The end of my blocks is the start of another.
My file:
authreq=false
default.secured=false
port=3181
protocol=TCP
seclevel=2
secured=false
[serv-test]
parent=PRODUCTION.Windows
host=1.1.1.1
[citrix]
parent=PRODUCTION.Windows
host=1.1.1.2
[cluster-serv]
parent=PRODUCTION.Unix._INFRA
host=1.1.1.3
port=3182
Instead of worrying about getting a hash, be satisfied with getting the data. If you give the top a section name, you have an INI File:
[Default]
authreq=false
default.secured=false
port=3181
protocol=TCP
seclevel=2
secured=false
[serv-test]
parent=PRODUCTION.Windows
host=1.1.1.1
[citrix]
parent=PRODUCTION.Windows
host=1.1.1.2
[cluster-serv]
parent=PRODUCTION.Unix._INFRA
host=1.1.1.3
port=3182
Now you can use Config::IniFiles:
use v5.10;
use Config::IniFiles;
my $cfg = Config::IniFiles->new(
-file => "test.ini"
) or die "#Config::IniFiles::errors";
say "Port is ", $cfg->val( 'Default', 'port' );
say "Cluster host is ", $cfg->val( 'cluster-serv', 'host' );
If you really want the hash, that's not so hard:
use Config::IniFiles;
use Data::Dumper;
my $cfg = Config::IniFiles->new(
-file => "test.ini"
) or die "#Config::IniFiles::errors";
my %hash;
foreach my $section ( $cfg->Sections ) {
foreach my $parameter ( $cfg->Parameters( $section ) ) {
$hash{$section}{$parameter} = $cfg->val( $section, $parameter );
}
}
say Dumper \%hash;
Now you have:
$VAR1 = {
'citrix' => {
'parent' => 'PRODUCTION.Windows',
'host' => '1.1.1.2'
},
'Default' => {
'secured' => 'false',
'port' => '3181',
'protocol' => 'TCP',
'default.secured' => 'false',
'authreq' => 'false',
'seclevel' => '2'
},
'serv-test' => {
'host' => '1.1.1.1',
'parent' => 'PRODUCTION.Windows'
},
'cluster-serv' => {
'port' => '3182',
'parent' => 'PRODUCTION.Unix._INFRA',
'host' => '1.1.1.3'
}
};
Don't reinvent the wheel. There are plenty of existing modules for working with INI-style files, including Config::Tiny, Config::INI, and Config::IniFiles, just to name a few.

Facebook status update using WWW::Mechanize

I am trying to update status on facebook using Mechanize.I am able to login using the script but unable to update.I verified the id of form for status update is "u_0_w".
But selecting the form_id method says "There is no form with ID "u_0_w"".
My script is this:
use WWW::Mechanize;
use strict;
use warnings;
use Data::Dumper;
use HTTP::Cookies::Netscape;
my $cookiesfilename='/home/xxx/xxx/cookies.txt';
my $out;
my $mech = WWW::Mechanize->new( cookie_jar => HTTP::Cookies::Netscape->new( file => $cookiesfilename ) );
$mech->get("https://www.facebook.com/login.php");
my $response=$mech->submit_form(
fields => {
email => 'xxxx#xxxx.com',
pass => 'xxxxx',
}
);
#my $array=$mech->forms();
#$mech->get('/home.php');
print Dumper($mech->forms());
#$mech->form_id("u_0_w");
$mech->submit_form(
fields => {
xhpc_message_text=>'Why so serious'
}
);
print $response->status_line;
open($out, ">", "output_page.html") or die "Can't open output_page.html: $!";
print $out $response->decoded_content;
Then I tried to print all the forms on the page using Dumper the output is:
$VAR1 = bless( {
'default_charset' => 'UTF-8',
'enctype' => 'application/x-www-form-urlencoded',
'accept_charset' => 'UNKNOWN',
'action' => bless( do{\(my $o = 'https://www.facebook.com/search/web/direct_search.php')}, 'URI::https' ),
'method' => 'GET',
'attr' => {
'method' => 'get'
},
'inputs' => [
bless( {
'tabindex' => '-1',
'value' => '1',
'class' => '_42ft _42fu _4w98',
'type' => 'submit'
}, 'HTML::Form::SubmitInput' ),
bless( {
'/' => '/',
'autocomplete' => 'off',
'tabindex' => '1',
'name' => 'q',
'aria-label' => 'Search Facebook',
'value_name' => '',
'class' => 'inputtext _586f',
'type' => 'text',
'id' => 'u_0_b',
'role' => 'combobox',
'placeholder' => 'Search Facebook'
}, 'HTML::Form::TextInput' )
]
}, 'HTML::Form' );
It means it is not detecting the status update form it is detecting only Facebook search form.
What may be the problem for mechanize not detecting all the form elements?
The form contains <button type="submit">. Do Mechanize support it?
Why do you have to use Mechanize for this? There's already a module available for this on CPAN.
Take a look at WWW::Facebook::API.
Also see a related question: How do I use Perl's WWW::Facebook::API to publish to a user's newsfeed?
Synopsis:
use WWW::Facebook::API;
my $facebook = WWW::Facebook::API->new(
desktop => 0,
api_key => $fb_api_key,
secret => $fb_secret,
session_key => $query->cookie($fb_api_key.'_session_key'),
session_expires => $query->cookie($fb_api_key.'_expires'),
session_uid => $query->cookie($fb_api_key.'_user')
);
my $response = $facebook->stream->publish(
message => qq|Test status message|,
);

POST web authentication of Nest Thermostat site in Perl (attempting to convert from Ruby)

there is a bit of code I'm trying to replicate in Perl using either LWP::UserAgent or WWW::Mechanize from an existing script.
The original script actually does more than I'm looking to do. I'd just like to log into the Nest website (the part I need help with) and then parse out some data for historical logging (I'm good there).
My current script I would expect to work, but I'm not sure if the authResult/access_token from the Ruby example us actually understood/used by either Perl module.
My code in Perl:
#!/usr/bin/perl
use WWW::Mechanize;
#use HTTP::Request::Common qw(POST);
use HTTP::Cookies;
use LWP::UserAgent;
use Data::Dumper;
use CGI;
my $email; #stores our mail
my $password; #stores our password
my $user_agent = 'Nest/1.1.0.10 CFNetwork/548.0.4';
$email = "email#email";
$password = "mypassword";
my #headers = (
'User-Agent' => 'Nest/1.1.0.10 CFNetwork/548.0.4',
'X-nl-user-id' => $email,
'X-nl-protocol-version' => '1',
'Accept-Language' => 'en-us',
'Connection' => 'keep-alive',
'Accept' => '*/*'
);
# print "Content-type: text/html\n\n";
my $cookie = HTTP::Cookies->new(file => 'cookie',autosave => 1,);
my $browser = WWW::Mechanize->new(cookie_jar => $cookie, autocheck => 1,);
# tell it to get the main page
$browser->get("https://home.nest.com/user/login");
print Dumper($browser->forms);
# okay, fill in the box with the name of the
# module we want to look up
$browser->form_number(1);
$browser->field("username", $email);
$browser->field("password", $password);
$browser->submit();
print $browser->content();
When I submit the form, I just get the same page returned back to me, and I don't know what exactly is causing Nest to not like what I'm submitting. There are two additional fields in the form on their log-in page:
'inputs' => [
bless( {
'maxlength' => '75',
'/' => '/',
'value_name' => 'E-mail address',
'name' => 'username',
'id' => 'id_username',
'type' => 'text'
}, 'HTML::Form::TextInput' ),
bless( {
'/' => '/',
'value_name' => 'Password',
'name' => 'password',
'id' => 'id_password',
'type' => 'password',
'minlength' => '6'
}, 'HTML::Form::TextInput' ),
bless( {
'readonly' => 1,
'/' => '/',
'value_name' => '',
'value' => '',
'name' => 'next',
'type' => 'hidden'
}, 'HTML::Form::TextInput' ),
bless( {
'readonly' => 1,
'/' => '/',
'value_name' => '',
'value' => 'dbbadca7910c5290a13d30785ac7fb79',
'name' => 'csrfmiddlewaretoken',
'type' => 'hidden'
}, 'HTML::Form::TextInput' )
Do I need to use the csrfmiddlewaretoken value in each submission? It appears to change. I thought getting a cookie upon a successful login would be enough.
Any suggestions on what I'm doing wrong?
Shot in the blue:
perl -E'use warnings; $email = "email#email"; say "<$email>"'
Possible unintended interpolation of #email in string at -e line 1.
<email>
I suspect it fails because the form gets the wrong user name, print it out to confirm. Always enable the pragmas strict and warnings to make many common mistakes visible.

perl - setting up conditions to find correct key in a hash

Problem:
Seeing exists argument is not a HASH or ARRAY element
Need help setting up several conditions to grab the right key.
Code: (I'm not sure also if my conditions are set up correctly. Need advice troubleshooting)
my $xml = qx(#cmdargs);
my $data = XMLin($xml);
my $size=0;
# checking for error string, if file not found then just exit
# otherwise check the hash keys for filename and get its file size
if (exists $data->{class} =~ /FileNotFound/) {
print "The directory: $Path does not exist\n";
exit;
} elsif (exists $data->{file}->{path}
and $data->{file}->{path} =~/test-out-XXXXX/) {
$size=$data->{file}->{size};
print "FILE SIZE:$size\n";
} else {
# print "Nothing to print.\n";
}
# print "$data";
print Dumper( $data );
My Data:
Data structure for xml file with FileNotFound:
$VAR1 = {
'file' => {},
'path' => '/source/feeds/customer/testA',
'class' => 'java.io.FileNotFoundException',
'message' => '/source/feeds/customer/testA: No such file or directory.'
};
Data structure for xml file found:
$VAR1 = {
'recursive' => 'no',
'version' => '0.20.202.1.1101050227',
'time' => '2011-09-30T02:49:39+0000',
'filter' => '.*',
'file' => {
'owner' => 'test_act',
'replication' => '3',
'blocksize' => '134217728',
'permission' => '-rw-------',
'path' => '/source/feeds/customer/test/test-out-00000',
'modified' => '2011-09-30T02:48:41+0000',
'size' => '135860644',
'group' => '',
'accesstime' => '2011-09-30T02:48:41+0000'
},
The interpreter is probably thinking you meant:
exists($data->{class}=~/FileNotFound/)
Try:
exists $data->{class} and $data->{class}=~/FileNotFound/
instead.