Perl-Mechanize posting hidden form value - perl

I am attempting to create a perl script to test a web form for me. I am using mechanize for the automation and am having trouble finding documentation on the the field method. I am using the field method to return the value of a hidden form and this is causing my post to fail. The problem is probably a simple oversight but I am curious about $mech->field('name'); as it seems to be returning the hidden form's value for me.
Using perl v5.16.3 (w & w/o warnings)
$id = $test->field('MId');
print $id . " \n";
#This is printing the desired Id ,
#the post will not succeed as long as $id assigned this way.
print "This is where I am attempting to upload the images\n";
my $fileuploadresult;
$fileuploadresult = $mech->post($uploadURL,
'Content_Type' => "multipart/form-data",
'Content' => [
'myFile' => $file , 'MId' => $id
]
);
print $fileuploadresult->content() . "\n\n\n"; #If I set $id to something like 'test' it
#will work fine.
#I am using two agent because there are two POST's going on and they have to be sequential.
I was wondering why my submit failed when I grabbed the value of a form. I just changed the 'field' method to 'value' and realized this fixed my problem. Sorry noob question didn't look at the documentation enough. When field returned the value I assumed that was part of its functionality I did not realize it also set the value to null. (Or as far as I can tell that is what its doing)

Related

PERL | Not getting the value after second submit

everyone.
I'm doing an tool with Perl that actually verifies a status in the database and if necessary it gets the properly queries and after confirmation it updates the value.
The page has three possible contents:
If the query that will be used is already filled it updates and returns the status.
If only the serial that the user input is filled it generates the query and then asks for confirmation.
If none of those 2 variables are already filled it goes to the main form where the user can submit the serial.
The problem that I'm facing is that after it generates the $final_query and shows up for confirmation, when I click the Confirm button it reloads the page but it pass directly through the if($final_query) and even the elseif that verifies if the $serial_no is already set.
Does Perl really lose those values if I perform a second submit or am I doing something wrong?
I'd love to have some explanation about it because it's the second time that I'm doing something with that language.
Thanks in advance!
--edit
I chopped the code to show where it defines the $final_query but I kept the structure to help in the understanding.
The full code is available at http://pastebin.com/6NqhbVau
#headers
if ($final_query) {
$content = "<h1>first if</h1>";
#updateESNDatabase($database, $final_query);
#it only enters here if the user type the ESN
}elsif ($serial_no) {
#selects the database
switch(checkUpdateNeeded($database, $serial_no)) {
case 0 {
#Shows that the updates are no needed
}
case 1 {
$final_query = `cat $query1`;
chop($final_query);
$final_query =~ s/SERIALNUM/$serial_no/g;
$final_query =~ s/LOGINID/$login_id/g;
$content = $cgi->start_form .
"<center>" .
"<h3> Please double check the queries below before you update on database </h3>" .
"</center>" .
$cgi->submit("Confirm") . $cgi->end_form;
$content .= $final_query;
}
case 2 {
#Makes almost the same as the first case, it only uses a different file to generate the query.
}
}
} else {
#Generates the first page, where the users inputs information
}
$page->set_content($content);
$page->process;
I figured out how to perform this.
As per my analysis I cannot simply create a variable and pass it through the cgi form.
To workaround this what I've done is create a hidden input in HTML and then send the variable through it.
<input type='hidden' name='the_query' value=\"$final_query\">

Can't call method "id" on an undefined value

