Why am I unable to fetch cookies in my CGI script? - perl

I am setting a cookie in my CGI script and trying to read it, but the read is failing. What could be causing this?
Here is my script:
use strict;
use warnings;
use CGI;
use CGI::Cookie;
use CGI qw/:standard/;
use CGI;
my $cgi = new CGI;
my $cookie1 = $cgi->cookie( -name => 'ID', -value => 123456 );
my $cookie2 = $cgi->cookie( -name => 'NAME', -value => 'ABCDEF' );
print "Cookies:\n" . header( -cookie => [ $cookie1, $cookie2 ] );
my $id = $cgi->cookie('ID') || 'No ID cookie';
my $name = $cgi->cookie('NAME') || 'No Name cookie';
print "ID-Cookie :$id\n";
print "Name-Cookie :$name\n";
my %cookies = CGI::Cookie->fetch;
my $count = 0;
for ( keys %cookies ) {
$count++;
print "Cookie[$count]: $cookies{$_}\n";
}
print "Total cookies: $count\n";
Output:
Cookies:
Set-Cookie: ID=123456; path=/
Set-Cookie: NAME=ABCDEF; path=/
Date: Thu, 09 Oct 2014 19:05:55 GMT
Content-Type: text/html; charset=ISO-8859-1
ID-Cookie :No ID cookie
Name-Cookie :No Name cookie
Total cookies: 0
I've also tried $cookies{'ID'}->value. That didn't work either.

A cookie is a string provided to a web browser which it will provide back to the web server in future requests, but it doesn't look like you used a browser. It looks like you didn't even execute the script as a CGI script!
The script creates an HTTP header (out of pure luck, judging by the Cookies: bit) which instructs the browser to set some cookies, then it displays the cookies it received.
The first time a browser fetches this page, it instructs the browser to set two cookies and that it didn't get any cookies.
The second time a browser fetches this page, it instructs the browser to set two cookies and that it received two cookies from the browser.
Simplified script:
use strict;
use warnings;
use CGI qw/:standard/;
my $cgi = CGI->new;
my $cookie1 = $cgi->cookie( -name => 'ID', -value => 123456 );
my $cookie2 = $cgi->cookie( -name => 'NAME', -value => 'ABCDEF' );
print header( -type => 'text/plain', -cookie => [ $cookie1, $cookie2 ] );
my $id = $cgi->cookie('ID') || 'No ID cookie';
my $name = $cgi->cookie('NAME') || 'No Name cookie';
print "ID-Cookie: $id\n";
print "Name-Cookie: $name\n";

I am not sure you are setting the cookie properly. Try:
use strict;
use warnings;
use CGI qw/:standard/;
use CGI::Cookie;
my $cookie1 = CGI::Cookie->new(-name => 'ID',-value=>123456);
my $cookie2 = CGI::Cookie->new(-name => 'NAME',-value=>'ABCDEF');
Check out the documentation for CGI::Cookie.

Related

How to force CGI to double-quote cookie value

For a simple demonstration, let's use the following Perl code:
use strict;
use warnings;
use CGI qw /:standard/;
use MIME::Base64;
# ...
my $cookie = cookie( -name => 'token',
-value => '"' . encode_base64($token, '') . '"',
-expires => '+1y',
);
print header( -status => 302,
'Location' => $uri,
-cookie => $cookie,
);
This script results in the following HTTP header:
However, I expected
Set-Cookie: token="lrv5Jy5KhkXb8qIWpgd3bA=="; path=/; expires=...
What should I do to get the desired cookie format?
Quotes around the cookie value are optional. The CGI module's authors choose not to include quotes, nor provide an API option to turn them on.
Your attempt is trying to include the quotes are part of the value instead of as characters that delimit it.
If you want to include the optional quotes, then you'll need to reimplement the cookie sub.
Solution #1:
print header(
-status => 302,
'Location' => $uri,
'Set-Cookie' => 'token="' . encode_base64($token, '') . '"; ' .
'path=/; expires=' . (POSIX::strftime
"%a, %d-%b-%Y %T GMT", gmtime (time + 365*24*60*60)) # +1y
);
Solution #2:
my $cookie = cookie( -name => 'token',
-value => '',
-expires => '+1y',
)
=~ s/=;/{ '="' . encode_base64($token, '') . '";' }/er;
print header( -status => 302,
'Location' => $uri,
-cookie => $cookie,
);
Not tested, however you can try
use CGI qw /:standard/;
$CGI::Q = $CGI::DefaultClass->new;
$CGI::Q->{escape} = 0;
# ...
https://metacpan.org/module/CGI::Util/source#L83
push #$result, make_attributes( $leftover, defined $CGI::Q ? $CGI::Q->{escape} : 1 )

