Diff for /loncom/Lond.pm between versions 1.1 and 1.15

version 1.1, 2012/04/11 21:32:28 version 1.15, 2019/07/11 18:12:01
Line 27 Line 27
 ###  ###
   
 #NOTE perldoc at the end of file  #NOTE perldoc at the end of file
   #TODO move remaining lond functions into this
   
 package LONCAPA::Lond;  package LONCAPA::Lond;
   
Line 36  use lib '/home/httpd/lib/perl/'; Line 37  use lib '/home/httpd/lib/perl/';
 use LONCAPA;  use LONCAPA;
 use Apache::lonnet;  use Apache::lonnet;
 use GDBM_File;  use GDBM_File;
   use MIME::Base64;
   use Crypt::OpenSSL::X509;
   use Crypt::X509::CRL;
   use Crypt::PKCS10;
   
 sub dump_with_regexp {  sub dump_with_regexp {
     #TODO encapsulate $clientname and $clientversion in a object.      my ( $tail, $clientversion ) = @_;
     my ( $cmd, $tail, $clientname, $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);      my ($start,$end);
   
     if (defined($range)) {      if (defined($range)) {
  if ($range =~/^(\d+)\-(\d+)$/) {          if ($range =~ /^(\d+)\-(\d+)$/) {
     ($start,$end) = ($1,$2);              ($start,$end) = ($1,$2);
  } elsif ($range =~/^(\d+)$/) {          } elsif ($range =~/^(\d+)$/) {
     ($start,$end) = (0,$1);              ($start,$end) = (0,$1);
  } else {          } else {
     undef($range);              undef($range);
  }          }
     }      }
     Apache::lonnet::logthis("Lond.pm: udom:[$udom] uname:[$uname] namespace:[$namespace]");  
     my $hashref = &tie_user_hash($udom, $uname, $namespace,      my $hashref = &tie_user_hash($udom, $uname, $namespace, &GDBM_READER()) or 
  &GDBM_READER());          return "error: ".($!+0)." tie(GDBM) Failed while attempting dump";
     my $skipcheck;  
     if ($hashref) {      my $qresult = '';
         my $qresult='';      my $count = 0;
  my $count=0;  
 #  #
 # When dump is for roles.db, determine if LON-CAPA version checking is needed.  # 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,  # Sessions on 2.10 and later do not require version checking, as that occurs
 # to indicate no version checking is needed (in this case, checking occurs  
 # on the server hosting the user session, when constructing the roles/courses   # on the server hosting the user session, when constructing the roles/courses 
 # screen).  # screen).
 #   # 
         if ($extra ne '') {      my $skipcheck;
             $extra = &Apache::lonnet::thaw_unescape($extra);      my @ids = &Apache::lonnet::current_machine_ids();
             $skipcheck = $extra->{'skipcheck'};      my (%homecourses, $major, $minor, $now);
         }  
         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     # 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    # version on the server which requested the data. 
 # 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.  
 #   # 
         if (($namespace eq 'roles') && (!$skipcheck)) {      if ($namespace eq 'roles') {
             my $loncaparev = $clientversion;          if ($clientversion =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?/) {
             if ($loncaparev eq '') {              $major = $1;
                 $loncaparev = $Apache::lonnet::loncaparevs{$clientname};              $minor = $2;
             }  
             if ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?/) {  
                 $major = $1;  
                 $minor = $2;  
             }  
             $now = time;  
         }          }
  while (my ($key,$value) = each(%$hashref)) {          if (($major > 2) || (($major == 2) && ($minor > 9))) {
             if ($namespace eq 'roles') {              $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)$}) {                  if ($key =~ m{^/($LONCAPA::match_domain)/($LONCAPA::match_courseid)(/?[^_]*)_(cc|co|in|ta|ep|ad|st|cr)$}) {
                     my $cdom = $1;                      my $cdom = $1;
                     my $cnum = $2;                      my $cnum = $2;
                     unless ($skipcheck) {                      my ($role,$roleend,$rolestart) = split(/\_/,$value);
                         my ($role,$roleend,$rolestart) = split(/\_/,$value);                      if (!$roleend || $roleend > $now) {
                         if (!$roleend || $roleend > $now) {  
 #  #
 # For active course roles, check that requesting server is running a LON-CAPA  # 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  # version which meets any version requirements for the course. Do not include
Line 116  sub dump_with_regexp { Line 107  sub dump_with_regexp {
 # homeserver is the current server, or whether it is a different server.  # homeserver is the current server, or whether it is a different server.
 # In both cases, the course's version requirement needs to be retrieved.  # In both cases, the course's version requirement needs to be retrieved.
 #   # 
                             next unless (&releasereqd_check($cnum,$cdom,$key,$value,$major,                          next unless (&releasereqd_check($cnum,$cdom,$key,$value,$major,
                                                             $minor,\%homecourses,\@ids));                                                          $minor,\%homecourses,\@ids));
                         }  
                     }                      }
                 }                  }
             }              }
     if ($regexp eq '.') {          if ($regexp eq '.') {
  $count++;              $count++;
  if (defined($range) && $count >= $end)   { last; }              if (defined($range) && $count >= $end)   { last; }
  if (defined($range) && $count <  $start) { next; }              if (defined($range) && $count <  $start) { next; }
  $qresult.=$key.'='.$value.'&';              $qresult.=$key.'='.$value.'&';
     } else {          } else {
  my $unescapeKey = &unescape($key);              my $unescapeKey = &unescape($key);
  if (eval('$unescapeKey=~/$regexp/')) {              if (eval('$unescapeKey=~/$regexp/')) {
     $count++;                  $count++;
     if (defined($range) && $count >= $end)   { last; }                  if (defined($range) && $count >= $end)   { last; }
     if (defined($range) && $count <  $start) { next; }                  if (defined($range) && $count <  $start) { next; }
     $qresult.="$key=$value&";                  $qresult.="$key=$value&";
  }              }
     }          }
  }      }
  if (&untie_user_hash($hashref)) {  
       &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  # 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  # version requirements for courses for which the current server is the home
Line 145  sub dump_with_regexp { Line 137  sub dump_with_regexp {
 # user's session. If so, include those role results in the data returned to    # user's session. If so, include those role results in the data returned to  
 # the client server.  # the client server.
 #  #
             if (($namespace eq 'roles') && (!$skipcheck)) {      if (($namespace eq 'roles') && (!$skipcheck)) {
                 if (keys(%homecourses) > 0) {          if (keys(%homecourses) > 0) {
                     $qresult .= &check_homecourses(\%homecourses,$regexp,$count,              $qresult .= &check_homecourses(\%homecourses,$regexp,$count,
                                                    $range,$start,$end,$major,$minor);                                             $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 {      } else {
     return "error: ".($!+0)." tie(GDBM) Failed while attempting dump";          if (ref($ids) eq 'ARRAY') {
     #&Failure($client, "error: ".($!+0)." tie(GDBM) Failed ".              if (grep(/^\Q$home\E$/,@{$ids})) {
  # "while attempting dump\n", $userinput);                  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  sub useable_role {
     die("SHOULD NOT HAPPEN!");      my ($reqdmajor,$reqdminor,$major,$minor) = @_; 
       if ($reqdmajor ne '' && $reqdminor ne '') {
           return if (($major eq '' && $minor eq '') ||
                      ($major < $reqdmajor) ||
                      (($major == $reqdmajor) && ($minor < $reqdminor)));
       }
     return 1;      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("<font color='blue'>WARNING courseiddump for $cnum:$cdom from $home timedout</font>");
           } else {
               &logthis("<font color='yellow'>WARNING unexpected error during eval of call for courseiddump from $home</font>");
           }
       } 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',
                        crl      => 'lonnetCertRevocationList',
                      );
       my (%md5hash,%expected_cn,%expired,%revoked,%wrongcn,%info,$crlfile,$cafile,
           %rvkcerts,$numrvk);
       %info = (
                   key => {},
                   ca  => {},
                   host => {},
                   hostname => {},
                   crl => {},
               );
       my @ordered = ('crl','key','ca','host','hostname');
       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->{$pemfiles{'crl'}};
               $cafile = $certsdir.'/'.$perlvar->{$pemfiles{'ca'}};
               foreach my $key (@ordered) {
                   if ($perlvar->{$pemfiles{$key}}) {
                       my $file = $certsdir.'/'.$perlvar->{$pemfiles{$key}};
                       if (-e $file) {
                           if ($key eq 'crl') {
                                if ((-e $crlfile) && (-e $cafile)) {
                                    if (open(PIPE,"openssl crl -in $crlfile -inform pem -CAfile $cafile -noout 2>&1 |")) {
                                        my $crlstatus = <PIPE>;
                                        close(PIPE);
                                        chomp($crlstatus);
                                        if ($crlstatus =~ /OK/) {
                                            $info{$key}{'status'} = 'ok';
                                            $info{$key}{'details'} = 'CRL valid for CA';
                                        }
                                    }
                                }
                                if (open(my $fh,'<',$crlfile)) {
                                    my $pem_crl = '';
                                    while (my $line=<$fh>) {
                                        chomp($line);
                                        next if ($line eq '-----BEGIN X509 CRL-----');
                                        next if ($line eq '-----END X509 CRL-----');
                                        $pem_crl .= $line;
                                    }
                                    close($fh);
                                    my $der_crl = MIME::Base64::decode_base64($pem_crl);
                                    if ($der_crl ne '') {
                                        my $decoded = Crypt::X509::CRL->new( crl => $der_crl );
                                        if ($decoded->error) {
                                            $info{$key}{'status'} = 'error';
                                        } elsif (ref($decoded)) {
                                            $info{$key}{'start'} = $decoded->this_update;
                                            $info{$key}{'end'} = $decoded->next_update;
                                            $info{$key}{'alg'} = $decoded->SigEncAlg.' '.$decoded->SigHashAlg;
                                            $info{$key}{'cn'} = $decoded->issuer_cn;
                                            $info{$key}{'email'} = $decoded->issuer_email;
                                            $info{$key}{'size'} = $decoded->signature_length;
                                            my $rlref = $decoded->revocation_list;
                                            if (ref($rlref) eq 'HASH') {
                                                foreach my $key (keys(%{$rlref})) {
                                                    my $hkey = sprintf("%X",$key);
                                                    $rvkcerts{$hkey} = 1;
                                                }
                                                $numrvk = scalar(keys(%{$rlref}));
                                                if ($numrvk) {
                                                    $info{$key}{'details'} .= " ($numrvk revoked)"; 
                                                }
                                            }
                                        }
                                    }
                               }
                           } elsif ($key eq 'key') {
                               if (open(PIPE,"openssl rsa -noout -in $file -check |")) {
                                   my $check = <PIPE>;
                                   close(PIPE);
                                   chomp($check);
                                   $info{$key}{'status'} = $check;
                               }
                               if (open(PIPE,"openssl rsa -noout -modulus -in $file | openssl md5 |")) {
                                   $md5hash{$key} = <PIPE>;
                                   close(PIPE);
                                   chomp($md5hash{$key});
                               }
                           } else {
                               if ($key eq 'ca') {
                                   if (open(PIPE,"openssl verify -CAfile $file $file |")) {
                                       my $check = <PIPE>;
                                       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} = <PIPE>;
                                       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'} = uc($x509->serial());
                               $info{$key}{'issuerhash'} = $x509->issuer_hash();
                               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 (($numrvk) && ($info{$key}{'serial'})) {
                                       if ($rvkcerts{$info{$key}{'serial'}}) {
                                           $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 = <PIPE>;
                                   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';
                   } elsif ((exists($info{'ca'}{'issuerhash'})) &&
                            ($info{'ca'}{'issuerhash'} ne $info{$key}{'issuerhash'})) {
                       $info{$key}{'status'} = 'mismatch';
                   } 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;  1;
   
 __END__  __END__
Line 191  LONCAPA::Lond.pm Line 1044  LONCAPA::Lond.pm
   
 =over 4  =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  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  the keys.  In this dump, no effort is made to separate symb from version
Line 199  information. Presumably the databases th Line 1052  information. Presumably the databases th
 different structure.  Need to look at this and improve the documentation of  different structure.  Need to look at this and improve the documentation of
 both this and the currentdump handler.  both this and the currentdump handler.
   
 $cmd is the command keyword.  
   
 $tail a colon separated list containing  $tail a colon separated list containing
   
 =over   =over 
Line 224  selective dumps. Line 1075  selective dumps.
   
 optional range of entries e.g., 10-20 would return the 10th to 19th items, etc.    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  =back
   
 $client is the channel open on the client.  $client is the channel open on the client.
Line 236  Returns: 1 (Continue processing). Line 1083  Returns: 1 (Continue processing).
   
 Side effects: response is written to $client.    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  =back
   
 =head1 BUGS  =head1 BUGS

Removed from v.1.1  
changed lines
  Added in v.1.15


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>