recently my hosting company updated their servers, and i believe my code's inefficiency is being brought to light.
i have this code:
#!/usr/bin/perl
use CGI qw(:standard);
use CGI::Session;
$cgi = new CGI;
$sid = $cgi->cookie("CGISESSID") || undef;
$session = new CGI::Session(undef, $sid, {Directory=>'/tmp'});
$cookie = $cgi->cookie(CGISESSID => $session->id);
$sSid = $session->id();
print $cgi->header(-cookie=>$cookie);
print << "EOF";
HTML CODE
EOF
this was fine until (it seems) the update - now i'm getting this error in my logs:
Can't call method "id" on an undefined value at /home/users/web/XXXX/my-file.cgi line 10.
i then edited this into line 10:
$cookie = $cgi->cookie(CGISESSID => $session->id) || undef;
figuring that it would take care of the issue, but it didn't.
it seems to be causing a brief 500 server error, and then the site comes back online, with the session id persisting through the error.
i'm completely lost as to what is happening. what's worse is it isn't happening every time the page loads - it seems so random but i'm sure i'm just not encountering the specific situation to bring that error.
i've been using the same setup for all my cgi's for the past 7 years. not sure why it's causing a brief error now, but that's besides the point.
$session is undef, so that means new returned undef. The docs have the following to say about that:
Returns new session object, or undef on failure. Error message is accessible through errstr() - class method.
So call CGI::Session->errstr to find out what the problem is.

HOP::Lexer with overlapping tokens

I'm using HOP::Lexer to scan BlitzMax module source code to fetch some data from it. One particular piece of data I'm currently interested in is a module description.
Currently I'm searching for a description in the format of ModuleInfo "Description: foobar" or ModuleInfo "Desc: foobar". This works fine. But sadly, most modules I scan have their description defined elsewhere, inside a comment block. Which is actually the common way to do it in BlitzMax, as the documentation generator expects it.
This is how all modules have their description defined in the main source file.
Rem
bbdoc: my module description
End Rem
Module namespace.modulename
This also isn't really a problem. But the line after the End Rem also contains data I want (the module name). This is a problem, since now 2 definitions of tokens overlap each other and after the first one has been detected it will continue from where it left off (position of content that's being scanned). Meaning that the token for the module name won't detect anything.
Yes, I've made sure my order of tokens is correct. It just doesn't seem possible (somewhat understandable) to move the cursor back a line.
A small piece of code for fetching the description from within a Rem-End Rem block which is above a module definition (not worked out, but working for the current test case):
[ 'MODULEDESCRIPTION',
qr/[ \t]*\bRem\n(?:\n|.)*?\s*\bEnd[ \t]*Rem\nModule[\s\t]+/i,
sub {
my ($label, $value) = #_;
$value =~ /bbdoc: (.+)/;
[$label, $1];
}
],
So in my test case I first scan for a single comment, then the block above (MODULEDESCRIPTION), then a block comment (Rem-End Rem), module name, etc.
Currently the only solution I can think of is setup a second lexer only for the module description, though I wouldn't prefer that. Is what I want even possible at all with HOP::Lexer?
Source of my Lexer can be found at https://github.com/maximos/maximus-web/blob/develop/lib/Maximus/Class/Lexer.pm
I've solved it by adding (a slightly modified version of) the MODULEDESCRIPTION. Inside the subroutine I simply filter out the module name and return an arrayref with 4 elements, which I later on iterate over to create a nice usable array with tokens and their values.
Solution is again at https://github.com/maximos/maximus-web/blob/develop/lib/Maximus/Class/Lexer.pm
Edit: Or let me just paste the piece of code here
[ 'MODULEDESCRIPTION',
qr/[ \t]*\bRem\R(?:\R|.)*?\bEnd[ \t]*Rem\R\bModule[\s\t]\w+\.\w+/i,
sub {
my ($label, $value) = #_;
my ($desc) = ($value =~ /\bbbdoc: (.+)/i);
my ($name) = ($value =~ /\bModule (\w+\.\w+)/i);
[$label, $desc, 'MODULENAME', $name];
}
],

CGI::Application and SQLite

