print to a text file.

They have: 12 posts

Joined: Aug 2000

I've tryed over at sitepoint now im going to try here i know its this part of the script now thats wrong : -

sub printaddr {
open (data, ">>$banlist");
@data=;
close(data);
foreach $data(@data) {
@datax=($data);
unless ($input{'ipnumber'} eq "@datax[0]") {
open(ip, ">>$banlist");
print ip "$input{'ipnumber'}\n";
close(ip);
}
}
}

What inside that coding looks wrong?. i'm trying to get it to print an ip address to a text file. So if i typed in 234.234 and pressed enter the script would print it to the file but this coding dont ive played about with it for nearly 4 hours now some help or even a solution would be great.

Mark Hensler's picture

He has: 4,048 posts

Joined: Aug 2000

1) your opening the file wrong the first time...open
(data, ">>$banlist"); This opens the file for appending. Use this: (data, "$banlist");

2) Why are you doing this?
foreach $data(@data) {
@datax=($data);

What is going to be used to split the string into an array?

3) Can we see the flat file?

If you just want to append to the flat file IPs that are not already in there, try this. (not tested):

open(FILE, $banlist);
@file = <FILE>;
close(FILE);

foreach $line (@file) {
     if ($input{'ipnumber'} eq $line)
          $found = "yes";
}

if ($found != "yes") {
     open(ip, ">>$banlist");
     print ip "$input{'ipnumber'}\n";
     close(ip);
}
'

This could be compressed a bit more, but it should work fine for what you need.

Mark Hensler
If there is no answer on Google, then there is no question.

They have: 12 posts

Joined: Aug 2000

Thats the file. I added your coding too in place of the orignal and got my best friend the internal server error. I'm guessing i may have to start from scratch with the peice of code you said but wouldnt know where to begin. It shows in the file im not the brightest.

#!/usr/bin/perl
#########################
# Variables

$banfile = "/home/demon/demon-www/cgi-bin/badboys.txt";
# data path to your banned.txt file.
# it must be chmodded 777, or world-writable
# (it was most convenient for me to place it
# in my messages dir since that was already
# chmodded 777)

$password = "admin";

# admin password
$scriptfn = "everyadmin.cgi";
# filename of this script (full path not necessary)
#########################
&get_input;
if ($input{'password'} ne "$password") {
print <<"END";
Content-type: text/html

<HTML>
<HEAD>
<TITLE>PASSWORD REQUIRED</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#001F76" VLINK="#001F76">
<form method="post" action="$scriptfn">
<center><table border="0" cellspacing="0" width="250">
<tr><td bgcolor="#001F76"><font color="white" size="+1"><b>PASSWORD REQUIRED</b></font>
</td></tr><tr><td bgcolor="#DCDCDC" align="center">
<input type="password" name="password"></td></tr></table></center></form>
</BODY>
</HTML>

END
}
elsif ($input{'ipnumber'} ne "") {
&printaddr;

print <<"END";
Content-type: text/html

<HTML>
<HEAD>
<TITLE>IP NUMBER BANNED</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#001F76" VLINK="#001F76">
<center><table border="0" cellspacing="0" width="250">
<tr><td bgcolor="#001F76"><font color="white" size="+1"><b>IP NUMBER BANNED</b></font>
</td></tr><tr><td bgcolor="#DCDCDC" align="center">
The IP number you entered has been banned.</td></tr></table></center>
</BODY>
</HTML>

END
} else {
print <<"END";
Content-type: text/html


<HTML>
<HEAD>
<TITLE>BAN IP NUMBER</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#001F76" VLINK="#001F76">
<form method="post" action="$scriptfn">
<center><table border="0" cellspacing="0" width="250">
<tr><td bgcolor="#001F76"><font color="white" size="+1"><b>BAN IP NUMBER</b></font>
</td></tr><tr><td bgcolor="#DCDCDC" align="center">
<input type="text" name="ipnumber" value="IP NUMBER" maxlength="15"></td></tr></table>
</center><input type="hidden" name="password" value="$input{'password'}"></form>
</BODY>
</HTML>

END
}
sub printaddr {
open (data, "<$banfile");
@data=<data>;
close(data);
foreach $data(@data) {
@datax=($data);
unless ($input{'ipnumber'} eq "@datax[0]") {
open(ip, "$banfile");
print ip "$input{'ipnumber'}\n";
close(ip);
}
}
}


