chriso has asked for the wisdom of the Perl Monks concerning the following question:

I'm running Apache2 on a NW 6.5 server. Here's my problem. I execute a script called makequiz.cgi which reads a text file and displays the questions in the browser. Currently there are 5 questions in this file.
After answering the questions and pressing the "Grade Quiz" button, the data is sent to a script called gradequiz.cgi which displays the questions and the correct answer and whether you got the question right/wrong.

The first time I run makequiz.cgi and answer the questions and submit them, the scripts run just fine. If I then go back and click the link again to execute makequiz.cgi, the questions are displayed ok, but when I submit the questions to gradequiz.cgi,

1. not all of the variables are passed to gradequiz. It grades the quiz indicating which questions are wrong/right but it does not pass the correct and incorrect answers or the user's name 2. It lists all the questions twice, questions 1-5 which it grades accurately, then redisplays the questions as 6-10, all of which are marked as wrong.
If I repeat the above by clicking the link to start the makequiz.cgi script, it does the same thing listed above only it now displays the questions 3 times, meaning it shows 15 questions. I'm sure it is something simple but I'm lost.

Here is my code for the makequiz:

print "<H2>Hello $fullname!</H2><P>"; print start_form (-name=>'makequiz', -method=>'POST', -action=>"http:/ +/ist221.nsm.tridenttech.edu/perl/gradequiz.cgi"); chdir("/perl/web"); open FILE, "$file" || die "Cannot open $file: $!"; while (<FILE>) { # ** READS EACH LINE OF TEXT FROM FILE chomp; # ** BY DEFAULT, CHOMPS OFF $/ F +ROM EACH LINE ($type, $value) = split (/:#\s*/, $_); # ***** SPLITS EACH LIN +E BY TYPE AND CONTENT if ($type =~ m/i/i) { # ** CHECKS FOR IMAGE print "<IMG SRC=http://ist221.tridenttech.edu/images/$courselocation/$ +value>"; print "<P>"; }elsif ($type =~ m/q/i) { # ** CHECKS FOR QUESTION LINE & PRINT +S QUESTION $questionno++; print "<B>$questionno. $value</B><BR>"; push(@allquestions, $value); }elsif ($type =~ m/a/i) { # ** CHECKS FOR ANSWER LINE @answers = split (/`\s*/, $value); $anslength = @answers; # ** LENGTH OF @ANSWERS push(@allanswers, $value); }elsif ($type =~ m/s/i) { # ** CHECKS FOR SELEC +TION LINE $buttonname = $type . $questionno; push(@allselections, $value); %selections = split (/`\s*/, $value); @options = keys (%selections); if ($anslength > 1) { # ** DETERMINE +S TO USE RADIO OR CHECKBOX print "<BR>"; print checkbox_group(-name=>$buttonname, -v +alues=>\@options, -linebreak=>'true', -labels=>\%selections); }else { print "<BR>"; print radio_group(-name=>$buttonname, -values +=>\@options, -linebreak=>'true', -labels=>\%selections, -default=>'-' +); } print "<BR><HR><BR>"; }elsif ($type =~ m/e/i) {# ** CHECKS FOR EXPLAINATION +LINE push (@explain, $questionno, $value); } # END ELSIF STATEMENTS } # ** END WHILE STATEMENT close (FILE); print "<CENTER>"; # ** THIS SECTION SENDS THREE ARRAYS TO THE GRADEQUIZ.CGI print hidden(-name=>"answers", -default=>\@allanswers); print hidden(-name=>"questions", -default=>\@allquestions); print hidden(-name=>"selections", -default=>\@allselections); print hidden(-name=>"explain", -default=>\@explain); print hidden(-name=>'fname', -value=>$fname); print hidden(-name=>'lname', -value=>$lname); print hidden(-name=>'reviewtime', -value=>$reviewtime); print hidden(-name=>'file', -value=>$file); print hidden(-name=>'id', -value=>$idinput); print submit (-value=>'Grade'); print "</CENTER>"; print end_form; } print "The last element in the questions array is $#allquestions<p>"; # ************************* END SUBROUTINE MAKEEXAM print end_html;

Thanks,
Chris

janitored by ybiC: Balanced <readmore> tags around longish codeblock, as per Monastery convention

Retitled by davido from 'Unusual problem'.

Replies are listed 'Best First'.
Re: Unusual problem with CGI based quiz
by Ovid (Cardinal) on Jul 06, 2005 at 17:06 UTC

    Hi chriso. When you make a major change in your node (aside from corrected a typo or adding a mild clarification sentence), it's helpful if you note the change. In this case, you replaced your code with something completely different. That's a shame because I was going to comment on your first code (not answering your question, but rather, pointing out some serious problems.)

    First off, your code had a significant security hole. I saw this:

    $file = param('file');

    That looked suspicious to me, so I read down further and saw this:

    open ALLSCORES, ">>/perl/web/examscores/$file" || open ALLSCORES, ">>/perl/web/examscores/$file.bak";

    There are a variety of issues with this. First off, you're allowing someone to atttempt to open any file on your system. What do you do when someone types in ../../../../../some_file_name? If the Web server is running with permissions to open that file, something very bad could happen. Unfortunately, since you list your URLs, bad guys not only know what the security hole is, they know where it is.

    If they don't have permissions to open the first file, you then have them attempt to open a second file. If the second open fails, it will do so silently and your code will not work.

    You may wish to read lesson three of my CGI course. That lesson covers basic security. The other lessons will probably benefit you, too, but that one is a good start.

    Cheers,
    Ovid

    New address of my CGI Course.

Re: Unusual problem with CGI based quiz
by cmeyer (Pilgrim) on Jul 06, 2005 at 17:12 UTC

    I'm guessing that you are running under mod_perl with the Apache::Registry module. If that is correct, then you are experiencing a classic problem with coding cgi style programs and running them under mod_perl.

    I'd suggest reading the excellent mod_perl user's guide http://perl.apache.org/docs/1.0/guide/. You might start with the chapter "CGI to mod_perl Porting", and pay special attention to the section called "Global Variables Persistence".

    Basically you want to use a lexically scoped variable (my $var;), scoped in manner that will go out of scope between each request.

    Coding cgi style programs under mod_perl contains several traps for those who aren't very familiar with Perl's variable scoping (and closures). Personally I feel that coding regular mod_perl handlers is much more straight forward.

    Ovid's comments are also very poignant.

    -Colin.

    WHITEPAGES.COM | INC

      Just to elaborate a little bit, if you're running this script under mod_perl or similar, every time the script is executed it just executes the code again, exactly as it was typed. In this case, because you use global variables, every time through the script you push more questions onto your array, and it never gets reset. The best way to fix this is by using my variables:
      { my @allquestions; ... push(@allquestions,"question"); ... print @allquestions; }
      This will cause the variable to be newly created every time your code is called, which will make it start off empty.

      In general, all of your variables in a mod_perl script should be my variables, unless you've thought about it and you really want them to retain their values between runs.

      You should also consider running your code under use strict and use warnings, and you should definitely configure your mod_perl to use "taint mode", which will stop mistakes like the one Ovid pointed out from becoming crippling security holes.

        "The best way to fix this is by using my variables:
        { my @allquestions; ... push(@allquestions,"question"); ... print @allquestions; }
        This will cause the variable to be newly created every time your code is called, which will make it start off empty."
        If I use "my" within an "if" statement, doesn't this make it impossible for me to pass that variable to another script when I use a form and pass them via a hidden variable if the hidden variables are outside the if statement?
Re: Unusual problem with CGI based quiz
by chriso (Sexton) on Jul 06, 2005 at 16:58 UTC
    I added the line "print The last element in the questions array is $#allquestions" to see what is happening to the questions array. Seems it gets bigger each time the script is run. Starts out as 4. Next time I run it it is 9. Next time is 14. If I restart my Apache server, it starts over with 4 but increases each time I use the makequiz.cgi script. However, only 5 questions display in the browser when makequiz executes. Any ideas as to how to clear that array?
    Chris
Re: Unusual problem with CGI based quiz
by Anonymous Monk on Jul 07, 2005 at 16:26 UTC
    Thank you everyone for your comments! I will pursue your recommendations. Also, thanks Ovid for taking the time on the security issue. As a novice perl prog. I'm glad ya'all take the time to offer constructive reviews.

    Kind regards,
    Chris