I've been messing around with CGI::application the past couple of days and decided to create a really basic forum: the first page displays all posts (only first level, no replies or anything) and a form which can be used to create a new post.
The issue I'm running into is that the data that gets entered into the form never gets inserted into the SQLite database.
Here's the sub procedure I'm having trouble with:
sub newpost {
my $self = shift;
if ( $self->param() ){
my $dbh = DBI->connect("dbi:SQLite:dbname=$database_file","","");
my $sth = $dbh->prepare("INSERT INTO posts (author, time, text) VALUES('testuser', '2011-10-23', 'This is a test!')");
$sth->execute();
$self->header_type('redirect');
$self->header_props(-url=> '?rm=viewall');
}
else {
my $tmpl_obj = $self->load_tmpl('newpost.html');
return $tmpl_obj->output();
}
What happens correctly is that when the newpost run mode is first called, the code within the else statement is run (the template with the form is loaded). The action for the form calls this same run mode, but now that parameters are being provided, the code in the if statement is run. I've checked the SQL code itself and it works, so there must be something else I'm over looking.
Also, is it considered best practice to go about implementing the form logic in this way?
Thanks
You're confusing $self->param() with $self->query->param. The 1st is per-request application level parameters (stuff you might set in one method and use again in another method) and the 2nd are the parameters from the GET query string or the POST body of the request. If you're expecting something from the user it will be in $self->query->param.
BTW, the $self->query object is a normal CGI object, so see it's documentation for specifics.

How to deal with nameless forms on websites?

I would like to write a script that lets me use this website
http://proteinmodel.org/AS2TS/LGA/lga.html
(I need to use it a few hundred times, and I don't feel like doing that manually)
I have searched the internet for ways how this could be done using Perl, and I came across WWW::Mechanize, which seemed to be just what I was looking for. But now I have discovered that the form on that website which I want to use has no name - its declaration line simply reads
<FORM METHOD="POST" ACTION="./lga-form.cgi" ENCTYPE=multipart/form-data>
At first I tried simply not setting my WWW::Mechanize object's form_name property, which gave me this error message when I provided a value for the form's email address field:
Argument "my_email#address.com" isn't numeric in numeric gt (>) at /usr/share/perl5/WWW/Mechanize.pm line 1618.
I then tried setting form_name to '' and later ' ', but it was to no avail, I simply got this message:
There is no form named " " at ./automate_LGA.pl line 40
What way is there to deal with forms that have no names? It would be most helpful if someone on here could answer this question - even if the answer points away from using WWW::Mechanize, as I just want to get the job done, (more or less) no matter how.
Thanks a lot in advance!
An easy and more robust way is to use the $mech->form_with_fields() method from WWW::Mechanize to select the form you want based on the fields it contains.
Easier still, use the submit_form method with the with_fields option.
For instance, to locate a form which has fields named 'username' and 'password', complete them and submit the form, it's as easy as:
$mech->submit_form(
with_fields => { username => $username, password => $password }
);
Doing it this way has the advantage that if they shuffle their HTML around, changing the order of the forms in the HTML, or adding a new form before the one you're interested in, your code will continue to work.
I don't know about WWW::Mechanize, but its Python equivalent, mechanize, gives you an array of forms that you can iterate even if you don't know their names.
Example (taken from its homepage):
import mechanize
br = mechanize.Browser()
br.open("http://www.example.com/")
for form in br.forms():
print form
EDIT: searching in the docs of WWW::Mechanize I found the $mech->forms() method, that could be what you need. But since I don't know perl or WWW::Mechanize, I'll leave there my python answer.
Okay, I have found the answer. I can address the nameless form by its number (there's just one form on the webpage, so I guessed it would be number 1, and it worked). Here's part of my code:
my $lga = WWW::Mechanize->new();
my $address = 'my_email#address.com';
my $options = '-3 -o0 -d:4.0';
my $pdb_2 = "${pdb_id}_1 ${pdb_id}_2";
$lga->get('http://proteinmodel.org/AS2TS/LGA/lga.html');
$lga->success or die "LGA GET fail\n";
$lga->form_number(1);
$lga->field('Address', $address);
$lga->field('Options', $options);
$lga->field('PDB_2', $pdb_2);
$lga->submit();
$lga->success or die "LGA POST fail\n";