sub get_input {

if($ENV{'REQUEST_METHOD'} =~ /get/i)
{
@pairs=split(/&/,$ENV{'QUERY_STRING'});
}
else
{
$buffer = "";
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs=split(/&/,$buffer);
}
foreach $pair (@pairs)
{
@a = split(/=/,$pair);
$name=$a[0];
$value=$a[1];
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$value =~ s/~!/ ~!/g;
$value =~ s/\+/ /g;
$value =~ s/(\r)+/\-\-/g;
$value =~ s/\n+//g;
push (@input,$name);
push (@input,$value);
}
%input=@input;
%input;
}
'

Mark Hensler's picture

He has: 4,048 posts

Joined: Aug 2000

I haven't analyzed the rest of the file...

What happens when we do something like this:

sub printaddr {

open (FILE, "$banfile");
$ThePage = join('|', <FILE>);
close (FILE);

unless ($ThePage =~ /$input{'ipnumber'}/) {
$ThePage =~ s/\|/\n/g;
$ThePage .= "$input{'ipnumber'}\n";

open(FILE, ">$banfile");
print FILE $ThePage;
close(FILE);
} #END unless

} #END sub
'

Logic:
By default, pages are split up by newline characters. So I'm joining them into one string (memory saver). $ThePage will look like this:
"line1|line2|line3" ... etc.

Then I say 'unless $ThePage' contains the IP, do the following:
1) replace "|" with newline characters
2) add the IP to $ThePage
3) put $ThePage back into the file

Mark Hensler
If there is no answer on Google, then there is no question.

Mark Hensler's picture

He has: 4,048 posts

Joined: Aug 2000

sorry about the face...
that one line looks like this:

$ThePage = join('|', );

They have: 12 posts

Joined: Aug 2000

thanks it works.

Whats the command to edit a text file from a cgi script?. To also remove the banned ip from the list. Instead of logging into ftp and removing it.

Thank you for your help.

Mark Hensler's picture

He has: 4,048 posts

Joined: Aug 2000

You want to make a script to remove IPs from the list?

I'll use the same variable for the IP and let you make a new subroutin to put it in...

sub removeaddr {

open (FILE, "$banfile");
$ThePage = join('|', <FILE>;
close (FILE);

$ThePage =~ /(.*)$input{'ipnumber'}|(.*)/;
$ThePage = $1 . $2;
$ThePage =~ s/\|/\n/g;

open(FILE, ">$banfile");
print FILE $ThePage;
close(FILE);
} #END unless

} #END sub
'

Logic:
1) replace "|" with newline characters
2) match the pattern [wild card] IP [wild card]
3) make $ThePage the first wild card and the second (thus, removing the IP)
4) put $ThePage back into the file

Mark Hensler
If there is no answer on Google, then there is no question.

They have: 568 posts

Joined: Nov 1999

You can't push the entire contents of a file into a file into a string without saying local($\); first.

Just thought i'd point that out. Smiling

Mark Hensler's picture

He has: 4,048 posts

Joined: Aug 2000

I don't understand?

your saying that this won't work?

