--- loncom/Lond.pm 2012/04/11 21:32:28 1.1
+++ loncom/Lond.pm 2018/12/10 18:56:18 1.13
@@ -1,6 +1,6 @@
# The LearningOnline Network
#
-# $Id: Lond.pm,v 1.1 2012/04/11 21:32:28 droeschl Exp $
+# $Id: Lond.pm,v 1.13 2018/12/10 18:56:18 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -27,6 +27,7 @@
###
#NOTE perldoc at the end of file
+#TODO move remaining lond functions into this
package LONCAPA::Lond;
@@ -36,76 +37,64 @@ use lib '/home/httpd/lib/perl/';
use LONCAPA;
use Apache::lonnet;
use GDBM_File;
-
+use Crypt::OpenSSL::X509;
+use Crypt::PKCS10;
sub dump_with_regexp {
- #TODO encapsulate $clientname and $clientversion in a object.
- my ( $cmd, $tail, $clientname, $clientversion ) = @_;
+ my ( $tail, $clientversion ) = @_;
+ my ( $udom, $uname, $namespace, $regexp, $range ) =
+ split /:/, $tail;
- my $userinput = "$cmd:$tail";
+ $regexp = $regexp ? unescape($regexp) : '.';
- my ($udom,$uname,$namespace,$regexp,$range,$extra)=split(/:/,$tail);
- if (defined($regexp)) {
- $regexp=&unescape($regexp);
- } else {
- $regexp='.';
- }
my ($start,$end);
+
if (defined($range)) {
- if ($range =~/^(\d+)\-(\d+)$/) {
- ($start,$end) = ($1,$2);
- } elsif ($range =~/^(\d+)$/) {
- ($start,$end) = (0,$1);
- } else {
- undef($range);
- }
+ if ($range =~ /^(\d+)\-(\d+)$/) {
+ ($start,$end) = ($1,$2);
+ } elsif ($range =~/^(\d+)$/) {
+ ($start,$end) = (0,$1);
+ } else {
+ undef($range);
+ }
}
- Apache::lonnet::logthis("Lond.pm: udom:[$udom] uname:[$uname] namespace:[$namespace]");
- my $hashref = &tie_user_hash($udom, $uname, $namespace,
- &GDBM_READER());
- my $skipcheck;
- if ($hashref) {
- my $qresult='';
- my $count=0;
+
+ my $hashref = &tie_user_hash($udom, $uname, $namespace, &GDBM_READER()) or
+ return "error: ".($!+0)." tie(GDBM) Failed while attempting dump";
+
+ my $qresult = '';
+ my $count = 0;
#
# When dump is for roles.db, determine if LON-CAPA version checking is needed.
-# Sessions on 2.10 and later will include skipcheck => 1 in extra args ref,
-# to indicate no version checking is needed (in this case, checking occurs
+# Sessions on 2.10 and later do not require version checking, as that occurs
# on the server hosting the user session, when constructing the roles/courses
# screen).
#
- if ($extra ne '') {
- $extra = &Apache::lonnet::thaw_unescape($extra);
- $skipcheck = $extra->{'skipcheck'};
- }
- my @ids = &Apache::lonnet::current_machine_ids();
- my (%homecourses,$major,$minor,$now);
+ my $skipcheck;
+ my @ids = &Apache::lonnet::current_machine_ids();
+ my (%homecourses, $major, $minor, $now);
#
# If dump is for roles.db from a pre-2.10 server, determine the LON-CAPA
-# version on the server which requested the data. For LON-CAPA 2.9, the
-# client session will have sent its LON-CAPA version when initiating the
-# connection. For LON-CAPA 2.8 and older, the version is retrieved from
-# the global %loncaparevs in lonnet.pm.
+# version on the server which requested the data.
#
- if (($namespace eq 'roles') && (!$skipcheck)) {
- my $loncaparev = $clientversion;
- if ($loncaparev eq '') {
- $loncaparev = $Apache::lonnet::loncaparevs{$clientname};
- }
- if ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?/) {
- $major = $1;
- $minor = $2;
- }
- $now = time;
+ if ($namespace eq 'roles') {
+ if ($clientversion =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?/) {
+ $major = $1;
+ $minor = $2;
+
}
- while (my ($key,$value) = each(%$hashref)) {
- if ($namespace eq 'roles') {
+ if (($major > 2) || (($major == 2) && ($minor > 9))) {
+ $skipcheck = 1;
+ }
+ $now = time;
+ }
+ while (my ($key,$value) = each(%$hashref)) {
+ if ($namespace eq 'roles' && (!$skipcheck)) {
if ($key =~ m{^/($LONCAPA::match_domain)/($LONCAPA::match_courseid)(/?[^_]*)_(cc|co|in|ta|ep|ad|st|cr)$}) {
my $cdom = $1;
my $cnum = $2;
- unless ($skipcheck) {
- my ($role,$roleend,$rolestart) = split(/\_/,$value);
- if (!$roleend || $roleend > $now) {
+ my ($role,$roleend,$rolestart) = split(/\_/,$value);
+ if (!$roleend || $roleend > $now) {
#
# For active course roles, check that requesting server is running a LON-CAPA
# version which meets any version requirements for the course. Do not include
@@ -116,28 +105,29 @@ sub dump_with_regexp {
# homeserver is the current server, or whether it is a different server.
# In both cases, the course's version requirement needs to be retrieved.
#
- next unless (&releasereqd_check($cnum,$cdom,$key,$value,$major,
- $minor,\%homecourses,\@ids));
- }
+ next unless (&releasereqd_check($cnum,$cdom,$key,$value,$major,
+ $minor,\%homecourses,\@ids));
}
}
}
- if ($regexp eq '.') {
- $count++;
- if (defined($range) && $count >= $end) { last; }
- if (defined($range) && $count < $start) { next; }
- $qresult.=$key.'='.$value.'&';
- } else {
- my $unescapeKey = &unescape($key);
- if (eval('$unescapeKey=~/$regexp/')) {
- $count++;
- if (defined($range) && $count >= $end) { last; }
- if (defined($range) && $count < $start) { next; }
- $qresult.="$key=$value&";
- }
- }
- }
- if (&untie_user_hash($hashref)) {
+ if ($regexp eq '.') {
+ $count++;
+ if (defined($range) && $count >= $end) { last; }
+ if (defined($range) && $count < $start) { next; }
+ $qresult.=$key.'='.$value.'&';
+ } else {
+ my $unescapeKey = &unescape($key);
+ if (eval('$unescapeKey=~/$regexp/')) {
+ $count++;
+ if (defined($range) && $count >= $end) { last; }
+ if (defined($range) && $count < $start) { next; }
+ $qresult.="$key=$value&";
+ }
+ }
+ }
+
+ &untie_user_hash($hashref) or
+ return "error: ".($!+0)." untie(GDBM) Failed while attempting dump";
#
# If dump is for roles.db from a pre-2.10 server, check if the LON-CAPA
# version requirements for courses for which the current server is the home
@@ -145,32 +135,837 @@ sub dump_with_regexp {
# user's session. If so, include those role results in the data returned to
# the client server.
#
- if (($namespace eq 'roles') && (!$skipcheck)) {
- if (keys(%homecourses) > 0) {
- $qresult .= &check_homecourses(\%homecourses,$regexp,$count,
- $range,$start,$end,$major,$minor);
- }
+ if (($namespace eq 'roles') && (!$skipcheck)) {
+ if (keys(%homecourses) > 0) {
+ $qresult .= &check_homecourses(\%homecourses,$regexp,$count,
+ $range,$start,$end,$major,$minor);
+ }
+ }
+ chop($qresult);
+ return $qresult;
+}
+
+
+sub releasereqd_check {
+ my ($cnum,$cdom,$key,$value,$major,$minor,$homecourses,$ids) = @_;
+ my $home = &Apache::lonnet::homeserver($cnum,$cdom);
+ return if ($home eq 'no_host');
+ my ($reqdmajor,$reqdminor,$displayrole);
+ if ($cnum =~ /$LONCAPA::match_community/) {
+ if ($major eq '' && $minor eq '') {
+ return unless ((ref($ids) eq 'ARRAY') &&
+ (grep(/^\Q$home\E$/,@{$ids})));
+ } else {
+ $reqdmajor = 2;
+ $reqdminor = 9;
+ return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
+ }
+ }
+ my $hashid = $cdom.':'.$cnum;
+ my ($courseinfo,$cached) =
+ &Apache::lonnet::is_cached_new('courseinfo',$hashid);
+ if (defined($cached)) {
+ if (ref($courseinfo) eq 'HASH') {
+ if (exists($courseinfo->{'releaserequired'})) {
+ my ($reqdmajor,$reqdminor) = split(/\./,$courseinfo->{'releaserequired'});
+ return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
}
- chop($qresult);
- Apache::lonnet::logthis("Lond.pm: qresult:[$qresult]");
- return $qresult;
- #&Reply($client, \$qresult, $userinput);
- } else {
- return "error: ".($!+0)." untie(GDBM) Failed while attempting dump";
- #&Failure( $client, "error: ".($!+0)." untie(GDBM) Failed ".
- # "while attempting dump\n", $userinput);
- }
+ }
} else {
- return "error: ".($!+0)." tie(GDBM) Failed while attempting dump";
- #&Failure($client, "error: ".($!+0)." tie(GDBM) Failed ".
- # "while attempting dump\n", $userinput);
+ if (ref($ids) eq 'ARRAY') {
+ if (grep(/^\Q$home\E$/,@{$ids})) {
+ if (ref($homecourses) eq 'HASH') {
+ if (ref($homecourses->{$cdom}) eq 'HASH') {
+ if (ref($homecourses->{$cdom}{$cnum}) eq 'HASH') {
+ if (ref($homecourses->{$cdom}{$cnum}) eq 'ARRAY') {
+ push(@{$homecourses->{$cdom}{$cnum}},{$key=>$value});
+ } else {
+ $homecourses->{$cdom}{$cnum} = [{$key=>$value}];
+ }
+ } else {
+ $homecourses->{$cdom}{$cnum} = [{$key=>$value}];
+ }
+ } else {
+ $homecourses->{$cdom}{$cnum} = [{$key=>$value}];
+ }
+ }
+ return;
+ }
+ }
+ my $courseinfo = &get_courseinfo_hash($cnum,$cdom,$home);
+ if (ref($courseinfo) eq 'HASH') {
+ if (exists($courseinfo->{'releaserequired'})) {
+ my ($reqdmajor,$reqdminor) = split(/\./,$courseinfo->{'releaserequired'});
+ return unless (&useable_role($reqdmajor,$reqdminor,$major,$minor));
+ }
+ } else {
+ return;
+ }
+ }
+ return 1;
+}
+
+
+sub check_homecourses {
+ my ($homecourses,$regexp,$count,$range,$start,$end,$major,$minor) = @_;
+ my ($result,%addtocache);
+ my $yesterday = time - 24*3600;
+ if (ref($homecourses) eq 'HASH') {
+ my (%okcourses,%courseinfo,%recent);
+ foreach my $domain (keys(%{$homecourses})) {
+ my $hashref =
+ &tie_domain_hash($domain, "nohist_courseids", &GDBM_WRCREAT());
+ if (ref($hashref) eq 'HASH') {
+ while (my ($key,$value) = each(%$hashref)) {
+ my $unesc_key = &unescape($key);
+ if ($unesc_key =~ /^lasttime:(\w+)$/) {
+ my $cid = $1;
+ $cid =~ s/_/:/;
+ if ($value > $yesterday ) {
+ $recent{$cid} = 1;
+ }
+ next;
+ }
+ my $items = &Apache::lonnet::thaw_unescape($value);
+ if (ref($items) eq 'HASH') {
+ my ($cdom,$cnum) = split(/_/,$unesc_key);
+ my $hashid = $cdom.':'.$cnum;
+ $courseinfo{$hashid} = $items;
+ if (ref($homecourses->{$cdom}{$cnum}) eq 'ARRAY') {
+ my ($reqdmajor,$reqdminor) = split(/\./,$items->{'releaserequired'});
+ if (&useable_role($reqdmajor,$reqdminor,$major,$minor)) {
+ $okcourses{$hashid} = 1;
+ }
+ }
+ }
+ }
+ unless (&untie_domain_hash($hashref)) {
+ &logthis("Failed to untie tied hash for nohist_courseids.db for $domain");
+ }
+ } else {
+ &logthis("Failed to tie hash for nohist_courseids.db for $domain");
+ }
+ }
+ foreach my $hashid (keys(%recent)) {
+ my ($result,$cached)=&Apache::lonnet::is_cached_new('courseinfo',$hashid);
+ unless ($cached) {
+ &Apache::lonnet::do_cache_new('courseinfo',$hashid,$courseinfo{$hashid},600);
+ }
+ }
+ foreach my $cdom (keys(%{$homecourses})) {
+ if (ref($homecourses->{$cdom}) eq 'HASH') {
+ foreach my $cnum (keys(%{$homecourses->{$cdom}})) {
+ my $hashid = $cdom.':'.$cnum;
+ next if ($recent{$hashid});
+ &Apache::lonnet::do_cache_new('courseinfo',$hashid,$courseinfo{$hashid},600);
+ }
+ }
+ }
+ foreach my $hashid (keys(%okcourses)) {
+ my ($cdom,$cnum) = split(/:/,$hashid);
+ if ((ref($homecourses->{$cdom}) eq 'HASH') &&
+ (ref($homecourses->{$cdom}{$cnum}) eq 'ARRAY')) {
+ foreach my $role (@{$homecourses->{$cdom}{$cnum}}) {
+ if (ref($role) eq 'HASH') {
+ while (my ($key,$value) = each(%{$role})) {
+ if ($regexp eq '.') {
+ $count++;
+ if (defined($range) && $count >= $end) { last; }
+ if (defined($range) && $count < $start) { next; }
+ $result.=$key.'='.$value.'&';
+ } else {
+ my $unescapeKey = &unescape($key);
+ if (eval('$unescapeKey=~/$regexp/')) {
+ $count++;
+ if (defined($range) && $count >= $end) { last; }
+ if (defined($range) && $count < $start) { next; }
+ $result.="$key=$value&";
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
+ return $result;
+}
+
- #never get here
- die("SHOULD NOT HAPPEN!");
+sub useable_role {
+ my ($reqdmajor,$reqdminor,$major,$minor) = @_;
+ if ($reqdmajor ne '' && $reqdminor ne '') {
+ return if (($major eq '' && $minor eq '') ||
+ ($major < $reqdmajor) ||
+ (($major == $reqdmajor) && ($minor < $reqdminor)));
+ }
return 1;
}
+
+sub get_courseinfo_hash {
+ my ($cnum,$cdom,$home) = @_;
+ my %info;
+ eval {
+ local($SIG{ALRM}) = sub { die "timeout\n"; };
+ local($SIG{__DIE__})='DEFAULT';
+ alarm(3);
+ %info = &Apache::lonnet::courseiddump($cdom,'.',1,'.','.',$cnum,1,[$home],'.');
+ alarm(0);
+ };
+ if ($@) {
+ if ($@ eq "timeout\n") {
+ &logthis("WARNING courseiddump for $cnum:$cdom from $home timedout");
+ } else {
+ &logthis("WARNING unexpected error during eval of call for courseiddump from $home");
+ }
+ } else {
+ if (ref($info{$cdom.'_'.$cnum}) eq 'HASH') {
+ my $hashid = $cdom.':'.$cnum;
+ return &Apache::lonnet::do_cache_new('courseinfo',$hashid,$info{$cdom.'_'.$cnum},600);
+ }
+ }
+ return;
+}
+
+sub dump_course_id_handler {
+ my ($tail) = @_;
+
+ my ($udom,$since,$description,$instcodefilter,$ownerfilter,$coursefilter,
+ $typefilter,$regexp_ok,$rtn_as_hash,$selfenrollonly,$catfilter,$showhidden,
+ $caller,$cloner,$cc_clone_list,$cloneonly,$createdbefore,$createdafter,
+ $creationcontext,$domcloner,$hasuniquecode,$reqcrsdom,$reqinstcode) = split(/:/,$tail);
+ my $now = time;
+ my ($cloneruname,$clonerudom,%cc_clone);
+ if (defined($description)) {
+ $description=&unescape($description);
+ } else {
+ $description='.';
+ }
+ if (defined($instcodefilter)) {
+ $instcodefilter=&unescape($instcodefilter);
+ } else {
+ $instcodefilter='.';
+ }
+ my ($ownerunamefilter,$ownerdomfilter);
+ if (defined($ownerfilter)) {
+ $ownerfilter=&unescape($ownerfilter);
+ if ($ownerfilter ne '.' && defined($ownerfilter)) {
+ if ($ownerfilter =~ /^([^:]*):([^:]*)$/) {
+ $ownerunamefilter = $1;
+ $ownerdomfilter = $2;
+ } else {
+ $ownerunamefilter = $ownerfilter;
+ $ownerdomfilter = '';
+ }
+ }
+ } else {
+ $ownerfilter='.';
+ }
+
+ if (defined($coursefilter)) {
+ $coursefilter=&unescape($coursefilter);
+ } else {
+ $coursefilter='.';
+ }
+ if (defined($typefilter)) {
+ $typefilter=&unescape($typefilter);
+ } else {
+ $typefilter='.';
+ }
+ if (defined($regexp_ok)) {
+ $regexp_ok=&unescape($regexp_ok);
+ }
+ if (defined($catfilter)) {
+ $catfilter=&unescape($catfilter);
+ }
+ if (defined($cloner)) {
+ $cloner = &unescape($cloner);
+ ($cloneruname,$clonerudom) = ($cloner =~ /^($LONCAPA::match_username):($LONCAPA::match_domain)$/);
+ }
+ if (defined($cc_clone_list)) {
+ $cc_clone_list = &unescape($cc_clone_list);
+ my @cc_cloners = split('&',$cc_clone_list);
+ foreach my $cid (@cc_cloners) {
+ my ($clonedom,$clonenum) = split(':',$cid);
+ next if ($clonedom ne $udom);
+ $cc_clone{$clonedom.'_'.$clonenum} = 1;
+ }
+ }
+ if ($createdbefore ne '') {
+ $createdbefore = &unescape($createdbefore);
+ } else {
+ $createdbefore = 0;
+ }
+ if ($createdafter ne '') {
+ $createdafter = &unescape($createdafter);
+ } else {
+ $createdafter = 0;
+ }
+ if ($creationcontext ne '') {
+ $creationcontext = &unescape($creationcontext);
+ } else {
+ $creationcontext = '.';
+ }
+ unless ($hasuniquecode) {
+ $hasuniquecode = '.';
+ }
+ if ($reqinstcode ne '') {
+ $reqinstcode = &unescape($reqinstcode);
+ }
+ my $unpack = 1;
+ if ($description eq '.' && $instcodefilter eq '.' && $ownerfilter eq '.' &&
+ $typefilter eq '.') {
+ $unpack = 0;
+ }
+ if (!defined($since)) { $since=0; }
+ my (%gotcodedefaults,%otcodedefaults);
+ my $qresult='';
+
+ my $hashref = &tie_domain_hash($udom, "nohist_courseids", &GDBM_WRCREAT())
+ or return "error: ".($!+0)." tie(GDBM) Failed while attempting courseiddump";
+
+ while (my ($key,$value) = each(%$hashref)) {
+ my ($unesc_key,$lasttime_key,$lasttime,$is_hash,%val,
+ %unesc_val,$selfenroll_end,$selfenroll_types,$created,
+ $context);
+ $unesc_key = &unescape($key);
+ if ($unesc_key =~ /^lasttime:/) {
+ next;
+ } else {
+ $lasttime_key = &escape('lasttime:'.$unesc_key);
+ }
+ if ($hashref->{$lasttime_key} ne '') {
+ $lasttime = $hashref->{$lasttime_key};
+ next if ($lasttime<$since);
+ }
+ my ($canclone,$valchange,$clonefromcode);
+ my $items = &Apache::lonnet::thaw_unescape($value);
+ if (ref($items) eq 'HASH') {
+ if ($hashref->{$lasttime_key} eq '') {
+ next if ($since > 1);
+ }
+ if ($items->{'inst_code'}) {
+ $clonefromcode = $items->{'inst_code'};
+ }
+ $is_hash = 1;
+ if ($domcloner) {
+ $canclone = 1;
+ } elsif (defined($clonerudom)) {
+ if ($items->{'cloners'}) {
+ my @cloneable = split(',',$items->{'cloners'});
+ if (@cloneable) {
+ if (grep(/^\*$/,@cloneable)) {
+ $canclone = 1;
+ } elsif (grep(/^\*:\Q$clonerudom\E$/,@cloneable)) {
+ $canclone = 1;
+ } elsif (grep(/^\Q$cloneruname\E:\Q$clonerudom\E$/,@cloneable)) {
+ $canclone = 1;
+ }
+ }
+ unless ($canclone) {
+ if ($cloneruname ne '' && $clonerudom ne '') {
+ if ($cc_clone{$unesc_key}) {
+ $canclone = 1;
+ $items->{'cloners'} .= ','.$cloneruname.':'.
+ $clonerudom;
+ $valchange = 1;
+ }
+ }
+ }
+ unless ($canclone) {
+ if (($reqcrsdom eq $udom) && ($reqinstcode) && ($clonefromcode)) {
+ if (grep(/\=/,@cloneable)) {
+ foreach my $cloner (@cloneable) {
+ if (($cloner ne '*') && ($cloner !~ /^\*\:$LONCAPA::match_domain$/) &&
+ ($cloner !~ /^$LONCAPA::match_username\:$LONCAPA::match_domain$/) && ($cloner ne '')) {
+ if ($cloner =~ /=/) {
+ my (%codedefaults,@code_order);
+ if (ref($gotcodedefaults{$udom}) eq 'HASH') {
+ if (ref($gotcodedefaults{$udom}{'defaults'}) eq 'HASH') {
+ %codedefaults = %{$gotcodedefaults{$udom}{'defaults'}};
+ }
+ if (ref($gotcodedefaults{$udom}{'order'}) eq 'ARRAY') {
+ @code_order = @{$gotcodedefaults{$udom}{'order'}};
+ }
+ } else {
+ &Apache::lonnet::auto_instcode_defaults($udom,
+ \%codedefaults,
+ \@code_order);
+ $gotcodedefaults{$udom}{'defaults'} = \%codedefaults;
+ $gotcodedefaults{$udom}{'order'} = \@code_order;
+ }
+ if (@code_order > 0) {
+ if (&Apache::lonnet::check_instcode_cloning(\%codedefaults,\@code_order,
+ $cloner,$clonefromcode,$reqinstcode)) {
+ $canclone = 1;
+ last;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } elsif (defined($cloneruname)) {
+ if ($cc_clone{$unesc_key}) {
+ $canclone = 1;
+ $items->{'cloners'} = $cloneruname.':'.$clonerudom;
+ $valchange = 1;
+ }
+ unless ($canclone) {
+ if ($items->{'owner'} =~ /:/) {
+ if ($items->{'owner'} eq $cloner) {
+ $canclone = 1;
+ }
+ } elsif ($cloner eq $items->{'owner'}.':'.$udom) {
+ $canclone = 1;
+ }
+ if ($canclone) {
+ $items->{'cloners'} = $cloneruname.':'.$clonerudom;
+ $valchange = 1;
+ }
+ }
+ }
+ unless (($canclone) || ($items->{'cloners'})) {
+ my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
+ if ($domdefs{'canclone'}) {
+ unless ($domdefs{'canclone'} eq 'none') {
+ if ($domdefs{'canclone'} eq 'domain') {
+ if ($clonerudom eq $udom) {
+ $canclone = 1;
+ }
+ } elsif (($clonefromcode) && ($reqinstcode) &&
+ ($udom eq $reqcrsdom)) {
+ if (&Apache::lonnet::default_instcode_cloning($udom,$domdefs{'canclone'},
+ $clonefromcode,$reqinstcode)) {
+ $canclone = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ if ($unpack || !$rtn_as_hash) {
+ $unesc_val{'descr'} = $items->{'description'};
+ $unesc_val{'inst_code'} = $items->{'inst_code'};
+ $unesc_val{'owner'} = $items->{'owner'};
+ $unesc_val{'type'} = $items->{'type'};
+ $unesc_val{'cloners'} = $items->{'cloners'};
+ $unesc_val{'created'} = $items->{'created'};
+ $unesc_val{'context'} = $items->{'context'};
+ }
+ $selfenroll_types = $items->{'selfenroll_types'};
+ $selfenroll_end = $items->{'selfenroll_end_date'};
+ $created = $items->{'created'};
+ $context = $items->{'context'};
+ if ($selfenrollonly) {
+ next if (!$selfenroll_types);
+ if (($selfenroll_end > 0) && ($selfenroll_end <= $now)) {
+ next;
+ }
+ }
+ if ($creationcontext ne '.') {
+ next if (($context ne '') && ($context ne $creationcontext));
+ }
+ if ($createdbefore > 0) {
+ next if (($created eq '') || ($created > $createdbefore));
+ }
+ if ($createdafter > 0) {
+ next if (($created eq '') || ($created <= $createdafter));
+ }
+ if ($catfilter ne '') {
+ next if ($items->{'categories'} eq '');
+ my @categories = split('&',$items->{'categories'});
+ next if (@categories == 0);
+ my @subcats = split('&',$catfilter);
+ my $matchcat = 0;
+ foreach my $cat (@categories) {
+ if (grep(/^\Q$cat\E$/,@subcats)) {
+ $matchcat = 1;
+ last;
+ }
+ }
+ next if (!$matchcat);
+ }
+ if ($caller eq 'coursecatalog') {
+ if ($items->{'hidefromcat'} eq 'yes') {
+ next if !$showhidden;
+ }
+ }
+ if ($hasuniquecode ne '.') {
+ next unless ($items->{'uniquecode'});
+ }
+ } else {
+ next if ($catfilter ne '');
+ next if ($selfenrollonly);
+ next if ($createdbefore || $createdafter);
+ next if ($creationcontext ne '.');
+ if ((defined($clonerudom)) && (defined($cloneruname))) {
+ if ($cc_clone{$unesc_key}) {
+ $canclone = 1;
+ $val{'cloners'} = &escape($cloneruname.':'.$clonerudom);
+ }
+ }
+ $is_hash = 0;
+ my @courseitems = split(/:/,$value);
+ $lasttime = pop(@courseitems);
+ if ($hashref->{$lasttime_key} eq '') {
+ next if ($lasttime<$since);
+ }
+ ($val{'descr'},$val{'inst_code'},$val{'owner'},$val{'type'}) = @courseitems;
+ }
+ if ($cloneonly) {
+ next unless ($canclone);
+ }
+ my $match = 1;
+ if ($description ne '.') {
+ if (!$is_hash) {
+ $unesc_val{'descr'} = &unescape($val{'descr'});
+ }
+ if (eval{$unesc_val{'descr'} !~ /\Q$description\E/i}) {
+ $match = 0;
+ }
+ }
+ if ($instcodefilter ne '.') {
+ if (!$is_hash) {
+ $unesc_val{'inst_code'} = &unescape($val{'inst_code'});
+ }
+ if ($regexp_ok == 1) {
+ if (eval{$unesc_val{'inst_code'} !~ /$instcodefilter/}) {
+ $match = 0;
+ }
+ } elsif ($regexp_ok == -1) {
+ if (eval{$unesc_val{'inst_code'} =~ /$instcodefilter/}) {
+ $match = 0;
+ }
+ } else {
+ if (eval{$unesc_val{'inst_code'} !~ /\Q$instcodefilter\E/i}) {
+ $match = 0;
+ }
+ }
+ }
+ if ($ownerfilter ne '.') {
+ if (!$is_hash) {
+ $unesc_val{'owner'} = &unescape($val{'owner'});
+ }
+ if (($ownerunamefilter ne '') && ($ownerdomfilter ne '')) {
+ if ($unesc_val{'owner'} =~ /:/) {
+ if (eval{$unesc_val{'owner'} !~
+ /\Q$ownerunamefilter\E:\Q$ownerdomfilter\E$/i}) {
+ $match = 0;
+ }
+ } else {
+ if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E/i}) {
+ $match = 0;
+ }
+ }
+ } elsif ($ownerunamefilter ne '') {
+ if ($unesc_val{'owner'} =~ /:/) {
+ if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E:[^:]+$/i}) {
+ $match = 0;
+ }
+ } else {
+ if (eval{$unesc_val{'owner'} !~ /\Q$ownerunamefilter\E/i}) {
+ $match = 0;
+ }
+ }
+ } elsif ($ownerdomfilter ne '') {
+ if ($unesc_val{'owner'} =~ /:/) {
+ if (eval{$unesc_val{'owner'} !~ /^[^:]+:\Q$ownerdomfilter\E/}) {
+ $match = 0;
+ }
+ } else {
+ if ($ownerdomfilter ne $udom) {
+ $match = 0;
+ }
+ }
+ }
+ }
+ if ($coursefilter ne '.') {
+ if (eval{$unesc_key !~ /^$udom(_)\Q$coursefilter\E$/}) {
+ $match = 0;
+ }
+ }
+ if ($typefilter ne '.') {
+ if (!$is_hash) {
+ $unesc_val{'type'} = &unescape($val{'type'});
+ }
+ if ($unesc_val{'type'} eq '') {
+ if ($typefilter ne 'Course') {
+ $match = 0;
+ }
+ } else {
+ if (eval{$unesc_val{'type'} !~ /^\Q$typefilter\E$/}) {
+ $match = 0;
+ }
+ }
+ }
+ if ($match == 1) {
+ if ($rtn_as_hash) {
+ if ($is_hash) {
+ if ($valchange) {
+ my $newvalue = &Apache::lonnet::freeze_escape($items);
+ $qresult.=$key.'='.$newvalue.'&';
+ } else {
+ $qresult.=$key.'='.$value.'&';
+ }
+ } else {
+ my %rtnhash = ( 'description' => &unescape($val{'descr'}),
+ 'inst_code' => &unescape($val{'inst_code'}),
+ 'owner' => &unescape($val{'owner'}),
+ 'type' => &unescape($val{'type'}),
+ 'cloners' => &unescape($val{'cloners'}),
+ );
+ my $items = &Apache::lonnet::freeze_escape(\%rtnhash);
+ $qresult.=$key.'='.$items.'&';
+ }
+ } else {
+ if ($is_hash) {
+ $qresult .= $key.'='.&escape($unesc_val{'descr'}).':'.
+ &escape($unesc_val{'inst_code'}).':'.
+ &escape($unesc_val{'owner'}).'&';
+ } else {
+ $qresult .= $key.'='.$val{'descr'}.':'.$val{'inst_code'}.
+ ':'.$val{'owner'}.'&';
+ }
+ }
+ }
+ }
+ &untie_domain_hash($hashref) or
+ return "error: ".($!+0)." untie(GDBM) Failed while attempting courseiddump";
+
+ chop($qresult);
+ return $qresult;
+}
+
+sub dump_profile_database {
+ my ($tail) = @_;
+
+ my ($udom,$uname,$namespace) = split(/:/,$tail);
+
+ my $hashref = &tie_user_hash($udom, $uname, $namespace, &GDBM_READER()) or
+ return "error: ".($!+0)." tie(GDBM) Failed while attempting currentdump";
+
+ # Structure of %data:
+ # $data{$symb}->{$parameter}=$value;
+ # $data{$symb}->{'v.'.$parameter}=$version;
+ # since $parameter will be unescaped, we do not
+ # have to worry about silly parameter names...
+
+ my $qresult='';
+ my %data = (); # A hash of anonymous hashes..
+ while (my ($key,$value) = each(%$hashref)) {
+ my ($v,$symb,$param) = split(/:/,$key);
+ next if ($v eq 'version' || $symb eq 'keys');
+ next if (exists($data{$symb}) &&
+ exists($data{$symb}->{$param}) &&
+ $data{$symb}->{'v.'.$param} > $v);
+ $data{$symb}->{$param}=$value;
+ $data{$symb}->{'v.'.$param}=$v;
+ }
+
+ &untie_user_hash($hashref) or
+ return "error: ".($!+0)." untie(GDBM) Failed while attempting currentdump";
+
+ while (my ($symb,$param_hash) = each(%data)) {
+ while(my ($param,$value) = each (%$param_hash)){
+ next if ($param =~ /^v\./); # Ignore versions...
+ #
+ # Just dump the symb=value pairs separated by &
+ #
+ $qresult.=$symb.':'.$param.'='.$value.'&';
+ }
+ }
+
+ chop($qresult);
+ return $qresult;
+}
+
+sub is_course {
+ my ($cdom,$cnum) = @_;
+
+ return unless (($cdom =~ /^$LONCAPA::match_domain$/) &&
+ ($cnum =~ /^$LONCAPA::match_courseid$/));
+ my $hashid = $cdom.':'.$cnum;
+ my ($iscourse,$cached) =
+ &Apache::lonnet::is_cached_new('iscourse',$hashid);
+ unless (defined($cached)) {
+ my $hashref =
+ &tie_domain_hash($cdom, "nohist_courseids", &GDBM_WRCREAT());
+ if (ref($hashref) eq 'HASH') {
+ my $esc_key = &escape($cdom.'_'.$cnum);
+ if (exists($hashref->{$esc_key})) {
+ $iscourse = 1;
+ } else {
+ $iscourse = 0;
+ }
+ &Apache::lonnet::do_cache_new('iscourse',$hashid,$iscourse,3600);
+ unless (&untie_domain_hash($hashref)) {
+ &logthis("Failed to untie tied hash for nohist_courseids.db for $cdom");
+ }
+ } else {
+ &logthis("Failed to tie hash for nohist_courseids.db for $cdom");
+ }
+ }
+ return $iscourse;
+}
+
+sub server_certs {
+ my ($perlvar,$lonhost,$hostname) = @_;
+ my %pemfiles = (
+ key => 'lonnetPrivateKey',
+ host => 'lonnetCertificate',
+ hostname => 'lonnetHostnameCertificate',
+ ca => 'lonnetCertificateAuthority',
+ );
+ my (%md5hash,%expected_cn,%expired,%revoked,%wrongcn,%info,$crlfile);
+ if (ref($perlvar) eq 'HASH') {
+ $expected_cn{'host'} = $Apache::lonnet::serverhomeIDs{$hostname};
+ $expected_cn{'hostname'} = 'internal-'.$hostname;
+ my $certsdir = $perlvar->{'lonCertificateDirectory'};
+ if (-d $certsdir) {
+ $crlfile = $certsdir.'/'.$perlvar->{'lonnetCertRevocationList'};
+ foreach my $key (keys(%pemfiles)) {
+ if ($perlvar->{$pemfiles{$key}}) {
+ my $file = $certsdir.'/'.$perlvar->{$pemfiles{$key}};
+ if (-e $file) {
+ if ($key eq 'key') {
+ if (open(PIPE,"openssl rsa -noout -in $file -check |")) {
+ my $check = ;
+ close(PIPE);
+ chomp($check);
+ $info{$key}{'status'} = $check;
+ }
+ if (open(PIPE,"openssl rsa -noout -modulus -in $file | openssl md5 |")) {
+ $md5hash{$key} = ;
+ close(PIPE);
+ chomp($md5hash{$key});
+ }
+ } else {
+ if ($key eq 'ca') {
+ if (open(PIPE,"openssl verify -CAfile $file $file |")) {
+ my $check = ;
+ close(PIPE);
+ chomp($check);
+ if ($check eq "$file: OK") {
+ $info{$key}{'status'} = 'ok';
+ } else {
+ $check =~ s/^\Q$file\E\:?\s*//;
+ $info{$key}{'status'} = $check;
+ }
+ }
+ } else {
+ if (open(PIPE,"openssl x509 -noout -modulus -in $file | openssl md5 |")) {
+ $md5hash{$key} = ;
+ close(PIPE);
+ chomp($md5hash{$key});
+ }
+ }
+ my $x509 = Crypt::OpenSSL::X509->new_from_file($file);
+ my @items = split(/,\s+/,$x509->subject());
+ foreach my $item (@items) {
+ my ($name,$value) = split(/=/,$item);
+ if ($name eq 'CN') {
+ $info{$key}{'cn'} = $value;
+ }
+ }
+ $info{$key}{'start'} = $x509->notBefore();
+ $info{$key}{'end'} = $x509->notAfter();
+ $info{$key}{'alg'} = $x509->sig_alg_name();
+ $info{$key}{'size'} = $x509->bit_length();
+ $info{$key}{'email'} = $x509->email();
+ $info{$key}{'serial'} = $x509->serial();
+ if ($x509->checkend(0)) {
+ $expired{$key} = 1;
+ }
+ if (($key eq 'host') || ($key eq 'hostname')) {
+ if ($info{$key}{'cn'} ne $expected_cn{$key}) {
+ $wrongcn{$key} = 1;
+ }
+ if ((-e $crlfile) && ($info{$key}{'serial'} =~ /^\w+$/)) {
+ my $serial = $info{$key}{'serial'};
+ if (open(PIPE,"openssl crl -inform PEM -text -in $crlfile | grep $serial |")) {
+ my $result = ;
+ close(PIPE);
+ chomp($result);
+ if ($result ne '') {
+ $revoked{$key} = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (($key eq 'host') || ($key eq 'hostname')) {
+ my $csrfile = $file;
+ $csrfile =~ s/\.pem$/.csr/;
+ if (-e $csrfile) {
+ if (open(PIPE,"openssl req -noout -modulus -in $csrfile |openssl md5 |")) {
+ my $csrhash = ;
+ close(PIPE);
+ chomp($csrhash);
+ if ((!-e $file) || ($csrhash ne $md5hash{$key}) || ($expired{$key}) ||
+ ($wrongcn{$key}) || ($revoked{$key})) {
+ Crypt::PKCS10->setAPIversion(1);
+ my $decoded = Crypt::PKCS10->new( $csrfile,(PEMonly => 1, readFile => 1));
+ if (ref($decoded)) {
+ if ($decoded->commonName() eq $expected_cn{$key}) {
+ $info{$key.'-csr'}{'cn'} = $decoded->commonName();
+ $info{$key.'-csr'}{'alg'} = $decoded->pkAlgorithm();
+ $info{$key.'-csr'}{'email'} = $decoded->emailAddress();
+ my $params = $decoded->subjectPublicKeyParams();
+ if (ref($params) eq 'HASH') {
+ $info{$key.'-csr'}{'size'} = $params->{keylen};
+ }
+ $md5hash{$key.'-csr'} = $csrhash;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ foreach my $key ('host','hostname') {
+ if ($md5hash{$key}) {
+ if ($md5hash{$key} eq $md5hash{'key'}) {
+ if ($revoked{$key}) {
+ $info{$key}{'status'} = 'revoked';
+ } elsif ($expired{$key}) {
+ $info{$key}{'status'} = 'expired';
+ } elsif ($wrongcn{$key}) {
+ $info{$key}{'status'} = 'wrongcn';
+ } else {
+ $info{$key}{'status'} = 'ok';
+ }
+ } elsif ($info{'key'}{'status'} =~ /ok/) {
+ $info{$key}{'status'} = 'otherkey';
+ } else {
+ $info{$key}{'status'} = 'nokey';
+ }
+ }
+ if ($md5hash{$key.'-csr'}) {
+ if ($md5hash{$key.'-csr'} eq $md5hash{'key'}) {
+ $info{$key.'-csr'}{'status'} = 'ok';
+ } elsif ($info{'key'}{'status'} =~ /ok/) {
+ $info{$key.'-csr'}{'status'} = 'otherkey';
+ } else {
+ $info{$key.'-csr'}{'status'} = 'nokey';
+ }
+ }
+ }
+ my $result;
+ foreach my $key (keys(%info)) {
+ $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($info{$key}).'&';
+ }
+ $result =~ s/\&$//;
+ return $result;
+}
+
1;
__END__
@@ -191,7 +986,7 @@ LONCAPA::Lond.pm
=over 4
-=item dump_with_regexp( $cmd, $tail, $client )
+=item dump_with_regexp( $tail, $client )
Dump a profile database with an optional regular expression to match against
the keys. In this dump, no effort is made to separate symb from version
@@ -199,8 +994,6 @@ information. Presumably the databases th
different structure. Need to look at this and improve the documentation of
both this and the currentdump handler.
-$cmd is the command keyword.
-
$tail a colon separated list containing
=over
@@ -224,10 +1017,6 @@ selective dumps.
optional range of entries e.g., 10-20 would return the 10th to 19th items, etc.
-=item extra
-
-optional ref to hash of additional args. currently skipcheck is only key used.
-
=back
$client is the channel open on the client.
@@ -236,6 +1025,71 @@ Returns: 1 (Continue processing).
Side effects: response is written to $client.
+=item dump_course_id_handler
+
+#TODO copy from lond
+
+=item dump_profile_database
+
+#TODO copy from lond
+
+=item releasereqd_check( $cnum, $cdom, $key, $value, $major, $minor,
+ $homecourses, $ids )
+
+releasereqd_check() will determine if a LON-CAPA version (defined in the
+$major,$minor args passed) is not too old to allow use of a role in a
+course ($cnum,$cdom args passed), if at least one of the following applies:
+(a) the course is a Community, (b) the course's home server is *not* the
+current server, or (c) cached course information is not stale.
+
+For the case where none of these apply, the course is added to the
+$homecourse hash ref (keys = courseIDs, values = array of a hash of roles).
+The $homecourse hash ref is for courses for which the current server is the
+home server. LON-CAPA version requirements are checked elsewhere for the
+items in $homecourse.
+
+
+=item check_homecourses( $homecourses, $regexp, $count, $range, $start, $end,
+ $major, $minor )
+
+check_homecourses() will retrieve course information for those courses which
+are keys of the $homecourses hash ref (first arg). The nohist_courseids.db
+GDBM file is tied and course information for each course retrieved. Last
+visit (lasttime key) is also retrieved for each, and cached values updated
+for any courses last visited less than 24 hours ago. Cached values are also
+updated for any courses included in the $homecourses hash ref.
+
+The reason for the 24 hours constraint is that the cron entry in
+/etc/cron.d/loncapa for /home/httpd/perl/refresh_courseids_db.pl causes
+cached course information to be updated nightly for courses with activity
+within the past 24 hours.
+
+Role information for the user (included in a ref to an array of hashes as the
+value for each key in $homecourses) is appended to the result returned by the
+routine, which will in turn be appended to the string returned to the client
+hosting the user's session.
+
+
+=item useable_role( $reqdmajor, $reqdminor, $major, $minor )
+
+useable_role() will compare the LON-CAPA version required by a course with
+the version available on the client server. If the client server's version
+is compatible, 1 will be returned.
+
+
+=item get_courseinfo_hash( $cnum, $cdom, $home )
+
+get_courseinfo_hash() is used to retrieve course information from the db
+file: nohist_courseids.db for a course for which the current server is *not*
+the home server.
+
+A hash of a hash will be retrieved. The outer hash contains a single key --
+courseID -- for the course for which the data are being requested.
+The contents of the inner hash, for that single item in the outer hash
+are returned (and cached in memcache for 10 minutes).
+
+
+
=back
=head1 BUGS