Session expires when using WWW::Mechanize on GeoServer

I try to use WWW::Mechanize in order to automate a session with GeoServer.
GeoServer comes with a REST API, which can be used with curl. But at the moment, it is impossible to create a datastore for ImageMosaicJDBC with the REST API, so i would like to add the new raster data source with a Perl script. it is based on WWW::Mechanize.
but it fails, with this message :
your session has expired.
The script is just below...
#!/usr/bin/perl
use strict;
use warnings;
use WWW::Mechanize;
use HTML::TreeBuilder;
use HTML::Tree;
use Getopt::Long;
use HTTP::Cookies;
my %CONF = (
username => 'admin',
password => 'geoserver',
);
GetOptions( \%CONF, "username=s", "password=s" ) or die "Bad options";
my $netloc = "193.55.67.151:8080";
my $url = "http://$netloc/geoserver/web/?wicket:bookmarkablePage=:org.geoserver.web.GeoServerLoginPage";
my $cookie_jar = HTTP::Cookies->new;
my $agent = WWW::Mechanize->new( cookie_jar => $cookie_jar );
$agent->agent('User-Agent=Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:44.0) Gecko/20100101 Firefox/44.0');
# auth
$agent->get($url);
die $agent->res->status_line unless $agent->success;
$agent->set_fields(%CONF);
$agent->submit;
die $agent->res->status_line unless $agent->success;
# adding data store
$url = "http://$netloc/geoserver/web?wicket:bookmarkablePage=:org.geoserver.web.data.store.NewDataPage";
my $content = $agent->get($url);
die $agent->res->status_line unless $agent->success;
my $tree = HTML::Tree->new();
$tree->parse($content);
print $agent->content;
# storeform
$url = "http://$netloc/geoserver/web/?wicket:interface=:5:storeForm::IFormSubmitListener::";
my $content = $agent->post($url);
die $agent->res->status_line unless $agent->success;
my $tree = HTML::Tree->new();
$tree->parse($content);
print $agent->content;
# newdatapage
$url = "http://$netloc/geoserver/web/?wicket:interface=:6::::";
my $ref = "http://$netloc/geoserver/web/?wicket:bookmarkablePage=:org.geoserver.web.data.store.NewDataPage";
my $content = $agent->get( $url, referer => $ref);
die $agent->res->status_line unless $agent->success;
my $tree = HTML::Tree->new();
$tree->parse($content);
print $agent->content;
I cannot see where the problem comes from... In particular, i used WireShark to inspect the HTTP exchanges, but every thing was ok for me. The JSESSIONID cookie was for example correctly rescueing.
Try setting timeout parameter while declaring $agent

How can I send an incorrect Content-Length header for an HTTP request using Perl?