open (FILE, "$banfile");
$ThePage = join('|', <FILE>;
close (FILE);

$ThePage =~ s/\|/\n/g;
print $ThePage;
'

Try it... I've used it before and it's worked fine.

what does local(); do?
I've seen $_ but not $\ ... do you mean local($_);?

Mark Hensler
If there is no answer on Google, then there is no question.

They have: 568 posts

Joined: Nov 1999

nope, that wont work

it will only do the first line of the file

try this

sub removeaddr {
local($/);
open (FILE, "$banfile");
$ThePage = join('|', <FILE>;
close (FILE);

$ThePage =~ /(.*)$input{'ipnumber'}|(.*)/;
$ThePage = $1 . $2;
$ThePage =~ s/\|/\n/g;

open(FILE, ">$banfile");
print FILE $ThePage;
close(FILE);
}
}
'

They have: 568 posts

Joined: Nov 1999

Ok, scratch that. It MIGHT work, i just noticed you didn't do a file to string right away, but it's best to be safe.

They have: 161 posts

Joined: Dec 1999

I really think we need to start from scratch here. WHAT does the file holding the IP addresses look like? Is it just one IP address per line? Or is there more data?

Next, all you want to do is see whether or not the IP address is in the file, and if it is not, add it?

If I have these basic ideas down correctly, then I suggest you try the following code. I'm going to present in stages.

Code 1.0

sub addIP {
  my $to_add = shift;
  my $found = 0;

  # look for it in the file
  open FILE, $IP_FILE or die "can't open $IP_FILE: $!";
  while (<FILE>) {
    chomp;
    if ($_ eq $to_add) { $found = 1; last; }
  }
  close FILE;

  # add it to the file if it wasn't there
  if (not $found) {
    open FILE, ">> $IP_FILE" or die "can't append to $IP_FILE: $!";
    print FILE "$to_add\n";
    close FILE;
  }
}
'

This code saves us the trouble of reading through the entire file if the IP address is already in there, due to me calling last() if the IP address is found. This code can be made more efficient, though, by opening the file for reading AND writing:

Code 2.0

sub addIP {
  my $to_add = shift;
  my $found = 0;

  # look for it in the file
  open FILE, "+< $IP_FILE" or die "can't open $IP_FILE for read/write: $!";
  seek FILE, 0, 0;  # ensure we're at the beginning
  while (<FILE>) {
    chomp;
    if ($_ eq $to_add) { $found = 1; last; }
  }

  # add it if not found
  if (not $found) {
    print FILE "$to_add\n";
  }
  close FILE;
}
'

This is better, since we only open the file once, and it still works as before. I'm going to streamline things a bit, to make it look a bit cleaner:

Code 2.1

sub addIP {
  my $to_add = shift;
  my $found = 0;

  # look for it in the file
  open FILE, "+< $IP_FILE" or die "can't open $IP_FILE for read/write: $!";
  seek FILE, 0, 0;  # ensure we're at the beginning
  while (<FILE>) {
    chomp;
    last if $found = ($_ eq $to_add);  # XXX
  }

  # add it if not found
  print FILE "$to_add\n" if not $found;

  close FILE;
}
'

The line I marked with XXX is pretty nifty -- if $_ and $to_add are the same, then $_ eq $to_add is true, and returns 1. Otherwise, it's false, and returns ''. In turn, $found gets set to the value returned by that test, so if the test returns 1, $found is set to 1, and we can exit the block.

There's another thing I'd suggest adding, which is file locking. This is so that if two people run your program at once, you needn't worry about your file getting corrupted.

Code 3.0

use Fcntl 'LOCK_EX';  # import the LOCK_EX constant

sub addIP {
  my $to_add = shift;
  my $found = 0;

  # look for it in the file
  open FILE, "+< $IP_FILE" or die "can't open $IP_FILE for read/write: $!";
  flock FILE, LOCK_EX;  # lock this file
  seek FILE, 0, 0;  # ensure we're at the beginning
  while (<FILE>) {
    chomp;
    last if $found = ($_ eq $to_add);
  }

  # add it if not found
  print FILE "$to_add\n" if not $found;

  close FILE;
  # file gets unlocked automatically
}
'

And last, but not least, let's localize the filehandle, so we can get rid of the $found variable too, as well as have Perl close the filehandle for us:

Code 3.1

use Fcntl 'LOCK_EX';  # import the LOCK_EX constant

sub addIP {
  my $to_add = shift;
  local *FILE;  # localize the filehandle

  # look for it in the file
  open FILE, "+< $IP_FILE" or die "can't open $IP_FILE for read/write: $!";
  flock FILE, LOCK_EX;  # lock this file
  seek FILE, 0, 0;  # ensure we're at the beginning
  while (<FILE>) {
    chomp;
    return if $_ eq $to_add;  # return if found
  }

  # if we got here, it couldn't have been found
  print FILE "$to_add\n";
  # file gets unlocked automatically
}
'

Ok, that's all for now.

They have: 12 posts

Joined: Aug 2000

Thanks guys for your help but both codes both cause internal server errors Sad.

They have: 161 posts

Joined: Dec 1999

Ok, so you're getting a server error. When you used my code, did you:

* make a variable called $IP_FILE that holds the path to the file you want to edit?
* call the function as addIP($addr), where $addr is a scalar holding an IP address?
* make sure the web user can write to the IP address file?
* check the error log to see WHY the program died?

I suggest you include this line of code near the top of your program:

use CGI::Carp 'fatalsToBrowser';
'

Assuming you HAVE the CGI::Carp module, this will send fatal errors to your browser, instead of just being less-than-helpful.

They have: 12 posts

Joined: Aug 2000

Thanks again.

Well im very stumped now. I tryed adding those to the script but dont know what im doing wrong because no matter what i get an internal server error. I'm no perl whizz as you can see.

Want to join the discussion? Create an account or log in if you already have one. Joining is fast, free and painless! We’ll even whisk you back here when you’ve finished.