OSSP CVS Repository

ossp - Check-in [3406]
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [Patchset]  [Tagging/Branching

Check-in Number: 3406
Date: 2003-May-29 11:32:11 (local)
2003-May-29 09:32:11 (UTC)
User:rse
Branch:
Comment: add our cool Multiple Choice Test (MCT) engine to OSSP CVS to make sure its source is not lost over time
Tickets:
Inspections:
Files:
ossp-pkg/mct/README      added-> 1.1
ossp-pkg/mct/TODO      added-> 1.1
ossp-pkg/mct/mct.cgi      added-> 1.1
ossp-pkg/mct/mct.css      added-> 1.1
ossp-pkg/mct/mct.html      added-> 1.1
ossp-pkg/mct/rsa.html      added-> 1.1
ossp-pkg/mct/rsa.mct      added-> 1.1

ossp-pkg/mct/README -> 1.1

*** /dev/null    Sun Apr 28 21:22:11 2024
--- -    Sun Apr 28 21:26:04 2024
***************
*** 0 ****
--- 1,3 ----
+ 
+   Multiple Choice Test (MCT) Engine
+ 


ossp-pkg/mct/TODO -> 1.1

*** /dev/null    Sun Apr 28 21:22:11 2024
--- -    Sun Apr 28 21:26:04 2024
***************
*** 0 ****
--- 1,5 ----
+ - $name -> $spec
+ - $form -> $button
+ - $help replace with better solution
+ - @TITLE@ in .html
+ - sd&m idea: store result and give anyone just their position


ossp-pkg/mct/mct.cgi -> 1.1

*** /dev/null    Sun Apr 28 21:22:11 2024
--- -    Sun Apr 28 21:26:04 2024
***************
*** 0 ****
--- 1,700 ----
+ #!/e/epaperwork/sw/bin/perl
+ ##
+ ##  OSSP mct - Multiple Choice Test Engine
+ ##  Copyright (c) 2002 Ralf S. Engelschall <rse@engelschall.com>
+ ##  Copyright (c) 2002 The OSSP Project <http://www.ossp.org/>
+ ##  Copyright (c) 2002 Cable & Wireless Deutschland <http://www.cw.com/de/>
+ ##
+ ##  This file is part of OSSP mct, a multiple choice test engine
+ ##  which can be found at http://www.ossp.org/pkg/tool/mct/.
+ ##
+ ##  Permission to use, copy, modify, and distribute this software for
+ ##  any purpose with or without fee is hereby granted, provided that
+ ##  the above copyright notice and this permission notice appear in all
+ ##  copies.
+ ##
+ ##  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ ##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ ##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ ##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+ ##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ ##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ ##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ ##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ ##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ ##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ ##  SUCH DAMAGE.
+ ##
+ ##  mct.cgi: multiple choice test CGI engine (language: Perl)
+ ##
+ 
+ #   requirements
+ require 5.006;
+ use IO;               # standard in Perl 5.6
+ use POSIX;            # standard in Perl 5.6
+ use CGI;              # standard in Perl 5.6
+ #use Mail::Sendmail;   # standard in OpenPKG's Perl, else: CPAN!
+ 
+ #   program configuration
+ my $MY = {
+     PROG_NAME => 'OSSP mct',
+     PROG_VERS => '1.0.0',
+     PROG_DESC => 'Multiple Choice Test Engine',
+     PROG_HOME => 'http://www.ossp.org/pkg/tool/mct/',
+     MAIL_FROM => 'mct@epaperwork.org',
+     MAIL_HOST => 'mail.epaperwork.org',
+     TEMPLATE  => "<html>\n<head>\n\@HEAD\@\n</head>\n<body>\n\@BODY\@\n</body></html>"
+ };
+ 
+ #   switch to unbuffered output
+ $|++;
+ 
+ #   establish CGI object
+ my $CGI = new CGI;
+ 
+ #   generate an HTTP response
+ sub http_response {
+     my ($head, $body) = @_;
+ 
+     #   fill in template page
+     my $response = $MY->{TEMPLATE};
+     $response =~ s|\@HEAD\@|$head|sg;
+     $response =~ s|\@BODY\@|$body|sg;
+     
+     #   prepend with HTTP header
+     $response = $CGI->header(
+         -type             => 'text/html', 
+         -expires          => 'now', 
+         '-Content-length' => length($response)
+     ) . $response;
+ 
+     return $response;
+ }
+ 
+ #   activate a general error handler
+ $SIG{__DIE__} = sub {
+     my ($err) = @_;
+ 
+     $err =~ s|at\s+\S+\s+line\s+(\d+)|(line $1)|s;
+     $err =~ s|\n|<br>\n|sg;
+     print STDOUT &http_response(
+         "<title>ePaperwork :: Multiple Choice Test: ERROR</title>",
+         "<h1>ePaperwork :: Multiple Choice Test: ERROR</h1>\n" .
+         "<pre>$err</pre>\n"
+     );
+     exit(0);
+ };
+ 
+ #   convert Sugar-style markup text to HTML text
+ sub sug2html {
+        my ($text) = @_;
+ 
+     #   escape HTML special characters
+        $text =~ s|&|&amp;|sg;
+        $text =~ s|<|&lt;|sg;
+        $text =~ s|>|&gt;|sg;
+ 
+     #   expand Sugar markup
+        $text =~ s/\|\|(.+?)\|\|/<tt>$1<\/tt>/sg;
+        $text =~ s|//(.+?)//|<i>$1</i>|sg;
+        $text =~ s|\*\*(.+?)\*\*|<b>$1</b>|sg;
+        $text =~ s|-&gt;(.+?)::(.+?)&lt;-|<a href="$2" target="mct-extern">$1</a>|sg;
+        $text =~ s|-&gt;(.+?)&lt;-|<a href="$1" target="mct-extern">$1</a>|sg;
+ 
+        return $text;
+ }
+ 
+ #   read and parse MCT specification
+ sub readmct {
+     my ($name) = @_;
+ 
+     #   read MCT specification
+     my $fh = new IO::File "<$name"
+         || die "unable to open MCT specification \"$name\"";
+     my $mct = '';
+     $mct .= $_ while (defined($_ = $fh->getline()));
+     $fh->close();
+ 
+     #   provide line continuation feature
+     $mct =~ s|\s*\\\s*\n\s*| |sg;
+ 
+     #   parse MCT specification
+     my $MCT = {
+         'HEADER'   => {},
+         'RESULT'   => [],
+         'QUESTION' => []
+     };
+     $mct =~ s|^(.+?\n)\n(.+)$|&parse_header($1), &parse_body($2), ''|sge;
+ 
+     #   parse header lines
+     sub parse_header {
+         my ($mct) = @_;
+         while ($mct =~ s|^([A-Za-z][A-Za-z0-9-]*):\s*(.*?)\n||s) {
+             if (not defined($MCT->{HEADER}->{uc($1)})) {
+                 $MCT->{HEADER}->{uc($1)} = $2;
+             }
+             else {
+                 $MCT->{HEADER}->{uc($1)} .= $2;
+             }
+         }
+     }
+ 
+     #   parse body blocks
+     sub parse_body {
+         my ($mct) = @_;
+         $mct = "\n" . $mct . "\n";
+         $mct =~ s|\n\?\?\s+(.+?)\n(.+?\n)(?=\n)|&parse_question($1, $2), ''|sge;
+         $mct =~ s|\n==\s+(.+?)\n(.+?\n)(?=\n)|&parse_result($1, $2), ''|sge;
+     }
+ 
+     #   parse a single question block
+     sub parse_question {
+         my ($title, $mct) = @_;
+         my $e = { 
+             'TITLE'   => $title, 
+             'HINT'    => '',
+             'CHOICES' => []
+         };
+         foreach my $line (split(/\n/, $mct)) {
+             if ($line =~ m/^((?:!!))\s+(.+)$/s) {
+                 $e->{HINT} = $2;
+             }
+             elsif ($line =~ m/^((?:--|\+\+))\s+(.+)$/s) {
+                 my $c = { 'TYPE' => $1, 'TEXT' => $2 };
+                 push(@{$e->{CHOICES}}, $c);
+             }
+         }
+         push(@{$MCT->{QUESTION}}, $e);
+     }
+ 
+     #   parse a single result block
+     sub parse_result {
+         my ($title, $mct) = @_;
+         my $e = { 
+             'TITLE'   => $title, 
+             'CHOICES' => []
+         };
+         foreach my $line (split(/\n/, $mct)) {
+             if ($line =~ m/^((?:--))\s+(\d+)-(\d+)\s+(.+)$/s) {
+                 my $c = { 
+                     'TYPE'  => $1, 
+                     'RANGE' => [$2, $3], 
+                     'TEXT'  => $4 };
+                 push(@{$e->{CHOICES}}, $c);
+             }
+         }
+         push(@{$MCT->{RESULT}}, $e);
+     }
+ 
+     return $MCT;
+ }
+ 
+ #   deterministically permutate an array
+ sub permute {
+     my ($k, @a) = @_;
+ 
+     my @b = ();
+     $k = ($k * ($#a+1)) / 9;
+     $k = 1 if ($k < 1);
+     $k = ($k % ($#a+1));
+     $k = 1 if ($k < 1);
+ 
+     my $i = $k/2;
+     while ($#a > -1) {
+         for (my $j = 0; $j < ($#a+1); $j++) {
+             my $x = shift(@a);
+             if (($i % $k) == 0) {
+                 push(@b, $x);
+             }
+             else {
+                 push(@a, $x);
+             }
+             $i++;
+         }
+         $k-- if ($k > 1);
+     }
+ 
+     return @b;
+ }
+ 
+ #   adjust CGI environment to make sure that self-referencing
+ #   URLs go again through the direct URL of the MCT file!!
+ $ENV{SCRIPT_NAME} = $ENV{PATH_INFO};
+ delete $ENV{REQUEST_URI};
+ 
+ #   fetch CGI parameters
+ my $name   = $CGI->path_translated();
+ my $form   = $CGI->param('form')   || '';
+ my $perm   = $CGI->param('perm')   || '';
+ my $hint   = $CGI->param('hint')   || '';
+ my $note   = $CGI->param('note')   || '';
+ my $serial = $CGI->param('serial') || '';
+ 
+ #   sanity checks on CGI parameters
+ if ($name !~ m|\.mct$|) {
+     die "MCT specification \"$name\": invalid path name";
+ }
+ if (not -f $name) {
+     die "MCT specification \"$name\": file not found";
+ }
+ if ($perm ne '' and $perm !~ m/^[1-9]$/) {
+     die "Invalid \"perm\" parameter value \"$perm\"";
+ }
+ if ($hint ne '' and $hint !~ m/^[01]$/) {
+     die "Invalid \"hint\" parameter value \"$hint\"";
+ }
+ if ($note ne '' and $note !~ m/^[01]$/) {
+     die "Invalid \"note\" parameter value \"$note\"";
+ }
+ if ($serial ne '' and $serial !~ m/^\d+$/) {
+     die "Invalid \"serial\" parameter value \"$serial\"";
+ }
+ 
+ #   read MCT specification
+ my $MCT = &readmct($name);
+ 
+ #  XXX
+ 
+ #   optionally read HTML page template
+ my $template = $name;
+ $template =~ s|\.mct$|.html|s;
+ my $fh = new IO::File "<$template" || die;
+ $MY->{TEMPLATE} = '';
+ $MY->{TEMPLATE} .= $_ while (<$fh>);
+ $fh->close();
+ 
+ #   make sure the serial numbers match
+ if ($serial ne '') {
+     if ($serial ne $MCT->{HEADER}->{SERIAL}) {
+         die "Sorry, serial in request (#$serial) does not match current MCT serial (#".$MCT->{HEADER}->{SERIAL}.")";
+     }
+ }
+ $serial = $MCT->{HEADER}->{SERIAL};
+ 
+ #   calculate query summary
+ my $summary = '';
+ sub summarynum {
+     my ($num) = @_;
+     return $num if ($num < 10);
+     return "{$num}";
+ }
+ my $nq = 0;
+ foreach my $q (@{$MCT->{QUESTION}}) {
+     $summary .= "," if ($summary ne '');
+     $summary .= &summarynum($nq);
+     $question = $CGI->param("q$nq");
+     my $nc = 0;
+     foreach my $c (@{$q->{CHOICES}}) {
+         my $choice = ($CGI->param("q$nq.c$nc") || ($question eq "c$nc"));
+         if ($choice) {
+             $summary .= &summarynum($nc);
+         }
+         $nc++;
+     }
+     $nq++;
+ }
+ 
+ #   support Submit button
+ if ($form eq 'Send Notes') {
+     my $body = '';
+     $body .= "Dear author of the Multiple Choice Test \"".$MCT->{HEADER}->{NAME}."\",\n" .
+              "my current choice (".$CGI->url(-full => 1)."?choice=$summary)\n" .
+              "resulted in the following annotation notes for you.\n" .
+              "\n";
+     $nq = 0;
+     foreach my $q (@{$MCT->{QUESTION}}) {
+         my $an = ($CGI->param("q${nq}.an") || "");
+         if ($an ne '') {
+             $body .= "Question:\n" .
+                      "| ".$q->{TITLE}."\n";
+             $an =~ s/^/| /mg;
+             $body .= "Note:\n".$an;
+             $body .= "\n\n";
+         }
+         $CGI->param("q${nq}.an", "");
+         $nq++;
+     }
+     my %mailcfg = ( 
+         'To'      => $MCT->{HEADER}->{"REPLY-TO"},
+         'From'    => $MY->{MAIL_FROM},
+         'Subject' => "Notes on MCT \"".$MCT->{HEADER}->{NAME}."\"",
+         'Message' => $body,
+         'smtp'    => $MY->{MAIL_HOST}
+     );
+     sendmail(%mailcfg) or die $Mail::Sendmail::error;
+     $CGI->param('note', 0);
+     $note = 0;
+     $form = "  Rate!  ";
+ }
+ 
+ #   support Mail button
+ if ($form eq 'Mail') {
+     sub urlescape {
+         my ($text) = @_;
+         $text =~ s|([ \t&+?:/=\n\r])|sprintf("%%%02x", ord($1))|sge;
+         return $text;
+     }
+     my $subject = &urlescape("Result in MCT \"".$MCT->{HEADER}->{NAME}."\"");
+     my $body    = &urlescape("Result: ".$CGI->url(-full => 1)."?choice=$summary");
+     print STDOUT $CGI->redirect(-uri => "mailto:".$MCT->{HEADER}->{"REPLY-TO"}."?subject=$subject&body=$body", -type => "text/plain");
+     exit(0);
+ }
+ 
+ #   support Restart button by redirecting via GET 
+ if ($form eq 'Restart') {
+     print STDOUT $CGI->redirect(-uri => $CGI->url(-full => 1), -type => "text/html");
+     exit(0);
+ }
+ 
+ #   support Note button
+ if ($form eq 'Show Notes') {
+     $CGI->param('note', 1);
+     $note = 1;
+ }
+ elsif ($form eq 'Hide Notes') {
+     $CGI->param('note', 0);
+     $note = 0;
+ }
+ 
+ #   support Hint button
+ if ($form eq 'Show Hints') {
+     $CGI->param('hint', 1);
+     $hint = 1;
+ }
+ elsif ($form eq 'Hide Hints') {
+     $CGI->param('hint', 0);
+     $hint = 0;
+ }
+ 
+ #   calculate new permution index
+ if ($perm eq '') {
+     $perm = int((rand 8) + 1);;
+     $CGI->param('perm', $perm);
+ }
+ 
+ #   support Bookmark button
+ if ($form eq 'Bookmark') {
+     #   redirect to bookmark URL
+     # XXX print STDOUT $CGI->redirect($CGI->url(-full => 1)."?name=$name&perm=$perm&choice=$summary");
+     print STDOUT $CGI->redirect(-uri => $CGI->url(-full => 1)."?serial=$serial&perm=$perm&choice=$summary", -type => "text/plain");
+     exit(0);
+ }
+ if ($CGI->param('choice') ne '') {
+     #   import choice from bookmark URL
+     my $choice = $CGI->param('choice');
+     sub nextnum {
+         my ($str) = @_;
+         my $num = '';
+         if ($str =~ m|^\{(\d+)\}(.*)$|s) {
+             ($num, $str) = ($1, $2);
+         }
+         elsif ($str =~ m|^(\d)(.*)$|s) {
+             ($num, $str) = ($1, $2);
+         }
+         elsif ($str =~ m|^.(.*)$|s) {
+             $str = $1;
+         }
+         return ($num, $str);
+     }
+     foreach $str (split(/,/, $choice)) {
+         my ($nq, $str) = &nextnum($str);
+         if ($nq ne '') {
+             if (defined($MCT->{QUESTION}->[$nq])) {
+                 $q = $MCT->{QUESTION}->[$nq];
+                 my $multichoice = 0;
+                 foreach my $c (@{$q->{CHOICES}}) {
+                     if ($c->{TYPE} eq '++') {
+                         $multichoice++;
+                     }
+                 }
+                 $multichoice = 0 if ($multichoice <= 1);
+                 if ($multichoice) {
+                     my $nc;
+                     while ($str ne '') {
+                         ($nc, $str) = &nextnum($str);
+                         $CGI->param("q$nq.c$nc", "1");
+                     }
+                 }
+                 else {
+                     my ($nc, $str) = &nextnum($str);
+                     $CGI->param("q$nq", "c$nc");
+                 }
+             }
+         }
+     }
+     $form = "  Rate!  ";
+ }
+ 
+ #   start generating the web page
+ my $head = '';
+ my $body = '';
+ 
+ $head .= "<title>ePaperwork :: Multiple Choice Test: ".
+          $MCT->{HEADER}->{TITLE}."</title>";
+ 
+ #   import CSS
+ sub addcss {
+     my ($file) = @_;
+     $head .= "<style type=\"text/css\"><!--\n";
+     my $fh = new IO::File "<$file" || die;
+     $head .= $_ while (<$fh>);
+     $fh->close();
+     $head .= "--></style>\n";
+ }
+ my $css = $name;
+ $css =~ s|\.mct$|.css|s;
+ if (-f "$css") {
+     &addcss("$css");
+ }
+ if (-f "mct.css") {
+     &addcss("mct.css");
+ }
+ 
+ $body .= "<table width=100% cellspacing=0 cellpadding=1 border=0>";
+ $body .= "<tr><td colspan=2 class=mct-border>";
+ # XXX $body .= $CGI->startform(-method => "POST", -action => $CGI->url(-full => 1)."?name=$name");
+ $body .= $CGI->startform(-method => "POST", -action => $CGI->url(-full => 1));
+ #$body .= $CGI->hidden(-name => 'name', -default => [$name]);
+ $body .= $CGI->hidden(-name => 'perm', -default => [$perm]);
+ $body .= $CGI->hidden(-name => 'hint', -default => [$hint]);
+ $body .= $CGI->hidden(-name => 'note', -default => [$note]);
+ $body .= $CGI->hidden(-name => 'serial', -default => [$serial]);
+ 
+ $body .= "<table width=100% cellspacing=0 cellpadding=4 border=0>";
+ $body .= "<tr><td colspan=2 class=\"mct-header\">\n";
+ $body .= "<blockquote>\n";
+ $body .= &sug2html($MCT->{HEADER}->{TITLE});
+ $body .= "</blockquote>\n";
+ $body .= "</td></tr>";
+ 
+ #   determine whether to enable cheat mode
+ my $cheat = 0;
+ if ($MCT->{HEADER}->{PASSWORD} ne '') {
+     $nq = 0;
+     foreach my $q (@{$MCT->{QUESTION}}) {
+         my $an = ($CGI->param("q${nq}.an") || "");
+         if (index($an, $MCT->{HEADER}->{PASSWORD}) > -1) {
+             $cheat = 1;
+             last;
+         }
+         $nq++;
+     }
+ }
+ 
+ my $r_questions = 0;
+ my $r_score     = 0;
+ 
+ $nq = 0;
+ foreach my $q (@{$MCT->{QUESTION}}) {
+        $r_questions++;
+        my $col0 = ($nq % 2);
+        $body .= "<tr>\n";
+        $body .= "<td width=40% valign=top class=mct-qt$col0>" .
+                         "<span class=\"mct-qt\">".&sug2html($q->{TITLE})."</span>";
+     my $multichoice = 0;
+        foreach my $c (@{$q->{CHOICES}}) {
+         if ($c->{TYPE} eq '++') {
+             $multichoice++;
+         }
+     }
+     if ($multichoice <= 1) {
+         $multichoice = 0;
+     }
+     if ($multichoice) {
+         $body .= "<br>($multichoice answers expected)";
+     }
+     if ($hint) {
+         if ($q->{HINT} ne '') {
+             $body .= "<br><span class=\"mct-hint\">Hint: ".&sug2html($q->{HINT})."</span>\n";
+         }
+     }
+     if ($note) {
+         $body .= "<br>" . 
+                  $CGI->textarea(-name => "q${nq}.an",
+                               -default => ($CGI->param("q${nq}.an") || ""), 
+                               -rows => 3, -columns => "30%");
+     }
+     else {
+         $body .= $CGI->hidden(-name => "q${nq}.an", -default => [($CGI->param("q${nq}.an") || "")]);
+     }
+     $body .= "<p></td>\n";
+        $body .= "<td width=60% valign=top class=mct-qt$col0>" .
+                         "<span class=\"mct-qc\">\n";
+        $body .= "<table width=100% cellspacing=0 cellpadding=0 border=0>\n";
+        my $nc = 0;
+        my $q_choices = 0;
+        my $q_score   = 0;
+        my $q_ok      = 0;
+     my @cells = ();
+        foreach my $c (@{$q->{CHOICES}}) {
+            $q_choices++;
+                my $choice = $CGI->param("q$nq.c$nc");
+         if (not $multichoice) {
+                    $choice = $CGI->param("q$nq");
+             if ($choice eq "c$nc") {
+                 $choice = 1;
+             }
+             else {
+                 $choice = 0;
+             }
+         }
+         my $class = '';
+                if ($choice) {
+                        if ($c->{TYPE} eq '++') {
+                                $class = "OK";
+                                $q_score++;
+                        }
+                        else {
+                                $class = "BAD";
+                                $q_score--;
+                        }
+                }
+         else {
+                        if ($c->{TYPE} eq '++') {
+                                $class = "CHEAT" if ($form ne '' && $cheat);
+             }
+         }
+                if ($c->{TYPE} eq '++') {
+                        $q_ok++;
+                }
+         my $cell = '';
+         if ($multichoice) {
+                    $cell .= "<input ".($choice ? "checked " : "").
+                      "type=\"checkbox\" name=\"q$nq.c$nc\" value=\"1\">";
+         }
+         else {
+                    $cell .= "<input ".($choice ? "checked " : "").
+                      "type=\"radio\" name=\"q$nq\" value=\"c$nc\">";
+         }
+                $cell .= "&nbsp;".&sug2html($c->{TEXT})."<br>\n";
+         my $c = {
+             'CLASS'  => $class,
+             'HTML'   => $cell,
+             'LENGTH' => length($c->{TEXT})
+         };
+         push(@cells, $c);
+                $nc++;
+        }
+ 
+     @cells = &permute($perm, @cells);
+ 
+     my $maxlen  = 0;
+     foreach my $cell (@cells) {
+         if ($maxlen < $cell->{LENGTH}) {
+             $maxlen = $cell->{LENGTH};
+         }
+     }
+     my $cols = 1;
+     $cols = 2 if ($maxlen < 30);
+     $cols = 3 if ($maxlen < 10);
+     $cols = 4 if ($maxlen < 5);
+     my $rows = sprintf("%d", (($#cells+1) + ($cols - 1)) / $cols);
+     for (my $i = 0; $i < $rows; $i++) {
+         $body .= "<tr>\n";
+         for (my $j = 0; $j < $cols; $j++) {
+             my $cell = ($cells[$i+($j*$rows)] || { 'CLASS' => '', 'HTML' => '&nbsp;'});
+             my $class = $cell->{CLASS};
+             if ($class eq '' or $class eq 'OK') {
+                        my $col1 = ($i % 2);
+                        $class = "$col0$col1";
+             }
+                    $body .= "<td class=mct-qc$class>";
+             $body .= $cell->{HTML};
+                    $body .= "</td>";
+         }
+         $body .= "</tr>\n";
+     }
+        $q_score = 0 if ($q_score < 0);
+        my $q_score = sprintf("%d", (($q_score / $q_ok) * 100));
+        $r_score += $q_score;
+        $body .= "</table>\n";
+        $body .= "</span><p></td>\n";
+        $body .= "</tr>\n";
+        $nq++;
+ }
+ $body .= "</table>\n";
+ my $result = sprintf("%d", ($r_score / $r_questions));
+ 
+ $body .= "</td></tr>";
+ $body .= "<tr>";
+ $body .= "<td align=left class=\"mct-button\">";
+ if ($form ne '') {
+     if ($note) {
+         $body .= "<span class=\"mct-button-switchnotes\">";
+         $body .= $CGI->submit(-name => 'form', -value => "Hide Notes") . "\n";
+         $body .= "</span>";
+         $body .= "<span class=\"mct-button-sendnotes\">";
+         $body .= $CGI->submit(-name => 'form', -value => "Send Notes") . "\n";
+         $body .= "</span>";
+     }
+     else {
+         $body .= "<span class=\"mct-button-switchnotes\">";
+         $body .= $CGI->submit(-name => 'form', -value => "Show Notes") . "\n";
+         $body .= "</span>";
+     }
+     if ($hint) {
+         $body .= "<span class=\"mct-button-hints\">";
+         $body .= $CGI->submit(-name => 'form', -value => "Hide Hints") . "\n";
+         $body .= "</span>";
+     }
+     else {
+         $body .= "<span class=\"mct-button-hints\">";
+         $body .= $CGI->submit(-name => 'form', -value => "Show Hints") . "\n";
+         $body .= "</span>";
+     }
+     $body .= "<span class=\"mct-button-bookmark\">";
+     $body .= $CGI->submit(-name => 'form', -value => "Bookmark") . "\n";
+     $body .= "</span>";
+     $body .= "<span class=\"mct-button-mail\">" .
+              $CGI->submit(-name => 'form', -value => "Mail") . 
+              "</span>\n";
+ }
+ $body .= '&nbsp;';
+ $body .= "</td>";
+ $body .= "<td align=right class=\"mct-button\">";
+ if ($form ne '') {
+     $body .= "<span class=\"mct-button-restart\">" .
+              $CGI->submit(-name => 'form', -value => "Restart") .
+              "</span>\n";
+ }
+ $body .= "<span class=\"mct-button-undo\">" .
+          $CGI->reset(-value => "Undo") . 
+          "</span>\n";
+ $body .= "<b><span class=\"mct-button-rate\">" . $CGI->submit(-name => 'form', -value => "  Rate!  ") . "</span></b>\n";
+ #$body .= "&nbsp;";
+ $body .= $CGI->endform;
+ $body .= "</td>";
+ $body .= "</tr>";
+ if ($form ne '') {
+        #   display results
+        $body .= "<tr>";
+        $body .= "<td colspan=2 align=left class=\"mct-result\">";
+        $body .= "<p><blockquote>";
+        $body .= "You have achieved <span class=\"mct-result-percent\">${result}%</span> success in this multiple choice test.\n";
+     foreach my $r (@{$MCT->{RESULT}}) {
+            $body .= $r->{TITLE} . " ";
+            foreach my $c (@{$r->{CHOICES}}) {
+                        if ($c->{RANGE}->[0] <= $result and $result <= $c->{RANGE}->[1]) {
+                                $body .= &sug2html($c->{TEXT});
+                                last;
+                        }
+                }
+                $body .= "\n";
+        }
+        $body .= "<br><br></blockquote>";
+        $body .= "</td></tr>";
+ }
+ $body .= "<tr>";
+ $body .= "<td colspan=2 align=center class=\"mct-footer\">";
+ $body .= "<a href=\"".$MY->{PROG_HOME}."\">".$MY->{PROG_NAME}."</a> ".
+          $MY->{PROG_VERS}." - ".$MY->{PROG_DESC}."<br>\n";
+ $body .= "</td>";
+ $body .= "</tr>";
+ $body .= "</table>\n";
+ $body .= "\n";
+ 
+ print STDOUT &http_response($head, $body);
+ 
+ exit(0);
+ 


ossp-pkg/mct/mct.css -> 1.1

*** /dev/null    Sun Apr 28 21:22:11 2024
--- -    Sun Apr 28 21:26:04 2024
***************
*** 0 ****
--- 1,103 ----
+ /*
+ **  OSSP mct - Multiple Choice Test
+ **  Copyright (c) 2002 Ralf S. Engelschall <rse@engelschall.com>
+ **  Copyright (c) 2002 The OSSP Project <http://www.ossp.org/>
+ **  Copyright (c) 2002 Cable & Wireless Deutschland <http://www.cw.com/de/>
+ **
+ **  This file is part of OSSP mct, a multiple choice test engine
+ **  which can be found at http://www.ossp.org/pkg/tool/mct/.
+ **
+ **  Permission to use, copy, modify, and distribute this software for
+ **  any purpose with or without fee is hereby granted, provided that
+ **  the above copyright notice and this permission notice appear in all
+ **  copies.
+ **
+ **  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ **  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ **  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ **  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+ **  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ **  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ **  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ **  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ **  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ **  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ **  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ **  SUCH DAMAGE.
+ **
+ **  mct.css: multiple choice test cascading style sheet (language: CSS1)
+ */
+ 
+ /* the outer border */
+ TD.mct-border                  { background: #000000; }
+ 
+ /* the header area */
+ .mct-header                    { background: #000000; color: #ffffff; 
+                                  font-family: helvetica,lucida,arial,sans-serif; font-weight: bold; font-size: 150%; }
+ .mct-header A                  { text-decoration: none; font-weight: bold; }
+ .mct-header A:link             { color: #ffffe0; }
+ .mct-header A:visited          { color: #ffffe0; }
+ .mct-header A:hover            { color: #ffffe0; text-decoration: underline; }
+ 
+ /* the hint texts */
+ .mct-hint                      { color: #333366;
+                                  font-family: helvetica,lucida,arial,sans-serif; font-weight: bold; }
+ .mct-hint A                    { text-decoration: none; font-weight: bold; }
+ .mct-hint A:link               { color: #666699; }
+ .mct-hint A:visited            { color: #666699; }
+ .mct-hint A:hover              { color: #666699; text-decoration: underline; }
+ 
+ /* the question area */
+ .mct-qt                        { font-family: helvetica,lucida,arial,sans-serif; font-size: 125%; }
+ .mct-qt CODE                   { font-family: courier,courier-new,terminal,fixed,monospace; }
+ .mct-qt A                      { text-decoration: none; }
+ .mct-qt A:link                 { color: #666699; }
+ .mct-qt A:visited              { color: #666699; }
+ .mct-qt A:hover                { color: #666699; text-decoration: underline; }
+ 
+ /* the choice area */
+ .mct-qc                        { font-family: helvetica,lucida,arial,sans-serif; }
+ .mct-qc A                      { text-decoration: none; }
+ .mct-qc A:link                 { color: #666699; }
+ .mct-qc A:visited              { color: #666699; }
+ .mct-qc A:hover                { color: #666699; text-decoration: underline; }
+ 
+ /* the question and choice area background/foreground colors */
+ .mct-qt0                       { background: #f0f0f0; }
+ .mct-qt1                       { background: #e0e0f0; }
+ .mct-qc00                      { background: #f0f0f0; }
+ .mct-qc01                      { background: #f8f8f8; }
+ .mct-qc10                      { background: #e0e0f0; }
+ .mct-qc11                      { background: #e8e8f8; }
+ .mct-qcBAD                     { background: #cc3333; color: #ffffff; }
+ .mct-qcCHEAT                   { background: #339933; color: #ffffff; }
+ 
+ /* the action button area */
+ .mct-button                    { background: #000000; }
+ .mct-button-switchnotes  INPUT { background: #666666; border-color: #666666; color: #cccccc; } 
+ .mct-button-sendnotes    INPUT { background: #666666; border-color: #666666; color: #cccccc; } 
+ .mct-button-hints        INPUT { background: #666666; border-color: #666666; color: #cccccc; } 
+ .mct-button-bookmark     INPUT { background: #666666; border-color: #666666; color: #cccccc; } 
+ .mct-button-mail         INPUT { background: #666666; border-color: #666666; color: #cccccc; }
+ .mct-button-restart      INPUT { background: #666666; border-color: #666666; color: #cccccc; } 
+ .mct-button-undo         INPUT { background: #666666; border-color: #666666; color: #cccccc; }
+ .mct-button-rate         INPUT { background: #c0c0f0; border-color: #c0c0f0; color: #000000; }
+ 
+ /* the result text area */
+ .mct-result                    { background: #000000; color: #ffffff; 
+                                  font-family: helvetica,lucida,arial,sans-serif; 
+                                  font-size: 150%; }
+ .mct-result-percent            { color: #ccccff; font-weight: bold; }
+ .mct-result A                  { text-decoration: none; font-weight: bold; }
+ .mct-result A:link             { color: #ffffe0; }
+ .mct-result A:visited          { color: #ffffe0; }
+ .mct-result A:hover            { color: #ffffe0; text-decoration: underline; }
+ 
+ /* the footer area (program name and version) */
+ .mct-footer                    { background: #000000; color: #c0c0c0;
+                                  font-family: helvetica,lucida,arial,sans-serif; font-weight: normal; }
+ .mct-footer A                  { text-decoration: none; }
+ .mct-footer A:hover            { text-decoration: underline; }
+ .mct-footer A:link             { color: #c0c0c0; }
+ .mct-footer A:visited          { color: #c0c0c0; }
+ 


ossp-pkg/mct/mct.html -> 1.1

*** /dev/null    Sun Apr 28 21:22:11 2024
--- -    Sun Apr 28 21:26:04 2024
***************
*** 0 ****
--- 1,8 ----
+ <html>
+   <head>
+        @HEAD@
+   </head>
+   <body bgcolor="#ffffff">
+     @BODY@
+   </body>
+ </html>


ossp-pkg/mct/rsa.html -> 1.1

*** /dev/null    Sun Apr 28 21:22:11 2024
--- -    Sun Apr 28 21:26:04 2024
***************
*** 0 ****
--- 1,8 ----
+ <html>
+   <head>
+        @HEAD@
+   </head>
+   <body bgcolor="#ffffff">
+     @BODY@
+   </body>
+ </html>


ossp-pkg/mct/rsa.mct -> 1.1

*** /dev/null    Sun Apr 28 21:22:11 2024
--- -    Sun Apr 28 21:26:04 2024
***************
*** 0 ****
--- 1,279 ----
+ Name:      rsa
+ Title:     RSA Public Key Algorithm
+ Author:    ->Ralf S. Engelschall::mailto:rse@engelschall.com<-
+ Reply-To:  rse@engelschall.com
+ Created:   11-May-2002
+ Modified:  11-May-2002
+ Serial:    1
+ Password:  CheatMe!
+ 
+ ?? What does **RSA** stand for? 
+ !! presentation, ->slide 2::rsa/slide-002-n.html<-
+ -- Ronald's Security Algorithm
+ -- Ronald Shane Adleman
+ ++ Rivest Shamir Adleman
+ -- Remainder Secret Algorithm
+ -- Remaining Security Advocation
+ -- Rivest Shamir Algorithm
+ 
+ ?? RSA is a...
+ !! presentation, ->slide 2::rsa/slide-002-n.html<-, ->slide 4::rsa/slide-004-n.html<-
+ ++ asymmetric cryptography algorithm
+ -- symmetric cryptography algorithm
+ -- elliptic curve cryptography algorithm
+ -- private key cryptography algorithm
+ 
+ ?? The RSA algorithm provides...
+ !! presentation, ->slide 2::rsa/slide-002-n.html<-
+ ++ Encryption
+ ++ Digital Signatures
+ ++ Secure Key Exchange
+ -- Steganography
+ -- Data Mining
+ -- Zero Knowledge 
+ -- Message Digesting
+ -- Electronic Cash
+ 
+ ?? RSA is one of the cryptrography algorithm underlying...
+ !! presentation, ->slide 3::rsa/slide-003-n.html<-
+ ++ SSL/TLS
+ ++ SSH
+ ++ IPsec
+ ++ DNSSEC
+ ++ PGP
+ -- DNS
+ ++ SMIME
+ -- MIME
+ -- HTTP
+ ++ HTTPS
+ -- FTP
+ ++ SFTP
+ -- IMAP
+ ++ IMAPS
+ -- POP3
+ 
+ ?? RSA is **mainly** based on mathematical foundations from...
+ !! presentation, ->slide 3::rsa/slide-003-n.html<-
+ -- function theory
+ -- coding theory
+ ++ algebra
+ ++ number theory
+ -- analysis
+ -- geometry
+ 
+ ?? The major drawback in symmetric cryptography is that the...
+ !! presentation, ->slide 4::rsa/slide-004-n.html<-
+ ++ key has to be securely interchanged before communication
+ -- key cannot be made as secure as needed
+ -- algorithms are not as secure as the asymmetric ones
+ -- algorithms can be implemented in hardware too easily
+ 
+ ?? Ciphers provide...
+ !! presentation, ->slide 4::rsa/slide-004-n.html<-
+ ++ privacy
+ -- integrity
+ -- authentication
+ -- invisibility
+ 
+ ?? Message digests provide...
+ !! presentation, ->slide 5::rsa/slide-005-n.html<-
+ -- privacy
+ ++ integrity
+ -- authentication
+ -- invisibility
+ 
+ ?? Certificates provide...
+ !! presentation, ->slide 6::rsa/slide-006-n.html<-
+ -- privacy
+ -- integrity
+ ++ authentication
+ -- invisibility
+ 
+ ?? Which of the following are **symmetric** cryptography algorithms?
+ !! presentation, ->slide 4::rsa/slide-004-n.html<-
+ ++ DES
+ ++ AES
+ ++ Blowfish
+ ++ RC4
+ ++ IDEA
+ -- DH
+ -- ElGammal
+ -- RSA
+ -- MD5
+ -- SHA1
+ -- RMD160
+ -- CRC
+ 
+ ?? Which of the following are **asymmetric** cryptography algorithms?
+ !! presentation, ->slide 4::rsa/slide-004-n.html<-
+ -- DES
+ -- AES
+ -- Blowfish
+ -- RC4
+ -- IDEA
+ ++ DH
+ ++ ElGammal
+ ++ RSA
+ -- MD5
+ -- SHA1
+ -- RMD160
+ -- CRC
+ 
+ ?? Which of the following are **cryptographically-strong** message digest algorithms?
+ !! presentation, ->slide 5::rsa/slide-005-n.html<-
+ -- DES
+ -- AES
+ -- Blowfish
+ -- RC4
+ -- IDEA
+ -- DH
+ -- ElGammal
+ -- RSA
+ ++ MD5
+ ++ SHA1
+ ++ RMD160
+ -- CRC
+ 
+ ?? Message digests generate for long variable-length messages...
+ !! presentation, ->slide 5::rsa/slide-005-n.html<-
+ -- long fixed-length representations
+ -- long variable-length representations
+ ++ short fixed-length representations
+ -- short variable-length representations
+ 
+ ?? Message digests for two different messages...
+ !! presentation, ->slide 5::rsa/slide-005-n.html<-
+ -- are equal
+ -- should be equal
+ -- are different
+ ++ should be different
+ 
+ ?? Digital certificates associate...
+ !! presentation, ->slide 6::rsa/slide-006-n.html<-
+ ++ a public key with an identity
+ -- a private key with an identity
+ -- a public key with a private key
+ 
+ ?? "x mod n = y" (x, y, n, k: all integers greater than 0), is equivalent with
+ !! presentation, ->slide 8::rsa/slide-008-n.html<-
+ ++ x = k*n + y
+ -- x = k/n + x
+ -- n = x/y
+ -- n = x*y
+ 
+ ?? Primes are numbers which...
+ !! presentation, ->slide 8::rsa/slide-008-n.html<-
+ -- if multiplied with theirself result in 1
+ ++ are without remainder only divisible by theirself and 1 
+ ++ have 1 as the greatest common divisor with all lower numbers
+ -- can be divided by theirself with a remainder of 1
+ 
+ ?? The primary mathematical foundation for the possibility that \
+    the encryption of RSA can be decrypted is the theorem of...
+ !! presentation, ->slide 9::rsa/slide-009-n.html<-
+ -- Gauß
+ ++ Euler/Fermat
+ -- Cauchy
+ -- Riemann
+ -- Rivest
+ -- Shamir
+ -- Adleman
+ -- Rivest/Shamir/Adleman
+ 
+ ?? Encrypting message M with RSA public key {e,n} (n modulus) into cipher text \
+    C basically means calculating
+ !! presentation, ->slide 10::rsa/slide-010-n.html<-
+ ++ C = M^e mod n
+ -- M = C^e mod n
+ -- C = M^n mod e
+ -- M = C^n mod e
+ -- e = M*C^2
+ -- C = M mod n^e
+ 
+ ?? Decrypting cipher text C with RSA private key {d,n} (n modulus) into message \
+    M basically means calculating
+ !! presentation, ->slide 10::rsa/slide-010-n.html<-
+ -- C = M^d mod n
+ ++ M = C^d mod n
+ -- C = M^n mod e
+ -- M = C^n mod e
+ -- d = M*C^2
+ -- M = C mod n^d
+ 
+ ?? In RSA the two involved keys are named the...
+ !! presentation, ->slide 11::rsa/slide-011-n.html<-
+ ++ public key
+ ++ private key
+ -- secret key
+ -- unsecure key
+ -- secure key
+ -- protected key
+ 
+ ?? The RSA private key is...
+ !! presentation, ->slide 11::rsa/slide-011-n.html<-
+ ++ technically fully interchangeable with the public key
+ ++ private by definition only, not by mathematical constraints 
+ -- private by mathematical constraints, not by definition
+ 
+ ?? **Very large** random prime numbers are efficiently determined by...
+ !! vocal explanations while presentation
+ -- deterministic calculation
+ ++ repeated guessing and testing
+ -- looking up in a table
+ 
+ ?? For applying RSA to encrypt arbitrary texts, one has to...
+ !! presentation, ->slide 13::rsa/slide-013-n.html<-
+ ++ treat character blocks as a large integer numbers
+ -- encrypt every single text character individually
+ 
+ ?? The security of RSA lies in the fact that...
+ !! presentation, ->slide 13::rsa/slide-013-n.html<-
+ ++ no efficient algorithm is known for factorization of numbers
+ -- no efficient algorithm is known for calculating discrete logarithm
+ -- the remaining algorithm details are kept secret by RSA, Inc.
+ -- the algorithm is proofed to be unbreakable
+ -- the algorithm is definetely secure by design
+ 
+ ?? RSA, compared to the speed of DES/AES, is...
+ !! presentation, ->slide 13::rsa/slide-013-n.html<-
+ -- dramatically faster
+ -- slightly faster
+ -- approximately equal
+ -- slightly slower
+ ++ dramatically slower
+ -- not comparable
+ 
+ ?? When implementing RSA, the **main** problems are that you need...
+ !! presentation, ->slide 13::rsa/slide-013-n.html<-
+ ++ arbitrary precision arithmetic
+ ++ good pseudo-random number generator
+ ++ efficient primaly test
+ -- fast hardware
+ -- secure memory
+ -- assembly language
+ 
+ ?? There patent on RSA...
+ !! presentation, ->slide 2::rsa/slide-002-n.html<-
+ -- expired in 1978
+ ++ started in 1978
+ ++ expired in 2000
+ -- started in 2000
+ -- expires in 2038
+ -- starts in 2038
+ 
+ == The analysis of your answers indicates that
+ -- 000-009 you have still not understood RSA at all. Sorry.
+ -- 010-029 you have understood only a few points about RSA. Hmmm..
+ -- 030-049 you have understood a fair amount about RSA. Go ahead.
+ -- 050-079 you have understood a lot about RSA, but failed a few points. Ok.
+ -- 080-089 you have understood mostly all about RSA. Great!
+ -- 090-100 you have fully understood RSA. Congratulations! Wohooo!
+ 
+ == We have to conclude that you are
+ -- 000-009 sleeping while presentations are held. **:-(**
+ -- 010-029 still not familiar enough with cryptography. **:-|**
+ -- 030-049 a cryptography newbie with at least increasing potential. **:-)**
+ -- 050-079 a cryptography fan with an already large potential. **:-)**
+ -- 080-089 a cryptography enthusiast. Just a few steps missing to a guru. **;-)**
+ -- 090-100 a cryptography guru already shaking hands with Ron, Adi and Len. **;-)**
+ 

CVSTrac 2.0.1