I'm trying to debug a weird warning that is showing up in server logs when a Plack::Request is being parsed. In some cases, a broken UserAgent will send a Content-Length header that looks something like "6375, 6375", which is obviously wrong.
To fix this properly, I need to be able to reproduce the warning. I'd like to include this in a unit test so that I can ensure there are no regressions after the warning is silenced. However, I'm having trouble doing this with Perl. I know this can be done using netcat and socat, but I don't want the unit test to have to rely on other binaries to be installed.
Here is what I've tried:
#!/usr/bin/env perl
use strict;
use warnings;
use JSON::XS qw( encode_json );
use WWW::Mechanize;
my $mech = WWW::Mechanize->new;
$mech->add_handler(
request_prepare => sub {
my ( $req, $ua, $h ) = #_;
$req->headers->header( 'Content-Length' => 9999 );
return;
}
);
my $json = encode_json( { foo => 'bar' } );
$mech->post(
'http://example.com'/url,
'Content-Length' => 999,
Content => $json
);
Output is:
Content-Length header value was wrong, fixed at /opt/perl5.16.3/lib/site_perl/5.16.3/LWP/Protocol/http.pm line 260.
200
That's entirely too helpful for me. :)
If I use HTTP::Request and LWP::UserAgent, it's the same end result.
So, I tried HTTP::Tiny.
#!/usr/bin/env perl
use strict;
use warnings;
use DDP;
use HTTP::Tiny;
use JSON::XS qw( encode_json );
my $http = HTTP::Tiny->new;
my $json = encode_json( { foo => 'bar' } );
my $response = $http->request(
'POST',
'http://example.com'/url',
{ headers => { 'Content-Length' => 999, },
content => $json,
}
);
p $response;
The output is:
{ content => "Content-Length missmatch (got: 13 expected: 999)
",
headers => {
content
-length => 49,
content-type => "text/plain",
},
reason => "Internal Exception",
status => 599,
success => "",
url => "http://example.com'/url",
}
Again, too helpful. At this point, I could use a few suggestions.
Seems like the higher level API's are fixing your error; Here's an example using raw sockets that overcomes this;
#!/usr/bin/env perl
use strict 'vars';
use warnings;
use Socket;
# initialize host and port
my $host = 'www.example.com';
my $port = 80;
# contact the server
open_tcp(F, $host, $port)
or die 'Could not connect to server';
# Send request data
while ( my $request = <DATA> ) {
print F $request;
}
# Get Response
while ( my $response = <F> ) {
print "Response:> $response";
}
close(F);
# TCP Helper
sub open_tcp
{
# get parameters
my ($FS, $dest, $port) = #_;
my $proto = getprotobyname('tcp');
socket($FS, PF_INET, SOCK_STREAM, $proto);
my $sin = sockaddr_in($port,inet_aton($dest));
connect($FS,$sin);
my $old_fh = select($FS);
$| = 1; # don't buffer output
select($old_fh);
}
__DATA__
GET / HTTP/1.1
Host: example.com
Content-Length: 999
-END-

Invalidate selected perl cookies

