| … | |
… | |
| 9 | use warnings; |
9 | use warnings; |
| 10 | use Opcode; |
10 | use Opcode; |
| 11 | use Safe; |
11 | use Safe; |
| 12 | use Net::SMTP; |
12 | use Net::SMTP; |
| 13 | use WeBWorK::PG::IO; |
13 | use WeBWorK::PG::IO; |
|
|
14 | |
|
|
15 | #use PadWalker; # used for processing error messages |
|
|
16 | #use Data::Dumper; |
| 14 | |
17 | |
| 15 | |
18 | |
| 16 | # loading GD within the Safe compartment has occasionally caused infinite recursion |
19 | # loading GD within the Safe compartment has occasionally caused infinite recursion |
| 17 | # Putting these use statements here seems to avoid this problem |
20 | # Putting these use statements here seems to avoid this problem |
| 18 | # It is not clear that this is essential once things are working properly. |
21 | # It is not clear that this is essential once things are working properly. |
| … | |
… | |
| 387 | # |
390 | # |
| 388 | # PG.pl, IO.pl and dangerousMacros.pl are loaded without restriction |
391 | # PG.pl, IO.pl and dangerousMacros.pl are loaded without restriction |
| 389 | # All other files are loaded with restriction |
392 | # All other files are loaded with restriction |
| 390 | # |
393 | # |
| 391 | # construct a regex that matches only these three files safely |
394 | # construct a regex that matches only these three files safely |
| 392 | my @unrestricted_files = qw/PG.pl dangerousMacros.pl IO.pl/; |
395 | my @unrestricted_files = (); # no longer needed? FIXME w/PG.pl dangerousMacros.pl IO.pl/; |
| 393 | my $unrestricted_files = join("|", map { quotemeta } @unrestricted_files); |
396 | my $unrestricted_files = join("|", map { quotemeta } @unrestricted_files); |
| 394 | |
397 | |
| 395 | my $store_mask; |
398 | my $store_mask; |
| 396 | if ($fileName =~ /^($unrestricted_files)$/) { |
399 | if ($fileName =~ /^($unrestricted_files)$/) { |
| 397 | $store_mask = $cached_safe_cmpt->mask(); |
400 | $store_mask = $cached_safe_cmpt->mask(); |
| … | |
… | |
| 534 | my $safe_cmpt_package_name = $safe_cmpt->root(); |
537 | my $safe_cmpt_package_name = $safe_cmpt->root(); |
| 535 | |
538 | |
| 536 | my $macro_file_name = fileFromPath($filePath); |
539 | my $macro_file_name = fileFromPath($filePath); |
| 537 | $macro_file_name =~s/\.pl//; # trim off the extenstion |
540 | $macro_file_name =~s/\.pl//; # trim off the extenstion |
| 538 | my $export_subroutine_name = "_${macro_file_name}_export"; |
541 | my $export_subroutine_name = "_${macro_file_name}_export"; |
| 539 | my $init_subroutine_name = "_${macro_file_name}_init"; |
542 | my $init_subroutine_name = "${safe_cmpt_package_name}::_${macro_file_name}_init"; |
|
|
543 | |
| 540 | my $local_errors = ""; |
544 | my $local_errors = ""; |
| 541 | no strict; |
545 | no strict; |
| 542 | # warn "dangerousMacros main:: contains <br>\n ".join("<br>\n ", %main::) if $debugON; |
546 | # warn "dangerousMacros main:: contains <br>\n ".join("<br>\n ", %main::) if $debugON; |
| 543 | my $init_subroutine = eval { \&{"${safe_cmpt_package_name}::$init_subroutine_name"} }; |
547 | my $init_subroutine = eval { \&{$init_subroutine_name} }; |
|
|
548 | warn "No init routine for $init_subroutine_name: $@" if $debugON and $@; |
| 544 | use strict; |
549 | use strict; |
| 545 | my $macro_file_loaded = defined(&$init_subroutine); |
550 | my $macro_file_loaded = ref($init_subroutine) =~ /CODE/; |
| 546 | |
551 | |
| 547 | #print STDERR "$macro_file_name has not yet been loaded\n" unless $macro_file_loaded; |
552 | #print STDERR "$macro_file_name has not yet been loaded\n" unless $macro_file_loaded; |
| 548 | unless ($macro_file_loaded) { |
553 | unless ($macro_file_loaded) { |
| 549 | ## load the $filePath file |
554 | ## load the $filePath file |
| 550 | ## Using rdo insures that the $filePath file is loaded for every problem, allowing initializations to occur. |
555 | ## Using rdo insures that the $filePath file is loaded for every problem, allowing initializations to occur. |
| 551 | ## Ordinary mortals should not be fooling with the fundamental macros in these files. |
556 | ## Ordinary mortals should not be fooling with the fundamental macros in these files. |
| 552 | my $local_errors = ""; |
557 | my $local_errors = ""; |
| 553 | if (-r $filePath ) { |
558 | if (-r $filePath ) { |
| 554 | my $rdoResult = $safe_cmpt->rdo($filePath); |
559 | my $rdoResult = $safe_cmpt->rdo($filePath); |
| 555 | #warn "There were problems compiling the file: $filePath: <BR>--$@" if $@; |
560 | #warn "unrestricted load: $filePath\n"; |
| 556 | $local_errors ="\nThere were problems compiling the file:\n $filePath\n $@\n" if $@; |
561 | $local_errors ="\nThere were problems compiling the file:\n $filePath\n $@\n" if $@; |
| 557 | $self ->{errors} .= $local_errors if $local_errors; |
562 | $self ->{errors} .= $local_errors if $local_errors; |
| 558 | use strict; |
563 | use strict; |
| 559 | } else { |
564 | } else { |
| 560 | $local_errors = "Can't open file $filePath for reading\n"; |
565 | $local_errors = "Can't open file $filePath for reading\n"; |
| … | |
… | |
| 562 | } |
567 | } |
| 563 | $safe_cmpt -> mask($store_mask); |
568 | $safe_cmpt -> mask($store_mask); |
| 564 | |
569 | |
| 565 | } |
570 | } |
| 566 | # try again to define the initization subroutine |
571 | # try again to define the initization subroutine |
| 567 | $init_subroutine = eval { \&{"${safe_cmpt_package_name}::$init_subroutine_name"} }; |
572 | $init_subroutine = eval { \&{"$init_subroutine_name"} }; |
| 568 | $macro_file_loaded = defined(&$init_subroutine ); |
573 | $macro_file_loaded = ref($init_subroutine) =~ /CODE/; |
| 569 | if ( $macro_file_loaded ) { |
574 | if ( $macro_file_loaded ) { |
| 570 | |
575 | |
| 571 | # warn "unrestricted load: initializing $macro_file_name $init_subroutine" ; |
576 | #warn "unrestricted load: initializing $macro_file_name $init_subroutine" ; |
| 572 | &$init_subroutine(); |
577 | &$init_subroutine(); |
| 573 | } |
578 | } |
| 574 | $local_errors .= "\nUnknown error. Unable to load $filePath\n" if ($local_errors eq '' and not $macro_file_loaded); |
579 | $local_errors .= "\nUnknown error. Unable to load $filePath\n" if ($local_errors eq '' and not $macro_file_loaded); |
| 575 | #print STDERR "$filePath is properly loaded\n\n" if $macro_file_loaded; |
580 | #print STDERR "$filePath is properly loaded\n\n" if $macro_file_loaded; |
| 576 | $local_errors; |
581 | $local_errors; |
| … | |
… | |
| 737 | the PG root directory by [PG]. |
742 | the PG root directory by [PG]. |
| 738 | |
743 | |
| 739 | =cut |
744 | =cut |
| 740 | |
745 | |
| 741 | sub PG_errorMessage { |
746 | sub PG_errorMessage { |
| 742 | my $return = shift; my $frame = 2; |
747 | my $return = shift; my $frame = 2; # return can be 'message' or 'traceback' |
| 743 | my $message = join("\n",@_); $message =~ s/\.?\s+$//; |
748 | my $message = join("\n",@_); $message =~ s/\.?\s+$//; |
| 744 | my $files = eval ('$main::__files__'); $files = {} unless $files; |
749 | my $files = eval ('$main::__files__'); $files = {} unless $files; |
| 745 | my $tmpl = $files->{tmpl} || '$'; |
750 | my $tmpl = $files->{tmpl} || '$'; |
| 746 | my $root = $files->{root} || '$'; |
751 | my $root = $files->{root} || '$'; |
| 747 | my $pg = $files->{pg} || '$'; |
752 | my $pg = $files->{pg} || '$'; |
| … | |
… | |
| 781 | # report the full traceback |
786 | # report the full traceback |
| 782 | # |
787 | # |
| 783 | return join("\n",@trace,''); |
788 | return join("\n",@trace,''); |
| 784 | } |
789 | } |
| 785 | |
790 | |
|
|
791 | =head2 PG_undef_var_check |
|
|
792 | |
|
|
793 | =pod |
|
|
794 | |
|
|
795 | Produces warnings of this type in order to help you guess which local variable is undefined |
|
|
796 | Warning: Use of uninitialized value in concatenation (.) or string at mpu.cgi line 25. |
|
|
797 | Possible variables are: |
|
|
798 | '$GLOBAL_VARIABLE' => \'global', |
|
|
799 | '$t' => \undef, |
|
|
800 | '$s' => \'regular output' |
|
|
801 | |
|
|
802 | |
|
|
803 | |
|
|
804 | |
|
|
805 | =cut |
|
|
806 | |
|
|
807 | sub PG_undef_var_check { |
|
|
808 | if($_[0] !~ /^Use of uninitialized value/) { |
|
|
809 | return @_; |
|
|
810 | } else { |
|
|
811 | # If there are objects, the output can be VERY large when you increase this |
|
|
812 | local $Data::Dumper::Maxdepth = 2; |
|
|
813 | # takes all lexical variables from caller-nemaspace |
|
|
814 | my $possibles = Data::Dumper::Dumper({ %{PadWalker::peek_my(1)}, %{PadWalker::peek_our(1)} }); |
|
|
815 | |
|
|
816 | $possibles ne "\$VAR1 = {};\n" ? ($possibles =~ s/^.*?\n(.*)\n.*?\n$/$1/ms) : ($possibles = ''); |
|
|
817 | return "Warning: " . join(', ', @_) . "Possible variables are:\n$possibles\n"; |
|
|
818 | } |
|
|
819 | |
|
|
820 | } |
| 786 | ############################################################################ |
821 | ############################################################################ |
| 787 | |
822 | |
| 788 | =head2 Translate |
823 | =head2 Translate |
| 789 | |
824 | |
| 790 | |
825 | |
| … | |
… | |
| 908 | |
943 | |
| 909 | =cut |
944 | =cut |
| 910 | |
945 | |
| 911 | my ($PG_PROBLEM_TEXT_REF, $PG_HEADER_TEXT_REF, $PG_ANSWER_HASH_REF, $PG_FLAGS_REF) |
946 | my ($PG_PROBLEM_TEXT_REF, $PG_HEADER_TEXT_REF, $PG_ANSWER_HASH_REF, $PG_FLAGS_REF) |
| 912 | =$safe_cmpt->reval(" $evalString"); |
947 | =$safe_cmpt->reval(" $evalString"); |
| 913 | |
948 | #warn "using safe compartment ", $safe_cmpt->root; |
| 914 | # This section could use some more error messages. In particular if a problem doesn't produce the right output, the user needs |
949 | # This section could use some more error messages. In particular if a problem doesn't produce the right output, the user needs |
| 915 | # information about which problem was at fault. |
950 | # information about which problem was at fault. |
| 916 | # |
951 | # |
| 917 | # |
952 | # |
| 918 | |
953 | |
| … | |
… | |
| 1148 | $temp_ans = '' unless defined($temp_ans); #make sure that answer is always defined |
1183 | $temp_ans = '' unless defined($temp_ans); #make sure that answer is always defined |
| 1149 | # in case the answer evaluator forgets to check |
1184 | # in case the answer evaluator forgets to check |
| 1150 | $self->{safe}->share('$rf_fun','$temp_ans'); |
1185 | $self->{safe}->share('$rf_fun','$temp_ans'); |
| 1151 | |
1186 | |
| 1152 | # clear %errorTable for each problem |
1187 | # clear %errorTable for each problem |
| 1153 | %errorTable = (); |
1188 | %errorTable = (); # is the error table being used? perhaps by math objects? |
| 1154 | |
1189 | |
| 1155 | my $rh_ans_evaluation_result; |
1190 | my $rh_ans_evaluation_result; |
| 1156 | if (ref($rf_fun) eq 'CODE' ) { |
1191 | if (ref($rf_fun) eq 'CODE' ) { |
| 1157 | $rh_ans_evaluation_result = $self->{safe} ->reval( '&{ $rf_fun }($temp_ans, ans_label => \''.$ans_name.'\')' ) ; |
1192 | $rh_ans_evaluation_result = $self->{safe} ->reval( '&{ $rf_fun }($temp_ans, ans_label => \''.$ans_name.'\')' ) ; |
| 1158 | warn "Error in Translator.pm::process_answers: Answer $ans_name: |$temp_ans|\n $@\n" if $@; |
1193 | warn "Error in Translator.pm::process_answers: Answer $ans_name: |$temp_ans|\n $@\n" if $@; |
| 1159 | } elsif (ref($rf_fun) =~ /AnswerEvaluator/) { |
1194 | } elsif (ref($rf_fun) =~ /AnswerEvaluator/) { |
| 1160 | $rh_ans_evaluation_result = $self->{safe} ->reval('$rf_fun->evaluate($temp_ans, ans_label => \''.$ans_name.'\')'); |
1195 | $rh_ans_evaluation_result = $self->{safe} ->reval('$rf_fun->evaluate($temp_ans, ans_label => \''.$ans_name.'\')'); |
| 1161 | $@ = $errorTable{$@} if $@ && defined($errorTable{$@}); |
1196 | $@ = $errorTable{$@} if $@ && defined($errorTable{$@}); #Are we redefining error messages here? |
| 1162 | warn "Error in Translator.pm::process_answers: Answer $ans_name: |$temp_ans|\n $@\n" if $@; |
1197 | warn "Error in Translator.pm::process_answers: Answer $ans_name: |$temp_ans|\n $@\n" if $@; |
| 1163 | warn "Evaluation error: Answer $ans_name:<BR>\n", |
1198 | warn "Evaluation error: Answer $ans_name:<BR>\n", |
| 1164 | $rh_ans_evaluation_result->error_flag(), " :: ", |
1199 | $rh_ans_evaluation_result->error_flag(), " :: ", |
| 1165 | $rh_ans_evaluation_result->error_message(),"<BR>\n" |
1200 | $rh_ans_evaluation_result->error_message(),"<BR>\n" |
| 1166 | if defined($rh_ans_evaluation_result) |
1201 | if defined($rh_ans_evaluation_result) |
| … | |
… | |
| 1560 | |
1595 | |
| 1561 | local $SIG{__WARN__} = "DEFAULT"; |
1596 | local $SIG{__WARN__} = "DEFAULT"; |
| 1562 | local $SIG{__DIE__} = "DEFAULT"; |
1597 | local $SIG{__DIE__} = "DEFAULT"; |
| 1563 | |
1598 | |
| 1564 | no strict; |
1599 | no strict; |
| 1565 | my $out = eval ("package main; be_strict();" . $string ); |
1600 | my $out = eval ("package main; be_strict();\n" . $string ); |
| 1566 | my $errors =$@; |
1601 | my $errors =$@; |
| 1567 | my $full_error_report = "PG_macro_file_eval detected error at line $line of file $file \n" |
1602 | my $full_error_report = "PG_macro_file_eval detected error at line $line of file $file \n" |
| 1568 | . $errors . |
1603 | . $errors . |
| 1569 | "The calling package is $pck\n" if defined($errors) && $errors =~/\S/; |
1604 | "The calling package is $pck\n" if defined($errors) && $errors =~/\S/; |
| 1570 | use strict; |
1605 | use strict; |