navwin » Tech Talk » Beyond the Basics » Grrr - Perl (Replace variables in Template)
Beyond the Basics
Post A Reply Post New Topic Grrr - Perl (Replace variables in Template) Go to Previous / Newer Topic Back to Topic List Go to Next / Older Topic
Christopher
Moderator
Member Rara Avis
since 1999-08-02
Posts 8296
Purgatorial Incarceration

0 posted 2002-04-18 12:18 PM



What am i doing wrong here? I basically modified this off one of my older scripts that i had working... and i can't for the life of me figure out why this isn't working??? I have all the permissions set properly... i can't find any missing semi-colons... all my braces match... *sigh*

Help! I thought i had a good handle on this... i know it's probably somethign really simple too. heh.

Chris
form: http://www.countlesshorizons.com/processforms/form.htm

perl:

#!/usr/local/bin/perl

$title = $input{'title'}; #title of poem
$author = $input{'author'}; #author's name
$lname = $input{'lname'}; #letter of alphabet to assign author (am or nz)
$submission = $input{'submission'}; #body of submission
$fileNumber = $input{'filenumber'}; #name of file
$prevNum = $fileNumber - 1; #insert file name to assign values
$nextNum = $fileNumber + 1; #insert file name to assign values
$authEmail = $input{'authemail'}; #author's email address


#Subroutines
&inputIn;
&header;
&writePage;

#subroutine to read and parse input and place into array
sub inputIn {
    if ($ENV{'REQUEST_METHOD'} eq 'GET')
    {$formInfo = $ENV{'QUERY_STRING'};}
    else
    {read(STDIN, $formInfo, $ENV{'CONTENT_LENGTH'});}
    @inputPairs = split (/[&;]/, $formInfo);
    %input = ();
    foreach $pair (@inputPairs) {
        $pair =~ s/\+/ /g;
        ($name, $value) = split (/=/, $pair);
        $name =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
        $value =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
        $input{$name} = $value;
        }
}

#Subroutine to write CGI Header for HTML page
sub header {
    print qq~Content-type: text/html\n\n~;
}

#imports template page and replaces variables
sub writePage {
open(TEMPLATE, 'templatepoems.htm');
@Page = <TEMPLATE>;
close(TEMPLATE);
    foreach $line(@Page) {
      $line =~ s/\*\*title\*\*/$title/;
      $line =~ s/\*\*author\*\*/$author/;
      $line =~ s/\*\*lname\*\*/$lname/;
      $line =~ s/\*\*submission\*\*/$submission/;
      $line =~ s/\*\*filenumber\*\*/$fileNumber/;
      $line =~ s/\*\*prevnum\*\*/$prevNum/;
      $line =~ s/\*\*nextnum\*\*/$nextNum/;
      $line =~ s/\*\*authemail\*\*/$authEmail/;
      print $line;
    }
}


template:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
**title**<br>
**author**<br>
**lname**<br>
**submission**<br>
**filenumber**<br>
**prevnum**<br>
**nextnum**<br>
**authemail**<br>
</body>
</html>

© Copyright 2002 C.G. Ward - All Rights Reserved
Ron
Administrator
Member Rara Avis
since 1999-05-19
Posts 8669
Michigan, US
1 posted 2002-04-18 05:37 PM


I haven't had the chance to look at the link to see what it's doing, and you didn't give any indication of what it's not doing, but I'm going to take a quick guess. If this doesn't help, I'll try to find time to look more deeply later.

My first glace suggests you're trying to use variable before they've been filled? The very first line of the script is assigning $title from the %input hash, but there's absolutely no way that hash can have anything in it yet. Put your call to the inputln routine above the assignments instead of below.

Better yet - don't fill two variable for the same job. Change your regular expressions to use the hash. For example …

$line =~ s/\*\*title\*\*/$input{'title'}/;

… will save a completely useless assignment. Make sense?

If that doesn't help, I'll be back after dinner.