I have the following perl script.
I need it to invalidate all cookies that are not in the validCookies hash. Note this is only the upper part of my code, the rest deals with printing all the cookies in the #cookieArray() and that works for me.
Since the right cookies are being set that I need to set manually later on in the code. ATM the code is not invalidating the cookies, anyone see why?
use CGI qw(:standard);
use CGI::Cookie;
#cookieArray = ();
#hash of cookie names that should not be set to null
%validCookies = ( cName=> 0, cAddress => 0, cCity => 0, cProvince => 0, cPostalCode => 0, cMail => 0, cDate => 0);
%cook = CGI::Cookie->fetch;
foreach $name ($cook){
if(exists ($validCookies{$name})){
} else {
$temp = CGI::Cookie->new(-name=>$name, -value =>"");
push(#cookieArray, $temp);
}
}
To invalidate a cookie, you must expire it. The following code expires all except the protected cookie names.
It is not necessary to use the CGI::Cookie low-level interface. All the functionality is already exposed through the cookie method.
use strict;
use warnings FATAL => 'all';
use CGI qw();
use Data::Dumper qw(Dumper);
my %protected_names = map { $_ => undef }
qw(cName cAddress cCity cProvince cPostalCode cMail cDate);
my $cgi = CGI->new;
print $cgi->header(
-type => 'text/plain',
-cookie => [
map {
$cgi->cookie(
-name => $_,
-value => (exists($protected_names{$_})
? $cgi->cookie($_)
: q()
),
)
} $cgi->cookie
],
);
print Dumper [$cgi->cookie];

How can I print the cookie_jar values in Perl's WWW::Mechanize?

How can I print the values of the cookie/cookie_jar being set?
Trying:
##my $cookie_jar=HTTP::Cookies->new(file => "cookie.jar",autosave=>1,ignore_discard=>1);
my $cookie_jar=HTTP::Cookies->new(); ## Would like it to be in memory
my $agent = WWW::Mechanize->new(cookie_jar => $cookie_jar);
##my $agent = WWW::Mechanize->new();
##my $agent = WWW::Mechanize->new(autocheck => 1);
##$agent->cookie_jar( {} );
# we need cookies
##$agent->cookie_jar(HTTP::Cookies->new);
print "Set Cookie Jar?\n";
print $agent->cookie_jar->as_string();
print "\n";
$agent->get($url); // url is a https site
Not too much luck with any of these, what am I doing wrong?
Well, you have to have some cookies in the cookie jar to see any cookies in the output. So far you have an empty cookie jar. Either ensure that you add some cookies or that the site you are accessing sets them:
use HTTP::Cookies;
use WWW::Mechanize;
my $cookie_jar = HTTP::Cookies->new;
my $agent = WWW::Mechanize->new( cookie_jar => $cookie_jar );
$cookie_jar->set_cookie(
qw(
3
cat
buster
/
.example.com
0
0
0
)
);
$agent->get( 'http://www.amazon.com' );
print "Set Cookie Jar?\n", $agent->cookie_jar->as_string, "\n";
This gave me the output:
Set Cookie Jar?
Set-Cookie3: session-id=000-0000000-0000000; path="/"; domain=.amazon.com; path_spec; discard; version=0
Set-Cookie3: session-id-time=1272524400l; path="/"; domain=.amazon.com; path_spec; discard; version=0 Set-Cookie3: cat=buster; path="/"; domain=.example.com; port=0; version=3
However, you don't need to invoke HTTP::Cookies directly. LWP will take care of that. You just give cookie_jar a hash reference:
my $agent = WWW::Mechanize->new( cookie_jar => {} );
If you just want the cookies from a particular response, you can create a separate cookie jar to hold the ones you extract from the response:
use WWW::Mechanize;
my $agent = WWW::Mechanize->new( cookie_jar => {} );
my $response = $agent->get( 'http://www.amazon.com' );
my $cookie_jar = HTTP::Cookies->new;
$cookie_jar->extract_cookies( $response );
print $cookie_jar->as_string;
If you're looking for a value of a specific cookie, you will need to scan the whole cookie jar using HTTP::Cookie's $cookie_jar->scan( \&callback ) method. For example, to get JSESSIONID cookie from www.linkedin.com, you can use the following code:
#!/usr/bin/perl
use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Cookies::Netscape;
my $cookies = HTTP::Cookies::Netscape->new(
hide_cookie2 => 1,
file => "$ENV{HOME}/.cookies.txt",
autosave => 1
);
my $browser = LWP::UserAgent->new(
env_proxy => 1,
autocheck => 1,
cookie_jar => $cookies,
agent => "get-jsessionid.pl/1.0"
);
$browser->env_proxy();
my $response = $browser->get( 'http://www.linkedin.com' );
if ($response->is_success)
{
$cookies->scan(sub
{
if ($_[1] eq 'JSESSIONID')
{
print "$_[1] # $_[4] = $_[2]\n";
};
}
);
}
else
{
die $response->status_line;
}
The output would be something like:
JSESSIONID # www.linkedin.com = "ajax:11122233344455556667"
Your main problem seems to be that you are trying to print the cookies before you actually visit the site. Try moving your print statements after your call to get()