How to force CGI to double-quote cookie value - perl

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 )

Related

Perl SOAP::Lite not setting cookes

I'm trying to use SOAP::Lite to interact with the Zimbra SOAP interface. Apparently, for admin functions, it needs an auth token sent as a cookie. When I use the documented form (apparently inherited from LWP::UserAgent), it's not working:
...
use SOAP::Lite +trace => [ transport => \&log_requests ];
...
my $cookies = HTTP::Cookies->new(ignore_discard => 1, ZM_ADMIN_AUTH_TOKEN => $auth_token);
$soap->transport->cookie_jar($cookies);
my $som = $soap->call('NoOpRequest',
SOAP::Data->name('authToken')->value($auth_token),
);
print Dumper($som->result);
...
sub log_requests {
open LOGFILE,">request.log";
print LOGFILE Dumper(#_);
close LOGFILE;
}
The relevant part of the request log is the headers in the request, which don't contain any cookies (and Zimbra is complaining about no auth token):
'_headers' => bless( {
'user-agent' => 'SOAP::Lite/Perl/1.27',
'soapaction' => '"urn:zimbraAdmin#NoOpRequest"',
'content-type' => 'text/xml; charset=utf-8',
'accept' => [
'text/xml',
'multipart/*',
'application/soap'
],
'content-length' => 827
}, 'HTTP::Headers' ),
ikegami's comment was the right answer (needed to call set_cookie, not pass the cookie as an argument to setting up the cookie jar), however the documentation on set_cookie() is ... incomplete; here are the notes I made after looking at the source to figure out what was needed to get the cookie to actually get set:
#
# $cookie_jar->set_cookie( $version, $key, $val, $path, $domain, $port, $path_spec, $secure, $maxage, $discard, \%rest )
# $version should be 0
# $key, $val, $path must be set
# $key must not start with $
# $path must start with /
# $port should be undef
# $maxage must be > 0
#
$cookies->set_cookie(0, 'ZM_ADMIN_AUTH_TOKEN', $admin_token, '/', 'zmail-admin.peak.org', undef, 1, 86400, 1);

Perl JIRA POST error "headers must be presented as a hashref"

I am writing a Perl script to POST an attachment to JIRA using
REST::Client to access the API
but I am getting an error.
use REST::Client;
use warnings;
use strict;
use File::Slurp;
use MIME::Base64;
my $user = 'user';
my $pass = 'pass';
my $url = "http://******/rest/api/2/issue/BugID/attachments";
my $client = REST::Client->new();
$client->addHeader( 'Authorization', 'Basic' . encode_base64( $user . ':' . $pass ) );
$client->addHeader( 'X-Atlassian-Token', 'no-check' );
$client->setHost( $url );
# my %header = ('Authorization' => 'Basic'. encode_base64($user . ':' . $pass),'X-Atlassian-Token' => 'no-check');
my $attachment = "C:\\Folder\\Test.txt";
$client->POST(
$url,
'Content_Type' => 'form-data',
'Content' => [ 'file' => [$attachment] ]
);
if ( $client->responseCode() eq '200' ) {
print "Updated\n";
}
# print the result
print $client->responseContent() . "\n";
The error I get is
REST::Client exception: headers must be presented as a hashref at C:\Users\a\filename.pl line 24.
As shown in the code, I have tried setting headers in different ways but I still get same error.
Please suggest if there is any other method.
I have tried using JIRA module but it gives error too.
According to the documentation, the POST method:
Takes an optional body content and hashref of custom request headers.
You need to put your headers in a hashref, e.g.:
$client->POST($url, $content, {
foo => 'bar',
baz => 'qux'
});
But...it looks like you're expecting REST::Client to use HTTP::Request::Common to construct a multipart/form-data request. Unfortunately, that's not the case, so you'll have to build the content by hand.
You could use HTTP::Request::Common directly like this:
use strict;
use warnings 'all';
use 5.010;
use HTTP::Request::Common;
use REST::Client;
my $client = REST::Client->new;
my $url = 'http://www.example.com';
my $req = POST($url,
Content_Type => 'form-data',
Content => [ file => [ 'foo.txt' ] ]
);
$client->POST($url, $req->content(), {
$req->headers->flatten()
});
But this is a bit convoluted; I would recommend dropping REST::Client and using LWP::UserAgent instead. REST::Client is just a thin wrapper for LWP::UserAgent with a few convenience features, like prepending a default host to all requests. In this case, it's just getting in the way and I don't think the conveniences are worth the trouble.
From the documentation:
POST ( $url, [$body_content, %$headers] )
And you're doing:
$client->POST(
$url,
'Content_Type' => 'form-data',
'Content' => [ 'file' => [$attachment] ]
);
So - passing a list of scalars, with an arrayref at the end.
Perhaps you want something like:
$client->POST(
$url,
$attachment,
{ 'Content-Type' => 'form-data' }
);
Note the {} to construct an anonymous hash for the headers.
Although you probably want to open and include the 'attachment', because there's nothing in REST::Client about opening files and sending them automagically.

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

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.

WWW::Mechanize gives corrupted uploaded file name

I have some weird problem while uploading a file with a Cyrillic name using WWW::Mechanize. The file is uploaded correctly but the name is broken (I see only ?????? on the target site).
The code is simple:
use WWW::Mechanize;
use Encode qw(from_to);
my $config = {
login => "login",
password => "pass",
source_folder => "$Bin/source_folder",
};
my $mech = WWW::Mechanize->new( autocheck => 1 );
$mech->agent_alias("Windows IE 6");
$mech->get("http://www.antiplagiat.ru/Cabinet/Cabinet.aspx?folderId=689935");
authorize($mech);
$mech->submit_form(
form_number => 1,
fields => {},
button =>
'ctl00$ctl00$Body$MainWorkSpacePlaceHolder$FolderControl_StdFolder_0$DocumentsGrid$btnAddItem',
);
find( \&wanted, $config->{source_folder} );
sub wanted {
return unless -f;
say $config->{source_folder} . "/" . $_;
#from_to($_, "CP1251", "UTF8"); doesn't work too :-(
my $mech = $mech->clone();
$mech->submit_form(
form_number => 1,
fields => {
'ctl00$ctl00$Body$MainWorkSpacePlaceHolder$fuDocumentUpload' =>
$config->{source_folder} . "/" . $_,
},
button => 'ctl00$ctl00$Body$MainWorkSpacePlaceHolder$btnCommitUpload',
);
}
If I encode the file name from CP1251 to UTF8 then the upload doesn't work. Please help me to find a solution.
Here is solution I use:
my $filename = $_;
from_to( $filename, "CP1251", "UTF8" );
my $mech = $mech->clone();
my $form = $mech->form_number(1);
$mech->field( 'ctl00$ctl00$Body$MainWorkSpacePlaceHolder$fuDocumentUpload',
$config->{source_folder} . "/" . $_ );
$form->find_input(
'ctl00$ctl00$Body$MainWorkSpacePlaceHolder$fuDocumentUpload')->filename($filename);
$mech->submit_form(
form_number => 1,
button => 'ctl00$ctl00$Body$MainWorkSpacePlaceHolder$btnCommitUpload',
);

Why does WWW::Mechanize and login-data break when I switch from a query string to a hash?

The following script works fine:
#!/usr/bin/env perl
use strict; use warnings;
use Data::Dumper;
use WWW::Mechanize;
my $loginData = "userName=username&password=password&deeplinkForward=%2Fselfcare%2Frestricted%2FprepareCoCo.do&x=84&y=7";
my $loginUrl = "https://www.login.login/login.do";
my $mech = WWW::Mechanize->new( show_progress => 1 );
my $req = $mech->post( $loginUrl, 'Content' => $loginData );
my $content = $req->content();
print Dumper $content;
But when I replace the line
my $req = $mech->post( $loginUrl, 'Content' => $loginData );
with
my %hash = (
'username' => 'username',
'password' => 'password',
'deeplinkForward' => '%2Fselfcare%2Frestricted%2FprepareCoCo.do',
'x' => '84',
'y' => '7'
);
my $req = $mech->post( $loginUrl, 'Content' => \%hash );
it doesn't work any more ( the script works, but the login doesn't ). Is there something worng?
You have to unescape deeplinkForward:
'deeplinkForward' => '/selfcare/restricted/prepareCoCo.do',
Otherwise, WWW::Mechanize thinks you want to send literal % signs, and helpfully escapes them for you.
To see what's going wrong, try adding this code right before the $mech->post line:
use HTTP::Request::Common 'POST';
print POST( $loginUrl, 'Content' => $loginData )->as_string;
print POST( $loginUrl, 'Content' => \%hash )->as_string;
They should be the same, except for the order of the fields.
It's conceivable that the server requires the fields to be listed in that order (it shouldn't, but...). In that case, you can use an array instead of a hash (hashes don't preserve ordering). Just replace %hash with #fields everywhere it appears.
print POST( $loginUrl, 'Content' => \#fields )->as_string;
i don't have mechanize in place, but you can try this and see how it goes
my $req = $mech->post( $loginUrl, \%hash);