Christopher
Moderator
Member Rara Avis
since 1999-08-02
Posts 8296
Purgatorial Incarceration
2 posted 2002-04-18 06:13 PM


actually that DOES make complete sense (both, cutting down the work, but especially the point at which the variables are being called)

what it's NOT doing, is not filling in the text as it should... not being defined would fit exactly what it's doing....

heh

i will try this when i get home tonight ron - thank you, as always, for your help... and... uhmm... if you DO get some free time (hahahah) or at least the inclination, would you mind a quick run through on how i might be able to have the output of this file saved to my server?

peace

Chris

Ron
Administrator
Member Rara Avis
since 1999-05-19
Posts 8669
Michigan, US
3 posted 2002-04-18 10:11 PM


Well, on one hand, saving the information to disk is simple. Concatenate the variables together, open a file for appending, write one line to the file, close the file.

Using the tilde char as your field delimiter, it might look like this:

$dataline = "$title~$author~$lname~$submission";
open(LOGS,">>path/file.log "); # append operation
print LOGS "$dataline\n";
close(LOGS)

Piece of cake. Unfortunately, there's always that other hand waiting around to spoil the simplicity.  

In a multi-user environment, you need to make sure two visitors aren't hitting the disk file at the same exact moment, so you need a file locking mechanism. You also need to make sure your delimiter (the tilde in this case) isn't included as text in any of your variables, or you'll have the wrong number of "fields" in that line. And because you're writing the data as a single line (record) in the text file, you'll need to make sure there's no line breaks in the data, too.

The last two are simple substitutions, but the locking mechanism takes a bit more. I use a couple of subroutines to do it.

# lock and unlock routines rely on SYMLINK and therefore a
# non-existant file can not be locked
sub GetLock {
  my $file_name = $_;
  my ($i);

  ## try several times, waiting a second in between
  for ($i = 1; $i <= 10; ++$i) {
    ## true if the lock file was created
    if ( symlink ($file_name, "$file_name.lock") ) {
      return 1;        # success!
    }
    sleep 1;
  }
  &FATAL("Could not get lock on $file_name<br>$file_name.lock");
  return 0;                # failure :-(
}

sub DropLock {
  my ($file_name) = $_;
  unlink ("$file_name.lock");
}

I make the necessary subsitutions in my input routine, what you're calling inputln. That's because there are a LOT of other substitutions you should make in order to protect the security of your system. A very common hacking trick is to type a OS command as data in a form and then hope the script passes it to the system. To protect ourselves, we need to test for that before doing anything else with the form data. Inside the foreach loop, just before you assign $value to the hash, I would do something like this:

# remove potentially dangerous commands that should never
# be passed to any Unix process (like sendmail)
$value =~ s/\0//g;
$value =~ s/system\(.+//g;
$value =~ s/grep//g;
$value =~ s/\srm\s//g;
$value =~ s/\srf\s//g;
$value =~ s/\.\.([\/\:]|$)//g;
$value =~ s/< *((SCRIPT)|(APPLET)|(EMBED))[^>]+>//ig;
# disable all HTML commands (I'm including spaces so it'll show here)
$value =~ s/</& lt;/g;
$value =~ s/>/& gt;/g;
$value =~ s/~/& #0124;/sg; # protect our delimiter
$value =~ s/\n/< br>/g; # convert line breaks

So now, our "simple" write to disk should look something like this:

$dataline = "$title~$author~$lname~$submission";
&GetLock("path/file.log");
open(LOGS,">>path/file.log "); # append operation
print LOGS "$dataline\n";
close(LOGS)
&DropLock("path/file.log");


Christopher
Moderator
Member Rara Avis
since 1999-08-02
Posts 8296
Purgatorial Incarceration
4 posted 2002-04-18 10:28 PM


worked perfectly fine - thank you so much for your patience Ron.

NOW -

if you have time, or inclination, i have a few other questions. i'll be trying to find the answers to these as well in my books, but thought i'd pose them in case you had any input:

1. re: above - saving file to server (a raw guess is something to do with STDOUT...)

2. i want to eliminate a space between the characters in a string... oh... i think i may have got it. maybe using something along the lines of: $variable =~ s/\%%20(i don't know what character to use here to represent the space)//g;

3. format a number properly - ie: in above examples, $filenumber comes out as a 6-digit number, but $prevNum and $nextNum comes out as a two or three digit number, skipping all the leading zeros.

okies... that should do for now, thoguh of course there's lots more later. heh.

thanks again Ron

Chris

Christopher
Moderator
Member Rara Avis
since 1999-08-02
Posts 8296
Purgatorial Incarceration
5 posted 2002-04-18 10:32 PM


heh


of course it's not simple!!!

you replied in the (long) time it took me to write my last reply (bouncing back and forth between book and screen... can't seem to find what represents the space character)

looks good, and i was just reading about the security issues today - thank you for that snippet.

does writing a new "htm" file work along the same lines? i would imagine it does... looks like i'm in for some testing!

will come back to this after playing a little bit - this is still so fun!

Chris

Ron
Administrator
Member Rara Avis
since 1999-05-19
Posts 8669
Michigan, US
6 posted 2002-04-19 01:05 AM


1. Writing files - yea, writing an HTML files is basically the same. Except you rarely have to lock them, and you do NOT want to append as I indicated for the data files (change the >> to a single > to create or re-create a file).

To write any file, remember that you have to have permission to do so. That usually means setting up a directory with write permissions before running the script.

2. Eliminating spaces - you have the right idea, but you don't have to "represent" a space with any special code. $variable =~ s/ //; will work fine.

3. Formatting fixed width numbers - this one requires a bit of a kludge. The function sprintf exists so we can print columns of numbers and make sure they line up correctly. It does its job by adding spaces to the beginning of numbers, but we can easily use a regexp to change those leading spaces into leading zeroes.

$number = 17;
$number = sprintf("%5d", $number);
$number =~tr/ /0/;

The result would be 00017

Christopher
Moderator
Member Rara Avis
since 1999-08-02
Posts 8296
Purgatorial Incarceration
7 posted 2002-04-20 05:38 PM


OK - all that is good! i even figured out a few things on my own (like how to turn all the letters in a string to lower case and how to retrieve just the first letter!!!)

HOWEVER - I am having problem with file permissions... how do i tell the program that i want to do this: ../../processforms/$filename   ? I tried it using escape characters (see following, but it's not working???)

hmm... will keep working at it. Thanks again Ron

Have:

open(FILE,">$dir..\/..\/\/processforms\/$fileNumber.htm") ||
die "Can't create index file $fileNumber: $!\n\n";

Get:

Can't create index file 00021: No such file or directory

Christopher
Moderator
Member Rara Avis
since 1999-08-02
Posts 8296
Purgatorial Incarceration
8 posted 2002-04-20 05:39 PM


and i DO have all my permissions set to read write and execute (just in case at this point)
Ron
Administrator
Member Rara Avis
since 1999-05-19
Posts 8669
Michigan, US
9 posted 2002-04-20 06:16 PM


Hard to say for sure what's happening, Chris, but the path in your open statement looks, uh, a little strange. How can you have a variable ($dir) in front of the parent dir symbols (double dots)?

The best thing is usually to assign your entire open parameter to a variable, and then print that in your die clause.

my $openPath = "$dir..\/..\/\/processforms\/$fileNumber.htm";
open(FILE,">$openPath ") || die "Can't create index file $openPath: $!\n\n";

Post A Reply Post New Topic ⇧ top of page ⇧ Go to Previous / Newer Topic Back to Topic List Go to Next / Older Topic
All times are ET (US). All dates are in Year-Month-Day format.
navwin » Tech Talk » Beyond the Basics » Grrr - Perl (Replace variables in Template)

Passions in Poetry | pipTalk Home Page | Main Poetry Forums | 100 Best Poems

How to Join | Member's Area / Help | Private Library | Search | Contact Us | Login
Discussion | Tech Talk | Archives | Sanctuary