Perl HTML CGI web page error - html

This is my perl code for a registration web page. It should append values to a csv page. It compiles perfectly but when I conncet it to my HTML program the web pae actually displays an error. This is the page
http://cs.mcgill.ca/~zviran1/register.html
HELP
#!/usr/loca/bin/perl
use CGI;
my $q = CGI->new();
use strict;
my $username = $q->param('username');
my $name = $q->param('name');
my $password = $q->param('password');
my #array = ($name, $username, $password, "\r");
my $line = join(' , ', #array);
print "Content-type: text/html \n\n";
my $file = 'Members.csv';
open (FILE, '+>>$file') or die "Cannot open file";
my $inputLine = <FILE>;
while($inputLine = <FILE>)
{
if(index($line, $username) != 4){
print "<HTML>\n";
print "<HEAD>\n";
print "<TITLE> Error Page </TITLE> \n";
print "</HEAD>\n";
print "<BODY>\n";
print "The username you have entered is already in use.";
print "<br>Home Page \n";
print "<br>Registration Page \n";
print "</BODY>\n";
close(FILE);
}
else {
#seeking to the end of the file to append
seek(FILE, 0, 2);
print FILE $line;
}
}
close(FILE);

Are you sure your Perl interpreter is where you want it to be? Your shebang line is
#!/usr/loca/bin/perl
which is probably missing an 'l'. When you run the script locally via perl myscript.pl it ignores that line but when you run it under a webserver like Apache, it's actually used to find the Perl interpreter.
Other than that, have a look at your web server log (you should be able to access the error log even on simple hosting providers) because the error page your browser gets is merely the web server saying 'Hey, something went wrong there'.

Related

DBI->connect working when executed in terminal but not when executed from browser

I am writing a CGI program that prints a database query result in the browser. The script looks like this
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
print "Content-type: text/html\n\n";
my $driver = "mysql";
my $database = "DBname";
my $ip = "127.0.0.1";
my $db = "DBI:$driver:DBNAME:$ip:database=$database";
my $username = "user";
my $password = "pass";
print "Connecting ...";
my $connection = DBI->connect($db, $username, $password)
or print "Couldn't connect to database: " . DBI->errstr . "\n\n";
print "Successful connection\n";
my $query = $connection->prepare("SELECT id FROM table");
$query->execute() or die $DBI::errstr;
while ( my #row = $query->fetchrow_array() ) {
my ($id) = #row;
print "ID = $id \n";
}
$query->finish();
Well, my problem is that when I run it from terminal using
perl test.cgi
it works fine, and I get the print results properly. The CGI script is located in /Library/Webserver/CGI-Executables/ and it is configured by default /cgi-bin/ in /etc/apache2/httpd.conf
If I execute it via the web browser, I just get the first print Connecting and nothing else, not even the or print from DBI->connect. I have been trying to realize what the error is, but I am unable to get a useful solution.
From your final comment it is as I suspected: the perl that the server is using simply doesn't have DBD::mysql installed, whereas your command-line perl does
Simply get that driver module installed on the server and all should be well
Note that is doesn't need to be "configured for Apache", it is simply that the copy of perl that is used by the CGI code doesn't have that module
By the way, the content type should be text/plain, not text/html: you're not generating HTML

Perl mechanize print HTML form names

I'm trying to automate hotmail login. How can I find what the appropriate fields are? When I print the forms I just get a bunch of hex information.
what's the correct method and how is it used?
use WWW::Mechanize;
use LWP::UserAgent;
my $mech = WWW::Mechanize->new();
my $url = "http://hotmail.com";
$mech->get($url);
print "Forms: $mech->forms";
if ($mech->success()){
print "Successful Connection\n";
} else {
print "Not a successful connection\n"; }
this may help you
use WWW::Mechanize;
use Data::Dumper;
my $mech = WWW::Mechanize->new();
my $url = "http://yoururl.com";
$mech->get($url);
my #forms = $mech->forms;
foreach my $form (#forms) {
my #inputfields = $form->param;
print Dumper \#inputfields;
}
Sometimes it is useful to look at what the web site is asking in advance of coding up a reader or interface to it.
I wrote this bookmarklet that you save in your browser bookmarks and when you click it while visiting any html web page will show in a pop-up all the forms actions and fields with values even hidden. Simply copy the text below and paste into a new bookmark location field, name it and save.
javascript:t=%22<TABLE%20BORDER='1'%20BGCOLOR='#B5D1E8'>%22;for(i=0;i<document.forms.length;i++){t+=%22<TR><TH%20colspan='4'%20align='left'%20BGCOLOR='#336699'>%22;t+=%22<FONT%20color='#FFFFFF'>%20Form%20Name:%20%22;t+=document.forms[i].name;t+=%22</FONT></TH></TR>%22;t+=%22<TR><TH%20colspan='4'%20align='left'%20BGCOLOR='#99BADD'>%22;t+=%22<FONT%20color='#FFFFFF'>%20Form%20Action:%20%22;t+=document.forms[i].action;t+=%22</FONT></TH></TR>%22;t+=%22<TR><TH%20colspan='4'%20align='left'%20BGCOLOR='#99BADD'>%22;t+=%22<FONT%20color='#FFFFFF'>%20Form%20onSubmit:%20%22;t+=document.forms[i].onSubmit;t+=%22</FONT></TH></TR>%22;t+=%22<TR><TH>ID:</TH><TH>Element%20Name:</TH><TH>Type:</TH><TH>Value:</TH></TR>%22;for(j=0;j<document.forms[i].elements.length;j++){t+=%22<TR%20BGCOLOR='#FFFFFF'><TD%20align='right'>%22;t+=document.forms[i].elements[j].id;t+=%22</TD><TD%20align='right'>%22;t+=document.forms[i].elements[j].name;t+=%22</TD><TD%20align='left'>%20%22;t+=document.forms[i].elements[j].type;t+=%22</TD><TD%20align='left'>%20%22;if((document.forms[i].elements[j].type==%22select-one%22)%20||%20(document.forms[i].elements[j].type==%22select-multiple%22)){t_b=%22%22;for(k=0;k<document.forms[i].elements[j].options.length;k++){if(document.forms[i].elements[j].options[k].selected){t_b+=document.forms[i].elements[j].options[k].value;t_b%20+=%20%22%20/%20%22;t_b+=document.forms[i].elements[j].options[k].text;t_b+=%22%20%22;}}t+=t_b;}else%20if%20(document.forms[i].elements[j].type==%22checkbox%22){if(document.forms[i].elements[j].checked==true){t+=%22True%22;}else{t+=%22False%22;}}else%20if(document.forms[i].elements[j].type%20==%20%22radio%22){if(document.forms[i].elements[j].checked%20==%20true){t+=document.forms[i].elements[j].value%20+%20%22%20-%20CHECKED%22;}else{t+=document.forms[i].elements[j].value;}}else{t+=document.forms[i].elements[j].value;}t+=%22</TD></TR>%22;}}t+=%22</TABLE>%22;mA='menubar=yes,scrollbars=yes,resizable=yes,height=800,width=600,alwaysRaised=yes';nW=window.open(%22/empty.html%22,%22Display_Vars%22,%20mA);nW.document.write(t);
I tried to mimc the post request that sends your login info, but the web site seems to be dynamically adding a bunch of id's ---long generated strings etc to the url and I couldn't figure out how to imitate them. So I wrote the hacky work-around below.
#!/usr/bin/perl
use strict;
use warnings;
use WWW::Curl::Easy;
use Data::Dumper;
my $curl = WWW::Curl::Easy->new;
#this is the name and complete path to the new html file we will create
my $new_html_file = 'XXXXXXXXX';
my $password = 'XXXXXXXX';
my $login = 'XXXXXXXXX';
#escape the .
$login =~ s/\./\\./g;
my $html_to_insert = qq(<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script><script type="text/javascript">setTimeout('testme()', 3400);function testme(){document.getElementById('res_box').innerHTML = '<h3 class="auto_click_login_np">Logging in...</h3>';document.f1.passwd.value = '$password';document.f1.login.value = '$login';\$("#idSIButton9").trigger("click");}var counter = 5;setInterval('countdown()', 1000);function countdown(){document.getElementById('res_box').innerHTML = '<h3 class="auto_click_login_np">You should be logged in within ' + counter + ' seconds</h3>';counter--;}</script><h2 style="background-color:#004c00; color: #fff; padding: 4px;" id="res_box" onclick="testme()" class="auto_click_login">If you are not logged in after a few seconds, click here.</h2>);
$curl->setopt(CURLOPT_HEADER,1);
my $url = 'https://login.live.com';
$curl->setopt(CURLOPT_URL, $url);
# A filehandle, reference to a scalar or reference to a typeglob can be used here.
my $response_body;
$curl->setopt(CURLOPT_WRITEDATA, \$response_body);
open( my $fresh_html_handle, '+>', 'fresh_html_from_login_page.html');
# Starts the actual request
my $curl_return_code = $curl->perform;
# Looking at the results...
if ($curl_return_code == 0) {
print("Transfer went ok\n");
my $response_code = $curl->getinfo(CURLINFO_HTTP_CODE);
# judge result and next action based on $response_code
print $fresh_html_handle $response_body;
} else {
# Error code, type of error, error message
print("An error happened: $curl_return_code ".$curl->strerror($curl_return_code)." ".$curl->errbuf."\n");
}
close($fresh_html_handle);
#erase whatever a pre-existing edited file if there is one
open my $erase_html_handle, ">", $new_html_file or die "Hork! $!\n";
print $erase_html_handle;
close $erase_html_handle;
#open the file with the login page html
open( FH, '<', 'fresh_html_from_login_page.html');
open( my $new_html_handle, '>>', $new_html_file);
my $tracker=0;
while( <FH> ){
if( $_ =~ /DOCTYPE/){
$tracker=1;
print $new_html_handle $_;
} elsif($_ =~ /<\/body><\/html>/){
#now add the javascript and html to automatically log the user in
print $new_html_handle "$html_to_insert\n$_";
}elsif( $tracker == 1){
print $new_html_handle $_;
}
}
close(FH);
close($new_html_handle);
my $sys_call_res = system("firefox file:///usr/bin/outlook_auto_login.html");
print "\n\nresult: $sys_call_res\n\n";

how to pass a variable from an HTML form to a perl cgi script?

I would like to use an HTML form to pass a variable to Perl CGI script so that I can process that variable, and then print it out on another HTML page.
Here is my HTML code: http://jsfiddle.net/wTVQ5/.
Here is my Perl CGI script to links the HTML. Here is the way I would like to do it (since it uses less lines and probably more efficient).
#!/usr/bin/perl
use warnings; use strict;
use CGI qw( :standard);
my $query = CGI->new;
# Process an HTTP request
my $user = $query->param('first_name');
# process $user... for example:
my $foo = "Foo";
my $str = $user . $foo;
print "Content-type:text/html\r\n\r\n";
print "<html>";
print "<head>";
print "<title>Hello - Second CGI Program</title>";
print "</head>";
print "<body>";
print "<h2>Hello $str - Second CGI Program</h2>";
print "</body>";
print "</html>";
1;
Here's a way I read from a tutorial and makes more sense to me:
#!/usr/bin/perl
use warnings; use strict;
my ($buffer, #pairs, $pair, $name, $value, %FORM);
# Read in text
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;
if ($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
}else {
$buffer = $ENV{'QUERY_STRING'};
}
# Split information into name/value pairs
#pairs = split(/&/, $buffer);
foreach $pair (#pairs) {
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%(..)/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
my $user = $FORM{first_name};
# process $user... for example:
my $foo = "Foo";
my $str = $user . $foo;
print "Content-type:text/html\r\n\r\n";
print "<html>";
print "<head>";
print "<title>Hello - Second CGI Program</title>";
print "</head>";
print "<body>";
print "<h2>Hello $str - Second CGI Program</h2>";
print "</body>";
print "</html>";
1;
Both of these don't work properly BTW. When I click on the submit button on the HTML page, it just links me to the script instead of passing the variable, processing it, and printing out the HTML page.
this line:
print "Content-type:text/html\r\n\r\n";
should be:
print "Content-type:text/html\n\n";
or better:
print $query->header;
Also, ensure your web server was well configurated for CGI. And, if you have enough time, use a modern web application approach, there are many frameworks that may be better than CGI (Dancer, Mojolicious, OX, ...)
I see your using CGI 'standard', no need really to initiate a CGI->new unless you just wanted to, also you said less lines, you could just do something like this.
use strict;
use warnings;
use CGI qw( :standard );
my $user = param('first_name') || q/foo/;
print header,
start_html(-title => 'Hello'), h1('Hello ' . $user), end_html;
You need to edit your httpd.conf with something like this.
AddHandler cgi-script cgi pl
<Directory /path/to/cgi/files>
Options +ExecCGI
</Directory>
If you are running this locally you could create a folder named public_html in your home directory structure and set this to run your scripts, you would just have to configure that also mapping it to that location.

Upload file using Perl CGI

I am able to create my directory but I cannot seem to place the file in the directory.
#!/usr/bin/perl
use Cwd;
use CGI;
my $dir = getcwd();
print "Current Working Directory: $ dir\n";
my $photoDir = "$dir/MyPhotos";
mkdir $photoDir
or die "Cannot mkdir $photoDir: $!"
unless -d $photoDir;
my $query = new CGI;
my $filename = $query->param("Photo");
my $description = $query->param("description");
print "Current filename: $filename\n";
my ( $name, $path, $extension ) = fileparse ( $filename, '\..*' ); $filename = $name . $extension;
print $filename;
my $upload_filehandle = $query->upload("Photo");
open ( UPLOADFILE, ">$photoDir/$filename" )
or die "$!";
binmode UPLOADFILE;
while ( <$upload_filehandle> )
{ print UPLOADFILE; }
close UPLOADFILE;
The CGI stack trace shows no errors but the log shows there is no output
LOG: 5 5020-0:0:0:0:0:0:0:1%0-9: CGI output 0 bytes.
CGI.pm manual suggests this path to saving uploaded files. Try this additional check and write method and see if it helps.
$lightweight_fh = $q->upload('field_name');
# undef may be returned if it's not a valid file handle
if (defined $lightweight_fh) {
# Upgrade the handle to one compatible with IO::Handle:
my $io_handle = $lightweight_fh->handle;
open (OUTFILE,'>>','/usr/local/web/users/feedback');
while ($bytesread = $io_handle->read($buffer,1024)) {
print OUTFILE $buffer;
}
}
Also make sure you have your HTML form has required type like this: <form action=... method=post enctype="multipart/form-data">

Script to change FTP password

I have the following script to update one of my FTP passwords every 15 days through a cronjob and e-mail the appropriate people after the attempt has been made. It randomly will fail and so I will run it again manually and it will work. I can't seem to find where it's going wrong.
The script is connecting to a local mysql database grabbing the login and password for an account and then changing that password on FTP. Everything is successful up until the changing the password part. Again it's random, sometimes it works, sometime it doesn't.
Thanks!
#!/usr/bin/perl -w
#
use DBI;
use Net::FTP;
our $dbh = DBI->connect('DBI:mysql:database:127.0.0.1','user','password') or die "Aargh $!\n";
$transquery=q{SELECT dest_login,dest_password FROM list where id=123};
$sth=$dbh->prepare($transquery);
$sth->execute();
while($co=$sth->fetchrow_hashref){
$login=$co->{'dest_login'};
$pass=$co->{'dest_password'};
}
$changeresult='FAIL';
$actionlog='';
$newstring='';
$upperchars='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$lowerchars='abcdefghijklmnopqrstuvwxyz';
$allowedchars='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789##$';
$l=length($upperchars);
$newstring.=substr($upperchars,int(rand($l)),1);
$newstring.=substr($lowerchars,int(rand($l)),1);
$l=length($allowedchars);
for ($i=0;$i<6;$i++){
$newstring.=substr($allowedchars,int(rand($l)),1);
}
print "$newstring\n";
$actionlog .= "Setting Password for $login from $pass to $newstring\n";
$username=
eval{
$ftp=Net::FTP->new('x.x.x.x',Timeout=>480,Debug=>1) or die "Error connecting FTP $!\n";
$changepassword="$pass/$newstring/$newstring";
$ftp->login($login,$changepassword) or die "Error changing password $!\n";
#If we are here, time to update the password
$changeresult='SUCCESS';
$actionlog .= "Password successfully updated\n";
$transquery=q{UPDATE list set dest_password=(?) where id=123};
$sth=$dbh->prepare($transquery);
$sth->execute($newstring);
};
if ($#) {
$actionlog = $actionlog . "$#\n";
};
if($actionlog ne ""){
#print $actionlog;
#my $send_to = "To: someone\#example.com\n";
my $send_to = "To: databaseusers\#example.com\n";
my $sendmail = "/usr/sbin/sendmail -t";
open(SENDMAIL, "|$sendmail") or die "Cannot open $sendmail: $!";
print SENDMAIL "Reply-to: databasepassword\#example.com\n";
print SENDMAIL "Subject: Password Change Information [$changeresult]\n";
print SENDMAIL $send_to;
print SENDMAIL "Content-type: text/plain\n\n";
print SENDMAIL $send_to;
print SENDMAIL "Content-type: text/plain\n\n";
print SENDMAIL $actionlog;
close(SENDMAIL);
$actionlog='';
}
else{
#print "Nothing done this session\n";
USUW might tell you something. ( use strict; use warnings; )
Does anything print?
You don't do much error checking in the DBI part at the beginning, perhaps you're getting a connect error. AIX boxes used to have this problem of getting a client port that the system was unsure about whether or not it was in use. When that happened, it would just fail to connect to the database.
I finally fixed that problem for our scripts by examining the $OS_ERROR ( aka $! ) for that particular code ( Errno::EADDRINUSE ) and then waiting and retrying, with an exponential falloff ( wait 2 seconds, then 4, then 8 ... ).
If your script "dies for some reason" then it's important the script can tell you that reason. I would investigate the topic of error reporting in the various modules you are using.
For example Net::FTP allows you to pass a Debug => 1 switch, and then you'll see the whole conversation.
And I know that there is a whole lot more with DBI where you can get error reporting.