--- loncom/lond 2009/07/25 15:25:10 1.417 +++ loncom/lond 2009/10/29 03:23:52 1.432 @@ -2,7 +2,7 @@ # The LearningOnline Network # lond "LON Daemon" Server (port "LOND" 5663) # -# $Id: lond,v 1.417 2009/07/25 15:25:10 raeburn Exp $ +# $Id: lond,v 1.432 2009/10/29 03:23:52 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -59,7 +59,7 @@ my $DEBUG = 0; # Non zero to ena my $status=''; my $lastlog=''; -my $VERSION='$Revision: 1.417 $'; #' stupid emacs +my $VERSION='$Revision: 1.432 $'; #' stupid emacs my $remoteVERSION; my $currenthostid="default"; my $currentdomainid; @@ -67,6 +67,7 @@ my $currentdomainid; my $client; my $clientip; # IP address of client. my $clientname; # LonCAPA name of client. +my $clientversion; # LonCAPA version running on client my $server; @@ -1815,8 +1816,9 @@ sub change_password_handler { # npass - New password. # context - Context in which this was called # (preferences or reset_by_email). + # lonhost - HostID of server where request originated - my ($udom,$uname,$upass,$npass,$context)=split(/:/,$tail); + my ($udom,$uname,$upass,$npass,$context,$lonhost)=split(/:/,$tail); $upass=&unescape($upass); $npass=&unescape($npass); @@ -1825,9 +1827,13 @@ sub change_password_handler { # First require that the user can be authenticated with their # old password unless context was 'reset_by_email': - my $validated; + my ($validated,$failure); if ($context eq 'reset_by_email') { - $validated = 1; + if ($lonhost eq '') { + $failure = 'invalid_client'; + } else { + $validated = 1; + } } else { $validated = &validate_user($udom, $uname, $upass); } @@ -1841,8 +1847,11 @@ sub change_password_handler { $salt=substr($salt,6,2); my $ncpass=crypt($npass,$salt); if(&rewrite_password_file($udom, $uname, "internal:$ncpass")) { - &logthis("Result of password change for " - ."$uname: pwchange_success"); + my $msg="Result of password change for $uname: pwchange_success"; + if ($lonhost) { + $msg .= " - request originated from: $lonhost"; + } + &logthis($msg); &Reply($client, "ok\n", $userinput); } else { &logthis("Unable to open $uname passwd " @@ -1863,7 +1872,10 @@ sub change_password_handler { } } else { - &Failure( $client, "non_authorized\n", $userinput); + if ($failure eq '') { + $failure = 'non_authorized'; + } + &Failure( $client, "$failure\n", $userinput); } return 1; @@ -3110,6 +3122,13 @@ sub dump_with_regexp { my $qresult=''; my $count=0; while (my ($key,$value) = each(%$hashref)) { + if ($namespace eq 'roles') { + if ($key =~ /^($LONCAPA::match_domain)_($LONCAPA::match_community)_cc$/) { + if ($clientversion =~ /^(\d+\.\d+)$/) { + next if ($1 <= 2.9); + } + } + } if ($regexp eq '.') { $count++; if (defined($range) && $count >= $end) { last; } @@ -3674,8 +3693,10 @@ sub put_course_id_hash_handler { # will be returned. Pre-2.2.0 legacy entries from # nohist_courseiddump will only contain usernames. # type - optional parameter for selection -# regexp_ok - if true, allow the supplied institutional code -# filter to behave as a regular expression. +# regexp_ok - if 1 or -1 allow the supplied institutional code +# filter to behave as a regular expression: +# 1 will not exclude the course if the instcode matches the RE +# -1 will exclude the course if the instcode matches the RE # rtn_as_hash - whether to return the information available for # each matched item as a frozen hash of all # key, value pairs in the item's hash, or as a @@ -3691,6 +3712,15 @@ sub put_course_id_hash_handler { # caller - if set to 'coursecatalog', courses set to be hidden # from course catalog will be excluded from results (unless # overridden by "showhidden". +# cloner - escaped username:domain of course cloner (if picking course to +# clone). +# cc_clone_list - escaped comma separated list of courses for which +# course cloner has active CC role (and so can clone +# automatically). +# cloneonly - filter by courses for which cloner has rights to clone. +# createdbefore - include courses for which creation date preceeded this date. +# createdafter - include courses for which creation date followed this date. +# creationcontext - include courses created in specified context # # $client - The socket open on the client. # Returns: @@ -3703,8 +3733,10 @@ sub dump_course_id_handler { my ($udom,$since,$description,$instcodefilter,$ownerfilter,$coursefilter, $typefilter,$regexp_ok,$rtn_as_hash,$selfenrollonly,$catfilter,$showhidden, - $caller) =split(/:/,$tail); + $caller,$cloner,$cc_clone_list,$cloneonly,$createdbefore,$createdafter, + $creationcontext) =split(/:/,$tail); my $now = time; + my ($cloneruname,$clonerudom,%cc_clone); if (defined($description)) { $description=&unescape($description); } else { @@ -3747,6 +3779,35 @@ sub dump_course_id_handler { 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 = '.'; + } + my $unpack = 1; if ($description eq '.' && $instcodefilter eq '.' && $coursefilter eq '.' && $typefilter eq '.') { @@ -3758,7 +3819,8 @@ sub dump_course_id_handler { if ($hashref) { while (my ($key,$value) = each(%$hashref)) { my ($unesc_key,$lasttime_key,$lasttime,$is_hash,%val, - %unesc_val,$selfenroll_end,$selfenroll_types); + %unesc_val,$selfenroll_end,$selfenroll_types,$created, + $context); $unesc_key = &unescape($key); if ($unesc_key =~ /^lasttime:/) { next; @@ -3769,23 +3831,71 @@ sub dump_course_id_handler { $lasttime = $hashref->{$lasttime_key}; next if ($lasttime<$since); } + my ($canclone,$valchange); my $items = &Apache::lonnet::thaw_unescape($value); if (ref($items) eq 'HASH') { + if ($hashref->{$lasttime_key} eq '') { + next if ($since > 1); + } $is_hash = 1; + if (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; + } + } + } + } elsif (defined($cloneruname)) { + if ($cc_clone{$unesc_key}) { + $canclone = 1; + $items->{'cloners'} = $cloneruname.':'.$clonerudom; + $valchange = 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'}); @@ -3807,7 +3917,15 @@ sub dump_course_id_handler { } } else { next if ($catfilter ne ''); - next if ($selfenrollonly); + 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); @@ -3816,6 +3934,9 @@ sub dump_course_id_handler { } ($val{'descr'},$val{'inst_code'},$val{'owner'},$val{'type'}) = @courseitems; } + if ($cloneonly) { + next unless ($canclone); + } my $match = 1; if ($description ne '.') { if (!$is_hash) { @@ -3829,10 +3950,14 @@ sub dump_course_id_handler { if (!$is_hash) { $unesc_val{'inst_code'} = &unescape($val{'inst_code'}); } - if ($regexp_ok) { + 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; @@ -3898,12 +4023,18 @@ sub dump_course_id_handler { if ($match == 1) { if ($rtn_as_hash) { if ($is_hash) { - $qresult.=$key.'='.$value.'&'; + 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.'&'; @@ -4027,7 +4158,6 @@ sub get_domain_handler { } ®ister_handler("getdom", \&get_domain_handler, 0, 1, 0); - # # Puts an id to a domains id database. # @@ -4334,27 +4464,30 @@ sub dump_domainroles_handler { $rolesfilter=&unescape($rolesfilter); @roles = split(/\&/,$rolesfilter); } - + my $hashref = &tie_domain_hash($udom, "nohist_domainroles", &GDBM_WRCREAT()); if ($hashref) { my $qresult = ''; while (my ($key,$value) = each(%$hashref)) { my $match = 1; - my ($start,$end) = split(/:/,&unescape($value)); + my ($end,$start) = split(/:/,&unescape($value)); my ($trole,$uname,$udom,$runame,$rudom,$rsec) = split(/:/,&unescape($key)); - unless ($startfilter eq '.' || !defined($startfilter)) { - if ((defined($start)) && ($start >= $startfilter)) { + unless (@roles < 1) { + unless (grep/^\Q$trole\E$/,@roles) { $match = 0; + next; } } - unless ($endfilter eq '.' || !defined($endfilter)) { - if ((defined($end)) && ($end <= $endfilter)) { + unless ($startfilter eq '.' || !defined($startfilter)) { + if ((defined($start)) && ($start >= $startfilter)) { $match = 0; + next; } } - unless (@roles < 1) { - unless (grep/^\Q$trole\E$/,@roles) { + unless ($endfilter eq '.' || !defined($endfilter)) { + if ((defined($end)) && (($end > 0) && ($end <= $endfilter))) { $match = 0; + next; } } if ($match == 1) { @@ -4642,17 +4775,23 @@ sub enrollment_enabled_handler { ®ister_handler("autorun", \&enrollment_enabled_handler, 0, 1, 0); # -# Validate an institutional code use for a LON-CAPA course. +# Validate an institutional code used for a LON-CAPA course. # # Formal Parameters: # $cmd - The command request that got us dispatched. # $tail - The tail of the command. In this case, # this is a colon separated set of words that will be split # into: -# $inst_course_id - The institutional cod3 from the -# institutions point of view. -# $cdom - The domain from the institutions -# point of view. +# $dom - The domain for which the check of +# institutional course code will occur. +# +# $instcode - The institutional code for the course +# being requested, or validated for rights +# to request. +# +# $owner - The course requestor (who will be the +# course owner, in the form username:domain +# # $client - Socket open on the client. # Returns: # 1 - Indicating processing should continue. @@ -4661,8 +4800,12 @@ sub validate_instcode_handler { my ($cmd, $tail, $client) = @_; my $userinput = "$cmd:$tail"; my ($dom,$instcode,$owner) = split(/:/, $tail); - my $outcome=&localenroll::validate_instcode($dom,$instcode,$owner); - &Reply($client, \$outcome, $userinput); + $instcode = &unescape($instcode); + $owner = &unescape($owner); + my ($outcome,$description) = + &localenroll::validate_instcode($dom,$instcode,$owner); + my $result = &escape($outcome).'&'.&escape($description); + &Reply($client, \$result, $userinput); return 1; } @@ -4871,6 +5014,61 @@ sub retrieve_auto_file_handler { } ®ister_handler("autoretrieve", \&retrieve_auto_file_handler, 0,1,0); +sub crsreq_checks_handler { + my ($cmd, $tail, $client) = @_; + my $userinput = "$cmd:$tail"; + my $dom = $tail; + my $result; + my @reqtypes = ('official','unofficial','community'); + eval { + local($SIG{__DIE__})='DEFAULT'; + my %validations; + my $response = &localenroll::crsreq_checks($dom,\@reqtypes, + \%validations); + if ($response eq 'ok') { + foreach my $key (keys(%validations)) { + $result .= &escape($key).'='.&Apache::lonnet::freeze_escape($validations{$key}).'&'; + } + $result =~ s/\&$//; + } else { + $result = 'error'; + } + }; + if (!$@) { + &Reply($client, \$result, $userinput); + } else { + &Failure($client,"unknown_cmd\n",$userinput); + } + return 1; +} +®ister_handler("autocrsreqchecks", \&crsreq_checks_handler, 0, 1, 0); + +sub validate_crsreq_handler { + my ($cmd, $tail, $client) = @_; + my $userinput = "$cmd:$tail"; + my ($dom,$owner,$crstype,$inststatuslist,$instcode,$instseclist) = split(/:/, $tail); + $instcode = &unescape($instcode); + $owner = &unescape($owner); + $crstype = &unescape($crstype); + $inststatuslist = &unescape($inststatuslist); + $instcode = &unescape($instcode); + $instseclist = &unescape($instseclist); + my $outcome; + eval { + local($SIG{__DIE__})='DEFAULT'; + $outcome = &localenroll::validate_crsreq($dom,$owner,$crstype, + $inststatuslist,$instcode, + $instseclist); + }; + if (!$@) { + &Reply($client, \$outcome, $userinput); + } else { + &Failure($client,"unknown_cmd\n",$userinput); + } + return 1; +} +®ister_handler("autocrsreqvalidation", \&validate_crsreq_handler, 0, 1, 0); + # # Read and retrieve institutional code format (for support form). # Formal Parameters: @@ -6073,7 +6271,7 @@ sub make_new_child { &ReadManagerTable(); my $clientrec=defined(&Apache::lonnet::get_hosts_from_ip($outsideip)); my $ismanager=($managers{$outsideip} ne undef); - $clientname = "[unknonwn]"; + $clientname = "[unknown]"; if($clientrec) { # Establish client type. $ConnectionType = "client"; $clientname = (&Apache::lonnet::get_hosts_from_ip($outsideip))[-1]; @@ -6101,7 +6299,7 @@ sub make_new_child { # # If the remote is attempting a local init... give that a try: # - my ($i, $inittype) = split(/:/, $remotereq); + (my $i, my $inittype, $clientversion) = split(/:/, $remotereq); # If the connection type is ssl, but I didn't get my # certificate files yet, then I'll drop back to @@ -6121,6 +6319,7 @@ sub make_new_child { } if($inittype eq "local") { + $clientversion = $perlvar{'lonVersion'}; my $key = LocalConnection($client, $remotereq); if($key) { Debug("Got local key $key");