--- loncom/lonnet/perl/lonnet.pm 2014/03/31 01:09:20 1.1254 +++ loncom/lonnet/perl/lonnet.pm 2014/11/24 02:36:21 1.1269 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.1254 2014/03/31 01:09:20 raeburn Exp $ +# $Id: lonnet.pm,v 1.1269 2014/11/24 02:36:21 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -961,12 +961,29 @@ sub has_user_session { # --------- determine least loaded server in a user's domain which allows login sub choose_server { - my ($udom,$checkloginvia,$required) = @_; + my ($udom,$checkloginvia,$required,$skiploadbal) = @_; my %domconfhash = &Apache::loncommon::get_domainconf($udom); my %servers = &get_servers($udom); my $lowest_load = 30000; - my ($login_host,$hostname,$portal_path,$isredirect); + my ($login_host,$hostname,$portal_path,$isredirect,$balancers); + if ($skiploadbal) { + ($balancers,my $cached)=&is_cached_new('loadbalancing',$udom); + unless (defined($cached)) { + my $cachetime = 60*60*24; + my %domconfig = + &Apache::lonnet::get_dom('configuration',['loadbalancing'],$udom); + if (ref($domconfig{'loadbalancing'}) eq 'HASH') { + $balancers = &do_cache_new('loadbalancing',$udom,$domconfig{'loadbalancing'}, + $cachetime); + } + } + } foreach my $lonhost (keys(%servers)) { + if ($skiploadbal) { + if (ref($balancers) eq 'HASH') { + next if (exists($balancers->{$lonhost})); + } + } my $loginvia; if ($checkloginvia) { $loginvia = $domconfhash{$udom.'.login.loginvia_'.$lonhost}; @@ -1635,6 +1652,7 @@ sub dump_dom { sub get_dom { my ($namespace,$storearr,$udom,$uhome)=@_; + return if ($udom eq 'public'); my $items=''; foreach my $item (@$storearr) { $items.=&escape($item).'&'; @@ -1642,6 +1660,7 @@ sub get_dom { $items=~s/\&$//; if (!$udom) { $udom=$env{'user.domain'}; + return if ($udom eq 'public'); if (defined(&domain($udom,'primary'))) { $uhome=&domain($udom,'primary'); } else { @@ -1745,14 +1764,13 @@ sub retrieve_inst_usertypes { my %domdefs = &Apache::lonnet::get_domain_defaults($udom); if ((ref($domdefs{'inststatustypes'}) eq 'HASH') && (ref($domdefs{'inststatusorder'}) eq 'ARRAY')) { - %returnhash = %{$domdefs{'inststatustypes'}}; - @order = @{$domdefs{'inststatusorder'}}; + return ($domdefs{'inststatustypes'},$domdefs{'inststatusorder'}); } else { if (defined(&domain($udom,'primary'))) { my $uhome=&domain($udom,'primary'); my $rep=&reply("inst_usertypes:$udom",$uhome); if ($rep =~ /^(con_lost|error|no_such_host|refused)/) { - &logthis("get_dom failed - $rep returned from $uhome in domain: $udom"); + &logthis("retrieve_inst_usertypes failed - $rep returned from $uhome in domain: $udom"); return (\%returnhash,\@order); } my ($hashitems,$orderitems) = split(/:/,$rep); @@ -1768,10 +1786,10 @@ sub retrieve_inst_usertypes { push(@order,&unescape($item)); } } else { - &logthis("get_dom failed - no primary domain server for $udom"); + &logthis("retrieve_inst_usertypes failed - no primary domain server for $udom"); } + return (\%returnhash,\@order); } - return (\%returnhash,\@order); } sub is_domainimage { @@ -2012,7 +2030,8 @@ sub get_domain_defaults { &Apache::lonnet::get_dom('configuration',['defaults','quotas', 'requestcourses','inststatus', 'coursedefaults','usersessions', - 'requestauthor','selfenrollment'],$domain); + 'requestauthor','selfenrollment', + 'coursecategories'],$domain); my @coursetypes = ('official','unofficial','community','textbook'); if (ref($domconfig{'defaults'}) eq 'HASH') { $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; @@ -2051,7 +2070,7 @@ sub get_domain_defaults { $domdefaults{'requestauthor'} = $domconfig{'requestauthor'}; } if (ref($domconfig{'inststatus'}) eq 'HASH') { - foreach my $item ('inststatustypes','inststatusorder') { + foreach my $item ('inststatustypes','inststatusorder','inststatusguest') { $domdefaults{$item} = $domconfig{'inststatus'}{$item}; } } @@ -2104,6 +2123,16 @@ sub get_domain_defaults { } } } + if (ref($domconfig{'coursecategories'}) eq 'HASH') { + $domdefaults{'catauth'} = 'std'; + $domdefaults{'catunauth'} = 'std'; + if ($domconfig{'coursecategories'}{'auth'}) { + $domdefaults{'catauth'} = $domconfig{'coursecategories'}{'auth'}; + } + if ($domconfig{'coursecategories'}{'unauth'}) { + $domdefaults{'catunauth'} = $domconfig{'coursecategories'}{'unauth'}; + } + } &do_cache_new('domdefaults',$domain,\%domdefaults,$cachetime); return %domdefaults; } @@ -4824,7 +4853,7 @@ sub tmprestore { # ----------------------------------------------------------------------- Store sub store { - my ($storehash,$symb,$namespace,$domain,$stuname) = @_; + my ($storehash,$symb,$namespace,$domain,$stuname,$laststore) = @_; my $home=''; if ($stuname) { $home=&homeserver($stuname,$domain); } @@ -4854,13 +4883,13 @@ sub store { } $namevalue=~s/\&$//; &courselog($symb.':'.$stuname.':'.$domain.':STORE:'.$namevalue); - return reply("store:$domain:$stuname:$namespace:$symb:$namevalue","$home"); + return reply("store:$domain:$stuname:$namespace:$symb:$namevalue:$laststore","$home"); } # -------------------------------------------------------------- Critical Store sub cstore { - my ($storehash,$symb,$namespace,$domain,$stuname) = @_; + my ($storehash,$symb,$namespace,$domain,$stuname,$laststore) = @_; my $home=''; if ($stuname) { $home=&homeserver($stuname,$domain); } @@ -4891,7 +4920,7 @@ sub cstore { $namevalue=~s/\&$//; &courselog($symb.':'.$stuname.':'.$domain.':CSTORE:'.$namevalue); return critical - ("store:$domain:$stuname:$namespace:$symb:$namevalue","$home"); + ("store:$domain:$stuname:$namespace:$symb:$namevalue:$laststore","$home"); } # --------------------------------------------------------------------- Restore @@ -5601,18 +5630,17 @@ sub dump { if (!$uname) { $uname=$env{'user.name'}; } my $uhome=&homeserver($uname,$udomain); - my $reply; + if ($regexp) { + $regexp=&escape($regexp); + } else { + $regexp='.'; + } if (grep { $_ eq $uhome } current_machine_ids()) { # user is hosted on this machine - $reply = LONCAPA::Lond::dump_with_regexp(join(":", ($udomain, + my $reply = LONCAPA::Lond::dump_with_regexp(join(":", ($udomain, $uname, $namespace, $regexp, $range)), $perlvar{'lonVersion'}); return %{unserialize($reply, $escapedkeys)}; } - if ($regexp) { - $regexp=&escape($regexp); - } else { - $regexp='.'; - } my $rep=&reply("dump:$udomain:$uname:$namespace:$regexp:$range",$uhome); my @pairs=split(/\&/,$rep); my %returnhash=(); @@ -5790,7 +5818,7 @@ sub newput { # --------------------------------------------------------- putstore interface sub putstore { - my ($namespace,$symb,$version,$storehash,$udomain,$uname)=@_; + my ($namespace,$symb,$version,$storehash,$udomain,$uname,$tolog)=@_; if (!$udomain) { $udomain=$env{'user.domain'}; } if (!$uname) { $uname=$env{'user.name'}; } my $uhome=&homeserver($uname,$udomain); @@ -5804,6 +5832,17 @@ sub putstore { my $reply = &reply("putstore:$udomain:$uname:$namespace:$esc_symb:$esc_v:$items", $uhome); + if (($tolog) && ($reply eq 'ok')) { + my $namevalue=''; + foreach my $key (keys(%{$storehash})) { + $namevalue.=&escape($key).'='.&freeze_escape($storehash->{$key}).'&'; + } + $namevalue .= 'ip='.&escape($ENV{'REMOTE_ADDR'}). + '&host='.&escape($perlvar{'lonHostID'}). + '&version='.$esc_v. + '&by='.&escape($env{'user.name'}.':'.$env{'user.domain'}); + &Apache::lonnet::courselog($symb.':'.$uname.':'.$udomain.':PUTSTORE:'.$namevalue); + } if ($reply eq 'unknown_cmd') { # gfall back to way things use to be done return &old_putstore($namespace,$symb,$version,$storehash,$udomain, @@ -5962,10 +6001,15 @@ sub get_timebased_id { my %inuse = &Apache::lonnet::dump('nohist_'.$namespace,$cdom,$cnum,$prefix); my $id = time; $newid = $id; + if ($idtype eq 'addcode') { + $newid .= &sixnum_code(); + } my $idtries = 0; while (exists($inuse{$prefix."\0".$newid}) && $idtries < $maxtries) { if ($idtype eq 'concat') { $newid = $id.$idtries; + } elsif ($idtype eq 'addcode') { + $newid = $newid.&sixnum_code(); } else { $newid ++; } @@ -5982,6 +6026,7 @@ sub get_timebased_id { $error = 'error saving new item: '.$putresult; } } else { + undef($newid); $error = ('error: no unique suffix available for the new item '); } # remove lock @@ -5994,6 +6039,14 @@ sub get_timebased_id { return ($newid,$dellock,$error); } +sub sixnum_code { + my $code; + for (0..6) { + $code .= int( rand(9) ); + } + return $code; +} + # -------------------------------------------------- portfolio access checking sub portfolio_access { @@ -7860,17 +7913,20 @@ sub auto_courserequest_checks { } sub auto_courserequest_validation { - my ($dom,$owner,$crstype,$inststatuslist,$instcode,$instseclist) = @_; + my ($dom,$owner,$crstype,$inststatuslist,$instcode,$instseclist,$custominfo) = @_; my ($homeserver,$response); if ($dom =~ /^$match_domain$/) { $homeserver = &domain($dom,'primary'); } - unless ($homeserver eq 'no_host') { - + unless ($homeserver eq 'no_host') { + my $customdata; + if (ref($custominfo) eq 'HASH') { + $customdata = &freeze_escape($custominfo); + } $response=&unescape(&reply('autocrsreqvalidation:'.$dom.':'.&escape($owner). ':'.&escape($crstype).':'.&escape($inststatuslist). - ':'.&escape($instcode).':'.&escape($instseclist), - $homeserver)); + ':'.&escape($instcode).':'.&escape($instseclist).':'. + $customdata,$homeserver)); } return $response; } @@ -7891,7 +7947,7 @@ sub auto_validate_class_sec { sub auto_crsreq_update { my ($cdom,$cnum,$crstype,$action,$ownername,$ownerdomain,$fullname,$title, - $code,$inbound) = @_; + $code,$accessstart,$accessend,$inbound) = @_; my ($homeserver,%crsreqresponse); if ($cdom =~ /^$match_domain$/) { $homeserver = &domain($cdom,'primary'); @@ -7904,7 +7960,9 @@ sub auto_crsreq_update { my $response=&reply('autocrsrequpdate:'.$cdom.':'.$cnum.':'.&escape($crstype). ':'.&escape($action).':'.&escape($ownername).':'. &escape($ownerdomain).':'.&escape($fullname).':'. - &escape($title).':'.&escape($code).':'.$info,$homeserver); + &escape($title).':'.&escape($code).':'. + &escape($accessstart).':'.&escape($accessend).':'.$info, + $homeserver); unless ($response =~ /(con_lost|error|no_such_host|refused)/) { my @items = split(/&/,$response); foreach my $item (@items) { @@ -10191,7 +10249,7 @@ sub metadata { ($uri =~ m|/$|) || ($uri =~ m|/.meta$|) || ($uri =~ m{^/*uploaded/.+\.sequence$})) { return undef; } - if (($uri =~ /^priv/ || $uri=~/home\/httpd\/html\/priv/) + if (($uri =~ /^priv/ || $uri=~m{^home/httpd/html/priv}) && &Apache::lonxml::get_state('target') =~ /^(|meta)$/) { return undef; } @@ -10824,14 +10882,10 @@ sub deversion { sub symbread { my ($thisfn,$donotrecurse)=@_; - my $cache_str; - if ($thisfn ne '') { - $cache_str='request.symbread.cached.'.$thisfn; - if ($env{$cache_str} ne '') { - return $env{$cache_str}; - } - } else { + my $cache_str='request.symbread.cached.'.$thisfn; + if (defined($env{$cache_str})) { return $env{$cache_str}; } # no filename provided? try from environment + unless ($thisfn) { if ($env{'request.symb'}) { return $env{$cache_str}=&symbclean($env{'request.symb'}); } @@ -11249,8 +11303,12 @@ sub rndseed_CODE_64bit5 { sub setup_random_from_rndseed { my ($rndseed)=@_; if ($rndseed =~/([,:])/) { - my ($num1,$num2)=split(/[,:]/,$rndseed); - &Math::Random::random_set_seed(abs($num1),abs($num2)); + my ($num1,$num2) = map { abs($_); } (split(/[,:]/,$rndseed)); + if ((!$num1) || (!$num2) || ($num1 > 2147483562) || ($num2 > 2147483398)) { + &Math::Random::random_set_seed_from_phrase($rndseed); + } else { + &Math::Random::random_set_seed($num1,$num2); + } } else { &Math::Random::random_set_seed_from_phrase($rndseed); } @@ -11641,7 +11699,9 @@ sub default_login_domain { sub declutter { my $thisfn=shift; if ($thisfn=~m|^/enc/|) { $thisfn=&Apache::lonenc::unencrypted($thisfn); } - $thisfn=~s/^\Q$perlvar{'lonDocRoot'}\E//; + unless ($thisfn=~m{^/home/httpd/html/priv/}) { + $thisfn=~s{^/home/httpd/html}{}; + } $thisfn=~s/^\///; $thisfn=~s|^adm/wrapper/||; $thisfn=~s|^adm/coursedocs/showdoc/||; @@ -11768,7 +11828,7 @@ sub get_dns { $alldns{$host} = $protocol; } while (%alldns) { - my ($dns) = keys(%alldns); + my ($dns) = sort { $b cmp $a } keys(%alldns); my $ua=new LWP::UserAgent; $ua->timeout(30); my $request=new HTTP::Request('GET',"$alldns{$dns}://$dns$url"); @@ -11794,8 +11854,22 @@ sub get_dns { # ------------------------------------------------------Get DNS checksums file sub parse_dns_checksums_tab { my ($lines,$hashref) = @_; - my $machine_dom = &Apache::lonnet::host_domain($perlvar{'lonHostID'}); + my $lonhost = $perlvar{'lonHostID'}; + my $machine_dom = &Apache::lonnet::host_domain($lonhost); my $loncaparev = &get_server_loncaparev($machine_dom); + my $distro = (split(/\:/,&get_server_distarch($lonhost)))[0]; + my $webconfdir = '/etc/httpd/conf'; + if ($distro =~ /^(ubuntu|debian)(\d+)$/) { + $webconfdir = '/etc/apache2'; + } elsif ($distro =~ /^sles(\d+)$/) { + if ($1 >= 10) { + $webconfdir = '/etc/apache2'; + } + } elsif ($distro =~ /^suse(\d+\.\d+)$/) { + if ($1 >= 10.0) { + $webconfdir = '/etc/apache2'; + } + } my ($release,$timestamp) = split(/\-/,$loncaparev); my (%chksum,%revnum); if (ref($lines) eq 'ARRAY') { @@ -11804,6 +11878,11 @@ sub parse_dns_checksums_tab { if ($version eq $release) { foreach my $line (@{$lines}) { my ($file,$version,$shasum) = split(/,/,$line); + if ($file =~ m{^/etc/httpd/conf}) { + if ($webconfdir eq '/etc/apache2') { + $file =~ s{^\Q/etc/httpd/conf/\E}{$webconfdir/}; + } + } $chksum{$file} = $shasum; $revnum{$file} = $version; } @@ -11821,7 +11900,7 @@ sub parse_dns_checksums_tab { sub fetch_dns_checksums { my %checksums; my $machine_dom = &Apache::lonnet::host_domain($perlvar{'lonHostID'}); - my $loncaparev = &get_server_loncaparev($machine_dom); + my $loncaparev = &get_server_loncaparev($machine_dom,$perlvar{'lonHostID'}); my ($release,$timestamp) = split(/\-/,$loncaparev); &get_dns("/adm/dns/checksums/$release",\&parse_dns_checksums_tab,1,1, \%checksums); @@ -12520,7 +12599,7 @@ were new keys. I.E. 1:foo will become 1: Calling convention: my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$uname); - &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$uname); + &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$uname,$laststore); For more detailed information, see lonnet specific documentation. @@ -13157,15 +13236,21 @@ homeserver. =item * -store($storehash,$symb,$namespace,$udom,$uname) : stores hash permanently -for this url; hashref needs to be given and should be a \%hashname; the -remaining args aren't required and if they aren't passed or are '' they will -be derived from the env +store($storehash,$symb,$namespace,$udom,$uname,$laststore) : stores hash +permanently for this url; hashref needs to be given and should be a \%hashname; +the remaining args aren't required and if they aren't passed or are '' they will +be derived from the env (with the exception of $laststore, which is an +optional arg used when a user's submission is stored in grading). +$laststore is $version=$timestamp, where $version is the most recent version +number retrieved for the corresponding $symb in the $namespace db file, and +$timestamp is the timestamp for that transaction (UNIX time). +$laststore is currently only passed when cstore() is called by +structuretags::finalize_storage(). =item * -cstore($storehash,$symb,$namespace,$udom,$uname) : same as store but -uses critical subroutine +cstore($storehash,$symb,$namespace,$udom,$uname,$laststore) : same as store +but uses critical subroutine =item * @@ -13188,10 +13273,11 @@ $range should be either an integer '100' =item * -putstore($namespace,$symb,$version,$storehash,$udomain,$uname) : +putstore($namespace,$symb,$version,$storehash,$udomain,$uname,$tolog) : replaces a &store() version of data with a replacement set of data for a particular resource in a namespace passed in the $storehash hash -reference +reference. If $tolog is true, the transaction is logged in the courselog +with an action=PUTSTORE. =item * @@ -13345,7 +13431,7 @@ inststatus: types of institutional affil =over =item -inststatustypes, inststatusorder +inststatustypes, inststatusorder, inststatusguest =back @@ -13610,7 +13696,8 @@ filelocation except for hrefs =item * -declutter() : declutters URLs (remove docroot, beginning slashes, 'res' etc) +declutter() : declutters URLs -- remove beginning slashes, 'res' etc. +also removes beginning /home/httpd/html unless /priv/ follows it. =back